LCOV - code coverage report
Current view: top level - lib/vhost - vhost.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 142 246 57.7 %
Date: 2024-07-15 02:08:34 Functions: 21 34 61.8 %

          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)

Generated by: LCOV version 1.15