[dpdk-dev] [PATCH v3] examples/vhost: introduce a new vhost-user-scsi sample application
Wodkowski, PawelX
pawelx.wodkowski at intel.com
Wed Jul 12 16:49:38 CEST 2017
Hi, sorry for late review but just spotted this patch.
Please see my comments
> 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.
> + 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.
> 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-
> >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-
> >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);
> +}
> +
> 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
More information about the dev
mailing list