Blob Blame History Raw
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,