aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandip71 <andreasp@gmx.de>2017-08-28 14:57:18 +0200
committerGreekDragon <alextsanisbadlorg@gmail.com>2019-04-20 04:59:06 -0700
commitbe86f0427a41b575dba552c6e6d931d35208820d (patch)
tree6f52c85a11c810966028a494091b9fbaef3cc543
parentd247c2591ed0aa61b8780886850c1c84e7a47708 (diff)
boeffla_wl_blocker: [SQUASHED]
[first commit] boeffla_wl_blocker: add generic wakelock blocker driver v1.0.0 Based on ideas of FranciscoFranco's non-generic driver. Sysfs node: /sys/class/misc/boeffla_wakelock_blocker/wakelock_blocker - list of wakelocks to be blocked, separated by semicolons /sys/class/misc/boeffla_wakelock_blocker/debug - write: 0/1 to switch off and on debug logging into dmesg - read: get current driver internals /sys/class/misc/boeffla_wakelock_blocker/version - show driver version [second commit] boeffla_wl_blocker: update to wakelock blocker driver v1.0.1 - currently active wakelocks on the list are forcefully killed [third commit] boeffla_wl_blocker: update to wakelock blocker driver v1.1.0 There are now two lists: - the previously existing list of user defined wakelocks to block - a new list called "wakelock_blocker_default" which comes prepopulated with the most common and safe wakelocks to block: qcom_rx_wakelock;wlan;wlan_wow_wl;wlan_extscan_wl;netmgr_wl;NETLINK A combination of both wakelock lists will be blocked finally. [fourth commit] boeffla_wl_blocker: export get_active_wakeup_sources Change-Id: Ieb188396babfc6e4128febb4b2b79ee1108bf46f
-rw-r--r--drivers/base/power/Makefile1
-rw-r--r--drivers/base/power/boeffla_wl_blocker.c236
-rw-r--r--drivers/base/power/boeffla_wl_blocker.h23
-rw-r--r--drivers/base/power/main.c6
-rw-r--r--drivers/base/power/wakeup.c93
-rw-r--r--kernel/power/Kconfig4
6 files changed, 354 insertions, 9 deletions
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 2e58ebb1f6c..6686053b66b 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.o
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 00000000000..e9c93ce97ce
--- /dev/null
+++ b/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 <linux/module.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/printk.h>
+#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/drivers/base/power/boeffla_wl_blocker.h b/drivers/base/power/boeffla_wl_blocker.h
new file mode 100644
index 00000000000..416eaa5ec4a
--- /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 "qcom_rx_wakelock;wlan;wlan_wow_wl;wlan_extscan_wl;netmgr_wl;NETLINK"
+
+#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/drivers/base/power/main.c b/drivers/base/power/main.c
index 5cd2b260be4..dc28db4ac74 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -587,6 +587,8 @@ static int device_resume_early(struct device *dev, pm_message_t state)
return error;
}
+extern void print_active_wakeup_sources(void);
+
/**
* dpm_resume_early - Execute "early resume" callbacks for all devices.
* @state: PM transition of the system being carried out.
@@ -595,6 +597,10 @@ static void dpm_resume_early(pm_message_t state)
{
ktime_t starttime = ktime_get();
+#ifdef CONFIG_BOEFFLA_WL_BLOCKER
+ print_active_wakeup_sources();
+#endif
+
mutex_lock(&dpm_list_mtx);
while (!list_empty(&dpm_late_early_list)) {
struct device *dev = to_device(dpm_late_early_list.next);
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index e568c3d9a30..aa4a15d54b9 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -19,6 +19,18 @@
#include "power.h"
+
+#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.
@@ -430,19 +442,77 @@ 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.
*/
static void wakeup_source_report_event(struct wakeup_source *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);
+#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
}
/**
@@ -503,6 +573,7 @@ static inline void update_prevent_sleep_time(struct wakeup_source *ws,
ktime_t now) {}
#endif
+
/**
* wakup_source_deactivate - Mark given wakeup source as inactive.
* @ws: Wakeup source to handle.
@@ -720,7 +791,7 @@ void pm_get_active_wakeup_sources(char *pending_wakeup_source, size_t max)
}
EXPORT_SYMBOL_GPL(pm_get_active_wakeup_sources);
-static void print_active_wakeup_sources(void)
+void print_active_wakeup_sources(void)
{
struct wakeup_source *ws;
int active = 0;
@@ -730,7 +801,10 @@ static void print_active_wakeup_sources(void)
list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
if (ws->active) {
pr_info("active wakeup source: %s\n", ws->name);
- active = 1;
+#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) >
@@ -744,6 +818,7 @@ static void print_active_wakeup_sources(void)
last_activity_ws->name);
rcu_read_unlock();
}
+EXPORT_SYMBOL_GPL(print_active_wakeup_sources);
/**
* pm_wakeup_pending - Check if power transition in progress should be aborted.
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index e97d22086bb..f05d80b0f34 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -305,3 +305,7 @@ config PARTIALRESUME
partial-resume handlers associated with them, and they all return
true.
+config BOEFFLA_WL_BLOCKER
+ bool "Boeffla generic wakelock blocker driver"
+ depends on PM
+ default N