/*
* 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
*/
/*
*/
#include <sys/byteorder.h>
#include <sys/stmf_ioctl.h>
#include <sys/stmf_sbd_ioctl.h>
#include "stmf_sbd.h"
#include "sbd_impl.h"
void sbd_pgr_reset(sbd_lu_t *);
void sbd_pgr_keylist_dealloc(sbd_lu_t *);
char *sbd_get_devid_string(sbd_lu_t *);
static void sbd_swap_pgr_info(sbd_pgr_info_t *);
static void sbd_swap_pgrkey_info(sbd_pgr_key_info_t *);
static void sbd_pgr_key_free(sbd_pgr_key_t *);
static void sbd_pgr_out_reserve(scsi_task_t *);
static void sbd_pgr_out_release(scsi_task_t *);
static void sbd_pgr_out_clear(scsi_task_t *);
uint16_t);
extern void sbd_swap_section_hdr(sm_section_hdr_t *);
extern char sbd_ctoi(char c);
/*
*
*
* +-----------+
* | |sl_it_list
* | |---------------------------------------+
* | | |
* | sbd_lu_t | |
* | | |
* | | |
* | | |
* +-----+-----+ V
* | +-------+
* V | |
* +-----------+ pgr_key_list +------>| |
* | |------------+ +-->(NULL) | +- ---|sbd_it |
* | | | | | | | _data |
* | sbd_pgr_t | V | | | | |
* | | +-------+ | | +-------+
* | |---+ | | | | |
* | | | |sbd_pgr|---------+ | v
* +-----------+ | | _key_t|<----------+ +-------+
* | | | | |
* | | | | |
* | +-------+ +--------| |
* | |^ | | |
* | || | | |
* | v| | +-------+
* | +-------+ | |
* | | | | v
* | |ALL_TG_|<-------+ +-------+
* | |PT = 1 |<---------+ | |
* | | |---+ | | |
* | | | | +------| |
* (pgr_rsvholder +-------+ V | |
* pgr_flags& |^ (NUll) | |
* RSVD_ONE) || +-------+
* | v| |
* | +-------+ v
* | | | +-------+
* | | not | | |
* | |claimed|---+ | |
* | | | | +----| unreg |
* | | | V | | |
* | +-------+ (NUll) V | |
* | |^ (NUll) +-------+
* | || |
* | v| v
* | +-------+ +-------+
* | | | | |
* | |reserv-|<----------------| |
* +----->| ation|---------------->| |
* |holder | | |
* |key | | |
* +-------+ +-------+
* |^ |
* || v
* v| +-------+
* +-------+ | |
* | | | |
* | not |---+ +----| unreg |
* |claimed| | | | |
* | | V V | |
* | | (NUll) (NUll) +-------+
* +-------+ |
* | v
* v (NULL)
* (NULL)
*
*
*/
/* ----------------------- */ \
/* SPC-3 (rev 23) Table 31 */ \
/* ----------------------- */ \
((cdb[0]) == SCMD_INQUIRY) || \
((cdb[0]) == SCMD_LOG_SENSE_G1) || \
((cdb[0]) == SCMD_PERSISTENT_RESERVE_IN) || \
((cdb[0]) == SCMD_REPORT_LUNS) || \
((cdb[0]) == SCMD_REQUEST_SENSE) || \
((cdb[0]) == SCMD_TEST_UNIT_READY) || \
/* PREVENT ALLOW MEDIUM REMOVAL with prevent == 0 */ \
/* SERVICE ACTION IN with READ MEDIA SERIAL NUMBER (0x01) */ \
(((cdb[0]) == SCMD_SVC_ACTION_IN_G5) && ( \
/* MAINTENANCE IN with service actions REPORT ALIASES (0x0Bh) */ \
/* REPORT DEVICE IDENTIFIER (0x05) REPORT PRIORITY (0x0Eh) */ \
/* REPORT TARGET PORT GROUPS (0x0A) REPORT TIMESTAMP (0x0F) */ \
(((cdb[0]) == SCMD_MAINTENANCE_IN) && ( \
/* REGISTER and REGISTER_AND_IGNORE_EXISTING_KEY */ \
/* actions for PERSISTENT RESERVE OUT command */ \
(((cdb[0]) == SCMD_PERSISTENT_RESERVE_OUT) && ( \
/* ----------------------- */ \
/* SBC-3 (rev 17) Table 3 */ \
/* ----------------------- */ \
/* READ CAPACITY(10) */ \
((cdb[0]) == SCMD_READ_CAPACITY) || \
/* READ CAPACITY(16) */ \
(((cdb[0]) == SCMD_SVC_ACTION_IN_G4) && ( \
/* START STOP UNIT with START bit 0 and POWER CONDITION 0 */ \
(((cdb[0]) == SCMD_START_STOP) && ( \
/* End of PGR_CONFLICT_FREE_CMDS */
/* Commands allowed for registered IT nexues but not reservation holder */
(((cdb[0]) == SCMD_PERSISTENT_RESERVE_OUT) && ( \
/* List of commands allowed when WR_EX type reservation held */
#define PGR_READ_POSSIBLE_CMDS(c) ( \
((c) == SCMD_READ) || \
((c) == SCMD_READ_G1) || \
((c) == SCMD_READ_G4) || \
((c) == SCMD_READ_G5) || \
/* READ FETCH (10) (16) */ \
((c) == SCMD_READ_POSITION) || \
((c) == 0x90) || \
/* READ DEFECT DATA */ \
((c) == SCMD_READ_DEFECT_LIST) || \
((c) == 0xB7) || \
/* VERIFY (10) (16) (12) */ \
((c) == SCMD_VERIFY) || \
((c) == SCMD_VERIFY_G4) || \
((c) == SCMD_VERIFY_G5) || \
/* XDREAD (10) */ \
((c) == 0x52))
((type) == PGR_TYPE_WR_EX) || \
((type) == PGR_TYPE_EX_AC) || \
((type) == PGR_TYPE_WR_EX_RO) || \
((type) == PGR_TYPE_EX_AC_RO) || \
((type) == PGR_TYPE_WR_EX_AR) || \
((type) == PGR_TYPE_EX_AC_AR))
static void
{
return;
}
static void
{
}
{
sz = sizeof (sbd_pgr_info_t);
return (ret);
}
{
if (ret != SBD_SUCCESS) {
/* No PGR section found, means volume made before PGR support */
if (ret == SBD_NOT_FOUND) {
/* So just create a default PGR section */
}
return (ret);
}
}
} else {
}
}
/* Calculate the size and next offset */
/* Validate the key fields */
(spi_key->pgr_key_lpt_len == 0 &&
goto sbd_pgr_meta_load_failed;
}
goto sbd_pgr_meta_load_failed;
}
goto sbd_pgr_meta_load_failed;
}
} else {
/*
* This block is executed only if the metadata was
* stored before the implementation of Transport ID
* support.
*/
(scsi_devid_desc_t *)ptr);
goto sbd_pgr_meta_load_failed;
}
}
if (last_key) {
} else {
}
(i == spi->pgr_rsvholder_indx)) {
}
}
return (ret);
{
"for lun %s.", lun_name);
return (ret);
}
}
{
/* Calculate total pgr meta section size needed */
sz = sizeof (sbd_pgr_info_t);
sizeof (sbd_pgr_key_info_t) - 1 +
}
}
spi->pgr_numkeys = 0;
sz = sizeof (sbd_pgr_info_t);
spi->pgr_numkeys++;
}
sizeof (sbd_pgr_key_info_t) - 1 +
}
}
if (ret != SBD_SUCCESS) {
"back to existing PGR state after meta write "
"failure, may cause PGR inconsistancy for lun %s.",
lun_name);
} else {
}
}
return (ret);
}
static sbd_pgr_key_t *
{
}
}
return (key);
}
static void
{
if (key->pgr_key_lpt_id) {
}
if (key->pgr_key_rpt_id) {
}
}
void
{
}
}
}
/*
* Reset and clear the keys, Can be used in the case of Lun Reset
*/
void
{
pgr->pgr_PRgeneration = 0;
pgr->pgr_rsv_type = 0;
}
}
static void
{
}
} else {
if (key->pgr_key_it) {
}
}
if (key->pgr_key_next) {
}
if (key->pgr_key_prev) {
} else {
}
}
/*
* Remove keys depends on boolean variable "match"
* match = B_TRUE ==> Remove all keys which matches the given svc_key,
* except for IT equal to given "my_it".
* match = B_FALSE ==> Remove all keys which does not matches the svc_key,
* except for IT equal to given "my_it"
*/
static uint32_t
{
while (key) {
/*
* If the key is registered by current IT keep it,
* but just remove pgr pointers from other ITs
*/
}
} else {
}
count++;
}
}
return (count);
}
static void
{
continue;
}
}
/*
* Set the SBD_IT_PGR_CHECK_FLAG depends on variable "registered". See Below.
*
* If
* registered is B_TRUE => Set PGR_CHECK_FLAG on all registered IT nexus
* registered is B_FALSE => Set PGR_CHECK_FLAG on all unregistered IT nexus
*/
static void
{
if (it->pgr_key_ptr) {
if (registered == B_TRUE) {
}
} else {
if (registered == B_FALSE)
}
}
}
static boolean_t
{
return (B_FALSE);
/*
* You can skip target port name comparison if ALL_TG_PT flag
* is set for this key;
*/
return (B_FALSE);
}
}
return (B_TRUE);
}
{
return (key);
}
}
return (NULL);
}
void
{
return;
continue;
/*
* SBD_PGR_ALL_KEYS_HAS_IT is set only if no single key
* in the list has SBD_PGR_KEY_ALL_TG_PT flag set and
* pgr_key_it all keys points to some IT
*/
/* Check if key matches with given lpt rpt combination */
continue;
/* IT nexus devid information matches with this key */
/*
* If ALL_TG_PT is set, pgr_key_it will point to NULL,
* unless pgr->pgr_rsvholder pointing to this key.
* In that case, pgr_key_it should point to the IT
* which initiated that reservation.
*/
id->ident_length) == 0)
}
}
} else {
}
return;
}
}
/*
* Check for any PGR Reservation conflict. return 0 if access allowed
*/
int
{
/* If Registered */
return (0);
/* If you are registered */
/*
* Note: it->pgr_key_ptr is protected by sl_lock. Also,
* it is expected to change its value only with pgr_lock
* held. Hence we are safe to read its value without
* grabbing sl_lock. But make sure that the value used is
* not from registers by using "volatile" keyword.
* Since this funtion is in performance path, we may want
* to avoid grabbing sl_lock.
*/
/* If you are the reservation holder */
return (0);
}
/* If reserve type is not EX_AC */
/* If reserve type is WR_EX allow read */
return (0);
}
/* For all other reserve types allow access */
} else {
return (0);
}
}
/* If registered, allow these commands */
return (0);
}
}
}
/* For any case, allow these commands */
return (0);
}
/* Give read access if reservation type WR_EX for registrants */
return (0);
}
/* If you reached here, No access for you */
return (1);
}
void
{
case PR_IN_READ_KEYS:
break;
case PR_IN_READ_RESERVATION:
break;
break;
case PR_IN_READ_FULL_STATUS:
break;
default :
break;
}
}
void
{
case PR_OUT_REGISTER:
case PR_OUT_RESERVE:
case PR_OUT_RELEASE:
case PR_OUT_CLEAR:
case PR_OUT_PREEMPT:
case PR_OUT_PREEMPT_ABORT:
case PR_OUT_REGISTER_MOVE:
if (param_len < MAX_PGR_PARAM_LIST_LENGTH &&
param_len > 0) {
} else {
}
break;
default :
break;
}
}
void
{
return;
}
/* SPC3 - 6.12.1 */
return;
}
}
/*
* Common Reservation Conflict Checks
*
* It is okey to handle REGISTER_MOVE with same plist here,
* because we are only accessing reservation key feild.
*/
/*
* Currently it is not mandatory to have volatile keyword here,
* because, it->pgr_key_ptr is not accessed yet. But still
* keeping it to safe gaurd against any possible future changes.
*/
/* if IT is not yet registered send conflict status */
SBD_PGR_RSVD_NONE(pgr)) {
} else {
}
return;
}
/* Given reservation key should matches with registered key */
return;
}
}
case PR_OUT_REGISTER:
break;
case PR_OUT_REGISTER_MOVE:
break;
case PR_OUT_RESERVE:
break;
case PR_OUT_RELEASE:
break;
case PR_OUT_CLEAR:
break;
case PR_OUT_PREEMPT:
case PR_OUT_PREEMPT_ABORT:
break;
default :
break;
}
}
static void
{
++numkeys;
reg_key++;
}
}
static void
{
if (SBD_PGR_RSVD_NONE(pgr)) {
buf_len = 8;
} else {
} else {
}
buf_len = 24;
}
}
static void
{
/* Persist Though Power Loss Active */
cdb_len, 8);
}
/* Minimum required size, SPC3 rev23 Table 110 */
/* Full Satus Descriptor Fromat size, SPC3 rev23 Table 111 */
static void
{
/* 4 byte allocation length for CDB, SPC3 rev23, Table 101 */
/* PRgeneration and additional length fields */
}
i = 0;
while (key) {
}
} else {
}
++i;
}
}
static void
{
/* Handling already registered IT session */
if (key) {
return;
}
return;
}
return;
}
if (svc_key == 0) {
} else {
}
goto sbd_pgr_reg_done;
}
/* Handling unregistered IT session */
return;
}
if (svc_key == 0) {
/* Do we need to consider aptpl here? I don't think so */
pgr->pgr_PRgeneration++;
return;
}
}
return;
}
/* Length validation SPC3 rev23 Section 6.12.3 and Table 115 */
sizeof (uint32_t))
/* SPC3 rev23, adn_len should be multiple of 4 */
if (adn_len % 4 != 0 ||
adn_len < sizeof (scsi_transport_id_t) +
sizeof (uint32_t) ||
return;
}
tpdnum = 0;
/* Check the validity of given TransportIDs */
while (adn_len != 0) {
break;
/* SPC3 rev23, tpd_sz should be multiple of 4 */
break;
/* make sure that there is no duplicates */
for (i = 0; i < tpdnum; i++) {
break;
}
if (i < tpdnum)
break;
/* Check if the given IT nexus is already registered */
break;
tpdnum++;
}
if (adn_len != 0) {
sizeof (stmf_remote_port_t) * max_tpdnum);
return;
}
for (i = 0; i < tpdnum; i++) {
}
}
else
return;
}
}
pgr->pgr_PRgeneration++;
}
static sbd_pgr_key_t *
{
if (lpt)
/* set PGR_CHECK flag for all unregistered IT nexus */
} else {
}
if (it) {
} else {
}
if (pgr->pgr_keylist) {
}
return (key);
}
static void
{
}
}
}
static void
{
return;
}
if (SBD_PGR_RSVD(pgr)) {
return;
}
} else {
return;
}
/* In case there is no reservation exist */
} else {
return;
}
}
}
}
static void
{
} else {
}
lpt_len = sizeof (scsi_devid_desc_t) +
}
}
}
static void
{
if (SBD_PGR_RSVD(pgr)) {
return;
}
}
}
}
static void
{
/* Reset pgr_flags */
/* set unit attention condition if necessary */
}
pgr->pgr_rsv_type = 0;
}
static void
{
pgr->pgr_rsv_type = 0;
/* Remove all pointers from IT to pgr keys */
}
return;
}
}
pgr->pgr_PRgeneration++;
}
static void
{
if (SBD_PGR_RSVD_NONE(pgr)) {
if (svc_key == 0 ||
return;
}
if (svc_key == 0) {
return;
}
/* Validity check of scope and type */
return;
}
}
change_rsv = 1;
}
change_rsv = 1;
}
} else {
/*
* Remove matched keys in all cases, except when the
* current IT nexus holds the reservation and the given
* svc_key matches with registered key.
* Note that, if the reservation is held by another
* IT nexus, and svc_key matches registered key for
* that IT nexus, sbd_pgr_remove_key() is not expected
* return 0. Hence, returning check condition after
* releasing the reservation does not arise.
*/
== 0) {
return;
}
}
if (change_rsv) {
}
if (svc_key == 0) {
return;
}
} else {
== 0) {
return;
}
}
}
return;
}
}
pgr->pgr_PRgeneration++;
}
}
static void
{
/*
* Check whether the key holds the reservation or current reservation
* is of type all registrants.
*/
return;
}
if (svc_key == 0) {
return;
}
uint16_t));
return;
}
return;
}
return;
}
if (newkey) {
/* Set the pgr_key, irrespective of what it currently holds */
/* all_tg_pt is set for found key, copy lpt info to the key */
if (newkey->pgr_key_lpt_id &&
newkey->pgr_key_lpt_len > 0) {
}
lpt_len = sizeof (scsi_devid_desc_t) +
}
/* No IT nexus information, hence set PGR_CHEK flag */
} else {
}
/* Now reserve the key corresponding to the specified IT nexus */
}
/* Write to disk if currenty aptpl is set or given task is setting it */
else
return;
}
}
pgr->pgr_PRgeneration++;
}
void
if (it->pgr_key_ptr) {
}
}
break;
}
}
}
char *
{
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
return (str);
}