From 36c9ac2dde759c455a6a6b5ad9c9f817d140784d Mon Sep 17 00:00:00 2001 From: Chunyang Hui Date: Wed, 23 Oct 2019 21:29:17 +0800 Subject: [PATCH] bdev/opal: Add rpc for init, revert and get info Add bdev_nvme_opal_init, bdev_nvme_opal_revert, bdev_opal_get_info rpc commands. Change-Id: Ib53492c02a1c18603834640d23f9fb2e7eb08657 Signed-off-by: Chunyang Hui Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/468917 Tested-by: SPDK CI Jenkins Reviewed-by: Shuhei Matsumoto Reviewed-by: Changpeng Liu Reviewed-by: Jim Harris --- doc/jsonrpc.md | 123 +++++++++++++++++ module/bdev/nvme/bdev_nvme.c | 6 + module/bdev/nvme/common.h | 1 + module/bdev/nvme/vbdev_opal.c | 77 +++++++++++ module/bdev/nvme/vbdev_opal.h | 5 + module/bdev/nvme/vbdev_opal_rpc.c | 213 ++++++++++++++++++++++++++++++ scripts/rpc.py | 29 ++++ scripts/rpc/bdev.py | 18 +++ scripts/rpc/nvme.py | 30 +++++ 9 files changed, 502 insertions(+) diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index e5572a44d..5fcc6e8a3 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -5468,6 +5468,80 @@ Example response: # OPAL +## bdev_nvme_opal_init {#rpc_bdev_nvme_opal_init} + +This is used to initialize OPAL of a given NVMe ctrlr, including taking ownership and activating. + +### Parameters + +Name | Optional | Type | Description +----------------------- | -------- | ----------- | ----------- +nvme_ctrlr_name | Required | string | name of nvme ctrlr +password | Required | string | admin password of OPAL + +### Example + +Example request: + +~~~ +{ + "jsonrpc": "2.0", + "method": "bdev_nvme_opal_init", + "id": 1, + "params": { + "nvme_ctrlr_name": "nvme0", + "password": "*****" + } +} +~~~ + +Example response: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "result": true +} +~~~ + +## bdev_nvme_opal_revert {#rpc_bdev_nvme_opal_revert} + +This is used to revert OPAL to its factory settings. Erase all user configuration and data. + +### Parameters + +Name | Optional | Type | Description +----------------------- | -------- | ----------- | ----------- +nvme_ctrlr_name | Required | string | name of nvme ctrlr +password | Required | string | admin password of OPAL + +### Example + +Example request: + +~~~ +{ + "jsonrpc": "2.0", + "method": "bdev_nvme_opal_revert", + "id": 1, + "params": { + "nvme_ctrlr_name": "nvme0", + "password": "*****" + } +} +~~~ + +Example response: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "result": true +} +~~~ + ## bdev_opal_create {#rpc_bdev_opal_create} This is used to create an OPAL virtual bdev. @@ -5517,6 +5591,55 @@ Example response: } ~~~ +## bdev_opal_get_info {#rpc_bdev_opal_get_info} + +This is used to get information of a given OPAL bdev. + +### Parameters + +Name | Optional | Type | Description +----------------------- | -------- | ----------- | ----------- +bdev_name | Required | string | name of OPAL vbdev +password | Required | string | admin password + +### Response + +The response is the locking info of OPAL virtual bdev. + +### Example + +Example request: + +~~~ +{ + "jsonrpc": "2.0", + "method": "bdev_opal_get_info", + "id": 1, + "params": { + "bdev_name": "nvme0n1r1", + "password": "*****" + } +} +~~~ + +Example response: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "name": "nvme0n1r1", + "range_start": 0, + "range_length": 4096, + "read_lock_enabled": true, + "write_lock_enabled": true, + "read_locked": false, + "write_locked": false + } +} +~~~ + ## bdev_opal_delete {#rpc_bdev_opal_delete} This is used to delete OPAL vbdev. diff --git a/module/bdev/nvme/bdev_nvme.c b/module/bdev/nvme/bdev_nvme.c index 135f7b35e..d09753da4 100644 --- a/module/bdev/nvme/bdev_nvme.c +++ b/module/bdev/nvme/bdev_nvme.c @@ -226,7 +226,13 @@ bdev_nvme_ctrlr_destruct(struct nvme_bdev_ctrlr *nvme_bdev_ctrlr) { assert(nvme_bdev_ctrlr->destruct); if (nvme_bdev_ctrlr->opal_dev) { + if (nvme_bdev_ctrlr->opal_poller != NULL) { + spdk_poller_unregister(&nvme_bdev_ctrlr->opal_poller); + /* wait until we get the result */ + while (spdk_opal_revert_poll(nvme_bdev_ctrlr->opal_dev) == -EAGAIN); + } spdk_opal_close(nvme_bdev_ctrlr->opal_dev); + nvme_bdev_ctrlr->opal_dev = NULL; } pthread_mutex_lock(&g_bdev_nvme_mutex); TAILQ_REMOVE(&g_nvme_bdev_ctrlrs, nvme_bdev_ctrlr, tailq); diff --git a/module/bdev/nvme/common.h b/module/bdev/nvme/common.h index 7be0d943e..1062ecf87 100644 --- a/module/bdev/nvme/common.h +++ b/module/bdev/nvme/common.h @@ -66,6 +66,7 @@ struct nvme_bdev_ctrlr { struct nvme_bdev *bdevs; struct spdk_opal_dev *opal_dev; + struct spdk_poller *opal_poller; struct spdk_poller *adminq_timer_poller; diff --git a/module/bdev/nvme/vbdev_opal.c b/module/bdev/nvme/vbdev_opal.c index 2de9dba4f..1b365a7df 100644 --- a/module/bdev/nvme/vbdev_opal.c +++ b/module/bdev/nvme/vbdev_opal.c @@ -228,6 +228,50 @@ vbdev_opal_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_ } } +struct spdk_opal_locking_range_info * +spdk_vbdev_opal_get_info_from_bdev(const char *opal_bdev_name, const char *password) +{ + struct opal_vbdev *vbdev; + struct nvme_bdev_ctrlr *nvme_ctrlr; + int locking_range_id; + int rc; + + TAILQ_FOREACH(vbdev, &g_opal_vbdev, tailq) { + if (strcmp(vbdev->name, opal_bdev_name) == 0) { + break; + } + } + + if (vbdev == NULL) { + SPDK_ERRLOG("%s not found\n", opal_bdev_name); + return NULL; + } + + nvme_ctrlr = vbdev->nvme_ctrlr; + if (nvme_ctrlr == NULL) { + SPDK_ERRLOG("can't find nvme_ctrlr of %s\n", vbdev->name); + return NULL; + } + + if (spdk_opal_get_max_locking_ranges(nvme_ctrlr->opal_dev) == 0) { + rc = spdk_opal_cmd_get_max_ranges(nvme_ctrlr->opal_dev, password); + if (rc) { + SPDK_ERRLOG("Get locking range number failure: %d\n", rc); + return NULL; + } + } + + locking_range_id = vbdev->cfg.locking_range_id; + rc = spdk_opal_cmd_get_locking_range_info(nvme_ctrlr->opal_dev, password, + OPAL_ADMIN1, locking_range_id); + if (rc) { + SPDK_ERRLOG("Get locking range info error: %d\n", rc); + return NULL; + } + + return spdk_opal_get_locking_range_info(nvme_ctrlr->opal_dev, locking_range_id); +} + static int vbdev_opal_dump_info_json(void *ctx, struct spdk_json_write_ctx *w) { @@ -514,6 +558,7 @@ spdk_vbdev_opal_destruct(const char *bdev_name, const char *password) goto err; } + spdk_opal_free_locking_range_info(opal_bdev->opal_dev, locking_range_id); vbdev_opal_destruct_bdev(opal_bdev); return 0; @@ -528,4 +573,36 @@ vbdev_opal_examine(struct spdk_bdev *bdev) spdk_bdev_module_examine_done(&opal_if); } +static int +vbdev_opal_recv_poll(void *arg) +{ + struct nvme_bdev_ctrlr *nvme_ctrlr = arg; + int rc; + + rc = spdk_opal_revert_poll(nvme_ctrlr->opal_dev); + if (rc == -EAGAIN) { + return -1; + } + + /* receive end */ + spdk_poller_unregister(&nvme_ctrlr->opal_poller); + nvme_ctrlr->opal_poller = NULL; + return 1; +} + +int +spdk_vbdev_opal_revert_tper(struct nvme_bdev_ctrlr *nvme_ctrlr, const char *password, + spdk_opal_revert_cb cb_fn, void *cb_ctx) +{ + int rc; + + rc = spdk_opal_cmd_revert_tper_async(nvme_ctrlr->opal_dev, password, cb_fn, cb_ctx); + if (rc) { + SPDK_ERRLOG("%s revert tper failure: %d\n", nvme_ctrlr->name, rc); + return rc; + } + nvme_ctrlr->opal_poller = spdk_poller_register(vbdev_opal_recv_poll, nvme_ctrlr, 50); + return 0; +} + SPDK_LOG_REGISTER_COMPONENT("vbdev_opal", SPDK_LOG_VBDEV_OPAL) diff --git a/module/bdev/nvme/vbdev_opal.h b/module/bdev/nvme/vbdev_opal.h index d220056c5..cdd65ec29 100644 --- a/module/bdev/nvme/vbdev_opal.h +++ b/module/bdev/nvme/vbdev_opal.h @@ -40,6 +40,11 @@ int spdk_vbdev_opal_create(const char *nvme_ctrlr_name, uint32_t nsid, uint8_t locking_range_id, uint64_t range_start, uint64_t range_length, const char *password); +struct spdk_opal_locking_range_info *spdk_vbdev_opal_get_info_from_bdev(const char *opal_bdev_name, + const char *password); + int spdk_vbdev_opal_destruct(const char *bdev_name, const char *password); +int spdk_vbdev_opal_revert_tper(struct nvme_bdev_ctrlr *nvme_ctrlr, const char *password, + spdk_opal_revert_cb cb_fn, void *cb_ctx); #endif diff --git a/module/bdev/nvme/vbdev_opal_rpc.c b/module/bdev/nvme/vbdev_opal_rpc.c index 842773cd7..10d3654ab 100644 --- a/module/bdev/nvme/vbdev_opal_rpc.c +++ b/module/bdev/nvme/vbdev_opal_rpc.c @@ -38,6 +38,160 @@ #include "vbdev_opal.h" +struct rpc_bdev_nvme_opal_init { + char *nvme_ctrlr_name; + char *password; +}; + +static void +free_rpc_bdev_nvme_opal_init(struct rpc_bdev_nvme_opal_init *req) +{ + free(req->nvme_ctrlr_name); + free(req->password); +} + +static const struct spdk_json_object_decoder rpc_bdev_nvme_opal_init_decoders[] = { + {"nvme_ctrlr_name", offsetof(struct rpc_bdev_nvme_opal_init, nvme_ctrlr_name), spdk_json_decode_string}, + {"password", offsetof(struct rpc_bdev_nvme_opal_init, password), spdk_json_decode_string}, +}; + +static void +spdk_rpc_bdev_nvme_opal_init(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_bdev_nvme_opal_init req = {}; + struct spdk_json_write_ctx *w; + struct nvme_bdev_ctrlr *nvme_ctrlr; + int rc; + + if (spdk_json_decode_object(params, rpc_bdev_nvme_opal_init_decoders, + SPDK_COUNTOF(rpc_bdev_nvme_opal_init_decoders), + &req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + goto out; + } + + /* check if opal supported */ + nvme_ctrlr = nvme_bdev_ctrlr_get_by_name(req.nvme_ctrlr_name); + if (nvme_ctrlr == NULL || nvme_ctrlr->opal_dev == NULL || + !spdk_opal_supported(nvme_ctrlr->opal_dev)) { + SPDK_ERRLOG("%s not support opal\n", req.nvme_ctrlr_name); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + goto out; + } + + /* take ownership */ + rc = spdk_opal_cmd_take_ownership(nvme_ctrlr->opal_dev, req.password); + if (rc) { + SPDK_ERRLOG("Take ownership failure: %d\n", rc); + switch (rc) { + case -EBUSY: + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "SP Busy, try again later"); + break; + case -EACCES: + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "This drive is already enabled"); + break; + default: + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + } + goto out; + } + + /* activate locking SP */ + rc = spdk_opal_cmd_activate_locking_sp(nvme_ctrlr->opal_dev, req.password); + if (rc) { + SPDK_ERRLOG("Activate locking SP failure: %d\n", rc); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + goto out; + } + + w = spdk_jsonrpc_begin_result(request); + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); + +out: + free_rpc_bdev_nvme_opal_init(&req); +} +SPDK_RPC_REGISTER("bdev_nvme_opal_init", spdk_rpc_bdev_nvme_opal_init, SPDK_RPC_RUNTIME) + +struct rpc_bdev_nvme_opal_revert { + char *nvme_ctrlr_name; + char *password; +}; + +static void +free_rpc_bdev_nvme_opal_revert(struct rpc_bdev_nvme_opal_revert *req) +{ + free(req->nvme_ctrlr_name); + free(req->password); +} + +static const struct spdk_json_object_decoder rpc_bdev_nvme_opal_revert_decoders[] = { + {"nvme_ctrlr_name", offsetof(struct rpc_bdev_nvme_opal_revert, nvme_ctrlr_name), spdk_json_decode_string}, + {"password", offsetof(struct rpc_bdev_nvme_opal_revert, password), spdk_json_decode_string}, +}; + +static void +revert_tper_done(struct spdk_opal_dev *dev, void *data, int rc) +{ + struct nvme_bdev_ctrlr *ctrlr = data; + + if (rc != 0) { + SPDK_ERRLOG("%s revert TPer failed\n", ctrlr->name); + return; + } + + SPDK_NOTICELOG("%s revert TPer done\n", ctrlr->name); +} + +static void +spdk_rpc_bdev_nvme_opal_revert(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_bdev_nvme_opal_revert req = {}; + struct spdk_json_write_ctx *w; + struct nvme_bdev_ctrlr *nvme_ctrlr; + int rc; + + if (spdk_json_decode_object(params, rpc_bdev_nvme_opal_revert_decoders, + SPDK_COUNTOF(rpc_bdev_nvme_opal_revert_decoders), + &req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + goto out; + } + + /* check if opal supported */ + nvme_ctrlr = nvme_bdev_ctrlr_get_by_name(req.nvme_ctrlr_name); + if (nvme_ctrlr == NULL || nvme_ctrlr->opal_dev == NULL || + !spdk_opal_supported(nvme_ctrlr->opal_dev)) { + SPDK_ERRLOG("%s not support opal\n", req.nvme_ctrlr_name); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + goto out; + } + + /* TODO: delete all opal vbdev before revert TPer */ + + rc = spdk_vbdev_opal_revert_tper(nvme_ctrlr, req.password, revert_tper_done, + nvme_ctrlr); + if (rc) { + SPDK_ERRLOG("Revert TPer failure: %d\n", rc); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + goto out; + } + + w = spdk_jsonrpc_begin_result(request); + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); + +out: + free_rpc_bdev_nvme_opal_revert(&req); +} +SPDK_RPC_REGISTER("bdev_nvme_opal_revert", spdk_rpc_bdev_nvme_opal_revert, SPDK_RPC_RUNTIME) + struct rpc_bdev_opal_create { char *nvme_ctrlr_name; uint32_t nsid; @@ -101,6 +255,65 @@ out: } SPDK_RPC_REGISTER("bdev_opal_create", spdk_rpc_bdev_opal_create, SPDK_RPC_RUNTIME) +struct rpc_bdev_opal_get_info { + char *bdev_name; + char *password; +}; + +static void +free_rpc_bdev_opal_get_info(struct rpc_bdev_opal_get_info *req) +{ + free(req->bdev_name); + free(req->password); +} + +static const struct spdk_json_object_decoder rpc_bdev_opal_get_info_decoders[] = { + {"bdev_name", offsetof(struct rpc_bdev_opal_get_info, bdev_name), spdk_json_decode_string}, + {"password", offsetof(struct rpc_bdev_opal_get_info, password), spdk_json_decode_string}, +}; + +static void +spdk_rpc_bdev_opal_get_info(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_bdev_opal_get_info req = {}; + struct spdk_json_write_ctx *w; + struct spdk_opal_locking_range_info *info; + + if (spdk_json_decode_object(params, rpc_bdev_opal_get_info_decoders, + SPDK_COUNTOF(rpc_bdev_opal_get_info_decoders), + &req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + goto out; + } + + info = spdk_vbdev_opal_get_info_from_bdev(req.bdev_name, req.password); + if (info == NULL) { + SPDK_ERRLOG("Get opal info failure\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + goto out; + } + + w = spdk_jsonrpc_begin_result(request); + spdk_json_write_object_begin(w); + + spdk_json_write_named_string(w, "name", req.bdev_name); + spdk_json_write_named_uint64(w, "range_start", info->range_start); + spdk_json_write_named_uint64(w, "range_length", info->range_length); + spdk_json_write_named_bool(w, "read_lock_enabled", info->read_lock_enabled); + spdk_json_write_named_bool(w, "write_lock_enabled", info->write_lock_enabled); + spdk_json_write_named_bool(w, "read_locked", info->read_locked); + spdk_json_write_named_bool(w, "write_locked", info->write_locked); + + spdk_json_write_object_end(w); + spdk_jsonrpc_end_result(request, w); + +out: + free_rpc_bdev_opal_get_info(&req); +} +SPDK_RPC_REGISTER("bdev_opal_get_info", spdk_rpc_bdev_opal_get_info, SPDK_RPC_RUNTIME) + struct rpc_bdev_opal_delete { char *bdev_name; char *password; diff --git a/scripts/rpc.py b/scripts/rpc.py index 2ea417b6a..7daa50a6d 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -2014,6 +2014,25 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse p.set_defaults(func=ioat_scan_copy_engine) # opal + def bdev_nvme_opal_init(args): + rpc.nvme.bdev_nvme_opal_init(args.client, + nvme_ctrlr_name=args.nvme_ctrlr_name, + password=args.password) + + p = subparsers.add_parser('bdev_nvme_opal_init', help='take ownership and activate') + p.add_argument('-b', '--nvme-ctrlr-name', help='nvme ctrlr name') + p.add_argument('-p', '--password', help='password for admin') + p.set_defaults(func=bdev_nvme_opal_init) + + def bdev_nvme_opal_revert(args): + rpc.nvme.bdev_nvme_opal_revert(args.client, + nvme_ctrlr_name=args.nvme_ctrlr_name, + password=args.password) + p = subparsers.add_parser('bdev_nvme_opal_revert', help='Revert to default factory settings') + p.add_argument('-b', '--nvme-ctrlr-name', help='nvme ctrlr name') + p.add_argument('-p', '--password', help='password') + p.set_defaults(func=bdev_nvme_opal_revert) + def bdev_opal_create(args): print_json(rpc.bdev.bdev_opal_create(args.client, nvme_ctrlr_name=args.nvme_ctrlr_name, @@ -2032,6 +2051,16 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse p.add_argument('-p', '--password', help='admin password', required=True) p.set_defaults(func=bdev_opal_create) + def bdev_opal_get_info(args): + print_dict(rpc.bdev.bdev_opal_get_info(args.client, + bdev_name=args.bdev_name, + password=args.password)) + + p = subparsers.add_parser('bdev_opal_get_info', help='get opal locking range info for this bdev') + p.add_argument('-b', '--bdev-name', help='opal bdev') + p.add_argument('-p', '--password', help='password') + p.set_defaults(func=bdev_opal_get_info) + def bdev_opal_delete(args): rpc.bdev.bdev_opal_delete(args.client, bdev_name=args.bdev_name, diff --git a/scripts/rpc/bdev.py b/scripts/rpc/bdev.py index 4d2c8da46..4bd8f6b44 100644 --- a/scripts/rpc/bdev.py +++ b/scripts/rpc/bdev.py @@ -689,6 +689,24 @@ def bdev_opal_create(client, nvme_ctrlr_name, nsid, locking_range_id, range_star return client.call('bdev_opal_create', params) +def bdev_opal_get_info(client, bdev_name, password): + """Get opal locking range info. + + Args: + bdev_name: name of opal vbdev to get info + password: admin password + + Returns: + Locking range info. + """ + params = { + 'bdev_name': bdev_name, + 'password': password, + } + + return client.call('bdev_opal_get_info', params) + + def bdev_opal_delete(client, bdev_name, password): """Delete opal virtual bdev from the system. diff --git a/scripts/rpc/nvme.py b/scripts/rpc/nvme.py index 173bcd7f5..e9a0ba6bb 100644 --- a/scripts/rpc/nvme.py +++ b/scripts/rpc/nvme.py @@ -55,3 +55,33 @@ def bdev_nvme_get_controllers(client, name=None): if name: params['name'] = name return client.call('bdev_nvme_get_controllers', params) + + +def bdev_nvme_opal_init(client, nvme_ctrlr_name, password): + """Init nvme opal. Take ownership and activate + + Args: + nvme_ctrlr_name: name of nvme ctrlr + password: password to init opal + """ + params = { + 'nvme_ctrlr_name': nvme_ctrlr_name, + 'password': password, + } + + return client.call('bdev_nvme_opal_init', params) + + +def bdev_nvme_opal_revert(client, nvme_ctrlr_name, password): + """Revert opal to default factory settings. Erase all data. + + Args: + nvme_ctrlr_name: name of nvme ctrlr + password: password + """ + params = { + 'nvme_ctrlr_name': nvme_ctrlr_name, + 'password': password, + } + + return client.call('bdev_nvme_opal_revert', params)