Skip to content

Commit 469e72a

Browse files
committed
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
Block layer patches: - Add block export infrastructure - iotests improvements - Document the throttle block filter - Misc code cleanups # gpg: Signature made Fri 02 Oct 2020 15:36:53 BST # gpg: using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6 # gpg: issuer "[email protected]" # gpg: Good signature from "Kevin Wolf <[email protected]>" [full] # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * remotes/kevin/tags/for-upstream: (37 commits) qcow2: Use L1E_SIZE in qcow2_write_l1_entry() qemu-storage-daemon: Fix help line for --export iotests: Test block-export-* QMP interface iotests: Allow supported and unsupported formats at the same time iotests: Introduce qemu_nbd_list_log() iotests: Factor out qemu_tool_pipe_and_status() nbd: Deprecate nbd-server-add/remove nbd: Merge nbd_export_new() and nbd_export_create() block/export: Move writable to BlockExportOptions block/export: Add query-block-exports block/export: Create BlockBackend in blk_exp_add() block/export: Move blk to BlockExport block/export: Add BLOCK_EXPORT_DELETED event block/export: Add block-export-del block/export: Move strong user reference to block_exports block/export: Add 'id' option to block-export-add block/export: Add blk_exp_close_all(_type) block/export: Allocate BlockExport in blk_exp_add() block/export: Add node-name to BlockExportOptions block/export: Move AioContext from NBDExport to BlockExport ... Signed-off-by: Peter Maydell <[email protected]>
2 parents dd8c1e8 + c508c73 commit 469e72a

30 files changed

+1428
-545
lines changed

block.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4462,7 +4462,7 @@ static void bdrv_close(BlockDriverState *bs)
44624462
void bdrv_close_all(void)
44634463
{
44644464
assert(job_next(NULL) == NULL);
4465-
nbd_export_close_all();
4465+
blk_exp_close_all();
44664466

44674467
/* Drop references from requests still in flight, such as canceled block
44684468
* jobs whose AIO context has not been polled yet */

block/export/export.c

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
/*
2+
* Common block export infrastructure
3+
*
4+
* Copyright (c) 2012, 2020 Red Hat, Inc.
5+
*
6+
* Authors:
7+
* Paolo Bonzini <[email protected]>
8+
* Kevin Wolf <[email protected]>
9+
*
10+
* This work is licensed under the terms of the GNU GPL, version 2 or
11+
* later. See the COPYING file in the top-level directory.
12+
*/
13+
14+
#include "qemu/osdep.h"
15+
16+
#include "block/block.h"
17+
#include "sysemu/block-backend.h"
18+
#include "block/export.h"
19+
#include "block/nbd.h"
20+
#include "qapi/error.h"
21+
#include "qapi/qapi-commands-block-export.h"
22+
#include "qapi/qapi-events-block-export.h"
23+
#include "qemu/id.h"
24+
25+
static const BlockExportDriver *blk_exp_drivers[] = {
26+
&blk_exp_nbd,
27+
};
28+
29+
/* Only accessed from the main thread */
30+
static QLIST_HEAD(, BlockExport) block_exports =
31+
QLIST_HEAD_INITIALIZER(block_exports);
32+
33+
BlockExport *blk_exp_find(const char *id)
34+
{
35+
BlockExport *exp;
36+
37+
QLIST_FOREACH(exp, &block_exports, next) {
38+
if (strcmp(id, exp->id) == 0) {
39+
return exp;
40+
}
41+
}
42+
43+
return NULL;
44+
}
45+
46+
static const BlockExportDriver *blk_exp_find_driver(BlockExportType type)
47+
{
48+
int i;
49+
50+
for (i = 0; i < ARRAY_SIZE(blk_exp_drivers); i++) {
51+
if (blk_exp_drivers[i]->type == type) {
52+
return blk_exp_drivers[i];
53+
}
54+
}
55+
return NULL;
56+
}
57+
58+
BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
59+
{
60+
const BlockExportDriver *drv;
61+
BlockExport *exp = NULL;
62+
BlockDriverState *bs;
63+
BlockBackend *blk;
64+
AioContext *ctx;
65+
uint64_t perm;
66+
int ret;
67+
68+
if (!id_wellformed(export->id)) {
69+
error_setg(errp, "Invalid block export id");
70+
return NULL;
71+
}
72+
if (blk_exp_find(export->id)) {
73+
error_setg(errp, "Block export id '%s' is already in use", export->id);
74+
return NULL;
75+
}
76+
77+
drv = blk_exp_find_driver(export->type);
78+
if (!drv) {
79+
error_setg(errp, "No driver found for the requested export type");
80+
return NULL;
81+
}
82+
83+
bs = bdrv_lookup_bs(NULL, export->node_name, errp);
84+
if (!bs) {
85+
return NULL;
86+
}
87+
88+
if (!export->has_writable) {
89+
export->writable = false;
90+
}
91+
if (bdrv_is_read_only(bs) && export->writable) {
92+
error_setg(errp, "Cannot export read-only node as writable");
93+
return NULL;
94+
}
95+
96+
ctx = bdrv_get_aio_context(bs);
97+
aio_context_acquire(ctx);
98+
99+
/*
100+
* Block exports are used for non-shared storage migration. Make sure
101+
* that BDRV_O_INACTIVE is cleared and the image is ready for write
102+
* access since the export could be available before migration handover.
103+
* ctx was acquired in the caller.
104+
*/
105+
bdrv_invalidate_cache(bs, NULL);
106+
107+
perm = BLK_PERM_CONSISTENT_READ;
108+
if (export->writable) {
109+
perm |= BLK_PERM_WRITE;
110+
}
111+
112+
blk = blk_new(ctx, perm, BLK_PERM_ALL);
113+
ret = blk_insert_bs(blk, bs, errp);
114+
if (ret < 0) {
115+
goto fail;
116+
}
117+
118+
if (!export->has_writethrough) {
119+
export->writethrough = false;
120+
}
121+
blk_set_enable_write_cache(blk, !export->writethrough);
122+
123+
assert(drv->instance_size >= sizeof(BlockExport));
124+
exp = g_malloc0(drv->instance_size);
125+
*exp = (BlockExport) {
126+
.drv = drv,
127+
.refcount = 1,
128+
.user_owned = true,
129+
.id = g_strdup(export->id),
130+
.ctx = ctx,
131+
.blk = blk,
132+
};
133+
134+
ret = drv->create(exp, export, errp);
135+
if (ret < 0) {
136+
goto fail;
137+
}
138+
139+
assert(exp->blk != NULL);
140+
141+
QLIST_INSERT_HEAD(&block_exports, exp, next);
142+
143+
aio_context_release(ctx);
144+
return exp;
145+
146+
fail:
147+
blk_unref(blk);
148+
aio_context_release(ctx);
149+
if (exp) {
150+
g_free(exp->id);
151+
g_free(exp);
152+
}
153+
return NULL;
154+
}
155+
156+
/* Callers must hold exp->ctx lock */
157+
void blk_exp_ref(BlockExport *exp)
158+
{
159+
assert(exp->refcount > 0);
160+
exp->refcount++;
161+
}
162+
163+
/* Runs in the main thread */
164+
static void blk_exp_delete_bh(void *opaque)
165+
{
166+
BlockExport *exp = opaque;
167+
AioContext *aio_context = exp->ctx;
168+
169+
aio_context_acquire(aio_context);
170+
171+
assert(exp->refcount == 0);
172+
QLIST_REMOVE(exp, next);
173+
exp->drv->delete(exp);
174+
blk_unref(exp->blk);
175+
qapi_event_send_block_export_deleted(exp->id);
176+
g_free(exp->id);
177+
g_free(exp);
178+
179+
aio_context_release(aio_context);
180+
}
181+
182+
/* Callers must hold exp->ctx lock */
183+
void blk_exp_unref(BlockExport *exp)
184+
{
185+
assert(exp->refcount > 0);
186+
if (--exp->refcount == 0) {
187+
/* Touch the block_exports list only in the main thread */
188+
aio_bh_schedule_oneshot(qemu_get_aio_context(), blk_exp_delete_bh,
189+
exp);
190+
}
191+
}
192+
193+
/*
194+
* Drops the user reference to the export and requests that all client
195+
* connections and other internally held references start to shut down. When
196+
* the function returns, there may still be active references while the export
197+
* is in the process of shutting down.
198+
*
199+
* Acquires exp->ctx internally. Callers must *not* hold the lock.
200+
*/
201+
void blk_exp_request_shutdown(BlockExport *exp)
202+
{
203+
AioContext *aio_context = exp->ctx;
204+
205+
aio_context_acquire(aio_context);
206+
207+
/*
208+
* If the user doesn't own the export any more, it is already shutting
209+
* down. We must not call .request_shutdown and decrease the refcount a
210+
* second time.
211+
*/
212+
if (!exp->user_owned) {
213+
goto out;
214+
}
215+
216+
exp->drv->request_shutdown(exp);
217+
218+
assert(exp->user_owned);
219+
exp->user_owned = false;
220+
blk_exp_unref(exp);
221+
222+
out:
223+
aio_context_release(aio_context);
224+
}
225+
226+
/*
227+
* Returns whether a block export of the given type exists.
228+
* type == BLOCK_EXPORT_TYPE__MAX checks for an export of any type.
229+
*/
230+
static bool blk_exp_has_type(BlockExportType type)
231+
{
232+
BlockExport *exp;
233+
234+
if (type == BLOCK_EXPORT_TYPE__MAX) {
235+
return !QLIST_EMPTY(&block_exports);
236+
}
237+
238+
QLIST_FOREACH(exp, &block_exports, next) {
239+
if (exp->drv->type == type) {
240+
return true;
241+
}
242+
}
243+
244+
return false;
245+
}
246+
247+
/* type == BLOCK_EXPORT_TYPE__MAX for all types */
248+
void blk_exp_close_all_type(BlockExportType type)
249+
{
250+
BlockExport *exp, *next;
251+
252+
assert(in_aio_context_home_thread(qemu_get_aio_context()));
253+
254+
QLIST_FOREACH_SAFE(exp, &block_exports, next, next) {
255+
if (type != BLOCK_EXPORT_TYPE__MAX && exp->drv->type != type) {
256+
continue;
257+
}
258+
blk_exp_request_shutdown(exp);
259+
}
260+
261+
AIO_WAIT_WHILE(NULL, blk_exp_has_type(type));
262+
}
263+
264+
void blk_exp_close_all(void)
265+
{
266+
blk_exp_close_all_type(BLOCK_EXPORT_TYPE__MAX);
267+
}
268+
269+
void qmp_block_export_add(BlockExportOptions *export, Error **errp)
270+
{
271+
blk_exp_add(export, errp);
272+
}
273+
274+
void qmp_block_export_del(const char *id,
275+
bool has_mode, BlockExportRemoveMode mode,
276+
Error **errp)
277+
{
278+
ERRP_GUARD();
279+
BlockExport *exp;
280+
281+
exp = blk_exp_find(id);
282+
if (exp == NULL) {
283+
error_setg(errp, "Export '%s' is not found", id);
284+
return;
285+
}
286+
if (!exp->user_owned) {
287+
error_setg(errp, "Export '%s' is already shutting down", id);
288+
return;
289+
}
290+
291+
if (!has_mode) {
292+
mode = BLOCK_EXPORT_REMOVE_MODE_SAFE;
293+
}
294+
if (mode == BLOCK_EXPORT_REMOVE_MODE_SAFE && exp->refcount > 1) {
295+
error_setg(errp, "export '%s' still in use", exp->id);
296+
error_append_hint(errp, "Use mode='hard' to force client "
297+
"disconnect\n");
298+
return;
299+
}
300+
301+
blk_exp_request_shutdown(exp);
302+
}
303+
304+
BlockExportInfoList *qmp_query_block_exports(Error **errp)
305+
{
306+
BlockExportInfoList *head = NULL, **p_next = &head;
307+
BlockExport *exp;
308+
309+
QLIST_FOREACH(exp, &block_exports, next) {
310+
BlockExportInfoList *entry = g_new0(BlockExportInfoList, 1);
311+
BlockExportInfo *info = g_new(BlockExportInfo, 1);
312+
*info = (BlockExportInfo) {
313+
.id = g_strdup(exp->id),
314+
.type = exp->drv->type,
315+
.node_name = g_strdup(bdrv_get_node_name(blk_bs(exp->blk))),
316+
.shutting_down = !exp->user_owned,
317+
};
318+
319+
entry->value = info;
320+
*p_next = entry;
321+
p_next = &entry->next;
322+
}
323+
324+
return head;
325+
}

block/export/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
block_ss.add(files('export.c'))

block/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ block_ss.add(module_block_h)
110110
block_ss.add(files('stream.c'))
111111

112112
softmmu_ss.add(files('qapi-sysemu.c'))
113+
114+
subdir('export')
113115
subdir('monitor')
114116

115117
modules += {'block': block_modules}

0 commit comments

Comments
 (0)