1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Intel Corporation. 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/string.h" 37 #include "spdk/rpc.h" 38 #include "spdk/util.h" 39 #include "spdk_internal/log.h" 40 41 #include "bdev_virtio.h" 42 43 #define SPDK_VIRTIO_USER_DEFAULT_VQ_COUNT 1 44 #define SPDK_VIRTIO_USER_DEFAULT_QUEUE_SIZE 512 45 46 struct rpc_remove_virtio_dev { 47 char *name; 48 }; 49 50 static const struct spdk_json_object_decoder rpc_remove_virtio_dev[] = { 51 {"name", offsetof(struct rpc_remove_virtio_dev, name), spdk_json_decode_string }, 52 }; 53 54 static void 55 spdk_rpc_remove_virtio_bdev_cb(void *ctx, int errnum) 56 { 57 struct spdk_jsonrpc_request *request = ctx; 58 struct spdk_json_write_ctx *w; 59 60 if (errnum != 0) { 61 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, 62 spdk_strerror(-errnum)); 63 return; 64 } 65 66 w = spdk_jsonrpc_begin_result(request); 67 spdk_json_write_bool(w, true); 68 spdk_jsonrpc_end_result(request, w); 69 } 70 71 static void 72 spdk_rpc_remove_virtio_bdev(struct spdk_jsonrpc_request *request, 73 const struct spdk_json_val *params) 74 { 75 struct rpc_remove_virtio_dev req = {NULL}; 76 int rc = 0; 77 78 if (spdk_json_decode_object(params, rpc_remove_virtio_dev, 79 SPDK_COUNTOF(rpc_remove_virtio_dev), 80 &req)) { 81 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 82 "spdk_json_decode_object failed"); 83 goto cleanup; 84 } 85 86 rc = bdev_virtio_blk_dev_remove(req.name, spdk_rpc_remove_virtio_bdev_cb, request); 87 if (rc == -ENODEV) { 88 rc = bdev_virtio_scsi_dev_remove(req.name, spdk_rpc_remove_virtio_bdev_cb, request); 89 } 90 91 if (rc != 0) { 92 spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); 93 } 94 95 cleanup: 96 free(req.name); 97 } 98 SPDK_RPC_REGISTER("remove_virtio_bdev", spdk_rpc_remove_virtio_bdev, SPDK_RPC_RUNTIME); 99 100 static void 101 spdk_rpc_get_virtio_scsi_devs(struct spdk_jsonrpc_request *request, 102 const struct spdk_json_val *params) 103 { 104 struct spdk_json_write_ctx *w; 105 106 if (params != NULL) { 107 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, 108 "get_virtio_scsi_devs requires no parameters"); 109 return; 110 } 111 112 w = spdk_jsonrpc_begin_result(request); 113 bdev_virtio_scsi_dev_list(w); 114 spdk_jsonrpc_end_result(request, w); 115 } 116 SPDK_RPC_REGISTER("get_virtio_scsi_devs", spdk_rpc_get_virtio_scsi_devs, SPDK_RPC_RUNTIME) 117 118 struct rpc_construct_virtio_dev { 119 char *name; 120 char *trtype; 121 char *traddr; 122 char *dev_type; 123 uint32_t vq_count; 124 uint32_t vq_size; 125 struct spdk_jsonrpc_request *request; 126 }; 127 128 static const struct spdk_json_object_decoder rpc_construct_virtio_dev[] = { 129 {"name", offsetof(struct rpc_construct_virtio_dev, name), spdk_json_decode_string }, 130 {"trtype", offsetof(struct rpc_construct_virtio_dev, trtype), spdk_json_decode_string }, 131 {"traddr", offsetof(struct rpc_construct_virtio_dev, traddr), spdk_json_decode_string }, 132 {"dev_type", offsetof(struct rpc_construct_virtio_dev, dev_type), spdk_json_decode_string }, 133 {"vq_count", offsetof(struct rpc_construct_virtio_dev, vq_count), spdk_json_decode_uint32, true }, 134 {"vq_size", offsetof(struct rpc_construct_virtio_dev, vq_size), spdk_json_decode_uint32, true }, 135 }; 136 137 static void 138 free_rpc_construct_virtio_dev(struct rpc_construct_virtio_dev *req) 139 { 140 free(req->name); 141 free(req->trtype); 142 free(req->traddr); 143 free(req->dev_type); 144 free(req); 145 } 146 147 static void 148 spdk_rpc_create_virtio_dev_cb(void *ctx, int result, struct spdk_bdev **bdevs, size_t cnt) 149 { 150 struct rpc_construct_virtio_dev *req = ctx; 151 struct spdk_json_write_ctx *w; 152 size_t i; 153 154 if (result) { 155 spdk_jsonrpc_send_error_response(req->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, 156 spdk_strerror(-result)); 157 free_rpc_construct_virtio_dev(req); 158 return; 159 } 160 161 w = spdk_jsonrpc_begin_result(req->request); 162 spdk_json_write_array_begin(w); 163 164 for (i = 0; i < cnt; i++) { 165 spdk_json_write_string(w, spdk_bdev_get_name(bdevs[i])); 166 } 167 168 spdk_json_write_array_end(w); 169 spdk_jsonrpc_end_result(req->request, w); 170 171 free_rpc_construct_virtio_dev(ctx); 172 } 173 174 static void 175 spdk_rpc_construct_virtio_dev(struct spdk_jsonrpc_request *request, 176 const struct spdk_json_val *params) 177 { 178 struct rpc_construct_virtio_dev *req; 179 struct spdk_bdev *bdev; 180 struct spdk_pci_addr pci_addr; 181 bool pci; 182 int rc; 183 184 req = calloc(1, sizeof(*req)); 185 if (!req) { 186 SPDK_ERRLOG("calloc() failed\n"); 187 spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM)); 188 return; 189 } 190 191 if (spdk_json_decode_object(params, rpc_construct_virtio_dev, 192 SPDK_COUNTOF(rpc_construct_virtio_dev), 193 req)) { 194 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 195 "spdk_json_decode_object failed"); 196 goto cleanup; 197 } 198 199 if (strcmp(req->trtype, "pci") == 0) { 200 if (req->vq_count != 0 || req->vq_size != 0) { 201 SPDK_ERRLOG("VQ count or size is not allowed for PCI transport type\n"); 202 spdk_jsonrpc_send_error_response(request, EINVAL, 203 "vq_count or vq_size is not allowed for PCI transport type."); 204 goto cleanup; 205 } 206 207 if (spdk_pci_addr_parse(&pci_addr, req->traddr) != 0) { 208 SPDK_ERRLOG("Invalid PCI address '%s'\n", req->traddr); 209 spdk_jsonrpc_send_error_response_fmt(request, EINVAL, "Invalid PCI address '%s'", req->traddr); 210 goto cleanup; 211 } 212 213 pci = true; 214 } else if (strcmp(req->trtype, "user") == 0) { 215 req->vq_count = req->vq_count == 0 ? SPDK_VIRTIO_USER_DEFAULT_VQ_COUNT : req->vq_count; 216 req->vq_size = req->vq_size == 0 ? SPDK_VIRTIO_USER_DEFAULT_QUEUE_SIZE : req->vq_size; 217 pci = false; 218 } else { 219 SPDK_ERRLOG("Invalid trtype '%s'\n", req->trtype); 220 spdk_jsonrpc_send_error_response_fmt(request, EINVAL, "Invalid trtype '%s'", req->trtype); 221 goto cleanup; 222 } 223 224 req->request = request; 225 if (strcmp(req->dev_type, "blk") == 0) { 226 if (pci) { 227 bdev = bdev_virtio_pci_blk_dev_create(req->name, &pci_addr); 228 } else { 229 bdev = bdev_virtio_user_blk_dev_create(req->name, req->traddr, req->vq_count, req->vq_size); 230 } 231 232 /* Virtio blk doesn't use callback so call it manually to send result. */ 233 rc = bdev ? 0 : -EINVAL; 234 spdk_rpc_create_virtio_dev_cb(req, rc, &bdev, bdev ? 1 : 0); 235 } else if (strcmp(req->dev_type, "scsi") == 0) { 236 if (pci) { 237 rc = bdev_virtio_pci_scsi_dev_create(req->name, &pci_addr, spdk_rpc_create_virtio_dev_cb, req); 238 } else { 239 rc = bdev_virtio_user_scsi_dev_create(req->name, req->traddr, req->vq_count, req->vq_size, 240 spdk_rpc_create_virtio_dev_cb, req); 241 } 242 243 if (rc < 0) { 244 /* In case of error callback is not called so do it manually to send result. */ 245 spdk_rpc_create_virtio_dev_cb(req, rc, NULL, 0); 246 } 247 } else { 248 SPDK_ERRLOG("Invalid dev_type '%s'\n", req->dev_type); 249 spdk_jsonrpc_send_error_response_fmt(request, EINVAL, "Invalid dev_type '%s'", req->dev_type); 250 goto cleanup; 251 } 252 253 return; 254 255 cleanup: 256 free_rpc_construct_virtio_dev(req); 257 } 258 SPDK_RPC_REGISTER("construct_virtio_dev", spdk_rpc_construct_virtio_dev, SPDK_RPC_RUNTIME); 259