Index: head/sbin/nvmecontrol/logpage.c =================================================================== --- head/sbin/nvmecontrol/logpage.c (revision 344468) +++ head/sbin/nvmecontrol/logpage.c (revision 344469) @@ -1,467 +1,474 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 EMC Corp. * All rights reserved. * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" #define LOGPAGE_USAGE \ "logpage <-p page_id> [-b] [-v vendor] [-x] \n" \ #define MAX_FW_SLOTS (7) -SET_CONCAT_DEF(logpage, struct logpage_function); +static SLIST_HEAD(,logpage_function) logpages; +void +logpage_register(struct logpage_function *p) +{ + + SLIST_INSERT_HEAD(&logpages, p, link); +} + const char * kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key) { static char bad[32]; size_t i; for (i = 0; i < kv_count; i++, kv++) if (kv->key == key) return kv->name; snprintf(bad, sizeof(bad), "Attribute %#x", key); return bad; } static void print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) { print_hex(data, length); } static void print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) { write(STDOUT_FILENO, data, length); } static void * get_log_buffer(uint32_t size) { void *buf; if ((buf = malloc(size)) == NULL) errx(1, "unable to malloc %u bytes", size); memset(buf, 0, size); return (buf); } void read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload, uint32_t payload_size) { struct nvme_pt_command pt; struct nvme_error_information_entry *err_entry; int i, err_pages; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; pt.cmd.nsid = htole32(nsid); pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; pt.cmd.cdw10 |= log_page; pt.cmd.cdw10 = htole32(pt.cmd.cdw10); pt.buf = payload; pt.len = payload_size; pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "get log page request failed"); /* Convert data to host endian */ switch (log_page) { case NVME_LOG_ERROR: err_entry = (struct nvme_error_information_entry *)payload; err_pages = payload_size / sizeof(struct nvme_error_information_entry); for (i = 0; i < err_pages; i++) nvme_error_information_entry_swapbytes(err_entry++); break; case NVME_LOG_HEALTH_INFORMATION: nvme_health_information_page_swapbytes( (struct nvme_health_information_page *)payload); break; case NVME_LOG_FIRMWARE_SLOT: nvme_firmware_page_swapbytes( (struct nvme_firmware_page *)payload); break; case INTEL_LOG_TEMP_STATS: intel_log_temp_stats_swapbytes( (struct intel_log_temp_stats *)payload); break; default: break; } if (nvme_completion_is_error(&pt.cpl)) errx(1, "get log page request returned error"); } static void print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) { int i, nentries; uint16_t status; uint8_t p, sc, sct, m, dnr; struct nvme_error_information_entry *entry = buf; printf("Error Information Log\n"); printf("=====================\n"); if (entry->error_count == 0) { printf("No error entries found\n"); return; } nentries = size/sizeof(struct nvme_error_information_entry); for (i = 0; i < nentries; i++, entry++) { if (entry->error_count == 0) break; status = entry->status; p = NVME_STATUS_GET_P(status); sc = NVME_STATUS_GET_SC(status); sct = NVME_STATUS_GET_SCT(status); m = NVME_STATUS_GET_M(status); dnr = NVME_STATUS_GET_DNR(status); printf("Entry %02d\n", i + 1); printf("=========\n"); printf(" Error count: %ju\n", entry->error_count); printf(" Submission queue ID: %u\n", entry->sqid); printf(" Command ID: %u\n", entry->cid); /* TODO: Export nvme_status_string structures from kernel? */ printf(" Status:\n"); printf(" Phase tag: %d\n", p); printf(" Status code: %d\n", sc); printf(" Status code type: %d\n", sct); printf(" More: %d\n", m); printf(" DNR: %d\n", dnr); printf(" Error location: %u\n", entry->error_location); printf(" LBA: %ju\n", entry->lba); printf(" Namespace ID: %u\n", entry->nsid); printf(" Vendor specific info: %u\n", entry->vendor_specific); } } void print_temp(uint16_t t) { printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); } static void print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) { struct nvme_health_information_page *health = buf; char cbuf[UINT128_DIG + 1]; uint8_t warning; int i; warning = health->critical_warning; printf("SMART/Health Information Log\n"); printf("============================\n"); printf("Critical Warning State: 0x%02x\n", warning); printf(" Available spare: %d\n", !!(warning & NVME_CRIT_WARN_ST_AVAILABLE_SPARE)); printf(" Temperature: %d\n", !!(warning & NVME_CRIT_WARN_ST_TEMPERATURE)); printf(" Device reliability: %d\n", !!(warning & NVME_CRIT_WARN_ST_DEVICE_RELIABILITY)); printf(" Read only: %d\n", !!(warning & NVME_CRIT_WARN_ST_READ_ONLY)); printf(" Volatile memory backup: %d\n", !!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP)); printf("Temperature: "); print_temp(health->temperature); printf("Available spare: %u\n", health->available_spare); printf("Available spare threshold: %u\n", health->available_spare_threshold); printf("Percentage used: %u\n", health->percentage_used); printf("Data units (512,000 byte) read: %s\n", uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); printf("Data units written: %s\n", uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); printf("Host read commands: %s\n", uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); printf("Host write commands: %s\n", uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf))); printf("Controller busy time (minutes): %s\n", uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf))); printf("Power cycles: %s\n", uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf))); printf("Power on hours: %s\n", uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf))); printf("Unsafe shutdowns: %s\n", uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf))); printf("Media errors: %s\n", uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); printf("No. error info log entries: %s\n", uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); printf("Error Temp Composite Time: %d\n", health->error_temp_time); for (i = 0; i < 8; i++) { if (health->temp_sensor[i] == 0) continue; printf("Temperature Sensor %d: ", i + 1); print_temp(health->temp_sensor[i]); } } static void print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused) { int i, slots; const char *status; struct nvme_firmware_page *fw = buf; uint8_t afi_slot; uint16_t oacs_fw; uint8_t fw_num_slots; afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT; afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK; oacs_fw = (cdata->oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) & NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) & NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; printf("Firmware Slot Log\n"); printf("=================\n"); if (oacs_fw == 0) slots = 1; else slots = MIN(fw_num_slots, MAX_FW_SLOTS); for (i = 0; i < slots; i++) { printf("Slot %d: ", i + 1); if (afi_slot == i + 1) status = " Active"; else status = "Inactive"; if (fw->revision[i] == 0LLU) printf("Empty\n"); else if (isprint(*(char *)&fw->revision[i])) printf("[%s] %.8s\n", status, (char *)&fw->revision[i]); else printf("[%s] %016jx\n", status, fw->revision[i]); } } /* * Table of log page printer / sizing. * * Make sure you keep all the pages of one vendor together so -v help * lists all the vendors pages. */ NVME_LOGPAGE(error, NVME_LOG_ERROR, NULL, "Drive Error Log", print_log_error, 0); NVME_LOGPAGE(health, NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data", print_log_health, sizeof(struct nvme_health_information_page)); NVME_LOGPAGE(fw, NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information", print_log_firmware, sizeof(struct nvme_firmware_page)); static void logpage_help(void) { - const struct logpage_function * const *f; + const struct logpage_function *f; const char *v; fprintf(stderr, "\n"); fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name"); fprintf(stderr, "-------- ---------- ----------\n"); - for (f = logpage_begin(); f < logpage_limit(); f++) { - v = (*f)->vendor == NULL ? "-" : (*f)->vendor; - fprintf(stderr, "0x%02x %-10s %s\n", (*f)->log_page, v, (*f)->name); + SLIST_FOREACH(f, &logpages, link) { + v = f->vendor == NULL ? "-" : f->vendor; + fprintf(stderr, "0x%02x %-10s %s\n", f->log_page, v, f->name); } exit(1); } static void logpage(const struct nvme_function *nf, int argc, char *argv[]) { int fd; int log_page = 0, pageflag = false; int binflag = false, hexflag = false, ns_specified; int opt; char *p; char cname[64]; uint32_t nsid, size; void *buf; const char *vendor = NULL; - const struct logpage_function * const *f; + const struct logpage_function *f; struct nvme_controller_data cdata; print_fn_t print_fn; uint8_t ns_smart; while ((opt = getopt(argc, argv, "bp:xv:")) != -1) { switch (opt) { case 'b': binflag = true; break; case 'p': if (strcmp(optarg, "help") == 0) logpage_help(); /* TODO: Add human-readable ASCII page IDs */ log_page = strtol(optarg, &p, 0); if (p != NULL && *p != '\0') { fprintf(stderr, "\"%s\" not valid log page id.\n", optarg); usage(nf); } pageflag = true; break; case 'x': hexflag = true; break; case 'v': if (strcmp(optarg, "help") == 0) logpage_help(); vendor = optarg; break; } } if (!pageflag) { printf("Missing page_id (-p).\n"); usage(nf); } /* Check that a controller and/or namespace was specified. */ if (optind >= argc) usage(nf); if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { ns_specified = true; parse_ns_str(argv[optind], cname, &nsid); open_dev(cname, &fd, 1, 1); } else { ns_specified = false; nsid = NVME_GLOBAL_NAMESPACE_TAG; open_dev(argv[optind], &fd, 1, 1); } read_controller_data(fd, &cdata); ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) & NVME_CTRLR_DATA_LPA_NS_SMART_MASK; /* * The log page attribtues indicate whether or not the controller * supports the SMART/Health information log page on a per * namespace basis. */ if (ns_specified) { if (log_page != NVME_LOG_HEALTH_INFORMATION) errx(1, "log page %d valid only at controller level", log_page); if (ns_smart == 0) errx(1, "controller does not support per namespace " "smart/health information"); } print_fn = print_log_hex; size = DEFAULT_SIZE; if (binflag) print_fn = print_bin; if (!binflag && !hexflag) { /* * See if there is a pretty print function for the specified log * page. If one isn't found, we just revert to the default * (print_hex). If there was a vendor specified by the user, and * the page is vendor specific, don't match the print function * unless the vendors match. */ - for (f = logpage_begin(); f < logpage_limit(); f++) { - if ((*f)->vendor != NULL && vendor != NULL && - strcmp((*f)->vendor, vendor) != 0) + SLIST_FOREACH(f, &logpages, link) { + if (f->vendor != NULL && vendor != NULL && + strcmp(f->vendor, vendor) != 0) continue; - if (log_page != (*f)->log_page) + if (log_page != f->log_page) continue; - print_fn = (*f)->print_fn; - size = (*f)->size; + print_fn = f->print_fn; + size = f->size; break; } } if (log_page == NVME_LOG_ERROR) { size = sizeof(struct nvme_error_information_entry); size *= (cdata.elpe + 1); } /* Read the log page */ buf = get_log_buffer(size); read_logpage(fd, log_page, nsid, buf, size); print_fn(&cdata, buf, size); close(fd); exit(0); } NVME_COMMAND(top, logpage, logpage, LOGPAGE_USAGE); Index: head/sbin/nvmecontrol/nvmecontrol.c =================================================================== --- head/sbin/nvmecontrol/nvmecontrol.c (revision 344468) +++ head/sbin/nvmecontrol/nvmecontrol.c (revision 344469) @@ -1,351 +1,348 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nvmecontrol.h" SET_CONCAT_DEF(top, struct nvme_function); static void print_usage(const struct nvme_function *f) { const char *cp; char ch; bool need_prefix = true; cp = f->usage; while (*cp) { ch = *cp++; if (need_prefix) { if (ch != ' ') fputs(" nvmecontrol ", stderr); else fputs(" ", stderr); } fputc(ch, stderr); need_prefix = (ch == '\n'); } if (!need_prefix) fputc('\n', stderr); } static void gen_usage_set(const struct nvme_function * const *f, const struct nvme_function * const *flimit) { fprintf(stderr, "usage:\n"); while (f < flimit) { print_usage(*f); f++; } exit(1); } void usage(const struct nvme_function *f) { fprintf(stderr, "usage:\n"); print_usage(f); exit(1); } void dispatch_set(int argc, char *argv[], const struct nvme_function * const *tbl, const struct nvme_function * const *tbl_limit) { const struct nvme_function * const *f = tbl; if (argv[1] == NULL) { gen_usage_set(tbl, tbl_limit); return; } while (f < tbl_limit) { if (strcmp(argv[1], (*f)->name) == 0) { (*f)->fn(*f, argc-1, &argv[1]); return; } f++; } fprintf(stderr, "Unknown command: %s\n", argv[1]); gen_usage_set(tbl, tbl_limit); } void set_concat_add(struct set_concat *m, void *b, void *e) { void **bp, **ep; int add_n, cur_n; if (b == NULL) return; /* * Args are really pointers to arrays of pointers, but C's * casting rules kinda suck since you can't directly cast * struct foo ** to a void **. */ bp = (void **)b; ep = (void **)e; add_n = ep - bp; cur_n = 0; if (m->begin != NULL) cur_n = m->limit - m->begin; m->begin = reallocarray(m->begin, cur_n + add_n, sizeof(void *)); if (m->begin == NULL) err(1, "expanding concat set"); memcpy(m->begin + cur_n, bp, add_n * sizeof(void *)); m->limit = m->begin + cur_n + add_n; } static void print_bytes(void *data, uint32_t length) { uint32_t i, j; uint8_t *p, *end; end = (uint8_t *)data + length; for (i = 0; i < length; i++) { p = (uint8_t *)data + (i*16); printf("%03x: ", i*16); for (j = 0; j < 16 && p < end; j++) printf("%02x ", *p++); if (p >= end) break; printf("\n"); } printf("\n"); } static void print_dwords(void *data, uint32_t length) { uint32_t *p; uint32_t i, j; p = (uint32_t *)data; length /= sizeof(uint32_t); for (i = 0; i < length; i+=8) { printf("%03x: ", i*4); for (j = 0; j < 8; j++) printf("%08x ", p[i+j]); printf("\n"); } printf("\n"); } void print_hex(void *data, uint32_t length) { if (length >= sizeof(uint32_t) || length % sizeof(uint32_t) == 0) print_dwords(data, length); else print_bytes(data, length); } void read_controller_data(int fd, struct nvme_controller_data *cdata) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.cdw10 = htole32(1); pt.buf = cdata; pt.len = sizeof(*cdata); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); /* Convert data to host endian */ nvme_controller_data_swapbytes(cdata); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); } void read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata) { struct nvme_pt_command pt; memset(&pt, 0, sizeof(pt)); pt.cmd.opc = NVME_OPC_IDENTIFY; pt.cmd.nsid = htole32(nsid); pt.buf = nsdata; pt.len = sizeof(*nsdata); pt.is_read = 1; if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(1, "identify request failed"); /* Convert data to host endian */ nvme_namespace_data_swapbytes(nsdata); if (nvme_completion_is_error(&pt.cpl)) errx(1, "identify request returned error"); } int open_dev(const char *str, int *fd, int show_error, int exit_on_error) { char full_path[64]; if (!strnstr(str, NVME_CTRLR_PREFIX, strlen(NVME_CTRLR_PREFIX))) { if (show_error) warnx("controller/namespace ids must begin with '%s'", NVME_CTRLR_PREFIX); if (exit_on_error) exit(1); else return (EINVAL); } snprintf(full_path, sizeof(full_path), _PATH_DEV"%s", str); *fd = open(full_path, O_RDWR); if (*fd < 0) { if (show_error) warn("could not open %s", full_path); if (exit_on_error) exit(1); else return (errno); } return (0); } void parse_ns_str(const char *ns_str, char *ctrlr_str, uint32_t *nsid) { char *nsloc; /* * Pull the namespace id from the string. +2 skips past the "ns" part * of the string. Don't search past 10 characters into the string, * otherwise we know it is malformed. */ nsloc = strnstr(ns_str, NVME_NS_PREFIX, 10); if (nsloc != NULL) *nsid = strtol(nsloc + 2, NULL, 10); if (nsloc == NULL || (*nsid == 0 && errno != 0)) errx(1, "invalid namespace ID '%s'", ns_str); /* * The controller string will include only the nvmX part of the * nvmeXnsY string. */ snprintf(ctrlr_str, nsloc - ns_str + 1, "%s", ns_str); } /* * Loads all the .so's from the specified directory. */ static void load_dir(const char *dir) { DIR *d; struct dirent *dent; char *path = NULL; void *h; d = opendir(dir); if (d == NULL) return; for (dent = readdir(d); dent != NULL; dent = readdir(d)) { if (strcmp(".so", dent->d_name + dent->d_namlen - 3) != 0) continue; asprintf(&path, "%s/%s", dir, dent->d_name); if (path == NULL) err(1, "Can't malloc for path, giving up."); if ((h = dlopen(path, RTLD_NOW | RTLD_GLOBAL)) == NULL) warnx("Can't load %s: %s", path, dlerror()); else { /* - * Add in the top (for cli commands) and logpage (for - * logpage parsing) linker sets. We have to do this by - * hand because linker sets aren't automatically merged. + * Add in the top (for cli commands)linker sets. We have + * to do this by hand because linker sets aren't + * automatically merged. */ void *begin, *limit; + begin = dlsym(h, "__start_set_top"); limit = dlsym(h, "__stop_set_top"); if (begin) add_to_top(begin, limit); - begin = dlsym(h, "__start_set_logpage"); - limit = dlsym(h, "__stop_set_logpage"); - if (begin) - add_to_logpage(begin, limit); + /* log pages use constructors so are done on load */ } free(path); path = NULL; } closedir(d); } int main(int argc, char *argv[]) { add_to_top(NVME_CMD_BEGIN(top), NVME_CMD_LIMIT(top)); - add_to_logpage(NVME_LOGPAGE_BEGIN, NVME_LOGPAGE_LIMIT); load_dir("/lib/nvmecontrol"); load_dir("/usr/local/lib/nvmecontrol"); if (argc < 2) gen_usage_set(top_begin(), top_limit()); dispatch_set(argc, argv, top_begin(), top_limit()); return (0); } Index: head/sbin/nvmecontrol/nvmecontrol.h =================================================================== --- head/sbin/nvmecontrol/nvmecontrol.h (revision 344468) +++ head/sbin/nvmecontrol/nvmecontrol.h (revision 344469) @@ -1,155 +1,153 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef __NVMECONTROL_H__ #define __NVMECONTROL_H__ #include +#include #include struct nvme_function; typedef void (*nvme_fn_t)(const struct nvme_function *nf, int argc, char *argv[]); struct nvme_function { const char *name; nvme_fn_t fn; const char *usage; }; #define NVME_SETNAME(set) set #define NVME_CMDSET(set, sym) DATA_SET(NVME_SETNAME(set), sym) #define NVME_COMMAND(set, nam, function, usage_str) \ static struct nvme_function function ## _nvme_cmd = \ { .name = #nam, .fn = function, .usage = usage_str }; \ NVME_CMDSET(set, function ## _nvme_cmd) #define NVME_CMD_BEGIN(set) SET_BEGIN(NVME_SETNAME(set)) #define NVME_CMD_LIMIT(set) SET_LIMIT(NVME_SETNAME(set)) #define NVME_CMD_DECLARE(set, t) SET_DECLARE(NVME_SETNAME(set), t) typedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size); struct logpage_function { + SLIST_ENTRY(logpage_function) link; uint8_t log_page; const char *vendor; const char *name; print_fn_t print_fn; size_t size; }; -#define NVME_LOGPAGESET(sym) DATA_SET(NVME_SETNAME(logpage), sym) #define NVME_LOGPAGE(unique, lp, vend, nam, fn, sz) \ static struct logpage_function unique ## _lpf = { \ .log_page = lp, \ .vendor = vend, \ .name = nam, \ .print_fn = fn, \ .size = sz, \ } ; \ - NVME_LOGPAGESET(unique ## _lpf) -#define NVME_LOGPAGE_BEGIN SET_BEGIN(NVME_SETNAME(logpage)) -#define NVME_LOGPAGE_LIMIT SET_LIMIT(NVME_SETNAME(logpage)) -#define NVME_LOGPAGE_DECLARE(t) SET_DECLARE(NVME_SETNAME(logpage), t) + static void logpage_reg_##unique(void) __attribute__((constructor)); \ + static void logpage_reg_##unique(void) { logpage_register(&unique##_lpf); } #define DEFAULT_SIZE (4096) struct kv_name { uint32_t key; const char *name; }; const char *kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key); NVME_CMD_DECLARE(top, struct nvme_function); -NVME_LOGPAGE_DECLARE(struct logpage_function); +void logpage_register(struct logpage_function *p); struct set_concat { void **begin; void **limit; }; void set_concat_add(struct set_concat *m, void *begin, void *end); #define SET_CONCAT_DEF(set, t) \ static struct set_concat set ## _concat; \ static inline const t * const *set ## _begin(void) { return ((const t * const *)set ## _concat.begin); } \ static inline const t * const *set ## _limit(void) { return ((const t * const *)set ## _concat.limit); } \ void add_to_ ## set(t **b, t **e) \ { \ set_concat_add(&set ## _concat, b, e); \ } #define SET_CONCAT_DECL(set, t) \ void add_to_ ## set(t **b, t **e) SET_CONCAT_DECL(top, struct nvme_function); -SET_CONCAT_DECL(logpage, struct logpage_function); #define NVME_CTRLR_PREFIX "nvme" #define NVME_NS_PREFIX "ns" int open_dev(const char *str, int *fd, int show_error, int exit_on_error); void parse_ns_str(const char *ns_str, char *ctrlr_str, uint32_t *nsid); void read_controller_data(int fd, struct nvme_controller_data *cdata); void read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata); void print_hex(void *data, uint32_t length); void read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload, uint32_t payload_size); void print_temp(uint16_t t); void print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused); void usage(const struct nvme_function *f); void dispatch_set(int argc, char *argv[], const struct nvme_function * const *tbl, const struct nvme_function * const *tbl_limit); #define DISPATCH(argc, argv, set) \ dispatch_set(argc, argv, \ (const struct nvme_function * const *)NVME_CMD_BEGIN(set), \ (const struct nvme_function * const *)NVME_CMD_LIMIT(set)) \ /* Utility Routines */ /* * 128-bit integer augments to standard values. On i386 this * doesn't exist, so we use 64-bit values. So, on 32-bit i386, * you'll get truncated values until someone implement 128bit * ints in sofware. */ #define UINT128_DIG 39 #ifdef __i386__ typedef uint64_t uint128_t; #else typedef __uint128_t uint128_t; #endif static __inline uint128_t to128(void *p) { return *(uint128_t *)p; } uint64_t le48dec(const void *pp); char * uint128_to_str(uint128_t u, char *buf, size_t buflen); #endif