From: Shrenik Shikhare <shrenik@xxxxxxxxxxx> 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 <cluo@xxxxxxxxxxx> Signed-off-by: Ganapathi Bhat <gbhat@xxxxxxxxxxx> --- 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