Block layer patches

- Discard alignment fixes
 - Remove unused callback .bdrv_aio_pdiscard()
 - qemu-img bench: Input validation fix
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmgLy7QRHGt3b2xmQHJl
 ZGhhdC5jb20ACgkQfwmycsiPL9YB4A//Zsbb+tVsyLBKeffwPpHF/cAzHVH7Q2dV
 GC2JJvfrwq0gykfjj+u4akVQnPh49QiQM623PX7O15IikwdLy45ddQcYL1qflYCs
 ZGmBOuz/deI74qjl67bZVqIm8WeRhwHkdutfXOL7GRe2IHbceLbwwGUcbCgOVavt
 LHu3E2MIbvkLJoHEgg8UbJhZZY9DTLGDaMt00Yhy3UvNHU8UDeIr8o4dxMVv3gOf
 +8kIjGQkYNqpWp7aCxy8vofdSFjbBp4lSCK4G83xikUw49qkwWcgZ6jyTzXALg0G
 V+nMjH+DnfIRqhi1skFTHQNmFc6upxr7FIOgC+G5amkKLHCPnX9j5/2pBwrk63R7
 kXqzIPfRmfOTnJX+m7a9K/pE6RU9aPfr8mQdokEcQtlJkEjc6QN9HKfy/CLnJ5Id
 Le8jQODSZ1zRsP6Z8jyG4unj0AuOucUoXjAKQ5EWK5RoRoLMirxqDEDd9tBjcPYB
 JQmB/j7aTrF3aDWBs5ragCQYdcoXJbAbqLAwhaofyVRmVyjYJmWEIkPGGo946GPd
 /BFgaUaea4qW5+iIpWFTD9TCQEY/A7RRpT4teu7anZ/hDzLUyXLJU28xYC6LxiDZ
 Yoy5M/U6MLvgkBVTNuss4T3CIutBrUI7a/DLuGB+cSM6KkigQvNwLuBqPzTDfEQP
 sQJOP4UsX6k=
 =8amc
 -----END PGP SIGNATURE-----

Merge tag 'for-upstream' of https://repo.or.cz/qemu/kevin into staging

Block layer patches

- Discard alignment fixes
- Remove unused callback .bdrv_aio_pdiscard()
- qemu-img bench: Input validation fix

# -----BEGIN PGP SIGNATURE-----
#
# iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmgLy7QRHGt3b2xmQHJl
# ZGhhdC5jb20ACgkQfwmycsiPL9YB4A//Zsbb+tVsyLBKeffwPpHF/cAzHVH7Q2dV
# GC2JJvfrwq0gykfjj+u4akVQnPh49QiQM623PX7O15IikwdLy45ddQcYL1qflYCs
# ZGmBOuz/deI74qjl67bZVqIm8WeRhwHkdutfXOL7GRe2IHbceLbwwGUcbCgOVavt
# LHu3E2MIbvkLJoHEgg8UbJhZZY9DTLGDaMt00Yhy3UvNHU8UDeIr8o4dxMVv3gOf
# +8kIjGQkYNqpWp7aCxy8vofdSFjbBp4lSCK4G83xikUw49qkwWcgZ6jyTzXALg0G
# V+nMjH+DnfIRqhi1skFTHQNmFc6upxr7FIOgC+G5amkKLHCPnX9j5/2pBwrk63R7
# kXqzIPfRmfOTnJX+m7a9K/pE6RU9aPfr8mQdokEcQtlJkEjc6QN9HKfy/CLnJ5Id
# Le8jQODSZ1zRsP6Z8jyG4unj0AuOucUoXjAKQ5EWK5RoRoLMirxqDEDd9tBjcPYB
# JQmB/j7aTrF3aDWBs5ragCQYdcoXJbAbqLAwhaofyVRmVyjYJmWEIkPGGo946GPd
# /BFgaUaea4qW5+iIpWFTD9TCQEY/A7RRpT4teu7anZ/hDzLUyXLJU28xYC6LxiDZ
# Yoy5M/U6MLvgkBVTNuss4T3CIutBrUI7a/DLuGB+cSM6KkigQvNwLuBqPzTDfEQP
# sQJOP4UsX6k=
# =8amc
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 25 Apr 2025 13:51:48 EDT
# gpg:                using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6
# gpg:                issuer "kwolf@redhat.com"
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full]
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* tag 'for-upstream' of https://repo.or.cz/qemu/kevin:
  qemu-img: improve queue depth validation in img_bench
  block: Remove unused callback function *bdrv_aio_pdiscard
  block/io: skip head/tail requests on EINVAL
  file-posix: probe discard alignment on Linux block devices

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2025-04-27 12:47:23 -04:00
commit 73d29ea241
4 changed files with 79 additions and 29 deletions

View File

@ -1276,10 +1276,10 @@ static int get_sysfs_zoned_model(struct stat *st, BlockZoneModel *zoned)
}
#endif /* defined(CONFIG_BLKZONED) */
#ifdef CONFIG_LINUX
/*
* Get a sysfs attribute value as a long integer.
*/
#ifdef CONFIG_LINUX
static long get_sysfs_long_val(struct stat *st, const char *attribute)
{
g_autofree char *str = NULL;
@ -1299,6 +1299,30 @@ static long get_sysfs_long_val(struct stat *st, const char *attribute)
}
return ret;
}
/*
* Get a sysfs attribute value as a uint32_t.
*/
static int get_sysfs_u32_val(struct stat *st, const char *attribute,
uint32_t *u32)
{
g_autofree char *str = NULL;
const char *end;
unsigned int val;
int ret;
ret = get_sysfs_str_val(st, attribute, &str);
if (ret < 0) {
return ret;
}
/* The file is ended with '\n', pass 'end' to accept that. */
ret = qemu_strtoui(str, &end, 10, &val);
if (ret == 0 && end && *end == '\0') {
*u32 = val;
}
return ret;
}
#endif
static int hdev_get_max_segments(int fd, struct stat *st)
@ -1318,6 +1342,23 @@ static int hdev_get_max_segments(int fd, struct stat *st)
#endif
}
/*
* Fills in *dalign with the discard alignment and returns 0 on success,
* -errno otherwise.
*/
static int hdev_get_pdiscard_alignment(struct stat *st, uint32_t *dalign)
{
#ifdef CONFIG_LINUX
/*
* Note that Linux "discard_granularity" is QEMU "discard_alignment". Linux
* "discard_alignment" is something else.
*/
return get_sysfs_u32_val(st, "discard_granularity", dalign);
#else
return -ENOTSUP;
#endif
}
#if defined(CONFIG_BLKZONED)
/*
* If the reset_all flag is true, then the wps of zone whose state is
@ -1527,6 +1568,30 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
}
}
if (S_ISBLK(st.st_mode)) {
uint32_t dalign = 0;
int ret;
ret = hdev_get_pdiscard_alignment(&st, &dalign);
if (ret == 0) {
uint32_t ralign = bs->bl.request_alignment;
/* Probably never happens, but handle it just in case */
if (dalign < ralign && (ralign % dalign == 0)) {
dalign = ralign;
}
/* The block layer requires a multiple of request_alignment */
if (dalign % ralign != 0) {
error_setg(errp, "Invalid pdiscard_alignment limit %u is not a "
"multiple of request_alignment %u", dalign, ralign);
return;
}
bs->bl.pdiscard_alignment = dalign;
}
}
raw_refresh_zoned_limits(bs, &st, errp);
}

View File

@ -3102,18 +3102,19 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset,
return 0;
}
if (!bs->drv->bdrv_co_pdiscard && !bs->drv->bdrv_aio_pdiscard) {
if (!bs->drv->bdrv_co_pdiscard) {
return 0;
}
/* Invalidate the cached block-status data range if this discard overlaps */
bdrv_bsc_invalidate_range(bs, offset, bytes);
/* Discard is advisory, but some devices track and coalesce
/*
* Discard is advisory, but some devices track and coalesce
* unaligned requests, so we must pass everything down rather than
* round here. Still, most devices will just silently ignore
* unaligned requests (by returning -ENOTSUP), so we must fragment
* the request accordingly. */
* round here. Still, most devices reject unaligned requests with
* -EINVAL or -ENOTSUP, so we must fragment the request accordingly.
*/
align = MAX(bs->bl.pdiscard_alignment, bs->bl.request_alignment);
assert(align % bs->bl.request_alignment == 0);
head = offset % align;
@ -3161,26 +3162,14 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset,
ret = -ENOMEDIUM;
goto out;
}
if (bs->drv->bdrv_co_pdiscard) {
ret = bs->drv->bdrv_co_pdiscard(bs, offset, num);
} else {
BlockAIOCB *acb;
CoroutineIOCompletion co = {
.coroutine = qemu_coroutine_self(),
};
acb = bs->drv->bdrv_aio_pdiscard(bs, offset, num,
bdrv_co_io_em_complete, &co);
if (acb == NULL) {
ret = -EIO;
goto out;
} else {
qemu_coroutine_yield();
ret = co.ret;
}
}
ret = bs->drv->bdrv_co_pdiscard(bs, offset, num);
if (ret && ret != -ENOTSUP) {
goto out;
if (ret == -EINVAL && (offset % align != 0 || num % align != 0)) {
/* Silently skip rejected unaligned head/tail requests */
} else {
goto out; /* bail out */
}
}
offset += num;

View File

@ -506,10 +506,6 @@ struct BlockDriver {
BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_flush)(
BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque);
BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_pdiscard)(
BlockDriverState *bs, int64_t offset, int bytes,
BlockCompletionFunc *cb, void *opaque);
int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_readv)(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);

View File

@ -4571,7 +4571,7 @@ static int img_bench(int argc, char **argv)
{
unsigned long res;
if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) {
if (qemu_strtoul(optarg, NULL, 0, &res) <= 0 || res > INT_MAX) {
error_report("Invalid queue depth specified");
return 1;
}