Hi,
I'm working on an iSCSI driver which gets the SCSI requests from the
mid-layer SCSI, transports them over the network and passes the
responses received over the network back to the mid-layer. After the
initial coding of the driver, I tested it using the generic SCSI
interface (sg) and everything seemed to work correctly. When I tried to
create a filesystem on a remote disk, the initiator's kernel panicked
(Kernel panic : scsi_free : Trying to free unused memory) while the
mke2fs was executing. While I was trying to isolate the problem, I
observed that the problem was occuring at the 2nd write request
triggered by mke2fs. So I decided to emulate the responses locally from
the initiators driver. I have written some code to generate dummy
responses to the TEST UNIT READY, INQUIRY, READ CAPACITY commands. In
the case of READ_6, READ_10, WRITE_6 and WRITE_10 commands I decided to
process the requests for block 0 and block 1 ( to be able to store the
partition table written by fdisk utility ) and ignore other requests
(i.e, write requests are returned with result = 0 but nothing is
recorded anywhere and no read requests other than for blocks 0 and 1
arrive before the problem occurs ). I disabled the scatter-gather to
simplify things. By setting can_queue field of the Scsi_Host_Template
structure to 0 and 1 I tried to use the command and queue_command
functions respectively for interfacing . In order to call the done
function after the queue_command function returns, I set a timer and
call the done function from within the timer handler function. Inside
the handler io_request_lock is acquired before the done function is
called, and released after done returns. Still the the problem remains
the same. I am attaching the part of code which includes the definitions
of the functions which are passed to the mid-layer SCSI inside the
Scsi_Host_Template structure.
I am probably missing some basic concept about the interface. ( When I
was looking at aha1740.c I read some comment about the possibility of
interrupts being enabled by printk, and I commented out all the
'printk's from the code ( just in the case that there could be an
assumption by the mid-layer SCSI code about interrupts being disabled
during the queue_command function ) but it did not help.
The kernel version is 2.2.14-6.1.1
Thanks in advance for your help.
Omer TUNALI
#include "iSCSI_mid_level.h"
#include <linux/wrapper.h>
struct proc_dir_entry proc_scsi_iSCSI = {
PROC_SCSI_SCSI_DEBUG, sizeof("iSCSI")-1, "iSCSI", // look at enum
scsi_directory_inos at <linux/proc_fs.h>
S_IFDIR | S_IRUGO | S_IXUGO, 2
};
static int iSCSI_detect(Scsi_Host_Template *);
static int iSCSI_command(Scsi_Cmnd *);
static int iSCSI_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
static int iSCSI_abort(Scsi_Cmnd * SCpnt);
static int iSCSI_bus_reset(Scsi_Cmnd * SCpnt);
static int iSCSI_dev_reset(Scsi_Cmnd * SCpnt);
static int iSCSI_host_reset(Scsi_Cmnd * SCpnt);
static int iSCSI_old_abort(Scsi_Cmnd * SCpnt);
static int iSCSI_old_reset(Scsi_Cmnd *, unsigned int);
static int iSCSI_biosparam(Disk *, kdev_t, int*);
static void poll_timer_handler(unsigned long);
static void emulate_response( Scsi_Cmnd* );
#define iSCSI_CAN_QUEUE 1
#define SBLOCK_SIZE 0x200
Scsi_Host_Template iSCSI_driver_template = { name : "iSCSI",
detect : iSCSI_detect,
command : iSCSI_command,
queuecommand : iSCSI_queuecommand,
abort : iSCSI_old_abort,
reset : iSCSI_old_reset,
eh_abort_handler : iSCSI_abort,
eh_device_reset_handler :
iSCSI_dev_reset,
eh_bus_reset_handler : iSCSI_bus_reset,
eh_host_reset_handler :
iSCSI_host_reset,
bios_param : iSCSI_biosparam,
can_queue : iSCSI_CAN_QUEUE,
this_id : -1,
sg_tablesize : SG_NONE, //disable
scatter gather
cmd_per_lun : 1,
unchecked_isa_dma : 1,
use_clustering : ENABLE_CLUSTERING,
use_new_eh_code : 1};
static int iSCSI_detect( Scsi_Host_Template* tpnt ) {
printk("iSCSI_detect\n");
tpnt->proc_dir = &proc_scsi_iSCSI;
scsi_register(tpnt,0);
return 1;
}
struct wait_queue* TimerWaitQ = NULL;
static int iSCSI_command( Scsi_Cmnd* SCpnt ) {
if (SCpnt->target || SCpnt->lun) {
SCpnt->result = DID_TIME_OUT << 16;
return 1;
} else {
emulate_response( SCpnt );
}
return 0;
}
Scsi_Cmnd* Global_SCpnt = NULL;
void (*Global_Done)(Scsi_Cmnd *) = NULL;
static struct timer_list poll_timeout = { function : poll_timer_handler,
data : 0 };
static void poll_timer_handler( unsigned long idx ) {
unsigned long flags;
spin_lock_irqsave( &io_request_lock, flags );
Global_Done( Global_SCpnt );
printk( "Current pid : %x poll_timer_called DONE...\n", current->pid );
spin_unlock_irqrestore( &io_request_lock, flags );
Global_Done = NULL;
}
static int iSCSI_queuecommand( Scsi_Cmnd* SCpnt, void (*done)(Scsi_Cmnd *) ) {
printk("iSCSI QUEUE COMMAND\n");
if (SCpnt->target || SCpnt->lun) {
SCpnt->result = DID_TIME_OUT << 16;
} else {
emulate_response( SCpnt );
}
if ( Global_Done ) { // command can not be queued. Queue size is 1.
printk("Core SCSI Command Rejected\n");
return 1;
}
Global_SCpnt = SCpnt;
Global_Done = done;
printk("Core SCSI Command Queued\n");
poll_timeout.expires = jiffies + 2;
add_timer(&poll_timeout);
printk( "queuecommand is returning...\n" );
return 0;
}
static void emulate_response( Scsi_Cmnd* SCpnt ) {
static char boot_sector[2*SBLOCK_SIZE];
unsigned char* cmd = (unsigned char*) SCpnt->cmnd;
unsigned char* dum_buf;
unsigned int doffset, dsize;
int i;
printk("Target : %02x Channel : %02x LUN :%02x\n", SCpnt->target,
SCpnt->channel, SCpnt->lun );
printk("Current PID : %02x\n", current->pid);
printk("Request Buffer Len : %x Ptr : %p\nData Buffer Len : %x Ptr : %p\n",
SCpnt->request_bufflen, SCpnt->request_buffer, SCpnt->bufflen ,SCpnt->buffer
);
printk("use_sg : %02x\n", SCpnt->use_sg);
printk("Command :\n");
for (i = 0; i < SCpnt->cmd_len; i++) printk("%02x ", SCpnt->cmnd[i] );
printk("\n");
SCpnt->result = 0;
if ( ( *cmd == WRITE_6 ) || ( *cmd == READ_6 ) ) {
doffset = cmd[1] << 16;
doffset += cmd[2] << 8;
doffset += cmd[3];
doffset *= SBLOCK_SIZE;
dsize = cmd[4] * SBLOCK_SIZE;
} else {
doffset = cmd[2] << 24;
doffset += cmd[3] << 16;
doffset += cmd[4] << 8;
doffset += cmd[5];
doffset *= SBLOCK_SIZE;
dsize = cmd[7] << 8;
dsize += cmd[8];
dsize *= SBLOCK_SIZE;
}
switch (*cmd) {
case TEST_UNIT_READY :
break;
case INQUIRY :
//Dummy values for INQUIRY
memset(SCpnt->request_buffer,0,SCpnt->request_bufflen);
dum_buf = (char *) SCpnt->request_buffer;
dum_buf[0] = 0x00;
dum_buf[1] = 0x00;
dum_buf[2] = 0x02;//Big-Endian?
dum_buf[3] = 0x02;
dum_buf[4] = 0x9F;
dum_buf[5] = 0x00;
dum_buf[6] = 0x00;
dum_buf[7] = 0x3A;
strcpy(&(dum_buf[8]),"XYZW");
strcpy(&(dum_buf[16]),"iSCSI drive");
strcpy(&(dum_buf[32]),"N.1");
strcpy(&(dum_buf[36]),"01/25/01");
strcpy(&(dum_buf[96]), "(c) Copyright 2001");
break;
case READ_CAPACITY :
//Dummy values for netscsi drive capacity
dum_buf = (char *) SCpnt->request_buffer;
//Capacity(in blocks)
dum_buf[0] = 0x02;
dum_buf[1] = 0x23;
dum_buf[2] = 0x31;
dum_buf[3] = 0xad;
//Block Size
dum_buf[4] = 0x00;
dum_buf[5] = 0x00;
dum_buf[6] = SBLOCK_SIZE / 0x100;
dum_buf[7] = SBLOCK_SIZE % 0x100;
break;
case WRITE_6 :
case WRITE_10 :
if ( doffset == 0 ) {
printk("WRITE request_bufflen = %d\n", SCpnt->request_bufflen );
if ( SCpnt->request_bufflen > 2*SBLOCK_SIZE )
panic( "request_bufflen for bootsector is greater than expected\n");
memcpy( boot_sector, SCpnt->request_buffer, SCpnt->request_bufflen );
}
break;
case READ_6 :
case READ_10 :
if ( doffset == 0 ) {
printk("READ request_bufflen = %d\n", SCpnt->request_bufflen );
if ( SCpnt->request_bufflen > 2*SBLOCK_SIZE )
panic( "request_bufflen for bootsector is greater than expected\n");
memcpy( SCpnt->request_buffer, boot_sector, SCpnt->request_bufflen );
}
break;
default :
panic( "Unemulated SCSI command encountered\n" );
break;
}
}
static int iSCSI_abort( Scsi_Cmnd* SCpnt ) {
printk("iSCSI_abort\n");
return 1;
}
static int iSCSI_bus_reset( Scsi_Cmnd* SCpnt ) {
printk("iSCSI_bus_reset\n");
return 1;
}
static int iSCSI_dev_reset( Scsi_Cmnd* SCpnt ) {
printk("iSCSI_dev_reset\n");
return 1;
}
static int iSCSI_host_reset( Scsi_Cmnd* SCpnt ) {
printk("iSCSI_host_reset\n");
return 1;
}
static int iSCSI_old_abort( Scsi_Cmnd* SCpnt ) {
printk("iSCSI_old_abort\n");
return 1;
}
static int iSCSI_old_reset(Scsi_Cmnd *i, unsigned int y) {
printk("iSCSI_old_reset\n");
return 1;
}
static int iSCSI_biosparam( Disk* y, kdev_t u, int geom[] ) {
printk("iSCSI_biosparam\n");
geom[0] = 255;
geom[1] = 63;
geom[2] = 2232;
return 0;
}