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 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 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 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 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 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 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 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