Branch data Line data Source code
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 : 22 : get_nsid(const struct spdk_nvme_transport_id *trid)
34 : : {
35 [ + - # # ]: 22 : if (!strcmp(trid->traddr, g_config.pmr_dev.trid.traddr)) {
36 : 22 : return g_config.pmr_dev.nsid;
37 : : }
38 : 0 : return 0;
39 : : }
40 : :
41 : : static void
42 : 66 : check_io(void *arg, const struct spdk_nvme_cpl *completion)
43 : : {
44 : 66 : g_config.pmr_dev.done = 1;
45 : 66 : }
46 : :
47 : : static int
48 : 22 : pmr_persistence(void)
49 : : {
50 : 22 : int rc = 0;
51 : : void *pmr_buf, *buf;
52 : 0 : size_t sz;
53 : : struct spdk_nvme_qpair *qpair;
54 : :
55 : : /* Allocate Queue Pair for the Controller with PMR */
56 : 22 : qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_config.pmr_dev.ctrlr, NULL, 0);
57 [ - + ]: 22 : if (qpair == NULL) {
58 : 0 : printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
59 : 0 : return -ENOMEM;
60 : : }
61 : :
62 : : /* Enable the PMR */
63 : 22 : rc = spdk_nvme_ctrlr_enable_pmr(g_config.pmr_dev.ctrlr);
64 [ - + ]: 22 : if (rc) {
65 : 0 : printf("ERROR: Enabling PMR failed\n");
66 : 0 : printf("Are you sure %s has a valid PMR?\n",
67 : : g_config.pmr_dev.trid.traddr);
68 : 0 : goto free_qpair;
69 : : }
70 : :
71 : : /* Allocate buffer from PMR */
72 : 22 : pmr_buf = spdk_nvme_ctrlr_map_pmr(g_config.pmr_dev.ctrlr, &sz);
73 [ + - - + ]: 22 : if (pmr_buf == NULL || sz < g_config.copy_size) {
74 : 0 : printf("ERROR: PMR buffer allocation failed\n");
75 : 0 : rc = -ENOMEM;
76 : 0 : goto disable_pmr;
77 : : }
78 : :
79 : : /* Clear the done flag */
80 : 22 : g_config.pmr_dev.done = 0;
81 : :
82 : : /* Do the write to the PMR IO buffer, reading from rlba */
83 : 22 : rc = spdk_nvme_ns_cmd_read(g_config.pmr_dev.ns, qpair, pmr_buf,
84 : 22 : g_config.pmr_dev.rlba, g_config.pmr_dev.nlbas,
85 : : check_io, NULL, 0);
86 [ - + ]: 22 : if (rc != 0) {
87 [ # # ]: 0 : fprintf(stderr, "Read I/O to PMR failed\n");
88 : 0 : rc = -EIO;
89 : 0 : goto unmap_pmr;
90 : : }
91 [ + + ]: 62083 : while (!g_config.pmr_dev.done) {
92 : 62061 : spdk_nvme_qpair_process_completions(qpair, 0);
93 : : }
94 : :
95 : : /* Clear the done flag */
96 : 22 : g_config.pmr_dev.done = 0;
97 : :
98 : 22 : pmr_buf = NULL;
99 : :
100 : : /* Free PMR buffer */
101 : 22 : rc = spdk_nvme_ctrlr_unmap_pmr(g_config.pmr_dev.ctrlr);
102 [ - + ]: 22 : if (rc) {
103 : 0 : printf("ERROR: Unmapping PMR failed\n");
104 : 0 : goto disable_pmr;
105 : : }
106 : :
107 : : /* Disable the PMR */
108 : 22 : rc = spdk_nvme_ctrlr_disable_pmr(g_config.pmr_dev.ctrlr);
109 [ - + ]: 22 : if (rc) {
110 : 0 : printf("ERROR: Disabling PMR failed\n");
111 : 0 : goto free_qpair;
112 : : }
113 : :
114 : : /* Free the queue */
115 : 22 : spdk_nvme_ctrlr_free_io_qpair(qpair);
116 : :
117 : 22 : rc = spdk_nvme_ctrlr_reset(g_config.pmr_dev.ctrlr);
118 [ - + ]: 22 : if (rc) {
119 : 0 : printf("ERROR: Resetting Controller failed\n");
120 : 0 : return rc;
121 : : }
122 : :
123 : : /* Allocate Queue Pair for the Controller with PMR */
124 : 22 : qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_config.pmr_dev.ctrlr, NULL, 0);
125 [ - + ]: 22 : if (qpair == NULL) {
126 : 0 : printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
127 : 0 : return -ENOMEM;
128 : : }
129 : :
130 : : /* Enable the PMR */
131 : 22 : rc = spdk_nvme_ctrlr_enable_pmr(g_config.pmr_dev.ctrlr);
132 [ - + ]: 22 : if (rc) {
133 : 0 : printf("ERROR: Enabling PMR failed\n");
134 : 0 : goto free_qpair;
135 : : }
136 : :
137 : : /* Allocate buffer from PMR */
138 : 22 : pmr_buf = spdk_nvme_ctrlr_map_pmr(g_config.pmr_dev.ctrlr, &sz);
139 [ + - - + ]: 22 : if (pmr_buf == NULL || sz < g_config.copy_size) {
140 : 0 : printf("ERROR: PMR buffer allocation failed\n");
141 : 0 : rc = -ENOMEM;
142 : 0 : goto disable_pmr;
143 : : }
144 : :
145 : : /* Do the read from the PMR IO buffer, write to wlba */
146 : 22 : rc = spdk_nvme_ns_cmd_write(g_config.pmr_dev.ns, qpair, pmr_buf,
147 : 22 : g_config.pmr_dev.wlba, g_config.pmr_dev.nlbas,
148 : : check_io, NULL, 0);
149 [ - + ]: 22 : if (rc != 0) {
150 [ # # ]: 0 : fprintf(stderr, "Read I/O from PMR failed\n");
151 : 0 : rc = -EIO;
152 : 0 : goto unmap_pmr;
153 : : }
154 [ + + ]: 141919 : while (!g_config.pmr_dev.done) {
155 : 141897 : spdk_nvme_qpair_process_completions(qpair, 0);
156 : : }
157 : :
158 : : /* Clear the done flag */
159 : 22 : g_config.pmr_dev.done = 0;
160 : :
161 : 22 : buf = spdk_zmalloc(g_config.copy_size, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA);
162 [ - + ]: 22 : if (buf == NULL) {
163 : 0 : printf("ERROR: Buffer allocation failed\n");
164 : 0 : rc = -ENOMEM;
165 : 0 : goto unmap_pmr;
166 : : }
167 : :
168 : : /* Do the read from wlba to a buffer */
169 : 22 : rc = spdk_nvme_ns_cmd_read(g_config.pmr_dev.ns, qpair, buf,
170 : 22 : g_config.pmr_dev.wlba, g_config.pmr_dev.nlbas,
171 : : check_io, NULL, 0);
172 [ - + ]: 22 : if (rc != 0) {
173 [ # # ]: 0 : fprintf(stderr, "Read I/O from WLBA failed\n");
174 : 0 : rc = -EIO;
175 : 0 : goto free_buf;
176 : : }
177 [ + + ]: 15459 : while (!g_config.pmr_dev.done) {
178 : 15437 : spdk_nvme_qpair_process_completions(qpair, 0);
179 : : }
180 : :
181 : : /* Clear the done flag */
182 : 22 : g_config.pmr_dev.done = 0;
183 : :
184 : : /* Compare the data in the read buffer to the PMR buffer */
185 [ - + # # : 22 : if (memcmp(buf, pmr_buf, g_config.copy_size)) {
# # ]
186 : 0 : printf("PMR Data Not Persistent, after Controller Reset\n");
187 : 0 : rc = -EIO;
188 : : } else {
189 : 22 : printf("PMR Data is Persistent across Controller Reset\n");
190 : : }
191 : :
192 : 22 : free_buf:
193 : 22 : spdk_free(buf);
194 : :
195 : 22 : unmap_pmr:
196 : : /* Free PMR buffer */
197 : 22 : spdk_nvme_ctrlr_unmap_pmr(g_config.pmr_dev.ctrlr);
198 : :
199 : 22 : disable_pmr:
200 : : /* Disable the PMR */
201 : 22 : spdk_nvme_ctrlr_disable_pmr(g_config.pmr_dev.ctrlr);
202 : :
203 : 22 : free_qpair:
204 : : /* Free the queue */
205 : 22 : spdk_nvme_ctrlr_free_io_qpair(qpair);
206 : :
207 : 22 : return rc;
208 : : }
209 : :
210 : : static bool
211 : 44 : 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 [ + + ]: 44 : if (spdk_nvme_transport_id_compare(trid, &g_config.pmr_dev.trid)) {
216 [ # # ]: 22 : printf("%s - not probed %s!\n", __func__, trid->traddr);
217 : 22 : return 0;
218 : : }
219 : :
220 [ # # ]: 22 : printf("%s - probed %s!\n", __func__, trid->traddr);
221 : 22 : return 1;
222 : : }
223 : :
224 : : static void
225 : 22 : 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 : 22 : ns = spdk_nvme_ctrlr_get_ns(ctrlr, get_nsid(trid));
231 [ - + ]: 22 : if (ns == NULL) {
232 [ # # # # ]: 0 : fprintf(stderr, "Could not locate namespace %d on controller %s.\n",
233 : 0 : get_nsid(trid), trid->traddr);
234 : 0 : exit(-1);
235 : : }
236 : :
237 : 22 : g_config.pmr_dev.ctrlr = ctrlr;
238 : 22 : g_config.pmr_dev.ns = ns;
239 : 22 : g_config.pmr_dev.lba_size = spdk_nvme_ns_get_sector_size(ns);
240 : :
241 [ # # ]: 22 : printf("%s - attached %s!\n", __func__, trid->traddr);
242 : 22 : }
243 : :
244 : : static void
245 : 0 : usage(char *program_name)
246 : : {
247 [ # # ]: 0 : printf("%s options (all mandatory)", program_name);
248 : 0 : printf("\n");
249 [ # # ]: 0 : printf("\t[-p PCIe address of the NVMe Device with PMR support]\n");
250 [ # # ]: 0 : printf("\t[-n Namespace ID]\n");
251 [ # # ]: 0 : printf("\t[-r Read LBA]\n");
252 [ # # ]: 0 : printf("\t[-l Number of LBAs to read]\n");
253 [ # # ]: 0 : printf("\t[-w Write LBA]\n");
254 : 0 : printf("\n");
255 : 0 : }
256 : :
257 : : static int
258 : 22 : parse_args(int argc, char **argv)
259 : : {
260 : : int op;
261 : 22 : unsigned num_args = 0;
262 : : long int val;
263 : :
264 [ + + # # : 132 : while ((op = getopt(argc, argv, "p:n:r:l:w:")) != -1) {
# # ]
265 [ + + - ]: 110 : switch (op) {
266 : 22 : case 'p':
267 [ # # ]: 22 : snprintf(&g_config.pmr_dev.trid.traddr[0], SPDK_NVMF_TRADDR_MAX_LEN + 1,
268 : : "%s", optarg);
269 : :
270 : 22 : g_config.pmr_dev.trid.trtype = SPDK_NVME_TRANSPORT_PCIE;
271 : :
272 : 22 : 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 : 22 : num_args++;
276 : 22 : break;
277 : 88 : case 'n':
278 : : case 'r':
279 : : case 'l':
280 : : case 'w':
281 : 88 : val = spdk_strtol(optarg, 10);
282 [ - + ]: 88 : if (val < 0) {
283 [ # # # # ]: 0 : fprintf(stderr, "Converting a string to integer failed\n");
284 : 0 : return val;
285 : : }
286 : : switch (op) {
287 : 22 : case 'n':
288 : 22 : g_config.pmr_dev.nsid = (unsigned)val;
289 : 22 : num_args++;
290 : 22 : break;
291 : 22 : case 'r':
292 : 22 : g_config.pmr_dev.rlba = (unsigned)val;
293 : 22 : num_args++;
294 : 22 : break;
295 : 22 : case 'l':
296 : 22 : g_config.pmr_dev.nlbas = (unsigned)val;
297 : 22 : num_args++;
298 : 22 : break;
299 : 22 : case 'w':
300 : 22 : g_config.pmr_dev.wlba = (unsigned)val;
301 : 22 : num_args++;
302 : 22 : break;
303 : : }
304 : 88 : break;
305 : 0 : default:
306 : 0 : usage(argv[0]);
307 : 0 : return 1;
308 : : }
309 : : }
310 : :
311 [ - + ]: 22 : if (num_args != 5) {
312 : 0 : usage(argv[0]);
313 : 0 : return 1;
314 : : }
315 : :
316 : 22 : return 0;
317 : : }
318 : :
319 : : static void
320 : 22 : cleanup(void)
321 : : {
322 : 22 : struct spdk_nvme_detach_ctx *detach_ctx = NULL;
323 : :
324 : 22 : spdk_nvme_detach_async(g_config.pmr_dev.ctrlr, &detach_ctx);
325 : :
326 [ + - ]: 22 : if (detach_ctx) {
327 : 22 : spdk_nvme_detach_poll(detach_ctx);
328 : : }
329 : 22 : }
330 : :
331 : : int
332 : 22 : main(int argc, char **argv)
333 : : {
334 : 22 : int rc = 0;
335 : 0 : 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 : 22 : rc = parse_args(argc, argv);
345 [ - + ]: 22 : if (rc) {
346 [ # # # # ]: 0 : fprintf(stderr, "Error in parse_args(): %d\n", rc);
347 : 0 : 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 : 22 : spdk_env_opts_init(&opts);
357 : 22 : opts.name = "pmr_persistence";
358 : 22 : opts.shm_id = 0;
359 [ - + ]: 22 : if (spdk_env_init(&opts) < 0) {
360 [ # # # # ]: 0 : fprintf(stderr, "Unable to initialize SPDK env\n");
361 : 0 : 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 : 22 : rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL);
371 [ - + ]: 22 : if (rc) {
372 [ # # # # ]: 0 : fprintf(stderr, "Error in spdk_nvme_probe(): %d\n", rc);
373 : 0 : cleanup();
374 : 0 : return rc;
375 : : }
376 : :
377 : 22 : 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 : 22 : rc = pmr_persistence();
385 [ - + ]: 22 : if (rc) {
386 [ # # # # ]: 0 : fprintf(stderr, "Error in pmr_persistence(): %d\n", rc);
387 : : }
388 : :
389 : 22 : cleanup();
390 : :
391 : 22 : return rc;
392 : : }
|