xref: /spdk/test/nvme/reserve/reserve.c (revision cdb0726b95631d46eaf4f2e39ddb6533f150fd27)
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/endian.h"
9 #include "spdk/nvme.h"
10 #include "spdk/env.h"
11 #include "spdk/log.h"
12 
13 #define MAX_DEVS 64
14 
15 struct dev {
16 	struct spdk_pci_addr			pci_addr;
17 	struct spdk_nvme_ctrlr			*ctrlr;
18 	char					name[100];
19 };
20 
21 static struct dev g_devs[MAX_DEVS];
22 static int g_num_devs = 0;
23 
24 #define foreach_dev(iter) \
25 	for (iter = g_devs; iter - g_devs < g_num_devs; iter++)
26 
27 static int g_outstanding_commands;
28 static int g_reserve_command_result;
29 static bool g_feat_host_id_successful;
30 
31 #define HOST_ID		0xABABABABCDCDCDCD
32 #define EXT_HOST_ID	((uint8_t[]){0x0f, 0x97, 0xcd, 0x74, 0x8c, 0x80, 0x41, 0x42, \
33 				     0x99, 0x0f, 0x65, 0xc4, 0xf0, 0x39, 0x24, 0x20})
34 
35 #define CR_KEY		0xDEADBEAF5A5A5A5B
36 
37 static void
38 feat_host_id_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
39 {
40 	if (spdk_nvme_cpl_is_error(cpl)) {
41 		fprintf(stdout, "Get/Set Features - Host Identifier failed\n");
42 		g_feat_host_id_successful = false;
43 	} else {
44 		g_feat_host_id_successful = true;
45 	}
46 	g_outstanding_commands--;
47 }
48 
49 static int
50 get_host_identifier(struct spdk_nvme_ctrlr *ctrlr)
51 {
52 	int ret;
53 	uint8_t host_id[16];
54 	uint32_t host_id_size;
55 	uint32_t cdw11;
56 
57 	if (spdk_nvme_ctrlr_get_data(ctrlr)->ctratt.host_id_exhid_supported) {
58 		host_id_size = 16;
59 		cdw11 = 1;
60 		printf("Using 128-bit extended host identifier\n");
61 	} else {
62 		host_id_size = 8;
63 		cdw11 = 0;
64 		printf("Using 64-bit host identifier\n");
65 	}
66 
67 	g_outstanding_commands = 0;
68 	ret = spdk_nvme_ctrlr_cmd_get_feature(ctrlr, SPDK_NVME_FEAT_HOST_IDENTIFIER, cdw11, host_id,
69 					      host_id_size,
70 					      feat_host_id_completion, NULL);
71 	if (ret) {
72 		fprintf(stdout, "Get Feature: Failed\n");
73 		return -1;
74 	}
75 
76 	g_outstanding_commands++;
77 	g_feat_host_id_successful = false;
78 
79 	while (g_outstanding_commands) {
80 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
81 	}
82 
83 	if (g_feat_host_id_successful) {
84 		spdk_log_dump(stdout, "Get Feature: Host Identifier:", host_id, host_id_size);
85 		return 0;
86 	}
87 
88 	return -1;
89 }
90 
91 static int
92 set_host_identifier(struct spdk_nvme_ctrlr *ctrlr)
93 {
94 	int ret;
95 	uint8_t host_id[16] = {};
96 	uint32_t host_id_size;
97 	uint32_t cdw11;
98 
99 	if (spdk_nvme_ctrlr_get_data(ctrlr)->ctratt.host_id_exhid_supported) {
100 		host_id_size = 16;
101 		cdw11 = 1;
102 		printf("Using 128-bit extended host identifier\n");
103 		memcpy(host_id, EXT_HOST_ID, host_id_size);
104 	} else {
105 		host_id_size = 8;
106 		cdw11 = 0;
107 		to_be64(host_id, HOST_ID);
108 		printf("Using 64-bit host identifier\n");
109 	}
110 
111 	g_outstanding_commands = 0;
112 	ret = spdk_nvme_ctrlr_cmd_set_feature(ctrlr, SPDK_NVME_FEAT_HOST_IDENTIFIER, cdw11, 0, host_id,
113 					      host_id_size, feat_host_id_completion, NULL);
114 	if (ret) {
115 		fprintf(stdout, "Set Feature: Failed\n");
116 		return -1;
117 	}
118 
119 	g_outstanding_commands++;
120 	g_feat_host_id_successful = false;
121 
122 	while (g_outstanding_commands) {
123 		spdk_nvme_ctrlr_process_admin_completions(ctrlr);
124 	}
125 
126 	if (g_feat_host_id_successful) {
127 		spdk_log_dump(stdout, "Set Feature: Host Identifier:", host_id, host_id_size);
128 		return 0;
129 	}
130 
131 	fprintf(stderr, "Set Feature: Host Identifier Failed\n");
132 	return -1;
133 }
134 
135 static void
136 reservation_ns_completion(void *cb_arg, const struct spdk_nvme_cpl *cpl)
137 {
138 	if (spdk_nvme_cpl_is_error(cpl)) {
139 		g_reserve_command_result = -1;
140 	} else {
141 		g_reserve_command_result = 0;
142 	}
143 
144 	g_outstanding_commands--;
145 }
146 
147 static int
148 reservation_ns_register(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair,
149 			uint32_t ns_id, bool reg)
150 {
151 	int ret;
152 	struct spdk_nvme_reservation_register_data rr_data;
153 	enum spdk_nvme_reservation_register_action action;
154 	struct spdk_nvme_ns *ns;
155 
156 	ns = spdk_nvme_ctrlr_get_ns(ctrlr, ns_id);
157 
158 	if (reg) {
159 		rr_data.crkey = 0;
160 		rr_data.nrkey = CR_KEY;
161 		action = SPDK_NVME_RESERVE_REGISTER_KEY;
162 	} else {
163 		rr_data.crkey = CR_KEY;
164 		rr_data.nrkey = 0;
165 		action = SPDK_NVME_RESERVE_UNREGISTER_KEY;
166 	}
167 
168 	g_outstanding_commands = 0;
169 	g_reserve_command_result = -1;
170 
171 	ret = spdk_nvme_ns_cmd_reservation_register(ns, qpair, &rr_data, true,
172 			action,
173 			SPDK_NVME_RESERVE_PTPL_CLEAR_POWER_ON,
174 			reservation_ns_completion, NULL);
175 	if (ret) {
176 		fprintf(stderr, "Reservation Register Failed\n");
177 		return -1;
178 	}
179 
180 	g_outstanding_commands++;
181 	while (g_outstanding_commands) {
182 		spdk_nvme_qpair_process_completions(qpair, 100);
183 	}
184 
185 	if (g_reserve_command_result) {
186 		fprintf(stderr, "Reservation Register Failed\n");
187 		return -1;
188 	}
189 
190 	return 0;
191 }
192 
193 static int
194 reservation_ns_report(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair, uint32_t ns_id)
195 {
196 	int ret, i;
197 	uint8_t *payload;
198 	struct spdk_nvme_reservation_status_data *status;
199 	struct spdk_nvme_registered_ctrlr_data *cdata;
200 	struct spdk_nvme_ns *ns;
201 
202 	ns = spdk_nvme_ctrlr_get_ns(ctrlr, ns_id);
203 
204 	g_outstanding_commands = 0;
205 	g_reserve_command_result = -1;
206 
207 	payload = spdk_dma_zmalloc(0x1000, 0x1000, NULL);
208 	if (!payload) {
209 		fprintf(stderr, "DMA Buffer Allocation Failed\n");
210 		return -1;
211 	}
212 
213 	ret = spdk_nvme_ns_cmd_reservation_report(ns, qpair, payload, 0x1000,
214 			reservation_ns_completion, NULL);
215 	if (ret) {
216 		fprintf(stderr, "Reservation Report Failed\n");
217 		spdk_dma_free(payload);
218 		return -1;
219 	}
220 
221 	g_outstanding_commands++;
222 	while (g_outstanding_commands) {
223 		spdk_nvme_qpair_process_completions(qpair, 100);
224 	}
225 
226 	if (g_reserve_command_result) {
227 		fprintf(stderr, "Reservation Report Failed\n");
228 		spdk_dma_free(payload);
229 		return -1;
230 	}
231 
232 	status = (struct spdk_nvme_reservation_status_data *)payload;
233 	fprintf(stdout, "Reservation Generation Counter                  %u\n", status->gen);
234 	fprintf(stdout, "Reservation type                                %u\n", status->rtype);
235 	fprintf(stdout, "Reservation Number of Registered Controllers    %u\n", status->regctl);
236 	fprintf(stdout, "Reservation Persist Through Power Loss State    %u\n", status->ptpls);
237 	for (i = 0; i < status->regctl; i++) {
238 		cdata = (struct spdk_nvme_registered_ctrlr_data *)(payload +
239 				sizeof(struct spdk_nvme_reservation_status_data) +
240 				sizeof(struct spdk_nvme_registered_ctrlr_data) * i);
241 		fprintf(stdout, "Controller ID                           %u\n", cdata->cntlid);
242 		fprintf(stdout, "Controller Reservation Status           %u\n", cdata->rcsts.status);
243 		fprintf(stdout, "Controller Host ID                      0x%"PRIx64"\n", cdata->hostid);
244 		fprintf(stdout, "Controller Reservation Key              0x%"PRIx64"\n", cdata->rkey);
245 	}
246 
247 	spdk_dma_free(payload);
248 	return 0;
249 }
250 
251 static int
252 reservation_ns_acquire(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair, uint32_t ns_id)
253 {
254 	int ret;
255 	struct spdk_nvme_reservation_acquire_data cdata;
256 	struct spdk_nvme_ns *ns;
257 
258 	ns = spdk_nvme_ctrlr_get_ns(ctrlr, ns_id);
259 	cdata.crkey = CR_KEY;
260 	cdata.prkey = 0;
261 
262 	g_outstanding_commands = 0;
263 	g_reserve_command_result = -1;
264 
265 	ret = spdk_nvme_ns_cmd_reservation_acquire(ns, qpair, &cdata,
266 			false,
267 			SPDK_NVME_RESERVE_ACQUIRE,
268 			SPDK_NVME_RESERVE_WRITE_EXCLUSIVE,
269 			reservation_ns_completion, NULL);
270 	if (ret) {
271 		fprintf(stderr, "Reservation Acquire Failed\n");
272 		return -1;
273 	}
274 
275 	g_outstanding_commands++;
276 	while (g_outstanding_commands) {
277 		spdk_nvme_qpair_process_completions(qpair, 100);
278 	}
279 
280 	if (g_reserve_command_result) {
281 		fprintf(stderr, "Reservation Acquire Failed\n");
282 		return -1;
283 	}
284 
285 	return 0;
286 }
287 
288 static int
289 reservation_ns_release(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair, uint32_t ns_id)
290 {
291 	int ret;
292 	struct spdk_nvme_reservation_key_data cdata;
293 	struct spdk_nvme_ns *ns;
294 
295 	ns = spdk_nvme_ctrlr_get_ns(ctrlr, ns_id);
296 	cdata.crkey = CR_KEY;
297 
298 	g_outstanding_commands = 0;
299 	g_reserve_command_result = -1;
300 
301 	ret = spdk_nvme_ns_cmd_reservation_release(ns, qpair, &cdata,
302 			false,
303 			SPDK_NVME_RESERVE_RELEASE,
304 			SPDK_NVME_RESERVE_WRITE_EXCLUSIVE,
305 			reservation_ns_completion, NULL);
306 	if (ret) {
307 		fprintf(stderr, "Reservation Release Failed\n");
308 		return -1;
309 	}
310 
311 	g_outstanding_commands++;
312 	while (g_outstanding_commands) {
313 		spdk_nvme_qpair_process_completions(qpair, 100);
314 	}
315 
316 	if (g_reserve_command_result) {
317 		fprintf(stderr, "Reservation Release Failed\n");
318 		return -1;
319 	}
320 
321 	return 0;
322 }
323 
324 static int
325 reserve_controller(struct spdk_nvme_ctrlr *ctrlr, const struct spdk_pci_addr *pci_addr)
326 {
327 	const struct spdk_nvme_ctrlr_data	*cdata;
328 	struct spdk_nvme_qpair			*qpair;
329 	int ret;
330 
331 	cdata = spdk_nvme_ctrlr_get_data(ctrlr);
332 
333 	printf("=====================================================\n");
334 	printf("NVMe Controller at PCI bus %d, device %d, function %d\n",
335 	       pci_addr->bus, pci_addr->dev, pci_addr->func);
336 	printf("=====================================================\n");
337 
338 	printf("Reservations:                %s\n",
339 	       cdata->oncs.reservations ? "Supported" : "Not Supported");
340 
341 	if (!cdata->oncs.reservations) {
342 		return 0;
343 	}
344 
345 	qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, NULL, 0);
346 	if (!qpair) {
347 		fprintf(stderr, "spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
348 		return -EIO;
349 	}
350 
351 	ret = set_host_identifier(ctrlr);
352 	if (ret) {
353 		goto out;
354 	}
355 
356 	ret = get_host_identifier(ctrlr);
357 	if (ret) {
358 		goto out;
359 	}
360 
361 	/* tested 1 namespace */
362 	ret += reservation_ns_register(ctrlr, qpair, 1, 1);
363 	ret += reservation_ns_acquire(ctrlr, qpair, 1);
364 	ret += reservation_ns_release(ctrlr, qpair, 1);
365 	ret += reservation_ns_register(ctrlr, qpair, 1, 0);
366 	ret += reservation_ns_report(ctrlr, qpair, 1);
367 
368 out:
369 	spdk_nvme_ctrlr_free_io_qpair(qpair);
370 	return ret;
371 }
372 
373 static bool
374 probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
375 	 struct spdk_nvme_ctrlr_opts *opts)
376 {
377 	return true;
378 }
379 
380 static void
381 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
382 	  struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
383 {
384 	struct dev *dev;
385 
386 	/* add to dev list */
387 	dev = &g_devs[g_num_devs++];
388 	spdk_pci_addr_parse(&dev->pci_addr, trid->traddr);
389 	dev->ctrlr = ctrlr;
390 }
391 
392 int
393 main(int argc, char **argv)
394 {
395 	struct dev		*iter;
396 	struct spdk_env_opts	opts;
397 	int			ret = 0;
398 	struct spdk_nvme_detach_ctx *detach_ctx = NULL;
399 
400 	spdk_env_opts_init(&opts);
401 	opts.name = "reserve";
402 	opts.core_mask = "0x1";
403 	opts.shm_id = 0;
404 	if (spdk_env_init(&opts) < 0) {
405 		fprintf(stderr, "Unable to initialize SPDK env\n");
406 		return 1;
407 	}
408 
409 	if (spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL) != 0) {
410 		fprintf(stderr, "spdk_nvme_probe() failed\n");
411 		return 1;
412 	}
413 
414 	foreach_dev(iter) {
415 		ret = reserve_controller(iter->ctrlr, &iter->pci_addr);
416 		if (ret) {
417 			break;
418 		}
419 	}
420 
421 	printf("Reservation test %s\n", ret ? "failed" : "passed");
422 
423 	foreach_dev(iter) {
424 		spdk_nvme_detach_async(iter->ctrlr, &detach_ctx);
425 	}
426 
427 	if (detach_ctx) {
428 		spdk_nvme_detach_poll(detach_ctx);
429 	}
430 
431 	return ret;
432 }
433