t10_raw_if.c revision 36c5fee33fa8b822175d410202aebcf592c8d342
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* []------------------------------------------------------------------[]
* | Implementation of SBC-2 emulation |
* []------------------------------------------------------------------[]
*/
#include <stddef.h>
#include <strings.h>
#include <errno.h>
#include <unistd.h>
#include <sys/sysmacros.h>
#include "t10.h"
#include "t10_spc.h"
#include "utility.h"
#include "target.h"
typedef struct raw_io {
char *r_data;
} raw_io_t;
typedef struct raw_params {
int r_dtype;
} raw_params_t;
/*
* Forward declarations
*/
static scsi_cmd_table_t raw_table[];
static void raw_write_cmplt(emul_handle_t e);
/*
* []----
* | raw_init_common -- Initialize LU data which is common to all I_T_Ls
* []----
*/
{
char *str;
raw_params_t *r;
return (False);
}
lu->l_dtype_params = (void *)r;
return (True);
}
void
{
}
/*
* []----
* | raw_init_per -- Initialize per I_T_L information
* []----
*/
void
{
/*
* The first time an I_T nexus connects to a LU it is supposed
* to receive an unit attention upon the first command sent.
*/
}
/*ARGSUSED*/
void
{
}
/*
* []----
* | raw_cmd -- start a SCSI command
* |
* | This routine is called from within the SAM-3 Task router.
* []----
*/
static void
{
scsi_cmd_table_t *e;
#ifdef FULL_DEBUG
char debug[80];
#endif
#ifdef FULL_DEBUG
#endif
}
/*
* []----
* | raw_data -- Data phase for command.
* |
* | Normally this is only called for the WRITE command. Other commands
* | that have a data in phase will probably be short circuited when
* | we call trans_rqst_dataout() and the data is already available.
* | At least this is true for iSCSI. FC however will need a DataIn phase
* | for commands like MODE SELECT and PGROUT.
* []----
*/
static void
{
scsi_cmd_table_t *e;
#ifdef FULL_DEBUG
char debug[80];
#endif
#ifdef FULL_DEBUG
#endif
}
/*
* []------------------------------------------------------------------[]
* | The following methods handle special case requirements for the |
* | raw devices. |
* []------------------------------------------------------------------[]
*/
/*
* []----
* | raw_read_tape -- handle SCSI reads from raw tape
* |
* | Need to handle reads from SCSI tape differently than LBA devices
* | for two reasons.
* | (1) The command block for tape reads is different than for
* | LBA devices. There's only a count field.
* | (2) Since tapes have records it's not possible to break up
* | the read operations in the same manner as LBA devices.
* | All of the data must first be read in from the device
* | and then broken up to fit the transport. This is a slower
* | approach, but nobody expects tapes to be quick. If speed
* | is needed a better approach would be to create a virtual
* | tape device and then stage out the data to the device later.
* []----
*/
/*ARGSUSED*/
static void
{
xfer;
req_len *= 512;
return;
}
return;
}
}
}
/*
* []----
* | raw_read -- emulation of SCSI READ command
* []----
*/
/*ARGSUSED*/
static void
{
/*LINTED*/
min;
int sense_len;
char debug[80];
raw_params_t *r;
return;
if (r->r_dtype == DTYPE_SEQUENTIAL) {
return;
}
switch (u->scc_cmd) {
case SCMD_READ:
/*
* SBC-2 Revision 16, section 5.5
* Reserve bit checks
*/
return;
}
cnt = GETG0COUNT(u);
/*
* SBC-2 Revision 16
* Section: 5.5 READ(6) command
* A TRANSFER LENGTH field set to zero specifies
* that 256 logical blocks shall be read.
*/
if (cnt == 0)
cnt = 256;
break;
case SCMD_READ_G1:
/*
* SBC-2 Revision 16, section 5.6
* Reserve bit checks.
*/
return;
}
cnt = GETG1COUNT(u);
break;
case SCMD_READ_G4:
/*
* SBC-2 Revision 16, section 5.8
* Reserve bit checks
*/
return;
}
addr = GETG4LONGADDR(u);
cnt = GETG4COUNT(u);
break;
default:
return;
}
/*
* request exceed the capacity of disk
* set error block number to capacity + 1
*/
/*
* doesn't care about these values when key is set
* to KEY_ILLEGAL_REQUEST.
*/
else
addl_sense_len = 0;
"RAW%d READ Illegal sector (0x%llx + 0x%x) > 0x%llx",
return;
}
if (cnt == 0) {
return;
}
do {
/*
* We're pretty much dead in the water. If we can't
* allocate memory. It's unlikey we'll be able to
* allocate a sense buffer or queue the command
* up to be sent back to the transport for delivery.
*/
return;
}
#ifdef FULL_DEBUG
"RAW%d blk 0x%llx, cnt %d, offset 0x%llx, size %d",
#endif
else
sense_len = 0;
return;
}
}
/*
* []----
* | raw_read_cmplt -- Once we have the data, need to send it along.
* []----
*/
static void
{
int sense_len;
else
sense_len = 0;
return;
}
}
}
/*ARGSUSED*/
static void
{
xfer;
return;
}
}
}
/*ARGSUSED*/
void
{
return;
} else {
}
}
/*
* []----
* | raw_write -- implement a SCSI write command.
* []----
*/
/*ARGSUSED*/
static void
{
/*LINTED*/
raw_params_t *r;
return;
if (r->r_dtype == DTYPE_SEQUENTIAL) {
return;
}
switch (cdb[0]) {
case SCMD_WRITE:
/*
* SBC-2 revision 16, section 5.24
* Reserve bit checks.
*/
return;
}
/*
* SBC-2 Revision 16/Section 5.24 WRITE(6)
* A TRANSFER LENGHT of 0 indicates that 256 logical blocks
* shall be written.
*/
if (cnt == 0)
cnt = 256;
break;
case SCMD_WRITE_G1:
/*
* SBC-2 revision 16, section 5.25
* Reserve bit checks.
*/
return;
}
break;
case SCMD_WRITE_G4:
/*
* SBC-2 revision 16, section 5.27
* Reserve bit checks.
*/
return;
}
break;
default:
"Unprocessed WRITE type");
return;
}
/*
* request exceed the capacity of disk
* set error block number to capacity + 1
*/
/*
* doesn't care about these values when key is set
* to KEY_ILLEGAL_REQUEST.
*/
else
addl_sense_len = 0;
"RAW%d WRITE Illegal sector (0x%llx + 0x%x) > 0x%llx",
return;
}
if (cnt == 0) {
return;
}
return;
}
/*
* Only update the statistics the first time through
* for this particular command. If the requested transfer
* is larger than the transport can handle this routine
* will be called many times.
*/
}
/*
* If a transport sets the maximum output value to zero we'll
* just request the entire amount. Otherwise, transfer no more
* than the maximum output or the reminder, whichever is less.
*/
#ifdef FULL_DEBUG
"RAW%d blk 0x%llx, cnt %d, offset 0x%llx, size %d",
io->r_data_len);
#endif
/*
* NOTE: May need a different ASC code
*/
else
addl_sense_len = 0;
return;
}
}
}
/*
* []----
* | raw_write_data -- store a chunk of data from the transport
* []----
*/
/*ARGSUSED*/
void
{
if (r == NULL)
return;
if (r->r_dtype == DTYPE_SEQUENTIAL) {
return;
}
}
/*
* []----
* | raw_write_cmplt -- deal with end game of write
* |
* | See if all of the data for this write operation has been dealt
* | with. If so, send a final acknowledgement back to the transport.
* | If not, update the offset, calculate the next transfer size, and
* | start the process again.
* []---
*/
static void
{
return;
}
}
static void
{
} else {
}
}
static void
{
} else {
}
}
static void
{
int len;
switch (cdb[0]) {
case SCMD_MODE_SENSE:
break;
case SCMD_MODE_SENSE_G1:
break;
}
return;
}
}
}
static void
{
} else {
}
}
static void
{
} else {
}
}
}
static void
{
struct scsi_inquiry inq;
raw_params_t *r;
return;
return;
}
}
}
}
static void
{
int len;
switch (cdb[0]) {
case SCMD_MODE_SELECT:
break;
case SCMD_MODE_SELECT_G1:
break;
}
}
/*ARGSUSED*/
static void
{
}
static void
{
} else {
}
}
static void
{
} else {
}
}
static void
{
int len;
}
/*ARGSUSED*/
static void
{
}
static void
{
struct scsi_capacity cap;
raw_params_t *r;
return;
return;
}
/*
* Currently there's a bug in ZFS which doesn't report a capacity
* for any of the volumes. This means that when using ZFS the
* administrator must supply the device size.
*/
}
}
static void
{
struct scsi_capacity_16 cap16;
raw_params_t *r;
return;
return;
}
/*
* Currently there's a bug in ZFS which doesn't report a capacity
* for any of the volumes. This means that when using ZFS the
* administrator must supply the device size.
*/
if (cap16.sc_capacity != 0)
}
}
static void
{
} else {
}
}
static void
{
} else {
}
}
static void
{
return;
}
}
}
static void
{
/*
* spec defines this command to return 6 bytes of data
*/
return;
}
}
}
/*
* []------------------------------------------------------------------[]
* | Support related functions for raw devices |
* []------------------------------------------------------------------[]
*/
static void
{
return;
}
if ((opt_data_len != 0) &&
return;
}
}
}
static raw_io_t *
{
return (NULL);
}
return (NULL);
}
return (io);
}
static int
{
struct uscsi_cmd u;
bzero(&u, sizeof (u));
u.uscsi_rqbuf = (char *)sense_buf;
u.uscsi_rqlen = sizeof (sense_buf);
(u.uscsi_status == 0)) {
return (0);
}
"RAW%d LUN%d USCSICMD errno %d, cmd_status %d, rqstatus %d, "
"rqresid %d",
if ((u.uscsi_rqlen - u.uscsi_rqresid) <
sizeof (struct scsi_extended_sense)) {
"RAW%x LUN%d -- No sense data, got=%d, needed=%d",
u.uscsi_rqlen - u.uscsi_rqresid,
sizeof (struct scsi_extended_sense));
return (STATUS_CHECK);
} else {
return (u.uscsi_status);
}
}
static void
{
if (io->r_data_len)
}
/*
* []----
* | Command table for LBA emulation. This is at the end of the file because
* | it's big and ugly. ;-) To make for fast translation to the appropriate
* | emulation routine we just have a big command table with all 256 possible
* | entries. Most will report STATUS_CHECK, unsupport operation. By doing
* | this we can avoid error checking for command range.
* []----
*/
static scsi_cmd_table_t raw_table[] = {
/* 0x00 -- 0x0f */
/* 0x10 -- 0x1f */
/* 0x20 -- 0x2f */
/* 0x30 -- 0x3f */
/* 0x40 -- 0x4f */
/* 0x50 -- 0x5f */
/* 0x60 -- 0x6f */
/* 0x70 -- 0x7f */
/* 0x80 -- 0x8f */
/* 0x90 -- 0x9f */
/* 0xa0 - 0xaf */
/* 0xb0 -- 0xbf */
/* 0xc0 -- 0xcf */
/* 0xd0 -- 0xdf */
/* 0xe0 -- 0xef */
/* 0xf0 -- 0xff */
};