1488570ebSJim Harris /* SPDX-License-Identifier: BSD-3-Clause 2a6dbe372Spaul luse * Copyright (C) 2016 Intel Corporation. 3213e7a64SJim Harris * All rights reserved. 4213e7a64SJim Harris */ 5213e7a64SJim Harris 6b961d9ccSBen Walker #include "spdk/stdinc.h" 7213e7a64SJim Harris 8213e7a64SJim Harris #include "spdk/nvme.h" 9cda27398SWojciech Malikowski #include "spdk/vmd.h" 10998ec7b0SNiklas Cassel #include "spdk/nvme_zns.h" 110dd80395SBen Walker #include "spdk/env.h" 121f085e8eSMao Jiang #include "spdk/string.h" 131f085e8eSMao Jiang #include "spdk/log.h" 14213e7a64SJim Harris 15219fbc90SKalwas, Jacek #define DATA_BUFFER_STRING "Hello world!" 16219fbc90SKalwas, Jacek 17213e7a64SJim Harris struct ctrlr_entry { 18213e7a64SJim Harris struct spdk_nvme_ctrlr *ctrlr; 194c3fd228SShuhei Matsumoto TAILQ_ENTRY(ctrlr_entry) link; 20213e7a64SJim Harris char name[1024]; 21213e7a64SJim Harris }; 22213e7a64SJim Harris 23213e7a64SJim Harris struct ns_entry { 24213e7a64SJim Harris struct spdk_nvme_ctrlr *ctrlr; 25213e7a64SJim Harris struct spdk_nvme_ns *ns; 264c3fd228SShuhei Matsumoto TAILQ_ENTRY(ns_entry) link; 27213e7a64SJim Harris struct spdk_nvme_qpair *qpair; 28213e7a64SJim Harris }; 29213e7a64SJim Harris 304c3fd228SShuhei Matsumoto static TAILQ_HEAD(, ctrlr_entry) g_controllers = TAILQ_HEAD_INITIALIZER(g_controllers); 314c3fd228SShuhei Matsumoto static TAILQ_HEAD(, ns_entry) g_namespaces = TAILQ_HEAD_INITIALIZER(g_namespaces); 321f085e8eSMao Jiang static struct spdk_nvme_transport_id g_trid = {}; 33213e7a64SJim Harris 34cda27398SWojciech Malikowski static bool g_vmd = false; 35cda27398SWojciech Malikowski 36213e7a64SJim Harris static void 37213e7a64SJim Harris register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns) 38213e7a64SJim Harris { 39213e7a64SJim Harris struct ns_entry *entry; 40213e7a64SJim Harris 41213e7a64SJim Harris if (!spdk_nvme_ns_is_active(ns)) { 42213e7a64SJim Harris return; 43213e7a64SJim Harris } 44213e7a64SJim Harris 45213e7a64SJim Harris entry = malloc(sizeof(struct ns_entry)); 46213e7a64SJim Harris if (entry == NULL) { 47213e7a64SJim Harris perror("ns_entry malloc"); 48213e7a64SJim Harris exit(1); 49213e7a64SJim Harris } 50213e7a64SJim Harris 51213e7a64SJim Harris entry->ctrlr = ctrlr; 52213e7a64SJim Harris entry->ns = ns; 534c3fd228SShuhei Matsumoto TAILQ_INSERT_TAIL(&g_namespaces, entry, link); 54213e7a64SJim Harris 55213e7a64SJim Harris printf(" Namespace ID: %d size: %juGB\n", spdk_nvme_ns_get_id(ns), 56213e7a64SJim Harris spdk_nvme_ns_get_size(ns) / 1000000000); 57213e7a64SJim Harris } 58213e7a64SJim Harris 59213e7a64SJim Harris struct hello_world_sequence { 60213e7a64SJim Harris struct ns_entry *ns_entry; 61213e7a64SJim Harris char *buf; 628f3d0a64SDaniel Verkamp unsigned using_cmb_io; 63213e7a64SJim Harris int is_completed; 64213e7a64SJim Harris }; 65213e7a64SJim Harris 66213e7a64SJim Harris static void 67213e7a64SJim Harris read_complete(void *arg, const struct spdk_nvme_cpl *completion) 68213e7a64SJim Harris { 69213e7a64SJim Harris struct hello_world_sequence *sequence = arg; 70213e7a64SJim Harris 715acf617cSJames Bergsten /* Assume the I/O was successful */ 725acf617cSJames Bergsten sequence->is_completed = 1; 735acf617cSJames Bergsten /* See if an error occurred. If so, display information 745acf617cSJames Bergsten * about it, and set completion value so that I/O 755acf617cSJames Bergsten * caller is aware that an error occurred. 765acf617cSJames Bergsten */ 775acf617cSJames Bergsten if (spdk_nvme_cpl_is_error(completion)) { 785acf617cSJames Bergsten spdk_nvme_qpair_print_completion(sequence->ns_entry->qpair, (struct spdk_nvme_cpl *)completion); 795acf617cSJames Bergsten fprintf(stderr, "I/O error status: %s\n", spdk_nvme_cpl_get_status_string(&completion->status)); 805acf617cSJames Bergsten fprintf(stderr, "Read I/O failed, aborting run\n"); 815acf617cSJames Bergsten sequence->is_completed = 2; 826915ad7cSNiklas Cassel exit(1); 835acf617cSJames Bergsten } 845acf617cSJames Bergsten 85219fbc90SKalwas, Jacek if (strcmp(sequence->buf, DATA_BUFFER_STRING)) { 86219fbc90SKalwas, Jacek fprintf(stderr, "Read data doesn't match write data\n"); 87219fbc90SKalwas, Jacek exit(1); 88219fbc90SKalwas, Jacek } 89219fbc90SKalwas, Jacek 90213e7a64SJim Harris /* 91213e7a64SJim Harris * The read I/O has completed. Print the contents of the 92213e7a64SJim Harris * buffer, free the buffer, then mark the sequence as 93213e7a64SJim Harris * completed. This will trigger the hello_world() function 94213e7a64SJim Harris * to exit its polling loop. 95213e7a64SJim Harris */ 96219fbc90SKalwas, Jacek printf("%s\n", sequence->buf); 97eb6a2cb8Szkhatami88 spdk_free(sequence->buf); 98213e7a64SJim Harris } 99213e7a64SJim Harris 100213e7a64SJim Harris static void 101213e7a64SJim Harris write_complete(void *arg, const struct spdk_nvme_cpl *completion) 102213e7a64SJim Harris { 103213e7a64SJim Harris struct hello_world_sequence *sequence = arg; 104213e7a64SJim Harris struct ns_entry *ns_entry = sequence->ns_entry; 105213e7a64SJim Harris int rc; 106213e7a64SJim Harris 1075acf617cSJames Bergsten /* See if an error occurred. If so, display information 1085acf617cSJames Bergsten * about it, and set completion value so that I/O 1095acf617cSJames Bergsten * caller is aware that an error occurred. 1105acf617cSJames Bergsten */ 1115acf617cSJames Bergsten if (spdk_nvme_cpl_is_error(completion)) { 1125acf617cSJames Bergsten spdk_nvme_qpair_print_completion(sequence->ns_entry->qpair, (struct spdk_nvme_cpl *)completion); 1135acf617cSJames Bergsten fprintf(stderr, "I/O error status: %s\n", spdk_nvme_cpl_get_status_string(&completion->status)); 1145acf617cSJames Bergsten fprintf(stderr, "Write I/O failed, aborting run\n"); 1155acf617cSJames Bergsten sequence->is_completed = 2; 1165acf617cSJames Bergsten exit(1); 1175acf617cSJames Bergsten } 118213e7a64SJim Harris /* 119213e7a64SJim Harris * The write I/O has completed. Free the buffer associated with 120213e7a64SJim Harris * the write I/O and allocate a new zeroed buffer for reading 121213e7a64SJim Harris * the data back from the NVMe namespace. 122213e7a64SJim Harris */ 1238f3d0a64SDaniel Verkamp if (sequence->using_cmb_io) { 124265a8436SBen Walker spdk_nvme_ctrlr_unmap_cmb(ns_entry->ctrlr); 1258f3d0a64SDaniel Verkamp } else { 126eb6a2cb8Szkhatami88 spdk_free(sequence->buf); 1278f3d0a64SDaniel Verkamp } 128*186b109dSJim Harris sequence->buf = spdk_zmalloc(0x1000, 0x1000, NULL, SPDK_ENV_NUMA_ID_ANY, SPDK_MALLOC_DMA); 129213e7a64SJim Harris 130213e7a64SJim Harris rc = spdk_nvme_ns_cmd_read(ns_entry->ns, ns_entry->qpair, sequence->buf, 131213e7a64SJim Harris 0, /* LBA start */ 132213e7a64SJim Harris 1, /* number of LBAs */ 133213e7a64SJim Harris read_complete, (void *)sequence, 0); 134213e7a64SJim Harris if (rc != 0) { 135213e7a64SJim Harris fprintf(stderr, "starting read I/O failed\n"); 136213e7a64SJim Harris exit(1); 137213e7a64SJim Harris } 138213e7a64SJim Harris } 139213e7a64SJim Harris 140213e7a64SJim Harris static void 141998ec7b0SNiklas Cassel reset_zone_complete(void *arg, const struct spdk_nvme_cpl *completion) 142998ec7b0SNiklas Cassel { 143998ec7b0SNiklas Cassel struct hello_world_sequence *sequence = arg; 144998ec7b0SNiklas Cassel 145998ec7b0SNiklas Cassel /* Assume the I/O was successful */ 146998ec7b0SNiklas Cassel sequence->is_completed = 1; 147998ec7b0SNiklas Cassel /* See if an error occurred. If so, display information 148998ec7b0SNiklas Cassel * about it, and set completion value so that I/O 149998ec7b0SNiklas Cassel * caller is aware that an error occurred. 150998ec7b0SNiklas Cassel */ 151998ec7b0SNiklas Cassel if (spdk_nvme_cpl_is_error(completion)) { 152998ec7b0SNiklas Cassel spdk_nvme_qpair_print_completion(sequence->ns_entry->qpair, (struct spdk_nvme_cpl *)completion); 153998ec7b0SNiklas Cassel fprintf(stderr, "I/O error status: %s\n", spdk_nvme_cpl_get_status_string(&completion->status)); 154998ec7b0SNiklas Cassel fprintf(stderr, "Reset zone I/O failed, aborting run\n"); 155998ec7b0SNiklas Cassel sequence->is_completed = 2; 156998ec7b0SNiklas Cassel exit(1); 157998ec7b0SNiklas Cassel } 158998ec7b0SNiklas Cassel } 159998ec7b0SNiklas Cassel 160998ec7b0SNiklas Cassel static void 161998ec7b0SNiklas Cassel reset_zone_and_wait_for_completion(struct hello_world_sequence *sequence) 162998ec7b0SNiklas Cassel { 163998ec7b0SNiklas Cassel if (spdk_nvme_zns_reset_zone(sequence->ns_entry->ns, sequence->ns_entry->qpair, 164998ec7b0SNiklas Cassel 0, /* starting LBA of the zone to reset */ 165998ec7b0SNiklas Cassel false, /* don't reset all zones */ 166998ec7b0SNiklas Cassel reset_zone_complete, 167998ec7b0SNiklas Cassel sequence)) { 168998ec7b0SNiklas Cassel fprintf(stderr, "starting reset zone I/O failed\n"); 169998ec7b0SNiklas Cassel exit(1); 170998ec7b0SNiklas Cassel } 171998ec7b0SNiklas Cassel while (!sequence->is_completed) { 172998ec7b0SNiklas Cassel spdk_nvme_qpair_process_completions(sequence->ns_entry->qpair, 0); 173998ec7b0SNiklas Cassel } 174998ec7b0SNiklas Cassel sequence->is_completed = 0; 175998ec7b0SNiklas Cassel } 176998ec7b0SNiklas Cassel 177998ec7b0SNiklas Cassel static void 178213e7a64SJim Harris hello_world(void) 179213e7a64SJim Harris { 180213e7a64SJim Harris struct ns_entry *ns_entry; 181213e7a64SJim Harris struct hello_world_sequence sequence; 182213e7a64SJim Harris int rc; 183265a8436SBen Walker size_t sz; 184213e7a64SJim Harris 1854c3fd228SShuhei Matsumoto TAILQ_FOREACH(ns_entry, &g_namespaces, link) { 186213e7a64SJim Harris /* 187213e7a64SJim Harris * Allocate an I/O qpair that we can use to submit read/write requests 188213e7a64SJim Harris * to namespaces on the controller. NVMe controllers typically support 189213e7a64SJim Harris * many qpairs per controller. Any I/O qpair allocated for a controller 190213e7a64SJim Harris * can submit I/O to any namespace on that controller. 191213e7a64SJim Harris * 192213e7a64SJim Harris * The SPDK NVMe driver provides no synchronization for qpair accesses - 193213e7a64SJim Harris * the application must ensure only a single thread submits I/O to a 194213e7a64SJim Harris * qpair, and that same thread must also check for completions on that 195213e7a64SJim Harris * qpair. This enables extremely efficient I/O processing by making all 196213e7a64SJim Harris * I/O operations completely lockless. 197213e7a64SJim Harris */ 198ce4fcbceSDaniel Verkamp ns_entry->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ns_entry->ctrlr, NULL, 0); 199213e7a64SJim Harris if (ns_entry->qpair == NULL) { 2004d01cd7dSgongchuang printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n"); 201213e7a64SJim Harris return; 202213e7a64SJim Harris } 203213e7a64SJim Harris 204213e7a64SJim Harris /* 2058a44220bSJohn Meneghini * Use spdk_dma_zmalloc to allocate a 4KB zeroed buffer. This memory 2062224ff21SBen Walker * will be pinned, which is required for data buffers used for SPDK NVMe 2072224ff21SBen Walker * I/O operations. 208213e7a64SJim Harris */ 2098f3d0a64SDaniel Verkamp sequence.using_cmb_io = 1; 210265a8436SBen Walker sequence.buf = spdk_nvme_ctrlr_map_cmb(ns_entry->ctrlr, &sz); 211265a8436SBen Walker if (sequence.buf == NULL || sz < 0x1000) { 2128f3d0a64SDaniel Verkamp sequence.using_cmb_io = 0; 213*186b109dSJim Harris sequence.buf = spdk_zmalloc(0x1000, 0x1000, NULL, SPDK_ENV_NUMA_ID_ANY, SPDK_MALLOC_DMA); 2148f3d0a64SDaniel Verkamp } 2158f3d0a64SDaniel Verkamp if (sequence.buf == NULL) { 2168f3d0a64SDaniel Verkamp printf("ERROR: write buffer allocation failed\n"); 2178f3d0a64SDaniel Verkamp return; 2188f3d0a64SDaniel Verkamp } 2198f3d0a64SDaniel Verkamp if (sequence.using_cmb_io) { 2208f3d0a64SDaniel Verkamp printf("INFO: using controller memory buffer for IO\n"); 2218f3d0a64SDaniel Verkamp } else { 2228f3d0a64SDaniel Verkamp printf("INFO: using host memory buffer for IO\n"); 2238f3d0a64SDaniel Verkamp } 224213e7a64SJim Harris sequence.is_completed = 0; 225213e7a64SJim Harris sequence.ns_entry = ns_entry; 226213e7a64SJim Harris 227213e7a64SJim Harris /* 228998ec7b0SNiklas Cassel * If the namespace is a Zoned Namespace, rather than a regular 229998ec7b0SNiklas Cassel * NVM namespace, we need to reset the first zone, before we 230998ec7b0SNiklas Cassel * write to it. This not needed for regular NVM namespaces. 231998ec7b0SNiklas Cassel */ 232998ec7b0SNiklas Cassel if (spdk_nvme_ns_get_csi(ns_entry->ns) == SPDK_NVME_CSI_ZNS) { 233998ec7b0SNiklas Cassel reset_zone_and_wait_for_completion(&sequence); 234998ec7b0SNiklas Cassel } 235998ec7b0SNiklas Cassel 236998ec7b0SNiklas Cassel /* 237219fbc90SKalwas, Jacek * Print DATA_BUFFER_STRING to sequence.buf. We will write this data to LBA 238213e7a64SJim Harris * 0 on the namespace, and then later read it back into a separate buffer 239213e7a64SJim Harris * to demonstrate the full I/O path. 240213e7a64SJim Harris */ 241219fbc90SKalwas, Jacek snprintf(sequence.buf, 0x1000, "%s", DATA_BUFFER_STRING); 242213e7a64SJim Harris 243213e7a64SJim Harris /* 244213e7a64SJim Harris * Write the data buffer to LBA 0 of this namespace. "write_complete" and 245213e7a64SJim Harris * "&sequence" are specified as the completion callback function and 246213e7a64SJim Harris * argument respectively. write_complete() will be called with the 247213e7a64SJim Harris * value of &sequence as a parameter when the write I/O is completed. 248213e7a64SJim Harris * This allows users to potentially specify different completion 249213e7a64SJim Harris * callback routines for each I/O, as well as pass a unique handle 250213e7a64SJim Harris * as an argument so the application knows which I/O has completed. 251213e7a64SJim Harris * 252213e7a64SJim Harris * Note that the SPDK NVMe driver will only check for completions 253213e7a64SJim Harris * when the application calls spdk_nvme_qpair_process_completions(). 254213e7a64SJim Harris * It is the responsibility of the application to trigger the polling 255213e7a64SJim Harris * process. 256213e7a64SJim Harris */ 257213e7a64SJim Harris rc = spdk_nvme_ns_cmd_write(ns_entry->ns, ns_entry->qpair, sequence.buf, 258213e7a64SJim Harris 0, /* LBA start */ 259213e7a64SJim Harris 1, /* number of LBAs */ 260213e7a64SJim Harris write_complete, &sequence, 0); 261213e7a64SJim Harris if (rc != 0) { 262213e7a64SJim Harris fprintf(stderr, "starting write I/O failed\n"); 263213e7a64SJim Harris exit(1); 264213e7a64SJim Harris } 265213e7a64SJim Harris 266213e7a64SJim Harris /* 267213e7a64SJim Harris * Poll for completions. 0 here means process all available completions. 268213e7a64SJim Harris * In certain usage models, the caller may specify a positive integer 269213e7a64SJim Harris * instead of 0 to signify the maximum number of completions it should 270213e7a64SJim Harris * process. This function will never block - if there are no 271213e7a64SJim Harris * completions pending on the specified qpair, it will return immediately. 272213e7a64SJim Harris * 273213e7a64SJim Harris * When the write I/O completes, write_complete() will submit a new I/O 274213e7a64SJim Harris * to read LBA 0 into a separate buffer, specifying read_complete() as its 275213e7a64SJim Harris * completion routine. When the read I/O completes, read_complete() will 276213e7a64SJim Harris * print the buffer contents and set sequence.is_completed = 1. That will 277213e7a64SJim Harris * break this loop and then exit the program. 278213e7a64SJim Harris */ 279213e7a64SJim Harris while (!sequence.is_completed) { 280213e7a64SJim Harris spdk_nvme_qpair_process_completions(ns_entry->qpair, 0); 281213e7a64SJim Harris } 282213e7a64SJim Harris 283213e7a64SJim Harris /* 284213e7a64SJim Harris * Free the I/O qpair. This typically is done when an application exits. 285213e7a64SJim Harris * But SPDK does support freeing and then reallocating qpairs during 286213e7a64SJim Harris * operation. It is the responsibility of the caller to ensure all 287213e7a64SJim Harris * pending I/O are completed before trying to free the qpair. 288213e7a64SJim Harris */ 289213e7a64SJim Harris spdk_nvme_ctrlr_free_io_qpair(ns_entry->qpair); 290213e7a64SJim Harris } 291213e7a64SJim Harris } 292213e7a64SJim Harris 293213e7a64SJim Harris static bool 29432e838afSBen Walker probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 295fcb00f37SDaniel Verkamp struct spdk_nvme_ctrlr_opts *opts) 296213e7a64SJim Harris { 29732e838afSBen Walker printf("Attaching to %s\n", trid->traddr); 298213e7a64SJim Harris 299213e7a64SJim Harris return true; 300213e7a64SJim Harris } 301213e7a64SJim Harris 302213e7a64SJim Harris static void 30332e838afSBen Walker attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, 304fcb00f37SDaniel Verkamp struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) 305213e7a64SJim Harris { 3067ffc8833SBen Walker int nsid; 307213e7a64SJim Harris struct ctrlr_entry *entry; 30834369a12SChangpeng Liu struct spdk_nvme_ns *ns; 309760ecb0aSJim Harris const struct spdk_nvme_ctrlr_data *cdata; 310213e7a64SJim Harris 311213e7a64SJim Harris entry = malloc(sizeof(struct ctrlr_entry)); 312213e7a64SJim Harris if (entry == NULL) { 313213e7a64SJim Harris perror("ctrlr_entry malloc"); 314213e7a64SJim Harris exit(1); 315213e7a64SJim Harris } 316213e7a64SJim Harris 31732e838afSBen Walker printf("Attached to %s\n", trid->traddr); 318213e7a64SJim Harris 319760ecb0aSJim Harris /* 320760ecb0aSJim Harris * spdk_nvme_ctrlr is the logical abstraction in SPDK for an NVMe 321760ecb0aSJim Harris * controller. During initialization, the IDENTIFY data for the 322760ecb0aSJim Harris * controller is read using an NVMe admin command, and that data 323760ecb0aSJim Harris * can be retrieved using spdk_nvme_ctrlr_get_data() to get 324760ecb0aSJim Harris * detailed information on the controller. Refer to the NVMe 325760ecb0aSJim Harris * specification for more details on IDENTIFY for NVMe controllers. 326760ecb0aSJim Harris */ 327760ecb0aSJim Harris cdata = spdk_nvme_ctrlr_get_data(ctrlr); 328760ecb0aSJim Harris 329213e7a64SJim Harris snprintf(entry->name, sizeof(entry->name), "%-20.20s (%-20.20s)", cdata->mn, cdata->sn); 330213e7a64SJim Harris 331213e7a64SJim Harris entry->ctrlr = ctrlr; 3324c3fd228SShuhei Matsumoto TAILQ_INSERT_TAIL(&g_controllers, entry, link); 333213e7a64SJim Harris 334213e7a64SJim Harris /* 33526c8e7ccSFelipe Franciosi * Each controller has one or more namespaces. An NVMe namespace is basically 336213e7a64SJim Harris * equivalent to a SCSI LUN. The controller's IDENTIFY data tells us how 337213e7a64SJim Harris * many namespaces exist on the controller. For Intel(R) P3X00 controllers, 338213e7a64SJim Harris * it will just be one namespace. 339213e7a64SJim Harris * 340213e7a64SJim Harris * Note that in NVMe, namespace IDs start at 1, not 0. 341213e7a64SJim Harris */ 3427ffc8833SBen Walker for (nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr); nsid != 0; 3437ffc8833SBen Walker nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr, nsid)) { 34434369a12SChangpeng Liu ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid); 34534369a12SChangpeng Liu if (ns == NULL) { 34634369a12SChangpeng Liu continue; 34734369a12SChangpeng Liu } 34834369a12SChangpeng Liu register_ns(ctrlr, ns); 349213e7a64SJim Harris } 350213e7a64SJim Harris } 351213e7a64SJim Harris 352213e7a64SJim Harris static void 353213e7a64SJim Harris cleanup(void) 354213e7a64SJim Harris { 3554c3fd228SShuhei Matsumoto struct ns_entry *ns_entry, *tmp_ns_entry; 3564c3fd228SShuhei Matsumoto struct ctrlr_entry *ctrlr_entry, *tmp_ctrlr_entry; 3570a903c91SShuhei Matsumoto struct spdk_nvme_detach_ctx *detach_ctx = NULL; 358213e7a64SJim Harris 3594c3fd228SShuhei Matsumoto TAILQ_FOREACH_SAFE(ns_entry, &g_namespaces, link, tmp_ns_entry) { 3604c3fd228SShuhei Matsumoto TAILQ_REMOVE(&g_namespaces, ns_entry, link); 361213e7a64SJim Harris free(ns_entry); 362213e7a64SJim Harris } 363213e7a64SJim Harris 3644c3fd228SShuhei Matsumoto TAILQ_FOREACH_SAFE(ctrlr_entry, &g_controllers, link, tmp_ctrlr_entry) { 3654c3fd228SShuhei Matsumoto TAILQ_REMOVE(&g_controllers, ctrlr_entry, link); 3660a903c91SShuhei Matsumoto spdk_nvme_detach_async(ctrlr_entry->ctrlr, &detach_ctx); 367213e7a64SJim Harris free(ctrlr_entry); 368213e7a64SJim Harris } 3690a903c91SShuhei Matsumoto 3704fe4040aSShuhei Matsumoto if (detach_ctx) { 3714fe4040aSShuhei Matsumoto spdk_nvme_detach_poll(detach_ctx); 3720a903c91SShuhei Matsumoto } 373213e7a64SJim Harris } 374213e7a64SJim Harris 375cda27398SWojciech Malikowski static void 376cda27398SWojciech Malikowski usage(const char *program_name) 377cda27398SWojciech Malikowski { 378cda27398SWojciech Malikowski printf("%s [options]", program_name); 3791f085e8eSMao Jiang printf("\t\n"); 380cda27398SWojciech Malikowski printf("options:\n"); 3811f085e8eSMao Jiang printf("\t[-d DPDK huge memory size in MB]\n"); 3821f085e8eSMao Jiang printf("\t[-g use single file descriptor for DPDK memory segments]\n"); 3831f085e8eSMao Jiang printf("\t[-i shared memory group ID]\n"); 3841f085e8eSMao Jiang printf("\t[-r remote NVMe over Fabrics target address]\n"); 3851f085e8eSMao Jiang printf("\t[-V enumerate VMD]\n"); 3861f085e8eSMao Jiang #ifdef DEBUG 3871f085e8eSMao Jiang printf("\t[-L enable debug logging]\n"); 3881f085e8eSMao Jiang #else 389809ae055Swanghailiangx printf("\t[-L enable debug logging (flag disabled, must reconfigure with --enable-debug)]\n"); 3901f085e8eSMao Jiang #endif 391cda27398SWojciech Malikowski } 392cda27398SWojciech Malikowski 393cda27398SWojciech Malikowski static int 3941f085e8eSMao Jiang parse_args(int argc, char **argv, struct spdk_env_opts *env_opts) 395cda27398SWojciech Malikowski { 3961f085e8eSMao Jiang int op, rc; 397cda27398SWojciech Malikowski 3981f085e8eSMao Jiang spdk_nvme_trid_populate_transport(&g_trid, SPDK_NVME_TRANSPORT_PCIE); 3991f085e8eSMao Jiang snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN); 4001f085e8eSMao Jiang 401206d8469SWojciech Panfil while ((op = getopt(argc, argv, "d:ghi:r:L:V")) != -1) { 402cda27398SWojciech Malikowski switch (op) { 403cda27398SWojciech Malikowski case 'V': 404cda27398SWojciech Malikowski g_vmd = true; 405cda27398SWojciech Malikowski break; 4061f085e8eSMao Jiang case 'i': 4071f085e8eSMao Jiang env_opts->shm_id = spdk_strtol(optarg, 10); 4081f085e8eSMao Jiang if (env_opts->shm_id < 0) { 4091f085e8eSMao Jiang fprintf(stderr, "Invalid shared memory ID\n"); 4101f085e8eSMao Jiang return env_opts->shm_id; 4111f085e8eSMao Jiang } 4121f085e8eSMao Jiang break; 4131f085e8eSMao Jiang case 'g': 4141f085e8eSMao Jiang env_opts->hugepage_single_segments = true; 4151f085e8eSMao Jiang break; 4161f085e8eSMao Jiang case 'r': 4171f085e8eSMao Jiang if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) { 4181f085e8eSMao Jiang fprintf(stderr, "Error parsing transport address\n"); 4191f085e8eSMao Jiang return 1; 4201f085e8eSMao Jiang } 4211f085e8eSMao Jiang break; 4221f085e8eSMao Jiang case 'd': 4231f085e8eSMao Jiang env_opts->mem_size = spdk_strtol(optarg, 10); 4241f085e8eSMao Jiang if (env_opts->mem_size < 0) { 4251f085e8eSMao Jiang fprintf(stderr, "Invalid DPDK memory size\n"); 4261f085e8eSMao Jiang return env_opts->mem_size; 4271f085e8eSMao Jiang } 4281f085e8eSMao Jiang break; 4291f085e8eSMao Jiang case 'L': 4301f085e8eSMao Jiang rc = spdk_log_set_flag(optarg); 4311f085e8eSMao Jiang if (rc < 0) { 4321f085e8eSMao Jiang fprintf(stderr, "unknown flag\n"); 4331f085e8eSMao Jiang usage(argv[0]); 4341f085e8eSMao Jiang exit(EXIT_FAILURE); 4351f085e8eSMao Jiang } 4361f085e8eSMao Jiang #ifdef DEBUG 4371f085e8eSMao Jiang spdk_log_set_print_level(SPDK_LOG_DEBUG); 4381f085e8eSMao Jiang #endif 4391f085e8eSMao Jiang break; 440206d8469SWojciech Panfil case 'h': 441206d8469SWojciech Panfil usage(argv[0]); 442206d8469SWojciech Panfil exit(EXIT_SUCCESS); 443cda27398SWojciech Malikowski default: 444cda27398SWojciech Malikowski usage(argv[0]); 445cda27398SWojciech Malikowski return 1; 446cda27398SWojciech Malikowski } 447cda27398SWojciech Malikowski } 448cda27398SWojciech Malikowski 449cda27398SWojciech Malikowski return 0; 450cda27398SWojciech Malikowski } 451cda27398SWojciech Malikowski 4528dd1cd21SBen Walker int 4538dd1cd21SBen Walker main(int argc, char **argv) 454213e7a64SJim Harris { 455213e7a64SJim Harris int rc; 45618d26e42SBen Walker struct spdk_env_opts opts; 457213e7a64SJim Harris 458213e7a64SJim Harris /* 45918d26e42SBen Walker * SPDK relies on an abstraction around the local environment 46018d26e42SBen Walker * named env that handles memory allocation and PCI device operations. 46118d26e42SBen Walker * This library must be initialized first. 462213e7a64SJim Harris * 463213e7a64SJim Harris */ 46457fd99b9SJim Harris opts.opts_size = sizeof(opts); 46518d26e42SBen Walker spdk_env_opts_init(&opts); 4661f085e8eSMao Jiang rc = parse_args(argc, argv, &opts); 4671f085e8eSMao Jiang if (rc != 0) { 4681f085e8eSMao Jiang return rc; 4691f085e8eSMao Jiang } 4701f085e8eSMao Jiang 47118d26e42SBen Walker opts.name = "hello_world"; 472095f4254SLance Hartmann if (spdk_env_init(&opts) < 0) { 473095f4254SLance Hartmann fprintf(stderr, "Unable to initialize SPDK env\n"); 474095f4254SLance Hartmann return 1; 475095f4254SLance Hartmann } 476213e7a64SJim Harris 477213e7a64SJim Harris printf("Initializing NVMe Controllers\n"); 478213e7a64SJim Harris 479cda27398SWojciech Malikowski if (g_vmd && spdk_vmd_init()) { 480cda27398SWojciech Malikowski fprintf(stderr, "Failed to initialize VMD." 481cda27398SWojciech Malikowski " Some NVMe devices can be unavailable.\n"); 482cda27398SWojciech Malikowski } 483cda27398SWojciech Malikowski 484213e7a64SJim Harris /* 485213e7a64SJim Harris * Start the SPDK NVMe enumeration process. probe_cb will be called 486213e7a64SJim Harris * for each NVMe controller found, giving our application a choice on 487213e7a64SJim Harris * whether to attach to each controller. attach_cb will then be 488213e7a64SJim Harris * called for each controller after the SPDK NVMe driver has completed 489213e7a64SJim Harris * initializing the controller we chose to attach. 490213e7a64SJim Harris */ 4911f085e8eSMao Jiang rc = spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL); 492213e7a64SJim Harris if (rc != 0) { 493213e7a64SJim Harris fprintf(stderr, "spdk_nvme_probe() failed\n"); 4949ec9c8b3SChangpeng Liu rc = 1; 4959ec9c8b3SChangpeng Liu goto exit; 496213e7a64SJim Harris } 497213e7a64SJim Harris 4984c3fd228SShuhei Matsumoto if (TAILQ_EMPTY(&g_controllers)) { 49966349fbfSJim Harris fprintf(stderr, "no NVMe controllers found\n"); 5009ec9c8b3SChangpeng Liu rc = 1; 5019ec9c8b3SChangpeng Liu goto exit; 50266349fbfSJim Harris } 50366349fbfSJim Harris 504213e7a64SJim Harris printf("Initialization complete.\n"); 505213e7a64SJim Harris hello_world(); 5067692207eSWojciech Panfil 5077692207eSWojciech Panfil exit: 50821828e4aSWojciech Panfil fflush(stdout); 509213e7a64SJim Harris cleanup(); 510ce6171d4SKonrad Sztyber if (g_vmd) { 511ce6171d4SKonrad Sztyber spdk_vmd_fini(); 512ce6171d4SKonrad Sztyber } 513ce6171d4SKonrad Sztyber 5149ec9c8b3SChangpeng Liu spdk_env_fini(); 5159ec9c8b3SChangpeng Liu return rc; 516213e7a64SJim Harris } 517