All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Rafael J. Wysocki" <rjw@sisk.pl>
To: "Arve Hjønnevåg" <arve@android.com>
Cc: ncunningham@crca.org.au, u.luckas@road.de, swetland@google.com,
	linux-pm@lists.linux-foundation.org
Subject: Re: [PATCH 03/13] PM: Implement wakelock api.
Date: Sat, 7 Feb 2009 23:27:10 +0100	[thread overview]
Message-ID: <200902072327.11251.rjw@sisk.pl> (raw)
In-Reply-To: <1233802226-23386-4-git-send-email-arve@android.com>

On Thursday 05 February 2009, Arve Hjønnevåg wrote:
> Signed-off-by: Arve Hjønnevåg <arve@android.com>
> ---
>  kernel/power/Kconfig    |   19 ++
>  kernel/power/Makefile   |    1 +
>  kernel/power/power.h    |    7 +
>  kernel/power/wakelock.c |  598 +++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 625 insertions(+), 0 deletions(-)
>  create mode 100644 kernel/power/wakelock.c
> 
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 23bd4da..6e3da6e 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -116,6 +116,25 @@ config SUSPEND_FREEZER
>  
>  	  Turning OFF this setting is NOT recommended! If in doubt, say Y.
>  
> +config HAS_WAKELOCK
> +	bool
> +
> +config WAKELOCK
> +	bool "Wake lock"
> +	depends on PM && RTC_CLASS
> +	default n
> +	select HAS_WAKELOCK
> +	---help---
> +	  Enable wakelocks. When user space request a sleep state the
> +	  sleep request will be delayed until no wake locks are held.
> +
> +config WAKELOCK_STAT
> +	bool "Wake lock stats"
> +	depends on WAKELOCK
> +	default y
> +	---help---
> +	  Report wake lock stats in /proc/wakelocks
> +
>  config HIBERNATION
>  	bool "Hibernation (aka 'suspend to disk')"
>  	depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
> diff --git a/kernel/power/Makefile b/kernel/power/Makefile
> index d7a1016..8d8672b 100644
> --- a/kernel/power/Makefile
> +++ b/kernel/power/Makefile
> @@ -6,6 +6,7 @@ endif
>  obj-y				:= main.o
>  obj-$(CONFIG_PM_SLEEP)		+= console.o
>  obj-$(CONFIG_FREEZER)		+= process.o
> +obj-$(CONFIG_WAKELOCK)		+= wakelock.o
>  obj-$(CONFIG_HIBERNATION)	+= swsusp.o disk.o snapshot.o swap.o user.o
>  
>  obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
> diff --git a/kernel/power/power.h b/kernel/power/power.h
> index 46b5ec7..1527174 100644
> --- a/kernel/power/power.h
> +++ b/kernel/power/power.h
> @@ -223,3 +223,10 @@ static inline void suspend_thaw_processes(void)
>  {
>  }
>  #endif
> +
> +#ifdef CONFIG_WAKELOCK
> +/* kernel/power/wakelock.c */
> +extern struct workqueue_struct *suspend_work_queue;
> +extern struct wake_lock main_wake_lock;
> +extern suspend_state_t requested_suspend_state;
> +#endif
> diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
> new file mode 100644
> index 0000000..c9e22f9
> --- /dev/null
> +++ b/kernel/power/wakelock.c
> @@ -0,0 +1,598 @@
> +/* kernel/power/wakelock.c
> + *
> + * Copyright (C) 2005-2008 Google, Inc.
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/rtc.h>
> +#include <linux/suspend.h>
> +#include <linux/syscalls.h> /* sys_sync */
> +#include <linux/wakelock.h>
> +#ifdef CONFIG_WAKELOCK_STAT
> +#include <linux/proc_fs.h>
> +#endif
> +#include "power.h"
> +
> +enum {
> +	DEBUG_EXIT_SUSPEND = 1U << 0,
> +	DEBUG_WAKEUP = 1U << 1,
> +	DEBUG_SUSPEND = 1U << 2,
> +	DEBUG_EXPIRE = 1U << 3,
> +	DEBUG_WAKE_LOCK = 1U << 4,
> +};
> +static int debug_mask = DEBUG_EXIT_SUSPEND | DEBUG_WAKEUP;
> +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
> +
> +#define WAKE_LOCK_TYPE_MASK              (0x0f)
> +#define WAKE_LOCK_INITIALIZED            (1U << 8)
> +#define WAKE_LOCK_ACTIVE                 (1U << 9)
> +#define WAKE_LOCK_AUTO_EXPIRE            (1U << 10)
> +#define WAKE_LOCK_PREVENTING_SUSPEND     (1U << 11)
> +
> +static DEFINE_SPINLOCK(list_lock);
> +static LIST_HEAD(inactive_locks);
> +static struct list_head active_wake_locks[WAKE_LOCK_TYPE_COUNT];
> +static int current_event_num;
> +struct workqueue_struct *suspend_work_queue;
> +struct wake_lock main_wake_lock;
> +suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
> +static struct wake_lock unknown_wakeup;
> +
> +#ifdef CONFIG_WAKELOCK_STAT
> +static struct wake_lock deleted_wake_locks;
> +static ktime_t last_sleep_time_update;
> +static int wait_for_wakeup;

Care to add kerneldoc comments to the functions?

> +int get_expired_time(struct wake_lock *lock, ktime_t *expire_time)

I would use 'bool'.

> +{
> +	struct timespec ts;
> +	struct timespec kt;
> +	struct timespec tomono;
> +	struct timespec delta;
> +	unsigned long seq;
> +	long timeout;
> +
> +	if (!(lock->flags & WAKE_LOCK_AUTO_EXPIRE))
> +		return 0;
> +	do {
> +		seq = read_seqbegin(&xtime_lock);
> +		timeout = lock->expires - jiffies;

Can it overflow?

Anyway, you are using struct timespec in computations, so why don't you
store it in the lock structure?

> +		if (timeout > 0)
> +			return 0;
> +		kt = current_kernel_time();
> +		tomono = wall_to_monotonic;
> +	} while (read_seqretry(&xtime_lock, seq));
> +	jiffies_to_timespec(-timeout, &delta);
> +	set_normalized_timespec(&ts, kt.tv_sec + tomono.tv_sec - delta.tv_sec,
> +				kt.tv_nsec + tomono.tv_nsec - delta.tv_nsec);
> +	*expire_time = timespec_to_ktime(ts);
> +	return 1;
> +}
> +
> +
> +static int print_lock_stat(char *buf, struct wake_lock *lock)
> +{
> +	int lock_count = lock->stat.count;
> +	int expire_count = lock->stat.expire_count;
> +	ktime_t active_time = ktime_set(0, 0);
> +	ktime_t total_time = lock->stat.total_time;
> +	ktime_t max_time = lock->stat.max_time;
> +	ktime_t prevent_suspend_time = lock->stat.prevent_suspend_time;
> +	if (lock->flags & WAKE_LOCK_ACTIVE) {
> +		ktime_t now, add_time;
> +		int expired = get_expired_time(lock, &now);
> +		if (!expired)
> +			now = ktime_get();
> +		add_time = ktime_sub(now, lock->stat.last_time);
> +		lock_count++;
> +		if (!expired)
> +			active_time = add_time;
> +		else
> +			expire_count++;
> +		total_time = ktime_add(total_time, add_time);
> +		if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND)
> +			prevent_suspend_time = ktime_add(prevent_suspend_time,
> +					ktime_sub(now, last_sleep_time_update));
> +		if (add_time.tv64 > max_time.tv64)
> +			max_time = add_time;
> +	}
> +
> +	return sprintf(buf, "\"%s\"\t%d\t%d\t%d\t%lld\t%lld\t%lld\t%lld\t"
> +		       "%lld\n", lock->name, lock_count, expire_count,
> +		       lock->stat.wakeup_count, ktime_to_ns(active_time),
> +		       ktime_to_ns(total_time),
> +		       ktime_to_ns(prevent_suspend_time), ktime_to_ns(max_time),
> +		       ktime_to_ns(lock->stat.last_time));
> +}
> +

Why did you decide to put that in /proc ?

> +static int wakelocks_read_proc(char *page, char **start, off_t off,
> +			       int count, int *eof, void *data)
> +{
> +	unsigned long irqflags;
> +	struct wake_lock *lock;
> +	int len = 0;
> +	char *p = page;
> +	int type;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +
> +	p += sprintf(p, "name\tcount\texpire_count\twake_count\tactive_since"
> +		     "\ttotal_time\tsleep_time\tmax_time\tlast_change\n");
> +	list_for_each_entry(lock, &inactive_locks, link) {
> +		p += print_lock_stat(p, lock);
> +	}
> +	for (type = 0; type < WAKE_LOCK_TYPE_COUNT; type++) {
> +		list_for_each_entry(lock, &active_wake_locks[type], link)
> +			p += print_lock_stat(p, lock);
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +
> +	*start = page + off;
> +
> +	len = p - page;
> +	if (len > off)
> +		len -= off;
> +	else
> +		len = 0;
> +
> +	return len < count ? len  : count;
> +}
> +
> +static void wake_unlock_stat_locked(struct wake_lock *lock, int expired)
> +{
> +	ktime_t duration;
> +	ktime_t now;
> +	if (!(lock->flags & WAKE_LOCK_ACTIVE))
> +		return;
> +	if (get_expired_time(lock, &now))
> +		expired = 1;

I'd use 'true'.

> +	else
> +		now = ktime_get();
> +	lock->stat.count++;
> +	if (expired)
> +		lock->stat.expire_count++;
> +	duration = ktime_sub(now, lock->stat.last_time);
> +	lock->stat.total_time = ktime_add(lock->stat.total_time, duration);
> +	if (ktime_to_ns(duration) > ktime_to_ns(lock->stat.max_time))
> +		lock->stat.max_time = duration;
> +	lock->stat.last_time = ktime_get();
> +	if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
> +		duration = ktime_sub(now, last_sleep_time_update);
> +		lock->stat.prevent_suspend_time = ktime_add(
> +			lock->stat.prevent_suspend_time, duration);
> +		lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
> +	}
> +}
> +
> +static void update_sleep_wait_stats_locked(int done)
> +{
> +	struct wake_lock *lock;
> +	ktime_t now, etime, elapsed, add;
> +	int expired;
> +
> +	now = ktime_get();
> +	elapsed = ktime_sub(now, last_sleep_time_update);
> +	list_for_each_entry(lock, &active_wake_locks[WAKE_LOCK_SUSPEND], link) {
> +		expired = get_expired_time(lock, &etime);
> +		if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND) {
> +			if (expired)
> +				add = ktime_sub(etime, last_sleep_time_update);
> +			else
> +				add = elapsed;
> +			lock->stat.prevent_suspend_time = ktime_add(
> +				lock->stat.prevent_suspend_time, add);
> +		}
> +		if (done || expired)
> +			lock->flags &= ~WAKE_LOCK_PREVENTING_SUSPEND;
> +		else
> +			lock->flags |= WAKE_LOCK_PREVENTING_SUSPEND;
> +	}
> +	last_sleep_time_update = now;
> +}
> +#endif
> +
> +
> +static void expire_wake_lock(struct wake_lock *lock)
> +{
> +#ifdef CONFIG_WAKELOCK_STAT
> +	wake_unlock_stat_locked(lock, 1);
> +#endif
> +	lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
> +	list_del(&lock->link);
> +	list_add(&lock->link, &inactive_locks);
> +	if (debug_mask & (DEBUG_WAKE_LOCK | DEBUG_EXPIRE))
> +		pr_info("expired wake lock %s\n", lock->name);
> +}
> +
> +static void print_active_locks(int type)
> +{
> +	unsigned long irqflags;
> +	struct wake_lock *lock;
> +
> +	BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	list_for_each_entry(lock, &active_wake_locks[type], link) {
> +		if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
> +			long timeout = lock->expires - jiffies;
> +			if (timeout <= 0)
> +				pr_info("wake lock %s, expired\n", lock->name);
> +			else
> +				pr_info("active wake lock %s, time left %ld\n",
> +					lock->name, timeout);
> +		} else
> +			pr_info("active wake lock %s\n", lock->name);
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +
> +static long has_wake_lock_locked(int type)

I'd change the name, it suggests something different from what the function
does.

> +{
> +	struct wake_lock *lock, *n;
> +	long max_timeout = 0;
> +
> +	BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
> +	list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) {
> +		if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {
> +			long timeout = lock->expires - jiffies;

I think time_after() is for things like this.

> +			if (timeout <= 0)
> +				expire_wake_lock(lock);
> +			else if (timeout > max_timeout)
> +				max_timeout = timeout;
> +		} else
> +			return -1;
> +	}
> +	return max_timeout;
> +}
> +
> +long has_wake_lock(int type)
> +{
> +	long ret;
> +	unsigned long irqflags;
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	ret = has_wake_lock_locked(type);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +	return ret;
> +}
> +
> +static void suspend(struct work_struct *work)
> +{
> +	int ret;
> +	int entry_event_num;
> +
> +	if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: abort suspend\n");
> +		return;
> +	}
> +
> +	entry_event_num = current_event_num;
> +	sys_sync();

pm_suspend() will do it, you don't need to.

> +	if (debug_mask & DEBUG_SUSPEND)
> +		pr_info("suspend: enter suspend\n");

Shouldn't that check if someone has taken a wakelock in the meantime?

> +	ret = pm_suspend(requested_suspend_state);
> +	if (debug_mask & DEBUG_EXIT_SUSPEND) {
> +		struct timespec ts;
> +		struct rtc_time tm;
> +		getnstimeofday(&ts);
> +		rtc_time_to_tm(ts.tv_sec, &tm);
> +		pr_info("suspend: exit suspend, ret = %d "
> +			"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
> +			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
> +			tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
> +	}
> +	if (current_event_num == entry_event_num) {
> +		if (debug_mask & DEBUG_SUSPEND)
> +			pr_info("suspend: pm_suspend returned with no event\n");
> +		wake_lock_timeout(&unknown_wakeup, HZ / 2);
> +	}
> +}
> +static DECLARE_WORK(suspend_work, suspend);
> +
> +static void expire_wake_locks(unsigned long data)
> +{
> +	long has_lock;
> +	unsigned long irqflags;
> +	if (debug_mask & DEBUG_EXPIRE)
> +		pr_info("expire_wake_locks: start\n");
> +	if (debug_mask & DEBUG_SUSPEND)
> +		print_active_locks(WAKE_LOCK_SUSPEND);
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND);
> +	if (debug_mask & DEBUG_EXPIRE)
> +		pr_info("expire_wake_locks: done, has_lock %ld\n", has_lock);
> +	if (has_lock == 0)
> +		queue_work(suspend_work_queue, &suspend_work);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
> +
> +static int power_suspend_late(struct platform_device *pdev, pm_message_t state)
> +{
> +	int ret = has_wake_lock(WAKE_LOCK_SUSPEND) ? -EAGAIN : 0;
> +#ifdef CONFIG_WAKELOCK_STAT
> +	wait_for_wakeup = 1;
> +#endif
> +	if (debug_mask & DEBUG_SUSPEND)
> +		pr_info("power_suspend_late return %d\n", ret);
> +	return ret;
> +}
> +
> +static struct platform_driver power_driver = {
> +	.driver.name = "power",
> +	.suspend_late = power_suspend_late,
> +};
> +static struct platform_device power_device = {
> +	.name = "power",
> +};

What's this and what's it for?

> +
> +void wake_lock_init(struct wake_lock *lock, int type, const char *name)
> +{
> +	unsigned long irqflags = 0;
> +
> +	if (name)
> +		lock->name = name;

Hm.  I'd rather reserve memory for the name and copy it from the address
provided by the caller.

> +	BUG_ON(!lock->name);

Isn't it a bit too drastic?  Perhaps make it return a value so that the caller
can check if it has the lock?

> +
> +	if (debug_mask & DEBUG_WAKE_LOCK)
> +		pr_info("wake_lock_init name=%s\n", lock->name);
> +#ifdef CONFIG_WAKELOCK_STAT
> +	lock->stat.count = 0;
> +	lock->stat.expire_count = 0;
> +	lock->stat.wakeup_count = 0;
> +	lock->stat.total_time = ktime_set(0, 0);
> +	lock->stat.prevent_suspend_time = ktime_set(0, 0);
> +	lock->stat.max_time = ktime_set(0, 0);
> +	lock->stat.last_time = ktime_set(0, 0);
> +#endif
> +	lock->flags = (type & WAKE_LOCK_TYPE_MASK) | WAKE_LOCK_INITIALIZED;
> +
> +	INIT_LIST_HEAD(&lock->link);
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	list_add(&lock->link, &inactive_locks);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(wake_lock_init);
> +
> +void wake_lock_destroy(struct wake_lock *lock)
> +{
> +	unsigned long irqflags;
> +	if (debug_mask & DEBUG_WAKE_LOCK)
> +		pr_info("wake_lock_destroy name=%s\n", lock->name);
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	lock->flags &= ~WAKE_LOCK_INITIALIZED;
> +#ifdef CONFIG_WAKELOCK_STAT
> +	if (lock->stat.count) {
> +		deleted_wake_locks.stat.count += lock->stat.count;
> +		deleted_wake_locks.stat.expire_count += lock->stat.expire_count;
> +		deleted_wake_locks.stat.total_time =
> +			ktime_add(deleted_wake_locks.stat.total_time,
> +				  lock->stat.total_time);
> +		deleted_wake_locks.stat.prevent_suspend_time =
> +			ktime_add(deleted_wake_locks.stat.prevent_suspend_time,
> +				  lock->stat.prevent_suspend_time);
> +		deleted_wake_locks.stat.max_time =
> +			ktime_add(deleted_wake_locks.stat.max_time,
> +				  lock->stat.max_time);
> +	}
> +#endif
> +	list_del(&lock->link);
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(wake_lock_destroy);
> +
> +static void wake_lock_internal(

What  about __wake_lock() ?

> +	struct wake_lock *lock, long timeout, int has_timeout)

What's the point of the last argument?  Wouldn't timeout == 0 be sufficient?

Also, does it make sense to pass negative timeout to it?

> +{
> +	int type;
> +	unsigned long irqflags;
> +	long expire_in;
> +
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	type = lock->flags & WAKE_LOCK_TYPE_MASK;
> +	BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
> +	BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));

I don't like these BUG_ON()s.  Please do something less drastic instead.

> +#ifdef CONFIG_WAKELOCK_STAT
> +	if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {
> +		if (debug_mask & DEBUG_WAKEUP)
> +			pr_info("wakeup wake lock: %s\n", lock->name);
> +		wait_for_wakeup = 0;
> +		lock->stat.wakeup_count++;
> +	}
> +	if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&
> +	    (long)(lock->expires - jiffies) <= 0) {
> +		wake_unlock_stat_locked(lock, 0);
> +		lock->stat.last_time = ktime_get();
> +	}
> +#endif
> +	if (!(lock->flags & WAKE_LOCK_ACTIVE)) {
> +		lock->flags |= WAKE_LOCK_ACTIVE;
> +#ifdef CONFIG_WAKELOCK_STAT
> +		lock->stat.last_time = ktime_get();
> +#endif
> +	}
> +	list_del(&lock->link);
> +	if (has_timeout) {
> +		if (debug_mask & DEBUG_WAKE_LOCK)
> +			pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",
> +				lock->name, type, timeout / HZ,
> +				(timeout % HZ) * MSEC_PER_SEC / HZ);
> +		lock->expires = jiffies + timeout;
> +		lock->flags |= WAKE_LOCK_AUTO_EXPIRE;
> +		list_add_tail(&lock->link, &active_wake_locks[type]);
> +	} else {
> +		if (debug_mask & DEBUG_WAKE_LOCK)
> +			pr_info("wake_lock: %s, type %d\n", lock->name, type);
> +		lock->expires = LONG_MAX;
> +		lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;
> +		list_add(&lock->link, &active_wake_locks[type]);
> +	}

Why not to use list_add_tail() in both cases?

> +	if (type == WAKE_LOCK_SUSPEND) {
> +		current_event_num++;
> +#ifdef CONFIG_WAKELOCK_STAT
> +		if (lock == &main_wake_lock)
> +			update_sleep_wait_stats_locked(1);
> +		else if (!wake_lock_active(&main_wake_lock))
> +			update_sleep_wait_stats_locked(0);
> +#endif
> +		if (has_timeout)
> +			expire_in = has_wake_lock_locked(type);
> +		else
> +			expire_in = -1;

	expire_i = has_timeout ? has_wake_lock_locked(type) : -1;

(there is something like this above too).

> +		if (expire_in > 0) {
> +			if (debug_mask & DEBUG_EXPIRE)
> +				pr_info("wake_lock: %s, start expire timer, "
> +					"%ld\n", lock->name, expire_in);
> +			mod_timer(&expire_timer, jiffies + expire_in);

What exactly are you trying to achieve here?

> +		} else {
> +			if (del_timer(&expire_timer))
> +				if (debug_mask & DEBUG_EXPIRE)
> +					pr_info("wake_lock: %s, stop expire "
> +						"timer\n", lock->name);
> +			if (expire_in == 0)
> +				queue_work(suspend_work_queue, &suspend_work);

This appears to only happen if timeout = 0 is passed to this function while
has_timeout == true.  Is it correct?

> +		}
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +
> +void wake_lock(struct wake_lock *lock)
> +{
> +	wake_lock_internal(lock, 0, 0);
> +}
> +EXPORT_SYMBOL(wake_lock);
> +
> +void wake_lock_timeout(struct wake_lock *lock, long timeout)
> +{
> +	wake_lock_internal(lock, timeout, 1);
> +}
> +EXPORT_SYMBOL(wake_lock_timeout);
> +
> +void wake_unlock(struct wake_lock *lock)
> +{
> +	int type;
> +	unsigned long irqflags;
> +	spin_lock_irqsave(&list_lock, irqflags);
> +	type = lock->flags & WAKE_LOCK_TYPE_MASK;
> +#ifdef CONFIG_WAKELOCK_STAT
> +	wake_unlock_stat_locked(lock, 0);
> +#endif
> +	if (debug_mask & DEBUG_WAKE_LOCK)
> +		pr_info("wake_unlock: %s\n", lock->name);
> +	lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
> +	list_del(&lock->link);
> +	list_add(&lock->link, &inactive_locks);
> +	if (type == WAKE_LOCK_SUSPEND) {
> +		long has_lock = has_wake_lock_locked(type);
> +		if (has_lock > 0) {
> +			if (debug_mask & DEBUG_EXPIRE)
> +				pr_info("wake_unlock: %s, start expire timer, "
> +					"%ld\n", lock->name, has_lock);
> +			mod_timer(&expire_timer, jiffies + has_lock);
> +		} else {
> +			if (del_timer(&expire_timer))
> +				if (debug_mask & DEBUG_EXPIRE)
> +					pr_info("wake_unlock: %s, stop expire "
> +						"timer\n", lock->name);
> +			if (has_lock == 0)
> +				queue_work(suspend_work_queue, &suspend_work);
> +		}
> +		if (lock == &main_wake_lock) {
> +			if (debug_mask & DEBUG_SUSPEND)
> +				print_active_locks(WAKE_LOCK_SUSPEND);
> +#ifdef CONFIG_WAKELOCK_STAT
> +			update_sleep_wait_stats_locked(0);
> +#endif
> +		}
> +	}
> +	spin_unlock_irqrestore(&list_lock, irqflags);
> +}
> +EXPORT_SYMBOL(wake_unlock);
> +
> +int wake_lock_active(struct wake_lock *lock)
> +{
> +	return !!(lock->flags & WAKE_LOCK_ACTIVE);
> +}
> +EXPORT_SYMBOL(wake_lock_active);
> +
> +static int __init wakelocks_init(void)
> +{
> +	int ret;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
> +		INIT_LIST_HEAD(&active_wake_locks[i]);
> +
> +#ifdef CONFIG_WAKELOCK_STAT
> +	wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,
> +			"deleted_wake_locks");
> +#endif
> +	wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");
> +	wake_lock(&main_wake_lock);
> +	wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups");
> +
> +	ret = platform_device_register(&power_device);
> +	if (ret) {
> +		pr_err("wakelocks_init: platform_device_register failed\n");
> +		goto err_platform_device_register;
> +	}
> +	ret = platform_driver_register(&power_driver);
> +	if (ret) {
> +		pr_err("wakelocks_init: platform_driver_register failed\n");
> +		goto err_platform_driver_register;
> +	}

Is this really a platform thing?

What exactly is the benefit of having the 'power_device' and 'power_driver'
registered?.

> +
> +	suspend_work_queue = create_singlethread_workqueue("suspend");
> +	if (suspend_work_queue == NULL) {
> +		ret = -ENOMEM;
> +		goto err_suspend_work_queue;
> +	}
> +
> +#ifdef CONFIG_WAKELOCK_STAT
> +	create_proc_read_entry("wakelocks", S_IRUGO, NULL,
> +				wakelocks_read_proc, NULL);
> +#endif
> +
> +	return 0;
> +
> +err_suspend_work_queue:
> +	platform_driver_unregister(&power_driver);
> +err_platform_driver_register:
> +	platform_device_unregister(&power_device);
> +err_platform_device_register:
> +	wake_lock_destroy(&unknown_wakeup);
> +	wake_lock_destroy(&main_wake_lock);
> +#ifdef CONFIG_WAKELOCK_STAT
> +	wake_lock_destroy(&deleted_wake_locks);
> +#endif
> +	return ret;
> +}
> +
> +static void  __exit wakelocks_exit(void)
> +{
> +#ifdef CONFIG_WAKELOCK_STAT
> +	remove_proc_entry("wakelocks", NULL);
> +#endif
> +	destroy_workqueue(suspend_work_queue);
> +	platform_driver_unregister(&power_driver);
> +	platform_device_unregister(&power_device);
> +	wake_lock_destroy(&unknown_wakeup);
> +	wake_lock_destroy(&main_wake_lock);
> +#ifdef CONFIG_WAKELOCK_STAT
> +	wake_lock_destroy(&deleted_wake_locks);
> +#endif
> +}
> +
> +core_initcall(wakelocks_init);
> +module_exit(wakelocks_exit);

In general, I found this mechanism overly complicated.

As I said previously, I don't really see a benefit of using multiple wakelocks
over using a single reference counter with certain timer mechanism triggering
suspend in the counter is 0.

Also, I'd really prefer to see the patch without the CONFIG_WAKELOCK_STAT
thing included, first.  It would have been much easier to read.

Thanks,
Rafael
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

  parent reply	other threads:[~2009-02-07 22:27 UTC|newest]

Thread overview: 192+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-02-05  2:50 [RFC][PATCH 00/11] Android PM extensions Arve Hjønnevåg
2009-02-05  2:50 ` [PATCH 01/13] PM: Add wake lock api Arve Hjønnevåg
2009-02-05  2:50   ` [PATCH 02/13] PM: Add early suspend api Arve Hjønnevåg
2009-02-05  2:50     ` [PATCH 03/13] PM: Implement wakelock api Arve Hjønnevåg
2009-02-05  2:50       ` [PATCH 04/13] PM: wakelock: Override wakelocks when using /sys/power/state Arve Hjønnevåg
2009-02-05  2:50         ` [PATCH 05/13] PM: Add option to disable /sys/power/state interface Arve Hjønnevåg
2009-02-05  2:50           ` [PATCH 06/13] PM: Implement early suspend api Arve Hjønnevåg
2009-02-05  2:50             ` [PATCH 07/13] PM: wakelock: Add /sys/power/request_state Arve Hjønnevåg
2009-02-05  2:50               ` [PATCH 08/13] PM: Add user-space wake lock api Arve Hjønnevåg
2009-02-05  2:50                 ` [PATCH 09/13] PM: wakelock: Abort task freezing if a wake lock is held Arve Hjønnevåg
2009-02-05  2:50                   ` [PATCH 10/13] PM: earlysuspend: Add console switch when user requested sleep state changes Arve Hjønnevåg
2009-02-05  2:50                     ` [PATCH 11/13] PM: earlysuspend: Removing dependence on console Arve Hjønnevåg
2009-02-05  2:50                       ` [PATCH 12/13] Input: Hold wake lock while event queue is not empty Arve Hjønnevåg
2009-02-05  2:50                         ` [PATCH 13/13] ledtrig-sleep: Add led trigger for sleep debugging Arve Hjønnevåg
2009-02-05  9:08                         ` [PATCH 12/13] Input: Hold wake lock while event queue is not empty Pavel Machek
2009-02-05  9:06                       ` [PATCH 11/13] PM: earlysuspend: Removing dependence on console Pavel Machek
2009-02-05  9:42                         ` Arve Hjønnevåg
2009-02-05  9:53                           ` Pavel Machek
2009-02-05  9:03                     ` [PATCH 10/13] PM: earlysuspend: Add console switch when user requested sleep state changes Pavel Machek
2009-02-05  9:37                       ` Arve Hjønnevåg
2009-02-05  9:51                         ` Pavel Machek
2009-02-05 10:54                         ` Uli Luckas
2009-02-06  2:29                       ` Arve Hjønnevåg
2009-02-08 22:02                         ` Pavel Machek
2009-02-08 22:53                           ` Arve Hjønnevåg
2009-02-08 22:58                             ` Pavel Machek
2009-02-05  8:55                   ` [PATCH 09/13] PM: wakelock: Abort task freezing if a wake lock is held Pavel Machek
2009-02-05  9:30                     ` Arve Hjønnevåg
2009-02-05  9:49                       ` Pavel Machek
2009-02-05  9:58                         ` Arve Hjønnevåg
2009-02-05 10:02                           ` Pavel Machek
2009-02-05 10:08                             ` Arve Hjønnevåg
2009-02-06  3:42                             ` Arve Hjønnevåg
2009-02-08 23:00                               ` Pavel Machek
2009-02-06  0:35                   ` mark gross
2009-02-05  8:53                 ` [PATCH 08/13] PM: Add user-space wake lock api Pavel Machek
2009-02-05  8:52               ` [PATCH 07/13] PM: wakelock: Add /sys/power/request_state Pavel Machek
2009-02-05  9:25                 ` Arve Hjønnevåg
2009-02-05  9:27                   ` Pavel Machek
2009-02-07 22:54               ` Rafael J. Wysocki
2009-02-06  0:18             ` [PATCH 06/13] PM: Implement early suspend api mark gross
2009-02-07 22:47             ` Rafael J. Wysocki
2009-02-08  2:32               ` Benjamin Herrenschmidt
2009-02-08 13:33                 ` Rafael J. Wysocki
2009-02-05  9:17           ` [PATCH 05/13] PM: Add option to disable /sys/power/state interface Pavel Machek
2009-02-07 22:37           ` Rafael J. Wysocki
2009-02-08 10:33             ` Pavel Machek
2009-02-08 13:50               ` Rafael J. Wysocki
2009-02-08 14:04                 ` Brian Swetland
2009-02-08 21:06                   ` Pavel Machek
2009-02-08 23:41                     ` Rafael J. Wysocki
2009-02-09  1:58                       ` Uli Luckas
2009-02-10  0:09                         ` Rafael J. Wysocki
2009-02-08 23:40                   ` Rafael J. Wysocki
2009-02-08 23:58                     ` Arve Hjønnevåg
2009-02-09  0:26                       ` Rafael J. Wysocki
2009-02-09  1:35                         ` Arve Hjønnevåg
2009-02-09  1:53                           ` Brian Swetland
2009-02-09  8:58                             ` Pavel Machek
2009-02-09 13:31                               ` Brian Swetland
2009-02-10 11:19                                 ` Pavel Machek
2009-02-09  9:15                           ` Pavel Machek
2009-02-09  3:07                       ` Alan Stern
2009-02-11 22:26                         ` Rafael J. Wysocki
2009-02-09  9:09                       ` Pavel Machek
2009-02-12 11:16                   ` Matthew Garrett
2009-02-08 21:04                 ` Pavel Machek
2009-02-08 21:40                   ` Alan Stern
2009-02-08 23:00                     ` Arve Hjønnevåg
2009-02-08 23:03                       ` Pavel Machek
2009-02-09  0:31                         ` Rafael J. Wysocki
2009-02-09  2:11                           ` Uli Luckas
2009-02-09  2:24                             ` Arve Hjønnevåg
2009-02-09  2:56                               ` Uli Luckas
2009-02-09  9:01                           ` Pavel Machek
2009-02-10  0:17                             ` Rafael J. Wysocki
2009-02-10  9:13                               ` Pavel Machek
2009-02-10 14:18                                 ` Rafael J. Wysocki
2009-02-08 23:41                                   ` Pavel Machek
2009-02-08 23:44                     ` Rafael J. Wysocki
2009-02-08 23:44                   ` Rafael J. Wysocki
2009-02-07 22:31         ` [PATCH 04/13] PM: wakelock: Override wakelocks when using /sys/power/state Rafael J. Wysocki
2009-02-05  9:16       ` [PATCH 03/13] PM: Implement wakelock api Pavel Machek
2009-02-05 15:24       ` Alan Stern
2009-02-06  0:10       ` mark gross
2009-02-06  0:38         ` Arve Hjønnevåg
2009-02-07  0:33           ` mark gross
2009-02-07  0:47             ` Arve Hjønnevåg
2009-02-09 18:00               ` mark gross
2009-02-10 20:24               ` Pavel Machek
2009-02-07 22:27       ` Rafael J. Wysocki [this message]
2009-02-11  2:52         ` Arve Hjønnevåg
2009-02-05  9:14     ` [PATCH 02/13] PM: Add early suspend api Pavel Machek
2009-02-05 23:26     ` mark gross
2009-02-06  9:33       ` Uli Luckas
2009-02-06 23:26         ` Arve Hjønnevåg
2009-02-07 20:53     ` Rafael J. Wysocki
2009-02-07 23:34       ` Arve Hjønnevåg
2009-02-08 20:59         ` Pavel Machek
2009-02-08 23:59         ` Rafael J. Wysocki
2009-02-05  9:11   ` [PATCH 01/13] PM: Add wake lock api Pavel Machek
2009-02-06  0:28     ` Arve Hjønnevåg
2009-02-06  9:45       ` Uli Luckas
2009-02-08 21:30       ` Pavel Machek
2009-02-08 23:11         ` Arve Hjønnevåg
2009-02-09  9:06           ` Pavel Machek
2009-02-08 22:17       ` non-racy examples, please (was Re: [PATCH 01/13] PM: Add wake lock api.) Pavel Machek
2009-02-08 22:40         ` Arve Hjønnevåg
2009-02-08 23:14           ` Pavel Machek
2009-02-08 23:35             ` Arve Hjønnevåg
2009-02-10 11:15               ` Pavel Machek
2009-02-11  3:12                 ` Arve Hjønnevåg
2009-02-09  1:49         ` non-racy examples, please (was Re: [PATCH 01/13] PM: Add wake lock api. ) Uli Luckas
2009-02-10 11:17           ` non-racy examples, please (was Re: [PATCH 01/13] PM: Add wake lock?api.) Pavel Machek
2009-02-10 12:10             ` Woodruff, Richard
2009-02-10 13:14               ` Pavel Machek
2009-02-10 13:20                 ` Woodruff, Richard
2009-02-10 13:42                   ` Brian Swetland
2009-02-10 12:35             ` Uli Luckas
2009-02-06  1:32     ` [PATCH 01/13] PM: Add wake lock api mark gross
2009-02-05 22:51   ` mark gross
2009-02-06  0:13     ` Arve Hjønnevåg
2009-02-10 20:25       ` Pavel Machek
2009-02-11  2:11         ` Arve Hjønnevåg
2009-02-11  4:47         ` Brian Swetland
2009-02-11  8:40           ` Uli Luckas
2009-02-11 14:58           ` Alan Stern
2009-02-11 15:45             ` Rafael J. Wysocki
2009-02-08 22:57               ` Pavel Machek
2009-02-11 21:37             ` Pavel Machek
2009-02-11 22:05               ` Alan Stern
2009-02-11 23:55               ` Arve Hjønnevåg
2009-02-12 18:47           ` mark gross
2009-02-07 18:56   ` Rafael J. Wysocki
2009-02-07 22:51     ` Arve Hjønnevåg
2009-02-07 23:25       ` Rafael J. Wysocki
2009-02-08  0:20         ` Arve Hjønnevåg
2009-02-08 21:21           ` Pavel Machek
2009-02-09  0:03             ` Rafael J. Wysocki
2009-02-09  0:15           ` Rafael J. Wysocki
2009-02-09  2:03             ` Arve Hjønnevåg
2009-02-11 22:23               ` Rafael J. Wysocki
2009-02-11 23:42                 ` Arve Hjønnevåg
2009-02-12 22:22                   ` Rafael J. Wysocki
2009-02-12 23:42                     ` Woodruff, Richard
2009-02-13  1:10                       ` Matthew Garrett
2009-02-13  2:21                         ` Arve Hjønnevåg
2009-02-13  2:40                           ` Nigel Cunningham
2009-02-13  3:17                         ` Woodruff, Richard
2009-02-13 10:55                         ` Uli Luckas
2009-02-13 14:06                           ` Matthew Garrett
2009-02-13 14:24                             ` Brian Swetland
2009-02-13 14:37                               ` Matthew Garrett
2009-02-13 14:46                                 ` Brian Swetland
2009-02-13 15:07                                   ` Matthew Garrett
2009-02-13 22:52                                     ` Rafael J. Wysocki
2009-02-13 16:46                                 ` Uli Luckas
2009-02-13 17:05                                   ` Matthew Garrett
2009-02-13 18:13                                     ` Uli Luckas
2009-02-13 19:14                                       ` Matthew Garrett
2009-02-13 19:35                                         ` Uli Luckas
2009-02-13 16:49                             ` Uli Luckas
2009-02-13 17:09                               ` Matthew Garrett
2009-02-13 18:18                                 ` Uli Luckas
2009-02-27 13:18                               ` Pavel Machek
2009-02-27 14:07                                 ` Uli Luckas
2009-02-27 20:32                                   ` Pavel Machek
2009-03-02 13:53                                     ` Uli Luckas
2009-03-03 14:02                                       ` Pavel Machek
2009-03-04 13:41                                         ` Uli Luckas
2009-03-04 14:00                                         ` Uli Luckas
2009-03-04 14:13                                           ` Pavel Machek
2009-03-04 14:34                                             ` Uli Luckas
2009-03-04 17:10                                               ` Pavel Machek
2009-03-05 17:42                                                 ` Uli Luckas
2009-03-08  8:32                                                   ` Pavel Machek
2009-03-08 12:34                                                     ` Alan Stern
2009-02-13 23:36                             ` Arve Hjønnevåg
2009-02-14  0:05                               ` Matthew Garrett
2009-02-14  0:50                                 ` Arve Hjønnevåg
2009-02-14  1:06                                   ` Matthew Garrett
2009-02-14  1:33                                     ` Arve Hjønnevåg
2009-02-14  1:49                                       ` Matthew Garrett
2009-02-14  5:51                                         ` Arve Hjønnevåg
2009-02-14 20:44                                           ` Matthew Garrett
2009-02-26 15:04                         ` Pavel Machek
2009-02-26 21:11                           ` Arve Hjønnevåg
2009-02-26 21:36                             ` Pavel Machek
2009-02-27  0:16                               ` Arve Hjønnevåg
2009-02-27  9:56                                 ` Pavel Machek
2009-02-28  3:20                                   ` Arve Hjønnevåg
2009-02-06 23:51 ` [RFC][PATCH 00/11] Android PM extensions Rafael J. Wysocki

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=200902072327.11251.rjw@sisk.pl \
    --to=rjw@sisk.pl \
    --cc=arve@android.com \
    --cc=linux-pm@lists.linux-foundation.org \
    --cc=ncunningham@crca.org.au \
    --cc=swetland@google.com \
    --cc=u.luckas@road.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.