xref: /openbmc/qemu/block/block-backend.c (revision 8fb3c76c)
1 /*
2  * QEMU Block backends
3  *
4  * Copyright (C) 2014 Red Hat, Inc.
5  *
6  * Authors:
7  *  Markus Armbruster <armbru@redhat.com>,
8  *
9  * This work is licensed under the terms of the GNU LGPL, version 2.1
10  * or later.  See the COPYING.LIB file in the top-level directory.
11  */
12 
13 #include "sysemu/block-backend.h"
14 #include "block/block_int.h"
15 #include "sysemu/blockdev.h"
16 
17 struct BlockBackend {
18     char *name;
19     int refcnt;
20     BlockDriverState *bs;
21     DriveInfo *legacy_dinfo;
22     QTAILQ_ENTRY(BlockBackend) link; /* for blk_backends */
23 };
24 
25 static void drive_info_del(DriveInfo *dinfo);
26 
27 /* All the BlockBackends (except for hidden ones) */
28 static QTAILQ_HEAD(, BlockBackend) blk_backends =
29     QTAILQ_HEAD_INITIALIZER(blk_backends);
30 
31 /*
32  * Create a new BlockBackend with @name, with a reference count of one.
33  * @name must not be null or empty.
34  * Fail if a BlockBackend with this name already exists.
35  * Store an error through @errp on failure, unless it's null.
36  * Return the new BlockBackend on success, null on failure.
37  */
38 BlockBackend *blk_new(const char *name, Error **errp)
39 {
40     BlockBackend *blk;
41 
42     assert(name && name[0]);
43     if (blk_by_name(name)) {
44         error_setg(errp, "Device with id '%s' already exists", name);
45         return NULL;
46     }
47 
48     blk = g_new0(BlockBackend, 1);
49     blk->name = g_strdup(name);
50     blk->refcnt = 1;
51     QTAILQ_INSERT_TAIL(&blk_backends, blk, link);
52     return blk;
53 }
54 
55 /*
56  * Create a new BlockBackend with a new BlockDriverState attached.
57  * Both have a reference count of one.  Caller owns *both* references.
58  * TODO Let caller own only the BlockBackend reference
59  * Otherwise just like blk_new(), which see.
60  */
61 BlockBackend *blk_new_with_bs(const char *name, Error **errp)
62 {
63     BlockBackend *blk;
64     BlockDriverState *bs;
65 
66     blk = blk_new(name, errp);
67     if (!blk) {
68         return NULL;
69     }
70 
71     bs = bdrv_new_root(name, errp);
72     if (!bs) {
73         blk_unref(blk);
74         return NULL;
75     }
76 
77     blk->bs = bs;
78     bs->blk = blk;
79     return blk;
80 }
81 
82 static void blk_delete(BlockBackend *blk)
83 {
84     assert(!blk->refcnt);
85     if (blk->bs) {
86         blk->bs->blk = NULL;
87         blk->bs = NULL;
88     }
89     /* Avoid double-remove after blk_hide_on_behalf_of_do_drive_del() */
90     if (blk->name[0]) {
91         QTAILQ_REMOVE(&blk_backends, blk, link);
92     }
93     g_free(blk->name);
94     drive_info_del(blk->legacy_dinfo);
95     g_free(blk);
96 }
97 
98 static void drive_info_del(DriveInfo *dinfo)
99 {
100     if (!dinfo) {
101         return;
102     }
103     qemu_opts_del(dinfo->opts);
104     g_free(dinfo->id);
105     g_free(dinfo->serial);
106     g_free(dinfo);
107 }
108 
109 /*
110  * Increment @blk's reference count.
111  * @blk must not be null.
112  */
113 void blk_ref(BlockBackend *blk)
114 {
115     blk->refcnt++;
116 }
117 
118 /*
119  * Decrement @blk's reference count.
120  * If this drops it to zero, destroy @blk.
121  * For convenience, do nothing if @blk is null.
122  * Does *not* touch the attached BlockDriverState's reference count.
123  * TODO Decrement it!
124  */
125 void blk_unref(BlockBackend *blk)
126 {
127     if (blk) {
128         assert(blk->refcnt > 0);
129         if (!--blk->refcnt) {
130             blk_delete(blk);
131         }
132     }
133 }
134 
135 /*
136  * Return the BlockBackend after @blk.
137  * If @blk is null, return the first one.
138  * Else, return @blk's next sibling, which may be null.
139  *
140  * To iterate over all BlockBackends, do
141  * for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
142  *     ...
143  * }
144  */
145 BlockBackend *blk_next(BlockBackend *blk)
146 {
147     return blk ? QTAILQ_NEXT(blk, link) : QTAILQ_FIRST(&blk_backends);
148 }
149 
150 /*
151  * Return @blk's name, a non-null string.
152  * Wart: the name is empty iff @blk has been hidden with
153  * blk_hide_on_behalf_of_do_drive_del().
154  */
155 const char *blk_name(BlockBackend *blk)
156 {
157     return blk->name;
158 }
159 
160 /*
161  * Return the BlockBackend with name @name if it exists, else null.
162  * @name must not be null.
163  */
164 BlockBackend *blk_by_name(const char *name)
165 {
166     BlockBackend *blk;
167 
168     assert(name);
169     QTAILQ_FOREACH(blk, &blk_backends, link) {
170         if (!strcmp(name, blk->name)) {
171             return blk;
172         }
173     }
174     return NULL;
175 }
176 
177 /*
178  * Return the BlockDriverState attached to @blk if any, else null.
179  */
180 BlockDriverState *blk_bs(BlockBackend *blk)
181 {
182     return blk->bs;
183 }
184 
185 /*
186  * Return @blk's DriveInfo if any, else null.
187  */
188 DriveInfo *blk_legacy_dinfo(BlockBackend *blk)
189 {
190     return blk->legacy_dinfo;
191 }
192 
193 /*
194  * Set @blk's DriveInfo to @dinfo, and return it.
195  * @blk must not have a DriveInfo set already.
196  * No other BlockBackend may have the same DriveInfo set.
197  */
198 DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo)
199 {
200     assert(!blk->legacy_dinfo);
201     return blk->legacy_dinfo = dinfo;
202 }
203 
204 /*
205  * Return the BlockBackend with DriveInfo @dinfo.
206  * It must exist.
207  */
208 BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo)
209 {
210     BlockBackend *blk;
211 
212     QTAILQ_FOREACH(blk, &blk_backends, link) {
213         if (blk->legacy_dinfo == dinfo) {
214             return blk;
215         }
216     }
217     abort();
218 }
219 
220 /*
221  * Hide @blk.
222  * @blk must not have been hidden already.
223  * Make attached BlockDriverState, if any, anonymous.
224  * Once hidden, @blk is invisible to all functions that don't receive
225  * it as argument.  For example, blk_by_name() won't return it.
226  * Strictly for use by do_drive_del().
227  * TODO get rid of it!
228  */
229 void blk_hide_on_behalf_of_do_drive_del(BlockBackend *blk)
230 {
231     QTAILQ_REMOVE(&blk_backends, blk, link);
232     blk->name[0] = 0;
233     if (blk->bs) {
234         bdrv_make_anon(blk->bs);
235     }
236 }
237