📄 mptsas.c
字号:
/* * linux/drivers/message/fusion/mptsas.c * For use with LSI Logic PCI chip/adapter(s) * running LSI Logic Fusion MPT (Message Passing Technology) firmware. * * Copyright (c) 1999-2005 LSI Logic Corporation * (mailto:mpt_linux_developer@lsil.com) * Copyright (c) 2005 Dell *//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* 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; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. NO WARRANTY THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. DISCLAIMER OF LIABILITY NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/workqueue.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_transport_sas.h>#include "mptbase.h"#include "mptscsih.h"#define my_NAME "Fusion MPT SAS Host driver"#define my_VERSION MPT_LINUX_VERSION_COMMON#define MYNAM "mptsas"MODULE_AUTHOR(MODULEAUTHOR);MODULE_DESCRIPTION(my_NAME);MODULE_LICENSE("GPL");static int mpt_pq_filter;module_param(mpt_pq_filter, int, 0);MODULE_PARM_DESC(mpt_pq_filter, "Enable peripheral qualifier filter: enable=1 " "(default=0)");static int mpt_pt_clear;module_param(mpt_pt_clear, int, 0);MODULE_PARM_DESC(mpt_pt_clear, "Clear persistency table: enable=1 " "(default=MPTSCSIH_PT_CLEAR=0)");static int mptsasDoneCtx = -1;static int mptsasTaskCtx = -1;static int mptsasInternalCtx = -1; /* Used only for internal commands */static int mptsasMgmtCtx = -1;/* * SAS topology structures * * The MPT Fusion firmware interface spreads information about the * SAS topology over many manufacture pages, thus we need some data * structure to collect it and process it for the SAS transport class. */struct mptsas_devinfo { u16 handle; /* unique id to address this device */ u8 phy_id; /* phy number of parent device */ u8 port_id; /* sas physical port this device is assoc'd with */ u8 target; /* logical target id of this device */ u8 bus; /* logical bus number of this device */ u64 sas_address; /* WWN of this device, SATA is assigned by HBA,expander */ u32 device_info; /* bitfield detailed info about this device */};struct mptsas_phyinfo { u8 phy_id; /* phy index */ u8 port_id; /* port number this phy is part of */ u8 negotiated_link_rate; /* nego'd link rate for this phy */ u8 hw_link_rate; /* hardware max/min phys link rate */ u8 programmed_link_rate; /* programmed max/min phy link rate */ struct mptsas_devinfo identify; /* point to phy device info */ struct mptsas_devinfo attached; /* point to attached device info */ struct sas_rphy *rphy;};struct mptsas_portinfo { struct list_head list; u16 handle; /* unique id to address this */ u8 num_phys; /* number of phys */ struct mptsas_phyinfo *phy_info;};#ifdef SASDEBUGstatic void mptsas_print_phy_data(MPI_SAS_IO_UNIT0_PHY_DATA *phy_data){ printk("---- IO UNIT PAGE 0 ------------\n"); printk("Handle=0x%X\n", le16_to_cpu(phy_data->AttachedDeviceHandle)); printk("Controller Handle=0x%X\n", le16_to_cpu(phy_data->ControllerDevHandle)); printk("Port=0x%X\n", phy_data->Port); printk("Port Flags=0x%X\n", phy_data->PortFlags); printk("PHY Flags=0x%X\n", phy_data->PhyFlags); printk("Negotiated Link Rate=0x%X\n", phy_data->NegotiatedLinkRate); printk("Controller PHY Device Info=0x%X\n", le32_to_cpu(phy_data->ControllerPhyDeviceInfo)); printk("DiscoveryStatus=0x%X\n", le32_to_cpu(phy_data->DiscoveryStatus)); printk("\n");}static void mptsas_print_phy_pg0(SasPhyPage0_t *pg0){ __le64 sas_address; memcpy(&sas_address, &pg0->SASAddress, sizeof(__le64)); printk("---- SAS PHY PAGE 0 ------------\n"); printk("Attached Device Handle=0x%X\n", le16_to_cpu(pg0->AttachedDevHandle)); printk("SAS Address=0x%llX\n", (unsigned long long)le64_to_cpu(sas_address)); printk("Attached PHY Identifier=0x%X\n", pg0->AttachedPhyIdentifier); printk("Attached Device Info=0x%X\n", le32_to_cpu(pg0->AttachedDeviceInfo)); printk("Programmed Link Rate=0x%X\n", pg0->ProgrammedLinkRate); printk("Change Count=0x%X\n", pg0->ChangeCount); printk("PHY Info=0x%X\n", le32_to_cpu(pg0->PhyInfo)); printk("\n");}static void mptsas_print_phy_pg1(SasPhyPage1_t *pg1){ printk("---- SAS PHY PAGE 1 ------------\n"); printk("Invalid Dword Count=0x%x\n", pg1->InvalidDwordCount); printk("Running Disparity Error Count=0x%x\n", pg1->RunningDisparityErrorCount); printk("Loss Dword Synch Count=0x%x\n", pg1->LossDwordSynchCount); printk("PHY Reset Problem Count=0x%x\n", pg1->PhyResetProblemCount); printk("\n");}static void mptsas_print_device_pg0(SasDevicePage0_t *pg0){ __le64 sas_address; memcpy(&sas_address, &pg0->SASAddress, sizeof(__le64)); printk("---- SAS DEVICE PAGE 0 ---------\n"); printk("Handle=0x%X\n" ,le16_to_cpu(pg0->DevHandle)); printk("Enclosure Handle=0x%X\n", le16_to_cpu(pg0->EnclosureHandle)); printk("Slot=0x%X\n", le16_to_cpu(pg0->Slot)); printk("SAS Address=0x%llX\n", le64_to_cpu(sas_address)); printk("Target ID=0x%X\n", pg0->TargetID); printk("Bus=0x%X\n", pg0->Bus); /* The PhyNum field specifies the PHY number of the parent * device this device is linked to */ printk("Parent Phy Num=0x%X\n", pg0->PhyNum); printk("Access Status=0x%X\n", le16_to_cpu(pg0->AccessStatus)); printk("Device Info=0x%X\n", le32_to_cpu(pg0->DeviceInfo)); printk("Flags=0x%X\n", le16_to_cpu(pg0->Flags)); printk("Physical Port=0x%X\n", pg0->PhysicalPort); printk("\n");}static void mptsas_print_expander_pg1(SasExpanderPage1_t *pg1){ printk("---- SAS EXPANDER PAGE 1 ------------\n"); printk("Physical Port=0x%X\n", pg1->PhysicalPort); printk("PHY Identifier=0x%X\n", pg1->PhyIdentifier); printk("Negotiated Link Rate=0x%X\n", pg1->NegotiatedLinkRate); printk("Programmed Link Rate=0x%X\n", pg1->ProgrammedLinkRate); printk("Hardware Link Rate=0x%X\n", pg1->HwLinkRate); printk("Owner Device Handle=0x%X\n", le16_to_cpu(pg1->OwnerDevHandle)); printk("Attached Device Handle=0x%X\n", le16_to_cpu(pg1->AttachedDevHandle));}#else#define mptsas_print_phy_data(phy_data) do { } while (0)#define mptsas_print_phy_pg0(pg0) do { } while (0)#define mptsas_print_phy_pg1(pg1) do { } while (0)#define mptsas_print_device_pg0(pg0) do { } while (0)#define mptsas_print_expander_pg1(pg1) do { } while (0)#endif/* * This is pretty ugly. We will be able to seriously clean it up * once the DV code in mptscsih goes away and we can properly * implement ->target_alloc. */static intmptsas_slave_alloc(struct scsi_device *device){ struct Scsi_Host *host = device->host; MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; struct sas_rphy *rphy; struct mptsas_portinfo *p; VirtDevice *vdev; uint target = device->id; int i; if ((vdev = hd->Targets[target]) != NULL) goto out; vdev = kmalloc(sizeof(VirtDevice), GFP_KERNEL); if (!vdev) { printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n", hd->ioc->name, sizeof(VirtDevice)); return -ENOMEM; } memset(vdev, 0, sizeof(VirtDevice)); vdev->tflags = MPT_TARGET_FLAGS_Q_YES|MPT_TARGET_FLAGS_VALID_INQUIRY; vdev->ioc_id = hd->ioc->id; rphy = dev_to_rphy(device->sdev_target->dev.parent); list_for_each_entry(p, &hd->ioc->sas_topology, list) { for (i = 0; i < p->num_phys; i++) { if (p->phy_info[i].attached.sas_address == rphy->identify.sas_address) { vdev->target_id = p->phy_info[i].attached.target; vdev->bus_id = p->phy_info[i].attached.bus; hd->Targets[device->id] = vdev; goto out; } } } printk("No matching SAS device found!!\n"); kfree(vdev); return -ENODEV; out: vdev->num_luns++; device->hostdata = vdev; return 0;}static struct scsi_host_template mptsas_driver_template = { .proc_name = "mptsas", .proc_info = mptscsih_proc_info, .name = "MPT SPI Host", .info = mptscsih_info, .queuecommand = mptscsih_qcmd, .slave_alloc = mptsas_slave_alloc, .slave_configure = mptscsih_slave_configure, .slave_destroy = mptscsih_slave_destroy, .change_queue_depth = mptscsih_change_queue_depth, .eh_abort_handler = mptscsih_abort, .eh_device_reset_handler = mptscsih_dev_reset, .eh_bus_reset_handler = mptscsih_bus_reset, .eh_host_reset_handler = mptscsih_host_reset, .bios_param = mptscsih_bios_param, .can_queue = MPT_FC_CAN_QUEUE, .this_id = -1, .sg_tablesize = MPT_SCSI_SG_DEPTH, .max_sectors = 8192, .cmd_per_lun = 7, .use_clustering = ENABLE_CLUSTERING,};static inline MPT_ADAPTER *phy_to_ioc(struct sas_phy *phy){ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;}static int mptsas_get_linkerrors(struct sas_phy *phy){ MPT_ADAPTER *ioc = phy_to_ioc(phy); ConfigExtendedPageHeader_t hdr; CONFIGPARMS cfg; SasPhyPage1_t *buffer; dma_addr_t dma_handle; int error; hdr.PageVersion = MPI_SASPHY1_PAGEVERSION; hdr.ExtPageLength = 0; hdr.PageNumber = 1 /* page number 1*/; hdr.Reserved1 = 0; hdr.Reserved2 = 0; hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_PHY; cfg.cfghdr.ehdr = &hdr; cfg.physAddr = -1; cfg.pageAddr = phy->identify.phy_identifier; cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; cfg.dir = 0; /* read */ cfg.timeout = 10; error = mpt_config(ioc, &cfg); if (error) return error; if (!hdr.ExtPageLength) return -ENXIO; buffer = pci_alloc_consistent(ioc->pcidev, hdr.ExtPageLength * 4, &dma_handle); if (!buffer) return -ENOMEM; cfg.physAddr = dma_handle; cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; error = mpt_config(ioc, &cfg); if (error) goto out_free_consistent; mptsas_print_phy_pg1(buffer); phy->invalid_dword_count = le32_to_cpu(buffer->InvalidDwordCount); phy->running_disparity_error_count = le32_to_cpu(buffer->RunningDisparityErrorCount); phy->loss_of_dword_sync_count = le32_to_cpu(buffer->LossDwordSynchCount); phy->phy_reset_problem_count = le32_to_cpu(buffer->PhyResetProblemCount); out_free_consistent: pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4, buffer, dma_handle); return error;}static int mptsas_mgmt_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply){ ioc->sas_mgmt.status |= MPT_SAS_MGMT_STATUS_COMMAND_GOOD; if (reply != NULL) { ioc->sas_mgmt.status |= MPT_SAS_MGMT_STATUS_RF_VALID; memcpy(ioc->sas_mgmt.reply, reply, min(ioc->reply_sz, 4 * reply->u.reply.MsgLength)); } complete(&ioc->sas_mgmt.done); return 1;}static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset){ MPT_ADAPTER *ioc = phy_to_ioc(phy); SasIoUnitControlRequest_t *req; SasIoUnitControlReply_t *reply; MPT_FRAME_HDR *mf; MPIHeader_t *hdr; unsigned long timeleft; int error = -ERESTARTSYS; /* not implemented for expanders */ if (phy->identify.target_port_protocols & SAS_PROTOCOL_SMP) return -ENXIO; if (down_interruptible(&ioc->sas_mgmt.mutex)) goto out; mf = mpt_get_msg_frame(mptsasMgmtCtx, ioc); if (!mf) { error = -ENOMEM; goto out_unlock; } hdr = (MPIHeader_t *) mf; req = (SasIoUnitControlRequest_t *)mf; memset(req, 0, sizeof(SasIoUnitControlRequest_t)); req->Function = MPI_FUNCTION_SAS_IO_UNIT_CONTROL; req->MsgContext = hdr->MsgContext; req->Operation = hard_reset ? MPI_SAS_OP_PHY_HARD_RESET : MPI_SAS_OP_PHY_LINK_RESET; req->PhyNum = phy->identify.phy_identifier; mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf); timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ); if (!timeleft) { /* On timeout reset the board */ mpt_free_msg_frame(ioc, mf); mpt_HardResetHandler(ioc, CAN_SLEEP); error = -ETIMEDOUT; goto out_unlock; } /* a reply frame is expected */ if ((ioc->sas_mgmt.status & MPT_IOCTL_STATUS_RF_VALID) == 0) { error = -ENXIO; goto out_unlock; } /* process the completed Reply Message Frame */ reply = (SasIoUnitControlReply_t *)ioc->sas_mgmt.reply; if (reply->IOCStatus != MPI_IOCSTATUS_SUCCESS) { printk("%s: IOCStatus=0x%X IOCLogInfo=0x%X\n", __FUNCTION__, reply->IOCStatus, reply->IOCLogInfo); error = -ENXIO; goto out_unlock; } error = 0; out_unlock: up(&ioc->sas_mgmt.mutex); out: return error;}static struct sas_function_template mptsas_transport_functions = { .get_linkerrors = mptsas_get_linkerrors, .phy_reset = mptsas_phy_reset,};static struct scsi_transport_template *mptsas_transport_template;static intmptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info){ ConfigExtendedPageHeader_t hdr; CONFIGPARMS cfg; SasIOUnitPage0_t *buffer; dma_addr_t dma_handle; int error, i; hdr.PageVersion = MPI_SASIOUNITPAGE0_PAGEVERSION; hdr.ExtPageLength = 0; hdr.PageNumber = 0; hdr.Reserved1 = 0; hdr.Reserved2 = 0; hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -