Commit 6cd60b37 authored by Nikith Ganigarakoppal's avatar Nikith Ganigarakoppal Committed by James Bottomley

[SCSI] pm80xx: Tasklets synchronization fix.

When multiple vectors are used, the vector variable is over written,
resulting in unhandled operation for those vectors.
This fix prevents the problem by maitaining HBA instance and
vector values for each irq.

[jejb: checkpatch fixes]
Signed-off-by: Nikith.Ganigarakoppal@pmcs.com
Signed-off-by: Anandkumar.Santhanam@pmcs.com
Reviewed-by: default avatarJack Wang <jinpu.wang@profitbricks.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent 7d029005
...@@ -175,20 +175,16 @@ static void pm8001_free(struct pm8001_hba_info *pm8001_ha) ...@@ -175,20 +175,16 @@ static void pm8001_free(struct pm8001_hba_info *pm8001_ha)
static void pm8001_tasklet(unsigned long opaque) static void pm8001_tasklet(unsigned long opaque)
{ {
struct pm8001_hba_info *pm8001_ha; struct pm8001_hba_info *pm8001_ha;
u32 vec; struct isr_param *irq_vector;
pm8001_ha = (struct pm8001_hba_info *)opaque;
irq_vector = (struct isr_param *)opaque;
pm8001_ha = irq_vector->drv_inst;
if (unlikely(!pm8001_ha)) if (unlikely(!pm8001_ha))
BUG_ON(1); BUG_ON(1);
vec = pm8001_ha->int_vector; PM8001_CHIP_DISP->isr(pm8001_ha, irq_vector->irq_id);
PM8001_CHIP_DISP->isr(pm8001_ha, vec);
} }
#endif #endif
static struct pm8001_hba_info *outq_to_hba(u8 *outq)
{
return container_of((outq - *outq), struct pm8001_hba_info, outq[0]);
}
/** /**
* pm8001_interrupt_handler_msix - main MSIX interrupt handler. * pm8001_interrupt_handler_msix - main MSIX interrupt handler.
* It obtains the vector number and calls the equivalent bottom * It obtains the vector number and calls the equivalent bottom
...@@ -198,18 +194,20 @@ static struct pm8001_hba_info *outq_to_hba(u8 *outq) ...@@ -198,18 +194,20 @@ static struct pm8001_hba_info *outq_to_hba(u8 *outq)
*/ */
static irqreturn_t pm8001_interrupt_handler_msix(int irq, void *opaque) static irqreturn_t pm8001_interrupt_handler_msix(int irq, void *opaque)
{ {
struct pm8001_hba_info *pm8001_ha = outq_to_hba(opaque); struct isr_param *irq_vector;
u8 outq = *(u8 *)opaque; struct pm8001_hba_info *pm8001_ha;
irqreturn_t ret = IRQ_HANDLED; irqreturn_t ret = IRQ_HANDLED;
irq_vector = (struct isr_param *)opaque;
pm8001_ha = irq_vector->drv_inst;
if (unlikely(!pm8001_ha)) if (unlikely(!pm8001_ha))
return IRQ_NONE; return IRQ_NONE;
if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha)) if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha))
return IRQ_NONE; return IRQ_NONE;
pm8001_ha->int_vector = outq;
#ifdef PM8001_USE_TASKLET #ifdef PM8001_USE_TASKLET
tasklet_schedule(&pm8001_ha->tasklet); tasklet_schedule(&pm8001_ha->tasklet[irq_vector->irq_id]);
#else #else
ret = PM8001_CHIP_DISP->isr(pm8001_ha, outq); ret = PM8001_CHIP_DISP->isr(pm8001_ha, irq_vector->irq_id);
#endif #endif
return ret; return ret;
} }
...@@ -230,9 +228,8 @@ static irqreturn_t pm8001_interrupt_handler_intx(int irq, void *dev_id) ...@@ -230,9 +228,8 @@ static irqreturn_t pm8001_interrupt_handler_intx(int irq, void *dev_id)
if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha)) if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha))
return IRQ_NONE; return IRQ_NONE;
pm8001_ha->int_vector = 0;
#ifdef PM8001_USE_TASKLET #ifdef PM8001_USE_TASKLET
tasklet_schedule(&pm8001_ha->tasklet); tasklet_schedule(&pm8001_ha->tasklet[0]);
#else #else
ret = PM8001_CHIP_DISP->isr(pm8001_ha, 0); ret = PM8001_CHIP_DISP->isr(pm8001_ha, 0);
#endif #endif
...@@ -457,7 +454,7 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev, ...@@ -457,7 +454,7 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev,
{ {
struct pm8001_hba_info *pm8001_ha; struct pm8001_hba_info *pm8001_ha;
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
int j;
pm8001_ha = sha->lldd_ha; pm8001_ha = sha->lldd_ha;
if (!pm8001_ha) if (!pm8001_ha)
...@@ -480,12 +477,14 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev, ...@@ -480,12 +477,14 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev,
pm8001_ha->iomb_size = IOMB_SIZE_SPC; pm8001_ha->iomb_size = IOMB_SIZE_SPC;
#ifdef PM8001_USE_TASKLET #ifdef PM8001_USE_TASKLET
/** /* Tasklet for non msi-x interrupt handler */
* default tasklet for non msi-x interrupt handler/first msi-x if ((!pdev->msix_cap) || (pm8001_ha->chip_id == chip_8001))
* interrupt handler tasklet_init(&pm8001_ha->tasklet[0], pm8001_tasklet,
**/ (unsigned long)&(pm8001_ha->irq_vector[0]));
tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet, else
(unsigned long)pm8001_ha); for (j = 0; j < PM8001_MAX_MSIX_VEC; j++)
tasklet_init(&pm8001_ha->tasklet[j], pm8001_tasklet,
(unsigned long)&(pm8001_ha->irq_vector[j]));
#endif #endif
pm8001_ioremap(pm8001_ha); pm8001_ioremap(pm8001_ha);
if (!pm8001_alloc(pm8001_ha, ent)) if (!pm8001_alloc(pm8001_ha, ent))
...@@ -733,19 +732,20 @@ static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha) ...@@ -733,19 +732,20 @@ static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha)
"pci_enable_msix request ret:%d no of intr %d\n", "pci_enable_msix request ret:%d no of intr %d\n",
rc, pm8001_ha->number_of_intr)); rc, pm8001_ha->number_of_intr));
for (i = 0; i < number_of_intr; i++)
pm8001_ha->outq[i] = i;
for (i = 0; i < number_of_intr; i++) { for (i = 0; i < number_of_intr; i++) {
snprintf(intr_drvname[i], sizeof(intr_drvname[0]), snprintf(intr_drvname[i], sizeof(intr_drvname[0]),
DRV_NAME"%d", i); DRV_NAME"%d", i);
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, if (request_irq(pm8001_ha->msix_entries[i].vector,
pm8001_interrupt_handler_msix, flag, pm8001_interrupt_handler_msix, flag,
intr_drvname[i], &pm8001_ha->outq[i])) { intr_drvname[i], &(pm8001_ha->irq_vector[i]))) {
for (j = 0; j < i; j++) for (j = 0; j < i; j++)
free_irq( free_irq(
pm8001_ha->msix_entries[j].vector, pm8001_ha->msix_entries[j].vector,
&pm8001_ha->outq[j]); &(pm8001_ha->irq_vector[i]));
pci_disable_msix(pm8001_ha->pdev); pci_disable_msix(pm8001_ha->pdev);
break; break;
} }
...@@ -907,7 +907,7 @@ static void pm8001_pci_remove(struct pci_dev *pdev) ...@@ -907,7 +907,7 @@ static void pm8001_pci_remove(struct pci_dev *pdev)
{ {
struct sas_ha_struct *sha = pci_get_drvdata(pdev); struct sas_ha_struct *sha = pci_get_drvdata(pdev);
struct pm8001_hba_info *pm8001_ha; struct pm8001_hba_info *pm8001_ha;
int i; int i, j;
pm8001_ha = sha->lldd_ha; pm8001_ha = sha->lldd_ha;
sas_unregister_ha(sha); sas_unregister_ha(sha);
sas_remove_host(pm8001_ha->shost); sas_remove_host(pm8001_ha->shost);
...@@ -921,13 +921,18 @@ static void pm8001_pci_remove(struct pci_dev *pdev) ...@@ -921,13 +921,18 @@ static void pm8001_pci_remove(struct pci_dev *pdev)
synchronize_irq(pm8001_ha->msix_entries[i].vector); synchronize_irq(pm8001_ha->msix_entries[i].vector);
for (i = 0; i < pm8001_ha->number_of_intr; i++) for (i = 0; i < pm8001_ha->number_of_intr; i++)
free_irq(pm8001_ha->msix_entries[i].vector, free_irq(pm8001_ha->msix_entries[i].vector,
&pm8001_ha->outq[i]); &(pm8001_ha->irq_vector[i]));
pci_disable_msix(pdev); pci_disable_msix(pdev);
#else #else
free_irq(pm8001_ha->irq, sha); free_irq(pm8001_ha->irq, sha);
#endif #endif
#ifdef PM8001_USE_TASKLET #ifdef PM8001_USE_TASKLET
tasklet_kill(&pm8001_ha->tasklet); /* For non-msix and msix interrupts */
if ((!pdev->msix_cap) || (pm8001_ha->chip_id == chip_8001))
tasklet_kill(&pm8001_ha->tasklet[0]);
else
for (j = 0; j < PM8001_MAX_MSIX_VEC; j++)
tasklet_kill(&pm8001_ha->tasklet[j]);
#endif #endif
pm8001_free(pm8001_ha); pm8001_free(pm8001_ha);
kfree(sha->sas_phy); kfree(sha->sas_phy);
...@@ -948,7 +953,7 @@ static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state) ...@@ -948,7 +953,7 @@ static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{ {
struct sas_ha_struct *sha = pci_get_drvdata(pdev); struct sas_ha_struct *sha = pci_get_drvdata(pdev);
struct pm8001_hba_info *pm8001_ha; struct pm8001_hba_info *pm8001_ha;
int i; int i, j;
u32 device_state; u32 device_state;
pm8001_ha = sha->lldd_ha; pm8001_ha = sha->lldd_ha;
flush_workqueue(pm8001_wq); flush_workqueue(pm8001_wq);
...@@ -964,13 +969,18 @@ static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state) ...@@ -964,13 +969,18 @@ static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state)
synchronize_irq(pm8001_ha->msix_entries[i].vector); synchronize_irq(pm8001_ha->msix_entries[i].vector);
for (i = 0; i < pm8001_ha->number_of_intr; i++) for (i = 0; i < pm8001_ha->number_of_intr; i++)
free_irq(pm8001_ha->msix_entries[i].vector, free_irq(pm8001_ha->msix_entries[i].vector,
&pm8001_ha->outq[i]); &(pm8001_ha->irq_vector[i]));
pci_disable_msix(pdev); pci_disable_msix(pdev);
#else #else
free_irq(pm8001_ha->irq, sha); free_irq(pm8001_ha->irq, sha);
#endif #endif
#ifdef PM8001_USE_TASKLET #ifdef PM8001_USE_TASKLET
tasklet_kill(&pm8001_ha->tasklet); /* For non-msix and msix interrupts */
if ((!pdev->msix_cap) || (pm8001_ha->chip_id == chip_8001))
tasklet_kill(&pm8001_ha->tasklet[0]);
else
for (j = 0; j < PM8001_MAX_MSIX_VEC; j++)
tasklet_kill(&pm8001_ha->tasklet[j]);
#endif #endif
device_state = pci_choose_state(pdev, state); device_state = pci_choose_state(pdev, state);
pm8001_printk("pdev=0x%p, slot=%s, entering " pm8001_printk("pdev=0x%p, slot=%s, entering "
...@@ -993,7 +1003,7 @@ static int pm8001_pci_resume(struct pci_dev *pdev) ...@@ -993,7 +1003,7 @@ static int pm8001_pci_resume(struct pci_dev *pdev)
struct sas_ha_struct *sha = pci_get_drvdata(pdev); struct sas_ha_struct *sha = pci_get_drvdata(pdev);
struct pm8001_hba_info *pm8001_ha; struct pm8001_hba_info *pm8001_ha;
int rc; int rc;
u8 i = 0; u8 i = 0, j;
u32 device_state; u32 device_state;
pm8001_ha = sha->lldd_ha; pm8001_ha = sha->lldd_ha;
device_state = pdev->current_state; device_state = pdev->current_state;
...@@ -1033,10 +1043,14 @@ static int pm8001_pci_resume(struct pci_dev *pdev) ...@@ -1033,10 +1043,14 @@ static int pm8001_pci_resume(struct pci_dev *pdev)
if (rc) if (rc)
goto err_out_disable; goto err_out_disable;
#ifdef PM8001_USE_TASKLET #ifdef PM8001_USE_TASKLET
/* default tasklet for non msi-x interrupt handler/first msi-x /* Tasklet for non msi-x interrupt handler */
* interrupt handler */ if ((!pdev->msix_cap) || (pm8001_ha->chip_id == chip_8001))
tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet, tasklet_init(&pm8001_ha->tasklet[0], pm8001_tasklet,
(unsigned long)pm8001_ha); (unsigned long)&(pm8001_ha->irq_vector[0]));
else
for (j = 0; j < PM8001_MAX_MSIX_VEC; j++)
tasklet_init(&pm8001_ha->tasklet[j], pm8001_tasklet,
(unsigned long)&(pm8001_ha->irq_vector[j]));
#endif #endif
PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0); PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0);
if (pm8001_ha->chip_id != chip_8001) { if (pm8001_ha->chip_id != chip_8001) {
......
...@@ -466,6 +466,10 @@ struct pm8001_hba_memspace { ...@@ -466,6 +466,10 @@ struct pm8001_hba_memspace {
u64 membase; u64 membase;
u32 memsize; u32 memsize;
}; };
struct isr_param {
struct pm8001_hba_info *drv_inst;
u32 irq_id;
};
struct pm8001_hba_info { struct pm8001_hba_info {
char name[PM8001_NAME_LENGTH]; char name[PM8001_NAME_LENGTH];
struct list_head list; struct list_head list;
...@@ -519,14 +523,13 @@ struct pm8001_hba_info { ...@@ -519,14 +523,13 @@ struct pm8001_hba_info {
int number_of_intr;/*will be used in remove()*/ int number_of_intr;/*will be used in remove()*/
#endif #endif
#ifdef PM8001_USE_TASKLET #ifdef PM8001_USE_TASKLET
struct tasklet_struct tasklet; struct tasklet_struct tasklet[PM8001_MAX_MSIX_VEC];
#endif #endif
u32 logging_level; u32 logging_level;
u32 fw_status; u32 fw_status;
u32 smp_exp_mode; u32 smp_exp_mode;
u32 int_vector;
const struct firmware *fw_image; const struct firmware *fw_image;
u8 outq[PM8001_MAX_MSIX_VEC]; struct isr_param irq_vector[PM8001_MAX_MSIX_VEC];
}; };
struct pm8001_work { struct pm8001_work {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment