mirror of https://gitee.com/openkylin/linux.git
ath10k: make sure to really disable irqs
This fixes two corner cases. One is a race between disabling copy engine interrupts and unhandled pending interrupts on the host. This could end up with a runaway tasklet and consequently memory leak of a few copy engine rx buffers. The other one is an unexpected (and non-maskable via device CSR) MSI fw indication interrupt during teardown. This could trigger the same problem as the first corner case. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
This commit is contained in:
parent
145cc1214a
commit
ec5ba4d3b6
|
@ -1121,6 +1121,40 @@ static int ath10k_pci_post_rx(struct ath10k *ar)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ath10k_pci_irq_disable(struct ath10k *ar)
|
||||||
|
{
|
||||||
|
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ath10k_ce_disable_interrupts(ar);
|
||||||
|
|
||||||
|
/* Regardless how many interrupts were assigned for MSI the first one
|
||||||
|
* is always used for firmware indications (crashes). There's no way to
|
||||||
|
* mask the irq in the device so call disable_irq(). Legacy (shared)
|
||||||
|
* interrupts can be masked on the device though.
|
||||||
|
*/
|
||||||
|
if (ar_pci->num_msi_intrs > 0)
|
||||||
|
disable_irq(ar_pci->pdev->irq);
|
||||||
|
else
|
||||||
|
ath10k_pci_disable_and_clear_legacy_irq(ar);
|
||||||
|
|
||||||
|
for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
|
||||||
|
synchronize_irq(ar_pci->pdev->irq + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ath10k_pci_irq_enable(struct ath10k *ar)
|
||||||
|
{
|
||||||
|
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
||||||
|
|
||||||
|
ath10k_ce_enable_interrupts(ar);
|
||||||
|
|
||||||
|
/* See comment in ath10k_pci_irq_disable() */
|
||||||
|
if (ar_pci->num_msi_intrs > 0)
|
||||||
|
enable_irq(ar_pci->pdev->irq);
|
||||||
|
else
|
||||||
|
ath10k_pci_enable_legacy_irq(ar);
|
||||||
|
}
|
||||||
|
|
||||||
static int ath10k_pci_hif_start(struct ath10k *ar)
|
static int ath10k_pci_hif_start(struct ath10k *ar)
|
||||||
{
|
{
|
||||||
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
||||||
|
@ -1138,7 +1172,7 @@ static int ath10k_pci_hif_start(struct ath10k *ar)
|
||||||
goto err_early_irq;
|
goto err_early_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
ath10k_ce_enable_interrupts(ar);
|
ath10k_pci_irq_enable(ar);
|
||||||
|
|
||||||
/* Post buffers once to start things off. */
|
/* Post buffers once to start things off. */
|
||||||
ret = ath10k_pci_post_rx(ar);
|
ret = ath10k_pci_post_rx(ar);
|
||||||
|
@ -1152,7 +1186,7 @@ static int ath10k_pci_hif_start(struct ath10k *ar)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_stop:
|
err_stop:
|
||||||
ath10k_ce_disable_interrupts(ar);
|
ath10k_pci_irq_disable(ar);
|
||||||
ath10k_pci_free_irq(ar);
|
ath10k_pci_free_irq(ar);
|
||||||
ath10k_pci_kill_tasklet(ar);
|
ath10k_pci_kill_tasklet(ar);
|
||||||
err_early_irq:
|
err_early_irq:
|
||||||
|
@ -1275,10 +1309,7 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
|
||||||
if (WARN_ON(!ar_pci->started))
|
if (WARN_ON(!ar_pci->started))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ret = ath10k_ce_disable_interrupts(ar);
|
ath10k_pci_irq_disable(ar);
|
||||||
if (ret)
|
|
||||||
ath10k_warn("failed to disable CE interrupts: %d\n", ret);
|
|
||||||
|
|
||||||
ath10k_pci_free_irq(ar);
|
ath10k_pci_free_irq(ar);
|
||||||
ath10k_pci_kill_tasklet(ar);
|
ath10k_pci_kill_tasklet(ar);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue