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_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 using. 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 = spdk_nbd_disk_find_by_nbd_path(nbd_device); 86 if (nbd) { 87 /* nbd_device is in using */ 88 return -EBUSY; 89 } 90 91 /* A valid pid file in /sys/block indicates the device is in using */ 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 using */ 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 spdk_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 spdk_rpc_start_nbd_done, req); 153 return; 154 } 155 156 SPDK_INFOLOG(SPDK_LOG_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 return; 162 } 163 164 w = spdk_jsonrpc_begin_result(request); 165 spdk_json_write_string(w, spdk_nbd_get_path(nbd)); 166 spdk_jsonrpc_end_result(request, w); 167 168 free_rpc_nbd_start_disk(req); 169 } 170 171 static void 172 spdk_rpc_nbd_start_disk(struct spdk_jsonrpc_request *request, 173 const struct spdk_json_val *params) 174 { 175 struct rpc_nbd_start_disk *req; 176 int rc; 177 178 req = calloc(1, sizeof(*req)); 179 if (req == NULL) { 180 SPDK_ERRLOG("could not allocate nbd_start_disk request.\n"); 181 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); 182 return; 183 } 184 185 if (spdk_json_decode_object(params, rpc_nbd_start_disk_decoders, 186 SPDK_COUNTOF(rpc_nbd_start_disk_decoders), 187 req)) { 188 SPDK_ERRLOG("spdk_json_decode_object failed\n"); 189 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 190 "spdk_json_decode_object failed"); 191 goto invalid; 192 } 193 194 if (req->bdev_name == NULL) { 195 goto invalid; 196 } 197 198 if (req->nbd_device != NULL) { 199 req->nbd_idx_specified = true; 200 rc = check_available_nbd_disk(req->nbd_device); 201 if (rc == -EBUSY) { 202 SPDK_DEBUGLOG(SPDK_LOG_NBD, "NBD device %s is in using.\n", req->nbd_device); 203 spdk_jsonrpc_send_error_response(request, -EBUSY, spdk_strerror(-rc)); 204 goto invalid; 205 } 206 207 if (rc != 0) { 208 SPDK_DEBUGLOG(SPDK_LOG_NBD, "Illegal nbd_device %s.\n", req->nbd_device); 209 spdk_jsonrpc_send_error_response_fmt(request, -ENODEV, 210 "illegal nbd device %s", req->nbd_device); 211 goto invalid; 212 } 213 } else { 214 req->nbd_idx = 0; 215 req->nbd_device = find_available_nbd_disk(req->nbd_idx, &req->nbd_idx); 216 if (req->nbd_device == NULL) { 217 SPDK_INFOLOG(SPDK_LOG_NBD, "There is no available nbd device.\n"); 218 spdk_jsonrpc_send_error_response(request, -ENODEV, 219 "nbd device not found"); 220 goto invalid; 221 } 222 } 223 224 req->request = request; 225 spdk_nbd_start(req->bdev_name, req->nbd_device, 226 spdk_rpc_start_nbd_done, req); 227 228 return; 229 230 invalid: 231 free_rpc_nbd_start_disk(req); 232 } 233 234 SPDK_RPC_REGISTER("nbd_start_disk", spdk_rpc_nbd_start_disk, SPDK_RPC_RUNTIME) 235 SPDK_RPC_REGISTER_ALIAS_DEPRECATED(nbd_start_disk, start_nbd_disk) 236 237 struct rpc_nbd_stop_disk { 238 char *nbd_device; 239 }; 240 241 static void 242 free_rpc_nbd_stop_disk(struct rpc_nbd_stop_disk *req) 243 { 244 free(req->nbd_device); 245 } 246 247 static const struct spdk_json_object_decoder rpc_nbd_stop_disk_decoders[] = { 248 {"nbd_device", offsetof(struct rpc_nbd_stop_disk, nbd_device), spdk_json_decode_string}, 249 }; 250 251 struct nbd_disconnect_arg { 252 struct spdk_jsonrpc_request *request; 253 struct spdk_nbd_disk *nbd; 254 }; 255 256 static void * 257 nbd_disconnect_thread(void *arg) 258 { 259 struct nbd_disconnect_arg *thd_arg = arg; 260 struct spdk_json_write_ctx *w; 261 262 spdk_unaffinitize_thread(); 263 264 nbd_disconnect(thd_arg->nbd); 265 266 w = spdk_jsonrpc_begin_result(thd_arg->request); 267 spdk_json_write_bool(w, true); 268 spdk_jsonrpc_end_result(thd_arg->request, w); 269 270 free(thd_arg); 271 pthread_exit(NULL); 272 } 273 274 static void 275 spdk_rpc_nbd_stop_disk(struct spdk_jsonrpc_request *request, 276 const struct spdk_json_val *params) 277 { 278 struct rpc_nbd_stop_disk req = {}; 279 struct spdk_nbd_disk *nbd; 280 pthread_t tid; 281 struct nbd_disconnect_arg *thd_arg = NULL; 282 int rc; 283 284 if (spdk_json_decode_object(params, rpc_nbd_stop_disk_decoders, 285 SPDK_COUNTOF(rpc_nbd_stop_disk_decoders), 286 &req)) { 287 SPDK_ERRLOG("spdk_json_decode_object failed\n"); 288 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 289 "spdk_json_decode_object failed"); 290 goto out; 291 } 292 293 if (req.nbd_device == NULL) { 294 spdk_jsonrpc_send_error_response(request, -ENODEV, "invalid nbd device"); 295 goto out; 296 } 297 298 /* make sure nbd_device is registered */ 299 nbd = spdk_nbd_disk_find_by_nbd_path(req.nbd_device); 300 if (!nbd) { 301 spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); 302 goto out; 303 } 304 305 /* 306 * thd_arg should be freed by created thread 307 * if thread is created successfully. 308 */ 309 thd_arg = malloc(sizeof(*thd_arg)); 310 if (!thd_arg) { 311 SPDK_ERRLOG("could not allocate nbd disconnect thread arg\n"); 312 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); 313 goto out; 314 } 315 316 thd_arg->request = request; 317 thd_arg->nbd = nbd; 318 319 /* 320 * NBD ioctl of disconnect will block until data are flushed. 321 * Create separate thread to execute it. 322 */ 323 rc = pthread_create(&tid, NULL, nbd_disconnect_thread, (void *)thd_arg); 324 if (rc != 0) { 325 SPDK_ERRLOG("could not create nbd disconnect thread: %s\n", spdk_strerror(rc)); 326 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, spdk_strerror(rc)); 327 free(thd_arg); 328 goto out; 329 } 330 331 rc = pthread_detach(tid); 332 if (rc != 0) { 333 SPDK_ERRLOG("could not detach nbd disconnect thread: %s\n", spdk_strerror(rc)); 334 goto out; 335 } 336 337 out: 338 free_rpc_nbd_stop_disk(&req); 339 } 340 341 SPDK_RPC_REGISTER("nbd_stop_disk", spdk_rpc_nbd_stop_disk, SPDK_RPC_RUNTIME) 342 SPDK_RPC_REGISTER_ALIAS_DEPRECATED(nbd_stop_disk, stop_nbd_disk) 343 344 static void 345 spdk_rpc_dump_nbd_info(struct spdk_json_write_ctx *w, 346 struct spdk_nbd_disk *nbd) 347 { 348 spdk_json_write_object_begin(w); 349 350 spdk_json_write_named_string(w, "nbd_device", spdk_nbd_disk_get_nbd_path(nbd)); 351 352 spdk_json_write_named_string(w, "bdev_name", spdk_nbd_disk_get_bdev_name(nbd)); 353 354 spdk_json_write_object_end(w); 355 } 356 357 struct rpc_nbd_get_disks { 358 char *nbd_device; 359 }; 360 361 static void 362 free_rpc_nbd_get_disks(struct rpc_nbd_get_disks *r) 363 { 364 free(r->nbd_device); 365 } 366 367 static const struct spdk_json_object_decoder rpc_nbd_get_disks_decoders[] = { 368 {"nbd_device", offsetof(struct rpc_nbd_get_disks, nbd_device), spdk_json_decode_string, true}, 369 }; 370 371 static void 372 spdk_rpc_nbd_get_disks(struct spdk_jsonrpc_request *request, 373 const struct spdk_json_val *params) 374 { 375 struct rpc_nbd_get_disks req = {}; 376 struct spdk_json_write_ctx *w; 377 struct spdk_nbd_disk *nbd = NULL; 378 379 if (params != NULL) { 380 if (spdk_json_decode_object(params, rpc_nbd_get_disks_decoders, 381 SPDK_COUNTOF(rpc_nbd_get_disks_decoders), 382 &req)) { 383 SPDK_ERRLOG("spdk_json_decode_object failed\n"); 384 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, 385 "spdk_json_decode_object failed"); 386 goto invalid; 387 } 388 389 if (req.nbd_device) { 390 nbd = spdk_nbd_disk_find_by_nbd_path(req.nbd_device); 391 if (nbd == NULL) { 392 SPDK_ERRLOG("nbd device '%s' does not exist\n", req.nbd_device); 393 spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); 394 goto invalid; 395 } 396 397 free_rpc_nbd_get_disks(&req); 398 } 399 } 400 401 w = spdk_jsonrpc_begin_result(request); 402 spdk_json_write_array_begin(w); 403 404 if (nbd != NULL) { 405 spdk_rpc_dump_nbd_info(w, nbd); 406 } else { 407 for (nbd = spdk_nbd_disk_first(); nbd != NULL; nbd = spdk_nbd_disk_next(nbd)) { 408 spdk_rpc_dump_nbd_info(w, nbd); 409 } 410 } 411 412 spdk_json_write_array_end(w); 413 414 spdk_jsonrpc_end_result(request, w); 415 416 return; 417 418 invalid: 419 free_rpc_nbd_get_disks(&req); 420 } 421 SPDK_RPC_REGISTER("nbd_get_disks", spdk_rpc_nbd_get_disks, SPDK_RPC_RUNTIME) 422 SPDK_RPC_REGISTER_ALIAS_DEPRECATED(nbd_get_disks, get_nbd_disks) 423