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