1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (C) 2017 Intel Corporation.
3 * All rights reserved.
4 */
5
6 #include "spdk/stdinc.h"
7
8 #include "spdk/string.h"
9 #include "spdk/rpc.h"
10 #include "spdk/util.h"
11 #include "spdk/log.h"
12 #include "spdk/thread.h"
13
14 #include "bdev_virtio.h"
15
16 #define SPDK_VIRTIO_USER_DEFAULT_VQ_COUNT 1
17 #define SPDK_VIRTIO_USER_DEFAULT_QUEUE_SIZE 512
18
19 struct rpc_bdev_virtio_blk_hotplug {
20 bool enabled;
21 uint64_t period_us;
22 };
23
24 static const struct spdk_json_object_decoder rpc_bdev_virtio_blk_hotplug_decoders[] = {
25 {"enable", offsetof(struct rpc_bdev_virtio_blk_hotplug, enabled), spdk_json_decode_bool, false},
26 {"period_us", offsetof(struct rpc_bdev_virtio_blk_hotplug, period_us), spdk_json_decode_uint64, true},
27 };
28
29 static void
rpc_bdev_virtio_blk_set_hotplug(struct spdk_jsonrpc_request * request,const struct spdk_json_val * params)30 rpc_bdev_virtio_blk_set_hotplug(struct spdk_jsonrpc_request *request,
31 const struct spdk_json_val *params)
32 {
33 struct rpc_bdev_virtio_blk_hotplug req = {false, 0};
34 int rc;
35
36 if (spdk_json_decode_object(params, rpc_bdev_virtio_blk_hotplug_decoders,
37 SPDK_COUNTOF(rpc_bdev_virtio_blk_hotplug_decoders), &req)) {
38 SPDK_ERRLOG("spdk_json_decode_object failed\n");
39 rc = -EINVAL;
40 goto invalid;
41 }
42
43 rc = bdev_virtio_pci_blk_set_hotplug(req.enabled, req.period_us);
44 if (rc) {
45 goto invalid;
46 }
47
48 spdk_jsonrpc_send_bool_response(request, true);
49 return;
50 invalid:
51 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, spdk_strerror(-rc));
52 }
53 SPDK_RPC_REGISTER("bdev_virtio_blk_set_hotplug", rpc_bdev_virtio_blk_set_hotplug, SPDK_RPC_RUNTIME)
54
55 struct rpc_remove_virtio_dev {
56 char *name;
57 };
58
59 static const struct spdk_json_object_decoder rpc_remove_virtio_dev[] = {
60 {"name", offsetof(struct rpc_remove_virtio_dev, name), spdk_json_decode_string },
61 };
62
63 static void
rpc_bdev_virtio_detach_controller_cb(void * ctx,int errnum)64 rpc_bdev_virtio_detach_controller_cb(void *ctx, int errnum)
65 {
66 struct spdk_jsonrpc_request *request = ctx;
67
68 if (errnum != 0) {
69 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
70 spdk_strerror(-errnum));
71 return;
72 }
73
74 spdk_jsonrpc_send_bool_response(request, true);
75 }
76
77 static void
rpc_bdev_virtio_detach_controller(struct spdk_jsonrpc_request * request,const struct spdk_json_val * params)78 rpc_bdev_virtio_detach_controller(struct spdk_jsonrpc_request *request,
79 const struct spdk_json_val *params)
80 {
81 struct rpc_remove_virtio_dev req = {NULL};
82 int rc = 0;
83
84 if (spdk_json_decode_object(params, rpc_remove_virtio_dev,
85 SPDK_COUNTOF(rpc_remove_virtio_dev),
86 &req)) {
87 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
88 "spdk_json_decode_object failed");
89 goto cleanup;
90 }
91
92 rc = bdev_virtio_blk_dev_remove(req.name, rpc_bdev_virtio_detach_controller_cb, request);
93 if (rc == -ENODEV) {
94 rc = bdev_virtio_scsi_dev_remove(req.name, rpc_bdev_virtio_detach_controller_cb, request);
95 }
96
97 if (rc != 0) {
98 spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
99 }
100
101 cleanup:
102 free(req.name);
103 }
104 SPDK_RPC_REGISTER("bdev_virtio_detach_controller",
105 rpc_bdev_virtio_detach_controller, SPDK_RPC_RUNTIME)
106
107 static void
rpc_bdev_virtio_scsi_get_devices(struct spdk_jsonrpc_request * request,const struct spdk_json_val * params)108 rpc_bdev_virtio_scsi_get_devices(struct spdk_jsonrpc_request *request,
109 const struct spdk_json_val *params)
110 {
111 struct spdk_json_write_ctx *w;
112
113 if (params != NULL) {
114 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
115 "bdev_virtio_scsi_get_devices requires no parameters");
116 return;
117 }
118
119 w = spdk_jsonrpc_begin_result(request);
120 bdev_virtio_scsi_dev_list(w);
121 spdk_jsonrpc_end_result(request, w);
122 }
123 SPDK_RPC_REGISTER("bdev_virtio_scsi_get_devices",
124 rpc_bdev_virtio_scsi_get_devices, SPDK_RPC_RUNTIME)
125
126 struct rpc_bdev_virtio_attach_controller_ctx {
127 char *name;
128 char *trtype;
129 char *traddr;
130 char *dev_type;
131 uint32_t vq_count;
132 uint32_t vq_size;
133 struct spdk_jsonrpc_request *request;
134 };
135
136 static const struct spdk_json_object_decoder rpc_bdev_virtio_attach_controller_ctx[] = {
137 {"name", offsetof(struct rpc_bdev_virtio_attach_controller_ctx, name), spdk_json_decode_string },
138 {"trtype", offsetof(struct rpc_bdev_virtio_attach_controller_ctx, trtype), spdk_json_decode_string },
139 {"traddr", offsetof(struct rpc_bdev_virtio_attach_controller_ctx, traddr), spdk_json_decode_string },
140 {"dev_type", offsetof(struct rpc_bdev_virtio_attach_controller_ctx, dev_type), spdk_json_decode_string },
141 {"vq_count", offsetof(struct rpc_bdev_virtio_attach_controller_ctx, vq_count), spdk_json_decode_uint32, true },
142 {"vq_size", offsetof(struct rpc_bdev_virtio_attach_controller_ctx, vq_size), spdk_json_decode_uint32, true },
143 };
144
145 static void
free_rpc_bdev_virtio_attach_controller_ctx(struct rpc_bdev_virtio_attach_controller_ctx * req)146 free_rpc_bdev_virtio_attach_controller_ctx(struct rpc_bdev_virtio_attach_controller_ctx *req)
147 {
148 free(req->name);
149 free(req->trtype);
150 free(req->traddr);
151 free(req->dev_type);
152 free(req);
153 }
154
155 static void
rpc_create_virtio_dev_cb(void * ctx,int result,struct spdk_bdev ** bdevs,size_t cnt)156 rpc_create_virtio_dev_cb(void *ctx, int result, struct spdk_bdev **bdevs, size_t cnt)
157 {
158 struct rpc_bdev_virtio_attach_controller_ctx *req = ctx;
159 struct spdk_json_write_ctx *w;
160 size_t i;
161
162 if (result) {
163 spdk_jsonrpc_send_error_response(req->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
164 spdk_strerror(-result));
165 free_rpc_bdev_virtio_attach_controller_ctx(req);
166 return;
167 }
168
169 w = spdk_jsonrpc_begin_result(req->request);
170 spdk_json_write_array_begin(w);
171
172 for (i = 0; i < cnt; i++) {
173 spdk_json_write_string(w, spdk_bdev_get_name(bdevs[i]));
174 }
175
176 spdk_json_write_array_end(w);
177 spdk_jsonrpc_end_result(req->request, w);
178
179 free_rpc_bdev_virtio_attach_controller_ctx(ctx);
180 }
181
182 static void
rpc_bdev_virtio_attach_controller(struct spdk_jsonrpc_request * request,const struct spdk_json_val * params)183 rpc_bdev_virtio_attach_controller(struct spdk_jsonrpc_request *request,
184 const struct spdk_json_val *params)
185 {
186 struct rpc_bdev_virtio_attach_controller_ctx *req;
187 struct spdk_bdev *bdev = NULL;
188 struct spdk_pci_addr pci_addr;
189 int rc = 0;
190
191 req = calloc(1, sizeof(*req));
192 if (!req) {
193 SPDK_ERRLOG("calloc() failed\n");
194 spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM));
195 return;
196 }
197
198 if (spdk_json_decode_object(params, rpc_bdev_virtio_attach_controller_ctx,
199 SPDK_COUNTOF(rpc_bdev_virtio_attach_controller_ctx),
200 req)) {
201 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
202 "spdk_json_decode_object failed");
203 goto cleanup;
204 }
205
206 if (strcmp(req->trtype, "pci") == 0) {
207 if (req->vq_count != 0 || req->vq_size != 0) {
208 SPDK_ERRLOG("VQ count or size is not allowed for PCI transport type\n");
209 spdk_jsonrpc_send_error_response(request, EINVAL,
210 "vq_count or vq_size is not allowed for PCI transport type.");
211 goto cleanup;
212 }
213
214 if (spdk_pci_addr_parse(&pci_addr, req->traddr) != 0) {
215 SPDK_ERRLOG("Invalid PCI address '%s'\n", req->traddr);
216 spdk_jsonrpc_send_error_response_fmt(request, EINVAL, "Invalid PCI address '%s'", req->traddr);
217 goto cleanup;
218 }
219 } else if (strcmp(req->trtype, "user") == 0) {
220 req->vq_count = req->vq_count == 0 ? SPDK_VIRTIO_USER_DEFAULT_VQ_COUNT : req->vq_count;
221 req->vq_size = req->vq_size == 0 ? SPDK_VIRTIO_USER_DEFAULT_QUEUE_SIZE : req->vq_size;
222 } else if (strcmp(req->trtype, "vfio-user") == 0) {
223 if (req->vq_count != 0 || req->vq_size != 0) {
224 SPDK_ERRLOG("VQ count or size is not allowed for vfio-user transport type\n");
225 spdk_jsonrpc_send_error_response(request, EINVAL,
226 "vq_count or vq_size is not allowed for vfio-user transport type.");
227 goto cleanup;
228 }
229 } else {
230 SPDK_ERRLOG("Invalid trtype '%s'\n", req->trtype);
231 spdk_jsonrpc_send_error_response_fmt(request, EINVAL, "Invalid trtype '%s'", req->trtype);
232 goto cleanup;
233 }
234
235 req->request = request;
236 if (strcmp(req->dev_type, "blk") == 0) {
237 if (strcmp(req->trtype, "pci") == 0) {
238 bdev = bdev_virtio_pci_blk_dev_create(req->name, &pci_addr);
239 } else if (strcmp(req->trtype, "user") == 0) {
240 bdev = bdev_virtio_user_blk_dev_create(req->name, req->traddr, req->vq_count, req->vq_size);
241 } else if (strcmp(req->trtype, "vfio-user") == 0) {
242 bdev = bdev_virtio_vfio_user_blk_dev_create(req->name, req->traddr);
243 }
244
245 /* Virtio blk doesn't use callback so call it manually to send result. */
246 rc = bdev ? 0 : -EINVAL;
247 rpc_create_virtio_dev_cb(req, rc, &bdev, bdev ? 1 : 0);
248 } else if (strcmp(req->dev_type, "scsi") == 0) {
249 if (strcmp(req->trtype, "pci") == 0) {
250 rc = bdev_virtio_pci_scsi_dev_create(req->name, &pci_addr, rpc_create_virtio_dev_cb, req);
251 } else if (strcmp(req->trtype, "user") == 0) {
252 rc = bdev_virtio_user_scsi_dev_create(req->name, req->traddr, req->vq_count, req->vq_size,
253 rpc_create_virtio_dev_cb, req);
254 } else if (strcmp(req->trtype, "vfio-user") == 0) {
255 rc = bdev_vfio_user_scsi_dev_create(req->name, req->traddr, rpc_create_virtio_dev_cb, req);
256 }
257
258 if (rc < 0) {
259 /* In case of error callback is not called so do it manually to send result. */
260 rpc_create_virtio_dev_cb(req, rc, NULL, 0);
261 }
262 } else {
263 SPDK_ERRLOG("Invalid dev_type '%s'\n", req->dev_type);
264 spdk_jsonrpc_send_error_response_fmt(request, EINVAL, "Invalid dev_type '%s'", req->dev_type);
265 goto cleanup;
266 }
267
268 return;
269
270 cleanup:
271 free_rpc_bdev_virtio_attach_controller_ctx(req);
272 }
273 SPDK_RPC_REGISTER("bdev_virtio_attach_controller",
274 rpc_bdev_virtio_attach_controller, SPDK_RPC_RUNTIME);
275