xref: /spdk/lib/vhost/vhost_rpc.c (revision 1fc4165fe9bf8512483356ad8e6d27f793f2e3db)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 
36 #include "spdk_internal/log.h"
37 #include "spdk/rpc.h"
38 #include "spdk/util.h"
39 #include "spdk/string.h"
40 #include "spdk/env.h"
41 
42 #include "spdk/scsi.h"
43 #include "spdk/vhost.h"
44 #include "vhost_internal.h"
45 #include "spdk/bdev.h"
46 
47 struct rpc_vhost_scsi_ctrlr {
48 	char *ctrlr;
49 	char *cpumask;
50 };
51 
52 static void
53 free_rpc_vhost_scsi_ctrlr(struct rpc_vhost_scsi_ctrlr *req)
54 {
55 	free(req->ctrlr);
56 	free(req->cpumask);
57 }
58 
59 static const struct spdk_json_object_decoder rpc_construct_vhost_ctrlr[] = {
60 	{"ctrlr", offsetof(struct rpc_vhost_scsi_ctrlr, ctrlr), spdk_json_decode_string },
61 	{"cpumask", offsetof(struct rpc_vhost_scsi_ctrlr, cpumask), spdk_json_decode_string, true},
62 };
63 
64 static void
65 spdk_rpc_construct_vhost_scsi_controller(struct spdk_jsonrpc_request *request,
66 		const struct spdk_json_val *params)
67 {
68 	struct rpc_vhost_scsi_ctrlr req = {0};
69 	struct spdk_json_write_ctx *w;
70 	int rc;
71 
72 	if (spdk_json_decode_object(params, rpc_construct_vhost_ctrlr,
73 				    SPDK_COUNTOF(rpc_construct_vhost_ctrlr),
74 				    &req)) {
75 		SPDK_DEBUGLOG(SPDK_LOG_VHOST_RPC, "spdk_json_decode_object failed\n");
76 		rc = -EINVAL;
77 		goto invalid;
78 	}
79 
80 	rc = spdk_vhost_scsi_dev_construct(req.ctrlr, req.cpumask);
81 	if (rc < 0) {
82 		goto invalid;
83 	}
84 
85 	free_rpc_vhost_scsi_ctrlr(&req);
86 
87 	w = spdk_jsonrpc_begin_result(request);
88 	if (w == NULL) {
89 		return;
90 	}
91 
92 	spdk_json_write_bool(w, true);
93 	spdk_jsonrpc_end_result(request, w);
94 	return;
95 
96 invalid:
97 	free_rpc_vhost_scsi_ctrlr(&req);
98 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
99 					 spdk_strerror(-rc));
100 }
101 SPDK_RPC_REGISTER("construct_vhost_scsi_controller", spdk_rpc_construct_vhost_scsi_controller,
102 		  SPDK_RPC_RUNTIME)
103 
104 struct rpc_add_vhost_scsi_ctrlr_lun {
105 	char *ctrlr;
106 	int32_t scsi_target_num;
107 	char *bdev_name;
108 };
109 
110 static void
111 free_rpc_add_vhost_scsi_ctrlr_lun(struct rpc_add_vhost_scsi_ctrlr_lun *req)
112 {
113 	free(req->ctrlr);
114 	free(req->bdev_name);
115 }
116 
117 static const struct spdk_json_object_decoder rpc_vhost_add_lun[] = {
118 	{"ctrlr", offsetof(struct rpc_add_vhost_scsi_ctrlr_lun, ctrlr), spdk_json_decode_string },
119 	{"scsi_target_num", offsetof(struct rpc_add_vhost_scsi_ctrlr_lun, scsi_target_num), spdk_json_decode_int32},
120 	{"bdev_name", offsetof(struct rpc_add_vhost_scsi_ctrlr_lun, bdev_name), spdk_json_decode_string },
121 };
122 
123 static void
124 spdk_rpc_add_vhost_scsi_lun(struct spdk_jsonrpc_request *request,
125 			    const struct spdk_json_val *params)
126 {
127 	struct rpc_add_vhost_scsi_ctrlr_lun req = {0};
128 	struct spdk_json_write_ctx *w;
129 	struct spdk_vhost_dev *vdev;
130 	int rc;
131 
132 	if (spdk_json_decode_object(params, rpc_vhost_add_lun,
133 				    SPDK_COUNTOF(rpc_vhost_add_lun),
134 				    &req)) {
135 		SPDK_DEBUGLOG(SPDK_LOG_VHOST_RPC, "spdk_json_decode_object failed\n");
136 		rc = -EINVAL;
137 		goto invalid;
138 	}
139 
140 	spdk_vhost_lock();
141 	vdev = spdk_vhost_dev_find(req.ctrlr);
142 	if (vdev == NULL) {
143 		spdk_vhost_unlock();
144 		rc = -ENODEV;
145 		goto invalid;
146 	}
147 
148 	rc = spdk_vhost_scsi_dev_add_tgt(vdev, req.scsi_target_num, req.bdev_name);
149 	spdk_vhost_unlock();
150 	if (rc < 0) {
151 		goto invalid;
152 	}
153 
154 	free_rpc_add_vhost_scsi_ctrlr_lun(&req);
155 
156 	w = spdk_jsonrpc_begin_result(request);
157 	if (w == NULL) {
158 		return;
159 	}
160 
161 	spdk_json_write_int32(w, rc);
162 	spdk_jsonrpc_end_result(request, w);
163 	return;
164 
165 invalid:
166 	free_rpc_add_vhost_scsi_ctrlr_lun(&req);
167 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
168 					 spdk_strerror(-rc));
169 }
170 SPDK_RPC_REGISTER("add_vhost_scsi_lun", spdk_rpc_add_vhost_scsi_lun, SPDK_RPC_RUNTIME)
171 
172 struct rpc_remove_vhost_scsi_ctrlr_target {
173 	char *ctrlr;
174 	uint32_t scsi_target_num;
175 };
176 
177 static void
178 free_rpc_remove_vhost_scsi_ctrlr_target(struct rpc_remove_vhost_scsi_ctrlr_target *req)
179 {
180 	free(req->ctrlr);
181 }
182 
183 static const struct spdk_json_object_decoder rpc_vhost_remove_target[] = {
184 	{"ctrlr", offsetof(struct rpc_remove_vhost_scsi_ctrlr_target, ctrlr), spdk_json_decode_string },
185 	{"scsi_target_num", offsetof(struct rpc_remove_vhost_scsi_ctrlr_target, scsi_target_num), spdk_json_decode_uint32},
186 };
187 
188 static int
189 spdk_rpc_remove_vhost_scsi_target_finish_cb(struct spdk_vhost_dev *vdev, void *arg)
190 {
191 	struct spdk_jsonrpc_request *request = arg;
192 	struct spdk_json_write_ctx *w;
193 
194 	w = spdk_jsonrpc_begin_result(request);
195 	if (w == NULL) {
196 		return -1;
197 	}
198 
199 	spdk_json_write_bool(w, true);
200 	spdk_jsonrpc_end_result(request, w);
201 	return 0;
202 }
203 
204 static void
205 spdk_rpc_remove_vhost_scsi_target(struct spdk_jsonrpc_request *request,
206 				  const struct spdk_json_val *params)
207 {
208 	struct rpc_remove_vhost_scsi_ctrlr_target req = {0};
209 	struct spdk_vhost_dev *vdev;
210 	int rc;
211 
212 	if (spdk_json_decode_object(params, rpc_vhost_remove_target,
213 				    SPDK_COUNTOF(rpc_vhost_remove_target),
214 				    &req)) {
215 		SPDK_DEBUGLOG(SPDK_LOG_VHOST_RPC, "spdk_json_decode_object failed\n");
216 		rc = -EINVAL;
217 		goto invalid;
218 	}
219 
220 	spdk_vhost_lock();
221 	vdev = spdk_vhost_dev_find(req.ctrlr);
222 	if (vdev == NULL) {
223 		spdk_vhost_unlock();
224 		rc = -ENODEV;
225 		goto invalid;
226 	}
227 
228 	rc = spdk_vhost_scsi_dev_remove_tgt(vdev, req.scsi_target_num,
229 					    spdk_rpc_remove_vhost_scsi_target_finish_cb,
230 					    request);
231 	spdk_vhost_unlock();
232 	if (rc < 0) {
233 		goto invalid;
234 	}
235 
236 	free_rpc_remove_vhost_scsi_ctrlr_target(&req);
237 	return;
238 
239 invalid:
240 	free_rpc_remove_vhost_scsi_ctrlr_target(&req);
241 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
242 					 spdk_strerror(-rc));
243 }
244 
245 SPDK_RPC_REGISTER("remove_vhost_scsi_target", spdk_rpc_remove_vhost_scsi_target, SPDK_RPC_RUNTIME)
246 
247 struct rpc_vhost_blk_ctrlr {
248 	char *ctrlr;
249 	char *dev_name;
250 	char *cpumask;
251 	bool readonly;
252 };
253 
254 static const struct spdk_json_object_decoder rpc_construct_vhost_blk_ctrlr[] = {
255 	{"ctrlr", offsetof(struct rpc_vhost_blk_ctrlr, ctrlr), spdk_json_decode_string },
256 	{"dev_name", offsetof(struct rpc_vhost_blk_ctrlr, dev_name), spdk_json_decode_string },
257 	{"cpumask", offsetof(struct rpc_vhost_blk_ctrlr, cpumask), spdk_json_decode_string, true},
258 	{"readonly", offsetof(struct rpc_vhost_blk_ctrlr, readonly), spdk_json_decode_bool, true},
259 };
260 
261 static void
262 free_rpc_vhost_blk_ctrlr(struct rpc_vhost_blk_ctrlr *req)
263 {
264 	free(req->ctrlr);
265 	free(req->dev_name);
266 	free(req->cpumask);
267 }
268 
269 static void
270 spdk_rpc_construct_vhost_blk_controller(struct spdk_jsonrpc_request *request,
271 					const struct spdk_json_val *params)
272 {
273 	struct rpc_vhost_blk_ctrlr req = {0};
274 	struct spdk_json_write_ctx *w;
275 	int rc;
276 
277 	if (spdk_json_decode_object(params, rpc_construct_vhost_blk_ctrlr,
278 				    SPDK_COUNTOF(rpc_construct_vhost_blk_ctrlr),
279 				    &req)) {
280 		SPDK_DEBUGLOG(SPDK_LOG_VHOST_RPC, "spdk_json_decode_object failed\n");
281 		rc = -EINVAL;
282 		goto invalid;
283 	}
284 
285 	rc = spdk_vhost_blk_construct(req.ctrlr, req.cpumask, req.dev_name, req.readonly);
286 	if (rc < 0) {
287 		goto invalid;
288 	}
289 
290 	free_rpc_vhost_blk_ctrlr(&req);
291 
292 	w = spdk_jsonrpc_begin_result(request);
293 	if (w == NULL) {
294 		return;
295 	}
296 
297 	spdk_json_write_bool(w, true);
298 	spdk_jsonrpc_end_result(request, w);
299 	return;
300 
301 invalid:
302 	free_rpc_vhost_blk_ctrlr(&req);
303 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
304 					 spdk_strerror(-rc));
305 
306 }
307 SPDK_RPC_REGISTER("construct_vhost_blk_controller", spdk_rpc_construct_vhost_blk_controller,
308 		  SPDK_RPC_RUNTIME)
309 
310 struct rpc_remove_vhost_ctrlr {
311 	char *ctrlr;
312 };
313 
314 static const struct spdk_json_object_decoder rpc_remove_vhost_ctrlr[] = {
315 	{"ctrlr", offsetof(struct rpc_remove_vhost_ctrlr, ctrlr), spdk_json_decode_string },
316 };
317 
318 static void
319 free_rpc_remove_vhost_ctrlr(struct rpc_remove_vhost_ctrlr *req)
320 {
321 	free(req->ctrlr);
322 }
323 
324 static void
325 spdk_rpc_remove_vhost_controller(struct spdk_jsonrpc_request *request,
326 				 const struct spdk_json_val *params)
327 {
328 	struct rpc_remove_vhost_ctrlr req = {0};
329 	struct spdk_json_write_ctx *w;
330 	struct spdk_vhost_dev *vdev;
331 	int rc;
332 
333 	if (spdk_json_decode_object(params, rpc_remove_vhost_ctrlr,
334 				    SPDK_COUNTOF(rpc_remove_vhost_ctrlr), &req)) {
335 		SPDK_DEBUGLOG(SPDK_LOG_VHOST_RPC, "spdk_json_decode_object failed\n");
336 		rc = -EINVAL;
337 		goto invalid;
338 	}
339 
340 	spdk_vhost_lock();
341 	vdev = spdk_vhost_dev_find(req.ctrlr);
342 	if (vdev == NULL) {
343 		spdk_vhost_unlock();
344 		rc = -ENODEV;
345 		goto invalid;
346 	}
347 
348 	rc = spdk_vhost_dev_remove(vdev);
349 	spdk_vhost_unlock();
350 	if (rc < 0) {
351 		goto invalid;
352 	}
353 
354 	free_rpc_remove_vhost_ctrlr(&req);
355 
356 	w = spdk_jsonrpc_begin_result(request);
357 	if (w == NULL) {
358 		return;
359 	}
360 
361 	spdk_json_write_bool(w, true);
362 	spdk_jsonrpc_end_result(request, w);
363 
364 	return;
365 
366 invalid:
367 	free_rpc_remove_vhost_ctrlr(&req);
368 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
369 					 spdk_strerror(-rc));
370 
371 }
372 SPDK_RPC_REGISTER("remove_vhost_controller", spdk_rpc_remove_vhost_controller, SPDK_RPC_RUNTIME)
373 
374 struct rpc_get_vhost_ctrlrs {
375 	char *name;
376 };
377 
378 static void
379 _spdk_rpc_get_vhost_controller(struct spdk_json_write_ctx *w, struct spdk_vhost_dev *vdev)
380 {
381 	uint32_t delay_base_us, iops_threshold;
382 
383 	spdk_vhost_get_coalescing(vdev, &delay_base_us, &iops_threshold);
384 
385 	spdk_json_write_object_begin(w);
386 
387 	spdk_json_write_named_string(w, "ctrlr", spdk_vhost_dev_get_name(vdev));
388 	spdk_json_write_named_string_fmt(w, "cpumask", "0x%s", spdk_cpuset_fmt(vdev->cpumask));
389 	spdk_json_write_named_uint32(w, "delay_base_us", delay_base_us);
390 	spdk_json_write_named_uint32(w, "iops_threshold", iops_threshold);
391 	spdk_json_write_named_string(w, "socket", vdev->path);
392 
393 	spdk_json_write_named_object_begin(w, "backend_specific");
394 	spdk_vhost_dump_info_json(vdev, w);
395 	spdk_json_write_object_end(w);
396 
397 	spdk_json_write_object_end(w);
398 }
399 
400 static const struct spdk_json_object_decoder rpc_get_vhost_ctrlrs_decoders[] = {
401 	{"name", offsetof(struct rpc_get_vhost_ctrlrs, name), spdk_json_decode_string, true},
402 };
403 
404 static void
405 free_rpc_get_vhost_ctrlrs(struct rpc_get_vhost_ctrlrs *req)
406 {
407 	free(req->name);
408 }
409 
410 static void
411 spdk_rpc_get_vhost_controllers(struct spdk_jsonrpc_request *request,
412 			       const struct spdk_json_val *params)
413 {
414 	struct rpc_get_vhost_ctrlrs req = {0};
415 	struct spdk_json_write_ctx *w;
416 	struct spdk_vhost_dev *vdev;
417 	int rc;
418 
419 	if (params && spdk_json_decode_object(params, rpc_get_vhost_ctrlrs_decoders,
420 					      SPDK_COUNTOF(rpc_get_vhost_ctrlrs_decoders), &req)) {
421 		SPDK_ERRLOG("spdk_json_decode_object failed\n");
422 		rc = -EINVAL;
423 		goto invalid;
424 	}
425 
426 	spdk_vhost_lock();
427 	if (req.name != NULL) {
428 		vdev = spdk_vhost_dev_find(req.name);
429 		if (vdev == NULL) {
430 			spdk_vhost_unlock();
431 			rc = -ENODEV;
432 			goto invalid;
433 		}
434 
435 		free_rpc_get_vhost_ctrlrs(&req);
436 
437 		w = spdk_jsonrpc_begin_result(request);
438 		if (w == NULL) {
439 			spdk_vhost_unlock();
440 			return;
441 		}
442 
443 		spdk_json_write_array_begin(w);
444 
445 		_spdk_rpc_get_vhost_controller(w, vdev);
446 		spdk_vhost_unlock();
447 
448 		spdk_json_write_array_end(w);
449 		spdk_jsonrpc_end_result(request, w);
450 		return;
451 	}
452 
453 	free_rpc_get_vhost_ctrlrs(&req);
454 
455 	w = spdk_jsonrpc_begin_result(request);
456 	if (w == NULL) {
457 		spdk_vhost_unlock();
458 		return;
459 	}
460 	spdk_json_write_array_begin(w);
461 
462 	vdev = spdk_vhost_dev_next(NULL);
463 	while (vdev != NULL) {
464 		_spdk_rpc_get_vhost_controller(w, vdev);
465 		vdev = spdk_vhost_dev_next(vdev);
466 	}
467 	spdk_vhost_unlock();
468 
469 	spdk_json_write_array_end(w);
470 	spdk_jsonrpc_end_result(request, w);
471 	return;
472 
473 invalid:
474 	free_rpc_get_vhost_ctrlrs(&req);
475 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
476 					 spdk_strerror(-rc));
477 }
478 SPDK_RPC_REGISTER("get_vhost_controllers", spdk_rpc_get_vhost_controllers, SPDK_RPC_RUNTIME)
479 
480 
481 struct rpc_vhost_ctrlr_coalescing {
482 	char *ctrlr;
483 	uint32_t delay_base_us;
484 	uint32_t iops_threshold;
485 };
486 
487 static const struct spdk_json_object_decoder rpc_set_vhost_ctrlr_coalescing[] = {
488 	{"ctrlr", offsetof(struct rpc_vhost_ctrlr_coalescing, ctrlr), spdk_json_decode_string },
489 	{"delay_base_us", offsetof(struct rpc_vhost_ctrlr_coalescing, delay_base_us), spdk_json_decode_uint32},
490 	{"iops_threshold", offsetof(struct rpc_vhost_ctrlr_coalescing, iops_threshold), spdk_json_decode_uint32},
491 };
492 
493 static void
494 free_rpc_set_vhost_controllers_event_coalescing(struct rpc_vhost_ctrlr_coalescing *req)
495 {
496 	free(req->ctrlr);
497 }
498 
499 static void
500 spdk_rpc_set_vhost_controller_coalescing(struct spdk_jsonrpc_request *request,
501 		const struct spdk_json_val *params)
502 {
503 	struct rpc_vhost_ctrlr_coalescing req = {0};
504 	struct spdk_json_write_ctx *w;
505 	struct spdk_vhost_dev *vdev;
506 	int rc;
507 
508 	if (spdk_json_decode_object(params, rpc_set_vhost_ctrlr_coalescing,
509 				    SPDK_COUNTOF(rpc_set_vhost_ctrlr_coalescing), &req)) {
510 		SPDK_DEBUGLOG(SPDK_LOG_VHOST_RPC, "spdk_json_decode_object failed\n");
511 		rc = -EINVAL;
512 		goto invalid;
513 	}
514 
515 	spdk_vhost_lock();
516 	vdev = spdk_vhost_dev_find(req.ctrlr);
517 	if (vdev == NULL) {
518 		spdk_vhost_unlock();
519 		rc = -ENODEV;
520 		goto invalid;
521 	}
522 
523 	rc = spdk_vhost_set_coalescing(vdev, req.delay_base_us, req.iops_threshold);
524 	spdk_vhost_unlock();
525 	if (rc) {
526 		goto invalid;
527 	}
528 
529 	free_rpc_set_vhost_controllers_event_coalescing(&req);
530 
531 	w = spdk_jsonrpc_begin_result(request);
532 	if (w != NULL) {
533 		spdk_json_write_bool(w, true);
534 		spdk_jsonrpc_end_result(request, w);
535 	}
536 
537 	return;
538 
539 invalid:
540 	free_rpc_set_vhost_controllers_event_coalescing(&req);
541 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
542 					 spdk_strerror(-rc));
543 }
544 SPDK_RPC_REGISTER("set_vhost_controller_coalescing", spdk_rpc_set_vhost_controller_coalescing,
545 		  SPDK_RPC_RUNTIME)
546 
547 struct rpc_vhost_nvme_ctrlr {
548 	char *ctrlr;
549 	uint32_t io_queues;
550 	char *cpumask;
551 };
552 
553 static const struct spdk_json_object_decoder rpc_construct_vhost_nvme_ctrlr[] = {
554 	{"ctrlr", offsetof(struct rpc_vhost_nvme_ctrlr, ctrlr), spdk_json_decode_string },
555 	{"io_queues", offsetof(struct rpc_vhost_nvme_ctrlr, io_queues), spdk_json_decode_uint32},
556 	{"cpumask", offsetof(struct rpc_vhost_nvme_ctrlr, cpumask), spdk_json_decode_string, true},
557 };
558 
559 static void
560 free_rpc_vhost_nvme_ctrlr(struct rpc_vhost_nvme_ctrlr *req)
561 {
562 	free(req->ctrlr);
563 	free(req->cpumask);
564 }
565 
566 static void
567 spdk_rpc_construct_vhost_nvme_controller(struct spdk_jsonrpc_request *request,
568 		const struct spdk_json_val *params)
569 {
570 	struct rpc_vhost_nvme_ctrlr req = {};
571 	struct spdk_json_write_ctx *w;
572 	int rc;
573 
574 	if (spdk_json_decode_object(params, rpc_construct_vhost_nvme_ctrlr,
575 				    SPDK_COUNTOF(rpc_construct_vhost_nvme_ctrlr),
576 				    &req)) {
577 		rc = -EINVAL;
578 		goto invalid;
579 	}
580 
581 	rc = spdk_vhost_nvme_dev_construct(req.ctrlr, req.cpumask, req.io_queues);
582 	if (rc < 0) {
583 		goto invalid;
584 	}
585 
586 	free_rpc_vhost_nvme_ctrlr(&req);
587 
588 	w = spdk_jsonrpc_begin_result(request);
589 	if (w == NULL) {
590 		return;
591 	}
592 
593 	spdk_json_write_bool(w, true);
594 	spdk_jsonrpc_end_result(request, w);
595 	return;
596 
597 invalid:
598 	free_rpc_vhost_nvme_ctrlr(&req);
599 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
600 					 spdk_strerror(-rc));
601 
602 }
603 SPDK_RPC_REGISTER("construct_vhost_nvme_controller", spdk_rpc_construct_vhost_nvme_controller,
604 		  SPDK_RPC_RUNTIME)
605 
606 struct rpc_add_vhost_nvme_ctrlr_ns {
607 	char *ctrlr;
608 	char *bdev_name;
609 };
610 
611 static void
612 free_rpc_add_vhost_nvme_ctrlr_ns(struct rpc_add_vhost_nvme_ctrlr_ns *req)
613 {
614 	free(req->ctrlr);
615 	free(req->bdev_name);
616 }
617 
618 static const struct spdk_json_object_decoder rpc_vhost_nvme_add_ns[] = {
619 	{"ctrlr", offsetof(struct rpc_add_vhost_nvme_ctrlr_ns, ctrlr), spdk_json_decode_string },
620 	{"bdev_name", offsetof(struct rpc_add_vhost_nvme_ctrlr_ns, bdev_name), spdk_json_decode_string },
621 };
622 
623 static void
624 spdk_rpc_add_vhost_nvme_ns(struct spdk_jsonrpc_request *request,
625 			   const struct spdk_json_val *params)
626 {
627 	struct rpc_add_vhost_nvme_ctrlr_ns req = {0};
628 	struct spdk_json_write_ctx *w;
629 	struct spdk_vhost_dev *vdev;
630 	int rc;
631 
632 	if (spdk_json_decode_object(params, rpc_vhost_nvme_add_ns,
633 				    SPDK_COUNTOF(rpc_vhost_nvme_add_ns),
634 				    &req)) {
635 		SPDK_DEBUGLOG(SPDK_LOG_VHOST_RPC, "spdk_json_decode_object failed\n");
636 		rc = -EINVAL;
637 		goto invalid;
638 	}
639 
640 	spdk_vhost_lock();
641 	vdev = spdk_vhost_dev_find(req.ctrlr);
642 	if (vdev == NULL) {
643 		spdk_vhost_unlock();
644 		rc = -ENODEV;
645 		goto invalid;
646 	}
647 
648 	rc = spdk_vhost_nvme_dev_add_ns(vdev, req.bdev_name);
649 	spdk_vhost_unlock();
650 	if (rc < 0) {
651 		goto invalid;
652 	}
653 	free_rpc_add_vhost_nvme_ctrlr_ns(&req);
654 
655 	w = spdk_jsonrpc_begin_result(request);
656 	if (w == NULL) {
657 		return;
658 	}
659 
660 	spdk_json_write_bool(w, true);
661 	spdk_jsonrpc_end_result(request, w);
662 	return;
663 
664 invalid:
665 	free_rpc_add_vhost_nvme_ctrlr_ns(&req);
666 	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
667 					 spdk_strerror(-rc));
668 }
669 SPDK_RPC_REGISTER("add_vhost_nvme_ns", spdk_rpc_add_vhost_nvme_ns, SPDK_RPC_RUNTIME)
670 
671 
672 SPDK_LOG_REGISTER_COMPONENT("vhost_rpc", SPDK_LOG_VHOST_RPC)
673