1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) 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; 188 struct spdk_pci_addr pci_addr; 189 bool pci; 190 int rc; 191 192 req = calloc(1, sizeof(*req)); 193 if (!req) { 194 SPDK_ERRLOG("calloc() failed\n"); 195 spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM)); 196 return; 197 } 198 199 if (spdk_json_decode_object(params, rpc_bdev_virtio_attach_controller_ctx, 200 SPDK_COUNTOF(rpc_bdev_virtio_attach_controller_ctx), 201 req)) { 202 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 203 "spdk_json_decode_object failed"); 204 goto cleanup; 205 } 206 207 if (strcmp(req->trtype, "pci") == 0) { 208 if (req->vq_count != 0 || req->vq_size != 0) { 209 SPDK_ERRLOG("VQ count or size is not allowed for PCI transport type\n"); 210 spdk_jsonrpc_send_error_response(request, EINVAL, 211 "vq_count or vq_size is not allowed for PCI transport type."); 212 goto cleanup; 213 } 214 215 if (spdk_pci_addr_parse(&pci_addr, req->traddr) != 0) { 216 SPDK_ERRLOG("Invalid PCI address '%s'\n", req->traddr); 217 spdk_jsonrpc_send_error_response_fmt(request, EINVAL, "Invalid PCI address '%s'", req->traddr); 218 goto cleanup; 219 } 220 221 pci = true; 222 } else if (strcmp(req->trtype, "user") == 0) { 223 req->vq_count = req->vq_count == 0 ? SPDK_VIRTIO_USER_DEFAULT_VQ_COUNT : req->vq_count; 224 req->vq_size = req->vq_size == 0 ? SPDK_VIRTIO_USER_DEFAULT_QUEUE_SIZE : req->vq_size; 225 pci = false; 226 } else { 227 SPDK_ERRLOG("Invalid trtype '%s'\n", req->trtype); 228 spdk_jsonrpc_send_error_response_fmt(request, EINVAL, "Invalid trtype '%s'", req->trtype); 229 goto cleanup; 230 } 231 232 req->request = request; 233 if (strcmp(req->dev_type, "blk") == 0) { 234 if (pci) { 235 bdev = bdev_virtio_pci_blk_dev_create(req->name, &pci_addr); 236 } else { 237 bdev = bdev_virtio_user_blk_dev_create(req->name, req->traddr, req->vq_count, req->vq_size); 238 } 239 240 /* Virtio blk doesn't use callback so call it manually to send result. */ 241 rc = bdev ? 0 : -EINVAL; 242 rpc_create_virtio_dev_cb(req, rc, &bdev, bdev ? 1 : 0); 243 } else if (strcmp(req->dev_type, "scsi") == 0) { 244 if (pci) { 245 rc = bdev_virtio_pci_scsi_dev_create(req->name, &pci_addr, rpc_create_virtio_dev_cb, req); 246 } else { 247 rc = bdev_virtio_user_scsi_dev_create(req->name, req->traddr, req->vq_count, req->vq_size, 248 rpc_create_virtio_dev_cb, req); 249 } 250 251 if (rc < 0) { 252 /* In case of error callback is not called so do it manually to send result. */ 253 rpc_create_virtio_dev_cb(req, rc, NULL, 0); 254 } 255 } else { 256 SPDK_ERRLOG("Invalid dev_type '%s'\n", req->dev_type); 257 spdk_jsonrpc_send_error_response_fmt(request, EINVAL, "Invalid dev_type '%s'", req->dev_type); 258 goto cleanup; 259 } 260 261 return; 262 263 cleanup: 264 free_rpc_bdev_virtio_attach_controller_ctx(req); 265 } 266 SPDK_RPC_REGISTER("bdev_virtio_attach_controller", 267 rpc_bdev_virtio_attach_controller, SPDK_RPC_RUNTIME); 268