blob: 760167e9720b7c2654d756cb6a06fcac90dd30e0 [file] [log] [blame]
Andrew Jefferye3e3c972021-05-26 14:37:07 +09301#include "NVMeBasicContext.hpp"
2
3#include <endian.h>
4#include <sys/ioctl.h>
5#include <unistd.h>
6
Ed Tanous73030632022-01-14 10:09:47 -08007#include <FileHandle.hpp>
Andrew Jefferye3e3c972021-05-26 14:37:07 +09308#include <boost/asio/read.hpp>
9#include <boost/asio/streambuf.hpp>
10#include <boost/asio/write.hpp>
11
12#include <cassert>
13#include <cerrno>
14#include <cinttypes>
15#include <cstdio>
16#include <cstring>
17#include <system_error>
18#include <thread>
19
20extern "C"
21{
22#include <i2c/smbus.h>
23#include <linux/i2c-dev.h>
24}
25
26/*
27 * NVMe-MI Basic Management Command
28 *
29 * https://nvmexpress.org/wp-content/uploads/NVMe_Management_-_Technical_Note_on_Basic_Management_Command.pdf
30 */
31
32static std::shared_ptr<std::array<uint8_t, 6>>
33 encodeBasicQuery(int bus, uint8_t device, uint8_t offset)
34{
35 if (bus < 0)
36 {
37 throw std::domain_error("Invalid bus argument");
38 }
39
40 /* bus + address + command */
41 uint32_t busle = htole32(static_cast<uint32_t>(bus));
42 auto command =
43 std::make_shared<std::array<uint8_t, sizeof(busle) + 1 + 1>>();
44 memcpy(command->data(), &busle, sizeof(busle));
45 (*command)[sizeof(busle) + 0] = device;
46 (*command)[sizeof(busle) + 1] = offset;
47
48 return command;
49}
50
51static void decodeBasicQuery(const std::array<uint8_t, 6>& req, int& bus,
52 uint8_t& device, uint8_t& offset)
53{
Ed Tanousa771f6a2022-01-14 09:36:51 -080054 uint32_t busle = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +093055
56 memcpy(&busle, req.data(), sizeof(busle));
57 bus = le32toh(busle);
58 device = req[sizeof(busle) + 0];
59 offset = req[sizeof(busle) + 1];
60}
61
62static ssize_t execBasicQuery(int bus, uint8_t addr, uint8_t cmd,
63 std::vector<uint8_t>& resp)
64{
Ed Tanousa771f6a2022-01-14 09:36:51 -080065 int32_t size = 0;
Ed Tanous73030632022-01-14 10:09:47 -080066 std::filesystem::path devpath = "/dev/i2c-" + std::to_string(bus);
67 FileHandle fileHandle(devpath);
Andrew Jefferye3e3c972021-05-26 14:37:07 +093068
69 /* Select the target device */
Ed Tanous99c44092022-01-14 09:59:09 -080070 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Ed Tanous73030632022-01-14 10:09:47 -080071 if (::ioctl(fileHandle.handle(), I2C_SLAVE, addr) == -1)
Andrew Jefferye3e3c972021-05-26 14:37:07 +093072 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +103073 std::cerr << "Failed to configure device address 0x" << std::hex
74 << (int)addr << " for bus " << std::dec << bus << ": "
75 << strerror(errno) << "\n";
Ed Tanous73030632022-01-14 10:09:47 -080076
77 return -errno;
Andrew Jefferye3e3c972021-05-26 14:37:07 +093078 }
79
Andrew Jefferya7afacc2022-03-31 11:21:28 +103080 resp.resize(UINT8_MAX + 1);
Andrew Jefferye3e3c972021-05-26 14:37:07 +093081
82 /* Issue the NVMe MI basic command */
Ed Tanous73030632022-01-14 10:09:47 -080083 size = i2c_smbus_read_block_data(fileHandle.handle(), cmd, resp.data());
Andrew Jefferye3e3c972021-05-26 14:37:07 +093084 if (size < 0)
85 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +103086 std::cerr << "Failed to read block data from device 0x" << std::hex
87 << (int)addr << " on bus " << std::dec << bus << ": "
88 << strerror(errno) << "\n";
Ed Tanous73030632022-01-14 10:09:47 -080089 return size;
Andrew Jefferye3e3c972021-05-26 14:37:07 +093090 }
Ed Tanous73030632022-01-14 10:09:47 -080091 if (size > UINT8_MAX + 1)
Andrew Jefferye3e3c972021-05-26 14:37:07 +093092 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +103093 std::cerr << "Unexpected message length from device 0x" << std::hex
94 << (int)addr << " on bus " << std::dec << bus << ": " << size
95 << " (" << UINT8_MAX << ")\n";
Ed Tanous73030632022-01-14 10:09:47 -080096 return -EBADMSG;
Andrew Jefferye3e3c972021-05-26 14:37:07 +093097 }
98
Andrew Jefferya7afacc2022-03-31 11:21:28 +103099 resp.resize(size);
100
Ed Tanous73030632022-01-14 10:09:47 -0800101 return size;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930102}
103
Ed Tanous73030632022-01-14 10:09:47 -0800104static ssize_t processBasicQueryStream(FileHandle& in, FileHandle& out)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930105{
106 std::vector<uint8_t> resp{};
Ed Tanousa771f6a2022-01-14 09:36:51 -0800107 ssize_t rc = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930108
109 while (true)
110 {
Ed Tanousa771f6a2022-01-14 09:36:51 -0800111 uint8_t device = 0;
112 uint8_t offset = 0;
113 uint8_t len = 0;
114 int bus = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930115
116 /* bus + address + command */
117 std::array<uint8_t, sizeof(uint32_t) + 1 + 1> req{};
118
119 /* Read the command parameters */
Ed Tanous73030632022-01-14 10:09:47 -0800120 ssize_t rc = ::read(in.handle(), req.data(), req.size());
121 if (rc != static_cast<ssize_t>(req.size()))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930122 {
Ed Tanous73030632022-01-14 10:09:47 -0800123 std::cerr << "Failed to read request from in descriptor "
124 << strerror(errno) << "\n";
125 if (rc)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930126 {
Ed Tanous73030632022-01-14 10:09:47 -0800127 return -errno;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930128 }
Ed Tanous73030632022-01-14 10:09:47 -0800129 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930130 }
131
132 decodeBasicQuery(req, bus, device, offset);
133
134 /* Execute the query */
135 rc = execBasicQuery(bus, device, offset, resp);
136
137 /* Bounds check the response */
138 if (rc < 0)
139 {
140 len = 0;
141 }
142 else if (rc > UINT8_MAX)
143 {
144 assert(rc == UINT8_MAX + 1);
145
146 /* YOLO: Lop off the PEC */
147 len = UINT8_MAX;
148 }
149 else
150 {
151 len = rc;
152 }
153
154 /* Write out the response length */
Ed Tanous73030632022-01-14 10:09:47 -0800155 rc = ::write(out.handle(), &len, sizeof(len));
156 if (rc != sizeof(len))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930157 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +1030158 std::cerr << "Failed to write block (" << std::dec << len
Ed Tanous73030632022-01-14 10:09:47 -0800159 << ") length to out descriptor: "
160 << strerror(static_cast<int>(-rc)) << "\n";
161 if (rc)
162 {
163 return -errno;
164 }
165 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930166 }
167
168 /* Write out the response data */
Ed Tanous73030632022-01-14 10:09:47 -0800169 std::vector<uint8_t>::iterator cursor = resp.begin();
170 while (cursor != resp.end())
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930171 {
Ed Tanous73030632022-01-14 10:09:47 -0800172 size_t lenRemaining = std::distance(cursor, resp.end());
173 ssize_t egress = ::write(out.handle(), &(*cursor), lenRemaining);
Ed Tanousa771f6a2022-01-14 09:36:51 -0800174 if (egress == -1)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930175 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +1030176 std::cerr << "Failed to write block data of length " << std::dec
Ed Tanous73030632022-01-14 10:09:47 -0800177 << lenRemaining << " to out pipe: " << strerror(errno)
178 << "\n";
179 if (rc)
180 {
181 return -errno;
182 }
183 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930184 }
185
186 cursor += egress;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930187 }
188 }
189
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930190 return rc;
191}
192
193/* Throws std::error_code on failure */
194/* FIXME: Probably shouldn't do fallible stuff in a constructor */
195NVMeBasicContext::NVMeBasicContext(boost::asio::io_service& io, int rootBus) :
196 NVMeContext::NVMeContext(io, rootBus), io(io), reqStream(io), respStream(io)
197{
Ed Tanousa771f6a2022-01-14 09:36:51 -0800198 std::array<int, 2> responsePipe{};
199 std::array<int, 2> requestPipe{};
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930200
201 /* Set up inter-thread communication */
202 if (::pipe(requestPipe.data()) == -1)
203 {
204 std::cerr << "Failed to create request pipe: " << strerror(errno)
205 << "\n";
206 throw std::error_code(errno, std::system_category());
207 }
208
209 if (::pipe(responsePipe.data()) == -1)
210 {
211 std::cerr << "Failed to create response pipe: " << strerror(errno)
212 << "\n";
213
214 if (::close(requestPipe[0]) == -1)
215 {
216 std::cerr << "Failed to close write fd of request pipe: "
217 << strerror(errno) << "\n";
218 }
219
220 if (::close(requestPipe[1]) == -1)
221 {
222 std::cerr << "Failed to close read fd of request pipe: "
223 << strerror(errno) << "\n";
224 }
225
226 throw std::error_code(errno, std::system_category());
227 }
228
229 reqStream.assign(requestPipe[1]);
Ed Tanous73030632022-01-14 10:09:47 -0800230 FileHandle streamIn(requestPipe[0]);
231 FileHandle streamOut(responsePipe[1]);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930232 respStream.assign(responsePipe[0]);
233
Ed Tanous73030632022-01-14 10:09:47 -0800234 std::thread thread([streamIn{std::move(streamIn)},
235 streamOut{std::move(streamOut)}]() mutable {
Ed Tanousa771f6a2022-01-14 09:36:51 -0800236 ssize_t rc = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930237
238 if ((rc = processBasicQueryStream(streamIn, streamOut)) < 0)
239 {
240 std::cerr << "Failure while processing query stream: "
Ed Tanousa771f6a2022-01-14 09:36:51 -0800241 << strerror(static_cast<int>(-rc)) << "\n";
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930242 }
243
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930244 std::cerr << "Terminating basic query thread\n";
245 });
246 thread.detach();
247}
248
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930249void NVMeBasicContext::readAndProcessNVMeSensor()
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930250{
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930251 if (pollCursor == sensors.end())
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930252 {
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030253 this->pollNVMeDevices();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930254 return;
255 }
256
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930257 std::shared_ptr<NVMeSensor> sensor = *pollCursor++;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930258
Andrew Jeffery3859c7f2021-10-29 15:51:16 +1030259 if (!sensor->readingStateGood())
260 {
261 sensor->markAvailable(false);
262 sensor->updateValue(std::numeric_limits<double>::quiet_NaN());
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930263 readAndProcessNVMeSensor();
Andrew Jeffery3859c7f2021-10-29 15:51:16 +1030264 return;
265 }
266
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030267 /* Potentially defer sampling the sensor sensor if it is in error */
268 if (!sensor->sample())
269 {
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930270 readAndProcessNVMeSensor();
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030271 return;
272 }
273
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930274 auto command = encodeBasicQuery(sensor->bus, 0x6a, 0x00);
275
276 /* Issue the request */
277 boost::asio::async_write(
278 reqStream, boost::asio::buffer(command->data(), command->size()),
Ed Tanous76b2bc72022-02-18 09:48:16 -0800279 [command](boost::system::error_code ec, std::size_t) {
Ed Tanousbb679322022-05-16 16:10:00 -0700280 if (ec)
281 {
282 std::cerr << "Got error writing basic query: " << ec << "\n";
283 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930284 });
285
Andrew Jeffery84c16872022-03-15 21:50:59 +1030286 auto response = std::make_shared<boost::asio::streambuf>();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930287 response->prepare(1);
288
289 /* Gather the response and dispatch for parsing */
290 boost::asio::async_read(
291 respStream, *response,
292 [response](const boost::system::error_code& ec, std::size_t n) {
Ed Tanousbb679322022-05-16 16:10:00 -0700293 if (ec)
294 {
295 std::cerr << "Got error completing basic query: " << ec << "\n";
296 return static_cast<std::size_t>(0);
297 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930298
Ed Tanousbb679322022-05-16 16:10:00 -0700299 if (n == 0)
300 {
301 return static_cast<std::size_t>(1);
302 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930303
Ed Tanousbb679322022-05-16 16:10:00 -0700304 std::istream is(response.get());
305 size_t len = static_cast<std::size_t>(is.peek());
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930306
Ed Tanousbb679322022-05-16 16:10:00 -0700307 if (n > len + 1)
308 {
309 std::cerr << "Query stream has become unsynchronised: "
310 << "n: " << n << ", "
311 << "len: " << len << "\n";
312 return static_cast<std::size_t>(0);
313 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930314
Ed Tanousbb679322022-05-16 16:10:00 -0700315 if (n == len + 1)
316 {
317 return static_cast<std::size_t>(0);
318 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930319
Ed Tanousbb679322022-05-16 16:10:00 -0700320 if (n > 1)
321 {
322 return len + 1 - n;
323 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930324
Ed Tanousbb679322022-05-16 16:10:00 -0700325 response->prepare(len);
326 return len;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930327 },
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930328 [self{shared_from_this()}, sensor, response](
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030329 const boost::system::error_code& ec, std::size_t length) mutable {
Ed Tanousbb679322022-05-16 16:10:00 -0700330 if (ec)
331 {
332 std::cerr << "Got error reading basic query: " << ec << "\n";
333 return;
334 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930335
Ed Tanousbb679322022-05-16 16:10:00 -0700336 if (length == 0)
337 {
338 std::cerr << "Invalid message length: " << length << "\n";
339 return;
340 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930341
Ed Tanousbb679322022-05-16 16:10:00 -0700342 /* Deserialise the response */
343 response->consume(1); /* Drop the length byte */
344 std::istream is(response.get());
345 std::vector<char> data(response->size());
346 is.read(data.data(), response->size());
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930347
Ed Tanousbb679322022-05-16 16:10:00 -0700348 /* Update the sensor */
349 self->processResponse(sensor, data.data(), data.size());
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030350
Ed Tanousbb679322022-05-16 16:10:00 -0700351 /* Enqueue processing of the next sensor */
352 self->readAndProcessNVMeSensor();
353 });
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930354}
355
356void NVMeBasicContext::pollNVMeDevices()
357{
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930358 pollCursor = sensors.begin();
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030359
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930360 scanTimer.expires_from_now(boost::posix_time::seconds(1));
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930361 scanTimer.async_wait(
362 [self{shared_from_this()}](const boost::system::error_code errorCode) {
Ed Tanousbb679322022-05-16 16:10:00 -0700363 if (errorCode == boost::asio::error::operation_aborted)
364 {
365 return;
366 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930367
Ed Tanousbb679322022-05-16 16:10:00 -0700368 if (errorCode)
369 {
370 std::cerr << errorCode.message() << "\n";
371 return;
372 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930373
Ed Tanousbb679322022-05-16 16:10:00 -0700374 self->readAndProcessNVMeSensor();
375 });
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930376}
377
378static double getTemperatureReading(int8_t reading)
379{
380 if (reading == static_cast<int8_t>(0x80) ||
381 reading == static_cast<int8_t>(0x81))
382 {
383 // 0x80 = No temperature data or temperature data is more the 5 s
384 // old 0x81 = Temperature sensor failure
385 return std::numeric_limits<double>::quiet_NaN();
386 }
387
388 return reading;
389}
390
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030391void NVMeBasicContext::processResponse(std::shared_ptr<NVMeSensor>& sensor,
392 void* msg, size_t len)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930393{
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030394 if (msg == nullptr || len < 6)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930395 {
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030396 sensor->incrementError();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930397 return;
398 }
399
400 uint8_t* messageData = static_cast<uint8_t*>(msg);
Andrew Jeffery7aeb1a52022-03-15 22:49:04 +1030401
402 uint8_t status = messageData[0];
403 if ((status & NVME_MI_BASIC_SFLGS_DRIVE_NOT_READY) ||
404 !(status & NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL))
405 {
406 sensor->markFunctional(false);
407 return;
408 }
409
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930410 double value = getTemperatureReading(messageData[2]);
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030411 if (!std::isfinite(value))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930412 {
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930413 sensor->incrementError();
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030414 return;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930415 }
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030416
417 sensor->updateValue(value);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930418}