Linux電源管理-autosleep

5691 ワード

前言


Autosleepとは?文字通り「用事がないときに寝る」です.最初はAutosleepはAndroid上のpatch(https://lwn.net/Articles/479711/)進化したもので、当時の名前は「Opportunistic sleep」、翻訳すると「機会主義睡眠」と呼ばれ、つまり機会があれば寝る.この名前は論争があったため、autosleepに変更された.
Autosleepは強力な電源管理方法で、システムに何もできないときにシステム全体が睡眠をとることができます.このメカニズムはandroid携帯電話で非常に有効であり、不良アプリケーションがシステムの起動を維持し、バッテリーを浪費することを阻止することができます.しかし重要なのは
現在のシステムに何もできないことをどのように判断しますか?つまり、システムがアイドル状態にあることをどのように判断しますか? 
このときwakeup event frameworkの出現でこの問題を解決することができ,システムにwakeup eventイベントがない場合,システムsuspendを試みる.suspendの過程でwakeup eventがあればresumeシステムに続くとよい.
同じだcの注釈はautosleepの前生がOpportunistic sleepであることを示すことができる.
/*
 * kernel/power/autosleep.c
 *
 * Opportunistic sleep support.
 *
 * Copyright (C) 2012 Rafael J. Wysocki 
 */

詳細は次のとおりです.https://lwn.net/Articles/479841/

Autosleepの原理


1.システムに何もしていないときはsusupendを試します.
2.システムにwakeup eventイベントが発生していない場合、suspendを試してみることができ、wakeup event frameworkメカニズムのサポートが必要です.
3.autosleep機能はkernel configでCONFIG_を開く必要があるPM_AUTOSLEEP=y.
4.「mem,disk,standby,freeze」を/sys/power/autosleepに書くことでautosleepを開くことができます.
5.「off」を/sys/power/autosleepに書くことでautosleepを閉じることができます.

プロセス分析


A:echo mem>/sys/power/autosleepというコマンドを実行すると、やることがないときにsuspendのプロセスを実行することを選択します.
static ssize_t autosleep_store(struct kobject *kobj,
			       struct kobj_attribute *attr,
			       const char *buf, size_t n)
{
	suspend_state_t state = decode_state(buf, n);
	int error;

	if (state == PM_SUSPEND_ON
	    && strcmp(buf, "off") && strcmp(buf, "off
")) return -EINVAL; error = pm_autosleep_set_state(state); return error ? error : n; }
現在のstateの状態を判断することによりoffであればそのまま戻り、そうでなければpm_を呼び出すautosleep_set_state関数.
int pm_autosleep_set_state(suspend_state_t state)
{

#ifndef CONFIG_HIBERNATION
	if (state >= PM_SUSPEND_MAX)
		return -EINVAL;
#endif

	__pm_stay_awake(autosleep_ws);

	mutex_lock(&autosleep_lock);

	autosleep_state = state;

	__pm_relax(autosleep_ws);

	if (state > PM_SUSPEND_ON) {
		pm_wakep_autosleep_enabled(true);
		queue_up_suspend_work();
	} else {
		pm_wakep_autosleep_enabled(false);
	}

	mutex_unlock(&autosleep_lock);
	return 0;
}
1. stateパラメータが合法かどうかを判断します.
2.呼び出し_pm_stay_awakeはwakeup eventsを報告し、システムの起動を維持します.
3.mutexを使用してグローバル変数autosleepを保護します.
4.パラメータstateを使用してautosleep_を修正state
5.呼び出し_pm_relaxはwakeup eventsを解放し、システムは眠ることができる.
6.stateの値を判断し、offであればpm_を呼び出すwakep_autosleep_enabled関数disable autosleep、そうでなければpm_を呼び出すwakep_autosleep_Enabled関数enable autosleep機能は、ワークキューをコミットしながら機能します.
void pm_wakep_autosleep_enabled(bool set)
{
	struct wakeup_source *ws;
	ktime_t now = ktime_get();

	rcu_read_lock();
	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
		spin_lock_irq(&ws->lock);
		if (ws->autosleep_enabled != set) {
			ws->autosleep_enabled = set;
			if (ws->active) {
				if (set)
					ws->start_prevent_time = now;
				else
					update_prevent_sleep_time(ws, now);
			}
		}
		spin_unlock_irq(&ws->lock);
	}
	rcu_read_unlock();
}
1. システム内のすべてのwakeup sourceに対してautosleep_を修正しますenableフラグ.
2.このwakeupステータスがactiveでautosleepがenableの場合、wakeup sourceのstart_を変更します.prevenet_timeが現在に変更されたのは、autosleepの機能が今からブロックされていることを意味します.
3.このwakeupステータスがactiveでautosleepがdisableの場合.このwakeup source activeの過程でautosleep機能をオフにし、autosleepを阻止する総時間を更新することを説明します.
void queue_up_suspend_work(void)
{
	if (autosleep_state > PM_SUSPEND_ON)
		queue_work(autosleep_wq, &suspend_work);
}
この時点でsuspend_work autosleep_へのマウントwqワークキューでautosleep_wqは関数pm_autosleep_Init関数で作成されます.
int __init pm_autosleep_init(void)
{
	autosleep_ws = wakeup_source_register("autosleep");
	if (!autosleep_ws)
		return -ENOMEM;

	autosleep_wq = alloc_ordered_workqueue("autosleep", 0);
	if (autosleep_wq)
		return 0;

	wakeup_source_unregister(autosleep_ws);
	return -ENOMEM;
}
1. この関数は、「autosleep」wakeupsourceとして主に使用されます.Autosleepの状態を設定するときに、システムを起動状態に保つために使用します.
2.「autosleep」という名前の秩序化されたワークキューを同時に作成します.1つの時点で1つのワークしか処理できないことを保証するために.
B:suspendをコミットするとワークキューにワークが到着するとtry_が呼び出されます.to_suspend関数クエリがsuspendにアクセスできるかどうか.
static void try_to_suspend(struct work_struct *work)
{
	unsigned int initial_count, final_count;

	if (!pm_get_wakeup_count(&initial_count, true))
		goto out;

	mutex_lock(&autosleep_lock);

	if (!pm_save_wakeup_count(initial_count) ||
		system_state != SYSTEM_RUNNING) {
		mutex_unlock(&autosleep_lock);
		goto out;
	}

	if (autosleep_state == PM_SUSPEND_ON) {
		mutex_unlock(&autosleep_lock);
		return;
	}
	if (autosleep_state >= PM_SUSPEND_MAX)
		hibernate();
	else
		pm_suspend(autosleep_state);

	mutex_unlock(&autosleep_lock);

	if (!pm_get_wakeup_count(&final_count, false))
		goto out;

	/*
	 * If the wakeup occured for an unknown reason, wait to prevent the
	 * system from trying to suspend and waking up in a tight loop.
	 */
	if (final_count == initial_count)
		schedule_timeout_uninterruptible(HZ / 2);

 out:
	queue_up_suspend_work();
}
1. pm_を呼び出すget_wakeup_count関数はシステムのwakeup count数を取得し、initial_に格納します.count変数です.
2.pm_を呼び出すsave_wakeup_count関数はsaved_に格納されますcount変数で失敗した場合、wakeup eventが発生したことを示します.outにジャンプして再提出し、再送信しました.
3.autosleep_によるstateの状態で、それなりの睡眠をとる.
4.システムがwakeup eventを生成した後、システムを起動した後にwakeup countの数を再び読み出す.
5.読み出し数がsuspendと同じであれば、不明な原因でシステムが起動したことを説明します.このとき、autosleep->wakeup->autosleep->wakeupのループが発生します.だからtry_to_suspend関数は0.5 s待って、再びsuspendを試します.
C:このときシステムは何もしていないときにsuspendを試します.
D:目覚ましイベントがあれば、システムは睡眠から目覚まします.