blob: c755e23464c8cf49fdbf118e705c4869a8ba9bef [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
80 resp.reserve(UINT8_MAX + 1);
81
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
Ed Tanous73030632022-01-14 10:09:47 -080099 return size;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930100}
101
Ed Tanous73030632022-01-14 10:09:47 -0800102static ssize_t processBasicQueryStream(FileHandle& in, FileHandle& out)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930103{
104 std::vector<uint8_t> resp{};
Ed Tanousa771f6a2022-01-14 09:36:51 -0800105 ssize_t rc = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930106
107 while (true)
108 {
Ed Tanousa771f6a2022-01-14 09:36:51 -0800109 uint8_t device = 0;
110 uint8_t offset = 0;
111 uint8_t len = 0;
112 int bus = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930113
114 /* bus + address + command */
115 std::array<uint8_t, sizeof(uint32_t) + 1 + 1> req{};
116
117 /* Read the command parameters */
Ed Tanous73030632022-01-14 10:09:47 -0800118 ssize_t rc = ::read(in.handle(), req.data(), req.size());
119 if (rc != static_cast<ssize_t>(req.size()))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930120 {
Ed Tanous73030632022-01-14 10:09:47 -0800121 std::cerr << "Failed to read request from in descriptor "
122 << strerror(errno) << "\n";
123 if (rc)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930124 {
Ed Tanous73030632022-01-14 10:09:47 -0800125 return -errno;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930126 }
Ed Tanous73030632022-01-14 10:09:47 -0800127 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930128 }
129
130 decodeBasicQuery(req, bus, device, offset);
131
132 /* Execute the query */
133 rc = execBasicQuery(bus, device, offset, resp);
134
135 /* Bounds check the response */
136 if (rc < 0)
137 {
138 len = 0;
139 }
140 else if (rc > UINT8_MAX)
141 {
142 assert(rc == UINT8_MAX + 1);
143
144 /* YOLO: Lop off the PEC */
145 len = UINT8_MAX;
146 }
147 else
148 {
149 len = rc;
150 }
151
152 /* Write out the response length */
Ed Tanous73030632022-01-14 10:09:47 -0800153 rc = ::write(out.handle(), &len, sizeof(len));
154 if (rc != sizeof(len))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930155 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +1030156 std::cerr << "Failed to write block (" << std::dec << len
Ed Tanous73030632022-01-14 10:09:47 -0800157 << ") length to out descriptor: "
158 << strerror(static_cast<int>(-rc)) << "\n";
159 if (rc)
160 {
161 return -errno;
162 }
163 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930164 }
165
166 /* Write out the response data */
Ed Tanous73030632022-01-14 10:09:47 -0800167 std::vector<uint8_t>::iterator cursor = resp.begin();
168 while (cursor != resp.end())
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930169 {
Ed Tanous73030632022-01-14 10:09:47 -0800170 size_t lenRemaining = std::distance(cursor, resp.end());
171 ssize_t egress = ::write(out.handle(), &(*cursor), lenRemaining);
Ed Tanousa771f6a2022-01-14 09:36:51 -0800172 if (egress == -1)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930173 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +1030174 std::cerr << "Failed to write block data of length " << std::dec
Ed Tanous73030632022-01-14 10:09:47 -0800175 << lenRemaining << " to out pipe: " << strerror(errno)
176 << "\n";
177 if (rc)
178 {
179 return -errno;
180 }
181 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930182 }
183
184 cursor += egress;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930185 }
186 }
187
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930188 return rc;
189}
190
191/* Throws std::error_code on failure */
192/* FIXME: Probably shouldn't do fallible stuff in a constructor */
193NVMeBasicContext::NVMeBasicContext(boost::asio::io_service& io, int rootBus) :
194 NVMeContext::NVMeContext(io, rootBus), io(io), reqStream(io), respStream(io)
195{
Ed Tanousa771f6a2022-01-14 09:36:51 -0800196 std::array<int, 2> responsePipe{};
197 std::array<int, 2> requestPipe{};
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930198
199 /* Set up inter-thread communication */
200 if (::pipe(requestPipe.data()) == -1)
201 {
202 std::cerr << "Failed to create request pipe: " << strerror(errno)
203 << "\n";
204 throw std::error_code(errno, std::system_category());
205 }
206
207 if (::pipe(responsePipe.data()) == -1)
208 {
209 std::cerr << "Failed to create response pipe: " << strerror(errno)
210 << "\n";
211
212 if (::close(requestPipe[0]) == -1)
213 {
214 std::cerr << "Failed to close write fd of request pipe: "
215 << strerror(errno) << "\n";
216 }
217
218 if (::close(requestPipe[1]) == -1)
219 {
220 std::cerr << "Failed to close read fd of request pipe: "
221 << strerror(errno) << "\n";
222 }
223
224 throw std::error_code(errno, std::system_category());
225 }
226
227 reqStream.assign(requestPipe[1]);
Ed Tanous73030632022-01-14 10:09:47 -0800228 FileHandle streamIn(requestPipe[0]);
229 FileHandle streamOut(responsePipe[1]);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930230 respStream.assign(responsePipe[0]);
231
Ed Tanous73030632022-01-14 10:09:47 -0800232 std::thread thread([streamIn{std::move(streamIn)},
233 streamOut{std::move(streamOut)}]() mutable {
Ed Tanousa771f6a2022-01-14 09:36:51 -0800234 ssize_t rc = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930235
236 if ((rc = processBasicQueryStream(streamIn, streamOut)) < 0)
237 {
238 std::cerr << "Failure while processing query stream: "
Ed Tanousa771f6a2022-01-14 09:36:51 -0800239 << strerror(static_cast<int>(-rc)) << "\n";
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930240 }
241
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930242 std::cerr << "Terminating basic query thread\n";
243 });
244 thread.detach();
245}
246
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030247void NVMeBasicContext::readAndProcessNVMeSensor(
248 std::list<std::shared_ptr<NVMeSensor>>::iterator iter)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930249{
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030250 if (iter == sensors.end())
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930251 {
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030252 this->pollNVMeDevices();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930253 return;
254 }
255
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030256 std::shared_ptr<NVMeSensor> sensor = *iter++;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930257
Andrew Jeffery3859c7f2021-10-29 15:51:16 +1030258 if (!sensor->readingStateGood())
259 {
260 sensor->markAvailable(false);
261 sensor->updateValue(std::numeric_limits<double>::quiet_NaN());
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030262 readAndProcessNVMeSensor(iter);
Andrew Jeffery3859c7f2021-10-29 15:51:16 +1030263 return;
264 }
265
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930266 auto command = encodeBasicQuery(sensor->bus, 0x6a, 0x00);
267
268 /* Issue the request */
269 boost::asio::async_write(
270 reqStream, boost::asio::buffer(command->data(), command->size()),
Ed Tanous76b2bc72022-02-18 09:48:16 -0800271 [command](boost::system::error_code ec, std::size_t) {
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930272 if (ec)
273 {
274 std::cerr << "Got error writing basic query: " << ec << "\n";
275 }
276 });
277
Andrew Jeffery84c16872022-03-15 21:50:59 +1030278 auto response = std::make_shared<boost::asio::streambuf>();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930279 response->prepare(1);
280
281 /* Gather the response and dispatch for parsing */
282 boost::asio::async_read(
283 respStream, *response,
284 [response](const boost::system::error_code& ec, std::size_t n) {
285 if (ec)
286 {
287 std::cerr << "Got error completing basic query: " << ec << "\n";
288 return static_cast<std::size_t>(0);
289 }
290
291 if (n == 0)
292 {
293 return static_cast<std::size_t>(1);
294 }
295
296 std::istream is(response.get());
297 size_t len = static_cast<std::size_t>(is.peek());
298
299 if (n > len + 1)
300 {
301 std::cerr << "Query stream has become unsynchronised: "
302 << "n: " << n << ", "
303 << "len: " << len << "\n";
304 return static_cast<std::size_t>(0);
305 }
306
307 if (n == len + 1)
308 {
309 return static_cast<std::size_t>(0);
310 }
311
312 if (n > 1)
313 {
314 return len + 1 - n;
315 }
316
317 response->prepare(len);
318 return len;
319 },
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030320 [self{shared_from_this()}, iter, sensor, response](
321 const boost::system::error_code& ec, std::size_t length) mutable {
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930322 if (ec)
323 {
324 std::cerr << "Got error reading basic query: " << ec << "\n";
325 return;
326 }
327
328 if (length == 0)
329 {
330 std::cerr << "Invalid message length: " << length << "\n";
331 return;
332 }
333
334 if (length == 1)
335 {
336 std::cerr << "Basic query failed\n";
337 return;
338 }
339
340 /* Deserialise the response */
341 response->consume(1); /* Drop the length byte */
342 std::istream is(response.get());
343 std::vector<char> data(response->size());
344 is.read(data.data(), response->size());
345
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030346 /* Update the sensor */
347 self->processResponse(sensor, data.data(), data.size());
348
349 /* Enqueue processing of the next sensor */
350 self->readAndProcessNVMeSensor(iter);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930351 });
352}
353
354void NVMeBasicContext::pollNVMeDevices()
355{
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030356 auto scan = sensors.begin();
357
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930358 scanTimer.expires_from_now(boost::posix_time::seconds(1));
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030359 scanTimer.async_wait([self{shared_from_this()},
360 scan](const boost::system::error_code errorCode) {
361 if (errorCode == boost::asio::error::operation_aborted)
362 {
363 return;
364 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930365
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030366 if (errorCode)
367 {
368 std::cerr << errorCode.message() << "\n";
369 return;
370 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930371
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030372 self->readAndProcessNVMeSensor(scan);
373 });
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930374}
375
376static double getTemperatureReading(int8_t reading)
377{
378 if (reading == static_cast<int8_t>(0x80) ||
379 reading == static_cast<int8_t>(0x81))
380 {
381 // 0x80 = No temperature data or temperature data is more the 5 s
382 // old 0x81 = Temperature sensor failure
383 return std::numeric_limits<double>::quiet_NaN();
384 }
385
386 return reading;
387}
388
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030389void NVMeBasicContext::processResponse(std::shared_ptr<NVMeSensor>& sensor,
390 void* msg, size_t len)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930391{
392 if (msg == nullptr)
393 {
394 std::cerr << "Bad message received\n";
395 return;
396 }
397
398 if (len < 6)
399 {
400 std::cerr << "Invalid message length: " << len << "\n";
401 return;
402 }
403
404 uint8_t* messageData = static_cast<uint8_t*>(msg);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930405 double value = getTemperatureReading(messageData[2]);
406
407 if (std::isfinite(value))
408 {
409 sensor->updateValue(value);
410 }
411 else
412 {
413 sensor->markAvailable(false);
414 sensor->incrementError();
415 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930416}