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>
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);
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);
+
}
/*
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);
int ret = -EINPROGRESS;
struct mwifiex_private *priv;
s32 i;
+ unsigned long flags;
struct sk_buff *skb;
/* mwifiex already shutdown */
}
}
+ 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) {
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.
*
(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)
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 &&
}
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;
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;
+ }
}
/*
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.
*
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);
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);
#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
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;
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;
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);
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);
"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);
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);
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: