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