LCOV - code coverage report
Current view: top level - lib/vhost - vhost.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 147 251 58.6 %
Date: 2024-12-16 00:20:30 Functions: 28 45 62.2 %

          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          46 : 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             : }
      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          12 :         return RB_FIND(vhost_dev_name_tree, &g_vhost_devices, &find);
      56             : }
      57             : 
      58             : static int
      59          13 : vhost_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask)
      60             : {
      61             :         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             : }
      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             :         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             :         }
     112           1 :         return NULL;
     113             : }
     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             :         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             :         }
     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             :         } 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             : }
     172             : 
     173             : int
     174          10 : vhost_dev_unregister(struct spdk_vhost_dev *vdev)
     175             : {
     176             :         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             :         } 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             :         }
     197           9 :         spdk_vhost_unlock();
     198             : 
     199           9 :         return 0;
     200             : }
     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             :         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             :         }
     279           1 :         init_cb(ret);
     280             : }
     281             : 
     282             : static void
     283           1 : vhost_fini(void)
     284             : {
     285             :         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             : }
     302             : 
     303             : void
     304           1 : spdk_vhost_blk_init(spdk_vhost_init_cb init_cb)
     305             : {
     306             :         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             :         }
     318           1 : 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             : }
     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             :         }
     372           0 : }
     373             : 
     374             : void
     375           0 : spdk_vhost_scsi_config_json(struct spdk_json_write_ctx *w)
     376             : {
     377             :         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             :                 }
     387             :         }
     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             :         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             :                 }
     410             :         }
     411           0 : }
     412             : 
     413             : void
     414           0 : spdk_vhost_blk_config_json(struct spdk_json_write_ctx *w)
     415             : {
     416             :         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             :                 }
     426             :         }
     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             :         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             : }
     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             :         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             :         }
     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             : }
     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             :         }
     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             :         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             :         }
     524           0 :         return NULL;
     525             : }
     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)

Generated by: LCOV version 1.15