xref: /spdk/module/bdev/virtio/bdev_virtio_scsi.c (revision 186b109dd3a723612e3df79bb3d97699173d39e3)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse  *   Copyright (C) 2017 Intel Corporation.
307fe6a43SSeth Howell  *   All rights reserved.
407fe6a43SSeth Howell  */
507fe6a43SSeth Howell 
607fe6a43SSeth Howell #include "spdk/stdinc.h"
707fe6a43SSeth Howell 
807fe6a43SSeth Howell #include "spdk/bdev.h"
907fe6a43SSeth Howell #include "spdk/endian.h"
1007fe6a43SSeth Howell #include "spdk/env.h"
1107fe6a43SSeth Howell #include "spdk/thread.h"
1207fe6a43SSeth Howell #include "spdk/scsi_spec.h"
1307fe6a43SSeth Howell #include "spdk/string.h"
1407fe6a43SSeth Howell #include "spdk/util.h"
1507fe6a43SSeth Howell #include "spdk/json.h"
1607fe6a43SSeth Howell 
1707fe6a43SSeth Howell #include "spdk/bdev_module.h"
184e8e97c8STomasz Zawadzki #include "spdk/log.h"
1907fe6a43SSeth Howell #include "spdk_internal/virtio.h"
20e7379fc2SChangpeng Liu #include "spdk_internal/vhost_user.h"
2107fe6a43SSeth Howell 
2207fe6a43SSeth Howell #include <linux/virtio_scsi.h>
234c890c31SJin Yu #include <linux/virtio_ids.h>
2407fe6a43SSeth Howell 
2507fe6a43SSeth Howell #include "bdev_virtio.h"
2607fe6a43SSeth Howell 
2707fe6a43SSeth Howell #define BDEV_VIRTIO_MAX_TARGET 64
2807fe6a43SSeth Howell #define BDEV_VIRTIO_SCAN_PAYLOAD_SIZE 256
2907fe6a43SSeth Howell #define MGMT_POLL_PERIOD_US (1000 * 5)
3007fe6a43SSeth Howell #define CTRLQ_RING_SIZE 16
3107fe6a43SSeth Howell #define SCAN_REQUEST_RETRIES 5
3207fe6a43SSeth Howell 
3307fe6a43SSeth Howell /* Number of non-request queues - eventq and controlq */
3407fe6a43SSeth Howell #define SPDK_VIRTIO_SCSI_QUEUE_NUM_FIXED 2
3507fe6a43SSeth Howell 
3607fe6a43SSeth Howell #define VIRTIO_SCSI_EVENTQ_BUFFER_COUNT 16
3707fe6a43SSeth Howell 
3807fe6a43SSeth Howell #define VIRTIO_SCSI_CONTROLQ	0
3907fe6a43SSeth Howell #define VIRTIO_SCSI_EVENTQ	1
4007fe6a43SSeth Howell #define VIRTIO_SCSI_REQUESTQ	2
4107fe6a43SSeth Howell 
4207fe6a43SSeth Howell static int bdev_virtio_initialize(void);
4307fe6a43SSeth Howell static void bdev_virtio_finish(void);
4407fe6a43SSeth Howell 
4507fe6a43SSeth Howell struct virtio_scsi_dev {
4607fe6a43SSeth Howell 	/* Generic virtio device data. */
4707fe6a43SSeth Howell 	struct virtio_dev		vdev;
4807fe6a43SSeth Howell 
4907fe6a43SSeth Howell 	/** Detected SCSI LUNs */
5007fe6a43SSeth Howell 	TAILQ_HEAD(, virtio_scsi_disk)	luns;
5107fe6a43SSeth Howell 
5207fe6a43SSeth Howell 	/** Context for the SCSI target scan. */
5307fe6a43SSeth Howell 	struct virtio_scsi_scan_base	*scan_ctx;
5407fe6a43SSeth Howell 
5507fe6a43SSeth Howell 	/** Controlq poller. */
5607fe6a43SSeth Howell 	struct spdk_poller		*mgmt_poller;
5707fe6a43SSeth Howell 
5807fe6a43SSeth Howell 	/** Controlq messages to be sent. */
5907fe6a43SSeth Howell 	struct spdk_ring		*ctrlq_ring;
6007fe6a43SSeth Howell 
6107fe6a43SSeth Howell 	/** Buffers for the eventq. */
6207fe6a43SSeth Howell 	struct virtio_scsi_eventq_io	*eventq_ios;
6307fe6a43SSeth Howell 
6407fe6a43SSeth Howell 	/** Device marked for removal. */
6507fe6a43SSeth Howell 	bool				removed;
6607fe6a43SSeth Howell 
6707fe6a43SSeth Howell 	/** Callback to be called after vdev removal. */
6807fe6a43SSeth Howell 	bdev_virtio_remove_cb		remove_cb;
6907fe6a43SSeth Howell 
7007fe6a43SSeth Howell 	/** Context for the `remove_cb`. */
7107fe6a43SSeth Howell 	void				*remove_ctx;
7207fe6a43SSeth Howell 
7307fe6a43SSeth Howell 	TAILQ_ENTRY(virtio_scsi_dev) tailq;
7407fe6a43SSeth Howell };
7507fe6a43SSeth Howell 
7607fe6a43SSeth Howell struct virtio_scsi_io_ctx {
7707fe6a43SSeth Howell 	struct iovec			iov_req;
7807fe6a43SSeth Howell 	struct iovec			iov_resp;
7907fe6a43SSeth Howell 	union {
8007fe6a43SSeth Howell 		struct virtio_scsi_cmd_req req;
8107fe6a43SSeth Howell 		struct virtio_scsi_ctrl_tmf_req tmf_req;
8207fe6a43SSeth Howell 	};
8307fe6a43SSeth Howell 	union {
8407fe6a43SSeth Howell 		struct virtio_scsi_cmd_resp resp;
8507fe6a43SSeth Howell 		struct virtio_scsi_ctrl_tmf_resp tmf_resp;
8607fe6a43SSeth Howell 	};
8707fe6a43SSeth Howell };
8807fe6a43SSeth Howell 
8907fe6a43SSeth Howell struct virtio_scsi_eventq_io {
9007fe6a43SSeth Howell 	struct iovec			iov;
9107fe6a43SSeth Howell 	struct virtio_scsi_event	ev;
9207fe6a43SSeth Howell };
9307fe6a43SSeth Howell 
9407fe6a43SSeth Howell struct virtio_scsi_scan_info {
9507fe6a43SSeth Howell 	uint64_t			num_blocks;
9607fe6a43SSeth Howell 	uint32_t			block_size;
9707fe6a43SSeth Howell 	uint8_t				target;
9807fe6a43SSeth Howell 	bool				unmap_supported;
9907fe6a43SSeth Howell 	TAILQ_ENTRY(virtio_scsi_scan_info) tailq;
10007fe6a43SSeth Howell };
10107fe6a43SSeth Howell 
10207fe6a43SSeth Howell struct virtio_scsi_scan_base {
10307fe6a43SSeth Howell 	struct virtio_scsi_dev		*svdev;
10407fe6a43SSeth Howell 
10507fe6a43SSeth Howell 	/** I/O channel used for the scan I/O. */
10607fe6a43SSeth Howell 	struct bdev_virtio_io_channel	*channel;
10707fe6a43SSeth Howell 
10807fe6a43SSeth Howell 	bdev_virtio_create_cb		cb_fn;
10907fe6a43SSeth Howell 	void				*cb_arg;
11007fe6a43SSeth Howell 
11107fe6a43SSeth Howell 	/** Scan all targets on the device. */
11207fe6a43SSeth Howell 	bool				full_scan;
11307fe6a43SSeth Howell 
11407fe6a43SSeth Howell 	/** Start a full rescan after receiving next scan I/O response. */
11507fe6a43SSeth Howell 	bool				restart;
11607fe6a43SSeth Howell 
11707fe6a43SSeth Howell 	/** Additional targets to be (re)scanned. */
11807fe6a43SSeth Howell 	TAILQ_HEAD(, virtio_scsi_scan_info) scan_queue;
11907fe6a43SSeth Howell 
12007fe6a43SSeth Howell 	/** Remaining attempts for sending the current request. */
12107fe6a43SSeth Howell 	unsigned                        retries;
12207fe6a43SSeth Howell 
12307fe6a43SSeth Howell 	/** If set, the last scan I/O needs to be resent */
12407fe6a43SSeth Howell 	bool				needs_resend;
12507fe6a43SSeth Howell 
12607fe6a43SSeth Howell 	struct virtio_scsi_io_ctx	io_ctx;
12707fe6a43SSeth Howell 	struct iovec			iov;
12807fe6a43SSeth Howell 	uint8_t				payload[BDEV_VIRTIO_SCAN_PAYLOAD_SIZE];
12907fe6a43SSeth Howell 
13007fe6a43SSeth Howell 	/** Scan results for the current target. */
13107fe6a43SSeth Howell 	struct virtio_scsi_scan_info	info;
13207fe6a43SSeth Howell };
13307fe6a43SSeth Howell 
13407fe6a43SSeth Howell struct virtio_scsi_disk {
13507fe6a43SSeth Howell 	struct spdk_bdev		bdev;
13607fe6a43SSeth Howell 	struct virtio_scsi_dev		*svdev;
13707fe6a43SSeth Howell 	struct virtio_scsi_scan_info	info;
13807fe6a43SSeth Howell 
13907fe6a43SSeth Howell 	/** Descriptor opened just to be notified of external bdev hotremove. */
14007fe6a43SSeth Howell 	struct spdk_bdev_desc		*notify_desc;
14107fe6a43SSeth Howell 
14207fe6a43SSeth Howell 	/** Disk marked for removal. */
14307fe6a43SSeth Howell 	bool				removed;
14407fe6a43SSeth Howell 	TAILQ_ENTRY(virtio_scsi_disk)	link;
14507fe6a43SSeth Howell };
14607fe6a43SSeth Howell 
14707fe6a43SSeth Howell struct bdev_virtio_io_channel {
14807fe6a43SSeth Howell 	struct virtio_scsi_dev	*svdev;
14907fe6a43SSeth Howell 
15007fe6a43SSeth Howell 	/** Virtqueue exclusively assigned to this channel. */
15107fe6a43SSeth Howell 	struct virtqueue	*vq;
15207fe6a43SSeth Howell 
15307fe6a43SSeth Howell 	/** Virtio response poller. */
15407fe6a43SSeth Howell 	struct spdk_poller	*poller;
15507fe6a43SSeth Howell };
15607fe6a43SSeth Howell 
15707fe6a43SSeth Howell static TAILQ_HEAD(, virtio_scsi_dev) g_virtio_scsi_devs =
15807fe6a43SSeth Howell 	TAILQ_HEAD_INITIALIZER(g_virtio_scsi_devs);
15907fe6a43SSeth Howell 
16007fe6a43SSeth Howell static pthread_mutex_t g_virtio_scsi_mutex = PTHREAD_MUTEX_INITIALIZER;
16107fe6a43SSeth Howell 
16207fe6a43SSeth Howell /** Module finish in progress */
16307fe6a43SSeth Howell static bool g_bdev_virtio_finish = false;
16407fe6a43SSeth Howell 
16507fe6a43SSeth Howell /* Features desired/implemented by this driver. */
16607fe6a43SSeth Howell #define VIRTIO_SCSI_DEV_SUPPORTED_FEATURES		\
16707fe6a43SSeth Howell 	(1ULL << VIRTIO_SCSI_F_INOUT		|	\
16807fe6a43SSeth Howell 	 1ULL << VIRTIO_SCSI_F_HOTPLUG		|	\
169515d028eSChangpeng Liu 	 1ULL << VIRTIO_RING_F_EVENT_IDX)
17007fe6a43SSeth Howell 
17107fe6a43SSeth Howell static void virtio_scsi_dev_unregister_cb(void *io_device);
17207fe6a43SSeth Howell static void virtio_scsi_dev_remove(struct virtio_scsi_dev *svdev,
17307fe6a43SSeth Howell 				   bdev_virtio_remove_cb cb_fn, void *cb_arg);
17407fe6a43SSeth Howell static int bdev_virtio_scsi_ch_create_cb(void *io_device, void *ctx_buf);
17507fe6a43SSeth Howell static void bdev_virtio_scsi_ch_destroy_cb(void *io_device, void *ctx_buf);
17607fe6a43SSeth Howell static void process_scan_resp(struct virtio_scsi_scan_base *base);
17707fe6a43SSeth Howell static int bdev_virtio_mgmt_poll(void *arg);
17807fe6a43SSeth Howell 
17907fe6a43SSeth Howell static int
18007fe6a43SSeth Howell virtio_scsi_dev_send_eventq_io(struct virtqueue *vq, struct virtio_scsi_eventq_io *io)
18107fe6a43SSeth Howell {
18207fe6a43SSeth Howell 	int rc;
18307fe6a43SSeth Howell 
18407fe6a43SSeth Howell 	rc = virtqueue_req_start(vq, io, 1);
18507fe6a43SSeth Howell 	if (rc != 0) {
18607fe6a43SSeth Howell 		return -1;
18707fe6a43SSeth Howell 	}
18807fe6a43SSeth Howell 
18907fe6a43SSeth Howell 	virtqueue_req_add_iovs(vq, &io->iov, 1, SPDK_VIRTIO_DESC_WR);
19007fe6a43SSeth Howell 	virtqueue_req_flush(vq);
19107fe6a43SSeth Howell 
19207fe6a43SSeth Howell 	return 0;
19307fe6a43SSeth Howell }
19407fe6a43SSeth Howell 
19507fe6a43SSeth Howell static int
196515d028eSChangpeng Liu virtio_scsi_dev_init(struct virtio_scsi_dev *svdev, uint16_t max_queues, uint64_t feature_bits)
19707fe6a43SSeth Howell {
19807fe6a43SSeth Howell 	struct virtio_dev *vdev = &svdev->vdev;
19907fe6a43SSeth Howell 	struct spdk_ring *ctrlq_ring;
20007fe6a43SSeth Howell 	struct virtio_scsi_eventq_io *eventq_io;
20107fe6a43SSeth Howell 	struct virtqueue *eventq;
20207fe6a43SSeth Howell 	uint16_t i, num_events;
20307fe6a43SSeth Howell 	int rc;
20407fe6a43SSeth Howell 
205515d028eSChangpeng Liu 	rc = virtio_dev_reset(vdev, feature_bits);
20607fe6a43SSeth Howell 	if (rc != 0) {
20707fe6a43SSeth Howell 		return rc;
20807fe6a43SSeth Howell 	}
20907fe6a43SSeth Howell 
21007fe6a43SSeth Howell 	rc = virtio_dev_start(vdev, max_queues, SPDK_VIRTIO_SCSI_QUEUE_NUM_FIXED);
21107fe6a43SSeth Howell 	if (rc != 0) {
21207fe6a43SSeth Howell 		return rc;
21307fe6a43SSeth Howell 	}
21407fe6a43SSeth Howell 
21507fe6a43SSeth Howell 	ctrlq_ring = spdk_ring_create(SPDK_RING_TYPE_MP_SC, CTRLQ_RING_SIZE,
216*186b109dSJim Harris 				      SPDK_ENV_NUMA_ID_ANY);
21707fe6a43SSeth Howell 	if (ctrlq_ring == NULL) {
21807fe6a43SSeth Howell 		SPDK_ERRLOG("Failed to allocate send ring for the controlq.\n");
21907fe6a43SSeth Howell 		return -1;
22007fe6a43SSeth Howell 	}
22107fe6a43SSeth Howell 
22207fe6a43SSeth Howell 	rc = virtio_dev_acquire_queue(vdev, VIRTIO_SCSI_CONTROLQ);
22307fe6a43SSeth Howell 	if (rc != 0) {
22407fe6a43SSeth Howell 		SPDK_ERRLOG("Failed to acquire the controlq.\n");
22507fe6a43SSeth Howell 		spdk_ring_free(ctrlq_ring);
22607fe6a43SSeth Howell 		return -1;
22707fe6a43SSeth Howell 	}
22807fe6a43SSeth Howell 
22907fe6a43SSeth Howell 	rc = virtio_dev_acquire_queue(vdev, VIRTIO_SCSI_EVENTQ);
23007fe6a43SSeth Howell 	if (rc != 0) {
23107fe6a43SSeth Howell 		SPDK_ERRLOG("Failed to acquire the eventq.\n");
23207fe6a43SSeth Howell 		virtio_dev_release_queue(vdev, VIRTIO_SCSI_CONTROLQ);
23307fe6a43SSeth Howell 		spdk_ring_free(ctrlq_ring);
23407fe6a43SSeth Howell 		return -1;
23507fe6a43SSeth Howell 	}
23607fe6a43SSeth Howell 
23707fe6a43SSeth Howell 	eventq = vdev->vqs[VIRTIO_SCSI_EVENTQ];
23807fe6a43SSeth Howell 	num_events = spdk_min(eventq->vq_nentries, VIRTIO_SCSI_EVENTQ_BUFFER_COUNT);
23907fe6a43SSeth Howell 	svdev->eventq_ios = spdk_zmalloc(sizeof(*svdev->eventq_ios) * num_events,
24007fe6a43SSeth Howell 					 0, NULL, SPDK_ENV_LCORE_ID_ANY,
24107fe6a43SSeth Howell 					 SPDK_MALLOC_DMA);
24207fe6a43SSeth Howell 	if (svdev->eventq_ios == NULL) {
24307fe6a43SSeth Howell 		SPDK_ERRLOG("cannot allocate memory for %"PRIu16" eventq buffers\n",
24407fe6a43SSeth Howell 			    num_events);
24507fe6a43SSeth Howell 		virtio_dev_release_queue(vdev, VIRTIO_SCSI_EVENTQ);
24607fe6a43SSeth Howell 		virtio_dev_release_queue(vdev, VIRTIO_SCSI_CONTROLQ);
24707fe6a43SSeth Howell 		spdk_ring_free(ctrlq_ring);
24807fe6a43SSeth Howell 		return -1;
24907fe6a43SSeth Howell 	}
25007fe6a43SSeth Howell 
25107fe6a43SSeth Howell 	for (i = 0; i < num_events; i++) {
25207fe6a43SSeth Howell 		eventq_io = &svdev->eventq_ios[i];
25307fe6a43SSeth Howell 		eventq_io->iov.iov_base = &eventq_io->ev;
25407fe6a43SSeth Howell 		eventq_io->iov.iov_len = sizeof(eventq_io->ev);
25507fe6a43SSeth Howell 		virtio_scsi_dev_send_eventq_io(eventq, eventq_io);
25607fe6a43SSeth Howell 	}
25707fe6a43SSeth Howell 
25807fe6a43SSeth Howell 	svdev->ctrlq_ring = ctrlq_ring;
25907fe6a43SSeth Howell 
260ab0bc5c2SShuhei Matsumoto 	svdev->mgmt_poller = SPDK_POLLER_REGISTER(bdev_virtio_mgmt_poll, svdev,
26107fe6a43SSeth Howell 			     MGMT_POLL_PERIOD_US);
26207fe6a43SSeth Howell 
26307fe6a43SSeth Howell 	TAILQ_INIT(&svdev->luns);
26407fe6a43SSeth Howell 	svdev->scan_ctx = NULL;
26507fe6a43SSeth Howell 	svdev->removed = false;
26607fe6a43SSeth Howell 	svdev->remove_cb = NULL;
26707fe6a43SSeth Howell 	svdev->remove_ctx = NULL;
26807fe6a43SSeth Howell 
26907fe6a43SSeth Howell 	spdk_io_device_register(svdev, bdev_virtio_scsi_ch_create_cb,
27007fe6a43SSeth Howell 				bdev_virtio_scsi_ch_destroy_cb,
27107fe6a43SSeth Howell 				sizeof(struct bdev_virtio_io_channel),
27207fe6a43SSeth Howell 				svdev->vdev.name);
27307fe6a43SSeth Howell 
27407fe6a43SSeth Howell 	pthread_mutex_lock(&g_virtio_scsi_mutex);
27507fe6a43SSeth Howell 	TAILQ_INSERT_TAIL(&g_virtio_scsi_devs, svdev, tailq);
27607fe6a43SSeth Howell 	pthread_mutex_unlock(&g_virtio_scsi_mutex);
27707fe6a43SSeth Howell 	return 0;
27807fe6a43SSeth Howell }
27907fe6a43SSeth Howell 
28007fe6a43SSeth Howell static struct virtio_scsi_dev *
28107fe6a43SSeth Howell virtio_pci_scsi_dev_create(const char *name, struct virtio_pci_ctx *pci_ctx)
28207fe6a43SSeth Howell {
28307fe6a43SSeth Howell 	static int pci_dev_counter = 0;
28407fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev;
28507fe6a43SSeth Howell 	struct virtio_dev *vdev;
28607fe6a43SSeth Howell 	char *default_name = NULL;
28707fe6a43SSeth Howell 	uint32_t num_queues;
28807fe6a43SSeth Howell 	int rc;
28907fe6a43SSeth Howell 
29007fe6a43SSeth Howell 	svdev = calloc(1, sizeof(*svdev));
29107fe6a43SSeth Howell 	if (svdev == NULL) {
29207fe6a43SSeth Howell 		SPDK_ERRLOG("virtio device calloc failed\n");
29307fe6a43SSeth Howell 		return NULL;
29407fe6a43SSeth Howell 	}
29507fe6a43SSeth Howell 
29607fe6a43SSeth Howell 	vdev = &svdev->vdev;
29707fe6a43SSeth Howell 	if (name == NULL) {
29807fe6a43SSeth Howell 		default_name = spdk_sprintf_alloc("VirtioScsi%"PRIu32, pci_dev_counter++);
29907fe6a43SSeth Howell 		if (default_name == NULL) {
30007fe6a43SSeth Howell 			free(vdev);
30107fe6a43SSeth Howell 			return NULL;
30207fe6a43SSeth Howell 		}
30307fe6a43SSeth Howell 		name = default_name;
30407fe6a43SSeth Howell 	}
30507fe6a43SSeth Howell 
30607fe6a43SSeth Howell 	rc = virtio_pci_dev_init(vdev, name, pci_ctx);
30707fe6a43SSeth Howell 	free(default_name);
30807fe6a43SSeth Howell 
30907fe6a43SSeth Howell 	if (rc != 0) {
31007fe6a43SSeth Howell 		free(svdev);
31107fe6a43SSeth Howell 		return NULL;
31207fe6a43SSeth Howell 	}
31307fe6a43SSeth Howell 
31407fe6a43SSeth Howell 	rc = virtio_dev_read_dev_config(vdev, offsetof(struct virtio_scsi_config, num_queues),
31507fe6a43SSeth Howell 					&num_queues, sizeof(num_queues));
31607fe6a43SSeth Howell 	if (rc) {
31707fe6a43SSeth Howell 		SPDK_ERRLOG("%s: config read failed: %s\n", vdev->name, spdk_strerror(-rc));
3187ef6d8ddSJin Yu 		goto fail;
31907fe6a43SSeth Howell 	}
32007fe6a43SSeth Howell 
321515d028eSChangpeng Liu 	rc = virtio_scsi_dev_init(svdev, num_queues, VIRTIO_SCSI_DEV_SUPPORTED_FEATURES);
32207fe6a43SSeth Howell 	if (rc != 0) {
3237ef6d8ddSJin Yu 		goto fail;
32407fe6a43SSeth Howell 	}
32507fe6a43SSeth Howell 
32607fe6a43SSeth Howell 	return svdev;
3277ef6d8ddSJin Yu 
3287ef6d8ddSJin Yu fail:
3297ef6d8ddSJin Yu 	vdev->ctx = NULL;
3307ef6d8ddSJin Yu 	virtio_dev_destruct(vdev);
3317ef6d8ddSJin Yu 	free(svdev);
3327ef6d8ddSJin Yu 	return NULL;
33307fe6a43SSeth Howell }
33407fe6a43SSeth Howell 
33507fe6a43SSeth Howell static struct virtio_scsi_dev *
33607fe6a43SSeth Howell virtio_user_scsi_dev_create(const char *name, const char *path,
33707fe6a43SSeth Howell 			    uint16_t num_queues, uint32_t queue_size)
33807fe6a43SSeth Howell {
33907fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev;
34007fe6a43SSeth Howell 	struct virtio_dev *vdev;
341515d028eSChangpeng Liu 	uint64_t feature_bits;
34207fe6a43SSeth Howell 	int rc;
34307fe6a43SSeth Howell 
34407fe6a43SSeth Howell 	svdev = calloc(1, sizeof(*svdev));
34507fe6a43SSeth Howell 	if (svdev == NULL) {
34607fe6a43SSeth Howell 		SPDK_ERRLOG("calloc failed for virtio device %s: %s\n", name, path);
34707fe6a43SSeth Howell 		return NULL;
34807fe6a43SSeth Howell 	}
34907fe6a43SSeth Howell 
35007fe6a43SSeth Howell 	vdev = &svdev->vdev;
35107fe6a43SSeth Howell 	rc = virtio_user_dev_init(vdev, name, path, queue_size);
35207fe6a43SSeth Howell 	if (rc != 0) {
35307fe6a43SSeth Howell 		SPDK_ERRLOG("Failed to create virito device %s: %s\n", name, path);
35407fe6a43SSeth Howell 		free(svdev);
35507fe6a43SSeth Howell 		return NULL;
35607fe6a43SSeth Howell 	}
35707fe6a43SSeth Howell 
358515d028eSChangpeng Liu 	feature_bits = VIRTIO_SCSI_DEV_SUPPORTED_FEATURES;
359515d028eSChangpeng Liu 	feature_bits |= (1ULL << VHOST_USER_F_PROTOCOL_FEATURES);
360a02483e6SChangpeng Liu 	rc = virtio_scsi_dev_init(svdev, num_queues + SPDK_VIRTIO_SCSI_QUEUE_NUM_FIXED, feature_bits);
36107fe6a43SSeth Howell 	if (rc != 0) {
36207fe6a43SSeth Howell 		virtio_dev_destruct(vdev);
36307fe6a43SSeth Howell 		free(svdev);
36407fe6a43SSeth Howell 		return NULL;
36507fe6a43SSeth Howell 	}
36607fe6a43SSeth Howell 
36707fe6a43SSeth Howell 	return svdev;
36807fe6a43SSeth Howell }
36907fe6a43SSeth Howell 
37007fe6a43SSeth Howell static struct virtio_scsi_disk *
37107fe6a43SSeth Howell virtio_scsi_dev_get_disk_by_id(struct virtio_scsi_dev *svdev, uint8_t target_id)
37207fe6a43SSeth Howell {
37307fe6a43SSeth Howell 	struct virtio_scsi_disk *disk;
37407fe6a43SSeth Howell 
37507fe6a43SSeth Howell 	TAILQ_FOREACH(disk, &svdev->luns, link) {
37607fe6a43SSeth Howell 		if (disk->info.target == target_id) {
37707fe6a43SSeth Howell 			return disk;
37807fe6a43SSeth Howell 		}
37907fe6a43SSeth Howell 	}
38007fe6a43SSeth Howell 
38107fe6a43SSeth Howell 	return NULL;
38207fe6a43SSeth Howell }
38307fe6a43SSeth Howell 
38407fe6a43SSeth Howell static int virtio_scsi_dev_scan(struct virtio_scsi_dev *svdev,
38507fe6a43SSeth Howell 				bdev_virtio_create_cb cb_fn, void *cb_arg);
38607fe6a43SSeth Howell static int send_scan_io(struct virtio_scsi_scan_base *base);
38707fe6a43SSeth Howell static void _virtio_scsi_dev_scan_tgt(struct virtio_scsi_scan_base *base, uint8_t target);
38807fe6a43SSeth Howell static int _virtio_scsi_dev_scan_next(struct virtio_scsi_scan_base *base, int rc);
38907fe6a43SSeth Howell static void _virtio_scsi_dev_scan_finish(struct virtio_scsi_scan_base *base, int errnum);
39007fe6a43SSeth Howell static int virtio_scsi_dev_scan_tgt(struct virtio_scsi_dev *svdev, uint8_t target);
39107fe6a43SSeth Howell 
39207fe6a43SSeth Howell static int
39307fe6a43SSeth Howell bdev_virtio_get_ctx_size(void)
39407fe6a43SSeth Howell {
39507fe6a43SSeth Howell 	return sizeof(struct virtio_scsi_io_ctx);
39607fe6a43SSeth Howell }
39707fe6a43SSeth Howell 
39807fe6a43SSeth Howell static int
39907fe6a43SSeth Howell bdev_virtio_scsi_config_json(struct spdk_json_write_ctx *w)
40007fe6a43SSeth Howell {
40107fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev;
40207fe6a43SSeth Howell 
40307fe6a43SSeth Howell 	pthread_mutex_lock(&g_virtio_scsi_mutex);
40407fe6a43SSeth Howell 	TAILQ_FOREACH(svdev, &g_virtio_scsi_devs, tailq) {
40507fe6a43SSeth Howell 		spdk_json_write_object_begin(w);
40607fe6a43SSeth Howell 
4072aed03f0SMaciej Wawryk 		spdk_json_write_named_string(w, "method", "bdev_virtio_attach_controller");
40807fe6a43SSeth Howell 
40907fe6a43SSeth Howell 		spdk_json_write_named_object_begin(w, "params");
41007fe6a43SSeth Howell 		spdk_json_write_named_string(w, "name", svdev->vdev.name);
41107fe6a43SSeth Howell 		spdk_json_write_named_string(w, "dev_type", "scsi");
41207fe6a43SSeth Howell 
41307fe6a43SSeth Howell 		/* Write transport specific parameters. */
41407fe6a43SSeth Howell 		svdev->vdev.backend_ops->write_json_config(&svdev->vdev, w);
41507fe6a43SSeth Howell 
41607fe6a43SSeth Howell 		spdk_json_write_object_end(w);
41707fe6a43SSeth Howell 
41807fe6a43SSeth Howell 		spdk_json_write_object_end(w);
41907fe6a43SSeth Howell 
42007fe6a43SSeth Howell 	}
42107fe6a43SSeth Howell 	pthread_mutex_unlock(&g_virtio_scsi_mutex);
42207fe6a43SSeth Howell 
42307fe6a43SSeth Howell 	return 0;
42407fe6a43SSeth Howell }
42507fe6a43SSeth Howell 
42607fe6a43SSeth Howell 
42707fe6a43SSeth Howell static struct spdk_bdev_module virtio_scsi_if = {
42807fe6a43SSeth Howell 	.name = "virtio_scsi",
42907fe6a43SSeth Howell 	.module_init = bdev_virtio_initialize,
43007fe6a43SSeth Howell 	.module_fini = bdev_virtio_finish,
43107fe6a43SSeth Howell 	.get_ctx_size = bdev_virtio_get_ctx_size,
43207fe6a43SSeth Howell 	.config_json = bdev_virtio_scsi_config_json,
43307fe6a43SSeth Howell 	.async_fini = true,
43407fe6a43SSeth Howell };
43507fe6a43SSeth Howell 
43607fe6a43SSeth Howell SPDK_BDEV_MODULE_REGISTER(virtio_scsi, &virtio_scsi_if)
43707fe6a43SSeth Howell 
43807fe6a43SSeth Howell static struct virtio_scsi_io_ctx *
43907fe6a43SSeth Howell bdev_virtio_init_io_vreq(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
44007fe6a43SSeth Howell {
44107fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req;
44207fe6a43SSeth Howell 	struct virtio_scsi_cmd_resp *resp;
44307fe6a43SSeth Howell 	struct virtio_scsi_disk *disk = (struct virtio_scsi_disk *)bdev_io->bdev;
44407fe6a43SSeth Howell 	struct virtio_scsi_io_ctx *io_ctx = (struct virtio_scsi_io_ctx *)bdev_io->driver_ctx;
44507fe6a43SSeth Howell 
44607fe6a43SSeth Howell 	req = &io_ctx->req;
44707fe6a43SSeth Howell 	resp = &io_ctx->resp;
44807fe6a43SSeth Howell 
44907fe6a43SSeth Howell 	io_ctx->iov_req.iov_base = req;
45007fe6a43SSeth Howell 	io_ctx->iov_req.iov_len = sizeof(*req);
45107fe6a43SSeth Howell 
45207fe6a43SSeth Howell 	io_ctx->iov_resp.iov_base = resp;
45307fe6a43SSeth Howell 	io_ctx->iov_resp.iov_len = sizeof(*resp);
45407fe6a43SSeth Howell 
45507fe6a43SSeth Howell 	memset(req, 0, sizeof(*req));
45607fe6a43SSeth Howell 	req->lun[0] = 1;
45707fe6a43SSeth Howell 	req->lun[1] = disk->info.target;
45807fe6a43SSeth Howell 
45907fe6a43SSeth Howell 	return io_ctx;
46007fe6a43SSeth Howell }
46107fe6a43SSeth Howell 
46207fe6a43SSeth Howell static struct virtio_scsi_io_ctx *
46307fe6a43SSeth Howell bdev_virtio_init_tmf_vreq(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
46407fe6a43SSeth Howell {
46507fe6a43SSeth Howell 	struct virtio_scsi_ctrl_tmf_req *tmf_req;
46607fe6a43SSeth Howell 	struct virtio_scsi_ctrl_tmf_resp *tmf_resp;
46707fe6a43SSeth Howell 	struct virtio_scsi_disk *disk = SPDK_CONTAINEROF(bdev_io->bdev, struct virtio_scsi_disk, bdev);
46807fe6a43SSeth Howell 	struct virtio_scsi_io_ctx *io_ctx = (struct virtio_scsi_io_ctx *)bdev_io->driver_ctx;
46907fe6a43SSeth Howell 
47007fe6a43SSeth Howell 	tmf_req = &io_ctx->tmf_req;
47107fe6a43SSeth Howell 	tmf_resp = &io_ctx->tmf_resp;
47207fe6a43SSeth Howell 
47307fe6a43SSeth Howell 	io_ctx->iov_req.iov_base = tmf_req;
47407fe6a43SSeth Howell 	io_ctx->iov_req.iov_len = sizeof(*tmf_req);
47507fe6a43SSeth Howell 	io_ctx->iov_resp.iov_base = tmf_resp;
47607fe6a43SSeth Howell 	io_ctx->iov_resp.iov_len = sizeof(*tmf_resp);
47707fe6a43SSeth Howell 
47807fe6a43SSeth Howell 	memset(tmf_req, 0, sizeof(*tmf_req));
47907fe6a43SSeth Howell 	tmf_req->lun[0] = 1;
48007fe6a43SSeth Howell 	tmf_req->lun[1] = disk->info.target;
48107fe6a43SSeth Howell 
48207fe6a43SSeth Howell 	return io_ctx;
48307fe6a43SSeth Howell }
48407fe6a43SSeth Howell 
48507fe6a43SSeth Howell static void
48607fe6a43SSeth Howell bdev_virtio_send_io(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
48707fe6a43SSeth Howell {
48807fe6a43SSeth Howell 	struct bdev_virtio_io_channel *virtio_channel = spdk_io_channel_get_ctx(ch);
48907fe6a43SSeth Howell 	struct virtqueue *vq = virtio_channel->vq;
49007fe6a43SSeth Howell 	struct virtio_scsi_io_ctx *io_ctx = (struct virtio_scsi_io_ctx *)bdev_io->driver_ctx;
49107fe6a43SSeth Howell 	int rc;
49207fe6a43SSeth Howell 
49307fe6a43SSeth Howell 	rc = virtqueue_req_start(vq, bdev_io, bdev_io->u.bdev.iovcnt + 2);
49407fe6a43SSeth Howell 	if (rc == -ENOMEM) {
49507fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM);
49607fe6a43SSeth Howell 		return;
49707fe6a43SSeth Howell 	} else if (rc != 0) {
49807fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
49907fe6a43SSeth Howell 		return;
50007fe6a43SSeth Howell 	}
50107fe6a43SSeth Howell 
50207fe6a43SSeth Howell 	virtqueue_req_add_iovs(vq, &io_ctx->iov_req, 1, SPDK_VIRTIO_DESC_RO);
50307fe6a43SSeth Howell 	if (bdev_io->type == SPDK_BDEV_IO_TYPE_READ) {
50407fe6a43SSeth Howell 		virtqueue_req_add_iovs(vq, &io_ctx->iov_resp, 1, SPDK_VIRTIO_DESC_WR);
50507fe6a43SSeth Howell 		virtqueue_req_add_iovs(vq, bdev_io->u.bdev.iovs, bdev_io->u.bdev.iovcnt,
50607fe6a43SSeth Howell 				       SPDK_VIRTIO_DESC_WR);
50707fe6a43SSeth Howell 	} else {
50807fe6a43SSeth Howell 		virtqueue_req_add_iovs(vq, bdev_io->u.bdev.iovs, bdev_io->u.bdev.iovcnt,
50907fe6a43SSeth Howell 				       SPDK_VIRTIO_DESC_RO);
51007fe6a43SSeth Howell 		virtqueue_req_add_iovs(vq, &io_ctx->iov_resp, 1, SPDK_VIRTIO_DESC_WR);
51107fe6a43SSeth Howell 	}
51207fe6a43SSeth Howell 
51307fe6a43SSeth Howell 	virtqueue_req_flush(vq);
51407fe6a43SSeth Howell }
51507fe6a43SSeth Howell 
51607fe6a43SSeth Howell static void
51707fe6a43SSeth Howell bdev_virtio_rw(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
51807fe6a43SSeth Howell {
51907fe6a43SSeth Howell 	struct virtio_scsi_disk *disk = SPDK_CONTAINEROF(bdev_io->bdev, struct virtio_scsi_disk, bdev);
52007fe6a43SSeth Howell 	struct virtio_scsi_io_ctx *io_ctx = bdev_virtio_init_io_vreq(ch, bdev_io);
52107fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &io_ctx->req;
52207fe6a43SSeth Howell 	bool is_write = bdev_io->type == SPDK_BDEV_IO_TYPE_WRITE;
52307fe6a43SSeth Howell 
52407fe6a43SSeth Howell 	if (disk->info.num_blocks > (1ULL << 32)) {
52507fe6a43SSeth Howell 		req->cdb[0] = is_write ? SPDK_SBC_WRITE_16 : SPDK_SBC_READ_16;
52607fe6a43SSeth Howell 		to_be64(&req->cdb[2], bdev_io->u.bdev.offset_blocks);
52707fe6a43SSeth Howell 		to_be32(&req->cdb[10], bdev_io->u.bdev.num_blocks);
52807fe6a43SSeth Howell 	} else {
52907fe6a43SSeth Howell 		req->cdb[0] = is_write ? SPDK_SBC_WRITE_10 : SPDK_SBC_READ_10;
53007fe6a43SSeth Howell 		to_be32(&req->cdb[2], bdev_io->u.bdev.offset_blocks);
53107fe6a43SSeth Howell 		to_be16(&req->cdb[7], bdev_io->u.bdev.num_blocks);
53207fe6a43SSeth Howell 	}
53307fe6a43SSeth Howell 
53407fe6a43SSeth Howell 	bdev_virtio_send_io(ch, bdev_io);
53507fe6a43SSeth Howell }
53607fe6a43SSeth Howell 
53707fe6a43SSeth Howell static void
53807fe6a43SSeth Howell bdev_virtio_reset(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
53907fe6a43SSeth Howell {
54007fe6a43SSeth Howell 	struct bdev_virtio_io_channel *virtio_ch = spdk_io_channel_get_ctx(ch);
54107fe6a43SSeth Howell 	struct virtio_scsi_io_ctx *io_ctx = bdev_virtio_init_tmf_vreq(ch, bdev_io);
54207fe6a43SSeth Howell 	struct virtio_scsi_ctrl_tmf_req *tmf_req = &io_ctx->tmf_req;
54307fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev = virtio_ch->svdev;
54407fe6a43SSeth Howell 	size_t enqueued_count;
54507fe6a43SSeth Howell 
54607fe6a43SSeth Howell 	tmf_req->type = VIRTIO_SCSI_T_TMF;
54707fe6a43SSeth Howell 	tmf_req->subtype = VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET;
54807fe6a43SSeth Howell 
54907fe6a43SSeth Howell 	enqueued_count = spdk_ring_enqueue(svdev->ctrlq_ring, (void **)&bdev_io, 1, NULL);
55007fe6a43SSeth Howell 	if (spdk_likely(enqueued_count == 1)) {
55107fe6a43SSeth Howell 		return;
55207fe6a43SSeth Howell 	} else {
55307fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM);
55407fe6a43SSeth Howell 	}
55507fe6a43SSeth Howell }
55607fe6a43SSeth Howell 
55707fe6a43SSeth Howell static void
55807fe6a43SSeth Howell bdev_virtio_unmap(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io, bool success)
55907fe6a43SSeth Howell {
56007fe6a43SSeth Howell 	struct virtio_scsi_io_ctx *io_ctx = bdev_virtio_init_io_vreq(ch, bdev_io);
56107fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &io_ctx->req;
56207fe6a43SSeth Howell 	struct spdk_scsi_unmap_bdesc *desc, *first_desc;
56307fe6a43SSeth Howell 	uint8_t *buf;
56407fe6a43SSeth Howell 	uint64_t offset_blocks, num_blocks;
56507fe6a43SSeth Howell 	uint16_t cmd_len;
56607fe6a43SSeth Howell 
56707fe6a43SSeth Howell 	if (!success) {
56807fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
56907fe6a43SSeth Howell 		return;
57007fe6a43SSeth Howell 	}
57107fe6a43SSeth Howell 
57207fe6a43SSeth Howell 	buf = bdev_io->u.bdev.iovs[0].iov_base;
57307fe6a43SSeth Howell 
57407fe6a43SSeth Howell 	offset_blocks = bdev_io->u.bdev.offset_blocks;
57507fe6a43SSeth Howell 	num_blocks = bdev_io->u.bdev.num_blocks;
57607fe6a43SSeth Howell 
57707fe6a43SSeth Howell 	/* (n-1) * 16-byte descriptors */
57807fe6a43SSeth Howell 	first_desc = desc = (struct spdk_scsi_unmap_bdesc *)&buf[8];
57907fe6a43SSeth Howell 	while (num_blocks > UINT32_MAX) {
58007fe6a43SSeth Howell 		to_be64(&desc->lba, offset_blocks);
58107fe6a43SSeth Howell 		to_be32(&desc->block_count, UINT32_MAX);
58207fe6a43SSeth Howell 		memset(&desc->reserved, 0, sizeof(desc->reserved));
58307fe6a43SSeth Howell 		offset_blocks += UINT32_MAX;
58407fe6a43SSeth Howell 		num_blocks -= UINT32_MAX;
58507fe6a43SSeth Howell 		desc++;
58607fe6a43SSeth Howell 	}
58707fe6a43SSeth Howell 
58807fe6a43SSeth Howell 	/* The last descriptor with block_count <= UINT32_MAX */
58907fe6a43SSeth Howell 	to_be64(&desc->lba, offset_blocks);
59007fe6a43SSeth Howell 	to_be32(&desc->block_count, num_blocks);
59107fe6a43SSeth Howell 	memset(&desc->reserved, 0, sizeof(desc->reserved));
59207fe6a43SSeth Howell 
59307fe6a43SSeth Howell 	/* 8-byte header + n * 16-byte block descriptor */
59407fe6a43SSeth Howell 	cmd_len = 8 + (desc - first_desc + 1) *  sizeof(struct spdk_scsi_unmap_bdesc);
59507fe6a43SSeth Howell 
59607fe6a43SSeth Howell 	req->cdb[0] = SPDK_SBC_UNMAP;
59707fe6a43SSeth Howell 	to_be16(&req->cdb[7], cmd_len);
59807fe6a43SSeth Howell 
59907fe6a43SSeth Howell 	/* 8-byte header */
60007fe6a43SSeth Howell 	to_be16(&buf[0], cmd_len - 2); /* total length (excluding the length field) */
60107fe6a43SSeth Howell 	to_be16(&buf[2], cmd_len - 8); /* length of block descriptors */
60207fe6a43SSeth Howell 	memset(&buf[4], 0, 4); /* reserved */
60307fe6a43SSeth Howell 
60407fe6a43SSeth Howell 	bdev_virtio_send_io(ch, bdev_io);
60507fe6a43SSeth Howell }
60607fe6a43SSeth Howell 
60707fe6a43SSeth Howell static void
60807fe6a43SSeth Howell bdev_virtio_get_buf_cb(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io,
60907fe6a43SSeth Howell 		       bool success)
61007fe6a43SSeth Howell {
61107fe6a43SSeth Howell 	if (!success) {
61207fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
61307fe6a43SSeth Howell 		return;
61407fe6a43SSeth Howell 	}
61507fe6a43SSeth Howell 
61607fe6a43SSeth Howell 	bdev_virtio_rw(ch, bdev_io);
61707fe6a43SSeth Howell }
61807fe6a43SSeth Howell 
6198dd1cd21SBen Walker static int
6208dd1cd21SBen Walker _bdev_virtio_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
62107fe6a43SSeth Howell {
62207fe6a43SSeth Howell 	struct virtio_scsi_disk *disk = SPDK_CONTAINEROF(bdev_io->bdev, struct virtio_scsi_disk, bdev);
62307fe6a43SSeth Howell 
62407fe6a43SSeth Howell 	switch (bdev_io->type) {
62507fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_READ:
62607fe6a43SSeth Howell 		spdk_bdev_io_get_buf(bdev_io, bdev_virtio_get_buf_cb,
62707fe6a43SSeth Howell 				     bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen);
62807fe6a43SSeth Howell 		return 0;
62907fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_WRITE:
63007fe6a43SSeth Howell 		bdev_virtio_rw(ch, bdev_io);
63107fe6a43SSeth Howell 		return 0;
63207fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_RESET:
63307fe6a43SSeth Howell 		bdev_virtio_reset(ch, bdev_io);
63407fe6a43SSeth Howell 		return 0;
63507fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_UNMAP: {
63607fe6a43SSeth Howell 		uint64_t buf_len = 8 /* header size */ +
63707fe6a43SSeth Howell 				   (bdev_io->u.bdev.num_blocks + UINT32_MAX - 1) /
63807fe6a43SSeth Howell 				   UINT32_MAX * sizeof(struct spdk_scsi_unmap_bdesc);
63907fe6a43SSeth Howell 
64007fe6a43SSeth Howell 		if (!disk->info.unmap_supported) {
64107fe6a43SSeth Howell 			return -1;
64207fe6a43SSeth Howell 		}
64307fe6a43SSeth Howell 
64407fe6a43SSeth Howell 		if (buf_len > SPDK_BDEV_LARGE_BUF_MAX_SIZE) {
64507fe6a43SSeth Howell 			SPDK_ERRLOG("Trying to UNMAP too many blocks: %"PRIu64"\n",
64607fe6a43SSeth Howell 				    bdev_io->u.bdev.num_blocks);
64707fe6a43SSeth Howell 			return -1;
64807fe6a43SSeth Howell 		}
64907fe6a43SSeth Howell 		spdk_bdev_io_get_buf(bdev_io, bdev_virtio_unmap, buf_len);
65007fe6a43SSeth Howell 		return 0;
65107fe6a43SSeth Howell 	}
65207fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_FLUSH:
65307fe6a43SSeth Howell 	default:
65407fe6a43SSeth Howell 		return -1;
65507fe6a43SSeth Howell 	}
65607fe6a43SSeth Howell 	return 0;
65707fe6a43SSeth Howell }
65807fe6a43SSeth Howell 
6598dd1cd21SBen Walker static void
6608dd1cd21SBen Walker bdev_virtio_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
66107fe6a43SSeth Howell {
66207fe6a43SSeth Howell 	if (_bdev_virtio_submit_request(ch, bdev_io) < 0) {
66307fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
66407fe6a43SSeth Howell 	}
66507fe6a43SSeth Howell }
66607fe6a43SSeth Howell 
66707fe6a43SSeth Howell static bool
66807fe6a43SSeth Howell bdev_virtio_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type)
66907fe6a43SSeth Howell {
67007fe6a43SSeth Howell 	struct virtio_scsi_disk *disk = ctx;
67107fe6a43SSeth Howell 
67207fe6a43SSeth Howell 	switch (io_type) {
67307fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_READ:
67407fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_WRITE:
67507fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_FLUSH:
67607fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_RESET:
67707fe6a43SSeth Howell 		return true;
67807fe6a43SSeth Howell 
67907fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_UNMAP:
68007fe6a43SSeth Howell 		return disk->info.unmap_supported;
68107fe6a43SSeth Howell 
68207fe6a43SSeth Howell 	default:
68307fe6a43SSeth Howell 		return false;
68407fe6a43SSeth Howell 	}
68507fe6a43SSeth Howell }
68607fe6a43SSeth Howell 
68707fe6a43SSeth Howell static struct spdk_io_channel *
68807fe6a43SSeth Howell bdev_virtio_get_io_channel(void *ctx)
68907fe6a43SSeth Howell {
69007fe6a43SSeth Howell 	struct virtio_scsi_disk *disk = ctx;
69107fe6a43SSeth Howell 
69207fe6a43SSeth Howell 	return spdk_get_io_channel(disk->svdev);
69307fe6a43SSeth Howell }
69407fe6a43SSeth Howell 
69507fe6a43SSeth Howell static int
69607fe6a43SSeth Howell bdev_virtio_disk_destruct(void *ctx)
69707fe6a43SSeth Howell {
69807fe6a43SSeth Howell 	struct virtio_scsi_disk *disk = ctx;
69907fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev = disk->svdev;
70007fe6a43SSeth Howell 
70107fe6a43SSeth Howell 	TAILQ_REMOVE(&svdev->luns, disk, link);
70207fe6a43SSeth Howell 	free(disk->bdev.name);
70307fe6a43SSeth Howell 	free(disk);
70407fe6a43SSeth Howell 
70507fe6a43SSeth Howell 	if (svdev->removed && TAILQ_EMPTY(&svdev->luns)) {
70607fe6a43SSeth Howell 		spdk_io_device_unregister(svdev, virtio_scsi_dev_unregister_cb);
70707fe6a43SSeth Howell 	}
70807fe6a43SSeth Howell 
70907fe6a43SSeth Howell 	return 0;
71007fe6a43SSeth Howell }
71107fe6a43SSeth Howell 
71207fe6a43SSeth Howell static int
71307fe6a43SSeth Howell bdev_virtio_dump_info_json(void *ctx, struct spdk_json_write_ctx *w)
71407fe6a43SSeth Howell {
71507fe6a43SSeth Howell 	struct virtio_scsi_disk *disk = ctx;
71607fe6a43SSeth Howell 
71707fe6a43SSeth Howell 	virtio_dev_dump_json_info(&disk->svdev->vdev, w);
71807fe6a43SSeth Howell 	return 0;
71907fe6a43SSeth Howell }
72007fe6a43SSeth Howell 
72107fe6a43SSeth Howell static void
72207fe6a43SSeth Howell bdev_virtio_write_config_json(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w)
72307fe6a43SSeth Howell {
72407fe6a43SSeth Howell 	/* SCSI targets and LUNS are discovered during scan process so nothing
72507fe6a43SSeth Howell 	 * to save here.
72607fe6a43SSeth Howell 	 */
72707fe6a43SSeth Howell }
72807fe6a43SSeth Howell 
72907fe6a43SSeth Howell static const struct spdk_bdev_fn_table virtio_fn_table = {
73007fe6a43SSeth Howell 	.destruct		= bdev_virtio_disk_destruct,
73107fe6a43SSeth Howell 	.submit_request		= bdev_virtio_submit_request,
73207fe6a43SSeth Howell 	.io_type_supported	= bdev_virtio_io_type_supported,
73307fe6a43SSeth Howell 	.get_io_channel		= bdev_virtio_get_io_channel,
73407fe6a43SSeth Howell 	.dump_info_json		= bdev_virtio_dump_info_json,
73507fe6a43SSeth Howell 	.write_config_json	= bdev_virtio_write_config_json,
73607fe6a43SSeth Howell };
73707fe6a43SSeth Howell 
73807fe6a43SSeth Howell static void
73907fe6a43SSeth Howell get_scsi_status(struct virtio_scsi_cmd_resp *resp, int *sk, int *asc, int *ascq)
74007fe6a43SSeth Howell {
74107fe6a43SSeth Howell 	/* see spdk_scsi_task_build_sense_data() for sense data details */
74207fe6a43SSeth Howell 	*sk = 0;
74307fe6a43SSeth Howell 	*asc = 0;
74407fe6a43SSeth Howell 	*ascq = 0;
74507fe6a43SSeth Howell 
74607fe6a43SSeth Howell 	if (resp->sense_len < 3) {
74707fe6a43SSeth Howell 		return;
74807fe6a43SSeth Howell 	}
74907fe6a43SSeth Howell 
75007fe6a43SSeth Howell 	*sk = resp->sense[2] & 0xf;
75107fe6a43SSeth Howell 
75207fe6a43SSeth Howell 	if (resp->sense_len < 13) {
75307fe6a43SSeth Howell 		return;
75407fe6a43SSeth Howell 	}
75507fe6a43SSeth Howell 
75607fe6a43SSeth Howell 	*asc = resp->sense[12];
75707fe6a43SSeth Howell 
75807fe6a43SSeth Howell 	if (resp->sense_len < 14) {
75907fe6a43SSeth Howell 		return;
76007fe6a43SSeth Howell 	}
76107fe6a43SSeth Howell 
76207fe6a43SSeth Howell 	*ascq = resp->sense[13];
76307fe6a43SSeth Howell }
76407fe6a43SSeth Howell 
76507fe6a43SSeth Howell static void
76607fe6a43SSeth Howell bdev_virtio_io_cpl(struct spdk_bdev_io *bdev_io)
76707fe6a43SSeth Howell {
76807fe6a43SSeth Howell 	struct virtio_scsi_io_ctx *io_ctx = (struct virtio_scsi_io_ctx *)bdev_io->driver_ctx;
76907fe6a43SSeth Howell 	int sk, asc, ascq;
77007fe6a43SSeth Howell 
77107fe6a43SSeth Howell 	get_scsi_status(&io_ctx->resp, &sk, &asc, &ascq);
77207fe6a43SSeth Howell 	spdk_bdev_io_complete_scsi_status(bdev_io, io_ctx->resp.status, sk, asc, ascq);
77307fe6a43SSeth Howell }
77407fe6a43SSeth Howell 
77507fe6a43SSeth Howell static int
77607fe6a43SSeth Howell bdev_virtio_poll(void *arg)
77707fe6a43SSeth Howell {
77807fe6a43SSeth Howell 	struct bdev_virtio_io_channel *ch = arg;
77907fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev = ch->svdev;
78007fe6a43SSeth Howell 	struct virtio_scsi_scan_base *scan_ctx = svdev->scan_ctx;
78107fe6a43SSeth Howell 	void *io[32];
78207fe6a43SSeth Howell 	uint32_t io_len[32];
78307fe6a43SSeth Howell 	uint16_t i, cnt;
78407fe6a43SSeth Howell 	int rc;
78507fe6a43SSeth Howell 
78607fe6a43SSeth Howell 	cnt = virtio_recv_pkts(ch->vq, (void **)io, io_len, SPDK_COUNTOF(io));
78707fe6a43SSeth Howell 	for (i = 0; i < cnt; ++i) {
78807fe6a43SSeth Howell 		if (spdk_unlikely(scan_ctx && io[i] == &scan_ctx->io_ctx)) {
78907fe6a43SSeth Howell 			if (svdev->removed) {
79007fe6a43SSeth Howell 				_virtio_scsi_dev_scan_finish(scan_ctx, -EINTR);
791eb05cbd6SMaciej Szwed 				return SPDK_POLLER_BUSY;
79207fe6a43SSeth Howell 			}
79307fe6a43SSeth Howell 
79407fe6a43SSeth Howell 			if (scan_ctx->restart) {
79507fe6a43SSeth Howell 				scan_ctx->restart = false;
79607fe6a43SSeth Howell 				scan_ctx->full_scan = true;
79707fe6a43SSeth Howell 				_virtio_scsi_dev_scan_tgt(scan_ctx, 0);
79807fe6a43SSeth Howell 				continue;
79907fe6a43SSeth Howell 			}
80007fe6a43SSeth Howell 
80107fe6a43SSeth Howell 			process_scan_resp(scan_ctx);
80207fe6a43SSeth Howell 			continue;
80307fe6a43SSeth Howell 		}
80407fe6a43SSeth Howell 
80507fe6a43SSeth Howell 		bdev_virtio_io_cpl(io[i]);
80607fe6a43SSeth Howell 	}
80707fe6a43SSeth Howell 
80808422b58SJim Harris 	/* scan_ctx could have been freed while processing completions above, so
80908422b58SJim Harris 	 * we need to re-read the value again here into the local variable before
81008422b58SJim Harris 	 * using it.
81108422b58SJim Harris 	 */
81208422b58SJim Harris 	scan_ctx = svdev->scan_ctx;
81307fe6a43SSeth Howell 	if (spdk_unlikely(scan_ctx && scan_ctx->needs_resend)) {
81407fe6a43SSeth Howell 		if (svdev->removed) {
81507fe6a43SSeth Howell 			_virtio_scsi_dev_scan_finish(scan_ctx, -EINTR);
816eb05cbd6SMaciej Szwed 			return SPDK_POLLER_BUSY;
81707fe6a43SSeth Howell 		} else if (cnt == 0) {
818eb05cbd6SMaciej Szwed 			return SPDK_POLLER_IDLE;
81907fe6a43SSeth Howell 		}
82007fe6a43SSeth Howell 
82107fe6a43SSeth Howell 		rc = send_scan_io(scan_ctx);
82207fe6a43SSeth Howell 		if (rc != 0) {
82307fe6a43SSeth Howell 			assert(scan_ctx->retries > 0);
82407fe6a43SSeth Howell 			scan_ctx->retries--;
82507fe6a43SSeth Howell 			if (scan_ctx->retries == 0) {
82607fe6a43SSeth Howell 				SPDK_ERRLOG("Target scan failed unrecoverably with rc = %d.\n", rc);
82707fe6a43SSeth Howell 				_virtio_scsi_dev_scan_finish(scan_ctx, rc);
82807fe6a43SSeth Howell 			}
82907fe6a43SSeth Howell 		}
83007fe6a43SSeth Howell 	}
83107fe6a43SSeth Howell 
83207fe6a43SSeth Howell 	return cnt;
83307fe6a43SSeth Howell }
83407fe6a43SSeth Howell 
83507fe6a43SSeth Howell static void
83607fe6a43SSeth Howell bdev_virtio_tmf_cpl_cb(void *ctx)
83707fe6a43SSeth Howell {
83807fe6a43SSeth Howell 	struct spdk_bdev_io *bdev_io = ctx;
83907fe6a43SSeth Howell 	struct virtio_scsi_io_ctx *io_ctx = (struct virtio_scsi_io_ctx *)bdev_io->driver_ctx;
84007fe6a43SSeth Howell 
84107fe6a43SSeth Howell 	if (io_ctx->tmf_resp.response == VIRTIO_SCSI_S_OK) {
84207fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_SUCCESS);
84307fe6a43SSeth Howell 	} else {
84407fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
84507fe6a43SSeth Howell 	}
84607fe6a43SSeth Howell }
84707fe6a43SSeth Howell 
84807fe6a43SSeth Howell static void
84907fe6a43SSeth Howell bdev_virtio_tmf_cpl(struct spdk_bdev_io *bdev_io)
85007fe6a43SSeth Howell {
85107fe6a43SSeth Howell 	spdk_thread_send_msg(spdk_bdev_io_get_thread(bdev_io), bdev_virtio_tmf_cpl_cb, bdev_io);
85207fe6a43SSeth Howell }
85307fe6a43SSeth Howell 
85407fe6a43SSeth Howell static void
85507fe6a43SSeth Howell bdev_virtio_eventq_io_cpl(struct virtio_scsi_dev *svdev, struct virtio_scsi_eventq_io *io)
85607fe6a43SSeth Howell {
85707fe6a43SSeth Howell 	struct virtio_scsi_event *ev = &io->ev;
85807fe6a43SSeth Howell 	struct virtio_scsi_disk *disk;
85907fe6a43SSeth Howell 
86007fe6a43SSeth Howell 	if (ev->lun[0] != 1) {
86107fe6a43SSeth Howell 		SPDK_WARNLOG("Received an event with invalid data layout.\n");
86207fe6a43SSeth Howell 		goto out;
86307fe6a43SSeth Howell 	}
86407fe6a43SSeth Howell 
86507fe6a43SSeth Howell 	if (ev->event & VIRTIO_SCSI_T_EVENTS_MISSED) {
86607fe6a43SSeth Howell 		ev->event &= ~VIRTIO_SCSI_T_EVENTS_MISSED;
86707fe6a43SSeth Howell 		virtio_scsi_dev_scan(svdev, NULL, NULL);
86807fe6a43SSeth Howell 	}
86907fe6a43SSeth Howell 
87007fe6a43SSeth Howell 	switch (ev->event) {
87107fe6a43SSeth Howell 	case VIRTIO_SCSI_T_NO_EVENT:
87207fe6a43SSeth Howell 		break;
87307fe6a43SSeth Howell 	case VIRTIO_SCSI_T_TRANSPORT_RESET:
87407fe6a43SSeth Howell 		switch (ev->reason) {
87507fe6a43SSeth Howell 		case VIRTIO_SCSI_EVT_RESET_RESCAN:
87607fe6a43SSeth Howell 			virtio_scsi_dev_scan_tgt(svdev, ev->lun[1]);
87707fe6a43SSeth Howell 			break;
87807fe6a43SSeth Howell 		case VIRTIO_SCSI_EVT_RESET_REMOVED:
87907fe6a43SSeth Howell 			disk = virtio_scsi_dev_get_disk_by_id(svdev, ev->lun[1]);
88007fe6a43SSeth Howell 			if (disk != NULL) {
88107fe6a43SSeth Howell 				spdk_bdev_unregister(&disk->bdev, NULL, NULL);
88207fe6a43SSeth Howell 			}
88307fe6a43SSeth Howell 			break;
88407fe6a43SSeth Howell 		default:
88507fe6a43SSeth Howell 			break;
88607fe6a43SSeth Howell 		}
88707fe6a43SSeth Howell 		break;
88807fe6a43SSeth Howell 	default:
88907fe6a43SSeth Howell 		break;
89007fe6a43SSeth Howell 	}
89107fe6a43SSeth Howell 
89207fe6a43SSeth Howell out:
89307fe6a43SSeth Howell 	virtio_scsi_dev_send_eventq_io(svdev->vdev.vqs[VIRTIO_SCSI_EVENTQ], io);
89407fe6a43SSeth Howell }
89507fe6a43SSeth Howell 
89607fe6a43SSeth Howell static void
89707fe6a43SSeth Howell bdev_virtio_tmf_abort_nomem_cb(void *ctx)
89807fe6a43SSeth Howell {
89907fe6a43SSeth Howell 	struct spdk_bdev_io *bdev_io = ctx;
90007fe6a43SSeth Howell 
90107fe6a43SSeth Howell 	spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM);
90207fe6a43SSeth Howell }
90307fe6a43SSeth Howell 
90407fe6a43SSeth Howell static void
90507fe6a43SSeth Howell bdev_virtio_tmf_abort_ioerr_cb(void *ctx)
90607fe6a43SSeth Howell {
90707fe6a43SSeth Howell 	struct spdk_bdev_io *bdev_io = ctx;
90807fe6a43SSeth Howell 
90907fe6a43SSeth Howell 	spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
91007fe6a43SSeth Howell }
91107fe6a43SSeth Howell 
91207fe6a43SSeth Howell static void
91307fe6a43SSeth Howell bdev_virtio_tmf_abort(struct spdk_bdev_io *bdev_io, int status)
91407fe6a43SSeth Howell {
91507fe6a43SSeth Howell 	spdk_msg_fn fn;
91607fe6a43SSeth Howell 
91707fe6a43SSeth Howell 	if (status == -ENOMEM) {
91807fe6a43SSeth Howell 		fn = bdev_virtio_tmf_abort_nomem_cb;
91907fe6a43SSeth Howell 	} else {
92007fe6a43SSeth Howell 		fn = bdev_virtio_tmf_abort_ioerr_cb;
92107fe6a43SSeth Howell 	}
92207fe6a43SSeth Howell 
92307fe6a43SSeth Howell 	spdk_thread_send_msg(spdk_bdev_io_get_thread(bdev_io), fn, bdev_io);
92407fe6a43SSeth Howell }
92507fe6a43SSeth Howell 
92607fe6a43SSeth Howell static int
92707fe6a43SSeth Howell bdev_virtio_send_tmf_io(struct virtqueue *ctrlq, struct spdk_bdev_io *bdev_io)
92807fe6a43SSeth Howell {
92907fe6a43SSeth Howell 	struct virtio_scsi_io_ctx *io_ctx = (struct virtio_scsi_io_ctx *)bdev_io->driver_ctx;
93007fe6a43SSeth Howell 	int rc;
93107fe6a43SSeth Howell 
93207fe6a43SSeth Howell 	rc = virtqueue_req_start(ctrlq, bdev_io, 2);
93307fe6a43SSeth Howell 	if (rc != 0) {
93407fe6a43SSeth Howell 		return rc;
93507fe6a43SSeth Howell 	}
93607fe6a43SSeth Howell 
93707fe6a43SSeth Howell 	virtqueue_req_add_iovs(ctrlq, &io_ctx->iov_req, 1, SPDK_VIRTIO_DESC_RO);
93807fe6a43SSeth Howell 	virtqueue_req_add_iovs(ctrlq, &io_ctx->iov_resp, 1, SPDK_VIRTIO_DESC_WR);
93907fe6a43SSeth Howell 
94007fe6a43SSeth Howell 	virtqueue_req_flush(ctrlq);
94107fe6a43SSeth Howell 	return 0;
94207fe6a43SSeth Howell }
94307fe6a43SSeth Howell 
94407fe6a43SSeth Howell static int
94507fe6a43SSeth Howell bdev_virtio_mgmt_poll(void *arg)
94607fe6a43SSeth Howell {
94707fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev = arg;
94807fe6a43SSeth Howell 	struct virtio_dev *vdev = &svdev->vdev;
94907fe6a43SSeth Howell 	struct virtqueue *eventq = vdev->vqs[VIRTIO_SCSI_EVENTQ];
95007fe6a43SSeth Howell 	struct virtqueue *ctrlq = vdev->vqs[VIRTIO_SCSI_CONTROLQ];
95107fe6a43SSeth Howell 	struct spdk_ring *send_ring = svdev->ctrlq_ring;
95207fe6a43SSeth Howell 	void *io[16];
95307fe6a43SSeth Howell 	uint32_t io_len[16];
95407fe6a43SSeth Howell 	uint16_t i, cnt;
95507fe6a43SSeth Howell 	int rc;
95607fe6a43SSeth Howell 	int total = 0;
95707fe6a43SSeth Howell 
95807fe6a43SSeth Howell 	cnt = spdk_ring_dequeue(send_ring, io, SPDK_COUNTOF(io));
95907fe6a43SSeth Howell 	total += cnt;
96007fe6a43SSeth Howell 	for (i = 0; i < cnt; ++i) {
96107fe6a43SSeth Howell 		rc = bdev_virtio_send_tmf_io(ctrlq, io[i]);
96207fe6a43SSeth Howell 		if (rc != 0) {
96307fe6a43SSeth Howell 			bdev_virtio_tmf_abort(io[i], rc);
96407fe6a43SSeth Howell 		}
96507fe6a43SSeth Howell 	}
96607fe6a43SSeth Howell 
96707fe6a43SSeth Howell 	cnt = virtio_recv_pkts(ctrlq, io, io_len, SPDK_COUNTOF(io));
96807fe6a43SSeth Howell 	total += cnt;
96907fe6a43SSeth Howell 	for (i = 0; i < cnt; ++i) {
97007fe6a43SSeth Howell 		bdev_virtio_tmf_cpl(io[i]);
97107fe6a43SSeth Howell 	}
97207fe6a43SSeth Howell 
97307fe6a43SSeth Howell 	cnt = virtio_recv_pkts(eventq, io, io_len, SPDK_COUNTOF(io));
97407fe6a43SSeth Howell 	total += cnt;
97507fe6a43SSeth Howell 	for (i = 0; i < cnt; ++i) {
97607fe6a43SSeth Howell 		bdev_virtio_eventq_io_cpl(svdev, io[i]);
97707fe6a43SSeth Howell 	}
97807fe6a43SSeth Howell 
97907fe6a43SSeth Howell 	return total;
98007fe6a43SSeth Howell }
98107fe6a43SSeth Howell 
98207fe6a43SSeth Howell static int
98307fe6a43SSeth Howell bdev_virtio_scsi_ch_create_cb(void *io_device, void *ctx_buf)
98407fe6a43SSeth Howell {
98507fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev = io_device;
98607fe6a43SSeth Howell 	struct virtio_dev *vdev = &svdev->vdev;
98707fe6a43SSeth Howell 	struct bdev_virtio_io_channel *ch = ctx_buf;
98807fe6a43SSeth Howell 	struct virtqueue *vq;
98907fe6a43SSeth Howell 	int32_t queue_idx;
99007fe6a43SSeth Howell 
99107fe6a43SSeth Howell 	queue_idx = virtio_dev_find_and_acquire_queue(vdev, VIRTIO_SCSI_REQUESTQ);
99207fe6a43SSeth Howell 	if (queue_idx < 0) {
99307fe6a43SSeth Howell 		SPDK_ERRLOG("Couldn't get an unused queue for the io_channel.\n");
99407fe6a43SSeth Howell 		return -1;
99507fe6a43SSeth Howell 	}
99607fe6a43SSeth Howell 
99707fe6a43SSeth Howell 	vq = vdev->vqs[queue_idx];
99807fe6a43SSeth Howell 
99907fe6a43SSeth Howell 	ch->svdev = svdev;
100007fe6a43SSeth Howell 	ch->vq = vq;
100107fe6a43SSeth Howell 
1002ab0bc5c2SShuhei Matsumoto 	ch->poller = SPDK_POLLER_REGISTER(bdev_virtio_poll, ch, 0);
100307fe6a43SSeth Howell 
100407fe6a43SSeth Howell 	return 0;
100507fe6a43SSeth Howell }
100607fe6a43SSeth Howell 
100707fe6a43SSeth Howell static void
100807fe6a43SSeth Howell bdev_virtio_scsi_ch_destroy_cb(void *io_device, void *ctx_buf)
100907fe6a43SSeth Howell {
101007fe6a43SSeth Howell 	struct bdev_virtio_io_channel *ch = ctx_buf;
101107fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev = ch->svdev;
101207fe6a43SSeth Howell 	struct virtio_dev *vdev = &svdev->vdev;
101307fe6a43SSeth Howell 	struct virtqueue *vq = ch->vq;
101407fe6a43SSeth Howell 
101507fe6a43SSeth Howell 	spdk_poller_unregister(&ch->poller);
101607fe6a43SSeth Howell 	virtio_dev_release_queue(vdev, vq->vq_queue_index);
101707fe6a43SSeth Howell }
101807fe6a43SSeth Howell 
101907fe6a43SSeth Howell static void
102007fe6a43SSeth Howell _virtio_scsi_dev_scan_finish(struct virtio_scsi_scan_base *base, int errnum)
102107fe6a43SSeth Howell {
102207fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev = base->svdev;
102307fe6a43SSeth Howell 	size_t bdevs_cnt;
102407fe6a43SSeth Howell 	struct spdk_bdev *bdevs[BDEV_VIRTIO_MAX_TARGET];
102507fe6a43SSeth Howell 	struct virtio_scsi_disk *disk;
102607fe6a43SSeth Howell 	struct virtio_scsi_scan_info *tgt, *next_tgt;
102707fe6a43SSeth Howell 
102807fe6a43SSeth Howell 	spdk_put_io_channel(spdk_io_channel_from_ctx(base->channel));
102907fe6a43SSeth Howell 	base->svdev->scan_ctx = NULL;
103007fe6a43SSeth Howell 
103107fe6a43SSeth Howell 	TAILQ_FOREACH_SAFE(tgt, &base->scan_queue, tailq, next_tgt) {
103207fe6a43SSeth Howell 		TAILQ_REMOVE(&base->scan_queue, tgt, tailq);
103307fe6a43SSeth Howell 		free(tgt);
103407fe6a43SSeth Howell 	}
103507fe6a43SSeth Howell 
103607fe6a43SSeth Howell 	if (base->cb_fn == NULL) {
103707fe6a43SSeth Howell 		spdk_free(base);
103807fe6a43SSeth Howell 		return;
103907fe6a43SSeth Howell 	}
104007fe6a43SSeth Howell 
104107fe6a43SSeth Howell 	bdevs_cnt = 0;
104207fe6a43SSeth Howell 	if (errnum == 0) {
104307fe6a43SSeth Howell 		TAILQ_FOREACH(disk, &svdev->luns, link) {
104407fe6a43SSeth Howell 			bdevs[bdevs_cnt] = &disk->bdev;
104507fe6a43SSeth Howell 			bdevs_cnt++;
104607fe6a43SSeth Howell 		}
104707fe6a43SSeth Howell 	}
104807fe6a43SSeth Howell 
104907fe6a43SSeth Howell 	base->cb_fn(base->cb_arg, errnum, bdevs, bdevs_cnt);
105007fe6a43SSeth Howell 	spdk_free(base);
105107fe6a43SSeth Howell }
105207fe6a43SSeth Howell 
105307fe6a43SSeth Howell static int
105407fe6a43SSeth Howell send_scan_io(struct virtio_scsi_scan_base *base)
105507fe6a43SSeth Howell {
105607fe6a43SSeth Howell 	struct virtio_scsi_io_ctx *io_ctx = &base->io_ctx;
105707fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &base->io_ctx.req;
105807fe6a43SSeth Howell 	struct virtqueue *vq = base->channel->vq;
105907fe6a43SSeth Howell 	int payload_iov_cnt = base->iov.iov_len > 0 ? 1 : 0;
106007fe6a43SSeth Howell 	int rc;
106107fe6a43SSeth Howell 
106207fe6a43SSeth Howell 	req->lun[0] = 1;
106307fe6a43SSeth Howell 	req->lun[1] = base->info.target;
106407fe6a43SSeth Howell 
106507fe6a43SSeth Howell 	rc = virtqueue_req_start(vq, io_ctx, 2 + payload_iov_cnt);
106607fe6a43SSeth Howell 	if (rc != 0) {
106707fe6a43SSeth Howell 		base->needs_resend = true;
106807fe6a43SSeth Howell 		return -1;
106907fe6a43SSeth Howell 	}
107007fe6a43SSeth Howell 
107107fe6a43SSeth Howell 	virtqueue_req_add_iovs(vq, &io_ctx->iov_req, 1, SPDK_VIRTIO_DESC_RO);
107207fe6a43SSeth Howell 	virtqueue_req_add_iovs(vq, &io_ctx->iov_resp, 1, SPDK_VIRTIO_DESC_WR);
107307fe6a43SSeth Howell 	virtqueue_req_add_iovs(vq, &base->iov, payload_iov_cnt, SPDK_VIRTIO_DESC_WR);
107407fe6a43SSeth Howell 
107507fe6a43SSeth Howell 	virtqueue_req_flush(vq);
107607fe6a43SSeth Howell 	return 0;
107707fe6a43SSeth Howell }
107807fe6a43SSeth Howell 
107907fe6a43SSeth Howell static int
108007fe6a43SSeth Howell send_inquiry(struct virtio_scsi_scan_base *base)
108107fe6a43SSeth Howell {
108207fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &base->io_ctx.req;
108307fe6a43SSeth Howell 	struct spdk_scsi_cdb_inquiry *cdb;
108407fe6a43SSeth Howell 
108507fe6a43SSeth Howell 	memset(req, 0, sizeof(*req));
108607fe6a43SSeth Howell 
108707fe6a43SSeth Howell 	base->iov.iov_len = BDEV_VIRTIO_SCAN_PAYLOAD_SIZE;
108807fe6a43SSeth Howell 	cdb = (struct spdk_scsi_cdb_inquiry *)req->cdb;
108907fe6a43SSeth Howell 	cdb->opcode = SPDK_SPC_INQUIRY;
109007fe6a43SSeth Howell 	to_be16(cdb->alloc_len, BDEV_VIRTIO_SCAN_PAYLOAD_SIZE);
109107fe6a43SSeth Howell 
109207fe6a43SSeth Howell 	return send_scan_io(base);
109307fe6a43SSeth Howell }
109407fe6a43SSeth Howell 
109507fe6a43SSeth Howell static int
109607fe6a43SSeth Howell send_inquiry_vpd(struct virtio_scsi_scan_base *base, uint8_t page_code)
109707fe6a43SSeth Howell {
109807fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &base->io_ctx.req;
109907fe6a43SSeth Howell 	struct spdk_scsi_cdb_inquiry *inquiry_cdb = (struct spdk_scsi_cdb_inquiry *)req->cdb;
110007fe6a43SSeth Howell 
110107fe6a43SSeth Howell 	memset(req, 0, sizeof(*req));
110207fe6a43SSeth Howell 
110307fe6a43SSeth Howell 	base->iov.iov_len = BDEV_VIRTIO_SCAN_PAYLOAD_SIZE;
110407fe6a43SSeth Howell 	inquiry_cdb->opcode = SPDK_SPC_INQUIRY;
110507fe6a43SSeth Howell 	inquiry_cdb->evpd = 1;
110607fe6a43SSeth Howell 	inquiry_cdb->page_code = page_code;
110707fe6a43SSeth Howell 	to_be16(inquiry_cdb->alloc_len, base->iov.iov_len);
110807fe6a43SSeth Howell 
110907fe6a43SSeth Howell 	return send_scan_io(base);
111007fe6a43SSeth Howell }
111107fe6a43SSeth Howell 
111207fe6a43SSeth Howell static int
111307fe6a43SSeth Howell send_read_cap_10(struct virtio_scsi_scan_base *base)
111407fe6a43SSeth Howell {
111507fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &base->io_ctx.req;
111607fe6a43SSeth Howell 
111707fe6a43SSeth Howell 	memset(req, 0, sizeof(*req));
111807fe6a43SSeth Howell 
111907fe6a43SSeth Howell 	base->iov.iov_len = 8;
112007fe6a43SSeth Howell 	req->cdb[0] = SPDK_SBC_READ_CAPACITY_10;
112107fe6a43SSeth Howell 
112207fe6a43SSeth Howell 	return send_scan_io(base);
112307fe6a43SSeth Howell }
112407fe6a43SSeth Howell 
112507fe6a43SSeth Howell static int
112607fe6a43SSeth Howell send_read_cap_16(struct virtio_scsi_scan_base *base)
112707fe6a43SSeth Howell {
112807fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &base->io_ctx.req;
112907fe6a43SSeth Howell 
113007fe6a43SSeth Howell 	memset(req, 0, sizeof(*req));
113107fe6a43SSeth Howell 
113207fe6a43SSeth Howell 	base->iov.iov_len = 32;
113307fe6a43SSeth Howell 	req->cdb[0] = SPDK_SPC_SERVICE_ACTION_IN_16;
113407fe6a43SSeth Howell 	req->cdb[1] = SPDK_SBC_SAI_READ_CAPACITY_16;
113507fe6a43SSeth Howell 	to_be32(&req->cdb[10], base->iov.iov_len);
113607fe6a43SSeth Howell 
113707fe6a43SSeth Howell 	return send_scan_io(base);
113807fe6a43SSeth Howell }
113907fe6a43SSeth Howell 
114007fe6a43SSeth Howell static int
114107fe6a43SSeth Howell send_test_unit_ready(struct virtio_scsi_scan_base *base)
114207fe6a43SSeth Howell {
114307fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &base->io_ctx.req;
114407fe6a43SSeth Howell 
114507fe6a43SSeth Howell 	memset(req, 0, sizeof(*req));
114607fe6a43SSeth Howell 	req->cdb[0] = SPDK_SPC_TEST_UNIT_READY;
114707fe6a43SSeth Howell 	base->iov.iov_len = 0;
114807fe6a43SSeth Howell 
114907fe6a43SSeth Howell 	return send_scan_io(base);
115007fe6a43SSeth Howell }
115107fe6a43SSeth Howell 
115207fe6a43SSeth Howell static int
115307fe6a43SSeth Howell send_start_stop_unit(struct virtio_scsi_scan_base *base)
115407fe6a43SSeth Howell {
115507fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &base->io_ctx.req;
115607fe6a43SSeth Howell 
115707fe6a43SSeth Howell 	memset(req, 0, sizeof(*req));
115807fe6a43SSeth Howell 	req->cdb[0] = SPDK_SBC_START_STOP_UNIT;
115907fe6a43SSeth Howell 	req->cdb[4] = SPDK_SBC_START_STOP_UNIT_START_BIT;
116007fe6a43SSeth Howell 	base->iov.iov_len = 0;
116107fe6a43SSeth Howell 
116207fe6a43SSeth Howell 	return send_scan_io(base);
116307fe6a43SSeth Howell }
116407fe6a43SSeth Howell 
116507fe6a43SSeth Howell static int
116607fe6a43SSeth Howell process_scan_start_stop_unit(struct virtio_scsi_scan_base *base)
116707fe6a43SSeth Howell {
116807fe6a43SSeth Howell 	struct virtio_scsi_cmd_resp *resp = &base->io_ctx.resp;
116907fe6a43SSeth Howell 
117007fe6a43SSeth Howell 	if (resp->status == SPDK_SCSI_STATUS_GOOD) {
117107fe6a43SSeth Howell 		return send_inquiry_vpd(base, SPDK_SPC_VPD_SUPPORTED_VPD_PAGES);
117207fe6a43SSeth Howell 	}
117307fe6a43SSeth Howell 
117407fe6a43SSeth Howell 	return -1;
117507fe6a43SSeth Howell }
117607fe6a43SSeth Howell 
117707fe6a43SSeth Howell static int
117807fe6a43SSeth Howell process_scan_test_unit_ready(struct virtio_scsi_scan_base *base)
117907fe6a43SSeth Howell {
118007fe6a43SSeth Howell 	struct virtio_scsi_cmd_resp *resp = &base->io_ctx.resp;
118107fe6a43SSeth Howell 	int sk, asc, ascq;
118207fe6a43SSeth Howell 
118307fe6a43SSeth Howell 	get_scsi_status(resp, &sk, &asc, &ascq);
118407fe6a43SSeth Howell 
118507fe6a43SSeth Howell 	/* check response, get VPD if spun up otherwise send SSU */
118607fe6a43SSeth Howell 	if (resp->status == SPDK_SCSI_STATUS_GOOD) {
118707fe6a43SSeth Howell 		return send_inquiry_vpd(base, SPDK_SPC_VPD_SUPPORTED_VPD_PAGES);
118807fe6a43SSeth Howell 	} else if (resp->response == VIRTIO_SCSI_S_OK &&
118907fe6a43SSeth Howell 		   resp->status == SPDK_SCSI_STATUS_CHECK_CONDITION &&
119007fe6a43SSeth Howell 		   sk == SPDK_SCSI_SENSE_UNIT_ATTENTION &&
119107fe6a43SSeth Howell 		   asc == SPDK_SCSI_ASC_LOGICAL_UNIT_NOT_READY) {
119207fe6a43SSeth Howell 		return send_start_stop_unit(base);
119307fe6a43SSeth Howell 	} else {
119407fe6a43SSeth Howell 		return -1;
119507fe6a43SSeth Howell 	}
119607fe6a43SSeth Howell }
119707fe6a43SSeth Howell 
119807fe6a43SSeth Howell static int
119907fe6a43SSeth Howell process_scan_inquiry_standard(struct virtio_scsi_scan_base *base)
120007fe6a43SSeth Howell {
120107fe6a43SSeth Howell 	struct virtio_scsi_cmd_resp *resp = &base->io_ctx.resp;
120207fe6a43SSeth Howell 	struct spdk_scsi_cdb_inquiry_data *inquiry_data =
120307fe6a43SSeth Howell 		(struct spdk_scsi_cdb_inquiry_data *)base->payload;
120407fe6a43SSeth Howell 
120507fe6a43SSeth Howell 	if (resp->status != SPDK_SCSI_STATUS_GOOD) {
120607fe6a43SSeth Howell 		return -1;
120707fe6a43SSeth Howell 	}
120807fe6a43SSeth Howell 
120907fe6a43SSeth Howell 	/* check to make sure its a supported device */
121007fe6a43SSeth Howell 	if (inquiry_data->peripheral_device_type != SPDK_SPC_PERIPHERAL_DEVICE_TYPE_DISK ||
121107fe6a43SSeth Howell 	    inquiry_data->peripheral_qualifier != SPDK_SPC_PERIPHERAL_QUALIFIER_CONNECTED) {
121207fe6a43SSeth Howell 		SPDK_WARNLOG("Unsupported peripheral device type 0x%02x (qualifier 0x%02x)\n",
121307fe6a43SSeth Howell 			     inquiry_data->peripheral_device_type,
121407fe6a43SSeth Howell 			     inquiry_data->peripheral_qualifier);
121507fe6a43SSeth Howell 		return -1;
121607fe6a43SSeth Howell 	}
121707fe6a43SSeth Howell 
121807fe6a43SSeth Howell 	return send_test_unit_ready(base);
121907fe6a43SSeth Howell }
122007fe6a43SSeth Howell 
122107fe6a43SSeth Howell static int
122207fe6a43SSeth Howell process_scan_inquiry_vpd_supported_vpd_pages(struct virtio_scsi_scan_base *base)
122307fe6a43SSeth Howell {
122407fe6a43SSeth Howell 	struct virtio_scsi_cmd_resp *resp = &base->io_ctx.resp;
122507fe6a43SSeth Howell 	bool block_provisioning_page_supported = false;
122607fe6a43SSeth Howell 
122707fe6a43SSeth Howell 	if (resp->status == SPDK_SCSI_STATUS_GOOD) {
122807fe6a43SSeth Howell 		const uint8_t *vpd_data = base->payload;
122907fe6a43SSeth Howell 		const uint8_t *supported_vpd_pages = vpd_data + 4;
123007fe6a43SSeth Howell 		uint16_t page_length;
123107fe6a43SSeth Howell 		uint16_t num_supported_pages;
123207fe6a43SSeth Howell 		uint16_t i;
123307fe6a43SSeth Howell 
123407fe6a43SSeth Howell 		page_length = from_be16(vpd_data + 2);
123507fe6a43SSeth Howell 		num_supported_pages = spdk_min(page_length, base->iov.iov_len - 4);
123607fe6a43SSeth Howell 
123707fe6a43SSeth Howell 		for (i = 0; i < num_supported_pages; i++) {
123807fe6a43SSeth Howell 			if (supported_vpd_pages[i] == SPDK_SPC_VPD_BLOCK_THIN_PROVISION) {
123907fe6a43SSeth Howell 				block_provisioning_page_supported = true;
124007fe6a43SSeth Howell 				break;
124107fe6a43SSeth Howell 			}
124207fe6a43SSeth Howell 		}
124307fe6a43SSeth Howell 	}
124407fe6a43SSeth Howell 
124507fe6a43SSeth Howell 	if (block_provisioning_page_supported) {
124607fe6a43SSeth Howell 		return send_inquiry_vpd(base, SPDK_SPC_VPD_BLOCK_THIN_PROVISION);
124707fe6a43SSeth Howell 	} else {
124807fe6a43SSeth Howell 		return send_read_cap_10(base);
124907fe6a43SSeth Howell 	}
125007fe6a43SSeth Howell }
125107fe6a43SSeth Howell 
125207fe6a43SSeth Howell static int
125307fe6a43SSeth Howell process_scan_inquiry_vpd_block_thin_provision(struct virtio_scsi_scan_base *base)
125407fe6a43SSeth Howell {
125507fe6a43SSeth Howell 	struct virtio_scsi_cmd_resp *resp = &base->io_ctx.resp;
125607fe6a43SSeth Howell 
125707fe6a43SSeth Howell 	base->info.unmap_supported = false;
125807fe6a43SSeth Howell 
125907fe6a43SSeth Howell 	if (resp->status == SPDK_SCSI_STATUS_GOOD) {
126007fe6a43SSeth Howell 		uint8_t *vpd_data = base->payload;
126107fe6a43SSeth Howell 
126207fe6a43SSeth Howell 		base->info.unmap_supported = !!(vpd_data[5] & SPDK_SCSI_UNMAP_LBPU);
126307fe6a43SSeth Howell 	}
126407fe6a43SSeth Howell 
12652172c432STomasz Zawadzki 	SPDK_INFOLOG(virtio, "Target %u: unmap supported = %d\n",
126607fe6a43SSeth Howell 		     base->info.target, (int)base->info.unmap_supported);
126707fe6a43SSeth Howell 
126807fe6a43SSeth Howell 	return send_read_cap_10(base);
126907fe6a43SSeth Howell }
127007fe6a43SSeth Howell 
127107fe6a43SSeth Howell static int
127207fe6a43SSeth Howell process_scan_inquiry(struct virtio_scsi_scan_base *base)
127307fe6a43SSeth Howell {
127407fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &base->io_ctx.req;
127507fe6a43SSeth Howell 	struct spdk_scsi_cdb_inquiry *inquiry_cdb = (struct spdk_scsi_cdb_inquiry *)req->cdb;
127607fe6a43SSeth Howell 
127707fe6a43SSeth Howell 	if ((inquiry_cdb->evpd & 1) == 0) {
127807fe6a43SSeth Howell 		return process_scan_inquiry_standard(base);
127907fe6a43SSeth Howell 	}
128007fe6a43SSeth Howell 
128107fe6a43SSeth Howell 	switch (inquiry_cdb->page_code) {
128207fe6a43SSeth Howell 	case SPDK_SPC_VPD_SUPPORTED_VPD_PAGES:
128307fe6a43SSeth Howell 		return process_scan_inquiry_vpd_supported_vpd_pages(base);
128407fe6a43SSeth Howell 	case SPDK_SPC_VPD_BLOCK_THIN_PROVISION:
128507fe6a43SSeth Howell 		return process_scan_inquiry_vpd_block_thin_provision(base);
128607fe6a43SSeth Howell 	default:
12872172c432STomasz Zawadzki 		SPDK_DEBUGLOG(virtio, "Unexpected VPD page 0x%02x\n", inquiry_cdb->page_code);
128807fe6a43SSeth Howell 		return -1;
128907fe6a43SSeth Howell 	}
129007fe6a43SSeth Howell }
129107fe6a43SSeth Howell 
129207fe6a43SSeth Howell static void
1293aed3d21eSShuhei Matsumoto bdev_virtio_disk_notify_remove(struct virtio_scsi_disk *disk)
129407fe6a43SSeth Howell {
129507fe6a43SSeth Howell 	disk->removed = true;
129607fe6a43SSeth Howell 	spdk_bdev_close(disk->notify_desc);
129707fe6a43SSeth Howell }
129807fe6a43SSeth Howell 
1299aed3d21eSShuhei Matsumoto static void
1300aed3d21eSShuhei Matsumoto bdev_virtio_disk_notify_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
1301aed3d21eSShuhei Matsumoto 				 void *event_ctx)
1302aed3d21eSShuhei Matsumoto {
1303aed3d21eSShuhei Matsumoto 	switch (type) {
1304aed3d21eSShuhei Matsumoto 	case SPDK_BDEV_EVENT_REMOVE:
1305aed3d21eSShuhei Matsumoto 		bdev_virtio_disk_notify_remove(event_ctx);
1306aed3d21eSShuhei Matsumoto 		break;
1307aed3d21eSShuhei Matsumoto 	default:
1308aed3d21eSShuhei Matsumoto 		SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type);
1309aed3d21eSShuhei Matsumoto 		break;
1310aed3d21eSShuhei Matsumoto 	}
1311aed3d21eSShuhei Matsumoto }
1312aed3d21eSShuhei Matsumoto 
131307fe6a43SSeth Howell /* To be called only from the thread performing target scan */
131407fe6a43SSeth Howell static int
131507fe6a43SSeth Howell virtio_scsi_dev_add_tgt(struct virtio_scsi_dev *svdev, struct virtio_scsi_scan_info *info)
131607fe6a43SSeth Howell {
131707fe6a43SSeth Howell 	struct virtio_scsi_disk *disk;
131807fe6a43SSeth Howell 	struct spdk_bdev *bdev;
131907fe6a43SSeth Howell 	int rc;
132007fe6a43SSeth Howell 
132107fe6a43SSeth Howell 	TAILQ_FOREACH(disk, &svdev->luns, link) {
132207fe6a43SSeth Howell 		if (disk->info.target == info->target) {
132307fe6a43SSeth Howell 			/* Target is already attached and param change is not supported */
132407fe6a43SSeth Howell 			return 0;
132507fe6a43SSeth Howell 		}
132607fe6a43SSeth Howell 	}
132707fe6a43SSeth Howell 
132807fe6a43SSeth Howell 	if (info->block_size == 0 || info->num_blocks == 0) {
132907fe6a43SSeth Howell 		SPDK_ERRLOG("%s: invalid target %u: bs=%"PRIu32" blocks=%"PRIu64"\n",
133007fe6a43SSeth Howell 			    svdev->vdev.name, info->target, info->block_size, info->num_blocks);
133107fe6a43SSeth Howell 		return -EINVAL;
133207fe6a43SSeth Howell 	}
133307fe6a43SSeth Howell 
133407fe6a43SSeth Howell 	disk = calloc(1, sizeof(*disk));
133507fe6a43SSeth Howell 	if (disk == NULL) {
133607fe6a43SSeth Howell 		SPDK_ERRLOG("could not allocate disk\n");
133707fe6a43SSeth Howell 		return -ENOMEM;
133807fe6a43SSeth Howell 	}
133907fe6a43SSeth Howell 
134007fe6a43SSeth Howell 	disk->svdev = svdev;
134107fe6a43SSeth Howell 	memcpy(&disk->info, info, sizeof(*info));
134207fe6a43SSeth Howell 
134307fe6a43SSeth Howell 	bdev = &disk->bdev;
134407fe6a43SSeth Howell 	bdev->name = spdk_sprintf_alloc("%st%"PRIu8, svdev->vdev.name, info->target);
134507fe6a43SSeth Howell 	if (bdev->name == NULL) {
134607fe6a43SSeth Howell 		SPDK_ERRLOG("Couldn't alloc memory for the bdev name.\n");
134707fe6a43SSeth Howell 		free(disk);
134807fe6a43SSeth Howell 		return -ENOMEM;
134907fe6a43SSeth Howell 	}
135007fe6a43SSeth Howell 
135107fe6a43SSeth Howell 	bdev->product_name = "Virtio SCSI Disk";
135207fe6a43SSeth Howell 	bdev->write_cache = 0;
135307fe6a43SSeth Howell 	bdev->blocklen = disk->info.block_size;
135407fe6a43SSeth Howell 	bdev->blockcnt = disk->info.num_blocks;
135507fe6a43SSeth Howell 
135607fe6a43SSeth Howell 	bdev->ctxt = disk;
135707fe6a43SSeth Howell 	bdev->fn_table = &virtio_fn_table;
135807fe6a43SSeth Howell 	bdev->module = &virtio_scsi_if;
135907fe6a43SSeth Howell 
136007fe6a43SSeth Howell 	rc = spdk_bdev_register(&disk->bdev);
136107fe6a43SSeth Howell 	if (rc) {
136207fe6a43SSeth Howell 		SPDK_ERRLOG("Failed to register bdev name=%s\n", disk->bdev.name);
136307fe6a43SSeth Howell 		free(bdev->name);
136407fe6a43SSeth Howell 		free(disk);
136507fe6a43SSeth Howell 		return rc;
136607fe6a43SSeth Howell 	}
136707fe6a43SSeth Howell 
1368aed3d21eSShuhei Matsumoto 	rc = spdk_bdev_open_ext(bdev->name, false, bdev_virtio_disk_notify_event_cb,
1369aed3d21eSShuhei Matsumoto 				disk, &disk->notify_desc);
137007fe6a43SSeth Howell 	if (rc) {
137107fe6a43SSeth Howell 		assert(false);
137207fe6a43SSeth Howell 	}
137307fe6a43SSeth Howell 
137407fe6a43SSeth Howell 	TAILQ_INSERT_TAIL(&svdev->luns, disk, link);
137507fe6a43SSeth Howell 	return 0;
137607fe6a43SSeth Howell }
137707fe6a43SSeth Howell 
137807fe6a43SSeth Howell static int
137907fe6a43SSeth Howell process_read_cap_10(struct virtio_scsi_scan_base *base)
138007fe6a43SSeth Howell {
138107fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &base->io_ctx.req;
138207fe6a43SSeth Howell 	struct virtio_scsi_cmd_resp *resp = &base->io_ctx.resp;
138307fe6a43SSeth Howell 	uint64_t max_block;
138407fe6a43SSeth Howell 	uint32_t block_size;
138507fe6a43SSeth Howell 	uint8_t target_id = req->lun[1];
138607fe6a43SSeth Howell 	int rc;
138707fe6a43SSeth Howell 
138807fe6a43SSeth Howell 	if (resp->response != VIRTIO_SCSI_S_OK || resp->status != SPDK_SCSI_STATUS_GOOD) {
138907fe6a43SSeth Howell 		SPDK_ERRLOG("READ CAPACITY (10) failed for target %"PRIu8".\n", target_id);
139007fe6a43SSeth Howell 		return -1;
139107fe6a43SSeth Howell 	}
139207fe6a43SSeth Howell 
139307fe6a43SSeth Howell 	block_size = from_be32(base->payload + 4);
139407fe6a43SSeth Howell 	max_block = from_be32(base->payload);
139507fe6a43SSeth Howell 
139607fe6a43SSeth Howell 	if (max_block == 0xffffffff) {
139707fe6a43SSeth Howell 		return send_read_cap_16(base);
139807fe6a43SSeth Howell 	}
139907fe6a43SSeth Howell 
140007fe6a43SSeth Howell 	base->info.num_blocks = (uint64_t)max_block + 1;
140107fe6a43SSeth Howell 	base->info.block_size = block_size;
140207fe6a43SSeth Howell 
140307fe6a43SSeth Howell 	rc = virtio_scsi_dev_add_tgt(base->svdev, &base->info);
140407fe6a43SSeth Howell 	if (rc != 0) {
140507fe6a43SSeth Howell 		return rc;
140607fe6a43SSeth Howell 	}
140707fe6a43SSeth Howell 
140807fe6a43SSeth Howell 	return _virtio_scsi_dev_scan_next(base, 0);
140907fe6a43SSeth Howell }
141007fe6a43SSeth Howell 
141107fe6a43SSeth Howell static int
141207fe6a43SSeth Howell process_read_cap_16(struct virtio_scsi_scan_base *base)
141307fe6a43SSeth Howell {
141407fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &base->io_ctx.req;
141507fe6a43SSeth Howell 	struct virtio_scsi_cmd_resp *resp = &base->io_ctx.resp;
141607fe6a43SSeth Howell 	uint8_t target_id = req->lun[1];
141707fe6a43SSeth Howell 	int rc;
141807fe6a43SSeth Howell 
141907fe6a43SSeth Howell 	if (resp->response != VIRTIO_SCSI_S_OK || resp->status != SPDK_SCSI_STATUS_GOOD) {
142007fe6a43SSeth Howell 		SPDK_ERRLOG("READ CAPACITY (16) failed for target %"PRIu8".\n", target_id);
142107fe6a43SSeth Howell 		return -1;
142207fe6a43SSeth Howell 	}
142307fe6a43SSeth Howell 
142407fe6a43SSeth Howell 	base->info.num_blocks = from_be64(base->payload) + 1;
142507fe6a43SSeth Howell 	base->info.block_size = from_be32(base->payload + 8);
142607fe6a43SSeth Howell 	rc = virtio_scsi_dev_add_tgt(base->svdev, &base->info);
142707fe6a43SSeth Howell 	if (rc != 0) {
142807fe6a43SSeth Howell 		return rc;
142907fe6a43SSeth Howell 	}
143007fe6a43SSeth Howell 
143107fe6a43SSeth Howell 	return _virtio_scsi_dev_scan_next(base, 0);
143207fe6a43SSeth Howell }
143307fe6a43SSeth Howell 
143407fe6a43SSeth Howell static void
143507fe6a43SSeth Howell process_scan_resp(struct virtio_scsi_scan_base *base)
143607fe6a43SSeth Howell {
143707fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req = &base->io_ctx.req;
143807fe6a43SSeth Howell 	struct virtio_scsi_cmd_resp *resp = &base->io_ctx.resp;
143907fe6a43SSeth Howell 	int rc, sk, asc, ascq;
144007fe6a43SSeth Howell 	uint8_t target_id;
144107fe6a43SSeth Howell 
144207fe6a43SSeth Howell 	if (base->io_ctx.iov_req.iov_len < sizeof(struct virtio_scsi_cmd_req) ||
144307fe6a43SSeth Howell 	    base->io_ctx.iov_resp.iov_len < sizeof(struct virtio_scsi_cmd_resp)) {
144407fe6a43SSeth Howell 		SPDK_ERRLOG("Received target scan message with invalid length.\n");
144507fe6a43SSeth Howell 		_virtio_scsi_dev_scan_next(base, -EIO);
144607fe6a43SSeth Howell 		return;
144707fe6a43SSeth Howell 	}
144807fe6a43SSeth Howell 
144907fe6a43SSeth Howell 	get_scsi_status(resp, &sk, &asc, &ascq);
145007fe6a43SSeth Howell 	target_id = req->lun[1];
145107fe6a43SSeth Howell 
145207fe6a43SSeth Howell 	if (resp->response == VIRTIO_SCSI_S_BAD_TARGET ||
145307fe6a43SSeth Howell 	    resp->response == VIRTIO_SCSI_S_INCORRECT_LUN) {
145407fe6a43SSeth Howell 		_virtio_scsi_dev_scan_next(base, -ENODEV);
145507fe6a43SSeth Howell 		return;
145607fe6a43SSeth Howell 	}
145707fe6a43SSeth Howell 
145807fe6a43SSeth Howell 	if (resp->response != VIRTIO_SCSI_S_OK ||
145907fe6a43SSeth Howell 	    (resp->status == SPDK_SCSI_STATUS_CHECK_CONDITION &&
146007fe6a43SSeth Howell 	     sk != SPDK_SCSI_SENSE_ILLEGAL_REQUEST)) {
146107fe6a43SSeth Howell 		assert(base->retries > 0);
146207fe6a43SSeth Howell 		base->retries--;
146307fe6a43SSeth Howell 		if (base->retries == 0) {
146407fe6a43SSeth Howell 			SPDK_NOTICELOG("Target %"PRIu8" is present, but unavailable.\n", target_id);
14652172c432STomasz Zawadzki 			SPDK_LOGDUMP(virtio, "CDB", req->cdb, sizeof(req->cdb));
14662172c432STomasz Zawadzki 			SPDK_LOGDUMP(virtio, "SENSE DATA", resp->sense, sizeof(resp->sense));
146707fe6a43SSeth Howell 			_virtio_scsi_dev_scan_next(base, -EBUSY);
146807fe6a43SSeth Howell 			return;
146907fe6a43SSeth Howell 		}
147007fe6a43SSeth Howell 
147107fe6a43SSeth Howell 		/* resend the same request */
147207fe6a43SSeth Howell 		rc = send_scan_io(base);
147307fe6a43SSeth Howell 		if (rc != 0) {
147407fe6a43SSeth Howell 			/* Let response poller do the resend */
147507fe6a43SSeth Howell 		}
147607fe6a43SSeth Howell 		return;
147707fe6a43SSeth Howell 	}
147807fe6a43SSeth Howell 
147907fe6a43SSeth Howell 	base->retries = SCAN_REQUEST_RETRIES;
148007fe6a43SSeth Howell 
148107fe6a43SSeth Howell 	switch (req->cdb[0]) {
148207fe6a43SSeth Howell 	case SPDK_SPC_INQUIRY:
148307fe6a43SSeth Howell 		rc = process_scan_inquiry(base);
148407fe6a43SSeth Howell 		break;
148507fe6a43SSeth Howell 	case SPDK_SPC_TEST_UNIT_READY:
148607fe6a43SSeth Howell 		rc = process_scan_test_unit_ready(base);
148707fe6a43SSeth Howell 		break;
148807fe6a43SSeth Howell 	case SPDK_SBC_START_STOP_UNIT:
148907fe6a43SSeth Howell 		rc = process_scan_start_stop_unit(base);
149007fe6a43SSeth Howell 		break;
149107fe6a43SSeth Howell 	case SPDK_SBC_READ_CAPACITY_10:
149207fe6a43SSeth Howell 		rc = process_read_cap_10(base);
149307fe6a43SSeth Howell 		break;
149407fe6a43SSeth Howell 	case SPDK_SPC_SERVICE_ACTION_IN_16:
149507fe6a43SSeth Howell 		rc = process_read_cap_16(base);
149607fe6a43SSeth Howell 		break;
149707fe6a43SSeth Howell 	default:
149807fe6a43SSeth Howell 		SPDK_ERRLOG("Received invalid target scan message: cdb[0] = %"PRIu8".\n", req->cdb[0]);
149907fe6a43SSeth Howell 		rc = -1;
150007fe6a43SSeth Howell 		break;
150107fe6a43SSeth Howell 	}
150207fe6a43SSeth Howell 
150307fe6a43SSeth Howell 	if (rc != 0) {
150407fe6a43SSeth Howell 		if (base->needs_resend) {
150507fe6a43SSeth Howell 			return; /* Let response poller do the resend */
150607fe6a43SSeth Howell 		}
150707fe6a43SSeth Howell 
150807fe6a43SSeth Howell 		_virtio_scsi_dev_scan_next(base, rc);
150907fe6a43SSeth Howell 	}
151007fe6a43SSeth Howell }
151107fe6a43SSeth Howell 
151207fe6a43SSeth Howell static int
151307fe6a43SSeth Howell _virtio_scsi_dev_scan_next(struct virtio_scsi_scan_base *base, int rc)
151407fe6a43SSeth Howell {
151507fe6a43SSeth Howell 	struct virtio_scsi_scan_info *next;
151607fe6a43SSeth Howell 	struct virtio_scsi_disk *disk;
151707fe6a43SSeth Howell 	uint8_t target_id;
151807fe6a43SSeth Howell 
151907fe6a43SSeth Howell 	if (base->full_scan) {
152007fe6a43SSeth Howell 		if (rc != 0) {
152107fe6a43SSeth Howell 			disk = virtio_scsi_dev_get_disk_by_id(base->svdev,
152207fe6a43SSeth Howell 							      base->info.target);
152307fe6a43SSeth Howell 			if (disk != NULL) {
152407fe6a43SSeth Howell 				spdk_bdev_unregister(&disk->bdev, NULL, NULL);
152507fe6a43SSeth Howell 			}
152607fe6a43SSeth Howell 		}
152707fe6a43SSeth Howell 
152807fe6a43SSeth Howell 		target_id = base->info.target + 1;
152907fe6a43SSeth Howell 		if (target_id < BDEV_VIRTIO_MAX_TARGET) {
153007fe6a43SSeth Howell 			_virtio_scsi_dev_scan_tgt(base, target_id);
153107fe6a43SSeth Howell 			return 0;
153207fe6a43SSeth Howell 		}
153307fe6a43SSeth Howell 
153407fe6a43SSeth Howell 		base->full_scan = false;
153507fe6a43SSeth Howell 	}
153607fe6a43SSeth Howell 
153707fe6a43SSeth Howell 	next = TAILQ_FIRST(&base->scan_queue);
153807fe6a43SSeth Howell 	if (next == NULL) {
153907fe6a43SSeth Howell 		_virtio_scsi_dev_scan_finish(base, 0);
154007fe6a43SSeth Howell 		return 0;
154107fe6a43SSeth Howell 	}
154207fe6a43SSeth Howell 
154307fe6a43SSeth Howell 	TAILQ_REMOVE(&base->scan_queue, next, tailq);
154407fe6a43SSeth Howell 	target_id = next->target;
154507fe6a43SSeth Howell 	free(next);
154607fe6a43SSeth Howell 
154707fe6a43SSeth Howell 	_virtio_scsi_dev_scan_tgt(base, target_id);
154807fe6a43SSeth Howell 	return 0;
154907fe6a43SSeth Howell }
155007fe6a43SSeth Howell 
155107fe6a43SSeth Howell static int
155207fe6a43SSeth Howell _virtio_scsi_dev_scan_init(struct virtio_scsi_dev *svdev)
155307fe6a43SSeth Howell {
155407fe6a43SSeth Howell 	struct virtio_scsi_scan_base *base;
155507fe6a43SSeth Howell 	struct spdk_io_channel *io_ch;
155607fe6a43SSeth Howell 	struct virtio_scsi_io_ctx *io_ctx;
155707fe6a43SSeth Howell 	struct virtio_scsi_cmd_req *req;
155807fe6a43SSeth Howell 	struct virtio_scsi_cmd_resp *resp;
155907fe6a43SSeth Howell 
156007fe6a43SSeth Howell 	io_ch = spdk_get_io_channel(svdev);
156107fe6a43SSeth Howell 	if (io_ch == NULL) {
156207fe6a43SSeth Howell 		return -EBUSY;
156307fe6a43SSeth Howell 	}
156407fe6a43SSeth Howell 
156507fe6a43SSeth Howell 	base = spdk_zmalloc(sizeof(*base), 64, NULL,
156607fe6a43SSeth Howell 			    SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
156707fe6a43SSeth Howell 	if (base == NULL) {
156807fe6a43SSeth Howell 		SPDK_ERRLOG("couldn't allocate memory for scsi target scan.\n");
156907fe6a43SSeth Howell 		return -ENOMEM;
157007fe6a43SSeth Howell 	}
157107fe6a43SSeth Howell 
157207fe6a43SSeth Howell 	base->svdev = svdev;
157307fe6a43SSeth Howell 
157407fe6a43SSeth Howell 	base->channel = spdk_io_channel_get_ctx(io_ch);
157507fe6a43SSeth Howell 	TAILQ_INIT(&base->scan_queue);
157607fe6a43SSeth Howell 	svdev->scan_ctx = base;
157707fe6a43SSeth Howell 
157807fe6a43SSeth Howell 	base->iov.iov_base = base->payload;
157907fe6a43SSeth Howell 	io_ctx = &base->io_ctx;
158007fe6a43SSeth Howell 	req = &io_ctx->req;
158107fe6a43SSeth Howell 	resp = &io_ctx->resp;
158207fe6a43SSeth Howell 	io_ctx->iov_req.iov_base = req;
158307fe6a43SSeth Howell 	io_ctx->iov_req.iov_len = sizeof(*req);
158407fe6a43SSeth Howell 	io_ctx->iov_resp.iov_base = resp;
158507fe6a43SSeth Howell 	io_ctx->iov_resp.iov_len = sizeof(*resp);
158607fe6a43SSeth Howell 
158707fe6a43SSeth Howell 	base->retries = SCAN_REQUEST_RETRIES;
158807fe6a43SSeth Howell 	return 0;
158907fe6a43SSeth Howell }
159007fe6a43SSeth Howell 
159107fe6a43SSeth Howell static void
159207fe6a43SSeth Howell _virtio_scsi_dev_scan_tgt(struct virtio_scsi_scan_base *base, uint8_t target)
159307fe6a43SSeth Howell {
159407fe6a43SSeth Howell 	int rc;
159507fe6a43SSeth Howell 
159607fe6a43SSeth Howell 	memset(&base->info, 0, sizeof(base->info));
159707fe6a43SSeth Howell 	base->info.target = target;
159807fe6a43SSeth Howell 
159907fe6a43SSeth Howell 	rc = send_inquiry(base);
160007fe6a43SSeth Howell 	if (rc) {
160107fe6a43SSeth Howell 		/* Let response poller do the resend */
160207fe6a43SSeth Howell 	}
160307fe6a43SSeth Howell }
160407fe6a43SSeth Howell 
160507fe6a43SSeth Howell static int
160607fe6a43SSeth Howell virtio_scsi_dev_scan(struct virtio_scsi_dev *svdev, bdev_virtio_create_cb cb_fn,
160707fe6a43SSeth Howell 		     void *cb_arg)
160807fe6a43SSeth Howell {
160907fe6a43SSeth Howell 	struct virtio_scsi_scan_base *base;
161007fe6a43SSeth Howell 	struct virtio_scsi_scan_info *tgt, *next_tgt;
161107fe6a43SSeth Howell 	int rc;
161207fe6a43SSeth Howell 
161307fe6a43SSeth Howell 	if (svdev->scan_ctx) {
161407fe6a43SSeth Howell 		if (svdev->scan_ctx->full_scan) {
161507fe6a43SSeth Howell 			return -EEXIST;
161607fe6a43SSeth Howell 		}
161707fe6a43SSeth Howell 
161807fe6a43SSeth Howell 		/* We're about to start a full rescan, so there's no need
161907fe6a43SSeth Howell 		 * to scan particular targets afterwards.
162007fe6a43SSeth Howell 		 */
162107fe6a43SSeth Howell 		TAILQ_FOREACH_SAFE(tgt, &svdev->scan_ctx->scan_queue, tailq, next_tgt) {
162207fe6a43SSeth Howell 			TAILQ_REMOVE(&svdev->scan_ctx->scan_queue, tgt, tailq);
162307fe6a43SSeth Howell 			free(tgt);
162407fe6a43SSeth Howell 		}
162507fe6a43SSeth Howell 
162607fe6a43SSeth Howell 		svdev->scan_ctx->cb_fn = cb_fn;
162707fe6a43SSeth Howell 		svdev->scan_ctx->cb_arg = cb_arg;
162807fe6a43SSeth Howell 		svdev->scan_ctx->restart = true;
162907fe6a43SSeth Howell 		return 0;
163007fe6a43SSeth Howell 	}
163107fe6a43SSeth Howell 
163207fe6a43SSeth Howell 	rc = _virtio_scsi_dev_scan_init(svdev);
163307fe6a43SSeth Howell 	if (rc != 0) {
163407fe6a43SSeth Howell 		return rc;
163507fe6a43SSeth Howell 	}
163607fe6a43SSeth Howell 
163707fe6a43SSeth Howell 	base = svdev->scan_ctx;
163807fe6a43SSeth Howell 	base->cb_fn = cb_fn;
163907fe6a43SSeth Howell 	base->cb_arg = cb_arg;
164007fe6a43SSeth Howell 	base->full_scan = true;
164107fe6a43SSeth Howell 
164207fe6a43SSeth Howell 	_virtio_scsi_dev_scan_tgt(base, 0);
164307fe6a43SSeth Howell 	return 0;
164407fe6a43SSeth Howell }
164507fe6a43SSeth Howell 
164607fe6a43SSeth Howell static int
164707fe6a43SSeth Howell virtio_scsi_dev_scan_tgt(struct virtio_scsi_dev *svdev, uint8_t target)
164807fe6a43SSeth Howell {
164907fe6a43SSeth Howell 	struct virtio_scsi_scan_base *base;
165007fe6a43SSeth Howell 	struct virtio_scsi_scan_info *info;
165107fe6a43SSeth Howell 	int rc;
165207fe6a43SSeth Howell 
165307fe6a43SSeth Howell 	base = svdev->scan_ctx;
165407fe6a43SSeth Howell 	if (base) {
165507fe6a43SSeth Howell 		info = calloc(1, sizeof(*info));
165607fe6a43SSeth Howell 		if (info == NULL) {
165707fe6a43SSeth Howell 			SPDK_ERRLOG("calloc failed\n");
165807fe6a43SSeth Howell 			return -ENOMEM;
165907fe6a43SSeth Howell 		}
166007fe6a43SSeth Howell 
166107fe6a43SSeth Howell 		info->target = target;
166207fe6a43SSeth Howell 		TAILQ_INSERT_TAIL(&base->scan_queue, info, tailq);
166307fe6a43SSeth Howell 		return 0;
166407fe6a43SSeth Howell 	}
166507fe6a43SSeth Howell 
166607fe6a43SSeth Howell 	rc = _virtio_scsi_dev_scan_init(svdev);
166707fe6a43SSeth Howell 	if (rc != 0) {
166807fe6a43SSeth Howell 		return rc;
166907fe6a43SSeth Howell 	}
167007fe6a43SSeth Howell 
167107fe6a43SSeth Howell 	base = svdev->scan_ctx;
167207fe6a43SSeth Howell 	base->full_scan = true;
167307fe6a43SSeth Howell 	_virtio_scsi_dev_scan_tgt(base, target);
167407fe6a43SSeth Howell 	return 0;
167507fe6a43SSeth Howell }
167607fe6a43SSeth Howell 
167707fe6a43SSeth Howell static int
167807fe6a43SSeth Howell bdev_virtio_initialize(void)
167907fe6a43SSeth Howell {
168007fe6a43SSeth Howell 	return 0;
168107fe6a43SSeth Howell }
168207fe6a43SSeth Howell 
168307fe6a43SSeth Howell static void
168407fe6a43SSeth Howell _virtio_scsi_dev_unregister_cb(void *io_device)
168507fe6a43SSeth Howell {
168607fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev = io_device;
168707fe6a43SSeth Howell 	struct virtio_dev *vdev = &svdev->vdev;
168807fe6a43SSeth Howell 	bool finish_module;
168907fe6a43SSeth Howell 	bdev_virtio_remove_cb remove_cb;
169007fe6a43SSeth Howell 	void *remove_ctx;
169107fe6a43SSeth Howell 
169207fe6a43SSeth Howell 	assert(spdk_ring_count(svdev->ctrlq_ring) == 0);
169307fe6a43SSeth Howell 	spdk_ring_free(svdev->ctrlq_ring);
169407fe6a43SSeth Howell 	spdk_poller_unregister(&svdev->mgmt_poller);
169507fe6a43SSeth Howell 
169607fe6a43SSeth Howell 	virtio_dev_release_queue(vdev, VIRTIO_SCSI_EVENTQ);
169707fe6a43SSeth Howell 	virtio_dev_release_queue(vdev, VIRTIO_SCSI_CONTROLQ);
169807fe6a43SSeth Howell 
169907fe6a43SSeth Howell 	virtio_dev_stop(vdev);
170007fe6a43SSeth Howell 	virtio_dev_destruct(vdev);
170107fe6a43SSeth Howell 
170207fe6a43SSeth Howell 	pthread_mutex_lock(&g_virtio_scsi_mutex);
170307fe6a43SSeth Howell 	TAILQ_REMOVE(&g_virtio_scsi_devs, svdev, tailq);
170407fe6a43SSeth Howell 	pthread_mutex_unlock(&g_virtio_scsi_mutex);
170507fe6a43SSeth Howell 
170607fe6a43SSeth Howell 	remove_cb = svdev->remove_cb;
170707fe6a43SSeth Howell 	remove_ctx = svdev->remove_ctx;
170807fe6a43SSeth Howell 	spdk_free(svdev->eventq_ios);
170907fe6a43SSeth Howell 	free(svdev);
171007fe6a43SSeth Howell 
171107fe6a43SSeth Howell 	if (remove_cb) {
171207fe6a43SSeth Howell 		remove_cb(remove_ctx, 0);
171307fe6a43SSeth Howell 	}
171407fe6a43SSeth Howell 
171507fe6a43SSeth Howell 	finish_module = TAILQ_EMPTY(&g_virtio_scsi_devs);
171607fe6a43SSeth Howell 
171707fe6a43SSeth Howell 	if (g_bdev_virtio_finish && finish_module) {
1718511fe155STomasz Zawadzki 		spdk_bdev_module_fini_done();
171907fe6a43SSeth Howell 	}
172007fe6a43SSeth Howell }
172107fe6a43SSeth Howell 
172207fe6a43SSeth Howell static void
172307fe6a43SSeth Howell virtio_scsi_dev_unregister_cb(void *io_device)
172407fe6a43SSeth Howell {
172507fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev = io_device;
172607fe6a43SSeth Howell 	struct spdk_thread *thread;
172707fe6a43SSeth Howell 
172807fe6a43SSeth Howell 	thread = virtio_dev_queue_get_thread(&svdev->vdev, VIRTIO_SCSI_CONTROLQ);
172907fe6a43SSeth Howell 	spdk_thread_send_msg(thread, _virtio_scsi_dev_unregister_cb, io_device);
173007fe6a43SSeth Howell }
173107fe6a43SSeth Howell 
173207fe6a43SSeth Howell static void
173307fe6a43SSeth Howell virtio_scsi_dev_remove(struct virtio_scsi_dev *svdev,
173407fe6a43SSeth Howell 		       bdev_virtio_remove_cb cb_fn, void *cb_arg)
173507fe6a43SSeth Howell {
173607fe6a43SSeth Howell 	struct virtio_scsi_disk *disk, *disk_tmp;
173707fe6a43SSeth Howell 	bool do_remove = true;
173807fe6a43SSeth Howell 
173907fe6a43SSeth Howell 	if (svdev->removed) {
174007fe6a43SSeth Howell 		if (cb_fn) {
174107fe6a43SSeth Howell 			cb_fn(cb_arg, -EBUSY);
174207fe6a43SSeth Howell 		}
174307fe6a43SSeth Howell 		return;
174407fe6a43SSeth Howell 	}
174507fe6a43SSeth Howell 
174607fe6a43SSeth Howell 	svdev->remove_cb = cb_fn;
174707fe6a43SSeth Howell 	svdev->remove_ctx = cb_arg;
174807fe6a43SSeth Howell 	svdev->removed = true;
174907fe6a43SSeth Howell 
175007fe6a43SSeth Howell 	if (svdev->scan_ctx) {
175107fe6a43SSeth Howell 		/* The removal will continue after we receive a pending scan I/O. */
175207fe6a43SSeth Howell 		return;
175307fe6a43SSeth Howell 	}
175407fe6a43SSeth Howell 
175507fe6a43SSeth Howell 	TAILQ_FOREACH_SAFE(disk, &svdev->luns, link, disk_tmp) {
175607fe6a43SSeth Howell 		if (!disk->removed) {
175707fe6a43SSeth Howell 			spdk_bdev_unregister(&disk->bdev, NULL, NULL);
175807fe6a43SSeth Howell 		}
175907fe6a43SSeth Howell 		do_remove = false;
176007fe6a43SSeth Howell 	}
176107fe6a43SSeth Howell 
176207fe6a43SSeth Howell 	if (do_remove) {
176307fe6a43SSeth Howell 		spdk_io_device_unregister(svdev, virtio_scsi_dev_unregister_cb);
176407fe6a43SSeth Howell 	}
176507fe6a43SSeth Howell }
176607fe6a43SSeth Howell 
176707fe6a43SSeth Howell static void
176807fe6a43SSeth Howell bdev_virtio_finish(void)
176907fe6a43SSeth Howell {
177007fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev, *next;
177107fe6a43SSeth Howell 
177207fe6a43SSeth Howell 	g_bdev_virtio_finish = true;
177307fe6a43SSeth Howell 
177407fe6a43SSeth Howell 	pthread_mutex_lock(&g_virtio_scsi_mutex);
177507fe6a43SSeth Howell 	if (TAILQ_EMPTY(&g_virtio_scsi_devs)) {
177607fe6a43SSeth Howell 		pthread_mutex_unlock(&g_virtio_scsi_mutex);
1777511fe155STomasz Zawadzki 		spdk_bdev_module_fini_done();
177807fe6a43SSeth Howell 		return;
177907fe6a43SSeth Howell 	}
178007fe6a43SSeth Howell 
178107fe6a43SSeth Howell 	/* Defer module finish until all controllers are removed. */
178207fe6a43SSeth Howell 	TAILQ_FOREACH_SAFE(svdev, &g_virtio_scsi_devs, tailq, next) {
178307fe6a43SSeth Howell 		virtio_scsi_dev_remove(svdev, NULL, NULL);
178407fe6a43SSeth Howell 	}
178507fe6a43SSeth Howell 	pthread_mutex_unlock(&g_virtio_scsi_mutex);
178607fe6a43SSeth Howell }
178707fe6a43SSeth Howell 
178807fe6a43SSeth Howell int
178907fe6a43SSeth Howell bdev_virtio_user_scsi_dev_create(const char *base_name, const char *path,
179007fe6a43SSeth Howell 				 unsigned num_queues, unsigned queue_size,
179107fe6a43SSeth Howell 				 bdev_virtio_create_cb cb_fn, void *cb_arg)
179207fe6a43SSeth Howell {
179307fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev;
179407fe6a43SSeth Howell 	int rc;
179507fe6a43SSeth Howell 
179607fe6a43SSeth Howell 	svdev = virtio_user_scsi_dev_create(base_name, path, num_queues, queue_size);
179707fe6a43SSeth Howell 	if (svdev == NULL) {
179807fe6a43SSeth Howell 		return -1;
179907fe6a43SSeth Howell 	}
180007fe6a43SSeth Howell 
180107fe6a43SSeth Howell 	rc = virtio_scsi_dev_scan(svdev, cb_fn, cb_arg);
180207fe6a43SSeth Howell 	if (rc) {
180307fe6a43SSeth Howell 		virtio_scsi_dev_remove(svdev, NULL, NULL);
180407fe6a43SSeth Howell 	}
180507fe6a43SSeth Howell 
180607fe6a43SSeth Howell 	return rc;
180707fe6a43SSeth Howell }
180807fe6a43SSeth Howell 
18098b260d5cSChangpeng Liu int
18108b260d5cSChangpeng Liu bdev_vfio_user_scsi_dev_create(const char *base_name, const char *path,
18118b260d5cSChangpeng Liu 			       bdev_virtio_create_cb cb_fn, void *cb_arg)
18128b260d5cSChangpeng Liu {
18138b260d5cSChangpeng Liu 	struct virtio_scsi_dev *svdev;
18148b260d5cSChangpeng Liu 	uint32_t num_queues = 0;
18158b260d5cSChangpeng Liu 	int rc;
18168b260d5cSChangpeng Liu 
18178b260d5cSChangpeng Liu 	svdev = calloc(1, sizeof(*svdev));
18188b260d5cSChangpeng Liu 	if (svdev == NULL) {
18198b260d5cSChangpeng Liu 		SPDK_ERRLOG("calloc failed for virtio device %s: %s\n", base_name, path);
18208b260d5cSChangpeng Liu 		return -ENOMEM;
18218b260d5cSChangpeng Liu 	}
18228b260d5cSChangpeng Liu 
18238b260d5cSChangpeng Liu 	rc = virtio_vfio_user_dev_init(&svdev->vdev, base_name, path);
18248b260d5cSChangpeng Liu 	if (rc != 0) {
18258b260d5cSChangpeng Liu 		SPDK_ERRLOG("Failed to create %s as virtio device\n", path);
18268b260d5cSChangpeng Liu 		free(svdev);
18278b260d5cSChangpeng Liu 		return -EFAULT;
18288b260d5cSChangpeng Liu 	}
18298b260d5cSChangpeng Liu 
18308b260d5cSChangpeng Liu 	rc = virtio_dev_read_dev_config(&svdev->vdev, offsetof(struct virtio_scsi_config, num_queues),
18318b260d5cSChangpeng Liu 					&num_queues, sizeof(num_queues));
18328b260d5cSChangpeng Liu 	if (rc) {
18338b260d5cSChangpeng Liu 		SPDK_ERRLOG("%s: config read failed: %s\n", base_name, spdk_strerror(-rc));
18348b260d5cSChangpeng Liu 		virtio_dev_destruct(&svdev->vdev);
18358b260d5cSChangpeng Liu 		free(svdev);
18368b260d5cSChangpeng Liu 		return rc;
18378b260d5cSChangpeng Liu 	}
18388b260d5cSChangpeng Liu 
18398b260d5cSChangpeng Liu 	if (num_queues < SPDK_VIRTIO_SCSI_QUEUE_NUM_FIXED) {
18408b260d5cSChangpeng Liu 		SPDK_ERRLOG("%s: invalid num_queues %u\n", base_name, num_queues);
18418b260d5cSChangpeng Liu 		virtio_dev_destruct(&svdev->vdev);
18428b260d5cSChangpeng Liu 		free(svdev);
18438b260d5cSChangpeng Liu 		return -EINVAL;
18448b260d5cSChangpeng Liu 	}
18458b260d5cSChangpeng Liu 
18468b260d5cSChangpeng Liu 	rc = virtio_scsi_dev_init(svdev, num_queues, VIRTIO_SCSI_DEV_SUPPORTED_FEATURES);
18478b260d5cSChangpeng Liu 	if (rc != 0) {
18488b260d5cSChangpeng Liu 		virtio_dev_destruct(&svdev->vdev);
18498b260d5cSChangpeng Liu 		free(svdev);
18508b260d5cSChangpeng Liu 		return -EFAULT;
18518b260d5cSChangpeng Liu 	}
18528b260d5cSChangpeng Liu 
18538b260d5cSChangpeng Liu 	rc = virtio_scsi_dev_scan(svdev, cb_fn, cb_arg);
18548b260d5cSChangpeng Liu 	if (rc) {
18558b260d5cSChangpeng Liu 		virtio_scsi_dev_remove(svdev, NULL, NULL);
18568b260d5cSChangpeng Liu 	}
18578b260d5cSChangpeng Liu 
18588b260d5cSChangpeng Liu 	return rc;
18598b260d5cSChangpeng Liu }
18608b260d5cSChangpeng Liu 
186107fe6a43SSeth Howell struct bdev_virtio_pci_dev_create_ctx {
186207fe6a43SSeth Howell 	const char *name;
186307fe6a43SSeth Howell 	bdev_virtio_create_cb cb_fn;
186407fe6a43SSeth Howell 	void *cb_arg;
186507fe6a43SSeth Howell };
186607fe6a43SSeth Howell 
186707fe6a43SSeth Howell static int
186807fe6a43SSeth Howell bdev_virtio_pci_scsi_dev_create_cb(struct virtio_pci_ctx *pci_ctx, void *ctx)
186907fe6a43SSeth Howell {
187007fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev;
187107fe6a43SSeth Howell 	struct bdev_virtio_pci_dev_create_ctx *create_ctx = ctx;
187207fe6a43SSeth Howell 	int rc;
187307fe6a43SSeth Howell 
187407fe6a43SSeth Howell 	svdev = virtio_pci_scsi_dev_create(create_ctx->name, pci_ctx);
187507fe6a43SSeth Howell 	if (svdev == NULL) {
187607fe6a43SSeth Howell 		return -1;
187707fe6a43SSeth Howell 	}
187807fe6a43SSeth Howell 
187907fe6a43SSeth Howell 	rc = virtio_scsi_dev_scan(svdev, create_ctx->cb_fn, create_ctx->cb_arg);
188007fe6a43SSeth Howell 	if (rc) {
18817ef6d8ddSJin Yu 		svdev->vdev.ctx = NULL;
188207fe6a43SSeth Howell 		virtio_scsi_dev_remove(svdev, NULL, NULL);
188307fe6a43SSeth Howell 	}
188407fe6a43SSeth Howell 
188507fe6a43SSeth Howell 	return rc;
188607fe6a43SSeth Howell }
188707fe6a43SSeth Howell 
188807fe6a43SSeth Howell int
188907fe6a43SSeth Howell bdev_virtio_pci_scsi_dev_create(const char *name, struct spdk_pci_addr *pci_addr,
189007fe6a43SSeth Howell 				bdev_virtio_create_cb cb_fn, void *cb_arg)
189107fe6a43SSeth Howell {
189207fe6a43SSeth Howell 	struct bdev_virtio_pci_dev_create_ctx create_ctx;
189307fe6a43SSeth Howell 
189407fe6a43SSeth Howell 	create_ctx.name = name;
189507fe6a43SSeth Howell 	create_ctx.cb_fn = cb_fn;
189607fe6a43SSeth Howell 	create_ctx.cb_arg = cb_arg;
189707fe6a43SSeth Howell 
189807fe6a43SSeth Howell 	return virtio_pci_dev_attach(bdev_virtio_pci_scsi_dev_create_cb, &create_ctx,
18994c890c31SJin Yu 				     VIRTIO_ID_SCSI, pci_addr);
190007fe6a43SSeth Howell }
190107fe6a43SSeth Howell 
190207fe6a43SSeth Howell int
190307fe6a43SSeth Howell bdev_virtio_scsi_dev_remove(const char *name, bdev_virtio_remove_cb cb_fn, void *cb_arg)
190407fe6a43SSeth Howell {
190507fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev;
190607fe6a43SSeth Howell 
190707fe6a43SSeth Howell 	pthread_mutex_lock(&g_virtio_scsi_mutex);
190807fe6a43SSeth Howell 	TAILQ_FOREACH(svdev, &g_virtio_scsi_devs, tailq) {
190907fe6a43SSeth Howell 		if (strcmp(svdev->vdev.name, name) == 0) {
191007fe6a43SSeth Howell 			break;
191107fe6a43SSeth Howell 		}
191207fe6a43SSeth Howell 	}
191307fe6a43SSeth Howell 
191407fe6a43SSeth Howell 	if (svdev == NULL) {
191507fe6a43SSeth Howell 		pthread_mutex_unlock(&g_virtio_scsi_mutex);
191607fe6a43SSeth Howell 		SPDK_ERRLOG("Cannot find Virtio-SCSI device named '%s'\n", name);
191707fe6a43SSeth Howell 		return -ENODEV;
191807fe6a43SSeth Howell 	}
191907fe6a43SSeth Howell 
192007fe6a43SSeth Howell 	virtio_scsi_dev_remove(svdev, cb_fn, cb_arg);
192107fe6a43SSeth Howell 	pthread_mutex_unlock(&g_virtio_scsi_mutex);
192207fe6a43SSeth Howell 
192307fe6a43SSeth Howell 	return 0;
192407fe6a43SSeth Howell }
192507fe6a43SSeth Howell 
192607fe6a43SSeth Howell void
192707fe6a43SSeth Howell bdev_virtio_scsi_dev_list(struct spdk_json_write_ctx *w)
192807fe6a43SSeth Howell {
192907fe6a43SSeth Howell 	struct virtio_scsi_dev *svdev;
193007fe6a43SSeth Howell 
193107fe6a43SSeth Howell 	spdk_json_write_array_begin(w);
193207fe6a43SSeth Howell 
193307fe6a43SSeth Howell 	pthread_mutex_lock(&g_virtio_scsi_mutex);
193407fe6a43SSeth Howell 	TAILQ_FOREACH(svdev, &g_virtio_scsi_devs, tailq) {
193507fe6a43SSeth Howell 		spdk_json_write_object_begin(w);
193607fe6a43SSeth Howell 
193707fe6a43SSeth Howell 		spdk_json_write_named_string(w, "name", svdev->vdev.name);
193807fe6a43SSeth Howell 
193907fe6a43SSeth Howell 		virtio_dev_dump_json_info(&svdev->vdev, w);
194007fe6a43SSeth Howell 
194107fe6a43SSeth Howell 		spdk_json_write_object_end(w);
194207fe6a43SSeth Howell 	}
194307fe6a43SSeth Howell 	pthread_mutex_unlock(&g_virtio_scsi_mutex);
194407fe6a43SSeth Howell 
194507fe6a43SSeth Howell 	spdk_json_write_array_end(w);
194607fe6a43SSeth Howell }
194707fe6a43SSeth Howell 
19482172c432STomasz Zawadzki SPDK_LOG_REGISTER_COMPONENT(virtio)
1949