sbd_scsi.c revision 450396635f70344c58b6b1e4db38cf17ff34445c
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/byteorder.h>
#include <stmf.h>
#include <lpif.h>
#include <portif.h>
#include <stmf_ioctl.h>
#include <stmf_sbd.h>
#include <stmf_sbd_ioctl.h>
#include <sbd_impl.h>
#define SCSI2_CONFLICT_FREE_CMDS(cdb) ( \
/* ----------------------- */ \
/* Refer Both */ \
/* SPC-2 (rev 20) Table 10 */ \
/* SPC-3 (rev 23) Table 31 */ \
/* ----------------------- */ \
((cdb[0]) == SCMD_INQUIRY) || \
((cdb[0]) == SCMD_LOG_SENSE_G1) || \
((cdb[0]) == SCMD_RELEASE) || \
((cdb[0]) == SCMD_RELEASE_G1) || \
((cdb[0]) == SCMD_REPORT_LUNS) || \
((cdb[0]) == SCMD_REQUEST_SENSE) || \
/* 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) && ( \
/* ----------------------- */ \
/* 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 SCSI2_CONFLICT_FREE_CMDS */
struct stmf_data_buf *initial_dbuf);
struct stmf_data_buf *initial_dbuf);
extern void sbd_pgr_initialize_it(scsi_task_t *);
extern int sbd_pgr_reservation_conflict(scsi_task_t *);
extern void sbd_pgr_reset(sbd_lu_t *);
/*
* IMPORTANT NOTE:
* =================
* The whole world here is based on the assumption that everything within
* a scsi task executes in a single threaded manner, even the aborts.
* Dont ever change that. There wont be any performance gain but there
* will be tons of race conditions.
*/
void
struct stmf_data_buf *dbuf)
{
int ndx;
int bufs_to_take;
/* Lets try not to hog all the buffers the port has. */
if (iolen == 0)
break;
/* Do not need to do xfer anymore, just complete it */
dbuf->db_data_size = 0;
return;
}
}
do {
/*
* A bad port implementation can keep on failing the
* the request but keep on sending us a false
* minsize.
*/
(minsize >= 512));
return;
}
}
}
void
struct stmf_data_buf *dbuf)
{
return;
}
return; /* wait for all buffers to complete */
else
return;
}
/* allocate new dbuf */
do {
(minsize >= 512));
}
return;
}
}
}
void
{
int fast_path;
if (len == 0) {
len = 256;
}
} else if (op == SCMD_READ_G1) {
} else if (op == SCMD_READ_G5) {
} else if (op == SCMD_READ_G4) {
} else {
return;
}
return;
}
}
fast_path = 0;
} else {
fast_path = 1;
}
if (len == 0) {
return;
}
if (initial_dbuf == NULL) {
do {
&minsize, 0);
(minsize >= 512));
if (initial_dbuf == NULL) {
return;
}
}
dbuf = initial_dbuf;
dbuf->db_relative_offset = 0;
} else {
}
return;
}
if (task->task_lu_private) {
} else {
}
scmd->current_ro = 0;
}
void
struct stmf_data_buf *dbuf)
{
int bufs_to_take;
/* Lets try not to hog all the buffers the port has. */
do {
(minsize >= 512));
return;
}
}
}
void
{
int ndx;
return;
}
goto WRITE_XFER_DONE;
}
if (iolen == 0)
break;
break;
}
}
return; /* wait for all buffers to complete */
else
return;
}
/* free current dbuf and allocate a new one */
do {
(minsize >= 512));
}
return;
}
}
}
void
{
return;
}
if (op == SCMD_WRITE) {
if (len == 0) {
len = 256;
}
} else if (op == SCMD_WRITE_G1) {
} else if (op == SCMD_WRITE_G5) {
} else if (op == SCMD_WRITE_G4) {
} else {
return;
}
return;
}
}
if (len == 0) {
return;
}
if (initial_dbuf == NULL) {
do {
&minsize, 0);
(minsize >= 512));
if (initial_dbuf == NULL) {
return;
}
if (initial_dbuf->db_data_size >
/* protocol error */
return;
}
}
do_immediate_data = 1;
}
dbuf = initial_dbuf;
if (task->task_lu_private) {
} else {
}
scmd->current_ro = 0;
if (do_immediate_data) {
} else {
}
}
/*
* Utility routine to handle small non performance data transfers to the
* initiators. dbuf is an initial data buf (if any), 'p' points to a data
* buffer which is source of data for transfer, cdb_xfer_size is the
* transfer size based on CDB, cmd_xfer_size is the actual amount of data
* which this command would transfer (the size of data pointed to by 'p').
*/
void
{
} else {
}
if (cmd_xfer_size == 0) {
return;
}
}
return;
}
uint8_t *d;
uint32_t s;
bufsize += s;
}
dbuf->db_relative_offset = 0;
}
}
void
struct stmf_data_buf *dbuf)
{
return;
}
}
void
{
} else {
}
if (cdb_xfer_size == 0) {
return;
}
KM_SLEEP);
} else {
}
return;
}
dbuf->db_relative_offset = 0;
} else {
STMF_ABORTED, NULL);
return;
}
}
}
void
{
/*
* For now lets assume we will get only one sglist element
* for short writes. If that ever changes, we should allocate
* a local buffer and copy all the sg elements to one linear space.
*/
return;
}
/* Lets find out who to call */
case SCMD_MODE_SELECT:
case SCMD_MODE_SELECT_G1:
if (st_ret != STMF_SUCCESS) {
}
} else {
}
break;
if (st_ret != STMF_SUCCESS) {
}
} else {
}
break;
default:
/* This should never happen */
STMF_ABORTED, NULL);
}
}
void
struct stmf_data_buf *initial_dbuf)
{
uint8_t p[32];
uint64_t s;
s--;
case SCMD_READ_CAPACITY:
if (s & 0xffffffff00000000ull) {
p[0] = p[1] = p[2] = p[3] = 0xFF;
} else {
p[0] = (s >> 24) & 0xff;
p[1] = (s >> 16) & 0xff;
p[2] = (s >> 8) & 0xff;
p[3] = s & 0xff;
}
p[4] = 0; p[5] = 0;
break;
case SCMD_SVC_ACTION_IN_G4:
bzero(p, 32);
p[0] = (s >> 56) & 0xff;
p[1] = (s >> 48) & 0xff;
p[2] = (s >> 40) & 0xff;
p[3] = (s >> 32) & 0xff;
p[4] = (s >> 24) & 0xff;
p[5] = (s >> 16) & 0xff;
p[6] = (s >> 8) & 0xff;
p[7] = s & 0xff;
cdb_len, 32);
break;
}
}
void
{
if (s < (4ull * 1024ull * 1024ull * 1024ull)) {
*nsectors = 32;
*nheads = 8;
} else {
*nsectors = 254;
*nheads = 254;
}
}
void
{
uint8_t *p;
p = buf; /* buf is assumed to be zeroed out and large enough */
n = 0;
if (cdb[0] == SCMD_MODE_SENSE) {
header_size = 4;
} else {
header_size = 8;
}
/* Now validate the command */
pc_valid = 1;
} else {
pc_valid = 0;
}
return;
}
/* We will update the length in the mode header at the end */
/* Block dev device specific param in mode param header has wp bit */
p[n + dev_spec_param_offset] = BIT_7;
}
n += header_size;
/* We are not going to return any block descriptor */
p[n] = 0x03;
p[n+1] = 0x16;
if (ctrl != 1) {
p[n + 11] = nsectors;
p[n + 20] = 0x80;
}
n += 24;
}
p[n] = 0x04;
p[n + 1] = 0x16;
if (ctrl != 1) {
p[n + 5] = nheads;
p[n + 20] = 0x15;
p[n + 21] = 0x18;
}
n += 24;
}
struct mode_caching *mode_caching_page;
mode_caching_page = (struct mode_caching *)&p[n];
switch (ctrl) {
case (0):
/* Current */
}
break;
case (1):
/* Changeable */
SL_WRITEBACK_CACHE_SET_UNSUPPORTED) == 0) {
}
break;
default:
SL_SAVED_WRITE_CACHE_DISABLE) == 0) {
}
break;
}
n += (sizeof (struct mode_page) +
}
struct mode_control_scsi3 *mode_control_page;
mode_control_page = (struct mode_control_scsi3 *)&p[n];
if (ctrl != 1) {
/* If not looking for changeable values, report this. */
}
n += (sizeof (struct mode_page) +
}
if (cdb[0] == SCMD_MODE_SENSE) {
if (n > 255) {
return;
}
/*
* Mode parameter header length doesn't include the number
* of bytes in the length field, so adjust the count.
* Byte count minus header length field size.
*/
} else {
/* Byte count minus header length field size. */
}
cmd_size, n);
}
void
{
} else {
}
return;
}
if (cmd_xfer_len == 0) {
/* zero byte mode selects are allowed */
return;
}
}
void
{
int i;
hdr_len = 4;
} else {
hdr_len = 8;
}
goto mode_sel_param_len_err;
goto mode_sel_param_len_err;
goto mode_sel_param_len_err;
}
goto mode_sel_param_field_err;
}
if (buf[i]) {
goto mode_sel_param_field_err;
}
}
sret = SBD_SUCCESS;
/* All good. Lets handle the write cache change, if any */
} else {
}
if (sret != SBD_SUCCESS) {
return;
}
/* set on the device passed, now set the flags */
} else {
}
continue;
}
} else {
}
} else {
}
if (sret == SBD_SUCCESS) {
} else {
}
return;
return;
}
/*
* This function parse through a string, passed to it as a pointer to a string,
* by adjusting the pointer to the first non-space character and returns
* Management URLs are stored as a space delimited string in sl_mgmt_url
* field of sbd_lu_t. This function is used to retrieve one url at a time.
*
* i/p : pointer to pointer to a url string
* o/p : Adjust the pointer to the url to the first non white character
* and returns the length of the URL
*/
sbd_parse_mgmt_url(char **url_addr) {
uint16_t url_length = 0;
char *url;
while (*url != '\0') {
(*url_addr)++;
} else {
break;
}
}
while (*url != '\0') {
break;
}
url++;
url_length++;
}
return (url_length);
}
void
{
uint8_t *p;
uint32_t mgmt_url_size = 0;
/*
* Basic protocol checks.
*/
return;
}
/*
* Zero byte allocation length is not an error. Just
* return success.
*/
if (cmd_size == 0) {
task->task_cmd_xfer_length = 0;
if (task->task_additional_flags &
}
return;
}
if (sl->sl_mgmt_url) {
}
/*
* Standard inquiry
*/
int i;
struct scsi_inquiry *inq;
inq = (struct scsi_inquiry *)p;
page_length = 69;
} else {
}
} else {
}
} else {
}
/* Adding Version Descriptors */
i = 0;
/* SAM-3 no version */
i++;
/* transport */
case PROTOCOL_FIBRE_CHANNEL:
i++;
break;
case PROTOCOL_PARALLEL_SCSI:
case PROTOCOL_SSA:
case PROTOCOL_IEEE_1394:
/* Currently no claims of conformance */
break;
case PROTOCOL_SRP:
i++;
break;
case PROTOCOL_iSCSI:
i++;
break;
case PROTOCOL_SAS:
case PROTOCOL_ADT:
case PROTOCOL_ATAPI:
default:
/* Currently no claims of conformance */
break;
}
/* SPC-3 no version */
i++;
/* SBC-2 no version */
return;
}
/*
* EVPD handling
*/
/* Default 512 bytes may not be enough, increase bsize if necessary */
}
switch (cdbp[2]) {
case 0x00:
p[0] = byte0;
p[3] = page_length;
/* Supported VPD pages in ascending order */
{
uint8_t i = 5;
p[i++] = 0x80;
p[i++] = 0x83;
if (mgmt_url_size != 0)
p[i++] = 0x85;
p[i++] = 0x86;
}
break;
case 0x80:
if (sl->sl_serial_no_size) {
} else {
/* if no serial num is specified set 4 spaces */
page_length = 4;
}
p[0] = byte0;
p[1] = 0x80;
p[3] = page_length;
break;
case 0x83:
break;
case 0x85:
if (mgmt_url_size == 0) {
return;
}
{
char *url;
p[0] = byte0;
p[1] = 0x85;
idx = 4;
/* Creating Network Service Descriptors */
while (url_size != 0) {
/* Null terminated and 4 Byte aligned */
/*
* SPC-3r23 : Table 320 (Sec 7.6.5)
* (Network service descriptor format
*
* Note: Hard coding service type as
* "Storage Configuration Service".
*/
p[idx] = 1;
}
/* skip to next mgmt url if any */
}
/* Total descriptor length */
break;
}
case 0x86:
page_length = 0x3c;
p[0] = byte0;
p[1] = 0x86; /* Page 86 response */
p[3] = page_length;
/*
* Bits 0, 1, and 2 will need to be updated
* that is implemented. For now, we're going
* to claim support only for Simple TA.
*/
p[5] = 1;
break;
default:
return;
}
}
{
if ((task->task_lu_private =
return (STMF_SUCCESS);
}
return (STMF_ALLOC_FAILURE);
}
void
{
break;
}
}
sbd_it_data_t *, it);
}
void
{
/* If we dont have any reservations, just get out. */
return;
}
/* Find the I_T nexus which is holding the reservation. */
break;
}
}
} else {
/*
* We were passed an I_T nexus. If this nexus does not hold
* the reservation, do nothing. This is why this function is
* called "check_and_clear".
*/
return;
}
}
}
void
{
if (it->sbd_it_session_id ==
return;
}
}
return;
}
!= STMF_SUCCESS) {
return;
}
}
}
if (task->task_mgmt_function) {
return;
}
/*
* if we're transitioning between access
* states, return NOT READY
*/
return;
}
/* Checking ua conditions as per SAM3R14 5.3.2 specified order */
saa = STMF_SAA_POR;
}
if (saa) {
return;
}
}
/* Reservation conflict checks */
if (sbd_pgr_reservation_conflict(task)) {
return;
}
return;
}
}
}
/* Rest of the ua conndition checks */
saa = 0;
} else {
}
} else if (it->sbd_it_ua_conditions &
} else if (it->sbd_it_ua_conditions &
} else if (it->sbd_it_ua_conditions &
} else {
it->sbd_it_ua_conditions = 0;
saa = 0;
}
if (saa) {
return;
}
}
if (cdb0 != SCMD_INQUIRY &&
cdb0 != SCMD_MODE_SENSE &&
cdb0 != SCMD_MODE_SENSE_G1 &&
cdb0 != SCMD_MODE_SELECT &&
cdb0 != SCMD_MODE_SELECT_G1 &&
cdb0 != SCMD_RESERVE &&
cdb0 != SCMD_RELEASE &&
cdb0 != SCMD_REQUEST_SENSE &&
!(cdb0 == SCMD_MAINTENANCE_IN &&
return;
}
/*
* is this a short write?
* if so, we'll need to wait until we have the buffer
* before proxying the command
*/
switch (cdb0) {
case SCMD_MODE_SELECT:
case SCMD_MODE_SELECT_G1:
break;
default:
if (st_ret != STMF_SUCCESS) {
}
return;
}
}
return;
}
return;
}
return;
}
return;
}
if (cdb0 == SCMD_PERSISTENT_RESERVE_OUT) {
return;
}
if (cdb0 == SCMD_PERSISTENT_RESERVE_IN) {
return;
}
if (cdb0 == SCMD_RELEASE) {
if (cdb1) {
return;
}
/* If not owner don't release it, just return good */
if (it->sbd_it_session_id !=
return;
}
}
return;
}
if (cdb0 == SCMD_RESERVE) {
if (cdb1) {
return;
}
/* If not owner, return conflict status */
if (it->sbd_it_session_id !=
return;
}
}
return;
}
if (cdb0 == SCMD_REQUEST_SENSE) {
/*
* LU provider needs to store unretrieved sense data
* return good status with no sense.
*/
} else {
}
return;
}
/* Report Target Port Groups */
if ((cdb0 == SCMD_MAINTENANCE_IN) &&
return;
}
task->task_cmd_xfer_length = 0;
return;
}
} else {
}
return;
}
uint8_t *p;
kmem_free(p, 512);
return;
}
return;
}
task->task_cmd_xfer_length = 0;
return;
}
return;
}
if (cdb1 == SSVC_ACTION_READ_CAPACITY_G4) {
return;
/*
* } else if (cdb1 == SSVC_ACTION_READ_LONG_G4) {
* sbd_handle_read(task, initial_dbuf);
* return;
*/
}
}
/*
* if (cdb0 == SCMD_SVC_ACTION_OUT_G4) {
* if (cdb1 == SSVC_ACTION_WRITE_LONG_G4) {
* sbd_handle_write(task, initial_dbuf);
* return;
* }
* }
*/
if (cdb0 == SCMD_VERIFY) {
/*
* Something more likely needs to be done here.
*/
task->task_cmd_xfer_length = 0;
return;
}
if (cdb0 == SCMD_SYNCHRONIZE_CACHE ||
return;
}
}
void
{
return;
case (SBD_CMD_SCSI_READ):
break;
case (SBD_CMD_SCSI_WRITE):
break;
case (SBD_CMD_SMALL_READ):
break;
case (SBD_CMD_SMALL_WRITE):
break;
default:
break;
}
}
/* ARGSUSED */
void
{
"sbd_send_status_done: this should not have been called");
}
void
{
if (task->task_lu_private) {
(void *)task);
}
}
}
/*
* Aborts are synchronus w.r.t. I/O AND
* All the I/O which SBD does is synchronous AND
* Everything within a task is single threaded.
* IT MEANS
* If this function is called, we are doing nothing with this task
* inside of sbd module.
*/
/* ARGSUSED */
{
if (abort_cmd == STMF_LU_RESET_STATE) {
return (sbd_lu_reset_state(lu));
}
if (abort_cmd == STMF_LU_ITL_HANDLE_REMOVED) {
return (STMF_SUCCESS);
}
if (task->task_lu_private) {
return (STMF_ABORT_SUCCESS);
}
}
return (STMF_NOT_FOUND);
}
/* ARGSUSED */
void
{
(cmd == STMF_CMD_LU_OFFLINE) ||
(cmd == STMF_ACK_LU_ONLINE_COMPLETE) ||
(cmd == STMF_ACK_LU_OFFLINE_COMPLETE));
switch (cmd) {
case STMF_CMD_LU_ONLINE:
}
break;
case STMF_CMD_LU_OFFLINE:
}
break;
/* Fallthrough */
sl->sl_state_not_acked = 0;
break;
}
}
/* ARGSUSED */
{
return (STMF_NOT_SUPPORTED);
}
{
}
} else {
(void) sbd_wcd_set(0, sl);
}
}
return (STMF_FAILURE);
}
return (STMF_SUCCESS);
}
{
int r = 0;
int ret;
if (fsync_done)
goto over_fsync;
return (SBD_FAILURE);
}
} else if (ret != 0) {
return (SBD_FAILURE);
}
}
return (SBD_SUCCESS);
}
/* ARGSUSED */
static void
struct stmf_data_buf *initial_dbuf)
{
int is_g4 = 0;
int immed;
task->task_cmd_xfer_length = 0;
/*
* Determine if this is a 10 or 16 byte CDB
*/
is_g4 = 1;
/*
* Determine other requested parameters
*
* We don't have a non-volatile cache, so don't care about SYNC_NV.
* Do not support the IMMED bit.
*/
if (immed) {
return;
}
/*
* Check to be sure we're not being asked to sync an LBA
* that is out of range. While checking, verify reserved fields.
*/
if (is_g4) {
return;
}
} else {
return;
}
}
return;
}
if (sret != SBD_SUCCESS) {
return;
}
}