diff options
| author | Dhoat Harpal <hdhoat@codeaurora.org> | 2016-02-08 11:46:12 +0530 |
|---|---|---|
| committer | Bryan Huntsman <bryanh@codeaurora.org> | 2016-04-12 15:49:53 -0700 |
| commit | 8876c65ca7dee5aedeb3d5377d2b943fad9b899d (patch) | |
| tree | b800f6a8493103e50c716320d086db0373e9718d | |
| parent | 4333a953a464c75d999eb055604e9499caed72e2 (diff) | |
soc: qcom: glink: Use tasklet/kworker for TX and RX path
Currently, Rx an Tx is based on workqueue and it is taking significant
time to schedule a workqueue which is hampering performance.
Use tasklet if underlying transport supports atomic context, otherwise
kworker is used.
CRs-Fixed: 978296
Change-Id: I736d2b90730ec10f9dff21944c4ad50e4d87da5c
Signed-off-by: Dhoat Harpal <hdhoat@codeaurora.org>
| -rw-r--r-- | drivers/soc/qcom/glink.c | 68 | ||||
| -rw-r--r-- | drivers/soc/qcom/glink_smd_xprt.c | 61 | ||||
| -rw-r--r-- | drivers/soc/qcom/glink_smem_native_xprt.c | 26 |
3 files changed, 116 insertions, 39 deletions
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c index 5086dd6d1495..aad67abc709a 100644 --- a/drivers/soc/qcom/glink.c +++ b/drivers/soc/qcom/glink.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-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 @@ -12,6 +12,7 @@ #include <asm/arch_timer.h> #include <linux/err.h> #include <linux/ipc_logging.h> +#include <linux/kthread.h> #include <linux/list.h> #include <linux/spinlock.h> #include <linux/module.h> @@ -36,6 +37,7 @@ #define GLINK_QOS_DEF_NUM_PRIORITY 1 #define GLINK_QOS_DEF_MTU 2048 +#define GLINK_KTHREAD_PRIO 1 /** * struct glink_qos_priority_bin - Packet Scheduler's priority bucket * @max_rate_kBps: Maximum rate supported by the priority bucket. @@ -78,8 +80,9 @@ struct glink_qos_priority_bin { * created channel * @max_cid: maximum number of channel identifiers supported * @max_iid: maximum number of intent identifiers supported - * @tx_work: work item to process @tx_ready - * @tx_wq: workqueue to run @tx_work + * @tx_kwork: work item to process @tx_ready + * @tx_wq: workqueue to run @tx_kwork + * @tx_task: handle to the running kthread * @channels: list of all existing channels on this transport * @mtu: MTU supported by this transport. * @token_count: Number of tokens to be assigned per assignment. @@ -119,8 +122,9 @@ struct glink_core_xprt_ctx { uint32_t max_cid; uint32_t max_iid; - struct work_struct tx_work; - struct workqueue_struct *tx_wq; + struct kthread_work tx_kwork; + struct kthread_worker tx_wq; + struct task_struct *tx_task; size_t mtu; uint32_t token_count; @@ -340,7 +344,7 @@ static int xprt_single_threaded_tx(struct glink_core_xprt_ctx *xprt_ptr, struct channel_ctx *ch_ptr, struct glink_core_tx_pkt *tx_info); -static void tx_work_func(struct work_struct *work); +static void tx_func(struct kthread_work *work); static struct channel_ctx *ch_name_to_ch_ctx_create( struct glink_core_xprt_ctx *xprt_ctx, @@ -3389,7 +3393,8 @@ void glink_xprt_ctx_release(struct rwref_lock *xprt_st_lock) xprt_rm_dbgfs.par_name = "xprt"; glink_debugfs_remove_recur(&xprt_rm_dbgfs); GLINK_INFO("%s: xprt debugfs removec\n", __func__); - destroy_workqueue(xprt_ctx->tx_wq); + kthread_stop(xprt_ctx->tx_task); + xprt_ctx->tx_task = NULL; glink_core_deinit_xprt_qos_cfg(xprt_ctx); kfree(xprt_ctx); xprt_ctx = NULL; @@ -3557,6 +3562,7 @@ static int glink_core_init_xprt_qos_cfg(struct glink_core_xprt_ctx *xprt_ptr, struct glink_core_transport_cfg *cfg) { int i; + struct sched_param param = { .sched_priority = GLINK_KTHREAD_PRIO }; xprt_ptr->mtu = cfg->mtu ? cfg->mtu : GLINK_QOS_DEF_MTU; xprt_ptr->num_priority = cfg->num_flows ? cfg->num_flows : @@ -3567,6 +3573,8 @@ static int glink_core_init_xprt_qos_cfg(struct glink_core_xprt_ctx *xprt_ptr, xprt_ptr->prio_bin = kzalloc(xprt_ptr->num_priority * sizeof(struct glink_qos_priority_bin), GFP_KERNEL); + if (xprt_ptr->num_priority > 1) + sched_setscheduler(xprt_ptr->tx_task, SCHED_FIFO, ¶m); if (!xprt_ptr->prio_bin) { GLINK_ERR("%s: unable to allocate priority bins\n", __func__); return -ENOMEM; @@ -3683,21 +3691,24 @@ int glink_core_register_transport(struct glink_transport_if *if_ptr, xprt_ptr->local_state = GLINK_XPRT_DOWN; xprt_ptr->remote_neg_completed = false; INIT_LIST_HEAD(&xprt_ptr->channels); - ret = glink_core_init_xprt_qos_cfg(xprt_ptr, cfg); - if (ret < 0) { - kfree(xprt_ptr); - return ret; - } spin_lock_init(&xprt_ptr->tx_ready_lock_lhb2); mutex_init(&xprt_ptr->xprt_dbgfs_lock_lhb3); - INIT_WORK(&xprt_ptr->tx_work, tx_work_func); - xprt_ptr->tx_wq = create_singlethread_workqueue("glink_tx"); - if (IS_ERR_OR_NULL(xprt_ptr->tx_wq)) { - GLINK_ERR("%s: unable to allocate workqueue\n", __func__); + init_kthread_work(&xprt_ptr->tx_kwork, tx_func); + init_kthread_worker(&xprt_ptr->tx_wq); + xprt_ptr->tx_task = kthread_run(kthread_worker_fn, + &xprt_ptr->tx_wq, "%s_%s_glink_tx", + xprt_ptr->edge, xprt_ptr->name); + if (IS_ERR_OR_NULL(xprt_ptr->tx_task)) { + GLINK_ERR("%s: unable to run thread\n", __func__); glink_core_deinit_xprt_qos_cfg(xprt_ptr); kfree(xprt_ptr); return -ENOMEM; } + ret = glink_core_init_xprt_qos_cfg(xprt_ptr, cfg); + if (ret < 0) { + kfree(xprt_ptr); + return ret; + } INIT_DELAYED_WORK(&xprt_ptr->pm_qos_work, glink_pm_qos_cancel_worker); pm_qos_add_request(&xprt_ptr->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); @@ -3789,7 +3800,7 @@ static void glink_core_link_down(struct glink_transport_if *if_ptr) GLINK_DBG_XPRT(xprt_ptr, "%s: Flushing work from tx_wq. Thread: %u\n", __func__, current->pid); - flush_workqueue(xprt_ptr->tx_wq); + flush_kthread_worker(&xprt_ptr->tx_wq); glink_core_channel_cleanup(xprt_ptr); check_link_notifier_and_notify(xprt_ptr, GLINK_LINK_STATE_DOWN); } @@ -4557,6 +4568,7 @@ static void glink_core_rx_cmd_ch_remote_close( { struct channel_ctx *ctx; bool is_ch_fully_closed; + struct glink_core_xprt_ctx *xprt_ptr = if_ptr->glink_core_priv; ctx = xprt_rcid_to_ch_ctx_get(if_ptr->glink_core_priv, rcid); if (!ctx) { @@ -4583,7 +4595,7 @@ static void glink_core_rx_cmd_ch_remote_close( if (is_ch_fully_closed) { glink_delete_ch_from_list(ctx, true); - flush_workqueue(ctx->transport_ptr->tx_wq); + flush_kthread_worker(&xprt_ptr->tx_wq); } rwref_put(&ctx->ch_state_lhc0); } @@ -4599,6 +4611,7 @@ static void glink_core_rx_cmd_ch_close_ack(struct glink_transport_if *if_ptr, { struct channel_ctx *ctx; bool is_ch_fully_closed; + struct glink_core_xprt_ctx *xprt_ptr = if_ptr->glink_core_priv; ctx = xprt_lcid_to_ch_ctx_get(if_ptr->glink_core_priv, lcid); if (!ctx) { @@ -4620,7 +4633,7 @@ static void glink_core_rx_cmd_ch_close_ack(struct glink_transport_if *if_ptr, is_ch_fully_closed = glink_core_ch_close_ack_common(ctx); if (is_ch_fully_closed) { glink_delete_ch_from_list(ctx, true); - flush_workqueue(ctx->transport_ptr->tx_wq); + flush_kthread_worker(&xprt_ptr->tx_wq); } rwref_put(&ctx->ch_state_lhc0); } @@ -4933,7 +4946,7 @@ static void xprt_schedule_tx(struct glink_core_xprt_ctx *xprt_ptr, spin_unlock(&ch_ptr->tx_lists_lock_lhc3); spin_unlock_irqrestore(&xprt_ptr->tx_ready_lock_lhb2, flags); - queue_work(xprt_ptr->tx_wq, &xprt_ptr->tx_work); + queue_kthread_work(&xprt_ptr->tx_wq, &xprt_ptr->tx_kwork); } /** @@ -5115,13 +5128,11 @@ static int glink_scheduler_tx(struct channel_ctx *ctx, } /** - * tx_work_func() - Transmit worker - * @work: Linux work structure + * tx_func() Transmit Kthread + * @work: Linux kthread work structure */ -static void tx_work_func(struct work_struct *work) +static void tx_func(struct kthread_work *work) { - struct glink_core_xprt_ctx *xprt_ptr = - container_of(work, struct glink_core_xprt_ctx, tx_work); struct channel_ctx *ch_ptr; uint32_t prio; uint32_t tx_ready_head_prio; @@ -5129,6 +5140,8 @@ static void tx_work_func(struct work_struct *work) struct channel_ctx *tx_ready_head = NULL; bool transmitted_successfully = true; unsigned long flags; + struct glink_core_xprt_ctx *xprt_ptr = container_of(work, + struct glink_core_xprt_ctx, tx_kwork); GLINK_PERF("%s: worker starting\n", __func__); @@ -5214,8 +5227,9 @@ static void tx_work_func(struct work_struct *work) static void glink_core_tx_resume(struct glink_transport_if *if_ptr) { - queue_work(if_ptr->glink_core_priv->tx_wq, - &if_ptr->glink_core_priv->tx_work); + struct glink_core_xprt_ctx *xprt_ptr = if_ptr->glink_core_priv; + + queue_kthread_work(&xprt_ptr->tx_wq, &xprt_ptr->tx_kwork); } /** diff --git a/drivers/soc/qcom/glink_smd_xprt.c b/drivers/soc/qcom/glink_smd_xprt.c index c3fb34773db0..9936dc22400c 100644 --- a/drivers/soc/qcom/glink_smd_xprt.c +++ b/drivers/soc/qcom/glink_smd_xprt.c @@ -10,6 +10,7 @@ * GNU General Public License for more details. */ #include <linux/delay.h> +#include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> @@ -140,6 +141,7 @@ struct channel { uint32_t lcid; uint32_t rcid; struct mutex ch_probe_lock; + struct mutex ch_tasklet_lock; bool wait_for_probe; bool had_probed; struct edge_info *edge; @@ -149,7 +151,7 @@ struct channel { spinlock_t intents_lock; uint32_t next_intent_id; struct workqueue_struct *wq; - struct work_struct work; + struct tasklet_struct data_tasklet; struct intent_info *cur_intent; bool intent_req; bool is_closing; @@ -159,6 +161,7 @@ struct channel { spinlock_t rx_data_lock; bool streaming_ch; bool tx_resume_needed; + bool is_tasklet_enabled; }; /** @@ -230,7 +233,7 @@ static struct glink_core_version versions[] = { static LIST_HEAD(pdrv_list); static DEFINE_MUTEX(pdrv_list_mutex); -static void process_data_event(struct work_struct *work); +static void process_data_event(unsigned long param); static int add_platform_driver(struct channel *ch); static void smd_data_ch_close(struct channel *ch); @@ -324,11 +327,17 @@ static void process_ctl_event(struct work_struct *work) strlcpy(ch->name, name, GLINK_NAME_SIZE); ch->edge = einfo; mutex_init(&ch->ch_probe_lock); + mutex_init(&ch->ch_tasklet_lock); INIT_LIST_HEAD(&ch->intents); INIT_LIST_HEAD(&ch->used_intents); spin_lock_init(&ch->intents_lock); spin_lock_init(&ch->rx_data_lock); - INIT_WORK(&ch->work, process_data_event); + mutex_lock(&ch->ch_tasklet_lock); + tasklet_init(&ch->data_tasklet, + process_data_event, (unsigned long)ch); + tasklet_disable(&ch->data_tasklet); + ch->is_tasklet_enabled = false; + mutex_unlock(&ch->ch_tasklet_lock); ch->wq = create_singlethread_workqueue( ch->name); if (!ch->wq) { @@ -362,6 +371,7 @@ static void process_ctl_event(struct work_struct *work) } else { spin_unlock_irqrestore( &einfo->channels_lock, flags); + tasklet_kill(&temp_ch->data_tasklet); destroy_workqueue(temp_ch->wq); kfree(temp_ch); } @@ -602,6 +612,12 @@ static void process_open_event(struct work_struct *work) SMD_TRANS_XPRT_ID); mutex_unlock(&einfo->rx_cmd_lock); } + mutex_lock(&ch->ch_tasklet_lock); + if (!ch->is_tasklet_enabled) { + tasklet_enable(&ch->data_tasklet); + ch->is_tasklet_enabled = true; + } + mutex_unlock(&ch->ch_tasklet_lock); kfree(ch_work); } @@ -626,6 +642,12 @@ static void process_close_event(struct work_struct *work) ch->rcid); mutex_unlock(&einfo->rx_cmd_lock); } + mutex_lock(&ch->ch_tasklet_lock); + if (ch->is_tasklet_enabled) { + tasklet_disable(&ch->data_tasklet); + ch->is_tasklet_enabled = false; + } + mutex_unlock(&ch->ch_tasklet_lock); ch->rcid = 0; } @@ -696,9 +718,9 @@ static void process_reopen_event(struct work_struct *work) /** * process_data_event() - process a data event task - * @work: The data task to process. + * @param: Pointer to the channel in long format. */ -static void process_data_event(struct work_struct *work) +static void process_data_event(unsigned long param) { struct channel *ch; struct edge_info *einfo; @@ -710,7 +732,7 @@ static void process_data_event(struct work_struct *work) unsigned long intents_flags; unsigned long rx_data_flags; - ch = container_of(work, struct channel, work); + ch = (struct channel *)param; einfo = ch->edge; if (ch->tx_resume_needed && smd_write_avail(ch->smd_ch) > 0) { @@ -810,7 +832,7 @@ static void smd_data_ch_notify(void *priv, unsigned event) switch (event) { case SMD_EVENT_DATA: - queue_work(ch->wq, &ch->work); + tasklet_hi_schedule(&ch->data_tasklet); break; case SMD_EVENT_OPEN: work = kmalloc(sizeof(*work), GFP_ATOMIC); @@ -883,6 +905,12 @@ static void smd_data_ch_close(struct channel *ch) ch->is_closing = true; ch->tx_resume_needed = false; + mutex_lock(&ch->ch_tasklet_lock); + if (ch->is_tasklet_enabled) { + tasklet_disable(&ch->data_tasklet); + ch->is_tasklet_enabled = false; + } + mutex_unlock(&ch->ch_tasklet_lock); flush_workqueue(ch->wq); mutex_lock(&ch->ch_probe_lock); @@ -1186,11 +1214,17 @@ static int tx_cmd_ch_open(struct glink_transport_if *if_ptr, uint32_t lcid, strlcpy(ch->name, name, GLINK_NAME_SIZE); ch->edge = einfo; mutex_init(&ch->ch_probe_lock); + mutex_init(&ch->ch_tasklet_lock); INIT_LIST_HEAD(&ch->intents); INIT_LIST_HEAD(&ch->used_intents); spin_lock_init(&ch->intents_lock); spin_lock_init(&ch->rx_data_lock); - INIT_WORK(&ch->work, process_data_event); + mutex_lock(&ch->ch_tasklet_lock); + tasklet_init(&ch->data_tasklet, process_data_event, + (unsigned long)ch); + tasklet_disable(&ch->data_tasklet); + ch->is_tasklet_enabled = false; + mutex_unlock(&ch->ch_tasklet_lock); ch->wq = create_singlethread_workqueue(ch->name); if (!ch->wq) { SMDXPRT_ERR(einfo, @@ -1220,6 +1254,7 @@ static int tx_cmd_ch_open(struct glink_transport_if *if_ptr, uint32_t lcid, spin_unlock_irqrestore(&einfo->channels_lock, flags); } else { spin_unlock_irqrestore(&einfo->channels_lock, flags); + tasklet_kill(&temp_ch->data_tasklet); destroy_workqueue(temp_ch->wq); kfree(temp_ch); } @@ -1463,6 +1498,12 @@ static int ssr(struct glink_transport_if *if_ptr) list_for_each_entry(ch, &einfo->channels, node) { spin_unlock_irqrestore(&einfo->channels_lock, flags); ch->is_closing = true; + mutex_lock(&ch->ch_tasklet_lock); + if (ch->is_tasklet_enabled) { + tasklet_disable(&ch->data_tasklet); + ch->is_tasklet_enabled = false; + } + mutex_unlock(&ch->ch_tasklet_lock); flush_workqueue(ch->wq); mutex_lock(&ch->ch_probe_lock); ch->wait_for_probe = false; @@ -1568,7 +1609,7 @@ static void check_and_resume_rx(struct channel *ch, size_t intent_size) { if (ch->intent_req && ch->intent_req_size <= intent_size) { ch->intent_req = false; - queue_work(ch->wq, &ch->work); + tasklet_hi_schedule(&ch->data_tasklet); } } @@ -1895,7 +1936,7 @@ static int poll(struct glink_transport_if *if_ptr, uint32_t lcid) spin_unlock_irqrestore(&einfo->channels_lock, flags); rc = smd_is_pkt_avail(ch->smd_ch); if (rc == 1) - process_data_event(&ch->work); + process_data_event((unsigned long)ch); return rc; } diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index ffcd44168fbf..d7d08dc588e5 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -169,7 +169,10 @@ struct mailbox_config_info { * been sent, and a response is pending from the * remote side. Protected by @write_lock. * @kwork: Work to be executed when an irq is received. - * @kworker: Handle to the entity processing @kwork. + * @kworker: Handle to the entity processing of + deferred commands. + * @tasklet Handle to tasklet to process incoming data + packets in atomic manner. * @task: Handle to the task context used to run @kworker. * @use_ref: Active uses of this transport use this to grab * a reference. Used for ssr synchronization. @@ -210,6 +213,7 @@ struct edge_info { struct kthread_work kwork; struct kthread_worker kworker; struct task_struct *task; + struct tasklet_struct tasklet; struct srcu_struct use_ref; bool in_ssr; spinlock_t rx_lock; @@ -1145,6 +1149,18 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) } /** + * rx_worker_atomic() - worker function to process received command in atomic + * context. + * @param: The param parameter passed during initialization of the tasklet. + */ +static void rx_worker_atomic(unsigned long param) +{ + struct edge_info *einfo = (struct edge_info *)param; + + __rx_worker(einfo, true); +} + +/** * rx_worker() - worker function to process received commands * @work: kwork associated with the edge to process commands on. */ @@ -1163,7 +1179,7 @@ irqreturn_t irq_handler(int irq, void *priv) if (einfo->rx_reset_reg) writel_relaxed(einfo->out_irq_mask, einfo->rx_reset_reg); - queue_kthread_work(&einfo->kworker, &einfo->kwork); + tasklet_hi_schedule(&einfo->tasklet); einfo->rx_irq_count++; return IRQ_HANDLED; @@ -2244,6 +2260,7 @@ static int glink_smem_native_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); + tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->read_from_fifo = read_from_fifo; einfo->write_to_fifo = write_to_fifo; init_srcu_struct(&einfo->use_ref); @@ -2344,6 +2361,7 @@ smem_alloc_fail: flush_kthread_worker(&einfo->kworker); kthread_stop(einfo->task); einfo->task = NULL; + tasklet_kill(&einfo->tasklet); kthread_fail: iounmap(einfo->out_irq_reg); ioremap_fail: @@ -2429,6 +2447,7 @@ static int glink_rpm_native_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); + tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->intentless = true; einfo->read_from_fifo = memcpy32_fromio; einfo->write_to_fifo = memcpy32_toio; @@ -2590,6 +2609,7 @@ toc_init_fail: flush_kthread_worker(&einfo->kworker); kthread_stop(einfo->task); einfo->task = NULL; + tasklet_kill(&einfo->tasklet); kthread_fail: iounmap(msgram); msgram_ioremap_fail: @@ -2718,6 +2738,7 @@ static int glink_mailbox_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); + tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->read_from_fifo = read_from_fifo; einfo->write_to_fifo = write_to_fifo; init_srcu_struct(&einfo->use_ref); @@ -2839,6 +2860,7 @@ smem_alloc_fail: flush_kthread_worker(&einfo->kworker); kthread_stop(einfo->task); einfo->task = NULL; + tasklet_kill(&einfo->tasklet); kthread_fail: iounmap(einfo->rx_reset_reg); rx_reset_ioremap_fail: |
