1 | The following changes since commit 19b599f7664b2ebfd0f405fb79c14dd241557452: | 1 | The following changes since commit 2d2c73d0e3d504a61f868e46e6abd5643f38091b: |
---|---|---|---|
2 | 2 | ||
3 | Merge remote-tracking branch 'remotes/armbru/tags/pull-error-2018-08-27-v2' into staging (2018-08-27 16:44:20 +0100) | 3 | Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20200914-1' into staging (2020-09-14 16:03:08 +0100) |
4 | 4 | ||
5 | are available in the Git repository at: | 5 | are available in the Git repository at: |
6 | 6 | ||
7 | https://git.xanclic.moe/XanClic/qemu.git tags/pull-block-2018-08-31 | 7 | https://github.com/XanClic/qemu.git tags/pull-block-2020-09-15 |
8 | 8 | ||
9 | for you to fetch changes up to 40954cc7831c4f95f9ce6402ae3d6761f44f31ff: | 9 | for you to fetch changes up to 7bae7c805d82675eb3a02c744093703d84ada2d6: |
10 | 10 | ||
11 | jobs: remove job_defer_to_main_loop (2018-08-31 16:11:27 +0200) | 11 | block/rbd: add 'namespace' to qemu_rbd_strong_runtime_opts[] (2020-09-15 11:31:10 +0200) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Block patches: | 14 | Block patches: |
15 | - (Block) job exit refactoring, part 1 | 15 | - Several qcow2 fixes and refactorings |
16 | (removing job_defer_to_main_loop()) | 16 | - Let qemu-img convert try to stay at cluster boundaries |
17 | - Locking fix for the file-posix block driver | 17 | - Stable child names for quorum (with x-blockdev-change) |
18 | - test-bdrv-drain leak fix | 18 | - Explicitly drop vhdx 4k sector support, as it was never actually |
19 | working | ||
20 | - rbd: Mark @namespace a strong runtime option | ||
21 | - iotests.py improvements | ||
22 | - Drop unused runtime_opts objects | ||
23 | - Skip a test case in 030 when run through make check-block | ||
19 | 24 | ||
20 | ---------------------------------------------------------------- | 25 | ---------------------------------------------------------------- |
21 | Fam Zheng (1): | 26 | Alberto Garcia (9): |
22 | file-posix: Skip effectiveless OFD lock operations | 27 | qcow2: Use macros for the L1, refcount and bitmap table entry sizes |
28 | qcow2: Fix removal of list members from BDRVQcow2State.cluster_allocs | ||
29 | qcow2: Don't check nb_clusters when removing l2meta from the list | ||
30 | qcow2: Rewrite the documentation of qcow2_alloc_cluster_offset() | ||
31 | qcow2: Handle QCowL2Meta on error in preallocate_co() | ||
32 | qcow2: Make qcow2_free_any_clusters() free only one cluster | ||
33 | qcow2: Return the original error code in qcow2_co_pwrite_zeroes() | ||
34 | qcow2: Make preallocate_co() resize the image to the correct size | ||
35 | qcow2: Convert qcow2_alloc_cluster_offset() into | ||
36 | qcow2_alloc_host_offset() | ||
23 | 37 | ||
24 | John Snow (9): | 38 | John Snow (2): |
25 | jobs: change start callback to run callback | 39 | block/rbd: remove runtime_opts |
26 | jobs: canonize Error object | 40 | block/qcow: remove runtime opts |
27 | jobs: add exit shim | ||
28 | block/commit: utilize job_exit shim | ||
29 | block/mirror: utilize job_exit shim | ||
30 | jobs: utilize job_exit shim | ||
31 | block/backup: make function variables consistently named | ||
32 | jobs: remove ret argument to job_completed; privatize it | ||
33 | jobs: remove job_defer_to_main_loop | ||
34 | 41 | ||
35 | Marc-André Lureau (1): | 42 | Lukas Straub (1): |
36 | tests: fix bdrv-drain leak | 43 | block/quorum.c: stable children names |
37 | 44 | ||
38 | include/qemu/job.h | 70 ++++++++++++++++----------------- | 45 | Nir Soffer (5): |
39 | block/backup.c | 81 ++++++++++++++++----------------------- | 46 | qemu-iotests: Fix FilePaths cleanup |
40 | block/commit.c | 29 +++++--------- | 47 | qemu-iotests: Fix FilePaths docstring |
41 | block/create.c | 19 +++------ | 48 | qemu-iotests: Support varargs syntax in FilePaths |
42 | block/file-posix.c | 41 +++++++++++++++----- | 49 | qemu-iotests: Merge FilePaths and FilePath |
43 | block/mirror.c | 39 ++++++++----------- | 50 | qemu-iotests: Simplify FilePath __init__ |
44 | block/stream.c | 29 ++++++-------- | 51 | |
45 | job-qmp.c | 5 ++- | 52 | Peter Lieven (1): |
46 | job.c | 73 ++++++++++++----------------------- | 53 | qemu-img: avoid unaligned read requests during convert |
47 | tests/test-bdrv-drain.c | 14 +++---- | 54 | |
48 | tests/test-blockjob-txn.c | 25 +++++------- | 55 | Stefano Garzarella (1): |
49 | tests/test-blockjob.c | 17 ++++---- | 56 | block/rbd: add 'namespace' to qemu_rbd_strong_runtime_opts[] |
50 | trace-events | 2 +- | 57 | |
51 | 13 files changed, 192 insertions(+), 252 deletions(-) | 58 | Swapnil Ingle (1): |
59 | block/vhdx: Support vhdx image only with 512 bytes logical sector size | ||
60 | |||
61 | Thomas Huth (1): | ||
62 | iotests: Skip test_stream_parallel in test 030 when doing "make check" | ||
63 | |||
64 | Yi Li (1): | ||
65 | qemu-img: Explicit number replaced by a constant | ||
66 | |||
67 | block/qcow2.h | 16 +++-- | ||
68 | block/qcow.c | 9 --- | ||
69 | block/qcow2-bitmap.c | 11 ++-- | ||
70 | block/qcow2-cluster.c | 69 ++++++++++++---------- | ||
71 | block/qcow2-refcount.c | 97 +++++++++++++++--------------- | ||
72 | block/qcow2-snapshot.c | 20 +++---- | ||
73 | block/qcow2.c | 108 ++++++++++++++-------------------- | ||
74 | block/quorum.c | 20 +++++-- | ||
75 | block/rbd.c | 43 +------------- | ||
76 | block/vhdx.c | 6 +- | ||
77 | qemu-img.c | 32 ++++++++-- | ||
78 | tests/check-block.sh | 3 + | ||
79 | tests/qemu-iotests/030 | 2 + | ||
80 | tests/qemu-iotests/125 | 44 ++++++++------ | ||
81 | tests/qemu-iotests/125.out | 28 ++++++++- | ||
82 | tests/qemu-iotests/194 | 4 +- | ||
83 | tests/qemu-iotests/208 | 2 +- | ||
84 | tests/qemu-iotests/222 | 2 +- | ||
85 | tests/qemu-iotests/251 | 7 ++- | ||
86 | tests/qemu-iotests/257 | 10 ++-- | ||
87 | tests/qemu-iotests/305 | 74 +++++++++++++++++++++++ | ||
88 | tests/qemu-iotests/305.out | 16 +++++ | ||
89 | tests/qemu-iotests/group | 1 + | ||
90 | tests/qemu-iotests/iotests.py | 53 +++++++++-------- | ||
91 | 24 files changed, 395 insertions(+), 282 deletions(-) | ||
92 | create mode 100755 tests/qemu-iotests/305 | ||
93 | create mode 100644 tests/qemu-iotests/305.out | ||
52 | 94 | ||
53 | -- | 95 | -- |
54 | 2.17.1 | 96 | 2.26.2 |
55 | 97 | ||
56 | 98 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Nir Soffer <nirsof@gmail.com> | ||
1 | 2 | ||
3 | If os.remove() fails to remove one of the paths, for example if the file | ||
4 | was removed by the test, the cleanup loop would exit silently, without | ||
5 | removing the rest of the files. | ||
6 | |||
7 | Fixes: de263986b5dc | ||
8 | Signed-off-by: Nir Soffer <nsoffer@redhat.com> | ||
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
10 | Message-Id: <20200828232152.205833-2-nsoffer@redhat.com> | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | ||
13 | tests/qemu-iotests/iotests.py | 8 ++++---- | ||
14 | 1 file changed, 4 insertions(+), 4 deletions(-) | ||
15 | |||
16 | diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/tests/qemu-iotests/iotests.py | ||
19 | +++ b/tests/qemu-iotests/iotests.py | ||
20 | @@ -XXX,XX +XXX,XX @@ class FilePaths: | ||
21 | return self.paths | ||
22 | |||
23 | def __exit__(self, exc_type, exc_val, exc_tb): | ||
24 | - try: | ||
25 | - for path in self.paths: | ||
26 | + for path in self.paths: | ||
27 | + try: | ||
28 | os.remove(path) | ||
29 | - except OSError: | ||
30 | - pass | ||
31 | + except OSError: | ||
32 | + pass | ||
33 | return False | ||
34 | |||
35 | class FilePath(FilePaths): | ||
36 | -- | ||
37 | 2.26.2 | ||
38 | |||
39 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Nir Soffer <nirsof@gmail.com> | ||
1 | 2 | ||
3 | When this class was extracted from FilePath, the docstring was not | ||
4 | updated for generating multiple files, and the example usage was | ||
5 | referencing unrelated file. | ||
6 | |||
7 | While fixing the docstring, add example for creating sockets, which | ||
8 | should use iotests.sock_dir instead of the default base_dir. | ||
9 | |||
10 | Fixes: de263986b5dc | ||
11 | Signed-off-by: Nir Soffer <nsoffer@redhat.com> | ||
12 | Message-Id: <20200828232152.205833-3-nsoffer@redhat.com> | ||
13 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
14 | --- | ||
15 | tests/qemu-iotests/iotests.py | 19 +++++++++++++------ | ||
16 | 1 file changed, 13 insertions(+), 6 deletions(-) | ||
17 | |||
18 | diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py | ||
19 | index XXXXXXX..XXXXXXX 100644 | ||
20 | --- a/tests/qemu-iotests/iotests.py | ||
21 | +++ b/tests/qemu-iotests/iotests.py | ||
22 | @@ -XXX,XX +XXX,XX @@ def file_pattern(name): | ||
23 | |||
24 | class FilePaths: | ||
25 | """ | ||
26 | - FilePaths is an auto-generated filename that cleans itself up. | ||
27 | + Context manager generating multiple file names. The generated files are | ||
28 | + removed when exiting the context. | ||
29 | |||
30 | - Use this context manager to generate filenames and ensure that the file | ||
31 | - gets deleted:: | ||
32 | + Example usage: | ||
33 | + | ||
34 | + with FilePaths(['a.img', 'b.img']) as (img_a, img_b): | ||
35 | + # Use img_a and img_b here... | ||
36 | + | ||
37 | + # a.img and b.img are automatically removed here. | ||
38 | + | ||
39 | + By default images are created in iotests.test_dir. To create sockets use | ||
40 | + iotests.sock_dir: | ||
41 | + | ||
42 | + with FilePaths(['a.sock'], base_dir=iotests.sock_dir) as (sock,): | ||
43 | |||
44 | - with FilePaths(['test.img']) as img_path: | ||
45 | - qemu_img('create', img_path, '1G') | ||
46 | - # migration_sock_path is automatically deleted | ||
47 | """ | ||
48 | def __init__(self, names, base_dir=test_dir): | ||
49 | self.paths = [] | ||
50 | -- | ||
51 | 2.26.2 | ||
52 | |||
53 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Nir Soffer <nirsof@gmail.com> | ||
1 | 2 | ||
3 | Accept variable number of names instead of a sequence: | ||
4 | |||
5 | with FilePaths("a", "b", "c") as (a, b, c): | ||
6 | |||
7 | The disadvantage is that base_dir must be used as kwarg: | ||
8 | |||
9 | with FilePaths("a", "b", base_dir=soc_dir) as (sock1, sock2): | ||
10 | |||
11 | But this is more clear and calling optional argument as positional | ||
12 | arguments is bad idea anyway. | ||
13 | |||
14 | Signed-off-by: Nir Soffer <nsoffer@redhat.com> | ||
15 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
16 | Message-Id: <20200828232152.205833-4-nsoffer@redhat.com> | ||
17 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
18 | --- | ||
19 | tests/qemu-iotests/194 | 4 ++-- | ||
20 | tests/qemu-iotests/257 | 10 ++++------ | ||
21 | tests/qemu-iotests/iotests.py | 8 ++++---- | ||
22 | 3 files changed, 10 insertions(+), 12 deletions(-) | ||
23 | |||
24 | diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194 | ||
25 | index XXXXXXX..XXXXXXX 100755 | ||
26 | --- a/tests/qemu-iotests/194 | ||
27 | +++ b/tests/qemu-iotests/194 | ||
28 | @@ -XXX,XX +XXX,XX @@ iotests.script_initialize(supported_fmts=['qcow2', 'qed', 'raw'], | ||
29 | |||
30 | with iotests.FilePath('source.img') as source_img_path, \ | ||
31 | iotests.FilePath('dest.img') as dest_img_path, \ | ||
32 | - iotests.FilePaths(['migration.sock', 'nbd.sock'], iotests.sock_dir) as \ | ||
33 | - [migration_sock_path, nbd_sock_path], \ | ||
34 | + iotests.FilePaths('migration.sock', 'nbd.sock', base_dir=iotests.sock_dir) \ | ||
35 | + as (migration_sock_path, nbd_sock_path), \ | ||
36 | iotests.VM('source') as source_vm, \ | ||
37 | iotests.VM('dest') as dest_vm: | ||
38 | |||
39 | diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257 | ||
40 | index XXXXXXX..XXXXXXX 100755 | ||
41 | --- a/tests/qemu-iotests/257 | ||
42 | +++ b/tests/qemu-iotests/257 | ||
43 | @@ -XXX,XX +XXX,XX @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): | ||
44 | an incomplete backup. Testing limitations prevent | ||
45 | testing competing writes. | ||
46 | """ | ||
47 | - with iotests.FilePaths(['img', 'bsync1', 'bsync2', | ||
48 | - 'fbackup0', 'fbackup1', 'fbackup2']) as \ | ||
49 | - (img_path, bsync1, bsync2, | ||
50 | - fbackup0, fbackup1, fbackup2), \ | ||
51 | + with iotests.FilePaths( | ||
52 | + 'img', 'bsync1', 'bsync2', 'fbackup0', 'fbackup1', 'fbackup2') as \ | ||
53 | + (img_path, bsync1, bsync2, fbackup0, fbackup1, fbackup2), \ | ||
54 | iotests.VM() as vm: | ||
55 | |||
56 | mode = "Mode {:s}; Bitmap Sync {:s}".format(msync_mode, bsync_mode) | ||
57 | @@ -XXX,XX +XXX,XX @@ def test_backup_api(): | ||
58 | """ | ||
59 | Test malformed and prohibited invocations of the backup API. | ||
60 | """ | ||
61 | - with iotests.FilePaths(['img', 'bsync1']) as \ | ||
62 | - (img_path, backup_path), \ | ||
63 | + with iotests.FilePaths('img', 'bsync1') as (img_path, backup_path), \ | ||
64 | iotests.VM() as vm: | ||
65 | |||
66 | log("\n=== API failure tests ===\n") | ||
67 | diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py | ||
68 | index XXXXXXX..XXXXXXX 100644 | ||
69 | --- a/tests/qemu-iotests/iotests.py | ||
70 | +++ b/tests/qemu-iotests/iotests.py | ||
71 | @@ -XXX,XX +XXX,XX @@ class FilePaths: | ||
72 | |||
73 | Example usage: | ||
74 | |||
75 | - with FilePaths(['a.img', 'b.img']) as (img_a, img_b): | ||
76 | + with FilePaths('a.img', 'b.img') as (img_a, img_b): | ||
77 | # Use img_a and img_b here... | ||
78 | |||
79 | # a.img and b.img are automatically removed here. | ||
80 | @@ -XXX,XX +XXX,XX @@ class FilePaths: | ||
81 | By default images are created in iotests.test_dir. To create sockets use | ||
82 | iotests.sock_dir: | ||
83 | |||
84 | - with FilePaths(['a.sock'], base_dir=iotests.sock_dir) as (sock,): | ||
85 | + with FilePaths('a.sock', base_dir=iotests.sock_dir) as (sock,): | ||
86 | |||
87 | """ | ||
88 | - def __init__(self, names, base_dir=test_dir): | ||
89 | + def __init__(self, *names, base_dir=test_dir): | ||
90 | self.paths = [] | ||
91 | for name in names: | ||
92 | self.paths.append(os.path.join(base_dir, file_pattern(name))) | ||
93 | @@ -XXX,XX +XXX,XX @@ class FilePath(FilePaths): | ||
94 | FilePath is a specialization of FilePaths that takes a single filename. | ||
95 | """ | ||
96 | def __init__(self, name, base_dir=test_dir): | ||
97 | - super(FilePath, self).__init__([name], base_dir) | ||
98 | + super(FilePath, self).__init__(name, base_dir=base_dir) | ||
99 | |||
100 | def __enter__(self): | ||
101 | return self.paths[0] | ||
102 | -- | ||
103 | 2.26.2 | ||
104 | |||
105 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Nir Soffer <nirsof@gmail.com> | ||
1 | 2 | ||
3 | FilePath creates now one temporary file: | ||
4 | |||
5 | with FilePath("a") as a: | ||
6 | |||
7 | Or more: | ||
8 | |||
9 | with FilePath("a", "b", "c") as (a, b, c): | ||
10 | |||
11 | This is also the behavior of the file_path() helper, used by some of the | ||
12 | tests. Now we have only 2 helpers for creating temporary files instead | ||
13 | of 3. | ||
14 | |||
15 | Signed-off-by: Nir Soffer <nsoffer@redhat.com> | ||
16 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
17 | Message-Id: <20200828232152.205833-5-nsoffer@redhat.com> | ||
18 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
19 | --- | ||
20 | tests/qemu-iotests/194 | 2 +- | ||
21 | tests/qemu-iotests/208 | 2 +- | ||
22 | tests/qemu-iotests/222 | 2 +- | ||
23 | tests/qemu-iotests/257 | 4 ++-- | ||
24 | tests/qemu-iotests/iotests.py | 23 ++++++++++------------- | ||
25 | 5 files changed, 15 insertions(+), 18 deletions(-) | ||
26 | |||
27 | diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194 | ||
28 | index XXXXXXX..XXXXXXX 100755 | ||
29 | --- a/tests/qemu-iotests/194 | ||
30 | +++ b/tests/qemu-iotests/194 | ||
31 | @@ -XXX,XX +XXX,XX @@ iotests.script_initialize(supported_fmts=['qcow2', 'qed', 'raw'], | ||
32 | |||
33 | with iotests.FilePath('source.img') as source_img_path, \ | ||
34 | iotests.FilePath('dest.img') as dest_img_path, \ | ||
35 | - iotests.FilePaths('migration.sock', 'nbd.sock', base_dir=iotests.sock_dir) \ | ||
36 | + iotests.FilePath('migration.sock', 'nbd.sock', base_dir=iotests.sock_dir) \ | ||
37 | as (migration_sock_path, nbd_sock_path), \ | ||
38 | iotests.VM('source') as source_vm, \ | ||
39 | iotests.VM('dest') as dest_vm: | ||
40 | diff --git a/tests/qemu-iotests/208 b/tests/qemu-iotests/208 | ||
41 | index XXXXXXX..XXXXXXX 100755 | ||
42 | --- a/tests/qemu-iotests/208 | ||
43 | +++ b/tests/qemu-iotests/208 | ||
44 | @@ -XXX,XX +XXX,XX @@ iotests.script_initialize(supported_fmts=['generic']) | ||
45 | |||
46 | with iotests.FilePath('disk.img') as disk_img_path, \ | ||
47 | iotests.FilePath('disk-snapshot.img') as disk_snapshot_img_path, \ | ||
48 | - iotests.FilePath('nbd.sock', iotests.sock_dir) as nbd_sock_path, \ | ||
49 | + iotests.FilePath('nbd.sock', base_dir=iotests.sock_dir) as nbd_sock_path, \ | ||
50 | iotests.VM() as vm: | ||
51 | |||
52 | img_size = '10M' | ||
53 | diff --git a/tests/qemu-iotests/222 b/tests/qemu-iotests/222 | ||
54 | index XXXXXXX..XXXXXXX 100755 | ||
55 | --- a/tests/qemu-iotests/222 | ||
56 | +++ b/tests/qemu-iotests/222 | ||
57 | @@ -XXX,XX +XXX,XX @@ remainder = [("0xd5", "0x108000", "32k"), # Right-end of partial-left [1] | ||
58 | |||
59 | with iotests.FilePath('base.img') as base_img_path, \ | ||
60 | iotests.FilePath('fleece.img') as fleece_img_path, \ | ||
61 | - iotests.FilePath('nbd.sock', iotests.sock_dir) as nbd_sock_path, \ | ||
62 | + iotests.FilePath('nbd.sock', base_dir=iotests.sock_dir) as nbd_sock_path, \ | ||
63 | iotests.VM() as vm: | ||
64 | |||
65 | log('--- Setting up images ---') | ||
66 | diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257 | ||
67 | index XXXXXXX..XXXXXXX 100755 | ||
68 | --- a/tests/qemu-iotests/257 | ||
69 | +++ b/tests/qemu-iotests/257 | ||
70 | @@ -XXX,XX +XXX,XX @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None): | ||
71 | an incomplete backup. Testing limitations prevent | ||
72 | testing competing writes. | ||
73 | """ | ||
74 | - with iotests.FilePaths( | ||
75 | + with iotests.FilePath( | ||
76 | 'img', 'bsync1', 'bsync2', 'fbackup0', 'fbackup1', 'fbackup2') as \ | ||
77 | (img_path, bsync1, bsync2, fbackup0, fbackup1, fbackup2), \ | ||
78 | iotests.VM() as vm: | ||
79 | @@ -XXX,XX +XXX,XX @@ def test_backup_api(): | ||
80 | """ | ||
81 | Test malformed and prohibited invocations of the backup API. | ||
82 | """ | ||
83 | - with iotests.FilePaths('img', 'bsync1') as (img_path, backup_path), \ | ||
84 | + with iotests.FilePath('img', 'bsync1') as (img_path, backup_path), \ | ||
85 | iotests.VM() as vm: | ||
86 | |||
87 | log("\n=== API failure tests ===\n") | ||
88 | diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py | ||
89 | index XXXXXXX..XXXXXXX 100644 | ||
90 | --- a/tests/qemu-iotests/iotests.py | ||
91 | +++ b/tests/qemu-iotests/iotests.py | ||
92 | @@ -XXX,XX +XXX,XX @@ class Timeout: | ||
93 | def file_pattern(name): | ||
94 | return "{0}-{1}".format(os.getpid(), name) | ||
95 | |||
96 | -class FilePaths: | ||
97 | +class FilePath: | ||
98 | """ | ||
99 | Context manager generating multiple file names. The generated files are | ||
100 | removed when exiting the context. | ||
101 | |||
102 | Example usage: | ||
103 | |||
104 | - with FilePaths('a.img', 'b.img') as (img_a, img_b): | ||
105 | + with FilePath('a.img', 'b.img') as (img_a, img_b): | ||
106 | # Use img_a and img_b here... | ||
107 | |||
108 | # a.img and b.img are automatically removed here. | ||
109 | @@ -XXX,XX +XXX,XX @@ class FilePaths: | ||
110 | By default images are created in iotests.test_dir. To create sockets use | ||
111 | iotests.sock_dir: | ||
112 | |||
113 | - with FilePaths('a.sock', base_dir=iotests.sock_dir) as (sock,): | ||
114 | + with FilePath('a.sock', base_dir=iotests.sock_dir) as sock: | ||
115 | + | ||
116 | + For convenience, calling with one argument yields a single file instead of | ||
117 | + a tuple with one item. | ||
118 | |||
119 | """ | ||
120 | def __init__(self, *names, base_dir=test_dir): | ||
121 | @@ -XXX,XX +XXX,XX @@ class FilePaths: | ||
122 | self.paths.append(os.path.join(base_dir, file_pattern(name))) | ||
123 | |||
124 | def __enter__(self): | ||
125 | - return self.paths | ||
126 | + if len(self.paths) == 1: | ||
127 | + return self.paths[0] | ||
128 | + else: | ||
129 | + return self.paths | ||
130 | |||
131 | def __exit__(self, exc_type, exc_val, exc_tb): | ||
132 | for path in self.paths: | ||
133 | @@ -XXX,XX +XXX,XX @@ class FilePaths: | ||
134 | pass | ||
135 | return False | ||
136 | |||
137 | -class FilePath(FilePaths): | ||
138 | - """ | ||
139 | - FilePath is a specialization of FilePaths that takes a single filename. | ||
140 | - """ | ||
141 | - def __init__(self, name, base_dir=test_dir): | ||
142 | - super(FilePath, self).__init__(name, base_dir=base_dir) | ||
143 | - | ||
144 | - def __enter__(self): | ||
145 | - return self.paths[0] | ||
146 | |||
147 | def file_path_remover(): | ||
148 | for path in reversed(file_path_remover.paths): | ||
149 | -- | ||
150 | 2.26.2 | ||
151 | |||
152 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Nir Soffer <nirsof@gmail.com> | ||
1 | 2 | ||
3 | Use list comprehension instead of append loop. | ||
4 | |||
5 | Signed-off-by: Nir Soffer <nsoffer@redhat.com> | ||
6 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
7 | Message-Id: <20200828232152.205833-6-nsoffer@redhat.com> | ||
8 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
9 | --- | ||
10 | tests/qemu-iotests/iotests.py | 5 ++--- | ||
11 | 1 file changed, 2 insertions(+), 3 deletions(-) | ||
12 | |||
13 | diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/tests/qemu-iotests/iotests.py | ||
16 | +++ b/tests/qemu-iotests/iotests.py | ||
17 | @@ -XXX,XX +XXX,XX @@ class FilePath: | ||
18 | |||
19 | """ | ||
20 | def __init__(self, *names, base_dir=test_dir): | ||
21 | - self.paths = [] | ||
22 | - for name in names: | ||
23 | - self.paths.append(os.path.join(base_dir, file_pattern(name))) | ||
24 | + self.paths = [os.path.join(base_dir, file_pattern(name)) | ||
25 | + for name in names] | ||
26 | |||
27 | def __enter__(self): | ||
28 | if len(self.paths) == 1: | ||
29 | -- | ||
30 | 2.26.2 | ||
31 | |||
32 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Lukas Straub <lukasstraub2@web.de> | ||
1 | 2 | ||
3 | If we remove the child with the highest index from the quorum, | ||
4 | decrement s->next_child_index. This way we get stable children | ||
5 | names as long as we only remove the last child. | ||
6 | |||
7 | Signed-off-by: Lukas Straub <lukasstraub2@web.de> | ||
8 | Fixes: https://bugs.launchpad.net/bugs/1881231 | ||
9 | Reviewed-by: Zhang Chen <chen.zhang@intel.com> | ||
10 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
11 | Message-Id: <5d5f930424c1c770754041aa8ad6421dc4e2b58e.1596536719.git.lukasstraub2@web.de> | ||
12 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
13 | --- | ||
14 | block/quorum.c | 20 ++++++++++++++------ | ||
15 | 1 file changed, 14 insertions(+), 6 deletions(-) | ||
16 | |||
17 | diff --git a/block/quorum.c b/block/quorum.c | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/block/quorum.c | ||
20 | +++ b/block/quorum.c | ||
21 | @@ -XXX,XX +XXX,XX @@ | ||
22 | |||
23 | #define HASH_LENGTH 32 | ||
24 | |||
25 | +#define INDEXSTR_LEN 32 | ||
26 | + | ||
27 | #define QUORUM_OPT_VOTE_THRESHOLD "vote-threshold" | ||
28 | #define QUORUM_OPT_BLKVERIFY "blkverify" | ||
29 | #define QUORUM_OPT_REWRITE "rewrite-corrupted" | ||
30 | @@ -XXX,XX +XXX,XX @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, | ||
31 | opened = g_new0(bool, s->num_children); | ||
32 | |||
33 | for (i = 0; i < s->num_children; i++) { | ||
34 | - char indexstr[32]; | ||
35 | - ret = snprintf(indexstr, 32, "children.%d", i); | ||
36 | - assert(ret < 32); | ||
37 | + char indexstr[INDEXSTR_LEN]; | ||
38 | + ret = snprintf(indexstr, INDEXSTR_LEN, "children.%d", i); | ||
39 | + assert(ret < INDEXSTR_LEN); | ||
40 | |||
41 | s->children[i] = bdrv_open_child(NULL, options, indexstr, bs, | ||
42 | &child_of_bds, BDRV_CHILD_DATA, false, | ||
43 | @@ -XXX,XX +XXX,XX @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, | ||
44 | { | ||
45 | BDRVQuorumState *s = bs->opaque; | ||
46 | BdrvChild *child; | ||
47 | - char indexstr[32]; | ||
48 | + char indexstr[INDEXSTR_LEN]; | ||
49 | int ret; | ||
50 | |||
51 | if (s->is_blkverify) { | ||
52 | @@ -XXX,XX +XXX,XX @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, | ||
53 | return; | ||
54 | } | ||
55 | |||
56 | - ret = snprintf(indexstr, 32, "children.%u", s->next_child_index); | ||
57 | - if (ret < 0 || ret >= 32) { | ||
58 | + ret = snprintf(indexstr, INDEXSTR_LEN, "children.%u", s->next_child_index); | ||
59 | + if (ret < 0 || ret >= INDEXSTR_LEN) { | ||
60 | error_setg(errp, "cannot generate child name"); | ||
61 | return; | ||
62 | } | ||
63 | @@ -XXX,XX +XXX,XX @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, | ||
64 | Error **errp) | ||
65 | { | ||
66 | BDRVQuorumState *s = bs->opaque; | ||
67 | + char indexstr[INDEXSTR_LEN]; | ||
68 | int i; | ||
69 | |||
70 | for (i = 0; i < s->num_children; i++) { | ||
71 | @@ -XXX,XX +XXX,XX @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, | ||
72 | /* We know now that num_children > threshold, so blkverify must be false */ | ||
73 | assert(!s->is_blkverify); | ||
74 | |||
75 | + snprintf(indexstr, INDEXSTR_LEN, "children.%u", s->next_child_index - 1); | ||
76 | + if (!strncmp(child->name, indexstr, INDEXSTR_LEN)) { | ||
77 | + s->next_child_index--; | ||
78 | + } | ||
79 | + | ||
80 | bdrv_drained_begin(bs); | ||
81 | |||
82 | /* We can safely remove this child now */ | ||
83 | -- | ||
84 | 2.26.2 | ||
85 | |||
86 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: Peter Lieven <pl@kamp.de> |
---|---|---|---|
2 | 2 | ||
3 | All jobs do the same thing when they leave their running loop: | 3 | in case of large continous areas that share the same allocation status |
4 | - Store the return code in a structure | 4 | it happens that the value of s->sector_next_status is unaligned to the |
5 | - wait to receive this structure in the main thread | 5 | cluster size or even request alignment of the source. Avoid this by |
6 | - signal job completion via job_completed | 6 | stripping down the s->sector_next_status position to cluster boundaries. |
7 | 7 | ||
8 | Few jobs do anything beyond exactly this. Consolidate this exit | 8 | Signed-off-by: Peter Lieven <pl@kamp.de> |
9 | logic for a net reduction in SLOC. | 9 | Message-Id: <20200901125129.6398-1-pl@kamp.de> |
10 | 10 | [mreitz: Disable vhdx for 251] | |
11 | More seriously, when we utilize job_defer_to_main_loop_bh to call | ||
12 | a function that calls job_completed, job_finalize_single will run | ||
13 | in a context where it has recursively taken the aio_context lock, | ||
14 | which can cause hangs if it puts down a reference that causes a flush. | ||
15 | |||
16 | You can observe this in practice by looking at mirror_exit's careful | ||
17 | placement of job_completed and bdrv_unref calls. | ||
18 | |||
19 | If we centralize job exiting, we can signal job completion from outside | ||
20 | of the aio_context, which should allow for job cleanup code to run with | ||
21 | only one lock, which makes cleanup callbacks less tricky to write. | ||
22 | |||
23 | Signed-off-by: John Snow <jsnow@redhat.com> | ||
24 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
25 | Message-id: 20180830015734.19765-4-jsnow@redhat.com | ||
26 | Reviewed-by: Jeff Cody <jcody@redhat.com> | ||
27 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 11 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
28 | --- | 12 | --- |
29 | include/qemu/job.h | 11 +++++++++++ | 13 | qemu-img.c | 22 ++++++++++++++++++++++ |
30 | job.c | 18 ++++++++++++++++++ | 14 | tests/qemu-iotests/251 | 7 +++++-- |
31 | 2 files changed, 29 insertions(+) | 15 | 2 files changed, 27 insertions(+), 2 deletions(-) |
32 | 16 | ||
33 | diff --git a/include/qemu/job.h b/include/qemu/job.h | 17 | diff --git a/qemu-img.c b/qemu-img.c |
34 | index XXXXXXX..XXXXXXX 100644 | 18 | index XXXXXXX..XXXXXXX 100644 |
35 | --- a/include/qemu/job.h | 19 | --- a/qemu-img.c |
36 | +++ b/include/qemu/job.h | 20 | +++ b/qemu-img.c |
37 | @@ -XXX,XX +XXX,XX @@ struct JobDriver { | 21 | @@ -XXX,XX +XXX,XX @@ enum ImgConvertBlockStatus { |
38 | */ | 22 | typedef struct ImgConvertState { |
39 | void (*drain)(Job *job); | 23 | BlockBackend **src; |
40 | 24 | int64_t *src_sectors; | |
41 | + /** | 25 | + int *src_alignment; |
42 | + * If the callback is not NULL, exit will be invoked from the main thread | 26 | int src_num; |
43 | + * when the job's coroutine has finished, but before transactional | 27 | int64_t total_sectors; |
44 | + * convergence; before @prepare or @abort. | 28 | int64_t allocated_sectors; |
45 | + * | 29 | @@ -XXX,XX +XXX,XX @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) |
46 | + * FIXME TODO: This callback is only temporary to transition remaining jobs | 30 | if (s->sector_next_status <= sector_num) { |
47 | + * to prepare/commit/abort/clean callbacks and will be removed before 3.1. | 31 | uint64_t offset = (sector_num - src_cur_offset) * BDRV_SECTOR_SIZE; |
48 | + * is released. | 32 | int64_t count; |
49 | + */ | 33 | + int tail; |
50 | + void (*exit)(Job *job); | 34 | BlockDriverState *src_bs = blk_bs(s->src[src_cur]); |
35 | BlockDriverState *base; | ||
36 | |||
37 | @@ -XXX,XX +XXX,XX @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) | ||
38 | |||
39 | n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); | ||
40 | |||
41 | + /* | ||
42 | + * Avoid that s->sector_next_status becomes unaligned to the source | ||
43 | + * request alignment and/or cluster size to avoid unnecessary read | ||
44 | + * cycles. | ||
45 | + */ | ||
46 | + tail = (sector_num - src_cur_offset + n) % s->src_alignment[src_cur]; | ||
47 | + if (n > tail) { | ||
48 | + n -= tail; | ||
49 | + } | ||
51 | + | 50 | + |
52 | /** | 51 | if (ret & BDRV_BLOCK_ZERO) { |
53 | * If the callback is not NULL, prepare will be invoked when all the jobs | 52 | s->status = post_backing_zero ? BLK_BACKING_FILE : BLK_ZERO; |
54 | * belonging to the same transaction complete; or upon this job's completion | 53 | } else if (ret & BDRV_BLOCK_DATA) { |
55 | diff --git a/job.c b/job.c | 54 | @@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv) |
56 | index XXXXXXX..XXXXXXX 100644 | 55 | |
57 | --- a/job.c | 56 | s.src = g_new0(BlockBackend *, s.src_num); |
58 | +++ b/job.c | 57 | s.src_sectors = g_new(int64_t, s.src_num); |
59 | @@ -XXX,XX +XXX,XX @@ void job_drain(Job *job) | 58 | + s.src_alignment = g_new(int, s.src_num); |
59 | |||
60 | for (bs_i = 0; bs_i < s.src_num; bs_i++) { | ||
61 | + BlockDriverState *src_bs; | ||
62 | s.src[bs_i] = img_open(image_opts, argv[optind + bs_i], | ||
63 | fmt, src_flags, src_writethrough, s.quiet, | ||
64 | force_share); | ||
65 | @@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv) | ||
66 | ret = -1; | ||
67 | goto out; | ||
68 | } | ||
69 | + src_bs = blk_bs(s.src[bs_i]); | ||
70 | + s.src_alignment[bs_i] = DIV_ROUND_UP(src_bs->bl.request_alignment, | ||
71 | + BDRV_SECTOR_SIZE); | ||
72 | + if (!bdrv_get_info(src_bs, &bdi)) { | ||
73 | + s.src_alignment[bs_i] = MAX(s.src_alignment[bs_i], | ||
74 | + bdi.cluster_size / BDRV_SECTOR_SIZE); | ||
75 | + } | ||
76 | s.total_sectors += s.src_sectors[bs_i]; | ||
60 | } | 77 | } |
61 | } | 78 | |
62 | 79 | @@ -XXX,XX +XXX,XX @@ out: | |
63 | +static void job_exit(void *opaque) | 80 | g_free(s.src); |
64 | +{ | 81 | } |
65 | + Job *job = (Job *)opaque; | 82 | g_free(s.src_sectors); |
66 | + AioContext *aio_context = job->aio_context; | 83 | + g_free(s.src_alignment); |
67 | + | 84 | fail_getopt: |
68 | + if (job->driver->exit) { | 85 | g_free(options); |
69 | + aio_context_acquire(aio_context); | 86 | |
70 | + job->driver->exit(job); | 87 | diff --git a/tests/qemu-iotests/251 b/tests/qemu-iotests/251 |
71 | + aio_context_release(aio_context); | 88 | index XXXXXXX..XXXXXXX 100755 |
72 | + } | 89 | --- a/tests/qemu-iotests/251 |
73 | + job_completed(job, job->ret); | 90 | +++ b/tests/qemu-iotests/251 |
74 | +} | 91 | @@ -XXX,XX +XXX,XX @@ if [ "$IMGOPTSSYNTAX" = "true" ]; then |
75 | 92 | # We use json:{} filenames here, so we cannot work with additional options. | |
76 | /** | 93 | _unsupported_fmt $IMGFMT |
77 | * All jobs must allow a pause point before entering their job proper. This | 94 | else |
78 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn job_co_entry(void *opaque) | 95 | - # With VDI, the output is ordered differently. Just disable it. |
79 | assert(job && job->driver && job->driver->run); | 96 | - _unsupported_fmt vdi |
80 | job_pause_point(job); | 97 | + # - With VDI, the output is ordered differently. Just disable it. |
81 | job->ret = job->driver->run(job, &job->err); | 98 | + # - VHDX has large clusters; because qemu-img convert tries to |
82 | + if (!job->deferred_to_main_loop) { | 99 | + # align the requests to the cluster size, the output is ordered |
83 | + job->deferred_to_main_loop = true; | 100 | + # differently, so disable it, too. |
84 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), | 101 | + _unsupported_fmt vdi vhdx |
85 | + job_exit, | 102 | fi |
86 | + job); | ||
87 | + } | ||
88 | } | ||
89 | 103 | ||
90 | 104 | ||
91 | -- | 105 | -- |
92 | 2.17.1 | 106 | 2.26.2 |
93 | 107 | ||
94 | 108 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: Alberto Garcia <berto@igalia.com> |
---|---|---|---|
2 | 2 | ||
3 | Now that the job infrastructure is handling the job_completed call for | 3 | This patch replaces instances of sizeof(uint64_t) in the qcow2 driver |
4 | all implemented jobs, we can remove the interface that allowed jobs to | 4 | with macros that indicate what those sizes are actually referring to. |
5 | schedule their own completion. | ||
6 | 5 | ||
7 | Signed-off-by: John Snow <jsnow@redhat.com> | 6 | Signed-off-by: Alberto Garcia <berto@igalia.com> |
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 7 | Message-Id: <20200828110828.13833-1-berto@igalia.com> |
9 | Message-id: 20180830015734.19765-10-jsnow@redhat.com | ||
10 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 8 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
11 | --- | 9 | --- |
12 | include/qemu/job.h | 17 ----------------- | 10 | block/qcow2.h | 6 +++ |
13 | job.c | 40 ++-------------------------------------- | 11 | block/qcow2-bitmap.c | 11 ++++-- |
14 | 2 files changed, 2 insertions(+), 55 deletions(-) | 12 | block/qcow2-cluster.c | 24 ++++++------ |
13 | block/qcow2-refcount.c | 89 ++++++++++++++++++++++-------------------- | ||
14 | block/qcow2-snapshot.c | 20 +++++----- | ||
15 | block/qcow2.c | 27 ++++++------- | ||
16 | 6 files changed, 94 insertions(+), 83 deletions(-) | ||
15 | 17 | ||
16 | diff --git a/include/qemu/job.h b/include/qemu/job.h | 18 | diff --git a/block/qcow2.h b/block/qcow2.h |
17 | index XXXXXXX..XXXXXXX 100644 | 19 | index XXXXXXX..XXXXXXX 100644 |
18 | --- a/include/qemu/job.h | 20 | --- a/block/qcow2.h |
19 | +++ b/include/qemu/job.h | 21 | +++ b/block/qcow2.h |
20 | @@ -XXX,XX +XXX,XX @@ void job_finalize(Job *job, Error **errp); | 22 | @@ -XXX,XX +XXX,XX @@ |
21 | */ | 23 | #define L2E_SIZE_NORMAL (sizeof(uint64_t)) |
22 | void job_dismiss(Job **job, Error **errp); | 24 | #define L2E_SIZE_EXTENDED (sizeof(uint64_t) * 2) |
23 | 25 | ||
24 | -typedef void JobDeferToMainLoopFn(Job *job, void *opaque); | 26 | +/* Size of L1 table entries */ |
25 | - | 27 | +#define L1E_SIZE (sizeof(uint64_t)) |
26 | -/** | 28 | + |
27 | - * @job: The job | 29 | +/* Size of reftable entries */ |
28 | - * @fn: The function to run in the main loop | 30 | +#define REFTABLE_ENTRY_SIZE (sizeof(uint64_t)) |
29 | - * @opaque: The opaque value that is passed to @fn | 31 | + |
30 | - * | 32 | #define MIN_CLUSTER_BITS 9 |
31 | - * This function must be called by the main job coroutine just before it | 33 | #define MAX_CLUSTER_BITS 21 |
32 | - * returns. @fn is executed in the main loop with the job AioContext acquired. | 34 | |
33 | - * | 35 | diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c |
34 | - * Block jobs must call bdrv_unref(), bdrv_close(), and anything that uses | ||
35 | - * bdrv_drain_all() in the main loop. | ||
36 | - * | ||
37 | - * The @job AioContext is held while @fn executes. | ||
38 | - */ | ||
39 | -void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); | ||
40 | - | ||
41 | /** | ||
42 | * Synchronously finishes the given @job. If @finish is given, it is called to | ||
43 | * trigger completion or cancellation of the job. | ||
44 | diff --git a/job.c b/job.c | ||
45 | index XXXXXXX..XXXXXXX 100644 | 36 | index XXXXXXX..XXXXXXX 100644 |
46 | --- a/job.c | 37 | --- a/block/qcow2-bitmap.c |
47 | +++ b/job.c | 38 | +++ b/block/qcow2-bitmap.c |
48 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn job_co_entry(void *opaque) | 39 | @@ -XXX,XX +XXX,XX @@ |
49 | assert(job && job->driver && job->driver->run); | 40 | #define BME_MIN_GRANULARITY_BITS 9 |
50 | job_pause_point(job); | 41 | #define BME_MAX_NAME_SIZE 1023 |
51 | job->ret = job->driver->run(job, &job->err); | 42 | |
52 | - if (!job->deferred_to_main_loop) { | 43 | +/* Size of bitmap table entries */ |
53 | - job->deferred_to_main_loop = true; | 44 | +#define BME_TABLE_ENTRY_SIZE (sizeof(uint64_t)) |
54 | - aio_bh_schedule_oneshot(qemu_get_aio_context(), | 45 | + |
55 | - job_exit, | 46 | QEMU_BUILD_BUG_ON(BME_MAX_NAME_SIZE != BDRV_BITMAP_MAX_NAME_SIZE); |
56 | - job); | 47 | |
57 | - } | 48 | #if BME_MAX_TABLE_SIZE * 8ULL > INT_MAX |
58 | + job->deferred_to_main_loop = true; | 49 | @@ -XXX,XX +XXX,XX @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, |
59 | + aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); | 50 | |
51 | assert(tb->size <= BME_MAX_TABLE_SIZE); | ||
52 | ret = bdrv_pread(bs->file, tb->offset, | ||
53 | - table, tb->size * sizeof(uint64_t)); | ||
54 | + table, tb->size * BME_TABLE_ENTRY_SIZE); | ||
55 | if (ret < 0) { | ||
56 | goto fail; | ||
57 | } | ||
58 | @@ -XXX,XX +XXX,XX @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) | ||
59 | } | ||
60 | |||
61 | clear_bitmap_table(bs, bitmap_table, tb->size); | ||
62 | - qcow2_free_clusters(bs, tb->offset, tb->size * sizeof(uint64_t), | ||
63 | + qcow2_free_clusters(bs, tb->offset, tb->size * BME_TABLE_ENTRY_SIZE, | ||
64 | QCOW2_DISCARD_OTHER); | ||
65 | g_free(bitmap_table); | ||
66 | |||
67 | @@ -XXX,XX +XXX,XX @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, | ||
68 | ret = qcow2_inc_refcounts_imrt(bs, res, | ||
69 | refcount_table, refcount_table_size, | ||
70 | bm->table.offset, | ||
71 | - bm->table.size * sizeof(uint64_t)); | ||
72 | + bm->table.size * BME_TABLE_ENTRY_SIZE); | ||
73 | if (ret < 0) { | ||
74 | goto out; | ||
75 | } | ||
76 | @@ -XXX,XX +XXX,XX @@ uint64_t qcow2_get_persistent_dirty_bitmap_size(BlockDriverState *in_bs, | ||
77 | /* Assume the entire bitmap is allocated */ | ||
78 | bitmaps_size += bmclusters * cluster_size; | ||
79 | /* Also reserve space for the bitmap table entries */ | ||
80 | - bitmaps_size += ROUND_UP(bmclusters * sizeof(uint64_t), | ||
81 | + bitmaps_size += ROUND_UP(bmclusters * BME_TABLE_ENTRY_SIZE, | ||
82 | cluster_size); | ||
83 | /* And space for contribution to bitmap directory size */ | ||
84 | bitmap_dir_size += calc_dir_entry_size(strlen(name), 0); | ||
85 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
86 | index XXXXXXX..XXXXXXX 100644 | ||
87 | --- a/block/qcow2-cluster.c | ||
88 | +++ b/block/qcow2-cluster.c | ||
89 | @@ -XXX,XX +XXX,XX @@ int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t exact_size) | ||
90 | |||
91 | BLKDBG_EVENT(bs->file, BLKDBG_L1_SHRINK_WRITE_TABLE); | ||
92 | ret = bdrv_pwrite_zeroes(bs->file, s->l1_table_offset + | ||
93 | - new_l1_size * sizeof(uint64_t), | ||
94 | - (s->l1_size - new_l1_size) * sizeof(uint64_t), 0); | ||
95 | + new_l1_size * L1E_SIZE, | ||
96 | + (s->l1_size - new_l1_size) * L1E_SIZE, 0); | ||
97 | if (ret < 0) { | ||
98 | goto fail; | ||
99 | } | ||
100 | @@ -XXX,XX +XXX,XX @@ fail: | ||
101 | * l1_table in memory to avoid possible image corruption. | ||
102 | */ | ||
103 | memset(s->l1_table + new_l1_size, 0, | ||
104 | - (s->l1_size - new_l1_size) * sizeof(uint64_t)); | ||
105 | + (s->l1_size - new_l1_size) * L1E_SIZE); | ||
106 | return ret; | ||
60 | } | 107 | } |
61 | 108 | ||
62 | 109 | @@ -XXX,XX +XXX,XX @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, | |
63 | @@ -XXX,XX +XXX,XX @@ void job_complete(Job *job, Error **errp) | 110 | /* Do a sanity check on min_size before trying to calculate new_l1_size |
64 | job->driver->complete(job, errp); | 111 | * (this prevents overflows during the while loop for the calculation of |
65 | } | 112 | * new_l1_size) */ |
66 | 113 | - if (min_size > INT_MAX / sizeof(uint64_t)) { | |
67 | - | 114 | + if (min_size > INT_MAX / L1E_SIZE) { |
68 | -typedef struct { | 115 | return -EFBIG; |
69 | - Job *job; | 116 | } |
70 | - JobDeferToMainLoopFn *fn; | 117 | |
71 | - void *opaque; | 118 | @@ -XXX,XX +XXX,XX @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, |
72 | -} JobDeferToMainLoopData; | 119 | } |
73 | - | 120 | |
74 | -static void job_defer_to_main_loop_bh(void *opaque) | 121 | QEMU_BUILD_BUG_ON(QCOW_MAX_L1_SIZE > INT_MAX); |
75 | -{ | 122 | - if (new_l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) { |
76 | - JobDeferToMainLoopData *data = opaque; | 123 | + if (new_l1_size > QCOW_MAX_L1_SIZE / L1E_SIZE) { |
77 | - Job *job = data->job; | 124 | return -EFBIG; |
78 | - AioContext *aio_context = job->aio_context; | 125 | } |
79 | - | 126 | |
80 | - aio_context_acquire(aio_context); | 127 | @@ -XXX,XX +XXX,XX @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, |
81 | - data->fn(data->job, data->opaque); | 128 | s->l1_size, new_l1_size); |
82 | - aio_context_release(aio_context); | 129 | #endif |
83 | - | 130 | |
84 | - g_free(data); | 131 | - new_l1_size2 = sizeof(uint64_t) * new_l1_size; |
85 | -} | 132 | + new_l1_size2 = L1E_SIZE * new_l1_size; |
86 | - | 133 | new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_size2); |
87 | -void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque) | 134 | if (new_l1_table == NULL) { |
88 | -{ | 135 | return -ENOMEM; |
89 | - JobDeferToMainLoopData *data = g_malloc(sizeof(*data)); | 136 | @@ -XXX,XX +XXX,XX @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, |
90 | - data->job = job; | 137 | memset(new_l1_table, 0, new_l1_size2); |
91 | - data->fn = fn; | 138 | |
92 | - data->opaque = opaque; | 139 | if (s->l1_size) { |
93 | - job->deferred_to_main_loop = true; | 140 | - memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); |
94 | - | 141 | + memcpy(new_l1_table, s->l1_table, s->l1_size * L1E_SIZE); |
95 | - aio_bh_schedule_oneshot(qemu_get_aio_context(), | 142 | } |
96 | - job_defer_to_main_loop_bh, data); | 143 | |
97 | -} | 144 | /* write new table (align to cluster) */ |
98 | - | 145 | @@ -XXX,XX +XXX,XX @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, |
99 | int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) | 146 | s->l1_table = new_l1_table; |
147 | old_l1_size = s->l1_size; | ||
148 | s->l1_size = new_l1_size; | ||
149 | - qcow2_free_clusters(bs, old_l1_table_offset, old_l1_size * sizeof(uint64_t), | ||
150 | + qcow2_free_clusters(bs, old_l1_table_offset, old_l1_size * L1E_SIZE, | ||
151 | QCOW2_DISCARD_OTHER); | ||
152 | return 0; | ||
153 | fail: | ||
154 | @@ -XXX,XX +XXX,XX @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) | ||
155 | BDRVQcow2State *s = bs->opaque; | ||
156 | int l1_start_index; | ||
157 | int i, ret; | ||
158 | - int bufsize = MAX(sizeof(uint64_t), | ||
159 | + int bufsize = MAX(L1E_SIZE, | ||
160 | MIN(bs->file->bs->bl.request_alignment, s->cluster_size)); | ||
161 | - int nentries = bufsize / sizeof(uint64_t); | ||
162 | + int nentries = bufsize / L1E_SIZE; | ||
163 | g_autofree uint64_t *buf = g_try_new0(uint64_t, nentries); | ||
164 | |||
165 | if (buf == NULL) { | ||
166 | @@ -XXX,XX +XXX,XX @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, | ||
167 | Error *local_err = NULL; | ||
168 | |||
169 | ret = qcow2_validate_table(bs, s->snapshots[i].l1_table_offset, | ||
170 | - s->snapshots[i].l1_size, sizeof(uint64_t), | ||
171 | + s->snapshots[i].l1_size, L1E_SIZE, | ||
172 | QCOW_MAX_L1_SIZE, "Snapshot L1 table", | ||
173 | &local_err); | ||
174 | if (ret < 0) { | ||
175 | @@ -XXX,XX +XXX,XX @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, | ||
176 | goto fail; | ||
177 | } | ||
178 | |||
179 | - l1_size2 = s->snapshots[i].l1_size * sizeof(uint64_t); | ||
180 | + l1_size2 = s->snapshots[i].l1_size * L1E_SIZE; | ||
181 | new_l1_table = g_try_realloc(l1_table, l1_size2); | ||
182 | |||
183 | if (!new_l1_table) { | ||
184 | diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c | ||
185 | index XXXXXXX..XXXXXXX 100644 | ||
186 | --- a/block/qcow2-refcount.c | ||
187 | +++ b/block/qcow2-refcount.c | ||
188 | @@ -XXX,XX +XXX,XX @@ int qcow2_refcount_init(BlockDriverState *bs) | ||
189 | s->get_refcount = get_refcount_funcs[s->refcount_order]; | ||
190 | s->set_refcount = set_refcount_funcs[s->refcount_order]; | ||
191 | |||
192 | - assert(s->refcount_table_size <= INT_MAX / sizeof(uint64_t)); | ||
193 | - refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); | ||
194 | + assert(s->refcount_table_size <= INT_MAX / REFTABLE_ENTRY_SIZE); | ||
195 | + refcount_table_size2 = s->refcount_table_size * REFTABLE_ENTRY_SIZE; | ||
196 | s->refcount_table = g_try_malloc(refcount_table_size2); | ||
197 | |||
198 | if (s->refcount_table_size > 0) { | ||
199 | @@ -XXX,XX +XXX,XX @@ static int alloc_refcount_block(BlockDriverState *bs, | ||
200 | if (refcount_table_index < s->refcount_table_size) { | ||
201 | uint64_t data64 = cpu_to_be64(new_block); | ||
202 | BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP); | ||
203 | - ret = bdrv_pwrite_sync(bs->file, | ||
204 | - s->refcount_table_offset + refcount_table_index * sizeof(uint64_t), | ||
205 | + ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset + | ||
206 | + refcount_table_index * REFTABLE_ENTRY_SIZE, | ||
207 | &data64, sizeof(data64)); | ||
208 | if (ret < 0) { | ||
209 | goto fail; | ||
210 | @@ -XXX,XX +XXX,XX @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, | ||
211 | DIV_ROUND_UP(total_refblock_count, 2); | ||
212 | } | ||
213 | /* The qcow2 file can only store the reftable size in number of clusters */ | ||
214 | - table_size = ROUND_UP(table_size, s->cluster_size / sizeof(uint64_t)); | ||
215 | - table_clusters = (table_size * sizeof(uint64_t)) / s->cluster_size; | ||
216 | + table_size = ROUND_UP(table_size, s->cluster_size / REFTABLE_ENTRY_SIZE); | ||
217 | + table_clusters = (table_size * REFTABLE_ENTRY_SIZE) / s->cluster_size; | ||
218 | |||
219 | if (table_size > QCOW_MAX_REFTABLE_SIZE) { | ||
220 | return -EFBIG; | ||
221 | @@ -XXX,XX +XXX,XX @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, | ||
222 | if (table_size > s->max_refcount_table_index) { | ||
223 | /* We're actually growing the reftable */ | ||
224 | memcpy(new_table, s->refcount_table, | ||
225 | - (s->max_refcount_table_index + 1) * sizeof(uint64_t)); | ||
226 | + (s->max_refcount_table_index + 1) * REFTABLE_ENTRY_SIZE); | ||
227 | } else { | ||
228 | /* Improbable case: We're shrinking the reftable. However, the caller | ||
229 | * has assured us that there is only empty space beyond @start_offset, | ||
230 | * so we can simply drop all of the refblocks that won't fit into the | ||
231 | * new reftable. */ | ||
232 | - memcpy(new_table, s->refcount_table, table_size * sizeof(uint64_t)); | ||
233 | + memcpy(new_table, s->refcount_table, table_size * REFTABLE_ENTRY_SIZE); | ||
234 | } | ||
235 | |||
236 | if (new_refblock_offset) { | ||
237 | @@ -XXX,XX +XXX,XX @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, | ||
238 | |||
239 | BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE); | ||
240 | ret = bdrv_pwrite_sync(bs->file, table_offset, new_table, | ||
241 | - table_size * sizeof(uint64_t)); | ||
242 | + table_size * REFTABLE_ENTRY_SIZE); | ||
243 | if (ret < 0) { | ||
244 | goto fail; | ||
245 | } | ||
246 | @@ -XXX,XX +XXX,XX @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, | ||
247 | update_max_refcount_table_index(s); | ||
248 | |||
249 | /* Free old table. */ | ||
250 | - qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t), | ||
251 | + qcow2_free_clusters(bs, old_table_offset, | ||
252 | + old_table_size * REFTABLE_ENTRY_SIZE, | ||
253 | QCOW2_DISCARD_OTHER); | ||
254 | |||
255 | return end_offset; | ||
256 | @@ -XXX,XX +XXX,XX @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||
257 | |||
258 | l2_slice = NULL; | ||
259 | l1_table = NULL; | ||
260 | - l1_size2 = l1_size * sizeof(uint64_t); | ||
261 | + l1_size2 = l1_size * L1E_SIZE; | ||
262 | slice_size2 = s->l2_slice_size * l2_entry_size(s); | ||
263 | n_slices = s->cluster_size / slice_size2; | ||
264 | |||
265 | @@ -XXX,XX +XXX,XX @@ static int check_refcounts_l1(BlockDriverState *bs, | ||
266 | uint64_t *l1_table = NULL, l2_offset, l1_size2; | ||
267 | int i, ret; | ||
268 | |||
269 | - l1_size2 = l1_size * sizeof(uint64_t); | ||
270 | + l1_size2 = l1_size * L1E_SIZE; | ||
271 | |||
272 | /* Mark L1 table as used */ | ||
273 | ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_table_size, | ||
274 | @@ -XXX,XX +XXX,XX @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, | ||
275 | res->corruptions++; | ||
276 | continue; | ||
277 | } | ||
278 | - if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) { | ||
279 | + if (sn->l1_size > QCOW_MAX_L1_SIZE / L1E_SIZE) { | ||
280 | fprintf(stderr, "ERROR snapshot %s (%s) l1_size=%#" PRIx32 ": " | ||
281 | "L1 table is too large; snapshot table entry corrupted\n", | ||
282 | sn->id_str, sn->name, sn->l1_size); | ||
283 | @@ -XXX,XX +XXX,XX @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, | ||
284 | /* refcount data */ | ||
285 | ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, | ||
286 | s->refcount_table_offset, | ||
287 | - s->refcount_table_size * sizeof(uint64_t)); | ||
288 | + s->refcount_table_size * | ||
289 | + REFTABLE_ENTRY_SIZE); | ||
290 | if (ret < 0) { | ||
291 | return ret; | ||
292 | } | ||
293 | @@ -XXX,XX +XXX,XX @@ write_refblocks: | ||
294 | uint32_t old_reftable_size = reftable_size; | ||
295 | uint64_t *new_on_disk_reftable; | ||
296 | |||
297 | - reftable_size = ROUND_UP((refblock_index + 1) * sizeof(uint64_t), | ||
298 | - s->cluster_size) / sizeof(uint64_t); | ||
299 | + reftable_size = ROUND_UP((refblock_index + 1) * REFTABLE_ENTRY_SIZE, | ||
300 | + s->cluster_size) / REFTABLE_ENTRY_SIZE; | ||
301 | new_on_disk_reftable = g_try_realloc(on_disk_reftable, | ||
302 | reftable_size * | ||
303 | - sizeof(uint64_t)); | ||
304 | + REFTABLE_ENTRY_SIZE); | ||
305 | if (!new_on_disk_reftable) { | ||
306 | res->check_errors++; | ||
307 | ret = -ENOMEM; | ||
308 | @@ -XXX,XX +XXX,XX @@ write_refblocks: | ||
309 | on_disk_reftable = new_on_disk_reftable; | ||
310 | |||
311 | memset(on_disk_reftable + old_reftable_size, 0, | ||
312 | - (reftable_size - old_reftable_size) * sizeof(uint64_t)); | ||
313 | + (reftable_size - old_reftable_size) * REFTABLE_ENTRY_SIZE); | ||
314 | |||
315 | /* The offset we have for the reftable is now no longer valid; | ||
316 | * this will leak that range, but we can easily fix that by running | ||
317 | @@ -XXX,XX +XXX,XX @@ write_refblocks: | ||
318 | reftable_offset < 0) | ||
319 | { | ||
320 | uint64_t reftable_clusters = size_to_clusters(s, reftable_size * | ||
321 | - sizeof(uint64_t)); | ||
322 | + REFTABLE_ENTRY_SIZE); | ||
323 | reftable_offset = alloc_clusters_imrt(bs, reftable_clusters, | ||
324 | refcount_table, nb_clusters, | ||
325 | &first_free_cluster); | ||
326 | @@ -XXX,XX +XXX,XX @@ write_refblocks: | ||
327 | uint64_t post_refblock_start, reftable_clusters; | ||
328 | |||
329 | post_refblock_start = ROUND_UP(*nb_clusters, s->refcount_block_size); | ||
330 | - reftable_clusters = size_to_clusters(s, | ||
331 | - reftable_size * sizeof(uint64_t)); | ||
332 | + reftable_clusters = | ||
333 | + size_to_clusters(s, reftable_size * REFTABLE_ENTRY_SIZE); | ||
334 | /* Not pretty but simple */ | ||
335 | if (first_free_cluster < post_refblock_start) { | ||
336 | first_free_cluster = post_refblock_start; | ||
337 | @@ -XXX,XX +XXX,XX @@ write_refblocks: | ||
338 | } | ||
339 | |||
340 | ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset, | ||
341 | - reftable_size * sizeof(uint64_t), | ||
342 | + reftable_size * REFTABLE_ENTRY_SIZE, | ||
343 | false); | ||
344 | if (ret < 0) { | ||
345 | fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); | ||
346 | goto fail; | ||
347 | } | ||
348 | |||
349 | - assert(reftable_size < INT_MAX / sizeof(uint64_t)); | ||
350 | + assert(reftable_size < INT_MAX / REFTABLE_ENTRY_SIZE); | ||
351 | ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable, | ||
352 | - reftable_size * sizeof(uint64_t)); | ||
353 | + reftable_size * REFTABLE_ENTRY_SIZE); | ||
354 | if (ret < 0) { | ||
355 | fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); | ||
356 | goto fail; | ||
357 | @@ -XXX,XX +XXX,XX @@ write_refblocks: | ||
358 | /* Enter new reftable into the image header */ | ||
359 | reftable_offset_and_clusters.reftable_offset = cpu_to_be64(reftable_offset); | ||
360 | reftable_offset_and_clusters.reftable_clusters = | ||
361 | - cpu_to_be32(size_to_clusters(s, reftable_size * sizeof(uint64_t))); | ||
362 | + cpu_to_be32(size_to_clusters(s, reftable_size * REFTABLE_ENTRY_SIZE)); | ||
363 | ret = bdrv_pwrite_sync(bs->file, | ||
364 | offsetof(QCowHeader, refcount_table_offset), | ||
365 | &reftable_offset_and_clusters, | ||
366 | @@ -XXX,XX +XXX,XX @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, | ||
367 | offset = start_of_cluster(s, offset); | ||
368 | |||
369 | if ((chk & QCOW2_OL_ACTIVE_L1) && s->l1_size) { | ||
370 | - if (overlaps_with(s->l1_table_offset, s->l1_size * sizeof(uint64_t))) { | ||
371 | + if (overlaps_with(s->l1_table_offset, s->l1_size * L1E_SIZE)) { | ||
372 | return QCOW2_OL_ACTIVE_L1; | ||
373 | } | ||
374 | } | ||
375 | |||
376 | if ((chk & QCOW2_OL_REFCOUNT_TABLE) && s->refcount_table_size) { | ||
377 | if (overlaps_with(s->refcount_table_offset, | ||
378 | - s->refcount_table_size * sizeof(uint64_t))) { | ||
379 | + s->refcount_table_size * REFTABLE_ENTRY_SIZE)) { | ||
380 | return QCOW2_OL_REFCOUNT_TABLE; | ||
381 | } | ||
382 | } | ||
383 | @@ -XXX,XX +XXX,XX @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, | ||
384 | for (i = 0; i < s->nb_snapshots; i++) { | ||
385 | if (s->snapshots[i].l1_size && | ||
386 | overlaps_with(s->snapshots[i].l1_table_offset, | ||
387 | - s->snapshots[i].l1_size * sizeof(uint64_t))) { | ||
388 | + s->snapshots[i].l1_size * L1E_SIZE)) { | ||
389 | return QCOW2_OL_INACTIVE_L1; | ||
390 | } | ||
391 | } | ||
392 | @@ -XXX,XX +XXX,XX @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, | ||
393 | for (i = 0; i < s->nb_snapshots; i++) { | ||
394 | uint64_t l1_ofs = s->snapshots[i].l1_table_offset; | ||
395 | uint32_t l1_sz = s->snapshots[i].l1_size; | ||
396 | - uint64_t l1_sz2 = l1_sz * sizeof(uint64_t); | ||
397 | + uint64_t l1_sz2 = l1_sz * L1E_SIZE; | ||
398 | uint64_t *l1; | ||
399 | int ret; | ||
400 | |||
401 | - ret = qcow2_validate_table(bs, l1_ofs, l1_sz, sizeof(uint64_t), | ||
402 | + ret = qcow2_validate_table(bs, l1_ofs, l1_sz, L1E_SIZE, | ||
403 | QCOW_MAX_L1_SIZE, "", NULL); | ||
404 | if (ret < 0) { | ||
405 | return ret; | ||
406 | @@ -XXX,XX +XXX,XX @@ static int alloc_refblock(BlockDriverState *bs, uint64_t **reftable, | ||
407 | uint64_t new_reftable_size; | ||
408 | |||
409 | new_reftable_size = ROUND_UP(reftable_index + 1, | ||
410 | - s->cluster_size / sizeof(uint64_t)); | ||
411 | - if (new_reftable_size > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) { | ||
412 | + s->cluster_size / REFTABLE_ENTRY_SIZE); | ||
413 | + if (new_reftable_size > QCOW_MAX_REFTABLE_SIZE / REFTABLE_ENTRY_SIZE) { | ||
414 | error_setg(errp, | ||
415 | "This operation would make the refcount table grow " | ||
416 | "beyond the maximum size supported by QEMU, aborting"); | ||
417 | @@ -XXX,XX +XXX,XX @@ static int alloc_refblock(BlockDriverState *bs, uint64_t **reftable, | ||
418 | } | ||
419 | |||
420 | new_reftable = g_try_realloc(*reftable, new_reftable_size * | ||
421 | - sizeof(uint64_t)); | ||
422 | + REFTABLE_ENTRY_SIZE); | ||
423 | if (!new_reftable) { | ||
424 | error_setg(errp, "Failed to increase reftable buffer size"); | ||
425 | return -ENOMEM; | ||
426 | } | ||
427 | |||
428 | memset(new_reftable + *reftable_size, 0, | ||
429 | - (new_reftable_size - *reftable_size) * sizeof(uint64_t)); | ||
430 | + (new_reftable_size - *reftable_size) * REFTABLE_ENTRY_SIZE); | ||
431 | |||
432 | *reftable = new_reftable; | ||
433 | *reftable_size = new_reftable_size; | ||
434 | @@ -XXX,XX +XXX,XX @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, | ||
435 | |||
436 | if (new_allocation) { | ||
437 | if (new_reftable_offset) { | ||
438 | - qcow2_free_clusters(bs, new_reftable_offset, | ||
439 | - allocated_reftable_size * sizeof(uint64_t), | ||
440 | - QCOW2_DISCARD_NEVER); | ||
441 | + qcow2_free_clusters( | ||
442 | + bs, new_reftable_offset, | ||
443 | + allocated_reftable_size * REFTABLE_ENTRY_SIZE, | ||
444 | + QCOW2_DISCARD_NEVER); | ||
445 | } | ||
446 | |||
447 | new_reftable_offset = qcow2_alloc_clusters(bs, new_reftable_size * | ||
448 | - sizeof(uint64_t)); | ||
449 | + REFTABLE_ENTRY_SIZE); | ||
450 | if (new_reftable_offset < 0) { | ||
451 | error_setg_errno(errp, -new_reftable_offset, | ||
452 | "Failed to allocate the new reftable"); | ||
453 | @@ -XXX,XX +XXX,XX @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, | ||
454 | |||
455 | /* Write the new reftable */ | ||
456 | ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset, | ||
457 | - new_reftable_size * sizeof(uint64_t), | ||
458 | + new_reftable_size * REFTABLE_ENTRY_SIZE, | ||
459 | false); | ||
460 | if (ret < 0) { | ||
461 | error_setg_errno(errp, -ret, "Overlap check failed"); | ||
462 | @@ -XXX,XX +XXX,XX @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, | ||
463 | } | ||
464 | |||
465 | ret = bdrv_pwrite(bs->file, new_reftable_offset, new_reftable, | ||
466 | - new_reftable_size * sizeof(uint64_t)); | ||
467 | + new_reftable_size * REFTABLE_ENTRY_SIZE); | ||
468 | |||
469 | for (i = 0; i < new_reftable_size; i++) { | ||
470 | be64_to_cpus(&new_reftable[i]); | ||
471 | @@ -XXX,XX +XXX,XX @@ done: | ||
472 | |||
473 | if (new_reftable_offset > 0) { | ||
474 | qcow2_free_clusters(bs, new_reftable_offset, | ||
475 | - new_reftable_size * sizeof(uint64_t), | ||
476 | + new_reftable_size * REFTABLE_ENTRY_SIZE, | ||
477 | QCOW2_DISCARD_OTHER); | ||
478 | } | ||
479 | } | ||
480 | @@ -XXX,XX +XXX,XX @@ int qcow2_shrink_reftable(BlockDriverState *bs) | ||
100 | { | 481 | { |
101 | Error *local_err = NULL; | 482 | BDRVQcow2State *s = bs->opaque; |
483 | uint64_t *reftable_tmp = | ||
484 | - g_malloc(s->refcount_table_size * sizeof(uint64_t)); | ||
485 | + g_malloc(s->refcount_table_size * REFTABLE_ENTRY_SIZE); | ||
486 | int i, ret; | ||
487 | |||
488 | for (i = 0; i < s->refcount_table_size; i++) { | ||
489 | @@ -XXX,XX +XXX,XX @@ int qcow2_shrink_reftable(BlockDriverState *bs) | ||
490 | } | ||
491 | |||
492 | ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset, reftable_tmp, | ||
493 | - s->refcount_table_size * sizeof(uint64_t)); | ||
494 | + s->refcount_table_size * REFTABLE_ENTRY_SIZE); | ||
495 | /* | ||
496 | * If the write in the reftable failed the image may contain a partially | ||
497 | * overwritten reftable. In this case it would be better to clear the | ||
498 | diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c | ||
499 | index XXXXXXX..XXXXXXX 100644 | ||
500 | --- a/block/qcow2-snapshot.c | ||
501 | +++ b/block/qcow2-snapshot.c | ||
502 | @@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||
503 | sn->extra_data_size = sizeof(QCowSnapshotExtraData); | ||
504 | |||
505 | /* Allocate the L1 table of the snapshot and copy the current one there. */ | ||
506 | - l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t)); | ||
507 | + l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * L1E_SIZE); | ||
508 | if (l1_table_offset < 0) { | ||
509 | ret = l1_table_offset; | ||
510 | goto fail; | ||
511 | @@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||
512 | } | ||
513 | |||
514 | ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset, | ||
515 | - s->l1_size * sizeof(uint64_t), false); | ||
516 | + s->l1_size * L1E_SIZE, false); | ||
517 | if (ret < 0) { | ||
518 | goto fail; | ||
519 | } | ||
520 | |||
521 | ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table, | ||
522 | - s->l1_size * sizeof(uint64_t)); | ||
523 | + s->l1_size * L1E_SIZE); | ||
524 | if (ret < 0) { | ||
525 | goto fail; | ||
526 | } | ||
527 | @@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) | ||
528 | sn = &s->snapshots[snapshot_index]; | ||
529 | |||
530 | ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size, | ||
531 | - sizeof(uint64_t), QCOW_MAX_L1_SIZE, | ||
532 | + L1E_SIZE, QCOW_MAX_L1_SIZE, | ||
533 | "Snapshot L1 table", &local_err); | ||
534 | if (ret < 0) { | ||
535 | error_report_err(local_err); | ||
536 | @@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) | ||
537 | goto fail; | ||
538 | } | ||
539 | |||
540 | - cur_l1_bytes = s->l1_size * sizeof(uint64_t); | ||
541 | - sn_l1_bytes = sn->l1_size * sizeof(uint64_t); | ||
542 | + cur_l1_bytes = s->l1_size * L1E_SIZE; | ||
543 | + sn_l1_bytes = sn->l1_size * L1E_SIZE; | ||
544 | |||
545 | /* | ||
546 | * Copy the snapshot L1 table to the current L1 table. | ||
547 | @@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_delete(BlockDriverState *bs, | ||
548 | sn = s->snapshots[snapshot_index]; | ||
549 | |||
550 | ret = qcow2_validate_table(bs, sn.l1_table_offset, sn.l1_size, | ||
551 | - sizeof(uint64_t), QCOW_MAX_L1_SIZE, | ||
552 | + L1E_SIZE, QCOW_MAX_L1_SIZE, | ||
553 | "Snapshot L1 table", errp); | ||
554 | if (ret < 0) { | ||
555 | return ret; | ||
556 | @@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_delete(BlockDriverState *bs, | ||
557 | error_setg_errno(errp, -ret, "Failed to free the cluster and L1 table"); | ||
558 | return ret; | ||
559 | } | ||
560 | - qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t), | ||
561 | + qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * L1E_SIZE, | ||
562 | QCOW2_DISCARD_SNAPSHOT); | ||
563 | |||
564 | /* must update the copied flag on the current cluster offsets */ | ||
565 | @@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, | ||
566 | |||
567 | /* Allocate and read in the snapshot's L1 table */ | ||
568 | ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size, | ||
569 | - sizeof(uint64_t), QCOW_MAX_L1_SIZE, | ||
570 | + L1E_SIZE, QCOW_MAX_L1_SIZE, | ||
571 | "Snapshot L1 table", errp); | ||
572 | if (ret < 0) { | ||
573 | return ret; | ||
574 | } | ||
575 | - new_l1_bytes = sn->l1_size * sizeof(uint64_t); | ||
576 | + new_l1_bytes = sn->l1_size * L1E_SIZE; | ||
577 | new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_bytes); | ||
578 | if (new_l1_table == NULL) { | ||
579 | return -ENOMEM; | ||
580 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
581 | index XXXXXXX..XXXXXXX 100644 | ||
582 | --- a/block/qcow2.c | ||
583 | +++ b/block/qcow2.c | ||
584 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, | ||
585 | |||
586 | /* read the level 1 table */ | ||
587 | ret = qcow2_validate_table(bs, header.l1_table_offset, | ||
588 | - header.l1_size, sizeof(uint64_t), | ||
589 | + header.l1_size, L1E_SIZE, | ||
590 | QCOW_MAX_L1_SIZE, "Active L1 table", errp); | ||
591 | if (ret < 0) { | ||
592 | goto fail; | ||
593 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, | ||
594 | } | ||
595 | |||
596 | if (s->l1_size > 0) { | ||
597 | - s->l1_table = qemu_try_blockalign(bs->file->bs, | ||
598 | - s->l1_size * sizeof(uint64_t)); | ||
599 | + s->l1_table = qemu_try_blockalign(bs->file->bs, s->l1_size * L1E_SIZE); | ||
600 | if (s->l1_table == NULL) { | ||
601 | error_setg(errp, "Could not allocate L1 table"); | ||
602 | ret = -ENOMEM; | ||
603 | goto fail; | ||
604 | } | ||
605 | ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, | ||
606 | - s->l1_size * sizeof(uint64_t)); | ||
607 | + s->l1_size * L1E_SIZE); | ||
608 | if (ret < 0) { | ||
609 | error_setg_errno(errp, -ret, "Could not read L1 table"); | ||
610 | goto fail; | ||
611 | @@ -XXX,XX +XXX,XX @@ int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size, | ||
612 | * where no further refcount blocks or table clusters are required to | ||
613 | * reference count every cluster. | ||
614 | */ | ||
615 | - int64_t blocks_per_table_cluster = cluster_size / sizeof(uint64_t); | ||
616 | + int64_t blocks_per_table_cluster = cluster_size / REFTABLE_ENTRY_SIZE; | ||
617 | int64_t refcounts_per_block = cluster_size * 8 / (1 << refcount_order); | ||
618 | int64_t table = 0; /* number of refcount table clusters */ | ||
619 | int64_t blocks = 0; /* number of refcount block clusters */ | ||
620 | @@ -XXX,XX +XXX,XX @@ static int64_t qcow2_calc_prealloc_size(int64_t total_size, | ||
621 | |||
622 | /* total size of L1 tables */ | ||
623 | nl1e = nl2e * l2e_size / cluster_size; | ||
624 | - nl1e = ROUND_UP(nl1e, cluster_size / sizeof(uint64_t)); | ||
625 | - meta_size += nl1e * sizeof(uint64_t); | ||
626 | + nl1e = ROUND_UP(nl1e, cluster_size / L1E_SIZE); | ||
627 | + meta_size += nl1e * L1E_SIZE; | ||
628 | |||
629 | /* total size of refcount table and blocks */ | ||
630 | meta_size += qcow2_refcount_metadata_size( | ||
631 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, | ||
632 | /* write updated header.size */ | ||
633 | offset = cpu_to_be64(offset); | ||
634 | ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), | ||
635 | - &offset, sizeof(uint64_t)); | ||
636 | + &offset, sizeof(offset)); | ||
637 | if (ret < 0) { | ||
638 | error_setg_errno(errp, -ret, "Failed to update the image size"); | ||
639 | goto fail; | ||
640 | @@ -XXX,XX +XXX,XX @@ static int make_completely_empty(BlockDriverState *bs) | ||
641 | |||
642 | BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); | ||
643 | |||
644 | - l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t)); | ||
645 | - l1_size2 = (uint64_t)s->l1_size * sizeof(uint64_t); | ||
646 | + l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / L1E_SIZE); | ||
647 | + l1_size2 = (uint64_t)s->l1_size * L1E_SIZE; | ||
648 | |||
649 | /* After this call, neither the in-memory nor the on-disk refcount | ||
650 | * information accurately describe the actual references */ | ||
651 | @@ -XXX,XX +XXX,XX @@ static int make_completely_empty(BlockDriverState *bs) | ||
652 | |||
653 | s->l1_table_offset = 3 * s->cluster_size; | ||
654 | |||
655 | - new_reftable = g_try_new0(uint64_t, s->cluster_size / sizeof(uint64_t)); | ||
656 | + new_reftable = g_try_new0(uint64_t, s->cluster_size / REFTABLE_ENTRY_SIZE); | ||
657 | if (!new_reftable) { | ||
658 | ret = -ENOMEM; | ||
659 | goto fail_broken_refcounts; | ||
660 | } | ||
661 | |||
662 | s->refcount_table_offset = s->cluster_size; | ||
663 | - s->refcount_table_size = s->cluster_size / sizeof(uint64_t); | ||
664 | + s->refcount_table_size = s->cluster_size / REFTABLE_ENTRY_SIZE; | ||
665 | s->max_refcount_table_index = 0; | ||
666 | |||
667 | g_free(s->refcount_table); | ||
668 | @@ -XXX,XX +XXX,XX @@ static int qcow2_make_empty(BlockDriverState *bs) | ||
669 | int step = QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size); | ||
670 | int l1_clusters, ret = 0; | ||
671 | |||
672 | - l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t)); | ||
673 | + l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / L1E_SIZE); | ||
674 | |||
675 | if (s->qcow_version >= 3 && !s->snapshots && !s->nb_bitmaps && | ||
676 | 3 + l1_clusters <= s->refcount_block_size && | ||
677 | @@ -XXX,XX +XXX,XX @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, | ||
678 | l2e_size = extended_l2 ? L2E_SIZE_EXTENDED : L2E_SIZE_NORMAL; | ||
679 | l2_tables = DIV_ROUND_UP(virtual_size / cluster_size, | ||
680 | cluster_size / l2e_size); | ||
681 | - if (l2_tables * sizeof(uint64_t) > QCOW_MAX_L1_SIZE) { | ||
682 | + if (l2_tables * L1E_SIZE > QCOW_MAX_L1_SIZE) { | ||
683 | error_setg(&local_err, "The image size is too large " | ||
684 | "(try using a larger cluster size)"); | ||
685 | goto err; | ||
102 | -- | 686 | -- |
103 | 2.17.1 | 687 | 2.26.2 |
104 | 688 | ||
105 | 689 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: Alberto Garcia <berto@igalia.com> |
---|---|---|---|
2 | 2 | ||
3 | Utilize the job_exit shim by not calling job_defer_to_main_loop, and | 3 | When a write request needs to allocate new clusters (or change the L2 |
4 | where applicable, converting the deferred callback into the job_exit | 4 | bitmap of existing ones) a QCowL2Meta structure is created so the L2 |
5 | callback. | 5 | metadata can be later updated and any copy-on-write can be performed |
6 | if necessary. | ||
6 | 7 | ||
7 | This converts backup, stream, create, and the unit tests all at once. | 8 | A write request can span a region consisting of an arbitrary |
8 | Most of these jobs do not see any changes to the order in which they | 9 | combination of previously unallocated and allocated clusters, and if |
9 | clean up their resources, except the test-blockjob-txn test, which | 10 | the unallocated ones can be put contiguous to the existing ones then |
10 | now puts down its bs before job_completed is called. | 11 | QEMU will do so in order to minimize the number of write operations. |
11 | 12 | ||
12 | This is safe for the same reason the reordering in the mirror job is | 13 | In practice this means that a write request has not just one but a |
13 | safe, because job_completed no longer runs under two locks, making | 14 | number of QCowL2Meta structures. All of them are added to the |
14 | the unref safe even if it causes a flush. | 15 | cluster_allocs list that is stored in BDRVQcow2State and is used to |
16 | detect overlapping requests. After the write request finishes all its | ||
17 | associated QCowL2Meta are removed from that list. calculate_l2_meta() | ||
18 | takes care of creating and putting those structures in the list, and | ||
19 | qcow2_handle_l2meta() takes care of removing them. | ||
15 | 20 | ||
16 | Signed-off-by: John Snow <jsnow@redhat.com> | 21 | The problem is that the error path in handle_alloc() also tries to |
17 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 22 | remove an item in that list, a remnant from the time when this was |
18 | Message-id: 20180830015734.19765-7-jsnow@redhat.com | 23 | handled there (that code would not even be correct anymore because |
24 | it only removes one struct and not all the ones from the same write | ||
25 | request). | ||
26 | |||
27 | This can trigger a double removal of the same item from the list, | ||
28 | causing a crash. This is not easy to reproduce in practice because | ||
29 | it requires that do_alloc_cluster_offset() fails after a successful | ||
30 | previous allocation during the same write request, but it can be | ||
31 | reproduced with the included test case. | ||
32 | |||
33 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
34 | Message-Id: <3440a1c4d53c4fe48312b478c96accb338cbef7c.1599150873.git.berto@igalia.com> | ||
19 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 35 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
20 | --- | 36 | --- |
21 | block/backup.c | 16 ---------------- | 37 | block/qcow2-cluster.c | 3 -- |
22 | block/create.c | 14 +++----------- | 38 | tests/qemu-iotests/305 | 74 ++++++++++++++++++++++++++++++++++++++ |
23 | block/stream.c | 22 +++++++--------------- | 39 | tests/qemu-iotests/305.out | 16 +++++++++ |
24 | tests/test-bdrv-drain.c | 6 ------ | 40 | tests/qemu-iotests/group | 1 + |
25 | tests/test-blockjob-txn.c | 11 ++--------- | 41 | 4 files changed, 91 insertions(+), 3 deletions(-) |
26 | tests/test-blockjob.c | 10 ++++------ | 42 | create mode 100755 tests/qemu-iotests/305 |
27 | 6 files changed, 16 insertions(+), 63 deletions(-) | 43 | create mode 100644 tests/qemu-iotests/305.out |
28 | 44 | ||
29 | diff --git a/block/backup.c b/block/backup.c | 45 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c |
30 | index XXXXXXX..XXXXXXX 100644 | 46 | index XXXXXXX..XXXXXXX 100644 |
31 | --- a/block/backup.c | 47 | --- a/block/qcow2-cluster.c |
32 | +++ b/block/backup.c | 48 | +++ b/block/qcow2-cluster.c |
33 | @@ -XXX,XX +XXX,XX @@ static BlockErrorAction backup_error_action(BackupBlockJob *job, | 49 | @@ -XXX,XX +XXX,XX @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, |
34 | } | 50 | |
35 | } | 51 | out: |
36 | 52 | qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | |
37 | -typedef struct { | 53 | - if (ret < 0 && *m && (*m)->nb_clusters > 0) { |
38 | - int ret; | 54 | - QLIST_REMOVE(*m, next_in_flight); |
39 | -} BackupCompleteData; | 55 | - } |
40 | - | ||
41 | -static void backup_complete(Job *job, void *opaque) | ||
42 | -{ | ||
43 | - BackupCompleteData *data = opaque; | ||
44 | - | ||
45 | - job_completed(job, data->ret); | ||
46 | - g_free(data); | ||
47 | -} | ||
48 | - | ||
49 | static bool coroutine_fn yield_and_check(BackupBlockJob *job) | ||
50 | { | ||
51 | uint64_t delay_ns; | ||
52 | @@ -XXX,XX +XXX,XX @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) | ||
53 | static int coroutine_fn backup_run(Job *opaque_job, Error **errp) | ||
54 | { | ||
55 | BackupBlockJob *job = container_of(opaque_job, BackupBlockJob, common.job); | ||
56 | - BackupCompleteData *data; | ||
57 | BlockDriverState *bs = blk_bs(job->common.blk); | ||
58 | int64_t offset, nb_clusters; | ||
59 | int ret = 0; | ||
60 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_run(Job *opaque_job, Error **errp) | ||
61 | qemu_co_rwlock_unlock(&job->flush_rwlock); | ||
62 | hbitmap_free(job->copy_bitmap); | ||
63 | |||
64 | - data = g_malloc(sizeof(*data)); | ||
65 | - data->ret = ret; | ||
66 | - job_defer_to_main_loop(&job->common.job, backup_complete, data); | ||
67 | return ret; | 56 | return ret; |
68 | } | 57 | } |
69 | 58 | ||
70 | diff --git a/block/create.c b/block/create.c | 59 | diff --git a/tests/qemu-iotests/305 b/tests/qemu-iotests/305 |
60 | new file mode 100755 | ||
61 | index XXXXXXX..XXXXXXX | ||
62 | --- /dev/null | ||
63 | +++ b/tests/qemu-iotests/305 | ||
64 | @@ -XXX,XX +XXX,XX @@ | ||
65 | +#!/usr/bin/env bash | ||
66 | +# | ||
67 | +# Test the handling of errors in write requests with multiple allocations | ||
68 | +# | ||
69 | +# Copyright (C) 2020 Igalia, S.L. | ||
70 | +# Author: Alberto Garcia <berto@igalia.com> | ||
71 | +# | ||
72 | +# This program is free software; you can redistribute it and/or modify | ||
73 | +# it under the terms of the GNU General Public License as published by | ||
74 | +# the Free Software Foundation; either version 2 of the License, or | ||
75 | +# (at your option) any later version. | ||
76 | +# | ||
77 | +# This program is distributed in the hope that it will be useful, | ||
78 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
79 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
80 | +# GNU General Public License for more details. | ||
81 | +# | ||
82 | +# You should have received a copy of the GNU General Public License | ||
83 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
84 | +# | ||
85 | + | ||
86 | +# creator | ||
87 | +owner=berto@igalia.com | ||
88 | + | ||
89 | +seq=`basename $0` | ||
90 | +echo "QA output created by $seq" | ||
91 | + | ||
92 | +status=1 # failure is the default! | ||
93 | + | ||
94 | +_cleanup() | ||
95 | +{ | ||
96 | + _cleanup_test_img | ||
97 | +} | ||
98 | +trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
99 | + | ||
100 | +# get standard environment, filters and checks | ||
101 | +. ./common.rc | ||
102 | +. ./common.filter | ||
103 | + | ||
104 | +_supported_fmt qcow2 | ||
105 | +_supported_proto file | ||
106 | +_supported_os Linux | ||
107 | +_unsupported_imgopts cluster_size refcount_bits extended_l2 compat=0.10 data_file | ||
108 | + | ||
109 | +echo '### Create the image' | ||
110 | +_make_test_img -o refcount_bits=64,cluster_size=1k 1M | ||
111 | + | ||
112 | +# The reference counts of the clusters for the first 123k of this | ||
113 | +# write request are stored in the first refcount block. The last | ||
114 | +# cluster (guest offset 123k) is referenced in the second refcount | ||
115 | +# block. | ||
116 | +echo '### Fill the first refcount block and one data cluster from the second' | ||
117 | +$QEMU_IO -c 'write 0 124k' "$TEST_IMG" | _filter_qemu_io | ||
118 | + | ||
119 | +echo '### Discard two of the last data clusters, leave one in the middle' | ||
120 | +$QEMU_IO -c 'discard 121k 1k' "$TEST_IMG" | _filter_qemu_io | ||
121 | +$QEMU_IO -c 'discard 123k 1k' "$TEST_IMG" | _filter_qemu_io | ||
122 | + | ||
123 | +echo '### Corrupt the offset of the second refcount block' | ||
124 | +refcount_table_offset=$(peek_file_be "$TEST_IMG" 48 8) | ||
125 | +poke_file "$TEST_IMG" $(($refcount_table_offset+14)) "\x06" | ||
126 | + | ||
127 | +# This tries to allocate the two clusters discarded earlier (guest | ||
128 | +# offsets 121k and 123k). Their reference counts are in the first and | ||
129 | +# second refcount blocks respectively, but only the first one can be | ||
130 | +# allocated correctly because the second entry of the refcount table | ||
131 | +# is corrupted. | ||
132 | +echo '### Try to allocate the discarded clusters again' | ||
133 | +$QEMU_IO -c 'write 121k 3k' "$TEST_IMG" | _filter_qemu_io | ||
134 | + | ||
135 | +# success, all done | ||
136 | +echo "*** done" | ||
137 | +rm -f $seq.full | ||
138 | +status=0 | ||
139 | diff --git a/tests/qemu-iotests/305.out b/tests/qemu-iotests/305.out | ||
140 | new file mode 100644 | ||
141 | index XXXXXXX..XXXXXXX | ||
142 | --- /dev/null | ||
143 | +++ b/tests/qemu-iotests/305.out | ||
144 | @@ -XXX,XX +XXX,XX @@ | ||
145 | +QA output created by 305 | ||
146 | +### Create the image | ||
147 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 | ||
148 | +### Fill the first refcount block and one data cluster from the second | ||
149 | +wrote 126976/126976 bytes at offset 0 | ||
150 | +124 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
151 | +### Discard two of the last data clusters, leave one in the middle | ||
152 | +discard 1024/1024 bytes at offset 123904 | ||
153 | +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
154 | +discard 1024/1024 bytes at offset 125952 | ||
155 | +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
156 | +### Corrupt the offset of the second refcount block | ||
157 | +### Try to allocate the discarded clusters again | ||
158 | +qcow2: Marking image as corrupt: Refblock offset 0x20600 unaligned (reftable index: 0x1); further corruption events will be suppressed | ||
159 | +write failed: Input/output error | ||
160 | +*** done | ||
161 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group | ||
71 | index XXXXXXX..XXXXXXX 100644 | 162 | index XXXXXXX..XXXXXXX 100644 |
72 | --- a/block/create.c | 163 | --- a/tests/qemu-iotests/group |
73 | +++ b/block/create.c | 164 | +++ b/tests/qemu-iotests/group |
74 | @@ -XXX,XX +XXX,XX @@ typedef struct BlockdevCreateJob { | 165 | @@ -XXX,XX +XXX,XX @@ |
75 | Job common; | 166 | 302 quick |
76 | BlockDriver *drv; | 167 | 303 rw quick |
77 | BlockdevCreateOptions *opts; | 168 | 304 rw quick |
78 | - int ret; | 169 | +305 rw quick |
79 | } BlockdevCreateJob; | ||
80 | |||
81 | -static void blockdev_create_complete(Job *job, void *opaque) | ||
82 | -{ | ||
83 | - BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); | ||
84 | - | ||
85 | - job_completed(job, s->ret); | ||
86 | -} | ||
87 | - | ||
88 | static int coroutine_fn blockdev_create_run(Job *job, Error **errp) | ||
89 | { | ||
90 | BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); | ||
91 | + int ret; | ||
92 | |||
93 | job_progress_set_remaining(&s->common, 1); | ||
94 | - s->ret = s->drv->bdrv_co_create(s->opts, errp); | ||
95 | + ret = s->drv->bdrv_co_create(s->opts, errp); | ||
96 | job_progress_update(&s->common, 1); | ||
97 | |||
98 | qapi_free_BlockdevCreateOptions(s->opts); | ||
99 | - job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL); | ||
100 | |||
101 | - return s->ret; | ||
102 | + return ret; | ||
103 | } | ||
104 | |||
105 | static const JobDriver blockdev_create_job_driver = { | ||
106 | diff --git a/block/stream.c b/block/stream.c | ||
107 | index XXXXXXX..XXXXXXX 100644 | ||
108 | --- a/block/stream.c | ||
109 | +++ b/block/stream.c | ||
110 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_populate(BlockBackend *blk, | ||
111 | return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ); | ||
112 | } | ||
113 | |||
114 | -typedef struct { | ||
115 | - int ret; | ||
116 | -} StreamCompleteData; | ||
117 | - | ||
118 | -static void stream_complete(Job *job, void *opaque) | ||
119 | +static void stream_exit(Job *job) | ||
120 | { | ||
121 | StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); | ||
122 | BlockJob *bjob = &s->common; | ||
123 | - StreamCompleteData *data = opaque; | ||
124 | BlockDriverState *bs = blk_bs(bjob->blk); | ||
125 | BlockDriverState *base = s->base; | ||
126 | Error *local_err = NULL; | ||
127 | + int ret = job->ret; | ||
128 | |||
129 | - if (!job_is_cancelled(job) && bs->backing && data->ret == 0) { | ||
130 | + if (!job_is_cancelled(job) && bs->backing && ret == 0) { | ||
131 | const char *base_id = NULL, *base_fmt = NULL; | ||
132 | if (base) { | ||
133 | base_id = s->backing_file_str; | ||
134 | @@ -XXX,XX +XXX,XX @@ static void stream_complete(Job *job, void *opaque) | ||
135 | base_fmt = base->drv->format_name; | ||
136 | } | ||
137 | } | ||
138 | - data->ret = bdrv_change_backing_file(bs, base_id, base_fmt); | ||
139 | + ret = bdrv_change_backing_file(bs, base_id, base_fmt); | ||
140 | bdrv_set_backing_hd(bs, base, &local_err); | ||
141 | if (local_err) { | ||
142 | error_report_err(local_err); | ||
143 | - data->ret = -EPERM; | ||
144 | + ret = -EPERM; | ||
145 | goto out; | ||
146 | } | ||
147 | } | ||
148 | @@ -XXX,XX +XXX,XX @@ out: | ||
149 | } | ||
150 | |||
151 | g_free(s->backing_file_str); | ||
152 | - job_completed(job, data->ret); | ||
153 | - g_free(data); | ||
154 | + job->ret = ret; | ||
155 | } | ||
156 | |||
157 | static int coroutine_fn stream_run(Job *job, Error **errp) | ||
158 | { | ||
159 | StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); | ||
160 | - StreamCompleteData *data; | ||
161 | BlockBackend *blk = s->common.blk; | ||
162 | BlockDriverState *bs = blk_bs(blk); | ||
163 | BlockDriverState *base = s->base; | ||
164 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp) | ||
165 | |||
166 | out: | ||
167 | /* Modify backing chain and close BDSes in main loop */ | ||
168 | - data = g_malloc(sizeof(*data)); | ||
169 | - data->ret = ret; | ||
170 | - job_defer_to_main_loop(&s->common.job, stream_complete, data); | ||
171 | return ret; | ||
172 | } | ||
173 | |||
174 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = { | ||
175 | .job_type = JOB_TYPE_STREAM, | ||
176 | .free = block_job_free, | ||
177 | .run = stream_run, | ||
178 | + .exit = stream_exit, | ||
179 | .user_resume = block_job_user_resume, | ||
180 | .drain = block_job_drain, | ||
181 | }, | ||
182 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c | ||
183 | index XXXXXXX..XXXXXXX 100644 | ||
184 | --- a/tests/test-bdrv-drain.c | ||
185 | +++ b/tests/test-bdrv-drain.c | ||
186 | @@ -XXX,XX +XXX,XX @@ typedef struct TestBlockJob { | ||
187 | bool should_complete; | ||
188 | } TestBlockJob; | ||
189 | |||
190 | -static void test_job_completed(Job *job, void *opaque) | ||
191 | -{ | ||
192 | - job_completed(job, 0); | ||
193 | -} | ||
194 | - | ||
195 | static int coroutine_fn test_job_run(Job *job, Error **errp) | ||
196 | { | ||
197 | TestBlockJob *s = container_of(job, TestBlockJob, common.job); | ||
198 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn test_job_run(Job *job, Error **errp) | ||
199 | job_pause_point(&s->common.job); | ||
200 | } | ||
201 | |||
202 | - job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c | ||
207 | index XXXXXXX..XXXXXXX 100644 | ||
208 | --- a/tests/test-blockjob-txn.c | ||
209 | +++ b/tests/test-blockjob-txn.c | ||
210 | @@ -XXX,XX +XXX,XX @@ typedef struct { | ||
211 | int *result; | ||
212 | } TestBlockJob; | ||
213 | |||
214 | -static void test_block_job_complete(Job *job, void *opaque) | ||
215 | +static void test_block_job_exit(Job *job) | ||
216 | { | ||
217 | BlockJob *bjob = container_of(job, BlockJob, job); | ||
218 | BlockDriverState *bs = blk_bs(bjob->blk); | ||
219 | - int rc = (intptr_t)opaque; | ||
220 | |||
221 | - if (job_is_cancelled(job)) { | ||
222 | - rc = -ECANCELED; | ||
223 | - } | ||
224 | - | ||
225 | - job_completed(job, rc); | ||
226 | bdrv_unref(bs); | ||
227 | } | ||
228 | |||
229 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn test_block_job_run(Job *job, Error **errp) | ||
230 | } | ||
231 | } | ||
232 | |||
233 | - job_defer_to_main_loop(job, test_block_job_complete, | ||
234 | - (void *)(intptr_t)s->rc); | ||
235 | return s->rc; | ||
236 | } | ||
237 | |||
238 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = { | ||
239 | .user_resume = block_job_user_resume, | ||
240 | .drain = block_job_drain, | ||
241 | .run = test_block_job_run, | ||
242 | + .exit = test_block_job_exit, | ||
243 | }, | ||
244 | }; | ||
245 | |||
246 | diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c | ||
247 | index XXXXXXX..XXXXXXX 100644 | ||
248 | --- a/tests/test-blockjob.c | ||
249 | +++ b/tests/test-blockjob.c | ||
250 | @@ -XXX,XX +XXX,XX @@ typedef struct CancelJob { | ||
251 | bool completed; | ||
252 | } CancelJob; | ||
253 | |||
254 | -static void cancel_job_completed(Job *job, void *opaque) | ||
255 | +static void cancel_job_exit(Job *job) | ||
256 | { | ||
257 | - CancelJob *s = opaque; | ||
258 | + CancelJob *s = container_of(job, CancelJob, common.job); | ||
259 | s->completed = true; | ||
260 | - job_completed(job, 0); | ||
261 | } | ||
262 | |||
263 | static void cancel_job_complete(Job *job, Error **errp) | ||
264 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn cancel_job_run(Job *job, Error **errp) | ||
265 | |||
266 | while (!s->should_complete) { | ||
267 | if (job_is_cancelled(&s->common.job)) { | ||
268 | - goto defer; | ||
269 | + return 0; | ||
270 | } | ||
271 | |||
272 | if (!job_is_ready(&s->common.job) && s->should_converge) { | ||
273 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn cancel_job_run(Job *job, Error **errp) | ||
274 | job_sleep_ns(&s->common.job, 100000); | ||
275 | } | ||
276 | |||
277 | - defer: | ||
278 | - job_defer_to_main_loop(&s->common.job, cancel_job_completed, s); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_cancel_driver = { | ||
283 | .user_resume = block_job_user_resume, | ||
284 | .drain = block_job_drain, | ||
285 | .run = cancel_job_run, | ||
286 | + .exit = cancel_job_exit, | ||
287 | .complete = cancel_job_complete, | ||
288 | }, | ||
289 | }; | ||
290 | -- | 170 | -- |
291 | 2.17.1 | 171 | 2.26.2 |
292 | 172 | ||
293 | 173 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
1 | 2 | ||
3 | In the past, when a new cluster was allocated the l2meta structure was | ||
4 | a variable in the stack so it was necessary to have a way to tell | ||
5 | whether it had been initialized and contained valid data or not. The | ||
6 | nb_clusters field was used for this purpose. Since commit f50f88b9fe | ||
7 | this is no longer the case, l2meta (nowadays a pointer to a list) is | ||
8 | only allocated when needed and nb_clusters is guaranteed to be > 0 so | ||
9 | this check is unnecessary. | ||
10 | |||
11 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
12 | Message-Id: <ab0b67c29c7ba26e598db35f12aa5ab5982539c1.1599150873.git.berto@igalia.com> | ||
13 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
14 | --- | ||
15 | block/qcow2.c | 4 +--- | ||
16 | 1 file changed, 1 insertion(+), 3 deletions(-) | ||
17 | |||
18 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
19 | index XXXXXXX..XXXXXXX 100644 | ||
20 | --- a/block/qcow2.c | ||
21 | +++ b/block/qcow2.c | ||
22 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs, | ||
23 | } | ||
24 | |||
25 | /* Take the request off the list of running requests */ | ||
26 | - if (l2meta->nb_clusters != 0) { | ||
27 | - QLIST_REMOVE(l2meta, next_in_flight); | ||
28 | - } | ||
29 | + QLIST_REMOVE(l2meta, next_in_flight); | ||
30 | |||
31 | qemu_co_queue_restart_all(&l2meta->dependent_requests); | ||
32 | |||
33 | -- | ||
34 | 2.26.2 | ||
35 | |||
36 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: Alberto Garcia <berto@igalia.com> |
---|---|---|---|
2 | 2 | ||
3 | Change the manual deferment to mirror_exit into the implicit | 3 | The current text corresponds to an earlier, simpler version of this |
4 | callback to job_exit and the mirror_exit callback. | 4 | function and it does not explain how it works now. |
5 | 5 | ||
6 | This does change the order of some bdrv_unref calls and job_completed, | 6 | Signed-off-by: Alberto Garcia <berto@igalia.com> |
7 | but thanks to the new context in which we call .exit, this is safe to | 7 | Message-Id: <bb5bd06f07c5a05b0818611de0d06ec5b66c8df3.1599150873.git.berto@igalia.com> |
8 | defer the possible flushing of any nodes to the job_finalize_single | ||
9 | cleanup stage. | ||
10 | |||
11 | Signed-off-by: John Snow <jsnow@redhat.com> | ||
12 | Message-id: 20180830015734.19765-6-jsnow@redhat.com | ||
13 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
14 | Reviewed-by: Jeff Cody <jcody@redhat.com> | ||
15 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 8 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
16 | --- | 9 | --- |
17 | block/mirror.c | 29 +++++++++++------------------ | 10 | block/qcow2-cluster.c | 24 ++++++++++++++---------- |
18 | 1 file changed, 11 insertions(+), 18 deletions(-) | 11 | 1 file changed, 14 insertions(+), 10 deletions(-) |
19 | 12 | ||
20 | diff --git a/block/mirror.c b/block/mirror.c | 13 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c |
21 | index XXXXXXX..XXXXXXX 100644 | 14 | index XXXXXXX..XXXXXXX 100644 |
22 | --- a/block/mirror.c | 15 | --- a/block/qcow2-cluster.c |
23 | +++ b/block/mirror.c | 16 | +++ b/block/qcow2-cluster.c |
24 | @@ -XXX,XX +XXX,XX @@ static void mirror_wait_for_all_io(MirrorBlockJob *s) | 17 | @@ -XXX,XX +XXX,XX @@ out: |
25 | } | ||
26 | } | 18 | } |
27 | 19 | ||
28 | -typedef struct { | 20 | /* |
29 | - int ret; | 21 | - * alloc_cluster_offset |
30 | -} MirrorExitData; | 22 | + * For a given area on the virtual disk defined by @offset and @bytes, |
31 | - | 23 | + * find the corresponding area on the qcow2 image, allocating new |
32 | -static void mirror_exit(Job *job, void *opaque) | 24 | + * clusters (or subclusters) if necessary. The result can span a |
33 | +static void mirror_exit(Job *job) | 25 | + * combination of allocated and previously unallocated clusters. |
34 | { | 26 | * |
35 | MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); | 27 | - * For a given offset on the virtual disk, find the cluster offset in qcow2 |
36 | BlockJob *bjob = &s->common; | 28 | - * file. If the offset is not found, allocate a new cluster. |
37 | - MirrorExitData *data = opaque; | 29 | + * On return, @host_offset is set to the beginning of the requested |
38 | MirrorBDSOpaque *bs_opaque = s->mirror_top_bs->opaque; | 30 | + * area. This area is guaranteed to be contiguous on the qcow2 file |
39 | AioContext *replace_aio_context = NULL; | 31 | + * but it can be smaller than initially requested. In this case @bytes |
40 | BlockDriverState *src = s->mirror_top_bs->backing->bs; | 32 | + * is updated with the actual size. |
41 | BlockDriverState *target_bs = blk_bs(s->target); | 33 | * |
42 | BlockDriverState *mirror_top_bs = s->mirror_top_bs; | 34 | - * If the cluster was already allocated, m->nb_clusters is set to 0 and |
43 | Error *local_err = NULL; | 35 | - * other fields in m are meaningless. |
44 | + int ret = job->ret; | 36 | - * |
45 | 37 | - * If the cluster is newly allocated, m->nb_clusters is set to the number of | |
46 | bdrv_release_dirty_bitmap(src, s->dirty_bitmap); | 38 | - * contiguous clusters that have been allocated. In this case, the other |
47 | 39 | - * fields of m are valid and contain information about the first allocated | |
48 | - /* Make sure that the source BDS doesn't go away before we called | 40 | - * cluster. |
49 | - * job_completed(). */ | 41 | + * If any clusters or subclusters were allocated then @m contains a |
50 | + /* Make sure that the source BDS doesn't go away during bdrv_replace_node, | 42 | + * list with the information of all the affected regions. Note that |
51 | + * before we can call bdrv_drained_end */ | 43 | + * this can happen regardless of whether this function succeeds or |
52 | bdrv_ref(src); | 44 | + * not. The caller is responsible for updating the L2 metadata of the |
53 | bdrv_ref(mirror_top_bs); | 45 | + * allocated clusters (on success) or freeing them (on failure), and |
54 | bdrv_ref(target_bs); | 46 | + * for clearing the contents of @m afterwards in both cases. |
55 | @@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque) | 47 | * |
56 | bdrv_set_backing_hd(target_bs, backing, &local_err); | 48 | * If the request conflicts with another write request in flight, the coroutine |
57 | if (local_err) { | 49 | * is queued and will be reentered when the dependency has completed. |
58 | error_report_err(local_err); | ||
59 | - data->ret = -EPERM; | ||
60 | + ret = -EPERM; | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | @@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque) | ||
65 | aio_context_acquire(replace_aio_context); | ||
66 | } | ||
67 | |||
68 | - if (s->should_complete && data->ret == 0) { | ||
69 | + if (s->should_complete && ret == 0) { | ||
70 | BlockDriverState *to_replace = src; | ||
71 | if (s->to_replace) { | ||
72 | to_replace = s->to_replace; | ||
73 | @@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque) | ||
74 | bdrv_drained_end(target_bs); | ||
75 | if (local_err) { | ||
76 | error_report_err(local_err); | ||
77 | - data->ret = -EPERM; | ||
78 | + ret = -EPERM; | ||
79 | } | ||
80 | } | ||
81 | if (s->to_replace) { | ||
82 | @@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque) | ||
83 | blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); | ||
84 | |||
85 | bs_opaque->job = NULL; | ||
86 | - job_completed(job, data->ret); | ||
87 | |||
88 | - g_free(data); | ||
89 | bdrv_drained_end(src); | ||
90 | bdrv_unref(mirror_top_bs); | ||
91 | bdrv_unref(src); | ||
92 | + | ||
93 | + job->ret = ret; | ||
94 | } | ||
95 | |||
96 | static void mirror_throttle(MirrorBlockJob *s) | ||
97 | @@ -XXX,XX +XXX,XX @@ static int mirror_flush(MirrorBlockJob *s) | ||
98 | static int coroutine_fn mirror_run(Job *job, Error **errp) | ||
99 | { | ||
100 | MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); | ||
101 | - MirrorExitData *data; | ||
102 | BlockDriverState *bs = s->mirror_top_bs->backing->bs; | ||
103 | BlockDriverState *target_bs = blk_bs(s->target); | ||
104 | bool need_drain = true; | ||
105 | @@ -XXX,XX +XXX,XX @@ immediate_exit: | ||
106 | g_free(s->in_flight_bitmap); | ||
107 | bdrv_dirty_iter_free(s->dbi); | ||
108 | |||
109 | - data = g_malloc(sizeof(*data)); | ||
110 | - data->ret = ret; | ||
111 | - | ||
112 | if (need_drain) { | ||
113 | bdrv_drained_begin(bs); | ||
114 | } | ||
115 | |||
116 | - job_defer_to_main_loop(&s->common.job, mirror_exit, data); | ||
117 | return ret; | ||
118 | } | ||
119 | |||
120 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = { | ||
121 | .user_resume = block_job_user_resume, | ||
122 | .drain = block_job_drain, | ||
123 | .run = mirror_run, | ||
124 | + .exit = mirror_exit, | ||
125 | .pause = mirror_pause, | ||
126 | .complete = mirror_complete, | ||
127 | }, | ||
128 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = { | ||
129 | .user_resume = block_job_user_resume, | ||
130 | .drain = block_job_drain, | ||
131 | .run = mirror_run, | ||
132 | + .exit = mirror_exit, | ||
133 | .pause = mirror_pause, | ||
134 | .complete = mirror_complete, | ||
135 | }, | ||
136 | -- | 50 | -- |
137 | 2.17.1 | 51 | 2.26.2 |
138 | 52 | ||
139 | 53 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: Yi Li <yili@winhong.com> |
---|---|---|---|
2 | 2 | ||
3 | Presently we codify the entry point for a job as the "start" callback, | 3 | Signed-off-by: Yi Li <yili@winhong.com> |
4 | but a more apt name would be "run" to clarify the idea that when this | 4 | Message-Id: <20200819013607.32280-1-yili@winhong.com> |
5 | function returns we consider the job to have "finished," except for | 5 | Reviewed-by: Alberto Garcia <berto@igalia.com> |
6 | any cleanup which occurs in separate callbacks later. | 6 | Reviewed-by: Stefano Garzarella <sgarzare@redhat.com> |
7 | |||
8 | As part of this clarification, change the signature to include an error | ||
9 | object and a return code. The error ptr is not yet used, and the return | ||
10 | code while captured, will be overwritten by actions in the job_completed | ||
11 | function. | ||
12 | |||
13 | Signed-off-by: John Snow <jsnow@redhat.com> | ||
14 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
15 | Message-id: 20180830015734.19765-2-jsnow@redhat.com | ||
16 | Reviewed-by: Jeff Cody <jcody@redhat.com> | ||
17 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 7 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
18 | --- | 8 | --- |
19 | include/qemu/job.h | 2 +- | 9 | qemu-img.c | 10 +++++----- |
20 | block/backup.c | 7 ++++--- | 10 | 1 file changed, 5 insertions(+), 5 deletions(-) |
21 | block/commit.c | 7 ++++--- | ||
22 | block/create.c | 8 +++++--- | ||
23 | block/mirror.c | 10 ++++++---- | ||
24 | block/stream.c | 7 ++++--- | ||
25 | job.c | 6 +++--- | ||
26 | tests/test-bdrv-drain.c | 7 ++++--- | ||
27 | tests/test-blockjob-txn.c | 16 ++++++++-------- | ||
28 | tests/test-blockjob.c | 7 ++++--- | ||
29 | 10 files changed, 43 insertions(+), 34 deletions(-) | ||
30 | 11 | ||
31 | diff --git a/include/qemu/job.h b/include/qemu/job.h | 12 | diff --git a/qemu-img.c b/qemu-img.c |
32 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
33 | --- a/include/qemu/job.h | 14 | --- a/qemu-img.c |
34 | +++ b/include/qemu/job.h | 15 | +++ b/qemu-img.c |
35 | @@ -XXX,XX +XXX,XX @@ struct JobDriver { | 16 | @@ -XXX,XX +XXX,XX @@ static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum, |
36 | JobType job_type; | 17 | *pnum = 0; |
37 | 18 | return 0; | |
38 | /** Mandatory: Entrypoint for the Coroutine. */ | ||
39 | - CoroutineEntry *start; | ||
40 | + int coroutine_fn (*run)(Job *job, Error **errp); | ||
41 | |||
42 | /** | ||
43 | * If the callback is not NULL, it will be invoked when the job transitions | ||
44 | diff --git a/block/backup.c b/block/backup.c | ||
45 | index XXXXXXX..XXXXXXX 100644 | ||
46 | --- a/block/backup.c | ||
47 | +++ b/block/backup.c | ||
48 | @@ -XXX,XX +XXX,XX @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) | ||
49 | bdrv_dirty_iter_free(dbi); | ||
50 | } | ||
51 | |||
52 | -static void coroutine_fn backup_run(void *opaque) | ||
53 | +static int coroutine_fn backup_run(Job *opaque_job, Error **errp) | ||
54 | { | ||
55 | - BackupBlockJob *job = opaque; | ||
56 | + BackupBlockJob *job = container_of(opaque_job, BackupBlockJob, common.job); | ||
57 | BackupCompleteData *data; | ||
58 | BlockDriverState *bs = blk_bs(job->common.blk); | ||
59 | int64_t offset, nb_clusters; | ||
60 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque) | ||
61 | data = g_malloc(sizeof(*data)); | ||
62 | data->ret = ret; | ||
63 | job_defer_to_main_loop(&job->common.job, backup_complete, data); | ||
64 | + return ret; | ||
65 | } | ||
66 | |||
67 | static const BlockJobDriver backup_job_driver = { | ||
68 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = { | ||
69 | .free = block_job_free, | ||
70 | .user_resume = block_job_user_resume, | ||
71 | .drain = block_job_drain, | ||
72 | - .start = backup_run, | ||
73 | + .run = backup_run, | ||
74 | .commit = backup_commit, | ||
75 | .abort = backup_abort, | ||
76 | .clean = backup_clean, | ||
77 | diff --git a/block/commit.c b/block/commit.c | ||
78 | index XXXXXXX..XXXXXXX 100644 | ||
79 | --- a/block/commit.c | ||
80 | +++ b/block/commit.c | ||
81 | @@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque) | ||
82 | bdrv_unref(top); | ||
83 | } | ||
84 | |||
85 | -static void coroutine_fn commit_run(void *opaque) | ||
86 | +static int coroutine_fn commit_run(Job *job, Error **errp) | ||
87 | { | ||
88 | - CommitBlockJob *s = opaque; | ||
89 | + CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | ||
90 | CommitCompleteData *data; | ||
91 | int64_t offset; | ||
92 | uint64_t delay_ns = 0; | ||
93 | @@ -XXX,XX +XXX,XX @@ out: | ||
94 | data = g_malloc(sizeof(*data)); | ||
95 | data->ret = ret; | ||
96 | job_defer_to_main_loop(&s->common.job, commit_complete, data); | ||
97 | + return ret; | ||
98 | } | ||
99 | |||
100 | static const BlockJobDriver commit_job_driver = { | ||
101 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = { | ||
102 | .free = block_job_free, | ||
103 | .user_resume = block_job_user_resume, | ||
104 | .drain = block_job_drain, | ||
105 | - .start = commit_run, | ||
106 | + .run = commit_run, | ||
107 | }, | ||
108 | }; | ||
109 | |||
110 | diff --git a/block/create.c b/block/create.c | ||
111 | index XXXXXXX..XXXXXXX 100644 | ||
112 | --- a/block/create.c | ||
113 | +++ b/block/create.c | ||
114 | @@ -XXX,XX +XXX,XX @@ static void blockdev_create_complete(Job *job, void *opaque) | ||
115 | job_completed(job, s->ret, s->err); | ||
116 | } | ||
117 | |||
118 | -static void coroutine_fn blockdev_create_run(void *opaque) | ||
119 | +static int coroutine_fn blockdev_create_run(Job *job, Error **errp) | ||
120 | { | ||
121 | - BlockdevCreateJob *s = opaque; | ||
122 | + BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); | ||
123 | |||
124 | job_progress_set_remaining(&s->common, 1); | ||
125 | s->ret = s->drv->bdrv_co_create(s->opts, &s->err); | ||
126 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn blockdev_create_run(void *opaque) | ||
127 | |||
128 | qapi_free_BlockdevCreateOptions(s->opts); | ||
129 | job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL); | ||
130 | + | ||
131 | + return s->ret; | ||
132 | } | ||
133 | |||
134 | static const JobDriver blockdev_create_job_driver = { | ||
135 | .instance_size = sizeof(BlockdevCreateJob), | ||
136 | .job_type = JOB_TYPE_CREATE, | ||
137 | - .start = blockdev_create_run, | ||
138 | + .run = blockdev_create_run, | ||
139 | }; | ||
140 | |||
141 | void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, | ||
142 | diff --git a/block/mirror.c b/block/mirror.c | ||
143 | index XXXXXXX..XXXXXXX 100644 | ||
144 | --- a/block/mirror.c | ||
145 | +++ b/block/mirror.c | ||
146 | @@ -XXX,XX +XXX,XX @@ static int mirror_flush(MirrorBlockJob *s) | ||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | -static void coroutine_fn mirror_run(void *opaque) | ||
151 | +static int coroutine_fn mirror_run(Job *job, Error **errp) | ||
152 | { | ||
153 | - MirrorBlockJob *s = opaque; | ||
154 | + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); | ||
155 | MirrorExitData *data; | ||
156 | BlockDriverState *bs = s->mirror_top_bs->backing->bs; | ||
157 | BlockDriverState *target_bs = blk_bs(s->target); | ||
158 | @@ -XXX,XX +XXX,XX @@ immediate_exit: | ||
159 | if (need_drain) { | ||
160 | bdrv_drained_begin(bs); | ||
161 | } | 19 | } |
162 | + | 20 | - is_zero = buffer_is_zero(buf, 512); |
163 | job_defer_to_main_loop(&s->common.job, mirror_exit, data); | 21 | + is_zero = buffer_is_zero(buf, BDRV_SECTOR_SIZE); |
164 | + return ret; | 22 | for(i = 1; i < n; i++) { |
165 | } | 23 | - buf += 512; |
166 | 24 | - if (is_zero != buffer_is_zero(buf, 512)) { | |
167 | static void mirror_complete(Job *job, Error **errp) | 25 | + buf += BDRV_SECTOR_SIZE; |
168 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = { | 26 | + if (is_zero != buffer_is_zero(buf, BDRV_SECTOR_SIZE)) { |
169 | .free = block_job_free, | ||
170 | .user_resume = block_job_user_resume, | ||
171 | .drain = block_job_drain, | ||
172 | - .start = mirror_run, | ||
173 | + .run = mirror_run, | ||
174 | .pause = mirror_pause, | ||
175 | .complete = mirror_complete, | ||
176 | }, | ||
177 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = { | ||
178 | .free = block_job_free, | ||
179 | .user_resume = block_job_user_resume, | ||
180 | .drain = block_job_drain, | ||
181 | - .start = mirror_run, | ||
182 | + .run = mirror_run, | ||
183 | .pause = mirror_pause, | ||
184 | .complete = mirror_complete, | ||
185 | }, | ||
186 | diff --git a/block/stream.c b/block/stream.c | ||
187 | index XXXXXXX..XXXXXXX 100644 | ||
188 | --- a/block/stream.c | ||
189 | +++ b/block/stream.c | ||
190 | @@ -XXX,XX +XXX,XX @@ out: | ||
191 | g_free(data); | ||
192 | } | ||
193 | |||
194 | -static void coroutine_fn stream_run(void *opaque) | ||
195 | +static int coroutine_fn stream_run(Job *job, Error **errp) | ||
196 | { | ||
197 | - StreamBlockJob *s = opaque; | ||
198 | + StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); | ||
199 | StreamCompleteData *data; | ||
200 | BlockBackend *blk = s->common.blk; | ||
201 | BlockDriverState *bs = blk_bs(blk); | ||
202 | @@ -XXX,XX +XXX,XX @@ out: | ||
203 | data = g_malloc(sizeof(*data)); | ||
204 | data->ret = ret; | ||
205 | job_defer_to_main_loop(&s->common.job, stream_complete, data); | ||
206 | + return ret; | ||
207 | } | ||
208 | |||
209 | static const BlockJobDriver stream_job_driver = { | ||
210 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = { | ||
211 | .instance_size = sizeof(StreamBlockJob), | ||
212 | .job_type = JOB_TYPE_STREAM, | ||
213 | .free = block_job_free, | ||
214 | - .start = stream_run, | ||
215 | + .run = stream_run, | ||
216 | .user_resume = block_job_user_resume, | ||
217 | .drain = block_job_drain, | ||
218 | }, | ||
219 | diff --git a/job.c b/job.c | ||
220 | index XXXXXXX..XXXXXXX 100644 | ||
221 | --- a/job.c | ||
222 | +++ b/job.c | ||
223 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn job_co_entry(void *opaque) | ||
224 | { | ||
225 | Job *job = opaque; | ||
226 | |||
227 | - assert(job && job->driver && job->driver->start); | ||
228 | + assert(job && job->driver && job->driver->run); | ||
229 | job_pause_point(job); | ||
230 | - job->driver->start(job); | ||
231 | + job->ret = job->driver->run(job, NULL); | ||
232 | } | ||
233 | |||
234 | |||
235 | void job_start(Job *job) | ||
236 | { | ||
237 | assert(job && !job_started(job) && job->paused && | ||
238 | - job->driver && job->driver->start); | ||
239 | + job->driver && job->driver->run); | ||
240 | job->co = qemu_coroutine_create(job_co_entry, job); | ||
241 | job->pause_count--; | ||
242 | job->busy = true; | ||
243 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c | ||
244 | index XXXXXXX..XXXXXXX 100644 | ||
245 | --- a/tests/test-bdrv-drain.c | ||
246 | +++ b/tests/test-bdrv-drain.c | ||
247 | @@ -XXX,XX +XXX,XX @@ static void test_job_completed(Job *job, void *opaque) | ||
248 | job_completed(job, 0, NULL); | ||
249 | } | ||
250 | |||
251 | -static void coroutine_fn test_job_start(void *opaque) | ||
252 | +static int coroutine_fn test_job_run(Job *job, Error **errp) | ||
253 | { | ||
254 | - TestBlockJob *s = opaque; | ||
255 | + TestBlockJob *s = container_of(job, TestBlockJob, common.job); | ||
256 | |||
257 | job_transition_to_ready(&s->common.job); | ||
258 | while (!s->should_complete) { | ||
259 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque) | ||
260 | } | ||
261 | |||
262 | job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); | ||
263 | + return 0; | ||
264 | } | ||
265 | |||
266 | static void test_job_complete(Job *job, Error **errp) | ||
267 | @@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = { | ||
268 | .free = block_job_free, | ||
269 | .user_resume = block_job_user_resume, | ||
270 | .drain = block_job_drain, | ||
271 | - .start = test_job_start, | ||
272 | + .run = test_job_run, | ||
273 | .complete = test_job_complete, | ||
274 | }, | ||
275 | }; | ||
276 | diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c | ||
277 | index XXXXXXX..XXXXXXX 100644 | ||
278 | --- a/tests/test-blockjob-txn.c | ||
279 | +++ b/tests/test-blockjob-txn.c | ||
280 | @@ -XXX,XX +XXX,XX @@ static void test_block_job_complete(Job *job, void *opaque) | ||
281 | bdrv_unref(bs); | ||
282 | } | ||
283 | |||
284 | -static void coroutine_fn test_block_job_run(void *opaque) | ||
285 | +static int coroutine_fn test_block_job_run(Job *job, Error **errp) | ||
286 | { | ||
287 | - TestBlockJob *s = opaque; | ||
288 | - BlockJob *job = &s->common; | ||
289 | + TestBlockJob *s = container_of(job, TestBlockJob, common.job); | ||
290 | |||
291 | while (s->iterations--) { | ||
292 | if (s->use_timer) { | ||
293 | - job_sleep_ns(&job->job, 0); | ||
294 | + job_sleep_ns(job, 0); | ||
295 | } else { | ||
296 | - job_yield(&job->job); | ||
297 | + job_yield(job); | ||
298 | } | ||
299 | |||
300 | - if (job_is_cancelled(&job->job)) { | ||
301 | + if (job_is_cancelled(job)) { | ||
302 | break; | 27 | break; |
303 | } | 28 | } |
304 | } | 29 | } |
305 | 30 | @@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv) | |
306 | - job_defer_to_main_loop(&job->job, test_block_job_complete, | 31 | } |
307 | + job_defer_to_main_loop(job, test_block_job_complete, | 32 | } |
308 | (void *)(intptr_t)s->rc); | 33 | |
309 | + return s->rc; | 34 | - qemu_opt_set_number(opts, BLOCK_OPT_SIZE, s.total_sectors * 512, |
310 | } | 35 | - &error_abort); |
311 | 36 | + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, | |
312 | typedef struct { | 37 | + s.total_sectors * BDRV_SECTOR_SIZE, &error_abort); |
313 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = { | 38 | ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL); |
314 | .free = block_job_free, | 39 | if (ret < 0) { |
315 | .user_resume = block_job_user_resume, | 40 | goto out; |
316 | .drain = block_job_drain, | ||
317 | - .start = test_block_job_run, | ||
318 | + .run = test_block_job_run, | ||
319 | }, | ||
320 | }; | ||
321 | |||
322 | diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c | ||
323 | index XXXXXXX..XXXXXXX 100644 | ||
324 | --- a/tests/test-blockjob.c | ||
325 | +++ b/tests/test-blockjob.c | ||
326 | @@ -XXX,XX +XXX,XX @@ static void cancel_job_complete(Job *job, Error **errp) | ||
327 | s->should_complete = true; | ||
328 | } | ||
329 | |||
330 | -static void coroutine_fn cancel_job_start(void *opaque) | ||
331 | +static int coroutine_fn cancel_job_run(Job *job, Error **errp) | ||
332 | { | ||
333 | - CancelJob *s = opaque; | ||
334 | + CancelJob *s = container_of(job, CancelJob, common.job); | ||
335 | |||
336 | while (!s->should_complete) { | ||
337 | if (job_is_cancelled(&s->common.job)) { | ||
338 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque) | ||
339 | |||
340 | defer: | ||
341 | job_defer_to_main_loop(&s->common.job, cancel_job_completed, s); | ||
342 | + return 0; | ||
343 | } | ||
344 | |||
345 | static const BlockJobDriver test_cancel_driver = { | ||
346 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_cancel_driver = { | ||
347 | .free = block_job_free, | ||
348 | .user_resume = block_job_user_resume, | ||
349 | .drain = block_job_drain, | ||
350 | - .start = cancel_job_start, | ||
351 | + .run = cancel_job_run, | ||
352 | .complete = cancel_job_complete, | ||
353 | }, | ||
354 | }; | ||
355 | -- | 41 | -- |
356 | 2.17.1 | 42 | 2.26.2 |
357 | 43 | ||
358 | 44 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Thomas Huth <thuth@redhat.com> | ||
1 | 2 | ||
3 | The test_stream_parallel test still occasionally fails in the CI. | ||
4 | Thus let's disable it during "make check" for now so that it does | ||
5 | not cause trouble during merge tests. We can enable it again once | ||
6 | the problem has been resolved. | ||
7 | |||
8 | Signed-off-by: Thomas Huth <thuth@redhat.com> | ||
9 | Message-Id: <20200907113824.134788-1-thuth@redhat.com> | ||
10 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
11 | --- | ||
12 | tests/check-block.sh | 3 +++ | ||
13 | tests/qemu-iotests/030 | 2 ++ | ||
14 | 2 files changed, 5 insertions(+) | ||
15 | |||
16 | diff --git a/tests/check-block.sh b/tests/check-block.sh | ||
17 | index XXXXXXX..XXXXXXX 100755 | ||
18 | --- a/tests/check-block.sh | ||
19 | +++ b/tests/check-block.sh | ||
20 | @@ -XXX,XX +XXX,XX @@ fi | ||
21 | |||
22 | cd tests/qemu-iotests | ||
23 | |||
24 | +# QEMU_CHECK_BLOCK_AUTO is used to disable some unstable sub-tests | ||
25 | +export QEMU_CHECK_BLOCK_AUTO=1 | ||
26 | + | ||
27 | ret=0 | ||
28 | for fmt in $format_list ; do | ||
29 | ./check -makecheck -$fmt $group || ret=1 | ||
30 | diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 | ||
31 | index XXXXXXX..XXXXXXX 100755 | ||
32 | --- a/tests/qemu-iotests/030 | ||
33 | +++ b/tests/qemu-iotests/030 | ||
34 | @@ -XXX,XX +XXX,XX @@ | ||
35 | import time | ||
36 | import os | ||
37 | import iotests | ||
38 | +import unittest | ||
39 | from iotests import qemu_img, qemu_io | ||
40 | |||
41 | backing_img = os.path.join(iotests.test_dir, 'backing.img') | ||
42 | @@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase): | ||
43 | |||
44 | # Test that it's possible to run several block-stream operations | ||
45 | # in parallel in the same snapshot chain | ||
46 | + @unittest.skipIf(os.environ.get('QEMU_CHECK_BLOCK_AUTO'), 'disabled in CI') | ||
47 | def test_stream_parallel(self): | ||
48 | self.assert_no_active_block_jobs() | ||
49 | |||
50 | -- | ||
51 | 2.26.2 | ||
52 | |||
53 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Swapnil Ingle <swapnil.ingle@nutanix.com> | ||
1 | 2 | ||
3 | block/vhdx uses qemu block layer where sector size is always 512 bytes. | ||
4 | This may have issues with 4K logical sector sized vhdx image. | ||
5 | |||
6 | For e.g qemu-img convert on such images fails with following assert: | ||
7 | |||
8 | $qemu-img convert -f vhdx -O raw 4KTest1.vhdx test.raw | ||
9 | qemu-img: util/iov.c:388: qiov_slice: Assertion `offset + len <= | ||
10 | qiov->size' failed. | ||
11 | Aborted | ||
12 | |||
13 | This patch adds an check to return ENOTSUP for vhdx images which | ||
14 | have logical sector size other than 512 bytes. | ||
15 | |||
16 | Signed-off-by: Swapnil Ingle <swapnil.ingle@nutanix.com> | ||
17 | Message-Id: <1596794594-44531-1-git-send-email-swapnil.ingle@nutanix.com> | ||
18 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
19 | --- | ||
20 | block/vhdx.c | 6 +++--- | ||
21 | 1 file changed, 3 insertions(+), 3 deletions(-) | ||
22 | |||
23 | diff --git a/block/vhdx.c b/block/vhdx.c | ||
24 | index XXXXXXX..XXXXXXX 100644 | ||
25 | --- a/block/vhdx.c | ||
26 | +++ b/block/vhdx.c | ||
27 | @@ -XXX,XX +XXX,XX @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) | ||
28 | goto exit; | ||
29 | } | ||
30 | |||
31 | - /* only 2 supported sector sizes */ | ||
32 | - if (s->logical_sector_size != 512 && s->logical_sector_size != 4096) { | ||
33 | - ret = -EINVAL; | ||
34 | + /* Currently we only support 512 */ | ||
35 | + if (s->logical_sector_size != 512) { | ||
36 | + ret = -ENOTSUP; | ||
37 | goto exit; | ||
38 | } | ||
39 | |||
40 | -- | ||
41 | 2.26.2 | ||
42 | |||
43 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: Alberto Garcia <berto@igalia.com> |
---|---|---|---|
2 | 2 | ||
3 | Rename opaque_job to job to be consistent with other job implementations. | 3 | If qcow2_alloc_cluster_offset() or qcow2_alloc_cluster_link_l2() fail |
4 | Rename 'job', the BackupBlockJob object, to 's' to also be consistent. | 4 | then this function simply returns the error code, potentially leaking |
5 | the QCowL2Meta structure and leaving stale items in s->cluster_allocs. | ||
5 | 6 | ||
6 | Suggested-by: Eric Blake <eblake@redhat.com> | 7 | A second problem is that this function calls qcow2_free_any_clusters() |
7 | Signed-off-by: John Snow <jsnow@redhat.com> | 8 | on failure but passing a host cluster offset instead of an L2 entry. |
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 9 | Luckily for normal uncompressed clusters a raw offset also works like |
9 | Message-id: 20180830015734.19765-8-jsnow@redhat.com | 10 | a valid L2 entry so it works just the same, but we should be using |
11 | qcow2_free_clusters() instead. | ||
12 | |||
13 | This patch fixes both problems by using qcow2_handle_l2meta(). | ||
14 | |||
15 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
16 | Message-Id: <cd3a6b9abd43f9c0b60be413d760f0cacc67eb66.1599573989.git.berto@igalia.com> | ||
17 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
10 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 18 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
11 | --- | 19 | --- |
12 | block/backup.c | 62 +++++++++++++++++++++++++------------------------- | 20 | block/qcow2.c | 40 +++++++++++++++++----------------------- |
13 | 1 file changed, 31 insertions(+), 31 deletions(-) | 21 | 1 file changed, 17 insertions(+), 23 deletions(-) |
14 | 22 | ||
15 | diff --git a/block/backup.c b/block/backup.c | 23 | diff --git a/block/qcow2.c b/block/qcow2.c |
16 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
17 | --- a/block/backup.c | 25 | --- a/block/qcow2.c |
18 | +++ b/block/backup.c | 26 | +++ b/block/qcow2.c |
19 | @@ -XXX,XX +XXX,XX @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) | 27 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs, |
20 | bdrv_dirty_iter_free(dbi); | 28 | QCowL2Meta *next; |
21 | } | 29 | |
22 | 30 | if (link_l2) { | |
23 | -static int coroutine_fn backup_run(Job *opaque_job, Error **errp) | 31 | - assert(!l2meta->prealloc); |
24 | +static int coroutine_fn backup_run(Job *job, Error **errp) | 32 | ret = qcow2_alloc_cluster_link_l2(bs, l2meta); |
25 | { | 33 | if (ret) { |
26 | - BackupBlockJob *job = container_of(opaque_job, BackupBlockJob, common.job); | 34 | goto out; |
27 | - BlockDriverState *bs = blk_bs(job->common.blk); | 35 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, |
28 | + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); | 36 | int64_t file_length; |
29 | + BlockDriverState *bs = blk_bs(s->common.blk); | 37 | unsigned int cur_bytes; |
30 | int64_t offset, nb_clusters; | 38 | int ret; |
31 | int ret = 0; | 39 | - QCowL2Meta *meta; |
32 | 40 | + QCowL2Meta *meta = NULL, *m; | |
33 | - QLIST_INIT(&job->inflight_reqs); | 41 | |
34 | - qemu_co_rwlock_init(&job->flush_rwlock); | 42 | assert(offset <= new_length); |
35 | + QLIST_INIT(&s->inflight_reqs); | 43 | bytes = new_length - offset; |
36 | + qemu_co_rwlock_init(&s->flush_rwlock); | 44 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, |
37 | 45 | &host_offset, &meta); | |
38 | - nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size); | 46 | if (ret < 0) { |
39 | - job_progress_set_remaining(&job->common.job, job->len); | 47 | error_setg_errno(errp, -ret, "Allocating clusters failed"); |
40 | + nb_clusters = DIV_ROUND_UP(s->len, s->cluster_size); | 48 | - return ret; |
41 | + job_progress_set_remaining(job, s->len); | 49 | + goto out; |
42 | 50 | } | |
43 | - job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); | 51 | |
44 | - if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | 52 | - while (meta) { |
45 | - backup_incremental_init_copy_bitmap(job); | 53 | - QCowL2Meta *next = meta->next; |
46 | + s->copy_bitmap = hbitmap_alloc(nb_clusters, 0); | 54 | - meta->prealloc = true; |
47 | + if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | 55 | - |
48 | + backup_incremental_init_copy_bitmap(s); | 56 | - ret = qcow2_alloc_cluster_link_l2(bs, meta); |
49 | } else { | 57 | - if (ret < 0) { |
50 | - hbitmap_set(job->copy_bitmap, 0, nb_clusters); | 58 | - error_setg_errno(errp, -ret, "Mapping clusters failed"); |
51 | + hbitmap_set(s->copy_bitmap, 0, nb_clusters); | 59 | - qcow2_free_any_clusters(bs, meta->alloc_offset, |
60 | - meta->nb_clusters, QCOW2_DISCARD_NEVER); | ||
61 | - return ret; | ||
62 | - } | ||
63 | - | ||
64 | - /* There are no dependent requests, but we need to remove our | ||
65 | - * request from the list of in-flight requests */ | ||
66 | - QLIST_REMOVE(meta, next_in_flight); | ||
67 | + for (m = meta; m != NULL; m = m->next) { | ||
68 | + m->prealloc = true; | ||
69 | + } | ||
70 | |||
71 | - g_free(meta); | ||
72 | - meta = next; | ||
73 | + ret = qcow2_handle_l2meta(bs, &meta, true); | ||
74 | + if (ret < 0) { | ||
75 | + error_setg_errno(errp, -ret, "Mapping clusters failed"); | ||
76 | + goto out; | ||
77 | } | ||
78 | |||
79 | /* TODO Preallocate data if requested */ | ||
80 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, | ||
81 | file_length = bdrv_getlength(s->data_file->bs); | ||
82 | if (file_length < 0) { | ||
83 | error_setg_errno(errp, -file_length, "Could not get file size"); | ||
84 | - return file_length; | ||
85 | + ret = file_length; | ||
86 | + goto out; | ||
52 | } | 87 | } |
53 | 88 | ||
54 | 89 | if (host_offset + cur_bytes > file_length) { | |
55 | - job->before_write.notify = backup_before_write_notify; | 90 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, |
56 | - bdrv_add_before_write_notifier(bs, &job->before_write); | 91 | ret = bdrv_co_truncate(s->data_file, host_offset + cur_bytes, false, |
57 | + s->before_write.notify = backup_before_write_notify; | 92 | mode, 0, errp); |
58 | + bdrv_add_before_write_notifier(bs, &s->before_write); | 93 | if (ret < 0) { |
59 | 94 | - return ret; | |
60 | - if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { | 95 | + goto out; |
61 | + if (s->sync_mode == MIRROR_SYNC_MODE_NONE) { | ||
62 | /* All bits are set in copy_bitmap to allow any cluster to be copied. | ||
63 | * This does not actually require them to be copied. */ | ||
64 | - while (!job_is_cancelled(&job->common.job)) { | ||
65 | + while (!job_is_cancelled(job)) { | ||
66 | /* Yield until the job is cancelled. We just let our before_write | ||
67 | * notify callback service CoW requests. */ | ||
68 | - job_yield(&job->common.job); | ||
69 | + job_yield(job); | ||
70 | } | ||
71 | - } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||
72 | - ret = backup_run_incremental(job); | ||
73 | + } else if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { | ||
74 | + ret = backup_run_incremental(s); | ||
75 | } else { | ||
76 | /* Both FULL and TOP SYNC_MODE's require copying.. */ | ||
77 | - for (offset = 0; offset < job->len; | ||
78 | - offset += job->cluster_size) { | ||
79 | + for (offset = 0; offset < s->len; | ||
80 | + offset += s->cluster_size) { | ||
81 | bool error_is_read; | ||
82 | int alloced = 0; | ||
83 | |||
84 | - if (yield_and_check(job)) { | ||
85 | + if (yield_and_check(s)) { | ||
86 | break; | ||
87 | } | ||
88 | |||
89 | - if (job->sync_mode == MIRROR_SYNC_MODE_TOP) { | ||
90 | + if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { | ||
91 | int i; | ||
92 | int64_t n; | ||
93 | |||
94 | /* Check to see if these blocks are already in the | ||
95 | * backing file. */ | ||
96 | |||
97 | - for (i = 0; i < job->cluster_size;) { | ||
98 | + for (i = 0; i < s->cluster_size;) { | ||
99 | /* bdrv_is_allocated() only returns true/false based | ||
100 | * on the first set of sectors it comes across that | ||
101 | * are are all in the same state. | ||
102 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_run(Job *opaque_job, Error **errp) | ||
103 | * needed but at some point that is always the case. */ | ||
104 | alloced = | ||
105 | bdrv_is_allocated(bs, offset + i, | ||
106 | - job->cluster_size - i, &n); | ||
107 | + s->cluster_size - i, &n); | ||
108 | i += n; | ||
109 | |||
110 | if (alloced || n == 0) { | ||
111 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_run(Job *opaque_job, Error **errp) | ||
112 | if (alloced < 0) { | ||
113 | ret = alloced; | ||
114 | } else { | ||
115 | - ret = backup_do_cow(job, offset, job->cluster_size, | ||
116 | + ret = backup_do_cow(s, offset, s->cluster_size, | ||
117 | &error_is_read, false); | ||
118 | } | ||
119 | if (ret < 0) { | ||
120 | /* Depending on error action, fail now or retry cluster */ | ||
121 | BlockErrorAction action = | ||
122 | - backup_error_action(job, error_is_read, -ret); | ||
123 | + backup_error_action(s, error_is_read, -ret); | ||
124 | if (action == BLOCK_ERROR_ACTION_REPORT) { | ||
125 | break; | ||
126 | } else { | ||
127 | - offset -= job->cluster_size; | ||
128 | + offset -= s->cluster_size; | ||
129 | continue; | ||
130 | } | ||
131 | } | ||
132 | } | 96 | } |
133 | } | 97 | } |
134 | 98 | ||
135 | - notifier_with_return_remove(&job->before_write); | 99 | - return 0; |
136 | + notifier_with_return_remove(&s->before_write); | 100 | + ret = 0; |
137 | 101 | + | |
138 | /* wait until pending backup_do_cow() calls have completed */ | 102 | +out: |
139 | - qemu_co_rwlock_wrlock(&job->flush_rwlock); | 103 | + qcow2_handle_l2meta(bs, &meta, false); |
140 | - qemu_co_rwlock_unlock(&job->flush_rwlock); | 104 | + return ret; |
141 | - hbitmap_free(job->copy_bitmap); | ||
142 | + qemu_co_rwlock_wrlock(&s->flush_rwlock); | ||
143 | + qemu_co_rwlock_unlock(&s->flush_rwlock); | ||
144 | + hbitmap_free(s->copy_bitmap); | ||
145 | |||
146 | return ret; | ||
147 | } | 105 | } |
106 | |||
107 | /* qcow2_refcount_metadata_size: | ||
148 | -- | 108 | -- |
149 | 2.17.1 | 109 | 2.26.2 |
150 | 110 | ||
151 | 111 | diff view generated by jsdifflib |
1 | From: Fam Zheng <famz@redhat.com> | 1 | From: Alberto Garcia <berto@igalia.com> |
---|---|---|---|
2 | 2 | ||
3 | If we know we've already locked the bytes, don't do it again; similarly | 3 | This function takes an L2 entry and a number of clusters to free. |
4 | don't unlock a byte if we haven't locked it. This doesn't change the | 4 | Although in principle it can free any type of cluster (using the L2 |
5 | behavior, but fixes a corner case explained below. | 5 | entry to determine its type) in practice the API is broken because |
6 | compressed clusters have a variable size and there is no way to free | ||
7 | more than one without having the L2 entry of each one of them. | ||
6 | 8 | ||
7 | Libvirt had an error handling bug that an image can get its (ownership, | 9 | The good news all callers are passing nb_clusters=1 so we can simply |
8 | file mode, SELinux) permissions changed (RHBZ 1584982) by mistake behind | 10 | get rid of that parameter. |
9 | QEMU. Specifically, an image in use by Libvirt VM has: | ||
10 | 11 | ||
11 | $ ls -lhZ b.img | 12 | Signed-off-by: Alberto Garcia <berto@igalia.com> |
12 | -rw-r--r--. qemu qemu system_u:object_r:svirt_image_t:s0:c600,c690 b.img | 13 | Message-Id: <77cea0f4616f921d37e971b3c5b18a2faa24b173.1599573989.git.berto@igalia.com> |
14 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
15 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
16 | --- | ||
17 | block/qcow2.h | 4 ++-- | ||
18 | block/qcow2-cluster.c | 6 +++--- | ||
19 | block/qcow2-refcount.c | 8 ++++---- | ||
20 | 3 files changed, 9 insertions(+), 9 deletions(-) | ||
13 | 21 | ||
14 | Trying to attach it a second time won't work because of image locking. | 22 | diff --git a/block/qcow2.h b/block/qcow2.h |
15 | And after the error, it becomes: | ||
16 | |||
17 | $ ls -lhZ b.img | ||
18 | -rw-r--r--. root root system_u:object_r:virt_image_t:s0 b.img | ||
19 | |||
20 | Then, we won't be able to do OFD lock operations with the existing fd. | ||
21 | In other words, the code such as in blk_detach_dev: | ||
22 | |||
23 | blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort); | ||
24 | |||
25 | can abort() QEMU, out of environmental changes. | ||
26 | |||
27 | This patch is an easy fix to this and the change is regardlessly | ||
28 | reasonable, so do it. | ||
29 | |||
30 | Signed-off-by: Fam Zheng <famz@redhat.com> | ||
31 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
32 | --- | ||
33 | block/file-posix.c | 41 +++++++++++++++++++++++++++++++---------- | ||
34 | 1 file changed, 31 insertions(+), 10 deletions(-) | ||
35 | |||
36 | diff --git a/block/file-posix.c b/block/file-posix.c | ||
37 | index XXXXXXX..XXXXXXX 100644 | 23 | index XXXXXXX..XXXXXXX 100644 |
38 | --- a/block/file-posix.c | 24 | --- a/block/qcow2.h |
39 | +++ b/block/file-posix.c | 25 | +++ b/block/qcow2.h |
40 | @@ -XXX,XX +XXX,XX @@ typedef enum { | 26 | @@ -XXX,XX +XXX,XX @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size); |
41 | * file; if @unlock == true, also unlock the unneeded bytes. | 27 | void qcow2_free_clusters(BlockDriverState *bs, |
42 | * @shared_perm_lock_bits is the mask of all permissions that are NOT shared. | 28 | int64_t offset, int64_t size, |
29 | enum qcow2_discard_type type); | ||
30 | -void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, | ||
31 | - int nb_clusters, enum qcow2_discard_type type); | ||
32 | +void qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry, | ||
33 | + enum qcow2_discard_type type); | ||
34 | |||
35 | int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||
36 | int64_t l1_table_offset, int l1_size, int addend); | ||
37 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
38 | index XXXXXXX..XXXXXXX 100644 | ||
39 | --- a/block/qcow2-cluster.c | ||
40 | +++ b/block/qcow2-cluster.c | ||
41 | @@ -XXX,XX +XXX,XX @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||
42 | */ | ||
43 | if (!m->keep_old_clusters && j != 0) { | ||
44 | for (i = 0; i < j; i++) { | ||
45 | - qcow2_free_any_clusters(bs, old_cluster[i], 1, QCOW2_DISCARD_NEVER); | ||
46 | + qcow2_free_any_cluster(bs, old_cluster[i], QCOW2_DISCARD_NEVER); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | @@ -XXX,XX +XXX,XX @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, | ||
51 | set_l2_bitmap(s, l2_slice, l2_index + i, new_l2_bitmap); | ||
52 | } | ||
53 | /* Then decrease the refcount */ | ||
54 | - qcow2_free_any_clusters(bs, old_l2_entry, 1, type); | ||
55 | + qcow2_free_any_cluster(bs, old_l2_entry, type); | ||
56 | } | ||
57 | |||
58 | qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); | ||
59 | @@ -XXX,XX +XXX,XX @@ static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, | ||
60 | |||
61 | qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); | ||
62 | if (unmap) { | ||
63 | - qcow2_free_any_clusters(bs, old_l2_entry, 1, QCOW2_DISCARD_REQUEST); | ||
64 | + qcow2_free_any_cluster(bs, old_l2_entry, QCOW2_DISCARD_REQUEST); | ||
65 | } | ||
66 | set_l2_entry(s, l2_slice, l2_index + i, new_l2_entry); | ||
67 | if (has_subclusters(s)) { | ||
68 | diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c | ||
69 | index XXXXXXX..XXXXXXX 100644 | ||
70 | --- a/block/qcow2-refcount.c | ||
71 | +++ b/block/qcow2-refcount.c | ||
72 | @@ -XXX,XX +XXX,XX @@ void qcow2_free_clusters(BlockDriverState *bs, | ||
73 | * Free a cluster using its L2 entry (handles clusters of all types, e.g. | ||
74 | * normal cluster, compressed cluster, etc.) | ||
43 | */ | 75 | */ |
44 | -static int raw_apply_lock_bytes(int fd, | 76 | -void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, |
45 | +static int raw_apply_lock_bytes(BDRVRawState *s, int fd, | 77 | - int nb_clusters, enum qcow2_discard_type type) |
46 | uint64_t perm_lock_bits, | 78 | +void qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry, |
47 | uint64_t shared_perm_lock_bits, | 79 | + enum qcow2_discard_type type) |
48 | bool unlock, Error **errp) | ||
49 | { | 80 | { |
50 | int ret; | 81 | BDRVQcow2State *s = bs->opaque; |
51 | int i; | 82 | QCow2ClusterType ctype = qcow2_get_cluster_type(bs, l2_entry); |
52 | + uint64_t locked_perm, locked_shared_perm; | 83 | @@ -XXX,XX +XXX,XX @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, |
53 | + | 84 | ctype == QCOW2_CLUSTER_ZERO_ALLOC)) |
54 | + if (s) { | 85 | { |
55 | + locked_perm = s->perm; | 86 | bdrv_pdiscard(s->data_file, l2_entry & L2E_OFFSET_MASK, |
56 | + locked_shared_perm = ~s->shared_perm & BLK_PERM_ALL; | 87 | - nb_clusters << s->cluster_bits); |
57 | + } else { | 88 | + s->cluster_size); |
58 | + /* | 89 | } |
59 | + * We don't have the previous bits, just lock/unlock for each of the | 90 | return; |
60 | + * requested bits. | ||
61 | + */ | ||
62 | + if (unlock) { | ||
63 | + locked_perm = BLK_PERM_ALL; | ||
64 | + locked_shared_perm = BLK_PERM_ALL; | ||
65 | + } else { | ||
66 | + locked_perm = 0; | ||
67 | + locked_shared_perm = 0; | ||
68 | + } | ||
69 | + } | ||
70 | |||
71 | PERM_FOREACH(i) { | ||
72 | int off = RAW_LOCK_PERM_BASE + i; | ||
73 | - if (perm_lock_bits & (1ULL << i)) { | ||
74 | + uint64_t bit = (1ULL << i); | ||
75 | + if ((perm_lock_bits & bit) && !(locked_perm & bit)) { | ||
76 | ret = qemu_lock_fd(fd, off, 1, false); | ||
77 | if (ret) { | ||
78 | error_setg(errp, "Failed to lock byte %d", off); | ||
79 | return ret; | ||
80 | } | ||
81 | - } else if (unlock) { | ||
82 | + } else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) { | ||
83 | ret = qemu_unlock_fd(fd, off, 1); | ||
84 | if (ret) { | ||
85 | error_setg(errp, "Failed to unlock byte %d", off); | ||
86 | @@ -XXX,XX +XXX,XX @@ static int raw_apply_lock_bytes(int fd, | ||
87 | } | 91 | } |
88 | PERM_FOREACH(i) { | 92 | @@ -XXX,XX +XXX,XX @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, |
89 | int off = RAW_LOCK_SHARED_BASE + i; | 93 | l2_entry & L2E_OFFSET_MASK); |
90 | - if (shared_perm_lock_bits & (1ULL << i)) { | 94 | } else { |
91 | + uint64_t bit = (1ULL << i); | 95 | qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK, |
92 | + if ((shared_perm_lock_bits & bit) && !(locked_shared_perm & bit)) { | 96 | - nb_clusters << s->cluster_bits, type); |
93 | ret = qemu_lock_fd(fd, off, 1, false); | 97 | + s->cluster_size, type); |
94 | if (ret) { | ||
95 | error_setg(errp, "Failed to lock byte %d", off); | ||
96 | return ret; | ||
97 | } | ||
98 | - } else if (unlock) { | ||
99 | + } else if (unlock && (locked_shared_perm & bit) && | ||
100 | + !(shared_perm_lock_bits & bit)) { | ||
101 | ret = qemu_unlock_fd(fd, off, 1); | ||
102 | if (ret) { | ||
103 | error_setg(errp, "Failed to unlock byte %d", off); | ||
104 | @@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs, | ||
105 | |||
106 | switch (op) { | ||
107 | case RAW_PL_PREPARE: | ||
108 | - ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm, | ||
109 | + ret = raw_apply_lock_bytes(s, s->lock_fd, s->perm | new_perm, | ||
110 | ~s->shared_perm | ~new_shared, | ||
111 | false, errp); | ||
112 | if (!ret) { | ||
113 | @@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs, | ||
114 | op = RAW_PL_ABORT; | ||
115 | /* fall through to unlock bytes. */ | ||
116 | case RAW_PL_ABORT: | ||
117 | - raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm, | ||
118 | + raw_apply_lock_bytes(s, s->lock_fd, s->perm, ~s->shared_perm, | ||
119 | true, &local_err); | ||
120 | if (local_err) { | ||
121 | /* Theoretically the above call only unlocks bytes and it cannot | ||
122 | @@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs, | ||
123 | } | 98 | } |
124 | break; | 99 | break; |
125 | case RAW_PL_COMMIT: | 100 | case QCOW2_CLUSTER_ZERO_PLAIN: |
126 | - raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared, | ||
127 | + raw_apply_lock_bytes(s, s->lock_fd, new_perm, ~new_shared, | ||
128 | true, &local_err); | ||
129 | if (local_err) { | ||
130 | /* Theoretically the above call only unlocks bytes and it cannot | ||
131 | @@ -XXX,XX +XXX,XX @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) | ||
132 | shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; | ||
133 | |||
134 | /* Step one: Take locks */ | ||
135 | - result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp); | ||
136 | + result = raw_apply_lock_bytes(NULL, fd, perm, ~shared, false, errp); | ||
137 | if (result < 0) { | ||
138 | goto out_close; | ||
139 | } | ||
140 | @@ -XXX,XX +XXX,XX @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) | ||
141 | } | ||
142 | |||
143 | out_unlock: | ||
144 | - raw_apply_lock_bytes(fd, 0, 0, true, &local_err); | ||
145 | + raw_apply_lock_bytes(NULL, fd, 0, 0, true, &local_err); | ||
146 | if (local_err) { | ||
147 | /* The above call should not fail, and if it does, that does | ||
148 | * not mean the whole creation operation has failed. So | ||
149 | -- | 101 | -- |
150 | 2.17.1 | 102 | 2.26.2 |
151 | 103 | ||
152 | 104 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
1 | 2 | ||
3 | This function checks the current status of a (sub)cluster in order to | ||
4 | see if an unaligned 'write zeroes' request can be done efficiently by | ||
5 | simply updating the L2 metadata and without having to write actual | ||
6 | zeroes to disk. | ||
7 | |||
8 | If the situation does not allow using the fast path then the function | ||
9 | returns -ENOTSUP and the caller falls back to writing zeroes. | ||
10 | |||
11 | If can happen however that the aforementioned check returns an actual | ||
12 | error code so in this case we should pass it to the caller. | ||
13 | |||
14 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
15 | Message-Id: <20200909123739.719-1-berto@igalia.com> | ||
16 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
17 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
18 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
19 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
20 | --- | ||
21 | block/qcow2.c | 2 +- | ||
22 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
23 | |||
24 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
25 | index XXXXXXX..XXXXXXX 100644 | ||
26 | --- a/block/qcow2.c | ||
27 | +++ b/block/qcow2.c | ||
28 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, | ||
29 | type != QCOW2_SUBCLUSTER_ZERO_PLAIN && | ||
30 | type != QCOW2_SUBCLUSTER_ZERO_ALLOC)) { | ||
31 | qemu_co_mutex_unlock(&s->lock); | ||
32 | - return -ENOTSUP; | ||
33 | + return ret < 0 ? ret : -ENOTSUP; | ||
34 | } | ||
35 | } else { | ||
36 | qemu_co_mutex_lock(&s->lock); | ||
37 | -- | ||
38 | 2.26.2 | ||
39 | |||
40 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: John Snow <jsnow@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Jobs presently use both an Error object in the case of the create job, | 3 | This saw its last use in 4bfb274165ba. |
4 | and char strings in the case of generic errors elsewhere. | ||
5 | |||
6 | Unify the two paths as just j->err, and remove the extra argument from | ||
7 | job_completed. The integer error code for job_completed is kept for now, | ||
8 | to be removed shortly in a separate patch. | ||
9 | 4 | ||
10 | Signed-off-by: John Snow <jsnow@redhat.com> | 5 | Signed-off-by: John Snow <jsnow@redhat.com> |
11 | Message-id: 20180830015734.19765-3-jsnow@redhat.com | 6 | Message-Id: <20200806211345.2925343-2-jsnow@redhat.com> |
12 | [mreitz: Dropped a superfluous g_strdup()] | ||
13 | Reviewed-by: Eric Blake <eblake@redhat.com> | ||
14 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 7 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
15 | --- | 8 | --- |
16 | include/qemu/job.h | 14 ++++++++------ | 9 | block/rbd.c | 42 ------------------------------------------ |
17 | block/backup.c | 2 +- | 10 | 1 file changed, 42 deletions(-) |
18 | block/commit.c | 2 +- | ||
19 | block/create.c | 5 ++--- | ||
20 | block/mirror.c | 2 +- | ||
21 | block/stream.c | 2 +- | ||
22 | job-qmp.c | 5 +++-- | ||
23 | job.c | 18 ++++++------------ | ||
24 | tests/test-bdrv-drain.c | 2 +- | ||
25 | tests/test-blockjob-txn.c | 2 +- | ||
26 | tests/test-blockjob.c | 2 +- | ||
27 | 11 files changed, 26 insertions(+), 30 deletions(-) | ||
28 | 11 | ||
29 | diff --git a/include/qemu/job.h b/include/qemu/job.h | 12 | diff --git a/block/rbd.c b/block/rbd.c |
30 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
31 | --- a/include/qemu/job.h | 14 | --- a/block/rbd.c |
32 | +++ b/include/qemu/job.h | 15 | +++ b/block/rbd.c |
33 | @@ -XXX,XX +XXX,XX @@ typedef struct Job { | 16 | @@ -XXX,XX +XXX,XX @@ static void qemu_rbd_memset(RADOSCB *rcb, int64_t offs) |
34 | /** Estimated progress_current value at the completion of the job */ | ||
35 | int64_t progress_total; | ||
36 | |||
37 | - /** Error string for a failed job (NULL if, and only if, job->ret == 0) */ | ||
38 | - char *error; | ||
39 | - | ||
40 | /** ret code passed to job_completed. */ | ||
41 | int ret; | ||
42 | |||
43 | + /** | ||
44 | + * Error object for a failed job. | ||
45 | + * If job->ret is nonzero and an error object was not set, it will be set | ||
46 | + * to strerror(-job->ret) during job_completed. | ||
47 | + */ | ||
48 | + Error *err; | ||
49 | + | ||
50 | /** The completion function that will be called when the job completes. */ | ||
51 | BlockCompletionFunc *cb; | ||
52 | |||
53 | @@ -XXX,XX +XXX,XX @@ void job_transition_to_ready(Job *job); | ||
54 | /** | ||
55 | * @job: The job being completed. | ||
56 | * @ret: The status code. | ||
57 | - * @error: The error message for a failing job (only with @ret < 0). If @ret is | ||
58 | - * negative, but NULL is given for @error, strerror() is used. | ||
59 | * | ||
60 | * Marks @job as completed. If @ret is non-zero, the job transaction it is part | ||
61 | * of is aborted. If @ret is zero, the job moves into the WAITING state. If it | ||
62 | * is the last job to complete in its transaction, all jobs in the transaction | ||
63 | * move from WAITING to PENDING. | ||
64 | */ | ||
65 | -void job_completed(Job *job, int ret, Error *error); | ||
66 | +void job_completed(Job *job, int ret); | ||
67 | |||
68 | /** Asynchronously complete the specified @job. */ | ||
69 | void job_complete(Job *job, Error **errp); | ||
70 | diff --git a/block/backup.c b/block/backup.c | ||
71 | index XXXXXXX..XXXXXXX 100644 | ||
72 | --- a/block/backup.c | ||
73 | +++ b/block/backup.c | ||
74 | @@ -XXX,XX +XXX,XX @@ static void backup_complete(Job *job, void *opaque) | ||
75 | { | ||
76 | BackupCompleteData *data = opaque; | ||
77 | |||
78 | - job_completed(job, data->ret, NULL); | ||
79 | + job_completed(job, data->ret); | ||
80 | g_free(data); | ||
81 | } | ||
82 | |||
83 | diff --git a/block/commit.c b/block/commit.c | ||
84 | index XXXXXXX..XXXXXXX 100644 | ||
85 | --- a/block/commit.c | ||
86 | +++ b/block/commit.c | ||
87 | @@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque) | ||
88 | * bdrv_set_backing_hd() to fail. */ | ||
89 | block_job_remove_all_bdrv(bjob); | ||
90 | |||
91 | - job_completed(job, ret, NULL); | ||
92 | + job_completed(job, ret); | ||
93 | g_free(data); | ||
94 | |||
95 | /* If bdrv_drop_intermediate() didn't already do that, remove the commit | ||
96 | diff --git a/block/create.c b/block/create.c | ||
97 | index XXXXXXX..XXXXXXX 100644 | ||
98 | --- a/block/create.c | ||
99 | +++ b/block/create.c | ||
100 | @@ -XXX,XX +XXX,XX @@ typedef struct BlockdevCreateJob { | ||
101 | BlockDriver *drv; | ||
102 | BlockdevCreateOptions *opts; | ||
103 | int ret; | ||
104 | - Error *err; | ||
105 | } BlockdevCreateJob; | ||
106 | |||
107 | static void blockdev_create_complete(Job *job, void *opaque) | ||
108 | { | ||
109 | BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); | ||
110 | |||
111 | - job_completed(job, s->ret, s->err); | ||
112 | + job_completed(job, s->ret); | ||
113 | } | ||
114 | |||
115 | static int coroutine_fn blockdev_create_run(Job *job, Error **errp) | ||
116 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn blockdev_create_run(Job *job, Error **errp) | ||
117 | BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); | ||
118 | |||
119 | job_progress_set_remaining(&s->common, 1); | ||
120 | - s->ret = s->drv->bdrv_co_create(s->opts, &s->err); | ||
121 | + s->ret = s->drv->bdrv_co_create(s->opts, errp); | ||
122 | job_progress_update(&s->common, 1); | ||
123 | |||
124 | qapi_free_BlockdevCreateOptions(s->opts); | ||
125 | diff --git a/block/mirror.c b/block/mirror.c | ||
126 | index XXXXXXX..XXXXXXX 100644 | ||
127 | --- a/block/mirror.c | ||
128 | +++ b/block/mirror.c | ||
129 | @@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque) | ||
130 | blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); | ||
131 | |||
132 | bs_opaque->job = NULL; | ||
133 | - job_completed(job, data->ret, NULL); | ||
134 | + job_completed(job, data->ret); | ||
135 | |||
136 | g_free(data); | ||
137 | bdrv_drained_end(src); | ||
138 | diff --git a/block/stream.c b/block/stream.c | ||
139 | index XXXXXXX..XXXXXXX 100644 | ||
140 | --- a/block/stream.c | ||
141 | +++ b/block/stream.c | ||
142 | @@ -XXX,XX +XXX,XX @@ out: | ||
143 | } | ||
144 | |||
145 | g_free(s->backing_file_str); | ||
146 | - job_completed(job, data->ret, NULL); | ||
147 | + job_completed(job, data->ret); | ||
148 | g_free(data); | ||
149 | } | ||
150 | |||
151 | diff --git a/job-qmp.c b/job-qmp.c | ||
152 | index XXXXXXX..XXXXXXX 100644 | ||
153 | --- a/job-qmp.c | ||
154 | +++ b/job-qmp.c | ||
155 | @@ -XXX,XX +XXX,XX @@ static JobInfo *job_query_single(Job *job, Error **errp) | ||
156 | .status = job->status, | ||
157 | .current_progress = job->progress_current, | ||
158 | .total_progress = job->progress_total, | ||
159 | - .has_error = !!job->error, | ||
160 | - .error = g_strdup(job->error), | ||
161 | + .has_error = !!job->err, | ||
162 | + .error = job->err ? \ | ||
163 | + g_strdup(error_get_pretty(job->err)) : NULL, | ||
164 | }; | ||
165 | |||
166 | return info; | ||
167 | diff --git a/job.c b/job.c | ||
168 | index XXXXXXX..XXXXXXX 100644 | ||
169 | --- a/job.c | ||
170 | +++ b/job.c | ||
171 | @@ -XXX,XX +XXX,XX @@ void job_unref(Job *job) | ||
172 | |||
173 | QLIST_REMOVE(job, job_list); | ||
174 | |||
175 | - g_free(job->error); | ||
176 | + error_free(job->err); | ||
177 | g_free(job->id); | ||
178 | g_free(job); | ||
179 | } | ||
180 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn job_co_entry(void *opaque) | ||
181 | |||
182 | assert(job && job->driver && job->driver->run); | ||
183 | job_pause_point(job); | ||
184 | - job->ret = job->driver->run(job, NULL); | ||
185 | + job->ret = job->driver->run(job, &job->err); | ||
186 | } | ||
187 | |||
188 | |||
189 | @@ -XXX,XX +XXX,XX @@ static void job_update_rc(Job *job) | ||
190 | job->ret = -ECANCELED; | ||
191 | } | ||
192 | if (job->ret) { | ||
193 | - if (!job->error) { | ||
194 | - job->error = g_strdup(strerror(-job->ret)); | ||
195 | + if (!job->err) { | ||
196 | + error_setg(&job->err, "%s", strerror(-job->ret)); | ||
197 | } | ||
198 | job_state_transition(job, JOB_STATUS_ABORTING); | ||
199 | } | ||
200 | @@ -XXX,XX +XXX,XX @@ static void job_completed_txn_success(Job *job) | ||
201 | } | 17 | } |
202 | } | 18 | } |
203 | 19 | ||
204 | -void job_completed(Job *job, int ret, Error *error) | 20 | -static QemuOptsList runtime_opts = { |
205 | +void job_completed(Job *job, int ret) | 21 | - .name = "rbd", |
206 | { | 22 | - .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), |
207 | assert(job && job->txn && !job_is_completed(job)); | 23 | - .desc = { |
208 | 24 | - { | |
209 | job->ret = ret; | 25 | - .name = "pool", |
210 | - if (error) { | 26 | - .type = QEMU_OPT_STRING, |
211 | - assert(job->ret < 0); | 27 | - .help = "Rados pool name", |
212 | - job->error = g_strdup(error_get_pretty(error)); | 28 | - }, |
213 | - error_free(error); | 29 | - { |
214 | - } | 30 | - .name = "namespace", |
31 | - .type = QEMU_OPT_STRING, | ||
32 | - .help = "Rados namespace name in the pool", | ||
33 | - }, | ||
34 | - { | ||
35 | - .name = "image", | ||
36 | - .type = QEMU_OPT_STRING, | ||
37 | - .help = "Image name in the pool", | ||
38 | - }, | ||
39 | - { | ||
40 | - .name = "conf", | ||
41 | - .type = QEMU_OPT_STRING, | ||
42 | - .help = "Rados config file location", | ||
43 | - }, | ||
44 | - { | ||
45 | - .name = "snapshot", | ||
46 | - .type = QEMU_OPT_STRING, | ||
47 | - .help = "Ceph snapshot name", | ||
48 | - }, | ||
49 | - { | ||
50 | - /* maps to 'id' in rados_create() */ | ||
51 | - .name = "user", | ||
52 | - .type = QEMU_OPT_STRING, | ||
53 | - .help = "Rados id name", | ||
54 | - }, | ||
55 | - /* | ||
56 | - * server.* extracted manually, see qemu_rbd_mon_host() | ||
57 | - */ | ||
58 | - { /* end of list */ } | ||
59 | - }, | ||
60 | -}; | ||
215 | - | 61 | - |
216 | job_update_rc(job); | 62 | /* FIXME Deprecate and remove keypairs or make it available in QMP. */ |
217 | trace_job_completed(job, ret, job->ret); | 63 | static int qemu_rbd_do_create(BlockdevCreateOptions *options, |
218 | if (job->ret) { | 64 | const char *keypairs, const char *password_secret, |
219 | @@ -XXX,XX +XXX,XX @@ void job_cancel(Job *job, bool force) | ||
220 | } | ||
221 | job_cancel_async(job, force); | ||
222 | if (!job_started(job)) { | ||
223 | - job_completed(job, -ECANCELED, NULL); | ||
224 | + job_completed(job, -ECANCELED); | ||
225 | } else if (job->deferred_to_main_loop) { | ||
226 | job_completed_txn_abort(job); | ||
227 | } else { | ||
228 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c | ||
229 | index XXXXXXX..XXXXXXX 100644 | ||
230 | --- a/tests/test-bdrv-drain.c | ||
231 | +++ b/tests/test-bdrv-drain.c | ||
232 | @@ -XXX,XX +XXX,XX @@ typedef struct TestBlockJob { | ||
233 | |||
234 | static void test_job_completed(Job *job, void *opaque) | ||
235 | { | ||
236 | - job_completed(job, 0, NULL); | ||
237 | + job_completed(job, 0); | ||
238 | } | ||
239 | |||
240 | static int coroutine_fn test_job_run(Job *job, Error **errp) | ||
241 | diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c | ||
242 | index XXXXXXX..XXXXXXX 100644 | ||
243 | --- a/tests/test-blockjob-txn.c | ||
244 | +++ b/tests/test-blockjob-txn.c | ||
245 | @@ -XXX,XX +XXX,XX @@ static void test_block_job_complete(Job *job, void *opaque) | ||
246 | rc = -ECANCELED; | ||
247 | } | ||
248 | |||
249 | - job_completed(job, rc, NULL); | ||
250 | + job_completed(job, rc); | ||
251 | bdrv_unref(bs); | ||
252 | } | ||
253 | |||
254 | diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c | ||
255 | index XXXXXXX..XXXXXXX 100644 | ||
256 | --- a/tests/test-blockjob.c | ||
257 | +++ b/tests/test-blockjob.c | ||
258 | @@ -XXX,XX +XXX,XX @@ static void cancel_job_completed(Job *job, void *opaque) | ||
259 | { | ||
260 | CancelJob *s = opaque; | ||
261 | s->completed = true; | ||
262 | - job_completed(job, 0, NULL); | ||
263 | + job_completed(job, 0); | ||
264 | } | ||
265 | |||
266 | static void cancel_job_complete(Job *job, Error **errp) | ||
267 | -- | 65 | -- |
268 | 2.17.1 | 66 | 2.26.2 |
269 | 67 | ||
270 | 68 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: John Snow <jsnow@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Change the manual deferment to commit_complete into the implicit | 3 | Introduced by d85f4222b468, |
4 | callback to job_exit, renaming commit_complete to commit_exit. | 4 | These were seemingly never used at all. |
5 | |||
6 | This conversion does change the timing of when job_completed is | ||
7 | called to after the bdrv_replace_node and bdrv_unref calls, which | ||
8 | could have implications for bjob->blk which will now be put down | ||
9 | after this cleanup. | ||
10 | |||
11 | Kevin highlights that we did not take any permissions for that backend | ||
12 | at job creation time, so it is safe to reorder these operations. | ||
13 | 5 | ||
14 | Signed-off-by: John Snow <jsnow@redhat.com> | 6 | Signed-off-by: John Snow <jsnow@redhat.com> |
15 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 7 | Message-Id: <20200806211345.2925343-3-jsnow@redhat.com> |
16 | Message-id: 20180830015734.19765-5-jsnow@redhat.com | ||
17 | Reviewed-by: Jeff Cody <jcody@redhat.com> | ||
18 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 8 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
19 | --- | 9 | --- |
20 | block/commit.c | 22 +++++----------------- | 10 | block/qcow.c | 9 --------- |
21 | 1 file changed, 5 insertions(+), 17 deletions(-) | 11 | 1 file changed, 9 deletions(-) |
22 | 12 | ||
23 | diff --git a/block/commit.c b/block/commit.c | 13 | diff --git a/block/qcow.c b/block/qcow.c |
24 | index XXXXXXX..XXXXXXX 100644 | 14 | index XXXXXXX..XXXXXXX 100644 |
25 | --- a/block/commit.c | 15 | --- a/block/qcow.c |
26 | +++ b/block/commit.c | 16 | +++ b/block/qcow.c |
27 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, | 17 | @@ -XXX,XX +XXX,XX @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) |
28 | return 0; | 18 | return 0; |
29 | } | 19 | } |
30 | 20 | ||
31 | -typedef struct { | 21 | -static QemuOptsList qcow_runtime_opts = { |
32 | - int ret; | 22 | - .name = "qcow", |
33 | -} CommitCompleteData; | 23 | - .head = QTAILQ_HEAD_INITIALIZER(qcow_runtime_opts.head), |
24 | - .desc = { | ||
25 | - BLOCK_CRYPTO_OPT_DEF_QCOW_KEY_SECRET("encrypt."), | ||
26 | - { /* end of list */ } | ||
27 | - }, | ||
28 | -}; | ||
34 | - | 29 | - |
35 | -static void commit_complete(Job *job, void *opaque) | 30 | static int qcow_open(BlockDriverState *bs, QDict *options, int flags, |
36 | +static void commit_exit(Job *job) | 31 | Error **errp) |
37 | { | 32 | { |
38 | CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | ||
39 | BlockJob *bjob = &s->common; | ||
40 | - CommitCompleteData *data = opaque; | ||
41 | BlockDriverState *top = blk_bs(s->top); | ||
42 | BlockDriverState *base = blk_bs(s->base); | ||
43 | BlockDriverState *commit_top_bs = s->commit_top_bs; | ||
44 | - int ret = data->ret; | ||
45 | bool remove_commit_top_bs = false; | ||
46 | |||
47 | /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ | ||
48 | @@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque) | ||
49 | * the normal backing chain can be restored. */ | ||
50 | blk_unref(s->base); | ||
51 | |||
52 | - if (!job_is_cancelled(job) && ret == 0) { | ||
53 | + if (!job_is_cancelled(job) && job->ret == 0) { | ||
54 | /* success */ | ||
55 | - ret = bdrv_drop_intermediate(s->commit_top_bs, base, | ||
56 | - s->backing_file_str); | ||
57 | + job->ret = bdrv_drop_intermediate(s->commit_top_bs, base, | ||
58 | + s->backing_file_str); | ||
59 | } else { | ||
60 | /* XXX Can (or should) we somehow keep 'consistent read' blocked even | ||
61 | * after the failed/cancelled commit job is gone? If we already wrote | ||
62 | @@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque) | ||
63 | * bdrv_set_backing_hd() to fail. */ | ||
64 | block_job_remove_all_bdrv(bjob); | ||
65 | |||
66 | - job_completed(job, ret); | ||
67 | - g_free(data); | ||
68 | - | ||
69 | /* If bdrv_drop_intermediate() didn't already do that, remove the commit | ||
70 | * filter driver from the backing chain. Do this as the final step so that | ||
71 | * the 'consistent read' permission can be granted. */ | ||
72 | @@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque) | ||
73 | static int coroutine_fn commit_run(Job *job, Error **errp) | ||
74 | { | ||
75 | CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); | ||
76 | - CommitCompleteData *data; | ||
77 | int64_t offset; | ||
78 | uint64_t delay_ns = 0; | ||
79 | int ret = 0; | ||
80 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp) | ||
81 | out: | ||
82 | qemu_vfree(buf); | ||
83 | |||
84 | - data = g_malloc(sizeof(*data)); | ||
85 | - data->ret = ret; | ||
86 | - job_defer_to_main_loop(&s->common.job, commit_complete, data); | ||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | @@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = { | ||
91 | .user_resume = block_job_user_resume, | ||
92 | .drain = block_job_drain, | ||
93 | .run = commit_run, | ||
94 | + .exit = commit_exit, | ||
95 | }, | ||
96 | }; | ||
97 | |||
98 | -- | 33 | -- |
99 | 2.17.1 | 34 | 2.26.2 |
100 | 35 | ||
101 | 36 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: Alberto Garcia <berto@igalia.com> | ||
1 | 2 | ||
3 | This function preallocates metadata structures and then extends the | ||
4 | image to its new size, but that new size calculation is wrong because | ||
5 | it doesn't take into account that the host_offset variable is always | ||
6 | cluster-aligned. | ||
7 | |||
8 | This problem can be reproduced with preallocation=metadata when the | ||
9 | original size is not cluster-aligned but the new size is. In this case | ||
10 | the final image size will be shorter than expected. | ||
11 | |||
12 | qemu-img create -f qcow2 img.qcow2 31k | ||
13 | qemu-img resize --preallocation=metadata img.qcow2 128k | ||
14 | |||
15 | Signed-off-by: Alberto Garcia <berto@igalia.com> | ||
16 | Message-Id: <adeb8b059917b141d5f5b3bd2a016262d3052c79.1599833007.git.berto@igalia.com> | ||
17 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
18 | [mreitz: Mark compat=0.10 unsupported for iotest 125] | ||
19 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
20 | --- | ||
21 | block/qcow2.c | 1 + | ||
22 | tests/qemu-iotests/125 | 44 ++++++++++++++++++++++---------------- | ||
23 | tests/qemu-iotests/125.out | 28 ++++++++++++++++++++++-- | ||
24 | 3 files changed, 53 insertions(+), 20 deletions(-) | ||
25 | |||
26 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
27 | index XXXXXXX..XXXXXXX 100644 | ||
28 | --- a/block/qcow2.c | ||
29 | +++ b/block/qcow2.c | ||
30 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, | ||
31 | error_setg_errno(errp, -ret, "Allocating clusters failed"); | ||
32 | goto out; | ||
33 | } | ||
34 | + host_offset += offset_into_cluster(s, offset); | ||
35 | |||
36 | for (m = meta; m != NULL; m = m->next) { | ||
37 | m->prealloc = true; | ||
38 | diff --git a/tests/qemu-iotests/125 b/tests/qemu-iotests/125 | ||
39 | index XXXXXXX..XXXXXXX 100755 | ||
40 | --- a/tests/qemu-iotests/125 | ||
41 | +++ b/tests/qemu-iotests/125 | ||
42 | @@ -XXX,XX +XXX,XX @@ get_image_size_on_host() | ||
43 | |||
44 | _supported_fmt qcow2 | ||
45 | _supported_proto file | ||
46 | +# Growing a file with a backing file (without preallocation=full or | ||
47 | +# =falloc) requires zeroing the newly added area, which is impossible | ||
48 | +# to do quickly for v2 images, and hence is unsupported. | ||
49 | +_unsupported_imgopts 'compat=0.10' | ||
50 | |||
51 | if [ -z "$TEST_IMG_FILE" ]; then | ||
52 | TEST_IMG_FILE=$TEST_IMG | ||
53 | @@ -XXX,XX +XXX,XX @@ done | ||
54 | $QEMU_IMG create -f raw "$TEST_IMG.base" 128k | _filter_img_create | ||
55 | $QEMU_IO -c 'write -q -P 1 0 128k' -f raw "$TEST_IMG.base" | ||
56 | for orig_size in 31k 33k; do | ||
57 | - echo "--- Resizing image from $orig_size to 96k ---" | ||
58 | - _make_test_img -F raw -b "$TEST_IMG.base" -o cluster_size=64k "$orig_size" | ||
59 | - $QEMU_IMG resize -f "$IMGFMT" --preallocation=full "$TEST_IMG" 96k | ||
60 | - # The first part of the image should contain data from the backing file | ||
61 | - $QEMU_IO -c "read -q -P 1 0 ${orig_size}" "$TEST_IMG" | ||
62 | - # The resized part of the image should contain zeroes | ||
63 | - $QEMU_IO -c "read -q -P 0 ${orig_size} 63k" "$TEST_IMG" | ||
64 | - # If the image does not have an external data file we can also verify its | ||
65 | - # actual size. The resized image should have 7 clusters: | ||
66 | - # header, L1 table, L2 table, refcount table, refcount block, 2 data clusters | ||
67 | - if ! _get_data_file "$TEST_IMG" > /dev/null; then | ||
68 | - expected_file_length=$((65536 * 7)) | ||
69 | - file_length=$(stat -c '%s' "$TEST_IMG_FILE") | ||
70 | - if [ "$file_length" != "$expected_file_length" ]; then | ||
71 | - echo "ERROR: file length $file_length (expected $expected_file_length)" | ||
72 | - fi | ||
73 | - fi | ||
74 | - echo | ||
75 | + for dst_size in 96k 128k; do | ||
76 | + for prealloc in metadata full; do | ||
77 | + echo "--- Resizing image from $orig_size to $dst_size (preallocation=$prealloc) ---" | ||
78 | + _make_test_img -F raw -b "$TEST_IMG.base" -o cluster_size=64k "$orig_size" | ||
79 | + $QEMU_IMG resize -f "$IMGFMT" --preallocation="$prealloc" "$TEST_IMG" "$dst_size" | ||
80 | + # The first part of the image should contain data from the backing file | ||
81 | + $QEMU_IO -c "read -q -P 1 0 ${orig_size}" "$TEST_IMG" | ||
82 | + # The resized part of the image should contain zeroes | ||
83 | + $QEMU_IO -c "read -q -P 0 ${orig_size} 63k" "$TEST_IMG" | ||
84 | + # If the image does not have an external data file we can also verify its | ||
85 | + # actual size. The resized image should have 7 clusters: | ||
86 | + # header, L1 table, L2 table, refcount table, refcount block, 2 data clusters | ||
87 | + if ! _get_data_file "$TEST_IMG" > /dev/null; then | ||
88 | + expected_file_length=$((65536 * 7)) | ||
89 | + file_length=$(stat -c '%s' "$TEST_IMG_FILE") | ||
90 | + if [ "$file_length" != "$expected_file_length" ]; then | ||
91 | + echo "ERROR: file length $file_length (expected $expected_file_length)" | ||
92 | + fi | ||
93 | + fi | ||
94 | + echo | ||
95 | + done | ||
96 | + done | ||
97 | done | ||
98 | |||
99 | # success, all done | ||
100 | diff --git a/tests/qemu-iotests/125.out b/tests/qemu-iotests/125.out | ||
101 | index XXXXXXX..XXXXXXX 100644 | ||
102 | --- a/tests/qemu-iotests/125.out | ||
103 | +++ b/tests/qemu-iotests/125.out | ||
104 | @@ -XXX,XX +XXX,XX @@ wrote 81920/81920 bytes at offset 2048000 | ||
105 | 80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
106 | |||
107 | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=raw size=131072 | ||
108 | ---- Resizing image from 31k to 96k --- | ||
109 | +--- Resizing image from 31k to 96k (preallocation=metadata) --- | ||
110 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=31744 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw | ||
111 | Image resized. | ||
112 | |||
113 | ---- Resizing image from 33k to 96k --- | ||
114 | +--- Resizing image from 31k to 96k (preallocation=full) --- | ||
115 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=31744 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw | ||
116 | +Image resized. | ||
117 | + | ||
118 | +--- Resizing image from 31k to 128k (preallocation=metadata) --- | ||
119 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=31744 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw | ||
120 | +Image resized. | ||
121 | + | ||
122 | +--- Resizing image from 31k to 128k (preallocation=full) --- | ||
123 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=31744 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw | ||
124 | +Image resized. | ||
125 | + | ||
126 | +--- Resizing image from 33k to 96k (preallocation=metadata) --- | ||
127 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33792 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw | ||
128 | +Image resized. | ||
129 | + | ||
130 | +--- Resizing image from 33k to 96k (preallocation=full) --- | ||
131 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33792 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw | ||
132 | +Image resized. | ||
133 | + | ||
134 | +--- Resizing image from 33k to 128k (preallocation=metadata) --- | ||
135 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33792 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw | ||
136 | +Image resized. | ||
137 | + | ||
138 | +--- Resizing image from 33k to 128k (preallocation=full) --- | ||
139 | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33792 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw | ||
140 | Image resized. | ||
141 | |||
142 | -- | ||
143 | 2.26.2 | ||
144 | |||
145 | diff view generated by jsdifflib |
1 | From: John Snow <jsnow@redhat.com> | 1 | From: Alberto Garcia <berto@igalia.com> |
---|---|---|---|
2 | 2 | ||
3 | Jobs are now expected to return their retcode on the stack, from the | 3 | qcow2_alloc_cluster_offset() takes an (unaligned) guest offset and |
4 | .run callback, so we can remove that argument. | 4 | returns the (aligned) offset of the corresponding cluster in the qcow2 |
5 | image. | ||
5 | 6 | ||
6 | job_cancel does not need to set -ECANCELED because job_completed will | 7 | In practice none of the callers need to know where the cluster starts |
7 | update the return code itself if the job was canceled. | 8 | so this patch makes the function calculate and return the final host |
9 | offset directly. The function is also renamed accordingly. | ||
8 | 10 | ||
9 | While we're here, make job_completed static to job.c and remove it from | 11 | See 388e581615 for a similar change to qcow2_get_cluster_offset(). |
10 | job.h; move the documentation of return code to the .run() callback and | ||
11 | to the job->ret property, accordingly. | ||
12 | 12 | ||
13 | Signed-off-by: John Snow <jsnow@redhat.com> | 13 | Signed-off-by: Alberto Garcia <berto@igalia.com> |
14 | Message-id: 20180830015734.19765-9-jsnow@redhat.com | 14 | Message-Id: <9bfef50ec9200d752413be4fc2aeb22a28378817.1599833007.git.berto@igalia.com> |
15 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 15 | Reviewed-by: Max Reitz <mreitz@redhat.com> |
16 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 16 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
17 | --- | 17 | --- |
18 | include/qemu/job.h | 28 +++++++++++++++------------- | 18 | block/qcow2.h | 6 +++--- |
19 | job.c | 11 ++++++----- | 19 | block/qcow2-cluster.c | 14 ++++++++++---- |
20 | trace-events | 2 +- | 20 | block/qcow2.c | 36 +++++++++++++----------------------- |
21 | 3 files changed, 22 insertions(+), 19 deletions(-) | 21 | 3 files changed, 26 insertions(+), 30 deletions(-) |
22 | 22 | ||
23 | diff --git a/include/qemu/job.h b/include/qemu/job.h | 23 | diff --git a/block/qcow2.h b/block/qcow2.h |
24 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
25 | --- a/include/qemu/job.h | 25 | --- a/block/qcow2.h |
26 | +++ b/include/qemu/job.h | 26 | +++ b/block/qcow2.h |
27 | @@ -XXX,XX +XXX,XX @@ typedef struct Job { | 27 | @@ -XXX,XX +XXX,XX @@ int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, |
28 | /** Estimated progress_current value at the completion of the job */ | 28 | int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, |
29 | int64_t progress_total; | 29 | unsigned int *bytes, uint64_t *host_offset, |
30 | 30 | QCow2SubclusterType *subcluster_type); | |
31 | - /** ret code passed to job_completed. */ | 31 | -int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, |
32 | + /** | 32 | - unsigned int *bytes, uint64_t *host_offset, |
33 | + * Return code from @run and/or @prepare callback(s). | 33 | - QCowL2Meta **m); |
34 | + * Not final until the job has reached the CONCLUDED status. | 34 | +int qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, |
35 | + * 0 on success, -errno on failure. | 35 | + unsigned int *bytes, uint64_t *host_offset, |
36 | + */ | 36 | + QCowL2Meta **m); |
37 | int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | ||
38 | uint64_t offset, | ||
39 | int compressed_size, | ||
40 | diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c | ||
41 | index XXXXXXX..XXXXXXX 100644 | ||
42 | --- a/block/qcow2-cluster.c | ||
43 | +++ b/block/qcow2-cluster.c | ||
44 | @@ -XXX,XX +XXX,XX @@ out: | ||
45 | * clusters (or subclusters) if necessary. The result can span a | ||
46 | * combination of allocated and previously unallocated clusters. | ||
47 | * | ||
48 | + * Note that offset may not be cluster aligned. In this case, the returned | ||
49 | + * *host_offset points to exact byte referenced by offset and therefore | ||
50 | + * isn't cluster aligned as well. | ||
51 | + * | ||
52 | * On return, @host_offset is set to the beginning of the requested | ||
53 | * area. This area is guaranteed to be contiguous on the qcow2 file | ||
54 | * but it can be smaller than initially requested. In this case @bytes | ||
55 | @@ -XXX,XX +XXX,XX @@ out: | ||
56 | * | ||
57 | * Return 0 on success and -errno in error cases | ||
58 | */ | ||
59 | -int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||
60 | - unsigned int *bytes, uint64_t *host_offset, | ||
61 | - QCowL2Meta **m) | ||
62 | +int qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, | ||
63 | + unsigned int *bytes, uint64_t *host_offset, | ||
64 | + QCowL2Meta **m) | ||
65 | { | ||
66 | BDRVQcow2State *s = bs->opaque; | ||
67 | uint64_t start, remaining; | ||
68 | @@ -XXX,XX +XXX,XX @@ again: | ||
69 | while (true) { | ||
70 | |||
71 | if (*host_offset == INV_OFFSET && cluster_offset != INV_OFFSET) { | ||
72 | - *host_offset = start_of_cluster(s, cluster_offset); | ||
73 | + *host_offset = cluster_offset; | ||
74 | } | ||
75 | |||
76 | assert(remaining >= cur_bytes); | ||
77 | @@ -XXX,XX +XXX,XX @@ again: | ||
78 | *bytes -= remaining; | ||
79 | assert(*bytes > 0); | ||
80 | assert(*host_offset != INV_OFFSET); | ||
81 | + assert(offset_into_cluster(s, *host_offset) == | ||
82 | + offset_into_cluster(s, offset)); | ||
83 | |||
84 | return 0; | ||
85 | } | ||
86 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
87 | index XXXXXXX..XXXXXXX 100644 | ||
88 | --- a/block/qcow2.c | ||
89 | +++ b/block/qcow2.c | ||
90 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_pwritev_part( | ||
91 | int offset_in_cluster; | ||
37 | int ret; | 92 | int ret; |
38 | 93 | unsigned int cur_bytes; /* number of sectors in current iteration */ | |
39 | /** | 94 | - uint64_t cluster_offset; |
40 | @@ -XXX,XX +XXX,XX @@ struct JobDriver { | 95 | + uint64_t host_offset; |
41 | /** Enum describing the operation */ | 96 | QCowL2Meta *l2meta = NULL; |
42 | JobType job_type; | 97 | AioTaskPool *aio = NULL; |
43 | 98 | ||
44 | - /** Mandatory: Entrypoint for the Coroutine. */ | 99 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_pwritev_part( |
45 | + /** | 100 | |
46 | + * Mandatory: Entrypoint for the Coroutine. | 101 | qemu_co_mutex_lock(&s->lock); |
47 | + * | 102 | |
48 | + * This callback will be invoked when moving from CREATED to RUNNING. | 103 | - ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes, |
49 | + * | 104 | - &cluster_offset, &l2meta); |
50 | + * If this callback returns nonzero, the job transaction it is part of is | 105 | + ret = qcow2_alloc_host_offset(bs, offset, &cur_bytes, |
51 | + * aborted. If it returns zero, the job moves into the WAITING state. If it | 106 | + &host_offset, &l2meta); |
52 | + * is the last job to complete in its transaction, all jobs in the | 107 | if (ret < 0) { |
53 | + * transaction move from WAITING to PENDING. | 108 | goto out_locked; |
54 | + */ | 109 | } |
55 | int coroutine_fn (*run)(Job *job, Error **errp); | 110 | |
56 | 111 | - assert(offset_into_cluster(s, cluster_offset) == 0); | |
57 | /** | ||
58 | @@ -XXX,XX +XXX,XX @@ void job_early_fail(Job *job); | ||
59 | /** Moves the @job from RUNNING to READY */ | ||
60 | void job_transition_to_ready(Job *job); | ||
61 | |||
62 | -/** | ||
63 | - * @job: The job being completed. | ||
64 | - * @ret: The status code. | ||
65 | - * | ||
66 | - * Marks @job as completed. If @ret is non-zero, the job transaction it is part | ||
67 | - * of is aborted. If @ret is zero, the job moves into the WAITING state. If it | ||
68 | - * is the last job to complete in its transaction, all jobs in the transaction | ||
69 | - * move from WAITING to PENDING. | ||
70 | - */ | ||
71 | -void job_completed(Job *job, int ret); | ||
72 | - | 112 | - |
73 | /** Asynchronously complete the specified @job. */ | 113 | - ret = qcow2_pre_write_overlap_check(bs, 0, |
74 | void job_complete(Job *job, Error **errp); | 114 | - cluster_offset + offset_in_cluster, |
75 | 115 | + ret = qcow2_pre_write_overlap_check(bs, 0, host_offset, | |
76 | diff --git a/job.c b/job.c | 116 | cur_bytes, true); |
77 | index XXXXXXX..XXXXXXX 100644 | 117 | if (ret < 0) { |
78 | --- a/job.c | 118 | goto out_locked; |
79 | +++ b/job.c | 119 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_pwritev_part( |
80 | @@ -XXX,XX +XXX,XX @@ void job_drain(Job *job) | 120 | aio = aio_task_pool_new(QCOW2_MAX_WORKERS); |
81 | } | 121 | } |
82 | } | 122 | ret = qcow2_add_task(bs, aio, qcow2_co_pwritev_task_entry, 0, |
83 | 123 | - cluster_offset + offset_in_cluster, offset, | |
84 | +static void job_completed(Job *job); | 124 | + host_offset, offset, |
85 | + | 125 | cur_bytes, qiov, qiov_offset, l2meta); |
86 | static void job_exit(void *opaque) | 126 | l2meta = NULL; /* l2meta is consumed by qcow2_co_pwritev_task() */ |
127 | if (ret < 0) { | ||
128 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, | ||
129 | |||
130 | while (bytes) { | ||
131 | cur_bytes = MIN(bytes, QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size)); | ||
132 | - ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes, | ||
133 | - &host_offset, &meta); | ||
134 | + ret = qcow2_alloc_host_offset(bs, offset, &cur_bytes, | ||
135 | + &host_offset, &meta); | ||
136 | if (ret < 0) { | ||
137 | error_setg_errno(errp, -ret, "Allocating clusters failed"); | ||
138 | goto out; | ||
139 | } | ||
140 | - host_offset += offset_into_cluster(s, offset); | ||
141 | |||
142 | for (m = meta; m != NULL; m = m->next) { | ||
143 | m->prealloc = true; | ||
144 | @@ -XXX,XX +XXX,XX @@ qcow2_co_copy_range_to(BlockDriverState *bs, | ||
145 | BdrvRequestFlags write_flags) | ||
87 | { | 146 | { |
88 | Job *job = (Job *)opaque; | 147 | BDRVQcow2State *s = bs->opaque; |
89 | @@ -XXX,XX +XXX,XX @@ static void job_exit(void *opaque) | 148 | - int offset_in_cluster; |
90 | job->driver->exit(job); | 149 | int ret; |
91 | aio_context_release(aio_context); | 150 | unsigned int cur_bytes; /* number of sectors in current iteration */ |
92 | } | 151 | - uint64_t cluster_offset; |
93 | - job_completed(job, job->ret); | 152 | + uint64_t host_offset; |
94 | + job_completed(job); | 153 | QCowL2Meta *l2meta = NULL; |
95 | } | 154 | |
96 | 155 | assert(!bs->encrypted); | |
97 | /** | 156 | @@ -XXX,XX +XXX,XX @@ qcow2_co_copy_range_to(BlockDriverState *bs, |
98 | @@ -XXX,XX +XXX,XX @@ static void job_completed_txn_success(Job *job) | 157 | |
99 | } | 158 | l2meta = NULL; |
100 | } | 159 | |
101 | 160 | - offset_in_cluster = offset_into_cluster(s, dst_offset); | |
102 | -void job_completed(Job *job, int ret) | 161 | cur_bytes = MIN(bytes, INT_MAX); |
103 | +static void job_completed(Job *job) | 162 | |
104 | { | 163 | /* TODO: |
105 | assert(job && job->txn && !job_is_completed(job)); | 164 | * If src->bs == dst->bs, we could simply copy by incrementing |
106 | 165 | * the refcnt, without copying user data. | |
107 | - job->ret = ret; | 166 | * Or if src->bs == dst->bs->backing->bs, we could copy by discarding. */ |
108 | job_update_rc(job); | 167 | - ret = qcow2_alloc_cluster_offset(bs, dst_offset, &cur_bytes, |
109 | - trace_job_completed(job, ret, job->ret); | 168 | - &cluster_offset, &l2meta); |
110 | + trace_job_completed(job, job->ret); | 169 | + ret = qcow2_alloc_host_offset(bs, dst_offset, &cur_bytes, |
111 | if (job->ret) { | 170 | + &host_offset, &l2meta); |
112 | job_completed_txn_abort(job); | 171 | if (ret < 0) { |
113 | } else { | 172 | goto fail; |
114 | @@ -XXX,XX +XXX,XX @@ void job_cancel(Job *job, bool force) | 173 | } |
115 | } | 174 | |
116 | job_cancel_async(job, force); | 175 | - assert(offset_into_cluster(s, cluster_offset) == 0); |
117 | if (!job_started(job)) { | 176 | - |
118 | - job_completed(job, -ECANCELED); | 177 | - ret = qcow2_pre_write_overlap_check(bs, 0, |
119 | + job_completed(job); | 178 | - cluster_offset + offset_in_cluster, cur_bytes, true); |
120 | } else if (job->deferred_to_main_loop) { | 179 | + ret = qcow2_pre_write_overlap_check(bs, 0, host_offset, cur_bytes, |
121 | job_completed_txn_abort(job); | 180 | + true); |
122 | } else { | 181 | if (ret < 0) { |
123 | diff --git a/trace-events b/trace-events | 182 | goto fail; |
124 | index XXXXXXX..XXXXXXX 100644 | 183 | } |
125 | --- a/trace-events | 184 | |
126 | +++ b/trace-events | 185 | qemu_co_mutex_unlock(&s->lock); |
127 | @@ -XXX,XX +XXX,XX @@ gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packe | 186 | - ret = bdrv_co_copy_range_to(src, src_offset, |
128 | # job.c | 187 | - s->data_file, |
129 | job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" | 188 | - cluster_offset + offset_in_cluster, |
130 | job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" | 189 | + ret = bdrv_co_copy_range_to(src, src_offset, s->data_file, host_offset, |
131 | -job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" | 190 | cur_bytes, read_flags, write_flags); |
132 | +job_completed(void *job, int ret) "job %p ret %d" | 191 | qemu_co_mutex_lock(&s->lock); |
133 | 192 | if (ret < 0) { | |
134 | # job-qmp.c | ||
135 | qmp_job_cancel(void *job) "job %p" | ||
136 | -- | 193 | -- |
137 | 2.17.1 | 194 | 2.26.2 |
138 | 195 | ||
139 | 196 | diff view generated by jsdifflib |
1 | From: Marc-André Lureau <marcandre.lureau@redhat.com> | 1 | From: Stefano Garzarella <sgarzare@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Spotted by ASAN: | 3 | Commit 19ae9ae014 ("block/rbd: Add support for ceph namespaces") |
4 | introduced namespace support for RBD, but we forgot to add the | ||
5 | new 'namespace' options to qemu_rbd_strong_runtime_opts[]. | ||
4 | 6 | ||
5 | ================================================================= | 7 | The 'namespace' is used to identify the image, so it is a strong |
6 | ==5378==ERROR: LeakSanitizer: detected memory leaks | 8 | option since it can changes the data of a BDS. |
7 | 9 | ||
8 | Direct leak of 65536 byte(s) in 1 object(s) allocated from: | 10 | Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1821528 |
9 | #0 0x7f788f83bc48 in malloc (/lib64/libasan.so.5+0xeec48) | 11 | Fixes: 19ae9ae014 ("block/rbd: Add support for ceph namespaces") |
10 | #1 0x7f788c9923c5 in g_malloc (/lib64/libglib-2.0.so.0+0x523c5) | 12 | Cc: Florian Florensa <fflorensa@online.net> |
11 | #2 0x5622a1fe37bc in coroutine_trampoline /home/elmarco/src/qq/util/coroutine-ucontext.c:116 | 13 | Signed-off-by: Stefano Garzarella <sgarzare@redhat.com> |
12 | #3 0x7f788a15d75f in __correctly_grouped_prefixwc (/lib64/libc.so.6+0x4c75f) | 14 | Message-Id: <20200914190553.74871-1-sgarzare@redhat.com> |
13 | 15 | Reviewed-by: Jason Dillaman <dillaman@redhat.com> | |
14 | (Broken in commit 4c8158e359d.) | ||
15 | |||
16 | Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> | ||
17 | Message-id: 20180809114417.28718-3-marcandre.lureau@redhat.com | ||
18 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 16 | Signed-off-by: Max Reitz <mreitz@redhat.com> |
19 | --- | 17 | --- |
20 | tests/test-bdrv-drain.c | 1 + | 18 | block/rbd.c | 1 + |
21 | 1 file changed, 1 insertion(+) | 19 | 1 file changed, 1 insertion(+) |
22 | 20 | ||
23 | diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c | 21 | diff --git a/block/rbd.c b/block/rbd.c |
24 | index XXXXXXX..XXXXXXX 100644 | 22 | index XXXXXXX..XXXXXXX 100644 |
25 | --- a/tests/test-bdrv-drain.c | 23 | --- a/block/rbd.c |
26 | +++ b/tests/test-bdrv-drain.c | 24 | +++ b/block/rbd.c |
27 | @@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_co_delete_by_drain(void *opaque) | 25 | @@ -XXX,XX +XXX,XX @@ static QemuOptsList qemu_rbd_create_opts = { |
28 | } | 26 | |
29 | 27 | static const char *const qemu_rbd_strong_runtime_opts[] = { | |
30 | dbdd->done = true; | 28 | "pool", |
31 | + g_free(buffer); | 29 | + "namespace", |
32 | } | 30 | "image", |
33 | 31 | "conf", | |
34 | /** | 32 | "snapshot", |
35 | -- | 33 | -- |
36 | 2.17.1 | 34 | 2.26.2 |
37 | 35 | ||
38 | 36 | diff view generated by jsdifflib |