LCOV - code coverage report
Current view: top level - lib/vhost - vhost.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 145 249 58.2 %
Date: 2024-11-05 10:06:02 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          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)

Generated by: LCOV version 1.15