From: Shrenik Shikhare <shre...@marvell.com>

There is a race condition for acquiring rx_proc_lock between
rx worker thread and USB RX data interrupt
(mwifiex_usb_rx_complete):

1. USB receives an RX data interrupt, queues rx_work
2. rx_work empties rx_data_q, tries to acquire rx_proc_lock (to
clear rx_processing flag)
3. While #2 is yet to acquire rx_proc_lock, driver receives
continuous RX data interupts(mwifiex_usb_rx_complete)
3. For each interrupt at #3, driver acquires rx_proc_lock(it gets
the lock since it is in interrupt context), tries to queue
rx_work, but fails to do so since rx_processing is still set(#2)
4. When rx_pending exceeds HIGH_RX_PENDING, driver stops
submitting URBs back to USB subsystem and thus firmware stops
uploading RX data to driver
5. Now finally #2 will acquire rx_proc_lock, but because of #4,
there are no further triggers to schedule rx_work again

The above scenario occurs in some platforms where the RX
processing is comparitively slower. This results in RX stall in
driver, command/TX timeouts in firmware. The above scenario is
introduced after commit c7dbdcb2a4e1
("mwifiex: schedule rx_work on RX interrupt for USB")

To fix this set a new more_rx_task_flag whenever RX data callback
is trying to schedule rx_work but rx_processing is not yet
cleared. This will let the current rx_work(which was waiting for
rx_proc_lock) to loopback and process newly arrived RX packets.

Fixes: c7dbdcb2a4e1 ("mwifiex: schedule rx_work on RX interrupt for USB")
Signed-off-by: Cathy Luo <c...@marvell.com>
Signed-off-by: Ganapathi Bhat <gb...@marvell.com>
---
v2: added 'Fixes' label in commit message
---
 drivers/net/wireless/marvell/mwifiex/main.c | 10 +++++++++-
 drivers/net/wireless/marvell/mwifiex/main.h |  1 +
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/marvell/mwifiex/main.c 
b/drivers/net/wireless/marvell/mwifiex/main.c
index 6e6e1a7..ea87c7c 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.c
+++ b/drivers/net/wireless/marvell/mwifiex/main.c
@@ -163,6 +163,7 @@ void mwifiex_queue_main_work(struct mwifiex_adapter 
*adapter)
        spin_lock_irqsave(&adapter->main_proc_lock, flags);
        if (adapter->mwifiex_processing) {
                adapter->more_task_flag = true;
+               adapter->more_rx_task_flag = true;
                spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
        } else {
                spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
@@ -177,6 +178,7 @@ void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter)
 
        spin_lock_irqsave(&adapter->rx_proc_lock, flags);
        if (adapter->rx_processing) {
+               adapter->more_rx_task_flag = true;
                spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
        } else {
                spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
@@ -193,13 +195,14 @@ static int mwifiex_process_rx(struct mwifiex_adapter 
*adapter)
 
        spin_lock_irqsave(&adapter->rx_proc_lock, flags);
        if (adapter->rx_processing || adapter->rx_locked) {
+               adapter->more_rx_task_flag = true;
                spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
                goto exit_rx_proc;
        } else {
                adapter->rx_processing = true;
                spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
        }
-
+rx_process_start:
        /* Check for Rx data */
        while ((skb = skb_dequeue(&adapter->rx_data_q))) {
                atomic_dec(&adapter->rx_pending);
@@ -221,6 +224,11 @@ static int mwifiex_process_rx(struct mwifiex_adapter 
*adapter)
                }
        }
        spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+       if (adapter->more_rx_task_flag) {
+               adapter->more_rx_task_flag = false;
+               spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+               goto rx_process_start;
+       }
        adapter->rx_processing = false;
        spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
 
diff --git a/drivers/net/wireless/marvell/mwifiex/main.h 
b/drivers/net/wireless/marvell/mwifiex/main.h
index 66ba95c..242e05e 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.h
+++ b/drivers/net/wireless/marvell/mwifiex/main.h
@@ -891,6 +891,7 @@ struct mwifiex_adapter {
        spinlock_t main_proc_lock;
        u32 mwifiex_processing;
        u8 more_task_flag;
+       u8 more_rx_task_flag;
        u16 tx_buf_size;
        u16 curr_tx_buf_size;
        /* sdio single port rx aggregation capability */
-- 
1.9.1

Reply via email to