blob: 40bb8ac295f93f17aef200f7c05fc9875e82d5ec [file] [log] [blame]
Andrew Jefferydae6e182021-05-21 16:23:07 +09301/*
2// Copyright (c) 2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Andrew Jefferya9d15082021-05-24 13:55:12 +093017#include "NVMeMCTPContext.hpp"
Andrew Jefferydae6e182021-05-21 16:23:07 +093018
19#include "NVMeDevice.hpp"
20
21#include <crc32c.h>
22#include <libmctp-smbus.h>
23#include <libmctp.h>
24
25#include <boost/container/flat_map.hpp>
26
27static constexpr bool debug = false;
28
29static void rxMessage(uint8_t eid, void* data, void* msg, size_t len);
30
31namespace nvmeMCTP
32{
33struct mctp_binding_smbus* smbus = mctp_smbus_init();
34struct mctp* mctp = mctp_init();
35
36static boost::container::flat_map<int, int> inFds;
37static boost::container::flat_map<int, int> outFds;
38
39int getInFd(int rootBus)
40{
41 auto findBus = inFds.find(rootBus);
42 if (findBus != inFds.end())
43 {
44 return findBus->second;
45 }
46 int fd = mctp_smbus_open_in_bus(smbus, rootBus);
47 if (fd < 0)
48 {
49 std::cerr << "Error opening IN Bus " << rootBus << "\n";
50 }
51 inFds[rootBus] = fd;
52 return fd;
53}
54
55int getOutFd(int bus)
56{
57 auto findBus = outFds.find(bus);
58 if (findBus != outFds.end())
59 {
60 return findBus->second;
61 }
62 int fd = mctp_smbus_open_out_bus(smbus, bus);
63 if (fd < 0)
64 {
65 std::cerr << "Error opening Out Bus " << bus << "\n";
66 }
67 outFds[bus] = fd;
68 return fd;
69}
70
71// we don't close the outFd as multiple sensors could be sharing the fd, we need
72// to close the inFd as it can only be used on 1 socket at a time
73void closeInFd(int rootBus)
74{
75 auto findFd = inFds.find(rootBus);
76 if (findFd == inFds.end())
77 {
78 return;
79 }
80 close(findFd->second);
81 inFds.erase(rootBus);
82}
83
84int getRootBus(int inFd)
85{
86 // we assume that we won't have too many FDs, so looping is OK
87 for (const auto [root, fd] : inFds)
88 {
89 if (fd == inFd)
90 {
91 return root;
92 }
93 }
94
95 return -1;
96}
97
98void init()
99{
100 if (mctp == nullptr || smbus == nullptr)
101 {
102 throw std::runtime_error("Unable to init mctp");
103 }
104 mctp_smbus_register_bus(smbus, nvmeMCTP::mctp, 0);
105 mctp_set_rx_all(mctp, rxMessage, nullptr);
106}
107
108} // namespace nvmeMCTP
109
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930110static void rxMessage(uint8_t eid, void*, void* msg, size_t len)
111{
112 int inFd = mctp_smbus_get_in_fd(nvmeMCTP::smbus);
113 int rootBus = nvmeMCTP::getRootBus(inFd);
114
115 NVMEMap& nvmeMap = getNVMEMap();
116 auto findMap = nvmeMap.find(rootBus);
117 if (findMap == nvmeMap.end())
118 {
119 std::cerr << "Unable to lookup root bus " << rootBus << "\n";
120 return;
121 }
122
123 if (debug)
124 {
125 std::cout << "Eid from the received messaged: " << eid << "\n";
126 }
127
128 findMap->second->processResponse(msg, len);
129}
130
Andrew Jefferydae6e182021-05-21 16:23:07 +0930131static int verifyIntegrity(uint8_t* msg, size_t len)
132{
133 uint32_t msgIntegrity = {0};
134 if (len < NVME_MI_MSG_RESPONSE_HEADER_SIZE + sizeof(msgIntegrity))
135 {
136 std::cerr << "Not enough bytes for nvme header and trailer\n";
137 return -1;
138 }
139
140 msgIntegrity = (msg[len - 4]) + (msg[len - 3] << 8) + (msg[len - 2] << 16) +
141 (msg[len - 1] << 24);
142
143 uint32_t calculateIntegrity = crc32c(msg, len - sizeof(msgIntegrity));
144 if (msgIntegrity != calculateIntegrity)
145 {
146 std::cerr << "CRC mismatch. Got=" << msgIntegrity
147 << " Expected=" << calculateIntegrity << "\n";
148 return -1;
149 }
150 return 0;
151}
152
153static double getTemperatureReading(int8_t reading)
154{
155
156 if (reading == static_cast<int8_t>(0x80) ||
157 reading == static_cast<int8_t>(0x81))
158 {
159 // 0x80 = No temperature data or temperature data is more the 5 s
160 // old 0x81 = Temperature sensor failure
161 return std::numeric_limits<double>::quiet_NaN();
162 }
163
164 return reading;
165}
166
Andrew Jefferya9d15082021-05-24 13:55:12 +0930167void NVMeMCTPContext::processResponse(void* msg, size_t len)
Andrew Jefferydae6e182021-05-21 16:23:07 +0930168{
169 struct nvme_mi_msg_response_header header
170 {};
171
Andrew Jefferydae6e182021-05-21 16:23:07 +0930172 if (msg == nullptr)
173 {
174 std::cerr << "Bad message received\n";
175 return;
176 }
177
178 if (len <= 0)
179 {
180 std::cerr << "Received message not long enough\n";
181 return;
182 }
183
Andrew Jefferydae6e182021-05-21 16:23:07 +0930184 uint8_t* messageData = static_cast<uint8_t*>(msg);
185
186 if ((*messageData & NVME_MI_MESSAGE_TYPE_MASK) != NVME_MI_MESSAGE_TYPE)
187 {
188 std::cerr << "Got unknown type message_type="
189 << (*messageData & NVME_MI_MESSAGE_TYPE_MASK) << "\n";
190 return;
191 }
192
193 if (len < NVME_MI_MSG_RESPONSE_HEADER_SIZE + sizeof(uint32_t))
194 {
195 std::cerr << "Not enough bytes for NVMe header and trailer\n";
196 return;
197 }
198
199 if (verifyIntegrity(messageData, len) != 0)
200 {
201 std::cerr << "Verification of message integrity failed\n";
202 return;
203 }
204
205 header.message_type = messageData[0];
206 header.flags = messageData[1];
207 header.status = messageData[4];
208
209 if (header.status == NVME_MI_HDR_STATUS_MORE_PROCESSING_REQUIRED)
210 {
211 return;
212 }
213
214 if (header.status != NVME_MI_HDR_STATUS_SUCCESS)
215 {
216 std::cerr << "Command failed with status= " << header.status << "\n";
217 return;
218 }
219
220 messageData += NVME_MI_MSG_RESPONSE_HEADER_SIZE;
221 size_t messageLength =
222 len - NVME_MI_MSG_RESPONSE_HEADER_SIZE - sizeof(uint32_t);
223 if (((header.flags >> NVME_MI_HDR_FLAG_MSG_TYPE_SHIFT) &
224 NVME_MI_HDR_FLAG_MSG_TYPE_MASK) != NVME_MI_HDR_MESSAGE_TYPE_MI_COMMAND)
225 {
226 std::cerr << "Not MI type comamnd\n";
227 return;
228 }
229
230 if (messageLength < NVME_MI_HEALTH_STATUS_POLL_MSG_MIN)
231 {
232 std::cerr << "Got improperly sized health status poll\n";
233 return;
234 }
235
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930236 std::shared_ptr<NVMeSensor> sensorInfo = sensors.front();
Andrew Jefferydae6e182021-05-21 16:23:07 +0930237 if (debug)
238 {
239 std::cout << "Temperature Reading: "
240 << getTemperatureReading(messageData[5])
241 << " Celsius for device " << sensorInfo->name << "\n";
242 }
243
244 double value = getTemperatureReading(messageData[5]);
245 if (!std::isfinite(value))
246 {
247 sensorInfo->markAvailable(false);
248 sensorInfo->incrementError();
249 }
250 else
251 {
252 sensorInfo->updateValue(value);
253 }
254
255 if (debug)
256 {
257 std::cout << "Cancelling the timer now\n";
258 }
259
260 // move to back of scan queue
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930261 sensors.pop_front();
262 sensors.emplace_back(sensorInfo);
Andrew Jefferydae6e182021-05-21 16:23:07 +0930263
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930264 mctpResponseTimer.cancel();
Andrew Jefferydae6e182021-05-21 16:23:07 +0930265}
266
267static int nvmeMessageTransmit(mctp& mctp, nvme_mi_msg_request& req)
268{
269 std::array<uint8_t, NVME_MI_MSG_BUFFER_SIZE> messageBuf = {};
270
271 req.header.flags |= NVME_MI_HDR_MESSAGE_TYPE_MI_COMMAND
272 << NVME_MI_HDR_FLAG_MSG_TYPE_SHIFT;
273 req.header.message_type =
274 NVME_MI_MESSAGE_TYPE | NVME_MI_MCTP_INTEGRITY_CHECK;
275
276 uint32_t integrity = 0;
277 size_t msgSize = NVME_MI_MSG_REQUEST_HEADER_SIZE + req.request_data_len +
278 sizeof(integrity);
279
280 if (sizeof(messageBuf) < msgSize)
281 {
282 return EXIT_FAILURE;
283 }
284
285 messageBuf[0] = req.header.message_type;
286 messageBuf[1] = req.header.flags;
287 // Reserved bytes 2-3
288
289 messageBuf[4] = req.header.opcode;
290 // reserved bytes 5-7
291 messageBuf[8] = req.header.dword0 & 0xff;
292 messageBuf[9] = (req.header.dword0 >> 8) & 0xff;
293 messageBuf[10] = (req.header.dword0 >> 16) & 0xff;
294 messageBuf[11] = (req.header.dword0 >> 24) & 0xff;
295
296 messageBuf[12] = req.header.dword1 & 0xff;
297 messageBuf[13] = (req.header.dword1 >> 8) & 0xff;
298 messageBuf[14] = (req.header.dword1 >> 16) & 0xff;
299 messageBuf[15] = (req.header.dword1 >> 24) & 0xff;
300
301 std::copy_n(req.request_data, req.request_data_len,
302 messageBuf.data() +
303 static_cast<uint8_t>(NVME_MI_MSG_REQUEST_HEADER_SIZE));
304
305 msgSize = NVME_MI_MSG_REQUEST_HEADER_SIZE + req.request_data_len;
306 integrity = crc32c(messageBuf.data(),
307 NVME_MI_MSG_REQUEST_HEADER_SIZE + req.request_data_len);
308 messageBuf[msgSize] = integrity & 0xff;
309 messageBuf[msgSize + 1] = (integrity >> 8) & 0xff;
310 messageBuf[msgSize + 2] = (integrity >> 16) & 0xff;
311 messageBuf[msgSize + 3] = (integrity >> 24) & 0xff;
312 msgSize += sizeof(integrity);
313
314 return mctp_message_tx(&mctp, 0, messageBuf.data(), msgSize);
315}
316
Andrew Jefferya9d15082021-05-24 13:55:12 +0930317void NVMeMCTPContext::readResponse()
Andrew Jefferydae6e182021-05-21 16:23:07 +0930318{
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930319 nvmeSlaveSocket.async_wait(
Andrew Jefferydae6e182021-05-21 16:23:07 +0930320 boost::asio::ip::tcp::socket::wait_error,
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930321 [this](const boost::system::error_code errorCode) {
Andrew Jefferydae6e182021-05-21 16:23:07 +0930322 if (errorCode)
323 {
324 return;
325 }
326
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930327 mctp_smbus_set_in_fd(nvmeMCTP::smbus, nvmeMCTP::getInFd(rootBus));
Andrew Jefferydae6e182021-05-21 16:23:07 +0930328
329 // through libmctp this will invoke rxMessage
330 mctp_smbus_read(nvmeMCTP::smbus);
331 });
332}
333
Andrew Jefferya9d15082021-05-24 13:55:12 +0930334void NVMeMCTPContext::readAndProcessNVMeSensor()
Andrew Jefferydae6e182021-05-21 16:23:07 +0930335{
336 struct nvme_mi_msg_request requestMsg = {};
337 requestMsg.header.opcode = NVME_MI_OPCODE_HEALTH_STATUS_POLL;
338 requestMsg.header.dword0 = 0;
339 requestMsg.header.dword1 = 0;
340
341 int mctpResponseTimeout = 1;
342
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930343 if (sensors.empty())
Andrew Jefferydae6e182021-05-21 16:23:07 +0930344 {
345 return;
346 }
347
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930348 std::shared_ptr<NVMeSensor>& sensor = sensors.front();
Andrew Jefferydae6e182021-05-21 16:23:07 +0930349
350 // setup the timeout timer
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930351 mctpResponseTimer.expires_from_now(
Andrew Jefferydae6e182021-05-21 16:23:07 +0930352 boost::posix_time::seconds(mctpResponseTimeout));
353
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930354 mctpResponseTimer.async_wait(
355 [sensor, this](const boost::system::error_code errorCode) {
Andrew Jefferydae6e182021-05-21 16:23:07 +0930356 if (errorCode)
357 {
358 // timer cancelled successfully
359 return;
360 }
361
362 sensor->incrementError();
363
364 // cycle it back
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930365 sensors.pop_front();
366 sensors.emplace_back(sensor);
Andrew Jefferydae6e182021-05-21 16:23:07 +0930367
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930368 nvmeSlaveSocket.cancel();
Andrew Jefferydae6e182021-05-21 16:23:07 +0930369 });
370
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930371 readResponse();
Andrew Jefferydae6e182021-05-21 16:23:07 +0930372
373 if (debug)
374 {
375 std::cout << "Sending message to read data from Drive on bus: "
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930376 << sensor->bus << " , rootBus: " << rootBus
Andrew Jefferydae6e182021-05-21 16:23:07 +0930377 << " device: " << sensor->name << "\n";
378 }
379
380 mctp_smbus_set_out_fd(nvmeMCTP::smbus, nvmeMCTP::getOutFd(sensor->bus));
381 int rc = nvmeMessageTransmit(*nvmeMCTP::mctp, requestMsg);
382
383 if (rc != 0)
384 {
385 std::cerr << "Error sending request message to NVMe device\n";
386 }
387}
388
Andrew Jefferya9d15082021-05-24 13:55:12 +0930389NVMeMCTPContext::NVMeMCTPContext(boost::asio::io_service& io, int rootBus) :
390 NVMeContext::NVMeContext(io, rootBus), nvmeSlaveSocket(io),
391 mctpResponseTimer(io)
Andrew Jefferydae6e182021-05-21 16:23:07 +0930392{
393 nvmeSlaveSocket.assign(boost::asio::ip::tcp::v4(),
394 nvmeMCTP::getInFd(rootBus));
395}
396
Andrew Jefferya9d15082021-05-24 13:55:12 +0930397void NVMeMCTPContext::pollNVMeDevices()
Andrew Jefferydae6e182021-05-21 16:23:07 +0930398{
399 scanTimer.expires_from_now(boost::posix_time::seconds(1));
400 scanTimer.async_wait(
401 [self{shared_from_this()}](const boost::system::error_code errorCode) {
402 if (errorCode == boost::asio::error::operation_aborted)
403 {
404 return; // we're being canceled
405 }
406 else if (errorCode)
407 {
408 std::cerr << "Error:" << errorCode.message() << "\n";
409 return;
410 }
411 else
412 {
Andrew Jefferyfa500ae2021-05-21 16:46:36 +0930413 self->readAndProcessNVMeSensor();
Andrew Jefferydae6e182021-05-21 16:23:07 +0930414 }
415
416 self->pollNVMeDevices();
417 });
418}
419
Andrew Jefferya9d15082021-05-24 13:55:12 +0930420void NVMeMCTPContext::close()
Andrew Jefferydae6e182021-05-21 16:23:07 +0930421{
Andrew Jefferya9d15082021-05-24 13:55:12 +0930422 this->NVMeContext::close();
423
Andrew Jefferydae6e182021-05-21 16:23:07 +0930424 mctpResponseTimer.cancel();
425 nvmeSlaveSocket.cancel();
426 nvmeMCTP::closeInFd(rootBus);
427}
428
Andrew Jefferya9d15082021-05-24 13:55:12 +0930429NVMeMCTPContext::~NVMeMCTPContext()
Andrew Jefferydae6e182021-05-21 16:23:07 +0930430{
431 close();
432}