blob: 09b58be5a14c7898b6c033609de4decb3168b1db [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
Andrew Jeffery1a143022022-07-19 14:18:24 +093062static void execBasicQuery(int bus, uint8_t addr, uint8_t cmd,
63 std::vector<uint8_t>& resp)
Andrew Jefferye3e3c972021-05-26 14:37:07 +093064{
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";
Andrew Jeffery1a143022022-07-19 14:18:24 +093076 return;
Andrew Jefferye3e3c972021-05-26 14:37:07 +093077 }
78
Andrew Jefferya7afacc2022-03-31 11:21:28 +103079 resp.resize(UINT8_MAX + 1);
Andrew Jefferye3e3c972021-05-26 14:37:07 +093080
81 /* Issue the NVMe MI basic command */
Ed Tanous73030632022-01-14 10:09:47 -080082 size = i2c_smbus_read_block_data(fileHandle.handle(), cmd, resp.data());
Andrew Jefferye3e3c972021-05-26 14:37:07 +093083 if (size < 0)
84 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +103085 std::cerr << "Failed to read block data from device 0x" << std::hex
86 << (int)addr << " on bus " << std::dec << bus << ": "
87 << strerror(errno) << "\n";
Andrew Jeffery1a143022022-07-19 14:18:24 +093088 resp.resize(0);
Andrew Jefferye3e3c972021-05-26 14:37:07 +093089 }
Andrew Jeffery1a143022022-07-19 14:18:24 +093090 else if (size > UINT8_MAX + 1)
Andrew Jefferye3e3c972021-05-26 14:37:07 +093091 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +103092 std::cerr << "Unexpected message length from device 0x" << std::hex
93 << (int)addr << " on bus " << std::dec << bus << ": " << size
94 << " (" << UINT8_MAX << ")\n";
Andrew Jeffery1a143022022-07-19 14:18:24 +093095 resp.resize(0);
Andrew Jefferye3e3c972021-05-26 14:37:07 +093096 }
Andrew Jeffery1a143022022-07-19 14:18:24 +093097 else
98 {
99 resp.resize(size);
100 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930101}
102
Ed Tanous73030632022-01-14 10:09:47 -0800103static ssize_t processBasicQueryStream(FileHandle& in, FileHandle& out)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930104{
105 std::vector<uint8_t> resp{};
Ed Tanousa771f6a2022-01-14 09:36:51 -0800106 ssize_t rc = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930107
108 while (true)
109 {
Ed Tanousa771f6a2022-01-14 09:36:51 -0800110 uint8_t device = 0;
111 uint8_t offset = 0;
112 uint8_t len = 0;
113 int bus = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930114
115 /* bus + address + command */
116 std::array<uint8_t, sizeof(uint32_t) + 1 + 1> req{};
117
118 /* Read the command parameters */
Ed Tanous73030632022-01-14 10:09:47 -0800119 ssize_t rc = ::read(in.handle(), req.data(), req.size());
120 if (rc != static_cast<ssize_t>(req.size()))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930121 {
Ed Tanous73030632022-01-14 10:09:47 -0800122 std::cerr << "Failed to read request from in descriptor "
123 << strerror(errno) << "\n";
Ed Tanous2049bd22022-07-09 07:20:26 -0700124 if (rc != 0)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930125 {
Ed Tanous73030632022-01-14 10:09:47 -0800126 return -errno;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930127 }
Ed Tanous73030632022-01-14 10:09:47 -0800128 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930129 }
130
131 decodeBasicQuery(req, bus, device, offset);
132
133 /* Execute the query */
Andrew Jeffery1a143022022-07-19 14:18:24 +0930134 execBasicQuery(bus, device, offset, resp);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930135
136 /* Write out the response length */
Andrew Jeffery1a143022022-07-19 14:18:24 +0930137 len = resp.size();
Ed Tanous73030632022-01-14 10:09:47 -0800138 rc = ::write(out.handle(), &len, sizeof(len));
139 if (rc != sizeof(len))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930140 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +1030141 std::cerr << "Failed to write block (" << std::dec << len
Ed Tanous73030632022-01-14 10:09:47 -0800142 << ") length to out descriptor: "
143 << strerror(static_cast<int>(-rc)) << "\n";
Ed Tanous2049bd22022-07-09 07:20:26 -0700144 if (rc != 0)
Ed Tanous73030632022-01-14 10:09:47 -0800145 {
146 return -errno;
147 }
148 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930149 }
150
151 /* Write out the response data */
Ed Tanous73030632022-01-14 10:09:47 -0800152 std::vector<uint8_t>::iterator cursor = resp.begin();
153 while (cursor != resp.end())
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930154 {
Ed Tanous73030632022-01-14 10:09:47 -0800155 size_t lenRemaining = std::distance(cursor, resp.end());
156 ssize_t egress = ::write(out.handle(), &(*cursor), lenRemaining);
Ed Tanousa771f6a2022-01-14 09:36:51 -0800157 if (egress == -1)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930158 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +1030159 std::cerr << "Failed to write block data of length " << std::dec
Ed Tanous73030632022-01-14 10:09:47 -0800160 << lenRemaining << " to out pipe: " << strerror(errno)
161 << "\n";
Ed Tanous2049bd22022-07-09 07:20:26 -0700162 if (rc != 0)
Ed Tanous73030632022-01-14 10:09:47 -0800163 {
164 return -errno;
165 }
166 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930167 }
168
169 cursor += egress;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930170 }
171 }
172
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930173 return rc;
174}
175
176/* Throws std::error_code on failure */
177/* FIXME: Probably shouldn't do fallible stuff in a constructor */
178NVMeBasicContext::NVMeBasicContext(boost::asio::io_service& io, int rootBus) :
179 NVMeContext::NVMeContext(io, rootBus), io(io), reqStream(io), respStream(io)
180{
Ed Tanousa771f6a2022-01-14 09:36:51 -0800181 std::array<int, 2> responsePipe{};
182 std::array<int, 2> requestPipe{};
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930183
184 /* Set up inter-thread communication */
185 if (::pipe(requestPipe.data()) == -1)
186 {
187 std::cerr << "Failed to create request pipe: " << strerror(errno)
188 << "\n";
189 throw std::error_code(errno, std::system_category());
190 }
191
192 if (::pipe(responsePipe.data()) == -1)
193 {
194 std::cerr << "Failed to create response pipe: " << strerror(errno)
195 << "\n";
196
197 if (::close(requestPipe[0]) == -1)
198 {
199 std::cerr << "Failed to close write fd of request pipe: "
200 << strerror(errno) << "\n";
201 }
202
203 if (::close(requestPipe[1]) == -1)
204 {
205 std::cerr << "Failed to close read fd of request pipe: "
206 << strerror(errno) << "\n";
207 }
208
209 throw std::error_code(errno, std::system_category());
210 }
211
212 reqStream.assign(requestPipe[1]);
Ed Tanous73030632022-01-14 10:09:47 -0800213 FileHandle streamIn(requestPipe[0]);
214 FileHandle streamOut(responsePipe[1]);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930215 respStream.assign(responsePipe[0]);
216
Ed Tanous73030632022-01-14 10:09:47 -0800217 std::thread thread([streamIn{std::move(streamIn)},
218 streamOut{std::move(streamOut)}]() mutable {
Ed Tanousa771f6a2022-01-14 09:36:51 -0800219 ssize_t rc = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930220
221 if ((rc = processBasicQueryStream(streamIn, streamOut)) < 0)
222 {
223 std::cerr << "Failure while processing query stream: "
Ed Tanousa771f6a2022-01-14 09:36:51 -0800224 << strerror(static_cast<int>(-rc)) << "\n";
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930225 }
226
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930227 std::cerr << "Terminating basic query thread\n";
228 });
229 thread.detach();
230}
231
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930232void NVMeBasicContext::readAndProcessNVMeSensor()
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930233{
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930234 if (pollCursor == sensors.end())
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930235 {
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030236 this->pollNVMeDevices();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930237 return;
238 }
239
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930240 std::shared_ptr<NVMeSensor> sensor = *pollCursor++;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930241
Andrew Jeffery3859c7f2021-10-29 15:51:16 +1030242 if (!sensor->readingStateGood())
243 {
244 sensor->markAvailable(false);
245 sensor->updateValue(std::numeric_limits<double>::quiet_NaN());
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930246 readAndProcessNVMeSensor();
Andrew Jeffery3859c7f2021-10-29 15:51:16 +1030247 return;
248 }
249
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030250 /* Potentially defer sampling the sensor sensor if it is in error */
251 if (!sensor->sample())
252 {
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930253 readAndProcessNVMeSensor();
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030254 return;
255 }
256
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930257 auto command = encodeBasicQuery(sensor->bus, 0x6a, 0x00);
258
259 /* Issue the request */
260 boost::asio::async_write(
261 reqStream, boost::asio::buffer(command->data(), command->size()),
Ed Tanous76b2bc72022-02-18 09:48:16 -0800262 [command](boost::system::error_code ec, std::size_t) {
Ed Tanousbb679322022-05-16 16:10:00 -0700263 if (ec)
264 {
265 std::cerr << "Got error writing basic query: " << ec << "\n";
266 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930267 });
268
Andrew Jeffery84c16872022-03-15 21:50:59 +1030269 auto response = std::make_shared<boost::asio::streambuf>();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930270 response->prepare(1);
271
272 /* Gather the response and dispatch for parsing */
273 boost::asio::async_read(
274 respStream, *response,
275 [response](const boost::system::error_code& ec, std::size_t n) {
Ed Tanousbb679322022-05-16 16:10:00 -0700276 if (ec)
277 {
278 std::cerr << "Got error completing basic query: " << ec << "\n";
279 return static_cast<std::size_t>(0);
280 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930281
Ed Tanousbb679322022-05-16 16:10:00 -0700282 if (n == 0)
283 {
284 return static_cast<std::size_t>(1);
285 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930286
Ed Tanousbb679322022-05-16 16:10:00 -0700287 std::istream is(response.get());
288 size_t len = static_cast<std::size_t>(is.peek());
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930289
Ed Tanousbb679322022-05-16 16:10:00 -0700290 if (n > len + 1)
291 {
292 std::cerr << "Query stream has become unsynchronised: "
293 << "n: " << n << ", "
294 << "len: " << len << "\n";
295 return static_cast<std::size_t>(0);
296 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930297
Ed Tanousbb679322022-05-16 16:10:00 -0700298 if (n == len + 1)
299 {
300 return static_cast<std::size_t>(0);
301 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930302
Ed Tanousbb679322022-05-16 16:10:00 -0700303 if (n > 1)
304 {
305 return len + 1 - n;
306 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930307
Ed Tanousbb679322022-05-16 16:10:00 -0700308 response->prepare(len);
309 return len;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930310 },
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930311 [self{shared_from_this()}, sensor, response](
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030312 const boost::system::error_code& ec, std::size_t length) mutable {
Ed Tanousbb679322022-05-16 16:10:00 -0700313 if (ec)
314 {
315 std::cerr << "Got error reading basic query: " << ec << "\n";
316 return;
317 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930318
Ed Tanousbb679322022-05-16 16:10:00 -0700319 if (length == 0)
320 {
321 std::cerr << "Invalid message length: " << length << "\n";
322 return;
323 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930324
Ed Tanousbb679322022-05-16 16:10:00 -0700325 /* Deserialise the response */
326 response->consume(1); /* Drop the length byte */
327 std::istream is(response.get());
328 std::vector<char> data(response->size());
329 is.read(data.data(), response->size());
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930330
Ed Tanousbb679322022-05-16 16:10:00 -0700331 /* Update the sensor */
332 self->processResponse(sensor, data.data(), data.size());
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030333
Ed Tanousbb679322022-05-16 16:10:00 -0700334 /* Enqueue processing of the next sensor */
335 self->readAndProcessNVMeSensor();
336 });
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930337}
338
339void NVMeBasicContext::pollNVMeDevices()
340{
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930341 pollCursor = sensors.begin();
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030342
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930343 scanTimer.expires_from_now(boost::posix_time::seconds(1));
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930344 scanTimer.async_wait(
345 [self{shared_from_this()}](const boost::system::error_code errorCode) {
Ed Tanousbb679322022-05-16 16:10:00 -0700346 if (errorCode == boost::asio::error::operation_aborted)
347 {
348 return;
349 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930350
Ed Tanousbb679322022-05-16 16:10:00 -0700351 if (errorCode)
352 {
353 std::cerr << errorCode.message() << "\n";
354 return;
355 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930356
Ed Tanousbb679322022-05-16 16:10:00 -0700357 self->readAndProcessNVMeSensor();
358 });
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930359}
360
361static double getTemperatureReading(int8_t reading)
362{
363 if (reading == static_cast<int8_t>(0x80) ||
364 reading == static_cast<int8_t>(0x81))
365 {
366 // 0x80 = No temperature data or temperature data is more the 5 s
367 // old 0x81 = Temperature sensor failure
368 return std::numeric_limits<double>::quiet_NaN();
369 }
370
371 return reading;
372}
373
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030374void NVMeBasicContext::processResponse(std::shared_ptr<NVMeSensor>& sensor,
375 void* msg, size_t len)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930376{
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030377 if (msg == nullptr || len < 6)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930378 {
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030379 sensor->incrementError();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930380 return;
381 }
382
383 uint8_t* messageData = static_cast<uint8_t*>(msg);
Andrew Jeffery7aeb1a52022-03-15 22:49:04 +1030384
385 uint8_t status = messageData[0];
Ed Tanous2049bd22022-07-09 07:20:26 -0700386 if (((status & NVME_MI_BASIC_SFLGS_DRIVE_NOT_READY) != 0) ||
387 ((status & NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL) == 0))
Andrew Jeffery7aeb1a52022-03-15 22:49:04 +1030388 {
389 sensor->markFunctional(false);
390 return;
391 }
392
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930393 double value = getTemperatureReading(messageData[2]);
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030394 if (!std::isfinite(value))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930395 {
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930396 sensor->incrementError();
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030397 return;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930398 }
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030399
400 sensor->updateValue(value);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930401}