diff mbox

[6/7] vhost-user: add multiple queue support

Message ID 1442301035-7550-8-git-send-email-yuanhan.liu@linux.intel.com
State New
Headers show

Commit Message

Yuanhan Liu Sept. 15, 2015, 7:10 a.m. UTC
From: Changchun Ouyang <changchun.ouyang@intel.com>

This patch is initially based a patch from Nikolay Nikolaev.

Here is the latest version for adding vhost-user multiple queue support,
by creating a nc and vhost_net pair for each queue.

Qemu exits if find that the backend can't support number of requested
queues(by providing queues=# option). The max number is queried by a
new message, VHOST_USER_GET_QUEUE_NUM, and is sent only when protocol
feature VHOST_USER_PROTOCOL_F_MQ is present first.

The max queue check is done at vhost-user initiation stage. We initiate
one queue first, which, in the meantime, also gets the max_queues the
backend supports.

In older version, it was reported that some messages are sent more times
than necessary. Here we came an agreement with Michael that we could
categorize vhost user messages to 2 types: none-vring specific messages,
which should be sent only once, and vring specific messages, which should
be sent per queue.

Here I introduced a helper function vhost_user_one_time_request(), which
lists following messages as none-vring specific messages:

        VHOST_USER_GET_FEATURES
        VHOST_USER_SET_FEATURES
        VHOST_USER_GET_PROTOCOL_FEATURES
        VHOST_USER_SET_PROTOCOL_FEATURES
        VHOST_USER_SET_OWNER
        VHOST_USER_RESET_DEVICE
        VHOST_USER_SET_MEM_TABLE
        VHOST_USER_GET_QUEUE_NUM

For above messages, we simply ignore them when they are not sent the first
time.

v9: per suggested by Jason Wang, we could invoke qemu_chr_add_handlers()
    once only, and invoke qemu_find_net_clients_except() at the handler
    to gather all related ncs, for initiating all queues. Which addresses
    a hidden bug in older verion in a more QEMU-like way.

v8: set net->dev.vq_index correctly inside vhost_net_init() based on the
    value from net->nc->queue_index.

Signed-off-by: Nikolay Nikolaev <n.nikolaev@virtualopensystems.com>
Signed-off-by: Changchun Ouyang <changchun.ouyang@intel.com>
Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
---
 docs/specs/vhost-user.txt |  13 +++++
 hw/net/vhost_net.c        |  10 ++--
 hw/virtio/vhost-user.c    |  26 +++++++++
 hw/virtio/vhost.c         |   5 +-
 net/vhost-user.c          | 146 +++++++++++++++++++++++++++++++++-------------
 qapi-schema.json          |   6 +-
 qemu-options.hx           |   5 +-
 7 files changed, 161 insertions(+), 50 deletions(-)

Comments

Eric Blake Sept. 15, 2015, 3:02 p.m. UTC | #1
On 09/15/2015 01:10 AM, Yuanhan Liu wrote:
> From: Changchun Ouyang <changchun.ouyang@intel.com>
> 
> This patch is initially based a patch from Nikolay Nikolaev.
> 
> Here is the latest version for adding vhost-user multiple queue support,
> by creating a nc and vhost_net pair for each queue.

The phrase "Here is the latest version" doesn't make much sense in the
long term in git (that is, a year from now, we won't care how many
preliminary versions there were, just about the version that got
committed; and if anything in git changes after that point, it is no
longer the latest version).

> 
> Qemu exits if find that the backend can't support number of requested
> queues(by providing queues=# option). The max number is queried by a

space before ( in English.

> new message, VHOST_USER_GET_QUEUE_NUM, and is sent only when protocol
> feature VHOST_USER_PROTOCOL_F_MQ is present first.
> 
> The max queue check is done at vhost-user initiation stage. We initiate
> one queue first, which, in the meantime, also gets the max_queues the
> backend supports.
> 
> In older version, it was reported that some messages are sent more times
> than necessary. Here we came an agreement with Michael that we could
> categorize vhost user messages to 2 types: none-vring specific messages,

s/none/non/

> which should be sent only once, and vring specific messages, which should
> be sent per queue.
> 
> Here I introduced a helper function vhost_user_one_time_request(), which
> lists following messages as none-vring specific messages:

s/none/non/

> 
>         VHOST_USER_GET_FEATURES
>         VHOST_USER_SET_FEATURES
>         VHOST_USER_GET_PROTOCOL_FEATURES
>         VHOST_USER_SET_PROTOCOL_FEATURES
>         VHOST_USER_SET_OWNER
>         VHOST_USER_RESET_DEVICE
>         VHOST_USER_SET_MEM_TABLE
>         VHOST_USER_GET_QUEUE_NUM
> 
> For above messages, we simply ignore them when they are not sent the first
> time.
> 

Up to here is mostly fine for the commit message.  Meanwhile...

> v9: per suggested by Jason Wang, we could invoke qemu_chr_add_handlers()
>     once only, and invoke qemu_find_net_clients_except() at the handler
>     to gather all related ncs, for initiating all queues. Which addresses
>     a hidden bug in older verion in a more QEMU-like way.
> 
> v8: set net->dev.vq_index correctly inside vhost_net_init() based on the
>     value from net->nc->queue_index.

...this chunk here is useful only on the mailing list, and not in git,
and therefore, should appear...

> 
> Signed-off-by: Nikolay Nikolaev <n.nikolaev@virtualopensystems.com>
> Signed-off-by: Changchun Ouyang <changchun.ouyang@intel.com>
> Signed-off-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
> ---

...here, after the --- separator.

>  docs/specs/vhost-user.txt |  13 +++++
>  hw/net/vhost_net.c        |  10 ++--
>  hw/virtio/vhost-user.c    |  26 +++++++++
>  hw/virtio/vhost.c         |   5 +-
>  net/vhost-user.c          | 146 +++++++++++++++++++++++++++++++++-------------
>  qapi-schema.json          |   6 +-
>  qemu-options.hx           |   5 +-
>  7 files changed, 161 insertions(+), 50 deletions(-)
> 
> diff --git a/docs/specs/vhost-user.txt b/docs/specs/vhost-user.txt
> index 43db9b4..acf5708 100644
> --- a/docs/specs/vhost-user.txt
> +++ b/docs/specs/vhost-user.txt
> @@ -135,6 +135,19 @@ As older slaves don't support negotiating protocol features,
>  a feature bit was dedicated for this purpose:
>  #define VHOST_USER_F_PROTOCOL_FEATURES 30
>  
> +Multiple queue support
> +-------------------

Nit: the ---- line looks better when it is as long as the text it is
highlighting (3 bytes more)

> +Multiple queue is treated as a protocol extension, hence the slave has to
> +implement protocol features first. Multiple queues is supported only when

Sounds awkward. Either "Multiple queues are supported" or "The multiple
queues feature is supported"

> +the protocol feature VHOST_USER_PROTOCOL_F_MQ(bit 0) is set.

space before ( in English.

> +
> +The max number of queues the slave supports can be queried with message
> +VHOST_USER_GET_PROTOCOL_FEATURES. Master should stop when the number of
> +requested queues is bigger than that.
> +
> +As all queues share one connection, the master uses a unique index for each
> +queue in the sent message to identify a specified queue.
> +
>  Message types
>  -------------
>  

> @@ -207,6 +224,15 @@ static int vhost_user_call(struct vhost_dev *dev, unsigned long int request,
>          msg_request = request;
>      }
>  
> +    /*
> +     * For none-vring specific requests, like VHOST_USER_GET_FEATURES,

s/none/non/

> +     * we just need send it once in the first time. For later such
> +     * request, we just ignore it.
> +     */
> +    if (vhost_user_one_time_request(msg_request) && dev->vq_index != 0) {
> +        return 0;
> +    }

> +++ b/qapi-schema.json
> @@ -2480,12 +2480,16 @@
>  #
>  # @vhostforce: #optional vhost on for non-MSIX virtio guests (default: false).
>  #
> +# @queues: #optional number of queues to be created for multiqueue vhost-user
> +#          (default: 1) (Since 2.5)
> +#
>  # Since 2.1
>  ##
>  { 'struct': 'NetdevVhostUserOptions',
>    'data': {
>      'chardev':        'str',
> -    '*vhostforce':    'bool' } }
> +    '*vhostforce':    'bool',
> +    '*queues':        'int' } }

Interface looks okay.
Yuanhan Liu Sept. 16, 2015, 2:06 a.m. UTC | #2
On Tue, Sep 15, 2015 at 09:02:07AM -0600, Eric Blake wrote:
> On 09/15/2015 01:10 AM, Yuanhan Liu wrote:
> > From: Changchun Ouyang <changchun.ouyang@intel.com>
> > 
> > This patch is initially based a patch from Nikolay Nikolaev.
> > 
> > Here is the latest version for adding vhost-user multiple queue support,
> > by creating a nc and vhost_net pair for each queue.
> 
> The phrase "Here is the latest version" doesn't make much sense in the
> long term in git (that is, a year from now, we won't care how many
> preliminary versions there were, just about the version that got
> committed; and if anything in git changes after that point, it is no
> longer the latest version).

Yeah, good point.

> 
> > 
> > Qemu exits if find that the backend can't support number of requested
> > queues(by providing queues=# option). The max number is queried by a
> 
> space before ( in English.

Michael reminded me behore, and sorry for making such mistake again.

And thanks for other typo corrections.

> 
> > new message, VHOST_USER_GET_QUEUE_NUM, and is sent only when protocol
> > feature VHOST_USER_PROTOCOL_F_MQ is present first.
> > 
> > The max queue check is done at vhost-user initiation stage. We initiate
> > one queue first, which, in the meantime, also gets the max_queues the
> > backend supports.
> > 
> > In older version, it was reported that some messages are sent more times
> > than necessary. Here we came an agreement with Michael that we could
> > categorize vhost user messages to 2 types: none-vring specific messages,
> 
> s/none/non/
> 
> > which should be sent only once, and vring specific messages, which should
> > be sent per queue.
> > 
> > Here I introduced a helper function vhost_user_one_time_request(), which
> > lists following messages as none-vring specific messages:
> 
> s/none/non/
> 
> > 
> >         VHOST_USER_GET_FEATURES
> >         VHOST_USER_SET_FEATURES
> >         VHOST_USER_GET_PROTOCOL_FEATURES
> >         VHOST_USER_SET_PROTOCOL_FEATURES
> >         VHOST_USER_SET_OWNER
> >         VHOST_USER_RESET_DEVICE
> >         VHOST_USER_SET_MEM_TABLE
> >         VHOST_USER_GET_QUEUE_NUM
> > 
> > For above messages, we simply ignore them when they are not sent the first
> > time.
> > 
> 
> Up to here is mostly fine for the commit message.  Meanwhile...
> 
> > v9: per suggested by Jason Wang, we could invoke qemu_chr_add_handlers()
> >     once only, and invoke qemu_find_net_clients_except() at the handler
> >     to gather all related ncs, for initiating all queues. Which addresses
> >     a hidden bug in older verion in a more QEMU-like way.
> > 
> > v8: set net->dev.vq_index correctly inside vhost_net_init() based on the
> >     value from net->nc->queue_index.
> 
> ...this chunk here is useful only on the mailing list, and not in git,
> and therefore, should appear...

Sorry that I may disagree here.

First of all, it does no harm at all to include few old version
descriptions. Instead, it may give some hints to the reader how
the patch evolves. Meanwhile, you will find they are quite common
in some well known open source projects, such as linux kernel:

    [yliu@yliu-dev ~/linux]$ git log | grep '\<[vV][1-9][0-9]*:' | wc -l
    10828

You can find them at qemu project (though not that common), too:

    [yliu@yliu-dev ~/qemu]$ git log | grep '\<[vV][1-9][0-9]*:' | wc -l
    390

So, if it's a qemu community specific culture, I could do what
you suggested.

If not, I'd like to put them into the commit log, as putting it
outside the commit log gives unnecessary extra burden to patch
author when he need update several version change information
in a patch set: he has to format the patch set first, and add
them one by one by editing those patches.

Thanks.

	--yliu
Yuanhan Liu Sept. 16, 2015, 2:48 a.m. UTC | #3
On Wed, Sep 16, 2015 at 10:06:56AM +0800, Yuanhan Liu wrote:
> On Tue, Sep 15, 2015 at 09:02:07AM -0600, Eric Blake wrote:
> > On 09/15/2015 01:10 AM, Yuanhan Liu wrote:
> > > From: Changchun Ouyang <changchun.ouyang@intel.com>
> > > 
> > > This patch is initially based a patch from Nikolay Nikolaev.
> > > 
> > > Here is the latest version for adding vhost-user multiple queue support,
> > > by creating a nc and vhost_net pair for each queue.
> > 
> > The phrase "Here is the latest version" doesn't make much sense in the
> > long term in git (that is, a year from now, we won't care how many
> > preliminary versions there were, just about the version that got
> > committed; and if anything in git changes after that point, it is no
> > longer the latest version).
> 
> Yeah, good point.
> 
> > 
> > > 
> > > Qemu exits if find that the backend can't support number of requested
> > > queues(by providing queues=# option). The max number is queried by a
> > 
> > space before ( in English.
> 
> Michael reminded me behore, and sorry for making such mistake again.
> 
> And thanks for other typo corrections.
> 
> > 
> > > new message, VHOST_USER_GET_QUEUE_NUM, and is sent only when protocol
> > > feature VHOST_USER_PROTOCOL_F_MQ is present first.
> > > 
> > > The max queue check is done at vhost-user initiation stage. We initiate
> > > one queue first, which, in the meantime, also gets the max_queues the
> > > backend supports.
> > > 
> > > In older version, it was reported that some messages are sent more times
> > > than necessary. Here we came an agreement with Michael that we could
> > > categorize vhost user messages to 2 types: none-vring specific messages,
> > 
> > s/none/non/
> > 
> > > which should be sent only once, and vring specific messages, which should
> > > be sent per queue.
> > > 
> > > Here I introduced a helper function vhost_user_one_time_request(), which
> > > lists following messages as none-vring specific messages:
> > 
> > s/none/non/
> > 
> > > 
> > >         VHOST_USER_GET_FEATURES
> > >         VHOST_USER_SET_FEATURES
> > >         VHOST_USER_GET_PROTOCOL_FEATURES
> > >         VHOST_USER_SET_PROTOCOL_FEATURES
> > >         VHOST_USER_SET_OWNER
> > >         VHOST_USER_RESET_DEVICE
> > >         VHOST_USER_SET_MEM_TABLE
> > >         VHOST_USER_GET_QUEUE_NUM
> > > 
> > > For above messages, we simply ignore them when they are not sent the first
> > > time.
> > > 
> > 
> > Up to here is mostly fine for the commit message.  Meanwhile...
> > 
> > > v9: per suggested by Jason Wang, we could invoke qemu_chr_add_handlers()
> > >     once only, and invoke qemu_find_net_clients_except() at the handler
> > >     to gather all related ncs, for initiating all queues. Which addresses
> > >     a hidden bug in older verion in a more QEMU-like way.
> > > 
> > > v8: set net->dev.vq_index correctly inside vhost_net_init() based on the
> > >     value from net->nc->queue_index.
> > 
> > ...this chunk here is useful only on the mailing list, and not in git,
> > and therefore, should appear...
> 
> Sorry that I may disagree here.
> 
> First of all, it does no harm at all to include few old version
> descriptions. Instead, it may give some hints to the reader how
> the patch evolves. Meanwhile, you will find they are quite common
> in some well known open source projects, such as linux kernel:
> 
>     [yliu@yliu-dev ~/linux]$ git log | grep '\<[vV][1-9][0-9]*:' | wc -l
>     10828
> 
> You can find them at qemu project (though not that common), too:
> 
>     [yliu@yliu-dev ~/qemu]$ git log | grep '\<[vV][1-9][0-9]*:' | wc -l
>     390
> 
> So, if it's a qemu community specific culture, I could do what
> you suggested.
> 
> If not, I'd like to put them into the commit log, as putting it
> outside the commit log gives unnecessary extra burden to patch
> author when he need update several version change information
> in a patch set: he has to format the patch set first, and add
> them one by one by editing those patches.

Oops, I just found we can do that while editing the commit log (IIRC,
I had a similar try long time ago, and it seems it didn't work).

	--yliu
Michael S. Tsirkin Sept. 16, 2015, 8:10 a.m. UTC | #4
On Wed, Sep 16, 2015 at 10:06:56AM +0800, Yuanhan Liu wrote:
> If not, I'd like to put them into the commit log, as putting it
> outside the commit log gives unnecessary extra burden to patch
> author when he need update several version change information
> in a patch set: he has to format the patch set first, and add
> them one by one by editing those patches.

I personally just describe the changes in the cover letter.

In fact, while not a lot of people do this, git-backport-diff
(from https://github.com/codyprime/git-scripts.git)
is a handy tool to show which patches changed since the
previous version, and it only works if the change log
is in the cover letter.

Or you can stick --- in the commit log as you have found out -
but that depends on the patch author being careful to log
all edits.
Yuanhan Liu Sept. 16, 2015, 8:23 a.m. UTC | #5
On Wed, Sep 16, 2015 at 11:10:35AM +0300, Michael S. Tsirkin wrote:
> On Wed, Sep 16, 2015 at 10:06:56AM +0800, Yuanhan Liu wrote:
> > If not, I'd like to put them into the commit log, as putting it
> > outside the commit log gives unnecessary extra burden to patch
> > author when he need update several version change information
> > in a patch set: he has to format the patch set first, and add
> > them one by one by editing those patches.
> 
> I personally just describe the changes in the cover letter.

I did that as well. I just thought metioning them inside the patch
again will make it clear for you to review.

	--yliu
> 
> In fact, while not a lot of people do this, git-backport-diff
> (from https://github.com/codyprime/git-scripts.git)
> is a handy tool to show which patches changed since the
> previous version, and it only works if the change log
> is in the cover letter.
> 
> Or you can stick --- in the commit log as you have found out -
> but that depends on the patch author being careful to log
> all edits.
> -- 
> MST
Eric Blake Sept. 16, 2015, 2:15 p.m. UTC | #6
On 09/15/2015 08:06 PM, Yuanhan Liu wrote:

>> Up to here is mostly fine for the commit message.  Meanwhile...
>>
>>> v9: per suggested by Jason Wang, we could invoke qemu_chr_add_handlers()
>>>     once only, and invoke qemu_find_net_clients_except() at the handler
>>>     to gather all related ncs, for initiating all queues. Which addresses
>>>     a hidden bug in older verion in a more QEMU-like way.
>>>
>>> v8: set net->dev.vq_index correctly inside vhost_net_init() based on the
>>>     value from net->nc->queue_index.
>>
>> ...this chunk here is useful only on the mailing list, and not in git,
>> and therefore, should appear...
> 
> Sorry that I may disagree here.
> 
> First of all, it does no harm at all to include few old version
> descriptions. Instead, it may give some hints to the reader how
> the patch evolves. Meanwhile, you will find they are quite common
> in some well known open source projects, such as linux kernel:

There's nothing wrong with including patch changelog in your mail; in
fact it is encouraged by:
http://wiki.qemu.org/Contribute/SubmitAPatch

However, we DO request that you put it after ---, because we do NOT want
it in qemu.git log.

I don't care if the kernel has a slightly different policy, and is more
tolerant of changelogs leaked into the git log.


> You can find them at qemu project (though not that common), too:
> 
>     [yliu@yliu-dev ~/qemu]$ git log | grep '\<[vV][1-9][0-9]*:' | wc -l
>     390

They're not common for a reason - we've documented that we don't like
them in git.  And that's because we expect the --- separator between the
git message and the changelog.

> 
> So, if it's a qemu community specific culture, I could do what
> you suggested.

Yes, since it is documented at
http://wiki.qemu.org/Contribute/SubmitAPatch, it is qemu culture.
Yuanhan Liu Sept. 16, 2015, 2:53 p.m. UTC | #7
On Wed, Sep 16, 2015 at 08:15:59AM -0600, Eric Blake wrote:
> On 09/15/2015 08:06 PM, Yuanhan Liu wrote:
> 
> >> Up to here is mostly fine for the commit message.  Meanwhile...
> >>
> >>> v9: per suggested by Jason Wang, we could invoke qemu_chr_add_handlers()
> >>>     once only, and invoke qemu_find_net_clients_except() at the handler
> >>>     to gather all related ncs, for initiating all queues. Which addresses
> >>>     a hidden bug in older verion in a more QEMU-like way.
> >>>
> >>> v8: set net->dev.vq_index correctly inside vhost_net_init() based on the
> >>>     value from net->nc->queue_index.
> >>
> >> ...this chunk here is useful only on the mailing list, and not in git,
> >> and therefore, should appear...
> > 
> > Sorry that I may disagree here.
> > 
> > First of all, it does no harm at all to include few old version
> > descriptions. Instead, it may give some hints to the reader how
> > the patch evolves. Meanwhile, you will find they are quite common
> > in some well known open source projects, such as linux kernel:
> 
> There's nothing wrong with including patch changelog in your mail; in
> fact it is encouraged by:
> http://wiki.qemu.org/Contribute/SubmitAPatch
> 
> However, we DO request that you put it after ---, because we do NOT want
> it in qemu.git log.
> 
> I don't care if the kernel has a slightly different policy, and is more
> tolerant of changelogs leaked into the git log.
> 
> 
> > You can find them at qemu project (though not that common), too:
> > 
> >     [yliu@yliu-dev ~/qemu]$ git log | grep '\<[vV][1-9][0-9]*:' | wc -l
> >     390
> 
> They're not common for a reason - we've documented that we don't like
> them in git.  And that's because we expect the --- separator between the
> git message and the changelog.
> 
> > 
> > So, if it's a qemu community specific culture, I could do what
> > you suggested.
> 
> Yes, since it is documented at
> http://wiki.qemu.org/Contribute/SubmitAPatch, it is qemu culture.

Good to know, then.

Thanks.

	--yliu
diff mbox

Patch

diff --git a/docs/specs/vhost-user.txt b/docs/specs/vhost-user.txt
index 43db9b4..acf5708 100644
--- a/docs/specs/vhost-user.txt
+++ b/docs/specs/vhost-user.txt
@@ -135,6 +135,19 @@  As older slaves don't support negotiating protocol features,
 a feature bit was dedicated for this purpose:
 #define VHOST_USER_F_PROTOCOL_FEATURES 30
 
+Multiple queue support
+-------------------
+Multiple queue is treated as a protocol extension, hence the slave has to
+implement protocol features first. Multiple queues is supported only when
+the protocol feature VHOST_USER_PROTOCOL_F_MQ(bit 0) is set.
+
+The max number of queues the slave supports can be queried with message
+VHOST_USER_GET_PROTOCOL_FEATURES. Master should stop when the number of
+requested queues is bigger than that.
+
+As all queues share one connection, the master uses a unique index for each
+queue in the sent message to identify a specified queue.
+
 Message types
 -------------
 
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index f9441e9..c114304 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -148,8 +148,11 @@  struct vhost_net *vhost_net_init(VhostNetOptions *options)
         fprintf(stderr, "vhost-net requires net backend to be setup\n");
         goto fail;
     }
+    net->nc = options->net_backend;
 
     net->dev.max_queues = 1;
+    net->dev.nvqs = 2;
+    net->dev.vqs = net->vqs;
 
     if (backend_kernel) {
         r = vhost_net_get_fd(options->net_backend);
@@ -164,11 +167,10 @@  struct vhost_net *vhost_net_init(VhostNetOptions *options)
         net->dev.backend_features = 0;
         net->dev.protocol_features = 0;
         net->backend = -1;
-    }
-    net->nc = options->net_backend;
 
-    net->dev.nvqs = 2;
-    net->dev.vqs = net->vqs;
+        /* vhost-user needs vq_index to initiate a specific queue pair */
+        net->dev.vq_index = net->nc->queue_index * net->dev.nvqs;
+    }
 
     r = vhost_dev_init(&net->dev, options->opaque,
                        options->backend_type);
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 69b085b..dfb5a61 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -187,6 +187,23 @@  static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg,
             0 : -1;
 }
 
+static bool vhost_user_one_time_request(VhostUserRequest request)
+{
+    switch (request) {
+    case VHOST_USER_GET_FEATURES:
+    case VHOST_USER_SET_FEATURES:
+    case VHOST_USER_GET_PROTOCOL_FEATURES:
+    case VHOST_USER_SET_PROTOCOL_FEATURES:
+    case VHOST_USER_SET_OWNER:
+    case VHOST_USER_RESET_DEVICE:
+    case VHOST_USER_SET_MEM_TABLE:
+    case VHOST_USER_GET_QUEUE_NUM:
+        return true;
+    default:
+        return false;
+    }
+}
+
 static int vhost_user_call(struct vhost_dev *dev, unsigned long int request,
         void *arg)
 {
@@ -207,6 +224,15 @@  static int vhost_user_call(struct vhost_dev *dev, unsigned long int request,
         msg_request = request;
     }
 
+    /*
+     * For none-vring specific requests, like VHOST_USER_GET_FEATURES,
+     * we just need send it once in the first time. For later such
+     * request, we just ignore it.
+     */
+    if (vhost_user_one_time_request(msg_request) && dev->vq_index != 0) {
+        return 0;
+    }
+
     msg.request = msg_request;
     msg.flags = VHOST_USER_VERSION;
     msg.size = 0;
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 717a0ba..14518d0 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -874,8 +874,9 @@  static void vhost_eventfd_del(MemoryListener *listener,
 static int vhost_virtqueue_init(struct vhost_dev *dev,
                                 struct vhost_virtqueue *vq, int n)
 {
+    int vhost_vq_index = dev->vhost_ops->vhost_backend_get_vq_index(dev, n);
     struct vhost_vring_file file = {
-        .index = n,
+        .index = vhost_vq_index,
     };
     int r = event_notifier_init(&vq->masked_notifier, 0);
     if (r < 0) {
@@ -926,7 +927,7 @@  int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
     }
 
     for (i = 0; i < hdev->nvqs; ++i) {
-        r = vhost_virtqueue_init(hdev, hdev->vqs + i, i);
+        r = vhost_virtqueue_init(hdev, hdev->vqs + i, hdev->vq_index + i);
         if (r < 0) {
             goto fail_vq;
         }
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 93dcecd..4fa3d64 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -39,37 +39,77 @@  static int vhost_user_running(VhostUserState *s)
     return (s->vhost_net) ? 1 : 0;
 }
 
-static int vhost_user_start(VhostUserState *s)
+static void vhost_user_stop(int queues, NetClientState *ncs[])
 {
-    VhostNetOptions options;
-
-    if (vhost_user_running(s)) {
-        return 0;
-    }
+    VhostUserState *s;
+    int i;
 
-    options.backend_type = VHOST_BACKEND_TYPE_USER;
-    options.net_backend = &s->nc;
-    options.opaque = s->chr;
+    for (i = 0; i < queues; i++) {
+        assert (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
 
-    s->vhost_net = vhost_net_init(&options);
+        s = DO_UPCAST(VhostUserState, nc, ncs[i]);
+        if (!vhost_user_running(s)) {
+            continue;
+        }
 
-    return vhost_user_running(s) ? 0 : -1;
+        if (s->vhost_net) {
+            vhost_net_cleanup(s->vhost_net);
+            s->vhost_net = NULL;
+        }
+    }
 }
 
-static void vhost_user_stop(VhostUserState *s)
+static int vhost_user_start(int queues, NetClientState *ncs[])
 {
-    if (vhost_user_running(s)) {
-        vhost_net_cleanup(s->vhost_net);
+    VhostNetOptions options;
+    VhostUserState *s;
+    int max_queues;
+    int i;
+
+    options.backend_type = VHOST_BACKEND_TYPE_USER;
+
+    for (i = 0; i < queues; i++) {
+        assert (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+
+        s = DO_UPCAST(VhostUserState, nc, ncs[i]);
+        if (vhost_user_running(s)) {
+            continue;
+        }
+
+        options.net_backend = ncs[i];
+        options.opaque      = s->chr;
+        s->vhost_net = vhost_net_init(&options);
+        if (!s->vhost_net) {
+            error_report("failed to init vhost_net for queue %d\n", i);
+            goto err;
+        }
+
+        if (i == 0) {
+            max_queues = vhost_net_get_max_queues(s->vhost_net);
+            if (queues > max_queues) {
+                error_report("you are asking more queues than "
+                             "supported: %d\n", max_queues);
+                goto err;
+            }
+        }
     }
 
-    s->vhost_net = 0;
+    return 0;
+
+err:
+    vhost_user_stop(i + 1, ncs);
+    return -1;
 }
 
 static void vhost_user_cleanup(NetClientState *nc)
 {
     VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
 
-    vhost_user_stop(s);
+    if (s->vhost_net) {
+        vhost_net_cleanup(s->vhost_net);
+        s->vhost_net = NULL;
+    }
+
     qemu_purge_queued_packets(nc);
 }
 
@@ -95,59 +135,81 @@  static NetClientInfo net_vhost_user_info = {
         .has_ufo = vhost_user_has_ufo,
 };
 
-static void net_vhost_link_down(VhostUserState *s, bool link_down)
+static void net_vhost_link_down(int queues, NetClientState *ncs[],
+                                bool link_down)
 {
-    s->nc.link_down = link_down;
+    NetClientState *nc;
+    int i;
 
-    if (s->nc.peer) {
-        s->nc.peer->link_down = link_down;
-    }
+    for (i = 0; i < queues; i++) {
+        nc = ncs[i];
 
-    if (s->nc.info->link_status_changed) {
-        s->nc.info->link_status_changed(&s->nc);
-    }
+        nc->link_down = link_down;
+
+        if (nc->peer) {
+            nc->peer->link_down = link_down;
+        }
+
+        if (nc->info->link_status_changed) {
+            nc->info->link_status_changed(nc);
+        }
 
-    if (s->nc.peer && s->nc.peer->info->link_status_changed) {
-        s->nc.peer->info->link_status_changed(s->nc.peer);
+        if (nc->peer && nc->peer->info->link_status_changed) {
+            nc->peer->info->link_status_changed(nc->peer);
+        }
     }
 }
 
 static void net_vhost_user_event(void *opaque, int event)
 {
-    VhostUserState *s = opaque;
+    const char *name = opaque;
+    NetClientState *ncs[MAX_QUEUE_NUM];
+    VhostUserState *s;
+    int queues;
 
+    queues = qemu_find_net_clients_except(name, ncs,
+                                          NET_CLIENT_OPTIONS_KIND_NIC,
+                                          MAX_QUEUE_NUM);
+    s = DO_UPCAST(VhostUserState, nc, ncs[0]);
     switch (event) {
     case CHR_EVENT_OPENED:
-        vhost_user_start(s);
-        net_vhost_link_down(s, false);
+        if (vhost_user_start(queues, ncs) < 0) {
+            exit(1);
+        }
+        net_vhost_link_down(queues, ncs, false);
         error_report("chardev \"%s\" went up", s->chr->label);
         break;
     case CHR_EVENT_CLOSED:
-        net_vhost_link_down(s, true);
-        vhost_user_stop(s);
+        net_vhost_link_down(queues, ncs, true);
+        vhost_user_stop(queues, ncs);
         error_report("chardev \"%s\" went down", s->chr->label);
         break;
     }
 }
 
 static int net_vhost_user_init(NetClientState *peer, const char *device,
-                               const char *name, CharDriverState *chr)
+                               const char *name, CharDriverState *chr,
+                               int queues)
 {
     NetClientState *nc;
     VhostUserState *s;
+    int i;
 
-    nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
+    for (i = 0; i < queues; i++) {
+        nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
 
-    snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user to %s",
-             chr->label);
+        snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s",
+                 i, chr->label);
 
-    s = DO_UPCAST(VhostUserState, nc, nc);
+        /* We don't provide a receive callback */
+        nc->receive_disabled = 1;
+        nc->queue_index = i;
 
-    /* We don't provide a receive callback */
-    s->nc.receive_disabled = 1;
-    s->chr = chr;
+        s = DO_UPCAST(VhostUserState, nc, nc);
+        s->chr = chr;
+    }
 
-    qemu_chr_add_handlers(s->chr, NULL, NULL, net_vhost_user_event, s);
+    qemu_chr_add_handlers(chr, NULL, NULL, net_vhost_user_event, (void*)name);
 
     return 0;
 }
@@ -226,6 +288,7 @@  static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
 int net_init_vhost_user(const NetClientOptions *opts, const char *name,
                         NetClientState *peer, Error **errp)
 {
+    int queues;
     const NetdevVhostUserOptions *vhost_user_opts;
     CharDriverState *chr;
 
@@ -243,6 +306,7 @@  int net_init_vhost_user(const NetClientOptions *opts, const char *name,
         return -1;
     }
 
+    queues = vhost_user_opts->has_queues ? vhost_user_opts->queues : 1;
 
-    return net_vhost_user_init(peer, "vhost_user", name, chr);
+    return net_vhost_user_init(peer, "vhost_user", name, chr, queues);
 }
diff --git a/qapi-schema.json b/qapi-schema.json
index 67fef37..55c33db 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2480,12 +2480,16 @@ 
 #
 # @vhostforce: #optional vhost on for non-MSIX virtio guests (default: false).
 #
+# @queues: #optional number of queues to be created for multiqueue vhost-user
+#          (default: 1) (Since 2.5)
+#
 # Since 2.1
 ##
 { 'struct': 'NetdevVhostUserOptions',
   'data': {
     'chardev':        'str',
-    '*vhostforce':    'bool' } }
+    '*vhostforce':    'bool',
+    '*queues':        'int' } }
 
 ##
 # @NetClientOptions
diff --git a/qemu-options.hx b/qemu-options.hx
index 77f5853..5bfa7a3 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1963,13 +1963,14 @@  The hubport netdev lets you connect a NIC to a QEMU "vlan" instead of a single
 netdev.  @code{-net} and @code{-device} with parameter @option{vlan} create the
 required hub automatically.
 
-@item -netdev vhost-user,chardev=@var{id}[,vhostforce=on|off]
+@item -netdev vhost-user,chardev=@var{id}[,vhostforce=on|off][,queues=n]
 
 Establish a vhost-user netdev, backed by a chardev @var{id}. The chardev should
 be a unix domain socket backed one. The vhost-user uses a specifically defined
 protocol to pass vhost ioctl replacement messages to an application on the other
 end of the socket. On non-MSIX guests, the feature can be forced with
-@var{vhostforce}.
+@var{vhostforce}. Use 'queues=@var{n}' to specify the number of queues to
+be created for multiqueue vhost-user.
 
 Example:
 @example