diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index b86dbaaf4..f25a97efa 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -1877,13 +1877,25 @@ Example response: ## bdev_nvme_detach_controller {#rpc_bdev_nvme_detach_controller} -Detach NVMe controller and delete any associated bdevs. +Detach NVMe controller and delete any associated bdevs. Optionally, +If all of the transport ID options are specified, only remove that +transport path from the specified controller. If that is the only +available path for the controller, this will also result in the +controller being detached and the associated bdevs being deleted. + +returns true if the controller and bdevs were successfully destroyed +or the address was properly removed, false otherwise. ### Parameters Name | Optional | Type | Description ----------------------- | -------- | ----------- | ----------- name | Required | string | Controller name +trtype | Optional | string | NVMe-oF target trtype: rdma or tcp +traddr | Optional | string | NVMe-oF target address: ip or BDF +adrfam | Optional | string | NVMe-oF target adrfam: ipv4, ipv6, ib, fc, intra_host +trsvcid | Optional | string | NVMe-oF target trsvcid: port number +subnqn | Optional | string | NVMe-oF target subnqn ### Example diff --git a/module/bdev/nvme/bdev_nvme_rpc.c b/module/bdev/nvme/bdev_nvme_rpc.c index 7e59d2d21..4366fb4e4 100644 --- a/module/bdev/nvme/bdev_nvme_rpc.c +++ b/module/bdev/nvme/bdev_nvme_rpc.c @@ -489,16 +489,31 @@ SPDK_RPC_REGISTER_ALIAS_DEPRECATED(bdev_nvme_get_controllers, get_nvme_controlle struct rpc_bdev_nvme_detach_controller { char *name; + char *trtype; + char *adrfam; + char *traddr; + char *trsvcid; + char *subnqn; }; static void free_rpc_bdev_nvme_detach_controller(struct rpc_bdev_nvme_detach_controller *req) { free(req->name); + free(req->trtype); + free(req->adrfam); + free(req->traddr); + free(req->trsvcid); + free(req->subnqn); } static const struct spdk_json_object_decoder rpc_bdev_nvme_detach_controller_decoders[] = { {"name", offsetof(struct rpc_bdev_nvme_detach_controller, name), spdk_json_decode_string}, + {"trtype", offsetof(struct rpc_bdev_nvme_detach_controller, trtype), spdk_json_decode_string, true}, + {"traddr", offsetof(struct rpc_bdev_nvme_detach_controller, traddr), spdk_json_decode_string, true}, + {"adrfam", offsetof(struct rpc_bdev_nvme_detach_controller, adrfam), spdk_json_decode_string, true}, + {"trsvcid", offsetof(struct rpc_bdev_nvme_detach_controller, trsvcid), spdk_json_decode_string, true}, + {"subnqn", offsetof(struct rpc_bdev_nvme_detach_controller, subnqn), spdk_json_decode_string, true}, }; static void @@ -506,8 +521,11 @@ rpc_bdev_nvme_detach_controller(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) { struct rpc_bdev_nvme_detach_controller req = {NULL}; + struct spdk_nvme_transport_id trid = {}; struct spdk_json_write_ctx *w; + size_t len, maxlen; int rc = 0; + bool all_trid_entries, one_trid_entry; if (spdk_json_decode_object(params, rpc_bdev_nvme_detach_controller_decoders, SPDK_COUNTOF(rpc_bdev_nvme_detach_controller_decoders), @@ -517,7 +535,65 @@ rpc_bdev_nvme_detach_controller(struct spdk_jsonrpc_request *request, goto cleanup; } - rc = bdev_nvme_delete(req.name); + all_trid_entries = req.trtype && req.traddr && req.adrfam && req.trsvcid && req.subnqn; + one_trid_entry = req.trtype || req.traddr || req.adrfam || req.trsvcid || req.subnqn; + + if (all_trid_entries ^ one_trid_entry) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "trtype, traddr, adrfam, trsvcid, subnqn must all be provided together or not at all."); + goto cleanup; + } + + if (all_trid_entries) { + /* Parse trtype */ + rc = spdk_nvme_transport_id_parse_trtype(&trid.trtype, req.trtype); + if (rc < 0) { + SPDK_ERRLOG("Failed to parse trtype: %s\n", req.trtype); + spdk_jsonrpc_send_error_response_fmt(request, -EINVAL, "Failed to parse trtype: %s", + req.trtype); + goto cleanup; + } + + /* Parse traddr */ + maxlen = sizeof(trid.traddr); + len = strnlen(req.traddr, maxlen); + if (len == maxlen) { + spdk_jsonrpc_send_error_response_fmt(request, -EINVAL, "traddr too long: %s", + req.traddr); + goto cleanup; + } + memcpy(trid.traddr, req.traddr, len + 1); + + rc = spdk_nvme_transport_id_parse_adrfam(&trid.adrfam, req.adrfam); + if (rc < 0) { + SPDK_ERRLOG("Failed to parse adrfam: %s\n", req.adrfam); + spdk_jsonrpc_send_error_response_fmt(request, -EINVAL, "Failed to parse adrfam: %s", + req.adrfam); + goto cleanup; + } + + maxlen = sizeof(trid.trsvcid); + len = strnlen(req.trsvcid, maxlen); + if (len == maxlen) { + spdk_jsonrpc_send_error_response_fmt(request, -EINVAL, "trsvcid too long: %s", + req.trsvcid); + goto cleanup; + } + memcpy(trid.trsvcid, req.trsvcid, len + 1); + + maxlen = sizeof(trid.subnqn); + len = strnlen(req.subnqn, maxlen); + if (len == maxlen) { + spdk_jsonrpc_send_error_response_fmt(request, -EINVAL, "subnqn too long: %s", + req.subnqn); + goto cleanup; + } + memcpy(trid.subnqn, req.subnqn, len + 1); + rc = bdev_nvme_remove_trid(req.name, &trid); + } else { + rc = bdev_nvme_delete(req.name); + } + if (rc != 0) { spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc)); goto cleanup; diff --git a/scripts/rpc.py b/scripts/rpc.py index 735a43cf3..722b6fb5f 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -484,11 +484,25 @@ if __name__ == "__main__": def bdev_nvme_detach_controller(args): rpc.bdev.bdev_nvme_detach_controller(args.client, - name=args.name) + name=args.name, + trtype=args.trtype, + traddr=args.traddr, + adrfam=args.adrfam, + trsvcid=args.trsvcid, + subnqn=args.subnqn) p = subparsers.add_parser('bdev_nvme_detach_controller', aliases=['delete_nvme_controller'], help='Detach an NVMe controller and delete any associated bdevs') p.add_argument('name', help="Name of the controller") + p.add_argument('-t', '--trtype', + help='NVMe-oF target trtype: e.g., rdma, pcie') + p.add_argument('-a', '--traddr', + help='NVMe-oF target address: e.g., an ip address or BDF') + p.add_argument('-f', '--adrfam', + help='NVMe-oF target adrfam: e.g., ipv4, ipv6, ib, fc, intra_host') + p.add_argument('-s', '--trsvcid', + help='NVMe-oF target trsvcid: e.g., a port number') + p.add_argument('-n', '--subnqn', help='NVMe-oF target subnqn') p.set_defaults(func=bdev_nvme_detach_controller) def bdev_nvme_cuse_register(args): diff --git a/scripts/rpc/bdev.py b/scripts/rpc/bdev.py index 83827f075..aa5dc1c12 100644 --- a/scripts/rpc/bdev.py +++ b/scripts/rpc/bdev.py @@ -492,14 +492,40 @@ def bdev_nvme_attach_controller(client, name, trtype, traddr, adrfam=None, trsvc @deprecated_alias('delete_nvme_controller') -def bdev_nvme_detach_controller(client, name): - """Detach NVMe controller and delete any associated bdevs. +def bdev_nvme_detach_controller(client, name, trtype=None, traddr=None, + adrfam=None, trsvcid=None, subnqn=None): + """Detach NVMe controller and delete any associated bdevs. Optionally, + If all of the transport ID options are specified, only remove that + transport path from the specified controller. If that is the only + available path for the controller, this will also result in the + controller being detached and the associated bdevs being deleted. Args: name: controller name + trtype: transport type ("PCIe", "RDMA") + traddr: transport address (PCI BDF or IP address) + adrfam: address family ("IPv4", "IPv6", "IB", or "FC") + trsvcid: transport service ID (port number for IP-based addresses) + subnqn: subsystem NQN to connect to (optional) """ params = {'name': name} + + if trtype: + params['trtype'] = trtype + + if traddr: + params['traddr'] = traddr + + if adrfam: + params['adrfam'] = adrfam + + if trsvcid: + params['trsvcid'] = trsvcid + + if subnqn: + params['subnqn'] = subnqn + return client.call('bdev_nvme_detach_controller', params)