Commit 8374a727 authored by Daniel Verkamp's avatar Daniel Verkamp
Browse files

nvme: refactor nvme_attach() into nvme_probe()



The new probing API will find all NVMe devices on the system and ask the
caller whether to attach to each one.  The caller will then receive a
callback once each controller has finished initializing and has been
attached to the driver.

This will enable cleanup of the PCI abstraction layer (enabling us to
use DPDK PCI functionality) as well as allowing future work on parallel
NVMe controller startup and PCIe hotplug support.

Change-Id: I3cdde7bfab0bc0bea1993dd549b9b0e8d36db9be
Signed-off-by: default avatarDaniel Verkamp <daniel.verkamp@intel.com>
parent 20c767e7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@

\section key_functions Key Functions

- nvme_attach() \copybrief nvme_attach()
- nvme_probe() \copybrief nvme_probe()
- nvme_ns_cmd_read() \copybrief nvme_ns_cmd_read()
- nvme_ns_cmd_write() \copybrief nvme_ns_cmd_write()
- nvme_ns_cmd_deallocate() \copybrief nvme_ns_cmd_deallocate()
+29 −36
Original line number Diff line number Diff line
@@ -768,6 +768,32 @@ parse_args(int argc, char **argv)
	return 0;
}

static bool
probe_cb(void *cb_ctx, void *pci_dev)
{
	struct pci_device *dev = pci_dev;

	if (pci_device_has_non_uio_driver(dev)) {
		fprintf(stderr, "non-uio kernel driver attached to NVMe\n");
		fprintf(stderr, " controller at PCI address %04x:%02x:%02x.%02x\n",
			spdk_pci_device_get_domain(dev),
			spdk_pci_device_get_bus(dev),
			spdk_pci_device_get_dev(dev),
			spdk_pci_device_get_func(dev));
		fprintf(stderr, " skipping...\n");
		return false;
	}

	return true;
}

static void
attach_cb(void *cb_ctx, void *pci_dev, struct nvme_controller *ctrlr)
{
	print_controller(ctrlr, pci_dev);
	nvme_detach(ctrlr);
}

static const char *ealargs[] = {
	"identify",
	"-c 0x1",
@@ -776,9 +802,6 @@ static const char *ealargs[] = {

int main(int argc, char **argv)
{
	struct pci_device_iterator	*pci_dev_iter;
	struct pci_device		*pci_dev;
	struct pci_id_match		match;
	int				rc;

	rc = parse_args(argc, argv);
@@ -806,43 +829,13 @@ int main(int argc, char **argv)

	pci_system_init();

	match.vendor_id =	PCI_MATCH_ANY;
	match.subvendor_id =	PCI_MATCH_ANY;
	match.subdevice_id =	PCI_MATCH_ANY;
	match.device_id =	PCI_MATCH_ANY;
	match.device_class =	NVME_CLASS_CODE;
	match.device_class_mask = 0xFFFFFF;

	pci_dev_iter = pci_id_match_iterator_create(&match);

	rc = 0;
	while ((pci_dev = pci_device_next(pci_dev_iter))) {
		struct nvme_controller *ctrlr;

		if (pci_device_has_non_uio_driver(pci_dev)) {
			fprintf(stderr, "non-uio kernel driver attached to nvme\n");
			fprintf(stderr, " controller at pci bdf %d:%d:%d\n",
				pci_dev->bus, pci_dev->dev, pci_dev->func);
			fprintf(stderr, " skipping...\n");
			continue;
		}

		pci_device_probe(pci_dev);

		ctrlr = nvme_attach(pci_dev);
		if (ctrlr == NULL) {
			fprintf(stderr, "failed to attach to NVMe controller at PCI BDF %d:%d:%d\n",
				pci_dev->bus, pci_dev->dev, pci_dev->func);
	if (nvme_probe(NULL, probe_cb, attach_cb) != 0) {
		fprintf(stderr, "nvme_probe() failed\n");
		rc = 1;
			continue;
		}

		print_controller(ctrlr, pci_dev);
		nvme_detach(ctrlr);
	}

	cleanup();

	pci_iterator_destroy(pci_dev_iter);
	return rc;
}
+43 −40
Original line number Diff line number Diff line
@@ -819,55 +819,58 @@ register_workers(void)
	return 0;
}

static int
register_controllers(void)
static bool
probe_cb(void *cb_ctx, void *pci_dev)
{
	struct pci_device_iterator	*pci_dev_iter;
	struct pci_device		*pci_dev;
	struct pci_id_match		match;
	int				rc;
	struct pci_device *dev = pci_dev;

	if (pci_device_has_non_uio_driver(dev)) {
		fprintf(stderr, "non-uio kernel driver attached to NVMe\n");
		fprintf(stderr, " controller at PCI address %04x:%02x:%02x.%02x\n",
			spdk_pci_device_get_domain(dev),
			spdk_pci_device_get_bus(dev),
			spdk_pci_device_get_dev(dev),
			spdk_pci_device_get_func(dev));
		fprintf(stderr, " skipping...\n");
		return false;
	}

	printf("Initializing NVMe Controllers\n");
	printf("Attaching to %04x:%02x:%02x.%02x\n",
	       spdk_pci_device_get_domain(dev),
	       spdk_pci_device_get_bus(dev),
	       spdk_pci_device_get_dev(dev),
	       spdk_pci_device_get_func(dev));

	pci_system_init();

	match.vendor_id =	PCI_MATCH_ANY;
	match.subvendor_id =	PCI_MATCH_ANY;
	match.subdevice_id =	PCI_MATCH_ANY;
	match.device_id =	PCI_MATCH_ANY;
	match.device_class =	NVME_CLASS_CODE;
	match.device_class_mask = 0xFFFFFF;
	return true;
}

	pci_dev_iter = pci_id_match_iterator_create(&match);
static void
attach_cb(void *cb_ctx, void *pci_dev, struct nvme_controller *ctrlr)
{
	struct pci_device *dev = pci_dev;

	rc = 0;
	while ((pci_dev = pci_device_next(pci_dev_iter))) {
		struct nvme_controller *ctrlr;
	printf("Attached to %04x:%02x:%02x.%02x\n",
	       spdk_pci_device_get_domain(dev),
	       spdk_pci_device_get_bus(dev),
	       spdk_pci_device_get_dev(dev),
	       spdk_pci_device_get_func(dev));

		if (pci_device_has_non_uio_driver(pci_dev)) {
			fprintf(stderr, "non-uio kernel driver attached to nvme\n");
			fprintf(stderr, " controller at pci bdf %d:%d:%d\n",
				pci_dev->bus, pci_dev->dev, pci_dev->func);
			fprintf(stderr, " skipping...\n");
			continue;
	register_ctrlr(ctrlr);
}

		pci_device_probe(pci_dev);
static int
register_controllers(void)
{
	printf("Initializing NVMe Controllers\n");

		ctrlr = nvme_attach(pci_dev);
		if (ctrlr == NULL) {
			fprintf(stderr, "nvme_attach failed for controller at pci bdf %d:%d:%d\n",
				pci_dev->bus, pci_dev->dev, pci_dev->func);
			rc = 1;
			continue;
		}
	pci_system_init();

		register_ctrlr(ctrlr);
	if (nvme_probe(NULL, probe_cb, attach_cb) != 0) {
		fprintf(stderr, "nvme_probe() failed\n");
		return 1;
	}

	pci_iterator_destroy(pci_dev_iter);

	return rc;
	return 0;
}

static void
+33 −40
Original line number Diff line number Diff line
@@ -378,6 +378,36 @@ reserve_controller(struct nvme_controller *ctrlr, struct pci_device *pci_dev)
	reservation_ns_release(ctrlr, 1);
}

static bool
probe_cb(void *cb_ctx, void *pci_dev)
{
	struct pci_device *dev = pci_dev;

	if (pci_device_has_non_uio_driver(dev)) {
		fprintf(stderr, "non-uio kernel driver attached to NVMe\n");
		fprintf(stderr, " controller at PCI address %04x:%02x:%02x.%02x\n",
			spdk_pci_device_get_domain(dev),
			spdk_pci_device_get_bus(dev),
			spdk_pci_device_get_dev(dev),
			spdk_pci_device_get_func(dev));
		fprintf(stderr, " skipping...\n");
		return false;
	}

	return true;
}

static void
attach_cb(void *cb_ctx, void *pci_dev, struct nvme_controller *ctrlr)
{
	struct dev *dev;

	/* add to dev list */
	dev = &devs[num_devs++];
	dev->pci_dev = pci_dev;
	dev->ctrlr = ctrlr;
}

static const char *ealargs[] = {
	"reserve",
	"-c 0x1",
@@ -386,10 +416,7 @@ static const char *ealargs[] = {

int main(int argc, char **argv)
{
	struct pci_device_iterator	*pci_dev_iter;
	struct pci_device		*pci_dev;
	struct dev			*iter;
	struct pci_id_match		match;
	int				rc, i;

	rc = rte_eal_init(sizeof(ealargs) / sizeof(ealargs[0]),
@@ -412,45 +439,11 @@ int main(int argc, char **argv)

	pci_system_init();

	match.vendor_id =	PCI_MATCH_ANY;
	match.subvendor_id =	PCI_MATCH_ANY;
	match.subdevice_id =	PCI_MATCH_ANY;
	match.device_id =	PCI_MATCH_ANY;
	match.device_class =	NVME_CLASS_CODE;
	match.device_class_mask = 0xFFFFFF;

	pci_dev_iter = pci_id_match_iterator_create(&match);

	rc = 0;
	while ((pci_dev = pci_device_next(pci_dev_iter))) {
		struct nvme_controller *ctrlr;
		struct dev *dev;

		if (pci_device_has_non_uio_driver(pci_dev)) {
			fprintf(stderr, "non-uio kernel driver attached to nvme\n");
			fprintf(stderr, " controller at pci bdf %d:%d:%d\n",
				pci_dev->bus, pci_dev->dev, pci_dev->func);
			fprintf(stderr, " skipping...\n");
			continue;
	if (nvme_probe(NULL, probe_cb, attach_cb) != 0) {
		fprintf(stderr, "nvme_probe() failed\n");
		return 1;
	}

		pci_device_probe(pci_dev);

		ctrlr = nvme_attach(pci_dev);
		if (ctrlr == NULL) {
			fprintf(stderr, "failed to attach to NVMe controller at PCI BDF %d:%d:%d\n",
				pci_dev->bus, pci_dev->dev, pci_dev->func);
			rc = 1;
			continue;
		}
		/* add to dev list */
		dev = &devs[num_devs++];
		dev->pci_dev = pci_dev;
		dev->ctrlr = ctrlr;
	}

	pci_iterator_destroy(pci_dev_iter);

	if (num_devs) {
		rc = nvme_register_io_thread();
		if (rc != 0)
+58 −35
Original line number Diff line number Diff line
@@ -48,25 +48,39 @@ extern int32_t nvme_retry_count;
extern "C" {
#endif

/** \brief Opaque handle to a controller. Obtained by calling nvme_attach(). */
/** \brief Opaque handle to a controller. Returned by \ref nvme_probe()'s attach_cb. */
struct nvme_controller;

/**
 * \brief Attaches specified device to the NVMe driver.
 * Callback for nvme_probe() enumeration.
 *
 * On success, the nvme_controller handle is valid for other nvme_ctrlr_* functions.
 * On failure, the return value will be NULL.
 * \return true to attach to this device.
 */
typedef bool (*nvme_probe_cb)(void *cb_ctx, void *pci_dev);

/**
 * Callback for nvme_probe() to report a device that has been attached to the userspace NVMe driver.
 */
typedef void (*nvme_attach_cb)(void *cb_ctx, void *pci_dev, struct nvme_controller *ctrlr);

/**
 * \brief Enumerate the NVMe devices attached to the system and attach the userspace NVMe driver
 * to them if desired.
 *
 * This function should be called from a single thread while no other threads or drivers
 * are actively using the NVMe device.
 * \param probe_cb will be called once per NVMe device found in the system.
 * \param attach_cb will be called for devices for which probe_cb returned true once that NVMe
 * controller has been attached to the userspace driver.
 *
 * If called more than once, only devices that are not already attached to the SPDK NVMe driver
 * will be reported.
 *
 * To stop using the the controller and release its associated resources,
 * call \ref nvme_detach with the nvme_controller instance returned by this function.
 */
struct nvme_controller *nvme_attach(void *devhandle);
int nvme_probe(void *cb_ctx, nvme_probe_cb probe_cb, nvme_attach_cb attach_cb);

/**
 * \brief Detaches specified device returned by \ref nvme_attach() from the NVMe driver.
 * \brief Detaches specified device returned by \ref nvme_probe()'s attach_cb from the NVMe driver.
 *
 * On success, the nvme_controller handle is no longer valid.
 *
@@ -91,7 +105,8 @@ int nvme_ctrlr_reset(struct nvme_controller *ctrlr);
/**
 * \brief Get the identify controller data as defined by the NVMe specification.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 *
 */
const struct nvme_controller_data *nvme_ctrlr_get_data(struct nvme_controller *ctrlr);
@@ -99,7 +114,8 @@ const struct nvme_controller_data *nvme_ctrlr_get_data(struct nvme_controller *c
/**
 * \brief Get the number of namespaces for the given NVMe controller.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 *
 * This is equivalent to calling nvme_ctrlr_get_data() to get the
 * nvme_controller_data and then reading the nn field.
@@ -110,7 +126,8 @@ uint32_t nvme_ctrlr_get_num_ns(struct nvme_controller *ctrlr);
/**
 * \brief Determine if a particular log page is supported by the given NVMe controller.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 *
 * \sa nvme_ctrlr_cmd_get_log_page()
 */
@@ -119,7 +136,8 @@ bool nvme_ctrlr_is_log_page_supported(struct nvme_controller *ctrlr, uint8_t log
/**
 * \brief Determine if a particular feature is supported by the given NVMe controller.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 *
 * \sa nvme_ctrlr_cmd_get_feature()
 */
@@ -180,7 +198,8 @@ int nvme_ctrlr_cmd_io_raw(struct nvme_controller *ctrlr,
 *
 * \return Number of completions processed (may be 0) or negative on error.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 *
 */
int32_t nvme_ctrlr_process_io_completions(struct nvme_controller *ctrlr, uint32_t max_completions);
@@ -195,8 +214,8 @@ int32_t nvme_ctrlr_process_io_completions(struct nvme_controller *ctrlr, uint32_
 * When constructing the nvme_command it is not necessary to fill out the PRP
 * list/SGL or the CID. The driver will handle both of those for you.
 *
 * This function is thread safe and can be called at any point after
 * \ref nvme_attach().
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 *
 * Call \ref nvme_ctrlr_process_admin_completions() to poll for completion
 * of commands submitted through this function.
@@ -217,7 +236,8 @@ int nvme_ctrlr_cmd_admin_raw(struct nvme_controller *ctrlr,
 *
 * \return Number of completions processed (may be 0) or negative on error.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 */
int32_t nvme_ctrlr_process_admin_completions(struct nvme_controller *ctrlr);

@@ -232,8 +252,8 @@ struct nvme_namespace;
 * be any gaps in the numbering. The number of namespaces is obtained by calling
 * nvme_ctrlr_get_num_ns().
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 *
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 */
struct nvme_namespace *nvme_ctrlr_get_ns(struct nvme_controller *ctrlr, uint32_t ns_id);

@@ -249,7 +269,8 @@ struct nvme_namespace *nvme_ctrlr_get_ns(struct nvme_controller *ctrlr, uint32_t
 *
 * \return 0 if successfully submitted, ENOMEM if resources could not be allocated for this request
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 *
 * Call \ref nvme_ctrlr_process_admin_completions() to poll for completion
 * of commands submitted through this function.
@@ -274,7 +295,8 @@ int nvme_ctrlr_cmd_get_log_page(struct nvme_controller *ctrlr,
 *
 * \return 0 if successfully submitted, ENOMEM if resources could not be allocated for this request
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 *
 * Call \ref nvme_ctrlr_process_admin_completions() to poll for completion
 * of commands submitted through this function.
@@ -298,7 +320,8 @@ int nvme_ctrlr_cmd_set_feature(struct nvme_controller *ctrlr,
 *
 * \return 0 if successfully submitted, ENOMEM if resources could not be allocated for this request
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 *
 * Call \ref nvme_ctrlr_process_admin_completions() to poll for completion
 * of commands submitted through this function.
@@ -313,48 +336,48 @@ int nvme_ctrlr_cmd_get_feature(struct nvme_controller *ctrlr,
/**
 * \brief Get the identify namespace data as defined by the NVMe specification.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 *
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 */
const struct nvme_namespace_data *nvme_ns_get_data(struct nvme_namespace *ns);

/**
 * \brief Get the namespace id (index number) from the given namespace handle.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 *
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 */
uint32_t nvme_ns_get_id(struct nvme_namespace *ns);

/**
 * \brief Get the maximum transfer size, in bytes, for an I/O sent to the given namespace.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 *
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 */
uint32_t nvme_ns_get_max_io_xfer_size(struct nvme_namespace *ns);

/**
 * \brief Get the sector size, in bytes, of the given namespace.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 *
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 */
uint32_t nvme_ns_get_sector_size(struct nvme_namespace *ns);

/**
 * \brief Get the number of sectors for the given namespace.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 *
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 */
uint64_t nvme_ns_get_num_sectors(struct nvme_namespace *ns);

/**
 * \brief Get the size, in bytes, of the given namespace.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 *
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 */
uint64_t nvme_ns_get_size(struct nvme_namespace *ns);

@@ -373,8 +396,8 @@ enum nvme_namespace_flags {
 *
 * See nvme_namespace_flags for the possible flags returned.
 *
 * This function is thread safe and can be called at any point after nvme_attach().
 *
 * This function is thread safe and can be called at any point while the controller is attached to
 *  the SPDK NVMe driver.
 */
uint32_t nvme_ns_get_flags(struct nvme_namespace *ns);

Loading