mwifiex: fix out of memory issue observed for USB chipsets
authorAmitkumar Karwar <akarwar@marvell.com>
Wed, 5 Nov 2014 11:34:29 +0000 (17:04 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 11 Nov 2014 21:31:12 +0000 (16:31 -0500)
On some platforms, system goes out of memory during heavy
Rx traffic with our USB chipsets.

In case of SDIO/PCIe, after receiving 50 packets in Rx queue
we stop processing interrupts till packets pending fall below
low threshold i.e 20. We don't have similar logic for USB,
so if host platform is slow, we would hit a case where firmware
keeps on pushing packets at high speed than driver/kernel can
process.

We will stop submitting URBs for Rx data when pending packet
count reaches high threshold and restart them when enough
packets are consumed to solve the problem.

BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=85071
Reported-by: Marek Belisko <marek.belisko@gmail.com>
Tested-by: Marek Belisko <marek.belisko@gmail.com>
Signed-off-by: Avinash Patil <patila@marvell.com>
Signed-off-by: Cathy Luo <cluo@marvell.com>
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/usb.c

index cb23ca3653b52bb6668a04052e45db466cb94b0a..2a5a59bec1240997ff89a306a96edb237ed3d8df 100644 (file)
@@ -146,6 +146,8 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
                atomic_dec(&adapter->rx_pending);
                if (adapter->delay_main_work &&
                    (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) {
+                       if (adapter->if_ops.submit_rem_rx_urbs)
+                               adapter->if_ops.submit_rem_rx_urbs(adapter);
                        adapter->delay_main_work = false;
                        queue_work(adapter->workqueue, &adapter->main_work);
                }
index 4ef3c7a8f9fbba6b12d8a68faa6c9e454ebd435e..eced41ef196786e083db6ef33764bd4c368cc720 100644 (file)
@@ -692,6 +692,7 @@ struct mwifiex_if_ops {
        void (*fw_dump)(struct mwifiex_adapter *);
        int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
        void (*iface_work)(struct work_struct *work);
+       void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter);
 };
 
 struct mwifiex_adapter {
index 08e9ec0652758b3d2cb68ec9e3b3b6edcf374368..6cc8519c0f4084c05272a50c7394a5c993be4650 100644 (file)
@@ -222,7 +222,13 @@ setup_for_next:
        else
                size = MWIFIEX_RX_DATA_BUF_SIZE;
 
-       mwifiex_usb_submit_rx_urb(context, size);
+       if (card->rx_cmd_ep == context->ep) {
+               mwifiex_usb_submit_rx_urb(context, size);
+       } else {
+               context->skb = NULL;
+               if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING)
+                       mwifiex_usb_submit_rx_urb(context, size);
+       }
 
        return;
 }
@@ -978,6 +984,20 @@ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
        return 0;
 }
 
+static void mwifiex_usb_submit_rem_rx_urbs(struct mwifiex_adapter *adapter)
+{
+       struct usb_card_rec *card = (struct usb_card_rec *)adapter->card;
+       int i;
+       struct urb_context *ctx;
+
+       for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) {
+               if (card->rx_data_list[i].skb)
+                       continue;
+               ctx = &card->rx_data_list[i];
+               mwifiex_usb_submit_rx_urb(ctx, MWIFIEX_RX_DATA_BUF_SIZE);
+       }
+}
+
 static struct mwifiex_if_ops usb_ops = {
        .register_dev =         mwifiex_register_dev,
        .unregister_dev =       mwifiex_unregister_dev,
@@ -989,6 +1009,7 @@ static struct mwifiex_if_ops usb_ops = {
        .cmdrsp_complete =      mwifiex_usb_cmd_event_complete,
        .event_complete =       mwifiex_usb_cmd_event_complete,
        .host_to_card =         mwifiex_usb_host_to_card,
+       .submit_rem_rx_urbs =   mwifiex_usb_submit_rem_rx_urbs,
 };
 
 /* This function initializes the USB driver module.