diff --git a/block.c b/block.c index 4f1581cedf..48a17f393c 100644 --- a/block.c +++ b/block.c @@ -606,12 +606,13 @@ create_file_fallback_zero_first_sector(BlockBackend *blk, int64_t current_size, Error **errp) { + uint32_t alignment = blk_get_pwrite_zeroes_alignment(blk); int64_t bytes_to_clear; int ret; GLOBAL_STATE_CODE(); - bytes_to_clear = MIN(current_size, BDRV_SECTOR_SIZE); + bytes_to_clear = MIN(current_size, MAX(BDRV_SECTOR_SIZE, alignment)); if (bytes_to_clear) { ret = blk_co_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP); if (ret < 0) { diff --git a/block/block-backend.c b/block/block-backend.c index f8d6ba65c1..98315d4470 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1318,9 +1318,9 @@ static void coroutine_fn blk_wait_while_drained(BlockBackend *blk) * section. */ qemu_mutex_lock(&blk->queued_requests_lock); + /* blk_root_drained_end() has the corresponding blk_inc_in_flight() */ blk_dec_in_flight(blk); qemu_co_queue_wait(&blk->queued_requests, &blk->queued_requests_lock); - blk_inc_in_flight(blk); qemu_mutex_unlock(&blk->queued_requests_lock); } } @@ -2305,6 +2305,17 @@ uint32_t blk_get_request_alignment(BlockBackend *blk) return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE; } +/* Returns the optimal write zeroes alignment, in bytes; guaranteed nonzero */ +uint32_t blk_get_pwrite_zeroes_alignment(BlockBackend *blk) +{ + BlockDriverState *bs = blk_bs(blk); + IO_CODE(); + if (!bs) { + return BDRV_SECTOR_SIZE; + } + return bs->bl.pwrite_zeroes_alignment ?: bs->bl.request_alignment; +} + /* Returns the maximum hardware transfer length, in bytes; guaranteed nonzero */ uint64_t blk_get_max_hw_transfer(BlockBackend *blk) { @@ -2767,9 +2778,11 @@ static void blk_root_drained_end(BdrvChild *child) blk->dev_ops->drained_end(blk->dev_opaque); } qemu_mutex_lock(&blk->queued_requests_lock); - while (qemu_co_enter_next(&blk->queued_requests, - &blk->queued_requests_lock)) { + while (!qemu_co_queue_empty(&blk->queued_requests)) { /* Resume all queued requests */ + blk_inc_in_flight(blk); + qemu_co_enter_next(&blk->queued_requests, + &blk->queued_requests_lock); } qemu_mutex_unlock(&blk->queued_requests_lock); } diff --git a/block/file-posix.c b/block/file-posix.c index 12d12970fa..c9e367a222 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1611,6 +1611,22 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.pdiscard_alignment = dalign; } + +#ifdef __linux__ + /* + * Linux requires logical block size alignment for write zeroes even + * when normal reads/writes do not require alignment. + */ + if (!s->needs_alignment) { + ret = probe_logical_blocksize(s->fd, + &bs->bl.pwrite_zeroes_alignment); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to probe logical block size"); + return; + } + } +#endif /* __linux__ */ } raw_refresh_zoned_limits(bs, &st, errp); diff --git a/include/system/block-backend-io.h b/include/system/block-backend-io.h index ba8dfcc7d0..6d5ac476fc 100644 --- a/include/system/block-backend-io.h +++ b/include/system/block-backend-io.h @@ -116,6 +116,7 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, void *opaque, int ret); uint32_t blk_get_request_alignment(BlockBackend *blk); +uint32_t blk_get_pwrite_zeroes_alignment(BlockBackend *blk); uint32_t blk_get_max_transfer(BlockBackend *blk); uint64_t blk_get_max_hw_transfer(BlockBackend *blk); diff --git a/tests/qemu-iotests/tests/loop-create-file b/tests/qemu-iotests/tests/loop-create-file new file mode 100755 index 0000000000..5ec75b046b --- /dev/null +++ b/tests/qemu-iotests/tests/loop-create-file @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# group: quick +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright Red Hat, Inc. +# +# Test Linux loop device image creation +# +# This test verifies #3127 "qemu-img create fails on loop device with sector size 4096" +# https://gitlab.com/qemu-project/qemu/-/issues/3127 + +seq="$(basename $0)" +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() { + if [ -n "$loopdev" ]; then + sudo losetup --detach "$loopdev" + fi + + _cleanup_test_img +} + +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter + +_supported_fmt raw +_supported_proto file +_supported_os Linux + +if ! sudo -n losetup &>/dev/null; then + _notrun "sudo losetup not available" +fi + +echo +echo "=== Create image on a 4 KB sector size loop device ===" +echo + +_make_test_img -f $IMGFMT 1M + +loopdev=$(sudo losetup --sector-size 4096 --find --show "$TEST_IMG") +if [ -z "$loopdev" ]; then + _fail +fi + +sudo $QEMU_IMG_PROG create -f raw "$loopdev" 1M | \ + sed -e "s#/dev/loop[0-9]\\+#LOOPDEV#g" + +# success, all done +echo +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/loop-create-file.out b/tests/qemu-iotests/tests/loop-create-file.out new file mode 100644 index 0000000000..32d4155695 --- /dev/null +++ b/tests/qemu-iotests/tests/loop-create-file.out @@ -0,0 +1,8 @@ +QA output created by loop-create-file + +=== Create image on a 4 KB sector size loop device === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +Formatting 'LOOPDEV', fmt=raw size=1048576 + +*** done