1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 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 17 static struct spdk_cpuset g_vhost_core_mask; 18 19 static TAILQ_HEAD(, spdk_vhost_dev) g_vhost_devices = TAILQ_HEAD_INITIALIZER( 20 g_vhost_devices); 21 static pthread_mutex_t g_vhost_mutex = PTHREAD_MUTEX_INITIALIZER; 22 23 static TAILQ_HEAD(, spdk_virtio_blk_transport) g_virtio_blk_transports = TAILQ_HEAD_INITIALIZER( 24 g_virtio_blk_transports); 25 26 struct spdk_vhost_dev * 27 spdk_vhost_dev_next(struct spdk_vhost_dev *vdev) 28 { 29 if (vdev == NULL) { 30 return TAILQ_FIRST(&g_vhost_devices); 31 } 32 33 return TAILQ_NEXT(vdev, tailq); 34 } 35 36 struct spdk_vhost_dev * 37 spdk_vhost_dev_find(const char *ctrlr_name) 38 { 39 struct spdk_vhost_dev *vdev; 40 41 TAILQ_FOREACH(vdev, &g_vhost_devices, tailq) { 42 if (strcmp(vdev->name, ctrlr_name) == 0) { 43 return vdev; 44 } 45 } 46 47 return NULL; 48 } 49 50 static int 51 vhost_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask) 52 { 53 int rc; 54 struct spdk_cpuset negative_vhost_mask; 55 56 if (cpumask == NULL) { 57 return -1; 58 } 59 60 if (mask == NULL) { 61 spdk_cpuset_copy(cpumask, &g_vhost_core_mask); 62 return 0; 63 } 64 65 rc = spdk_cpuset_parse(cpumask, mask); 66 if (rc < 0) { 67 SPDK_ERRLOG("invalid cpumask %s\n", mask); 68 return -1; 69 } 70 71 spdk_cpuset_copy(&negative_vhost_mask, &g_vhost_core_mask); 72 spdk_cpuset_negate(&negative_vhost_mask); 73 spdk_cpuset_and(&negative_vhost_mask, cpumask); 74 75 if (spdk_cpuset_count(&negative_vhost_mask) != 0) { 76 SPDK_ERRLOG("one of selected cpu is outside of core mask(=%s)\n", 77 spdk_cpuset_fmt(&g_vhost_core_mask)); 78 return -1; 79 } 80 81 spdk_cpuset_and(cpumask, &g_vhost_core_mask); 82 83 if (spdk_cpuset_count(cpumask) == 0) { 84 SPDK_ERRLOG("no cpu is selected among core mask(=%s)\n", 85 spdk_cpuset_fmt(&g_vhost_core_mask)); 86 return -1; 87 } 88 89 return 0; 90 } 91 92 TAILQ_HEAD(, virtio_blk_transport_ops_list_element) 93 g_spdk_virtio_blk_transport_ops = TAILQ_HEAD_INITIALIZER(g_spdk_virtio_blk_transport_ops); 94 95 const struct spdk_virtio_blk_transport_ops * 96 virtio_blk_get_transport_ops(const char *transport_name) 97 { 98 struct virtio_blk_transport_ops_list_element *ops; 99 TAILQ_FOREACH(ops, &g_spdk_virtio_blk_transport_ops, link) { 100 if (strcasecmp(transport_name, ops->ops.name) == 0) { 101 return &ops->ops; 102 } 103 } 104 return NULL; 105 } 106 107 int 108 vhost_dev_register(struct spdk_vhost_dev *vdev, const char *name, const char *mask_str, 109 const struct spdk_json_val *params, 110 const struct spdk_vhost_dev_backend *backend, 111 const struct spdk_vhost_user_dev_backend *user_backend) 112 { 113 struct spdk_cpuset cpumask = {}; 114 int rc; 115 116 assert(vdev); 117 if (name == NULL) { 118 SPDK_ERRLOG("Can't register controller with no name\n"); 119 return -EINVAL; 120 } 121 122 if (vhost_parse_core_mask(mask_str, &cpumask) != 0) { 123 SPDK_ERRLOG("cpumask %s is invalid (core mask is 0x%s)\n", 124 mask_str, spdk_cpuset_fmt(&g_vhost_core_mask)); 125 return -EINVAL; 126 } 127 128 if (spdk_vhost_dev_find(name)) { 129 SPDK_ERRLOG("vhost controller %s already exists.\n", name); 130 return -EEXIST; 131 } 132 133 vdev->name = strdup(name); 134 if (vdev->name == NULL) { 135 return -EIO; 136 } 137 138 vdev->backend = backend; 139 if (vdev->backend->type == VHOST_BACKEND_SCSI) { 140 rc = vhost_user_dev_register(vdev, name, &cpumask, user_backend); 141 } else { 142 rc = virtio_blk_construct_ctrlr(vdev, name, &cpumask, params, user_backend); 143 } 144 if (rc != 0) { 145 free(vdev->name); 146 return rc; 147 } 148 149 TAILQ_INSERT_TAIL(&g_vhost_devices, vdev, tailq); 150 151 SPDK_INFOLOG(vhost, "Controller %s: new controller added\n", vdev->name); 152 return 0; 153 } 154 155 int 156 vhost_dev_unregister(struct spdk_vhost_dev *vdev) 157 { 158 int rc; 159 160 if (vdev->backend->type == VHOST_BACKEND_SCSI) { 161 rc = vhost_user_dev_unregister(vdev); 162 } else { 163 rc = virtio_blk_destroy_ctrlr(vdev); 164 } 165 if (rc != 0) { 166 return rc; 167 } 168 169 SPDK_INFOLOG(vhost, "Controller %s: removed\n", vdev->name); 170 171 free(vdev->name); 172 TAILQ_REMOVE(&g_vhost_devices, vdev, tailq); 173 return 0; 174 } 175 176 const char * 177 spdk_vhost_dev_get_name(struct spdk_vhost_dev *vdev) 178 { 179 assert(vdev != NULL); 180 return vdev->name; 181 } 182 183 const struct spdk_cpuset * 184 spdk_vhost_dev_get_cpumask(struct spdk_vhost_dev *vdev) 185 { 186 assert(vdev != NULL); 187 return spdk_thread_get_cpumask(vdev->thread); 188 } 189 190 void 191 vhost_dump_info_json(struct spdk_vhost_dev *vdev, struct spdk_json_write_ctx *w) 192 { 193 assert(vdev->backend->dump_info_json != NULL); 194 vdev->backend->dump_info_json(vdev, w); 195 } 196 197 int 198 spdk_vhost_dev_remove(struct spdk_vhost_dev *vdev) 199 { 200 return vdev->backend->remove_device(vdev); 201 } 202 203 void 204 spdk_vhost_lock(void) 205 { 206 pthread_mutex_lock(&g_vhost_mutex); 207 } 208 209 int 210 spdk_vhost_trylock(void) 211 { 212 return -pthread_mutex_trylock(&g_vhost_mutex); 213 } 214 215 void 216 spdk_vhost_unlock(void) 217 { 218 pthread_mutex_unlock(&g_vhost_mutex); 219 } 220 221 void 222 spdk_vhost_scsi_init(spdk_vhost_init_cb init_cb) 223 { 224 uint32_t i; 225 int ret = 0; 226 227 ret = vhost_user_init(); 228 if (ret != 0) { 229 init_cb(ret); 230 return; 231 } 232 233 spdk_cpuset_zero(&g_vhost_core_mask); 234 SPDK_ENV_FOREACH_CORE(i) { 235 spdk_cpuset_set_cpu(&g_vhost_core_mask, i, true); 236 } 237 init_cb(ret); 238 } 239 240 static spdk_vhost_fini_cb g_fini_cb; 241 242 static void 243 vhost_fini(void) 244 { 245 struct spdk_vhost_dev *vdev, *tmp; 246 247 spdk_vhost_lock(); 248 vdev = spdk_vhost_dev_next(NULL); 249 while (vdev != NULL) { 250 tmp = spdk_vhost_dev_next(vdev); 251 spdk_vhost_dev_remove(vdev); 252 /* don't care if it fails, there's nothing we can do for now */ 253 vdev = tmp; 254 } 255 spdk_vhost_unlock(); 256 257 g_fini_cb(); 258 } 259 260 void 261 spdk_vhost_blk_init(spdk_vhost_init_cb init_cb) 262 { 263 uint32_t i; 264 int ret = 0; 265 266 ret = virtio_blk_transport_create("vhost_user_blk", NULL); 267 if (ret != 0) { 268 goto out; 269 } 270 271 spdk_cpuset_zero(&g_vhost_core_mask); 272 SPDK_ENV_FOREACH_CORE(i) { 273 spdk_cpuset_set_cpu(&g_vhost_core_mask, i, true); 274 } 275 out: 276 init_cb(ret); 277 } 278 279 void 280 spdk_vhost_scsi_fini(spdk_vhost_fini_cb fini_cb) 281 { 282 g_fini_cb = fini_cb; 283 284 vhost_user_fini(vhost_fini); 285 } 286 287 static void 288 virtio_blk_transports_destroy(void) 289 { 290 struct spdk_virtio_blk_transport *transport = TAILQ_FIRST(&g_virtio_blk_transports); 291 292 if (transport == NULL) { 293 g_fini_cb(); 294 return; 295 } 296 TAILQ_REMOVE(&g_virtio_blk_transports, transport, tailq); 297 virtio_blk_transport_destroy(transport, virtio_blk_transports_destroy); 298 } 299 300 void 301 spdk_vhost_blk_fini(spdk_vhost_fini_cb fini_cb) 302 { 303 g_fini_cb = fini_cb; 304 305 virtio_blk_transports_destroy(); 306 } 307 308 static void 309 vhost_user_config_json(struct spdk_vhost_dev *vdev, struct spdk_json_write_ctx *w) 310 { 311 uint32_t delay_base_us; 312 uint32_t iops_threshold; 313 314 vdev->backend->write_config_json(vdev, w); 315 316 spdk_vhost_get_coalescing(vdev, &delay_base_us, &iops_threshold); 317 if (delay_base_us) { 318 spdk_json_write_object_begin(w); 319 spdk_json_write_named_string(w, "method", "vhost_controller_set_coalescing"); 320 321 spdk_json_write_named_object_begin(w, "params"); 322 spdk_json_write_named_string(w, "ctrlr", vdev->name); 323 spdk_json_write_named_uint32(w, "delay_base_us", delay_base_us); 324 spdk_json_write_named_uint32(w, "iops_threshold", iops_threshold); 325 spdk_json_write_object_end(w); 326 327 spdk_json_write_object_end(w); 328 } 329 } 330 331 void 332 spdk_vhost_scsi_config_json(struct spdk_json_write_ctx *w) 333 { 334 struct spdk_vhost_dev *vdev; 335 336 spdk_json_write_array_begin(w); 337 338 spdk_vhost_lock(); 339 for (vdev = spdk_vhost_dev_next(NULL); vdev != NULL; 340 vdev = spdk_vhost_dev_next(vdev)) { 341 if (vdev->backend->type == VHOST_BACKEND_SCSI) { 342 vhost_user_config_json(vdev, w); 343 } 344 } 345 spdk_vhost_unlock(); 346 347 spdk_json_write_array_end(w); 348 } 349 350 void 351 spdk_vhost_blk_config_json(struct spdk_json_write_ctx *w) 352 { 353 struct spdk_vhost_dev *vdev; 354 355 spdk_json_write_array_begin(w); 356 357 spdk_vhost_lock(); 358 for (vdev = spdk_vhost_dev_next(NULL); vdev != NULL; 359 vdev = spdk_vhost_dev_next(vdev)) { 360 if (vdev->backend->type == VHOST_BACKEND_BLK) { 361 vhost_user_config_json(vdev, w); 362 } 363 } 364 spdk_vhost_unlock(); 365 366 spdk_json_write_array_end(w); 367 } 368 369 void 370 virtio_blk_transport_register(const struct spdk_virtio_blk_transport_ops *ops) 371 { 372 struct virtio_blk_transport_ops_list_element *new_ops; 373 374 if (virtio_blk_get_transport_ops(ops->name) != NULL) { 375 SPDK_ERRLOG("Double registering virtio blk transport type %s.\n", ops->name); 376 assert(false); 377 return; 378 } 379 380 new_ops = calloc(1, sizeof(*new_ops)); 381 if (new_ops == NULL) { 382 SPDK_ERRLOG("Unable to allocate memory to register new transport type %s.\n", ops->name); 383 assert(false); 384 return; 385 } 386 387 new_ops->ops = *ops; 388 389 TAILQ_INSERT_TAIL(&g_spdk_virtio_blk_transport_ops, new_ops, link); 390 } 391 392 int 393 virtio_blk_transport_create(const char *transport_name, 394 const struct spdk_json_val *params) 395 { 396 const struct spdk_virtio_blk_transport_ops *ops = NULL; 397 struct spdk_virtio_blk_transport *transport; 398 399 TAILQ_FOREACH(transport, &g_virtio_blk_transports, tailq) { 400 if (strcasecmp(transport->ops->name, transport_name) == 0) { 401 return -EEXIST; 402 } 403 } 404 405 ops = virtio_blk_get_transport_ops(transport_name); 406 if (!ops) { 407 SPDK_ERRLOG("Transport type '%s' unavailable.\n", transport_name); 408 return -ENOENT; 409 } 410 411 transport = ops->create(params); 412 if (!transport) { 413 SPDK_ERRLOG("Unable to create new transport of type %s\n", transport_name); 414 return -EPERM; 415 } 416 417 transport->ops = ops; 418 TAILQ_INSERT_TAIL(&g_virtio_blk_transports, transport, tailq); 419 return 0; 420 } 421 422 int 423 virtio_blk_transport_destroy(struct spdk_virtio_blk_transport *transport, 424 spdk_vhost_fini_cb cb_fn) 425 { 426 return transport->ops->destroy(transport, cb_fn); 427 } 428 429 SPDK_LOG_REGISTER_COMPONENT(vhost) 430 SPDK_LOG_REGISTER_COMPONENT(vhost_ring) 431