Re: [PATCH] virtiofs: Enable multiple request queues

From: Vivek Goyal
Date: Mon May 10 2021 - 13:50:19 EST


On Mon, May 10, 2021 at 11:15:21AM -0500, Connor Kuehl wrote:
> On 5/10/21 10:25 AM, Vivek Goyal wrote:
> > On Fri, May 07, 2021 at 03:15:27PM -0700, Connor Kuehl wrote:
> >> Distribute requests across the multiqueue complex automatically based
> >> on the IRQ affinity.
> >
> > Hi Connor,
> >
> > Thanks for the patch. I will look into it and also test it.
> >
> > How did you test it? Did you modify vitiofsd to support multiqueue. Did
> > you also run some performance numbers. Does it provide better/worse
> > performance as compared to single queue.
>
> Thanks, Vivek! I need to NACK this version of the patch for inclusion
> though since I think the way I did per-CPU state will not work for
> multiple virtio-fs mounts because it will be overwritten with each new
> mount, but for testing purposes this should be OK with just one mount.

Hi Connor,

Ok. Will wait for next version which fixes the multiple mount issue.

>
> I need to do more benchmarking on this.

That would be nice.

>
> I had to hack multiqueue support into virtiofsd, which runs against the
> warning in the virtiofsd source code that instructs people to *not*
> enable multiqueue due to thread-safety concerns. I didn't audit
> virtiofsd for correctness, so I also worry this has the potential of
> affecting benchmarks if there are races.

filesystem code already can handle multiple threads because on a single
queue we can have a thread pool processing requests in parallel. I am
not aware of any issues about supporting multiple queues. I think
may be fuse_virtio.c might require a little closer inspection to make
sure nothing is dependent on single queue.

>
> For testing, QEMU needs to be invoked with `num-request-queues` like
> this:
>
> -device vhost-user-fs-pci,chardev=char0,tag=myfs,num-request-queues=2
>
> And obviously you can choose any value >= 1 for num-request-queues.
>
> and I also made a quick-and-dirty hack to let me pass in the number of
> total queues to virtiofsd on the command line:

Ok. May be there is some inspiration to take from virtio-blk. How do they
specific number of queues by default and how many. I thought stefan mentioned
that by default there is one queue per vcpu.

Vivek

>
> diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c
> index 58e32fc963..cf8f132efd 100644
> --- a/tools/virtiofsd/fuse_lowlevel.c
> +++ b/tools/virtiofsd/fuse_lowlevel.c
> @@ -2565,9 +2565,9 @@ out1:
> return NULL;
> }
>
> -int fuse_session_mount(struct fuse_session *se)
> +int fuse_session_mount(struct fuse_session *se, unsigned int num_queues)
> {
> - return virtio_session_mount(se);
> + return virtio_session_mount(se, num_queues);
> }
>
> int fuse_session_fd(struct fuse_session *se)
> diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h
> index 3bf786b034..50bf86113d 100644
> --- a/tools/virtiofsd/fuse_lowlevel.h
> +++ b/tools/virtiofsd/fuse_lowlevel.h
> @@ -1842,7 +1842,7 @@ struct fuse_session *fuse_session_new(struct fuse_args *args,
> *
> * @return 0 on success, -1 on failure.
> **/
> -int fuse_session_mount(struct fuse_session *se);
> +int fuse_session_mount(struct fuse_session *se, unsigned int num_queues);
>
> /**
> * Enter a single threaded, blocking event loop.
> diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c
> index 3e13997406..8622c3dce6 100644
> --- a/tools/virtiofsd/fuse_virtio.c
> +++ b/tools/virtiofsd/fuse_virtio.c
> @@ -747,20 +747,6 @@ static void fv_queue_set_started(VuDev *dev, int qidx, bool started)
> started);
> assert(qidx >= 0);
>
> - /*
> - * Ignore additional request queues for now. passthrough_ll.c must be
> - * audited for thread-safety issues first. It was written with a
> - * well-behaved client in mind and may not protect against all types of
> - * races yet.
> - */
> - if (qidx > 1) {
> - fuse_log(FUSE_LOG_ERR,
> - "%s: multiple request queues not yet implemented, please only "
> - "configure 1 request queue\n",
> - __func__);
> - exit(EXIT_FAILURE);
> - }
> -
> if (started) {
> /* Fire up a thread to watch this queue */
> if (qidx >= vud->nqueues) {
> @@ -997,7 +983,7 @@ static int fv_create_listen_socket(struct fuse_session *se)
> return 0;
> }
>
> -int virtio_session_mount(struct fuse_session *se)
> +int virtio_session_mount(struct fuse_session *se, unsigned int num_queues)
> {
> int ret;
>
> @@ -1048,8 +1034,8 @@ int virtio_session_mount(struct fuse_session *se)
> se->vu_socketfd = data_sock;
> se->virtio_dev->se = se;
> pthread_rwlock_init(&se->virtio_dev->vu_dispatch_rwlock, NULL);
> - if (!vu_init(&se->virtio_dev->dev, 2, se->vu_socketfd, fv_panic, NULL,
> - fv_set_watch, fv_remove_watch, &fv_iface)) {
> + if (!vu_init(&se->virtio_dev->dev, num_queues, se->vu_socketfd,
> + fv_panic, NULL, fv_set_watch, fv_remove_watch, &fv_iface)) {
> fuse_log(FUSE_LOG_ERR, "%s: vu_init failed\n", __func__);
> return -1;
> }
> diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h
> index 111684032c..a0e78b9b84 100644
> --- a/tools/virtiofsd/fuse_virtio.h
> +++ b/tools/virtiofsd/fuse_virtio.h
> @@ -18,7 +18,7 @@
>
> struct fuse_session;
>
> -int virtio_session_mount(struct fuse_session *se);
> +int virtio_session_mount(struct fuse_session *se, unsigned int num_queues);
> void virtio_session_close(struct fuse_session *se);
> int virtio_loop(struct fuse_session *se);
>
> diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c
> index 1553d2ef45..9fd4e34980 100644
> --- a/tools/virtiofsd/passthrough_ll.c
> +++ b/tools/virtiofsd/passthrough_ll.c
> @@ -161,6 +161,7 @@ struct lo_data {
> int allow_direct_io;
> int announce_submounts;
> bool use_statx;
> + int num_vqs;
> struct lo_inode root;
> GHashTable *inodes; /* protected by lo->mutex */
> struct lo_map ino_map; /* protected by lo->mutex */
> @@ -204,6 +205,7 @@ static const struct fuse_opt lo_opts[] = {
> { "announce_submounts", offsetof(struct lo_data, announce_submounts), 1 },
> { "killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 1 },
> { "no_killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 0 },
> + { "num_queues=%d", offsetof(struct lo_data, num_vqs), 2 },
> FUSE_OPT_END
> };
> static bool use_syslog = false;
> @@ -3848,6 +3850,12 @@ int main(int argc, char *argv[])
> exit(1);
> }
>
> + if (lo.num_vqs < 2) {
> + fuse_log(FUSE_LOG_ERR, "num_queues must be at least 2 (got %d)\n",
> + lo.num_vqs);
> + exit(1);
> + }
> +
> lo.use_statx = true;
>
> se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
> @@ -3859,7 +3867,7 @@ int main(int argc, char *argv[])
> goto err_out2;
> }
>
> - if (fuse_session_mount(se) != 0) {
> + if (fuse_session_mount(se, lo.num_vqs) != 0) {
> goto err_out3;
> }
>
>