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;
}


Reply via email to