1
The following changes since commit 711c0418c8c1ce3a24346f058b001c4c5a2f0f81:
1
The following changes since commit f5fe7c17ac4e309e47e78f0f9761aebc8d2f2c81:
2
2
3
Merge remote-tracking branch 'remotes/philmd/tags/mips-20210702' into staging (2021-07-04 14:04:12 +0100)
3
Merge tag 'pull-tcg-20230823-2' of https://gitlab.com/rth7680/qemu into staging (2023-08-28 16:07:04 -0400)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
https://gitlab.com/stefanha/qemu.git tags/block-pull-request
7
https://gitlab.com/hreitz/qemu.git tags/pull-block-2023-09-01
8
8
9
for you to fetch changes up to 9f460c64e13897117f35ffb61f6f5e0102cabc70:
9
for you to fetch changes up to 380448464dd89291cf7fd7434be6c225482a334d:
10
10
11
block/io: Merge discard request alignments (2021-07-06 14:28:55 +0100)
11
tests/file-io-error: New test (2023-08-29 13:01:24 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Pull request
14
Block patches
15
16
- Fix for file-posix's zoning code crashing on I/O errors
17
- Throttling refactoring
15
18
16
----------------------------------------------------------------
19
----------------------------------------------------------------
20
Hanna Czenczek (5):
21
file-posix: Clear bs->bl.zoned on error
22
file-posix: Check bs->bl.zoned for zone info
23
file-posix: Fix zone update in I/O error path
24
file-posix: Simplify raw_co_prw's 'out' zone code
25
tests/file-io-error: New test
17
26
18
Akihiko Odaki (3):
27
Zhenwei Pi (9):
19
block/file-posix: Optimize for macOS
28
throttle: introduce enum ThrottleDirection
20
block: Add backend_defaults property
29
test-throttle: use enum ThrottleDirection
21
block/io: Merge discard request alignments
30
throttle: support read-only and write-only
31
test-throttle: test read only and write only
32
cryptodev: use NULL throttle timer cb for read direction
33
throttle: use enum ThrottleDirection instead of bool is_write
34
throttle: use THROTTLE_MAX/ARRAY_SIZE for hard code
35
fsdev: Use ThrottleDirection instread of bool is_write
36
block/throttle-groups: Use ThrottleDirection instread of bool is_write
22
37
23
Stefan Hajnoczi (2):
38
fsdev/qemu-fsdev-throttle.h | 4 +-
24
util/async: add a human-readable name to BHs for debugging
39
include/block/throttle-groups.h | 6 +-
25
util/async: print leaked BH name when AioContext finalizes
40
include/qemu/throttle.h | 16 +-
26
41
backends/cryptodev.c | 12 +-
27
include/block/aio.h | 31 ++++++++++++++++++++++---
42
block/block-backend.c | 4 +-
28
include/hw/block/block.h | 3 +++
43
block/file-posix.c | 42 +++---
29
include/qemu/main-loop.h | 4 +++-
44
block/throttle-groups.c | 163 +++++++++++----------
30
block/file-posix.c | 27 ++++++++++++++++++++--
45
block/throttle.c | 8 +-
31
block/io.c | 2 ++
46
fsdev/qemu-fsdev-throttle.c | 18 ++-
32
hw/block/block.c | 42 ++++++++++++++++++++++++++++++----
47
hw/9pfs/cofile.c | 4 +-
33
tests/unit/ptimer-test-stubs.c | 2 +-
48
tests/unit/test-throttle.c | 76 +++++++++-
34
util/async.c | 25 ++++++++++++++++----
49
util/throttle.c | 84 +++++++----
35
util/main-loop.c | 4 ++--
50
tests/qemu-iotests/tests/file-io-error | 119 +++++++++++++++
36
tests/qemu-iotests/172.out | 38 ++++++++++++++++++++++++++++++
51
tests/qemu-iotests/tests/file-io-error.out | 33 +++++
37
10 files changed, 161 insertions(+), 17 deletions(-)
52
14 files changed, 418 insertions(+), 171 deletions(-)
53
create mode 100755 tests/qemu-iotests/tests/file-io-error
54
create mode 100644 tests/qemu-iotests/tests/file-io-error.out
38
55
39
--
56
--
40
2.31.1
57
2.41.0
41
diff view generated by jsdifflib
New patch
1
From: zhenwei pi <pizhenwei@bytedance.com>
1
2
3
Use enum ThrottleDirection instead of number index.
4
5
Reviewed-by: Alberto Garcia <berto@igalia.com>
6
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
7
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
8
Message-Id: <20230728022006.1098509-2-pizhenwei@bytedance.com>
9
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
10
---
11
include/qemu/throttle.h | 11 ++++++++---
12
util/throttle.c | 16 +++++++++-------
13
2 files changed, 17 insertions(+), 10 deletions(-)
14
15
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
16
index XXXXXXX..XXXXXXX 100644
17
--- a/include/qemu/throttle.h
18
+++ b/include/qemu/throttle.h
19
@@ -XXX,XX +XXX,XX @@ typedef struct ThrottleState {
20
int64_t previous_leak; /* timestamp of the last leak done */
21
} ThrottleState;
22
23
+typedef enum {
24
+ THROTTLE_READ = 0,
25
+ THROTTLE_WRITE,
26
+ THROTTLE_MAX
27
+} ThrottleDirection;
28
+
29
typedef struct ThrottleTimers {
30
- QEMUTimer *timers[2]; /* timers used to do the throttling */
31
+ QEMUTimer *timers[THROTTLE_MAX]; /* timers used to do the throttling */
32
QEMUClockType clock_type; /* the clock used */
33
34
/* Callbacks */
35
- QEMUTimerCB *read_timer_cb;
36
- QEMUTimerCB *write_timer_cb;
37
+ QEMUTimerCB *timer_cb[THROTTLE_MAX];
38
void *timer_opaque;
39
} ThrottleTimers;
40
41
diff --git a/util/throttle.c b/util/throttle.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/util/throttle.c
44
+++ b/util/throttle.c
45
@@ -XXX,XX +XXX,XX @@ static bool throttle_compute_timer(ThrottleState *ts,
46
void throttle_timers_attach_aio_context(ThrottleTimers *tt,
47
AioContext *new_context)
48
{
49
- tt->timers[0] = aio_timer_new(new_context, tt->clock_type, SCALE_NS,
50
- tt->read_timer_cb, tt->timer_opaque);
51
- tt->timers[1] = aio_timer_new(new_context, tt->clock_type, SCALE_NS,
52
- tt->write_timer_cb, tt->timer_opaque);
53
+ tt->timers[THROTTLE_READ] =
54
+ aio_timer_new(new_context, tt->clock_type, SCALE_NS,
55
+ tt->timer_cb[THROTTLE_READ], tt->timer_opaque);
56
+ tt->timers[THROTTLE_WRITE] =
57
+ aio_timer_new(new_context, tt->clock_type, SCALE_NS,
58
+ tt->timer_cb[THROTTLE_WRITE], tt->timer_opaque);
59
}
60
61
/*
62
@@ -XXX,XX +XXX,XX @@ void throttle_timers_init(ThrottleTimers *tt,
63
memset(tt, 0, sizeof(ThrottleTimers));
64
65
tt->clock_type = clock_type;
66
- tt->read_timer_cb = read_timer_cb;
67
- tt->write_timer_cb = write_timer_cb;
68
+ tt->timer_cb[THROTTLE_READ] = read_timer_cb;
69
+ tt->timer_cb[THROTTLE_WRITE] = write_timer_cb;
70
tt->timer_opaque = timer_opaque;
71
throttle_timers_attach_aio_context(tt, aio_context);
72
}
73
@@ -XXX,XX +XXX,XX @@ void throttle_timers_detach_aio_context(ThrottleTimers *tt)
74
{
75
int i;
76
77
- for (i = 0; i < 2; i++) {
78
+ for (i = 0; i < THROTTLE_MAX; i++) {
79
throttle_timer_destroy(&tt->timers[i]);
80
}
81
}
82
--
83
2.41.0
diff view generated by jsdifflib
New patch
1
From: zhenwei pi <pizhenwei@bytedance.com>
1
2
3
Use enum ThrottleDirection instead in the throttle test codes.
4
5
Reviewed-by: Alberto Garcia <berto@igalia.com>
6
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
7
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
8
Message-Id: <20230728022006.1098509-3-pizhenwei@bytedance.com>
9
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
10
---
11
tests/unit/test-throttle.c | 6 +++---
12
1 file changed, 3 insertions(+), 3 deletions(-)
13
14
diff --git a/tests/unit/test-throttle.c b/tests/unit/test-throttle.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/tests/unit/test-throttle.c
17
+++ b/tests/unit/test-throttle.c
18
@@ -XXX,XX +XXX,XX @@ static void test_init(void)
19
20
/* check initialized fields */
21
g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL);
22
- g_assert(tt->timers[0]);
23
- g_assert(tt->timers[1]);
24
+ g_assert(tt->timers[THROTTLE_READ]);
25
+ g_assert(tt->timers[THROTTLE_WRITE]);
26
27
/* check other fields where cleared */
28
g_assert(!ts.previous_leak);
29
@@ -XXX,XX +XXX,XX @@ static void test_destroy(void)
30
throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
31
read_timer_cb, write_timer_cb, &ts);
32
throttle_timers_destroy(tt);
33
- for (i = 0; i < 2; i++) {
34
+ for (i = 0; i < THROTTLE_MAX; i++) {
35
g_assert(!tt->timers[i]);
36
}
37
}
38
--
39
2.41.0
diff view generated by jsdifflib
1
It can be difficult to debug issues with BHs in production environments.
1
From: zhenwei pi <pizhenwei@bytedance.com>
2
Although BHs can usually be identified by looking up their ->cb()
3
function pointer, this requires debug information for the program. It is
4
also not possible to print human-readable diagnostics about BHs because
5
they have no identifier.
6
2
7
This patch adds a name to each BH. The name is not unique per instance
3
Only one direction is necessary in several scenarios:
8
but differentiates between cb() functions, which is usually enough. It's
4
- a read-only disk
9
done by changing aio_bh_new() and friends to macros that stringify cb.
5
- operations on a device are considered as *write* only. For example,
6
encrypt/decrypt/sign/verify operations on a cryptodev use a single
7
*write* timer(read timer callback is defined, but never invoked).
10
8
11
The next patch will use the name field when reporting leaked BHs.
9
Allow a single direction in throttle, this reduces memory, and uplayer
10
does not need a dummy callback any more.
12
11
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Reviewed-by: Alberto Garcia <berto@igalia.com>
14
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
13
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
15
Message-Id: <20210414200247.917496-2-stefanha@redhat.com>
14
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
15
Message-Id: <20230728022006.1098509-4-pizhenwei@bytedance.com>
16
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
16
---
17
---
17
include/block/aio.h | 31 ++++++++++++++++++++++++++++---
18
util/throttle.c | 42 ++++++++++++++++++++++++++++--------------
18
include/qemu/main-loop.h | 4 +++-
19
1 file changed, 28 insertions(+), 14 deletions(-)
19
tests/unit/ptimer-test-stubs.c | 2 +-
20
util/async.c | 9 +++++++--
21
util/main-loop.c | 4 ++--
22
5 files changed, 41 insertions(+), 9 deletions(-)
23
20
24
diff --git a/include/block/aio.h b/include/block/aio.h
21
diff --git a/util/throttle.c b/util/throttle.c
25
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/aio.h
23
--- a/util/throttle.c
27
+++ b/include/block/aio.h
24
+++ b/util/throttle.c
28
@@ -XXX,XX +XXX,XX @@ void aio_context_acquire(AioContext *ctx);
25
@@ -XXX,XX +XXX,XX @@ static bool throttle_compute_timer(ThrottleState *ts,
29
/* Relinquish ownership of the AioContext. */
26
void throttle_timers_attach_aio_context(ThrottleTimers *tt,
30
void aio_context_release(AioContext *ctx);
27
AioContext *new_context)
31
28
{
32
+/**
29
- tt->timers[THROTTLE_READ] =
33
+ * aio_bh_schedule_oneshot_full: Allocate a new bottom half structure that will
30
- aio_timer_new(new_context, tt->clock_type, SCALE_NS,
34
+ * run only once and as soon as possible.
31
- tt->timer_cb[THROTTLE_READ], tt->timer_opaque);
35
+ *
32
- tt->timers[THROTTLE_WRITE] =
36
+ * @name: A human-readable identifier for debugging purposes.
33
- aio_timer_new(new_context, tt->clock_type, SCALE_NS,
37
+ */
34
- tt->timer_cb[THROTTLE_WRITE], tt->timer_opaque);
38
+void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque,
35
+ ThrottleDirection dir;
39
+ const char *name);
40
+
36
+
41
/**
37
+ for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) {
42
* aio_bh_schedule_oneshot: Allocate a new bottom half structure that will run
38
+ if (tt->timer_cb[dir]) {
43
* only once and as soon as possible.
39
+ tt->timers[dir] =
44
+ *
40
+ aio_timer_new(new_context, tt->clock_type, SCALE_NS,
45
+ * A convenience wrapper for aio_bh_schedule_oneshot_full() that uses cb as the
41
+ tt->timer_cb[dir], tt->timer_opaque);
46
+ * name string.
42
+ }
47
*/
43
+ }
48
-void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque);
44
}
49
+#define aio_bh_schedule_oneshot(ctx, cb, opaque) \
45
50
+ aio_bh_schedule_oneshot_full((ctx), (cb), (opaque), (stringify(cb)))
46
/*
51
47
@@ -XXX,XX +XXX,XX @@ void throttle_timers_init(ThrottleTimers *tt,
52
/**
48
QEMUTimerCB *write_timer_cb,
53
- * aio_bh_new: Allocate a new bottom half structure.
49
void *timer_opaque)
54
+ * aio_bh_new_full: Allocate a new bottom half structure.
50
{
55
*
51
+ assert(read_timer_cb || write_timer_cb);
56
* Bottom halves are lightweight callbacks whose invocation is guaranteed
52
memset(tt, 0, sizeof(ThrottleTimers));
57
* to be wait-free, thread-safe and signal-safe. The #QEMUBH structure
53
58
* is opaque and must be allocated prior to its use.
54
tt->clock_type = clock_type;
59
+ *
55
@@ -XXX,XX +XXX,XX @@ void throttle_timers_init(ThrottleTimers *tt,
60
+ * @name: A human-readable identifier for debugging purposes.
56
/* destroy a timer */
61
*/
57
static void throttle_timer_destroy(QEMUTimer **timer)
62
-QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque);
58
{
63
+QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque,
59
- assert(*timer != NULL);
64
+ const char *name);
60
+ if (*timer == NULL) {
61
+ return;
62
+ }
63
64
timer_free(*timer);
65
*timer = NULL;
66
@@ -XXX,XX +XXX,XX @@ static void throttle_timer_destroy(QEMUTimer **timer)
67
/* Remove timers from event loop */
68
void throttle_timers_detach_aio_context(ThrottleTimers *tt)
69
{
70
- int i;
71
+ ThrottleDirection dir;
72
73
- for (i = 0; i < THROTTLE_MAX; i++) {
74
- throttle_timer_destroy(&tt->timers[i]);
75
+ for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) {
76
+ throttle_timer_destroy(&tt->timers[dir]);
77
}
78
}
79
80
@@ -XXX,XX +XXX,XX @@ void throttle_timers_destroy(ThrottleTimers *tt)
81
/* is any throttling timer configured */
82
bool throttle_timers_are_initialized(ThrottleTimers *tt)
83
{
84
- if (tt->timers[0]) {
85
- return true;
86
+ ThrottleDirection dir;
65
+
87
+
66
+/**
88
+ for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) {
67
+ * aio_bh_new: Allocate a new bottom half structure
89
+ if (tt->timers[dir]) {
68
+ *
90
+ return true;
69
+ * A convenience wrapper for aio_bh_new_full() that uses the cb as the name
91
+ }
70
+ * string.
92
}
71
+ */
93
72
+#define aio_bh_new(ctx, cb, opaque) \
94
return false;
73
+ aio_bh_new_full((ctx), (cb), (opaque), (stringify(cb)))
95
@@ -XXX,XX +XXX,XX @@ bool throttle_schedule_timer(ThrottleState *ts,
74
96
{
75
/**
97
int64_t now = qemu_clock_get_ns(tt->clock_type);
76
* aio_notify: Force processing of pending events.
98
int64_t next_timestamp;
77
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
99
+ QEMUTimer *timer;
78
index XXXXXXX..XXXXXXX 100644
100
bool must_wait;
79
--- a/include/qemu/main-loop.h
101
80
+++ b/include/qemu/main-loop.h
102
+ timer = is_write ? tt->timers[THROTTLE_WRITE] : tt->timers[THROTTLE_READ];
81
@@ -XXX,XX +XXX,XX @@ void qemu_cond_timedwait_iothread(QemuCond *cond, int ms);
103
+ assert(timer);
82
104
+
83
void qemu_fd_register(int fd);
105
must_wait = throttle_compute_timer(ts,
84
106
is_write,
85
-QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque);
107
now,
86
+#define qemu_bh_new(cb, opaque) \
108
@@ -XXX,XX +XXX,XX @@ bool throttle_schedule_timer(ThrottleState *ts,
87
+ qemu_bh_new_full((cb), (opaque), (stringify(cb)))
109
}
88
+QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name);
110
89
void qemu_bh_schedule_idle(QEMUBH *bh);
111
/* request throttled and timer pending -> do nothing */
90
112
- if (timer_pending(tt->timers[is_write])) {
91
enum {
113
+ if (timer_pending(timer)) {
92
diff --git a/tests/unit/ptimer-test-stubs.c b/tests/unit/ptimer-test-stubs.c
114
return true;
93
index XXXXXXX..XXXXXXX 100644
115
}
94
--- a/tests/unit/ptimer-test-stubs.c
116
95
+++ b/tests/unit/ptimer-test-stubs.c
117
/* request throttled and timer not pending -> arm timer */
96
@@ -XXX,XX +XXX,XX @@ int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask)
118
- timer_mod(tt->timers[is_write], next_timestamp);
97
return deadline;
119
+ timer_mod(timer, next_timestamp);
120
return true;
98
}
121
}
99
122
100
-QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
101
+QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name)
102
{
103
QEMUBH *bh = g_new(QEMUBH, 1);
104
105
diff --git a/util/async.c b/util/async.c
106
index XXXXXXX..XXXXXXX 100644
107
--- a/util/async.c
108
+++ b/util/async.c
109
@@ -XXX,XX +XXX,XX @@ enum {
110
111
struct QEMUBH {
112
AioContext *ctx;
113
+ const char *name;
114
QEMUBHFunc *cb;
115
void *opaque;
116
QSLIST_ENTRY(QEMUBH) next;
117
@@ -XXX,XX +XXX,XX @@ static QEMUBH *aio_bh_dequeue(BHList *head, unsigned *flags)
118
return bh;
119
}
120
121
-void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
122
+void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb,
123
+ void *opaque, const char *name)
124
{
125
QEMUBH *bh;
126
bh = g_new(QEMUBH, 1);
127
@@ -XXX,XX +XXX,XX @@ void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
128
.ctx = ctx,
129
.cb = cb,
130
.opaque = opaque,
131
+ .name = name,
132
};
133
aio_bh_enqueue(bh, BH_SCHEDULED | BH_ONESHOT);
134
}
135
136
-QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
137
+QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque,
138
+ const char *name)
139
{
140
QEMUBH *bh;
141
bh = g_new(QEMUBH, 1);
142
@@ -XXX,XX +XXX,XX @@ QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
143
.ctx = ctx,
144
.cb = cb,
145
.opaque = opaque,
146
+ .name = name,
147
};
148
return bh;
149
}
150
diff --git a/util/main-loop.c b/util/main-loop.c
151
index XXXXXXX..XXXXXXX 100644
152
--- a/util/main-loop.c
153
+++ b/util/main-loop.c
154
@@ -XXX,XX +XXX,XX @@ void main_loop_wait(int nonblocking)
155
156
/* Functions to operate on the main QEMU AioContext. */
157
158
-QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
159
+QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name)
160
{
161
- return aio_bh_new(qemu_aio_context, cb, opaque);
162
+ return aio_bh_new_full(qemu_aio_context, cb, opaque, name);
163
}
164
165
/*
166
--
123
--
167
2.31.1
124
2.41.0
168
diff view generated by jsdifflib
New patch
1
From: zhenwei pi <pizhenwei@bytedance.com>
1
2
3
Reviewed-by: Alberto Garcia <berto@igalia.com>
4
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
5
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
6
Message-Id: <20230728022006.1098509-5-pizhenwei@bytedance.com>
7
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
8
---
9
tests/unit/test-throttle.c | 66 ++++++++++++++++++++++++++++++++++++++
10
1 file changed, 66 insertions(+)
11
12
diff --git a/tests/unit/test-throttle.c b/tests/unit/test-throttle.c
13
index XXXXXXX..XXXXXXX 100644
14
--- a/tests/unit/test-throttle.c
15
+++ b/tests/unit/test-throttle.c
16
@@ -XXX,XX +XXX,XX @@ static void test_init(void)
17
throttle_timers_destroy(tt);
18
}
19
20
+static void test_init_readonly(void)
21
+{
22
+ int i;
23
+
24
+ tt = &tgm.throttle_timers;
25
+
26
+ /* fill the structures with crap */
27
+ memset(&ts, 1, sizeof(ts));
28
+ memset(tt, 1, sizeof(*tt));
29
+
30
+ /* init structures */
31
+ throttle_init(&ts);
32
+ throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
33
+ read_timer_cb, NULL, &ts);
34
+
35
+ /* check initialized fields */
36
+ g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL);
37
+ g_assert(tt->timers[THROTTLE_READ]);
38
+ g_assert(!tt->timers[THROTTLE_WRITE]);
39
+
40
+ /* check other fields where cleared */
41
+ g_assert(!ts.previous_leak);
42
+ g_assert(!ts.cfg.op_size);
43
+ for (i = 0; i < BUCKETS_COUNT; i++) {
44
+ g_assert(!ts.cfg.buckets[i].avg);
45
+ g_assert(!ts.cfg.buckets[i].max);
46
+ g_assert(!ts.cfg.buckets[i].level);
47
+ }
48
+
49
+ throttle_timers_destroy(tt);
50
+}
51
+
52
+static void test_init_writeonly(void)
53
+{
54
+ int i;
55
+
56
+ tt = &tgm.throttle_timers;
57
+
58
+ /* fill the structures with crap */
59
+ memset(&ts, 1, sizeof(ts));
60
+ memset(tt, 1, sizeof(*tt));
61
+
62
+ /* init structures */
63
+ throttle_init(&ts);
64
+ throttle_timers_init(tt, ctx, QEMU_CLOCK_VIRTUAL,
65
+ NULL, write_timer_cb, &ts);
66
+
67
+ /* check initialized fields */
68
+ g_assert(tt->clock_type == QEMU_CLOCK_VIRTUAL);
69
+ g_assert(!tt->timers[THROTTLE_READ]);
70
+ g_assert(tt->timers[THROTTLE_WRITE]);
71
+
72
+ /* check other fields where cleared */
73
+ g_assert(!ts.previous_leak);
74
+ g_assert(!ts.cfg.op_size);
75
+ for (i = 0; i < BUCKETS_COUNT; i++) {
76
+ g_assert(!ts.cfg.buckets[i].avg);
77
+ g_assert(!ts.cfg.buckets[i].max);
78
+ g_assert(!ts.cfg.buckets[i].level);
79
+ }
80
+
81
+ throttle_timers_destroy(tt);
82
+}
83
+
84
static void test_destroy(void)
85
{
86
int i;
87
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
88
g_test_add_func("/throttle/leak_bucket", test_leak_bucket);
89
g_test_add_func("/throttle/compute_wait", test_compute_wait);
90
g_test_add_func("/throttle/init", test_init);
91
+ g_test_add_func("/throttle/init_readonly", test_init_readonly);
92
+ g_test_add_func("/throttle/init_writeonly", test_init_writeonly);
93
g_test_add_func("/throttle/destroy", test_destroy);
94
g_test_add_func("/throttle/have_timer", test_have_timer);
95
g_test_add_func("/throttle/detach_attach", test_detach_attach);
96
--
97
2.41.0
diff view generated by jsdifflib
New patch
1
From: zhenwei pi <pizhenwei@bytedance.com>
1
2
3
Operations on a cryptodev are considered as *write* only, the callback
4
of read direction is never invoked. Use NULL instead of an unreachable
5
path(cryptodev_backend_throttle_timer_cb on read direction).
6
7
The dummy read timer(never invoked) is already removed here, it means
8
that the 'FIXME' tag is no longer needed.
9
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
11
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
12
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
13
Message-Id: <20230728022006.1098509-6-pizhenwei@bytedance.com>
14
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
15
---
16
backends/cryptodev.c | 3 +--
17
1 file changed, 1 insertion(+), 2 deletions(-)
18
19
diff --git a/backends/cryptodev.c b/backends/cryptodev.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/backends/cryptodev.c
22
+++ b/backends/cryptodev.c
23
@@ -XXX,XX +XXX,XX @@ static void cryptodev_backend_set_throttle(CryptoDevBackend *backend, int field,
24
if (!enabled) {
25
throttle_init(&backend->ts);
26
throttle_timers_init(&backend->tt, qemu_get_aio_context(),
27
- QEMU_CLOCK_REALTIME,
28
- cryptodev_backend_throttle_timer_cb, /* FIXME */
29
+ QEMU_CLOCK_REALTIME, NULL,
30
cryptodev_backend_throttle_timer_cb, backend);
31
}
32
33
--
34
2.41.0
diff view generated by jsdifflib
New patch
1
From: zhenwei pi <pizhenwei@bytedance.com>
1
2
3
enum ThrottleDirection is already there, use ThrottleDirection instead
4
of 'bool is_write' for throttle API, also modify related codes from
5
block, fsdev, cryptodev and tests.
6
7
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
8
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
9
Message-Id: <20230728022006.1098509-7-pizhenwei@bytedance.com>
10
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
11
---
12
include/qemu/throttle.h | 5 +++--
13
backends/cryptodev.c | 9 +++++----
14
block/throttle-groups.c | 6 ++++--
15
fsdev/qemu-fsdev-throttle.c | 8 +++++---
16
tests/unit/test-throttle.c | 4 ++--
17
util/throttle.c | 31 +++++++++++++++++--------------
18
6 files changed, 36 insertions(+), 27 deletions(-)
19
20
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
21
index XXXXXXX..XXXXXXX 100644
22
--- a/include/qemu/throttle.h
23
+++ b/include/qemu/throttle.h
24
@@ -XXX,XX +XXX,XX @@ void throttle_config_init(ThrottleConfig *cfg);
25
/* usage */
26
bool throttle_schedule_timer(ThrottleState *ts,
27
ThrottleTimers *tt,
28
- bool is_write);
29
+ ThrottleDirection direction);
30
31
-void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
32
+void throttle_account(ThrottleState *ts, ThrottleDirection direction,
33
+ uint64_t size);
34
void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
35
Error **errp);
36
void throttle_config_to_limits(ThrottleConfig *cfg, ThrottleLimits *var);
37
diff --git a/backends/cryptodev.c b/backends/cryptodev.c
38
index XXXXXXX..XXXXXXX 100644
39
--- a/backends/cryptodev.c
40
+++ b/backends/cryptodev.c
41
@@ -XXX,XX +XXX,XX @@ static void cryptodev_backend_throttle_timer_cb(void *opaque)
42
continue;
43
}
44
45
- throttle_account(&backend->ts, true, ret);
46
+ throttle_account(&backend->ts, THROTTLE_WRITE, ret);
47
cryptodev_backend_operation(backend, op_info);
48
if (throttle_enabled(&backend->tc) &&
49
- throttle_schedule_timer(&backend->ts, &backend->tt, true)) {
50
+ throttle_schedule_timer(&backend->ts, &backend->tt,
51
+ THROTTLE_WRITE)) {
52
break;
53
}
54
}
55
@@ -XXX,XX +XXX,XX @@ int cryptodev_backend_crypto_operation(
56
goto do_account;
57
}
58
59
- if (throttle_schedule_timer(&backend->ts, &backend->tt, true) ||
60
+ if (throttle_schedule_timer(&backend->ts, &backend->tt, THROTTLE_WRITE) ||
61
!QTAILQ_EMPTY(&backend->opinfos)) {
62
QTAILQ_INSERT_TAIL(&backend->opinfos, op_info, next);
63
return 0;
64
@@ -XXX,XX +XXX,XX @@ do_account:
65
return ret;
66
}
67
68
- throttle_account(&backend->ts, true, ret);
69
+ throttle_account(&backend->ts, THROTTLE_WRITE, ret);
70
71
return cryptodev_backend_operation(backend, op_info);
72
}
73
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
74
index XXXXXXX..XXXXXXX 100644
75
--- a/block/throttle-groups.c
76
+++ b/block/throttle-groups.c
77
@@ -XXX,XX +XXX,XX @@ static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm,
78
ThrottleState *ts = tgm->throttle_state;
79
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
80
ThrottleTimers *tt = &tgm->throttle_timers;
81
+ ThrottleDirection direction = is_write ? THROTTLE_WRITE : THROTTLE_READ;
82
bool must_wait;
83
84
if (qatomic_read(&tgm->io_limits_disabled)) {
85
@@ -XXX,XX +XXX,XX @@ static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm,
86
return true;
87
}
88
89
- must_wait = throttle_schedule_timer(ts, tt, is_write);
90
+ must_wait = throttle_schedule_timer(ts, tt, direction);
91
92
/* If a timer just got armed, set tgm as the current token */
93
if (must_wait) {
94
@@ -XXX,XX +XXX,XX @@ void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm
95
bool must_wait;
96
ThrottleGroupMember *token;
97
ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts);
98
+ ThrottleDirection direction = is_write ? THROTTLE_WRITE : THROTTLE_READ;
99
100
assert(bytes >= 0);
101
102
@@ -XXX,XX +XXX,XX @@ void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm
103
}
104
105
/* The I/O will be executed, so do the accounting */
106
- throttle_account(tgm->throttle_state, is_write, bytes);
107
+ throttle_account(tgm->throttle_state, direction, bytes);
108
109
/* Schedule the next request */
110
schedule_next_request(tgm, is_write);
111
diff --git a/fsdev/qemu-fsdev-throttle.c b/fsdev/qemu-fsdev-throttle.c
112
index XXXXXXX..XXXXXXX 100644
113
--- a/fsdev/qemu-fsdev-throttle.c
114
+++ b/fsdev/qemu-fsdev-throttle.c
115
@@ -XXX,XX +XXX,XX @@ void fsdev_throttle_init(FsThrottle *fst)
116
void coroutine_fn fsdev_co_throttle_request(FsThrottle *fst, bool is_write,
117
struct iovec *iov, int iovcnt)
118
{
119
+ ThrottleDirection direction = is_write ? THROTTLE_WRITE : THROTTLE_READ;
120
+
121
if (throttle_enabled(&fst->cfg)) {
122
- if (throttle_schedule_timer(&fst->ts, &fst->tt, is_write) ||
123
+ if (throttle_schedule_timer(&fst->ts, &fst->tt, direction) ||
124
!qemu_co_queue_empty(&fst->throttled_reqs[is_write])) {
125
qemu_co_queue_wait(&fst->throttled_reqs[is_write], NULL);
126
}
127
128
- throttle_account(&fst->ts, is_write, iov_size(iov, iovcnt));
129
+ throttle_account(&fst->ts, direction, iov_size(iov, iovcnt));
130
131
if (!qemu_co_queue_empty(&fst->throttled_reqs[is_write]) &&
132
- !throttle_schedule_timer(&fst->ts, &fst->tt, is_write)) {
133
+ !throttle_schedule_timer(&fst->ts, &fst->tt, direction)) {
134
qemu_co_queue_next(&fst->throttled_reqs[is_write]);
135
}
136
}
137
diff --git a/tests/unit/test-throttle.c b/tests/unit/test-throttle.c
138
index XXXXXXX..XXXXXXX 100644
139
--- a/tests/unit/test-throttle.c
140
+++ b/tests/unit/test-throttle.c
141
@@ -XXX,XX +XXX,XX @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
142
throttle_config(&ts, QEMU_CLOCK_VIRTUAL, &cfg);
143
144
/* account a read */
145
- throttle_account(&ts, false, size);
146
+ throttle_account(&ts, THROTTLE_READ, size);
147
/* account a write */
148
- throttle_account(&ts, true, size);
149
+ throttle_account(&ts, THROTTLE_WRITE, size);
150
151
/* check total result */
152
index = to_test[is_ops][0];
153
diff --git a/util/throttle.c b/util/throttle.c
154
index XXXXXXX..XXXXXXX 100644
155
--- a/util/throttle.c
156
+++ b/util/throttle.c
157
@@ -XXX,XX +XXX,XX @@ int64_t throttle_compute_wait(LeakyBucket *bkt)
158
159
/* This function compute the time that must be waited while this IO
160
*
161
- * @is_write: true if the current IO is a write, false if it's a read
162
+ * @direction: throttle direction
163
* @ret: time to wait
164
*/
165
static int64_t throttle_compute_wait_for(ThrottleState *ts,
166
- bool is_write)
167
+ ThrottleDirection direction)
168
{
169
BucketType to_check[2][4] = { {THROTTLE_BPS_TOTAL,
170
THROTTLE_OPS_TOTAL,
171
@@ -XXX,XX +XXX,XX @@ static int64_t throttle_compute_wait_for(ThrottleState *ts,
172
int i;
173
174
for (i = 0; i < 4; i++) {
175
- BucketType index = to_check[is_write][i];
176
+ BucketType index = to_check[direction][i];
177
wait = throttle_compute_wait(&ts->cfg.buckets[index]);
178
if (wait > max_wait) {
179
max_wait = wait;
180
@@ -XXX,XX +XXX,XX @@ static int64_t throttle_compute_wait_for(ThrottleState *ts,
181
182
/* compute the timer for this type of operation
183
*
184
- * @is_write: the type of operation
185
+ * @direction: throttle direction
186
* @now: the current clock timestamp
187
* @next_timestamp: the resulting timer
188
* @ret: true if a timer must be set
189
*/
190
static bool throttle_compute_timer(ThrottleState *ts,
191
- bool is_write,
192
+ ThrottleDirection direction,
193
int64_t now,
194
int64_t *next_timestamp)
195
{
196
@@ -XXX,XX +XXX,XX @@ static bool throttle_compute_timer(ThrottleState *ts,
197
throttle_do_leak(ts, now);
198
199
/* compute the wait time if any */
200
- wait = throttle_compute_wait_for(ts, is_write);
201
+ wait = throttle_compute_wait_for(ts, direction);
202
203
/* if the code must wait compute when the next timer should fire */
204
if (wait) {
205
@@ -XXX,XX +XXX,XX @@ void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg)
206
* NOTE: this function is not unit tested due to it's usage of timer_mod
207
*
208
* @tt: the timers structure
209
- * @is_write: the type of operation (read/write)
210
+ * @direction: throttle direction
211
* @ret: true if the timer has been scheduled else false
212
*/
213
bool throttle_schedule_timer(ThrottleState *ts,
214
ThrottleTimers *tt,
215
- bool is_write)
216
+ ThrottleDirection direction)
217
{
218
int64_t now = qemu_clock_get_ns(tt->clock_type);
219
int64_t next_timestamp;
220
QEMUTimer *timer;
221
bool must_wait;
222
223
- timer = is_write ? tt->timers[THROTTLE_WRITE] : tt->timers[THROTTLE_READ];
224
+ assert(direction < THROTTLE_MAX);
225
+ timer = tt->timers[direction];
226
assert(timer);
227
228
must_wait = throttle_compute_timer(ts,
229
- is_write,
230
+ direction,
231
now,
232
&next_timestamp);
233
234
@@ -XXX,XX +XXX,XX @@ bool throttle_schedule_timer(ThrottleState *ts,
235
236
/* do the accounting for this operation
237
*
238
- * @is_write: the type of operation (read/write)
239
+ * @direction: throttle direction
240
* @size: the size of the operation
241
*/
242
-void throttle_account(ThrottleState *ts, bool is_write, uint64_t size)
243
+void throttle_account(ThrottleState *ts, ThrottleDirection direction,
244
+ uint64_t size)
245
{
246
const BucketType bucket_types_size[2][2] = {
247
{ THROTTLE_BPS_TOTAL, THROTTLE_BPS_READ },
248
@@ -XXX,XX +XXX,XX @@ void throttle_account(ThrottleState *ts, bool is_write, uint64_t size)
249
double units = 1.0;
250
unsigned i;
251
252
+ assert(direction < THROTTLE_MAX);
253
/* if cfg.op_size is defined and smaller than size we compute unit count */
254
if (ts->cfg.op_size && size > ts->cfg.op_size) {
255
units = (double) size / ts->cfg.op_size;
256
@@ -XXX,XX +XXX,XX @@ void throttle_account(ThrottleState *ts, bool is_write, uint64_t size)
257
for (i = 0; i < 2; i++) {
258
LeakyBucket *bkt;
259
260
- bkt = &ts->cfg.buckets[bucket_types_size[is_write][i]];
261
+ bkt = &ts->cfg.buckets[bucket_types_size[direction][i]];
262
bkt->level += size;
263
if (bkt->burst_length > 1) {
264
bkt->burst_level += size;
265
}
266
267
- bkt = &ts->cfg.buckets[bucket_types_units[is_write][i]];
268
+ bkt = &ts->cfg.buckets[bucket_types_units[direction][i]];
269
bkt->level += units;
270
if (bkt->burst_length > 1) {
271
bkt->burst_level += units;
272
--
273
2.41.0
diff view generated by jsdifflib
New patch
1
From: zhenwei pi <pizhenwei@bytedance.com>
1
2
3
The first dimension of both to_check and
4
bucket_types_size/bucket_types_units is used as throttle direction,
5
use THROTTLE_MAX instead of hard coded number. Also use ARRAY_SIZE()
6
to avoid hard coded number for the second dimension.
7
8
Hanna noticed that the two array should be static. Yes, turn them
9
into static variables.
10
11
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
12
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
13
Message-Id: <20230728022006.1098509-8-pizhenwei@bytedance.com>
14
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
15
---
16
util/throttle.c | 11 ++++++-----
17
1 file changed, 6 insertions(+), 5 deletions(-)
18
19
diff --git a/util/throttle.c b/util/throttle.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/util/throttle.c
22
+++ b/util/throttle.c
23
@@ -XXX,XX +XXX,XX @@ int64_t throttle_compute_wait(LeakyBucket *bkt)
24
static int64_t throttle_compute_wait_for(ThrottleState *ts,
25
ThrottleDirection direction)
26
{
27
- BucketType to_check[2][4] = { {THROTTLE_BPS_TOTAL,
28
+ static const BucketType to_check[THROTTLE_MAX][4] = {
29
+ {THROTTLE_BPS_TOTAL,
30
THROTTLE_OPS_TOTAL,
31
THROTTLE_BPS_READ,
32
THROTTLE_OPS_READ},
33
@@ -XXX,XX +XXX,XX @@ static int64_t throttle_compute_wait_for(ThrottleState *ts,
34
int64_t wait, max_wait = 0;
35
int i;
36
37
- for (i = 0; i < 4; i++) {
38
+ for (i = 0; i < ARRAY_SIZE(to_check[THROTTLE_READ]); i++) {
39
BucketType index = to_check[direction][i];
40
wait = throttle_compute_wait(&ts->cfg.buckets[index]);
41
if (wait > max_wait) {
42
@@ -XXX,XX +XXX,XX @@ bool throttle_schedule_timer(ThrottleState *ts,
43
void throttle_account(ThrottleState *ts, ThrottleDirection direction,
44
uint64_t size)
45
{
46
- const BucketType bucket_types_size[2][2] = {
47
+ static const BucketType bucket_types_size[THROTTLE_MAX][2] = {
48
{ THROTTLE_BPS_TOTAL, THROTTLE_BPS_READ },
49
{ THROTTLE_BPS_TOTAL, THROTTLE_BPS_WRITE }
50
};
51
- const BucketType bucket_types_units[2][2] = {
52
+ static const BucketType bucket_types_units[THROTTLE_MAX][2] = {
53
{ THROTTLE_OPS_TOTAL, THROTTLE_OPS_READ },
54
{ THROTTLE_OPS_TOTAL, THROTTLE_OPS_WRITE }
55
};
56
@@ -XXX,XX +XXX,XX @@ void throttle_account(ThrottleState *ts, ThrottleDirection direction,
57
units = (double) size / ts->cfg.op_size;
58
}
59
60
- for (i = 0; i < 2; i++) {
61
+ for (i = 0; i < ARRAY_SIZE(bucket_types_size[THROTTLE_READ]); i++) {
62
LeakyBucket *bkt;
63
64
bkt = &ts->cfg.buckets[bucket_types_size[direction][i]];
65
--
66
2.41.0
diff view generated by jsdifflib
1
From: Akihiko Odaki <akihiko.odaki@gmail.com>
1
From: zhenwei pi <pizhenwei@bytedance.com>
2
2
3
backend_defaults property allow users to control if default block
3
'bool is_write' style is obsolete from throttle framework, adapt
4
properties should be decided with backend information.
4
fsdev to the new style.
5
5
6
If it is off, any backend information will be discarded, which is
6
Cc: Greg Kurz <groug@kaod.org>
7
suitable if you plan to perform live migration to a different disk backend.
7
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
8
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
9
Message-Id: <20230728022006.1098509-9-pizhenwei@bytedance.com>
10
Reviewed-by: Greg Kurz <groug@kaod.org>
11
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
12
---
13
fsdev/qemu-fsdev-throttle.h | 4 ++--
14
fsdev/qemu-fsdev-throttle.c | 14 +++++++-------
15
hw/9pfs/cofile.c | 4 ++--
16
3 files changed, 11 insertions(+), 11 deletions(-)
8
17
9
If it is on, a block device may utilize backend information more
18
diff --git a/fsdev/qemu-fsdev-throttle.h b/fsdev/qemu-fsdev-throttle.h
10
aggressively.
11
12
By default, it is auto, which uses backend information for block
13
sizes and ignores the others, which is consistent with the older
14
versions.
15
16
Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
17
Message-id: 20210705130458.97642-2-akihiko.odaki@gmail.com
18
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
---
20
include/hw/block/block.h | 3 +++
21
hw/block/block.c | 42 ++++++++++++++++++++++++++++++++++----
22
tests/qemu-iotests/172.out | 38 ++++++++++++++++++++++++++++++++++
23
3 files changed, 79 insertions(+), 4 deletions(-)
24
25
diff --git a/include/hw/block/block.h b/include/hw/block/block.h
26
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
27
--- a/include/hw/block/block.h
20
--- a/fsdev/qemu-fsdev-throttle.h
28
+++ b/include/hw/block/block.h
21
+++ b/fsdev/qemu-fsdev-throttle.h
29
@@ -XXX,XX +XXX,XX @@
22
@@ -XXX,XX +XXX,XX @@ typedef struct FsThrottle {
30
23
ThrottleState ts;
31
typedef struct BlockConf {
24
ThrottleTimers tt;
32
BlockBackend *blk;
25
ThrottleConfig cfg;
33
+ OnOffAuto backend_defaults;
26
- CoQueue throttled_reqs[2];
34
uint32_t physical_block_size;
27
+ CoQueue throttled_reqs[THROTTLE_MAX];
35
uint32_t logical_block_size;
28
} FsThrottle;
36
uint32_t min_io_size;
29
37
@@ -XXX,XX +XXX,XX @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
30
int fsdev_throttle_parse_opts(QemuOpts *, FsThrottle *, Error **);
31
32
void fsdev_throttle_init(FsThrottle *);
33
34
-void coroutine_fn fsdev_co_throttle_request(FsThrottle *, bool ,
35
+void coroutine_fn fsdev_co_throttle_request(FsThrottle *, ThrottleDirection ,
36
struct iovec *, int);
37
38
void fsdev_throttle_cleanup(FsThrottle *);
39
diff --git a/fsdev/qemu-fsdev-throttle.c b/fsdev/qemu-fsdev-throttle.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/fsdev/qemu-fsdev-throttle.c
42
+++ b/fsdev/qemu-fsdev-throttle.c
43
@@ -XXX,XX +XXX,XX @@ void fsdev_throttle_init(FsThrottle *fst)
44
}
38
}
45
}
39
46
40
#define DEFINE_BLOCK_PROPERTIES_BASE(_state, _conf) \
47
-void coroutine_fn fsdev_co_throttle_request(FsThrottle *fst, bool is_write,
41
+ DEFINE_PROP_ON_OFF_AUTO("backend_defaults", _state, \
48
+void coroutine_fn fsdev_co_throttle_request(FsThrottle *fst,
42
+ _conf.backend_defaults, ON_OFF_AUTO_AUTO), \
49
+ ThrottleDirection direction,
43
DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \
50
struct iovec *iov, int iovcnt)
44
_conf.logical_block_size), \
45
DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \
46
diff --git a/hw/block/block.c b/hw/block/block.c
47
index XXXXXXX..XXXXXXX 100644
48
--- a/hw/block/block.c
49
+++ b/hw/block/block.c
50
@@ -XXX,XX +XXX,XX @@ bool blkconf_blocksizes(BlockConf *conf, Error **errp)
51
{
51
{
52
BlockBackend *blk = conf->blk;
52
- ThrottleDirection direction = is_write ? THROTTLE_WRITE : THROTTLE_READ;
53
BlockSizes blocksizes;
53
-
54
- int backend_ret;
54
+ assert(direction < THROTTLE_MAX);
55
+ BlockDriverState *bs;
55
if (throttle_enabled(&fst->cfg)) {
56
+ bool use_blocksizes;
56
if (throttle_schedule_timer(&fst->ts, &fst->tt, direction) ||
57
+ bool use_bs;
57
- !qemu_co_queue_empty(&fst->throttled_reqs[is_write])) {
58
+
58
- qemu_co_queue_wait(&fst->throttled_reqs[is_write], NULL);
59
+ switch (conf->backend_defaults) {
59
+ !qemu_co_queue_empty(&fst->throttled_reqs[direction])) {
60
+ case ON_OFF_AUTO_AUTO:
60
+ qemu_co_queue_wait(&fst->throttled_reqs[direction], NULL);
61
+ use_blocksizes = !blk_probe_blocksizes(blk, &blocksizes);
61
}
62
+ use_bs = false;
62
63
+ break;
63
throttle_account(&fst->ts, direction, iov_size(iov, iovcnt));
64
+
64
65
+ case ON_OFF_AUTO_ON:
65
- if (!qemu_co_queue_empty(&fst->throttled_reqs[is_write]) &&
66
+ use_blocksizes = !blk_probe_blocksizes(blk, &blocksizes);
66
+ if (!qemu_co_queue_empty(&fst->throttled_reqs[direction]) &&
67
+ bs = blk_bs(blk);
67
!throttle_schedule_timer(&fst->ts, &fst->tt, direction)) {
68
+ use_bs = bs;
68
- qemu_co_queue_next(&fst->throttled_reqs[is_write]);
69
+ break;
69
+ qemu_co_queue_next(&fst->throttled_reqs[direction]);
70
+
71
+ case ON_OFF_AUTO_OFF:
72
+ use_blocksizes = false;
73
+ use_bs = false;
74
+ break;
75
+
76
+ default:
77
+ abort();
78
+ }
79
80
- backend_ret = blk_probe_blocksizes(blk, &blocksizes);
81
/* fill in detected values if they are not defined via qemu command line */
82
if (!conf->physical_block_size) {
83
- if (!backend_ret) {
84
+ if (use_blocksizes) {
85
conf->physical_block_size = blocksizes.phys;
86
} else {
87
conf->physical_block_size = BDRV_SECTOR_SIZE;
88
}
70
}
89
}
71
}
90
if (!conf->logical_block_size) {
72
}
91
- if (!backend_ret) {
73
diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c
92
+ if (use_blocksizes) {
74
index XXXXXXX..XXXXXXX 100644
93
conf->logical_block_size = blocksizes.log;
75
--- a/hw/9pfs/cofile.c
94
} else {
76
+++ b/hw/9pfs/cofile.c
95
conf->logical_block_size = BDRV_SECTOR_SIZE;
77
@@ -XXX,XX +XXX,XX @@ int coroutine_fn v9fs_co_pwritev(V9fsPDU *pdu, V9fsFidState *fidp,
96
}
78
if (v9fs_request_cancelled(pdu)) {
79
return -EINTR;
97
}
80
}
98
+ if (use_bs) {
81
- fsdev_co_throttle_request(s->ctx.fst, true, iov, iovcnt);
99
+ if (!conf->opt_io_size) {
82
+ fsdev_co_throttle_request(s->ctx.fst, THROTTLE_WRITE, iov, iovcnt);
100
+ conf->opt_io_size = bs->bl.opt_transfer;
83
v9fs_co_run_in_worker(
101
+ }
84
{
102
+ if (conf->discard_granularity == -1) {
85
err = s->ops->pwritev(&s->ctx, &fidp->fs, iov, iovcnt, offset);
103
+ if (bs->bl.pdiscard_alignment) {
86
@@ -XXX,XX +XXX,XX @@ int coroutine_fn v9fs_co_preadv(V9fsPDU *pdu, V9fsFidState *fidp,
104
+ conf->discard_granularity = bs->bl.pdiscard_alignment;
87
if (v9fs_request_cancelled(pdu)) {
105
+ } else if (bs->bl.request_alignment != 1) {
88
return -EINTR;
106
+ conf->discard_granularity = bs->bl.request_alignment;
89
}
107
+ }
90
- fsdev_co_throttle_request(s->ctx.fst, false, iov, iovcnt);
108
+ }
91
+ fsdev_co_throttle_request(s->ctx.fst, THROTTLE_READ, iov, iovcnt);
109
+ }
92
v9fs_co_run_in_worker(
110
93
{
111
if (conf->logical_block_size > conf->physical_block_size) {
94
err = s->ops->preadv(&s->ctx, &fidp->fs, iov, iovcnt, offset);
112
error_setg(errp,
113
diff --git a/tests/qemu-iotests/172.out b/tests/qemu-iotests/172.out
114
index XXXXXXX..XXXXXXX 100644
115
--- a/tests/qemu-iotests/172.out
116
+++ b/tests/qemu-iotests/172.out
117
@@ -XXX,XX +XXX,XX @@ Testing:
118
dev: floppy, id ""
119
unit = 0 (0x0)
120
drive = "floppy0"
121
+ backend_defaults = "auto"
122
logical_block_size = 512 (512 B)
123
physical_block_size = 512 (512 B)
124
min_io_size = 0 (0 B)
125
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2
126
dev: floppy, id ""
127
unit = 0 (0x0)
128
drive = "floppy0"
129
+ backend_defaults = "auto"
130
logical_block_size = 512 (512 B)
131
physical_block_size = 512 (512 B)
132
min_io_size = 0 (0 B)
133
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2
134
dev: floppy, id ""
135
unit = 1 (0x1)
136
drive = "floppy1"
137
+ backend_defaults = "auto"
138
logical_block_size = 512 (512 B)
139
physical_block_size = 512 (512 B)
140
min_io_size = 0 (0 B)
141
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2
142
dev: floppy, id ""
143
unit = 0 (0x0)
144
drive = "floppy0"
145
+ backend_defaults = "auto"
146
logical_block_size = 512 (512 B)
147
physical_block_size = 512 (512 B)
148
min_io_size = 0 (0 B)
149
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2
150
dev: floppy, id ""
151
unit = 1 (0x1)
152
drive = "floppy1"
153
+ backend_defaults = "auto"
154
logical_block_size = 512 (512 B)
155
physical_block_size = 512 (512 B)
156
min_io_size = 0 (0 B)
157
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2.2
158
dev: floppy, id ""
159
unit = 0 (0x0)
160
drive = "floppy0"
161
+ backend_defaults = "auto"
162
logical_block_size = 512 (512 B)
163
physical_block_size = 512 (512 B)
164
min_io_size = 0 (0 B)
165
@@ -XXX,XX +XXX,XX @@ Testing: -fdb
166
dev: floppy, id ""
167
unit = 1 (0x1)
168
drive = "floppy1"
169
+ backend_defaults = "auto"
170
logical_block_size = 512 (512 B)
171
physical_block_size = 512 (512 B)
172
min_io_size = 0 (0 B)
173
@@ -XXX,XX +XXX,XX @@ Testing: -fdb
174
dev: floppy, id ""
175
unit = 0 (0x0)
176
drive = "floppy0"
177
+ backend_defaults = "auto"
178
logical_block_size = 512 (512 B)
179
physical_block_size = 512 (512 B)
180
min_io_size = 0 (0 B)
181
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2
182
dev: floppy, id ""
183
unit = 0 (0x0)
184
drive = "floppy0"
185
+ backend_defaults = "auto"
186
logical_block_size = 512 (512 B)
187
physical_block_size = 512 (512 B)
188
min_io_size = 0 (0 B)
189
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
190
dev: floppy, id ""
191
unit = 1 (0x1)
192
drive = "floppy1"
193
+ backend_defaults = "auto"
194
logical_block_size = 512 (512 B)
195
physical_block_size = 512 (512 B)
196
min_io_size = 0 (0 B)
197
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1
198
dev: floppy, id ""
199
unit = 0 (0x0)
200
drive = "floppy0"
201
+ backend_defaults = "auto"
202
logical_block_size = 512 (512 B)
203
physical_block_size = 512 (512 B)
204
min_io_size = 0 (0 B)
205
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t
206
dev: floppy, id ""
207
unit = 1 (0x1)
208
drive = "floppy1"
209
+ backend_defaults = "auto"
210
logical_block_size = 512 (512 B)
211
physical_block_size = 512 (512 B)
212
min_io_size = 0 (0 B)
213
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t
214
dev: floppy, id ""
215
unit = 0 (0x0)
216
drive = "floppy0"
217
+ backend_defaults = "auto"
218
logical_block_size = 512 (512 B)
219
physical_block_size = 512 (512 B)
220
min_io_size = 0 (0 B)
221
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0
222
dev: floppy, id ""
223
unit = 0 (0x0)
224
drive = "none0"
225
+ backend_defaults = "auto"
226
logical_block_size = 512 (512 B)
227
physical_block_size = 512 (512 B)
228
min_io_size = 0 (0 B)
229
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1
230
dev: floppy, id ""
231
unit = 1 (0x1)
232
drive = "none0"
233
+ backend_defaults = "auto"
234
logical_block_size = 512 (512 B)
235
physical_block_size = 512 (512 B)
236
min_io_size = 0 (0 B)
237
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
238
dev: floppy, id ""
239
unit = 1 (0x1)
240
drive = "none1"
241
+ backend_defaults = "auto"
242
logical_block_size = 512 (512 B)
243
physical_block_size = 512 (512 B)
244
min_io_size = 0 (0 B)
245
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qco
246
dev: floppy, id ""
247
unit = 0 (0x0)
248
drive = "none0"
249
+ backend_defaults = "auto"
250
logical_block_size = 512 (512 B)
251
physical_block_size = 512 (512 B)
252
min_io_size = 0 (0 B)
253
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
254
dev: floppy, id ""
255
unit = 1 (0x1)
256
drive = "none0"
257
+ backend_defaults = "auto"
258
logical_block_size = 512 (512 B)
259
physical_block_size = 512 (512 B)
260
min_io_size = 0 (0 B)
261
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
262
dev: floppy, id ""
263
unit = 0 (0x0)
264
drive = "floppy0"
265
+ backend_defaults = "auto"
266
logical_block_size = 512 (512 B)
267
physical_block_size = 512 (512 B)
268
min_io_size = 0 (0 B)
269
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
270
dev: floppy, id ""
271
unit = 1 (0x1)
272
drive = "none0"
273
+ backend_defaults = "auto"
274
logical_block_size = 512 (512 B)
275
physical_block_size = 512 (512 B)
276
min_io_size = 0 (0 B)
277
@@ -XXX,XX +XXX,XX @@ Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
278
dev: floppy, id ""
279
unit = 0 (0x0)
280
drive = "floppy0"
281
+ backend_defaults = "auto"
282
logical_block_size = 512 (512 B)
283
physical_block_size = 512 (512 B)
284
min_io_size = 0 (0 B)
285
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
286
dev: floppy, id ""
287
unit = 0 (0x0)
288
drive = "none0"
289
+ backend_defaults = "auto"
290
logical_block_size = 512 (512 B)
291
physical_block_size = 512 (512 B)
292
min_io_size = 0 (0 B)
293
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
294
dev: floppy, id ""
295
unit = 1 (0x1)
296
drive = "floppy1"
297
+ backend_defaults = "auto"
298
logical_block_size = 512 (512 B)
299
physical_block_size = 512 (512 B)
300
min_io_size = 0 (0 B)
301
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
302
dev: floppy, id ""
303
unit = 0 (0x0)
304
drive = "none0"
305
+ backend_defaults = "auto"
306
logical_block_size = 512 (512 B)
307
physical_block_size = 512 (512 B)
308
min_io_size = 0 (0 B)
309
@@ -XXX,XX +XXX,XX @@ Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2.2 -device fl
310
dev: floppy, id ""
311
unit = 1 (0x1)
312
drive = "floppy1"
313
+ backend_defaults = "auto"
314
logical_block_size = 512 (512 B)
315
physical_block_size = 512 (512 B)
316
min_io_size = 0 (0 B)
317
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
318
dev: floppy, id ""
319
unit = 1 (0x1)
320
drive = "none0"
321
+ backend_defaults = "auto"
322
logical_block_size = 512 (512 B)
323
physical_block_size = 512 (512 B)
324
min_io_size = 0 (0 B)
325
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
326
dev: floppy, id ""
327
unit = 0 (0x0)
328
drive = "floppy0"
329
+ backend_defaults = "auto"
330
logical_block_size = 512 (512 B)
331
physical_block_size = 512 (512 B)
332
min_io_size = 0 (0 B)
333
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
334
dev: floppy, id ""
335
unit = 1 (0x1)
336
drive = "none0"
337
+ backend_defaults = "auto"
338
logical_block_size = 512 (512 B)
339
physical_block_size = 512 (512 B)
340
min_io_size = 0 (0 B)
341
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.q
342
dev: floppy, id ""
343
unit = 0 (0x0)
344
drive = "floppy0"
345
+ backend_defaults = "auto"
346
logical_block_size = 512 (512 B)
347
physical_block_size = 512 (512 B)
348
min_io_size = 0 (0 B)
349
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global floppy.drive=none0 -device
350
dev: floppy, id ""
351
unit = 0 (0x0)
352
drive = "none0"
353
+ backend_defaults = "auto"
354
logical_block_size = 512 (512 B)
355
physical_block_size = 512 (512 B)
356
min_io_size = 0 (0 B)
357
@@ -XXX,XX +XXX,XX @@ Testing: -device floppy
358
dev: floppy, id ""
359
unit = 0 (0x0)
360
drive = ""
361
+ backend_defaults = "auto"
362
logical_block_size = 512 (512 B)
363
physical_block_size = 512 (512 B)
364
min_io_size = 0 (0 B)
365
@@ -XXX,XX +XXX,XX @@ Testing: -device floppy,drive-type=120
366
dev: floppy, id ""
367
unit = 0 (0x0)
368
drive = ""
369
+ backend_defaults = "auto"
370
logical_block_size = 512 (512 B)
371
physical_block_size = 512 (512 B)
372
min_io_size = 0 (0 B)
373
@@ -XXX,XX +XXX,XX @@ Testing: -device floppy,drive-type=144
374
dev: floppy, id ""
375
unit = 0 (0x0)
376
drive = ""
377
+ backend_defaults = "auto"
378
logical_block_size = 512 (512 B)
379
physical_block_size = 512 (512 B)
380
min_io_size = 0 (0 B)
381
@@ -XXX,XX +XXX,XX @@ Testing: -device floppy,drive-type=288
382
dev: floppy, id ""
383
unit = 0 (0x0)
384
drive = ""
385
+ backend_defaults = "auto"
386
logical_block_size = 512 (512 B)
387
physical_block_size = 512 (512 B)
388
min_io_size = 0 (0 B)
389
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t
390
dev: floppy, id ""
391
unit = 0 (0x0)
392
drive = "none0"
393
+ backend_defaults = "auto"
394
logical_block_size = 512 (512 B)
395
physical_block_size = 512 (512 B)
396
min_io_size = 0 (0 B)
397
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-t
398
dev: floppy, id ""
399
unit = 0 (0x0)
400
drive = "none0"
401
+ backend_defaults = "auto"
402
logical_block_size = 512 (512 B)
403
physical_block_size = 512 (512 B)
404
min_io_size = 0 (0 B)
405
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical
406
dev: floppy, id ""
407
unit = 0 (0x0)
408
drive = "none0"
409
+ backend_defaults = "auto"
410
logical_block_size = 512 (512 B)
411
physical_block_size = 512 (512 B)
412
min_io_size = 0 (0 B)
413
@@ -XXX,XX +XXX,XX @@ Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physica
414
dev: floppy, id ""
415
unit = 0 (0x0)
416
drive = "none0"
417
+ backend_defaults = "auto"
418
logical_block_size = 512 (512 B)
419
physical_block_size = 512 (512 B)
420
min_io_size = 0 (0 B)
421
--
95
--
422
2.31.1
96
2.41.0
423
diff view generated by jsdifflib
New patch
1
1
From: zhenwei pi <pizhenwei@bytedance.com>
2
3
'bool is_write' style is obsolete from throttle framework, adapt
4
block throttle groups to the new style:
5
- use ThrottleDirection instead of 'bool is_write'. Ex,
6
schedule_next_request(ThrottleGroupMember *tgm, bool is_write)
7
-> schedule_next_request(ThrottleGroupMember *tgm, ThrottleDirection direction)
8
9
- use THROTTLE_MAX instead of hard code. Ex, ThrottleGroupMember *tokens[2]
10
-> ThrottleGroupMember *tokens[THROTTLE_MAX]
11
12
- use ThrottleDirection instead of hard code on iteration. Ex, (i = 0; i < 2; i++)
13
-> for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++)
14
15
Use a simple python script to test the new style:
16
#!/usr/bin/python3
17
import subprocess
18
import random
19
import time
20
21
commands = ['virsh blkdeviotune jammy vda --write-bytes-sec ', \
22
'virsh blkdeviotune jammy vda --write-iops-sec ', \
23
'virsh blkdeviotune jammy vda --read-bytes-sec ', \
24
'virsh blkdeviotune jammy vda --read-iops-sec ']
25
26
for loop in range(1, 1000):
27
time.sleep(random.randrange(3, 5))
28
command = commands[random.randrange(0, 3)] + str(random.randrange(0, 1000000))
29
subprocess.run(command, shell=True, check=True)
30
31
This works fine.
32
33
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
34
Message-Id: <20230728022006.1098509-10-pizhenwei@bytedance.com>
35
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
36
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
37
---
38
include/block/throttle-groups.h | 6 +-
39
block/block-backend.c | 4 +-
40
block/throttle-groups.c | 161 ++++++++++++++++----------------
41
block/throttle.c | 8 +-
42
4 files changed, 90 insertions(+), 89 deletions(-)
43
44
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
45
index XXXXXXX..XXXXXXX 100644
46
--- a/include/block/throttle-groups.h
47
+++ b/include/block/throttle-groups.h
48
@@ -XXX,XX +XXX,XX @@ typedef struct ThrottleGroupMember {
49
AioContext *aio_context;
50
/* throttled_reqs_lock protects the CoQueues for throttled requests. */
51
CoMutex throttled_reqs_lock;
52
- CoQueue throttled_reqs[2];
53
+ CoQueue throttled_reqs[THROTTLE_MAX];
54
55
/* Nonzero if the I/O limits are currently being ignored; generally
56
* it is zero. Accessed with atomic operations.
57
@@ -XXX,XX +XXX,XX @@ typedef struct ThrottleGroupMember {
58
* throttle_state tells us if I/O limits are configured. */
59
ThrottleState *throttle_state;
60
ThrottleTimers throttle_timers;
61
- unsigned pending_reqs[2];
62
+ unsigned pending_reqs[THROTTLE_MAX];
63
QLIST_ENTRY(ThrottleGroupMember) round_robin;
64
65
} ThrottleGroupMember;
66
@@ -XXX,XX +XXX,XX @@ void throttle_group_restart_tgm(ThrottleGroupMember *tgm);
67
68
void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm,
69
int64_t bytes,
70
- bool is_write);
71
+ ThrottleDirection direction);
72
void throttle_group_attach_aio_context(ThrottleGroupMember *tgm,
73
AioContext *new_context);
74
void throttle_group_detach_aio_context(ThrottleGroupMember *tgm);
75
diff --git a/block/block-backend.c b/block/block-backend.c
76
index XXXXXXX..XXXXXXX 100644
77
--- a/block/block-backend.c
78
+++ b/block/block-backend.c
79
@@ -XXX,XX +XXX,XX @@ blk_co_do_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes,
80
/* throttling disk I/O */
81
if (blk->public.throttle_group_member.throttle_state) {
82
throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
83
- bytes, false);
84
+ bytes, THROTTLE_READ);
85
}
86
87
ret = bdrv_co_preadv_part(blk->root, offset, bytes, qiov, qiov_offset,
88
@@ -XXX,XX +XXX,XX @@ blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes,
89
/* throttling disk I/O */
90
if (blk->public.throttle_group_member.throttle_state) {
91
throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member,
92
- bytes, true);
93
+ bytes, THROTTLE_WRITE);
94
}
95
96
if (!blk->enable_write_cache) {
97
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
98
index XXXXXXX..XXXXXXX 100644
99
--- a/block/throttle-groups.c
100
+++ b/block/throttle-groups.c
101
@@ -XXX,XX +XXX,XX @@
102
103
static void throttle_group_obj_init(Object *obj);
104
static void throttle_group_obj_complete(UserCreatable *obj, Error **errp);
105
-static void timer_cb(ThrottleGroupMember *tgm, bool is_write);
106
+static void timer_cb(ThrottleGroupMember *tgm, ThrottleDirection direction);
107
108
/* The ThrottleGroup structure (with its ThrottleState) is shared
109
* among different ThrottleGroupMembers and it's independent from
110
@@ -XXX,XX +XXX,XX @@ struct ThrottleGroup {
111
QemuMutex lock; /* This lock protects the following four fields */
112
ThrottleState ts;
113
QLIST_HEAD(, ThrottleGroupMember) head;
114
- ThrottleGroupMember *tokens[2];
115
- bool any_timer_armed[2];
116
+ ThrottleGroupMember *tokens[THROTTLE_MAX];
117
+ bool any_timer_armed[THROTTLE_MAX];
118
QEMUClockType clock_type;
119
120
/* This field is protected by the global QEMU mutex */
121
@@ -XXX,XX +XXX,XX @@ static ThrottleGroupMember *throttle_group_next_tgm(ThrottleGroupMember *tgm)
122
* This assumes that tg->lock is held.
123
*
124
* @tgm: the ThrottleGroupMember
125
- * @is_write: the type of operation (read/write)
126
+ * @direction: the ThrottleDirection
127
* @ret: whether the ThrottleGroupMember has pending requests.
128
*/
129
static inline bool tgm_has_pending_reqs(ThrottleGroupMember *tgm,
130
- bool is_write)
131
+ ThrottleDirection direction)
132
{
133
- return tgm->pending_reqs[is_write];
134
+ return tgm->pending_reqs[direction];
135
}
136
137
/* Return the next ThrottleGroupMember in the round-robin sequence with pending
138
@@ -XXX,XX +XXX,XX @@ static inline bool tgm_has_pending_reqs(ThrottleGroupMember *tgm,
139
* This assumes that tg->lock is held.
140
*
141
* @tgm: the current ThrottleGroupMember
142
- * @is_write: the type of operation (read/write)
143
+ * @direction: the ThrottleDirection
144
* @ret: the next ThrottleGroupMember with pending requests, or tgm if
145
* there is none.
146
*/
147
static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm,
148
- bool is_write)
149
+ ThrottleDirection direction)
150
{
151
ThrottleState *ts = tgm->throttle_state;
152
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
153
@@ -XXX,XX +XXX,XX @@ static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm,
154
* it's being drained. Skip the round-robin search and return tgm
155
* immediately if it has pending requests. Otherwise we could be
156
* forcing it to wait for other member's throttled requests. */
157
- if (tgm_has_pending_reqs(tgm, is_write) &&
158
+ if (tgm_has_pending_reqs(tgm, direction) &&
159
qatomic_read(&tgm->io_limits_disabled)) {
160
return tgm;
161
}
162
163
- start = token = tg->tokens[is_write];
164
+ start = token = tg->tokens[direction];
165
166
/* get next bs round in round robin style */
167
token = throttle_group_next_tgm(token);
168
- while (token != start && !tgm_has_pending_reqs(token, is_write)) {
169
+ while (token != start && !tgm_has_pending_reqs(token, direction)) {
170
token = throttle_group_next_tgm(token);
171
}
172
173
@@ -XXX,XX +XXX,XX @@ static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm,
174
* then decide the token is the current tgm because chances are
175
* the current tgm got the current request queued.
176
*/
177
- if (token == start && !tgm_has_pending_reqs(token, is_write)) {
178
+ if (token == start && !tgm_has_pending_reqs(token, direction)) {
179
token = tgm;
180
}
181
182
/* Either we return the original TGM, or one with pending requests */
183
- assert(token == tgm || tgm_has_pending_reqs(token, is_write));
184
+ assert(token == tgm || tgm_has_pending_reqs(token, direction));
185
186
return token;
187
}
188
@@ -XXX,XX +XXX,XX @@ static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm,
189
* This assumes that tg->lock is held.
190
*
191
* @tgm: the current ThrottleGroupMember
192
- * @is_write: the type of operation (read/write)
193
+ * @direction: the ThrottleDirection
194
* @ret: whether the I/O request needs to be throttled or not
195
*/
196
static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm,
197
- bool is_write)
198
+ ThrottleDirection direction)
199
{
200
ThrottleState *ts = tgm->throttle_state;
201
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
202
ThrottleTimers *tt = &tgm->throttle_timers;
203
- ThrottleDirection direction = is_write ? THROTTLE_WRITE : THROTTLE_READ;
204
bool must_wait;
205
206
if (qatomic_read(&tgm->io_limits_disabled)) {
207
@@ -XXX,XX +XXX,XX @@ static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm,
208
}
209
210
/* Check if any of the timers in this group is already armed */
211
- if (tg->any_timer_armed[is_write]) {
212
+ if (tg->any_timer_armed[direction]) {
213
return true;
214
}
215
216
@@ -XXX,XX +XXX,XX @@ static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm,
217
218
/* If a timer just got armed, set tgm as the current token */
219
if (must_wait) {
220
- tg->tokens[is_write] = tgm;
221
- tg->any_timer_armed[is_write] = true;
222
+ tg->tokens[direction] = tgm;
223
+ tg->any_timer_armed[direction] = true;
224
}
225
226
return must_wait;
227
@@ -XXX,XX +XXX,XX @@ static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm,
228
* any request was actually pending.
229
*
230
* @tgm: the current ThrottleGroupMember
231
- * @is_write: the type of operation (read/write)
232
+ * @direction: the ThrottleDirection
233
*/
234
static bool coroutine_fn throttle_group_co_restart_queue(ThrottleGroupMember *tgm,
235
- bool is_write)
236
+ ThrottleDirection direction)
237
{
238
bool ret;
239
240
qemu_co_mutex_lock(&tgm->throttled_reqs_lock);
241
- ret = qemu_co_queue_next(&tgm->throttled_reqs[is_write]);
242
+ ret = qemu_co_queue_next(&tgm->throttled_reqs[direction]);
243
qemu_co_mutex_unlock(&tgm->throttled_reqs_lock);
244
245
return ret;
246
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn throttle_group_co_restart_queue(ThrottleGroupMember *tg
247
* This assumes that tg->lock is held.
248
*
249
* @tgm: the current ThrottleGroupMember
250
- * @is_write: the type of operation (read/write)
251
+ * @direction: the ThrottleDirection
252
*/
253
-static void schedule_next_request(ThrottleGroupMember *tgm, bool is_write)
254
+static void schedule_next_request(ThrottleGroupMember *tgm,
255
+ ThrottleDirection direction)
256
{
257
ThrottleState *ts = tgm->throttle_state;
258
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
259
@@ -XXX,XX +XXX,XX @@ static void schedule_next_request(ThrottleGroupMember *tgm, bool is_write)
260
ThrottleGroupMember *token;
261
262
/* Check if there's any pending request to schedule next */
263
- token = next_throttle_token(tgm, is_write);
264
- if (!tgm_has_pending_reqs(token, is_write)) {
265
+ token = next_throttle_token(tgm, direction);
266
+ if (!tgm_has_pending_reqs(token, direction)) {
267
return;
268
}
269
270
/* Set a timer for the request if it needs to be throttled */
271
- must_wait = throttle_group_schedule_timer(token, is_write);
272
+ must_wait = throttle_group_schedule_timer(token, direction);
273
274
/* If it doesn't have to wait, queue it for immediate execution */
275
if (!must_wait) {
276
/* Give preference to requests from the current tgm */
277
if (qemu_in_coroutine() &&
278
- throttle_group_co_restart_queue(tgm, is_write)) {
279
+ throttle_group_co_restart_queue(tgm, direction)) {
280
token = tgm;
281
} else {
282
ThrottleTimers *tt = &token->throttle_timers;
283
int64_t now = qemu_clock_get_ns(tg->clock_type);
284
- timer_mod(tt->timers[is_write], now);
285
- tg->any_timer_armed[is_write] = true;
286
+ timer_mod(tt->timers[direction], now);
287
+ tg->any_timer_armed[direction] = true;
288
}
289
- tg->tokens[is_write] = token;
290
+ tg->tokens[direction] = token;
291
}
292
}
293
294
@@ -XXX,XX +XXX,XX @@ static void schedule_next_request(ThrottleGroupMember *tgm, bool is_write)
295
*
296
* @tgm: the current ThrottleGroupMember
297
* @bytes: the number of bytes for this I/O
298
- * @is_write: the type of operation (read/write)
299
+ * @direction: the ThrottleDirection
300
*/
301
void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm,
302
int64_t bytes,
303
- bool is_write)
304
+ ThrottleDirection direction)
305
{
306
bool must_wait;
307
ThrottleGroupMember *token;
308
ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts);
309
- ThrottleDirection direction = is_write ? THROTTLE_WRITE : THROTTLE_READ;
310
311
assert(bytes >= 0);
312
+ assert(direction < THROTTLE_MAX);
313
314
qemu_mutex_lock(&tg->lock);
315
316
/* First we check if this I/O has to be throttled. */
317
- token = next_throttle_token(tgm, is_write);
318
- must_wait = throttle_group_schedule_timer(token, is_write);
319
+ token = next_throttle_token(tgm, direction);
320
+ must_wait = throttle_group_schedule_timer(token, direction);
321
322
/* Wait if there's a timer set or queued requests of this type */
323
- if (must_wait || tgm->pending_reqs[is_write]) {
324
- tgm->pending_reqs[is_write]++;
325
+ if (must_wait || tgm->pending_reqs[direction]) {
326
+ tgm->pending_reqs[direction]++;
327
qemu_mutex_unlock(&tg->lock);
328
qemu_co_mutex_lock(&tgm->throttled_reqs_lock);
329
- qemu_co_queue_wait(&tgm->throttled_reqs[is_write],
330
+ qemu_co_queue_wait(&tgm->throttled_reqs[direction],
331
&tgm->throttled_reqs_lock);
332
qemu_co_mutex_unlock(&tgm->throttled_reqs_lock);
333
qemu_mutex_lock(&tg->lock);
334
- tgm->pending_reqs[is_write]--;
335
+ tgm->pending_reqs[direction]--;
336
}
337
338
/* The I/O will be executed, so do the accounting */
339
throttle_account(tgm->throttle_state, direction, bytes);
340
341
/* Schedule the next request */
342
- schedule_next_request(tgm, is_write);
343
+ schedule_next_request(tgm, direction);
344
345
qemu_mutex_unlock(&tg->lock);
346
}
347
348
typedef struct {
349
ThrottleGroupMember *tgm;
350
- bool is_write;
351
+ ThrottleDirection direction;
352
} RestartData;
353
354
static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
355
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
356
ThrottleGroupMember *tgm = data->tgm;
357
ThrottleState *ts = tgm->throttle_state;
358
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
359
- bool is_write = data->is_write;
360
+ ThrottleDirection direction = data->direction;
361
bool empty_queue;
362
363
- empty_queue = !throttle_group_co_restart_queue(tgm, is_write);
364
+ empty_queue = !throttle_group_co_restart_queue(tgm, direction);
365
366
/* If the request queue was empty then we have to take care of
367
* scheduling the next one */
368
if (empty_queue) {
369
qemu_mutex_lock(&tg->lock);
370
- schedule_next_request(tgm, is_write);
371
+ schedule_next_request(tgm, direction);
372
qemu_mutex_unlock(&tg->lock);
373
}
374
375
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
376
aio_wait_kick();
377
}
378
379
-static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write)
380
+static void throttle_group_restart_queue(ThrottleGroupMember *tgm,
381
+ ThrottleDirection direction)
382
{
383
Coroutine *co;
384
RestartData *rd = g_new0(RestartData, 1);
385
386
rd->tgm = tgm;
387
- rd->is_write = is_write;
388
+ rd->direction = direction;
389
390
/* This function is called when a timer is fired or when
391
* throttle_group_restart_tgm() is called. Either way, there can
392
* be no timer pending on this tgm at this point */
393
- assert(!timer_pending(tgm->throttle_timers.timers[is_write]));
394
+ assert(!timer_pending(tgm->throttle_timers.timers[direction]));
395
396
qatomic_inc(&tgm->restart_pending);
397
398
@@ -XXX,XX +XXX,XX @@ static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write
399
400
void throttle_group_restart_tgm(ThrottleGroupMember *tgm)
401
{
402
- int i;
403
+ ThrottleDirection dir;
404
405
if (tgm->throttle_state) {
406
- for (i = 0; i < 2; i++) {
407
- QEMUTimer *t = tgm->throttle_timers.timers[i];
408
+ for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) {
409
+ QEMUTimer *t = tgm->throttle_timers.timers[dir];
410
if (timer_pending(t)) {
411
/* If there's a pending timer on this tgm, fire it now */
412
timer_del(t);
413
- timer_cb(tgm, i);
414
+ timer_cb(tgm, dir);
415
} else {
416
/* Else run the next request from the queue manually */
417
- throttle_group_restart_queue(tgm, i);
418
+ throttle_group_restart_queue(tgm, dir);
419
}
420
}
421
}
422
@@ -XXX,XX +XXX,XX @@ void throttle_group_get_config(ThrottleGroupMember *tgm, ThrottleConfig *cfg)
423
* because it had been throttled.
424
*
425
* @tgm: the ThrottleGroupMember whose request had been throttled
426
- * @is_write: the type of operation (read/write)
427
+ * @direction: the ThrottleDirection
428
*/
429
-static void timer_cb(ThrottleGroupMember *tgm, bool is_write)
430
+static void timer_cb(ThrottleGroupMember *tgm, ThrottleDirection direction)
431
{
432
ThrottleState *ts = tgm->throttle_state;
433
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
434
435
/* The timer has just been fired, so we can update the flag */
436
qemu_mutex_lock(&tg->lock);
437
- tg->any_timer_armed[is_write] = false;
438
+ tg->any_timer_armed[direction] = false;
439
qemu_mutex_unlock(&tg->lock);
440
441
/* Run the request that was waiting for this timer */
442
- throttle_group_restart_queue(tgm, is_write);
443
+ throttle_group_restart_queue(tgm, direction);
444
}
445
446
static void read_timer_cb(void *opaque)
447
{
448
- timer_cb(opaque, false);
449
+ timer_cb(opaque, THROTTLE_READ);
450
}
451
452
static void write_timer_cb(void *opaque)
453
{
454
- timer_cb(opaque, true);
455
+ timer_cb(opaque, THROTTLE_WRITE);
456
}
457
458
/* Register a ThrottleGroupMember from the throttling group, also initializing
459
@@ -XXX,XX +XXX,XX @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
460
const char *groupname,
461
AioContext *ctx)
462
{
463
- int i;
464
+ ThrottleDirection dir;
465
ThrottleState *ts = throttle_group_incref(groupname);
466
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
467
468
@@ -XXX,XX +XXX,XX @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
469
470
QEMU_LOCK_GUARD(&tg->lock);
471
/* If the ThrottleGroup is new set this ThrottleGroupMember as the token */
472
- for (i = 0; i < 2; i++) {
473
- if (!tg->tokens[i]) {
474
- tg->tokens[i] = tgm;
475
+ for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) {
476
+ if (!tg->tokens[dir]) {
477
+ tg->tokens[dir] = tgm;
478
}
479
+ qemu_co_queue_init(&tgm->throttled_reqs[dir]);
480
}
481
482
QLIST_INSERT_HEAD(&tg->head, tgm, round_robin);
483
@@ -XXX,XX +XXX,XX @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
484
write_timer_cb,
485
tgm);
486
qemu_co_mutex_init(&tgm->throttled_reqs_lock);
487
- qemu_co_queue_init(&tgm->throttled_reqs[0]);
488
- qemu_co_queue_init(&tgm->throttled_reqs[1]);
489
}
490
491
/* Unregister a ThrottleGroupMember from its group, removing it from the list,
492
@@ -XXX,XX +XXX,XX @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
493
ThrottleState *ts = tgm->throttle_state;
494
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
495
ThrottleGroupMember *token;
496
- int i;
497
+ ThrottleDirection dir;
498
499
if (!ts) {
500
/* Discard already unregistered tgm */
501
@@ -XXX,XX +XXX,XX @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
502
AIO_WAIT_WHILE(tgm->aio_context, qatomic_read(&tgm->restart_pending) > 0);
503
504
WITH_QEMU_LOCK_GUARD(&tg->lock) {
505
- for (i = 0; i < 2; i++) {
506
- assert(tgm->pending_reqs[i] == 0);
507
- assert(qemu_co_queue_empty(&tgm->throttled_reqs[i]));
508
- assert(!timer_pending(tgm->throttle_timers.timers[i]));
509
- if (tg->tokens[i] == tgm) {
510
+ for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) {
511
+ assert(tgm->pending_reqs[dir] == 0);
512
+ assert(qemu_co_queue_empty(&tgm->throttled_reqs[dir]));
513
+ assert(!timer_pending(tgm->throttle_timers.timers[dir]));
514
+ if (tg->tokens[dir] == tgm) {
515
token = throttle_group_next_tgm(tgm);
516
/* Take care of the case where this is the last tgm in the group */
517
if (token == tgm) {
518
token = NULL;
519
}
520
- tg->tokens[i] = token;
521
+ tg->tokens[dir] = token;
522
}
523
}
524
525
@@ -XXX,XX +XXX,XX @@ void throttle_group_detach_aio_context(ThrottleGroupMember *tgm)
526
{
527
ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts);
528
ThrottleTimers *tt = &tgm->throttle_timers;
529
- int i;
530
+ ThrottleDirection dir;
531
532
/* Requests must have been drained */
533
- assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0);
534
- assert(qemu_co_queue_empty(&tgm->throttled_reqs[0]));
535
- assert(qemu_co_queue_empty(&tgm->throttled_reqs[1]));
536
+ for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) {
537
+ assert(tgm->pending_reqs[dir] == 0);
538
+ assert(qemu_co_queue_empty(&tgm->throttled_reqs[dir]));
539
+ }
540
541
/* Kick off next ThrottleGroupMember, if necessary */
542
WITH_QEMU_LOCK_GUARD(&tg->lock) {
543
- for (i = 0; i < 2; i++) {
544
- if (timer_pending(tt->timers[i])) {
545
- tg->any_timer_armed[i] = false;
546
- schedule_next_request(tgm, i);
547
+ for (dir = THROTTLE_READ; dir < THROTTLE_MAX; dir++) {
548
+ if (timer_pending(tt->timers[dir])) {
549
+ tg->any_timer_armed[dir] = false;
550
+ schedule_next_request(tgm, dir);
551
}
552
}
553
}
554
diff --git a/block/throttle.c b/block/throttle.c
555
index XXXXXXX..XXXXXXX 100644
556
--- a/block/throttle.c
557
+++ b/block/throttle.c
558
@@ -XXX,XX +XXX,XX @@ throttle_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
559
{
560
561
ThrottleGroupMember *tgm = bs->opaque;
562
- throttle_group_co_io_limits_intercept(tgm, bytes, false);
563
+ throttle_group_co_io_limits_intercept(tgm, bytes, THROTTLE_READ);
564
565
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
566
}
567
@@ -XXX,XX +XXX,XX @@ throttle_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
568
QEMUIOVector *qiov, BdrvRequestFlags flags)
569
{
570
ThrottleGroupMember *tgm = bs->opaque;
571
- throttle_group_co_io_limits_intercept(tgm, bytes, true);
572
+ throttle_group_co_io_limits_intercept(tgm, bytes, THROTTLE_WRITE);
573
574
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
575
}
576
@@ -XXX,XX +XXX,XX @@ throttle_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes,
577
BdrvRequestFlags flags)
578
{
579
ThrottleGroupMember *tgm = bs->opaque;
580
- throttle_group_co_io_limits_intercept(tgm, bytes, true);
581
+ throttle_group_co_io_limits_intercept(tgm, bytes, THROTTLE_WRITE);
582
583
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
584
}
585
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn GRAPH_RDLOCK
586
throttle_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
587
{
588
ThrottleGroupMember *tgm = bs->opaque;
589
- throttle_group_co_io_limits_intercept(tgm, bytes, true);
590
+ throttle_group_co_io_limits_intercept(tgm, bytes, THROTTLE_WRITE);
591
592
return bdrv_co_pdiscard(bs->file, offset, bytes);
593
}
594
--
595
2.41.0
diff view generated by jsdifflib
New patch
1
bs->bl.zoned is what indicates whether the zone information is present
2
and valid; it is the only thing that raw_refresh_zoned_limits() sets if
3
CONFIG_BLKZONED is not defined, and it is also the only thing that it
4
sets if CONFIG_BLKZONED is defined, but there are no zones.
1
5
6
Make sure that it is always set to BLK_Z_NONE if there is an error
7
anywhere in raw_refresh_zoned_limits() so that we do not accidentally
8
announce zones while our information is incomplete or invalid.
9
10
This also fixes a memory leak in the last error path in
11
raw_refresh_zoned_limits().
12
13
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
14
Message-Id: <20230824155345.109765-2-hreitz@redhat.com>
15
Reviewed-by: Sam Li <faithilikerun@gmail.com>
16
---
17
block/file-posix.c | 21 ++++++++++++---------
18
1 file changed, 12 insertions(+), 9 deletions(-)
19
20
diff --git a/block/file-posix.c b/block/file-posix.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/block/file-posix.c
23
+++ b/block/file-posix.c
24
@@ -XXX,XX +XXX,XX @@ static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st,
25
BlockZoneModel zoned;
26
int ret;
27
28
- bs->bl.zoned = BLK_Z_NONE;
29
-
30
ret = get_sysfs_zoned_model(st, &zoned);
31
if (ret < 0 || zoned == BLK_Z_NONE) {
32
- return;
33
+ goto no_zoned;
34
}
35
bs->bl.zoned = zoned;
36
37
@@ -XXX,XX +XXX,XX @@ static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st,
38
if (ret < 0) {
39
error_setg_errno(errp, -ret, "Unable to read chunk_sectors "
40
"sysfs attribute");
41
- return;
42
+ goto no_zoned;
43
} else if (!ret) {
44
error_setg(errp, "Read 0 from chunk_sectors sysfs attribute");
45
- return;
46
+ goto no_zoned;
47
}
48
bs->bl.zone_size = ret << BDRV_SECTOR_BITS;
49
50
@@ -XXX,XX +XXX,XX @@ static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st,
51
if (ret < 0) {
52
error_setg_errno(errp, -ret, "Unable to read nr_zones "
53
"sysfs attribute");
54
- return;
55
+ goto no_zoned;
56
} else if (!ret) {
57
error_setg(errp, "Read 0 from nr_zones sysfs attribute");
58
- return;
59
+ goto no_zoned;
60
}
61
bs->bl.nr_zones = ret;
62
63
@@ -XXX,XX +XXX,XX @@ static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st,
64
ret = get_zones_wp(bs, s->fd, 0, bs->bl.nr_zones, 0);
65
if (ret < 0) {
66
error_setg_errno(errp, -ret, "report wps failed");
67
- bs->wps = NULL;
68
- return;
69
+ goto no_zoned;
70
}
71
qemu_co_mutex_init(&bs->wps->colock);
72
+ return;
73
+
74
+no_zoned:
75
+ bs->bl.zoned = BLK_Z_NONE;
76
+ g_free(bs->wps);
77
+ bs->wps = NULL;
78
}
79
#else /* !defined(CONFIG_BLKZONED) */
80
static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st,
81
--
82
2.41.0
diff view generated by jsdifflib
1
From: Akihiko Odaki <akihiko.odaki@gmail.com>
1
Instead of checking bs->wps or bs->bl.zone_size for whether zone
2
information is present, check bs->bl.zoned. That is the flag that
3
raw_refresh_zoned_limits() reliably sets to indicate zone support. If
4
it is set to something other than BLK_Z_NONE, other values and objects
5
like bs->wps and bs->bl.zone_size must be non-null/zero and valid; if it
6
is not, we cannot rely on their validity.
2
7
3
This commit introduces "punch hole" operation and optimizes transfer
8
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
4
block size for macOS.
9
Message-Id: <20230824155345.109765-3-hreitz@redhat.com>
5
10
Reviewed-by: Sam Li <faithilikerun@gmail.com>
6
Thanks to Konstantin Nazarov for detailed analysis of a flaw in an
7
old version of this change:
8
https://gist.github.com/akihikodaki/87df4149e7ca87f18dc56807ec5a1bc5#gistcomment-3654667
9
10
Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
11
Message-id: 20210705130458.97642-1-akihiko.odaki@gmail.com
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
11
---
14
block/file-posix.c | 27 +++++++++++++++++++++++++--
12
block/file-posix.c | 12 +++++++-----
15
1 file changed, 25 insertions(+), 2 deletions(-)
13
1 file changed, 7 insertions(+), 5 deletions(-)
16
14
17
diff --git a/block/file-posix.c b/block/file-posix.c
15
diff --git a/block/file-posix.c b/block/file-posix.c
18
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
19
--- a/block/file-posix.c
17
--- a/block/file-posix.c
20
+++ b/block/file-posix.c
18
+++ b/block/file-posix.c
21
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset,
22
#if defined(HAVE_HOST_BLOCK_DEVICE)
20
if (fd_open(bs) < 0)
23
#include <paths.h>
21
return -EIO;
24
#include <sys/param.h>
22
#if defined(CONFIG_BLKZONED)
25
+#include <sys/mount.h>
23
- if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) && bs->wps) {
26
#include <IOKit/IOKitLib.h>
24
+ if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) &&
27
#include <IOKit/IOBSD.h>
25
+ bs->bl.zoned != BLK_Z_NONE) {
28
#include <IOKit/storage/IOMediaBSDClient.h>
26
qemu_co_mutex_lock(&bs->wps->colock);
29
@@ -XXX,XX +XXX,XX @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
27
- if (type & QEMU_AIO_ZONE_APPEND && bs->bl.zone_size) {
30
return;
28
+ if (type & QEMU_AIO_ZONE_APPEND) {
29
int index = offset / bs->bl.zone_size;
30
offset = bs->wps->wp[index];
31
}
32
@@ -XXX,XX +XXX,XX @@ out:
33
{
34
BlockZoneWps *wps = bs->wps;
35
if (ret == 0) {
36
- if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND))
37
- && wps && bs->bl.zone_size) {
38
+ if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) &&
39
+ bs->bl.zoned != BLK_Z_NONE) {
40
uint64_t *wp = &wps->wp[offset / bs->bl.zone_size];
41
if (!BDRV_ZT_IS_CONV(*wp)) {
42
if (type & QEMU_AIO_ZONE_APPEND) {
43
@@ -XXX,XX +XXX,XX @@ out:
44
}
31
}
45
}
32
46
33
+#if defined(__APPLE__) && (__MACH__)
47
- if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) && wps) {
34
+ struct statfs buf;
48
+ if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) &&
35
+
49
+ bs->blk.zoned != BLK_Z_NONE) {
36
+ if (!fstatfs(s->fd, &buf)) {
50
qemu_co_mutex_unlock(&wps->colock);
37
+ bs->bl.opt_transfer = buf.f_iosize;
38
+ bs->bl.pdiscard_alignment = buf.f_bsize;
39
+ }
40
+#endif
41
+
42
if (bs->sg || S_ISBLK(st.st_mode)) {
43
int ret = hdev_get_max_hw_transfer(s->fd, &st);
44
45
@@ -XXX,XX +XXX,XX @@ out:
46
}
51
}
47
}
52
}
48
49
+#if defined(CONFIG_FALLOCATE) || defined(BLKZEROOUT) || defined(BLKDISCARD)
50
static int translate_err(int err)
51
{
52
if (err == -ENODEV || err == -ENOSYS || err == -EOPNOTSUPP ||
53
@@ -XXX,XX +XXX,XX @@ static int translate_err(int err)
54
}
55
return err;
56
}
57
+#endif
58
59
#ifdef CONFIG_FALLOCATE
60
static int do_fallocate(int fd, int mode, off_t offset, off_t len)
61
@@ -XXX,XX +XXX,XX @@ static int handle_aiocb_discard(void *opaque)
62
}
63
} while (errno == EINTR);
64
65
- ret = -errno;
66
+ ret = translate_err(-errno);
67
#endif
68
} else {
69
#ifdef CONFIG_FALLOCATE_PUNCH_HOLE
70
ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
71
aiocb->aio_offset, aiocb->aio_nbytes);
72
+ ret = translate_err(-errno);
73
+#elif defined(__APPLE__) && (__MACH__)
74
+ fpunchhole_t fpunchhole;
75
+ fpunchhole.fp_flags = 0;
76
+ fpunchhole.reserved = 0;
77
+ fpunchhole.fp_offset = aiocb->aio_offset;
78
+ fpunchhole.fp_length = aiocb->aio_nbytes;
79
+ if (fcntl(s->fd, F_PUNCHHOLE, &fpunchhole) == -1) {
80
+ ret = errno == ENODEV ? -ENOTSUP : -errno;
81
+ } else {
82
+ ret = 0;
83
+ }
84
#endif
85
}
86
87
- ret = translate_err(ret);
88
if (ret == -ENOTSUP) {
89
s->has_discard = false;
90
}
91
--
53
--
92
2.31.1
54
2.41.0
93
diff view generated by jsdifflib
1
BHs must be deleted before the AioContext is finalized. If not, it's a
1
We must check that zone information is present before running
2
bug and probably indicates that some part of the program still expects
2
update_zones_wp().
3
the BH to run in the future. That can lead to memory leaks, inconsistent
4
state, or just hangs.
5
3
6
Unfortunately the assert(flags & BH_DELETED) call in aio_ctx_finalize()
4
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2234374
7
is difficult to debug because the assertion failure contains no
5
Fixes: Coverity CID 1512459
8
information about the BH!
6
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
7
Message-Id: <20230824155345.109765-4-hreitz@redhat.com>
8
Reviewed-by: Sam Li <faithilikerun@gmail.com>
9
---
10
block/file-posix.c | 3 ++-
11
1 file changed, 2 insertions(+), 1 deletion(-)
9
12
10
Use the QEMUBH name field added in the previous patch to show a useful
13
diff --git a/block/file-posix.c b/block/file-posix.c
11
error when a leaked BH is detected.
12
13
Suggested-by: Eric Ernst <eric.g.ernst@gmail.com>
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Message-Id: <20210414200247.917496-3-stefanha@redhat.com>
16
---
17
util/async.c | 16 ++++++++++++++--
18
1 file changed, 14 insertions(+), 2 deletions(-)
19
20
diff --git a/util/async.c b/util/async.c
21
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
22
--- a/util/async.c
15
--- a/block/file-posix.c
23
+++ b/util/async.c
16
+++ b/block/file-posix.c
24
@@ -XXX,XX +XXX,XX @@ aio_ctx_finalize(GSource *source)
17
@@ -XXX,XX +XXX,XX @@ out:
25
assert(QSIMPLEQ_EMPTY(&ctx->bh_slice_list));
18
}
26
19
}
27
while ((bh = aio_bh_dequeue(&ctx->bh_list, &flags))) {
20
} else {
28
- /* qemu_bh_delete() must have been called on BHs in this AioContext */
21
- if (type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) {
29
- assert(flags & BH_DELETED);
22
+ if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) &&
30
+ /*
23
+ bs->bl.zoned != BLK_Z_NONE) {
31
+ * qemu_bh_delete() must have been called on BHs in this AioContext. In
24
update_zones_wp(bs, s->fd, 0, 1);
32
+ * many cases memory leaks, hangs, or inconsistent state occur when a
25
}
33
+ * BH is leaked because something still expects it to run.
34
+ *
35
+ * If you hit this, fix the lifecycle of the BH so that
36
+ * qemu_bh_delete() and any associated cleanup is called before the
37
+ * AioContext is finalized.
38
+ */
39
+ if (unlikely(!(flags & BH_DELETED))) {
40
+ fprintf(stderr, "%s: BH '%s' leaked, aborting...\n",
41
+ __func__, bh->name);
42
+ abort();
43
+ }
44
45
g_free(bh);
46
}
26
}
47
--
27
--
48
2.31.1
28
2.41.0
49
diff view generated by jsdifflib
New patch
1
We duplicate the same condition three times here, pull it out to the top
2
level.
1
3
4
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
5
Message-Id: <20230824155345.109765-5-hreitz@redhat.com>
6
Reviewed-by: Sam Li <faithilikerun@gmail.com>
7
---
8
block/file-posix.c | 18 +++++-------------
9
1 file changed, 5 insertions(+), 13 deletions(-)
10
11
diff --git a/block/file-posix.c b/block/file-posix.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/block/file-posix.c
14
+++ b/block/file-posix.c
15
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset,
16
17
out:
18
#if defined(CONFIG_BLKZONED)
19
-{
20
- BlockZoneWps *wps = bs->wps;
21
- if (ret == 0) {
22
- if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) &&
23
- bs->bl.zoned != BLK_Z_NONE) {
24
+ if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) &&
25
+ bs->bl.zoned != BLK_Z_NONE) {
26
+ BlockZoneWps *wps = bs->wps;
27
+ if (ret == 0) {
28
uint64_t *wp = &wps->wp[offset / bs->bl.zone_size];
29
if (!BDRV_ZT_IS_CONV(*wp)) {
30
if (type & QEMU_AIO_ZONE_APPEND) {
31
@@ -XXX,XX +XXX,XX @@ out:
32
*wp = offset + bytes;
33
}
34
}
35
- }
36
- } else {
37
- if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) &&
38
- bs->bl.zoned != BLK_Z_NONE) {
39
+ } else {
40
update_zones_wp(bs, s->fd, 0, 1);
41
}
42
- }
43
44
- if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) &&
45
- bs->blk.zoned != BLK_Z_NONE) {
46
qemu_co_mutex_unlock(&wps->colock);
47
}
48
-}
49
#endif
50
return ret;
51
}
52
--
53
2.41.0
diff view generated by jsdifflib
1
From: Akihiko Odaki <akihiko.odaki@gmail.com>
1
This is a regression test for
2
https://bugzilla.redhat.com/show_bug.cgi?id=2234374.
2
3
3
Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
4
All this test needs to do is trigger an I/O error inside of file-posix
4
Message-id: 20210705130458.97642-3-akihiko.odaki@gmail.com
5
(specifically raw_co_prw()). One reliable way to do this without
5
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
6
requiring special privileges is to use a FUSE export, which allows us to
7
inject any error that we want, e.g. via blkdebug.
8
9
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
10
Message-Id: <20230824155345.109765-6-hreitz@redhat.com>
11
[hreitz: Fixed test to be skipped when there is no FUSE support, to
12
suppress fusermount's allow_other warning, and to be skipped
13
with $IMGOPTSSYNTAX enabled]
14
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
6
---
15
---
7
block/io.c | 2 ++
16
tests/qemu-iotests/tests/file-io-error | 119 +++++++++++++++++++++
8
1 file changed, 2 insertions(+)
17
tests/qemu-iotests/tests/file-io-error.out | 33 ++++++
18
2 files changed, 152 insertions(+)
19
create mode 100755 tests/qemu-iotests/tests/file-io-error
20
create mode 100644 tests/qemu-iotests/tests/file-io-error.out
9
21
10
diff --git a/block/io.c b/block/io.c
22
diff --git a/tests/qemu-iotests/tests/file-io-error b/tests/qemu-iotests/tests/file-io-error
11
index XXXXXXX..XXXXXXX 100644
23
new file mode 100755
12
--- a/block/io.c
24
index XXXXXXX..XXXXXXX
13
+++ b/block/io.c
25
--- /dev/null
14
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll)
26
+++ b/tests/qemu-iotests/tests/file-io-error
15
27
@@ -XXX,XX +XXX,XX @@
16
static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
28
+#!/usr/bin/env bash
17
{
29
+# group: rw
18
+ dst->pdiscard_alignment = MAX(dst->pdiscard_alignment,
30
+#
19
+ src->pdiscard_alignment);
31
+# Produce an I/O error in file-posix, and hope that it is not catastrophic.
20
dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer);
32
+# Regression test for: https://bugzilla.redhat.com/show_bug.cgi?id=2234374
21
dst->max_transfer = MIN_NON_ZERO(dst->max_transfer, src->max_transfer);
33
+#
22
dst->max_hw_transfer = MIN_NON_ZERO(dst->max_hw_transfer,
34
+# Copyright (C) 2023 Red Hat, Inc.
35
+#
36
+# This program is free software; you can redistribute it and/or modify
37
+# it under the terms of the GNU General Public License as published by
38
+# the Free Software Foundation; either version 2 of the License, or
39
+# (at your option) any later version.
40
+#
41
+# This program is distributed in the hope that it will be useful,
42
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
43
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44
+# GNU General Public License for more details.
45
+#
46
+# You should have received a copy of the GNU General Public License
47
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
48
+#
49
+
50
+seq=$(basename "$0")
51
+echo "QA output created by $seq"
52
+
53
+status=1    # failure is the default!
54
+
55
+_cleanup()
56
+{
57
+ _cleanup_qemu
58
+ rm -f "$TEST_DIR/fuse-export"
59
+}
60
+trap "_cleanup; exit \$status" 0 1 2 3 15
61
+
62
+# get standard environment, filters and checks
63
+. ../common.rc
64
+. ../common.filter
65
+. ../common.qemu
66
+
67
+# Format-agnostic (we do not use any), but we do test the file protocol
68
+_supported_proto file
69
+_require_drivers blkdebug null-co
70
+
71
+if [ "$IMGOPTSSYNTAX" = "true" ]; then
72
+ # We need `$QEMU_IO -f file` to work; IMGOPTSSYNTAX uses --image-opts,
73
+ # breaking -f.
74
+ _unsupported_fmt $IMGFMT
75
+fi
76
+
77
+# This is a regression test of a bug in which flie-posix would access zone
78
+# information in case of an I/O error even when there is no zone information,
79
+# resulting in a division by zero.
80
+# To reproduce the problem, we need to trigger an I/O error inside of
81
+# file-posix, which can be done (rootless) by providing a FUSE export that
82
+# presents only errors when accessed.
83
+
84
+_launch_qemu
85
+_send_qemu_cmd $QEMU_HANDLE \
86
+ "{'execute': 'qmp_capabilities'}" \
87
+ 'return'
88
+
89
+_send_qemu_cmd $QEMU_HANDLE \
90
+ "{'execute': 'blockdev-add',
91
+ 'arguments': {
92
+ 'driver': 'blkdebug',
93
+ 'node-name': 'node0',
94
+ 'inject-error': [{'event': 'none'}],
95
+ 'image': {
96
+ 'driver': 'null-co'
97
+ }
98
+ }}" \
99
+ 'return'
100
+
101
+# FUSE mountpoint must exist and be a regular file
102
+touch "$TEST_DIR/fuse-export"
103
+
104
+# The grep -v to filter fusermount's (benign) error when /etc/fuse.conf does
105
+# not contain user_allow_other and the subsequent check for missing FUSE support
106
+# have both been taken from iotest 308.
107
+output=$(_send_qemu_cmd $QEMU_HANDLE \
108
+ "{'execute': 'block-export-add',
109
+ 'arguments': {
110
+ 'id': 'exp0',
111
+ 'type': 'fuse',
112
+ 'node-name': 'node0',
113
+ 'mountpoint': '$TEST_DIR/fuse-export',
114
+ 'writable': true
115
+ }}" \
116
+ 'return' \
117
+ | grep -v 'option allow_other only allowed if')
118
+
119
+if echo "$output" | grep -q "Parameter 'type' does not accept value 'fuse'"; then
120
+ _notrun 'No FUSE support'
121
+fi
122
+echo "$output"
123
+
124
+echo
125
+# This should fail, but gracefully, i.e. just print an I/O error, not crash.
126
+$QEMU_IO -f file -c 'write 0 64M' "$TEST_DIR/fuse-export" | _filter_qemu_io
127
+echo
128
+
129
+_send_qemu_cmd $QEMU_HANDLE \
130
+ "{'execute': 'block-export-del',
131
+ 'arguments': {'id': 'exp0'}}" \
132
+ 'return'
133
+
134
+_send_qemu_cmd $QEMU_HANDLE \
135
+ '' \
136
+ 'BLOCK_EXPORT_DELETED'
137
+
138
+_send_qemu_cmd $QEMU_HANDLE \
139
+ "{'execute': 'blockdev-del',
140
+ 'arguments': {'node-name': 'node0'}}" \
141
+ 'return'
142
+
143
+# success, all done
144
+echo "*** done"
145
+rm -f $seq.full
146
+status=0
147
diff --git a/tests/qemu-iotests/tests/file-io-error.out b/tests/qemu-iotests/tests/file-io-error.out
148
new file mode 100644
149
index XXXXXXX..XXXXXXX
150
--- /dev/null
151
+++ b/tests/qemu-iotests/tests/file-io-error.out
152
@@ -XXX,XX +XXX,XX @@
153
+QA output created by file-io-error
154
+{'execute': 'qmp_capabilities'}
155
+{"return": {}}
156
+{'execute': 'blockdev-add',
157
+ 'arguments': {
158
+ 'driver': 'blkdebug',
159
+ 'node-name': 'node0',
160
+ 'inject-error': [{'event': 'none'}],
161
+ 'image': {
162
+ 'driver': 'null-co'
163
+ }
164
+ }}
165
+{"return": {}}
166
+{'execute': 'block-export-add',
167
+ 'arguments': {
168
+ 'id': 'exp0',
169
+ 'type': 'fuse',
170
+ 'node-name': 'node0',
171
+ 'mountpoint': 'TEST_DIR/fuse-export',
172
+ 'writable': true
173
+ }}
174
+{"return": {}}
175
+
176
+write failed: Input/output error
177
+
178
+{'execute': 'block-export-del',
179
+ 'arguments': {'id': 'exp0'}}
180
+{"return": {}}
181
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "exp0"}}
182
+{'execute': 'blockdev-del',
183
+ 'arguments': {'node-name': 'node0'}}
184
+{"return": {}}
185
+*** done
23
--
186
--
24
2.31.1
187
2.41.0
25
diff view generated by jsdifflib