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 } 317