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: Himateja Reddy <hmreddy@qualcomm.com>
Signed-off-by: Anirudh Raghavendra <araghave@codeaurora.org>
Signed-off-by: Edgar Flores <edgarf@codeaurora.org>
This commit is contained in:
Anirudh Raghavendra 2019-12-02 11:35:42 -08:00 committed by Edgar Flores
parent 108427ae28
commit ece1046cb9
3 changed files with 92 additions and 67 deletions

View File

@ -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;
}
wake_source = fl->wake_source;
static inline void fastrpc_pm_relax(bool *pm_awake_voted, int channel_type)
{
struct fastrpc_apps *me = &gfa;
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,18 +2370,16 @@ 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 &&
fl->sctx != NULL);
fl->sctx != NULL);
if (err) {
pr_err("adsprpc: ERROR: %s: kernel session not initialized yet for %s\n",
__func__, current->comm);
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);
}

View File

@ -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;

View File

@ -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;
};
};