msm: ipa: Add napi support to rmnet ipa device

Add napi framework to use napi API's to poll
embedded data from IPA HW.

Change-Id: Icb6ec9d060ca4fb02e95c1e98bded89422bb1fff
CRs-Fixed: 1002026
Signed-off-by: Sunil Paidimarri <hisunil@codeaurora.org>
This commit is contained in:
Sunil Paidimarri 2016-06-08 11:36:05 -07:00 committed by Kyle Yan
parent e639e800ed
commit 238f9c0973
15 changed files with 977 additions and 381 deletions

View file

@ -10,6 +10,8 @@ Optional:
- qcom,ipa-loaduC: indicate that ipa uC should be loaded
- qcom,ipa-advertise-sg-support: determine how to respond to a query
regarding scatter-gather capability
- qcom,ipa-napi-enable: Boolean context flag to indicate whether
to enable napi framework or not
Example:
qcom,rmnet-ipa {

View file

@ -10,6 +10,8 @@ Optional:
- qcom,ipa-loaduC: indicate that ipa uC should be loaded
- qcom,ipa-advertise-sg-support: determine how to respond to a query
regarding scatter-gather capability
- qcom,ipa-napi-enable: Boolean context flag to indicate whether
to enable napi framework or not
Example:
qcom,rmnet-ipa3 {

View file

@ -2654,6 +2654,37 @@ void ipa_assert(void)
BUG();
}
/**
* ipa_rx_poll() - Poll the rx packets from IPA HW in the
* softirq context
*
* @budget: number of packets to be polled in single iteration
*
* Return codes: >= 0 : Actual number of packets polled
*
*/
int ipa_rx_poll(u32 clnt_hdl, int budget)
{
int ret;
IPA_API_DISPATCH_RETURN(ipa_rx_poll, clnt_hdl, budget);
return ret;
}
EXPORT_SYMBOL(ipa_rx_poll);
/**
* ipa_recycle_wan_skb() - Recycle the Wan skb
*
* @skb: skb that needs to recycle
*
*/
void ipa_recycle_wan_skb(struct sk_buff *skb)
{
IPA_API_DISPATCH(ipa_recycle_wan_skb, skb);
}
EXPORT_SYMBOL(ipa_recycle_wan_skb);
static const struct dev_pm_ops ipa_pm_ops = {
.suspend_noirq = ipa_ap_suspend,
.resume_noirq = ipa_ap_resume,

View file

@ -356,6 +356,10 @@ struct ipa_api_controller {
void *(*ipa_get_ipc_logbuf_low)(void);
int (*ipa_rx_poll)(u32 clnt_hdl, int budget);
void (*ipa_recycle_wan_skb)(struct sk_buff *skb);
};
#ifdef CONFIG_IPA

View file

@ -77,6 +77,7 @@ static void ipa_dma_memcpy_notify(struct ipa_sys_context *sys,
struct sps_iovec *iovec);
static u32 ipa_adjust_ra_buff_base_sz(u32 aggr_byte_limit);
static void ipa_fast_replenish_rx_cache(struct ipa_sys_context *sys);
static void ipa_wq_write_done_common(struct ipa_sys_context *sys, u32 cnt)
{
@ -761,6 +762,29 @@ static void ipa_sps_irq_tx_no_aggr_notify(struct sps_event_notify *notify)
}
}
/**
* ipa_poll_pkt() - Poll packet from SPS BAM
* return 0 to caller on poll successfully
* else -EIO
*
*/
static int ipa_poll_pkt(struct ipa_sys_context *sys,
struct sps_iovec *iov)
{
int ret;
ret = sps_get_iovec(sys->ep->ep_hdl, iov);
if (ret) {
IPAERR("sps_get_iovec failed %d\n", ret);
return ret;
}
if (iov->addr == 0)
return -EIO;
return 0;
}
/**
* ipa_handle_rx_core() - The core functionality of packet reception. This
* function is read from multiple code paths.
@ -787,14 +811,10 @@ static int ipa_handle_rx_core(struct ipa_sys_context *sys, bool process_all,
if (cnt && !process_all)
break;
ret = sps_get_iovec(sys->ep->ep_hdl, &iov);
if (ret) {
IPAERR("sps_get_iovec failed %d\n", ret);
ret = ipa_poll_pkt(sys, &iov);
if (ret)
break;
}
if (iov.addr == 0)
break;
if (IPA_CLIENT_IS_MEMCPY_DMA_CONS(sys->ep->client))
ipa_dma_memcpy_notify(sys, &iov);
else if (IPA_CLIENT_IS_WLAN_CONS(sys->ep->client))
@ -851,7 +871,8 @@ static void ipa_rx_switch_to_intr_mode(struct ipa_sys_context *sys)
goto fail;
}
atomic_set(&sys->curr_polling_state, 0);
ipa_handle_rx_core(sys, true, false);
if (!sys->ep->napi_enabled)
ipa_handle_rx_core(sys, true, false);
ipa_dec_release_wakelock(sys->ep->wakelock_client);
return;
@ -965,26 +986,30 @@ static void ipa_sps_irq_rx_notify(struct sps_event_notify *notify)
case SPS_EVENT_EOT:
if (IPA_CLIENT_IS_APPS_CONS(sys->ep->client))
atomic_set(&ipa_ctx->sps_pm.eot_activity, 1);
if (!atomic_read(&sys->curr_polling_state)) {
ret = sps_get_config(sys->ep->ep_hdl,
&sys->ep->connect);
if (ret) {
IPAERR("sps_get_config() failed %d\n", ret);
break;
}
sys->ep->connect.options = SPS_O_AUTO_ENABLE |
SPS_O_ACK_TRANSFERS | SPS_O_POLL;
ret = sps_set_config(sys->ep->ep_hdl,
&sys->ep->connect);
if (ret) {
IPAERR("sps_set_config() failed %d\n", ret);
break;
}
ipa_inc_acquire_wakelock(sys->ep->wakelock_client);
atomic_set(&sys->curr_polling_state, 1);
trace_intr_to_poll(sys->ep->client);
queue_work(sys->wq, &sys->work);
if (atomic_read(&sys->curr_polling_state)) {
sys->ep->eot_in_poll_err++;
break;
}
ret = sps_get_config(sys->ep->ep_hdl,
&sys->ep->connect);
if (ret) {
IPAERR("sps_get_config() failed %d\n", ret);
break;
}
sys->ep->connect.options = SPS_O_AUTO_ENABLE |
SPS_O_ACK_TRANSFERS | SPS_O_POLL;
ret = sps_set_config(sys->ep->ep_hdl,
&sys->ep->connect);
if (ret) {
IPAERR("sps_set_config() failed %d\n", ret);
break;
}
ipa_inc_acquire_wakelock(sys->ep->wakelock_client);
atomic_set(&sys->curr_polling_state, 1);
trace_intr_to_poll(sys->ep->client);
queue_work(sys->wq, &sys->work);
break;
default:
IPAERR("received unexpected event id %d\n", notify->event_id);
@ -1041,6 +1066,58 @@ static void ipa_handle_rx(struct ipa_sys_context *sys)
IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
}
/**
* ipa2_rx_poll() - Poll the rx packets from IPA HW. This
* function is exectued in the softirq context
*
* if input budget is zero, the driver switches back to
* interrupt mode
*
* return number of polled packets, on error 0(zero)
*/
int ipa2_rx_poll(u32 clnt_hdl, int weight)
{
struct ipa_ep_context *ep;
int ret;
int cnt = 0;
unsigned int delay = 1;
struct sps_iovec iov;
IPADBG("\n");
if (clnt_hdl >= ipa_ctx->ipa_num_pipes ||
ipa_ctx->ep[clnt_hdl].valid == 0) {
IPAERR("bad parm 0x%x\n", clnt_hdl);
return cnt;
}
ep = &ipa_ctx->ep[clnt_hdl];
while (cnt < weight &&
atomic_read(&ep->sys->curr_polling_state)) {
ret = ipa_poll_pkt(ep->sys, &iov);
if (ret)
break;
ipa_wq_rx_common(ep->sys, iov.size);
cnt += 5;
};
if (cnt == 0) {
ep->inactive_cycles++;
ep->client_notify(ep->priv, IPA_CLIENT_COMP_NAPI, 0);
if (ep->inactive_cycles > 3 || ep->sys->len == 0) {
ep->switch_to_intr = true;
delay = 0;
}
queue_delayed_work(ep->sys->wq,
&ep->sys->switch_to_intr_work, msecs_to_jiffies(delay));
} else
ep->inactive_cycles = 0;
return cnt;
}
static void switch_to_intr_rx_work_func(struct work_struct *work)
{
struct delayed_work *dwork;
@ -1048,7 +1125,18 @@ static void switch_to_intr_rx_work_func(struct work_struct *work)
dwork = container_of(work, struct delayed_work, work);
sys = container_of(dwork, struct ipa_sys_context, switch_to_intr_work);
ipa_handle_rx(sys);
if (sys->ep->napi_enabled) {
if (sys->ep->switch_to_intr) {
ipa_rx_switch_to_intr_mode(sys);
IPA_ACTIVE_CLIENTS_DEC_SPECIAL("NAPI");
sys->ep->switch_to_intr = false;
sys->ep->inactive_cycles = 0;
} else
sys->ep->client_notify(sys->ep->priv,
IPA_CLIENT_START_POLL, 0);
} else
ipa_handle_rx(sys);
}
/**
@ -1196,6 +1284,7 @@ int ipa2_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
}
INIT_LIST_HEAD(&ep->sys->head_desc_list);
INIT_LIST_HEAD(&ep->sys->rcycl_list);
spin_lock_init(&ep->sys->spinlock);
} else {
memset(ep->sys, 0, offsetof(struct ipa_sys_context, ep));
@ -1211,6 +1300,7 @@ int ipa2_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
ep->valid = 1;
ep->client = sys_in->client;
ep->client_notify = sys_in->notify;
ep->napi_enabled = sys_in->napi_enabled;
ep->priv = sys_in->priv;
ep->keep_ipa_awake = sys_in->keep_ipa_awake;
atomic_set(&ep->avail_fifo_desc,
@ -1334,9 +1424,7 @@ int ipa2_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
*clnt_hdl = ipa_ep_idx;
if (nr_cpu_ids > 1 &&
(sys_in->client == IPA_CLIENT_APPS_LAN_CONS ||
sys_in->client == IPA_CLIENT_APPS_WAN_CONS)) {
if (ep->sys->repl_hdlr == ipa_fast_replenish_rx_cache) {
ep->sys->repl.capacity = ep->sys->rx_pool_sz + 1;
ep->sys->repl.cache = kzalloc(ep->sys->repl.capacity *
sizeof(void *), GFP_KERNEL);
@ -1425,7 +1513,12 @@ int ipa2_teardown_sys_pipe(u32 clnt_hdl)
IPA_ACTIVE_CLIENTS_INC_EP(ipa2_get_client_mapping(clnt_hdl));
ipa_disable_data_path(clnt_hdl);
ep->valid = 0;
if (ep->napi_enabled) {
ep->switch_to_intr = true;
do {
usleep_range(95, 105);
} while (atomic_read(&ep->sys->curr_polling_state));
}
if (IPA_CLIENT_IS_PROD(ep->client)) {
do {
@ -1471,6 +1564,7 @@ int ipa2_teardown_sys_pipe(u32 clnt_hdl)
if (!atomic_read(&ipa_ctx->wc_memb.active_clnt_cnt))
ipa_cleanup_wlan_rx_common_cache();
ep->valid = 0;
IPA_ACTIVE_CLIENTS_DEC_EP(ipa2_get_client_mapping(clnt_hdl));
IPADBG("client (ep: %d) disconnected\n", clnt_hdl);
@ -1721,7 +1815,13 @@ static void ipa_wq_handle_rx(struct work_struct *work)
struct ipa_sys_context *sys;
sys = container_of(work, struct ipa_sys_context, work);
ipa_handle_rx(sys);
if (sys->ep->napi_enabled) {
IPA_ACTIVE_CLIENTS_INC_SPECIAL("NAPI");
sys->ep->client_notify(sys->ep->priv,
IPA_CLIENT_START_POLL, 0);
} else
ipa_handle_rx(sys);
}
static void ipa_wq_repl_rx(struct work_struct *work)
@ -2024,6 +2124,63 @@ fail_kmem_cache_alloc:
msecs_to_jiffies(1));
}
static void ipa_replenish_rx_cache_recycle(struct ipa_sys_context *sys)
{
void *ptr;
struct ipa_rx_pkt_wrapper *rx_pkt;
int ret;
int rx_len_cached = 0;
rx_len_cached = sys->len;
while (rx_len_cached < sys->rx_pool_sz) {
spin_lock_bh(&sys->spinlock);
if (list_empty(&sys->rcycl_list))
goto fail_kmem_cache_alloc;
rx_pkt = list_first_entry(&sys->rcycl_list,
struct ipa_rx_pkt_wrapper, link);
list_del(&rx_pkt->link);
spin_unlock_bh(&sys->spinlock);
INIT_LIST_HEAD(&rx_pkt->link);
ptr = skb_put(rx_pkt->data.skb, sys->rx_buff_sz);
rx_pkt->data.dma_addr = dma_map_single(ipa_ctx->pdev,
ptr, sys->rx_buff_sz, DMA_FROM_DEVICE);
if (rx_pkt->data.dma_addr == 0 ||
rx_pkt->data.dma_addr == ~0)
goto fail_dma_mapping;
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
rx_len_cached = ++sys->len;
ret = sps_transfer_one(sys->ep->ep_hdl,
rx_pkt->data.dma_addr, sys->rx_buff_sz, rx_pkt, 0);
if (ret) {
IPAERR("sps_transfer_one failed %d\n", ret);
goto fail_sps_transfer;
}
}
return;
fail_sps_transfer:
rx_len_cached = --sys->len;
list_del(&rx_pkt->link);
INIT_LIST_HEAD(&rx_pkt->link);
dma_unmap_single(ipa_ctx->pdev, rx_pkt->data.dma_addr,
sys->rx_buff_sz, DMA_FROM_DEVICE);
fail_dma_mapping:
spin_lock_bh(&sys->spinlock);
list_add_tail(&rx_pkt->link, &sys->rcycl_list);
INIT_LIST_HEAD(&rx_pkt->link);
spin_unlock_bh(&sys->spinlock);
fail_kmem_cache_alloc:
spin_unlock_bh(&sys->spinlock);
if (rx_len_cached == 0)
queue_delayed_work(sys->wq, &sys->replenish_rx_work,
msecs_to_jiffies(1));
}
static void ipa_fast_replenish_rx_cache(struct ipa_sys_context *sys)
{
struct ipa_rx_pkt_wrapper *rx_pkt;
@ -2035,8 +2192,10 @@ static void ipa_fast_replenish_rx_cache(struct ipa_sys_context *sys)
curr = atomic_read(&sys->repl.head_idx);
while (rx_len_cached < sys->rx_pool_sz) {
if (curr == atomic_read(&sys->repl.tail_idx))
if (curr == atomic_read(&sys->repl.tail_idx)) {
queue_work(sys->repl_wq, &sys->repl_work);
break;
}
rx_pkt = sys->repl.cache[curr];
list_add_tail(&rx_pkt->link, &sys->head_desc_list);
@ -2107,6 +2266,15 @@ static void ipa_cleanup_rx(struct ipa_sys_context *sys)
kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt);
}
list_for_each_entry_safe(rx_pkt, r,
&sys->rcycl_list, link) {
list_del(&rx_pkt->link);
dma_unmap_single(ipa_ctx->pdev, rx_pkt->data.dma_addr,
sys->rx_buff_sz, DMA_FROM_DEVICE);
sys->free_skb(rx_pkt->data.skb);
kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt);
}
if (sys->repl.cache) {
head = atomic_read(&sys->repl.head_idx);
tail = atomic_read(&sys->repl.tail_idx);
@ -2471,6 +2639,10 @@ static int ipa_wan_rx_pyld_hdlr(struct sk_buff *skb,
IPA_RECEIVE, (unsigned long)(skb));
return rc;
}
if (sys->repl_hdlr == ipa_replenish_rx_cache_recycle) {
IPAERR("Recycle should enable only with GRO Aggr\n");
ipa_assert();
}
/*
* payload splits across 2 buff or more,
* take the start of the payload from prev_skb
@ -2721,6 +2893,37 @@ void ipa_lan_rx_cb(void *priv, enum ipa_dp_evt_type evt, unsigned long data)
ep->client_notify(ep->priv, IPA_RECEIVE, (unsigned long)(rx_skb));
}
void ipa2_recycle_wan_skb(struct sk_buff *skb)
{
struct ipa_rx_pkt_wrapper *rx_pkt;
int ep_idx = ipa2_get_ep_mapping(
IPA_CLIENT_APPS_WAN_CONS);
gfp_t flag = GFP_NOWAIT | __GFP_NOWARN |
(ipa_ctx->use_dma_zone ? GFP_DMA : 0);
if (unlikely(ep_idx == -1)) {
IPAERR("dest EP does not exist\n");
ipa_assert();
}
rx_pkt = kmem_cache_zalloc(
ipa_ctx->rx_pkt_wrapper_cache, flag);
if (!rx_pkt)
ipa_assert();
INIT_WORK(&rx_pkt->work, ipa_wq_rx_avail);
rx_pkt->sys = ipa_ctx->ep[ep_idx].sys;
rx_pkt->data.skb = skb;
rx_pkt->data.dma_addr = 0;
ipa_skb_recycle(rx_pkt->data.skb);
skb_reserve(rx_pkt->data.skb, IPA_HEADROOM);
INIT_LIST_HEAD(&rx_pkt->link);
spin_lock_bh(&rx_pkt->sys->spinlock);
list_add_tail(&rx_pkt->link, &rx_pkt->sys->rcycl_list);
spin_unlock_bh(&rx_pkt->sys->spinlock);
}
static void ipa_wq_rx_common(struct ipa_sys_context *sys, u32 size)
{
struct ipa_rx_pkt_wrapper *rx_pkt_expected;
@ -2858,6 +3061,220 @@ static int ipa_odu_rx_pyld_hdlr(struct sk_buff *rx_skb,
return 0;
}
static int ipa_assign_policy_v2(struct ipa_sys_connect_params *in,
struct ipa_sys_context *sys)
{
unsigned long int aggr_byte_limit;
sys->ep->status.status_en = true;
sys->ep->wakelock_client = IPA_WAKELOCK_REF_CLIENT_MAX;
if (IPA_CLIENT_IS_PROD(in->client)) {
if (!sys->ep->skip_ep_cfg) {
sys->policy = IPA_POLICY_NOINTR_MODE;
sys->sps_option = SPS_O_AUTO_ENABLE;
sys->sps_callback = NULL;
sys->ep->status.status_ep = ipa2_get_ep_mapping(
IPA_CLIENT_APPS_LAN_CONS);
if (IPA_CLIENT_IS_MEMCPY_DMA_PROD(in->client))
sys->ep->status.status_en = false;
} else {
sys->policy = IPA_POLICY_INTR_MODE;
sys->sps_option = (SPS_O_AUTO_ENABLE |
SPS_O_EOT);
sys->sps_callback =
ipa_sps_irq_tx_no_aggr_notify;
}
return 0;
}
aggr_byte_limit =
(unsigned long int)IPA_GENERIC_RX_BUFF_SZ(
ipa_adjust_ra_buff_base_sz(
in->ipa_ep_cfg.aggr.aggr_byte_limit));
if (in->client == IPA_CLIENT_APPS_LAN_CONS ||
in->client == IPA_CLIENT_APPS_WAN_CONS) {
sys->policy = IPA_POLICY_INTR_POLL_MODE;
sys->sps_option = (SPS_O_AUTO_ENABLE | SPS_O_EOT
| SPS_O_ACK_TRANSFERS);
sys->sps_callback = ipa_sps_irq_rx_notify;
INIT_WORK(&sys->work, ipa_wq_handle_rx);
INIT_DELAYED_WORK(&sys->switch_to_intr_work,
switch_to_intr_rx_work_func);
INIT_DELAYED_WORK(&sys->replenish_rx_work,
replenish_rx_work_func);
INIT_WORK(&sys->repl_work, ipa_wq_repl_rx);
atomic_set(&sys->curr_polling_state, 0);
sys->rx_buff_sz = IPA_GENERIC_RX_BUFF_SZ(
IPA_GENERIC_RX_BUFF_BASE_SZ) -
IPA_HEADROOM;
sys->get_skb = ipa_get_skb_ipa_rx_headroom;
sys->free_skb = ipa_free_skb_rx;
in->ipa_ep_cfg.aggr.aggr_en = IPA_ENABLE_AGGR;
in->ipa_ep_cfg.aggr.aggr = IPA_GENERIC;
in->ipa_ep_cfg.aggr.aggr_time_limit =
IPA_GENERIC_AGGR_TIME_LIMIT;
if (in->client == IPA_CLIENT_APPS_LAN_CONS) {
sys->pyld_hdlr = ipa_lan_rx_pyld_hdlr;
if (nr_cpu_ids > 1) {
sys->repl_hdlr =
ipa_fast_replenish_rx_cache;
sys->repl_trig_thresh =
sys->rx_pool_sz / 8;
} else {
sys->repl_hdlr =
ipa_replenish_rx_cache;
}
sys->rx_pool_sz =
IPA_GENERIC_RX_POOL_SZ;
in->ipa_ep_cfg.aggr.aggr_byte_limit =
IPA_GENERIC_AGGR_BYTE_LIMIT;
in->ipa_ep_cfg.aggr.aggr_pkt_limit =
IPA_GENERIC_AGGR_PKT_LIMIT;
sys->ep->wakelock_client =
IPA_WAKELOCK_REF_CLIENT_LAN_RX;
} else if (in->client ==
IPA_CLIENT_APPS_WAN_CONS) {
sys->pyld_hdlr = ipa_wan_rx_pyld_hdlr;
if (in->napi_enabled) {
sys->repl_hdlr =
ipa_replenish_rx_cache_recycle;
sys->rx_pool_sz =
IPA_WAN_NAPI_CONS_RX_POOL_SZ;
} else {
if (nr_cpu_ids > 1) {
sys->repl_hdlr =
ipa_fast_replenish_rx_cache;
sys->repl_trig_thresh =
sys->rx_pool_sz / 8;
} else {
sys->repl_hdlr =
ipa_replenish_rx_cache;
}
sys->rx_pool_sz =
ipa_ctx->wan_rx_ring_size;
}
sys->ep->wakelock_client =
IPA_WAKELOCK_REF_CLIENT_WAN_RX;
in->ipa_ep_cfg.aggr.aggr_sw_eof_active
= true;
if (ipa_ctx->ipa_client_apps_wan_cons_agg_gro) {
IPAERR("get close-by %u\n",
ipa_adjust_ra_buff_base_sz(
in->ipa_ep_cfg.aggr.
aggr_byte_limit));
IPAERR("set rx_buff_sz %lu\n", aggr_byte_limit);
/* disable ipa_status */
sys->ep->status.
status_en = false;
sys->rx_buff_sz =
IPA_GENERIC_RX_BUFF_SZ(
ipa_adjust_ra_buff_base_sz(
in->ipa_ep_cfg.aggr.
aggr_byte_limit));
in->ipa_ep_cfg.aggr.
aggr_byte_limit =
sys->rx_buff_sz < in->
ipa_ep_cfg.aggr.aggr_byte_limit ?
IPA_ADJUST_AGGR_BYTE_LIMIT(
sys->rx_buff_sz) :
IPA_ADJUST_AGGR_BYTE_LIMIT(
in->ipa_ep_cfg.
aggr.aggr_byte_limit);
IPAERR("set aggr_limit %lu\n",
(unsigned long int)
in->ipa_ep_cfg.aggr.
aggr_byte_limit);
} else {
in->ipa_ep_cfg.aggr.
aggr_byte_limit =
IPA_GENERIC_AGGR_BYTE_LIMIT;
in->ipa_ep_cfg.aggr.
aggr_pkt_limit =
IPA_GENERIC_AGGR_PKT_LIMIT;
}
}
} else if (IPA_CLIENT_IS_WLAN_CONS(in->client)) {
IPADBG("assigning policy to client:%d",
in->client);
sys->ep->status.status_en = false;
sys->policy = IPA_POLICY_INTR_POLL_MODE;
sys->sps_option = (SPS_O_AUTO_ENABLE | SPS_O_EOT
| SPS_O_ACK_TRANSFERS);
sys->sps_callback = ipa_sps_irq_rx_notify;
INIT_WORK(&sys->work, ipa_wq_handle_rx);
INIT_DELAYED_WORK(&sys->switch_to_intr_work,
switch_to_intr_rx_work_func);
INIT_DELAYED_WORK(&sys->replenish_rx_work,
replenish_rx_work_func);
atomic_set(&sys->curr_polling_state, 0);
sys->rx_buff_sz = IPA_WLAN_RX_BUFF_SZ;
sys->rx_pool_sz = in->desc_fifo_sz /
sizeof(struct sps_iovec) - 1;
if (sys->rx_pool_sz > IPA_WLAN_RX_POOL_SZ)
sys->rx_pool_sz = IPA_WLAN_RX_POOL_SZ;
sys->pyld_hdlr = NULL;
sys->repl_hdlr = ipa_replenish_wlan_rx_cache;
sys->get_skb = ipa_get_skb_ipa_rx;
sys->free_skb = ipa_free_skb_rx;
in->ipa_ep_cfg.aggr.aggr_en = IPA_BYPASS_AGGR;
sys->ep->wakelock_client =
IPA_WAKELOCK_REF_CLIENT_WLAN_RX;
} else if (IPA_CLIENT_IS_ODU_CONS(in->client)) {
IPADBG("assigning policy to client:%d",
in->client);
sys->ep->status.status_en = false;
sys->policy = IPA_POLICY_INTR_POLL_MODE;
sys->sps_option = (SPS_O_AUTO_ENABLE | SPS_O_EOT
| SPS_O_ACK_TRANSFERS);
sys->sps_callback = ipa_sps_irq_rx_notify;
INIT_WORK(&sys->work, ipa_wq_handle_rx);
INIT_DELAYED_WORK(&sys->switch_to_intr_work,
switch_to_intr_rx_work_func);
INIT_DELAYED_WORK(&sys->replenish_rx_work,
replenish_rx_work_func);
atomic_set(&sys->curr_polling_state, 0);
sys->rx_buff_sz = IPA_ODU_RX_BUFF_SZ;
sys->rx_pool_sz = in->desc_fifo_sz /
sizeof(struct sps_iovec) - 1;
if (sys->rx_pool_sz > IPA_ODU_RX_POOL_SZ)
sys->rx_pool_sz = IPA_ODU_RX_POOL_SZ;
sys->pyld_hdlr = ipa_odu_rx_pyld_hdlr;
sys->get_skb = ipa_get_skb_ipa_rx;
sys->free_skb = ipa_free_skb_rx;
sys->repl_hdlr = ipa_replenish_rx_cache;
sys->ep->wakelock_client =
IPA_WAKELOCK_REF_CLIENT_ODU_RX;
} else if (in->client ==
IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS) {
IPADBG("assigning policy to client:%d",
in->client);
sys->ep->status.status_en = false;
sys->policy = IPA_POLICY_INTR_POLL_MODE;
sys->sps_option = (SPS_O_AUTO_ENABLE | SPS_O_EOT
| SPS_O_ACK_TRANSFERS);
sys->sps_callback = ipa_sps_irq_rx_notify;
INIT_WORK(&sys->work, ipa_wq_handle_rx);
INIT_DELAYED_WORK(&sys->switch_to_intr_work,
switch_to_intr_rx_work_func);
} else if (in->client ==
IPA_CLIENT_MEMCPY_DMA_SYNC_CONS) {
IPADBG("assigning policy to client:%d",
in->client);
sys->ep->status.status_en = false;
sys->policy = IPA_POLICY_NOINTR_MODE;
sys->sps_option = SPS_O_AUTO_ENABLE |
SPS_O_ACK_TRANSFERS | SPS_O_POLL;
} else {
IPAERR("Need to install a RX pipe hdlr\n");
WARN_ON(1);
return -EINVAL;
}
return 0;
}
static int ipa_assign_policy(struct ipa_sys_connect_params *in,
struct ipa_sys_context *sys)
{
@ -2904,203 +3321,14 @@ static int ipa_assign_policy(struct ipa_sys_connect_params *in,
WARN_ON(1);
return -EINVAL;
}
} else if (ipa_ctx->ipa_hw_type >= IPA_HW_v2_0) {
sys->ep->status.status_en = true;
sys->ep->wakelock_client = IPA_WAKELOCK_REF_CLIENT_MAX;
if (IPA_CLIENT_IS_PROD(in->client)) {
if (!sys->ep->skip_ep_cfg) {
sys->policy = IPA_POLICY_NOINTR_MODE;
sys->sps_option = SPS_O_AUTO_ENABLE;
sys->sps_callback = NULL;
sys->ep->status.status_ep = ipa2_get_ep_mapping(
IPA_CLIENT_APPS_LAN_CONS);
if (IPA_CLIENT_IS_MEMCPY_DMA_PROD(in->client))
sys->ep->status.status_en = false;
} else {
sys->policy = IPA_POLICY_INTR_MODE;
sys->sps_option = (SPS_O_AUTO_ENABLE |
SPS_O_EOT);
sys->sps_callback =
ipa_sps_irq_tx_no_aggr_notify;
}
} else {
if (in->client == IPA_CLIENT_APPS_LAN_CONS ||
in->client == IPA_CLIENT_APPS_WAN_CONS) {
sys->policy = IPA_POLICY_INTR_POLL_MODE;
sys->sps_option = (SPS_O_AUTO_ENABLE | SPS_O_EOT
| SPS_O_ACK_TRANSFERS);
sys->sps_callback = ipa_sps_irq_rx_notify;
INIT_WORK(&sys->work, ipa_wq_handle_rx);
INIT_DELAYED_WORK(&sys->switch_to_intr_work,
switch_to_intr_rx_work_func);
INIT_DELAYED_WORK(&sys->replenish_rx_work,
replenish_rx_work_func);
INIT_WORK(&sys->repl_work, ipa_wq_repl_rx);
atomic_set(&sys->curr_polling_state, 0);
sys->rx_buff_sz = IPA_GENERIC_RX_BUFF_SZ(
IPA_GENERIC_RX_BUFF_BASE_SZ) -
IPA_HEADROOM;
sys->get_skb = ipa_get_skb_ipa_rx_headroom;
sys->free_skb = ipa_free_skb_rx;
in->ipa_ep_cfg.aggr.aggr_en = IPA_ENABLE_AGGR;
in->ipa_ep_cfg.aggr.aggr = IPA_GENERIC;
in->ipa_ep_cfg.aggr.aggr_time_limit =
IPA_GENERIC_AGGR_TIME_LIMIT;
if (in->client == IPA_CLIENT_APPS_LAN_CONS) {
sys->pyld_hdlr = ipa_lan_rx_pyld_hdlr;
sys->rx_pool_sz =
IPA_GENERIC_RX_POOL_SZ;
in->ipa_ep_cfg.aggr.aggr_byte_limit =
IPA_GENERIC_AGGR_BYTE_LIMIT;
in->ipa_ep_cfg.aggr.aggr_pkt_limit =
IPA_GENERIC_AGGR_PKT_LIMIT;
sys->ep->wakelock_client =
IPA_WAKELOCK_REF_CLIENT_LAN_RX;
} else if (in->client ==
IPA_CLIENT_APPS_WAN_CONS) {
sys->pyld_hdlr = ipa_wan_rx_pyld_hdlr;
sys->rx_pool_sz =
ipa_ctx->wan_rx_ring_size;
sys->ep->wakelock_client =
IPA_WAKELOCK_REF_CLIENT_WAN_RX;
in->ipa_ep_cfg.aggr.aggr_sw_eof_active
= true;
if (ipa_ctx->
ipa_client_apps_wan_cons_agg_gro) {
IPAERR("get close-by %u\n",
ipa_adjust_ra_buff_base_sz(
in->ipa_ep_cfg.aggr.
aggr_byte_limit));
IPAERR("set rx_buff_sz %lu\n",
(unsigned long int)
IPA_GENERIC_RX_BUFF_SZ(
ipa_adjust_ra_buff_base_sz(
in->ipa_ep_cfg.
aggr.aggr_byte_limit)));
/* disable ipa_status */
sys->ep->status.
status_en = false;
sys->rx_buff_sz =
IPA_GENERIC_RX_BUFF_SZ(
ipa_adjust_ra_buff_base_sz(
in->ipa_ep_cfg.aggr.
aggr_byte_limit));
in->ipa_ep_cfg.aggr.
aggr_byte_limit =
sys->rx_buff_sz < in->
ipa_ep_cfg.aggr.
aggr_byte_limit ?
IPA_ADJUST_AGGR_BYTE_LIMIT(
sys->rx_buff_sz) :
IPA_ADJUST_AGGR_BYTE_LIMIT(
in->ipa_ep_cfg.
aggr.aggr_byte_limit);
IPAERR("set aggr_limit %lu\n",
(unsigned long int)
in->ipa_ep_cfg.aggr.
aggr_byte_limit);
} else {
in->ipa_ep_cfg.aggr.
aggr_byte_limit =
IPA_GENERIC_AGGR_BYTE_LIMIT;
in->ipa_ep_cfg.aggr.
aggr_pkt_limit =
IPA_GENERIC_AGGR_PKT_LIMIT;
}
}
sys->repl_trig_thresh = sys->rx_pool_sz / 8;
if (nr_cpu_ids > 1)
sys->repl_hdlr =
ipa_fast_replenish_rx_cache;
else
sys->repl_hdlr = ipa_replenish_rx_cache;
} else if (IPA_CLIENT_IS_WLAN_CONS(in->client)) {
IPADBG("assigning policy to client:%d",
in->client);
sys->ep->status.status_en = false;
sys->policy = IPA_POLICY_INTR_POLL_MODE;
sys->sps_option = (SPS_O_AUTO_ENABLE | SPS_O_EOT
| SPS_O_ACK_TRANSFERS);
sys->sps_callback = ipa_sps_irq_rx_notify;
INIT_WORK(&sys->work, ipa_wq_handle_rx);
INIT_DELAYED_WORK(&sys->switch_to_intr_work,
switch_to_intr_rx_work_func);
INIT_DELAYED_WORK(&sys->replenish_rx_work,
replenish_rx_work_func);
atomic_set(&sys->curr_polling_state, 0);
sys->rx_buff_sz = IPA_WLAN_RX_BUFF_SZ;
sys->rx_pool_sz = in->desc_fifo_sz/
sizeof(struct sps_iovec) - 1;
if (sys->rx_pool_sz > IPA_WLAN_RX_POOL_SZ)
sys->rx_pool_sz = IPA_WLAN_RX_POOL_SZ;
sys->pyld_hdlr = NULL;
sys->repl_hdlr = ipa_replenish_wlan_rx_cache;
sys->get_skb = ipa_get_skb_ipa_rx;
sys->free_skb = ipa_free_skb_rx;
in->ipa_ep_cfg.aggr.aggr_en = IPA_BYPASS_AGGR;
sys->ep->wakelock_client =
IPA_WAKELOCK_REF_CLIENT_WLAN_RX;
} else if (IPA_CLIENT_IS_ODU_CONS(in->client)) {
IPADBG("assigning policy to client:%d",
in->client);
return 0;
} else if (ipa_ctx->ipa_hw_type >= IPA_HW_v2_0)
return ipa_assign_policy_v2(in, sys);
sys->ep->status.status_en = false;
sys->policy = IPA_POLICY_INTR_POLL_MODE;
sys->sps_option = (SPS_O_AUTO_ENABLE | SPS_O_EOT
| SPS_O_ACK_TRANSFERS);
sys->sps_callback = ipa_sps_irq_rx_notify;
INIT_WORK(&sys->work, ipa_wq_handle_rx);
INIT_DELAYED_WORK(&sys->switch_to_intr_work,
switch_to_intr_rx_work_func);
INIT_DELAYED_WORK(&sys->replenish_rx_work,
replenish_rx_work_func);
atomic_set(&sys->curr_polling_state, 0);
sys->rx_buff_sz = IPA_ODU_RX_BUFF_SZ;
sys->rx_pool_sz = in->desc_fifo_sz /
sizeof(struct sps_iovec) - 1;
if (sys->rx_pool_sz > IPA_ODU_RX_POOL_SZ)
sys->rx_pool_sz = IPA_ODU_RX_POOL_SZ;
sys->pyld_hdlr = ipa_odu_rx_pyld_hdlr;
sys->get_skb = ipa_get_skb_ipa_rx;
sys->free_skb = ipa_free_skb_rx;
sys->repl_hdlr = ipa_replenish_rx_cache;
sys->ep->wakelock_client =
IPA_WAKELOCK_REF_CLIENT_ODU_RX;
} else if (in->client ==
IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS) {
IPADBG("assigning policy to client:%d",
in->client);
sys->ep->status.status_en = false;
sys->policy = IPA_POLICY_INTR_POLL_MODE;
sys->sps_option = (SPS_O_AUTO_ENABLE | SPS_O_EOT
| SPS_O_ACK_TRANSFERS);
sys->sps_callback = ipa_sps_irq_rx_notify;
INIT_WORK(&sys->work, ipa_wq_handle_rx);
INIT_DELAYED_WORK(&sys->switch_to_intr_work,
switch_to_intr_rx_work_func);
} else if (in->client ==
IPA_CLIENT_MEMCPY_DMA_SYNC_CONS) {
IPADBG("assigning policy to client:%d",
in->client);
sys->ep->status.status_en = false;
sys->policy = IPA_POLICY_NOINTR_MODE;
sys->sps_option = SPS_O_AUTO_ENABLE |
SPS_O_ACK_TRANSFERS | SPS_O_POLL;
} else {
IPAERR("Need to install a RX pipe hdlr\n");
WARN_ON(1);
return -EINVAL;
}
}
} else {
IPAERR("Unsupported HW type %d\n", ipa_ctx->ipa_hw_type);
WARN_ON(1);
return -EINVAL;
}
return 0;
IPAERR("Unsupported HW type %d\n", ipa_ctx->ipa_hw_type);
WARN_ON(1);
return -EINVAL;
}
/**

View file

@ -39,6 +39,8 @@
#define MTU_BYTE 1500
#define IPA_MAX_NUM_PIPES 0x14
#define IPA_WAN_CONS_DESC_FIFO_SZ 0x5E80
#define IPA_WAN_NAPI_CONS_RX_POOL_SZ 3000
#define IPA_SYS_DESC_FIFO_SZ 0x2000
#define IPA_SYS_TX_DATA_DESC_FIFO_SZ 0x1000
#define IPA_LAN_RX_HEADER_LENGTH (2)
@ -515,6 +517,7 @@ enum ipa_wakelock_ref_client {
* @disconnect_in_progress: Indicates client disconnect in progress.
* @qmi_request_sent: Indicates whether QMI request to enable clear data path
* request is sent or not.
* @napi_enabled: when true, IPA call client callback to start polling
*/
struct ipa_ep_context {
int valid;
@ -546,6 +549,10 @@ struct ipa_ep_context {
bool disconnect_in_progress;
u32 qmi_request_sent;
enum ipa_wakelock_ref_client wakelock_client;
bool napi_enabled;
bool switch_to_intr;
int inactive_cycles;
u32 eot_in_poll_err;
/* sys MUST be the last element of this struct */
struct ipa_sys_context *sys;
@ -603,6 +610,7 @@ struct ipa_sys_context {
/* ordering is important - mutable fields go above */
struct ipa_ep_context *ep;
struct list_head head_desc_list;
struct list_head rcycl_list;
spinlock_t spinlock;
struct workqueue_struct *wq;
struct workqueue_struct *repl_wq;
@ -1929,4 +1937,6 @@ void ipa_inc_acquire_wakelock(enum ipa_wakelock_ref_client ref_client);
void ipa_dec_release_wakelock(enum ipa_wakelock_ref_client ref_client);
int ipa_iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot);
int ipa2_rx_poll(u32 clnt_hdl, int budget);
void ipa2_recycle_wan_skb(struct sk_buff *skb);
#endif /* _IPA_I_H_ */

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2016, The Linux Foundation. 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
@ -127,6 +127,23 @@ TRACE_EVENT(
TP_printk("rx_pkt_cnt=%lu", __entry->rx_pkt_cnt)
);
TRACE_EVENT(
rmnet_ipa_netif_rcv_skb,
TP_PROTO(unsigned long rx_pkt_cnt),
TP_ARGS(rx_pkt_cnt),
TP_STRUCT__entry(
__field(unsigned long, rx_pkt_cnt)
),
TP_fast_assign(
__entry->rx_pkt_cnt = rx_pkt_cnt;
),
TP_printk("rx_pkt_cnt=%lu", __entry->rx_pkt_cnt)
);
#endif /* _IPA_TRACE_H */
/* This part must be outside protection */

View file

@ -5128,7 +5128,8 @@ int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type,
ipa2_set_required_perf_profile;
api_ctrl->ipa_get_ipc_logbuf = ipa2_get_ipc_logbuf;
api_ctrl->ipa_get_ipc_logbuf_low = ipa2_get_ipc_logbuf_low;
api_ctrl->ipa_rx_poll = ipa2_rx_poll;
api_ctrl->ipa_recycle_wan_skb = ipa2_recycle_wan_skb;
return 0;
}

View file

@ -59,6 +59,8 @@
#define IPA_QUOTA_REACH_IF_NAME_MAX_SIZE 64
#define IPA_UEVENT_NUM_EVNP 4 /* number of event pointers */
#define NAPI_WEIGHT 60
static struct net_device *ipa_netdevs[IPA_WWAN_DEVICE_COUNT];
static struct ipa_sys_connect_params apps_to_ipa_ep_cfg, ipa_to_apps_ep_cfg;
static u32 qmap_hdr_hdl, dflt_v4_wan_rt_hdl, dflt_v6_wan_rt_hdl;
@ -76,6 +78,8 @@ static struct mutex ipa_to_apps_pipe_handle_guard;
static int wwan_add_ul_flt_rule_to_ipa(void);
static int wwan_del_ul_flt_rule_to_ipa(void);
static void ipa_wwan_msg_free_cb(void*, u32, u32);
static void ipa_rmnet_rx_cb(void *priv);
static int ipa_rmnet_poll(struct napi_struct *napi, int budget);
static void wake_tx_queue(struct work_struct *work);
static DECLARE_WORK(ipa_tx_wakequeue_work, wake_tx_queue);
@ -93,8 +97,10 @@ struct ipa_rmnet_plat_drv_res {
bool ipa_rmnet_ssr;
bool ipa_loaduC;
bool ipa_advertise_sg_support;
bool ipa_napi_enable;
};
static struct ipa_rmnet_plat_drv_res ipa_rmnet_res;
/**
* struct wwan_private - WWAN private data
* @net: network interface struct implemented by this driver
@ -119,6 +125,7 @@ struct wwan_private {
spinlock_t lock;
struct completion resource_granted_completion;
enum wwan_device_status device_status;
struct napi_struct napi;
};
/**
@ -936,6 +943,9 @@ static int __ipa_wwan_open(struct net_device *dev)
if (wwan_ptr->device_status != WWAN_DEVICE_ACTIVE)
reinit_completion(&wwan_ptr->resource_granted_completion);
wwan_ptr->device_status = WWAN_DEVICE_ACTIVE;
if (ipa_rmnet_res.ipa_napi_enable)
napi_enable(&(wwan_ptr->napi));
return 0;
}
@ -970,6 +980,8 @@ static int __ipa_wwan_close(struct net_device *dev)
/* do not close wwan port once up, this causes
remote side to hang if tried to open again */
reinit_completion(&wwan_ptr->resource_granted_completion);
if (ipa_rmnet_res.ipa_napi_enable)
napi_disable(&(wwan_ptr->napi));
rc = ipa2_deregister_intf(dev->name);
if (rc) {
IPAWANERR("[%s]: ipa2_deregister_intf failed %d\n",
@ -1168,39 +1180,50 @@ static void apps_ipa_packet_receive_notify(void *priv,
enum ipa_dp_evt_type evt,
unsigned long data)
{
struct sk_buff *skb = (struct sk_buff *)data;
struct net_device *dev = (struct net_device *)priv;
int result;
unsigned int packet_len = skb->len;
IPAWANDBG("Rx packet was received");
if (evt != IPA_RECEIVE) {
IPAWANERR("A none IPA_RECEIVE event in wan_ipa_receive\n");
return;
}
if (evt == IPA_RECEIVE) {
struct sk_buff *skb = (struct sk_buff *)data;
int result;
unsigned int packet_len = skb->len;
skb->dev = ipa_netdevs[0];
skb->protocol = htons(ETH_P_MAP);
IPAWANDBG("Rx packet was received");
skb->dev = ipa_netdevs[0];
skb->protocol = htons(ETH_P_MAP);
if (dev->stats.rx_packets % IPA_WWAN_RX_SOFTIRQ_THRESH == 0) {
trace_rmnet_ipa_netifni(dev->stats.rx_packets);
result = netif_rx_ni(skb);
} else {
trace_rmnet_ipa_netifrx(dev->stats.rx_packets);
result = netif_rx(skb);
}
if (ipa_rmnet_res.ipa_napi_enable) {
trace_rmnet_ipa_netif_rcv_skb(dev->stats.rx_packets);
result = netif_receive_skb(skb);
} else {
if (dev->stats.rx_packets % IPA_WWAN_RX_SOFTIRQ_THRESH
== 0) {
trace_rmnet_ipa_netifni(dev->stats.rx_packets);
result = netif_rx_ni(skb);
} else {
trace_rmnet_ipa_netifrx(dev->stats.rx_packets);
result = netif_rx(skb);
}
}
if (result) {
pr_err_ratelimited(DEV_NAME " %s:%d fail on netif_receive_skb\n",
__func__, __LINE__);
dev->stats.rx_dropped++;
}
dev->stats.rx_packets++;
dev->stats.rx_bytes += packet_len;
} else if (evt == IPA_CLIENT_START_POLL)
ipa_rmnet_rx_cb(priv);
else if (evt == IPA_CLIENT_COMP_NAPI) {
struct wwan_private *wwan_ptr = netdev_priv(dev);
if (ipa_rmnet_res.ipa_napi_enable)
napi_complete(&(wwan_ptr->napi));
} else
IPAWANERR("Invalid evt %d received in wan_ipa_receive\n", evt);
if (result) {
pr_err_ratelimited(DEV_NAME " %s:%d fail on netif_rx\n",
__func__, __LINE__);
dev->stats.rx_dropped++;
}
dev->stats.rx_packets++;
dev->stats.rx_bytes += packet_len;
}
static struct ipa_rmnet_plat_drv_res ipa_rmnet_res = {0, };
/**
* ipa_wwan_ioctl() - I/O control for wwan network driver.
*
@ -1555,9 +1578,17 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
ipa_to_apps_ep_cfg.client = IPA_CLIENT_APPS_WAN_CONS;
ipa_to_apps_ep_cfg.notify =
apps_ipa_packet_receive_notify;
ipa_to_apps_ep_cfg.desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ;
ipa_to_apps_ep_cfg.priv = dev;
ipa_to_apps_ep_cfg.napi_enabled =
ipa_rmnet_res.ipa_napi_enable;
if (ipa_to_apps_ep_cfg.napi_enabled)
ipa_to_apps_ep_cfg.desc_fifo_sz =
IPA_WAN_CONS_DESC_FIFO_SZ;
else
ipa_to_apps_ep_cfg.desc_fifo_sz =
IPA_SYS_DESC_FIFO_SZ;
mutex_lock(&ipa_to_apps_pipe_handle_guard);
if (atomic_read(&is_ssr)) {
IPAWANDBG("In SSR sequence/recovery\n");
@ -1899,6 +1930,12 @@ static int get_ipa_rmnet_dts_configuration(struct platform_device *pdev,
"qcom,ipa-advertise-sg-support");
pr_info("IPA SG support = %s\n",
ipa_rmnet_drv_res->ipa_advertise_sg_support ? "True" : "False");
ipa_rmnet_drv_res->ipa_napi_enable =
of_property_read_bool(pdev->dev.of_node,
"qcom,ipa-napi-enable");
pr_info("IPA Napi Enable = %s\n",
ipa_rmnet_drv_res->ipa_napi_enable ? "True" : "False");
return 0;
}
@ -2044,6 +2081,12 @@ static int ipa_wwan_probe(struct platform_device *pdev)
if (ipa_rmnet_res.ipa_advertise_sg_support)
dev->hw_features |= NETIF_F_SG;
/* Enable NAPI support in netdevice. */
if (ipa_rmnet_res.ipa_napi_enable) {
netif_napi_add(dev, &(wwan_ptr->napi),
ipa_rmnet_poll, NAPI_WEIGHT);
}
ret = register_netdev(dev);
if (ret) {
IPAWANERR("unable to register ipa_netdev %d rc=%d\n",
@ -2068,6 +2111,8 @@ static int ipa_wwan_probe(struct platform_device *pdev)
pr_info("rmnet_ipa completed initialization\n");
return 0;
config_err:
if (ipa_rmnet_res.ipa_napi_enable)
netif_napi_del(&(wwan_ptr->napi));
unregister_netdev(ipa_netdevs[0]);
set_perf_err:
ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_WWAN_0_PROD,
@ -2107,6 +2152,9 @@ setup_a7_qmap_hdr_err:
static int ipa_wwan_remove(struct platform_device *pdev)
{
int ret;
struct wwan_private *wwan_ptr;
wwan_ptr = netdev_priv(ipa_netdevs[0]);
pr_info("rmnet_ipa started deinitialization\n");
mutex_lock(&ipa_to_apps_pipe_handle_guard);
@ -2115,6 +2163,8 @@ static int ipa_wwan_remove(struct platform_device *pdev)
IPAWANERR("Failed to teardown IPA->APPS pipe\n");
else
ipa_to_apps_hdl = -1;
if (ipa_rmnet_res.ipa_napi_enable)
netif_napi_del(&(wwan_ptr->napi));
mutex_unlock(&ipa_to_apps_pipe_handle_guard);
unregister_netdev(ipa_netdevs[0]);
ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_WWAN_0_PROD,
@ -2802,6 +2852,31 @@ static void ipa_wwan_msg_free_cb(void *buff, u32 len, u32 type)
kfree(buff);
}
static void ipa_rmnet_rx_cb(void *priv)
{
struct net_device *dev = priv;
struct wwan_private *wwan_ptr;
IPAWANDBG("\n");
if (dev != ipa_netdevs[0]) {
IPAWANERR("Not matching with netdev\n");
return;
}
wwan_ptr = netdev_priv(dev);
napi_schedule(&(wwan_ptr->napi));
}
static int ipa_rmnet_poll(struct napi_struct *napi, int budget)
{
int rcvd_pkts = 0;
rcvd_pkts = ipa_rx_poll(ipa_to_apps_hdl, NAPI_WEIGHT);
IPAWANDBG("rcvd packets: %d\n", rcvd_pkts);
return rcvd_pkts;
}
late_initcall(ipa_wwan_init);
module_exit(ipa_wwan_cleanup);
MODULE_DESCRIPTION("WWAN Network Interface");

View file

@ -1010,25 +1010,28 @@ static void ipa3_sps_irq_rx_notify(struct sps_event_notify *notify)
if (IPA_CLIENT_IS_APPS_CONS(sys->ep->client))
atomic_set(&ipa3_ctx->transport_pm.eot_activity, 1);
if (!atomic_read(&sys->curr_polling_state)) {
ret = sps_get_config(sys->ep->ep_hdl,
&sys->ep->connect);
if (ret) {
IPAERR("sps_get_config() failed %d\n", ret);
break;
}
sys->ep->connect.options = SPS_O_AUTO_ENABLE |
SPS_O_ACK_TRANSFERS | SPS_O_POLL;
ret = sps_set_config(sys->ep->ep_hdl,
&sys->ep->connect);
if (ret) {
IPAERR("sps_set_config() failed %d\n", ret);
break;
}
ipa3_inc_acquire_wakelock();
atomic_set(&sys->curr_polling_state, 1);
trace_intr_to_poll3(sys->ep->client);
queue_work(sys->wq, &sys->work);
sys->ep->eot_in_poll_err++;
break;
}
ret = sps_get_config(sys->ep->ep_hdl,
&sys->ep->connect);
if (ret) {
IPAERR("sps_get_config() failed %d\n", ret);
break;
}
sys->ep->connect.options = SPS_O_AUTO_ENABLE |
SPS_O_ACK_TRANSFERS | SPS_O_POLL;
ret = sps_set_config(sys->ep->ep_hdl,
&sys->ep->connect);
if (ret) {
IPAERR("sps_set_config() failed %d\n", ret);
break;
}
ipa3_inc_acquire_wakelock();
atomic_set(&sys->curr_polling_state, 1);
trace_intr_to_poll3(sys->ep->client);
queue_work(sys->wq, &sys->work);
break;
default:
IPAERR("received unexpected event id %d\n", notify->event_id);
@ -1089,7 +1092,18 @@ static void ipa3_switch_to_intr_rx_work_func(struct work_struct *work)
dwork = container_of(work, struct delayed_work, work);
sys = container_of(dwork, struct ipa3_sys_context, switch_to_intr_work);
ipa3_handle_rx(sys);
if (sys->ep->napi_enabled) {
if (sys->ep->switch_to_intr) {
ipa3_rx_switch_to_intr_mode(sys);
IPA_ACTIVE_CLIENTS_DEC_SPECIAL("NAPI");
sys->ep->switch_to_intr = false;
sys->ep->inactive_cycles = 0;
} else
sys->ep->client_notify(sys->ep->priv,
IPA_CLIENT_START_POLL, 0);
} else
ipa3_handle_rx(sys);
}
/**
@ -1217,6 +1231,7 @@ int ipa3_setup_sys_pipe(struct ipa_sys_connect_params *sys_in, u32 *clnt_hdl)
ep->valid = 1;
ep->client = sys_in->client;
ep->client_notify = sys_in->notify;
ep->napi_enabled = sys_in->napi_enabled;
ep->priv = sys_in->priv;
ep->keep_ipa_awake = sys_in->keep_ipa_awake;
atomic_set(&ep->avail_fifo_desc,
@ -1423,6 +1438,12 @@ int ipa3_teardown_sys_pipe(u32 clnt_hdl)
IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
ipa3_disable_data_path(clnt_hdl);
if (ep->napi_enabled) {
ep->switch_to_intr = true;
do {
usleep_range(95, 105);
} while (atomic_read(&ep->sys->curr_polling_state));
}
if (IPA_CLIENT_IS_PROD(ep->client)) {
do {
@ -1772,7 +1793,13 @@ static void ipa3_wq_handle_rx(struct work_struct *work)
struct ipa3_sys_context *sys;
sys = container_of(work, struct ipa3_sys_context, work);
ipa3_handle_rx(sys);
if (sys->ep->napi_enabled) {
IPA_ACTIVE_CLIENTS_INC_SPECIAL("NAPI");
sys->ep->client_notify(sys->ep->priv,
IPA_CLIENT_START_POLL, 0);
} else
ipa3_handle_rx(sys);
}
static void ipa3_wq_repl_rx(struct work_struct *work)
@ -2717,6 +2744,11 @@ static int ipa3_wan_rx_pyld_hdlr(struct sk_buff *skb,
IPA_RECEIVE, (unsigned long)(skb));
return rc;
}
if (sys->repl_hdlr == ipa3_replenish_rx_cache_recycle) {
IPAERR("Recycle should enable only with GRO Aggr\n");
ipa_assert();
}
/*
* payload splits across 2 buff or more,
* take the start of the payload from prev_skb
@ -2909,6 +2941,30 @@ static void ipa3_recycle_rx_wrapper(struct ipa3_rx_pkt_wrapper *rx_pkt)
spin_unlock_bh(&rx_pkt->sys->spinlock);
}
void ipa3_recycle_wan_skb(struct sk_buff *skb)
{
struct ipa3_rx_pkt_wrapper *rx_pkt;
int ep_idx = ipa3_get_ep_mapping(
IPA_CLIENT_APPS_WAN_CONS);
gfp_t flag = GFP_NOWAIT | __GFP_NOWARN;
if (unlikely(ep_idx == -1)) {
IPAERR("dest EP does not exist\n");
ipa_assert();
}
rx_pkt = kmem_cache_zalloc(ipa3_ctx->rx_pkt_wrapper_cache,
flag);
if (!rx_pkt)
ipa_assert();
INIT_WORK(&rx_pkt->work, ipa3_wq_rx_avail);
rx_pkt->sys = ipa3_ctx->ep[ep_idx].sys;
rx_pkt->data.skb = skb;
ipa3_recycle_rx_wrapper(rx_pkt);
}
static void ipa3_wq_rx_common(struct ipa3_sys_context *sys, u32 size)
{
struct ipa3_rx_pkt_wrapper *rx_pkt_expected;
@ -3123,14 +3179,22 @@ static int ipa3_assign_policy(struct ipa_sys_connect_params *in,
IPA_CLIENT_APPS_WAN_CONS) {
sys->pyld_hdlr = ipa3_wan_rx_pyld_hdlr;
sys->free_rx_wrapper = ipa3_free_rx_wrapper;
if (nr_cpu_ids > 1)
if (in->napi_enabled) {
sys->repl_hdlr =
ipa3_fast_replenish_rx_cache;
else
sys->repl_hdlr =
ipa3_replenish_rx_cache;
sys->rx_pool_sz =
ipa3_ctx->wan_rx_ring_size;
ipa3_replenish_rx_cache_recycle;
sys->rx_pool_sz =
IPA_WAN_NAPI_CONS_RX_POOL_SZ;
} else {
if (nr_cpu_ids > 1) {
sys->repl_hdlr =
ipa3_fast_replenish_rx_cache;
} else {
sys->repl_hdlr =
ipa3_replenish_rx_cache;
}
sys->rx_pool_sz =
ipa3_ctx->wan_rx_ring_size;
}
in->ipa_ep_cfg.aggr.aggr_sw_eof_active
= true;
if (ipa3_ctx->
@ -3941,68 +4005,40 @@ static int ipa_populate_tag_field(struct ipa3_desc *desc,
return 0;
}
static int ipa_poll_gsi_pkt(struct ipa3_sys_context *sys,
struct ipa_mem_buffer *mem_info)
{
int ret;
struct gsi_chan_xfer_notify xfer_notify;
struct ipa3_rx_pkt_wrapper *rx_pkt;
if (sys->ep->bytes_xfered_valid) {
mem_info->phys_base = sys->ep->phys_base;
mem_info->size = (u32)sys->ep->bytes_xfered;
sys->ep->bytes_xfered_valid = false;
return GSI_STATUS_SUCCESS;
}
ret = gsi_poll_channel(sys->ep->gsi_chan_hdl,
&xfer_notify);
if (ret == GSI_STATUS_POLL_EMPTY)
return ret;
else if (ret != GSI_STATUS_SUCCESS) {
IPAERR("Poll channel err: %d\n", ret);
return ret;
}
rx_pkt = (struct ipa3_rx_pkt_wrapper *)
xfer_notify.xfer_user_data;
mem_info->phys_base = rx_pkt->data.dma_addr;
mem_info->size = xfer_notify.bytes_xfered;
return ret;
}
static int ipa_handle_rx_core_gsi(struct ipa3_sys_context *sys,
bool process_all, bool in_poll_state)
{
int ret;
int cnt = 0;
struct ipa3_sys_context *sys_ptr;
struct ipa3_rx_pkt_wrapper *rx_pkt;
struct gsi_chan_xfer_notify xfer_notify;
struct ipa_mem_buffer mem_info = {0};
enum ipa_client_type client;
if (sys->ep->bytes_xfered_valid) {
mem_info.phys_base = sys->ep->phys_base;
mem_info.size = (u32)sys->ep->bytes_xfered;
sys_ptr = sys;
if (IPA_CLIENT_IS_MEMCPY_DMA_CONS(sys->ep->client))
ipa3_dma_memcpy_notify(sys_ptr, &mem_info);
else if (IPA_CLIENT_IS_WLAN_CONS(sys->ep->client))
ipa3_wlan_wq_rx_common(sys_ptr, mem_info.size);
else
ipa3_wq_rx_common(sys_ptr, mem_info.size);
cnt++;
sys->ep->bytes_xfered_valid = false;
}
while ((in_poll_state ? atomic_read(&sys->curr_polling_state) :
!atomic_read(&sys->curr_polling_state))) {
if (cnt && !process_all)
break;
ret = gsi_poll_channel(sys->ep->gsi_chan_hdl,
&xfer_notify);
if (ret == GSI_STATUS_POLL_EMPTY)
break;
else if (ret == GSI_STATUS_SUCCESS) {
sys_ptr = (struct ipa3_sys_context *)
xfer_notify.chan_user_data;
rx_pkt = (struct ipa3_rx_pkt_wrapper *)
xfer_notify.xfer_user_data;
mem_info.phys_base = rx_pkt->data.dma_addr;
mem_info.size = xfer_notify.bytes_xfered;
client = sys->ep->client;
if (IPA_CLIENT_IS_MEMCPY_DMA_CONS(client))
ipa3_dma_memcpy_notify(sys_ptr, &mem_info);
else if (IPA_CLIENT_IS_WLAN_CONS(client))
ipa3_wlan_wq_rx_common(sys_ptr, mem_info.size);
else
ipa3_wq_rx_common(sys_ptr, mem_info.size);
cnt++;
} else
IPAERR("Poll channel err: %d\n", ret);
}
return cnt;
}
static int ipa_handle_rx_core_sps(struct ipa3_sys_context *sys,
bool process_all, bool in_poll_state)
{
struct sps_iovec iov;
int ret;
int cnt = 0;
struct ipa_mem_buffer mem_info = {0};
@ -4012,17 +4048,10 @@ static int ipa_handle_rx_core_sps(struct ipa3_sys_context *sys,
if (cnt && !process_all)
break;
ret = sps_get_iovec(sys->ep->ep_hdl, &iov);
if (ret) {
IPAERR("sps_get_iovec failed %d\n", ret);
break;
}
if (iov.addr == 0)
ret = ipa_poll_gsi_pkt(sys, &mem_info);
if (ret)
break;
mem_info.phys_base = iov.addr;
mem_info.size = iov.size;
if (IPA_CLIENT_IS_MEMCPY_DMA_CONS(sys->ep->client))
ipa3_dma_memcpy_notify(sys, &mem_info);
else if (IPA_CLIENT_IS_WLAN_CONS(sys->ep->client))
@ -4035,6 +4064,112 @@ static int ipa_handle_rx_core_sps(struct ipa3_sys_context *sys,
return cnt;
}
static int ipa_poll_sps_pkt(struct ipa3_sys_context *sys,
struct ipa_mem_buffer *mem_info)
{
int ret;
struct sps_iovec iov;
ret = sps_get_iovec(sys->ep->ep_hdl, &iov);
if (ret) {
IPAERR("sps_get_iovec failed %d\n", ret);
return ret;
}
if (iov.addr == 0)
return -EIO;
mem_info->phys_base = iov.addr;
mem_info->size = iov.size;
return 0;
}
static int ipa_handle_rx_core_sps(struct ipa3_sys_context *sys,
bool process_all, bool in_poll_state)
{
int ret;
int cnt = 0;
struct ipa_mem_buffer mem_info = {0};
while ((in_poll_state ? atomic_read(&sys->curr_polling_state) :
!atomic_read(&sys->curr_polling_state))) {
if (cnt && !process_all)
break;
ret = ipa_poll_sps_pkt(sys, &mem_info);
if (ret)
break;
if (IPA_CLIENT_IS_MEMCPY_DMA_CONS(sys->ep->client))
ipa3_dma_memcpy_notify(sys, &mem_info);
else if (IPA_CLIENT_IS_WLAN_CONS(sys->ep->client))
ipa3_wlan_wq_rx_common(sys, mem_info.size);
else
ipa3_wq_rx_common(sys, mem_info.size);
cnt++;
}
return cnt;
}
/**
* ipa3_rx_poll() - Poll the rx packets from IPA HW. This
* function is exectued in the softirq context
*
* if input budget is zero, the driver switches back to
* interrupt mode
*
* return number of polled packets, on error 0(zero)
*/
int ipa3_rx_poll(u32 clnt_hdl, int weight)
{
struct ipa3_ep_context *ep;
int ret;
int cnt = 0;
unsigned int delay = 1;
struct ipa_mem_buffer mem_info = {0};
IPADBG("\n");
if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
ipa3_ctx->ep[clnt_hdl].valid == 0) {
IPAERR("bad parm 0x%x\n", clnt_hdl);
return cnt;
}
ep = &ipa3_ctx->ep[clnt_hdl];
while (cnt < weight &&
atomic_read(&ep->sys->curr_polling_state)) {
if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI)
ret = ipa_poll_gsi_pkt(ep->sys, &mem_info);
else
ret = ipa_poll_sps_pkt(ep->sys, &mem_info);
if (ret)
break;
ipa3_wq_rx_common(ep->sys, mem_info.size);
cnt += 5;
};
if (cnt == 0) {
ep->inactive_cycles++;
ep->client_notify(ep->priv, IPA_CLIENT_COMP_NAPI, 0);
if (ep->inactive_cycles > 3 || ep->sys->len == 0) {
ep->switch_to_intr = true;
delay = 0;
}
queue_delayed_work(ep->sys->wq,
&ep->sys->switch_to_intr_work, msecs_to_jiffies(delay));
} else
ep->inactive_cycles = 0;
return cnt;
}
static unsigned long tag_to_pointer_wa(uint64_t tag)
{
return 0xFFFF000000000000 | (unsigned long) tag;

View file

@ -41,6 +41,8 @@
#define MTU_BYTE 1500
#define IPA3_MAX_NUM_PIPES 31
#define IPA_WAN_CONS_DESC_FIFO_SZ 0x5E80
#define IPA_WAN_NAPI_CONS_RX_POOL_SZ 3000
#define IPA_SYS_DESC_FIFO_SZ 0x800
#define IPA_SYS_TX_DATA_DESC_FIFO_SZ 0x1000
#define IPA_LAN_RX_HEADER_LENGTH (2)
@ -550,6 +552,7 @@ struct ipa3_status_stats {
* @disconnect_in_progress: Indicates client disconnect in progress.
* @qmi_request_sent: Indicates whether QMI request to enable clear data path
* request is sent or not.
* @napi_enabled: when true, IPA call client callback to start polling
*/
struct ipa3_ep_context {
int valid;
@ -586,6 +589,10 @@ struct ipa3_ep_context {
u32 wdi_state;
bool disconnect_in_progress;
u32 qmi_request_sent;
bool napi_enabled;
bool switch_to_intr;
int inactive_cycles;
u32 eot_in_poll_err;
/* sys MUST be the last element of this struct */
struct ipa3_sys_context *sys;
@ -2262,4 +2269,6 @@ int ipa3_load_fws(const struct firmware *firmware);
int ipa3_register_ipa_ready_cb(void (*ipa_ready_cb)(void *), void *user_data);
const char *ipa_hw_error_str(enum ipa3_hw_errors err_type);
int ipa_gsi_ch20_wa(void);
int ipa3_rx_poll(u32 clnt_hdl, int budget);
void ipa3_recycle_wan_skb(struct sk_buff *skb);
#endif /* _IPA3_I_H_ */

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2016, The Linux Foundation. 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
@ -127,6 +127,24 @@ TRACE_EVENT(
TP_printk("rx_pkt_cnt=%lu", __entry->rx_pkt_cnt)
);
TRACE_EVENT(
rmnet_ipa_netif_rcv_skb3,
TP_PROTO(unsigned long rx_pkt_cnt),
TP_ARGS(rx_pkt_cnt),
TP_STRUCT__entry(
__field(unsigned long, rx_pkt_cnt)
),
TP_fast_assign(
__entry->rx_pkt_cnt = rx_pkt_cnt;
),
TP_printk("rx_pkt_cnt=%lu", __entry->rx_pkt_cnt)
);
#endif /* _IPA_TRACE_H */
/* This part must be outside protection */

View file

@ -4657,6 +4657,8 @@ int ipa3_bind_api_controller(enum ipa_hw_type ipa_hw_type,
ipa3_set_required_perf_profile;
api_ctrl->ipa_get_ipc_logbuf = ipa3_get_ipc_logbuf;
api_ctrl->ipa_get_ipc_logbuf_low = ipa3_get_ipc_logbuf_low;
api_ctrl->ipa_rx_poll = ipa3_rx_poll;
api_ctrl->ipa_recycle_wan_skb = ipa3_recycle_wan_skb;
return 0;
}

View file

@ -57,6 +57,7 @@
#define IPA_QUOTA_REACH_ALERT_MAX_SIZE 64
#define IPA_QUOTA_REACH_IF_NAME_MAX_SIZE 64
#define IPA_UEVENT_NUM_EVNP 4 /* number of event pointers */
#define NAPI_WEIGHT 60
#define IPA_NETDEV() \
((rmnet_ipa3_ctx && rmnet_ipa3_ctx->wwan_priv) ? \
@ -66,6 +67,8 @@
static int ipa3_wwan_add_ul_flt_rule_to_ipa(void);
static int ipa3_wwan_del_ul_flt_rule_to_ipa(void);
static void ipa3_wwan_msg_free_cb(void*, u32, u32);
static void ipa3_rmnet_rx_cb(void *priv);
static int ipa3_rmnet_poll(struct napi_struct *napi, int budget);
static void ipa3_wake_tx_queue(struct work_struct *work);
static DECLARE_WORK(ipa3_tx_wakequeue_work, ipa3_wake_tx_queue);
@ -83,6 +86,7 @@ struct ipa3_rmnet_plat_drv_res {
bool ipa_rmnet_ssr;
bool ipa_loaduC;
bool ipa_advertise_sg_support;
bool ipa_napi_enable;
};
/**
@ -109,6 +113,7 @@ struct ipa3_wwan_private {
spinlock_t lock;
struct completion resource_granted_completion;
enum ipa3_wwan_device_status device_status;
struct napi_struct napi;
};
struct rmnet_ipa3_context {
@ -134,6 +139,7 @@ struct rmnet_ipa3_context {
};
static struct rmnet_ipa3_context *rmnet_ipa3_ctx;
static struct ipa3_rmnet_plat_drv_res ipa3_rmnet_res;
/**
* ipa3_setup_a7_qmap_hdr() - Setup default a7 qmap hdr
@ -957,6 +963,9 @@ static int __ipa_wwan_open(struct net_device *dev)
if (wwan_ptr->device_status != WWAN_DEVICE_ACTIVE)
reinit_completion(&wwan_ptr->resource_granted_completion);
wwan_ptr->device_status = WWAN_DEVICE_ACTIVE;
if (ipa3_rmnet_res.ipa_napi_enable)
napi_enable(&(wwan_ptr->napi));
return 0;
}
@ -1189,39 +1198,47 @@ static void apps_ipa_packet_receive_notify(void *priv,
enum ipa_dp_evt_type evt,
unsigned long data)
{
struct sk_buff *skb = (struct sk_buff *)data;
struct net_device *dev = (struct net_device *)priv;
int result;
unsigned int packet_len = skb->len;
IPAWANDBG_LOW("Rx packet was received");
if (evt != IPA_RECEIVE) {
IPAWANERR("A none IPA_RECEIVE event in wan_ipa_receive\n");
return;
}
if (evt == IPA_RECEIVE) {
struct sk_buff *skb = (struct sk_buff *)data;
int result;
unsigned int packet_len = skb->len;
skb->dev = IPA_NETDEV();
skb->protocol = htons(ETH_P_MAP);
IPAWANDBG_LOW("Rx packet was received");
skb->dev = IPA_NETDEV();
skb->protocol = htons(ETH_P_MAP);
if (dev->stats.rx_packets % IPA_WWAN_RX_SOFTIRQ_THRESH == 0) {
trace_rmnet_ipa_netifni3(dev->stats.rx_packets);
result = netif_rx_ni(skb);
} else {
trace_rmnet_ipa_netifrx3(dev->stats.rx_packets);
result = netif_rx(skb);
}
if (ipa3_rmnet_res.ipa_napi_enable) {
trace_rmnet_ipa_netif_rcv_skb3(dev->stats.rx_packets);
result = netif_receive_skb(skb);
} else {
if (dev->stats.rx_packets % IPA_WWAN_RX_SOFTIRQ_THRESH
== 0) {
trace_rmnet_ipa_netifni3(dev->stats.rx_packets);
result = netif_rx_ni(skb);
} else {
trace_rmnet_ipa_netifrx3(dev->stats.rx_packets);
result = netif_rx(skb);
}
}
if (result) {
pr_err_ratelimited(DEV_NAME " %s:%d fail on netif_rx\n",
__func__, __LINE__);
dev->stats.rx_dropped++;
}
dev->stats.rx_packets++;
dev->stats.rx_bytes += packet_len;
if (result) {
pr_err_ratelimited(DEV_NAME " %s:%d fail on netif_receive_skb\n",
__func__, __LINE__);
dev->stats.rx_dropped++;
}
dev->stats.rx_packets++;
dev->stats.rx_bytes += packet_len;
} else if (evt == IPA_CLIENT_START_POLL)
ipa3_rmnet_rx_cb(priv);
else if (evt == IPA_CLIENT_COMP_NAPI) {
if (ipa3_rmnet_res.ipa_napi_enable)
napi_complete(&(rmnet_ipa3_ctx->wwan_priv->napi));
} else
IPAWANERR("Invalid evt %d received in wan_ipa_receive\n", evt);
}
static struct ipa3_rmnet_plat_drv_res ipa3_rmnet_res = {0, };
/**
* ipa3_wwan_ioctl() - I/O control for wwan network driver.
*
@ -1595,10 +1612,17 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
IPA_CLIENT_APPS_WAN_CONS;
rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.notify =
apps_ipa_packet_receive_notify;
rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.desc_fifo_sz =
IPA_SYS_DESC_FIFO_SZ;
rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.priv = dev;
rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.napi_enabled =
ipa3_rmnet_res.ipa_napi_enable;
if (rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.napi_enabled)
rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.
desc_fifo_sz = IPA_WAN_CONS_DESC_FIFO_SZ;
else
rmnet_ipa3_ctx->ipa_to_apps_ep_cfg.
desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ;
mutex_lock(
&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard);
if (atomic_read(&rmnet_ipa3_ctx->is_ssr)) {
@ -2126,6 +2150,9 @@ static int ipa3_wwan_probe(struct platform_device *pdev)
if (ipa3_rmnet_res.ipa_advertise_sg_support)
dev->hw_features |= NETIF_F_SG;
if (ipa3_rmnet_res.ipa_napi_enable)
netif_napi_add(dev, &(rmnet_ipa3_ctx->wwan_priv->napi),
ipa3_rmnet_poll, NAPI_WEIGHT);
ret = register_netdev(dev);
if (ret) {
IPAWANERR("unable to register ipa_netdev %d rc=%d\n",
@ -2149,6 +2176,8 @@ static int ipa3_wwan_probe(struct platform_device *pdev)
pr_info("rmnet_ipa completed initialization\n");
return 0;
config_err:
if (ipa3_rmnet_res.ipa_napi_enable)
netif_napi_del(&(rmnet_ipa3_ctx->wwan_priv->napi));
unregister_netdev(dev);
set_perf_err:
ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_WWAN_0_PROD,
@ -2196,6 +2225,8 @@ static int ipa3_wwan_remove(struct platform_device *pdev)
IPAWANERR("Failed to teardown IPA->APPS pipe\n");
else
rmnet_ipa3_ctx->ipa3_to_apps_hdl = -1;
if (ipa3_rmnet_res.ipa_napi_enable)
netif_napi_del(&(rmnet_ipa3_ctx->wwan_priv->napi));
mutex_unlock(&rmnet_ipa3_ctx->ipa_to_apps_pipe_handle_guard);
unregister_netdev(IPA_NETDEV());
ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_WWAN_0_PROD,
@ -2903,6 +2934,22 @@ static void ipa3_wwan_msg_free_cb(void *buff, u32 len, u32 type)
kfree(buff);
}
static void ipa3_rmnet_rx_cb(void *priv)
{
IPAWANDBG_LOW("\n");
napi_schedule(&(rmnet_ipa3_ctx->wwan_priv->napi));
}
static int ipa3_rmnet_poll(struct napi_struct *napi, int budget)
{
int rcvd_pkts = 0;
rcvd_pkts = ipa_rx_poll(rmnet_ipa3_ctx->ipa3_to_apps_hdl,
NAPI_WEIGHT);
IPAWANDBG_LOW("rcvd packets: %d\n", rcvd_pkts);
return rcvd_pkts;
}
late_initcall(ipa3_wwan_init);
module_exit(ipa3_wwan_cleanup);
MODULE_DESCRIPTION("WWAN Network Interface");

View file

@ -93,6 +93,8 @@ enum ipa_aggr_mode {
enum ipa_dp_evt_type {
IPA_RECEIVE,
IPA_WRITE_DONE,
IPA_CLIENT_START_POLL,
IPA_CLIENT_COMP_NAPI,
};
/**
@ -538,6 +540,7 @@ struct ipa_ext_intf {
* @skip_ep_cfg: boolean field that determines if EP should be configured
* by IPA driver
* @keep_ipa_awake: when true, IPA will not be clock gated
* @napi_enabled: when true, IPA call client callback to start polling
*/
struct ipa_sys_connect_params {
struct ipa_ep_cfg ipa_ep_cfg;
@ -547,6 +550,7 @@ struct ipa_sys_connect_params {
ipa_notify_cb notify;
bool skip_ep_cfg;
bool keep_ipa_awake;
bool napi_enabled;
};
/**
@ -1233,6 +1237,8 @@ int ipa_tx_dp_mul(enum ipa_client_type dst,
struct ipa_tx_data_desc *data_desc);
void ipa_free_skb(struct ipa_rx_data *);
int ipa_rx_poll(u32 clnt_hdl, int budget);
void ipa_recycle_wan_skb(struct sk_buff *skb);
/*
* System pipes
@ -1763,6 +1769,15 @@ static inline void ipa_free_skb(struct ipa_rx_data *rx_in)
return;
}
static inline int ipa_rx_poll(u32 clnt_hdl, int budget)
{
return -EPERM;
}
static inline void ipa_recycle_wan_skb(struct sk_buff *skb)
{
}
/*
* System pipes
*/