Commit 6de68644 authored by Ankit Kumar's avatar Ankit Kumar Committed by Konrad Sztyber
Browse files

nvme/poll_group: create and manage fd_group for nvme poll group



Create spdk_fd_group within spdk_nvme_poll_group, which manages interrupt
events for all the file descriptors of spdk_nvme_qpair that are part of
this poll group.

Two new APIs have been introduced to manage this fd_group

1). spdk_nvme_poll_group_get_fd()
Fetches the internal epoll file descriptor of the poll group.

2). spdk_nvme_poll_group_wait()
Collectively waits for interrupt events on all the I/O queue pair file
descriptors managed by the poll group.
When an interrupt event gets generated, it processes any outstanding
completions on the I/O queue pair with interrupts. These interrupt events
are registered at the the time of I/O queue pair creation.

The nvme_poll_group_connect_qpair() has been modified. Based on the poll
group interrupt support, this now registers an event source for the file
descriptor of queue pair to the internal epoll file descriptor of the
poll group.
Similarly, the nvme_poll_group_disconnect_qpair() unregisters the event
source for file descriptor of the queue pair from the internal epoll file
descriptor of the poll group.

Additional checks are in place to prevent mixing of interrupts enabled
and interrupts disabled I/O queue pairs. The poll group interrupt support
capability is set by the first I/O queue pair added to it.

Change-Id: If40f1ea82051ae598590f5a23ab9ed58bcb4af09
Signed-off-by: default avatarAnkit Kumar <ankit.kumar@samsung.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/25080


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
Community-CI: Mellanox Build Bot
Community-CI: Community CI Samsung <spdk.community.ci.samsung@gmail.com>
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Reviewed-by: default avatarJim Harris <jim.harris@samsung.com>
parent 1efa1b16
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -35,6 +35,17 @@ fill it out which can be used by the application to register interrupts on the q

Added `opts_size` in `spdk_nvme_io_qpair_opts` structure to align it with other opts structures.

`spdk_nvme_poll_group_create()` now creates a fd group to manage interrupt events.

Two new APIs have been added to manage interrupt events in poll group.

`spdk_nvme_poll_group_get_fd()` retrieves the internal epoll file descriptor of the poll group.

`spdk_nvme_poll_group_wait()` waits for interrupt events on all the I/O queue pair file descriptors
in a poll group. When an interrupt event gets generated, it processes any outstanding completions
on the I/O queue pair with interrupts. These interrupt events are registered at the the time of I/O
queue pair creation.

### nvmf

Added public API `spdk_nvmf_send_discovery_log_notice` to send discovery log page
+29 −0
Original line number Diff line number Diff line
@@ -3026,6 +3026,35 @@ int spdk_nvme_poll_group_add(struct spdk_nvme_poll_group *group, struct spdk_nvm
 */
int spdk_nvme_poll_group_remove(struct spdk_nvme_poll_group *group, struct spdk_nvme_qpair *qpair);

/**
 * Wait for interrupt events on file descriptors of all the qpairs in this poll group.
 *
 * In interrupt mode all the file descriptors of qpairs are registered to the fd group within the
 * poll group. This can collectively wait for interrupt events on all those file descriptors.
 *
 * the disconnected_qpair_cb will be called for all disconnected qpairs in the poll group
 * including qpairs which fail within the context of this call.
 * The user is responsible for trying to reconnect or destroy those qpairs.
 *
 * \param group The poll group on which to wait for interrupt events.
 * \param disconnected_qpair_cb A callback function of type spdk_nvme_disconnected_qpair_cb. Must
 * be non-NULL.
 *
 * \return number of events processed on success, -EINVAL if no disconnected_qpair_cb is passed, or
 * -errno in case of failure.
 */
int spdk_nvme_poll_group_wait(struct spdk_nvme_poll_group *group,
			      spdk_nvme_disconnected_qpair_cb disconnected_qpair_cb);

/**
 * Return the internal epoll file descriptor of this poll group.
 *
 * \param group The poll group for which epoll fd is requested.
 *
 * \return epoll fd for the poll group, -EINVAL if there is no fd group for this poll group.
 */
int spdk_nvme_poll_group_get_fd(struct spdk_nvme_poll_group *group);

/**
 * Destroy an empty poll group.
 *
+3 −0
Original line number Diff line number Diff line
@@ -537,6 +537,9 @@ struct spdk_nvme_poll_group {
	struct spdk_nvme_accel_fn_table			accel_fn_table;
	STAILQ_HEAD(, spdk_nvme_transport_poll_group)	tgroups;
	bool						in_process_completions;
	bool						enable_interrupts;
	bool						enable_interrupts_is_valid;
	struct spdk_fd_group				*fgrp;
};

struct spdk_nvme_transport_poll_group {
+127 −1
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ struct spdk_nvme_poll_group *
spdk_nvme_poll_group_create(void *ctx, struct spdk_nvme_accel_fn_table *table)
{
	struct spdk_nvme_poll_group *group;
	int rc;

	group = calloc(1, sizeof(*group));
	if (group == NULL) {
@@ -57,12 +58,36 @@ spdk_nvme_poll_group_create(void *ctx, struct spdk_nvme_accel_fn_table *table)
		return NULL;
	}

	/* If interrupt is enabled, this fd_group will be used to manage events triggerd on file
	 * descriptors of all the qpairs in this poll group */
	rc = spdk_fd_group_create(&group->fgrp);
	if (rc) {
		/* Ignore this for non-Linux platforms, as fd_groups aren't supported there. */
#if defined(__linux__)
		SPDK_ERRLOG("Cannot create fd group for the nvme poll group\n");
		free(group);
		return NULL;
#endif
	}

	group->ctx = ctx;
	STAILQ_INIT(&group->tgroups);

	return group;
}

int
spdk_nvme_poll_group_get_fd(struct spdk_nvme_poll_group *group)
{
	if (!group->fgrp) {
		SPDK_ERRLOG("No fd group present for the nvme poll group.\n");
		assert(false);
		return -EINVAL;
	}

	return spdk_fd_group_get_fd(group->fgrp);
}

struct spdk_nvme_poll_group *
spdk_nvme_qpair_get_optimal_poll_group(struct spdk_nvme_qpair *qpair)
{
@@ -87,6 +112,15 @@ spdk_nvme_poll_group_add(struct spdk_nvme_poll_group *group, struct spdk_nvme_qp
		return -EINVAL;
	}

	if (!group->enable_interrupts_is_valid) {
		group->enable_interrupts_is_valid = true;
		group->enable_interrupts = qpair->ctrlr->opts.enable_interrupts;
	} else if (qpair->ctrlr->opts.enable_interrupts != group->enable_interrupts) {
		SPDK_ERRLOG("Queue pair %s interrupts cannot be added to poll group\n",
			    qpair->ctrlr->opts.enable_interrupts ? "without" : "with");
		return -EINVAL;
	}

	STAILQ_FOREACH(tgroup, &group->tgroups, link) {
		if (tgroup->transport == qpair->transport) {
			break;
@@ -127,18 +161,106 @@ spdk_nvme_poll_group_remove(struct spdk_nvme_poll_group *group, struct spdk_nvme
	return -ENODEV;
}

static int
nvme_qpair_process_completion_wrapper(void *arg)
{
	struct spdk_nvme_qpair *qpair = arg;

	return spdk_nvme_qpair_process_completions(qpair, 0);
}

static int
nvme_poll_group_add_qpair_fd(struct spdk_nvme_qpair *qpair)
{
	struct spdk_nvme_poll_group *group;
	struct spdk_event_handler_opts opts = {
		.opts_size = SPDK_SIZEOF(&opts, fd_type),
	};
	int fd;

	group = qpair->poll_group->group;
	if (group->enable_interrupts == false) {
		return 0;
	}

	fd = spdk_nvme_qpair_get_fd(qpair, &opts);
	if (fd < 0) {
		SPDK_ERRLOG("Cannot get fd for the qpair: %d\n", fd);
		return -EINVAL;
	}

	return SPDK_FD_GROUP_ADD_EXT(group->fgrp, fd, nvme_qpair_process_completion_wrapper,
				     qpair, &opts);
}

static void
nvme_poll_group_remove_qpair_fd(struct spdk_nvme_qpair *qpair)
{
	struct spdk_nvme_poll_group *group;
	int fd;

	group = qpair->poll_group->group;
	if (group->enable_interrupts == false) {
		return;
	}

	fd = spdk_nvme_qpair_get_fd(qpair, NULL);
	if (fd < 0) {
		SPDK_ERRLOG("Cannot get fd for the qpair: %d\n", fd);
		assert(false);
		return;
	}

	spdk_fd_group_remove(group->fgrp, fd);
}

int
nvme_poll_group_connect_qpair(struct spdk_nvme_qpair *qpair)
{
	return nvme_transport_poll_group_connect_qpair(qpair);
	int rc;

	rc = nvme_transport_poll_group_connect_qpair(qpair);
	if (rc != 0) {
		return rc;
	}

	rc = nvme_poll_group_add_qpair_fd(qpair);
	if (rc != 0) {
		nvme_transport_poll_group_disconnect_qpair(qpair);
		return rc;
	}

	return 0;
}

int
nvme_poll_group_disconnect_qpair(struct spdk_nvme_qpair *qpair)
{
	nvme_poll_group_remove_qpair_fd(qpair);

	return nvme_transport_poll_group_disconnect_qpair(qpair);
}

int
spdk_nvme_poll_group_wait(struct spdk_nvme_poll_group *group,
			  spdk_nvme_disconnected_qpair_cb disconnected_qpair_cb)
{
	struct spdk_nvme_transport_poll_group *tgroup;
	int num_events, timeout = -1;

	if (disconnected_qpair_cb == NULL) {
		return -EINVAL;
	}

	STAILQ_FOREACH(tgroup, &group->tgroups, link) {
		nvme_transport_poll_group_check_disconnected_qpairs(tgroup, disconnected_qpair_cb);
	}

	num_events = spdk_fd_group_wait(group->fgrp, timeout);

	return num_events;
}

int64_t
spdk_nvme_poll_group_process_completions(struct spdk_nvme_poll_group *group,
		uint32_t completions_per_qpair, spdk_nvme_disconnected_qpair_cb disconnected_qpair_cb)
@@ -221,6 +343,10 @@ spdk_nvme_poll_group_destroy(struct spdk_nvme_poll_group *group)

	}

	if (group->fgrp) {
		spdk_fd_group_destroy(group->fgrp);
	}

	free(group);

	return 0;
+2 −0
Original line number Diff line number Diff line
@@ -133,6 +133,8 @@
	spdk_nvme_poll_group_process_completions;
	spdk_nvme_poll_group_all_connected;
	spdk_nvme_poll_group_get_ctx;
	spdk_nvme_poll_group_wait;
	spdk_nvme_poll_group_get_fd;

	spdk_nvme_ns_get_data;
	spdk_nvme_ns_get_id;
Loading