[stgt] [PATCH] persistent reservation support

FUJITA Tomonori fujita.tomonori at lab.ntt.co.jp
Tue Oct 27 15:32:51 CET 2009


On Tue, 27 Oct 2009 17:41:36 +1100
Mark Harvey <markh794 at gmail.com> wrote:

> I'll be testing over the next few days and will advise of any
> success/failures.

Thanks a lot!

I fixed some bugs. Can you try the following latest patch?

=
From: FUJITA Tomonori <fujita.tomonori at lab.ntt.co.jp>
Subject: [PATCH] persistent reservation support

This is the initial try to support persistent reservation

I finished only READ_KEYS, READ_RESERVATION, REPORT_CAPABILITIES, and
REGISTER, RESERVE, RELEASE (and they are not complete).

So I guess that this enables you to only play sg_persist and see if
commands are rejected properly.

Signed-off-by: FUJITA Tomonori <fujita.tomonori at lab.ntt.co.jp>
---
 usr/sbc.c    |   38 +++---
 usr/scc.c    |    2 +-
 usr/scsi.c   |   40 +++++
 usr/scsi.h   |   31 ++++
 usr/smc.c    |    2 +-
 usr/spc.c    |  491 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 usr/spc.h    |    8 +-
 usr/ssc.c    |    2 +-
 usr/target.c |   25 +++
 usr/tgtd.h   |   21 +++
 10 files changed, 629 insertions(+), 31 deletions(-)

diff --git a/usr/sbc.c b/usr/sbc.c
index 710165d..f443508 100644
--- a/usr/sbc.c
+++ b/usr/sbc.c
@@ -284,9 +284,9 @@ static struct device_type_template sbc_template = {
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 
-		{sbc_rw,},
+		{sbc_rw, NULL, PR_EA_FA|PR_EA_FN},
 		{spc_illegal_op,},
-		{sbc_rw,},
+		{sbc_rw, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
@@ -299,14 +299,14 @@ static struct device_type_template sbc_template = {
 		{spc_inquiry,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{sbc_mode_select},
+		{sbc_mode_select, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN},
 		{sbc_reserve,},
 		{sbc_release,},
 
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{spc_mode_sense,},
-		{spc_start_stop,},
+		{spc_mode_sense, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN},
+		{spc_start_stop, NULL, PR_SPECIAL},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
@@ -322,14 +322,14 @@ static struct device_type_template sbc_template = {
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 
-		{sbc_rw},
+		{sbc_rw, NULL, PR_EA_FA|PR_EA_FN},
 		{spc_illegal_op,},
-		{sbc_rw},
+		{sbc_rw, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{sbc_verify,},
+		{sbc_verify, NULL, PR_EA_FA|PR_EA_FN},
 
 		/* 0x30 */
 		{spc_illegal_op,},
@@ -337,7 +337,7 @@ static struct device_type_template sbc_template = {
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{sbc_sync_cache,},
+		{sbc_sync_cache, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 
@@ -358,18 +358,18 @@ static struct device_type_template sbc_template = {
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{sbc_mode_select,},
+		{sbc_mode_select, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{spc_mode_sense,},
-		{spc_illegal_op,},
-		{spc_illegal_op,},
+		{spc_mode_sense, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
+		{spc_service_action, persistent_reserve_in_actions,},
+		{spc_service_action, persistent_reserve_out_actions,},
 
 		[0x60 ... 0x7f] = {spc_illegal_op,},
 
@@ -383,9 +383,9 @@ static struct device_type_template sbc_template = {
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 
-		{sbc_rw,},
+		{sbc_rw, NULL, PR_EA_FA|PR_EA_FN},
 		{spc_illegal_op,},
-		{sbc_rw,},
+		{sbc_rw, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
@@ -394,7 +394,7 @@ static struct device_type_template sbc_template = {
 
 		/* 0x90 */
 		{spc_illegal_op,},
-		{sbc_sync_cache,},
+		{sbc_sync_cache, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
@@ -415,15 +415,15 @@ static struct device_type_template sbc_template = {
 		{spc_report_luns,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{spc_maint_in, maint_in_service_actions,},
+		{spc_service_action, maint_in_service_actions,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 
-		{sbc_rw,},
+		{sbc_rw, NULL, PR_EA_FA|PR_EA_FN},
 		{spc_illegal_op,},
-		{sbc_rw,},
+		{sbc_rw, NULL, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
diff --git a/usr/scc.c b/usr/scc.c
index eed48a0..27bc07b 100644
--- a/usr/scc.c
+++ b/usr/scc.c
@@ -143,7 +143,7 @@ static struct device_type_template scc_template = {
 		{spc_report_luns,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{spc_maint_in, maint_in_service_actions,},
+		{spc_service_action, maint_in_service_actions,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
diff --git a/usr/scsi.c b/usr/scsi.c
index daf4aef..61a2d7e 100644
--- a/usr/scsi.c
+++ b/usr/scsi.c
@@ -183,6 +183,43 @@ uint32_t scsi_rw_count(uint8_t *scb)
 	return cnt;
 }
 
+static int scsi_pr_access_check(struct scsi_cmd *cmd)
+{
+	unsigned char op = cmd->scb[0];
+	uint8_t bits;
+	uint8_t pr_type;
+	struct registration *reg;
+	int conflict = 0;
+
+	if (!cmd->dev->pr_holder)
+		return 0;
+
+	reg = lookup_registration_by_nexus(cmd->dev, cmd->cmd_itn_id);
+
+	if (reg == cmd->dev->pr_holder)
+		return 0;
+
+	pr_type = cmd->dev->pr_holder->pr_type;
+	bits = cmd->dev->dev_type_template.ops[op].pr_conflict_bits;
+
+	if (pr_type == PR_TYPE_WRITE_EXCLUSIVE ||
+	    pr_type == PR_TYPE_EXCLUSIVE_ACCESS)
+		conflict = bits & (PR_WE_FA|PR_EA_FA);
+	else {
+		if (reg)
+			conflict = bits & PR_RR_FR;
+		else {
+			if (pr_type == PR_TYPE_WRITE_EXCLUSIVE_REGONLY ||
+			    pr_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG)
+				conflict = bits & PR_WE_FN;
+			else
+				conflict = bits & PR_EA_FN;
+		}
+	}
+
+	return conflict;
+}
+
 int scsi_cmd_perform(int host_no, struct scsi_cmd *cmd)
 {
 	int ret;
@@ -227,6 +264,9 @@ int scsi_cmd_perform(int host_no, struct scsi_cmd *cmd)
 			return SAM_STAT_CHECK_CONDITION;
 	}
 
+	if (scsi_pr_access_check(cmd))
+		return SAM_STAT_RESERVATION_CONFLICT;
+
 	return cmd->dev->dev_type_template.ops[op].cmd_perform(host_no, cmd);
 }
 
diff --git a/usr/scsi.h b/usr/scsi.h
index 84fadff..9fd78fd 100644
--- a/usr/scsi.h
+++ b/usr/scsi.h
@@ -201,12 +201,14 @@
 #define ASC_INVALID_FIELD_IN_CDB		0x2400
 #define ASC_LUN_NOT_SUPPORTED			0x2500
 #define ASC_INVALID_FIELD_IN_PARMS		0x2600
+#define ASC_INVALID_RELEASE_OF_PERSISTENT_RESERVATION	0x2604
 #define ASC_INCOMPATIBLE_FORMAT			0x3005
 #define ASC_SAVING_PARMS_UNSUP			0x3900
 #define ASC_MEDIUM_DEST_FULL			0x3b0d
 #define ASC_MEDIUM_SRC_EMPTY			0x3b0e
 #define ASC_POSITION_PAST_BOM			0x3b0c
 #define ASC_MEDIUM_REMOVAL_PREVENTED		0x5302
+#define ASC_INSUFFICENT_REGISTRATION_RESOURCES	0x5504
 #define ASC_BAD_MICROCODE_DETECTED		0x8283
 
 /* Key 6: Unit Attention */
@@ -214,6 +216,7 @@
 #define ASC_POWERON_RESET			0x2900
 #define ASC_I_T_NEXUS_LOSS_OCCURRED		0x2907
 #define ASC_MODE_PARAMETERS_CHANGED		0x2a01
+#define ASC_RESERVATIONS_RELEASED		0x2a04
 #define ASC_INSUFFICIENT_TIME_FOR_OPERATION	0x2e00
 #define ASC_COMMANDS_CLEARED_BY_ANOTHOR_INI	0x2f00
 #define ASC_MICROCODE_DOWNLOADED		0x3f01
@@ -225,4 +228,32 @@
 #define ASC_WRITE_PROTECT			0x2700
 #define ASC_MEDIUM_OVERWRITE_ATTEMPTED		0x300c
 
+
+/* PERSISTENT_RESERVE_IN service action codes */
+#define PR_IN_READ_KEYS				0x00
+#define PR_IN_READ_RESERVATION			0x01
+#define PR_IN_REPORT_CAPABILITIES		0x02
+#define PR_IN_READ_FULL_STATUS			0x03
+
+/* PERSISTENT_RESERVE_OUT service action codes */
+#define PR_OUT_REGISTER				0x00
+#define PR_OUT_RESERVE				0x01
+#define PR_OUT_RELEASE				0x02
+#define PR_OUT_CLEAR				0x03
+#define PR_OUT_PREEMPT				0x04
+#define PR_OUT_PREEMPT_AND_ABORT		0x05
+#define PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY	0x06
+#define PR_OUT_REGISTER_AND_MOVE		0x07
+
+/* Persistent Reservation scope */
+#define PR_LU_SCOPE				0x00
+
+/* Persistent Reservation Type Mask format */
+#define PR_TYPE_WRITE_EXCLUSIVE			0x01
+#define PR_TYPE_EXCLUSIVE_ACCESS		0x03
+#define PR_TYPE_WRITE_EXCLUSIVE_REGONLY		0x05
+#define PR_TYPE_EXCLUSIVE_ACCESS_REGONLY	0x06
+#define PR_TYPE_WRITE_EXCLUSIVE_ALLREG		0x07
+#define PR_TYPE_EXCLUSIVE_ACCESS_ALLREG		0x08
+
 #endif
diff --git a/usr/smc.c b/usr/smc.c
index ab36e9c..6430882 100644
--- a/usr/smc.c
+++ b/usr/smc.c
@@ -861,7 +861,7 @@ struct device_type_template smc_template = {
 		{spc_report_luns,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{spc_maint_in, maint_in_service_actions,},
+		{spc_service_action, maint_in_service_actions,},
 		{spc_illegal_op,},
 		{smc_move_medium,},
 		{spc_illegal_op,},
diff --git a/usr/spc.c b/usr/spc.c
index 1a75766..e396746 100644
--- a/usr/spc.c
+++ b/usr/spc.c
@@ -720,7 +720,7 @@ struct service_action maint_in_service_actions[] = {
 	{0, NULL}
 };
 
-struct service_action *
+static struct service_action *
 find_service_action(struct service_action *service_action, uint32_t action)
 {
 	while (service_action->cmd_perform) {
@@ -731,16 +731,20 @@ find_service_action(struct service_action *service_action, uint32_t action)
 	return NULL;
 }
 
-/**
- * This functions emulates the various commands using the 0xa3 cdb opcode
+/*
+ * This is useful for the various commands using the SERVICE ACTION
+ * format.
  */
-int spc_maint_in(int host_no, struct scsi_cmd *cmd)
+int spc_service_action(int host_no, struct scsi_cmd *cmd)
 {
 	uint8_t action;
-	struct service_action *service_action;
+	unsigned char op = cmd->scb[0];
+	struct service_action *service_action, *actions;
 
 	action = cmd->scb[1] & 0x1f;
-	service_action = find_service_action(maint_in_service_actions, action);
+	actions = cmd->dev->dev_type_template.ops[op].service_actions;
+
+	service_action = find_service_action(actions, action);
 
 	if (!service_action) {
 		scsi_set_in_resid_by_actual(cmd, 0);
@@ -752,6 +756,480 @@ int spc_maint_in(int host_no, struct scsi_cmd *cmd)
 	return service_action->cmd_perform(host_no, cmd);
 }
 
+static int spc_pr_read_keys(int host_no, struct scsi_cmd *cmd)
+{
+	uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
+	uint8_t key = ILLEGAL_REQUEST;
+	struct registration *reg;
+	uint16_t len;
+	uint32_t keys;
+	uint8_t *buf;
+	int off;
+
+	len = get_unaligned_be16(cmd->scb + 7);
+	if (len < 8)
+		goto sense;
+
+	if (scsi_get_in_length(cmd) < len)
+		goto sense;
+
+	buf = scsi_get_in_buffer(cmd);
+	memset(buf, 0, len);
+
+	len &= ~(8 - 1);
+	keys = 0;
+	off = 8;
+
+	put_unaligned_be32(cmd->dev->prgeneration, &buf[0]);
+
+	list_for_each_entry(reg, &cmd->dev->registration_list,
+			    registration_siblings) {
+
+		if (!len)
+			continue;
+		put_unaligned_be64(reg->key, &buf[off]);
+
+		len -= 8;
+		off += 8;
+		keys++;
+	}
+
+	put_unaligned_be32(keys * 8, &buf[4]);
+
+	scsi_set_in_resid_by_actual(cmd, keys * 8 + 8);
+
+	return SAM_STAT_GOOD;
+sense:
+	scsi_set_in_resid_by_actual(cmd, 0);
+	sense_data_build(cmd, key, asc);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int spc_pr_read_reservation(int host_no, struct scsi_cmd *cmd)
+{
+	uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
+	uint8_t key = ILLEGAL_REQUEST;
+	struct registration *reg;
+	uint16_t len;
+	uint8_t *buf;
+	uint64_t res_key;
+
+	reg = cmd->dev->pr_holder;
+
+	if (reg)
+		len = 24;
+	else
+		len = 8;
+
+	if (get_unaligned_be16(cmd->scb + 7) < len)
+		goto sense;
+
+	if (scsi_get_in_length(cmd) < len)
+		goto sense;
+
+	buf = scsi_get_in_buffer(cmd);
+	memset(buf, 0, len);
+
+	put_unaligned_be32(cmd->dev->prgeneration, &buf[0]);
+
+	if (reg) {
+		put_unaligned_be32(16, &buf[4]);
+
+		if (reg->pr_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG ||
+		    reg->pr_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG)
+			res_key = 0;
+		else
+			res_key = reg->key;
+
+		put_unaligned_be32(res_key, &buf[8]);
+		buf[21] = ((reg->pr_scope << 4) & 0xf0) | (reg->pr_type & 0x0f);
+	} else
+		put_unaligned_be32(0, &buf[4]);
+
+	scsi_set_in_resid_by_actual(cmd, len);
+
+	return SAM_STAT_GOOD;
+sense:
+	scsi_set_in_resid_by_actual(cmd, 0);
+	sense_data_build(cmd, key, asc);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int spc_pr_report_capabilities(int host_no, struct scsi_cmd *cmd)
+{
+	uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
+	uint8_t key = ILLEGAL_REQUEST;
+	uint8_t *buf;
+	uint16_t len;
+
+	len = get_unaligned_be16(cmd->scb + 7);
+	if (len < 8)
+		goto sense;
+
+	if (scsi_get_in_length(cmd) < len)
+		goto sense;
+
+	buf = scsi_get_in_buffer(cmd);
+
+	len = 8;
+
+	memset(buf, 0, len);
+
+	put_unaligned_be16(len, &buf[0]);
+
+	/* we don't set any capability for now */
+
+	/* Persistent Reservation Type Mask format */
+	buf[4] |= 0x80; /* PR_TYPE_EXCLUSIVE_ACCESS_ALLREG */
+	buf[4] |= 0x40; /* PR_TYPE_EXCLUSIVE_ACCESS_REGONLY */
+	buf[4] |= 0x20; /* PR_TYPE_WRITE_EXCLUSIVE_REGONLY */
+	buf[4] |= 0x08; /* PR_TYPE_EXCLUSIVE_ACCESS */
+	buf[4] |= 0x02; /* PR_TYPE_WRITE_EXCLUSIVE */
+	buf[5] |= 0x01; /* PR_TYPE_EXCLUSIVE_ACCESS_ALLREG */
+
+	return SAM_STAT_GOOD;
+sense:
+	scsi_set_in_resid_by_actual(cmd, 0);
+	sense_data_build(cmd, key, asc);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int spc_pr_read_full_status(int host_no, struct scsi_cmd *cmd)
+{
+	uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
+	uint8_t key = ILLEGAL_REQUEST;
+	struct registration *reg;
+	uint16_t len;
+	uint32_t keys;
+	uint8_t *buf;
+	int off;
+
+	len = get_unaligned_be16(cmd->scb + 7);
+	if (len < 8)
+		goto sense;
+	if (scsi_get_in_length(cmd) < len)
+		goto sense;
+
+	buf = scsi_get_in_buffer(cmd);
+	memset(buf, 0, len);
+
+	put_unaligned_be32(cmd->dev->prgeneration, &buf[0]);
+
+	len &= ~(24 - 1);
+	off = 8;
+	keys = 0;
+
+	list_for_each_entry(reg, &cmd->dev->registration_list,
+			    registration_siblings) {
+
+		if (!len)
+			continue;
+
+		put_unaligned_be64(reg->key, &buf[off + 0]);
+
+		/* TODO */
+
+		put_unaligned_be32(0, &buf[off + 20]);
+
+		keys++;
+		len -= 24;
+		off += 24;
+	}
+
+	put_unaligned_be32(keys * 24, &buf[4]);
+
+	scsi_set_in_resid_by_actual(cmd, keys * 24 + 8);
+
+	return SAM_STAT_GOOD;
+sense:
+	scsi_set_in_resid_by_actual(cmd, 0);
+	sense_data_build(cmd, key, asc);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+struct service_action persistent_reserve_in_actions[] = {
+	{PR_IN_READ_KEYS, spc_pr_read_keys},
+	{PR_IN_READ_RESERVATION, spc_pr_read_reservation},
+	{PR_IN_REPORT_CAPABILITIES, spc_pr_report_capabilities},
+	{PR_IN_READ_FULL_STATUS, spc_pr_read_full_status},
+	{0, NULL},
+};
+
+struct registration *lookup_registration_by_nexus(struct scsi_lu *lu,
+						  uint64_t nexus_id)
+{
+	struct registration *reg;
+
+	list_for_each_entry(reg, &lu->registration_list, registration_siblings) {
+		if (reg->nexus_id == nexus_id)
+			return reg;
+	}
+
+	return NULL;
+}
+
+static int check_pr_out_basic_parameter(struct scsi_cmd *cmd)
+{
+	uint8_t spec_i_pt, all_tg_pt, aptpl;
+	uint8_t *buf;
+	uint16_t len = 24;
+
+	if (get_unaligned_be16(cmd->scb + 7) < len)
+		return 1;
+
+	if (scsi_get_out_length(cmd) < len)
+		return 1;
+
+	buf = scsi_get_out_buffer(cmd);
+
+	spec_i_pt = buf[20] & (1U << 3);
+	all_tg_pt = buf[20] & (1U << 2);
+	aptpl = buf[20] & (1U << 0);
+
+	if (spec_i_pt | all_tg_pt | aptpl) {
+		/*
+		 * for now, we say that we don't support these bits
+		 * via REPORT CAPABILITIES.
+		 */
+		return 1;
+	}
+
+	return 0;
+}
+
+static int spc_pr_register(int host_no, struct scsi_cmd *cmd)
+{
+	uint8_t force, key = ILLEGAL_REQUEST;
+	uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
+	uint64_t res_key, sa_res_key;
+	int ret;
+	uint8_t *buf;
+	struct registration *reg;
+
+	force = ((cmd->scb[1] & 0x1f) == PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY);
+
+	ret = check_pr_out_basic_parameter(cmd);
+	if (ret)
+		goto sense;
+
+	buf = scsi_get_out_buffer(cmd);
+
+	res_key = get_unaligned_be64(buf);
+	sa_res_key = get_unaligned_be64(buf + 8);
+
+	reg = lookup_registration_by_nexus(cmd->dev, cmd->cmd_itn_id);
+	if (reg) {
+		if (force || reg->key == res_key) {
+			if (sa_res_key)
+				reg->key = sa_res_key;
+			else {
+				/* unregister */
+				list_del(&reg->registration_siblings);
+				free(reg);
+			}
+		} else
+			return SAM_STAT_RESERVATION_CONFLICT;
+	} else {
+		if (force || !res_key) {
+			if (sa_res_key) {
+				reg = zalloc(sizeof(*reg));
+				if (!reg) {
+					key = ILLEGAL_REQUEST;
+					asc = ASC_INSUFFICENT_REGISTRATION_RESOURCES;
+					goto sense;
+				}
+
+				reg->key = sa_res_key;
+				reg->nexus_id = cmd->cmd_itn_id;
+				list_add_tail(&reg->registration_siblings,
+					      &cmd->dev->registration_list);
+			} else
+				; /* do nothing */
+		} else
+			return SAM_STAT_RESERVATION_CONFLICT;
+	}
+
+	cmd->dev->prgeneration++;
+
+	return SAM_STAT_GOOD;
+sense:
+	scsi_set_in_resid_by_actual(cmd, 0);
+	sense_data_build(cmd, key, asc);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int spc_pr_reserve(int host_no, struct scsi_cmd *cmd)
+{
+	uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
+	uint8_t key = ILLEGAL_REQUEST;
+	uint8_t pr_scope, pr_type;
+	uint8_t *buf;
+	uint64_t res_key, sa_res_key;
+	int ret;
+	struct registration *reg, *holder;
+
+	ret = check_pr_out_basic_parameter(cmd);
+	if (ret)
+		goto sense;
+
+	pr_scope = (cmd->scb[2] & 0xf0) >> 4;
+	pr_type = cmd->scb[2] & 0x0f;
+
+	buf = scsi_get_out_buffer(cmd);
+
+	res_key = get_unaligned_be64(buf);
+	sa_res_key = get_unaligned_be64(buf + 8);
+
+	switch (pr_type) {
+	case PR_TYPE_WRITE_EXCLUSIVE:
+	case PR_TYPE_EXCLUSIVE_ACCESS:
+	case PR_TYPE_WRITE_EXCLUSIVE_REGONLY:
+	case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY:
+	case PR_TYPE_WRITE_EXCLUSIVE_ALLREG:
+	case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG:
+		break;
+	default:
+		goto sense;
+	}
+
+	if (pr_scope != PR_LU_SCOPE)
+		goto sense;
+
+	reg = lookup_registration_by_nexus(cmd->dev, cmd->cmd_itn_id);
+	if (!reg)
+		return SAM_STAT_RESERVATION_CONFLICT;
+
+	holder = cmd->dev->pr_holder;
+	if (holder) {
+		if (holder != reg)
+			return SAM_STAT_RESERVATION_CONFLICT;
+
+		if (holder->pr_type != pr_type ||
+		    holder->pr_scope != pr_scope)
+			return SAM_STAT_RESERVATION_CONFLICT;
+
+		return SAM_STAT_GOOD;
+	}
+
+	reg->pr_scope = pr_scope;
+	reg->pr_type = pr_type;
+	cmd->dev->pr_holder = reg;
+
+	return SAM_STAT_GOOD;
+sense:
+	scsi_set_in_resid_by_actual(cmd, 0);
+	sense_data_build(cmd, key, asc);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int spc_pr_release(int host_no, struct scsi_cmd *cmd)
+{
+	uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
+	uint8_t key = ILLEGAL_REQUEST;
+	uint8_t pr_scope, pr_type;
+	uint8_t *buf;
+	uint64_t res_key, sa_res_key;
+	int ret;
+	struct registration *reg, *holder, *sibling;
+
+	ret = check_pr_out_basic_parameter(cmd);
+	if (ret)
+		goto sense;
+
+	pr_scope = (cmd->scb[2] & 0xf0) >> 4;
+	pr_type = cmd->scb[2] & 0x0f;
+
+	buf = scsi_get_out_buffer(cmd);
+
+	res_key = get_unaligned_be64(buf);
+	sa_res_key = get_unaligned_be64(buf + 8);
+
+	reg = lookup_registration_by_nexus(cmd->dev, cmd->cmd_itn_id);
+	if (!reg)
+		return SAM_STAT_RESERVATION_CONFLICT;
+
+	holder = cmd->dev->pr_holder;
+	if (!holder)
+		return SAM_STAT_GOOD;
+
+	if (holder != reg)
+		return SAM_STAT_GOOD;
+
+	if (res_key != reg->key)
+		return SAM_STAT_RESERVATION_CONFLICT;
+
+	if (reg->pr_scope != pr_scope || reg->pr_type != pr_type) {
+		asc = ASC_INVALID_RELEASE_OF_PERSISTENT_RESERVATION;
+		goto sense;
+	}
+
+	cmd->dev->pr_holder = NULL;
+	reg->pr_scope = 0;
+	reg->pr_type = 0;
+
+	switch (pr_type) {
+	case PR_TYPE_WRITE_EXCLUSIVE_REGONLY:
+	case PR_TYPE_EXCLUSIVE_ACCESS_REGONLY:
+	case PR_TYPE_WRITE_EXCLUSIVE_ALLREG:
+	case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG:
+		return SAM_STAT_GOOD;
+	default:
+		;
+	}
+
+	list_for_each_entry(sibling, &cmd->dev->registration_list,
+			    registration_siblings) {
+		/* we don't send myself */
+		if (sibling == reg)
+			continue;
+
+		ua_sense_add_other_it_nexus(sibling->nexus_id,
+					    cmd->dev, ASC_RESERVATIONS_RELEASED);
+	}
+
+	return SAM_STAT_GOOD;
+sense:
+	scsi_set_in_resid_by_actual(cmd, 0);
+	sense_data_build(cmd, key, asc);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+#if 0
+static int spc_pr_clear(int host_no, struct scsi_cmd *cmd)
+{
+	return SAM_STAT_GOOD;
+}
+
+static int spc_pr_preempt(int host_no, struct scsi_cmd *cmd)
+{
+	return SAM_STAT_GOOD;
+}
+
+static int spc_pr_preempt_and_abort(int host_no, struct scsi_cmd *cmd)
+{
+	return SAM_STAT_GOOD;
+}
+
+static int spc_pr_register_and_move(int host_no, struct scsi_cmd *cmd)
+{
+	return SAM_STAT_GOOD;
+}
+#endif
+
+struct service_action persistent_reserve_out_actions[] = {
+	{PR_OUT_REGISTER, spc_pr_register},
+	{PR_OUT_RESERVE, spc_pr_reserve},
+	{PR_OUT_RELEASE, spc_pr_release},
+#if 0
+	{PR_OUT_CLEAR, spc_pr_clear},
+	{PR_OUT_PREEMPT, spc_pr_preempt},
+	{PR_OUT_PREEMPT_AND_ABORT, spc_pr_preempt_and_abort},
+	{PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY, spc_pr_register},
+	{PR_OUT_REGISTER_AND_MOVE, spc_pr_register_and_move},
+#endif
+	{0, NULL},
+};
+
 int spc_request_sense(int host_no, struct scsi_cmd *cmd)
 {
 	uint8_t *data;
@@ -1065,4 +1543,3 @@ void spc_lu_exit(struct scsi_lu *lu)
 		if (lu->mode_pgs[i])
 			free(lu->mode_pgs[i]);
 }
-
diff --git a/usr/spc.h b/usr/spc.h
index cfc9cf3..a731b94 100644
--- a/usr/spc.h
+++ b/usr/spc.h
@@ -1,8 +1,10 @@
 #ifndef __SPC_H
 #define __SPC_H
 
-extern struct service_action maint_in_service_actions[];
-extern int spc_maint_in(int host_no, struct scsi_cmd *cmd);
+extern struct service_action maint_in_service_actions[],
+	persistent_reserve_in_actions[], persistent_reserve_out_actions[];
+
+extern int spc_service_action(int host_no, struct scsi_cmd *cmd);
 extern int spc_inquiry(int host_no, struct scsi_cmd *cmd);
 extern int spc_report_luns(int host_no, struct scsi_cmd *cmd);
 extern int spc_start_stop(int host_no, struct scsi_cmd *cmd);
@@ -27,4 +29,6 @@ extern struct vpd *alloc_vpd(uint16_t size);
 extern int spc_lu_online(struct scsi_lu *lu);
 extern int spc_lu_offline(struct scsi_lu *lu);
 
+struct registration *lookup_registration_by_nexus(struct scsi_lu *lu,
+						  uint64_t nexus_id);
 #endif
diff --git a/usr/ssc.c b/usr/ssc.c
index 834ec16..84fa317 100644
--- a/usr/ssc.c
+++ b/usr/ssc.c
@@ -310,7 +310,7 @@ static struct device_type_template ssc_template = {
 		{spc_report_luns,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
-		{spc_maint_in, maint_in_service_actions,},
+		{spc_service_action, maint_in_service_actions,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
 		{spc_illegal_op,},
diff --git a/usr/target.c b/usr/target.c
index 14e3632..65d9d91 100644
--- a/usr/target.c
+++ b/usr/target.c
@@ -288,6 +288,8 @@ int it_nexus_destroy(int tid, uint64_t itn_id)
 {
 	int i;
 	struct it_nexus *itn;
+	struct scsi_lu *lu;
+	struct registration *reg, *n;
 
 	dprintf("%d %" PRIu64 "\n", tid, itn_id);
 
@@ -299,6 +301,19 @@ int it_nexus_destroy(int tid, uint64_t itn_id)
 		if (!list_empty(&itn->cmd_hash_list[i]))
 			return -EBUSY;
 
+	list_for_each_entry(lu, &itn->nexus_target->device_list,
+			    device_siblings) {
+		list_for_each_entry_safe(reg, n, &lu->registration_list,
+					 registration_siblings) {
+
+			if (reg->nexus_id == itn->itn_id) {
+				list_del(&reg->registration_siblings);
+				free(reg);
+				break;
+			}
+		}
+	}
+
 	it_nexus_del_lu_info(itn);
 
 	list_del(&itn->nexus_siblings);
@@ -490,6 +505,9 @@ int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params,
 	lu->tgt = target;
 	lu->lun = lun;
 	tgt_cmd_queue_init(&lu->cmd_queue);
+	INIT_LIST_HEAD(&lu->registration_list);
+	lu->prgeneration = 0;
+	lu->pr_holder = NULL;
 
  	if (lu->dev_type_template.lu_init) {
 		ret = lu->dev_type_template.lu_init(lu);
@@ -567,6 +585,7 @@ int tgt_device_destroy(int tid, uint64_t lun, int force)
 	struct scsi_lu *lu;
 	struct it_nexus *itn;
 	struct it_nexus_lu_info *itn_lu, *next;
+	struct registration *reg, *reg_next;
 	int ret;
 
 	dprintf("%u %" PRIu64 "\n", tid, lun);
@@ -606,6 +625,12 @@ int tgt_device_destroy(int tid, uint64_t lun, int force)
 	}
 
 	list_del(&lu->device_siblings);
+
+	list_for_each_entry_safe(reg, reg_next, &lu->registration_list,
+				 registration_siblings) {
+		free(reg);
+	}
+
 	free(lu);
 
 	list_for_each_entry(itn, &target->it_nexus_list, nexus_siblings) {
diff --git a/usr/tgtd.h b/usr/tgtd.h
index febaed7..96c744f 100644
--- a/usr/tgtd.h
+++ b/usr/tgtd.h
@@ -90,8 +90,16 @@ struct service_action {
 struct device_type_operations {
 	int (*cmd_perform)(int host_no, struct scsi_cmd *cmd);
 	struct service_action *service_actions;
+	uint8_t pr_conflict_bits;
 };
 
+#define PR_SPECIAL	(1U << 5)
+#define PR_WE_FA	(1U << 4)
+#define PR_EA_FA	(1U << 3)
+#define PR_RR_FR	(1U << 2)
+#define PR_WE_FN	(1U << 1)
+#define PR_EA_FN	(1U << 0)
+
 struct device_type_template {
 	unsigned char type;
 
@@ -126,6 +134,15 @@ struct mode_pg {
 	uint8_t mode_data[0];	/* Rest of mode page info */
 };
 
+struct registration {
+	uint64_t key;
+	uint64_t nexus_id;
+	struct list_head registration_siblings;
+
+	uint8_t pr_scope;
+	uint8_t pr_type;
+};
+
 struct scsi_lu {
 	int fd;
 	uint64_t addr; /* persistent mapped address */
@@ -152,6 +169,10 @@ struct scsi_lu {
 
 	struct lu_phy_attr attrs;
 
+	struct list_head registration_list;
+	uint32_t prgeneration;
+	struct registration *pr_holder;
+
 	/* A pointer for each modules private use.
 	 * Currently used by ssc, smc and mmc modules.
 	 */
-- 
1.5.6.5


--
To unsubscribe from this list: send the line "unsubscribe stgt" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the stgt mailing list