diff --git a/CHANGELOG.md b/CHANGELOG.md index 30d6fdecf..5f8563547 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,7 +100,8 @@ is specified, use that specified nbd device. If it's not specified, pick availab ### Opal Add Opal scan support for NVMe to check whether it supports SED Opal and dump -device info. nvme_manage tool can be used to invoke this. +device info. Add Opal take ownership command support. nvme_manage tool can be used to invoke this. +This module should be considered experimental pending additional features and tests. ### iSCSI target diff --git a/examples/nvme/nvme_manage/nvme_manage.c b/examples/nvme/nvme_manage/nvme_manage.c index e7f465367..bf5deb2f2 100644 --- a/examples/nvme/nvme_manage/nvme_manage.c +++ b/examples/nvme/nvme_manage/nvme_manage.c @@ -142,7 +142,7 @@ static void usage(void) printf("\t[5: detach namespace from controller]\n"); printf("\t[6: format namespace or controller]\n"); printf("\t[7: firmware update]\n"); - printf("\t[8: opal scan]\n"); + printf("\t[8: opal]\n"); printf("\t[9: quit]\n"); } @@ -853,7 +853,8 @@ update_firmware_image(void) spdk_dma_free(fw_image); } -static void spdk_dump_opal_info(struct spdk_opal_info *opal) +static void +opal_dump_info(struct spdk_opal_info *opal) { if (!opal->opal_ssc_dev) { SPDK_ERRLOG("This device is not Opal enabled. Not Supported!\n"); @@ -928,30 +929,119 @@ static void spdk_dump_opal_info(struct spdk_opal_info *opal) } } +static void +opal_usage(void) +{ + printf("Opal General Usage:\n"); + printf("\n"); + printf("\t[1: scan device]\n"); + printf("\t[2: take ownership]\n"); + printf("\t[0: quit]\n"); +} + +static void +opal_scan(struct dev *iter) +{ + if (spdk_nvme_ctrlr_get_flags(iter->ctrlr) & SPDK_NVME_CTRLR_SECURITY_SEND_RECV_SUPPORTED) { + iter->opal_dev = spdk_opal_init_dev(iter->ctrlr); + if (iter->opal_dev == NULL) { + return; + } + + if (spdk_opal_supported(iter->opal_dev)) { + printf("\n\nOpal Supported:\n"); + display_controller(iter, CONTROLLER_DISPLAY_SIMPLISTIC); + spdk_opal_scan(iter->opal_dev); + opal_dump_info(spdk_opal_get_info(iter->opal_dev)); + } + spdk_opal_close(iter->opal_dev); + } else { + printf("%04x:%02x:%02x.%02x: NVMe Security Support/Receive Not supported.\n", + iter->pci_addr.domain, iter->pci_addr.bus, iter->pci_addr.dev, iter->pci_addr.func); + printf("%04x:%02x:%02x.%02x: Opal Not Supported\n\n\n", + iter->pci_addr.domain, iter->pci_addr.bus, iter->pci_addr.dev, iter->pci_addr.func); + } +} + +static void +opal_take_ownership(struct dev *iter) +{ + char new_passwd[MAX_PASSWORD_SIZE]; + char *passwd_p; + int ret; + int ch; + + if (spdk_nvme_ctrlr_get_flags(iter->ctrlr) & SPDK_NVME_CTRLR_SECURITY_SEND_RECV_SUPPORTED) { + iter->opal_dev = spdk_opal_init_dev(iter->ctrlr); + if (iter->opal_dev == NULL) { + return; + } + if (spdk_opal_supported(iter->opal_dev)) { + memset(new_passwd, 0, sizeof(new_passwd)); + printf("Please input the new password for ownership:\n"); + while ((ch = getchar()) != '\n' && ch != EOF); + passwd_p = get_line(new_passwd, MAX_PASSWORD_SIZE, stdin); + if (passwd_p) { + ret = spdk_opal_cmd(iter->opal_dev, OPAL_CMD_TAKE_OWNERSHIP, passwd_p); + if (ret) { + printf("Take ownership failure: %d\n", ret); + return; + } + printf("...\n...\nTake Ownership Success\n"); + } else { + printf("Input password invalid. Take ownership failure\n"); + } + } + spdk_opal_close(iter->opal_dev); + } else { + printf("%04x:%02x:%02x.%02x: NVMe Security Support/Receive Not supported.\n", + iter->pci_addr.domain, iter->pci_addr.bus, iter->pci_addr.dev, iter->pci_addr.func); + printf("%04x:%02x:%02x.%02x: Opal Not Supported\n\n\n", + iter->pci_addr.domain, iter->pci_addr.bus, iter->pci_addr.dev, iter->pci_addr.func); + } +} + static void test_opal(void) { - struct dev *iter; + int exit_flag = false; + struct dev *ctrlr; - foreach_dev(iter) { - if (spdk_nvme_ctrlr_get_flags(iter->ctrlr) & SPDK_NVME_CTRLR_SECURITY_SEND_RECV_SUPPORTED) { - iter->opal_dev = spdk_opal_init_dev(iter->ctrlr); - if (iter->opal_dev == NULL) { - return; - } - if (spdk_opal_supported(iter->opal_dev)) { - printf("\n\nOpal Supported:\n"); - display_controller(iter, CONTROLLER_DISPLAY_SIMPLISTIC); - spdk_opal_scan(iter->opal_dev); - spdk_dump_opal_info(spdk_opal_get_info(iter->opal_dev)); - } - spdk_opal_close(iter->opal_dev); - } else { - printf("%04x:%02x:%02x.%02x: NVMe Security Support/Receive Not supported.\n", - iter->pci_addr.domain, iter->pci_addr.bus, iter->pci_addr.dev, iter->pci_addr.func); - printf("%04x:%02x:%02x.%02x: Opal Not Supported\n\n\n", - iter->pci_addr.domain, iter->pci_addr.bus, iter->pci_addr.dev, iter->pci_addr.func); + ctrlr = get_controller(); + if (ctrlr == NULL) { + printf("Invalid controller PCI Address.\n"); + return; + } + + opal_usage(); + while (!exit_flag) { + int cmd; + if (!scanf("%d", &cmd)) { + printf("Invalid Command: command must be number 0-2\n"); + while (getchar() != '\n'); + opal_usage(); + continue; } + + switch (cmd) { + case 0: + exit_flag = true; + continue; + case 1: + opal_scan(ctrlr); + break; + case 2: + opal_take_ownership(ctrlr); + break; + + default: + printf("Invalid option\n"); + } + + while (getchar() != '\n'); + printf("press Enter to display Opal cmd menu ...\n"); + while (getchar() != '\n'); + opal_usage(); } } diff --git a/include/spdk/opal.h b/include/spdk/opal.h index 0aec383a8..325b745ab 100644 --- a/include/spdk/opal.h +++ b/include/spdk/opal.h @@ -42,6 +42,8 @@ #define SPDK_OPAL_NOT_SUPPORTED 0xFF +#define MAX_PASSWORD_SIZE 32 /* in byte */ + /* * TCG Storage Architecture Core Spec v2.01 r1.00 * 5.1.5 Method Status Codes @@ -84,6 +86,7 @@ enum spdk_opal_cmd { OPAL_CMD_ERASE_LOCKING_RANGE, OPAL_CMD_SECURE_ERASE_LOCKING_RANGE, OPAL_CMD_INITIAL_SETUP, + OPAL_CMD_SCAN, }; struct spdk_opal_info { @@ -136,10 +139,12 @@ struct spdk_opal_dev; struct spdk_opal_dev *spdk_opal_init_dev(void *dev_handler); -void spdk_opal_scan(struct spdk_opal_dev *dev); +int spdk_opal_scan(struct spdk_opal_dev *dev); void spdk_opal_close(struct spdk_opal_dev *dev); struct spdk_opal_info *spdk_opal_get_info(struct spdk_opal_dev *dev); bool spdk_opal_supported(struct spdk_opal_dev *dev); +int spdk_opal_cmd(struct spdk_opal_dev *dev, unsigned int cmd, void *arg); + #endif diff --git a/lib/nvme/nvme_opal.c b/lib/nvme/nvme_opal.c index 12eaa913a..f55aa8ccb 100644 --- a/lib/nvme/nvme_opal.c +++ b/lib/nvme/nvme_opal.c @@ -129,6 +129,97 @@ opal_add_token_u8(int *err, struct spdk_opal_dev *dev, uint8_t token) dev->cmd[dev->cmd_pos++] = token; } +static void +opal_add_short_atom_header(struct spdk_opal_dev *dev, bool bytestring, + bool has_sign, size_t len) +{ + uint8_t atom; + int err = 0; + + atom = SPDK_SHORT_ATOM_ID; + atom |= bytestring ? SPDK_SHORT_ATOM_BYTESTRING_FLAG : 0; + atom |= has_sign ? SPDK_SHORT_ATOM_SIGN_FLAG : 0; + atom |= len & SPDK_SHORT_ATOM_LEN_MASK; + + opal_add_token_u8(&err, dev, atom); +} + +static void +opal_add_medium_atom_header(struct spdk_opal_dev *dev, bool bytestring, + bool has_sign, size_t len) +{ + uint8_t header; + + header = SPDK_MEDIUM_ATOM_ID; + header |= bytestring ? SPDK_MEDIUM_ATOM_BYTESTRING_FLAG : 0; + header |= has_sign ? SPDK_MEDIUM_ATOM_SIGN_FLAG : 0; + header |= (len >> 8) & SPDK_MEDIUM_ATOM_LEN_MASK; + dev->cmd[dev->cmd_pos++] = header; + dev->cmd[dev->cmd_pos++] = len; +} + +static void +opal_add_token_bytestring(int *err, struct spdk_opal_dev *dev, + const uint8_t *bytestring, size_t len) +{ + size_t header_len = 1; + bool is_short_atom = true; + + if (*err) { + return; + } + + if (len & ~SPDK_SHORT_ATOM_LEN_MASK) { + header_len = 2; + is_short_atom = false; + } + + if (len >= IO_BUFFER_LENGTH - dev->cmd_pos - header_len) { + SPDK_ERRLOG("Error adding bytestring: end of buffer.\n"); + *err = -ERANGE; + return; + } + + if (is_short_atom) { + opal_add_short_atom_header(dev, true, false, len); + } else { + opal_add_medium_atom_header(dev, true, false, len); + } + + memcpy(&dev->cmd[dev->cmd_pos], bytestring, len); + dev->cmd_pos += len; +} + +static void +opal_add_token_u64(int *err, struct spdk_opal_dev *dev, uint64_t number) +{ + int startat = 0; + + /* add header first */ + if (number <= SPDK_TINY_ATOM_DATA_MASK) { + dev->cmd[dev->cmd_pos++] = (uint8_t) number & SPDK_TINY_ATOM_DATA_MASK; + } else { + if (number < 0x100) { + dev->cmd[dev->cmd_pos++] = 0x81; /* short atom, 1 byte length */ + startat = 0; + } else if (number < 0x10000) { + dev->cmd[dev->cmd_pos++] = 0x82; /* short atom, 2 byte length */ + startat = 1; + } else if (number < 0x100000000) { + dev->cmd[dev->cmd_pos++] = 0x84; /* short atom, 4 byte length */ + startat = 3; + } else { + dev->cmd[dev->cmd_pos++] = 0x88; /* short atom, 8 byte length */ + startat = 7; + } + + /* add number value */ + for (int i = startat; i > -1; i--) { + dev->cmd[dev->cmd_pos++] = (uint8_t)((number >> (i * 8)) & 0xff); + } + } +} + static int opal_cmd_finalize(struct spdk_opal_dev *dev, uint32_t hsn, uint32_t tsn, bool eod) { @@ -367,7 +458,6 @@ opal_response_token_matches(const struct spdk_opal_resp_token *token, if (!token || token->type != OPAL_DTA_TOKENID_TOKEN || token->pos[0] != match) { - SPDK_ERRLOG("Token not matching\n"); return false; } return true; @@ -417,6 +507,31 @@ opal_response_get_u64(const struct spdk_opal_resp_parsed *resp, int index) return resp->resp_tokens[index].stored.unsigned_num; } +static size_t +opal_response_get_string(const struct spdk_opal_resp_parsed *resp, int n, + const char **store) +{ + *store = NULL; + if (!resp) { + SPDK_ERRLOG("Response is NULL\n"); + return 0; + } + + if (n > resp->num) { + SPDK_ERRLOG("Response has %d tokens. Can't access %d\n", + resp->num, n); + return 0; + } + + if (resp->resp_tokens[n].type != OPAL_DTA_TOKENID_BYTESTRING) { + SPDK_ERRLOG("Token is not a byte string!\n"); + return 0; + } + + *store = resp->resp_tokens[n].pos + 1; + return resp->resp_tokens[n].len - 1; +} + static int opal_response_status(const struct spdk_opal_resp_parsed *resp) { @@ -789,10 +904,276 @@ opal_check_support(struct spdk_opal_dev *dev) void spdk_opal_close(struct spdk_opal_dev *dev) { + pthread_mutex_destroy(&dev->mutex_lock); free(dev->opal_info); free(dev); } +static int +opal_start_session_cb(struct spdk_opal_dev *dev) +{ + uint32_t hsn, tsn; + int error = 0; + + error = opal_parse_and_check_status(dev); + if (error) { + return error; + } + + hsn = opal_response_get_u64(&dev->parsed_resp, 4); + tsn = opal_response_get_u64(&dev->parsed_resp, 5); + + if (hsn == 0 && tsn == 0) { + SPDK_ERRLOG("Couldn't authenticate session\n"); + return -EPERM; + } + + dev->hsn = hsn; + dev->tsn = tsn; + return 0; +} + +static int +opal_start_generic_session(struct spdk_opal_dev *dev, + enum opal_uid_enum auth, + enum opal_uid_enum sp_type, + const char *key, + uint8_t key_len) +{ + uint32_t hsn; + int err = 0; + + if (key == NULL && auth != UID_ANYBODY) { + return OPAL_INVAL_PARAM; + } + + opal_clear_cmd(dev); + + opal_set_comid(dev, dev->comid); + hsn = GENERIC_HOST_SESSION_NUM; + + opal_add_token_u8(&err, dev, SPDK_OPAL_CALL); + opal_add_token_bytestring(&err, dev, spdk_opal_uid[UID_SMUID], + OPAL_UID_LENGTH); + opal_add_token_bytestring(&err, dev, spdk_opal_method[STARTSESSION_METHOD], + OPAL_UID_LENGTH); + opal_add_token_u8(&err, dev, SPDK_OPAL_STARTLIST); + opal_add_token_u64(&err, dev, hsn); + opal_add_token_bytestring(&err, dev, spdk_opal_uid[sp_type], OPAL_UID_LENGTH); + opal_add_token_u8(&err, dev, SPDK_OPAL_TRUE); /* Write */ + + switch (auth) { + case UID_ANYBODY: + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDLIST); + break; + case UID_ADMIN1: + case UID_SID: + opal_add_token_u8(&err, dev, SPDK_OPAL_STARTNAME); + opal_add_token_u8(&err, dev, 0); /* HostChallenge */ + opal_add_token_bytestring(&err, dev, key, key_len); + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDNAME); + opal_add_token_u8(&err, dev, SPDK_OPAL_STARTNAME); + opal_add_token_u8(&err, dev, 3); /* HostSignAuth */ + opal_add_token_bytestring(&err, dev, spdk_opal_uid[auth], + OPAL_UID_LENGTH); + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDNAME); + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDLIST); + break; + default: + SPDK_ERRLOG("Cannot start Admin SP session with auth %d\n", auth); + return -EINVAL; + } + + if (err) { + SPDK_ERRLOG("Error building start adminsp session command.\n"); + return err; + } + + return opal_finalize_and_send(dev, 1, opal_start_session_cb); +} + + +static int +opal_start_anybody_adminsp_session(struct spdk_opal_dev *dev, void *data) +{ + return opal_start_generic_session(dev, UID_ANYBODY, + UID_ADMINSP, NULL, 0); +} + +static int +opal_get_msid_cpin_pin_cb(struct spdk_opal_dev *dev) +{ + const char *msid_pin; + size_t strlen; + int error = 0; + + error = opal_parse_and_check_status(dev); + if (error) { + return error; + } + + strlen = opal_response_get_string(&dev->parsed_resp, 4, &msid_pin); + if (!msid_pin) { + SPDK_ERRLOG("Couldn't extract PIN from response\n"); + return -EINVAL; + } + + dev->prev_d_len = strlen; + dev->prev_data = calloc(1, strlen); + if (!dev->prev_data) { + SPDK_ERRLOG("memory allocation error\n"); + return -ENOMEM; + } + memcpy(dev->prev_data, msid_pin, strlen); + + SPDK_DEBUGLOG(SPDK_LOG_OPAL, "MSID = %p\n", dev->prev_data); + return 0; +} + +static int +opal_get_msid_cpin_pin(struct spdk_opal_dev *dev, void *data) +{ + int err = 0; + + opal_clear_cmd(dev); + opal_set_comid(dev, dev->comid); + + opal_add_token_u8(&err, dev, SPDK_OPAL_CALL); + opal_add_token_bytestring(&err, dev, spdk_opal_uid[UID_C_PIN_MSID], + OPAL_UID_LENGTH); + opal_add_token_bytestring(&err, dev, spdk_opal_method[GET_METHOD], OPAL_UID_LENGTH); + + opal_add_token_u8(&err, dev, SPDK_OPAL_STARTLIST); + opal_add_token_u8(&err, dev, SPDK_OPAL_STARTLIST); + + opal_add_token_u8(&err, dev, SPDK_OPAL_STARTNAME); + opal_add_token_u8(&err, dev, SPDK_OPAL_STARTCOLUMN); + opal_add_token_u8(&err, dev, SPDK_OPAL_PIN); + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDNAME); + + opal_add_token_u8(&err, dev, SPDK_OPAL_STARTNAME); + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDCOLUMN); + opal_add_token_u8(&err, dev, SPDK_OPAL_PIN); + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDNAME); + + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDLIST); + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDLIST); + + if (err) { + SPDK_ERRLOG("Error building Get MSID CPIN PIN command.\n"); + return err; + } + + return opal_finalize_and_send(dev, 1, opal_get_msid_cpin_pin_cb); +} + +static int +opal_start_adminsp_session(struct spdk_opal_dev *dev, void *data) +{ + int ret; + uint8_t *key = dev->prev_data; + + if (!key) { + const struct spdk_opal_key *okey = data; + if (okey == NULL) { + SPDK_ERRLOG("No key found for auth session\n"); + return -EINVAL; + } + ret = opal_start_generic_session(dev, UID_SID, + UID_ADMINSP, + okey->key, + okey->key_len); + } else { + ret = opal_start_generic_session(dev, UID_SID, + UID_ADMINSP, + key, dev->prev_d_len); + free(key); + dev->prev_data = NULL; + } + + return ret; +} + +static int +opal_generic_pw_cmd(uint8_t *key, size_t key_len, uint8_t *cpin_uid, + struct spdk_opal_dev *dev) +{ + int err = 0; + + opal_clear_cmd(dev); + opal_set_comid(dev, dev->comid); + + opal_add_token_u8(&err, dev, SPDK_OPAL_CALL); + opal_add_token_bytestring(&err, dev, cpin_uid, OPAL_UID_LENGTH); + opal_add_token_bytestring(&err, dev, spdk_opal_method[SET_METHOD], + OPAL_UID_LENGTH); + opal_add_token_u8(&err, dev, SPDK_OPAL_STARTLIST); + opal_add_token_u8(&err, dev, SPDK_OPAL_STARTNAME); + opal_add_token_u8(&err, dev, SPDK_OPAL_VALUES); + opal_add_token_u8(&err, dev, SPDK_OPAL_STARTLIST); + opal_add_token_u8(&err, dev, SPDK_OPAL_STARTNAME); + opal_add_token_u8(&err, dev, SPDK_OPAL_PIN); + opal_add_token_bytestring(&err, dev, key, key_len); + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDNAME); + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDLIST); + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDNAME); + opal_add_token_u8(&err, dev, SPDK_OPAL_ENDLIST); + + return err; +} + +static int +opal_set_sid_cpin_pin(struct spdk_opal_dev *dev, void *data) +{ + uint8_t cpin_uid[OPAL_UID_LENGTH]; + const char *new_passwd = data; + struct spdk_opal_key *opal_key; + + opal_key = calloc(1, sizeof(struct spdk_opal_key)); + if (!opal_key) { + SPDK_ERRLOG("Memory allocation failed for spdk_opal_key\n"); + return -ENOMEM; + } + + opal_key->key_len = strlen(new_passwd); + memcpy(opal_key->key, new_passwd, opal_key->key_len); + dev->dev_key = opal_key; + + memcpy(cpin_uid, spdk_opal_uid[UID_C_PIN_SID], OPAL_UID_LENGTH); + + if (opal_generic_pw_cmd(opal_key->key, opal_key->key_len, cpin_uid, dev)) { + SPDK_ERRLOG("Error building Set SID cpin\n"); + return -ERANGE; + } + return opal_finalize_and_send(dev, 1, opal_parse_and_check_status); +} + +static int +spdk_opal_take_ownership(struct spdk_opal_dev *dev, char *new_passwd) +{ + const struct spdk_opal_step owner_steps[] = { + { opal_discovery0, }, + { opal_start_anybody_adminsp_session, }, + { opal_get_msid_cpin_pin, }, + { opal_end_session, }, + { opal_start_adminsp_session, }, + { opal_set_sid_cpin_pin, new_passwd }, + { opal_end_session, }, + { NULL, } + }; + int ret; + + if (!dev) { + return -ENODEV; + } + + pthread_mutex_lock(&dev->mutex_lock); + opal_setup_dev(dev, owner_steps); + ret = opal_next(dev); + pthread_mutex_unlock(&dev->mutex_lock); + return ret; +} + struct spdk_opal_dev * spdk_opal_init_dev(void *dev_handler) { @@ -819,10 +1200,18 @@ spdk_opal_init_dev(void *dev_handler) SPDK_INFOLOG(SPDK_LOG_OPAL, "Opal is not supported on this device\n"); dev->supported = false; } + + if (pthread_mutex_init(&dev->mutex_lock, NULL)) { + SPDK_ERRLOG("Mutex init failed\n"); + free(dev->opal_info); + free(dev); + return NULL; + } + return dev; } -void +int spdk_opal_scan(struct spdk_opal_dev *dev) { int ret; @@ -831,8 +1220,8 @@ spdk_opal_scan(struct spdk_opal_dev *dev) if (ret) { SPDK_ERRLOG("check opal support failed: %d\n", ret); spdk_opal_close(dev); - return; } + return ret; } struct spdk_opal_info * @@ -847,5 +1236,33 @@ spdk_opal_supported(struct spdk_opal_dev *dev) return dev->supported; } +int +spdk_opal_cmd(struct spdk_opal_dev *dev, unsigned int cmd, void *arg) +{ + if (!dev) { + SPDK_ERRLOG("Device null\n"); + return -ENODEV; + } + if (!dev->supported) { + SPDK_ERRLOG("Device not supported\n"); + return -EINVAL; + } + + switch (cmd) { + case OPAL_CMD_SCAN: + return spdk_opal_scan(dev); + case OPAL_CMD_TAKE_OWNERSHIP: + return spdk_opal_take_ownership(dev, arg); + case OPAL_CMD_LOCK_UNLOCK: + case OPAL_CMD_ACTIVATE_LSP: + case OPAL_CMD_REVERT_TPER: + case OPAL_CMD_SETUP_LOCKING_RANGE: + + default: + SPDK_ERRLOG("NOT SUPPORTED\n"); + return -EINVAL; + } +} + /* Log component for opal submodule */ SPDK_LOG_REGISTER_COMPONENT("opal", SPDK_LOG_OPAL) diff --git a/lib/nvme/nvme_opal_internal.h b/lib/nvme/nvme_opal_internal.h index 7552b62f6..3473414f1 100644 --- a/lib/nvme/nvme_opal_internal.h +++ b/lib/nvme/nvme_opal_internal.h @@ -325,6 +325,8 @@ struct spdk_opal_dev { struct spdk_opal_info *opal_info; uint64_t timeout; /* seconds */ + + pthread_mutex_t mutex_lock; /* some structs are accessed by current thread only */ }; enum opal_lock_state {