From ece1046cb9a5225aa984f05b9ae958bb30747dfb Mon Sep 17 00:00:00 2001 From: Anirudh Raghavendra Date: Mon, 2 Dec 2019 11:35:42 -0800 Subject: [PATCH] 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 Signed-off-by: Anirudh Raghavendra Signed-off-by: Edgar Flores --- drivers/char/adsprpc.c | 137 +++++++++++++++++----------------- drivers/char/adsprpc_compat.c | 16 ++++ drivers/char/adsprpc_shared.h | 6 ++ 3 files changed, 92 insertions(+), 67 deletions(-) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 40d900e7ff1a..75696b31aea7 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -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 @@ bail: 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 @@ invoke_end: 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 @@ bail: 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 @@ device_create_bail: 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); } diff --git a/drivers/char/adsprpc_compat.c b/drivers/char/adsprpc_compat.c index 51fbf273a175..3e7a42129938 100644 --- a/drivers/char/adsprpc_compat.c +++ b/drivers/char/adsprpc_compat.c @@ -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; diff --git a/drivers/char/adsprpc_shared.h b/drivers/char/adsprpc_shared.h index 71b5df76a128..29f5fb864972 100644 --- a/drivers/char/adsprpc_shared.h +++ b/drivers/char/adsprpc_shared.h @@ -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; }; };