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

2026-05-21 Thread Stefan Hajnoczi
On Wed, May 20, 2026 at 08:23:41PM +0200, Sam Li wrote:
> On Wed, May 20, 2026 at 7:59 PM Stefan Hajnoczi  wrote:
> >
> > On Tue, May 19, 2026 at 11:20:18PM +0200, Sam Li wrote:
> > > On Tue, May 19, 2026 at 5:49 PM Stefan Hajnoczi  
> > > wrote:
> > > >
> > > > On Mon, May 18, 2026 at 12:21:55AM +0200, Sam Li wrote:
> > > > > On Thu, May 14, 2026 at 9:49 PM Stefan Hajnoczi  
> > > > > wrote:
> > > > > > On Sun, May 10, 2026 at 07:50:57PM +0200, Sam Li wrote:
> > > > > > > + 48 - 55:  zonedmeta_offset
> > > > > > > +   The offset of zoned metadata structure in the 
> > > > > > > contained
> > > > > > > +   image, in bytes.
> > > > > >
> > > > > > Do you want to say anything about the order in which metadata is
> > > > > > persisted to disk when zones used? I guess the data is written into 
> > > > > > the
> > > > > > image file first, then the non-zoned qcow2 L1/L2/refcount metadata 
> > > > > > is
> > > > > > updated, and finally the write pointer is written. Write pointers 
> > > > > > are
> > > > > > not guaranteed to be updated on disk until the write request 
> > > > > > followed by
> > > > > > a flush request are both completed.
> > > > >
> > > > > The current ordering is not like that. The write pointer is written
> > > > > persistently first, then the data writes and the non-zoned qcow2
> > > > > L1/L2/refcount metadata updates. On IO failure, the corresponding
> > > > > write pointer is re-read from disk. As noted in the previous comment,
> > > > > the wp must be updated when issuing the IO, under the assumption that
> > > > > the write IO will succeed.
> > > > >
> > > > > The ordering has been settled this way since v7 to deal with
> > > > > concurrent zone append writes. If the wp was only updated after data
> > > > > I/O, two concurrent appends would both have read the same wp and tried
> > > > > to write to the same position.
> > > > >
> > > > > >
> > > > > > (The idea is that the data must be visible in the qcow2 file before 
> > > > > > it
> > > > > > is safe to update the write pointer. Otherwise a power failure would
> > > > > > leave the file in an inconsistent state where the write pointer has
> > > > > > advanced but the data was not written.)
> > > > >
> > > > > The crash-consistency is a concern...
> > > >
> > > > Yes, I'm thinking about crash-consistency. The ordering you described
> > > > can result in qcow2 images where the write pointer is ahead of the
> > > > actually written data after a power failure or maybe a QEMU crash.
> > > >
> > > > QEMU's block layer must follow the same data integrity behavior that
> > > > real devices guarantee.
> > >
> > > I may have found a solution to deal with both cases. The fix is to
> > > update wp in memory instead of flushing it before qcow2 metadata and
> > > data writes. The zone append write path would become:
> > >
> > > On submission:
> > >
> > > 1) wp_lock()
> > > 2) Check write alignment
> > > 3) wp_update (in memory)
> > > 4) wp_unlock()
> > > 5) Issue write
> > >
> > > And on completion:
> > > 1) If no error: wp_flush with locks and return success
> >
> > The data may not be visible in the qcow2 file yet because qcow2's 
> > L1/L2/refcount
> > cache is not written back to the file until a flush request. I think the
> > write pointer updates should have a dependency on the qcow2 metadata so
> > that write pointers are only written after qcow2 metadata.
> 
> Indeed. The qcow2 cache was also my concern. Since wp should be
> persisted after corresponding data is flushed, the cache dependency
> would be qcow2 metadata -> data -> wp. Can we set wp's dependency on
> the data so that wp is written after data is persisted? I might be
> missing something here.

The cached metadata is written after the data, so you don't need to do
anything special to ensure data -> qcow2 metadata -> wp ordering.

One thing to consider is when to increment the write pointer in the
cache. When there are concurrent requests, the wp written to file should
reflect the last _completed_ data write and not in-flight data writes.

It might be necessary to use additional state rather than incrementing
the wp cache immediately when submitting a write request. For example,
iterating over in-flight write requests to calculate the next wp based
on the maximum offset + length and only falling back to the wp cache
when there are no in-flight append requests in this zone.

> 
> >
> > See block/qcow2-cache.c and qcow2_cache_set_dependency(). The idea is
> > that one type of cached metadata can set a dependency on another type of
> > cached metadata so that ordering is guaranteed.
> 
> Thanks, I'll check it out.

By the way, I think this will require making the wp metadata a qcow2
cache object that is created with qcow2_cache_create().

Stefan

> 
> >
> > > 2) else, wp_lock()
> > > 3) read_wp (from disk) and use the read wp value as the current wp
> > > 4) wp_unlock()
> > > 5) return IO error
> > >
> > > Sam
> > >
> > > >
> > > > Damien: Do

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

2026-05-20 Thread Sam Li
On Wed, May 20, 2026 at 7:59 PM Stefan Hajnoczi  wrote:
>
> On Tue, May 19, 2026 at 11:20:18PM +0200, Sam Li wrote:
> > On Tue, May 19, 2026 at 5:49 PM Stefan Hajnoczi  wrote:
> > >
> > > On Mon, May 18, 2026 at 12:21:55AM +0200, Sam Li wrote:
> > > > On Thu, May 14, 2026 at 9:49 PM Stefan Hajnoczi  
> > > > wrote:
> > > > > On Sun, May 10, 2026 at 07:50:57PM +0200, Sam Li wrote:
> > > > > > + 48 - 55:  zonedmeta_offset
> > > > > > +   The offset of zoned metadata structure in the 
> > > > > > contained
> > > > > > +   image, in bytes.
> > > > >
> > > > > Do you want to say anything about the order in which metadata is
> > > > > persisted to disk when zones used? I guess the data is written into 
> > > > > the
> > > > > image file first, then the non-zoned qcow2 L1/L2/refcount metadata is
> > > > > updated, and finally the write pointer is written. Write pointers are
> > > > > not guaranteed to be updated on disk until the write request followed 
> > > > > by
> > > > > a flush request are both completed.
> > > >
> > > > The current ordering is not like that. The write pointer is written
> > > > persistently first, then the data writes and the non-zoned qcow2
> > > > L1/L2/refcount metadata updates. On IO failure, the corresponding
> > > > write pointer is re-read from disk. As noted in the previous comment,
> > > > the wp must be updated when issuing the IO, under the assumption that
> > > > the write IO will succeed.
> > > >
> > > > The ordering has been settled this way since v7 to deal with
> > > > concurrent zone append writes. If the wp was only updated after data
> > > > I/O, two concurrent appends would both have read the same wp and tried
> > > > to write to the same position.
> > > >
> > > > >
> > > > > (The idea is that the data must be visible in the qcow2 file before it
> > > > > is safe to update the write pointer. Otherwise a power failure would
> > > > > leave the file in an inconsistent state where the write pointer has
> > > > > advanced but the data was not written.)
> > > >
> > > > The crash-consistency is a concern...
> > >
> > > Yes, I'm thinking about crash-consistency. The ordering you described
> > > can result in qcow2 images where the write pointer is ahead of the
> > > actually written data after a power failure or maybe a QEMU crash.
> > >
> > > QEMU's block layer must follow the same data integrity behavior that
> > > real devices guarantee.
> >
> > I may have found a solution to deal with both cases. The fix is to
> > update wp in memory instead of flushing it before qcow2 metadata and
> > data writes. The zone append write path would become:
> >
> > On submission:
> >
> > 1) wp_lock()
> > 2) Check write alignment
> > 3) wp_update (in memory)
> > 4) wp_unlock()
> > 5) Issue write
> >
> > And on completion:
> > 1) If no error: wp_flush with locks and return success
>
> The data may not be visible in the qcow2 file yet because qcow2's 
> L1/L2/refcount
> cache is not written back to the file until a flush request. I think the
> write pointer updates should have a dependency on the qcow2 metadata so
> that write pointers are only written after qcow2 metadata.

Indeed. The qcow2 cache was also my concern. Since wp should be
persisted after corresponding data is flushed, the cache dependency
would be qcow2 metadata -> data -> wp. Can we set wp's dependency on
the data so that wp is written after data is persisted? I might be
missing something here.

>
> See block/qcow2-cache.c and qcow2_cache_set_dependency(). The idea is
> that one type of cached metadata can set a dependency on another type of
> cached metadata so that ordering is guaranteed.

Thanks, I'll check it out.

>
> > 2) else, wp_lock()
> > 3) read_wp (from disk) and use the read wp value as the current wp
> > 4) wp_unlock()
> > 5) return IO error
> >
> > Sam
> >
> > >
> > > Damien: Do real zoned block devices guarantee that the updated write
> > > pointer is persisted only after appended data has written been
> > > persisted?
> > >
> > > Stefan
> >



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

2026-05-20 Thread Stefan Hajnoczi
On Tue, May 19, 2026 at 11:20:18PM +0200, Sam Li wrote:
> On Tue, May 19, 2026 at 5:49 PM Stefan Hajnoczi  wrote:
> >
> > On Mon, May 18, 2026 at 12:21:55AM +0200, Sam Li wrote:
> > > On Thu, May 14, 2026 at 9:49 PM Stefan Hajnoczi  
> > > wrote:
> > > > On Sun, May 10, 2026 at 07:50:57PM +0200, Sam Li wrote:
> > > > > + 48 - 55:  zonedmeta_offset
> > > > > +   The offset of zoned metadata structure in the 
> > > > > contained
> > > > > +   image, in bytes.
> > > >
> > > > Do you want to say anything about the order in which metadata is
> > > > persisted to disk when zones used? I guess the data is written into the
> > > > image file first, then the non-zoned qcow2 L1/L2/refcount metadata is
> > > > updated, and finally the write pointer is written. Write pointers are
> > > > not guaranteed to be updated on disk until the write request followed by
> > > > a flush request are both completed.
> > >
> > > The current ordering is not like that. The write pointer is written
> > > persistently first, then the data writes and the non-zoned qcow2
> > > L1/L2/refcount metadata updates. On IO failure, the corresponding
> > > write pointer is re-read from disk. As noted in the previous comment,
> > > the wp must be updated when issuing the IO, under the assumption that
> > > the write IO will succeed.
> > >
> > > The ordering has been settled this way since v7 to deal with
> > > concurrent zone append writes. If the wp was only updated after data
> > > I/O, two concurrent appends would both have read the same wp and tried
> > > to write to the same position.
> > >
> > > >
> > > > (The idea is that the data must be visible in the qcow2 file before it
> > > > is safe to update the write pointer. Otherwise a power failure would
> > > > leave the file in an inconsistent state where the write pointer has
> > > > advanced but the data was not written.)
> > >
> > > The crash-consistency is a concern...
> >
> > Yes, I'm thinking about crash-consistency. The ordering you described
> > can result in qcow2 images where the write pointer is ahead of the
> > actually written data after a power failure or maybe a QEMU crash.
> >
> > QEMU's block layer must follow the same data integrity behavior that
> > real devices guarantee.
> 
> I may have found a solution to deal with both cases. The fix is to
> update wp in memory instead of flushing it before qcow2 metadata and
> data writes. The zone append write path would become:
> 
> On submission:
> 
> 1) wp_lock()
> 2) Check write alignment
> 3) wp_update (in memory)
> 4) wp_unlock()
> 5) Issue write
> 
> And on completion:
> 1) If no error: wp_flush with locks and return success

The data may not be visible in the qcow2 file yet because qcow2's L1/L2/refcount
cache is not written back to the file until a flush request. I think the
write pointer updates should have a dependency on the qcow2 metadata so
that write pointers are only written after qcow2 metadata.

See block/qcow2-cache.c and qcow2_cache_set_dependency(). The idea is
that one type of cached metadata can set a dependency on another type of
cached metadata so that ordering is guaranteed.

> 2) else, wp_lock()
> 3) read_wp (from disk) and use the read wp value as the current wp
> 4) wp_unlock()
> 5) return IO error
> 
> Sam
> 
> >
> > Damien: Do real zoned block devices guarantee that the updated write
> > pointer is persisted only after appended data has written been
> > persisted?
> >
> > Stefan
> 


signature.asc
Description: PGP signature


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

2026-05-19 Thread Sam Li
On Tue, May 19, 2026 at 5:49 PM Stefan Hajnoczi  wrote:
>
> On Mon, May 18, 2026 at 12:21:55AM +0200, Sam Li wrote:
> > On Thu, May 14, 2026 at 9:49 PM Stefan Hajnoczi  wrote:
> > > On Sun, May 10, 2026 at 07:50:57PM +0200, Sam Li wrote:
> > > > + 48 - 55:  zonedmeta_offset
> > > > +   The offset of zoned metadata structure in the 
> > > > contained
> > > > +   image, in bytes.
> > >
> > > Do you want to say anything about the order in which metadata is
> > > persisted to disk when zones used? I guess the data is written into the
> > > image file first, then the non-zoned qcow2 L1/L2/refcount metadata is
> > > updated, and finally the write pointer is written. Write pointers are
> > > not guaranteed to be updated on disk until the write request followed by
> > > a flush request are both completed.
> >
> > The current ordering is not like that. The write pointer is written
> > persistently first, then the data writes and the non-zoned qcow2
> > L1/L2/refcount metadata updates. On IO failure, the corresponding
> > write pointer is re-read from disk. As noted in the previous comment,
> > the wp must be updated when issuing the IO, under the assumption that
> > the write IO will succeed.
> >
> > The ordering has been settled this way since v7 to deal with
> > concurrent zone append writes. If the wp was only updated after data
> > I/O, two concurrent appends would both have read the same wp and tried
> > to write to the same position.
> >
> > >
> > > (The idea is that the data must be visible in the qcow2 file before it
> > > is safe to update the write pointer. Otherwise a power failure would
> > > leave the file in an inconsistent state where the write pointer has
> > > advanced but the data was not written.)
> >
> > The crash-consistency is a concern...
>
> Yes, I'm thinking about crash-consistency. The ordering you described
> can result in qcow2 images where the write pointer is ahead of the
> actually written data after a power failure or maybe a QEMU crash.
>
> QEMU's block layer must follow the same data integrity behavior that
> real devices guarantee.

I may have found a solution to deal with both cases. The fix is to
update wp in memory instead of flushing it before qcow2 metadata and
data writes. The zone append write path would become:

On submission:

1) wp_lock()
2) Check write alignment
3) wp_update (in memory)
4) wp_unlock()
5) Issue write

And on completion:
1) If no error: wp_flush with locks and return success
2) else, wp_lock()
3) read_wp (from disk) and use the read wp value as the current wp
4) wp_unlock()
5) return IO error

Sam

>
> Damien: Do real zoned block devices guarantee that the updated write
> pointer is persisted only after appended data has written been
> persisted?
>
> Stefan



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

2026-05-19 Thread Damien Le Moal
On 2026/05/19 17:49, Stefan Hajnoczi wrote:
> Damien: Do real zoned block devices guarantee that the updated write
> pointer is persisted only after appended data has written been
> persisted?

Yes, they do. Write data first, then persist the write pointer.
Note that when there is data (write) caching, the write pointer can show the
data as "written", but in that case, a power loss will result in returning to
the last position of written data. Persistence of the write pointer happens
always after the data is persisted on media. qcow2 image should be the same.



-- 
Damien Le Moal
Western Digital Research



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

2026-05-19 Thread Stefan Hajnoczi
On Mon, May 18, 2026 at 12:21:55AM +0200, Sam Li wrote:
> On Thu, May 14, 2026 at 9:49 PM Stefan Hajnoczi  wrote:
> > On Sun, May 10, 2026 at 07:50:57PM +0200, Sam Li wrote:
> > > + 48 - 55:  zonedmeta_offset
> > > +   The offset of zoned metadata structure in the 
> > > contained
> > > +   image, in bytes.
> >
> > Do you want to say anything about the order in which metadata is
> > persisted to disk when zones used? I guess the data is written into the
> > image file first, then the non-zoned qcow2 L1/L2/refcount metadata is
> > updated, and finally the write pointer is written. Write pointers are
> > not guaranteed to be updated on disk until the write request followed by
> > a flush request are both completed.
> 
> The current ordering is not like that. The write pointer is written
> persistently first, then the data writes and the non-zoned qcow2
> L1/L2/refcount metadata updates. On IO failure, the corresponding
> write pointer is re-read from disk. As noted in the previous comment,
> the wp must be updated when issuing the IO, under the assumption that
> the write IO will succeed.
> 
> The ordering has been settled this way since v7 to deal with
> concurrent zone append writes. If the wp was only updated after data
> I/O, two concurrent appends would both have read the same wp and tried
> to write to the same position.
> 
> >
> > (The idea is that the data must be visible in the qcow2 file before it
> > is safe to update the write pointer. Otherwise a power failure would
> > leave the file in an inconsistent state where the write pointer has
> > advanced but the data was not written.)
> 
> The crash-consistency is a concern...

Yes, I'm thinking about crash-consistency. The ordering you described
can result in qcow2 images where the write pointer is ahead of the
actually written data after a power failure or maybe a QEMU crash.

QEMU's block layer must follow the same data integrity behavior that
real devices guarantee.

Damien: Do real zoned block devices guarantee that the updated write
pointer is persisted only after appended data has written been
persisted?

Stefan


signature.asc
Description: PGP signature


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

2026-05-18 Thread Markus Armbruster
Sam Li  writes:

> To configure the zoned format feature on the qcow2 driver, it
> requires settings as: the device size, zone model, zone size,
> zone capacity, number of conventional zones, limits on zone
> resources (max append bytes, max open zones, and max_active_zones).
>
> To create a qcow2 image with zoned format feature, use command like
> this:
> qemu-img create -f qcow2 zbc.qcow2 -o size=768M \
> -o zone.size=64M -o zone.capacity=64M -o zone.conventional_zones=0 \
> -o zone.max_append_bytes=4096 -o zone.max_open_zones=6 \
> -o zone.max_active_zones=8 -o zone.mode=host-managed
>
> Signed-off-by: Sam Li 

[...]

Just doc polish this time around.  Never been closer!

> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 508b081ac1..d771dfb4a1 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -5268,6 +5268,70 @@
>  { 'enum': 'Qcow2CompressionType',
>'data': [ 'zlib', { 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] }
>  
> +##
> +# @Qcow2ZoneModel:
> +#
> +# Zoned device model used in qcow2 image file
> +#
> +# @host-managed: The host-managed model only allows sequential write
> +# over the device zones.
> +#
> +# Since 11.0

It's 11.1 now.  More of the same below, not flagging it again.

> +##
> +{ 'enum': 'Qcow2ZoneModel',
> +  'data': [ 'host-managed'] }
> +
> +##
> +# @Qcow2ZoneHostManaged:
> +#
> +# The host-managed zone model.  It only allows sequential writes.
> +#
> +# @size: Total number of bytes within zones (default 256 MB).
> +#
> +# @capacity: The number of usable logical blocks within zones
> +# in bytes.  A zone capacity is always smaller or equal to the
> +# zone size (default to zone size).

"Number blocks ... in bytes" is awkward.  Do you mean the usable space,
which is a multiple of the logical block size?

> +#
> +# @conventional-zones: The number of conventional zones of the
> +# zoned device (default 0).
> +#
> +# @max-open-zones: The maximal number of open zones.  It is less than
> +# or equal to the number of sequential write required zones of
> +# the device (default 0).

Is the "number of sequential write required zones" visible via QMP?

> +#
> +# @max-active-zones: The maximal number of zones in the implicit
> +# open, explicit open or closed state.  It is less than or equal
> +# to the max open zones (default 0).

"to the maximal number of open zones" or "to @max-open-zones"

> +#
> +# @max-append-bytes: The maximal number of bytes of a zone
> +# append request that can be issued to the device.  It must be
> +# 512-byte aligned and less than the zone capacity
> +# (default 64 KB).

"Must be 512-byte aligned": do you mean it must be a multiple of 512?

> +#
> +# Since 11.0
> +##
> +{ 'struct': 'Qcow2ZoneHostManaged',
> +  'data': { '*size':  'size',
> +'*capacity':  'size',
> +'*conventional-zones': 'uint32',
> +'*max-open-zones': 'uint32',
> +'*max-active-zones':   'uint32',
> +'*max-append-bytes':   'size' } }
> +
> +##
> +# @Qcow2ZoneCreateOptions:
> +#
> +# The zone device model for the qcow2 image.

"Device model" has a specific meaning in QEMU: it's a device frontend,
such as "ide-hd", "e1000", "usb-tablet", ...

Maybe something like "Creation options for zoned qcow2 images"?

> +#
> +# @mode: The zone device model modes.

Sure its plural modes, and not mode?

> +#
> +# Since 11.0
> +##
> +{ 'union': 'Qcow2ZoneCreateOptions',
> +  'base': { 'mode': 'Qcow2ZoneModel' },
> +  'discriminator': 'mode',
> +  'data': { 'host-managed': 'Qcow2ZoneHostManaged' } }
> +
>  ##
>  # @BlockdevCreateOptionsQcow2:
>  #
> @@ -5310,6 +5374,9 @@
>  # @compression-type: The image cluster compression method
>  # (default: zlib, since 5.1)
>  #
> +# @zone: The zone device model modes.  The default is that the
> +# device is not zoned.  (since 11.0)

Maybe something like

   # @zone: Options for zoned images.  If absent, the device is not
   # zoned.  (Since 11.0)

> +#
>  # Since: 2.12
>  ##
>  { 'struct': 'BlockdevCreateOptionsQcow2',
> @@ -5326,7 +5393,8 @@
>  '*preallocation':   'PreallocMode',
>  '*lazy-refcounts':  'bool',
>  '*refcount-bits':   'int',
> -'*compression-type':'Qcow2CompressionType' } }
> +'*compression-type':'Qcow2CompressionType',
> +'*zone':'Qcow2ZoneCreateOptions' } }
>  
>  ##
>  # @BlockdevCreateOptionsQed:




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

2026-05-17 Thread Sam Li
On Thu, May 14, 2026 at 9:49 PM Stefan Hajnoczi  wrote:
>
> On Sun, May 10, 2026 at 07:50:57PM +0200, Sam Li wrote:
> > To configure the zoned format feature on the qcow2 driver, it
> > requires settings as: the device size, zone model, zone size,
> > zone capacity, number of conventional zones, limits on zone
> > resources (max append bytes, max open zones, and max_active_zones).
> >
> > To create a qcow2 image with zoned format feature, use command like
> > this:
> > qemu-img create -f qcow2 zbc.qcow2 -o size=768M \
> > -o zone.size=64M -o zone.capacity=64M -o zone.conventional_zones=0 \
> > -o zone.max_append_bytes=4096 -o zone.max_open_zones=6 \
> > -o zone.max_active_zones=8 -o zone.mode=host-managed
> >
> > Signed-off-by: Sam Li 
> > ---
> >  block/file-posix.c   |   2 +-
> >  block/qcow2.c| 264 ++-
> >  block/qcow2.h|  32 +++-
> >  docs/interop/qcow2.rst   | 110 -
> >  include/block/block_int-common.h |  15 +-
> >  qapi/block-core.json |  70 +++-
> >  6 files changed, 485 insertions(+), 8 deletions(-)
> >
> > diff --git a/block/file-posix.c b/block/file-posix.c
> > index e49b13d6ab..14278785b9 100644
> > --- a/block/file-posix.c
> > +++ b/block/file-posix.c
> > @@ -3607,7 +3607,7 @@ raw_co_zone_append(BlockDriverState *bs,
> >
> >  if (*offset & zone_size_mask) {
> >  error_report("sector offset %" PRId64 " is not aligned to zone 
> > size "
> > - "%" PRId32 "", *offset / 512, bs->bl.zone_size / 512);
> > + "%" PRId64 "", *offset / 512, bs->bl.zone_size / 512);
> >  return -EINVAL;
> >  }
> >
> > diff --git a/block/qcow2.c b/block/qcow2.c
> > index 81fd299b4c..b543bcf3e3 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 0x007a6264
> >
> >  static int coroutine_fn
> >  qcow2_co_preadv_compressed(BlockDriverState *bs,
> > @@ -194,6 +195,74 @@ qcow2_extract_crypto_opts(QemuOpts *opts, const char 
> > *fmt, Error **errp)
> >  return cryptoopts_qdict;
> >  }
> >
> > +/*
> > + * Passing by the zoned device configurations by a zoned_header struct, 
> > check
> > + * if the zone device options are under constraints. Return false when some
> > + * option is invalid
> > + */
> > +static inline bool
> > +qcow2_check_zone_options(Qcow2ZonedHeaderExtension *zone_opt, Error **errp)
> > +{
> > +if (zone_opt) {
>
> The zone_opt == NULL case never happens. Remove it to simplify the
> function?
>
> > +uint32_t sequential_zones;
> > +
> > +if (zone_opt->zone_size == 0) {
> > +error_setg(errp, "Zoned extension header zone_size field "
> > +   "can not be 0");
> > +return false;
> > +}
> > +
> > +if (zone_opt->zone_capacity > zone_opt->zone_size) {
> > +error_setg(errp, "zone capacity %" PRIu64 "B exceeds zone size 
> > "
> > +   "%" PRIu64 "B", zone_opt->zone_capacity,
> > +   zone_opt->zone_size);
> > +return false;
> > +}
> > +
> > +if (zone_opt->max_append_bytes + BDRV_SECTOR_SIZE >=
> > +zone_opt->zone_capacity) {
> > +error_setg(errp, "max append bytes %" PRIu64 "B exceeds zone "
> > +   "capacity %" PRIu32 "B by more than block size",
> > +   zone_opt->zone_capacity,
> > +   zone_opt->max_append_bytes);
> > +return false;
> > +}
> > +
> > +if (zone_opt->conventional_zones >= zone_opt->nr_zones) {
> > +error_setg(errp, "Conventional_zones %" PRIu32 " exceeds "
> > +   "nr_zones %" PRIu32 ".",
> > +   zone_opt->conventional_zones, zone_opt->nr_zones);
> > +return false;
> > +}
> > +
> > +if (zone_opt->max_active_zones > zone_opt->nr_zones) {
> > +error_setg(errp, "Max_active_zones %" PRIu32 " exceeds "
> > +   "nr_zones %" PRIu32 ". Set it to nr_zones.",
> > +   zone_opt->max_active_zones, zone_opt->nr_zones);
> > +zone_opt->max_active_zones = zone_opt->nr_zones;
> > +}
> > +
> > +sequential_zones = zone_opt->nr_zones - 
> > zone_opt->conventional_zones;
> > +if (zone_opt->max_open_zones > sequential_zones) {
> > +error_setg(errp, "Max_open_zones field can not be larger than"
> > +   "the number of SWR zones. Set it to number of SWR"
> > +   "zones %" PRIu32 ".", sequential_zones);
> > +zone_opt->max_open_zones = sequential_zones;
> > +}
> > +if (zone_opt->max_open_zones > zone_opt->max_

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

2026-05-14 Thread Stefan Hajnoczi
On Sun, May 10, 2026 at 07:50:57PM +0200, Sam Li wrote:
> To configure the zoned format feature on the qcow2 driver, it
> requires settings as: the device size, zone model, zone size,
> zone capacity, number of conventional zones, limits on zone
> resources (max append bytes, max open zones, and max_active_zones).
> 
> To create a qcow2 image with zoned format feature, use command like
> this:
> qemu-img create -f qcow2 zbc.qcow2 -o size=768M \
> -o zone.size=64M -o zone.capacity=64M -o zone.conventional_zones=0 \
> -o zone.max_append_bytes=4096 -o zone.max_open_zones=6 \
> -o zone.max_active_zones=8 -o zone.mode=host-managed
> 
> Signed-off-by: Sam Li 
> ---
>  block/file-posix.c   |   2 +-
>  block/qcow2.c| 264 ++-
>  block/qcow2.h|  32 +++-
>  docs/interop/qcow2.rst   | 110 -
>  include/block/block_int-common.h |  15 +-
>  qapi/block-core.json |  70 +++-
>  6 files changed, 485 insertions(+), 8 deletions(-)
> 
> diff --git a/block/file-posix.c b/block/file-posix.c
> index e49b13d6ab..14278785b9 100644
> --- a/block/file-posix.c
> +++ b/block/file-posix.c
> @@ -3607,7 +3607,7 @@ raw_co_zone_append(BlockDriverState *bs,
>  
>  if (*offset & zone_size_mask) {
>  error_report("sector offset %" PRId64 " is not aligned to zone size "
> - "%" PRId32 "", *offset / 512, bs->bl.zone_size / 512);
> + "%" PRId64 "", *offset / 512, bs->bl.zone_size / 512);
>  return -EINVAL;
>  }
>  
> diff --git a/block/qcow2.c b/block/qcow2.c
> index 81fd299b4c..b543bcf3e3 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 0x007a6264
>  
>  static int coroutine_fn
>  qcow2_co_preadv_compressed(BlockDriverState *bs,
> @@ -194,6 +195,74 @@ qcow2_extract_crypto_opts(QemuOpts *opts, const char 
> *fmt, Error **errp)
>  return cryptoopts_qdict;
>  }
>  
> +/*
> + * Passing by the zoned device configurations by a zoned_header struct, check
> + * if the zone device options are under constraints. Return false when some
> + * option is invalid
> + */
> +static inline bool
> +qcow2_check_zone_options(Qcow2ZonedHeaderExtension *zone_opt, Error **errp)
> +{
> +if (zone_opt) {

The zone_opt == NULL case never happens. Remove it to simplify the
function?

> +uint32_t sequential_zones;
> +
> +if (zone_opt->zone_size == 0) {
> +error_setg(errp, "Zoned extension header zone_size field "
> +   "can not be 0");
> +return false;
> +}
> +
> +if (zone_opt->zone_capacity > zone_opt->zone_size) {
> +error_setg(errp, "zone capacity %" PRIu64 "B exceeds zone size "
> +   "%" PRIu64 "B", zone_opt->zone_capacity,
> +   zone_opt->zone_size);
> +return false;
> +}
> +
> +if (zone_opt->max_append_bytes + BDRV_SECTOR_SIZE >=
> +zone_opt->zone_capacity) {
> +error_setg(errp, "max append bytes %" PRIu64 "B exceeds zone "
> +   "capacity %" PRIu32 "B by more than block size",
> +   zone_opt->zone_capacity,
> +   zone_opt->max_append_bytes);
> +return false;
> +}
> +
> +if (zone_opt->conventional_zones >= zone_opt->nr_zones) {
> +error_setg(errp, "Conventional_zones %" PRIu32 " exceeds "
> +   "nr_zones %" PRIu32 ".",
> +   zone_opt->conventional_zones, zone_opt->nr_zones);
> +return false;
> +}
> +
> +if (zone_opt->max_active_zones > zone_opt->nr_zones) {
> +error_setg(errp, "Max_active_zones %" PRIu32 " exceeds "
> +   "nr_zones %" PRIu32 ". Set it to nr_zones.",
> +   zone_opt->max_active_zones, zone_opt->nr_zones);
> +zone_opt->max_active_zones = zone_opt->nr_zones;
> +}
> +
> +sequential_zones = zone_opt->nr_zones - zone_opt->conventional_zones;
> +if (zone_opt->max_open_zones > sequential_zones) {
> +error_setg(errp, "Max_open_zones field can not be larger than"
> +   "the number of SWR zones. Set it to number of SWR"
> +   "zones %" PRIu32 ".", sequential_zones);
> +zone_opt->max_open_zones = sequential_zones;
> +}
> +if (zone_opt->max_open_zones > zone_opt->max_active_zones) {
> +error_setg(errp, "Max_open_zones %" PRIu32 " exceeds "
> +   "max_active_zones %" PRIu32 ". Set it to "
> +   "max_active_zones.",
> +   zone_opt->max_open_zones,
> +   zone_opt-