diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/discover-loop.c | 89 | ||||
-rw-r--r-- | examples/discover-loop.py | 35 | ||||
-rw-r--r-- | examples/display-columnar.c | 121 | ||||
-rw-r--r-- | examples/display-tree.c | 71 | ||||
-rw-r--r-- | examples/meson.build | 34 | ||||
-rw-r--r-- | examples/telemetry-listen.c | 167 |
6 files changed, 517 insertions, 0 deletions
diff --git a/examples/discover-loop.c b/examples/discover-loop.c new file mode 100644 index 0000000..a577cd4 --- /dev/null +++ b/examples/discover-loop.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/** + * This file is part of libnvme. + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: Keith Busch <keith.busch@wdc.com> + */ + +/** + * discover-loop: Use fabrics commands to discover any loop targets and print + * those records. You must have at least one configured nvme loop target on the + * system (no existing connection required). The output will look more + * interesting with more targets. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <libnvme.h> + +#include <ccan/endian/endian.h> + +static void print_discover_log(struct nvmf_discovery_log *log) +{ + int i, numrec = le64_to_cpu(log->numrec); + + printf(".\n"); + printf("|-- genctr:%llx\n", log->genctr); + printf("|-- numrec:%x\n", numrec); + printf("`-- recfmt:%x\n", log->recfmt); + + for (i = 0; i < numrec; i++) { + struct nvmf_disc_log_entry *e = &log->entries[i]; + + printf(" %c-- Entry:%d\n", (i < numrec - 1) ? '|' : '`', i); + printf(" %c |-- trtype:%x\n", (i < numrec - 1) ? '|' : ' ', e->trtype); + printf(" %c |-- adrfam:%x\n", (i < numrec - 1) ? '|' : ' ', e->adrfam); + printf(" %c |-- subtype:%x\n", (i < numrec - 1) ? '|' : ' ', e->subtype); + printf(" %c |-- treq:%x\n", (i < numrec - 1) ? '|' : ' ', e->treq); + printf(" %c |-- portid:%x\n", (i < numrec - 1) ? '|' : ' ', e->portid); + printf(" %c |-- cntlid:%x\n", (i < numrec - 1) ? '|' : ' ', e->cntlid); + printf(" %c |-- asqsz:%x\n", (i < numrec - 1) ? '|' : ' ', e->asqsz); + printf(" %c |-- trsvcid:%s\n", (i < numrec - 1) ? '|' : ' ', e->trsvcid); + printf(" %c |-- subnqn:%s\n", (i < numrec - 1) ? '|' : ' ', e->subnqn); + printf(" %c `-- traddr:%s\n", (i < numrec - 1) ? '|' : ' ', e->traddr); + } +} + +int main() +{ + struct nvmf_discovery_log *log = NULL; + nvme_root_t r; + nvme_host_t h; + nvme_ctrl_t c; + int ret; + struct nvme_fabrics_config cfg; + + nvmf_default_config(&cfg); + + r = nvme_scan(NULL); + h = nvme_default_host(r); + if (!h) { + fprintf(stderr, "Failed to allocated memory\n"); + return ENOMEM; + } + c = nvme_create_ctrl(r, NVME_DISC_SUBSYS_NAME, "loop", + NULL, NULL, NULL, NULL); + if (!c) { + fprintf(stderr, "Failed to allocate memory\n"); + return ENOMEM; + } + ret = nvmf_add_ctrl(h, c, &cfg); + if (ret < 0) { + fprintf(stderr, "no controller found\n"); + return errno; + } + + ret = nvmf_get_discovery_log(c, &log, 4); + nvme_disconnect_ctrl(c); + nvme_free_ctrl(c); + + if (ret) + fprintf(stderr, "nvmf-discover-log:%x\n", ret); + else + print_discover_log(log); + + nvme_free_tree(r); + return 0; +} diff --git a/examples/discover-loop.py b/examples/discover-loop.py new file mode 100644 index 0000000..94e8c72 --- /dev/null +++ b/examples/discover-loop.py @@ -0,0 +1,35 @@ +#!/usr/bin/python3 +''' +Example script for nvme discovery + +Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions +Licensed under the Apache License, Version 2.0 (the "License"); you may +not use this file except in compliance with the License. You may obtain +a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. +''' + +from libnvme import nvme +r = nvme.root() +h = nvme.host(r) +c = nvme.ctrl(nvme.NVME_DISC_SUBSYS_NAME, 'loop') +try: + c.connect(h) +except: + sys.exit("Failed to connect!") + +print("connected to %s subsys %s" % (c.name, c.subsystem.name)) +try: + d = c.discover() + print (d) +except: + print("Failed to discover!") + pass +c.disconnect() diff --git a/examples/display-columnar.c b/examples/display-columnar.c new file mode 100644 index 0000000..db98bdf --- /dev/null +++ b/examples/display-columnar.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/** + * This file is part of libnvme. + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: Keith Busch <keith.busch@wdc.com> + */ + +/** + * display-columnar: Scans the nvme topology, prints each record type in a + * column format for easy visual scanning. + */ +#include <stdio.h> +#include <inttypes.h> +#include <libnvme.h> + +static const char dash[101] = {[0 ... 99] = '-'}; +int main() +{ + nvme_root_t r; + nvme_host_t h; + nvme_subsystem_t s; + nvme_ctrl_t c; + nvme_path_t p; + nvme_ns_t n; + + r = nvme_scan(NULL); + if (!r) + return -1; + + + printf("%-16s %-96s %-.16s\n", "Subsystem", "Subsystem-NQN", "Controllers"); + printf("%-.16s %-.96s %-.16s\n", dash, dash, dash); + + nvme_for_each_host(r, h) { + nvme_for_each_subsystem(h, s) { + bool first = true; + printf("%-16s %-96s ", nvme_subsystem_get_name(s), + nvme_subsystem_get_nqn(s)); + + nvme_subsystem_for_each_ctrl(s, c) { + printf("%s%s", first ? "": ", ", + nvme_ctrl_get_name(c)); + first = false; + } + printf("\n"); + } + } + printf("\n"); + + printf("%-8s %-20s %-40s %-8s %-6s %-14s %-12s %-16s\n", "Device", + "SN", "MN", "FR", "TxPort", "Address", "Subsystem", "Namespaces"); + printf("%-.8s %-.20s %-.40s %-.8s %-.6s %-.14s %-.12s %-.16s\n", dash, dash, + dash, dash, dash, dash, dash, dash); + + nvme_for_each_host(r, h) { + nvme_for_each_subsystem(h, s) { + nvme_subsystem_for_each_ctrl(s, c) { + bool first = true; + + printf("%-8s %-20s %-40s %-8s %-6s %-14s %-12s ", + nvme_ctrl_get_name(c), + nvme_ctrl_get_serial(c), + nvme_ctrl_get_model(c), + nvme_ctrl_get_firmware(c), + nvme_ctrl_get_transport(c), + nvme_ctrl_get_address(c), + nvme_subsystem_get_name(s)); + + nvme_ctrl_for_each_ns(c, n) { + printf("%s%s", first ? "": ", ", + nvme_ns_get_name(n)); + first = false; + } + + nvme_ctrl_for_each_path(c, p) { + printf("%s%s", first ? "": ", ", + nvme_ns_get_name(nvme_path_get_ns(p))); + first = false; + } + printf("\n"); + } + } + } + printf("\n"); + + printf("%-12s %-8s %-16s %-8s %-16s\n", "Device", "NSID", "Sectors", "Format", "Controllers"); + printf("%-.12s %-.8s %-.16s %-.8s %-.16s\n", dash, dash, dash, dash, dash); + + nvme_for_each_host(r, h) { + nvme_for_each_subsystem(h, s) { + nvme_subsystem_for_each_ctrl(s, c) { + nvme_ctrl_for_each_ns(c, n) + printf("%-12s %-8d %-16" PRIu64 " %-8d %s\n", + nvme_ns_get_name(n), + nvme_ns_get_nsid(n), + nvme_ns_get_lba_count(n), + nvme_ns_get_lba_size(n), + nvme_ctrl_get_name(c)); + } + + nvme_subsystem_for_each_ns(s, n) { + bool first = true; + + printf("%-12s %-8d %-16" PRIu64 " %-8d ", + nvme_ns_get_name(n), + nvme_ns_get_nsid(n), + nvme_ns_get_lba_count(n), + nvme_ns_get_lba_size(n)); + nvme_subsystem_for_each_ctrl(s, c) { + printf("%s%s", first ? "" : ", ", + nvme_ctrl_get_name(c)); + first = false; + } + printf("\n"); + } + } + } + return 0; +} + diff --git a/examples/display-tree.c b/examples/display-tree.c new file mode 100644 index 0000000..f5bddb2 --- /dev/null +++ b/examples/display-tree.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/** + * This file is part of libnvme. + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: Keith Busch <keith.busch@wdc.com> + */ + +/** + * display-tree: Scans the nvme topology, prints as an ascii tree with some + * selected attributes for each component. + */ +#include <stdio.h> +#include <libnvme.h> + +int main() +{ + nvme_root_t r; + nvme_host_t h; + nvme_subsystem_t s, _s; + nvme_ctrl_t c, _c; + nvme_path_t p, _p; + nvme_ns_t n, _n; + + r = nvme_scan(NULL); + if (!r) + return -1; + + printf(".\n"); + nvme_for_each_host(r, h) { + nvme_for_each_subsystem_safe(h, s, _s) { + printf("%c-- %s - NQN=%s\n", _s ? '|' : '`', + nvme_subsystem_get_name(s), + nvme_subsystem_get_nqn(s)); + + nvme_subsystem_for_each_ns_safe(s, n, _n) { + printf("%c |-- %s lba size:%d lba max:%lu\n", + _s ? '|' : ' ', + nvme_ns_get_name(n), + nvme_ns_get_lba_size(n), + nvme_ns_get_lba_count(n)); + } + + nvme_subsystem_for_each_ctrl_safe(s, c, _c) { + printf("%c %c-- %s %s %s %s\n", + _s ? '|' : ' ', _c ? '|' : '`', + nvme_ctrl_get_name(c), + nvme_ctrl_get_transport(c), + nvme_ctrl_get_address(c), + nvme_ctrl_get_state(c)); + + nvme_ctrl_for_each_ns_safe(c, n, _n) + printf("%c %c %c-- %s lba size:%d lba max:%lu\n", + _s ? '|' : ' ', _c ? '|' : ' ', + _n ? '|' : '`', + nvme_ns_get_name(n), + nvme_ns_get_lba_size(n), + nvme_ns_get_lba_count(n)); + + nvme_ctrl_for_each_path_safe(c, p, _p) + printf("%c %c %c-- %s %s\n", + _s ? '|' : ' ', _c ? '|' : ' ', + _p ? '|' : '`', + nvme_path_get_name(p), + nvme_path_get_ana_state(p)); + } + } + } + nvme_free_tree(r); + return 0; +} diff --git a/examples/meson.build b/examples/meson.build new file mode 100644 index 0000000..f1f8ee5 --- /dev/null +++ b/examples/meson.build @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of libnvme. +# Copyright (c) 2021 Dell Inc. +# +# Authors: Martin Belanger <Martin.Belanger@dell.com> +# +executable( + 'telemetry-listen', + ['telemetry-listen.c'], + link_with: libnvme, + include_directories: [incdir, internal_incdir] +) + +executable( + 'display-columnar', + ['display-columnar.c'], + link_with: libnvme, + include_directories: [incdir, internal_incdir] +) + +executable( + 'display-tree', + ['display-tree.c'], + link_with: libnvme, + include_directories: [incdir, internal_incdir] +) + +executable( + 'discover-loop', + ['discover-loop.c'], + link_with: libnvme, + include_directories: [incdir, internal_incdir] +) diff --git a/examples/telemetry-listen.c b/examples/telemetry-listen.c new file mode 100644 index 0000000..746992b --- /dev/null +++ b/examples/telemetry-listen.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/** + * This file is part of libnvme. + * Copyright (c) 2020 Western Digital Corporation or its affiliates. + * + * Authors: Keith Busch <keith.busch@wdc.com> + */ + +/** + * Open all nvme controller's uevent and listen for changes. If NVME_AEN event + * is observed with controller telemetry data, read the log and save it to a + * file in /var/log/ with the device's unique name and epoch timestamp. + */ +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <libnvme.h> + +#include <ccan/endian/endian.h> + +struct events { + nvme_ctrl_t c; + int uevent_fd; +}; + +static int open_uevent(nvme_ctrl_t c) +{ + char buf[0x1000]; + if (snprintf(buf, sizeof(buf), "%s/uevent", nvme_ctrl_get_sysfs_dir(c)) < 0) + return -1; + return open(buf, O_RDONLY); +} + +static void save_telemetry(nvme_ctrl_t c) +{ + char buf[0x1000]; + size_t log_size; + int ret, fd; + struct nvme_telemetry_log *log; + time_t s; + + /* Clear the log (rae == false) at the end to see new telemetry events later */ + ret = nvme_get_ctrl_telemetry(nvme_ctrl_get_fd(c), false, &log, NVME_TELEMETRY_DA_3, &log_size); + if (ret) + return; + + s = time(NULL); + ret = snprintf(buf, sizeof(buf), "/var/log/%s-telemetry-%ld", + nvme_ctrl_get_subsysnqn(c), s); + if (ret < 0) { + free(log); + return; + } + log_size = (le16_to_cpu(log->dalb3) + 1) * NVME_LOG_TELEM_BLOCK_SIZE; + + fd = open(buf, O_CREAT|O_WRONLY, S_IRUSR|S_IRGRP); + if (fd < 0) { + free(log); + return; + } + + ret = write(fd, log, log_size); + if (ret < 0) + printf("failed to write telemetry log\n"); + else + printf("telemetry log save as %s, wrote:%d size:%ld\n", buf, + ret, log_size); + close(fd); + free(log); +} + +static void check_telemetry(nvme_ctrl_t c, int ufd) +{ + char buf[0x1000] = { 0 }; + char *p, *ptr; + + if (read(ufd, buf, sizeof(buf)) < 0) + return; + + ptr = buf; + while ((p = strsep(&ptr, "\n")) != NULL) { + __u32 aen, type, info, lid; + + if (sscanf(p, "NVME_AEN=0x%08x", &aen) != 1) + continue; + + type = aen & 0x07; + info = (aen >> 8) & 0xff; + lid = (aen >> 16) & 0xff; + + printf("%s: aen type:%x info:%x lid:%d\n", + nvme_ctrl_get_name(c), type, info, lid); + if (type == NVME_AER_NOTICE && + info == NVME_AER_NOTICE_TELEMETRY) + save_telemetry(c); + } +} + +static void wait_events(fd_set *fds, struct events *e, int nr) +{ + int ret, i; + + for (i = 0; i < nr; i++) + check_telemetry(e[i].c, e[i].uevent_fd); + + while (1) { + ret = select(nr, fds, NULL, NULL, NULL); + if (ret < 0) + return; + + for (i = 0; i < nr; i++) { + if (!FD_ISSET(e[i].uevent_fd, fds)) + continue; + check_telemetry(e[i].c, e[i].uevent_fd); + } + } +} + +int main() +{ + struct events *e; + fd_set fds; + int i = 0; + + nvme_subsystem_t s; + nvme_ctrl_t c; + nvme_host_t h; + nvme_root_t r; + + r = nvme_scan(NULL); + if (!r) + return EXIT_FAILURE; + + nvme_for_each_host(r, h) + nvme_for_each_subsystem(h, s) + nvme_subsystem_for_each_ctrl(s, c) + i++; + + e = calloc(i, sizeof(e)); + FD_ZERO(&fds); + i = 0; + + nvme_for_each_host(r, h) { + nvme_for_each_subsystem(h, s) { + nvme_subsystem_for_each_ctrl(s, c) { + int fd = open_uevent(c); + + if (fd < 0) + continue; + FD_SET(fd, &fds); + e[i].uevent_fd = fd; + e[i].c = c; + i++; + } + } + } + + wait_events(&fds, e, i); + nvme_free_tree(r); + free(e); + + return EXIT_SUCCESS; +} |