diff options
Diffstat (limited to 'drivers/scsi/scsi.c')
-rw-r--r-- | drivers/scsi/scsi.c | 429 |
1 files changed, 420 insertions, 9 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 2d86cbf95..a6e2c14d9 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -224,6 +224,8 @@ void scsi_wait_cmd (Scsi_Cmnd * SCpnt, const void *cmnd , { DECLARE_MUTEX_LOCKED(sem); + if (buffer != NULL && SCpnt->sc_data_direction == SCSI_DATA_NONE) + BUG(); SCpnt->request.sem = &sem; SCpnt->request.rq_status = RQ_SCSI_BUSY; scsi_do_cmd (SCpnt, (void *) cmnd, @@ -248,6 +250,73 @@ static spinlock_t device_request_lock = SPIN_LOCK_UNLOCKED; static spinlock_t scsi_bhqueue_lock = SPIN_LOCK_UNLOCKED; /* + * Function: scsi_allocate_request + * + * Purpose: Allocate a request descriptor. + * + * Arguments: device - device for which we want a request + * + * Lock status: No locks assumed to be held. This function is SMP-safe. + * + * Returns: Pointer to request block. + * + * Notes: With the new queueing code, it becomes important + * to track the difference between a command and a + * request. A request is a pending item in the queue that + * has not yet reached the top of the queue. + */ + +Scsi_Request *scsi_allocate_request(Scsi_Device * device) +{ + Scsi_Request *SRpnt = NULL; + + if (!device) + panic("No device passed to scsi_allocate_request().\n"); + + SRpnt = (Scsi_Request *) kmalloc(sizeof(Scsi_Request), GFP_ATOMIC); + if( SRpnt == NULL ) + { + return NULL; + } + + memset(SRpnt, 0, sizeof(Scsi_Request)); + SRpnt->sr_device = device; + SRpnt->sr_host = device->host; + SRpnt->sr_magic = SCSI_REQ_MAGIC; + SRpnt->sr_data_direction = SCSI_DATA_UNKNOWN; + + return SRpnt; +} + +/* + * Function: scsi_release_request + * + * Purpose: Release a request descriptor. + * + * Arguments: device - device for which we want a request + * + * Lock status: No locks assumed to be held. This function is SMP-safe. + * + * Returns: Pointer to request block. + * + * Notes: With the new queueing code, it becomes important + * to track the difference between a command and a + * request. A request is a pending item in the queue that + * has not yet reached the top of the queue. We still need + * to free a request when we are done with it, of course. + */ +void scsi_release_request(Scsi_Request * req) +{ + if( req->sr_command != NULL ) + { + scsi_release_command(req->sr_command); + req->sr_command = NULL; + } + + kfree(req); +} + +/* * Function: scsi_allocate_device * * Purpose: Allocate a command descriptor. @@ -269,6 +338,9 @@ static spinlock_t scsi_bhqueue_lock = SPIN_LOCK_UNLOCKED; * command block, this function will interrupt and return * NULL in the event that a signal arrives that needs to * be handled. + * + * This function is deprecated, and drivers should be + * rewritten to use Scsi_Request instead of Scsi_Cmnd. */ Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait, @@ -417,6 +489,10 @@ Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait, SCpnt->transfersize = 0; /* No default transfer size */ SCpnt->cmd_len = 0; + SCpnt->sc_data_direction = SCSI_DATA_UNKNOWN; + SCpnt->sc_request = NULL; + SCpnt->sc_magic = SCSI_CMND_MAGIC; + SCpnt->result = 0; SCpnt->underflow = 0; /* Do not flag underflow conditions */ SCpnt->resid = 0; @@ -451,6 +527,9 @@ Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait, * gets hidden in this function. Upper level drivers don't * have any chickens to wave in the air to get things to * work reliably. + * + * This function is deprecated, and drivers should be + * rewritten to use Scsi_Request instead of Scsi_Cmnd. */ void scsi_release_command(Scsi_Cmnd * SCpnt) { @@ -636,6 +715,8 @@ int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt) return rtn; } +devfs_handle_t scsi_devfs_handle = NULL; + /* * scsi_do_cmd sends all the commands out to the low-level driver. It * handles the specifics required for each low level driver - ie queued @@ -643,6 +724,215 @@ int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt) * drivers go for the same host at the same time. */ +void scsi_wait_req (Scsi_Request * SRpnt, const void *cmnd , + void *buffer, unsigned bufflen, + int timeout, int retries) +{ + DECLARE_MUTEX_LOCKED(sem); + + SRpnt->sr_request.sem = &sem; + SRpnt->sr_request.rq_status = RQ_SCSI_BUSY; + scsi_do_req (SRpnt, (void *) cmnd, + buffer, bufflen, scsi_wait_done, timeout, retries); + down (&sem); + SRpnt->sr_request.sem = NULL; + if( SRpnt->sr_command != NULL ) + { + scsi_release_command(SRpnt->sr_command); + SRpnt->sr_command = NULL; + } + +} + +/* + * Function: scsi_do_req + * + * Purpose: Queue a SCSI request + * + * Arguments: SRpnt - command descriptor. + * cmnd - actual SCSI command to be performed. + * buffer - data buffer. + * bufflen - size of data buffer. + * done - completion function to be run. + * timeout - how long to let it run before timeout. + * retries - number of retries we allow. + * + * Lock status: With the new queueing code, this is SMP-safe, and no locks + * need be held upon entry. The old queueing code the lock was + * assumed to be held upon entry. + * + * Returns: Nothing. + * + * Notes: Prior to the new queue code, this function was not SMP-safe. + * Also, this function is now only used for queueing requests + * for things like ioctls and character device requests - this + * is because we essentially just inject a request into the + * queue for the device. Normal block device handling manipulates + * the queue directly. + */ +void scsi_do_req(Scsi_Request * SRpnt, const void *cmnd, + void *buffer, unsigned bufflen, void (*done) (Scsi_Cmnd *), + int timeout, int retries) +{ + Scsi_Device * SDpnt = SRpnt->sr_device; + struct Scsi_Host *host = SDpnt->host; + + ASSERT_LOCK(&io_request_lock, 0); + + SCSI_LOG_MLQUEUE(4, + { + int i; + int target = SDpnt->id; + printk("scsi_do_req (host = %d, channel = %d target = %d, " + "buffer =%p, bufflen = %d, done = %p, timeout = %d, " + "retries = %d)\n" + "command : ", host->host_no, SDpnt->channel, target, buffer, + bufflen, done, timeout, retries); + for (i = 0; i < 10; ++i) + printk("%02x ", ((unsigned char *) cmnd)[i]); + printk("\n"); + }); + + if (!host) { + panic("Invalid or not present host.\n"); + } + + /* + * If the upper level driver is reusing these things, then + * we should release the low-level block now. Another one will + * be allocated later when this request is getting queued. + */ + if( SRpnt->sr_command != NULL ) + { + scsi_release_command(SRpnt->sr_command); + SRpnt->sr_command = NULL; + } + + /* + * We must prevent reentrancy to the lowlevel host driver. This prevents + * it - we enter a loop until the host we want to talk to is not busy. + * Race conditions are prevented, as interrupts are disabled in between the + * time we check for the host being not busy, and the time we mark it busy + * ourselves. + */ + + + /* + * Our own function scsi_done (which marks the host as not busy, disables + * the timeout counter, etc) will be called by us or by the + * scsi_hosts[host].queuecommand() function needs to also call + * the completion function for the high level driver. + */ + + memcpy((void *) SRpnt->sr_cmnd, (const void *) cmnd, + sizeof(SRpnt->sr_cmnd)); + SRpnt->sr_bufflen = bufflen; + SRpnt->sr_buffer = buffer; + SRpnt->sr_allowed = retries; + SRpnt->sr_done = done; + SRpnt->sr_timeout_per_command = timeout; + + memcpy((void *) SRpnt->sr_cmnd, (const void *) cmnd, + sizeof(SRpnt->sr_cmnd)); + + if (SRpnt->sr_cmd_len == 0) + SRpnt->sr_cmd_len = COMMAND_SIZE(SRpnt->sr_cmnd[0]); + + /* + * At this point, we merely set up the command, stick it in the normal + * request queue, and return. Eventually that request will come to the + * top of the list, and will be dispatched. + */ + scsi_insert_special_req(SRpnt, 0); + + SCSI_LOG_MLQUEUE(3, printk("Leaving scsi_do_cmd()\n")); +} + +/* + * Function: scsi_init_cmd_from_req + * + * Purpose: Queue a SCSI command + * Purpose: Initialize a Scsi_Cmnd from a Scsi_Request + * + * Arguments: SCpnt - command descriptor. + * SRpnt - Request from the queue. + * + * Lock status: None needed. + * + * Returns: Nothing. + * + * Notes: Mainly transfer data from the request structure to the + * command structure. The request structure is allocated + * using the normal memory allocator, and requests can pile + * up to more or less any depth. The command structure represents + * a consumable resource, as these are allocated into a pool + * when the SCSI subsystem initializes. The preallocation is + * required so that in low-memory situations a disk I/O request + * won't cause the memory manager to try and write out a page. + * The request structure is generally used by ioctls and character + * devices. + */ +void scsi_init_cmd_from_req(Scsi_Cmnd * SCpnt, Scsi_Request * SRpnt) +{ + struct Scsi_Host *host = SCpnt->host; + + ASSERT_LOCK(&io_request_lock, 0); + + SCpnt->owner = SCSI_OWNER_MIDLEVEL; + SRpnt->sr_command = SCpnt; + + if (!host) { + panic("Invalid or not present host.\n"); + } + + SCpnt->cmd_len = SRpnt->sr_cmd_len; + SCpnt->use_sg = SRpnt->sr_use_sg; + + memcpy((void *) &SCpnt->request, (const void *) &SRpnt->sr_request, + sizeof(SRpnt->sr_request)); + memcpy((void *) SCpnt->data_cmnd, (const void *) SRpnt->sr_cmnd, + sizeof(SCpnt->data_cmnd)); + SCpnt->reset_chain = NULL; + SCpnt->serial_number = 0; + SCpnt->serial_number_at_timeout = 0; + SCpnt->bufflen = SRpnt->sr_bufflen; + SCpnt->buffer = SRpnt->sr_buffer; + SCpnt->flags = 0; + SCpnt->retries = 0; + SCpnt->allowed = SRpnt->sr_allowed; + SCpnt->done = SRpnt->sr_done; + SCpnt->timeout_per_command = SRpnt->sr_timeout_per_command; + + SCpnt->sc_data_direction = SRpnt->sr_data_direction; + + SCpnt->sglist_len = SRpnt->sr_sglist_len; + SCpnt->underflow = SRpnt->sr_underflow; + + SCpnt->sc_request = SRpnt; + + memcpy((void *) SCpnt->cmnd, (const void *) SRpnt->sr_cmnd, + sizeof(SCpnt->cmnd)); + /* Zero the sense buffer. Some host adapters automatically request + * sense on error. 0 is not a valid sense code. + */ + memset((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer); + SCpnt->request_buffer = SRpnt->sr_buffer; + SCpnt->request_bufflen = SRpnt->sr_bufflen; + SCpnt->old_use_sg = SCpnt->use_sg; + if (SCpnt->cmd_len == 0) + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + SCpnt->old_cmd_len = SCpnt->cmd_len; + SCpnt->sc_old_data_direction = SCpnt->sc_data_direction; + + /* Start the timer ticking. */ + + SCpnt->internal_timeout = NORMAL_TIMEOUT; + SCpnt->abort_reason = 0; + SCpnt->result = 0; + + SCSI_LOG_MLQUEUE(3, printk("Leaving scsi_do_cmd()\n")); +} + /* * Function: scsi_do_cmd * @@ -737,6 +1027,7 @@ void scsi_do_cmd(Scsi_Cmnd * SCpnt, const void *cmnd, if (SCpnt->cmd_len == 0) SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); SCpnt->old_cmd_len = SCpnt->cmd_len; + SCpnt->sc_old_data_direction = SCpnt->sc_data_direction; /* Start the timer ticking. */ @@ -996,6 +1287,7 @@ int scsi_retry_command(Scsi_Cmnd * SCpnt) SCpnt->request_bufflen = SCpnt->bufflen; SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; + SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; /* * Zero the sense information from the last time we tried @@ -1017,6 +1309,7 @@ void scsi_finish_command(Scsi_Cmnd * SCpnt) { struct Scsi_Host *host; Scsi_Device *device; + Scsi_Request * SRpnt; unsigned long flags; ASSERT_LOCK(&io_request_lock, 0); @@ -1061,6 +1354,20 @@ void scsi_finish_command(Scsi_Cmnd * SCpnt) /* We can get here with use_sg=0, causing a panic in the upper level (DB) */ SCpnt->use_sg = SCpnt->old_use_sg; + /* + * If there is an associated request structure, copy the data over before we call the + * completion function. + */ + SRpnt = SCpnt->sc_request; + if( SRpnt != NULL ) { + SRpnt->sr_result = SRpnt->sr_command->result; + if( SRpnt->sr_result != 0 ) { + memcpy(SRpnt->sr_sense_buffer, + SRpnt->sr_command->sense_buffer, + sizeof(SRpnt->sr_sense_buffer)); + } + } + SCpnt->done(SCpnt); } @@ -1096,6 +1403,7 @@ void scsi_release_commandblocks(Scsi_Device * SDpnt) kfree((char *) SCpnt); } SDpnt->has_cmdblocks = 0; + SDpnt->queue_depth = 0; spin_unlock_irqrestore(&device_request_lock, flags); } @@ -1168,7 +1476,39 @@ void scsi_build_commandblocks(Scsi_Device * SDpnt) static int proc_scsi_gen_write(struct file * file, const char * buf, unsigned long length, void *data); +void __init scsi_host_no_insert(char *str, int n) +{ + Scsi_Host_Name *shn, *shn2; + int len; + + len = strlen(str); + if (len && (shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC))) { + if ((shn->name = kmalloc(len+1, GFP_ATOMIC))) { + strncpy(shn->name, str, len); + shn->name[len] = 0; + shn->host_no = n; + shn->host_registered = 0; + shn->loaded_as_module = 1; /* numbers shouldn't be freed in any case */ + shn->next = NULL; + if (scsi_host_no_list) { + for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next) + ; + shn2->next = shn; + } + else + scsi_host_no_list = shn; + max_scsi_hosts = n+1; + } + else + kfree((char *) shn); + } +} + #ifndef MODULE /* { */ + +char scsi_host_no_table[20][10] __initdata = {}; +int scsi_host_no_set __initdata = 0; + /* * scsi_dev_init() is our initialization routine, which in turn calls host * initialization, bus scanning, and sd/st initialization routines. @@ -1184,8 +1524,16 @@ int __init scsi_dev_init(void) return; #endif + /* Initialize list of host_no if kernel parameter set */ + if (scsi_host_no_set) { + int i; + for (i = 0;i < sizeof(scsi_host_no_table)/sizeof(scsi_host_no_table[0]);i++) + scsi_host_no_insert(scsi_host_no_table[i], i); + } + /* Yes we're here... */ + scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", 4, NULL); /* * This makes /proc/scsi and /proc/scsi/scsi visible. */ @@ -1535,6 +1883,7 @@ static int proc_scsi_gen_write(struct file * file, const char * buf, * Nobody is using this device any more. * Free all of the command structures. */ + devfs_unregister (scd->de); scsi_release_commandblocks(scd); /* Now we can remove the device structure */ @@ -1834,6 +2183,7 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt) printk("Attached usage count = %d\n", SDpnt->attached); return; } + devfs_unregister (SDpnt->de); } } @@ -2143,11 +2493,12 @@ static void scsi_dump_status(int level) printk("Dump of scsi host parameters:\n"); i = 0; for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { - printk(" %d %d %d : %d\n", + printk(" %d %d %d : %d %d\n", shpnt->host_failed, shpnt->host_busy, atomic_read(&shpnt->host_active), - shpnt->host_blocked); + shpnt->host_blocked, + shpnt->host_self_blocked); } @@ -2193,19 +2544,24 @@ static void scsi_dump_status(int level) /* Now dump the request lists for each block device */ printk("Dump of pending block device requests\n"); for (i = 0; i < MAX_BLKDEV; i++) { - if (blk_dev[i].request_queue.current_request) { + struct list_head * queue_head; + + queue_head = &blk_dev[i].request_queue.queue_head; + if (!list_empty(queue_head)) { struct request *req; + struct list_head * entry; + printk("%d: ", i); - req = blk_dev[i].request_queue.current_request; - while (req) { + entry = queue_head->next; + do { + req = blkdev_entry_to_request(entry); printk("(%s %d %ld %ld %ld) ", kdevname(req->rq_dev), req->cmd, req->sector, req->nr_sectors, req->current_nr_sectors); - req = req->next; - } + } while ((entry = entry->next) != queue_head); printk("\n"); } } @@ -2216,12 +2572,52 @@ static void scsi_dump_status(int level) } #endif /* CONFIG_PROC_FS */ +static int scsi_host_no_init (char *str) +{ + static int next_no = 0; + char *temp; + +#ifndef MODULE + int len; + scsi_host_no_set = 1; + memset(scsi_host_no_table, 0, sizeof(scsi_host_no_table)); +#endif /* MODULE */ + + while (str) { + temp = str; + while (*temp && (*temp != ':') && (*temp != ',')) + temp++; + if (!*temp) + temp = NULL; + else + *temp++ = 0; +#ifdef MODULE + scsi_host_no_insert(str, next_no); +#else + if (next_no < sizeof(scsi_host_no_table)/sizeof(scsi_host_no_table[0])) { + if ((len = strlen(str)) >= sizeof(scsi_host_no_table[0])) + len = sizeof(scsi_host_no_table[0])-1; + strncpy(scsi_host_no_table[next_no], str, len); + scsi_host_no_table[next_no][len] = 0; + } +#endif /* MODULE */ + str = temp; + next_no++; + } + return 1; +} + +#ifndef MODULE +__setup("scsihosts=", scsi_host_no_init); +#endif + #ifdef MODULE +static char *scsihosts; + +MODULE_PARM(scsihosts, "s"); int init_module(void) { - unsigned long size; - int has_space = 0; struct proc_dir_entry *generic; if( scsi_init_minimal_dma_pool() != 0 ) @@ -2249,6 +2645,8 @@ int init_module(void) scsi_loadable_module_flag = 1; + scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", 4, NULL); + scsi_host_no_init (scsihosts); /* * This is where the processing takes place for most everything * when commands are completed. @@ -2260,8 +2658,21 @@ int init_module(void) void cleanup_module(void) { + Scsi_Host_Name *shn, *shn2 = NULL; + remove_bh(SCSI_BH); + devfs_unregister (scsi_devfs_handle); + for (shn = scsi_host_no_list;shn;shn = shn->next) { + if (shn->name) + kfree(shn->name); + if (shn2) + kfree (shn2); + shn2 = shn; + } + if (shn2) + kfree (shn2); + #ifdef CONFIG_PROC_FS /* No, we're not here anymore. Don't show the /proc/scsi files. */ remove_proc_entry ("scsi/scsi", 0); |