From 9f17609968af25a4082d4329abbc6e4f52eda7cd Mon Sep 17 00:00:00 2001 From: Bradley Grove Date: Wed, 9 Jul 2014 17:20:23 +0530 Subject: pm8001: Fix hibernation issue During hibernation, the HBA firmware may lose power and forget the device id info. This causes the HBA to reject IO upon resume. The fix is to call the libsas power management routines to make the domain device forgetful. This fixes bug 76681: https://bugzilla.kernel.org/show_bug.cgi?id=76681 Signed-off-by: Bradley Grove Acked-by: Jack Wang Signed-off-by: Christoph Hellwig --- drivers/scsi/pm8001/pm8001_init.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/pm8001/pm8001_init.c') diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index e90c89f1d480..aad060aa3c0c 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -964,6 +964,7 @@ static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state) int i, j; u32 device_state; pm8001_ha = sha->lldd_ha; + sas_suspend_ha(sha); flush_workqueue(pm8001_wq); scsi_block_requests(pm8001_ha->shost); if (!pdev->pm_cap) { @@ -1013,6 +1014,7 @@ static int pm8001_pci_resume(struct pci_dev *pdev) int rc; u8 i = 0, j; u32 device_state; + DECLARE_COMPLETION_ONSTACK(completion); pm8001_ha = sha->lldd_ha; device_state = pdev->current_state; @@ -1033,7 +1035,7 @@ static int pm8001_pci_resume(struct pci_dev *pdev) rc = pci_go_44(pdev); if (rc) goto err_out_disable; - + sas_prep_resume_ha(sha); /* chip soft rst only for spc */ if (pm8001_ha->chip_id == chip_8001) { PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha); @@ -1065,7 +1067,13 @@ static int pm8001_pci_resume(struct pci_dev *pdev) for (i = 1; i < pm8001_ha->number_of_intr; i++) PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, i); } - scsi_unblock_requests(pm8001_ha->shost); + pm8001_ha->flags = PM8001F_RUN_TIME; + for (i = 0; i < pm8001_ha->chip->n_phy; i++) { + pm8001_ha->phy[i].enable_completion = &completion; + PM8001_CHIP_DISP->phy_start_req(pm8001_ha, i); + wait_for_completion(&completion); + } + sas_resume_ha(sha); return 0; err_out_disable: -- cgit v1.2.3 From 646cdf0083e3d4a9b995f37b72c3c8a22d9307de Mon Sep 17 00:00:00 2001 From: Tomas Henzl Date: Wed, 9 Jul 2014 17:21:01 +0530 Subject: pm8001: add a new spinlock to protect the CCB Patch adds a new spinlock to protect the ccb management. It may happen that concurrent threads become the same tag value from the 'alloc' function', the spinlock prevents this situation. Signed-off-by: Tomas Henzl Acked-by: Suresh Thiagarajan Acked-by: Jack Wang Signed-off-by: Christoph Hellwig --- drivers/scsi/pm8001/pm8001_init.c | 1 + drivers/scsi/pm8001/pm8001_sas.c | 7 ++++++- drivers/scsi/pm8001/pm8001_sas.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/pm8001/pm8001_init.c') diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index aad060aa3c0c..acef7d6a5790 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -246,6 +246,7 @@ static int pm8001_alloc(struct pm8001_hba_info *pm8001_ha, { int i; spin_lock_init(&pm8001_ha->lock); + spin_lock_init(&pm8001_ha->bitmap_lock); PM8001_INIT_DBG(pm8001_ha, pm8001_printk("pm8001_alloc: PHY:%x\n", pm8001_ha->chip->n_phy)); diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index be55859042cd..34cea8291772 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -77,11 +77,16 @@ inline int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out) { unsigned int tag; void *bitmap = pm8001_ha->tags; + unsigned long flags; + spin_lock_irqsave(&pm8001_ha->bitmap_lock, flags); tag = find_first_zero_bit(bitmap, pm8001_ha->tags_num); - if (tag >= pm8001_ha->tags_num) + if (tag >= pm8001_ha->tags_num) { + spin_unlock_irqrestore(&pm8001_ha->bitmap_lock, flags); return -SAS_QUEUE_FULL; + } set_bit(tag, bitmap); + spin_unlock_irqrestore(&pm8001_ha->bitmap_lock, flags); *tag_out = tag; return 0; } diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h index 14106adec009..f6b2ac59dae4 100644 --- a/drivers/scsi/pm8001/pm8001_sas.h +++ b/drivers/scsi/pm8001/pm8001_sas.h @@ -475,6 +475,7 @@ struct pm8001_hba_info { struct list_head list; unsigned long flags; spinlock_t lock;/* host-wide lock */ + spinlock_t bitmap_lock; struct pci_dev *pdev;/* our device */ struct device *dev; struct pm8001_hba_memspace io_mem[6]; -- cgit v1.2.3 From 5b4ce882d56e5356ea38ab86f6da91df4ac57842 Mon Sep 17 00:00:00 2001 From: Tomas Henzl Date: Wed, 9 Jul 2014 17:21:11 +0530 Subject: pm8001: more fixes to honor return value The driver ignores the return value in a lot of places, fix it at least somewhere (and release the resources in such cases), to avoid that bad things happen. A memory leak is fixed too. Signed-off-by: Tomas Henzl Acked-by: Suresh Thiagarajan Acked-by: Jack Wang Signed-off-by: Christoph Hellwig --- drivers/scsi/pm8001/pm8001_ctl.c | 5 ++++- drivers/scsi/pm8001/pm8001_init.c | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/pm8001/pm8001_init.c') diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c index a368d77b8d41..ade62c8a19fa 100644 --- a/drivers/scsi/pm8001/pm8001_ctl.c +++ b/drivers/scsi/pm8001/pm8001_ctl.c @@ -397,7 +397,10 @@ static ssize_t pm8001_ctl_bios_version_show(struct device *cdev, payload.func_specific = kzalloc(4096, GFP_KERNEL); if (!payload.func_specific) return -ENOMEM; - PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload); + if (PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload)) { + kfree(payload.func_specific); + return -ENOMEM; + } wait_for_completion(&completion); virt_addr = pm8001_ha->memoryMap.region[NVMD].virt_ptr; for (bios_index = BIOSOFFSET; bios_index < BIOS_OFFSET_LIMIT; diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index acef7d6a5790..236fba40e540 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -622,6 +622,8 @@ static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha) DECLARE_COMPLETION_ONSTACK(completion); struct pm8001_ioctl_payload payload; u16 deviceid; + int rc; + pci_read_config_word(pm8001_ha->pdev, PCI_DEVICE_ID, &deviceid); pm8001_ha->nvmd_completion = &completion; @@ -639,7 +641,16 @@ static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha) } payload.offset = 0; payload.func_specific = kzalloc(payload.length, GFP_KERNEL); - PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload); + if (!payload.func_specific) { + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("mem alloc fail\n")); + return; + } + rc = PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload); + if (rc) { + kfree(payload.func_specific); + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("nvmd failed\n")); + return; + } wait_for_completion(&completion); for (i = 0, j = 0; i <= 7; i++, j++) { @@ -662,6 +673,7 @@ static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha) pm8001_printk("phy %d sas_addr = %016llx\n", i, pm8001_ha->phy[i].dev_sas_addr)); } + kfree(payload.func_specific); #else for (i = 0; i < pm8001_ha->chip->n_phy; i++) { pm8001_ha->phy[i].dev_sas_addr = 0x50010c600047f9d0ULL; @@ -685,6 +697,7 @@ static int pm8001_get_phy_settings_info(struct pm8001_hba_info *pm8001_ha) /*OPTION ROM FLASH read for the SPC cards */ DECLARE_COMPLETION_ONSTACK(completion); struct pm8001_ioctl_payload payload; + int rc; pm8001_ha->nvmd_completion = &completion; /* SAS ADDRESS read from flash / EEPROM */ @@ -695,7 +708,12 @@ static int pm8001_get_phy_settings_info(struct pm8001_hba_info *pm8001_ha) if (!payload.func_specific) return -ENOMEM; /* Read phy setting values from flash */ - PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload); + rc = PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload); + if (rc) { + kfree(payload.func_specific); + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("nvmd failed\n")); + return -ENOMEM; + } wait_for_completion(&completion); pm8001_set_phy_profile(pm8001_ha, sizeof(u8), payload.func_specific); kfree(payload.func_specific); -- cgit v1.2.3 From 5607de73dbdaeeef67aaae69c769424c638cf3d9 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Wed, 16 Jul 2014 20:05:21 +0200 Subject: pm8001: Fix invalid return when request_irq() failed When a call to request_irq() failed pm8001_setup_msix() still returns the success. This udate fixes the described misbehaviour. Signed-off-by: Alexander Gordeev Acked-by: Jack Wang Signed-off-by: Christoph Hellwig --- drivers/scsi/pm8001/pm8001_init.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/pm8001/pm8001_init.c') diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index 236fba40e540..e49623a897a7 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -763,9 +763,10 @@ static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha) pm8001_ha->irq_vector[i].irq_id = i; pm8001_ha->irq_vector[i].drv_inst = pm8001_ha; - if (request_irq(pm8001_ha->msix_entries[i].vector, + rc = request_irq(pm8001_ha->msix_entries[i].vector, pm8001_interrupt_handler_msix, flag, - intr_drvname[i], &(pm8001_ha->irq_vector[i]))) { + intr_drvname[i], &(pm8001_ha->irq_vector[i])); + if (rc) { for (j = 0; j < i; j++) free_irq( pm8001_ha->msix_entries[j].vector, -- cgit v1.2.3 From b4d511e5c7c3d063c6cb1110797eb8221c5b2c09 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Wed, 16 Jul 2014 20:05:22 +0200 Subject: pm8001: Use pci_enable_msix_exact() instead of pci_enable_msix() As result of deprecation of MSI-X/MSI enablement functions pci_enable_msix() and pci_enable_msi_block() all drivers using these two interfaces need to be updated to use the new pci_enable_msi_range() or pci_enable_msi_exact() and pci_enable_msix_range() or pci_enable_msix_exact() interfaces. Signed-off-by: Alexander Gordeev Reviewed-by: Jack Wang Signed-off-by: Christoph Hellwig --- drivers/scsi/pm8001/pm8001_init.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) (limited to 'drivers/scsi/pm8001/pm8001_init.c') diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index e49623a897a7..666bf5af06e2 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -748,34 +748,35 @@ static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha) sizeof(pm8001_ha->msix_entries[0]); for (i = 0; i < max_entry ; i++) pm8001_ha->msix_entries[i].entry = i; - rc = pci_enable_msix(pm8001_ha->pdev, pm8001_ha->msix_entries, + rc = pci_enable_msix_exact(pm8001_ha->pdev, pm8001_ha->msix_entries, number_of_intr); pm8001_ha->number_of_intr = number_of_intr; - if (!rc) { - PM8001_INIT_DBG(pm8001_ha, pm8001_printk( - "pci_enable_msix request ret:%d no of intr %d\n", - rc, pm8001_ha->number_of_intr)); + if (rc) + return rc; + PM8001_INIT_DBG(pm8001_ha, pm8001_printk( + "pci_enable_msix_exact request ret:%d no of intr %d\n", + rc, pm8001_ha->number_of_intr)); - for (i = 0; i < number_of_intr; i++) { - snprintf(intr_drvname[i], sizeof(intr_drvname[0]), - DRV_NAME"%d", i); - pm8001_ha->irq_vector[i].irq_id = i; - pm8001_ha->irq_vector[i].drv_inst = pm8001_ha; + for (i = 0; i < number_of_intr; i++) { + snprintf(intr_drvname[i], sizeof(intr_drvname[0]), + DRV_NAME"%d", i); + pm8001_ha->irq_vector[i].irq_id = i; + pm8001_ha->irq_vector[i].drv_inst = pm8001_ha; - rc = request_irq(pm8001_ha->msix_entries[i].vector, - pm8001_interrupt_handler_msix, flag, - intr_drvname[i], &(pm8001_ha->irq_vector[i])); - if (rc) { - for (j = 0; j < i; j++) - free_irq( - pm8001_ha->msix_entries[j].vector, + rc = request_irq(pm8001_ha->msix_entries[i].vector, + pm8001_interrupt_handler_msix, flag, + intr_drvname[i], &(pm8001_ha->irq_vector[i])); + if (rc) { + for (j = 0; j < i; j++) { + free_irq(pm8001_ha->msix_entries[j].vector, &(pm8001_ha->irq_vector[i])); - pci_disable_msix(pm8001_ha->pdev); - break; } + pci_disable_msix(pm8001_ha->pdev); + break; } } + return rc; } #endif -- cgit v1.2.3