Commit 9bfaa6b2 authored by Dariusz Stojaczyk's avatar Dariusz Stojaczyk Committed by Ben Walker
Browse files

bdev/virtio: implement eventq / hot-detach



Eventq is a special virtqueue. The driver
(e.g. SPDK virtio) enqueues some fixed
amount of write-only requests. The host
device completes them only when it has
something to tell us - like a device
hotremove. After we receive the response,
containing full event details, we schedule
proper action and re-enqueue the event to
be used later by the host as another event.

Change-Id: I0516b161d8d4a49490b909fa2a454c9c9fa517f2
Signed-off-by: default avatarDariusz Stojaczyk <dariuszx.stojaczyk@intel.com>
Reviewed-on: https://review.gerrithub.io/390115


Reviewed-by: default avatarDaniel Verkamp <daniel.verkamp@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Tested-by: default avatarSPDK Automated Test System <sys_sgsw@intel.com>
parent a1ca7a12
Loading
Loading
Loading
Loading
+114 −10
Original line number Diff line number Diff line
@@ -60,6 +60,8 @@
/* Number of non-request queues - eventq and controlq */
#define SPDK_VIRTIO_SCSI_QUEUE_NUM_FIXED 2

#define VIRTIO_SCSI_EVENTQ_BUFFER_COUNT 16

#define VIRTIO_SCSI_CONTROLQ	0
#define VIRTIO_SCSI_EVENTQ	1
#define VIRTIO_SCSI_REQUESTQ	2
@@ -83,6 +85,9 @@ struct virtio_scsi_dev {
	/** Controlq messages to be sent. */
	struct spdk_ring		*ctrlq_ring;

	/** Buffers for the eventq. */
	struct virtio_scsi_eventq_io	*eventq_ios;

	/** Device marked for removal. */
	bool removed;
};
@@ -100,6 +105,11 @@ struct virtio_scsi_io_ctx {
	};
};

struct virtio_scsi_eventq_io {
	struct iovec			iov;
	struct virtio_scsi_event	ev;
};

struct virtio_scsi_scan_info {
	uint64_t			num_blocks;
	uint32_t			block_size;
@@ -155,20 +165,40 @@ static bool g_bdev_virtio_finish = false;

/* Features desired/implemented by this driver. */
#define VIRTIO_SCSI_DEV_SUPPORTED_FEATURES		\
	(1ULL << VIRTIO_SCSI_F_INOUT)
	(1ULL << VIRTIO_SCSI_F_INOUT		|	\
	 1ULL << VIRTIO_SCSI_F_HOTPLUG)

static void virtio_scsi_dev_unregister_cb(void *io_device);
static void virtio_scsi_dev_remove(struct virtio_scsi_dev *svdev);
static int bdev_virtio_scsi_ch_create_cb(void *io_device, void *ctx_buf);
static void bdev_virtio_scsi_ch_destroy_cb(void *io_device, void *ctx_buf);
static void process_scan_resp(struct virtio_scsi_scan_base *base);
static void bdev_virtio_ctrlq_poll(void *arg);
static void bdev_virtio_mgmt_poll(void *arg);

static int
virtio_scsi_dev_send_eventq_io(struct virtqueue *vq, struct virtio_scsi_eventq_io *io)
{
	int rc;

	rc = virtqueue_req_start(vq, io, 1);
	if (rc != 0) {
		return -1;
	}

	virtqueue_req_add_iovs(vq, &io->iov, 1, SPDK_VIRTIO_DESC_WR);
	virtqueue_req_flush(vq);

	return 0;
}

static int
virtio_scsi_dev_init(struct virtio_scsi_dev *svdev)
{
	struct virtio_dev *vdev = &svdev->vdev;
	struct spdk_ring *ctrlq_ring;
	struct virtio_scsi_eventq_io *eventq_io;
	struct virtqueue *eventq;
	uint16_t i, num_events;
	int rc;

	rc = virtio_dev_restart(vdev, VIRTIO_SCSI_DEV_SUPPORTED_FEATURES);
@@ -190,8 +220,37 @@ virtio_scsi_dev_init(struct virtio_scsi_dev *svdev)
		return -1;
	}

	rc = virtio_dev_acquire_queue(vdev, VIRTIO_SCSI_EVENTQ);
	if (rc != 0) {
		SPDK_ERRLOG("Failed to acquire the eventq.\n");
		virtio_dev_release_queue(vdev, VIRTIO_SCSI_CONTROLQ);
		spdk_ring_free(ctrlq_ring);
		return -1;
	}

	eventq = vdev->vqs[VIRTIO_SCSI_EVENTQ];
	num_events = spdk_min(eventq->vq_nentries, VIRTIO_SCSI_EVENTQ_BUFFER_COUNT);
	svdev->eventq_ios = spdk_dma_zmalloc(sizeof(*svdev->eventq_ios) * num_events,
					     0, NULL);
	if (svdev->eventq_ios == NULL) {
		SPDK_ERRLOG("cannot allocate memory for %"PRIu16" eventq buffers \n",
			    num_events);
		virtio_dev_release_queue(vdev, VIRTIO_SCSI_EVENTQ);
		virtio_dev_release_queue(vdev, VIRTIO_SCSI_CONTROLQ);
		spdk_ring_free(ctrlq_ring);
		return -1;
	}

	for (i = 0; i < num_events; i++) {
		eventq_io = &svdev->eventq_ios[i];
		eventq_io->iov.iov_base = &eventq_io->ev;
		eventq_io->iov.iov_len = sizeof(eventq_io->ev);
		virtio_scsi_dev_send_eventq_io(eventq, eventq_io);
	}

	svdev->ctrlq_ring = ctrlq_ring;
	svdev->mgmt_poller = spdk_poller_register(bdev_virtio_ctrlq_poll, svdev,

	svdev->mgmt_poller = spdk_poller_register(bdev_virtio_mgmt_poll, svdev,
			     MGMT_POLL_PERIOD_US);

	TAILQ_INIT(&svdev->luns);
@@ -283,6 +342,20 @@ virtio_user_scsi_dev_create(const char *name, const char *path,
	return svdev;
}

static struct virtio_scsi_disk *
virtio_scsi_dev_get_disk_by_id(struct virtio_scsi_dev *svdev, uint8_t target_id)
{
	struct virtio_scsi_disk *disk;

	TAILQ_FOREACH(disk, &svdev->luns, link) {
		if (disk->info.target == target_id) {
			return disk;
		}
	}

	return NULL;
}

static int _virtio_scsi_dev_scan_next(struct virtio_scsi_scan_base *base);

static int
@@ -651,6 +724,29 @@ bdev_virtio_tmf_cpl(struct spdk_bdev_io *bdev_io)
	spdk_thread_send_msg(spdk_bdev_io_get_thread(bdev_io), bdev_virtio_tmf_cpl_cb, bdev_io);
}

static void
bdev_virtio_eventq_io_cpl(struct virtio_scsi_dev *svdev, struct virtio_scsi_eventq_io *io)
{
	struct virtio_scsi_event *ev = &io->ev;
	struct virtio_scsi_disk *disk;

	if (ev->lun[0] != 1) {
		SPDK_WARNLOG("Received an event with invalid data layout.\n");
		goto out;
	}

	if (ev->event == VIRTIO_SCSI_T_TRANSPORT_RESET &&
	    ev->reason == VIRTIO_SCSI_EVT_RESET_REMOVED) {
		disk = virtio_scsi_dev_get_disk_by_id(svdev, ev->lun[1]);
		if (disk != NULL) {
			spdk_bdev_unregister(&disk->bdev, NULL, NULL);
		}
	}

out:
	virtio_scsi_dev_send_eventq_io(svdev->vdev.vqs[VIRTIO_SCSI_EVENTQ], io);
}

static void
bdev_virtio_tmf_abort_nomem_cb(void *ctx)
{
@@ -700,28 +796,34 @@ bdev_virtio_send_tmf_io(struct virtqueue *ctrlq, struct spdk_bdev_io *bdev_io)
}

static void
bdev_virtio_ctrlq_poll(void *arg)
bdev_virtio_mgmt_poll(void *arg)
{
	struct virtio_scsi_dev *svdev = arg;
	struct virtio_dev *vdev = &svdev->vdev;
	struct virtqueue *eventq = vdev->vqs[VIRTIO_SCSI_EVENTQ];
	struct virtqueue *ctrlq = vdev->vqs[VIRTIO_SCSI_CONTROLQ];
	struct spdk_ring *send_ring = svdev->ctrlq_ring;
	struct spdk_bdev_io *bdev_io[16];
	void *io[16];
	uint32_t io_len[16];
	uint16_t i, cnt;
	int rc;

	cnt = spdk_ring_dequeue(send_ring, (void **)bdev_io, SPDK_COUNTOF(bdev_io));
	cnt = spdk_ring_dequeue(send_ring, io, SPDK_COUNTOF(io));
	for (i = 0; i < cnt; ++i) {
		rc = bdev_virtio_send_tmf_io(ctrlq, bdev_io[i]);
		rc = bdev_virtio_send_tmf_io(ctrlq, io[i]);
		if (rc != 0) {
			bdev_virtio_tmf_abort(bdev_io[i], rc);
			bdev_virtio_tmf_abort(io[i], rc);
		}
	}

	cnt = virtio_recv_pkts(ctrlq, (void **)bdev_io, io_len, SPDK_COUNTOF(bdev_io));
	cnt = virtio_recv_pkts(ctrlq, io, io_len, SPDK_COUNTOF(io));
	for (i = 0; i < cnt; ++i) {
		bdev_virtio_tmf_cpl(io[i]);
	}

	cnt = virtio_recv_pkts(eventq, io, io_len, SPDK_COUNTOF(io));
	for (i = 0; i < cnt; ++i) {
		bdev_virtio_tmf_cpl(bdev_io[i]);
		bdev_virtio_eventq_io_cpl(svdev, io[i]);
	}
}

@@ -1462,10 +1564,12 @@ virtio_scsi_dev_unregister_cb(void *io_device)
	spdk_ring_free(svdev->ctrlq_ring);
	spdk_poller_unregister(&svdev->mgmt_poller);

	virtio_dev_release_queue(vdev, VIRTIO_SCSI_EVENTQ);
	virtio_dev_release_queue(vdev, VIRTIO_SCSI_CONTROLQ);

	virtio_dev_stop(vdev);
	virtio_dev_destruct(vdev);
	spdk_dma_free(svdev->eventq_ios);
	free(svdev);

	TAILQ_REMOVE(&g_virtio_driver.scsi_devs, vdev, tailq);