1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2025 Oxide Computer Company 14 */ 15 16 /* 17 * This program is used to check everything about a device before we continue 18 * execution. 19 * 20 * 1) We will check that there are no blkdev attached namespaces. That is it is 21 * up to the user of this program to ensure that this is already the case. This 22 * helps make sure that we don't end up in an awkward spot with tests that issue 23 * blkdev detaches and related. 24 * 25 * 2) We will verify that the device supports namespace management and format 26 * commands. All of our destructive tests require this. 27 * 28 * 3) After doing this, we will confirm with the user that this is explicitly 29 * what they asked for! 30 */ 31 32 #include <err.h> 33 #include <string.h> 34 #include <pcidb.h> 35 #include "libnvme_test_common.h" 36 37 static bool 38 check_blkdev_cb(nvme_ctrl_t *ctrl, const nvme_ns_disc_t *disc, void *arg) 39 { 40 if (nvme_ns_disc_level(disc) < NVME_NS_DISC_F_BLKDEV) { 41 return (true); 42 } 43 44 (void) fprintf(stderr, "\nDevice not suitable: Encountered blkdev " 45 "on namespace %u\n", nvme_ns_disc_nsid(disc)); 46 47 (void) fprintf(stderr, "\nThis test will erase all data found on the " 48 "requested device! To\nindicate that you intend to use this device " 49 "for destructive testing,\nyou must manually detach blkdev from " 50 "all namespaces on this device!\n\nALL DATA ON THIS DEVICE WILL BE " 51 "LOST!\n"); 52 exit(EXIT_FAILURE); 53 } 54 55 static void 56 check_feats(nvme_ctrl_info_t *info) 57 { 58 const nvme_version_t *vers = nvme_ctrl_info_version(info); 59 const nvme_identify_ctrl_t *id = nvme_ctrl_info_identify(info); 60 61 (void) printf("NVMe Version: %u.%u\n", vers->v_major, vers->v_minor); 62 63 if (!NVME_VERSION_ATLEAST(vers, 1, 2)) { 64 (void) fprintf(stderr, "\nDevice not suitable: device revision " 65 "must be at least NVMe 1.2\nto support NVMe namespace " 66 "management! Namespace management is required for " 67 "destructive tests!"); 68 exit(EXIT_FAILURE); 69 } 70 71 (void) printf("Format NVM: %s\n", id->id_oacs.oa_format != 0 ? 72 "supported" : "unsupported"); 73 (void) printf("Namespace Management: %s\n", id->id_oacs.oa_nsmgmt ? 74 "supported" : "unsupported"); 75 76 if (id->id_oacs.oa_format == 0 || id->id_oacs.oa_nsmgmt == 0) { 77 (void) fprintf(stderr, "\nDevice not suitable: missing " 78 "required command set support!\nPlease pick another " 79 "device.\n"); 80 exit(EXIT_FAILURE); 81 } 82 83 (void) printf("Namespace Count: %u\n", id->id_nn); 84 if (id->id_nn <= 1) { 85 (void) fprintf(stderr, "\nDevice not suitable: at least " 86 "two namespaces required!\nPlease pick another " 87 "device.\n"); 88 exit(EXIT_FAILURE); 89 } 90 } 91 92 int 93 main(void) 94 { 95 nvme_t *nvme; 96 nvme_ctrl_t *ctrl; 97 nvme_ctrl_info_t *info; 98 pcidb_hdl_t *pcidb; 99 pcidb_vendor_t *vendor; 100 char buf[64]; 101 102 if ((pcidb = pcidb_open(PCIDB_VERSION)) == NULL) { 103 err(EXIT_FAILURE, "failed to initialize PCI DB handle"); 104 } 105 106 libnvme_test_init(&nvme, &ctrl); 107 if (!nvme_ctrl_info_snap(ctrl, &info)) { 108 libnvme_test_ctrl_fatal(ctrl, "failed to get controller " 109 "information snapshot"); 110 } 111 112 (void) printf("Checking NVMe device %s for destructive test " 113 "suitability\n", getenv(NVME_TEST_DEV_ENVVAR)); 114 vendor = pcidb_lookup_vendor(pcidb, nvme_ctrl_info_vendor(info)); 115 if (vendor != NULL) { 116 (void) printf("Vendor: %s (0x%x)\n", pcidb_vendor_name(vendor), 117 nvme_ctrl_info_vendor(info)); 118 } else { 119 (void) printf("Vendor ID: 0x%x\n", nvme_ctrl_info_vendor(info)); 120 } 121 (void) printf("Model: %s\n", nvme_ctrl_info_model(info)); 122 (void) printf("Serial: %s\n", nvme_ctrl_info_serial(info)); 123 (void) printf("Firmware Revision: %s\n", nvme_ctrl_info_fwrev(info)); 124 125 if (!nvme_ctrl_lock(ctrl, NVME_LOCK_L_WRITE, NVME_LOCK_F_DONT_BLOCK)) { 126 libnvme_test_ctrl_fatal(ctrl, "failed to obtain write lock"); 127 } 128 129 if (!nvme_ns_discover(ctrl, NVME_NS_DISC_F_ALL, check_blkdev_cb, 130 NULL)) { 131 libnvme_test_ctrl_fatal(ctrl, "failed to discover namespaces"); 132 } 133 134 check_feats(info); 135 136 (void) printf("\nALL DATA ON THE ABOVE DEVICE WILL BE LOST!!\n"); 137 (void) printf("Continue [yes/No]: "); 138 (void) fflush(stdout); 139 if (fgets(buf, sizeof (buf), stdin) == NULL) { 140 errx(EXIT_FAILURE, "aborting: failed to read from stdin\n"); 141 } 142 143 if (strcmp(buf, "yes\n") != 0) { 144 (void) printf("Aborting on user request\n"); 145 exit(EXIT_FAILURE); 146 } 147 148 nvme_ctrl_info_free(info); 149 nvme_ctrl_fini(ctrl); 150 nvme_fini(nvme); 151 return (EXIT_SUCCESS); 152 } 153