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 + -
显示快捷键?