diff --git a/lib/bdev/virtio/bdev_virtio.h b/lib/bdev/virtio/bdev_virtio.h index 6baf7c81a0..31a4f2798b 100644 --- a/lib/bdev/virtio/bdev_virtio.h +++ b/lib/bdev/virtio/bdev_virtio.h @@ -47,6 +47,14 @@ typedef void (*bdev_virtio_create_cb)(void *ctx, int errnum, struct spdk_bdev **bdevs, size_t bdev_cnt); +/** + * Callback for removing virtio devices. + * + * \param ctx opaque context set by the user + * \param errnum error code. 0 on success, negative errno on error. + */ +typedef void (*bdev_virtio_remove_cb)(void *ctx, int errnum); + /** * Connect to a vhost-user Unix domain socket and create a Virtio SCSI device. * If the connection is successful, the device will be automatically scanned. @@ -73,4 +81,19 @@ int bdev_virtio_scsi_dev_create(const char *name, const char *path, unsigned num_queues, unsigned queue_size, bdev_virtio_create_cb cb_fn, void *cb_arg); +/** + * Remove a Virtio device with given name. This will destroy all bdevs exposed + * by this device. + * + * \param name virtio device name + * \param cb_fn function to be called after scanning all targets on the virtio + * device. It's optional, can be NULL. See \c bdev_virtio_create_cb. Possible + * error codes are: + * * ENODEV - couldn't find device with given name + * * EBUSY - device is already being removed + * \param cb_arg argument for the `cb_fn` + */ +void bdev_virtio_scsi_dev_remove(const char *name, + bdev_virtio_remove_cb cb_fn, void *cb_arg); + #endif /* SPDK_BDEV_VIRTIO_H */ diff --git a/lib/bdev/virtio/bdev_virtio_rpc.c b/lib/bdev/virtio/bdev_virtio_rpc.c index d0b9454fc8..67e28a24f7 100644 --- a/lib/bdev/virtio/bdev_virtio_rpc.c +++ b/lib/bdev/virtio/bdev_virtio_rpc.c @@ -135,3 +135,79 @@ invalid: free_rpc_connect_virtio_user_scsi_dev(req); } SPDK_RPC_REGISTER("construct_virtio_user_scsi_bdev", spdk_rpc_create_virtio_user_scsi_bdev); + +struct rpc_remove_virtio_scsi_dev { + char *name; + struct spdk_jsonrpc_request *request; +}; + +static const struct spdk_json_object_decoder rpc_remove_virtio_scsi_dev[] = { + {"name", offsetof(struct rpc_remove_virtio_scsi_dev, name), spdk_json_decode_string }, +}; + +static void +free_rpc_remove_virtio_scsi_dev(struct rpc_remove_virtio_scsi_dev *req) +{ + if (!req) { + return; + } + + free(req->name); + free(req); +} + +static void +spdk_rpc_remove_virtio_scsi_bdev_cb(void *ctx, int errnum) +{ + struct rpc_remove_virtio_scsi_dev *req = ctx; + struct spdk_jsonrpc_request *request = req->request; + struct spdk_json_write_ctx *w; + + free_rpc_remove_virtio_scsi_dev(req); + + if (errnum != 0) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + spdk_strerror(-errnum)); + return; + } + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); +} + +static void +spdk_rpc_remove_virtio_scsi_bdev(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_remove_virtio_scsi_dev *req; + int rc; + + req = calloc(1, sizeof(*req)); + if (!req) { + rc = -ENOMEM; + goto invalid; + } + + if (spdk_json_decode_object(params, rpc_remove_virtio_scsi_dev, + SPDK_COUNTOF(rpc_remove_virtio_scsi_dev), + req)) { + rc = -EINVAL; + goto invalid; + } + + req->request = request; + bdev_virtio_scsi_dev_remove(req->name, spdk_rpc_remove_virtio_scsi_bdev_cb, req); + + return; + +invalid: + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + spdk_strerror(-rc)); + free_rpc_remove_virtio_scsi_dev(req); +} +SPDK_RPC_REGISTER("remove_virtio_scsi_bdev", spdk_rpc_remove_virtio_scsi_bdev); diff --git a/lib/bdev/virtio/bdev_virtio_scsi.c b/lib/bdev/virtio/bdev_virtio_scsi.c index 055d49e9ff..dc349a5536 100644 --- a/lib/bdev/virtio/bdev_virtio_scsi.c +++ b/lib/bdev/virtio/bdev_virtio_scsi.c @@ -89,7 +89,13 @@ struct virtio_scsi_dev { struct virtio_scsi_eventq_io *eventq_ios; /** Device marked for removal. */ - bool removed; + bool removed; + + /** Callback to be called after vdev removal. */ + bdev_virtio_remove_cb remove_cb; + + /** Context for the `remove_cb`. */ + void *remove_ctx; }; struct virtio_scsi_io_ctx { @@ -182,7 +188,8 @@ static bool g_bdev_virtio_finish = false; 1ULL << VIRTIO_SCSI_F_HOTPLUG) static void virtio_scsi_dev_unregister_cb(void *io_device); -static void virtio_scsi_dev_remove(struct virtio_scsi_dev *svdev); +static void virtio_scsi_dev_remove(struct virtio_scsi_dev *svdev, + bdev_virtio_remove_cb cb_fn, void *cb_arg); static int bdev_virtio_scsi_ch_create_cb(void *io_device, void *ctx_buf); static void bdev_virtio_scsi_ch_destroy_cb(void *io_device, void *ctx_buf); static void process_scan_resp(struct virtio_scsi_scan_base *base); @@ -274,6 +281,8 @@ virtio_scsi_dev_init(struct virtio_scsi_dev *svdev, uint16_t max_queues) TAILQ_INIT(&svdev->luns); svdev->scan_ctx = NULL; svdev->removed = false; + svdev->remove_cb = NULL; + svdev->remove_ctx = NULL; spdk_io_device_register(svdev, bdev_virtio_scsi_ch_create_cb, bdev_virtio_scsi_ch_destroy_cb, @@ -1676,7 +1685,7 @@ out: TAILQ_FOREACH_SAFE(vdev, &g_virtio_driver.scsi_devs, tailq, next_vdev) { svdev = virtio_dev_to_scsi(vdev); TAILQ_REMOVE(&g_virtio_driver.scsi_devs, vdev, tailq); - virtio_scsi_dev_remove(svdev); + virtio_scsi_dev_remove(svdev, NULL, NULL); } spdk_bdev_module_init_done(SPDK_GET_BDEV_MODULE(virtio_scsi)); @@ -1690,6 +1699,8 @@ virtio_scsi_dev_unregister_cb(void *io_device) struct virtio_dev *vdev = &svdev->vdev; struct spdk_thread *thread; bool finish_module; + bdev_virtio_remove_cb remove_cb; + void *remove_ctx; thread = virtio_dev_queue_get_thread(vdev, VIRTIO_SCSI_CONTROLQ); if (thread != spdk_get_thread()) { @@ -1707,10 +1718,17 @@ virtio_scsi_dev_unregister_cb(void *io_device) virtio_dev_stop(vdev); virtio_dev_destruct(vdev); + + TAILQ_REMOVE(&g_virtio_driver.scsi_devs, vdev, tailq); + remove_cb = svdev->remove_cb; + remove_ctx = svdev->remove_ctx; spdk_dma_free(svdev->eventq_ios); free(svdev); - TAILQ_REMOVE(&g_virtio_driver.scsi_devs, vdev, tailq); + if (remove_cb) { + remove_cb(remove_ctx, 0); + } + finish_module = TAILQ_EMPTY(&g_virtio_driver.scsi_devs); if (g_bdev_virtio_finish && finish_module) { @@ -1719,16 +1737,21 @@ virtio_scsi_dev_unregister_cb(void *io_device) } static void -virtio_scsi_dev_remove(struct virtio_scsi_dev *svdev) +virtio_scsi_dev_remove(struct virtio_scsi_dev *svdev, + bdev_virtio_remove_cb cb_fn, void *cb_arg) { struct virtio_scsi_disk *disk, *disk_tmp; bool do_remove = true; if (svdev->removed) { - /** device removal in progress */ + if (cb_fn) { + cb_fn(cb_arg, -EBUSY); + } return; } + svdev->remove_cb = cb_fn; + svdev->remove_ctx = cb_arg; svdev->removed = true; if (svdev->scan_ctx) { @@ -1762,7 +1785,7 @@ bdev_virtio_finish(void) /* Defer module finish until all controllers are removed. */ TAILQ_FOREACH_SAFE(vdev, &g_virtio_driver.scsi_devs, tailq, next) { - virtio_scsi_dev_remove(virtio_dev_to_scsi(vdev)); + virtio_scsi_dev_remove(virtio_dev_to_scsi(vdev), NULL, NULL); } } @@ -1780,11 +1803,32 @@ bdev_virtio_scsi_dev_create(const char *base_name, const char *path, unsigned nu rc = virtio_scsi_dev_scan(svdev, cb_fn, cb_arg); if (rc) { - virtio_scsi_dev_remove(svdev); + virtio_scsi_dev_remove(svdev, NULL, NULL); } return rc; } +void +bdev_virtio_scsi_dev_remove(const char *name, bdev_virtio_remove_cb cb_fn, void *cb_arg) +{ + struct virtio_scsi_dev *svdev = NULL; + struct virtio_dev *vdev; + + TAILQ_FOREACH(vdev, &g_virtio_driver.scsi_devs, tailq) { + if (strcmp(vdev->name, name) == 0) { + svdev = virtio_dev_to_scsi(vdev); + break; + } + } + + if (svdev == NULL) { + SPDK_ERRLOG("Cannot find Virtio-SCSI device named '%s'\n", name); + cb_fn(cb_arg, -ENODEV); + return; + } + + virtio_scsi_dev_remove(svdev, cb_fn, cb_arg); +} SPDK_LOG_REGISTER_COMPONENT("virtio", SPDK_LOG_VIRTIO) diff --git a/scripts/rpc.py b/scripts/rpc.py index eda1e5b7ee..507fe9785e 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -836,6 +836,15 @@ p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=i p.add_argument('--vq-size', help='Size of each queue', type=int) p.set_defaults(func=construct_virtio_user_scsi_bdev) +def remove_virtio_scsi_bdev(args): + params = {'name': args.name} + jsonrpc_call('remove_virtio_scsi_bdev', params) + +p = subparsers.add_parser('remove_virtio_scsi_bdev', help="""Remove a Virtio-SCSI device +This will delete all bdevs exposed by this device""") +p.add_argument('name', help='Virtio device name. E.g. VirtioUser0') +p.set_defaults(func=remove_virtio_scsi_bdev) + def get_rpc_methods(args): print_dict(jsonrpc_call('get_rpc_methods'))