block/export: Add blk_exp_close_all(_type)
[qemu.git] / block / export / export.c
1 /*
2 * Common block export infrastructure
3 *
4 * Copyright (c) 2012, 2020 Red Hat, Inc.
5 *
6 * Authors:
7 * Paolo Bonzini <pbonzini@redhat.com>
8 * Kevin Wolf <kwolf@redhat.com>
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
23 static const BlockExportDriver *blk_exp_drivers[] = {
24 &blk_exp_nbd,
25 };
26
27 /* Only accessed from the main thread */
28 static QLIST_HEAD(, BlockExport) block_exports =
29 QLIST_HEAD_INITIALIZER(block_exports);
30
31 static const BlockExportDriver *blk_exp_find_driver(BlockExportType type)
32 {
33 int i;
34
35 for (i = 0; i < ARRAY_SIZE(blk_exp_drivers); i++) {
36 if (blk_exp_drivers[i]->type == type) {
37 return blk_exp_drivers[i];
38 }
39 }
40 return NULL;
41 }
42
43 BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
44 {
45 const BlockExportDriver *drv;
46 BlockExport *exp;
47 int ret;
48
49 drv = blk_exp_find_driver(export->type);
50 if (!drv) {
51 error_setg(errp, "No driver found for the requested export type");
52 return NULL;
53 }
54
55 assert(drv->instance_size >= sizeof(BlockExport));
56 exp = g_malloc0(drv->instance_size);
57 *exp = (BlockExport) {
58 .drv = drv,
59 .refcount = 1,
60 };
61
62 ret = drv->create(exp, export, errp);
63 if (ret < 0) {
64 g_free(exp);
65 return NULL;
66 }
67
68 QLIST_INSERT_HEAD(&block_exports, exp, next);
69 return exp;
70 }
71
72 /* Callers must hold exp->ctx lock */
73 void blk_exp_ref(BlockExport *exp)
74 {
75 assert(exp->refcount > 0);
76 exp->refcount++;
77 }
78
79 /* Runs in the main thread */
80 static void blk_exp_delete_bh(void *opaque)
81 {
82 BlockExport *exp = opaque;
83 AioContext *aio_context = exp->ctx;
84
85 aio_context_acquire(aio_context);
86
87 assert(exp->refcount == 0);
88 QLIST_REMOVE(exp, next);
89 exp->drv->delete(exp);
90 g_free(exp);
91
92 aio_context_release(aio_context);
93 }
94
95 /* Callers must hold exp->ctx lock */
96 void blk_exp_unref(BlockExport *exp)
97 {
98 assert(exp->refcount > 0);
99 if (--exp->refcount == 0) {
100 /* Touch the block_exports list only in the main thread */
101 aio_bh_schedule_oneshot(qemu_get_aio_context(), blk_exp_delete_bh,
102 exp);
103 }
104 }
105
106 /*
107 * Drops the user reference to the export and requests that all client
108 * connections and other internally held references start to shut down. When
109 * the function returns, there may still be active references while the export
110 * is in the process of shutting down.
111 *
112 * Acquires exp->ctx internally. Callers must *not* hold the lock.
113 */
114 void blk_exp_request_shutdown(BlockExport *exp)
115 {
116 AioContext *aio_context = exp->ctx;
117
118 aio_context_acquire(aio_context);
119 exp->drv->request_shutdown(exp);
120 aio_context_release(aio_context);
121 }
122
123 /*
124 * Returns whether a block export of the given type exists.
125 * type == BLOCK_EXPORT_TYPE__MAX checks for an export of any type.
126 */
127 static bool blk_exp_has_type(BlockExportType type)
128 {
129 BlockExport *exp;
130
131 if (type == BLOCK_EXPORT_TYPE__MAX) {
132 return !QLIST_EMPTY(&block_exports);
133 }
134
135 QLIST_FOREACH(exp, &block_exports, next) {
136 if (exp->drv->type == type) {
137 return true;
138 }
139 }
140
141 return false;
142 }
143
144 /* type == BLOCK_EXPORT_TYPE__MAX for all types */
145 void blk_exp_close_all_type(BlockExportType type)
146 {
147 BlockExport *exp, *next;
148
149 assert(in_aio_context_home_thread(qemu_get_aio_context()));
150
151 QLIST_FOREACH_SAFE(exp, &block_exports, next, next) {
152 if (type != BLOCK_EXPORT_TYPE__MAX && exp->drv->type != type) {
153 continue;
154 }
155 blk_exp_request_shutdown(exp);
156 }
157
158 AIO_WAIT_WHILE(NULL, blk_exp_has_type(type));
159 }
160
161 void blk_exp_close_all(void)
162 {
163 blk_exp_close_all_type(BLOCK_EXPORT_TYPE__MAX);
164 }
165
166 void qmp_block_export_add(BlockExportOptions *export, Error **errp)
167 {
168 blk_exp_add(export, errp);
169 }