/* * ipr.c -- driver for IBM Power Linux RAID adapters * * Written By: Brian King , IBM Corporation * * Copyright (C) 2003, 2004 IBM 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. * * 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. * * 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 * */ /* * Notes: * * This driver is used to control the following SCSI adapters: * * IBM iSeries: 5702, 5703, 2780, 5709, 570A, 570B * * IBM pSeries: PCI-X Dual Channel Ultra 320 SCSI RAID Adapter * PCI-X Dual Channel Ultra 320 SCSI Adapter * PCI-X Dual Channel Ultra 320 SCSI RAID Enablement Card * Embedded SCSI adapter on p615 and p655 systems * * Supported Hardware Features: * - Ultra 320 SCSI controller * - PCI-X host interface * - Embedded PowerPC RISC Processor and Hardware XOR DMA Engine * - Non-Volatile Write Cache * - Supports attachment of non-RAID disks, tape, and optical devices * - RAID Levels 0, 5, 10 * - Hot spare * - Background Parity Checking * - Background Data Scrubbing * - Ability to increase the capacity of an existing RAID 5 disk array * by adding disks * * Driver Features: * - Tagged command queuing * - Adapter microcode download * - PCI hot plug * - SCSI device hot plug * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipr.h" /* * Global Data */ static LIST_HEAD(ipr_ioa_head); static unsigned int ipr_log_level = IPR_DEFAULT_LOG_LEVEL; static unsigned int ipr_max_speed = 1; static int ipr_testmode = 0; static unsigned int ipr_fastfail = 0; static unsigned int ipr_transop_timeout = 0; static unsigned int ipr_debug = 0; static unsigned int ipr_max_devs = IPR_DEFAULT_SIS64_DEVS; static unsigned int ipr_dual_ioa_raid = 1; static unsigned int ipr_number_of_msix = 2; static DEFINE_SPINLOCK(ipr_driver_lock); /* This table describes the differences between DMA controller chips */ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = { { /* Gemstone, Citrine, Obsidian, and Obsidian-E */ .mailbox = 0x0042C, .max_cmds = 100, .cache_line_size = 0x20, .clear_isr = 1, .iopoll_weight = 0, { .set_interrupt_mask_reg = 0x0022C, .clr_interrupt_mask_reg = 0x00230, .clr_interrupt_mask_reg32 = 0x00230, .sense_interrupt_mask_reg = 0x0022C, .sense_interrupt_mask_reg32 = 0x0022C, .clr_interrupt_reg = 0x00228, .clr_interrupt_reg32 = 0x00228, .sense_interrupt_reg = 0x00224, .sense_interrupt_reg32 = 0x00224, .ioarrin_reg = 0x00404, .sense_uproc_interrupt_reg = 0x00214, .sense_uproc_interrupt_reg32 = 0x00214, .set_uproc_interrupt_reg = 0x00214, .set_uproc_interrupt_reg32 = 0x00214, .clr_uproc_interrupt_reg = 0x00218, .clr_uproc_interrupt_reg32 = 0x00218 } }, { /* Snipe and Scamp */ .mailbox = 0x0052C, .max_cmds = 100, .cache_line_size = 0x20, .clear_isr = 1, .iopoll_weight = 0, { .set_interrupt_mask_reg = 0x00288, .clr_interrupt_mask_reg = 0x0028C, .clr_interrupt_mask_reg32 = 0x0028C, .sense_interrupt_mask_reg = 0x00288, .sense_interrupt_mask_reg32 = 0x00288, .clr_interrupt_reg = 0x00284, .clr_interrupt_reg32 = 0x00284, .sense_interrupt_reg = 0x00280, .sense_interrupt_reg32 = 0x00280, .ioarrin_reg = 0x00504, .sense_uproc_interrupt_reg = 0x00290, .sense_uproc_interrupt_reg32 = 0x00290, .set_uproc_interrupt_reg = 0x00290, .set_uproc_interrupt_reg32 = 0x00290, .clr_uproc_interrupt_reg = 0x00294, .clr_uproc_interrupt_reg32 = 0x00294 } }, { /* CRoC */ .mailbox = 0x00044, .max_cmds = 1000, .cache_line_size = 0x20, .clear_isr = 0, .iopoll_weight = 64, { .set_interrupt_mask_reg = 0x00010, .clr_interrupt_mask_reg = 0x00018, .clr_interrupt_mask_reg32 = 0x0001C, .sense_interrupt_mask_reg = 0x00010, .sense_interrupt_mask_reg32 = 0x00014, .clr_interrupt_reg = 0x00008, .clr_interrupt_reg32 = 0x0000C, .sense_interrupt_reg = 0x00000, .sense_interrupt_reg32 = 0x00004, .ioarrin_reg = 0x00070, .sense_uproc_interrupt_reg = 0x00020, .sense_uproc_interrupt_reg32 = 0x00024, .set_uproc_interrupt_reg = 0x00020, .set_uproc_interrupt_reg32 = 0x00024, .clr_uproc_interrupt_reg = 0x00028, .clr_uproc_interrupt_reg32 = 0x0002C, .init_feedback_reg = 0x0005C, .dump_addr_reg = 0x00064, .dump_data_reg = 0x00068, .endian_swap_reg = 0x00084 } }, }; static const struct ipr_chip_t ipr_chip[] = { { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] }, { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E, IPR_USE_MSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[1] }, { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[1] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2, IPR_USE_MSI, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE, IPR_USE_MSI, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] } }; static int ipr_max_bus_speeds[] = { IPR_80MBs_SCSI_RATE, IPR_U160_SCSI_RATE, IPR_U320_SCSI_RATE }; MODULE_AUTHOR("Brian King "); MODULE_DESCRIPTION("IBM Power RAID SCSI Adapter Driver"); module_param_named(max_speed, ipr_max_speed, uint, 0); MODULE_PARM_DESC(max_speed, "Maximum bus speed (0-2). Default: 1=U160. Speeds: 0=80 MB/s, 1=U160, 2=U320"); module_param_named(log_level, ipr_log_level, uint, 0); MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver"); module_param_named(testmode, ipr_testmode, int, 0); MODULE_PARM_DESC(testmode, "DANGEROUS!!! Allows unsupported configurations"); module_param_named(fastfail, ipr_fastfail, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(fastfail, "Reduce timeouts and retries"); module_param_named(transop_timeout, ipr_transop_timeout, int, 0); MODULE_PARM_DESC(transop_timeout, "Time in seconds to wait for adapter to come operational (default: 300)"); module_param_named(debug, ipr_debug, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Enable device driver debugging logging. Set to 1 to enable. (default: 0)"); module_param_named(dual_ioa_raid, ipr_dual_ioa_raid, int, 0); MODULE_PARM_DESC(dual_ioa_raid, "Enable dual adapter RAID support. Set to 1 to enable. (default: 1)"); module_param_named(max_devs, ipr_max_devs, int, 0); MODULE_PARM_DESC(max_devs, "Specify the maximum number of physical devices. " "[Default=" __stringify(IPR_DEFAULT_SIS64_DEVS) "]"); module_param_named(number_of_msix, ipr_number_of_msix, int, 0); MODULE_PARM_DESC(number_of_msix, "Specify the number of MSIX interrupts to use on capable adapters (1 - 5). (default:2)"); MODULE_LICENSE("GPL"); MODULE_VERSION(IPR_DRIVER_VERSION); /* A constant array of IOASCs/URCs/Error Messages */ static const struct ipr_error_table_t ipr_error_table[] = { {0x00000000, 1, IPR_DEFAULT_LOG_LEVEL, "8155: An unknown error was received"}, {0x00330000, 0, 0, "Soft underlength error"}, {0x005A0000, 0, 0, "Command to be cancelled not found"}, {0x00808000, 0, 0, "Qualified success"}, {0x01080000, 1, IPR_DEFAULT_LOG_LEVEL, "FFFE: Soft device bus error recovered by the IOA"}, {0x01088100, 0, IPR_DEFAULT_LOG_LEVEL, "4101: Soft device bus fabric error"}, {0x01100100, 0, IPR_DEFAULT_LOG_LEVEL, "FFFC: Logical block guard error recovered by the device"}, {0x01100300, 0, IPR_DEFAULT_LOG_LEVEL, "FFFC: Logical block reference tag error recovered by the device"}, {0x01108300, 0, IPR_DEFAULT_LOG_LEVEL, "4171: Recovered scatter list tag / sequence number error"}, {0x01109000, 0, IPR_DEFAULT_LOG_LEVEL, "FF3D: Recovered logical block CRC error on IOA to Host transfer"}, {0x01109200, 0, IPR_DEFAULT_LOG_LEVEL, "4171: Recovered logical block sequence number error on IOA to Host transfer"}, {0x0110A000, 0, IPR_DEFAULT_LOG_LEVEL, "FFFD: Recovered logical block reference tag error detected by the IOA"}, {0x0110A100, 0, IPR_DEFAULT_LOG_LEVEL, "FFFD: Logical block guard error recovered by the IOA"}, {0x01170600, 0, IPR_DEFAULT_LOG_LEVEL, "FFF9: Device sector reassign successful"}, {0x01170900, 0, IPR_DEFAULT_LOG_LEVEL, "FFF7: Media error recovered by device rewrite procedures"}, {0x01180200, 0, IPR_DEFAULT_LOG_LEVEL, "7001: IOA sector reassignment successful"}, {0x01180500, 0, IPR_DEFAULT_LOG_LEVEL, "FFF9: Soft media error. Sector reassignment recommended"}, {0x01180600, 0, IPR_DEFAULT_LOG_LEVEL, "FFF7: Media error recovered by IOA rewrite procedures"}, {0x01418000, 0, IPR_DEFAULT_LOG_LEVEL, "FF3D: Soft PCI bus error recovered by the IOA"}, {0x01440000, 1, IPR_DEFAULT_LOG_LEVEL, "FFF6: Device hardware error recovered by the IOA"}, {0x01448100, 0, IPR_DEFAULT_LOG_LEVEL, "FFF6: Device hardware error recovered by the device"}, {0x01448200, 1, IPR_DEFAULT_LOG_LEVEL, "FF3D: Soft IOA error recovered by the IOA"}, {0x01448300, 0, IPR_DEFAULT_LOG_LEVEL, "FFFA: Undefined device response recovered by the IOA"}, {0x014A0000, 1, IPR_DEFAULT_LOG_LEVEL, "FFF6: Device bus error, message or command phase"}, {0x014A8000, 0, IPR_DEFAULT_LOG_LEVEL, "FFFE: Task Management Function failed"}, {0x015D0000, 0, IPR_DEFAULT_LOG_LEVEL, "FFF6: Failure prediction threshold exceeded"}, {0x015D9200, 0, IPR_DEFAULT_LOG_LEVEL, "8009: Impending cache battery pack failure"}, {0x02040400, 0, 0, "34FF: Disk device format in progress"}, {0x02048000, 0, IPR_DEFAULT_LOG_LEVEL, "9070: IOA requested reset"}, {0x023F0000, 0, 0, "Synchronization required"}, {0x024E0000, 0, 0, "No ready, IOA shutdown"}, {0x025A0000, 0, 0, "Not ready, IOA has been shutdown"}, {0x02670100, 0, IPR_DEFAULT_LOG_LEVEL, "3020: Storage subsystem configuration error"}, {0x03110B00, 0, 0, "FFF5: Medium error, data unreadable, recommend reassign"}, {0x03110C00, 0, 0, "7000: Medium error, data unreadable, do not reassign"}, {0x03310000, 0, IPR_DEFAULT_LOG_LEVEL, "FFF3: Disk media format bad"}, {0x04050000, 0, IPR_DEFAULT_LOG_LEVEL, "3002: Addressed device failed to respond to selection"}, {0x04080000, 1, IPR_DEFAULT_LOG_LEVEL, "3100: Device bus error"}, {0x04080100, 0, IPR_DEFAULT_LOG_LEVEL, "3109: IOA timed out a device command"}, {0x04088000, 0, 0, "3120: SCSI bus is not operational"}, {0x04088100, 0, IPR_DEFAULT_LOG_LEVEL, "4100: Hard device bus fabric error"}, {0x04100100, 0, IPR_DEFAULT_LOG_LEVEL, "310C: Logical block guard error detected by the device"}, {0x04100300, 0, IPR_DEFAULT_LOG_LEVEL, "310C: Logical block reference tag error detected by the device"}, {0x04108300, 1, IPR_DEFAULT_LOG_LEVEL, "4170: Scatter list tag / sequence number error"}, {0x04109000, 1, IPR_DEFAULT_LOG_LEVEL, "8150: Logical block CRC error on IOA to Host transfer"}, {0x04109200, 1, IPR_DEFAULT_LOG_LEVEL, "4170: Logical block sequence number error on IOA to Host transfer"}, {0x0410A000, 0, IPR_DEFAULT_LOG_LEVEL, "310D: Logical block reference tag error detected by the IOA"}, {0x0410A100, 0, IPR_DEFAULT_LOG_LEVEL, "310D: Logical block guard error detected by the IOA"}, {0x04118000, 0, IPR_DEFAULT_LOG_LEVEL, "9000: IOA reserved area data check"}, {0x04118100, 0, IPR_DEFAULT_LOG_LEVEL, "9001: IOA reserved area invalid data pattern"}, {0x04118200, 0, IPR_DEFAULT_LOG_LEVEL, "9002: IOA reserved area LRC error"}, {0x04118300, 1, IPR_DEFAULT_LOG_LEVEL, "Hardware Error, IOA metadata access error"}, {0x04320000, 0, IPR_DEFAULT_LOG_LEVEL, "102E: Out of alternate sectors for disk storage"}, {0x04330000, 1, IPR_DEFAULT_LOG_LEVEL, "FFF4: Data transfer underlength error"}, {0x04338000, 1, IPR_DEFAULT_LOG_LEVEL, "FFF4: Data transfer overlength error"}, {0x043E0100, 0, IPR_DEFAULT_LOG_LEVEL, "3400: Logical unit failure"}, {0x04408500, 0, IPR_DEFAULT_LOG_LEVEL, "FFF4: Device microcode is corrupt"}, {0x04418000, 1, IPR_DEFAULT_LOG_LEVEL, "8150: PCI bus error"}, {0x04430000, 1, 0, "Unsupported device bus message received"}, {0x04440000, 1, IPR_DEFAULT_LOG_LEVEL, "FFF4: Disk device problem"}, {0x04448200, 1, IPR_DEFAULT_LOG_LEVEL, "8150: Permanent IOA failure"}, {0x04448300, 0, IPR_DEFAULT_LOG_LEVEL, "3010: Disk device returned wrong response to IOA"}, {0x04448400, 0, IPR_DEFAULT_LOG_LEVEL, "8151: IOA microcode error"}, {0x04448500, 0, 0, "Device bus status error"}, {0x04448600, 0, IPR_DEFAULT_LOG_LEVEL, "8157: IOA error requiring IOA reset to recover"}, {0x04448700, 0, 0, "ATA device status error"}, {0x04490000, 0, 0, "Message reject received from the device"}, {0x04449200, 0, IPR_DEFAULT_LOG_LEVEL, "8008: A permanent cache battery pack failure occurred"}, {0x0444A000, 0, IPR_DEFAULT_LOG_LEVEL, "9090: Disk unit has been modified after the last known status"}, {0x0444A200, 0, IPR_DEFAULT_LOG_LEVEL, "9081: IOA detected device error"}, {0x0444A300, 0, IPR_DEFAULT_LOG_LEVEL, "9082: IOA detected device error"}, {0x044A0000, 1, IPR_DEFAULT_LOG_LEVEL, "3110: Device bus error, message or command phase"}, {0x044A8000, 1, IPR_DEFAULT_LOG_LEVEL, "3110: SAS Command / Task Management Function failed"}, {0x04670400, 0, IPR_DEFAULT_LOG_LEVEL, "9091: Incorrect hardware configuration change has been detected"}, {0x04678000, 0, IPR_DEFAULT_LOG_LEVEL, "9073: Invalid multi-adapter configuration"}, {0x04678100, 0, IPR_DEFAULT_LOG_LEVEL, "4010: Incorrect connection between cascaded expanders"}, {0x04678200, 0, IPR_DEFAULT_LOG_LEVEL, "4020: Connections exceed IOA design limits"}, {0x04678300, 0, IPR_DEFAULT_LOG_LEVEL, "4030: Incorrect multipath connection"}, {0x04679000, 0, IPR_DEFAULT_LOG_LEVEL, "4110: Unsupported enclosure function"}, {0x046E0000, 0, IPR_DEFAULT_LOG_LEVEL, "FFF4: Command to logical unit failed"}, {0x05240000, 1, 0, "Illegal request, invalid request type or request packet"}, {0x05250000, 0, 0, "Illegal request, invalid resource handle"}, {0x05258000, 0, 0, "Illegal request, commands not allowed to this device"}, {0x05258100, 0, 0, "Illegal request, command not allowed to a secondary adapter"}, {0x05258200, 0, 0, "Illegal request, command not allowed to a non-optimized resource"}, {0x05260000, 0, 0, "Illegal request, invalid field in parameter list"}, {0x05260100, 0, 0, "Illegal request, parameter not supported"}, {0x05260200, 0, 0, "Illegal request, parameter value invalid"}, {0x052C0000, 0, 0, "Illegal request, command sequence error"}, {0x052C8000, 1, 0, "Illegal request, dual adapter support not enabled"}, {0x06040500, 0, IPR_DEFAULT_LOG_LEVEL, "9031: Array protection temporarily suspended, protection resuming"}, {0x06040600, 0, IPR_DEFAULT_LOG_LEVEL, "9040: Array protection temporarily suspended, protection resuming"}, {0x06288000, 0, IPR_DEFAULT_LOG_LEVEL, "3140: Device bus not ready to ready transition"}, {0x06290000, 0, IPR_DEFAULT_LOG_LEVEL, "FFFB: SCSI bus was reset"}, {0x06290500, 0, 0, "FFFE: SCSI bus transition to single ended"}, {0x06290600, 0, 0, "FFFE: SCSI bus transition to LVD"}, {0x06298000, 0, IPR_DEFAULT_LOG_LEVEL, "FFFB: SCSI bus was reset by another initiator"}, {0x063F0300, 0, IPR_DEFAULT_LOG_LEVEL, "3029: A device replacement has occurred"}, {0x064C8000, 0, IPR_DEFAULT_LOG_LEVEL, "9051: IOA cache data exists for a missing or failed device"}, {0x064C8100, 0, IPR_DEFAULT_LOG_LEVEL, "9055: Auxiliary cache IOA contains cache data needed by the primary IOA"}, {0x06670100, 0, IPR_DEFAULT_LOG_LEVEL, "9025: Disk unit is not supported at its physical location"}, {0x06670600, 0, IPR_DEFAULT_LOG_LEVEL, "3020: IOA detected a SCSI bus configuration error"}, {0x06678000, 0, IPR_DEFAULT_LOG_LEVEL, "3150: SCSI bus configuration error"}, {0x06678100, 0, IPR_DEFAULT_LOG_LEVEL, "9074: Asymmetric advanced function disk configuration"}, {0x06678300, 0, IPR_DEFAULT_LOG_LEVEL, "4040: Incomplete multipath connection between IOA and enclosure"}, {0x06678400, 0, IPR_DEFAULT_LOG_LEVEL, "4041: Incomplete multipath connection between enclosure and device"}, {0x06678500, 0, IPR_DEFAULT_LOG_LEVEL, "9075: Incomplete multipath connection between IOA and remote IOA"}, {0x06678600, 0, IPR_DEFAULT_LOG_LEVEL, "9076: Configuration error, missing remote IOA"}, {0x06679100, 0, IPR_DEFAULT_LOG_LEVEL, "4050: Enclosure does not support a required multipath function"}, {0x06690000, 0, IPR_DEFAULT_LOG_LEVEL, "4070: Logically bad block written on device"}, {0x06690200, 0, IPR_DEFAULT_LOG_LEVEL, "9041: Array protection temporarily suspended"}, {0x06698200, 0, IPR_DEFAULT_LOG_LEVEL, "9042: Corrupt array parity detected on specified device"}, {0x066B0200, 0, IPR_DEFAULT_LOG_LEVEL, "9030: Array no longer protected due to missing or failed disk unit"}, {0x066B8000, 0, IPR_DEFAULT_LOG_LEVEL, "9071: Link operational transition"}, {0x066B8100, 0, IPR_DEFAULT_LOG_LEVEL, "9072: Link not operational transition"}, {0x066B8200, 0, IPR_DEFAULT_LOG_LEVEL, "9032: Array exposed but still protected"}, {0x066B8300, 0, IPR_DEFAULT_LOG_LEVEL + 1, "70DD: Device forced failed by disrupt device command"}, {0x066B9100, 0, IPR_DEFAULT_LOG_LEVEL, "4061: Multipath redundancy level got better"}, {0x066B9200, 0, IPR_DEFAULT_LOG_LEVEL, "4060: Multipath redundancy level got worse"}, {0x07270000, 0, 0, "Failure due to other device"}, {0x07278000, 0, IPR_DEFAULT_LOG_LEVEL, "9008: IOA does not support functions expected by devices"}, {0x07278100, 0, IPR_DEFAULT_LOG_LEVEL, "9010: Cache data associated with attached devices cannot be found"}, {0x07278200, 0, IPR_DEFAULT_LOG_LEVEL, "9011: Cache data belongs to devices other than those attached"}, {0x07278400, 0, IPR_DEFAULT_LOG_LEVEL, "9020: Array missing 2 or more devices with only 1 device present"}, {0x07278500, 0, IPR_DEFAULT_LOG_LEVEL, "9021: Array missing 2 or more devices with 2 or more devices present"}, {0x07278600, 0, IPR_DEFAULT_LOG_LEVEL, "9022: Exposed array is missing a required device"}, {0x07278700, 0, IPR_DEFAULT_LOG_LEVEL, "9023: Array member(s) not at required physical locations"}, {0x07278800, 0, IPR_DEFAULT_LOG_LEVEL, "9024: Array not functional due to present hardware configuration"}, {0x07278900, 0, IPR_DEFAULT_LOG_LEVEL, "9026: Array not functional due to present hardware configuration"}, {0x07278A00, 0, IPR_DEFAULT_LOG_LEVEL, "9027: Array is missing a device and parity is out of sync"}, {0x07278B00, 0, IPR_DEFAULT_LOG_LEVEL, "9028: Maximum number of arrays already exist"}, {0x07278C00, 0, IPR_DEFAULT_LOG_LEVEL, "9050: Required cache data cannot be located for a disk unit"}, {0x07278D00, 0, IPR_DEFAULT_LOG_LEVEL, "9052: Cache data exists for a device that has been modified"}, {0x07278F00, 0, IPR_DEFAULT_LOG_LEVEL, "9054: IOA resources not available due to previous problems"}, {0x07279100, 0, IPR_DEFAULT_LOG_LEVEL, "9092: Disk unit requires initialization before use"}, {0x07279200, 0, IPR_DEFAULT_LOG_LEVEL, "9029: Incorrect hardware configuration change has been detected"}, {0x07279600, 0, IPR_DEFAULT_LOG_LEVEL, "9060: One or more disk pairs are missing from an array"}, {0x07279700, 0, IPR_DEFAULT_LOG_LEVEL, "9061: One or more disks are missing from an array"}, {0x07279800, 0, IPR_DEFAULT_LOG_LEVEL, "9062: One or more disks are missing from an array"}, {0x07279900, 0, IPR_DEFAULT_LOG_LEVEL, "9063: Maximum number of functional arrays has been exceeded"}, {0x0B260000, 0, 0, "Aborted command, invalid descriptor"}, {0x0B5A0000, 0, 0, "Command terminated by host"} }; static const struct ipr_ses_table_entry ipr_ses_table[] = { { "2104-DL1 ", "XXXXXXXXXXXXXXXX", 80 }, { "2104-TL1 ", "XXXXXXXXXXXXXXXX", 80 }, { "HSBP07M P U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Hidive 7 slot */ { "HSBP05M P U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Hidive 5 slot */ { "HSBP05M S U2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* Bowtie */ { "HSBP06E ASU2SCSI", "XXXXXXXXXXXXXXXX", 80 }, /* MartinFenning */ { "2104-DU3 ", "XXXXXXXXXXXXXXXX", 160 }, { "2104-TU3 ", "XXXXXXXXXXXXXXXX", 160 }, { "HSBP04C RSU2SCSI", "XXXXXXX*XXXXXXXX", 160 }, { "HSBP06E RSU2SCSI", "XXXXXXX*XXXXXXXX", 160 }, { "St V1S2 ", "XXXXXXXXXXXXXXXX", 160 }, { "HSBPD4M PU3SCSI", "XXXXXXX*XXXXXXXX", 160 }, { "VSBPD1H U3SCSI", "XXXXXXX*XXXXXXXX", 160 } }; /* * Function Prototypes */ static int ipr_reset_alert(struct ipr_cmnd *); static void ipr_process_ccn(struct ipr_cmnd *); static void ipr_process_error(struct ipr_cmnd *); static void ipr_reset_ioa_job(struct ipr_cmnd *); static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *, enum ipr_shutdown_type); #ifdef CONFIG_SCSI_IPR_TRACE /** * ipr_trc_hook - Add a trace entry to the driver trace * @ipr_cmd: ipr command struct * @type: trace type * @add_data: additional data * * Return value: * none **/ static void ipr_trc_hook(struct ipr_cmnd *ipr_cmd, u8 type, u32 add_data) { struct ipr_trace_entry *trace_entry; struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; trace_entry = &ioa_cfg->trace[atomic_add_return (1, &ioa_cfg->trace_index)%IPR_NUM_TRACE_ENTRIES]; trace_entry->time = jiffies; trace_entry->op_code = ipr_cmd->ioarcb.cmd_pkt.cdb[0]; trace_entry->type = type; if (ipr_cmd->ioa_cfg->sis64) trace_entry->ata_op_code = ipr_cmd->i.ata_ioadl.regs.command; else trace_entry->ata_op_code = ipr_cmd->ioarcb.u.add_data.u.regs.command; trace_entry->cmd_index = ipr_cmd->cmd_index & 0xff; trace_entry->res_handle = ipr_cmd->ioarcb.res_handle; trace_entry->u.add_data = add_data; wmb(); } #else #define ipr_trc_hook(ipr_cmd, type, add_data) do { } while (0) #endif /** * ipr_lock_and_done - Acquire lock and complete command * @ipr_cmd: ipr command struct * * Return value: * none **/ static void ipr_lock_and_done(struct ipr_cmnd *ipr_cmd) { unsigned long lock_flags; struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); ipr_cmd->done(ipr_cmd); spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); } /** * ipr_reinit_ipr_cmnd - Re-initialize an IPR Cmnd block for reuse * @ipr_cmd: ipr command struct * * Return value: * none **/ static void ipr_reinit_ipr_cmnd(struct ipr_cmnd *ipr_cmd) { struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; struct ipr_ioasa *ioasa = &ipr_cmd->s.ioasa; struct ipr_ioasa64 *ioasa64 = &ipr_cmd->s.ioasa64; dma_addr_t dma_addr = ipr_cmd->dma_addr; int hrrq_id; hrrq_id = ioarcb->cmd_pkt.hrrq_id; memset(&ioarcb->cmd_pkt, 0, sizeof(struct ipr_cmd_pkt)); ioarcb->cmd_pkt.hrrq_id = hrrq_id; ioarcb->data_transfer_length = 0; ioarcb->read_data_transfer_length = 0; ioarcb->ioadl_len = 0; ioarcb->read_ioadl_len = 0; if (ipr_cmd->ioa_cfg->sis64) { ioarcb->u.sis64_addr_data.data_ioadl_addr = cpu_to_be64(dma_addr + offsetof(struct ipr_cmnd, i.ioadl64)); ioasa64->u.gata.status = 0; } else { ioarcb->write_ioadl_addr = cpu_to_be32(dma_addr + offsetof(struct ipr_cmnd, i.ioadl)); ioarcb->read_ioadl_addr = ioarcb->write_ioadl_addr; ioasa->u.gata.status = 0; } ioasa->hdr.ioasc = 0; ioasa->hdr.residual_data_len = 0; ipr_cmd->scsi_cmd = NULL; ipr_cmd->qc = NULL; ipr_cmd->sense_buffer[0] = 0; ipr_cmd->dma_use_sg = 0; } /** * ipr_init_ipr_cmnd - Initialize an IPR Cmnd block * @ipr_cmd: ipr command struct * * Return value: * none **/ static void ipr_init_ipr_cmnd(struct ipr_cmnd *ipr_cmd, void (*fast_done) (struct ipr_cmnd *)) { ipr_reinit_ipr_cmnd(ipr_cmd); ipr_cmd->u.scratch = 0; ipr_cmd->sibling = NULL; ipr_cmd->fast_done = fast_done; init_timer(&ipr_cmd->timer); } /** * __ipr_get_free_ipr_cmnd - Get a free IPR Cmnd block * @ioa_cfg: ioa config struct * * Return value: * pointer to ipr command struct **/ static struct ipr_cmnd *__ipr_get_free_ipr_cmnd(struct ipr_hrr_queue *hrrq) { struct ipr_cmnd *ipr_cmd = NULL; if (likely(!list_empty(&hrrq->hrrq_free_q))) { ipr_cmd = list_entry(hrrq->hrrq_free_q.next, struct ipr_cmnd, queue); list_del(&ipr_cmd->queue); } return ipr_cmd; } /** * ipr_get_free_ipr_cmnd - Get a free IPR Cmnd block and initialize it * @ioa_cfg: ioa config struct * * Return value: * pointer to ipr command struct **/ static struct ipr_cmnd *ipr_get_free_ipr_cmnd(struct ipr_ioa_cfg *ioa_cfg) { struct ipr_cmnd *ipr_cmd = __ipr_get_free_ipr_cmnd(&ioa_cfg->hrrq[IPR_INIT_HRRQ]); ipr_init_ipr_cmnd(ipr_cmd, ipr_lock_and_done); return ipr_cmd; } /** * ipr_mask_and_clear_interrupts - Mask all and clear specified interrupts * @ioa_cfg: ioa config struct * @clr_ints: interrupts to clear * * This function masks all interrupts on the adapter, then clears the * interrupts specified in the mask * * Return value: * none **/ static void ipr_mask_and_clear_interrupts(struct ipr_ioa_cfg *ioa_cfg, u32 clr_ints) { volatile u32 int_reg; int i; /* Stop new interrupts */ for (i = 0; i < ioa_cfg->hrrq_num; i++) { spin_lock(&ioa_cfg->hrrq[i]._lock); ioa_cfg->hrrq[i].allow_interrupts = 0; spin_unlock(&ioa_cfg->hrrq[i]._lock); } wmb(); /* Set interrupt mask to stop all new interrupts */ if (ioa_cfg->sis64) writeq(~0, ioa_cfg->regs.set_interrupt_mask_reg); else writel(~0, ioa_cfg->regs.set_interrupt_mask_reg); /* Clear any pending interrupts */ if (ioa_cfg->sis64) writel(~0, ioa_cfg->regs.clr_interrupt_reg); writel(clr_ints, ioa_cfg->regs.clr_interrupt_reg32); int_reg = readl(ioa_cfg->regs.sense_interrupt_reg); } /** * ipr_save_pcix_cmd_reg - Save PCI-X command register * @ioa_cfg: ioa config struct * * Return value: * 0 on success / -EIO on failure **/ static int ipr_save_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg) { int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX); if (pcix_cmd_reg == 0) return 0; if (pci_read_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD, &ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) { dev_err(&ioa_cfg->pdev->dev, "Failed to save PCI-X command register\n"); return -EIO; } ioa_cfg->saved_pcix_cmd_reg |= PCI_X_CMD_DPERR_E | PCI_X_CMD_ERO; return 0; } /** * ipr_set_pcix_cmd_reg - Setup PCI-X command register * @ioa_cfg: ioa config struct * * Return value: * 0 on success / -EIO on failure **/ static int ipr_set_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg) { int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX); if (pcix_cmd_reg) { if (pci_write_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD, ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) { dev_err(&ioa_cfg->pdev->dev, "Failed to setup PCI-X command register\n"); return -EIO; } } return 0; } /** * ipr_sata_eh_done - done function for aborted SATA commands * @ipr_cmd: ipr command struct * * This function is invoked for ops generated to SATA * devices which are being aborted. * * Return value: * none **/ static void ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd) { struct ata_queued_cmd *qc = ipr_cmd->qc; struct ipr_sata_port *sata_port = qc->ap->private_data; qc->err_mask |= AC_ERR_OTHER; sata_port->ioasa.status |= ATA_BUSY; list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q); ata_qc_complete(qc); } /** * ipr_scsi_eh_done - mid-layer done function for aborted ops * @ipr_cmd: ipr command struct * * This function is invoked by the interrupt handler for * ops generated by the SCSI mid-layer which are being aborted. * * Return value: * none **/ static void ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd) { struct scsi_cmnd *scsi_cmd = ipr_cmd->scsi_cmd; scsi_cmd->result |= (DID_ERROR << 16); scsi_dma_unmap(ipr_cmd->scsi_cmd); scsi_cmd->scsi_done(scsi_cmd); list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q); } /** * ipr_fail_all_ops - Fails all outstanding ops. * @ioa_cfg: ioa config struct * * This function fails all outstanding ops. * * Return value: * none **/ static void ipr_fail_all_ops(struct ipr_ioa_cfg *ioa_cfg) { struct ipr_cmnd *ipr_cmd, *temp; struct ipr_hrr_queue *hrrq; ENTER; for_each_hrrq(hrrq, ioa_cfg) { spin_lock(&hrrq->_lock); list_for_each_entry_safe(ipr_cmd, temp, &hrrq->hrrq_pending_q, queue) { list_del(&ipr_cmd->queue); ipr_cmd->s.ioasa.hdr.ioasc = cpu_to_be32(IPR_IOASC_IOA_WAS_RESET); ipr_cmd->s.ioasa.hdr.ilid = cpu_to_be32(IPR_DRIVER_ILID); if (ipr_cmd->scsi_cmd) ipr_cmd->done = ipr_scsi_eh_done; else if (ipr_cmd->qc) ipr_cmd->done = ipr_sata_eh_done; ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, IPR_IOASC_IOA_WAS_RESET); del_timer(&ipr_cmd->timer); ipr_cmd->done(ipr_cmd); } spin_unlock(&hrrq->_lock); } LEAVE; } /** * ipr_send_command - Send driver initiated requests. * @ipr_cmd: ipr command struct * * This function sends a command to the adapter using the correct write call. * In the case of sis64, calculate the ioarcb size required. Then or in the * appropriate bits. * * Return value: * none **/ static void ipr_send_command(struct ipr_cmnd *ipr_cmd) { struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; dma_addr_t send_dma_addr = ipr_cmd->dma_addr; if (ioa_cfg->sis64) { /* The default size is 256 bytes */ send_dma_addr |= 0x1; /* If the number of ioadls * size of ioadl > 128 bytes, then use a 512 byte ioarcb */ if (ipr_cmd->dma_use_sg * sizeof(struct ipr_ioadl64_desc) > 128 ) send_dma_addr |= 0x4; writeq(send_dma_addr, ioa_cfg->regs.ioarrin_reg); } else writel(send_dma_addr, ioa_cfg->regs.ioarrin_reg); } /** * ipr_do_req - Send driver initiated requests. * @ipr_cmd: ipr command struct * @done: done function * @timeout_func: timeout function * @timeout: timeout value * * This function sends the specified command to the adapter with the * timeout given. The done function is invoked on command completion. * * Return value: * none **/ static void ipr_do_req(struct ipr_cmnd *ipr_cmd, void (*done) (struct ipr_cmnd *), void (*timeout_func) (struct ipr_cmnd *), u32 timeout) { list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q); ipr_cmd->done = done; ipr_cmd->timer.data = (unsigned long) ipr_cmd; ipr_cmd->timer.expires = jiffies + timeout; ipr_cmd->timer.function = (void (*)(unsigned long))timeout_func; add_timer(&ipr_cmd->timer); ipr_trc_hook(ipr_cmd, IPR_TRACE_START, 0); ipr_send_command(ipr_cmd); } /** * ipr_internal_cmd_done - Op done function for an internally generated op. * @ipr_cmd: ipr command struct * * This function is the op done function for an internally generated, * blocking op. It simply wakes the sleeping thread. * * Return value: * none **/ static void ipr_internal_cmd_done(struct ipr_cmnd *ipr_cmd) { if (ipr_cmd->sibling) ipr_cmd->sibling = NULL; else complete(&ipr_cmd->completion); } /** * ipr_init_ioadl - initialize the ioadl for the correct SIS type * @ipr_cmd: ipr command struct * @dma_addr: dma address * @len: transfer length * @flags: ioadl flag value * * This function initializes an ioadl in the case where there is only a single * descriptor. * * Return value: * nothing **/ static void ipr_init_ioadl(struct ipr_cmnd *ipr_cmd, dma_addr_t dma_addr, u32 len, int flags) { struct ipr_ioadl_desc *ioadl = ipr_cmd->i.ioadl; struct ipr_ioadl64_desc *ioadl64 = ipr_cmd->i.ioadl64; ipr_cmd->dma_use_sg = 1; if (ipr_cmd->ioa_cfg->sis64) { ioadl64->flags = cpu_to_be32(flags); ioadl64->data_len = cpu_to_be32(len); ioadl64->address = cpu_to_be64(dma_addr); ipr_cmd->ioarcb.ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl64_desc)); ipr_cmd->ioarcb.data_transfer_length = cpu_to_be32(len); } else { ioadl->flags_and_data_len = cpu_to_be32(flags | len); ioadl->address = cpu_to_be32(dma_addr); if (flags == IPR_IOADL_FLAGS_READ_LAST) { ipr_cmd->ioarcb.read_ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc)); ipr_cmd->ioarcb.read_data_transfer_length = cpu_to_be32(len); } else { ipr_cmd->ioarcb.ioadl_len = cpu_to_be32(sizeof(struct ipr_ioadl_desc)); ipr_cmd->ioarcb.data_transfer_length = cpu_to_be32(len); } } } /** * ipr_send_blocking_cmd - Send command and sleep on its completion. * @ipr_cmd: ipr command struct * @timeout_func: function to invoke if command times out * @timeout: timeout * * Return value: * none **/ static void ipr_send_blocking_cmd(struct ipr_cmnd *ipr_cmd, void (*timeout_func) (struct ipr_cmnd *ipr_cmd), u32 timeout) { struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; init_completion(&ipr_cmd->completion); ipr_do_req(ipr_cmd, ipr_internal_cmd_done, timeout_func, timeout); spin_unlock_irq(ioa_cfg->host->host_lock); wait_for_completion(&ipr_cmd->completion); spin_lock_irq(ioa_cfg->host->host_lock); } static int ipr_get_hrrq_index(struct ipr_ioa_cfg *ioa_cfg) { if (ioa_cfg->hrrq_num == 1) return 0; else return (atomic_add_return(1, &ioa_cfg->hrrq_index) % (ioa_cfg->hrrq_num - 1)) + 1; } /** * ipr_send_hcam - Send an HCAM to the adapter. * @ioa_cfg: ioa config struct * @type: HCAM type * @hostrcb: hostrcb struct * * This function will send a Host Controlled Async command to the adapter. * If HCAMs are currently not allowed to be issued to the adapter, it will * place the hostrcb on the free queue. * * Return value: * none **/ static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type, struct ipr_hostrcb *hostrcb) { struct ipr_cmnd *ipr_cmd; struct ipr_ioarcb *ioarcb; if (ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds) { ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_pending_q); list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_pending_q); ipr_cmd->u.hostrcb = hostrcb; ioarcb = &ipr_cmd->ioarcb; ioarcb->res_handle = cpu_to_be32(IPR_IOA_RES_HANDLE); ioarcb->cmd_pkt.request_type = IPR_RQTYPE_HCAM; ioarcb->cmd_pkt.cdb[0] = IPR_HOST_CONTROLLED_ASYNC; ioarcb->cmd_pkt.cdb[1] = type; ioarcb->cmd_pkt.cdb[7] = (sizeof(hostrcb->hcam) >> 8) & 0xff; ioarcb->cmd_pkt.cdb[8] = sizeof(hostrcb->hcam) & 0xff; ipr_init_ioadl(ipr_cmd, hostrcb->hostrcb_dma, sizeof(hostrcb->hcam), IPR_IOADL_FLAGS_READ_LAST); if (type == IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE) ipr_cmd->done = ipr_process_ccn; else ipr_cmd->done = ipr_process_error; ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_IOA_RES_ADDR); ipr_send_command(ipr_cmd); } else { list_add_tail(&hostrcb->queue, &ioa_cfg->hostrcb_free_q); } } /** * ipr_update_ata_class - Update the ata class in the resource entry * @res: resource entry struct * @proto: cfgte device bus protocol value * * Return value: * none **/ static void ipr_update_ata_class(struct ipr_resource_entry *res, unsigned int proto) { switch (proto) { case IPR_PROTO_SATA: case IPR_PROTO_SAS_STP: res->ata_class = ATA_DEV_ATA; break; case IPR_PROTO_SATA_ATAPI: case IPR_PROTO_SAS_STP_ATAPI: res->ata_class = ATA_DEV_ATAPI; break; default: res->ata_class = ATA_DEV_UNKNOWN; break; }; } /** * ipr_init_res_entry - Initialize a resource entry struct. * @res: resource entry struct * @cfgtew: config table entry wrapper struct * * Return value: * none **/ static void ipr_init_res_entry(struct ipr_resource_entry *res, struct ipr_config_table_entry_wrapper *cfgtew) { int found = 0; unsigned int proto; struct ipr_ioa_cfg *ioa_cfg = res->ioa_cfg; struct ipr_resource_entry *gscsi_res = NULL; res->needs_sync_complete = 0; res->in_erp = 0; res->add_to_ml = 0; res->del_from_ml = 0; res->resetting_device = 0; res->sdev = NULL; res->sata_port = NULL; if (ioa_cfg->sis64) { proto = cfgtew->u.cfgte64->proto; res->res_flags = cfgtew->u.cfgte64->res_flags; res->qmodel = IPR_QUEUEING_MODEL64(res); res->type = cfgtew->u.cfgte64->res_type; memcpy(res->res_path, &cfgtew->u.cfgte64->res_path, sizeof(res->res_path)); res->bus = 0; memcpy(&res->dev_lun.scsi_lun, &cfgtew->u.cfgte64->lun, sizeof(res->dev_lun.scsi_lun)); res->lun = scsilun_to_int(&res->dev_lun); if (res->type == IPR_RES_TYPE_GENERIC_SCSI) { list_for_each_entry(gscsi_res, &ioa_cfg->used_res_q, queue) { if (gscsi_res->dev_id == cfgtew->u.cfgte64->dev_id) { found = 1; res->target = gscsi_res->target; break; } } if (!found) { res->target = find_first_zero_bit(ioa_cfg->target_ids, ioa_cfg->max_devs_supported); set_bit(res->target, ioa_cfg->target_ids); } } else if (res->type == IPR_RES_TYPE_IOAFP) { res->bus = IPR_IOAFP_VIRTUAL_BUS; res->target = 0; } else if (res->type == IPR_RES_TYPE_ARRAY) { res->bus = IPR_ARRAY_VIRTUAL_BUS; res->target = find_first_zero_bit(ioa_cfg->array_ids, ioa_cfg->max_devs_supported); set_bit(res->target, ioa_cfg->array_ids); } else if (res->type == IPR_RES_TYPE_VOLUME_SET) { res->bus = IPR_VSET_VIRTUAL_BUS; res->target = find_first_zero_bit(ioa_cfg->vset_ids, ioa_cfg->max_devs_supported); set_bit(res->target, ioa_cfg->vset_ids); } else { res->target = find_first_zero_bit(ioa_cfg->target_ids, ioa_cfg->max_devs_supported); set_bit(res->target, ioa_cfg->target_ids); } } else { proto = cfgtew->u.cfgte->proto; res->qmodel = IPR_QUEUEING_MODEL(res); res->flags = cfgtew->u.cfgte->flags; if (res->flags & IPR_IS_IOA_RESOURCE) res->type = IPR_RES_TYPE_IOAFP; else res->type = cfgtew->u.cfgte->rsvd_subtype & 0x0f; res->bus = cfgtew->u.cfgte->res_addr.bus; res->target = cfgtew->u.cfgte->res_addr.target; res->lun = cfgtew->u.cfgte->res_addr.lun; res->lun_wwn = get_unaligned_be64(cfgtew->u.cfgte->lun_wwn); } ipr_update_ata_class(res, proto); } /** * ipr_is_same_device - Determine if two devices are the same. * @res: resource entry struct * @cfgtew: config table entry wrapper struct * * Return value: * 1 if the devices are the same / 0 otherwise **/ static int ipr_is_same_device(struct ipr_resource_entry *res, struct ipr_config_table_entry_wrapper *cfgtew) { if (res->ioa_cfg->sis64) { if (!memcmp(&res->dev_id, &cfgtew->u.cfgte64->dev_id, sizeof(cfgtew->u.cfgte64->dev_id)) && !memcmp(&res->dev_lun.scsi_lun, &cfgtew->u.cfgte64->lun, sizeof(cfgtew->u.cfgte64->lun))) { return 1; } } else { if (res->bus == cfgtew->u.cfgte->res_addr.bus && res->target == cfgtew->u.cfgte->res_addr.target && res->lun == cfgtew->u.cfgte->res_addr.lun) return 1; } return 0; } /** * __ipr_format_res_path - Format the resource path for printing. * @res_path: resource path * @buf: buffer * @len: length of buffer provided * * Return value: * pointer to buffer **/ static char *__ipr_format_res_path(u8 *res_path, char *buffer, int len) { int i; char *p = buffer; *p = '\0'; p += snprintf(p, buffer + len - p, "%02X", res_path[0]); for (i = 1; res_path[i] != 0xff && ((i * 3) < len); i++) p += snprintf(p, buffer + len - p, "-%02X", res_path[i]); return buffer; } /** * ipr_format_res_path - Format the resource path for printing. * @ioa_cfg: ioa config struct * @res_path: resource path * @buf: buffer * @len: length of buffer provided * * Return value: * pointer to buffer **/ static char *ipr_format_res_path(struct ipr_ioa_cfg *ioa_cfg, u8 *res_path, char *buffer, int len) { char *p = buffer; *p = '\0'; p += snprintf(p, buffer + len - p, "%d/", ioa_cfg->host->host_no); __ipr_format_res_path(res_path, p, len - (buffer - p)); return buffer; } /** * ipr_update_res_entry - Update the resource entry. * @res: resource entry struct * @cfgtew: config table entry wrapper struct * * Return value: * none **/ static void ipr_update_res_entry(struct ipr_resource_entry *res, struct ipr_config_table_entry_wrapper *cfgtew) { char buffer[IPR_MAX_RES_PATH_LENGTH]; unsigned int proto; int new_path = 0; if (res->ioa_cfg->sis64) { res->flags = cfgtew->u.cfgte64->flags; res->res_flags = cfgtew->u.cfgte64->res_flags; res->type = cfgtew->u.cfgte64->res_type; memcpy(&res->std_inq_data, &cfgtew->u.cfgte64->std_inq_data, sizeof(struct ipr_std_inq_data)); res->qmodel = IPR_QUEUEING_MODEL64(res); proto = cfgtew->u.cfgte64->proto; res->res_handle = cfgtew->u.cfgte64->res_handle; res->dev_id = cfgtew->u.cfgte64->dev_id; memcpy(&res->dev_lun.scsi_lun, &cfgtew->u.cfgte64->lun, sizeof(res->dev_lun.scsi_lun)); if (memcmp(res->res_path, &cfgtew->u.cfgte64->res_path, sizeof(res->res_path))) { memcpy(res->res_path, &cfgtew->u.cfgte64->res_path, sizeof(res->res_path)); new_path = 1; } if (res->sdev && new_path) sdev_printk(KERN_INFO, res->sdev, "Resource path: %s\n", ipr_format_res_path(res->ioa_cfg, res->res_path, buffer, sizeof(buffer))); } else { res->flags = cfgtew->u.cfgte->flags; if (res->flags & IPR_IS_IOA_RESOURCE) res->type = IPR_RES_TYPE_IOAFP; else res->type = cfgtew->u.cfgte->rsvd_subtype & 0x0f; memcpy(&res->std_inq_data, &cfgtew->u.cfgte->std_inq_data, sizeof(struct ipr_std_inq_data)); res->qmodel = IPR_QUEUEING_MODEL(res); proto = cfgtew->u.cfgte->proto; res->res_handle = cfgtew->u.cfgte->res_handle; } ipr_update_ata_class(res, proto); } /** * ipr_clear_res_target - Clear the bit in the bit map representing the target * for the resource. * @res: resource entry struct * @cfgtew: config table entry wrapper struct * * Return value: * none **/ static void ipr_clear_res_target(struct ipr_resource_entry *res) { struct ipr_resource_entry *gscsi_res = NULL; struct ipr_ioa_cfg *ioa_cfg = res->ioa_cfg; if (!ioa_cfg->sis64) return; if (res->bus == IPR_ARRAY_VIRTUAL_BUS) clear_bit(res->target, ioa_cfg->array_ids); else if (res->bus == IPR_VSET_VIRTUAL_BUS) clear_bit(res->target, ioa_cfg->vset_ids); else if (res->bus == 0 && res->type == IPR_RES_TYPE_GENERIC_SCSI) { list_for_each_entry(gscsi_res, &ioa_cfg->used_res_q, queue) if (gscsi_res->dev_id == res->dev_id && gscsi_res != res) return; clear_bit(res->target, ioa_cfg->target_ids); } else if (res->bus == 0) clear_bit(res->target, ioa_cfg->target_ids); } /** * ipr_handle_config_change - Handle a config change from the adapter * @ioa_cfg: ioa config struct * @hostrcb: hostrcb * * Return value: * none **/ static void ipr_handle_config_change(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { struct ipr_resource_entry *res = NULL; struct ipr_config_table_entry_wrapper cfgtew; __be32 cc_res_handle; u32 is_ndn = 1; if (ioa_cfg->sis64) { cfgtew.u.cfgte64 = &hostrcb->hcam.u.ccn.u.cfgte64; cc_res_handle = cfgtew.u.cfgte64->res_handle; } else { cfgtew.u.cfgte = &hostrcb->hcam.u.ccn.u.cfgte; cc_res_handle = cfgtew.u.cfgte->res_handle; } list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { if (res->res_handle == cc_res_handle) { is_ndn = 0; break; } } if (is_ndn) { if (list_empty(&ioa_cfg->free_res_q)) { ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb); return; } res = list_entry(ioa_cfg->free_res_q.next, struct ipr_resource_entry, queue); list_del(&res->queue); ipr_init_res_entry(res, &cfgtew); list_add_tail(&res->queue, &ioa_cfg->used_res_q); } ipr_update_res_entry(res, &cfgtew); if (hostrcb->hcam.notify_type == IPR_HOST_RCB_NOTIF_TYPE_REM_ENTRY) { if (res->sdev) { res->del_from_ml = 1; res->res_handle = IPR_INVALID_RES_HANDLE; if (ioa_cfg->allow_ml_add_del) schedule_work(&ioa_cfg->work_q); } else { ipr_clear_res_target(res); list_move_tail(&res->queue, &ioa_cfg->free_res_q); } } else if (!res->sdev || res->del_from_ml) { res->add_to_ml = 1; if (ioa_cfg->allow_ml_add_del) schedule_work(&ioa_cfg->work_q); } ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb); } /** * ipr_process_ccn - Op done function for a CCN. * @ipr_cmd: ipr command struct * * This function is the op done function for a configuration * change notification host controlled async from the adapter. * * Return value: * none **/ static void ipr_process_ccn(struct ipr_cmnd *ipr_cmd) { struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; struct ipr_hostrcb *hostrcb = ipr_cmd->u.hostrcb; u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc); list_del(&hostrcb->queue); list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q); if (ioasc) { if (ioasc != IPR_IOASC_IOA_WAS_RESET) dev_err(&ioa_cfg->pdev->dev, "Host RCB failed with IOASC: 0x%08X\n", ioasc); ipr_send_hcam(ioa_cfg, IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE, hostrcb); } else { ipr_handle_config_change(ioa_cfg, hostrcb); } } /** * strip_and_pad_whitespace - Strip and pad trailing whitespace. * @i: index into buffer * @buf: string to modify * * This function will strip all trailing whitespace, pad the end * of the string with a single space, and NULL terminate the string. * * Return value: * new length of string **/ static int strip_and_pad_whitespace(int i, char *buf) { while (i && buf[i] == ' ') i--; buf[i+1] = ' '; buf[i+2] = '\0'; return i + 2; } /** * ipr_log_vpd_compact - Log the passed extended VPD compactly. * @prefix: string to print at start of printk * @hostrcb: hostrcb pointer * @vpd: vendor/product id/sn struct * * Return value: * none **/ static void ipr_log_vpd_compact(char *prefix, struct ipr_hostrcb *hostrcb, struct ipr_vpd *vpd) { char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN + IPR_SERIAL_NUM_LEN + 3]; int i = 0; memcpy(buffer, vpd->vpids.vendor_id, IPR_VENDOR_ID_LEN); i = strip_and_pad_whitespace(IPR_VENDOR_ID_LEN - 1, buffer); memcpy(&buffer[i], vpd->vpids.product_id, IPR_PROD_ID_LEN); i = strip_and_pad_whitespace(i + IPR_PROD_ID_LEN - 1, buffer); memcpy(&buffer[i], vpd->sn, IPR_SERIAL_NUM_LEN); buffer[IPR_SERIAL_NUM_LEN + i] = '\0'; ipr_hcam_err(hostrcb, "%s VPID/SN: %s\n", prefix, buffer); } /** * ipr_log_vpd - Log the passed VPD to the error log. * @vpd: vendor/product id/sn struct * * Return value: * none **/ static void ipr_log_vpd(struct ipr_vpd *vpd) { char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN + IPR_SERIAL_NUM_LEN]; memcpy(buffer, vpd->vpids.vendor_id, IPR_VENDOR_ID_LEN); memcpy(buffer + IPR_VENDOR_ID_LEN, vpd->vpids.product_id, IPR_PROD_ID_LEN); buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN] = '\0'; ipr_err("Vendor/Product ID: %s\n", buffer); memcpy(buffer, vpd->sn, IPR_SERIAL_NUM_LEN); buffer[IPR_SERIAL_NUM_LEN] = '\0'; ipr_err(" Serial Number: %s\n", buffer); } /** * ipr_log_ext_vpd_compact - Log the passed extended VPD compactly. * @prefix: string to print at start of printk * @hostrcb: hostrcb pointer * @vpd: vendor/product id/sn/wwn struct * * Return value: * none **/ static void ipr_log_ext_vpd_compact(char *prefix, struct ipr_hostrcb *hostrcb, struct ipr_ext_vpd *vpd) { ipr_log_vpd_compact(prefix, hostrcb, &vpd->vpd); ipr_hcam_err(hostrcb, "%s WWN: %08X%08X\n", prefix, be32_to_cpu(vpd->wwid[0]), be32_to_cpu(vpd->wwid[1])); } /** * ipr_log_ext_vpd - Log the passed extended VPD to the error log. * @vpd: vendor/product id/sn/wwn struct * * Return value: * none **/ static void ipr_log_ext_vpd(struct ipr_ext_vpd *vpd) { ipr_log_vpd(&vpd->vpd); ipr_err(" WWN: %08X%08X\n", be32_to_cpu(vpd->wwid[0]), be32_to_cpu(vpd->wwid[1])); } /** * ipr_log_enhanced_cache_error - Log a cache error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_enhanced_cache_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { struct ipr_hostrcb_type_12_error *error; if (ioa_cfg->sis64) error = &hostrcb->hcam.u.error64.u.type_12_error; else error = &hostrcb->hcam.u.error.u.type_12_error; ipr_err("-----Current Configuration-----\n"); ipr_err("Cache Directory Card Information:\n"); ipr_log_ext_vpd(&error->ioa_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_ext_vpd(&error->cfc_vpd); ipr_err("-----Expected Configuration-----\n"); ipr_err("Cache Directory Card Information:\n"); ipr_log_ext_vpd(&error->ioa_last_attached_to_cfc_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_ext_vpd(&error->cfc_last_attached_to_ioa_vpd); ipr_err("Additional IOA Data: %08X %08X %08X\n", be32_to_cpu(error->ioa_data[0]), be32_to_cpu(error->ioa_data[1]), be32_to_cpu(error->ioa_data[2])); } /** * ipr_log_cache_error - Log a cache error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_cache_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { struct ipr_hostrcb_type_02_error *error = &hostrcb->hcam.u.error.u.type_02_error; ipr_err("-----Current Configuration-----\n"); ipr_err("Cache Directory Card Information:\n"); ipr_log_vpd(&error->ioa_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_vpd(&error->cfc_vpd); ipr_err("-----Expected Configuration-----\n"); ipr_err("Cache Directory Card Information:\n"); ipr_log_vpd(&error->ioa_last_attached_to_cfc_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_vpd(&error->cfc_last_attached_to_ioa_vpd); ipr_err("Additional IOA Data: %08X %08X %08X\n", be32_to_cpu(error->ioa_data[0]), be32_to_cpu(error->ioa_data[1]), be32_to_cpu(error->ioa_data[2])); } /** * ipr_log_enhanced_config_error - Log a configuration error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_enhanced_config_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { int errors_logged, i; struct ipr_hostrcb_device_data_entry_enhanced *dev_entry; struct ipr_hostrcb_type_13_error *error; error = &hostrcb->hcam.u.error.u.type_13_error; errors_logged = be32_to_cpu(error->errors_logged); ipr_err("Device Errors Detected/Logged: %d/%d\n", be32_to_cpu(error->errors_detected), errors_logged); dev_entry = error->dev; for (i = 0; i < errors_logged; i++, dev_entry++) { ipr_err_separator; ipr_phys_res_err(ioa_cfg, dev_entry->dev_res_addr, "Device %d", i + 1); ipr_log_ext_vpd(&dev_entry->vpd); ipr_err("-----New Device Information-----\n"); ipr_log_ext_vpd(&dev_entry->new_vpd); ipr_err("Cache Directory Card Information:\n"); ipr_log_ext_vpd(&dev_entry->ioa_last_with_dev_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_ext_vpd(&dev_entry->cfc_last_with_dev_vpd); } } /** * ipr_log_sis64_config_error - Log a device error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_sis64_config_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { int errors_logged, i; struct ipr_hostrcb64_device_data_entry_enhanced *dev_entry; struct ipr_hostrcb_type_23_error *error; char buffer[IPR_MAX_RES_PATH_LENGTH]; error = &hostrcb->hcam.u.error64.u.type_23_error; errors_logged = be32_to_cpu(error->errors_logged); ipr_err("Device Errors Detected/Logged: %d/%d\n", be32_to_cpu(error->errors_detected), errors_logged); dev_entry = error->dev; for (i = 0; i < errors_logged; i++, dev_entry++) { ipr_err_separator; ipr_err("Device %d : %s", i + 1, __ipr_format_res_path(dev_entry->res_path, buffer, sizeof(buffer))); ipr_log_ext_vpd(&dev_entry->vpd); ipr_err("-----New Device Information-----\n"); ipr_log_ext_vpd(&dev_entry->new_vpd); ipr_err("Cache Directory Card Information:\n"); ipr_log_ext_vpd(&dev_entry->ioa_last_with_dev_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_ext_vpd(&dev_entry->cfc_last_with_dev_vpd); } } /** * ipr_log_config_error - Log a configuration error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_config_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { int errors_logged, i; struct ipr_hostrcb_device_data_entry *dev_entry; struct ipr_hostrcb_type_03_error *error; error = &hostrcb->hcam.u.error.u.type_03_error; errors_logged = be32_to_cpu(error->errors_logged); ipr_err("Device Errors Detected/Logged: %d/%d\n", be32_to_cpu(error->errors_detected), errors_logged); dev_entry = error->dev; for (i = 0; i < errors_logged; i++, dev_entry++) { ipr_err_separator; ipr_phys_res_err(ioa_cfg, dev_entry->dev_res_addr, "Device %d", i + 1); ipr_log_vpd(&dev_entry->vpd); ipr_err("-----New Device Information-----\n"); ipr_log_vpd(&dev_entry->new_vpd); ipr_err("Cache Directory Card Information:\n"); ipr_log_vpd(&dev_entry->ioa_last_with_dev_vpd); ipr_err("Adapter Card Information:\n"); ipr_log_vpd(&dev_entry->cfc_last_with_dev_vpd); ipr_err("Additional IOA Data: %08X %08X %08X %08X %08X\n", be32_to_cpu(dev_entry->ioa_data[0]), be32_to_cpu(dev_entry->ioa_data[1]), be32_to_cpu(dev_entry->ioa_data[2]), be32_to_cpu(dev_entry->ioa_data[3]), be32_to_cpu(dev_entry->ioa_data[4])); } } /** * ipr_log_enhanced_array_error - Log an array configuration error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_enhanced_array_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { int i, num_entries; struct ipr_hostrcb_type_14_error *error; struct ipr_hostrcb_array_data_entry_enhanced *array_entry; const u8 zero_sn[IPR_SERIAL_NUM_LEN] = { [0 ... IPR_SERIAL_NUM_LEN-1] = '0' }; error = &hostrcb->hcam.u.error.u.type_14_error; ipr_err_separator; ipr_err("RAID %s Array Configuration: %d:%d:%d:%d\n", error->protection_level, ioa_cfg->host->host_no, error->last_func_vset_res_addr.bus, error->last_func_vset_res_addr.target, error->last_func_vset_res_addr.lun); ipr_err_separator; array_entry = error->array_member; num_entries = min_t(u32, be32_to_cpu(error->num_entries), ARRAY_SIZE(error->array_member)); for (i = 0; i < num_entries; i++, array_entry++) { if (!memcmp(array_entry->vpd.vpd.sn, zero_sn, IPR_SERIAL_NUM_LEN)) continue; if (be32_to_cpu(error->exposed_mode_adn) == i) ipr_err("Exposed Array Member %d:\n", i); else ipr_err("Array Member %d:\n", i); ipr_log_ext_vpd(&array_entry->vpd); ipr_phys_res_err(ioa_cfg, array_entry->dev_res_addr, "Current Location"); ipr_phys_res_err(ioa_cfg, array_entry->expected_dev_res_addr, "Expected Location"); ipr_err_separator; } } /** * ipr_log_array_error - Log an array configuration error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_array_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { int i; struct ipr_hostrcb_type_04_error *error; struct ipr_hostrcb_array_data_entry *array_entry; const u8 zero_sn[IPR_SERIAL_NUM_LEN] = { [0 ... IPR_SERIAL_NUM_LEN-1] = '0' }; error = &hostrcb->hcam.u.error.u.type_04_error; ipr_err_separator; ipr_err("RAID %s Array Configuration: %d:%d:%d:%d\n", error->protection_level, ioa_cfg->host->host_no, error->last_func_vset_res_addr.bus, error->last_func_vset_res_addr.target, error->last_func_vset_res_addr.lun); ipr_err_separator; array_entry = error->array_member; for (i = 0; i < 18; i++) { if (!memcmp(array_entry->vpd.sn, zero_sn, IPR_SERIAL_NUM_LEN)) continue; if (be32_to_cpu(error->exposed_mode_adn) == i) ipr_err("Exposed Array Member %d:\n", i); else ipr_err("Array Member %d:\n", i); ipr_log_vpd(&array_entry->vpd); ipr_phys_res_err(ioa_cfg, array_entry->dev_res_addr, "Current Location"); ipr_phys_res_err(ioa_cfg, array_entry->expected_dev_res_addr, "Expected Location"); ipr_err_separator; if (i == 9) array_entry = error->array_member2; else array_entry++; } } /** * ipr_log_hex_data - Log additional hex IOA error data. * @ioa_cfg: ioa config struct * @data: IOA error data * @len: data length * * Return value: * none **/ static void ipr_log_hex_data(struct ipr_ioa_cfg *ioa_cfg, u32 *data, int len) { int i; if (len == 0) return; if (ioa_cfg->log_level <= IPR_DEFAULT_LOG_LEVEL) len = min_t(int, len, IPR_DEFAULT_MAX_ERROR_DUMP); for (i = 0; i < len / 4; i += 4) { ipr_err("%08X: %08X %08X %08X %08X\n", i*4, be32_to_cpu(data[i]), be32_to_cpu(data[i+1]), be32_to_cpu(data[i+2]), be32_to_cpu(data[i+3])); } } /** * ipr_log_enhanced_dual_ioa_error - Log an enhanced dual adapter error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_enhanced_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { struct ipr_hostrcb_type_17_error *error; if (ioa_cfg->sis64) error = &hostrcb->hcam.u.error64.u.type_17_error; else error = &hostrcb->hcam.u.error.u.type_17_error; error->failure_reason[sizeof(error->failure_reason) - 1] = '\0'; strim(error->failure_reason); ipr_hcam_err(hostrcb, "%s [PRC: %08X]\n", error->failure_reason, be32_to_cpu(hostrcb->hcam.u.error.prc)); ipr_log_ext_vpd_compact("Remote IOA", hostrcb, &error->vpd); ipr_log_hex_data(ioa_cfg, error->data, be32_to_cpu(hostrcb->hcam.length) - (offsetof(struct ipr_hostrcb_error, u) + offsetof(struct ipr_hostrcb_type_17_error, data))); } /** * ipr_log_dual_ioa_error - Log a dual adapter error. * @ioa_cfg: ioa config struct * @hostrcb: hostrcb struct * * Return value: * none **/ static void ipr_log_dual_ioa_error(struct ipr_ioa_cfg *ioa_cfg, struct ipr_hostrcb *hostrcb) { struct ipr_hostrcb_type_07_error *error; error = &hostrcb->hcam.u.error.u.type_07_error; error->failure_reason[sizeof(error->failure_reason) - 1] = '\0'; strim(error->failure_reason); ipr_hcam_err(hostrcb, "%s [PRC: %08X]\n", error->failure_reason, be32_to_cpu(hostrcb->hcam.u.error.prc)); ipr_log_vpd_compact("Remote IOA", hostrcb, &error->vpd); ipr_log_hex_data(ioa_cfg, error->data, be32_to_cpu(hostrcb->hcam.length) - (offsetof(struct ipr_hostrcb_error, u) + offsetof(struct ipr_hostrcb_type_07_error, data))); } static const struct { u8 active; char *desc; } path_active_desc[] = { { IPR_PATH_NO_INFO, "Path" }, { IPR_PATH_ACTIVE, "Active path" }, { IPR_PATH_NOT_ACTIVE, "Inactive path" } }; static const struct { u8 state; char *desc; } path_state_desc[] = { { IPR_PATH_STATE_NO_INFO, "has no path state information available" }, { IPR_PATH_HEALTHY, "is healthy" }, { IPR_PATH_DEGRADED, "is degraded" }, { IPR_PATH_FAILED, "is failed" } }; /** * ipr_log_fabric_path - Log a fabric path error * @hostrcb: hostrcb struct * @fabric: fabric descriptor * * Return value: * none **/ static void ipr_log_fabric_path(struct ipr_hostrcb *hostrcb, struct ipr_hostrcb_fabric_desc *fabric) { int i, j; u8 path_state = fabric->path_state; u8 active = path_state & IPR_PATH_ACTIVE_MASK; u8 state = path_state & IPR_PATH_STATE_MASK; for (i = 0; i < ARRAY_SIZE(path_active_desc); i++) { if (path_active_desc[i].active != active) continue; for (j = 0; j < ARRAY_SIZE(path_state_desc); j++) { if (path_state_desc[j].state != state) continue; if (fabric->cascaded_expander == 0xff && fabric->phy == 0xff) { ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d\n", path_active_desc[i].desc, path_state_desc[j].desc, fabric->ioa_port); } else if (fabric->cascaded_expander == 0xff) { ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Phy=%d\n", path_active_desc[i].desc, path_state_desc[j].desc, fabric->ioa_port, fabric->phy); } else if (fabric->phy == 0xff) { ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d\n", path_active_desc[i].desc, path_state_desc[j].desc, fabric->ioa_port, fabric->cascaded_expander); } else { ipr_hcam_err(hostrcb, "%s %s: IOA Port=%d, Cascade=%d, Phy=%d\n", path_active_desc[i].desc, path_state_desc[j].desc, fabric->ioa_port, fabric->cascaded_expander, fabric->phy); } return; } } ipr_err("Path state=%02X IOA Port=%d Cascade=%d Phy=%d\n", path_state, fabric->ioa_port, fabric->cascaded_expander, fabric->phy); } /** * ipr_log64_fabric_path - Log a fabric path error * @hostrcb: hostrcb struct * @fabric: fabric descriptor * * Return value: * none **/ static void ipr_log64_fabric_path(struct ipr_hostrcb *hostrcb, struct ipr_hostrcb64_fabric_desc *fabric) { int i, j; u8 path_state = fabric->path_state; u8 active = path_state & IPR_PATH_ACTIVE_MASK; u8 state = path_state & IPR_PATH_STATE_MASK; char buffer[IPR_MAX_RES_PATH_LENGTH]; for (i = 0; i < ARRAY_SIZE(path_active_desc); i++) { if (path_active_desc[i].active != active) continue; for (j = 0; j < ARRAY_SIZE(path_state_desc); j++) { if (path_state_desc[j].state != state) continue; ipr_hcam_err(hostrcb, "%s %s: Resource Path=%s\n", path_active_desc[i].desc, path_state_desc[j].desc, ipr_format_res_path(hostrcb->ioa_cfg, fabric->res_path, buffer, sizeof(buffer))); return; } } ipr_err("Path state=%02X Resource Path=%s\n", path_state, ipr_format_res_path(hostrcb->ioa_cfg, fabric->res_path, buffer, sizeof(buffer))); } static const struct { u8 type; char *desc; } path_type_desc[] = { { IPR_PATH_CFG_IOA_PORT, "IOA port" }, { IPR_PATH_CFG_EXP_PORT, "Expander port" }, { IPR_PATH_CFG_DEVICE_PORT, "Device port" }, { IPR_PATH_CFG_DEVICE_LUN, "Device LUN" } }; static const struct { u8 status; char *desc; } path_status_desc[] = { { IPR_PATH_CFG_NO_PROB, "Functional" }, { IPR_PATH_CFG_DEGRADED, "Degraded" }, { IPR_PATH_CFG_FAILED, "Failed" }, { IPR_PATH_CFG_SUSPECT, "Suspect" }, { IPR_PATH_NOT_DETECTED, "Missing" }, { IPR_PATH_INCORRECT_CONN, "Incorrectly connected" } }; static const char *link_rate[] = { "unknown", "disabled", "phy reset problem", "spinup hold", "port selector", "unknown", "unknown", "unknown", "1.5Gbps", "3.0Gbps", "unknown", "unknown", "unknown", "unknown", "unknown", "unknown" }; /** * ipr_log_path_elem - Log a fabric path element. * @hostrcb: hostrcb struct * @cfg: fabric path element struct * * Return value: * none **/ static void ipr_log_path_elem(struct ipr_hostrcb *hostrcb, struct ipr_hostrcb_config_element *cfg) { int i, j; u8 type = cfg->type_status & IPR_PATH_CFG_TYPE_MASK; u8 status = cfg->type_status & IPR_PATH_CFG_STATUS_MASK; if (type == IPR_PATH_CFG_NOT_EXIST) return; for (i = 0; i < ARRAY_SIZE(path_type_desc); i++) { if (path_type_desc[i].type != type) continue; for (j = 0; j < ARRAY_SIZE(path_status_desc); j++) { if (path_status_desc[j].status != status) continue; if (type == IPR_PATH_CFG_IOA_PORT) { ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, WWN=%08X%08X\n", path_status_desc[j].desc, path_type_desc[i].desc, cfg->phy, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); } else { if (cfg->cascaded_expander == 0xff && cfg->phy == 0xff) { ipr_hcam_err(hostrcb, "%s %s: Link rate=%s, WWN=%08X%08X\n", path_status_desc[j].desc, path_type_desc[i].desc, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); } else if (cfg->cascaded_expander == 0xff) { ipr_hcam_err(hostrcb, "%s %s: Phy=%d, Link rate=%s, " "WWN=%08X%08X\n", path_status_desc[j].desc, path_type_desc[i].desc, cfg->phy, link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK], be32_to_cpu(cfg->wwid[0]), be32_to_cpu(cfg->wwid[1])); } else if (cfg->phy == 0xff) {