Re: [RFC 2/4] qcow2: add configurations for zoned format extension

2023-06-20 Thread Sam Li
Stefan Hajnoczi  于2023年6月20日周二 22:48写道:
>
> On Mon, Jun 19, 2023 at 10:50:31PM +0800, Sam Li wrote:
> > Stefan Hajnoczi  于2023年6月19日周一 22:42写道:
> > >
> > > On Mon, Jun 19, 2023 at 06:32:52PM +0800, Sam Li wrote:
> > > > Stefan Hajnoczi  于2023年6月19日周一 18:10写道:
> > > > > On Mon, Jun 05, 2023 at 06:41:06PM +0800, Sam Li wrote:
> > > > > > diff --git a/block/qcow2.h b/block/qcow2.h
> > > > > > index 4f67eb912a..fe18dc4d97 100644
> > > > > > --- a/block/qcow2.h
> > > > > > +++ b/block/qcow2.h
> > > > > > @@ -235,6 +235,20 @@ typedef struct Qcow2CryptoHeaderExtension {
> > > > > >  uint64_t length;
> > > > > >  } QEMU_PACKED Qcow2CryptoHeaderExtension;
> > > > > >
> > > > > > +typedef struct Qcow2ZonedHeaderExtension {
> > > > > > +/* Zoned device attributes */
> > > > > > +BlockZonedProfile zoned_profile;
> > > > > > +BlockZoneModel zoned;
> > > > > > +uint32_t zone_size;
> > > > > > +uint32_t zone_capacity;
> > > > > > +uint32_t nr_zones;
> > > > > > +uint32_t zone_nr_conv;
> > > > > > +uint32_t max_active_zones;
> > > > > > +uint32_t max_open_zones;
> > > > > > +uint32_t max_append_sectors;
> > > > > > +uint8_t padding[3];
> > > > >
> > > > > This looks strange. Why is there 3 bytes of padding at the end? 
> > > > > Normally
> > > > > padding would align to an even power-of-two number of bytes like 2, 4,
> > > > > 8, etc.
> > > >
> > > > It is calculated as 3 if sizeof(zoned+zoned_profile) = 8. Else if it's
> > > > 16, the padding is 2.
> > >
> > > I don't understand. Can you explain why there is padding at the end of
> > > this struct?
> >
> > The overall size should be aligned with 64 bit, which leaves use one
> > uint32_t and two fields zoned, zoned_profile. I am not sure the size
> > of macros here and it used 4 for each. So it makes 3 (*8) + 32 + 8 =
> > 64 in the end. If the macro size is wrong, then the padding will
> > change as well.
>
> The choice of the type (char or int) representing an enum is
> implementation-defined according to the C17 standard (see "6.7.2.2
> Enumeration specifiers").
>
> Therefore it's not portable to use enums in structs exposed to the
> outside world (on-disk formats or network protocols).
>
> Please use uint8_t for the zoned_profile and zoned fields and move them
> to the end of the struct so the uint32_t fields are naturally aligned.
>
> I think only 2 bytes of padding will be required to align the struct to
> a 64-bit boundary once you've done that.

I see. Thanks!

Sam



Re: [RFC 2/4] qcow2: add configurations for zoned format extension

2023-06-20 Thread Stefan Hajnoczi
On Mon, Jun 19, 2023 at 10:50:31PM +0800, Sam Li wrote:
> Stefan Hajnoczi  于2023年6月19日周一 22:42写道:
> >
> > On Mon, Jun 19, 2023 at 06:32:52PM +0800, Sam Li wrote:
> > > Stefan Hajnoczi  于2023年6月19日周一 18:10写道:
> > > > On Mon, Jun 05, 2023 at 06:41:06PM +0800, Sam Li wrote:
> > > > > diff --git a/block/qcow2.h b/block/qcow2.h
> > > > > index 4f67eb912a..fe18dc4d97 100644
> > > > > --- a/block/qcow2.h
> > > > > +++ b/block/qcow2.h
> > > > > @@ -235,6 +235,20 @@ typedef struct Qcow2CryptoHeaderExtension {
> > > > >  uint64_t length;
> > > > >  } QEMU_PACKED Qcow2CryptoHeaderExtension;
> > > > >
> > > > > +typedef struct Qcow2ZonedHeaderExtension {
> > > > > +/* Zoned device attributes */
> > > > > +BlockZonedProfile zoned_profile;
> > > > > +BlockZoneModel zoned;
> > > > > +uint32_t zone_size;
> > > > > +uint32_t zone_capacity;
> > > > > +uint32_t nr_zones;
> > > > > +uint32_t zone_nr_conv;
> > > > > +uint32_t max_active_zones;
> > > > > +uint32_t max_open_zones;
> > > > > +uint32_t max_append_sectors;
> > > > > +uint8_t padding[3];
> > > >
> > > > This looks strange. Why is there 3 bytes of padding at the end? Normally
> > > > padding would align to an even power-of-two number of bytes like 2, 4,
> > > > 8, etc.
> > >
> > > It is calculated as 3 if sizeof(zoned+zoned_profile) = 8. Else if it's
> > > 16, the padding is 2.
> >
> > I don't understand. Can you explain why there is padding at the end of
> > this struct?
> 
> The overall size should be aligned with 64 bit, which leaves use one
> uint32_t and two fields zoned, zoned_profile. I am not sure the size
> of macros here and it used 4 for each. So it makes 3 (*8) + 32 + 8 =
> 64 in the end. If the macro size is wrong, then the padding will
> change as well.

The choice of the type (char or int) representing an enum is
implementation-defined according to the C17 standard (see "6.7.2.2
Enumeration specifiers").

Therefore it's not portable to use enums in structs exposed to the
outside world (on-disk formats or network protocols).

Please use uint8_t for the zoned_profile and zoned fields and move them
to the end of the struct so the uint32_t fields are naturally aligned.

I think only 2 bytes of padding will be required to align the struct to
a 64-bit boundary once you've done that.

Stefan


signature.asc
Description: PGP signature


Re: [RFC 2/4] qcow2: add configurations for zoned format extension

2023-06-19 Thread Sam Li
Stefan Hajnoczi  于2023年6月19日周一 22:42写道:
>
> On Mon, Jun 19, 2023 at 06:32:52PM +0800, Sam Li wrote:
> > Stefan Hajnoczi  于2023年6月19日周一 18:10写道:
> > > On Mon, Jun 05, 2023 at 06:41:06PM +0800, Sam Li wrote:
> > > > diff --git a/block/qcow2.h b/block/qcow2.h
> > > > index 4f67eb912a..fe18dc4d97 100644
> > > > --- a/block/qcow2.h
> > > > +++ b/block/qcow2.h
> > > > @@ -235,6 +235,20 @@ typedef struct Qcow2CryptoHeaderExtension {
> > > >  uint64_t length;
> > > >  } QEMU_PACKED Qcow2CryptoHeaderExtension;
> > > >
> > > > +typedef struct Qcow2ZonedHeaderExtension {
> > > > +/* Zoned device attributes */
> > > > +BlockZonedProfile zoned_profile;
> > > > +BlockZoneModel zoned;
> > > > +uint32_t zone_size;
> > > > +uint32_t zone_capacity;
> > > > +uint32_t nr_zones;
> > > > +uint32_t zone_nr_conv;
> > > > +uint32_t max_active_zones;
> > > > +uint32_t max_open_zones;
> > > > +uint32_t max_append_sectors;
> > > > +uint8_t padding[3];
> > >
> > > This looks strange. Why is there 3 bytes of padding at the end? Normally
> > > padding would align to an even power-of-two number of bytes like 2, 4,
> > > 8, etc.
> >
> > It is calculated as 3 if sizeof(zoned+zoned_profile) = 8. Else if it's
> > 16, the padding is 2.
>
> I don't understand. Can you explain why there is padding at the end of
> this struct?

The overall size should be aligned with 64 bit, which leaves use one
uint32_t and two fields zoned, zoned_profile. I am not sure the size
of macros here and it used 4 for each. So it makes 3 (*8) + 32 + 8 =
64 in the end. If the macro size is wrong, then the padding will
change as well.

Sam



Re: [RFC 2/4] qcow2: add configurations for zoned format extension

2023-06-19 Thread Stefan Hajnoczi
On Mon, Jun 19, 2023 at 06:32:52PM +0800, Sam Li wrote:
> Stefan Hajnoczi  于2023年6月19日周一 18:10写道:
> > On Mon, Jun 05, 2023 at 06:41:06PM +0800, Sam Li wrote:
> > > diff --git a/block/qcow2.h b/block/qcow2.h
> > > index 4f67eb912a..fe18dc4d97 100644
> > > --- a/block/qcow2.h
> > > +++ b/block/qcow2.h
> > > @@ -235,6 +235,20 @@ typedef struct Qcow2CryptoHeaderExtension {
> > >  uint64_t length;
> > >  } QEMU_PACKED Qcow2CryptoHeaderExtension;
> > >
> > > +typedef struct Qcow2ZonedHeaderExtension {
> > > +/* Zoned device attributes */
> > > +BlockZonedProfile zoned_profile;
> > > +BlockZoneModel zoned;
> > > +uint32_t zone_size;
> > > +uint32_t zone_capacity;
> > > +uint32_t nr_zones;
> > > +uint32_t zone_nr_conv;
> > > +uint32_t max_active_zones;
> > > +uint32_t max_open_zones;
> > > +uint32_t max_append_sectors;
> > > +uint8_t padding[3];
> >
> > This looks strange. Why is there 3 bytes of padding at the end? Normally
> > padding would align to an even power-of-two number of bytes like 2, 4,
> > 8, etc.
> 
> It is calculated as 3 if sizeof(zoned+zoned_profile) = 8. Else if it's
> 16, the padding is 2.

I don't understand. Can you explain why there is padding at the end of
this struct?


signature.asc
Description: PGP signature


Re: [RFC 2/4] qcow2: add configurations for zoned format extension

2023-06-19 Thread Sam Li
Stefan Hajnoczi  于2023年6月19日周一 18:10写道:
>
> On Mon, Jun 05, 2023 at 06:41:06PM +0800, Sam Li wrote:
> > To configure the zoned format feature on the qcow2 driver, it
> > requires following arguments: the device size, zoned profile,
> > zoned model, zone size, zone capacity, number of conventional
> > zones, limits on zone resources (max append sectors, max open
> > zones, and max_active_zones).
> >
> > To create a qcow2 file with zoned format, use command like this:
> > $ qemu-img create -f qcow2 test.qcow2 -o size=768M -o
> > zone_size=64M -o zone_capacity=64M -o zone_nr_conv=0 -o
> > max_append_sectors=512 -o max_open_zones=0 -o max_active_zones=0
> >  -o zoned_profile=zbc
> >
> > Signed-off-by: Sam Li 
> > ---
> >  block/qcow2.c| 119 +++
> >  block/qcow2.h|  21 ++
> >  include/block/block-common.h |   5 ++
> >  include/block/block_int-common.h |   8 +++
> >  qapi/block-core.json |  46 
> >  5 files changed, 185 insertions(+), 14 deletions(-)
> >
> > diff --git a/block/qcow2.c b/block/qcow2.c
> > index 7f3948360d..b886dab42b 100644
> > --- a/block/qcow2.c
> > +++ b/block/qcow2.c
> > @@ -73,6 +73,7 @@ typedef struct {
> >  #define  QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77
> >  #define  QCOW2_EXT_MAGIC_BITMAPS 0x23852875
> >  #define  QCOW2_EXT_MAGIC_DATA_FILE 0x44415441
> > +#define  QCOW2_EXT_MAGIC_ZONED_FORMAT 0x7a6264
> >
> >  static int coroutine_fn
> >  qcow2_co_preadv_compressed(BlockDriverState *bs,
> > @@ -210,6 +211,7 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t 
> > start_offset,
> >  uint64_t offset;
> >  int ret;
> >  Qcow2BitmapHeaderExt bitmaps_ext;
> > +Qcow2ZonedHeaderExtension zoned_ext;
> >
> >  if (need_update_header != NULL) {
> >  *need_update_header = false;
> > @@ -431,6 +433,37 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t 
> > start_offset,
> >  break;
> >  }
> >
> > +case QCOW2_EXT_MAGIC_ZONED_FORMAT:
> > +{
> > +if (ext.len != sizeof(zoned_ext)) {
> > +error_setg_errno(errp, -ret, "zoned_ext: "
> > + "Invalid extension length");
> > +return -EINVAL;
> > +}
> > +ret = bdrv_pread(bs->file, offset, ext.len, _ext, 0);
> > +if (ret < 0) {
> > +error_setg_errno(errp, -ret, "zoned_ext: "
> > + "Could not read ext header");
> > +return ret;
> > +}
> > +
> > +zoned_ext.zone_size = be32_to_cpu(zoned_ext.zone_size);
> > +zoned_ext.nr_zones = be32_to_cpu(zoned_ext.nr_zones);
> > +zoned_ext.zone_nr_conv = be32_to_cpu(zoned_ext.zone_nr_conv);
> > +zoned_ext.max_open_zones = 
> > be32_to_cpu(zoned_ext.max_open_zones);
> > +zoned_ext.max_active_zones =
> > +be32_to_cpu(zoned_ext.max_active_zones);
> > +zoned_ext.max_append_sectors =
> > +be32_to_cpu(zoned_ext.max_append_sectors);
> > +s->zoned_header = zoned_ext;
>
> Please validate these values. The image file is not trusted and may be
> broken/corrupt. For example, zone_size=0 and nr_zones=0 must be rejected
> because the code can't do anything useful when these values are zero
> (similar for values that are not multiples of the block size).
>
> > +
> > +#ifdef DEBUG_EXT
> > +printf("Qcow2: Got zoned format extension: "
> > +   "offset=%" PRIu32 "\n", offset);
> > +#endif
> > +break;
> > +}
> > +
> >  default:
> >  /* unknown magic - save it in case we need to rewrite the 
> > header */
> >  /* If you add a new feature, make sure to also update the fast
> > @@ -3071,6 +3104,31 @@ int qcow2_update_header(BlockDriverState *bs)
> >  buflen -= ret;
> >  }
> >
> > +/* Zoned devices header extension */
> > +if (s->zoned_header.zoned == BLK_Z_HM) {
> > +Qcow2ZonedHeaderExtension zoned_header = {
> > +.zoned_profile  = s->zoned_header.zoned_profile,
> > +.zoned  = s->zoned_header.zoned,
> > +.nr_zones   = cpu_to_be32(s->zoned_header.nr_zones),
> > +.zone_size  = cpu_to_be32(s->zoned_header.zone_size),
> > +.zone_capacity  = 
> > cpu_to_be32(s->zoned_header.zone_capacity),
> > +.zone_nr_conv   = 
> > cpu_to_be32(s->zoned_header.zone_nr_conv),
> > +.max_open_zones = 
> > cpu_to_be32(s->zoned_header.max_open_zones),
> > +.max_active_zones   =
> > +cpu_to_be32(s->zoned_header.max_active_zones),
> > +.max_append_sectors =
> > +cpu_to_be32(s->zoned_header.max_append_sectors)
> > +};
> > +ret = header_ext_add(buf, QCOW2_EXT_MAGIC_ZONED_FORMAT,
> > +   

Re: [RFC 2/4] qcow2: add configurations for zoned format extension

2023-06-19 Thread Stefan Hajnoczi
On Mon, Jun 05, 2023 at 06:41:06PM +0800, Sam Li wrote:
> To configure the zoned format feature on the qcow2 driver, it
> requires following arguments: the device size, zoned profile,
> zoned model, zone size, zone capacity, number of conventional
> zones, limits on zone resources (max append sectors, max open
> zones, and max_active_zones).
> 
> To create a qcow2 file with zoned format, use command like this:
> $ qemu-img create -f qcow2 test.qcow2 -o size=768M -o
> zone_size=64M -o zone_capacity=64M -o zone_nr_conv=0 -o
> max_append_sectors=512 -o max_open_zones=0 -o max_active_zones=0
>  -o zoned_profile=zbc
> 
> Signed-off-by: Sam Li 
> ---
>  block/qcow2.c| 119 +++
>  block/qcow2.h|  21 ++
>  include/block/block-common.h |   5 ++
>  include/block/block_int-common.h |   8 +++
>  qapi/block-core.json |  46 
>  5 files changed, 185 insertions(+), 14 deletions(-)
> 
> diff --git a/block/qcow2.c b/block/qcow2.c
> index 7f3948360d..b886dab42b 100644
> --- a/block/qcow2.c
> +++ b/block/qcow2.c
> @@ -73,6 +73,7 @@ typedef struct {
>  #define  QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77
>  #define  QCOW2_EXT_MAGIC_BITMAPS 0x23852875
>  #define  QCOW2_EXT_MAGIC_DATA_FILE 0x44415441
> +#define  QCOW2_EXT_MAGIC_ZONED_FORMAT 0x7a6264
>  
>  static int coroutine_fn
>  qcow2_co_preadv_compressed(BlockDriverState *bs,
> @@ -210,6 +211,7 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t 
> start_offset,
>  uint64_t offset;
>  int ret;
>  Qcow2BitmapHeaderExt bitmaps_ext;
> +Qcow2ZonedHeaderExtension zoned_ext;
>  
>  if (need_update_header != NULL) {
>  *need_update_header = false;
> @@ -431,6 +433,37 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t 
> start_offset,
>  break;
>  }
>  
> +case QCOW2_EXT_MAGIC_ZONED_FORMAT:
> +{
> +if (ext.len != sizeof(zoned_ext)) {
> +error_setg_errno(errp, -ret, "zoned_ext: "
> + "Invalid extension length");
> +return -EINVAL;
> +}
> +ret = bdrv_pread(bs->file, offset, ext.len, _ext, 0);
> +if (ret < 0) {
> +error_setg_errno(errp, -ret, "zoned_ext: "
> + "Could not read ext header");
> +return ret;
> +}
> +
> +zoned_ext.zone_size = be32_to_cpu(zoned_ext.zone_size);
> +zoned_ext.nr_zones = be32_to_cpu(zoned_ext.nr_zones);
> +zoned_ext.zone_nr_conv = be32_to_cpu(zoned_ext.zone_nr_conv);
> +zoned_ext.max_open_zones = be32_to_cpu(zoned_ext.max_open_zones);
> +zoned_ext.max_active_zones =
> +be32_to_cpu(zoned_ext.max_active_zones);
> +zoned_ext.max_append_sectors =
> +be32_to_cpu(zoned_ext.max_append_sectors);
> +s->zoned_header = zoned_ext;

Please validate these values. The image file is not trusted and may be
broken/corrupt. For example, zone_size=0 and nr_zones=0 must be rejected
because the code can't do anything useful when these values are zero
(similar for values that are not multiples of the block size).

> +
> +#ifdef DEBUG_EXT
> +printf("Qcow2: Got zoned format extension: "
> +   "offset=%" PRIu32 "\n", offset);
> +#endif
> +break;
> +}
> +
>  default:
>  /* unknown magic - save it in case we need to rewrite the header 
> */
>  /* If you add a new feature, make sure to also update the fast
> @@ -3071,6 +3104,31 @@ int qcow2_update_header(BlockDriverState *bs)
>  buflen -= ret;
>  }
>  
> +/* Zoned devices header extension */
> +if (s->zoned_header.zoned == BLK_Z_HM) {
> +Qcow2ZonedHeaderExtension zoned_header = {
> +.zoned_profile  = s->zoned_header.zoned_profile,
> +.zoned  = s->zoned_header.zoned,
> +.nr_zones   = cpu_to_be32(s->zoned_header.nr_zones),
> +.zone_size  = cpu_to_be32(s->zoned_header.zone_size),
> +.zone_capacity  = cpu_to_be32(s->zoned_header.zone_capacity),
> +.zone_nr_conv   = cpu_to_be32(s->zoned_header.zone_nr_conv),
> +.max_open_zones = 
> cpu_to_be32(s->zoned_header.max_open_zones),
> +.max_active_zones   =
> +cpu_to_be32(s->zoned_header.max_active_zones),
> +.max_append_sectors =
> +cpu_to_be32(s->zoned_header.max_append_sectors)
> +};
> +ret = header_ext_add(buf, QCOW2_EXT_MAGIC_ZONED_FORMAT,
> + _header, sizeof(zoned_header),
> + buflen);
> +if (ret < 0) {
> +goto fail;
> +}
> +buf += ret;
> +buflen -= ret;
> +}
> +
>  /* Keep unknown header extensions 

Re: [RFC 2/4] qcow2: add configurations for zoned format extension

2023-06-05 Thread Eric Blake
On Mon, Jun 05, 2023 at 06:41:06PM +0800, Sam Li wrote:
> To configure the zoned format feature on the qcow2 driver, it
> requires following arguments: the device size, zoned profile,
> zoned model, zone size, zone capacity, number of conventional
> zones, limits on zone resources (max append sectors, max open
> zones, and max_active_zones).
> 
> To create a qcow2 file with zoned format, use command like this:
> $ qemu-img create -f qcow2 test.qcow2 -o size=768M -o
> zone_size=64M -o zone_capacity=64M -o zone_nr_conv=0 -o
> max_append_sectors=512 -o max_open_zones=0 -o max_active_zones=0
>  -o zoned_profile=zbc
> 
> Signed-off-by: Sam Li 
> ---
>  block/qcow2.c| 119 +++
>  block/qcow2.h|  21 ++
>  include/block/block-common.h |   5 ++
>  include/block/block_int-common.h |   8 +++
>  qapi/block-core.json |  46 
>  5 files changed, 185 insertions(+), 14 deletions(-)
>
...
> diff --git a/block/qcow2.c b/block/qcow2.c
> index 7f3948360d..b886dab42b 100644
> --- a/block/qcow2.c
> +++ b/block/qcow2.c
> @@ -73,6 +73,7 @@ typedef struct {
>  #define  QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77
>  #define  QCOW2_EXT_MAGIC_BITMAPS 0x23852875
>  #define  QCOW2_EXT_MAGIC_DATA_FILE 0x44415441
> +#define  QCOW2_EXT_MAGIC_ZONED_FORMAT 0x7a6264
>  
>  static int coroutine_fn
>  qcow2_co_preadv_compressed(BlockDriverState *bs,
> @@ -210,6 +211,7 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t 
> start_offset,
>  uint64_t offset;
>  int ret;
>  Qcow2BitmapHeaderExt bitmaps_ext;
> +Qcow2ZonedHeaderExtension zoned_ext;
>  
>  if (need_update_header != NULL) {
>  *need_update_header = false;
> @@ -431,6 +433,37 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t 
> start_offset,
>  break;
>  }
>  
> +case QCOW2_EXT_MAGIC_ZONED_FORMAT:
> +{

Missing a patch to docs/interop/qcow2.txt that describes the new
header so that other qcow2 implementations can be interoperable with
it.

[unrelated - maybe we should convert that file to .rst someday?]

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.   +1-919-301-3266
Virtualization:  qemu.org | libvirt.org




[RFC 2/4] qcow2: add configurations for zoned format extension

2023-06-05 Thread Sam Li
To configure the zoned format feature on the qcow2 driver, it
requires following arguments: the device size, zoned profile,
zoned model, zone size, zone capacity, number of conventional
zones, limits on zone resources (max append sectors, max open
zones, and max_active_zones).

To create a qcow2 file with zoned format, use command like this:
$ qemu-img create -f qcow2 test.qcow2 -o size=768M -o
zone_size=64M -o zone_capacity=64M -o zone_nr_conv=0 -o
max_append_sectors=512 -o max_open_zones=0 -o max_active_zones=0
 -o zoned_profile=zbc

Signed-off-by: Sam Li 
---
 block/qcow2.c| 119 +++
 block/qcow2.h|  21 ++
 include/block/block-common.h |   5 ++
 include/block/block_int-common.h |   8 +++
 qapi/block-core.json |  46 
 5 files changed, 185 insertions(+), 14 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 7f3948360d..b886dab42b 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -73,6 +73,7 @@ typedef struct {
 #define  QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77
 #define  QCOW2_EXT_MAGIC_BITMAPS 0x23852875
 #define  QCOW2_EXT_MAGIC_DATA_FILE 0x44415441
+#define  QCOW2_EXT_MAGIC_ZONED_FORMAT 0x7a6264
 
 static int coroutine_fn
 qcow2_co_preadv_compressed(BlockDriverState *bs,
@@ -210,6 +211,7 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t 
start_offset,
 uint64_t offset;
 int ret;
 Qcow2BitmapHeaderExt bitmaps_ext;
+Qcow2ZonedHeaderExtension zoned_ext;
 
 if (need_update_header != NULL) {
 *need_update_header = false;
@@ -431,6 +433,37 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t 
start_offset,
 break;
 }
 
+case QCOW2_EXT_MAGIC_ZONED_FORMAT:
+{
+if (ext.len != sizeof(zoned_ext)) {
+error_setg_errno(errp, -ret, "zoned_ext: "
+ "Invalid extension length");
+return -EINVAL;
+}
+ret = bdrv_pread(bs->file, offset, ext.len, _ext, 0);
+if (ret < 0) {
+error_setg_errno(errp, -ret, "zoned_ext: "
+ "Could not read ext header");
+return ret;
+}
+
+zoned_ext.zone_size = be32_to_cpu(zoned_ext.zone_size);
+zoned_ext.nr_zones = be32_to_cpu(zoned_ext.nr_zones);
+zoned_ext.zone_nr_conv = be32_to_cpu(zoned_ext.zone_nr_conv);
+zoned_ext.max_open_zones = be32_to_cpu(zoned_ext.max_open_zones);
+zoned_ext.max_active_zones =
+be32_to_cpu(zoned_ext.max_active_zones);
+zoned_ext.max_append_sectors =
+be32_to_cpu(zoned_ext.max_append_sectors);
+s->zoned_header = zoned_ext;
+
+#ifdef DEBUG_EXT
+printf("Qcow2: Got zoned format extension: "
+   "offset=%" PRIu32 "\n", offset);
+#endif
+break;
+}
+
 default:
 /* unknown magic - save it in case we need to rewrite the header */
 /* If you add a new feature, make sure to also update the fast
@@ -3071,6 +3104,31 @@ int qcow2_update_header(BlockDriverState *bs)
 buflen -= ret;
 }
 
+/* Zoned devices header extension */
+if (s->zoned_header.zoned == BLK_Z_HM) {
+Qcow2ZonedHeaderExtension zoned_header = {
+.zoned_profile  = s->zoned_header.zoned_profile,
+.zoned  = s->zoned_header.zoned,
+.nr_zones   = cpu_to_be32(s->zoned_header.nr_zones),
+.zone_size  = cpu_to_be32(s->zoned_header.zone_size),
+.zone_capacity  = cpu_to_be32(s->zoned_header.zone_capacity),
+.zone_nr_conv   = cpu_to_be32(s->zoned_header.zone_nr_conv),
+.max_open_zones = cpu_to_be32(s->zoned_header.max_open_zones),
+.max_active_zones   =
+cpu_to_be32(s->zoned_header.max_active_zones),
+.max_append_sectors =
+cpu_to_be32(s->zoned_header.max_append_sectors)
+};
+ret = header_ext_add(buf, QCOW2_EXT_MAGIC_ZONED_FORMAT,
+ _header, sizeof(zoned_header),
+ buflen);
+if (ret < 0) {
+goto fail;
+}
+buf += ret;
+buflen -= ret;
+}
+
 /* Keep unknown header extensions */
 QLIST_FOREACH(uext, >unknown_header_ext, next) {
 ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen);
@@ -3755,6 +3813,18 @@ qcow2_co_create(BlockdevCreateOptions *create_options, 
Error **errp)
 s->image_data_file = g_strdup(data_bs->filename);
 }
 
+if (!strcmp(qcow2_opts->zoned_profile, "zbc")) {
+BDRVQcow2State *s = blk_bs(blk)->opaque;
+s->zoned_header.zoned_profile = BLK_ZP_ZBC;
+s->zoned_header.zoned = BLK_Z_HM;
+s->zoned_header.zone_size = qcow2_opts->zone_size;
+