msm: bam_dmux: Improve robustness of A2 State Changes
Add code to handle unexpected A2 disconnects while
in polling mode and defer polling-to-interrupt
mode switch upon reconnect.
CRs-Fixed: 323592
Change-Id: I13ee41c29c429d70e060c64c46e7bcf590f23565
Signed-off-by: Eric Holmberg <eholmber@codeaurora.org>
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index afcb3bc..e52884f 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -747,78 +747,105 @@
return ret;
}
+static void rx_switch_to_interrupt_mode(void)
+{
+ struct sps_connect cur_rx_conn;
+ struct sps_iovec iov;
+ struct rx_pkt_info *info;
+ int ret;
+
+ /*
+ * Attempt to enable interrupts - if this fails,
+ * continue polling and we will retry later.
+ */
+ ret = sps_get_config(bam_rx_pipe, &cur_rx_conn);
+ if (ret) {
+ pr_err("%s: sps_get_config() failed %d\n", __func__, ret);
+ goto fail;
+ }
+
+ rx_register_event.options = SPS_O_EOT;
+ ret = sps_register_event(bam_rx_pipe, &rx_register_event);
+ if (ret) {
+ pr_err("%s: sps_register_event() failed %d\n", __func__, ret);
+ goto fail;
+ }
+
+ cur_rx_conn.options = SPS_O_AUTO_ENABLE |
+ SPS_O_EOT | SPS_O_ACK_TRANSFERS;
+ ret = sps_set_config(bam_rx_pipe, &cur_rx_conn);
+ if (ret) {
+ pr_err("%s: sps_set_config() failed %d\n", __func__, ret);
+ goto fail;
+ }
+ polling_mode = 0;
+
+ /* handle any rx packets before interrupt was enabled */
+ while (bam_connection_is_active && !polling_mode) {
+ ret = sps_get_iovec(bam_rx_pipe, &iov);
+ if (ret) {
+ pr_err("%s: sps_get_iovec failed %d\n",
+ __func__, ret);
+ break;
+ }
+ if (iov.addr == 0)
+ break;
+
+ mutex_lock(&bam_rx_pool_mutexlock);
+ if (unlikely(list_empty(&bam_rx_pool))) {
+ mutex_unlock(&bam_rx_pool_mutexlock);
+ continue;
+ }
+ info = list_first_entry(&bam_rx_pool, struct rx_pkt_info,
+ list_node);
+ list_del(&info->list_node);
+ mutex_unlock(&bam_rx_pool_mutexlock);
+ handle_bam_mux_cmd(&info->work);
+ }
+ return;
+
+fail:
+ pr_err("%s: reverting to polling\n", __func__);
+ queue_work(bam_mux_rx_workqueue, &rx_timer_work);
+}
+
static void rx_timer_work_func(struct work_struct *work)
{
struct sps_iovec iov;
- struct list_head *node;
struct rx_pkt_info *info;
int inactive_cycles = 0;
int ret;
- struct sps_connect cur_rx_conn;
- while (1) { /* timer loop */
+ while (bam_connection_is_active) { /* timer loop */
++inactive_cycles;
- while (1) { /* deplete queue loop */
+ while (bam_connection_is_active) { /* deplete queue loop */
if (in_global_reset)
return;
- sps_get_iovec(bam_rx_pipe, &iov);
+
+ ret = sps_get_iovec(bam_rx_pipe, &iov);
+ if (ret) {
+ pr_err("%s: sps_get_iovec failed %d\n",
+ __func__, ret);
+ break;
+ }
if (iov.addr == 0)
break;
inactive_cycles = 0;
mutex_lock(&bam_rx_pool_mutexlock);
- node = bam_rx_pool.next;
- list_del(node);
+ if (unlikely(list_empty(&bam_rx_pool))) {
+ mutex_unlock(&bam_rx_pool_mutexlock);
+ continue;
+ }
+ info = list_first_entry(&bam_rx_pool,
+ struct rx_pkt_info, list_node);
+ list_del(&info->list_node);
mutex_unlock(&bam_rx_pool_mutexlock);
- info = container_of(node, struct rx_pkt_info,
- list_node);
handle_bam_mux_cmd(&info->work);
}
if (inactive_cycles == POLLING_INACTIVITY) {
- /*
- * attempt to enable interrupts in this pipe
- * if enabling interrupts fails, continue polling
- */
- ret = sps_get_config(bam_rx_pipe, &cur_rx_conn);
- if (ret) {
- pr_err("%s: sps_get_config() failed, interrupts"
- " not enabled\n", __func__);
- queue_work(bam_mux_rx_workqueue,
- &rx_timer_work);
- return;
- } else {
- rx_register_event.options = SPS_O_EOT;
- /* should check return value */
- sps_register_event(bam_rx_pipe,
- &rx_register_event);
- cur_rx_conn.options = SPS_O_AUTO_ENABLE |
- SPS_O_EOT | SPS_O_ACK_TRANSFERS;
- ret = sps_set_config(bam_rx_pipe, &cur_rx_conn);
- if (ret) {
- pr_err("%s: sps_set_config() failed, "
- "interrupts not enabled\n",
- __func__);
- queue_work(bam_mux_rx_workqueue,
- &rx_timer_work);
- return;
- }
- polling_mode = 0;
- }
- if (in_global_reset)
- return;
- /* handle race condition - missed packet? */
- sps_get_iovec(bam_rx_pipe, &iov);
- if (iov.addr == 0)
- return;
- inactive_cycles = 0;
- mutex_lock(&bam_rx_pool_mutexlock);
- node = bam_rx_pool.next;
- list_del(node);
- mutex_unlock(&bam_rx_pool_mutexlock);
- info = container_of(node, struct rx_pkt_info,
- list_node);
- handle_bam_mux_cmd(&info->work);
- return;
+ rx_switch_to_interrupt_mode();
+ break;
}
usleep_range(POLLING_MIN_SLEEP, POLLING_MAX_SLEEP);
@@ -869,16 +896,16 @@
if (!polling_mode) {
ret = sps_get_config(bam_rx_pipe, &cur_rx_conn);
if (ret) {
- pr_err("%s: sps_get_config() failed, interrupts"
- " not disabled\n", __func__);
+ pr_err("%s: sps_get_config() failed %d, interrupts"
+ " not disabled\n", __func__, ret);
break;
}
cur_rx_conn.options = SPS_O_AUTO_ENABLE |
SPS_O_ACK_TRANSFERS | SPS_O_POLL;
ret = sps_set_config(bam_rx_pipe, &cur_rx_conn);
if (ret) {
- pr_err("%s: sps_set_config() failed, interrupts"
- " not disabled\n", __func__);
+ pr_err("%s: sps_set_config() failed %d, interrupts"
+ " not disabled\n", __func__, ret);
break;
}
polling_mode = 1;
@@ -1083,10 +1110,16 @@
i = sps_register_event(bam_rx_pipe, &rx_register_event);
if (i)
pr_err("%s: rx event reg failed rc = %d\n", __func__, i);
+
+ bam_connection_is_active = 1;
+
+ if (polling_mode)
+ rx_switch_to_interrupt_mode();
+
for (i = 0; i < NUM_BUFFERS; ++i)
queue_rx();
+
toggle_apps_ack();
- bam_connection_is_active = 1;
complete_all(&bam_connection_completion);
}
@@ -1102,6 +1135,8 @@
unvote_dfab();
__memzero(rx_desc_mem_buf.base, rx_desc_mem_buf.size);
__memzero(tx_desc_mem_buf.base, tx_desc_mem_buf.size);
+
+ mutex_lock(&bam_rx_pool_mutexlock);
while (!list_empty(&bam_rx_pool)) {
node = bam_rx_pool.next;
list_del(node);
@@ -1111,6 +1146,7 @@
dev_kfree_skb_any(info->skb);
kfree(info);
}
+ mutex_unlock(&bam_rx_pool_mutexlock);
}
static void vote_dfab(void)