aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/platform/msm/ipa/ipa_dp.c136
-rw-r--r--drivers/platform/msm/ipa/ipa_i.h15
-rw-r--r--include/uapi/linux/msm_ipa.h6
3 files changed, 105 insertions, 52 deletions
diff --git a/drivers/platform/msm/ipa/ipa_dp.c b/drivers/platform/msm/ipa/ipa_dp.c
index 59e2612c969..6e051f5127e 100644
--- a/drivers/platform/msm/ipa/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_dp.c
@@ -65,7 +65,6 @@ static void ipa_wq_write_done_common(struct ipa_sys_context *sys, u32 cnt)
for (i = 0; i < cnt; i++) {
spin_lock_bh(&sys->spinlock);
if (unlikely(list_empty(&sys->head_desc_list))) {
- WARN_ON(1);
spin_unlock_bh(&sys->spinlock);
return;
}
@@ -99,12 +98,48 @@ static void ipa_wq_write_done_common(struct ipa_sys_context *sys, u32 cnt)
}
}
+static void ipa_purge_tx_list(struct ipa_sys_context *sys)
+{
+ struct ipa_tx_pkt_wrapper *tx_pkt_expected;
+ struct list_head free_list;
+
+ INIT_LIST_HEAD(&free_list);
+
+ spin_lock_bh(&sys->spinlock);
+ list_splice_tail_init(&sys->head_desc_list, &free_list);
+ sys->len = 0;
+ spin_unlock_bh(&sys->spinlock);
+
+ while (!list_empty(&free_list)) {
+ tx_pkt_expected = list_first_entry(&free_list,
+ struct ipa_tx_pkt_wrapper,
+ link);
+ list_del(&tx_pkt_expected->link);
+ if (!tx_pkt_expected->no_unmap_dma)
+ dma_unmap_single(ipa_ctx->pdev,
+ tx_pkt_expected->mem.phys_base,
+ tx_pkt_expected->mem.size,
+ DMA_TO_DEVICE);
+ if (tx_pkt_expected->callback)
+ tx_pkt_expected->callback(tx_pkt_expected->user1,
+ tx_pkt_expected->user2);
+ if (tx_pkt_expected->cnt > 1 &&
+ tx_pkt_expected->cnt != IPA_LAST_DESC_CNT)
+ dma_free_coherent(ipa_ctx->pdev,
+ tx_pkt_expected->mult.size,
+ tx_pkt_expected->mult.base,
+ tx_pkt_expected->mult.phys_base);
+ kmem_cache_free(ipa_ctx->tx_pkt_wrapper_cache, tx_pkt_expected);
+ }
+}
+
static void ipa_wq_write_done_status(int src_pipe)
{
struct ipa_tx_pkt_wrapper *tx_pkt_expected;
struct ipa_sys_context *sys;
u32 cnt;
+ WARN_ON(src_pipe >= IPA_NUM_PIPES);
sys = ipa_ctx->ep[src_pipe].sys;
if (!sys) {
IPAERR("null sys pipe src %d\n", src_pipe);
@@ -114,7 +149,6 @@ static void ipa_wq_write_done_status(int src_pipe)
spin_lock_bh(&sys->spinlock);
if (unlikely(list_empty(&sys->head_desc_list))) {
- WARN_ON(1);
spin_unlock_bh(&sys->spinlock);
return;
}
@@ -750,12 +784,8 @@ static int ipa_handle_rx_core(struct ipa_sys_context *sys, bool process_all,
if (iov.addr == 0)
break;
- if (sys->ep->client == IPA_CLIENT_WLAN1_CONS ||
- sys->ep->client == IPA_CLIENT_WLAN2_CONS ||
- sys->ep->client == IPA_CLIENT_WLAN3_CONS ||
- sys->ep->client == IPA_CLIENT_WLAN4_CONS) {
+ if (IPA_CLIENT_IS_WLAN_CONS(sys->ep->client))
ipa_wlan_wq_rx_common(sys, iov.size);
- }
else
ipa_wq_rx_common(sys, iov.size);
@@ -968,24 +998,34 @@ int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
}
}
- memset(ep, 0, sizeof(struct ipa_ep_context));
+ memset(ep, 0, offsetof(struct ipa_ep_context, sys));
- ep->sys = kzalloc(sizeof(struct ipa_sys_context), GFP_KERNEL);
if (!ep->sys) {
- IPAERR("failed to sys ctx for client %d\n", sys_in->client);
- result = -ENOMEM;
- goto fail_and_disable_clocks;
- }
+ ep->sys = kzalloc(sizeof(struct ipa_sys_context), GFP_KERNEL);
+ if (!ep->sys) {
+ IPAERR("failed to sys ctx for client %d\n",
+ sys_in->client);
+ result = -ENOMEM;
+ goto fail_and_disable_clocks;
+ }
+
+ ep->sys->ep = ep;
+ snprintf(buff, IPA_RESOURCE_NAME_MAX, "ipawq%d",
+ sys_in->client);
+ ep->sys->wq = create_singlethread_workqueue(buff);
+ if (!ep->sys->wq) {
+ IPAERR("failed to create wq for client %d\n",
+ sys_in->client);
+ result = -EFAULT;
+ goto fail_wq;
+ }
- snprintf(buff, IPA_RESOURCE_NAME_MAX, "ipawq%d", sys_in->client);
- ep->sys->wq = create_singlethread_workqueue(buff);
- if (!ep->sys->wq) {
- IPAERR("failed to create wq for client %d\n", sys_in->client);
- result = -EFAULT;
- goto fail_wq;
+ INIT_LIST_HEAD(&ep->sys->head_desc_list);
+ spin_lock_init(&ep->sys->spinlock);
+ } else {
+ memset(ep->sys, 0, offsetof(struct ipa_sys_context, ep));
}
- ep->sys->ep = ep;
ep->skip_ep_cfg = sys_in->skip_ep_cfg;
if (ipa_assign_policy(sys_in, ep->sys)) {
IPAERR("failed to sys ctx for client %d\n", sys_in->client);
@@ -1000,9 +1040,6 @@ int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
ep->keep_ipa_awake = sys_in->keep_ipa_awake;
ep->avail_fifo_desc =
((sys_in->desc_fifo_sz/sizeof(struct sps_iovec))-1);
- INIT_LIST_HEAD(&ep->sys->head_desc_list);
-
- spin_lock_init(&ep->sys->spinlock);
result = ipa_enable_data_path(ipa_ep_idx);
if (result) {
@@ -1094,12 +1131,8 @@ int ipa_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
if (IPA_CLIENT_IS_CONS(sys_in->client))
ipa_replenish_rx_cache(ep->sys);
- if (sys_in->client == IPA_CLIENT_WLAN1_CONS ||
- sys_in->client == IPA_CLIENT_WLAN2_CONS ||
- sys_in->client == IPA_CLIENT_WLAN3_CONS ||
- sys_in->client == IPA_CLIENT_WLAN4_CONS) {
+ if (IPA_CLIENT_IS_WLAN_CONS(sys_in->client))
ipa_allocate_wlan_rx_common_cache(IPA_WLAN_COMM_RX_POOL_LOW);
- }
if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(sys_in->client))
ipa_install_dflt_flt_rules(ipa_ep_idx);
@@ -1142,6 +1175,7 @@ EXPORT_SYMBOL(ipa_setup_sys_pipe);
int ipa_teardown_sys_pipe(u32 clnt_hdl)
{
struct ipa_ep_context *ep;
+ int empty;
if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
IPAERR("bad parm.\n");
@@ -1153,19 +1187,31 @@ int ipa_teardown_sys_pipe(u32 clnt_hdl)
if (!ep->keep_ipa_awake)
ipa_inc_client_enable_clks();
- if (IPA_CLIENT_IS_CONS(ep->client))
- ipa_cleanup_rx(ep->sys);
-
ipa_disable_data_path(clnt_hdl);
+ ep->valid = 0;
+
+ do {
+ spin_lock_bh(&ep->sys->spinlock);
+ empty = list_empty(&ep->sys->head_desc_list);
+ spin_unlock_bh(&ep->sys->spinlock);
+ if (!empty)
+ usleep(100);
+ else
+ break;
+ } while (1);
+
+ flush_workqueue(ep->sys->wq);
sps_disconnect(ep->ep_hdl);
dma_free_coherent(ipa_ctx->pdev, ep->connect.desc.size,
ep->connect.desc.base,
ep->connect.desc.phys_base);
sps_free_endpoint(ep->ep_hdl);
- destroy_workqueue(ep->sys->wq);
- kfree(ep->sys);
+ if (IPA_CLIENT_IS_CONS(ep->client))
+ ipa_cleanup_rx(ep->sys);
+ else
+ ipa_purge_tx_list(ep->sys);
+
ipa_delete_dflt_flt_rules(clnt_hdl);
- memset(ep, 0, sizeof(struct ipa_ep_context));
ipa_dec_client_disable_clks();
@@ -1266,6 +1312,11 @@ int ipa_tx_dp(enum ipa_client_type dst, struct sk_buff *skb,
sys = ipa_ctx->ep[src_ep_idx].sys;
+ if (!sys->ep->valid) {
+ IPAERR("pipe not valid\n");
+ goto fail_gen;
+ }
+
if (dst_ep_idx != -1) {
/* SW data path */
cmd = kzalloc(sizeof(struct ipa_ip_packet_init), GFP_ATOMIC);
@@ -1451,7 +1502,6 @@ fail_kmem_cache_alloc:
return;
}
-
/**
* ipa_replenish_rx_cache() - Replenish the Rx packets cache.
*
@@ -1715,7 +1765,7 @@ begin:
skb2 = skb_clone(skb, GFP_KERNEL);
if (likely(skb2)) {
- if (skb->len < len) {
+ if (skb->len < len + IPA_PKT_STATUS_SIZE) {
IPADBG("SPL skb len %d len %d\n",
skb->len, len);
sys->prev_skb = skb2;
@@ -1748,15 +1798,10 @@ begin:
}
}
/* TX comp */
- WARN_ON(status->endp_src_idx >= IPA_NUM_PIPES);
- if (ipa_ctx->ep[status->endp_src_idx].sys) {
- ipa_wq_write_done_status(status->endp_src_idx);
- IPADBG("tx comp imp for %d\n",
- status->endp_src_idx);
- }
+ ipa_wq_write_done_status(status->endp_src_idx);
+ IPADBG("tx comp imp for %d\n", status->endp_src_idx);
} else {
/* TX comp */
- WARN_ON(status->endp_src_idx >= IPA_NUM_PIPES);
ipa_wq_write_done_status(status->endp_src_idx);
IPADBG("tx comp exp for %d\n", status->endp_src_idx);
skb_pull(skb, IPA_PKT_STATUS_SIZE);
@@ -2236,10 +2281,7 @@ static int ipa_assign_policy(struct ipa_sys_connect_params *in,
IPA_CLIENT_APPS_WAN_CONS) {
sys->pyld_hdlr = ipa_wan_rx_pyld_hdlr;
}
- } else if (in->client == IPA_CLIENT_WLAN1_CONS ||
- in->client == IPA_CLIENT_WLAN2_CONS ||
- in->client == IPA_CLIENT_WLAN3_CONS ||
- in->client == IPA_CLIENT_WLAN4_CONS) {
+ } else if (IPA_CLIENT_IS_WLAN_CONS(in->client)) {
IPADBG("assigning policy to client:%d",
in->client);
diff --git a/drivers/platform/msm/ipa/ipa_i.h b/drivers/platform/msm/ipa/ipa_i.h
index 03e547e1086..200eb48ea75 100644
--- a/drivers/platform/msm/ipa/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_i.h
@@ -356,13 +356,15 @@ struct ipa_ep_context {
u32 data_fifo_pipe_mem_ofst;
bool desc_fifo_client_allocated;
bool data_fifo_client_allocated;
- struct ipa_sys_context *sys;
u32 avail_fifo_desc;
u32 dflt_flt4_rule_hdl;
u32 dflt_flt6_rule_hdl;
bool skip_ep_cfg;
bool keep_ipa_awake;
bool resume_on_connect;
+
+ /* sys MUST be the last element of this struct */
+ struct ipa_sys_context *sys;
};
enum ipa_sys_pipe_policy {
@@ -382,11 +384,8 @@ enum ipa_sys_pipe_policy {
* IPA context specific to the system-bam pipes a.k.a LAN IN/OUT and WAN
*/
struct ipa_sys_context {
- struct list_head head_desc_list;
u32 len;
- spinlock_t spinlock;
struct sps_register_event event;
- struct ipa_ep_context *ep;
atomic_t curr_polling_state;
struct delayed_work switch_to_intr_work;
enum ipa_sys_pipe_policy policy;
@@ -395,7 +394,6 @@ struct ipa_sys_context {
void (*free_skb)(struct sk_buff *skb);
u32 rx_buff_sz;
u32 rx_pool_sz;
- struct workqueue_struct *wq;
struct sk_buff *prev_skb;
unsigned int len_rem;
unsigned int len_pad;
@@ -404,6 +402,13 @@ struct ipa_sys_context {
void (*sps_callback)(struct sps_event_notify *notify);
enum sps_option sps_option;
struct delayed_work replenish_rx_work;
+
+ /* ordering is important - mutable fields go above */
+ struct ipa_ep_context *ep;
+ struct list_head head_desc_list;
+ spinlock_t spinlock;
+ struct workqueue_struct *wq;
+ /* ordering is important - other immutable fields go below */
};
/**
diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h
index 6dd9b11deb9..acefe64b2e6 100644
--- a/include/uapi/linux/msm_ipa.h
+++ b/include/uapi/linux/msm_ipa.h
@@ -164,6 +164,12 @@ enum ipa_client_type {
(client) == IPA_CLIENT_USB3_CONS || \
(client) == IPA_CLIENT_USB4_CONS)
+#define IPA_CLIENT_IS_WLAN_CONS(client) \
+ ((client) == IPA_CLIENT_WLAN1_CONS || \
+ (client) == IPA_CLIENT_WLAN2_CONS || \
+ (client) == IPA_CLIENT_WLAN3_CONS || \
+ (client) == IPA_CLIENT_WLAN4_CONS)
+
/**
* enum ipa_ip_type - Address family: IPv4 or IPv6
*/