mwifiex: add rx workqueue support
authorAvinash Patil <patila@marvell.com>
Fri, 12 Sep 2014 14:38:59 +0000 (20:08 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 15 Sep 2014 19:00:52 +0000 (15:00 -0400)
This patch adds RX work queue support to mwifiex.
Packets received are queued to internal queue which are then
processed by scheduling a work item for RX process.

RX work is enabled only on SMP systems.

Reviewed-by: James Cameron <quozl@laptop.org>
Signed-off-by: Avinash Patil <patila@marvell.com>
Signed-off-by: Marc Yang <yangyang@marvell.com>
Signed-off-by: Cathy Luo <cluo@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/mwifiex/11n_rxreorder.c
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/sdio.c

index 06a2c215ef5e13129a53e72add714c05cbb77489..40057079ffb9537eae9549518be3e5df29f1a16b 100644 (file)
@@ -183,6 +183,15 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv,
        if (!tbl)
                return;
 
+       spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags);
+       priv->adapter->rx_locked = true;
+       if (priv->adapter->rx_processing) {
+               spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags);
+               flush_workqueue(priv->adapter->rx_workqueue);
+       } else {
+               spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags);
+       }
+
        start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1);
        mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
 
@@ -194,6 +203,11 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv,
 
        kfree(tbl->rx_reorder_ptr);
        kfree(tbl);
+
+       spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags);
+       priv->adapter->rx_locked = false;
+       spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags);
+
 }
 
 /*
index cd9baad60595eb8a3efc1fcde43ed0baa65c6bca..f7c97cf3840bfa582633906ddb4ac9187420b064 100644 (file)
@@ -447,8 +447,11 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
        spin_lock_init(&adapter->cmd_free_q_lock);
        spin_lock_init(&adapter->cmd_pending_q_lock);
        spin_lock_init(&adapter->scan_pending_q_lock);
+       spin_lock_init(&adapter->rx_q_lock);
+       spin_lock_init(&adapter->rx_proc_lock);
 
        skb_queue_head_init(&adapter->usb_rx_data_q);
+       skb_queue_head_init(&adapter->rx_data_q);
 
        for (i = 0; i < adapter->priv_num; ++i) {
                INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head);
@@ -614,6 +617,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
        int ret = -EINPROGRESS;
        struct mwifiex_private *priv;
        s32 i;
+       unsigned long flags;
        struct sk_buff *skb;
 
        /* mwifiex already shutdown */
@@ -648,6 +652,21 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
                }
        }
 
+       spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+
+       while ((skb = skb_dequeue(&adapter->rx_data_q))) {
+               struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
+
+               atomic_dec(&adapter->rx_pending);
+               priv = adapter->priv[rx_info->bss_num];
+               if (priv)
+                       priv->stats.rx_dropped++;
+
+               dev_kfree_skb_any(skb);
+       }
+
+       spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+
        spin_lock(&adapter->mwifiex_lock);
 
        if (adapter->if_ops.data_complete) {
index f1393868d04ceb627ceec52c6205a0f821ea3481..b522f7c3690118720ba01b7206a487fbdfab9ebe 100644 (file)
@@ -126,6 +126,42 @@ static int mwifiex_unregister(struct mwifiex_adapter *adapter)
        return 0;
 }
 
+static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
+{
+       unsigned long flags;
+       struct sk_buff *skb;
+       bool delay_main_work = adapter->delay_main_work;
+
+       spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+       if (adapter->rx_processing || adapter->rx_locked) {
+               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);
+       }
+
+       /* Check for Rx data */
+       while ((skb = skb_dequeue(&adapter->rx_data_q))) {
+               atomic_dec(&adapter->rx_pending);
+               if (adapter->delay_main_work &&
+                   (atomic_dec_return(&adapter->rx_pending) <
+                    LOW_RX_PENDING)) {
+                       adapter->delay_main_work = false;
+                       queue_work(adapter->rx_workqueue, &adapter->rx_work);
+               }
+               mwifiex_handle_rx_packet(adapter, skb);
+       }
+       spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+       adapter->rx_processing = false;
+       spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+
+       if (delay_main_work)
+               queue_work(adapter->workqueue, &adapter->main_work);
+exit_rx_proc:
+       return 0;
+}
+
 /*
  * The main process.
  *
@@ -163,6 +199,19 @@ process_start:
                    (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY))
                        break;
 
+               /* If we process interrupts first, it would increase RX pending
+                * even further. Avoid this by checking if rx_pending has
+                * crossed high threshold and schedule rx work queue
+                * and then process interrupts
+                */
+               if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING) {
+                       adapter->delay_main_work = true;
+                       if (!adapter->rx_processing)
+                               queue_work(adapter->rx_workqueue,
+                                          &adapter->rx_work);
+                       break;
+               }
+
                /* Handle pending interrupt if any */
                if (adapter->int_status) {
                        if (adapter->hs_activated)
@@ -171,6 +220,9 @@ process_start:
                                adapter->if_ops.process_int_status(adapter);
                }
 
+               if (adapter->rx_work_enabled && adapter->data_received)
+                       queue_work(adapter->rx_workqueue, &adapter->rx_work);
+
                /* Need to wake up the card ? */
                if ((adapter->ps_state == PS_STATE_SLEEP) &&
                    (adapter->pm_wakeup_card_req &&
@@ -183,6 +235,7 @@ process_start:
                }
 
                if (IS_CARD_RX_RCVD(adapter)) {
+                       adapter->data_received = false;
                        adapter->pm_wakeup_fw_try = false;
                        if (adapter->ps_state == PS_STATE_SLEEP)
                                adapter->ps_state = PS_STATE_AWAKE;
@@ -318,6 +371,12 @@ static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
        flush_workqueue(adapter->workqueue);
        destroy_workqueue(adapter->workqueue);
        adapter->workqueue = NULL;
+
+       if (adapter->rx_workqueue) {
+               flush_workqueue(adapter->rx_workqueue);
+               destroy_workqueue(adapter->rx_workqueue);
+               adapter->rx_workqueue = NULL;
+       }
 }
 
 /*
@@ -731,6 +790,21 @@ int is_command_pending(struct mwifiex_adapter *adapter)
        return !is_cmd_pend_q_empty;
 }
 
+/*
+ * This is the RX work queue function.
+ *
+ * It handles the RX operations.
+ */
+static void mwifiex_rx_work_queue(struct work_struct *work)
+{
+       struct mwifiex_adapter *adapter =
+               container_of(work, struct mwifiex_adapter, rx_work);
+
+       if (adapter->surprise_removed)
+               return;
+       mwifiex_process_rx(adapter);
+}
+
 /*
  * This is the main work queue function.
  *
@@ -787,6 +861,11 @@ mwifiex_add_card(void *card, struct semaphore *sem,
        adapter->cmd_wait_q.status = 0;
        adapter->scan_wait_q_woken = false;
 
+       if (num_possible_cpus() > 1) {
+               adapter->rx_work_enabled = true;
+               pr_notice("rx work enabled, cpus %d\n", num_possible_cpus());
+       }
+
        adapter->workqueue =
                alloc_workqueue("MWIFIEX_WORK_QUEUE",
                                WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
@@ -794,6 +873,18 @@ mwifiex_add_card(void *card, struct semaphore *sem,
                goto err_kmalloc;
 
        INIT_WORK(&adapter->main_work, mwifiex_main_work_queue);
+
+       if (adapter->rx_work_enabled) {
+               adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE",
+                                                       WQ_HIGHPRI |
+                                                       WQ_MEM_RECLAIM |
+                                                       WQ_UNBOUND, 1);
+               if (!adapter->rx_workqueue)
+                       goto err_kmalloc;
+
+               INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue);
+       }
+
        if (adapter->if_ops.iface_work)
                INIT_WORK(&adapter->iface_work, adapter->if_ops.iface_work);
 
index e9cd4ab4902d16cfe3c031a045367eb3e07baa9d..1a999999b3916319fee711dba6f3dd074033bc23 100644 (file)
@@ -58,6 +58,9 @@ enum {
 #define MAX_TX_PENDING      100
 #define LOW_TX_PENDING      80
 
+#define HIGH_RX_PENDING     50
+#define LOW_RX_PENDING      20
+
 #define MWIFIEX_UPLD_SIZE               (2312)
 
 #define MAX_EVENT_SIZE                  2048
@@ -714,6 +717,12 @@ struct mwifiex_adapter {
        atomic_t cmd_pending;
        struct workqueue_struct *workqueue;
        struct work_struct main_work;
+       struct workqueue_struct *rx_workqueue;
+       struct work_struct rx_work;
+       bool rx_work_enabled;
+       bool rx_processing;
+       bool delay_main_work;
+       bool rx_locked;
        struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM];
        /* spin lock for init/shutdown */
        spinlock_t mwifiex_lock;
@@ -754,6 +763,10 @@ struct mwifiex_adapter {
        struct list_head scan_pending_q;
        /* spin lock for scan_pending_q */
        spinlock_t scan_pending_q_lock;
+       /* spin lock for RX queue */
+       spinlock_t rx_q_lock;
+       /* spin lock for RX processing routine */
+       spinlock_t rx_proc_lock;
        struct sk_buff_head usb_rx_data_q;
        u32 scan_processing;
        u16 region_code;
@@ -831,6 +844,7 @@ struct mwifiex_adapter {
        u8 num_mem_types;
        u8 curr_mem_idx;
        bool scan_chan_gap_enabled;
+       struct sk_buff_head rx_data_q;
 };
 
 int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
index 2ada1b709778e393651d704c7dfb890e02203495..1504b16e248ed6d5abf60dfc020c9d1cd8688078 100644 (file)
@@ -1233,6 +1233,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
        struct sk_buff *skb_tmp = NULL;
        struct mwifiex_pcie_buf_desc *desc;
        struct mwifiex_pfu_buf_desc *desc2;
+       unsigned long flags;
 
        if (!mwifiex_pcie_ok_to_access_hw(adapter))
                mwifiex_pm_wakeup_card(adapter);
@@ -1283,7 +1284,16 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter)
                                "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n",
                                card->rxbd_rdptr, wrptr, rx_len);
                        skb_pull(skb_data, INTF_HEADER_LEN);
-                       mwifiex_handle_rx_packet(adapter, skb_data);
+                       if (adapter->rx_work_enabled) {
+                               spin_lock_irqsave(&adapter->rx_q_lock, flags);
+                               skb_queue_tail(&adapter->rx_data_q, skb_data);
+                               spin_unlock_irqrestore(&adapter->rx_q_lock,
+                                                      flags);
+                               adapter->data_received = true;
+                               atomic_inc(&adapter->rx_pending);
+                       } else {
+                               mwifiex_handle_rx_packet(adapter, skb_data);
+                       }
                }
 
                skb_tmp = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE);
index bdab122db5cf931f85fddffb7beace437e3cf7fa..ea8fc587e90ff5d7838179d0daa89eec1dac8cc3 100644 (file)
@@ -1039,6 +1039,7 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter,
                                    struct sk_buff *skb, u32 upld_typ)
 {
        u8 *cmd_buf;
+       unsigned long flags;
        __le16 *curr_ptr = (__le16 *)skb->data;
        u16 pkt_len = le16_to_cpu(*curr_ptr);
 
@@ -1048,7 +1049,15 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter,
        switch (upld_typ) {
        case MWIFIEX_TYPE_DATA:
                dev_dbg(adapter->dev, "info: --- Rx: Data packet ---\n");
-               mwifiex_handle_rx_packet(adapter, skb);
+               if (adapter->rx_work_enabled) {
+                       spin_lock_irqsave(&adapter->rx_q_lock, flags);
+                       skb_queue_tail(&adapter->rx_data_q, skb);
+                       spin_unlock_irqrestore(&adapter->rx_q_lock, flags);
+                       adapter->data_received = true;
+                       atomic_inc(&adapter->rx_pending);
+               } else {
+                       mwifiex_handle_rx_packet(adapter, skb);
+               }
                break;
 
        case MWIFIEX_TYPE_CMD: