Historical ath9k-devel archives
 help / color / mirror / Atom feed
* [ath9k-devel] [RFT] ath10k: serialize CE completions properly
       [not found] <87haie3mlw.fsf_-_@kamboji.qca.qualcomm.com>
@ 2013-05-08 10:00 ` Michal Kazior
  2013-05-09  4:07   ` Sujith Manoharan
  2013-05-10 12:43   ` [ath9k-devel] [PATCH] " Michal Kazior
  0 siblings, 2 replies; 4+ messages in thread
From: Michal Kazior @ 2013-05-08 10:00 UTC (permalink / raw)
  To: ath9k-devel

This fixes memory leak when doing heavy TX.

ath10k_pci_check_process_ce() was checking
`compl_processing` and would call
ath10k_pci_process_ce() if its false. The
ath10k_pci_process_ce() would then set it to true
if there's at least one completion. The lock
protecting the variable is not held between
checking and setting the variable, meaning it is a
possible race condition.

If we then assume ath10k_pci_process_ce() is
called simultaneusly from different contexts we
have a race. And apparently this was possible:

ath10k_pci_check_process_ce() was called from
ath10k_ce_per_engine_service(). This in turn could
be called either from multiple tasklets (MSI-X) or
a tasklet (MSI, Legacy INTR) or
ath10k_htc_send_complete_check().

The race itself would cause corruption of
htt.refcount (ath10k_skb_cb) and we'd leak
multiple skbuffs per tx.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---

I'm unable to reproduce the memleak.
Can someone verify this patch, please?

 drivers/net/wireless/ath/ath10k/pci.c |   39 +++++++++++++--------------------
 1 file changed, 15 insertions(+), 24 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index e348fd9..4b1d8f7 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -506,24 +506,6 @@ exit:
 	return compl;
 }
 
-static void ath10k_pci_check_process_ce(struct ath10k *ar)
-{
-	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-
-	/*
-	 * Check if another tasklet is already processing
-	 * the completion list. This could happen in multiple-MSI.
-	 */
-	spin_lock_bh(&ar_pci->compl_lock);
-	if (ar_pci->compl_processing) {
-		spin_unlock_bh(&ar_pci->compl_lock);
-		return;
-	}
-	spin_unlock_bh(&ar_pci->compl_lock);
-
-	ath10k_pci_process_ce(ar);
-}
-
 /* Called by lower (CE) layer when a send to Target completes. */
 static void ath10k_pci_ce_send_done(struct ce_state *ce_state,
 				    void *transfer_context,
@@ -583,7 +565,7 @@ static void ath10k_pci_ce_send_done(struct ce_state *ce_state,
 	if (!process)
 		return;
 
-	ath10k_pci_check_process_ce(ar);
+	ath10k_pci_process_ce(ar);
 }
 
 /* Called by lower (CE) layer when data is received from the Target. */
@@ -629,7 +611,7 @@ static void ath10k_pci_ce_recv_data(struct ce_state *ce_state,
 							   &transfer_id,
 							   &flags) == 0);
 
-	ath10k_pci_check_process_ce(ar);
+	ath10k_pci_process_ce(ar);
 }
 
 /* Send the first nbytes bytes of the buffer */
@@ -888,7 +870,18 @@ static void ath10k_pci_process_ce(struct ath10k *ar)
 	unsigned int nbytes;
 	int ret, send_done = 0;
 
-	do {
+	/* Upper layers aren't ready to handle tx/rx completions in parallel so
+	 * we must serialize all completion processing. */
+
+	spin_lock_bh(&ar_pci->compl_lock);
+	if (ar_pci->compl_processing) {
+		spin_unlock_bh(&ar_pci->compl_lock);
+		return;
+	}
+	ar_pci->compl_processing = true;
+	spin_unlock_bh(&ar_pci->compl_lock);
+
+	for (;;) {
 		spin_lock_bh(&ar_pci->compl_lock);
 		if (list_empty(&ar_pci->compl_process)) {
 			spin_unlock_bh(&ar_pci->compl_lock);
@@ -897,7 +890,6 @@ static void ath10k_pci_process_ce(struct ath10k *ar)
 		compl = list_first_entry(&ar_pci->compl_process,
 					 struct ath10k_pci_compl, list);
 		list_del(&compl->list);
-		ar_pci->compl_processing = true;
 		spin_unlock_bh(&ar_pci->compl_lock);
 
 		if (compl->send_or_recv == HIF_CE_COMPLETE_SEND) {
@@ -943,8 +935,7 @@ static void ath10k_pci_process_ce(struct ath10k *ar)
 		list_add_tail(&compl->list, &compl->pipe_info->compl_free);
 		compl->pipe_info->num_sends_allowed += send_done;
 		spin_unlock_bh(&compl->pipe_info->pipe_lock);
-
-	} while (1);
+	}
 
 	spin_lock_bh(&ar_pci->compl_lock);
 	ar_pci->compl_processing = false;
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [ath9k-devel] [RFT] ath10k: serialize CE completions properly
  2013-05-08 10:00 ` [ath9k-devel] [RFT] ath10k: serialize CE completions properly Michal Kazior
@ 2013-05-09  4:07   ` Sujith Manoharan
  2013-05-10 12:43   ` [ath9k-devel] [PATCH] " Michal Kazior
  1 sibling, 0 replies; 4+ messages in thread
From: Sujith Manoharan @ 2013-05-09  4:07 UTC (permalink / raw)
  To: ath9k-devel

Michal Kazior wrote:
> This fixes memory leak when doing heavy TX.
> 
> ath10k_pci_check_process_ce() was checking
> `compl_processing` and would call
> ath10k_pci_process_ce() if its false. The
> ath10k_pci_process_ce() would then set it to true
> if there's at least one completion. The lock
> protecting the variable is not held between
> checking and setting the variable, meaning it is a
> possible race condition.
> 
> If we then assume ath10k_pci_process_ce() is
> called simultaneusly from different contexts we
> have a race. And apparently this was possible:
> 
> ath10k_pci_check_process_ce() was called from
> ath10k_ce_per_engine_service(). This in turn could
> be called either from multiple tasklets (MSI-X) or
> a tasklet (MSI, Legacy INTR) or
> ath10k_htc_send_complete_check().
> 
> The race itself would cause corruption of
> htt.refcount (ath10k_skb_cb) and we'd leak
> multiple skbuffs per tx.
> 
> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>

Looks good to me.

Sujith

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [ath9k-devel] [PATCH] ath10k: serialize CE completions properly
  2013-05-08 10:00 ` [ath9k-devel] [RFT] ath10k: serialize CE completions properly Michal Kazior
  2013-05-09  4:07   ` Sujith Manoharan
@ 2013-05-10 12:43   ` Michal Kazior
  2013-05-14  4:29     ` Kalle Valo
  1 sibling, 1 reply; 4+ messages in thread
From: Michal Kazior @ 2013-05-10 12:43 UTC (permalink / raw)
  To: ath9k-devel

This fixes memory leak when doing heavy TX.

ath10k_pci_check_process_ce() was checking
`compl_processing` and would call
ath10k_pci_process_ce() if its false. The
ath10k_pci_process_ce() would then set it to true
if there's at least one completion. The lock
protecting the variable is not held between
checking and setting the variable, meaning it is a
possible race condition.

If we then assume ath10k_pci_process_ce() is
called simultaneusly from different contexts we
have a race. And apparently this was possible:

ath10k_pci_check_process_ce() was called from
ath10k_ce_per_engine_service(). This in turn could
be called either from multiple tasklets (MSI-X) or
a tasklet (MSI, Legacy INTR) or
ath10k_htc_send_complete_check().

The race itself would cause corruption of
htt.refcount (ath10k_skb_cb) and we'd leak
multiple skbuffs per tx.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
---
 drivers/net/wireless/ath/ath10k/pci.c |   39 +++++++++++++--------------------
 1 file changed, 15 insertions(+), 24 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index e348fd9..4b1d8f7 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -506,24 +506,6 @@ exit:
 	return compl;
 }
 
-static void ath10k_pci_check_process_ce(struct ath10k *ar)
-{
-	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-
-	/*
-	 * Check if another tasklet is already processing
-	 * the completion list. This could happen in multiple-MSI.
-	 */
-	spin_lock_bh(&ar_pci->compl_lock);
-	if (ar_pci->compl_processing) {
-		spin_unlock_bh(&ar_pci->compl_lock);
-		return;
-	}
-	spin_unlock_bh(&ar_pci->compl_lock);
-
-	ath10k_pci_process_ce(ar);
-}
-
 /* Called by lower (CE) layer when a send to Target completes. */
 static void ath10k_pci_ce_send_done(struct ce_state *ce_state,
 				    void *transfer_context,
@@ -583,7 +565,7 @@ static void ath10k_pci_ce_send_done(struct ce_state *ce_state,
 	if (!process)
 		return;
 
-	ath10k_pci_check_process_ce(ar);
+	ath10k_pci_process_ce(ar);
 }
 
 /* Called by lower (CE) layer when data is received from the Target. */
@@ -629,7 +611,7 @@ static void ath10k_pci_ce_recv_data(struct ce_state *ce_state,
 							   &transfer_id,
 							   &flags) == 0);
 
-	ath10k_pci_check_process_ce(ar);
+	ath10k_pci_process_ce(ar);
 }
 
 /* Send the first nbytes bytes of the buffer */
@@ -888,7 +870,18 @@ static void ath10k_pci_process_ce(struct ath10k *ar)
 	unsigned int nbytes;
 	int ret, send_done = 0;
 
-	do {
+	/* Upper layers aren't ready to handle tx/rx completions in parallel so
+	 * we must serialize all completion processing. */
+
+	spin_lock_bh(&ar_pci->compl_lock);
+	if (ar_pci->compl_processing) {
+		spin_unlock_bh(&ar_pci->compl_lock);
+		return;
+	}
+	ar_pci->compl_processing = true;
+	spin_unlock_bh(&ar_pci->compl_lock);
+
+	for (;;) {
 		spin_lock_bh(&ar_pci->compl_lock);
 		if (list_empty(&ar_pci->compl_process)) {
 			spin_unlock_bh(&ar_pci->compl_lock);
@@ -897,7 +890,6 @@ static void ath10k_pci_process_ce(struct ath10k *ar)
 		compl = list_first_entry(&ar_pci->compl_process,
 					 struct ath10k_pci_compl, list);
 		list_del(&compl->list);
-		ar_pci->compl_processing = true;
 		spin_unlock_bh(&ar_pci->compl_lock);
 
 		if (compl->send_or_recv == HIF_CE_COMPLETE_SEND) {
@@ -943,8 +935,7 @@ static void ath10k_pci_process_ce(struct ath10k *ar)
 		list_add_tail(&compl->list, &compl->pipe_info->compl_free);
 		compl->pipe_info->num_sends_allowed += send_done;
 		spin_unlock_bh(&compl->pipe_info->pipe_lock);
-
-	} while (1);
+	}
 
 	spin_lock_bh(&ar_pci->compl_lock);
 	ar_pci->compl_processing = false;
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [ath9k-devel] [PATCH] ath10k: serialize CE completions properly
  2013-05-10 12:43   ` [ath9k-devel] [PATCH] " Michal Kazior
@ 2013-05-14  4:29     ` Kalle Valo
  0 siblings, 0 replies; 4+ messages in thread
From: Kalle Valo @ 2013-05-14  4:29 UTC (permalink / raw)
  To: ath9k-devel

Michal Kazior <michal.kazior@tieto.com> writes:

> This fixes memory leak when doing heavy TX.
>
> ath10k_pci_check_process_ce() was checking
> `compl_processing` and would call
> ath10k_pci_process_ce() if its false. The
> ath10k_pci_process_ce() would then set it to true
> if there's at least one completion. The lock
> protecting the variable is not held between
> checking and setting the variable, meaning it is a
> possible race condition.
>
> If we then assume ath10k_pci_process_ce() is
> called simultaneusly from different contexts we
> have a race. And apparently this was possible:
>
> ath10k_pci_check_process_ce() was called from
> ath10k_ce_per_engine_service(). This in turn could
> be called either from multiple tasklets (MSI-X) or
> a tasklet (MSI, Legacy INTR) or
> ath10k_htc_send_complete_check().
>
> The race itself would cause corruption of
> htt.refcount (ath10k_skb_cb) and we'd leak
> multiple skbuffs per tx.
>
> Signed-off-by: Michal Kazior <michal.kazior@tieto.com>

Thanks, applied.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2013-05-14  4:29 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <87haie3mlw.fsf_-_@kamboji.qca.qualcomm.com>
2013-05-08 10:00 ` [ath9k-devel] [RFT] ath10k: serialize CE completions properly Michal Kazior
2013-05-09  4:07   ` Sujith Manoharan
2013-05-10 12:43   ` [ath9k-devel] [PATCH] " Michal Kazior
2013-05-14  4:29     ` Kalle Valo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox