Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cbd7fd87 authored by Dhoat Harpal's avatar Dhoat Harpal
Browse files

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: default avatarDhoat Harpal <hdhoat@codeaurora.org>
parent 2741ea71
Loading
Loading
Loading
Loading
+47 −34
Original line number Diff line number Diff line
/* 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,9 +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;
	unsigned long curr_qos_rate_kBps;
@@ -340,7 +343,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 +3392,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,7 +3561,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 :
					GLINK_QOS_DEF_NUM_PRIORITY;
@@ -3567,6 +3571,9 @@ 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 +3690,26 @@ 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 +3801,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 +4569,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 +4596,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 +4612,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 +4634,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);
}
@@ -4932,8 +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);
}

/**
@@ -5114,14 +5127,11 @@ static int glink_scheduler_tx(struct channel_ctx *ctx,
	return ret;
}

/**
 * tx_work_func() - Transmit worker
 * @work:	Linux work structure
 */
static void tx_work_func(struct work_struct *work)

/**Actual Transmit function
**/
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 +5139,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 +5226,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);
}

/**
+51 −10
Original line number Diff line number Diff line
@@ -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;
}

+24 −2
Original line number Diff line number Diff line
@@ -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;
@@ -1144,6 +1148,18 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx)
	srcu_read_unlock(&einfo->use_ref, rcu_id);
}

/**
 * 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: