blob: 322c13388a19e5c086964cea351018019415d91f [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>
Andrew Jefferye3e3c972021-05-26 14:37:07 +093018
19extern "C"
20{
21#include <i2c/smbus.h>
22#include <linux/i2c-dev.h>
23}
24
25/*
26 * NVMe-MI Basic Management Command
27 *
28 * https://nvmexpress.org/wp-content/uploads/NVMe_Management_-_Technical_Note_on_Basic_Management_Command.pdf
29 */
30
31static std::shared_ptr<std::array<uint8_t, 6>>
32 encodeBasicQuery(int bus, uint8_t device, uint8_t offset)
33{
34 if (bus < 0)
35 {
36 throw std::domain_error("Invalid bus argument");
37 }
38
39 /* bus + address + command */
40 uint32_t busle = htole32(static_cast<uint32_t>(bus));
41 auto command =
42 std::make_shared<std::array<uint8_t, sizeof(busle) + 1 + 1>>();
43 memcpy(command->data(), &busle, sizeof(busle));
44 (*command)[sizeof(busle) + 0] = device;
45 (*command)[sizeof(busle) + 1] = offset;
46
47 return command;
48}
49
50static void decodeBasicQuery(const std::array<uint8_t, 6>& req, int& bus,
51 uint8_t& device, uint8_t& offset)
52{
Ed Tanousa771f6a2022-01-14 09:36:51 -080053 uint32_t busle = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +093054
55 memcpy(&busle, req.data(), sizeof(busle));
56 bus = le32toh(busle);
57 device = req[sizeof(busle) + 0];
58 offset = req[sizeof(busle) + 1];
59}
60
Andrew Jeffery1a143022022-07-19 14:18:24 +093061static void execBasicQuery(int bus, uint8_t addr, uint8_t cmd,
62 std::vector<uint8_t>& resp)
Andrew Jefferye3e3c972021-05-26 14:37:07 +093063{
Ed Tanousa771f6a2022-01-14 09:36:51 -080064 int32_t size = 0;
Ed Tanous73030632022-01-14 10:09:47 -080065 std::filesystem::path devpath = "/dev/i2c-" + std::to_string(bus);
66 FileHandle fileHandle(devpath);
Andrew Jefferye3e3c972021-05-26 14:37:07 +093067
68 /* Select the target device */
Ed Tanous99c44092022-01-14 09:59:09 -080069 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Ed Tanous73030632022-01-14 10:09:47 -080070 if (::ioctl(fileHandle.handle(), I2C_SLAVE, addr) == -1)
Andrew Jefferye3e3c972021-05-26 14:37:07 +093071 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +103072 std::cerr << "Failed to configure device address 0x" << std::hex
73 << (int)addr << " for bus " << std::dec << bus << ": "
74 << strerror(errno) << "\n";
Andrew Jeffery1a143022022-07-19 14:18:24 +093075 return;
Andrew Jefferye3e3c972021-05-26 14:37:07 +093076 }
77
Andrew Jefferya7afacc2022-03-31 11:21:28 +103078 resp.resize(UINT8_MAX + 1);
Andrew Jefferye3e3c972021-05-26 14:37:07 +093079
80 /* Issue the NVMe MI basic command */
Ed Tanous73030632022-01-14 10:09:47 -080081 size = i2c_smbus_read_block_data(fileHandle.handle(), cmd, resp.data());
Andrew Jefferye3e3c972021-05-26 14:37:07 +093082 if (size < 0)
83 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +103084 std::cerr << "Failed to read block data from device 0x" << std::hex
85 << (int)addr << " on bus " << std::dec << bus << ": "
86 << strerror(errno) << "\n";
Andrew Jeffery1a143022022-07-19 14:18:24 +093087 resp.resize(0);
Andrew Jefferye3e3c972021-05-26 14:37:07 +093088 }
Andrew Jeffery1a143022022-07-19 14:18:24 +093089 else if (size > UINT8_MAX + 1)
Andrew Jefferye3e3c972021-05-26 14:37:07 +093090 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +103091 std::cerr << "Unexpected message length from device 0x" << std::hex
92 << (int)addr << " on bus " << std::dec << bus << ": " << size
93 << " (" << UINT8_MAX << ")\n";
Andrew Jeffery1a143022022-07-19 14:18:24 +093094 resp.resize(0);
Andrew Jefferye3e3c972021-05-26 14:37:07 +093095 }
Andrew Jeffery1a143022022-07-19 14:18:24 +093096 else
97 {
98 resp.resize(size);
99 }
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";
Ed Tanous2049bd22022-07-09 07:20:26 -0700123 if (rc != 0)
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 */
Andrew Jeffery1a143022022-07-19 14:18:24 +0930133 execBasicQuery(bus, device, offset, resp);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930134
135 /* Write out the response length */
Andrew Jeffery1a143022022-07-19 14:18:24 +0930136 len = resp.size();
Ed Tanous73030632022-01-14 10:09:47 -0800137 rc = ::write(out.handle(), &len, sizeof(len));
138 if (rc != sizeof(len))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930139 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +1030140 std::cerr << "Failed to write block (" << std::dec << len
Ed Tanous73030632022-01-14 10:09:47 -0800141 << ") length to out descriptor: "
142 << strerror(static_cast<int>(-rc)) << "\n";
Ed Tanous2049bd22022-07-09 07:20:26 -0700143 if (rc != 0)
Ed Tanous73030632022-01-14 10:09:47 -0800144 {
145 return -errno;
146 }
147 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930148 }
149
150 /* Write out the response data */
Ed Tanous73030632022-01-14 10:09:47 -0800151 std::vector<uint8_t>::iterator cursor = resp.begin();
152 while (cursor != resp.end())
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930153 {
Ed Tanous73030632022-01-14 10:09:47 -0800154 size_t lenRemaining = std::distance(cursor, resp.end());
155 ssize_t egress = ::write(out.handle(), &(*cursor), lenRemaining);
Ed Tanousa771f6a2022-01-14 09:36:51 -0800156 if (egress == -1)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930157 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +1030158 std::cerr << "Failed to write block data of length " << std::dec
Ed Tanous73030632022-01-14 10:09:47 -0800159 << lenRemaining << " to out pipe: " << strerror(errno)
160 << "\n";
Ed Tanous2049bd22022-07-09 07:20:26 -0700161 if (rc != 0)
Ed Tanous73030632022-01-14 10:09:47 -0800162 {
163 return -errno;
164 }
165 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930166 }
167
168 cursor += egress;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930169 }
170 }
171
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930172 return rc;
173}
174
175/* Throws std::error_code on failure */
176/* FIXME: Probably shouldn't do fallible stuff in a constructor */
177NVMeBasicContext::NVMeBasicContext(boost::asio::io_service& io, int rootBus) :
178 NVMeContext::NVMeContext(io, rootBus), io(io), reqStream(io), respStream(io)
179{
Ed Tanousa771f6a2022-01-14 09:36:51 -0800180 std::array<int, 2> responsePipe{};
181 std::array<int, 2> requestPipe{};
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930182
183 /* Set up inter-thread communication */
184 if (::pipe(requestPipe.data()) == -1)
185 {
186 std::cerr << "Failed to create request pipe: " << strerror(errno)
187 << "\n";
188 throw std::error_code(errno, std::system_category());
189 }
190
191 if (::pipe(responsePipe.data()) == -1)
192 {
193 std::cerr << "Failed to create response pipe: " << strerror(errno)
194 << "\n";
195
196 if (::close(requestPipe[0]) == -1)
197 {
198 std::cerr << "Failed to close write fd of request pipe: "
199 << strerror(errno) << "\n";
200 }
201
202 if (::close(requestPipe[1]) == -1)
203 {
204 std::cerr << "Failed to close read fd of request pipe: "
205 << strerror(errno) << "\n";
206 }
207
208 throw std::error_code(errno, std::system_category());
209 }
210
211 reqStream.assign(requestPipe[1]);
Ed Tanous73030632022-01-14 10:09:47 -0800212 FileHandle streamIn(requestPipe[0]);
213 FileHandle streamOut(responsePipe[1]);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930214 respStream.assign(responsePipe[0]);
215
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930216 thread = std::jthread([streamIn{std::move(streamIn)},
217 streamOut{std::move(streamOut)}]() mutable {
Ed Tanousa771f6a2022-01-14 09:36:51 -0800218 ssize_t rc = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930219
220 if ((rc = processBasicQueryStream(streamIn, streamOut)) < 0)
221 {
222 std::cerr << "Failure while processing query stream: "
Ed Tanousa771f6a2022-01-14 09:36:51 -0800223 << strerror(static_cast<int>(-rc)) << "\n";
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930224 }
225
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930226 std::cerr << "Terminating basic query thread\n";
227 });
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930228}
229
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930230void NVMeBasicContext::readAndProcessNVMeSensor()
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930231{
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930232 if (pollCursor == sensors.end())
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930233 {
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030234 this->pollNVMeDevices();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930235 return;
236 }
237
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930238 std::shared_ptr<NVMeSensor> sensor = *pollCursor++;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930239
Andrew Jeffery3859c7f2021-10-29 15:51:16 +1030240 if (!sensor->readingStateGood())
241 {
242 sensor->markAvailable(false);
243 sensor->updateValue(std::numeric_limits<double>::quiet_NaN());
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930244 readAndProcessNVMeSensor();
Andrew Jeffery3859c7f2021-10-29 15:51:16 +1030245 return;
246 }
247
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030248 /* Potentially defer sampling the sensor sensor if it is in error */
249 if (!sensor->sample())
250 {
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930251 readAndProcessNVMeSensor();
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030252 return;
253 }
254
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930255 auto command = encodeBasicQuery(sensor->bus, 0x6a, 0x00);
256
257 /* Issue the request */
258 boost::asio::async_write(
259 reqStream, boost::asio::buffer(command->data(), command->size()),
Ed Tanous76b2bc72022-02-18 09:48:16 -0800260 [command](boost::system::error_code ec, std::size_t) {
Ed Tanousbb679322022-05-16 16:10:00 -0700261 if (ec)
262 {
263 std::cerr << "Got error writing basic query: " << ec << "\n";
264 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930265 });
266
Andrew Jeffery84c16872022-03-15 21:50:59 +1030267 auto response = std::make_shared<boost::asio::streambuf>();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930268 response->prepare(1);
269
270 /* Gather the response and dispatch for parsing */
271 boost::asio::async_read(
272 respStream, *response,
273 [response](const boost::system::error_code& ec, std::size_t n) {
Ed Tanousbb679322022-05-16 16:10:00 -0700274 if (ec)
275 {
276 std::cerr << "Got error completing basic query: " << ec << "\n";
277 return static_cast<std::size_t>(0);
278 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930279
Ed Tanousbb679322022-05-16 16:10:00 -0700280 if (n == 0)
281 {
282 return static_cast<std::size_t>(1);
283 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930284
Ed Tanousbb679322022-05-16 16:10:00 -0700285 std::istream is(response.get());
286 size_t len = static_cast<std::size_t>(is.peek());
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930287
Ed Tanousbb679322022-05-16 16:10:00 -0700288 if (n > len + 1)
289 {
290 std::cerr << "Query stream has become unsynchronised: "
291 << "n: " << n << ", "
292 << "len: " << len << "\n";
293 return static_cast<std::size_t>(0);
294 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930295
Ed Tanousbb679322022-05-16 16:10:00 -0700296 if (n == len + 1)
297 {
298 return static_cast<std::size_t>(0);
299 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930300
Ed Tanousbb679322022-05-16 16:10:00 -0700301 if (n > 1)
302 {
303 return len + 1 - n;
304 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930305
Ed Tanousbb679322022-05-16 16:10:00 -0700306 response->prepare(len);
307 return len;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930308 },
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930309 [weakSelf{weak_from_this()}, sensor, response](
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030310 const boost::system::error_code& ec, std::size_t length) mutable {
Ed Tanousbb679322022-05-16 16:10:00 -0700311 if (ec)
312 {
313 std::cerr << "Got error reading basic query: " << ec << "\n";
314 return;
315 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930316
Ed Tanousbb679322022-05-16 16:10:00 -0700317 if (length == 0)
318 {
319 std::cerr << "Invalid message length: " << length << "\n";
320 return;
321 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930322
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930323 if (auto self = weakSelf.lock())
324 {
325 /* 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
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930331 /* Update the sensor */
332 self->processResponse(sensor, data.data(), data.size());
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030333
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930334 /* Enqueue processing of the next sensor */
335 self->readAndProcessNVMeSensor();
336 }
Ed Tanousbb679322022-05-16 16:10:00 -0700337 });
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930338}
339
340void NVMeBasicContext::pollNVMeDevices()
341{
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930342 pollCursor = sensors.begin();
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030343
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930344 scanTimer.expires_from_now(boost::posix_time::seconds(1));
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930345 scanTimer.async_wait([weakSelf{weak_from_this()}](
346 const boost::system::error_code errorCode) {
Ed Tanousbb679322022-05-16 16:10:00 -0700347 if (errorCode == boost::asio::error::operation_aborted)
348 {
349 return;
350 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930351
Ed Tanousbb679322022-05-16 16:10:00 -0700352 if (errorCode)
353 {
354 std::cerr << errorCode.message() << "\n";
355 return;
356 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930357
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930358 if (auto self = weakSelf.lock())
359 {
360 self->readAndProcessNVMeSensor();
361 }
Ed Tanousbb679322022-05-16 16:10:00 -0700362 });
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930363}
364
365static double getTemperatureReading(int8_t reading)
366{
367 if (reading == static_cast<int8_t>(0x80) ||
368 reading == static_cast<int8_t>(0x81))
369 {
370 // 0x80 = No temperature data or temperature data is more the 5 s
371 // old 0x81 = Temperature sensor failure
372 return std::numeric_limits<double>::quiet_NaN();
373 }
374
375 return reading;
376}
377
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030378void NVMeBasicContext::processResponse(std::shared_ptr<NVMeSensor>& sensor,
379 void* msg, size_t len)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930380{
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030381 if (msg == nullptr || len < 6)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930382 {
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030383 sensor->incrementError();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930384 return;
385 }
386
387 uint8_t* messageData = static_cast<uint8_t*>(msg);
Andrew Jeffery7aeb1a52022-03-15 22:49:04 +1030388
389 uint8_t status = messageData[0];
Ed Tanous2049bd22022-07-09 07:20:26 -0700390 if (((status & NVME_MI_BASIC_SFLGS_DRIVE_NOT_READY) != 0) ||
391 ((status & NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL) == 0))
Andrew Jeffery7aeb1a52022-03-15 22:49:04 +1030392 {
393 sensor->markFunctional(false);
394 return;
395 }
396
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930397 double value = getTemperatureReading(messageData[2]);
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030398 if (!std::isfinite(value))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930399 {
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930400 sensor->incrementError();
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030401 return;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930402 }
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030403
404 sensor->updateValue(value);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930405}