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