1
0
Fork 0
libnvme/test/test.c
Daniel Baumann a02d194ad0
Merging upstream version 1.10.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-02-16 10:50:54 +01:00

437 lines
12 KiB
C

// 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>
*/
/**
* Basic libnvme test: uses scan filters, single controllers, and many admin
* command APIs for identifications, logs, and features. No verification for
* specific values are performed: the test will only report which commands
* executed were completed successfully or with an error. User inspection of
* the output woould be required to know if everything is working when the
* program exists successfully; an ungraceful exit means a bug exists
* somewhere.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
#include <libnvme.h>
#include <ccan/endian/endian.h>
static bool nvme_match_subsysnqn_filter(nvme_subsystem_t s,
nvme_ctrl_t c, nvme_ns_t ns, void *f_args)
{
char *nqn_match = f_args;
if (s)
return strcmp(nvme_subsystem_get_nqn(s), nqn_match) == 0;
return true;
}
static int test_ctrl(nvme_ctrl_t c)
{
static __u8 buf[0x1000];
enum nvme_get_features_sel sel = NVME_GET_FEATURES_SEL_CURRENT;
int ret, temp, fd = nvme_ctrl_get_fd(c);
struct nvme_error_log_page error[64];
struct nvme_smart_log smart = { 0 };
struct nvme_firmware_slot fw = { 0 };
struct nvme_ns_list ns_list = { 0 };
struct nvme_cmd_effects_log cfx = { 0 };
struct nvme_self_test_log st = { 0 };
struct nvme_telemetry_log *telem = (void *)buf;
struct nvme_endurance_group_log eglog = { 0 };
struct nvme_ana_log *analog = (void *)buf;
struct nvme_resv_notification_log resvnotify = { 0 };
struct nvme_sanitize_log_page sanlog = { 0 };
struct nvme_id_uuid_list uuid = { 0 };
struct nvme_id_ns_granularity_list gran = { 0 };
struct nvme_secondary_ctrl_list sec = { 0 };
struct nvme_primary_ctrl_cap prim = { 0 };
struct nvme_ctrl_list ctrlist = { 0 };
struct nvme_id_ctrl id = { 0 };
__u32 result;
ret = nvme_ctrl_identify(c, &id);
if (ret) {
printf("ERROR: no identify for:%s\n", nvme_ctrl_get_name(c));
return ret;
}
else {
printf("PASSED: Identify controller\n");
}
ret = nvme_get_log_smart(fd, NVME_NSID_ALL, true, &smart);
if (ret) {
printf("ERROR: no smart log for:%s %#x\n", nvme_ctrl_get_name(c), ret);
return ret;
}
else {
printf("PASSED: smart log\n");
}
temp = ((smart.temperature[1] << 8) | smart.temperature[0]) - 273;
printf("Controller:%s\n", nvme_ctrl_get_name(c));
printf("\nIdentify:\n");
printf(" vid:%#04x\n", le16_to_cpu(id.vid));
printf(" ssvid:%#04x\n", le16_to_cpu(id.ssvid));
printf(" oacs:%#x\n", id.oacs);
printf(" lpa:%#x\n", id.lpa);
printf(" sn:%-.20s\n", id.sn);
printf(" model:%-.40s\n", id.mn);
ret = nvme_identify_allocated_ns_list(fd, 0, &ns_list);
if (!ret)
printf(" PASSED: Allocated NS List\n");
else
printf(" ERROR: Allocated NS List:%x\n", ret);
ret = nvme_identify_active_ns_list(fd, 0, &ns_list);
if (!ret)
printf(" PASSED: Active NS List\n");
else
printf(" ERROR: Active NS List:%x\n", ret);
ret = nvme_identify_ctrl_list(fd, 0, &ctrlist);
if (!ret)
printf(" PASSED: Ctrl List\n");
else
printf(" ERROR: CtrlList:%x\n", ret);
ret = nvme_identify_nsid_ctrl_list(fd, 1, 0, &ctrlist);
if (!ret)
printf(" PASSED: NSID Ctrl List\n");
else
printf(" ERROR: NSID CtrlList:%x\n", ret);
ret = nvme_identify_primary_ctrl(fd, 0, &prim);
if (!ret)
printf(" PASSED: Identify Primary\n");
else
printf(" ERROR: Identify Primary:%x\n", ret);
ret = nvme_identify_secondary_ctrl_list(fd, 0, &sec);
if (!ret)
printf(" PASSED: Identify Secondary\n");
else
printf(" ERROR: Identify Secondary:%x\n", ret);
ret = nvme_identify_ns_granularity(fd, &gran);
if (!ret)
printf(" PASSED: Identify NS granularity\n");
else
printf(" ERROR: Identify NS granularity:%x\n", ret);
ret = nvme_identify_uuid(fd, &uuid);
if (!ret)
printf(" PASSED: Identify UUID List\n");
else
printf(" ERROR: Identify UUID List:%x\n", ret);
printf("\nLogs\n");
printf(" SMART: Current temperature:%d percent used:%d%%\n", temp,
smart.percent_used);
ret = nvme_get_log_sanitize(fd, true, &sanlog);
if (!ret)
printf(" Sanitize Log:\n");
else
printf(" ERROR: Sanitize Log:%x\n", ret);
ret = nvme_get_log_reservation(fd, true, &resvnotify);
if (!ret)
printf(" Reservation Log\n");
else
printf(" ERROR: Reservation Log:%x\n", ret);
ret = nvme_get_log_ana_groups(fd, true, sizeof(buf), analog);
if (!ret)
printf(" ANA Groups\n");
else
printf(" ERROR: ANA Groups:%x\n", ret);
ret = nvme_get_log_endurance_group(fd, 0, &eglog);
if (!ret)
printf(" Endurance Group\n");
else
printf(" ERROR: Endurance Group:%x\n", ret);
ret = nvme_get_log_telemetry_ctrl(fd, true, 0, sizeof(buf), telem);
if (!ret)
printf(" Telemetry Controller\n");
else
printf(" ERROR: Telemetry Controller:%x\n", ret);
ret = nvme_get_log_device_self_test(fd, &st);
if (!ret)
printf(" Device Self Test\n");
else
printf(" ERROR: Device Self Test:%x\n", ret);
ret = nvme_get_log_cmd_effects(fd, NVME_CSI_NVM, &cfx);
if (!ret)
printf(" Command Effects\n");
else
printf(" ERROR: Command Effects:%x\n", ret);
ret = nvme_get_log_changed_ns_list(fd, true, &ns_list);
if (!ret)
printf(" Change NS List\n");
else
printf(" ERROR: Change NS List:%x\n", ret);
ret = nvme_get_log_fw_slot(fd, true, &fw);
if (!ret)
printf(" FW Slot\n");
else
printf(" ERROR: FW Slot%x\n", ret);
ret = nvme_get_log_error(fd, 64, true, error);
if (!ret)
printf(" Error Log\n");
else
printf(" ERROR: Error Log:%x\n", ret);
printf("\nFeatures\n");
ret = nvme_get_features_arbitration(fd, sel, &result);
if (!ret)
printf(" Arbitration:%x\n", result);
else if (ret > 0)
printf(" ERROR: Arbitration:%x\n", ret);
ret = nvme_get_features_power_mgmt(fd, sel, &result);
if (!ret)
printf(" Power Management:%x\n", result);
else if (ret > 0)
printf(" ERROR: Power Management:%x\n", ret);
ret = nvme_get_features_temp_thresh(fd, sel, &result);
if (!ret)
printf(" Temperature Threshold:%x\n", result);
else if (ret > 0)
printf(" ERROR: Temperature Threshold:%x\n", ret);
ret = nvme_get_features_err_recovery2(fd, sel, 0, &result);
if (!ret)
printf(" Error Recovery:%x\n", result);
else if (ret > 0)
printf(" ERROR: Error Recovery:%x\n", ret);
ret = nvme_get_features_volatile_wc(fd, sel, &result);
if (!ret)
printf(" Volatile Write Cache:%x\n", result);
else if (ret > 0)
printf(" ERROR: Volatile Write Cache:%x\n", ret);
ret = nvme_get_features_num_queues(fd, sel, &result);
if (!ret)
printf(" Number of Queues:%x\n", result);
else if (ret > 0)
printf(" ERROR: Number of Queues:%x\n", ret);
ret = nvme_get_features_irq_coalesce(fd, sel, &result);
if (!ret)
printf(" IRQ Coalescing:%x\n", result);
else if (ret > 0)
printf(" ERROR: IRQ Coalescing:%x\n", ret);
ret = nvme_get_features_write_atomic(fd, sel, &result);
if (!ret)
printf(" Write Atomic:%x\n", result);
else if (ret > 0)
printf(" ERROR: Write Atomic:%x\n", ret);
ret = nvme_get_features_async_event(fd, sel, &result);
if (!ret)
printf(" Asycn Event Config:%x\n", result);
else if (ret > 0)
printf(" ERROR: Asycn Event Config:%x\n", ret);
ret = nvme_get_features_hctm(fd, sel, &result);
if (!ret)
printf(" HCTM:%x\n", result);
else if (ret > 0)
printf(" ERROR: HCTM:%x\n", ret);
ret = nvme_get_features_nopsc(fd, sel, &result);
if (!ret)
printf(" NOP Power State Config:%x\n", result);
else if (ret > 0)
printf(" ERROR: NOP Power State Configrbitration:%x\n", ret);
ret = nvme_get_features_rrl(fd, sel, &result);
if (!ret)
printf(" Read Recover Levels:%x\n", result);
else if (ret > 0)
printf(" ERROR: Read Recover Levels:%x\n", ret);
ret = nvme_get_features_lba_sts_interval(fd, sel, &result);
if (!ret)
printf(" LBA Status Interval:%x\n", result);
else if (ret > 0)
printf(" ERROR: LBA Status Interval:%x\n", ret);
ret = nvme_get_features_sanitize(fd, sel, &result);
if (!ret)
printf(" Sanitize:%x\n", result);
else if (ret > 0)
printf(" ERROR: SW Progress Marker:%x\n", ret);
ret = nvme_get_features_sw_progress(fd, sel, &result);
if (!ret)
printf(" SW Progress Marker:%x\n", result);
else if (ret > 0)
printf(" ERROR: Sanitize:%x\n", ret);
ret = nvme_get_features_resv_mask2(fd, sel, 0, &result);
if (!ret)
printf(" Reservation Mask:%x\n", result);
else if (ret > 0)
printf(" ERROR: Reservation Mask:%x\n", ret);
ret = nvme_get_features_resv_persist2(fd, sel, 0, &result);
if (!ret)
printf(" Reservation Persistence:%x\n", result);
else if (ret > 0)
printf(" ERROR: Reservation Persistence:%x\n", ret);
return 0;
}
static int test_namespace(nvme_ns_t n)
{
int ret, nsid = nvme_ns_get_nsid(n), fd = nvme_ns_get_fd(n);
struct nvme_id_ns ns = { 0 }, allocated = { 0 };
struct nvme_ns_id_desc *descs;
__u32 result = 0;
__u8 flbas;
ret = nvme_ns_identify(n, &ns);
if (ret)
return ret;
nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &flbas);
printf("%s: nsze:%" PRIu64 " lba size:%d\n",
nvme_ns_get_name(n), le64_to_cpu(ns.nsze),
1 << ns.lbaf[flbas].ds);
ret = nvme_identify_allocated_ns(fd, nsid, &allocated);
if (!ret)
printf(" Identify allocated ns\n");
else
printf(" ERROR: Identify allocated ns:%x\n", ret);
descs = malloc(NVME_IDENTIFY_DATA_SIZE);
if (!descs)
return -1;
ret = nvme_identify_ns_descs(fd, nsid, descs);
if (!ret)
printf(" Identify NS Descriptors\n");
else
printf(" ERROR: Identify NS Descriptors:%x\n", ret);
free(descs);
ret = nvme_get_features_write_protect(fd, nsid,
NVME_GET_FEATURES_SEL_CURRENT, &result);
if (!ret)
printf(" Write Protect:%x\n", result);
else if (ret > 0)
printf(" ERROR: Write Protect:%x\n", ret);
return 0;
}
static void print_hex(const uint8_t *x, int len)
{
int i;
for (i = 0; i < len; i++)
printf("%02x", x[i]);
}
int main(int argc, char **argv)
{
nvme_root_t r;
nvme_host_t h;
nvme_subsystem_t s;
nvme_ctrl_t c;
nvme_path_t p;
nvme_ns_t n;
const char *ctrl = "nvme4";
const char *nqn_match = "testnqn";
printf("Test filter for common loop back target\n");
r = nvme_create_root(NULL, DEFAULT_LOGLEVEL);
if (!r)
return 1;
nvme_scan_topology(r, nvme_match_subsysnqn_filter, (void *)nqn_match);
nvme_for_each_host(r, h) {
nvme_for_each_subsystem(h, s) {
printf("%s - NQN=%s\n", nvme_subsystem_get_name(s),
nvme_subsystem_get_nqn(s));
nvme_subsystem_for_each_ctrl(s, c) {
printf(" %s %s %s %s\n", nvme_ctrl_get_name(c),
nvme_ctrl_get_transport(c),
nvme_ctrl_get_address(c),
nvme_ctrl_get_state(c));
}
}
}
printf("\n");
if (argc > 1)
ctrl = argv[1];
printf("Test scan specific controller\n");
c = nvme_scan_ctrl(r, ctrl);
if (c) {
printf("%s %s %s %s\n", nvme_ctrl_get_name(c),
nvme_ctrl_get_transport(c),
nvme_ctrl_get_address(c),
nvme_ctrl_get_state(c));
nvme_free_ctrl(c);
}
printf("\n");
nvme_free_tree(r);
r = nvme_scan(NULL);
if (!r)
return -1;
printf("Test walking the topology\n");
nvme_for_each_host(r, h) {
nvme_for_each_subsystem(h, s) {
printf("%s - NQN=%s\n", nvme_subsystem_get_name(s),
nvme_subsystem_get_nqn(s));
nvme_subsystem_for_each_ctrl(s, c) {
printf(" `- %s %s %s %s\n",
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(c, n) {
char uuid_str[NVME_UUID_LEN_STRING];
unsigned char uuid[NVME_UUID_LEN];
printf(" `- %s lba size:%d lba max:%" PRIu64 "\n",
nvme_ns_get_name(n),
nvme_ns_get_lba_size(n),
nvme_ns_get_lba_count(n));
printf(" eui:");
print_hex(nvme_ns_get_eui64(n), 8);
printf(" nguid:");
print_hex(nvme_ns_get_nguid(n), 16);
nvme_ns_get_uuid(n, uuid);
nvme_uuid_to_string(uuid, uuid_str);
printf(" uuid:%s csi:%d\n", uuid_str,
nvme_ns_get_csi(n));
}
nvme_ctrl_for_each_path(c, p)
printf(" `- %s %s\n",
nvme_path_get_name(p),
nvme_path_get_ana_state(p));
}
nvme_subsystem_for_each_ns(s, n) {
printf(" `- %s lba size:%d lba max:%" PRIu64 "\n",
nvme_ns_get_name(n),
nvme_ns_get_lba_size(n),
nvme_ns_get_lba_count(n));
}
}
printf("\n");
}
printf("Test identification, logs, and features\n");
nvme_for_each_host(r, h) {
nvme_for_each_subsystem(h, s) {
nvme_subsystem_for_each_ctrl(s, c) {
test_ctrl(c);
printf("\n");
nvme_ctrl_for_each_ns(c, n) {
test_namespace(n);
printf("\n");
}
}
nvme_subsystem_for_each_ns(s, n) {
test_namespace(n);
printf("\n");
}
}
}
nvme_free_tree(r);
return 0;
}