Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ec5ba4d3 authored by Michal Kazior's avatar Michal Kazior Committed by Kalle Valo
Browse files

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: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 145cc121
Loading
Loading
Loading
Loading
+37 −6
Original line number Diff line number Diff line
@@ -1121,6 +1121,40 @@ static int ath10k_pci_post_rx(struct ath10k *ar)
	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)
{
	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;
	}

	ath10k_ce_enable_interrupts(ar);
	ath10k_pci_irq_enable(ar);

	/* Post buffers once to start things off. */
	ret = ath10k_pci_post_rx(ar);
@@ -1152,7 +1186,7 @@ static int ath10k_pci_hif_start(struct ath10k *ar)
	return 0;

err_stop:
	ath10k_ce_disable_interrupts(ar);
	ath10k_pci_irq_disable(ar);
	ath10k_pci_free_irq(ar);
	ath10k_pci_kill_tasklet(ar);
err_early_irq:
@@ -1275,10 +1309,7 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
	if (WARN_ON(!ar_pci->started))
		return;

	ret = ath10k_ce_disable_interrupts(ar);
	if (ret)
		ath10k_warn("failed to disable CE interrupts: %d\n", ret);

	ath10k_pci_irq_disable(ar);
	ath10k_pci_free_irq(ar);
	ath10k_pci_kill_tasklet(ar);