changeset 5044:366fa13eb032

6415440 iSCSI target needs persistent reserve support
author jdunham
date Thu, 13 Sep 2007 15:17:59 -0700
parents 667e437d66d0
children 75a798a98460
files deleted_files/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.c deleted_files/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.h deleted_files/usr/src/uts/common/sys/scsi/generic/persist.h usr/src/cmd/iscsi/iscsitgtd/Makefile usr/src/cmd/iscsi/iscsitgtd/Makefile.com usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.c usr/src/cmd/iscsi/iscsitgtd/main.c usr/src/cmd/iscsi/iscsitgtd/queue.h usr/src/cmd/iscsi/iscsitgtd/t10.h usr/src/cmd/iscsi/iscsitgtd/t10_raw_if.c usr/src/cmd/iscsi/iscsitgtd/t10_sam.c usr/src/cmd/iscsi/iscsitgtd/t10_sbc.c usr/src/cmd/iscsi/iscsitgtd/t10_sbc.h usr/src/cmd/iscsi/iscsitgtd/t10_spc.c usr/src/cmd/iscsi/iscsitgtd/t10_spc.h usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.c usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.h usr/src/cmd/iscsi/iscsitgtd/target.h usr/src/cmd/iscsi/iscsitgtd/util_queue.c usr/src/lib/libiscsitgt/common/iscsitgt_impl.h usr/src/pkgdefs/SUNWhea/prototype_com usr/src/uts/common/sys/Makefile usr/src/uts/common/sys/scsi/generic/commands.h usr/src/uts/common/sys/scsi/generic/persist.h
diffstat 24 files changed, 2869 insertions(+), 2770 deletions(-) [+]
line wrap: on
line diff
--- a/deleted_files/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.c	Thu Sep 13 13:50:11 2007 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1913 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-/*
- * []------------------------------------------------------------------[]
- * | Implementation of SPC-3 Persistent Reserve emulation		|
- * []------------------------------------------------------------------[]
- */
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/asynch.h>
-#include <sys/param.h>
-#include <sys/sysmacros.h>
-#include <strings.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <assert.h>
-
-#include <sys/scsi/generic/sense.h>
-#include <sys/scsi/generic/status.h>
-#include <sys/scsi/generic/inquiry.h>
-#include <sys/scsi/generic/mode.h>
-#include <sys/scsi/generic/commands.h>
-#include <sys/scsi/generic/persist.h>
-
-#include "t10.h"
-#include "t10_spc.h"
-#include "t10_spc_pr.h"
-#include "t10_sbc.h"
-#include "target.h"
-
-/*
- * External declarations
- */
-void spc_free(emul_handle_t id);
-void sbc_cmd(t10_cmd_t *, uint8_t *, size_t);
-void sbc_cmd_reserved(t10_cmd_t *, uint8_t *, size_t);
-
-extern target_queue_t *mgmtq;
-
-/*
- * Forward declarations
- */
-static
-    spc_pr_rsrv_t *spc_pr_rsrv_find(scsi3_pgr_t *, uint64_t, uint64_t, char *);
-static
-    spc_pr_rsrv_t *spc_pr_rsrv_alloc(scsi3_pgr_t *, uint64_t, uint64_t, char *,
-    uint8_t, uint8_t);
-static
-    spc_pr_key_t *spc_pr_key_find(scsi3_pgr_t *, uint64_t, uint64_t, char *);
-static
-    spc_pr_key_t *spc_pr_key_alloc(scsi3_pgr_t *, uint64_t, uint64_t, char *);
-
-static void spc_pr_rsrv_release(t10_cmd_t *, scsi3_pgr_t *, spc_pr_rsrv_t *);
-static void spc_pr_key_free(scsi3_pgr_t *, spc_pr_key_t *);
-static void spc_pr_rsrv_free(scsi3_pgr_t *, spc_pr_rsrv_t *);
-static void spc_pr_erase(scsi3_pgr_t *);
-static void spc_pr_key_rsrv_init(scsi3_pgr_t *);
-
-static int spc_pr_register(t10_cmd_t *, void *, size_t);
-static int spc_pr_reserve(t10_cmd_t *, void *, size_t);
-static int spc_pr_release(t10_cmd_t *, void *, size_t);
-static int spc_pr_clear(t10_cmd_t *, void *, size_t);
-static int spc_pr_preempt(t10_cmd_t *, void *, size_t);
-static int spc_pr_register_and_move(t10_cmd_t *, void *, size_t);
-
-static int spc_pr_in_readkeys(char *, scsi3_pgr_t *, void *, uint16_t);
-static int spc_pr_in_readrsrv(char *, scsi3_pgr_t *, void *, uint16_t);
-static int spc_pr_in_repcap(char *, scsi3_pgr_t *, void *, uint16_t);
-static int spc_pr_in_fullstat(char *, scsi3_pgr_t *, void *, uint16_t);
-
-static int spc_pgr_isconflict(uint8_t *, uint_t);
-Boolean_t spc_pr_write(t10_cmd_t *);
-
-/*
- * []----
- * | spc_pgr_check --  PERSISTENT_RESERVE {IN|OUT} check of I_T_L
- * |	Refer to SPC-3, Section ?.?, Tables ?? and ??
- * []----
- */
-Boolean_t
-spc_pgr_check(t10_cmd_t *cmd, uint8_t *cdb)
-{
-	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	sbc_reserve_t		*res = &p->d_sbc_reserve;
-	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
-	spc_pr_rsrv_t		*rsrv;
-	Boolean_t		conflict = False;
-
-	/*
-	 * Check reservation commands.
-	 */
-	switch (cdb[0]) {
-		/*
-		 * Always dis-allow these commands.
-		 */
-		case SCMD_RESERVE:
-		case SCMD_RESERVE_G1:
-		case SCMD_RELEASE:
-		case SCMD_RELEASE_G1:
-			conflict = True;
-			goto done;
-
-		/*
-		 * Always allow these commands.
-		 */
-		case SCMD_PERSISTENT_RESERVE_IN:
-		case SCMD_PERSISTENT_RESERVE_OUT:
-			conflict = False;
-			goto done;
-	}
-
-	/*
-	 * If no reservations exist, allow all remaining command types.
-	 */
-	assert(res->res_type == RT_PGR);
-	if (pgr->pgr_numrsrv == 0) {
-		conflict = False;
-		goto done;
-	}
-
-	/*
-	 * At this point we know there is at least one reservation.
-	 * If there is no reservation set on this service delivery
-	 * port then conflict all remaining command types.
-	 */
-	if (!(rsrv = spc_pr_rsrv_find(pgr, 0, 0, T10_PGR_TID(cmd)))) {
-		queue_prt(mgmtq, Q_PR_IO, "PGR Reserved on other port\n",
-		    "\t%016x:%s\n", T10_PGR_ISID(cmd), T10_PGR_TID(cmd));
-		conflict = True;
-		goto done;
-	}
-
-	/*
-	 * Check the command against the reservation type for this port.
-	 */
-	switch (rsrv->r_type) {
-		case PGR_TYPE_WR_EX:
-		case PGR_TYPE_EX_AC:
-			if (T10_PGR_ISID(cmd) == rsrv->r_isid)
-				conflict = False;
-			else
-				conflict = spc_pgr_isconflict(cdb,
-				    rsrv->r_type);
-			break;
-		case PGR_TYPE_WR_EX_RO:
-		case PGR_TYPE_EX_AC_RO:
-			if (spc_pr_key_find(
-			    pgr, 0, T10_PGR_ISID(cmd), T10_PGR_TID(cmd)))
-				conflict = False;
-			else
-				conflict = spc_pgr_isconflict(cdb,
-				    rsrv->r_type);
-			break;
-		case PGR_TYPE_WR_EX_AR:
-		case PGR_TYPE_EX_AC_AR:
-			if (spc_pr_key_find(pgr, 0, 0, T10_PGR_TID(cmd)))
-				conflict = False;
-			else
-				conflict = spc_pgr_isconflict(cdb,
-				    rsrv->r_type);
-			break;
-		default:
-			conflict = True;
-			break;
-	}
-
-done:
-	queue_prt(mgmtq, Q_PR_IO, "PGR%d LUN%d CDB:%s - spc_pgr_check(%s)\n",
-	    cmd->c_lu->l_targ->s_targ_num,
-	    cmd->c_lu->l_common->l_num,
-	    cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name == NULL
-	    ? "(no name)"
-	    : cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name,
-	    (conflict) ? "Conflict" : "Allowed");
-
-	return (conflict);
-}
-
-/*
- * []----
- * | spc_pgr_isconflict
- * |	PGR reservation conflict checking.
- * |	SPC-3, Revision 23, Table 31
- * []----
- */
-static int
-spc_pgr_isconflict(uint8_t *cdb, uint_t type)
-{
-	Boolean_t		conflict = False;
-
-	switch (cdb[0]) {
-		case SCMD_FORMAT:
-		case SCMD_EXTENDED_COPY:
-		case SCMD_LOG_SELECT_G1:
-		case SCMD_MODE_SELECT:
-		case SCMD_MODE_SELECT_G1:
-		case SCMD_MODE_SENSE:
-		case SCMD_MODE_SENSE_G1:
-		case SCMD_READ_ATTRIBUTE:
-		case SCMD_READ_BUFFER:
-		case SCMD_GDIAG:	/* SCMD_RECEIVE_DIAGNOSTIC_RESULTS */
-		case SCMD_SDIAG:	/* SCMD_SEND_DIAGNOSTIC_RESULTS */
-		case SCMD_WRITE_ATTRIBUTE:
-		case SCMD_WRITE_BUFFER:
-			conflict = True;
-			break;
-
-		case SCMD_DOORLOCK:	/* SCMD_PREVENT_ALLOW_MEDIA_REMOVAL */
-			/*
-			 * As per SPC-3, Revision 23, Table 31
-			 * (prevent <> 0)
-			 */
-			conflict = (cdb[4] & 0x1) ? True: False;
-			break;
-
-		case SCMD_REPORT_TARGET_PORT_GROUPS:	/* SCMD_REPORT_ */
-			/*
-			 * As pee SPC-3, Revision 23, Section 6.23
-			 */
-			switch ((cdb[1] & 0x03)) {
-				/* SCMD_REPORT_SUPPORTED_OPERATION_CODES */
-				case 0x0c:
-				/* SCMD_REPORT_SUPPORTED_MANAGEMENT_FUNCTIONS */
-				case 0x0d:
-
-					conflict = True;
-					break;
-			}
-			break;
-
-		case SCMD_SET_DEVICE:
-			/*
-			 * SPC-3, Revision 23, Section 6.29
-			 */
-			switch ((cdb[1] & 0x1F)) {
-				case SCMD_SET_DEVICE_IDENTIFIER:
-				case SCMD_SET_PRIORITY:
-				case SCMD_SET_TARGET_PORT_GROUPS:
-				case SCMD_SET_TIMESTAMP:
-				conflict = True;
-				break;
-			}
-			break;
-
-		case SCMD_READ:
-		case SCMD_READ_G1:
-		case SCMD_READ_G4:
-			if (type == PGR_TYPE_EX_AC || type == PGR_TYPE_EX_AC_RO)
-				conflict = True;
-			break;
-	}
-
-	return (conflict);
-}
-
-
-/*
- * []----
- * | spc_cmd_pr_in --  PERSISTENT_RESERVE IN
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-/*ARGSUSED*/
-void
-spc_cmd_pr_in(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
-{
-	scsi_cdb_prin_t		*p_prin = (scsi_cdb_prin_t *)cdb;
-	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	sbc_reserve_t		*res = &p->d_sbc_reserve;
-	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
-	uint16_t		alen;
-	size_t			len;
-	void			*buf;
-	Boolean_t		status;
-
-	/*
-	 * Information obtained from:
-	 *	SPC-3, Revision 23
-	 *	Section 6.11 PERSISTENCE RESERVE IN
-	 * Need to generate a CHECK CONDITION with ILLEGAL REQUEST
-	 * and INVALID FIELD IN CDB (0x24/0x00) if any of the following is
-	 * true.
-	 *	(1) The SERVICE ACTION field is 004h - 01fh,
-	 *	(2) The reserved area in byte 1 is set,
-	 *	(3) The reserved area in bytes 2 thru 6 are set,
-	 *	(4) If any of the reserved bits in the CONTROL byte are set.
-	 */
-	if ((p_prin->action >= 0x4) || p_prin->resbits || p_prin->resbytes[0] ||
-	    p_prin->resbytes[1] || p_prin->resbytes[2] || p_prin->resbytes[3] ||
-	    p_prin->resbytes[4] || p_prin->control) {
-		spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
-		spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
-		trans_send_complete(cmd, STATUS_CHECK);
-		return;
-	}
-
-	/*
-	 * Information obtained from:
-	 *	SPC-3, Revision 23
-	 *	Section 6.11 PERSISTENCE RESERVE IN
-	 * Acquire ALLOCATION LENGTH from bytes 7, 8
-	 * A zero(0) length allocation is not an error and we should just
-	 * acknowledge the operation.
-	 */
-	if ((alen = SCSI_READ16(p_prin->alloc_len)) == 0) {
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGR:%d LUN:%d CDB:%s - spc_cmd_pr_in, len = 0\n",
-		    cmd->c_lu->l_targ->s_targ_num,
-		    cmd->c_lu->l_common->l_num,
-		    cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name == NULL
-		    ? "(no name)"
-		    : cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name);
-
-		trans_send_complete(cmd, STATUS_GOOD);
-		return;
-	}
-
-	/*
-	 * Allocate space with an alignment that will work for any casting.
-	 */
-	if ((buf = memalign(sizeof (void *), alen)) == NULL) {
-		/*
-		 * Lack of memory is not fatal, just too busy
-		 */
-		trans_send_complete(cmd, STATUS_BUSY);
-		return;
-	} else {
-		bzero(buf, alen);
-	}
-
-	/*
-	 * Start processing, lock reservation
-	 */
-	pthread_rwlock_rdlock(&res->res_rwlock);
-
-	/*
-	 * Per SPC-3, Revision 23, Table 102, validate ranget of service actions
-	 */
-	switch (p_prin->action) {
-		case PR_IN_READ_KEYS:
-			len = spc_pr_in_readkeys(
-			    T10_PGR_TID(cmd), pgr, buf, alen);
-			break;
-		case PR_IN_READ_RESERVATION:
-			len = spc_pr_in_readrsrv(
-			    T10_PGR_TID(cmd), pgr, buf, alen);
-			break;
-		case PR_IN_REPORT_CAPABILITIES:
-			len = spc_pr_in_repcap(
-			    T10_PGR_TID(cmd), pgr, buf, alen);
-			break;
-		case PR_IN_READ_FULL_STATUS:
-			len = spc_pr_in_fullstat(
-			    T10_PGR_TID(cmd), pgr, buf, alen);
-			break;
-		default:
-			pthread_rwlock_unlock(&res->res_rwlock);
-			spc_free(buf);
-
-			/*
-			 * Fail command
-			 */
-			spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
-			spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
-			trans_send_complete(cmd, STATUS_CHECK);
-			return;
-	}
-
-	/*
-	 * Complete processing, unlock reservation
-	 */
-	pthread_rwlock_unlock(&res->res_rwlock);
-
-	/*
-	 * Now send the selected Persistent Reservation response back
-	 */
-	if (trans_send_datain(cmd, buf, alen, 0, spc_free, True, buf) == False)
-		trans_send_complete(cmd, STATUS_BUSY);
-}
-
-/*
- * []----
- * |   spc_pr_in_readkey -
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-static int
-spc_pr_in_readkeys(char *transportID, scsi3_pgr_t *pgr, void *bp,
-    uint16_t alloc_len)
-{
-	int			i = 0, max_buf_keys, hsize;
-	scsi_prin_readrsrv_t	*buf = (scsi_prin_readrsrv_t *)bp;
-	spc_pr_key_t		*key;
-
-	hsize = sizeof (buf->PRgeneration) + sizeof (buf->add_len);
-	max_buf_keys = ((int)alloc_len - hsize) / sizeof (key->k_key);
-
-	queue_prt(mgmtq, Q_PR_IO,
-	    "PGRIN readkeys - transportID=%s\n", transportID);
-
-	if (pgr->pgr_numkeys)
-	for (key  = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd;
-	    key != (spc_pr_key_t *)&pgr->pgr_keylist;
-	    key  = (spc_pr_key_t *)key->k_link.lnk_fwd) {
-
-		if (strcmp(key->k_transportID, transportID))
-			continue;
-
-		if (i < max_buf_keys)
-			SCSI_WRITE64(buf->res_key_list[i].reservation_key,
-			    key->k_key);
-
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGRIN readkeys - key:%016x, isid:%016x\n",
-		    key->k_key, key->k_isid);
-
-		i++;
-	}
-
-	SCSI_WRITE32(buf->add_len, i * sizeof (key->k_key));
-	SCSI_WRITE32(buf->PRgeneration, pgr->pgr_generation);
-
-	return (hsize + min(SCSI_READ32(buf->add_len),
-	    (int)(max_buf_keys * sizeof (key->k_key))));
-}
-
-/*
- * []----
- * |   spc_pr_in_readresv -
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-static int
-spc_pr_in_readrsrv(
-    char *transportID, scsi3_pgr_t *pgr, void *bp, uint16_t alloc_len)
-{
-	int			i = 0, max_buf_rsrv, hsize;
-	spc_pr_rsrv_t		*rsrv;
-	scsi_prin_readrsrv_t	*buf = (scsi_prin_readrsrv_t *)bp;
-
-	hsize = sizeof (buf->PRgeneration) + sizeof (buf->add_len);
-	max_buf_rsrv = ((int)alloc_len - hsize) / sizeof (scsi_prin_rsrvdesc_t);
-
-	queue_prt(mgmtq, Q_PR_IO,
-	    "PGRIN readrsrv - transportID=%s\n", transportID);
-
-	if (pgr->pgr_numrsrv)
-	for (rsrv  = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd;
-	    rsrv != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist;
-	    rsrv  = (spc_pr_rsrv_t *)rsrv->r_link.lnk_fwd) {
-
-		if (strcmp(rsrv->r_transportID, transportID))
-			continue;
-
-		if (i < max_buf_rsrv) {
-			SCSI_WRITE64(buf->res_key_list[i].reservation_key,
-			    rsrv->r_key);
-			buf->res_key_list[i].scope = rsrv->r_scope;
-			buf->res_key_list[i].type = rsrv->r_type;
-		}
-
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGRIN readrsrv - "
-		    "key:%016x isid:%016x scope:%d type:%d \n",
-		    rsrv->r_key, rsrv->r_isid, rsrv->r_scope, rsrv->r_type);
-
-		i++;
-	}
-
-	SCSI_WRITE32(buf->add_len, i * sizeof (scsi_prin_rsrvdesc_t));
-	SCSI_WRITE32(buf->PRgeneration, pgr->pgr_generation);
-
-	return (hsize + min(SCSI_READ32(buf->add_len),
-	    (int)(max_buf_rsrv * sizeof (scsi_prin_rsrvdesc_t))));
-}
-
-/*
- * []----
- * |   spc_pr_in_repcap -
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-/*
- */
-static int
-spc_pr_in_repcap(
-    char *transportID, scsi3_pgr_t *pgr, void *bp, uint16_t alloc_len)
-{
-	scsi_prin_rpt_cap_t	*buf = (scsi_prin_rpt_cap_t *)bp;
-
-	buf->crh = 0;			/* Supports Reserve / Release */
-	buf->sip_c = 1;			/* Specify Initiator Ports Capable */
-	buf->atp_c = 1;			/* All Target Ports Capable */
-	buf->ptpl_c = 1;		/* Persist Through Power Loss C */
-	buf->tmv = 1;			/* Type Mask Valid */
-	buf->ptpl_a = pgr_persist;	/* Persist Though Power Loss Active */
-	buf->pr_type.wr_ex = 1;		/* Write Exclusve */
-	buf->pr_type.ex_ac = 1;		/* Exclusive Access */
-	buf->pr_type.wr_ex_ro = 1;	/* Write Exclusive Registrants Only */
-	buf->pr_type.ex_ac_ro = 1;	/* Exclusive Access Registrants Only */
-	buf->pr_type.wr_ex_ar = 1;	/* Write Exclusive All Registrants */
-	buf->pr_type.ex_ac_ar = 1;	/* Exclusive Access All Registrants */
-
-	SCSI_WRITE16(buf->length, sizeof (scsi_prin_rpt_cap_t));
-
-	return (sizeof (scsi_prin_rpt_cap_t));
-}
-
-/*
- * []----
- * |   spc_pr_in_fullstat -
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-/*
- */
-static int
-spc_pr_in_fullstat(
-    char *transportID, scsi3_pgr_t *pgr, void *bp, uint16_t alloc_len)
-{
-	int			i = 0, max_buf_rsrv, hsize;
-	spc_pr_rsrv_t		*rsrv;
-	scsi_prin_full_status_t	*buf = (scsi_prin_full_status_t *)bp;
-
-	hsize = sizeof (buf->PRgeneration) + sizeof (buf->add_len);
-	max_buf_rsrv = ((int)alloc_len - hsize) /
-	    sizeof (scsi_prin_full_status_t);
-
-	if (pgr->pgr_numrsrv)
-	for (i = 0, rsrv  = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd;
-	    rsrv != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist;
-	    rsrv  = (spc_pr_rsrv_t *)rsrv->r_link.lnk_fwd) {
-
-		if (i < max_buf_rsrv) {
-			SCSI_WRITE64(buf->full_desc[i].reservation_key,
-			    rsrv->r_key);
-			buf->full_desc[i].all_tg_pt = 1;
-			buf->full_desc[i].r_holder =
-			    strcmp(rsrv->r_transportID, transportID) ? 0 : 1;
-			buf->full_desc[i].scope = rsrv->r_scope;
-			buf->full_desc[i].type = rsrv->r_type;
-			SCSI_WRITE16(buf->full_desc[i].rel_tgt_port_id, 0);
-			SCSI_WRITE32(buf->full_desc[i].add_len,
-			    sizeof (scsi_transport_id_t));
-			buf->full_desc[i].trans_id.protocol_id =
-			    iSCSI_PROTOCOL_ID;
-			buf->full_desc[i].trans_id.format_code =
-			    WW_UID_DEVICE_NAME;
-			SCSI_WRITE16(buf->full_desc[i].trans_id.add_len, 0);
-			sprintf(buf->full_desc[i].trans_id.iscsi_name, "");
-		}
-
-		i++;
-	}
-
-	SCSI_WRITE32(buf->add_len, i * sizeof (scsi_prin_rsrvdesc_t));
-	SCSI_WRITE32(buf->PRgeneration, pgr->pgr_generation);
-
-	return (hsize + min(SCSI_READ32(buf->add_len),
-	    (int)(max_buf_rsrv * sizeof (scsi_prin_rsrvdesc_t))));
-
-}
-
-/*
- * []----
- * | spc_cmd_pr_out --  PERSISTENT_RESERVE OUT
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-/*ARGSUSED*/
-void
-spc_cmd_pr_out(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
-{
-	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cdb;
-	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	sbc_reserve_t		*res = &p->d_sbc_reserve;
-	size_t			len;
-	void			*buf;
-
-	/*
-	 * Information obtained from:
-	 *	SPC-3, Revision 23
-	 *	Section 6.12 PERSISTENCE RESERVE OUT
-	 * Need to generate a CHECK CONDITION with ILLEGAL REQUEST
-	 * and INVALID FIELD IN CDB (0x24/0x00) if any of the following is
-	 * true.
-	 *	(1) The SERVICE ACTION field is 008h - 01fh,
-	 *	(2) The reserved area in byte 1 is set,
-	 *	(3) The TYPE and SCOPE fields are invalid,
-	 *	(4) The reserved area in bytes 3 and 4 are set,
-	 *	(5) If any of the reserved bits in the CONTROL byte are set.
-	 */
-	if ((p_prout->action >= 0x8) || p_prout->resbits ||
-	    (p_prout->type >= 0x9) ||
-	    (p_prout->scope >= 0x3) || p_prout->control) {
-		spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
-		spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
-		trans_send_complete(cmd, STATUS_CHECK);
-		return;
-	}
-
-	/*
-	 * Information obtained from:
-	 *	SPC-3, Revision 23
-	 *	Section 6.12 PERSISTENCE RESERVE OUT
-	 * Acquire ALLOCATION LENGTH from bytes 5 thru 8
-	 */
-	len = SCSI_READ32(p_prout->param_len);
-
-	/*
-	 * Parameter list length shall contain 24 (0x18),
-	 * the SPEC_I_PIT is zero (it is because we don't support SIP_C))
-	 * the service action is not REGISTER AND MOVE
-	 */
-	if ((p_prout->action != PR_OUT_REGISTER_MOVE) && (len != 24)) {
-		spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
-		spc_sense_ascq(cmd, SPC_ASC_PARAM_LIST_LEN, 0x00);
-		trans_send_complete(cmd, STATUS_CHECK);
-		return;
-	}
-
-	/*
-	 * Information obtained from:
-	 *	SPC-3, Revision 23
-	 *	Section 6.11.3.3 Persistent Reservation Scope
-	 * SCOPE field shall be set to LU_SCOPE
-	 */
-	if (p_prout->scope != PR_LU_SCOPE) {
-		pthread_rwlock_unlock(&res->res_rwlock);
-		spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
-		spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
-		trans_send_complete(cmd, STATUS_CHECK);
-		return;
-	}
-
-	/*
-	 * Allocate space with an alignment that will work for any casting.
-	 */
-	if ((buf = memalign(sizeof (void *), len)) == NULL) {
-		/*
-		 * Lack of memory is not fatal, just too busy
-		 */
-		trans_send_complete(cmd, STATUS_BUSY);
-		return;
-	}
-
-	/*
-	 * Now request the Persistent Reserve OUT parameter list
-	 */
-	if (trans_rqst_dataout(cmd, buf, len, 0, buf, spc_free) == False)
-		trans_send_complete(cmd, STATUS_BUSY);
-}
-
-/*
- * []----
- * | spc_cmd_pr_out_data -- DataIn phase of PERSISTENT_RESERVE OUT command
- * []----
- */
-/*ARGSUSED*/
-void
-spc_cmd_pr_out_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
-    size_t data_len)
-{
-	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cmd->c_cdb;
-	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	scsi_prout_plist_t	*plist = (scsi_prout_plist_t *)data;
-	sbc_reserve_t		*res = &p->d_sbc_reserve;
-	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
-	t10_lu_impl_t		*lu;
-	int			status;
-
-	/*
-	 * If this is the first time using the persistance data,
-	 * initialize the reservation and resource key queues
-	 */
-	pthread_rwlock_wrlock(&res->res_rwlock);
-	if (pgr->pgr_rsrvlist.lnk_fwd == NULL) {
-		spc_pr_key_rsrv_init(pgr);
-	}
-
-	/*
-	 * Now process the action.
-	 */
-	switch (p_prout->action) {
-	case PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY:
-	case PR_OUT_REGISTER:
-		/*
-		 * PR_OUT_REGISTER_IGNORE differs from PR_OUT_REGISTER
-		 * in that the reservation_key is ignored.
-		 */
-		status = spc_pr_register(cmd, data, data_len);
-		break;
-
-	case PR_OUT_RESERVE:
-		status = spc_pr_reserve(cmd, data, data_len);
-		break;
-
-	case PR_OUT_RELEASE:
-		status = spc_pr_release(cmd, data, data_len);
-		break;
-
-	case PR_OUT_CLEAR:
-		status = spc_pr_clear(cmd, data, data_len);
-		break;
-
-	case PR_OUT_PREEMPT_ABORT:
-	case PR_OUT_PREEMPT:
-		/*
-		 * PR_OUT_PREEMPT_ABORT differs from PR_OUT_PREEMPT
-		 * in that all current acitivy for the preempted
-		 * Initiators will be terminated.
-		 */
-		status = spc_pr_preempt(cmd, data, data_len);
-		break;
-
-	case PR_OUT_REGISTER_MOVE:
-		/*
-		 * PR_OUT_REGISTER_MOVE registers a key for another I_T
-		 */
-		status = spc_pr_register_and_move(cmd, data, data_len);
-		break;
-	}
-
-	/*
-	 * Check status of action performed.
-	 */
-	if (status == STATUS_CHECK) {
-		/*
-		 * Check condition required.
-		 */
-		pthread_rwlock_unlock(&res->res_rwlock);
-		spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
-		spc_sense_ascq(cmd, cmd->c_lu->l_asc, cmd->c_lu->l_ascq);
-		trans_send_complete(cmd, STATUS_CHECK);
-		return;
-	}
-
-	/*
-	 * Handle Failed processing status
-	 */
-	if (status != STATUS_GOOD) {
-		pthread_rwlock_unlock(&res->res_rwlock);
-		trans_send_complete(cmd, status);
-		return;
-	}
-
-	/*
-	 * Successful, bump the PRgeneration value
-	 */
-	if (p_prout->action != PR_OUT_RESERVE &&
-	    p_prout->action != PR_OUT_RELEASE)
-		pgr->pgr_generation++;
-
-	/*
-	 * If Activate Persist Through Power Loss (APTPL) is set, persist
-	 * this PGR data on disk
-	 */
-	if (plist->aptpl || pgr->pgr_aptpl)
-		spc_pr_write(cmd);
-
-	/*
-	 * When the last registration is removed, PGR is no longer
-	 * active and we must reset the reservation type.
-	 */
-	if (pgr->pgr_numkeys == 0 && pgr->pgr_numrsrv == 0) {
-		res->res_type = RT_NONE;
-		pgr->pgr_aptpl = 0;
-	} else {
-		res->res_type = RT_PGR;
-	}
-
-	/*
-	 * Set the command dispatcher according to the reservation type
-	 */
-	lu = avl_first(&cmd->c_lu->l_common->l_all_open);
-	do {
-		lu->l_cmd = (res->res_type == RT_NONE)
-		    ? sbc_cmd
-		    : sbc_cmd_reserved;
-		lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
-	} while (lu != NULL);
-
-	queue_prt(mgmtq, Q_PR_IO, "PGROUT:%d LUN:%d action:%s\n",
-	    cmd->c_lu->l_targ->s_targ_num,
-	    cmd->c_lu->l_common->l_num,
-	    (p_prout->action == PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY)
-	    ? "Register & ignore existing key"
-	    : (p_prout->action == PR_OUT_REGISTER)
-	    ? "Register"
-	    : (p_prout->action == PR_OUT_RESERVE)
-	    ? "Reserve"
-	    : (p_prout->action == PR_OUT_RELEASE)
-	    ? "Release"
-	    : (p_prout->action == PR_OUT_CLEAR)
-	    ? "Clear"
-	    : (p_prout->action == PR_OUT_PREEMPT_ABORT)
-	    ? "Preempt & abort"
-	    : (p_prout->action == PR_OUT_PREEMPT)
-	    ? "Preempt"
-	    : (p_prout->action == PR_OUT_REGISTER_MOVE)
-	    ? "Register & move"
-	    : "Uknown");
-
-	/*
-	 * Processing is complete, release mutex
-	 */
-	pthread_rwlock_unlock(&res->res_rwlock);
-
-	/*
-	 * Send back a succesful response
-	 */
-	trans_send_complete(cmd, STATUS_GOOD);
-}
-
-/*
- * []----
- * | spc_pr_register
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-static int
-spc_pr_register(t10_cmd_t *cmd, void *data, size_t data_len)
-{
-	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cmd->c_cdb;
-	scsi_prout_plist_t	*plist = (scsi_prout_plist_t *)data;
-	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	sbc_reserve_t		*res = &p->d_sbc_reserve;
-	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
-	spc_pr_rsrv_t		*rsrv;
-	spc_pr_key_t		*key;
-	uint64_t		reservation_key;
-	uint64_t		service_key;
-	t10_lu_impl_t		*lu;
-	t10_targ_impl_t		*ti;
-
-	/*
-	 * Validate Persistent Reserver Out parameter list
-	 */
-	if (plist->obsolete1[0] || plist->obsolete1[1] ||
-	    plist->obsolete1[2] || plist->obsolete1[3] ||
-	    plist->resbits1 || plist->resbits2 || plist->resbytes1 ||
-	    plist->obsolete2[0] || plist->obsolete2[1]) {
-		cmd->c_lu->l_status = KEY_ILLEGAL_REQUEST;
-		cmd->c_lu->l_asc = SPC_ASC_INVALID_CDB;
-		cmd->c_lu->l_ascq = 0;
-		return (STATUS_CHECK);
-	}
-
-	/*
-	 * Determine if Activate Persist Trhough Power Loss (APTPL)
-	 * is valid for this device server.
-	 */
-	if (plist->aptpl && (pgr_persist == 0)) {
-		/* pgr - define SCSI-3 error codes */
-		cmd->c_lu->l_status = KEY_ILLEGAL_REQUEST;
-		cmd->c_lu->l_asc = SPC_ASC_INVALID_FIELD_IN_PARAMETER_LIST;
-		cmd->c_lu->l_ascq = 0;
-		return (STATUS_CHECK);
-	}
-
-	/*
-	 * Get reservation values
-	 */
-	reservation_key = SCSI_READ64(plist->reservation_key);
-	service_key = SCSI_READ64(plist->service_key);
-
-	queue_prt(mgmtq, Q_PR_IO,
-	    "PGROUT: register reservation:%016x, key:%016x\n",
-	    reservation_key, service_key);
-
-	/*
-	 * We may need register all initiators, depending on ALL_TG_TP
-	 */
-	lu = avl_first(&cmd->c_lu->l_common->l_all_open);
-	do {
-		/*
-		 * Find specified key
-		 */
-		ti = lu->l_targ;
-		key = spc_pr_key_find(pgr, 0, ti->s_isid, ti->s_transportID);
-		if (key) {
-			/*
-			 * What about ALL_TG_TP?
-			 */
-			if (plist->all_tg_pt ||
-			    (key->k_isid == T10_PGR_ISID(cmd))) {
-
-				if (p_prout->action == PR_OUT_REGISTER &&
-				    key->k_key != reservation_key) {
-					/*
-					 * The Initiator did not specify the
-					 * existing key. Reservation conflict.
-					 */
-					return (STATUS_RESERVATION_CONFLICT);
-				}
-				/*
-				 * Change existing key ?
-				 */
-				if (service_key) {
-					queue_prt(mgmtq, Q_PR_IO,
-					    "PGROUT: change "
-					    "old:%016x = new:%016x\n",
-					    key->k_key, service_key);
-
-					/*
-					 * Overwrite (change) key
-					 */
-					key->k_key = service_key;
-
-				} else {
-					/*
-					 * Remove existing key
-					 * NOTE: If we own the reservation then
-					 * we must release it.
-					 */
-					queue_prt(mgmtq, Q_PR_IO,
-					    "PGROUT: delete "
-					    "old:%016x = new:%016x\n",
-					    key->k_key, service_key);
-
-					rsrv = spc_pr_rsrv_find(pgr, 0,
-					    ti->s_isid, ti->s_transportID);
-					if (rsrv) {
-						spc_pr_rsrv_release(
-						    cmd, pgr, rsrv);
-						spc_pr_key_free(pgr, key);
-					}
-				}
-			}
-		} else {
-			/*
-			 * What about ALL_TG_TP?
-			 */
-			if (plist->all_tg_pt ||
-			    (ti->s_isid == T10_PGR_ISID(cmd))) {
-				/*
-				 * Process request from un-registered Initiator.
-				 */
-				if ((p_prout->action == PR_OUT_REGISTER) &&
-				    (reservation_key || service_key == 0)) {
-					/*
-					 * Unregistered initiator is attempting
-					 * to modify a key.
-					 */
-					return (STATUS_RESERVATION_CONFLICT);
-				}
-
-				/*
-				 * Allocate new key.
-				 */
-				queue_prt(mgmtq, Q_PR_IO,
-				    "PGROUT: new:%016x\n", service_key);
-
-				key = spc_pr_key_alloc(pgr, service_key,
-				    ti->s_isid, ti->s_transportID);
-				if (key == NULL) {
-					/* pgr - define SCSI-3 error codes */
-					cmd->c_lu->l_status =
-					    KEY_ABORTED_COMMAND;
-					cmd->c_lu->l_asc =
-					    SPC_ASC_MEMORY_OUT_OF;
-					cmd->c_lu->l_ascq =
-					    SPC_ASCQ_RESERVATION_FAIL;
-					return (STATUS_CHECK);
-				}
-			}
-		}
-		lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
-	} while (lu != NULL);
-
-	/*
-	 * Apply the last valid APTPL bit
-	 *	SPC-3, Revision 23
-	 *	Section 5.6.4.1 Preserving persistent reservervations and
-	 *	registrations through power loss
-	 */
-	pgr->pgr_aptpl = plist->aptpl;
-
-	return (STATUS_GOOD);
-}
-
-/*
- * []----
- * | spc_pr_reserve
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-/* ARGSUSED */
-static int
-spc_pr_reserve(t10_cmd_t *cmd, void *data, size_t data_len)
-{
-	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cmd->c_cdb;
-	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	sbc_reserve_t		*res = &p->d_sbc_reserve;
-	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
-	spc_pr_rsrv_t		*rsrv;
-	scsi_prout_plist_t	*plist = (scsi_prout_plist_t *)data;
-	uint64_t		reservation_key;
-	int			status;
-
-	/*
-	 * Do not allow an unregistered initiator to
-	 * make a reservation.
-	 */
-	reservation_key = SCSI_READ64(plist->reservation_key);
-	if (!spc_pr_key_find(
-	    pgr, reservation_key, T10_PGR_ISID(cmd), T10_PGR_TID(cmd))) {
-
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT: reserve reservation:%016x not found\n",
-		    reservation_key);
-
-		return (STATUS_RESERVATION_CONFLICT);
-	} else {
-
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT: reserve reservation:%016x\n", reservation_key);
-
-	}
-
-	/*
-	 * See if there is a reservation on this port by
-	 * another Initiator.  There can be only one LU_SCOPE
-	 * reservation per ITL.  We do not support extents.
-	 */
-	if (rsrv = spc_pr_rsrv_find(pgr, 0, 0, T10_PGR_TID(cmd))) {
-		if (rsrv->r_isid != T10_PGR_ISID(cmd)) {
-
-			queue_prt(mgmtq, Q_PR_IO,
-			    "PGROUT: reserve %016x != %016x:%s\n",
-			    rsrv->r_isid, T10_PGR_ISID(cmd),
-			    T10_PGR_TID(cmd));
-
-			return (STATUS_RESERVATION_CONFLICT);
-		}
-	}
-
-	/*
-	 * At this point there is either no reservation or the
-	 * reservation is held by this Initiator.
-	 */
-	if (rsrv != NULL) {
-
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT reserve(+) - transportID=%s\n"
-		    "\tkey:%016x isid:%016x scope:%d type:%d \n",
-		    rsrv->r_transportID, rsrv->r_key, rsrv->r_isid,
-		    rsrv->r_scope, rsrv->r_type);
-
-		/*
-		 * An Initiator cannot re-reserve.  It must first
-		 * release.  But if its' type and scope match then
-		 * return STATUS_GOOD.
-		 */
-		if (rsrv->r_type == p_prout->type &&
-		    rsrv->r_scope == p_prout->scope) {
-			status = STATUS_GOOD;
-		} else {
-			status = STATUS_RESERVATION_CONFLICT;
-		}
-	} else {
-		/*
-		 * No reservation exists.  Establish a new one.
-		 */
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT reserve - transportID=%s\n"
-		    "\tkey:%016x isid:%016x scope:%d type:%d \n",
-		    T10_PGR_TID(cmd), reservation_key, T10_PGR_ISID(cmd),
-		    p_prout->scope, p_prout->type);
-
-		rsrv = spc_pr_rsrv_alloc(pgr, reservation_key,
-		    T10_PGR_ISID(cmd), T10_PGR_TID(cmd),
-		    p_prout->scope, p_prout->type);
-		if (rsrv == NULL) {
-			cmd->c_lu->l_status = KEY_ABORTED_COMMAND;
-			cmd->c_lu->l_asc = SPC_ASC_MEMORY_OUT_OF;
-			cmd->c_lu->l_ascq = SPC_ASCQ_RESERVATION_FAIL;
-			status = STATUS_CHECK;
-		} else {
-			status = STATUS_GOOD;
-		}
-	}
-
-	return (status);
-}
-
-/*
- * []----
- * | spc_pr_release
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-static int
-spc_pr_release(t10_cmd_t *cmd, void *data, size_t data_len)
-{
-	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cmd->c_cdb;
-	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	sbc_reserve_t		*res = &p->d_sbc_reserve;
-	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
-	spc_pr_rsrv_t		*rsrv;
-	scsi_prout_plist_t	*plist = (scsi_prout_plist_t *)data;
-	uint64_t		reservation_key;
-	int			status;
-
-	/*
-	 * Do not allow an unregistered initiator to attempting to
-	 * release a reservation.
-	 */
-	reservation_key = SCSI_READ64(plist->reservation_key);
-	if (!spc_pr_key_find(
-	    pgr, reservation_key, T10_PGR_ISID(cmd), T10_PGR_TID(cmd))) {
-
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT: release reservation:%016x not found\n",
-		    reservation_key);
-
-		return (STATUS_RESERVATION_CONFLICT);
-	} else {
-
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT: release reservation:%016x\n", reservation_key);
-	}
-
-	if (!(rsrv = spc_pr_rsrv_find(
-	    pgr, 0, T10_PGR_ISID(cmd), T10_PGR_TID(cmd)))) {
-		/*
-		 * Releasing a non-existent reservation is allowed.
-		 */
-		status = STATUS_GOOD;
-
-	} else if (p_prout->scope != rsrv->r_scope ||
-	    p_prout->type != rsrv->r_type ||
-	    reservation_key != rsrv->r_key) {
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT release failed - transportID=%s\n"
-		    "\tkey:%016x isid:%016x scope:%d type:%d \n",
-		    T10_PGR_TID(cmd), reservation_key, T10_PGR_ISID(cmd),
-		    p_prout->scope, p_prout->type);
-
-		/*
-		 * Scope and key must match to release.
-		 */
-		cmd->c_lu->l_status = KEY_ILLEGAL_REQUEST;
-		cmd->c_lu->l_asc = SPC_ASC_PARAMETERS_CHANGED;
-		cmd->c_lu->l_ascq = SPC_ASCQ_RES_RELEASED;
-		status = STATUS_CHECK;
-	} else {
-		/*
-		 * Now release the reservation.
-		 */
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT release - transportID=%s\n"
-		    "\tkey:%016x isid:%016x scope:%d type:%d \n",
-		    rsrv->r_transportID, rsrv->r_key, rsrv->r_isid,
-		    rsrv->r_scope, rsrv->r_type);
-
-		spc_pr_rsrv_release(cmd, pgr, rsrv);
-		status = STATUS_GOOD;
-	}
-
-	return (status);
-}
-
-/*
- * []----
- * | spc_pr_preempt
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-/* ARGSUSED */
-static int
-spc_pr_preempt(t10_cmd_t *cmd, void *data, size_t data_len)
-{
-	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cmd->c_cdb;
-	t10_lu_impl_t		*lu;
-	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	sbc_reserve_t		*res = &p->d_sbc_reserve;
-	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
-	scsi_prout_plist_t	*plist = (scsi_prout_plist_t *)data;
-	uint64_t		reservation_key;
-	uint64_t		service_key;
-	spc_pr_key_t		*key;
-	spc_pr_rsrv_t		*rsrv;
-	int			status = STATUS_GOOD;
-
-	/*
-	 * Get reservation values
-	 */
-	reservation_key = SCSI_READ64(plist->reservation_key);
-	service_key = SCSI_READ64(plist->service_key);
-
-
-	/*
-	 * Initiator must be registered and service key (preempt key)
-	 * must exist.
-	 */
-	if ((!(key = spc_pr_key_find(pgr, service_key, 0, ""))) ||
-	    (!(rsrv = spc_pr_rsrv_find(pgr, reservation_key,
-	    T10_PGR_ISID(cmd), "")))) {
-
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT: preempt failed reservation:%016x, key:%016x\n",
-		    reservation_key, service_key);
-
-		return (STATUS_RESERVATION_CONFLICT);
-	} else {
-
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT: preempt reservation:%016x, key:%016x\n",
-		    reservation_key, service_key);
-	}
-
-	/*
-	 * Preempt all keys matching service action key and free
-	 * the associated structures.  Do not set UNIT_ATTN for
-	 * the Initiator which requested the action.
-	 *
-	 * Unlike the other Persistent Reservation commands, the preempt,
-	 * preempt_and_abort and clear actions are service delivery port
-	 * independent.  So we remove matching keys across ports.
-	 */
-	for (key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd;
-	    key != (spc_pr_key_t *)&pgr->pgr_keylist;
-	    key = (spc_pr_key_t *)key->k_link.lnk_fwd) {
-
-		/* Skip non-matching keys */
-		if (key->k_key != service_key)
-			continue;
-
-		/* Remove the registration key. */
-		spc_pr_key_free(pgr, key);
-
-		/* Do not set UNIT ATTN for calling Initiator */
-		if (key->k_isid == T10_PGR_ISID(cmd))
-			continue;
-
-		/*
-		 * Find associated I_T Nexuses
-		 */
-		lu = avl_first(&cmd->c_lu->l_common->l_all_open);
-		do {
-			lu->l_cmd	= sbc_cmd;
-			lu->l_status	= KEY_UNIT_ATTENTION;
-			lu->l_asc	= SPC_ASC_PARAMETERS_CHANGED;
-			lu->l_ascq	= SPC_ASCQ_RES_PREEMPTED;
-			lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
-		} while (lu != NULL);
-
-		/*
-		 * Is this the preempt and abort?
-		 */
-		if (p_prout->action == PR_OUT_PREEMPT_ABORT) {
-			queue_message_set(
-			    cmd->c_lu->l_common->l_from_transports,
-			    Q_HIGH, msg_reset_lu, (void *)cmd->c_lu);
-		}
-	}
-
-	/*
-	 * Re-establish our registration key if we preempted it.
-	 */
-	if (!(key = spc_pr_key_find(
-	    pgr, reservation_key, T10_PGR_ISID(cmd), T10_PGR_TID(cmd)))) {
-
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT: preempt - register:%016x, isid:%016x:%s\n",
-		    reservation_key, T10_PGR_ISID(cmd), T10_PGR_TID(cmd));
-
-		key = spc_pr_key_alloc(pgr, reservation_key,
-		    T10_PGR_ISID(cmd), T10_PGR_TID(cmd));
-	}
-
-	/*
-	 * Now look for a matching reservation to preempt.
-	 */
-	for (rsrv = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd;
-	    rsrv != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist;
-	    rsrv = (spc_pr_rsrv_t *)rsrv->r_link.lnk_fwd) {
-
-		/* Skip non-matching keys */
-		if (rsrv->r_key != service_key)
-			continue;
-
-		/*
-		 * Remove matching reservations on other ports
-		 * and establish a new reservation on this port only.
-		 * To change the fuctionality to preempt rather than
-		 * delete the reservations on other ports just remove
-		 * the following block of code.
-		 */
-		if (strcmp(rsrv->r_transportID, T10_PGR_TID(cmd))) {
-			spc_pr_rsrv_free(pgr, rsrv);
-			continue;
-		}
-
-		rsrv->r_key = reservation_key;
-		rsrv->r_isid = T10_PGR_ISID(cmd);
-		rsrv->r_scope = p_prout->scope;
-		rsrv->r_type = p_prout->type;
-
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT preempt - transportID=%s\n"
-		    "\tkey:%016x isid:%016x scope:%d type:%d \n",
-		    rsrv->r_transportID, rsrv->r_key, rsrv->r_isid,
-		    rsrv->r_scope, rsrv->r_type);
-	}
-
-	return (status);
-}
-
-/*
- * []----
- * | spc_pr_clear
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-/* ARGSUSED */
-static int
-spc_pr_clear(t10_cmd_t *cmd, void *data, size_t data_len)
-{
-	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cmd->c_cdb;
-	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	sbc_reserve_t		*res = &p->d_sbc_reserve;
-	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
-	scsi_prout_plist_t	*plist = (scsi_prout_plist_t *)data;
-	uint64_t		reservation_key;
-	spc_pr_key_t		*key;
-	t10_targ_impl_t		*tp;
-	t10_lu_impl_t		*lu;
-
-	/*
-	 * Do not allow an unregistered initiator to attempting to
-	 * clear the PGR.
-	 */
-	reservation_key = SCSI_READ64(plist->reservation_key);
-	if (!spc_pr_key_find(pgr, reservation_key, T10_PGR_ISID(cmd), "")) {
-
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT: clear pgr:%016x not found\n", reservation_key);
-
-		return (STATUS_RESERVATION_CONFLICT);
-	} else {
-		queue_prt(mgmtq, Q_PR_IO,
-		    "PGROUT: clear pgr:%016x\n", reservation_key);
-	}
-
-	/*
-	 * We need to set UNIT ATTENTION for all registered initiators.
-	 */
-	for (key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd;
-	    key != (spc_pr_key_t *)&pgr->pgr_keylist;
-	    key = (spc_pr_key_t *)key->k_link.lnk_fwd) {
-
-		/* Do not set UNIT ATTN for calling Initiator */
-		if (key->k_isid == T10_PGR_ISID(cmd))
-			continue;
-		/*
-		 * At this point the only way to get in here is to be the owner
-		 * of the reservation.
-		 */
-		lu = avl_first(&cmd->c_lu->l_common->l_all_open);
-		do {
-			lu->l_status = KEY_UNIT_ATTENTION;
-			lu->l_asc = SPC_ASC_PARAMETERS_CHANGED;
-			lu->l_ascq = SPC_ASCQ_RES_PREEMPTED;
-			lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
-		} while (lu != NULL);
-	}
-
-	/*
-	 * Now erase the reservation and registration info.
-	 */
-	spc_pr_erase(pgr);
-
-	return (STATUS_GOOD);
-}
-
-/*
- * []----
- * | spc_pr_register_and_move
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-static int
-spc_pr_register_and_move(t10_cmd_t *cmd, void *data, size_t data_len)
-{
-	return (STATUS_RESERVATION_CONFLICT);
-}
-
-/*
- * []----
- * | spc_pr_key_alloc -
- * | 	Allocate a new registration key and add it to the key list.
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-static spc_pr_key_t *
-spc_pr_key_alloc(scsi3_pgr_t *pgr, uint64_t service_key, uint64_t isid,
-    char *transportID)
-{
-	spc_pr_key_t	*key = (spc_pr_key_t *)
-	    memalign(sizeof (void *), sizeof (spc_pr_key_t));
-
-	if (key != NULL) {
-		key->k_key = service_key;
-		key->k_isid = isid;
-		key->k_transportID = strdup(transportID);
-
-		insque(&key->k_link, pgr->pgr_keylist.lnk_bwd);
-
-		pgr->pgr_numkeys++;
-		assert(pgr->pgr_numkeys > 0);
-	}
-
-	return (key);
-}
-
-/*
- * []----
- * | spc_pr_key_rsrv_init -
- * |	Initialize registration & reservervation queues
- * []----
- */
-static void
-spc_pr_key_rsrv_init(scsi3_pgr_t *pgr)
-{
-	assert(pgr->pgr_numrsrv == 0);
-	assert(pgr->pgr_numkeys == 0);
-	pgr->pgr_rsrvlist.lnk_fwd = (key_link_t *)&pgr->pgr_rsrvlist.lnk_fwd;
-
-	assert(pgr->pgr_rsrvlist.lnk_bwd == NULL);
-	pgr->pgr_rsrvlist.lnk_bwd = (key_link_t *)&pgr->pgr_rsrvlist.lnk_fwd;
-
-	assert(pgr->pgr_keylist.lnk_fwd == NULL);
-	pgr->pgr_keylist.lnk_fwd = (key_link_t *)&pgr->pgr_keylist.lnk_fwd;
-
-	assert(pgr->pgr_keylist.lnk_bwd == NULL);
-	pgr->pgr_keylist.lnk_bwd = (key_link_t *)&pgr->pgr_keylist.lnk_fwd;
-}
-
-/*
- * []----
- * | spc_pr_key_free -
- * |	Free a registration key
- * []----
- */
-static void
-spc_pr_key_free(scsi3_pgr_t *pgr, spc_pr_key_t *key)
-{
-	remque(&key->k_link);
-	free(key->k_transportID);
-	free(key);
-
-	pgr->pgr_numkeys--;
-	assert(pgr->pgr_numkeys >= 0);
-}
-
-/*
- * []----
- * | spc_pr_key_find -
- * |	Find a registration key based on the key, owner id and port id.
- * []----
- */
-static spc_pr_key_t *
-spc_pr_key_find(scsi3_pgr_t *pgr, uint64_t key, uint64_t isid,
-    char *transportID)
-{
-	spc_pr_key_t	*kp;
-	spc_pr_key_t	*rval = NULL;
-
-
-	for (kp = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd;
-	    kp != (spc_pr_key_t *)&pgr->pgr_keylist;
-	    kp = (spc_pr_key_t *)kp->k_link.lnk_fwd) {
-		if ((key == 0 || kp->k_key == key) &&
-		    (isid == 0 || kp->k_isid == isid) &&
-		    (strlen(transportID) == 0 ||
-		    (strcmp(kp->k_transportID, transportID) == 0))) {
-			rval = kp;
-			break;
-		}
-	}
-
-	return (rval);
-}
-
-
-/*
- * []----
- * | spc_pr_rsrv_alloc -
- * |	Allocate a new reservation and add it to the rsrv list.
- * []----
- */
-static spc_pr_rsrv_t *
-spc_pr_rsrv_alloc(scsi3_pgr_t *pgr, uint64_t service_key, uint64_t isid,
-    char *transportID, uint8_t scope, uint8_t type)
-{
-	spc_pr_rsrv_t	*rsrv = (spc_pr_rsrv_t *)
-	    memalign(sizeof (void *), sizeof (spc_pr_rsrv_t));
-
-	if (rsrv != NULL) {
-		rsrv->r_key = service_key;
-		rsrv->r_isid = isid;
-		rsrv->r_transportID = strdup(transportID);
-		rsrv->r_scope = scope;
-		rsrv->r_type = type;
-
-		insque(&rsrv->r_link, pgr->pgr_rsrvlist.lnk_bwd);
-
-		pgr->pgr_numrsrv++;
-		assert(pgr->pgr_numrsrv > 0);
-	}
-
-	return (rsrv);
-}
-
-
-/*
- * []----
- * | spc_pr_rsrv_free -
- * |	Free a reservation.
- * []----
- */
-static void
-spc_pr_rsrv_free(scsi3_pgr_t *pgr, spc_pr_rsrv_t *rsrv)
-{
-	remque(&rsrv->r_link);
-	free(rsrv->r_transportID);
-	free(rsrv);
-
-	pgr->pgr_numrsrv--;
-	assert(pgr->pgr_numrsrv >= 0);
-}
-
-/*
- * []----
- * | spc_pr_rsrv_find -
- * |	Find a reservation based on the key, owner id and port id.
- * []----
- */
-static spc_pr_rsrv_t *
-spc_pr_rsrv_find(scsi3_pgr_t *pgr, uint64_t key, uint64_t isid,
-    char *transportID)
-{
-	spc_pr_rsrv_t	*rp, *rval = NULL;
-
-	for (rp = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd;
-	    rp != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist;
-	    rp = (spc_pr_rsrv_t *)rp->r_link.lnk_fwd) {
-		if ((key == 0 || rp->r_key == key) &&
-		    (isid == 0 || rp->r_isid == isid) &&
-		    (strlen(transportID) == 0 ||
-		    (strcmp(rp->r_transportID, transportID) == 0))) {
-			rval = rp;
-			break;
-		}
-	}
-
-	return (rval);
-}
-
-/*
- * []----
- * | spc_pr_erase -
- * |	Find specified key / reservation and erease it
- * []----
- */
-/*
- */
-static void
-spc_pr_erase(scsi3_pgr_t *pgr)
-{
-	spc_pr_key_t		*key;
-	spc_pr_rsrv_t		*rsrv;
-
-	while ((key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd) !=
-	    (spc_pr_key_t *)&pgr->pgr_keylist) {
-		spc_pr_key_free(pgr, key);
-	}
-
-	assert(pgr->pgr_numkeys == 0);
-
-	while ((rsrv = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd) !=
-	    (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist) {
-		spc_pr_rsrv_free(pgr, rsrv);
-	}
-
-	assert(pgr->pgr_numrsrv == 0);
-
-	pgr->pgr_generation = 0;
-	pgr->pgr_aptpl = 0;
-}
-
-/*
- * []----
- * | spc_pr_rsrv_release -
- * |	Release the reservation the perform any other required clearing actions.
- * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
- * []----
- */
-static void
-spc_pr_rsrv_release(t10_cmd_t *cmd, scsi3_pgr_t *pgr, spc_pr_rsrv_t *rsrv)
-{
-	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	t10_lu_impl_t		*lu;
-	spc_pr_key_t		*key;
-
-	/*
-	 * For Registrants-Only mode set UNIT ATTN.
-	 */
-	if (rsrv->r_type == PGR_TYPE_WR_EX_RO ||
-	    rsrv->r_type == PGR_TYPE_EX_AC_RO) {
-
-		for (key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd;
-		    key != (spc_pr_key_t *)&pgr->pgr_keylist;
-		    key = (spc_pr_key_t *)key->k_link.lnk_fwd) {
-
-			/*
-			 * No UNIT ATTN for the requesting Initiator.
-			 */
-			if (key->k_isid == T10_PGR_ISID(cmd))
-				continue;
-
-			/*
-			 * Find associated I_T Nexuses
-			 */
-			lu = avl_first(&cmd->c_lu->l_common->l_all_open);
-			do {
-				lu->l_cmd	= sbc_cmd;
-				lu->l_status	= KEY_UNIT_ATTENTION;
-				lu->l_asc	= SPC_ASC_PARAMETERS_CHANGED;
-				lu->l_ascq	= SPC_ASCQ_RES_RELEASED;
-				lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open,
-				    lu);
-			} while (lu != NULL);
-		}
-	}
-
-	/*
-	 * Remove the reservation.
-	 */
-	spc_pr_rsrv_free(pgr, rsrv);
-}
-
-/*
- * []----
- * | spc_pr_read -
- * |	Read in pgr keys and reservations for this device from backend storage.
- * |	At least the local pgr write lock must be held.
- * []----
- */
-Boolean_t
-spc_pr_read(t10_cmd_t *cmd)
-{
-	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	sbc_reserve_t		*res = &p->d_sbc_reserve;
-	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
-	spc_pr_key_t		*key;
-	spc_pr_rsrv_t		*rsrv;
-	spc_pr_diskkey_t	*klist;
-	spc_pr_diskrsrv_t	*rlist;
-	spc_pr_persist_disk_t	*buf = NULL;
-	t10_lu_impl_t		*lu;
-	int			i, pfd;
-	Boolean_t		status = False;
-	char			path[MAXPATHLEN];
-
-	/*
-	 * If the pre-processor supported "#if .. sizeof", these would
-	 * not be required here
-	 */
-	assert(sizeof (spc_pr_diskkey_t) == 256);
-	assert(sizeof (spc_pr_diskrsrv_t) == 256);
-	assert(sizeof (spc_pr_persist_disk_t) == 512);
-
-	/*
-	 * Open/create the PERSISTANCE file specification
-	 */
-	(void) snprintf(path, MAXPATHLEN, "%s/%s/%s%d",
-	    target_basedir, cmd->c_lu->l_targ->s_targ_base,
-	    PERSISTANCEBASE, cmd->c_lu->l_common->l_num);
-	if ((pfd = open(path, O_RDONLY)) >= 0) {
-		struct stat pstat;
-		if ((fstat(pfd, &pstat)) == 0)
-			if (pstat.st_size > 0)
-				if (buf  = malloc(pstat.st_size))
-					if (read(pfd, buf, pstat.st_size) ==
-					    pstat.st_size)
-						status = True;
-	}
-
-	/*
-	 * Clean up on no persistence file found
-	 */
-	if (status == False) {
-		if (pfd >= 0)
-			close(pfd);
-		if (buf)
-			free(buf);
-		return (status);
-	}
-
-	/*
-	 * If this is the first time using the persistance data,
-	 * initialize the reservation and resource key queues
-	 */
-	if (pgr->pgr_rsrvlist.lnk_fwd == NULL) {
-		spc_pr_key_rsrv_init(pgr);
-	}
-
-	/*
-	 * Perform some vailidation
-	 */
-	if ((buf->magic != PGRMAGIC) ||
-	    (buf->revision != SPC_PGR_PERSIST_DATA_REVISION)) {
-		status = False;
-		goto done;
-	}
-
-	/*
-	 * Get the registration keys.
-	 */
-	klist = buf->keylist;
-	for (i = 0; i < buf->numkeys; i++) {
-		if (klist[i].rectype != PGRDISKKEY) {
-			status = False;
-			goto done;
-		}
-		key = spc_pr_key_alloc(pgr, klist[i].key, klist[i].isid,
-		    klist[i].transportID);
-		if (key == NULL) {
-			status = False;
-			goto done;
-		}
-	}
-
-	/*
-	 * Get the reservations.
-	 */
-	rlist = (spc_pr_diskrsrv_t *)&buf->keylist[buf->numkeys];
-	for (i = 0; i < buf->numrsrv; i++) {
-		if (rlist[i].rectype != PGRDISKRSRV) {
-			status = False;
-			goto done;
-		}
-		rsrv = spc_pr_rsrv_alloc(pgr, rlist[i].key, rlist[i].isid,
-		    rlist[i].transportID, rlist[i].scope, rlist[i].type);
-		if (rsrv == NULL) {
-			status = False;
-			goto done;
-		}
-	}
-
-	/*
-	 * If there was data then set the reservation type.
-	 */
-	if (pgr->pgr_numkeys > 0 || pgr->pgr_numrsrv > 0) {
-		res->res_type = RT_PGR;
-		pgr->pgr_generation = buf->generation;
-
-		/*
-		 * Set the command dispatcher according to the reservation type
-		 */
-		lu = avl_first(&cmd->c_lu->l_common->l_all_open);
-		do {
-			lu->l_cmd = sbc_cmd_reserved;
-			lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
-		} while (lu != NULL);
-	}
-
-done:	pthread_rwlock_unlock(&res->res_rwlock);
-	free(buf);
-	return (status);
-}
-
-/*
- * []----
- * | spc_pr_write -
- * |	Write PGR keys and reservations for this device to backend storage.
- * |	At least the local pgr write lock must be held.
- * []----
- */
-Boolean_t
-spc_pr_write(t10_cmd_t *cmd)
-{
-	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	sbc_reserve_t		*res = &p->d_sbc_reserve;
-	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
-	spc_pr_key_t		*key;
-	spc_pr_rsrv_t		*rsrv;
-	spc_pr_diskkey_t	*klist;
-	spc_pr_diskrsrv_t	*rlist;
-	spc_pr_persist_disk_t	*buf;
-	ssize_t			length, bufsize;
-	int			i, pfd = -1;
-	char			path[MAXPATHLEN];
-	Boolean_t		status = True;
-
-	/*
-	 * If the pre-processor supported "#if .. sizeof", these would
-	 * not be required here
-	 */
-	assert(sizeof (spc_pr_diskkey_t) == 256);
-	assert(sizeof (spc_pr_diskrsrv_t) == 256);
-	assert(sizeof (spc_pr_persist_disk_t) == 512);
-
-	/*
-	 * Verify space requirements and allocate buffer memory.
-	 * Space needed is header + keylist + rsrvlist.
-	 * Subtract 1 from numkeys since header already defines
-	 * the first element of the keylist.
-	 * Round up the bufsize to the next FBA boundary.
-	 */
-	bufsize = sizeof (spc_pr_persist_disk_t) +
-	    (pgr->pgr_numkeys - 1) * sizeof (spc_pr_diskkey_t) +
-	    pgr->pgr_numrsrv * sizeof (spc_pr_diskrsrv_t);
-	bufsize = roundup(bufsize, 512);
-	if ((buf = memalign(sizeof (void *), bufsize)) == NULL)
-		return (False);
-	else
-		bzero(buf, bufsize);
-
-	/*
-	 * Build header.
-	 */
-	buf->magic = PGRMAGIC;
-	buf->revision = SPC_PGR_PERSIST_DATA_REVISION;
-	buf->generation = pgr->pgr_generation;
-	buf->numkeys = pgr->pgr_numkeys;
-	buf->numrsrv = pgr->pgr_numrsrv;
-
-	/*
-	 * Copy the keys.
-	 */
-	klist = buf->keylist;
-	for (i = 0, key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd;
-	    key != (spc_pr_key_t *)&pgr->pgr_keylist && i < pgr->pgr_numkeys;
-	    key = (spc_pr_key_t *)key->k_link.lnk_fwd, i++) {
-
-		klist[i].rectype = PGRDISKKEY;
-		klist[i].reserved = 0;
-		klist[i].key = key->k_key;
-		klist[i].isid = key->k_isid;
-		strncpy(klist[i].transportID, key->k_transportID,
-		    sizeof (klist[i].transportID));
-	}
-
-	/*
-	 * Copy the reservations.
-	 */
-	rlist = (spc_pr_diskrsrv_t *)&buf->keylist[pgr->pgr_numkeys];
-	for (i = 0, rsrv = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd;
-	    rsrv != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist &&
-	    i < pgr->pgr_numrsrv;
-	    rsrv = (spc_pr_rsrv_t *)rsrv->r_link.lnk_fwd, i++) {
-
-		rlist[i].rectype = PGRDISKRSRV;
-		rlist[i].reserved = 0;
-		rlist[i].scope = rsrv->r_scope;
-		rlist[i].type = rsrv->r_type;
-		rlist[i].key = rsrv->r_key;
-		rlist[i].isid = rsrv->r_isid;
-		strncpy(rlist[i].transportID, rsrv->r_transportID,
-		    sizeof (rlist[i].transportID));
-	}
-
-	/*
-	 * Open/create the PERSISTANCE file specification
-	 */
-	(void) snprintf(path, MAXPATHLEN, "%s/%s/%s%d",
-	    target_basedir, cmd->c_lu->l_targ->s_targ_base,
-	    PERSISTANCEBASE, cmd->c_lu->l_common->l_num);
-	if ((pfd = open(path, O_WRONLY|O_CREAT)) >= 0) {
-		length = write(pfd, buf, bufsize);
-		close(pfd);
-	} else {
-		if ((pfd < 0) || (length != bufsize))
-			status = False;
-	}
-
-	/*
-	 * Free allocated buffer
-	 */
-	free(buf);
-	return (status);
-}
--- a/deleted_files/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.h	Thu Sep 13 13:50:11 2007 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _T10_SPC_PR_H
-#define	_T10_SPC_PR_H
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * SPC-3 Persistent Reservation specific structures and defines
- */
-
-/*
- * Key Linked Lists
- */
-typedef struct key_link {
-	union {
-		uint64_t	align;
-		struct {
-			struct key_link *_lnk_fwd;	/* Forward element */
-			struct key_link *_lnk_bwd;	/* Backward element */
-		} key_ptr;
-	} key_link;
-} key_link_t;
-#define	lnk_fwd	key_link.key_ptr._lnk_fwd
-#define	lnk_bwd	key_link.key_ptr._lnk_bwd
-#define	insque(a, b) do { \
-	((key_link_t *)(a))->lnk_fwd = (key_link_t *)(b); \
-	((key_link_t *)(a))->lnk_bwd = ((key_link_t *)(b))->lnk_bwd; \
-	((key_link_t *)(b))->lnk_bwd = (key_link_t *)(a); \
-	((key_link_t *)(a))->lnk_bwd->lnk_fwd = (key_link_t *)(a); \
-	} while (0)
-
-#define	remque(A) do { \
-	((key_link_t *)(A))->lnk_bwd->lnk_fwd = ((key_link_t *)(A))->lnk_fwd; \
-	((key_link_t *)(A))->lnk_fwd->lnk_bwd = ((key_link_t *)(A))->lnk_bwd; \
-	} while (0)
-
-/*
- * Reservation Types (res_type).
- */
-typedef enum {
-	RT_NONE = 0,			/* None */
-	RT_PGR				/* SCSI-3 Persistent Reservation */
-} spc_reserve_types;
-
-/*
- * Persistent reservation data.
- */
-typedef struct spc_pr_key {
-	key_link_t	k_link;			/* Key linked list */
-	uint64_t	k_key;			/* registration key */
-	uint64_t	k_isid;			/* Owner ISID */
-	char		*k_transportID;		/* transport ID */
-} spc_pr_key_t;
-
-typedef struct spc_pr_rsrv {
-	key_link_t	r_link;			/* Key linked list */
-	uint64_t	r_key;			/* reservation key */
-	uint64_t	r_isid;			/* Owner ISID */
-	char		*r_transportID;		/* transport ID */
-	uint8_t		r_scope;		/* reservation scope */
-	uint8_t		r_type;			/* reservation type */
-} spc_pr_rsrv_t;
-
-/*
- * Persistent Reservation data
- */
-typedef struct scsi3_pgr {
-	uint32_t	pgr_generation;		/* PGR PRgeneration value */
-	uint16_t	pgr_unused;
-	uint16_t	pgr_bits	: 15,
-			pgr_aptpl	: 1;	/* persistence data exists */
-	int32_t		pgr_numkeys;		/* # entries in key list */
-	int32_t		pgr_numrsrv;		/* # entries in rsrv list */
-	key_link_t	pgr_keylist;		/* Registration key list */
-	key_link_t	pgr_rsrvlist;		/* reservation list */
-} scsi3_pgr_t;
-
-typedef struct sbc_reserve {
-	spc_reserve_types	res_type;	/* standard or pr active */
-	pthread_rwlock_t	res_rwlock;	/* Lock for coordination */
-	scsi3_pgr_t		res_scsi_3_pgr;	/* SCSI-3 PGR */
-} sbc_reserve_t;
-
-/*
- * On-disk PGR data.
- *
- * NOTE: The following three structures should be rounded up to 256 bytes each
- *	to prevent potential problems with on-disk data.
- */
-typedef struct spc_pr_diskkey {
-	uint32_t	rectype;		/* record type */
-	uint32_t	reserved;
-	uint64_t	key;			/* registration key */
-	uint64_t	isid;			/* Owner ISID */
-	char		transportID[228];	/* transport ID */
-	char		filler[4];		/* Unsed, round to 256 bytes */
-} spc_pr_diskkey_t;
-
-typedef struct spc_pr_diskrsrv {
-	uint32_t	rectype;		/* record type */
-	uint16_t	reserved;
-	uint8_t		scope;			/* reservation scope */
-	uint8_t		type;			/* reservation type */
-	uint64_t	key;			/* reservation key */
-	uint64_t	isid;			/* Owner ISID */
-	char		transportID[228];	/* Transport ID */
-	char		filler[4];		/* Unsed, round to 256 bytes */
-} spc_pr_diskrsrv_t;
-
-typedef struct spc_pr_persist_disk {
-	uint64_t	magic;			/* magic number */
-	uint32_t	revision;		/* header format revision */
-	uint32_t	generation;		/* pgr generation count */
-	int32_t		numkeys;		/* # items in key list */
-	int32_t		numrsrv;		/* # items in rsrv list */
-	char		filler[232];		/* Unused, round to 256 bytes */
-
-/*
- * After the header the data is laid out as follows:
- *	spc_pr_diskkey_t	keylist[];
- *	spc_pr_diskrsrv_t	rsrvlist[];
- */
-	spc_pr_diskkey_t	keylist[1];
-} spc_pr_persist_disk_t;
-
-
-#define	SPC_PGR_PERSIST_DATA_REVISION 0x01	/* REVISON = 1 */
-#define	PGRMAGIC	0x5047524D41474943LL	/* "PGRMAGIC" */
-#define	PGRDISKKEY	0x5047526B		/* "PGRk" */
-#define	PGRDISKRSRV	0x50475272		/* "PGRr" */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _T10_SPC_PR_H */
--- a/deleted_files/usr/src/uts/common/sys/scsi/generic/persist.h	Thu Sep 13 13:50:11 2007 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,398 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef	_SYS_SCSI_GENERIC_PERSIST_H
-#define	_SYS_SCSI_GENERIC_PERSIST_H
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/*
- * SCSI Persistence Data
- *
- * Format of data returned as a result of PERSISTENCE RESERVER { IN | OUT }
- */
-
-/*
- * SPC-3 revision 23, Section 6.11.1, Table 102
- * Persistent Reservations
- * Persistent Reserve In service actions
- */
-#define	PR_IN_READ_KEYS		0x0 /* Read all registered reservation keys */
-#define	PR_IN_READ_RESERVATION	0x1 /* Reads th persistent reservations */
-#define	PR_IN_REPORT_CAPABILITIES 0x2 /* Returns capability information */
-#define	PR_IN_READ_FULL_STATUS	0x3 /* Reads complete information about all */
-				    /* registrations and the persistent */
-				    /* reservations, if any */
-/*
- * SPC-3 revision 23, Section 6.11.3.3, Table 106
- * Persistent reservation scope codes
- */
-#define	PR_LU_SCOPE		0x0	/* Persistent reservation applies to */
-					/* full logical unit */
-/*
- * SPC-3 revision 23, Section 6.11.3.4, Table 107
- * Persistent Reservations
- * Persistent reservation type codes
- */
-#define	PGR_TYPE_WR_EX		0x1	/* Write Exclusive */
-#define	PGR_TYPE_EX_AC		0x3	/* Exclusive Access */
-#define	PGR_TYPE_WR_EX_RO	0x5	/* Write Exclusive, Registrants Only */
-#define	PGR_TYPE_EX_AC_RO	0x6	/* Exclusive Access, Registrants Only */
-#define	PGR_TYPE_WR_EX_AR	0x7	/* Write Exclusive, All Registrants */
-#define	PGR_TYPE_EX_AC_AR	0x8	/* Exclusive Access, All Registrants */
-
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.11.5 PERSISTENCE RESERVE IN
- *	Table 111 - full status descriptor format
- */
-/* Table 289 - iSCSI Initiator Device TransportID format */
-
-#define	iSCSI_PROTOCOL_ID	0x5	/* Table 262 - iSCSI Protocol ID  */
-#define	WW_UID_DEVICE_NAME	0x0	/* Table 288 - iSCSI Transport IDs */
-
-
-#if defined(_BIT_FIELDS_LTOH)
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.11.1 PERSISTENCE RESERVE IN
- *	Table 101 - PERSISTENCE RESERVE IN command
- */
-typedef struct scsi_cdb_prin {
-	uint8_t			cmd;
-	uint8_t			action : 5,
-				resbits : 3;
-	uint8_t			resbytes[5];
-	uint8_t			alloc_len[2];
-	uint8_t			control;
-} scsi_cdb_prin_t;
-
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.11.2 PERSISTENCE RESERVE IN
- *	Table 103/104/105 - parameter data for READS KEYS
- */
-typedef struct scsi_prin_rsrvdesc {
-	uint8_t			reservation_key[8];
-	uint8_t			obsolete1[4];
-	uint8_t			resbytes;
-	uint8_t			type : 4,
-				scope : 4;
-	uint8_t			obsolete2[2];
-} scsi_prin_rsrvdesc_t;
-typedef struct scsi_prin_readrsrv {
-	uint8_t			PRgeneration[4];
-	uint8_t			add_len[4];
-	scsi_prin_rsrvdesc_t	res_key_list[1];
-} scsi_prin_readrsrv_t;
-
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.11.4 PERSISTENCE RESERVE IN
- * 	Table 108 - parameter data for REPORT CAPABILTIES
- */
-typedef struct scsi_per_res_type {
-	uint8_t			resbits1 : 1,
-				wr_ex : 1,
-				resbits2 : 1,
-				ex_ac : 1,
-				resbits3 : 1,
-				wr_ex_ro : 1,
-				ex_ac_ro : 1,
-				wr_ex_ar : 1;
-	uint8_t			ex_ac_ar : 1,
-				resbits4 : 7;
-} scsi_per_res_type_t;
-typedef struct scsi_prin_rpt_cap {
-	uint8_t			length[2];
-	uint8_t			ptpl_c : 1,
-				resbits1 : 1,
-				atp_c : 1,
-				sip_c : 1,
-				crh : 1,
-				resbits2 : 3;
-	uint8_t			ptpl_a : 1,
-				resbits3 : 6,
-				tmv : 1;
-	scsi_per_res_type_t	pr_type;
-	uint8_t			resbytes[2];
-} scsi_prin_rpt_cap_t;
-
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.11.5 PERSISTENCE RESERVE IN
- * 	Table 110/111 - parameter data for READ FULL STATUS
- *	Table 281 - TransportId format
- */
-typedef struct scsi_transport_id {
-	uint8_t			protocol_id : 4,
-				resbits : 2,
-				format_code : 2;
-	uint8_t			add_len[2];
-	char			iscsi_name[1];
-} scsi_transport_id_t;
-typedef struct scsi_prin_status_t {
-	uint8_t			reservation_key[8];
-	uint8_t			resbytes1[4];
-	uint8_t			r_holder : 1,
-				all_tg_pt : 1,
-				resbits : 6;
-	uint8_t			type : 4,
-				scope : 4;
-	uint8_t			resbytes2[4];
-	uint8_t			rel_tgt_port_id[2];
-	uint8_t			add_len[4];
-	scsi_transport_id_t	trans_id;
-} scsi_prin_status_t;
-typedef struct scsi_prin_full_status {
-	uint8_t			PRgeneration[4];
-	uint8_t			add_len[4];
-	scsi_prin_status_t	full_desc[1];
-} scsi_prin_full_status_t;
-
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.12.1 PERSISTENCE RESERVE OUT
- *	Table 112 - PERSISTENCE RESERVE OUT command
- */
-typedef struct scsi_cdb_prout {
-	uint8_t			cmd;
-	uint8_t			action : 5,
-				resbits : 3;
-	uint8_t			type : 4,
-				scope : 4;
-	uint8_t			resbytes[2];
-	uint8_t			param_len[4];
-	uint8_t			control;
-} scsi_cdb_prout_t;
-
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.12.3 PERSISTENCE RESERVE OUT
- *	Table 114 - PERSISTENCE RESERVE OUT parameter list
- */
-typedef struct scsi_prout_plist {
-	uint8_t			reservation_key[8];
-	uint8_t			service_key[8];
-	uint8_t			obsolete1[4];
-	uint8_t			aptpl : 1,
-				resbits1 : 1,
-				all_tg_pt : 1,
-				spec_i_pt : 1,
-				resbits2 : 4;
-	uint8_t			resbytes1;
-	uint8_t			obsolete2[2];
-	uint8_t			apd[1];
-} scsi_prout_plist_t;
-
-#elif defined(_BIT_FIELDS_HTOL)
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.11.1 PERSISTENCE RESERVE IN
- *	Table 101 - PERSISTENCE RESERVE IN command
- */
-typedef struct scsi_cdb_prin {
-	uint8_t			cmd;
-	uint8_t			resbits : 3,
-				action : 5;
-	uint8_t			resbytes[5];
-	uint8_t			alloc_len[2];
-	uint8_t			control;
-} scsi_cdb_prin_t;
-
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.11.2 PERSISTENCE RESERVE IN
- *	Table 103/104/105 - parameter data for READS KEYS
- */
-typedef struct scsi_prin_rsrvdesc {
-	uint8_t			reservation_key[8];
-	uint8_t			obsolete1[4];
-	uint8_t			resbytes;
-	uint8_t			scope : 4,
-				type : 4;
-	uint8_t			obsolete2[8];
-} scsi_prin_rsrvdesc_t;
-typedef struct scsi_prin_readrsrv {
-	uint8_t			PRgeneration[4];
-	uint8_t			add_len[4];
-	scsi_prin_rsrvdesc_t	res_key_list[1];
-} scsi_prin_readrsrv_t;
-
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.11.4 PERSISTENCE RESERVE IN
- * 	Table 108 - parameter data for REPORT CAPABILTIES
- */
-typedef struct scsi_per_res_type {
-	uint8_t			wr_ex_ar : 1,
-				ex_ac_ro : 1,
-				wr_ex_ro : 1,
-				resbits3 : 1,
-				ex_ac : 1,
-				resbits2 : 1,
-				wr_ex : 1,
-				resbits1 : 1;
-	uint8_t			resbits4 : 7,
-				ex_ac_ar : 1;
-} scsi_per_res_type_t;
-typedef struct scsi_prin_rpt_cap {
-	uint8_t			length[2];
-	uint8_t			resbits2 : 3,
-				crh : 1,
-				sip_c : 1,
-				atp_c : 1,
-				resbits1 : 1,
-				ptpl_c : 1;
-	uint8_t			tmv : 1,
-				resbits3 : 6,
-				ptpl_a : 1;
-	scsi_per_res_type_t	pr_type;
-	uint8_t			resbytes[2];
-} scsi_prin_rpt_cap_t;
-
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.11.5 PERSISTENCE RESERVE IN
- * 	Table 110/111 - parameter data for READ FULL STATUS
- *	Table 281 - TransportId format
- */
-typedef struct scsi_transport_id {
-	uint8_t			format_code : 2,
-				resbits : 2,
-				protocol_id : 4;
-	uint8_t			add_len[2];
-	char			iscsi_name[1];
-} scsi_transport_id_t;
-typedef struct scsi_prin_status_t {
-	uint8_t			reservation_key[8];
-	uint8_t			resbytes1[4];
-	uint8_t			resbits : 6,
-				all_tg_pt : 1,
-				r_holder : 1;
-	uint8_t			scope : 4,
-				type : 4;
-	uint8_t			resbytes2[4];
-	uint8_t			rel_tgt_port_id[2];
-	uint8_t			add_len[4];
-	scsi_transport_id_t	trans_id;
-} scsi_prin_status_t;
-typedef struct scsi_prin_full_status {
-	uint8_t			PRgeneration[4];
-	uint8_t			add_len[4];
-	scsi_prin_status_t	full_desc[1];
-} scsi_prin_full_status_t;
-
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.12.1 PERSISTENCE RESERVE OUT
- *	Table 112 - PERSISTENCE RESERVE OUT command
- */
-typedef struct scsi_cdb_prout {
-	uint8_t			cmd;
-	uint8_t			resbits : 3,
-				action : 5;
-	uint8_t			scope : 4,
-				type : 4;
-	uint8_t			resbytes[2];
-	uint8_t			param_len[4];
-	uint8_t			control;
-} scsi_cdb_prout_t;
-
-/*
- * Information obtained from:
- *	SPC-3, Revision 23
- *	Section 6.12.3 PERSISTENCE RESERVE OUT
- *	Table 114 - PERSISTENCE RESERVE OUT parameter list
- */
-typedef struct scsi_prout_plist {
-	uint8_t			reservation_key[8];
-	uint8_t			service_key[8];
-	uint8_t			obsolete1[4];
-	uint8_t			resbits1 : 4,
-				spec_i_pt : 1,
-				all_tg_pt : 1,
-				resbits2 : 1,
-				aptpl : 1;
-	uint8_t			resbytes1;
-	uint8_t			obsolete2[2];
-	uint8_t			apd[1];
-} scsi_prout_plist_t;
-
-#else
-#error	One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
-#endif	/* _BIT_FIELDS_LTOH */
-
-
-/*
- * SPC-3 revision 23, Section 6.12.2, Table 113
- * Persistent Reservations
- * Persistent Reserve Out service action codes
- */
-#define	PR_OUT_REGISTER		0x0	/* Register/unregister a reservation */
-					/* key with the device server */
-#define	PR_OUT_RESERVE		0x1	/* Create a persistent reservation */
-					/* having a specified SCOPE & TYPE */
-#define	PR_OUT_RELEASE		0x2	/* Release the selected persistent */
-					/* reservation */
-#define	PR_OUT_CLEAR		0x3	/* Clears all reservation keys and */
-					/* all persistent reservations */
-#define	PR_OUT_PREEMPT		0x4	/* Preempts persistent reservations */
-					/* and/or removes reservations */
-#define	PR_OUT_PREEMPT_ABORT	0x5	/* Preempts persistent reservations */
-					/* and/or removes reservations, and */
-					/* aborts all tasks for all preempted */
-					/* I_T nexuses */
-#define	PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY	0x06
-					/* Register a reservation key with */
-					/* the device server, or unregister a */
-					/* reservation key */
-#define	PR_OUT_REGISTER_MOVE	0x7	/* Register a reservation key for */
-					/* another I_T nexus with the device */
-					/* server and move a persistent */
-					/* reservation to the I_T nexus */
-
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* _SYS_SCSI_GENERIC_PERSIST_H */
--- a/usr/src/cmd/iscsi/iscsitgtd/Makefile	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/Makefile	Thu Sep 13 15:17:59 2007 -0700
@@ -31,7 +31,7 @@
 OBJS	+= iscsi_authclient.o iscsi_authglue.o iscsi_cmd.o iscsi_conn.o
 OBJS	+= iscsi_crc.o iscsi_ffp.o iscsi_login.o iscsi_sess.o radius.o
 OBJS	+= t10_sam.o t10_spc.o t10_sbc.o t10_raw_if.o t10_ssc.o t10_osd.o
-OBJS	+= util.o util_err.o util_ifname.o util_port.o util_queue.o
+OBJS	+= t10_spc_pr.o util.o util_err.o util_ifname.o util_port.o util_queue.o
 OBJS	+= isns_client.o isns.o
 POFILE= iscsitgtd.po
 POFILES	= $(OBJS:%.o=%.po)
--- a/usr/src/cmd/iscsi/iscsitgtd/Makefile.com	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/Makefile.com	Thu Sep 13 15:17:59 2007 -0700
@@ -34,7 +34,7 @@
 COBJS	+= iscsi_authclient.o iscsi_authglue.o iscsi_cmd.o iscsi_conn.o
 COBJS	+= iscsi_crc.o iscsi_ffp.o iscsi_login.o iscsi_sess.o radius.o
 COBJS	+= t10_sam.o t10_spc.o t10_sbc.o t10_raw_if.o t10_ssc.o t10_osd.o
-COBJS	+= util.o util_err.o util_ifname.o util_port.o util_queue.o
+COBJS	+= t10_spc_pr.o util.o util_err.o util_ifname.o util_port.o util_queue.o
 COBJS	+= isns_client.o isns.o
 OBJS=	$(COBJS) $(DSRC:%.d=%.o)
 SRCS=	$(COBJS:%.o=../%.c) $(COMMON_SRCS)
--- a/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.c	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.c	Thu Sep 13 15:17:59 2007 -0700
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -71,8 +71,7 @@
 Boolean_t
 session_alloc(iscsi_conn_t *c, uint8_t *isid)
 {
-	iscsi_sess_t	*s,
-			*n;
+	iscsi_sess_t	*s, *n;
 
 	if (c->c_sess != NULL)
 		return (True);
@@ -187,8 +186,7 @@
 convert_i_local(char *ip, char **rtn)
 {
 	tgt_node_t	*inode = NULL;
-	char		*iname,
-			*name;
+	char		*iname, *name;
 
 	while ((inode = tgt_node_next(main_config, XML_ELEMENT_INIT, inode)) !=
 	    NULL) {
@@ -298,8 +296,7 @@
 	mgmt_request_t	*mgmt;
 	name_request_t	*nr;
 	t10_cmd_t	*t10_cmd;
-	char		**buf,
-			local_buf[16];
+	char		**buf, local_buf[16];
 	int		lun;
 	extern void dataout_callback(t10_cmd_t *t, char *data, size_t *xfer);
 
@@ -321,8 +318,9 @@
 				 * XXX Need to rethink how I should do
 				 * the callback.
 				 */
-				s->s_t10 = t10_handle_create(s->s_t_name,
-				    T10_TRANS_ISCSI, s->s_conn_head->c_tpgt,
+				s->s_t10 = t10_handle_create(
+				    s->s_t_name, s->s_i_name, T10_TRANS_ISCSI,
+				    s->s_conn_head->c_tpgt,
 				    s->s_conn_head->c_max_burst_len,
 				    s->s_t10q, dataout_callback);
 			}
@@ -681,7 +679,7 @@
 		}
 
 		if (iscsiAuthClientSetVersion(auth_client,
-			iscsiAuthVersionRfc) != iscsiAuthStatusNoError) {
+		    iscsiAuthVersionRfc) != iscsiAuthStatusNoError) {
 			syslog(LOG_ERR, "iscsi connection login failed - "
 			    "unable to set version\n");
 			return;
@@ -716,8 +714,7 @@
 		}
 
 		if (iscsiAuthClientSetAuthRemote(auth_client,
-			isp->sess_auth.auth_enabled) !=
-		    iscsiAuthStatusNoError) {
+		    isp->sess_auth.auth_enabled) != iscsiAuthStatusNoError) {
 			syslog(LOG_ERR, "iscsi connection login failed - "
 			    "unable to set remote authentication\n");
 			return;
--- a/usr/src/cmd/iscsi/iscsitgtd/main.c	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/main.c	Thu Sep 13 15:17:59 2007 -0700
@@ -89,7 +89,8 @@
 Boolean_t	enforce_strict_guid	= True,
 		thin_provisioning	= False,
 		disable_tpgs		= False,
-		dbg_timestamps		= False;
+		dbg_timestamps		= False,
+		pgr_persist		= True;
 int		targets_vers_maj,
 		targets_vers_min,
 		main_vers_maj,
@@ -370,6 +371,8 @@
 		    &disable_tpgs);
 		(void) tgt_find_value_boolean(node, XML_ELEMENT_TIMESTAMPS,
 		    &dbg_timestamps);
+		(void) tgt_find_value_boolean(node, XML_ELEMENT_PGR_PERSIST,
+		    &pgr_persist);
 		if (tgt_find_value_int(node, XML_ELEMENT_LOGLVL,
 		    &qlog_lvl) == True)
 			queue_log(True);
--- a/usr/src/cmd/iscsi/iscsitgtd/queue.h	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/queue.h	Thu Sep 13 15:17:59 2007 -0700
@@ -41,29 +41,39 @@
 
 #include <iscsitgt_impl.h>
 
-#define	Q_CONN_ERRS	0x00001
-#define	Q_CONN_LOGIN	0x00002
-#define	Q_CONN_NONIO	0x00004
-#define	Q_CONN_IO	0x00008
+/* Connections */
+#define	Q_CONN_ERRS	0x00000001
+#define	Q_CONN_LOGIN	0x00000002
+#define	Q_CONN_NONIO	0x00000004
+#define	Q_CONN_IO	0x00000008
+
+/* Sessions */
+#define	Q_SESS_ERRS	0x00000010
+#define	Q_SESS_LOGIN	0x00000020
+#define	Q_SESS_NONIO	0x00000040
+#define	Q_SESS_IO	0x00000080
 
-#define	Q_SESS_ERRS	0x00010
-#define	Q_SESS_LOGIN	0x00020
-#define	Q_SESS_NONIO	0x00040
-#define	Q_SESS_IO	0x00080
+/* SCSI Target Emulation */
+#define	Q_STE_ERRS	0x00000100
+#define	Q_STE_NONIO	0x00000200
+#define	Q_STE_IO	0x00000400
 
-#define	Q_STE_ERRS	0x00100
-#define	Q_STE_NONIO	0x00200
-#define	Q_STE_IO	0x00400
+/* General Errors */
+#define	Q_GEN_ERRS	0x00001000
+#define	Q_GEN_DETAILS	0x00002000
 
-#define	Q_GEN_ERRS	0x01000
-#define	Q_GEN_DETAILS	0x02000
+/* ISCSI Debugging */
+#define	Q_ISNS_DBG	0x00004000
 
-#define	Q_ISNS_DBG	0x10000
+/* Persistent Reservations */
+#define	Q_PR_ERRS	0x00010000
+#define	Q_PR_NONIO	0x00020000
+#define	Q_PR_IO		0x00040000
 
 /*
  * When used the queue request will be place at the head of the queue.
  */
-#define	Q_HIGH		0x10000
+#define	Q_HIGH		0x80000000
 
 extern int qlog_lvl;
 
--- a/usr/src/cmd/iscsi/iscsitgtd/t10.h	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10.h	Thu Sep 13 15:17:59 2007 -0700
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -126,6 +126,8 @@
 #define	T10_CMD_RESID(cmd)	(cmd->c_resid)
 #define	T10_SENSE_LEN(cmd)	(cmd->c_cmd_sense_len)
 #define	T10_SENSE_DATA(cmd)	(cmd->c_cmd_sense)
+#define	T10_PGR_TNAME(cmd)	(cmd->c_lu->l_targ->s_targ_base)
+#define	T10_PGR_INAME(cmd)	(cmd->c_lu->l_targ->s_i_name)
 
 #define	T10_DEFAULT_TPG	1
 
@@ -341,7 +343,7 @@
 	Boolean_t		l_fast_write_ack;
 
 	/*
-	 * AVL tree containing all I_T_Q nexus' which are actively using
+	 * AVL tree containing all I_T_L nexus' which are actively using
 	 * this LUN.
 	 */
 	avl_tree_t		l_all_open;
@@ -366,7 +368,7 @@
 } t10_lu_common_t;
 
 /*
- * Each I_T_Q has a LU structure associated with it.
+ * Each I_T_L has a LU structure associated with it.
  */
 typedef struct t10_lu_impl {
 	/*
@@ -427,6 +429,7 @@
 	int			l_targ_lun;
 
 	Boolean_t		l_dsense_enabled;
+	Boolean_t		l_pgr_read;
 
 	/*
 	 * Statistics on a per ITL basis
@@ -449,6 +452,7 @@
 } t10_lu_impl_t;
 
 typedef struct t10_targ_impl {
+	char			*s_i_name;
 	char			*s_targ_base;
 	int			s_targ_num; /* used in log messages */
 	avl_tree_t		s_open_lu;
@@ -463,7 +467,7 @@
 	/*
 	 * Target Port Set
 	 */
-	int			s_tp_grp;
+	int			s_tpgt;
 
 	/*
 	 * transport version number to use in standard inquiry data
@@ -528,8 +532,8 @@
  * t10_handle_create -- create target handle to be used by transports
  */
 t10_targ_handle_t
-t10_handle_create(char *targ_name, int trans_version, int tpg, int max_out,
-    target_queue_t *transq, void (*datain_cb)(t10_cmd_t *, char *, size_t *));
+t10_handle_create(char *targ, char *init, int trans_vers, int tpg, int max_out,
+    target_queue_t *tq, void (*datain_cb)(t10_cmd_t *, char *, size_t *));
 
 /*
  * t10_handle_disable -- drains commands from emulation queues
--- a/usr/src/cmd/iscsi/iscsitgtd/t10_raw_if.c	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_raw_if.c	Thu Sep 13 15:17:59 2007 -0700
@@ -234,8 +234,8 @@
 static void
 raw_read_tape(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
 {
-	size_t		req_len,
-			xfer;
+	size_t		req_len;
+	size_t		xfer;
 	off_t		offset		= 0;
 	raw_io_t	*io;
 	Boolean_t	last;
@@ -285,8 +285,8 @@
 	union scsi_cdb	*u		= (union scsi_cdb *)cdb;
 	diskaddr_t	addr;
 	off_t		offset		= 0;
-	uint32_t	cnt,
-			min;
+	uint32_t	cnt;
+	uint32_t	min;
 	raw_io_t	*io;
 	uint64_t	err_blkno;
 	int		sense_len;
@@ -499,8 +499,8 @@
 static void
 raw_write_tape(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
 {
-	size_t		request_len,
-			xfer;
+	size_t		request_len;
+	size_t		xfer;
 	raw_io_t	*io;
 
 	request_len	= (cdb[2] << 16) | (cdb[3] << 8) | cdb[4];
@@ -824,6 +824,37 @@
 }
 
 static void
+raw_persist_in(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+	raw_io_t	*io;
+
+	if ((io = do_datain(cmd, cdb, CDB_GROUP1, 0)) == NULL) {
+		trans_send_complete(cmd, STATUS_CHECK);
+	} else {
+		trans_send_complete(cmd, io->r_status);
+		raw_free_io(io);
+	}
+}
+
+static void
+raw_persist_out(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+	size_t		len;
+
+	len = (cdb[5] << 24) | (cdb[6] << 16) | (cdb[7] << 8) | cdb[8];
+	do_dataout(cmd, cdb, cdb_len, len);
+}
+
+/*ARGSUSED*/
+static void
+raw_persist_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
+    size_t data_len)
+{
+	raw_io_t	*io = (raw_io_t *)id;
+	trans_send_complete(cmd, do_uscsi(cmd, io, RawDataToDevice));
+}
+
+static void
 raw_msense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
 {
 	raw_io_t	*io;
@@ -1351,8 +1382,8 @@
 	{ spc_unsupported,	NULL,	NULL,	NULL },
 	{ spc_unsupported,	NULL,	NULL,	NULL },
 	{ spc_unsupported,	NULL,	NULL,	NULL },
-	{ spc_unsupported,	NULL,	NULL,	NULL },
-	{ spc_unsupported,	NULL,	NULL,	NULL },
+	{ raw_persist_in,	NULL,	NULL,	"PERSISTENT_RESERVE_IN" },
+	{ raw_persist_out, raw_persist_data, NULL, "PERSISTENT_RESERVE_OUT" },
 
 	/* 0x60 -- 0x6f */
 	{ spc_unsupported,	NULL,	NULL,	NULL },
--- a/usr/src/cmd/iscsi/iscsitgtd/t10_sam.c	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_sam.c	Thu Sep 13 15:17:59 2007 -0700
@@ -99,7 +99,7 @@
 static sam_device_table_t sam_emul_table[];
 
 /*
- * Global variables
+ * Local variables
  */
 static avl_tree_t	lu_list;
 static pthread_mutex_t	lu_list_mutex;
@@ -205,7 +205,7 @@
  * []----
  */
 t10_targ_handle_t
-t10_handle_create(char *targ, int trans_version, int tpg, int max_out,
+t10_handle_create(char *targ, char *init, int trans_vers, int tpg, int max_out,
     target_queue_t *tq, void (*datain_cb)(t10_cmd_t *, char *, size_t *))
 {
 	t10_targ_impl_t	*t = calloc(1, sizeof (t10_targ_impl_t));
@@ -217,7 +217,8 @@
 	t->s_targ_num		= t10_num++;
 	(void) pthread_mutex_unlock(&t10_mutex);
 	t->s_targ_base		= strdup(targ);
-	t->s_trans_vers		= trans_version;
+	t->s_i_name		= strdup(init);
+	t->s_trans_vers		= trans_vers;
 	t->s_maxout		= max_out;
 	t->s_to_transport	= tq;
 	t->s_dataout_cb		= datain_cb;
@@ -234,13 +235,13 @@
 	 * to determine relative path numbering there's no issue with changing
 	 * this later if need be.
 	 */
-	switch (trans_version) {
+	switch (trans_vers) {
 	case T10_TRANS_ISCSI:
-		t->s_tp_grp	= 0x0000 | tpg;
+		t->s_tpgt	= 0x0000 | tpg;
 		break;
 
 	case T10_TRANS_FC:
-		t->s_tp_grp	= 0x8000 | tpg;
+		t->s_tpgt	= 0x8000 | tpg;
 		break;
 	}
 
@@ -286,8 +287,8 @@
 {
 	t10_targ_impl_t	*t		= (t10_targ_impl_t *)tp;
 	t10_lu_impl_t	*l;
-	t10_cmd_t	*c,
-			*c2free;
+	t10_cmd_t	*c;
+	t10_cmd_t	*c2free;
 	int		fast_free	= 0;
 
 	(void) pthread_mutex_lock(&t->s_mutex);
@@ -348,6 +349,7 @@
 	(void) pthread_mutex_unlock(&t->s_mutex);
 
 	free(t->s_targ_base);
+	free(t->s_i_name);
 	free(t);
 }
 
@@ -689,8 +691,8 @@
 t10_task_mgmt(t10_targ_handle_t t1, TaskOp_t op, int opt_lun, void *tag)
 {
 	t10_targ_impl_t	*t = (t10_targ_impl_t *)t1;
-	t10_lu_impl_t	search,
-			*lu;
+	t10_lu_impl_t	search;
+	t10_lu_impl_t	*lu;
 
 	switch (op) {
 	case InventoryChange:
@@ -753,8 +755,8 @@
 {
 	t10_targ_impl_t	*t = (t10_targ_impl_t *)t1;
 	t10_lu_impl_t	*itl;
-	char		lb[32],
-			*p;
+	char		lb[32];
+	char		*p;
 
 	/*
 	 * It's possible for the management interfaces to request stats
@@ -819,8 +821,8 @@
 	t10_cmd_t		*cmd		= NULL;
 	uint8_t			cdb[16];	/* ---- fake buffer ---- */
 	diskaddr_t		offset		= 0;
-	size_t			size,
-				sync_size;
+	size_t			size;
+	size_t			sync_size;
 	msg_t			*m		= NULL;
 	target_queue_t		*rq		= NULL;
 	char			path[MAXPATHLEN];
@@ -835,7 +837,7 @@
 	 * having something fixed/change in one location that isn't
 	 * in another. Obvious right?
 	 */
-	if ((t = t10_handle_create(target, 0, 0, 0, q, NULL)) == NULL) {
+	if ((t = t10_handle_create(target, "", 0, 0, 0, q, NULL)) == NULL) {
 		queue_prt(mgmtq, Q_STE_ERRS, "STE%x  Failed to create handle\n",
 		    lun);
 		return (False);
@@ -1311,19 +1313,19 @@
 static Boolean_t
 t10_find_lun(t10_targ_impl_t *t, int lun, t10_cmd_t *cmd)
 {
-	t10_lu_impl_t		*l		= NULL,
-				search;
-	avl_index_t		wc		= 0, /* where common */
-				wt		= 0; /* where target */
-	char			*guid		= NULL,
-				*str,
-				*dataset	= NULL;
-	t10_lu_common_t		lc,
-				*common		= NULL;
-	tgt_node_t		*n		= NULL,
-				*n1,
-				*targ,
-				*ll;
+	t10_lu_impl_t		*l		= NULL;
+	t10_lu_impl_t		search;
+	avl_index_t		wc		= 0; /* where common */
+	avl_index_t		wt		= 0; /* where target */
+	char			*guid		= NULL;
+	char			*str;
+	char			*dataset	= NULL;
+	t10_lu_common_t		lc;
+	t10_lu_common_t		*common		= NULL;
+	tgt_node_t		*n		= NULL;
+	tgt_node_t		*n1;
+	tgt_node_t		*targ;
+	tgt_node_t		*ll;
 	xmlTextReaderPtr	r		= NULL;
 	char			path[MAXPATHLEN];
 	int			xml_fd		= -1;
@@ -1702,11 +1704,11 @@
 	msg_t		*m;
 	t10_lu_impl_t	*itl;
 	t10_cmd_t	*cmd;
-	char		*data,
-			*path;
-	size_t		data_len,
-			new_size,
-			offset;
+	char		*data;
+	char		*path;
+	size_t		data_len;
+	size_t		new_size;
+	size_t		offset;
 	ssize_t		cc;
 	void		*provo_err;
 	t10_shutdown_t	*s;
@@ -1736,8 +1738,8 @@
 				trans_send_complete(cmd, STATUS_CHECK);
 			} else {
 				lu->l_curr		= cmd;
-				(*cmd->c_lu->l_cmd)(cmd, cmd->c_cdb,
-						    cmd->c_cdb_len);
+				(*cmd->c_lu->l_cmd)
+				    (cmd, cmd->c_cdb, cmd->c_cdb_len);
 				lu->l_curr		= NULL;
 			}
 			break;
@@ -1896,13 +1898,13 @@
 				    cmd->c_data, cmd->c_data_len);
 				cmd->c_lu->l_cmds_read++;
 				cmd->c_lu->l_sects_read +=
-					cmd->c_data_len / 512;
+				    cmd->c_data_len / 512;
 				bcopy(cmd->c_data,
 				    (char *)lu->l_mmap + cmd->c_offset,
 				    cmd->c_data_len);
 				cmd->c_lu->l_cmds_write++;
 				cmd->c_lu->l_sects_write +=
-					cmd->c_data_len / 512;
+				    cmd->c_data_len / 512;
 				lu->l_curr		= NULL;
 				lu->l_curr_provo	= False;
 				provo_err		= 0;
@@ -1920,7 +1922,7 @@
 					    lu->l_num, errno);
 				}
 				provo_err = (cc == cmd->c_data_len) ?
-					(void *)0 : (void *)1;
+				    (void *)0 : (void *)1;
 			}
 			/*
 			 * acknowledge this op and wait for next
@@ -2119,13 +2121,13 @@
 static Boolean_t
 load_params(t10_lu_common_t *lu, char *basedir)
 {
-	char		file[MAXPATHLEN],
-			*str;
+	char		file[MAXPATHLEN];
+	char		*str;
 	int		oflags		= O_RDWR|O_LARGEFILE|O_NDELAY;
 	Boolean_t	mmap_lun	= True;
 	tgt_node_t	*node		= NULL;
-	int		version_maj	= XML_VERS_LUN_MAJ,
-			version_min	= XML_VERS_LUN_MIN;
+	int		version_maj	= XML_VERS_LUN_MAJ;
+	int		version_min	= XML_VERS_LUN_MIN;
 
 	/*
 	 * Clean up from previous call to this function. This occurs if
@@ -2398,8 +2400,8 @@
 static int
 find_lu_by_num(const void *v1, const void *v2)
 {
-	t10_lu_impl_t	*l1	= (t10_lu_impl_t *)v1,
-			*l2	= (t10_lu_impl_t *)v2;
+	t10_lu_impl_t	*l1	= (t10_lu_impl_t *)v1;
+	t10_lu_impl_t	*l2	= (t10_lu_impl_t *)v2;
 
 	if (l1->l_targ_lun < l2->l_targ_lun)
 		return (-1);
@@ -2416,8 +2418,8 @@
 static int
 find_lu_by_guid(const void *v1, const void *v2)
 {
-	t10_lu_common_t	*l1	= (t10_lu_common_t *)v1,
-			*l2	= (t10_lu_common_t *)v2;
+	t10_lu_common_t	*l1	= (t10_lu_common_t *)v1;
+	t10_lu_common_t	*l2	= (t10_lu_common_t *)v2;
 	int		i;
 
 	if (l1->l_guid_len != l2->l_guid_len) {
@@ -2445,8 +2447,8 @@
 static int
 find_lu_by_targ(const void *v1, const void *v2)
 {
-	t10_lu_impl_t	*l1	= (t10_lu_impl_t *)v1,
-			*l2	= (t10_lu_impl_t *)v2;
+	t10_lu_impl_t	*l1	= (t10_lu_impl_t *)v1;
+	t10_lu_impl_t	*l2	= (t10_lu_impl_t *)v2;
 
 	if ((uint64_t)(uintptr_t)l1->l_targ < (uint64_t)(uintptr_t)l2->l_targ)
 		return (-1);
@@ -2465,8 +2467,8 @@
 static int
 find_cmd_by_addr(const void *v1, const void *v2)
 {
-	uint64_t	cmd1	= (uint64_t)(uintptr_t)v1,
-			cmd2	= (uint64_t)(uintptr_t)v2;
+	uint64_t	cmd1	= (uint64_t)(uintptr_t)v1;
+	uint64_t	cmd2	= (uint64_t)(uintptr_t)v2;
 
 	if (cmd1 < cmd2)
 		return (-1);
--- a/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.c	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.c	Thu Sep 13 15:17:59 2007 -0700
@@ -49,10 +49,21 @@
 
 #include "t10.h"
 #include "t10_spc.h"
+#include "t10_spc_pr.h"
 #include "t10_sbc.h"
 #include "utility.h"
 
 /*
+ * External declarations
+ */
+void sbc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
+void spc_cmd_pr_in(t10_cmd_t *, uint8_t *, size_t);
+void spc_cmd_pr_out(t10_cmd_t *, uint8_t *, size_t);
+void spc_cmd_pr_out_data(t10_cmd_t *, emul_handle_t, size_t, char *, size_t);
+void spc_pr_read(t10_cmd_t *);
+Boolean_t spc_pgr_check(t10_cmd_t *, uint8_t *);
+
+/*
  * Forward declarations
  */
 static int sbc_mmap_overlap(const void *v1, const void *v2);
@@ -60,7 +71,6 @@
 static void sbc_overlap_free(disk_io_t *io);
 static void sbc_overlap_check(disk_io_t *io);
 static void sbc_overlap_flush(disk_params_t *d);
-static void sbc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len);
 static void sbc_data(t10_cmd_t *cmd, emul_handle_t e, size_t offset,
     char *data, size_t data_len);
 static disk_io_t *sbc_io_alloc(t10_cmd_t *c);
@@ -160,8 +170,10 @@
 {
 	disk_params_t	*d = (disk_params_t *)itl->l_common->l_dtype_params;
 
-	if (d->d_state == lu_online)
+	if (d->d_state == lu_online) {
 		itl->l_cmd	= sbc_cmd;
+		itl->l_pgr_read = False;	/* Look for PGR data */
+	}
 	else
 		itl->l_cmd	= spc_cmd_offline;
 	itl->l_data	= sbc_data;
@@ -171,22 +183,6 @@
 void
 sbc_per_fini(t10_lu_impl_t *itl)
 {
-	disk_params_t	*d = (disk_params_t *)itl->l_common->l_dtype_params;
-	t10_lu_impl_t	*lu;
-
-	if (d->d_reserve_owner == itl) {
-
-		/*
-		 * Since we currently own the reservation, drop it,
-		 * and restore everyone elses command pointer.
-		 */
-		lu = avl_first(&itl->l_common->l_all_open);
-		do {
-			lu->l_cmd = sbc_cmd;
-			lu = AVL_NEXT(&itl->l_common->l_all_open, lu);
-		} while (lu != NULL);
-		d->d_reserve_owner = NULL;
-	}
 }
 
 /*
@@ -196,11 +192,19 @@
  * | This routine is called from within the SAM-3 Task router.
  * []----
  */
-static void
+void
 sbc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
 {
 	scsi_cmd_table_t	*e;
 
+	/*
+	 * Determine if there is persistent data for this I_T_L Nexus
+	 */
+	if (cmd->c_lu->l_pgr_read == False) {
+		spc_pr_read(cmd);
+		cmd->c_lu->l_pgr_read = True;
+	}
+
 	e = &cmd->c_lu->l_cmd_table[cdb[0]];
 #ifdef FULL_DEBUG
 	queue_prt(mgmtq, Q_STE_IO, "SBC%x  LUN%d Cmd %s id=%p\n",
@@ -215,34 +219,60 @@
  * | sbc_cmd_reserve -- Run commands when another I_T_L has a reservation
  * []----
  */
-static void
+void
 sbc_cmd_reserved(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
 {
-	scsi_cmd_table_t	*e;
+	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+	sbc_reserve_t		*res = &p->d_sbc_reserve;
+	Boolean_t		conflict = False;
 
+	/*
+	 * SPC-3, revision 23, Table 31
+	 * SPC commands that are allowed in the presence of various reservations
+	 */
 	switch (cdb[0]) {
-	case SCMD_TEST_UNIT_READY:
 	case SCMD_INQUIRY:
+	case SCMD_LOG_SENSE_G1:
+	case SCMD_PERSISTENT_RESERVE_IN:
+	case SCMD_READ_MEDIA_SERIAL:
 	case SCMD_REPORT_LUNS:
-	case SCMD_LOG_SENSE_G1:
-	case SCMD_READ_MEDIA_SERIAL:
 	case SCMD_REPORT_TARGET_PORT_GROUPS:
 	case SCMD_REQUEST_SENSE:
-		/*
-		 * SPC-2, revision 20, Section 5.5.1 table 10
-		 * The specification allows these three commands
-		 * to run even through there's a reservation in place.
-		 */
-		e = &cmd->c_lu->l_cmd_table[cdb[0]];
-#ifdef FULL_DEBUG
-		queue_prt(mgmtq, Q_STE_IO, "RESERVED: SBC%x  LUN%d Cmd %s\n",
-		    cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
-		    e->cmd_name == NULL ? "(no name)" : e->cmd_name);
-#endif
-		(*e->cmd_start)(cmd, cdb, cdb_len);
+	case SCMD_TEST_UNIT_READY:
 		break;
+	default:
+		pthread_rwlock_rdlock(&res->res_rwlock);
+		switch (res->res_type) {
+		case RT_NONE:
+			/* conflict = False; */
+			break;
+		case RT_PGR:
+			conflict = spc_pgr_check(cmd, cdb);
+			break;
+		default:
+			conflict = True;
+			break;
+		}
+		pthread_rwlock_unlock(&res->res_rwlock);
+	}
 
-	default:
+	queue_prt(mgmtq, Q_PR_IO,
+	    "PGR%x LUN%d CDB:%s - sbc_cmd_reserved(%s:%s)\n",
+	    cmd->c_lu->l_targ->s_targ_num,
+	    cmd->c_lu->l_common->l_num,
+	    cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name == NULL
+	    ? "(no name)"
+	    : cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name,
+	    res->res_type == RT_PGR ? "PGR" :
+	    res->res_type == RT_NONE ? "" : "unknown",
+	    conflict ? "Conflict" : "Allowed");
+
+	/*
+	 * If no conflict at this point, allow command
+	 */
+	if (conflict == False) {
+		sbc_cmd(cmd, cdb, cdb_len);
+	} else {
 		trans_send_complete(cmd, STATUS_RESERVATION_CONFLICT);
 	}
 }
@@ -295,8 +325,8 @@
 	union scsi_cdb	*u		= (union scsi_cdb *)cdb;
 	diskaddr_t	addr;
 	off_t		offset		= 0;
-	uint32_t	cnt,
-			min;
+	uint32_t	cnt;
+	uint32_t	min;
 	disk_io_t	*io;
 	void		*mmap_data	= T10_MMAP_AREA(cmd);
 	uint64_t	err_blkno;
@@ -984,7 +1014,7 @@
 	 * to contain it's size.
 	 */
 	mode_hdr->length = sizeof (struct mode_header) - 1 +
-		MODE_BLK_DESC_LENGTH;
+	    MODE_BLK_DESC_LENGTH;
 	mode_hdr->bdesc_length	= MODE_BLK_DESC_LENGTH;
 
 	/*
@@ -1057,7 +1087,7 @@
 			    sizeof (struct mode_geometry);
 
 		np = io->da_data + sizeof (*mode_hdr) +
-			mode_hdr->bdesc_length;
+		    mode_hdr->bdesc_length;
 		if (io->da_data_len < (sizeof (struct mode_format) +
 		    sizeof (struct mode_geometry) +
 		    sizeof (struct mode_cache_scsi3) +
@@ -1207,8 +1237,8 @@
 static void
 sbc_read_capacity16(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
 {
-	uint64_t		capacity,
-				lba;
+	uint64_t		capacity;
+	uint64_t		lba;
 	int			rep_size;	/* response data size */
 	struct scsi_capacity_16	*cap16;
 	disk_params_t		*d;
@@ -1245,9 +1275,9 @@
 	}
 
 	lba = (uint64_t)cdb[2] << 56 | (uint64_t)cdb[3] << 48 |
-		(uint64_t)cdb[4] << 40 | (uint64_t)cdb[5] << 32 |
-		(uint64_t)cdb[6] << 24 | (uint64_t)cdb[7] << 16 |
-		(uint64_t)cdb[8] << 8 | (uint64_t)cdb[9];
+	    (uint64_t)cdb[4] << 40 | (uint64_t)cdb[5] << 32 |
+	    (uint64_t)cdb[6] << 24 | (uint64_t)cdb[7] << 16 |
+	    (uint64_t)cdb[8] << 8 | (uint64_t)cdb[9];
 
 	io = sbc_io_alloc(cmd);
 
@@ -1286,100 +1316,15 @@
 
 /*ARGSUSED*/
 static void
-sbc_reserve(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
-{
-	disk_params_t	*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	t10_lu_impl_t	*lu;
-
-	if (p == NULL)
-		return;
-
-	if (cdb[1] & 0xe0 || SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
-		spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
-		spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
-		trans_send_complete(cmd, STATUS_CHECK);
-		return;
-	}
-
-	if ((p->d_reserve_owner != NULL) &&
-	    (p->d_reserve_owner != cmd->c_lu)) {
-
-		trans_send_complete(cmd, STATUS_RESERVATION_CONFLICT);
-		return;
-
-	} else if (p->d_reserve_owner == cmd->c_lu) {
-
-		/*
-		 * According SPC-2 revision 20, section 7.21.2
-		 * It shall be permissible for an initiator to
-		 * reserve a logic unit that is currently reserved
-		 * by that initiator
-		 */
-		trans_send_complete(cmd, STATUS_GOOD);
-	} else {
-
-		lu = avl_first(&cmd->c_lu->l_common->l_all_open);
-		do {
-			if (lu != cmd->c_lu)
-				lu->l_cmd = sbc_cmd_reserved;
-			lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
-		} while (lu != NULL);
-		p->d_reserve_owner = cmd->c_lu;
-		trans_send_complete(cmd, STATUS_GOOD);
-	}
-}
-
-/*ARGSUSED*/
-static void
-sbc_release(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
-{
-	disk_params_t	*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
-	t10_lu_impl_t	*lu;
-
-	if (p == NULL)
-		return;
-
-	if (cdb[1] & 0xe0 || cdb[3] || cdb[4] ||
-	    SAM_CONTROL_BYTE_RESERVED(cdb[5])) {
-		spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
-		spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
-		trans_send_complete(cmd, STATUS_CHECK);
-		return;
-	}
-
-	if (p->d_reserve_owner == NULL) {
-
-		/*
-		 * If nobody is the owner this command is successful.
-		 */
-		trans_send_complete(cmd, STATUS_GOOD);
-		return;
-	}
-
-	/*
-	 * At this point the only way to get in here is to be the owner
-	 * of the reservation.
-	 */
-	lu = avl_first(&cmd->c_lu->l_common->l_all_open);
-	do {
-		lu->l_cmd = sbc_cmd;
-		lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
-	} while (lu != NULL);
-	p->d_reserve_owner = NULL;
-	trans_send_complete(cmd, STATUS_GOOD);
-}
-
-/*ARGSUSED*/
-static void
 sbc_verify(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
 {
 	/*LINTED*/
 	union scsi_cdb	*u		= (union scsi_cdb *)cdb;
 	diskaddr_t	addr;
-	uint32_t	cnt,
-			chk_size;
-	uint64_t	sz,
-			err_blkno;
+	uint32_t	cnt;
+	uint32_t	chk_size;
+	uint64_t	sz;
+	uint64_t	err_blkno;
 	Boolean_t	bytchk;
 	char		*chk_block;
 	disk_io_t	*io;
@@ -1572,6 +1517,7 @@
 		spc_sense_create(cmd, KEY_MISCOMPARE, 0);
 		spc_sense_ascq(cmd, SPC_ASC_DATA_PATH, SPC_ASCQ_DATA_PATH);
 		trans_send_complete(cmd, STATUS_CHECK);
+		free(on_disk_buf);
 		sbc_io_free(io);
 		return;
 	}
@@ -1579,6 +1525,7 @@
 		spc_sense_create(cmd, KEY_MISCOMPARE, 0);
 		spc_sense_ascq(cmd, SPC_ASC_MISCOMPARE, SPC_ASCQ_MISCOMPARE);
 		trans_send_complete(cmd, STATUS_CHECK);
+		free(on_disk_buf);
 		sbc_io_free(io);
 		return;
 	}
@@ -1776,8 +1723,8 @@
 static int
 sbc_mmap_overlap(const void *v1, const void *v2)
 {
-	disk_io_t	*d1	= (disk_io_t *)v1,
-			*d2	= (disk_io_t *)v2;
+	disk_io_t	*d1	= (disk_io_t *)v1;
+	disk_io_t	*d2	= (disk_io_t *)v2;
 
 	if ((d1->da_data + d1->da_data_len) < d2->da_data)
 		return (-1);
@@ -1904,8 +1851,8 @@
 	{ spc_unsupported,	NULL,	NULL,	NULL },
 	{ spc_unsupported,	NULL,	NULL,	NULL },
 	{ spc_mselect, spc_mselect_data, NULL,		"MODE_SELECT" },
-	{ sbc_reserve,		NULL,	NULL,		"RESERVE" },
-	{ sbc_release,		NULL,	NULL,		"RELEASE" },
+	{ spc_unsupported,	NULL,	NULL,	NULL },
+	{ spc_unsupported,	NULL,	NULL,	NULL },
 	{ spc_unsupported,	NULL,	NULL,	NULL },
 	{ spc_unsupported,	NULL,	NULL,	NULL },
 	{ sbc_msense,		NULL,	NULL,		"MODE_SENSE" },
@@ -1984,8 +1931,8 @@
 	{ spc_unsupported,	NULL,	NULL,	NULL },
 	{ spc_unsupported,	NULL,	NULL,	NULL },
 	{ spc_unsupported,	NULL,	NULL,	NULL },
-	{ spc_unsupported,	NULL,	NULL,	"PERSISTENT_IN" },
-	{ spc_unsupported,	NULL,	NULL,	"PERSISTENT_OUT" },
+	{ spc_cmd_pr_in,	NULL,	NULL,	"PERSISTENT_RESERVE_IN" },
+	{ spc_cmd_pr_out, spc_cmd_pr_out_data, NULL, "PERSISTENT_RESERVE_OUT" },
 
 	/* 0x60 -- 0x6f */
 	{ spc_unsupported,	NULL,	NULL,	NULL },
--- a/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.h	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.h	Thu Sep 13 15:17:59 2007 -0700
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -120,8 +120,7 @@
 
 	Boolean_t	d_fast_write;
 	t10_lu_state_t	d_state;
-
-	t10_lu_impl_t	*d_reserve_owner;
+	sbc_reserve_t	d_sbc_reserve;
 } disk_params_t;
 
 typedef struct disk_io {
--- a/usr/src/cmd/iscsi/iscsitgtd/t10_spc.c	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_spc.c	Thu Sep 13 15:17:59 2007 -0700
@@ -51,7 +51,7 @@
 #include "t10_spc.h"
 #include "target.h"
 
-static void spc_free(emul_handle_t id);
+void spc_free(emul_handle_t id);
 
 /*
  * []----
@@ -134,13 +134,13 @@
 void
 spc_inquiry(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
 {
-	uint8_t			*rsp_buf,
-				*rbp;		/* temporary var */
+	uint8_t			*rsp_buf;
+	uint8_t			*rbp;		/* temporary var */
 	struct scsi_inquiry	*inq;
-	uint32_t		len,
-				page83_len,
-				rqst_len,
-				rtn_len;
+	uint32_t		len;
+	uint32_t		page83_len;
+	uint32_t		rqst_len;
+	uint32_t		rtn_len;
 	struct vpd_hdr		*vhp;
 	struct vpd_desc		vd;
 	size_t			scsi_len;
@@ -188,7 +188,7 @@
 	 */
 	scsi_len = ((strlen(cmd->c_lu->l_targ->s_targ_base) + 1) + 3) & ~3;
 	page83_len = (sizeof (struct vpd_desc) * 6) + scsi_len +
-		(lu->l_guid_len * 3) + (sizeof (uint32_t) * 2);
+	    (lu->l_guid_len * 3) + (sizeof (uint32_t) * 2);
 
 	/*
 	 * We always allocate enough space so that the code can create
@@ -396,8 +396,8 @@
 			bcopy(&vd, rbp, sizeof (vd));
 			rbp		+= sizeof (vd);
 
-			rbp[2]	= hibyte(loword(cmd->c_lu->l_targ->s_tp_grp));
-			rbp[3]	= lobyte(loword(cmd->c_lu->l_targ->s_tp_grp));
+			rbp[2]	= hibyte(loword(cmd->c_lu->l_targ->s_tpgt));
+			rbp[3]	= lobyte(loword(cmd->c_lu->l_targ->s_tpgt));
 			rbp	+= vd.len;
 
 			/* ---- VPD descriptor ---- */
@@ -588,16 +588,16 @@
 {
 	int		expected_data;
 	uint8_t		*buf			= NULL;
-	int		entries			= 0,
-			len,
-			len_network,
-			select,
-			lun_idx,
-			lun_val;
+	int		entries			= 0;
+	int		len;
+	int		len_network;
+	int		select;
+	int		lun_idx;
+	int		lun_val;
 	char		*str;
-	tgt_node_t	*targ,
-			*lun_list,
-			*lun;
+	tgt_node_t	*targ;
+	tgt_node_t	*lun_list;
+	tgt_node_t	*lun;
 
 	/*
 	 * SPC-3 Revision 21c section 6.21
@@ -702,9 +702,9 @@
 	rtpg_hdr_t		*r;
 	rtpg_desc_t		*dp;
 	rtpg_targ_desc_t	*tp;
-	int			rqst_len,
-				alloc_len,
-				i;
+	int			rqst_len;
+	int			alloc_len;
+	int			i;
 	t10_lu_common_t		*lu	= cmd->c_lu->l_common;
 	t10_lu_impl_t		*lu_per;
 
@@ -778,8 +778,8 @@
 	tp		= &dp->targ_list[0];
 	lu_per		= avl_first(&lu->l_all_open);
 	do {
-		tp->rel_tpi[0] = hibyte(loword(lu_per->l_targ->s_tp_grp));
-		tp->rel_tpi[1] = lobyte(loword(lu_per->l_targ->s_tp_grp));
+		tp->rel_tpi[0] = hibyte(loword(lu_per->l_targ->s_tpgt));
+		tp->rel_tpi[1] = lobyte(loword(lu_per->l_targ->s_tpgt));
 		lu_per = AVL_NEXT(&lu->l_all_open, lu_per);
 		tp++;
 	} while (lu_per != NULL);
@@ -815,7 +815,7 @@
 	trans_send_complete(cmd, STATUS_GOOD);
 }
 
-static void
+void
 spc_free(emul_handle_t id)
 {
 	free(id);
@@ -1139,14 +1139,13 @@
 		 * SAM-3 revision 14, Section 4.9.7.
 		 * 14-bit flat address space.
 		 */
-		buf[0] = SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE |
-			(lun >> 8 & 0x3f);
+		buf[0] = SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE | (lun >> 8 & 0x3f);
 		buf[1] = lun & 0xff;
 
 	} else if (select_field == SCSI_REPORTLUNS_SELECT_ALL) {
 
 		buf[0] = SCSI_REPORTLUNS_ADDRESS_EXTENDED_UNIT |
-			SCSI_REPORTLUNS_ADDRESS_EXTENDED_6B;
+		    SCSI_REPORTLUNS_ADDRESS_EXTENDED_6B;
 		/*
 		 * 32-bit limitation. This format should be able to
 		 * handle a 40-bit LUN.
--- a/usr/src/cmd/iscsi/iscsitgtd/t10_spc.h	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_spc.h	Thu Sep 13 15:17:59 2007 -0700
@@ -20,15 +20,19 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#ifndef _SPC_H
-#define	_SPC_H
+#ifndef _T10_SPC_H
+#define	_T10_SPC_H
 
 #pragma ident	"%Z%%M%	%I%	%E% SMI"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /*
  * []------------------------------------------------------------------[]
  * | SPC-3								|
@@ -98,33 +102,60 @@
  * | by the code.							|
  * []------------------------------------------------------------------[]
  */
-#define	SPC_ASC_INVALID_CDB	0x24
-#define	SPC_ASCQ_INVALID_CDB	0x00
-#define	SPC_ASC_PWR_RESET	0x29
-#define	SPC_ASCQ_PWR_RESET	0x00
-#define	SPC_ASC_PWR_ON		0x29
-#define	SPC_ASCQ_PWR_ON		0x01
-#define	SPC_ASC_BUS_RESET	0x29
-#define	SPC_ASCQ_BUS_RESET	0x02
 #define	SPC_ASC_FM_DETECTED	0x00 /* file-mark detected */
 #define	SPC_ASCQ_FM_DETECTED	0x01
+
 #define	SPC_ASC_EOP		0x00 /* end-of-partition/medium detected */
 #define	SPC_ASCQ_EOP		0x02
-#define	SPC_ASC_WRITE_ERROR	0x0c
-#define	SPC_ASCQ_WRITE_ERROR	0x00
-#define	SPC_ASC_CAP_CHANGE	0x2a
-#define	SPC_ASCQ_CAP_CHANGE	0x09
+
 #define	SPC_ASC_IN_PROG		0x04
 #define	SPC_ASCQ_IN_PROG	0x07
-#define	SPC_ASC_DATA_PATH	0x41
-#define	SPC_ASCQ_DATA_PATH	0x00
+
+#define	SPC_ASC_WRITE_ERROR	0x0c
+#define	SPC_ASCQ_WRITE_ERROR	0x00
+
+#define	SPC_ASC_PARAM_LIST_LEN	0x1a /* Parameter List Length Error */
+#define	SPC_ASCQ_PARAM_LIST_LEN	0x00
+
 #define	SPC_ASC_MISCOMPARE	0x1d
 #define	SPC_ASCQ_MISCOMPARE	0x00
+
 #define	SPC_ASC_INVALID_LU	0x20
 #define	SPC_ASCQ_INVALID_LU	0x09
+
 #define	SPC_ASC_BLOCK_RANGE	0x21
 #define	SPC_ASCQ_BLOCK_RANGE	0x00
 
+#define	SPC_ASC_INVALID_FIELD_IN_PARAMETER_LIST		0x26
+#define	SPC_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST	0x00
+
+#define	SPC_ASC_INVALID_CDB	0x24
+#define	SPC_ASCQ_INVALID_CDB	0x00
+
+#define	SPC_ASC_PARAMETERS_CHANGED	0x2a
+#define	SPC_ASCQ_RES_PREEMPTED	0x03
+#define	SPC_ASCQ_RES_RELEASED	0x04
+
+#define	SPC_ASC_PWR_RESET	0x29
+#define	SPC_ASCQ_PWR_RESET	0x00
+
+#define	SPC_ASC_PWR_ON		0x29
+#define	SPC_ASCQ_PWR_ON		0x01
+
+#define	SPC_ASC_BUS_RESET	0x29
+#define	SPC_ASCQ_BUS_RESET	0x02
+
+#define	SPC_ASC_CAP_CHANGE	0x2a
+#define	SPC_ASCQ_CAP_CHANGE	0x09
+
+#define	SPC_ASC_DATA_PATH	0x41
+#define	SPC_ASCQ_DATA_PATH	0x00
+
+#define	SPC_ASC_MEMORY_OUT_OF	0x55 /* Auxillary Memory Out Of Space */
+#define	SPC_ASCQ_MEMORY_OUT_OF	0x00
+#define	SPC_ASCQ_RESERVATION_FAIL 0x02
+
+
 /*
  * []------------------------------------------------------------------[]
  * | SAM-3, revision 14, section 5.2 - Command descriptor block (CDB)	|
@@ -417,4 +448,8 @@
 #define	SCSI_REPORTLUNS_ADDRESS_EXTENDED_MASK		0x30
 #define	SCSI_REPORTLUNS_SELECT_ALL			0x02
 
-#endif /* _SPC_H */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _T10_SPC_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.c	Thu Sep 13 15:17:59 2007 -0700
@@ -0,0 +1,1964 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * []------------------------------------------------------------------[]
+ * | Implementation of SPC-3 Persistent Reserve emulation		|
+ * []------------------------------------------------------------------[]
+ */
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/asynch.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <strings.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <assert.h>
+
+#include <sys/scsi/generic/sense.h>
+#include <sys/scsi/generic/status.h>
+#include <sys/scsi/generic/inquiry.h>
+#include <sys/scsi/generic/mode.h>
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/generic/persist.h>
+
+#include "t10.h"
+#include "t10_spc.h"
+#include "t10_spc_pr.h"
+#include "t10_sbc.h"
+#include "target.h"
+
+/*
+ * External declarations
+ */
+extern target_queue_t *mgmtq;
+void spc_free(emul_handle_t id);
+void sbc_cmd(t10_cmd_t *, uint8_t *, size_t);
+void sbc_cmd_reserved(t10_cmd_t *, uint8_t *, size_t);
+
+/*
+ * Forward declarations
+ */
+static spc_pr_key_t *spc_pr_key_find(scsi3_pgr_t *, uint64_t, char *, char *);
+static spc_pr_key_t *spc_pr_key_alloc(scsi3_pgr_t *, uint64_t, char *, char *);
+static spc_pr_rsrv_t *spc_pr_rsrv_find(scsi3_pgr_t *, uint64_t, char *, char *);
+static spc_pr_rsrv_t *spc_pr_rsrv_alloc(scsi3_pgr_t *, uint64_t, char *, char *,
+    uint8_t, uint8_t);
+
+static void spc_pr_key_free(scsi3_pgr_t *, spc_pr_key_t *);
+static void spc_pr_rsrv_free(scsi3_pgr_t *, spc_pr_rsrv_t *);
+static void spc_pr_rsrv_release(t10_cmd_t *, scsi3_pgr_t *, spc_pr_rsrv_t *);
+
+static int spc_pr_out_register(t10_cmd_t *, void *, size_t);
+static int spc_pr_out_reserve(t10_cmd_t *, void *, size_t);
+static int spc_pr_out_release(t10_cmd_t *, void *, size_t);
+static int spc_pr_out_clear(t10_cmd_t *, void *, size_t);
+static int spc_pr_out_preempt(t10_cmd_t *, void *, size_t);
+static int spc_pr_out_register_and_move(t10_cmd_t *, void *, size_t);
+
+static int spc_pr_in_readkeys(char *, scsi3_pgr_t *, void *, uint16_t);
+static int spc_pr_in_readrsrv(char *, scsi3_pgr_t *, void *, uint16_t);
+static int spc_pr_in_repcap(char *, scsi3_pgr_t *, void *, uint16_t);
+static int spc_pr_in_fullstat(char *, scsi3_pgr_t *, void *, uint16_t);
+
+Boolean_t spc_pr_write(t10_cmd_t *);
+static void spc_pr_erase(scsi3_pgr_t *);
+static void spc_pr_initialize(scsi3_pgr_t *);
+
+/*
+ * []----
+ * | spc_pgr_is_conflicting
+ * |	PGR reservation conflict checking.
+ * |	SPC-3, Revision 23, Table 31
+ * []----
+ */
+static int
+spc_pgr_is_conflicting(uint8_t *cdb, uint_t type)
+{
+	Boolean_t		conflict = False;
+
+	switch (cdb[0]) {
+		case SCMD_FORMAT:
+		case SCMD_EXTENDED_COPY:
+		case SCMD_LOG_SELECT_G1:
+		case SCMD_MODE_SELECT:
+		case SCMD_MODE_SELECT_G1:
+		case SCMD_MODE_SENSE:
+		case SCMD_MODE_SENSE_G1:
+		case SCMD_READ_ATTRIBUTE:
+		case SCMD_READ_BUFFER:
+		case SCMD_GDIAG:	/* SCMD_RECEIVE_DIAGNOSTIC_RESULTS */
+		case SCMD_SDIAG:	/* SCMD_SEND_DIAGNOSTIC_RESULTS */
+		case SCMD_WRITE_ATTRIBUTE:
+		case SCMD_WRITE_BUFFER:
+			conflict = True;
+			break;
+
+		case SCMD_DOORLOCK:	/* SCMD_PREVENT_ALLOW_MEDIA_REMOVAL */
+			/*
+			 * As per SPC-3, Revision 23, Table 31
+			 * (prevent <> 0)
+			 */
+			conflict = (cdb[4] & 0x1) ? True: False;
+			break;
+
+		case SCMD_REPORT_TARGET_PORT_GROUPS:	/* SCMD_REPORT_ */
+			/*
+			 * As pee SPC-3, Revision 23, Section 6.23
+			 */
+			switch ((cdb[1] & 0x03)) {
+				/* SCMD_REPORT_SUPPORTED_OPERATION_CODES */
+				case 0x0c:
+				/* SCMD_REPORT_SUPPORTED_MANAGEMENT_FUNCTIONS */
+				case 0x0d:
+
+					conflict = True;
+					break;
+			}
+			break;
+
+		case SCMD_SET_DEVICE:
+			/*
+			 * SPC-3, Revision 23, Section 6.29
+			 */
+			switch ((cdb[1] & 0x1F)) {
+				case SCMD_SET_DEVICE_IDENTIFIER:
+				case SCMD_SET_PRIORITY:
+				case SCMD_SET_TARGET_PORT_GROUPS:
+				case SCMD_SET_TIMESTAMP:
+				conflict = True;
+				break;
+			}
+			break;
+
+		case SCMD_READ:
+		case SCMD_READ_G1:
+		case SCMD_READ_G4:
+			/*
+			 * Exclusive Access, and EA Registrants Only
+			 */
+			if (type == PGR_TYPE_EX_AC || type == PGR_TYPE_EX_AC_RO)
+				conflict = True;
+			break;
+	}
+
+	return (conflict);
+}
+
+/*
+ * []----
+ * | spc_pgr_check --  PERSISTENT_RESERVE {IN|OUT} check of I_T_L
+ * |	Refer to SPC-3, Section ?.?, Tables ?? and ??
+ * []----
+ */
+Boolean_t
+spc_pgr_check(t10_cmd_t *cmd, uint8_t *cdb)
+{
+	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+	sbc_reserve_t		*res = &p->d_sbc_reserve;
+	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
+	spc_pr_rsrv_t		*rsrv = NULL;
+	Boolean_t		conflict = False;
+
+	/*
+	 * If no reservations exist, allow all remaining command types.
+	 */
+	assert(res->res_type == RT_PGR);
+	if (pgr->pgr_numrsrv == 0) {
+		conflict = False;
+		goto done;
+	}
+
+	/*
+	 * At this point we know there is at least one reservation.
+	 * If there is no reservation set on this service delivery
+	 * port then conflict all remaining command types.
+	 */
+	if (!(rsrv = spc_pr_rsrv_find(pgr, 0, "", T10_PGR_TNAME(cmd)))) {
+		queue_prt(mgmtq, Q_PR_ERRS, "PGR%x Reserved on other port\n",
+		    "\t%s:%s\n", cmd->c_lu->l_targ->s_targ_num,
+		    T10_PGR_INAME(cmd), T10_PGR_TNAME(cmd));
+		conflict = True;
+		goto done;
+	}
+
+	/*
+	 * Check the command against the reservation type for this port.
+	 */
+	switch (rsrv->r_type) {
+		case PGR_TYPE_WR_EX:	/* Write Exclusive */
+		case PGR_TYPE_EX_AC:	/* Exclusive Access */
+			if (strcmp(T10_PGR_INAME(cmd), rsrv->r_i_name) == 0)
+				conflict = False;
+			else
+				conflict = spc_pgr_is_conflicting(cdb,
+				    rsrv->r_type);
+			break;
+		case PGR_TYPE_WR_EX_RO:	/* Write Exclusive, Registrants Only */
+		case PGR_TYPE_EX_AC_RO:	/* Exclusive Access, Registrants Only */
+			if (spc_pr_key_find(
+			    pgr, 0, T10_PGR_INAME(cmd), T10_PGR_TNAME(cmd)))
+				conflict = False;
+			else
+				conflict = spc_pgr_is_conflicting(cdb,
+				    rsrv->r_type);
+			break;
+		case PGR_TYPE_WR_EX_AR:	/* Write Exclusive, All Registrants */
+		case PGR_TYPE_EX_AC_AR:	/* Exclusive Access, All Registrants */
+			if (spc_pr_key_find(pgr, 0, "", T10_PGR_TNAME(cmd)))
+				conflict = False;
+			else
+				conflict = spc_pgr_is_conflicting(cdb,
+				    rsrv->r_type);
+			break;
+		default:
+			conflict = True;
+			break;
+	}
+
+done:
+	queue_prt(mgmtq, Q_PR_IO, "PGR%x LUN%d CDB:%s - spc_pgr_check(%s:%s)\n",
+	    cmd->c_lu->l_targ->s_targ_num,
+	    cmd->c_lu->l_common->l_num,
+	    cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name == NULL
+	    ? "(no name)"
+	    : cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name,
+	    (rsrv == NULL)
+	    ? "<none>"
+	    : (rsrv->r_type == PR_IN_READ_KEYS)
+	    ? "Write Exclusive"
+	    : (rsrv->r_type == PGR_TYPE_WR_EX)
+	    ? "Exclusive Access"
+	    : (rsrv->r_type == PGR_TYPE_EX_AC)
+	    ? "Report capabilties"
+	    : (rsrv->r_type == PGR_TYPE_WR_EX_RO)
+	    ? "Write Exclusive, Registrants Only"
+	    : (rsrv->r_type == PGR_TYPE_EX_AC_RO)
+	    ? "Exclusive Access, Registrants Only"
+	    : (rsrv->r_type == PGR_TYPE_WR_EX_AR)
+	    ? "Write Exclusive, All Registrants"
+	    : (rsrv->r_type == PGR_TYPE_EX_AC_AR)
+	    ? "Exclusive Access, All Registrants"
+	    : "Uknown reservation type",
+	    (conflict) ? "Conflict" : "Allowed");
+
+	return (conflict);
+}
+
+/*
+ * []----
+ * | spc_cmd_pr_in --  PERSISTENT_RESERVE IN
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+/*ARGSUSED*/
+void
+spc_cmd_pr_in(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+	scsi_cdb_prin_t		*p_prin = (scsi_cdb_prin_t *)cdb;
+	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+	sbc_reserve_t		*res = &p->d_sbc_reserve;
+	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
+	uint16_t		alen;
+	size_t			len = 0;
+	void			*buf;
+
+	/*
+	 * Information obtained from:
+	 *	SPC-3, Revision 23
+	 *	Section 6.11 PERSISTENCE RESERVE IN
+	 * Need to generate a CHECK CONDITION with ILLEGAL REQUEST
+	 * and INVALID FIELD IN CDB (0x24/0x00) if any of the following is
+	 * true.
+	 *	(1) The SERVICE ACTION field is 004h - 01fh,
+	 *	(2) The reserved area in byte 1 is set,
+	 *	(3) The reserved area in bytes 2 thru 6 are set,
+	 *	(4) If any of the reserved bits in the CONTROL byte are set.
+	 */
+	if ((p_prin->action >= 0x4) || p_prin->resbits || p_prin->resbytes[0] ||
+	    p_prin->resbytes[1] || p_prin->resbytes[2] || p_prin->resbytes[3] ||
+	    p_prin->resbytes[4] || p_prin->control) {
+		spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+		spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+		trans_send_complete(cmd, STATUS_CHECK);
+		return;
+	}
+
+	/*
+	 * Information obtained from:
+	 *	SPC-3, Revision 23
+	 *	Section 6.11 PERSISTENCE RESERVE IN
+	 * Acquire ALLOCATION LENGTH from bytes 7, 8
+	 * A zero(0) length allocation is not an error and we should just
+	 * acknowledge the operation.
+	 */
+	if ((alen = SCSI_READ16(p_prin->alloc_len)) == 0) {
+		queue_prt(mgmtq, Q_PR_ERRS,
+		    "PGR%x LUN%d CDB:%s - spc_cmd_pr_in, len = 0\n",
+		    cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+		    cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name == NULL
+		    ? "(no name)"
+		    : cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name);
+
+		trans_send_complete(cmd, STATUS_GOOD);
+		return;
+	}
+
+	/*
+	 * Allocate space with an alignment that will work for any casting.
+	 */
+	if ((buf = memalign(sizeof (void *), alen)) == NULL) {
+		/*
+		 * Lack of memory is not fatal, just too busy
+		 */
+		trans_send_complete(cmd, STATUS_BUSY);
+		return;
+	} else {
+		bzero(buf, alen);
+	}
+
+	/*
+	 * Start processing, lock reservation
+	 */
+	pthread_rwlock_rdlock(&res->res_rwlock);
+
+	queue_prt(mgmtq, Q_PR_NONIO, "PGR%x LUN%d action:%s\n",
+	    cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+	    (p_prin->action == PR_IN_READ_KEYS)
+	    ? "Read keys"
+	    : (p_prin->action == PR_IN_READ_RESERVATION)
+	    ? "Read reservation"
+	    : (p_prin->action == PR_IN_REPORT_CAPABILITIES)
+	    ? "Report capabilties"
+	    : (p_prin->action == PR_IN_READ_FULL_STATUS)
+	    ? "Read full status"
+	    : "Uknown");
+
+	/*
+	 * Per SPC-3, Revision 23, Table 102, validate ranget of service actions
+	 */
+	switch (p_prin->action) {
+		case PR_IN_READ_KEYS:
+			len = spc_pr_in_readkeys(
+			    T10_PGR_TNAME(cmd), pgr, buf, alen);
+			break;
+		case PR_IN_READ_RESERVATION:
+			len = spc_pr_in_readrsrv(
+			    T10_PGR_TNAME(cmd), pgr, buf, alen);
+			break;
+		case PR_IN_REPORT_CAPABILITIES:
+			len = spc_pr_in_repcap(
+			    T10_PGR_TNAME(cmd), pgr, buf, alen);
+			break;
+		case PR_IN_READ_FULL_STATUS:
+			len = spc_pr_in_fullstat(
+			    T10_PGR_TNAME(cmd), pgr, buf, alen);
+			break;
+	}
+
+	/*
+	 * Complete processing, unlock reservation
+	 */
+	pthread_rwlock_unlock(&res->res_rwlock);
+
+	/*
+	 * Now send the selected Persistent Reservation response back
+	 */
+	if (trans_send_datain(cmd, buf, len, 0, spc_free, True, buf) == False)
+		trans_send_complete(cmd, STATUS_BUSY);
+}
+
+/*
+ * []----
+ * |   spc_pr_in_readkey -
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+static int
+spc_pr_in_readkeys(char *transportID, scsi3_pgr_t *pgr, void *bp,
+    uint16_t alloc_len)
+{
+	int			i = 0, max_buf_keys, hsize;
+	scsi_prin_readrsrv_t	*buf = (scsi_prin_readrsrv_t *)bp;
+	spc_pr_key_t		*key;
+
+	hsize = sizeof (buf->PRgeneration) + sizeof (buf->add_len);
+	max_buf_keys = ((int)alloc_len - hsize) / sizeof (key->k_key);
+
+	queue_prt(mgmtq, Q_PR_NONIO,
+	    "PGRIN readkeys - transportID=%s\n", transportID);
+
+	if (pgr->pgr_numkeys)
+	for (key  = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd;
+	    key != (spc_pr_key_t *)&pgr->pgr_keylist;
+	    key  = (spc_pr_key_t *)key->k_link.lnk_fwd) {
+
+		if (strcmp(key->k_transportID, transportID))
+			continue;
+
+		if (i < max_buf_keys) {
+			SCSI_WRITE64(&buf->key_list.service_key[i], key->k_key);
+			queue_prt(mgmtq, Q_PR_NONIO,
+			    "PGRIN readkeys - key:%016lx, i_name:%s\n",
+			    key->k_key, key->k_i_name);
+			i++;
+		}
+		else
+			break;		/* No room left, leave now */
+	}
+
+	SCSI_WRITE32(buf->PRgeneration, pgr->pgr_generation);
+	SCSI_WRITE32(buf->add_len, i * sizeof (key->k_key));
+
+	return (hsize + min(i, max_buf_keys) * sizeof (key->k_key));
+}
+
+/*
+ * []----
+ * |   spc_pr_in_readresv -
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+static int
+spc_pr_in_readrsrv(
+    char *transportID, scsi3_pgr_t *pgr, void *bp, uint16_t alloc_len)
+{
+	int			i = 0, max_buf_rsrv, hsize;
+	scsi_prin_readrsrv_t	*buf = (scsi_prin_readrsrv_t *)bp;
+	scsi_prin_rsrvdesc_t	*desc;
+	spc_pr_rsrv_t		*rsrv;
+
+	hsize = sizeof (buf->PRgeneration) + sizeof (buf->add_len);
+	max_buf_rsrv = ((int)alloc_len - hsize) / sizeof (scsi_prin_rsrvdesc_t);
+
+	queue_prt(mgmtq, Q_PR_NONIO,
+	    "PGRIN readrsrv - transportID=%s\n", transportID);
+
+	if (pgr->pgr_numrsrv)
+	for (rsrv  = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd;
+	    rsrv != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist;
+	    rsrv  = (spc_pr_rsrv_t *)rsrv->r_link.lnk_fwd) {
+
+		if (strcmp(rsrv->r_transportID, transportID))
+			continue;
+
+		if (i < max_buf_rsrv) {
+			desc = &buf->key_list.res_key_list[i];
+			SCSI_WRITE64(desc->reservation_key, rsrv->r_key);
+			desc->scope = rsrv->r_scope;
+			desc->type = rsrv->r_type;
+
+			queue_prt(mgmtq, Q_PR_NONIO,
+			    "PGRIN readrsrv - "
+			    "key:%016lx i_name:%s scope:%d type:%d \n",
+			    rsrv->r_key, rsrv->r_i_name,
+			    rsrv->r_scope, rsrv->r_type);
+
+			i++;
+		}
+		else
+			break;		/* No room left, leave now */
+	}
+
+	SCSI_WRITE32(buf->PRgeneration, pgr->pgr_generation);
+	SCSI_WRITE32(buf->add_len, i * sizeof (scsi_prin_rsrvdesc_t));
+
+	return (hsize + min(i, max_buf_rsrv)* sizeof (scsi_prin_rsrvdesc_t));
+}
+
+/*
+ * []----
+ * |   spc_pr_in_repcap -
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+/*
+ */
+static int
+spc_pr_in_repcap(
+    char *transportID, scsi3_pgr_t *pgr, void *bp, uint16_t alloc_len)
+{
+	scsi_prin_rpt_cap_t	*buf = (scsi_prin_rpt_cap_t *)bp;
+
+	buf->crh = 0;			/* Supports Reserve / Release */
+	buf->sip_c = 1;			/* Specify Initiator Ports Capable */
+	buf->atp_c = 1;			/* All Target Ports Capable */
+	buf->ptpl_c = 1;		/* Persist Through Power Loss C */
+	buf->tmv = 1;			/* Type Mask Valid */
+	buf->ptpl_a = pgr_persist;	/* Persist Though Power Loss Active */
+	buf->pr_type.wr_ex = 1;		/* Write Exclusve */
+	buf->pr_type.ex_ac = 1;		/* Exclusive Access */
+	buf->pr_type.wr_ex_ro = 1;	/* Write Exclusive Registrants Only */
+	buf->pr_type.ex_ac_ro = 1;	/* Exclusive Access Registrants Only */
+	buf->pr_type.wr_ex_ar = 1;	/* Write Exclusive All Registrants */
+	buf->pr_type.ex_ac_ar = 1;	/* Exclusive Access All Registrants */
+
+	SCSI_WRITE16(buf->length, sizeof (scsi_prin_rpt_cap_t));
+
+	return (sizeof (scsi_prin_rpt_cap_t));
+}
+
+/*
+ * []----
+ * |   spc_pr_in_fullstat -
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+/*
+ */
+static int
+spc_pr_in_fullstat(
+    char *transportID, scsi3_pgr_t *pgr, void *bp, uint16_t alloc_len)
+{
+	int			i = 0, max_buf_rsrv, hsize;
+	spc_pr_rsrv_t		*rsrv;
+	scsi_prin_full_status_t	*buf = (scsi_prin_full_status_t *)bp;
+
+	hsize = sizeof (buf->PRgeneration) + sizeof (buf->add_len);
+	max_buf_rsrv = ((int)alloc_len - hsize) /
+	    sizeof (scsi_prin_full_status_t);
+
+	if (pgr->pgr_numrsrv)
+	for (i = 0, rsrv  = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd;
+	    rsrv != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist;
+	    rsrv  = (spc_pr_rsrv_t *)rsrv->r_link.lnk_fwd) {
+
+		if (i < max_buf_rsrv) {
+			SCSI_WRITE64(buf->full_desc[i].reservation_key,
+			    rsrv->r_key);
+			buf->full_desc[i].all_tg_pt = 1;
+			buf->full_desc[i].r_holder =
+			    strcmp(rsrv->r_transportID, transportID) ? 0 : 1;
+			buf->full_desc[i].scope = rsrv->r_scope;
+			buf->full_desc[i].type = rsrv->r_type;
+			SCSI_WRITE16(buf->full_desc[i].rel_tgt_port_id, 0);
+			SCSI_WRITE32(buf->full_desc[i].add_len,
+			    sizeof (scsi_transport_id_t));
+			buf->full_desc[i].trans_id.protocol_id =
+			    iSCSI_PROTOCOL_ID;
+			buf->full_desc[i].trans_id.format_code =
+			    WW_UID_DEVICE_NAME;
+			SCSI_WRITE16(buf->full_desc[i].trans_id.add_len, 0);
+			sprintf(buf->full_desc[i].trans_id.iscsi_name, "");
+
+			i++;
+		}
+		else
+			break;		/* No room left, leave now */
+
+	}
+
+	SCSI_WRITE32(buf->PRgeneration, pgr->pgr_generation);
+	SCSI_WRITE32(buf->add_len, i * sizeof (scsi_prin_rsrvdesc_t));
+
+	return (hsize + min(i, max_buf_rsrv) * sizeof (scsi_prin_rsrvdesc_t));
+
+}
+
+/*
+ * []----
+ * | spc_cmd_pr_out --  PERSISTENT_RESERVE OUT
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+/*ARGSUSED*/
+void
+spc_cmd_pr_out(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len)
+{
+	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cdb;
+	size_t			len;
+	void			*buf;
+
+	/*
+	 * Information obtained from:
+	 *	SPC-3, Revision 23
+	 *	Section 6.12 PERSISTENCE RESERVE OUT
+	 * Need to generate a CHECK CONDITION with ILLEGAL REQUEST
+	 * and INVALID FIELD IN CDB (0x24/0x00) if any of the following is
+	 * true.
+	 *	(1) The SERVICE ACTION field is 008h - 01fh,
+	 *	(2) The reserved area in byte 1 is set,
+	 *	(3) The TYPE and SCOPE fields are invalid,
+	 *	(4) The reserved area in bytes 3 and 4 are set,
+	 *	(5) If any of the reserved bits in the CONTROL byte are set.
+	 */
+	if ((p_prout->action >= 0x8) || p_prout->resbits ||
+	    (p_prout->type >= 0x9) ||
+	    (p_prout->scope >= 0x3) || p_prout->control) {
+		spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+		spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+		trans_send_complete(cmd, STATUS_CHECK);
+		return;
+	}
+
+	/*
+	 * Information obtained from:
+	 *	SPC-3, Revision 23
+	 *	Section 6.12 PERSISTENCE RESERVE OUT
+	 * Acquire ALLOCATION LENGTH from bytes 5 thru 8
+	 */
+	len = SCSI_READ32(p_prout->param_len);
+
+	/*
+	 * Parameter list length shall contain 24 (0x18),
+	 * the SPEC_I_PIT is zero (it is because we don't support SIP_C))
+	 * the service action is not REGISTER AND MOVE
+	 */
+	if ((p_prout->action != PR_OUT_REGISTER_MOVE) && (len != 24)) {
+		spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+		spc_sense_ascq(cmd, SPC_ASC_PARAM_LIST_LEN, 0x00);
+		trans_send_complete(cmd, STATUS_CHECK);
+		return;
+	}
+
+	/*
+	 * Information obtained from:
+	 *	SPC-3, Revision 23
+	 *	Section 6.11.3.3 Persistent Reservation Scope
+	 * SCOPE field shall be set to LU_SCOPE
+	 */
+	if (p_prout->scope != PR_LU_SCOPE) {
+		spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+		spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00);
+		trans_send_complete(cmd, STATUS_CHECK);
+		return;
+	}
+
+	/*
+	 * Allocate space with an alignment that will work for any casting.
+	 */
+	if ((buf = memalign(sizeof (void *), len)) == NULL) {
+		/*
+		 * Lack of memory is not fatal, just too busy
+		 */
+		trans_send_complete(cmd, STATUS_BUSY);
+		return;
+	}
+
+	/*
+	 * Now request the Persistent Reserve OUT parameter list
+	 */
+	if (trans_rqst_dataout(cmd, buf, len, 0, buf, spc_free) == False)
+		trans_send_complete(cmd, STATUS_BUSY);
+}
+
+/*
+ * []----
+ * | spc_cmd_pr_out_data -- DataIn phase of PERSISTENT_RESERVE OUT command
+ * []----
+ */
+/*ARGSUSED*/
+void
+spc_cmd_pr_out_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data,
+    size_t data_len)
+{
+	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cmd->c_cdb;
+	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+	sbc_reserve_t		*res = &p->d_sbc_reserve;
+	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
+	scsi_prout_plist_t	*plist = (scsi_prout_plist_t *)data;
+	t10_lu_impl_t		*lu;
+	int			status;
+
+	/*
+	 * If this is the first time using the persistance data,
+	 * initialize the reservation and resource key queues
+	 */
+	pthread_rwlock_wrlock(&res->res_rwlock);
+	if (pgr->pgr_rsrvlist.lnk_fwd == NULL) {
+		spc_pr_initialize(pgr);
+	}
+
+	queue_prt(mgmtq, Q_PR_NONIO, "PGR%x LUN%d action:%s\n",
+	    cmd->c_lu->l_targ->s_targ_num,
+	    cmd->c_lu->l_common->l_num,
+	    (p_prout->action == PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY)
+	    ? "Register & ignore existing key"
+	    : (p_prout->action == PR_OUT_REGISTER)
+	    ? "Register"
+	    : (p_prout->action == PR_OUT_RESERVE)
+	    ? "Reserve"
+	    : (p_prout->action == PR_OUT_RELEASE)
+	    ? "Release"
+	    : (p_prout->action == PR_OUT_CLEAR)
+	    ? "Clear"
+	    : (p_prout->action == PR_OUT_PREEMPT_ABORT)
+	    ? "Preempt & abort"
+	    : (p_prout->action == PR_OUT_PREEMPT)
+	    ? "Preempt"
+	    : (p_prout->action == PR_OUT_REGISTER_MOVE)
+	    ? "Register & move"
+	    : "Uknown");
+
+	/*
+	 * Now process the action.
+	 */
+	switch (p_prout->action) {
+	case PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY:
+	case PR_OUT_REGISTER:
+		/*
+		 * PR_OUT_REGISTER_IGNORE differs from PR_OUT_REGISTER
+		 * in that the reservation_key is ignored.
+		 */
+		status = spc_pr_out_register(cmd, data, data_len);
+		break;
+
+	case PR_OUT_RESERVE:
+		status = spc_pr_out_reserve(cmd, data, data_len);
+		break;
+
+	case PR_OUT_RELEASE:
+		status = spc_pr_out_release(cmd, data, data_len);
+		break;
+
+	case PR_OUT_CLEAR:
+		status = spc_pr_out_clear(cmd, data, data_len);
+		break;
+
+	case PR_OUT_PREEMPT_ABORT:
+	case PR_OUT_PREEMPT:
+		/*
+		 * PR_OUT_PREEMPT_ABORT differs from PR_OUT_PREEMPT
+		 * in that all current acitivy for the preempted
+		 * Initiators will be terminated.
+		 */
+		status = spc_pr_out_preempt(cmd, data, data_len);
+		break;
+
+	case PR_OUT_REGISTER_MOVE:
+		/*
+		 * PR_OUT_REGISTER_MOVE registers a key for another I_T
+		 */
+		status = spc_pr_out_register_and_move(cmd, data, data_len);
+		break;
+	}
+
+	/*
+	 * Check status of action performed.
+	 */
+	if (status == STATUS_CHECK) {
+		/*
+		 * Check condition required.
+		 */
+		pthread_rwlock_unlock(&res->res_rwlock);
+		spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0);
+		spc_sense_ascq(cmd, cmd->c_lu->l_asc, cmd->c_lu->l_ascq);
+		trans_send_complete(cmd, STATUS_CHECK);
+		return;
+	}
+
+	/*
+	 * Handle Failed processing status
+	 */
+	if (status != STATUS_GOOD) {
+		pthread_rwlock_unlock(&res->res_rwlock);
+		trans_send_complete(cmd, status);
+		return;
+	}
+
+	/*
+	 * Successful, bump the PRgeneration value
+	 */
+	if (p_prout->action != PR_OUT_RESERVE &&
+	    p_prout->action != PR_OUT_RELEASE)
+		pgr->pgr_generation++;
+
+	/*
+	 * If Activate Persist Through Power Loss (APTPL) is set, persist
+	 * this PGR data on disk
+	 */
+	if (plist->aptpl || pgr->pgr_aptpl)
+		spc_pr_write(cmd);
+
+	/*
+	 * When the last registration is removed, PGR is no longer
+	 * active and we must reset the reservation type.
+	 */
+	if ((pgr->pgr_numkeys == 0) && (pgr->pgr_numrsrv == 0)) {
+		res->res_type = RT_NONE;
+		pgr->pgr_aptpl = 0;
+	} else {
+		res->res_type = RT_PGR;
+	}
+
+	/*
+	 * Set the command dispatcher according to the reservation type
+	 */
+	(void) pthread_mutex_lock(&cmd->c_lu->l_common->l_common_mutex);
+	lu = avl_first(&cmd->c_lu->l_common->l_all_open);
+	do {
+		lu->l_cmd = (res->res_type == RT_NONE)
+		    ? sbc_cmd : sbc_cmd_reserved;
+		lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
+	} while (lu != NULL);
+	(void) pthread_mutex_unlock(&cmd->c_lu->l_common->l_common_mutex);
+
+	/*
+	 * Processing is complete, release mutex
+	 */
+	pthread_rwlock_unlock(&res->res_rwlock);
+
+	/*
+	 * Send back a succesful response
+	 */
+	trans_send_complete(cmd, STATUS_GOOD);
+}
+
+/*
+ * []----
+ * | spc_pr_out_register
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+static int
+spc_pr_out_register(t10_cmd_t *cmd, void *data, size_t data_len)
+{
+	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cmd->c_cdb;
+	scsi_prout_plist_t	*plist = (scsi_prout_plist_t *)data;
+	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+	sbc_reserve_t		*res = &p->d_sbc_reserve;
+	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
+	spc_pr_rsrv_t		*rsrv;
+	spc_pr_key_t		*key;
+	uint64_t		reservation_key;
+	uint64_t		service_key;
+	t10_lu_impl_t		*lu;
+	t10_targ_impl_t		*ti;
+
+	/*
+	 * Validate Persistent Reserver Out parameter list
+	 */
+	if (plist->obsolete1[0] || plist->obsolete1[1] ||
+	    plist->obsolete1[2] || plist->obsolete1[3] ||
+	    plist->resbits1 || plist->resbits2 || plist->resbytes1 ||
+	    plist->obsolete2[0] || plist->obsolete2[1]) {
+		cmd->c_lu->l_status = KEY_ILLEGAL_REQUEST;
+		cmd->c_lu->l_asc = SPC_ASC_INVALID_CDB;
+		cmd->c_lu->l_ascq = 0;
+		return (STATUS_CHECK);
+	}
+
+	/*
+	 * Determine if Activate Persist Trhough Power Loss (APTPL)
+	 * is valid for this device server.
+	 */
+	if (plist->aptpl && (pgr_persist == 0)) {
+		/* pgr - define SCSI-3 error codes */
+		cmd->c_lu->l_status = KEY_ILLEGAL_REQUEST;
+		cmd->c_lu->l_asc = SPC_ASC_INVALID_FIELD_IN_PARAMETER_LIST;
+		cmd->c_lu->l_ascq = 0;
+		return (STATUS_CHECK);
+	}
+
+	/*
+	 * Get reservation values
+	 */
+	reservation_key = SCSI_READ64(plist->reservation_key);
+	service_key = SCSI_READ64(plist->service_key);
+
+	queue_prt(mgmtq, Q_PR_NONIO,
+	    "PGR%x LUN%d register reservation:%016lx, key:%016lx\n",
+	    cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+	    reservation_key, service_key);
+
+	/*
+	 * We may need register all initiators, depending on ALL_TG_TP
+	 */
+	lu = avl_first(&cmd->c_lu->l_common->l_all_open);
+	do {
+		/*
+		 * Find specified key
+		 */
+		ti = lu->l_targ;
+		key = spc_pr_key_find(pgr, 0, ti->s_i_name, ti->s_targ_base);
+		if (key) {
+			/*
+			 * What about ALL_TG_TP?
+			 */
+			if (plist->all_tg_pt ||
+			    (strcmp(key->k_i_name, T10_PGR_INAME(cmd)) == 0)) {
+
+				if (p_prout->action == PR_OUT_REGISTER &&
+				    key->k_key != reservation_key) {
+					/*
+					 * The Initiator did not specify the
+					 * existing key. Reservation conflict.
+					 */
+					return (STATUS_RESERVATION_CONFLICT);
+				}
+				/*
+				 * Change existing key ?
+				 */
+				if (service_key) {
+					queue_prt(mgmtq, Q_PR_NONIO,
+					    "PGROUT: change "
+					    "old:%016lx = new:%016lx\n",
+					    key->k_key, service_key);
+
+					/*
+					 * Overwrite (change) key
+					 */
+					key->k_key = service_key;
+
+				} else {
+					/*
+					 * Remove existing key
+					 * NOTE: If we own the reservation then
+					 * we must release it.
+					 */
+					queue_prt(mgmtq, Q_PR_NONIO,
+					    "PGROUT: delete "
+					    "old:%016lx = new:%016lx\n",
+					    key->k_key, service_key);
+
+					rsrv = spc_pr_rsrv_find(pgr, 0,
+					    ti->s_i_name, ti->s_targ_base);
+					if (rsrv) {
+						spc_pr_rsrv_release(
+						    cmd, pgr, rsrv);
+					}
+					spc_pr_key_free(pgr, key);
+				}
+			}
+		} else {
+			/*
+			 * What about ALL_TG_TP?
+			 */
+			if (plist->all_tg_pt ||
+			    (strcmp(ti->s_i_name, T10_PGR_INAME(cmd)) == 0)) {
+				/*
+				 * Process request from un-registered Initiator.
+				 */
+				if ((p_prout->action == PR_OUT_REGISTER) &&
+				    (reservation_key || service_key == 0)) {
+					/*
+					 * Unregistered initiator is attempting
+					 * to modify a key.
+					 */
+					return (STATUS_RESERVATION_CONFLICT);
+				}
+
+				key = spc_pr_key_alloc(pgr, service_key,
+				    ti->s_i_name, ti->s_targ_base);
+				if (key == NULL) {
+					/* pgr - define SCSI-3 error codes */
+					cmd->c_lu->l_status =
+					    KEY_ABORTED_COMMAND;
+					cmd->c_lu->l_asc =
+					    SPC_ASC_MEMORY_OUT_OF;
+					cmd->c_lu->l_ascq =
+					    SPC_ASCQ_RESERVATION_FAIL;
+					return (STATUS_CHECK);
+				}
+			}
+		}
+		lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
+	} while (lu != NULL);
+
+	/*
+	 * Apply the last valid APTPL bit
+	 *	SPC-3, Revision 23
+	 *	Section 5.6.4.1 Preserving persistent reservervations and
+	 *	registrations through power loss
+	 */
+	pgr->pgr_aptpl = plist->aptpl;
+
+	return (STATUS_GOOD);
+}
+
+/*
+ * []----
+ * | spc_pr_out_reserve
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+/* ARGSUSED */
+static int
+spc_pr_out_reserve(t10_cmd_t *cmd, void *data, size_t data_len)
+{
+	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cmd->c_cdb;
+	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+	sbc_reserve_t		*res = &p->d_sbc_reserve;
+	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
+	spc_pr_rsrv_t		*rsrv;
+	scsi_prout_plist_t	*plist = (scsi_prout_plist_t *)data;
+	uint64_t		reservation_key;
+	uint64_t		service_key;
+	int			status;
+
+	/*
+	 * Do not allow an unregistered initiator to
+	 * make a reservation.
+	 */
+	reservation_key = SCSI_READ64(plist->reservation_key);
+	service_key = SCSI_READ64(plist->service_key);
+
+	queue_prt(mgmtq, Q_PR_NONIO,
+	    "PGR%x LUN%d reserve reservation:%016lx, key:%016lx\n",
+	    cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+	    reservation_key, service_key);
+
+	if (!spc_pr_key_find(
+	    pgr, reservation_key, T10_PGR_INAME(cmd), T10_PGR_TNAME(cmd))) {
+
+		queue_prt(mgmtq, Q_PR_ERRS,
+		    "PGROUT: reserve service:%016lx not found\n",
+		    reservation_key);
+
+		return (STATUS_RESERVATION_CONFLICT);
+	}
+
+	/*
+	 * See if there is a reservation on this port by
+	 * another Initiator.  There can be only one LU_SCOPE
+	 * reservation per ITL.  We do not support extents.
+	 */
+	if (rsrv = spc_pr_rsrv_find(pgr, 0, "", T10_PGR_TNAME(cmd))) {
+		if (strcmp(rsrv->r_i_name, T10_PGR_INAME(cmd)) != 0) {
+
+			queue_prt(mgmtq, Q_PR_ERRS,
+			    "PGROUT: reserve %s != %s:%s\n", rsrv->r_i_name,
+			    T10_PGR_INAME(cmd), T10_PGR_TNAME(cmd));
+
+			return (STATUS_RESERVATION_CONFLICT);
+		}
+	}
+
+	/*
+	 * At this point there is either no reservation or the
+	 * reservation is held by this Initiator.
+	 */
+	if (rsrv != NULL) {
+
+		/*
+		 * An Initiator cannot re-reserve.  It must first
+		 * release.  But if its' type and scope match then
+		 * return STATUS_GOOD.
+		 */
+		if (rsrv->r_type == p_prout->type &&
+		    rsrv->r_scope == p_prout->scope) {
+			queue_prt(mgmtq, Q_PR_NONIO,
+			    "PGROUT reserve - transportID=%s\n"
+			    "\tkey:%016lx i_name:%s scope:%d type:%d \n",
+			    rsrv->r_transportID, rsrv->r_key, rsrv->r_i_name,
+			    rsrv->r_scope, rsrv->r_type);
+			status = STATUS_GOOD;
+		} else {
+			queue_prt(mgmtq, Q_PR_ERRS,
+			    "PGROUT reserve failed - transportID=%s\n"
+			    "\tkey:%016lx i_name:%s scope:%d type:%d \n",
+			    rsrv->r_transportID, rsrv->r_key, rsrv->r_i_name,
+			    rsrv->r_scope, rsrv->r_type);
+			status = STATUS_RESERVATION_CONFLICT;
+		}
+	} else {
+		/*
+		 * No reservation exists.  Establish a new one.
+		 */
+		queue_prt(mgmtq, Q_PR_NONIO,
+		    "PGROUT reserve - transportID=%s\n"
+		    "\tkey:%016lx i_name:%s scope:%d type:%d \n",
+		    T10_PGR_TNAME(cmd), reservation_key, T10_PGR_INAME(cmd),
+		    p_prout->scope, p_prout->type);
+
+		rsrv = spc_pr_rsrv_alloc(pgr, reservation_key,
+		    T10_PGR_INAME(cmd), T10_PGR_TNAME(cmd),
+		    p_prout->scope, p_prout->type);
+		if (rsrv == NULL) {
+			cmd->c_lu->l_status = KEY_ABORTED_COMMAND;
+			cmd->c_lu->l_asc = SPC_ASC_MEMORY_OUT_OF;
+			cmd->c_lu->l_ascq = SPC_ASCQ_RESERVATION_FAIL;
+			status = STATUS_CHECK;
+		} else {
+			status = STATUS_GOOD;
+		}
+	}
+
+	return (status);
+}
+
+/*
+ * []----
+ * | spc_pr_out_release
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+static int
+spc_pr_out_release(t10_cmd_t *cmd, void *data, size_t data_len)
+{
+	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cmd->c_cdb;
+	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+	sbc_reserve_t		*res = &p->d_sbc_reserve;
+	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
+	spc_pr_rsrv_t		*rsrv;
+	scsi_prout_plist_t	*plist = (scsi_prout_plist_t *)data;
+	uint64_t		reservation_key;
+	uint64_t		service_key;
+	int			status;
+
+	/*
+	 * Do not allow an unregistered initiator to
+	 * make a reservation.
+	 */
+	reservation_key = SCSI_READ64(plist->reservation_key);
+	service_key = SCSI_READ64(plist->service_key);
+
+	queue_prt(mgmtq, Q_PR_NONIO,
+	    "PGR%x LUN%d release reservation:%016lx, key:%016lx\n",
+	    cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+	    reservation_key, service_key);
+
+	if (!spc_pr_key_find(
+	    pgr, reservation_key, T10_PGR_INAME(cmd), T10_PGR_TNAME(cmd))) {
+
+		queue_prt(mgmtq, Q_PR_ERRS,
+		    "PGROUT: release service:%016lx not found\n",
+		    reservation_key);
+
+		return (STATUS_RESERVATION_CONFLICT);
+	} else {
+
+		queue_prt(mgmtq, Q_PR_NONIO,
+		    "PGROUT: release service:%016lx\n", service_key);
+	}
+
+	/*
+	 * Releasing a non-existent reservation is allowed.
+	 */
+	if (!(rsrv = spc_pr_rsrv_find(
+	    pgr, 0, T10_PGR_INAME(cmd), T10_PGR_TNAME(cmd)))) {
+
+		status = STATUS_GOOD;
+
+	} else if (p_prout->scope != rsrv->r_scope ||
+	    p_prout->type != rsrv->r_type ||
+	    reservation_key != rsrv->r_key) {
+		queue_prt(mgmtq, Q_PR_ERRS,
+		    "PGROUT release failed - transportID=%s\n"
+		    "\tkey:%016lx i_name:%s scope:%d type:%d \n",
+		    T10_PGR_TNAME(cmd), reservation_key, T10_PGR_INAME(cmd),
+		    p_prout->scope, p_prout->type);
+
+		/*
+		 * Scope and key must match to release.
+		 */
+		cmd->c_lu->l_status = KEY_ILLEGAL_REQUEST;
+		cmd->c_lu->l_asc = SPC_ASC_PARAMETERS_CHANGED;
+		cmd->c_lu->l_ascq = SPC_ASCQ_RES_RELEASED;
+		status = STATUS_CHECK;
+	} else {
+		/*
+		 * Now release the reservation.
+		 */
+		queue_prt(mgmtq, Q_PR_NONIO,
+		    "PGROUT release - transportID=%s\n"
+		    "\tkey:%016lx i_name:s scope:%d type:%d \n",
+		    rsrv->r_transportID, rsrv->r_key, rsrv->r_i_name,
+		    rsrv->r_scope, rsrv->r_type);
+
+		spc_pr_rsrv_release(cmd, pgr, rsrv);
+		status = STATUS_GOOD;
+	}
+
+	return (status);
+}
+
+/*
+ * []----
+ * | spc_pr_out_preempt
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+/* ARGSUSED */
+static int
+spc_pr_out_preempt(t10_cmd_t *cmd, void *data, size_t data_len)
+{
+	scsi_cdb_prout_t	*p_prout = (scsi_cdb_prout_t *)cmd->c_cdb;
+	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+	sbc_reserve_t		*res = &p->d_sbc_reserve;
+	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
+	scsi_prout_plist_t	*plist = (scsi_prout_plist_t *)data;
+	uint64_t		reservation_key;
+	uint64_t		service_key;
+	spc_pr_key_t		*key, *key_next;
+	spc_pr_rsrv_t		*rsrv, *rsrv_next;
+	t10_lu_impl_t		*lu;
+	int			status = STATUS_GOOD;
+
+	/*
+	 * Get reservation values
+	 */
+	reservation_key = SCSI_READ64(plist->reservation_key);
+	service_key = SCSI_READ64(plist->service_key);
+
+	queue_prt(mgmtq, Q_PR_NONIO,
+	    "PGR%x LUN%d preempt reservation:%016lx, key:%016lx\n",
+	    cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+	    reservation_key, service_key);
+
+	/*
+	 * Service key (preempt key) must exist, and
+	 * Initiator must be registered
+	 */
+	if (spc_pr_key_find(pgr, service_key, "", "") == NULL ||
+	    spc_pr_key_find(pgr, reservation_key, T10_PGR_INAME(cmd), "") ==
+	    NULL) {
+
+		queue_prt(mgmtq, Q_PR_ERRS,
+		    "PGROUT: preempt failed reservation:%016lx, key:%016lx\n",
+		    reservation_key, service_key);
+
+		return (STATUS_RESERVATION_CONFLICT);
+	}
+
+	/*
+	 * Preempt all keys matching service action key and free
+	 * the associated structures.  Do not set UNIT_ATTN for
+	 * the Initiator which requested the action.
+	 *
+	 * Unlike the other Persistent Reservation commands, the preempt,
+	 * preempt_and_abort and clear actions are service delivery port
+	 * independent.  So we remove matching keys across ports.
+	 */
+	for (key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd;
+	    key != (spc_pr_key_t *)&pgr->pgr_keylist;
+	    key = (spc_pr_key_t *)key_next) {
+
+		Boolean_t	unit_attn;
+
+		/*
+		 * Get next pointer in case the key gets deallocated
+		 */
+		key_next = (spc_pr_key_t *)key->k_link.lnk_fwd;
+
+		/* Skip non-matching keys */
+		if (key->k_key != service_key) {
+			queue_prt(mgmtq, Q_PR_NONIO,
+			    "PGROUT preempt key:%016lx != key:%016lx "
+			    "i_name:%s transportID:%s\n", service_key,
+			    key->k_key, key->k_i_name, key->k_transportID);
+			continue;
+		}
+
+		/*
+		 * Determine if UNIT ATTN needed
+		 */
+		unit_attn = strcmp(key->k_i_name, T10_PGR_INAME(cmd));
+
+		/*
+		 * Remove the registration key
+		 */
+		queue_prt(mgmtq, Q_PR_NONIO,
+		    "PGROUT preempt delete key:%016lx "
+		    "i_name:%s transportID:%s\n",
+		    key->k_key, key->k_i_name, key->k_transportID);
+		spc_pr_key_free(pgr, key);
+
+		/*
+		 * UNIT ATTN needed ?
+		 * Do not set UNIT ATTN for calling Initiator
+		 */
+		if (unit_attn == False)
+			continue;
+
+		/*
+		 * Is this the preempt and abort?
+		 */
+		if (p_prout->action == PR_OUT_PREEMPT_ABORT) {
+			queue_message_set(
+			    cmd->c_lu->l_common->l_from_transports,
+			    Q_HIGH, msg_reset_lu, (void *)cmd->c_lu);
+		}
+
+		/*
+		 * Find associated I_T Nexuses
+		 */
+		(void) pthread_mutex_lock(&cmd->c_lu->l_common->l_common_mutex);
+		lu = avl_first(&cmd->c_lu->l_common->l_all_open);
+		do {
+			lu->l_status	= KEY_UNIT_ATTENTION;
+			lu->l_asc	= SPC_ASC_PARAMETERS_CHANGED;
+			lu->l_ascq	= SPC_ASCQ_RES_PREEMPTED;
+			lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
+		} while (lu != NULL);
+		(void) pthread_mutex_unlock(
+		    &cmd->c_lu->l_common->l_common_mutex);
+	}
+
+	/*
+	 * Re-establish our service key if we preempted it.
+	 */
+	if (!(key = spc_pr_key_find(
+	    pgr, reservation_key, T10_PGR_INAME(cmd), T10_PGR_TNAME(cmd)))) {
+
+		queue_prt(mgmtq, Q_PR_NONIO,
+		    "PGROUT: preempt - register:%016lx, i_name:%s:%s\n",
+		    reservation_key, T10_PGR_INAME(cmd), T10_PGR_TNAME(cmd));
+
+		key = spc_pr_key_alloc(pgr, reservation_key,
+		    T10_PGR_INAME(cmd), T10_PGR_TNAME(cmd));
+		if (key == NULL) {
+			cmd->c_lu->l_status = KEY_ABORTED_COMMAND;
+			cmd->c_lu->l_asc = SPC_ASC_MEMORY_OUT_OF;
+			cmd->c_lu->l_ascq = SPC_ASCQ_RESERVATION_FAIL;
+			return (STATUS_CHECK);
+		}
+	}
+
+	/*
+	 * Now look for a matching reservation to preempt.
+	 */
+	for (rsrv = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd;
+	    rsrv != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist;
+	    rsrv = (spc_pr_rsrv_t *)rsrv_next) {
+
+		/*
+		 * Get next pointer in case the reservation gets deallocated
+		 */
+		rsrv_next = (spc_pr_rsrv_t *)rsrv->r_link.lnk_fwd;
+
+		/* Skip non-matching keys */
+		if (rsrv->r_key != service_key) {
+			queue_prt(mgmtq, Q_PR_NONIO,
+			    "PGROUT preempt rsrv:%016lx != rsrv:%016lx"
+			    "i_name:%s scope:%d type:%d \n", service_key,
+			    rsrv->r_key, rsrv->r_i_name,
+			    rsrv->r_scope, rsrv->r_type);
+			continue;
+		}
+
+		/*
+		 * Remove matching reservations on other ports
+		 * and establish a new reservation on this port only.
+		 * To change the fuctionality to preempt rather than
+		 * delete the reservations on other ports just remove
+		 * the following block of code.
+		 */
+		if (strcmp(rsrv->r_transportID, T10_PGR_TNAME(cmd))) {
+			queue_prt(mgmtq, Q_PR_NONIO,
+			    "PGROUT preempt(-) rsrv:%016lx "
+			    "i_name:%s scope:%d type:%d \n",
+			    rsrv->r_key, rsrv->r_i_name,
+			    rsrv->r_scope, rsrv->r_type);
+
+			spc_pr_rsrv_free(pgr, rsrv);
+			continue;
+		} else {
+			/*
+			 * We have a matching reservation so preempt it.
+			 */
+			rsrv->r_key = reservation_key;
+			rsrv->r_i_name = strdup(T10_PGR_INAME(cmd));
+			rsrv->r_scope = p_prout->scope;
+			rsrv->r_type = p_prout->type;
+
+			queue_prt(mgmtq, Q_PR_NONIO,
+			    "PGROUT preempt(+) rsrv:%016lx "
+			    "i_name:%s scope:%d type:%d \n",
+			    rsrv->r_key, rsrv->r_i_name,
+			    rsrv->r_scope, rsrv->r_type);
+		}
+	}
+
+	return (status);
+}
+
+/*
+ * []----
+ * | spc_pr_out_clear
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+/* ARGSUSED */
+static int
+spc_pr_out_clear(t10_cmd_t *cmd, void *data, size_t data_len)
+{
+	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+	sbc_reserve_t		*res = &p->d_sbc_reserve;
+	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
+	scsi_prout_plist_t	*plist = (scsi_prout_plist_t *)data;
+	uint64_t		reservation_key;
+	uint64_t		service_key;
+	spc_pr_key_t		*key;
+	t10_lu_impl_t		*lu;
+
+	/*
+	 * Do not allow an unregistered initiator to attempting to
+	 * clear the PGR.
+	 */
+	reservation_key = SCSI_READ64(plist->reservation_key);
+	service_key = SCSI_READ64(plist->service_key);
+
+	queue_prt(mgmtq, Q_PR_NONIO,
+	    "PGR%x LUN%d clear reservation:%016lx, key:%016lx\n",
+	    cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num,
+	    reservation_key, service_key);
+
+	if (!spc_pr_key_find(pgr, reservation_key, T10_PGR_INAME(cmd), "")) {
+
+		queue_prt(mgmtq, Q_PR_ERRS,
+		    "PGROUT: clear service:%016lx not found\n",
+		    reservation_key);
+
+		return (STATUS_RESERVATION_CONFLICT);
+	}
+
+	/*
+	 * We need to set UNIT ATTENTION for all registered initiators.
+	 */
+	for (key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd;
+	    key != (spc_pr_key_t *)&pgr->pgr_keylist;
+	    key = (spc_pr_key_t *)key->k_link.lnk_fwd) {
+
+		/* Do not set UNIT ATTN for calling Initiator */
+		if (!(strcmp(key->k_i_name, T10_PGR_INAME(cmd))))
+			continue;
+		/*
+		 * At this point the only way to get in here is to be the owner
+		 * of the reservation.
+		 */
+		(void) pthread_mutex_lock(&cmd->c_lu->l_common->l_common_mutex);
+		lu = avl_first(&cmd->c_lu->l_common->l_all_open);
+		do {
+			lu->l_status = KEY_UNIT_ATTENTION;
+			lu->l_asc = SPC_ASC_PARAMETERS_CHANGED;
+			lu->l_ascq = SPC_ASCQ_RES_PREEMPTED;
+			lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
+		} while (lu != NULL);
+		(void) pthread_mutex_unlock(
+		    &cmd->c_lu->l_common->l_common_mutex);
+	}
+
+	/*
+	 * Now erase the reservation and registration info.
+	 */
+	spc_pr_erase(pgr);
+
+	return (STATUS_GOOD);
+}
+
+/*
+ * []----
+ * | spc_pr_out_register_and_move
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+static int
+spc_pr_out_register_and_move(t10_cmd_t *cmd, void *data, size_t data_len)
+{
+	return (STATUS_RESERVATION_CONFLICT);
+}
+
+/*
+ * []----
+ * | spc_pr_key_alloc -
+ * | 	Allocate a new registration key and add it to the key list.
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+static spc_pr_key_t *
+spc_pr_key_alloc(scsi3_pgr_t *pgr, uint64_t service_key, char *i_name,
+    char *transportID)
+{
+	spc_pr_key_t	*key = (spc_pr_key_t *)
+	    memalign(sizeof (void *), sizeof (spc_pr_key_t));
+
+	if (key != NULL) {
+		key->k_key = service_key;
+		key->k_i_name = strdup(i_name);
+		key->k_transportID = strdup(transportID);
+
+		insque(&key->k_link, pgr->pgr_keylist.lnk_bwd);
+
+		pgr->pgr_numkeys++;
+		assert(pgr->pgr_numkeys > 0);
+	}
+
+	return (key);
+}
+
+/*
+ * []----
+ * | spc_pr_initialize -
+ * |	Initialize registration & reservervation queues
+ * []----
+ */
+static void
+spc_pr_initialize(scsi3_pgr_t *pgr)
+{
+	assert(pgr->pgr_numrsrv == 0);
+	assert(pgr->pgr_numkeys == 0);
+	pgr->pgr_rsrvlist.lnk_fwd = (key_link_t *)&pgr->pgr_rsrvlist.lnk_fwd;
+
+	assert(pgr->pgr_rsrvlist.lnk_bwd == NULL);
+	pgr->pgr_rsrvlist.lnk_bwd = (key_link_t *)&pgr->pgr_rsrvlist.lnk_fwd;
+
+	assert(pgr->pgr_keylist.lnk_fwd == NULL);
+	pgr->pgr_keylist.lnk_fwd = (key_link_t *)&pgr->pgr_keylist.lnk_fwd;
+
+	assert(pgr->pgr_keylist.lnk_bwd == NULL);
+	pgr->pgr_keylist.lnk_bwd = (key_link_t *)&pgr->pgr_keylist.lnk_fwd;
+}
+
+/*
+ * []----
+ * | spc_pr_key_free -
+ * |	Free a registration key
+ * []----
+ */
+static void
+spc_pr_key_free(scsi3_pgr_t *pgr, spc_pr_key_t *key)
+{
+	remque(&key->k_link);
+	free(key->k_i_name);
+	free(key->k_transportID);
+	free(key);
+
+	pgr->pgr_numkeys--;
+	assert(pgr->pgr_numkeys >= 0);
+}
+
+/*
+ * []----
+ * | spc_pr_key_find -
+ * |	Find a registration key based on the key, owner id and port id.
+ * []----
+ */
+static spc_pr_key_t *
+spc_pr_key_find(scsi3_pgr_t *pgr, uint64_t key, char *i_name, char *transportID)
+{
+	spc_pr_key_t	*kp;
+	spc_pr_key_t	*rval = NULL;
+
+	for (kp = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd;
+	    kp != (spc_pr_key_t *)&pgr->pgr_keylist;
+	    kp = (spc_pr_key_t *)kp->k_link.lnk_fwd) {
+		if ((key == 0 || kp->k_key == key) &&
+		    (strlen(i_name) == 0 ||
+		    (strcmp(kp->k_i_name, i_name) == 0)) &&
+		    (strlen(transportID) == 0 ||
+		    (strcmp(kp->k_transportID, transportID) == 0))) {
+			rval = kp;
+			break;
+		}
+	}
+
+	return (rval);
+}
+
+
+/*
+ * []----
+ * | spc_pr_rsrv_alloc -
+ * |	Allocate a new reservation and add it to the rsrv list.
+ * []----
+ */
+static spc_pr_rsrv_t *
+spc_pr_rsrv_alloc(scsi3_pgr_t *pgr, uint64_t service_key, char *i_name,
+    char *transportID, uint8_t scope, uint8_t type)
+{
+	spc_pr_rsrv_t	*rsrv = (spc_pr_rsrv_t *)
+	    memalign(sizeof (void *), sizeof (spc_pr_rsrv_t));
+
+	if (rsrv != NULL) {
+		rsrv->r_key = service_key;
+		rsrv->r_i_name = strdup(i_name);
+		rsrv->r_transportID = strdup(transportID);
+		rsrv->r_scope = scope;
+		rsrv->r_type = type;
+
+		insque(&rsrv->r_link, pgr->pgr_rsrvlist.lnk_bwd);
+
+		pgr->pgr_numrsrv++;
+		assert(pgr->pgr_numrsrv > 0);
+	}
+
+	return (rsrv);
+}
+
+
+/*
+ * []----
+ * | spc_pr_rsrv_free -
+ * |	Free a reservation.
+ * []----
+ */
+static void
+spc_pr_rsrv_free(scsi3_pgr_t *pgr, spc_pr_rsrv_t *rsrv)
+{
+	remque(&rsrv->r_link);
+	free(rsrv->r_i_name);
+	free(rsrv->r_transportID);
+	free(rsrv);
+
+	pgr->pgr_numrsrv--;
+	assert(pgr->pgr_numrsrv >= 0);
+}
+
+/*
+ * []----
+ * | spc_pr_rsrv_find -
+ * |	Find a reservation based on the key, owner id and port id.
+ * []----
+ */
+static spc_pr_rsrv_t *
+spc_pr_rsrv_find(scsi3_pgr_t *pgr, uint64_t key, char *i_name,
+    char *transportID)
+{
+	spc_pr_rsrv_t	*rp, *rval = NULL;
+
+	for (rp = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd;
+	    rp != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist;
+	    rp = (spc_pr_rsrv_t *)rp->r_link.lnk_fwd) {
+		if ((key == 0 || rp->r_key == key) &&
+		    (strlen(i_name) == 0 ||
+		    (strcmp(rp->r_i_name, i_name) == 0)) &&
+		    (strlen(transportID) == 0 ||
+		    (strcmp(rp->r_transportID, transportID) == 0))) {
+			rval = rp;
+			break;
+		}
+	}
+
+	return (rval);
+}
+
+/*
+ * []----
+ * | spc_pr_erase -
+ * |	Find specified key / reservation and erease it
+ * []----
+ */
+/*
+ */
+static void
+spc_pr_erase(scsi3_pgr_t *pgr)
+{
+	spc_pr_key_t		*key;
+	spc_pr_rsrv_t		*rsrv;
+
+	while ((key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd) !=
+	    (spc_pr_key_t *)&pgr->pgr_keylist) {
+		spc_pr_key_free(pgr, key);
+	}
+
+	assert(pgr->pgr_numkeys == 0);
+
+	while ((rsrv = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd) !=
+	    (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist) {
+		spc_pr_rsrv_free(pgr, rsrv);
+	}
+
+	assert(pgr->pgr_numrsrv == 0);
+
+	pgr->pgr_generation = 0;
+	pgr->pgr_aptpl = 0;
+}
+
+/*
+ * []----
+ * | spc_pr_rsrv_release -
+ * |	Release the reservation the perform any other required clearing actions.
+ * |	Refer to SPC-3, Section 6.1, Tables ?? and ??
+ * []----
+ */
+static void
+spc_pr_rsrv_release(t10_cmd_t *cmd, scsi3_pgr_t *pgr, spc_pr_rsrv_t *rsrv)
+{
+	t10_lu_impl_t		*lu;
+	spc_pr_key_t		*key;
+
+	/*
+	 * For Registrants-Only mode set UNIT ATTN.
+	 */
+	if (rsrv->r_type == PGR_TYPE_WR_EX_RO ||
+	    rsrv->r_type == PGR_TYPE_EX_AC_RO) {
+
+		for (key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd;
+		    key != (spc_pr_key_t *)&pgr->pgr_keylist;
+		    key = (spc_pr_key_t *)key->k_link.lnk_fwd) {
+
+			/*
+			 * No UNIT ATTN for the requesting Initiator.
+			 */
+			if (!(strcmp(key->k_i_name, T10_PGR_INAME(cmd))))
+				continue;
+
+			/*
+			 * Find associated I_T Nexuses
+			 */
+			(void) pthread_mutex_lock(
+			    &cmd->c_lu->l_common->l_common_mutex);
+			lu = avl_first(&cmd->c_lu->l_common->l_all_open);
+			do {
+				lu->l_status	= KEY_UNIT_ATTENTION;
+				lu->l_asc	= SPC_ASC_PARAMETERS_CHANGED;
+				lu->l_ascq	= SPC_ASCQ_RES_RELEASED;
+				lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open,
+				    lu);
+			} while (lu != NULL);
+			(void) pthread_mutex_unlock(
+			    &cmd->c_lu->l_common->l_common_mutex);
+		}
+	}
+
+	/*
+	 * Remove the reservation.
+	 */
+	spc_pr_rsrv_free(pgr, rsrv);
+}
+
+/*
+ * []----
+ * | spc_pr_read -
+ * |	Read in pgr keys and reservations for this device from backend storage.
+ * |	At least the local pgr write lock must be held.
+ * []----
+ */
+void
+spc_pr_read(t10_cmd_t *cmd)
+{
+	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+	sbc_reserve_t		*res = &p->d_sbc_reserve;
+	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
+	spc_pr_key_t		*key;
+	spc_pr_rsrv_t		*rsrv;
+	spc_pr_diskkey_t	*klist;
+	spc_pr_diskrsrv_t	*rlist;
+	spc_pr_persist_disk_t	*buf = NULL;
+	t10_lu_impl_t		*lu;
+	int			i, pfd;
+	Boolean_t		status = False;
+	char			path[MAXPATHLEN];
+
+	/*
+	 * Open the PERSISTANCE file specification if one exists
+	 */
+	(void) snprintf(path, MAXPATHLEN, "%s/%s/%s%d",
+	    target_basedir, cmd->c_lu->l_targ->s_targ_base,
+	    PERSISTANCEBASE, cmd->c_lu->l_common->l_num);
+	if ((pfd = open(path, O_RDONLY)) >= 0) {
+		struct stat pstat;
+		if ((fstat(pfd, &pstat)) == 0)
+			if (pstat.st_size > 0)
+				if (buf  = malloc(pstat.st_size))
+					if (read(pfd, buf, pstat.st_size) ==
+					    pstat.st_size)
+						status = True;
+	}
+
+	/*
+	 * Clean up on no persistence file found
+	 */
+	if (status == False) {
+		if (pfd >= 0)
+			close(pfd);
+		if (buf)
+			free(buf);
+		return;
+	}
+
+	/*
+	 * If this is the first time using the persistance data,
+	 * initialize the reservation and resource key queues
+	 */
+	if (pgr->pgr_rsrvlist.lnk_fwd == NULL) {
+		(void) spc_pr_initialize(pgr);
+	}
+
+	/*
+	 * Perform some vailidation on what we are looking at
+	 */
+	assert(buf->magic == PGRMAGIC);
+	assert(buf->revision == SPC_PGR_PERSIST_DATA_REVISION);
+
+	/*
+	 * Get the PGR keys
+	 */
+	klist = (spc_pr_diskkey_t *)&buf->keylist[0];
+	for (i = 0; i < buf->numkeys; i++) {
+		assert(klist[i].rectype == PGRDISKKEY);
+
+		/*
+		 * Was the key previously read, if not restore it
+		 */
+		key = spc_pr_key_find(pgr, 0, T10_PGR_INAME(cmd),
+		    T10_PGR_TNAME(cmd));
+		if (key == NULL)
+			key = spc_pr_key_alloc(pgr, klist[i].key,
+			    klist[i].i_name, klist[i].transportID);
+		assert(key);
+	}
+
+	/*
+	 * Get the PGR reservations
+	 */
+	rlist = (spc_pr_diskrsrv_t *)&buf->keylist[buf->numkeys];
+	for (i = 0; i < buf->numrsrv; i++) {
+		assert(rlist[i].rectype == PGRDISKRSRV);
+
+		/*
+		 * Was the reservation previously read, if not restore it
+		 */
+		rsrv = spc_pr_rsrv_find(pgr, 0, T10_PGR_INAME(cmd),
+		    T10_PGR_TNAME(cmd));
+		if (rsrv == NULL)
+			rsrv = spc_pr_rsrv_alloc(pgr, rlist[i].key,
+			    rlist[i].i_name, rlist[i].transportID,
+			    rlist[i].scope, rlist[i].type);
+		assert(rsrv);
+	}
+
+	/*
+	 * If there was data then set the reservation type.
+	 */
+	if (pgr->pgr_numkeys > 0 || pgr->pgr_numrsrv > 0) {
+		res->res_type = RT_PGR;
+		pgr->pgr_generation = buf->generation;
+
+		/*
+		 * Set the command dispatcher according to the reservation type
+		 */
+		(void) pthread_mutex_lock(&cmd->c_lu->l_common->l_common_mutex);
+		lu = avl_first(&cmd->c_lu->l_common->l_all_open);
+		do {
+			lu->l_cmd = sbc_cmd_reserved;
+			lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu);
+		} while (lu != NULL);
+		(void) pthread_mutex_unlock(
+		    &cmd->c_lu->l_common->l_common_mutex);
+	}
+
+	free(buf);
+}
+
+/*
+ * []----
+ * | spc_pr_write -
+ * |	Write PGR keys and reservations for this device to backend storage.
+ * |	At least the local pgr write lock must be held.
+ * []----
+ */
+Boolean_t
+spc_pr_write(t10_cmd_t *cmd)
+{
+	disk_params_t		*p = (disk_params_t *)T10_PARAMS_AREA(cmd);
+	sbc_reserve_t		*res = &p->d_sbc_reserve;
+	scsi3_pgr_t		*pgr = &res->res_scsi_3_pgr;
+	spc_pr_key_t		*key;
+	spc_pr_rsrv_t		*rsrv;
+	spc_pr_diskkey_t	*klist;
+	spc_pr_diskrsrv_t	*rlist;
+	spc_pr_persist_disk_t	*buf;
+	ssize_t			length, bufsize;
+	int			i, pfd = -1;
+	char			path[MAXPATHLEN];
+	Boolean_t		status = True;
+
+	/*
+	 * Verify space requirements and allocate buffer memory.
+	 * Space needed is header + keylist + rsrvlist.
+	 * Subtract 1 from numkeys since header already defines
+	 * the first element of the keylist.
+	 * Round up the bufsize to the next FBA boundary.
+	 */
+	bufsize = sizeof (spc_pr_persist_disk_t) +
+	    (pgr->pgr_numkeys - 1) * sizeof (spc_pr_diskkey_t) +
+	    pgr->pgr_numrsrv * sizeof (spc_pr_diskrsrv_t);
+	bufsize = roundup(bufsize, 512);
+	if ((buf = memalign(sizeof (void *), bufsize)) == NULL)
+		return (False);
+	else
+		bzero(buf, bufsize);
+
+	/*
+	 * Build header.
+	 */
+	buf->magic = PGRMAGIC;
+	buf->revision = SPC_PGR_PERSIST_DATA_REVISION;
+	buf->generation = pgr->pgr_generation;
+	buf->numkeys = pgr->pgr_numkeys;
+	buf->numrsrv = pgr->pgr_numrsrv;
+
+	/*
+	 * Copy the keys.
+	 */
+	klist = buf->keylist;
+	for (i = 0, key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd;
+	    key != (spc_pr_key_t *)&pgr->pgr_keylist && i < pgr->pgr_numkeys;
+	    key = (spc_pr_key_t *)key->k_link.lnk_fwd, i++) {
+
+		klist[i].rectype = PGRDISKKEY;
+		klist[i].key = key->k_key;
+		strncpy(klist[i].i_name, key->k_i_name,
+		    sizeof (klist[i].i_name));
+		strncpy(klist[i].transportID, key->k_transportID,
+		    sizeof (klist[i].transportID));
+	}
+
+	/*
+	 * Copy the reservations.
+	 */
+	rlist = (spc_pr_diskrsrv_t *)&buf->keylist[pgr->pgr_numkeys];
+	for (i = 0, rsrv = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd;
+	    rsrv != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist &&
+	    i < pgr->pgr_numrsrv;
+	    rsrv = (spc_pr_rsrv_t *)rsrv->r_link.lnk_fwd, i++) {
+
+		rlist[i].rectype = PGRDISKRSRV;
+		rlist[i].key = rsrv->r_key;
+		rlist[i].scope = rsrv->r_scope;
+		rlist[i].type = rsrv->r_type;
+		strncpy(rlist[i].i_name, rsrv->r_i_name,
+		    sizeof (rlist[i].i_name));
+		strncpy(rlist[i].transportID, rsrv->r_transportID,
+		    sizeof (rlist[i].transportID));
+	}
+
+	/*
+	 * Open/create the PERSISTANCE file specification
+	 */
+	(void) snprintf(path, MAXPATHLEN, "%s/%s/%s%d",
+	    target_basedir, cmd->c_lu->l_targ->s_targ_base,
+	    PERSISTANCEBASE, cmd->c_lu->l_common->l_num);
+	if ((pfd = open(path, O_WRONLY|O_CREAT, 0600)) >= 0) {
+		length = write(pfd, buf, bufsize);
+		close(pfd);
+	} else {
+		if ((pfd < 0) || (length != bufsize))
+			status = False;
+	}
+
+	/*
+	 * Free allocated buffer
+	 */
+	free(buf);
+	return (status);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.h	Thu Sep 13 15:17:59 2007 -0700
@@ -0,0 +1,163 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _T10_SPC_PR_H
+#define	_T10_SPC_PR_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * SPC-3 Persistent Reservation specific structures and defines
+ */
+
+/*
+ * Key Linked Lists
+ */
+typedef struct key_link {
+	union {
+		uint64_t	align;
+		struct {
+			struct key_link *_lnk_fwd;	/* Forward element */
+			struct key_link *_lnk_bwd;	/* Backward element */
+		} key_ptr;
+	} key_link;
+} key_link_t;
+#define	lnk_fwd	key_link.key_ptr._lnk_fwd
+#define	lnk_bwd	key_link.key_ptr._lnk_bwd
+#define	insque(a, b) \
+	((key_link_t *)(a))->lnk_fwd = (key_link_t *)(b); \
+	((key_link_t *)(a))->lnk_bwd = ((key_link_t *)(b))->lnk_bwd; \
+	((key_link_t *)(b))->lnk_bwd = (key_link_t *)(a); \
+	((key_link_t *)(a))->lnk_bwd->lnk_fwd = (key_link_t *)(a);
+
+#define	remque(A) \
+	((key_link_t *)(A))->lnk_bwd->lnk_fwd = ((key_link_t *)(A))->lnk_fwd; \
+	((key_link_t *)(A))->lnk_fwd->lnk_bwd = ((key_link_t *)(A))->lnk_bwd;
+
+/*
+ * Reservation Types (res_type).
+ */
+typedef enum {
+	RT_NONE = 0,			/* None */
+	RT_PGR				/* SCSI-3 Persistent Reservation */
+} spc_reserve_types;
+
+/*
+ * Persistent reservation data.
+ */
+typedef struct spc_pr_key {
+	key_link_t	k_link;			/* Key linked list */
+	uint64_t	k_key;			/* registration key */
+	char		*k_i_name;		/* initiator name */
+	char		*k_transportID;		/* transport ID */
+} spc_pr_key_t;
+
+typedef struct spc_pr_rsrv {
+	key_link_t	r_link;			/* Key linked list */
+	uint64_t	r_key;			/* reservation key */
+	char		*r_i_name;		/* initiator name */
+	char		*r_transportID;		/* transport ID */
+	uint8_t		r_scope;		/* reservation scope */
+	uint8_t		r_type;			/* reservation type */
+} spc_pr_rsrv_t;
+
+/*
+ * Persistent Reservation data
+ */
+typedef struct scsi3_pgr {
+	uint32_t	pgr_generation;		/* PGR PRgeneration value */
+	uint16_t	pgr_unused;
+	uint16_t	pgr_bits	: 15,
+			pgr_aptpl	: 1;	/* persistence data exists */
+	int32_t		pgr_numkeys;		/* # entries in key list */
+	int32_t		pgr_numrsrv;		/* # entries in rsrv list */
+	key_link_t	pgr_keylist;		/* Registration key list */
+	key_link_t	pgr_rsrvlist;		/* reservation list */
+} scsi3_pgr_t;
+
+typedef struct sbc_reserve {
+	spc_reserve_types	res_type;	/* standard or pr active */
+	pthread_rwlock_t	res_rwlock;	/* Lock for coordination */
+	scsi3_pgr_t		res_scsi_3_pgr;	/* SCSI-3 PGR */
+} sbc_reserve_t;
+
+/*
+ * On-disk PGR data.
+ *
+ * NOTE: The following three structures should be rounded up to 512 bytes each
+ *	to prevent potential problems with on-disk data skew
+ */
+typedef struct spc_pr_diskkey {
+	uint64_t	key;			/* registration key (8) */
+	uint32_t	rectype;		/* record type (4) */
+	char		i_name[224];		/* initiator name (224) */
+	char		filler1[20];		/* filler to 256 bytes */
+	char		transportID[228];	/* transport ID (228) */
+	char		filler2[28];		/* filler to 512 bytes */
+} spc_pr_diskkey_t;
+
+typedef struct spc_pr_diskrsrv {
+	uint64_t	key;			/* reservation key (8) */
+	uint32_t	rectype;		/* record type (4) */
+	uint8_t		scope;			/* reservation scope (1) */
+	uint8_t		type;			/* reservation type (1) */
+	char		i_name[224]; 		/* initiator name (224) */
+	char		filler1[18];		/* filler to 256 bytes */
+	char		transportID[228];	/* Transport ID (228) */
+	char		filler2[28];		/* filler to 512 bytes */
+} spc_pr_diskrsrv_t;
+
+typedef struct spc_pr_persist_disk {
+	uint64_t	magic;			/* magic number (8) */
+	uint32_t	revision;		/* header format revision (4) */
+	uint32_t	generation;		/* pgr generation count (4) */
+	int32_t		numkeys;		/* # items in key list (4) */
+	int32_t		numrsrv;		/* # items in rsrv list (4) */
+	char		filler[488];		/* 8+4+4+4+4 */
+
+/*
+ * After the header the data is laid out as follows:
+ *	spc_pr_diskkey_t	keylist[];
+ *	spc_pr_diskrsrv_t	rsrvlist[];
+ */
+	spc_pr_diskkey_t	keylist[1];
+} spc_pr_persist_disk_t;
+
+
+#define	SPC_PGR_PERSIST_DATA_REVISION 0x01	/* REVISON = 1 */
+#define	PGRMAGIC	0x5047524D41474943LL	/* "PGRMAGIC" */
+#define	PGRDISKKEY	0x5047526B		/* "PGRk" */
+#define	PGRDISKRSRV	0x50475272		/* "PGRr" */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _T10_SPC_PR_H */
--- a/usr/src/cmd/iscsi/iscsitgtd/target.h	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/target.h	Thu Sep 13 15:17:59 2007 -0700
@@ -76,8 +76,16 @@
 #define	PARAMBASE		"params."
 #define	LUNBASE			"lun."
 #define	OSDBASE			"osd_root."
+#define	PERSISTANCEBASE		"pgr."
+#define	ISCSI_TARGET_ALIAS	"TargetAlias"
 
-#define	ISCSI_TARGET_ALIAS	"TargetAlias"
+/*
+ * Base file name for persistent reservation data (PR). The format used is pr.
+ * This name is used both to build the PR name and when searching the target
+ * directory for persistent reservation data. Don't change these names unless
+ * the upgrade path has been thought about.
+ */
+#define	PRBASE			"persistent_reservations"
 
 /*
  * The IQN names that are created use libuuid + the local target name
@@ -201,7 +209,8 @@
 extern Boolean_t	enforce_strict_guid,
 			thin_provisioning,
 			disable_tpgs,
-			dbg_timestamps;
+			dbg_timestamps,
+			pgr_persist;
 extern pthread_mutex_t	targ_config_mutex;
 extern umem_cache_t	*iscsi_cmd_cache,
 			*t10_cmd_cache,
--- a/usr/src/cmd/iscsi/iscsitgtd/util_queue.c	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/cmd/iscsi/iscsitgtd/util_queue.c	Thu Sep 13 15:17:59 2007 -0700
@@ -196,8 +196,8 @@
 queue_walker_free(target_queue_t *q, Boolean_t (*func)(msg_t *m, void *v),
     void *v1)
 {
-	msg_t	*m,		/* current working message */
-		*n;		/* next message */
+	msg_t	*m;		/* current working message */
+	msg_t	*n;		/* next message */
 
 	(void) pthread_mutex_lock(&q->q_mutex);
 	m = q->q_head;
@@ -234,8 +234,8 @@
 void
 queue_reset(target_queue_t *q)
 {
-	msg_t	*m,
-		*n;
+	msg_t	*m;
+	msg_t	*n;
 
 	(void) pthread_mutex_lock(&q->q_mutex);
 	m = q->q_head;
@@ -301,8 +301,8 @@
 void
 queue_free(target_queue_t *q, void (*free_func)(msg_t *))
 {
-	msg_t	*m,
-		*n;
+	msg_t	*m;
+	msg_t	*n;
 
 	(void) pthread_mutex_lock(&q->q_mutex);
 	m = q->q_head;
@@ -343,8 +343,8 @@
 {
 	int		len;
 	char		*m;
-	hrtime_t	h	= gethrtime(),
-			delta;
+	hrtime_t	h	= gethrtime();
+	hrtime_t	delta;
 	static hrtime_t	last_h	= 0;
 
 	(void) pthread_mutex_lock(&q_mutex);
--- a/usr/src/lib/libiscsitgt/common/iscsitgt_impl.h	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/lib/libiscsitgt/common/iscsitgt_impl.h	Thu Sep 13 15:17:59 2007 -0700
@@ -145,6 +145,7 @@
 #define	XML_ELEMENT_VALIDATE	"validate"
 #define	XML_ELEMENT_MORESPACE	"more-space-required"
 #define	XML_VALUE_TRUE		"true"
+#define	XML_ELEMENT_PGR_PERSIST	"PGR-persist"
 
 typedef enum {
 	NodeFree,
--- a/usr/src/pkgdefs/SUNWhea/prototype_com	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/pkgdefs/SUNWhea/prototype_com	Thu Sep 13 15:17:59 2007 -0700
@@ -1141,6 +1141,7 @@
 f none usr/include/sys/scsi/generic/inquiry.h 644 root bin
 f none usr/include/sys/scsi/generic/message.h 644 root bin
 f none usr/include/sys/scsi/generic/mode.h 644 root bin
+f none usr/include/sys/scsi/generic/persist.h 644 root bin
 f none usr/include/sys/scsi/generic/sense.h 644 root bin
 f none usr/include/sys/scsi/generic/status.h 644 root bin
 d none usr/include/sys/scsi/impl 755 root bin
--- a/usr/src/uts/common/sys/Makefile	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/uts/common/sys/Makefile	Thu Sep 13 15:17:59 2007 -0700
@@ -788,6 +788,7 @@
 	inquiry.h	\
 	message.h	\
 	mode.h		\
+	persist.h	\
 	sense.h		\
 	status.h
 
--- a/usr/src/uts/common/sys/scsi/generic/commands.h	Thu Sep 13 13:50:11 2007 -0700
+++ b/usr/src/uts/common/sys/scsi/generic/commands.h	Thu Sep 13 15:17:59 2007 -0700
@@ -332,6 +332,11 @@
 #define	SCMD_GROUP5		0xA0
 #define	SCMD_REPORT_LUNS	0xA0
 #define	SCMD_REPORT_TARGET_PORT_GROUPS	0xA3
+#define	SCMD_SET_DEVICE		0xA4
+#define	SCMD_SET_DEVICE_IDENTIFIER	0x06
+#define	SCMD_SET_PRIORITY		0x0e
+#define	SCMD_SET_TARGET_PORT_GROUPS	0x0a
+#define	SCMD_SET_TIMESTAMP		0x0f
 #define	SCMD_READ_G5		0xA8
 #define	SCMD_WRITE_G5		0xAA
 #define	SCMD_READ_MEDIA_SERIAL	0xAB
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/generic/persist.h	Thu Sep 13 15:17:59 2007 -0700
@@ -0,0 +1,404 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_SCSI_GENERIC_PERSIST_H
+#define	_SYS_SCSI_GENERIC_PERSIST_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * SCSI Persistence Data
+ *
+ * Format of data returned as a result of PERSISTENCE RESERVER { IN | OUT }
+ */
+
+/*
+ * SPC-3 revision 23, Section 6.11.1, Table 102
+ * Persistent Reservations
+ * Persistent Reserve In service actions
+ */
+#define	PR_IN_READ_KEYS		0x0 /* Read all registered reservation keys */
+#define	PR_IN_READ_RESERVATION	0x1 /* Reads th persistent reservations */
+#define	PR_IN_REPORT_CAPABILITIES 0x2 /* Returns capability information */
+#define	PR_IN_READ_FULL_STATUS	0x3 /* Reads complete information about all */
+				    /* registrations and the persistent */
+				    /* reservations, if any */
+/*
+ * SPC-3 revision 23, Section 6.11.3.3, Table 106
+ * Persistent reservation scope codes
+ */
+#define	PR_LU_SCOPE		0x0	/* Persistent reservation applies to */
+					/* full logical unit */
+/*
+ * SPC-3 revision 23, Section 6.11.3.4, Table 107
+ * Persistent Reservations
+ * Persistent reservation type codes
+ */
+#define	PGR_TYPE_WR_EX		0x1	/* Write Exclusive */
+#define	PGR_TYPE_EX_AC		0x3	/* Exclusive Access */
+#define	PGR_TYPE_WR_EX_RO	0x5	/* Write Exclusive, Registrants Only */
+#define	PGR_TYPE_EX_AC_RO	0x6	/* Exclusive Access, Registrants Only */
+#define	PGR_TYPE_WR_EX_AR	0x7	/* Write Exclusive, All Registrants */
+#define	PGR_TYPE_EX_AC_AR	0x8	/* Exclusive Access, All Registrants */
+
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.11.5 PERSISTENCE RESERVE IN
+ *	Table 111 - full status descriptor format
+ */
+/* Table 289 - iSCSI Initiator Device TransportID format */
+
+#define	iSCSI_PROTOCOL_ID	0x5	/* Table 262 - iSCSI Protocol ID  */
+#define	WW_UID_DEVICE_NAME	0x0	/* Table 288 - iSCSI Transport IDs */
+
+
+#if defined(_BIT_FIELDS_LTOH)
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.11.1 PERSISTENCE RESERVE IN
+ *	Table 101 - PERSISTENCE RESERVE IN command
+ */
+typedef struct scsi_cdb_prin {
+	uint8_t			cmd;
+	uint8_t			action : 5,
+				resbits : 3;
+	uint8_t			resbytes[5];
+	uint8_t			alloc_len[2];
+	uint8_t			control;
+} scsi_cdb_prin_t;
+
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.11.2 PERSISTENCE RESERVE IN
+ *	Table 103/104/105 - parameter data for READS KEYS
+ */
+typedef struct scsi_prin_rsrvdesc {
+	uint8_t			reservation_key[8];
+	uint8_t			obsolete1[4];
+	uint8_t			resbytes;
+	uint8_t			type : 4,
+				scope : 4;
+	uint8_t			obsolete2[2];
+} scsi_prin_rsrvdesc_t;
+typedef struct scsi_prin_readrsrv {
+	uint8_t			PRgeneration[4];
+	uint8_t			add_len[4];
+	union {
+		uint64_t		service_key[1];
+		scsi_prin_rsrvdesc_t	res_key_list[1];
+	} key_list;
+} scsi_prin_readrsrv_t;
+
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.11.4 PERSISTENCE RESERVE IN
+ * 	Table 108 - parameter data for REPORT CAPABILTIES
+ */
+typedef struct scsi_per_res_type {
+	uint8_t			resbits1 : 1,
+				wr_ex : 1,
+				resbits2 : 1,
+				ex_ac : 1,
+				resbits3 : 1,
+				wr_ex_ro : 1,
+				ex_ac_ro : 1,
+				wr_ex_ar : 1;
+	uint8_t			ex_ac_ar : 1,
+				resbits4 : 7;
+} scsi_per_res_type_t;
+typedef struct scsi_prin_rpt_cap {
+	uint8_t			length[2];
+	uint8_t			ptpl_c : 1,
+				resbits1 : 1,
+				atp_c : 1,
+				sip_c : 1,
+				crh : 1,
+				resbits2 : 3;
+	uint8_t			ptpl_a : 1,
+				resbits3 : 6,
+				tmv : 1;
+	scsi_per_res_type_t	pr_type;
+	uint8_t			resbytes[2];
+} scsi_prin_rpt_cap_t;
+
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.11.5 PERSISTENCE RESERVE IN
+ * 	Table 110/111 - parameter data for READ FULL STATUS
+ *	Table 281 - TransportId format
+ */
+typedef struct scsi_transport_id {
+	uint8_t			protocol_id : 4,
+				resbits : 2,
+				format_code : 2;
+	uint8_t			add_len[2];
+	char			iscsi_name[1];
+} scsi_transport_id_t;
+typedef struct scsi_prin_status_t {
+	uint8_t			reservation_key[8];
+	uint8_t			resbytes1[4];
+	uint8_t			r_holder : 1,
+				all_tg_pt : 1,
+				resbits : 6;
+	uint8_t			type : 4,
+				scope : 4;
+	uint8_t			resbytes2[4];
+	uint8_t			rel_tgt_port_id[2];
+	uint8_t			add_len[4];
+	scsi_transport_id_t	trans_id;
+} scsi_prin_status_t;
+typedef struct scsi_prin_full_status {
+	uint8_t			PRgeneration[4];
+	uint8_t			add_len[4];
+	scsi_prin_status_t	full_desc[1];
+} scsi_prin_full_status_t;
+
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.12.1 PERSISTENCE RESERVE OUT
+ *	Table 112 - PERSISTENCE RESERVE OUT command
+ */
+typedef struct scsi_cdb_prout {
+	uint8_t			cmd;
+	uint8_t			action : 5,
+				resbits : 3;
+	uint8_t			type : 4,
+				scope : 4;
+	uint8_t			resbytes[2];
+	uint8_t			param_len[4];
+	uint8_t			control;
+} scsi_cdb_prout_t;
+
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.12.3 PERSISTENCE RESERVE OUT
+ *	Table 114 - PERSISTENCE RESERVE OUT parameter list
+ */
+typedef struct scsi_prout_plist {
+	uint8_t			reservation_key[8];
+	uint8_t			service_key[8];
+	uint8_t			obsolete1[4];
+	uint8_t			aptpl : 1,
+				resbits1 : 1,
+				all_tg_pt : 1,
+				spec_i_pt : 1,
+				resbits2 : 4;
+	uint8_t			resbytes1;
+	uint8_t			obsolete2[2];
+	uint8_t			apd[1];
+} scsi_prout_plist_t;
+
+#elif defined(_BIT_FIELDS_HTOL)
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.11.1 PERSISTENCE RESERVE IN
+ *	Table 101 - PERSISTENCE RESERVE IN command
+ */
+typedef struct scsi_cdb_prin {
+	uint8_t			cmd;
+	uint8_t			resbits : 3,
+				action : 5;
+	uint8_t			resbytes[5];
+	uint8_t			alloc_len[2];
+	uint8_t			control;
+} scsi_cdb_prin_t;
+
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.11.2 PERSISTENCE RESERVE IN
+ *	Table 103/104/105 - parameter data for READS KEYS
+ */
+typedef struct scsi_prin_rsrvdesc {
+	uint8_t			reservation_key[8];
+	uint8_t			obsolete1[4];
+	uint8_t			resbytes;
+	uint8_t			scope : 4,
+				type : 4;
+	uint8_t			obsolete2[2];
+} scsi_prin_rsrvdesc_t;
+typedef struct scsi_prin_readrsrv {
+	uint8_t			PRgeneration[4];
+	uint8_t			add_len[4];
+	union {
+		uint64_t		service_key[1];
+		scsi_prin_rsrvdesc_t	res_key_list[1];
+	} key_list;
+} scsi_prin_readrsrv_t;
+
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.11.4 PERSISTENCE RESERVE IN
+ * 	Table 108 - parameter data for REPORT CAPABILTIES
+ */
+typedef struct scsi_per_res_type {
+	uint8_t			wr_ex_ar : 1,
+				ex_ac_ro : 1,
+				wr_ex_ro : 1,
+				resbits3 : 1,
+				ex_ac : 1,
+				resbits2 : 1,
+				wr_ex : 1,
+				resbits1 : 1;
+	uint8_t			resbits4 : 7,
+				ex_ac_ar : 1;
+} scsi_per_res_type_t;
+typedef struct scsi_prin_rpt_cap {
+	uint8_t			length[2];
+	uint8_t			resbits2 : 3,
+				crh : 1,
+				sip_c : 1,
+				atp_c : 1,
+				resbits1 : 1,
+				ptpl_c : 1;
+	uint8_t			tmv : 1,
+				resbits3 : 6,
+				ptpl_a : 1;
+	scsi_per_res_type_t	pr_type;
+	uint8_t			resbytes[2];
+} scsi_prin_rpt_cap_t;
+
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.11.5 PERSISTENCE RESERVE IN
+ * 	Table 110/111 - parameter data for READ FULL STATUS
+ *	Table 281 - TransportId format
+ */
+typedef struct scsi_transport_id {
+	uint8_t			format_code : 2,
+				resbits : 2,
+				protocol_id : 4;
+	uint8_t			add_len[2];
+	char			iscsi_name[1];
+} scsi_transport_id_t;
+typedef struct scsi_prin_status_t {
+	uint8_t			reservation_key[8];
+	uint8_t			resbytes1[4];
+	uint8_t			resbits : 6,
+				all_tg_pt : 1,
+				r_holder : 1;
+	uint8_t			scope : 4,
+				type : 4;
+	uint8_t			resbytes2[4];
+	uint8_t			rel_tgt_port_id[2];
+	uint8_t			add_len[4];
+	scsi_transport_id_t	trans_id;
+} scsi_prin_status_t;
+typedef struct scsi_prin_full_status {
+	uint8_t			PRgeneration[4];
+	uint8_t			add_len[4];
+	scsi_prin_status_t	full_desc[1];
+} scsi_prin_full_status_t;
+
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.12.1 PERSISTENCE RESERVE OUT
+ *	Table 112 - PERSISTENCE RESERVE OUT command
+ */
+typedef struct scsi_cdb_prout {
+	uint8_t			cmd;
+	uint8_t			resbits : 3,
+				action : 5;
+	uint8_t			scope : 4,
+				type : 4;
+	uint8_t			resbytes[2];
+	uint8_t			param_len[4];
+	uint8_t			control;
+} scsi_cdb_prout_t;
+
+/*
+ * Information obtained from:
+ *	SPC-3, Revision 23
+ *	Section 6.12.3 PERSISTENCE RESERVE OUT
+ *	Table 114 - PERSISTENCE RESERVE OUT parameter list
+ */
+typedef struct scsi_prout_plist {
+	uint8_t			reservation_key[8];
+	uint8_t			service_key[8];
+	uint8_t			obsolete1[4];
+	uint8_t			resbits1 : 4,
+				spec_i_pt : 1,
+				all_tg_pt : 1,
+				resbits2 : 1,
+				aptpl : 1;
+	uint8_t			resbytes1;
+	uint8_t			obsolete2[2];
+	uint8_t			apd[1];
+} scsi_prout_plist_t;
+
+#else
+#error	One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif	/* _BIT_FIELDS_LTOH */
+
+
+/*
+ * SPC-3 revision 23, Section 6.12.2, Table 113
+ * Persistent Reservations
+ * Persistent Reserve Out service action codes
+ */
+#define	PR_OUT_REGISTER		0x0	/* Register/unregister a reservation */
+					/* key with the device server */
+#define	PR_OUT_RESERVE		0x1	/* Create a persistent reservation */
+					/* having a specified SCOPE & TYPE */
+#define	PR_OUT_RELEASE		0x2	/* Release the selected persistent */
+					/* reservation */
+#define	PR_OUT_CLEAR		0x3	/* Clears all reservation keys and */
+					/* all persistent reservations */
+#define	PR_OUT_PREEMPT		0x4	/* Preempts persistent reservations */
+					/* and/or removes reservations */
+#define	PR_OUT_PREEMPT_ABORT	0x5	/* Preempts persistent reservations */
+					/* and/or removes reservations, and */
+					/* aborts all tasks for all preempted */
+					/* I_T nexuses */
+#define	PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY	0x06
+					/* Register a reservation key with */
+					/* the device server, or unregister a */
+					/* reservation key */
+#define	PR_OUT_REGISTER_MOVE	0x7	/* Register a reservation key for */
+					/* another I_T nexus with the device */
+					/* server and move a persistent */
+					/* reservation to the I_T nexus */
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_SCSI_GENERIC_PERSIST_H */