📄 megaraid.c
字号:
/* * * Linux MegaRAID device driver * * Copyright (c) 2002 LSI Logic Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Copyright (c) 2002 Red Hat, Inc. All rights reserved. * - fixes * - speed-ups (list handling fixes, issued_list, optimizations.) * - lots of cleanups. * * Copyright (c) 2003 Christoph Hellwig <hch@lst.de> * - new-style, hotplug-aware pci probing and scsi registration * * Version : v2.00.4 Mon Nov 14 14:02:43 EST 2005 - Seokmann Ju * <Seokmann.Ju@lsil.com> * * Description: Linux device driver for LSI Logic MegaRAID controller * * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490, 493 * 518, 520, 531, 532 * * This driver is supported by LSI Logic, with assistance from Red Hat, Dell, * and others. Please send updates to the mailing list * linux-scsi@vger.kernel.org . * */#include <linux/mm.h>#include <linux/fs.h>#include <linux/blkdev.h>#include <asm/uaccess.h>#include <asm/io.h>#include <linux/completion.h>#include <linux/delay.h>#include <linux/proc_fs.h>#include <linux/reboot.h>#include <linux/module.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/dma-mapping.h>#include <scsi/scsicam.h>#include "scsi.h"#include <scsi/scsi_host.h>#include "megaraid.h"#define MEGARAID_MODULE_VERSION "2.00.4"MODULE_AUTHOR ("sju@lsil.com");MODULE_DESCRIPTION ("LSI Logic MegaRAID legacy driver");MODULE_LICENSE ("GPL");MODULE_VERSION(MEGARAID_MODULE_VERSION);static unsigned int max_cmd_per_lun = DEF_CMD_PER_LUN;module_param(max_cmd_per_lun, uint, 0);MODULE_PARM_DESC(max_cmd_per_lun, "Maximum number of commands which can be issued to a single LUN (default=DEF_CMD_PER_LUN=63)");static unsigned short int max_sectors_per_io = MAX_SECTORS_PER_IO;module_param(max_sectors_per_io, ushort, 0);MODULE_PARM_DESC(max_sectors_per_io, "Maximum number of sectors per I/O request (default=MAX_SECTORS_PER_IO=128)");static unsigned short int max_mbox_busy_wait = MBOX_BUSY_WAIT;module_param(max_mbox_busy_wait, ushort, 0);MODULE_PARM_DESC(max_mbox_busy_wait, "Maximum wait for mailbox in microseconds if busy (default=MBOX_BUSY_WAIT=10)");#define RDINDOOR(adapter) readl((adapter)->mmio_base + 0x20)#define RDOUTDOOR(adapter) readl((adapter)->mmio_base + 0x2C)#define WRINDOOR(adapter,value) writel(value, (adapter)->mmio_base + 0x20)#define WROUTDOOR(adapter,value) writel(value, (adapter)->mmio_base + 0x2C)/* * Global variables */static int hba_count;static adapter_t *hba_soft_state[MAX_CONTROLLERS];static struct proc_dir_entry *mega_proc_dir_entry;/* For controller re-ordering */static struct mega_hbas mega_hbas[MAX_CONTROLLERS];/* * The File Operations structure for the serial/ioctl interface of the driver */static const struct file_operations megadev_fops = { .owner = THIS_MODULE, .ioctl = megadev_ioctl, .open = megadev_open,};/* * Array to structures for storing the information about the controllers. This * information is sent to the user level applications, when they do an ioctl * for this information. */static struct mcontroller mcontroller[MAX_CONTROLLERS];/* The current driver version */static u32 driver_ver = 0x02000000;/* major number used by the device for character interface */static int major;#define IS_RAID_CH(hba, ch) (((hba)->mega_ch_class >> (ch)) & 0x01)/* * Debug variable to print some diagnostic messages */static int trace_level;/** * mega_setup_mailbox() * @adapter - pointer to our soft state * * Allocates a 8 byte aligned memory for the handshake mailbox. */static intmega_setup_mailbox(adapter_t *adapter){ unsigned long align; adapter->una_mbox64 = pci_alloc_consistent(adapter->dev, sizeof(mbox64_t), &adapter->una_mbox64_dma); if( !adapter->una_mbox64 ) return -1; adapter->mbox = &adapter->una_mbox64->mbox; adapter->mbox = (mbox_t *)((((unsigned long) adapter->mbox) + 15) & (~0UL ^ 0xFUL)); adapter->mbox64 = (mbox64_t *)(((unsigned long)adapter->mbox) - 8); align = ((void *)adapter->mbox) - ((void *)&adapter->una_mbox64->mbox); adapter->mbox_dma = adapter->una_mbox64_dma + 8 + align; /* * Register the mailbox if the controller is an io-mapped controller */ if( adapter->flag & BOARD_IOMAP ) { outb_p(adapter->mbox_dma & 0xFF, adapter->host->io_port + MBOX_PORT0); outb_p((adapter->mbox_dma >> 8) & 0xFF, adapter->host->io_port + MBOX_PORT1); outb_p((adapter->mbox_dma >> 16) & 0xFF, adapter->host->io_port + MBOX_PORT2); outb_p((adapter->mbox_dma >> 24) & 0xFF, adapter->host->io_port + MBOX_PORT3); outb_p(ENABLE_MBOX_BYTE, adapter->host->io_port + ENABLE_MBOX_REGION); irq_ack(adapter); irq_enable(adapter); } return 0;}/* * mega_query_adapter() * @adapter - pointer to our soft state * * Issue the adapter inquiry commands to the controller and find out * information and parameter about the devices attached */static intmega_query_adapter(adapter_t *adapter){ dma_addr_t prod_info_dma_handle; mega_inquiry3 *inquiry3; u8 raw_mbox[sizeof(struct mbox_out)]; mbox_t *mbox; int retval; /* Initialize adapter inquiry mailbox */ mbox = (mbox_t *)raw_mbox; memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); memset(&mbox->m_out, 0, sizeof(raw_mbox)); /* * Try to issue Inquiry3 command * if not succeeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and * update enquiry3 structure */ mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle; inquiry3 = (mega_inquiry3 *)adapter->mega_buffer; raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ raw_mbox[2] = NC_SUBOP_ENQUIRY3; /* i.e. 0x0F */ raw_mbox[3] = ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */ /* Issue a blocking command to the card */ if ((retval = issue_scb_block(adapter, raw_mbox))) { /* the adapter does not support 40ld */ mraid_ext_inquiry *ext_inq; mraid_inquiry *inq; dma_addr_t dma_handle; ext_inq = pci_alloc_consistent(adapter->dev, sizeof(mraid_ext_inquiry), &dma_handle); if( ext_inq == NULL ) return -1; inq = &ext_inq->raid_inq; mbox->m_out.xferaddr = (u32)dma_handle; /*issue old 0x04 command to adapter */ mbox->m_out.cmd = MEGA_MBOXCMD_ADPEXTINQ; issue_scb_block(adapter, raw_mbox); /* * update Enquiry3 and ProductInfo structures with * mraid_inquiry structure */ mega_8_to_40ld(inq, inquiry3, (mega_product_info *)&adapter->product_info); pci_free_consistent(adapter->dev, sizeof(mraid_ext_inquiry), ext_inq, dma_handle); } else { /*adapter supports 40ld */ adapter->flag |= BOARD_40LD; /* * get product_info, which is static information and will be * unchanged */ prod_info_dma_handle = pci_map_single(adapter->dev, (void *) &adapter->product_info, sizeof(mega_product_info), PCI_DMA_FROMDEVICE); mbox->m_out.xferaddr = prod_info_dma_handle; raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ raw_mbox[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */ if ((retval = issue_scb_block(adapter, raw_mbox))) printk(KERN_WARNING "megaraid: Product_info cmd failed with error: %d\n", retval); pci_unmap_single(adapter->dev, prod_info_dma_handle, sizeof(mega_product_info), PCI_DMA_FROMDEVICE); } /* * kernel scans the channels from 0 to <= max_channel */ adapter->host->max_channel = adapter->product_info.nchannels + NVIRT_CHAN -1; adapter->host->max_id = 16; /* max targets per channel */ adapter->host->max_lun = 7; /* Upto 7 luns for non disk devices */ adapter->host->cmd_per_lun = max_cmd_per_lun; adapter->numldrv = inquiry3->num_ldrv; adapter->max_cmds = adapter->product_info.max_commands; if(adapter->max_cmds > MAX_COMMANDS) adapter->max_cmds = MAX_COMMANDS; adapter->host->can_queue = adapter->max_cmds - 1; /* * Get the maximum number of scatter-gather elements supported by this * firmware */ mega_get_max_sgl(adapter); adapter->host->sg_tablesize = adapter->sglen; /* use HP firmware and bios version encoding */ if (adapter->product_info.subsysvid == HP_SUBSYS_VID) { sprintf (adapter->fw_version, "%c%d%d.%d%d", adapter->product_info.fw_version[2], adapter->product_info.fw_version[1] >> 8, adapter->product_info.fw_version[1] & 0x0f, adapter->product_info.fw_version[0] >> 8, adapter->product_info.fw_version[0] & 0x0f); sprintf (adapter->bios_version, "%c%d%d.%d%d", adapter->product_info.bios_version[2], adapter->product_info.bios_version[1] >> 8, adapter->product_info.bios_version[1] & 0x0f, adapter->product_info.bios_version[0] >> 8, adapter->product_info.bios_version[0] & 0x0f); } else { memcpy(adapter->fw_version, (char *)adapter->product_info.fw_version, 4); adapter->fw_version[4] = 0; memcpy(adapter->bios_version, (char *)adapter->product_info.bios_version, 4); adapter->bios_version[4] = 0; } printk(KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives.\n", adapter->fw_version, adapter->bios_version, adapter->numldrv); /* * Do we support extended (>10 bytes) cdbs */ adapter->support_ext_cdb = mega_support_ext_cdb(adapter); if (adapter->support_ext_cdb) printk(KERN_NOTICE "megaraid: supports extended CDBs.\n"); return 0;}/** * mega_runpendq() * @adapter - pointer to our soft state * * Runs through the list of pending requests. */static inline voidmega_runpendq(adapter_t *adapter){ if(!list_empty(&adapter->pending_list)) __mega_runpendq(adapter);}/* * megaraid_queue() * @scmd - Issue this scsi command * @done - the callback hook into the scsi mid-layer * * The command queuing entry point for the mid-layer. */static intmegaraid_queue(Scsi_Cmnd *scmd, void (*done)(Scsi_Cmnd *)){ adapter_t *adapter; scb_t *scb; int busy=0; unsigned long flags; adapter = (adapter_t *)scmd->device->host->hostdata; scmd->scsi_done = done; /* * Allocate and build a SCB request * busy flag will be set if mega_build_cmd() command could not * allocate scb. We will return non-zero status in that case. * NOTE: scb can be null even though certain commands completed * successfully, e.g., MODE_SENSE and TEST_UNIT_READY, we would * return 0 in that case. */ spin_lock_irqsave(&adapter->lock, flags); scb = mega_build_cmd(adapter, scmd, &busy); if (!scb) goto out; scb->state |= SCB_PENDQ; list_add_tail(&scb->list, &adapter->pending_list); /* * Check if the HBA is in quiescent state, e.g., during a * delete logical drive opertion. If it is, don't run * the pending_list. */ if (atomic_read(&adapter->quiescent) == 0) mega_runpendq(adapter); busy = 0; out: spin_unlock_irqrestore(&adapter->lock, flags); return busy;}/** * mega_allocate_scb() * @adapter - pointer to our soft state * @cmd - scsi command from the mid-layer * * Allocate a SCB structure. This is the central structure for controller * commands. */static inline scb_t *mega_allocate_scb(adapter_t *adapter, Scsi_Cmnd *cmd){ struct list_head *head = &adapter->free_list; scb_t *scb; /* Unlink command from Free List */ if( !list_empty(head) ) { scb = list_entry(head->next, scb_t, list); list_del_init(head->next); scb->state = SCB_ACTIVE; scb->cmd = cmd; scb->dma_type = MEGA_DMA_TYPE_NONE; return scb; } return NULL;}/** * mega_get_ldrv_num() * @adapter - pointer to our soft state * @cmd - scsi mid layer command * @channel - channel on the controller * * Calculate the logical drive number based on the information in scsi command * and the channel number. */static inline intmega_get_ldrv_num(adapter_t *adapter, Scsi_Cmnd *cmd, int channel){ int tgt; int ldrv_num; tgt = cmd->device->id; if ( tgt > adapter->this_id ) tgt--; /* we do not get inquires for initiator id */ ldrv_num = (channel * 15) + tgt; /* * If we have a logical drive with boot enabled, project it first */ if( adapter->boot_ldrv_enabled ) { if( ldrv_num == 0 ) { ldrv_num = adapter->boot_ldrv; } else { if( ldrv_num <= adapter->boot_ldrv ) { ldrv_num--; } } } /* * If "delete logical drive" feature is enabled on this controller. * Do only if at least one delete logical drive operation was done. * * Also, after logical drive deletion, instead of logical drive number, * the value returned should be 0x80+logical drive id. * * These is valid only for IO commands. */ if (adapter->support_random_del && adapter->read_ldidmap ) switch (cmd->cmnd[0]) { case READ_6: /* fall through */ case WRITE_6: /* fall through */ case READ_10: /* fall through */ case WRITE_10: ldrv_num += 0x80; } return ldrv_num;}/** * mega_build_cmd() * @adapter - pointer to our soft state * @cmd - Prepare using this scsi command * @busy - busy flag if no resources * * Prepares a command and scatter gather list for the controller. This routine * also finds out if the commands is intended for a logical drive or a * physical device and prepares the controller command accordingly. * * We also re-order the logical drives and physical devices based on their * boot settings. */static scb_t *mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy){ mega_ext_passthru *epthru; mega_passthru *pthru; scb_t *scb; mbox_t *mbox; long seg; char islogical; int max_ldrv_num; int channel = 0; int target = 0; int ldrv_num = 0; /* logical drive number */ /* * filter the internal and ioctl commands */ if((cmd->cmnd[0] == MEGA_INTERNAL_CMD)) return (scb_t *)cmd->host_scribble; /* * We know what channels our logical drives are on - mega_find_card() */ islogical = adapter->logdrv_chan[cmd->device->channel]; /* * The theory: If physical drive is chosen for boot, all the physical * devices are exported before the logical drives, otherwise physical * devices are pushed after logical drives, in which case - Kernel sees * the physical devices on virtual channel which is obviously converted * to actual channel on the HBA. */ if( adapter->boot_pdrv_enabled ) { if( islogical ) { /* logical channel */ channel = cmd->device->channel - adapter->product_info.nchannels; } else { /* this is physical channel */ channel = cmd->device->channel; target = cmd->device->id; /* * boot from a physical disk, that disk needs to be * exposed first IF both the channels are SCSI, then * booting from the second channel is not allowed. */ if( target == 0 ) { target = adapter->boot_pdrv_tgt; } else if( target == adapter->boot_pdrv_tgt ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -