blob: 465f177aa2b7ef069b793baefff6d35c643cb000 [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);
Andrew Jefferye3e3c972021-05-26 14:37:07 +093066
chaul.ampere0fe02292022-07-21 06:37:39 +000067 try
Andrew Jefferye3e3c972021-05-26 14:37:07 +093068 {
chaul.ampere0fe02292022-07-21 06:37:39 +000069 FileHandle fileHandle(devpath);
70
71 /* Select the target device */
72 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
73 if (::ioctl(fileHandle.handle(), I2C_SLAVE, addr) == -1)
74 {
75 std::cerr << "Failed to configure device address 0x" << std::hex
76 << (int)addr << " for bus " << std::dec << bus << ": "
77 << strerror(errno) << "\n";
78 resp.resize(0);
79 return;
80 }
81
82 resp.resize(UINT8_MAX + 1);
83
84 /* Issue the NVMe MI basic command */
85 size = i2c_smbus_read_block_data(fileHandle.handle(), cmd, resp.data());
86 if (size < 0)
87 {
88 std::cerr << "Failed to read block data from device 0x" << std::hex
89 << (int)addr << " on bus " << std::dec << bus << ": "
90 << strerror(errno) << "\n";
91 resp.resize(0);
92 }
93 else if (size > UINT8_MAX + 1)
94 {
95 std::cerr << "Unexpected message length from device 0x" << std::hex
96 << (int)addr << " on bus " << std::dec << bus << ": "
97 << size << " (" << UINT8_MAX << ")\n";
98 resp.resize(0);
99 }
100 else
101 {
102 resp.resize(size);
103 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930104 }
chaul.ampere0fe02292022-07-21 06:37:39 +0000105 catch (const std::out_of_range& e)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930106 {
chaul.ampere0fe02292022-07-21 06:37:39 +0000107 std::cerr << "Failed to create file handle for bus " << std::dec << bus
108 << ": " << e.what() << "\n";
Andrew Jeffery1a143022022-07-19 14:18:24 +0930109 resp.resize(0);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930110 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930111}
112
Ed Tanous73030632022-01-14 10:09:47 -0800113static ssize_t processBasicQueryStream(FileHandle& in, FileHandle& out)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930114{
115 std::vector<uint8_t> resp{};
Ed Tanousa771f6a2022-01-14 09:36:51 -0800116 ssize_t rc = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930117
118 while (true)
119 {
Ed Tanousa771f6a2022-01-14 09:36:51 -0800120 uint8_t device = 0;
121 uint8_t offset = 0;
122 uint8_t len = 0;
123 int bus = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930124
125 /* bus + address + command */
126 std::array<uint8_t, sizeof(uint32_t) + 1 + 1> req{};
127
128 /* Read the command parameters */
Ed Tanous73030632022-01-14 10:09:47 -0800129 ssize_t rc = ::read(in.handle(), req.data(), req.size());
130 if (rc != static_cast<ssize_t>(req.size()))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930131 {
Ed Tanous73030632022-01-14 10:09:47 -0800132 std::cerr << "Failed to read request from in descriptor "
133 << strerror(errno) << "\n";
Ed Tanous2049bd22022-07-09 07:20:26 -0700134 if (rc != 0)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930135 {
Ed Tanous73030632022-01-14 10:09:47 -0800136 return -errno;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930137 }
Ed Tanous73030632022-01-14 10:09:47 -0800138 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930139 }
140
141 decodeBasicQuery(req, bus, device, offset);
142
143 /* Execute the query */
Andrew Jeffery1a143022022-07-19 14:18:24 +0930144 execBasicQuery(bus, device, offset, resp);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930145
146 /* Write out the response length */
Andrew Jeffery1a143022022-07-19 14:18:24 +0930147 len = resp.size();
Ed Tanous73030632022-01-14 10:09:47 -0800148 rc = ::write(out.handle(), &len, sizeof(len));
149 if (rc != sizeof(len))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930150 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +1030151 std::cerr << "Failed to write block (" << std::dec << len
Ed Tanous73030632022-01-14 10:09:47 -0800152 << ") length to out descriptor: "
153 << strerror(static_cast<int>(-rc)) << "\n";
Ed Tanous2049bd22022-07-09 07:20:26 -0700154 if (rc != 0)
Ed Tanous73030632022-01-14 10:09:47 -0800155 {
156 return -errno;
157 }
158 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930159 }
160
161 /* Write out the response data */
Ed Tanous73030632022-01-14 10:09:47 -0800162 std::vector<uint8_t>::iterator cursor = resp.begin();
163 while (cursor != resp.end())
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930164 {
Ed Tanous73030632022-01-14 10:09:47 -0800165 size_t lenRemaining = std::distance(cursor, resp.end());
166 ssize_t egress = ::write(out.handle(), &(*cursor), lenRemaining);
Ed Tanousa771f6a2022-01-14 09:36:51 -0800167 if (egress == -1)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930168 {
Andrew Jeffery9ca98ec2021-11-02 09:50:47 +1030169 std::cerr << "Failed to write block data of length " << std::dec
Ed Tanous73030632022-01-14 10:09:47 -0800170 << lenRemaining << " to out pipe: " << strerror(errno)
171 << "\n";
Ed Tanous2049bd22022-07-09 07:20:26 -0700172 if (rc != 0)
Ed Tanous73030632022-01-14 10:09:47 -0800173 {
174 return -errno;
175 }
176 return -EIO;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930177 }
178
179 cursor += egress;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930180 }
181 }
182
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930183 return rc;
184}
185
186/* Throws std::error_code on failure */
187/* FIXME: Probably shouldn't do fallible stuff in a constructor */
188NVMeBasicContext::NVMeBasicContext(boost::asio::io_service& io, int rootBus) :
189 NVMeContext::NVMeContext(io, rootBus), io(io), reqStream(io), respStream(io)
190{
Ed Tanousa771f6a2022-01-14 09:36:51 -0800191 std::array<int, 2> responsePipe{};
192 std::array<int, 2> requestPipe{};
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930193
194 /* Set up inter-thread communication */
195 if (::pipe(requestPipe.data()) == -1)
196 {
197 std::cerr << "Failed to create request pipe: " << strerror(errno)
198 << "\n";
199 throw std::error_code(errno, std::system_category());
200 }
201
202 if (::pipe(responsePipe.data()) == -1)
203 {
204 std::cerr << "Failed to create response pipe: " << strerror(errno)
205 << "\n";
206
207 if (::close(requestPipe[0]) == -1)
208 {
209 std::cerr << "Failed to close write fd of request pipe: "
210 << strerror(errno) << "\n";
211 }
212
213 if (::close(requestPipe[1]) == -1)
214 {
215 std::cerr << "Failed to close read fd of request pipe: "
216 << strerror(errno) << "\n";
217 }
218
219 throw std::error_code(errno, std::system_category());
220 }
221
222 reqStream.assign(requestPipe[1]);
Ed Tanous73030632022-01-14 10:09:47 -0800223 FileHandle streamIn(requestPipe[0]);
224 FileHandle streamOut(responsePipe[1]);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930225 respStream.assign(responsePipe[0]);
226
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930227 thread = std::jthread([streamIn{std::move(streamIn)},
228 streamOut{std::move(streamOut)}]() mutable {
Ed Tanousa771f6a2022-01-14 09:36:51 -0800229 ssize_t rc = 0;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930230
231 if ((rc = processBasicQueryStream(streamIn, streamOut)) < 0)
232 {
233 std::cerr << "Failure while processing query stream: "
Ed Tanousa771f6a2022-01-14 09:36:51 -0800234 << strerror(static_cast<int>(-rc)) << "\n";
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930235 }
236
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930237 std::cerr << "Terminating basic query thread\n";
238 });
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930239}
240
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930241void NVMeBasicContext::readAndProcessNVMeSensor()
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930242{
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930243 if (pollCursor == sensors.end())
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930244 {
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030245 this->pollNVMeDevices();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930246 return;
247 }
248
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930249 std::shared_ptr<NVMeSensor> sensor = *pollCursor++;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930250
Andrew Jeffery3859c7f2021-10-29 15:51:16 +1030251 if (!sensor->readingStateGood())
252 {
253 sensor->markAvailable(false);
254 sensor->updateValue(std::numeric_limits<double>::quiet_NaN());
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930255 readAndProcessNVMeSensor();
Andrew Jeffery3859c7f2021-10-29 15:51:16 +1030256 return;
257 }
258
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030259 /* Potentially defer sampling the sensor sensor if it is in error */
260 if (!sensor->sample())
261 {
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930262 readAndProcessNVMeSensor();
Andrew Jeffery14108bb2022-03-21 15:00: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) {
Ed Tanousbb679322022-05-16 16:10:00 -0700272 if (ec)
273 {
274 std::cerr << "Got error writing basic query: " << ec << "\n";
275 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930276 });
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) {
Ed Tanousbb679322022-05-16 16:10:00 -0700285 if (ec)
286 {
287 std::cerr << "Got error completing basic query: " << ec << "\n";
288 return static_cast<std::size_t>(0);
289 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930290
Ed Tanousbb679322022-05-16 16:10:00 -0700291 if (n == 0)
292 {
293 return static_cast<std::size_t>(1);
294 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930295
Ed Tanousbb679322022-05-16 16:10:00 -0700296 std::istream is(response.get());
297 size_t len = static_cast<std::size_t>(is.peek());
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930298
Ed Tanousbb679322022-05-16 16:10:00 -0700299 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 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930306
Ed Tanousbb679322022-05-16 16:10:00 -0700307 if (n == len + 1)
308 {
309 return static_cast<std::size_t>(0);
310 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930311
Ed Tanousbb679322022-05-16 16:10:00 -0700312 if (n > 1)
313 {
314 return len + 1 - n;
315 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930316
Ed Tanousbb679322022-05-16 16:10:00 -0700317 response->prepare(len);
318 return len;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930319 },
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930320 [weakSelf{weak_from_this()}, sensor, response](
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030321 const boost::system::error_code& ec, std::size_t length) mutable {
Ed Tanousbb679322022-05-16 16:10:00 -0700322 if (ec)
323 {
324 std::cerr << "Got error reading basic query: " << ec << "\n";
325 return;
326 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930327
Ed Tanousbb679322022-05-16 16:10:00 -0700328 if (length == 0)
329 {
330 std::cerr << "Invalid message length: " << length << "\n";
331 return;
332 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930333
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930334 if (auto self = weakSelf.lock())
335 {
336 /* Deserialise the response */
337 response->consume(1); /* Drop the length byte */
338 std::istream is(response.get());
339 std::vector<char> data(response->size());
340 is.read(data.data(), response->size());
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930341
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930342 /* Update the sensor */
343 self->processResponse(sensor, data.data(), data.size());
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030344
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930345 /* Enqueue processing of the next sensor */
346 self->readAndProcessNVMeSensor();
347 }
Ed Tanousbb679322022-05-16 16:10:00 -0700348 });
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930349}
350
351void NVMeBasicContext::pollNVMeDevices()
352{
Andrew Jefferyb5d7a7f2022-05-02 11:57:03 +0930353 pollCursor = sensors.begin();
Andrew Jeffery8c7074e2022-03-21 14:58:13 +1030354
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930355 scanTimer.expires_from_now(boost::posix_time::seconds(1));
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930356 scanTimer.async_wait([weakSelf{weak_from_this()}](
357 const boost::system::error_code errorCode) {
Ed Tanousbb679322022-05-16 16:10:00 -0700358 if (errorCode == boost::asio::error::operation_aborted)
359 {
360 return;
361 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930362
Ed Tanousbb679322022-05-16 16:10:00 -0700363 if (errorCode)
364 {
365 std::cerr << errorCode.message() << "\n";
366 return;
367 }
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930368
Andrew Jeffery3cbd5a12022-07-18 16:32:11 +0930369 if (auto self = weakSelf.lock())
370 {
371 self->readAndProcessNVMeSensor();
372 }
Ed Tanousbb679322022-05-16 16:10:00 -0700373 });
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{
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030392 if (msg == nullptr || len < 6)
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930393 {
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030394 sensor->incrementError();
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930395 return;
396 }
397
398 uint8_t* messageData = static_cast<uint8_t*>(msg);
Andrew Jeffery7aeb1a52022-03-15 22:49:04 +1030399
400 uint8_t status = messageData[0];
Ed Tanous2049bd22022-07-09 07:20:26 -0700401 if (((status & NVME_MI_BASIC_SFLGS_DRIVE_NOT_READY) != 0) ||
402 ((status & NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL) == 0))
Andrew Jeffery7aeb1a52022-03-15 22:49:04 +1030403 {
404 sensor->markFunctional(false);
405 return;
406 }
407
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930408 double value = getTemperatureReading(messageData[2]);
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030409 if (!std::isfinite(value))
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930410 {
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930411 sensor->incrementError();
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030412 return;
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930413 }
Andrew Jeffery14108bb2022-03-21 15:00:16 +1030414
415 sensor->updateValue(value);
Andrew Jefferye3e3c972021-05-26 14:37:07 +0930416}