#define list_for_each_entry_rcu_locked(pos, head, member) \
list_for_each_entry_rcu(pos, head, member, \
srcu_read_lock_held(&wakeup_srcu)) /* * If set, the suspend/hibernate code will abort transitions to a sleep state * if wakeup events are registered during or immediately before the transition.
*/ bool events_check_enabled __read_mostly;
/* First wakeup IRQ seen by the kernel in the last cycle. */ staticunsignedint wakeup_irq[2] __read_mostly; static DEFINE_RAW_SPINLOCK(wakeup_irq_lock);
/* If greater than 0 and the system is suspending, terminate the suspend. */ static atomic_t pm_abort_suspend __read_mostly;
/* * Combined counters of registered wakeup events and wakeup events in progress. * They need to be modified together atomically, so it's better to use one * atomic variable to hold them both.
*/ static atomic_t combined_event_count = ATOMIC_INIT(0);
/** * wakeup_source_create - Create a struct wakeup_source object. * @name: Name of the new wakeup source.
*/ staticstruct wakeup_source *wakeup_source_create(constchar *name)
{ struct wakeup_source *ws; constchar *ws_name; int id;
ws = kzalloc(sizeof(*ws), GFP_KERNEL); if (!ws) goto err_ws;
/* * Record wakeup_source statistics being deleted into a dummy wakeup_source.
*/ staticvoid wakeup_source_record(struct wakeup_source *ws)
{ unsignedlong flags;
/** * wakeup_source_destroy - Destroy a struct wakeup_source object. * @ws: Wakeup source to destroy. * * Use only for wakeup source objects created with wakeup_source_create().
*/ staticvoid wakeup_source_destroy(struct wakeup_source *ws)
{ if (!ws) return;
/** * wakeup_source_add - Add given object to the list of wakeup sources. * @ws: Wakeup source object to add to the list.
*/ staticvoid wakeup_source_add(struct wakeup_source *ws)
{ unsignedlong flags;
/** * wakeup_source_remove - Remove given object from the wakeup sources list. * @ws: Wakeup source object to remove from the list.
*/ staticvoid wakeup_source_remove(struct wakeup_source *ws)
{ unsignedlong flags;
timer_delete_sync(&ws->timer); /* * Clear timer.function to make wakeup_source_not_registered() treat * this wakeup source as not registered.
*/
ws->timer.function = NULL;
}
/** * 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, constchar *name)
{ struct wakeup_source *ws; int ret;
ws = wakeup_source_create(name); if (ws) { if (!dev || device_is_registered(dev)) {
ret = wakeup_source_sysfs_add(dev, ws); if (ret) {
wakeup_source_free(ws); return NULL;
}
}
wakeup_source_add(ws);
} return ws;
}
EXPORT_SYMBOL_GPL(wakeup_source_register);
/** * wakeup_source_unregister - Remove wakeup source from the list and remove it. * @ws: Wakeup source object to unregister.
*/ void wakeup_source_unregister(struct wakeup_source *ws)
{ if (ws) {
wakeup_source_remove(ws); if (ws->dev)
wakeup_source_sysfs_remove(ws);
/** * wakeup_sources_read_lock - Lock wakeup source list for read. * * Returns an index of srcu lock for struct wakeup_srcu. * This index must be passed to the matching wakeup_sources_read_unlock().
*/ int wakeup_sources_read_lock(void)
{ return srcu_read_lock(&wakeup_srcu);
}
EXPORT_SYMBOL_GPL(wakeup_sources_read_lock);
/** * wakeup_sources_walk_start - Begin a walk on wakeup source list * * Returns first object of the list of wakeup sources. * * Note that to be safe, wakeup sources list needs to be locked by calling * wakeup_source_read_lock() for this.
*/ struct wakeup_source *wakeup_sources_walk_start(void)
{ struct list_head *ws_head = &wakeup_sources;
/** * wakeup_sources_walk_next - Get next wakeup source from the list * @ws: Previous wakeup source object * * Note that to be safe, wakeup sources list needs to be locked by calling * wakeup_source_read_lock() for this.
*/ struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws)
{ struct list_head *ws_head = &wakeup_sources;
/** * device_wakeup_attach - Attach a wakeup source object to a device object. * @dev: Device to handle. * @ws: Wakeup source object to attach to @dev. * * This causes @dev to be treated as a wakeup device.
*/ staticint device_wakeup_attach(struct device *dev, struct wakeup_source *ws)
{
spin_lock_irq(&dev->power.lock); if (dev->power.wakeup) {
spin_unlock_irq(&dev->power.lock); return -EEXIST;
}
dev->power.wakeup = ws; if (dev->power.wakeirq)
device_wakeup_attach_irq(dev, dev->power.wakeirq);
spin_unlock_irq(&dev->power.lock); return 0;
}
/** * device_wakeup_enable - Enable given device to be a wakeup source. * @dev: Device to handle. * * Create a wakeup source object, register it and attach it to @dev.
*/ int device_wakeup_enable(struct device *dev)
{ struct wakeup_source *ws; int ret;
if (!dev || !dev->power.can_wakeup) return -EINVAL;
if (pm_sleep_transition_in_progress())
dev_dbg(dev, "Suspicious %s() during system transition!\n", __func__);
ws = wakeup_source_register(dev, dev_name(dev)); if (!ws) return -ENOMEM;
ret = device_wakeup_attach(dev, ws); if (ret)
wakeup_source_unregister(ws);
/** * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source * @dev: Device to handle * @wakeirq: Device specific wakeirq entry * * Attach a device wakeirq to the wakeup source so the device * wake IRQ can be configured automatically for suspend and * resume. * * Call under the device's power.lock lock.
*/ void device_wakeup_attach_irq(struct device *dev, struct wake_irq *wakeirq)
{ struct wakeup_source *ws;
ws = dev->power.wakeup; if (!ws) return;
if (ws->wakeirq)
dev_err(dev, "Leftover wakeup IRQ found, overriding\n");
ws->wakeirq = wakeirq;
}
/** * device_wakeup_detach_irq - Detach a wakeirq from a wakeup source * @dev: Device to handle * * Removes a device wakeirq from the wakeup source. * * Call under the device's power.lock lock.
*/ void device_wakeup_detach_irq(struct device *dev)
{ struct wakeup_source *ws;
ws = dev->power.wakeup; if (ws)
ws->wakeirq = NULL;
}
/** * device_wakeup_arm_wake_irqs - * * Iterates over the list of device wakeirqs to arm them.
*/ void device_wakeup_arm_wake_irqs(void)
{ struct wakeup_source *ws; int srcuidx;
/** * device_wakeup_disarm_wake_irqs - * * Iterates over the list of device wakeirqs to disarm them.
*/ void device_wakeup_disarm_wake_irqs(void)
{ struct wakeup_source *ws; int srcuidx;
/** * device_wakeup_detach - Detach a device's wakeup source object from it. * @dev: Device to detach the wakeup source object from. * * After it returns, @dev will not be treated as a wakeup device any more.
*/ staticstruct wakeup_source *device_wakeup_detach(struct device *dev)
{ struct wakeup_source *ws;
/** * device_wakeup_disable - Do not regard a device as a wakeup source any more. * @dev: Device to handle. * * Detach the @dev's wakeup source object from it, unregister this wakeup source * object and destroy it.
*/ void device_wakeup_disable(struct device *dev)
{ struct wakeup_source *ws;
/** * device_set_wakeup_capable - Set/reset device wakeup capability flag. * @dev: Device to handle. * @capable: Whether or not @dev is capable of waking up the system from sleep. * * If @capable is set, set the @dev's power.can_wakeup flag and add its * wakeup-related attributes to sysfs. Otherwise, unset the @dev's * power.can_wakeup flag and remove its wakeup-related attributes from sysfs. * * This function may sleep and it can't be called from any context where * sleeping is not allowed.
*/ void device_set_wakeup_capable(struct device *dev, bool capable)
{ if (!!dev->power.can_wakeup == !!capable) return;
dev->power.can_wakeup = capable; if (device_is_registered(dev) && !list_empty(&dev->power.entry)) { if (capable) { int ret = wakeup_sysfs_add(dev);
if (ret)
dev_info(dev, "Wakeup sysfs attributes not added\n");
} else {
wakeup_sysfs_remove(dev);
}
}
}
EXPORT_SYMBOL_GPL(device_set_wakeup_capable);
/** * device_set_wakeup_enable - Enable or disable a device to wake up the system. * @dev: Device to handle. * @enable: enable/disable flag
*/ int device_set_wakeup_enable(struct device *dev, bool enable)
{ if (enable) return device_wakeup_enable(dev);
/** * wakeup_source_not_registered - validate the given wakeup source. * @ws: Wakeup source to be validated.
*/ staticbool wakeup_source_not_registered(struct wakeup_source *ws)
{ /* * Use timer struct to check if the given source is initialized * by wakeup_source_add.
*/ return ws->timer.function != pm_wakeup_timer_fn;
}
/* * The functions below use the observation that each wakeup event starts a * period in which the system should not be suspended. The moment this period * will end depends on how the wakeup event is going to be processed after being * detected and all of the possible cases can be divided into two distinct * groups. * * First, a wakeup event may be detected by the same functional unit that will * carry out the entire processing of it and possibly will pass it to user space * for further processing. In that case the functional unit that has detected * the event may later "close" the "no suspend" period associated with it * directly as soon as it has been dealt with. The pair of pm_stay_awake() and * pm_relax(), balanced with each other, is supposed to be used in such * situations. * * Second, a wakeup event may be detected by one functional unit and processed * by another one. In that case the unit that has detected it cannot really * "close" the "no suspend" period associated with it, unless it knows in * advance what's going to happen to the event during processing. This * knowledge, however, may not be available to it, so it can simply specify time * to wait before the system can be suspended and pass it as the second * argument of pm_wakeup_event(). * * It is valid to call pm_relax() after pm_wakeup_event(), in which case the * "no suspend" period will be ended either by the pm_relax(), or by the timer * function executed when the timer expires, whichever comes first.
*/
/** * wakeup_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 the wakeup events being * processed.
*/ staticvoid wakeup_source_activate(struct wakeup_source *ws)
{ unsignedint cec;
if (WARN_ONCE(wakeup_source_not_registered(ws), "unregistered wakeup source\n")) return;
/* 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.
*/ staticvoid 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)
{ unsignedlong flags;
/** * pm_stay_awake - Notify the PM core that a wakeup event is being processed. * @dev: Device the wakeup event is related to. * * Notify the PM core of a wakeup event (signaled by @dev) by calling * __pm_stay_awake for the @dev's wakeup source object. * * Call this function after detecting of a wakeup event if pm_relax() is going * to be called directly after processing the event (and possibly passing it to * user space for further processing).
*/ void pm_stay_awake(struct device *dev)
{ unsignedlong flags;
/** * wakeup_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.
*/ staticvoid wakeup_source_deactivate(struct wakeup_source *ws)
{ unsignedint cnt, inpr, cec;
ktime_t duration;
ktime_t now;
ws->relax_count++; /* * __pm_relax() may be called directly or from a timer function. * If it is called directly right after the timer function has been * started, but before the timer function calls __pm_relax(), it is * possible that __pm_stay_awake() will be called in the meantime and * will set ws->active. Then, ws->active may be cleared immediately * by the __pm_relax() called from the timer function, but in such a * case ws->relax_count will be different from ws->active_count.
*/ if (ws->relax_count != ws->active_count) {
ws->relax_count--; return;
}
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;
if (ws->autosleep_enabled)
update_prevent_sleep_time(ws, now);
/* * Increment the counter of registered wakeup events and decrement the * counter of wakeup events in progress simultaneously.
*/
cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count);
trace_wakeup_source_deactivate(ws->name, cec);
split_counters(&cnt, &inpr); if (!inpr && waitqueue_active(&wakeup_count_wait_queue))
wake_up(&wakeup_count_wait_queue);
}
/** * __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)
{ unsignedlong 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);
/** * pm_relax - Notify the PM core that processing of a wakeup event has ended. * @dev: Device that signaled the event. * * Execute __pm_relax() for the @dev's wakeup source object.
*/ void pm_relax(struct device *dev)
{ unsignedlong flags;
/** * pm_wakeup_timer_fn - Delayed finalization of a wakeup event. * @t: timer list * * 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.
*/ staticvoid pm_wakeup_timer_fn(struct timer_list *t)
{ struct wakeup_source *ws = timer_container_of(ws, t, timer); unsignedlong flags;
/** * 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, unsignedint msec, bool hard)
{ unsignedlong flags; unsignedlong 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;
/** * pm_wakeup_dev_event - Notify the PM core of a wakeup event. * @dev: Device the wakeup event is related to. * @msec: Anticipated event processing time (in milliseconds). * @hard: If set, abort suspends in progress and wake up from suspend-to-idle. * * Call pm_wakeup_ws_event() for the @dev's wakeup source object.
*/ void pm_wakeup_dev_event(struct device *dev, unsignedint msec, bool hard)
{ unsignedlong flags;
if (!active && last_activity_ws)
pm_pr_dbg("last active wakeup source: %s\n",
last_activity_ws->name);
srcu_read_unlock(&wakeup_srcu, srcuidx);
}
EXPORT_SYMBOL_GPL(pm_print_active_wakeup_sources);
/** * pm_wakeup_pending - Check if power transition in progress should be aborted. * * Compare the current number of registered wakeup events with its preserved * value from the past and return true if new wakeup events have been registered * since the old value was stored. Also return true if the current number of * wakeup events being processed is different from zero.
*/ bool pm_wakeup_pending(void)
{ unsignedlong flags; bool ret = false;
raw_spin_lock_irqsave(&events_lock, flags); if (events_check_enabled) { unsignedint cnt, inpr;
/** * pm_get_wakeup_count - Read the number of registered wakeup events. * @count: Address to store the value at. * @block: Whether or not to block. * * Store the number of registered wakeup events at the address in @count. If * @block is set, block until the current number of wakeup events being * processed is zero. * * Return 'false' if the current number of wakeup events being processed is * nonzero. Otherwise return 'true'.
*/ bool pm_get_wakeup_count(unsignedint *count, bool block)
{ unsignedint cnt, inpr;
/** * pm_save_wakeup_count - Save the current number of registered wakeup events. * @count: Value to compare with the current number of registered wakeup events. * * If @count is equal to the current number of registered wakeup events and the * current number of wakeup events being processed is zero, store @count as the * old number of registered wakeup events for pm_check_wakeup_events(), enable * wakeup events detection and return 'true'. Otherwise disable wakeup events * detection and return 'false'.
*/ bool pm_save_wakeup_count(unsignedint count)
{ unsignedint cnt, inpr; unsignedlong flags;
#ifdef CONFIG_PM_AUTOSLEEP /** * pm_wakep_autosleep_enabled - Modify autosleep_enabled for all wakeup sources. * @set: Whether to set or to clear the autosleep_enabled flags.
*/ void pm_wakep_autosleep_enabled(bool set)
{ struct wakeup_source *ws;
ktime_t now = ktime_get(); int srcuidx;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.