[SPDK] Re: [RFC PATCH v1] [RFC] Use multiple threads to handle vhost virtqueues
Liu, Changpeng <changpeng.liu at intel.com> Fri, 12 Nov 2021 04:59:17 +0000
| Newsgroups | dev.linux.lists.spdk |
|---|---|
| Message-ID | <SJ0PR11MB510189815B4512EA06452914EE959@SJ0PR11MB5101.namprd11.prod.outlook.com> |
Hi Jieyue,
Thanks for submitting patch to SPDK, but we use gerrit for code development, you can refer https://spdk.io/development/ for submitting your patch.
> -----Original Message-----
> From: majieyue(a)linux.alibaba.com <majieyue(a)linux.alibaba.com>
> Sent: Friday, November 12, 2021 12:56 PM
> To: spdk(a)lists.01.org
> Cc: Ma Jie Yue <majieyue(a)linux.alibaba.com>
> Subject: [SPDK] [RFC PATCH v1] [RFC] Use multiple threads to handle vhost
> virtqueues
>
> From: Ma Jie Yue <majieyue(a)linux.alibaba.com>
>
> Currently the vhost virtqueues of the same device are handled by only one spdk
> thread, even we have many reactors running, which means the performance of a
> vhost device can not be scaled up with multiple cores.
>
> This patch bind each virtqueue to an individual spdk thread, and leverage the
> spdk scheduler ability to run these threads on different reactors. Now only
> the vhost blk module is finished, and just leave the vhost scsi later.
>
> During the test, the spdk_top shows theses threads are indeed dispatched to
> different reactors, and the IO performance is also increased with the number
> of queues.
>
> Signed-off-by: Ma Jie Yue <majieyue(a)linux.alibaba.com>
> ---
> lib/vhost/vhost.c | 158 +++++++++++++++----
> lib/vhost/vhost_blk.c | 384 +++++++++++++++++++++------------------------
> lib/vhost/vhost_internal.h | 28 +++-
> 3 files changed, 324 insertions(+), 246 deletions(-)
>
> diff --git a/lib/vhost/vhost.c b/lib/vhost/vhost.c
> index edae5938e..80343dd1c 100644
> --- a/lib/vhost/vhost.c
> +++ b/lib/vhost/vhost.c
> @@ -1089,6 +1089,46 @@ vhost_session_stop_done(struct spdk_vhost_session
> *vsession, int response)
> vhost_session_cb_done(response);
> }
>
> +void
> +vhost_session_start_vq_done(struct spdk_vhost_virtqueue *vq, int response)
> +{
> + struct spdk_vhost_session *vsession = vq->vsession;
> +
> + if (response == 0) {
> + vq->started = true;
> + vsession->active_queues++;
> +
> + if (vsession->active_queues == vsession->max_queues) {
> + vsession->started = true;
> +
> + assert(vsession->vdev->active_session_num <
> UINT32_MAX);
> + vsession->vdev->active_session_num++;
> + }
> + }
> +
> + vhost_session_cb_done(response);
> +}
> +
> +void
> +vhost_session_stop_vq_done(struct spdk_vhost_virtqueue *vq, int response)
> +{
> + struct spdk_vhost_session *vsession = vq->vsession;
> +
> + if (response == 0) {
> + vq->started = false;
> + vsession->active_queues--;
> +
> + if (vsession->active_queues == 0) {
> + vsession->started = false;
> +
> + assert(vsession->vdev->active_session_num > 0);
> + vsession->vdev->active_session_num--;
> + }
> + }
> +
> + vhost_session_cb_done(response);
> +}
> +
> static void
> vhost_event_cb(void *arg1)
> {
> @@ -1101,7 +1141,7 @@ vhost_event_cb(void *arg1)
> }
>
> vsession = vhost_session_find_by_id(ctx->vdev, ctx->vsession_id);
> - ctx->cb_fn(ctx->vdev, vsession, NULL);
> + ctx->cb_fn(ctx->vdev, vsession, ctx->user_ctx);
> pthread_mutex_unlock(&g_vhost_mutex);
> }
>
> @@ -1126,6 +1166,34 @@ vhost_session_send_event(struct spdk_vhost_session
> *vsession,
> return g_dpdk_response;
> }
>
> +int
> +vhost_session_send_event_mt(struct spdk_vhost_session *vsession,
> + spdk_vhost_session_fn cb_fn, unsigned timeout_sec,
> + const char *errmsg)
> +{
> + struct vhost_session_fn_ctx ev_ctx = {0};
> + struct spdk_vhost_dev *vdev = vsession->vdev;
> + unsigned long i;
> +
> + ev_ctx.vdev = vdev;
> + ev_ctx.vsession_id = vsession->id;
> + ev_ctx.cb_fn = cb_fn;
> +
> + for (i = 0; i < vsession->max_queues; i++) {
> + ev_ctx.user_ctx = (void *)i;
> + spdk_thread_send_msg(vsession->thread[i], vhost_event_cb,
> &ev_ctx);
> +
> + pthread_mutex_unlock(&g_vhost_mutex);
> + wait_for_semaphore(timeout_sec, errmsg);
> + pthread_mutex_lock(&g_vhost_mutex);
> +
> + if (g_dpdk_response)
> + break;
> + }
> +
> + return g_dpdk_response;
> +}
> +
> static void
> foreach_session_finish_cb(void *arg1)
> {
> @@ -1250,7 +1318,7 @@ int
> vhost_stop_device_cb(int vid)
> {
> struct spdk_vhost_session *vsession;
> - int rc;
> + int i, rc;
>
> pthread_mutex_lock(&g_vhost_mutex);
> vsession = vhost_session_find_by_vid(vid);
> @@ -1267,6 +1335,14 @@ vhost_stop_device_cb(int vid)
> }
>
> rc = _stop_session(vsession);
> +
> + /* clean up the threads */
> + if (!rc) {
> + for (i = 0; i < vsession->max_queues; i++) {
> + spdk_thread_send_msg(vsession->thread[i],
> vhost_dev_thread_exit, NULL);
> + }
> + }
> +
> pthread_mutex_unlock(&g_vhost_mutex);
>
> return rc;
> @@ -1280,6 +1356,7 @@ vhost_start_device_cb(int vid)
> int rc = -1;
> uint16_t i;
> bool packed_ring;
> + struct spdk_cpuset *cpumask;
>
> pthread_mutex_lock(&g_vhost_mutex);
>
> @@ -1304,9 +1381,11 @@ vhost_start_device_cb(int vid)
> packed_ring = ((vsession->negotiated_features & (1ULL <<
> VIRTIO_F_RING_PACKED)) != 0);
>
> vsession->max_queues = 0;
> + cpumask = spdk_thread_get_cpumask(vdev->thread);
> memset(vsession->virtqueue, 0, sizeof(vsession->virtqueue));
> for (i = 0; i < SPDK_VHOST_MAX_VQUEUES; i++) {
> struct spdk_vhost_virtqueue *q = &vsession->virtqueue[i];
> + char *name;
>
> q->vsession = vsession;
> q->vring_idx = -1;
> @@ -1362,6 +1441,16 @@ vhost_start_device_cb(int vid)
> }
>
> q->packed.packed_ring = packed_ring;
> +
> + name = spdk_sprintf_alloc("%s.%u", vsession->name, i);
> + vsession->thread[i] = spdk_thread_create(name, cpumask);
> + free(name);
> + if (!vsession->thread[i]) {
> + SPDK_ERRLOG("Failed to create thread for
> virtqueue %s.%u", vsession->name, i);
> + rc = -EIO;
> + goto out;
> + }
> +
> vsession->max_queues = i + 1;
> }
>
> @@ -1401,55 +1490,56 @@ vhost_start_device_cb(int vid)
> }
>
> out:
> + if (rc) {
> + for (i = 0; i < vsession->max_queues; i++) {
> + spdk_thread_send_msg(vsession->thread[i],
> vhost_dev_thread_exit, NULL);
> + }
> + }
> pthread_mutex_unlock(&g_vhost_mutex);
> return rc;
> }
>
> void
> -vhost_session_set_interrupt_mode(struct spdk_vhost_session *vsession, bool
> interrupt_mode)
> +vhost_session_set_vq_interrupt_mode(struct spdk_vhost_virtqueue *q, bool
> interrupt_mode)
> {
> - uint16_t i;
> bool packed_ring;
> int rc = 0;
> + uint64_t num_events = 1;
> + struct spdk_vhost_session *vsession = q->vsession;
>
> packed_ring = ((vsession->negotiated_features & (1ULL <<
> VIRTIO_F_RING_PACKED)) != 0);
>
> - for (i = 0; i < vsession->max_queues; i++) {
> - struct spdk_vhost_virtqueue *q = &vsession->virtqueue[i];
> - uint64_t num_events = 1;
> + /* vring.desc and vring.desc_packed are in a union struct
> + * so q->vring.desc can replace q->vring.desc_packed.
> + */
> + if (q->vring.desc == NULL || q->vring.size == 0) {
> + return;
> + }
>
> - /* vring.desc and vring.desc_packed are in a union struct
> - * so q->vring.desc can replace q->vring.desc_packed.
> - */
> - if (q->vring.desc == NULL || q->vring.size == 0) {
> - continue;
> + if (interrupt_mode) {
> + /* Enable I/O submission notifications, we'll be interrupting. */
> + if (packed_ring) {
> + * (volatile uint16_t *) &q->vring.device_event->flags =
> VRING_PACKED_EVENT_FLAG_ENABLE;
> + } else {
> + * (volatile uint16_t *) &q->vring.used->flags = 0;
> }
>
> - if (interrupt_mode) {
> - /* Enable I/O submission notifications, we'll be
> interrupting. */
> - if (packed_ring) {
> - * (volatile uint16_t *) &q->vring.device_event-
> >flags = VRING_PACKED_EVENT_FLAG_ENABLE;
> - } else {
> - * (volatile uint16_t *) &q->vring.used->flags = 0;
> - }
> -
> - /* In case of race condition, always kick vring when
> switch to intr */
> - rc = write(q->vring.kickfd, &num_events,
> sizeof(num_events));
> - if (rc < 0) {
> - SPDK_ERRLOG("failed to kick vring: %s.\n",
> spdk_strerror(errno));
> - }
> + /* In case of race condition, always kick vring when switch to intr
> */
> + rc = write(q->vring.kickfd, &num_events, sizeof(num_events));
> + if (rc < 0) {
> + SPDK_ERRLOG("failed to kick vring: %s.\n",
> spdk_strerror(errno));
> + }
>
> - vsession->interrupt_mode = true;
> + vsession->interrupt_mode = true;
> + } else {
> + /* Disable I/O submission notifications, we'll be polling. */
> + if (packed_ring) {
> + * (volatile uint16_t *) &q->vring.device_event->flags =
> VRING_PACKED_EVENT_FLAG_DISABLE;
> } else {
> - /* Disable I/O submission notifications, we'll be polling.
> */
> - if (packed_ring) {
> - * (volatile uint16_t *) &q->vring.device_event-
> >flags = VRING_PACKED_EVENT_FLAG_DISABLE;
> - } else {
> - * (volatile uint16_t *) &q->vring.used->flags =
> VRING_USED_F_NO_NOTIFY;
> - }
> -
> - vsession->interrupt_mode = false;
> + * (volatile uint16_t *) &q->vring.used->flags =
> VRING_USED_F_NO_NOTIFY;
> }
> +
> + vsession->interrupt_mode = false;
> }
> }
>
> diff --git a/lib/vhost/vhost_blk.c b/lib/vhost/vhost_blk.c
> index 55fb82530..78821ce09 100644
> --- a/lib/vhost/vhost_blk.c
> +++ b/lib/vhost/vhost_blk.c
> @@ -102,17 +102,20 @@ struct spdk_vhost_blk_session {
> /* The parent session must be the very first field in this struct */
> struct spdk_vhost_session vsession;
> struct spdk_vhost_blk_dev *bvdev;
> - struct spdk_poller *requestq_poller;
> - struct spdk_io_channel *io_channel;
> - struct spdk_poller *stop_poller;
> + struct spdk_poller *requestq_poller[SPDK_VHOST_MAX_VQUEUES];
> + struct spdk_io_channel *io_channel[SPDK_VHOST_MAX_VQUEUES];
> + struct spdk_poller *stop_poller[SPDK_VHOST_MAX_VQUEUES];
> };
>
> /* forward declaration */
> +static int vhost_blk_stop_vq_cb(struct spdk_vhost_dev *vdev,
> + struct spdk_vhost_session *vsession, void
> *unused);
> +
> static const struct spdk_vhost_dev_backend vhost_blk_device_backend;
>
> static int
> process_blk_request(struct spdk_vhost_blk_task *task,
> - struct spdk_vhost_blk_session *bvsession);
> + struct spdk_vhost_virtqueue *vq);
>
> static struct spdk_vhost_blk_session *
> to_blk_session(struct spdk_vhost_session *vsession)
> @@ -124,8 +127,8 @@ to_blk_session(struct spdk_vhost_session *vsession)
> static void
> blk_task_finish(struct spdk_vhost_blk_task *task)
> {
> - assert(task->bvsession->vsession.task_cnt > 0);
> - task->bvsession->vsession.task_cnt--;
> + assert(task->vq->task_cnt > 0);
> + task->vq->task_cnt--;
> task->used = false;
> }
>
> @@ -421,7 +424,7 @@ blk_request_resubmit(void *arg)
> struct spdk_vhost_blk_task *task = (struct spdk_vhost_blk_task *)arg;
> int rc = 0;
>
> - rc = process_blk_request(task, task->bvsession);
> + rc = process_blk_request(task, task->vq);
> if (rc == 0) {
> SPDK_DEBUGLOG(vhost_blk, "====== Task %p resubmitted
> ======\n", task);
> } else {
> @@ -435,12 +438,13 @@ blk_request_queue_io(struct spdk_vhost_blk_task
> *task)
> int rc;
> struct spdk_vhost_blk_session *bvsession = task->bvsession;
> struct spdk_bdev *bdev = bvsession->bvdev->bdev;
> + struct spdk_vhost_virtqueue *vq = task->vq;
>
> task->bdev_io_wait.bdev = bdev;
> task->bdev_io_wait.cb_fn = blk_request_resubmit;
> task->bdev_io_wait.cb_arg = task;
>
> - rc = spdk_bdev_queue_io_wait(bdev, bvsession->io_channel, &task-
> >bdev_io_wait);
> + rc = spdk_bdev_queue_io_wait(bdev, bvsession->io_channel[vq-
> >vring_idx], &task->bdev_io_wait);
> if (rc != 0) {
> SPDK_ERRLOG("%s: failed to queue I/O, rc=%d\n", bvsession-
> >vsession.name, rc);
> invalid_blk_request(task, VIRTIO_BLK_S_IOERR);
> @@ -449,8 +453,10 @@ blk_request_queue_io(struct spdk_vhost_blk_task *task)
>
> static int
> process_blk_request(struct spdk_vhost_blk_task *task,
> - struct spdk_vhost_blk_session *bvsession)
> + struct spdk_vhost_virtqueue *vq)
> {
> + struct spdk_vhost_session *vsession = vq->vsession;
> + struct spdk_vhost_blk_session *bvsession = to_blk_session(vsession);
> struct spdk_vhost_blk_dev *bvdev = bvsession->bvdev;
> const struct virtio_blk_outhdr *req;
> struct virtio_blk_discard_write_zeroes *desc;
> @@ -503,12 +509,12 @@ process_blk_request(struct spdk_vhost_blk_task *task,
>
> if (type == VIRTIO_BLK_T_IN) {
> task->used_len = payload_len + sizeof(*task->status);
> - rc = spdk_bdev_readv(bvdev->bdev_desc, bvsession-
> >io_channel,
> + rc = spdk_bdev_readv(bvdev->bdev_desc, bvsession-
> >io_channel[vq->vring_idx],
> &task->iovs[1], task->iovcnt, req-
> >sector * 512,
> payload_len,
> blk_request_complete_cb, task);
> } else if (!bvdev->readonly) {
> task->used_len = sizeof(*task->status);
> - rc = spdk_bdev_writev(bvdev->bdev_desc, bvsession-
> >io_channel,
> + rc = spdk_bdev_writev(bvdev->bdev_desc, bvsession-
> >io_channel[vq->vring_idx],
> &task->iovs[1], task->iovcnt, req-
> >sector * 512,
> payload_len,
> blk_request_complete_cb, task);
> } else {
> @@ -540,7 +546,7 @@ process_blk_request(struct spdk_vhost_blk_task *task,
> return -1;
> }
>
> - rc = spdk_bdev_unmap(bvdev->bdev_desc, bvsession-
> >io_channel,
> + rc = spdk_bdev_unmap(bvdev->bdev_desc, bvsession-
> >io_channel[vq->vring_idx],
> desc->sector * 512, desc->num_sectors * 512,
> blk_request_complete_cb, task);
> if (rc) {
> @@ -570,7 +576,7 @@ process_blk_request(struct spdk_vhost_blk_task *task,
> (uint64_t)desc->sector * 512, (uint64_t)desc-
> >num_sectors * 512);
> }
>
> - rc = spdk_bdev_write_zeroes(bvdev->bdev_desc, bvsession-
> >io_channel,
> + rc = spdk_bdev_write_zeroes(bvdev->bdev_desc, bvsession-
> >io_channel[vq->vring_idx],
> desc->sector * 512, desc->num_sectors
> * 512,
> blk_request_complete_cb, task);
> if (rc) {
> @@ -590,7 +596,7 @@ process_blk_request(struct spdk_vhost_blk_task *task,
> invalid_blk_request(task, VIRTIO_BLK_S_IOERR);
> return -1;
> }
> - rc = spdk_bdev_flush(bvdev->bdev_desc, bvsession->io_channel,
> + rc = spdk_bdev_flush(bvdev->bdev_desc, bvsession-
> >io_channel[vq->vring_idx],
> 0, flush_bytes,
> blk_request_complete_cb, task);
> if (rc) {
> @@ -639,7 +645,7 @@ process_blk_task(struct spdk_vhost_virtqueue *vq,
> uint16_t req_idx)
> return;
> }
>
> - task->bvsession->vsession.task_cnt++;
> + vq->task_cnt++;
>
> blk_task_init(task);
>
> @@ -653,7 +659,7 @@ process_blk_task(struct spdk_vhost_virtqueue *vq,
> uint16_t req_idx)
> return;
> }
>
> - if (process_blk_request(task, task->bvsession) == 0) {
> + if (process_blk_request(task, vq) == 0) {
> SPDK_DEBUGLOG(vhost_blk, "====== Task %p req_idx %d
> submitted ======\n", task,
> req_idx);
> } else {
> @@ -702,7 +708,7 @@ process_packed_blk_task(struct spdk_vhost_virtqueue
> *vq, uint16_t req_idx)
> req_idx, (req_idx + num_descs - 1) %
> vq->vring.size,
> &task->inflight_head);
>
> - task->bvsession->vsession.task_cnt++;
> + vq->task_cnt++;
>
> blk_task_init(task);
>
> @@ -715,7 +721,7 @@ process_packed_blk_task(struct spdk_vhost_virtqueue
> *vq, uint16_t req_idx)
> return;
> }
>
> - if (process_blk_request(task, task->bvsession) == 0) {
> + if (process_blk_request(task, vq) == 0) {
> SPDK_DEBUGLOG(vhost_blk, "====== Task %p req_idx %d
> submitted ======\n", task,
> task_idx);
> } else {
> @@ -760,7 +766,7 @@ process_packed_inflight_blk_task(struct
> spdk_vhost_virtqueue *vq,
> /* It's for cleaning inflight entries */
> task->inflight_head = req_idx;
>
> - task->bvsession->vsession.task_cnt++;
> + vq->task_cnt++;
>
> blk_task_init(task);
>
> @@ -773,7 +779,7 @@ process_packed_inflight_blk_task(struct
> spdk_vhost_virtqueue *vq,
> return;
> }
>
> - if (process_blk_request(task, task->bvsession) == 0) {
> + if (process_blk_request(task, vq) == 0) {
> SPDK_DEBUGLOG(vhost_blk, "====== Task %p req_idx %d
> submitted ======\n", task,
> task_idx);
> } else {
> @@ -893,20 +899,6 @@ vdev_vq_worker(void *arg)
> return _vdev_vq_worker(vq);
> }
>
> -static int
> -vdev_worker(void *arg)
> -{
> - struct spdk_vhost_blk_session *bvsession = arg;
> - struct spdk_vhost_session *vsession = &bvsession->vsession;
> - uint16_t q_idx;
> -
> - for (q_idx = 0; q_idx < vsession->max_queues; q_idx++) {
> - _vdev_vq_worker(&vsession->virtqueue[q_idx]);
> - }
> -
> - return SPDK_POLLER_BUSY;
> -}
> -
> static void
> no_bdev_process_vq(struct spdk_vhost_blk_session *bvsession, struct
> spdk_vhost_virtqueue *vq)
> {
> @@ -985,9 +977,9 @@ _no_bdev_vdev_vq_worker(struct spdk_vhost_virtqueue
> *vq)
>
> vhost_session_vq_used_signal(vq);
>
> - if (vsession->task_cnt == 0 && bvsession->io_channel) {
> - spdk_put_io_channel(bvsession->io_channel);
> - bvsession->io_channel = NULL;
> + if (vq->task_cnt == 0 && bvsession->io_channel[vq->vring_idx]) {
> + spdk_put_io_channel(bvsession->io_channel[vq->vring_idx]);
> + bvsession->io_channel[vq->vring_idx] = NULL;
> }
>
> return SPDK_POLLER_BUSY;
> @@ -1001,75 +993,55 @@ no_bdev_vdev_vq_worker(void *arg)
> return _no_bdev_vdev_vq_worker(vq);
> }
>
> -static int
> -no_bdev_vdev_worker(void *arg)
> -{
> - struct spdk_vhost_blk_session *bvsession = arg;
> - struct spdk_vhost_session *vsession = &bvsession->vsession;
> - uint16_t q_idx;
> -
> - for (q_idx = 0; q_idx < vsession->max_queues; q_idx++) {
> - _no_bdev_vdev_vq_worker(&vsession->virtqueue[q_idx]);
> - }
> -
> - return SPDK_POLLER_BUSY;
> -}
> -
> static void
> -vhost_blk_session_unregister_interrupts(struct spdk_vhost_blk_session
> *bvsession)
> +vhost_blk_session_unregister_vq_interrupts(struct spdk_vhost_blk_session
> *bvsession,
> + int vq_idx)
> {
> struct spdk_vhost_session *vsession = &bvsession->vsession;
> struct spdk_vhost_virtqueue *vq;
> - int i;
> -
> - SPDK_DEBUGLOG(vhost_blk, "unregister virtqueues interrupt\n");
> - for (i = 0; i < vsession->max_queues; i++) {
> - vq = &vsession->virtqueue[i];
> - if (vq->intr == NULL) {
> - break;
> - }
>
> - SPDK_DEBUGLOG(vhost_blk, "unregister vq[%d]'s kickfd is %d\n",
> - i, vq->vring.kickfd);
> - spdk_interrupt_unregister(&vq->intr);
> + SPDK_DEBUGLOG(vhost_blk, "unregister virtqueues %d interrupt\n",
> vq_idx);
> + vq = &vsession->virtqueue[vq_idx];
> + if (vq->intr == NULL) {
> + return;
> }
> +
> + SPDK_DEBUGLOG(vhost_blk, "unregister vq[%d]'s kickfd is %d\n",
> + vq_idx, vq->vring.kickfd);
> + spdk_interrupt_unregister(&vq->intr);
> }
>
> static int
> -vhost_blk_session_register_interrupts(struct spdk_vhost_blk_session
> *bvsession,
> - spdk_interrupt_fn fn, const char *name)
> +vhost_blk_session_register_vq_interrupts(struct spdk_vhost_blk_session
> *bvsession,
> + spdk_interrupt_fn fn, int vq_idx)
> {
> struct spdk_vhost_session *vsession = &bvsession->vsession;
> struct spdk_vhost_virtqueue *vq = NULL;
> - int i;
> -
> - SPDK_DEBUGLOG(vhost_blk, "Register virtqueues interrupt\n");
> - for (i = 0; i < vsession->max_queues; i++) {
> - vq = &vsession->virtqueue[i];
> - SPDK_DEBUGLOG(vhost_blk, "Register vq[%d]'s kickfd is %d\n",
> - i, vq->vring.kickfd);
> -
> - vq->intr = spdk_interrupt_register(vq->vring.kickfd, fn, vq, name);
> - if (vq->intr == NULL) {
> - SPDK_ERRLOG("Fail to register req notifier handler.\n");
> - goto err;
> - }
> +
> + SPDK_DEBUGLOG(vhost_blk, "Register virtqueues %d interrupt\n",
> vq_idx);
> +
> + vq = &vsession->virtqueue[vq_idx];
> + SPDK_DEBUGLOG(vhost_blk, "Register vq[%d]'s kickfd is %d\n",
> + vq_idx, vq->vring.kickfd);
> +
> + vq->intr = SPDK_INTERRUPT_REGISTER(vq->vring.kickfd, fn, vq);
> + if (vq->intr == NULL) {
> + SPDK_ERRLOG("Fail to register req notifier handler.\n");
> + goto err;
> }
>
> return 0;
>
> err:
> - vhost_blk_session_unregister_interrupts(bvsession);
> -
> return -1;
> }
>
> static void
> -vhost_blk_poller_set_interrupt_mode(struct spdk_poller *poller, void *cb_arg,
> bool interrupt_mode)
> +vhost_blk_poller_set_vq_interrupt_mode(struct spdk_poller *poller, void
> *cb_arg, bool interrupt_mode)
> {
> - struct spdk_vhost_blk_session *bvsession = cb_arg;
> + struct spdk_vhost_virtqueue *vq = cb_arg;
>
> - vhost_session_set_interrupt_mode(&bvsession->vsession,
> interrupt_mode);
> + vhost_session_set_vq_interrupt_mode(vq, interrupt_mode);
> }
>
> static struct spdk_vhost_blk_dev *
> @@ -1127,35 +1099,44 @@ vhost_dev_bdev_remove_cpl_cb(struct
> spdk_vhost_dev *vdev, void *ctx)
> bvdev->bdev = NULL;
> }
>
> -static int
> -vhost_session_bdev_remove_cb(struct spdk_vhost_dev *vdev,
> +static int vq_bdev_remove_cb(struct spdk_vhost_dev *vdev,
> struct spdk_vhost_session *vsession,
> void *ctx)
> {
> - struct spdk_vhost_blk_session *bvsession;
> + struct spdk_vhost_blk_session *bvsession = to_blk_session(vsession);
> + unsigned long vq_idx = (unsigned long)ctx;
> int rc;
>
> - bvsession = to_blk_session(vsession);
> - if (bvsession->requestq_poller) {
> - spdk_poller_unregister(&bvsession->requestq_poller);
> - if (vsession->virtqueue[0].intr) {
> - vhost_blk_session_unregister_interrupts(bvsession);
> - rc = vhost_blk_session_register_interrupts(bvsession,
> no_bdev_vdev_vq_worker,
> - "no_bdev_vdev_vq_worker");
> - if (rc) {
> - SPDK_ERRLOG("%s: Interrupt register failed\n",
> vsession->name);
> - return rc;
> - }
> - }
> + if (bvsession->requestq_poller[vq_idx]) {
> + spdk_poller_unregister(&bvsession->requestq_poller[vq_idx]);
> + }
> +
> + vhost_blk_session_unregister_vq_interrupts(bvsession, vq_idx);
>
> - bvsession->requestq_poller =
> SPDK_POLLER_REGISTER(no_bdev_vdev_worker, bvsession, 0);
> - spdk_poller_register_interrupt(bvsession->requestq_poller,
> vhost_blk_poller_set_interrupt_mode,
> - bvsession);
> + if (spdk_interrupt_mode_is_enabled()) {
> + rc = vhost_blk_session_register_vq_interrupts(bvsession,
> no_bdev_vdev_vq_worker, vq_idx);
> + if (rc) {
> + SPDK_ERRLOG("%s: Interrupt register failed\n", vsession-
> >name);
> + return rc;
> + }
> }
>
> + bvsession->requestq_poller[vq_idx] =
> SPDK_POLLER_REGISTER(no_bdev_vdev_vq_worker, bvsession, 0);
> + spdk_poller_register_interrupt(bvsession->requestq_poller[vq_idx],
> vhost_blk_poller_set_vq_interrupt_mode,
> + bvsession);
> +
> return 0;
> }
>
> +static int
> +vhost_session_bdev_remove_cb(struct spdk_vhost_dev *vdev,
> + struct spdk_vhost_session *vsession,
> + void *ctx)
> +{
> + return vhost_session_send_event_mt(vsession, vq_bdev_remove_cb,
> + 3, "remove bdev");
> +}
> +
> static void
> bdev_remove_cb(void *remove_ctx)
> {
> @@ -1194,156 +1175,143 @@ bdev_event_cb(enum spdk_bdev_event_type
> type, struct spdk_bdev *bdev,
> }
>
> static void
> -free_task_pool(struct spdk_vhost_blk_session *bvsession)
> +free_vq_task_pool(struct spdk_vhost_virtqueue *vq)
> {
> - struct spdk_vhost_session *vsession = &bvsession->vsession;
> - struct spdk_vhost_virtqueue *vq;
> - uint16_t i;
> -
> - for (i = 0; i < vsession->max_queues; i++) {
> - vq = &vsession->virtqueue[i];
> - if (vq->tasks == NULL) {
> - continue;
> - }
> -
> - spdk_free(vq->tasks);
> - vq->tasks = NULL;
> + if (vq->tasks == NULL) {
> + return;
> }
> +
> + spdk_free(vq->tasks);
> + vq->tasks = NULL;
> + return;
> }
>
> static int
> -alloc_task_pool(struct spdk_vhost_blk_session *bvsession)
> +alloc_vq_task_pool(struct spdk_vhost_virtqueue *vq)
> {
> - struct spdk_vhost_session *vsession = &bvsession->vsession;
> - struct spdk_vhost_virtqueue *vq;
> + struct spdk_vhost_session *vsession = vq->vsession;
> + struct spdk_vhost_blk_session *bvsession = to_blk_session(vsession);
> struct spdk_vhost_blk_task *task;
> uint32_t task_cnt;
> - uint16_t i;
> uint32_t j;
>
> - for (i = 0; i < vsession->max_queues; i++) {
> - vq = &vsession->virtqueue[i];
> - if (vq->vring.desc == NULL) {
> - continue;
> - }
> + if (vq->vring.desc == NULL) {
> + return 0;
> + }
>
> - task_cnt = vq->vring.size;
> - if (task_cnt > SPDK_VHOST_MAX_VQ_SIZE) {
> - /* sanity check */
> - SPDK_ERRLOG("%s: virtuque %"PRIu16" is too big. (size
> = %"PRIu32", max = %"PRIu32")\n",
> - vsession->name, i, task_cnt,
> SPDK_VHOST_MAX_VQ_SIZE);
> - free_task_pool(bvsession);
> - return -1;
> - }
> - vq->tasks = spdk_zmalloc(sizeof(struct spdk_vhost_blk_task) *
> task_cnt,
> - SPDK_CACHE_LINE_SIZE, NULL,
> - SPDK_ENV_LCORE_ID_ANY,
> SPDK_MALLOC_DMA);
> - if (vq->tasks == NULL) {
> - SPDK_ERRLOG("%s: failed to allocate %"PRIu32" tasks for
> virtqueue %"PRIu16"\n",
> - vsession->name, task_cnt, i);
> - free_task_pool(bvsession);
> - return -1;
> - }
> + task_cnt = vq->vring.size;
> + if (task_cnt > SPDK_VHOST_MAX_VQ_SIZE) {
> + /* sanity check */
> + SPDK_ERRLOG("%s: virtuque %"PRIu16" is too big. (size
> = %"PRIu32", max = %"PRIu32")\n",
> + vsession->name, vq->vring_idx, task_cnt,
> SPDK_VHOST_MAX_VQ_SIZE);
> + return -1;
> + }
> + vq->tasks = spdk_zmalloc(sizeof(struct spdk_vhost_blk_task) * task_cnt,
> + SPDK_CACHE_LINE_SIZE, NULL,
> + SPDK_ENV_LCORE_ID_ANY,
> SPDK_MALLOC_DMA);
> + if (vq->tasks == NULL) {
> + SPDK_ERRLOG("%s: failed to allocate %"PRIu32" tasks for
> virtqueue %"PRIu16"\n",
> + vsession->name, task_cnt, vq->vring_idx);
> + return -1;
> + }
>
> - for (j = 0; j < task_cnt; j++) {
> - task = &((struct spdk_vhost_blk_task *)vq->tasks)[j];
> - task->bvsession = bvsession;
> - task->req_idx = j;
> - task->vq = vq;
> - }
> + for (j = 0; j < task_cnt; j++) {
> + task = &((struct spdk_vhost_blk_task *)vq->tasks)[j];
> + task->bvsession = bvsession;
> + task->req_idx = j;
> + task->vq = vq;
> }
>
> return 0;
> }
>
> static int
> -vhost_blk_start_cb(struct spdk_vhost_dev *vdev,
> +vhost_blk_start_vq_cb(struct spdk_vhost_dev *vdev,
> struct spdk_vhost_session *vsession, void *unused)
> {
> struct spdk_vhost_blk_session *bvsession = to_blk_session(vsession);
> struct spdk_vhost_blk_dev *bvdev;
> - int i, rc = 0;
> + int rc = 0;
> + unsigned int vq_idx = (unsigned long)unused;
> + struct spdk_vhost_virtqueue *vq = &vsession->virtqueue[vq_idx];
>
> bvdev = to_blk_dev(vdev);
> assert(bvdev != NULL);
> bvsession->bvdev = bvdev;
>
> - /* validate all I/O queues are in a contiguous index range */
> - for (i = 0; i < vsession->max_queues; i++) {
> - /* vring.desc and vring.desc_packed are in a union struct
> - * so q->vring.desc can replace q->vring.desc_packed.
> - */
> - if (vsession->virtqueue[i].vring.desc == NULL) {
> - SPDK_ERRLOG("%s: queue %"PRIu32" is empty\n",
> vsession->name, i);
> - rc = -1;
> - goto out;
> - }
> + assert(vq->vring_idx == vq_idx);
> +
> + /* vring.desc and vring.desc_packed are in a union struct
> + * so q->vring.desc can replace q->vring.desc_packed.
> + */
> + if (vsession->virtqueue[vq_idx].vring.desc == NULL) {
> + SPDK_ERRLOG("%s: queue %"PRIu32" is empty\n", vsession-
> >name, vq_idx);
> + rc = -1;
> + goto out;
> }
>
> - rc = alloc_task_pool(bvsession);
> + rc = alloc_vq_task_pool(vq);
> if (rc != 0) {
> - SPDK_ERRLOG("%s: failed to alloc task pool.\n", vsession->name);
> + SPDK_ERRLOG("%s: failed to alloc %u task pool.\n", vsession-
> >name, vq_idx);
> goto out;
> }
>
> if (bvdev->bdev) {
> - bvsession->io_channel = spdk_bdev_get_io_channel(bvdev-
> >bdev_desc);
> - if (!bvsession->io_channel) {
> - free_task_pool(bvsession);
> - SPDK_ERRLOG("%s: I/O channel allocation failed\n",
> vsession->name);
> + bvsession->io_channel[vq_idx] =
> spdk_bdev_get_io_channel(bvdev->bdev_desc);
> + if (!bvsession->io_channel[vq_idx]) {
> + free_vq_task_pool(vq);
> + SPDK_ERRLOG("%s: I/O channel %u allocation failed\n",
> vsession->name, vq_idx);
> rc = -1;
> goto out;
> }
> }
>
> if (spdk_interrupt_mode_is_enabled()) {
> - if (bvdev->bdev) {
> - rc = vhost_blk_session_register_interrupts(bvsession,
> - vdev_vq_worker,
> - "vdev_vq_worker");
> - } else {
> - rc = vhost_blk_session_register_interrupts(bvsession,
> - no_bdev_vdev_vq_worker,
> - "no_bdev_vdev_vq_worker");
> - }
> -
> + rc = vhost_blk_session_register_vq_interrupts(bvsession,
> + bvdev->bdev ? vdev_vq_worker :
> no_bdev_vdev_vq_worker, vq_idx);
> if (rc) {
> - SPDK_ERRLOG("%s: Interrupt register failed\n", vsession-
> >name);
> + SPDK_ERRLOG("%s: Interrupt %u register failed\n",
> vsession->name, vq->vring_idx);
> goto out;
> }
> }
>
> - if (bvdev->bdev) {
> - bvsession->requestq_poller =
> SPDK_POLLER_REGISTER(vdev_worker, bvsession, 0);
> - } else {
> - bvsession->requestq_poller =
> SPDK_POLLER_REGISTER(no_bdev_vdev_worker, bvsession, 0);
> - }
> - SPDK_INFOLOG(vhost, "%s: started poller on lcore %d\n",
> - vsession->name, spdk_env_get_current_core());
> + bvsession->requestq_poller[vq_idx] = SPDK_POLLER_REGISTER(bvdev-
> >bdev ? vdev_vq_worker : no_bdev_vdev_vq_worker,
> + vq, 0);
> + SPDK_INFOLOG(vhost, "%s.%u: started poller on lcore %d\n",
> + vsession->name, vq_idx, spdk_env_get_current_core());
> +
> + spdk_poller_register_interrupt(bvsession->requestq_poller[vq_idx],
> vhost_blk_poller_set_vq_interrupt_mode,
> + vq);
>
> - spdk_poller_register_interrupt(bvsession->requestq_poller,
> vhost_blk_poller_set_interrupt_mode,
> - bvsession);
>
> out:
> - vhost_session_start_done(vsession, rc);
> + vhost_session_start_vq_done(vq, rc);
> return rc;
> }
>
> static int
> vhost_blk_start(struct spdk_vhost_session *vsession)
> {
> - return vhost_session_send_event(vsession, vhost_blk_start_cb,
> + int rc;
> +
> + rc = vhost_session_send_event_mt(vsession, vhost_blk_start_vq_cb,
> 3, "start session");
> + if (rc) {
> + vhost_session_send_event_mt(vsession, vhost_blk_stop_vq_cb,
> + 3, "stop session");
> + }
> +
> + return rc;
> }
>
> static int
> -destroy_session_poller_cb(void *arg)
> +destroy_session_vq_poller_cb(void *arg)
> {
> - struct spdk_vhost_blk_session *bvsession = arg;
> - struct spdk_vhost_session *vsession = &bvsession->vsession;
> - int i;
> + struct spdk_vhost_virtqueue *vq = arg;
> + struct spdk_vhost_session *vsession = vq->vsession;
> + struct spdk_vhost_blk_session *bvsession = to_blk_session(vsession);
>
> - if (vsession->task_cnt > 0) {
> + if (vq->task_cnt > 0) {
> return SPDK_POLLER_BUSY;
> }
>
> @@ -1351,48 +1319,46 @@ destroy_session_poller_cb(void *arg)
> return SPDK_POLLER_BUSY;
> }
>
> - for (i = 0; i < vsession->max_queues; i++) {
> - vsession->virtqueue[i].next_event_time = 0;
> - vhost_vq_used_signal(vsession, &vsession->virtqueue[i]);
> - }
> + vq->next_event_time = 0;
> + vhost_vq_used_signal(vsession, vq);
>
> SPDK_INFOLOG(vhost, "%s: stopping poller on lcore %d\n",
> vsession->name, spdk_env_get_current_core());
>
> - if (bvsession->io_channel) {
> - spdk_put_io_channel(bvsession->io_channel);
> - bvsession->io_channel = NULL;
> + if (bvsession->io_channel[vq->vring_idx]) {
> + spdk_put_io_channel(bvsession->io_channel[vq->vring_idx]);
> + bvsession->io_channel[vq->vring_idx] = NULL;
> }
>
> - free_task_pool(bvsession);
> - spdk_poller_unregister(&bvsession->stop_poller);
> - vhost_session_stop_done(vsession, 0);
> + free_vq_task_pool(vq);
> + spdk_poller_unregister(&bvsession->stop_poller[vq->vring_idx]);
> + vhost_session_stop_vq_done(vq, 0);
>
> spdk_vhost_unlock();
> return SPDK_POLLER_BUSY;
> }
>
> static int
> -vhost_blk_stop_cb(struct spdk_vhost_dev *vdev,
> +vhost_blk_stop_vq_cb(struct spdk_vhost_dev *vdev,
> struct spdk_vhost_session *vsession, void *unused)
> {
> struct spdk_vhost_blk_session *bvsession = to_blk_session(vsession);
> + unsigned long vq_idx = (unsigned long)unused;
> + struct spdk_vhost_virtqueue *vq = &vsession->virtqueue[vq_idx];
>
> - spdk_poller_unregister(&bvsession->requestq_poller);
> -
> - if (vsession->virtqueue[0].intr) {
> - vhost_blk_session_unregister_interrupts(bvsession);
> + if (bvsession->requestq_poller[vq_idx]) {
> + spdk_poller_unregister(&bvsession->requestq_poller[vq_idx]);
> }
>
> - bvsession->stop_poller =
> SPDK_POLLER_REGISTER(destroy_session_poller_cb,
> - bvsession, 1000);
> + vhost_blk_session_unregister_vq_interrupts(bvsession, vq_idx);
> + bvsession->stop_poller[vq_idx] =
> SPDK_POLLER_REGISTER(destroy_session_vq_poller_cb, vq, 1000);
> return 0;
> }
>
> static int
> vhost_blk_stop(struct spdk_vhost_session *vsession)
> {
> - return vhost_session_send_event(vsession, vhost_blk_stop_cb,
> + return vhost_session_send_event_mt(vsession, vhost_blk_stop_vq_cb,
> 3, "stop session");
> }
>
> diff --git a/lib/vhost/vhost_internal.h b/lib/vhost/vhost_internal.h
> index 36ab0c16f..92d096a8c 100644
> --- a/lib/vhost/vhost_internal.h
> +++ b/lib/vhost/vhost_internal.h
> @@ -111,6 +111,14 @@ struct spdk_vhost_virtqueue {
>
> void *tasks;
>
> + int task_cnt;
> +
> + bool initialized;
> + bool started;
> + bool needs_restart;
> + bool forced_polling;
> + bool interrupt_mode;
> +
> /* Request count from last stats check */
> uint32_t req_cnt;
>
> @@ -150,10 +158,12 @@ struct spdk_vhost_session {
>
> struct rte_vhost_memory *mem;
>
> - int task_cnt;
> -
> uint16_t max_queues;
>
> + uint16_t active_queues;
> +
> + int task_cnt;
> +
> uint64_t negotiated_features;
>
> /* Local copy of device coalescing settings. */
> @@ -168,6 +178,8 @@ struct spdk_vhost_session {
>
> struct spdk_vhost_virtqueue virtqueue[SPDK_VHOST_MAX_VQUEUES];
>
> + struct spdk_thread *thread[SPDK_VHOST_MAX_VQUEUES]; /* thread of
> data plane per vq */
> +
> TAILQ_ENTRY(spdk_vhost_session) tailq;
> };
>
> @@ -175,7 +187,7 @@ struct spdk_vhost_dev {
> char *name;
> char *path;
>
> - struct spdk_thread *thread;
> + struct spdk_thread *thread; /* if support mt, only run as control plane */
> bool registered;
>
> uint64_t virtio_features;
> @@ -420,6 +432,7 @@ int vhost_destroy_connection_cb(int vid);
> * Set vhost session to run in interrupt or poll mode
> */
> void vhost_session_set_interrupt_mode(struct spdk_vhost_session *vsession,
> bool interrupt_mode);
> +void vhost_session_set_vq_interrupt_mode(struct spdk_vhost_virtqueue *vq,
> bool interrupt_mode);
>
> /*
> * Memory registration functions used in start/stop device callbacks
> @@ -464,6 +477,10 @@ int vhost_session_send_event(struct
> spdk_vhost_session *vsession,
> spdk_vhost_session_fn cb_fn, unsigned timeout_sec,
> const char *errmsg);
>
> +int vhost_session_send_event_mt(struct spdk_vhost_session *vsession,
> + spdk_vhost_session_fn cb_fn, unsigned timeout_sec,
> + const char *errmsg);
> +
> /**
> * Finish a blocking spdk_vhost_session_send_event() call and finally
> * start the session. This must be called on the target lcore, which
> @@ -477,6 +494,8 @@ int vhost_session_send_event(struct spdk_vhost_session
> *vsession,
> */
> void vhost_session_start_done(struct spdk_vhost_session *vsession, int
> response);
>
> +void vhost_session_start_vq_done(struct spdk_vhost_virtqueue *vq, int
> response);
> +
> /**
> * Finish a blocking spdk_vhost_session_send_event() call and finally
> * stop the session. This must be called on the session's lcore which
> @@ -493,6 +512,9 @@ void vhost_session_start_done(struct
> spdk_vhost_session *vsession, int response)
> */
> void vhost_session_stop_done(struct spdk_vhost_session *vsession, int
> response);
>
> +void vhost_session_stop_vq_done(struct spdk_vhost_virtqueue *vq, int
> response);
> +
> +
> struct spdk_vhost_session *vhost_session_find_by_vid(int vid);
> void vhost_session_install_rte_compat_hooks(struct spdk_vhost_session
> *vsession);
> int vhost_register_unix_socket(const char *path, const char *ctrl_name,
> --
> 2.14.1.40.g8e62ba1
> _______________________________________________
> SPDK mailing list -- spdk(a)lists.01.org
> To unsubscribe send an email to spdk-leave(a)lists.01.org