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