xref: /spdk/test/nvme/startup/startup.c (revision 8dd1cd2104ea4001e4a0da2a4851ccd62c82f8e8)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (c) Intel Corporation.
3  *   All rights reserved.
4  */
5 
6 #include "spdk/stdinc.h"
7 
8 #include "spdk/nvme.h"
9 #include "spdk/env.h"
10 #include "spdk/string.h"
11 
12 struct ctrlr_entry {
13 	struct spdk_nvme_ctrlr		*ctrlr;
14 	TAILQ_ENTRY(ctrlr_entry)	link;
15 	char				name[1024];
16 };
17 
18 struct ns_entry {
19 	struct spdk_nvme_ctrlr	*ctrlr;
20 	struct spdk_nvme_ns	*ns;
21 	TAILQ_ENTRY(ns_entry)	link;
22 	struct spdk_nvme_qpair	*qpair;
23 };
24 
25 static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers);
26 static TAILQ_HEAD(, ns_entry) g_namespaces = TAILQ_HEAD_INITIALIZER(g_namespaces);
27 static int g_startup_time = 0;
28 
29 static bool
30 probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
31 	 struct spdk_nvme_ctrlr_opts *opts)
32 {
33 	printf("Attaching to %s\n", trid->traddr);
34 
35 	return true;
36 }
37 
38 static void
39 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
40 	  struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
41 {
42 
43 	struct ctrlr_entry *entry;
44 	const struct spdk_nvme_ctrlr_data *cdata;
45 
46 	entry = malloc(sizeof(struct ctrlr_entry));
47 	if (entry == NULL) {
48 		perror("ctrlr_entry malloc");
49 		exit(1);
50 	}
51 
52 	printf("Attached to %s\n", trid->traddr);
53 
54 	/*
55 	 * spdk_nvme_ctrlr is the logical abstraction in SPDK for an NVMe
56 	 *  controller.  During initialization, the IDENTIFY data for the
57 	 *  controller is read using an NVMe admin command, and that data
58 	 *  can be retrieved using spdk_nvme_ctrlr_get_data() to get
59 	 *  detailed information on the controller.  Refer to the NVMe
60 	 *  specification for more details on IDENTIFY for NVMe controllers.
61 	 */
62 	cdata = spdk_nvme_ctrlr_get_data(ctrlr);
63 
64 	snprintf(entry->name, sizeof(entry->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn);
65 
66 	entry->ctrlr = ctrlr;
67 	TAILQ_INSERT_TAIL(&g_controllers, entry, link);
68 }
69 
70 static void
71 cleanup(void)
72 {
73 	struct ns_entry *ns_entry, *tmp_ns_entry;
74 	struct ctrlr_entry *ctrlr_entry, *tmp_ctrlr_entry;
75 	struct spdk_nvme_detach_ctx *detach_ctx = NULL;
76 
77 	TAILQ_FOREACH_SAFE(ns_entry, &g_namespaces, link, tmp_ns_entry) {
78 		TAILQ_REMOVE(&g_namespaces, ns_entry, link);
79 		free(ns_entry);
80 	}
81 
82 	TAILQ_FOREACH_SAFE(ctrlr_entry, &g_controllers, link, tmp_ctrlr_entry) {
83 		TAILQ_REMOVE(&g_controllers, ctrlr_entry, link);
84 		spdk_nvme_detach_async(ctrlr_entry->ctrlr, &detach_ctx);
85 		free(ctrlr_entry);
86 	}
87 
88 	if (detach_ctx) {
89 		spdk_nvme_detach_poll(detach_ctx);
90 	}
91 }
92 
93 static void
94 usage(const char *program_name)
95 {
96 	printf("%s [options]", program_name);
97 	printf("\n");
98 	printf("options:\n");
99 	printf(" -t         The maximum time needed for startup. The unit is us. The value should be bigger than 0.\n");
100 }
101 
102 static int
103 parse_args(int argc, char **argv)
104 {
105 	int op;
106 
107 	while ((op = getopt(argc, argv, "t:")) != -1) {
108 		switch (op) {
109 		case 't':
110 			g_startup_time = spdk_strtol(optarg, 10);
111 			if (g_startup_time < 0) {
112 				fprintf(stderr, "Invalid nvme startup time\n");
113 				return g_startup_time;
114 			}
115 			break;
116 		default:
117 			usage(argv[0]);
118 			return 1;
119 		}
120 	}
121 
122 	return 0;
123 }
124 
125 int
126 main(int argc, char **argv)
127 {
128 	int rc;
129 	struct spdk_env_opts opts;
130 	uint64_t start_tsc, end_tsc, tsc_diff;
131 	float  time_used_in_usec;
132 
133 	rc = parse_args(argc, argv);
134 	if (rc != 0) {
135 		return rc;
136 	}
137 
138 	if (g_startup_time == 0) {
139 		usage(argv[0]);
140 		return 1;
141 	}
142 
143 	start_tsc = spdk_get_ticks();
144 	/*
145 	 * SPDK relies on an abstraction around the local environment
146 	 * named env that handles memory allocation and PCI device operations.
147 	 * This library must be initialized first.
148 	 *
149 	 */
150 	spdk_env_opts_init(&opts);
151 	opts.name = "startup";
152 	opts.shm_id = 0;
153 	if (spdk_env_init(&opts) < 0) {
154 		fprintf(stderr, "Unable to initialize SPDK env\n");
155 		return 1;
156 	}
157 
158 	printf("Initializing NVMe Controllers\n");
159 
160 
161 	/*
162 	 * Start the SPDK NVMe enumeration process.  probe_cb will be called
163 	 *  for each NVMe controller found, giving our application a choice on
164 	 *  whether to attach to each controller.  attach_cb will then be
165 	 *  called for each controller after the SPDK NVMe driver has completed
166 	 *  initializing the controller we chose to attach.
167 	 */
168 	rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL);
169 	if (rc != 0) {
170 		fprintf(stderr, "spdk_nvme_probe() failed\n");
171 		cleanup();
172 		return 1;
173 	}
174 
175 	if (TAILQ_EMPTY(&g_controllers)) {
176 		fprintf(stderr, "no NVMe controllers found\n");
177 		return 0;
178 	}
179 
180 	end_tsc = spdk_get_ticks();
181 	tsc_diff = end_tsc - start_tsc;
182 	time_used_in_usec = ((float)tsc_diff) * 1000 * 1000 / spdk_get_ticks_hz();
183 	printf("Initialization complete.\n");
184 	printf("Time used:%-16.3f(us).\n", time_used_in_usec);
185 	if (time_used_in_usec > g_startup_time) {
186 		fprintf(stderr, "Too long time for initialization.\n");
187 		cleanup();
188 		return 1;
189 	}
190 	cleanup();
191 	return 0;
192 }
193