xref: /spdk/examples/nvme/hotplug/hotplug.c (revision 57fd99b91e71a4baa5543e19ff83958dc99d4dac)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2016 Intel Corporation.
303823694SCunyin Chang  *   All rights reserved.
403823694SCunyin Chang  */
503823694SCunyin Chang 
6b961d9ccSBen Walker #include "spdk/stdinc.h"
703823694SCunyin Chang 
803823694SCunyin Chang #include "spdk/nvme.h"
9104356c9SDaniel Verkamp #include "spdk/queue.h"
100f9dc2afSShuhei Matsumoto #include "spdk/string.h"
11885bc995SJinYu #include "spdk/util.h"
12bace0549SKrzysztof Karas #include "spdk/log.h"
13ce29e013SKrzysztof Karas #include "spdk/rpc.h"
14ce29e013SKrzysztof Karas 
15ce29e013SKrzysztof Karas static const char *g_rpc_addr = "/var/tmp/spdk.sock";
1603823694SCunyin Chang 
17104356c9SDaniel Verkamp struct dev_ctx {
18104356c9SDaniel Verkamp 	TAILQ_ENTRY(dev_ctx)	tailq;
19104356c9SDaniel Verkamp 	bool			is_new;
2003823694SCunyin Chang 	bool			is_removed;
21104356c9SDaniel Verkamp 	bool			is_draining;
2203823694SCunyin Chang 	struct spdk_nvme_ctrlr	*ctrlr;
2303823694SCunyin Chang 	struct spdk_nvme_ns	*ns;
24104356c9SDaniel Verkamp 	struct spdk_nvme_qpair	*qpair;
2503823694SCunyin Chang 	uint32_t		io_size_blocks;
2603823694SCunyin Chang 	uint64_t		size_in_ios;
2703823694SCunyin Chang 	uint64_t		io_completed;
28104356c9SDaniel Verkamp 	uint64_t		prev_io_completed;
2903823694SCunyin Chang 	uint64_t		current_queue_depth;
3003823694SCunyin Chang 	uint64_t		offset_in_ios;
31104356c9SDaniel Verkamp 	char			name[1024];
3203823694SCunyin Chang };
3303823694SCunyin Chang 
3403823694SCunyin Chang struct perf_task {
35104356c9SDaniel Verkamp 	struct dev_ctx		*dev;
3603823694SCunyin Chang 	void			*buf;
3703823694SCunyin Chang };
3803823694SCunyin Chang 
39104356c9SDaniel Verkamp static TAILQ_HEAD(, dev_ctx) g_devs = TAILQ_HEAD_INITIALIZER(g_devs);
4003823694SCunyin Chang 
4103823694SCunyin Chang static uint64_t g_tsc_rate;
4203823694SCunyin Chang 
43104356c9SDaniel Verkamp static uint32_t g_io_size_bytes = 4096;
44104356c9SDaniel Verkamp static int g_queue_depth = 4;
4503823694SCunyin Chang static int g_time_in_sec;
462bd8c87aSCunyin Chang static int g_expected_insert_times = -1;
472bd8c87aSCunyin Chang static int g_expected_removal_times = -1;
482bd8c87aSCunyin Chang static int g_insert_times;
492bd8c87aSCunyin Chang static int g_removal_times;
5018d26e42SBen Walker static int g_shm_id = -1;
51ac1abb5bSKrzysztof Karas static const char *g_iova_mode = NULL;
52885bc995SJinYu static uint64_t g_timeout_in_us = SPDK_SEC_TO_USEC;
539a99ff90SShuhei Matsumoto static struct spdk_nvme_detach_ctx *g_detach_ctx;
5403823694SCunyin Chang 
55ce29e013SKrzysztof Karas static bool g_wait_for_rpc = false;
56ce29e013SKrzysztof Karas static bool g_rpc_received = false;
57ce29e013SKrzysztof Karas 
588dd1cd21SBen Walker static void task_complete(struct perf_task *task);
5903823694SCunyin Chang 
608dd1cd21SBen Walker static void timeout_cb(void *cb_arg, struct spdk_nvme_ctrlr *ctrlr,
614cfd17a7SChangpeng Liu 		       struct spdk_nvme_qpair *qpair, uint16_t cid);
62885bc995SJinYu 
63885bc995SJinYu static void
64104356c9SDaniel Verkamp register_dev(struct spdk_nvme_ctrlr *ctrlr)
6503823694SCunyin Chang {
66104356c9SDaniel Verkamp 	struct dev_ctx *dev;
6703823694SCunyin Chang 	const struct spdk_nvme_ctrlr_data *cdata = spdk_nvme_ctrlr_get_data(ctrlr);
6803823694SCunyin Chang 
69104356c9SDaniel Verkamp 	dev = calloc(1, sizeof(*dev));
70104356c9SDaniel Verkamp 	if (dev == NULL) {
71104356c9SDaniel Verkamp 		perror("dev_ctx malloc");
7203823694SCunyin Chang 		exit(1);
7303823694SCunyin Chang 	}
7403823694SCunyin Chang 
75104356c9SDaniel Verkamp 	snprintf(dev->name, sizeof(dev->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn);
76104356c9SDaniel Verkamp 
77104356c9SDaniel Verkamp 	dev->ctrlr = ctrlr;
78104356c9SDaniel Verkamp 	dev->is_new = true;
79104356c9SDaniel Verkamp 	dev->is_removed = false;
80104356c9SDaniel Verkamp 	dev->is_draining = false;
81104356c9SDaniel Verkamp 
825288c4dfSMatt Dumm 	spdk_nvme_ctrlr_register_timeout_callback(ctrlr, g_timeout_in_us, g_timeout_in_us, timeout_cb,
835288c4dfSMatt Dumm 			NULL);
84885bc995SJinYu 
85104356c9SDaniel Verkamp 	dev->ns = spdk_nvme_ctrlr_get_ns(ctrlr, 1);
86104356c9SDaniel Verkamp 
87104356c9SDaniel Verkamp 	if (!dev->ns || !spdk_nvme_ns_is_active(dev->ns)) {
88d77a04d1SCunyin Chang 		fprintf(stderr, "Controller %s: No active namespace; skipping\n", dev->name);
89104356c9SDaniel Verkamp 		goto skip;
9003823694SCunyin Chang 	}
9103823694SCunyin Chang 
92104356c9SDaniel Verkamp 	if (spdk_nvme_ns_get_size(dev->ns) < g_io_size_bytes ||
93104356c9SDaniel Verkamp 	    spdk_nvme_ns_get_sector_size(dev->ns) > g_io_size_bytes) {
94d77a04d1SCunyin Chang 		fprintf(stderr, "Controller %s: Invalid "
95104356c9SDaniel Verkamp 			"ns size %" PRIu64 " / block size %u for I/O size %u\n",
96104356c9SDaniel Verkamp 			dev->name,
97104356c9SDaniel Verkamp 			spdk_nvme_ns_get_size(dev->ns),
98104356c9SDaniel Verkamp 			spdk_nvme_ns_get_sector_size(dev->ns),
99104356c9SDaniel Verkamp 			g_io_size_bytes);
100104356c9SDaniel Verkamp 		goto skip;
10103823694SCunyin Chang 	}
10203823694SCunyin Chang 
103104356c9SDaniel Verkamp 	dev->size_in_ios = spdk_nvme_ns_get_size(dev->ns) / g_io_size_bytes;
104104356c9SDaniel Verkamp 	dev->io_size_blocks = g_io_size_bytes / spdk_nvme_ns_get_sector_size(dev->ns);
105104356c9SDaniel Verkamp 
106ce4fcbceSDaniel Verkamp 	dev->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, NULL, 0);
107104356c9SDaniel Verkamp 	if (!dev->qpair) {
108d77a04d1SCunyin Chang 		fprintf(stderr, "ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
109104356c9SDaniel Verkamp 		goto skip;
110104356c9SDaniel Verkamp 	}
1112bd8c87aSCunyin Chang 	g_insert_times++;
112104356c9SDaniel Verkamp 	TAILQ_INSERT_TAIL(&g_devs, dev, tailq);
113104356c9SDaniel Verkamp 	return;
114104356c9SDaniel Verkamp 
115104356c9SDaniel Verkamp skip:
116104356c9SDaniel Verkamp 	free(dev);
117104356c9SDaniel Verkamp }
118104356c9SDaniel Verkamp 
119104356c9SDaniel Verkamp static void
120104356c9SDaniel Verkamp unregister_dev(struct dev_ctx *dev)
121104356c9SDaniel Verkamp {
122d77a04d1SCunyin Chang 	fprintf(stderr, "unregister_dev: %s\n", dev->name);
123104356c9SDaniel Verkamp 
124104356c9SDaniel Verkamp 	spdk_nvme_ctrlr_free_io_qpair(dev->qpair);
1259a99ff90SShuhei Matsumoto 	spdk_nvme_detach_async(dev->ctrlr, &g_detach_ctx);
126104356c9SDaniel Verkamp 
127104356c9SDaniel Verkamp 	TAILQ_REMOVE(&g_devs, dev, tailq);
128104356c9SDaniel Verkamp 	free(dev);
12903823694SCunyin Chang }
13003823694SCunyin Chang 
131455525f5SDaniel Verkamp static struct perf_task *
132455525f5SDaniel Verkamp alloc_task(struct dev_ctx *dev)
13303823694SCunyin Chang {
134455525f5SDaniel Verkamp 	struct perf_task *task;
135455525f5SDaniel Verkamp 
136455525f5SDaniel Verkamp 	task = calloc(1, sizeof(*task));
137455525f5SDaniel Verkamp 	if (task == NULL) {
138455525f5SDaniel Verkamp 		return NULL;
139455525f5SDaniel Verkamp 	}
140455525f5SDaniel Verkamp 
1418a44220bSJohn Meneghini 	task->buf = spdk_dma_zmalloc(g_io_size_bytes, 0x200, NULL);
14203823694SCunyin Chang 	if (task->buf == NULL) {
14369a0f437SDaniel Verkamp 		free(task);
144455525f5SDaniel Verkamp 		return NULL;
14503823694SCunyin Chang 	}
146455525f5SDaniel Verkamp 
147455525f5SDaniel Verkamp 	task->dev = dev;
148455525f5SDaniel Verkamp 
149455525f5SDaniel Verkamp 	return task;
150455525f5SDaniel Verkamp }
151455525f5SDaniel Verkamp 
152455525f5SDaniel Verkamp static void
153455525f5SDaniel Verkamp free_task(struct perf_task *task)
154455525f5SDaniel Verkamp {
155455525f5SDaniel Verkamp 	spdk_dma_free(task->buf);
156455525f5SDaniel Verkamp 	free(task);
15703823694SCunyin Chang }
15803823694SCunyin Chang 
15903823694SCunyin Chang static void io_complete(void *ctx, const struct spdk_nvme_cpl *completion);
16003823694SCunyin Chang 
16103823694SCunyin Chang static void
162455525f5SDaniel Verkamp submit_single_io(struct perf_task *task)
16303823694SCunyin Chang {
164455525f5SDaniel Verkamp 	struct dev_ctx		*dev = task->dev;
16503823694SCunyin Chang 	uint64_t		offset_in_ios;
16603823694SCunyin Chang 	int			rc;
16703823694SCunyin Chang 
168104356c9SDaniel Verkamp 	offset_in_ios = dev->offset_in_ios++;
169104356c9SDaniel Verkamp 	if (dev->offset_in_ios == dev->size_in_ios) {
170104356c9SDaniel Verkamp 		dev->offset_in_ios = 0;
17103823694SCunyin Chang 	}
17203823694SCunyin Chang 
173104356c9SDaniel Verkamp 	rc = spdk_nvme_ns_cmd_read(dev->ns, dev->qpair, task->buf,
174104356c9SDaniel Verkamp 				   offset_in_ios * dev->io_size_blocks,
175104356c9SDaniel Verkamp 				   dev->io_size_blocks, io_complete, task, 0);
17603823694SCunyin Chang 
17703823694SCunyin Chang 	if (rc != 0) {
17803823694SCunyin Chang 		fprintf(stderr, "starting I/O failed\n");
179455525f5SDaniel Verkamp 		free_task(task);
18003823694SCunyin Chang 	} else {
181104356c9SDaniel Verkamp 		dev->current_queue_depth++;
18203823694SCunyin Chang 	}
18303823694SCunyin Chang }
18403823694SCunyin Chang 
18503823694SCunyin Chang static void
18603823694SCunyin Chang task_complete(struct perf_task *task)
18703823694SCunyin Chang {
188104356c9SDaniel Verkamp 	struct dev_ctx *dev;
18903823694SCunyin Chang 
190104356c9SDaniel Verkamp 	dev = task->dev;
191104356c9SDaniel Verkamp 	dev->current_queue_depth--;
192104356c9SDaniel Verkamp 	dev->io_completed++;
19303823694SCunyin Chang 
19403823694SCunyin Chang 	/*
19503823694SCunyin Chang 	 * is_draining indicates when time has expired for the test run
19603823694SCunyin Chang 	 * and we are just waiting for the previously submitted I/O
19703823694SCunyin Chang 	 * to complete.  In this case, do not submit a new I/O to replace
19803823694SCunyin Chang 	 * the one just completed.
19903823694SCunyin Chang 	 */
200104356c9SDaniel Verkamp 	if (!dev->is_draining && !dev->is_removed) {
201455525f5SDaniel Verkamp 		submit_single_io(task);
202455525f5SDaniel Verkamp 	} else {
203455525f5SDaniel Verkamp 		free_task(task);
20403823694SCunyin Chang 	}
20503823694SCunyin Chang }
20603823694SCunyin Chang 
20703823694SCunyin Chang static void
20803823694SCunyin Chang io_complete(void *ctx, const struct spdk_nvme_cpl *completion)
20903823694SCunyin Chang {
21003823694SCunyin Chang 	task_complete((struct perf_task *)ctx);
21103823694SCunyin Chang }
21203823694SCunyin Chang 
21303823694SCunyin Chang static void
214104356c9SDaniel Verkamp check_io(struct dev_ctx *dev)
21503823694SCunyin Chang {
216104356c9SDaniel Verkamp 	spdk_nvme_qpair_process_completions(dev->qpair, 0);
21703823694SCunyin Chang }
21803823694SCunyin Chang 
21903823694SCunyin Chang static void
220104356c9SDaniel Verkamp submit_io(struct dev_ctx *dev, int queue_depth)
22103823694SCunyin Chang {
222455525f5SDaniel Verkamp 	struct perf_task *task;
223455525f5SDaniel Verkamp 
22403823694SCunyin Chang 	while (queue_depth-- > 0) {
225455525f5SDaniel Verkamp 		task = alloc_task(dev);
226455525f5SDaniel Verkamp 		if (task == NULL) {
227455525f5SDaniel Verkamp 			fprintf(stderr, "task allocation failed\n");
228455525f5SDaniel Verkamp 			exit(1);
229455525f5SDaniel Verkamp 		}
230455525f5SDaniel Verkamp 
231455525f5SDaniel Verkamp 		submit_single_io(task);
23203823694SCunyin Chang 	}
23303823694SCunyin Chang }
23403823694SCunyin Chang 
23503823694SCunyin Chang static void
236104356c9SDaniel Verkamp drain_io(struct dev_ctx *dev)
23703823694SCunyin Chang {
238104356c9SDaniel Verkamp 	dev->is_draining = true;
239104356c9SDaniel Verkamp 	while (dev->current_queue_depth > 0) {
240104356c9SDaniel Verkamp 		check_io(dev);
24103823694SCunyin Chang 	}
24203823694SCunyin Chang }
24303823694SCunyin Chang 
24403823694SCunyin Chang static void
24503823694SCunyin Chang print_stats(void)
24603823694SCunyin Chang {
247104356c9SDaniel Verkamp 	struct dev_ctx *dev;
248104356c9SDaniel Verkamp 
249104356c9SDaniel Verkamp 	TAILQ_FOREACH(dev, &g_devs, tailq) {
250d77a04d1SCunyin Chang 		fprintf(stderr, "%-43.43s: %10" PRIu64 " I/Os completed (+%" PRIu64 ")\n",
251104356c9SDaniel Verkamp 			dev->name,
252104356c9SDaniel Verkamp 			dev->io_completed,
253104356c9SDaniel Verkamp 			dev->io_completed - dev->prev_io_completed);
254104356c9SDaniel Verkamp 		dev->prev_io_completed = dev->io_completed;
25503823694SCunyin Chang 	}
25603823694SCunyin Chang 
257d77a04d1SCunyin Chang 	fprintf(stderr, "\n");
25803823694SCunyin Chang }
25903823694SCunyin Chang 
26003823694SCunyin Chang static bool
26132e838afSBen Walker probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
26203823694SCunyin Chang 	 struct spdk_nvme_ctrlr_opts *opts)
26303823694SCunyin Chang {
264d77a04d1SCunyin Chang 	fprintf(stderr, "Attaching to %s\n", trid->traddr);
26503823694SCunyin Chang 
26603823694SCunyin Chang 	return true;
26703823694SCunyin Chang }
26803823694SCunyin Chang 
26903823694SCunyin Chang static void
27032e838afSBen Walker attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
27103823694SCunyin Chang 	  struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
27203823694SCunyin Chang {
273d77a04d1SCunyin Chang 	fprintf(stderr, "Attached to %s\n", trid->traddr);
27403823694SCunyin Chang 
275104356c9SDaniel Verkamp 	register_dev(ctrlr);
27603823694SCunyin Chang }
27703823694SCunyin Chang 
27803823694SCunyin Chang static void
27903823694SCunyin Chang remove_cb(void *cb_ctx, struct spdk_nvme_ctrlr *ctrlr)
28003823694SCunyin Chang {
281104356c9SDaniel Verkamp 	struct dev_ctx *dev;
28203823694SCunyin Chang 
283104356c9SDaniel Verkamp 	TAILQ_FOREACH(dev, &g_devs, tailq) {
284104356c9SDaniel Verkamp 		if (dev->ctrlr == ctrlr) {
285104356c9SDaniel Verkamp 			/*
286104356c9SDaniel Verkamp 			 * Mark the device as removed, but don't detach yet.
287104356c9SDaniel Verkamp 			 *
288104356c9SDaniel Verkamp 			 * The I/O handling code will detach once it sees that
289104356c9SDaniel Verkamp 			 * is_removed is true and all outstanding I/O have been completed.
290104356c9SDaniel Verkamp 			 */
291104356c9SDaniel Verkamp 			dev->is_removed = true;
292d77a04d1SCunyin Chang 			fprintf(stderr, "Controller removed: %s\n", dev->name);
293104356c9SDaniel Verkamp 			return;
294104356c9SDaniel Verkamp 		}
295104356c9SDaniel Verkamp 	}
296104356c9SDaniel Verkamp 
297104356c9SDaniel Verkamp 	/*
298104356c9SDaniel Verkamp 	 * If we get here, this remove_cb is for a controller that we are not tracking
299104356c9SDaniel Verkamp 	 * in g_devs (for example, because we skipped it during register_dev),
300104356c9SDaniel Verkamp 	 * so immediately detach it.
301104356c9SDaniel Verkamp 	 */
3029a99ff90SShuhei Matsumoto 	spdk_nvme_detach_async(ctrlr, &g_detach_ctx);
303104356c9SDaniel Verkamp }
304104356c9SDaniel Verkamp 
3052bd8c87aSCunyin Chang static void
3064cfd17a7SChangpeng Liu timeout_cb(void *cb_arg, struct spdk_nvme_ctrlr *ctrlr,
3074cfd17a7SChangpeng Liu 	   struct spdk_nvme_qpair *qpair, uint16_t cid)
3084cfd17a7SChangpeng Liu {
3094cfd17a7SChangpeng Liu 	/* leave hotplug monitor loop, use the timeout_cb to monitor the hotplug */
3104cfd17a7SChangpeng Liu 	if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, remove_cb) != 0) {
3114cfd17a7SChangpeng Liu 		fprintf(stderr, "spdk_nvme_probe() failed\n");
3124cfd17a7SChangpeng Liu 	}
3134cfd17a7SChangpeng Liu }
3144cfd17a7SChangpeng Liu 
3154cfd17a7SChangpeng Liu static void
316104356c9SDaniel Verkamp io_loop(void)
317104356c9SDaniel Verkamp {
318104356c9SDaniel Verkamp 	struct dev_ctx *dev, *dev_tmp;
319104356c9SDaniel Verkamp 	uint64_t tsc_end;
320104356c9SDaniel Verkamp 	uint64_t next_stats_tsc;
3219a99ff90SShuhei Matsumoto 	int rc;
322104356c9SDaniel Verkamp 
3230de6ed9dSJim Harris 	if (g_time_in_sec > 0) {
324104356c9SDaniel Verkamp 		tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate;
3250de6ed9dSJim Harris 	} else {
3260de6ed9dSJim Harris 		/* User specified 0 seconds for timeout, which means no timeout.
3270de6ed9dSJim Harris 		 * So just set tsc_end to UINT64_MAX which ensures the loop
3280de6ed9dSJim Harris 		 * will never time out.
3290de6ed9dSJim Harris 		 */
3300de6ed9dSJim Harris 		tsc_end = UINT64_MAX;
3310de6ed9dSJim Harris 	}
3320de6ed9dSJim Harris 
333104356c9SDaniel Verkamp 	next_stats_tsc = spdk_get_ticks();
334104356c9SDaniel Verkamp 
335104356c9SDaniel Verkamp 	while (1) {
336104356c9SDaniel Verkamp 		uint64_t now;
337104356c9SDaniel Verkamp 
338104356c9SDaniel Verkamp 		/*
339104356c9SDaniel Verkamp 		 * Check for completed I/O for each controller. A new
340104356c9SDaniel Verkamp 		 * I/O will be submitted in the io_complete callback
341104356c9SDaniel Verkamp 		 * to replace each I/O that is completed.
342104356c9SDaniel Verkamp 		 */
343104356c9SDaniel Verkamp 		TAILQ_FOREACH(dev, &g_devs, tailq) {
344104356c9SDaniel Verkamp 			if (dev->is_new) {
345104356c9SDaniel Verkamp 				/* Submit initial I/O for this controller. */
346104356c9SDaniel Verkamp 				submit_io(dev, g_queue_depth);
347104356c9SDaniel Verkamp 				dev->is_new = false;
348104356c9SDaniel Verkamp 			}
349104356c9SDaniel Verkamp 
350104356c9SDaniel Verkamp 			check_io(dev);
351104356c9SDaniel Verkamp 		}
352104356c9SDaniel Verkamp 
353104356c9SDaniel Verkamp 		/*
354104356c9SDaniel Verkamp 		 * Check for hotplug events.
355104356c9SDaniel Verkamp 		 */
356df46c41aSBen Walker 		if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, remove_cb) != 0) {
357104356c9SDaniel Verkamp 			fprintf(stderr, "spdk_nvme_probe() failed\n");
35803823694SCunyin Chang 			break;
35903823694SCunyin Chang 		}
360104356c9SDaniel Verkamp 
361104356c9SDaniel Verkamp 		/*
362104356c9SDaniel Verkamp 		 * Check for devices which were hot-removed and have finished
363104356c9SDaniel Verkamp 		 * processing outstanding I/Os.
364104356c9SDaniel Verkamp 		 *
365104356c9SDaniel Verkamp 		 * unregister_dev() may remove devs from the list, so use the
366104356c9SDaniel Verkamp 		 * removal-safe iterator.
367104356c9SDaniel Verkamp 		 */
368104356c9SDaniel Verkamp 		TAILQ_FOREACH_SAFE(dev, &g_devs, tailq, dev_tmp) {
369104356c9SDaniel Verkamp 			if (dev->is_removed && dev->current_queue_depth == 0) {
3702bd8c87aSCunyin Chang 				g_removal_times++;
371104356c9SDaniel Verkamp 				unregister_dev(dev);
372104356c9SDaniel Verkamp 			}
37303823694SCunyin Chang 		}
37403823694SCunyin Chang 
3759a99ff90SShuhei Matsumoto 		if (g_detach_ctx) {
3769a99ff90SShuhei Matsumoto 			rc = spdk_nvme_detach_poll_async(g_detach_ctx);
3779a99ff90SShuhei Matsumoto 			if (rc == 0) {
3789a99ff90SShuhei Matsumoto 				g_detach_ctx = NULL;
3799a99ff90SShuhei Matsumoto 			}
3809a99ff90SShuhei Matsumoto 		}
3819a99ff90SShuhei Matsumoto 
38291b8f349SKrzysztof Karas 		if (g_insert_times == g_expected_insert_times && g_removal_times == g_expected_removal_times) {
38391b8f349SKrzysztof Karas 			break;
38491b8f349SKrzysztof Karas 		}
38591b8f349SKrzysztof Karas 
386104356c9SDaniel Verkamp 		now = spdk_get_ticks();
387104356c9SDaniel Verkamp 		if (now > tsc_end) {
38891b8f349SKrzysztof Karas 			SPDK_ERRLOG("Timing out hotplug application!\n");
389104356c9SDaniel Verkamp 			break;
390104356c9SDaniel Verkamp 		}
391104356c9SDaniel Verkamp 		if (now > next_stats_tsc) {
392104356c9SDaniel Verkamp 			print_stats();
393104356c9SDaniel Verkamp 			next_stats_tsc += g_tsc_rate;
394104356c9SDaniel Verkamp 		}
39503823694SCunyin Chang 	}
39603823694SCunyin Chang 
397104356c9SDaniel Verkamp 	TAILQ_FOREACH_SAFE(dev, &g_devs, tailq, dev_tmp) {
398104356c9SDaniel Verkamp 		drain_io(dev);
399104356c9SDaniel Verkamp 		unregister_dev(dev);
40003823694SCunyin Chang 	}
4019a99ff90SShuhei Matsumoto 
4029a99ff90SShuhei Matsumoto 	if (g_detach_ctx) {
4039a99ff90SShuhei Matsumoto 		spdk_nvme_detach_poll(g_detach_ctx);
4049a99ff90SShuhei Matsumoto 	}
40503823694SCunyin Chang }
406104356c9SDaniel Verkamp 
4078dd1cd21SBen Walker static void
4088dd1cd21SBen Walker usage(char *program_name)
409104356c9SDaniel Verkamp {
410104356c9SDaniel Verkamp 	printf("%s options", program_name);
411104356c9SDaniel Verkamp 	printf("\n");
412885bc995SJinYu 	printf("\t[-c timeout for each command in second(default:1s)]\n");
41318d26e42SBen Walker 	printf("\t[-i shm id (optional)]\n");
41418d26e42SBen Walker 	printf("\t[-n expected hot insert times]\n");
4152bd8c87aSCunyin Chang 	printf("\t[-r expected hot removal times]\n");
4160de6ed9dSJim Harris 	printf("\t[-t time in seconds to wait for all events (default: forever)]\n");
417ac1abb5bSKrzysztof Karas 	printf("\t[-m iova mode: pa or va (optional)\n");
418bace0549SKrzysztof Karas 	printf("\t[-l log level]\n");
419bace0549SKrzysztof Karas 	printf("\t Available log levels:\n");
420bace0549SKrzysztof Karas 	printf("\t  disabled, error, warning, notice, info, debug\n");
421ce29e013SKrzysztof Karas 	printf("\t[--wait-for-rpc wait for RPC perform_tests\n");
422ce29e013SKrzysztof Karas 	printf("\t  to proceed with starting IO on NVMe disks]\n");
42303823694SCunyin Chang }
424104356c9SDaniel Verkamp 
425ce29e013SKrzysztof Karas static const struct option g_wait_option[] = {
426ce29e013SKrzysztof Karas #define WAIT_FOR_RPC_OPT_IDX	257
427ce29e013SKrzysztof Karas 	{"wait-for-rpc", no_argument, NULL, WAIT_FOR_RPC_OPT_IDX},
428ce29e013SKrzysztof Karas };
429ce29e013SKrzysztof Karas 
430104356c9SDaniel Verkamp static int
431104356c9SDaniel Verkamp parse_args(int argc, char **argv)
432104356c9SDaniel Verkamp {
433ce29e013SKrzysztof Karas 	int op, opt_idx;
4340f9dc2afSShuhei Matsumoto 	long int val;
435104356c9SDaniel Verkamp 
436104356c9SDaniel Verkamp 	/* default value */
437104356c9SDaniel Verkamp 	g_time_in_sec = 0;
438104356c9SDaniel Verkamp 
439ce29e013SKrzysztof Karas 	while ((op = getopt_long(argc, argv, "c:i:l:m:n:r:t:", g_wait_option, &opt_idx)) != -1) {
4400f9dc2afSShuhei Matsumoto 		if (op == '?') {
4410f9dc2afSShuhei Matsumoto 			usage(argv[0]);
4420f9dc2afSShuhei Matsumoto 			return 1;
4430f9dc2afSShuhei Matsumoto 		}
4440f9dc2afSShuhei Matsumoto 
445eb48f76dSKrzysztof Karas 		switch (op) {
446ce29e013SKrzysztof Karas 		case WAIT_FOR_RPC_OPT_IDX:
447ce29e013SKrzysztof Karas 			g_wait_for_rpc = true;
448ce29e013SKrzysztof Karas 			break;
449eb48f76dSKrzysztof Karas 		case 'c':
450eb48f76dSKrzysztof Karas 		case 'i':
451eb48f76dSKrzysztof Karas 		case 'n':
452eb48f76dSKrzysztof Karas 		case 'r':
453eb48f76dSKrzysztof Karas 		case 't':
4540f9dc2afSShuhei Matsumoto 			val = spdk_strtol(optarg, 10);
4550f9dc2afSShuhei Matsumoto 			if (val < 0) {
4560f9dc2afSShuhei Matsumoto 				fprintf(stderr, "Converting a string to integer failed\n");
4570f9dc2afSShuhei Matsumoto 				return val;
4580f9dc2afSShuhei Matsumoto 			}
459104356c9SDaniel Verkamp 			switch (op) {
460885bc995SJinYu 			case 'c':
461885bc995SJinYu 				g_timeout_in_us = val * SPDK_SEC_TO_USEC;
462885bc995SJinYu 				break;
4632bd8c87aSCunyin Chang 			case 'i':
4640f9dc2afSShuhei Matsumoto 				g_shm_id = val;
46518d26e42SBen Walker 				break;
46618d26e42SBen Walker 			case 'n':
4670f9dc2afSShuhei Matsumoto 				g_expected_insert_times = val;
4682bd8c87aSCunyin Chang 				break;
4692bd8c87aSCunyin Chang 			case 'r':
4700f9dc2afSShuhei Matsumoto 				g_expected_removal_times = val;
4712bd8c87aSCunyin Chang 				break;
472104356c9SDaniel Verkamp 			case 't':
4730f9dc2afSShuhei Matsumoto 				g_time_in_sec = val;
474104356c9SDaniel Verkamp 				break;
475eb48f76dSKrzysztof Karas 			}
476eb48f76dSKrzysztof Karas 			break;
477eb48f76dSKrzysztof Karas 		case 'm':
478eb48f76dSKrzysztof Karas 			g_iova_mode = optarg;
479eb48f76dSKrzysztof Karas 			break;
480bace0549SKrzysztof Karas 		case 'l':
481bace0549SKrzysztof Karas 			if (!strcmp(optarg, "disabled")) {
482bace0549SKrzysztof Karas 				spdk_log_set_print_level(SPDK_LOG_DISABLED);
483bace0549SKrzysztof Karas 			} else if (!strcmp(optarg, "error")) {
484bace0549SKrzysztof Karas 				spdk_log_set_print_level(SPDK_LOG_ERROR);
485bace0549SKrzysztof Karas 			} else if (!strcmp(optarg, "warning")) {
486bace0549SKrzysztof Karas 				spdk_log_set_print_level(SPDK_LOG_WARN);
487bace0549SKrzysztof Karas 			} else if (!strcmp(optarg, "notice")) {
488bace0549SKrzysztof Karas 				spdk_log_set_print_level(SPDK_LOG_NOTICE);
489bace0549SKrzysztof Karas 			} else if (!strcmp(optarg, "info")) {
490bace0549SKrzysztof Karas 				spdk_log_set_print_level(SPDK_LOG_INFO);
491bace0549SKrzysztof Karas 			} else if (!strcmp(optarg, "debug")) {
492bace0549SKrzysztof Karas 				spdk_log_set_print_level(SPDK_LOG_DEBUG);
493bace0549SKrzysztof Karas 			} else {
494bace0549SKrzysztof Karas 				fprintf(stderr, "Unrecognized log level: %s\n", optarg);
495bace0549SKrzysztof Karas 				return 1;
496bace0549SKrzysztof Karas 			}
497bace0549SKrzysztof Karas 			break;
498104356c9SDaniel Verkamp 		default:
499104356c9SDaniel Verkamp 			usage(argv[0]);
500104356c9SDaniel Verkamp 			return 1;
50103823694SCunyin Chang 		}
50203823694SCunyin Chang 	}
50303823694SCunyin Chang 
504104356c9SDaniel Verkamp 	return 0;
505104356c9SDaniel Verkamp }
506104356c9SDaniel Verkamp 
507104356c9SDaniel Verkamp 
50803823694SCunyin Chang static int
50903823694SCunyin Chang register_controllers(void)
51003823694SCunyin Chang {
511d77a04d1SCunyin Chang 	fprintf(stderr, "Initializing NVMe Controllers\n");
51203823694SCunyin Chang 
513df46c41aSBen Walker 	if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, remove_cb) != 0) {
51403823694SCunyin Chang 		fprintf(stderr, "spdk_nvme_probe() failed\n");
51503823694SCunyin Chang 		return 1;
51603823694SCunyin Chang 	}
5172bd8c87aSCunyin Chang 	/* Reset g_insert_times to 0 so that we do not count controllers attached at start as hotplug events. */
5182bd8c87aSCunyin Chang 	g_insert_times = 0;
51903823694SCunyin Chang 	return 0;
52003823694SCunyin Chang }
52103823694SCunyin Chang 
522ce29e013SKrzysztof Karas /* Hotplug RPC */
523ce29e013SKrzysztof Karas static void
524ce29e013SKrzysztof Karas rpc_perform_tests(struct spdk_jsonrpc_request *request,
525ce29e013SKrzysztof Karas 		  const struct spdk_json_val *params)
526ce29e013SKrzysztof Karas {
527ce29e013SKrzysztof Karas 	if (params) {
528ce29e013SKrzysztof Karas 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
529ce29e013SKrzysztof Karas 						 "'perform_tests' requires no arguments");
530ce29e013SKrzysztof Karas 		return;
531ce29e013SKrzysztof Karas 	}
532ce29e013SKrzysztof Karas 
533ce29e013SKrzysztof Karas 	spdk_jsonrpc_send_bool_response(request, true);
534ce29e013SKrzysztof Karas 
535ce29e013SKrzysztof Karas 	g_rpc_received = true;
536ce29e013SKrzysztof Karas }
537ce29e013SKrzysztof Karas SPDK_RPC_REGISTER("perform_tests", rpc_perform_tests, SPDK_RPC_RUNTIME);
538ce29e013SKrzysztof Karas 
539ce29e013SKrzysztof Karas static void
540ce29e013SKrzysztof Karas wait_for_rpc_call(void)
541ce29e013SKrzysztof Karas {
542ce29e013SKrzysztof Karas 	fprintf(stderr,
543ce29e013SKrzysztof Karas 		"Listening for perform_tests to start the application...\n");
544ce29e013SKrzysztof Karas 	spdk_rpc_listen(g_rpc_addr);
545ce29e013SKrzysztof Karas 	spdk_rpc_set_state(SPDK_RPC_RUNTIME);
546ce29e013SKrzysztof Karas 
547ce29e013SKrzysztof Karas 	while (!g_rpc_received) {
548ce29e013SKrzysztof Karas 		spdk_rpc_accept();
549ce29e013SKrzysztof Karas 	}
550ce29e013SKrzysztof Karas 	/* Run spdk_rpc_accept() one more time to trigger
551ce29e013SKrzysztof Karas 	 * spdk_jsonrpv_server_poll() and send the RPC response. */
552ce29e013SKrzysztof Karas 	spdk_rpc_accept();
553ce29e013SKrzysztof Karas }
554ce29e013SKrzysztof Karas 
5558dd1cd21SBen Walker int
5568dd1cd21SBen Walker main(int argc, char **argv)
55703823694SCunyin Chang {
55803823694SCunyin Chang 	int rc;
55918d26e42SBen Walker 	struct spdk_env_opts opts;
56003823694SCunyin Chang 
56103823694SCunyin Chang 	rc = parse_args(argc, argv);
56203823694SCunyin Chang 	if (rc != 0) {
56303823694SCunyin Chang 		return rc;
56403823694SCunyin Chang 	}
56503823694SCunyin Chang 
566*57fd99b9SJim Harris 	opts.opts_size = sizeof(opts);
56718d26e42SBen Walker 	spdk_env_opts_init(&opts);
56818d26e42SBen Walker 	opts.name = "hotplug";
56918d26e42SBen Walker 	opts.core_mask = "0x1";
57018d26e42SBen Walker 	if (g_shm_id > -1) {
57118d26e42SBen Walker 		opts.shm_id = g_shm_id;
57203823694SCunyin Chang 	}
573ac1abb5bSKrzysztof Karas 	if (g_iova_mode) {
574ac1abb5bSKrzysztof Karas 		opts.iova_mode = g_iova_mode;
575ac1abb5bSKrzysztof Karas 	}
576095f4254SLance Hartmann 	if (spdk_env_init(&opts) < 0) {
577095f4254SLance Hartmann 		fprintf(stderr, "Unable to initialize SPDK env\n");
578095f4254SLance Hartmann 		return 1;
579095f4254SLance Hartmann 	}
58003823694SCunyin Chang 
58103823694SCunyin Chang 	g_tsc_rate = spdk_get_ticks_hz();
58203823694SCunyin Chang 
583104356c9SDaniel Verkamp 	/* Detect the controllers that are plugged in at startup. */
58403823694SCunyin Chang 	if (register_controllers() != 0) {
5859ec9c8b3SChangpeng Liu 		rc = 1;
5869ec9c8b3SChangpeng Liu 		goto cleanup;
58703823694SCunyin Chang 	}
58803823694SCunyin Chang 
589ce29e013SKrzysztof Karas 	if (g_wait_for_rpc) {
590ce29e013SKrzysztof Karas 		wait_for_rpc_call();
591ce29e013SKrzysztof Karas 	}
592ce29e013SKrzysztof Karas 
593d77a04d1SCunyin Chang 	fprintf(stderr, "Initialization complete. Starting I/O...\n");
5942bd8c87aSCunyin Chang 	io_loop();
59503823694SCunyin Chang 
596f8baa259SDaniel Verkamp 	if (g_expected_insert_times != -1 && g_insert_times != g_expected_insert_times) {
597f8baa259SDaniel Verkamp 		fprintf(stderr, "Expected inserts %d != actual inserts %d\n",
598f8baa259SDaniel Verkamp 			g_expected_insert_times, g_insert_times);
5999ec9c8b3SChangpeng Liu 		rc = 1;
6009ec9c8b3SChangpeng Liu 		goto cleanup;
601f8baa259SDaniel Verkamp 	}
602f8baa259SDaniel Verkamp 
603f8baa259SDaniel Verkamp 	if (g_expected_removal_times != -1 && g_removal_times != g_expected_removal_times) {
604f8baa259SDaniel Verkamp 		fprintf(stderr, "Expected removals %d != actual removals %d\n",
605f8baa259SDaniel Verkamp 			g_expected_removal_times, g_removal_times);
6069ec9c8b3SChangpeng Liu 		rc = 1;
6072bd8c87aSCunyin Chang 	}
6082bd8c87aSCunyin Chang 
6099ec9c8b3SChangpeng Liu cleanup:
610b0b407aeSKrzysztof Karas 	spdk_rpc_close();
6119ec9c8b3SChangpeng Liu 	spdk_env_fini();
6129ec9c8b3SChangpeng Liu 	return rc;
61303823694SCunyin Chang }
614