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

Commit ece1046c authored by Anirudh Raghavendra's avatar Anirudh Raghavendra Committed by Edgar Flores
Browse files

msm: ADSPRPC: Awake PM with a timeout



Awake PM with a timeout in kernel soon after response from DSP to
keep the system awake and allow the ioctl call reach user space from
kernel space.

Change-Id: If518e17ec300565af2d16feca0524e741790ba7f
Acked-by: default avatarHimateja Reddy <hmreddy@qualcomm.com>
Signed-off-by: default avatarAnirudh Raghavendra <araghave@codeaurora.org>
Signed-off-by: default avatarEdgar Flores <edgarf@codeaurora.org>
parent 108427ae
Loading
Loading
Loading
Loading
+70 −67
Original line number Diff line number Diff line
@@ -136,6 +136,9 @@
#define FASTRPC_STATIC_HANDLE_MAX (20)
#define FASTRPC_LATENCY_CTRL_ENB  (1)

/* Maximum PM timeout that can be voted through fastrpc */
#define MAX_PM_TIMEOUT_MS 50

/* timeout in us for busy polling after early response from remote processor */
#define FASTRPC_POLL_TIME (4000)

@@ -304,7 +307,6 @@ struct smq_invoke_ctx {
	uint32_t early_wake_time;
	/* work done status flag */
	bool is_work_done;
	bool pm_awake_voted;
	/* Store Async job in the context*/
	struct fastrpc_async_job asyncjob;
	/* Async early flag to check the state of context */
@@ -399,11 +401,9 @@ struct fastrpc_apps {
	bool legacy_remote_heap;
	/* Unique job id for each message */
	uint64_t jobid[NUM_CHANNELS];
	/* Secure subsystems like ADSP/SLPI will use secure client */
	struct wakeup_source *wake_source_secure;
	/* Non-secure subsystem like CDSP will use regular client */
	struct wakeup_source *wake_source;
	struct gid_list gidlist;
	struct device *secure_dev;
	struct device *non_secure_dev;
};

struct fastrpc_mmap {
@@ -494,6 +494,11 @@ struct fastrpc_file {
	wait_queue_head_t async_wait_queue;
	/* IRQ safe spin lock for protecting async queue */
	spinlock_t aqlock;
	/* Secure subsystems like ADSP/SLPI will use secure client */
	struct wakeup_source *wake_source_secure;
	/* Non-secure subsystem like CDSP will use regular client */
	struct wakeup_source *wake_source;
	uint32_t ws_timeout;
};

static struct fastrpc_apps gfa;
@@ -566,9 +571,7 @@ static struct fastrpc_channel_ctx gcinfo[NUM_CHANNELS] = {
static int hlosvm[1] = {VMID_HLOS};
static int hlosvmperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};

static void fastrpc_pm_awake(int fl_wake_enable, bool *pm_awake_voted,
			int channel_type);
static void fastrpc_pm_relax(bool *pm_awake_voted, int channel_type);
static inline void fastrpc_pm_awake(struct fastrpc_file *fl, int channel_type);

static inline int64_t getnstimediff(struct timespec64 *start)
{
@@ -1417,7 +1420,6 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel,
	ctx->magic = FASTRPC_CTX_MAGIC;
	ctx->rsp_flags = NORMAL_RESPONSE;
	ctx->is_work_done = false;
	ctx->pm_awake_voted = false;
	ctx->copybuf = NULL;
	ctx->is_early_wakeup = false;
	if (invokefd->job) {
@@ -1528,8 +1530,7 @@ static void fastrpc_queue_completed_async_job(struct smq_invoke_ctx *ctx)
static void context_notify_user(struct smq_invoke_ctx *ctx,
		int retval, uint32_t rsp_flags, uint32_t early_wake_time)
{
	fastrpc_pm_awake(ctx->fl->wake_enable, &ctx->pm_awake_voted,
		gcinfo[ctx->fl->cid].secure);
	fastrpc_pm_awake(ctx->fl, gcinfo[ctx->fl->cid].secure);
	ctx->retval = retval;
	ctx->rsp_flags = (enum fastrpc_response_flags)rsp_flags;
	trace_fastrpc_context_complete(ctx->fl->cid, (uint64_t)ctx, retval,
@@ -2207,31 +2208,22 @@ static void fastrpc_init(struct fastrpc_apps *me)
	me->channel[CDSP_DOMAIN_ID].secure = NON_SECURE_CHANNEL;
}

static inline void fastrpc_pm_awake(int fl_wake_enable, bool *pm_awake_voted,
				int channel_type)
static inline void fastrpc_pm_awake(struct fastrpc_file *fl, int channel_type)
{
	struct fastrpc_apps *me = &gfa;
	struct wakeup_source *wake_source = NULL;

	if (!fl_wake_enable || *pm_awake_voted)
	if (!fl->wake_enable || !fl->wake_source || !fl->wake_source_secure)
		return;
	/*
	 * Vote with PM to abort any suspend in progress and
	 * keep system awake for specified timeout
	 */
	if (channel_type == SECURE_CHANNEL)
		__pm_stay_awake(me->wake_source_secure);
		wake_source = fl->wake_source_secure;
	else if (channel_type == NON_SECURE_CHANNEL)
		__pm_stay_awake(me->wake_source);
	*pm_awake_voted = true;
}

static inline void fastrpc_pm_relax(bool *pm_awake_voted, int channel_type)
{
	struct fastrpc_apps *me = &gfa;
		wake_source = fl->wake_source;

	if (!(*pm_awake_voted))
		return;
	if (channel_type == SECURE_CHANNEL)
		__pm_relax(me->wake_source_secure);
	else if (channel_type == NON_SECURE_CHANNEL)
		__pm_relax(me->wake_source);
	*pm_awake_voted = false;
	pm_wakeup_ws_event(wake_source, fl->ws_timeout, true);
}

static inline int fastrpc_wait_for_response(struct smq_invoke_ctx *ctx,
@@ -2378,7 +2370,6 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
	int err = 0, interrupted = 0, cid = fl->cid;
	struct timespec64 invoket = {0};
	int64_t *perf_counter = NULL;
	bool pm_awake_voted = false;
	bool isasyncinvoke = false;

	VERIFY(err, cid >= ADSP_DOMAIN_ID && cid < NUM_CHANNELS &&
@@ -2389,7 +2380,6 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
		err = -EBADR;
		goto bail;
	}
	fastrpc_pm_awake(fl->wake_enable, &pm_awake_voted, gcinfo[cid].secure);

	if (fl->profile) {
		perf_counter = getperfcounter(fl, PERF_COUNT);
@@ -2449,9 +2439,7 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
	if (isasyncinvoke)
		goto invoke_end;
 wait:
	fastrpc_pm_relax(&pm_awake_voted, gcinfo[cid].secure);
	fastrpc_wait_for_completion(ctx, &interrupted, kernel, 0);
	pm_awake_voted = ctx->pm_awake_voted;
	VERIFY(err, 0 == (err = interrupted));
	if (err)
		goto bail;
@@ -2491,7 +2479,6 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
	if (fl->profile && !interrupted)
		fastrpc_update_invoke_count(invoke->handle, perf_counter,
						&invoket);
	fastrpc_pm_relax(&pm_awake_voted, gcinfo[cid].secure);
	return err;
}

@@ -3855,6 +3842,8 @@ static int fastrpc_file_free(struct fastrpc_file *fl)
	spin_lock(&fl->apps->hlock);
	hlist_del_init(&fl->hn);
	spin_unlock(&fl->apps->hlock);
	wakeup_source_unregister(fl->wake_source);
	wakeup_source_unregister(fl->wake_source_secure);
	kfree(fl->debug_buf);
	kfree(fl->gidlist.gids);

@@ -4199,6 +4188,21 @@ static int fastrpc_channel_open(struct fastrpc_file *fl)
	return err;
}

static inline void fastrpc_register_wakeup_source(struct device *dev,
	const char *client_name, struct wakeup_source **device_wake_source)
{
	struct wakeup_source *wake_source;

	wake_source = wakeup_source_register(dev, client_name);
	if (IS_ERR_OR_NULL(wake_source)) {
		pr_err("adsprpc: Error: %s: %s: wakeup_source_register failed for %s (%s) with err %ld\n",
			__func__, current->comm, dev_name(dev),
			client_name, PTR_ERR(wake_source));
		return;
	}
	*device_wake_source = wake_source;
}

static int fastrpc_device_open(struct inode *inode, struct file *filp)
{
	int err = 0;
@@ -4237,6 +4241,10 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp)
	debugfs_file = debugfs_create_file(fl->debug_buf, 0644, debugfs_root,
						fl, &debugfs_fops);

	fastrpc_register_wakeup_source(me->non_secure_dev, "adsprpc-non_secure",
						&fl->wake_source);
	fastrpc_register_wakeup_source(me->secure_dev, "adsprpc-secure",
						&fl->wake_source_secure);
	context_list_ctor(&fl->clst);
	spin_lock_init(&fl->hlock);
	spin_lock_init(&fl->aqlock);
@@ -4375,8 +4383,26 @@ static int fastrpc_internal_control(struct fastrpc_file *fl,
		cp->kalloc.kalloc_support = 1;
		break;
	case FASTRPC_CONTROL_WAKELOCK:
		if (fl->dev_minor != MINOR_NUM_SECURE_DEV) {
			pr_err("adsprpc: %s: %s: PM voting not allowed for non-secure device node %d\n",
				current->comm, __func__, fl->dev_minor);
			err = -EPERM;
			goto bail;
		}
		fl->wake_enable = cp->wp.enable;
		break;
	case FASTRPC_CONTROL_PM:
		if (!fl->wake_enable) {
			/* Kernel PM voting not requested by this application */
			err = -EACCES;
			goto bail;
		}
		if (cp->pm.timeout > MAX_PM_TIMEOUT_MS)
			fl->ws_timeout = MAX_PM_TIMEOUT_MS;
		else
			fl->ws_timeout = cp->pm.timeout;
		fastrpc_pm_awake(fl, gcinfo[fl->cid].secure);
		break;
	default:
		err = -EBADRQC;
		break;
@@ -5205,8 +5231,6 @@ static struct rpmsg_driver fastrpc_rpmsg_client = {
static int __init fastrpc_device_init(void)
{
	struct fastrpc_apps *me = &gfa;
	struct device *dev = NULL;
	struct device *secure_dev = NULL;
	int err = 0, i;

	debugfs_root = debugfs_create_dir("adsprpc", NULL);
@@ -5237,26 +5261,26 @@ static int __init fastrpc_device_init(void)
	 * Create devices and register with sysfs
	 * Create first device with minor number 0
	 */
	dev = device_create(me->class, NULL,
	me->non_secure_dev = device_create(me->class, NULL,
				MKDEV(MAJOR(me->dev_no), MINOR_NUM_DEV),
				NULL, DEVICE_NAME);
	VERIFY(err, !IS_ERR_OR_NULL(dev));
	VERIFY(err, !IS_ERR_OR_NULL(me->non_secure_dev));
	if (err)
		goto device_create_bail;

	/* Create secure device with minor number for secure device */
	secure_dev = device_create(me->class, NULL,
	me->secure_dev = device_create(me->class, NULL,
				MKDEV(MAJOR(me->dev_no), MINOR_NUM_SECURE_DEV),
				NULL, DEVICE_NAME_SECURE);
	VERIFY(err, !IS_ERR_OR_NULL(secure_dev));
	VERIFY(err, !IS_ERR_OR_NULL(me->secure_dev));
	if (err)
		goto device_create_bail;

	for (i = 0; i < NUM_CHANNELS; i++) {
		me->jobid[i] = 1;
		me->channel[i].dev = secure_dev;
		me->channel[i].dev = me->secure_dev;
		if (i == CDSP_DOMAIN_ID)
			me->channel[i].dev = dev;
			me->channel[i].dev = me->non_secure_dev;
		me->channel[i].ssrcount = 0;
		me->channel[i].prevssrcount = 0;
		me->channel[i].issubsystemup = 1;
@@ -5283,23 +5307,6 @@ static int __init fastrpc_device_init(void)
	}
	me->rpmsg_register = 1;

	me->wake_source = wakeup_source_register(dev, "adsprpc-non_secure");
	VERIFY(err, !IS_ERR_OR_NULL(me->wake_source));
	if (err) {
		pr_err("adsprpc: Error: %s: wakeup_source_register failed for %s with err %ld\n",
			__func__, dev_name(dev), PTR_ERR(me->wake_source));
		goto device_create_bail;
	}

	me->wake_source_secure = wakeup_source_register(secure_dev,
							"adsprpc-secure");
	VERIFY(err, !IS_ERR_OR_NULL(me->wake_source_secure));
	if (err) {
		pr_err("adsprpc: Error: %s: wakeup_source_register failed for %s with err %ld\n",
			__func__, dev_name(secure_dev),
			PTR_ERR(me->wake_source_secure));
		goto device_create_bail;
	}
	return 0;
device_create_bail:
	for (i = 0; i < NUM_CHANNELS; i++) {
@@ -5307,10 +5314,10 @@ static int __init fastrpc_device_init(void)
			subsys_notif_unregister_notifier(me->channel[i].handle,
							&me->channel[i].nb);
	}
	if (!IS_ERR_OR_NULL(dev))
	if (!IS_ERR_OR_NULL(me->non_secure_dev))
		device_destroy(me->class, MKDEV(MAJOR(me->dev_no),
						MINOR_NUM_DEV));
	if (!IS_ERR_OR_NULL(secure_dev))
	if (!IS_ERR_OR_NULL(me->secure_dev))
		device_destroy(me->class, MKDEV(MAJOR(me->dev_no),
						 MINOR_NUM_SECURE_DEV));
	class_destroy(me->class);
@@ -5349,10 +5356,6 @@ static void __exit fastrpc_device_exit(void)
	unregister_chrdev_region(me->dev_no, NUM_CHANNELS);
	if (me->rpmsg_register == 1)
		unregister_rpmsg_driver(&fastrpc_rpmsg_client);
	if (me->wake_source)
		wakeup_source_unregister(me->wake_source);
	if (me->wake_source_secure)
		wakeup_source_unregister(me->wake_source_secure);
	kfree(me->gidlist.gids);
	debugfs_remove_recursive(debugfs_root);
}
+16 −0
Original line number Diff line number Diff line
@@ -148,11 +148,21 @@ struct compat_fastrpc_ctrl_kalloc {
	compat_uint_t kalloc_support; /* Remote memory allocation from kernel */
};

struct compat_fastrpc_ctrl_wakelock {
	compat_uint_t enable;	/* wakelock control enable */
};

struct compat_fastrpc_ctrl_pm {
	compat_uint_t timeout;	/* timeout(in ms) for PM to keep system awake */
};

struct compat_fastrpc_ioctl_control {
	compat_uint_t req;
	union {
		struct compat_fastrpc_ctrl_latency lp;
		struct compat_fastrpc_ctrl_kalloc kalloc;
		struct compat_fastrpc_ctrl_wakelock wp;
		struct compat_fastrpc_ctrl_pm pm;
	};
};

@@ -493,6 +503,12 @@ static int compat_get_fastrpc_ioctl_control(
		err |= put_user(p, &ctrl->lp.enable);
		err |= get_user(p, &ctrl32->lp.latency);
		err |= put_user(p, &ctrl->lp.latency);
	} else if (p == FASTRPC_CONTROL_WAKELOCK) {
		err |= get_user(p, &ctrl32->wp.enable);
		err |= put_user(p, &ctrl->wp.enable);
	} else if (p == FASTRPC_CONTROL_PM) {
		err |= get_user(p, &ctrl32->pm.timeout);
		err |= put_user(p, &ctrl->pm.timeout);
	}

	return err;
+6 −0
Original line number Diff line number Diff line
@@ -282,6 +282,7 @@ enum fastrpc_control_type {
	FASTRPC_CONTROL_SMMU		=	2,
	FASTRPC_CONTROL_KALLOC		=	3,
	FASTRPC_CONTROL_WAKELOCK	=	4,
	FASTRPC_CONTROL_PM		=	5,
};

struct fastrpc_ctrl_latency {
@@ -297,12 +298,17 @@ struct fastrpc_ctrl_wakelock {
	uint32_t enable;	/* wakelock control enable */
};

struct fastrpc_ctrl_pm {
	uint32_t timeout;	/* timeout(in ms) for PM to keep system awake */
};

struct fastrpc_ioctl_control {
	uint32_t req;
	union {
		struct fastrpc_ctrl_latency lp;
		struct fastrpc_ctrl_kalloc kalloc;
		struct fastrpc_ctrl_wakelock wp;
		struct fastrpc_ctrl_pm pm;
	};
};