xref: /spdk/lib/vhost/vhost.c (revision 307b8c112ffd90a26d53dd15fad67bd9038ef526)
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