xref: /openbmc/qemu/block/export/export.c (revision 1a9f7a80)
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 #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;
62     int ret;
63 
64     if (!id_wellformed(export->id)) {
65         error_setg(errp, "Invalid block export id");
66         return NULL;
67     }
68     if (blk_exp_find(export->id)) {
69         error_setg(errp, "Block export id '%s' is already in use", export->id);
70         return NULL;
71     }
72 
73     drv = blk_exp_find_driver(export->type);
74     if (!drv) {
75         error_setg(errp, "No driver found for the requested export type");
76         return NULL;
77     }
78 
79     assert(drv->instance_size >= sizeof(BlockExport));
80     exp = g_malloc0(drv->instance_size);
81     *exp = (BlockExport) {
82         .drv        = drv,
83         .refcount   = 1,
84         .user_owned = true,
85         .id         = g_strdup(export->id),
86     };
87 
88     ret = drv->create(exp, export, errp);
89     if (ret < 0) {
90         g_free(exp->id);
91         g_free(exp);
92         return NULL;
93     }
94 
95     QLIST_INSERT_HEAD(&block_exports, exp, next);
96     return exp;
97 }
98 
99 /* Callers must hold exp->ctx lock */
100 void blk_exp_ref(BlockExport *exp)
101 {
102     assert(exp->refcount > 0);
103     exp->refcount++;
104 }
105 
106 /* Runs in the main thread */
107 static void blk_exp_delete_bh(void *opaque)
108 {
109     BlockExport *exp = opaque;
110     AioContext *aio_context = exp->ctx;
111 
112     aio_context_acquire(aio_context);
113 
114     assert(exp->refcount == 0);
115     QLIST_REMOVE(exp, next);
116     exp->drv->delete(exp);
117     qapi_event_send_block_export_deleted(exp->id);
118     g_free(exp->id);
119     g_free(exp);
120 
121     aio_context_release(aio_context);
122 }
123 
124 /* Callers must hold exp->ctx lock */
125 void blk_exp_unref(BlockExport *exp)
126 {
127     assert(exp->refcount > 0);
128     if (--exp->refcount == 0) {
129         /* Touch the block_exports list only in the main thread */
130         aio_bh_schedule_oneshot(qemu_get_aio_context(), blk_exp_delete_bh,
131                                 exp);
132     }
133 }
134 
135 /*
136  * Drops the user reference to the export and requests that all client
137  * connections and other internally held references start to shut down. When
138  * the function returns, there may still be active references while the export
139  * is in the process of shutting down.
140  *
141  * Acquires exp->ctx internally. Callers must *not* hold the lock.
142  */
143 void blk_exp_request_shutdown(BlockExport *exp)
144 {
145     AioContext *aio_context = exp->ctx;
146 
147     aio_context_acquire(aio_context);
148 
149     /*
150      * If the user doesn't own the export any more, it is already shutting
151      * down. We must not call .request_shutdown and decrease the refcount a
152      * second time.
153      */
154     if (!exp->user_owned) {
155         goto out;
156     }
157 
158     exp->drv->request_shutdown(exp);
159 
160     assert(exp->user_owned);
161     exp->user_owned = false;
162     blk_exp_unref(exp);
163 
164 out:
165     aio_context_release(aio_context);
166 }
167 
168 /*
169  * Returns whether a block export of the given type exists.
170  * type == BLOCK_EXPORT_TYPE__MAX checks for an export of any type.
171  */
172 static bool blk_exp_has_type(BlockExportType type)
173 {
174     BlockExport *exp;
175 
176     if (type == BLOCK_EXPORT_TYPE__MAX) {
177         return !QLIST_EMPTY(&block_exports);
178     }
179 
180     QLIST_FOREACH(exp, &block_exports, next) {
181         if (exp->drv->type == type) {
182             return true;
183         }
184     }
185 
186     return false;
187 }
188 
189 /* type == BLOCK_EXPORT_TYPE__MAX for all types */
190 void blk_exp_close_all_type(BlockExportType type)
191 {
192     BlockExport *exp, *next;
193 
194     assert(in_aio_context_home_thread(qemu_get_aio_context()));
195 
196     QLIST_FOREACH_SAFE(exp, &block_exports, next, next) {
197         if (type != BLOCK_EXPORT_TYPE__MAX && exp->drv->type != type) {
198             continue;
199         }
200         blk_exp_request_shutdown(exp);
201     }
202 
203     AIO_WAIT_WHILE(NULL, blk_exp_has_type(type));
204 }
205 
206 void blk_exp_close_all(void)
207 {
208     blk_exp_close_all_type(BLOCK_EXPORT_TYPE__MAX);
209 }
210 
211 void qmp_block_export_add(BlockExportOptions *export, Error **errp)
212 {
213     blk_exp_add(export, errp);
214 }
215 
216 void qmp_block_export_del(const char *id,
217                           bool has_mode, BlockExportRemoveMode mode,
218                           Error **errp)
219 {
220     ERRP_GUARD();
221     BlockExport *exp;
222 
223     exp = blk_exp_find(id);
224     if (exp == NULL) {
225         error_setg(errp, "Export '%s' is not found", id);
226         return;
227     }
228     if (!exp->user_owned) {
229         error_setg(errp, "Export '%s' is already shutting down", id);
230         return;
231     }
232 
233     if (!has_mode) {
234         mode = BLOCK_EXPORT_REMOVE_MODE_SAFE;
235     }
236     if (mode == BLOCK_EXPORT_REMOVE_MODE_SAFE && exp->refcount > 1) {
237         error_setg(errp, "export '%s' still in use", exp->id);
238         error_append_hint(errp, "Use mode='hard' to force client "
239                           "disconnect\n");
240         return;
241     }
242 
243     blk_exp_request_shutdown(exp);
244 }
245