From a914b6f0647d6503ba92e7f93c21aab77b387799 Mon Sep 17 00:00:00 2001 From: andip71 Date: Wed, 30 Aug 2023 15:08:55 -0500 Subject: [PATCH] boeffla_wl_blocker: add generic wakelock blocker driver v1.0.0 - v1.1.0 --- .../common/arch/arm64/configs/gki_defconfig | 1 + .../common/drivers/base/power/Makefile | 1 + .../drivers/base/power/boeffla_wl_blocker.c | 236 ++++++++++++++++++ .../drivers/base/power/boeffla_wl_blocker.h | 23 ++ .../common/drivers/base/power/main.c | 4 + .../common/drivers/base/power/wakeup.c | 89 ++++++- kernel_platform/common/kernel/power/Kconfig | 5 + 7 files changed, 348 insertions(+), 11 deletions(-) create mode 100644 kernel_platform/common/drivers/base/power/boeffla_wl_blocker.c create mode 100644 kernel_platform/common/drivers/base/power/boeffla_wl_blocker.h diff --git a/kernel_platform/common/arch/arm64/configs/gki_defconfig b/kernel_platform/common/arch/arm64/configs/gki_defconfig index dfe4c5536..f6be7480a 100755 --- a/kernel_platform/common/arch/arm64/configs/gki_defconfig +++ b/kernel_platform/common/arch/arm64/configs/gki_defconfig @@ -72,6 +72,7 @@ CONFIG_CMDLINE_EXTEND=y CONFIG_HIBERNATION=y CONFIG_PM_WAKELOCKS=y CONFIG_PM_WAKELOCKS_LIMIT=0 +CONFIG_BOEFFLA_WL_BLOCKER=y # CONFIG_PM_WAKELOCKS_GC is not set CONFIG_ENERGY_MODEL=y CONFIG_CPU_IDLE=y diff --git a/kernel_platform/common/drivers/base/power/Makefile b/kernel_platform/common/drivers/base/power/Makefile index ffef25fec..38a8a3aa1 100755 --- a/kernel_platform/common/drivers/base/power/Makefile +++ b/kernel_platform/common/drivers/base/power/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o obj-$(CONFIG_HAVE_CLK) += clock_ops.o obj-$(CONFIG_PM_QOS_KUNIT_TEST) += qos-test.o +obj-$(CONFIG_BOEFFLA_WL_BLOCKER) += boeffla_wl_blocker.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/kernel_platform/common/drivers/base/power/boeffla_wl_blocker.c b/kernel_platform/common/drivers/base/power/boeffla_wl_blocker.c new file mode 100644 index 000000000..e9c93ce97 --- /dev/null +++ b/kernel_platform/common/drivers/base/power/boeffla_wl_blocker.c @@ -0,0 +1,236 @@ +/* + * Author: andip71, 01.09.2017 + * + * Version 1.1.0 + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * Change log: + * + * 1.1.0 (01.09.2017) + * - By default, the following wakelocks are blocked in an own list + * qcom_rx_wakelock, wlan, wlan_wow_wl, wlan_extscan_wl, NETLINK + * + * 1.0.1 (29.08.2017) + * - Add killing wakelock when currently active + * + * 1.0.0 (28.08.2017) + * - Initial version + * + */ + +#include +#include +#include +#include +#include +#include +#include "boeffla_wl_blocker.h" + + +/*****************************************/ +// Variables +/*****************************************/ + +char list_wl[LENGTH_LIST_WL] = {0}; +char list_wl_default[LENGTH_LIST_WL_DEFAULT] = {0}; + +extern char list_wl_search[LENGTH_LIST_WL_SEARCH]; +extern bool wl_blocker_active; +extern bool wl_blocker_debug; + + +/*****************************************/ +// internal functions +/*****************************************/ + +static void build_search_string(char *list1, char *list2) +{ + // store wakelock list and search string (with semicolons added at start and end) + sprintf(list_wl_search, ";%s;%s;", list1, list2); + + // set flag if wakelock blocker should be active (for performance reasons) + if (strlen(list_wl_search) > 5) + wl_blocker_active = true; + else + wl_blocker_active = false; +} + + +/*****************************************/ +// sysfs interface functions +/*****************************************/ + +// show list of user configured wakelocks +static ssize_t wakelock_blocker_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + // return list of wakelocks to be blocked + return sprintf(buf, "%s\n", list_wl); +} + + +// store list of user configured wakelocks +static ssize_t wakelock_blocker_store(struct device * dev, struct device_attribute *attr, + const char * buf, size_t n) +{ + int len = n; + + // check if string is too long to be stored + if (len > LENGTH_LIST_WL) + return -EINVAL; + + // store user configured wakelock list and rebuild search string + sscanf(buf, "%s", list_wl); + build_search_string(list_wl_default, list_wl); + + return n; +} + + +// show list of default, predefined wakelocks +static ssize_t wakelock_blocker_default_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + // return list of wakelocks to be blocked + return sprintf(buf, "%s\n", list_wl_default); +} + + +// store list of default, predefined wakelocks +static ssize_t wakelock_blocker_default_store(struct device * dev, struct device_attribute *attr, + const char * buf, size_t n) +{ + int len = n; + + // check if string is too long to be stored + if (len > LENGTH_LIST_WL_DEFAULT) + return -EINVAL; + + // store default, predefined wakelock list and rebuild search string + sscanf(buf, "%s", list_wl_default); + build_search_string(list_wl_default, list_wl); + + return n; +} + + +// show debug information of driver internals +static ssize_t debug_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + // return current debug status + return sprintf(buf, "Debug status: %d\n\nUser list: %s\nDefault list: %s\nSearch list: %s\nActive: %d\n", + wl_blocker_debug, list_wl, list_wl_default, list_wl_search, wl_blocker_active); +} + + +// store debug mode on/off (1/0) +static ssize_t debug_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int ret = -EINVAL; + unsigned int val; + + // check data and store if valid + ret = sscanf(buf, "%d", &val); + + if (ret != 1) + return -EINVAL; + + if (val == 1) + wl_blocker_debug = true; + else + wl_blocker_debug = false; + + return count; +} + + +static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + // return version information + return sprintf(buf, "%s\n", BOEFFLA_WL_BLOCKER_VERSION); +} + + + +/*****************************************/ +// Initialize sysfs objects +/*****************************************/ + +// define objects +static DEVICE_ATTR(wakelock_blocker, 0644, wakelock_blocker_show, wakelock_blocker_store); +static DEVICE_ATTR(wakelock_blocker_default, 0644, wakelock_blocker_default_show, wakelock_blocker_default_store); +static DEVICE_ATTR(debug, 0664, debug_show, debug_store); +static DEVICE_ATTR(version, 0664, version_show, NULL); + +// define attributes +static struct attribute *boeffla_wl_blocker_attributes[] = { + &dev_attr_wakelock_blocker.attr, + &dev_attr_wakelock_blocker_default.attr, + &dev_attr_debug.attr, + &dev_attr_version.attr, + NULL +}; + +// define attribute group +static struct attribute_group boeffla_wl_blocker_control_group = { + .attrs = boeffla_wl_blocker_attributes, +}; + +// define control device +static struct miscdevice boeffla_wl_blocker_control_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "boeffla_wakelock_blocker", +}; + + +/*****************************************/ +// Driver init and exit functions +/*****************************************/ + +static int boeffla_wl_blocker_init(void) +{ + // register boeffla wakelock blocker control device + misc_register(&boeffla_wl_blocker_control_device); + if (sysfs_create_group(&boeffla_wl_blocker_control_device.this_device->kobj, + &boeffla_wl_blocker_control_group) < 0) { + printk("Boeffla WL blocker: failed to create sys fs object.\n"); + return 0; + } + + // initialize default list + sprintf(list_wl_default, "%s", LIST_WL_DEFAULT); + build_search_string(list_wl_default, list_wl); + + // Print debug info + printk("Boeffla WL blocker: driver version %s started\n", BOEFFLA_WL_BLOCKER_VERSION); + + return 0; +} + + +static void boeffla_wl_blocker_exit(void) +{ + // remove boeffla wakelock blocker control device + sysfs_remove_group(&boeffla_wl_blocker_control_device.this_device->kobj, + &boeffla_wl_blocker_control_group); + + // Print debug info + printk("Boeffla WL blocker: driver stopped\n"); +} + + +/* define driver entry points */ +module_init(boeffla_wl_blocker_init); +module_exit(boeffla_wl_blocker_exit); diff --git a/kernel_platform/common/drivers/base/power/boeffla_wl_blocker.h b/kernel_platform/common/drivers/base/power/boeffla_wl_blocker.h new file mode 100644 index 000000000..299b8806a --- /dev/null +++ b/kernel_platform/common/drivers/base/power/boeffla_wl_blocker.h @@ -0,0 +1,23 @@ +/* + * Author: andip71, 01.09.2017 + * + * Version 1.1.0 + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define BOEFFLA_WL_BLOCKER_VERSION "1.1.0" + +#define LIST_WL_DEFAULT "qcom_rx_wakelock;smp2p-sleepstate;wlan;wlan_pno_wl;hal_bluetooth_lock;898000.qcom,qup_uart;grip_wake_lock" + +#define LENGTH_LIST_WL 255 +#define LENGTH_LIST_WL_DEFAULT 100 +#define LENGTH_LIST_WL_SEARCH LENGTH_LIST_WL + LENGTH_LIST_WL_DEFAULT + 5 diff --git a/kernel_platform/common/drivers/base/power/main.c b/kernel_platform/common/drivers/base/power/main.c index 595646768..d1fd7ec3a 100755 --- a/kernel_platform/common/drivers/base/power/main.c +++ b/kernel_platform/common/drivers/base/power/main.c @@ -696,6 +696,10 @@ static void dpm_noirq_resume_devices(pm_message_t state) struct device *dev; ktime_t starttime = ktime_get(); +#ifdef CONFIG_BOEFFLA_WL_BLOCKER + pm_print_active_wakeup_sources(); +#endif + trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, true); mutex_lock(&dpm_list_mtx); pm_transition = state; diff --git a/kernel_platform/common/drivers/base/power/wakeup.c b/kernel_platform/common/drivers/base/power/wakeup.c index a0013a386..49c76ed8d 100755 --- a/kernel_platform/common/drivers/base/power/wakeup.c +++ b/kernel_platform/common/drivers/base/power/wakeup.c @@ -30,6 +30,17 @@ suspend_state_t pm_suspend_target_state; #define list_for_each_entry_rcu_locked(pos, head, member) \ list_for_each_entry_rcu(pos, head, member, \ srcu_read_lock_held(&wakeup_srcu)) + +#ifdef CONFIG_BOEFFLA_WL_BLOCKER +#include "boeffla_wl_blocker.h" + +char list_wl_search[LENGTH_LIST_WL_SEARCH] = {0}; +bool wl_blocker_active = false; +bool wl_blocker_debug = false; + +static void wakeup_source_deactivate(struct wakeup_source *ws); +#endif + /* * If set, the suspend/hibernate code will abort transitions to a sleep state * if wakeup events are registered during or immediately before the transition. @@ -613,6 +624,54 @@ static void wakeup_source_activate(struct wakeup_source *ws) trace_wakeup_source_activate(ws->name, cec); } +#ifdef CONFIG_BOEFFLA_WL_BLOCKER +// AP: Function to check if a wakelock is on the wakelock blocker list +static bool check_for_block(struct wakeup_source *ws) +{ + char wakelock_name[52] = {0}; + int length; + // if debug mode on, print every wakelock requested + if (wl_blocker_debug) + printk("Boeffla WL blocker: %s requested\n", ws->name); + + // if there is no list of wakelocks to be blocked, exit without futher checking + if (!wl_blocker_active) + return false; + + // only if ws structure is valid + if (ws) + { + // wake lock names handled have maximum length=50 and minimum=1 + length = strlen(ws->name); + if ((length > 50) || (length < 1)) + return false; + // check if wakelock is in wake lock list to be blocked + sprintf(wakelock_name, ";%s;", ws->name); + + if(strstr(list_wl_search, wakelock_name) == NULL) + return false; + + // wake lock is in list, print it if debug mode on + if (wl_blocker_debug) + printk("Boeffla WL blocker: %s blocked\n", ws->name); + + // if it is currently active, deactivate it immediately + log in debug mode + if (ws->active) + { + wakeup_source_deactivate(ws); + + if (wl_blocker_debug) + printk("Boeffla WL blocker: %s killed\n", ws->name); + } + + // finally block it + return true; + } + // there was no valid ws structure, do not block by default + return false; +} +#endif + /** * wakeup_source_report_event - Report wakeup event using the given source. * @ws: Wakeup source to report the event for. @@ -620,16 +679,20 @@ static void wakeup_source_activate(struct wakeup_source *ws) */ static void wakeup_source_report_event(struct wakeup_source *ws, bool hard) { - ws->event_count++; - /* This is racy, but the counter is approximate anyway. */ - if (events_check_enabled) - ws->wakeup_count++; - - if (!ws->active) - wakeup_source_activate(ws); - - if (hard) - pm_system_wakeup(); +#ifdef CONFIG_BOEFFLA_WL_BLOCKER + if (!check_for_block(ws)) // AP: check if wakelock is on wakelock blocker list + { +#endif + ws->event_count++; + /* This is racy, but the counter is approximate anyway. */ + if (events_check_enabled) + ws->wakeup_count++; + + if (!ws->active) + wakeup_source_activate(ws); +#ifdef CONFIG_BOEFFLA_WL_BLOCKER + } +#endif } /** @@ -918,7 +981,11 @@ void pm_print_active_wakeup_sources(void) list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) { if (ws->active) { pm_pr_dbg("active wakeup source: %s\n", ws->name); - active = 1; + pr_info("active wakeup source: %s\n", ws->name); +#ifdef CONFIG_BOEFFLA_WL_BLOCKER + if (!check_for_block(ws)) // AP: check if wakelock is on wakelock blocker list +#endif + active = 1; } else if (!active && (!last_activity_ws || ktime_to_ns(ws->last_time) > diff --git a/kernel_platform/common/kernel/power/Kconfig b/kernel_platform/common/kernel/power/Kconfig index 4d17bc60e..8caa49dbf 100755 --- a/kernel_platform/common/kernel/power/Kconfig +++ b/kernel_platform/common/kernel/power/Kconfig @@ -332,3 +332,8 @@ config ENERGY_MODEL The exact usage of the energy model is subsystem-dependent. If in doubt, say N. + +config BOEFFLA_WL_BLOCKER + bool "Boeffla generic wakelock blocker driver" + depends on PM + default N