Commit 130fbeeb authored by James Bottomley's avatar James Bottomley

Merge ssh://linux-scsi@linux-scsi.bkbits.net/scsi-for-linus-2.5

into mulgrave.(none):/home/jejb/BK/scsi-for-linus-2.5
parents d572f1a5 8403fb48
......@@ -7,11 +7,18 @@ Tested in single and dual HBA configuration, 32 and 64bit busses,
SEST size 512 Exchanges (simultaneous I/Os) limited by module kmalloc()
max of 128k bytes contiguous.
Ver 2.5.3 Aug 01, 2002
* fix the passthru ioctl to handle the Scsi_Cmnd->request being a pointer
Ver 2.5.1 Jul 30, 2002
* fix ioctl to pay attention to the specified LUN.
Ver 2.5.0 Nov 29, 2001
* eliminated io_request_lock. This change makes the driver specific
to the 2.5.x kernels.
* silenced excessively noisy printks.
Ver 2.1.2 July 23, 2002
* initialize DumCmnd->lun in cpqfcTS_ioctl (used in fcFindLoggedInPorts as LUN index)
Ver 2.1.1 Oct 18, 2001
* reinitialize Cmnd->SCp.sent_command (used to identify commands as
passthrus) on calling scsi_done, since the scsi mid layer does not
......
......@@ -68,7 +68,7 @@
/* Embedded module documentation macros - see module.h */
MODULE_AUTHOR("Compaq Computer Corporation");
MODULE_DESCRIPTION("Driver for Compaq 64-bit/66Mhz PCI Fibre Channel HBA v. 2.1.1");
MODULE_DESCRIPTION("Driver for Compaq 64-bit/66Mhz PCI Fibre Channel HBA v. 2.5.3");
MODULE_LICENSE("GPL");
int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev, unsigned int reset_flags);
......@@ -105,15 +105,16 @@ static struct proc_dir_entry proc_scsi_cpqfcTS =
# define CPQFC_WAIT_FOR_COMPLETION(x) down(x)
#endif
static int cpqfc_alloc_private_data_pool(CPQFCHBA *hba);
/* local function to load our per-HBA (local) data for chip
registers, FC link state, all FC exchanges, etc.
We allocate space and compute address offsets for the
most frequently accessed addresses; others (like World Wide
Name) are not necessary.
*/
static void Cpqfc_initHBAdata( CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev )
static void Cpqfc_initHBAdata(CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev )
{
cpqfcHBAdata->PciDev = PciDev; // copy PCI info ptr
......@@ -226,8 +227,11 @@ static void Cpqfc_initHBAdata( CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev )
cpqfcHBAdata->fcChip.ReadWriteWWN = CpqTsReadWriteWWN;
cpqfcHBAdata->fcChip.ReadWriteNVRAM = CpqTsReadWriteNVRAM;
if (cpqfc_alloc_private_data_pool(cpqfcHBAdata) != 0) {
printk(KERN_WARNING
"cpqfc: unable to allocate pool for passthru ioctls. "
"Passthru ioctls disabled.\n");
}
}
......@@ -483,6 +487,75 @@ static void my_ioctl_done (Scsi_Cmnd * SCpnt)
}
static int cpqfc_alloc_private_data_pool(CPQFCHBA *hba)
{
hba->private_data_bits = NULL;
hba->private_data_pool = NULL;
hba->private_data_bits =
kmalloc(((CPQFC_MAX_PASSTHRU_CMDS+BITS_PER_LONG-1) /
BITS_PER_LONG)*sizeof(unsigned long),
GFP_KERNEL);
if (hba->private_data_bits == NULL)
return -1;
memset(hba->private_data_bits, 0,
((CPQFC_MAX_PASSTHRU_CMDS+BITS_PER_LONG-1) /
BITS_PER_LONG)*sizeof(unsigned long));
hba->private_data_pool = kmalloc(sizeof(cpqfc_passthru_private_t) *
CPQFC_MAX_PASSTHRU_CMDS, GFP_KERNEL);
if (hba->private_data_pool == NULL) {
kfree(hba->private_data_bits);
hba->private_data_bits = NULL;
return -1;
}
return 0;
}
static void cpqfc_free_private_data_pool(CPQFCHBA *hba)
{
kfree(hba->private_data_bits);
kfree(hba->private_data_pool);
}
int is_private_data_of_cpqfc(CPQFCHBA *hba, void *pointer)
{
/* Is pointer within our private data pool?
We use Scsi_Request->upper_private_data (normally
reserved for upper layer drivers, e.g. the sg driver)
We check to see if the pointer is ours by looking at
its address. Is this ok? Hmm, it occurs to me that
a user app might do something bad by using sg to send
a cpqfc passthrough ioctl with upper_data_private
forged to be somewhere in our pool..., though they'd
normally have to be root already to do this. */
return (pointer != NULL &&
pointer >= (void *) hba->private_data_pool &&
pointer < (void *) hba->private_data_pool +
sizeof(*hba->private_data_pool) *
CPQFC_MAX_PASSTHRU_CMDS);
}
cpqfc_passthru_private_t *cpqfc_alloc_private_data(CPQFCHBA *hba)
{
int i;
do {
i = find_first_zero_bit(hba->private_data_bits,
CPQFC_MAX_PASSTHRU_CMDS);
if (i == CPQFC_MAX_PASSTHRU_CMDS)
return NULL;
} while ( test_and_set_bit(i & (BITS_PER_LONG - 1),
hba->private_data_bits+(i/BITS_PER_LONG)) != 0);
return &hba->private_data_pool[i];
}
void cpqfc_free_private_data(CPQFCHBA *hba, cpqfc_passthru_private_t *data)
{
int i;
i = data - hba->private_data_pool;
clear_bit(i&(BITS_PER_LONG-1),
hba->private_data_bits+(i/BITS_PER_LONG));
}
int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
{
......@@ -490,35 +563,19 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
struct Scsi_Host *HostAdapter = ScsiDev->host;
CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
PTACHYON fcChip = &cpqfcHBAdata->fcChip;
PFC_LOGGEDIN_PORT pLoggedInPort;
PFC_LOGGEDIN_PORT pLoggedInPort = NULL;
Scsi_Cmnd DumCmnd;
int i, j;
VENDOR_IOCTL_REQ ioc;
cpqfc_passthru_t *vendor_cmd;
Scsi_Device *SDpnt;
Scsi_Cmnd *ScsiPassThruCmnd;
Scsi_Request *ScsiPassThruReq;
cpqfc_passthru_private_t *privatedata;
ENTER("cpqfcTS_ioctl ");
// can we find an FC device mapping to this SCSI target?
DumCmnd.channel = ScsiDev->channel; // For searching
DumCmnd.target = ScsiDev->id;
pLoggedInPort = fcFindLoggedInPort( fcChip,
&DumCmnd, // search Scsi Nexus
0, // DON'T search linked list for FC port id
NULL, // DON'T search linked list for FC WWN
NULL); // DON'T care about end of list
if( pLoggedInPort == NULL ) // not found!
{
result = -ENXIO;
}
else // we know what FC device to operate on...
{
// printk("ioctl CMND %d", Cmnd);
switch (Cmnd)
{
switch (Cmnd) {
// Passthrough provides a mechanism to bypass the RAID
// or other controller and talk directly to the devices
// (e.g. physical disk drive)
......@@ -528,6 +585,10 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
{
void *buf = NULL; // for kernel space buffer for user data
/* Check that our pool got allocated ok. */
if (cpqfcHBAdata->private_data_pool == NULL)
return -ENOMEM;
if( !arg)
return -EINVAL;
......@@ -549,83 +610,70 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
if( !buf)
return -ENOMEM;
}
// Now build a Scsi_Request to pass down...
ScsiPassThruReq = scsi_allocate_request(ScsiDev);
if (ScsiPassThruReq == NULL) {
kfree(buf);
return -ENOMEM;
}
ScsiPassThruReq->upper_private_data =
cpqfc_alloc_private_data(cpqfcHBAdata);
if (ScsiPassThruReq->upper_private_data == NULL) {
kfree(buf);
scsi_release_request(ScsiPassThruReq); // "de-allocate"
return -ENOMEM;
}
// Now build a SCSI_CMND to pass down...
// This function allocates and sets Scsi_Cmnd ptrs such as
// ->channel, ->target, ->host
ScsiPassThruCmnd = scsi_allocate_device(ScsiDev, 1, 1);
// Need data from user?
// make sure caller's buffer is in kernel space.
if( (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) &&
vendor_cmd->len)
if( copy_from_user( buf, vendor_cmd->bufp, vendor_cmd->len))
if (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) {
if (vendor_cmd->len) { // Need data from user?
if (copy_from_user(buf, vendor_cmd->bufp,
vendor_cmd->len)) {
kfree(buf);
cpqfc_free_private_data(cpqfcHBAdata,
ScsiPassThruReq->upper_private_data);
scsi_release_request(ScsiPassThruReq);
return( -EFAULT);
// copy the CDB (if/when MAX_COMMAND_SIZE is 16, remove copy below)
memcpy( &ScsiPassThruCmnd->cmnd[0],
&vendor_cmd->cdb[0],
MAX_COMMAND_SIZE);
// we want to copy all 16 bytes into the FCP-SCSI CDB,
// although the actual passthru only uses up to the
// first 12.
ScsiPassThruCmnd->cmd_len = 16; // sizeof FCP-SCSI CDB
// Unfortunately, the SCSI command cmnd[] field has only
// 12 bytes. Ideally the MAX_COMMAND_SIZE should be increased
// to 16 for newer Fibre Channel and SCSI-3 larger CDBs.
// However, to avoid a mandatory kernel rebuild, we use the SCp
// spare field to store the extra 4 bytes ( ugly :-(
if( MAX_COMMAND_SIZE < 16)
{
memcpy( &ScsiPassThruCmnd->SCp.buffers_residual,
&vendor_cmd->cdb[12], 4);
}
}
ScsiPassThruReq->sr_data_direction = SCSI_DATA_WRITE;
} else if (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) {
ScsiPassThruReq->sr_data_direction = SCSI_DATA_READ;
} else
// maybe this means a bug in the user app
ScsiPassThruReq->sr_data_direction = SCSI_DATA_NONE;
ScsiPassThruReq->sr_cmd_len = 0; // set correctly by scsi_do_req()
ScsiPassThruReq->sr_sense_buffer[0] = 0;
ScsiPassThruReq->sr_sense_buffer[2] = 0;
ScsiPassThruCmnd->SCp.sent_command = 1; // PASSTHRU!
// suppress LUN masking
// and VSA logic
// Use spare fields to copy FCP-SCSI LUN address info...
ScsiPassThruCmnd->SCp.phase = vendor_cmd->bus;
ScsiPassThruCmnd->SCp.have_data_in = vendor_cmd->pdrive;
// We copy the scheme used by scsi.c to submit commands
// We copy the scheme used by sd.c:spinup_disk() to submit commands
// to our own HBA. We do this in order to stall the
// thread calling the IOCTL until it completes, and use
// the same "_quecommand" function for synchronizing
// FC Link events with our "worker thread".
{
CPQFC_DECLARE_COMPLETION(wait);
ScsiPassThruCmnd->request->CPQFC_WAITING = &wait;
// eventually gets us to our own _quecommand routine
scsi_do_cmd( ScsiPassThruCmnd, &vendor_cmd->cdb[0],
buf,
vendor_cmd->len,
my_ioctl_done,
10*HZ, 1);// timeout,retries
// Other I/Os can now resume; we wait for our ioctl
// command to complete
CPQFC_WAIT_FOR_COMPLETION(&wait);
ScsiPassThruCmnd->request->CPQFC_WAITING = NULL;
}
privatedata = ScsiPassThruReq->upper_private_data;
privatedata->bus = vendor_cmd->bus;
privatedata->pdrive = vendor_cmd->pdrive;
result = ScsiPassThruCmnd->result;
// eventually gets us to our own _quecommand routine
scsi_wait_req(ScsiPassThruReq,
&vendor_cmd->cdb[0], buf, vendor_cmd->len,
10*HZ, // timeout
1); // retries
result = ScsiPassThruReq->sr_result;
// copy any sense data back to caller
if( result != 0 )
{
memcpy( vendor_cmd->sense_data, // see struct def - size=40
ScsiPassThruCmnd->sense_buffer,
sizeof(ScsiPassThruCmnd->sense_buffer));
ScsiPassThruReq->sr_sense_buffer,
sizeof(ScsiPassThruReq->sr_sense_buffer));
}
SDpnt = ScsiPassThruCmnd->device;
scsi_release_command(ScsiPassThruCmnd); // "de-allocate"
ScsiPassThruCmnd = NULL;
SDpnt = ScsiPassThruReq->sr_device;
/* upper_private_data is already freed in call_scsi_done() */
scsi_release_request(ScsiPassThruReq); // "de-allocate"
ScsiPassThruReq = NULL;
// if (!SDpnt->was_reset && SDpnt->scsi_request_fn)
// (*SDpnt->scsi_request_fn)();
......@@ -679,10 +727,21 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
case CPQFC_IOCTL_FC_TARGET_ADDRESS:
result =
verify_area(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress));
if (result)
// can we find an FC device mapping to this SCSI target?
DumCmnd.channel = ScsiDev->channel; // For searching
DumCmnd.target = ScsiDev->id;
DumCmnd.lun = ScsiDev->lun;
pLoggedInPort = fcFindLoggedInPort( fcChip,
&DumCmnd, // search Scsi Nexus
0, // DON'T search linked list for FC port id
NULL, // DON'T search linked list for FC WWN
NULL); // DON'T care about end of list
if (pLoggedInPort == NULL) {
result = -ENXIO;
break;
}
result = verify_area(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress));
if (result) break;
put_user(pLoggedInPort->port_id,
&((Scsi_FCTargAddress *) arg)->host_port_id);
......@@ -709,7 +768,6 @@ int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
result = -EINVAL;
break;
}
}
LEAVE("cpqfcTS_ioctl");
return result;
......@@ -747,6 +805,7 @@ int cpqfcTS_release(struct Scsi_Host *HostAdapter)
}
cpqfc_free_private_data_pool(cpqfcHBAdata);
// free Linux resources
DEBUG_PCI( printk(" cpqfcTS: freeing resources...\n"));
free_irq( HostAdapter->irq, HostAdapter);
......@@ -1527,6 +1586,12 @@ int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev,
Scsi_Cmnd * SCpnt;
Scsi_Device * SDpnt;
// FIXME, cpqfcTS_TargetDeviceReset needs to be fixed
// similarly to how the passthrough ioctl was fixed
// around the 2.5.30 kernel. Scsi_Cmnd replaced with
// Scsi_Request, etc.
// For now, so people don't fall into a hole...
return -ENOTSUPP;
// printk(" ENTERING cpqfcTS_TargetDeviceReset() - flag=%d \n",reset_flags);
......@@ -1543,6 +1608,7 @@ int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev,
SCpnt->SCp.buffers_residual = FCP_TARGET_RESET;
// FIXME: this would panic, SCpnt->request would be NULL.
SCpnt->request->CPQFC_WAITING = &wait;
scsi_do_cmd(SCpnt, scsi_cdb, NULL, 0, my_ioctl_done, timeout, retries);
CPQFC_WAIT_FOR_COMPLETION(&wait);
......@@ -1701,6 +1767,18 @@ void cpqfcTS_intr_handler( int irq,
UCHAR IntStat;
printk(" cpqfcTS adapter PCI error detected\n");
IntStat = readb( cpqfcHBA->fcChip.Registers.INTSTAT.address);
printk("cpqfc: ISR = 0x%02x\n", IntStat);
if (IntStat & 0x1) {
__u16 pcistat;
/* read the pci status register */
pci_read_config_word(cpqfcHBA->PciDev, 0x06, &pcistat);
printk("PCI status register is 0x%04x\n", pcistat);
if (pcistat & 0x8000) printk("Parity Error Detected.\n");
if (pcistat & 0x4000) printk("Signalled System Error\n");
if (pcistat & 0x2000) printk("Received Master Abort\n");
if (pcistat & 0x1000) printk("Received Target Abort\n");
if (pcistat & 0x0800) printk("Signalled Target Abort\n");
}
if (IntStat & 0x4) printk("(INT)\n");
if (IntStat & 0x8)
printk("CRS: PCI master address crossed 46 bit bouandary\n");
......
......@@ -33,7 +33,7 @@
// don't forget to also change MODULE_DESCRIPTION in cpqfcTSinit.c
#define VER_MAJOR 2
#define VER_MINOR 5
#define VER_SUBMINOR 0
#define VER_SUBMINOR 3
// Macros for kernel (esp. SMP) tracing using a PCI analyzer
// (e.g. x86).
......@@ -907,9 +907,17 @@ typedef struct
} FC_SCSI_QUE, *PFC_SCSI_QUE;
typedef struct {
/* This is tacked on to a Scsi_Request in upper_private_data
for pasthrough ioctls, as a place to hold data that can't
be stashed anywhere else in the Scsi_Request. We differentiate
this from _real_ upper_private_data by checking if the virt addr
is within our special pool. */
ushort bus;
ushort pdrive;
} cpqfc_passthru_private_t;
#define CPQFC_MAX_PASSTHRU_CMDS 100
#define DYNAMIC_ALLOCATIONS 4 // Tachyon aligned allocations: ERQ,IMQ,SFQ,SEST
......@@ -949,6 +957,8 @@ typedef struct
PFC_LINK_QUE fcLQ; // the WorkerThread operates on this
spinlock_t hba_spinlock; // held/released by WorkerThread
cpqfc_passthru_private_t *private_data_pool;
unsigned long *private_data_bits;
} CPQFCHBA;
......@@ -1405,6 +1415,7 @@ struct ext_sg_entry_t {
__u32 lba; /* lower bus address bits 0-31 */
};
// J. McCarty's LINK.H
//
// LS_RJT Reason Codes
......
......@@ -2887,15 +2887,22 @@ static void ScsiReportLunsDone(Scsi_Cmnd *Cmnd)
Done:
}
extern int is_private_data_of_cpqfc(CPQFCHBA *hba, void * pointer);
extern void cpqfc_free_private_data(CPQFCHBA *hba, cpqfc_passthru_private_t *data);
static void
call_scsi_done(Scsi_Cmnd *Cmnd)
{
// We have to reinitialize sent_command here, so the scsi-mid
// layer won't re-use the scsi command leaving it set incorrectly.
// (incorrectly for our purposes...it's normally unused.)
if (Cmnd->SCp.sent_command != 0) { // was it a passthru?
Cmnd->SCp.sent_command = 0;
CPQFCHBA *hba;
hba = (CPQFCHBA *) Cmnd->host->hostdata;
// Was this command a cpqfc passthru ioctl ?
if (Cmnd->sc_request != NULL && Cmnd->host != NULL &&
Cmnd->host->hostdata != NULL &&
is_private_data_of_cpqfc((CPQFCHBA *) Cmnd->host->hostdata,
Cmnd->sc_request->upper_private_data)) {
cpqfc_free_private_data(hba,
Cmnd->sc_request->upper_private_data);
Cmnd->sc_request->upper_private_data = NULL;
Cmnd->result &= 0xff00ffff;
Cmnd->result |= (DID_PASSTHROUGH << 16); // prevents retry
}
......@@ -3293,6 +3300,7 @@ static int GetLoopID( ULONG al_pa )
}
#endif
extern cpqfc_passthru_private_t *cpqfc_private(Scsi_Request *sr);
// Search the singly (forward) linked list "fcPorts" looking for
// either the SCSI target (if != -1), port_id (if not NULL),
......@@ -3366,8 +3374,18 @@ PFC_LOGGEDIN_PORT fcFindLoggedInPort(
{
// For "passthru" modes, the IOCTL caller is responsible
// for setting the FCP-LUN addressing
if( !Cmnd->SCp.sent_command ) // NOT passthru?
{
if (Cmnd->sc_request != NULL && Cmnd->host != NULL &&
Cmnd->host->hostdata != NULL &&
is_private_data_of_cpqfc((CPQFCHBA *) Cmnd->host->hostdata,
Cmnd->sc_request->upper_private_data)) {
/* This is a passthru... */
cpqfc_passthru_private_t *pd;
pd = Cmnd->sc_request->upper_private_data;
Cmnd->SCp.phase = pd->bus;
// Cmnd->SCp.have_data_in = pd->pdrive;
Cmnd->SCp.have_data_in = Cmnd->lun;
} else {
/* This is not a passthru... */
// set the FCP-LUN addressing type
Cmnd->SCp.phase = pLoggedInPort->ScsiNexus.VolumeSetAddressing;
......@@ -3380,6 +3398,8 @@ PFC_LOGGEDIN_PORT fcFindLoggedInPort(
// Report Luns command
if( pLoggedInPort->ScsiNexus.LunMasking == 1)
{
if (Cmnd->lun > sizeof(pLoggedInPort->ScsiNexus.lun))
return NULL;
// we KNOW all the valid LUNs... 0xFF is invalid!
Cmnd->SCp.have_data_in = pLoggedInPort->ScsiNexus.lun[Cmnd->lun];
if (pLoggedInPort->ScsiNexus.lun[Cmnd->lun] == 0xFF)
......@@ -3504,7 +3524,6 @@ static void UnblockScsiDevice( struct Scsi_Host *HostAdapter,
{
printk("LinkDnCmnd scsi_done ptr null, port_id %Xh\n",
pLoggedInPort->port_id);
Cmnd->SCp.sent_command = 0;
}
else
call_scsi_done(Cmnd);
......@@ -5232,7 +5251,6 @@ static ULONG build_SEST_sgList(
sgl = (struct scatterlist*)Cmnd->request_buffer;
sg_count = pci_map_sg(pcidev, sgl, Cmnd->use_sg,
scsi_to_pci_dma_dir(Cmnd->sc_data_direction));
// printk("sgl = %p, sg_count = %d\n", (void *) sgl, sg_count);
if( sg_count <= 3 ) {
// we need to be careful here that no individual mapping
......@@ -5261,7 +5279,6 @@ static ULONG build_SEST_sgList(
// printk("totalsgs = %d, sgcount=%d\n",totalsgs,sg_count);
}
// printk("totalsgs = %d, sgcount=%d\n", totalsgs, sg_count);
if( totalsgs <= 3 ) // can (must) use "local" SEST list
{
while( bytes_to_go)
......@@ -6164,13 +6181,11 @@ void cpqfcTSCompleteExchange(
}
else
{
Exchanges->fcExchange[ x_ID ].Cmnd->SCp.sent_command = 0;
// printk(" not calling scsi_done on x_ID %Xh, Cmnd %p\n",
// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
}
}
else{
Exchanges->fcExchange[ x_ID ].Cmnd->SCp.sent_command = 0;
printk(" x_ID %Xh, type %Xh, Cdb0 %Xh\n", x_ID,
Exchanges->fcExchange[ x_ID ].type,
Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0]);
......@@ -6463,10 +6478,10 @@ static int build_FCP_payload( Scsi_Cmnd *Cmnd,
for( i=0; (i < Cmnd->cmd_len) && i < MAX_COMMAND_SIZE; i++)
*payload++ = Cmnd->cmnd[i];
if( Cmnd->cmd_len == 16 )
{
memcpy( payload, &Cmnd->SCp.buffers_residual, 4);
}
// if( Cmnd->cmd_len == 16 )
// {
// memcpy( payload, &Cmnd->SCp.buffers_residual, 4);
// }
payload+= (16 - i);
// FCP_DL is largest number of expected data bytes
......
/* $Id: scsi_debug.c,v 1.1 1992/07/24 06:27:38 root Exp root $
/*
* linux/kernel/scsi_debug.c
*
* Copyright (C) 1992 Eric Youngdale
......@@ -12,10 +12,11 @@
*
* For documentation see http://www.torque.net/sg/sdebug.html
*
* D. Gilbert (dpg) work for MOD device test [20010421]
* dpg, work for devfs large number of disks [20010809]
* dpg, forked for lk 2.5 series [20011216, 20020101]
* dpg, use vmalloc() more inquiry+mode_sense [20020302]
* D. Gilbert (dpg) work for Magneto-Optical device test [20010421]
* dpg: work for devfs large number of disks [20010809]
* forked for lk 2.5 series [20011216, 20020101]
* use vmalloc() more inquiry+mode_sense [20020302]
* add timers for delayed responses [20020721]
*/
#include <linux/config.h>
......@@ -46,7 +47,12 @@
#include <linux/version.h>
#endif
static char scsi_debug_version_str[] = "Version: 1.59 (20020302)";
#include "scsi_debug.h"
static const char * scsi_debug_version_str = "Version: 1.62 (20020812)";
#define DRIVERFS_SUPPORT 1 /* comment out whole line to disable */
#ifndef SCSI_CMD_READ_16
#define SCSI_CMD_READ_16 0x88
......@@ -54,29 +60,36 @@ static char scsi_debug_version_str[] = "Version: 1.59 (20020302)";
#ifndef SCSI_CMD_WRITE_16
#define SCSI_CMD_WRITE_16 0x8a
#endif
#ifndef REPORT_LUNS
#define REPORT_LUNS 0xa0
#endif
/* A few options that we want selected */
#define DEF_NR_FAKE_DEVS 1
#define DEF_DEV_SIZE_MB 8
#define DEF_FAKE_BLK0 0
#define DEF_EVERY_NTH 100
#define DEF_DELAY 1
#define DEF_OPTS 0
#define SCSI_DEBUG_OPT_NOISE 1
#define SCSI_DEBUG_OPT_MEDIUM_ERR 2
#define SCSI_DEBUG_OPT_EVERY_NTH 4
#define OPT_MEDIUM_ERR_ADDR 0x1234
static int scsi_debug_num_devs = DEF_NR_FAKE_DEVS;
static int scsi_debug_opts = DEF_OPTS;
static int scsi_debug_every_nth = DEF_EVERY_NTH;
static int scsi_debug_cmnd_count = 0;
static int scsi_debug_delay = DEF_DELAY;
#define NR_HOSTS_PRESENT (((scsi_debug_num_devs - 1) / 7) + 1)
#define N_HEAD 8
#define N_SECTOR 32
#define DEV_READONLY(TGT) (0)
#define DEV_REMOVEABLE(TGT) (0)
#define DEVICE_TYPE(TGT) (TYPE_DISK);
#define SCSI_DEBUG_MAILBOXES (scsi_debug_num_devs + 1)
#define PERIPH_DEVICE_TYPE(TGT) (TYPE_DISK);
static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
#define STORE_SIZE (scsi_debug_dev_size_mb * 1024 * 1024)
......@@ -87,81 +100,79 @@ static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
#define N_CYLINDER (STORE_SIZE / (SECT_SIZE * N_SECTOR * N_HEAD))
/* Do not attempt to use a timer to simulate a real disk with latency */
/* Only use this in the actual kernel, not in the simulator. */
#define IMMEDIATE
#define START_PARTITION 4
/* Time to wait before completing a command */
#define DISK_SPEED (HZ/10) /* 100ms */
#define CAPACITY (N_HEAD * N_SECTOR * N_CYLINDER)
#define SECT_SIZE_PER(TGT) SECT_SIZE
static int starts[] =
{N_SECTOR,
N_HEAD * N_SECTOR, /* Single cylinder */
N_HEAD * N_SECTOR * 4,
0 /* CAPACITY */, 0};
static unsigned char * fake_storep;
#define SDEBUG_SENSE_LEN 32
typedef struct sdebug_dev_info {
struct sdebug_dev_info {
Scsi_Device * sdp;
unsigned short host_no;
unsigned short id;
unsigned char sense_buff[SDEBUG_SENSE_LEN]; /* weak nexus */
char reset;
char sb_index;
} Sdebug_dev_info;
static Sdebug_dev_info * devInfop;
};
static struct sdebug_dev_info * devInfop;
typedef void (* done_funct_t) (Scsi_Cmnd *);
struct sdebug_queued_cmd {
int in_use;
struct timer_list cmnd_timer;
done_funct_t done_funct;
struct scsi_cmnd * a_cmnd;
int scsi_result;
};
static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
static unsigned char * fake_storep; /* ramdisk storage */
static unsigned char broken_buff[SDEBUG_SENSE_LEN];
static int num_aborts = 0;
static int num_dev_resets = 0;
static int num_bus_resets = 0;
static int num_host_resets = 0;
static spinlock_t mailbox_lock = SPIN_LOCK_UNLOCKED;
static rwlock_t sdebug_atomic_rw = RW_LOCK_UNLOCKED;
#include "scsi_debug.h"
static spinlock_t queued_arr_lock = SPIN_LOCK_UNLOCKED;
static rwlock_t atomic_rw = RW_LOCK_UNLOCKED;
typedef void (*done_fct_t) (Scsi_Cmnd *);
static volatile done_fct_t * do_done = 0;
static struct Scsi_Host * SHpnt = NULL;
#ifdef DRIVERFS_SUPPORT
static struct device_driver sdebug_driverfs_driver;
#endif
static int scsi_debug_inquiry(unsigned char * cmd, int target,
unsigned char * buff, int bufflen,
Sdebug_dev_info * devip);
static int scsi_debug_mode_sense(unsigned char * cmd, int target,
/* function declarations */
static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,
int bufflen, struct sdebug_dev_info * devip);
static int resp_mode_sense(unsigned char * cmd, int target,
unsigned char * buff, int bufflen,
Sdebug_dev_info * devip);
static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block,
int num, int * errstsp, Sdebug_dev_info * devip);
static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block,
int num, int * errstsp, Sdebug_dev_info * devip);
static void scsi_debug_intr_handle(unsigned long);
static Sdebug_dev_info * devInfoReg(Scsi_Device * sdp);
static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key,
struct sdebug_dev_info * devip);
static int resp_read(Scsi_Cmnd * SCpnt, int upper_blk, int block,
int num, struct sdebug_dev_info * devip);
static int resp_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, int num,
struct sdebug_dev_info * devip);
static int resp_report_luns(unsigned char * cmd, unsigned char * buff,
int bufflen, struct sdebug_dev_info * devip);
static void timer_intr_handler(unsigned long);
static struct sdebug_dev_info * devInfoReg(Scsi_Device * sdp);
static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
int asc, int asq, int inbandLen);
static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip);
static struct timer_list * timeout = 0;
static Scsi_Cmnd ** SCint = 0;
/*
* Semaphore used to simulate bus lockups.
*/
static int scsi_debug_lockup = 0;
#define NUM_SENSE_BUFFS 4
#define SENSE_BUFF_LEN 32
static char sense_buffers[NUM_SENSE_BUFFS][SENSE_BUFF_LEN];
static int check_reset(Scsi_Cmnd * SCpnt, struct sdebug_dev_info * devip);
static int schedule_resp(struct scsi_cmnd * cmnd,
struct sdebug_dev_info * devip,
done_funct_t done, int scsi_result, int delta_jiff);
static void init_all_queued(void);
static void stop_all_queued(void);
static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
const char * dev_id_str, int dev_id_str_len);
#ifdef DRIVERFS_SUPPORT
static void do_create_driverfs_files(void);
static void do_remove_driverfs_files(void);
#endif
static inline
unsigned char * sdebug_scatg2virt(const struct scatterlist * sclp)
static unsigned char * scatg2virt(const struct scatterlist * sclp)
{
if (NULL == sclp)
return NULL;
......@@ -173,61 +184,47 @@ unsigned char * sdebug_scatg2virt(const struct scatterlist * sclp)
}
static
int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, done_funct_t done)
{
unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
int block;
int upper_blk;
unsigned char *buff;
int scsi_debug_errsts;
int errsts = 0;
int target = SCpnt->target;
int bufflen = SCpnt->request_bufflen;
unsigned long iflags;
int i, num, capac;
Sdebug_dev_info * devip = NULL;
char * sbuff;
int num, capac;
struct sdebug_dev_info * devip = NULL;
unsigned char * sbuff;
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
printk(KERN_INFO "scsi_debug: queue_command: cmd ");
for (i = 0, num = SCpnt->cmd_len; i < num; ++i)
printk("%02x ", cmd[i]);
printk(" use_sg=%d\n", SCpnt->use_sg);
}
/*
* If we are being notified of the mid-level reposessing a command
* due to timeout, just return.
*/
if (done == NULL) {
return 0;
}
if (done == NULL)
return 0; /* assume mid level reprocessing command */
if (SCpnt->use_sg) { /* just use first element */
struct scatterlist *sgpnt = (struct scatterlist *)
SCpnt->request_buffer;
buff = sdebug_scatg2virt(&sgpnt[0]);
buff = scatg2virt(&sgpnt[0]);
bufflen = sgpnt[0].length;
/* READ and WRITE process scatterlist themselves */
}
else
buff = (unsigned char *) SCpnt->request_buffer;
/*
* If a command comes for the ID of the host itself, just print
* a silly message and return.
*/
if(target == 7) {
printk(KERN_WARNING "How do you do!\n");
SCpnt->result = 0;
done(SCpnt);
return 0;
if (NULL == buff) {
printk(KERN_WARNING "scsi_debug:qc: buff was NULL??\n");
buff = broken_buff; /* just point at dummy */
bufflen = SDEBUG_SENSE_LEN;
}
if ((target > 7) || (SCpnt->lun != 0)) {
SCpnt->result = DID_NO_CONNECT << 16;
done(SCpnt);
return 0;
if(target == driver_template.this_id) {
printk(KERN_WARNING
"scsi_debug: initiator's id used as target!\n");
return schedule_resp(SCpnt, NULL, done, 0, 0);
}
if ((target > driver_template.this_id) || (SCpnt->lun != 0))
return schedule_resp(SCpnt, NULL, done,
DID_NO_CONNECT << 16, 0);
#if 0
printk(KERN_INFO "sdebug:qc: host_no=%d, id=%d, sdp=%p, cmd=0x%x\n",
(int)SCpnt->device->host->host_no, (int)SCpnt->device->id,
......@@ -235,101 +232,93 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
#endif
if (NULL == SCpnt->device->hostdata) {
devip = devInfoReg(SCpnt->device);
if (NULL == devip) {
SCpnt->result = DID_NO_CONNECT << 16;
done(SCpnt);
return 0;
}
if (NULL == devip)
return schedule_resp(SCpnt, NULL, done,
DID_NO_CONNECT << 16, 0);
SCpnt->device->hostdata = devip;
}
devip = SCpnt->device->hostdata;
if ((SCSI_DEBUG_OPT_EVERY_NTH & scsi_debug_opts) &&
(scsi_debug_every_nth > 0) &&
(++scsi_debug_cmnd_count >= scsi_debug_every_nth)) {
scsi_debug_cmnd_count =0;
return 0; /* ignore command causing timeout */
}
switch (*cmd) {
case INQUIRY: /* mandatory */
scsi_debug_errsts = scsi_debug_inquiry(cmd, target, buff,
bufflen, devip);
/* assume INQUIRY called first so setup max_cmd_len */
if (SCpnt->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
SCpnt->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
errsts = resp_inquiry(cmd, target, buff, bufflen, devip);
break;
case REQUEST_SENSE: /* mandatory */
SCSI_LOG_LLQUEUE(3, printk("Request sense...\n"));
/* Since this driver indicates autosense by placing the
* sense buffer in the scsi_cmnd structure in the response
* (when CHECK_CONDITION is set), the mid level shouldn't
* need to call REQUEST_SENSE */
if (devip) {
sbuff = &sense_buffers[(int)devip->sb_index][0];
devip->sb_index = 0;
sbuff = devip->sense_buff;
memcpy(buff, sbuff, (bufflen < SDEBUG_SENSE_LEN) ?
bufflen : SDEBUG_SENSE_LEN);
mk_sense_buffer(devip, 0, 0x0, 0, 7);
} else {
memset(buff, 0, bufflen);
buff[0] = 0x70;
}
else
sbuff = &sense_buffers[0][0];
memcpy(buff, sbuff, (bufflen < SENSE_BUFF_LEN) ?
bufflen : SENSE_BUFF_LEN);
memset(sbuff, 0, SENSE_BUFF_LEN);
sbuff[0] = 0x70;
SCpnt->result = 0;
done(SCpnt);
return 0;
break;
case START_STOP:
if (check_reset(SCpnt, devip)) {
done(SCpnt);
return 0;
}
SCSI_LOG_LLQUEUE(3, printk("START_STOP\n"));
scsi_debug_errsts = 0;
errsts = check_reset(SCpnt, devip);
break;
case ALLOW_MEDIUM_REMOVAL:
if (check_reset(SCpnt, devip)) {
done(SCpnt);
return 0;
}
if (cmd[4]) {
SCSI_LOG_LLQUEUE(2, printk(
"Medium removal inhibited..."));
} else {
SCSI_LOG_LLQUEUE(2,
printk("Medium removal enabled..."));
}
scsi_debug_errsts = 0;
if ((errsts = check_reset(SCpnt, devip)))
break;
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk("\tMedium removal %s\n",
cmd[4] ? "inhibited" : "enabled");
break;
case SEND_DIAGNOSTIC: /* mandatory */
SCSI_LOG_LLQUEUE(3, printk("Send Diagnostic\n"));
if (buff)
memset(buff, 0, bufflen);
scsi_debug_errsts = 0;
break;
case TEST_UNIT_READY: /* mandatory */
SCSI_LOG_LLQUEUE(3, printk("Test unit ready(%p %d)\n",
buff, bufflen));
if (buff)
memset(buff, 0, bufflen);
scsi_debug_errsts = 0;
break;
case RESERVE:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case RESERVE_10:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case RELEASE:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case RELEASE_10:
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
break;
case READ_CAPACITY:
if (check_reset(SCpnt, devip)) {
done(SCpnt);
return 0;
}
SCSI_LOG_LLQUEUE(3, printk("Read Capacity\n"));
SHpnt = SCpnt->host;
errsts = check_reset(SCpnt, devip);
memset(buff, 0, bufflen);
if (bufflen > 7) {
capac = CAPACITY - 1;
buff[0] = (capac >> 24);
buff[1] = (capac >> 16) & 0xff;
buff[2] = (capac >> 8) & 0xff;
buff[3] = capac & 0xff;
buff[4] = 0;
buff[5] = 0;
buff[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
buff[7] = SECT_SIZE_PER(target) & 0xff;
scsi_debug_errsts = 0;
}
break;
case SCSI_CMD_READ_16: /* SBC-2 */
case READ_12:
case READ_10:
case READ_6:
if (check_reset(SCpnt, devip)) {
done(SCpnt);
return 0;
}
if ((errsts = check_reset(SCpnt, devip)))
break;
upper_blk = 0;
if ((*cmd) == SCSI_CMD_READ_16) {
upper_blk = cmd[5] + (cmd[4] << 8) +
......@@ -338,38 +327,31 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
(cmd[7] << 16) + (cmd[6] << 24);
num = cmd[13] + (cmd[12] << 8) +
(cmd[11] << 16) + (cmd[10] << 24);
}
else if ((*cmd) == READ_12) {
} else if ((*cmd) == READ_12) {
block = cmd[5] + (cmd[4] << 8) +
(cmd[3] << 16) + (cmd[2] << 24);
num = cmd[9] + (cmd[8] << 8) +
(cmd[7] << 16) + (cmd[6] << 24);
}
else if ((*cmd) == READ_10) {
} else if ((*cmd) == READ_10) {
block = cmd[5] + (cmd[4] << 8) +
(cmd[3] << 16) + (cmd[2] << 24);
num = cmd[8] + (cmd[7] << 8);
}
else {
} else {
block = cmd[3] + (cmd[2] << 8) +
((cmd[1] & 0x1f) << 16);
num = cmd[4];
}
if (scsi_debug_read(SCpnt, upper_blk, block, num,
&scsi_debug_errsts, devip))
errsts = resp_read(SCpnt, upper_blk, block, num, devip);
break;
case REPORT_LUNS:
errsts = resp_report_luns(cmd, buff, bufflen, devip);
break;
SCpnt->result = 0;
/* calls bottom half in upper layers before return from scsi_do_...() */
(done) (SCpnt);
return 0;
case SCSI_CMD_WRITE_16: /* SBC-2 */
case WRITE_12:
case WRITE_10:
case WRITE_6:
if (check_reset(SCpnt, devip)) {
done(SCpnt);
return 0;
}
if ((errsts = check_reset(SCpnt, devip)))
break;
upper_blk = 0;
if ((*cmd) == SCSI_CMD_WRITE_16) {
upper_blk = cmd[5] + (cmd[4] << 8) +
......@@ -378,97 +360,38 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
(cmd[7] << 16) + (cmd[6] << 24);
num = cmd[13] + (cmd[12] << 8) +
(cmd[11] << 16) + (cmd[10] << 24);
}
else if ((*cmd) == WRITE_12) {
} else if ((*cmd) == WRITE_12) {
block = cmd[5] + (cmd[4] << 8) +
(cmd[3] << 16) + (cmd[2] << 24);
num = cmd[9] + (cmd[8] << 8) +
(cmd[7] << 16) + (cmd[6] << 24);
}
else if ((*cmd) == WRITE_10) {
} else if ((*cmd) == WRITE_10) {
block = cmd[5] + (cmd[4] << 8) +
(cmd[3] << 16) + (cmd[2] << 24);
num = cmd[8] + (cmd[7] << 8);
}
else {
} else {
block = cmd[3] + (cmd[2] << 8) +
((cmd[1] & 0x1f) << 16);
num = cmd[4];
}
if (scsi_debug_write(SCpnt, upper_blk, block, num,
&scsi_debug_errsts, devip))
errsts = resp_write(SCpnt, upper_blk, block, num, devip);
break;
SCpnt->result = 0;
/* calls bottom half in upper layers before return from scsi_do_...() */
(done) (SCpnt);
return 0;
case MODE_SENSE:
case MODE_SENSE_10:
scsi_debug_errsts =
scsi_debug_mode_sense(cmd, target, buff, bufflen, devip);
errsts = resp_mode_sense(cmd, target, buff, bufflen, devip);
break;
default:
#if 0
printk(KERN_INFO "scsi_debug: Unsupported command, "
"opcode=0x%x\n", (int)cmd[0]);
#endif
if (check_reset(SCpnt, devip)) {
done(SCpnt);
return 0;
}
scsi_debug_errsts = (COMMAND_COMPLETE << 8) |
(CHECK_CONDITION << 1);
mk_sense_buffer(devip, 2, ILLEGAL_REQUEST, 0x20, 0, 14);
if ((errsts = check_reset(SCpnt, devip)))
break;
}
spin_lock_irqsave(&mailbox_lock, iflags);
for (i = 0; i < SCSI_DEBUG_MAILBOXES; i++) {
if (timeout[i].function == NULL)
mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x20, 0, 14);
errsts = (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
break;
}
/*
* If all of the slots are full, just return 1. The new error
* handling scheme allows this, and the mid-level should queue things.
*/
if (i >= SCSI_DEBUG_MAILBOXES || timeout[i].function != 0) {
SCSI_LOG_LLQUEUE(1, printk("Command rejected - host busy\n"));
spin_unlock_irqrestore(&mailbox_lock, iflags);
return 1;
}
SCSI_LOG_LLQUEUE(1, printk("Command accepted - slot %d\n", i));
#ifdef IMMEDIATE
if (!scsi_debug_lockup) {
SCpnt->result = scsi_debug_errsts;
SCint[i] = SCpnt;
do_done[i] = done;
scsi_debug_intr_handle(i); /* No timer - do this one right away */
}
spin_unlock_irqrestore(&mailbox_lock, iflags);
#else
SCpnt->result = scsi_debug_errsts;
timeout[i].function = scsi_debug_intr_handle;
timeout[i].data = i;
timeout[i].expires = jiffies + DISK_SPEED;
SCint[i] = SCpnt;
do_done[i] = done;
spin_unlock_irqrestore(&mailbox_lock, iflags);
add_timer(&timeout[i]);
if (!done)
printk(KERN_ERR "scsi_debug_queuecommand: "
"done can't be NULL\n");
#if 0
printk(KERN_INFO "Sending command (%d %x %d %d)...", i, done,
timeout[i].expires, jiffies);
#endif
#endif
return 0;
return schedule_resp(SCpnt, devip, done, errsts, scsi_debug_delay);
}
static int scsi_debug_ioctl(Scsi_Device *dev, int cmd, void *arg)
......@@ -476,58 +399,101 @@ static int scsi_debug_ioctl(Scsi_Device *dev, int cmd, void *arg)
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
}
return -ENOTTY;
return -EINVAL;
/* return -ENOTTY; // correct return but upsets fdisk */
}
static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip)
static int check_reset(Scsi_Cmnd * SCpnt, struct sdebug_dev_info * devip)
{
if (devip->reset) {
devip->reset = 0;
mk_sense_buffer(devip, 3, UNIT_ATTENTION, 0x29, 0, 14);
SCpnt->result = (COMMAND_COMPLETE << 8) |
(CHECK_CONDITION << 1);
return 1;
mk_sense_buffer(devip, UNIT_ATTENTION, 0x29, 0, 14);
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
}
return 0;
}
#define SDEBUG_MAX_INQ_SZ 58
#define SDEBUG_LONG_INQ_SZ 58
#define SDEBUG_MAX_INQ_ARR_SZ 128
static int scsi_debug_inquiry(unsigned char * cmd, int target,
unsigned char * buff, int bufflen,
Sdebug_dev_info * devip)
static const char * vendor_id = "Linux ";
static const char * product_id = "scsi_debug ";
static const char * product_rev = "0004";
static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
const char * dev_id_str, int dev_id_str_len)
{
int num;
/* Two identification descriptors: */
/* T10 vendor identifier field format (faked) */
arr[0] = 0x2; /* ASCII */
arr[1] = 0x1;
arr[2] = 0x0;
memcpy(&arr[4], vendor_id, 8);
memcpy(&arr[12], product_id, 16);
memcpy(&arr[28], dev_id_str, dev_id_str_len);
num = 8 + 16 + dev_id_str_len;
arr[3] = num;
num += 4;
/* NAA IEEE registered identifier (faked) */
arr[num] = 0x1; /* binary */
arr[num + 1] = 0x3;
arr[num + 2] = 0x0;
arr[num + 3] = 0x8;
arr[num + 4] = 0x51; /* ieee company id=0x123456 (faked) */
arr[num + 5] = 0x23;
arr[num + 6] = 0x45;
arr[num + 7] = 0x60;
arr[num + 8] = (dev_id_num >> 24);
arr[num + 9] = (dev_id_num >> 16) & 0xff;
arr[num + 10] = (dev_id_num >> 8) & 0xff;
arr[num + 11] = dev_id_num & 0xff;
return num + 12;
}
static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff,
int bufflen, struct sdebug_dev_info * devip)
{
unsigned char pq_pdt;
unsigned char arr[SDEBUG_MAX_INQ_SZ];
int min_len = bufflen > SDEBUG_MAX_INQ_SZ ?
SDEBUG_MAX_INQ_SZ : bufflen;
unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ];
int min_len = bufflen > SDEBUG_MAX_INQ_ARR_SZ ?
SDEBUG_MAX_INQ_ARR_SZ : bufflen;
SCSI_LOG_LLQUEUE(3, printk("Inquiry...(%p %d)\n", buff, bufflen));
if (bufflen < cmd[4])
printk(KERN_INFO "scsi_debug: inquiry: bufflen=%d "
"< alloc_length=%d\n", bufflen, (int)cmd[4]);
memset(buff, 0, bufflen);
memset(arr, 0, SDEBUG_MAX_INQ_SZ);
pq_pdt = DEVICE_TYPE(target);
memset(arr, 0, SDEBUG_MAX_INQ_ARR_SZ);
pq_pdt = PERIPH_DEVICE_TYPE(target);
arr[0] = pq_pdt;
if (0x2 & cmd[1]) { /* CMDDT bit set */
mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14);
mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14);
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
}
else if (0x1 & cmd[1]) { /* EVPD bit set */
} else if (0x1 & cmd[1]) { /* EVPD bit set */
int dev_id_num, len;
char dev_id_str[6];
dev_id_num = ((devip->sdp->host->host_no + 1) * 1000) +
devip->sdp->id;
len = snprintf(dev_id_str, 6, "%d", dev_id_num);
len = (len > 6) ? 6 : len;
if (0 == cmd[2]) { /* supported vital product data pages */
arr[3] = 1;
arr[4] = 0x80; /* ... only unit serial number */
}
else if (0x80 == cmd[2]) { /* unit serial number */
arr[3] = 3;
arr[4] = 0x0; /* this page */
arr[5] = 0x80; /* unit serial number */
arr[6] = 0x83; /* device identification */
} else if (0x80 == cmd[2]) { /* unit serial number */
arr[1] = 0x80;
arr[3] = 4;
arr[4] = '1'; arr[5] = '2'; arr[6] = '3';
arr[7] = '4';
}
else {
arr[3] = len;
memcpy(&arr[4], dev_id_str, len);
} else if (0x83 == cmd[2]) { /* device identification */
arr[1] = 0x83;
arr[3] = inquiry_evpd_83(&arr[4], dev_id_num,
dev_id_str, len);
} else {
/* Illegal request, invalid field in cdb */
mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14);
mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14);
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
}
memcpy(buff, arr, min_len);
......@@ -536,18 +502,18 @@ static int scsi_debug_inquiry(unsigned char * cmd, int target,
/* drops through here for a standard inquiry */
arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */
arr[2] = 3; /* claim SCSI 3 */
arr[4] = SDEBUG_MAX_INQ_SZ - 5;
arr[4] = SDEBUG_LONG_INQ_SZ - 5;
arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */
memcpy(&arr[8], "Linux ", 8);
memcpy(&arr[16], "scsi_debug ", 16);
memcpy(&arr[32], "0003", 4);
memcpy(&arr[8], vendor_id, 8);
memcpy(&arr[16], product_id, 16);
memcpy(&arr[32], product_rev, 4);
memcpy(buff, arr, min_len);
return 0;
}
/* <<Following mode page info copied from ST318451LW>> */
static int sdebug_err_recov_pg(unsigned char * p, int pcontrol, int target)
static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
{ /* Read-Write Error Recovery page for mode_sense */
unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
5, 0, 0xff, 0xff};
......@@ -558,7 +524,7 @@ static int sdebug_err_recov_pg(unsigned char * p, int pcontrol, int target)
return sizeof(err_recov_pg);
}
static int sdebug_disconnect_pg(unsigned char * p, int pcontrol, int target)
static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
{ /* Disconnect-Reconnect page for mode_sense */
unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
......@@ -569,7 +535,25 @@ static int sdebug_disconnect_pg(unsigned char * p, int pcontrol, int target)
return sizeof(disconnect_pg);
}
static int sdebug_caching_pg(unsigned char * p, int pcontrol, int target)
static int resp_format_pg(unsigned char * p, int pcontrol, int target)
{ /* Format device page for mode_sense */
unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0x40, 0, 0, 0};
memcpy(p, format_pg, sizeof(format_pg));
p[10] = (N_SECTOR >> 8) & 0xff;
p[11] = N_SECTOR & 0xff;
p[12] = (SECT_SIZE >> 8) & 0xff;
p[13] = SECT_SIZE & 0xff;
if (DEV_REMOVEABLE(target))
p[20] |= 0x20; /* should agree with INQUIRY */
if (1 == pcontrol)
memset(p + 2, 0, sizeof(format_pg) - 2);
return sizeof(format_pg);
}
static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
{ /* Caching page for mode_sense */
unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0};
......@@ -580,7 +564,7 @@ static int sdebug_caching_pg(unsigned char * p, int pcontrol, int target)
return sizeof(caching_pg);
}
static int sdebug_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
{ /* Control mode page for mode_sense */
unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
0, 0, 0x2, 0x4b};
......@@ -594,9 +578,9 @@ static int sdebug_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
#define SDEBUG_MAX_MSENSE_SZ 256
static int scsi_debug_mode_sense(unsigned char * cmd, int target,
static int resp_mode_sense(unsigned char * cmd, int target,
unsigned char * buff, int bufflen,
Sdebug_dev_info * devip)
struct sdebug_dev_info * devip)
{
unsigned char dbd;
int pcontrol, pcode;
......@@ -622,15 +606,14 @@ static int scsi_debug_mode_sense(unsigned char * cmd, int target,
memset(buff, 0, bufflen);
memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
if (0x3 == pcontrol) { /* Saving values not supported */
mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x39, 0, 14);
mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x39, 0, 14);
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
}
dev_spec = DEV_READONLY(target) ? 0x80 : 0x0;
if (msense_6) {
arr[2] = dev_spec;
offset = 4;
}
else {
} else {
arr[3] = dev_spec;
offset = 8;
}
......@@ -638,30 +621,34 @@ static int scsi_debug_mode_sense(unsigned char * cmd, int target,
switch (pcode) {
case 0x1: /* Read-Write error recovery page, direct access */
len = sdebug_err_recov_pg(ap, pcontrol, target);
len = resp_err_recov_pg(ap, pcontrol, target);
offset += len;
break;
case 0x2: /* Disconnect-Reconnect page, all devices */
len = sdebug_disconnect_pg(ap, pcontrol, target);
len = resp_disconnect_pg(ap, pcontrol, target);
offset += len;
break;
case 0x3: /* Format device page, direct access */
len = resp_format_pg(ap, pcontrol, target);
offset += len;
break;
case 0x8: /* Caching page, direct access */
len = sdebug_caching_pg(ap, pcontrol, target);
len = resp_caching_pg(ap, pcontrol, target);
offset += len;
break;
case 0xa: /* Control Mode page, all devices */
len = sdebug_ctrl_m_pg(ap, pcontrol, target);
len = resp_ctrl_m_pg(ap, pcontrol, target);
offset += len;
break;
case 0x3f: /* Read all Mode pages */
len = sdebug_err_recov_pg(ap, pcontrol, target);
len += sdebug_disconnect_pg(ap + len, pcontrol, target);
len += sdebug_caching_pg(ap + len, pcontrol, target);
len += sdebug_ctrl_m_pg(ap + len, pcontrol, target);
len = resp_err_recov_pg(ap, pcontrol, target);
len += resp_disconnect_pg(ap + len, pcontrol, target);
len += resp_caching_pg(ap + len, pcontrol, target);
len += resp_ctrl_m_pg(ap + len, pcontrol, target);
offset += len;
break;
default:
mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14);
mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14);
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
}
if (msense_6)
......@@ -675,8 +662,8 @@ static int scsi_debug_mode_sense(unsigned char * cmd, int target,
return 0;
}
static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block,
int num, int * errstsp, Sdebug_dev_info * devip)
static int resp_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, int num,
struct sdebug_dev_info * devip)
{
unsigned char *buff = (unsigned char *) SCpnt->request_buffer;
int nbytes, sgcount;
......@@ -685,30 +672,17 @@ static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block,
unsigned long iflags;
if (upper_blk || (block + num > CAPACITY)) {
*errstsp = (COMMAND_COMPLETE << 8) |
(CHECK_CONDITION << 1);
mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x21, 0, 14);
return 1;
}
#if defined(SCSI_SETUP_LATENCY) || defined(SCSI_DATARATE)
{
int delay = SCSI_SETUP_LATENCY;
delay += SCpnt->request->nr_sectors * SCSI_DATARATE;
if (delay)
usleep(delay);
mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x21, 0, 14);
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
}
#endif
if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
(block >= OPT_MEDIUM_ERR_ADDR) &&
(block < (OPT_MEDIUM_ERR_ADDR + num))) {
*errstsp = (COMMAND_COMPLETE << 8) |
(CHECK_CONDITION << 1);
mk_sense_buffer(devip, 1, MEDIUM_ERROR, 0x11, 0, 14);
mk_sense_buffer(devip, MEDIUM_ERROR, 0x11, 0, 14);
/* claim unrecoverable read error */
return 1;
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
}
read_lock_irqsave(&sdebug_atomic_rw, iflags);
read_lock_irqsave(&atomic_rw, iflags);
sgcount = 0;
nbytes = bufflen;
/* printk(KERN_INFO "scsi_debug_read: block=%d, tot_bufflen=%d\n",
......@@ -716,46 +690,29 @@ static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block,
if (SCpnt->use_sg) {
sgcount = 0;
sgpnt = (struct scatterlist *) buff;
buff = sdebug_scatg2virt(&sgpnt[sgcount]);
buff = scatg2virt(&sgpnt[sgcount]);
bufflen = sgpnt[sgcount].length;
}
*errstsp = 0;
do {
memcpy(buff, fake_storep + (block * SECT_SIZE), bufflen);
#if 0
/* Simulate a disk change */
if (block == 0xfff0) {
sense_buffer[0] = 0x70;
sense_buffer[2] = UNIT_ATTENTION;
starts[0] += 10;
starts[1] += 10;
starts[2] += 10;
*errstsp = (COMMAND_COMPLETE << 8) |
(CHECK_CONDITION << 1);
read_unlock_irqrestore(&sdebug_atomic_rw, iflags);
return 1;
} /* End phony disk change code */
#endif
nbytes -= bufflen;
if (SCpnt->use_sg) {
block += bufflen >> POW2_SECT_SIZE;
sgcount++;
if (nbytes) {
buff = sdebug_scatg2virt(&sgpnt[sgcount]);
buff = scatg2virt(&sgpnt[sgcount]);
bufflen = sgpnt[sgcount].length;
}
}
else if (nbytes > 0)
printk(KERN_WARNING "sdebug_read: unexpected "
} else if (nbytes > 0)
printk(KERN_WARNING "scsi_debug:resp_read: unexpected "
"nbytes=%d\n", nbytes);
} while (nbytes);
read_unlock_irqrestore(&sdebug_atomic_rw, iflags);
read_unlock_irqrestore(&atomic_rw, iflags);
return 0;
}
static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block,
int num, int * errstsp, Sdebug_dev_info * devip)
static int resp_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, int num,
struct sdebug_dev_info * devip)
{
unsigned char *buff = (unsigned char *) SCpnt->request_buffer;
int nbytes, sgcount;
......@@ -764,22 +721,19 @@ static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block,
unsigned long iflags;
if (upper_blk || (block + num > CAPACITY)) {
*errstsp = (COMMAND_COMPLETE << 8) |
(CHECK_CONDITION << 1);
mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x21, 0, 14);
return 1;
mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x21, 0, 14);
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
}
write_lock_irqsave(&sdebug_atomic_rw, iflags);
write_lock_irqsave(&atomic_rw, iflags);
sgcount = 0;
nbytes = bufflen;
if (SCpnt->use_sg) {
sgcount = 0;
sgpnt = (struct scatterlist *) buff;
buff = sdebug_scatg2virt(&sgpnt[sgcount]);
buff = scatg2virt(&sgpnt[sgcount]);
bufflen = sgpnt[sgcount].length;
}
*errstsp = 0;
do {
memcpy(fake_storep + (block * SECT_SIZE), buff, bufflen);
......@@ -788,104 +742,64 @@ static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block,
block += bufflen >> POW2_SECT_SIZE;
sgcount++;
if (nbytes) {
buff = sdebug_scatg2virt(&sgpnt[sgcount]);
buff = scatg2virt(&sgpnt[sgcount]);
bufflen = sgpnt[sgcount].length;
}
}
else if (nbytes > 0)
printk(KERN_WARNING "sdebug_write: "
} else if (nbytes > 0)
printk(KERN_WARNING "scsi_debug:resp_write: "
"unexpected nbytes=%d\n", nbytes);
} while (nbytes);
write_unlock_irqrestore(&sdebug_atomic_rw, iflags);
write_unlock_irqrestore(&atomic_rw, iflags);
return 0;
}
/* A "high" level interrupt handler. This should be called once per jiffy
* to simulate a regular scsi disk. We use a timer to do this. */
static void scsi_debug_intr_handle(unsigned long indx)
static int resp_report_luns(unsigned char * cmd, unsigned char * buff,
int bufflen, struct sdebug_dev_info * devip)
{
Scsi_Cmnd *SCtmp;
void (*my_done) (Scsi_Cmnd *);
#if 0
del_timer(&timeout[indx]);
#endif
SCtmp = (Scsi_Cmnd *) SCint[indx];
my_done = do_done[indx];
do_done[indx] = NULL;
timeout[indx].function = NULL;
SCint[indx] = NULL;
unsigned int alloc_len;
int select_report = (int)cmd[2];
if (!my_done) {
printk(KERN_ERR "scsi_debug_intr_handle: Unexpected "
"interrupt\n");
return;
alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
if ((alloc_len < 16) || (select_report > 2)) {
mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14);
return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
}
if (bufflen > 3) {
memset(buff, 0, bufflen);
buff[3] = 8;
}
#if 0
printk(KERN_INFO "In intr_handle...");
printk(KERN_INFO "...done %d %x %d %d\n", i, my_done, to, jiffies);
printk(KERN_INFO "In intr_handle: %d %x %x\n", i, SCtmp, my_done);
#endif
my_done(SCtmp);
#if 0
printk(KERN_INFO "Called done.\n");
#endif
}
static int initialized = 0;
static int do_init(void)
{
int sz = STORE_SIZE;
starts[3] = CAPACITY;
fake_storep = vmalloc(sz);
if (NULL == fake_storep)
return 1;
memset(fake_storep, 0, sz);
sz = sizeof(done_fct_t) * SCSI_DEBUG_MAILBOXES;
do_done = kmalloc(sz, GFP_ATOMIC);
if (NULL == do_done)
goto out;
memset((void *)do_done, 0, sz);
sz = sizeof(struct timer_list) * SCSI_DEBUG_MAILBOXES;
timeout = kmalloc(sz, GFP_ATOMIC);
if (NULL == timeout)
goto out;
memset(timeout, 0, sz);
sz = sizeof(Scsi_Cmnd *) * SCSI_DEBUG_MAILBOXES;
SCint = kmalloc(sz, GFP_ATOMIC);
if (NULL == SCint)
goto out;
memset(SCint, 0, sz);
return 0;
out:
if (fake_storep)
vfree(fake_storep);
if (do_done)
kfree((void *)do_done);
if (timeout)
kfree(timeout);
if (SCint)
kfree(SCint);
return 1;
}
static void do_end(void)
/* When timer goes off this function is called. */
static void timer_intr_handler(unsigned long indx)
{
kfree(SCint);
kfree(timeout);
kfree((void *)do_done);
vfree(fake_storep);
struct sdebug_queued_cmd * sqcp;
unsigned int iflags;
if (indx >= SCSI_DEBUG_CANQUEUE) {
printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
"large\n");
return;
}
spin_lock_irqsave(&queued_arr_lock, iflags);
sqcp = &queued_arr[(int)indx];
if (! sqcp->in_use) {
printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
"interrupt\n");
spin_unlock_irqrestore(&queued_arr_lock, iflags);
return;
}
sqcp->in_use = 0;
if (sqcp->done_funct)
sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
sqcp->done_funct = NULL;
spin_unlock_irqrestore(&queued_arr_lock, iflags);
}
static int initialized = 0;
static int num_present = 0;
static const char * sdebug_proc_name = "scsi_debug";
static int scsi_debug_detect(Scsi_Host_Template * tpnt)
{
......@@ -895,28 +809,41 @@ static int scsi_debug_detect(Scsi_Host_Template * tpnt)
printk(KERN_INFO "scsi_debug: detect\n");
if (0 == initialized) {
++initialized;
sz = sizeof(Sdebug_dev_info) * scsi_debug_num_devs;
devInfop = kmalloc(sz, GFP_ATOMIC);
sz = sizeof(struct sdebug_dev_info) * scsi_debug_num_devs;
devInfop = vmalloc(sz);
if (NULL == devInfop) {
printk(KERN_ERR "scsi_debug_detect: out of "
"memory, 0.5\n");
return 0;
}
memset(devInfop, 0, sz);
if (do_init()) {
sz = STORE_SIZE;
fake_storep = vmalloc(sz);
if (NULL == fake_storep) {
printk(KERN_ERR "scsi_debug_detect: out of memory"
", 0\n");
return 0;
}
for (k = 0; k < NUM_SENSE_BUFFS; ++k)
sense_buffers[k][0] = 0x70;
for (k = 0; k < NR_HOSTS_PRESENT; k++) {
tpnt->proc_name = "scsi_debug"; /* In the loop??? */
scsi_register(tpnt, 0);
memset(fake_storep, 0, sz);
init_all_queued();
tpnt->proc_name = (char *)sdebug_proc_name;
for (num_present = 0, k = 0; k < NR_HOSTS_PRESENT; k++) {
if (NULL == scsi_register(tpnt, 0))
printk(KERN_ERR "scsi_debug_detect: "
"scsi_register failed k=%d\n", k);
else
++num_present;
}
return NR_HOSTS_PRESENT;
#ifdef DRIVERFS_SUPPORT
if (num_present) {
sdebug_driverfs_driver.name = (char *)sdebug_proc_name;
sdebug_driverfs_driver.bus = &scsi_driverfs_bus_type;
driver_register(&sdebug_driverfs_driver);
do_create_driverfs_files();
}
else {
#endif
return num_present;
} else {
printk(KERN_WARNING "scsi_debug_detect: called again\n");
return 0;
}
......@@ -929,51 +856,52 @@ static int scsi_debug_release(struct Scsi_Host * hpnt)
{
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk(KERN_INFO "scsi_debug: release\n");
stop_all_queued();
scsi_unregister(hpnt);
if (++num_releases != NR_HOSTS_PRESENT)
return 0;
do_end();
kfree(devInfop);
if (++num_releases == num_present) {
#ifdef DRIVERFS_SUPPORT
do_remove_driverfs_files();
remove_driver(&sdebug_driverfs_driver);
// driver_unregister(&sdebug_driverfs_driver);
#endif
vfree(fake_storep);
vfree(devInfop);
}
return 0;
}
static Sdebug_dev_info * devInfoReg(Scsi_Device * sdp)
static struct sdebug_dev_info * devInfoReg(Scsi_Device * sdp)
{
int k;
unsigned short host_no, id;
Sdebug_dev_info * devip;
struct sdebug_dev_info * devip;
host_no = sdp->host->host_no;
id = (unsigned short)sdp->id;
for (k = 0; k < scsi_debug_num_devs; ++k) {
devip = &devInfop[k];
if (devip->sdp && (host_no == devip->host_no) &&
(id == devip->id)) {
devip->sdp = sdp; /* overwrite previous sdp */
if (devip->sdp == sdp)
return devip;
}
for (k = 0; k < scsi_debug_num_devs; ++k) {
devip = &devInfop[k];
if (NULL == devip->sdp) {
devip->sdp = sdp;
devip->host_no = host_no;
devip->id = id;
devip->reset = 1;
devip->sb_index = 0;
memset(devip->sense_buff, 0, SDEBUG_SENSE_LEN);
devip->sense_buff[0] = 0x70;
return devip;
}
}
return NULL;
}
static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key,
static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
int asc, int asq, int inbandLen)
{
char * sbuff;
if ((index < 0) || (index >= NUM_SENSE_BUFFS))
return;
if (devip)
devip->sb_index = index;
sbuff = &sense_buffers[index][0];
memset(sbuff, 0, SENSE_BUFF_LEN);
unsigned char * sbuff;
sbuff = devip->sense_buff;
memset(sbuff, 0, SDEBUG_SENSE_LEN);
if (inbandLen > SDEBUG_SENSE_LEN)
inbandLen = SDEBUG_SENSE_LEN;
sbuff[0] = 0x70;
sbuff[2] = key;
sbuff[7] = (inbandLen > 7) ? (inbandLen - 8) : 0;
......@@ -985,30 +913,13 @@ static int scsi_debug_abort(Scsi_Cmnd * SCpnt)
{
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk(KERN_INFO "scsi_debug: abort\n");
#if 1
++num_aborts;
stop_queued_cmnd(SCpnt);
return SUCCESS;
#else
int j;
void (*my_done) (Scsi_Cmnd *);
unsigned long iflags;
SCpnt->result = SCpnt->abort_reason << 16;
for (j = 0; j < SCSI_DEBUG_MAILBOXES; j++) {
if (SCpnt == SCint[j]) {
my_done = do_done[j];
my_done(SCpnt);
spin_lock_irqsave(&mailbox_lock, iflags);
timeout[j] = 0;
SCint[j] = NULL;
do_done[j] = NULL;
spin_unlock_irqrestore(&mailbox_lock, iflags);
}
}
return SCSI_ABORT_SNOOZE;
#endif
}
static int scsi_debug_biosparam(Disk * disk, struct block_device *dev, int *info)
static int scsi_debug_biosparam(Disk * disk, struct block_device * bdev,
int *info)
{
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk(KERN_INFO "scsi_debug: biosparam\n");
......@@ -1067,12 +978,124 @@ static int scsi_debug_host_reset(Scsi_Cmnd * SCpnt)
++num_host_resets;
for (k = 0; k < scsi_debug_num_devs; ++k)
devInfop[k].reset = 1;
stop_all_queued();
return SUCCESS;
}
/* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
static int stop_queued_cmnd(struct scsi_cmnd * cmnd)
{
unsigned long iflags;
int k;
struct sdebug_queued_cmd * sqcp;
spin_lock_irqsave(&queued_arr_lock, iflags);
for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
sqcp = &queued_arr[k];
if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
del_timer_sync(&sqcp->cmnd_timer);
sqcp->in_use = 0;
sqcp->a_cmnd = NULL;
break;
}
}
spin_unlock_irqrestore(&queued_arr_lock, iflags);
return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0;
}
/* Deletes (stops) timers of all queued commands */
static void stop_all_queued(void)
{
unsigned long iflags;
int k;
struct sdebug_queued_cmd * sqcp;
spin_lock_irqsave(&queued_arr_lock, iflags);
for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
sqcp = &queued_arr[k];
if (sqcp->in_use && sqcp->a_cmnd) {
del_timer_sync(&sqcp->cmnd_timer);
sqcp->in_use = 0;
sqcp->a_cmnd = NULL;
}
}
spin_unlock_irqrestore(&queued_arr_lock, iflags);
}
/* Initializes timers in queued array */
static void init_all_queued(void)
{
unsigned long iflags;
int k;
struct sdebug_queued_cmd * sqcp;
spin_lock_irqsave(&queued_arr_lock, iflags);
for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
sqcp = &queued_arr[k];
init_timer(&sqcp->cmnd_timer);
sqcp->in_use = 0;
sqcp->a_cmnd = NULL;
}
spin_unlock_irqrestore(&queued_arr_lock, iflags);
}
static int schedule_resp(struct scsi_cmnd * cmnd,
struct sdebug_dev_info * devip,
done_funct_t done, int scsi_result, int delta_jiff)
{
int k, num;
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
printk(KERN_INFO "scsi_debug: cmd ");
for (k = 0, num = cmnd->cmd_len; k < num; ++k)
printk("%02x ", (int)cmnd->cmnd[k]);
printk("result=0x%x\n", scsi_result);
}
if (cmnd && devip) {
/* simulate autosense by this driver */
if (CHECK_CONDITION == status_byte(scsi_result))
memcpy(cmnd->sense_buffer, devip->sense_buff,
(SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
}
if (delta_jiff <= 0) {
if (cmnd)
cmnd->result = scsi_result;
if (done)
done(cmnd);
return 0;
} else {
unsigned long iflags;
int k;
struct sdebug_queued_cmd * sqcp = NULL;
spin_lock_irqsave(&queued_arr_lock, iflags);
for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
sqcp = &queued_arr[k];
if (! sqcp->in_use)
break;
}
if (k >= SCSI_DEBUG_CANQUEUE) {
spin_unlock_irqrestore(&queued_arr_lock, iflags);
printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
return 1; /* report busy to mid level */
}
sqcp->in_use = 1;
sqcp->a_cmnd = cmnd;
sqcp->scsi_result = scsi_result;
sqcp->done_funct = done;
sqcp->cmnd_timer.function = timer_intr_handler;
sqcp->cmnd_timer.data = k;
sqcp->cmnd_timer.expires = jiffies + delta_jiff;
add_timer(&sqcp->cmnd_timer);
spin_unlock_irqrestore(&queued_arr_lock, iflags);
return 0;
}
}
#ifndef MODULE
static int __init scsi_debug_num_devs_setup(char *str)
static int __init num_devs_setup(char *str)
{
int tmp;
......@@ -1086,10 +1109,9 @@ static int __init scsi_debug_num_devs_setup(char *str)
return 0;
}
}
__setup("scsi_debug_num_devs=", num_devs_setup);
__setup("scsi_debug_num_devs=", scsi_debug_num_devs_setup);
static int __init scsi_debug_dev_size_mb_setup(char *str)
static int __init dev_size_mb_setup(char *str)
{
int tmp;
......@@ -1104,10 +1126,9 @@ static int __init scsi_debug_dev_size_mb_setup(char *str)
return 0;
}
}
__setup("scsi_debug_dev_size_mb=", dev_size_mb_setup);
__setup("scsi_debug_dev_size_mb=", scsi_debug_dev_size_mb_setup);
static int __init scsi_debug_opts_setup(char *str)
static int __init opts_setup(char *str)
{
int tmp;
......@@ -1122,8 +1143,41 @@ static int __init scsi_debug_opts_setup(char *str)
return 0;
}
}
__setup("scsi_debug_opts=", opts_setup);
static int __init every_nth_setup(char *str)
{
int tmp;
if (get_option(&str, &tmp) == 1) {
if (tmp > 0)
scsi_debug_every_nth = tmp;
return 1;
} else {
printk(KERN_INFO "scsi_debug_every_nth: usage "
"scsi_debug_every_nth=<n>\n"
" timeout every nth command (when ...)\n");
return 0;
}
}
__setup("scsi_debug_every_nth=", every_nth_setup);
static int __init delay_setup(char *str)
{
int tmp;
if (get_option(&str, &tmp) == 1) {
scsi_debug_delay = tmp;
return 1;
} else {
printk(KERN_INFO "scsi_debug_delay: usage "
"scsi_debug_delay=<n>\n"
" delay response <n> jiffies\n");
return 0;
}
}
__setup("scsi_debug_delay=", delay_setup);
__setup("scsi_debug_opts=", scsi_debug_opts_setup);
#endif
MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
......@@ -1134,10 +1188,11 @@ MODULE_PARM(scsi_debug_dev_size_mb, "i");
MODULE_PARM_DESC(scsi_debug_dev_size_mb, "size in MB of ram shared by devs");
MODULE_PARM(scsi_debug_opts, "i");
MODULE_PARM_DESC(scsi_debug_opts, "1->noise, 2->medium_error, 4->...");
#ifdef MODULE_LICENSE
MODULE_PARM(scsi_debug_every_nth, "i");
MODULE_PARM_DESC(scsi_debug_every_nth, "timeout every nth command(def=100)");
MODULE_PARM(scsi_debug_delay, "i");
MODULE_PARM_DESC(scsi_debug_delay, "# of jiffies to delay response(def=1)");
MODULE_LICENSE("GPL");
#endif
static char sdebug_info[256];
......@@ -1172,17 +1227,21 @@ static int scsi_debug_proc_info(char *buffer, char **start, off_t offset,
if (1 != sscanf(arr, "%d", &pos))
return -EINVAL;
scsi_debug_opts = pos;
if (SCSI_DEBUG_OPT_EVERY_NTH & scsi_debug_opts)
scsi_debug_cmnd_count = 0;
return length;
}
begin = 0;
pos = len = sprintf(buffer, "scsi_debug adapter driver, %s\n"
"num_devs=%d, shared (ram) size=%d MB, opts=0x%x\n"
"sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
"number of aborts=%d, device_reset=%d, bus_resets=%d, "
"num_devs=%d, shared (ram) size=%d MB, opts=0x%x, "
"every_nth=%d(curr:%d)\n"
"sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d, "
"delay=%d\nnumber of aborts=%d, device_reset=%d, bus_resets=%d, "
"host_resets=%d\n",
scsi_debug_version_str, scsi_debug_num_devs,
scsi_debug_dev_size_mb, scsi_debug_opts, SECT_SIZE,
N_CYLINDER, N_HEAD, N_SECTOR,
scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
scsi_debug_cmnd_count,
SECT_SIZE, N_CYLINDER, N_HEAD, N_SECTOR, scsi_debug_delay,
num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
if (pos < offset) {
len = 0;
......@@ -1196,8 +1255,86 @@ static int scsi_debug_proc_info(char *buffer, char **start, off_t offset,
return (len);
}
/* Eventually this will go into an include file, but this will be later */
static Scsi_Host_Template driver_template = SCSI_DEBUG_TEMPLATE;
#ifdef DRIVERFS_SUPPORT
static ssize_t sdebug_delay_read(struct device_driver * ddp, char * buf,
size_t count, loff_t off)
{
return off ? 0 : snprintf(buf, count, "%d\n", scsi_debug_delay);
}
#include "scsi_module.c"
static ssize_t sdebug_delay_write(struct device_driver * ddp,
const char * buf, size_t count, loff_t off)
{
int delay;
char work[20];
if (off)
return 0;
if (1 == sscanf(buf, "%10s", work)) {
if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
scsi_debug_delay = delay;
return count;
}
}
return -EINVAL;
}
DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_read,
sdebug_delay_write)
static ssize_t sdebug_opts_read(struct device_driver * ddp, char * buf,
size_t count, loff_t off)
{
return off ? 0 : snprintf(buf, count, "0x%x\n", scsi_debug_opts);
}
static ssize_t sdebug_opts_write(struct device_driver * ddp,
const char * buf, size_t count, loff_t off)
{
int opts;
char work[20];
if (off)
return 0;
if (1 == sscanf(buf, "%10s", work)) {
if (0 == strnicmp(work,"0x", 2)) {
if (1 == sscanf(&work[2], "%x", &opts))
goto opts_done;
} else {
if (1 == sscanf(work, "%d", &opts))
goto opts_done;
}
}
return -EINVAL;
opts_done:
scsi_debug_opts = opts;
return count;
}
DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_read,
sdebug_opts_write)
static ssize_t sdebug_num_devs_read(struct device_driver * ddp, char * buf,
size_t count, loff_t off)
{
return off ? 0 : snprintf(buf, count, "%d\n", scsi_debug_num_devs);
}
DRIVER_ATTR(num_devs, S_IRUGO, sdebug_num_devs_read, NULL)
static void do_create_driverfs_files()
{
driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_devs);
}
static void do_remove_driverfs_files()
{
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_devs);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
}
#endif
#include "scsi_module.c"
#ifndef _SCSI_DEBUG_H
#include <linux/types.h>
#include <linux/kdev_t.h>
static int scsi_debug_detect(Scsi_Host_Template *);
static int scsi_debug_release(struct Scsi_Host *);
/* static int scsi_debug_command(Scsi_Cmnd *); */
static int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
static int scsi_debug_abort(Scsi_Cmnd *);
static int scsi_debug_ioctl(Scsi_Device *, int, void *);
static int scsi_debug_biosparam(Disk *, struct block_device *, int[]);
static int scsi_debug_abort(Scsi_Cmnd *);
static int scsi_debug_bus_reset(Scsi_Cmnd *);
static int scsi_debug_device_reset(Scsi_Cmnd *);
static int scsi_debug_host_reset(Scsi_Cmnd *);
......@@ -20,29 +23,29 @@ static const char * scsi_debug_info(struct Scsi_Host *);
/*
* This driver is written for the lk 2.5 series
*/
#define SCSI_DEBUG_CANQUEUE 255
#define SCSI_DEBUG_CANQUEUE 255 /* needs to be >= 1 */
#define SCSI_DEBUG_MAX_CMD_LEN 16
#define SCSI_DEBUG_TEMPLATE \
{proc_info: scsi_debug_proc_info, \
name: "SCSI DEBUG", \
info: scsi_debug_info, \
detect: scsi_debug_detect, \
release: scsi_debug_release, \
ioctl: scsi_debug_ioctl, \
queuecommand: scsi_debug_queuecommand, \
eh_abort_handler: scsi_debug_abort, \
eh_bus_reset_handler: scsi_debug_bus_reset, \
eh_device_reset_handler: scsi_debug_device_reset, \
eh_host_reset_handler: scsi_debug_host_reset, \
bios_param: scsi_debug_biosparam, \
can_queue: SCSI_DEBUG_CANQUEUE, \
this_id: 7, \
sg_tablesize: 64, \
cmd_per_lun: 3, \
unchecked_isa_dma: 0, \
use_clustering: ENABLE_CLUSTERING, \
}
static Scsi_Host_Template driver_template = {
.proc_info = scsi_debug_proc_info,
.name = "SCSI DEBUG",
.info = scsi_debug_info,
.detect = scsi_debug_detect,
.release = scsi_debug_release,
.ioctl = scsi_debug_ioctl,
.queuecommand = scsi_debug_queuecommand,
.eh_abort_handler = scsi_debug_abort,
.eh_bus_reset_handler = scsi_debug_bus_reset,
.eh_device_reset_handler = scsi_debug_device_reset,
.eh_host_reset_handler = scsi_debug_host_reset,
.bios_param = scsi_debug_biosparam,
.can_queue = SCSI_DEBUG_CANQUEUE,
.this_id = 7,
.sg_tablesize = 64,
.cmd_per_lun = 3,
.unchecked_isa_dma = 0,
.use_clustering = ENABLE_CLUSTERING,
}; /* the name 'driver_template' is used by scsi_module.c */
#endif
......@@ -34,12 +34,15 @@ scsi_module.c is normally included at the end of a lower
level driver. For it to work a declaration like this is needed before
it is included:
static Scsi_Host_Template driver_template = DRIVER_TEMPLATE;
/* DRIVER_TEMPLATE should contain pointers to supported interface
functions. Scsi_Host_Template is defined in hosts.h */
#include "scsi_module.c"
In this case "DRIVER_TEMPLATE" is defined to be a structure initializer
that is placed in the driver header file by convention. It contains
pointers to supported interface functions and other values.
Scsi_Host_Template is defined in hosts.h .
The scsi_module.c assumes the name "driver_template" is appropriately
defined. It contains 2 functions:
defined. scsi_module.c contains 2 functions:
1) init_this_scsi_driver() called during builtin and module driver
initialization: invokes mid level's scsi_register_host()
2) exit_this_scsi_driver() called during closedown: invokes
......@@ -68,7 +71,7 @@ The interface functions are listed below in alphabetical order.
/**
* bios_param - fetch head, sector, cylinder info for a disk
* @sdkp: pointer to disk structure (defined in sd.h)
* @dev: corresponds to dev_t of device file name (e.g. /dev/sdb)
* @bdev: pointer to block device context (defined in fs.h)
* @params: three element array to place output:
* params[0] number of heads
* params[1] number of sectors
......@@ -267,6 +270,8 @@ The interface functions are listed below in alphabetical order.
* unsupported ioctl() 'cmd' numbers should return -ENOTTY.
* However the mid level returns -EINVAL for unrecognized 'cmd'
* numbers when this function is not supplied by the driver.
* Unfortunately some applications expect -EINVAL and react badly
* when -ENOTTY is returned; stick with -EINVAL.
**/
int ioctl(Scsi_Device *sdp, int cmd, void *arg);
......@@ -304,7 +309,10 @@ int proc_info(char * buffer, char ** start, off_t offset,
* @scp: pointer to scsi command object
* @done: function pointer to be invoked on completion
*
* Returns 1 if the adapter is busy, else returns 0.
* Returns 1 if the adapter (host) is busy, else returns 0. One
* reason for an adapter to be busy is that the number
* of outstanding queued commands is already equal to
* Scsi_Host::can_queue .
*
* Required: if Scsi_Host::can_queue is ever non-zero
* then this function is required.
......@@ -324,6 +332,9 @@ int proc_info(char * buffer, char ** start, off_t offset,
* return value should be generated by this function. However, in
* this case, it should be placed in scp->result before this function
* returns.
* If a status of CHECK CONDITION is placed in "result" when the
* 'done' callback is invoked, then the lower level driver should
* perform autosense and fill in the Scsi_Cmnd::sense_buffer array.
**/
int queuecommand(Scsi_Cmnd * scp, void (*done)(Scsi_Cmnd *));
......@@ -505,6 +516,28 @@ lock of equal granularity (i.e. per host). Using finer grain locks
(e.g. per scsi device) may be possible by juggling locks in
queuecommand().
Autosense
=========
Autosense (or auto-sense) is defined in the SAM-2 document as "the
automatic return of sense data to the application client coincident
with the completion of a SCSI command" when a status of CHECK CONDITION
occurs. Lower level drivers should perform autosense. This should be
done when the lower level driver detects a CHECK CONDITION status by either:
a) instructing the SCSI protocol (e.g. SCSI Parallel Interface (SPI))
to perform an extra data in phase on such responses
b) or, the lower level driver issuing a REQUEST SENSE command itself
Either way, the mid level decides whether the lower level driver has
performed autosense by checking Scsi_Cmnd::sense_buffer[0] . If this
byte has an upper nibble of 7 then autosense is assumed to have taken
place. If it has another value (and this byte is initialized to 0 before
each command) then the mid level will issue a REQUEST SENSE command.
In the presence of queued commands the "nexus" that maintains sense
buffer data from the command that failed until a following REQUEST SENSE
may get out of synchronization. This is why it is best for the lower
level driver to perform autosense.
Changes since lk 2.4 series
===========================
......@@ -514,6 +547,7 @@ per scsi host.
The older error handling mechanism has been removed. This means the
lower level interface functions abort() and reset() have been removed.
The Scsi_Host_Template::use_new_eh_code flag has been removed.
In the 2.4 series the scsi subsystem configuration descriptions were
aggregated with the configuration descriptions from all other Linux
......@@ -532,4 +566,4 @@ The following people have contributed to this document:
Douglas Gilbert
dgilbert@interlog.com
27th April 2002
13th August 2002
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -11,9 +11,13 @@ Original driver (sg.h):
Version 2 and 3 extensions to driver:
* Copyright (C) 1998 - 2002 Douglas Gilbert
Version: 3.5.26 (20020708)
Version: 3.5.27 (20020812)
This version is for 2.5 series kernels.
Changes since 3.5.26 (20020708)
- re-add direct IO using Kai Makisara's work
- re-tab to 8, start using C99-isms
- simplify memory management
Changes since 3.5.25 (20020504)
- driverfs additions
- copy_to/from_user() fixes [William Stinson]
......
......@@ -129,6 +129,7 @@ EXPORT_SYMBOL(highmem_start_page);
EXPORT_SYMBOL(kmap_prot);
EXPORT_SYMBOL(kmap_pte);
#endif
EXPORT_SYMBOL(get_user_pages);
/* filesystem internal functions */
EXPORT_SYMBOL(def_blk_fops);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment