From bc40f5f66108de8d98ccebf240fb02b0f2eb18bc Mon Sep 17 00:00:00 2001 From: Chunyang Hui Date: Wed, 9 Oct 2019 19:55:46 +0800 Subject: [PATCH] bdev/opal: Add rpc commands for multiuser Add rpc commands bdev_opal_lock_unlock, bdev_opal_new_user. Admin can add new user for opal bdev created and the user can lock/unlock the bdev by himself. Change-Id: I9a1e360399617b5a039dc5353097ab525c7eb964 Signed-off-by: Chunyang Hui Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/471475 Reviewed-by: Changpeng Liu Reviewed-by: Jim Harris Tested-by: SPDK CI Jenkins --- doc/jsonrpc.md | 83 +++++++++++++++++++++++ module/bdev/nvme/vbdev_opal.c | 105 +++++++++++++++++++++++++++++ module/bdev/nvme/vbdev_opal.h | 6 ++ module/bdev/nvme/vbdev_opal_rpc.c | 107 ++++++++++++++++++++++++++++++ scripts/rpc.py | 29 ++++++++ scripts/rpc/bdev.py | 38 +++++++++++ 6 files changed, 368 insertions(+) diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index 5fcc6e8a34..c01dcd8f38 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -5677,6 +5677,89 @@ Example response: } ~~~ +## bdev_opal_new_user {#rpc_bdev_opal_new_user} + +This enables a new user to the specified opal bdev so that the user can lock/unlock the bdev. +Recalling this for the same opal bdev, only the newest user will have the privilege. + +### Parameters + +Name | Optional | Type | Description +----------------------- | -------- | ----------- | ----------- +bdev_name | Required | string | name of OPAL vbdev +admin_password | Required | string | admin password +user_id | Required | number | user ID +user_password | Required | string | user password + +### Example + +Example request: + +~~~ +{ + "jsonrpc": "2.0", + "method": "bdev_opal_new_user", + "id": 1, + "params": { + "bdev_name": "nvme0n1r1", + "admin_password": "*****", + "user_id": "1", + "user_password": "********" + } +} +~~~ + +Example response: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "result": true +} +~~~ + +## bdev_opal_set_lock_state {#rpc_bdev_opal_set_lock_state} + +This is used to lock/unlock specific opal bdev providing user ID and password. + +### Parameters + +Name | Optional | Type | Description +----------------------- | -------- | ----------- | ----------- +bdev_name | Required | string | name of OPAL vbdev +user_id | Required | number | user ID +password | Required | string | user password +lock_state | Required | string | lock state + +### Example + +Example request: + +~~~ +{ + "jsonrpc": "2.0", + "method": "bdev_opal_set_lock_state", + "id": 1, + "params": { + "bdev_name": "nvme0n1r1", + "user_id": "1", + "user_password": "********", + "lock_state": "rwlock" + } +} +~~~ + +Example response: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "result": true +} +~~~ + # Notifications ## notify_get_types {#rpc_notify_get_types} diff --git a/module/bdev/nvme/vbdev_opal.c b/module/bdev/nvme/vbdev_opal.c index 1b365a7df7..413d1657a8 100644 --- a/module/bdev/nvme/vbdev_opal.c +++ b/module/bdev/nvme/vbdev_opal.c @@ -605,4 +605,109 @@ spdk_vbdev_opal_revert_tper(struct nvme_bdev_ctrlr *nvme_ctrlr, const char *pass return 0; } +int +spdk_vbdev_opal_set_lock_state(const char *bdev_name, uint16_t user_id, const char *password, + const char *lock_state) +{ + struct nvme_bdev_ctrlr *nvme_ctrlr; + int locking_range_id; + int rc; + enum spdk_opal_lock_state state_flag; + struct opal_vbdev *opal_bdev; + + TAILQ_FOREACH(opal_bdev, &g_opal_vbdev, tailq) { + if (strcmp(opal_bdev->name, bdev_name) == 0) { + break; + } + } + + if (opal_bdev == NULL) { + SPDK_ERRLOG("%s not found\n", bdev_name); + return -ENODEV; + } + + nvme_ctrlr = opal_bdev->nvme_ctrlr; + if (nvme_ctrlr == NULL) { + SPDK_ERRLOG("can't find nvme_ctrlr of %s\n", opal_bdev->name); + return -ENODEV; + } + + if (strcasecmp(lock_state, "READWRITE") == 0) { + state_flag = OPAL_READWRITE; + } else if (strcasecmp(lock_state, "READONLY") == 0) { + state_flag = OPAL_READONLY; + } else if (strcasecmp(lock_state, "RWLOCK") == 0) { + state_flag = OPAL_RWLOCK; + } else { + SPDK_ERRLOG("Invalid OPAL lock state input\n"); + return -EINVAL; + } + + locking_range_id = opal_bdev->cfg.locking_range_id; + rc = spdk_opal_cmd_lock_unlock(nvme_ctrlr->opal_dev, user_id, state_flag, locking_range_id, + password); + if (rc) { + SPDK_ERRLOG("%s lock/unlock failure: %d\n", bdev_name, rc); + } + + return rc; +} + +int +spdk_vbdev_opal_enable_new_user(const char *bdev_name, const char *admin_password, uint16_t user_id, + const char *user_password) +{ + struct nvme_bdev_ctrlr *nvme_ctrlr; + int locking_range_id; + int rc; + struct opal_vbdev *opal_bdev; + + TAILQ_FOREACH(opal_bdev, &g_opal_vbdev, tailq) { + if (strcmp(opal_bdev->name, bdev_name) == 0) { + break; + } + } + + if (opal_bdev == NULL) { + SPDK_ERRLOG("%s not found\n", bdev_name); + return -ENODEV; + } + + nvme_ctrlr = opal_bdev->nvme_ctrlr; + if (nvme_ctrlr == NULL) { + SPDK_ERRLOG("can't find nvme_ctrlr of %s\n", opal_bdev->name); + return -ENODEV; + } + + rc = spdk_opal_cmd_enable_user(nvme_ctrlr->opal_dev, user_id, admin_password); + if (rc) { + SPDK_ERRLOG("%s enable user error: %d\n", bdev_name, rc); + return rc; + } + + rc = spdk_opal_cmd_set_new_passwd(nvme_ctrlr->opal_dev, user_id, user_password, admin_password, + true); + if (rc) { + SPDK_ERRLOG("%s set user password error: %d\n", bdev_name, rc); + return rc; + } + + locking_range_id = opal_bdev->cfg.locking_range_id; + rc = spdk_opal_cmd_add_user_to_locking_range(nvme_ctrlr->opal_dev, user_id, locking_range_id, + OPAL_READONLY, admin_password); + if (rc) { + SPDK_ERRLOG("%s add user READONLY priority error: %d\n", bdev_name, rc); + return rc; + } + + rc = spdk_opal_cmd_add_user_to_locking_range(nvme_ctrlr->opal_dev, user_id, locking_range_id, + OPAL_READWRITE, admin_password); + if (rc) { + SPDK_ERRLOG("%s add user READWRITE priority error: %d\n", bdev_name, rc); + return rc; + } + + 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 cdd65ec292..81123fc778 100644 --- a/module/bdev/nvme/vbdev_opal.h +++ b/module/bdev/nvme/vbdev_opal.h @@ -47,4 +47,10 @@ 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); +int spdk_vbdev_opal_enable_new_user(const char *bdev_name, const char *admin_password, + uint16_t user_id, const char *user_password); + +int spdk_vbdev_opal_set_lock_state(const char *bdev_name, uint16_t user_id, const char *password, + const char *lock_state); + #endif diff --git a/module/bdev/nvme/vbdev_opal_rpc.c b/module/bdev/nvme/vbdev_opal_rpc.c index 10d3654ab2..48f7f70f5f 100644 --- a/module/bdev/nvme/vbdev_opal_rpc.c +++ b/module/bdev/nvme/vbdev_opal_rpc.c @@ -360,3 +360,110 @@ out: free_rpc_bdev_opal_delete(&req); } SPDK_RPC_REGISTER("bdev_opal_delete", spdk_rpc_bdev_opal_delete, SPDK_RPC_RUNTIME) + +struct rpc_bdev_opal_set_lock_state { + char *bdev_name; + uint16_t user_id; + char *password; + char *lock_state; +}; + +static void +free_rpc_bdev_opal_set_lock_state(struct rpc_bdev_opal_set_lock_state *req) +{ + free(req->bdev_name); + free(req->password); + free(req->lock_state); +} + +static const struct spdk_json_object_decoder rpc_bdev_opal_set_lock_state_decoders[] = { + {"bdev_name", offsetof(struct rpc_bdev_opal_set_lock_state, bdev_name), spdk_json_decode_string}, + {"user_id", offsetof(struct rpc_bdev_opal_set_lock_state, user_id), spdk_json_decode_uint16}, + {"password", offsetof(struct rpc_bdev_opal_set_lock_state, password), spdk_json_decode_string}, + {"lock_state", offsetof(struct rpc_bdev_opal_set_lock_state, lock_state), spdk_json_decode_string}, +}; + +static void +spdk_rpc_bdev_opal_set_lock_state(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_bdev_opal_set_lock_state req = {}; + struct spdk_json_write_ctx *w; + int rc; + + if (spdk_json_decode_object(params, rpc_bdev_opal_set_lock_state_decoders, + SPDK_COUNTOF(rpc_bdev_opal_set_lock_state_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; + } + + rc = spdk_vbdev_opal_set_lock_state(req.bdev_name, req.user_id, req.password, req.lock_state); + if (rc != 0) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, spdk_strerror(-rc)); + goto out; + } + + w = spdk_jsonrpc_begin_result(request); + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); + +out: + free_rpc_bdev_opal_set_lock_state(&req); +} +SPDK_RPC_REGISTER("bdev_opal_set_lock_state", spdk_rpc_bdev_opal_set_lock_state, SPDK_RPC_RUNTIME) + +struct rpc_bdev_opal_new_user { + char *bdev_name; + char *admin_password; + uint16_t user_id; + char *user_password; +}; + +static void +free_rpc_bdev_opal_new_user(struct rpc_bdev_opal_new_user *req) +{ + free(req->bdev_name); + free(req->admin_password); + free(req->user_password); +} + +static const struct spdk_json_object_decoder rpc_bdev_opal_new_user_decoders[] = { + {"bdev_name", offsetof(struct rpc_bdev_opal_new_user, bdev_name), spdk_json_decode_string}, + {"admin_password", offsetof(struct rpc_bdev_opal_new_user, admin_password), spdk_json_decode_string}, + {"user_id", offsetof(struct rpc_bdev_opal_new_user, user_id), spdk_json_decode_uint16}, + {"user_password", offsetof(struct rpc_bdev_opal_new_user, user_password), spdk_json_decode_string}, +}; + +static void +spdk_rpc_bdev_opal_new_user(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_bdev_opal_new_user req = {}; + struct spdk_json_write_ctx *w; + int rc; + + if (spdk_json_decode_object(params, rpc_bdev_opal_new_user_decoders, + SPDK_COUNTOF(rpc_bdev_opal_new_user_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; + } + + rc = spdk_vbdev_opal_enable_new_user(req.bdev_name, req.admin_password, req.user_id, + req.user_password); + if (rc != 0) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, spdk_strerror(-rc)); + goto out; + } + + w = spdk_jsonrpc_begin_result(request); + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); + +out: + free_rpc_bdev_opal_new_user(&req); +} +SPDK_RPC_REGISTER("bdev_opal_new_user", spdk_rpc_bdev_opal_new_user, SPDK_RPC_RUNTIME) diff --git a/scripts/rpc.py b/scripts/rpc.py index 7daa50a6de..ac9ef827aa 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -2071,6 +2071,35 @@ 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_delete) + def bdev_opal_new_user(args): + rpc.bdev.bdev_opal_new_user(args.client, + bdev_name=args.bdev_name, + admin_password=args.admin_password, + user_id=args.user_id, + user_password=args.user_password) + + p = subparsers.add_parser('bdev_opal_new_user', help="""Add a user to opal bdev who can set lock state for this bdev""") + p.add_argument('-b', '--bdev-name', help='opal bdev', required=True) + p.add_argument('-p', '--admin-password', help='admin password', required=True) + p.add_argument('-i', '--user-id', help='ID for new user', type=int, required=True) + p.add_argument('-u', '--user-password', help='password set for this user', required=True) + p.set_defaults(func=bdev_opal_new_user) + + def bdev_opal_set_lock_state(args): + rpc.bdev.bdev_opal_set_lock_state(args.client, + bdev_name=args.bdev_name, + user_id=args.user_id, + password=args.password, + lock_state=args.lock_state) + + p = subparsers.add_parser('bdev_opal_set_lock_state', help="""set lock state for an opal bdev""") + p.add_argument('-b', '--bdev-name', help='opal bdev', required=True) + p.add_argument('-i', '--user-id', help='ID of the user who want to set lock state, either admin or a user assigned to this bdev', + type=int, required=True) + p.add_argument('-p', '--password', help='password of this user', required=True) + p.add_argument('-l', '--lock-state', help='lock state to set, choose from {readwrite, readonly, rwlock}', required=True) + p.set_defaults(func=bdev_opal_set_lock_state) + # bdev_nvme_send_cmd def bdev_nvme_send_cmd(args): print_dict(rpc.nvme.bdev_nvme_send_cmd(args.client, diff --git a/scripts/rpc/bdev.py b/scripts/rpc/bdev.py index 4bd8f6b446..4fd360e890 100644 --- a/scripts/rpc/bdev.py +++ b/scripts/rpc/bdev.py @@ -722,6 +722,44 @@ def bdev_opal_delete(client, bdev_name, password): return client.call('bdev_opal_delete', params) +def bdev_opal_new_user(client, bdev_name, admin_password, user_id, user_password): + """Add a user to opal bdev who can set lock state for this bdev. + + Args: + bdev_name: name of opal vbdev + admin_password: admin password + user_id: ID of the user who will be added to this opal bdev + user_password: password set for this user + """ + params = { + 'bdev_name': bdev_name, + 'admin_password': admin_password, + 'user_id': user_id, + 'user_password': user_password, + } + + return client.call('bdev_opal_new_user', params) + + +def bdev_opal_set_lock_state(client, bdev_name, user_id, password, lock_state): + """set lock state for an opal bdev. + + Args: + bdev_name: name of opal vbdev + user_id: ID of the user who will set lock state + password: password of the user + lock_state: lock state to set + """ + params = { + 'bdev_name': bdev_name, + 'user_id': user_id, + 'password': password, + 'lock_state': lock_state, + } + + return client.call('bdev_opal_set_lock_state', params) + + @deprecated_alias('construct_split_vbdev') def bdev_split_create(client, base_bdev, split_count, split_size_mb=None): """Create split block devices from a base bdev.