diff options
Diffstat (limited to 'drivers/scsi')
57 files changed, 4353 insertions, 544 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index a5f0aaaf0dd..c46666a2480 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -722,7 +722,7 @@ config SCSI_FD_MCS config SCSI_GDTH tristate "Intel/ICP (former GDT SCSI Disk Array) RAID Controller support" - depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API && PCI_LEGACY + depends on (ISA || EISA || PCI) && SCSI && ISA_DMA_API ---help--- Formerly called GDT SCSI Disk Array Controller Support. @@ -992,6 +992,16 @@ config SCSI_IZIP_SLOW_CTR Generally, saying N is fine. +config SCSI_MVSAS + tristate "Marvell 88SE6440 SAS/SATA support" + depends on PCI && SCSI + select SCSI_SAS_LIBSAS + help + This driver supports Marvell SAS/SATA PCI devices. + + To compiler this driver as a module, choose M here: the module + will be called mvsas. + config SCSI_NCR53C406A tristate "NCR53c406a SCSI support" depends on ISA && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 925c26b4fff..23e6ecbd477 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/ obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvscsi/ obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o obj-$(CONFIG_SCSI_STEX) += stex.o +obj-$(CONFIG_SCSI_MVSAS) += mvsas.o obj-$(CONFIG_PS3_ROM) += ps3rom.o obj-$(CONFIG_ARM) += arm/ diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index bfd0e64964a..c05092fd3a9 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -144,51 +144,77 @@ static char *aac_get_status_string(u32 status); */ static int nondasd = -1; -static int aac_cache = 0; +static int aac_cache; static int dacmode = -1; - +int aac_msi; int aac_commit = -1; int startup_timeout = 180; int aif_timeout = 120; module_param(nondasd, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(nondasd, "Control scanning of hba for nondasd devices. 0=off, 1=on"); +MODULE_PARM_DESC(nondasd, "Control scanning of hba for nondasd devices." + " 0=off, 1=on"); module_param_named(cache, aac_cache, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(cache, "Disable Queue Flush commands:\n\tbit 0 - Disable FUA in WRITE SCSI commands\n\tbit 1 - Disable SYNCHRONIZE_CACHE SCSI command\n\tbit 2 - Disable only if Battery not protecting Cache"); +MODULE_PARM_DESC(cache, "Disable Queue Flush commands:\n" + "\tbit 0 - Disable FUA in WRITE SCSI commands\n" + "\tbit 1 - Disable SYNCHRONIZE_CACHE SCSI command\n" + "\tbit 2 - Disable only if Battery not protecting Cache"); module_param(dacmode, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(dacmode, "Control whether dma addressing is using 64 bit DAC. 0=off, 1=on"); +MODULE_PARM_DESC(dacmode, "Control whether dma addressing is using 64 bit DAC." + " 0=off, 1=on"); module_param_named(commit, aac_commit, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(commit, "Control whether a COMMIT_CONFIG is issued to the adapter for foreign arrays.\nThis is typically needed in systems that do not have a BIOS. 0=off, 1=on"); +MODULE_PARM_DESC(commit, "Control whether a COMMIT_CONFIG is issued to the" + " adapter for foreign arrays.\n" + "This is typically needed in systems that do not have a BIOS." + " 0=off, 1=on"); +module_param_named(msi, aac_msi, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(msi, "IRQ handling." + " 0=PIC(default), 1=MSI, 2=MSI-X(unsupported, uses MSI)"); module_param(startup_timeout, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(startup_timeout, "The duration of time in seconds to wait for adapter to have it's kernel up and\nrunning. This is typically adjusted for large systems that do not have a BIOS."); +MODULE_PARM_DESC(startup_timeout, "The duration of time in seconds to wait for" + " adapter to have it's kernel up and\n" + "running. This is typically adjusted for large systems that do not" + " have a BIOS."); module_param(aif_timeout, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(aif_timeout, "The duration of time in seconds to wait for applications to pick up AIFs before\nderegistering them. This is typically adjusted for heavily burdened systems."); +MODULE_PARM_DESC(aif_timeout, "The duration of time in seconds to wait for" + " applications to pick up AIFs before\n" + "deregistering them. This is typically adjusted for heavily burdened" + " systems."); int numacb = -1; module_param(numacb, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(numacb, "Request a limit to the number of adapter control blocks (FIB) allocated. Valid values are 512 and down. Default is to use suggestion from Firmware."); +MODULE_PARM_DESC(numacb, "Request a limit to the number of adapter control" + " blocks (FIB) allocated. Valid values are 512 and down. Default is" + " to use suggestion from Firmware."); int acbsize = -1; module_param(acbsize, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(acbsize, "Request a specific adapter control block (FIB) size. Valid values are 512, 2048, 4096 and 8192. Default is to use suggestion from Firmware."); +MODULE_PARM_DESC(acbsize, "Request a specific adapter control block (FIB)" + " size. Valid values are 512, 2048, 4096 and 8192. Default is to use" + " suggestion from Firmware."); int update_interval = 30 * 60; module_param(update_interval, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(update_interval, "Interval in seconds between time sync updates issued to adapter."); +MODULE_PARM_DESC(update_interval, "Interval in seconds between time sync" + " updates issued to adapter."); int check_interval = 24 * 60 * 60; module_param(check_interval, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(check_interval, "Interval in seconds between adapter health checks."); +MODULE_PARM_DESC(check_interval, "Interval in seconds between adapter health" + " checks."); int aac_check_reset = 1; module_param_named(check_reset, aac_check_reset, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(aac_check_reset, "If adapter fails health check, reset the adapter. a value of -1 forces the reset to adapters programmed to ignore it."); +MODULE_PARM_DESC(aac_check_reset, "If adapter fails health check, reset the" + " adapter. a value of -1 forces the reset to adapters programmed to" + " ignore it."); int expose_physicals = -1; module_param(expose_physicals, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(expose_physicals, "Expose physical components of the arrays. -1=protect 0=off, 1=on"); +MODULE_PARM_DESC(expose_physicals, "Expose physical components of the arrays." + " -1=protect 0=off, 1=on"); -int aac_reset_devices = 0; +int aac_reset_devices; module_param_named(reset_devices, aac_reset_devices, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(reset_devices, "Force an adapter reset at initialization."); @@ -1315,7 +1341,7 @@ int aac_get_adapter_info(struct aac_dev* dev) (int)sizeof(dev->supplement_adapter_info.VpdInfo.Tsid), dev->supplement_adapter_info.VpdInfo.Tsid); } - if (!aac_check_reset || ((aac_check_reset != 1) && + if (!aac_check_reset || ((aac_check_reset == 1) && (dev->supplement_adapter_info.SupportedOptions2 & AAC_OPTION_IGNORE_RESET))) { printk(KERN_INFO "%s%d: Reset Adapter Ignored\n", @@ -1353,13 +1379,14 @@ int aac_get_adapter_info(struct aac_dev* dev) if (nondasd != -1) dev->nondasd_support = (nondasd!=0); - if(dev->nondasd_support != 0) { + if (dev->nondasd_support && !dev->in_reset) printk(KERN_INFO "%s%d: Non-DASD support enabled.\n",dev->name, dev->id); - } dev->dac_support = 0; if( (sizeof(dma_addr_t) > 4) && (dev->adapter_info.options & AAC_OPT_SGMAP_HOST64)){ - printk(KERN_INFO "%s%d: 64bit support enabled.\n", dev->name, dev->id); + if (!dev->in_reset) + printk(KERN_INFO "%s%d: 64bit support enabled.\n", + dev->name, dev->id); dev->dac_support = 1; } @@ -1369,8 +1396,9 @@ int aac_get_adapter_info(struct aac_dev* dev) if(dev->dac_support != 0) { if (!pci_set_dma_mask(dev->pdev, DMA_64BIT_MASK) && !pci_set_consistent_dma_mask(dev->pdev, DMA_64BIT_MASK)) { - printk(KERN_INFO"%s%d: 64 Bit DAC enabled\n", - dev->name, dev->id); + if (!dev->in_reset) + printk(KERN_INFO"%s%d: 64 Bit DAC enabled\n", + dev->name, dev->id); } else if (!pci_set_dma_mask(dev->pdev, DMA_32BIT_MASK) && !pci_set_consistent_dma_mask(dev->pdev, DMA_32BIT_MASK)) { printk(KERN_INFO"%s%d: DMA mask set failed, 64 Bit DAC disabled\n", diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index 3195d29f217..ace0b751c13 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -1026,6 +1026,7 @@ struct aac_dev u8 raw_io_64; u8 printf_enabled; u8 in_reset; + u8 msi; }; #define aac_adapter_interrupt(dev) \ @@ -1881,6 +1882,7 @@ extern int startup_timeout; extern int aif_timeout; extern int expose_physicals; extern int aac_reset_devices; +extern int aac_msi; extern int aac_commit; extern int update_interval; extern int check_interval; diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 81b36923e0e..47434499e82 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -1458,7 +1458,7 @@ int aac_check_health(struct aac_dev * aac) printk(KERN_ERR "%s: Host adapter BLINK LED 0x%x\n", aac->name, BlinkLED); - if (!aac_check_reset || ((aac_check_reset != 1) && + if (!aac_check_reset || ((aac_check_reset == 1) && (aac->supplement_adapter_info.SupportedOptions2 & AAC_OPTION_IGNORE_RESET))) goto out; diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index e80d2a0c46a..ae5f74fb62d 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -275,9 +275,9 @@ static const char *aac_info(struct Scsi_Host *shost) /** * aac_get_driver_ident - * @devtype: index into lookup table + * @devtype: index into lookup table * - * Returns a pointer to the entry in the driver lookup table. + * Returns a pointer to the entry in the driver lookup table. */ struct aac_driver_ident* aac_get_driver_ident(int devtype) @@ -494,13 +494,14 @@ static int aac_change_queue_depth(struct scsi_device *sdev, int depth) static ssize_t aac_show_raid_level(struct device *dev, struct device_attribute *attr, char *buf) { - struct scsi_device * sdev = to_scsi_device(dev); + struct scsi_device *sdev = to_scsi_device(dev); + struct aac_dev *aac = (struct aac_dev *)(sdev->host->hostdata); if (sdev_channel(sdev) != CONTAINER_CHANNEL) return snprintf(buf, PAGE_SIZE, sdev->no_uld_attach - ? "Hidden\n" : "JBOD"); + ? "Hidden\n" : + ((aac->jbod && (sdev->type == TYPE_DISK)) ? "JBOD\n" : "")); return snprintf(buf, PAGE_SIZE, "%s\n", - get_container_type(((struct aac_dev *)(sdev->host->hostdata)) - ->fsa_dev[sdev_id(sdev)].type)); + get_container_type(aac->fsa_dev[sdev_id(sdev)].type)); } static struct device_attribute aac_raid_level_attr = { @@ -641,7 +642,7 @@ static int aac_eh_reset(struct scsi_cmnd* cmd) AAC_OPTION_MU_RESET) && aac_check_reset && ((aac_check_reset != 1) || - (aac->supplement_adapter_info.SupportedOptions2 & + !(aac->supplement_adapter_info.SupportedOptions2 & AAC_OPTION_IGNORE_RESET))) aac_reset_adapter(aac, 2); /* Bypass wait for command quiesce */ return SUCCESS; /* Cause an immediate retry of the command with a ten second delay after successful tur */ @@ -860,8 +861,8 @@ ssize_t aac_show_serial_number(struct class_device *class_dev, char *buf) le32_to_cpu(dev->adapter_info.serial[0])); if (len && !memcmp(&dev->supplement_adapter_info.MfgPcbaSerialNo[ - sizeof(dev->supplement_adapter_info.MfgPcbaSerialNo)+2-len], - buf, len)) + sizeof(dev->supplement_adapter_info.MfgPcbaSerialNo)-len], + buf, len-1)) len = snprintf(buf, PAGE_SIZE, "%.*s\n", (int)sizeof(dev->supplement_adapter_info.MfgPcbaSerialNo), dev->supplement_adapter_info.MfgPcbaSerialNo); @@ -1004,32 +1005,32 @@ static const struct file_operations aac_cfg_fops = { static struct scsi_host_template aac_driver_template = { .module = THIS_MODULE, - .name = "AAC", + .name = "AAC", .proc_name = AAC_DRIVERNAME, - .info = aac_info, - .ioctl = aac_ioctl, + .info = aac_info, + .ioctl = aac_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = aac_compat_ioctl, #endif - .queuecommand = aac_queuecommand, - .bios_param = aac_biosparm, + .queuecommand = aac_queuecommand, + .bios_param = aac_biosparm, .shost_attrs = aac_attrs, .slave_configure = aac_slave_configure, .change_queue_depth = aac_change_queue_depth, .sdev_attrs = aac_dev_attrs, .eh_abort_handler = aac_eh_abort, .eh_host_reset_handler = aac_eh_reset, - .can_queue = AAC_NUM_IO_FIB, - .this_id = MAXIMUM_NUM_CONTAINERS, - .sg_tablesize = 16, - .max_sectors = 128, + .can_queue = AAC_NUM_IO_FIB, + .this_id = MAXIMUM_NUM_CONTAINERS, + .sg_tablesize = 16, + .max_sectors = 128, #if (AAC_NUM_IO_FIB > 256) .cmd_per_lun = 256, #else - .cmd_per_lun = AAC_NUM_IO_FIB, + .cmd_per_lun = AAC_NUM_IO_FIB, #endif .use_clustering = ENABLE_CLUSTERING, - .emulated = 1, + .emulated = 1, }; static void __aac_shutdown(struct aac_dev * aac) @@ -1039,6 +1040,8 @@ static void __aac_shutdown(struct aac_dev * aac) aac_send_shutdown(aac); aac_adapter_disable_int(aac); free_irq(aac->pdev->irq, aac); + if (aac->msi) + pci_disable_msi(aac->pdev); } static int __devinit aac_probe_one(struct pci_dev *pdev, @@ -1254,7 +1257,7 @@ static struct pci_driver aac_pci_driver = { .id_table = aac_pci_tbl, .probe = aac_probe_one, .remove = __devexit_p(aac_remove_one), - .shutdown = aac_shutdown, + .shutdown = aac_shutdown, }; static int __init aac_init(void) @@ -1271,7 +1274,7 @@ static int __init aac_init(void) aac_cfg_major = register_chrdev( 0, "aac", &aac_cfg_fops); if (aac_cfg_major < 0) { printk(KERN_WARNING - "aacraid: unable to register \"aac\" device.\n"); + "aacraid: unable to register \"aac\" device.\n"); } return 0; diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c index a08bbf1fd76..1f18b83e1e0 100644 --- a/drivers/scsi/aacraid/rx.c +++ b/drivers/scsi/aacraid/rx.c @@ -625,8 +625,11 @@ int _aac_rx_init(struct aac_dev *dev) if (aac_init_adapter(dev) == NULL) goto error_iounmap; aac_adapter_comm(dev, dev->comm_interface); - if (request_irq(dev->scsi_host_ptr->irq, dev->a_ops.adapter_intr, + dev->msi = aac_msi && !pci_enable_msi(dev->pdev); + if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr, IRQF_SHARED|IRQF_DISABLED, "aacraid", dev) < 0) { + if (dev->msi) + pci_disable_msi(dev->pdev); printk(KERN_ERR "%s%d: Interrupt unavailable.\n", name, instance); goto error_iounmap; diff --git a/drivers/scsi/aacraid/sa.c b/drivers/scsi/aacraid/sa.c index 85b91bc578c..cfc3410ec07 100644 --- a/drivers/scsi/aacraid/sa.c +++ b/drivers/scsi/aacraid/sa.c @@ -31,6 +31,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> +#include <linux/pci.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/blkdev.h> @@ -385,7 +386,7 @@ int aac_sa_init(struct aac_dev *dev) if(aac_init_adapter(dev) == NULL) goto error_irq; - if (request_irq(dev->scsi_host_ptr->irq, dev->a_ops.adapter_intr, + if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr, IRQF_SHARED|IRQF_DISABLED, "aacraid", (void *)dev ) < 0) { printk(KERN_WARNING "%s%d: Interrupt unavailable.\n", @@ -403,7 +404,7 @@ int aac_sa_init(struct aac_dev *dev) error_irq: aac_sa_disable_interrupt(dev); - free_irq(dev->scsi_host_ptr->irq, (void *)dev); + free_irq(dev->pdev->irq, (void *)dev); error_iounmap: diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index ccef891d642..3c2d6888bb8 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -566,7 +566,7 @@ typedef struct asc_dvc_var { ASC_SCSI_BIT_ID_TYPE unit_not_ready; ASC_SCSI_BIT_ID_TYPE queue_full_or_busy; ASC_SCSI_BIT_ID_TYPE start_motor; - uchar overrun_buf[ASC_OVERRUN_BSIZE] __aligned(8); + uchar *overrun_buf; dma_addr_t overrun_dma; uchar scsi_reset_wait; uchar chip_no; @@ -13833,6 +13833,12 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost, */ if (ASC_NARROW_BOARD(boardp)) { ASC_DBG(2, "AscInitAsc1000Driver()\n"); + + asc_dvc_varp->overrun_buf = kzalloc(ASC_OVERRUN_BSIZE, GFP_KERNEL); + if (!asc_dvc_varp->overrun_buf) { + ret = -ENOMEM; + goto err_free_wide_mem; + } warn_code = AscInitAsc1000Driver(asc_dvc_varp); if (warn_code || asc_dvc_varp->err_code) { @@ -13840,8 +13846,10 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost, "warn 0x%x, error 0x%x\n", asc_dvc_varp->init_state, warn_code, asc_dvc_varp->err_code); - if (asc_dvc_varp->err_code) + if (asc_dvc_varp->err_code) { ret = -ENODEV; + kfree(asc_dvc_varp->overrun_buf); + } } } else { if (advansys_wide_init_chip(shost)) @@ -13894,6 +13902,7 @@ static int advansys_release(struct Scsi_Host *shost) dma_unmap_single(board->dev, board->dvc_var.asc_dvc_var.overrun_dma, ASC_OVERRUN_BSIZE, DMA_FROM_DEVICE); + kfree(board->dvc_var.asc_dvc_var.overrun_buf); } else { iounmap(board->ioremap_addr); advansys_wide_free_mem(board); diff --git a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c index 4150c8a8fdc..dfaaae5e73a 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c @@ -89,7 +89,7 @@ ahd_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg) pci_save_state(pdev); pci_disable_device(pdev); - if (mesg.event == PM_EVENT_SUSPEND) + if (mesg.event & PM_EVENT_SLEEP) pci_set_power_state(pdev, PCI_D3hot); return rc; diff --git a/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c index 6d2ae641273..64e62ce59c1 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_core.c +++ b/drivers/scsi/aic7xxx/aic7xxx_core.c @@ -695,15 +695,16 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) scb_index = ahc_inb(ahc, SCB_TAG); scb = ahc_lookup_scb(ahc, scb_index); if (devinfo.role == ROLE_INITIATOR) { - if (scb == NULL) - panic("HOST_MSG_LOOP with " - "invalid SCB %x\n", scb_index); + if (bus_phase == P_MESGOUT) { + if (scb == NULL) + panic("HOST_MSG_LOOP with " + "invalid SCB %x\n", + scb_index); - if (bus_phase == P_MESGOUT) ahc_setup_initiator_msgout(ahc, &devinfo, scb); - else { + } else { ahc->msg_type = MSG_TYPE_INITIATOR_MSGIN; ahc->msgin_index = 0; diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c index dd6e21d6f1d..3d3eaef65fb 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c @@ -134,7 +134,7 @@ ahc_linux_pci_dev_suspend(struct pci_dev *pdev, pm_message_t mesg) pci_save_state(pdev); pci_disable_device(pdev); - if (mesg.event == PM_EVENT_SUSPEND) + if (mesg.event & PM_EVENT_SLEEP) pci_set_power_state(pdev, PCI_D3hot); return rc; diff --git a/drivers/scsi/aic94xx/aic94xx_sas.h b/drivers/scsi/aic94xx/aic94xx_sas.h index fa7c5290257..912e6b755f7 100644 --- a/drivers/scsi/aic94xx/aic94xx_sas.h +++ b/drivers/scsi/aic94xx/aic94xx_sas.h @@ -292,7 +292,7 @@ struct scb_header { #define INITIATE_SSP_TASK 0x00 #define INITIATE_LONG_SSP_TASK 0x01 #define INITIATE_BIDIR_SSP_TASK 0x02 -#define ABORT_TASK 0x03 +#define SCB_ABORT_TASK 0x03 #define INITIATE_SSP_TMF 0x04 #define SSP_TARG_GET_DATA 0x05 #define SSP_TARG_GET_DATA_GOOD 0x06 diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c index 0febad4dd75..ab350504ca5 100644 --- a/drivers/scsi/aic94xx/aic94xx_scb.c +++ b/drivers/scsi/aic94xx/aic94xx_scb.c @@ -458,13 +458,19 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, tc_abort = le16_to_cpu(tc_abort); list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) { - struct sas_task *task = ascb->uldd_task; + struct sas_task *task = a->uldd_task; + + if (a->tc_index != tc_abort) + continue; - if (task && a->tc_index == tc_abort) { + if (task) { failed_dev = task->dev; sas_task_abort(task); - break; + } else { + ASD_DPRINTK("R_T_A for non TASK scb 0x%x\n", + a->scb->header.opcode); } + break; } if (!failed_dev) { @@ -478,7 +484,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb, * that the EH will wake up and do something. */ list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) { - struct sas_task *task = ascb->uldd_task; + struct sas_task *task = a->uldd_task; if (task && task->dev == failed_dev && diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c index 87b2f6e6adf..144f5ad2045 100644 --- a/drivers/scsi/aic94xx/aic94xx_tmf.c +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c @@ -151,8 +151,6 @@ static int asd_clear_nexus_I_T(struct domain_device *dev) CLEAR_NEXUS_PRE; scb->clear_nexus.nexus = NEXUS_I_T; scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ; - if (dev->tproto) - scb->clear_nexus.flags |= SUSPEND_TX; scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long) dev->lldd_dev); CLEAR_NEXUS_POST; @@ -169,8 +167,6 @@ static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun) CLEAR_NEXUS_PRE; scb->clear_nexus.nexus = NEXUS_I_T_L; scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ; - if (dev->tproto) - scb->clear_nexus.flags |= SUSPEND_TX; memcpy(scb->clear_nexus.ssp_task.lun, lun, 8); scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long) dev->lldd_dev); @@ -369,7 +365,7 @@ int asd_abort_task(struct sas_task *task) return -ENOMEM; scb = ascb->scb; - scb->header.opcode = ABORT_TASK; + scb->header.opcode = SCB_ABORT_TASK; switch (task->task_proto) { case SAS_PROTOCOL_SATA: diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 4f9ff32cfed..f91f79c8007 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -1387,18 +1387,16 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, \ switch(controlcode) { case ARCMSR_MESSAGE_READ_RQBUFFER: { - unsigned long *ver_addr; + unsigned char *ver_addr; uint8_t *pQbuffer, *ptmpQbuffer; int32_t allxfer_len = 0; - void *tmp; - tmp = kmalloc(1032, GFP_KERNEL|GFP_DMA); - ver_addr = (unsigned long *)tmp; - if (!tmp) { + ver_addr = kmalloc(1032, GFP_ATOMIC); + if (!ver_addr) { retvalue = ARCMSR_MESSAGE_FAIL; goto message_out; } - ptmpQbuffer = (uint8_t *) ver_addr; + ptmpQbuffer = ver_addr; while ((acb->rqbuf_firstindex != acb->rqbuf_lastindex) && (allxfer_len < 1031)) { pQbuffer = &acb->rqbuffer[acb->rqbuf_firstindex]; @@ -1427,26 +1425,24 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, \ } arcmsr_iop_message_read(acb); } - memcpy(pcmdmessagefld->messagedatabuffer, (uint8_t *)ver_addr, allxfer_len); + memcpy(pcmdmessagefld->messagedatabuffer, ver_addr, allxfer_len); pcmdmessagefld->cmdmessage.Length = allxfer_len; pcmdmessagefld->cmdmessage.ReturnCode = ARCMSR_MESSAGE_RETURNCODE_OK; - kfree(tmp); + kfree(ver_addr); } break; case ARCMSR_MESSAGE_WRITE_WQBUFFER: { - unsigned long *ver_addr; + unsigned char *ver_addr; int32_t my_empty_len, user_len, wqbuf_firstindex, wqbuf_lastindex; uint8_t *pQbuffer, *ptmpuserbuffer; - void *tmp; - tmp = kmalloc(1032, GFP_KERNEL|GFP_DMA); - ver_addr = (unsigned long *)tmp; - if (!tmp) { + ver_addr = kmalloc(1032, GFP_ATOMIC); + if (!ver_addr) { retvalue = ARCMSR_MESSAGE_FAIL; goto message_out; } - ptmpuserbuffer = (uint8_t *)ver_addr; + ptmpuserbuffer = ver_addr; user_len = pcmdmessagefld->cmdmessage.Length; memcpy(ptmpuserbuffer, pcmdmessagefld->messagedatabuffer, user_len); wqbuf_lastindex = acb->wqbuf_lastindex; @@ -1492,7 +1488,7 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb, \ retvalue = ARCMSR_MESSAGE_FAIL; } } - kfree(tmp); + kfree(ver_addr); } break; diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c index fb5f2028438..a715632e19d 100644 --- a/drivers/scsi/arm/fas216.c +++ b/drivers/scsi/arm/fas216.c @@ -2018,6 +2018,7 @@ static void fas216_rq_sns_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, * the upper layers to process. This would have been set * correctly by fas216_std_done. */ + scsi_eh_restore_cmnd(SCpnt, &info->ses); SCpnt->scsi_done(SCpnt); } @@ -2103,23 +2104,12 @@ request_sense: if (SCpnt->cmnd[0] == REQUEST_SENSE) goto done; + scsi_eh_prep_cmnd(SCpnt, &info->ses, NULL, 0, ~0); fas216_log_target(info, LOG_CONNECT, SCpnt->device->id, "requesting sense"); - memset(SCpnt->cmnd, 0, sizeof (SCpnt->cmnd)); - SCpnt->cmnd[0] = REQUEST_SENSE; - SCpnt->cmnd[1] = SCpnt->device->lun << 5; - SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); - SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); - SCpnt->SCp.buffer = NULL; - SCpnt->SCp.buffers_residual = 0; - SCpnt->SCp.ptr = (char *)SCpnt->sense_buffer; - SCpnt->SCp.this_residual = sizeof(SCpnt->sense_buffer); - SCpnt->SCp.phase = sizeof(SCpnt->sense_buffer); + init_SCp(SCpnt); SCpnt->SCp.Message = 0; SCpnt->SCp.Status = 0; - SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer); - SCpnt->sc_data_direction = DMA_FROM_DEVICE; - SCpnt->use_sg = 0; SCpnt->tag = 0; SCpnt->host_scribble = (void *)fas216_rq_sns_done; diff --git a/drivers/scsi/arm/fas216.h b/drivers/scsi/arm/fas216.h index 00e5f055afd..b65f4cf0eec 100644 --- a/drivers/scsi/arm/fas216.h +++ b/drivers/scsi/arm/fas216.h @@ -16,6 +16,8 @@ #define NO_IRQ 255 #endif +#include <scsi/scsi_eh.h> + #include "queue.h" #include "msgqueue.h" @@ -311,6 +313,7 @@ typedef struct { /* miscellaneous */ int internal_done; /* flag to indicate request done */ + struct scsi_eh_save ses; /* holds request sense restore info */ unsigned long magic_end; } FAS216_Info; diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index c82523908c2..6d67f5c0eb8 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -642,12 +642,15 @@ static void __init gdth_search_dev(gdth_pci_str *pcistr, ushort *cnt, *cnt, vendor, device)); pdev = NULL; - while ((pdev = pci_find_device(vendor, device, pdev)) + while ((pdev = pci_get_device(vendor, device, pdev)) != NULL) { if (pci_enable_device(pdev)) continue; - if (*cnt >= MAXHA) + if (*cnt >= MAXHA) { + pci_dev_put(pdev); return; + } + /* GDT PCI controller found, resources are already in pdev */ pcistr[*cnt].pdev = pdev; pcistr[*cnt].irq = pdev->irq; @@ -4836,6 +4839,9 @@ static int __init gdth_isa_probe_one(ulong32 isa_bios) if (error) goto out_free_coal_stat; list_add_tail(&ha->list, &gdth_instances); + + scsi_scan_host(shp); + return 0; out_free_coal_stat: @@ -4963,6 +4969,9 @@ static int __init gdth_eisa_probe_one(ushort eisa_slot) if (error) goto out_free_coal_stat; list_add_tail(&ha->list, &gdth_instances); + + scsi_scan_host(shp); + return 0; out_free_ccb_phys: @@ -5100,6 +5109,9 @@ static int __init gdth_pci_probe_one(gdth_pci_str *pcistr, int ctr) if (error) goto out_free_coal_stat; list_add_tail(&ha->list, &gdth_instances); + + scsi_scan_host(shp); + return 0; out_free_coal_stat: diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c index de5773443c6..ce0228e26ae 100644 --- a/drivers/scsi/gdth_proc.c +++ b/drivers/scsi/gdth_proc.c @@ -694,15 +694,13 @@ static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, ulong64 paddr) { ulong flags; - spin_lock_irqsave(&ha->smp_lock, flags); - if (buf == ha->pscratch) { + spin_lock_irqsave(&ha->smp_lock, flags); ha->scratch_busy = FALSE; + spin_unlock_irqrestore(&ha->smp_lock, flags); } else { pci_free_consistent(ha->pdev, size, buf, paddr); } - - spin_unlock_irqrestore(&ha->smp_lock, flags); } #ifdef GDTH_IOCTL_PROC diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 2074701f7e7..c72014a3e7d 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -5140,7 +5140,7 @@ static void ipr_build_ata_ioadl(struct ipr_cmnd *ipr_cmd, struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl; struct ipr_ioadl_desc *last_ioadl = NULL; - int len = qc->nbytes + qc->pad_len; + int len = qc->nbytes; struct scatterlist *sg; unsigned int si; @@ -5206,7 +5206,7 @@ static unsigned int ipr_qc_issue(struct ata_queued_cmd *qc) ioarcb->cmd_pkt.request_type = IPR_RQTYPE_ATA_PASSTHRU; ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_LINK_DESC; ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK; - ipr_cmd->dma_use_sg = qc->pad_len ? qc->n_elem + 1 : qc->n_elem; + ipr_cmd->dma_use_sg = qc->n_elem; ipr_build_ata_ioadl(ipr_cmd, qc); regs->flags |= IPR_ATA_FLAG_STATUS_ON_GOOD_COMPLETION; diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index bb152fb9fec..7ed568f180a 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -1576,7 +1576,7 @@ ips_make_passthru(ips_ha_t *ha, struct scsi_cmnd *SC, ips_scb_t *scb, int intr) METHOD_TRACE("ips_make_passthru", 1); scsi_for_each_sg(SC, sg, scsi_sg_count(SC), i) - length += sg[i].length; + length += sg->length; if (length < sizeof (ips_passthru_t)) { /* wrong size */ diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 0996f866f14..7cd05b599a1 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -178,8 +178,8 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) task->uldd_task = qc; if (ata_is_atapi(qc->tf.protocol)) { memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len); - task->total_xfer_len = qc->nbytes + qc->pad_len; - task->num_scatter = qc->pad_len ? qc->n_elem + 1 : qc->n_elem; + task->total_xfer_len = qc->nbytes; + task->num_scatter = qc->n_elem; } else { for_each_sg(qc->sg, sg, qc->n_elem, si) xfer += sg->length; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index f869fba8680..704ea06a6e5 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -51,10 +51,14 @@ static void sas_scsi_task_done(struct sas_task *task) { struct task_status_struct *ts = &task->task_status; struct scsi_cmnd *sc = task->uldd_task; - struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(sc->device->host); - unsigned ts_flags = task->task_state_flags; int hs = 0, stat = 0; + if (unlikely(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + /* Aborted tasks will be completed by the error handler */ + SAS_DPRINTK("task done but aborted\n"); + return; + } + if (unlikely(!sc)) { SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n"); list_del_init(&task->list); @@ -120,11 +124,7 @@ static void sas_scsi_task_done(struct sas_task *task) sc->result = (hs << 16) | stat; list_del_init(&task->list); sas_free_task(task); - /* This is very ugly but this is how SCSI Core works. */ - if (ts_flags & SAS_TASK_STATE_ABORTED) - scsi_eh_finish_cmd(sc, &sas_ha->eh_done_q); - else - sc->scsi_done(sc); + sc->scsi_done(sc); } static enum task_attribute sas_scsi_get_task_attr(struct scsi_cmnd *cmd) @@ -255,13 +255,34 @@ out: return res; } +static void sas_eh_finish_cmd(struct scsi_cmnd *cmd) +{ + struct sas_task *task = TO_SAS_TASK(cmd); + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(cmd->device->host); + + /* remove the aborted task flag to allow the task to be + * completed now. At this point, we only get called following + * an actual abort of the task, so we should be guaranteed not + * to be racing with any completions from the LLD (hence we + * don't need the task state lock to clear the flag) */ + task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; + /* Now call task_done. However, task will be free'd after + * this */ + task->task_done(task); + /* now finish the command and move it on to the error + * handler done list, this also takes it off the + * error handler pending list */ + scsi_eh_finish_cmd(cmd, &sas_ha->eh_done_q); +} + static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd *my_cmd) { struct scsi_cmnd *cmd, *n; list_for_each_entry_safe(cmd, n, error_q, eh_entry) { - if (cmd == my_cmd) - list_del_init(&cmd->eh_entry); + if (cmd->device->sdev_target == my_cmd->device->sdev_target && + cmd->device->lun == my_cmd->device->lun) + sas_eh_finish_cmd(cmd); } } @@ -274,7 +295,7 @@ static void sas_scsi_clear_queue_I_T(struct list_head *error_q, struct domain_device *x = cmd_to_domain_dev(cmd); if (x == dev) - list_del_init(&cmd->eh_entry); + sas_eh_finish_cmd(cmd); } } @@ -288,7 +309,7 @@ static void sas_scsi_clear_queue_port(struct list_head *error_q, struct asd_sas_port *x = dev->port; if (x == port) - list_del_init(&cmd->eh_entry); + sas_eh_finish_cmd(cmd); } } @@ -528,14 +549,14 @@ Again: case TASK_IS_DONE: SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__, task); - task->task_done(task); + sas_eh_finish_cmd(cmd); if (need_reset) try_to_reset_cmd_device(shost, cmd); continue; case TASK_IS_ABORTED: SAS_DPRINTK("%s: task 0x%p is aborted\n", __FUNCTION__, task); - task->task_done(task); + sas_eh_finish_cmd(cmd); if (need_reset) try_to_reset_cmd_device(shost, cmd); continue; @@ -547,7 +568,7 @@ Again: "recovered\n", SAS_ADDR(task->dev), cmd->device->lun); - task->task_done(task); + sas_eh_finish_cmd(cmd); if (need_reset) try_to_reset_cmd_device(shost, cmd); sas_scsi_clear_queue_lu(work_q, cmd); @@ -562,7 +583,7 @@ Again: if (tmf_resp == TMF_RESP_FUNC_COMPLETE) { SAS_DPRINTK("I_T %016llx recovered\n", SAS_ADDR(task->dev->sas_addr)); - task->task_done(task); + sas_eh_finish_cmd(cmd); if (need_reset) try_to_reset_cmd_device(shost, cmd); sas_scsi_clear_queue_I_T(work_q, task->dev); @@ -577,7 +598,7 @@ Again: if (res == TMF_RESP_FUNC_COMPLETE) { SAS_DPRINTK("clear nexus port:%d " "succeeded\n", port->id); - task->task_done(task); + sas_eh_finish_cmd(cmd); if (need_reset) try_to_reset_cmd_device(shost, cmd); sas_scsi_clear_queue_port(work_q, @@ -591,10 +612,10 @@ Again: if (res == TMF_RESP_FUNC_COMPLETE) { SAS_DPRINTK("clear nexus ha " "succeeded\n"); - task->task_done(task); + sas_eh_finish_cmd(cmd); if (need_reset) try_to_reset_cmd_device(shost, cmd); - goto out; + goto clear_q; } } /* If we are here -- this means that no amount @@ -606,21 +627,18 @@ Again: SAS_ADDR(task->dev->sas_addr), cmd->device->lun); - task->task_done(task); + sas_eh_finish_cmd(cmd); if (need_reset) try_to_reset_cmd_device(shost, cmd); goto clear_q; } } -out: return list_empty(work_q); clear_q: SAS_DPRINTK("--- Exit %s -- clear_q\n", __FUNCTION__); - list_for_each_entry_safe(cmd, n, work_q, eh_entry) { - struct sas_task *task = TO_SAS_TASK(cmd); - list_del_init(&cmd->eh_entry); - task->task_done(task); - } + list_for_each_entry_safe(cmd, n, work_q, eh_entry) + sas_eh_finish_cmd(cmd); + return list_empty(work_q); } diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 83567b9755b..2ab2d24dcc1 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2007 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -307,6 +307,7 @@ struct lpfc_vport { uint32_t fc_nlp_cnt; /* outstanding NODELIST requests */ uint32_t fc_rscn_id_cnt; /* count of RSCNs payloads in list */ + uint32_t fc_rscn_flush; /* flag use of fc_rscn_id_list */ struct lpfc_dmabuf *fc_rscn_id_list[FC_MAX_HOLD_RSCN]; struct lpfc_name fc_nodename; /* fc nodename */ struct lpfc_name fc_portname; /* fc portname */ @@ -392,6 +393,13 @@ enum hba_temp_state { HBA_OVER_TEMP }; +enum intr_type_t { + NONE = 0, + INTx, + MSI, + MSIX, +}; + struct lpfc_hba { struct lpfc_sli sli; uint32_t sli_rev; /* SLI2 or SLI3 */ @@ -409,7 +417,7 @@ struct lpfc_hba { /* This flag is set while issuing */ /* INIT_LINK mailbox command */ #define LS_NPIV_FAB_SUPPORTED 0x2 /* Fabric supports NPIV */ -#define LS_IGNORE_ERATT 0x3 /* intr handler should ignore ERATT */ +#define LS_IGNORE_ERATT 0x4 /* intr handler should ignore ERATT */ struct lpfc_sli2_slim *slim2p; struct lpfc_dmabuf hbqslimp; @@ -487,6 +495,8 @@ struct lpfc_hba { wait_queue_head_t *work_wait; struct task_struct *worker_thread; + uint32_t hbq_in_use; /* HBQs in use flag */ + struct list_head hbqbuf_in_list; /* in-fly hbq buffer list */ uint32_t hbq_count; /* Count of configured HBQs */ struct hbq_s hbqs[LPFC_MAX_HBQS]; /* local copy of hbq indicies */ @@ -555,7 +565,8 @@ struct lpfc_hba { mempool_t *nlp_mem_pool; struct fc_host_statistics link_stats; - uint8_t using_msi; + enum intr_type_t intr_type; + struct msix_entry msix_entries[1]; struct list_head port_list; struct lpfc_vport *pport; /* physical lpfc_vport pointer */ @@ -595,6 +606,8 @@ struct lpfc_hba { unsigned long last_completion_time; struct timer_list hb_tmofunc; uint8_t hb_outstanding; + /* ndlp reference management */ + spinlock_t ndlp_lock; /* * Following bit will be set for all buffer tags which are not * associated with any HBQ. diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 4bae4a2ed2f..b12a841703c 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -1191,7 +1191,7 @@ lpfc_update_rport_devloss_tmo(struct lpfc_vport *vport) shost = lpfc_shost_from_vport(vport); spin_lock_irq(shost->host_lock); list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) - if (ndlp->rport) + if (NLP_CHK_NODE_ACT(ndlp) && ndlp->rport) ndlp->rport->dev_loss_tmo = vport->cfg_devloss_tmo; spin_unlock_irq(shost->host_lock); } @@ -1592,9 +1592,11 @@ LPFC_ATTR_RW(poll_tmo, 10, 1, 255, # support this feature # 0 = MSI disabled (default) # 1 = MSI enabled -# Value range is [0,1]. Default value is 0. +# 2 = MSI-X enabled +# Value range is [0,2]. Default value is 0. */ -LPFC_ATTR_R(use_msi, 0, 0, 1, "Use Message Signaled Interrupts, if possible"); +LPFC_ATTR_R(use_msi, 0, 0, 2, "Use Message Signaled Interrupts (1) or " + "MSI-X (2), if possible"); /* # lpfc_enable_hba_reset: Allow or prevent HBA resets to the hardware. @@ -1946,11 +1948,13 @@ sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr, } /* If HBA encountered an error attention, allow only DUMP - * mailbox command until the HBA is restarted. + * or RESTART mailbox commands until the HBA is restarted. */ if ((phba->pport->stopped) && - (phba->sysfs_mbox.mbox->mb.mbxCommand - != MBX_DUMP_MEMORY)) { + (phba->sysfs_mbox.mbox->mb.mbxCommand != + MBX_DUMP_MEMORY && + phba->sysfs_mbox.mbox->mb.mbxCommand != + MBX_RESTART)) { sysfs_mbox_idle(phba); spin_unlock_irq(&phba->hbalock); return -EPERM; @@ -2384,7 +2388,8 @@ lpfc_get_node_by_target(struct scsi_target *starget) spin_lock_irq(shost->host_lock); /* Search for this, mapped, target ID */ list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { - if (ndlp->nlp_state == NLP_STE_MAPPED_NODE && + if (NLP_CHK_NODE_ACT(ndlp) && + ndlp->nlp_state == NLP_STE_MAPPED_NODE && starget->id == ndlp->nlp_sid) { spin_unlock_irq(shost->host_lock); return ndlp; diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 50fcb7c930b..0819f5f39de 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2007 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -53,7 +53,10 @@ void lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *, LPFC_MBOXQ_t *); void lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); void lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); void lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_enqueue_node(struct lpfc_vport *, struct lpfc_nodelist *); void lpfc_dequeue_node(struct lpfc_vport *, struct lpfc_nodelist *); +struct lpfc_nodelist *lpfc_enable_node(struct lpfc_vport *, + struct lpfc_nodelist *, int); void lpfc_nlp_set_state(struct lpfc_vport *, struct lpfc_nodelist *, int); void lpfc_drop_node(struct lpfc_vport *, struct lpfc_nodelist *); void lpfc_set_disctmo(struct lpfc_vport *); diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 92441ce610e..3d0ccd9b341 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2007 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -294,7 +294,7 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp, /* Save for completion so we can release these resources */ geniocb->context1 = (uint8_t *) inp; geniocb->context2 = (uint8_t *) outp; - geniocb->context_un.ndlp = ndlp; + geniocb->context_un.ndlp = lpfc_nlp_get(ndlp); /* Fill in payload, bp points to frame payload */ icmd->ulpCommand = CMD_GEN_REQUEST64_CR; @@ -489,8 +489,10 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size) */ ndlp = lpfc_findnode_did(vport, Did); - if (ndlp && (ndlp->nlp_type & - NLP_FCP_TARGET)) + if (ndlp && + NLP_CHK_NODE_ACT(ndlp) + && (ndlp->nlp_type & + NLP_FCP_TARGET)) lpfc_setup_disc_node (vport, Did); else if (lpfc_ns_cmd(vport, @@ -773,7 +775,7 @@ lpfc_cmpl_ct_cmd_gff_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, "0267 NameServer GFF Rsp " "x%x Error (%d %d) Data: x%x x%x\n", did, irsp->ulpStatus, irsp->un.ulpWord[4], - vport->fc_flag, vport->fc_rscn_id_cnt) + vport->fc_flag, vport->fc_rscn_id_cnt); } /* This is a target port, unregistered port, or the GFF_ID failed */ @@ -1064,7 +1066,8 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, int rc = 0; ndlp = lpfc_findnode_did(vport, NameServer_DID); - if (ndlp == NULL || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) { + if (!ndlp || !NLP_CHK_NODE_ACT(ndlp) + || ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) { rc=1; goto ns_cmd_exit; } @@ -1213,8 +1216,9 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, cmpl = lpfc_cmpl_ct_cmd_rff_id; break; } - lpfc_nlp_get(ndlp); - + /* The lpfc_ct_cmd/lpfc_get_req shall increment ndlp reference count + * to hold ndlp reference for the corresponding callback function. + */ if (!lpfc_ct_cmd(vport, mp, bmp, ndlp, cmpl, rsp_size, retry)) { /* On success, The cmpl function will free the buffers */ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT, @@ -1222,9 +1226,13 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, cmdcode, ndlp->nlp_DID, 0); return 0; } - rc=6; + + /* Decrement ndlp reference count to release ndlp reference held + * for the failed command's callback function. + */ lpfc_nlp_put(ndlp); + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); ns_cmd_free_bmp: kfree(bmp); @@ -1271,6 +1279,9 @@ lpfc_cmpl_ct_cmd_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, } ndlp = lpfc_findnode_did(vport, FDMI_DID); + if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) + goto fail_out; + if (fdmi_rsp == be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) { /* FDMI rsp failed */ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, @@ -1294,6 +1305,8 @@ lpfc_cmpl_ct_cmd_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RHBA); break; } + +fail_out: lpfc_ct_free_iocb(phba, cmdiocb); return; } @@ -1650,12 +1663,18 @@ lpfc_fdmi_cmd(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, int cmdcode) bpl->tus.w = le32_to_cpu(bpl->tus.w); cmpl = lpfc_cmpl_ct_cmd_fdmi; - lpfc_nlp_get(ndlp); + /* The lpfc_ct_cmd/lpfc_get_req shall increment ndlp reference count + * to hold ndlp reference for the corresponding callback function. + */ if (!lpfc_ct_cmd(vport, mp, bmp, ndlp, cmpl, FC_MAX_NS_RSP, 0)) return 0; + /* Decrement ndlp reference count to release ndlp reference held + * for the failed command's callback function. + */ lpfc_nlp_put(ndlp); + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); fdmi_cmd_free_bmp: kfree(bmp); @@ -1698,7 +1717,7 @@ lpfc_fdmi_timeout_handler(struct lpfc_vport *vport) struct lpfc_nodelist *ndlp; ndlp = lpfc_findnode_did(vport, FDMI_DID); - if (ndlp) { + if (ndlp && NLP_CHK_NODE_ACT(ndlp)) { if (init_utsname()->nodename[0] != '\0') lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA); else diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h index cfe81c50529..2db0b74b6fa 100644 --- a/drivers/scsi/lpfc/lpfc_disc.h +++ b/drivers/scsi/lpfc/lpfc_disc.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2007 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -73,6 +73,12 @@ struct lpfc_nodelist { uint8_t nlp_fcp_info; /* class info, bits 0-3 */ #define NLP_FCP_2_DEVICE 0x10 /* FCP-2 device */ + uint16_t nlp_usg_map; /* ndlp management usage bitmap */ +#define NLP_USG_NODE_ACT_BIT 0x1 /* Indicate ndlp is actively used */ +#define NLP_USG_IACT_REQ_BIT 0x2 /* Request to inactivate ndlp */ +#define NLP_USG_FREE_REQ_BIT 0x4 /* Request to invoke ndlp memory free */ +#define NLP_USG_FREE_ACK_BIT 0x8 /* Indicate ndlp memory free invoked */ + struct timer_list nlp_delayfunc; /* Used for delayed ELS cmds */ struct fc_rport *rport; /* Corresponding FC transport port structure */ @@ -85,25 +91,51 @@ struct lpfc_nodelist { }; /* Defines for nlp_flag (uint32) */ -#define NLP_PLOGI_SND 0x20 /* sent PLOGI request for this entry */ -#define NLP_PRLI_SND 0x40 /* sent PRLI request for this entry */ -#define NLP_ADISC_SND 0x80 /* sent ADISC request for this entry */ -#define NLP_LOGO_SND 0x100 /* sent LOGO request for this entry */ -#define NLP_RNID_SND 0x400 /* sent RNID request for this entry */ -#define NLP_ELS_SND_MASK 0x7e0 /* sent ELS request for this entry */ -#define NLP_DEFER_RM 0x10000 /* Remove this ndlp if no longer used */ -#define NLP_DELAY_TMO 0x20000 /* delay timeout is running for node */ -#define NLP_NPR_2B_DISC 0x40000 /* node is included in num_disc_nodes */ -#define NLP_RCV_PLOGI 0x80000 /* Rcv'ed PLOGI from remote system */ -#define NLP_LOGO_ACC 0x100000 /* Process LOGO after ACC completes */ -#define NLP_TGT_NO_SCSIID 0x200000 /* good PRLI but no binding for scsid */ -#define NLP_ACC_REGLOGIN 0x1000000 /* Issue Reg Login after successful +#define NLP_PLOGI_SND 0x00000020 /* sent PLOGI request for this entry */ +#define NLP_PRLI_SND 0x00000040 /* sent PRLI request for this entry */ +#define NLP_ADISC_SND 0x00000080 /* sent ADISC request for this entry */ +#define NLP_LOGO_SND 0x00000100 /* sent LOGO request for this entry */ +#define NLP_RNID_SND 0x00000400 /* sent RNID request for this entry */ +#define NLP_ELS_SND_MASK 0x000007e0 /* sent ELS request for this entry */ +#define NLP_DEFER_RM 0x00010000 /* Remove this ndlp if no longer used */ +#define NLP_DELAY_TMO 0x00020000 /* delay timeout is running for node */ +#define NLP_NPR_2B_DISC 0x00040000 /* node is included in num_disc_nodes */ +#define NLP_RCV_PLOGI 0x00080000 /* Rcv'ed PLOGI from remote system */ +#define NLP_LOGO_ACC 0x00100000 /* Process LOGO after ACC completes */ +#define NLP_TGT_NO_SCSIID 0x00200000 /* good PRLI but no binding for scsid */ +#define NLP_ACC_REGLOGIN 0x01000000 /* Issue Reg Login after successful ACC */ -#define NLP_NPR_ADISC 0x2000000 /* Issue ADISC when dq'ed from +#define NLP_NPR_ADISC 0x02000000 /* Issue ADISC when dq'ed from NPR list */ -#define NLP_RM_DFLT_RPI 0x4000000 /* need to remove leftover dflt RPI */ -#define NLP_NODEV_REMOVE 0x8000000 /* Defer removal till discovery ends */ +#define NLP_RM_DFLT_RPI 0x04000000 /* need to remove leftover dflt RPI */ +#define NLP_NODEV_REMOVE 0x08000000 /* Defer removal till discovery ends */ #define NLP_TARGET_REMOVE 0x10000000 /* Target remove in process */ +#define NLP_SC_REQ 0x20000000 /* Target requires authentication */ + +/* ndlp usage management macros */ +#define NLP_CHK_NODE_ACT(ndlp) (((ndlp)->nlp_usg_map \ + & NLP_USG_NODE_ACT_BIT) \ + && \ + !((ndlp)->nlp_usg_map \ + & NLP_USG_FREE_ACK_BIT)) +#define NLP_SET_NODE_ACT(ndlp) ((ndlp)->nlp_usg_map \ + |= NLP_USG_NODE_ACT_BIT) +#define NLP_INT_NODE_ACT(ndlp) ((ndlp)->nlp_usg_map \ + = NLP_USG_NODE_ACT_BIT) +#define NLP_CLR_NODE_ACT(ndlp) ((ndlp)->nlp_usg_map \ + &= ~NLP_USG_NODE_ACT_BIT) +#define NLP_CHK_IACT_REQ(ndlp) ((ndlp)->nlp_usg_map \ + & NLP_USG_IACT_REQ_BIT) +#define NLP_SET_IACT_REQ(ndlp) ((ndlp)->nlp_usg_map \ + |= NLP_USG_IACT_REQ_BIT) +#define NLP_CHK_FREE_REQ(ndlp) ((ndlp)->nlp_usg_map \ + & NLP_USG_FREE_REQ_BIT) +#define NLP_SET_FREE_REQ(ndlp) ((ndlp)->nlp_usg_map \ + |= NLP_USG_FREE_REQ_BIT) +#define NLP_CHK_FREE_ACK(ndlp) ((ndlp)->nlp_usg_map \ + & NLP_USG_FREE_ACK_BIT) +#define NLP_SET_FREE_ACK(ndlp) ((ndlp)->nlp_usg_map \ + |= NLP_USG_FREE_ACK_BIT) /* There are 4 different double linked lists nodelist entries can reside on. * The Port Login (PLOGI) list and Address Discovery (ADISC) list are used diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index c6b739dc6bc..cbb68a94225 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2007 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -113,6 +113,7 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp, if (elsiocb == NULL) return NULL; + icmd = &elsiocb->iocb; /* fill in BDEs for command */ @@ -134,9 +135,8 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp, if (!prsp || !prsp->virt) goto els_iocb_free_prsp_exit; INIT_LIST_HEAD(&prsp->list); - } else { + } else prsp = NULL; - } /* Allocate buffer for Buffer ptr list */ pbuflist = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); @@ -246,7 +246,7 @@ lpfc_issue_fabric_reglogin(struct lpfc_vport *vport) sp = &phba->fc_fabparam; ndlp = lpfc_findnode_did(vport, Fabric_DID); - if (!ndlp) { + if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) { err = 1; goto fail; } @@ -282,6 +282,9 @@ lpfc_issue_fabric_reglogin(struct lpfc_vport *vport) mbox->mbox_cmpl = lpfc_mbx_cmpl_fabric_reg_login; mbox->vport = vport; + /* increment the reference count on ndlp to hold reference + * for the callback routine. + */ mbox->context2 = lpfc_nlp_get(ndlp); rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT); @@ -293,6 +296,9 @@ lpfc_issue_fabric_reglogin(struct lpfc_vport *vport) return 0; fail_issue_reg_login: + /* decrement the reference count on ndlp just incremented + * for the failed mbox command. + */ lpfc_nlp_put(ndlp); mp = (struct lpfc_dmabuf *) mbox->context1; lpfc_mbuf_free(phba, mp->virt, mp->phys); @@ -381,6 +387,8 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, */ list_for_each_entry_safe(np, next_np, &vport->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) + continue; if ((np->nlp_state != NLP_STE_NPR_NODE) || !(np->nlp_flag & NLP_NPR_ADISC)) continue; @@ -456,6 +464,9 @@ lpfc_cmpl_els_flogi_nport(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, mempool_free(mbox, phba->mbox_mem_pool); goto fail; } + /* Decrement ndlp reference count indicating that ndlp can be + * safely released when other references to it are done. + */ lpfc_nlp_put(ndlp); ndlp = lpfc_findnode_did(vport, PT2PT_RemoteID); @@ -467,22 +478,29 @@ lpfc_cmpl_els_flogi_nport(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); if (!ndlp) goto fail; - lpfc_nlp_init(vport, ndlp, PT2PT_RemoteID); + } else if (!NLP_CHK_NODE_ACT(ndlp)) { + ndlp = lpfc_enable_node(vport, ndlp, + NLP_STE_UNUSED_NODE); + if(!ndlp) + goto fail; } memcpy(&ndlp->nlp_portname, &sp->portName, sizeof(struct lpfc_name)); memcpy(&ndlp->nlp_nodename, &sp->nodeName, sizeof(struct lpfc_name)); + /* Set state will put ndlp onto node list if not already done */ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); spin_lock_irq(shost->host_lock); ndlp->nlp_flag |= NLP_NPR_2B_DISC; spin_unlock_irq(shost->host_lock); - } else { - /* This side will wait for the PLOGI */ + } else + /* This side will wait for the PLOGI, decrement ndlp reference + * count indicating that ndlp can be released when other + * references to it are done. + */ lpfc_nlp_put(ndlp); - } /* If we are pt2pt with another NPort, force NPIV off! */ phba->sli3_options &= ~LPFC_SLI3_NPIV_ENABLED; @@ -728,16 +746,21 @@ lpfc_initial_flogi(struct lpfc_vport *vport) if (!ndlp) return 0; lpfc_nlp_init(vport, ndlp, Fabric_DID); - } else { - lpfc_dequeue_node(vport, ndlp); + /* Put ndlp onto node list */ + lpfc_enqueue_node(vport, ndlp); + } else if (!NLP_CHK_NODE_ACT(ndlp)) { + /* re-setup ndlp without removing from node list */ + ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE); + if (!ndlp) + return 0; } - if (lpfc_issue_els_flogi(vport, ndlp, 0)) { + if (lpfc_issue_els_flogi(vport, ndlp, 0)) /* This decrement of reference count to node shall kick off * the release of the node. */ lpfc_nlp_put(ndlp); - } + return 1; } @@ -755,9 +778,15 @@ lpfc_initial_fdisc(struct lpfc_vport *vport) if (!ndlp) return 0; lpfc_nlp_init(vport, ndlp, Fabric_DID); - } else { - lpfc_dequeue_node(vport, ndlp); + /* Put ndlp onto node list */ + lpfc_enqueue_node(vport, ndlp); + } else if (!NLP_CHK_NODE_ACT(ndlp)) { + /* re-setup ndlp without removing from node list */ + ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE); + if (!ndlp) + return 0; } + if (lpfc_issue_els_fdisc(vport, ndlp, 0)) { /* decrement node reference count to trigger the release of * the node. @@ -816,7 +845,7 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, */ new_ndlp = lpfc_findnode_wwpn(vport, &sp->portName); - if (new_ndlp == ndlp) + if (new_ndlp == ndlp && NLP_CHK_NODE_ACT(new_ndlp)) return ndlp; if (!new_ndlp) { @@ -827,8 +856,12 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, new_ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC); if (!new_ndlp) return ndlp; - lpfc_nlp_init(vport, new_ndlp, ndlp->nlp_DID); + } else if (!NLP_CHK_NODE_ACT(new_ndlp)) { + new_ndlp = lpfc_enable_node(vport, new_ndlp, + NLP_STE_UNUSED_NODE); + if (!new_ndlp) + return ndlp; } lpfc_unreg_rpi(vport, new_ndlp); @@ -839,6 +872,7 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, new_ndlp->nlp_flag |= NLP_NPR_2B_DISC; ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + /* Set state will put new_ndlp on to node list if not already done */ lpfc_nlp_set_state(vport, new_ndlp, ndlp->nlp_state); /* Move this back to NPR state */ @@ -912,7 +946,7 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, irsp->un.elsreq64.remoteID); ndlp = lpfc_findnode_did(vport, irsp->un.elsreq64.remoteID); - if (!ndlp) { + if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, "0136 PLOGI completes to NPort x%x " "with no ndlp. Data: x%x x%x x%x\n", @@ -962,12 +996,11 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, } /* PLOGI failed */ /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ - if (lpfc_error_lost_link(irsp)) { + if (lpfc_error_lost_link(irsp)) rc = NLP_STE_FREED_NODE; - } else { + else rc = lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_PLOGI); - } } else { /* Good status, call state machine */ prsp = list_entry(((struct lpfc_dmabuf *) @@ -1015,8 +1048,10 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry) pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ ndlp = lpfc_findnode_did(vport, did); - /* If ndlp if not NULL, we will bump the reference count on it */ + if (ndlp && !NLP_CHK_NODE_ACT(ndlp)) + ndlp = NULL; + /* If ndlp is not NULL, we will bump the reference count on it */ cmdsize = (sizeof(uint32_t) + sizeof(struct serv_parm)); elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, did, ELS_CMD_PLOGI); @@ -1097,18 +1132,15 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, } /* PRLI failed */ /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ - if (lpfc_error_lost_link(irsp)) { + if (lpfc_error_lost_link(irsp)) goto out; - } else { + else lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_PRLI); - } - } else { + } else /* Good status, call state machine */ lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_PRLI); - } - out: lpfc_els_free_iocb(phba, cmdiocb); return; @@ -1275,15 +1307,13 @@ lpfc_cmpl_els_adisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, } /* ADISC failed */ /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ - if (!lpfc_error_lost_link(irsp)) { + if (!lpfc_error_lost_link(irsp)) lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_ADISC); - } - } else { + } else /* Good status, call state machine */ lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_ADISC); - } if (disc && vport->num_disc_nodes) { /* Check to see if there are more ADISCs to be sent */ @@ -1443,14 +1473,12 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, else lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO); - } else { + } else /* Good status, call state machine. * This will unregister the rpi if needed. */ lpfc_disc_state_machine(vport, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO); - } - out: lpfc_els_free_iocb(phba, cmdiocb); return; @@ -1556,11 +1584,19 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry) psli = &phba->sli; pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ cmdsize = (sizeof(uint32_t) + sizeof(SCR)); - ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); - if (!ndlp) - return 1; - lpfc_nlp_init(vport, ndlp, nportid); + ndlp = lpfc_findnode_did(vport, nportid); + if (!ndlp) { + ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); + if (!ndlp) + return 1; + lpfc_nlp_init(vport, ndlp, nportid); + lpfc_enqueue_node(vport, ndlp); + } else if (!NLP_CHK_NODE_ACT(ndlp)) { + ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE); + if (!ndlp) + return 1; + } elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, ndlp->nlp_DID, ELS_CMD_SCR); @@ -1623,11 +1659,19 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry) psli = &phba->sli; pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ cmdsize = (sizeof(uint32_t) + sizeof(FARP)); - ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); - if (!ndlp) - return 1; - lpfc_nlp_init(vport, ndlp, nportid); + ndlp = lpfc_findnode_did(vport, nportid); + if (!ndlp) { + ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); + if (!ndlp) + return 1; + lpfc_nlp_init(vport, ndlp, nportid); + lpfc_enqueue_node(vport, ndlp); + } else if (!NLP_CHK_NODE_ACT(ndlp)) { + ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE); + if (!ndlp) + return 1; + } elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, ndlp->nlp_DID, ELS_CMD_RNID); @@ -1657,7 +1701,7 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry) memcpy(&fp->RportName, &vport->fc_portname, sizeof(struct lpfc_name)); memcpy(&fp->RnodeName, &vport->fc_nodename, sizeof(struct lpfc_name)); ondlp = lpfc_findnode_did(vport, nportid); - if (ondlp) { + if (ondlp && NLP_CHK_NODE_ACT(ondlp)) { memcpy(&fp->OportName, &ondlp->nlp_portname, sizeof(struct lpfc_name)); memcpy(&fp->OnodeName, &ondlp->nlp_nodename, @@ -1690,6 +1734,7 @@ void lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp) { struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + struct lpfc_work_evt *evtp; spin_lock_irq(shost->host_lock); nlp->nlp_flag &= ~NLP_DELAY_TMO; @@ -1697,8 +1742,12 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_vport *vport, struct lpfc_nodelist *nlp) del_timer_sync(&nlp->nlp_delayfunc); nlp->nlp_last_elscmd = 0; - if (!list_empty(&nlp->els_retry_evt.evt_listp)) + if (!list_empty(&nlp->els_retry_evt.evt_listp)) { list_del_init(&nlp->els_retry_evt.evt_listp); + /* Decrement nlp reference count held for the delayed retry */ + evtp = &nlp->els_retry_evt; + lpfc_nlp_put((struct lpfc_nodelist *)evtp->evt_arg1); + } if (nlp->nlp_flag & NLP_NPR_2B_DISC) { spin_lock_irq(shost->host_lock); @@ -1842,13 +1891,14 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, cmd = *elscmd++; } - if (ndlp) + if (ndlp && NLP_CHK_NODE_ACT(ndlp)) did = ndlp->nlp_DID; else { /* We should only hit this case for retrying PLOGI */ did = irsp->un.elsreq64.remoteID; ndlp = lpfc_findnode_did(vport, did); - if (!ndlp && (cmd != ELS_CMD_PLOGI)) + if ((!ndlp || !NLP_CHK_NODE_ACT(ndlp)) + && (cmd != ELS_CMD_PLOGI)) return 1; } @@ -1870,18 +1920,15 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, break; case IOERR_ILLEGAL_COMMAND: - if ((phba->sli3_options & LPFC_SLI3_VPORT_TEARDOWN) && - (cmd == ELS_CMD_FDISC)) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, - "0124 FDISC failed (3/6) " - "retrying...\n"); - lpfc_mbx_unreg_vpi(vport); - retry = 1; - /* FDISC retry policy */ - maxretry = 48; - if (cmdiocb->retry >= 32) - delay = 1000; - } + lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, + "0124 Retry illegal cmd x%x " + "retry:x%x delay:x%x\n", + cmd, cmdiocb->retry, delay); + retry = 1; + /* All command's retry policy */ + maxretry = 8; + if (cmdiocb->retry > 2) + delay = 1000; break; case IOERR_NO_RESOURCES: @@ -1967,6 +2014,17 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, break; case LSRJT_LOGICAL_ERR: + /* There are some cases where switches return this + * error when they are not ready and should be returning + * Logical Busy. We should delay every time. + */ + if (cmd == ELS_CMD_FDISC && + stat.un.b.lsRjtRsnCodeExp == LSEXP_PORT_LOGIN_REQ) { + maxretry = 3; + delay = 1000; + retry = 1; + break; + } case LSRJT_PROTOCOL_ERR: if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) && (cmd == ELS_CMD_FDISC) && @@ -1996,7 +2054,8 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, retry = 1; if ((cmd == ELS_CMD_FLOGI) && - (phba->fc_topology != TOPOLOGY_LOOP)) { + (phba->fc_topology != TOPOLOGY_LOOP) && + !lpfc_error_lost_link(irsp)) { /* FLOGI retry policy */ retry = 1; maxretry = 48; @@ -2322,6 +2381,9 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, if ((rspiocb->iocb.ulpStatus == 0) && (ndlp->nlp_flag & NLP_ACC_REGLOGIN)) { lpfc_unreg_rpi(vport, ndlp); + /* Increment reference count to ndlp to hold the + * reference to ndlp for the callback function. + */ mbox->context2 = lpfc_nlp_get(ndlp); mbox->vport = vport; if (ndlp->nlp_flag & NLP_RM_DFLT_RPI) { @@ -2335,9 +2397,13 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, NLP_STE_REG_LOGIN_ISSUE); } if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) - != MBX_NOT_FINISHED) { + != MBX_NOT_FINISHED) goto out; - } + else + /* Decrement the ndlp reference count we + * set for this failed mailbox command. + */ + lpfc_nlp_put(ndlp); /* ELS rsp: Cannot issue reg_login for <NPortid> */ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, @@ -2796,6 +2862,8 @@ lpfc_els_disc_adisc(struct lpfc_vport *vport) /* go thru NPR nodes and issue any remaining ELS ADISCs */ list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) + continue; if (ndlp->nlp_state == NLP_STE_NPR_NODE && (ndlp->nlp_flag & NLP_NPR_2B_DISC) != 0 && (ndlp->nlp_flag & NLP_NPR_ADISC) != 0) { @@ -2833,6 +2901,8 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport) /* go thru NPR nodes and issue any remaining ELS PLOGIs */ list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) + continue; if (ndlp->nlp_state == NLP_STE_NPR_NODE && (ndlp->nlp_flag & NLP_NPR_2B_DISC) != 0 && (ndlp->nlp_flag & NLP_DELAY_TMO) == 0 && @@ -2869,6 +2939,16 @@ lpfc_els_flush_rscn(struct lpfc_vport *vport) struct lpfc_hba *phba = vport->phba; int i; + spin_lock_irq(shost->host_lock); + if (vport->fc_rscn_flush) { + /* Another thread is walking fc_rscn_id_list on this vport */ + spin_unlock_irq(shost->host_lock); + return; + } + /* Indicate we are walking lpfc_els_flush_rscn on this vport */ + vport->fc_rscn_flush = 1; + spin_unlock_irq(shost->host_lock); + for (i = 0; i < vport->fc_rscn_id_cnt; i++) { lpfc_in_buf_free(phba, vport->fc_rscn_id_list[i]); vport->fc_rscn_id_list[i] = NULL; @@ -2878,6 +2958,8 @@ lpfc_els_flush_rscn(struct lpfc_vport *vport) vport->fc_flag &= ~(FC_RSCN_MODE | FC_RSCN_DISCOVERY); spin_unlock_irq(shost->host_lock); lpfc_can_disctmo(vport); + /* Indicate we are done walking this fc_rscn_id_list */ + vport->fc_rscn_flush = 0; } int @@ -2887,6 +2969,7 @@ lpfc_rscn_payload_check(struct lpfc_vport *vport, uint32_t did) D_ID rscn_did; uint32_t *lp; uint32_t payload_len, i; + struct Scsi_Host *shost = lpfc_shost_from_vport(vport); ns_did.un.word = did; @@ -2898,6 +2981,15 @@ lpfc_rscn_payload_check(struct lpfc_vport *vport, uint32_t did) if (vport->fc_flag & FC_RSCN_DISCOVERY) return did; + spin_lock_irq(shost->host_lock); + if (vport->fc_rscn_flush) { + /* Another thread is walking fc_rscn_id_list on this vport */ + spin_unlock_irq(shost->host_lock); + return 0; + } + /* Indicate we are walking fc_rscn_id_list on this vport */ + vport->fc_rscn_flush = 1; + spin_unlock_irq(shost->host_lock); for (i = 0; i < vport->fc_rscn_id_cnt; i++) { lp = vport->fc_rscn_id_list[i]->virt; payload_len = be32_to_cpu(*lp++ & ~ELS_CMD_MASK); @@ -2908,16 +3000,16 @@ lpfc_rscn_payload_check(struct lpfc_vport *vport, uint32_t did) switch (rscn_did.un.b.resv) { case 0: /* Single N_Port ID effected */ if (ns_did.un.word == rscn_did.un.word) - return did; + goto return_did_out; break; case 1: /* Whole N_Port Area effected */ if ((ns_did.un.b.domain == rscn_did.un.b.domain) && (ns_did.un.b.area == rscn_did.un.b.area)) - return did; + goto return_did_out; break; case 2: /* Whole N_Port Domain effected */ if (ns_did.un.b.domain == rscn_did.un.b.domain) - return did; + goto return_did_out; break; default: /* Unknown Identifier in RSCN node */ @@ -2926,11 +3018,17 @@ lpfc_rscn_payload_check(struct lpfc_vport *vport, uint32_t did) "RSCN payload Data: x%x\n", rscn_did.un.word); case 3: /* Whole Fabric effected */ - return did; + goto return_did_out; } } } + /* Indicate we are done with walking fc_rscn_id_list on this vport */ + vport->fc_rscn_flush = 0; return 0; +return_did_out: + /* Indicate we are done with walking fc_rscn_id_list on this vport */ + vport->fc_rscn_flush = 0; + return did; } static int @@ -2943,7 +3041,8 @@ lpfc_rscn_recovery_check(struct lpfc_vport *vport) */ list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { - if (ndlp->nlp_state == NLP_STE_UNUSED_NODE || + if (!NLP_CHK_NODE_ACT(ndlp) || + ndlp->nlp_state == NLP_STE_UNUSED_NODE || lpfc_rscn_payload_check(vport, ndlp->nlp_DID) == 0) continue; @@ -2971,7 +3070,7 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, uint32_t *lp, *datap; IOCB_t *icmd; uint32_t payload_len, length, nportid, *cmd; - int rscn_cnt = vport->fc_rscn_id_cnt; + int rscn_cnt; int rscn_id = 0, hba_id = 0; int i; @@ -2984,7 +3083,8 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, /* RSCN received */ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "0214 RSCN received Data: x%x x%x x%x x%x\n", - vport->fc_flag, payload_len, *lp, rscn_cnt); + vport->fc_flag, payload_len, *lp, + vport->fc_rscn_id_cnt); for (i = 0; i < payload_len/sizeof(uint32_t); i++) fc_host_post_event(shost, fc_get_event_number(), FCH_EVT_RSCN, lp[i]); @@ -3022,7 +3122,7 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, "0214 Ignore RSCN " "Data: x%x x%x x%x x%x\n", vport->fc_flag, payload_len, - *lp, rscn_cnt); + *lp, vport->fc_rscn_id_cnt); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, "RCV RSCN vport: did:x%x/ste:x%x flg:x%x", ndlp->nlp_DID, vport->port_state, @@ -3034,6 +3134,18 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, } } + spin_lock_irq(shost->host_lock); + if (vport->fc_rscn_flush) { + /* Another thread is walking fc_rscn_id_list on this vport */ + spin_unlock_irq(shost->host_lock); + vport->fc_flag |= FC_RSCN_DISCOVERY; + return 0; + } + /* Indicate we are walking fc_rscn_id_list on this vport */ + vport->fc_rscn_flush = 1; + spin_unlock_irq(shost->host_lock); + /* Get the array count after sucessfully have the token */ + rscn_cnt = vport->fc_rscn_id_cnt; /* If we are already processing an RSCN, save the received * RSCN payload buffer, cmdiocb->context2 to process later. */ @@ -3055,7 +3167,7 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, if ((rscn_cnt) && (payload_len + length <= LPFC_BPL_SIZE)) { *cmd &= ELS_CMD_MASK; - *cmd |= be32_to_cpu(payload_len + length); + *cmd |= cpu_to_be32(payload_len + length); memcpy(((uint8_t *)cmd) + length, lp, payload_len); } else { @@ -3066,7 +3178,6 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, */ cmdiocb->context2 = NULL; } - /* Deferred RSCN */ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "0235 Deferred RSCN " @@ -3083,9 +3194,10 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, vport->fc_rscn_id_cnt, vport->fc_flag, vport->port_state); } + /* Indicate we are done walking fc_rscn_id_list on this vport */ + vport->fc_rscn_flush = 0; /* Send back ACC */ lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL); - /* send RECOVERY event for ALL nodes that match RSCN payload */ lpfc_rscn_recovery_check(vport); spin_lock_irq(shost->host_lock); @@ -3093,7 +3205,6 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, spin_unlock_irq(shost->host_lock); return 0; } - lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, "RCV RSCN: did:x%x/ste:x%x flg:x%x", ndlp->nlp_DID, vport->port_state, ndlp->nlp_flag); @@ -3102,20 +3213,18 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, vport->fc_flag |= FC_RSCN_MODE; spin_unlock_irq(shost->host_lock); vport->fc_rscn_id_list[vport->fc_rscn_id_cnt++] = pcmd; + /* Indicate we are done walking fc_rscn_id_list on this vport */ + vport->fc_rscn_flush = 0; /* * If we zero, cmdiocb->context2, the calling routine will * not try to free it. */ cmdiocb->context2 = NULL; - lpfc_set_disctmo(vport); - /* Send back ACC */ lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL); - /* send RECOVERY event for ALL nodes that match RSCN payload */ lpfc_rscn_recovery_check(vport); - return lpfc_els_handle_rscn(vport); } @@ -3145,7 +3254,8 @@ lpfc_els_handle_rscn(struct lpfc_vport *vport) vport->num_disc_nodes = 0; ndlp = lpfc_findnode_did(vport, NameServer_DID); - if (ndlp && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) { + if (ndlp && NLP_CHK_NODE_ACT(ndlp) + && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) { /* Good ndlp, issue CT Request to NameServer */ if (lpfc_ns_cmd(vport, SLI_CTNS_GID_FT, 0, 0) == 0) /* Wait for NameServer query cmpl before we can @@ -3155,25 +3265,35 @@ lpfc_els_handle_rscn(struct lpfc_vport *vport) /* If login to NameServer does not exist, issue one */ /* Good status, issue PLOGI to NameServer */ ndlp = lpfc_findnode_did(vport, NameServer_DID); - if (ndlp) + if (ndlp && NLP_CHK_NODE_ACT(ndlp)) /* Wait for NameServer login cmpl before we can continue */ return 1; - ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); - if (!ndlp) { - lpfc_els_flush_rscn(vport); - return 0; + if (ndlp) { + ndlp = lpfc_enable_node(vport, ndlp, + NLP_STE_PLOGI_ISSUE); + if (!ndlp) { + lpfc_els_flush_rscn(vport); + return 0; + } + ndlp->nlp_prev_state = NLP_STE_UNUSED_NODE; } else { + ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); + if (!ndlp) { + lpfc_els_flush_rscn(vport); + return 0; + } lpfc_nlp_init(vport, ndlp, NameServer_DID); - ndlp->nlp_type |= NLP_FABRIC; ndlp->nlp_prev_state = ndlp->nlp_state; lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE); - lpfc_issue_els_plogi(vport, NameServer_DID, 0); - /* Wait for NameServer login cmpl before we can - continue */ - return 1; } + ndlp->nlp_type |= NLP_FABRIC; + lpfc_issue_els_plogi(vport, NameServer_DID, 0); + /* Wait for NameServer login cmpl before we can + * continue + */ + return 1; } lpfc_els_flush_rscn(vport); @@ -3672,6 +3792,8 @@ lpfc_els_rcv_fan(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) + continue; if (ndlp->nlp_state != NLP_STE_NPR_NODE) continue; if (ndlp->nlp_type & NLP_FABRIC) { @@ -3697,6 +3819,8 @@ lpfc_els_rcv_fan(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, */ list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) + continue; if (ndlp->nlp_state != NLP_STE_NPR_NODE) continue; @@ -3936,7 +4060,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, uint32_t cmd, did, newnode, rjt_err = 0; IOCB_t *icmd = &elsiocb->iocb; - if (vport == NULL || elsiocb->context2 == NULL) + if (!vport || !(elsiocb->context2)) goto dropit; newnode = 0; @@ -3971,14 +4095,20 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, lpfc_nlp_init(vport, ndlp, did); lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); newnode = 1; - if ((did & Fabric_DID_MASK) == Fabric_DID_MASK) { + if ((did & Fabric_DID_MASK) == Fabric_DID_MASK) ndlp->nlp_type |= NLP_FABRIC; + } else { + if (!NLP_CHK_NODE_ACT(ndlp)) { + ndlp = lpfc_enable_node(vport, ndlp, + NLP_STE_UNUSED_NODE); + if (!ndlp) + goto dropit; } - } - else { if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) { /* This is simular to the new node path */ - lpfc_nlp_get(ndlp); + ndlp = lpfc_nlp_get(ndlp); + if (!ndlp) + goto dropit; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); newnode = 1; } @@ -3987,6 +4117,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, phba->fc_stat.elsRcvFrame++; if (elsiocb->context1) lpfc_nlp_put(elsiocb->context1); + elsiocb->context1 = lpfc_nlp_get(ndlp); elsiocb->vport = vport; @@ -4007,8 +4138,15 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, ndlp = lpfc_plogi_confirm_nport(phba, payload, ndlp); if (vport->port_state < LPFC_DISC_AUTH) { - rjt_err = LSRJT_UNABLE_TPC; - break; + if (!(phba->pport->fc_flag & FC_PT2PT) || + (phba->pport->fc_flag & FC_PT2PT_PLOGI)) { + rjt_err = LSRJT_UNABLE_TPC; + break; + } + /* We get here, and drop thru, if we are PT2PT with + * another NPort and the other side has initiated + * the PLOGI before responding to our FLOGI. + */ } shost = lpfc_shost_from_vport(vport); @@ -4251,15 +4389,15 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, vport = lpfc_find_vport_by_vpid(phba, vpi); } } - /* If there are no BDEs associated - * with this IOCB, there is nothing to do. - */ + /* If there are no BDEs associated + * with this IOCB, there is nothing to do. + */ if (icmd->ulpBdeCount == 0) return; - /* type of ELS cmd is first 32bit word - * in packet - */ + /* type of ELS cmd is first 32bit word + * in packet + */ if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { elsiocb->context2 = bdeBuf1; } else { @@ -4314,6 +4452,18 @@ lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport) } lpfc_nlp_init(vport, ndlp, NameServer_DID); ndlp->nlp_type |= NLP_FABRIC; + } else if (!NLP_CHK_NODE_ACT(ndlp)) { + ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE); + if (!ndlp) { + if (phba->fc_topology == TOPOLOGY_LOOP) { + lpfc_disc_start(vport); + return; + } + lpfc_vport_set_state(vport, FC_VPORT_FAILED); + lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, + "0348 NameServer login: node freed\n"); + return; + } } lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE); @@ -4360,6 +4510,7 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) switch (mb->mbxStatus) { case 0x11: /* unsupported feature */ case 0x9603: /* max_vpi exceeded */ + case 0x9602: /* Link event since CLEAR_LA */ /* giving up on vport registration */ lpfc_vport_set_state(vport, FC_VPORT_FAILED); spin_lock_irq(shost->host_lock); @@ -4373,7 +4524,10 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) spin_lock_irq(shost->host_lock); vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; spin_unlock_irq(shost->host_lock); - lpfc_initial_fdisc(vport); + if (vport->port_type == LPFC_PHYSICAL_PORT) + lpfc_initial_flogi(vport); + else + lpfc_initial_fdisc(vport); break; } @@ -4471,7 +4625,6 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, irsp->ulpStatus, irsp->un.ulpWord[4]); if (vport->fc_vport->vport_state == FC_VPORT_INITIALIZING) lpfc_vport_set_state(vport, FC_VPORT_FAILED); - lpfc_nlp_put(ndlp); /* giving up on FDISC. Cancel discovery timer */ lpfc_can_disctmo(vport); @@ -4492,8 +4645,9 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, */ list_for_each_entry_safe(np, next_np, &vport->fc_nodes, nlp_listp) { - if (np->nlp_state != NLP_STE_NPR_NODE - || !(np->nlp_flag & NLP_NPR_ADISC)) + if (!NLP_CHK_NODE_ACT(ndlp) || + (np->nlp_state != NLP_STE_NPR_NODE) || + !(np->nlp_flag & NLP_NPR_ADISC)) continue; spin_lock_irq(shost->host_lock); np->nlp_flag &= ~NLP_NPR_ADISC; @@ -4599,6 +4753,8 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, { struct lpfc_vport *vport = cmdiocb->vport; IOCB_t *irsp; + struct lpfc_nodelist *ndlp; + ndlp = (struct lpfc_nodelist *)cmdiocb->context1; irsp = &rspiocb->iocb; lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD, @@ -4607,6 +4763,9 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, lpfc_els_free_iocb(phba, cmdiocb); vport->unreg_vpi_cmpl = VPORT_ERROR; + + /* Trigger the release of the ndlp after logo */ + lpfc_nlp_put(ndlp); } int @@ -4686,11 +4845,12 @@ lpfc_resume_fabric_iocbs(struct lpfc_hba *phba) repeat: iocb = NULL; spin_lock_irqsave(&phba->hbalock, iflags); - /* Post any pending iocb to the SLI layer */ + /* Post any pending iocb to the SLI layer */ if (atomic_read(&phba->fabric_iocb_count) == 0) { list_remove_head(&phba->fabric_iocb_list, iocb, typeof(*iocb), list); if (iocb) + /* Increment fabric iocb count to hold the position */ atomic_inc(&phba->fabric_iocb_count); } spin_unlock_irqrestore(&phba->hbalock, iflags); @@ -4737,9 +4897,7 @@ lpfc_block_fabric_iocbs(struct lpfc_hba *phba) int blocked; blocked = test_and_set_bit(FABRIC_COMANDS_BLOCKED, &phba->bit_flags); - /* Start a timer to unblock fabric - * iocbs after 100ms - */ + /* Start a timer to unblock fabric iocbs after 100ms */ if (!blocked) mod_timer(&phba->fabric_block_timer, jiffies + HZ/10 ); @@ -4787,8 +4945,8 @@ lpfc_cmpl_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, atomic_dec(&phba->fabric_iocb_count); if (!test_bit(FABRIC_COMANDS_BLOCKED, &phba->bit_flags)) { - /* Post any pending iocbs to HBA */ - lpfc_resume_fabric_iocbs(phba); + /* Post any pending iocbs to HBA */ + lpfc_resume_fabric_iocbs(phba); } } @@ -4807,6 +4965,9 @@ lpfc_issue_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *iocb) ready = atomic_read(&phba->fabric_iocb_count) == 0 && !test_bit(FABRIC_COMANDS_BLOCKED, &phba->bit_flags); + if (ready) + /* Increment fabric iocb count to hold the position */ + atomic_inc(&phba->fabric_iocb_count); spin_unlock_irqrestore(&phba->hbalock, iflags); if (ready) { iocb->fabric_iocb_cmpl = iocb->iocb_cmpl; @@ -4817,7 +4978,6 @@ lpfc_issue_fabric_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *iocb) "Fabric sched2: ste:x%x", iocb->vport->port_state, 0, 0); - atomic_inc(&phba->fabric_iocb_count); ret = lpfc_sli_issue_iocb(phba, pring, iocb, 0); if (ret == IOCB_ERROR) { diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index dc042bd97ba..976653440fb 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2007 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -272,9 +272,8 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) if (!(vport->load_flag & FC_UNLOADING) && !(ndlp->nlp_flag & NLP_DELAY_TMO) && !(ndlp->nlp_flag & NLP_NPR_2B_DISC) && - (ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)) { + (ndlp->nlp_state != NLP_STE_UNMAPPED_NODE)) lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM); - } } @@ -566,9 +565,10 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove) int rc; list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) + continue; if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) continue; - if ((phba->sli3_options & LPFC_SLI3_VPORT_TEARDOWN) || ((vport->port_type == LPFC_NPIV_PORT) && (ndlp->nlp_DID == NameServer_DID))) @@ -629,9 +629,8 @@ lpfc_linkdown(struct lpfc_hba *phba) LPFC_MBOXQ_t *mb; int i; - if (phba->link_state == LPFC_LINK_DOWN) { + if (phba->link_state == LPFC_LINK_DOWN) return 0; - } spin_lock_irq(&phba->hbalock); if (phba->link_state > LPFC_LINK_DOWN) { phba->link_state = LPFC_LINK_DOWN; @@ -684,20 +683,21 @@ lpfc_linkup_cleanup_nodes(struct lpfc_vport *vport) struct lpfc_nodelist *ndlp; list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) + continue; if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) continue; - if (ndlp->nlp_type & NLP_FABRIC) { - /* On Linkup its safe to clean up the ndlp - * from Fabric connections. - */ + /* On Linkup its safe to clean up the ndlp + * from Fabric connections. + */ if (ndlp->nlp_DID != Fabric_DID) lpfc_unreg_rpi(vport, ndlp); lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); } else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) { - /* Fail outstanding IO now since device is - * marked for PLOGI. - */ + /* Fail outstanding IO now since device is + * marked for PLOGI. + */ lpfc_unreg_rpi(vport, ndlp); } } @@ -799,21 +799,9 @@ lpfc_mbx_cmpl_clear_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) writel(control, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ spin_unlock_irq(&phba->hbalock); + mempool_free(pmb, phba->mbox_mem_pool); return; - vport->num_disc_nodes = 0; - /* go thru NPR nodes and issue ELS PLOGIs */ - if (vport->fc_npr_cnt) - lpfc_els_disc_plogi(vport); - - if (!vport->num_disc_nodes) { - spin_lock_irq(shost->host_lock); - vport->fc_flag &= ~FC_NDISC_ACTIVE; - spin_unlock_irq(shost->host_lock); - } - - vport->port_state = LPFC_VPORT_READY; - out: /* Device Discovery completes */ lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, @@ -1133,7 +1121,7 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) if (la->attType == AT_LINK_UP) { phba->fc_stat.LinkUp++; if (phba->link_flag & LS_LOOPBACK_MODE) { - lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT, + lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, "1306 Link Up Event in loop back mode " "x%x received Data: x%x x%x x%x x%x\n", la->eventTag, phba->fc_eventTag, @@ -1150,11 +1138,21 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) lpfc_mbx_process_link_up(phba, la); } else { phba->fc_stat.LinkDown++; - lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, + if (phba->link_flag & LS_LOOPBACK_MODE) { + lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, + "1308 Link Down Event in loop back mode " + "x%x received " + "Data: x%x x%x x%x\n", + la->eventTag, phba->fc_eventTag, + phba->pport->port_state, vport->fc_flag); + } + else { + lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, "1305 Link Down Event x%x received " "Data: x%x x%x x%x\n", la->eventTag, phba->fc_eventTag, phba->pport->port_state, vport->fc_flag); + } lpfc_mbx_issue_link_down(phba); } @@ -1305,7 +1303,6 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) lpfc_mbuf_free(phba, mp->virt, mp->phys); kfree(mp); mempool_free(pmb, phba->mbox_mem_pool); - lpfc_nlp_put(ndlp); if (phba->fc_topology == TOPOLOGY_LOOP) { /* FLOGI failed, use loop map to make discovery list */ @@ -1313,6 +1310,10 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) /* Start discovery */ lpfc_disc_start(vport); + /* Decrement the reference count to ndlp after the + * reference to the ndlp are done. + */ + lpfc_nlp_put(ndlp); return; } @@ -1320,6 +1321,10 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX, "0258 Register Fabric login error: 0x%x\n", mb->mbxStatus); + /* Decrement the reference count to ndlp after the reference + * to the ndlp are done. + */ + lpfc_nlp_put(ndlp); return; } @@ -1327,8 +1332,6 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) ndlp->nlp_type |= NLP_FABRIC; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); - lpfc_nlp_put(ndlp); /* Drop the reference from the mbox */ - if (vport->port_state == LPFC_FABRIC_CFG_LINK) { vports = lpfc_create_vport_work_array(phba); if (vports != NULL) @@ -1356,6 +1359,11 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) lpfc_mbuf_free(phba, mp->virt, mp->phys); kfree(mp); mempool_free(pmb, phba->mbox_mem_pool); + + /* Drop the reference count from the mbox at the end after + * all the current reference to the ndlp have been done. + */ + lpfc_nlp_put(ndlp); return; } @@ -1463,9 +1471,8 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * registered the port. */ if (ndlp->rport && ndlp->rport->dd_data && - ((struct lpfc_rport_data *) ndlp->rport->dd_data)->pnode == ndlp) { + ((struct lpfc_rport_data *) ndlp->rport->dd_data)->pnode == ndlp) lpfc_nlp_put(ndlp); - } lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT, "rport add: did:x%x flg:x%x type x%x", @@ -1660,6 +1667,18 @@ lpfc_nlp_set_state(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, } void +lpfc_enqueue_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) +{ + struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + + if (list_empty(&ndlp->nlp_listp)) { + spin_lock_irq(shost->host_lock); + list_add_tail(&ndlp->nlp_listp, &vport->fc_nodes); + spin_unlock_irq(shost->host_lock); + } +} + +void lpfc_dequeue_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) { struct Scsi_Host *shost = lpfc_shost_from_vport(vport); @@ -1672,7 +1691,80 @@ lpfc_dequeue_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) list_del_init(&ndlp->nlp_listp); spin_unlock_irq(shost->host_lock); lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state, - NLP_STE_UNUSED_NODE); + NLP_STE_UNUSED_NODE); +} + +static void +lpfc_disable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) +{ + if ((ndlp->nlp_flag & NLP_DELAY_TMO) != 0) + lpfc_cancel_retry_delay_tmo(vport, ndlp); + if (ndlp->nlp_state && !list_empty(&ndlp->nlp_listp)) + lpfc_nlp_counters(vport, ndlp->nlp_state, -1); + lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state, + NLP_STE_UNUSED_NODE); +} + +struct lpfc_nodelist * +lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, + int state) +{ + struct lpfc_hba *phba = vport->phba; + uint32_t did; + unsigned long flags; + + if (!ndlp) + return NULL; + + spin_lock_irqsave(&phba->ndlp_lock, flags); + /* The ndlp should not be in memory free mode */ + if (NLP_CHK_FREE_REQ(ndlp)) { + spin_unlock_irqrestore(&phba->ndlp_lock, flags); + lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE, + "0277 lpfc_enable_node: ndlp:x%p " + "usgmap:x%x refcnt:%d\n", + (void *)ndlp, ndlp->nlp_usg_map, + atomic_read(&ndlp->kref.refcount)); + return NULL; + } + /* The ndlp should not already be in active mode */ + if (NLP_CHK_NODE_ACT(ndlp)) { + spin_unlock_irqrestore(&phba->ndlp_lock, flags); + lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE, + "0278 lpfc_enable_node: ndlp:x%p " + "usgmap:x%x refcnt:%d\n", + (void *)ndlp, ndlp->nlp_usg_map, + atomic_read(&ndlp->kref.refcount)); + return NULL; + } + + /* Keep the original DID */ + did = ndlp->nlp_DID; + + /* re-initialize ndlp except of ndlp linked list pointer */ + memset((((char *)ndlp) + sizeof (struct list_head)), 0, + sizeof (struct lpfc_nodelist) - sizeof (struct list_head)); + INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp); + INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp); + init_timer(&ndlp->nlp_delayfunc); + ndlp->nlp_delayfunc.function = lpfc_els_retry_delay; + ndlp->nlp_delayfunc.data = (unsigned long)ndlp; + ndlp->nlp_DID = did; + ndlp->vport = vport; + ndlp->nlp_sid = NLP_NO_SID; + /* ndlp management re-initialize */ + kref_init(&ndlp->kref); + NLP_INT_NODE_ACT(ndlp); + + spin_unlock_irqrestore(&phba->ndlp_lock, flags); + + if (state != NLP_STE_UNUSED_NODE) + lpfc_nlp_set_state(vport, ndlp, state); + + lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE, + "node enable: did:x%x", + ndlp->nlp_DID, 0, 0); + return ndlp; } void @@ -1972,7 +2064,21 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) "Data: x%x x%x x%x\n", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); - lpfc_dequeue_node(vport, ndlp); + if (NLP_CHK_FREE_REQ(ndlp)) { + lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE, + "0280 lpfc_cleanup_node: ndlp:x%p " + "usgmap:x%x refcnt:%d\n", + (void *)ndlp, ndlp->nlp_usg_map, + atomic_read(&ndlp->kref.refcount)); + lpfc_dequeue_node(vport, ndlp); + } else { + lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE, + "0281 lpfc_cleanup_node: ndlp:x%p " + "usgmap:x%x refcnt:%d\n", + (void *)ndlp, ndlp->nlp_usg_map, + atomic_read(&ndlp->kref.refcount)); + lpfc_disable_node(vport, ndlp); + } /* cleanup any ndlp on mbox q waiting for reglogin cmpl */ if ((mb = phba->sli.mbox_active)) { @@ -1994,12 +2100,16 @@ lpfc_cleanup_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) } list_del(&mb->list); mempool_free(mb, phba->mbox_mem_pool); - lpfc_nlp_put(ndlp); + /* We shall not invoke the lpfc_nlp_put to decrement + * the ndlp reference count as we are in the process + * of lpfc_nlp_release. + */ } } spin_unlock_irq(&phba->hbalock); - lpfc_els_abort(phba,ndlp); + lpfc_els_abort(phba, ndlp); + spin_lock_irq(shost->host_lock); ndlp->nlp_flag &= ~NLP_DELAY_TMO; spin_unlock_irq(shost->host_lock); @@ -2057,7 +2167,6 @@ lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) } } } - lpfc_cleanup_node(vport, ndlp); /* @@ -2182,7 +2291,16 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) ndlp->nlp_flag |= NLP_NPR_2B_DISC; spin_unlock_irq(shost->host_lock); return ndlp; + } else if (!NLP_CHK_NODE_ACT(ndlp)) { + ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_NPR_NODE); + if (!ndlp) + return NULL; + spin_lock_irq(shost->host_lock); + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + spin_unlock_irq(shost->host_lock); + return ndlp; } + if (vport->fc_flag & FC_RSCN_MODE) { if (lpfc_rscn_payload_check(vport, did)) { /* If we've already recieved a PLOGI from this NPort @@ -2363,6 +2481,7 @@ lpfc_disc_start(struct lpfc_vport *vport) * continue discovery. */ if ((phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) && + !(vport->fc_flag & FC_PT2PT) && !(vport->fc_flag & FC_RSCN_MODE)) { lpfc_issue_reg_vpi(phba, vport); return; @@ -2485,6 +2604,8 @@ lpfc_disc_flush_list(struct lpfc_vport *vport) if (vport->fc_plogi_cnt || vport->fc_adisc_cnt) { list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) + continue; if (ndlp->nlp_state == NLP_STE_PLOGI_ISSUE || ndlp->nlp_state == NLP_STE_ADISC_ISSUE) { lpfc_free_tx(phba, ndlp); @@ -2572,6 +2693,8 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport) /* Start discovery by sending FLOGI, clean up old rpis */ list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) + continue; if (ndlp->nlp_state != NLP_STE_NPR_NODE) continue; if (ndlp->nlp_type & NLP_FABRIC) { @@ -2618,7 +2741,7 @@ lpfc_disc_timeout_handler(struct lpfc_vport *vport) "NameServer login\n"); /* Next look for NameServer ndlp */ ndlp = lpfc_findnode_did(vport, NameServer_DID); - if (ndlp) + if (ndlp && NLP_CHK_NODE_ACT(ndlp)) lpfc_els_abort(phba, ndlp); /* ReStart discovery */ @@ -2897,6 +3020,7 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_sid = NLP_NO_SID; INIT_LIST_HEAD(&ndlp->nlp_listp); kref_init(&ndlp->kref); + NLP_INT_NODE_ACT(ndlp); lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE, "node init: did:x%x", @@ -2911,6 +3035,8 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, static void lpfc_nlp_release(struct kref *kref) { + struct lpfc_hba *phba; + unsigned long flags; struct lpfc_nodelist *ndlp = container_of(kref, struct lpfc_nodelist, kref); @@ -2918,8 +3044,24 @@ lpfc_nlp_release(struct kref *kref) "node release: did:x%x flg:x%x type:x%x", ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type); + lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE, + "0279 lpfc_nlp_release: ndlp:x%p " + "usgmap:x%x refcnt:%d\n", + (void *)ndlp, ndlp->nlp_usg_map, + atomic_read(&ndlp->kref.refcount)); + + /* remove ndlp from action. */ lpfc_nlp_remove(ndlp->vport, ndlp); - mempool_free(ndlp, ndlp->vport->phba->nlp_mem_pool); + + /* clear the ndlp active flag for all release cases */ + phba = ndlp->vport->phba; + spin_lock_irqsave(&phba->ndlp_lock, flags); + NLP_CLR_NODE_ACT(ndlp); + spin_unlock_irqrestore(&phba->ndlp_lock, flags); + + /* free ndlp memory for final ndlp release */ + if (NLP_CHK_FREE_REQ(ndlp)) + mempool_free(ndlp, ndlp->vport->phba->nlp_mem_pool); } /* This routine bumps the reference count for a ndlp structure to ensure @@ -2929,37 +3071,108 @@ lpfc_nlp_release(struct kref *kref) struct lpfc_nodelist * lpfc_nlp_get(struct lpfc_nodelist *ndlp) { + struct lpfc_hba *phba; + unsigned long flags; + if (ndlp) { lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE, "node get: did:x%x flg:x%x refcnt:x%x", ndlp->nlp_DID, ndlp->nlp_flag, atomic_read(&ndlp->kref.refcount)); - kref_get(&ndlp->kref); + /* The check of ndlp usage to prevent incrementing the + * ndlp reference count that is in the process of being + * released. + */ + phba = ndlp->vport->phba; + spin_lock_irqsave(&phba->ndlp_lock, flags); + if (!NLP_CHK_NODE_ACT(ndlp) || NLP_CHK_FREE_ACK(ndlp)) { + spin_unlock_irqrestore(&phba->ndlp_lock, flags); + lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_NODE, + "0276 lpfc_nlp_get: ndlp:x%p " + "usgmap:x%x refcnt:%d\n", + (void *)ndlp, ndlp->nlp_usg_map, + atomic_read(&ndlp->kref.refcount)); + return NULL; + } else + kref_get(&ndlp->kref); + spin_unlock_irqrestore(&phba->ndlp_lock, flags); } return ndlp; } - /* This routine decrements the reference count for a ndlp structure. If the - * count goes to 0, this indicates the the associated nodelist should be freed. + * count goes to 0, this indicates the the associated nodelist should be + * freed. Returning 1 indicates the ndlp resource has been released; on the + * other hand, returning 0 indicates the ndlp resource has not been released + * yet. */ int lpfc_nlp_put(struct lpfc_nodelist *ndlp) { - if (ndlp) { - lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE, - "node put: did:x%x flg:x%x refcnt:x%x", - ndlp->nlp_DID, ndlp->nlp_flag, - atomic_read(&ndlp->kref.refcount)); + struct lpfc_hba *phba; + unsigned long flags; + + if (!ndlp) + return 1; + + lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE, + "node put: did:x%x flg:x%x refcnt:x%x", + ndlp->nlp_DID, ndlp->nlp_flag, + atomic_read(&ndlp->kref.refcount)); + phba = ndlp->vport->phba; + spin_lock_irqsave(&phba->ndlp_lock, flags); + /* Check the ndlp memory free acknowledge flag to avoid the + * possible race condition that kref_put got invoked again + * after previous one has done ndlp memory free. + */ + if (NLP_CHK_FREE_ACK(ndlp)) { + spin_unlock_irqrestore(&phba->ndlp_lock, flags); + lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_NODE, + "0274 lpfc_nlp_put: ndlp:x%p " + "usgmap:x%x refcnt:%d\n", + (void *)ndlp, ndlp->nlp_usg_map, + atomic_read(&ndlp->kref.refcount)); + return 1; } - return ndlp ? kref_put(&ndlp->kref, lpfc_nlp_release) : 0; + /* Check the ndlp inactivate log flag to avoid the possible + * race condition that kref_put got invoked again after ndlp + * is already in inactivating state. + */ + if (NLP_CHK_IACT_REQ(ndlp)) { + spin_unlock_irqrestore(&phba->ndlp_lock, flags); + lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_NODE, + "0275 lpfc_nlp_put: ndlp:x%p " + "usgmap:x%x refcnt:%d\n", + (void *)ndlp, ndlp->nlp_usg_map, + atomic_read(&ndlp->kref.refcount)); + return 1; + } + /* For last put, mark the ndlp usage flags to make sure no + * other kref_get and kref_put on the same ndlp shall get + * in between the process when the final kref_put has been + * invoked on this ndlp. + */ + if (atomic_read(&ndlp->kref.refcount) == 1) { + /* Indicate ndlp is put to inactive state. */ + NLP_SET_IACT_REQ(ndlp); + /* Acknowledge ndlp memory free has been seen. */ + if (NLP_CHK_FREE_REQ(ndlp)) + NLP_SET_FREE_ACK(ndlp); + } + spin_unlock_irqrestore(&phba->ndlp_lock, flags); + /* Note, the kref_put returns 1 when decrementing a reference + * count that was 1, it invokes the release callback function, + * but it still left the reference count as 1 (not actually + * performs the last decrementation). Otherwise, it actually + * decrements the reference count and returns 0. + */ + return kref_put(&ndlp->kref, lpfc_nlp_release); } /* This routine free's the specified nodelist if it is not in use - * by any other discovery thread. This routine returns 1 if the ndlp - * is not being used by anyone and has been freed. A return value of - * 0 indicates it is being used by another discovery thread and the - * refcount is left unchanged. + * by any other discovery thread. This routine returns 1 if the + * ndlp has been freed. A return value of 0 indicates the ndlp is + * not yet been released. */ int lpfc_nlp_not_used(struct lpfc_nodelist *ndlp) @@ -2968,11 +3181,8 @@ lpfc_nlp_not_used(struct lpfc_nodelist *ndlp) "node not used: did:x%x flg:x%x refcnt:x%x", ndlp->nlp_DID, ndlp->nlp_flag, atomic_read(&ndlp->kref.refcount)); - - if (atomic_read(&ndlp->kref.refcount) == 1) { - lpfc_nlp_put(ndlp); - return 1; - } + if (atomic_read(&ndlp->kref.refcount) == 1) + if (lpfc_nlp_put(ndlp)) + return 1; return 0; } - diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 041f83e7634..7773b949aa7 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2007 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -581,6 +581,7 @@ struct ls_rjt { /* Structure is in Big Endian format */ #define LSEXP_INVALID_O_SID 0x15 #define LSEXP_INVALID_OX_RX 0x17 #define LSEXP_CMD_IN_PROGRESS 0x19 +#define LSEXP_PORT_LOGIN_REQ 0x1E #define LSEXP_INVALID_NPORT_ID 0x1F #define LSEXP_INVALID_SEQ_ID 0x21 #define LSEXP_INVALID_XCHG 0x23 @@ -1376,11 +1377,26 @@ typedef struct { /* FireFly BIU registers */ #define CMD_QUE_XRI64_CX 0xB3 #define CMD_IOCB_RCV_SEQ64_CX 0xB5 #define CMD_IOCB_RCV_ELS64_CX 0xB7 +#define CMD_IOCB_RET_XRI64_CX 0xB9 #define CMD_IOCB_RCV_CONT64_CX 0xBB #define CMD_GEN_REQUEST64_CR 0xC2 #define CMD_GEN_REQUEST64_CX 0xC3 +/* Unhandled SLI-3 Commands */ +#define CMD_IOCB_XMIT_MSEQ64_CR 0xB0 +#define CMD_IOCB_XMIT_MSEQ64_CX 0xB1 +#define CMD_IOCB_RCV_SEQ_LIST64_CX 0xC1 +#define CMD_IOCB_RCV_ELS_LIST64_CX 0xCD +#define CMD_IOCB_CLOSE_EXTENDED_CN 0xB6 +#define CMD_IOCB_ABORT_EXTENDED_CN 0xBA +#define CMD_IOCB_RET_HBQE64_CN 0xCA +#define CMD_IOCB_FCP_IBIDIR64_CR 0xAC +#define CMD_IOCB_FCP_IBIDIR64_CX 0xAD +#define CMD_IOCB_FCP_ITASKMGT64_CX 0xAF +#define CMD_IOCB_LOGENTRY_CN 0x94 +#define CMD_IOCB_LOGENTRY_ASYNC_CN 0x96 + #define CMD_MAX_IOCB_CMD 0xE6 #define CMD_IOCB_MASK 0xff diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 6cfeba7454d..22843751c2c 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2007 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -461,11 +461,21 @@ lpfc_config_port_post(struct lpfc_hba *phba) int lpfc_hba_down_prep(struct lpfc_hba *phba) { + struct lpfc_vport **vports; + int i; /* Disable interrupts */ writel(0, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ - lpfc_cleanup_discovery_resources(phba->pport); + if (phba->pport->load_flag & FC_UNLOADING) + lpfc_cleanup_discovery_resources(phba->pport); + else { + vports = lpfc_create_vport_work_array(phba); + if (vports != NULL) + for(i = 0; i <= phba->max_vpi && vports[i] != NULL; i++) + lpfc_cleanup_discovery_resources(vports[i]); + lpfc_destroy_vport_work_array(phba, vports); + } return 0; } @@ -1422,9 +1432,32 @@ lpfc_cleanup(struct lpfc_vport *vport) lpfc_port_link_failure(vport); list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) { + ndlp = lpfc_enable_node(vport, ndlp, + NLP_STE_UNUSED_NODE); + if (!ndlp) + continue; + spin_lock_irq(&phba->ndlp_lock); + NLP_SET_FREE_REQ(ndlp); + spin_unlock_irq(&phba->ndlp_lock); + /* Trigger the release of the ndlp memory */ + lpfc_nlp_put(ndlp); + continue; + } + spin_lock_irq(&phba->ndlp_lock); + if (NLP_CHK_FREE_REQ(ndlp)) { + /* The ndlp should not be in memory free mode already */ + spin_unlock_irq(&phba->ndlp_lock); + continue; + } else + /* Indicate request for freeing ndlp memory */ + NLP_SET_FREE_REQ(ndlp); + spin_unlock_irq(&phba->ndlp_lock); + if (ndlp->nlp_type & NLP_FABRIC) lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RECOVERY); + lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM); } @@ -1438,6 +1471,17 @@ lpfc_cleanup(struct lpfc_vport *vport) if (i++ > 3000) { lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY, "0233 Nodelist not empty\n"); + list_for_each_entry_safe(ndlp, next_ndlp, + &vport->fc_nodes, nlp_listp) { + lpfc_printf_vlog(ndlp->vport, KERN_ERR, + LOG_NODE, + "0282: did:x%x ndlp:x%p " + "usgmap:x%x refcnt:%d\n", + ndlp->nlp_DID, (void *)ndlp, + ndlp->nlp_usg_map, + atomic_read( + &ndlp->kref.refcount)); + } break; } @@ -1586,6 +1630,8 @@ lpfc_offline_prep(struct lpfc_hba * phba) list_for_each_entry_safe(ndlp, next_ndlp, &vports[i]->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) + continue; if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) continue; if (ndlp->nlp_type & NLP_FABRIC) { @@ -1695,9 +1741,9 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) vport = (struct lpfc_vport *) shost->hostdata; vport->phba = phba; - vport->load_flag |= FC_LOADING; vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; + vport->fc_rscn_flush = 0; lpfc_get_vport_cfgparam(vport); shost->unique_id = instance; @@ -1879,6 +1925,42 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost) spin_unlock_irq(shost->host_lock); } +static int +lpfc_enable_msix(struct lpfc_hba *phba) +{ + int error; + + phba->msix_entries[0].entry = 0; + phba->msix_entries[0].vector = 0; + + error = pci_enable_msix(phba->pcidev, phba->msix_entries, + ARRAY_SIZE(phba->msix_entries)); + if (error) { + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "0420 Enable MSI-X failed (%d), continuing " + "with MSI\n", error); + pci_disable_msix(phba->pcidev); + return error; + } + + error = request_irq(phba->msix_entries[0].vector, lpfc_intr_handler, 0, + LPFC_DRIVER_NAME, phba); + if (error) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "0421 MSI-X request_irq failed (%d), " + "continuing with MSI\n", error); + pci_disable_msix(phba->pcidev); + } + return error; +} + +static void +lpfc_disable_msix(struct lpfc_hba *phba) +{ + free_irq(phba->msix_entries[0].vector, phba); + pci_disable_msix(phba->pcidev); +} + static int __devinit lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) { @@ -1905,6 +1987,9 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) spin_lock_init(&phba->hbalock); + /* Initialize ndlp management spinlock */ + spin_lock_init(&phba->ndlp_lock); + phba->pcidev = pdev; /* Assign an unused board number */ @@ -2002,6 +2087,8 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) memset(phba->hbqslimp.virt, 0, lpfc_sli_hbq_size()); + INIT_LIST_HEAD(&phba->hbqbuf_in_list); + /* Initialize the SLI Layer to run with lpfc HBAs. */ lpfc_sli_setup(phba); lpfc_sli_queue_setup(phba); @@ -2077,24 +2164,36 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) lpfc_debugfs_initialize(vport); pci_set_drvdata(pdev, shost); + phba->intr_type = NONE; - if (phba->cfg_use_msi) { + if (phba->cfg_use_msi == 2) { + error = lpfc_enable_msix(phba); + if (!error) + phba->intr_type = MSIX; + } + + /* Fallback to MSI if MSI-X initialization failed */ + if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) { retval = pci_enable_msi(phba->pcidev); if (!retval) - phba->using_msi = 1; + phba->intr_type = MSI; else lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "0452 Enable MSI failed, continuing " "with IRQ\n"); } - retval = request_irq(phba->pcidev->irq, lpfc_intr_handler, IRQF_SHARED, - LPFC_DRIVER_NAME, phba); - if (retval) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0451 Enable interrupt handler failed\n"); - error = retval; - goto out_disable_msi; + /* MSI-X is the only case the doesn't need to call request_irq */ + if (phba->intr_type != MSIX) { + retval = request_irq(phba->pcidev->irq, lpfc_intr_handler, + IRQF_SHARED, LPFC_DRIVER_NAME, phba); + if (retval) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0451 Enable " + "interrupt handler failed\n"); + error = retval; + goto out_disable_msi; + } else if (phba->intr_type != MSI) + phba->intr_type = INTx; } phba->MBslimaddr = phba->slim_memmap_p; @@ -2139,9 +2238,14 @@ out_remove_device: out_free_irq: lpfc_stop_phba_timers(phba); phba->pport->work_port_events = 0; - free_irq(phba->pcidev->irq, phba); + + if (phba->intr_type == MSIX) + lpfc_disable_msix(phba); + else + free_irq(phba->pcidev->irq, phba); + out_disable_msi: - if (phba->using_msi) + if (phba->intr_type == MSI) pci_disable_msi(phba->pcidev); destroy_port(vport); out_kthread_stop: @@ -2214,10 +2318,13 @@ lpfc_pci_remove_one(struct pci_dev *pdev) lpfc_debugfs_terminate(vport); - /* Release the irq reservation */ - free_irq(phba->pcidev->irq, phba); - if (phba->using_msi) - pci_disable_msi(phba->pcidev); + if (phba->intr_type == MSIX) + lpfc_disable_msix(phba); + else { + free_irq(phba->pcidev->irq, phba); + if (phba->intr_type == MSI) + pci_disable_msi(phba->pcidev); + } pci_set_drvdata(pdev, NULL); scsi_host_put(shost); @@ -2276,10 +2383,13 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev, pring = &psli->ring[psli->fcp_ring]; lpfc_sli_abort_iocb_ring(phba, pring); - /* Release the irq reservation */ - free_irq(phba->pcidev->irq, phba); - if (phba->using_msi) - pci_disable_msi(phba->pcidev); + if (phba->intr_type == MSIX) + lpfc_disable_msix(phba); + else { + free_irq(phba->pcidev->irq, phba); + if (phba->intr_type == MSI) + pci_disable_msi(phba->pcidev); + } /* Request a slot reset. */ return PCI_ERS_RESULT_NEED_RESET; diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h index c5841d7565f..39fd2b843be 100644 --- a/drivers/scsi/lpfc/lpfc_logmsg.h +++ b/drivers/scsi/lpfc/lpfc_logmsg.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2005 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -35,11 +35,15 @@ #define LOG_ALL_MSG 0xffff /* LOG all messages */ #define lpfc_printf_vlog(vport, level, mask, fmt, arg...) \ + do { \ { if (((mask) &(vport)->cfg_log_verbose) || (level[1] <= '3')) \ dev_printk(level, &((vport)->phba->pcidev)->dev, "%d:(%d):" \ - fmt, (vport)->phba->brd_no, vport->vpi, ##arg); } + fmt, (vport)->phba->brd_no, vport->vpi, ##arg); } \ + } while (0) #define lpfc_printf_log(phba, level, mask, fmt, arg...) \ + do { \ { if (((mask) &(phba)->pport->cfg_log_verbose) || (level[1] <= '3')) \ dev_printk(level, &((phba)->pcidev)->dev, "%d:" \ - fmt, phba->brd_no, ##arg); } + fmt, phba->brd_no, ##arg); } \ + } while (0) diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c index 6dc5ab8d671..3c0cebc7180 100644 --- a/drivers/scsi/lpfc/lpfc_mem.c +++ b/drivers/scsi/lpfc/lpfc_mem.c @@ -264,19 +264,30 @@ void lpfc_in_buf_free(struct lpfc_hba *phba, struct lpfc_dmabuf *mp) { struct hbq_dmabuf *hbq_entry; + unsigned long flags; + + if (!mp) + return; if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { + /* Check whether HBQ is still in use */ + spin_lock_irqsave(&phba->hbalock, flags); + if (!phba->hbq_in_use) { + spin_unlock_irqrestore(&phba->hbalock, flags); + return; + } hbq_entry = container_of(mp, struct hbq_dmabuf, dbuf); + list_del(&hbq_entry->dbuf.list); if (hbq_entry->tag == -1) { (phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer) (phba, hbq_entry); } else { lpfc_sli_free_hbq(phba, hbq_entry); } + spin_unlock_irqrestore(&phba->hbalock, flags); } else { lpfc_mbuf_free(phba, mp->virt, mp->phys); kfree(mp); } return; } - diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 4a0e3406e37..d513813f669 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2007 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -249,6 +249,7 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_hba *phba = vport->phba; struct lpfc_dmabuf *pcmd; + struct lpfc_work_evt *evtp; uint32_t *lp; IOCB_t *icmd; struct serv_parm *sp; @@ -435,8 +436,14 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, del_timer_sync(&ndlp->nlp_delayfunc); ndlp->nlp_last_elscmd = 0; - if (!list_empty(&ndlp->els_retry_evt.evt_listp)) + if (!list_empty(&ndlp->els_retry_evt.evt_listp)) { list_del_init(&ndlp->els_retry_evt.evt_listp); + /* Decrement ndlp reference count held for the + * delayed retry + */ + evtp = &ndlp->els_retry_evt; + lpfc_nlp_put((struct lpfc_nodelist *)evtp->evt_arg1); + } if (ndlp->nlp_flag & NLP_NPR_2B_DISC) { spin_lock_irq(shost->host_lock); @@ -638,13 +645,15 @@ lpfc_disc_set_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) return 0; } - /* Check config parameter use-adisc or FCP-2 */ - if ((vport->cfg_use_adisc && (vport->fc_flag & FC_RSCN_MODE)) || - ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) { - spin_lock_irq(shost->host_lock); - ndlp->nlp_flag |= NLP_NPR_ADISC; - spin_unlock_irq(shost->host_lock); - return 1; + if (!(vport->fc_flag & FC_PT2PT)) { + /* Check config parameter use-adisc or FCP-2 */ + if ((vport->cfg_use_adisc && (vport->fc_flag & FC_RSCN_MODE)) || + ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) { + spin_lock_irq(shost->host_lock); + ndlp->nlp_flag |= NLP_NPR_ADISC; + spin_unlock_irq(shost->host_lock); + return 1; + } } ndlp->nlp_flag &= ~NLP_NPR_ADISC; lpfc_unreg_rpi(vport, ndlp); @@ -656,7 +665,7 @@ lpfc_disc_illegal(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, void *arg, uint32_t evt) { lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY, - "0253 Illegal State Transition: node x%x " + "0271 Illegal State Transition: node x%x " "event x%x, state x%x Data: x%x x%x\n", ndlp->nlp_DID, evt, ndlp->nlp_state, ndlp->nlp_rpi, ndlp->nlp_flag); @@ -674,7 +683,7 @@ lpfc_cmpl_plogi_illegal(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, */ if (!(ndlp->nlp_flag & NLP_RCV_PLOGI)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY, - "0253 Illegal State Transition: node x%x " + "0272 Illegal State Transition: node x%x " "event x%x, state x%x Data: x%x x%x\n", ndlp->nlp_DID, evt, ndlp->nlp_state, ndlp->nlp_rpi, ndlp->nlp_flag); @@ -2144,8 +2153,11 @@ lpfc_disc_state_machine(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, uint32_t cur_state, rc; uint32_t(*func) (struct lpfc_vport *, struct lpfc_nodelist *, void *, uint32_t); + uint32_t got_ndlp = 0; + + if (lpfc_nlp_get(ndlp)) + got_ndlp = 1; - lpfc_nlp_get(ndlp); cur_state = ndlp->nlp_state; /* DSM in event <evt> on NPort <nlp_DID> in state <cur_state> */ @@ -2162,15 +2174,24 @@ lpfc_disc_state_machine(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, rc = (func) (vport, ndlp, arg, evt); /* DSM out state <rc> on NPort <nlp_DID> */ - lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, + if (got_ndlp) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, "0212 DSM out state %d on NPort x%x Data: x%x\n", rc, ndlp->nlp_DID, ndlp->nlp_flag); - lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_DSM, - "DSM out: ste:%d did:x%x flg:x%x", - rc, ndlp->nlp_DID, ndlp->nlp_flag); + lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_DSM, + "DSM out: ste:%d did:x%x flg:x%x", + rc, ndlp->nlp_DID, ndlp->nlp_flag); + /* Decrement the ndlp reference count held for this function */ + lpfc_nlp_put(ndlp); + } else { + lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, + "0212 DSM out state %d on NPort free\n", rc); - lpfc_nlp_put(ndlp); + lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_DSM, + "DSM out: ste:%d did:x%x flg:x%x", + rc, 0, 0); + } return rc; } diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index fc5c3a42b05..70255c11d3a 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2007 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -1283,6 +1283,8 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd) match = 0; spin_lock_irq(shost->host_lock); list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) + continue; if (ndlp->nlp_state == NLP_STE_MAPPED_NODE && i == ndlp->nlp_sid && ndlp->rport) { diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index fdd01e384e3..fc0d9501aba 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2007 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -203,8 +203,25 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd) case CMD_IOCB_RCV_SEQ64_CX: case CMD_IOCB_RCV_ELS64_CX: case CMD_IOCB_RCV_CONT64_CX: + case CMD_IOCB_RET_XRI64_CX: type = LPFC_UNSOL_IOCB; break; + case CMD_IOCB_XMIT_MSEQ64_CR: + case CMD_IOCB_XMIT_MSEQ64_CX: + case CMD_IOCB_RCV_SEQ_LIST64_CX: + case CMD_IOCB_RCV_ELS_LIST64_CX: + case CMD_IOCB_CLOSE_EXTENDED_CN: + case CMD_IOCB_ABORT_EXTENDED_CN: + case CMD_IOCB_RET_HBQE64_CN: + case CMD_IOCB_FCP_IBIDIR64_CR: + case CMD_IOCB_FCP_IBIDIR64_CX: + case CMD_IOCB_FCP_ITASKMGT64_CX: + case CMD_IOCB_LOGENTRY_CN: + case CMD_IOCB_LOGENTRY_ASYNC_CN: + printk("%s - Unhandled SLI-3 Command x%x\n", + __FUNCTION__, iocb_cmnd); + type = LPFC_UNKNOWN_IOCB; + break; default: type = LPFC_UNKNOWN_IOCB; break; @@ -529,10 +546,13 @@ lpfc_sli_hbqbuf_free_all(struct lpfc_hba *phba) { struct lpfc_dmabuf *dmabuf, *next_dmabuf; struct hbq_dmabuf *hbq_buf; + unsigned long flags; int i, hbq_count; + uint32_t hbqno; hbq_count = lpfc_sli_hbq_count(); /* Return all memory used by all HBQs */ + spin_lock_irqsave(&phba->hbalock, flags); for (i = 0; i < hbq_count; ++i) { list_for_each_entry_safe(dmabuf, next_dmabuf, &phba->hbqs[i].hbq_buffer_list, list) { @@ -542,6 +562,28 @@ lpfc_sli_hbqbuf_free_all(struct lpfc_hba *phba) } phba->hbqs[i].buffer_count = 0; } + /* Return all HBQ buffer that are in-fly */ + list_for_each_entry_safe(dmabuf, next_dmabuf, + &phba->hbqbuf_in_list, list) { + hbq_buf = container_of(dmabuf, struct hbq_dmabuf, dbuf); + list_del(&hbq_buf->dbuf.list); + if (hbq_buf->tag == -1) { + (phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer) + (phba, hbq_buf); + } else { + hbqno = hbq_buf->tag >> 16; + if (hbqno >= LPFC_MAX_HBQS) + (phba->hbqs[LPFC_ELS_HBQ].hbq_free_buffer) + (phba, hbq_buf); + else + (phba->hbqs[hbqno].hbq_free_buffer)(phba, + hbq_buf); + } + } + + /* Mark the HBQs not in use */ + phba->hbq_in_use = 0; + spin_unlock_irqrestore(&phba->hbalock, flags); } static struct lpfc_hbq_entry * @@ -603,30 +645,40 @@ static int lpfc_sli_hbqbuf_fill_hbqs(struct lpfc_hba *phba, uint32_t hbqno, uint32_t count) { uint32_t i, start, end; + unsigned long flags; struct hbq_dmabuf *hbq_buffer; - if (!phba->hbqs[hbqno].hbq_alloc_buffer) { + if (!phba->hbqs[hbqno].hbq_alloc_buffer) return 0; - } start = phba->hbqs[hbqno].buffer_count; end = count + start; - if (end > lpfc_hbq_defs[hbqno]->entry_count) { + if (end > lpfc_hbq_defs[hbqno]->entry_count) end = lpfc_hbq_defs[hbqno]->entry_count; - } + + /* Check whether HBQ is still in use */ + spin_lock_irqsave(&phba->hbalock, flags); + if (!phba->hbq_in_use) + goto out; /* Populate HBQ entries */ for (i = start; i < end; i++) { hbq_buffer = (phba->hbqs[hbqno].hbq_alloc_buffer)(phba); if (!hbq_buffer) - return 1; + goto err; hbq_buffer->tag = (i | (hbqno << 16)); if (lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buffer)) phba->hbqs[hbqno].buffer_count++; else (phba->hbqs[hbqno].hbq_free_buffer)(phba, hbq_buffer); } + + out: + spin_unlock_irqrestore(&phba->hbalock, flags); return 0; + err: + spin_unlock_irqrestore(&phba->hbalock, flags); + return 1; } int @@ -910,16 +962,29 @@ lpfc_sli_replace_hbqbuff(struct lpfc_hba *phba, uint32_t tag) uint32_t hbqno; void *virt; /* virtual address ptr */ dma_addr_t phys; /* mapped address */ + unsigned long flags; + + /* Check whether HBQ is still in use */ + spin_lock_irqsave(&phba->hbalock, flags); + if (!phba->hbq_in_use) { + spin_unlock_irqrestore(&phba->hbalock, flags); + return NULL; + } hbq_entry = lpfc_sli_hbqbuf_find(phba, tag); - if (hbq_entry == NULL) + if (hbq_entry == NULL) { + spin_unlock_irqrestore(&phba->hbalock, flags); return NULL; + } list_del(&hbq_entry->dbuf.list); hbqno = tag >> 16; new_hbq_entry = (phba->hbqs[hbqno].hbq_alloc_buffer)(phba); - if (new_hbq_entry == NULL) + if (new_hbq_entry == NULL) { + list_add_tail(&hbq_entry->dbuf.list, &phba->hbqbuf_in_list); + spin_unlock_irqrestore(&phba->hbalock, flags); return &hbq_entry->dbuf; + } new_hbq_entry->tag = -1; phys = new_hbq_entry->dbuf.phys; virt = new_hbq_entry->dbuf.virt; @@ -928,6 +993,9 @@ lpfc_sli_replace_hbqbuff(struct lpfc_hba *phba, uint32_t tag) hbq_entry->dbuf.phys = phys; hbq_entry->dbuf.virt = virt; lpfc_sli_free_hbq(phba, hbq_entry); + list_add_tail(&new_hbq_entry->dbuf.list, &phba->hbqbuf_in_list); + spin_unlock_irqrestore(&phba->hbalock, flags); + return &new_hbq_entry->dbuf; } @@ -951,6 +1019,7 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, uint32_t Rctl, Type; uint32_t match, i; struct lpfc_iocbq *iocbq; + struct lpfc_dmabuf *dmzbuf; match = 0; irsp = &(saveq->iocb); @@ -972,6 +1041,29 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, return 1; } + if ((irsp->ulpCommand == CMD_IOCB_RET_XRI64_CX) && + (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)) { + if (irsp->ulpBdeCount > 0) { + dmzbuf = lpfc_sli_get_buff(phba, pring, + irsp->un.ulpWord[3]); + lpfc_in_buf_free(phba, dmzbuf); + } + + if (irsp->ulpBdeCount > 1) { + dmzbuf = lpfc_sli_get_buff(phba, pring, + irsp->unsli3.sli3Words[3]); + lpfc_in_buf_free(phba, dmzbuf); + } + + if (irsp->ulpBdeCount > 2) { + dmzbuf = lpfc_sli_get_buff(phba, pring, + irsp->unsli3.sli3Words[7]); + lpfc_in_buf_free(phba, dmzbuf); + } + + return 1; + } + if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) { if (irsp->ulpBdeCount != 0) { saveq->context2 = lpfc_sli_get_buff(phba, pring, @@ -2293,6 +2385,7 @@ lpfc_sli_hbq_setup(struct lpfc_hba *phba) /* Initialize the struct lpfc_sli_hbq structure for each hbq */ phba->link_state = LPFC_INIT_MBX_CMDS; + phba->hbq_in_use = 1; hbq_entry_index = 0; for (hbqno = 0; hbqno < hbq_count; ++hbqno) { @@ -2404,9 +2497,7 @@ lpfc_do_config_port(struct lpfc_hba *phba, int sli_mode) if ((pmb->mb.un.varCfgPort.sli_mode == 3) && (!pmb->mb.un.varCfgPort.cMA)) { rc = -ENXIO; - goto do_prep_failed; } - return rc; do_prep_failed: mempool_free(pmb, phba->mbox_mem_pool); @@ -2625,14 +2716,14 @@ lpfc_sli_issue_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, uint32_t flag) spin_unlock_irqrestore(&phba->hbalock, drvr_flag); /* Mbox command <mbxCommand> cannot issue */ - LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag) + LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag); return MBX_NOT_FINISHED; } if (mb->mbxCommand != MBX_KILL_BOARD && flag & MBX_NOWAIT && !(readl(phba->HCregaddr) & HC_MBINT_ENA)) { spin_unlock_irqrestore(&phba->hbalock, drvr_flag); - LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag) + LOG_MBOX_CANNOT_ISSUE_DATA(phba, pmbox, psli, flag); return MBX_NOT_FINISHED; } diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 4b633d39a82..ca540d1d041 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -18,7 +18,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "8.2.4" +#define LPFC_DRIVER_VERSION "8.2.5" #define LPFC_DRIVER_NAME "lpfc" diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index 9fad7663c11..86d05beb00b 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2006 Emulex. All rights reserved. * + * Copyright (C) 2004-2008 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -327,7 +327,8 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable) * up and ready to FDISC. */ ndlp = lpfc_findnode_did(phba->pport, Fabric_DID); - if (ndlp && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) { + if (ndlp && NLP_CHK_NODE_ACT(ndlp) && + ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) { if (phba->link_flag & LS_NPIV_FAB_SUPPORTED) { lpfc_set_disctmo(vport); lpfc_initial_fdisc(vport); @@ -358,7 +359,8 @@ disable_vport(struct fc_vport *fc_vport) long timeout; ndlp = lpfc_findnode_did(vport, Fabric_DID); - if (ndlp && phba->link_state >= LPFC_LINK_UP) { + if (ndlp && NLP_CHK_NODE_ACT(ndlp) + && phba->link_state >= LPFC_LINK_UP) { vport->unreg_vpi_cmpl = VPORT_INVAL; timeout = msecs_to_jiffies(phba->fc_ratov * 2000); if (!lpfc_issue_els_npiv_logo(vport, ndlp)) @@ -372,6 +374,8 @@ disable_vport(struct fc_vport *fc_vport) * calling lpfc_cleanup_rpis(vport, 1) */ list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { + if (!NLP_CHK_NODE_ACT(ndlp)) + continue; if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) continue; lpfc_disc_state_machine(vport, ndlp, NULL, @@ -414,7 +418,8 @@ enable_vport(struct fc_vport *fc_vport) * up and ready to FDISC. */ ndlp = lpfc_findnode_did(phba->pport, Fabric_DID); - if (ndlp && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) { + if (ndlp && NLP_CHK_NODE_ACT(ndlp) + && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) { if (phba->link_flag & LS_NPIV_FAB_SUPPORTED) { lpfc_set_disctmo(vport); lpfc_initial_fdisc(vport); @@ -498,7 +503,41 @@ lpfc_vport_delete(struct fc_vport *fc_vport) scsi_remove_host(lpfc_shost_from_vport(vport)); ndlp = lpfc_findnode_did(phba->pport, Fabric_DID); - if (ndlp && ndlp->nlp_state == NLP_STE_UNMAPPED_NODE && + + /* In case of driver unload, we shall not perform fabric logo as the + * worker thread already stopped at this stage and, in this case, we + * can safely skip the fabric logo. + */ + if (phba->pport->load_flag & FC_UNLOADING) { + if (ndlp && NLP_CHK_NODE_ACT(ndlp) && + ndlp->nlp_state == NLP_STE_UNMAPPED_NODE && + phba->link_state >= LPFC_LINK_UP) { + /* First look for the Fabric ndlp */ + ndlp = lpfc_findnode_did(vport, Fabric_DID); + if (!ndlp) + goto skip_logo; + else if (!NLP_CHK_NODE_ACT(ndlp)) { + ndlp = lpfc_enable_node(vport, ndlp, + NLP_STE_UNUSED_NODE); + if (!ndlp) + goto skip_logo; + } + /* Remove ndlp from vport npld list */ + lpfc_dequeue_node(vport, ndlp); + + /* Indicate free memory when release */ + spin_lock_irq(&phba->ndlp_lock); + NLP_SET_FREE_REQ(ndlp); + spin_unlock_irq(&phba->ndlp_lock); + /* Kick off release ndlp when it can be safely done */ + lpfc_nlp_put(ndlp); + } + goto skip_logo; + } + + /* Otherwise, we will perform fabric logo as needed */ + if (ndlp && NLP_CHK_NODE_ACT(ndlp) && + ndlp->nlp_state == NLP_STE_UNMAPPED_NODE && phba->link_state >= LPFC_LINK_UP) { if (vport->cfg_enable_da_id) { timeout = msecs_to_jiffies(phba->fc_ratov * 2000); @@ -519,8 +558,27 @@ lpfc_vport_delete(struct fc_vport *fc_vport) if (!ndlp) goto skip_logo; lpfc_nlp_init(vport, ndlp, Fabric_DID); + /* Indicate free memory when release */ + NLP_SET_FREE_REQ(ndlp); } else { + if (!NLP_CHK_NODE_ACT(ndlp)) + ndlp = lpfc_enable_node(vport, ndlp, + NLP_STE_UNUSED_NODE); + if (!ndlp) + goto skip_logo; + + /* Remove ndlp from vport npld list */ lpfc_dequeue_node(vport, ndlp); + spin_lock_irq(&phba->ndlp_lock); + if (!NLP_CHK_FREE_REQ(ndlp)) + /* Indicate free memory when release */ + NLP_SET_FREE_REQ(ndlp); + else { + /* Skip this if ndlp is already in free mode */ + spin_unlock_irq(&phba->ndlp_lock); + goto skip_logo; + } + spin_unlock_irq(&phba->ndlp_lock); } vport->unreg_vpi_cmpl = VPORT_INVAL; timeout = msecs_to_jiffies(phba->fc_ratov * 2000); @@ -534,9 +592,9 @@ skip_logo: lpfc_sli_host_down(vport); lpfc_stop_vport_timers(vport); - lpfc_unreg_all_rpis(vport); if (!(phba->pport->load_flag & FC_UNLOADING)) { + lpfc_unreg_all_rpis(vport); lpfc_unreg_default_rpis(vport); /* * Completion of unreg_vpi (lpfc_mbx_cmpl_unreg_vpi) diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 4d59ae8491a..b135a1ed4b2 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -151,19 +151,19 @@ mega_setup_mailbox(adapter_t *adapter) */ if( adapter->flag & BOARD_IOMAP ) { - outb_p(adapter->mbox_dma & 0xFF, + outb(adapter->mbox_dma & 0xFF, adapter->host->io_port + MBOX_PORT0); - outb_p((adapter->mbox_dma >> 8) & 0xFF, + outb((adapter->mbox_dma >> 8) & 0xFF, adapter->host->io_port + MBOX_PORT1); - outb_p((adapter->mbox_dma >> 16) & 0xFF, + outb((adapter->mbox_dma >> 16) & 0xFF, adapter->host->io_port + MBOX_PORT2); - outb_p((adapter->mbox_dma >> 24) & 0xFF, + outb((adapter->mbox_dma >> 24) & 0xFF, adapter->host->io_port + MBOX_PORT3); - outb_p(ENABLE_MBOX_BYTE, + outb(ENABLE_MBOX_BYTE, adapter->host->io_port + ENABLE_MBOX_REGION); irq_ack(adapter); diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c index b6587a6d848..0ad215e27b8 100644 --- a/drivers/scsi/megaraid/megaraid_mm.c +++ b/drivers/scsi/megaraid/megaraid_mm.c @@ -59,7 +59,6 @@ EXPORT_SYMBOL(mraid_mm_register_adp); EXPORT_SYMBOL(mraid_mm_unregister_adp); EXPORT_SYMBOL(mraid_mm_adapter_app_handle); -static int majorno; static uint32_t drvr_ver = 0x02200207; static int adapters_count_g; @@ -76,6 +75,12 @@ static const struct file_operations lsi_fops = { .owner = THIS_MODULE, }; +static struct miscdevice megaraid_mm_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "megadev0", + .fops = &lsi_fops, +}; + /** * mraid_mm_open - open routine for char node interface * @inode : unused @@ -1184,15 +1189,16 @@ mraid_mm_teardown_dma_pools(mraid_mmadp_t *adp) static int __init mraid_mm_init(void) { + int err; + // Announce the driver version con_log(CL_ANN, (KERN_INFO "megaraid cmm: %s %s\n", LSI_COMMON_MOD_VERSION, LSI_COMMON_MOD_EXT_VERSION)); - majorno = register_chrdev(0, "megadev", &lsi_fops); - - if (majorno < 0) { - con_log(CL_ANN, ("megaraid cmm: cannot get major\n")); - return majorno; + err = misc_register(&megaraid_mm_dev); + if (err < 0) { + con_log(CL_ANN, ("megaraid cmm: cannot register misc device\n")); + return err; } init_waitqueue_head(&wait_q); @@ -1230,7 +1236,7 @@ mraid_mm_exit(void) { con_log(CL_DLEVEL1 , ("exiting common mod\n")); - unregister_chrdev(majorno, "megadev"); + misc_deregister(&megaraid_mm_dev); } module_init(mraid_mm_init); diff --git a/drivers/scsi/megaraid/megaraid_mm.h b/drivers/scsi/megaraid/megaraid_mm.h index c8762b2b8ed..55b425c0a65 100644 --- a/drivers/scsi/megaraid/megaraid_mm.h +++ b/drivers/scsi/megaraid/megaraid_mm.h @@ -22,6 +22,7 @@ #include <linux/moduleparam.h> #include <linux/pci.h> #include <linux/list.h> +#include <linux/miscdevice.h> #include "mbox_defs.h" #include "megaraid_ioctl.h" diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c index 651d09b08f2..fd63b06d9ef 100644 --- a/drivers/scsi/mesh.c +++ b/drivers/scsi/mesh.c @@ -1759,6 +1759,7 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg) switch (mesg.event) { case PM_EVENT_SUSPEND: + case PM_EVENT_HIBERNATE: case PM_EVENT_FREEZE: break; default: diff --git a/drivers/scsi/mvsas.c b/drivers/scsi/mvsas.c new file mode 100644 index 00000000000..d4a6ac3c9c4 --- /dev/null +++ b/drivers/scsi/mvsas.c @@ -0,0 +1,2970 @@ +/* + mvsas.c - Marvell 88SE6440 SAS/SATA support + + Copyright 2007 Red Hat, Inc. + Copyright 2008 Marvell. <kewei@marvell.com> + + 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, + 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; see the file COPYING. If not, + write to the Free Software Foundation, 675 Mass Ave, Cambridge, + MA 02139, USA. + + --------------------------------------------------------------- + + Random notes: + * hardware supports controlling the endian-ness of data + structures. this permits elimination of all the le32_to_cpu() + and cpu_to_le32() conversions. + + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/ctype.h> +#include <scsi/libsas.h> +#include <asm/io.h> + +#define DRV_NAME "mvsas" +#define DRV_VERSION "0.5" +#define _MV_DUMP 0 +#define MVS_DISABLE_NVRAM +#define MVS_DISABLE_MSI + +#define mr32(reg) readl(regs + MVS_##reg) +#define mw32(reg,val) writel((val), regs + MVS_##reg) +#define mw32_f(reg,val) do { \ + writel((val), regs + MVS_##reg); \ + readl(regs + MVS_##reg); \ + } while (0) + +#define MVS_ID_NOT_MAPPED 0xff +#define MVS_CHIP_SLOT_SZ (1U << mvi->chip->slot_width) + +/* offset for D2H FIS in the Received FIS List Structure */ +#define SATA_RECEIVED_D2H_FIS(reg_set) \ + ((void *) mvi->rx_fis + 0x400 + 0x100 * reg_set + 0x40) +#define SATA_RECEIVED_PIO_FIS(reg_set) \ + ((void *) mvi->rx_fis + 0x400 + 0x100 * reg_set + 0x20) +#define UNASSOC_D2H_FIS(id) \ + ((void *) mvi->rx_fis + 0x100 * id) + +#define for_each_phy(__lseq_mask, __mc, __lseq, __rest) \ + for ((__mc) = (__lseq_mask), (__lseq) = 0; \ + (__mc) != 0 && __rest; \ + (++__lseq), (__mc) >>= 1) + +/* driver compile-time configuration */ +enum driver_configuration { + MVS_TX_RING_SZ = 1024, /* TX ring size (12-bit) */ + MVS_RX_RING_SZ = 1024, /* RX ring size (12-bit) */ + /* software requires power-of-2 + ring size */ + + MVS_SLOTS = 512, /* command slots */ + MVS_SLOT_BUF_SZ = 8192, /* cmd tbl + IU + status + PRD */ + MVS_SSP_CMD_SZ = 64, /* SSP command table buffer size */ + MVS_ATA_CMD_SZ = 96, /* SATA command table buffer size */ + MVS_OAF_SZ = 64, /* Open address frame buffer size */ + + MVS_RX_FIS_COUNT = 17, /* Optional rx'd FISs (max 17) */ + + MVS_QUEUE_SIZE = 30, /* Support Queue depth */ +}; + +/* unchangeable hardware details */ +enum hardware_details { + MVS_MAX_PHYS = 8, /* max. possible phys */ + MVS_MAX_PORTS = 8, /* max. possible ports */ + MVS_RX_FISL_SZ = 0x400 + (MVS_RX_FIS_COUNT * 0x100), +}; + +/* peripheral registers (BAR2) */ +enum peripheral_registers { + SPI_CTL = 0x10, /* EEPROM control */ + SPI_CMD = 0x14, /* EEPROM command */ + SPI_DATA = 0x18, /* EEPROM data */ +}; + +enum peripheral_register_bits { + TWSI_RDY = (1U << 7), /* EEPROM interface ready */ + TWSI_RD = (1U << 4), /* EEPROM read access */ + + SPI_ADDR_MASK = 0x3ffff, /* bits 17:0 */ +}; + +/* enhanced mode registers (BAR4) */ +enum hw_registers { + MVS_GBL_CTL = 0x04, /* global control */ + MVS_GBL_INT_STAT = 0x08, /* global irq status */ + MVS_GBL_PI = 0x0C, /* ports implemented bitmask */ + MVS_GBL_PORT_TYPE = 0xa0, /* port type */ + + MVS_CTL = 0x100, /* SAS/SATA port configuration */ + MVS_PCS = 0x104, /* SAS/SATA port control/status */ + MVS_CMD_LIST_LO = 0x108, /* cmd list addr */ + MVS_CMD_LIST_HI = 0x10C, + MVS_RX_FIS_LO = 0x110, /* RX FIS list addr */ + MVS_RX_FIS_HI = 0x114, + + MVS_TX_CFG = 0x120, /* TX configuration */ + MVS_TX_LO = 0x124, /* TX (delivery) ring addr */ + MVS_TX_HI = 0x128, + + MVS_TX_PROD_IDX = 0x12C, /* TX producer pointer */ + MVS_TX_CONS_IDX = 0x130, /* TX consumer pointer (RO) */ + MVS_RX_CFG = 0x134, /* RX configuration */ + MVS_RX_LO = 0x138, /* RX (completion) ring addr */ + MVS_RX_HI = 0x13C, + MVS_RX_CONS_IDX = 0x140, /* RX consumer pointer (RO) */ + + MVS_INT_COAL = 0x148, /* Int coalescing config */ + MVS_INT_COAL_TMOUT = 0x14C, /* Int coalescing timeout */ + MVS_INT_STAT = 0x150, /* Central int status */ + MVS_INT_MASK = 0x154, /* Central int enable */ + MVS_INT_STAT_SRS = 0x158, /* SATA register set status */ + MVS_INT_MASK_SRS = 0x15C, + + /* ports 1-3 follow after this */ + MVS_P0_INT_STAT = 0x160, /* port0 interrupt status */ + MVS_P0_INT_MASK = 0x164, /* port0 interrupt mask */ + MVS_P4_INT_STAT = 0x200, /* Port 4 interrupt status */ + MVS_P4_INT_MASK = 0x204, /* Port 4 interrupt enable mask */ + + /* ports 1-3 follow after this */ + MVS_P0_SER_CTLSTAT = 0x180, /* port0 serial control/status */ + MVS_P4_SER_CTLSTAT = 0x220, /* port4 serial control/status */ + + MVS_CMD_ADDR = 0x1B8, /* Command register port (addr) */ + MVS_CMD_DATA = 0x1BC, /* Command register port (data) */ + + /* ports 1-3 follow after this */ + MVS_P0_CFG_ADDR = 0x1C0, /* port0 phy register address */ + MVS_P0_CFG_DATA = 0x1C4, /* port0 phy register data */ + MVS_P4_CFG_ADDR = 0x230, /* Port 4 config address */ + MVS_P4_CFG_DATA = 0x234, /* Port 4 config data */ + + /* ports 1-3 follow after this */ + MVS_P0_VSR_ADDR = 0x1E0, /* port0 VSR address */ + MVS_P0_VSR_DATA = 0x1E4, /* port0 VSR data */ + MVS_P4_VSR_ADDR = 0x250, /* port 4 VSR addr */ + MVS_P4_VSR_DATA = 0x254, /* port 4 VSR data */ +}; + +enum hw_register_bits { + /* MVS_GBL_CTL */ + INT_EN = (1U << 1), /* Global int enable */ + HBA_RST = (1U << 0), /* HBA reset */ + + /* MVS_GBL_INT_STAT */ + INT_XOR = (1U << 4), /* XOR engine event */ + INT_SAS_SATA = (1U << 0), /* SAS/SATA event */ + + /* MVS_GBL_PORT_TYPE */ /* shl for ports 1-3 */ + SATA_TARGET = (1U << 16), /* port0 SATA target enable */ + MODE_AUTO_DET_PORT7 = (1U << 15), /* port0 SAS/SATA autodetect */ + MODE_AUTO_DET_PORT6 = (1U << 14), + MODE_AUTO_DET_PORT5 = (1U << 13), + MODE_AUTO_DET_PORT4 = (1U << 12), + MODE_AUTO_DET_PORT3 = (1U << 11), + MODE_AUTO_DET_PORT2 = (1U << 10), + MODE_AUTO_DET_PORT1 = (1U << 9), + MODE_AUTO_DET_PORT0 = (1U << 8), + MODE_AUTO_DET_EN = MODE_AUTO_DET_PORT0 | MODE_AUTO_DET_PORT1 | + MODE_AUTO_DET_PORT2 | MODE_AUTO_DET_PORT3 | + MODE_AUTO_DET_PORT4 | MODE_AUTO_DET_PORT5 | + MODE_AUTO_DET_PORT6 | MODE_AUTO_DET_PORT7, + MODE_SAS_PORT7_MASK = (1U << 7), /* port0 SAS(1), SATA(0) mode */ + MODE_SAS_PORT6_MASK = (1U << 6), + MODE_SAS_PORT5_MASK = (1U << 5), + MODE_SAS_PORT4_MASK = (1U << 4), + MODE_SAS_PORT3_MASK = (1U << 3), + MODE_SAS_PORT2_MASK = (1U << 2), + MODE_SAS_PORT1_MASK = (1U << 1), + MODE_SAS_PORT0_MASK = (1U << 0), + MODE_SAS_SATA = MODE_SAS_PORT0_MASK | MODE_SAS_PORT1_MASK | + MODE_SAS_PORT2_MASK | MODE_SAS_PORT3_MASK | + MODE_SAS_PORT4_MASK | MODE_SAS_PORT5_MASK | + MODE_SAS_PORT6_MASK | MODE_SAS_PORT7_MASK, + + /* SAS_MODE value may be + * dictated (in hw) by values + * of SATA_TARGET & AUTO_DET + */ + + /* MVS_TX_CFG */ + TX_EN = (1U << 16), /* Enable TX */ + TX_RING_SZ_MASK = 0xfff, /* TX ring size, bits 11:0 */ + + /* MVS_RX_CFG */ + RX_EN = (1U << 16), /* Enable RX */ + RX_RING_SZ_MASK = 0xfff, /* RX ring size, bits 11:0 */ + + /* MVS_INT_COAL */ + COAL_EN = (1U << 16), /* Enable int coalescing */ + + /* MVS_INT_STAT, MVS_INT_MASK */ + CINT_I2C = (1U << 31), /* I2C event */ + CINT_SW0 = (1U << 30), /* software event 0 */ + CINT_SW1 = (1U << 29), /* software event 1 */ + CINT_PRD_BC = (1U << 28), /* PRD BC err for read cmd */ + CINT_DMA_PCIE = (1U << 27), /* DMA to PCIE timeout */ + CINT_MEM = (1U << 26), /* int mem parity err */ + CINT_I2C_SLAVE = (1U << 25), /* slave I2C event */ + CINT_SRS = (1U << 3), /* SRS event */ + CINT_CI_STOP = (1U << 1), /* cmd issue stopped */ + CINT_DONE = (1U << 0), /* cmd completion */ + + /* shl for ports 1-3 */ + CINT_PORT_STOPPED = (1U << 16), /* port0 stopped */ + CINT_PORT = (1U << 8), /* port0 event */ + CINT_PORT_MASK_OFFSET = 8, + CINT_PORT_MASK = (0xFF << CINT_PORT_MASK_OFFSET), + + /* TX (delivery) ring bits */ + TXQ_CMD_SHIFT = 29, + TXQ_CMD_SSP = 1, /* SSP protocol */ + TXQ_CMD_SMP = 2, /* SMP protocol */ + TXQ_CMD_STP = 3, /* STP/SATA protocol */ + TXQ_CMD_SSP_FREE_LIST = 4, /* add to SSP targ free list */ + TXQ_CMD_SLOT_RESET = 7, /* reset command slot */ + TXQ_MODE_I = (1U << 28), /* mode: 0=target,1=initiator */ + TXQ_PRIO_HI = (1U << 27), /* priority: 0=normal, 1=high */ + TXQ_SRS_SHIFT = 20, /* SATA register set */ + TXQ_SRS_MASK = 0x7f, + TXQ_PHY_SHIFT = 12, /* PHY bitmap */ + TXQ_PHY_MASK = 0xff, + TXQ_SLOT_MASK = 0xfff, /* slot number */ + + /* RX (completion) ring bits */ + RXQ_GOOD = (1U << 23), /* Response good */ + RXQ_SLOT_RESET = (1U << 21), /* Slot reset complete */ + RXQ_CMD_RX = (1U << 20), /* target cmd received */ + RXQ_ATTN = (1U << 19), /* attention */ + RXQ_RSP = (1U << 18), /* response frame xfer'd */ + RXQ_ERR = (1U << 17), /* err info rec xfer'd */ + RXQ_DONE = (1U << 16), /* cmd complete */ + RXQ_SLOT_MASK = 0xfff, /* slot number */ + + /* mvs_cmd_hdr bits */ + MCH_PRD_LEN_SHIFT = 16, /* 16-bit PRD table len */ + MCH_SSP_FR_TYPE_SHIFT = 13, /* SSP frame type */ + + /* SSP initiator only */ + MCH_SSP_FR_CMD = 0x0, /* COMMAND frame */ + + /* SSP initiator or target */ + MCH_SSP_FR_TASK = 0x1, /* TASK frame */ + + /* SSP target only */ + MCH_SSP_FR_XFER_RDY = 0x4, /* XFER_RDY frame */ + MCH_SSP_FR_RESP = 0x5, /* RESPONSE frame */ + MCH_SSP_FR_READ = 0x6, /* Read DATA frame(s) */ + MCH_SSP_FR_READ_RESP = 0x7, /* ditto, plus RESPONSE */ + + MCH_PASSTHRU = (1U << 12), /* pass-through (SSP) */ + MCH_FBURST = (1U << 11), /* first burst (SSP) */ + MCH_CHK_LEN = (1U << 10), /* chk xfer len (SSP) */ + MCH_RETRY = (1U << 9), /* tport layer retry (SSP) */ + MCH_PROTECTION = (1U << 8), /* protection info rec (SSP) */ + MCH_RESET = (1U << 7), /* Reset (STP/SATA) */ + MCH_FPDMA = (1U << 6), /* First party DMA (STP/SATA) */ + MCH_ATAPI = (1U << 5), /* ATAPI (STP/SATA) */ + MCH_BIST = (1U << 4), /* BIST activate (STP/SATA) */ + MCH_PMP_MASK = 0xf, /* PMP from cmd FIS (STP/SATA)*/ + + CCTL_RST = (1U << 5), /* port logic reset */ + + /* 0(LSB first), 1(MSB first) */ + CCTL_ENDIAN_DATA = (1U << 3), /* PRD data */ + CCTL_ENDIAN_RSP = (1U << 2), /* response frame */ + CCTL_ENDIAN_OPEN = (1U << 1), /* open address frame */ + CCTL_ENDIAN_CMD = (1U << 0), /* command table */ + + /* MVS_Px_SER_CTLSTAT (per-phy control) */ + PHY_SSP_RST = (1U << 3), /* reset SSP link layer */ + PHY_BCAST_CHG = (1U << 2), /* broadcast(change) notif */ + PHY_RST_HARD = (1U << 1), /* hard reset + phy reset */ + PHY_RST = (1U << 0), /* phy reset */ + PHY_MIN_SPP_PHYS_LINK_RATE_MASK = (0xF << 8), + PHY_MAX_SPP_PHYS_LINK_RATE_MASK = (0xF << 12), + PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET = (16), + PHY_NEG_SPP_PHYS_LINK_RATE_MASK = + (0xF << PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET), + PHY_READY_MASK = (1U << 20), + + /* MVS_Px_INT_STAT, MVS_Px_INT_MASK (per-phy events) */ + PHYEV_DEC_ERR = (1U << 24), /* Phy Decoding Error */ + PHYEV_UNASSOC_FIS = (1U << 19), /* unassociated FIS rx'd */ + PHYEV_AN = (1U << 18), /* SATA async notification */ + PHYEV_BIST_ACT = (1U << 17), /* BIST activate FIS */ + PHYEV_SIG_FIS = (1U << 16), /* signature FIS */ + PHYEV_POOF = (1U << 12), /* phy ready from 1 -> 0 */ + PHYEV_IU_BIG = (1U << 11), /* IU too long err */ + PHYEV_IU_SMALL = (1U << 10), /* IU too short err */ + PHYEV_UNK_TAG = (1U << 9), /* unknown tag */ + PHYEV_BROAD_CH = (1U << 8), /* broadcast(CHANGE) */ + PHYEV_COMWAKE = (1U << 7), /* COMWAKE rx'd */ + PHYEV_PORT_SEL = (1U << 6), /* port selector present */ + PHYEV_HARD_RST = (1U << 5), /* hard reset rx'd */ + PHYEV_ID_TMOUT = (1U << 4), /* identify timeout */ + PHYEV_ID_FAIL = (1U << 3), /* identify failed */ + PHYEV_ID_DONE = (1U << 2), /* identify done */ + PHYEV_HARD_RST_DONE = (1U << 1), /* hard reset done */ + PHYEV_RDY_CH = (1U << 0), /* phy ready changed state */ + + /* MVS_PCS */ + PCS_EN_SATA_REG_SHIFT = (16), /* Enable SATA Register Set */ + PCS_EN_PORT_XMT_SHIFT = (12), /* Enable Port Transmit */ + PCS_EN_PORT_XMT_SHIFT2 = (8), /* For 6480 */ + PCS_SATA_RETRY = (1U << 8), /* retry ctl FIS on R_ERR */ + PCS_RSP_RX_EN = (1U << 7), /* raw response rx */ + PCS_SELF_CLEAR = (1U << 5), /* self-clearing int mode */ + PCS_FIS_RX_EN = (1U << 4), /* FIS rx enable */ + PCS_CMD_STOP_ERR = (1U << 3), /* cmd stop-on-err enable */ + PCS_CMD_RST = (1U << 1), /* reset cmd issue */ + PCS_CMD_EN = (1U << 0), /* enable cmd issue */ + + /* Port n Attached Device Info */ + PORT_DEV_SSP_TRGT = (1U << 19), + PORT_DEV_SMP_TRGT = (1U << 18), + PORT_DEV_STP_TRGT = (1U << 17), + PORT_DEV_SSP_INIT = (1U << 11), + PORT_DEV_SMP_INIT = (1U << 10), + PORT_DEV_STP_INIT = (1U << 9), + PORT_PHY_ID_MASK = (0xFFU << 24), + PORT_DEV_TRGT_MASK = (0x7U << 17), + PORT_DEV_INIT_MASK = (0x7U << 9), + PORT_DEV_TYPE_MASK = (0x7U << 0), + + /* Port n PHY Status */ + PHY_RDY = (1U << 2), + PHY_DW_SYNC = (1U << 1), + PHY_OOB_DTCTD = (1U << 0), + + /* VSR */ + /* PHYMODE 6 (CDB) */ + PHY_MODE6_DTL_SPEED = (1U << 27), +}; + +enum mvs_info_flags { + MVF_MSI = (1U << 0), /* MSI is enabled */ + MVF_PHY_PWR_FIX = (1U << 1), /* bug workaround */ +}; + +enum sas_cmd_port_registers { + CMD_CMRST_OOB_DET = 0x100, /* COMRESET OOB detect register */ + CMD_CMWK_OOB_DET = 0x104, /* COMWAKE OOB detect register */ + CMD_CMSAS_OOB_DET = 0x108, /* COMSAS OOB detect register */ + CMD_BRST_OOB_DET = 0x10c, /* burst OOB detect register */ + CMD_OOB_SPACE = 0x110, /* OOB space control register */ + CMD_OOB_BURST = 0x114, /* OOB burst control register */ + CMD_PHY_TIMER = 0x118, /* PHY timer control register */ + CMD_PHY_CONFIG0 = 0x11c, /* PHY config register 0 */ + CMD_PHY_CONFIG1 = 0x120, /* PHY config register 1 */ + CMD_SAS_CTL0 = 0x124, /* SAS control register 0 */ + CMD_SAS_CTL1 = 0x128, /* SAS control register 1 */ + CMD_SAS_CTL2 = 0x12c, /* SAS control register 2 */ + CMD_SAS_CTL3 = 0x130, /* SAS control register 3 */ + CMD_ID_TEST = 0x134, /* ID test register */ + CMD_PL_TIMER = 0x138, /* PL timer register */ + CMD_WD_TIMER = 0x13c, /* WD timer register */ + CMD_PORT_SEL_COUNT = 0x140, /* port selector count register */ + CMD_APP_MEM_CTL = 0x144, /* Application Memory Control */ + CMD_XOR_MEM_CTL = 0x148, /* XOR Block Memory Control */ + CMD_DMA_MEM_CTL = 0x14c, /* DMA Block Memory Control */ + CMD_PORT_MEM_CTL0 = 0x150, /* Port Memory Control 0 */ + CMD_PORT_MEM_CTL1 = 0x154, /* Port Memory Control 1 */ + CMD_SATA_PORT_MEM_CTL0 = 0x158, /* SATA Port Memory Control 0 */ + CMD_SATA_PORT_MEM_CTL1 = 0x15c, /* SATA Port Memory Control 1 */ + CMD_XOR_MEM_BIST_CTL = 0x160, /* XOR Memory BIST Control */ + CMD_XOR_MEM_BIST_STAT = 0x164, /* XOR Memroy BIST Status */ + CMD_DMA_MEM_BIST_CTL = 0x168, /* DMA Memory BIST Control */ + CMD_DMA_MEM_BIST_STAT = 0x16c, /* DMA Memory BIST Status */ + CMD_PORT_MEM_BIST_CTL = 0x170, /* Port Memory BIST Control */ + CMD_PORT_MEM_BIST_STAT0 = 0x174, /* Port Memory BIST Status 0 */ + CMD_PORT_MEM_BIST_STAT1 = 0x178, /* Port Memory BIST Status 1 */ + CMD_STP_MEM_BIST_CTL = 0x17c, /* STP Memory BIST Control */ + CMD_STP_MEM_BIST_STAT0 = 0x180, /* STP Memory BIST Status 0 */ + CMD_STP_MEM_BIST_STAT1 = 0x184, /* STP Memory BIST Status 1 */ + CMD_RESET_COUNT = 0x188, /* Reset Count */ + CMD_MONTR_DATA_SEL = 0x18C, /* Monitor Data/Select */ + CMD_PLL_PHY_CONFIG = 0x190, /* PLL/PHY Configuration */ + CMD_PHY_CTL = 0x194, /* PHY Control and Status */ + CMD_PHY_TEST_COUNT0 = 0x198, /* Phy Test Count 0 */ + CMD_PHY_TEST_COUNT1 = 0x19C, /* Phy Test Count 1 */ + CMD_PHY_TEST_COUNT2 = 0x1A0, /* Phy Test Count 2 */ + CMD_APP_ERR_CONFIG = 0x1A4, /* Application Error Configuration */ + CMD_PND_FIFO_CTL0 = 0x1A8, /* Pending FIFO Control 0 */ + CMD_HOST_CTL = 0x1AC, /* Host Control Status */ + CMD_HOST_WR_DATA = 0x1B0, /* Host Write Data */ + CMD_HOST_RD_DATA = 0x1B4, /* Host Read Data */ + CMD_PHY_MODE_21 = 0x1B8, /* Phy Mode 21 */ + CMD_SL_MODE0 = 0x1BC, /* SL Mode 0 */ + CMD_SL_MODE1 = 0x1C0, /* SL Mode 1 */ + CMD_PND_FIFO_CTL1 = 0x1C4, /* Pending FIFO Control 1 */ +}; + +/* SAS/SATA configuration port registers, aka phy registers */ +enum sas_sata_config_port_regs { + PHYR_IDENTIFY = 0x00, /* info for IDENTIFY frame */ + PHYR_ADDR_LO = 0x04, /* my SAS address (low) */ + PHYR_ADDR_HI = 0x08, /* my SAS address (high) */ + PHYR_ATT_DEV_INFO = 0x0C, /* attached device info */ + PHYR_ATT_ADDR_LO = 0x10, /* attached dev SAS addr (low) */ + PHYR_ATT_ADDR_HI = 0x14, /* attached dev SAS addr (high) */ + PHYR_SATA_CTL = 0x18, /* SATA control */ + PHYR_PHY_STAT = 0x1C, /* PHY status */ + PHYR_SATA_SIG0 = 0x20, /*port SATA signature FIS(Byte 0-3) */ + PHYR_SATA_SIG1 = 0x24, /*port SATA signature FIS(Byte 4-7) */ + PHYR_SATA_SIG2 = 0x28, /*port SATA signature FIS(Byte 8-11) */ + PHYR_SATA_SIG3 = 0x2c, /*port SATA signature FIS(Byte 12-15) */ + PHYR_R_ERR_COUNT = 0x30, /* port R_ERR count register */ + PHYR_CRC_ERR_COUNT = 0x34, /* port CRC error count register */ + PHYR_WIDE_PORT = 0x38, /* wide port participating */ + PHYR_CURRENT0 = 0x80, /* current connection info 0 */ + PHYR_CURRENT1 = 0x84, /* current connection info 1 */ + PHYR_CURRENT2 = 0x88, /* current connection info 2 */ +}; + +/* SAS/SATA Vendor Specific Port Registers */ +enum sas_sata_vsp_regs { + VSR_PHY_STAT = 0x00, /* Phy Status */ + VSR_PHY_MODE1 = 0x01, /* phy tx */ + VSR_PHY_MODE2 = 0x02, /* tx scc */ + VSR_PHY_MODE3 = 0x03, /* pll */ + VSR_PHY_MODE4 = 0x04, /* VCO */ + VSR_PHY_MODE5 = 0x05, /* Rx */ + VSR_PHY_MODE6 = 0x06, /* CDR */ + VSR_PHY_MODE7 = 0x07, /* Impedance */ + VSR_PHY_MODE8 = 0x08, /* Voltage */ + VSR_PHY_MODE9 = 0x09, /* Test */ + VSR_PHY_MODE10 = 0x0A, /* Power */ + VSR_PHY_MODE11 = 0x0B, /* Phy Mode */ + VSR_PHY_VS0 = 0x0C, /* Vednor Specific 0 */ + VSR_PHY_VS1 = 0x0D, /* Vednor Specific 1 */ +}; + +enum pci_cfg_registers { + PCR_PHY_CTL = 0x40, + PCR_PHY_CTL2 = 0x90, + PCR_DEV_CTRL = 0xE8, +}; + +enum pci_cfg_register_bits { + PCTL_PWR_ON = (0xFU << 24), + PCTL_OFF = (0xFU << 12), + PRD_REQ_SIZE = (0x4000), + PRD_REQ_MASK = (0x00007000), +}; + +enum nvram_layout_offsets { + NVR_SIG = 0x00, /* 0xAA, 0x55 */ + NVR_SAS_ADDR = 0x02, /* 8-byte SAS address */ +}; + +enum chip_flavors { + chip_6320, + chip_6440, + chip_6480, +}; + +enum port_type { + PORT_TYPE_SAS = (1L << 1), + PORT_TYPE_SATA = (1L << 0), +}; + +/* Command Table Format */ +enum ct_format { + /* SSP */ + SSP_F_H = 0x00, + SSP_F_IU = 0x18, + SSP_F_MAX = 0x4D, + /* STP */ + STP_CMD_FIS = 0x00, + STP_ATAPI_CMD = 0x40, + STP_F_MAX = 0x10, + /* SMP */ + SMP_F_T = 0x00, + SMP_F_DEP = 0x01, + SMP_F_MAX = 0x101, +}; + +enum status_buffer { + SB_EIR_OFF = 0x00, /* Error Information Record */ + SB_RFB_OFF = 0x08, /* Response Frame Buffer */ + SB_RFB_MAX = 0x400, /* RFB size*/ +}; + +enum error_info_rec { + CMD_ISS_STPD = (1U << 31), /* Cmd Issue Stopped */ +}; + +struct mvs_chip_info { + u32 n_phy; + u32 srs_sz; + u32 slot_width; +}; + +struct mvs_err_info { + __le32 flags; + __le32 flags2; +}; + +struct mvs_prd { + __le64 addr; /* 64-bit buffer address */ + __le32 reserved; + __le32 len; /* 16-bit length */ +}; + +struct mvs_cmd_hdr { + __le32 flags; /* PRD tbl len; SAS, SATA ctl */ + __le32 lens; /* cmd, max resp frame len */ + __le32 tags; /* targ port xfer tag; tag */ + __le32 data_len; /* data xfer len */ + __le64 cmd_tbl; /* command table address */ + __le64 open_frame; /* open addr frame address */ + __le64 status_buf; /* status buffer address */ + __le64 prd_tbl; /* PRD tbl address */ + __le32 reserved[4]; +}; + +struct mvs_slot_info { + struct sas_task *task; + u32 n_elem; + u32 tx; + + /* DMA buffer for storing cmd tbl, open addr frame, status buffer, + * and PRD table + */ + void *buf; + dma_addr_t buf_dma; +#if _MV_DUMP + u32 cmd_size; +#endif + + void *response; +}; + +struct mvs_port { + struct asd_sas_port sas_port; + u8 port_attached; + u8 taskfileset; + u8 wide_port_phymap; +}; + +struct mvs_phy { + struct mvs_port *port; + struct asd_sas_phy sas_phy; + struct sas_identify identify; + struct scsi_device *sdev; + u64 dev_sas_addr; + u64 att_dev_sas_addr; + u32 att_dev_info; + u32 dev_info; + u32 phy_type; + u32 phy_status; + u32 irq_status; + u32 frame_rcvd_size; + u8 frame_rcvd[32]; + u8 phy_attached; +}; + +struct mvs_info { + unsigned long flags; + + spinlock_t lock; /* host-wide lock */ + struct pci_dev *pdev; /* our device */ + void __iomem *regs; /* enhanced mode registers */ + void __iomem *peri_regs; /* peripheral registers */ + + u8 sas_addr[SAS_ADDR_SIZE]; + struct sas_ha_struct sas; /* SCSI/SAS glue */ + struct Scsi_Host *shost; + + __le32 *tx; /* TX (delivery) DMA ring */ + dma_addr_t tx_dma; + u32 tx_prod; /* cached next-producer idx */ + + __le32 *rx; /* RX (completion) DMA ring */ + dma_addr_t rx_dma; + u32 rx_cons; /* RX consumer idx */ + + __le32 *rx_fis; /* RX'd FIS area */ + dma_addr_t rx_fis_dma; + + struct mvs_cmd_hdr *slot; /* DMA command header slots */ + dma_addr_t slot_dma; + + const struct mvs_chip_info *chip; + + unsigned long tags[MVS_SLOTS]; + struct mvs_slot_info slot_info[MVS_SLOTS]; + /* further per-slot information */ + struct mvs_phy phy[MVS_MAX_PHYS]; + struct mvs_port port[MVS_MAX_PHYS]; + + u32 can_queue; /* per adapter */ + u32 tag_out; /*Get*/ + u32 tag_in; /*Give*/ +}; + +struct mvs_queue_task { + struct list_head list; + + void *uldd_task; +}; + +static int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, + void *funcdata); +static u32 mvs_read_phy_ctl(struct mvs_info *mvi, u32 port); +static void mvs_write_phy_ctl(struct mvs_info *mvi, u32 port, u32 val); +static u32 mvs_read_port_irq_stat(struct mvs_info *mvi, u32 port); +static void mvs_write_port_irq_stat(struct mvs_info *mvi, u32 port, u32 val); +static void mvs_write_port_irq_mask(struct mvs_info *mvi, u32 port, u32 val); +static u32 mvs_read_port_irq_mask(struct mvs_info *mvi, u32 port); + +static u32 mvs_is_phy_ready(struct mvs_info *mvi, int i); +static void mvs_detect_porttype(struct mvs_info *mvi, int i); +static void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st); + +static int mvs_scan_finished(struct Scsi_Host *, unsigned long); +static void mvs_scan_start(struct Scsi_Host *); +static int mvs_sas_slave_alloc(struct scsi_device *scsi_dev); + +static struct scsi_transport_template *mvs_stt; + +static const struct mvs_chip_info mvs_chips[] = { + [chip_6320] = { 2, 16, 9 }, + [chip_6440] = { 4, 16, 9 }, + [chip_6480] = { 8, 32, 10 }, +}; + +static struct scsi_host_template mvs_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .queuecommand = sas_queuecommand, + .target_alloc = sas_target_alloc, + .slave_configure = sas_slave_configure, + .slave_destroy = sas_slave_destroy, + .scan_finished = mvs_scan_finished, + .scan_start = mvs_scan_start, + .change_queue_depth = sas_change_queue_depth, + .change_queue_type = sas_change_queue_type, + .bios_param = sas_bios_param, + .can_queue = 1, + .cmd_per_lun = 1, + .this_id = -1, + .sg_tablesize = SG_ALL, + .max_sectors = SCSI_DEFAULT_MAX_SECTORS, + .use_clustering = ENABLE_CLUSTERING, + .eh_device_reset_handler = sas_eh_device_reset_handler, + .eh_bus_reset_handler = sas_eh_bus_reset_handler, + .slave_alloc = mvs_sas_slave_alloc, + .target_destroy = sas_target_destroy, + .ioctl = sas_ioctl, +}; + +static void mvs_hexdump(u32 size, u8 *data, u32 baseaddr) +{ + u32 i; + u32 run; + u32 offset; + + offset = 0; + while (size) { + printk("%08X : ", baseaddr + offset); + if (size >= 16) + run = 16; + else + run = size; + size -= run; + for (i = 0; i < 16; i++) { + if (i < run) + printk("%02X ", (u32)data[i]); + else + printk(" "); + } + printk(": "); + for (i = 0; i < run; i++) + printk("%c", isalnum(data[i]) ? data[i] : '.'); + printk("\n"); + data = &data[16]; + offset += run; + } + printk("\n"); +} + +static void mvs_hba_sb_dump(struct mvs_info *mvi, u32 tag, + enum sas_protocol proto) +{ +#if _MV_DUMP + u32 offset; + struct pci_dev *pdev = mvi->pdev; + struct mvs_slot_info *slot = &mvi->slot_info[tag]; + + offset = slot->cmd_size + MVS_OAF_SZ + + sizeof(struct mvs_prd) * slot->n_elem; + dev_printk(KERN_DEBUG, &pdev->dev, "+---->Status buffer[%d] :\n", + tag); + mvs_hexdump(32, (u8 *) slot->response, + (u32) slot->buf_dma + offset); +#endif +} + +static void mvs_hba_memory_dump(struct mvs_info *mvi, u32 tag, + enum sas_protocol proto) +{ +#if _MV_DUMP + u32 sz, w_ptr, r_ptr; + u64 addr; + void __iomem *regs = mvi->regs; + struct pci_dev *pdev = mvi->pdev; + struct mvs_slot_info *slot = &mvi->slot_info[tag]; + + /*Delivery Queue */ + sz = mr32(TX_CFG) & TX_RING_SZ_MASK; + w_ptr = mr32(TX_PROD_IDX) & TX_RING_SZ_MASK; + r_ptr = mr32(TX_CONS_IDX) & TX_RING_SZ_MASK; + addr = mr32(TX_HI) << 16 << 16 | mr32(TX_LO); + dev_printk(KERN_DEBUG, &pdev->dev, + "Delivery Queue Size=%04d , WRT_PTR=%04X , RD_PTR=%04X\n", + sz, w_ptr, r_ptr); + dev_printk(KERN_DEBUG, &pdev->dev, + "Delivery Queue Base Address=0x%llX (PA)" + "(tx_dma=0x%llX), Entry=%04d\n", + addr, mvi->tx_dma, w_ptr); + mvs_hexdump(sizeof(u32), (u8 *)(&mvi->tx[mvi->tx_prod]), + (u32) mvi->tx_dma + sizeof(u32) * w_ptr); + /*Command List */ + addr = mr32(CMD_LIST_HI) << 16 << 16 | mr32(CMD_LIST_LO); + dev_printk(KERN_DEBUG, &pdev->dev, + "Command List Base Address=0x%llX (PA)" + "(slot_dma=0x%llX), Header=%03d\n", + addr, mvi->slot_dma, tag); + dev_printk(KERN_DEBUG, &pdev->dev, "Command Header[%03d]:\n", tag); + /*mvs_cmd_hdr */ + mvs_hexdump(sizeof(struct mvs_cmd_hdr), (u8 *)(&mvi->slot[tag]), + (u32) mvi->slot_dma + tag * sizeof(struct mvs_cmd_hdr)); + /*1.command table area */ + dev_printk(KERN_DEBUG, &pdev->dev, "+---->Command Table :\n"); + mvs_hexdump(slot->cmd_size, (u8 *) slot->buf, (u32) slot->buf_dma); + /*2.open address frame area */ + dev_printk(KERN_DEBUG, &pdev->dev, "+---->Open Address Frame :\n"); + mvs_hexdump(MVS_OAF_SZ, (u8 *) slot->buf + slot->cmd_size, + (u32) slot->buf_dma + slot->cmd_size); + /*3.status buffer */ + mvs_hba_sb_dump(mvi, tag, proto); + /*4.PRD table */ + dev_printk(KERN_DEBUG, &pdev->dev, "+---->PRD table :\n"); + mvs_hexdump(sizeof(struct mvs_prd) * slot->n_elem, + (u8 *) slot->buf + slot->cmd_size + MVS_OAF_SZ, + (u32) slot->buf_dma + slot->cmd_size + MVS_OAF_SZ); +#endif +} + +static void mvs_hba_cq_dump(struct mvs_info *mvi) +{ +#if _MV_DUMP + u64 addr; + void __iomem *regs = mvi->regs; + struct pci_dev *pdev = mvi->pdev; + u32 entry = mvi->rx_cons + 1; + u32 rx_desc = le32_to_cpu(mvi->rx[entry]); + + /*Completion Queue */ + addr = mr32(RX_HI) << 16 << 16 | mr32(RX_LO); + dev_printk(KERN_DEBUG, &pdev->dev, "Completion Task = 0x%08X\n", + (u32) mvi->slot_info[rx_desc & RXQ_SLOT_MASK].task); + dev_printk(KERN_DEBUG, &pdev->dev, + "Completion List Base Address=0x%llX (PA), " + "CQ_Entry=%04d, CQ_WP=0x%08X\n", + addr, entry - 1, mvi->rx[0]); + mvs_hexdump(sizeof(u32), (u8 *)(&rx_desc), + mvi->rx_dma + sizeof(u32) * entry); +#endif +} + +static void mvs_hba_interrupt_enable(struct mvs_info *mvi) +{ + void __iomem *regs = mvi->regs; + u32 tmp; + + tmp = mr32(GBL_CTL); + + mw32(GBL_CTL, tmp | INT_EN); +} + +static void mvs_hba_interrupt_disable(struct mvs_info *mvi) +{ + void __iomem *regs = mvi->regs; + u32 tmp; + + tmp = mr32(GBL_CTL); + + mw32(GBL_CTL, tmp & ~INT_EN); +} + +static int mvs_int_rx(struct mvs_info *mvi, bool self_clear); + +/* move to PCI layer or libata core? */ +static int pci_go_64(struct pci_dev *pdev) +{ + int rc; + + if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { + rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); + if (rc) { + rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "64-bit DMA enable failed\n"); + return rc; + } + } + } else { + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit DMA enable failed\n"); + return rc; + } + rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit consistent DMA enable failed\n"); + return rc; + } + } + + return rc; +} + +static void mvs_tag_clear(struct mvs_info *mvi, u32 tag) +{ + mvi->tag_in = (mvi->tag_in + 1) & (MVS_SLOTS - 1); + mvi->tags[mvi->tag_in] = tag; +} + +static void mvs_tag_free(struct mvs_info *mvi, u32 tag) +{ + mvi->tag_out = (mvi->tag_out - 1) & (MVS_SLOTS - 1); +} + +static int mvs_tag_alloc(struct mvs_info *mvi, u32 *tag_out) +{ + if (mvi->tag_out != mvi->tag_in) { + *tag_out = mvi->tags[mvi->tag_out]; + mvi->tag_out = (mvi->tag_out + 1) & (MVS_SLOTS - 1); + return 0; + } + return -EBUSY; +} + +static void mvs_tag_init(struct mvs_info *mvi) +{ + int i; + for (i = 0; i < MVS_SLOTS; ++i) + mvi->tags[i] = i; + mvi->tag_out = 0; + mvi->tag_in = MVS_SLOTS - 1; +} + +#ifndef MVS_DISABLE_NVRAM +static int mvs_eep_read(void __iomem *regs, u32 addr, u32 *data) +{ + int timeout = 1000; + + if (addr & ~SPI_ADDR_MASK) + return -EINVAL; + + writel(addr, regs + SPI_CMD); + writel(TWSI_RD, regs + SPI_CTL); + + while (timeout-- > 0) { + if (readl(regs + SPI_CTL) & TWSI_RDY) { + *data = readl(regs + SPI_DATA); + return 0; + } + + udelay(10); + } + + return -EBUSY; +} + +static int mvs_eep_read_buf(void __iomem *regs, u32 addr, + void *buf, u32 buflen) +{ + u32 addr_end, tmp_addr, i, j; + u32 tmp = 0; + int rc; + u8 *tmp8, *buf8 = buf; + + addr_end = addr + buflen; + tmp_addr = ALIGN(addr, 4); + if (addr > 0xff) + return -EINVAL; + + j = addr & 0x3; + if (j) { + rc = mvs_eep_read(regs, tmp_addr, &tmp); + if (rc) + return rc; + + tmp8 = (u8 *)&tmp; + for (i = j; i < 4; i++) + *buf8++ = tmp8[i]; + + tmp_addr += 4; + } + + for (j = ALIGN(addr_end, 4); tmp_addr < j; tmp_addr += 4) { + rc = mvs_eep_read(regs, tmp_addr, &tmp); + if (rc) + return rc; + + memcpy(buf8, &tmp, 4); + buf8 += 4; + } + + if (tmp_addr < addr_end) { + rc = mvs_eep_read(regs, tmp_addr, &tmp); + if (rc) + return rc; + + tmp8 = (u8 *)&tmp; + j = addr_end - tmp_addr; + for (i = 0; i < j; i++) + *buf8++ = tmp8[i]; + + tmp_addr += 4; + } + + return 0; +} +#endif + +static int mvs_nvram_read(struct mvs_info *mvi, u32 addr, + void *buf, u32 buflen) +{ +#ifndef MVS_DISABLE_NVRAM + void __iomem *regs = mvi->regs; + int rc, i; + u32 sum; + u8 hdr[2], *tmp; + const char *msg; + + rc = mvs_eep_read_buf(regs, addr, &hdr, 2); + if (rc) { + msg = "nvram hdr read failed"; + goto err_out; + } + rc = mvs_eep_read_buf(regs, addr + 2, buf, buflen); + if (rc) { + msg = "nvram read failed"; + goto err_out; + } + + if (hdr[0] != 0x5A) { + /* entry id */ + msg = "invalid nvram entry id"; + rc = -ENOENT; + goto err_out; + } + + tmp = buf; + sum = ((u32)hdr[0]) + ((u32)hdr[1]); + for (i = 0; i < buflen; i++) + sum += ((u32)tmp[i]); + + if (sum) { + msg = "nvram checksum failure"; + rc = -EILSEQ; + goto err_out; + } + + return 0; + +err_out: + dev_printk(KERN_ERR, &mvi->pdev->dev, "%s", msg); + return rc; +#else + /* FIXME , For SAS target mode */ + memcpy(buf, "\x00\x00\xab\x11\x30\x04\x05\x50", 8); + return 0; +#endif +} + +static void mvs_bytes_dmaed(struct mvs_info *mvi, int i) +{ + struct mvs_phy *phy = &mvi->phy[i]; + + if (!phy->phy_attached) + return; + + if (phy->phy_type & PORT_TYPE_SAS) { + struct sas_identify_frame *id; + + id = (struct sas_identify_frame *)phy->frame_rcvd; + id->dev_type = phy->identify.device_type; + id->initiator_bits = SAS_PROTOCOL_ALL; + id->target_bits = phy->identify.target_port_protocols; + } else if (phy->phy_type & PORT_TYPE_SATA) { + /* TODO */ + } + mvi->sas.sas_phy[i]->frame_rcvd_size = phy->frame_rcvd_size; + mvi->sas.notify_port_event(mvi->sas.sas_phy[i], + PORTE_BYTES_DMAED); +} + +static int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time) +{ + /* give the phy enabling interrupt event time to come in (1s + * is empirically about all it takes) */ + if (time < HZ) + return 0; + /* Wait for discovery to finish */ + scsi_flush_work(shost); + return 1; +} + +static void mvs_scan_start(struct Scsi_Host *shost) +{ + int i; + struct mvs_info *mvi = SHOST_TO_SAS_HA(shost)->lldd_ha; + + for (i = 0; i < mvi->chip->n_phy; ++i) { + mvs_bytes_dmaed(mvi, i); + } +} + +static int mvs_sas_slave_alloc(struct scsi_device *scsi_dev) +{ + int rc; + + rc = sas_slave_alloc(scsi_dev); + + return rc; +} + +static void mvs_int_port(struct mvs_info *mvi, int port_no, u32 events) +{ + struct pci_dev *pdev = mvi->pdev; + struct sas_ha_struct *sas_ha = &mvi->sas; + struct mvs_phy *phy = &mvi->phy[port_no]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + + phy->irq_status = mvs_read_port_irq_stat(mvi, port_no); + /* + * events is port event now , + * we need check the interrupt status which belongs to per port. + */ + dev_printk(KERN_DEBUG, &pdev->dev, + "Port %d Event = %X\n", + port_no, phy->irq_status); + + if (phy->irq_status & (PHYEV_POOF | PHYEV_DEC_ERR)) { + if (!mvs_is_phy_ready(mvi, port_no)) { + sas_phy_disconnected(sas_phy); + sas_ha->notify_phy_event(sas_phy, PHYE_LOSS_OF_SIGNAL); + } else + mvs_phy_control(sas_phy, PHY_FUNC_LINK_RESET, NULL); + } + if (!(phy->irq_status & PHYEV_DEC_ERR)) { + if (phy->irq_status & PHYEV_COMWAKE) { + u32 tmp = mvs_read_port_irq_mask(mvi, port_no); + mvs_write_port_irq_mask(mvi, port_no, + tmp | PHYEV_SIG_FIS); + } + if (phy->irq_status & (PHYEV_SIG_FIS | PHYEV_ID_DONE)) { + phy->phy_status = mvs_is_phy_ready(mvi, port_no); + if (phy->phy_status) { + mvs_detect_porttype(mvi, port_no); + + if (phy->phy_type & PORT_TYPE_SATA) { + u32 tmp = mvs_read_port_irq_mask(mvi, + port_no); + tmp &= ~PHYEV_SIG_FIS; + mvs_write_port_irq_mask(mvi, + port_no, tmp); + } + + mvs_update_phyinfo(mvi, port_no, 0); + sas_ha->notify_phy_event(sas_phy, + PHYE_OOB_DONE); + mvs_bytes_dmaed(mvi, port_no); + } else { + dev_printk(KERN_DEBUG, &pdev->dev, + "plugin interrupt but phy is gone\n"); + mvs_phy_control(sas_phy, PHY_FUNC_LINK_RESET, + NULL); + } + } else if (phy->irq_status & PHYEV_BROAD_CH) + sas_ha->notify_port_event(sas_phy, + PORTE_BROADCAST_RCVD); + } + mvs_write_port_irq_stat(mvi, port_no, phy->irq_status); +} + +static void mvs_int_sata(struct mvs_info *mvi) +{ + /* FIXME */ +} + +static void mvs_slot_free(struct mvs_info *mvi, struct sas_task *task, + struct mvs_slot_info *slot, u32 slot_idx) +{ + if (!sas_protocol_ata(task->task_proto)) + if (slot->n_elem) + pci_unmap_sg(mvi->pdev, task->scatter, + slot->n_elem, task->data_dir); + + switch (task->task_proto) { + case SAS_PROTOCOL_SMP: + pci_unmap_sg(mvi->pdev, &task->smp_task.smp_resp, 1, + PCI_DMA_FROMDEVICE); + pci_unmap_sg(mvi->pdev, &task->smp_task.smp_req, 1, + PCI_DMA_TODEVICE); + break; + + case SAS_PROTOCOL_SATA: + case SAS_PROTOCOL_STP: + case SAS_PROTOCOL_SSP: + default: + /* do nothing */ + break; + } + + slot->task = NULL; + mvs_tag_clear(mvi, slot_idx); +} + +static void mvs_slot_err(struct mvs_info *mvi, struct sas_task *task, + u32 slot_idx) +{ + struct mvs_slot_info *slot = &mvi->slot_info[slot_idx]; + u64 err_dw0 = *(u32 *) slot->response; + void __iomem *regs = mvi->regs; + u32 tmp; + + if (err_dw0 & CMD_ISS_STPD) + if (sas_protocol_ata(task->task_proto)) { + tmp = mr32(INT_STAT_SRS); + mw32(INT_STAT_SRS, tmp & 0xFFFF); + } + + mvs_hba_sb_dump(mvi, slot_idx, task->task_proto); +} + +static int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc) +{ + u32 slot_idx = rx_desc & RXQ_SLOT_MASK; + struct mvs_slot_info *slot = &mvi->slot_info[slot_idx]; + struct sas_task *task = slot->task; + struct task_status_struct *tstat = &task->task_status; + struct mvs_port *port = &mvi->port[task->dev->port->id]; + bool aborted; + void *to; + + spin_lock(&task->task_state_lock); + aborted = task->task_state_flags & SAS_TASK_STATE_ABORTED; + if (!aborted) { + task->task_state_flags &= + ~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR); + task->task_state_flags |= SAS_TASK_STATE_DONE; + } + spin_unlock(&task->task_state_lock); + + if (aborted) + return -1; + + memset(tstat, 0, sizeof(*tstat)); + tstat->resp = SAS_TASK_COMPLETE; + + + if (unlikely(!port->port_attached)) { + tstat->stat = SAS_PHY_DOWN; + goto out; + } + + /* error info record present */ + if ((rx_desc & RXQ_ERR) && (*(u64 *) slot->response)) { + tstat->stat = SAM_CHECK_COND; + mvs_slot_err(mvi, task, slot_idx); + goto out; + } + + switch (task->task_proto) { + case SAS_PROTOCOL_SSP: + /* hw says status == 0, datapres == 0 */ + if (rx_desc & RXQ_GOOD) { + tstat->stat = SAM_GOOD; + tstat->resp = SAS_TASK_COMPLETE; + } + /* response frame present */ + else if (rx_desc & RXQ_RSP) { + struct ssp_response_iu *iu = + slot->response + sizeof(struct mvs_err_info); + sas_ssp_task_response(&mvi->pdev->dev, task, iu); + } + + /* should never happen? */ + else + tstat->stat = SAM_CHECK_COND; + break; + + case SAS_PROTOCOL_SMP: { + struct scatterlist *sg_resp = &task->smp_task.smp_resp; + tstat->stat = SAM_GOOD; + to = kmap_atomic(sg_page(sg_resp), KM_IRQ0); + memcpy(to + sg_resp->offset, + slot->response + sizeof(struct mvs_err_info), + sg_dma_len(sg_resp)); + kunmap_atomic(to, KM_IRQ0); + break; + } + + case SAS_PROTOCOL_SATA: + case SAS_PROTOCOL_STP: + case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: { + struct ata_task_resp *resp = + (struct ata_task_resp *)tstat->buf; + + if ((rx_desc & (RXQ_DONE | RXQ_ERR | RXQ_ATTN)) == + RXQ_DONE) + tstat->stat = SAM_GOOD; + else + tstat->stat = SAM_CHECK_COND; + + resp->frame_len = sizeof(struct dev_to_host_fis); + memcpy(&resp->ending_fis[0], + SATA_RECEIVED_D2H_FIS(port->taskfileset), + sizeof(struct dev_to_host_fis)); + if (resp->ending_fis[2] & ATA_ERR) + mvs_hexdump(16, resp->ending_fis, 0); + break; + } + + default: + tstat->stat = SAM_CHECK_COND; + break; + } + +out: + mvs_slot_free(mvi, task, slot, slot_idx); + task->task_done(task); + return tstat->stat; +} + +static void mvs_int_full(struct mvs_info *mvi) +{ + void __iomem *regs = mvi->regs; + u32 tmp, stat; + int i; + + stat = mr32(INT_STAT); + + mvs_int_rx(mvi, false); + + for (i = 0; i < MVS_MAX_PORTS; i++) { + tmp = (stat >> i) & (CINT_PORT | CINT_PORT_STOPPED); + if (tmp) + mvs_int_port(mvi, i, tmp); + } + + if (stat & CINT_SRS) + mvs_int_sata(mvi); + + mw32(INT_STAT, stat); +} + +static int mvs_int_rx(struct mvs_info *mvi, bool self_clear) +{ + void __iomem *regs = mvi->regs; + u32 rx_prod_idx, rx_desc; + bool attn = false; + struct pci_dev *pdev = mvi->pdev; + + /* the first dword in the RX ring is special: it contains + * a mirror of the hardware's RX producer index, so that + * we don't have to stall the CPU reading that register. + * The actual RX ring is offset by one dword, due to this. + */ + rx_prod_idx = mr32(RX_CONS_IDX) & RX_RING_SZ_MASK; + if (rx_prod_idx == 0xfff) { /* h/w hasn't touched RX ring yet */ + mvi->rx_cons = 0xfff; + return 0; + } + + /* The CMPL_Q may come late, read from register and try again + * note: if coalescing is enabled, + * it will need to read from register every time for sure + */ + if (mvi->rx_cons == rx_prod_idx) + return 0; + + if (mvi->rx_cons == 0xfff) + mvi->rx_cons = MVS_RX_RING_SZ - 1; + + while (mvi->rx_cons != rx_prod_idx) { + + /* increment our internal RX consumer pointer */ + mvi->rx_cons = (mvi->rx_cons + 1) & (MVS_RX_RING_SZ - 1); + + rx_desc = le32_to_cpu(mvi->rx[mvi->rx_cons + 1]); + + mvs_hba_cq_dump(mvi); + + if (unlikely(rx_desc & RXQ_DONE)) + mvs_slot_complete(mvi, rx_desc); + if (rx_desc & RXQ_ATTN) { + attn = true; + dev_printk(KERN_DEBUG, &pdev->dev, "ATTN %X\n", + rx_desc); + } else if (rx_desc & RXQ_ERR) { + dev_printk(KERN_DEBUG, &pdev->dev, "RXQ_ERR %X\n", + rx_desc); + } + } + + if (attn && self_clear) + mvs_int_full(mvi); + + return 0; +} + +static irqreturn_t mvs_interrupt(int irq, void *opaque) +{ + struct mvs_info *mvi = opaque; + void __iomem *regs = mvi->regs; + u32 stat; + + stat = mr32(GBL_INT_STAT); + + /* clear CMD_CMPLT ASAP */ + mw32_f(INT_STAT, CINT_DONE); + + if (stat == 0 || stat == 0xffffffff) + return IRQ_NONE; + + spin_lock(&mvi->lock); + + mvs_int_full(mvi); + + spin_unlock(&mvi->lock); + + return IRQ_HANDLED; +} + +#ifndef MVS_DISABLE_MSI +static irqreturn_t mvs_msi_interrupt(int irq, void *opaque) +{ + struct mvs_info *mvi = opaque; + + spin_lock(&mvi->lock); + + mvs_int_rx(mvi, true); + + spin_unlock(&mvi->lock); + + return IRQ_HANDLED; +} +#endif + +struct mvs_task_exec_info { + struct sas_task *task; + struct mvs_cmd_hdr *hdr; + struct mvs_port *port; + u32 tag; + int n_elem; +}; + +static int mvs_task_prep_smp(struct mvs_info *mvi, + struct mvs_task_exec_info *tei) +{ + int elem, rc, i; + struct sas_task *task = tei->task; + struct mvs_cmd_hdr *hdr = tei->hdr; + struct scatterlist *sg_req, *sg_resp; + u32 req_len, resp_len, tag = tei->tag; + void *buf_tmp; + u8 *buf_oaf; + dma_addr_t buf_tmp_dma; + struct mvs_prd *buf_prd; + struct scatterlist *sg; + struct mvs_slot_info *slot = &mvi->slot_info[tag]; + struct asd_sas_port *sas_port = task->dev->port; + u32 flags = (tei->n_elem << MCH_PRD_LEN_SHIFT); +#if _MV_DUMP + u8 *buf_cmd; + void *from; +#endif + /* + * DMA-map SMP request, response buffers + */ + sg_req = &task->smp_task.smp_req; + elem = pci_map_sg(mvi->pdev, sg_req, 1, PCI_DMA_TODEVICE); + if (!elem) + return -ENOMEM; + req_len = sg_dma_len(sg_req); + + sg_resp = &task->smp_task.smp_resp; + elem = pci_map_sg(mvi->pdev, sg_resp, 1, PCI_DMA_FROMDEVICE); + if (!elem) { + rc = -ENOMEM; + goto err_out; + } + resp_len = sg_dma_len(sg_resp); + + /* must be in dwords */ + if ((req_len & 0x3) || (resp_len & 0x3)) { + rc = -EINVAL; + goto err_out_2; + } + + /* + * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs + */ + + /* region 1: command table area (MVS_SSP_CMD_SZ bytes) ************** */ + buf_tmp = slot->buf; + buf_tmp_dma = slot->buf_dma; + +#if _MV_DUMP + buf_cmd = buf_tmp; + hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma); + buf_tmp += req_len; + buf_tmp_dma += req_len; + slot->cmd_size = req_len; +#else + hdr->cmd_tbl = cpu_to_le64(sg_dma_address(sg_req)); +#endif + + /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */ + buf_oaf = buf_tmp; + hdr->open_frame = cpu_to_le64(buf_tmp_dma); + + buf_tmp += MVS_OAF_SZ; + buf_tmp_dma += MVS_OAF_SZ; + + /* region 3: PRD table ********************************************* */ + buf_prd = buf_tmp; + if (tei->n_elem) + hdr->prd_tbl = cpu_to_le64(buf_tmp_dma); + else + hdr->prd_tbl = 0; + + i = sizeof(struct mvs_prd) * tei->n_elem; + buf_tmp += i; + buf_tmp_dma += i; + + /* region 4: status buffer (larger the PRD, smaller this buf) ****** */ + slot->response = buf_tmp; + hdr->status_buf = cpu_to_le64(buf_tmp_dma); + + /* + * Fill in TX ring and command slot header + */ + slot->tx = mvi->tx_prod; + mvi->tx[mvi->tx_prod] = cpu_to_le32((TXQ_CMD_SMP << TXQ_CMD_SHIFT) | + TXQ_MODE_I | tag | + (sas_port->phy_mask << TXQ_PHY_SHIFT)); + + hdr->flags |= flags; + hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | ((req_len - 4) / 4)); + hdr->tags = cpu_to_le32(tag); + hdr->data_len = 0; + + /* generate open address frame hdr (first 12 bytes) */ + buf_oaf[0] = (1 << 7) | (0 << 4) | 0x01; /* initiator, SMP, ftype 1h */ + buf_oaf[1] = task->dev->linkrate & 0xf; + *(u16 *)(buf_oaf + 2) = 0xFFFF; /* SAS SPEC */ + memcpy(buf_oaf + 4, task->dev->sas_addr, SAS_ADDR_SIZE); + + /* fill in PRD (scatter/gather) table, if any */ + for_each_sg(task->scatter, sg, tei->n_elem, i) { + buf_prd->addr = cpu_to_le64(sg_dma_address(sg)); + buf_prd->len = cpu_to_le32(sg_dma_len(sg)); + buf_prd++; + } + +#if _MV_DUMP + /* copy cmd table */ + from = kmap_atomic(sg_page(sg_req), KM_IRQ0); + memcpy(buf_cmd, from + sg_req->offset, req_len); + kunmap_atomic(from, KM_IRQ0); +#endif + return 0; + +err_out_2: + pci_unmap_sg(mvi->pdev, &tei->task->smp_task.smp_resp, 1, + PCI_DMA_FROMDEVICE); +err_out: + pci_unmap_sg(mvi->pdev, &tei->task->smp_task.smp_req, 1, + PCI_DMA_TODEVICE); + return rc; +} + +static void mvs_free_reg_set(struct mvs_info *mvi, struct mvs_port *port) +{ + void __iomem *regs = mvi->regs; + u32 tmp, offs; + u8 *tfs = &port->taskfileset; + + if (*tfs == MVS_ID_NOT_MAPPED) + return; + + offs = 1U << ((*tfs & 0x0f) + PCS_EN_SATA_REG_SHIFT); + if (*tfs < 16) { + tmp = mr32(PCS); + mw32(PCS, tmp & ~offs); + } else { + tmp = mr32(CTL); + mw32(CTL, tmp & ~offs); + } + + tmp = mr32(INT_STAT_SRS) & (1U << *tfs); + if (tmp) + mw32(INT_STAT_SRS, tmp); + + *tfs = MVS_ID_NOT_MAPPED; +} + +static u8 mvs_assign_reg_set(struct mvs_info *mvi, struct mvs_port *port) +{ + int i; + u32 tmp, offs; + void __iomem *regs = mvi->regs; + + if (port->taskfileset != MVS_ID_NOT_MAPPED) + return 0; + + tmp = mr32(PCS); + + for (i = 0; i < mvi->chip->srs_sz; i++) { + if (i == 16) + tmp = mr32(CTL); + offs = 1U << ((i & 0x0f) + PCS_EN_SATA_REG_SHIFT); + if (!(tmp & offs)) { + port->taskfileset = i; + + if (i < 16) + mw32(PCS, tmp | offs); + else + mw32(CTL, tmp | offs); + tmp = mr32(INT_STAT_SRS) & (1U << i); + if (tmp) + mw32(INT_STAT_SRS, tmp); + return 0; + } + } + return MVS_ID_NOT_MAPPED; +} + +static u32 mvs_get_ncq_tag(struct sas_task *task) +{ + u32 tag = 0; + struct ata_queued_cmd *qc = task->uldd_task; + + if (qc) + tag = qc->tag; + + return tag; +} + +static int mvs_task_prep_ata(struct mvs_info *mvi, + struct mvs_task_exec_info *tei) +{ + struct sas_task *task = tei->task; + struct domain_device *dev = task->dev; + struct mvs_cmd_hdr *hdr = tei->hdr; + struct asd_sas_port *sas_port = dev->port; + struct mvs_slot_info *slot; + struct scatterlist *sg; + struct mvs_prd *buf_prd; + struct mvs_port *port = tei->port; + u32 tag = tei->tag; + u32 flags = (tei->n_elem << MCH_PRD_LEN_SHIFT); + void *buf_tmp; + u8 *buf_cmd, *buf_oaf; + dma_addr_t buf_tmp_dma; + u32 i, req_len, resp_len; + const u32 max_resp_len = SB_RFB_MAX; + + if (mvs_assign_reg_set(mvi, port) == MVS_ID_NOT_MAPPED) + return -EBUSY; + + slot = &mvi->slot_info[tag]; + slot->tx = mvi->tx_prod; + mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | tag | + (TXQ_CMD_STP << TXQ_CMD_SHIFT) | + (sas_port->phy_mask << TXQ_PHY_SHIFT) | + (port->taskfileset << TXQ_SRS_SHIFT)); + + if (task->ata_task.use_ncq) + flags |= MCH_FPDMA; + if (dev->sata_dev.command_set == ATAPI_COMMAND_SET) { + if (task->ata_task.fis.command != ATA_CMD_ID_ATAPI) + flags |= MCH_ATAPI; + } + + /* FIXME: fill in port multiplier number */ + + hdr->flags = cpu_to_le32(flags); + + /* FIXME: the low order order 5 bits for the TAG if enable NCQ */ + if (task->ata_task.use_ncq) { + hdr->tags = cpu_to_le32(mvs_get_ncq_tag(task)); + /*Fill in task file */ + task->ata_task.fis.sector_count = hdr->tags << 3; + } else + hdr->tags = cpu_to_le32(tag); + hdr->data_len = cpu_to_le32(task->total_xfer_len); + + /* + * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs + */ + + /* region 1: command table area (MVS_ATA_CMD_SZ bytes) ************** */ + buf_cmd = buf_tmp = slot->buf; + buf_tmp_dma = slot->buf_dma; + + hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma); + + buf_tmp += MVS_ATA_CMD_SZ; + buf_tmp_dma += MVS_ATA_CMD_SZ; +#if _MV_DUMP + slot->cmd_size = MVS_ATA_CMD_SZ; +#endif + + /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */ + /* used for STP. unused for SATA? */ + buf_oaf = buf_tmp; + hdr->open_frame = cpu_to_le64(buf_tmp_dma); + + buf_tmp += MVS_OAF_SZ; + buf_tmp_dma += MVS_OAF_SZ; + + /* region 3: PRD table ********************************************* */ + buf_prd = buf_tmp; + if (tei->n_elem) + hdr->prd_tbl = cpu_to_le64(buf_tmp_dma); + else + hdr->prd_tbl = 0; + + i = sizeof(struct mvs_prd) * tei->n_elem; + buf_tmp += i; + buf_tmp_dma += i; + + /* region 4: status buffer (larger the PRD, smaller this buf) ****** */ + /* FIXME: probably unused, for SATA. kept here just in case + * we get a STP/SATA error information record + */ + slot->response = buf_tmp; + hdr->status_buf = cpu_to_le64(buf_tmp_dma); + + req_len = sizeof(struct host_to_dev_fis); + resp_len = MVS_SLOT_BUF_SZ - MVS_ATA_CMD_SZ - + sizeof(struct mvs_err_info) - i; + + /* request, response lengths */ + resp_len = min(resp_len, max_resp_len); + hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | (req_len / 4)); + + task->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */ + /* fill in command FIS and ATAPI CDB */ + memcpy(buf_cmd, &task->ata_task.fis, sizeof(struct host_to_dev_fis)); + if (dev->sata_dev.command_set == ATAPI_COMMAND_SET) + memcpy(buf_cmd + STP_ATAPI_CMD, + task->ata_task.atapi_packet, 16); + + /* generate open address frame hdr (first 12 bytes) */ + buf_oaf[0] = (1 << 7) | (2 << 4) | 0x1; /* initiator, STP, ftype 1h */ + buf_oaf[1] = task->dev->linkrate & 0xf; + *(u16 *)(buf_oaf + 2) = cpu_to_be16(tag); + memcpy(buf_oaf + 4, task->dev->sas_addr, SAS_ADDR_SIZE); + + /* fill in PRD (scatter/gather) table, if any */ + for_each_sg(task->scatter, sg, tei->n_elem, i) { + buf_prd->addr = cpu_to_le64(sg_dma_address(sg)); + buf_prd->len = cpu_to_le32(sg_dma_len(sg)); + buf_prd++; + } + + return 0; +} + +static int mvs_task_prep_ssp(struct mvs_info *mvi, + struct mvs_task_exec_info *tei) +{ + struct sas_task *task = tei->task; + struct mvs_cmd_hdr *hdr = tei->hdr; + struct mvs_port *port = tei->port; + struct mvs_slot_info *slot; + struct scatterlist *sg; + struct mvs_prd *buf_prd; + struct ssp_frame_hdr *ssp_hdr; + void *buf_tmp; + u8 *buf_cmd, *buf_oaf, fburst = 0; + dma_addr_t buf_tmp_dma; + u32 flags; + u32 resp_len, req_len, i, tag = tei->tag; + const u32 max_resp_len = SB_RFB_MAX; + + slot = &mvi->slot_info[tag]; + + slot->tx = mvi->tx_prod; + mvi->tx[mvi->tx_prod] = cpu_to_le32(TXQ_MODE_I | tag | + (TXQ_CMD_SSP << TXQ_CMD_SHIFT) | + (port->wide_port_phymap << TXQ_PHY_SHIFT)); + + flags = MCH_RETRY; + if (task->ssp_task.enable_first_burst) { + flags |= MCH_FBURST; + fburst = (1 << 7); + } + hdr->flags = cpu_to_le32(flags | + (tei->n_elem << MCH_PRD_LEN_SHIFT) | + (MCH_SSP_FR_CMD << MCH_SSP_FR_TYPE_SHIFT)); + + hdr->tags = cpu_to_le32(tag); + hdr->data_len = cpu_to_le32(task->total_xfer_len); + + /* + * arrange MVS_SLOT_BUF_SZ-sized DMA buffer according to our needs + */ + + /* region 1: command table area (MVS_SSP_CMD_SZ bytes) ************** */ + buf_cmd = buf_tmp = slot->buf; + buf_tmp_dma = slot->buf_dma; + + hdr->cmd_tbl = cpu_to_le64(buf_tmp_dma); + + buf_tmp += MVS_SSP_CMD_SZ; + buf_tmp_dma += MVS_SSP_CMD_SZ; +#if _MV_DUMP + slot->cmd_size = MVS_SSP_CMD_SZ; +#endif + + /* region 2: open address frame area (MVS_OAF_SZ bytes) ********* */ + buf_oaf = buf_tmp; + hdr->open_frame = cpu_to_le64(buf_tmp_dma); + + buf_tmp += MVS_OAF_SZ; + buf_tmp_dma += MVS_OAF_SZ; + + /* region 3: PRD table ********************************************* */ + buf_prd = buf_tmp; + if (tei->n_elem) + hdr->prd_tbl = cpu_to_le64(buf_tmp_dma); + else + hdr->prd_tbl = 0; + + i = sizeof(struct mvs_prd) * tei->n_elem; + buf_tmp += i; + buf_tmp_dma += i; + + /* region 4: status buffer (larger the PRD, smaller this buf) ****** */ + slot->response = buf_tmp; + hdr->status_buf = cpu_to_le64(buf_tmp_dma); + + resp_len = MVS_SLOT_BUF_SZ - MVS_SSP_CMD_SZ - MVS_OAF_SZ - + sizeof(struct mvs_err_info) - i; + resp_len = min(resp_len, max_resp_len); + + req_len = sizeof(struct ssp_frame_hdr) + 28; + + /* request, response lengths */ + hdr->lens = cpu_to_le32(((resp_len / 4) << 16) | (req_len / 4)); + + /* generate open address frame hdr (first 12 bytes) */ + buf_oaf[0] = (1 << 7) | (1 << 4) | 0x1; /* initiator, SSP, ftype 1h */ + buf_oaf[1] = task->dev->linkrate & 0xf; + *(u16 *)(buf_oaf + 2) = cpu_to_be16(tag); + memcpy(buf_oaf + 4, task->dev->sas_addr, SAS_ADDR_SIZE); + + /* fill in SSP frame header (Command Table.SSP frame header) */ + ssp_hdr = (struct ssp_frame_hdr *)buf_cmd; + ssp_hdr->frame_type = SSP_COMMAND; + memcpy(ssp_hdr->hashed_dest_addr, task->dev->hashed_sas_addr, + HASHED_SAS_ADDR_SIZE); + memcpy(ssp_hdr->hashed_src_addr, + task->dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE); + ssp_hdr->tag = cpu_to_be16(tag); + + /* fill in command frame IU */ + buf_cmd += sizeof(*ssp_hdr); + memcpy(buf_cmd, &task->ssp_task.LUN, 8); + buf_cmd[9] = fburst | task->ssp_task.task_attr | + (task->ssp_task.task_prio << 3); + memcpy(buf_cmd + 12, &task->ssp_task.cdb, 16); + + /* fill in PRD (scatter/gather) table, if any */ + for_each_sg(task->scatter, sg, tei->n_elem, i) { + buf_prd->addr = cpu_to_le64(sg_dma_address(sg)); + buf_prd->len = cpu_to_le32(sg_dma_len(sg)); + buf_prd++; + } + + return 0; +} + +static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags) +{ + struct domain_device *dev = task->dev; + struct mvs_info *mvi = dev->port->ha->lldd_ha; + struct pci_dev *pdev = mvi->pdev; + void __iomem *regs = mvi->regs; + struct mvs_task_exec_info tei; + struct sas_task *t = task; + u32 tag = 0xdeadbeef, rc, n_elem = 0; + unsigned long flags; + u32 n = num, pass = 0; + + spin_lock_irqsave(&mvi->lock, flags); + + do { + tei.port = &mvi->port[dev->port->id]; + + if (!tei.port->port_attached) { + struct task_status_struct *ts = &t->task_status; + ts->stat = SAS_PHY_DOWN; + t->task_done(t); + rc = 0; + goto exec_exit; + } + if (!sas_protocol_ata(t->task_proto)) { + if (t->num_scatter) { + n_elem = pci_map_sg(mvi->pdev, t->scatter, + t->num_scatter, + t->data_dir); + if (!n_elem) { + rc = -ENOMEM; + goto err_out; + } + } + } else { + n_elem = t->num_scatter; + } + + rc = mvs_tag_alloc(mvi, &tag); + if (rc) + goto err_out; + + mvi->slot_info[tag].task = t; + mvi->slot_info[tag].n_elem = n_elem; + memset(mvi->slot_info[tag].buf, 0, MVS_SLOT_BUF_SZ); + tei.task = t; + tei.hdr = &mvi->slot[tag]; + tei.tag = tag; + tei.n_elem = n_elem; + + switch (t->task_proto) { + case SAS_PROTOCOL_SMP: + rc = mvs_task_prep_smp(mvi, &tei); + break; + case SAS_PROTOCOL_SSP: + rc = mvs_task_prep_ssp(mvi, &tei); + break; + case SAS_PROTOCOL_SATA: + case SAS_PROTOCOL_STP: + case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: + rc = mvs_task_prep_ata(mvi, &tei); + break; + default: + dev_printk(KERN_ERR, &pdev->dev, + "unknown sas_task proto: 0x%x\n", + t->task_proto); + rc = -EINVAL; + break; + } + + if (rc) + goto err_out_tag; + + /* TODO: select normal or high priority */ + + spin_lock(&t->task_state_lock); + t->task_state_flags |= SAS_TASK_AT_INITIATOR; + spin_unlock(&t->task_state_lock); + + if (n == 1) { + spin_unlock_irqrestore(&mvi->lock, flags); + mw32(TX_PROD_IDX, mvi->tx_prod); + } + mvs_hba_memory_dump(mvi, tag, t->task_proto); + + ++pass; + mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1); + + if (n == 1) + break; + + t = list_entry(t->list.next, struct sas_task, list); + } while (--n); + + return 0; + +err_out_tag: + mvs_tag_free(mvi, tag); +err_out: + dev_printk(KERN_ERR, &pdev->dev, "mvsas exec failed[%d]!\n", rc); + if (!sas_protocol_ata(t->task_proto)) + if (n_elem) + pci_unmap_sg(mvi->pdev, t->scatter, n_elem, + t->data_dir); +exec_exit: + if (pass) + mw32(TX_PROD_IDX, (mvi->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1)); + spin_unlock_irqrestore(&mvi->lock, flags); + return rc; +} + +static int mvs_task_abort(struct sas_task *task) +{ + int rc = 1; + unsigned long flags; + struct mvs_info *mvi = task->dev->port->ha->lldd_ha; + struct pci_dev *pdev = mvi->pdev; + + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_DONE) { + rc = TMF_RESP_FUNC_COMPLETE; + goto out_done; + } + spin_unlock_irqrestore(&task->task_state_lock, flags); + + /*FIXME*/ + rc = TMF_RESP_FUNC_COMPLETE; + + switch (task->task_proto) { + case SAS_PROTOCOL_SMP: + dev_printk(KERN_DEBUG, &pdev->dev, "SMP Abort! "); + break; + case SAS_PROTOCOL_SSP: + dev_printk(KERN_DEBUG, &pdev->dev, "SSP Abort! "); + break; + case SAS_PROTOCOL_SATA: + case SAS_PROTOCOL_STP: + case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:{ + dev_printk(KERN_DEBUG, &pdev->dev, "STP Abort! " + "Dump D2H FIS: \n"); + mvs_hexdump(sizeof(struct host_to_dev_fis), + (void *)&task->ata_task.fis, 0); + dev_printk(KERN_DEBUG, &pdev->dev, "Dump ATAPI Cmd : \n"); + mvs_hexdump(16, task->ata_task.atapi_packet, 0); + break; + } + default: + break; + } +out_done: + return rc; +} + +static void mvs_free(struct mvs_info *mvi) +{ + int i; + + if (!mvi) + return; + + for (i = 0; i < MVS_SLOTS; i++) { + struct mvs_slot_info *slot = &mvi->slot_info[i]; + + if (slot->buf) + dma_free_coherent(&mvi->pdev->dev, MVS_SLOT_BUF_SZ, + slot->buf, slot->buf_dma); + } + + if (mvi->tx) + dma_free_coherent(&mvi->pdev->dev, + sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ, + mvi->tx, mvi->tx_dma); + if (mvi->rx_fis) + dma_free_coherent(&mvi->pdev->dev, MVS_RX_FISL_SZ, + mvi->rx_fis, mvi->rx_fis_dma); + if (mvi->rx) + dma_free_coherent(&mvi->pdev->dev, + sizeof(*mvi->rx) * MVS_RX_RING_SZ, + mvi->rx, mvi->rx_dma); + if (mvi->slot) + dma_free_coherent(&mvi->pdev->dev, + sizeof(*mvi->slot) * MVS_SLOTS, + mvi->slot, mvi->slot_dma); +#ifdef MVS_ENABLE_PERI + if (mvi->peri_regs) + iounmap(mvi->peri_regs); +#endif + if (mvi->regs) + iounmap(mvi->regs); + if (mvi->shost) + scsi_host_put(mvi->shost); + kfree(mvi->sas.sas_port); + kfree(mvi->sas.sas_phy); + kfree(mvi); +} + +/* FIXME: locking? */ +static int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, + void *funcdata) +{ + struct mvs_info *mvi = sas_phy->ha->lldd_ha; + int rc = 0, phy_id = sas_phy->id; + u32 tmp; + + tmp = mvs_read_phy_ctl(mvi, phy_id); + + switch (func) { + case PHY_FUNC_SET_LINK_RATE:{ + struct sas_phy_linkrates *rates = funcdata; + u32 lrmin = 0, lrmax = 0; + + lrmin = (rates->minimum_linkrate << 8); + lrmax = (rates->maximum_linkrate << 12); + + if (lrmin) { + tmp &= ~(0xf << 8); + tmp |= lrmin; + } + if (lrmax) { + tmp &= ~(0xf << 12); + tmp |= lrmax; + } + mvs_write_phy_ctl(mvi, phy_id, tmp); + break; + } + + case PHY_FUNC_HARD_RESET: + if (tmp & PHY_RST_HARD) + break; + mvs_write_phy_ctl(mvi, phy_id, tmp | PHY_RST_HARD); + break; + + case PHY_FUNC_LINK_RESET: + mvs_write_phy_ctl(mvi, phy_id, tmp | PHY_RST); + break; + + case PHY_FUNC_DISABLE: + case PHY_FUNC_RELEASE_SPINUP_HOLD: + default: + rc = -EOPNOTSUPP; + } + + return rc; +} + +static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id) +{ + struct mvs_phy *phy = &mvi->phy[phy_id]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + + sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0; + sas_phy->class = SAS; + sas_phy->iproto = SAS_PROTOCOL_ALL; + sas_phy->tproto = 0; + sas_phy->type = PHY_TYPE_PHYSICAL; + sas_phy->role = PHY_ROLE_INITIATOR; + sas_phy->oob_mode = OOB_NOT_CONNECTED; + sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; + + sas_phy->id = phy_id; + sas_phy->sas_addr = &mvi->sas_addr[0]; + sas_phy->frame_rcvd = &phy->frame_rcvd[0]; + sas_phy->ha = &mvi->sas; + sas_phy->lldd_phy = phy; +} + +static struct mvs_info *__devinit mvs_alloc(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct mvs_info *mvi; + unsigned long res_start, res_len, res_flag; + struct asd_sas_phy **arr_phy; + struct asd_sas_port **arr_port; + const struct mvs_chip_info *chip = &mvs_chips[ent->driver_data]; + int i; + + /* + * alloc and init our per-HBA mvs_info struct + */ + + mvi = kzalloc(sizeof(*mvi), GFP_KERNEL); + if (!mvi) + return NULL; + + spin_lock_init(&mvi->lock); + mvi->pdev = pdev; + mvi->chip = chip; + + if (pdev->device == 0x6440 && pdev->revision == 0) + mvi->flags |= MVF_PHY_PWR_FIX; + + /* + * alloc and init SCSI, SAS glue + */ + + mvi->shost = scsi_host_alloc(&mvs_sht, sizeof(void *)); + if (!mvi->shost) + goto err_out; + + arr_phy = kcalloc(MVS_MAX_PHYS, sizeof(void *), GFP_KERNEL); + arr_port = kcalloc(MVS_MAX_PHYS, sizeof(void *), GFP_KERNEL); + if (!arr_phy || !arr_port) + goto err_out; + + for (i = 0; i < MVS_MAX_PHYS; i++) { + mvs_phy_init(mvi, i); + arr_phy[i] = &mvi->phy[i].sas_phy; + arr_port[i] = &mvi->port[i].sas_port; + } + + SHOST_TO_SAS_HA(mvi->shost) = &mvi->sas; + mvi->shost->transportt = mvs_stt; + mvi->shost->max_id = 21; + mvi->shost->max_lun = ~0; + mvi->shost->max_channel = 0; + mvi->shost->max_cmd_len = 16; + + mvi->sas.sas_ha_name = DRV_NAME; + mvi->sas.dev = &pdev->dev; + mvi->sas.lldd_module = THIS_MODULE; + mvi->sas.sas_addr = &mvi->sas_addr[0]; + mvi->sas.sas_phy = arr_phy; + mvi->sas.sas_port = arr_port; + mvi->sas.num_phys = chip->n_phy; + mvi->sas.lldd_max_execute_num = MVS_CHIP_SLOT_SZ - 1; + mvi->sas.lldd_queue_size = MVS_QUEUE_SIZE; + mvi->can_queue = (MVS_CHIP_SLOT_SZ >> 1) - 1; + mvi->sas.lldd_ha = mvi; + mvi->sas.core.shost = mvi->shost; + + mvs_tag_init(mvi); + + /* + * ioremap main and peripheral registers + */ + +#ifdef MVS_ENABLE_PERI + res_start = pci_resource_start(pdev, 2); + res_len = pci_resource_len(pdev, 2); + if (!res_start || !res_len) + goto err_out; + + mvi->peri_regs = ioremap_nocache(res_start, res_len); + if (!mvi->peri_regs) + goto err_out; +#endif + + res_start = pci_resource_start(pdev, 4); + res_len = pci_resource_len(pdev, 4); + if (!res_start || !res_len) + goto err_out; + + res_flag = pci_resource_flags(pdev, 4); + if (res_flag & IORESOURCE_CACHEABLE) + mvi->regs = ioremap(res_start, res_len); + else + mvi->regs = ioremap_nocache(res_start, res_len); + + if (!mvi->regs) + goto err_out; + + /* + * alloc and init our DMA areas + */ + + mvi->tx = dma_alloc_coherent(&pdev->dev, + sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ, + &mvi->tx_dma, GFP_KERNEL); + if (!mvi->tx) + goto err_out; + memset(mvi->tx, 0, sizeof(*mvi->tx) * MVS_CHIP_SLOT_SZ); + + mvi->rx_fis = dma_alloc_coherent(&pdev->dev, MVS_RX_FISL_SZ, + &mvi->rx_fis_dma, GFP_KERNEL); + if (!mvi->rx_fis) + goto err_out; + memset(mvi->rx_fis, 0, MVS_RX_FISL_SZ); + + mvi->rx = dma_alloc_coherent(&pdev->dev, + sizeof(*mvi->rx) * MVS_RX_RING_SZ, + &mvi->rx_dma, GFP_KERNEL); + if (!mvi->rx) + goto err_out; + memset(mvi->rx, 0, sizeof(*mvi->rx) * MVS_RX_RING_SZ); + + mvi->rx[0] = cpu_to_le32(0xfff); + mvi->rx_cons = 0xfff; + + mvi->slot = dma_alloc_coherent(&pdev->dev, + sizeof(*mvi->slot) * MVS_SLOTS, + &mvi->slot_dma, GFP_KERNEL); + if (!mvi->slot) + goto err_out; + memset(mvi->slot, 0, sizeof(*mvi->slot) * MVS_SLOTS); + + for (i = 0; i < MVS_SLOTS; i++) { + struct mvs_slot_info *slot = &mvi->slot_info[i]; + + slot->buf = dma_alloc_coherent(&pdev->dev, MVS_SLOT_BUF_SZ, + &slot->buf_dma, GFP_KERNEL); + if (!slot->buf) + goto err_out; + memset(slot->buf, 0, MVS_SLOT_BUF_SZ); + } + + /* finally, read NVRAM to get our SAS address */ + if (mvs_nvram_read(mvi, NVR_SAS_ADDR, &mvi->sas_addr, 8)) + goto err_out; + return mvi; + +err_out: + mvs_free(mvi); + return NULL; +} + +static u32 mvs_cr32(void __iomem *regs, u32 addr) +{ + mw32(CMD_ADDR, addr); + return mr32(CMD_DATA); +} + +static void mvs_cw32(void __iomem *regs, u32 addr, u32 val) +{ + mw32(CMD_ADDR, addr); + mw32(CMD_DATA, val); +} + +static u32 mvs_read_phy_ctl(struct mvs_info *mvi, u32 port) +{ + void __iomem *regs = mvi->regs; + return (port < 4)?mr32(P0_SER_CTLSTAT + port * 4): + mr32(P4_SER_CTLSTAT + (port - 4) * 4); +} + +static void mvs_write_phy_ctl(struct mvs_info *mvi, u32 port, u32 val) +{ + void __iomem *regs = mvi->regs; + if (port < 4) + mw32(P0_SER_CTLSTAT + port * 4, val); + else + mw32(P4_SER_CTLSTAT + (port - 4) * 4, val); +} + +static u32 mvs_read_port(struct mvs_info *mvi, u32 off, u32 off2, u32 port) +{ + void __iomem *regs = mvi->regs + off; + void __iomem *regs2 = mvi->regs + off2; + return (port < 4)?readl(regs + port * 8): + readl(regs2 + (port - 4) * 8); +} + +static void mvs_write_port(struct mvs_info *mvi, u32 off, u32 off2, + u32 port, u32 val) +{ + void __iomem *regs = mvi->regs + off; + void __iomem *regs2 = mvi->regs + off2; + if (port < 4) + writel(val, regs + port * 8); + else + writel(val, regs2 + (port - 4) * 8); +} + +static u32 mvs_read_port_cfg_data(struct mvs_info *mvi, u32 port) +{ + return mvs_read_port(mvi, MVS_P0_CFG_DATA, MVS_P4_CFG_DATA, port); +} + +static void mvs_write_port_cfg_data(struct mvs_info *mvi, u32 port, u32 val) +{ + mvs_write_port(mvi, MVS_P0_CFG_DATA, MVS_P4_CFG_DATA, port, val); +} + +static void mvs_write_port_cfg_addr(struct mvs_info *mvi, u32 port, u32 addr) +{ + mvs_write_port(mvi, MVS_P0_CFG_ADDR, MVS_P4_CFG_ADDR, port, addr); +} + +static u32 mvs_read_port_vsr_data(struct mvs_info *mvi, u32 port) +{ + return mvs_read_port(mvi, MVS_P0_VSR_DATA, MVS_P4_VSR_DATA, port); +} + +static void mvs_write_port_vsr_data(struct mvs_info *mvi, u32 port, u32 val) +{ + mvs_write_port(mvi, MVS_P0_VSR_DATA, MVS_P4_VSR_DATA, port, val); +} + +static void mvs_write_port_vsr_addr(struct mvs_info *mvi, u32 port, u32 addr) +{ + mvs_write_port(mvi, MVS_P0_VSR_ADDR, MVS_P4_VSR_ADDR, port, addr); +} + +static u32 mvs_read_port_irq_stat(struct mvs_info *mvi, u32 port) +{ + return mvs_read_port(mvi, MVS_P0_INT_STAT, MVS_P4_INT_STAT, port); +} + +static void mvs_write_port_irq_stat(struct mvs_info *mvi, u32 port, u32 val) +{ + mvs_write_port(mvi, MVS_P0_INT_STAT, MVS_P4_INT_STAT, port, val); +} + +static u32 mvs_read_port_irq_mask(struct mvs_info *mvi, u32 port) +{ + return mvs_read_port(mvi, MVS_P0_INT_MASK, MVS_P4_INT_MASK, port); +} + +static void mvs_write_port_irq_mask(struct mvs_info *mvi, u32 port, u32 val) +{ + mvs_write_port(mvi, MVS_P0_INT_MASK, MVS_P4_INT_MASK, port, val); +} + +static void __devinit mvs_phy_hacks(struct mvs_info *mvi) +{ + void __iomem *regs = mvi->regs; + u32 tmp; + + /* workaround for SATA R-ERR, to ignore phy glitch */ + tmp = mvs_cr32(regs, CMD_PHY_TIMER); + tmp &= ~(1 << 9); + tmp |= (1 << 10); + mvs_cw32(regs, CMD_PHY_TIMER, tmp); + + /* enable retry 127 times */ + mvs_cw32(regs, CMD_SAS_CTL1, 0x7f7f); + + /* extend open frame timeout to max */ + tmp = mvs_cr32(regs, CMD_SAS_CTL0); + tmp &= ~0xffff; + tmp |= 0x3fff; + mvs_cw32(regs, CMD_SAS_CTL0, tmp); + + /* workaround for WDTIMEOUT , set to 550 ms */ + mvs_cw32(regs, CMD_WD_TIMER, 0xffffff); + + /* not to halt for different port op during wideport link change */ + mvs_cw32(regs, CMD_APP_ERR_CONFIG, 0xffefbf7d); + + /* workaround for Seagate disk not-found OOB sequence, recv + * COMINIT before sending out COMWAKE */ + tmp = mvs_cr32(regs, CMD_PHY_MODE_21); + tmp &= 0x0000ffff; + tmp |= 0x00fa0000; + mvs_cw32(regs, CMD_PHY_MODE_21, tmp); + + tmp = mvs_cr32(regs, CMD_PHY_TIMER); + tmp &= 0x1fffffff; + tmp |= (2U << 29); /* 8 ms retry */ + mvs_cw32(regs, CMD_PHY_TIMER, tmp); + + /* TEST - for phy decoding error, adjust voltage levels */ + mw32(P0_VSR_ADDR + 0, 0x8); + mw32(P0_VSR_DATA + 0, 0x2F0); + + mw32(P0_VSR_ADDR + 8, 0x8); + mw32(P0_VSR_DATA + 8, 0x2F0); + + mw32(P0_VSR_ADDR + 16, 0x8); + mw32(P0_VSR_DATA + 16, 0x2F0); + + mw32(P0_VSR_ADDR + 24, 0x8); + mw32(P0_VSR_DATA + 24, 0x2F0); + +} + +static void mvs_enable_xmt(struct mvs_info *mvi, int PhyId) +{ + void __iomem *regs = mvi->regs; + u32 tmp; + + tmp = mr32(PCS); + if (mvi->chip->n_phy <= 4) + tmp |= 1 << (PhyId + PCS_EN_PORT_XMT_SHIFT); + else + tmp |= 1 << (PhyId + PCS_EN_PORT_XMT_SHIFT2); + mw32(PCS, tmp); +} + +static void mvs_detect_porttype(struct mvs_info *mvi, int i) +{ + void __iomem *regs = mvi->regs; + u32 reg; + struct mvs_phy *phy = &mvi->phy[i]; + + /* TODO check & save device type */ + reg = mr32(GBL_PORT_TYPE); + + if (reg & MODE_SAS_SATA & (1 << i)) + phy->phy_type |= PORT_TYPE_SAS; + else + phy->phy_type |= PORT_TYPE_SATA; +} + +static void *mvs_get_d2h_reg(struct mvs_info *mvi, int i, void *buf) +{ + u32 *s = (u32 *) buf; + + if (!s) + return NULL; + + mvs_write_port_cfg_addr(mvi, i, PHYR_SATA_SIG3); + s[3] = mvs_read_port_cfg_data(mvi, i); + + mvs_write_port_cfg_addr(mvi, i, PHYR_SATA_SIG2); + s[2] = mvs_read_port_cfg_data(mvi, i); + + mvs_write_port_cfg_addr(mvi, i, PHYR_SATA_SIG1); + s[1] = mvs_read_port_cfg_data(mvi, i); + + mvs_write_port_cfg_addr(mvi, i, PHYR_SATA_SIG0); + s[0] = mvs_read_port_cfg_data(mvi, i); + + return (void *)s; +} + +static u32 mvs_is_sig_fis_received(u32 irq_status) +{ + return irq_status & PHYEV_SIG_FIS; +} + +static void mvs_update_wideport(struct mvs_info *mvi, int i) +{ + struct mvs_phy *phy = &mvi->phy[i]; + struct mvs_port *port = phy->port; + int j, no; + + for_each_phy(port->wide_port_phymap, no, j, mvi->chip->n_phy) + if (no & 1) { + mvs_write_port_cfg_addr(mvi, no, PHYR_WIDE_PORT); + mvs_write_port_cfg_data(mvi, no, + port->wide_port_phymap); + } else { + mvs_write_port_cfg_addr(mvi, no, PHYR_WIDE_PORT); + mvs_write_port_cfg_data(mvi, no, 0); + } +} + +static u32 mvs_is_phy_ready(struct mvs_info *mvi, int i) +{ + u32 tmp; + struct mvs_phy *phy = &mvi->phy[i]; + struct mvs_port *port; + + tmp = mvs_read_phy_ctl(mvi, i); + + if ((tmp & PHY_READY_MASK) && !(phy->irq_status & PHYEV_POOF)) { + if (!phy->port) + phy->phy_attached = 1; + return tmp; + } + + port = phy->port; + if (port) { + if (phy->phy_type & PORT_TYPE_SAS) { + port->wide_port_phymap &= ~(1U << i); + if (!port->wide_port_phymap) + port->port_attached = 0; + mvs_update_wideport(mvi, i); + } else if (phy->phy_type & PORT_TYPE_SATA) + port->port_attached = 0; + mvs_free_reg_set(mvi, phy->port); + phy->port = NULL; + phy->phy_attached = 0; + phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA); + } + return 0; +} + +static void mvs_update_phyinfo(struct mvs_info *mvi, int i, + int get_st) +{ + struct mvs_phy *phy = &mvi->phy[i]; + struct pci_dev *pdev = mvi->pdev; + u32 tmp, j; + u64 tmp64; + + mvs_write_port_cfg_addr(mvi, i, PHYR_IDENTIFY); + phy->dev_info = mvs_read_port_cfg_data(mvi, i); + + mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_HI); + phy->dev_sas_addr = (u64) mvs_read_port_cfg_data(mvi, i) << 32; + + mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_LO); + phy->dev_sas_addr |= mvs_read_port_cfg_data(mvi, i); + + if (get_st) { + phy->irq_status = mvs_read_port_irq_stat(mvi, i); + phy->phy_status = mvs_is_phy_ready(mvi, i); + } + + if (phy->phy_status) { + u32 phy_st; + struct asd_sas_phy *sas_phy = mvi->sas.sas_phy[i]; + + mvs_write_port_cfg_addr(mvi, i, PHYR_PHY_STAT); + phy_st = mvs_read_port_cfg_data(mvi, i); + + sas_phy->linkrate = + (phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >> + PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET; + + /* Updated attached_sas_addr */ + mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_HI); + phy->att_dev_sas_addr = + (u64) mvs_read_port_cfg_data(mvi, i) << 32; + + mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_ADDR_LO); + phy->att_dev_sas_addr |= mvs_read_port_cfg_data(mvi, i); + + dev_printk(KERN_DEBUG, &pdev->dev, + "phy[%d] Get Attached Address 0x%llX ," + " SAS Address 0x%llX\n", + i, phy->att_dev_sas_addr, phy->dev_sas_addr); + dev_printk(KERN_DEBUG, &pdev->dev, + "Rate = %x , type = %d\n", + sas_phy->linkrate, phy->phy_type); + +#if 1 + /* + * If the device is capable of supporting a wide port + * on its phys, it may configure the phys as a wide port. + */ + if (phy->phy_type & PORT_TYPE_SAS) + for (j = 0; j < mvi->chip->n_phy && j != i; ++j) { + if ((mvi->phy[j].phy_attached) && + (mvi->phy[j].phy_type & PORT_TYPE_SAS)) + if (phy->att_dev_sas_addr == + mvi->phy[j].att_dev_sas_addr - 1) { + phy->att_dev_sas_addr = + mvi->phy[j].att_dev_sas_addr; + break; + } + } + +#endif + + tmp64 = cpu_to_be64(phy->att_dev_sas_addr); + memcpy(sas_phy->attached_sas_addr, &tmp64, SAS_ADDR_SIZE); + + if (phy->phy_type & PORT_TYPE_SAS) { + mvs_write_port_cfg_addr(mvi, i, PHYR_ATT_DEV_INFO); + phy->att_dev_info = mvs_read_port_cfg_data(mvi, i); + phy->identify.device_type = + phy->att_dev_info & PORT_DEV_TYPE_MASK; + + if (phy->identify.device_type == SAS_END_DEV) + phy->identify.target_port_protocols = + SAS_PROTOCOL_SSP; + else if (phy->identify.device_type != NO_DEVICE) + phy->identify.target_port_protocols = + SAS_PROTOCOL_SMP; + if (phy_st & PHY_OOB_DTCTD) + sas_phy->oob_mode = SAS_OOB_MODE; + phy->frame_rcvd_size = + sizeof(struct sas_identify_frame); + } else if (phy->phy_type & PORT_TYPE_SATA) { + phy->identify.target_port_protocols = SAS_PROTOCOL_STP; + if (mvs_is_sig_fis_received(phy->irq_status)) { + if (phy_st & PHY_OOB_DTCTD) + sas_phy->oob_mode = SATA_OOB_MODE; + phy->frame_rcvd_size = + sizeof(struct dev_to_host_fis); + mvs_get_d2h_reg(mvi, i, + (void *)sas_phy->frame_rcvd); + } else { + dev_printk(KERN_DEBUG, &pdev->dev, + "No sig fis\n"); + } + } + /* workaround for HW phy decoding error on 1.5g disk drive */ + mvs_write_port_vsr_addr(mvi, i, VSR_PHY_MODE6); + tmp = mvs_read_port_vsr_data(mvi, i); + if (((phy->phy_status & PHY_NEG_SPP_PHYS_LINK_RATE_MASK) >> + PHY_NEG_SPP_PHYS_LINK_RATE_MASK_OFFSET) == + SAS_LINK_RATE_1_5_GBPS) + tmp &= ~PHY_MODE6_DTL_SPEED; + else + tmp |= PHY_MODE6_DTL_SPEED; + mvs_write_port_vsr_data(mvi, i, tmp); + + } + if (get_st) + mvs_write_port_irq_stat(mvi, i, phy->irq_status); +} + +static void mvs_port_formed(struct asd_sas_phy *sas_phy) +{ + struct sas_ha_struct *sas_ha = sas_phy->ha; + struct mvs_info *mvi = sas_ha->lldd_ha; + struct asd_sas_port *sas_port = sas_phy->port; + struct mvs_phy *phy = sas_phy->lldd_phy; + struct mvs_port *port = &mvi->port[sas_port->id]; + unsigned long flags; + + spin_lock_irqsave(&mvi->lock, flags); + port->port_attached = 1; + phy->port = port; + port->taskfileset = MVS_ID_NOT_MAPPED; + if (phy->phy_type & PORT_TYPE_SAS) { + port->wide_port_phymap = sas_port->phy_mask; + mvs_update_wideport(mvi, sas_phy->id); + } + spin_unlock_irqrestore(&mvi->lock, flags); +} + +static int __devinit mvs_hw_init(struct mvs_info *mvi) +{ + void __iomem *regs = mvi->regs; + int i; + u32 tmp, cctl; + + /* make sure interrupts are masked immediately (paranoia) */ + mw32(GBL_CTL, 0); + tmp = mr32(GBL_CTL); + + /* Reset Controller */ + if (!(tmp & HBA_RST)) { + if (mvi->flags & MVF_PHY_PWR_FIX) { + pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &tmp); + tmp &= ~PCTL_PWR_ON; + tmp |= PCTL_OFF; + pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp); + + pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &tmp); + tmp &= ~PCTL_PWR_ON; + tmp |= PCTL_OFF; + pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp); + } + + /* global reset, incl. COMRESET/H_RESET_N (self-clearing) */ + mw32_f(GBL_CTL, HBA_RST); + } + + /* wait for reset to finish; timeout is just a guess */ + i = 1000; + while (i-- > 0) { + msleep(10); + + if (!(mr32(GBL_CTL) & HBA_RST)) + break; + } + if (mr32(GBL_CTL) & HBA_RST) { + dev_printk(KERN_ERR, &mvi->pdev->dev, "HBA reset failed\n"); + return -EBUSY; + } + + /* Init Chip */ + /* make sure RST is set; HBA_RST /should/ have done that for us */ + cctl = mr32(CTL); + if (cctl & CCTL_RST) + cctl &= ~CCTL_RST; + else + mw32_f(CTL, cctl | CCTL_RST); + + /* write to device control _AND_ device status register? - A.C. */ + pci_read_config_dword(mvi->pdev, PCR_DEV_CTRL, &tmp); + tmp &= ~PRD_REQ_MASK; + tmp |= PRD_REQ_SIZE; + pci_write_config_dword(mvi->pdev, PCR_DEV_CTRL, tmp); + + pci_read_config_dword(mvi->pdev, PCR_PHY_CTL, &tmp); + tmp |= PCTL_PWR_ON; + tmp &= ~PCTL_OFF; + pci_write_config_dword(mvi->pdev, PCR_PHY_CTL, tmp); + + pci_read_config_dword(mvi->pdev, PCR_PHY_CTL2, &tmp); + tmp |= PCTL_PWR_ON; + tmp &= ~PCTL_OFF; + pci_write_config_dword(mvi->pdev, PCR_PHY_CTL2, tmp); + + mw32_f(CTL, cctl); + + /* reset control */ + mw32(PCS, 0); /*MVS_PCS */ + + mvs_phy_hacks(mvi); + + mw32(CMD_LIST_LO, mvi->slot_dma); + mw32(CMD_LIST_HI, (mvi->slot_dma >> 16) >> 16); + + mw32(RX_FIS_LO, mvi->rx_fis_dma); + mw32(RX_FIS_HI, (mvi->rx_fis_dma >> 16) >> 16); + + mw32(TX_CFG, MVS_CHIP_SLOT_SZ); + mw32(TX_LO, mvi->tx_dma); + mw32(TX_HI, (mvi->tx_dma >> 16) >> 16); + + mw32(RX_CFG, MVS_RX_RING_SZ); + mw32(RX_LO, mvi->rx_dma); + mw32(RX_HI, (mvi->rx_dma >> 16) >> 16); + + /* enable auto port detection */ + mw32(GBL_PORT_TYPE, MODE_AUTO_DET_EN); + msleep(100); + /* init and reset phys */ + for (i = 0; i < mvi->chip->n_phy; i++) { + /* FIXME: is this the correct dword order? */ + u32 lo = *((u32 *)&mvi->sas_addr[0]); + u32 hi = *((u32 *)&mvi->sas_addr[4]); + + mvs_detect_porttype(mvi, i); + + /* set phy local SAS address */ + mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_LO); + mvs_write_port_cfg_data(mvi, i, lo); + mvs_write_port_cfg_addr(mvi, i, PHYR_ADDR_HI); + mvs_write_port_cfg_data(mvi, i, hi); + + /* reset phy */ + tmp = mvs_read_phy_ctl(mvi, i); + tmp |= PHY_RST; + mvs_write_phy_ctl(mvi, i, tmp); + } + + msleep(100); + + for (i = 0; i < mvi->chip->n_phy; i++) { + /* clear phy int status */ + tmp = mvs_read_port_irq_stat(mvi, i); + tmp &= ~PHYEV_SIG_FIS; + mvs_write_port_irq_stat(mvi, i, tmp); + + /* set phy int mask */ + tmp = PHYEV_RDY_CH | PHYEV_BROAD_CH | PHYEV_UNASSOC_FIS | + PHYEV_ID_DONE | PHYEV_DEC_ERR; + mvs_write_port_irq_mask(mvi, i, tmp); + + msleep(100); + mvs_update_phyinfo(mvi, i, 1); + mvs_enable_xmt(mvi, i); + } + + /* FIXME: update wide port bitmaps */ + + /* little endian for open address and command table, etc. */ + /* A.C. + * it seems that ( from the spec ) turning on big-endian won't + * do us any good on big-endian machines, need further confirmation + */ + cctl = mr32(CTL); + cctl |= CCTL_ENDIAN_CMD; + cctl |= CCTL_ENDIAN_DATA; + cctl &= ~CCTL_ENDIAN_OPEN; + cctl |= CCTL_ENDIAN_RSP; + mw32_f(CTL, cctl); + + /* reset CMD queue */ + tmp = mr32(PCS); + tmp |= PCS_CMD_RST; + mw32(PCS, tmp); + /* interrupt coalescing may cause missing HW interrput in some case, + * and the max count is 0x1ff, while our max slot is 0x200, + * it will make count 0. + */ + tmp = 0; + mw32(INT_COAL, tmp); + + tmp = 0x100; + mw32(INT_COAL_TMOUT, tmp); + + /* ladies and gentlemen, start your engines */ + mw32(TX_CFG, 0); + mw32(TX_CFG, MVS_CHIP_SLOT_SZ | TX_EN); + mw32(RX_CFG, MVS_RX_RING_SZ | RX_EN); + /* enable CMD/CMPL_Q/RESP mode */ + mw32(PCS, PCS_SATA_RETRY | PCS_FIS_RX_EN | PCS_CMD_EN); + + /* re-enable interrupts globally */ + mvs_hba_interrupt_enable(mvi); + + /* enable completion queue interrupt */ + tmp = (CINT_PORT_MASK | CINT_DONE | CINT_MEM); + mw32(INT_MASK, tmp); + + return 0; +} + +static void __devinit mvs_print_info(struct mvs_info *mvi) +{ + struct pci_dev *pdev = mvi->pdev; + static int printed_version; + + if (!printed_version++) + dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); + + dev_printk(KERN_INFO, &pdev->dev, "%u phys, addr %llx\n", + mvi->chip->n_phy, SAS_ADDR(mvi->sas_addr)); +} + +static int __devinit mvs_pci_init(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int rc; + struct mvs_info *mvi; + irq_handler_t irq_handler = mvs_interrupt; + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + pci_set_master(pdev); + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out_disable; + + rc = pci_go_64(pdev); + if (rc) + goto err_out_regions; + + mvi = mvs_alloc(pdev, ent); + if (!mvi) { + rc = -ENOMEM; + goto err_out_regions; + } + + rc = mvs_hw_init(mvi); + if (rc) + goto err_out_mvi; + +#ifndef MVS_DISABLE_MSI + if (!pci_enable_msi(pdev)) { + u32 tmp; + void __iomem *regs = mvi->regs; + mvi->flags |= MVF_MSI; + irq_handler = mvs_msi_interrupt; + tmp = mr32(PCS); + mw32(PCS, tmp | PCS_SELF_CLEAR); + } +#endif + + rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME, mvi); + if (rc) + goto err_out_msi; + + rc = scsi_add_host(mvi->shost, &pdev->dev); + if (rc) + goto err_out_irq; + + rc = sas_register_ha(&mvi->sas); + if (rc) + goto err_out_shost; + + pci_set_drvdata(pdev, mvi); + + mvs_print_info(mvi); + + scsi_scan_host(mvi->shost); + + return 0; + +err_out_shost: + scsi_remove_host(mvi->shost); +err_out_irq: + free_irq(pdev->irq, mvi); +err_out_msi: + if (mvi->flags |= MVF_MSI) + pci_disable_msi(pdev); +err_out_mvi: + mvs_free(mvi); +err_out_regions: + pci_release_regions(pdev); +err_out_disable: + pci_disable_device(pdev); + return rc; +} + +static void __devexit mvs_pci_remove(struct pci_dev *pdev) +{ + struct mvs_info *mvi = pci_get_drvdata(pdev); + + pci_set_drvdata(pdev, NULL); + + if (mvi) { + sas_unregister_ha(&mvi->sas); + mvs_hba_interrupt_disable(mvi); + sas_remove_host(mvi->shost); + scsi_remove_host(mvi->shost); + + free_irq(pdev->irq, mvi); + if (mvi->flags & MVF_MSI) + pci_disable_msi(pdev); + mvs_free(mvi); + pci_release_regions(pdev); + } + pci_disable_device(pdev); +} + +static struct sas_domain_function_template mvs_transport_ops = { + .lldd_execute_task = mvs_task_exec, + .lldd_control_phy = mvs_phy_control, + .lldd_abort_task = mvs_task_abort, + .lldd_port_formed = mvs_port_formed +}; + +static struct pci_device_id __devinitdata mvs_pci_table[] = { + { PCI_VDEVICE(MARVELL, 0x6320), chip_6320 }, + { PCI_VDEVICE(MARVELL, 0x6340), chip_6440 }, + { PCI_VDEVICE(MARVELL, 0x6440), chip_6440 }, + { PCI_VDEVICE(MARVELL, 0x6480), chip_6480 }, + + { } /* terminate list */ +}; + +static struct pci_driver mvs_pci_driver = { + .name = DRV_NAME, + .id_table = mvs_pci_table, + .probe = mvs_pci_init, + .remove = __devexit_p(mvs_pci_remove), +}; + +static int __init mvs_init(void) +{ + int rc; + + mvs_stt = sas_domain_attach_transport(&mvs_transport_ops); + if (!mvs_stt) + return -ENOMEM; + + rc = pci_register_driver(&mvs_pci_driver); + if (rc) + goto err_out; + + return 0; + +err_out: + sas_release_transport(mvs_stt); + return rc; +} + +static void __exit mvs_exit(void) +{ + pci_unregister_driver(&mvs_pci_driver); + sas_release_transport(mvs_stt); +} + +module_init(mvs_init); +module_exit(mvs_exit); + +MODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>"); +MODULE_DESCRIPTION("Marvell 88SE6440 SAS/SATA controller driver"); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, mvs_pci_table); diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index 1479c60441c..2cd899bfe84 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -23,7 +23,7 @@ qla2x00_dfs_fce_show(struct seq_file *s, void *unused) mutex_lock(&ha->fce_mutex); seq_printf(s, "FCE Trace Buffer\n"); - seq_printf(s, "In Pointer = %llx\n\n", ha->fce_wr); + seq_printf(s, "In Pointer = %llx\n\n", (unsigned long long)ha->fce_wr); seq_printf(s, "Base = %llx\n\n", (unsigned long long) ha->fce_dma); seq_printf(s, "FCE Enable Registers\n"); seq_printf(s, "%08x %08x %08x %08x %08x %08x\n", diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c index 0f029d0d731..fc84db4069f 100644 --- a/drivers/scsi/qla4xxx/ql4_isr.c +++ b/drivers/scsi/qla4xxx/ql4_isr.c @@ -100,8 +100,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, if (sts_entry->iscsiFlags &ISCSI_FLAG_RESIDUAL_UNDER) { scsi_set_resid(cmd, residual); - if (!scsi_status && ((scsi_bufflen(cmd) - residual) < - cmd->underflow)) { + if ((scsi_bufflen(cmd) - residual) < cmd->underflow) { cmd->result = DID_ERROR << 16; diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 65455ab1f3b..4a1cf6377f6 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -651,7 +651,7 @@ static int qlogicpti_verify_tmon(struct qlogicpti *qpti) static irqreturn_t qpti_intr(int irq, void *dev_id); -static void __init qpti_chain_add(struct qlogicpti *qpti) +static void __devinit qpti_chain_add(struct qlogicpti *qpti) { spin_lock_irq(&qptichain_lock); if (qptichain != NULL) { @@ -667,7 +667,7 @@ static void __init qpti_chain_add(struct qlogicpti *qpti) spin_unlock_irq(&qptichain_lock); } -static void __init qpti_chain_del(struct qlogicpti *qpti) +static void __devexit qpti_chain_del(struct qlogicpti *qpti) { spin_lock_irq(&qptichain_lock); if (qptichain == qpti) { @@ -682,7 +682,7 @@ static void __init qpti_chain_del(struct qlogicpti *qpti) spin_unlock_irq(&qptichain_lock); } -static int __init qpti_map_regs(struct qlogicpti *qpti) +static int __devinit qpti_map_regs(struct qlogicpti *qpti) { struct sbus_dev *sdev = qpti->sdev; @@ -705,7 +705,7 @@ static int __init qpti_map_regs(struct qlogicpti *qpti) return 0; } -static int __init qpti_register_irq(struct qlogicpti *qpti) +static int __devinit qpti_register_irq(struct qlogicpti *qpti) { struct sbus_dev *sdev = qpti->sdev; @@ -730,7 +730,7 @@ fail: return -1; } -static void __init qpti_get_scsi_id(struct qlogicpti *qpti) +static void __devinit qpti_get_scsi_id(struct qlogicpti *qpti) { qpti->scsi_id = prom_getintdefault(qpti->prom_node, "initiator-id", @@ -783,7 +783,7 @@ static void qpti_get_clock(struct qlogicpti *qpti) /* The request and response queues must each be aligned * on a page boundary. */ -static int __init qpti_map_queues(struct qlogicpti *qpti) +static int __devinit qpti_map_queues(struct qlogicpti *qpti) { struct sbus_dev *sdev = qpti->sdev; diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 1541c174937..d1777a9a962 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -222,7 +222,7 @@ static struct scsi_host_template sdebug_driver_template = { .cmd_per_lun = 16, .max_sectors = 0xffff, .unchecked_isa_dma = 0, - .use_clustering = ENABLE_CLUSTERING, + .use_clustering = DISABLE_CLUSTERING, .module = THIS_MODULE, }; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 135c1d05470..ba21d97d185 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1014,10 +1014,6 @@ static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb, } req->buffer = NULL; - if (blk_pc_request(req)) - sdb->length = req->data_len; - else - sdb->length = req->nr_sectors << 9; /* * Next, walk the list, and fill in the addresses and sizes of @@ -1026,6 +1022,10 @@ static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb, count = blk_rq_map_sg(req->q, req, sdb->table.sgl); BUG_ON(count > sdb->table.nents); sdb->table.nents = count; + if (blk_pc_request(req)) + sdb->length = req->data_len; + else + sdb->length = req->nr_sectors << 9; return BLKPREP_OK; } diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index fac7534f3ec..9981682d530 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -231,7 +231,7 @@ static struct { { ISCSI_SESSION_FREE, "FREE" }, }; -const char *iscsi_session_state_name(int state) +static const char *iscsi_session_state_name(int state) { int i; char *name = NULL; @@ -373,7 +373,7 @@ static void session_recovery_timedout(struct work_struct *work) scsi_target_unblock(&session->dev); } -void __iscsi_unblock_session(struct iscsi_cls_session *session) +static void __iscsi_unblock_session(struct iscsi_cls_session *session) { if (!cancel_delayed_work(&session->recovery_work)) flush_workqueue(iscsi_eh_timer_workq); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 37df8bbe7f4..7aee64dbfbe 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1835,8 +1835,7 @@ static int sd_suspend(struct device *dev, pm_message_t mesg) goto done; } - if (mesg.event == PM_EVENT_SUSPEND && - sdkp->device->manage_start_stop) { + if ((mesg.event & PM_EVENT_SLEEP) && sdkp->device->manage_start_stop) { sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n"); ret = sd_start_stop_device(sdkp, 0); } diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 2a6e4f472ea..a6d96694d0a 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -33,9 +33,9 @@ #include <scsi/scsi_host.h> struct ses_device { - char *page1; - char *page2; - char *page10; + unsigned char *page1; + unsigned char *page2; + unsigned char *page10; short page1_len; short page2_len; short page10_len; @@ -67,7 +67,7 @@ static int ses_probe(struct device *dev) static int ses_recv_diag(struct scsi_device *sdev, int page_code, void *buf, int bufflen) { - char cmd[] = { + unsigned char cmd[] = { RECEIVE_DIAGNOSTIC, 1, /* Set PCV bit */ page_code, @@ -85,7 +85,7 @@ static int ses_send_diag(struct scsi_device *sdev, int page_code, { u32 result; - char cmd[] = { + unsigned char cmd[] = { SEND_DIAGNOSTIC, 0x10, /* Set PF bit */ 0, @@ -104,13 +104,13 @@ static int ses_send_diag(struct scsi_device *sdev, int page_code, static int ses_set_page2_descriptor(struct enclosure_device *edev, struct enclosure_component *ecomp, - char *desc) + unsigned char *desc) { int i, j, count = 0, descriptor = ecomp->number; struct scsi_device *sdev = to_scsi_device(edev->cdev.dev); struct ses_device *ses_dev = edev->scratch; - char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; - char *desc_ptr = ses_dev->page2 + 8; + unsigned char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; + unsigned char *desc_ptr = ses_dev->page2 + 8; /* Clear everything */ memset(desc_ptr, 0, ses_dev->page2_len - 8); @@ -133,14 +133,14 @@ static int ses_set_page2_descriptor(struct enclosure_device *edev, return ses_send_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len); } -static char *ses_get_page2_descriptor(struct enclosure_device *edev, +static unsigned char *ses_get_page2_descriptor(struct enclosure_device *edev, struct enclosure_component *ecomp) { int i, j, count = 0, descriptor = ecomp->number; struct scsi_device *sdev = to_scsi_device(edev->cdev.dev); struct ses_device *ses_dev = edev->scratch; - char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; - char *desc_ptr = ses_dev->page2 + 8; + unsigned char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; + unsigned char *desc_ptr = ses_dev->page2 + 8; ses_recv_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len); @@ -160,17 +160,18 @@ static char *ses_get_page2_descriptor(struct enclosure_device *edev, static void ses_get_fault(struct enclosure_device *edev, struct enclosure_component *ecomp) { - char *desc; + unsigned char *desc; desc = ses_get_page2_descriptor(edev, ecomp); - ecomp->fault = (desc[3] & 0x60) >> 4; + if (desc) + ecomp->fault = (desc[3] & 0x60) >> 4; } static int ses_set_fault(struct enclosure_device *edev, struct enclosure_component *ecomp, enum enclosure_component_setting val) { - char desc[4] = {0 }; + unsigned char desc[4] = {0 }; switch (val) { case ENCLOSURE_SETTING_DISABLED: @@ -190,26 +191,28 @@ static int ses_set_fault(struct enclosure_device *edev, static void ses_get_status(struct enclosure_device *edev, struct enclosure_component *ecomp) { - char *desc; + unsigned char *desc; desc = ses_get_page2_descriptor(edev, ecomp); - ecomp->status = (desc[0] & 0x0f); + if (desc) + ecomp->status = (desc[0] & 0x0f); } static void ses_get_locate(struct enclosure_device *edev, struct enclosure_component *ecomp) { - char *desc; + unsigned char *desc; desc = ses_get_page2_descriptor(edev, ecomp); - ecomp->locate = (desc[2] & 0x02) ? 1 : 0; + if (desc) + ecomp->locate = (desc[2] & 0x02) ? 1 : 0; } static int ses_set_locate(struct enclosure_device *edev, struct enclosure_component *ecomp, enum enclosure_component_setting val) { - char desc[4] = {0 }; + unsigned char desc[4] = {0 }; switch (val) { case ENCLOSURE_SETTING_DISABLED: @@ -229,7 +232,7 @@ static int ses_set_active(struct enclosure_device *edev, struct enclosure_component *ecomp, enum enclosure_component_setting val) { - char desc[4] = {0 }; + unsigned char desc[4] = {0 }; switch (val) { case ENCLOSURE_SETTING_DISABLED: @@ -409,18 +412,18 @@ static int ses_intf_add(struct class_device *cdev, { struct scsi_device *sdev = to_scsi_device(cdev->dev); struct scsi_device *tmp_sdev; - unsigned char *buf = NULL, *hdr_buf, *type_ptr, *desc_ptr, - *addl_desc_ptr; + unsigned char *buf = NULL, *hdr_buf, *type_ptr, *desc_ptr = NULL, + *addl_desc_ptr = NULL; struct ses_device *ses_dev; u32 result; - int i, j, types, len, components = 0; + int i, j, types, len, page7_len = 0, components = 0; int err = -ENOMEM; struct enclosure_device *edev; - struct ses_component *scomp; + struct ses_component *scomp = NULL; if (!scsi_device_enclosure(sdev)) { /* not an enclosure, but might be in one */ - edev = enclosure_find(&sdev->host->shost_gendev); + edev = enclosure_find(&sdev->host->shost_gendev); if (edev) { ses_match_to_enclosure(edev, sdev); class_device_put(&edev->cdev); @@ -447,7 +450,7 @@ static int ses_intf_add(struct class_device *cdev, * traversal routines more complex */ sdev_printk(KERN_ERR, sdev, "FIXME driver has no support for subenclosures (%d)\n", - buf[1]); + hdr_buf[1]); goto err_free; } @@ -456,23 +459,22 @@ static int ses_intf_add(struct class_device *cdev, if (!buf) goto err_free; - ses_dev->page1 = buf; - ses_dev->page1_len = len; - result = ses_recv_diag(sdev, 1, buf, len); if (result) goto recv_failed; types = buf[10]; - len = buf[11]; - type_ptr = buf + 12 + len; + type_ptr = buf + 12 + buf[11]; for (i = 0; i < types; i++, type_ptr += 4) { if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) components += type_ptr[1]; } + ses_dev->page1 = buf; + ses_dev->page1_len = len; + buf = NULL; result = ses_recv_diag(sdev, 2, hdr_buf, INIT_ALLOC_SIZE); if (result) @@ -489,28 +491,29 @@ static int ses_intf_add(struct class_device *cdev, goto recv_failed; ses_dev->page2 = buf; ses_dev->page2_len = len; + buf = NULL; /* The additional information page --- allows us * to match up the devices */ result = ses_recv_diag(sdev, 10, hdr_buf, INIT_ALLOC_SIZE); - if (result) - goto no_page10; - - len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; - buf = kzalloc(len, GFP_KERNEL); - if (!buf) - goto err_free; - - result = ses_recv_diag(sdev, 10, buf, len); - if (result) - goto recv_failed; - ses_dev->page10 = buf; - ses_dev->page10_len = len; + if (!result) { + + len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + goto err_free; + + result = ses_recv_diag(sdev, 10, buf, len); + if (result) + goto recv_failed; + ses_dev->page10 = buf; + ses_dev->page10_len = len; + buf = NULL; + } - no_page10: - scomp = kmalloc(sizeof(struct ses_component) * components, GFP_KERNEL); + scomp = kzalloc(sizeof(struct ses_component) * components, GFP_KERNEL); if (!scomp) - goto err_free; + goto err_free; edev = enclosure_register(cdev->dev, sdev->sdev_gendev.bus_id, components, &ses_enclosure_callbacks); @@ -521,17 +524,18 @@ static int ses_intf_add(struct class_device *cdev, edev->scratch = ses_dev; for (i = 0; i < components; i++) - edev->component[i].scratch = scomp++; + edev->component[i].scratch = scomp + i; /* Page 7 for the descriptors is optional */ - buf = NULL; result = ses_recv_diag(sdev, 7, hdr_buf, INIT_ALLOC_SIZE); if (result) goto simple_populate; - len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; + page7_len = len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; /* add 1 for trailing '\0' we'll use */ buf = kzalloc(len + 1, GFP_KERNEL); + if (!buf) + goto simple_populate; result = ses_recv_diag(sdev, 7, buf, len); if (result) { simple_populate: @@ -544,7 +548,8 @@ static int ses_intf_add(struct class_device *cdev, len = (desc_ptr[2] << 8) + desc_ptr[3]; /* skip past overall descriptor */ desc_ptr += len + 4; - addl_desc_ptr = ses_dev->page10 + 8; + if (ses_dev->page10) + addl_desc_ptr = ses_dev->page10 + 8; } type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; components = 0; @@ -554,29 +559,35 @@ static int ses_intf_add(struct class_device *cdev, struct enclosure_component *ecomp; if (desc_ptr) { - len = (desc_ptr[2] << 8) + desc_ptr[3]; - desc_ptr += 4; - /* Add trailing zero - pushes into - * reserved space */ - desc_ptr[len] = '\0'; - name = desc_ptr; + if (desc_ptr >= buf + page7_len) { + desc_ptr = NULL; + } else { + len = (desc_ptr[2] << 8) + desc_ptr[3]; + desc_ptr += 4; + /* Add trailing zero - pushes into + * reserved space */ + desc_ptr[len] = '\0'; + name = desc_ptr; + } } - if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE && - type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE) - continue; - ecomp = enclosure_component_register(edev, + if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || + type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) { + + ecomp = enclosure_component_register(edev, components++, type_ptr[0], name); - if (desc_ptr) { - desc_ptr += len; - if (!IS_ERR(ecomp)) + + if (!IS_ERR(ecomp) && addl_desc_ptr) ses_process_descriptor(ecomp, addl_desc_ptr); - - if (addl_desc_ptr) - addl_desc_ptr += addl_desc_ptr[1] + 2; } + if (desc_ptr) + desc_ptr += len; + + if (addl_desc_ptr) + addl_desc_ptr += addl_desc_ptr[1] + 2; + } } kfree(buf); @@ -598,6 +609,7 @@ static int ses_intf_add(struct class_device *cdev, err = -ENODEV; err_free: kfree(buf); + kfree(scomp); kfree(ses_dev->page10); kfree(ses_dev->page2); kfree(ses_dev->page1); @@ -630,6 +642,7 @@ static void ses_intf_remove(struct class_device *cdev, ses_dev = edev->scratch; edev->scratch = NULL; + kfree(ses_dev->page10); kfree(ses_dev->page1); kfree(ses_dev->page2); kfree(ses_dev); diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 71952703125..0a52d9d2da2 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -17,7 +17,7 @@ Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support */ -static const char *verstr = "20080117"; +static const char *verstr = "20080221"; #include <linux/module.h> @@ -1172,7 +1172,7 @@ static int st_open(struct inode *inode, struct file *filp) STp->try_dio_now = STp->try_dio; STp->recover_count = 0; DEB( STp->nbr_waits = STp->nbr_finished = 0; - STp->nbr_requests = STp->nbr_dio = STp->nbr_pages = STp->nbr_combinable = 0; ) + STp->nbr_requests = STp->nbr_dio = STp->nbr_pages = 0; ) retval = check_tape(STp, filp); if (retval < 0) @@ -1226,8 +1226,8 @@ static int st_flush(struct file *filp, fl_owner_t id) } DEBC( if (STp->nbr_requests) - printk(KERN_DEBUG "%s: Number of r/w requests %d, dio used in %d, pages %d (%d).\n", - name, STp->nbr_requests, STp->nbr_dio, STp->nbr_pages, STp->nbr_combinable)); + printk(KERN_DEBUG "%s: Number of r/w requests %d, dio used in %d, pages %d.\n", + name, STp->nbr_requests, STp->nbr_dio, STp->nbr_pages)); if (STps->rw == ST_WRITING && !STp->pos_unknown) { struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat; @@ -1422,9 +1422,6 @@ static int setup_buffering(struct scsi_tape *STp, const char __user *buf, if (STbp->do_dio) { STp->nbr_dio++; STp->nbr_pages += STbp->do_dio; - for (i=1; i < STbp->do_dio; i++) - if (page_to_pfn(STbp->sg[i].page) == page_to_pfn(STbp->sg[i-1].page) + 1) - STp->nbr_combinable++; } ) } else diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index 6c807571297..5931726fcf9 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h @@ -164,7 +164,6 @@ struct scsi_tape { int nbr_requests; int nbr_dio; int nbr_pages; - int nbr_combinable; unsigned char last_cmnd[6]; unsigned char last_sense[16]; #endif diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 72f6d801535..654430edf74 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -461,30 +461,14 @@ static void stex_internal_copy(struct scsi_cmnd *cmd, } } -static int stex_direct_copy(struct scsi_cmnd *cmd, - const void *src, size_t count) -{ - size_t cp_len = count; - int n_elem = 0; - - n_elem = scsi_dma_map(cmd); - if (n_elem < 0) - return 0; - - stex_internal_copy(cmd, src, &cp_len, n_elem, ST_TO_CMD); - - scsi_dma_unmap(cmd); - - return cp_len == count; -} - static void stex_controller_info(struct st_hba *hba, struct st_ccb *ccb) { struct st_frame *p; size_t count = sizeof(struct st_frame); p = hba->copy_buffer; - stex_internal_copy(ccb->cmd, p, &count, ccb->sg_count, ST_FROM_CMD); + stex_internal_copy(ccb->cmd, p, &count, scsi_sg_count(ccb->cmd), + ST_FROM_CMD); memset(p->base, 0, sizeof(u32)*6); *(unsigned long *)(p->base) = pci_resource_start(hba->pdev, 0); p->rom_addr = 0; @@ -502,7 +486,8 @@ static void stex_controller_info(struct st_hba *hba, struct st_ccb *ccb) p->subid = hba->pdev->subsystem_vendor << 16 | hba->pdev->subsystem_device; - stex_internal_copy(ccb->cmd, p, &count, ccb->sg_count, ST_TO_CMD); + stex_internal_copy(ccb->cmd, p, &count, scsi_sg_count(ccb->cmd), + ST_TO_CMD); } static void @@ -569,8 +554,10 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)) unsigned char page; page = cmd->cmnd[2] & 0x3f; if (page == 0x8 || page == 0x3f) { - stex_direct_copy(cmd, ms10_caching_page, - sizeof(ms10_caching_page)); + size_t cp_len = sizeof(ms10_caching_page); + stex_internal_copy(cmd, ms10_caching_page, + &cp_len, scsi_sg_count(cmd), + ST_TO_CMD); cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8; done(cmd); } else @@ -599,8 +586,10 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)) if (id != host->max_id - 1) break; if (lun == 0 && (cmd->cmnd[1] & INQUIRY_EVPD) == 0) { - stex_direct_copy(cmd, console_inq_page, - sizeof(console_inq_page)); + size_t cp_len = sizeof(console_inq_page); + stex_internal_copy(cmd, console_inq_page, + &cp_len, scsi_sg_count(cmd), + ST_TO_CMD); cmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8; done(cmd); } else @@ -609,6 +598,7 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)) case PASSTHRU_CMD: if (cmd->cmnd[1] == PASSTHRU_GET_DRVVER) { struct st_drvver ver; + size_t cp_len = sizeof(ver); ver.major = ST_VER_MAJOR; ver.minor = ST_VER_MINOR; ver.oem = ST_OEM; @@ -616,7 +606,9 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)) ver.signature[0] = PASSTHRU_SIGNATURE; ver.console_id = host->max_id - 1; ver.host_no = hba->host->host_no; - cmd->result = stex_direct_copy(cmd, &ver, sizeof(ver)) ? + stex_internal_copy(cmd, &ver, &cp_len, + scsi_sg_count(cmd), ST_TO_CMD); + cmd->result = sizeof(ver) == cp_len ? DID_OK << 16 | COMMAND_COMPLETE << 8 : DID_ERROR << 16 | COMMAND_COMPLETE << 8; done(cmd); @@ -709,7 +701,7 @@ static void stex_copy_data(struct st_ccb *ccb, if (ccb->cmd == NULL) return; stex_internal_copy(ccb->cmd, - resp->variable, &count, ccb->sg_count, ST_TO_CMD); + resp->variable, &count, scsi_sg_count(ccb->cmd), ST_TO_CMD); } static void stex_ys_commands(struct st_hba *hba, @@ -734,7 +726,7 @@ static void stex_ys_commands(struct st_hba *hba, count = STEX_EXTRA_SIZE; stex_internal_copy(ccb->cmd, hba->copy_buffer, - &count, ccb->sg_count, ST_FROM_CMD); + &count, scsi_sg_count(ccb->cmd), ST_FROM_CMD); inq_data = (ST_INQ *)hba->copy_buffer; if (inq_data->DeviceTypeQualifier != 0) ccb->srb_status = SRB_STATUS_SELECTION_TIMEOUT; diff --git a/drivers/scsi/sym53c416.c b/drivers/scsi/sym53c416.c index 6325901e509..f7d279542fa 100644 --- a/drivers/scsi/sym53c416.c +++ b/drivers/scsi/sym53c416.c @@ -187,10 +187,10 @@ #define sym53c416_base_2 sym53c416_2 #define sym53c416_base_3 sym53c416_3 -static unsigned int sym53c416_base[2] = {0,0}; -static unsigned int sym53c416_base_1[2] = {0,0}; -static unsigned int sym53c416_base_2[2] = {0,0}; -static unsigned int sym53c416_base_3[2] = {0,0}; +static unsigned int sym53c416_base[2]; +static unsigned int sym53c416_base_1[2]; +static unsigned int sym53c416_base_2[2]; +static unsigned int sym53c416_base_3[2]; #endif @@ -621,25 +621,25 @@ int __init sym53c416_detect(struct scsi_host_template *tpnt) int ints[3]; ints[0] = 2; - if(sym53c416_base) + if(sym53c416_base[0]) { ints[1] = sym53c416_base[0]; ints[2] = sym53c416_base[1]; sym53c416_setup(NULL, ints); } - if(sym53c416_base_1) + if(sym53c416_base_1[0]) { ints[1] = sym53c416_base_1[0]; ints[2] = sym53c416_base_1[1]; sym53c416_setup(NULL, ints); } - if(sym53c416_base_2) + if(sym53c416_base_2[0]) { ints[1] = sym53c416_base_2[0]; ints[2] = sym53c416_base_2[1]; sym53c416_setup(NULL, ints); } - if(sym53c416_base_3) + if(sym53c416_base_3[0]) { ints[1] = sym53c416_base_3[0]; ints[2] = sym53c416_base_3[1]; |