blob: 29dab7acae78f077772685bb4595208fe0c51737 [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 Jeffery14108bb2022-03-21 15:00:16 +1030266 /* Potentially defer sampling the sensor sensor if it is in error */
267 if (!sensor->sample())
268 {
269 readAndProcessNVMeSensor(iter);
270 return;
271 }
272
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930273 auto command = encodeBasicQuery(sensor->bus, 0x6a, 0x00);
274
275 /* Issue the request */
276 boost::asio::async_write(
277 reqStream, boost::asio::buffer(command->data(), command->size()),
Ed Tanous76b2bc72022-02-18 09:48:16 -0800278 [command](boost::system::error_code ec, std::size_t) {
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930279 if (ec)
280 {
281 std::cerr << "Got error writing basic query: " << ec << "\n";
282 }
283 });
284
Andrew Jeffery84c16872022-03-15 21:50:59 +1030285 auto response = std::make_shared<boost::asio::streambuf>();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930286 response->prepare(1);
287
288 /* Gather the response and dispatch for parsing */
289 boost::asio::async_read(
290 respStream, *response,
291 [response](const boost::system::error_code& ec, std::size_t n) {
292 if (ec)
293 {
294 std::cerr << "Got error completing basic query: " << ec << "\n";
295 return static_cast<std::size_t>(0);
296 }
297
298 if (n == 0)
299 {
300 return static_cast<std::size_t>(1);
301 }
302
303 std::istream is(response.get());
304 size_t len = static_cast<std::size_t>(is.peek());
305
306 if (n > len + 1)
307 {
308 std::cerr << "Query stream has become unsynchronised: "
309 << "n: " << n << ", "
310 << "len: " << len << "\n";
311 return static_cast<std::size_t>(0);
312 }
313
314 if (n == len + 1)
315 {
316 return static_cast<std::size_t>(0);
317 }
318
319 if (n > 1)
320 {
321 return len + 1 - n;
322 }
323
324 response->prepare(len);
325 return len;
326 },
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030327 [self{shared_from_this()}, iter, sensor, response](
328 const boost::system::error_code& ec, std::size_t length) mutable {
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930329 if (ec)
330 {
331 std::cerr << "Got error reading basic query: " << ec << "\n";
332 return;
333 }
334
335 if (length == 0)
336 {
337 std::cerr << "Invalid message length: " << length << "\n";
338 return;
339 }
340
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930341 /* Deserialise the response */
342 response->consume(1); /* Drop the length byte */
343 std::istream is(response.get());
344 std::vector<char> data(response->size());
345 is.read(data.data(), response->size());
346
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030347 /* Update the sensor */
348 self->processResponse(sensor, data.data(), data.size());
349
350 /* Enqueue processing of the next sensor */
351 self->readAndProcessNVMeSensor(iter);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930352 });
353}
354
355void NVMeBasicContext::pollNVMeDevices()
356{
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030357 auto scan = sensors.begin();
358
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930359 scanTimer.expires_from_now(boost::posix_time::seconds(1));
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030360 scanTimer.async_wait([self{shared_from_this()},
361 scan](const boost::system::error_code errorCode) {
362 if (errorCode == boost::asio::error::operation_aborted)
363 {
364 return;
365 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930366
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030367 if (errorCode)
368 {
369 std::cerr << errorCode.message() << "\n";
370 return;
371 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930372
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030373 self->readAndProcessNVMeSensor(scan);
374 });
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930375}
376
377static double getTemperatureReading(int8_t reading)
378{
379 if (reading == static_cast<int8_t>(0x80) ||
380 reading == static_cast<int8_t>(0x81))
381 {
382 // 0x80 = No temperature data or temperature data is more the 5 s
383 // old 0x81 = Temperature sensor failure
384 return std::numeric_limits<double>::quiet_NaN();
385 }
386
387 return reading;
388}
389
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030390void NVMeBasicContext::processResponse(std::shared_ptr<NVMeSensor>& sensor,
391 void* msg, size_t len)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930392{
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030393 if (msg == nullptr || len < 6)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930394 {
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030395 sensor->incrementError();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930396 return;
397 }
398
399 uint8_t* messageData = static_cast<uint8_t*>(msg);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930400 double value = getTemperatureReading(messageData[2]);
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030401 if (!std::isfinite(value))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930402 {
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930403 sensor->incrementError();
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030404 return;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930405 }
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030406
407 sensor->updateValue(value);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930408}