summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDhoat Harpal <hdhoat@codeaurora.org>2016-02-08 11:46:12 +0530
committerBryan Huntsman <bryanh@codeaurora.org>2016-04-12 15:49:53 -0700
commit8876c65ca7dee5aedeb3d5377d2b943fad9b899d (patch)
treeb800f6a8493103e50c716320d086db0373e9718d
parent4333a953a464c75d999eb055604e9499caed72e2 (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.c68
-rw-r--r--drivers/soc/qcom/glink_smd_xprt.c61
-rw-r--r--drivers/soc/qcom/glink_smem_native_xprt.c26
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, &param);
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: