From d58bb4614c60cb3b6b634ed821f39de22a368665 Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss@suse.de>
Date: Fri, 11 Sep 2015 11:57:46 +0200
Subject: [PATCH] target/rbd: SCSI3 check conflict support
Patch-mainline: Not yet, SES2 clustered LIO/RBD
References: fate#318836
Check for existing SCSI3 persistent reservations prior to SCSI command
execution.
Signed-off-by: David Disseldorp <ddiss@suse.de>
---
drivers/target/target_core_rbd.c | 96 +++++++++++++++++++++++++++++++++++++++
1 file changed, 96 insertions(+)
--- a/drivers/target/target_core_rbd.c
+++ b/drivers/target/target_core_rbd.c
@@ -2447,7 +2447,103 @@ tcm_rbd_execute_pr_register_and_move(str
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
+static sense_reason_t
+tcm_rbd_execute_pr_check_scsi3_conflict(struct se_cmd *cmd,
+ struct tcm_rbd_pr_info *pr_info,
+ char *it_nexus)
+{
+ struct tcm_rbd_pr_rsv *rsv;
+ struct tcm_rbd_pr_reg *reg;
+ bool registered_nexus;
+ int rc;
+
+ if (!pr_info->rsv) {
+ dout("no SCSI3 persistent reservation\n");
+ return TCM_NO_SENSE;
+ }
+
+ rsv = pr_info->rsv;
+ dout("PR reservation holder: %s, us: %s\n", rsv->it_nexus, it_nexus);
+
+ if (!strncmp(rsv->it_nexus, it_nexus, ARRAY_SIZE(rsv->it_nexus))) {
+ dout("cmd is from reservation holder\n");
+ return TCM_NO_SENSE;
+ }
+
+ registered_nexus = false;
+ list_for_each_entry(reg, &pr_info->regs, regs_node) {
+ if (!strncmp(reg->it_nexus, it_nexus,
+ ARRAY_SIZE(reg->it_nexus))) {
+ dout("cmd is from PR registrant: %s\n", it_nexus);
+ registered_nexus = true;
+ break;
+ }
+ }
+ rc = core_scsi3_pr_seq_non_holder(cmd, rsv->type, it_nexus,
+ registered_nexus);
+ if (rc == 1) {
+ dout("SCSI3 reservation conflict\n");
+ return TCM_RESERVATION_CONFLICT;
+ } else if (rc < 0) {
+ pr_warn("SCSI3 PR non-holder check failed\n");
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+
+ return TCM_NO_SENSE;
+}
+
+static sense_reason_t
+tcm_rbd_execute_pr_check_conflict(struct se_cmd *cmd,
+ enum target_pr_check_type type)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct tcm_rbd_dev *tcm_rbd_dev = TCM_RBD_DEV(dev);
+ struct tcm_rbd_pr_info *pr_info;
+ char nexus_buf[TCM_RBD_PR_IT_NEXUS_MAXLEN];
+ int rc;
+ sense_reason_t ret;
+
+ switch (cmd->t_task_cdb[0]) {
+ case INQUIRY:
+ case RELEASE:
+ case RELEASE_10:
+ /* always allow cdb execution */
+ return TCM_NO_SENSE;
+ default:
+ break;
+ }
+
+ rc = tcm_rbd_pr_info_get(tcm_rbd_dev, &pr_info, NULL, NULL);
+ if (rc == -ENODATA) {
+ dout("no PR info, can't conflict\n");
+ return TCM_NO_SENSE;
+ }
+ if (rc < 0) {
+ /* existing registration required for reservation */
+ pr_err("failed to obtain PR info\n");
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+
+ rc = tcm_rbd_gen_it_nexus(cmd->se_sess, nexus_buf,
+ ARRAY_SIZE(nexus_buf));
+ if (rc < 0) {
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ goto out_info_free;
+ }
+
+ ret = tcm_rbd_execute_pr_check_scsi3_conflict(cmd, pr_info, nexus_buf);
+ if (ret)
+ goto out_info_free;
+
+ ret = TCM_NO_SENSE;
+out_info_free:
+ kfree(pr_info);
+ return ret;
+}
+
static struct target_pr_ops tcm_rbd_pr_ops = {
+ .check_conflict = tcm_rbd_execute_pr_check_conflict,
+
.pr_read_keys = tcm_rbd_execute_pr_read_keys,
.pr_read_reservation = tcm_rbd_execute_pr_read_reservation,
.pr_report_capabilities = tcm_rbd_execute_pr_report_capabilities,