scsi.c

来自「linux 内核源代码」· C语言 代码 · 共 1,118 行 · 第 1/3 页

C
1,118
字号
/* *  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/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/unistd.h>#include <linux/spinlock.h>#include <linux/kmod.h>#include <linux/interrupt.h>#include <linux/notifier.h>#include <linux/cpu.h>#include <linux/mutex.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_dbg.h>#include <scsi/scsi_device.h>#include <scsi/scsi_driver.h>#include <scsi/scsi_eh.h>#include <scsi/scsi_host.h>#include <scsi/scsi_tcq.h>#include "scsi_priv.h"#include "scsi_logging.h"static void scsi_done(struct scsi_cmnd *cmd);/* * 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)/* * 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;#if defined(CONFIG_SCSI_LOGGING)EXPORT_SYMBOL(scsi_logging_level);#endif/* NB: These are exposed through /proc/scsi/scsi and form part of the ABI. * You may not alter any existing entry (although adding new ones is * encouraged once assigned by ANSI/INCITS T10 */static const char *const scsi_device_types[] = {	"Direct-Access    ",	"Sequential-Access",	"Printer          ",	"Processor        ",	"WORM             ",	"CD-ROM           ",	"Scanner          ",	"Optical Device   ",	"Medium Changer   ",	"Communications   ",	"ASC IT8          ",	"ASC IT8          ",	"RAID             ",	"Enclosure        ",	"Direct-Access-RBC",	"Optical card     ",	"Bridge controller",	"Object storage   ",	"Automation/Drive ",};const char * scsi_device_type(unsigned type){	if (type == 0x1e)		return "Well-known LUN   ";	if (type == 0x1f)		return "No Device        ";	if (type >= ARRAY_SIZE(scsi_device_types))		return "Unknown          ";	return scsi_device_types[type];}EXPORT_SYMBOL(scsi_device_type);struct scsi_host_cmd_pool {	struct kmem_cache	*slab;	unsigned int	users;	char		*name;	unsigned int	slab_flags;	gfp_t		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 DEFINE_MUTEX(host_cmd_pool_mutex);struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t 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;}EXPORT_SYMBOL_GPL(__scsi_get_command);/* * 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, gfp_t gfp_mask){	struct scsi_cmnd *cmd;	/* Bail if we can't get a reference to the device */	if (!get_device(&dev->sdev_gendev))		return NULL;	cmd = __scsi_get_command(dev->host, gfp_mask);	if (likely(cmd != NULL)) {		unsigned long flags;		memset(cmd, 0, sizeof(*cmd));		cmd->device = dev;		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);		cmd->jiffies_at_alloc = jiffies;	} else		put_device(&dev->sdev_gendev);	return cmd;}EXPORT_SYMBOL(scsi_get_command);void __scsi_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd,			struct device *dev){	unsigned long flags;	/* changing locks here, don't need to restore the irq state */	spin_lock_irqsave(&shost->free_list_lock, flags);	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);	put_device(dev);}EXPORT_SYMBOL(__scsi_put_command);/* * 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_device *sdev = cmd->device;	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_irqrestore(&cmd->device->list_lock, flags);	__scsi_put_command(cmd->device->host, cmd, &sdev->sdev_gendev);}EXPORT_SYMBOL(scsi_put_command);/* * 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.	 */	mutex_lock(&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);		if (!pool->slab)			goto fail;	}	pool->users++;	shost->cmd_pool = pool;	mutex_unlock(&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:	mutex_unlock(&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);	}	mutex_lock(&host_cmd_pool_mutex);	if (!--shost->cmd_pool->users)		kmem_cache_destroy(shost->cmd_pool->slab);	mutex_unlock(&host_cmd_pool_mutex);}#ifdef CONFIG_SCSI_LOGGINGvoid scsi_log_send(struct scsi_cmnd *cmd){	unsigned int level;	/*	 * 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) {			scmd_printk(KERN_INFO, cmd, "Send: ");			if (level > 2)				printk("0x%p ", cmd);			printk("\n");			scsi_print_command(cmd);			if (level > 3) {				printk(KERN_INFO "buffer = 0x%p, bufflen = %d,"				       " queuecommand 0x%p\n",					scsi_sglist(cmd), scsi_bufflen(cmd),					cmd->device->host->hostt->queuecommand);

⌨️ 快捷键说明

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