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/string.h" 35 #include "spdk/env.h" 36 #include "spdk/rpc.h" 37 #include "spdk/util.h" 38 39 #include <linux/nbd.h> 40 41 #include "nbd_internal.h" 42 #include "spdk_internal/log.h" 43 44 struct rpc_start_nbd_disk { 45 char *bdev_name; 46 char *nbd_device; 47 }; 48 49 static void 50 free_rpc_start_nbd_disk(struct rpc_start_nbd_disk *req) 51 { 52 free(req->bdev_name); 53 free(req->nbd_device); 54 } 55 56 static const struct spdk_json_object_decoder rpc_start_nbd_disk_decoders[] = { 57 {"bdev_name", offsetof(struct rpc_start_nbd_disk, bdev_name), spdk_json_decode_string}, 58 {"nbd_device", offsetof(struct rpc_start_nbd_disk, nbd_device), spdk_json_decode_string}, 59 }; 60 61 static void 62 spdk_rpc_start_nbd_done(void *cb_arg, struct spdk_nbd_disk *nbd, int rc) 63 { 64 struct spdk_jsonrpc_request *request = cb_arg; 65 struct spdk_json_write_ctx *w; 66 67 if (rc) { 68 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); 69 return; 70 } 71 72 w = spdk_jsonrpc_begin_result(request); 73 if (w == NULL) { 74 return; 75 } 76 77 spdk_json_write_string(w, spdk_nbd_get_path(nbd)); 78 spdk_jsonrpc_end_result(request, w); 79 } 80 81 static void 82 spdk_rpc_start_nbd_disk(struct spdk_jsonrpc_request *request, 83 const struct spdk_json_val *params) 84 { 85 struct rpc_start_nbd_disk req = {}; 86 struct spdk_nbd_disk *nbd; 87 88 if (spdk_json_decode_object(params, rpc_start_nbd_disk_decoders, 89 SPDK_COUNTOF(rpc_start_nbd_disk_decoders), 90 &req)) { 91 SPDK_ERRLOG("spdk_json_decode_object failed\n"); 92 goto invalid; 93 } 94 95 if (req.nbd_device == NULL || req.bdev_name == NULL) { 96 goto invalid; 97 } 98 99 /* make sure nbd_device is not registered */ 100 nbd = spdk_nbd_disk_find_by_nbd_path(req.nbd_device); 101 if (nbd) { 102 goto invalid; 103 } 104 105 spdk_nbd_start(req.bdev_name, req.nbd_device, 106 spdk_rpc_start_nbd_done, request); 107 108 free_rpc_start_nbd_disk(&req); 109 return; 110 111 invalid: 112 free_rpc_start_nbd_disk(&req); 113 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); 114 } 115 116 SPDK_RPC_REGISTER("start_nbd_disk", spdk_rpc_start_nbd_disk, SPDK_RPC_RUNTIME) 117 118 struct rpc_stop_nbd_disk { 119 char *nbd_device; 120 }; 121 122 static void 123 free_rpc_stop_nbd_disk(struct rpc_stop_nbd_disk *req) 124 { 125 free(req->nbd_device); 126 } 127 128 static const struct spdk_json_object_decoder rpc_stop_nbd_disk_decoders[] = { 129 {"nbd_device", offsetof(struct rpc_stop_nbd_disk, nbd_device), spdk_json_decode_string}, 130 }; 131 132 struct nbd_disconnect_arg { 133 struct spdk_jsonrpc_request *request; 134 struct spdk_nbd_disk *nbd; 135 }; 136 137 static void * 138 nbd_disconnect_thread(void *arg) 139 { 140 struct nbd_disconnect_arg *thd_arg = arg; 141 struct spdk_json_write_ctx *w; 142 143 spdk_unaffinitize_thread(); 144 145 nbd_disconnect(thd_arg->nbd); 146 147 w = spdk_jsonrpc_begin_result(thd_arg->request); 148 if (w == NULL) { 149 goto out; 150 } 151 152 spdk_json_write_bool(w, true); 153 spdk_jsonrpc_end_result(thd_arg->request, w); 154 155 out: 156 free(thd_arg); 157 pthread_exit(NULL); 158 } 159 160 static void 161 spdk_rpc_stop_nbd_disk(struct spdk_jsonrpc_request *request, 162 const struct spdk_json_val *params) 163 { 164 struct rpc_stop_nbd_disk req = {}; 165 struct spdk_nbd_disk *nbd; 166 pthread_t tid; 167 struct nbd_disconnect_arg *thd_arg = NULL; 168 int rc; 169 170 if (spdk_json_decode_object(params, rpc_stop_nbd_disk_decoders, 171 SPDK_COUNTOF(rpc_stop_nbd_disk_decoders), 172 &req)) { 173 SPDK_ERRLOG("spdk_json_decode_object failed\n"); 174 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, 175 "Invalid parameters"); 176 goto out; 177 } 178 179 if (req.nbd_device == NULL) { 180 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, 181 "Invalid parameters"); 182 goto out; 183 } 184 185 /* make sure nbd_device is registered */ 186 nbd = spdk_nbd_disk_find_by_nbd_path(req.nbd_device); 187 if (!nbd) { 188 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, 189 "Invalid parameters"); 190 goto out; 191 } 192 193 /* 194 * thd_arg should be freed by created thread 195 * if thread is created successfully. 196 */ 197 thd_arg = malloc(sizeof(*thd_arg)); 198 if (!thd_arg) { 199 SPDK_ERRLOG("could not allocate nbd disconnect thread arg\n"); 200 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); 201 goto out; 202 } 203 204 thd_arg->request = request; 205 thd_arg->nbd = nbd; 206 207 /* 208 * NBD ioctl of disconnect will block until data are flushed. 209 * Create separate thread to execute it. 210 */ 211 rc = pthread_create(&tid, NULL, nbd_disconnect_thread, (void *)thd_arg); 212 if (rc != 0) { 213 SPDK_ERRLOG("could not create nbd disconnect thread: %s\n", spdk_strerror(rc)); 214 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, spdk_strerror(rc)); 215 free(thd_arg); 216 goto out; 217 } 218 219 rc = pthread_detach(tid); 220 if (rc != 0) { 221 SPDK_ERRLOG("could not detach nbd disconnect thread: %s\n", spdk_strerror(rc)); 222 goto out; 223 } 224 225 out: 226 free_rpc_stop_nbd_disk(&req); 227 } 228 229 SPDK_RPC_REGISTER("stop_nbd_disk", spdk_rpc_stop_nbd_disk, SPDK_RPC_RUNTIME) 230 231 static void 232 spdk_rpc_dump_nbd_info(struct spdk_json_write_ctx *w, 233 struct spdk_nbd_disk *nbd) 234 { 235 spdk_json_write_object_begin(w); 236 237 spdk_json_write_name(w, "nbd_device"); 238 spdk_json_write_string(w, spdk_nbd_disk_get_nbd_path(nbd)); 239 240 spdk_json_write_name(w, "bdev_name"); 241 spdk_json_write_string(w, spdk_nbd_disk_get_bdev_name(nbd)); 242 243 spdk_json_write_object_end(w); 244 } 245 246 struct rpc_get_nbd_disks { 247 char *nbd_device; 248 }; 249 250 static void 251 free_rpc_get_nbd_disks(struct rpc_get_nbd_disks *r) 252 { 253 free(r->nbd_device); 254 } 255 256 static const struct spdk_json_object_decoder rpc_get_nbd_disks_decoders[] = { 257 {"nbd_device", offsetof(struct rpc_get_nbd_disks, nbd_device), spdk_json_decode_string, true}, 258 }; 259 260 static void 261 spdk_rpc_get_nbd_disks(struct spdk_jsonrpc_request *request, 262 const struct spdk_json_val *params) 263 { 264 struct rpc_get_nbd_disks req = {}; 265 struct spdk_json_write_ctx *w; 266 struct spdk_nbd_disk *nbd = NULL; 267 268 if (params != NULL) { 269 if (spdk_json_decode_object(params, rpc_get_nbd_disks_decoders, 270 SPDK_COUNTOF(rpc_get_nbd_disks_decoders), 271 &req)) { 272 SPDK_ERRLOG("spdk_json_decode_object failed\n"); 273 goto invalid; 274 } 275 276 if (req.nbd_device) { 277 nbd = spdk_nbd_disk_find_by_nbd_path(req.nbd_device); 278 if (nbd == NULL) { 279 SPDK_ERRLOG("nbd device '%s' does not exist\n", req.nbd_device); 280 goto invalid; 281 } 282 283 free_rpc_get_nbd_disks(&req); 284 } 285 } 286 287 w = spdk_jsonrpc_begin_result(request); 288 if (w == NULL) { 289 return; 290 } 291 292 spdk_json_write_array_begin(w); 293 294 if (nbd != NULL) { 295 spdk_rpc_dump_nbd_info(w, nbd); 296 } else { 297 for (nbd = spdk_nbd_disk_first(); nbd != NULL; nbd = spdk_nbd_disk_next(nbd)) { 298 spdk_rpc_dump_nbd_info(w, nbd); 299 } 300 } 301 302 spdk_json_write_array_end(w); 303 304 spdk_jsonrpc_end_result(request, w); 305 306 return; 307 308 invalid: 309 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); 310 311 free_rpc_get_nbd_disks(&req); 312 } 313 SPDK_RPC_REGISTER("get_nbd_disks", spdk_rpc_get_nbd_disks, SPDK_RPC_RUNTIME) 314