Linux内核--wakeup_source机制流程简介

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);
}

/**

  • init_timer_key - initialize a timer
  • @timer: the timer to be initialized
  • @func: timer callback function
  • @flags: timer flags
  • @name: name of the timer
  • @key: lockdep class key of the fake lock used for tracking timer
  •   sync lock dependencies
    
  • init_timer_key() must be done to a timer prior calling any of the
  • other timer functions.
    */
    void init_timer_key(struct timer_list *timer,
    void (*func)(struct timer_list *), unsigned int flags,
    const char *name, struct lock_class_key *key)
    {
    debug_init(timer);
    do_init_timer(timer, func, flags, name, key);
    }
    EXPORT_SYMBOL(init_timer_key);

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)

/**

  • timer_setup - prepare a timer for first use
  • @timer: the timer in question
  • @callback: the function to call when timer expires
  • @flags: any TIMER_* flags
  • Regular timer initialization should use either DEFINE_TIMER() above,
  • or timer_setup(). For timers on the stack, timer_setup_on_stack() must
  • be used and must be balanced with a call to destroy_timer_on_stack().
    */
    #define timer_setup(timer, callback, flags)
    __init_timer((timer), (callback), (flags))

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++;
    /*

    • __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;

    ws->last_time = now;
    del_timer(&ws->timer);
    ws->timer_expires = 0;

    if (ws->autosleep_enabled)
    update_prevent_sleep_time(ws, now);

    /*

    • Increment the counter of registered wakeup events and decrement the
    • couter 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)) //如果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的激活

/*

  • 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.
    */

/**

  • 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:

  • 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.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);

你可能感兴趣的:(linux内核)