smartmontools SVN Rev 5388
Utility to control and monitor storage systems with "S.M.A.R.T."
nvmecmds.cpp
Go to the documentation of this file.
1/*
2 * nvmecmds.cpp
3 *
4 * Home page of code is: https://www.smartmontools.org
5 *
6 * Copyright (C) 2016-21 Christian Franke
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11#include "config.h"
12#include "nvmecmds.h"
13
14const char * nvmecmds_cvsid = "$Id: nvmecmds.cpp 5219 2021-06-04 16:39:50Z chrfranke $"
16
17#include "dev_interface.h"
18#include "atacmds.h" // swapx(), dont_print_serial_number
19#include "scsicmds.h" // dStrHex()
20#include "utility.h"
21
22#include <errno.h>
23
24using namespace smartmontools;
25
26// Print NVMe debug messages?
27unsigned char nvme_debugmode = 0;
28
29// Dump up to 4096 bytes, do not dump trailing zero bytes.
30// TODO: Handle this by new unified function in utility.cpp
31static void debug_hex_dump(const void * data, unsigned size)
32{
33 const unsigned char * p = (const unsigned char *)data;
34 const unsigned limit = 4096; // sizeof(nvme_id_ctrl)
35 unsigned sz = (size <= limit ? size : limit);
36
37 while (sz > 0x10 && !p[sz-1])
38 sz--;
39 if (sz < size) {
40 if (sz & 0x0f)
41 sz = (sz & ~0x0f) + 0x10;
42 sz += 0x10;
43 if (sz > size)
44 sz = size;
45 }
46
47 dStrHex((const uint8_t *)p, sz, 0);
48 if (sz < size)
49 pout(" ...\n");
50}
51
52// Call NVMe pass-through and print debug info if requested.
53static bool nvme_pass_through(nvme_device * device, const nvme_cmd_in & in,
54 nvme_cmd_out & out)
55{
56 if (nvme_debugmode) {
57 pout(" [NVMe call: opcode=0x%02x, size=0x%04x, nsid=0x%08x, cdw10=0x%08x",
58 in.opcode, in.size, in.nsid, in.cdw10);
59 if (in.cdw11 || in.cdw12 || in.cdw13 || in.cdw14 || in.cdw15)
60 pout(",\n cdw1x=0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x",
61 in.cdw11, in.cdw12, in.cdw13, in.cdw14, in.cdw15);
62 pout("]\n");
63 }
64
65 auto start_usec = (nvme_debugmode ? get_timer_usec() : -1);
66
67 bool ok = device->nvme_pass_through(in, out);
68
69 if (start_usec >= 0) {
70 auto duration_usec = get_timer_usec() - start_usec;
71 if (duration_usec > 0)
72 pout(" [Duration: %.6fs]\n", duration_usec / 1000000.0);
73 }
74
76 && in.opcode == nvme_admin_identify && in.cdw10 == 0x01) {
77 // Invalidate serial number
78 nvme_id_ctrl & id_ctrl = *reinterpret_cast<nvme_id_ctrl *>(in.buffer);
79 memset(id_ctrl.sn, 'X', sizeof(id_ctrl.sn));
80 }
81
82 if (nvme_debugmode) {
83 if (!ok) {
84 pout(" [NVMe call failed: ");
85 if (out.status_valid)
86 pout("NVMe Status=0x%04x", out.status);
87 else
88 pout("%s", device->get_errmsg());
89 }
90 else {
91 pout(" [NVMe call succeeded: result=0x%08x", out.result);
93 pout("\n");
95 pout(" ");
96 }
97 }
98 pout("]\n");
99 }
100
101 return ok;
102}
103
104// Call NVMe pass-through and print debug info if requested.
105// Version without output parameters.
106static bool nvme_pass_through(nvme_device * device, const nvme_cmd_in & in)
107{
108 nvme_cmd_out out;
109 return nvme_pass_through(device, in, out);
110}
111
112// Read NVMe identify info with controller/namespace field CNS.
113static bool nvme_read_identify(nvme_device * device, unsigned nsid,
114 unsigned char cns, void * data, unsigned size)
115{
116 memset(data, 0, size);
117 nvme_cmd_in in;
119 in.nsid = nsid;
120 in.cdw10 = cns;
121
122 return nvme_pass_through(device, in);
123}
124
125// Read NVMe Identify Controller data structure.
127{
128 if (!nvme_read_identify(device, 0, 0x01, &id_ctrl, sizeof(id_ctrl)))
129 return false;
130
131 if (isbigendian()) {
132 swapx(&id_ctrl.vid);
133 swapx(&id_ctrl.ssvid);
134 swapx(&id_ctrl.cntlid);
135 swapx(&id_ctrl.ver);
136 swapx(&id_ctrl.oacs);
137 swapx(&id_ctrl.wctemp);
138 swapx(&id_ctrl.cctemp);
139 swapx(&id_ctrl.mtfa);
140 swapx(&id_ctrl.hmpre);
141 swapx(&id_ctrl.hmmin);
142 swapx(&id_ctrl.rpmbs);
143 swapx(&id_ctrl.nn);
144 swapx(&id_ctrl.oncs);
145 swapx(&id_ctrl.fuses);
146 swapx(&id_ctrl.awun);
147 swapx(&id_ctrl.awupf);
148 swapx(&id_ctrl.acwu);
149 swapx(&id_ctrl.sgls);
150 for (int i = 0; i < 32; i++) {
151 swapx(&id_ctrl.psd[i].max_power);
152 swapx(&id_ctrl.psd[i].entry_lat);
153 swapx(&id_ctrl.psd[i].exit_lat);
154 swapx(&id_ctrl.psd[i].idle_power);
155 swapx(&id_ctrl.psd[i].active_power);
156 }
157 }
158
159 return true;
160}
161
162// Read NVMe Identify Namespace data structure for namespace NSID.
163bool nvme_read_id_ns(nvme_device * device, unsigned nsid, nvme_id_ns & id_ns)
164{
165 if (!nvme_read_identify(device, nsid, 0x00, &id_ns, sizeof(id_ns)))
166 return false;
167
168 if (isbigendian()) {
169 swapx(&id_ns.nsze);
170 swapx(&id_ns.ncap);
171 swapx(&id_ns.nuse);
172 swapx(&id_ns.nawun);
173 swapx(&id_ns.nawupf);
174 swapx(&id_ns.nacwu);
175 swapx(&id_ns.nabsn);
176 swapx(&id_ns.nabo);
177 swapx(&id_ns.nabspf);
178 for (int i = 0; i < 16; i++)
179 swapx(&id_ns.lbaf[i].ms);
180 }
181
182 return true;
183}
184
185static bool nvme_read_log_page_1(nvme_device * device, unsigned nsid,
186 unsigned char lid, void * data, unsigned size, unsigned offset = 0)
187{
188 if (!(4 <= size && size <= 0x1000 && !(size % 4) && !(offset % 4)))
189 return device->set_err(EINVAL, "Invalid NVMe log size %u or offset %u", size, offset);
190
191 memset(data, 0, size);
192 nvme_cmd_in in;
194 in.nsid = nsid;
195 in.cdw10 = lid | (((size / 4) - 1) << 16);
196 in.cdw12 = offset; // LPOL, NVMe 1.2.1
197
198 return nvme_pass_through(device, in);
199}
200
201// Read NVMe log page with identifier LID.
202unsigned nvme_read_log_page(nvme_device * device, unsigned nsid, unsigned char lid,
203 void * data, unsigned size, bool lpo_sup, unsigned offset /* = 0 */)
204{
205 unsigned n, bs;
206 for (n = 0; n < size; n += bs) {
207 if (!lpo_sup && offset + n > 0) {
208 device->set_err(ENOSYS, "Log Page Offset not supported");
209 break;
210 }
211
212 // Limit transfer size to one page to avoid problems with
213 // limits of NVMe pass-through layer or too low MDTS values.
214 bs = size - n;
215 if (bs > 0x1000)
216 bs = 0x1000;
217 if (!nvme_read_log_page_1(device, nsid, lid, (char *)data + n, bs, offset + n))
218 break;
219 }
220
221 return n;
222}
223
224// Read NVMe Error Information Log.
226 unsigned num_entries, bool lpo_sup)
227{
228 unsigned n = nvme_read_log_page(device, 0xffffffff, 0x01, error_log,
229 num_entries * sizeof(*error_log), lpo_sup);
230
231 if (isbigendian()) {
232 for (unsigned i = 0; i < n; i++) {
233 swapx(&error_log[i].error_count);
234 swapx(&error_log[i].sqid);
235 swapx(&error_log[i].cmdid);
236 swapx(&error_log[i].status_field);
237 swapx(&error_log[i].parm_error_location);
238 swapx(&error_log[i].lba);
239 swapx(&error_log[i].nsid);
240 }
241 }
242
243 return n / sizeof(*error_log);
244}
245
246// Read NVMe SMART/Health Information log.
248{
249 if (!nvme_read_log_page_1(device, 0xffffffff, 0x02, &smart_log, sizeof(smart_log)))
250 return false;
251
252 if (isbigendian()) {
253 swapx(&smart_log.warning_temp_time);
254 swapx(&smart_log.critical_comp_time);
255 for (int i = 0; i < 8; i++)
256 swapx(&smart_log.temp_sensor[i]);
257 }
258
259 return true;
260}
bool dont_print_serial_number
Definition: atacmds.cpp:37
NVMe device access.
virtual bool nvme_pass_through(const nvme_cmd_in &in, nvme_cmd_out &out)=0
NVMe pass through.
const char * get_errmsg() const
Get last error message.
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
ptr_t data
Definition: megaraid.h:15
u32 size
Definition: megaraid.h:0
@ nvme_admin_identify
Definition: nvmecmds.h:208
@ nvme_admin_get_log_page
Definition: nvmecmds.h:205
uint32_t nsid
uint16_t sqid
static bool nvme_read_log_page_1(nvme_device *device, unsigned nsid, unsigned char lid, void *data, unsigned size, unsigned offset=0)
Definition: nvmecmds.cpp:185
bool nvme_read_id_ns(nvme_device *device, unsigned nsid, nvme_id_ns &id_ns)
Definition: nvmecmds.cpp:163
bool nvme_read_id_ctrl(nvme_device *device, nvme_id_ctrl &id_ctrl)
Definition: nvmecmds.cpp:126
unsigned char nvme_debugmode
Definition: nvmecmds.cpp:27
bool nvme_read_smart_log(nvme_device *device, nvme_smart_log &smart_log)
Definition: nvmecmds.cpp:247
unsigned nvme_read_log_page(nvme_device *device, unsigned nsid, unsigned char lid, void *data, unsigned size, bool lpo_sup, unsigned offset)
Definition: nvmecmds.cpp:202
unsigned nvme_read_error_log(nvme_device *device, nvme_error_log_page *error_log, unsigned num_entries, bool lpo_sup)
Definition: nvmecmds.cpp:225
const char * nvmecmds_cvsid
Definition: nvmecmds.cpp:14
static bool nvme_pass_through(nvme_device *device, const nvme_cmd_in &in, nvme_cmd_out &out)
Definition: nvmecmds.cpp:53
static bool nvme_read_identify(nvme_device *device, unsigned nsid, unsigned char cns, void *data, unsigned size)
Definition: nvmecmds.cpp:113
static void debug_hex_dump(const void *data, unsigned size)
Definition: nvmecmds.cpp:31
#define NVMECMDS_H_CVSID
Definition: nvmecmds.h:17
void dStrHex(const uint8_t *up, int len, int no_ascii)
Definition: scsicmds.cpp:78
void pout(const char *fmt,...)
Definition: smartd.cpp:1309
NVMe pass through input parameters.
unsigned char direction() const
Get I/O direction from opcode.
unsigned cdw10
unsigned cdw13
unsigned cdw11
unsigned char opcode
Opcode (CDW0 07:00)
unsigned size
Size of buffer.
unsigned cdw14
unsigned cdw15
Cmd specific.
unsigned nsid
Namespace ID.
unsigned cdw12
void set_data_in(unsigned char op, void *buf, unsigned sz)
void * buffer
Pointer to data buffer.
NVMe pass through output parameters.
bool status_valid
true if status is valid
unsigned short status
Status Field (DW3 31:17)
unsigned result
Command specific result (DW0)
unsigned short wctemp
Definition: nvmecmds.h:90
unsigned short ssvid
Definition: nvmecmds.h:66
unsigned short cntlid
Definition: nvmecmds.h:74
struct nvme_id_power_state psd[32]
Definition: nvmecmds.h:131
unsigned short fuses
Definition: nvmecmds.h:112
unsigned short mtfa
Definition: nvmecmds.h:92
unsigned short vid
Definition: nvmecmds.h:65
unsigned short oacs
Definition: nvmecmds.h:81
unsigned short awupf
Definition: nvmecmds.h:116
unsigned short cctemp
Definition: nvmecmds.h:91
struct nvme_lbaf lbaf[16]
Definition: nvmecmds.h:168
unsigned short nabspf
Definition: nvmecmds.h:162
unsigned short nacwu
Definition: nvmecmds.h:159
unsigned short nawun
Definition: nvmecmds.h:157
unsigned short nabo
Definition: nvmecmds.h:161
unsigned short nabsn
Definition: nvmecmds.h:160
unsigned short nawupf
Definition: nvmecmds.h:158
unsigned short ms
Definition: nvmecmds.h:137
unsigned int critical_comp_time
Definition: nvmecmds.h:192
unsigned short temp_sensor[8]
Definition: nvmecmds.h:193
unsigned int warning_temp_time
Definition: nvmecmds.h:191
long long get_timer_usec()
Get microseconds since some unspecified starting point.
Definition: utility.cpp:846
bool isbigendian()
Definition: utility.h:81
void swapx(unsigned short *p)
Definition: utility.h:94