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