msm: bam_dmux: handle system wide low memory conditions
When A2 turns on, queue_rx() is first run from the SMSM thread, and
attempts to allocate 32 2kB-buffers with GFP_KERNEL for DMA transactions.
If the system is low on memory, the allocation could put the thread to
sleep while the memory allocator scavenges for enough memory to satisfy
the request. This is not desired as it stalls a critical system thread
that is communicating with a real-time system.
Instead, use GFP_NOWAIT which will return immediately if no memory is
available for the allocation request so that bam_dmux may handle the
situation gracefully and possibly reschedule the allocations for a later
time.
CRs-Fixed: 406946
Change-Id: I645acd29f7e0fa27883a27c043fc951cbdd91b84
Signed-off-by: Jeffrey Hugo <jhugo@codeaurora.org>
(cherry picked from commit ad75d8d57e70aab72d1c0c1411f58d1d3028da40)
(cherry picked from commit bcbb7dda88b66d69c2ebb6c31b5a6083f4df6518)
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index 1a02fce..a3a70eb 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -223,6 +223,7 @@
static void rx_timer_work_func(struct work_struct *work);
static DECLARE_WORK(rx_timer_work, rx_timer_work_func);
+static struct delayed_work queue_rx_work;
static struct workqueue_struct *bam_mux_rx_workqueue;
static struct workqueue_struct *bam_mux_tx_workqueue;
@@ -430,21 +431,27 @@
rx_len_cached = bam_rx_pool_len;
mutex_unlock(&bam_rx_pool_mutexlock);
- while (rx_len_cached < NUM_BUFFERS) {
+ while (bam_connection_is_active && rx_len_cached < NUM_BUFFERS) {
if (in_global_reset)
goto fail;
- info = kmalloc(sizeof(struct rx_pkt_info), GFP_KERNEL);
+ info = kmalloc(sizeof(struct rx_pkt_info),
+ GFP_NOWAIT | __GFP_NOWARN);
if (!info) {
- pr_err("%s: unable to alloc rx_pkt_info\n", __func__);
+ DMUX_LOG_KERR(
+ "%s: unable to alloc rx_pkt_info, will retry later\n",
+ __func__);
goto fail;
}
INIT_WORK(&info->work, handle_bam_mux_cmd);
- info->skb = __dev_alloc_skb(BUFFER_SIZE, GFP_KERNEL);
+ info->skb = __dev_alloc_skb(BUFFER_SIZE,
+ GFP_NOWAIT | __GFP_NOWARN);
if (info->skb == NULL) {
- DMUX_LOG_KERR("%s: unable to alloc skb\n", __func__);
+ DMUX_LOG_KERR(
+ "%s: unable to alloc skb, will retry later\n",
+ __func__);
goto fail_info;
}
ptr = skb_put(info->skb, BUFFER_SIZE);
@@ -488,11 +495,16 @@
fail:
if (rx_len_cached == 0) {
- DMUX_LOG_KERR("%s: RX queue failure\n", __func__);
- in_global_reset = 1;
+ DMUX_LOG_KERR("%s: rescheduling\n", __func__);
+ schedule_delayed_work(&queue_rx_work, msecs_to_jiffies(100));
}
}
+static void queue_rx_work_func(struct work_struct *work)
+{
+ queue_rx();
+}
+
static void bam_mux_process_data(struct sk_buff *rx_skb)
{
unsigned long flags;
@@ -2424,6 +2436,7 @@
init_completion(&bam_connection_completion);
init_completion(&dfab_unvote_completion);
INIT_DELAYED_WORK(&ul_timeout_work, ul_timeout);
+ INIT_DELAYED_WORK(&queue_rx_work, queue_rx_work_func);
wake_lock_init(&bam_wakelock, WAKE_LOCK_SUSPEND, "bam_dmux_wakelock");
rc = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_A2_POWER_CONTROL,