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