aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block/file-posix.c67
-rw-r--r--block/io.c35
-rw-r--r--include/block/block_int-common.h4
-rw-r--r--qemu-img.c2
4 files changed, 79 insertions, 29 deletions
diff --git a/block/file-posix.c b/block/file-posix.c
index 56d1972..0d6e12f 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -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);
}
diff --git a/block/io.c b/block/io.c
index 1ba8d1a..6d98b0a 100644
--- a/block/io.c
+++ b/block/io.c
@@ -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,27 +3162,15 @@ 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;
+
+ ret = bs->drv->bdrv_co_pdiscard(bs, offset, num);
+ if (ret && ret != -ENOTSUP) {
+ if (ret == -EINVAL && (offset % align != 0 || num % align != 0)) {
+ /* Silently skip rejected unaligned head/tail requests */
} else {
- qemu_coroutine_yield();
- ret = co.ret;
+ goto out; /* bail out */
}
}
- if (ret && ret != -ENOTSUP) {
- goto out;
- }
offset += num;
bytes -= num;
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
index ebb4e56..0d8187f 100644
--- a/include/block/block_int-common.h
+++ b/include/block/block_int-common.h
@@ -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);
diff --git a/qemu-img.c b/qemu-img.c
index 2044c22..76ac5d3 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -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;
}