Mercurial > illumos > illumos-gate
view usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.c @ 5044:366fa13eb032
6415440 iSCSI target needs persistent reserve support
author | jdunham |
---|---|
date | Thu, 13 Sep 2007 15:17:59 -0700 |
parents | deleted_files/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.c@98925bbaa307 |
children | ddcf8536c1f9 |
line wrap: on
line source
/* * 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); }