xref: /spdk/examples/nvme/pmr_persistence/pmr_persistence.c (revision 307b8c112ffd90a26d53dd15fad67bd9038ef526)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (c) Samsung Electronics Co., Ltd.
3  *   All rights reserved.
4  */
5 
6 #include "spdk/stdinc.h"
7 
8 #include "spdk/env.h"
9 #include "spdk/nvme.h"
10 #include "spdk/string.h"
11 
12 struct nvme_io {
13 	struct spdk_nvme_ctrlr *ctrlr;
14 	struct spdk_nvme_transport_id trid;
15 	struct spdk_nvme_ns *ns;
16 	unsigned nsid;
17 	unsigned rlba;
18 	unsigned nlbas;
19 	unsigned wlba;
20 	uint32_t lba_size;
21 	unsigned done;
22 };
23 
24 struct config {
25 	struct nvme_io pmr_dev;
26 	size_t         copy_size;
27 };
28 
29 static struct config g_config;
30 
31 /* Namespaces index from 1. Return 0 to invoke an error */
32 static unsigned
33 get_nsid(const struct spdk_nvme_transport_id *trid)
34 {
35 	if (!strcmp(trid->traddr, g_config.pmr_dev.trid.traddr)) {
36 		return g_config.pmr_dev.nsid;
37 	}
38 	return 0;
39 }
40 
41 static void
42 check_io(void *arg, const struct spdk_nvme_cpl *completion)
43 {
44 	g_config.pmr_dev.done = 1;
45 }
46 
47 static int
48 pmr_persistence(void)
49 {
50 	int rc = 0;
51 	void *pmr_buf, *buf;
52 	size_t sz;
53 	struct spdk_nvme_qpair	*qpair;
54 
55 	/* Allocate Queue Pair for the Controller with PMR */
56 	qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_config.pmr_dev.ctrlr, NULL, 0);
57 	if (qpair == NULL) {
58 		printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
59 		return -ENOMEM;
60 	}
61 
62 	/* Enable the PMR */
63 	rc = spdk_nvme_ctrlr_enable_pmr(g_config.pmr_dev.ctrlr);
64 	if (rc) {
65 		printf("ERROR: Enabling PMR failed\n");
66 		printf("Are you sure %s has a valid PMR?\n",
67 		       g_config.pmr_dev.trid.traddr);
68 		goto free_qpair;
69 	}
70 
71 	/* Allocate buffer from PMR */
72 	pmr_buf = spdk_nvme_ctrlr_map_pmr(g_config.pmr_dev.ctrlr, &sz);
73 	if (pmr_buf == NULL || sz < g_config.copy_size) {
74 		printf("ERROR: PMR buffer allocation failed\n");
75 		rc = -ENOMEM;
76 		goto disable_pmr;
77 	}
78 
79 	/* Clear the done flag */
80 	g_config.pmr_dev.done = 0;
81 
82 	/* Do the write to the PMR IO buffer, reading from rlba */
83 	rc = spdk_nvme_ns_cmd_read(g_config.pmr_dev.ns, qpair, pmr_buf,
84 				   g_config.pmr_dev.rlba, g_config.pmr_dev.nlbas,
85 				   check_io, NULL, 0);
86 	if (rc != 0) {
87 		fprintf(stderr, "Read I/O to PMR failed\n");
88 		rc = -EIO;
89 		goto unmap_pmr;
90 	}
91 	while (!g_config.pmr_dev.done) {
92 		spdk_nvme_qpair_process_completions(qpair, 0);
93 	}
94 
95 	/* Clear the done flag */
96 	g_config.pmr_dev.done = 0;
97 
98 	pmr_buf = NULL;
99 
100 	/* Free PMR buffer */
101 	rc = spdk_nvme_ctrlr_unmap_pmr(g_config.pmr_dev.ctrlr);
102 	if (rc) {
103 		printf("ERROR: Unmapping PMR failed\n");
104 		goto disable_pmr;
105 	}
106 
107 	/* Disable the PMR */
108 	rc = spdk_nvme_ctrlr_disable_pmr(g_config.pmr_dev.ctrlr);
109 	if (rc) {
110 		printf("ERROR: Disabling PMR failed\n");
111 		goto free_qpair;
112 	}
113 
114 	/* Free the queue */
115 	spdk_nvme_ctrlr_free_io_qpair(qpair);
116 
117 	rc = spdk_nvme_ctrlr_reset(g_config.pmr_dev.ctrlr);
118 	if (rc) {
119 		printf("ERROR: Resetting Controller failed\n");
120 		return rc;
121 	}
122 
123 	/* Allocate Queue Pair for the Controller with PMR */
124 	qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_config.pmr_dev.ctrlr, NULL, 0);
125 	if (qpair == NULL) {
126 		printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
127 		return -ENOMEM;
128 	}
129 
130 	/* Enable the PMR */
131 	rc = spdk_nvme_ctrlr_enable_pmr(g_config.pmr_dev.ctrlr);
132 	if (rc) {
133 		printf("ERROR: Enabling PMR failed\n");
134 		goto free_qpair;
135 	}
136 
137 	/* Allocate buffer from PMR */
138 	pmr_buf = spdk_nvme_ctrlr_map_pmr(g_config.pmr_dev.ctrlr, &sz);
139 	if (pmr_buf == NULL || sz < g_config.copy_size) {
140 		printf("ERROR: PMR buffer allocation failed\n");
141 		rc = -ENOMEM;
142 		goto disable_pmr;
143 	}
144 
145 	/* Do the read from the PMR IO buffer, write to wlba */
146 	rc = spdk_nvme_ns_cmd_write(g_config.pmr_dev.ns, qpair, pmr_buf,
147 				    g_config.pmr_dev.wlba, g_config.pmr_dev.nlbas,
148 				    check_io, NULL, 0);
149 	if (rc != 0) {
150 		fprintf(stderr, "Read I/O from PMR failed\n");
151 		rc = -EIO;
152 		goto unmap_pmr;
153 	}
154 	while (!g_config.pmr_dev.done) {
155 		spdk_nvme_qpair_process_completions(qpair, 0);
156 	}
157 
158 	/* Clear the done flag */
159 	g_config.pmr_dev.done = 0;
160 
161 	buf = spdk_zmalloc(g_config.copy_size, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA);
162 	if (buf == NULL) {
163 		printf("ERROR: Buffer allocation failed\n");
164 		rc = -ENOMEM;
165 		goto unmap_pmr;
166 	}
167 
168 	/* Do the read from wlba to a buffer */
169 	rc = spdk_nvme_ns_cmd_read(g_config.pmr_dev.ns, qpair, buf,
170 				   g_config.pmr_dev.wlba, g_config.pmr_dev.nlbas,
171 				   check_io, NULL, 0);
172 	if (rc != 0) {
173 		fprintf(stderr, "Read I/O from WLBA failed\n");
174 		rc = -EIO;
175 		goto free_buf;
176 	}
177 	while (!g_config.pmr_dev.done) {
178 		spdk_nvme_qpair_process_completions(qpair, 0);
179 	}
180 
181 	/* Clear the done flag */
182 	g_config.pmr_dev.done = 0;
183 
184 	/* Compare the data in the read buffer to the PMR buffer */
185 	if (memcmp(buf, pmr_buf, g_config.copy_size)) {
186 		printf("PMR Data Not Persistent, after Controller Reset\n");
187 		rc = -EIO;
188 	} else {
189 		printf("PMR Data is Persistent across Controller Reset\n");
190 	}
191 
192 free_buf:
193 	spdk_free(buf);
194 
195 unmap_pmr:
196 	/* Free PMR buffer */
197 	spdk_nvme_ctrlr_unmap_pmr(g_config.pmr_dev.ctrlr);
198 
199 disable_pmr:
200 	/* Disable the PMR */
201 	spdk_nvme_ctrlr_disable_pmr(g_config.pmr_dev.ctrlr);
202 
203 free_qpair:
204 	/* Free the queue */
205 	spdk_nvme_ctrlr_free_io_qpair(qpair);
206 
207 	return rc;
208 }
209 
210 static bool
211 probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
212 	 struct spdk_nvme_ctrlr_opts *opts)
213 {
214 	/* We will only attach to the Controller specified by the user */
215 	if (spdk_nvme_transport_id_compare(trid, &g_config.pmr_dev.trid)) {
216 		printf("%s - not probed %s!\n", __func__, trid->traddr);
217 		return 0;
218 	}
219 
220 	printf("%s - probed %s!\n", __func__, trid->traddr);
221 	return 1;
222 }
223 
224 static void
225 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
226 	  struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
227 {
228 	struct spdk_nvme_ns *ns;
229 
230 	ns = spdk_nvme_ctrlr_get_ns(ctrlr, get_nsid(trid));
231 	if (ns == NULL) {
232 		fprintf(stderr, "Could not locate namespace %d on controller %s.\n",
233 			get_nsid(trid), trid->traddr);
234 		exit(-1);
235 	}
236 
237 	g_config.pmr_dev.ctrlr    = ctrlr;
238 	g_config.pmr_dev.ns       = ns;
239 	g_config.pmr_dev.lba_size = spdk_nvme_ns_get_sector_size(ns);
240 
241 	printf("%s - attached %s!\n", __func__, trid->traddr);
242 }
243 
244 static void
245 usage(char *program_name)
246 {
247 	printf("%s options (all mandatory)", program_name);
248 	printf("\n");
249 	printf("\t[-p PCIe address of the NVMe Device with PMR support]\n");
250 	printf("\t[-n Namespace ID]\n");
251 	printf("\t[-r Read LBA]\n");
252 	printf("\t[-l Number of LBAs to read]\n");
253 	printf("\t[-w Write LBA]\n");
254 	printf("\n");
255 }
256 
257 static int
258 parse_args(int argc, char **argv)
259 {
260 	int op;
261 	unsigned num_args = 0;
262 	long int val;
263 
264 	while ((op = getopt(argc, argv, "p:n:r:l:w:")) != -1) {
265 		switch (op) {
266 		case 'p':
267 			snprintf(&g_config.pmr_dev.trid.traddr[0], SPDK_NVMF_TRADDR_MAX_LEN + 1,
268 				 "%s", optarg);
269 
270 			g_config.pmr_dev.trid.trtype = SPDK_NVME_TRANSPORT_PCIE;
271 
272 			spdk_nvme_transport_id_populate_trstring(&g_config.pmr_dev.trid,
273 					spdk_nvme_transport_id_trtype_str(g_config.pmr_dev.trid.trtype));
274 
275 			num_args++;
276 			break;
277 		case 'n':
278 		case 'r':
279 		case 'l':
280 		case 'w':
281 			val = spdk_strtol(optarg, 10);
282 			if (val < 0) {
283 				fprintf(stderr, "Converting a string to integer failed\n");
284 				return val;
285 			}
286 			switch (op) {
287 			case 'n':
288 				g_config.pmr_dev.nsid = (unsigned)val;
289 				num_args++;
290 				break;
291 			case 'r':
292 				g_config.pmr_dev.rlba = (unsigned)val;
293 				num_args++;
294 				break;
295 			case 'l':
296 				g_config.pmr_dev.nlbas = (unsigned)val;
297 				num_args++;
298 				break;
299 			case 'w':
300 				g_config.pmr_dev.wlba = (unsigned)val;
301 				num_args++;
302 				break;
303 			}
304 			break;
305 		default:
306 			usage(argv[0]);
307 			return 1;
308 		}
309 	}
310 
311 	if (num_args != 5) {
312 		usage(argv[0]);
313 		return 1;
314 	}
315 
316 	return 0;
317 }
318 
319 static void
320 cleanup(void)
321 {
322 	struct spdk_nvme_detach_ctx *detach_ctx = NULL;
323 
324 	spdk_nvme_detach_async(g_config.pmr_dev.ctrlr, &detach_ctx);
325 
326 	if (detach_ctx) {
327 		spdk_nvme_detach_poll(detach_ctx);
328 	}
329 }
330 
331 int
332 main(int argc, char **argv)
333 {
334 	int rc = 0;
335 	struct spdk_env_opts opts;
336 
337 	/*
338 	 * Parse the input arguments. For now we use the following
339 	 * format list:
340 	 *
341 	 * -p <pci id> -n <namespace> -r <Read LBA> -l <number of LBAs> -w <Write LBA>
342 	 *
343 	 */
344 	rc = parse_args(argc, argv);
345 	if (rc) {
346 		fprintf(stderr, "Error in parse_args(): %d\n", rc);
347 		return rc;
348 	}
349 
350 	/*
351 	 * SPDK relies on an abstraction around the local environment
352 	 * named env that handles memory allocation and PCI device operations.
353 	 * This library must be initialized first.
354 	 *
355 	 */
356 	spdk_env_opts_init(&opts);
357 	opts.name = "pmr_persistence";
358 	opts.shm_id = 0;
359 	if (spdk_env_init(&opts) < 0) {
360 		fprintf(stderr, "Unable to initialize SPDK env\n");
361 		return 1;
362 	}
363 
364 	/*
365 	 * PMRs only apply to PCIe attached NVMe controllers so we
366 	 * only probe the PCIe bus. This is the default when we pass
367 	 * in NULL for the first argument.
368 	 */
369 
370 	rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL);
371 	if (rc) {
372 		fprintf(stderr, "Error in spdk_nvme_probe(): %d\n", rc);
373 		cleanup();
374 		return rc;
375 	}
376 
377 	g_config.copy_size = g_config.pmr_dev.nlbas * g_config.pmr_dev.lba_size;
378 
379 	/*
380 	 * Call the pmr_persistence() function which performs the data copy
381 	 * to PMR region, resets the Controller and verifies the data persistence
382 	 * or returns an error code if it fails.
383 	 */
384 	rc = pmr_persistence();
385 	if (rc) {
386 		fprintf(stderr, "Error in pmr_persistence(): %d\n", rc);
387 	}
388 
389 	cleanup();
390 
391 	return rc;
392 }
393