Skip to content

Commit c302660

Browse files
committed
Merge tag 'for-upstream' of https://repo.or.cz/qemu/kevin into staging
Block layer patches - scsi-disk: Apply error policy for host_status errors again - qcow2: Fix qemu-img info crash with missing crypto header - qemu-img bench: Fix division by zero for zero-sized images - test-bdrv-drain: Fix data races # -----BEGIN PGP SIGNATURE----- # # iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmf1HdQRHGt3b2xmQHJl # ZGhhdC5jb20ACgkQfwmycsiPL9Z9QxAAlKjkXt5mshcMPPNAIFkBarvF318T8azh # 5A4soABMpgZBceXaadWMEkBiYGW7jvoBwRVivVNB7jLfar3jchfW8xEAerLXMpAE # O6n6vwXQz5fy1w5VqJuA/lA/5ZGdt8P7NvvOGcd00GySo6df2lOBtCbDjtwT5t6a # 0w6b5d/qSIsfm7wEIh7Vh8HjQ88WoOXSti9xQppyd48onNRT+6p2XtyXD75EeZi+ # uYS/NNwViNVRD2df3q4Thi3Q9AMhlDn8yZUqgMpwupbZcXNgjdfMNMPUUmRTNDrO # 33byZu+nrrq+Qz5xTSekD9anV4M1yJ+aWYxL7BI2RP87u4OgcZuCgNcFHzZ2j9BJ # xrV0wPdh1xdY8kn/5+X27/gC5cjb5AYoiA4SGZJsZpcvYnBz/jRIMoUY9HVc1Y+N # hW/endbNTpQYlEzmTb6RRccV7gTsD8V+Dc5TOg/RLgpdxahiZg0JAxT4sUkb52Ij # CH5kPRkEsluSXf86qFyDitMlE/SCl4bL9xoHnydgeaMJovMRAT6I/UpUdLkgsacL # ul6snvKPRXXP6PnM8hKHJmZwzKyzJVaVnQSG4TefNQTLIro3ZgVKzUek4dmpIHmg # hn9GOqENeS3soKg1vyniWEsNdg/t6YvEfFutJk5LJVRb5F18sht9IIYWNJKdWxuV # S7S3kAlMXow= # =Dv5w # -----END PGP SIGNATURE----- # gpg: Signature made Tue 08 Apr 2025 09:00:04 EDT # gpg: using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6 # gpg: issuer "[email protected]" # gpg: Good signature from "Kevin Wolf <[email protected]>" [full] # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * tag 'for-upstream' of https://repo.or.cz/qemu/kevin: test-bdrv-drain: Fix data races scsi-disk: Apply error policy for host_status errors again qcow2: Don't crash qemu-img info with missing crypto header qemu-img: fix division by zero in bench_cb() for zero-sized images Signed-off-by: Stefan Hajnoczi <[email protected]>
2 parents 70ff69a + f8222bf commit c302660

File tree

8 files changed

+167
-30
lines changed

8 files changed

+167
-30
lines changed

block/qcow2.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,7 +1721,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
17211721
ret = -EINVAL;
17221722
goto fail;
17231723
}
1724-
} else if (!(flags & BDRV_O_NO_IO)) {
1724+
} else {
17251725
error_setg(errp, "Missing CRYPTO header for crypt method %d",
17261726
s->crypt_method_header);
17271727
ret = -EINVAL;
@@ -1976,7 +1976,7 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
19761976
{
19771977
BDRVQcow2State *s = bs->opaque;
19781978

1979-
if (bs->encrypted) {
1979+
if (s->crypto) {
19801980
/* Encryption works on a sector granularity */
19811981
bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto);
19821982
}

hw/scsi/scsi-disk.c

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,9 @@ struct SCSIDiskClass {
6868
SCSIDeviceClass parent_class;
6969
/*
7070
* Callbacks receive ret == 0 for success. Errors are represented either as
71-
* negative errno values, or as positive SAM status codes.
72-
*
73-
* Beware: For errors returned in host_status, the function may directly
74-
* complete the request and never call the callback.
71+
* negative errno values, or as positive SAM status codes. For host_status
72+
* errors, the function passes ret == -ENODEV and sets the host_status field
73+
* of the SCSIRequest.
7574
*/
7675
DMAIOFunc *dma_readv;
7776
DMAIOFunc *dma_writev;
@@ -225,11 +224,26 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed)
225224
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
226225
SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s));
227226
SCSISense sense = SENSE_CODE(NO_SENSE);
227+
int16_t host_status;
228228
int error;
229229
bool req_has_sense = false;
230230
BlockErrorAction action;
231231
int status;
232232

233+
/*
234+
* host_status should only be set for SG_IO requests that came back with a
235+
* host_status error in scsi_block_sgio_complete(). This error path passes
236+
* -ENODEV as the return value.
237+
*
238+
* Reset host_status in the request because we may still want to complete
239+
* the request successfully with the 'stop' or 'ignore' error policy.
240+
*/
241+
host_status = r->req.host_status;
242+
if (host_status != -1) {
243+
assert(ret == -ENODEV);
244+
r->req.host_status = -1;
245+
}
246+
233247
if (ret < 0) {
234248
status = scsi_sense_from_errno(-ret, &sense);
235249
error = -ret;
@@ -289,6 +303,10 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed)
289303
if (acct_failed) {
290304
block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
291305
}
306+
if (host_status != -1) {
307+
scsi_req_complete_failed(&r->req, host_status);
308+
return true;
309+
}
292310
if (req_has_sense) {
293311
sdc->update_sense(&r->req);
294312
} else if (status == CHECK_CONDITION) {
@@ -409,7 +427,6 @@ static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret)
409427
scsi_req_unref(&r->req);
410428
}
411429

412-
/* May not be called in all error cases, don't rely on cleanup here */
413430
static void scsi_dma_complete(void *opaque, int ret)
414431
{
415432
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
@@ -448,7 +465,6 @@ static void scsi_read_complete_noio(SCSIDiskReq *r, int ret)
448465
scsi_req_unref(&r->req);
449466
}
450467

451-
/* May not be called in all error cases, don't rely on cleanup here */
452468
static void scsi_read_complete(void *opaque, int ret)
453469
{
454470
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
@@ -585,7 +601,6 @@ static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)
585601
scsi_req_unref(&r->req);
586602
}
587603

588-
/* May not be called in all error cases, don't rely on cleanup here */
589604
static void scsi_write_complete(void * opaque, int ret)
590605
{
591606
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
@@ -2846,14 +2861,10 @@ static void scsi_block_sgio_complete(void *opaque, int ret)
28462861
sg_io_hdr_t *io_hdr = &req->io_header;
28472862

28482863
if (ret == 0) {
2849-
/* FIXME This skips calling req->cb() and any cleanup in it */
28502864
if (io_hdr->host_status != SCSI_HOST_OK) {
2851-
scsi_req_complete_failed(&r->req, io_hdr->host_status);
2852-
scsi_req_unref(&r->req);
2853-
return;
2854-
}
2855-
2856-
if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) {
2865+
r->req.host_status = io_hdr->host_status;
2866+
ret = -ENODEV;
2867+
} else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) {
28572868
ret = BUSY;
28582869
} else {
28592870
ret = io_hdr->status;

include/qemu/job.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,9 @@ bool job_is_ready(Job *job);
545545
/* Same as job_is_ready(), but called with job lock held. */
546546
bool job_is_ready_locked(Job *job);
547547

548+
/** Returns whether the job is paused. Called with job_mutex *not* held. */
549+
bool job_is_paused(Job *job);
550+
548551
/**
549552
* Request @job to pause at the next pause point. Must be paired with
550553
* job_resume(). If the job is supposed to be resumed by user action, call

job.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,12 @@ bool job_is_cancelled_locked(Job *job)
251251
return job->force_cancel;
252252
}
253253

254+
bool job_is_paused(Job *job)
255+
{
256+
JOB_LOCK_GUARD();
257+
return job->paused;
258+
}
259+
254260
bool job_is_cancelled(Job *job)
255261
{
256262
JOB_LOCK_GUARD();

qemu-img.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4488,7 +4488,11 @@ static void bench_cb(void *opaque, int ret)
44884488
*/
44894489
b->in_flight++;
44904490
b->offset += b->step;
4491-
b->offset %= b->image_size;
4491+
if (b->image_size == 0) {
4492+
b->offset = 0;
4493+
} else {
4494+
b->offset %= b->image_size;
4495+
}
44924496
if (b->write) {
44934497
acb = blk_aio_pwritev(b->blk, offset, b->qiov, 0, bench_cb, b);
44944498
} else {
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/usr/bin/env bash
2+
# group: rw quick
3+
#
4+
# Test case for encryption support in qcow2
5+
#
6+
# Copyright (C) 2025 Red Hat, Inc.
7+
#
8+
# This program is free software; you can redistribute it and/or modify
9+
# it under the terms of the GNU General Public License as published by
10+
# the Free Software Foundation; either version 2 of the License, or
11+
# (at your option) any later version.
12+
#
13+
# This program is distributed in the hope that it will be useful,
14+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
# GNU General Public License for more details.
17+
#
18+
# You should have received a copy of the GNU General Public License
19+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
#
21+
22+
# creator
23+
24+
25+
seq="$(basename $0)"
26+
echo "QA output created by $seq"
27+
28+
status=1 # failure is the default!
29+
30+
_cleanup()
31+
{
32+
_cleanup_test_img
33+
}
34+
trap "_cleanup; exit \$status" 0 1 2 3 15
35+
36+
# get standard environment, filters and checks
37+
. ../common.rc
38+
. ../common.filter
39+
40+
# This tests qcow2-specific low-level functionality
41+
_supported_fmt qcow2
42+
_supported_proto file
43+
_require_working_luks
44+
45+
IMG_SIZE=64M
46+
47+
echo
48+
echo "=== Create an encrypted image ==="
49+
echo
50+
51+
_make_test_img --object secret,id=sec0,data=123456 -o encrypt.format=luks,encrypt.key-secret=sec0 $IMG_SIZE
52+
$PYTHON ../qcow2.py "$TEST_IMG" dump-header-exts
53+
_img_info
54+
$QEMU_IMG check \
55+
--object secret,id=sec0,data=123456 \
56+
--image-opts file.filename="$TEST_IMG",encrypt.key-secret=sec0 \
57+
| _filter_qemu_img_check
58+
59+
echo
60+
echo "=== Remove the header extension ==="
61+
echo
62+
63+
$PYTHON ../qcow2.py "$TEST_IMG" del-header-ext 0x0537be77
64+
$PYTHON ../qcow2.py "$TEST_IMG" dump-header-exts
65+
_img_info
66+
$QEMU_IMG check \
67+
--object secret,id=sec0,data=123456 \
68+
--image-opts file.filename="$TEST_IMG",encrypt.key-secret=sec0 2>&1 \
69+
| _filter_qemu_img_check \
70+
| _filter_testdir
71+
72+
# success, all done
73+
echo "*** done"
74+
rm -f $seq.full
75+
status=0
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
QA output created by qcow2-encryption
2+
3+
=== Create an encrypted image ===
4+
5+
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
6+
Header extension:
7+
magic 0x537be77 (Crypto header)
8+
length 16
9+
data <binary>
10+
11+
Header extension:
12+
magic 0x6803f857 (Feature table)
13+
length 384
14+
data <binary>
15+
16+
image: TEST_DIR/t.IMGFMT
17+
file format: IMGFMT
18+
virtual size: 64 MiB (67108864 bytes)
19+
encrypted: yes
20+
cluster_size: 65536
21+
No errors were found on the image.
22+
23+
=== Remove the header extension ===
24+
25+
Header extension:
26+
magic 0x6803f857 (Feature table)
27+
length 384
28+
data <binary>
29+
30+
qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Missing CRYPTO header for crypt method 2
31+
qemu-img: Could not open 'file.filename=TEST_DIR/t.qcow2,encrypt.key-secret=sec0': Missing CRYPTO header for crypt method 2
32+
*** done

tests/unit/test-bdrv-drain.c

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,8 @@ typedef struct TestBlockJob {
632632
BlockDriverState *bs;
633633
int run_ret;
634634
int prepare_ret;
635+
636+
/* Accessed with atomics */
635637
bool running;
636638
bool should_complete;
637639
} TestBlockJob;
@@ -667,10 +669,10 @@ static int coroutine_fn test_job_run(Job *job, Error **errp)
667669

668670
/* We are running the actual job code past the pause point in
669671
* job_co_entry(). */
670-
s->running = true;
672+
qatomic_set(&s->running, true);
671673

672674
job_transition_to_ready(&s->common.job);
673-
while (!s->should_complete) {
675+
while (!qatomic_read(&s->should_complete)) {
674676
/* Avoid job_sleep_ns() because it marks the job as !busy. We want to
675677
* emulate some actual activity (probably some I/O) here so that drain
676678
* has to wait for this activity to stop. */
@@ -685,7 +687,7 @@ static int coroutine_fn test_job_run(Job *job, Error **errp)
685687
static void test_job_complete(Job *job, Error **errp)
686688
{
687689
TestBlockJob *s = container_of(job, TestBlockJob, common.job);
688-
s->should_complete = true;
690+
qatomic_set(&s->should_complete, true);
689691
}
690692

691693
BlockJobDriver test_job_driver = {
@@ -791,15 +793,15 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type,
791793
/* job_co_entry() is run in the I/O thread, wait for the actual job
792794
* code to start (we don't want to catch the job in the pause point in
793795
* job_co_entry(). */
794-
while (!tjob->running) {
796+
while (!qatomic_read(&tjob->running)) {
795797
aio_poll(qemu_get_aio_context(), false);
796798
}
797799
}
798800

799801
WITH_JOB_LOCK_GUARD() {
800802
g_assert_cmpint(job->job.pause_count, ==, 0);
801803
g_assert_false(job->job.paused);
802-
g_assert_true(tjob->running);
804+
g_assert_true(qatomic_read(&tjob->running));
803805
g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */
804806
}
805807

@@ -825,7 +827,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type,
825827
*
826828
* paused is reset in the I/O thread, wait for it
827829
*/
828-
while (job->job.paused) {
830+
while (job_is_paused(&job->job)) {
829831
aio_poll(qemu_get_aio_context(), false);
830832
}
831833
}
@@ -858,7 +860,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type,
858860
*
859861
* paused is reset in the I/O thread, wait for it
860862
*/
861-
while (job->job.paused) {
863+
while (job_is_paused(&job->job)) {
862864
aio_poll(qemu_get_aio_context(), false);
863865
}
864866
}
@@ -1411,18 +1413,20 @@ static void test_set_aio_context(void)
14111413

14121414
typedef struct TestDropBackingBlockJob {
14131415
BlockJob common;
1414-
bool should_complete;
14151416
bool *did_complete;
14161417
BlockDriverState *detach_also;
14171418
BlockDriverState *bs;
1419+
1420+
/* Accessed with atomics */
1421+
bool should_complete;
14181422
} TestDropBackingBlockJob;
14191423

14201424
static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp)
14211425
{
14221426
TestDropBackingBlockJob *s =
14231427
container_of(job, TestDropBackingBlockJob, common.job);
14241428

1425-
while (!s->should_complete) {
1429+
while (!qatomic_read(&s->should_complete)) {
14261430
job_sleep_ns(job, 0);
14271431
}
14281432

@@ -1541,7 +1545,7 @@ static void test_blockjob_commit_by_drained_end(void)
15411545

15421546
job_start(&job->common.job);
15431547

1544-
job->should_complete = true;
1548+
qatomic_set(&job->should_complete, true);
15451549
bdrv_drained_begin(bs_child);
15461550
g_assert(!job_has_completed);
15471551
bdrv_drained_end(bs_child);
@@ -1557,15 +1561,17 @@ static void test_blockjob_commit_by_drained_end(void)
15571561

15581562
typedef struct TestSimpleBlockJob {
15591563
BlockJob common;
1560-
bool should_complete;
15611564
bool *did_complete;
1565+
1566+
/* Accessed with atomics */
1567+
bool should_complete;
15621568
} TestSimpleBlockJob;
15631569

15641570
static int coroutine_fn test_simple_job_run(Job *job, Error **errp)
15651571
{
15661572
TestSimpleBlockJob *s = container_of(job, TestSimpleBlockJob, common.job);
15671573

1568-
while (!s->should_complete) {
1574+
while (!qatomic_read(&s->should_complete)) {
15691575
job_sleep_ns(job, 0);
15701576
}
15711577

@@ -1700,7 +1706,7 @@ static void test_drop_intermediate_poll(void)
17001706
job->did_complete = &job_has_completed;
17011707

17021708
job_start(&job->common.job);
1703-
job->should_complete = true;
1709+
qatomic_set(&job->should_complete, true);
17041710

17051711
g_assert(!job_has_completed);
17061712
ret = bdrv_drop_intermediate(chain[1], chain[0], NULL, false);

0 commit comments

Comments
 (0)