diff --git a/arch/arm64/configs/vendor/sdmsteppe-perf_defconfig b/arch/arm64/configs/vendor/sdmsteppe-perf_defconfig index 143a5cd002ba..ffe1a14045b2 100644 --- a/arch/arm64/configs/vendor/sdmsteppe-perf_defconfig +++ b/arch/arm64/configs/vendor/sdmsteppe-perf_defconfig @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm64 4.14.344-openela Kernel Configuration +# Linux/arm64 4.14.345-openela Kernel Configuration # CONFIG_ARM64=y CONFIG_64BIT=y @@ -730,6 +730,7 @@ CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y CONFIG_PM_GENERIC_DOMAINS_SLEEP=y CONFIG_PM_GENERIC_DOMAINS_OF=y CONFIG_CPU_PM=y +CONFIG_BOEFFLA_WL_BLOCKER=y CONFIG_ARCH_HIBERNATION_POSSIBLE=y CONFIG_ARCH_SUSPEND_POSSIBLE=y @@ -4466,7 +4467,7 @@ CONFIG_QCOM_DEVFREQ_DEVBW=y # CONFIG_DEVFREQ_SPDM is not set CONFIG_DEVFREQ_GOV_CDSPL3=y CONFIG_DEVFREQ_BOOST=y -CONFIG_DEVFREQ_INPUT_BOOST_DURATION_MS=100 +CONFIG_DEVFREQ_INPUT_BOOST_DURATION_MS=200 CONFIG_DEVFREQ_WAKE_BOOST_DURATION_MS=1000 CONFIG_DEVFREQ_MSM_CPUBW_BOOST_FREQ=7110 CONFIG_DEVFREQ_MSM_LLCCBW_BOOST_FREQ=3879 diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 2eddf419335a..cf8e7103b5a4 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_OPP) += opp/ obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o obj-$(CONFIG_HAVE_CLK) += clock_ops.o +obj-$(CONFIG_BOEFFLA_WL_BLOCKER) += boeffla_wl_blocker.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/power/boeffla_wl_blocker.c b/drivers/base/power/boeffla_wl_blocker.c new file mode 100644 index 000000000000..7a84b505fc58 --- /dev/null +++ b/drivers/base/power/boeffla_wl_blocker.c @@ -0,0 +1,235 @@ +/* + * 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 "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) */ + snprintf(list_wl_search, sizeof(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 snprintf(buf, sizeof(list_wl), "%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 snprintf(buf, sizeof(list_wl_default), "%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 snprintf(buf, sizeof(BOEFFLA_WL_BLOCKER_VERSION), "%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) { + pr_debug("Boeffla WL blocker: failed to create sys fs object.\n"); + return 0; + } + + /* initialize default list */ + snprintf(list_wl_default, sizeof(LIST_WL_DEFAULT), "%s", LIST_WL_DEFAULT); + build_search_string(list_wl_default, list_wl); + + /* Print debug info */ + pr_debug("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 */ + pr_debug("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/drivers/base/power/boeffla_wl_blocker.h b/drivers/base/power/boeffla_wl_blocker.h new file mode 100644 index 000000000000..06840cba1ec7 --- /dev/null +++ b/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 "" + +#define LENGTH_LIST_WL 512 +#define LENGTH_LIST_WL_DEFAULT 512 +#define LENGTH_LIST_WL_SEARCH LENGTH_LIST_WL + LENGTH_LIST_WL_DEFAULT + 5 diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 5ee1c08fe6a4..a8d1e4d637e6 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -780,6 +780,10 @@ void dpm_resume_early(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_early"), state.event, true); mutex_lock(&dpm_list_mtx); pm_transition = state; diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index db2a02b8b54a..ab361a6c6f73 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -30,6 +30,17 @@ suspend_state_t pm_suspend_target_state; #define pm_suspend_target_state (PM_SUSPEND_ON) #endif +#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. @@ -597,6 +608,56 @@ 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) +{ + // 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) + { + int length = strlen(ws->name); + char wakelock_name[length + 2]; + + // wake lock names handled have maximum length=50 and minimum=1 + 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. @@ -604,6 +665,21 @@ static void wakeup_source_activate(struct wakeup_source *ws) */ static void wakeup_source_report_event(struct wakeup_source *ws, bool hard) { + +#ifdef CONFIG_BOEFFLA_WL_BLOCKER + if (!check_for_block(ws)) { + 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(); + } +#endif ws->event_count++; /* This is racy, but the counter is approximate anyway. */ if (events_check_enabled) @@ -903,6 +979,9 @@ void pm_print_active_wakeup_sources(void) list_for_each_entry_rcu(ws, &wakeup_sources, entry) { if (ws->active) { pr_debug("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 || diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index fac423faa397..73fe66818558 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -340,3 +340,8 @@ config PM_GENERIC_DOMAINS_OF config CPU_PM bool + +config BOEFFLA_WL_BLOCKER + bool "Boeffla generic wakelock blocker driver" + depends on PM + default N