2 * Copyright (c) 2016 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 typedef int (*cmd_t
)(int ac
, char **av
, const char *id
, int fd
);
39 static cmd_t
parsecmd(int ac
, char **av
, int *globokp
);
40 static int cmd_info(int ac
, char **av
, const char *id
, int fd
);
41 static int cmd_errors(int ac
, char **av
, const char *id
, int fd
);
42 static void usage(int rc
);
47 main(int ac
, char **av
)
56 while ((ch
= getopt(ac
, av
, "v")) != -1) {
70 for (nvmei
= ac
; nvmei
> 0; --nvmei
) {
71 if (strncmp(av
[nvmei
- 1], "nvme", 4) != 0)
78 cmd
= parsecmd(nvmei
, av
, &globok
);
80 if (nvmei
== ac
&& globok
) {
88 asprintf(&path
, "/dev/nvme%d", i
);
89 fd
= open(path
, O_RDWR
);
93 rc
+= cmd(nvmei
, av
, path
+ 5, fd
);
97 } else if (nvmei
== ac
&& !globok
) {
98 fprintf(stderr
, "must specify nvmeX device for command\n");
100 for (i
= nvmei
; i
< ac
; ++i
) {
107 asprintf(&path
, "/dev/%s", av
[i
]);
108 fd
= open(path
, O_RDWR
);
110 fprintf(stderr
, "open \"%s\": %s\n",
114 rc
+= cmd(nvmei
, av
, path
+ 5, fd
);
125 parsecmd(int ac
, char **av
, int *globokp
)
129 if (strcmp(av
[0], "info") == 0) {
133 if (strcmp(av
[0], "errors") == 0) {
137 fprintf(stderr
, "Command %s not recognized\n", av
[0]);
140 return NULL
; /* NOT REACHED */
145 cmd_info(int ac __unused
, char **av __unused
, const char *id
, int fd
)
147 nvme_getlog_ioctl_t ioc
;
148 nvme_log_smart_data_t
*smart
;
152 bzero(&ioc
, sizeof(ioc
));
153 ioc
.lid
= NVME_LID_SMART
;
154 ioc
.ret_size
= sizeof(ioc
.info
.logsmart
);
156 if (ioctl(fd
, NVMEIOCGETLOG
, &ioc
) < 0) {
157 fprintf(stderr
, "ioctl failed: %s\n", strerror(errno
));
160 if (NVME_COMQ_STATUS_CODE_GET(ioc
.status
)) {
161 fprintf(stderr
, "%s: type %d code 0x%02x\n",
163 NVME_COMQ_STATUS_TYPE_GET(ioc
.status
),
164 NVME_COMQ_STATUS_CODE_GET(ioc
.status
));
168 smart
= &ioc
.info
.logsmart
;
170 printf("\tcrit_flags:\t");
171 if (smart
->crit_flags
) {
172 if (smart
->crit_flags
& NVME_SMART_CRF_RES80
)
174 if (smart
->crit_flags
& NVME_SMART_CRF_RES40
)
176 if (smart
->crit_flags
& NVME_SMART_CRF_RES20
)
178 if (smart
->crit_flags
& NVME_SMART_CRF_VOLTL_BKUP_FAIL
)
179 printf(" MEM_BACKUP_FAILED");
180 if (smart
->crit_flags
& NVME_SMART_CRF_MEDIA_RO
)
181 printf(" MEDIA_RDONLY");
182 if (smart
->crit_flags
& NVME_SMART_CRF_UNRELIABLE
)
183 printf(" MEDIA_UNRELIABLE");
184 if (smart
->crit_flags
& NVME_SMART_CRF_ABOVE_THRESH
)
186 if (smart
->crit_flags
& NVME_SMART_CRF_BELOW_THRESH
)
191 printf("\tcomp_temp:\t%dC\n",
192 (int)(smart
->comp_temp1
+ (smart
->comp_temp2
<< 8)) - 273);
193 printf("\tLIFE_LEFT:\t%d%% (%d%% used)\n",
194 100 - (int)smart
->rated_life
,
195 (int)smart
->rated_life
);
197 printf("\tread_bytes:\t%s\n",
198 format_number(smart
->read_count
[0] * 512000));
199 printf("\twrite_bytes:\t%s\n",
200 format_number(smart
->write_count
[0] * 512000));
201 printf("\tread_cmds:\t%s\n",
202 format_number(smart
->read_cmds
[0]));
203 printf("\twrite_cmds:\t%s\n",
204 format_number(smart
->write_cmds
[0]));
205 printf("\tbusy_time:\t%ld min (%1.2f hrs)\n",
207 (double)smart
->busy_time
[0] / 60.0);
208 printf("\tpowon_hours:\t%ld\n", smart
->powon_hours
[0]);
209 printf("\tpower_cyc:\t%ld\n", smart
->power_cycles
[0]);
210 printf("\tunsafe_shut:\t%ld\n", smart
->unsafe_shutdowns
[0]);
212 printf("\tUNRECOV_ERR:\t%ld", smart
->unrecoverable_errors
[0]);
213 if (smart
->unrecoverable_errors
[0])
214 printf("\t*******WARNING*******");
217 printf("\terr_log_ent:\t%ld\n", smart
->error_log_entries
[0]);
218 printf("\twarn_temp_time:\t%d min (%1.2f hrs)\n",
219 smart
->warn_comp_temp_time
,
220 (double)smart
->warn_comp_temp_time
/ 60.0);
221 printf("\tcrit_temp_time:\t%d min (%1.2f hrs)\n",
222 smart
->crit_comp_temp_time
,
223 (double)smart
->crit_comp_temp_time
/ 60.0);
225 printf("\ttemp_sensors:\t");
226 for (i
= count
= 0; i
< 8; ++i
) {
227 if (smart
->temp_sensors
[i
]) {
230 printf("%dC", smart
->temp_sensors
[i
] - 273);
243 cmd_errors(int ac __unused
, char **av __unused
, const char *id
, int fd
)
245 nvme_getlog_ioctl_t ioc
;
246 nvme_log_error_data_t
*errs
;
249 bzero(&ioc
, sizeof(ioc
));
250 ioc
.lid
= NVME_LID_ERROR
;
251 ioc
.ret_size
= sizeof(ioc
.info
.logsmart
);
253 if (ioctl(fd
, NVMEIOCGETLOG
, &ioc
) < 0) {
254 fprintf(stderr
, "ioctl failed: %s\n", strerror(errno
));
257 if (NVME_COMQ_STATUS_CODE_GET(ioc
.status
)) {
258 fprintf(stderr
, "%s: type %d code 0x%02x\n",
260 NVME_COMQ_STATUS_TYPE_GET(ioc
.status
),
261 NVME_COMQ_STATUS_CODE_GET(ioc
.status
));
265 errs
= &ioc
.info
.logerr
[0];
267 for (i
= 0; i
< 64; ++i
) {
268 if (errs
->error_count
== 0 && errs
->subq_id
== 0 &&
269 errs
->cmd_id
== 0 && errs
->status
== 0 &&
270 errs
->param
== 0 && errs
->nsid
== 0 &&
271 errs
->vendor
== 0 && errs
->csi
== 0 && errs
->lba
== 0)
274 if (errs
->param
|| errs
->vendor
|| errs
->csi
) {
275 printf("\t%2d cnt=%-3ld subq=%-2d cmdi=%-3d "
276 "status=%d,0x%02x parm=%04x nsid=%-3d vend=%d "
278 i
, errs
->error_count
,
279 (int16_t)errs
->subq_id
,
280 (int16_t)errs
->cmd_id
,
281 NVME_COMQ_STATUS_TYPE_GET(errs
->status
),
282 NVME_COMQ_STATUS_CODE_GET(errs
->status
),
283 errs
->param
, errs
->nsid
,
285 errs
->csi
, errs
->lba
);
287 printf("\t%2d cnt=%-3ld subq=%-2d cmdi=%-3d "
288 "status=%d,0x%02x nsid=%-3d lba=%ld",
289 i
, errs
->error_count
,
290 (int16_t)errs
->subq_id
,
291 (int16_t)errs
->cmd_id
,
292 NVME_COMQ_STATUS_TYPE_GET(errs
->status
),
293 NVME_COMQ_STATUS_CODE_GET(errs
->status
),
297 if (errs
->status
& NVME_COMQ_STATUS_DNR
)
299 printf(" %s\n", status_to_str(errs
->status
));
311 "nvmectl [-v] cmd [nvme0,1,2...]\n"