1
The following changes since commit 7bc8f9734213b76e76631a483be13d6737c2adbc:
1
The following changes since commit fe8d2d5737ab20ed0118863f5eb888cae37122ab:
2
2
3
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20191025' into staging (2019-10-25 13:12:16 +0100)
3
Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-3.0-pull-request' into staging (2018-07-04 22:38:10 +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 5e9785505210e2477e590e61b1ab100d0ec22b01:
9
for you to fetch changes up to 7c20c808a5cbf5d244735bc78fc3138c739c1946:
10
10
11
qcow2: Fix corruption bug in qcow2_detect_metadata_preallocation() (2019-10-25 15:18:55 +0200)
11
file-posix: Unlock FD after creation (2018-07-05 11:07:58 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches:
15
15
16
- qcow2: Fix data corruption bug that is triggered in partial cluster
16
- qcow2: Use worker threads for compression to improve performance of
17
allocation with default options
17
'qemu-img convert -W' and compressed backup jobs
18
- qapi: add support for blkreplay driver
18
- blklogwrites: New filter driver to log write requests to an image in
19
- doc: Describe missing generic -blockdev options
19
the dm-log-writes format
20
- iotests: Fix 118 when run as root
20
- file-posix: Fix image locking during image creation
21
- Minor code cleanups
21
- crypto: Fix memory leak in error path
22
- Error out instead of silently truncating node names
22
23
23
----------------------------------------------------------------
24
----------------------------------------------------------------
24
Kevin Wolf (5):
25
Aapo Vienamo (1):
25
iotests: Skip read-only cases in 118 when run as root
26
block: Add blklogwrites
26
blockdev: Use error_report() in hmp_commit()
27
doc: Describe missing generic -blockdev options
28
coroutine: Add qemu_co_mutex_assert_locked()
29
qcow2: Fix corruption bug in qcow2_detect_metadata_preallocation()
30
27
31
Pavel Dovgaluk (1):
28
Ari Sundholm (4):
32
qapi: add support for blkreplay driver
29
block: Move two block permission constants to the relevant enum
30
block/blklogwrites: Change log_sector_size from int64_t to uint64_t
31
block/blklogwrites: Add an option for appending to an old log
32
block/blklogwrites: Add an option for the update interval of the log superblock
33
33
34
Vladimir Sementsov-Ogievskiy (1):
34
Kevin Wolf (2):
35
block/backup: drop dead code from backup_job_create
35
block: Don't silently truncate node names
36
block/crypto: Fix memory leak in create error path
36
37
37
qapi/block-core.json | 18 ++++++++++++++++--
38
Max Reitz (2):
38
include/qemu/coroutine.h | 15 +++++++++++++++
39
file-posix: Fix creation locking
39
block/backup.c | 5 +----
40
file-posix: Unlock FD after creation
40
block/qcow2-refcount.c | 2 ++
41
block/qcow2.c | 3 ++-
42
blockdev.c | 7 +++----
43
qemu-options.hx | 22 +++++++++++++++++++++-
44
tests/qemu-iotests/118 | 3 +++
45
tests/qemu-iotests/iotests.py | 10 ++++++++++
46
9 files changed, 73 insertions(+), 12 deletions(-)
47
41
42
Vladimir Sementsov-Ogievskiy (3):
43
qemu-img: allow compressed not-in-order writes
44
qcow2: refactor data compression
45
qcow2: add compress threads
48
46
47
qapi/block-core.json | 38 ++-
48
block/qcow2.h | 3 +
49
include/block/block.h | 7 +
50
block.c | 12 +-
51
block/blklogwrites.c | 547 ++++++++++++++++++++++++++++++++++++++++++
52
block/crypto.c | 2 +-
53
block/file-posix.c | 21 +-
54
block/qcow2.c | 138 ++++++++---
55
qemu-img.c | 5 -
56
MAINTAINERS | 6 +
57
block/Makefile.objs | 1 +
58
tests/qemu-iotests/051 | 15 ++
59
tests/qemu-iotests/051.out | 23 ++
60
tests/qemu-iotests/051.pc.out | 23 ++
61
14 files changed, 791 insertions(+), 50 deletions(-)
62
create mode 100644 block/blklogwrites.c
63
diff view generated by jsdifflib
New patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
2
3
No reason to forbid them, and they are needed to improve performance
4
with compress-threads in further patches.
5
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
9
qemu-img.c | 5 -----
10
1 file changed, 5 deletions(-)
11
12
diff --git a/qemu-img.c b/qemu-img.c
13
index XXXXXXX..XXXXXXX 100644
14
--- a/qemu-img.c
15
+++ b/qemu-img.c
16
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
17
goto fail_getopt;
18
}
19
20
- if (!s.wr_in_order && s.compressed) {
21
- error_report("Out of order write and compress are mutually exclusive");
22
- goto fail_getopt;
23
- }
24
-
25
if (tgt_image_opts && !skip_create) {
26
error_report("--target-image-opts requires use of -n flag");
27
goto fail_getopt;
28
--
29
2.13.6
30
31
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
After commit 00e30f05de1d195, there is no more "goto error" points
3
Make a separate function for compression to be parallelized later.
4
after job creation, so after "error:" @job is always NULL and we don't
4
- use .avail_out field instead of .next_out to calculate size of
5
need roll-back job creation.
5
compressed data. It looks more natural and it allows to keep dest to
6
be void pointer
7
- set avail_out to be at least one byte less than input, to be sure
8
avoid inefficient compression earlier
6
9
7
Reported-by: Coverity (CID 1406402)
8
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
---
12
block/backup.c | 5 +----
13
block/qcow2.c | 78 ++++++++++++++++++++++++++++++++++++++---------------------
13
1 file changed, 1 insertion(+), 4 deletions(-)
14
1 file changed, 51 insertions(+), 27 deletions(-)
14
15
15
diff --git a/block/backup.c b/block/backup.c
16
diff --git a/block/qcow2.c b/block/qcow2.c
16
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
17
--- a/block/backup.c
18
--- a/block/qcow2.c
18
+++ b/block/backup.c
19
+++ b/block/qcow2.c
19
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
20
@@ -XXX,XX +XXX,XX @@
20
if (sync_bitmap) {
21
*/
21
bdrv_reclaim_dirty_bitmap(sync_bitmap, NULL);
22
22
}
23
#include "qemu/osdep.h"
23
- if (job) {
24
+
24
- backup_clean(&job->common.job);
25
+#define ZLIB_CONST
25
- job_early_fail(&job->common.job);
26
+#include <zlib.h>
26
- } else if (backup_top) {
27
+
27
+ if (backup_top) {
28
#include "block/block_int.h"
28
bdrv_backup_top_drop(backup_top);
29
#include "block/qdict.h"
29
}
30
#include "sysemu/block-backend.h"
30
31
#include "qemu/module.h"
32
-#include <zlib.h>
33
#include "qcow2.h"
34
#include "qemu/error-report.h"
35
#include "qapi/error.h"
36
@@ -XXX,XX +XXX,XX @@ fail:
37
return ret;
38
}
39
40
+/*
41
+ * qcow2_compress()
42
+ *
43
+ * @dest - destination buffer, at least of @size-1 bytes
44
+ * @src - source buffer, @size bytes
45
+ *
46
+ * Returns: compressed size on success
47
+ * -1 if compression is inefficient
48
+ * -2 on any other error
49
+ */
50
+static ssize_t qcow2_compress(void *dest, const void *src, size_t size)
51
+{
52
+ ssize_t ret;
53
+ z_stream strm;
54
+
55
+ /* best compression, small window, no zlib header */
56
+ memset(&strm, 0, sizeof(strm));
57
+ ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
58
+ -12, 9, Z_DEFAULT_STRATEGY);
59
+ if (ret != 0) {
60
+ return -2;
61
+ }
62
+
63
+ /* strm.next_in is not const in old zlib versions, such as those used on
64
+ * OpenBSD/NetBSD, so cast the const away */
65
+ strm.avail_in = size;
66
+ strm.next_in = (void *) src;
67
+ strm.avail_out = size - 1;
68
+ strm.next_out = dest;
69
+
70
+ ret = deflate(&strm, Z_FINISH);
71
+ if (ret == Z_STREAM_END) {
72
+ ret = size - 1 - strm.avail_out;
73
+ } else {
74
+ ret = (ret == Z_OK ? -1 : -2);
75
+ }
76
+
77
+ deflateEnd(&strm);
78
+
79
+ return ret;
80
+}
81
+
82
/* XXX: put compressed sectors first, then all the cluster aligned
83
tables to avoid losing bytes in alignment */
84
static coroutine_fn int
85
@@ -XXX,XX +XXX,XX @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
86
BDRVQcow2State *s = bs->opaque;
87
QEMUIOVector hd_qiov;
88
struct iovec iov;
89
- z_stream strm;
90
- int ret, out_len;
91
+ int ret;
92
+ size_t out_len;
93
uint8_t *buf, *out_buf;
94
int64_t cluster_offset;
95
96
@@ -XXX,XX +XXX,XX @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
97
98
out_buf = g_malloc(s->cluster_size);
99
100
- /* best compression, small window, no zlib header */
101
- memset(&strm, 0, sizeof(strm));
102
- ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
103
- Z_DEFLATED, -12,
104
- 9, Z_DEFAULT_STRATEGY);
105
- if (ret != 0) {
106
+ out_len = qcow2_compress(out_buf, buf, s->cluster_size);
107
+ if (out_len == -2) {
108
ret = -EINVAL;
109
goto fail;
110
- }
111
-
112
- strm.avail_in = s->cluster_size;
113
- strm.next_in = (uint8_t *)buf;
114
- strm.avail_out = s->cluster_size;
115
- strm.next_out = out_buf;
116
-
117
- ret = deflate(&strm, Z_FINISH);
118
- if (ret != Z_STREAM_END && ret != Z_OK) {
119
- deflateEnd(&strm);
120
- ret = -EINVAL;
121
- goto fail;
122
- }
123
- out_len = strm.next_out - out_buf;
124
-
125
- deflateEnd(&strm);
126
-
127
- if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
128
+ } else if (out_len == -1) {
129
/* could not compress: write normal cluster */
130
ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0);
131
if (ret < 0) {
31
--
132
--
32
2.20.1
133
2.13.6
33
134
34
135
diff view generated by jsdifflib
1
qcow2_detect_metadata_preallocation() calls qcow2_get_refcount() which
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
requires s->lock to be taken to protect its accesses to the refcount
3
table and refcount blocks. However, nothing in this code path actually
4
took the lock. This could cause the same cache entry to be used by two
5
requests at the same time, for different tables at different offsets,
6
resulting in image corruption.
7
2
8
As it would be preferable to base the detection on consistent data (even
3
Do data compression in separate threads. This significantly improve
9
though it's just heuristics), let's take the lock not only around the
4
performance for qemu-img convert with -W (allow async writes) and -c
10
qcow2_get_refcount() calls, but around the whole function.
5
(compressed) options.
11
6
12
This patch takes the lock in qcow2_co_block_status() earlier and asserts
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
in qcow2_detect_metadata_preallocation() that we hold the lock.
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
10
block/qcow2.h | 3 +++
11
block/qcow2.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
12
2 files changed, 64 insertions(+), 1 deletion(-)
14
13
15
Fixes: 69f47505ee66afaa513305de0c1895a224e52c45
14
diff --git a/block/qcow2.h b/block/qcow2.h
16
Cc: qemu-stable@nongnu.org
17
Reported-by: Michael Weiser <michael.weiser@gmx.de>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
Tested-by: Michael Weiser <michael.weiser@gmx.de>
20
Reviewed-by: Michael Weiser <michael.weiser@gmx.de>
21
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
22
Reviewed-by: Max Reitz <mreitz@redhat.com>
23
---
24
block/qcow2-refcount.c | 2 ++
25
block/qcow2.c | 3 ++-
26
2 files changed, 4 insertions(+), 1 deletion(-)
27
28
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
29
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
30
--- a/block/qcow2-refcount.c
16
--- a/block/qcow2.h
31
+++ b/block/qcow2-refcount.c
17
+++ b/block/qcow2.h
32
@@ -XXX,XX +XXX,XX @@ int qcow2_detect_metadata_preallocation(BlockDriverState *bs)
18
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVQcow2State {
33
int64_t i, end_cluster, cluster_count = 0, threshold;
19
* override) */
34
int64_t file_length, real_allocation, real_clusters;
20
char *image_backing_file;
35
21
char *image_backing_format;
36
+ qemu_co_mutex_assert_locked(&s->lock);
37
+
22
+
38
file_length = bdrv_getlength(bs->file->bs);
23
+ CoQueue compress_wait_queue;
39
if (file_length < 0) {
24
+ int nb_compress_threads;
40
return file_length;
25
} BDRVQcow2State;
26
27
typedef struct Qcow2COWRegion {
41
diff --git a/block/qcow2.c b/block/qcow2.c
28
diff --git a/block/qcow2.c b/block/qcow2.c
42
index XXXXXXX..XXXXXXX 100644
29
index XXXXXXX..XXXXXXX 100644
43
--- a/block/qcow2.c
30
--- a/block/qcow2.c
44
+++ b/block/qcow2.c
31
+++ b/block/qcow2.c
45
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
32
@@ -XXX,XX +XXX,XX @@
46
unsigned int bytes;
33
#include "qapi/qobject-input-visitor.h"
47
int status = 0;
34
#include "qapi/qapi-visit-block-core.h"
48
35
#include "crypto.h"
49
+ qemu_co_mutex_lock(&s->lock);
36
+#include "block/thread-pool.h"
37
38
/*
39
Differences with QCOW:
40
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
41
qcow2_check_refcounts(bs, &result, 0);
42
}
43
#endif
50
+
44
+
51
if (!s->metadata_preallocation_checked) {
45
+ qemu_co_queue_init(&s->compress_wait_queue);
52
ret = qcow2_detect_metadata_preallocation(bs);
46
+
53
s->metadata_preallocation = (ret == 1);
47
return ret;
54
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
48
55
}
49
fail:
56
50
@@ -XXX,XX +XXX,XX @@ static ssize_t qcow2_compress(void *dest, const void *src, size_t size)
57
bytes = MIN(INT_MAX, count);
51
return ret;
58
- qemu_co_mutex_lock(&s->lock);
52
}
59
ret = qcow2_get_cluster_offset(bs, offset, &bytes, &cluster_offset);
53
60
qemu_co_mutex_unlock(&s->lock);
54
+#define MAX_COMPRESS_THREADS 4
61
if (ret < 0) {
55
+
56
+typedef struct Qcow2CompressData {
57
+ void *dest;
58
+ const void *src;
59
+ size_t size;
60
+ ssize_t ret;
61
+} Qcow2CompressData;
62
+
63
+static int qcow2_compress_pool_func(void *opaque)
64
+{
65
+ Qcow2CompressData *data = opaque;
66
+
67
+ data->ret = qcow2_compress(data->dest, data->src, data->size);
68
+
69
+ return 0;
70
+}
71
+
72
+static void qcow2_compress_complete(void *opaque, int ret)
73
+{
74
+ qemu_coroutine_enter(opaque);
75
+}
76
+
77
+/* See qcow2_compress definition for parameters description */
78
+static ssize_t qcow2_co_compress(BlockDriverState *bs,
79
+ void *dest, const void *src, size_t size)
80
+{
81
+ BDRVQcow2State *s = bs->opaque;
82
+ BlockAIOCB *acb;
83
+ ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
84
+ Qcow2CompressData arg = {
85
+ .dest = dest,
86
+ .src = src,
87
+ .size = size,
88
+ };
89
+
90
+ while (s->nb_compress_threads >= MAX_COMPRESS_THREADS) {
91
+ qemu_co_queue_wait(&s->compress_wait_queue, NULL);
92
+ }
93
+
94
+ s->nb_compress_threads++;
95
+ acb = thread_pool_submit_aio(pool, qcow2_compress_pool_func, &arg,
96
+ qcow2_compress_complete,
97
+ qemu_coroutine_self());
98
+
99
+ if (!acb) {
100
+ s->nb_compress_threads--;
101
+ return -EINVAL;
102
+ }
103
+ qemu_coroutine_yield();
104
+ s->nb_compress_threads--;
105
+ qemu_co_queue_next(&s->compress_wait_queue);
106
+
107
+ return arg.ret;
108
+}
109
+
110
/* XXX: put compressed sectors first, then all the cluster aligned
111
tables to avoid losing bytes in alignment */
112
static coroutine_fn int
113
@@ -XXX,XX +XXX,XX @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
114
115
out_buf = g_malloc(s->cluster_size);
116
117
- out_len = qcow2_compress(out_buf, buf, s->cluster_size);
118
+ out_len = qcow2_co_compress(bs, out_buf, buf, s->cluster_size);
119
if (out_len == -2) {
120
ret = -EINVAL;
121
goto fail;
62
--
122
--
63
2.20.1
123
2.13.6
64
124
65
125
diff view generated by jsdifflib
New patch
1
From: Ari Sundholm <ari@tuxera.com>
1
2
3
This allows using the two constants outside of block.c, which will
4
happen in a subsequent patch.
5
6
Signed-off-by: Ari Sundholm <ari@tuxera.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
9
include/block/block.h | 7 +++++++
10
block.c | 6 ------
11
2 files changed, 7 insertions(+), 6 deletions(-)
12
13
diff --git a/include/block/block.h b/include/block/block.h
14
index XXXXXXX..XXXXXXX 100644
15
--- a/include/block/block.h
16
+++ b/include/block/block.h
17
@@ -XXX,XX +XXX,XX @@ enum {
18
BLK_PERM_GRAPH_MOD = 0x10,
19
20
BLK_PERM_ALL = 0x1f,
21
+
22
+ DEFAULT_PERM_PASSTHROUGH = BLK_PERM_CONSISTENT_READ
23
+ | BLK_PERM_WRITE
24
+ | BLK_PERM_WRITE_UNCHANGED
25
+ | BLK_PERM_RESIZE,
26
+
27
+ DEFAULT_PERM_UNCHANGED = BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH,
28
};
29
30
char *bdrv_perm_names(uint64_t perm);
31
diff --git a/block.c b/block.c
32
index XXXXXXX..XXXXXXX 100644
33
--- a/block.c
34
+++ b/block.c
35
@@ -XXX,XX +XXX,XX @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
36
return 0;
37
}
38
39
-#define DEFAULT_PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
40
- | BLK_PERM_WRITE \
41
- | BLK_PERM_WRITE_UNCHANGED \
42
- | BLK_PERM_RESIZE)
43
-#define DEFAULT_PERM_UNCHANGED (BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH)
44
-
45
void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
46
const BdrvChildRole *role,
47
BlockReopenQueue *reopen_queue,
48
--
49
2.13.6
50
51
diff view generated by jsdifflib
1
From: Pavel Dovgalyuk <pavel.dovgaluk@gmail.com>
1
From: Aapo Vienamo <aapo@tuxera.com>
2
2
3
This patch adds support for blkreplay driver to the blockdev options.
3
Implements a block device write logging system, similar to Linux kernel
4
Now blkreplay can be used with -blockdev command line option
4
device mapper dm-log-writes. The write operations that are performed
5
in the following format:
5
on a block device are logged to a file or another block device. The
6
-blockdev driver=blkreplay,image=file-node-name,node-name=replay-node-name
6
write log format is identical to the dm-log-writes format. Currently,
7
7
log markers are not supported.
8
This option makes possible implementation of the better command
8
9
line support for record/replay invocations.
9
This functionality can be used for crash consistency and fs consistency
10
10
testing. By implementing it in qemu, tests utilizing write logs can be
11
Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru>
11
be used to test non-Linux drivers and older kernels.
12
13
The driver accepts an optional parameter to set the sector size used
14
for logging. This makes the driver require all requests to be aligned
15
to this sector size and also makes offsets and sizes of writes in the
16
log metadata to be expressed in terms of this value (the log format has
17
a granularity of one sector for offsets and sizes). This allows
18
accurate logging of writes to guest block devices that have unusual
19
sector sizes.
20
21
The implementation is based on the blkverify and blkdebug block
22
drivers.
23
24
Signed-off-by: Aapo Vienamo <aapo@tuxera.com>
25
Signed-off-by: Ari Sundholm <ari@tuxera.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
---
27
---
14
qapi/block-core.json | 18 ++++++++++++++++--
28
qapi/block-core.json | 33 +++-
15
1 file changed, 16 insertions(+), 2 deletions(-)
29
block/blklogwrites.c | 414 +++++++++++++++++++++++++++++++++++++++++++++++++++
30
MAINTAINERS | 6 +
31
block/Makefile.objs | 1 +
32
4 files changed, 448 insertions(+), 6 deletions(-)
33
create mode 100644 block/blklogwrites.c
16
34
17
diff --git a/qapi/block-core.json b/qapi/block-core.json
35
diff --git a/qapi/block-core.json b/qapi/block-core.json
18
index XXXXXXX..XXXXXXX 100644
36
index XXXXXXX..XXXXXXX 100644
19
--- a/qapi/block-core.json
37
--- a/qapi/block-core.json
20
+++ b/qapi/block-core.json
38
+++ b/qapi/block-core.json
21
@@ -XXX,XX +XXX,XX @@
39
@@ -XXX,XX +XXX,XX @@
40
# @throttle: Since 2.11
22
# @nvme: Since 2.12
41
# @nvme: Since 2.12
23
# @copy-on-read: Since 3.0
42
# @copy-on-read: Since 3.0
24
# @blklogwrites: Since 3.0
43
+# @blklogwrites: Since 3.0
25
+# @blkreplay: Since 4.2
26
#
44
#
27
# Since: 2.9
45
# Since: 2.9
28
##
46
##
29
{ 'enum': 'BlockdevDriver',
47
{ 'enum': 'BlockdevDriver',
30
- 'data': [ 'blkdebug', 'blklogwrites', 'blkverify', 'bochs', 'cloop',
48
- 'data': [ 'blkdebug', 'blkverify', 'bochs', 'cloop', 'copy-on-read',
31
- 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
49
- 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom',
32
+ 'data': [ 'blkdebug', 'blklogwrites', 'blkreplay', 'blkverify', 'bochs',
50
- 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
33
+ 'cloop', 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
51
- 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow', 'qcow2', 'qed',
34
'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks',
52
- 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
35
'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow',
53
- 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
36
'qcow2', 'qed', 'quorum', 'raw', 'rbd',
54
+ 'data': [ 'blkdebug', 'blklogwrites', 'blkverify', 'bochs', 'cloop',
55
+ 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
56
+ 'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks',
57
+ 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow',
58
+ 'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog',
59
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
60
61
##
62
# @BlockdevOptionsFile:
37
@@ -XXX,XX +XXX,XX @@
63
@@ -XXX,XX +XXX,XX @@
38
'data': { 'test': 'BlockdevRef',
64
'*set-state': ['BlkdebugSetStateOptions'] } }
39
'raw': 'BlockdevRef' } }
65
40
66
##
67
+# @BlockdevOptionsBlklogwrites:
68
+#
69
+# Driver specific block device options for blklogwrites.
70
+#
71
+# @file: block device
72
+#
73
+# @log: block device used to log writes to @file
74
+#
75
+# @log-sector-size: sector size used in logging writes to @file, determines
76
+# granularity of offsets and sizes of writes (default: 512)
77
+#
78
+# Since: 3.0
41
+##
79
+##
42
+# @BlockdevOptionsBlkreplay:
80
+{ 'struct': 'BlockdevOptionsBlklogwrites',
43
+#
81
+ 'data': { 'file': 'BlockdevRef',
44
+# Driver specific block device options for blkreplay.
82
+ 'log': 'BlockdevRef',
45
+#
83
+ '*log-sector-size': 'uint32' } }
46
+# @image: disk image which should be controlled with blkreplay
84
+
47
+#
48
+# Since: 4.2
49
+##
85
+##
50
+{ 'struct': 'BlockdevOptionsBlkreplay',
86
# @BlockdevOptionsBlkverify:
51
+ 'data': { 'image': 'BlockdevRef' } }
52
+
53
##
54
# @QuorumReadPattern:
55
#
87
#
88
# Driver specific block device options for blkverify.
56
@@ -XXX,XX +XXX,XX @@
89
@@ -XXX,XX +XXX,XX @@
90
'discriminator': 'driver',
91
'data': {
57
'blkdebug': 'BlockdevOptionsBlkdebug',
92
'blkdebug': 'BlockdevOptionsBlkdebug',
58
'blklogwrites':'BlockdevOptionsBlklogwrites',
93
+ 'blklogwrites':'BlockdevOptionsBlklogwrites',
59
'blkverify': 'BlockdevOptionsBlkverify',
94
'blkverify': 'BlockdevOptionsBlkverify',
60
+ 'blkreplay': 'BlockdevOptionsBlkreplay',
61
'bochs': 'BlockdevOptionsGenericFormat',
95
'bochs': 'BlockdevOptionsGenericFormat',
62
'cloop': 'BlockdevOptionsGenericFormat',
96
'cloop': 'BlockdevOptionsGenericFormat',
63
'copy-on-read':'BlockdevOptionsGenericFormat',
97
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
98
new file mode 100644
99
index XXXXXXX..XXXXXXX
100
--- /dev/null
101
+++ b/block/blklogwrites.c
102
@@ -XXX,XX +XXX,XX @@
103
+/*
104
+ * Write logging blk driver based on blkverify and blkdebug.
105
+ *
106
+ * Copyright (c) 2017 Tuomas Tynkkynen <tuomas@tuxera.com>
107
+ * Copyright (c) 2018 Aapo Vienamo <aapo@tuxera.com>
108
+ * Copyright (c) 2018 Ari Sundholm <ari@tuxera.com>
109
+ *
110
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
111
+ * See the COPYING file in the top-level directory.
112
+ */
113
+
114
+#include "qemu/osdep.h"
115
+#include "qapi/error.h"
116
+#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
117
+#include "block/block_int.h"
118
+#include "qapi/qmp/qdict.h"
119
+#include "qapi/qmp/qstring.h"
120
+#include "qemu/cutils.h"
121
+#include "qemu/option.h"
122
+
123
+/* Disk format stuff - taken from Linux drivers/md/dm-log-writes.c */
124
+
125
+#define LOG_FLUSH_FLAG (1 << 0)
126
+#define LOG_FUA_FLAG (1 << 1)
127
+#define LOG_DISCARD_FLAG (1 << 2)
128
+#define LOG_MARK_FLAG (1 << 3)
129
+
130
+#define WRITE_LOG_VERSION 1ULL
131
+#define WRITE_LOG_MAGIC 0x6a736677736872ULL
132
+
133
+/* All fields are little-endian. */
134
+struct log_write_super {
135
+ uint64_t magic;
136
+ uint64_t version;
137
+ uint64_t nr_entries;
138
+ uint32_t sectorsize;
139
+} QEMU_PACKED;
140
+
141
+struct log_write_entry {
142
+ uint64_t sector;
143
+ uint64_t nr_sectors;
144
+ uint64_t flags;
145
+ uint64_t data_len;
146
+} QEMU_PACKED;
147
+
148
+/* End of disk format structures. */
149
+
150
+typedef struct {
151
+ BdrvChild *log_file;
152
+ uint32_t sectorsize;
153
+ uint32_t sectorbits;
154
+ uint64_t cur_log_sector;
155
+ uint64_t nr_entries;
156
+} BDRVBlkLogWritesState;
157
+
158
+static QemuOptsList runtime_opts = {
159
+ .name = "blklogwrites",
160
+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
161
+ .desc = {
162
+ {
163
+ .name = "log-sector-size",
164
+ .type = QEMU_OPT_SIZE,
165
+ .help = "Log sector size",
166
+ },
167
+ { /* end of list */ }
168
+ },
169
+};
170
+
171
+static inline uint32_t blk_log_writes_log2(uint32_t value)
172
+{
173
+ assert(value > 0);
174
+ return 31 - clz32(value);
175
+}
176
+
177
+static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
178
+ Error **errp)
179
+{
180
+ BDRVBlkLogWritesState *s = bs->opaque;
181
+ QemuOpts *opts;
182
+ Error *local_err = NULL;
183
+ int ret;
184
+ int64_t log_sector_size;
185
+
186
+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
187
+ qemu_opts_absorb_qdict(opts, options, &local_err);
188
+ if (local_err) {
189
+ ret = -EINVAL;
190
+ error_propagate(errp, local_err);
191
+ goto fail;
192
+ }
193
+
194
+ /* Open the file */
195
+ bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
196
+ &local_err);
197
+ if (local_err) {
198
+ ret = -EINVAL;
199
+ error_propagate(errp, local_err);
200
+ goto fail;
201
+ }
202
+
203
+ log_sector_size = qemu_opt_get_size(opts, "log-sector-size",
204
+ BDRV_SECTOR_SIZE);
205
+
206
+ if (log_sector_size < 0 || log_sector_size > (1ull << 23) ||
207
+ !is_power_of_2(log_sector_size))
208
+ {
209
+ ret = -EINVAL;
210
+ error_setg(errp, "Invalid log sector size %"PRId64, log_sector_size);
211
+ goto fail;
212
+ }
213
+
214
+ s->sectorsize = log_sector_size;
215
+ s->sectorbits = blk_log_writes_log2(log_sector_size);
216
+ s->cur_log_sector = 1;
217
+ s->nr_entries = 0;
218
+
219
+ /* Open the log file */
220
+ s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false,
221
+ &local_err);
222
+ if (local_err) {
223
+ ret = -EINVAL;
224
+ error_propagate(errp, local_err);
225
+ goto fail;
226
+ }
227
+
228
+ ret = 0;
229
+fail:
230
+ if (ret < 0) {
231
+ bdrv_unref_child(bs, bs->file);
232
+ bs->file = NULL;
233
+ }
234
+ qemu_opts_del(opts);
235
+ return ret;
236
+}
237
+
238
+static void blk_log_writes_close(BlockDriverState *bs)
239
+{
240
+ BDRVBlkLogWritesState *s = bs->opaque;
241
+
242
+ bdrv_unref_child(bs, s->log_file);
243
+ s->log_file = NULL;
244
+}
245
+
246
+static int64_t blk_log_writes_getlength(BlockDriverState *bs)
247
+{
248
+ return bdrv_getlength(bs->file->bs);
249
+}
250
+
251
+static void blk_log_writes_refresh_filename(BlockDriverState *bs,
252
+ QDict *options)
253
+{
254
+ BDRVBlkLogWritesState *s = bs->opaque;
255
+
256
+ /* bs->file->bs has already been refreshed */
257
+ bdrv_refresh_filename(s->log_file->bs);
258
+
259
+ if (bs->file->bs->full_open_options
260
+ && s->log_file->bs->full_open_options)
261
+ {
262
+ QDict *opts = qdict_new();
263
+ qdict_put_str(opts, "driver", "blklogwrites");
264
+
265
+ qobject_ref(bs->file->bs->full_open_options);
266
+ qdict_put_obj(opts, "file", QOBJECT(bs->file->bs->full_open_options));
267
+ qobject_ref(s->log_file->bs->full_open_options);
268
+ qdict_put_obj(opts, "log",
269
+ QOBJECT(s->log_file->bs->full_open_options));
270
+ qdict_put_int(opts, "log-sector-size", s->sectorsize);
271
+
272
+ bs->full_open_options = opts;
273
+ }
274
+}
275
+
276
+static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
277
+ const BdrvChildRole *role,
278
+ BlockReopenQueue *ro_q,
279
+ uint64_t perm, uint64_t shrd,
280
+ uint64_t *nperm, uint64_t *nshrd)
281
+{
282
+ if (!c) {
283
+ *nperm = perm & DEFAULT_PERM_PASSTHROUGH;
284
+ *nshrd = (shrd & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED;
285
+ return;
286
+ }
287
+
288
+ if (!strcmp(c->name, "log")) {
289
+ bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
290
+ } else {
291
+ bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
292
+ }
293
+}
294
+
295
+static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp)
296
+{
297
+ BDRVBlkLogWritesState *s = bs->opaque;
298
+ bs->bl.request_alignment = s->sectorsize;
299
+}
300
+
301
+static int coroutine_fn
302
+blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
303
+ QEMUIOVector *qiov, int flags)
304
+{
305
+ return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
306
+}
307
+
308
+typedef struct BlkLogWritesFileReq {
309
+ BlockDriverState *bs;
310
+ uint64_t offset;
311
+ uint64_t bytes;
312
+ int file_flags;
313
+ QEMUIOVector *qiov;
314
+ int (*func)(struct BlkLogWritesFileReq *r);
315
+ int file_ret;
316
+} BlkLogWritesFileReq;
317
+
318
+typedef struct {
319
+ BlockDriverState *bs;
320
+ QEMUIOVector *qiov;
321
+ struct log_write_entry entry;
322
+ uint64_t zero_size;
323
+ int log_ret;
324
+} BlkLogWritesLogReq;
325
+
326
+static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr)
327
+{
328
+ BDRVBlkLogWritesState *s = lr->bs->opaque;
329
+ uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits;
330
+
331
+ s->nr_entries++;
332
+ s->cur_log_sector +=
333
+ ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits;
334
+
335
+ lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size,
336
+ lr->qiov, 0);
337
+
338
+ /* Logging for the "write zeroes" operation */
339
+ if (lr->log_ret == 0 && lr->zero_size) {
340
+ cur_log_offset = s->cur_log_sector << s->sectorbits;
341
+ s->cur_log_sector +=
342
+ ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits;
343
+
344
+ lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset,
345
+ lr->zero_size, 0);
346
+ }
347
+
348
+ /* Update super block on flush */
349
+ if (lr->log_ret == 0 && lr->entry.flags & LOG_FLUSH_FLAG) {
350
+ struct log_write_super super = {
351
+ .magic = cpu_to_le64(WRITE_LOG_MAGIC),
352
+ .version = cpu_to_le64(WRITE_LOG_VERSION),
353
+ .nr_entries = cpu_to_le64(s->nr_entries),
354
+ .sectorsize = cpu_to_le32(s->sectorsize),
355
+ };
356
+ void *zeroes = g_malloc0(s->sectorsize - sizeof(super));
357
+ QEMUIOVector qiov;
358
+
359
+ qemu_iovec_init(&qiov, 2);
360
+ qemu_iovec_add(&qiov, &super, sizeof(super));
361
+ qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super));
362
+
363
+ lr->log_ret =
364
+ bdrv_co_pwritev(s->log_file, 0, s->sectorsize, &qiov, 0);
365
+ if (lr->log_ret == 0) {
366
+ lr->log_ret = bdrv_co_flush(s->log_file->bs);
367
+ }
368
+ qemu_iovec_destroy(&qiov);
369
+ g_free(zeroes);
370
+ }
371
+}
372
+
373
+static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr)
374
+{
375
+ fr->file_ret = fr->func(fr);
376
+}
377
+
378
+static int coroutine_fn
379
+blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
380
+ QEMUIOVector *qiov, int flags,
381
+ int (*file_func)(BlkLogWritesFileReq *r),
382
+ uint64_t entry_flags, bool is_zero_write)
383
+{
384
+ QEMUIOVector log_qiov;
385
+ size_t niov = qiov ? qiov->niov : 0;
386
+ BDRVBlkLogWritesState *s = bs->opaque;
387
+ BlkLogWritesFileReq fr = {
388
+ .bs = bs,
389
+ .offset = offset,
390
+ .bytes = bytes,
391
+ .file_flags = flags,
392
+ .qiov = qiov,
393
+ .func = file_func,
394
+ };
395
+ BlkLogWritesLogReq lr = {
396
+ .bs = bs,
397
+ .qiov = &log_qiov,
398
+ .entry = {
399
+ .sector = cpu_to_le64(offset >> s->sectorbits),
400
+ .nr_sectors = cpu_to_le64(bytes >> s->sectorbits),
401
+ .flags = cpu_to_le64(entry_flags),
402
+ .data_len = 0,
403
+ },
404
+ .zero_size = is_zero_write ? bytes : 0,
405
+ };
406
+ void *zeroes = g_malloc0(s->sectorsize - sizeof(lr.entry));
407
+
408
+ assert((1 << s->sectorbits) == s->sectorsize);
409
+ assert(bs->bl.request_alignment == s->sectorsize);
410
+ assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
411
+ assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
412
+
413
+ qemu_iovec_init(&log_qiov, niov + 2);
414
+ qemu_iovec_add(&log_qiov, &lr.entry, sizeof(lr.entry));
415
+ qemu_iovec_add(&log_qiov, zeroes, s->sectorsize - sizeof(lr.entry));
416
+ if (qiov) {
417
+ qemu_iovec_concat(&log_qiov, qiov, 0, qiov->size);
418
+ }
419
+
420
+ blk_log_writes_co_do_file(&fr);
421
+ blk_log_writes_co_do_log(&lr);
422
+
423
+ qemu_iovec_destroy(&log_qiov);
424
+ g_free(zeroes);
425
+
426
+ if (lr.log_ret < 0) {
427
+ return lr.log_ret;
428
+ }
429
+
430
+ return fr.file_ret;
431
+}
432
+
433
+static int coroutine_fn
434
+blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr)
435
+{
436
+ return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes,
437
+ fr->qiov, fr->file_flags);
438
+}
439
+
440
+static int coroutine_fn
441
+blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr)
442
+{
443
+ return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes,
444
+ fr->file_flags);
445
+}
446
+
447
+static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr)
448
+{
449
+ return bdrv_co_flush(fr->bs->file->bs);
450
+}
451
+
452
+static int coroutine_fn
453
+blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr)
454
+{
455
+ return bdrv_co_pdiscard(fr->bs->file->bs, fr->offset, fr->bytes);
456
+}
457
+
458
+static int coroutine_fn
459
+blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
460
+ QEMUIOVector *qiov, int flags)
461
+{
462
+ return blk_log_writes_co_log(bs, offset, bytes, qiov, flags,
463
+ blk_log_writes_co_do_file_pwritev, 0, false);
464
+}
465
+
466
+static int coroutine_fn
467
+blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
468
+ BdrvRequestFlags flags)
469
+{
470
+ return blk_log_writes_co_log(bs, offset, bytes, NULL, flags,
471
+ blk_log_writes_co_do_file_pwrite_zeroes, 0,
472
+ true);
473
+}
474
+
475
+static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs)
476
+{
477
+ return blk_log_writes_co_log(bs, 0, 0, NULL, 0,
478
+ blk_log_writes_co_do_file_flush,
479
+ LOG_FLUSH_FLAG, false);
480
+}
481
+
482
+static int coroutine_fn
483
+blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
484
+{
485
+ return blk_log_writes_co_log(bs, offset, count, NULL, 0,
486
+ blk_log_writes_co_do_file_pdiscard,
487
+ LOG_DISCARD_FLAG, false);
488
+}
489
+
490
+static BlockDriver bdrv_blk_log_writes = {
491
+ .format_name = "blklogwrites",
492
+ .instance_size = sizeof(BDRVBlkLogWritesState),
493
+
494
+ .bdrv_open = blk_log_writes_open,
495
+ .bdrv_close = blk_log_writes_close,
496
+ .bdrv_getlength = blk_log_writes_getlength,
497
+ .bdrv_refresh_filename = blk_log_writes_refresh_filename,
498
+ .bdrv_child_perm = blk_log_writes_child_perm,
499
+ .bdrv_refresh_limits = blk_log_writes_refresh_limits,
500
+
501
+ .bdrv_co_preadv = blk_log_writes_co_preadv,
502
+ .bdrv_co_pwritev = blk_log_writes_co_pwritev,
503
+ .bdrv_co_pwrite_zeroes = blk_log_writes_co_pwrite_zeroes,
504
+ .bdrv_co_flush_to_disk = blk_log_writes_co_flush_to_disk,
505
+ .bdrv_co_pdiscard = blk_log_writes_co_pdiscard,
506
+ .bdrv_co_block_status = bdrv_co_block_status_from_file,
507
+
508
+ .is_filter = true,
509
+};
510
+
511
+static void bdrv_blk_log_writes_init(void)
512
+{
513
+ bdrv_register(&bdrv_blk_log_writes);
514
+}
515
+
516
+block_init(bdrv_blk_log_writes_init);
517
diff --git a/MAINTAINERS b/MAINTAINERS
518
index XXXXXXX..XXXXXXX 100644
519
--- a/MAINTAINERS
520
+++ b/MAINTAINERS
521
@@ -XXX,XX +XXX,XX @@ S: Supported
522
F: block/quorum.c
523
L: qemu-block@nongnu.org
524
525
+blklogwrites
526
+M: Ari Sundholm <ari@tuxera.com>
527
+L: qemu-block@nongnu.org
528
+S: Supported
529
+F: block/blklogwrites.c
530
+
531
blkverify
532
M: Stefan Hajnoczi <stefanha@redhat.com>
533
L: qemu-block@nongnu.org
534
diff --git a/block/Makefile.objs b/block/Makefile.objs
535
index XXXXXXX..XXXXXXX 100644
536
--- a/block/Makefile.objs
537
+++ b/block/Makefile.objs
538
@@ -XXX,XX +XXX,XX @@ block-obj-y += qed-check.o
539
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
540
block-obj-y += quorum.o
541
block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
542
+block-obj-y += blklogwrites.o
543
block-obj-y += block-backend.o snapshot.o qapi.o
544
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
545
block-obj-$(CONFIG_POSIX) += file-posix.o
64
--
546
--
65
2.20.1
547
2.13.6
66
548
67
549
diff view generated by jsdifflib
New patch
1
If the user passes a too long node name string, we silently truncate it
2
to fit into BlockDriverState.node_name, i.e. to 31 characters. Apart
3
from surprising the user when the node has a different name than
4
requested, this also bypasses the check for duplicate names, so that the
5
same name can be assigned to multiple nodes.
1
6
7
Fix this by just making too long node names an error.
8
9
Reported-by: Peter Krempa <pkrempa@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
12
block.c | 6 ++++++
13
tests/qemu-iotests/051 | 15 +++++++++++++++
14
tests/qemu-iotests/051.out | 23 +++++++++++++++++++++++
15
tests/qemu-iotests/051.pc.out | 23 +++++++++++++++++++++++
16
4 files changed, 67 insertions(+)
17
18
diff --git a/block.c b/block.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block.c
21
+++ b/block.c
22
@@ -XXX,XX +XXX,XX @@ static void bdrv_assign_node_name(BlockDriverState *bs,
23
goto out;
24
}
25
26
+ /* Make sure that the node name isn't truncated */
27
+ if (strlen(node_name) >= sizeof(bs->node_name)) {
28
+ error_setg(errp, "Node name too long");
29
+ goto out;
30
+ }
31
+
32
/* copy node name into the bs and insert it into the graph list */
33
pstrcpy(bs->node_name, sizeof(bs->node_name), node_name);
34
QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list);
35
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
36
index XXXXXXX..XXXXXXX 100755
37
--- a/tests/qemu-iotests/051
38
+++ b/tests/qemu-iotests/051
39
@@ -XXX,XX +XXX,XX @@ run_qemu -drive file="$TEST_IMG",driver=raw,format=qcow2
40
run_qemu -drive file="$TEST_IMG",driver=qcow2,format=qcow2
41
42
echo
43
+echo === Node names ===
44
+echo
45
+
46
+# Maximum length: 31 characters
47
+run_qemu -drive file="$TEST_IMG",node-name=x123456789012345678901234567890
48
+run_qemu -drive file="$TEST_IMG",node-name=x1234567890123456789012345678901
49
+
50
+# First character must be alphabetic
51
+# Following characters alphanumeric or -._
52
+run_qemu -drive file="$TEST_IMG",node-name=All-Types.of_all0wed_chars
53
+run_qemu -drive file="$TEST_IMG",node-name=123foo
54
+run_qemu -drive file="$TEST_IMG",node-name=_foo
55
+run_qemu -drive file="$TEST_IMG",node-name=foo#12
56
+
57
+echo
58
echo === Device without drive ===
59
echo
60
61
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
62
index XXXXXXX..XXXXXXX 100644
63
--- a/tests/qemu-iotests/051.out
64
+++ b/tests/qemu-iotests/051.out
65
@@ -XXX,XX +XXX,XX @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2
66
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format'
67
68
69
+=== Node names ===
70
+
71
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=x123456789012345678901234567890
72
+QEMU X.Y.Z monitor - type 'help' for more information
73
+(qemu) quit
74
+
75
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901
76
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901: Node name too long
77
+
78
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=All-Types.of_all0wed_chars
79
+QEMU X.Y.Z monitor - type 'help' for more information
80
+(qemu) quit
81
+
82
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=123foo
83
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=123foo: Invalid node name
84
+
85
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=_foo
86
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=_foo: Invalid node name
87
+
88
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=foo#12
89
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=foo#12: Invalid node name
90
+
91
+
92
=== Device without drive ===
93
94
Testing: -device VIRTIO_SCSI -device scsi-hd
95
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
96
index XXXXXXX..XXXXXXX 100644
97
--- a/tests/qemu-iotests/051.pc.out
98
+++ b/tests/qemu-iotests/051.pc.out
99
@@ -XXX,XX +XXX,XX @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2
100
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format'
101
102
103
+=== Node names ===
104
+
105
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=x123456789012345678901234567890
106
+QEMU X.Y.Z monitor - type 'help' for more information
107
+(qemu) quit
108
+
109
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901
110
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901: Node name too long
111
+
112
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=All-Types.of_all0wed_chars
113
+QEMU X.Y.Z monitor - type 'help' for more information
114
+(qemu) quit
115
+
116
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=123foo
117
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=123foo: Invalid node name
118
+
119
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=_foo
120
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=_foo: Invalid node name
121
+
122
+Testing: -drive file=TEST_DIR/t.qcow2,node-name=foo#12
123
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=foo#12: Invalid node name
124
+
125
+
126
=== Device without drive ===
127
128
Testing: -device VIRTIO_SCSI -device scsi-hd
129
--
130
2.13.6
131
132
diff view generated by jsdifflib
New patch
1
Fixes: Coverity CID 1393782
2
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
4
---
5
block/crypto.c | 2 +-
6
1 file changed, 1 insertion(+), 1 deletion(-)
1
7
8
diff --git a/block/crypto.c b/block/crypto.c
9
index XXXXXXX..XXXXXXX 100644
10
--- a/block/crypto.c
11
+++ b/block/crypto.c
12
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
13
/* Create protocol layer */
14
ret = bdrv_create_file(filename, opts, errp);
15
if (ret < 0) {
16
- return ret;
17
+ goto fail;
18
}
19
20
bs = bdrv_open(filename, NULL, NULL,
21
--
22
2.13.6
23
24
diff view generated by jsdifflib
1
Instead of using monitor_printf() to report errors, hmp_commit() should
1
From: Ari Sundholm <ari@tuxera.com>
2
use error_report() like other places do.
3
2
3
This was a simple oversight when working on intermediate versions
4
of the original patch which introduced blklogwrites.
5
6
Signed-off-by: Ari Sundholm <ari@tuxera.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
7
---
8
---
8
blockdev.c | 7 +++----
9
block/blklogwrites.c | 8 +++-----
9
1 file changed, 3 insertions(+), 4 deletions(-)
10
1 file changed, 3 insertions(+), 5 deletions(-)
10
11
11
diff --git a/blockdev.c b/blockdev.c
12
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
12
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
13
--- a/blockdev.c
14
--- a/block/blklogwrites.c
14
+++ b/blockdev.c
15
+++ b/block/blklogwrites.c
15
@@ -XXX,XX +XXX,XX @@ void hmp_commit(Monitor *mon, const QDict *qdict)
16
@@ -XXX,XX +XXX,XX @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
16
17
QemuOpts *opts;
17
blk = blk_by_name(device);
18
Error *local_err = NULL;
18
if (!blk) {
19
int ret;
19
- monitor_printf(mon, "Device '%s' not found\n", device);
20
- int64_t log_sector_size;
20
+ error_report("Device '%s' not found", device);
21
+ uint64_t log_sector_size;
21
return;
22
22
}
23
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
23
if (!blk_is_available(blk)) {
24
qemu_opts_absorb_qdict(opts, options, &local_err);
24
- monitor_printf(mon, "Device '%s' has no medium\n", device);
25
@@ -XXX,XX +XXX,XX @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
25
+ error_report("Device '%s' has no medium", device);
26
log_sector_size = qemu_opt_get_size(opts, "log-sector-size",
26
return;
27
BDRV_SECTOR_SIZE);
27
}
28
28
29
- if (log_sector_size < 0 || log_sector_size > (1ull << 23) ||
29
@@ -XXX,XX +XXX,XX @@ void hmp_commit(Monitor *mon, const QDict *qdict)
30
- !is_power_of_2(log_sector_size))
30
aio_context_release(aio_context);
31
- {
32
+ if (log_sector_size > (1ull << 23) || !is_power_of_2(log_sector_size)) {
33
ret = -EINVAL;
34
- error_setg(errp, "Invalid log sector size %"PRId64, log_sector_size);
35
+ error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size);
36
goto fail;
31
}
37
}
32
if (ret < 0) {
33
- monitor_printf(mon, "'commit' error for '%s': %s\n", device,
34
- strerror(-ret));
35
+ error_report("'commit' error for '%s': %s", device, strerror(-ret));
36
}
37
}
38
38
39
--
39
--
40
2.20.1
40
2.13.6
41
41
42
42
diff view generated by jsdifflib
New patch
1
1
From: Ari Sundholm <ari@tuxera.com>
2
3
Suggested by Kevin Wolf. May be useful when testing multiple batches
4
of writes or doing long-term testing involving restarts of the VM.
5
6
Signed-off-by: Ari Sundholm <ari@tuxera.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
9
qapi/block-core.json | 3 +-
10
block/blklogwrites.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++-----
11
2 files changed, 135 insertions(+), 15 deletions(-)
12
13
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
index XXXXXXX..XXXXXXX 100644
15
--- a/qapi/block-core.json
16
+++ b/qapi/block-core.json
17
@@ -XXX,XX +XXX,XX @@
18
{ 'struct': 'BlockdevOptionsBlklogwrites',
19
'data': { 'file': 'BlockdevRef',
20
'log': 'BlockdevRef',
21
- '*log-sector-size': 'uint32' } }
22
+ '*log-sector-size': 'uint32',
23
+ '*log-append': 'bool' } }
24
25
##
26
# @BlockdevOptionsBlkverify:
27
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
28
index XXXXXXX..XXXXXXX 100644
29
--- a/block/blklogwrites.c
30
+++ b/block/blklogwrites.c
31
@@ -XXX,XX +XXX,XX @@
32
#define LOG_FUA_FLAG (1 << 1)
33
#define LOG_DISCARD_FLAG (1 << 2)
34
#define LOG_MARK_FLAG (1 << 3)
35
+#define LOG_FLAG_MASK (LOG_FLUSH_FLAG \
36
+ | LOG_FUA_FLAG \
37
+ | LOG_DISCARD_FLAG \
38
+ | LOG_MARK_FLAG)
39
40
#define WRITE_LOG_VERSION 1ULL
41
#define WRITE_LOG_MAGIC 0x6a736677736872ULL
42
@@ -XXX,XX +XXX,XX @@ static QemuOptsList runtime_opts = {
43
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
44
.desc = {
45
{
46
+ .name = "log-append",
47
+ .type = QEMU_OPT_BOOL,
48
+ .help = "Append to an existing log",
49
+ },
50
+ {
51
.name = "log-sector-size",
52
.type = QEMU_OPT_SIZE,
53
.help = "Log sector size",
54
@@ -XXX,XX +XXX,XX @@ static inline uint32_t blk_log_writes_log2(uint32_t value)
55
return 31 - clz32(value);
56
}
57
58
+static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size)
59
+{
60
+ return sector_size < (1ull << 24) && is_power_of_2(sector_size);
61
+}
62
+
63
+static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log,
64
+ uint32_t sector_size,
65
+ uint64_t nr_entries,
66
+ Error **errp)
67
+{
68
+ uint64_t cur_sector = 1;
69
+ uint64_t cur_idx = 0;
70
+ uint32_t sector_bits = blk_log_writes_log2(sector_size);
71
+ struct log_write_entry cur_entry;
72
+
73
+ while (cur_idx < nr_entries) {
74
+ int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry,
75
+ sizeof(cur_entry));
76
+ if (read_ret < 0) {
77
+ error_setg_errno(errp, -read_ret,
78
+ "Failed to read log entry %"PRIu64, cur_idx);
79
+ return (uint64_t)-1ull;
80
+ }
81
+
82
+ if (cur_entry.flags & ~cpu_to_le64(LOG_FLAG_MASK)) {
83
+ error_setg(errp, "Invalid flags 0x%"PRIx64" in log entry %"PRIu64,
84
+ le64_to_cpu(cur_entry.flags), cur_idx);
85
+ return (uint64_t)-1ull;
86
+ }
87
+
88
+ /* Account for the sector of the entry itself */
89
+ ++cur_sector;
90
+
91
+ /*
92
+ * Account for the data of the write.
93
+ * For discards, this data is not present.
94
+ */
95
+ if (!(cur_entry.flags & cpu_to_le64(LOG_DISCARD_FLAG))) {
96
+ cur_sector += le64_to_cpu(cur_entry.nr_sectors);
97
+ }
98
+
99
+ ++cur_idx;
100
+ }
101
+
102
+ return cur_sector;
103
+}
104
+
105
static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
106
Error **errp)
107
{
108
@@ -XXX,XX +XXX,XX @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
109
Error *local_err = NULL;
110
int ret;
111
uint64_t log_sector_size;
112
+ bool log_append;
113
114
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
115
qemu_opts_absorb_qdict(opts, options, &local_err);
116
@@ -XXX,XX +XXX,XX @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
117
goto fail;
118
}
119
120
- log_sector_size = qemu_opt_get_size(opts, "log-sector-size",
121
- BDRV_SECTOR_SIZE);
122
-
123
- if (log_sector_size > (1ull << 23) || !is_power_of_2(log_sector_size)) {
124
- ret = -EINVAL;
125
- error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size);
126
- goto fail;
127
- }
128
-
129
- s->sectorsize = log_sector_size;
130
- s->sectorbits = blk_log_writes_log2(log_sector_size);
131
- s->cur_log_sector = 1;
132
- s->nr_entries = 0;
133
-
134
/* Open the log file */
135
s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false,
136
&local_err);
137
@@ -XXX,XX +XXX,XX @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
138
goto fail;
139
}
140
141
+ log_append = qemu_opt_get_bool(opts, "log-append", false);
142
+
143
+ if (log_append) {
144
+ struct log_write_super log_sb = { 0, 0, 0, 0 };
145
+
146
+ if (qemu_opt_find(opts, "log-sector-size")) {
147
+ ret = -EINVAL;
148
+ error_setg(errp, "log-append and log-sector-size are mutually "
149
+ "exclusive");
150
+ goto fail_log;
151
+ }
152
+
153
+ /* Read log superblock or fake one for an empty log */
154
+ if (!bdrv_getlength(s->log_file->bs)) {
155
+ log_sb.magic = cpu_to_le64(WRITE_LOG_MAGIC);
156
+ log_sb.version = cpu_to_le64(WRITE_LOG_VERSION);
157
+ log_sb.nr_entries = cpu_to_le64(0);
158
+ log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE);
159
+ } else {
160
+ ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb));
161
+ if (ret < 0) {
162
+ error_setg_errno(errp, -ret, "Could not read log superblock");
163
+ goto fail_log;
164
+ }
165
+ }
166
+
167
+ if (log_sb.magic != cpu_to_le64(WRITE_LOG_MAGIC)) {
168
+ ret = -EINVAL;
169
+ error_setg(errp, "Invalid log superblock magic");
170
+ goto fail_log;
171
+ }
172
+
173
+ if (log_sb.version != cpu_to_le64(WRITE_LOG_VERSION)) {
174
+ ret = -EINVAL;
175
+ error_setg(errp, "Unsupported log version %"PRIu64,
176
+ le64_to_cpu(log_sb.version));
177
+ goto fail_log;
178
+ }
179
+
180
+ log_sector_size = le32_to_cpu(log_sb.sectorsize);
181
+ s->cur_log_sector = 1;
182
+ s->nr_entries = 0;
183
+
184
+ if (blk_log_writes_sector_size_valid(log_sector_size)) {
185
+ s->cur_log_sector =
186
+ blk_log_writes_find_cur_log_sector(s->log_file, log_sector_size,
187
+ le64_to_cpu(log_sb.nr_entries), &local_err);
188
+ if (local_err) {
189
+ ret = -EINVAL;
190
+ error_propagate(errp, local_err);
191
+ goto fail_log;
192
+ }
193
+
194
+ s->nr_entries = le64_to_cpu(log_sb.nr_entries);
195
+ }
196
+ } else {
197
+ log_sector_size = qemu_opt_get_size(opts, "log-sector-size",
198
+ BDRV_SECTOR_SIZE);
199
+ s->cur_log_sector = 1;
200
+ s->nr_entries = 0;
201
+ }
202
+
203
+ if (!blk_log_writes_sector_size_valid(log_sector_size)) {
204
+ ret = -EINVAL;
205
+ error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size);
206
+ goto fail_log;
207
+ }
208
+
209
+ s->sectorsize = log_sector_size;
210
+ s->sectorbits = blk_log_writes_log2(log_sector_size);
211
+
212
ret = 0;
213
+fail_log:
214
+ if (ret < 0) {
215
+ bdrv_unref_child(bs, s->log_file);
216
+ s->log_file = NULL;
217
+ }
218
fail:
219
if (ret < 0) {
220
bdrv_unref_child(bs, bs->file);
221
--
222
2.13.6
223
224
diff view generated by jsdifflib
1
Some functions require that the caller holds a certain CoMutex for them
1
From: Ari Sundholm <ari@tuxera.com>
2
to operate correctly. Add a function so that they can assert the lock is
3
really held.
4
2
5
Cc: qemu-stable@nongnu.org
3
This is a way to ensure that the log superblock is periodically
4
updated. Before, this was only done on flush requests, which may
5
not be enough if the VM exits abnormally, omitting the final flush.
6
7
The default interval is 4096 write requests.
8
9
Signed-off-by: Ari Sundholm <ari@tuxera.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Tested-by: Michael Weiser <michael.weiser@gmx.de>
8
Reviewed-by: Michael Weiser <michael.weiser@gmx.de>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Reviewed-by: Denis V. Lunev <den@openvz.org>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
---
11
---
13
include/qemu/coroutine.h | 15 +++++++++++++++
12
qapi/block-core.json | 6 +++++-
14
1 file changed, 15 insertions(+)
13
block/blklogwrites.c | 20 ++++++++++++++++++--
14
2 files changed, 23 insertions(+), 3 deletions(-)
15
15
16
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
16
diff --git a/qapi/block-core.json b/qapi/block-core.json
17
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
18
--- a/include/qemu/coroutine.h
18
--- a/qapi/block-core.json
19
+++ b/include/qemu/coroutine.h
19
+++ b/qapi/block-core.json
20
@@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex);
20
@@ -XXX,XX +XXX,XX @@
21
*/
21
# @log-sector-size: sector size used in logging writes to @file, determines
22
void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex);
22
# granularity of offsets and sizes of writes (default: 512)
23
23
#
24
+/**
24
+# @log-super-update-interval: interval of write requests after which the log
25
+ * Assert that the current coroutine holds @mutex.
25
+# super block is updated to disk (default: 4096)
26
+ */
26
+#
27
+static inline coroutine_fn void qemu_co_mutex_assert_locked(CoMutex *mutex)
27
# Since: 3.0
28
+{
28
##
29
+ /*
29
{ 'struct': 'BlockdevOptionsBlklogwrites',
30
+ * mutex->holder doesn't need any synchronisation if the assertion holds
30
'data': { 'file': 'BlockdevRef',
31
+ * true because the mutex protects it. If it doesn't hold true, we still
31
'log': 'BlockdevRef',
32
+ * don't mind if another thread takes or releases mutex behind our back,
32
'*log-sector-size': 'uint32',
33
+ * because the condition will be false no matter whether we read NULL or
33
- '*log-append': 'bool' } }
34
+ * the pointer for any other coroutine.
34
+ '*log-append': 'bool',
35
+ */
35
+ '*log-super-update-interval': 'uint64' } }
36
+ assert(atomic_read(&mutex->locked) &&
36
37
+ mutex->holder == qemu_coroutine_self());
37
##
38
+}
38
# @BlockdevOptionsBlkverify:
39
39
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
40
/**
40
index XXXXXXX..XXXXXXX 100644
41
* CoQueues are a mechanism to queue coroutines in order to continue executing
41
--- a/block/blklogwrites.c
42
+++ b/block/blklogwrites.c
43
@@ -XXX,XX +XXX,XX @@ typedef struct {
44
uint32_t sectorbits;
45
uint64_t cur_log_sector;
46
uint64_t nr_entries;
47
+ uint64_t update_interval;
48
} BDRVBlkLogWritesState;
49
50
static QemuOptsList runtime_opts = {
51
@@ -XXX,XX +XXX,XX @@ static QemuOptsList runtime_opts = {
52
.type = QEMU_OPT_SIZE,
53
.help = "Log sector size",
54
},
55
+ {
56
+ .name = "log-super-update-interval",
57
+ .type = QEMU_OPT_NUMBER,
58
+ .help = "Log superblock update interval (# of write requests)",
59
+ },
60
{ /* end of list */ }
61
},
62
};
63
@@ -XXX,XX +XXX,XX @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
64
65
s->sectorsize = log_sector_size;
66
s->sectorbits = blk_log_writes_log2(log_sector_size);
67
+ s->update_interval = qemu_opt_get_number(opts, "log-super-update-interval",
68
+ 4096);
69
+ if (!s->update_interval) {
70
+ ret = -EINVAL;
71
+ error_setg(errp, "Invalid log superblock update interval %"PRIu64,
72
+ s->update_interval);
73
+ goto fail_log;
74
+ }
75
76
ret = 0;
77
fail_log:
78
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr)
79
lr->zero_size, 0);
80
}
81
82
- /* Update super block on flush */
83
- if (lr->log_ret == 0 && lr->entry.flags & LOG_FLUSH_FLAG) {
84
+ /* Update super block on flush or every update interval */
85
+ if (lr->log_ret == 0 && ((lr->entry.flags & LOG_FLUSH_FLAG)
86
+ || (s->nr_entries % s->update_interval == 0)))
87
+ {
88
struct log_write_super super = {
89
.magic = cpu_to_le64(WRITE_LOG_MAGIC),
90
.version = cpu_to_le64(WRITE_LOG_VERSION),
42
--
91
--
43
2.20.1
92
2.13.6
44
93
45
94
diff view generated by jsdifflib
1
We added more generic options after introducing -blockdev and forgot to
1
From: Max Reitz <mreitz@redhat.com>
2
update the documentation (man page and --help output) accordingly. Do
3
that now.
4
2
3
raw_apply_lock_bytes() takes a bit mask of "permissions that are NOT
4
shared".
5
6
Also, make the "perm" and "shared" variables uint64_t, because I do not
7
particularly like using ~ on signed integers (and other permission masks
8
are usually uint64_t, too).
9
10
Reported-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
7
---
13
---
8
qemu-options.hx | 22 +++++++++++++++++++++-
14
block/file-posix.c | 4 ++--
9
1 file changed, 21 insertions(+), 1 deletion(-)
15
1 file changed, 2 insertions(+), 2 deletions(-)
10
16
11
diff --git a/qemu-options.hx b/qemu-options.hx
17
diff --git a/block/file-posix.c b/block/file-posix.c
12
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
13
--- a/qemu-options.hx
19
--- a/block/file-posix.c
14
+++ b/qemu-options.hx
20
+++ b/block/file-posix.c
15
@@ -XXX,XX +XXX,XX @@ ETEXI
21
@@ -XXX,XX +XXX,XX @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
16
DEF("blockdev", HAS_ARG, QEMU_OPTION_blockdev,
22
{
17
"-blockdev [driver=]driver[,node-name=N][,discard=ignore|unmap]\n"
23
BlockdevCreateOptionsFile *file_opts;
18
" [,cache.direct=on|off][,cache.no-flush=on|off]\n"
24
int fd;
19
- " [,read-only=on|off][,detect-zeroes=on|off|unmap]\n"
25
- int perm, shared;
20
+ " [,read-only=on|off][,auto-read-only=on|off]\n"
26
+ uint64_t perm, shared;
21
+ " [,force-share=on|off][,detect-zeroes=on|off|unmap]\n"
27
int result = 0;
22
" [,driver specific parameters...]\n"
28
23
" configure a block backend\n", QEMU_ARCH_ALL)
29
/* Validate options and set default values */
24
STEXI
30
@@ -XXX,XX +XXX,XX @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
25
@@ -XXX,XX +XXX,XX @@ name is not intended to be predictable and changes between QEMU invocations.
31
shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
26
For the top level, an explicit node name must be specified.
32
27
@item read-only
33
/* Step one: Take locks */
28
Open the node read-only. Guest write attempts will fail.
34
- result = raw_apply_lock_bytes(fd, perm, shared, false, errp);
29
+
35
+ result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp);
30
+Note that some block drivers support only read-only access, either generally or
36
if (result < 0) {
31
+in certain configurations. In this case, the default value
37
goto out_close;
32
+@option{read-only=off} does not work and the option must be specified
38
}
33
+explicitly.
34
+@item auto-read-only
35
+If @option{auto-read-only=on} is set, QEMU may fall back to read-only usage
36
+even when @option{read-only=off} is requested, or even switch between modes as
37
+needed, e.g. depending on whether the image file is writable or whether a
38
+writing user is attached to the node.
39
+@item force-share
40
+Override the image locking system of QEMU by forcing the node to utilize
41
+weaker shared access for permissions where it would normally request exclusive
42
+access. When there is the potential for multiple instances to have the same
43
+file open (whether this invocation of QEMU is the first or the second
44
+instance), both instances must permit shared access for the second instance to
45
+succeed at opening the file.
46
+
47
+Enabling @option{force-share=on} requires @option{read-only=on}.
48
@item cache.direct
49
The host page cache can be avoided with @option{cache.direct=on}. This will
50
attempt to do disk IO directly to the guest's memory. QEMU may still perform an
51
--
39
--
52
2.20.1
40
2.13.6
53
41
54
42
diff view generated by jsdifflib
1
Some tests in 118 use chmod to remove write permissions from the file
1
From: Max Reitz <mreitz@redhat.com>
2
and assume that the image can indeed not be opened read-write
3
afterwards. This doesn't work when the test is run as root, because root
4
can still open the file as writable even when the permission bit isn't
5
set.
6
2
7
Introduce a @skip_if_root decorator and use it in 118 to skip the tests
3
Closing the FD does not necessarily mean that it is unlocked. Fix this
8
in question when the script is run as root.
4
by relinquishing all permission locks before qemu_close().
9
5
6
Reported-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
12
---
9
---
13
tests/qemu-iotests/118 | 3 +++
10
block/file-posix.c | 17 ++++++++++++++---
14
tests/qemu-iotests/iotests.py | 10 ++++++++++
11
1 file changed, 14 insertions(+), 3 deletions(-)
15
2 files changed, 13 insertions(+)
16
12
17
diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118
13
diff --git a/block/file-posix.c b/block/file-posix.c
18
index XXXXXXX..XXXXXXX 100755
19
--- a/tests/qemu-iotests/118
20
+++ b/tests/qemu-iotests/118
21
@@ -XXX,XX +XXX,XX @@ class TestChangeReadOnly(ChangeBaseClass):
22
self.assert_qmp(result, 'return[0]/inserted/ro', True)
23
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
24
25
+ @iotests.skip_if_user_is_root
26
def test_rw_ro_retain(self):
27
os.chmod(new_img, 0o444)
28
self.vm.add_drive(old_img, 'media=disk', 'none')
29
@@ -XXX,XX +XXX,XX @@ class TestChangeReadOnly(ChangeBaseClass):
30
self.assert_qmp(result, 'return[0]/inserted/ro', True)
31
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
32
33
+ @iotests.skip_if_user_is_root
34
def test_make_ro_rw(self):
35
os.chmod(new_img, 0o444)
36
self.vm.add_drive(old_img, 'media=disk', 'none')
37
@@ -XXX,XX +XXX,XX @@ class TestChangeReadOnly(ChangeBaseClass):
38
self.assert_qmp(result, 'return[0]/inserted/ro', True)
39
self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
40
41
+ @iotests.skip_if_user_is_root
42
def test_make_ro_rw_by_retain(self):
43
os.chmod(new_img, 0o444)
44
self.vm.add_drive(old_img, 'media=disk', 'none')
45
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
46
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
47
--- a/tests/qemu-iotests/iotests.py
15
--- a/block/file-posix.c
48
+++ b/tests/qemu-iotests/iotests.py
16
+++ b/block/file-posix.c
49
@@ -XXX,XX +XXX,XX @@ def skip_if_unsupported(required_formats=[], read_only=False):
17
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn
50
return func_wrapper
18
raw_co_create(BlockdevCreateOptions *options, Error **errp)
51
return skip_test_decorator
19
{
52
20
BlockdevCreateOptionsFile *file_opts;
53
+def skip_if_user_is_root(func):
21
+ Error *local_err = NULL;
54
+ '''Skip Test Decorator
22
int fd;
55
+ Runs the test only without root permissions'''
23
uint64_t perm, shared;
56
+ def func_wrapper(*args, **kwargs):
24
int result = 0;
57
+ if os.getuid() == 0:
25
@@ -XXX,XX +XXX,XX @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
58
+ case_notrun('{}: cannot be run as root'.format(args[0]))
26
/* Step two: Check that nobody else has taken conflicting locks */
59
+ else:
27
result = raw_check_lock_bytes(fd, perm, shared, errp);
60
+ return func(*args, **kwargs)
28
if (result < 0) {
61
+ return func_wrapper
29
- goto out_close;
30
+ goto out_unlock;
31
}
32
33
/* Clear the file by truncating it to 0 */
34
result = raw_regular_truncate(NULL, fd, 0, PREALLOC_MODE_OFF, errp);
35
if (result < 0) {
36
- goto out_close;
37
+ goto out_unlock;
38
}
39
40
if (file_opts->nocow) {
41
@@ -XXX,XX +XXX,XX @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
42
result = raw_regular_truncate(NULL, fd, file_opts->size,
43
file_opts->preallocation, errp);
44
if (result < 0) {
45
- goto out_close;
46
+ goto out_unlock;
47
+ }
62
+
48
+
63
def execute_unittest(output, verbosity, debug):
49
+out_unlock:
64
runner = unittest.TextTestRunner(stream=output, descriptions=True,
50
+ raw_apply_lock_bytes(fd, 0, 0, true, &local_err);
65
verbosity=verbosity)
51
+ if (local_err) {
52
+ /* The above call should not fail, and if it does, that does
53
+ * not mean the whole creation operation has failed. So
54
+ * report it the user for their convenience, but do not report
55
+ * it to the caller. */
56
+ error_report_err(local_err);
57
}
58
59
out_close:
66
--
60
--
67
2.20.1
61
2.13.6
68
62
69
63
diff view generated by jsdifflib