t10_spc_pr.c revision f218e94ba20e9f27fa304f7daae68e9129427398
/*
* 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
* 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/sysmacros.h>
#include <strings.h>
#include <unistd.h>
#include <pthread.h>
#include <assert.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;
/*
* Forward declarations
*/
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
{
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:
break;
case SCMD_DOORLOCK: /* SCMD_PREVENT_ALLOW_MEDIA_REMOVAL */
/*
* As per SPC-3, Revision 23, Table 31
* (prevent <> 0)
*/
break;
case SCMD_MAINTENANCE_IN: /* SCMD_REPORT_ */
/*
* As per SPC-3, Revision 23, Section 6.23
*/
/* SCMD_REPORT_SUPPORTED_OPERATION_CODES */
case 0x0c:
/* SCMD_REPORT_SUPPORTED_MANAGEMENT_FUNCTIONS */
case 0x0d:
break;
}
break;
case SCMD_MAINTENANCE_OUT:
/*
* SPC-3, Revision 23, Section 6.29
*/
case SCMD_SET_PRIORITY:
case SCMD_SET_TIMESTAMP:
break;
}
break;
case SCMD_READ:
case SCMD_READ_G1:
case SCMD_READ_G4:
/*
* Exclusive Access, and EA Registrants Only
*/
break;
}
return (conflict);
}
/*
* []----
* | spc_pgr_check -- PERSISTENT_RESERVE {IN|OUT} check of I_T_L
* | Refer to SPC-3, Section ?.?, Tables ?? and ??
* []----
*/
{
/*
* If no reservations exist, allow all remaining command types.
*/
if (pgr->pgr_numrsrv == 0) {
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.
*/
goto done;
}
/*
* Check the command against the reservation type for this port.
*/
case PGR_TYPE_WR_EX: /* Write Exclusive */
case PGR_TYPE_EX_AC: /* Exclusive Access */
else
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(
else
break;
case PGR_TYPE_WR_EX_AR: /* Write Exclusive, All Registrants */
case PGR_TYPE_EX_AC_AR: /* Exclusive Access, All Registrants */
else
break;
default:
break;
}
done:
? "(no name)"
? "<none>"
? "Write Exclusive"
? "Exclusive Access"
? "Report capabilties"
? "Write Exclusive, Registrants Only"
? "Exclusive Access, Registrants Only"
? "Write Exclusive, All Registrants"
? "Exclusive Access, All Registrants"
: "Uknown reservation type",
return (conflict);
}
/*
* []----
* | spc_cmd_pr_in -- PERSISTENT_RESERVE IN
* | Refer to SPC-3, Section 6.1, Tables ?? and ??
* []----
*/
/*ARGSUSED*/
void
{
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.
*/
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.
*/
"PGR%x LUN%d CDB:%s - spc_cmd_pr_in, len = 0\n",
? "(no name)"
return;
}
/*
* Allocate space with an alignment that will work for any casting.
*/
/*
* Lack of memory is not fatal, just too busy
*/
return;
} else {
}
/*
* Start processing, lock reservation
*/
? "Read keys"
? "Read reservation"
? "Report capabilties"
? "Read full status"
: "Uknown");
/*
* Per SPC-3, Revision 23, Table 102, validate ranget of service actions
*/
case PR_IN_READ_KEYS:
break;
case PR_IN_READ_RESERVATION:
break;
break;
case PR_IN_READ_FULL_STATUS:
break;
}
/*
* Complete processing, unlock reservation
*/
/*
* Now send the selected Persistent Reservation response back
*/
}
/*
* []----
* | spc_pr_in_readkey -
* | Refer to SPC-3, Section 6.1, Tables ?? and ??
* []----
*/
static int
{
int i = 0, max_buf_keys, hsize;
"PGRIN readkeys - transportID=%s\n", transportID);
if (pgr->pgr_numkeys)
continue;
if (i < max_buf_keys) {
"PGRIN readkeys - key:%016lx, i_name:%s\n",
i++;
}
else
break; /* No room left, leave now */
}
}
/*
* []----
* | spc_pr_in_readresv -
* | Refer to SPC-3, Section 6.1, Tables ?? and ??
* []----
*/
static int
{
int i = 0, max_buf_rsrv, hsize;
"PGRIN readrsrv - transportID=%s\n", transportID);
if (pgr->pgr_numrsrv)
continue;
if (i < max_buf_rsrv) {
"PGRIN readrsrv - "
"key:%016lx i_name:%s scope:%d type:%d \n",
i++;
}
else
break; /* No room left, leave now */
}
}
/*
* []----
* | spc_pr_in_repcap -
* | Refer to SPC-3, Section 6.1, Tables ?? and ??
* []----
*/
/*
*/
static int
{
return (sizeof (scsi_prin_rpt_cap_t));
}
/*
* []----
* | spc_pr_in_fullstat -
* | Refer to SPC-3, Section 6.1, Tables ?? and ??
* []----
*/
/*
*/
static int
{
int i = 0, max_buf_rsrv, hsize;
sizeof (scsi_prin_full_status_t);
if (pgr->pgr_numrsrv)
if (i < max_buf_rsrv) {
sizeof (scsi_transport_id_t));
i++;
}
else
break; /* No room left, leave now */
}
}
/*
* []----
* | spc_cmd_pr_out -- PERSISTENT_RESERVE OUT
* | Refer to SPC-3, Section 6.1, Tables ?? and ??
* []----
*/
/*ARGSUSED*/
void
{
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.
*/
return;
}
/*
* Information obtained from:
* SPC-3, Revision 23
* Section 6.12 PERSISTENCE RESERVE OUT
* Acquire ALLOCATION LENGTH from bytes 5 thru 8
*/
/*
* 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
*/
return;
}
/*
* Information obtained from:
* SPC-3, Revision 23
* Section 6.11.3.3 Persistent Reservation Scope
* SCOPE field shall be set to LU_SCOPE
*/
return;
}
/*
* Allocate space with an alignment that will work for any casting.
*/
/*
* Lack of memory is not fatal, just too busy
*/
return;
}
/*
* Now request the Persistent Reserve OUT parameter list
*/
}
/*
* []----
* | spc_cmd_pr_out_data -- DataIn phase of PERSISTENT_RESERVE OUT command
* []----
*/
/*ARGSUSED*/
void
{
int status;
/*
* If this is the first time using the persistance data,
* initialize the reservation and resource key queues
*/
}
? "Register & ignore existing key"
? "Register"
? "Reserve"
? "Release"
? "Clear"
? "Preempt & abort"
? "Preempt"
? "Register & move"
: "Uknown");
/*
* Now process the action.
*/
case PR_OUT_REGISTER:
/*
* PR_OUT_REGISTER_IGNORE differs from PR_OUT_REGISTER
* in that the reservation_key is ignored.
*/
break;
case PR_OUT_RESERVE:
break;
case PR_OUT_RELEASE:
break;
case PR_OUT_CLEAR:
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.
*/
break;
case PR_OUT_REGISTER_MOVE:
/*
* PR_OUT_REGISTER_MOVE registers a key for another I_T
*/
break;
}
/*
* Check status of action performed.
*/
if (status == STATUS_CHECK) {
/*
* Check condition required.
*/
return;
}
/*
* Handle Failed processing status
*/
if (status != STATUS_GOOD) {
return;
}
/*
* Successful, bump the PRgeneration value
*/
pgr->pgr_generation++;
/*
* If Activate Persist Through Power Loss (APTPL) is set, persist
* this PGR data on disk
*/
/*
* When the last registration is removed, PGR is no longer
* active and we must reset the reservation type.
*/
} else {
}
/*
* Set the command dispatcher according to the reservation type
*/
do {
? sbc_cmd : sbc_cmd_reserved;
/*
* Processing is complete, release mutex
*/
/*
* Send back a succesful response
*/
}
/*
* []----
* | spc_pr_out_register
* | Refer to SPC-3, Section 6.1, Tables ?? and ??
* []----
*/
static int
{
/*
* Validate Persistent Reserver Out parameter list
*/
return (STATUS_CHECK);
}
/*
* Determine if Activate Persist Trhough Power Loss (APTPL)
* is valid for this device server.
*/
/* pgr - define SCSI-3 error codes */
return (STATUS_CHECK);
}
/*
* Get reservation values
*/
"PGR%x LUN%d register reservation:%016lx, key:%016lx\n",
/*
* We may need register all initiators, depending on ALL_TG_TP
*/
do {
/*
* Find specified key
*/
if (key) {
/*
* What about ALL_TG_TP?
*/
/*
* The Initiator did not specify the
* existing key. Reservation conflict.
*/
return (STATUS_RESERVATION_CONFLICT);
}
/*
* Change existing key ?
*/
if (service_key) {
"PGROUT: change "
"old:%016lx = new:%016lx\n",
/*
* Overwrite (change) key
*/
} else {
/*
* Remove existing key
* NOTE: If we own the reservation then
* we must release it.
*/
"PGROUT: delete "
"old:%016lx = new:%016lx\n",
if (rsrv) {
}
}
}
} else {
/*
* What about ALL_TG_TP?
*/
/*
* Process request from un-registered Initiator.
*/
(reservation_key || service_key == 0)) {
/*
* Unregistered initiator is attempting
* to modify a key.
*/
return (STATUS_RESERVATION_CONFLICT);
}
/* pgr - define SCSI-3 error codes */
return (STATUS_CHECK);
}
}
}
/*
* Apply the last valid APTPL bit
* SPC-3, Revision 23
* Section 5.6.4.1 Preserving persistent reservervations and
* registrations through power loss
*/
return (STATUS_GOOD);
}
/*
* []----
* | spc_pr_out_reserve
* | Refer to SPC-3, Section 6.1, Tables ?? and ??
* []----
*/
/* ARGSUSED */
static int
{
int status;
/*
* Do not allow an unregistered initiator to
* make a reservation.
*/
"PGR%x LUN%d reserve reservation:%016lx, key:%016lx\n",
if (!spc_pr_key_find(
"PGROUT: reserve service:%016lx not found\n",
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.
*/
return (STATUS_RESERVATION_CONFLICT);
}
}
/*
* At this point there is either no reservation or the
* reservation is held by this Initiator.
*/
/*
* An Initiator cannot re-reserve. It must first
* release. But if its' type and scope match then
* return STATUS_GOOD.
*/
"PGROUT reserve - transportID=%s\n"
"\tkey:%016lx i_name:%s scope:%d type:%d \n",
} else {
"PGROUT reserve failed - transportID=%s\n"
"\tkey:%016lx i_name:%s scope:%d type:%d \n",
}
} else {
/*
* No reservation exists. Establish a new one.
*/
"PGROUT reserve - transportID=%s\n"
"\tkey:%016lx i_name:%s scope:%d type:%d \n",
} else {
}
}
return (status);
}
/*
* []----
* | spc_pr_out_release
* | Refer to SPC-3, Section 6.1, Tables ?? and ??
* []----
*/
static int
{
int status;
/*
* Do not allow an unregistered initiator to
* make a reservation.
*/
"PGR%x LUN%d release reservation:%016lx, key:%016lx\n",
if (!spc_pr_key_find(
"PGROUT: release service:%016lx not found\n",
return (STATUS_RESERVATION_CONFLICT);
} else {
"PGROUT: release service:%016lx\n", service_key);
}
/*
* Releasing a non-existent reservation is allowed.
*/
if (!(rsrv = spc_pr_rsrv_find(
"PGROUT release failed - transportID=%s\n"
"\tkey:%016lx i_name:%s scope:%d type:%d \n",
/*
* Scope and key must match to release.
*/
} else {
/*
* Now release the reservation.
*/
"PGROUT release - transportID=%s\n"
"\tkey:%016lx i_name:s scope:%d type:%d \n",
}
return (status);
}
/*
* []----
* | spc_pr_out_preempt
* | Refer to SPC-3, Section 6.1, Tables ?? and ??
* []----
*/
/* ARGSUSED */
static int
{
int status = STATUS_GOOD;
/*
* Get reservation values
*/
"PGR%x LUN%d preempt reservation:%016lx, key:%016lx\n",
/*
* Service key (preempt key) must exist, and
* Initiator must be registered
*/
NULL) {
"PGROUT: preempt failed reservation:%016lx, key:%016lx\n",
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.
*/
/*
* Get next pointer in case the key gets deallocated
*/
/* Skip non-matching keys */
"PGROUT preempt key:%016lx != key:%016lx "
"i_name:%s transportID:%s\n", service_key,
continue;
}
/*
* Determine if UNIT ATTN needed
*/
/*
* Remove the registration key
*/
"PGROUT preempt delete key:%016lx "
"i_name:%s transportID:%s\n",
/*
* UNIT ATTN needed ?
* Do not set UNIT ATTN for calling Initiator
*/
continue;
/*
* Is this the preempt and abort?
*/
}
/*
* Find associated I_T Nexuses
*/
do {
(void) pthread_mutex_unlock(
}
/*
* Re-establish our service key if we preempted it.
*/
if (!(key = spc_pr_key_find(
"PGROUT: preempt - register:%016lx, i_name:%s:%s\n",
return (STATUS_CHECK);
}
}
/*
* Now look for a matching reservation to preempt.
*/
/*
* Get next pointer in case the reservation gets deallocated
*/
/* Skip non-matching keys */
"PGROUT preempt rsrv:%016lx != rsrv:%016lx"
"i_name:%s scope:%d type:%d \n", 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.
*/
"PGROUT preempt(-) rsrv:%016lx "
"i_name:%s scope:%d type:%d \n",
continue;
} else {
/*
* We have a matching reservation so preempt it.
*/
"PGROUT preempt(+) rsrv:%016lx "
"i_name:%s scope:%d type:%d \n",
}
}
return (status);
}
/*
* []----
* | spc_pr_out_clear
* | Refer to SPC-3, Section 6.1, Tables ?? and ??
* []----
*/
/* ARGSUSED */
static int
{
/*
* Do not allow an unregistered initiator to attempting to
* clear the PGR.
*/
"PGR%x LUN%d clear reservation:%016lx, key:%016lx\n",
"PGROUT: clear service:%016lx not found\n",
return (STATUS_RESERVATION_CONFLICT);
}
/*
* We need to set UNIT ATTENTION for all registered initiators.
*/
/* Do not set UNIT ATTN for calling Initiator */
continue;
/*
* At this point the only way to get in here is to be the owner
* of the reservation.
*/
do {
(void) pthread_mutex_unlock(
}
/*
* Now erase the reservation and registration info.
*/
return (STATUS_GOOD);
}
/*
* []----
* | spc_pr_out_register_and_move
* | Refer to SPC-3, Section 6.1, Tables ?? and ??
* []----
*/
static int
{
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 *
char *transportID)
{
memalign(sizeof (void *), sizeof (spc_pr_key_t));
pgr->pgr_numkeys++;
}
return (key);
}
/*
* []----
* | spc_pr_initialize -
* | Initialize registration & reservervation queues
* []----
*/
static void
{
}
/*
* []----
* | spc_pr_key_free -
* | Free a registration key
* []----
*/
static void
{
pgr->pgr_numkeys--;
}
/*
* []----
* | spc_pr_key_find -
* | Find a registration key based on the key, owner id and port id.
* []----
*/
static spc_pr_key_t *
{
(strlen(transportID) == 0 ||
break;
}
}
return (rval);
}
/*
* []----
* | spc_pr_rsrv_alloc -
* | Allocate a new reservation and add it to the rsrv list.
* []----
*/
static spc_pr_rsrv_t *
{
memalign(sizeof (void *), sizeof (spc_pr_rsrv_t));
pgr->pgr_numrsrv++;
}
return (rsrv);
}
/*
* []----
* | spc_pr_rsrv_free -
* | Free a reservation.
* []----
*/
static void
{
pgr->pgr_numrsrv--;
}
/*
* []----
* | spc_pr_rsrv_find -
* | Find a reservation based on the key, owner id and port id.
* []----
*/
static spc_pr_rsrv_t *
char *transportID)
{
(strlen(transportID) == 0 ||
break;
}
}
return (rval);
}
/*
* []----
* | spc_pr_erase -
* | Find specified key / reservation and erease it
* []----
*/
/*
*/
static void
{
}
}
pgr->pgr_generation = 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
{
/*
* For Registrants-Only mode set UNIT ATTN.
*/
/*
* No UNIT ATTN for the requesting Initiator.
*/
continue;
/*
* Find associated I_T Nexuses
*/
(void) pthread_mutex_lock(
do {
lu);
(void) pthread_mutex_unlock(
}
}
/*
* Remove the reservation.
*/
}
/*
* []----
* | 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
{
int i, pfd;
char path[MAXPATHLEN];
/*
* Open the PERSISTANCE file specification if one exists
*/
}
/*
* Clean up on no persistence file found
*/
if (pfd >= 0)
if (buf)
return;
}
/*
* If this is the first time using the persistance data,
* initialize the reservation and resource key queues
*/
(void) spc_pr_initialize(pgr);
}
/*
* Perform some vailidation on what we are looking at
*/
/*
* Get the PGR keys
*/
/*
* Was the key previously read, if not restore it
*/
T10_PGR_TNAME(cmd));
}
/*
* Get the PGR reservations
*/
/*
* Was the reservation previously read, if not restore it
*/
T10_PGR_TNAME(cmd));
}
/*
* If there was data then set the reservation type.
*/
/*
* Set the command dispatcher according to the reservation type
*/
do {
(void) pthread_mutex_unlock(
}
}
/*
* []----
* | spc_pr_write -
* | Write PGR keys and reservations for this device to backend storage.
* | At least the local pgr write lock must be held.
* []----
*/
{
int i, pfd = -1;
char path[MAXPATHLEN];
/*
* 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) +
return (False);
else
/*
* Build header.
*/
/*
* Copy the keys.
*/
sizeof (klist[i].transportID));
}
/*
* Copy the reservations.
*/
i < pgr->pgr_numrsrv;
sizeof (rlist[i].transportID));
}
/*
*/
} else {
}
/*
* Free allocated buffer
*/
return (status);
}