scsi: add persistent reservation out with register feature support
To establish a persistent reservation the application client shall first register an I_T nexus with the device server. An application client registers with a logical unit by issuing a PERSISTENT RESERVE OUT command with REGISTER service action or REGISTER AND IGNORE EXISTING KEY service action. Specify Initiator Ports (SPEC_I_PT) bit and All Target Ports (ALL_TG_PT) bit are not supported for now, the registrants belong to the I_T nexus who sends the command. Also Activate Persist Through Power Loss (APTPL) bit will be supported in following patches. Change-Id: If057a764a4bfa73017f98048c94b697dbfa4b4a1 Signed-off-by: Changpeng Liu <changpeng.liu@intel.com> Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/436088 Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
This commit is contained in:
parent
6a8a1b6bef
commit
d0d19eb82e
@ -34,7 +34,7 @@
|
||||
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
C_SRCS = dev.c lun.c port.c scsi.c scsi_bdev.c scsi_rpc.c task.c
|
||||
C_SRCS = dev.c lun.c port.c scsi.c scsi_bdev.c scsi_pr.c scsi_rpc.c task.c
|
||||
LIBNAME = scsi
|
||||
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk
|
||||
|
@ -214,6 +214,12 @@ spdk_scsi_lun_execute_tasks(struct spdk_scsi_lun *lun)
|
||||
static void
|
||||
scsi_lun_remove(struct spdk_scsi_lun *lun)
|
||||
{
|
||||
struct spdk_scsi_pr_registrant *reg, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
|
||||
TAILQ_REMOVE(&lun->reg_head, reg, link);
|
||||
free(reg);
|
||||
}
|
||||
spdk_bdev_close(lun->bdev_desc);
|
||||
|
||||
free(lun);
|
||||
@ -358,6 +364,7 @@ spdk_scsi_lun_construct(struct spdk_bdev *bdev,
|
||||
lun->hotremove_cb = hotremove_cb;
|
||||
lun->hotremove_ctx = hotremove_ctx;
|
||||
TAILQ_INIT(&lun->open_descs);
|
||||
TAILQ_INIT(&lun->reg_head);
|
||||
|
||||
return lun;
|
||||
}
|
||||
|
@ -2018,6 +2018,31 @@ bdev_scsi_process_primary(struct spdk_scsi_task *task)
|
||||
rc = 0;
|
||||
break;
|
||||
|
||||
case SPDK_SPC_PERSISTENT_RESERVE_OUT:
|
||||
pllen = from_be32(&cdb[5]);
|
||||
rc = bdev_scsi_check_len(task, pllen, 24);
|
||||
if (rc < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
data = spdk_scsi_task_gather_data(task, &rc);
|
||||
if (rc < 0) {
|
||||
break;
|
||||
}
|
||||
data_len = rc;
|
||||
if (data_len < 24) {
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = spdk_scsi_pr_out(task, cdb, data, data_len);
|
||||
if (rc < 0) {
|
||||
break;
|
||||
}
|
||||
rc = pllen;
|
||||
data_len = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return SPDK_SCSI_TASK_UNKNOWN;
|
||||
}
|
||||
|
@ -60,21 +60,41 @@ struct spdk_scsi_port {
|
||||
char name[SPDK_SCSI_PORT_MAX_NAME_LENGTH];
|
||||
};
|
||||
|
||||
/* Registrant with I_T nextus */
|
||||
struct spdk_scsi_pr_registrant {
|
||||
uint64_t rkey;
|
||||
uint16_t relative_target_port_id;
|
||||
uint16_t transport_id_len;
|
||||
char transport_id[SPDK_SCSI_MAX_TRANSPORT_ID_LENGTH];
|
||||
char initiator_port_name[SPDK_SCSI_PORT_MAX_NAME_LENGTH];
|
||||
char target_port_name[SPDK_SCSI_PORT_MAX_NAME_LENGTH];
|
||||
struct spdk_scsi_port *initiator_port;
|
||||
struct spdk_scsi_port *target_port;
|
||||
TAILQ_ENTRY(spdk_scsi_pr_registrant) link;
|
||||
};
|
||||
|
||||
/* Reservation with LU_SCOPE */
|
||||
struct spdk_scsi_pr_reservation {
|
||||
struct spdk_scsi_pr_registrant *holder;
|
||||
enum spdk_scsi_pr_type_code rtype;
|
||||
uint64_t crkey;
|
||||
};
|
||||
|
||||
struct spdk_scsi_dev {
|
||||
int id;
|
||||
int is_allocated;
|
||||
bool removed;
|
||||
spdk_scsi_dev_destruct_cb_t remove_cb;
|
||||
void *remove_ctx;
|
||||
int id;
|
||||
int is_allocated;
|
||||
bool removed;
|
||||
spdk_scsi_dev_destruct_cb_t remove_cb;
|
||||
void *remove_ctx;
|
||||
|
||||
char name[SPDK_SCSI_DEV_MAX_NAME + 1];
|
||||
char name[SPDK_SCSI_DEV_MAX_NAME + 1];
|
||||
|
||||
struct spdk_scsi_lun *lun[SPDK_SCSI_DEV_MAX_LUN];
|
||||
struct spdk_scsi_lun *lun[SPDK_SCSI_DEV_MAX_LUN];
|
||||
|
||||
int num_ports;
|
||||
struct spdk_scsi_port port[SPDK_SCSI_DEV_MAX_PORTS];
|
||||
int num_ports;
|
||||
struct spdk_scsi_port port[SPDK_SCSI_DEV_MAX_PORTS];
|
||||
|
||||
uint8_t protocol_id;
|
||||
uint8_t protocol_id;
|
||||
};
|
||||
|
||||
struct spdk_scsi_lun_desc {
|
||||
@ -115,6 +135,13 @@ struct spdk_scsi_lun {
|
||||
/** Argument for hotremove_cb */
|
||||
void *hotremove_ctx;
|
||||
|
||||
/** Registrant head for I_T nexus */
|
||||
TAILQ_HEAD(, spdk_scsi_pr_registrant) reg_head;
|
||||
/** Persistent Reservation Generation */
|
||||
uint32_t pr_generation;
|
||||
/** Reservation for the LUN */
|
||||
struct spdk_scsi_pr_reservation reservation;
|
||||
|
||||
/** List of open descriptors for this LUN. */
|
||||
TAILQ_HEAD(, spdk_scsi_lun_desc) open_descs;
|
||||
|
||||
@ -174,6 +201,8 @@ void spdk_bdev_scsi_reset(struct spdk_scsi_task *task);
|
||||
bool spdk_scsi_bdev_get_dif_ctx(struct spdk_bdev *bdev, uint8_t *cdb, uint32_t offset,
|
||||
struct spdk_dif_ctx *dif_ctx);
|
||||
|
||||
int spdk_scsi_pr_out(struct spdk_scsi_task *task, uint8_t *cdb, uint8_t *data, uint16_t data_len);
|
||||
|
||||
struct spdk_scsi_globals {
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
261
lib/scsi/scsi_pr.c
Normal file
261
lib/scsi/scsi_pr.c
Normal file
@ -0,0 +1,261 @@
|
||||
/*-
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright (c) Intel Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "scsi_internal.h"
|
||||
|
||||
#include "spdk/endian.h"
|
||||
|
||||
/* Get registrant by I_T nexus */
|
||||
static struct spdk_scsi_pr_registrant *
|
||||
spdk_scsi_pr_get_registrant(struct spdk_scsi_lun *lun,
|
||||
struct spdk_scsi_port *initiator_port,
|
||||
struct spdk_scsi_port *target_port)
|
||||
{
|
||||
struct spdk_scsi_pr_registrant *reg, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(reg, &lun->reg_head, link, tmp) {
|
||||
if (initiator_port == reg->initiator_port &&
|
||||
target_port == reg->target_port) {
|
||||
return reg;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Reservation type is all registrants or not */
|
||||
static inline bool
|
||||
spdk_scsi_pr_is_all_registrants_type(struct spdk_scsi_lun *lun)
|
||||
{
|
||||
return (lun->reservation.rtype == SPDK_SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS ||
|
||||
lun->reservation.rtype == SPDK_SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS);
|
||||
}
|
||||
|
||||
/* Registrant is reservation holder or not */
|
||||
static inline bool
|
||||
spdk_scsi_pr_registrant_is_holder(struct spdk_scsi_lun *lun,
|
||||
struct spdk_scsi_pr_registrant *reg)
|
||||
{
|
||||
if (spdk_scsi_pr_is_all_registrants_type(lun)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (lun->reservation.holder == reg);
|
||||
}
|
||||
|
||||
static int
|
||||
spdk_scsi_pr_register_registrant(struct spdk_scsi_lun *lun,
|
||||
struct spdk_scsi_port *initiator_port,
|
||||
struct spdk_scsi_port *target_port,
|
||||
uint64_t sa_rkey)
|
||||
{
|
||||
struct spdk_scsi_pr_registrant *reg;
|
||||
|
||||
/* Register sa_rkey with the I_T nexus */
|
||||
reg = calloc(1, sizeof(*reg));
|
||||
if (!reg) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: new registrant registered "
|
||||
"with key 0x%"PRIx64"\n", sa_rkey);
|
||||
|
||||
/* New I_T nexus */
|
||||
reg->initiator_port = initiator_port;
|
||||
snprintf(reg->initiator_port_name, sizeof(reg->initiator_port_name), "%s",
|
||||
initiator_port->name);
|
||||
reg->transport_id_len = initiator_port->transport_id_len;
|
||||
memcpy(reg->transport_id, initiator_port->transport_id, reg->transport_id_len);
|
||||
reg->target_port = target_port;
|
||||
snprintf(reg->target_port_name, sizeof(reg->target_port_name), "%s",
|
||||
target_port->name);
|
||||
reg->relative_target_port_id = target_port->index;
|
||||
reg->rkey = sa_rkey;
|
||||
TAILQ_INSERT_TAIL(&lun->reg_head, reg, link);
|
||||
lun->pr_generation++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
spdk_scsi_pr_release_reservation(struct spdk_scsi_lun *lun, struct spdk_scsi_pr_registrant *reg)
|
||||
{
|
||||
bool all_regs = false;
|
||||
|
||||
SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: release reservation "
|
||||
"with type %u\n", lun->reservation.rtype);
|
||||
|
||||
/* TODO: Unit Attention */
|
||||
all_regs = spdk_scsi_pr_is_all_registrants_type(lun);
|
||||
if (all_regs && !TAILQ_EMPTY(&lun->reg_head)) {
|
||||
lun->reservation.holder = TAILQ_FIRST(&lun->reg_head);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&lun->reservation, 0, sizeof(struct spdk_scsi_pr_reservation));
|
||||
}
|
||||
|
||||
static void
|
||||
spdk_scsi_pr_unregister_registrant(struct spdk_scsi_lun *lun,
|
||||
struct spdk_scsi_pr_registrant *reg)
|
||||
{
|
||||
SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: unregister registrant\n");
|
||||
|
||||
TAILQ_REMOVE(&lun->reg_head, reg, link);
|
||||
if (spdk_scsi_pr_registrant_is_holder(lun, reg)) {
|
||||
spdk_scsi_pr_release_reservation(lun, reg);
|
||||
}
|
||||
|
||||
free(reg);
|
||||
lun->pr_generation++;
|
||||
}
|
||||
|
||||
static void
|
||||
spdk_scsi_pr_replace_registrant_key(struct spdk_scsi_lun *lun,
|
||||
struct spdk_scsi_pr_registrant *reg,
|
||||
uint64_t sa_rkey)
|
||||
{
|
||||
SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: replace with new "
|
||||
"reservation key 0x%"PRIx64"\n", sa_rkey);
|
||||
reg->rkey = sa_rkey;
|
||||
lun->pr_generation++;
|
||||
}
|
||||
|
||||
static int
|
||||
spdk_scsi_pr_out_register(struct spdk_scsi_task *task,
|
||||
enum spdk_scsi_pr_out_service_action_code action,
|
||||
uint64_t rkey, uint64_t sa_rkey,
|
||||
uint8_t spec_i_pt, uint8_t all_tg_pt, uint8_t aptpl)
|
||||
{
|
||||
struct spdk_scsi_lun *lun = task->lun;
|
||||
struct spdk_scsi_pr_registrant *reg;
|
||||
int sc, sk, asc;
|
||||
|
||||
SPDK_DEBUGLOG(SPDK_LOG_SCSI, "PR OUT REGISTER: rkey 0x%"PRIx64", "
|
||||
"sa_key 0x%"PRIx64", reservation type %u\n", rkey, sa_rkey, lun->reservation.rtype);
|
||||
|
||||
/* TODO: don't support now */
|
||||
if (spec_i_pt || all_tg_pt || aptpl) {
|
||||
SPDK_ERRLOG("Unsupported spec_i_pt/all_tg_pt/aptpl field\n");
|
||||
sc = SPDK_SCSI_STATUS_CHECK_CONDITION;
|
||||
sk = SPDK_SCSI_SENSE_ILLEGAL_REQUEST;
|
||||
asc = SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB;
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
reg = spdk_scsi_pr_get_registrant(lun, task->initiator_port, task->target_port);
|
||||
/* an unregistered I_T nexus session */
|
||||
if (!reg) {
|
||||
if (rkey && (action == SPDK_SCSI_PR_OUT_REGISTER)) {
|
||||
SPDK_ERRLOG("Reservation key field is not empty\n");
|
||||
sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
|
||||
sk = SPDK_SCSI_SENSE_NO_SENSE;
|
||||
asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
if (!sa_rkey) {
|
||||
/* Do nothing except return GOOD status */
|
||||
SPDK_DEBUGLOG(SPDK_LOG_SCSI, "REGISTER: service action "
|
||||
"reservation key is zero, do noting\n");
|
||||
return 0;
|
||||
}
|
||||
/* Add a new registrant for the I_T nexus */
|
||||
return spdk_scsi_pr_register_registrant(lun, task->initiator_port,
|
||||
task->target_port, sa_rkey);
|
||||
} else {
|
||||
/* a registered I_T nexus */
|
||||
if (rkey != reg->rkey && action == SPDK_SCSI_PR_OUT_REGISTER) {
|
||||
SPDK_ERRLOG("Reservation key 0x%"PRIx64" don't match "
|
||||
"registrant's key 0x%"PRIx64"\n", rkey, reg->rkey);
|
||||
sc = SPDK_SCSI_STATUS_RESERVATION_CONFLICT;
|
||||
sk = SPDK_SCSI_SENSE_NO_SENSE;
|
||||
asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE;
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
if (!sa_rkey) {
|
||||
/* unregister */
|
||||
spdk_scsi_pr_unregister_registrant(lun, reg);
|
||||
} else {
|
||||
/* replace */
|
||||
spdk_scsi_pr_replace_registrant_key(lun, reg, sa_rkey);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_exit:
|
||||
spdk_scsi_task_set_status(task, sc, sk, asc, SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_scsi_pr_out(struct spdk_scsi_task *task,
|
||||
uint8_t *cdb, uint8_t *data,
|
||||
uint16_t data_len)
|
||||
{
|
||||
int rc = -1;
|
||||
uint64_t rkey, sa_rkey;
|
||||
uint8_t spec_i_pt, all_tg_pt, aptpl;
|
||||
enum spdk_scsi_pr_out_service_action_code action;
|
||||
struct spdk_scsi_pr_out_param_list *param = (struct spdk_scsi_pr_out_param_list *)data;
|
||||
|
||||
action = cdb[1] & 0x0f;
|
||||
|
||||
rkey = from_be64(¶m->rkey);
|
||||
sa_rkey = from_be64(¶m->sa_rkey);
|
||||
aptpl = param->aptpl;
|
||||
spec_i_pt = param->spec_i_pt;
|
||||
all_tg_pt = param->all_tg_pt;
|
||||
|
||||
switch (action) {
|
||||
case SPDK_SCSI_PR_OUT_REGISTER:
|
||||
case SPDK_SCSI_PR_OUT_REG_AND_IGNORE_KEY:
|
||||
rc = spdk_scsi_pr_out_register(task, action, rkey, sa_rkey,
|
||||
spec_i_pt, all_tg_pt, aptpl);
|
||||
break;
|
||||
default:
|
||||
SPDK_ERRLOG("Invalid service action code %u\n", action);
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
invalid:
|
||||
spdk_scsi_task_set_status(task, SPDK_SCSI_STATUS_CHECK_CONDITION,
|
||||
SPDK_SCSI_SENSE_ILLEGAL_REQUEST,
|
||||
SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB,
|
||||
SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
|
||||
return -EINVAL;
|
||||
}
|
@ -102,6 +102,9 @@ DEFINE_STUB(spdk_bdev_is_dif_head_of_md, bool,
|
||||
DEFINE_STUB(spdk_bdev_is_dif_check_enabled, bool,
|
||||
(const struct spdk_bdev *bdev, enum spdk_dif_check_type check_type), false);
|
||||
|
||||
DEFINE_STUB(spdk_scsi_pr_out, int, (struct spdk_scsi_task *task,
|
||||
uint8_t *cdb, uint8_t *data, uint16_t data_len), 0);
|
||||
|
||||
void
|
||||
spdk_scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user