xref: /spdk/test/nvme/reserve/reserve.c (revision cc6920a4763d4b9a43aa40583c8397d8f14fa100)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 
36 #include "spdk/endian.h"
37 #include "spdk/nvme.h"
38 #include "spdk/env.h"
39 #include "spdk/log.h"
40 
41 #define MAX_DEVS 64
42 
43 struct dev {
44 	struct spdk_pci_addr			pci_addr;
45 	struct spdk_nvme_ctrlr			*ctrlr;
46 	char					name[100];
47 };
48 
49 static struct dev g_devs[MAX_DEVS];
50 static int g_num_devs = 0;
51 
52 #define foreach_dev(iter) \
53 	for (iter = g_devs; iter - g_devs < g_num_devs; iter++)
54 
55 static int g_outstanding_commands;
56 static int g_reserve_command_result;
57 static bool g_feat_host_id_successful;
58 
59 #define HOST_ID		0xABABABABCDCDCDCD
60 #define EXT_HOST_ID	((uint8_t[]){0x0f, 0x97, 0xcd, 0x74, 0x8c, 0x80, 0x41, 0x42, \
61 				     0x99, 0x0f, 0x65, 0xc4, 0xf0, 0x39, 0x24, 0x20})
62 
63 #define CR_KEY		0xDEADBEAF5A5A5A5B
64 
65 static void
66 feat_host_id_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
67 {
68 	if (spdk_nvme_cpl_is_error(cpl)) {
69 		fprintf(stdout, "Get/Set Features - Host Identifier failed\n");
70 		g_feat_host_id_successful = false;
71 	} else {
72 		g_feat_host_id_successful = true;
73 	}
74 	g_outstanding_commands--;
75 }
76 
77 static int
78 get_host_identifier(struct spdk_nvme_ctrlr *ctrlr)
79 {
80 	int ret;
81 	uint8_t host_id[16];
82 	uint32_t host_id_size;
83 	uint32_t cdw11;
84 
85 	if (spdk_nvme_ctrlr_get_data(ctrlr)->ctratt.host_id_exhid_supported) {
86 		host_id_size = 16;
87 		cdw11 = 1;
88 		printf("Using 128-bit extended host identifier\n");
89 	} else {
90 		host_id_size = 8;
91 		cdw11 = 0;
92 		printf("Using 64-bit host identifier\n");
93 	}
94 
95 	g_outstanding_commands = 0;
96 	ret = spdk_nvme_ctrlr_cmd_get_feature(ctrlr, SPDK_NVME_FEAT_HOST_IDENTIFIER, cdw11, host_id,
97 					      host_id_size,
98 					      feat_host_id_completion, NULL);
99 	if (ret) {
100 		fprintf(stdout, "Get Feature: Failed\n");
101 		return -1;
102 	}
103 
104 	g_outstanding_commands++;
105 	g_feat_host_id_successful = false;
106 
107 	while (g_outstanding_commands) {
108 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
109 	}
110 
111 	if (g_feat_host_id_successful) {
112 		spdk_log_dump(stdout, "Get Feature: Host Identifier:", host_id, host_id_size);
113 		return 0;
114 	}
115 
116 	return -1;
117 }
118 
119 static int
120 set_host_identifier(struct spdk_nvme_ctrlr *ctrlr)
121 {
122 	int ret;
123 	uint8_t host_id[16] = {};
124 	uint32_t host_id_size;
125 	uint32_t cdw11;
126 
127 	if (spdk_nvme_ctrlr_get_data(ctrlr)->ctratt.host_id_exhid_supported) {
128 		host_id_size = 16;
129 		cdw11 = 1;
130 		printf("Using 128-bit extended host identifier\n");
131 		memcpy(host_id, EXT_HOST_ID, host_id_size);
132 	} else {
133 		host_id_size = 8;
134 		cdw11 = 0;
135 		to_be64(host_id, HOST_ID);
136 		printf("Using 64-bit host identifier\n");
137 	}
138 
139 	g_outstanding_commands = 0;
140 	ret = spdk_nvme_ctrlr_cmd_set_feature(ctrlr, SPDK_NVME_FEAT_HOST_IDENTIFIER, cdw11, 0, host_id,
141 					      host_id_size, feat_host_id_completion, NULL);
142 	if (ret) {
143 		fprintf(stdout, "Set Feature: Failed\n");
144 		return -1;
145 	}
146 
147 	g_outstanding_commands++;
148 	g_feat_host_id_successful = false;
149 
150 	while (g_outstanding_commands) {
151 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
152 	}
153 
154 	if (g_feat_host_id_successful) {
155 		spdk_log_dump(stdout, "Set Feature: Host Identifier:", host_id, host_id_size);
156 		return 0;
157 	}
158 
159 	fprintf(stderr, "Set Feature: Host Identifier Failed\n");
160 	return -1;
161 }
162 
163 static void
164 reservation_ns_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
165 {
166 	if (spdk_nvme_cpl_is_error(cpl)) {
167 		g_reserve_command_result = -1;
168 	} else {
169 		g_reserve_command_result = 0;
170 	}
171 
172 	g_outstanding_commands--;
173 }
174 
175 static int
176 reservation_ns_register(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair,
177 			uint32_t ns_id, bool reg)
178 {
179 	int ret;
180 	struct spdk_nvme_reservation_register_data rr_data;
181 	enum spdk_nvme_reservation_register_action action;
182 	struct spdk_nvme_ns *ns;
183 
184 	ns = spdk_nvme_ctrlr_get_ns(ctrlr, ns_id);
185 
186 	if (reg) {
187 		rr_data.crkey = 0;
188 		rr_data.nrkey = CR_KEY;
189 		action = SPDK_NVME_RESERVE_REGISTER_KEY;
190 	} else {
191 		rr_data.crkey = CR_KEY;
192 		rr_data.nrkey = 0;
193 		action = SPDK_NVME_RESERVE_UNREGISTER_KEY;
194 	}
195 
196 	g_outstanding_commands = 0;
197 	g_reserve_command_result = -1;
198 
199 	ret = spdk_nvme_ns_cmd_reservation_register(ns, qpair, &rr_data, true,
200 			action,
201 			SPDK_NVME_RESERVE_PTPL_CLEAR_POWER_ON,
202 			reservation_ns_completion, NULL);
203 	if (ret) {
204 		fprintf(stderr, "Reservation Register Failed\n");
205 		return -1;
206 	}
207 
208 	g_outstanding_commands++;
209 	while (g_outstanding_commands) {
210 		spdk_nvme_qpair_process_completions(qpair, 100);
211 	}
212 
213 	if (g_reserve_command_result) {
214 		fprintf(stderr, "Reservation Register Failed\n");
215 		return -1;
216 	}
217 
218 	return 0;
219 }
220 
221 static int
222 reservation_ns_report(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair, uint32_t ns_id)
223 {
224 	int ret, i;
225 	uint8_t *payload;
226 	struct spdk_nvme_reservation_status_data *status;
227 	struct spdk_nvme_registered_ctrlr_data *cdata;
228 	struct spdk_nvme_ns *ns;
229 
230 	ns = spdk_nvme_ctrlr_get_ns(ctrlr, ns_id);
231 
232 	g_outstanding_commands = 0;
233 	g_reserve_command_result = -1;
234 
235 	payload = spdk_dma_zmalloc(0x1000, 0x1000, NULL);
236 	if (!payload) {
237 		fprintf(stderr, "DMA Buffer Allocation Failed\n");
238 		return -1;
239 	}
240 
241 	ret = spdk_nvme_ns_cmd_reservation_report(ns, qpair, payload, 0x1000,
242 			reservation_ns_completion, NULL);
243 	if (ret) {
244 		fprintf(stderr, "Reservation Report Failed\n");
245 		spdk_dma_free(payload);
246 		return -1;
247 	}
248 
249 	g_outstanding_commands++;
250 	while (g_outstanding_commands) {
251 		spdk_nvme_qpair_process_completions(qpair, 100);
252 	}
253 
254 	if (g_reserve_command_result) {
255 		fprintf(stderr, "Reservation Report Failed\n");
256 		spdk_dma_free(payload);
257 		return -1;
258 	}
259 
260 	status = (struct spdk_nvme_reservation_status_data *)payload;
261 	fprintf(stdout, "Reservation Generation Counter                  %u\n", status->gen);
262 	fprintf(stdout, "Reservation type                                %u\n", status->rtype);
263 	fprintf(stdout, "Reservation Number of Registered Controllers    %u\n", status->regctl);
264 	fprintf(stdout, "Reservation Persist Through Power Loss State    %u\n", status->ptpls);
265 	for (i = 0; i < status->regctl; i++) {
266 		cdata = (struct spdk_nvme_registered_ctrlr_data *)(payload +
267 				sizeof(struct spdk_nvme_reservation_status_data) +
268 				sizeof(struct spdk_nvme_registered_ctrlr_data) * i);
269 		fprintf(stdout, "Controller ID                           %u\n", cdata->cntlid);
270 		fprintf(stdout, "Controller Reservation Status           %u\n", cdata->rcsts.status);
271 		fprintf(stdout, "Controller Host ID                      0x%"PRIx64"\n", cdata->hostid);
272 		fprintf(stdout, "Controller Reservation Key              0x%"PRIx64"\n", cdata->rkey);
273 	}
274 
275 	spdk_dma_free(payload);
276 	return 0;
277 }
278 
279 static int
280 reservation_ns_acquire(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair, uint32_t ns_id)
281 {
282 	int ret;
283 	struct spdk_nvme_reservation_acquire_data cdata;
284 	struct spdk_nvme_ns *ns;
285 
286 	ns = spdk_nvme_ctrlr_get_ns(ctrlr, ns_id);
287 	cdata.crkey = CR_KEY;
288 	cdata.prkey = 0;
289 
290 	g_outstanding_commands = 0;
291 	g_reserve_command_result = -1;
292 
293 	ret = spdk_nvme_ns_cmd_reservation_acquire(ns, qpair, &cdata,
294 			false,
295 			SPDK_NVME_RESERVE_ACQUIRE,
296 			SPDK_NVME_RESERVE_WRITE_EXCLUSIVE,
297 			reservation_ns_completion, NULL);
298 	if (ret) {
299 		fprintf(stderr, "Reservation Acquire Failed\n");
300 		return -1;
301 	}
302 
303 	g_outstanding_commands++;
304 	while (g_outstanding_commands) {
305 		spdk_nvme_qpair_process_completions(qpair, 100);
306 	}
307 
308 	if (g_reserve_command_result) {
309 		fprintf(stderr, "Reservation Acquire Failed\n");
310 		return -1;
311 	}
312 
313 	return 0;
314 }
315 
316 static int
317 reservation_ns_release(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair, uint32_t ns_id)
318 {
319 	int ret;
320 	struct spdk_nvme_reservation_key_data cdata;
321 	struct spdk_nvme_ns *ns;
322 
323 	ns = spdk_nvme_ctrlr_get_ns(ctrlr, ns_id);
324 	cdata.crkey = CR_KEY;
325 
326 	g_outstanding_commands = 0;
327 	g_reserve_command_result = -1;
328 
329 	ret = spdk_nvme_ns_cmd_reservation_release(ns, qpair, &cdata,
330 			false,
331 			SPDK_NVME_RESERVE_RELEASE,
332 			SPDK_NVME_RESERVE_WRITE_EXCLUSIVE,
333 			reservation_ns_completion, NULL);
334 	if (ret) {
335 		fprintf(stderr, "Reservation Release Failed\n");
336 		return -1;
337 	}
338 
339 	g_outstanding_commands++;
340 	while (g_outstanding_commands) {
341 		spdk_nvme_qpair_process_completions(qpair, 100);
342 	}
343 
344 	if (g_reserve_command_result) {
345 		fprintf(stderr, "Reservation Release Failed\n");
346 		return -1;
347 	}
348 
349 	return 0;
350 }
351 
352 static int
353 reserve_controller(struct spdk_nvme_ctrlr *ctrlr, const struct spdk_pci_addr *pci_addr)
354 {
355 	const struct spdk_nvme_ctrlr_data	*cdata;
356 	struct spdk_nvme_qpair			*qpair;
357 	int ret;
358 
359 	cdata = spdk_nvme_ctrlr_get_data(ctrlr);
360 
361 	printf("=====================================================\n");
362 	printf("NVMe Controller at PCI bus %d, device %d, function %d\n",
363 	       pci_addr->bus, pci_addr->dev, pci_addr->func);
364 	printf("=====================================================\n");
365 
366 	printf("Reservations:                %s\n",
367 	       cdata->oncs.reservations ? "Supported" : "Not Supported");
368 
369 	if (!cdata->oncs.reservations) {
370 		return 0;
371 	}
372 
373 	qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, NULL, 0);
374 	if (!qpair) {
375 		fprintf(stderr, "spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
376 		return -EIO;
377 	}
378 
379 	ret = set_host_identifier(ctrlr);
380 	if (ret) {
381 		goto out;
382 	}
383 
384 	ret = get_host_identifier(ctrlr);
385 	if (ret) {
386 		goto out;
387 	}
388 
389 	/* tested 1 namespace */
390 	ret += reservation_ns_register(ctrlr, qpair, 1, 1);
391 	ret += reservation_ns_acquire(ctrlr, qpair, 1);
392 	ret += reservation_ns_release(ctrlr, qpair, 1);
393 	ret += reservation_ns_register(ctrlr, qpair, 1, 0);
394 	ret += reservation_ns_report(ctrlr, qpair, 1);
395 
396 out:
397 	spdk_nvme_ctrlr_free_io_qpair(qpair);
398 	return ret;
399 }
400 
401 static bool
402 probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
403 	 struct spdk_nvme_ctrlr_opts *opts)
404 {
405 	return true;
406 }
407 
408 static void
409 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
410 	  struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
411 {
412 	struct dev *dev;
413 
414 	/* add to dev list */
415 	dev = &g_devs[g_num_devs++];
416 	spdk_pci_addr_parse(&dev->pci_addr, trid->traddr);
417 	dev->ctrlr = ctrlr;
418 }
419 
420 int main(int argc, char **argv)
421 {
422 	struct dev		*iter;
423 	struct spdk_env_opts	opts;
424 	int			ret = 0;
425 	struct spdk_nvme_detach_ctx *detach_ctx = NULL;
426 
427 	spdk_env_opts_init(&opts);
428 	opts.name = "reserve";
429 	opts.core_mask = "0x1";
430 	opts.shm_id = 0;
431 	if (spdk_env_init(&opts) < 0) {
432 		fprintf(stderr, "Unable to initialize SPDK env\n");
433 		return 1;
434 	}
435 
436 	if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL) != 0) {
437 		fprintf(stderr, "spdk_nvme_probe() failed\n");
438 		return 1;
439 	}
440 
441 	foreach_dev(iter) {
442 		ret = reserve_controller(iter->ctrlr, &iter->pci_addr);
443 		if (ret) {
444 			break;
445 		}
446 	}
447 
448 	printf("Reservation test %s\n", ret ? "failed" : "passed");
449 
450 	foreach_dev(iter) {
451 		spdk_nvme_detach_async(iter->ctrlr, &detach_ctx);
452 	}
453 
454 	if (detach_ctx) {
455 		spdk_nvme_detach_poll(detach_ctx);
456 	}
457 
458 	return ret;
459 }
460