📄 scsi.c
字号:
/* * scsi.c Copyright (C) 1992 Drew Eckhardt * Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale * * generic mid-level SCSI driver * Initial versions: Drew Eckhardt * Subsequent revisions: Eric Youngdale * * <drew@colorado.edu> * * Bug correction thanks go to : * Rik Faith <faith@cs.unc.edu> * Tommy Thorn <tthorn> * Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de> * * Modified by Eric Youngdale eric@andante.org or ericy@gnu.ai.mit.edu to * add scatter-gather, multiple outstanding request, and other * enhancements. * * Native multichannel, wide scsi, /proc/scsi and hot plugging * support added by Michael Neuffer <mike@i-connect.net> * * Added request_module("scsi_hostadapter") for kerneld: * (Put an "alias scsi_hostadapter your_hostadapter" in /etc/modules.conf) * Bjorn Ekwall <bj0rn@blox.se> * (changed to kmod) * * Major improvements to the timeout, abort, and reset processing, * as well as performance modifications for large queue depths by * Leonard N. Zubkoff <lnz@dandelion.com> * * Converted cli() code to spinlocks, Ingo Molnar * * Jiffies wrap fixes (host->resetting), 3 Dec 1998 Andrea Arcangeli * * out_of_space hacks, D. Gilbert (dpg) 990608 */#define REVISION "Revision: 1.00"#define VERSION "Id: scsi.c 1.00 2000/09/26"#include <linux/config.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/kernel.h>#include <linux/stat.h>#include <linux/blk.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/smp_lock.h>#include <linux/completion.h>#define __KERNEL_SYSCALLS__#include <linux/unistd.h>#include <linux/spinlock.h>#include <asm/system.h>#include <asm/irq.h>#include <asm/dma.h>#include <asm/uaccess.h>#include "scsi.h"#include "hosts.h"#include "constants.h"#ifdef CONFIG_KMOD#include <linux/kmod.h>#endif#undef USE_STATIC_SCSI_MEMORYstruct proc_dir_entry *proc_scsi;#ifdef CONFIG_PROC_FSstatic int scsi_proc_info(char *buffer, char **start, off_t offset, int length);static void scsi_dump_status(int level);#endif/* static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/scsi.c,v 1.38 1997/01/19 23:07:18 davem Exp $"; *//* * Definitions and constants. */#define MIN_RESET_DELAY (2*HZ)/* Do not call reset on error if we just did a reset within 15 sec. */#define MIN_RESET_PERIOD (15*HZ)/* * Macro to determine the size of SCSI command. This macro takes vendor * unique commands into account. SCSI commands in groups 6 and 7 are * vendor unique and we will depend upon the command length being * supplied correctly in cmd_len. */#define CDB_SIZE(SCpnt) ((((SCpnt->cmnd[0] >> 5) & 7) < 6) ? \ COMMAND_SIZE(SCpnt->cmnd[0]) : SCpnt->cmd_len)/* * Data declarations. */unsigned long scsi_pid;Scsi_Cmnd *last_cmnd;/* Command group 3 is reserved and should never be used. */const unsigned char scsi_command_size[8] ={ 6, 10, 10, 12, 16, 12, 10, 10};static unsigned long serial_number;static Scsi_Cmnd *scsi_bh_queue_head;static Scsi_Cmnd *scsi_bh_queue_tail;/* * Note - the initial logging level can be set here to log events at boot time. * After the system is up, you may enable logging via the /proc interface. */unsigned int scsi_logging_level;const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] ={ "Direct-Access ", "Sequential-Access", "Printer ", "Processor ", "WORM ", "CD-ROM ", "Scanner ", "Optical Device ", "Medium Changer ", "Communications ", "Unknown ", "Unknown ", "Unknown ", "Enclosure ",};/* * Function prototypes. */extern void scsi_times_out(Scsi_Cmnd * SCpnt);void scsi_build_commandblocks(Scsi_Device * SDpnt);/* * These are the interface to the old error handling code. It should go away * someday soon. */extern void scsi_old_done(Scsi_Cmnd * SCpnt);extern void scsi_old_times_out(Scsi_Cmnd * SCpnt);/* * Function: scsi_initialize_queue() * * Purpose: Selects queue handler function for a device. * * Arguments: SDpnt - device for which we need a handler function. * * Returns: Nothing * * Lock status: No locking assumed or required. * * Notes: Most devices will end up using scsi_request_fn for the * handler function (at least as things are done now). * The "block" feature basically ensures that only one of * the blocked hosts is active at one time, mainly to work around * buggy DMA chipsets where the memory gets starved. * For this case, we have a special handler function, which * does some checks and ultimately calls scsi_request_fn. * * The single_lun feature is a similar special case. * * We handle these things by stacking the handlers. The * special case handlers simply check a few conditions, * and return if they are not supposed to do anything. * In the event that things are OK, then they call the next * handler in the list - ultimately they call scsi_request_fn * to do the dirty deed. */void scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt) { blk_init_queue(&SDpnt->request_queue, scsi_request_fn); blk_queue_headactive(&SDpnt->request_queue, 0); SDpnt->request_queue.queuedata = (void *) SDpnt;}#ifdef MODULEMODULE_PARM(scsi_logging_level, "i");MODULE_PARM_DESC(scsi_logging_level, "SCSI logging level; should be zero or nonzero");#elsestatic int __init scsi_logging_setup(char *str){ int tmp; if (get_option(&str, &tmp) == 1) { scsi_logging_level = (tmp ? ~0 : 0); return 1; } else { printk(KERN_INFO "scsi_logging_setup : usage scsi_logging_level=n " "(n should be 0 or non-zero)\n"); return 0; }}__setup("scsi_logging=", scsi_logging_setup);#endif/* * Issue a command and wait for it to complete */ static void scsi_wait_done(Scsi_Cmnd * SCpnt){ struct request *req; req = &SCpnt->request; req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */ if (req->waiting != NULL) { complete(req->waiting); }}/* * This lock protects the freelist for all devices on the system. * We could make this finer grained by having a single lock per * device if it is ever found that there is excessive contention * on this lock. */static spinlock_t device_request_lock = SPIN_LOCK_UNLOCKED;/* * Used to protect insertion into and removal from the queue of * commands to be processed by the bottom half handler. */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. * * Arguments: device - device for which we want a command descriptor * wait - 1 if we should wait in the event that none * are available. * interruptible - 1 if we should unblock and return NULL * in the event that we must wait, and a signal * arrives. * * Lock status: No locks assumed to be held. This function is SMP-safe. * * Returns: Pointer to command descriptor. * * Notes: Prior to the new queue code, this function was not SMP-safe. * * If the wait flag is true, and we are waiting for a free * 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, int interruptable){ struct Scsi_Host *host; Scsi_Cmnd *SCpnt = NULL; Scsi_Device *SDpnt; unsigned long flags; if (!device) panic("No device passed to scsi_allocate_device().\n"); host = device->host; spin_lock_irqsave(&device_request_lock, flags); while (1 == 1) { SCpnt = NULL; if (!device->device_blocked) { if (device->single_lun) { /* * FIXME(eric) - this is not at all optimal. Given that * single lun devices are rare and usually slow * (i.e. CD changers), this is good enough for now, but * we may want to come back and optimize this later. * * Scan through all of the devices attached to this * host, and see if any are active or not. If so, * we need to defer this command. * * We really need a busy counter per device. This would * allow us to more easily figure out whether we should * do anything here or not. */ for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { /* * Only look for other devices on the same bus * with the same target ID. */ if (SDpnt->channel != device->channel || SDpnt->id != device->id || SDpnt == device) { continue; } if( atomic_read(&SDpnt->device_active) != 0) { break; } } if (SDpnt) { /* * Some other device in this cluster is busy. * If asked to wait, we need to wait, otherwise * return NULL. */ SCpnt = NULL; goto busy; } } /* * Now we can check for a free command block for this device. */ for (SCpnt = device->device_queue; SCpnt; SCpnt = SCpnt->next) { if (SCpnt->request.rq_status == RQ_INACTIVE) break; } } /* * If we couldn't find a free command block, and we have been * asked to wait, then do so. */ if (SCpnt) { break; } busy: /* * If we have been asked to wait for a free block, then * wait here. */ if (wait) { DECLARE_WAITQUEUE(wait, current); /* * We need to wait for a free commandblock. We need to * insert ourselves into the list before we release the * lock. This way if a block were released the same * microsecond that we released the lock, the call * to schedule() wouldn't block (well, it might switch, * but the current task will still be schedulable. */ add_wait_queue(&device->scpnt_wait, &wait); if( interruptable ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -