timer.c
static void do_init_timer(struct timer_list *timer,
void (*func)(struct timer_list *),
unsigned int flags,
const char *name, struct lock_class_key *key)
{
timer->entry.pprev = NULL;
timer->function = func;
timer->flags = flags | raw_smp_processor_id();
lockdep_init_map(&timer->lockdep_map, name, key, 0);
}
/**
sync lock dependencies
timer.h
#define __init_timer(_timer, _fn, _flags)
do {
static struct lock_class_key __key;
init_timer_key((_timer), (_fn), (_flags), #_timer, &__key);
} while (0)
/**
wake_stats.c
/**
wakeup_source_sysfs_add - Add wakeup_source attributes to sysfs.
@parent: Device given wakeup source is associated with (or NULL if virtual).
@ws: Wakeup source to be added in sysfs.
*/
int wakeup_source_sysfs_add(struct device *parent, struct wakeup_source *ws)
{
struct device *dev;
dev = wakeup_source_device_create(parent, ws);
if (IS_ERR(dev))
return PTR_ERR(dev);
ws->dev = dev;
return 0;
}
wakeup.c
/**
wakeup_source_create - Create a struct wakeup_source object.
@name: Name of the new wakeup source.
*/
struct wakeup_source *wakeup_source_create(const char *name)
{
struct wakeup_source *ws;
const char *ws_name;
int id;
ws = kzalloc(sizeof(*ws), GFP_KERNEL);
if (!ws)
goto err_ws;
ws_name = kstrdup_const(name, GFP_KERNEL);
if (!ws_name)
goto err_name;
ws->name = ws_name;
id = ida_alloc(&wakeup_ida, GFP_KERNEL);
if (id < 0)
goto err_id;
ws->id = id;
return ws;
err_id:
kfree_const(ws->name);
err_name:
kfree(ws);
err_ws:
return NULL;
}
EXPORT_SYMBOL_GPL(wakeup_source_create);
/**
wakup_source_deactivate - Mark given wakeup source as inactive.
@ws: Wakeup source to handle.
Update the @ws’ statistics and notify the PM core that the wakeup source has
become inactive by decrementing the counter of wakeup events being processed
and incrementing the counter of registered wakeup events.
*/
static void wakeup_source_deactivate(struct wakeup_source *ws)
{
unsigned int cnt, inpr, cec;
ktime_t duration;
ktime_t now;
ws->relax_count++;
/*
ws->active = false;
now = ktime_get();
duration = ktime_sub(now, ws->last_time);
ws->total_time = ktime_add(ws->total_time, duration);
if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time))
ws->max_time = duration;
ws->last_time = now;
del_timer(&ws->timer);
ws->timer_expires = 0;
if (ws->autosleep_enabled)
update_prevent_sleep_time(ws, now);
/*
split_counters(&cnt, &inpr);
if (!inpr && waitqueue_active(&wakeup_count_wait_queue)) //如果wait_queue中还有等待任务,则继续处理
wake_up(&wakeup_count_wait_queue);
}
/**
pm_wakeup_timer_fn - Delayed finalization of a wakeup event.
@data: Address of the wakeup source object associated with the event source.
Call wakeup_source_deactivate() for the wakeup source whose address is stored
in @data if it is currently active and its timer has not been canceled and
the expiration time of the timer is not in future.
*/
static void pm_wakeup_timer_fn(struct timer_list *t)
{
struct wakeup_source *ws = from_timer(ws, t, timer);
unsigned long flags;
spin_lock_irqsave(&ws->lock, flags);
if (ws->active && ws->timer_expires
&& time_after_eq(jiffies, ws->timer_expires)) {
wakeup_source_deactivate(ws); //Mark given wakeup source as inactive.
ws->expire_count++;
}
spin_unlock_irqrestore(&ws->lock, flags);
}
/**
wakeup_source_add - Add given object to the list of wakeup sources.
@ws: Wakeup source object to add to the list.
*/
void wakeup_source_add(struct wakeup_source *ws)
{
unsigned long flags;
if (WARN_ON(!ws))
return;
spin_lock_init(&ws->lock);
timer_setup(&ws->timer, pm_wakeup_timer_fn, 0); //初始化timer_list, 特别是指定callback函数:pm_wakeup_timer_fn
ws->active = false;
raw_spin_lock_irqsave(&events_lock, flags);
list_add_rcu(&ws->entry, &wakeup_sources); //将ws加入到wakeup_sources全局链表中
raw_spin_unlock_irqrestore(&events_lock, flags);
}
EXPORT_SYMBOL_GPL(wakeup_source_add);
1:wakeup_source的动态注册
/**
wakeup_source_register - Create wakeup source and add it to the list.
@dev: Device this wakeup source is associated with (or NULL if virtual).
@name: Name of the wakeup source to register.
*/
struct wakeup_source *wakeup_source_register(struct device *dev,
const char *name)
{
struct wakeup_source *ws;
int ret;
ws = wakeup_source_create(name); //分配结构体内存及全局ID
if (ws) {
if (!dev || device_is_registered(dev)) { //如果ws所依附的dev为NULL或者已经在sysfs中注册
ret = wakeup_source_sysfs_add(dev, ws); //创建ws为dev子设备并添加到sys/devices/ 文件系统中
if (ret) {
wakeup_source_free(ws);
return NULL;
}
}
wakeup_source_add(ws); //将ws加入到wakeup_source全局链表中
}
return ws;
}
EXPORT_SYMBOL_GPL(wakeup_source_register);
2:wakeup_source的激活
/*
/**
wakup_source_activate - Mark given wakeup source as active.
@ws: Wakeup source to handle.
Update the @ws’ statistics and, if @ws has just been activated, notify the PM
core of the event by incrementing the counter of of wakeup events being
processed.
*/
static void wakeup_source_activate(struct wakeup_source *ws)
{
unsigned int cec;
if (WARN_ONCE(wakeup_source_not_registered(ws),
“unregistered wakeup source\n”))
return;
ws->active = true;
ws->active_count++;
ws->last_time = ktime_get();
if (ws->autosleep_enabled)
ws->start_prevent_time = ws->last_time;
/* Increment the counter of events in progress. */
cec = atomic_inc_return(&combined_event_count);
trace_wakeup_source_activate(ws->name, cec);
}
/**
wakeup_source_report_event - Report wakeup event using the given source.
@ws: Wakeup source to report the event for.
@hard: If set, abort suspends in progress and wake up from suspend-to-idle.
*/
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();
}
/**
__pm_stay_awake - Notify the PM core of a wakeup event.
@ws: Wakeup source object associated with the source of the event.
It is safe to call this function from interrupt context.
*/
void __pm_stay_awake(struct wakeup_source *ws)
{
unsigned long flags;
if (!ws)
return;
spin_lock_irqsave(&ws->lock, flags);
wakeup_source_report_event(ws, false);
del_timer(&ws->timer);
ws->timer_expires = 0;
spin_unlock_irqrestore(&ws->lock, flags);
}
EXPORT_SYMBOL_GPL(__pm_stay_awake);
注:
1:还有一种情况,如果控制wakeup_source的线程跟任务处理线程不同,且不清楚具体处理情况,
可通过设置倒计时timer解决,api如下:pm_wakeup_event()
如:__pm_wakeup_event(&info->wakeup_source, 5000);
2:
wakeup.c
/**
pm_wakeup_ws_event - Notify the PM core of a wakeup event.
@ws: Wakeup source object associated with the event source.
@msec: Anticipated event processing time (in milliseconds).
@hard: If set, abort suspends in progress and wake up from suspend-to-idle.
Notify the PM core of a wakeup event whose source is @ws that will take
approximately @msec milliseconds to be processed by the kernel. If @ws is
not active, activate it. If @msec is nonzero, set up the @ws’ timer to
execute pm_wakeup_timer_fn() in future.
It is safe to call this function from interrupt context.
*/
void pm_wakeup_ws_event(struct wakeup_source *ws, unsigned int msec, bool hard)
{
unsigned long flags;
unsigned long expires;
if (!ws)
return;
spin_lock_irqsave(&ws->lock, flags);
wakeup_source_report_event(ws, hard);
if (!msec) {
wakeup_source_deactivate(ws);
goto unlock;
}
expires = jiffies + msecs_to_jiffies(msec);
if (!expires)
expires = 1;
if (!ws->timer_expires || time_after(expires, ws->timer_expires)) {
mod_timer(&ws->timer, expires);
ws->timer_expires = expires;
}
unlock:
spin_unlock_irqrestore(&ws->lock, flags);
}
EXPORT_SYMBOL_GPL(pm_wakeup_ws_event);
pm_wakeup.h
static inline void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec)
{
return pm_wakeup_ws_event(ws, msec, false);
}
3: wakeup_source deactive
/**
__pm_relax - Notify the PM core that processing of a wakeup event has ended.
@ws: Wakeup source object associated with the source of the event.
Call this function for wakeup events whose processing started with calling
__pm_stay_awake().
It is safe to call it from interrupt context.
*/
void __pm_relax(struct wakeup_source *ws)
{
unsigned long flags;
if (!ws)
return;
spin_lock_irqsave(&ws->lock, flags);
if (ws->active)
wakeup_source_deactivate(ws);
spin_unlock_irqrestore(&ws->lock, flags);
}
EXPORT_SYMBOL_GPL(__pm_relax);