⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scsi.c

📁 讲述linux的初始化过程
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  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/malloc.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>#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)/* * Data declarations. */unsigned long scsi_pid;Scsi_Cmnd *last_cmnd;/* Command groups 3 and 4 are reserved and should never be used.  */const unsigned char scsi_command_size[8] ={	6, 10, 10, 12,	12, 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->sem != NULL) {		up(req->sem);	}}/* * 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 ) {                                set_current_state(TASK_INTERRUPTIBLE);                        } else {                                set_current_state(TASK_UNINTERRUPTIBLE);                        }                        spin_unlock_irqrestore(&device_request_lock, flags);			/*			 * This should block until a device command block

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -