This patch updates the SCSI-3 Persistent Reservation code to add support
for reservations of type=7 (Write Excslusive, All Registrants) and type=8
(Exclusive Access, All Registrants), as these newer reservation types
are needed by some types of clustering software.

Existing bahvavior for other reservation types does not change.

Target tested on SLES 11 SP2, with one initiator on the same system, and
one on a OpenSUSE 12.1 system (both open-iscsi).

Tested:
 - REGISTER [and unregister]
 - REPORT CAPABILITIES
 - READ FULL STATUS
 - RESERVE
 - RELEASE
 - CLEAR
 - PREEMPT

Signed-of-by: Lee Duncan <lduncan@suse.com>

 persist.c |  115 +++++++++++++++++++++++++++++++++++++-------------------------
 persist.h |    7 +++
 volume.c  |    2 +
 3 files changed, 79 insertions(+), 45 deletions(-)

Index: trunk/kernel/persist.h
===================================================================
--- trunk/kernel/persist.h	(revision 470)
+++ trunk/kernel/persist.h	(working copy)
@@ -224,6 +224,13 @@ pr_is_reserved(const struct reservation* res)
 	return res->reservation_type != RESERVATION_TYPE_NONE;
 }
 
+static inline bool
+pr_type_is_all_registrants(const struct reservation *res)
+{
+	return ((res->persistent_type == PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS) ||
+		(res->persistent_type == PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS));
+}
+
 struct iscsi_session;
 
 bool
Index: trunk/kernel/volume.c
===================================================================
--- trunk/kernel/volume.c	(revision 470)
+++ trunk/kernel/volume.c	(working copy)
@@ -420,9 +420,11 @@ int is_volume_reserved(struct iet_volume *volume,
 		excl_access = true;
 		break;
 	case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY:
+	case PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS:
 		write_excl_ro = true;
 		break;
 	case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY:
+	case PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS:
 		excl_access_ro = true;
 		break;
 	default:
Index: trunk/kernel/persist.c
===================================================================
--- trunk/kernel/persist.c	(revision 470)
+++ trunk/kernel/persist.c	(working copy)
@@ -9,21 +9,28 @@
 #include "iscsi_dbg.h"
 #include "persist.h"
 
-bool
-pr_is_reserved_by_session(const struct reservation *res,
-			  const struct iscsi_session *sess)
-{
-	return (pr_is_reserved(res) && res->sid == sess->sid);
-}
-
 static bool
 pr_is_reserved_by_sid(const struct reservation *res,
 		      const u64 sid)
 {
-	return (pr_is_reserved(res) && res->sid == sid);
+	if (!pr_is_reserved(res))
+		return false;
+
+	if (pr_type_is_all_registrants(res)) {
+		return pr_initiator_has_registered(res, sid);
+	} else {
+		return res->sid == sid;
+	}
 }
 
 bool
+pr_is_reserved_by_session(const struct reservation *res,
+			  const struct iscsi_session *sess)
+{
+	return pr_is_reserved_by_sid(res, sess->sid);
+}
+
+bool
 pr_initiator_has_registered(const struct reservation *res,
 			    u64 sid)
 {
@@ -44,7 +51,9 @@ static const struct pr_in_report_capabilities_data
 	.type_mask = cpu_to_be16(PR_TYPE_WR_EX|
 				 PR_TYPE_EX_AC|
 				 PR_TYPE_WR_EX_RO|
-				 PR_TYPE_EX_AC_RO),
+				 PR_TYPE_EX_AC_RO|
+				 PR_TYPE_WR_EX_AR|
+				 PR_TYPE_EX_AC_AR),
 };
 
 static void
@@ -79,7 +88,10 @@ pr_in_read_reservation(struct iscsi_cmnd *cmnd,
 	pin_data->generation = cpu_to_be32(reservation->generation);
 
 	if (pr_is_reserved(reservation)) {
-		pin_data->reservation_key = reservation->reservation_key;
+		if (pr_type_is_all_registrants(reservation))
+			pin_data->reservation_key = 0;
+		else
+			pin_data->reservation_key = reservation->reservation_key;
 		pin_data->scope_type = PR_SCOPE_LU | reservation->persistent_type;
 		/*
 		 * SPC-3, 6.11.3.2
@@ -350,7 +362,8 @@ pr_out_register(struct iscsi_cmnd *cmnd, bool igno
 		}
 
 		if (!param->service_action_key) {
-			if (pr_is_reserved_by_session(reservation, session)) {
+			if (pr_is_reserved_by_session(reservation, session) &&
+			    !pr_type_is_all_registrants(reservation)) {
 				reservation->reservation_type = RESERVATION_TYPE_NONE;
 				reservation->persistent_type = 0;
 				reservation->reservation_key = 0;
@@ -361,6 +374,12 @@ pr_out_register(struct iscsi_cmnd *cmnd, bool igno
 			}
 			list_del(&reg->r_list);
 			kfree(reg);
+			if (list_empty(&reservation->registration_list) &&
+			    pr_type_is_all_registrants(reservation)) {
+				reservation->reservation_type = RESERVATION_TYPE_NONE;
+				reservation->persistent_type = 0;
+				reservation->reservation_key = 0;
+			}
 		} else {
 			reg->reservation_key = param->service_action_key;
 		}
@@ -410,6 +429,8 @@ persistent_type_valid(int type)
 	case PR_TYPE_EXCLUSIVE_ACCESS:
 	case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY:
 	case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY:
+	case PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS:
+	case PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS:
 		return true;
 	default:
 		return false;
@@ -500,17 +521,20 @@ pr_out_release(struct iscsi_cmnd *cmnd,
 		goto out;
 	}
 
-	if (reservation->sid != session->sid)
-		goto out;
-
-	if (reservation->reservation_key != param->reservation_key) {
-		cmnd->status = SAM_STAT_RESERVATION_CONFLICT;
-		goto out;
+	if (!pr_type_is_all_registrants(reservation)) {
+		if (reservation->sid != session->sid)
+			goto out;
+		if (reservation->reservation_key != param->reservation_key) {
+			cmnd->status = SAM_STAT_RESERVATION_CONFLICT;
+			goto out;
+		}
 	}
 
 	switch (reservation->persistent_type) {
 	case PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY:
 	case PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY:
+	case PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS:
+	case PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS:
 		send_ua = true;
 		break;
 	default:
@@ -601,54 +625,55 @@ pr_out_preempt(struct iscsi_cmnd *cmnd,
 	struct reservation *reservation = &volume->reservation;
 	bool all = 0;
 
-	if (!param->service_action_key) {
+	spin_lock(&volume->reserve_lock);
+	if (!param->service_action_key &&
+	    !pr_type_is_all_registrants(reservation)) {
 		iscsi_cmnd_set_sense(cmnd,
 				     ILLEGAL_REQUEST,
 				     INVALID_FIELD_IN_PARAMETER_LIST_ASC,
 				     INVALID_FIELD_IN_PARAMETER_LIST_ASCQ);
-		return;
+		goto out;
 	}
 
-	spin_lock(&volume->reserve_lock);
 	registered = pr_initiator_has_registered(reservation, session->sid);
 	if (!registered) {
 		cmnd->status = SAM_STAT_RESERVATION_CONFLICT;
 		goto out;
 	}
 
-	if (pr_is_reserved(reservation) &&
-	    reservation->reservation_key == param->service_action_key &&
-	    reservation->sid != session->sid) {
-		if (!persistent_type_valid(pr_type)) {
-			iscsi_cmnd_set_sense(cmnd,
-					     ILLEGAL_REQUEST,
-					     INVALID_FIELD_IN_CDB_ASC,
-					     INVALID_FIELD_IN_CDB_ASCQ);
-			goto out;
-		}
+	if (pr_is_reserved(reservation)) {
+		if ((!pr_type_is_all_registrants(reservation) &&
+		     reservation->reservation_key == param->service_action_key &&
+		     reservation->sid != session->sid) ||
+		    (pr_type_is_all_registrants(reservation) &&
+		     !param->service_action_key)) {
 
-		reserv_session = session_lookup(target, reservation->sid);
-		if (reserv_session) {
-			if (abort)
-				session_abort_tasks(reserv_session, volume->lun);
-			ua_establish_for_session(reserv_session,
-						 volume->lun,
-						 RESERVATIONS_PREEMPTED_ASC,
-						 RESERVATIONS_PREEMPTED_ASCQ);
+			reserv_session = session_lookup(target, reservation->sid);
+			if (reserv_session) {
+				if (abort)
+					session_abort_tasks(reserv_session,
+					    volume->lun);
+				ua_establish_for_session(reserv_session,
+							 volume->lun,
+							 RESERVATIONS_PREEMPTED_ASC,
+							 RESERVATIONS_PREEMPTED_ASCQ);
+			}
+
+			reservation->reservation_type = RESERVATION_TYPE_PERSISTENT;
+			reservation->sid = session->sid;
+			reservation->reservation_key = param->reservation_key;
+			reservation->persistent_type = pr_type;
+			all = true;
 		}
-
-		reservation->reservation_type = RESERVATION_TYPE_PERSISTENT;
-		reservation->sid = session->sid;
-		reservation->reservation_key = param->reservation_key;
-		reservation->persistent_type = pr_type;
-		all = true;
 	}
 
 	list_for_each_entry_safe(reg, tmp_reg, &reservation->registration_list, r_list) {
 		if (reg->sid == session->sid)
 			continue;
 
-		if (!all && reg->reservation_key != param->service_action_key)
+		if (!all &&
+		    reg->reservation_key != param->service_action_key &&
+		    !pr_type_is_all_registrants(reservation))
 			continue;
 
 		reserv_session = session_lookup(target, reg->sid);
