t10_sbc.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 SBC-2 emulation |
* []------------------------------------------------------------------[]
*/
#include <aio.h>
#include <stddef.h>
#include <strings.h>
#include <unistd.h>
#include <assert.h>
#include "t10.h"
#include "t10_spc.h"
#include "t10_spc_pr.h"
#include "t10_sbc.h"
#include "utility.h"
/*
* External declarations
*/
void spc_pr_read(t10_cmd_t *);
/*
* Forward declarations
*/
static void sbc_overlap_flush(disk_params_t *d);
static void sbc_io_free(emul_handle_t e);
static void sbc_read_cmplt(emul_handle_t e);
static void sbc_write_cmplt(emul_handle_t e);
static char *sense_info_ctrl(char *buf);
static scsi_cmd_table_t lba_table[];
static long sbc_page_size;
/*
* []----
* | sbc_init_common -- Initialize LU data which is common to all I_T_Ls
* []----
*/
{
disk_params_t *d;
return (False);
(int *)&d->d_bytes_sect);
(int *)&d->d_heads);
(int *)&d->d_spt);
(int *)&d->d_cyl);
(int *)&d->d_rpm);
(int *)&d->d_interleave);
NULL) {
free(d);
return (False);
}
lu->l_dtype_params = (void *)d;
return (True);
}
void
{
avl_destroy(&d->d_mmap_overlaps);
free(d->d_io_reserved);
}
void
{
switch (op) {
case CapacityChange:
break;
case DeviceOnline:
break;
}
}
/*
* []----
* | sbc_init_per -- Initialize per I_T_L information
* []----
*/
void
{
}
else
}
void
{
}
/*
* []----
* | sbc_cmd -- start a SCSI command
* |
* | This routine is called from within the SAM-3 Task router.
* []----
*/
void
{
scsi_cmd_table_t *e;
/*
* Determine if there is persistent data for this I_T_L Nexus
*/
}
#ifdef FULL_DEBUG
#endif
}
/*
* []----
* | sbc_cmd_reserve -- Run commands when another I_T_L has a reservation
* []----
*/
void
{
/*
* SPC-3, revision 23, Table 31
* SPC commands that are allowed in the presence of various reservations
*/
switch (cdb[0]) {
case SCMD_INQUIRY:
case SCMD_LOG_SENSE_G1:
case SCMD_SVC_ACTION_IN_G5:
case SCMD_REPORT_LUNS:
case SCMD_MAINTENANCE_OUT:
case SCMD_REQUEST_SENSE:
case SCMD_TEST_UNIT_READY:
break;
default:
case RT_NONE:
/* conflict = False; */
break;
case RT_PGR:
break;
default:
break;
}
}
"PGR%x LUN%d CDB:%s - sbc_cmd_reserved(%s:%s)\n",
? "(no name)"
/*
* If no conflict at this point, allow command
*/
} else {
}
}
/*
* []----
* | sbc_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
#endif
}
/*
* []------------------------------------------------------------------[]
* | SCSI Block Commands - 2 |
* | T10/1417-D |
* | The following functions implement the emulation of SBC-2 type |
* | commands. |
* []------------------------------------------------------------------[]
*/
/*
* []----
* | sbc_read -- emulation of SCSI READ command
* []----
*/
/*ARGSUSED*/
static void
{
/*LINTED*/
disk_params_t *d;
t10_cmd_t *c;
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;
}
else
else
addl_sense_len = 0;
"SBC%x LUN%d READ Illegal sector "
"(0x%llx + 0x%x) > 0x%ullx\n",
return;
}
if (cnt == 0) {
return;
}
do {
c = trans_cmd_dup(cmd);
else
c = cmd;
io = sbc_io_alloc(c);
#ifdef FULL_DEBUG
"SBC%x LUN%d blk 0x%llx, cnt %d, offset 0x%llx, size %d\n",
#endif
if (mmap_data != MAP_FAILED) {
} else {
return;
}
}
}
/*
* []----
* | sbc_read_cmplt -- Once we have the data, need to send it along.
* []----
*/
static void
{
int sense_len;
else
sense_len = 0;
return;
}
}
}
/*
* []----
* | sbc_write -- implement a SCSI write command.
* []----
*/
/*ARGSUSED*/
static void
{
union scsi_cdb *u;
disk_params_t *d;
void *mmap_area;
return;
}
/*LINTED*/
switch (u->scc_cmd) {
case SCMD_WRITE:
/*
* SBC-2 revision 16, section 5.24
* Reserve bit checks.
*/
return;
}
cnt = GETG0COUNT(u);
/*
* 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;
}
cnt = GETG1COUNT(u);
break;
case SCMD_WRITE_G4:
/*
* SBC-2 revision 16, section 5.27
* Reserve bit checks.
*/
return;
}
cnt = GETG4COUNT(u);
break;
default:
return;
}
else
else
addl_sense_len = 0;
"SBC%x LUN%d WRITE Illegal sector "
"(0x%llx + 0x%x) > 0x%ullx\n",
return;
}
if (cnt == 0) {
"SBC%x LUN%d WRITE zero block count for addr 0x%x\n",
addr);
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.
*/
#ifdef FULL_DEBUG
"SBC%x LUN%d blk 0x%llx, cnt 0x%x\n",
#endif
}
/*
* 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.
*/
if (mmap_area != MAP_FAILED) {
return;
} else {
}
}
}
/*
* []----
* | sbc_write_data -- store a chunk of data from the transport
* []----
*/
/*ARGSUSED*/
void
{
disk_params_t *d;
} else {
return;
if (d->d_fast_write == False) {
/*
* msync requires the address to be page aligned.
* That means we need to account for any alignment
* loss in the len field and access the full page.
*/
~(sbc_page_size -1);
/*
* We only need to worry about sync'ing the blocks
* in the mmap case because if the fast cache isn't
* enabled for AIO the file will be opened with F_SYNC
* which performs the correct action.
*/
perror("msync");
return;
}
}
/*
* Since the data has already been transfered from the
* transport to the mmap area we just need to call
* the complete routine.
*/
}
}
/*
* []----
* | sbc_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;
}
}
/*ARGSUSED*/
void
{
/*
* SBC-2 revision 16, section 5.17
* Reserve bit checks
*/
return;
}
/*
* More reserve bit checks
*/
case SBC_PWR_START_VALID:
/*
* It's an error to ask that the media be ejected.
*
* NOTE: Look for method to pass the START bit
* along to underlying storage. If we're asked to
* stop the drive there's not much that we can do
* for the virtual storage, but maybe everything else
* has been requested to stop as well.
*/
goto send_error;
}
break;
case SBC_PWR_ACTIVE:
case SBC_PWR_IDLE:
case SBC_PWR_STANDBY:
case SBC_PWR_OBSOLETE:
break;
case SBC_PWR_LU_CONTROL:
case SBC_PWR_FORCE_IDLE_0:
case SBC_PWR_FORCE_STANDBY_0:
break;
default:
return;
}
/*
* Immediate bit is not set, so go ahead a flush things.
*/
return;
}
} else {
return;
}
}
}
}
/*
* []----
* | sbc_recap -- read capacity of device being emulated.
* []----
*/
/*ARGSUSED*/
void
{
int len;
struct scsi_capacity *cap;
disk_params_t *d;
return;
len = sizeof (struct scsi_capacity);
/*
* SBC-2 Revision 16, section 5.10.1
* Any of the following conditions will generate an error.
* (1) PMI bit is zero and LOGICAL block address is non-zero
* (2) Rserved bytes are not zero
* (3) Reseved bits are not zero
* (4) Reserved CONTROL bits are not zero
*/
return;
}
/*
* if the device capacity larger than 32 bits then set
* the capacity of the device to all 0xf's.
* a device that supports LBAs larger than 32 bits which
* should be used read_capacity(16) comand to get the capacity.
* NOTE: the adjustment to subject one from the capacity is
* done below.
*/
if (capacity & 0xFFFFFFFF00000000ULL)
capacity = 0xFFFFFFFF;
return;
}
if (capacity != 0xFFFFFFFF) {
/*
* Look at the PMI information
*/
else
} else {
}
} else {
}
}
}
/*ARGSUSED*/
void
{
struct mode_header *mode_hdr;
char *np;
disk_params_t *d;
int rtn_len;
struct block_descriptor bd;
return;
/*
* SPC-3 Revision 21c section 6.8
* Reserve bit checks
*/
return;
}
/*
* Zero length causes a simple ack to occur.
*/
if (cdb[4] == 0) {
return;
}
/*
* Make sure that we have enough room in the data buffer. We'll
* only send back the amount requested though
*/
sizeof (struct mode_geometry) +
sizeof (struct mode_control_scsi3) +
sizeof (struct mode_cache_scsi3) +
return;
}
/*
* If DBD flag is set, then we should not send back the block
* descriptor details
*/
mode_hdr->bdesc_length = 0;
}
/*
* If DBD flag is zero, then we should add block descriptor details
*/
else {
/*
* We subtract one from the length because this value is not
* supposed to contain it's size.
*/
/*
* Need to fill in the block size. Some initiators are starting
* to use this value, which is correct, instead of looking at
* the page3 data which is starting to become obsolete.
*
* We define the space for the structure on the stack and then
* copy it into the return area to avoid structure alignment
* issues.
*/
}
/*
* cdb[2] contains page code, and page control field. So, we need
* to mask page control field, while checking for the page code.
*/
case MODE_SENSE_PAGE3_CODE:
return;
}
(void) sense_page3(d,
break;
case MODE_SENSE_PAGE4_CODE:
return;
}
(void) sense_page4(d,
break;
case MODE_SENSE_CACHE:
(void) sense_cache(d,
break;
case MODE_SENSE_CONTROL:
break;
case MODE_SENSE_INFO_CTRL:
break;
case MODE_SENSE_SEND_ALL:
/*
* SPC-3 revision 21c
* Section 6.9.1 Table 97
* "Return all subpage 00h mode pages in page_0 format"
*/
sizeof (struct mode_control_scsi3) +
sizeof (struct mode_info_ctrl);
sizeof (struct mode_geometry);
sizeof (struct mode_geometry) +
sizeof (struct mode_cache_scsi3) +
sizeof (struct mode_control_scsi3) +
sizeof (struct mode_info_ctrl))) {
/*
* Believe it or not, there's an initiator out
* there which sends a mode sense request for all
* of the pages, without always sending a data-in
* size which is large enough.
* NOTE: Need to check the error key returned
* here and see if something else should be used.
*/
} else {
/*
* If we don't have geometry then don't attempt
* report that information.
*/
}
(void) sense_info_ctrl(np);
}
break;
case 0x00:
/*
* SPC-3 Revision 21c, section 6.9.1
* Table 97 -- Mode page code usage for all devices
* Page Code 00 == Vendor specific. We are going to return
* zeros.
*/
break;
default:
"SBC%x LUN%d Unsupported mode_sense request 0x%x\n",
cdb[2]);
return;
break;
}
}
}
/*ARGSUSED*/
void
{
/*
* SBC-2 revision 16, section 5.18
* Reserve bit checks
*/
} else {
/*
* SBC-3, revision 16, section 5.18
* An IMMED bit set to one specifies that the device server
* shall return status as soon as the CDB has been validated.
*/
/*
* Immediately return a status of GOOD. If an error
* pick up an error.
*/
}
} else {
-1) {
}
}
} else {
KEY_HARDWARE_ERROR, 0);
} else
} else {
-1) {
KEY_HARDWARE_ERROR, 0);
} else
}
}
}
}
/*ARGSUSED*/
void
{
break;
default:
break;
}
}
/*ARGSUSED*/
static void
{
int rep_size; /* response data size */
struct scsi_capacity_16 *cap16;
disk_params_t *d;
return;
/*
* READ_CAPACITY(16) command
*/
if (rep_size == 0) {
/*
* A zero length field means we're done.
*/
return;
}
/*
* Reserve bit checks.
*/
return;
}
/*
* We'll malloc enough space for the structure so that we can
* set the values as we place. However, we'll set the transfer
* length to the minimum of the requested size and our structure.
* This is per SBC-2 revision 16, section 5.11.1 regarding
* ALLOCATION LENGTH.
*/
NULL) {
return;
}
else
} else {
}
}
}
/*ARGSUSED*/
static void
{
/*LINTED*/
char *chk_block;
disk_params_t *d;
return;
}
/*
* Check the common reserved bits here and check the CONTROL byte
* in each specific section for the different CDB sizes.
* NOTE: If the VRPROTECT is non-zero we're required by SBC-3
* to return an error since our emulation code doesn't have
* any protection information stored on the media that we can
* access.
*/
return;
}
switch (u->scc_cmd) {
case SCMD_VERIFY:
/*
* BYTE 6 of the VERIFY(10) contains bits:
* 0-4: Group number -- not supported must be zero
* 5-6: Reserved
* 7 : Restricted for MMC-4
*/
return;
}
cnt = GETG1COUNT(u);
break;
case SCMD_VERIFY_G4:
/*
* See VERIFY(10) above for definitions of what byte 14
* contains.
*/
return;
}
addr = GETG4LONGADDR(u);
cnt = GETG4COUNT(u);
break;
case SCMD_VERIFY_G5:
/*
* See VERIFY(10) above for definitions of what byte 10
* contains.
*/
return;
}
cnt = GETG5COUNT(u);
break;
default:
return;
}
else
else
addl_sense_len = 0;
"SBC%x LUN%d WRITE Illegal sector "
"(0x%llx + 0x%x) > 0x%ullx\n",
return;
}
/*
* With Byte Check being false all we need to do
* is make sure that we can read the data off of the
* media.
*/
return;
}
while (cnt) {
/*
* Even if the device is mmap'd in use pread. This
* way we know directly if a read of the data has
* failed.
*/
return;
}
}
} else {
}
(cnt * 512);
/*
* Since we're going to just check the data we don't wish
* to possibly change the on disk data. Therefore, even if
* the backing store is mmap'd in we allocate space for the
* data out buffer.
*/
return;
}
}
}
/*ARGSUSED*/
static void
{
char *on_disk_buf;
}
return;
}
return;
}
}
return;
}
}
/*
* []------------------------------------------------------------------[]
* | Support related functions for SBC-2 |
* []------------------------------------------------------------------[]
*/
/*
* []----
* | sense_page3 -- Create page3 sense code for Disk.
* |
* | This is a separate routine because this is called in two different
* | locations.
* []----
*/
static char *
{
struct mode_format mode_fmt;
sizeof (struct mode_page);
}
/*
* []----
* | sense_page4 -- Create page4 sense code for Disk.
* |
* | This is a separate routine because this is called in two different
* | locations.
* []----
*/
static char *
{
struct mode_geometry mode_geom;
sizeof (struct mode_geometry) - sizeof (struct mode_page);
}
static char *
{
struct mode_cache_scsi3 mode_cache;
sizeof (struct mode_page);
return (buf + sizeof (mode_cache));
}
/*
* []----
* | sense_mode_control -- Create mode control page for disk
* []----
*/
static char *
{
struct mode_control_scsi3 m;
bzero(&m, sizeof (m));
sizeof (struct mode_page);
return (buf + sizeof (m));
}
/*
* []----
* | sense_info_ctrl -- Create mode information control page
* []----
*/
static char *
sense_info_ctrl(char *buf)
{
struct mode_info_ctrl info;
sizeof (struct mode_page);
}
/*
* []----
* | sbc_io_alloc -- return a disk_io_t structure
* |
* | If the call to calloc fails we use the structure that was allocate
* | during the initial common initialization call. This will allow the
* | daemon to at least make progress.
* []----
*/
static disk_io_t *
{
disk_params_t *d = T10_PARAMS_AREA(c);
(void) pthread_mutex_lock(&d->d_mutex);
}
io = d->d_io_reserved;
(void) pthread_mutex_unlock(&d->d_mutex);
}
return (io);
}
/*
* []----
* | sbc_io_free -- free local i/o buffers when transport is finished
* |
* | If the io structure being free is the preallocated buffer see if
* | anyone is waiting for the buffer. If so, wake them up.
* []----
*/
static void
{
} else {
}
}
static int
{
return (-1);
return (1);
return (0);
}
static void
{
avl_index_t where = 0;
(void) pthread_mutex_lock(&d->d_mutex);
(void) pthread_mutex_unlock(&d->d_mutex);
}
static void
{
(void) pthread_mutex_lock(&d->d_mutex);
if (d->d_mmap_paused == True) {
d->d_mmap_paused = False;
(void) pthread_cond_signal(&d->d_mmap_cond);
}
(void) pthread_mutex_unlock(&d->d_mutex);
}
static void
{
(void) pthread_mutex_lock(&d->d_mutex);
d->d_mmap_paused = True;
while (d->d_mmap_paused == True)
(void) pthread_cond_wait(&d->d_mmap_cond,
&d->d_mutex);
/*
* After waiting on the condition variable the link
* list has changed because someone removed a command.
* So, drop the lock and reexamine the list.
*/
(void) pthread_mutex_unlock(&d->d_mutex);
goto recheck;
}
(void) pthread_mutex_unlock(&d->d_mutex);
}
/*
* []----
* | sbc_overlap_flush -- wait until everyone has reported in
* []----
*/
static void
{
(void) pthread_mutex_lock(&d->d_mutex);
if (avl_numnodes(&d->d_mmap_overlaps) != 0) {
d->d_mmap_paused = True;
while (d->d_mmap_paused == True)
(void) pthread_cond_wait(&d->d_mmap_cond,
&d->d_mutex);
/*
* After waiting on the condition variable the link
* list has changed because someone removed a command.
* So, drop the lock and reexamine the list.
*/
(void) pthread_mutex_unlock(&d->d_mutex);
goto recheck;
}
(void) pthread_mutex_unlock(&d->d_mutex);
}
/*
* []----
* | 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 lba_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 */
};