Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2017 Intel Corporation. All rights reserved.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : :
8 : : #include "spdk/env.h"
9 : : #include "spdk/likely.h"
10 : : #include "spdk/string.h"
11 : : #include "spdk/util.h"
12 : : #include "spdk/memory.h"
13 : : #include "spdk/barrier.h"
14 : : #include "spdk/vhost.h"
15 : : #include "vhost_internal.h"
16 : : #include "spdk/queue.h"
17 : :
18 : :
19 : : static struct spdk_cpuset g_vhost_core_mask;
20 : :
21 : : static TAILQ_HEAD(, spdk_vhost_dev) g_vhost_devices = TAILQ_HEAD_INITIALIZER(
22 : : g_vhost_devices);
23 : : static pthread_mutex_t g_vhost_mutex = PTHREAD_MUTEX_INITIALIZER;
24 : :
25 : : static TAILQ_HEAD(, spdk_virtio_blk_transport) g_virtio_blk_transports = TAILQ_HEAD_INITIALIZER(
26 : : g_virtio_blk_transports);
27 : :
28 : : static spdk_vhost_fini_cb g_fini_cb;
29 : :
30 : : struct spdk_vhost_dev *
31 : 20557 : spdk_vhost_dev_next(struct spdk_vhost_dev *vdev)
32 : : {
33 [ + + ]: 20557 : if (vdev == NULL) {
34 : 9427 : return TAILQ_FIRST(&g_vhost_devices);
35 : : }
36 : :
37 : 11130 : return TAILQ_NEXT(vdev, tailq);
38 : : }
39 : :
40 : : struct spdk_vhost_dev *
41 : 385 : spdk_vhost_dev_find(const char *ctrlr_name)
42 : : {
43 : : struct spdk_vhost_dev *vdev;
44 : :
45 [ + + ]: 689 : TAILQ_FOREACH(vdev, &g_vhost_devices, tailq) {
46 [ + + - + : 549 : if (strcmp(vdev->name, ctrlr_name) == 0) {
+ + ]
47 : 245 : return vdev;
48 : : }
49 : : }
50 : :
51 : 140 : return NULL;
52 : : }
53 : :
54 : : static int
55 : 154 : vhost_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask)
56 : : {
57 : : int rc;
58 : 126 : struct spdk_cpuset negative_vhost_mask;
59 : :
60 [ - + ]: 154 : if (cpumask == NULL) {
61 : 0 : return -1;
62 : : }
63 : :
64 [ + + ]: 154 : if (mask == NULL) {
65 : 67 : spdk_cpuset_copy(cpumask, &g_vhost_core_mask);
66 : 67 : return 0;
67 : : }
68 : :
69 : 87 : rc = spdk_cpuset_parse(cpumask, mask);
70 [ - + ]: 87 : if (rc < 0) {
71 : 0 : SPDK_ERRLOG("invalid cpumask %s\n", mask);
72 : 0 : return -1;
73 : : }
74 : :
75 : 87 : spdk_cpuset_copy(&negative_vhost_mask, &g_vhost_core_mask);
76 : 87 : spdk_cpuset_negate(&negative_vhost_mask);
77 : 87 : spdk_cpuset_and(&negative_vhost_mask, cpumask);
78 : :
79 [ + + ]: 87 : if (spdk_cpuset_count(&negative_vhost_mask) != 0) {
80 : 16 : SPDK_ERRLOG("one of selected cpu is outside of core mask(=%s)\n",
81 : : spdk_cpuset_fmt(&g_vhost_core_mask));
82 : 16 : return -1;
83 : : }
84 : :
85 : 71 : spdk_cpuset_and(cpumask, &g_vhost_core_mask);
86 : :
87 [ - + ]: 71 : if (spdk_cpuset_count(cpumask) == 0) {
88 : 0 : SPDK_ERRLOG("no cpu is selected among core mask(=%s)\n",
89 : : spdk_cpuset_fmt(&g_vhost_core_mask));
90 : 0 : return -1;
91 : : }
92 : :
93 : 71 : return 0;
94 : : }
95 : :
96 : : TAILQ_HEAD(, virtio_blk_transport_ops_list_element)
97 : : g_spdk_virtio_blk_transport_ops = TAILQ_HEAD_INITIALIZER(g_spdk_virtio_blk_transport_ops);
98 : :
99 : : const struct spdk_virtio_blk_transport_ops *
100 : 1181 : virtio_blk_get_transport_ops(const char *transport_name)
101 : : {
102 : : struct virtio_blk_transport_ops_list_element *ops;
103 [ + + ]: 1181 : TAILQ_FOREACH(ops, &g_spdk_virtio_blk_transport_ops, link) {
104 [ + + - + : 592 : if (strcasecmp(transport_name, ops->ops.name) == 0) {
+ - ]
105 : 592 : return &ops->ops;
106 : : }
107 : : }
108 : 589 : return NULL;
109 : : }
110 : :
111 : : int
112 : 160 : vhost_dev_register(struct spdk_vhost_dev *vdev, const char *name, const char *mask_str,
113 : : const struct spdk_json_val *params, const struct spdk_vhost_dev_backend *backend,
114 : : const struct spdk_vhost_user_dev_backend *user_backend, bool delay)
115 : : {
116 : 160 : struct spdk_cpuset cpumask = {};
117 : : int rc;
118 : :
119 [ - + ]: 160 : assert(vdev);
120 [ + + ]: 160 : if (name == NULL) {
121 : 6 : SPDK_ERRLOG("Can't register controller with no name\n");
122 : 6 : return -EINVAL;
123 : : }
124 : :
125 [ + + ]: 154 : if (vhost_parse_core_mask(mask_str, &cpumask) != 0) {
126 : 16 : SPDK_ERRLOG("cpumask %s is invalid (core mask is 0x%s)\n",
127 : : mask_str, spdk_cpuset_fmt(&g_vhost_core_mask));
128 : 16 : return -EINVAL;
129 : : }
130 : :
131 : 138 : spdk_vhost_lock();
132 [ + + ]: 138 : if (spdk_vhost_dev_find(name)) {
133 : 6 : SPDK_ERRLOG("vhost controller %s already exists.\n", name);
134 : 6 : spdk_vhost_unlock();
135 : 6 : return -EEXIST;
136 : : }
137 : :
138 [ - + ]: 132 : vdev->name = strdup(name);
139 [ - + ]: 132 : if (vdev->name == NULL) {
140 : 0 : spdk_vhost_unlock();
141 : 0 : return -EIO;
142 : : }
143 : :
144 : 132 : vdev->backend = backend;
145 [ + + ]: 132 : if (vdev->backend->type == VHOST_BACKEND_SCSI) {
146 : 89 : rc = vhost_user_dev_create(vdev, name, &cpumask, user_backend, delay);
147 : : } else {
148 : : /* When VHOST_BACKEND_BLK, delay should not be true. */
149 [ - + ]: 43 : assert(delay == false);
150 : 43 : rc = virtio_blk_construct_ctrlr(vdev, name, &cpumask, params, user_backend);
151 : : }
152 [ + + ]: 132 : if (rc != 0) {
153 : 8 : free(vdev->name);
154 : 8 : spdk_vhost_unlock();
155 : 8 : return rc;
156 : : }
157 : :
158 : 124 : TAILQ_INSERT_TAIL(&g_vhost_devices, vdev, tailq);
159 : 124 : spdk_vhost_unlock();
160 : :
161 [ - + - + ]: 124 : SPDK_INFOLOG(vhost, "Controller %s: new controller added\n", vdev->name);
162 : 124 : return 0;
163 : : }
164 : :
165 : : int
166 : 130 : vhost_dev_unregister(struct spdk_vhost_dev *vdev)
167 : : {
168 : : int rc;
169 : :
170 : 130 : spdk_vhost_lock();
171 [ + + ]: 130 : if (vdev->backend->type == VHOST_BACKEND_SCSI) {
172 : 88 : rc = vhost_user_dev_unregister(vdev);
173 : : } else {
174 : 42 : rc = virtio_blk_destroy_ctrlr(vdev);
175 : : }
176 [ + + ]: 130 : if (rc != 0) {
177 : 6 : spdk_vhost_unlock();
178 : 6 : return rc;
179 : : }
180 : :
181 [ - + - + ]: 124 : SPDK_INFOLOG(vhost, "Controller %s: removed\n", vdev->name);
182 : :
183 : 124 : free(vdev->name);
184 : :
185 [ + + ]: 124 : TAILQ_REMOVE(&g_vhost_devices, vdev, tailq);
186 [ + + + + ]: 124 : if (TAILQ_EMPTY(&g_vhost_devices) && g_fini_cb != NULL) {
187 : 13 : g_fini_cb();
188 : : }
189 : 124 : spdk_vhost_unlock();
190 : :
191 : 124 : return 0;
192 : : }
193 : :
194 : : const char *
195 : 846 : spdk_vhost_dev_get_name(struct spdk_vhost_dev *vdev)
196 : : {
197 [ - + ]: 846 : assert(vdev != NULL);
198 : 846 : return vdev->name;
199 : : }
200 : :
201 : : const struct spdk_cpuset *
202 : 0 : spdk_vhost_dev_get_cpumask(struct spdk_vhost_dev *vdev)
203 : : {
204 [ # # ]: 0 : assert(vdev != NULL);
205 : 0 : return spdk_thread_get_cpumask(vdev->thread);
206 : : }
207 : :
208 : : void
209 : 840 : vhost_dump_info_json(struct spdk_vhost_dev *vdev, struct spdk_json_write_ctx *w)
210 : : {
211 [ - + ]: 840 : assert(vdev->backend->dump_info_json != NULL);
212 : 840 : vdev->backend->dump_info_json(vdev, w);
213 : 840 : }
214 : :
215 : : int
216 : 76 : spdk_vhost_dev_remove(struct spdk_vhost_dev *vdev)
217 : : {
218 : 76 : return vdev->backend->remove_device(vdev);
219 : : }
220 : :
221 : : int
222 : 7 : spdk_vhost_set_coalescing(struct spdk_vhost_dev *vdev, uint32_t delay_base_us,
223 : : uint32_t iops_threshold)
224 : : {
225 [ - + ]: 7 : assert(vdev->backend->set_coalescing != NULL);
226 : 7 : return vdev->backend->set_coalescing(vdev, delay_base_us, iops_threshold);
227 : : }
228 : :
229 : : void
230 : 872 : spdk_vhost_get_coalescing(struct spdk_vhost_dev *vdev, uint32_t *delay_base_us,
231 : : uint32_t *iops_threshold)
232 : : {
233 [ - + ]: 872 : assert(vdev->backend->get_coalescing != NULL);
234 : 872 : vdev->backend->get_coalescing(vdev, delay_base_us, iops_threshold);
235 : 872 : }
236 : :
237 : : void
238 : 8834 : spdk_vhost_lock(void)
239 : : {
240 [ - + ]: 8834 : pthread_mutex_lock(&g_vhost_mutex);
241 : 8834 : }
242 : :
243 : : int
244 : 0 : spdk_vhost_trylock(void)
245 : : {
246 [ # # ]: 0 : return -pthread_mutex_trylock(&g_vhost_mutex);
247 : : }
248 : :
249 : : void
250 : 8834 : spdk_vhost_unlock(void)
251 : : {
252 [ - + ]: 8834 : pthread_mutex_unlock(&g_vhost_mutex);
253 : 8834 : }
254 : :
255 : : void
256 : 545 : spdk_vhost_scsi_init(spdk_vhost_init_cb init_cb)
257 : : {
258 : : uint32_t i;
259 : 545 : int ret = 0;
260 : :
261 : 545 : ret = vhost_user_init();
262 [ - + ]: 545 : if (ret != 0) {
263 : 0 : init_cb(ret);
264 : 0 : return;
265 : : }
266 : :
267 : 545 : spdk_cpuset_zero(&g_vhost_core_mask);
268 [ + + ]: 1346 : SPDK_ENV_FOREACH_CORE(i) {
269 : 801 : spdk_cpuset_set_cpu(&g_vhost_core_mask, i, true);
270 : : }
271 : 545 : init_cb(ret);
272 : : }
273 : :
274 : : static void
275 : 545 : vhost_fini(void)
276 : : {
277 : : struct spdk_vhost_dev *vdev, *tmp;
278 : :
279 [ + + ]: 545 : if (spdk_vhost_dev_next(NULL) == NULL) {
280 : 532 : g_fini_cb();
281 : 532 : return;
282 : : }
283 : :
284 : 13 : vdev = spdk_vhost_dev_next(NULL);
285 [ + + ]: 37 : while (vdev != NULL) {
286 : 24 : tmp = spdk_vhost_dev_next(vdev);
287 : 24 : spdk_vhost_dev_remove(vdev);
288 : : /* don't care if it fails, there's nothing we can do for now */
289 : 24 : vdev = tmp;
290 : : }
291 : :
292 : : /* g_fini_cb will get called when last device is unregistered. */
293 : : }
294 : :
295 : : void
296 : 545 : spdk_vhost_blk_init(spdk_vhost_init_cb init_cb)
297 : : {
298 : : uint32_t i;
299 : 545 : int ret = 0;
300 : :
301 : 545 : ret = virtio_blk_transport_create("vhost_user_blk", NULL);
302 [ - + ]: 545 : if (ret != 0) {
303 : 0 : goto out;
304 : : }
305 : :
306 : 545 : spdk_cpuset_zero(&g_vhost_core_mask);
307 [ + + ]: 1346 : SPDK_ENV_FOREACH_CORE(i) {
308 : 801 : spdk_cpuset_set_cpu(&g_vhost_core_mask, i, true);
309 : : }
310 : 545 : out:
311 : 545 : init_cb(ret);
312 : 545 : }
313 : :
314 : : void
315 : 545 : spdk_vhost_scsi_fini(spdk_vhost_fini_cb fini_cb)
316 : : {
317 : 545 : g_fini_cb = fini_cb;
318 : :
319 : 545 : vhost_user_fini(vhost_fini);
320 : 545 : }
321 : :
322 : : static void
323 : 1090 : virtio_blk_transports_destroy(void)
324 : : {
325 : 1090 : struct spdk_virtio_blk_transport *transport = TAILQ_FIRST(&g_virtio_blk_transports);
326 : :
327 [ + + ]: 1090 : if (transport == NULL) {
328 : 545 : g_fini_cb();
329 : 545 : return;
330 : : }
331 [ - + ]: 545 : TAILQ_REMOVE(&g_virtio_blk_transports, transport, tailq);
332 : 545 : virtio_blk_transport_destroy(transport, virtio_blk_transports_destroy);
333 : : }
334 : :
335 : : void
336 : 545 : spdk_vhost_blk_fini(spdk_vhost_fini_cb fini_cb)
337 : : {
338 : 545 : g_fini_cb = fini_cb;
339 : :
340 : 545 : virtio_blk_transports_destroy();
341 : 545 : }
342 : :
343 : : static void
344 : 32 : vhost_user_config_json(struct spdk_vhost_dev *vdev, struct spdk_json_write_ctx *w)
345 : : {
346 : 24 : uint32_t delay_base_us;
347 : 24 : uint32_t iops_threshold;
348 : :
349 : 32 : vdev->backend->write_config_json(vdev, w);
350 : :
351 : 32 : spdk_vhost_get_coalescing(vdev, &delay_base_us, &iops_threshold);
352 [ + + ]: 32 : if (delay_base_us) {
353 : 12 : spdk_json_write_object_begin(w);
354 : 12 : spdk_json_write_named_string(w, "method", "vhost_controller_set_coalescing");
355 : :
356 : 12 : spdk_json_write_named_object_begin(w, "params");
357 : 12 : spdk_json_write_named_string(w, "ctrlr", vdev->name);
358 : 12 : spdk_json_write_named_uint32(w, "delay_base_us", delay_base_us);
359 : 12 : spdk_json_write_named_uint32(w, "iops_threshold", iops_threshold);
360 : 12 : spdk_json_write_object_end(w);
361 : :
362 : 12 : spdk_json_write_object_end(w);
363 : : }
364 : 32 : }
365 : :
366 : : void
367 : 87 : spdk_vhost_scsi_config_json(struct spdk_json_write_ctx *w)
368 : : {
369 : : struct spdk_vhost_dev *vdev;
370 : :
371 : 87 : spdk_json_write_array_begin(w);
372 : :
373 : 87 : spdk_vhost_lock();
374 [ + + ]: 113 : for (vdev = spdk_vhost_dev_next(NULL); vdev != NULL;
375 : 26 : vdev = spdk_vhost_dev_next(vdev)) {
376 [ + + ]: 26 : if (vdev->backend->type == VHOST_BACKEND_SCSI) {
377 : 16 : vhost_user_config_json(vdev, w);
378 : : }
379 : : }
380 : 87 : spdk_vhost_unlock();
381 : :
382 : 87 : spdk_json_write_array_end(w);
383 : 87 : }
384 : :
385 : : static void
386 : 87 : vhost_blk_dump_config_json(struct spdk_json_write_ctx *w)
387 : : {
388 : : struct spdk_virtio_blk_transport *transport;
389 : :
390 : : /* Write vhost transports */
391 [ + + ]: 174 : TAILQ_FOREACH(transport, &g_virtio_blk_transports, tailq) {
392 : : /* Since vhost_user_blk is always added on SPDK startup,
393 : : * do not emit virtio_blk_create_transport RPC. */
394 [ - + - + ]: 87 : if (strcasecmp(transport->ops->name, "vhost_user_blk") != 0) {
395 : 0 : spdk_json_write_object_begin(w);
396 : 0 : spdk_json_write_named_string(w, "method", "virtio_blk_create_transport");
397 : 0 : spdk_json_write_named_object_begin(w, "params");
398 : 0 : transport->ops->dump_opts(transport, w);
399 : 0 : spdk_json_write_object_end(w);
400 : 0 : spdk_json_write_object_end(w);
401 : : }
402 : : }
403 : 87 : }
404 : :
405 : : void
406 : 87 : spdk_vhost_blk_config_json(struct spdk_json_write_ctx *w)
407 : : {
408 : : struct spdk_vhost_dev *vdev;
409 : :
410 : 87 : spdk_json_write_array_begin(w);
411 : :
412 : 87 : spdk_vhost_lock();
413 [ + + ]: 119 : for (vdev = spdk_vhost_dev_next(NULL); vdev != NULL;
414 : 32 : vdev = spdk_vhost_dev_next(vdev)) {
415 [ + + ]: 32 : if (vdev->backend->type == VHOST_BACKEND_BLK) {
416 : 16 : vhost_user_config_json(vdev, w);
417 : : }
418 : : }
419 : 87 : spdk_vhost_unlock();
420 : :
421 : 87 : vhost_blk_dump_config_json(w);
422 : :
423 : 87 : spdk_json_write_array_end(w);
424 : 87 : }
425 : :
426 : : void
427 : 589 : virtio_blk_transport_register(const struct spdk_virtio_blk_transport_ops *ops)
428 : : {
429 : : struct virtio_blk_transport_ops_list_element *new_ops;
430 : :
431 [ - + ]: 589 : if (virtio_blk_get_transport_ops(ops->name) != NULL) {
432 : 0 : SPDK_ERRLOG("Double registering virtio blk transport type %s.\n", ops->name);
433 : 0 : assert(false);
434 : : return;
435 : : }
436 : :
437 : 589 : new_ops = calloc(1, sizeof(*new_ops));
438 [ - + ]: 589 : if (new_ops == NULL) {
439 : 0 : SPDK_ERRLOG("Unable to allocate memory to register new transport type %s.\n", ops->name);
440 : 0 : assert(false);
441 : : return;
442 : : }
443 : :
444 : 589 : new_ops->ops = *ops;
445 : :
446 : 589 : TAILQ_INSERT_TAIL(&g_spdk_virtio_blk_transport_ops, new_ops, link);
447 : : }
448 : :
449 : : int
450 : 546 : virtio_blk_transport_create(const char *transport_name,
451 : : const struct spdk_json_val *params)
452 : : {
453 : 546 : const struct spdk_virtio_blk_transport_ops *ops = NULL;
454 : : struct spdk_virtio_blk_transport *transport;
455 : :
456 [ + + ]: 546 : TAILQ_FOREACH(transport, &g_virtio_blk_transports, tailq) {
457 [ - + - + : 1 : if (strcasecmp(transport->ops->name, transport_name) == 0) {
+ - ]
458 : 1 : return -EEXIST;
459 : : }
460 : : }
461 : :
462 : 545 : ops = virtio_blk_get_transport_ops(transport_name);
463 [ - + ]: 545 : if (!ops) {
464 : 0 : SPDK_ERRLOG("Transport type '%s' unavailable.\n", transport_name);
465 : 0 : return -ENOENT;
466 : : }
467 : :
468 : 545 : transport = ops->create(params);
469 [ - + ]: 545 : if (!transport) {
470 : 0 : SPDK_ERRLOG("Unable to create new transport of type %s\n", transport_name);
471 : 0 : return -EPERM;
472 : : }
473 : :
474 : 545 : transport->ops = ops;
475 : 545 : TAILQ_INSERT_TAIL(&g_virtio_blk_transports, transport, tailq);
476 : 545 : return 0;
477 : : }
478 : :
479 : : struct spdk_virtio_blk_transport *
480 : 0 : virtio_blk_transport_get_first(void)
481 : : {
482 : 0 : return TAILQ_FIRST(&g_virtio_blk_transports);
483 : : }
484 : :
485 : : struct spdk_virtio_blk_transport *
486 : 0 : virtio_blk_transport_get_next(struct spdk_virtio_blk_transport *transport)
487 : : {
488 : 0 : return TAILQ_NEXT(transport, tailq);
489 : : }
490 : :
491 : : void
492 : 0 : virtio_blk_transport_dump_opts(struct spdk_virtio_blk_transport *transport,
493 : : struct spdk_json_write_ctx *w)
494 : : {
495 : 0 : spdk_json_write_object_begin(w);
496 : :
497 : 0 : spdk_json_write_named_string(w, "name", transport->ops->name);
498 : :
499 [ # # ]: 0 : if (transport->ops->dump_opts) {
500 : 0 : transport->ops->dump_opts(transport, w);
501 : : }
502 : :
503 : 0 : spdk_json_write_object_end(w);
504 : 0 : }
505 : :
506 : : struct spdk_virtio_blk_transport *
507 : 0 : virtio_blk_tgt_get_transport(const char *transport_name)
508 : : {
509 : : struct spdk_virtio_blk_transport *transport;
510 : :
511 [ # # ]: 0 : TAILQ_FOREACH(transport, &g_virtio_blk_transports, tailq) {
512 [ # # # # : 0 : if (strcasecmp(transport->ops->name, transport_name) == 0) {
# # ]
513 : 0 : return transport;
514 : : }
515 : : }
516 : 0 : return NULL;
517 : : }
518 : :
519 : : int
520 : 545 : virtio_blk_transport_destroy(struct spdk_virtio_blk_transport *transport,
521 : : spdk_vhost_fini_cb cb_fn)
522 : : {
523 : 545 : return transport->ops->destroy(transport, cb_fn);
524 : : }
525 : :
526 : 589 : SPDK_LOG_REGISTER_COMPONENT(vhost)
527 : 589 : SPDK_LOG_REGISTER_COMPONENT(vhost_ring)
|