Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 : : */
4 : :
5 : : #include "spdk/stdinc.h"
6 : : #include "spdk/fsdev.h"
7 : : #include "spdk/config.h"
8 : : #include "spdk/env.h"
9 : : #include "spdk/likely.h"
10 : : #include "spdk/queue.h"
11 : : #include "spdk/util.h"
12 : : #include "spdk/notify.h"
13 : : #include "spdk/fsdev_module.h"
14 : : #include "spdk/log.h"
15 : : #include "spdk/string.h"
16 : : #include "fsdev_internal.h"
17 : :
18 : : #define SPDK_FSDEV_IO_POOL_SIZE (64 * 1024 - 1)
19 : : #define SPDK_FSDEV_IO_CACHE_SIZE 256
20 : :
21 : : static struct spdk_fsdev_opts g_fsdev_opts = {
22 : : .fsdev_io_pool_size = SPDK_FSDEV_IO_POOL_SIZE,
23 : : .fsdev_io_cache_size = SPDK_FSDEV_IO_CACHE_SIZE,
24 : : };
25 : :
26 : : TAILQ_HEAD(spdk_fsdev_list, spdk_fsdev);
27 : :
28 : : RB_HEAD(fsdev_name_tree, spdk_fsdev_name);
29 : :
30 : : static int
31 : 118 : fsdev_name_cmp(struct spdk_fsdev_name *name1, struct spdk_fsdev_name *name2)
32 : : {
33 [ - + - + : 118 : return strcmp(name1->name, name2->name);
# # # # #
# # # ]
34 : : }
35 : :
36 [ + + - + : 472 : RB_GENERATE_STATIC(fsdev_name_tree, spdk_fsdev_name, node, fsdev_name_cmp);
+ + + - +
- + - - +
- + - + -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - + #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
37 : :
38 : : struct spdk_fsdev_mgr {
39 : : struct spdk_mempool *fsdev_io_pool;
40 : :
41 : : TAILQ_HEAD(fsdev_module_list, spdk_fsdev_module) fsdev_modules;
42 : :
43 : : struct spdk_fsdev_list fsdevs;
44 : : struct fsdev_name_tree fsdev_names;
45 : :
46 : : bool init_complete;
47 : : bool module_init_complete;
48 : :
49 : : struct spdk_spinlock spinlock;
50 : : };
51 : :
52 : : static struct spdk_fsdev_mgr g_fsdev_mgr = {
53 : : .fsdev_modules = TAILQ_HEAD_INITIALIZER(g_fsdev_mgr.fsdev_modules),
54 : : .fsdevs = TAILQ_HEAD_INITIALIZER(g_fsdev_mgr.fsdevs),
55 : : .fsdev_names = RB_INITIALIZER(g_fsdev_mgr.fsdev_names),
56 : : .init_complete = false,
57 : : .module_init_complete = false,
58 : : };
59 : :
60 : : static void
61 : : __attribute__((constructor))
62 : 2053 : _fsdev_init(void)
63 : : {
64 : 2053 : spdk_spin_init(&g_fsdev_mgr.spinlock);
65 : 2053 : }
66 : :
67 : :
68 : : static spdk_fsdev_init_cb g_init_cb_fn = NULL;
69 : : static void *g_init_cb_arg = NULL;
70 : :
71 : : static spdk_fsdev_fini_cb g_fini_cb_fn = NULL;
72 : : static void *g_fini_cb_arg = NULL;
73 : : static struct spdk_thread *g_fini_thread = NULL;
74 : :
75 : : struct spdk_fsdev_mgmt_channel {
76 : : /*
77 : : * Each thread keeps a cache of fsdev_io - this allows
78 : : * fsdev threads which are *not* DPDK threads to still
79 : : * benefit from a per-thread fsdev_io cache. Without
80 : : * this, non-DPDK threads fetching from the mempool
81 : : * incur a cmpxchg on get and put.
82 : : */
83 : : fsdev_io_stailq_t per_thread_cache;
84 : : uint32_t per_thread_cache_count;
85 : : uint32_t fsdev_io_cache_size;
86 : :
87 : : TAILQ_HEAD(, spdk_fsdev_shared_resource) shared_resources;
88 : : };
89 : :
90 : : /*
91 : : * Per-module (or per-io_device) data. Multiple fsdevs built on the same io_device
92 : : * will queue here their IO that awaits retry. It makes it possible to retry sending
93 : : * IO to one fsdev after IO from other fsdev completes.
94 : : */
95 : : struct spdk_fsdev_shared_resource {
96 : : /* The fsdev management channel */
97 : : struct spdk_fsdev_mgmt_channel *mgmt_ch;
98 : :
99 : : /*
100 : : * Count of I/O submitted to fsdev module and waiting for completion.
101 : : * Incremented before submit_request() is called on an spdk_fsdev_io.
102 : : */
103 : : uint64_t io_outstanding;
104 : :
105 : : /* I/O channel allocated by a fsdev module */
106 : : struct spdk_io_channel *shared_ch;
107 : :
108 : : /* Refcount of fsdev channels using this resource */
109 : : uint32_t ref;
110 : :
111 : : TAILQ_ENTRY(spdk_fsdev_shared_resource) link;
112 : : };
113 : :
114 : : struct spdk_fsdev_channel {
115 : : struct spdk_fsdev *fsdev;
116 : :
117 : : /* The channel for the underlying device */
118 : : struct spdk_io_channel *channel;
119 : :
120 : : /* Per io_device per thread data */
121 : : struct spdk_fsdev_shared_resource *shared_resource;
122 : :
123 : : /*
124 : : * Count of I/O submitted to the underlying dev module through this channel
125 : : * and waiting for completion.
126 : : */
127 : : uint64_t io_outstanding;
128 : :
129 : : /*
130 : : * List of all submitted I/Os.
131 : : */
132 : : fsdev_io_tailq_t io_submitted;
133 : : };
134 : :
135 : : struct spdk_fsdev_desc {
136 : : struct spdk_fsdev *fsdev;
137 : : struct spdk_thread *thread;
138 : : struct {
139 : : spdk_fsdev_event_cb_t event_fn;
140 : : void *ctx;
141 : : } callback;
142 : : bool closed;
143 : : struct spdk_spinlock spinlock;
144 : : uint32_t refs;
145 : : TAILQ_ENTRY(spdk_fsdev_desc) link;
146 : : };
147 : :
148 : : #define __fsdev_to_io_dev(fsdev) (((char *)fsdev) + 1)
149 : : #define __fsdev_from_io_dev(io_dev) ((struct spdk_fsdev *)(((char *)io_dev) - 1))
150 : : #define __io_ch_to_fsdev_mgmt_ch(io_ch) ((struct spdk_fsdev_mgmt_channel *)spdk_io_channel_get_ctx(io_ch))
151 : :
152 : : static struct spdk_fsdev *
153 : 118 : fsdev_get_by_name(const char *fsdev_name)
154 : : {
155 : 118 : struct spdk_fsdev_name find;
156 : : struct spdk_fsdev_name *res;
157 : :
158 : 118 : find.name = (char *)fsdev_name;
159 : 118 : res = RB_FIND(fsdev_name_tree, &g_fsdev_mgr.fsdev_names, &find);
160 [ + - ]: 118 : if (res != NULL) {
161 [ # # # # ]: 118 : return res->fsdev;
162 : : }
163 : :
164 : 0 : return NULL;
165 : 0 : }
166 : :
167 : : static int
168 : 594 : fsdev_module_get_max_ctx_size(void)
169 : : {
170 : : struct spdk_fsdev_module *fsdev_module;
171 : 594 : int max_fsdev_module_size = 0;
172 : :
173 [ + + + - : 1188 : TAILQ_FOREACH(fsdev_module, &g_fsdev_mgr.fsdev_modules, internal.tailq) {
+ + + - +
- + - +
- ]
174 [ + - + + : 594 : if (fsdev_module->get_ctx_size && fsdev_module->get_ctx_size() > max_fsdev_module_size) {
+ - + - +
- - + + -
- + ]
175 [ + - + - : 591 : max_fsdev_module_size = fsdev_module->get_ctx_size();
- + + - ]
176 : 21 : }
177 : 21 : }
178 : :
179 : 594 : return max_fsdev_module_size;
180 : : }
181 : :
182 : : void
183 : 102 : spdk_fsdev_subsystem_config_json(struct spdk_json_write_ctx *w)
184 : : {
185 : : struct spdk_fsdev_module *fsdev_module;
186 : : struct spdk_fsdev *fsdev;
187 : :
188 [ + + # # ]: 102 : assert(w != NULL);
189 : :
190 : 102 : spdk_json_write_array_begin(w);
191 : :
192 : 102 : spdk_json_write_object_begin(w);
193 : 102 : spdk_json_write_named_string(w, "method", "fsdev_set_opts");
194 : 102 : spdk_json_write_named_object_begin(w, "params");
195 : 102 : spdk_json_write_named_uint32(w, "fsdev_io_pool_size", g_fsdev_opts.fsdev_io_pool_size);
196 : 102 : spdk_json_write_named_uint32(w, "fsdev_io_cache_size", g_fsdev_opts.fsdev_io_cache_size);
197 : 102 : spdk_json_write_object_end(w); /* params */
198 : 102 : spdk_json_write_object_end(w);
199 : :
200 [ + + + - : 204 : TAILQ_FOREACH(fsdev_module, &g_fsdev_mgr.fsdev_modules, internal.tailq) {
+ + + - +
- + - +
- ]
201 [ + + + - : 102 : if (fsdev_module->config_json) {
+ - ]
202 [ # # # # : 0 : fsdev_module->config_json(w);
# # # # ]
203 : 0 : }
204 : 1 : }
205 : :
206 : 102 : spdk_spin_lock(&g_fsdev_mgr.spinlock);
207 : :
208 [ + + + - : 102 : TAILQ_FOREACH(fsdev, &g_fsdev_mgr.fsdevs, internal.link) {
- + # # #
# # # #
# ]
209 [ # # # # : 0 : if (fsdev->fn_table->write_config_json) {
# # # # #
# ]
210 [ # # # # : 0 : fsdev->fn_table->write_config_json(fsdev, w);
# # # # #
# # # ]
211 : 0 : }
212 : 0 : }
213 : :
214 : 102 : spdk_spin_unlock(&g_fsdev_mgr.spinlock);
215 : 102 : spdk_json_write_array_end(w);
216 : 102 : }
217 : :
218 : : static void
219 : 115 : fsdev_mgmt_channel_destroy(void *io_device, void *ctx_buf)
220 : : {
221 : 115 : struct spdk_fsdev_mgmt_channel *ch = ctx_buf;
222 : : struct spdk_fsdev_io *fsdev_io;
223 : :
224 [ - + # # : 115 : if (!TAILQ_EMPTY(&ch->shared_resources)) {
# # # # ]
225 : 0 : SPDK_ERRLOG("Module channel list wasn't empty on mgmt channel free\n");
226 : 0 : }
227 : :
228 [ + + # # : 58739 : while (!STAILQ_EMPTY(&ch->per_thread_cache)) {
# # # # ]
229 [ # # # # : 58624 : fsdev_io = STAILQ_FIRST(&ch->per_thread_cache);
# # ]
230 [ + + # # : 58624 : STAILQ_REMOVE_HEAD(&ch->per_thread_cache, internal.buf_link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
231 [ # # ]: 58624 : ch->per_thread_cache_count--;
232 : 58624 : spdk_mempool_put(g_fsdev_mgr.fsdev_io_pool, (void *)fsdev_io);
233 : : }
234 : :
235 [ - + # # : 115 : assert(ch->per_thread_cache_count == 0);
# # # # ]
236 : 115 : return;
237 : : }
238 : :
239 : : static int
240 : 115 : fsdev_mgmt_channel_create(void *io_device, void *ctx_buf)
241 : : {
242 : 115 : struct spdk_fsdev_mgmt_channel *ch = ctx_buf;
243 : : struct spdk_fsdev_io *fsdev_io;
244 : : uint32_t i;
245 : :
246 [ # # # # : 115 : STAILQ_INIT(&ch->per_thread_cache);
# # # # #
# # # # #
# # ]
247 [ # # # # ]: 115 : ch->fsdev_io_cache_size = g_fsdev_opts.fsdev_io_cache_size;
248 : :
249 : : /* Pre-populate fsdev_io cache to ensure this thread cannot be starved. */
250 [ # # # # ]: 115 : ch->per_thread_cache_count = 0;
251 [ + + # # : 58739 : for (i = 0; i < ch->fsdev_io_cache_size; i++) {
# # ]
252 : 58624 : fsdev_io = spdk_mempool_get(g_fsdev_mgr.fsdev_io_pool);
253 [ - + ]: 58624 : if (fsdev_io == NULL) {
254 : 0 : SPDK_ERRLOG("You need to increase fsdev_io_pool_size using fsdev_set_options RPC.\n");
255 [ # # ]: 0 : assert(false);
256 : : fsdev_mgmt_channel_destroy(io_device, ctx_buf);
257 : : return -1;
258 : : }
259 [ # # ]: 58624 : ch->per_thread_cache_count++;
260 [ + + # # : 58624 : STAILQ_INSERT_HEAD(&ch->per_thread_cache, fsdev_io, internal.buf_link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
261 : 0 : }
262 : :
263 [ # # # # : 115 : TAILQ_INIT(&ch->shared_resources);
# # # # #
# # # # #
# # ]
264 : 115 : return 0;
265 : : }
266 : :
267 : : static void
268 : 594 : fsdev_init_complete(int rc)
269 : : {
270 : 594 : spdk_fsdev_init_cb cb_fn = g_init_cb_fn;
271 : 594 : void *cb_arg = g_init_cb_arg;
272 : :
273 [ + - ]: 594 : g_fsdev_mgr.init_complete = true;
274 : 594 : g_init_cb_fn = NULL;
275 : 594 : g_init_cb_arg = NULL;
276 : :
277 [ - + + - ]: 594 : cb_fn(cb_arg, rc);
278 : 594 : }
279 : :
280 : : static void
281 : 0 : fsdev_init_failed(void *cb_arg)
282 : : {
283 : 0 : fsdev_init_complete(-1);
284 : 0 : }
285 : :
286 : : static int
287 : 594 : fsdev_modules_init(void)
288 : : {
289 : : struct spdk_fsdev_module *module;
290 : 594 : int rc = 0;
291 : :
292 [ + + + - : 1188 : TAILQ_FOREACH(module, &g_fsdev_mgr.fsdev_modules, internal.tailq) {
+ + + - +
- + - +
- ]
293 [ + - + - : 594 : rc = module->module_init();
- + + - ]
294 [ + + ]: 594 : if (rc != 0) {
295 : 0 : spdk_thread_send_msg(spdk_get_thread(), fsdev_init_failed, module);
296 : 0 : return rc;
297 : : }
298 : 21 : }
299 : :
300 : 594 : return 0;
301 : 21 : }
302 : :
303 : : void
304 : 594 : spdk_fsdev_initialize(spdk_fsdev_init_cb cb_fn, void *cb_arg)
305 : : {
306 : 594 : int rc = 0;
307 : 573 : char mempool_name[32];
308 : :
309 [ + + # # ]: 594 : assert(cb_fn != NULL);
310 : :
311 : 594 : g_init_cb_fn = cb_fn;
312 : 594 : g_init_cb_arg = cb_arg;
313 : :
314 : 594 : spdk_notify_type_register("fsdev_register");
315 : 594 : spdk_notify_type_register("fsdev_unregister");
316 : :
317 [ - + ]: 594 : snprintf(mempool_name, sizeof(mempool_name), "fsdev_io_%d", getpid());
318 : :
319 : 1188 : g_fsdev_mgr.fsdev_io_pool = spdk_mempool_create(mempool_name,
320 : 594 : g_fsdev_opts.fsdev_io_pool_size,
321 : 21 : sizeof(struct spdk_fsdev_io) +
322 : 594 : fsdev_module_get_max_ctx_size(),
323 : : 0,
324 : : SPDK_ENV_NUMA_ID_ANY);
325 : :
326 [ + + ]: 594 : if (g_fsdev_mgr.fsdev_io_pool == NULL) {
327 : 0 : SPDK_ERRLOG("Could not allocate spdk_fsdev_io pool\n");
328 : 0 : fsdev_init_complete(-1);
329 : 0 : return;
330 : : }
331 : :
332 : 594 : spdk_io_device_register(&g_fsdev_mgr, fsdev_mgmt_channel_create,
333 : : fsdev_mgmt_channel_destroy,
334 : : sizeof(struct spdk_fsdev_mgmt_channel),
335 : : "fsdev_mgr");
336 : :
337 : 594 : rc = fsdev_modules_init();
338 : 594 : g_fsdev_mgr.module_init_complete = true;
339 [ - + ]: 594 : if (rc != 0) {
340 : 0 : SPDK_ERRLOG("fsdev modules init failed\n");
341 : 0 : return;
342 : : }
343 : :
344 : 594 : fsdev_init_complete(0);
345 : 21 : }
346 : :
347 : : static void
348 : 594 : fsdev_mgr_unregister_cb(void *io_device)
349 : : {
350 : 594 : spdk_fsdev_fini_cb cb_fn = g_fini_cb_fn;
351 : :
352 [ + + ]: 594 : if (g_fsdev_mgr.fsdev_io_pool) {
353 [ + + ]: 594 : if (spdk_mempool_count(g_fsdev_mgr.fsdev_io_pool) != g_fsdev_opts.fsdev_io_pool_size) {
354 : 3 : SPDK_ERRLOG("fsdev IO pool count is %zu but should be %u\n",
355 : : spdk_mempool_count(g_fsdev_mgr.fsdev_io_pool),
356 : : g_fsdev_opts.fsdev_io_pool_size);
357 : 0 : }
358 : :
359 : 594 : spdk_mempool_free(g_fsdev_mgr.fsdev_io_pool);
360 : 21 : }
361 : :
362 [ - + + - ]: 594 : cb_fn(g_fini_cb_arg);
363 : 594 : g_fini_cb_fn = NULL;
364 : 594 : g_fini_cb_arg = NULL;
365 [ + - ]: 594 : g_fsdev_mgr.init_complete = false;
366 : 594 : g_fsdev_mgr.module_init_complete = false;
367 : 594 : }
368 : :
369 : : static void
370 : 594 : fsdev_module_fini_iter(void *arg)
371 : : {
372 : : struct spdk_fsdev_module *fsdev_module;
373 : :
374 : : /* FIXME: Handling initialization failures is broken now,
375 : : * so we won't even try cleaning up after successfully
376 : : * initialized modules. if module_init_complete is false,
377 : : * just call spdk_fsdev_mgr_unregister_cb
378 : : */
379 [ + + + + ]: 594 : if (!g_fsdev_mgr.module_init_complete) {
380 : 0 : fsdev_mgr_unregister_cb(NULL);
381 : 0 : return;
382 : : }
383 : :
384 : : /* Start iterating from the last touched module */
385 [ - + - + : 594 : fsdev_module = TAILQ_LAST(&g_fsdev_mgr.fsdev_modules, fsdev_module_list);
- + - + -
+ ]
386 [ + + ]: 1188 : while (fsdev_module) {
387 [ + - + - : 594 : if (fsdev_module->module_fini) {
- + ]
388 [ + - + - : 594 : fsdev_module->module_fini();
- + + - ]
389 : 21 : }
390 : :
391 [ + - + - : 594 : fsdev_module = TAILQ_PREV(fsdev_module, fsdev_module_list,
+ - + - +
- + - +
- ]
392 : : internal.tailq);
393 : : }
394 : :
395 : 594 : spdk_io_device_unregister(&g_fsdev_mgr, fsdev_mgr_unregister_cb);
396 : 21 : }
397 : :
398 : : static void
399 : 595 : fsdev_finish_unregister_fsdevs_iter(void *cb_arg, int fsdeverrno)
400 : : {
401 : 595 : struct spdk_fsdev *fsdev = cb_arg;
402 : :
403 [ - + - - ]: 595 : if (fsdeverrno && fsdev) {
404 [ # # # # ]: 0 : SPDK_WARNLOG("Unable to unregister fsdev '%s' during spdk_fsdev_finish()\n",
405 : : fsdev->name);
406 : :
407 : : /*
408 : : * Since the call to spdk_fsdev_unregister() failed, we have no way to free this
409 : : * fsdev; try to continue by manually removing this fsdev from the list and continue
410 : : * with the next fsdev in the list.
411 : : */
412 [ # # # # : 0 : TAILQ_REMOVE(&g_fsdev_mgr.fsdevs, fsdev, internal.link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
413 : 0 : }
414 : :
415 [ + - + - ]: 595 : fsdev = TAILQ_FIRST(&g_fsdev_mgr.fsdevs);
416 [ + + ]: 595 : if (!fsdev) {
417 [ + + + + : 594 : SPDK_DEBUGLOG(fsdev, "Done unregistering fsdevs\n");
+ - ]
418 : : /*
419 : : * Fsdev module finish need to be deferred as we might be in the middle of some context
420 : : * that will use this fsdev (or private fsdev driver ctx data)
421 : : * after returning.
422 : : */
423 : 594 : spdk_thread_send_msg(spdk_get_thread(), fsdev_module_fini_iter, NULL);
424 : 594 : return;
425 : : }
426 : :
427 [ - + - + : 1 : SPDK_DEBUGLOG(fsdev, "Unregistering fsdev '%s'\n", fsdev->name);
# # # # #
# ]
428 : 1 : spdk_fsdev_unregister(fsdev, fsdev_finish_unregister_fsdevs_iter, fsdev);
429 : 1 : return;
430 : 21 : }
431 : :
432 : : void
433 : 594 : spdk_fsdev_finish(spdk_fsdev_fini_cb cb_fn, void *cb_arg)
434 : : {
435 [ + + # # ]: 594 : assert(cb_fn != NULL);
436 : 594 : g_fini_thread = spdk_get_thread();
437 : 594 : g_fini_cb_fn = cb_fn;
438 : 594 : g_fini_cb_arg = cb_arg;
439 : 594 : fsdev_finish_unregister_fsdevs_iter(NULL, 0);
440 : 594 : }
441 : :
442 : : struct spdk_fsdev_io *
443 : 524443 : fsdev_channel_get_io(struct spdk_fsdev_channel *channel)
444 : : {
445 [ # # # # : 524443 : struct spdk_fsdev_mgmt_channel *ch = channel->shared_resource->mgmt_ch;
# # # # ]
446 : : struct spdk_fsdev_io *fsdev_io;
447 : :
448 [ + - # # : 524443 : if (ch->per_thread_cache_count > 0) {
# # ]
449 [ # # # # : 524443 : fsdev_io = STAILQ_FIRST(&ch->per_thread_cache);
# # ]
450 [ - + # # : 524443 : STAILQ_REMOVE_HEAD(&ch->per_thread_cache, internal.buf_link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
451 [ # # ]: 524443 : ch->per_thread_cache_count--;
452 : 0 : } else {
453 : 0 : fsdev_io = spdk_mempool_get(g_fsdev_mgr.fsdev_io_pool);
454 : : }
455 : :
456 : 524443 : return fsdev_io;
457 : : }
458 : :
459 : : void
460 : 524443 : spdk_fsdev_free_io(struct spdk_fsdev_io *fsdev_io)
461 : : {
462 : : struct spdk_fsdev_mgmt_channel *ch;
463 : :
464 [ - + # # ]: 524443 : assert(fsdev_io != NULL);
465 : :
466 [ # # # # : 524443 : ch = fsdev_io->internal.ch->shared_resource->mgmt_ch;
# # # # #
# # # #
# ]
467 : :
468 [ + - # # : 524443 : if (ch->per_thread_cache_count < ch->fsdev_io_cache_size) {
# # # # #
# ]
469 [ # # ]: 524443 : ch->per_thread_cache_count++;
470 [ - + # # : 524443 : STAILQ_INSERT_HEAD(&ch->per_thread_cache, fsdev_io, internal.buf_link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
471 : 0 : } else {
472 : 0 : spdk_mempool_put(g_fsdev_mgr.fsdev_io_pool, (void *)fsdev_io);
473 : : }
474 : 524443 : }
475 : :
476 : : void
477 : 524443 : fsdev_io_submit(struct spdk_fsdev_io *fsdev_io)
478 : : {
479 [ # # # # ]: 524443 : struct spdk_fsdev *fsdev = fsdev_io->fsdev;
480 [ # # # # : 524443 : struct spdk_fsdev_channel *ch = fsdev_io->internal.ch;
# # ]
481 [ # # # # ]: 524443 : struct spdk_fsdev_shared_resource *shared_resource = ch->shared_resource;
482 : :
483 [ # # # # : 524443 : TAILQ_INSERT_TAIL(&ch->io_submitted, fsdev_io, internal.ch_link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
484 : :
485 [ # # ]: 524443 : ch->io_outstanding++;
486 [ # # ]: 524443 : shared_resource->io_outstanding++;
487 [ # # # # : 524443 : fsdev_io->internal.in_submit_request = true;
# # ]
488 [ # # # # : 524443 : fsdev->fn_table->submit_request(ch->channel, fsdev_io);
# # # # #
# # # # #
# # ]
489 [ # # # # : 524443 : fsdev_io->internal.in_submit_request = false;
# # ]
490 : 524443 : }
491 : :
492 : : static void
493 : 115 : fsdev_channel_destroy_resource(struct spdk_fsdev_channel *ch)
494 : : {
495 : : struct spdk_fsdev_shared_resource *shared_resource;
496 : :
497 [ # # # # ]: 115 : spdk_put_io_channel(ch->channel);
498 : :
499 [ # # # # ]: 115 : shared_resource = ch->shared_resource;
500 : :
501 [ - + # # : 115 : assert(TAILQ_EMPTY(&ch->io_submitted));
# # # # #
# ]
502 [ - + # # : 115 : assert(ch->io_outstanding == 0);
# # # # ]
503 [ - + # # : 115 : assert(shared_resource->ref > 0);
# # # # ]
504 [ # # ]: 115 : shared_resource->ref--;
505 [ + - # # : 115 : if (shared_resource->ref == 0) {
# # ]
506 [ - + # # : 115 : assert(shared_resource->io_outstanding == 0);
# # # # ]
507 [ - + # # : 115 : TAILQ_REMOVE(&shared_resource->mgmt_ch->shared_resources, shared_resource, link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
508 [ # # # # ]: 115 : spdk_put_io_channel(spdk_io_channel_from_ctx(shared_resource->mgmt_ch));
509 : 115 : free(shared_resource);
510 : 0 : }
511 : 115 : }
512 : :
513 : : static void
514 : 118 : fsdev_desc_free(struct spdk_fsdev_desc *desc)
515 : : {
516 [ # # ]: 118 : spdk_spin_destroy(&desc->spinlock);
517 : 118 : free(desc);
518 : 118 : }
519 : :
520 : :
521 : : static int
522 : 115 : fsdev_channel_create(void *io_device, void *ctx_buf)
523 : : {
524 [ # # ]: 115 : struct spdk_fsdev *fsdev = __fsdev_from_io_dev(io_device);
525 : 115 : struct spdk_fsdev_channel *ch = ctx_buf;
526 : : struct spdk_io_channel *mgmt_io_ch;
527 : : struct spdk_fsdev_mgmt_channel *mgmt_ch;
528 : : struct spdk_fsdev_shared_resource *shared_resource;
529 : :
530 [ # # # # ]: 115 : ch->fsdev = fsdev;
531 [ # # # # : 115 : ch->channel = fsdev->fn_table->get_io_channel(fsdev->ctxt);
# # # # #
# # # # #
# # # # #
# ]
532 [ - + # # : 115 : if (!ch->channel) {
# # ]
533 : 0 : return -1;
534 : : }
535 : :
536 : 115 : mgmt_io_ch = spdk_get_io_channel(&g_fsdev_mgr);
537 [ - + ]: 115 : if (!mgmt_io_ch) {
538 [ # # # # ]: 0 : spdk_put_io_channel(ch->channel);
539 : 0 : return -1;
540 : : }
541 : :
542 : 115 : mgmt_ch = __io_ch_to_fsdev_mgmt_ch(mgmt_io_ch);
543 [ - + # # : 115 : TAILQ_FOREACH(shared_resource, &mgmt_ch->shared_resources, link) {
# # # # #
# # # #
# ]
544 [ # # # # : 0 : if (shared_resource->shared_ch == ch->channel) {
# # # # #
# ]
545 : 0 : spdk_put_io_channel(mgmt_io_ch);
546 [ # # ]: 0 : shared_resource->ref++;
547 : 0 : break;
548 : : }
549 : 0 : }
550 : :
551 [ + - ]: 115 : if (shared_resource == NULL) {
552 : 115 : shared_resource = calloc(1, sizeof(*shared_resource));
553 [ - + ]: 115 : if (shared_resource == NULL) {
554 [ # # # # ]: 0 : spdk_put_io_channel(ch->channel);
555 : 0 : spdk_put_io_channel(mgmt_io_ch);
556 : 0 : return -1;
557 : : }
558 : :
559 [ # # # # ]: 115 : shared_resource->mgmt_ch = mgmt_ch;
560 [ # # # # ]: 115 : shared_resource->io_outstanding = 0;
561 [ # # # # : 115 : shared_resource->shared_ch = ch->channel;
# # # # ]
562 [ # # # # ]: 115 : shared_resource->ref = 1;
563 [ # # # # : 115 : TAILQ_INSERT_TAIL(&mgmt_ch->shared_resources, shared_resource, link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
564 : 0 : }
565 : :
566 [ # # # # ]: 115 : ch->io_outstanding = 0;
567 [ # # # # ]: 115 : ch->shared_resource = shared_resource;
568 [ # # # # : 115 : TAILQ_INIT(&ch->io_submitted);
# # # # #
# # # # #
# # ]
569 : 115 : return 0;
570 : 0 : }
571 : :
572 : : static void
573 : 115 : fsdev_channel_destroy(void *io_device, void *ctx_buf)
574 : : {
575 : 115 : struct spdk_fsdev_channel *ch = ctx_buf;
576 : :
577 [ - + - + : 115 : SPDK_DEBUGLOG(fsdev, "Destroying channel %p for fsdev %s on thread %p\n",
# # # # #
# # # #
# ]
578 : : ch, ch->fsdev->name,
579 : : spdk_get_thread());
580 : 115 : fsdev_channel_destroy_resource(ch);
581 : 115 : }
582 : :
583 : : /*
584 : : * If the name already exists in the global fsdev name tree, RB_INSERT() returns a pointer
585 : : * to it. Hence we do not have to call fsdev_get_by_name() when using this function.
586 : : */
587 : : static int
588 : 118 : fsdev_name_add(struct spdk_fsdev_name *fsdev_name, struct spdk_fsdev *fsdev, const char *name)
589 : : {
590 : : struct spdk_fsdev_name *tmp;
591 : :
592 [ - + # # : 118 : fsdev_name->name = strdup(name);
# # ]
593 [ - + # # : 118 : if (fsdev_name->name == NULL) {
# # ]
594 : 0 : SPDK_ERRLOG("Unable to allocate fsdev name\n");
595 : 0 : return -ENOMEM;
596 : : }
597 : :
598 [ # # # # ]: 118 : fsdev_name->fsdev = fsdev;
599 : :
600 : 118 : spdk_spin_lock(&g_fsdev_mgr.spinlock);
601 : 118 : tmp = RB_INSERT(fsdev_name_tree, &g_fsdev_mgr.fsdev_names, fsdev_name);
602 : 118 : spdk_spin_unlock(&g_fsdev_mgr.spinlock);
603 [ - + ]: 118 : if (tmp != NULL) {
604 : 0 : SPDK_ERRLOG("Fsdev name %s already exists\n", name);
605 [ # # # # ]: 0 : free(fsdev_name->name);
606 : 0 : return -EEXIST;
607 : : }
608 : :
609 : 118 : return 0;
610 : 0 : }
611 : :
612 : : static void
613 : 118 : fsdev_name_del_unsafe(struct spdk_fsdev_name *fsdev_name)
614 : : {
615 : 118 : RB_REMOVE(fsdev_name_tree, &g_fsdev_mgr.fsdev_names, fsdev_name);
616 [ # # # # ]: 118 : free(fsdev_name->name);
617 : 118 : }
618 : :
619 : : struct spdk_io_channel *
620 : 115 : spdk_fsdev_get_io_channel(struct spdk_fsdev_desc *desc)
621 : : {
622 [ # # ]: 115 : return spdk_get_io_channel(__fsdev_to_io_dev(spdk_fsdev_desc_get_fsdev(desc)));
623 : : }
624 : :
625 : : int
626 : 46 : spdk_fsdev_set_opts(const struct spdk_fsdev_opts *opts)
627 : : {
628 : : uint32_t min_pool_size;
629 : :
630 [ + + ]: 46 : if (!opts) {
631 : 3 : SPDK_ERRLOG("opts cannot be NULL\n");
632 : 3 : return -EINVAL;
633 : : }
634 : :
635 [ + + + - : 43 : if (!opts->opts_size) {
+ - ]
636 : 3 : SPDK_ERRLOG("opts_size inside opts cannot be zero value\n");
637 : 3 : return -EINVAL;
638 : : }
639 : :
640 : : /*
641 : : * Add 1 to the thread count to account for the extra mgmt_ch that gets created during subsystem
642 : : * initialization. A second mgmt_ch will be created on the same thread when the application starts
643 : : * but before the deferred put_io_channel event is executed for the first mgmt_ch.
644 : : */
645 [ + - + - ]: 40 : min_pool_size = opts->fsdev_io_cache_size * (spdk_thread_get_count() + 1);
646 [ + + + - : 40 : if (opts->fsdev_io_pool_size < min_pool_size) {
- + ]
647 [ # # # # : 0 : SPDK_ERRLOG("fsdev_io_pool_size %" PRIu32 " is not compatible with bdev_io_cache_size %" PRIu32
# # # # ]
648 : : " and %" PRIu32 " threads\n", opts->fsdev_io_pool_size, opts->fsdev_io_cache_size,
649 : : spdk_thread_get_count());
650 : 0 : SPDK_ERRLOG("fsdev_io_pool_size must be at least %" PRIu32 "\n", min_pool_size);
651 : 0 : return -EINVAL;
652 : : }
653 : :
654 : : #define SET_FIELD(field) \
655 : : if (offsetof(struct spdk_fsdev_opts, field) + sizeof(opts->field) <= opts->opts_size) { \
656 : : g_fsdev_opts.field = opts->field; \
657 : : } \
658 : :
659 [ + - + - : 40 : SET_FIELD(fsdev_io_pool_size);
- + - + -
+ ]
660 [ + - + - : 40 : SET_FIELD(fsdev_io_cache_size);
- + - + -
+ ]
661 : :
662 [ + - + - ]: 40 : g_fsdev_opts.opts_size = opts->opts_size;
663 : :
664 : : #undef SET_FIELD
665 : :
666 : 40 : return 0;
667 : 1 : }
668 : :
669 : : int
670 : 44 : spdk_fsdev_get_opts(struct spdk_fsdev_opts *opts, size_t opts_size)
671 : : {
672 [ + + ]: 44 : if (!opts) {
673 : 0 : SPDK_ERRLOG("opts should not be NULL\n");
674 : 0 : return -EINVAL;
675 : : }
676 : :
677 [ + + ]: 44 : if (!opts_size) {
678 : 0 : SPDK_ERRLOG("opts_size should not be zero value\n");
679 : 0 : return -EINVAL;
680 : : }
681 : :
682 [ + - + - ]: 44 : opts->opts_size = opts_size;
683 : :
684 : : #define SET_FIELD(field) \
685 : : if (offsetof(struct spdk_fsdev_opts, field) + sizeof(opts->field) <= opts_size) { \
686 : : opts->field = g_fsdev_opts.field; \
687 : : }
688 : :
689 [ + + + - : 44 : SET_FIELD(fsdev_io_pool_size);
+ - ]
690 [ + + + - : 44 : SET_FIELD(fsdev_io_cache_size);
+ - ]
691 : :
692 : : /* Do not remove this statement, you should always update this statement when you adding a new field,
693 : : * and do not forget to add the SET_FIELD statement for your added field. */
694 : : SPDK_STATIC_ASSERT(sizeof(struct spdk_fsdev_opts) == 12, "Incorrect size");
695 : :
696 : : #undef SET_FIELD
697 : 44 : return 0;
698 : 1 : }
699 : :
700 : : int
701 : 0 : spdk_fsdev_get_memory_domains(struct spdk_fsdev *fsdev, struct spdk_memory_domain **domains,
702 : : int array_size)
703 : : {
704 [ # # ]: 0 : if (!fsdev) {
705 : 0 : return -EINVAL;
706 : : }
707 : :
708 [ # # # # : 0 : if (fsdev->fn_table->get_memory_domains) {
# # # # #
# ]
709 [ # # # # : 0 : return fsdev->fn_table->get_memory_domains(fsdev->ctxt, domains, array_size);
# # # # #
# # # # #
# # ]
710 : : }
711 : :
712 : 0 : return 0;
713 : 0 : }
714 : :
715 : : const char *
716 : 3 : spdk_fsdev_get_module_name(const struct spdk_fsdev *fsdev)
717 : : {
718 [ # # # # : 3 : return fsdev->module->name;
# # # # ]
719 : : }
720 : :
721 : : const char *
722 : 239 : spdk_fsdev_get_name(const struct spdk_fsdev *fsdev)
723 : : {
724 [ # # # # ]: 239 : return fsdev->name;
725 : : }
726 : :
727 : : static inline void
728 : 524598 : fsdev_io_complete(void *ctx)
729 : : {
730 : 524598 : struct spdk_fsdev_io *fsdev_io = ctx;
731 [ # # # # : 524598 : struct spdk_fsdev_channel *fsdev_ch = fsdev_io->internal.ch;
# # ]
732 : :
733 [ - + + + : 524598 : if (spdk_unlikely(fsdev_io->internal.in_submit_request)) {
# # # # #
# ]
734 : : /*
735 : : * Defer completion to avoid potential infinite recursion if the
736 : : * user's completion callback issues a new I/O.
737 : : */
738 : 155 : spdk_thread_send_msg(spdk_fsdev_io_get_thread(fsdev_io),
739 : 0 : fsdev_io_complete, fsdev_io);
740 : 155 : return;
741 : : }
742 : :
743 [ + + # # : 524443 : TAILQ_REMOVE(&fsdev_ch->io_submitted, fsdev_io, internal.ch_link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
744 : :
745 [ - + # # : 524443 : assert(fsdev_io->internal.cb_fn != NULL);
# # # # #
# ]
746 [ - + # # ]: 524443 : assert(spdk_get_thread() == spdk_fsdev_io_get_thread(fsdev_io));
747 [ # # # # : 524443 : fsdev_io->internal.cb_fn(fsdev_io, fsdev_io->internal.cb_arg);
# # # # #
# # # # #
# # ]
748 : 0 : }
749 : :
750 : :
751 : : void
752 : 524443 : spdk_fsdev_io_complete(struct spdk_fsdev_io *fsdev_io, int status)
753 : : {
754 [ # # # # : 524443 : struct spdk_fsdev_channel *fsdev_ch = fsdev_io->internal.ch;
# # ]
755 [ # # # # ]: 524443 : struct spdk_fsdev_shared_resource *shared_resource = fsdev_ch->shared_resource;
756 : :
757 [ - + # # ]: 524443 : assert(status <= 0);
758 [ # # # # : 524443 : fsdev_io->internal.status = status;
# # ]
759 [ - + # # : 524443 : assert(fsdev_ch->io_outstanding > 0);
# # # # ]
760 [ - + # # : 524443 : assert(shared_resource->io_outstanding > 0);
# # # # ]
761 [ # # ]: 524443 : fsdev_ch->io_outstanding--;
762 [ # # ]: 524443 : shared_resource->io_outstanding--;
763 : 524443 : fsdev_io_complete(fsdev_io);
764 : 524443 : }
765 : :
766 : : struct spdk_thread *
767 : 524598 : spdk_fsdev_io_get_thread(struct spdk_fsdev_io *fsdev_io)
768 : : {
769 [ # # # # : 524598 : return spdk_io_channel_get_thread(fsdev_io->internal.ch->channel);
# # # # #
# ]
770 : : }
771 : :
772 : : struct spdk_io_channel *
773 : 0 : spdk_fsdev_io_get_io_channel(struct spdk_fsdev_io *fsdev_io)
774 : : {
775 [ # # # # : 0 : return fsdev_io->internal.ch->channel;
# # # # #
# ]
776 : : }
777 : :
778 : : static int
779 : 118 : fsdev_register(struct spdk_fsdev *fsdev)
780 : : {
781 : : char *fsdev_name;
782 : : int ret;
783 : :
784 [ - + # # : 118 : assert(fsdev->module != NULL);
# # # # ]
785 : :
786 [ - + # # : 118 : if (!fsdev->name) {
# # ]
787 : 0 : SPDK_ERRLOG("Fsdev name is NULL\n");
788 : 0 : return -EINVAL;
789 : : }
790 : :
791 [ - + # # : 118 : if (!strlen(fsdev->name)) {
# # # # ]
792 : 0 : SPDK_ERRLOG("Fsdev name must not be an empty string\n");
793 : 0 : return -EINVAL;
794 : : }
795 : :
796 : : /* Users often register their own I/O devices using the fsdev name. In
797 : : * order to avoid conflicts, prepend fsdev_. */
798 [ # # # # ]: 118 : fsdev_name = spdk_sprintf_alloc("fsdev_%s", fsdev->name);
799 [ - + ]: 118 : if (!fsdev_name) {
800 : 0 : SPDK_ERRLOG("Unable to allocate memory for internal fsdev name.\n");
801 : 0 : return -ENOMEM;
802 : : }
803 : :
804 [ # # # # : 118 : fsdev->internal.status = SPDK_FSDEV_STATUS_READY;
# # ]
805 [ # # # # : 118 : TAILQ_INIT(&fsdev->internal.open_descs);
# # # # #
# # # # #
# # # # #
# # # ]
806 : :
807 [ # # # # : 118 : ret = fsdev_name_add(&fsdev->internal.fsdev_name, fsdev, fsdev->name);
# # # # ]
808 [ - + ]: 118 : if (ret != 0) {
809 : 0 : free(fsdev_name);
810 : 0 : return ret;
811 : : }
812 : :
813 [ # # ]: 118 : spdk_io_device_register(__fsdev_to_io_dev(fsdev),
814 : : fsdev_channel_create, fsdev_channel_destroy,
815 : : sizeof(struct spdk_fsdev_channel),
816 : 0 : fsdev_name);
817 : :
818 : 118 : free(fsdev_name);
819 : :
820 [ # # # # ]: 118 : spdk_spin_init(&fsdev->internal.spinlock);
821 : :
822 [ - + - + : 118 : SPDK_DEBUGLOG(fsdev, "Inserting fsdev %s into list\n", fsdev->name);
# # # # #
# ]
823 [ # # # # : 118 : TAILQ_INSERT_TAIL(&g_fsdev_mgr.fsdevs, fsdev, internal.link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
824 : 118 : return 0;
825 : 0 : }
826 : :
827 : : static void
828 : 118 : fsdev_destroy_cb(void *io_device)
829 : : {
830 : : int rc;
831 : : struct spdk_fsdev *fsdev;
832 : : spdk_fsdev_unregister_cb cb_fn;
833 : : void *cb_arg;
834 : :
835 [ # # ]: 118 : fsdev = __fsdev_from_io_dev(io_device);
836 [ # # # # : 118 : cb_fn = fsdev->internal.unregister_cb;
# # ]
837 [ # # # # : 118 : cb_arg = fsdev->internal.unregister_ctx;
# # ]
838 : :
839 [ # # # # ]: 118 : spdk_spin_destroy(&fsdev->internal.spinlock);
840 : :
841 [ # # # # : 118 : rc = fsdev->fn_table->destruct(fsdev->ctxt);
# # # # #
# # # # #
# # ]
842 [ - + ]: 118 : if (rc < 0) {
843 : 0 : SPDK_ERRLOG("destruct failed\n");
844 : 0 : }
845 [ + - + - ]: 118 : if (rc <= 0 && cb_fn != NULL) {
846 [ # # # # ]: 118 : cb_fn(cb_arg, rc);
847 : 0 : }
848 : 118 : }
849 : :
850 : : void
851 : 0 : spdk_fsdev_destruct_done(struct spdk_fsdev *fsdev, int fsdeverrno)
852 : : {
853 [ # # # # : 0 : if (fsdev->internal.unregister_cb != NULL) {
# # # # ]
854 [ # # # # : 0 : fsdev->internal.unregister_cb(fsdev->internal.unregister_ctx, fsdeverrno);
# # # # #
# # # # #
# # ]
855 : 0 : }
856 : 0 : }
857 : :
858 : : static void
859 : 0 : _remove_notify(void *arg)
860 : : {
861 : 0 : struct spdk_fsdev_desc *desc = arg;
862 : :
863 [ # # ]: 0 : spdk_spin_lock(&desc->spinlock);
864 [ # # ]: 0 : desc->refs--;
865 : :
866 [ # # # # : 0 : if (!desc->closed) {
# # # # ]
867 [ # # ]: 0 : spdk_spin_unlock(&desc->spinlock);
868 [ # # # # : 0 : desc->callback.event_fn(SPDK_FSDEV_EVENT_REMOVE, desc->fsdev, desc->callback.ctx);
# # # # #
# # # # #
# # # # #
# ]
869 : 0 : return;
870 [ # # # # : 0 : } else if (0 == desc->refs) {
# # ]
871 : : /* This descriptor was closed after this remove_notify message was sent.
872 : : * spdk_fsdev_close() could not free the descriptor since this message was
873 : : * in flight, so we free it now using fsdev_desc_free().
874 : : */
875 [ # # ]: 0 : spdk_spin_unlock(&desc->spinlock);
876 : 0 : fsdev_desc_free(desc);
877 : 0 : return;
878 : : }
879 [ # # ]: 0 : spdk_spin_unlock(&desc->spinlock);
880 : 0 : }
881 : :
882 : : /* Must be called while holding g_fsdev_mgr.mutex and fsdev->internal.spinlock.
883 : : * returns: 0 - fsdev removed and ready to be destructed.
884 : : * -EBUSY - fsdev can't be destructed yet. */
885 : : static int
886 : 118 : fsdev_unregister_unsafe(struct spdk_fsdev *fsdev)
887 : : {
888 : : struct spdk_fsdev_desc *desc, *tmp;
889 : 118 : int rc = 0;
890 : :
891 : : /* Notify each descriptor about hotremoval */
892 [ - + # # : 118 : TAILQ_FOREACH_SAFE(desc, &fsdev->internal.open_descs, link, tmp) {
# # # # #
# # # # #
# # # # ]
893 : 0 : rc = -EBUSY;
894 [ # # ]: 0 : spdk_spin_lock(&desc->spinlock);
895 : : /*
896 : : * Defer invocation of the event_cb to a separate message that will
897 : : * run later on its thread. This ensures this context unwinds and
898 : : * we don't recursively unregister this fsdev again if the event_cb
899 : : * immediately closes its descriptor.
900 : : */
901 [ # # ]: 0 : desc->refs++;
902 [ # # # # ]: 0 : spdk_thread_send_msg(desc->thread, _remove_notify, desc);
903 [ # # ]: 0 : spdk_spin_unlock(&desc->spinlock);
904 : 0 : }
905 : :
906 : : /* If there are no descriptors, proceed removing the fsdev */
907 [ + - ]: 118 : if (rc == 0) {
908 [ - + # # : 118 : TAILQ_REMOVE(&g_fsdev_mgr.fsdevs, fsdev, internal.link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
909 [ - + - + : 118 : SPDK_DEBUGLOG(fsdev, "Removing fsdev %s from list done\n", fsdev->name);
# # # # #
# ]
910 [ # # # # ]: 118 : fsdev_name_del_unsafe(&fsdev->internal.fsdev_name);
911 : 118 : spdk_notify_send("fsdev_unregister", spdk_fsdev_get_name(fsdev));
912 : 0 : }
913 : :
914 : 118 : return rc;
915 : : }
916 : :
917 : : static void
918 : 118 : fsdev_unregister(struct spdk_fsdev *fsdev, void *_ctx, int status)
919 : : {
920 : : int rc;
921 : :
922 : 118 : spdk_spin_lock(&g_fsdev_mgr.spinlock);
923 [ # # # # ]: 118 : spdk_spin_lock(&fsdev->internal.spinlock);
924 : : /*
925 : : * Set the status to REMOVING after completing to abort channels. Otherwise,
926 : : * the last spdk_fsdev_close() may call spdk_io_device_unregister() while
927 : : * spdk_fsdev_for_each_channel() is executed and spdk_io_device_unregister()
928 : : * may fail.
929 : : */
930 [ # # # # : 118 : fsdev->internal.status = SPDK_FSDEV_STATUS_REMOVING;
# # ]
931 : 118 : rc = fsdev_unregister_unsafe(fsdev);
932 [ # # # # ]: 118 : spdk_spin_unlock(&fsdev->internal.spinlock);
933 : 118 : spdk_spin_unlock(&g_fsdev_mgr.spinlock);
934 : :
935 [ + - ]: 118 : if (rc == 0) {
936 [ # # ]: 118 : spdk_io_device_unregister(__fsdev_to_io_dev(fsdev), fsdev_destroy_cb);
937 : 0 : }
938 : 118 : }
939 : :
940 : : void
941 : 118 : spdk_fsdev_unregister(struct spdk_fsdev *fsdev, spdk_fsdev_unregister_cb cb_fn, void *cb_arg)
942 : : {
943 : : struct spdk_thread *thread;
944 : :
945 [ - + - + : 118 : SPDK_DEBUGLOG(fsdev, "Removing fsdev %s from list\n", fsdev->name);
# # # # #
# ]
946 : :
947 : 118 : thread = spdk_get_thread();
948 [ - + ]: 118 : if (!thread) {
949 : : /* The user called this from a non-SPDK thread. */
950 [ # # ]: 0 : if (cb_fn != NULL) {
951 [ # # # # ]: 0 : cb_fn(cb_arg, -ENOTSUP);
952 : 0 : }
953 : 0 : return;
954 : : }
955 : :
956 : 118 : spdk_spin_lock(&g_fsdev_mgr.spinlock);
957 [ + - # # : 118 : if (fsdev->internal.status == SPDK_FSDEV_STATUS_UNREGISTERING ||
# # # # #
# ]
958 [ - + # # : 118 : fsdev->internal.status == SPDK_FSDEV_STATUS_REMOVING) {
# # ]
959 : 0 : spdk_spin_unlock(&g_fsdev_mgr.spinlock);
960 [ # # ]: 0 : if (cb_fn) {
961 [ # # # # ]: 0 : cb_fn(cb_arg, -EBUSY);
962 : 0 : }
963 : 0 : return;
964 : : }
965 : :
966 [ # # # # ]: 118 : spdk_spin_lock(&fsdev->internal.spinlock);
967 [ # # # # : 118 : fsdev->internal.status = SPDK_FSDEV_STATUS_UNREGISTERING;
# # ]
968 [ # # # # : 118 : fsdev->internal.unregister_cb = cb_fn;
# # ]
969 [ # # # # : 118 : fsdev->internal.unregister_ctx = cb_arg;
# # ]
970 [ # # # # ]: 118 : spdk_spin_unlock(&fsdev->internal.spinlock);
971 : 118 : spdk_spin_unlock(&g_fsdev_mgr.spinlock);
972 : :
973 : : /* @todo: bdev aborts IOs on all channels here. */
974 : 118 : fsdev_unregister(fsdev, fsdev, 0);
975 : 0 : }
976 : :
977 : : static void
978 : 0 : _tmp_fsdev_event_cb(enum spdk_fsdev_event_type type, struct spdk_fsdev *fsdev, void *ctx)
979 : : {
980 : 0 : SPDK_NOTICELOG("Unexpected fsdev event type: %d\n", type);
981 : 0 : }
982 : :
983 : : int
984 : 0 : spdk_fsdev_unregister_by_name(const char *fsdev_name, struct spdk_fsdev_module *module,
985 : : spdk_fsdev_unregister_cb cb_fn, void *cb_arg)
986 : : {
987 : 0 : struct spdk_fsdev_desc *desc;
988 : : struct spdk_fsdev *fsdev;
989 : : int rc;
990 : :
991 : 0 : rc = spdk_fsdev_open(fsdev_name, _tmp_fsdev_event_cb, NULL, &desc);
992 [ # # ]: 0 : if (rc != 0) {
993 : 0 : SPDK_ERRLOG("Failed to open fsdev with name: %s\n", fsdev_name);
994 : 0 : return rc;
995 : : }
996 : :
997 : 0 : fsdev = spdk_fsdev_desc_get_fsdev(desc);
998 : :
999 [ # # # # : 0 : if (fsdev->module != module) {
# # ]
1000 : 0 : spdk_fsdev_close(desc);
1001 : 0 : SPDK_ERRLOG("Fsdev %s was not registered by the specified module.\n",
1002 : : fsdev_name);
1003 : 0 : return -ENODEV;
1004 : : }
1005 : :
1006 : 0 : spdk_fsdev_unregister(fsdev, cb_fn, cb_arg);
1007 : 0 : spdk_fsdev_close(desc);
1008 : :
1009 : 0 : return 0;
1010 : 0 : }
1011 : :
1012 : : static int
1013 : 118 : fsdev_open(struct spdk_fsdev *fsdev, struct spdk_fsdev_desc *desc)
1014 : : {
1015 : : struct spdk_thread *thread;
1016 : :
1017 : 118 : thread = spdk_get_thread();
1018 [ - + ]: 118 : if (!thread) {
1019 : 0 : SPDK_ERRLOG("Cannot open fsdev from non-SPDK thread.\n");
1020 : 0 : return -ENOTSUP;
1021 : : }
1022 : :
1023 [ - + - + : 118 : SPDK_DEBUGLOG(fsdev, "Opening descriptor %p for fsdev %s on thread %p\n",
# # # # #
# ]
1024 : : desc, fsdev->name, spdk_get_thread());
1025 : :
1026 [ # # # # ]: 118 : desc->fsdev = fsdev;
1027 [ # # # # ]: 118 : desc->thread = thread;
1028 : :
1029 [ # # # # ]: 118 : spdk_spin_lock(&fsdev->internal.spinlock);
1030 [ + - # # : 118 : if (fsdev->internal.status == SPDK_FSDEV_STATUS_UNREGISTERING ||
# # # # #
# ]
1031 [ - + # # : 118 : fsdev->internal.status == SPDK_FSDEV_STATUS_REMOVING) {
# # ]
1032 [ # # # # ]: 0 : spdk_spin_unlock(&fsdev->internal.spinlock);
1033 : 0 : return -ENODEV;
1034 : : }
1035 : :
1036 [ # # # # : 118 : TAILQ_INSERT_TAIL(&fsdev->internal.open_descs, desc, link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
1037 [ # # # # ]: 118 : spdk_spin_unlock(&fsdev->internal.spinlock);
1038 : 118 : return 0;
1039 : 0 : }
1040 : :
1041 : : static int
1042 : 118 : fsdev_desc_alloc(struct spdk_fsdev *fsdev, spdk_fsdev_event_cb_t event_cb, void *event_ctx,
1043 : : struct spdk_fsdev_desc **_desc)
1044 : : {
1045 : : struct spdk_fsdev_desc *desc;
1046 : :
1047 : 118 : desc = calloc(1, sizeof(*desc));
1048 [ - + ]: 118 : if (desc == NULL) {
1049 : 0 : SPDK_ERRLOG("Failed to allocate memory for fsdev descriptor\n");
1050 : 0 : return -ENOMEM;
1051 : : }
1052 : :
1053 [ # # # # : 118 : desc->callback.event_fn = event_cb;
# # ]
1054 [ # # # # : 118 : desc->callback.ctx = event_ctx;
# # ]
1055 [ # # ]: 118 : spdk_spin_init(&desc->spinlock);
1056 [ # # ]: 118 : *_desc = desc;
1057 : 118 : return 0;
1058 : 0 : }
1059 : :
1060 : : int
1061 : 118 : spdk_fsdev_open(const char *fsdev_name, spdk_fsdev_event_cb_t event_cb, void *event_ctx,
1062 : : struct spdk_fsdev_desc **_desc)
1063 : : {
1064 : 118 : struct spdk_fsdev_desc *desc;
1065 : : struct spdk_fsdev *fsdev;
1066 : : int rc;
1067 : :
1068 [ - + ]: 118 : if (event_cb == NULL) {
1069 : 0 : SPDK_ERRLOG("Missing event callback function\n");
1070 : 0 : return -EINVAL;
1071 : : }
1072 : :
1073 : 118 : spdk_spin_lock(&g_fsdev_mgr.spinlock);
1074 : :
1075 : 118 : fsdev = fsdev_get_by_name(fsdev_name);
1076 [ - + ]: 118 : if (fsdev == NULL) {
1077 : 0 : SPDK_NOTICELOG("Currently unable to find fsdev with name: %s\n", fsdev_name);
1078 : 0 : spdk_spin_unlock(&g_fsdev_mgr.spinlock);
1079 : 0 : return -ENODEV;
1080 : : }
1081 : :
1082 : 118 : rc = fsdev_desc_alloc(fsdev, event_cb, event_ctx, &desc);
1083 [ - + ]: 118 : if (rc != 0) {
1084 : 0 : spdk_spin_unlock(&g_fsdev_mgr.spinlock);
1085 : 0 : return rc;
1086 : : }
1087 : :
1088 : 118 : rc = fsdev_open(fsdev, desc);
1089 [ - + ]: 118 : if (rc != 0) {
1090 : 0 : fsdev_desc_free(desc);
1091 : 0 : desc = NULL;
1092 : 0 : }
1093 : :
1094 [ # # ]: 118 : *_desc = desc;
1095 : 118 : spdk_spin_unlock(&g_fsdev_mgr.spinlock);
1096 : 118 : return rc;
1097 : 0 : }
1098 : :
1099 : : static void
1100 : 118 : fsdev_close(struct spdk_fsdev *fsdev, struct spdk_fsdev_desc *desc)
1101 : : {
1102 : : int rc;
1103 : :
1104 [ # # # # ]: 118 : spdk_spin_lock(&fsdev->internal.spinlock);
1105 [ # # ]: 118 : spdk_spin_lock(&desc->spinlock);
1106 : :
1107 [ - + # # : 118 : TAILQ_REMOVE(&fsdev->internal.open_descs, desc, link);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
1108 [ # # # # ]: 118 : desc->closed = true;
1109 [ + - # # : 118 : if (0 == desc->refs) {
# # ]
1110 [ # # ]: 118 : spdk_spin_unlock(&desc->spinlock);
1111 : 118 : fsdev_desc_free(desc);
1112 : 0 : } else {
1113 [ # # ]: 0 : spdk_spin_unlock(&desc->spinlock);
1114 : : }
1115 : :
1116 [ - + # # : 118 : if (fsdev->internal.status == SPDK_FSDEV_STATUS_REMOVING &&
# # # # #
# ]
1117 [ # # # # : 0 : TAILQ_EMPTY(&fsdev->internal.open_descs)) {
# # # # ]
1118 : 0 : rc = fsdev_unregister_unsafe(fsdev);
1119 [ # # # # ]: 0 : spdk_spin_unlock(&fsdev->internal.spinlock);
1120 : :
1121 [ # # ]: 0 : if (rc == 0) {
1122 [ # # ]: 0 : spdk_io_device_unregister(__fsdev_to_io_dev(fsdev), fsdev_destroy_cb);
1123 : 0 : }
1124 : 0 : } else {
1125 [ # # # # ]: 118 : spdk_spin_unlock(&fsdev->internal.spinlock);
1126 : : }
1127 : 118 : }
1128 : :
1129 : : void
1130 : 118 : spdk_fsdev_close(struct spdk_fsdev_desc *desc)
1131 : : {
1132 : 118 : struct spdk_fsdev *fsdev = spdk_fsdev_desc_get_fsdev(desc);
1133 : :
1134 [ - + - + : 118 : SPDK_DEBUGLOG(fsdev, "Closing descriptor %p for fsdev %s on thread %p\n",
# # # # #
# ]
1135 : : desc, fsdev->name, spdk_get_thread());
1136 [ - + # # : 118 : assert(desc->thread == spdk_get_thread());
# # # # ]
1137 : 118 : spdk_spin_lock(&g_fsdev_mgr.spinlock);
1138 : 118 : fsdev_close(fsdev, desc);
1139 : 118 : spdk_spin_unlock(&g_fsdev_mgr.spinlock);
1140 : 118 : }
1141 : :
1142 : : int
1143 : 118 : spdk_fsdev_register(struct spdk_fsdev *fsdev)
1144 : : {
1145 : : int rc;
1146 : :
1147 : 118 : rc = fsdev_register(fsdev);
1148 [ - + ]: 118 : if (rc != 0) {
1149 : 0 : return rc;
1150 : : }
1151 : :
1152 : 118 : spdk_notify_send("fsdev_register", spdk_fsdev_get_name(fsdev));
1153 : 118 : return rc;
1154 : 0 : }
1155 : :
1156 : : struct spdk_fsdev *
1157 : 524682 : spdk_fsdev_desc_get_fsdev(struct spdk_fsdev_desc *desc)
1158 : : {
1159 [ - + # # ]: 524682 : assert(desc != NULL);
1160 [ # # # # ]: 524682 : return desc->fsdev;
1161 : : }
1162 : :
1163 : : void
1164 : 2053 : spdk_fsdev_module_list_add(struct spdk_fsdev_module *fsdev_module)
1165 : : {
1166 : :
1167 [ + + + - : 2053 : if (spdk_fsdev_module_list_find(fsdev_module->name)) {
+ - ]
1168 [ # # # # ]: 0 : SPDK_ERRLOG("ERROR: module '%s' already registered.\n", fsdev_module->name);
1169 [ # # ]: 0 : assert(false);
1170 : : }
1171 : :
1172 [ + - + - : 2053 : TAILQ_INSERT_TAIL(&g_fsdev_mgr.fsdev_modules, fsdev_module, internal.tailq);
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - ]
1173 : 2053 : }
1174 : :
1175 : : struct spdk_fsdev_module *
1176 : 2053 : spdk_fsdev_module_list_find(const char *name)
1177 : : {
1178 : : struct spdk_fsdev_module *fsdev_module;
1179 : :
1180 [ + + + - : 2053 : TAILQ_FOREACH(fsdev_module, &g_fsdev_mgr.fsdev_modules, internal.tailq) {
+ - # # #
# # # #
# ]
1181 [ # # # # : 0 : if (strcmp(name, fsdev_module->name) == 0) {
# # # # #
# ]
1182 : 0 : break;
1183 : : }
1184 : 0 : }
1185 : :
1186 : 2053 : return fsdev_module;
1187 : : }
1188 : :
1189 : 2053 : SPDK_LOG_REGISTER_COMPONENT(fsdev)
|