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 :
131 11 : spdk_vhost_lock();
132 11 : if (spdk_vhost_dev_find(name)) {
133 1 : SPDK_ERRLOG("vhost controller %s already exists.\n", name);
134 1 : spdk_vhost_unlock();
135 1 : return -EEXIST;
136 : }
137 :
138 10 : vdev->name = strdup(name);
139 10 : if (vdev->name == NULL) {
140 0 : spdk_vhost_unlock();
141 0 : return -EIO;
142 : }
143 :
144 10 : vdev->backend = backend;
145 10 : if (vdev->backend->type == VHOST_BACKEND_SCSI) {
146 9 : rc = vhost_user_dev_create(vdev, name, &cpumask, user_backend, delay);
147 : } else {
148 : /* When VHOST_BACKEND_BLK, delay should not be true. */
149 1 : assert(delay == false);
150 1 : rc = virtio_blk_construct_ctrlr(vdev, name, &cpumask, params, user_backend);
151 : }
152 10 : if (rc != 0) {
153 1 : free(vdev->name);
154 1 : spdk_vhost_unlock();
155 1 : return rc;
156 : }
157 :
158 9 : TAILQ_INSERT_TAIL(&g_vhost_devices, vdev, tailq);
159 9 : spdk_vhost_unlock();
160 :
161 9 : SPDK_INFOLOG(vhost, "Controller %s: new controller added\n", vdev->name);
162 9 : return 0;
163 : }
164 :
165 : int
166 10 : vhost_dev_unregister(struct spdk_vhost_dev *vdev)
167 : {
168 : int rc;
169 :
170 10 : spdk_vhost_lock();
171 10 : if (vdev->backend->type == VHOST_BACKEND_SCSI) {
172 9 : rc = vhost_user_dev_unregister(vdev);
173 : } else {
174 1 : rc = virtio_blk_destroy_ctrlr(vdev);
175 : }
176 10 : if (rc != 0) {
177 1 : spdk_vhost_unlock();
178 1 : return rc;
179 : }
180 :
181 9 : SPDK_INFOLOG(vhost, "Controller %s: removed\n", vdev->name);
182 :
183 9 : free(vdev->name);
184 :
185 9 : TAILQ_REMOVE(&g_vhost_devices, vdev, tailq);
186 9 : if (TAILQ_EMPTY(&g_vhost_devices) && g_fini_cb != NULL) {
187 0 : g_fini_cb();
188 : }
189 9 : spdk_vhost_unlock();
190 :
191 9 : return 0;
192 : }
193 :
194 : const char *
195 1 : spdk_vhost_dev_get_name(struct spdk_vhost_dev *vdev)
196 : {
197 1 : assert(vdev != NULL);
198 1 : 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 0 : vhost_dump_info_json(struct spdk_vhost_dev *vdev, struct spdk_json_write_ctx *w)
210 : {
211 0 : assert(vdev->backend->dump_info_json != NULL);
212 0 : vdev->backend->dump_info_json(vdev, w);
213 0 : }
214 :
215 : int
216 1 : spdk_vhost_dev_remove(struct spdk_vhost_dev *vdev)
217 : {
218 1 : return vdev->backend->remove_device(vdev);
219 : }
220 :
221 : int
222 0 : spdk_vhost_set_coalescing(struct spdk_vhost_dev *vdev, uint32_t delay_base_us,
223 : uint32_t iops_threshold)
224 : {
225 0 : assert(vdev->backend->set_coalescing != NULL);
226 0 : return vdev->backend->set_coalescing(vdev, delay_base_us, iops_threshold);
227 : }
228 :
229 : void
230 0 : spdk_vhost_get_coalescing(struct spdk_vhost_dev *vdev, uint32_t *delay_base_us,
231 : uint32_t *iops_threshold)
232 : {
233 0 : assert(vdev->backend->get_coalescing != NULL);
234 0 : vdev->backend->get_coalescing(vdev, delay_base_us, iops_threshold);
235 0 : }
236 :
237 : void
238 23 : spdk_vhost_lock(void)
239 : {
240 23 : pthread_mutex_lock(&g_vhost_mutex);
241 23 : }
242 :
243 : int
244 0 : spdk_vhost_trylock(void)
245 : {
246 0 : return -pthread_mutex_trylock(&g_vhost_mutex);
247 : }
248 :
249 : void
250 23 : spdk_vhost_unlock(void)
251 : {
252 23 : pthread_mutex_unlock(&g_vhost_mutex);
253 23 : }
254 :
255 : void
256 1 : spdk_vhost_scsi_init(spdk_vhost_init_cb init_cb)
257 : {
258 : uint32_t i;
259 1 : int ret = 0;
260 :
261 1 : ret = vhost_user_init();
262 1 : if (ret != 0) {
263 0 : init_cb(ret);
264 0 : return;
265 : }
266 :
267 1 : spdk_cpuset_zero(&g_vhost_core_mask);
268 2 : SPDK_ENV_FOREACH_CORE(i) {
269 1 : spdk_cpuset_set_cpu(&g_vhost_core_mask, i, true);
270 : }
271 1 : init_cb(ret);
272 : }
273 :
274 : static void
275 1 : vhost_fini(void)
276 : {
277 : struct spdk_vhost_dev *vdev, *tmp;
278 :
279 1 : if (spdk_vhost_dev_next(NULL) == NULL) {
280 1 : g_fini_cb();
281 1 : return;
282 : }
283 :
284 0 : vdev = spdk_vhost_dev_next(NULL);
285 0 : while (vdev != NULL) {
286 0 : tmp = spdk_vhost_dev_next(vdev);
287 0 : spdk_vhost_dev_remove(vdev);
288 : /* don't care if it fails, there's nothing we can do for now */
289 0 : vdev = tmp;
290 : }
291 :
292 : /* g_fini_cb will get called when last device is unregistered. */
293 : }
294 :
295 : void
296 1 : spdk_vhost_blk_init(spdk_vhost_init_cb init_cb)
297 : {
298 : uint32_t i;
299 1 : int ret = 0;
300 :
301 1 : ret = virtio_blk_transport_create("vhost_user_blk", NULL);
302 1 : if (ret != 0) {
303 0 : goto out;
304 : }
305 :
306 1 : spdk_cpuset_zero(&g_vhost_core_mask);
307 2 : SPDK_ENV_FOREACH_CORE(i) {
308 1 : spdk_cpuset_set_cpu(&g_vhost_core_mask, i, true);
309 : }
310 1 : out:
311 1 : init_cb(ret);
312 1 : }
313 :
314 : void
315 1 : spdk_vhost_scsi_fini(spdk_vhost_fini_cb fini_cb)
316 : {
317 1 : g_fini_cb = fini_cb;
318 :
319 1 : vhost_user_fini(vhost_fini);
320 1 : }
321 :
322 : static void
323 2 : virtio_blk_transports_destroy(void)
324 : {
325 2 : struct spdk_virtio_blk_transport *transport = TAILQ_FIRST(&g_virtio_blk_transports);
326 :
327 2 : if (transport == NULL) {
328 1 : g_fini_cb();
329 1 : return;
330 : }
331 1 : TAILQ_REMOVE(&g_virtio_blk_transports, transport, tailq);
332 1 : virtio_blk_transport_destroy(transport, virtio_blk_transports_destroy);
333 : }
334 :
335 : void
336 1 : spdk_vhost_blk_fini(spdk_vhost_fini_cb fini_cb)
337 : {
338 1 : g_fini_cb = fini_cb;
339 :
340 1 : virtio_blk_transports_destroy();
341 1 : }
342 :
343 : static void
344 0 : vhost_user_config_json(struct spdk_vhost_dev *vdev, struct spdk_json_write_ctx *w)
345 : {
346 0 : uint32_t delay_base_us;
347 0 : uint32_t iops_threshold;
348 :
349 0 : vdev->backend->write_config_json(vdev, w);
350 :
351 0 : spdk_vhost_get_coalescing(vdev, &delay_base_us, &iops_threshold);
352 0 : if (delay_base_us) {
353 0 : spdk_json_write_object_begin(w);
354 0 : spdk_json_write_named_string(w, "method", "vhost_controller_set_coalescing");
355 :
356 0 : spdk_json_write_named_object_begin(w, "params");
357 0 : spdk_json_write_named_string(w, "ctrlr", vdev->name);
358 0 : spdk_json_write_named_uint32(w, "delay_base_us", delay_base_us);
359 0 : spdk_json_write_named_uint32(w, "iops_threshold", iops_threshold);
360 0 : spdk_json_write_object_end(w);
361 :
362 0 : spdk_json_write_object_end(w);
363 : }
364 0 : }
365 :
366 : void
367 0 : spdk_vhost_scsi_config_json(struct spdk_json_write_ctx *w)
368 : {
369 : struct spdk_vhost_dev *vdev;
370 :
371 0 : spdk_json_write_array_begin(w);
372 :
373 0 : spdk_vhost_lock();
374 0 : for (vdev = spdk_vhost_dev_next(NULL); vdev != NULL;
375 0 : vdev = spdk_vhost_dev_next(vdev)) {
376 0 : if (vdev->backend->type == VHOST_BACKEND_SCSI) {
377 0 : vhost_user_config_json(vdev, w);
378 : }
379 : }
380 0 : spdk_vhost_unlock();
381 :
382 0 : spdk_json_write_array_end(w);
383 0 : }
384 :
385 : static void
386 0 : 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 0 : 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 0 : 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 0 : }
404 :
405 : void
406 0 : spdk_vhost_blk_config_json(struct spdk_json_write_ctx *w)
407 : {
408 : struct spdk_vhost_dev *vdev;
409 :
410 0 : spdk_json_write_array_begin(w);
411 :
412 0 : spdk_vhost_lock();
413 0 : for (vdev = spdk_vhost_dev_next(NULL); vdev != NULL;
414 0 : vdev = spdk_vhost_dev_next(vdev)) {
415 0 : if (vdev->backend->type == VHOST_BACKEND_BLK) {
416 0 : vhost_user_config_json(vdev, w);
417 : }
418 : }
419 0 : spdk_vhost_unlock();
420 :
421 0 : vhost_blk_dump_config_json(w);
422 :
423 0 : spdk_json_write_array_end(w);
424 0 : }
425 :
426 : void
427 1 : 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 1 : 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 1 : new_ops = calloc(1, sizeof(*new_ops));
438 1 : 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 1 : new_ops->ops = *ops;
445 :
446 1 : TAILQ_INSERT_TAIL(&g_spdk_virtio_blk_transport_ops, new_ops, link);
447 : }
448 :
449 : int
450 1 : virtio_blk_transport_create(const char *transport_name,
451 : const struct spdk_json_val *params)
452 : {
453 1 : const struct spdk_virtio_blk_transport_ops *ops = NULL;
454 : struct spdk_virtio_blk_transport *transport;
455 :
456 1 : TAILQ_FOREACH(transport, &g_virtio_blk_transports, tailq) {
457 0 : if (strcasecmp(transport->ops->name, transport_name) == 0) {
458 0 : return -EEXIST;
459 : }
460 : }
461 :
462 1 : ops = virtio_blk_get_transport_ops(transport_name);
463 1 : if (!ops) {
464 0 : SPDK_ERRLOG("Transport type '%s' unavailable.\n", transport_name);
465 0 : return -ENOENT;
466 : }
467 :
468 1 : transport = ops->create(params);
469 1 : if (!transport) {
470 0 : SPDK_ERRLOG("Unable to create new transport of type %s\n", transport_name);
471 0 : return -EPERM;
472 : }
473 :
474 1 : transport->ops = ops;
475 1 : TAILQ_INSERT_TAIL(&g_virtio_blk_transports, transport, tailq);
476 1 : 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 1 : virtio_blk_transport_destroy(struct spdk_virtio_blk_transport *transport,
521 : spdk_vhost_fini_cb cb_fn)
522 : {
523 1 : return transport->ops->destroy(transport, cb_fn);
524 : }
525 :
526 1 : SPDK_LOG_REGISTER_COMPONENT(vhost)
527 1 : SPDK_LOG_REGISTER_COMPONENT(vhost_ring)
|