Thread (22 messages) 22 messages, 5 authors, 2017-07-19

Re: [PATCH v3] examples/vhost: introduce a new vhost-user-scsi sample application

From: Wodkowski, PawelX <hidden>
Date: 2017-07-12 14:49:42

Hi, sorry for late review but just spotted this patch.
Please see my comments
quoted hunk ↗ jump to hunk
diff --git a/examples/vhost_scsi/scsi.c b/examples/vhost_scsi/scsi.c
new file mode 100644
index 0000000..08dfe61
--- /dev/null
+++ b/examples/vhost_scsi/scsi.c
@@ -0,0 +1,507 @@
+static int
+vhost_hex2bin(char ch)
+{
+	if ((ch >= '0') && (ch <= '9'))
+		return ch - '0';
+	ch = tolower(ch);
+	if ((ch >= 'a') && (ch <= 'f'))
+		return ch - 'a' + 10;
+	return (int)ch;
+}
consider using strtol()
+
+int
+vhost_bdev_process_scsi_commands(struct vhost_block_dev *bdev,
+				 struct vhost_scsi_task *task)
+{
+	int len;
+	uint8_t *data;
+	uint64_t fmt_lun = 0;
+	const uint8_t *lun;
+	uint8_t *cdb = (uint8_t *)task->req->cdb;
+
+	lun = (const uint8_t *)task->req->lun;
+	/* only 1 LUN supported */
+	if (lun[0] != 1 || lun[1] >= 1)
+		return -1;
+
+	switch (cdb[0]) {
+	case SPC_INQUIRY:
+		len = vhost_bdev_scsi_inquiry_command(bdev, task);
+		task->data_len = len;
+		break;
+	case SPC_REPORT_LUNS:
+		data = (uint8_t *)task->iovs[0].iov_base;
+		fmt_lun |= (0x0ULL & 0x00ffULL) << 48;
Please provide a description for this magical formula for non-SCSI proficient community.
+		to_be64((void *)&data[8], fmt_lun);
+		to_be32((void *)data, 8);
+		task->data_len = 16;
+		scsi_task_set_status(task, SCSI_STATUS_GOOD, 0, 0, 0);
+		break;
+	case SPC_MODE_SELECT_6:
+	case SPC_MODE_SELECT_10:
+		/* TODO: Add SCSI Commands support */
+		scsi_task_set_status(task, SCSI_STATUS_GOOD, 0, 0, 0);
+		break;
+	case SPC_MODE_SENSE_6:
+	case SPC_MODE_SENSE_10:
+		/* TODO: Add SCSI Commands support */
+		scsi_task_set_status(task, SCSI_STATUS_GOOD, 0, 0, 0);
+		break;
If those cases are mandatory they should be implemented if not removing TODO would be better than informing that
there is something to do.
quoted hunk ↗ jump to hunk
+	case SPC_TEST_UNIT_READY:
+		scsi_task_set_status(task, SCSI_STATUS_GOOD, 0, 0, 0);
+		break;
+	default:
+		len = vhost_bdev_scsi_process_block(bdev, task);
+		task->data_len = len;
+	}
+
+	return 0;
+}
diff --git a/examples/vhost_scsi/scsi_spec.h b/examples/vhost_scsi/scsi_spec.h
new file mode 100644
index 0000000..0474d92
--- /dev/null
+++ b/examples/vhost_scsi/scsi_spec.h
@@ -0,0 +1,488 @@
Most of the scsi_spec.h file is unused, please remove unnecessary parts. 
quoted hunk ↗ jump to hunk
diff --git a/examples/vhost_scsi/vhost_scsi.c
b/examples/vhost_scsi/vhost_scsi.c
new file mode 100644
index 0000000..160c2e0
--- /dev/null
+++ b/examples/vhost_scsi/vhost_scsi.c
@@ -0,0 +1,472 @@
+
+#define VIRTIO_SCSI_FEATURES ((1 << VIRTIO_F_NOTIFY_ON_EMPTY) |\
+			      (1 << VIRTIO_RING_F_EVENT_IDX) |\
Am I missing something or this example app doesn't support EVENT_IDX?
+			      (1 << VIRTIO_SCSI_F_INOUT) |\
+			      (1 << VIRTIO_SCSI_F_CHANGE))
+
+/* Path to folder where character device will be created. Can be set by user.
*/
+static char dev_pathname[PATH_MAX] = "";
+
+static struct vhost_scsi_ctrlr *g_vhost_ctrlr;
+static int g_should_stop;
+static sem_t exit_sem;
+
+#define NUM_OF_SCSI_QUEUES 3
+
+static struct vhost_scsi_ctrlr *
+vhost_scsi_ctrlr_find(__rte_unused const char *ctrlr_name)
+{
+	/* currently we only support 1 socket file fd */
So remove this function.
+	return g_vhost_ctrlr;
+}
+
+static uint64_t gpa_to_vva(int vid, uint64_t gpa)
+{
+	char path[PATH_MAX];
+	struct vhost_scsi_ctrlr *ctrlr;
+	int ret = 0;
+
+	ret = rte_vhost_get_ifname(vid, path, PATH_MAX);
+	if (ret) {
+		fprintf(stderr, "Cannot get socket name\n");
+		assert(ret != 0);
+	}
+
+	ctrlr = vhost_scsi_ctrlr_find(path);
+	if (!ctrlr) {
+		fprintf(stderr, "Controller is not ready\n");
+		assert(ctrlr != NULL);
+	}
+
+	assert(ctrlr->mem != NULL);
+
+	return rte_vhost_gpa_to_vva(ctrlr->mem, gpa);
+}
+
Replace vid in  vhost_block_dev by pointer to struct vhost_scsi_ctrlr
This way you will not need this whole function at all.
+static struct vring_desc *
+descriptor_get_next(struct vring_desc *vq_desc, struct vring_desc
*cur_desc)
+{
+	return &vq_desc[cur_desc->next];
+}
+
+static bool
+descriptor_has_next(struct vring_desc *cur_desc)
+{
+	return !!(cur_desc->flags & VRING_DESC_F_NEXT);
+}
+
+static bool
+descriptor_is_wr(struct vring_desc *cur_desc)
+{
+	return !!(cur_desc->flags & VRING_DESC_F_WRITE);
+}
I have an impression that all those functions should go to library instead of example application.
+
+static struct vhost_block_dev *
+vhost_scsi_bdev_construct(const char *bdev_name, const char
*bdev_serial,
+			  uint32_t blk_size, uint64_t blk_cnt,
+			  bool wce_enable)
+{
+	struct vhost_block_dev *bdev;
+
+	bdev = rte_zmalloc(NULL, sizeof(*bdev), RTE_CACHE_LINE_SIZE);
+	if (!bdev)
+		return NULL;
+
+	strncpy(bdev->name, bdev_name, sizeof(bdev->name));
+	strncpy(bdev->product_name, bdev_serial, sizeof(bdev-
quoted
product_name));
+	bdev->blocklen = blk_size;
+	bdev->blockcnt = blk_cnt;
+	bdev->write_cache = wce_enable;
write_cache is not used and should be removed.
+
+	/* use memory as disk storage space */
+	bdev->data = rte_zmalloc(NULL, blk_cnt * blk_size, 0);
+	if (!bdev->data) {
+		fprintf(stderr, "no enough reseverd huge memory for
disk\n");
+		return NULL;
+	}
+
+	return bdev;
+}
+
+static void
+process_requestq(struct vhost_scsi_ctrlr *ctrlr, uint32_t q_idx)
+{
+	int ret;
+	struct vhost_scsi_queue *scsi_vq;
+	struct rte_vhost_vring *vq;
+
+	scsi_vq = &ctrlr->bdev->queues[q_idx];
+	vq = &scsi_vq->vq;
+	ret = rte_vhost_get_vhost_vring(ctrlr->bdev->vid, q_idx, vq);
+	assert(ret == 0);
+
+	while (vq->avail->idx != scsi_vq->last_used_idx) {
+		int req_idx;
+		uint16_t last_idx;
+		struct vhost_scsi_task *task;
+
+		last_idx = scsi_vq->last_used_idx & (vq->size - 1);
+		req_idx = vq->avail->ring[last_idx];
+
+		task = rte_zmalloc(NULL, sizeof(*task), 0);
+		assert(task != NULL);
+
+		task->ctrlr = ctrlr;
+		task->bdev = ctrlr->bdev;
+		task->vq = vq;
+		task->req_idx = req_idx;
+		task->desc = &task->vq->desc[task->req_idx];
+
+		/* does not support indirect descriptors */
+		assert((task->desc->flags & VRING_DESC_F_INDIRECT) == 0);
+		scsi_vq->last_used_idx++;
+
+		task->req = (void *)gpa_to_vva(task->bdev->vid,
+					       task->desc->addr);
+
+		task->desc = descriptor_get_next(task->vq->desc, task-
quoted
desc);
+		if (!descriptor_has_next(task->desc)) {
+			task->dxfer_dir = SCSI_DIR_NONE;
+			task->resp = (void *)gpa_to_vva(task->bdev->vid,
+							task->desc->addr);
+
+		} else if (!descriptor_is_wr(task->desc)) {
+			task->dxfer_dir = SCSI_DIR_TO_DEV;
+			vhost_process_write_payload_chain(task);
+		} else {
+			task->dxfer_dir = SCSI_DIR_FROM_DEV;
+			vhost_process_read_payload_chain(task);
+		}
+
+		ret = vhost_bdev_process_scsi_commands(ctrlr->bdev,
task);
+		if (ret) {
+			/* invalid response */
+			task->resp->response =
VIRTIO_SCSI_S_BAD_TARGET;
+		} else {
+			/* successfully */
+			task->resp->response = VIRTIO_SCSI_S_OK;
+			task->resp->status = 0;
You need to fill resp->resid field here.
+		}
+		submit_completion(task);
+		rte_free(task);
+	}
+}
+
+/* Main framework for processing IOs */
+static void *
+ctrlr_worker(void *arg)
+{
+	uint32_t idx, num;
+	struct vhost_scsi_ctrlr *ctrlr = (struct vhost_scsi_ctrlr *)arg;
+	cpu_set_t cpuset;
+	pthread_t thread;
+
+	thread = pthread_self();
+	CPU_ZERO(&cpuset);
+	CPU_SET(0, &cpuset);
+	pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
+
+	num =  rte_vhost_get_vring_num(ctrlr->bdev->vid);
+	fprintf(stdout, "Ctrlr Worker Thread Started with %u Vring\n", num);
+
+	if (num != NUM_OF_SCSI_QUEUES) {
Again, how many queues doeas this app support 8 or NUM_OF_SCSI_QUEUES?
+		fprintf(stderr, "Only 1 IO queue are supported\n");
+		exit(0);
+	}
+
+	while (!g_should_stop && ctrlr->bdev != NULL) {
+		/* At least 3 vrings, currently only can support 1 IO queue
+		 * Queue 2 for IO queue, does not support TMF and hotplug
+		 * for the example application now
+		 */
+		for (idx = 2; idx < num; idx++)
+			process_requestq(ctrlr, idx);
+	}
+
+	fprintf(stdout, "Ctrlr Worker Thread Exiting\n");
+	sem_post(&exit_sem);
+	return NULL;
+}
+
+
+static struct vhost_scsi_ctrlr *
+vhost_scsi_ctrlr_construct(const char *ctrlr_name)
+{
+	int ret;
+	struct vhost_scsi_ctrlr *ctrlr;
+	char *path;
+	char cwd[PATH_MAX];
+
+	/* always use current directory */
+	path = getcwd(cwd, PATH_MAX);
+	if (!path) {
+		fprintf(stderr, "Cannot get current working directory\n");
+		return NULL;
+	}
+	snprintf(dev_pathname, sizeof(dev_pathname), "%s/%s", path,
ctrlr_name);
+
+	if (access(dev_pathname, F_OK) != -1) {
+		if (unlink(dev_pathname) != 0)
+			rte_exit(EXIT_FAILURE, "Cannot remove %s.\n",
+				 dev_pathname);
+	}
+
+	fprintf(stdout, "socket file: %s\n", dev_pathname);
+
+	if (rte_vhost_driver_register(dev_pathname, 0) != 0) {
+		fprintf(stderr, "socket %s already exists\n", dev_pathname);
+		return NULL;
+	}
+
+	ret = rte_vhost_driver_set_features(dev_pathname,
VIRTIO_SCSI_FEATURES);
+	if (ret) {
+		fprintf(stderr, "Set vhost driver features failed\n");
+		return NULL;
+	}
+
+	ctrlr = rte_zmalloc(NULL, sizeof(*ctrlr), RTE_CACHE_LINE_SIZE);
+	if (!ctrlr)
+		return NULL;
+
+	ctrlr->name = strdup(ctrlr_name);
name is never used  and also never free.
+
+	rte_vhost_driver_callback_register(dev_pathname,
+					   &vhost_scsi_device_ops);
+
+	return ctrlr;
+}
+
+static void
+signal_handler(__rte_unused int signum)
+{
Some message would be good to inform about successful exit.
+	if (access(dev_pathname, F_OK) == 0)
+		unlink(dev_pathname);
+	exit(0);
+}
+
quoted hunk ↗ jump to hunk
diff --git a/examples/vhost_scsi/vhost_scsi.h b/examples/vhost_scsi/vhost_scsi.h
new file mode 100644
index 0000000..b5340cc
--- /dev/null
+++ b/examples/vhost_scsi/vhost_scsi.h
@@ -0,0 +1,250 @@
Do we need this file at all? I think it can got to .c file
+
+#include <rte_vhost.h>
+
+static inline uint16_t
+from_be16(const void *ptr)
+{
+	const uint8_t *tmp = (const uint8_t *)ptr;
+
+	return (((uint16_t)tmp[0] << 8) | tmp[1]);
+}
+
Why implementing this instead of using existing macros from rte_byteorder.h?
+
+struct vaddr_region {
+	void *vaddr;
+	uint64_t len;
+};
I don't see any usage of this struct.
+
+struct vhost_scsi_queue {
+	struct rte_vhost_vring vq;
+	uint16_t last_avail_idx;
+	uint16_t last_used_idx;
+};
+
+struct vhost_block_dev {
+	/** ID for vhost library. */
+	int vid;
Don't think this is right place for vid. As mentioned, pointer to struct vhost_scsi_ctrlr would be better here.
+	/** Queues for the block device */
+	struct vhost_scsi_queue queues[8];
You define 8 queues here but in vhost_scsi.c NUM_OF_SCSI_QUEUES is 3.
Maybe move definition of NUM_OF_SCSI_QUEUES to .h file and use it here instead of '8'
+	/** Unique name for this block device. */
+	char name[64];
+
+	/** Unique product name for this kind of block device. */
+	char product_name[256];
+
+	/** Size in bytes of a logical block for the backend */
+	uint32_t blocklen;
+
+	/** Number of blocks */
+	uint64_t blockcnt;
+
+	/** write cache enabled, not used at the moment */
+	int write_cache;
So can be removed.
+
+	/** use memory as disk storage space */
+	uint8_t *data;
+};
+
Thanks
Pawel
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help