From 92a55813f0d48e4277d5d65705a19af547bd430b Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss@suse.de>
Date: Tue, 11 Aug 2015 01:46:08 +0200
Subject: [PATCH] target/pr: add backend API for reservation handling
Patch-mainline: Not yet, SES2 clustered LIO/RBD
References: fate#318836
With Persistent Reservation support moving to the block layer, it makes
sense to allow for backend modules (such as iblock) to handle
reservation requests directly.
This change only adds a new target_pr_ops entry to target_backend_ops,
offering hooks for:
- PR registration
- PR reserve/release
- PR clear
- PR preempt
- PR report capabilities
- PR read keys
- PR read reservation
- SCSI2 reserve/release
- SCSI2 reset reservation
- reservation conflict checks
Signed-off-by: David Disseldorp <ddiss@suse.de>
[ddiss: rebase atop 63ce3c384db2 ("scsi: target: Fix truncated PR-in
ReadKeys response")]
---
drivers/target/target_core_pr.c | 483 +++++++++++++++++++++++++++--------
drivers/target/target_core_tmr.c | 23 +
include/target/target_core_backend.h | 40 ++
3 files changed, 432 insertions(+), 114 deletions(-)
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -198,16 +198,14 @@ static int target_check_scsi2_reservatio
return 0;
}
-sense_reason_t
-target_scsi2_reservation_release(struct se_cmd *cmd)
+static sense_reason_t
+target_scsi2_reservation_release_execute(struct se_cmd *cmd)
{
struct se_device *dev = cmd->se_dev;
struct se_session *sess = cmd->se_sess;
struct se_portal_group *tpg;
int rc;
- if (!sess || !sess->se_tpg)
- goto out;
rc = target_check_scsi2_reservation_conflict(cmd);
if (rc == 1)
goto out;
@@ -240,30 +238,38 @@ target_scsi2_reservation_release(struct
out_unlock:
spin_unlock(&dev->dev_reservation_lock);
out:
- target_complete_cmd(cmd, GOOD);
return 0;
}
sense_reason_t
-target_scsi2_reservation_reserve(struct se_cmd *cmd)
+target_scsi2_reservation_release(struct se_cmd *cmd)
{
struct se_device *dev = cmd->se_dev;
struct se_session *sess = cmd->se_sess;
- struct se_portal_group *tpg;
sense_reason_t ret = 0;
- int rc;
- if ((cmd->t_task_cdb[1] & 0x01) &&
- (cmd->t_task_cdb[1] & 0x02)) {
- pr_err("LongIO and Obsolete Bits set, returning ILLEGAL_REQUEST\n");
- return TCM_UNSUPPORTED_SCSI_OPCODE;
- }
- /*
- * This is currently the case for target_core_mod passthrough struct se_cmd
- * ops
- */
if (!sess || !sess->se_tpg)
goto out;
+
+ if (dev->transport->pr_ops && dev->transport->pr_ops->scsi2_release)
+ ret = dev->transport->pr_ops->scsi2_release(cmd);
+ else
+ ret = target_scsi2_reservation_release_execute(cmd);
+out:
+ if (!ret)
+ target_complete_cmd(cmd, GOOD);
+ return ret;
+}
+
+static sense_reason_t
+target_scsi2_reservation_reserve_execute(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
+ struct se_portal_group *tpg;
+ sense_reason_t ret = 0;
+ int rc;
+
rc = target_check_scsi2_reservation_conflict(cmd);
if (rc == 1)
goto out;
@@ -275,7 +281,7 @@ target_scsi2_reservation_reserve(struct
spin_lock(&dev->dev_reservation_lock);
if (dev->dev_reserved_node_acl &&
(dev->dev_reserved_node_acl != sess->se_node_acl)) {
- pr_err("SCSI-2 RESERVATION CONFLIFT for %s fabric\n",
+ pr_err("SCSI-2 RESERVATION CONFLICT for %s fabric\n",
tpg->se_tpg_tfo->get_fabric_name());
pr_err("Original reserver LUN: %llu %s\n",
cmd->se_lun->unpacked_lun,
@@ -302,12 +308,39 @@ target_scsi2_reservation_reserve(struct
out_unlock:
spin_unlock(&dev->dev_reservation_lock);
out:
+ return ret;
+}
+
+sense_reason_t
+target_scsi2_reservation_reserve(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct se_session *sess = cmd->se_sess;
+ sense_reason_t ret = 0;
+
+ if ((cmd->t_task_cdb[1] & 0x01) &&
+ (cmd->t_task_cdb[1] & 0x02)) {
+ pr_err("LongIO and Obselete Bits set, returning"
+ " ILLEGAL_REQUEST\n");
+ return TCM_UNSUPPORTED_SCSI_OPCODE;
+ }
+ /*
+ * This is currently the case for target_core_mod passthrough struct se_cmd
+ * ops
+ */
+ if (!sess || !sess->se_tpg)
+ goto out;
+
+ if (dev->transport->pr_ops && dev->transport->pr_ops->scsi2_reserve)
+ ret = dev->transport->pr_ops->scsi2_reserve(cmd);
+ else
+ ret = target_scsi2_reservation_reserve_execute(cmd);
+out:
if (!ret)
target_complete_cmd(cmd, GOOD);
return ret;
}
-
/*
* Begin SPC-3/SPC-4 Persistent Reservations emulation support
*
@@ -2050,8 +2083,10 @@ retry:
}
static sense_reason_t
-core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key,
- bool aptpl, bool all_tg_pt, bool spec_i_pt, enum register_type register_type)
+core_scsi3_emulate_pro_register_execute(struct se_cmd *cmd, u64 res_key,
+ u64 sa_res_key, bool aptpl,
+ bool all_tg_pt, bool spec_i_pt,
+ enum register_type register_type)
{
struct se_session *se_sess = cmd->se_sess;
struct se_device *dev = cmd->se_dev;
@@ -2252,6 +2287,32 @@ out:
return ret;
}
+static sense_reason_t
+core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key,
+ bool aptpl, bool all_tg_pt, bool spec_i_pt,
+ enum register_type register_type)
+{
+ struct se_device *dev = cmd->se_dev;
+ sense_reason_t ret;
+
+ if (dev->transport->pr_ops && dev->transport->pr_ops->pr_register) {
+ bool ignore_existing;
+ if (register_type == REGISTER_AND_IGNORE_EXISTING_KEY)
+ ignore_existing = true;
+ else
+ ignore_existing = false;
+ ret = dev->transport->pr_ops->pr_register(cmd, res_key,
+ sa_res_key, aptpl, all_tg_pt, spec_i_pt,
+ ignore_existing);
+ } else {
+ ret = core_scsi3_emulate_pro_register_execute(cmd,
+ res_key, sa_res_key, aptpl, all_tg_pt, spec_i_pt,
+ register_type);
+ }
+
+ return ret;
+}
+
unsigned char *core_scsi3_pr_dump_type(int type)
{
switch (type) {
@@ -2318,21 +2379,6 @@ core_scsi3_pro_reserve(struct se_cmd *cm
goto out_put_pr_reg;
}
/*
- * From spc4r17 Section 5.7.9: Reserving:
- *
- * From above:
- * b) TYPE field and SCOPE field set to the persistent reservation
- * being created.
- *
- * Only one persistent reservation is allowed at a time per logical unit
- * and that persistent reservation has a scope of LU_SCOPE.
- */
- if (scope != PR_SCOPE_LU_SCOPE) {
- pr_err("SPC-3 PR: Illegal SCOPE: 0x%02x\n", scope);
- ret = TCM_INVALID_PARAMETER_LIST;
- goto out_put_pr_reg;
- }
- /*
* See if we have an existing PR reservation holder pointer at
* struct se_device->dev_pr_res_holder in the form struct t10_pr_registration
* *pr_res_holder.
@@ -2434,6 +2480,9 @@ static sense_reason_t
core_scsi3_emulate_pro_reserve(struct se_cmd *cmd, int type, int scope,
u64 res_key)
{
+ struct se_device *dev = cmd->se_dev;
+ sense_reason_t ret;
+
switch (type) {
case PR_TYPE_WRITE_EXCLUSIVE:
case PR_TYPE_EXCLUSIVE_ACCESS:
@@ -2441,7 +2490,30 @@ core_scsi3_emulate_pro_reserve(struct se
case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY:
case PR_TYPE_WRITE_EXCLUSIVE_ALLREG:
case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG:
- return core_scsi3_pro_reserve(cmd, type, scope, res_key);
+ /*
+ * From spc4r17 Section 5.7.9: Reserving:
+ *
+ * From above:
+ * b) TYPE field and SCOPE field set to the persistent
+ * reservation being created.
+ *
+ * Only one persistent reservation is allowed at a time per
+ * logical unit and that persistent reservation has a scope of
+ * LU_SCOPE.
+ */
+ if (scope != PR_SCOPE_LU_SCOPE) {
+ pr_err("SPC-3 PR: Illegal SCOPE: 0x%02x\n", scope);
+ return TCM_INVALID_PARAMETER_LIST;
+ }
+
+ if (dev->transport->pr_ops
+ && dev->transport->pr_ops->pr_reserve) {
+ ret = dev->transport->pr_ops->pr_reserve(cmd, type,
+ res_key);
+ } else {
+ ret = core_scsi3_pro_reserve(cmd, type, scope, res_key);
+ }
+ return ret;
default:
pr_err("SPC-3 PR: Unknown Service Action RESERVE Type:"
" 0x%02x\n", type);
@@ -2519,7 +2591,7 @@ out:
}
static sense_reason_t
-core_scsi3_emulate_pro_release(struct se_cmd *cmd, int type, int scope,
+core_scsi3_emulate_pro_release_execute(struct se_cmd *cmd, int type, int scope,
u64 res_key)
{
struct se_device *dev = cmd->se_dev;
@@ -2676,7 +2748,27 @@ out_put_pr_reg:
}
static sense_reason_t
-core_scsi3_emulate_pro_clear(struct se_cmd *cmd, u64 res_key)
+core_scsi3_emulate_pro_release(struct se_cmd *cmd, int type, int scope,
+ u64 res_key)
+{
+ struct se_device *dev = cmd->se_dev;
+ sense_reason_t ret;
+
+ if (dev->transport->pr_ops && dev->transport->pr_ops->pr_release) {
+ if (scope != PR_SCOPE_LU_SCOPE) {
+ pr_err("SPC-3 PR: Illegal SCOPE: 0x%02x\n", scope);
+ return TCM_INVALID_PARAMETER_LIST;
+ }
+ ret = dev->transport->pr_ops->pr_release(cmd, type, res_key);
+ } else {
+ ret = core_scsi3_emulate_pro_release_execute(cmd, type, scope,
+ res_key);
+ }
+ return ret;
+}
+
+static sense_reason_t
+core_scsi3_emulate_pro_clear_execute(struct se_cmd *cmd, u64 res_key)
{
struct se_device *dev = cmd->se_dev;
struct se_node_acl *pr_reg_nacl;
@@ -2707,7 +2799,7 @@ core_scsi3_emulate_pro_clear(struct se_c
* that is registered with the logical unit for the I_T nexus.
*/
if (res_key != pr_reg_n->pr_res_key) {
- pr_err("SPC-3 PR REGISTER: Received"
+ pr_err("SPC-3 PR CLEAR: Received"
" res_key: 0x%016Lx does not match"
" existing SA REGISTER res_key:"
" 0x%016Lx\n", res_key, pr_reg_n->pr_res_key);
@@ -2759,6 +2851,20 @@ core_scsi3_emulate_pro_clear(struct se_c
return 0;
}
+static sense_reason_t
+core_scsi3_emulate_pro_clear(struct se_cmd *cmd, u64 res_key)
+{
+ struct se_device *dev = cmd->se_dev;
+ sense_reason_t ret;
+
+ if (dev->transport->pr_ops && dev->transport->pr_ops->pr_clear) {
+ ret = dev->transport->pr_ops->pr_clear(cmd, res_key);
+ } else {
+ ret = core_scsi3_emulate_pro_clear_execute(cmd, res_key);
+ }
+ return ret;
+}
+
/*
* Called with struct se_device->dev_reservation_lock held.
*/
@@ -3128,6 +3234,9 @@ static sense_reason_t
core_scsi3_emulate_pro_preempt(struct se_cmd *cmd, int type, int scope,
u64 res_key, u64 sa_res_key, enum preempt_type preempt_type)
{
+ struct se_device *dev = cmd->se_dev;
+ sense_reason_t ret;
+
switch (type) {
case PR_TYPE_WRITE_EXCLUSIVE:
case PR_TYPE_EXCLUSIVE_ACCESS:
@@ -3135,8 +3244,26 @@ core_scsi3_emulate_pro_preempt(struct se
case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY:
case PR_TYPE_WRITE_EXCLUSIVE_ALLREG:
case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG:
- return core_scsi3_pro_preempt(cmd, type, scope, res_key,
- sa_res_key, preempt_type);
+ if (dev->transport->pr_ops
+ && dev->transport->pr_ops->pr_preempt) {
+ bool abort;
+ if (preempt_type == PREEMPT_AND_ABORT)
+ abort = true;
+ else
+ abort = false;
+
+ if (scope != PR_SCOPE_LU_SCOPE) {
+ pr_err("SPC-3 PR: Illegal SCOPE: 0x%02x\n", scope);
+ return TCM_INVALID_PARAMETER_LIST;
+ }
+ ret = dev->transport->pr_ops->pr_preempt(cmd, res_key,
+ sa_res_key,
+ type, abort);
+ } else {
+ ret = core_scsi3_pro_preempt(cmd, type, scope, res_key,
+ sa_res_key, preempt_type);
+ }
+ return ret;
default:
pr_err("SPC-3 PR: Unknown Service Action PREEMPT%s"
" Type: 0x%02x\n", (preempt_type == PREEMPT_AND_ABORT) ? "_AND_ABORT" : "", type);
@@ -3144,9 +3271,8 @@ core_scsi3_emulate_pro_preempt(struct se
}
}
-
static sense_reason_t
-core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
+core_scsi3_emulate_pro_register_and_move_execute(struct se_cmd *cmd, u64 res_key,
u64 sa_res_key, int aptpl, int unreg)
{
struct se_session *se_sess = cmd->se_sess;
@@ -3547,6 +3673,26 @@ out_put_pr_reg:
return ret;
}
+static sense_reason_t
+core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
+ u64 sa_res_key, int aptpl, int unreg)
+{
+ struct se_device *dev = cmd->se_dev;
+ sense_reason_t ret;
+
+ if (dev->transport->pr_ops
+ && dev->transport->pr_ops->pr_register_and_move) {
+ ret = dev->transport->pr_ops->pr_register_and_move(cmd, res_key,
+ sa_res_key, aptpl, unreg);
+ } else {
+ ret = core_scsi3_emulate_pro_register_and_move_execute(cmd,
+ res_key, sa_res_key, aptpl, unreg);
+ }
+
+ return ret;
+}
+
+
/*
* See spc4r17 section 6.14 Table 170
*/
@@ -3570,7 +3716,13 @@ target_scsi3_emulate_pr_out(struct se_cm
* initiator or service action and shall terminate with a RESERVATION
* CONFLICT status.
*/
- if (cmd->se_dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS) {
+ if (dev->transport->pr_ops && dev->transport->pr_ops->check_conflict) {
+ ret = dev->transport->pr_ops->check_conflict(cmd,
+ TARGET_PR_CHECK_SCSI2_ANY);
+ if (ret) {
+ return ret;
+ }
+ } else if (cmd->se_dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS) {
pr_err("Received PERSISTENT_RESERVE CDB while legacy"
" SPC-2 reservation is held, returning"
" RESERVATION_CONFLICT\n");
@@ -3696,29 +3848,14 @@ target_scsi3_emulate_pr_out(struct se_cm
return ret;
}
-/*
- * PERSISTENT_RESERVE_IN Service Action READ_KEYS
- *
- * See spc4r17 section 5.7.6.2 and section 6.13.2, Table 160
- */
static sense_reason_t
-core_scsi3_pri_read_keys(struct se_cmd *cmd)
+core_scsi3_pri_read_keys_execute(struct se_cmd *cmd, unsigned char *buf,
+ u32 buf_len)
{
struct se_device *dev = cmd->se_dev;
struct t10_pr_registration *pr_reg;
- unsigned char *buf;
u32 add_len = 0, off = 8;
- if (cmd->data_length < 8) {
- pr_err("PRIN SA READ_KEYS SCSI Data Length: %u"
- " too small\n", cmd->data_length);
- return TCM_INVALID_CDB_FIELD;
- }
-
- buf = transport_kmap_data_sg(cmd);
- if (!buf)
- return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
-
put_unaligned_be32(dev->t10_pr.pr_generation, buf);
spin_lock(&dev->t10_pr.registration_lock);
@@ -3728,7 +3865,7 @@ core_scsi3_pri_read_keys(struct se_cmd *
* Check for overflow of 8byte PRI READ_KEYS payload and
* next reservation key list descriptor.
*/
- if (off + 8 <= cmd->data_length) {
+ if (off + 8 <= buf_len) {
put_unaligned_be64(pr_reg->pr_res_key, &buf[off]);
off += 8;
}
@@ -3744,27 +3881,23 @@ core_scsi3_pri_read_keys(struct se_cmd *
put_unaligned_be32(add_len, &buf[4]);
- transport_kunmap_data_sg(cmd);
-
- return 0;
+ return TCM_NO_SENSE;
}
/*
- * PERSISTENT_RESERVE_IN Service Action READ_RESERVATION
+ * PERSISTENT_RESERVE_IN Service Action READ_KEYS
*
- * See spc4r17 section 5.7.6.3 and section 6.13.3.2 Table 161 and 162
+ * See spc4r17 section 5.7.6.2 and section 6.13.2, Table 160
*/
static sense_reason_t
-core_scsi3_pri_read_reservation(struct se_cmd *cmd)
+core_scsi3_pri_read_keys(struct se_cmd *cmd)
{
struct se_device *dev = cmd->se_dev;
- struct t10_pr_registration *pr_reg;
unsigned char *buf;
- u64 pr_res_key;
- u32 add_len = 16; /* Hardcoded to 16 when a reservation is held. */
+ sense_reason_t ret;
if (cmd->data_length < 8) {
- pr_err("PRIN SA READ_RESERVATIONS SCSI Data Length: %u"
+ pr_err("PRIN SA READ_KEYS SCSI Data Length: %u"
" too small\n", cmd->data_length);
return TCM_INVALID_CDB_FIELD;
}
@@ -3773,6 +3906,33 @@ core_scsi3_pri_read_reservation(struct s
if (!buf)
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ if (dev->transport->pr_ops && dev->transport->pr_ops->pr_read_keys) {
+ ret = dev->transport->pr_ops->pr_read_keys(cmd, buf,
+ cmd->data_length);
+ } else {
+ ret = core_scsi3_pri_read_keys_execute(cmd, buf,
+ cmd->data_length);
+ }
+ if (ret)
+ goto err_unmap;
+
+ ret = TCM_NO_SENSE;
+err_unmap:
+ transport_kunmap_data_sg(cmd);
+
+ return ret;
+}
+
+static sense_reason_t
+core_scsi3_pri_read_reservation_execute(struct se_cmd *cmd, unsigned char *buf,
+ u32 buf_len)
+
+{
+ struct se_device *dev = cmd->se_dev;
+ struct t10_pr_registration *pr_reg;
+ u64 pr_res_key;
+ u32 add_len = 16; /* Hardcoded to 16 when a reservation is held. */
+
put_unaligned_be32(dev->t10_pr.pr_generation, &buf[0]);
spin_lock(&dev->dev_reservation_lock);
@@ -3783,7 +3943,7 @@ core_scsi3_pri_read_reservation(struct s
*/
put_unaligned_be32(add_len, &buf[4]);
- if (cmd->data_length < 22)
+ if (buf_len < 22)
goto err;
/*
@@ -3818,27 +3978,25 @@ core_scsi3_pri_read_reservation(struct s
err:
spin_unlock(&dev->dev_reservation_lock);
- transport_kunmap_data_sg(cmd);
return 0;
}
/*
- * PERSISTENT_RESERVE_IN Service Action REPORT_CAPABILITIES
+ * PERSISTENT_RESERVE_IN Service Action READ_RESERVATION
*
- * See spc4r17 section 6.13.4 Table 165
+ * See spc4r17 section 5.7.6.3 and section 6.13.3.2 Table 161 and 162
*/
static sense_reason_t
-core_scsi3_pri_report_capabilities(struct se_cmd *cmd)
+core_scsi3_pri_read_reservation(struct se_cmd *cmd)
{
struct se_device *dev = cmd->se_dev;
- struct t10_reservation *pr_tmpl = &dev->t10_pr;
unsigned char *buf;
- u16 add_len = 8; /* Hardcoded to 8. */
+ sense_reason_t ret;
- if (cmd->data_length < 6) {
- pr_err("PRIN SA REPORT_CAPABILITIES SCSI Data Length:"
- " %u too small\n", cmd->data_length);
+ if (cmd->data_length < 8) {
+ pr_err("PRIN SA READ_RESERVATIONS SCSI Data Length: %u"
+ " too small\n", cmd->data_length);
return TCM_INVALID_CDB_FIELD;
}
@@ -3846,8 +4004,34 @@ core_scsi3_pri_report_capabilities(struc
if (!buf)
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ if (dev->transport->pr_ops
+ && dev->transport->pr_ops->pr_read_reservation) {
+ ret = dev->transport->pr_ops->pr_read_reservation(cmd, buf,
+ cmd->data_length);
+ } else {
+ ret = core_scsi3_pri_read_reservation_execute(cmd, buf,
+ cmd->data_length);
+ }
+ if (ret)
+ goto err_unmap;
+
+ ret = TCM_NO_SENSE;
+err_unmap:
+ transport_kunmap_data_sg(cmd);
+
+ return ret;
+}
+
+static sense_reason_t
+core_scsi3_pri_report_capabilities_execute(struct se_cmd *cmd,
+ unsigned char *buf, u32 buf_len)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct t10_reservation *pr_tmpl = &dev->t10_pr;
+ u16 add_len = 8; /* Hardcoded to 8. */
+
put_unaligned_be16(add_len, &buf[0]);
- buf[2] |= 0x10; /* CRH: Compatible Reservation Hanlding bit. */
+ buf[2] |= 0x10; /* CRH: Compatible Reservation Handling bit. */
buf[2] |= 0x08; /* SIP_C: Specify Initiator Ports Capable bit */
buf[2] |= 0x04; /* ATP_C: All Target Ports Capable bit */
buf[2] |= 0x01; /* PTPL_C: Persistence across Target Power Loss bit */
@@ -3857,9 +4041,9 @@ core_scsi3_pri_report_capabilities(struc
*/
buf[3] |= 0x80;
/*
- * Change ALLOW COMMANDs to 0x20 or 0x40 later from Table 166
+ * Change ALLOW COMMANDS to 0x20 or 0x40 later from Table 166
*/
- buf[3] |= 0x10; /* ALLOW COMMANDs field 001b */
+ buf[3] |= 0x10; /* ALLOW COMMANDS field 001b */
/*
* PTPL_A: Persistence across Target Power Loss Active bit
*/
@@ -3875,9 +4059,47 @@ core_scsi3_pri_report_capabilities(struc
buf[4] |= 0x02; /* PR_TYPE_WRITE_EXCLUSIVE */
buf[5] |= 0x01; /* PR_TYPE_EXCLUSIVE_ACCESS_ALLREG */
+ return TCM_NO_SENSE;
+}
+
+/*
+ * PERSISTENT_RESERVE_IN Service Action REPORT_CAPABILITIES
+ *
+ * See spc4r17 section 6.13.4 Table 165
+ */
+static sense_reason_t
+core_scsi3_pri_report_capabilities(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ unsigned char *buf;
+ sense_reason_t ret;
+
+ if (cmd->data_length < 6) {
+ pr_err("PRIN SA REPORT_CAPABILITIES SCSI Data Length:"
+ " %u too small\n", cmd->data_length);
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ if (dev->transport->pr_ops
+ && dev->transport->pr_ops->pr_report_capabilities) {
+ ret = dev->transport->pr_ops->pr_report_capabilities(cmd, buf,
+ cmd->data_length);
+ } else {
+ ret = core_scsi3_pri_report_capabilities_execute(cmd, buf,
+ cmd->data_length);
+ }
+ if (ret)
+ goto err_unmap;
+
+ ret = TCM_NO_SENSE;
+err_unmap:
transport_kunmap_data_sg(cmd);
- return 0;
+ return ret;
}
/*
@@ -3886,29 +4108,21 @@ core_scsi3_pri_report_capabilities(struc
* See spc4r17 section 6.13.5 Table 168 and 169
*/
static sense_reason_t
-core_scsi3_pri_read_full_status(struct se_cmd *cmd)
+core_scsi3_pri_read_full_status_execute(struct se_cmd *cmd, unsigned char *buf,
+ u32 buf_len)
{
struct se_device *dev = cmd->se_dev;
struct se_node_acl *se_nacl;
struct se_portal_group *se_tpg;
struct t10_pr_registration *pr_reg, *pr_reg_tmp;
struct t10_reservation *pr_tmpl = &dev->t10_pr;
- unsigned char *buf;
u32 add_desc_len = 0, add_len = 0;
u32 off = 8; /* off into first Full Status descriptor */
int format_code = 0, pr_res_type = 0, pr_res_scope = 0;
int exp_desc_len, desc_len;
bool all_reg = false;
- if (cmd->data_length < 8) {
- pr_err("PRIN SA READ_FULL_STATUS SCSI Data Length: %u"
- " too small\n", cmd->data_length);
- return TCM_INVALID_CDB_FIELD;
- }
-
- buf = transport_kmap_data_sg(cmd);
- if (!buf)
- return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ BUG_ON(buf_len < 8);
put_unaligned_be32(dev->t10_pr.pr_generation, &buf[0]);
@@ -3942,9 +4156,9 @@ core_scsi3_pri_read_full_status(struct s
exp_desc_len = target_get_pr_transport_id_len(se_nacl, pr_reg,
&format_code);
if (exp_desc_len < 0 ||
- exp_desc_len + add_len > cmd->data_length) {
+ exp_desc_len + add_len > buf_len) {
pr_warn("SPC-3 PRIN READ_FULL_STATUS ran"
- " out of buffer: %d\n", cmd->data_length);
+ " out of buffer: %d\n", buf_len);
spin_lock(&pr_tmpl->registration_lock);
atomic_dec_mb(&pr_reg->pr_res_holders);
break;
@@ -4036,14 +4250,48 @@ core_scsi3_pri_read_full_status(struct s
*/
put_unaligned_be32(add_len, &buf[4]);
+ return 0;
+}
+
+static sense_reason_t
+core_scsi3_pri_read_full_status(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ unsigned char *buf;
+ sense_reason_t ret;
+
+ if (cmd->data_length < 8) {
+ pr_err("PRIN SA READ_FULL_STATUS SCSI Data Length: %u"
+ " too small\n", cmd->data_length);
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ buf = transport_kmap_data_sg(cmd);
+ if (!buf)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ if (dev->transport->pr_ops
+ && dev->transport->pr_ops->pr_read_full_status) {
+ ret = dev->transport->pr_ops->pr_read_full_status(cmd, buf,
+ cmd->data_length);
+ } else {
+ ret = core_scsi3_pri_read_full_status_execute(cmd, buf,
+ cmd->data_length);
+ }
+ if (ret)
+ goto err_unmap;
+
+ ret = TCM_NO_SENSE;
+err_unmap:
transport_kunmap_data_sg(cmd);
- return 0;
+ return ret;
}
sense_reason_t
target_scsi3_emulate_pr_in(struct se_cmd *cmd)
{
+ struct se_device *dev = cmd->se_dev;
sense_reason_t ret;
/*
@@ -4055,7 +4303,13 @@ target_scsi3_emulate_pr_in(struct se_cmd
* initiator or service action and shall terminate with a RESERVATION
* CONFLICT status.
*/
- if (cmd->se_dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS) {
+ if (dev->transport->pr_ops && dev->transport->pr_ops->check_conflict) {
+ ret = dev->transport->pr_ops->check_conflict(cmd,
+ TARGET_PR_CHECK_SCSI2_ANY);
+ if (ret) {
+ return ret;
+ }
+ } else if (cmd->se_dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS) {
pr_err("Received PERSISTENT_RESERVE CDB while legacy"
" SPC-2 reservation is held, returning"
" RESERVATION_CONFLICT\n");
@@ -4086,6 +4340,22 @@ target_scsi3_emulate_pr_in(struct se_cmd
return ret;
}
+static sense_reason_t
+target_check_reservation_execute(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ sense_reason_t ret;
+
+ spin_lock(&dev->dev_reservation_lock);
+ if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
+ ret = target_scsi2_reservation_check(cmd);
+ else
+ ret = target_scsi3_pr_reservation_check(cmd);
+ spin_unlock(&dev->dev_reservation_lock);
+
+ return ret;
+}
+
sense_reason_t
target_check_reservation(struct se_cmd *cmd)
{
@@ -4101,12 +4371,11 @@ target_check_reservation(struct se_cmd *
if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH_PGR)
return 0;
- spin_lock(&dev->dev_reservation_lock);
- if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
- ret = target_scsi2_reservation_check(cmd);
+ if (dev->transport->pr_ops && dev->transport->pr_ops->check_conflict)
+ ret = dev->transport->pr_ops->check_conflict(cmd,
+ TARGET_PR_CHECK_SCSI2_SCSI3);
else
- ret = target_scsi3_pr_reservation_check(cmd);
- spin_unlock(&dev->dev_reservation_lock);
+ ret = target_check_reservation_execute(cmd);
return ret;
}
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -424,13 +424,22 @@ int core_tmr_lun_reset(
* Clear any legacy SPC-2 reservation when called during
* LOGICAL UNIT RESET
*/
- if (!preempt_and_abort_list &&
- (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)) {
- spin_lock(&dev->dev_reservation_lock);
- dev->dev_reserved_node_acl = NULL;
- dev->dev_reservation_flags &= ~DRF_SPC2_RESERVATIONS;
- spin_unlock(&dev->dev_reservation_lock);
- pr_debug("LUN_RESET: SCSI-2 Released reservation\n");
+ if (!preempt_and_abort_list) {
+ if (dev->transport->pr_ops && dev->transport->pr_ops->reset) {
+ sense_reason_t ret;
+
+ ret = dev->transport->pr_ops->reset(dev);
+ if (ret != TCM_NO_SENSE) {
+ pr_err("LUN_RESET: failed to release "
+ "reservations: %u\n", ret);
+ }
+ } else if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS) {
+ spin_lock(&dev->dev_reservation_lock);
+ dev->dev_reserved_node_acl = NULL;
+ dev->dev_reservation_flags &= ~DRF_SPC2_RESERVATIONS;
+ spin_unlock(&dev->dev_reservation_lock);
+ pr_debug("LUN_RESET: SCSI-2 Released reservation\n");
+ }
}
atomic_long_inc(&dev->num_resets);
--- a/include/target/target_core_backend.h
+++ b/include/target/target_core_backend.h
@@ -16,6 +16,43 @@
struct request_queue;
struct scatterlist;
+enum target_pr_check_type {
+ /* check for *any* SCSI2 reservations, including own */
+ TARGET_PR_CHECK_SCSI2_ANY,
+ /* check for conflicting SCSI2 or SCSI3 reservation */
+ TARGET_PR_CHECK_SCSI2_SCSI3,
+};
+
+struct target_pr_ops {
+ sense_reason_t (*check_conflict)(struct se_cmd *cmd,
+ enum target_pr_check_type);
+ sense_reason_t (*scsi2_reserve)(struct se_cmd *cmd);
+ sense_reason_t (*scsi2_release)(struct se_cmd *cmd);
+ sense_reason_t (*reset)(struct se_device *dev);
+ /* persistent reservation (out) API attempts to mirror block layer */
+ sense_reason_t (*pr_register)(struct se_cmd *cmd, u64 old_key,
+ u64 new_key, bool aptpl, bool all_tg_pt,
+ bool spec_i_pt, bool ignore_existing);
+ sense_reason_t (*pr_reserve)(struct se_cmd *cmd, int type, u64 key);
+ sense_reason_t (*pr_release)(struct se_cmd *cmd, int type, u64 key);
+ sense_reason_t (*pr_clear)(struct se_cmd *cmd, u64 key);
+ sense_reason_t (*pr_preempt)(struct se_cmd *cmd, u64 old_key,
+ u64 new_key, int type, bool abort);
+ sense_reason_t (*pr_register_and_move)(struct se_cmd *cmd, u64 old_key,
+ u64 new_key, bool aptpl,
+ int unreg);
+ /* persistent reservation (in) API not proposed for block layer yet */
+ sense_reason_t (*pr_read_keys)(struct se_cmd *cmd, unsigned char *buf,
+ u32 buf_len);
+ sense_reason_t (*pr_read_reservation)(struct se_cmd *cmd,
+ unsigned char *buf, u32 buf_len);
+ sense_reason_t (*pr_report_capabilities)(struct se_cmd *cmd,
+ unsigned char *buf,
+ u32 buf_len);
+ sense_reason_t (*pr_read_full_status)(struct se_cmd *cmd,
+ unsigned char *buf, u32 buf_len);
+};
+
struct target_backend_ops {
char name[16];
char inquiry_prod[16];
@@ -51,6 +88,9 @@ struct target_backend_ops {
int (*format_prot)(struct se_device *);
void (*free_prot)(struct se_device *);
+ /* backend reservation hooks */
+ struct target_pr_ops *pr_ops;
+
struct configfs_attribute **tb_dev_attrib_attrs;
struct configfs_attribute **tb_dev_action_attrs;
};