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

📄 scsi_queue.c

📁 基于组件方式开发操作系统的OSKIT源代码
💻 C
字号:
/* *  scsi_queue.c Copyright (C) 1997 Eric Youngdale * *  generic mid-level SCSI queueing. * *  The point of this is that we need to track when hosts are unable to *  accept a command because they are busy.  In addition, we track devices *  that cannot accept a command because of a QUEUE_FULL condition.  In both *  of these cases, we enter the command in the queue.  At some later point, *  we attempt to remove commands from the queue and retry them. */#define __NO_VERSION__#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/smp_lock.h>#define __KERNEL_SYSCALLS__#include <linux/unistd.h>#include <asm/system.h>#include <asm/irq.h>#include <asm/dma.h>#include "scsi.h"#include "hosts.h"#include "constants.h"/* * TODO: *	1) Prevent multiple traversals of list to look for commands to *	   queue. *	2) Protect against multiple insertions of list at the same time. * DONE: *	1) Set state of scsi command to a new state value for ml queue. *	2) Insert into queue when host rejects command. *	3) Make sure status code is properly passed from low-level queue func *	   so that internal_cmnd properly returns the right value. *	4) Insert into queue when QUEUE_FULL. *	5) Cull queue in bottom half handler. *	6) Check usage count prior to queue insertion.  Requeue if usage *	   count is 0. *	7) Don't send down any more commands if the host/device is busy. */static const char RCSid[] = "$Header: /mnt/ide/home/eric/CVSROOT/linux/drivers/scsi/scsi_queue.c,v 1.1 1997/10/21 11:16:38 eric Exp $";/* * Lock used to prevent more than one process from frobbing the list at the * same time.  FIXME(eric) - there should be separate spinlocks for each host. * This will reduce contention. */spinlock_t	scsi_mlqueue_lock        = SPIN_LOCK_UNLOCKED;spinlock_t	scsi_mlqueue_remove_lock = SPIN_LOCK_UNLOCKED;/* * Function:    scsi_mlqueue_insert() * * Purpose:     Insert a command in the midlevel queue. * * Arguments:   cmd    - command that we are adding to queue. *		reason - why we are inserting command to queue. * * Returns:     Nothing. * * Notes:	We do this for one of two cases.  Either the host is busy *		and it cannot accept any more commands for the time being, *		or the device returned QUEUE_FULL and can accept no more *		commands. * Notes:	This could be called either from an interrupt context or a *		normal process context. */intscsi_mlqueue_insert(Scsi_Cmnd * cmd, int reason){    Scsi_Cmnd        * cpnt;    unsigned long      flags;    struct Scsi_Host * host;    SCSI_LOG_MLQUEUE(1,printk("Inserting command %p into mlqueue\n", cmd));    /*     * We are inserting the command into the ml queue.  First, we     * cancel the timer, so it doesn't time out.     */    scsi_delete_timer(cmd);    host = cmd->host;    /*     * Next, set the appropriate busy bit for the device/host.     */    if( reason == SCSI_MLQUEUE_HOST_BUSY )    {	/*	 * Protect against race conditions.  If the host isn't busy,	 * assume that something actually completed, and that we should	 * be able to queue a command now.  Note that there is an implicit	 * assumption that every host can always queue at least one command.	 * If a host is inactive and cannot queue any commands, I don't see	 * how things could possibly work anyways.	 */	if( host->host_busy == 0 )	{	    if( scsi_retry_command(cmd) == 0 )	    {		return 0;	    }	}	host->host_blocked   = TRUE;	cmd->host_wait       = TRUE;    }    else    {	/*	 * Protect against race conditions.  If the device isn't busy,	 * assume that something actually completed, and that we should	 * be able to queue a command now.  Note that there is an implicit	 * assumption that every host can always queue at least one command.	 * If a host is inactive and cannot queue any commands, I don't see	 * how things could possibly work anyways.	 */	if( cmd->device->device_busy == 0 )	{	    if( scsi_retry_command(cmd) == 0 )	    {		return 0;	    }	}	cmd->device->device_busy = TRUE;	cmd->device_wait         = TRUE;    }    /*     * Register the fact that we own the thing for now.     */    cmd->state = SCSI_STATE_MLQUEUE;    cmd->owner = SCSI_OWNER_MIDLEVEL;    cmd->bh_next = NULL;    /*     * As a performance enhancement, look to see whether the list is     * empty.  If it is, then we can just atomicly insert the command     * in the list and return without locking.     */    if( host->pending_commands == NULL )    {	cpnt = xchg(&host->pending_commands, cmd);	if( cpnt == NULL )	{	    return 0;	}	/*	 * Rats.  Something slipped in while we were exchanging.	 * Swap it back and fall through to do it the hard way.	 */	cmd = xchg(&host->pending_commands, cpnt);    }    /*     * Next append the command to the list of pending commands.     */    spin_lock_irqsave(&scsi_mlqueue_lock, flags);    for(cpnt = host->pending_commands; cpnt && cpnt->bh_next; 	cpnt = cpnt->bh_next)    {	continue;    }    if( cpnt != NULL )    {	cpnt->bh_next = cmd;    }    else    {	host->pending_commands = cmd;    }    spin_unlock_irqrestore(&scsi_mlqueue_lock, flags);    return 0;}/* * Function:    scsi_mlqueue_finish() * * Purpose:     Try and queue commands from the midlevel queue. * * Arguments:   host    - host that just finished a command. *		device  - device that just finished a command. * * Returns:     Nothing. * * Notes:	This could be called either from an interrupt context or a *		normal process context. */intscsi_mlqueue_finish(struct Scsi_Host * host, Scsi_Device * device){    Scsi_Cmnd      * cpnt;    unsigned long    flags;    Scsi_Cmnd      * next;    Scsi_Cmnd      * prev;    int              reason = 0;    int              rtn;    SCSI_LOG_MLQUEUE(2,printk("scsi_mlqueue_finish starting\n"));    /*     * First, clear the flag for the host/device.  We will then start     * pushing commands through until either something else blocks, or     * the queue is empty.     */    if( host->host_blocked )    {	reason = SCSI_MLQUEUE_HOST_BUSY;	host->host_blocked = FALSE;    }    if( device->device_busy )    {	reason = SCSI_MLQUEUE_DEVICE_BUSY;	device->device_busy = FALSE;    }    /*     * Walk the list of commands to see if there is anything we can     * queue.  This probably needs to be optimized for performance at     * some point.     */    prev = NULL;    spin_lock_irqsave(&scsi_mlqueue_remove_lock, flags);    for(cpnt = host->pending_commands; cpnt; cpnt = next)    {	next = cpnt->bh_next;	/*	 * First, see if this command is suitable for being retried now.	 */	if( reason == SCSI_MLQUEUE_HOST_BUSY )	{	    /*	     * The host was busy, but isn't any more.  Thus we may be	     * able to queue the command now, but we were waiting for	     * the device, then we should keep waiting.  Similarily, if	     * the device is now busy, we should also keep waiting.	     */	    if(    (cpnt->host_wait == FALSE)		|| (device->device_busy == TRUE) )	    {		prev = cpnt;		continue;	    }	}	if( reason == SCSI_MLQUEUE_DEVICE_BUSY )	{	    /*	     * The device was busy, but isn't any more.  Thus we may be	     * able to queue the command now, but we were waiting for	     * the host, then we should keep waiting.  Similarily, if	     * the host is now busy, we should also keep waiting.	     */	    if(    (cpnt->device_wait == FALSE)		|| (host->host_blocked == TRUE) )	    {		prev = cpnt;		continue;	    }	}	/*	 * First, remove the command from the list.	 */	if( prev == NULL )	{	    host->pending_commands = next;	}	else	{	    prev->bh_next = next;	}	cpnt->bh_next = NULL;	rtn = scsi_retry_command(cpnt);	/*	 * If we got a non-zero return value, it means that the host rejected	 * the command.  The internal_cmnd function will have added the	 * command back to the end of the list, so we don't have anything	 * more to do here except return.	 */	if( rtn )	{	    spin_unlock_irqrestore(&scsi_mlqueue_remove_lock, flags);	    SCSI_LOG_MLQUEUE(1,printk("Unable to remove command %p from mlqueue\n", cpnt));	    goto finish;	}	SCSI_LOG_MLQUEUE(1,printk("Removed command %p from mlqueue\n", cpnt));    }    spin_unlock_irqrestore(&scsi_mlqueue_remove_lock, flags);finish:    SCSI_LOG_MLQUEUE(2,printk("scsi_mlqueue_finish returning\n"));    return 0;}

⌨️ 快捷键说明

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