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