--- linux/drivers/scsi/scsi_debug.h Wed Jul 24 23:57:35 2002 +++ linux/drivers/scsi/scsi_debug.h162 Wed Aug 14 17:51:19 2002 @@ -1,48 +1,44 @@ #ifndef _SCSI_DEBUG_H -#include - 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 *); static int scsi_debug_proc_info(char *, char **, off_t, int, int, int); static const char * scsi_debug_info(struct Scsi_Host *); -#ifndef NULL -#define NULL 0 -#endif - -/* - * 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 +/* struct Scsi_Host_Template initializer used to initialize 'driver_template' + in scsi_debug.c */ #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, \ +{ \ + .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, \ } #endif --- linux/drivers/scsi/scsi_debug.c Wed Jul 24 23:57:35 2002 +++ linux/drivers/scsi/scsi_debug.c162 Wed Aug 14 18:06:33 2002 @@ -1,4 +1,4 @@ -/* $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 @@ -46,7 +47,9 @@ #include #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 (20020814)"; #ifndef SCSI_CMD_READ_16 #define SCSI_CMD_READ_16 0x88 @@ -54,29 +57,36 @@ #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 +97,77 @@ #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 { +static Scsi_Host_Template driver_template = SCSI_DEBUG_TEMPLATE; + +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" - -typedef void (*done_fct_t) (Scsi_Cmnd *); - -static volatile done_fct_t * do_done = 0; +static spinlock_t queued_arr_lock = SPIN_LOCK_UNLOCKED; +static rwlock_t atomic_rw = RW_LOCK_UNLOCKED; -static struct Scsi_Host * SHpnt = NULL; +static struct device_driver sdebug_driverfs_driver; -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, - 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, +/* 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, + 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); +static void do_create_driverfs_files(void); +static void do_remove_driverfs_files(void); -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 +179,47 @@ } 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 (NULL == buff) { + printk(KERN_WARNING "scsi_debug:qc: buff was NULL??\n"); + buff = broken_buff; /* just point at dummy */ + bufflen = SDEBUG_SENSE_LEN; + } - /* - * 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(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 > 7) || (SCpnt->lun != 0)) { - SCpnt->result = DID_NO_CONNECT << 16; - done(SCpnt); - return 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 +227,93 @@ #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; + memset(buff, 0, bufflen); 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; + memset(buff, 0, bufflen); 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); - 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; + 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[6] = (SECT_SIZE_PER(target) >> 8) & 0xff; + buff[7] = SECT_SIZE_PER(target) & 0xff; + } 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 +322,31 @@ (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)) - break; - SCpnt->result = 0; -/* calls bottom half in upper layers before return from scsi_do_...() */ - (done) (SCpnt); - return 0; + errsts = resp_read(SCpnt, upper_blk, block, num, devip); + break; + case REPORT_LUNS: + errsts = resp_report_luns(cmd, buff, bufflen, devip); + break; 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 +355,38 @@ (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)) - break; - SCpnt->result = 0; -/* calls bottom half in upper layers before return from scsi_do_...() */ - (done) (SCpnt); - return 0; + errsts = resp_write(SCpnt, upper_blk, block, num, devip); + break; 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); - break; - } - - spin_lock_irqsave(&mailbox_lock, iflags); - for (i = 0; i < SCSI_DEBUG_MAILBOXES; i++) { - if (timeout[i].function == NULL) + if ((errsts = check_reset(SCpnt, devip))) break; + 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 +394,101 @@ 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 +497,18 @@ /* 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; } /* <> */ -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 +519,7 @@ 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 +530,25 @@ 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 +559,7 @@ 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 +573,9 @@ #define SDEBUG_MAX_MSENSE_SZ 256 -static int scsi_debug_mode_sense(unsigned char * cmd, int target, - unsigned char * buff, int bufflen, - Sdebug_dev_info * devip) +static int resp_mode_sense(unsigned char * cmd, int target, + unsigned char * buff, int bufflen, + struct sdebug_dev_info * devip) { unsigned char dbd; int pcontrol, pcode; @@ -622,15 +601,14 @@ 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 +616,34 @@ 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 +657,8 @@ 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 +667,17 @@ 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 +685,29 @@ 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 +716,19 @@ 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 +737,64 @@ 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 +804,39 @@ 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; - } - else { + 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(); + } + return num_present; + } else { printk(KERN_WARNING "scsi_debug_detect: called again\n"); return 0; } @@ -929,51 +849,50 @@ { 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) { + do_remove_driverfs_files(); + remove_driver(&sdebug_driverfs_driver); + // driver_unregister(&sdebug_driverfs_driver); + 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 +904,13 @@ { 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 +969,124 @@ ++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 +1100,9 @@ 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 +1117,9 @@ 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 +1134,41 @@ 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" + " 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" + " delay response 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 +1179,11 @@ 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 +1218,21 @@ 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 +1246,124 @@ return (len); } -/* Eventually this will go into an include file, but this will be later */ -static Scsi_Host_Template driver_template = SCSI_DEBUG_TEMPLATE; +/* Following functions support driverfs driver parameters found in the + /bus/scsi/drivers/scsi_debug directory. */ -#include "scsi_module.c" +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); +} + +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 ssize_t sdebug_dev_size_mb_read(struct device_driver * ddp, char * buf, + size_t count, loff_t off) +{ + return off ? 0 : snprintf(buf, count, "%d\n", scsi_debug_dev_size_mb); +} + +DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_read, NULL) + +static ssize_t sdebug_every_nth_read(struct device_driver * ddp, char * buf, + size_t count, loff_t off) +{ + return off ? 0 : snprintf(buf, count, "%d\n", scsi_debug_every_nth); +} + +static ssize_t sdebug_every_nth_write(struct device_driver * ddp, + const char * buf, size_t count, loff_t off) +{ + int every_nth; + char work[20]; + if (off) + return 0; + if (1 == sscanf(buf, "%10s", work)) { + if ((1 == sscanf(work, "%d", &every_nth)) && (every_nth >= 0)) { + scsi_debug_every_nth = every_nth; + return count; + } + } + return -EINVAL; +} + +DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_read, + sdebug_every_nth_write) + +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); + driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb); + driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth); +} + +static void do_remove_driverfs_files() +{ + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth); + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb); + 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); +} + +/* scsi_module.c assumes 'driver_template' is defined */ +#include "scsi_module.c"