xref: /spdk/examples/nvme/cmb_copy/cmb_copy.c (revision b37db06935181fd0e8f5592a96d860040abaa201)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2020 Intel Corporation.
31f9da54eSStephen Bates  *   Copyright (c) Eideticom Inc.
41f9da54eSStephen Bates  *   All rights reserved.
51f9da54eSStephen Bates  */
61f9da54eSStephen Bates 
71f9da54eSStephen Bates #include "spdk/stdinc.h"
81f9da54eSStephen Bates 
91f9da54eSStephen Bates #include "spdk/env.h"
101f9da54eSStephen Bates #include "spdk/nvme.h"
110f9dc2afSShuhei Matsumoto #include "spdk/string.h"
121f9da54eSStephen Bates 
131f9da54eSStephen Bates #define CMB_COPY_DELIM "-"
141f9da54eSStephen Bates #define CMB_COPY_READ 0
151f9da54eSStephen Bates #define CMB_COPY_WRITE 1
161f9da54eSStephen Bates 
171f9da54eSStephen Bates struct nvme_io {
181f9da54eSStephen Bates 	struct spdk_nvme_ctrlr *ctrlr;
191f9da54eSStephen Bates 	struct spdk_nvme_transport_id trid;
201f9da54eSStephen Bates 	struct spdk_nvme_qpair	*qpair;
211f9da54eSStephen Bates 	struct spdk_nvme_ns *ns;
221f9da54eSStephen Bates 	unsigned nsid;
231f9da54eSStephen Bates 	unsigned slba;
241f9da54eSStephen Bates 	unsigned nlbas;
251f9da54eSStephen Bates 	uint32_t lba_size;
261f9da54eSStephen Bates 	unsigned done;
271f9da54eSStephen Bates };
281f9da54eSStephen Bates 
291f9da54eSStephen Bates struct cmb_t {
301f9da54eSStephen Bates 	struct spdk_nvme_transport_id trid;
311f9da54eSStephen Bates 	struct spdk_nvme_ctrlr *ctrlr;
321f9da54eSStephen Bates };
331f9da54eSStephen Bates 
341f9da54eSStephen Bates struct config {
351f9da54eSStephen Bates 	struct nvme_io read;
361f9da54eSStephen Bates 	struct nvme_io write;
371f9da54eSStephen Bates 	struct cmb_t   cmb;
381f9da54eSStephen Bates 	size_t         copy_size;
391f9da54eSStephen Bates };
401f9da54eSStephen Bates 
411f9da54eSStephen Bates static struct config g_config;
421f9da54eSStephen Bates 
431f9da54eSStephen Bates /* Namespaces index from 1. Return 0 to invoke an error */
4404470499Sdongx.yi static unsigned
4504470499Sdongx.yi get_nsid(const struct spdk_nvme_transport_id *trid)
461f9da54eSStephen Bates {
471f9da54eSStephen Bates 	if (!strcmp(trid->traddr, g_config.read.trid.traddr)) {
481f9da54eSStephen Bates 		return g_config.read.nsid;
491f9da54eSStephen Bates 	}
501f9da54eSStephen Bates 	if (!strcmp(trid->traddr, g_config.write.trid.traddr)) {
511f9da54eSStephen Bates 		return g_config.write.nsid;
521f9da54eSStephen Bates 	}
531f9da54eSStephen Bates 	return 0;
541f9da54eSStephen Bates }
551f9da54eSStephen Bates 
5604470499Sdongx.yi static int
5704470499Sdongx.yi get_rw(const struct spdk_nvme_transport_id *trid)
581f9da54eSStephen Bates {
591f9da54eSStephen Bates 	if (!strcmp(trid->traddr, g_config.read.trid.traddr)) {
601f9da54eSStephen Bates 		return CMB_COPY_READ;
611f9da54eSStephen Bates 	}
621f9da54eSStephen Bates 	if (!strcmp(trid->traddr, g_config.write.trid.traddr)) {
631f9da54eSStephen Bates 		return CMB_COPY_WRITE;
641f9da54eSStephen Bates 	}
651f9da54eSStephen Bates 	return -1;
661f9da54eSStephen Bates }
671f9da54eSStephen Bates 
681f9da54eSStephen Bates static void
691f9da54eSStephen Bates check_io(void *arg, const struct spdk_nvme_cpl *completion)
701f9da54eSStephen Bates {
711f9da54eSStephen Bates 	int *rw = (unsigned *)arg;
721f9da54eSStephen Bates 
731f9da54eSStephen Bates 	if (*rw == CMB_COPY_READ) {
741f9da54eSStephen Bates 		g_config.read.done = 1;
751f9da54eSStephen Bates 	} else {
761f9da54eSStephen Bates 		g_config.write.done = 1;
771f9da54eSStephen Bates 	}
781f9da54eSStephen Bates }
791f9da54eSStephen Bates 
801f9da54eSStephen Bates static int
811f9da54eSStephen Bates cmb_copy(void)
821f9da54eSStephen Bates {
831f9da54eSStephen Bates 	int rc = 0, rw;
841f9da54eSStephen Bates 	void *buf;
85265a8436SBen Walker 	size_t sz;
861f9da54eSStephen Bates 
871f9da54eSStephen Bates 	/* Allocate QPs for the read and write controllers */
881f9da54eSStephen Bates 	g_config.read.qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_config.read.ctrlr, NULL, 0);
891f9da54eSStephen Bates 	g_config.write.qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_config.write.ctrlr, NULL, 0);
901f9da54eSStephen Bates 	if (g_config.read.qpair == NULL || g_config.read.qpair == NULL) {
911f9da54eSStephen Bates 		printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
921f9da54eSStephen Bates 		return -ENOMEM;
931f9da54eSStephen Bates 	}
941f9da54eSStephen Bates 
951f9da54eSStephen Bates 	/* Allocate a buffer from our CMB */
96265a8436SBen Walker 	buf = spdk_nvme_ctrlr_map_cmb(g_config.cmb.ctrlr, &sz);
97c34f224fSChangpeng Liu 	if (buf == NULL || sz < g_config.copy_size) {
981f9da54eSStephen Bates 		printf("ERROR: buffer allocation failed\n");
991f9da54eSStephen Bates 		printf("Are you sure %s has a valid CMB?\n",
1001f9da54eSStephen Bates 		       g_config.cmb.trid.traddr);
1011f9da54eSStephen Bates 		return -ENOMEM;
1021f9da54eSStephen Bates 	}
1031f9da54eSStephen Bates 
1041f9da54eSStephen Bates 	/* Clear the done flags */
1051f9da54eSStephen Bates 	g_config.read.done = 0;
1061f9da54eSStephen Bates 	g_config.write.done = 0;
1071f9da54eSStephen Bates 
1081f9da54eSStephen Bates 	rw = CMB_COPY_READ;
1091f9da54eSStephen Bates 	/* Do the read to the CMB IO buffer */
1101f9da54eSStephen Bates 	rc = spdk_nvme_ns_cmd_read(g_config.read.ns, g_config.read.qpair, buf,
1111f9da54eSStephen Bates 				   g_config.read.slba, g_config.read.nlbas,
1121f9da54eSStephen Bates 				   check_io, &rw, 0);
1131f9da54eSStephen Bates 	if (rc != 0) {
1141f9da54eSStephen Bates 		fprintf(stderr, "starting read I/O failed\n");
1151f9da54eSStephen Bates 		return -EIO;
1161f9da54eSStephen Bates 	}
1171f9da54eSStephen Bates 	while (!g_config.read.done) {
1181f9da54eSStephen Bates 		spdk_nvme_qpair_process_completions(g_config.read.qpair, 0);
1191f9da54eSStephen Bates 	}
1201f9da54eSStephen Bates 
1211f9da54eSStephen Bates 	/* Do the write from the CMB IO buffer */
1221f9da54eSStephen Bates 	rw = CMB_COPY_WRITE;
1231f9da54eSStephen Bates 	rc = spdk_nvme_ns_cmd_write(g_config.write.ns, g_config.write.qpair, buf,
1241f9da54eSStephen Bates 				    g_config.write.slba, g_config.write.nlbas,
1251f9da54eSStephen Bates 				    check_io, &rw, 0);
1261f9da54eSStephen Bates 	if (rc != 0) {
1271f9da54eSStephen Bates 		fprintf(stderr, "starting write I/O failed\n");
1281f9da54eSStephen Bates 		return -EIO;
1291f9da54eSStephen Bates 	}
1301f9da54eSStephen Bates 	while (!g_config.write.done) {
1311f9da54eSStephen Bates 		spdk_nvme_qpair_process_completions(g_config.write.qpair, 0);
1321f9da54eSStephen Bates 	}
1331f9da54eSStephen Bates 
1341f9da54eSStephen Bates 	/* Clear the done flags */
1351f9da54eSStephen Bates 	g_config.read.done = 0;
1361f9da54eSStephen Bates 	g_config.write.done = 0;
1371f9da54eSStephen Bates 
1381f9da54eSStephen Bates 	/* Free CMB buffer */
139265a8436SBen Walker 	spdk_nvme_ctrlr_unmap_cmb(g_config.cmb.ctrlr);
1401f9da54eSStephen Bates 
1411f9da54eSStephen Bates 	/* Free the queues */
1421f9da54eSStephen Bates 	spdk_nvme_ctrlr_free_io_qpair(g_config.read.qpair);
1431f9da54eSStephen Bates 	spdk_nvme_ctrlr_free_io_qpair(g_config.write.qpair);
1441f9da54eSStephen Bates 
1451f9da54eSStephen Bates 	return rc;
1461f9da54eSStephen Bates }
1471f9da54eSStephen Bates 
1481f9da54eSStephen Bates static bool
1491f9da54eSStephen Bates probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
1501f9da54eSStephen Bates 	 struct spdk_nvme_ctrlr_opts *opts)
1511f9da54eSStephen Bates {
1521f9da54eSStephen Bates 	/* We will only attach to the read or write controller */
1531f9da54eSStephen Bates 	if (strcmp(trid->traddr, g_config.read.trid.traddr) &&
1541f9da54eSStephen Bates 	    strcmp(trid->traddr, g_config.write.trid.traddr)) {
1551f9da54eSStephen Bates 		printf("%s - not probed %s!\n", __func__, trid->traddr);
1561f9da54eSStephen Bates 		return 0;
1571f9da54eSStephen Bates 	}
1581f9da54eSStephen Bates 
159c34f224fSChangpeng Liu 	opts->use_cmb_sqs = false;
160c34f224fSChangpeng Liu 
1611f9da54eSStephen Bates 	printf("%s - probed %s!\n", __func__, trid->traddr);
1621f9da54eSStephen Bates 	return 1;
1631f9da54eSStephen Bates }
1641f9da54eSStephen Bates 
1651f9da54eSStephen Bates static void
1661f9da54eSStephen Bates attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
1671f9da54eSStephen Bates 	  struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
1681f9da54eSStephen Bates {
1691f9da54eSStephen Bates 	struct spdk_nvme_ns *ns;
1701f9da54eSStephen Bates 
1711f9da54eSStephen Bates 	ns = spdk_nvme_ctrlr_get_ns(ctrlr, get_nsid(trid));
1721f9da54eSStephen Bates 	if (ns == NULL) {
1731f9da54eSStephen Bates 		fprintf(stderr, "Could not locate namespace %d on controller %s.\n",
1741f9da54eSStephen Bates 			get_nsid(trid), trid->traddr);
1751f9da54eSStephen Bates 		exit(-1);
1761f9da54eSStephen Bates 	}
1771f9da54eSStephen Bates 	if (get_rw(trid) == CMB_COPY_READ) {
1781f9da54eSStephen Bates 		g_config.read.ctrlr    = ctrlr;
1791f9da54eSStephen Bates 		g_config.read.ns       = ns;
1801f9da54eSStephen Bates 		g_config.read.lba_size = spdk_nvme_ns_get_sector_size(ns);
1811f9da54eSStephen Bates 	} else {
1821f9da54eSStephen Bates 		g_config.write.ctrlr    = ctrlr;
1831f9da54eSStephen Bates 		g_config.write.ns       = ns;
1841f9da54eSStephen Bates 		g_config.write.lba_size = spdk_nvme_ns_get_sector_size(ns);
1851f9da54eSStephen Bates 	}
1861f9da54eSStephen Bates 	printf("%s - attached %s!\n", __func__, trid->traddr);
1871f9da54eSStephen Bates 
1881f9da54eSStephen Bates 	return;
1891f9da54eSStephen Bates }
1901f9da54eSStephen Bates 
1911f9da54eSStephen Bates static void
1921f9da54eSStephen Bates usage(char *program_name)
1931f9da54eSStephen Bates {
1941f9da54eSStephen Bates 	printf("%s options (all mandatory)", program_name);
1951f9da54eSStephen Bates 	printf("\n");
1961f9da54eSStephen Bates 	printf("\t[-r NVMe read parameters]\n");
1971f9da54eSStephen Bates 	printf("\t[-w NVMe write parameters]\n");
1981f9da54eSStephen Bates 	printf("\t[-c CMB to use for data buffers]\n");
1991f9da54eSStephen Bates 	printf("\n");
2001f9da54eSStephen Bates 	printf("Read/Write params:\n");
2011f9da54eSStephen Bates 	printf("  <pci id>-<namespace>-<start LBA>-<number of LBAs>\n");
2021f9da54eSStephen Bates }
2031f9da54eSStephen Bates 
2041f9da54eSStephen Bates static void
2051f9da54eSStephen Bates parse(char *in, struct nvme_io *io)
2061f9da54eSStephen Bates {
2071f9da54eSStephen Bates 	char *tok = NULL;
208*b37db069SXuQi 	char *sp = NULL;
2090f9dc2afSShuhei Matsumoto 	long int val;
2101f9da54eSStephen Bates 
211*b37db069SXuQi 	tok = strtok_r(in, CMB_COPY_DELIM, &sp);
2121f9da54eSStephen Bates 	if (tok == NULL) {
2131f9da54eSStephen Bates 		goto err;
2141f9da54eSStephen Bates 	}
2151f9da54eSStephen Bates 	snprintf(&io->trid.traddr[0], SPDK_NVMF_TRADDR_MAX_LEN + 1,
2161f9da54eSStephen Bates 		 "%s", tok);
2171f9da54eSStephen Bates 
218*b37db069SXuQi 	tok = strtok_r(NULL, CMB_COPY_DELIM, &sp);
2191f9da54eSStephen Bates 	if (tok == NULL) {
2201f9da54eSStephen Bates 		goto err;
2211f9da54eSStephen Bates 	}
2220f9dc2afSShuhei Matsumoto 	val = spdk_strtol(tok, 10);
2230f9dc2afSShuhei Matsumoto 	if (val < 0) {
2240f9dc2afSShuhei Matsumoto 		goto err;
2250f9dc2afSShuhei Matsumoto 	}
2260f9dc2afSShuhei Matsumoto 	io->nsid  = (unsigned)val;
2271f9da54eSStephen Bates 
228*b37db069SXuQi 	tok = strtok_r(NULL, CMB_COPY_DELIM, &sp);
2291f9da54eSStephen Bates 	if (tok == NULL) {
2301f9da54eSStephen Bates 		goto err;
2311f9da54eSStephen Bates 	}
2320f9dc2afSShuhei Matsumoto 	val = spdk_strtol(tok, 10);
2330f9dc2afSShuhei Matsumoto 	if (val < 0) {
2340f9dc2afSShuhei Matsumoto 		goto err;
2350f9dc2afSShuhei Matsumoto 	}
2360f9dc2afSShuhei Matsumoto 	io->slba  = (unsigned)val;
2371f9da54eSStephen Bates 
238*b37db069SXuQi 	tok = strtok_r(NULL, CMB_COPY_DELIM, &sp);
2391f9da54eSStephen Bates 	if (tok == NULL) {
2401f9da54eSStephen Bates 		goto err;
2411f9da54eSStephen Bates 	}
2420f9dc2afSShuhei Matsumoto 	val = spdk_strtol(tok, 10);
2430f9dc2afSShuhei Matsumoto 	if (val < 0) {
2440f9dc2afSShuhei Matsumoto 		goto err;
2450f9dc2afSShuhei Matsumoto 	}
2460f9dc2afSShuhei Matsumoto 	io->nlbas = (unsigned)val;
2471f9da54eSStephen Bates 
248*b37db069SXuQi 	tok = strtok_r(NULL, CMB_COPY_DELIM, &sp);
2491f9da54eSStephen Bates 	if (tok != NULL) {
2501f9da54eSStephen Bates 		goto err;
2511f9da54eSStephen Bates 	}
2521f9da54eSStephen Bates 	return;
2531f9da54eSStephen Bates 
2541f9da54eSStephen Bates err:
2551f9da54eSStephen Bates 	fprintf(stderr, "%s: error parsing %s\n", __func__, in);
2561f9da54eSStephen Bates 	exit(-1);
2571f9da54eSStephen Bates 
2581f9da54eSStephen Bates }
2591f9da54eSStephen Bates 
2601f9da54eSStephen Bates static int
2611f9da54eSStephen Bates parse_args(int argc, char **argv)
2621f9da54eSStephen Bates {
2631f9da54eSStephen Bates 	int op;
2641f9da54eSStephen Bates 	unsigned read = 0, write = 0, cmb = 0;
2651f9da54eSStephen Bates 
2661f9da54eSStephen Bates 	while ((op = getopt(argc, argv, "r:w:c:")) != -1) {
2671f9da54eSStephen Bates 		switch (op) {
2681f9da54eSStephen Bates 		case 'r':
2691f9da54eSStephen Bates 			parse(optarg, &g_config.read);
2701f9da54eSStephen Bates 			read = 1;
2711f9da54eSStephen Bates 			break;
2721f9da54eSStephen Bates 		case 'w':
2731f9da54eSStephen Bates 			parse(optarg, &g_config.write);
2741f9da54eSStephen Bates 			write = 1;
2751f9da54eSStephen Bates 			break;
2761f9da54eSStephen Bates 		case 'c':
2771f9da54eSStephen Bates 			snprintf(g_config.cmb.trid.traddr, SPDK_NVMF_TRADDR_MAX_LEN + 1,
2781f9da54eSStephen Bates 				 "%s", optarg);
2791f9da54eSStephen Bates 			cmb = 1;
2801f9da54eSStephen Bates 			break;
2811f9da54eSStephen Bates 		default:
2821f9da54eSStephen Bates 			usage(argv[0]);
2831f9da54eSStephen Bates 			return 1;
2841f9da54eSStephen Bates 		}
2851f9da54eSStephen Bates 	}
2861f9da54eSStephen Bates 
2871f9da54eSStephen Bates 	if ((!read || !write || !cmb)) {
2881f9da54eSStephen Bates 		usage(argv[0]);
2891f9da54eSStephen Bates 		return 1;
2901f9da54eSStephen Bates 	}
2911f9da54eSStephen Bates 
2921f9da54eSStephen Bates 	return 0;
2931f9da54eSStephen Bates }
2941f9da54eSStephen Bates 
2958dd1cd21SBen Walker int
2968dd1cd21SBen Walker main(int argc, char **argv)
2971f9da54eSStephen Bates {
2981f9da54eSStephen Bates 	int rc = 0;
2991f9da54eSStephen Bates 	struct spdk_env_opts opts;
3001f9da54eSStephen Bates 
3011f9da54eSStephen Bates 	/*
3021f9da54eSStephen Bates 	 * Parse the input arguments. For now we use the following
3031f9da54eSStephen Bates 	 * format list:
3041f9da54eSStephen Bates 	 *
3051f9da54eSStephen Bates 	 * <pci id>-<namespace>-<start LBA>-<number of LBAs>
3061f9da54eSStephen Bates 	 *
3071f9da54eSStephen Bates 	 */
3081f9da54eSStephen Bates 	rc = parse_args(argc, argv);
3091f9da54eSStephen Bates 	if (rc) {
3101f9da54eSStephen Bates 		fprintf(stderr, "Error in parse_args(): %d\n",
3111f9da54eSStephen Bates 			rc);
3121f9da54eSStephen Bates 		return -1;
3131f9da54eSStephen Bates 	}
3141f9da54eSStephen Bates 
3151f9da54eSStephen Bates 	/*
3161f9da54eSStephen Bates 	 * SPDK relies on an abstraction around the local environment
3171f9da54eSStephen Bates 	 * named env that handles memory allocation and PCI device operations.
3181f9da54eSStephen Bates 	 * This library must be initialized first.
3191f9da54eSStephen Bates 	 *
3201f9da54eSStephen Bates 	 */
32157fd99b9SJim Harris 	opts.opts_size = sizeof(opts);
3221f9da54eSStephen Bates 	spdk_env_opts_init(&opts);
3231f9da54eSStephen Bates 	opts.name = "cmb_copy";
3241f9da54eSStephen Bates 	opts.shm_id = 0;
3251f9da54eSStephen Bates 	if (spdk_env_init(&opts) < 0) {
3261f9da54eSStephen Bates 		fprintf(stderr, "Unable to initialize SPDK env\n");
3271f9da54eSStephen Bates 		return 1;
3281f9da54eSStephen Bates 	}
3291f9da54eSStephen Bates 
3301f9da54eSStephen Bates 	/*
3311f9da54eSStephen Bates 	 * CMBs only apply to PCIe attached NVMe controllers so we
3321f9da54eSStephen Bates 	 * only probe the PCIe bus. This is the default when we pass
3331f9da54eSStephen Bates 	 * in NULL for the first argument.
3341f9da54eSStephen Bates 	 */
3351f9da54eSStephen Bates 
3361f9da54eSStephen Bates 	rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL);
3371f9da54eSStephen Bates 	if (rc) {
3381f9da54eSStephen Bates 		fprintf(stderr, "Error in spdk_nvme_probe(): %d\n",
3391f9da54eSStephen Bates 			rc);
3401f9da54eSStephen Bates 		return -1;
3411f9da54eSStephen Bates 	}
3421f9da54eSStephen Bates 
3431f9da54eSStephen Bates 	/*
3441f9da54eSStephen Bates 	 * For now enforce that the read and write controller are not
3451f9da54eSStephen Bates 	 * the same. This avoids an internal only DMA.
3461f9da54eSStephen Bates 	 */
3471f9da54eSStephen Bates 	if (!strcmp(g_config.write.trid.traddr, g_config.read.trid.traddr)) {
3481f9da54eSStephen Bates 		fprintf(stderr, "Read and Write controllers must differ!\n");
3491f9da54eSStephen Bates 		return -1;
3501f9da54eSStephen Bates 	}
3511f9da54eSStephen Bates 
3521f9da54eSStephen Bates 	/*
3531f9da54eSStephen Bates 	 * Perform a few sanity checks and set the buffer size for the
3541f9da54eSStephen Bates 	 * CMB.
3551f9da54eSStephen Bates 	 */
3561f9da54eSStephen Bates 	if (g_config.read.nlbas * g_config.read.lba_size !=
3571f9da54eSStephen Bates 	    g_config.write.nlbas * g_config.write.lba_size) {
3581f9da54eSStephen Bates 		fprintf(stderr, "Read and write sizes do not match!\n");
3591f9da54eSStephen Bates 		return -1;
3601f9da54eSStephen Bates 	}
3611f9da54eSStephen Bates 	g_config.copy_size = g_config.read.nlbas * g_config.read.lba_size;
3621f9da54eSStephen Bates 
3631f9da54eSStephen Bates 	/*
3641f9da54eSStephen Bates 	 * Get the ctrlr pointer for the CMB. For now we assume this
3651f9da54eSStephen Bates 	 * is either the read or write NVMe controller though in
3661f9da54eSStephen Bates 	 * theory that is not a necessary condition.
3671f9da54eSStephen Bates 	 */
3681f9da54eSStephen Bates 
3691f9da54eSStephen Bates 	if (!strcmp(g_config.cmb.trid.traddr, g_config.read.trid.traddr)) {
3701f9da54eSStephen Bates 		g_config.cmb.ctrlr = g_config.read.ctrlr;
3711f9da54eSStephen Bates 	}
3721f9da54eSStephen Bates 	if (!strcmp(g_config.cmb.trid.traddr, g_config.write.trid.traddr)) {
3731f9da54eSStephen Bates 		g_config.cmb.ctrlr = g_config.write.ctrlr;
3741f9da54eSStephen Bates 	}
3751f9da54eSStephen Bates 
3769f7cb6aaSChangpeng Liu 	if (!g_config.read.ctrlr || !g_config.write.ctrlr) {
3779f7cb6aaSChangpeng Liu 		fprintf(stderr, "No NVMe controller that support CMB was found!\n");
3789f7cb6aaSChangpeng Liu 		return -1;
3799f7cb6aaSChangpeng Liu 	}
3809f7cb6aaSChangpeng Liu 
3811f9da54eSStephen Bates 	/*
3821f9da54eSStephen Bates 	 * Call the cmb_copy() function which performs the CMB
3831f9da54eSStephen Bates 	 * based copy or returns an error code if it fails.
3841f9da54eSStephen Bates 	 */
3851f9da54eSStephen Bates 	rc = cmb_copy();
3861f9da54eSStephen Bates 	if (rc) {
3871f9da54eSStephen Bates 		fprintf(stderr, "Error in spdk_cmb_copy(): %d\n",
3881f9da54eSStephen Bates 			rc);
3891f9da54eSStephen Bates 		return -1;
3901f9da54eSStephen Bates 	}
3911f9da54eSStephen Bates 
3921f9da54eSStephen Bates 	return rc;
3931f9da54eSStephen Bates }
394