diff mbox series

[v2,11/13] block/export: convert vhost-user-blk server to block export API

Message ID 20200924151549.913737-12-stefanha@redhat.com
State Superseded
Headers show
Series block/export: convert vhost-user-blk-server to block exports API | expand

Commit Message

Stefan Hajnoczi Sept. 24, 2020, 3:15 p.m. UTC
Use the new QAPI block exports API instead of defining our own QOM
objects.

This is a large change because the lifecycle of VuBlockDev needs to
follow BlockExportDriver. QOM properties are replaced by QAPI options
objects.

VuBlockDev is renamed VuBlkExport and contains a BlockExport field.
Several fields can be dropped since BlockExport already has equivalents.

The file names and meson build integration will be adjusted in a future
patch. libvhost-user should probably be built as a static library that
is linked into QEMU instead of as a .c file that results in duplicate
compilation.

The new command-line syntax is:

  $ qemu-storage-daemon \
      --blockdev file,node-name=drive0,filename=test.img \
      --export vhost-user-blk,node-name=drive0,id=export0,unix-socket=/tmp/vhost-user-blk.sock

Note that unix-socket is optional because we may wish to accept chardevs
too in the future.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
v2:
 * Replace str unix-socket with SocketAddress addr to match NBD and
   support file descriptor passing
 * Make addr mandatory [Markus]
 * Update vhost-user-blk-test.c to use --export syntax
---
 qapi/block-export.json               |  21 +-
 block/export/vhost-user-blk-server.h |  23 +-
 block/export/export.c                |   8 +-
 block/export/vhost-user-blk-server.c | 452 +++++++--------------------
 tests/qtest/vhost-user-blk-test.c    |   2 +-
 util/vhost-user-server.c             |  10 +-
 block/export/meson.build             |   1 +
 block/meson.build                    |   1 -
 8 files changed, 158 insertions(+), 360 deletions(-)

Comments

Markus Armbruster Sept. 30, 2020, 5:28 a.m. UTC | #1
Stefan Hajnoczi <stefanha@redhat.com> writes:

> Use the new QAPI block exports API instead of defining our own QOM

> objects.

>

> This is a large change because the lifecycle of VuBlockDev needs to

> follow BlockExportDriver. QOM properties are replaced by QAPI options

> objects.

>

> VuBlockDev is renamed VuBlkExport and contains a BlockExport field.

> Several fields can be dropped since BlockExport already has equivalents.

>

> The file names and meson build integration will be adjusted in a future

> patch. libvhost-user should probably be built as a static library that

> is linked into QEMU instead of as a .c file that results in duplicate

> compilation.

>

> The new command-line syntax is:

>

>   $ qemu-storage-daemon \

>       --blockdev file,node-name=drive0,filename=test.img \

>       --export vhost-user-blk,node-name=drive0,id=export0,unix-socket=/tmp/vhost-user-blk.sock

>

> Note that unix-socket is optional because we may wish to accept chardevs

> too in the future.

>

> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>

> ---

> v2:

>  * Replace str unix-socket with SocketAddress addr to match NBD and

>    support file descriptor passing

>  * Make addr mandatory [Markus]

>  * Update vhost-user-blk-test.c to use --export syntax

> ---

>  qapi/block-export.json               |  21 +-

>  block/export/vhost-user-blk-server.h |  23 +-

>  block/export/export.c                |   8 +-

>  block/export/vhost-user-blk-server.c | 452 +++++++--------------------

>  tests/qtest/vhost-user-blk-test.c    |   2 +-

>  util/vhost-user-server.c             |  10 +-

>  block/export/meson.build             |   1 +

>  block/meson.build                    |   1 -

>  8 files changed, 158 insertions(+), 360 deletions(-)

>

> diff --git a/qapi/block-export.json b/qapi/block-export.json

> index ace0d66e17..2e44625bb1 100644

> --- a/qapi/block-export.json

> +++ b/qapi/block-export.json

> @@ -84,6 +84,21 @@

>    'data': { '*name': 'str', '*description': 'str',

>              '*bitmap': 'str' } }

>  

> +##

> +# @BlockExportOptionsVhostUserBlk:

> +#

> +# A vhost-user-blk block export.

> +#

> +# @addr: The vhost-user socket on which to listen. Both 'unix' and 'fd'

> +#        SocketAddress types are supported. Passed fds must be UNIX domain

> +#        sockets.


"addr.type must be 'unix' or 'fd'" is not visible in introspection.
Awkward.  Practical problem only if other addresses ever become
available here.  Is that possible?

> +# @logical-block-size: Logical block size in bytes. Defaults to 512 bytes.

> +#

> +# Since: 5.2

> +##

> +{ 'struct': 'BlockExportOptionsVhostUserBlk',

> +  'data': { 'addr': 'SocketAddress', '*logical-block-size': 'size' } }

> +

>  ##

>  # @NbdServerAddOptions:

>  #

> @@ -180,11 +195,12 @@

>  # An enumeration of block export types

>  #

>  # @nbd: NBD export

> +# @vhost-user-blk: vhost-user-blk export (since 5.2)

>  #

>  # Since: 4.2

>  ##

>  { 'enum': 'BlockExportType',

> -  'data': [ 'nbd' ] }

> +  'data': [ 'nbd', 'vhost-user-blk' ] }

>  

>  ##

>  # @BlockExportOptions:

> @@ -213,7 +229,8 @@

>              '*writethrough': 'bool' },

>    'discriminator': 'type',

>    'data': {

> -      'nbd': 'BlockExportOptionsNbd'

> +      'nbd': 'BlockExportOptionsNbd',

> +      'vhost-user-blk': 'BlockExportOptionsVhostUserBlk'

>     } }

>  

>  ##

[...]
Stefan Hajnoczi Sept. 30, 2020, 9:45 a.m. UTC | #2
On Wed, Sep 30, 2020 at 07:28:58AM +0200, Markus Armbruster wrote:
> Stefan Hajnoczi <stefanha@redhat.com> writes:

> 

> > Use the new QAPI block exports API instead of defining our own QOM

> > objects.

> >

> > This is a large change because the lifecycle of VuBlockDev needs to

> > follow BlockExportDriver. QOM properties are replaced by QAPI options

> > objects.

> >

> > VuBlockDev is renamed VuBlkExport and contains a BlockExport field.

> > Several fields can be dropped since BlockExport already has equivalents.

> >

> > The file names and meson build integration will be adjusted in a future

> > patch. libvhost-user should probably be built as a static library that

> > is linked into QEMU instead of as a .c file that results in duplicate

> > compilation.

> >

> > The new command-line syntax is:

> >

> >   $ qemu-storage-daemon \

> >       --blockdev file,node-name=drive0,filename=test.img \

> >       --export vhost-user-blk,node-name=drive0,id=export0,unix-socket=/tmp/vhost-user-blk.sock

> >

> > Note that unix-socket is optional because we may wish to accept chardevs

> > too in the future.

> >

> > Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>

> > ---

> > v2:

> >  * Replace str unix-socket with SocketAddress addr to match NBD and

> >    support file descriptor passing

> >  * Make addr mandatory [Markus]

> >  * Update vhost-user-blk-test.c to use --export syntax

> > ---

> >  qapi/block-export.json               |  21 +-

> >  block/export/vhost-user-blk-server.h |  23 +-

> >  block/export/export.c                |   8 +-

> >  block/export/vhost-user-blk-server.c | 452 +++++++--------------------

> >  tests/qtest/vhost-user-blk-test.c    |   2 +-

> >  util/vhost-user-server.c             |  10 +-

> >  block/export/meson.build             |   1 +

> >  block/meson.build                    |   1 -

> >  8 files changed, 158 insertions(+), 360 deletions(-)

> >

> > diff --git a/qapi/block-export.json b/qapi/block-export.json

> > index ace0d66e17..2e44625bb1 100644

> > --- a/qapi/block-export.json

> > +++ b/qapi/block-export.json

> > @@ -84,6 +84,21 @@

> >    'data': { '*name': 'str', '*description': 'str',

> >              '*bitmap': 'str' } }

> >  

> > +##

> > +# @BlockExportOptionsVhostUserBlk:

> > +#

> > +# A vhost-user-blk block export.

> > +#

> > +# @addr: The vhost-user socket on which to listen. Both 'unix' and 'fd'

> > +#        SocketAddress types are supported. Passed fds must be UNIX domain

> > +#        sockets.

> 

> "addr.type must be 'unix' or 'fd'" is not visible in introspection.

> Awkward.  Practical problem only if other addresses ever become

> available here.  Is that possible?


addr.type=fd itself has the same problem, because it is a file
descriptor without type information. Therefore the QMP client cannot
introspect which types of file descriptors can be passed.

Two ideas:

1. Introduce per-address family fd types (SocketAddrFdTcpInet,
   SocketAddrFdTcpInet6, SocketAddrFdUnix, etc) to express specific
   address families in the QAPI schema.

   Then use per-command unions to express the address families supported
   by specific commands. For example,
   BlockExportOptionsVhostUserBlkSocketAddr would only allow
   SocketAddrUnix and SocketAddrFdUnix. That way new address families
   can be supported in the future and introspection reports.

2. Use a side-channel (query-features, I think we discussed something
   like this a while back) to report features that cannot be
   introspected.

I think the added complexity for achieving full introspection is not
worth it. It becomes harder to define new QAPI commands, increases the
chance of errors, and is more tedious to program for clients/servers.

Accepting any SocketAddr seems reasonable to me since vhost-user
requires an address family that has file descriptor passing. Very few
address families support this feature and we don't expect to add new
ones often.

Stefan
Markus Armbruster Sept. 30, 2020, 2:33 p.m. UTC | #3
Stefan Hajnoczi <stefanha@redhat.com> writes:

> On Wed, Sep 30, 2020 at 07:28:58AM +0200, Markus Armbruster wrote:

>> Stefan Hajnoczi <stefanha@redhat.com> writes:

>> 

>> > Use the new QAPI block exports API instead of defining our own QOM

>> > objects.

>> >

>> > This is a large change because the lifecycle of VuBlockDev needs to

>> > follow BlockExportDriver. QOM properties are replaced by QAPI options

>> > objects.

>> >

>> > VuBlockDev is renamed VuBlkExport and contains a BlockExport field.

>> > Several fields can be dropped since BlockExport already has equivalents.

>> >

>> > The file names and meson build integration will be adjusted in a future

>> > patch. libvhost-user should probably be built as a static library that

>> > is linked into QEMU instead of as a .c file that results in duplicate

>> > compilation.

>> >

>> > The new command-line syntax is:

>> >

>> >   $ qemu-storage-daemon \

>> >       --blockdev file,node-name=drive0,filename=test.img \

>> >       --export vhost-user-blk,node-name=drive0,id=export0,unix-socket=/tmp/vhost-user-blk.sock

>> >

>> > Note that unix-socket is optional because we may wish to accept chardevs

>> > too in the future.

>> >

>> > Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>

>> > ---

>> > v2:

>> >  * Replace str unix-socket with SocketAddress addr to match NBD and

>> >    support file descriptor passing

>> >  * Make addr mandatory [Markus]

>> >  * Update vhost-user-blk-test.c to use --export syntax

>> > ---

>> >  qapi/block-export.json               |  21 +-

>> >  block/export/vhost-user-blk-server.h |  23 +-

>> >  block/export/export.c                |   8 +-

>> >  block/export/vhost-user-blk-server.c | 452 +++++++--------------------

>> >  tests/qtest/vhost-user-blk-test.c    |   2 +-

>> >  util/vhost-user-server.c             |  10 +-

>> >  block/export/meson.build             |   1 +

>> >  block/meson.build                    |   1 -

>> >  8 files changed, 158 insertions(+), 360 deletions(-)

>> >

>> > diff --git a/qapi/block-export.json b/qapi/block-export.json

>> > index ace0d66e17..2e44625bb1 100644

>> > --- a/qapi/block-export.json

>> > +++ b/qapi/block-export.json

>> > @@ -84,6 +84,21 @@

>> >    'data': { '*name': 'str', '*description': 'str',

>> >              '*bitmap': 'str' } }

>> >  

>> > +##

>> > +# @BlockExportOptionsVhostUserBlk:

>> > +#

>> > +# A vhost-user-blk block export.

>> > +#

>> > +# @addr: The vhost-user socket on which to listen. Both 'unix' and 'fd'

>> > +#        SocketAddress types are supported. Passed fds must be UNIX domain

>> > +#        sockets.

>> 

>> "addr.type must be 'unix' or 'fd'" is not visible in introspection.

>> Awkward.  Practical problem only if other addresses ever become

>> available here.  Is that possible?

>

> addr.type=fd itself has the same problem, because it is a file

> descriptor without type information. Therefore the QMP client cannot

> introspect which types of file descriptors can be passed.


Yes, but if introspection could tell us which which values of addr.type
are valid, then a client should figure out the address families, as
follows.  Any valid value other than 'fd' corresponds to an address
family.  The set of values valid for addr.type therefore corresponds to
a set of address families.  The address families in that set are all
valid with 'fd', aren't they?

> Two ideas:

>

> 1. Introduce per-address family fd types (SocketAddrFdTcpInet,

>    SocketAddrFdTcpInet6, SocketAddrFdUnix, etc) to express specific

>    address families in the QAPI schema.

>

>    Then use per-command unions to express the address families supported

>    by specific commands. For example,

>    BlockExportOptionsVhostUserBlkSocketAddr would only allow

>    SocketAddrUnix and SocketAddrFdUnix. That way new address families

>    can be supported in the future and introspection reports.


Awkward.  These types would have to differ structurally, or else they
are indistinguishable in introspection.

> 2. Use a side-channel (query-features, I think we discussed something

>    like this a while back) to report features that cannot be

>    introspected.


We implemented this in the form of QAPI feature flags, visible in
introspection.  You could do something like

  'addr': { 'type': 'SocketAddress',
            'features': [ 'unix' ] }

> I think the added complexity for achieving full introspection is not

> worth it. It becomes harder to define new QAPI commands, increases the

> chance of errors, and is more tedious to program for clients/servers.


Hence my question: is it possible that address families other than unix
become available here?

When that happens, we have an introspection problem of the sort we
common solve with a feature flag.

> Accepting any SocketAddr seems reasonable to me since vhost-user

> requires an address family that has file descriptor passing. Very few

> address families support this feature and we don't expect to add new

> ones often.


Your answer appears to be "yes in theory, quite unlikely in practice".
Correct?
Stefan Hajnoczi Oct. 1, 2020, 3:25 p.m. UTC | #4
On Wed, Sep 30, 2020 at 04:33:18PM +0200, Markus Armbruster wrote:
> Stefan Hajnoczi <stefanha@redhat.com> writes:

> 

> > On Wed, Sep 30, 2020 at 07:28:58AM +0200, Markus Armbruster wrote:

> >> Stefan Hajnoczi <stefanha@redhat.com> writes:

> >> 

> >> > Use the new QAPI block exports API instead of defining our own QOM

> >> > objects.

> >> >

> >> > This is a large change because the lifecycle of VuBlockDev needs to

> >> > follow BlockExportDriver. QOM properties are replaced by QAPI options

> >> > objects.

> >> >

> >> > VuBlockDev is renamed VuBlkExport and contains a BlockExport field.

> >> > Several fields can be dropped since BlockExport already has equivalents.

> >> >

> >> > The file names and meson build integration will be adjusted in a future

> >> > patch. libvhost-user should probably be built as a static library that

> >> > is linked into QEMU instead of as a .c file that results in duplicate

> >> > compilation.

> >> >

> >> > The new command-line syntax is:

> >> >

> >> >   $ qemu-storage-daemon \

> >> >       --blockdev file,node-name=drive0,filename=test.img \

> >> >       --export vhost-user-blk,node-name=drive0,id=export0,unix-socket=/tmp/vhost-user-blk.sock

> >> >

> >> > Note that unix-socket is optional because we may wish to accept chardevs

> >> > too in the future.

> >> >

> >> > Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>

> >> > ---

> >> > v2:

> >> >  * Replace str unix-socket with SocketAddress addr to match NBD and

> >> >    support file descriptor passing

> >> >  * Make addr mandatory [Markus]

> >> >  * Update vhost-user-blk-test.c to use --export syntax

> >> > ---

> >> >  qapi/block-export.json               |  21 +-

> >> >  block/export/vhost-user-blk-server.h |  23 +-

> >> >  block/export/export.c                |   8 +-

> >> >  block/export/vhost-user-blk-server.c | 452 +++++++--------------------

> >> >  tests/qtest/vhost-user-blk-test.c    |   2 +-

> >> >  util/vhost-user-server.c             |  10 +-

> >> >  block/export/meson.build             |   1 +

> >> >  block/meson.build                    |   1 -

> >> >  8 files changed, 158 insertions(+), 360 deletions(-)

> >> >

> >> > diff --git a/qapi/block-export.json b/qapi/block-export.json

> >> > index ace0d66e17..2e44625bb1 100644

> >> > --- a/qapi/block-export.json

> >> > +++ b/qapi/block-export.json

> >> > @@ -84,6 +84,21 @@

> >> >    'data': { '*name': 'str', '*description': 'str',

> >> >              '*bitmap': 'str' } }

> >> >  

> >> > +##

> >> > +# @BlockExportOptionsVhostUserBlk:

> >> > +#

> >> > +# A vhost-user-blk block export.

> >> > +#

> >> > +# @addr: The vhost-user socket on which to listen. Both 'unix' and 'fd'

> >> > +#        SocketAddress types are supported. Passed fds must be UNIX domain

> >> > +#        sockets.

> >> 

> >> "addr.type must be 'unix' or 'fd'" is not visible in introspection.

> >> Awkward.  Practical problem only if other addresses ever become

> >> available here.  Is that possible?

> >

> > addr.type=fd itself has the same problem, because it is a file

> > descriptor without type information. Therefore the QMP client cannot

> > introspect which types of file descriptors can be passed.

> 

> Yes, but if introspection could tell us which which values of addr.type

> are valid, then a client should figure out the address families, as

> follows.  Any valid value other than 'fd' corresponds to an address

> family.  The set of values valid for addr.type therefore corresponds to

> a set of address families.  The address families in that set are all

> valid with 'fd', aren't they?


Yes, in this case the address families in addr.type are the ones also
supported by addr.type=fd.

> > Two ideas:

> >

> > 1. Introduce per-address family fd types (SocketAddrFdTcpInet,

> >    SocketAddrFdTcpInet6, SocketAddrFdUnix, etc) to express specific

> >    address families in the QAPI schema.

> >

> >    Then use per-command unions to express the address families supported

> >    by specific commands. For example,

> >    BlockExportOptionsVhostUserBlkSocketAddr would only allow

> >    SocketAddrUnix and SocketAddrFdUnix. That way new address families

> >    can be supported in the future and introspection reports.

> 

> Awkward.  These types would have to differ structurally, or else they

> are indistinguishable in introspection.

>

> > 2. Use a side-channel (query-features, I think we discussed something

> >    like this a while back) to report features that cannot be

> >    introspected.

> 

> We implemented this in the form of QAPI feature flags, visible in

> introspection.  You could do something like

> 

>   'addr': { 'type': 'SocketAddress',

>             'features': [ 'unix' ] }


That is nice.

> 

> > I think the added complexity for achieving full introspection is not

> > worth it. It becomes harder to define new QAPI commands, increases the

> > chance of errors, and is more tedious to program for clients/servers.

> 

> Hence my question: is it possible that address families other than unix

> become available here?

> 

> When that happens, we have an introspection problem of the sort we

> common solve with a feature flag.

> 

> > Accepting any SocketAddr seems reasonable to me since vhost-user

> > requires an address family that has file descriptor passing. Very few

> > address families support this feature and we don't expect to add new

> > ones often.

> 

> Your answer appears to be "yes in theory, quite unlikely in practice".

> Correct?


Yes.

Stefan
Markus Armbruster Oct. 2, 2020, 5:20 a.m. UTC | #5
Stefan Hajnoczi <stefanha@redhat.com> writes:

> On Wed, Sep 30, 2020 at 04:33:18PM +0200, Markus Armbruster wrote:

>> Stefan Hajnoczi <stefanha@redhat.com> writes:

>> 

>> > On Wed, Sep 30, 2020 at 07:28:58AM +0200, Markus Armbruster wrote:

>> >> Stefan Hajnoczi <stefanha@redhat.com> writes:

>> >> 

>> >> > Use the new QAPI block exports API instead of defining our own QOM

>> >> > objects.

>> >> >

>> >> > This is a large change because the lifecycle of VuBlockDev needs to

>> >> > follow BlockExportDriver. QOM properties are replaced by QAPI options

>> >> > objects.

>> >> >

>> >> > VuBlockDev is renamed VuBlkExport and contains a BlockExport field.

>> >> > Several fields can be dropped since BlockExport already has equivalents.

>> >> >

>> >> > The file names and meson build integration will be adjusted in a future

>> >> > patch. libvhost-user should probably be built as a static library that

>> >> > is linked into QEMU instead of as a .c file that results in duplicate

>> >> > compilation.

>> >> >

>> >> > The new command-line syntax is:

>> >> >

>> >> >   $ qemu-storage-daemon \

>> >> >       --blockdev file,node-name=drive0,filename=test.img \

>> >> >       --export vhost-user-blk,node-name=drive0,id=export0,unix-socket=/tmp/vhost-user-blk.sock

>> >> >

>> >> > Note that unix-socket is optional because we may wish to accept chardevs

>> >> > too in the future.

>> >> >

>> >> > Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>

>> >> > ---

>> >> > v2:

>> >> >  * Replace str unix-socket with SocketAddress addr to match NBD and

>> >> >    support file descriptor passing

>> >> >  * Make addr mandatory [Markus]

>> >> >  * Update vhost-user-blk-test.c to use --export syntax

>> >> > ---

>> >> >  qapi/block-export.json               |  21 +-

>> >> >  block/export/vhost-user-blk-server.h |  23 +-

>> >> >  block/export/export.c                |   8 +-

>> >> >  block/export/vhost-user-blk-server.c | 452 +++++++--------------------

>> >> >  tests/qtest/vhost-user-blk-test.c    |   2 +-

>> >> >  util/vhost-user-server.c             |  10 +-

>> >> >  block/export/meson.build             |   1 +

>> >> >  block/meson.build                    |   1 -

>> >> >  8 files changed, 158 insertions(+), 360 deletions(-)

>> >> >

>> >> > diff --git a/qapi/block-export.json b/qapi/block-export.json

>> >> > index ace0d66e17..2e44625bb1 100644

>> >> > --- a/qapi/block-export.json

>> >> > +++ b/qapi/block-export.json

>> >> > @@ -84,6 +84,21 @@

>> >> >    'data': { '*name': 'str', '*description': 'str',

>> >> >              '*bitmap': 'str' } }

>> >> >  

>> >> > +##

>> >> > +# @BlockExportOptionsVhostUserBlk:

>> >> > +#

>> >> > +# A vhost-user-blk block export.

>> >> > +#

>> >> > +# @addr: The vhost-user socket on which to listen. Both 'unix' and 'fd'

>> >> > +#        SocketAddress types are supported. Passed fds must be UNIX domain

>> >> > +#        sockets.

>> >> 

>> >> "addr.type must be 'unix' or 'fd'" is not visible in introspection.

>> >> Awkward.  Practical problem only if other addresses ever become

>> >> available here.  Is that possible?

>> >

>> > addr.type=fd itself has the same problem, because it is a file

>> > descriptor without type information. Therefore the QMP client cannot

>> > introspect which types of file descriptors can be passed.

>> 

>> Yes, but if introspection could tell us which which values of addr.type

>> are valid, then a client should figure out the address families, as

>> follows.  Any valid value other than 'fd' corresponds to an address

>> family.  The set of values valid for addr.type therefore corresponds to

>> a set of address families.  The address families in that set are all

>> valid with 'fd', aren't they?

>

> Yes, in this case the address families in addr.type are the ones also

> supported by addr.type=fd.

>

>> > Two ideas:

>> >

>> > 1. Introduce per-address family fd types (SocketAddrFdTcpInet,

>> >    SocketAddrFdTcpInet6, SocketAddrFdUnix, etc) to express specific

>> >    address families in the QAPI schema.

>> >

>> >    Then use per-command unions to express the address families supported

>> >    by specific commands. For example,

>> >    BlockExportOptionsVhostUserBlkSocketAddr would only allow

>> >    SocketAddrUnix and SocketAddrFdUnix. That way new address families

>> >    can be supported in the future and introspection reports.

>> 

>> Awkward.  These types would have to differ structurally, or else they

>> are indistinguishable in introspection.

>>

>> > 2. Use a side-channel (query-features, I think we discussed something

>> >    like this a while back) to report features that cannot be

>> >    introspected.

>> 

>> We implemented this in the form of QAPI feature flags, visible in

>> introspection.  You could do something like

>> 

>>   'addr': { 'type': 'SocketAddress',

>>             'features': [ 'unix' ] }

>

> That is nice.

>

>> 

>> > I think the added complexity for achieving full introspection is not

>> > worth it. It becomes harder to define new QAPI commands, increases the

>> > chance of errors, and is more tedious to program for clients/servers.

>> 

>> Hence my question: is it possible that address families other than unix

>> become available here?

>> 

>> When that happens, we have an introspection problem of the sort we

>> common solve with a feature flag.

>> 

>> > Accepting any SocketAddr seems reasonable to me since vhost-user

>> > requires an address family that has file descriptor passing. Very few

>> > address families support this feature and we don't expect to add new

>> > ones often.

>> 

>> Your answer appears to be "yes in theory, quite unlikely in practice".

>> Correct?


Keeping introspection "tight" would be nice, but since a real need for
"tight" here seems quite unlikely, it doesn't seem to be worth the
trouble.

Perhaps this argument could be worked into the commit message.  Up to
you.

Acked-by: Markus Armbruster <armbru@redhat.com>
Stefan Hajnoczi Oct. 8, 2020, 4:02 p.m. UTC | #6
On Fri, Oct 02, 2020 at 07:20:48AM +0200, Markus Armbruster wrote:
> Stefan Hajnoczi <stefanha@redhat.com> writes:

> > On Wed, Sep 30, 2020 at 04:33:18PM +0200, Markus Armbruster wrote:

> >> Stefan Hajnoczi <stefanha@redhat.com> writes:

> >> > On Wed, Sep 30, 2020 at 07:28:58AM +0200, Markus Armbruster wrote:

> >> >> Stefan Hajnoczi <stefanha@redhat.com> writes:

> >> Hence my question: is it possible that address families other than unix

> >> become available here?

> >> 

> >> When that happens, we have an introspection problem of the sort we

> >> common solve with a feature flag.

> >> 

> >> > Accepting any SocketAddr seems reasonable to me since vhost-user

> >> > requires an address family that has file descriptor passing. Very few

> >> > address families support this feature and we don't expect to add new

> >> > ones often.

> >> 

> >> Your answer appears to be "yes in theory, quite unlikely in practice".

> >> Correct?

> 

> Keeping introspection "tight" would be nice, but since a real need for

> "tight" here seems quite unlikely, it doesn't seem to be worth the

> trouble.

> 

> Perhaps this argument could be worked into the commit message.  Up to

> you.

> 

> Acked-by: Markus Armbruster <armbru@redhat.com>


Thanks, I will update the commit message when merging.
diff mbox series

Patch

diff --git a/qapi/block-export.json b/qapi/block-export.json
index ace0d66e17..2e44625bb1 100644
--- a/qapi/block-export.json
+++ b/qapi/block-export.json
@@ -84,6 +84,21 @@ 
   'data': { '*name': 'str', '*description': 'str',
             '*bitmap': 'str' } }
 
+##
+# @BlockExportOptionsVhostUserBlk:
+#
+# A vhost-user-blk block export.
+#
+# @addr: The vhost-user socket on which to listen. Both 'unix' and 'fd'
+#        SocketAddress types are supported. Passed fds must be UNIX domain
+#        sockets.
+# @logical-block-size: Logical block size in bytes. Defaults to 512 bytes.
+#
+# Since: 5.2
+##
+{ 'struct': 'BlockExportOptionsVhostUserBlk',
+  'data': { 'addr': 'SocketAddress', '*logical-block-size': 'size' } }
+
 ##
 # @NbdServerAddOptions:
 #
@@ -180,11 +195,12 @@ 
 # An enumeration of block export types
 #
 # @nbd: NBD export
+# @vhost-user-blk: vhost-user-blk export (since 5.2)
 #
 # Since: 4.2
 ##
 { 'enum': 'BlockExportType',
-  'data': [ 'nbd' ] }
+  'data': [ 'nbd', 'vhost-user-blk' ] }
 
 ##
 # @BlockExportOptions:
@@ -213,7 +229,8 @@ 
             '*writethrough': 'bool' },
   'discriminator': 'type',
   'data': {
-      'nbd': 'BlockExportOptionsNbd'
+      'nbd': 'BlockExportOptionsNbd',
+      'vhost-user-blk': 'BlockExportOptionsVhostUserBlk'
    } }
 
 ##
diff --git a/block/export/vhost-user-blk-server.h b/block/export/vhost-user-blk-server.h
index f06f37c4c8..fcf46fc8a5 100644
--- a/block/export/vhost-user-blk-server.h
+++ b/block/export/vhost-user-blk-server.h
@@ -10,27 +10,10 @@ 
 
 #ifndef VHOST_USER_BLK_SERVER_H
 #define VHOST_USER_BLK_SERVER_H
-#include "util/vhost-user-server.h"
 
-typedef struct VuBlockDev VuBlockDev;
-#define TYPE_VHOST_USER_BLK_SERVER "vhost-user-blk-server"
-#define VHOST_USER_BLK_SERVER(obj) \
-   OBJECT_CHECK(VuBlockDev, obj, TYPE_VHOST_USER_BLK_SERVER)
+#include "block/export.h"
 
-/* vhost user block device */
-struct VuBlockDev {
-    Object parent_obj;
-    char *node_name;
-    SocketAddress *addr;
-    AioContext *ctx;
-    VuServer vu_server;
-    bool running;
-    uint32_t blk_size;
-    BlockBackend *backend;
-    QIOChannelSocket *sioc;
-    QTAILQ_ENTRY(VuBlockDev) next;
-    struct virtio_blk_config blkcfg;
-    bool writable;
-};
+/* For block/export/export.c */
+extern const BlockExportDriver blk_exp_vhost_user_blk;
 
 #endif /* VHOST_USER_BLK_SERVER_H */
diff --git a/block/export/export.c b/block/export/export.c
index 87516d1e05..d0c7590911 100644
--- a/block/export/export.c
+++ b/block/export/export.c
@@ -17,6 +17,9 @@ 
 #include "sysemu/block-backend.h"
 #include "block/export.h"
 #include "block/nbd.h"
+#if CONFIG_LINUX
+#include "block/export/vhost-user-blk-server.h"
+#endif
 #include "qapi/error.h"
 #include "qapi/qapi-commands-block-export.h"
 #include "qapi/qapi-events-block-export.h"
@@ -24,6 +27,9 @@ 
 
 static const BlockExportDriver *blk_exp_drivers[] = {
     &blk_exp_nbd,
+#if CONFIG_LINUX
+    &blk_exp_vhost_user_blk,
+#endif
 };
 
 /* Only accessed from the main thread */
@@ -123,7 +129,7 @@  BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
     assert(drv->instance_size >= sizeof(BlockExport));
     exp = g_malloc0(drv->instance_size);
     *exp = (BlockExport) {
-        .drv        = &blk_exp_nbd,
+        .drv        = drv,
         .refcount   = 1,
         .user_owned = true,
         .id         = g_strdup(export->id),
diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c
index 44d3c45fa2..91fc7040b2 100644
--- a/block/export/vhost-user-blk-server.c
+++ b/block/export/vhost-user-blk-server.c
@@ -11,6 +11,9 @@ 
  */
 #include "qemu/osdep.h"
 #include "block/block.h"
+#include "contrib/libvhost-user/libvhost-user.h"
+#include "standard-headers/linux/virtio_blk.h"
+#include "util/vhost-user-server.h"
 #include "vhost-user-blk-server.h"
 #include "qapi/error.h"
 #include "qom/object_interfaces.h"
@@ -24,7 +27,7 @@  struct virtio_blk_inhdr {
     unsigned char status;
 };
 
-typedef struct VuBlockReq {
+typedef struct VuBlkReq {
     VuVirtqElement elem;
     int64_t sector_num;
     size_t size;
@@ -32,9 +35,19 @@  typedef struct VuBlockReq {
     struct virtio_blk_outhdr out;
     VuServer *server;
     struct VuVirtq *vq;
-} VuBlockReq;
+} VuBlkReq;
 
-static void vu_block_req_complete(VuBlockReq *req)
+/* vhost user block device */
+typedef struct {
+    BlockExport export;
+    VuServer vu_server;
+    uint32_t blk_size;
+    QIOChannelSocket *sioc;
+    struct virtio_blk_config blkcfg;
+    bool writable;
+} VuBlkExport;
+
+static void vu_blk_req_complete(VuBlkReq *req)
 {
     VuDev *vu_dev = &req->server->vu_dev;
 
@@ -45,14 +58,9 @@  static void vu_block_req_complete(VuBlockReq *req)
     free(req);
 }
 
-static VuBlockDev *get_vu_block_device_by_server(VuServer *server)
-{
-    return container_of(server, VuBlockDev, vu_server);
-}
-
 static int coroutine_fn
-vu_block_discard_write_zeroes(VuBlockReq *req, struct iovec *iov,
-                              uint32_t iovcnt, uint32_t type)
+vu_blk_discard_write_zeroes(BlockBackend *blk, struct iovec *iov,
+                            uint32_t iovcnt, uint32_t type)
 {
     struct virtio_blk_discard_write_zeroes desc;
     ssize_t size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc));
@@ -61,16 +69,14 @@  vu_block_discard_write_zeroes(VuBlockReq *req, struct iovec *iov,
         return -EINVAL;
     }
 
-    VuBlockDev *vdev_blk = get_vu_block_device_by_server(req->server);
     uint64_t range[2] = { le64_to_cpu(desc.sector) << 9,
                           le32_to_cpu(desc.num_sectors) << 9 };
     if (type == VIRTIO_BLK_T_DISCARD) {
-        if (blk_co_pdiscard(vdev_blk->backend, range[0], range[1]) == 0) {
+        if (blk_co_pdiscard(blk, range[0], range[1]) == 0) {
             return 0;
         }
     } else if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
-        if (blk_co_pwrite_zeroes(vdev_blk->backend,
-                                 range[0], range[1], 0) == 0) {
+        if (blk_co_pwrite_zeroes(blk, range[0], range[1], 0) == 0) {
             return 0;
         }
     }
@@ -78,22 +84,15 @@  vu_block_discard_write_zeroes(VuBlockReq *req, struct iovec *iov,
     return -EINVAL;
 }
 
-static int coroutine_fn vu_block_flush(VuBlockReq *req)
+static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
 {
-    VuBlockDev *vdev_blk = get_vu_block_device_by_server(req->server);
-    BlockBackend *backend = vdev_blk->backend;
-    return blk_co_flush(backend);
-}
-
-static void coroutine_fn vu_block_virtio_process_req(void *opaque)
-{
-    VuBlockReq *req = opaque;
+    VuBlkReq *req = opaque;
     VuServer *server = req->server;
     VuVirtqElement *elem = &req->elem;
     uint32_t type;
 
-    VuBlockDev *vdev_blk = get_vu_block_device_by_server(server);
-    BlockBackend *backend = vdev_blk->backend;
+    VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server);
+    BlockBackend *blk = vexp->export.blk;
 
     struct iovec *in_iov = elem->in_sg;
     struct iovec *out_iov = elem->out_sg;
@@ -133,16 +132,19 @@  static void coroutine_fn vu_block_virtio_process_req(void *opaque)
         bool is_write = type & VIRTIO_BLK_T_OUT;
         req->sector_num = le64_to_cpu(req->out.sector);
 
-        int64_t offset = req->sector_num * vdev_blk->blk_size;
+        if (is_write && !vexp->writable) {
+            req->in->status = VIRTIO_BLK_S_IOERR;
+            break;
+        }
+
+        int64_t offset = req->sector_num * vexp->blk_size;
         QEMUIOVector qiov;
         if (is_write) {
             qemu_iovec_init_external(&qiov, out_iov, out_num);
-            ret = blk_co_pwritev(backend, offset, qiov.size,
-                                 &qiov, 0);
+            ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0);
         } else {
             qemu_iovec_init_external(&qiov, in_iov, in_num);
-            ret = blk_co_preadv(backend, offset, qiov.size,
-                                &qiov, 0);
+            ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0);
         }
         if (ret >= 0) {
             req->in->status = VIRTIO_BLK_S_OK;
@@ -152,7 +154,7 @@  static void coroutine_fn vu_block_virtio_process_req(void *opaque)
         break;
     }
     case VIRTIO_BLK_T_FLUSH:
-        if (vu_block_flush(req) == 0) {
+        if (blk_co_flush(blk) == 0) {
             req->in->status = VIRTIO_BLK_S_OK;
         } else {
             req->in->status = VIRTIO_BLK_S_IOERR;
@@ -169,8 +171,13 @@  static void coroutine_fn vu_block_virtio_process_req(void *opaque)
     case VIRTIO_BLK_T_DISCARD:
     case VIRTIO_BLK_T_WRITE_ZEROES: {
         int rc;
-        rc = vu_block_discard_write_zeroes(req, &elem->out_sg[1],
-                                           out_num, type);
+
+        if (!vexp->writable) {
+            req->in->status = VIRTIO_BLK_S_IOERR;
+            break;
+        }
+
+        rc = vu_blk_discard_write_zeroes(blk, &elem->out_sg[1], out_num, type);
         if (rc == 0) {
             req->in->status = VIRTIO_BLK_S_OK;
         } else {
@@ -183,22 +190,22 @@  static void coroutine_fn vu_block_virtio_process_req(void *opaque)
         break;
     }
 
-    vu_block_req_complete(req);
+    vu_blk_req_complete(req);
     return;
 
 err:
-    free(elem);
+    free(req);
 }
 
-static void vu_block_process_vq(VuDev *vu_dev, int idx)
+static void vu_blk_process_vq(VuDev *vu_dev, int idx)
 {
     VuServer *server = container_of(vu_dev, VuServer, vu_dev);
     VuVirtq *vq = vu_get_queue(vu_dev, idx);
 
     while (1) {
-        VuBlockReq *req;
+        VuBlkReq *req;
 
-        req = vu_queue_pop(vu_dev, vq, sizeof(VuBlockReq));
+        req = vu_queue_pop(vu_dev, vq, sizeof(VuBlkReq));
         if (!req) {
             break;
         }
@@ -207,26 +214,26 @@  static void vu_block_process_vq(VuDev *vu_dev, int idx)
         req->vq = vq;
 
         Coroutine *co =
-            qemu_coroutine_create(vu_block_virtio_process_req, req);
+            qemu_coroutine_create(vu_blk_virtio_process_req, req);
         qemu_coroutine_enter(co);
     }
 }
 
-static void vu_block_queue_set_started(VuDev *vu_dev, int idx, bool started)
+static void vu_blk_queue_set_started(VuDev *vu_dev, int idx, bool started)
 {
     VuVirtq *vq;
 
     assert(vu_dev);
 
     vq = vu_get_queue(vu_dev, idx);
-    vu_set_queue_handler(vu_dev, vq, started ? vu_block_process_vq : NULL);
+    vu_set_queue_handler(vu_dev, vq, started ? vu_blk_process_vq : NULL);
 }
 
-static uint64_t vu_block_get_features(VuDev *dev)
+static uint64_t vu_blk_get_features(VuDev *dev)
 {
     uint64_t features;
     VuServer *server = container_of(dev, VuServer, vu_dev);
-    VuBlockDev *vdev_blk = get_vu_block_device_by_server(server);
+    VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server);
     features = 1ull << VIRTIO_BLK_F_SIZE_MAX |
                1ull << VIRTIO_BLK_F_SEG_MAX |
                1ull << VIRTIO_BLK_F_TOPOLOGY |
@@ -240,35 +247,35 @@  static uint64_t vu_block_get_features(VuDev *dev)
                1ull << VIRTIO_RING_F_EVENT_IDX |
                1ull << VHOST_USER_F_PROTOCOL_FEATURES;
 
-    if (!vdev_blk->writable) {
+    if (!vexp->writable) {
         features |= 1ull << VIRTIO_BLK_F_RO;
     }
 
     return features;
 }
 
-static uint64_t vu_block_get_protocol_features(VuDev *dev)
+static uint64_t vu_blk_get_protocol_features(VuDev *dev)
 {
     return 1ull << VHOST_USER_PROTOCOL_F_CONFIG |
            1ull << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD;
 }
 
 static int
-vu_block_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len)
+vu_blk_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len)
 {
+    /* TODO blkcfg must be little-endian for VIRTIO 1.0 */
     VuServer *server = container_of(vu_dev, VuServer, vu_dev);
-    VuBlockDev *vdev_blk = get_vu_block_device_by_server(server);
-    memcpy(config, &vdev_blk->blkcfg, len);
-
+    VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server);
+    memcpy(config, &vexp->blkcfg, len);
     return 0;
 }
 
 static int
-vu_block_set_config(VuDev *vu_dev, const uint8_t *data,
+vu_blk_set_config(VuDev *vu_dev, const uint8_t *data,
                     uint32_t offset, uint32_t size, uint32_t flags)
 {
     VuServer *server = container_of(vu_dev, VuServer, vu_dev);
-    VuBlockDev *vdev_blk = get_vu_block_device_by_server(server);
+    VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server);
     uint8_t wce;
 
     /* don't support live migration */
@@ -282,8 +289,8 @@  vu_block_set_config(VuDev *vu_dev, const uint8_t *data,
     }
 
     wce = *data;
-    vdev_blk->blkcfg.wce = wce;
-    blk_set_enable_write_cache(vdev_blk->backend, wce);
+    vexp->blkcfg.wce = wce;
+    blk_set_enable_write_cache(vexp->export.blk, wce);
     return 0;
 }
 
@@ -295,7 +302,7 @@  vu_block_set_config(VuDev *vu_dev, const uint8_t *data,
  * of vu_process_message.
  *
  */
-static int vu_block_process_msg(VuDev *dev, VhostUserMsg *vmsg, int *do_reply)
+static int vu_blk_process_msg(VuDev *dev, VhostUserMsg *vmsg, int *do_reply)
 {
     if (vmsg->request == VHOST_USER_NONE) {
         dev->panic(dev, "disconnect");
@@ -304,29 +311,29 @@  static int vu_block_process_msg(VuDev *dev, VhostUserMsg *vmsg, int *do_reply)
     return false;
 }
 
-static const VuDevIface vu_block_iface = {
-    .get_features          = vu_block_get_features,
-    .queue_set_started     = vu_block_queue_set_started,
-    .get_protocol_features = vu_block_get_protocol_features,
-    .get_config            = vu_block_get_config,
-    .set_config            = vu_block_set_config,
-    .process_msg           = vu_block_process_msg,
+static const VuDevIface vu_blk_iface = {
+    .get_features          = vu_blk_get_features,
+    .queue_set_started     = vu_blk_queue_set_started,
+    .get_protocol_features = vu_blk_get_protocol_features,
+    .get_config            = vu_blk_get_config,
+    .set_config            = vu_blk_set_config,
+    .process_msg           = vu_blk_process_msg,
 };
 
 static void blk_aio_attached(AioContext *ctx, void *opaque)
 {
-    VuBlockDev *vub_dev = opaque;
-    vhost_user_server_attach_aio_context(&vub_dev->vu_server, ctx);
+    VuBlkExport *vexp = opaque;
+    vhost_user_server_attach_aio_context(&vexp->vu_server, ctx);
 }
 
 static void blk_aio_detach(void *opaque)
 {
-    VuBlockDev *vub_dev = opaque;
-    vhost_user_server_detach_aio_context(&vub_dev->vu_server);
+    VuBlkExport *vexp = opaque;
+    vhost_user_server_detach_aio_context(&vexp->vu_server);
 }
 
 static void
-vu_block_initialize_config(BlockDriverState *bs,
+vu_blk_initialize_config(BlockDriverState *bs,
                            struct virtio_blk_config *config, uint32_t blk_size)
 {
     config->capacity = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
@@ -343,290 +350,67 @@  vu_block_initialize_config(BlockDriverState *bs,
     config->max_write_zeroes_seg = 1;
 }
 
-static VuBlockDev *vu_block_init(VuBlockDev *vu_block_device, Error **errp)
+static void vu_blk_exp_request_shutdown(BlockExport *exp)
 {
+    VuBlkExport *vexp = container_of(exp, VuBlkExport, export);
 
-    BlockBackend *blk;
-    Error *local_error = NULL;
-    const char *node_name = vu_block_device->node_name;
-    bool writable = vu_block_device->writable;
-    uint64_t perm = BLK_PERM_CONSISTENT_READ;
-    int ret;
-
-    AioContext *ctx;
-
-    BlockDriverState *bs = bdrv_lookup_bs(node_name, node_name, &local_error);
-
-    if (!bs) {
-        error_propagate(errp, local_error);
-        return NULL;
-    }
-
-    if (bdrv_is_read_only(bs)) {
-        writable = false;
-    }
-
-    if (writable) {
-        perm |= BLK_PERM_WRITE;
-    }
-
-    ctx = bdrv_get_aio_context(bs);
-    aio_context_acquire(ctx);
-    bdrv_invalidate_cache(bs, NULL);
-    aio_context_release(ctx);
-
-    /*
-     * Don't allow resize while the vhost user server is running,
-     * otherwise we don't care what happens with the node.
-     */
-    blk = blk_new(bdrv_get_aio_context(bs), perm,
-                  BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
-                  BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD);
-    ret = blk_insert_bs(blk, bs, errp);
-
-    if (ret < 0) {
-        goto fail;
-    }
-
-    blk_set_enable_write_cache(blk, false);
-
-    blk_set_allow_aio_context_change(blk, true);
-
-    vu_block_device->blkcfg.wce = 0;
-    vu_block_device->backend = blk;
-    if (!vu_block_device->blk_size) {
-        vu_block_device->blk_size = BDRV_SECTOR_SIZE;
-    }
-    vu_block_device->blkcfg.blk_size = vu_block_device->blk_size;
-    blk_set_guest_block_size(blk, vu_block_device->blk_size);
-    vu_block_initialize_config(bs, &vu_block_device->blkcfg,
-                                   vu_block_device->blk_size);
-    return vu_block_device;
-
-fail:
-    blk_unref(blk);
-    return NULL;
-}
-
-static void vu_block_deinit(VuBlockDev *vu_block_device)
-{
-    if (vu_block_device->backend) {
-        blk_remove_aio_context_notifier(vu_block_device->backend, blk_aio_attached,
-                                        blk_aio_detach, vu_block_device);
-    }
-
-    blk_unref(vu_block_device->backend);
-}
-
-static void vhost_user_blk_server_stop(VuBlockDev *vu_block_device)
-{
-    vhost_user_server_stop(&vu_block_device->vu_server);
-    vu_block_deinit(vu_block_device);
-}
-
-static void vhost_user_blk_server_start(VuBlockDev *vu_block_device,
-                                        Error **errp)
-{
-    AioContext *ctx;
-    SocketAddress *addr = vu_block_device->addr;
-
-    if (!vu_block_init(vu_block_device, errp)) {
-        return;
-    }
-
-    ctx = bdrv_get_aio_context(blk_bs(vu_block_device->backend));
-
-    if (!vhost_user_server_start(&vu_block_device->vu_server, addr, ctx,
-                                 VHOST_USER_BLK_MAX_QUEUES, &vu_block_iface,
-                                 errp)) {
-        goto error;
-    }
-
-    blk_add_aio_context_notifier(vu_block_device->backend, blk_aio_attached,
-                                 blk_aio_detach, vu_block_device);
-    vu_block_device->running = true;
-    return;
-
- error:
-    vu_block_deinit(vu_block_device);
-}
-
-static bool vu_prop_modifiable(VuBlockDev *vus, Error **errp)
-{
-    if (vus->running) {
-            error_setg(errp, "The property can't be modified "
-                       "while the server is running");
-            return false;
-    }
-    return true;
-}
-
-static void vu_set_node_name(Object *obj, const char *value, Error **errp)
-{
-    VuBlockDev *vus = VHOST_USER_BLK_SERVER(obj);
-
-    if (!vu_prop_modifiable(vus, errp)) {
-        return;
-    }
-
-    if (vus->node_name) {
-        g_free(vus->node_name);
-    }
-
-    vus->node_name = g_strdup(value);
-}
-
-static char *vu_get_node_name(Object *obj, Error **errp)
-{
-    VuBlockDev *vus = VHOST_USER_BLK_SERVER(obj);
-    return g_strdup(vus->node_name);
-}
-
-static void free_socket_addr(SocketAddress *addr)
-{
-        g_free(addr->u.q_unix.path);
-        g_free(addr);
-}
-
-static void vu_set_unix_socket(Object *obj, const char *value,
-                               Error **errp)
-{
-    VuBlockDev *vus = VHOST_USER_BLK_SERVER(obj);
-
-    if (!vu_prop_modifiable(vus, errp)) {
-        return;
-    }
-
-    if (vus->addr) {
-        free_socket_addr(vus->addr);
-    }
-
-    SocketAddress *addr = g_new0(SocketAddress, 1);
-    addr->type = SOCKET_ADDRESS_TYPE_UNIX;
-    addr->u.q_unix.path = g_strdup(value);
-    vus->addr = addr;
+    vhost_user_server_stop(&vexp->vu_server);
 }
 
-static char *vu_get_unix_socket(Object *obj, Error **errp)
+static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
+                             Error **errp)
 {
-    VuBlockDev *vus = VHOST_USER_BLK_SERVER(obj);
-    return g_strdup(vus->addr->u.q_unix.path);
-}
-
-static bool vu_get_block_writable(Object *obj, Error **errp)
-{
-    VuBlockDev *vus = VHOST_USER_BLK_SERVER(obj);
-    return vus->writable;
-}
-
-static void vu_set_block_writable(Object *obj, bool value, Error **errp)
-{
-    VuBlockDev *vus = VHOST_USER_BLK_SERVER(obj);
-
-    if (!vu_prop_modifiable(vus, errp)) {
-            return;
-    }
-
-    vus->writable = value;
-}
-
-static void vu_get_blk_size(Object *obj, Visitor *v, const char *name,
-                            void *opaque, Error **errp)
-{
-    VuBlockDev *vus = VHOST_USER_BLK_SERVER(obj);
-    uint32_t value = vus->blk_size;
-
-    visit_type_uint32(v, name, &value, errp);
-}
-
-static void vu_set_blk_size(Object *obj, Visitor *v, const char *name,
-                            void *opaque, Error **errp)
-{
-    VuBlockDev *vus = VHOST_USER_BLK_SERVER(obj);
-
+    VuBlkExport *vexp = container_of(exp, VuBlkExport, export);
+    BlockExportOptionsVhostUserBlk *vu_opts = &opts->u.vhost_user_blk;
     Error *local_err = NULL;
-    uint32_t value;
+    uint64_t logical_block_size;
 
-    if (!vu_prop_modifiable(vus, errp)) {
-            return;
-    }
+    vexp->writable = opts->writable;
+    vexp->blkcfg.wce = 0;
 
-    visit_type_uint32(v, name, &value, &local_err);
-    if (local_err) {
-        goto out;
+    if (vu_opts->has_logical_block_size) {
+        logical_block_size = vu_opts->logical_block_size;
+    } else {
+        logical_block_size = BDRV_SECTOR_SIZE;
     }
-
-    check_block_size(object_get_typename(obj), name, value, &local_err);
+    check_block_size(exp->id, "logical-block-size", logical_block_size,
+                     &local_err);
     if (local_err) {
-        goto out;
+        error_propagate(errp, local_err);
+        return -EINVAL;
+    }
+    vexp->blk_size = logical_block_size;
+    blk_set_guest_block_size(exp->blk, logical_block_size);
+    vu_blk_initialize_config(blk_bs(exp->blk), &vexp->blkcfg,
+                               logical_block_size);
+
+    blk_set_allow_aio_context_change(exp->blk, true);
+    blk_add_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach,
+                                 vexp);
+
+    if (!vhost_user_server_start(&vexp->vu_server, vu_opts->addr, exp->ctx,
+                                 VHOST_USER_BLK_MAX_QUEUES, &vu_blk_iface,
+                                 errp)) {
+        blk_remove_aio_context_notifier(exp->blk, blk_aio_attached,
+                                        blk_aio_detach, vexp);
+        return -EADDRNOTAVAIL;
     }
 
-    vus->blk_size = value;
-
-out:
-    error_propagate(errp, local_err);
-}
-
-static void vhost_user_blk_server_instance_finalize(Object *obj)
-{
-    VuBlockDev *vub = VHOST_USER_BLK_SERVER(obj);
-
-    vhost_user_blk_server_stop(vub);
-
-    /*
-     * Unlike object_property_add_str, object_class_property_add_str
-     * doesn't have a release method. Thus manual memory freeing is
-     * needed.
-     */
-    free_socket_addr(vub->addr);
-    g_free(vub->node_name);
-}
-
-static void vhost_user_blk_server_complete(UserCreatable *obj, Error **errp)
-{
-    VuBlockDev *vub = VHOST_USER_BLK_SERVER(obj);
-
-    vhost_user_blk_server_start(vub, errp);
+    return 0;
 }
 
-static void vhost_user_blk_server_class_init(ObjectClass *klass,
-                                             void *class_data)
+static void vu_blk_exp_delete(BlockExport *exp)
 {
-    UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
-    ucc->complete = vhost_user_blk_server_complete;
-
-    object_class_property_add_bool(klass, "writable",
-                                   vu_get_block_writable,
-                                   vu_set_block_writable);
-
-    object_class_property_add_str(klass, "node-name",
-                                  vu_get_node_name,
-                                  vu_set_node_name);
-
-    object_class_property_add_str(klass, "unix-socket",
-                                  vu_get_unix_socket,
-                                  vu_set_unix_socket);
+    VuBlkExport *vexp = container_of(exp, VuBlkExport, export);
 
-    object_class_property_add(klass, "logical-block-size", "uint32",
-                              vu_get_blk_size, vu_set_blk_size,
-                              NULL, NULL);
+    blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach,
+                                    vexp);
 }
 
-static const TypeInfo vhost_user_blk_server_info = {
-    .name = TYPE_VHOST_USER_BLK_SERVER,
-    .parent = TYPE_OBJECT,
-    .instance_size = sizeof(VuBlockDev),
-    .instance_finalize = vhost_user_blk_server_instance_finalize,
-    .class_init = vhost_user_blk_server_class_init,
-    .interfaces = (InterfaceInfo[]) {
-        {TYPE_USER_CREATABLE},
-        {}
-    },
+const BlockExportDriver blk_exp_vhost_user_blk = {
+    .type               = BLOCK_EXPORT_TYPE_VHOST_USER_BLK,
+    .instance_size      = sizeof(VuBlkExport),
+    .create             = vu_blk_exp_create,
+    .delete             = vu_blk_exp_delete,
+    .request_shutdown   = vu_blk_exp_request_shutdown,
 };
-
-static void vhost_user_blk_server_register_types(void)
-{
-    type_register_static(&vhost_user_blk_server_info);
-}
-
-type_init(vhost_user_blk_server_register_types)
diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c
index d4ccac6b54..42e4cfde82 100644
--- a/tests/qtest/vhost-user-blk-test.c
+++ b/tests/qtest/vhost-user-blk-test.c
@@ -674,7 +674,7 @@  static char *start_vhost_user_blk(GString *cmd_line, int vus_instances)
         img_path = drive_create();
         g_string_append_printf(storage_daemon_command,
             "--blockdev driver=file,node-name=disk%d,filename=%s "
-            "--object vhost-user-blk-server,id=disk%d,unix-socket=%s,"
+            "--export type=vhost-user-blk,id=disk%d,addr.type=unix,addr.path=%s,"
             "node-name=disk%i,writable=on ",
             i, img_path, i, sock_path, i);
 
diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c
index d8b8c08b5f..2a27139eb8 100644
--- a/util/vhost-user-server.c
+++ b/util/vhost-user-server.c
@@ -408,7 +408,15 @@  bool vhost_user_server_start(VuServer *server,
                              Error **errp)
 {
     QEMUBH *bh;
-    QIONetListener *listener = qio_net_listener_new();
+    QIONetListener *listener;
+
+    if (socket_addr->type != SOCKET_ADDRESS_TYPE_UNIX &&
+        socket_addr->type != SOCKET_ADDRESS_TYPE_FD) {
+        error_setg(errp, "Only socket address types 'unix' and 'fd' are supported");
+        return false;
+    }
+
+    listener = qio_net_listener_new();
     if (qio_net_listener_open_sync(listener, socket_addr, 1,
                                    errp) < 0) {
         object_unref(OBJECT(listener));
diff --git a/block/export/meson.build b/block/export/meson.build
index 558ef35d38..ef3a9576f7 100644
--- a/block/export/meson.build
+++ b/block/export/meson.build
@@ -1 +1,2 @@ 
 block_ss.add(files('export.c'))
+block_ss.add(when: 'CONFIG_LINUX', if_true: files('vhost-user-blk-server.c', '../../contrib/libvhost-user/libvhost-user.c'))
diff --git a/block/meson.build b/block/meson.build
index cd52104c84..0b38dc36f7 100644
--- a/block/meson.build
+++ b/block/meson.build
@@ -60,7 +60,6 @@  block_ss.add(when: 'CONFIG_WIN32', if_true: files('file-win32.c', 'win32-aio.c')
 block_ss.add(when: 'CONFIG_POSIX', if_true: [files('file-posix.c'), coref, iokit])
 block_ss.add(when: 'CONFIG_LIBISCSI', if_true: files('iscsi-opts.c'))
 block_ss.add(when: 'CONFIG_LINUX', if_true: files('nvme.c'))
-block_ss.add(when: 'CONFIG_LINUX', if_true: files('export/vhost-user-blk-server.c', '../contrib/libvhost-user/libvhost-user.c'))
 block_ss.add(when: 'CONFIG_REPLICATION', if_true: files('replication.c'))
 block_ss.add(when: 'CONFIG_SHEEPDOG', if_true: files('sheepdog.c'))
 block_ss.add(when: ['CONFIG_LINUX_AIO', libaio], if_true: files('linux-aio.c'))