@ -1323,9 +1323,93 @@ static int hdev_get_max_segments(int fd, struct stat *st)
}
# if defined(CONFIG_BLKZONED)
/*
* If the reset_all flag is true , then the wps of zone whose state is
* not readonly or offline should be all reset to the start sector .
* Else , take the real wp of the device .
*/
static int get_zones_wp ( BlockDriverState * bs , int fd , int64_t offset ,
unsigned int nrz , bool reset_all )
{
struct blk_zone * blkz ;
size_t rep_size ;
uint64_t sector = offset > > BDRV_SECTOR_BITS ;
BlockZoneWps * wps = bs - > wps ;
unsigned int j = offset / bs - > bl . zone_size ;
unsigned int n = 0 , i = 0 ;
int ret ;
rep_size = sizeof ( struct blk_zone_report ) + nrz * sizeof ( struct blk_zone ) ;
g_autofree struct blk_zone_report * rep = NULL ;
rep = g_malloc ( rep_size ) ;
blkz = ( struct blk_zone * ) ( rep + 1 ) ;
while ( n < nrz ) {
memset ( rep , 0 , rep_size ) ;
rep - > sector = sector ;
rep - > nr_zones = nrz - n ;
do {
ret = ioctl ( fd , BLKREPORTZONE , rep ) ;
} while ( ret ! = 0 & & errno = = EINTR ) ;
if ( ret ! = 0 ) {
error_report ( " %d: ioctl BLKREPORTZONE at % " PRId64 " failed %d " ,
fd , offset , errno ) ;
return - errno ;
}
if ( ! rep - > nr_zones ) {
break ;
}
for ( i = 0 ; i < rep - > nr_zones ; + + i , + + n , + + j ) {
/*
* The wp tracking cares only about sequential writes required and
* sequential write preferred zones so that the wp can advance to
* the right location .
* Use the most significant bit of the wp location to indicate the
* zone type : 0 for SWR / SWP zones and 1 for conventional zones .
*/
if ( blkz [ i ] . type = = BLK_ZONE_TYPE_CONVENTIONAL ) {
wps - > wp [ j ] | = 1ULL < < 63 ;
} else {
switch ( blkz [ i ] . cond ) {
case BLK_ZONE_COND_FULL :
case BLK_ZONE_COND_READONLY :
/* Zone not writable */
wps - > wp [ j ] = ( blkz [ i ] . start + blkz [ i ] . len ) < < BDRV_SECTOR_BITS ;
break ;
case BLK_ZONE_COND_OFFLINE :
/* Zone not writable nor readable */
wps - > wp [ j ] = ( blkz [ i ] . start ) < < BDRV_SECTOR_BITS ;
break ;
default :
if ( reset_all ) {
wps - > wp [ j ] = blkz [ i ] . start < < BDRV_SECTOR_BITS ;
} else {
wps - > wp [ j ] = blkz [ i ] . wp < < BDRV_SECTOR_BITS ;
}
break ;
}
}
}
sector = blkz [ i - 1 ] . start + blkz [ i - 1 ] . len ;
}
return 0 ;
}
static void update_zones_wp ( BlockDriverState * bs , int fd , int64_t offset ,
unsigned int nrz )
{
if ( get_zones_wp ( bs , fd , offset , nrz , 0 ) < 0 ) {
error_report ( " update zone wp failed " ) ;
}
}
static void raw_refresh_zoned_limits ( BlockDriverState * bs , struct stat * st ,
Error * * errp )
{
BDRVRawState * s = bs - > opaque ;
BlockZoneModel zoned ;
int ret ;
@ -1376,6 +1460,23 @@ static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st,
if ( ret > 0 ) {
bs - > bl . max_append_sectors = ret > > BDRV_SECTOR_BITS ;
}
ret = get_sysfs_long_val ( st , " physical_block_size " ) ;
if ( ret > = 0 ) {
bs - > bl . write_granularity = ret ;
}
/* The refresh_limits() function can be called multiple times. */
g_free ( bs - > wps ) ;
bs - > wps = g_malloc ( sizeof ( BlockZoneWps ) +
sizeof ( int64_t ) * bs - > bl . nr_zones ) ;
ret = get_zones_wp ( bs , s - > fd , 0 , bs - > bl . nr_zones , 0 ) ;
if ( ret < 0 ) {
error_setg_errno ( errp , - ret , " report wps failed " ) ;
bs - > wps = NULL ;
return ;
}
qemu_co_mutex_init ( & bs - > wps - > colock ) ;
}
# else /* !defined(CONFIG_BLKZONED) */
static void raw_refresh_zoned_limits ( BlockDriverState * bs , struct stat * st ,
@ -2061,7 +2162,7 @@ static int handle_aiocb_zone_mgmt(void *opaque)
ret = ioctl ( fd , aiocb - > zone_mgmt . op , & range ) ;
} while ( ret ! = 0 & & errno = = EINTR ) ;
return ret ;
return ret < 0 ? - errno : ret ;
}
# endif
@ -2347,9 +2448,15 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset,
{
BDRVRawState * s = bs - > opaque ;
RawPosixAIOData acb ;
int ret ;
if ( fd_open ( bs ) < 0 )
return - EIO ;
# if defined(CONFIG_BLKZONED)
if ( type & QEMU_AIO_WRITE & & bs - > wps ) {
qemu_co_mutex_lock ( & bs - > wps - > colock ) ;
}
# endif
/*
* When using O_DIRECT , the request must be aligned to be able to use
@ -2362,12 +2469,15 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset,
# ifdef CONFIG_LINUX_IO_URING
} else if ( s - > use_linux_io_uring ) {
assert ( qiov - > size = = bytes ) ;
return luring_co_submit ( bs , s - > fd , offset , qiov , type ) ;
ret = luring_co_submit ( bs , s - > fd , offset , qiov , type ) ;
goto out ;
# endif
# ifdef CONFIG_LINUX_AIO
} else if ( s - > use_linux_aio ) {
assert ( qiov - > size = = bytes ) ;
return laio_co_submit ( s - > fd , offset , qiov , type , s - > aio_max_batch ) ;
ret = laio_co_submit ( s - > fd , offset , qiov , type ,
s - > aio_max_batch ) ;
goto out ;
# endif
}
@ -2384,7 +2494,35 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset,
} ;
assert ( qiov - > size = = bytes ) ;
return raw_thread_pool_submit ( handle_aiocb_rw , & acb ) ;
ret = raw_thread_pool_submit ( handle_aiocb_rw , & acb ) ;
goto out ; /* Avoid the compiler err of unused label */
out :
# if defined(CONFIG_BLKZONED)
{
BlockZoneWps * wps = bs - > wps ;
if ( ret = = 0 ) {
if ( type & QEMU_AIO_WRITE & & wps & & bs - > bl . zone_size ) {
uint64_t * wp = & wps - > wp [ offset / bs - > bl . zone_size ] ;
if ( ! BDRV_ZT_IS_CONV ( * wp ) ) {
/* Advance the wp if needed */
if ( offset + bytes > * wp ) {
* wp = offset + bytes ;
}
}
}
} else {
if ( type & QEMU_AIO_WRITE ) {
update_zones_wp ( bs , s - > fd , 0 , 1 ) ;
}
}
if ( type & QEMU_AIO_WRITE & & wps ) {
qemu_co_mutex_unlock ( & wps - > colock ) ;
}
}
# endif
return ret ;
}
static int coroutine_fn raw_co_preadv ( BlockDriverState * bs , int64_t offset ,
@ -2487,6 +2625,9 @@ static void raw_close(BlockDriverState *bs)
BDRVRawState * s = bs - > opaque ;
if ( s - > fd > = 0 ) {
# if defined(CONFIG_BLKZONED)
g_free ( bs - > wps ) ;
# endif
qemu_close ( s - > fd ) ;
s - > fd = - 1 ;
}
@ -3284,6 +3425,7 @@ static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op,
const char * op_name ;
unsigned long zo ;
int ret ;
BlockZoneWps * wps = bs - > wps ;
int64_t capacity = bs - > total_sectors < < BDRV_SECTOR_BITS ;
zone_size = bs - > bl . zone_size ;
@ -3301,6 +3443,14 @@ static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op,
return - EINVAL ;
}
uint32_t i = offset / bs - > bl . zone_size ;
uint32_t nrz = len / bs - > bl . zone_size ;
uint64_t * wp = & wps - > wp [ i ] ;
if ( BDRV_ZT_IS_CONV ( * wp ) & & len ! = capacity ) {
error_report ( " zone mgmt operations are not allowed for conventional zones " ) ;
return - EIO ;
}
switch ( op ) {
case BLK_ZO_OPEN :
op_name = " BLKOPENZONE " ;
@ -3338,7 +3488,27 @@ static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op,
len > > BDRV_SECTOR_BITS ) ;
ret = raw_thread_pool_submit ( handle_aiocb_zone_mgmt , & acb ) ;
if ( ret ! = 0 ) {
update_zones_wp ( bs , s - > fd , offset , i ) ;
error_report ( " ioctl %s failed %d " , op_name , ret ) ;
return ret ;
}
if ( zo = = BLKRESETZONE & & len = = capacity ) {
ret = get_zones_wp ( bs , s - > fd , 0 , bs - > bl . nr_zones , 1 ) ;
if ( ret < 0 ) {
error_report ( " reporting single wp failed " ) ;
return ret ;
}
} else if ( zo = = BLKRESETZONE ) {
for ( unsigned int j = 0 ; j < nrz ; + + j ) {
wp [ j ] = offset + j * zone_size ;
}
} else if ( zo = = BLKFINISHZONE ) {
for ( unsigned int j = 0 ; j < nrz ; + + j ) {
/* The zoned device allows the last zone smaller that the
* zone size . */
wp [ j ] = MIN ( offset + ( j + 1 ) * zone_size , offset + len ) ;
}
}
return ret ;