xref: /illumos-gate/usr/src/test/nvme-tests/tests/libnvme/check-destruct.c (revision f5f0964ce91892f7482efc86903b0ec7c7b6ba66)
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
check_blkdev_cb(nvme_ctrl_t * ctrl,const nvme_ns_disc_t * disc,void * arg)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
check_feats(nvme_ctrl_info_t * info)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
main(void)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