nrelease - fix/improve livecd
[dragonfly.git] / sbin / nvmectl / nvmectl.c
1 /*
2  * Copyright (c) 2016 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
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
16  *    distribution.
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.
20  *
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
32  * SUCH DAMAGE.
33  */
34
35 #include "nvmectl.h"
36
37 typedef int (*cmd_t)(int ac, char **av, const char *id, int fd);
38
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);
43
44 int VerboseOpt;
45
46 int
47 main(int ac, char **av)
48 {
49         int rc = 0;
50         int ch;
51         int nvmei;
52         int globok;
53         int i;
54         cmd_t cmd;
55
56         while ((ch = getopt(ac, av, "v")) != -1) {
57                 switch(ch) {
58                 case 'v':
59                         ++VerboseOpt;
60                         break;
61                 default:
62                         usage(1);
63                         break;
64                 }
65         }
66
67         ac -= optind;
68         av += optind;
69
70         for (nvmei = ac; nvmei > 0; --nvmei) {
71                 if (strncmp(av[nvmei - 1], "nvme", 4) != 0)
72                         break;
73         }
74         if (nvmei == 0)
75                 usage(1);
76
77         globok = 0;
78         cmd = parsecmd(nvmei, av, &globok);
79
80         if (nvmei == ac && globok) {
81                 i = 0;
82                 for (;;) {
83                         char *path;
84                         int fd;
85
86                         if (i)
87                                 printf("\n");
88                         asprintf(&path, "/dev/nvme%d", i);
89                         fd = open(path, O_RDWR);
90                         free(path);
91                         if (fd < 0)
92                                 break;
93                         rc += cmd(nvmei, av, path + 5, fd);
94                         close(fd);
95                         ++i;
96                 }
97         } else if (nvmei == ac && !globok) {
98                 fprintf(stderr, "must specify nvmeX device for command\n");
99         } else {
100                 for (i = nvmei; i < ac; ++i) {
101                         char *path;
102                         int fd;
103
104                         if (i != nvmei)
105                                 printf("\n");
106
107                         asprintf(&path, "/dev/%s", av[i]);
108                         fd = open(path, O_RDWR);
109                         if (fd < 0) {
110                                 fprintf(stderr, "open \"%s\": %s\n",
111                                         path,
112                                         strerror(errno));
113                         } else {
114                                 rc += cmd(nvmei, av, path + 5, fd);
115                                 close(fd);
116                         }
117                         free(path);
118                 }
119         }
120         return (rc ? 1 : 0);
121 }
122
123 static
124 cmd_t
125 parsecmd(int ac, char **av, int *globokp)
126 {
127         if (ac == 0)
128                 usage(1);
129         if (strcmp(av[0], "info") == 0) {
130                 *globokp = 1;
131                 return cmd_info;
132         }
133         if (strcmp(av[0], "errors") == 0) {
134                 *globokp = 1;
135                 return cmd_errors;
136         }
137         fprintf(stderr, "Command %s not recognized\n", av[0]);
138
139         usage(1);
140         return NULL;    /* NOT REACHED */
141 }
142
143 static
144 int
145 cmd_info(int ac __unused, char **av __unused, const char *id, int fd)
146 {
147         nvme_getlog_ioctl_t ioc;
148         nvme_log_smart_data_t *smart;
149         int count;
150         int i;
151
152         bzero(&ioc, sizeof(ioc));
153         ioc.lid = NVME_LID_SMART;
154         ioc.ret_size = sizeof(ioc.info.logsmart);
155
156         if (ioctl(fd, NVMEIOCGETLOG, &ioc) < 0) {
157                 fprintf(stderr, "ioctl failed: %s\n", strerror(errno));
158                 return 1;
159         }
160         if (NVME_COMQ_STATUS_CODE_GET(ioc.status)) {
161                 fprintf(stderr, "%s: type %d code 0x%02x\n",
162                         id,
163                         NVME_COMQ_STATUS_TYPE_GET(ioc.status),
164                         NVME_COMQ_STATUS_CODE_GET(ioc.status));
165                 return 1;
166         }
167         printf("%s:\n", id);
168         smart = &ioc.info.logsmart;
169
170         printf("\tcrit_flags:\t");
171         if (smart->crit_flags) {
172                 if (smart->crit_flags & NVME_SMART_CRF_RES80)
173                         printf(" 80");
174                 if (smart->crit_flags & NVME_SMART_CRF_RES40)
175                         printf(" 40");
176                 if (smart->crit_flags & NVME_SMART_CRF_RES20)
177                         printf(" 20");
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)
185                         printf(" TOO_HOT");
186                 if (smart->crit_flags & NVME_SMART_CRF_BELOW_THRESH)
187                         printf(" TOO_COLD");
188         } else {
189                 printf("none\n");
190         }
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);
196
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",
206                 smart->busy_time[0],
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]);
211
212         printf("\tUNRECOV_ERR:\t%ld", smart->unrecoverable_errors[0]);
213         if (smart->unrecoverable_errors[0])
214                 printf("\t*******WARNING*******");
215         printf("\n");
216
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);
224
225         printf("\ttemp_sensors:\t");
226         for (i = count = 0; i < 8; ++i) {
227                 if (smart->temp_sensors[i]) {
228                         if (count)
229                                 printf(" ");
230                         printf("%dC", smart->temp_sensors[i] - 273);
231                         ++count;
232                 }
233         }
234         if (count == 0)
235                 printf("none");
236         printf("\n");
237
238         return 0;
239 }
240
241 static
242 int
243 cmd_errors(int ac __unused, char **av __unused, const char *id, int fd)
244 {
245         nvme_getlog_ioctl_t ioc;
246         nvme_log_error_data_t *errs;
247         int i;
248
249         bzero(&ioc, sizeof(ioc));
250         ioc.lid = NVME_LID_ERROR;
251         ioc.ret_size = sizeof(ioc.info.logsmart);
252
253         if (ioctl(fd, NVMEIOCGETLOG, &ioc) < 0) {
254                 fprintf(stderr, "ioctl failed: %s\n", strerror(errno));
255                 return 1;
256         }
257         if (NVME_COMQ_STATUS_CODE_GET(ioc.status)) {
258                 fprintf(stderr, "%s: type %d code 0x%02x\n",
259                         id,
260                         NVME_COMQ_STATUS_TYPE_GET(ioc.status),
261                         NVME_COMQ_STATUS_CODE_GET(ioc.status));
262                 return 1;
263         }
264         printf("%s:\n", id);
265         errs = &ioc.info.logerr[0];
266
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)
272                         continue;
273
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 "
277                                "csi=0x%lx lba=%ld",
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,
284                                errs->vendor,
285                                errs->csi, errs->lba);
286                 } else {
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),
294                                errs->nsid,
295                                errs->lba);
296                 }
297                 if (errs->status & NVME_COMQ_STATUS_DNR)
298                         printf(" DNR");
299                 printf(" %s\n", status_to_str(errs->status));
300                 ++errs;
301         }
302
303         return 0;
304 }
305
306 static
307 void
308 usage(int rc)
309 {
310         fprintf(stderr,
311                 "nvmectl [-v] cmd [nvme0,1,2...]\n"
312                 "\tinfo\n"
313                 "\terrors\n"
314         );
315         exit(rc);
316 }