scsi.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,249 行 · 第 1/3 页

C
1,249
字号
/* *  scsi.c Copyright (C) 1992 Drew Eckhardt *         Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale *         Copyright (C) 2002, 2003 Christoph Hellwig * *  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/modprobe.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 */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/completion.h>#include <linux/devfs_fs_kernel.h>#include <linux/unistd.h>#include <linux/spinlock.h>#include <linux/kmod.h>#include <linux/interrupt.h>#include <linux/notifier.h>#include <linux/cpu.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_dbg.h>#include <scsi/scsi_device.h>#include <scsi/scsi_eh.h>#include <scsi/scsi_host.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi_request.h>#include "scsi_priv.h"#include "scsi_logging.h"/* * 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(cmd)	(((((cmd)->cmnd[0] >> 5) & 7) < 6) ? \				COMMAND_SIZE((cmd)->cmnd[0]) : (cmd)->cmd_len)/* * Data declarations. */unsigned long scsi_pid;static unsigned long serial_number;/* * 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          ",	"RAID             ",	"Enclosure        ",};/* * Function:    scsi_allocate_request * * Purpose:     Allocate a request descriptor. * * Arguments:   device		- device for which we want a request *		gfp_mask	- allocation flags passed to kmalloc * * Lock status: No locks assumed to be held.  This function is SMP-safe. * * Returns:     Pointer to request block. */struct scsi_request *scsi_allocate_request(struct scsi_device *sdev,					   int gfp_mask){	const int offset = ALIGN(sizeof(struct scsi_request), 4);	const int size = offset + sizeof(struct request);	struct scsi_request *sreq;  	sreq = kmalloc(size, gfp_mask);	if (likely(sreq != NULL)) {		memset(sreq, 0, size);		sreq->sr_request = (struct request *)(((char *)sreq) + offset);		sreq->sr_device = sdev;		sreq->sr_host = sdev->host;		sreq->sr_magic = SCSI_REQ_MAGIC;		sreq->sr_data_direction = DMA_BIDIRECTIONAL;	}	return sreq;}void __scsi_release_request(struct scsi_request *sreq){	struct request *req = sreq->sr_request;	/* unlikely because the tag was usually ended earlier by the	 * mid-layer. However, for layering reasons ULD's don't end	 * the tag of commands they generate. */	if (unlikely(blk_rq_tagged(req))) {		unsigned long flags;		struct request_queue *q = req->q;		spin_lock_irqsave(q->queue_lock, flags);		blk_queue_end_tag(q, req);		spin_unlock_irqrestore(q->queue_lock, flags);	}	if (likely(sreq->sr_command != NULL)) {		struct scsi_cmnd *cmd = sreq->sr_command;		sreq->sr_command = NULL;		scsi_next_command(cmd);	}}/* * Function:    scsi_release_request * * Purpose:     Release a request descriptor. * * Arguments:   sreq    - request to release * * Lock status: No locks assumed to be held.  This function is SMP-safe. */void scsi_release_request(struct scsi_request *sreq){	__scsi_release_request(sreq);	kfree(sreq);}struct scsi_host_cmd_pool {	kmem_cache_t	*slab;	unsigned int	users;	char		*name;	unsigned int	slab_flags;	unsigned int	gfp_mask;};static struct scsi_host_cmd_pool scsi_cmd_pool = {	.name		= "scsi_cmd_cache",	.slab_flags	= SLAB_HWCACHE_ALIGN,};static struct scsi_host_cmd_pool scsi_cmd_dma_pool = {	.name		= "scsi_cmd_cache(DMA)",	.slab_flags	= SLAB_HWCACHE_ALIGN|SLAB_CACHE_DMA,	.gfp_mask	= __GFP_DMA,};static DECLARE_MUTEX(host_cmd_pool_mutex);static struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost,					    int gfp_mask){	struct scsi_cmnd *cmd;	cmd = kmem_cache_alloc(shost->cmd_pool->slab,			gfp_mask | shost->cmd_pool->gfp_mask);	if (unlikely(!cmd)) {		unsigned long flags;		spin_lock_irqsave(&shost->free_list_lock, flags);		if (likely(!list_empty(&shost->free_list))) {			cmd = list_entry(shost->free_list.next,					 struct scsi_cmnd, list);			list_del_init(&cmd->list);		}		spin_unlock_irqrestore(&shost->free_list_lock, flags);	}	return cmd;}/* * Function:	scsi_get_command() * * Purpose:	Allocate and setup a scsi command block * * Arguments:	dev	- parent scsi device *		gfp_mask- allocator flags * * Returns:	The allocated scsi command structure. */struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, int gfp_mask){	struct scsi_cmnd *cmd = __scsi_get_command(dev->host, gfp_mask);	if (likely(cmd != NULL)) {		unsigned long flags;		memset(cmd, 0, sizeof(*cmd));		cmd->device = dev;		cmd->state = SCSI_STATE_UNUSED;		cmd->owner = SCSI_OWNER_NOBODY;		init_timer(&cmd->eh_timeout);		INIT_LIST_HEAD(&cmd->list);		spin_lock_irqsave(&dev->list_lock, flags);		list_add_tail(&cmd->list, &dev->cmd_list);		spin_unlock_irqrestore(&dev->list_lock, flags);	}	return cmd;}				/* * Function:	scsi_put_command() * * Purpose:	Free a scsi command block * * Arguments:	cmd	- command block to free * * Returns:	Nothing. * * Notes:	The command must not belong to any lists. */void scsi_put_command(struct scsi_cmnd *cmd){	struct Scsi_Host *shost = cmd->device->host;	unsigned long flags;		/* serious error if the command hasn't come from a device list */	spin_lock_irqsave(&cmd->device->list_lock, flags);	BUG_ON(list_empty(&cmd->list));	list_del_init(&cmd->list);	spin_unlock(&cmd->device->list_lock);	/* changing locks here, don't need to restore the irq state */	spin_lock(&shost->free_list_lock);	if (unlikely(list_empty(&shost->free_list))) {		list_add(&cmd->list, &shost->free_list);		cmd = NULL;	}	spin_unlock_irqrestore(&shost->free_list_lock, flags);	if (likely(cmd != NULL))		kmem_cache_free(shost->cmd_pool->slab, cmd);}/* * Function:	scsi_setup_command_freelist() * * Purpose:	Setup the command freelist for a scsi host. * * Arguments:	shost	- host to allocate the freelist for. * * Returns:	Nothing. */int scsi_setup_command_freelist(struct Scsi_Host *shost){	struct scsi_host_cmd_pool *pool;	struct scsi_cmnd *cmd;	spin_lock_init(&shost->free_list_lock);	INIT_LIST_HEAD(&shost->free_list);	/*	 * Select a command slab for this host and create it if not	 * yet existant.	 */	down(&host_cmd_pool_mutex);	pool = (shost->unchecked_isa_dma ? &scsi_cmd_dma_pool : &scsi_cmd_pool);	if (!pool->users) {		pool->slab = kmem_cache_create(pool->name,				sizeof(struct scsi_cmnd), 0,				pool->slab_flags, NULL, NULL);		if (!pool->slab)			goto fail;	}	pool->users++;	shost->cmd_pool = pool;	up(&host_cmd_pool_mutex);	/*	 * Get one backup command for this host.	 */	cmd = kmem_cache_alloc(shost->cmd_pool->slab,			GFP_KERNEL | shost->cmd_pool->gfp_mask);	if (!cmd)		goto fail2;	list_add(&cmd->list, &shost->free_list);			return 0; fail2:	if (!--pool->users)		kmem_cache_destroy(pool->slab);	return -ENOMEM; fail:	up(&host_cmd_pool_mutex);	return -ENOMEM;}/* * Function:	scsi_destroy_command_freelist() * * Purpose:	Release the command freelist for a scsi host. * * Arguments:	shost	- host that's freelist is going to be destroyed */void scsi_destroy_command_freelist(struct Scsi_Host *shost){	while (!list_empty(&shost->free_list)) {		struct scsi_cmnd *cmd;		cmd = list_entry(shost->free_list.next, struct scsi_cmnd, list);		list_del_init(&cmd->list);		kmem_cache_free(shost->cmd_pool->slab, cmd);	}	down(&host_cmd_pool_mutex);	if (!--shost->cmd_pool->users)		kmem_cache_destroy(shost->cmd_pool->slab);	up(&host_cmd_pool_mutex);}#ifdef CONFIG_SCSI_LOGGINGvoid scsi_log_send(struct scsi_cmnd *cmd){	unsigned int level;	struct scsi_device *sdev;	/*	 * If ML QUEUE log level is greater than or equal to:	 *	 * 1: nothing (match completion)	 *	 * 2: log opcode + command of all commands	 *	 * 3: same as 2 plus dump cmd address	 *	 * 4: same as 3 plus dump extra junk	 */	if (unlikely(scsi_logging_level)) {		level = SCSI_LOG_LEVEL(SCSI_LOG_MLQUEUE_SHIFT,				       SCSI_LOG_MLQUEUE_BITS);		if (level > 1) {			sdev = cmd->device;			printk(KERN_INFO "scsi <%d:%d:%d:%d> send ",			       sdev->host->host_no, sdev->channel, sdev->id,			       sdev->lun);			if (level > 2)				printk("0x%p ", cmd);			/*			 * spaces to match disposition and cmd->result			 * output in scsi_log_completion.			 */			printk("                 ");			scsi_print_command(cmd);			if (level > 3) {				printk(KERN_INFO "buffer = 0x%p, bufflen = %d,"				       " done = 0x%p, queuecommand 0x%p\n",					cmd->buffer, cmd->bufflen,					cmd->done,					sdev->host->hostt->queuecommand);			}

⌨️ 快捷键说明

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