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"