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/log.h" 43 44 struct rpc_nbd_start_disk { 45 char *bdev_name; 46 char *nbd_device; 47 /* Used to search one available nbd device */ 48 int nbd_idx; 49 bool nbd_idx_specified; 50 struct spdk_jsonrpc_request *request; 51 }; 52 53 static void 54 free_rpc_nbd_start_disk(struct rpc_nbd_start_disk *req) 55 { 56 free(req->bdev_name); 57 free(req->nbd_device); 58 free(req); 59 } 60 61 static const struct spdk_json_object_decoder rpc_nbd_start_disk_decoders[] = { 62 {"bdev_name", offsetof(struct rpc_nbd_start_disk, bdev_name), spdk_json_decode_string}, 63 {"nbd_device", offsetof(struct rpc_nbd_start_disk, nbd_device), spdk_json_decode_string, true}, 64 }; 65 66 /* Return 0 to indicate the nbd_device might be available, 67 * or non-zero to indicate the nbd_device is invalid or in use. 68 */ 69 static int 70 check_available_nbd_disk(char *nbd_device) 71 { 72 char nbd_block_path[256]; 73 char tail[2]; 74 int rc; 75 unsigned int nbd_idx; 76 struct spdk_nbd_disk *nbd; 77 78 /* nbd device path must be in format of /dev/nbd<num>, with no tail. */ 79 rc = sscanf(nbd_device, "/dev/nbd%u%1s", &nbd_idx, tail); 80 if (rc != 1) { 81 return -errno; 82 } 83 84 /* make sure nbd_device is not registered inside SPDK */ 85 nbd = nbd_disk_find_by_nbd_path(nbd_device); 86 if (nbd) { 87 /* nbd_device is in use */ 88 return -EBUSY; 89 } 90 91 /* A valid pid file in /sys/block indicates the device is in use */ 92 snprintf(nbd_block_path, 256, "/sys/block/nbd%u/pid", nbd_idx); 93 94 rc = open(nbd_block_path, O_RDONLY); 95 if (rc < 0) { 96 if (errno == ENOENT) { 97 /* nbd_device might be available */ 98 return 0; 99 } else { 100 SPDK_ERRLOG("Failed to check PID file %s: %s\n", nbd_block_path, spdk_strerror(errno)); 101 return -errno; 102 } 103 } 104 105 close(rc); 106 107 /* nbd_device is in use */ 108 return -EBUSY; 109 } 110 111 static char * 112 find_available_nbd_disk(int nbd_idx, int *next_nbd_idx) 113 { 114 int i, rc; 115 char nbd_device[20]; 116 117 for (i = nbd_idx; ; i++) { 118 snprintf(nbd_device, 20, "/dev/nbd%d", i); 119 /* Check whether an nbd device exists in order to reach the last one nbd device */ 120 rc = access(nbd_device, F_OK); 121 if (rc != 0) { 122 break; 123 } 124 125 rc = check_available_nbd_disk(nbd_device); 126 if (rc == 0) { 127 if (next_nbd_idx != NULL) { 128 *next_nbd_idx = i + 1; 129 } 130 131 return strdup(nbd_device); 132 } 133 } 134 135 return NULL; 136 } 137 138 static void 139 rpc_start_nbd_done(void *cb_arg, struct spdk_nbd_disk *nbd, int rc) 140 { 141 struct rpc_nbd_start_disk *req = cb_arg; 142 struct spdk_jsonrpc_request *request = req->request; 143 struct spdk_json_write_ctx *w; 144 145 /* Check whether it's automatic nbd-device assignment */ 146 if (rc == -EBUSY && req->nbd_idx_specified == false) { 147 free(req->nbd_device); 148 149 req->nbd_device = find_available_nbd_disk(req->nbd_idx, &req->nbd_idx); 150 if (req->nbd_device != NULL) { 151 spdk_nbd_start(req->bdev_name, req->nbd_device, 152 rpc_start_nbd_done, req); 153 return; 154 } 155 156 SPDK_INFOLOG(nbd, "There is no available nbd device.\n"); 157 } 158 159 if (rc) { 160 spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); 161 free_rpc_nbd_start_disk(req); 162 return; 163 } 164 165 w = spdk_jsonrpc_begin_result(request); 166 spdk_json_write_string(w, spdk_nbd_get_path(nbd)); 167 spdk_jsonrpc_end_result(request, w); 168 169 free_rpc_nbd_start_disk(req); 170 } 171 172 static void 173 rpc_nbd_start_disk(struct spdk_jsonrpc_request *request, 174 const struct spdk_json_val *params) 175 { 176 struct rpc_nbd_start_disk *req; 177 int rc; 178 179 req = calloc(1, sizeof(*req)); 180 if (req == NULL) { 181 SPDK_ERRLOG("could not allocate nbd_start_disk request.\n"); 182 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); 183 return; 184 } 185 186 if (spdk_json_decode_object(params, rpc_nbd_start_disk_decoders, 187 SPDK_COUNTOF(rpc_nbd_start_disk_decoders), 188 req)) { 189 SPDK_ERRLOG("spdk_json_decode_object failed\n"); 190 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 191 "spdk_json_decode_object failed"); 192 goto invalid; 193 } 194 195 if (req->bdev_name == NULL) { 196 goto invalid; 197 } 198 199 if (req->nbd_device != NULL) { 200 req->nbd_idx_specified = true; 201 rc = check_available_nbd_disk(req->nbd_device); 202 if (rc == -EBUSY) { 203 SPDK_DEBUGLOG(nbd, "NBD device %s is in use.\n", req->nbd_device); 204 spdk_jsonrpc_send_error_response(request, -EBUSY, spdk_strerror(-rc)); 205 goto invalid; 206 } 207 208 if (rc != 0) { 209 SPDK_DEBUGLOG(nbd, "Illegal nbd_device %s.\n", req->nbd_device); 210 spdk_jsonrpc_send_error_response_fmt(request, -ENODEV, 211 "illegal nbd device %s", req->nbd_device); 212 goto invalid; 213 } 214 } else { 215 req->nbd_idx = 0; 216 req->nbd_device = find_available_nbd_disk(req->nbd_idx, &req->nbd_idx); 217 if (req->nbd_device == NULL) { 218 SPDK_INFOLOG(nbd, "There is no available nbd device.\n"); 219 spdk_jsonrpc_send_error_response(request, -ENODEV, 220 "nbd device not found"); 221 goto invalid; 222 } 223 } 224 225 req->request = request; 226 spdk_nbd_start(req->bdev_name, req->nbd_device, 227 rpc_start_nbd_done, req); 228 229 return; 230 231 invalid: 232 free_rpc_nbd_start_disk(req); 233 } 234 235 SPDK_RPC_REGISTER("nbd_start_disk", rpc_nbd_start_disk, SPDK_RPC_RUNTIME) 236 SPDK_RPC_REGISTER_ALIAS_DEPRECATED(nbd_start_disk, start_nbd_disk) 237 238 struct rpc_nbd_stop_disk { 239 char *nbd_device; 240 }; 241 242 static void 243 free_rpc_nbd_stop_disk(struct rpc_nbd_stop_disk *req) 244 { 245 free(req->nbd_device); 246 } 247 248 static const struct spdk_json_object_decoder rpc_nbd_stop_disk_decoders[] = { 249 {"nbd_device", offsetof(struct rpc_nbd_stop_disk, nbd_device), spdk_json_decode_string}, 250 }; 251 252 struct nbd_disconnect_arg { 253 struct spdk_jsonrpc_request *request; 254 struct spdk_nbd_disk *nbd; 255 }; 256 257 static void * 258 nbd_disconnect_thread(void *arg) 259 { 260 struct nbd_disconnect_arg *thd_arg = arg; 261 262 spdk_unaffinitize_thread(); 263 264 nbd_disconnect(thd_arg->nbd); 265 266 spdk_jsonrpc_send_bool_response(thd_arg->request, true); 267 268 free(thd_arg); 269 pthread_exit(NULL); 270 } 271 272 static void 273 rpc_nbd_stop_disk(struct spdk_jsonrpc_request *request, 274 const struct spdk_json_val *params) 275 { 276 struct rpc_nbd_stop_disk req = {}; 277 struct spdk_nbd_disk *nbd; 278 pthread_t tid; 279 struct nbd_disconnect_arg *thd_arg = NULL; 280 int rc; 281 282 if (spdk_json_decode_object(params, rpc_nbd_stop_disk_decoders, 283 SPDK_COUNTOF(rpc_nbd_stop_disk_decoders), 284 &req)) { 285 SPDK_ERRLOG("spdk_json_decode_object failed\n"); 286 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 287 "spdk_json_decode_object failed"); 288 goto out; 289 } 290 291 if (req.nbd_device == NULL) { 292 spdk_jsonrpc_send_error_response(request, -ENODEV, "invalid nbd device"); 293 goto out; 294 } 295 296 /* make sure nbd_device is registered */ 297 nbd = nbd_disk_find_by_nbd_path(req.nbd_device); 298 if (!nbd) { 299 spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); 300 goto out; 301 } 302 303 /* 304 * thd_arg should be freed by created thread 305 * if thread is created successfully. 306 */ 307 thd_arg = malloc(sizeof(*thd_arg)); 308 if (!thd_arg) { 309 SPDK_ERRLOG("could not allocate nbd disconnect thread arg\n"); 310 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); 311 goto out; 312 } 313 314 thd_arg->request = request; 315 thd_arg->nbd = nbd; 316 317 /* 318 * NBD ioctl of disconnect will block until data are flushed. 319 * Create separate thread to execute it. 320 */ 321 rc = pthread_create(&tid, NULL, nbd_disconnect_thread, (void *)thd_arg); 322 if (rc != 0) { 323 SPDK_ERRLOG("could not create nbd disconnect thread: %s\n", spdk_strerror(rc)); 324 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, spdk_strerror(rc)); 325 free(thd_arg); 326 goto out; 327 } 328 329 rc = pthread_detach(tid); 330 if (rc != 0) { 331 SPDK_ERRLOG("could not detach nbd disconnect thread: %s\n", spdk_strerror(rc)); 332 goto out; 333 } 334 335 out: 336 free_rpc_nbd_stop_disk(&req); 337 } 338 339 SPDK_RPC_REGISTER("nbd_stop_disk", rpc_nbd_stop_disk, SPDK_RPC_RUNTIME) 340 SPDK_RPC_REGISTER_ALIAS_DEPRECATED(nbd_stop_disk, stop_nbd_disk) 341 342 static void 343 rpc_dump_nbd_info(struct spdk_json_write_ctx *w, 344 struct spdk_nbd_disk *nbd) 345 { 346 spdk_json_write_object_begin(w); 347 348 spdk_json_write_named_string(w, "nbd_device", nbd_disk_get_nbd_path(nbd)); 349 350 spdk_json_write_named_string(w, "bdev_name", nbd_disk_get_bdev_name(nbd)); 351 352 spdk_json_write_object_end(w); 353 } 354 355 struct rpc_nbd_get_disks { 356 char *nbd_device; 357 }; 358 359 static void 360 free_rpc_nbd_get_disks(struct rpc_nbd_get_disks *r) 361 { 362 free(r->nbd_device); 363 } 364 365 static const struct spdk_json_object_decoder rpc_nbd_get_disks_decoders[] = { 366 {"nbd_device", offsetof(struct rpc_nbd_get_disks, nbd_device), spdk_json_decode_string, true}, 367 }; 368 369 static void 370 rpc_nbd_get_disks(struct spdk_jsonrpc_request *request, 371 const struct spdk_json_val *params) 372 { 373 struct rpc_nbd_get_disks req = {}; 374 struct spdk_json_write_ctx *w; 375 struct spdk_nbd_disk *nbd = NULL; 376 377 if (params != NULL) { 378 if (spdk_json_decode_object(params, rpc_nbd_get_disks_decoders, 379 SPDK_COUNTOF(rpc_nbd_get_disks_decoders), 380 &req)) { 381 SPDK_ERRLOG("spdk_json_decode_object failed\n"); 382 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 383 "spdk_json_decode_object failed"); 384 goto invalid; 385 } 386 387 if (req.nbd_device) { 388 nbd = nbd_disk_find_by_nbd_path(req.nbd_device); 389 if (nbd == NULL) { 390 SPDK_ERRLOG("nbd device '%s' does not exist\n", req.nbd_device); 391 spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); 392 goto invalid; 393 } 394 395 free_rpc_nbd_get_disks(&req); 396 } 397 } 398 399 w = spdk_jsonrpc_begin_result(request); 400 spdk_json_write_array_begin(w); 401 402 if (nbd != NULL) { 403 rpc_dump_nbd_info(w, nbd); 404 } else { 405 for (nbd = nbd_disk_first(); nbd != NULL; nbd = nbd_disk_next(nbd)) { 406 rpc_dump_nbd_info(w, nbd); 407 } 408 } 409 410 spdk_json_write_array_end(w); 411 412 spdk_jsonrpc_end_result(request, w); 413 414 return; 415 416 invalid: 417 free_rpc_nbd_get_disks(&req); 418 } 419 SPDK_RPC_REGISTER("nbd_get_disks", rpc_nbd_get_disks, SPDK_RPC_RUNTIME) 420 SPDK_RPC_REGISTER_ALIAS_DEPRECATED(nbd_get_disks, get_nbd_disks) 421