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