1 | The following changes since commit 5704c36d25ee84e7129722cb0db53df9faefe943: | 1 | The following changes since commit 42f6c9179be4401974dd3a75ee72defd16b5092d: |
---|---|---|---|
2 | 2 | ||
3 | Merge remote-tracking branch 'remotes/kraxel/tags/fixes-31-20181112-pull-request' into staging (2018-11-12 15:55:40 +0000) | 3 | Merge tag 'pull-ppc-20211112' of https://github.com/legoater/qemu into staging (2021-11-12 12:28:25 +0100) |
4 | 4 | ||
5 | are available in the Git repository at: | 5 | are available in the Git repository at: |
6 | 6 | ||
7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream | 7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream |
8 | 8 | ||
9 | for you to fetch changes up to 1a42e5d8298d1b0f90d2254e7d559391dd3a45ca: | 9 | for you to fetch changes up to 7461272c5f6032436ef9032c091c0118539483e4: |
10 | 10 | ||
11 | Merge remote-tracking branch 'mreitz/tags/pull-block-2018-11-12' into queue-block (2018-11-12 17:57:32 +0100) | 11 | softmmu/qdev-monitor: fix use-after-free in qdev_set_id() (2021-11-15 15:49:46 +0100) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Block layer patches: | 14 | Block layer patches |
15 | 15 | ||
16 | - file-posix: Don't waste a file descriptor for locking, don't lock the | 16 | - Fixes to image streaming job and block layer reconfiguration to make |
17 | same bit multiple times | 17 | iotest 030 pass again |
18 | - nvme: Fix double free and memory leak | 18 | - docs: Deprecate incorrectly typed device_add arguments |
19 | - Misc error handling fixes | 19 | - file-posix: Fix alignment after reopen changing O_DIRECT |
20 | - Added NULL checks found by static analysis | ||
21 | - Allow more block drivers to not be included in the qemu build | ||
22 | 20 | ||
23 | ---------------------------------------------------------------- | 21 | ---------------------------------------------------------------- |
24 | Fam Zheng (4): | 22 | Hanna Reitz (10): |
25 | file-posix: Use error API properly | 23 | stream: Traverse graph after modification |
26 | file-posix: Skip effectiveless OFD lock operations | 24 | block: Manipulate children list in .attach/.detach |
27 | file-posix: Drop s->lock_fd | 25 | block: Unite remove_empty_child and child_free |
28 | tests: Add unit tests for image locking | 26 | block: Drop detached child from ignore list |
27 | block: Pass BdrvChild ** to replace_child_noperm | ||
28 | block: Restructure remove_file_or_backing_child() | ||
29 | transactions: Invoke clean() after everything else | ||
30 | block: Let replace_child_tran keep indirect pointer | ||
31 | block: Let replace_child_noperm free children | ||
32 | iotests/030: Unthrottle parallel jobs in reverse | ||
29 | 33 | ||
30 | Jeff Cody (1): | 34 | Kevin Wolf (2): |
31 | block: Make more block drivers compile-time configurable | 35 | docs: Deprecate incorrectly typed device_add arguments |
36 | file-posix: Fix alignment after reopen changing O_DIRECT | ||
32 | 37 | ||
33 | Kevin Wolf (1): | 38 | Stefan Hajnoczi (1): |
34 | Merge remote-tracking branch 'mreitz/tags/pull-block-2018-11-12' into queue-block | 39 | softmmu/qdev-monitor: fix use-after-free in qdev_set_id() |
35 | 40 | ||
36 | Li Qiang (2): | 41 | docs/about/deprecated.rst | 14 +++ |
37 | nvme: don't unref ctrl_mem when device unrealized | 42 | include/qemu/transactions.h | 3 + |
38 | nvme: free cmbuf in nvme_exit | 43 | block.c | 233 +++++++++++++++++++++++++++++++++----------- |
44 | block/file-posix.c | 20 +++- | ||
45 | block/stream.c | 7 +- | ||
46 | softmmu/qdev-monitor.c | 2 +- | ||
47 | util/transactions.c | 8 +- | ||
48 | tests/qemu-iotests/030 | 11 ++- | ||
49 | tests/qemu-iotests/142 | 22 +++++ | ||
50 | tests/qemu-iotests/142.out | 15 +++ | ||
51 | 10 files changed, 269 insertions(+), 66 deletions(-) | ||
39 | 52 | ||
40 | Liam Merwick (5): | ||
41 | job: Fix off-by-one assert checks for JobSTT and JobVerbTable | ||
42 | block: Null pointer dereference in blk_root_get_parent_desc() | ||
43 | qemu-img: assert block_job_get() does not return NULL in img_commit() | ||
44 | block: Fix potential Null pointer dereferences in vvfat.c | ||
45 | qcow2: Read outside array bounds in qcow2_pre_write_overlap_check() | ||
46 | 53 | ||
47 | Peter Maydell (1): | ||
48 | blockdev: Consistently use snapshot_node_name in external_snapshot_prepare() | ||
49 | |||
50 | zhenwei pi (1): | ||
51 | blockdev: handle error on block latency histogram set error | ||
52 | |||
53 | configure | 91 ++++++++++++++++++++++++++ | ||
54 | block/block-backend.c | 3 +- | ||
55 | block/file-posix.c | 122 ++++++++++++++++++++--------------- | ||
56 | block/qcow2-refcount.c | 18 +++--- | ||
57 | block/vvfat.c | 46 ++++++++----- | ||
58 | blockdev.c | 21 ++++-- | ||
59 | hw/block/nvme.c | 6 +- | ||
60 | job.c | 4 +- | ||
61 | qemu-img.c | 1 + | ||
62 | tests/test-image-locking.c | 157 +++++++++++++++++++++++++++++++++++++++++++++ | ||
63 | block/Makefile.objs | 22 +++++-- | ||
64 | tests/Makefile.include | 2 + | ||
65 | 12 files changed, 400 insertions(+), 93 deletions(-) | ||
66 | create mode 100644 tests/test-image-locking.c | ||
67 | diff view generated by jsdifflib |
1 | From: Liam Merwick <Liam.Merwick@oracle.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Although the function block_job_get() can return NULL, it would be a | 3 | bdrv_cor_filter_drop() modifies the block graph. That means that other |
4 | serious bug if it did so (because the job yields before executing anything | 4 | parties can also modify the block graph before it returns. Therefore, |
5 | (if it started successfully); but otherwise, commit_active_start() would | 5 | we cannot assume that the result of a graph traversal we did before |
6 | have returned an error). However, as a precaution, before dereferencing | 6 | remains valid afterwards. |
7 | the 'job' pointer in img_commit() assert it is not NULL. | ||
8 | 7 | ||
9 | Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com> | 8 | We should thus fetch `base` and `unfiltered_base` afterwards instead of |
10 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 9 | before. |
11 | Message-id: 1541453919-25973-4-git-send-email-Liam.Merwick@oracle.com | 10 | |
12 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 11 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> |
12 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
14 | Message-Id: <20211111120829.81329-2-hreitz@redhat.com> | ||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | --- | 16 | --- |
14 | qemu-img.c | 1 + | 17 | block/stream.c | 7 +++++-- |
15 | 1 file changed, 1 insertion(+) | 18 | 1 file changed, 5 insertions(+), 2 deletions(-) |
16 | 19 | ||
17 | diff --git a/qemu-img.c b/qemu-img.c | 20 | diff --git a/block/stream.c b/block/stream.c |
18 | index XXXXXXX..XXXXXXX 100644 | 21 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/qemu-img.c | 22 | --- a/block/stream.c |
20 | +++ b/qemu-img.c | 23 | +++ b/block/stream.c |
21 | @@ -XXX,XX +XXX,XX @@ static int img_commit(int argc, char **argv) | 24 | @@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job) |
22 | } | 25 | { |
23 | 26 | StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); | |
24 | job = block_job_get("commit"); | 27 | BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs); |
25 | + assert(job); | 28 | - BlockDriverState *base = bdrv_filter_or_cow_bs(s->above_base); |
26 | run_block_job(job, &local_err); | 29 | - BlockDriverState *unfiltered_base = bdrv_skip_filters(base); |
27 | if (local_err) { | 30 | + BlockDriverState *base; |
28 | goto unref_backing; | 31 | + BlockDriverState *unfiltered_base; |
32 | Error *local_err = NULL; | ||
33 | int ret = 0; | ||
34 | |||
35 | @@ -XXX,XX +XXX,XX @@ static int stream_prepare(Job *job) | ||
36 | bdrv_cor_filter_drop(s->cor_filter_bs); | ||
37 | s->cor_filter_bs = NULL; | ||
38 | |||
39 | + base = bdrv_filter_or_cow_bs(s->above_base); | ||
40 | + unfiltered_base = bdrv_skip_filters(base); | ||
41 | + | ||
42 | if (bdrv_cow_child(unfiltered_bs)) { | ||
43 | const char *base_id = NULL, *base_fmt = NULL; | ||
44 | if (unfiltered_base) { | ||
29 | -- | 45 | -- |
30 | 2.19.1 | 46 | 2.31.1 |
31 | 47 | ||
32 | 48 | diff view generated by jsdifflib |
1 | From: Fam Zheng <famz@redhat.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | The lock_fd field is not strictly necessary because transferring locked | 3 | The children list is specific to BDS parents. We should not modify it |
4 | bytes from old fd to the new one shouldn't fail anyway. This spares the | 4 | in the general children modification code, but let BDS parents deal with |
5 | user one fd per image. | 5 | it in their .attach() and .detach() methods. |
6 | 6 | ||
7 | Signed-off-by: Fam Zheng <famz@redhat.com> | 7 | This also has the advantage that a BdrvChild is removed from the |
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 8 | children list before its .bs pointer can become NULL. BDS parents |
9 | generally assume that their children's .bs pointer is never NULL, so | ||
10 | this is actually a bug fix. | ||
11 | |||
12 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> | ||
13 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
14 | Message-Id: <20211111120829.81329-3-hreitz@redhat.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | --- | 16 | --- |
11 | block/file-posix.c | 37 +++++++++++++------------------------ | 17 | block.c | 14 +++++--------- |
12 | 1 file changed, 13 insertions(+), 24 deletions(-) | 18 | 1 file changed, 5 insertions(+), 9 deletions(-) |
13 | 19 | ||
14 | diff --git a/block/file-posix.c b/block/file-posix.c | 20 | diff --git a/block.c b/block.c |
15 | index XXXXXXX..XXXXXXX 100644 | 21 | index XXXXXXX..XXXXXXX 100644 |
16 | --- a/block/file-posix.c | 22 | --- a/block.c |
17 | +++ b/block/file-posix.c | 23 | +++ b/block.c |
18 | @@ -XXX,XX +XXX,XX @@ do { \ | 24 | @@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_attach(BdrvChild *child) |
19 | 25 | { | |
20 | typedef struct BDRVRawState { | 26 | BlockDriverState *bs = child->opaque; |
21 | int fd; | 27 | |
22 | - int lock_fd; | 28 | + QLIST_INSERT_HEAD(&bs->children, child, next); |
23 | bool use_lock; | 29 | + |
24 | int type; | 30 | if (child->role & BDRV_CHILD_COW) { |
25 | int open_flags; | 31 | bdrv_backing_attach(child); |
26 | @@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState { | ||
27 | uint64_t shared_perm; | ||
28 | |||
29 | /* The perms bits whose corresponding bytes are already locked in | ||
30 | - * s->lock_fd. */ | ||
31 | + * s->fd. */ | ||
32 | uint64_t locked_perm; | ||
33 | uint64_t locked_shared_perm; | ||
34 | |||
35 | @@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options, | ||
36 | } | 32 | } |
37 | s->fd = fd; | 33 | @@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_detach(BdrvChild *child) |
38 | |||
39 | - s->lock_fd = -1; | ||
40 | - if (s->use_lock) { | ||
41 | - fd = qemu_open(filename, s->open_flags); | ||
42 | - if (fd < 0) { | ||
43 | - ret = -errno; | ||
44 | - error_setg_errno(errp, errno, "Could not open '%s' for locking", | ||
45 | - filename); | ||
46 | - qemu_close(s->fd); | ||
47 | - goto fail; | ||
48 | - } | ||
49 | - s->lock_fd = fd; | ||
50 | - } | ||
51 | s->perm = 0; | ||
52 | s->shared_perm = BLK_PERM_ALL; | ||
53 | |||
54 | @@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs, | ||
55 | return 0; | ||
56 | } | 34 | } |
57 | 35 | ||
58 | - assert(s->lock_fd > 0); | 36 | bdrv_unapply_subtree_drain(child, bs); |
37 | + | ||
38 | + QLIST_REMOVE(child, next); | ||
39 | } | ||
40 | |||
41 | static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base, | ||
42 | @@ -XXX,XX +XXX,XX @@ static void bdrv_child_free(void *opaque) | ||
43 | static void bdrv_remove_empty_child(BdrvChild *child) | ||
44 | { | ||
45 | assert(!child->bs); | ||
46 | - QLIST_SAFE_REMOVE(child, next); | ||
47 | + assert(!child->next.le_prev); /* not in children list */ | ||
48 | bdrv_child_free(child); | ||
49 | } | ||
50 | |||
51 | @@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs, | ||
52 | return ret; | ||
53 | } | ||
54 | |||
55 | - QLIST_INSERT_HEAD(&parent_bs->children, *child, next); | ||
56 | - /* | ||
57 | - * child is removed in bdrv_attach_child_common_abort(), so don't care to | ||
58 | - * abort this change separately. | ||
59 | - */ | ||
59 | - | 60 | - |
60 | switch (op) { | 61 | return 0; |
61 | case RAW_PL_PREPARE: | ||
62 | - ret = raw_apply_lock_bytes(s, s->lock_fd, s->perm | new_perm, | ||
63 | + ret = raw_apply_lock_bytes(s, s->fd, s->perm | new_perm, | ||
64 | ~s->shared_perm | ~new_shared, | ||
65 | false, errp); | ||
66 | if (!ret) { | ||
67 | - ret = raw_check_lock_bytes(s->lock_fd, new_perm, new_shared, errp); | ||
68 | + ret = raw_check_lock_bytes(s->fd, new_perm, new_shared, errp); | ||
69 | if (!ret) { | ||
70 | return 0; | ||
71 | } | ||
72 | @@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs, | ||
73 | op = RAW_PL_ABORT; | ||
74 | /* fall through to unlock bytes. */ | ||
75 | case RAW_PL_ABORT: | ||
76 | - raw_apply_lock_bytes(s, s->lock_fd, s->perm, ~s->shared_perm, | ||
77 | + raw_apply_lock_bytes(s, s->fd, s->perm, ~s->shared_perm, | ||
78 | true, &local_err); | ||
79 | if (local_err) { | ||
80 | /* Theoretically the above call only unlocks bytes and it cannot | ||
81 | @@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs, | ||
82 | } | ||
83 | break; | ||
84 | case RAW_PL_COMMIT: | ||
85 | - raw_apply_lock_bytes(s, s->lock_fd, new_perm, ~new_shared, | ||
86 | + raw_apply_lock_bytes(s, s->fd, new_perm, ~new_shared, | ||
87 | true, &local_err); | ||
88 | if (local_err) { | ||
89 | /* Theoretically the above call only unlocks bytes and it cannot | ||
90 | @@ -XXX,XX +XXX,XX @@ static void raw_reopen_commit(BDRVReopenState *state) | ||
91 | { | ||
92 | BDRVRawReopenState *rs = state->opaque; | ||
93 | BDRVRawState *s = state->bs->opaque; | ||
94 | + Error *local_err = NULL; | ||
95 | |||
96 | s->check_cache_dropped = rs->check_cache_dropped; | ||
97 | s->open_flags = rs->open_flags; | ||
98 | |||
99 | + /* Copy locks to the new fd before closing the old one. */ | ||
100 | + raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm, | ||
101 | + ~s->locked_shared_perm, false, &local_err); | ||
102 | + if (local_err) { | ||
103 | + /* shouldn't fail in a sane host, but report it just in case. */ | ||
104 | + error_report_err(local_err); | ||
105 | + } | ||
106 | qemu_close(s->fd); | ||
107 | s->fd = rs->fd; | ||
108 | |||
109 | @@ -XXX,XX +XXX,XX @@ static void raw_close(BlockDriverState *bs) | ||
110 | qemu_close(s->fd); | ||
111 | s->fd = -1; | ||
112 | } | ||
113 | - if (s->lock_fd >= 0) { | ||
114 | - qemu_close(s->lock_fd); | ||
115 | - s->lock_fd = -1; | ||
116 | - } | ||
117 | } | 62 | } |
118 | 63 | ||
119 | /** | 64 | @@ -XXX,XX +XXX,XX @@ static void bdrv_remove_filter_or_cow_child_abort(void *opaque) |
65 | BdrvRemoveFilterOrCowChild *s = opaque; | ||
66 | BlockDriverState *parent_bs = s->child->opaque; | ||
67 | |||
68 | - QLIST_INSERT_HEAD(&parent_bs->children, s->child, next); | ||
69 | if (s->is_backing) { | ||
70 | parent_bs->backing = s->child; | ||
71 | } else { | ||
72 | @@ -XXX,XX +XXX,XX @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, | ||
73 | }; | ||
74 | tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s); | ||
75 | |||
76 | - QLIST_SAFE_REMOVE(child, next); | ||
77 | if (s->is_backing) { | ||
78 | bs->backing = NULL; | ||
79 | } else { | ||
120 | -- | 80 | -- |
121 | 2.19.1 | 81 | 2.31.1 |
122 | 82 | ||
123 | 83 | diff view generated by jsdifflib |
1 | From: Liam Merwick <Liam.Merwick@oracle.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | The calls to find_mapping_for_cluster() may return NULL but it | 3 | Now that bdrv_remove_empty_child() no longer removes the child from the |
4 | isn't always checked for before dereferencing the value returned. | 4 | parent's children list but only checks that it is not in such a list, it |
5 | Additionally, add some asserts to cover cases where NULL can't | 5 | is only a wrapper around bdrv_child_free() that checks that the child is |
6 | be returned but which might not be obvious at first glance. | 6 | empty and unused. That should apply to all children that we free, so |
7 | put those checks into bdrv_child_free() and drop | ||
8 | bdrv_remove_empty_child(). | ||
7 | 9 | ||
8 | Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com> | 10 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> |
9 | Message-id: 1541453919-25973-5-git-send-email-Liam.Merwick@oracle.com | 11 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
10 | [mreitz: Dropped superfluous check of "mapping" following an assertion | 12 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
11 | that it is not NULL, and fixed some indentation] | 13 | Message-Id: <20211111120829.81329-4-hreitz@redhat.com> |
12 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
13 | --- | 15 | --- |
14 | block/vvfat.c | 46 ++++++++++++++++++++++++++++++---------------- | 16 | block.c | 26 +++++++++++++------------- |
15 | 1 file changed, 30 insertions(+), 16 deletions(-) | 17 | 1 file changed, 13 insertions(+), 13 deletions(-) |
16 | 18 | ||
17 | diff --git a/block/vvfat.c b/block/vvfat.c | 19 | diff --git a/block.c b/block.c |
18 | index XXXXXXX..XXXXXXX 100644 | 20 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/block/vvfat.c | 21 | --- a/block.c |
20 | +++ b/block/vvfat.c | 22 | +++ b/block.c |
21 | @@ -XXX,XX +XXX,XX @@ static inline void array_free(array_t* array) | 23 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child, |
22 | /* does not automatically grow */ | 24 | } |
23 | static inline void* array_get(array_t* array,unsigned int index) { | ||
24 | assert(index < array->next); | ||
25 | + assert(array->pointer); | ||
26 | return array->pointer + index * array->item_size; | ||
27 | } | 25 | } |
28 | 26 | ||
29 | -static inline int array_ensure_allocated(array_t* array, int index) | 27 | -static void bdrv_child_free(void *opaque) |
30 | +static inline void array_ensure_allocated(array_t *array, int index) | 28 | -{ |
29 | - BdrvChild *c = opaque; | ||
30 | - | ||
31 | - g_free(c->name); | ||
32 | - g_free(c); | ||
33 | -} | ||
34 | - | ||
35 | -static void bdrv_remove_empty_child(BdrvChild *child) | ||
36 | +/** | ||
37 | + * Free the given @child. | ||
38 | + * | ||
39 | + * The child must be empty (i.e. `child->bs == NULL`) and it must be | ||
40 | + * unused (i.e. not in a children list). | ||
41 | + */ | ||
42 | +static void bdrv_child_free(BdrvChild *child) | ||
31 | { | 43 | { |
32 | if((index + 1) * array->item_size > array->size) { | 44 | assert(!child->bs); |
33 | int new_size = (index + 32) * array->item_size; | 45 | assert(!child->next.le_prev); /* not in children list */ |
34 | array->pointer = g_realloc(array->pointer, new_size); | 46 | - bdrv_child_free(child); |
35 | - if (!array->pointer) | 47 | + |
36 | - return -1; | 48 | + g_free(child->name); |
37 | + assert(array->pointer); | 49 | + g_free(child); |
38 | memset(array->pointer + array->size, 0, new_size - array->size); | 50 | } |
39 | array->size = new_size; | 51 | |
40 | array->next = index + 1; | 52 | typedef struct BdrvAttachChildCommonState { |
53 | @@ -XXX,XX +XXX,XX @@ static void bdrv_attach_child_common_abort(void *opaque) | ||
41 | } | 54 | } |
42 | - | 55 | |
43 | - return 0; | 56 | bdrv_unref(bs); |
57 | - bdrv_remove_empty_child(child); | ||
58 | + bdrv_child_free(child); | ||
59 | *s->child = NULL; | ||
44 | } | 60 | } |
45 | 61 | ||
46 | static inline void* array_get_next(array_t* array) { | 62 | @@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs, |
47 | unsigned int next = array->next; | 63 | |
48 | 64 | if (ret < 0) { | |
49 | - if (array_ensure_allocated(array, next) < 0) | 65 | error_propagate(errp, local_err); |
50 | - return NULL; | 66 | - bdrv_remove_empty_child(new_child); |
51 | - | 67 | + bdrv_child_free(new_child); |
52 | + array_ensure_allocated(array, next); | 68 | return ret; |
53 | array->next = next + 1; | 69 | } |
54 | return array_get(array, next); | 70 | } |
55 | } | 71 | @@ -XXX,XX +XXX,XX @@ static void bdrv_detach_child(BdrvChild *child) |
56 | @@ -XXX,XX +XXX,XX @@ static int commit_direntries(BDRVVVFATState* s, | 72 | BlockDriverState *old_bs = child->bs; |
57 | direntry_t* direntry = array_get(&(s->directory), dir_index); | 73 | |
58 | uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry); | 74 | bdrv_replace_child_noperm(child, NULL); |
59 | mapping_t* mapping = find_mapping_for_cluster(s, first_cluster); | 75 | - bdrv_remove_empty_child(child); |
60 | - | 76 | + bdrv_child_free(child); |
61 | int factor = 0x10 * s->sectors_per_cluster; | 77 | |
62 | int old_cluster_count, new_cluster_count; | 78 | if (old_bs) { |
63 | - int current_dir_index = mapping->info.dir.first_dir_index; | 79 | /* |
64 | - int first_dir_index = current_dir_index; | ||
65 | + int current_dir_index; | ||
66 | + int first_dir_index; | ||
67 | int ret, i; | ||
68 | uint32_t c; | ||
69 | |||
70 | -DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index)); | ||
71 | - | ||
72 | assert(direntry); | ||
73 | assert(mapping); | ||
74 | assert(mapping->begin == first_cluster); | ||
75 | @@ -XXX,XX +XXX,XX @@ DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapp | ||
76 | assert(mapping->mode & MODE_DIRECTORY); | ||
77 | assert(dir_index == 0 || is_directory(direntry)); | ||
78 | |||
79 | + DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", | ||
80 | + mapping->path, parent_mapping_index)); | ||
81 | + | ||
82 | + current_dir_index = mapping->info.dir.first_dir_index; | ||
83 | + first_dir_index = current_dir_index; | ||
84 | mapping->info.dir.parent_mapping_index = parent_mapping_index; | ||
85 | |||
86 | if (first_cluster == 0) { | ||
87 | @@ -XXX,XX +XXX,XX @@ DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapp | ||
88 | direntry = array_get(&(s->directory), first_dir_index + i); | ||
89 | if (is_directory(direntry) && !is_dot(direntry)) { | ||
90 | mapping = find_mapping_for_cluster(s, first_cluster); | ||
91 | + if (mapping == NULL) { | ||
92 | + return -1; | ||
93 | + } | ||
94 | assert(mapping->mode & MODE_DIRECTORY); | ||
95 | ret = commit_direntries(s, first_dir_index + i, | ||
96 | array_index(&(s->mapping), mapping)); | ||
97 | @@ -XXX,XX +XXX,XX @@ static int commit_one_file(BDRVVVFATState* s, | ||
98 | assert(offset < size); | ||
99 | assert((offset % s->cluster_size) == 0); | ||
100 | |||
101 | + if (mapping == NULL) { | ||
102 | + return -1; | ||
103 | + } | ||
104 | + | ||
105 | for (i = s->cluster_size; i < offset; i += s->cluster_size) | ||
106 | c = modified_fat_get(s, c); | ||
107 | |||
108 | @@ -XXX,XX +XXX,XX @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s) | ||
109 | if (commit->action == ACTION_RENAME) { | ||
110 | mapping_t* mapping = find_mapping_for_cluster(s, | ||
111 | commit->param.rename.cluster); | ||
112 | - char* old_path = mapping->path; | ||
113 | + char *old_path; | ||
114 | |||
115 | + if (mapping == NULL) { | ||
116 | + return -1; | ||
117 | + } | ||
118 | + old_path = mapping->path; | ||
119 | assert(commit->path); | ||
120 | mapping->path = commit->path; | ||
121 | if (rename(old_path, mapping->path)) | ||
122 | @@ -XXX,XX +XXX,XX @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s) | ||
123 | direntry_t* d = direntry + i; | ||
124 | |||
125 | if (is_file(d) || (is_directory(d) && !is_dot(d))) { | ||
126 | + int l; | ||
127 | + char *new_path; | ||
128 | mapping_t* m = find_mapping_for_cluster(s, | ||
129 | begin_of_direntry(d)); | ||
130 | - int l = strlen(m->path); | ||
131 | - char* new_path = g_malloc(l + diff + 1); | ||
132 | + if (m == NULL) { | ||
133 | + return -1; | ||
134 | + } | ||
135 | + l = strlen(m->path); | ||
136 | + new_path = g_malloc(l + diff + 1); | ||
137 | |||
138 | assert(!strncmp(m->path, mapping->path, l2)); | ||
139 | |||
140 | -- | 80 | -- |
141 | 2.19.1 | 81 | 2.31.1 |
142 | 82 | ||
143 | 83 | diff view generated by jsdifflib |
1 | From: zhenwei pi <pizhenwei@bytedance.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Function block_latency_histogram_set may return error, but qapi ignore this. | 3 | bdrv_attach_child_common_abort() restores the parent's AioContext. To |
4 | This can be reproduced easily by qmp command: | 4 | do so, the child (which was supposed to be attached, but is now detached |
5 | virsh qemu-monitor-command INSTANCE '{"execute":"x-block-latency-histogram-set", | 5 | again by this abort handler) is added to the ignore list for the |
6 | "arguments":{"device":"drive-virtio-disk1","boundaries":[10,200,40]}}' | 6 | AioContext changing functions. |
7 | In fact this command does not work, but we still get success result. | ||
8 | 7 | ||
9 | qmp_x_block_latency_histogram_set is a batch setting API, report error ASAP. | 8 | However, since we modify a BDS's children list in the BdrvChildClass's |
9 | .attach and .detach handlers, the child is already effectively detached | ||
10 | from the parent by this point. We do not need to put it into the ignore | ||
11 | list. | ||
10 | 12 | ||
11 | Signed-off-by: zhenwei pi <pizhenwei@bytedance.com> | 13 | Use this opportunity to clean up the empty line structure: Keep setting |
14 | the ignore list, invoking the AioContext function, and freeing the | ||
15 | ignore list in blocks separated by empty lines. | ||
16 | |||
17 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> | ||
18 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> | ||
19 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
20 | Message-Id: <20211111120829.81329-5-hreitz@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 21 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
13 | --- | 22 | --- |
14 | blockdev.c | 19 ++++++++++++++++--- | 23 | block.c | 8 +++++--- |
15 | 1 file changed, 16 insertions(+), 3 deletions(-) | 24 | 1 file changed, 5 insertions(+), 3 deletions(-) |
16 | 25 | ||
17 | diff --git a/blockdev.c b/blockdev.c | 26 | diff --git a/block.c b/block.c |
18 | index XXXXXXX..XXXXXXX 100644 | 27 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/blockdev.c | 28 | --- a/block.c |
20 | +++ b/blockdev.c | 29 | +++ b/block.c |
21 | @@ -XXX,XX +XXX,XX @@ void qmp_x_block_latency_histogram_set( | 30 | @@ -XXX,XX +XXX,XX @@ static void bdrv_attach_child_common_abort(void *opaque) |
22 | { | ||
23 | BlockBackend *blk = blk_by_name(device); | ||
24 | BlockAcctStats *stats; | ||
25 | + int ret; | ||
26 | |||
27 | if (!blk) { | ||
28 | error_setg(errp, "Device '%s' not found", device); | ||
29 | @@ -XXX,XX +XXX,XX @@ void qmp_x_block_latency_histogram_set( | ||
30 | } | 31 | } |
31 | 32 | ||
32 | if (has_boundaries || has_boundaries_read) { | 33 | if (bdrv_child_get_parent_aio_context(child) != s->old_parent_ctx) { |
33 | - block_latency_histogram_set( | 34 | - GSList *ignore = g_slist_prepend(NULL, child); |
34 | + ret = block_latency_histogram_set( | 35 | + GSList *ignore; |
35 | stats, BLOCK_ACCT_READ, | 36 | |
36 | has_boundaries_read ? boundaries_read : boundaries); | 37 | + /* No need to ignore `child`, because it has been detached already */ |
37 | + if (ret) { | 38 | + ignore = NULL; |
38 | + error_setg(errp, "Device '%s' set read boundaries fail", device); | 39 | child->klass->can_set_aio_ctx(child, s->old_parent_ctx, &ignore, |
39 | + return; | 40 | &error_abort); |
40 | + } | 41 | g_slist_free(ignore); |
42 | - ignore = g_slist_prepend(NULL, child); | ||
43 | - child->klass->set_aio_ctx(child, s->old_parent_ctx, &ignore); | ||
44 | |||
45 | + ignore = NULL; | ||
46 | + child->klass->set_aio_ctx(child, s->old_parent_ctx, &ignore); | ||
47 | g_slist_free(ignore); | ||
41 | } | 48 | } |
42 | 49 | ||
43 | if (has_boundaries || has_boundaries_write) { | ||
44 | - block_latency_histogram_set( | ||
45 | + ret = block_latency_histogram_set( | ||
46 | stats, BLOCK_ACCT_WRITE, | ||
47 | has_boundaries_write ? boundaries_write : boundaries); | ||
48 | + if (ret) { | ||
49 | + error_setg(errp, "Device '%s' set write boundaries fail", device); | ||
50 | + return; | ||
51 | + } | ||
52 | } | ||
53 | |||
54 | if (has_boundaries || has_boundaries_flush) { | ||
55 | - block_latency_histogram_set( | ||
56 | + ret = block_latency_histogram_set( | ||
57 | stats, BLOCK_ACCT_FLUSH, | ||
58 | has_boundaries_flush ? boundaries_flush : boundaries); | ||
59 | + if (ret) { | ||
60 | + error_setg(errp, "Device '%s' set flush boundaries fail", device); | ||
61 | + return; | ||
62 | + } | ||
63 | } | ||
64 | } | ||
65 | |||
66 | -- | 50 | -- |
67 | 2.19.1 | 51 | 2.31.1 |
68 | 52 | ||
69 | 53 | diff view generated by jsdifflib |
1 | From: Fam Zheng <famz@redhat.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Use error_report for situations that affect user operation (i.e. we're | 3 | bdrv_replace_child_noperm() modifies BdrvChild.bs, and can potentially |
4 | actually returning error), and warn_report/warn_report_err when some | 4 | set it to NULL. That is dangerous, because BDS parents generally assume |
5 | less critical error happened but the user operation can still carry on. | 5 | that their children's .bs pointer is never NULL. We therefore want to |
6 | let bdrv_replace_child_noperm() set the corresponding BdrvChild pointer | ||
7 | to NULL, too. | ||
6 | 8 | ||
7 | For raw_normalize_devicepath, add Error parameter to propagate to | 9 | This patch lays the foundation for it by passing a BdrvChild ** pointer |
8 | its callers. | 10 | to bdrv_replace_child_noperm() so that it can later use it to NULL the |
11 | BdrvChild pointer immediately after setting BdrvChild.bs to NULL. | ||
9 | 12 | ||
10 | Suggested-by: Markus Armbruster <armbru@redhat.com> | 13 | (We will still need to undertake some intermediate steps, though.) |
11 | Signed-off-by: Fam Zheng <famz@redhat.com> | 14 | |
15 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> | ||
16 | Message-Id: <20211111120829.81329-6-hreitz@redhat.com> | ||
17 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 18 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
13 | --- | 19 | --- |
14 | block/file-posix.c | 39 ++++++++++++++++----------------------- | 20 | block.c | 23 ++++++++++++----------- |
15 | 1 file changed, 16 insertions(+), 23 deletions(-) | 21 | 1 file changed, 12 insertions(+), 11 deletions(-) |
16 | 22 | ||
17 | diff --git a/block/file-posix.c b/block/file-posix.c | 23 | diff --git a/block.c b/block.c |
18 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
19 | --- a/block/file-posix.c | 25 | --- a/block.c |
20 | +++ b/block/file-posix.c | 26 | +++ b/block.c |
21 | @@ -XXX,XX +XXX,XX @@ static int cdrom_reopen(BlockDriverState *bs); | 27 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename, |
22 | #endif | 28 | static bool bdrv_recurse_has_child(BlockDriverState *bs, |
23 | 29 | BlockDriverState *child); | |
24 | #if defined(__NetBSD__) | 30 | |
25 | -static int raw_normalize_devicepath(const char **filename) | 31 | -static void bdrv_replace_child_noperm(BdrvChild *child, |
26 | +static int raw_normalize_devicepath(const char **filename, Error **errp) | 32 | +static void bdrv_replace_child_noperm(BdrvChild **child, |
33 | BlockDriverState *new_bs); | ||
34 | static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, | ||
35 | BdrvChild *child, | ||
36 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_abort(void *opaque) | ||
37 | BlockDriverState *new_bs = s->child->bs; | ||
38 | |||
39 | /* old_bs reference is transparently moved from @s to @s->child */ | ||
40 | - bdrv_replace_child_noperm(s->child, s->old_bs); | ||
41 | + bdrv_replace_child_noperm(&s->child, s->old_bs); | ||
42 | bdrv_unref(new_bs); | ||
43 | } | ||
44 | |||
45 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, | ||
46 | if (new_bs) { | ||
47 | bdrv_ref(new_bs); | ||
48 | } | ||
49 | - bdrv_replace_child_noperm(child, new_bs); | ||
50 | + bdrv_replace_child_noperm(&child, new_bs); | ||
51 | /* old_bs reference is transparently moved from @child to @s */ | ||
52 | } | ||
53 | |||
54 | @@ -XXX,XX +XXX,XX @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm) | ||
55 | return permissions[qapi_perm]; | ||
56 | } | ||
57 | |||
58 | -static void bdrv_replace_child_noperm(BdrvChild *child, | ||
59 | +static void bdrv_replace_child_noperm(BdrvChild **childp, | ||
60 | BlockDriverState *new_bs) | ||
27 | { | 61 | { |
28 | static char namebuf[PATH_MAX]; | 62 | + BdrvChild *child = *childp; |
29 | const char *dp, *fname; | 63 | BlockDriverState *old_bs = child->bs; |
30 | @@ -XXX,XX +XXX,XX @@ static int raw_normalize_devicepath(const char **filename) | 64 | int new_bs_quiesce_counter; |
31 | fname = *filename; | 65 | int drain_saldo; |
32 | dp = strrchr(fname, '/'); | 66 | @@ -XXX,XX +XXX,XX @@ static void bdrv_attach_child_common_abort(void *opaque) |
33 | if (lstat(fname, &sb) < 0) { | 67 | BdrvChild *child = *s->child; |
34 | - fprintf(stderr, "%s: stat failed: %s\n", | 68 | BlockDriverState *bs = child->bs; |
35 | - fname, strerror(errno)); | 69 | |
36 | + error_setg_errno(errp, errno, "%s: stat failed", fname); | 70 | - bdrv_replace_child_noperm(child, NULL); |
37 | return -errno; | 71 | + bdrv_replace_child_noperm(s->child, NULL); |
72 | |||
73 | if (bdrv_get_aio_context(bs) != s->old_child_ctx) { | ||
74 | bdrv_try_set_aio_context(bs, s->old_child_ctx, &error_abort); | ||
75 | @@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs, | ||
38 | } | 76 | } |
39 | 77 | ||
40 | @@ -XXX,XX +XXX,XX @@ static int raw_normalize_devicepath(const char **filename) | 78 | bdrv_ref(child_bs); |
41 | snprintf(namebuf, PATH_MAX, "%.*s/r%s", | 79 | - bdrv_replace_child_noperm(new_child, child_bs); |
42 | (int)(dp - fname), fname, dp + 1); | 80 | + bdrv_replace_child_noperm(&new_child, child_bs); |
43 | } | 81 | |
44 | - fprintf(stderr, "%s is a block device", fname); | 82 | *child = new_child; |
45 | *filename = namebuf; | 83 | |
46 | - fprintf(stderr, ", using %s\n", *filename); | 84 | @@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs, |
47 | + warn_report("%s is a block device, using %s", fname, *filename); | ||
48 | |||
49 | return 0; | 85 | return 0; |
50 | } | 86 | } |
51 | #else | 87 | |
52 | -static int raw_normalize_devicepath(const char **filename) | 88 | -static void bdrv_detach_child(BdrvChild *child) |
53 | +static int raw_normalize_devicepath(const char **filename, Error **errp) | 89 | +static void bdrv_detach_child(BdrvChild **childp) |
54 | { | 90 | { |
55 | return 0; | 91 | - BlockDriverState *old_bs = child->bs; |
92 | + BlockDriverState *old_bs = (*childp)->bs; | ||
93 | |||
94 | - bdrv_replace_child_noperm(child, NULL); | ||
95 | - bdrv_child_free(child); | ||
96 | + bdrv_replace_child_noperm(childp, NULL); | ||
97 | + bdrv_child_free(*childp); | ||
98 | |||
99 | if (old_bs) { | ||
100 | /* | ||
101 | @@ -XXX,XX +XXX,XX @@ void bdrv_root_unref_child(BdrvChild *child) | ||
102 | BlockDriverState *child_bs; | ||
103 | |||
104 | child_bs = child->bs; | ||
105 | - bdrv_detach_child(child); | ||
106 | + bdrv_detach_child(&child); | ||
107 | bdrv_unref(child_bs); | ||
56 | } | 108 | } |
57 | @@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options, | ||
58 | |||
59 | filename = qemu_opt_get(opts, "filename"); | ||
60 | |||
61 | - ret = raw_normalize_devicepath(&filename); | ||
62 | + ret = raw_normalize_devicepath(&filename, errp); | ||
63 | if (ret != 0) { | ||
64 | - error_setg_errno(errp, -ret, "Could not normalize device path"); | ||
65 | goto fail; | ||
66 | } | ||
67 | |||
68 | @@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options, | ||
69 | case ON_OFF_AUTO_ON: | ||
70 | s->use_lock = true; | ||
71 | if (!qemu_has_ofd_lock()) { | ||
72 | - fprintf(stderr, | ||
73 | - "File lock requested but OFD locking syscall is " | ||
74 | - "unavailable, falling back to POSIX file locks.\n" | ||
75 | - "Due to the implementation, locks can be lost " | ||
76 | - "unexpectedly.\n"); | ||
77 | + warn_report("File lock requested but OFD locking syscall is " | ||
78 | + "unavailable, falling back to POSIX file locks"); | ||
79 | + error_printf("Due to the implementation, locks can be lost " | ||
80 | + "unexpectedly.\n"); | ||
81 | } | ||
82 | break; | ||
83 | case ON_OFF_AUTO_OFF: | ||
84 | @@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs, | ||
85 | /* Theoretically the above call only unlocks bytes and it cannot | ||
86 | * fail. Something weird happened, report it. | ||
87 | */ | ||
88 | - error_report_err(local_err); | ||
89 | + warn_report_err(local_err); | ||
90 | } | ||
91 | break; | ||
92 | case RAW_PL_COMMIT: | ||
93 | @@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs, | ||
94 | /* Theoretically the above call only unlocks bytes and it cannot | ||
95 | * fail. Something weird happened, report it. | ||
96 | */ | ||
97 | - error_report_err(local_err); | ||
98 | + warn_report_err(local_err); | ||
99 | } | ||
100 | break; | ||
101 | } | ||
102 | @@ -XXX,XX +XXX,XX @@ static int raw_reopen_prepare(BDRVReopenState *state, | ||
103 | /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */ | ||
104 | if (rs->fd == -1) { | ||
105 | const char *normalized_filename = state->bs->filename; | ||
106 | - ret = raw_normalize_devicepath(&normalized_filename); | ||
107 | - if (ret < 0) { | ||
108 | - error_setg_errno(errp, -ret, "Could not normalize device path"); | ||
109 | - } else { | ||
110 | + ret = raw_normalize_devicepath(&normalized_filename, errp); | ||
111 | + if (ret >= 0) { | ||
112 | assert(!(rs->open_flags & O_CREAT)); | ||
113 | rs->fd = qemu_open(normalized_filename, rs->open_flags); | ||
114 | if (rs->fd == -1) { | ||
115 | @@ -XXX,XX +XXX,XX @@ static int aio_worker(void *arg) | ||
116 | ret = handle_aiocb_truncate(aiocb); | ||
117 | break; | ||
118 | default: | ||
119 | - fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); | ||
120 | + error_report("invalid aio request (0x%x)", aiocb->aio_type); | ||
121 | ret = -EINVAL; | ||
122 | break; | ||
123 | } | ||
124 | @@ -XXX,XX +XXX,XX @@ out_unlock: | ||
125 | * not mean the whole creation operation has failed. So | ||
126 | * report it the user for their convenience, but do not report | ||
127 | * it to the caller. */ | ||
128 | - error_report_err(local_err); | ||
129 | + warn_report_err(local_err); | ||
130 | } | ||
131 | |||
132 | out_close: | ||
133 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts | ||
134 | |||
135 | (void)has_prefix; | ||
136 | |||
137 | - ret = raw_normalize_devicepath(&filename); | ||
138 | + ret = raw_normalize_devicepath(&filename, errp); | ||
139 | if (ret < 0) { | ||
140 | - error_setg_errno(errp, -ret, "Could not normalize device path"); | ||
141 | return ret; | ||
142 | } | ||
143 | 109 | ||
144 | -- | 110 | -- |
145 | 2.19.1 | 111 | 2.31.1 |
146 | 112 | ||
147 | 113 | diff view generated by jsdifflib |
1 | From: Liam Merwick <Liam.Merwick@oracle.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | The dev_id returned by the call to blk_get_attached_dev_id() in | 3 | As of a future patch, bdrv_replace_child_tran() will take a BdrvChild ** |
4 | blk_root_get_parent_desc() can be NULL (an internal call to | 4 | pointer. Prepare for that by getting such a pointer and using it where |
5 | object_get_canonical_path may have returned NULL). | 5 | applicable, and (dereferenced) as a parameter for |
6 | bdrv_replace_child_tran(). | ||
6 | 7 | ||
7 | Instead of just checking this case before before dereferencing, | 8 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> |
8 | adjust blk_get_attached_dev_id() to return the empty string if no | 9 | Message-Id: <20211111120829.81329-7-hreitz@redhat.com> |
9 | object path can be found (similar to the case when blk->dev is NULL | 10 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
10 | and an empty string is returned). | 11 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
12 | --- | ||
13 | block.c | 21 ++++++++++++--------- | ||
14 | 1 file changed, 12 insertions(+), 9 deletions(-) | ||
11 | 15 | ||
12 | Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com> | 16 | diff --git a/block.c b/block.c |
13 | Message-id: 1541453919-25973-3-git-send-email-Liam.Merwick@oracle.com | ||
14 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
15 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
16 | --- | ||
17 | block/block-backend.c | 3 ++- | ||
18 | 1 file changed, 2 insertions(+), 1 deletion(-) | ||
19 | |||
20 | diff --git a/block/block-backend.c b/block/block-backend.c | ||
21 | index XXXXXXX..XXXXXXX 100644 | 17 | index XXXXXXX..XXXXXXX 100644 |
22 | --- a/block/block-backend.c | 18 | --- a/block.c |
23 | +++ b/block/block-backend.c | 19 | +++ b/block.c |
24 | @@ -XXX,XX +XXX,XX @@ char *blk_get_attached_dev_id(BlockBackend *blk) | 20 | @@ -XXX,XX +XXX,XX @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, |
25 | } else if (dev->id) { | 21 | BdrvChild *child, |
26 | return g_strdup(dev->id); | 22 | Transaction *tran) |
23 | { | ||
24 | + BdrvChild **childp; | ||
25 | BdrvRemoveFilterOrCowChild *s; | ||
26 | |||
27 | - assert(child == bs->backing || child == bs->file); | ||
28 | - | ||
29 | if (!child) { | ||
30 | return; | ||
27 | } | 31 | } |
28 | - return object_get_canonical_path(OBJECT(dev)); | 32 | |
33 | + if (child == bs->backing) { | ||
34 | + childp = &bs->backing; | ||
35 | + } else if (child == bs->file) { | ||
36 | + childp = &bs->file; | ||
37 | + } else { | ||
38 | + g_assert_not_reached(); | ||
39 | + } | ||
29 | + | 40 | + |
30 | + return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); | 41 | if (child->bs) { |
42 | - bdrv_replace_child_tran(child, NULL, tran); | ||
43 | + bdrv_replace_child_tran(*childp, NULL, tran); | ||
44 | } | ||
45 | |||
46 | s = g_new(BdrvRemoveFilterOrCowChild, 1); | ||
47 | *s = (BdrvRemoveFilterOrCowChild) { | ||
48 | .child = child, | ||
49 | - .is_backing = (child == bs->backing), | ||
50 | + .is_backing = (childp == &bs->backing), | ||
51 | }; | ||
52 | tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s); | ||
53 | |||
54 | - if (s->is_backing) { | ||
55 | - bs->backing = NULL; | ||
56 | - } else { | ||
57 | - bs->file = NULL; | ||
58 | - } | ||
59 | + *childp = NULL; | ||
31 | } | 60 | } |
32 | 61 | ||
33 | /* | 62 | /* |
34 | -- | 63 | -- |
35 | 2.19.1 | 64 | 2.31.1 |
36 | 65 | ||
37 | 66 | diff view generated by jsdifflib |
1 | From: Liam Merwick <Liam.Merwick@oracle.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | In the assert checking the array dereference of JobVerbTable[verb] | 3 | Invoke the transaction drivers' .clean() methods only after all |
4 | in job_apply_verb() the check of the index, verb, allows an overrun | 4 | .commit() or .abort() handlers are done. |
5 | because an index equal to the array size is permitted. | ||
6 | 5 | ||
7 | Similarly, in the assert check of JobSTT[s0][s1] with index s1 | 6 | This makes it easier to have nested transactions where the top-level |
8 | in job_state_transition(), an off-by-one overrun is not flagged | 7 | transactions pass objects to lower transactions that the latter can |
9 | either. | 8 | still use throughout their commit/abort phases, while the top-level |
9 | transaction keeps a reference that is released in its .clean() method. | ||
10 | 10 | ||
11 | This is not a run-time issue as there are no callers actually | 11 | (Before this commit, that is also possible, but the top-level |
12 | passing in the max value. | 12 | transaction would need to take care to invoke tran_add() before the |
13 | lower-level transaction does. This commit makes the ordering | ||
14 | irrelevant, which is just a bit nicer.) | ||
13 | 15 | ||
14 | Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com> | 16 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> |
15 | Reviewed-by: Darren Kenny <Darren.Kenny@oracle.com> | 17 | Message-Id: <20211111120829.81329-8-hreitz@redhat.com> |
16 | Reviewed-by: Mark Kanda <Mark.Kanda@oracle.com> | 18 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> |
17 | Reviewed-by: Eric Blake <eblake@redhat.com> | 19 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
18 | Reviewed-by: John Snow <jsnow@redhat.com> | ||
19 | Message-id: 1541453919-25973-2-git-send-email-Liam.Merwick@oracle.com | ||
20 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
21 | --- | 20 | --- |
22 | job.c | 4 ++-- | 21 | include/qemu/transactions.h | 3 +++ |
23 | 1 file changed, 2 insertions(+), 2 deletions(-) | 22 | util/transactions.c | 8 ++++++-- |
23 | 2 files changed, 9 insertions(+), 2 deletions(-) | ||
24 | 24 | ||
25 | diff --git a/job.c b/job.c | 25 | diff --git a/include/qemu/transactions.h b/include/qemu/transactions.h |
26 | index XXXXXXX..XXXXXXX 100644 | 26 | index XXXXXXX..XXXXXXX 100644 |
27 | --- a/job.c | 27 | --- a/include/qemu/transactions.h |
28 | +++ b/job.c | 28 | +++ b/include/qemu/transactions.h |
29 | @@ -XXX,XX +XXX,XX @@ bool job_is_internal(Job *job) | 29 | @@ -XXX,XX +XXX,XX @@ |
30 | static void job_state_transition(Job *job, JobStatus s1) | 30 | * tran_create(), call your "prepare" functions on it, and finally call |
31 | * tran_abort() or tran_commit() to finalize the transaction by corresponding | ||
32 | * finalization actions in reverse order. | ||
33 | + * | ||
34 | + * The clean() functions registered by the drivers in a transaction are called | ||
35 | + * last, after all abort() or commit() functions have been called. | ||
36 | */ | ||
37 | |||
38 | #ifndef QEMU_TRANSACTIONS_H | ||
39 | diff --git a/util/transactions.c b/util/transactions.c | ||
40 | index XXXXXXX..XXXXXXX 100644 | ||
41 | --- a/util/transactions.c | ||
42 | +++ b/util/transactions.c | ||
43 | @@ -XXX,XX +XXX,XX @@ void tran_abort(Transaction *tran) | ||
31 | { | 44 | { |
32 | JobStatus s0 = job->status; | 45 | TransactionAction *act, *next; |
33 | - assert(s1 >= 0 && s1 <= JOB_STATUS__MAX); | 46 | |
34 | + assert(s1 >= 0 && s1 < JOB_STATUS__MAX); | 47 | - QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) { |
35 | trace_job_state_transition(job, job->ret, | 48 | + QSLIST_FOREACH(act, &tran->actions, entry) { |
36 | JobSTT[s0][s1] ? "allowed" : "disallowed", | 49 | if (act->drv->abort) { |
37 | JobStatus_str(s0), JobStatus_str(s1)); | 50 | act->drv->abort(act->opaque); |
38 | @@ -XXX,XX +XXX,XX @@ static void job_state_transition(Job *job, JobStatus s1) | 51 | } |
39 | int job_apply_verb(Job *job, JobVerb verb, Error **errp) | 52 | + } |
53 | |||
54 | + QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) { | ||
55 | if (act->drv->clean) { | ||
56 | act->drv->clean(act->opaque); | ||
57 | } | ||
58 | @@ -XXX,XX +XXX,XX @@ void tran_commit(Transaction *tran) | ||
40 | { | 59 | { |
41 | JobStatus s0 = job->status; | 60 | TransactionAction *act, *next; |
42 | - assert(verb >= 0 && verb <= JOB_VERB__MAX); | 61 | |
43 | + assert(verb >= 0 && verb < JOB_VERB__MAX); | 62 | - QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) { |
44 | trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), | 63 | + QSLIST_FOREACH(act, &tran->actions, entry) { |
45 | JobVerbTable[verb][s0] ? "allowed" : "prohibited"); | 64 | if (act->drv->commit) { |
46 | if (JobVerbTable[verb][s0]) { | 65 | act->drv->commit(act->opaque); |
66 | } | ||
67 | + } | ||
68 | |||
69 | + QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) { | ||
70 | if (act->drv->clean) { | ||
71 | act->drv->clean(act->opaque); | ||
72 | } | ||
47 | -- | 73 | -- |
48 | 2.19.1 | 74 | 2.31.1 |
49 | 75 | ||
50 | 76 | diff view generated by jsdifflib |
1 | From: Fam Zheng <famz@redhat.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Signed-off-by: Fam Zheng <famz@redhat.com> | 3 | As of a future commit, bdrv_replace_child_noperm() will clear the |
4 | indirect BdrvChild pointer passed to it if the new child BDS is NULL. | ||
5 | bdrv_replace_child_tran() will want to let it do that, but revert this | ||
6 | change in its abort handler. For that, we need to have it receive a | ||
7 | BdrvChild ** pointer, too, and keep it stored in the | ||
8 | BdrvReplaceChildState object that we attach to the transaction. | ||
9 | |||
10 | Note that we do not need to store it in the BdrvReplaceChildState when | ||
11 | new_bs is not NULL, because then there is nothing to revert. This is | ||
12 | important so that bdrv_replace_node_noperm() can pass a pointer to a | ||
13 | loop-local variable to bdrv_replace_child_tran() without worrying that | ||
14 | this pointer will outlive one loop iteration. | ||
15 | |||
16 | (Of course, for that to work, bdrv_replace_node_noperm() and in turn | ||
17 | bdrv_replace_node() and its relatives may not be called with a NULL @to | ||
18 | node. Luckily, they already are not, but now we should assert this.) | ||
19 | |||
20 | bdrv_remove_file_or_backing_child() on the other hand needs to ensure | ||
21 | that the indirect pointer it passes will stay valid for the duration of | ||
22 | the transaction. Ensure this by keeping a strong reference to the BDS | ||
23 | whose &bs->backing or &bs->file it passes to bdrv_replace_child_tran(), | ||
24 | and giving up that reference only in the transaction .clean() handler. | ||
25 | |||
26 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> | ||
27 | Message-Id: <20211111120829.81329-9-hreitz@redhat.com> | ||
28 | Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 29 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
5 | --- | 30 | --- |
6 | tests/test-image-locking.c | 157 +++++++++++++++++++++++++++++++++++++ | 31 | block.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++------- |
7 | tests/Makefile.include | 2 + | 32 | 1 file changed, 73 insertions(+), 10 deletions(-) |
8 | 2 files changed, 159 insertions(+) | 33 | |
9 | create mode 100644 tests/test-image-locking.c | 34 | diff --git a/block.c b/block.c |
10 | 35 | index XXXXXXX..XXXXXXX 100644 | |
11 | diff --git a/tests/test-image-locking.c b/tests/test-image-locking.c | 36 | --- a/block.c |
12 | new file mode 100644 | 37 | +++ b/block.c |
13 | index XXXXXXX..XXXXXXX | 38 | @@ -XXX,XX +XXX,XX @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, |
14 | --- /dev/null | 39 | |
15 | +++ b/tests/test-image-locking.c | 40 | typedef struct BdrvReplaceChildState { |
16 | @@ -XXX,XX +XXX,XX @@ | 41 | BdrvChild *child; |
17 | +/* | 42 | + BdrvChild **childp; |
18 | + * Image locking tests | 43 | BlockDriverState *old_bs; |
44 | } BdrvReplaceChildState; | ||
45 | |||
46 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_abort(void *opaque) | ||
47 | BdrvReplaceChildState *s = opaque; | ||
48 | BlockDriverState *new_bs = s->child->bs; | ||
49 | |||
50 | - /* old_bs reference is transparently moved from @s to @s->child */ | ||
51 | + /* | ||
52 | + * old_bs reference is transparently moved from @s to s->child. | ||
53 | + * | ||
54 | + * Pass &s->child here instead of s->childp, because: | ||
55 | + * (1) s->old_bs must be non-NULL, so bdrv_replace_child_noperm() will not | ||
56 | + * modify the BdrvChild * pointer we indirectly pass to it, i.e. it | ||
57 | + * will not modify s->child. From that perspective, it does not matter | ||
58 | + * whether we pass s->childp or &s->child. | ||
59 | + * (TODO: Right now, bdrv_replace_child_noperm() never modifies that | ||
60 | + * pointer anyway (though it will in the future), so at this point it | ||
61 | + * absolutely does not matter whether we pass s->childp or &s->child.) | ||
62 | + * (2) If new_bs is not NULL, s->childp will be NULL. We then cannot use | ||
63 | + * it here. | ||
64 | + * (3) If new_bs is NULL, *s->childp will have been NULLed by | ||
65 | + * bdrv_replace_child_tran()'s bdrv_replace_child_noperm() call, and we | ||
66 | + * must not pass a NULL *s->childp here. | ||
67 | + * (TODO: In its current state, bdrv_replace_child_noperm() will not | ||
68 | + * have NULLed *s->childp, so this does not apply yet. It will in the | ||
69 | + * future.) | ||
70 | + * | ||
71 | + * So whether new_bs was NULL or not, we cannot pass s->childp here; and in | ||
72 | + * any case, there is no reason to pass it anyway. | ||
73 | + */ | ||
74 | bdrv_replace_child_noperm(&s->child, s->old_bs); | ||
75 | bdrv_unref(new_bs); | ||
76 | } | ||
77 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = { | ||
78 | * Note: real unref of old_bs is done only on commit. | ||
79 | * | ||
80 | * The function doesn't update permissions, caller is responsible for this. | ||
19 | + * | 81 | + * |
20 | + * Copyright (c) 2018 Red Hat Inc. | 82 | + * Note that if new_bs == NULL, @childp is stored in a state object attached |
83 | + * to @tran, so that the old child can be reinstated in the abort handler. | ||
84 | + * Therefore, if @new_bs can be NULL, @childp must stay valid until the | ||
85 | + * transaction is committed or aborted. | ||
21 | + * | 86 | + * |
22 | + * Author: Fam Zheng <famz@redhat.com> | 87 | + * (TODO: The reinstating does not happen yet, but it will once |
88 | + * bdrv_replace_child_noperm() NULLs *childp when new_bs is NULL.) | ||
89 | */ | ||
90 | -static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, | ||
91 | +static void bdrv_replace_child_tran(BdrvChild **childp, | ||
92 | + BlockDriverState *new_bs, | ||
93 | Transaction *tran) | ||
94 | { | ||
95 | BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1); | ||
96 | *s = (BdrvReplaceChildState) { | ||
97 | - .child = child, | ||
98 | - .old_bs = child->bs, | ||
99 | + .child = *childp, | ||
100 | + .childp = new_bs == NULL ? childp : NULL, | ||
101 | + .old_bs = (*childp)->bs, | ||
102 | }; | ||
103 | tran_add(tran, &bdrv_replace_child_drv, s); | ||
104 | |||
105 | if (new_bs) { | ||
106 | bdrv_ref(new_bs); | ||
107 | } | ||
108 | - bdrv_replace_child_noperm(&child, new_bs); | ||
109 | - /* old_bs reference is transparently moved from @child to @s */ | ||
110 | + bdrv_replace_child_noperm(childp, new_bs); | ||
111 | + /* old_bs reference is transparently moved from *childp to @s */ | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | @@ -XXX,XX +XXX,XX @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) | ||
116 | |||
117 | typedef struct BdrvRemoveFilterOrCowChild { | ||
118 | BdrvChild *child; | ||
119 | + BlockDriverState *bs; | ||
120 | bool is_backing; | ||
121 | } BdrvRemoveFilterOrCowChild; | ||
122 | |||
123 | @@ -XXX,XX +XXX,XX @@ static void bdrv_remove_filter_or_cow_child_commit(void *opaque) | ||
124 | bdrv_child_free(s->child); | ||
125 | } | ||
126 | |||
127 | +static void bdrv_remove_filter_or_cow_child_clean(void *opaque) | ||
128 | +{ | ||
129 | + BdrvRemoveFilterOrCowChild *s = opaque; | ||
130 | + | ||
131 | + /* Drop the bs reference after the transaction is done */ | ||
132 | + bdrv_unref(s->bs); | ||
133 | + g_free(s); | ||
134 | +} | ||
135 | + | ||
136 | static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = { | ||
137 | .abort = bdrv_remove_filter_or_cow_child_abort, | ||
138 | .commit = bdrv_remove_filter_or_cow_child_commit, | ||
139 | - .clean = g_free, | ||
140 | + .clean = bdrv_remove_filter_or_cow_child_clean, | ||
141 | }; | ||
142 | |||
143 | /* | ||
144 | @@ -XXX,XX +XXX,XX @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, | ||
145 | return; | ||
146 | } | ||
147 | |||
148 | + /* | ||
149 | + * Keep a reference to @bs so @childp will stay valid throughout the | ||
150 | + * transaction (required by bdrv_replace_child_tran()) | ||
151 | + */ | ||
152 | + bdrv_ref(bs); | ||
153 | if (child == bs->backing) { | ||
154 | childp = &bs->backing; | ||
155 | } else if (child == bs->file) { | ||
156 | @@ -XXX,XX +XXX,XX @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, | ||
157 | } | ||
158 | |||
159 | if (child->bs) { | ||
160 | - bdrv_replace_child_tran(*childp, NULL, tran); | ||
161 | + bdrv_replace_child_tran(childp, NULL, tran); | ||
162 | } | ||
163 | |||
164 | s = g_new(BdrvRemoveFilterOrCowChild, 1); | ||
165 | *s = (BdrvRemoveFilterOrCowChild) { | ||
166 | .child = child, | ||
167 | + .bs = bs, | ||
168 | .is_backing = (childp == &bs->backing), | ||
169 | }; | ||
170 | tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s); | ||
171 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from, | ||
172 | { | ||
173 | BdrvChild *c, *next; | ||
174 | |||
175 | + assert(to != NULL); | ||
176 | + | ||
177 | QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { | ||
178 | assert(c->bs == from); | ||
179 | if (!should_update_child(c, to)) { | ||
180 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from, | ||
181 | c->name, from->node_name); | ||
182 | return -EPERM; | ||
183 | } | ||
184 | - bdrv_replace_child_tran(c, to, tran); | ||
185 | + | ||
186 | + /* | ||
187 | + * Passing a pointer to the local variable @c is fine here, because | ||
188 | + * @to is not NULL, and so &c will not be attached to the transaction. | ||
189 | + */ | ||
190 | + bdrv_replace_child_tran(&c, to, tran); | ||
191 | } | ||
192 | |||
193 | return 0; | ||
194 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from, | ||
195 | * | ||
196 | * With @detach_subchain=true @to must be in a backing chain of @from. In this | ||
197 | * case backing link of the cow-parent of @to is removed. | ||
23 | + * | 198 | + * |
24 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | 199 | + * @to must not be NULL. |
25 | + * of this software and associated documentation files (the "Software"), to deal | 200 | */ |
26 | + * in the Software without restriction, including without limitation the rights | 201 | static int bdrv_replace_node_common(BlockDriverState *from, |
27 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 202 | BlockDriverState *to, |
28 | + * copies of the Software, and to permit persons to whom the Software is | 203 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_common(BlockDriverState *from, |
29 | + * furnished to do so, subject to the following conditions: | 204 | BlockDriverState *to_cow_parent = NULL; |
30 | + * | 205 | int ret; |
31 | + * The above copyright notice and this permission notice shall be included in | 206 | |
32 | + * all copies or substantial portions of the Software. | 207 | + assert(to != NULL); |
33 | + * | 208 | + |
34 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 209 | if (detach_subchain) { |
35 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 210 | assert(bdrv_chain_contains(from, to)); |
36 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 211 | assert(from != to); |
37 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 212 | @@ -XXX,XX +XXX,XX @@ out: |
38 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 213 | return ret; |
39 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 214 | } |
40 | + * THE SOFTWARE. | 215 | |
216 | +/** | ||
217 | + * Replace node @from by @to (where neither may be NULL). | ||
41 | + */ | 218 | + */ |
42 | + | 219 | int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, |
43 | +#include "qemu/osdep.h" | 220 | Error **errp) |
44 | +#include "block/block.h" | 221 | { |
45 | +#include "sysemu/block-backend.h" | 222 | @@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, |
46 | +#include "qapi/error.h" | 223 | bdrv_drained_begin(old_bs); |
47 | +#include "qapi/qmp/qdict.h" | 224 | bdrv_drained_begin(new_bs); |
48 | + | 225 | |
49 | +static BlockBackend *open_image(const char *path, | 226 | - bdrv_replace_child_tran(child, new_bs, tran); |
50 | + uint64_t perm, uint64_t shared_perm, | 227 | + bdrv_replace_child_tran(&child, new_bs, tran); |
51 | + Error **errp) | 228 | |
52 | +{ | 229 | found = g_hash_table_new(NULL, NULL); |
53 | + Error *local_err = NULL; | 230 | refresh_list = bdrv_topological_dfs(refresh_list, found, old_bs); |
54 | + BlockBackend *blk; | ||
55 | + QDict *options = qdict_new(); | ||
56 | + | ||
57 | + qdict_put_str(options, "driver", "raw"); | ||
58 | + blk = blk_new_open(path, NULL, options, BDRV_O_RDWR, &local_err); | ||
59 | + if (blk) { | ||
60 | + g_assert_null(local_err); | ||
61 | + if (blk_set_perm(blk, perm, shared_perm, errp)) { | ||
62 | + blk_unref(blk); | ||
63 | + blk = NULL; | ||
64 | + } | ||
65 | + } else { | ||
66 | + error_propagate(errp, local_err); | ||
67 | + } | ||
68 | + return blk; | ||
69 | +} | ||
70 | + | ||
71 | +static void check_locked_bytes(int fd, uint64_t perm_locks, | ||
72 | + uint64_t shared_perm_locks) | ||
73 | +{ | ||
74 | + int i; | ||
75 | + | ||
76 | + if (!perm_locks && !shared_perm_locks) { | ||
77 | + g_assert(!qemu_lock_fd_test(fd, 0, 0, true)); | ||
78 | + return; | ||
79 | + } | ||
80 | + for (i = 0; (1ULL << i) <= BLK_PERM_ALL; i++) { | ||
81 | + uint64_t bit = (1ULL << i); | ||
82 | + bool perm_expected = !!(bit & perm_locks); | ||
83 | + bool shared_perm_expected = !!(bit & shared_perm_locks); | ||
84 | + g_assert_cmpint(perm_expected, ==, | ||
85 | + !!qemu_lock_fd_test(fd, 100 + i, 1, true)); | ||
86 | + g_assert_cmpint(shared_perm_expected, ==, | ||
87 | + !!qemu_lock_fd_test(fd, 200 + i, 1, true)); | ||
88 | + } | ||
89 | +} | ||
90 | + | ||
91 | +static void test_image_locking_basic(void) | ||
92 | +{ | ||
93 | + BlockBackend *blk1, *blk2, *blk3; | ||
94 | + char img_path[] = "/tmp/qtest.XXXXXX"; | ||
95 | + uint64_t perm, shared_perm; | ||
96 | + | ||
97 | + int fd = mkstemp(img_path); | ||
98 | + assert(fd >= 0); | ||
99 | + | ||
100 | + perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; | ||
101 | + shared_perm = BLK_PERM_ALL; | ||
102 | + blk1 = open_image(img_path, perm, shared_perm, &error_abort); | ||
103 | + g_assert(blk1); | ||
104 | + | ||
105 | + check_locked_bytes(fd, perm, ~shared_perm); | ||
106 | + | ||
107 | + /* compatible perm between blk1 and blk2 */ | ||
108 | + blk2 = open_image(img_path, perm | BLK_PERM_RESIZE, shared_perm, NULL); | ||
109 | + g_assert(blk2); | ||
110 | + check_locked_bytes(fd, perm | BLK_PERM_RESIZE, ~shared_perm); | ||
111 | + | ||
112 | + /* incompatible perm with already open blk1 and blk2 */ | ||
113 | + blk3 = open_image(img_path, perm, BLK_PERM_WRITE_UNCHANGED, NULL); | ||
114 | + g_assert_null(blk3); | ||
115 | + | ||
116 | + blk_unref(blk2); | ||
117 | + | ||
118 | + /* Check that extra bytes in blk2 are correctly unlocked */ | ||
119 | + check_locked_bytes(fd, perm, ~shared_perm); | ||
120 | + | ||
121 | + blk_unref(blk1); | ||
122 | + | ||
123 | + /* Image is unused, no lock there */ | ||
124 | + check_locked_bytes(fd, 0, 0); | ||
125 | + blk3 = open_image(img_path, perm, BLK_PERM_WRITE_UNCHANGED, &error_abort); | ||
126 | + g_assert(blk3); | ||
127 | + blk_unref(blk3); | ||
128 | + close(fd); | ||
129 | + unlink(img_path); | ||
130 | +} | ||
131 | + | ||
132 | +static void test_set_perm_abort(void) | ||
133 | +{ | ||
134 | + BlockBackend *blk1, *blk2; | ||
135 | + char img_path[] = "/tmp/qtest.XXXXXX"; | ||
136 | + uint64_t perm, shared_perm; | ||
137 | + int r; | ||
138 | + int fd = mkstemp(img_path); | ||
139 | + assert(fd >= 0); | ||
140 | + | ||
141 | + perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; | ||
142 | + shared_perm = BLK_PERM_ALL; | ||
143 | + blk1 = open_image(img_path, perm, shared_perm, &error_abort); | ||
144 | + g_assert(blk1); | ||
145 | + | ||
146 | + blk2 = open_image(img_path, perm, shared_perm, &error_abort); | ||
147 | + g_assert(blk2); | ||
148 | + | ||
149 | + check_locked_bytes(fd, perm, ~shared_perm); | ||
150 | + | ||
151 | + /* A failed blk_set_perm mustn't change perm status (locked bytes) */ | ||
152 | + r = blk_set_perm(blk2, perm | BLK_PERM_RESIZE, BLK_PERM_WRITE_UNCHANGED, | ||
153 | + NULL); | ||
154 | + g_assert_cmpint(r, !=, 0); | ||
155 | + check_locked_bytes(fd, perm, ~shared_perm); | ||
156 | + blk_unref(blk1); | ||
157 | + blk_unref(blk2); | ||
158 | +} | ||
159 | + | ||
160 | +int main(int argc, char **argv) | ||
161 | +{ | ||
162 | + bdrv_init(); | ||
163 | + qemu_init_main_loop(&error_abort); | ||
164 | + | ||
165 | + g_test_init(&argc, &argv, NULL); | ||
166 | + | ||
167 | + if (qemu_has_ofd_lock()) { | ||
168 | + g_test_add_func("/image-locking/basic", test_image_locking_basic); | ||
169 | + g_test_add_func("/image-locking/set-perm-abort", test_set_perm_abort); | ||
170 | + } | ||
171 | + | ||
172 | + return g_test_run(); | ||
173 | +} | ||
174 | diff --git a/tests/Makefile.include b/tests/Makefile.include | ||
175 | index XXXXXXX..XXXXXXX 100644 | ||
176 | --- a/tests/Makefile.include | ||
177 | +++ b/tests/Makefile.include | ||
178 | @@ -XXX,XX +XXX,XX @@ check-unit-y += tests/test-bdrv-drain$(EXESUF) | ||
179 | check-unit-y += tests/test-blockjob$(EXESUF) | ||
180 | check-unit-y += tests/test-blockjob-txn$(EXESUF) | ||
181 | check-unit-y += tests/test-block-backend$(EXESUF) | ||
182 | +check-unit-y += tests/test-image-locking$(EXESUF) | ||
183 | check-unit-y += tests/test-x86-cpuid$(EXESUF) | ||
184 | # all code tested by test-x86-cpuid is inside topology.h | ||
185 | ifeq ($(CONFIG_SOFTMMU),y) | ||
186 | @@ -XXX,XX +XXX,XX @@ tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(te | ||
187 | tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y) | ||
188 | tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y) | ||
189 | tests/test-block-backend$(EXESUF): tests/test-block-backend.o $(test-block-obj-y) $(test-util-obj-y) | ||
190 | +tests/test-image-locking$(EXESUF): tests/test-image-locking.o $(test-block-obj-y) $(test-util-obj-y) | ||
191 | tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y) | ||
192 | tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y) | ||
193 | tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y) $(test-crypto-obj-y) | ||
194 | -- | 231 | -- |
195 | 2.19.1 | 232 | 2.31.1 |
196 | 233 | ||
197 | 234 | diff view generated by jsdifflib |
1 | From: Liam Merwick <Liam.Merwick@oracle.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | The commit for 0e4e4318eaa5 increments QCOW2_OL_MAX_BITNR but does not | 3 | In most of the block layer, especially when traversing down from other |
4 | add an array entry for QCOW2_OL_BITMAP_DIRECTORY_BITNR to metadata_ol_names[]. | 4 | BlockDriverStates, we assume that BdrvChild.bs can never be NULL. When |
5 | As a result, an array dereference of metadata_ol_names[8] in | 5 | it becomes NULL, it is expected that the corresponding BdrvChild pointer |
6 | qcow2_pre_write_overlap_check() could result in a read outside of the array bounds. | 6 | also becomes NULL and the BdrvChild object is freed. |
7 | 7 | ||
8 | Fixes: 0e4e4318eaa5 ('qcow2: add overlap check for bitmap directory') | 8 | Therefore, once bdrv_replace_child_noperm() sets the BdrvChild.bs |
9 | 9 | pointer to NULL, it should also immediately set the corresponding | |
10 | Cc: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 10 | BdrvChild pointer (like bs->file or bs->backing) to NULL. |
11 | Signed-off-by: Liam Merwick <Liam.Merwick@oracle.com> | 11 | |
12 | Reviewed-by: Eric Blake <eblake@redhat.com> | 12 | In that context, it also makes sense for this function to free the |
13 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 13 | child. Sometimes we cannot do so, though, because it is called in a |
14 | Message-id: 1541453919-25973-6-git-send-email-Liam.Merwick@oracle.com | 14 | transactional context where the caller might still want to reinstate the |
15 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 15 | child in the abort branch (and free it only on commit), so this behavior |
16 | has to remain optional. | ||
17 | |||
18 | In bdrv_replace_child_tran()'s abort handler, we now rely on the fact | ||
19 | that the BdrvChild passed to bdrv_replace_child_tran() must have had a | ||
20 | non-NULL .bs pointer initially. Make a note of that and assert it. | ||
21 | |||
22 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> | ||
23 | Message-Id: <20211111120829.81329-10-hreitz@redhat.com> | ||
24 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
16 | --- | 25 | --- |
17 | block/qcow2-refcount.c | 18 ++++++++++-------- | 26 | block.c | 102 +++++++++++++++++++++++++++++++++++++++++++------------- |
18 | 1 file changed, 10 insertions(+), 8 deletions(-) | 27 | 1 file changed, 79 insertions(+), 23 deletions(-) |
19 | 28 | ||
20 | diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c | 29 | diff --git a/block.c b/block.c |
21 | index XXXXXXX..XXXXXXX 100644 | 30 | index XXXXXXX..XXXXXXX 100644 |
22 | --- a/block/qcow2-refcount.c | 31 | --- a/block.c |
23 | +++ b/block/qcow2-refcount.c | 32 | +++ b/block.c |
24 | @@ -XXX,XX +XXX,XX @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, | 33 | @@ -XXX,XX +XXX,XX @@ static BlockDriverState *bdrv_open_inherit(const char *filename, |
25 | } | 34 | static bool bdrv_recurse_has_child(BlockDriverState *bs, |
26 | 35 | BlockDriverState *child); | |
27 | static const char *metadata_ol_names[] = { | 36 | |
28 | - [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header", | 37 | +static void bdrv_child_free(BdrvChild *child); |
29 | - [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table", | 38 | static void bdrv_replace_child_noperm(BdrvChild **child, |
30 | - [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table", | 39 | - BlockDriverState *new_bs); |
31 | - [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table", | 40 | + BlockDriverState *new_bs, |
32 | - [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block", | 41 | + bool free_empty_child); |
33 | - [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table", | 42 | static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, |
34 | - [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table", | 43 | BdrvChild *child, |
35 | - [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table", | 44 | Transaction *tran); |
36 | + [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header", | 45 | @@ -XXX,XX +XXX,XX @@ typedef struct BdrvReplaceChildState { |
37 | + [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table", | 46 | BdrvChild *child; |
38 | + [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table", | 47 | BdrvChild **childp; |
39 | + [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table", | 48 | BlockDriverState *old_bs; |
40 | + [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block", | 49 | + bool free_empty_child; |
41 | + [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table", | 50 | } BdrvReplaceChildState; |
42 | + [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table", | 51 | |
43 | + [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table", | 52 | static void bdrv_replace_child_commit(void *opaque) |
44 | + [QCOW2_OL_BITMAP_DIRECTORY_BITNR] = "bitmap directory", | 53 | { |
45 | }; | 54 | BdrvReplaceChildState *s = opaque; |
46 | +QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names)); | 55 | |
56 | + if (s->free_empty_child && !s->child->bs) { | ||
57 | + bdrv_child_free(s->child); | ||
58 | + } | ||
59 | bdrv_unref(s->old_bs); | ||
60 | } | ||
61 | |||
62 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_abort(void *opaque) | ||
63 | * modify the BdrvChild * pointer we indirectly pass to it, i.e. it | ||
64 | * will not modify s->child. From that perspective, it does not matter | ||
65 | * whether we pass s->childp or &s->child. | ||
66 | - * (TODO: Right now, bdrv_replace_child_noperm() never modifies that | ||
67 | - * pointer anyway (though it will in the future), so at this point it | ||
68 | - * absolutely does not matter whether we pass s->childp or &s->child.) | ||
69 | * (2) If new_bs is not NULL, s->childp will be NULL. We then cannot use | ||
70 | * it here. | ||
71 | * (3) If new_bs is NULL, *s->childp will have been NULLed by | ||
72 | * bdrv_replace_child_tran()'s bdrv_replace_child_noperm() call, and we | ||
73 | * must not pass a NULL *s->childp here. | ||
74 | - * (TODO: In its current state, bdrv_replace_child_noperm() will not | ||
75 | - * have NULLed *s->childp, so this does not apply yet. It will in the | ||
76 | - * future.) | ||
77 | * | ||
78 | * So whether new_bs was NULL or not, we cannot pass s->childp here; and in | ||
79 | * any case, there is no reason to pass it anyway. | ||
80 | */ | ||
81 | - bdrv_replace_child_noperm(&s->child, s->old_bs); | ||
82 | + bdrv_replace_child_noperm(&s->child, s->old_bs, true); | ||
83 | + /* | ||
84 | + * The child was pre-existing, so s->old_bs must be non-NULL, and | ||
85 | + * s->child thus must not have been freed | ||
86 | + */ | ||
87 | + assert(s->child != NULL); | ||
88 | + if (!new_bs) { | ||
89 | + /* As described above, *s->childp was cleared, so restore it */ | ||
90 | + assert(s->childp != NULL); | ||
91 | + *s->childp = s->child; | ||
92 | + } | ||
93 | bdrv_unref(new_bs); | ||
94 | } | ||
95 | |||
96 | @@ -XXX,XX +XXX,XX @@ static TransactionActionDrv bdrv_replace_child_drv = { | ||
97 | * | ||
98 | * The function doesn't update permissions, caller is responsible for this. | ||
99 | * | ||
100 | + * (*childp)->bs must not be NULL. | ||
101 | + * | ||
102 | * Note that if new_bs == NULL, @childp is stored in a state object attached | ||
103 | * to @tran, so that the old child can be reinstated in the abort handler. | ||
104 | * Therefore, if @new_bs can be NULL, @childp must stay valid until the | ||
105 | * transaction is committed or aborted. | ||
106 | * | ||
107 | - * (TODO: The reinstating does not happen yet, but it will once | ||
108 | - * bdrv_replace_child_noperm() NULLs *childp when new_bs is NULL.) | ||
109 | + * If @free_empty_child is true and @new_bs is NULL, the BdrvChild is | ||
110 | + * freed (on commit). @free_empty_child should only be false if the | ||
111 | + * caller will free the BDrvChild themselves (which may be important | ||
112 | + * if this is in turn called in another transactional context). | ||
113 | */ | ||
114 | static void bdrv_replace_child_tran(BdrvChild **childp, | ||
115 | BlockDriverState *new_bs, | ||
116 | - Transaction *tran) | ||
117 | + Transaction *tran, | ||
118 | + bool free_empty_child) | ||
119 | { | ||
120 | BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1); | ||
121 | *s = (BdrvReplaceChildState) { | ||
122 | .child = *childp, | ||
123 | .childp = new_bs == NULL ? childp : NULL, | ||
124 | .old_bs = (*childp)->bs, | ||
125 | + .free_empty_child = free_empty_child, | ||
126 | }; | ||
127 | tran_add(tran, &bdrv_replace_child_drv, s); | ||
128 | |||
129 | + /* The abort handler relies on this */ | ||
130 | + assert(s->old_bs != NULL); | ||
131 | + | ||
132 | if (new_bs) { | ||
133 | bdrv_ref(new_bs); | ||
134 | } | ||
135 | - bdrv_replace_child_noperm(childp, new_bs); | ||
136 | + /* | ||
137 | + * Pass free_empty_child=false, we will free the child (if | ||
138 | + * necessary) in bdrv_replace_child_commit() (if our | ||
139 | + * @free_empty_child parameter was true). | ||
140 | + */ | ||
141 | + bdrv_replace_child_noperm(childp, new_bs, false); | ||
142 | /* old_bs reference is transparently moved from *childp to @s */ | ||
143 | } | ||
144 | |||
145 | @@ -XXX,XX +XXX,XX @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm) | ||
146 | return permissions[qapi_perm]; | ||
147 | } | ||
148 | |||
149 | +/** | ||
150 | + * Replace (*childp)->bs by @new_bs. | ||
151 | + * | ||
152 | + * If @new_bs is NULL, *childp will be set to NULL, too: BDS parents | ||
153 | + * generally cannot handle a BdrvChild with .bs == NULL, so clearing | ||
154 | + * BdrvChild.bs should generally immediately be followed by the | ||
155 | + * BdrvChild pointer being cleared as well. | ||
156 | + * | ||
157 | + * If @free_empty_child is true and @new_bs is NULL, the BdrvChild is | ||
158 | + * freed. @free_empty_child should only be false if the caller will | ||
159 | + * free the BdrvChild themselves (this may be important in a | ||
160 | + * transactional context, where it may only be freed on commit). | ||
161 | + */ | ||
162 | static void bdrv_replace_child_noperm(BdrvChild **childp, | ||
163 | - BlockDriverState *new_bs) | ||
164 | + BlockDriverState *new_bs, | ||
165 | + bool free_empty_child) | ||
166 | { | ||
167 | BdrvChild *child = *childp; | ||
168 | BlockDriverState *old_bs = child->bs; | ||
169 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild **childp, | ||
170 | } | ||
171 | |||
172 | child->bs = new_bs; | ||
173 | + if (!new_bs) { | ||
174 | + *childp = NULL; | ||
175 | + } | ||
176 | |||
177 | if (new_bs) { | ||
178 | QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); | ||
179 | @@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild **childp, | ||
180 | bdrv_parent_drained_end_single(child); | ||
181 | drain_saldo++; | ||
182 | } | ||
183 | + | ||
184 | + if (free_empty_child && !child->bs) { | ||
185 | + bdrv_child_free(child); | ||
186 | + } | ||
187 | } | ||
188 | |||
189 | /** | ||
190 | @@ -XXX,XX +XXX,XX @@ static void bdrv_attach_child_common_abort(void *opaque) | ||
191 | BdrvChild *child = *s->child; | ||
192 | BlockDriverState *bs = child->bs; | ||
193 | |||
194 | - bdrv_replace_child_noperm(s->child, NULL); | ||
195 | + /* | ||
196 | + * Pass free_empty_child=false, because we still need the child | ||
197 | + * for the AioContext operations on the parent below; those | ||
198 | + * BdrvChildClass methods all work on a BdrvChild object, so we | ||
199 | + * need to keep it as an empty shell (after this function, it will | ||
200 | + * not be attached to any parent, and it will not have a .bs). | ||
201 | + */ | ||
202 | + bdrv_replace_child_noperm(s->child, NULL, false); | ||
203 | |||
204 | if (bdrv_get_aio_context(bs) != s->old_child_ctx) { | ||
205 | bdrv_try_set_aio_context(bs, s->old_child_ctx, &error_abort); | ||
206 | @@ -XXX,XX +XXX,XX @@ static void bdrv_attach_child_common_abort(void *opaque) | ||
207 | |||
208 | bdrv_unref(bs); | ||
209 | bdrv_child_free(child); | ||
210 | - *s->child = NULL; | ||
211 | } | ||
212 | |||
213 | static TransactionActionDrv bdrv_attach_child_common_drv = { | ||
214 | @@ -XXX,XX +XXX,XX @@ static int bdrv_attach_child_common(BlockDriverState *child_bs, | ||
215 | } | ||
216 | |||
217 | bdrv_ref(child_bs); | ||
218 | - bdrv_replace_child_noperm(&new_child, child_bs); | ||
219 | + bdrv_replace_child_noperm(&new_child, child_bs, true); | ||
220 | + /* child_bs was non-NULL, so new_child must not have been freed */ | ||
221 | + assert(new_child != NULL); | ||
222 | |||
223 | *child = new_child; | ||
224 | |||
225 | @@ -XXX,XX +XXX,XX @@ static void bdrv_detach_child(BdrvChild **childp) | ||
226 | { | ||
227 | BlockDriverState *old_bs = (*childp)->bs; | ||
228 | |||
229 | - bdrv_replace_child_noperm(childp, NULL); | ||
230 | - bdrv_child_free(*childp); | ||
231 | + bdrv_replace_child_noperm(childp, NULL, true); | ||
232 | |||
233 | if (old_bs) { | ||
234 | /* | ||
235 | @@ -XXX,XX +XXX,XX @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, | ||
236 | } | ||
237 | |||
238 | if (child->bs) { | ||
239 | - bdrv_replace_child_tran(childp, NULL, tran); | ||
240 | + /* | ||
241 | + * Pass free_empty_child=false, we will free the child in | ||
242 | + * bdrv_remove_filter_or_cow_child_commit() | ||
243 | + */ | ||
244 | + bdrv_replace_child_tran(childp, NULL, tran, false); | ||
245 | } | ||
246 | |||
247 | s = g_new(BdrvRemoveFilterOrCowChild, 1); | ||
248 | @@ -XXX,XX +XXX,XX @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, | ||
249 | .is_backing = (childp == &bs->backing), | ||
250 | }; | ||
251 | tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s); | ||
252 | - | ||
253 | - *childp = NULL; | ||
254 | } | ||
47 | 255 | ||
48 | /* | 256 | /* |
49 | * First performs a check for metadata overlaps (through | 257 | @@ -XXX,XX +XXX,XX @@ static int bdrv_replace_node_noperm(BlockDriverState *from, |
258 | * Passing a pointer to the local variable @c is fine here, because | ||
259 | * @to is not NULL, and so &c will not be attached to the transaction. | ||
260 | */ | ||
261 | - bdrv_replace_child_tran(&c, to, tran); | ||
262 | + bdrv_replace_child_tran(&c, to, tran, true); | ||
263 | } | ||
264 | |||
265 | return 0; | ||
266 | @@ -XXX,XX +XXX,XX @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, | ||
267 | bdrv_drained_begin(old_bs); | ||
268 | bdrv_drained_begin(new_bs); | ||
269 | |||
270 | - bdrv_replace_child_tran(&child, new_bs, tran); | ||
271 | + bdrv_replace_child_tran(&child, new_bs, tran, true); | ||
272 | + /* @new_bs must have been non-NULL, so @child must not have been freed */ | ||
273 | + assert(child != NULL); | ||
274 | |||
275 | found = g_hash_table_new(NULL, NULL); | ||
276 | refresh_list = bdrv_topological_dfs(refresh_list, found, old_bs); | ||
50 | -- | 277 | -- |
51 | 2.19.1 | 278 | 2.31.1 |
52 | 279 | ||
53 | 280 | diff view generated by jsdifflib |
1 | From: Li Qiang <liq3ea@gmail.com> | 1 | From: Hanna Reitz <hreitz@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Currently, when hotplug/unhotplug nvme device, it will cause an | 3 | See the comment for why this is necessary. |
4 | assert in object.c. Following is the backtrack: | ||
5 | 4 | ||
6 | ERROR:qom/object.c:981:object_unref: assertion failed: (obj->ref > 0) | 5 | Signed-off-by: Hanna Reitz <hreitz@redhat.com> |
7 | 6 | Message-Id: <20211111120829.81329-11-hreitz@redhat.com> | |
8 | Thread 2 "qemu-system-x86" received signal SIGABRT, Aborted. | ||
9 | [Switching to Thread 0x7fffcbd32700 (LWP 18844)] | ||
10 | 0x00007fffdb9e4fff in raise () from /lib/x86_64-linux-gnu/libc.so.6 | ||
11 | (gdb) bt | ||
12 | /lib/x86_64-linux-gnu/libglib-2.0.so.0 | ||
13 | /lib/x86_64-linux-gnu/libglib-2.0.so.0 | ||
14 | qom/object.c:981 | ||
15 | /home/liqiang02/qemu-upstream/qemu/memory.c:1732 | ||
16 | /home/liqiang02/qemu-upstream/qemu/memory.c:285 | ||
17 | util/qemu-thread-posix.c:504 | ||
18 | /lib/x86_64-linux-gnu/libpthread.so.0 | ||
19 | |||
20 | This is caused by memory_region_unref in nvme_exit. | ||
21 | |||
22 | Remove it to make the PCIdevice refcount correct. | ||
23 | |||
24 | Signed-off-by: Li Qiang <liq3ea@gmail.com> | ||
25 | Reviewed-by: Igor Mammedov <imammedo@redhat.com> | ||
26 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
27 | --- | 8 | --- |
28 | hw/block/nvme.c | 3 --- | 9 | tests/qemu-iotests/030 | 11 ++++++++++- |
29 | 1 file changed, 3 deletions(-) | 10 | 1 file changed, 10 insertions(+), 1 deletion(-) |
30 | 11 | ||
31 | diff --git a/hw/block/nvme.c b/hw/block/nvme.c | 12 | diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 |
32 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100755 |
33 | --- a/hw/block/nvme.c | 14 | --- a/tests/qemu-iotests/030 |
34 | +++ b/hw/block/nvme.c | 15 | +++ b/tests/qemu-iotests/030 |
35 | @@ -XXX,XX +XXX,XX @@ static void nvme_exit(PCIDevice *pci_dev) | 16 | @@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase): |
36 | g_free(n->namespaces); | 17 | speed=1024) |
37 | g_free(n->cq); | 18 | self.assert_qmp(result, 'return', {}) |
38 | g_free(n->sq); | 19 | |
39 | - if (n->cmbsz) { | 20 | - for job in pending_jobs: |
40 | - memory_region_unref(&n->ctrl_mem); | 21 | + # Do this in reverse: After unthrottling them, some jobs may finish |
41 | - } | 22 | + # before we have unthrottled all of them. This will drain their |
42 | 23 | + # subgraph, and this will make jobs above them advance (despite those | |
43 | msix_uninit_exclusive_bar(pci_dev); | 24 | + # jobs on top being throttled). In the worst case, all jobs below the |
44 | } | 25 | + # top one are finished before we can unthrottle it, and this makes it |
26 | + # advance so far that it completes before we can unthrottle it - which | ||
27 | + # results in an error. | ||
28 | + # Starting from the top (i.e. in reverse) does not have this problem: | ||
29 | + # When a job finishes, the ones below it are not advanced. | ||
30 | + for job in reversed(pending_jobs): | ||
31 | result = self.vm.qmp('block-job-set-speed', device=job, speed=0) | ||
32 | self.assert_qmp(result, 'return', {}) | ||
33 | |||
45 | -- | 34 | -- |
46 | 2.19.1 | 35 | 2.31.1 |
47 | 36 | ||
48 | 37 | diff view generated by jsdifflib |
1 | From: Li Qiang <liq3ea@gmail.com> | 1 | While introducing a non-QemuOpts code path for device creation for JSON |
---|---|---|---|
2 | -device, we noticed that QMP device_add doesn't check its input | ||
3 | correctly (accepting arguments that should have been rejected), and that | ||
4 | users may be relying on this behaviour (libvirt did until it was fixed | ||
5 | recently). | ||
2 | 6 | ||
3 | This avoid a memory leak in unhotplug nvme device. | 7 | Let's use a deprecation period before we fix this bug in QEMU to avoid |
8 | nasty surprises for users. | ||
4 | 9 | ||
5 | Signed-off-by: Li Qiang <liq3ea@gmail.com> | 10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
6 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> | 11 | Message-Id: <20211111143530.18985-1-kwolf@redhat.com> |
12 | Reviewed-by: Markus Armbruster <armbru@redhat.com> | ||
13 | Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 14 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | --- | 15 | --- |
9 | hw/block/nvme.c | 3 +++ | 16 | docs/about/deprecated.rst | 14 ++++++++++++++ |
10 | 1 file changed, 3 insertions(+) | 17 | 1 file changed, 14 insertions(+) |
11 | 18 | ||
12 | diff --git a/hw/block/nvme.c b/hw/block/nvme.c | 19 | diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst |
13 | index XXXXXXX..XXXXXXX 100644 | 20 | index XXXXXXX..XXXXXXX 100644 |
14 | --- a/hw/block/nvme.c | 21 | --- a/docs/about/deprecated.rst |
15 | +++ b/hw/block/nvme.c | 22 | +++ b/docs/about/deprecated.rst |
16 | @@ -XXX,XX +XXX,XX @@ static void nvme_exit(PCIDevice *pci_dev) | 23 | @@ -XXX,XX +XXX,XX @@ options are removed in favor of using explicit ``blockdev-create`` and |
17 | g_free(n->cq); | 24 | ``blockdev-add`` calls. See :doc:`/interop/live-block-operations` for |
18 | g_free(n->sq); | 25 | details. |
19 | 26 | ||
20 | + if (n->cmb_size_mb) { | 27 | +Incorrectly typed ``device_add`` arguments (since 6.2) |
21 | + g_free(n->cmbuf); | 28 | +'''''''''''''''''''''''''''''''''''''''''''''''''''''' |
22 | + } | 29 | + |
23 | msix_uninit_exclusive_bar(pci_dev); | 30 | +Due to shortcomings in the internal implementation of ``device_add``, QEMU |
24 | } | 31 | +incorrectly accepts certain invalid arguments: Any object or list arguments are |
32 | +silently ignored. Other argument types are not checked, but an implicit | ||
33 | +conversion happens, so that e.g. string values can be assigned to integer | ||
34 | +device properties or vice versa. | ||
35 | + | ||
36 | +This is a bug in QEMU that will be fixed in the future so that previously | ||
37 | +accepted incorrect commands will return an error. Users should make sure that | ||
38 | +all arguments passed to ``device_add`` are consistent with the documented | ||
39 | +property types. | ||
40 | + | ||
41 | System accelerators | ||
42 | ------------------- | ||
25 | 43 | ||
26 | -- | 44 | -- |
27 | 2.19.1 | 45 | 2.31.1 |
28 | 46 | ||
29 | 47 | diff view generated by jsdifflib |
1 | From: Fam Zheng <famz@redhat.com> | 1 | At the end of a reopen, we already call bdrv_refresh_limits(), which |
---|---|---|---|
2 | should update bs->request_alignment according to the new file | ||
3 | descriptor. However, raw_probe_alignment() relies on s->needs_alignment | ||
4 | and just uses 1 if it isn't set. We neglected to update this field, so | ||
5 | starting with cache=writeback and then reopening with cache=none means | ||
6 | that we get an incorrect bs->request_alignment == 1 and unaligned | ||
7 | requests fail instead of being automatically aligned. | ||
2 | 8 | ||
3 | If we know we've already locked the bytes, don't do it again; similarly | 9 | Fix this by recalculating s->needs_alignment in raw_refresh_limits() |
4 | don't unlock a byte if we haven't locked it. This doesn't change the | 10 | before calling raw_probe_alignment(). |
5 | behavior, but fixes a corner case explained below. | ||
6 | 11 | ||
7 | Libvirt had an error handling bug that an image can get its (ownership, | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
8 | file mode, SELinux) permissions changed (RHBZ 1584982) by mistake behind | 13 | Message-Id: <20211104113109.56336-1-kwolf@redhat.com> |
9 | QEMU. Specifically, an image in use by Libvirt VM has: | 14 | Reviewed-by: Hanna Reitz <hreitz@redhat.com> |
10 | 15 | Reviewed-by: Stefano Garzarella <sgarzare@redhat.com> | |
11 | $ ls -lhZ b.img | ||
12 | -rw-r--r--. qemu qemu system_u:object_r:svirt_image_t:s0:c600,c690 b.img | ||
13 | |||
14 | Trying to attach it a second time won't work because of image locking. | ||
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 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
32 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
33 | --- | 17 | --- |
34 | block/file-posix.c | 54 +++++++++++++++++++++++++++++++++++++--------- | 18 | block/file-posix.c | 20 ++++++++++++++++---- |
35 | 1 file changed, 44 insertions(+), 10 deletions(-) | 19 | tests/qemu-iotests/142 | 22 ++++++++++++++++++++++ |
20 | tests/qemu-iotests/142.out | 15 +++++++++++++++ | ||
21 | 3 files changed, 53 insertions(+), 4 deletions(-) | ||
36 | 22 | ||
37 | diff --git a/block/file-posix.c b/block/file-posix.c | 23 | diff --git a/block/file-posix.c b/block/file-posix.c |
38 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
39 | --- a/block/file-posix.c | 25 | --- a/block/file-posix.c |
40 | +++ b/block/file-posix.c | 26 | +++ b/block/file-posix.c |
41 | @@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState { | 27 | @@ -XXX,XX +XXX,XX @@ typedef struct BDRVRawState { |
42 | uint64_t perm; | 28 | int page_cache_inconsistent; /* errno from fdatasync failure */ |
43 | uint64_t shared_perm; | 29 | bool has_fallocate; |
44 | 30 | bool needs_alignment; | |
45 | + /* The perms bits whose corresponding bytes are already locked in | 31 | + bool force_alignment; |
46 | + * s->lock_fd. */ | 32 | bool drop_cache; |
47 | + uint64_t locked_perm; | 33 | bool check_cache_dropped; |
48 | + uint64_t locked_shared_perm; | 34 | struct { |
35 | @@ -XXX,XX +XXX,XX @@ static bool dio_byte_aligned(int fd) | ||
36 | return false; | ||
37 | } | ||
38 | |||
39 | +static bool raw_needs_alignment(BlockDriverState *bs) | ||
40 | +{ | ||
41 | + BDRVRawState *s = bs->opaque; | ||
49 | + | 42 | + |
43 | + if ((bs->open_flags & BDRV_O_NOCACHE) != 0 && !dio_byte_aligned(s->fd)) { | ||
44 | + return true; | ||
45 | + } | ||
46 | + | ||
47 | + return s->force_alignment; | ||
48 | +} | ||
49 | + | ||
50 | /* Check if read is allowed with given memory buffer and length. | ||
51 | * | ||
52 | * This function is used to check O_DIRECT memory buffer and request alignment. | ||
53 | @@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options, | ||
54 | |||
55 | s->has_discard = true; | ||
56 | s->has_write_zeroes = true; | ||
57 | - if ((bs->open_flags & BDRV_O_NOCACHE) != 0 && !dio_byte_aligned(s->fd)) { | ||
58 | - s->needs_alignment = true; | ||
59 | - } | ||
60 | |||
61 | if (fstat(s->fd, &st) < 0) { | ||
62 | ret = -errno; | ||
63 | @@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options, | ||
64 | * so QEMU makes sure all IO operations on the device are aligned | ||
65 | * to sector size, or else FreeBSD will reject them with EINVAL. | ||
66 | */ | ||
67 | - s->needs_alignment = true; | ||
68 | + s->force_alignment = true; | ||
69 | } | ||
70 | #endif | ||
71 | + s->needs_alignment = raw_needs_alignment(bs); | ||
72 | |||
50 | #ifdef CONFIG_XFS | 73 | #ifdef CONFIG_XFS |
51 | bool is_xfs:1; | 74 | if (platform_test_xfs_fd(s->fd)) { |
52 | #endif | 75 | @@ -XXX,XX +XXX,XX @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) |
53 | @@ -XXX,XX +XXX,XX @@ typedef enum { | 76 | BDRVRawState *s = bs->opaque; |
54 | * file; if @unlock == true, also unlock the unneeded bytes. | 77 | struct stat st; |
55 | * @shared_perm_lock_bits is the mask of all permissions that are NOT shared. | 78 | |
56 | */ | 79 | + s->needs_alignment = raw_needs_alignment(bs); |
57 | -static int raw_apply_lock_bytes(int fd, | 80 | raw_probe_alignment(bs, s->fd, errp); |
58 | +static int raw_apply_lock_bytes(BDRVRawState *s, int fd, | ||
59 | uint64_t perm_lock_bits, | ||
60 | uint64_t shared_perm_lock_bits, | ||
61 | bool unlock, Error **errp) | ||
62 | { | ||
63 | int ret; | ||
64 | int i; | ||
65 | + uint64_t locked_perm, locked_shared_perm; | ||
66 | + | 81 | + |
67 | + if (s) { | 82 | bs->bl.min_mem_alignment = s->buf_align; |
68 | + locked_perm = s->locked_perm; | 83 | bs->bl.opt_mem_alignment = MAX(s->buf_align, qemu_real_host_page_size); |
69 | + locked_shared_perm = s->locked_shared_perm; | 84 | |
70 | + } else { | 85 | diff --git a/tests/qemu-iotests/142 b/tests/qemu-iotests/142 |
71 | + /* | 86 | index XXXXXXX..XXXXXXX 100755 |
72 | + * We don't have the previous bits, just lock/unlock for each of the | 87 | --- a/tests/qemu-iotests/142 |
73 | + * requested bits. | 88 | +++ b/tests/qemu-iotests/142 |
74 | + */ | 89 | @@ -XXX,XX +XXX,XX @@ info block backing-file" |
75 | + if (unlock) { | 90 | |
76 | + locked_perm = BLK_PERM_ALL; | 91 | echo "$hmp_cmds" | run_qemu -drive "$files","$ids" | grep "Cache" |
77 | + locked_shared_perm = BLK_PERM_ALL; | 92 | |
78 | + } else { | 93 | +echo |
79 | + locked_perm = 0; | 94 | +echo "--- Alignment after changing O_DIRECT ---" |
80 | + locked_shared_perm = 0; | 95 | +echo |
81 | + } | 96 | + |
82 | + } | 97 | +# Directly test the protocol level: Can unaligned requests succeed even if |
83 | 98 | +# O_DIRECT was only enabled through a reopen and vice versa? | |
84 | PERM_FOREACH(i) { | 99 | + |
85 | int off = RAW_LOCK_PERM_BASE + i; | 100 | +$QEMU_IO --cache=writeback -f file $TEST_IMG <<EOF | _filter_qemu_io |
86 | - if (perm_lock_bits & (1ULL << i)) { | 101 | +read 42 42 |
87 | + uint64_t bit = (1ULL << i); | 102 | +reopen -o cache.direct=on |
88 | + if ((perm_lock_bits & bit) && !(locked_perm & bit)) { | 103 | +read 42 42 |
89 | ret = qemu_lock_fd(fd, off, 1, false); | 104 | +reopen -o cache.direct=off |
90 | if (ret) { | 105 | +read 42 42 |
91 | error_setg(errp, "Failed to lock byte %d", off); | 106 | +EOF |
92 | return ret; | 107 | +$QEMU_IO --cache=none -f file $TEST_IMG <<EOF | _filter_qemu_io |
93 | + } else if (s) { | 108 | +read 42 42 |
94 | + s->locked_perm |= bit; | 109 | +reopen -o cache.direct=off |
95 | } | 110 | +read 42 42 |
96 | - } else if (unlock) { | 111 | +reopen -o cache.direct=on |
97 | + } else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) { | 112 | +read 42 42 |
98 | ret = qemu_unlock_fd(fd, off, 1); | 113 | +EOF |
99 | if (ret) { | 114 | + |
100 | error_setg(errp, "Failed to unlock byte %d", off); | 115 | # success, all done |
101 | return ret; | 116 | echo "*** done" |
102 | + } else if (s) { | 117 | rm -f $seq.full |
103 | + s->locked_perm &= ~bit; | 118 | diff --git a/tests/qemu-iotests/142.out b/tests/qemu-iotests/142.out |
104 | } | 119 | index XXXXXXX..XXXXXXX 100644 |
105 | } | 120 | --- a/tests/qemu-iotests/142.out |
106 | } | 121 | +++ b/tests/qemu-iotests/142.out |
107 | PERM_FOREACH(i) { | 122 | @@ -XXX,XX +XXX,XX @@ cache.no-flush=on on backing-file |
108 | int off = RAW_LOCK_SHARED_BASE + i; | 123 | Cache mode: writeback |
109 | - if (shared_perm_lock_bits & (1ULL << i)) { | 124 | Cache mode: writeback, direct |
110 | + uint64_t bit = (1ULL << i); | 125 | Cache mode: writeback, ignore flushes |
111 | + if ((shared_perm_lock_bits & bit) && !(locked_shared_perm & bit)) { | 126 | + |
112 | ret = qemu_lock_fd(fd, off, 1, false); | 127 | +--- Alignment after changing O_DIRECT --- |
113 | if (ret) { | 128 | + |
114 | error_setg(errp, "Failed to lock byte %d", off); | 129 | +read 42/42 bytes at offset 42 |
115 | return ret; | 130 | +42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
116 | + } else if (s) { | 131 | +read 42/42 bytes at offset 42 |
117 | + s->locked_shared_perm |= bit; | 132 | +42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
118 | } | 133 | +read 42/42 bytes at offset 42 |
119 | - } else if (unlock) { | 134 | +42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
120 | + } else if (unlock && (locked_shared_perm & bit) && | 135 | +read 42/42 bytes at offset 42 |
121 | + !(shared_perm_lock_bits & bit)) { | 136 | +42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
122 | ret = qemu_unlock_fd(fd, off, 1); | 137 | +read 42/42 bytes at offset 42 |
123 | if (ret) { | 138 | +42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
124 | error_setg(errp, "Failed to unlock byte %d", off); | 139 | +read 42/42 bytes at offset 42 |
125 | return ret; | 140 | +42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) |
126 | + } else if (s) { | 141 | *** done |
127 | + s->locked_shared_perm &= ~bit; | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | @@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs, | ||
132 | |||
133 | switch (op) { | ||
134 | case RAW_PL_PREPARE: | ||
135 | - ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm, | ||
136 | + ret = raw_apply_lock_bytes(s, s->lock_fd, s->perm | new_perm, | ||
137 | ~s->shared_perm | ~new_shared, | ||
138 | false, errp); | ||
139 | if (!ret) { | ||
140 | @@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs, | ||
141 | op = RAW_PL_ABORT; | ||
142 | /* fall through to unlock bytes. */ | ||
143 | case RAW_PL_ABORT: | ||
144 | - raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm, | ||
145 | + raw_apply_lock_bytes(s, s->lock_fd, s->perm, ~s->shared_perm, | ||
146 | true, &local_err); | ||
147 | if (local_err) { | ||
148 | /* Theoretically the above call only unlocks bytes and it cannot | ||
149 | @@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs, | ||
150 | } | ||
151 | break; | ||
152 | case RAW_PL_COMMIT: | ||
153 | - raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared, | ||
154 | + raw_apply_lock_bytes(s, s->lock_fd, new_perm, ~new_shared, | ||
155 | true, &local_err); | ||
156 | if (local_err) { | ||
157 | /* Theoretically the above call only unlocks bytes and it cannot | ||
158 | @@ -XXX,XX +XXX,XX @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) | ||
159 | shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; | ||
160 | |||
161 | /* Step one: Take locks */ | ||
162 | - result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp); | ||
163 | + result = raw_apply_lock_bytes(NULL, fd, perm, ~shared, false, errp); | ||
164 | if (result < 0) { | ||
165 | goto out_close; | ||
166 | } | ||
167 | @@ -XXX,XX +XXX,XX @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) | ||
168 | } | ||
169 | |||
170 | out_unlock: | ||
171 | - raw_apply_lock_bytes(fd, 0, 0, true, &local_err); | ||
172 | + raw_apply_lock_bytes(NULL, fd, 0, 0, true, &local_err); | ||
173 | if (local_err) { | ||
174 | /* The above call should not fail, and if it does, that does | ||
175 | * not mean the whole creation operation has failed. So | ||
176 | -- | 142 | -- |
177 | 2.19.1 | 143 | 2.31.1 |
178 | 144 | ||
179 | 145 | diff view generated by jsdifflib |
1 | From: Peter Maydell <peter.maydell@linaro.org> | 1 | From: Stefan Hajnoczi <stefanha@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | In the function external_snapshot_prepare() we have a | 3 | Reported by Coverity (CID 1465222). |
4 | BlockdevSnapshotSync struct, which has the usual combination | ||
5 | of has_snapshot_node_name and snapshot_node_name fields for an | ||
6 | optional field. We set up a local variable | ||
7 | const char *snapshot_node_name = | ||
8 | s->has_snapshot_node_name ? s->snapshot_node_name : NULL; | ||
9 | 4 | ||
10 | and then mostly use "if (!snapshot_node_name)" for checking | 5 | Fixes: 4a1d937796de0fecd8b22d7dbebf87f38e8282fd ("softmmu/qdev-monitor: add error handling in qdev_set_id") |
11 | whether we have a snapshot node name. The exception is that in | 6 | Cc: Damien Hedde <damien.hedde@greensocs.com> |
12 | one place we check s->has_snapshot_node_name instead. This | 7 | Cc: Kevin Wolf <kwolf@redhat.com> |
13 | confuses Coverity (CID 1396473), which thinks it might be | 8 | Cc: Michael S. Tsirkin <mst@redhat.com> |
14 | possible to get here with s->has_snapshot_node_name true but | 9 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> |
15 | snapshot_node_name NULL, and warns that the call to | 10 | Message-Id: <20211102163342.31162-1-stefanha@redhat.com> |
16 | qdict_put_str() will segfault in that case. | 11 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
17 | 12 | Reviewed-by: Michael S. Tsirkin <mst@redhat.com> | |
18 | Make the code consistent and unconfuse Coverity by using | 13 | Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> |
19 | the same check for this conditional that we do in the rest | 14 | Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> |
20 | of the surrounding code. | 15 | Reviewed-by: Damien Hedde <damien.hedde@greensocs.com> |
21 | 16 | Reviewed-by: Markus Armbruster <armbru@redhat.com> | |
22 | Signed-off-by: Peter Maydell <peter.maydell@linaro.org> | ||
23 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
24 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 17 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
25 | --- | 18 | --- |
26 | blockdev.c | 2 +- | 19 | softmmu/qdev-monitor.c | 2 +- |
27 | 1 file changed, 1 insertion(+), 1 deletion(-) | 20 | 1 file changed, 1 insertion(+), 1 deletion(-) |
28 | 21 | ||
29 | diff --git a/blockdev.c b/blockdev.c | 22 | diff --git a/softmmu/qdev-monitor.c b/softmmu/qdev-monitor.c |
30 | index XXXXXXX..XXXXXXX 100644 | 23 | index XXXXXXX..XXXXXXX 100644 |
31 | --- a/blockdev.c | 24 | --- a/softmmu/qdev-monitor.c |
32 | +++ b/blockdev.c | 25 | +++ b/softmmu/qdev-monitor.c |
33 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common, | 26 | @@ -XXX,XX +XXX,XX @@ const char *qdev_set_id(DeviceState *dev, char *id, Error **errp) |
27 | if (prop) { | ||
28 | dev->id = id; | ||
29 | } else { | ||
30 | - g_free(id); | ||
31 | error_setg(errp, "Duplicate device ID '%s'", id); | ||
32 | + g_free(id); | ||
33 | return NULL; | ||
34 | } | 34 | } |
35 | 35 | } else { | |
36 | options = qdict_new(); | ||
37 | - if (s->has_snapshot_node_name) { | ||
38 | + if (snapshot_node_name) { | ||
39 | qdict_put_str(options, "node-name", snapshot_node_name); | ||
40 | } | ||
41 | qdict_put_str(options, "driver", format); | ||
42 | -- | 36 | -- |
43 | 2.19.1 | 37 | 2.31.1 |
44 | 38 | ||
45 | 39 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Jeff Cody <jcody@redhat.com> | ||
2 | 1 | ||
3 | This adds configure options to control the following block drivers: | ||
4 | |||
5 | * Bochs | ||
6 | * Cloop | ||
7 | * Dmg | ||
8 | * Qcow (V1) | ||
9 | * Vdi | ||
10 | * Vvfat | ||
11 | * qed | ||
12 | * parallels | ||
13 | * sheepdog | ||
14 | |||
15 | Each of these defaults to being enabled. | ||
16 | |||
17 | Signed-off-by: Jeff Cody <jcody@redhat.com> | ||
18 | Signed-off-by: Markus Armbruster <armbru@redhat.com> | ||
19 | Message-id: 20181107063644.2254-1-armbru@redhat.com | ||
20 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
21 | --- | ||
22 | configure | 91 +++++++++++++++++++++++++++++++++++++++++++++ | ||
23 | block/Makefile.objs | 22 ++++++++--- | ||
24 | 2 files changed, 107 insertions(+), 6 deletions(-) | ||
25 | |||
26 | diff --git a/configure b/configure | ||
27 | index XXXXXXX..XXXXXXX 100755 | ||
28 | --- a/configure | ||
29 | +++ b/configure | ||
30 | @@ -XXX,XX +XXX,XX @@ tcmalloc="no" | ||
31 | jemalloc="no" | ||
32 | replication="yes" | ||
33 | vxhs="" | ||
34 | +bochs="yes" | ||
35 | +cloop="yes" | ||
36 | +dmg="yes" | ||
37 | +qcow1="yes" | ||
38 | +vdi="yes" | ||
39 | +vvfat="yes" | ||
40 | +qed="yes" | ||
41 | +parallels="yes" | ||
42 | +sheepdog="yes" | ||
43 | libxml2="" | ||
44 | docker="no" | ||
45 | debug_mutex="no" | ||
46 | @@ -XXX,XX +XXX,XX @@ for opt do | ||
47 | ;; | ||
48 | --enable-vxhs) vxhs="yes" | ||
49 | ;; | ||
50 | + --disable-bochs) bochs="no" | ||
51 | + ;; | ||
52 | + --enable-bochs) bochs="yes" | ||
53 | + ;; | ||
54 | + --disable-cloop) cloop="no" | ||
55 | + ;; | ||
56 | + --enable-cloop) cloop="yes" | ||
57 | + ;; | ||
58 | + --disable-dmg) dmg="no" | ||
59 | + ;; | ||
60 | + --enable-dmg) dmg="yes" | ||
61 | + ;; | ||
62 | + --disable-qcow1) qcow1="no" | ||
63 | + ;; | ||
64 | + --enable-qcow1) qcow1="yes" | ||
65 | + ;; | ||
66 | + --disable-vdi) vdi="no" | ||
67 | + ;; | ||
68 | + --enable-vdi) vdi="yes" | ||
69 | + ;; | ||
70 | + --disable-vvfat) vvfat="no" | ||
71 | + ;; | ||
72 | + --enable-vvfat) vvfat="yes" | ||
73 | + ;; | ||
74 | + --disable-qed) qed="no" | ||
75 | + ;; | ||
76 | + --enable-qed) qed="yes" | ||
77 | + ;; | ||
78 | + --disable-parallels) parallels="no" | ||
79 | + ;; | ||
80 | + --enable-parallels) parallels="yes" | ||
81 | + ;; | ||
82 | + --disable-sheepdog) sheepdog="no" | ||
83 | + ;; | ||
84 | + --enable-sheepdog) sheepdog="yes" | ||
85 | + ;; | ||
86 | --disable-vhost-user) vhost_user="no" | ||
87 | ;; | ||
88 | --enable-vhost-user) | ||
89 | @@ -XXX,XX +XXX,XX @@ disabled with --disable-FEATURE, default is enabled if available: | ||
90 | qom-cast-debug cast debugging support | ||
91 | tools build qemu-io, qemu-nbd and qemu-image tools | ||
92 | vxhs Veritas HyperScale vDisk backend support | ||
93 | + bochs bochs image format support | ||
94 | + cloop cloop image format support | ||
95 | + dmg dmg image format support | ||
96 | + qcow1 qcow v1 image format support | ||
97 | + vdi vdi image format support | ||
98 | + vvfat vvfat image format support | ||
99 | + qed qed image format support | ||
100 | + parallels parallels image format support | ||
101 | + sheepdog sheepdog block driver support | ||
102 | crypto-afalg Linux AF_ALG crypto backend driver | ||
103 | vhost-user vhost-user support | ||
104 | capstone capstone disassembler support | ||
105 | @@ -XXX,XX +XXX,XX @@ echo "jemalloc support $jemalloc" | ||
106 | echo "avx2 optimization $avx2_opt" | ||
107 | echo "replication support $replication" | ||
108 | echo "VxHS block device $vxhs" | ||
109 | +echo "bochs support $bochs" | ||
110 | +echo "cloop support $cloop" | ||
111 | +echo "dmg support $dmg" | ||
112 | +echo "qcow v1 support $qcow1" | ||
113 | +echo "vdi support $vdi" | ||
114 | +echo "vvfat support $vvfat" | ||
115 | +echo "qed support $qed" | ||
116 | +echo "parallels support $parallels" | ||
117 | +echo "sheepdog support $sheepdog" | ||
118 | echo "capstone $capstone" | ||
119 | echo "docker $docker" | ||
120 | echo "libpmem support $libpmem" | ||
121 | @@ -XXX,XX +XXX,XX @@ if test "$libpmem" = "yes" ; then | ||
122 | echo "CONFIG_LIBPMEM=y" >> $config_host_mak | ||
123 | fi | ||
124 | |||
125 | +if test "$bochs" = "yes" ; then | ||
126 | + echo "CONFIG_BOCHS=y" >> $config_host_mak | ||
127 | +fi | ||
128 | +if test "$cloop" = "yes" ; then | ||
129 | + echo "CONFIG_CLOOP=y" >> $config_host_mak | ||
130 | +fi | ||
131 | +if test "$dmg" = "yes" ; then | ||
132 | + echo "CONFIG_DMG=y" >> $config_host_mak | ||
133 | +fi | ||
134 | +if test "$qcow1" = "yes" ; then | ||
135 | + echo "CONFIG_QCOW1=y" >> $config_host_mak | ||
136 | +fi | ||
137 | +if test "$vdi" = "yes" ; then | ||
138 | + echo "CONFIG_VDI=y" >> $config_host_mak | ||
139 | +fi | ||
140 | +if test "$vvfat" = "yes" ; then | ||
141 | + echo "CONFIG_VVFAT=y" >> $config_host_mak | ||
142 | +fi | ||
143 | +if test "$qed" = "yes" ; then | ||
144 | + echo "CONFIG_QED=y" >> $config_host_mak | ||
145 | +fi | ||
146 | +if test "$parallels" = "yes" ; then | ||
147 | + echo "CONFIG_PARALLELS=y" >> $config_host_mak | ||
148 | +fi | ||
149 | +if test "$sheepdog" = "yes" ; then | ||
150 | + echo "CONFIG_SHEEPDOG=y" >> $config_host_mak | ||
151 | +fi | ||
152 | + | ||
153 | if test "$tcg_interpreter" = "yes"; then | ||
154 | QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES" | ||
155 | elif test "$ARCH" = "sparc64" ; then | ||
156 | diff --git a/block/Makefile.objs b/block/Makefile.objs | ||
157 | index XXXXXXX..XXXXXXX 100644 | ||
158 | --- a/block/Makefile.objs | ||
159 | +++ b/block/Makefile.objs | ||
160 | @@ -XXX,XX +XXX,XX @@ | ||
161 | -block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o | ||
162 | +block-obj-y += raw-format.o vmdk.o vpc.o | ||
163 | +block-obj-$(CONFIG_QCOW1) += qcow.o | ||
164 | +block-obj-$(CONFIG_VDI) += vdi.o | ||
165 | +block-obj-$(CONFIG_CLOOP) += cloop.o | ||
166 | +block-obj-$(CONFIG_BOCHS) += bochs.o | ||
167 | +block-obj-$(CONFIG_VVFAT) += vvfat.o | ||
168 | +block-obj-$(CONFIG_DMG) += dmg.o | ||
169 | + | ||
170 | block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o | ||
171 | -block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o | ||
172 | -block-obj-y += qed-check.o | ||
173 | +block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o | ||
174 | +block-obj-$(CONFIG_QED) += qed-check.o | ||
175 | block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o | ||
176 | block-obj-y += quorum.o | ||
177 | -block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o | ||
178 | +block-obj-y += blkdebug.o blkverify.o blkreplay.o | ||
179 | +block-obj-$(CONFIG_PARALLELS) += parallels.o | ||
180 | block-obj-y += blklogwrites.o | ||
181 | block-obj-y += block-backend.o snapshot.o qapi.o | ||
182 | block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o | ||
183 | @@ -XXX,XX +XXX,XX @@ block-obj-y += null.o mirror.o commit.o io.o create.o | ||
184 | block-obj-y += throttle-groups.o | ||
185 | block-obj-$(CONFIG_LINUX) += nvme.o | ||
186 | |||
187 | -block-obj-y += nbd.o nbd-client.o sheepdog.o | ||
188 | +block-obj-y += nbd.o nbd-client.o | ||
189 | +block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o | ||
190 | block-obj-$(CONFIG_LIBISCSI) += iscsi.o | ||
191 | block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o | ||
192 | block-obj-$(CONFIG_LIBNFS) += nfs.o | ||
193 | @@ -XXX,XX +XXX,XX @@ gluster.o-libs := $(GLUSTERFS_LIBS) | ||
194 | vxhs.o-libs := $(VXHS_LIBS) | ||
195 | ssh.o-cflags := $(LIBSSH2_CFLAGS) | ||
196 | ssh.o-libs := $(LIBSSH2_LIBS) | ||
197 | -block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o | ||
198 | +block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o | ||
199 | +block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y) | ||
200 | dmg-bz2.o-libs := $(BZIP2_LIBS) | ||
201 | qcow.o-libs := -lz | ||
202 | linux-aio.o-libs := -laio | ||
203 | -- | ||
204 | 2.19.1 | ||
205 | |||
206 | diff view generated by jsdifflib |