Android待機的suspend_sys_sync_queue分析

Android待機的suspend_sys_sync_queue分析


Android的4.0在待機機制上和之前版本大同小異,也可以說是機制相對完善並沒多大的問題反饋出來。不過有個細節的地方,改動幅度較大,來看看


在linux待機機制中,開始待機的時候會調用sys_sync函數,sys_sync系統調用被用戶空間函數調用,

用來將緩存中的數據寫入塊設備,sys_sync系統調用將buffer、inode和super在緩存中的數據寫入設備。

此函數的介紹參看博文鏈接http://blog.chinaunix.net/uid-24237502-id-106067.html。


sys_sync函數執行時間長度依文件系統而定,長至上百毫秒,也有若干毫秒。函數執行的必要性如何呢?

如果有種需求,在很短時間(毫秒級)內需要連續進出待機,sys_sync函數是否需要每次都執行一遍呢?

如果只第一次執行,會有什麼隱患問題呢?


由於待機請求有自動超時待機和按鍵進入待機,自動進入待機問題不大,基本上是鬧鐘或者計時時間發起事件,

而按鍵進入待機不同,按鍵時間貫穿kernel到android上層,涉及節點處理,文件系統操作,

所以對同步設備節點數據sys_sync就有需求,可以看出來,android4.0在對sys_sync的調用修改上,也是考慮到按鍵進入待機的隱藏問題。

那到底做了哪些修改呢?


suspend_sys_sync_queue有三個地方在調用

Earlysuspend.c (kernel\power)

Suspend.c (kernel\power):

Wakelock.c (kernel\power):

定義是在Wakelock.c (kernel\power)中

這個函數實現執行隊列中suspend_sys_sync_work

void suspend_sys_sync_queue(void)
{
	int ret;

	spin_lock(&suspend_sys_sync_lock);
	ret = queue_work(suspend_sys_sync_work_queue, &suspend_sys_sync_work);
	if (ret)
		suspend_sys_sync_count++;
	spin_unlock(&suspend_sys_sync_lock);
}

	suspend_sys_sync_work_queue =
		create_singlethread_workqueue("suspend_sys_sync");

static void suspend_sys_sync(struct work_struct *work)
{
	if (debug_mask & DEBUG_SUSPEND)
		pr_info("PM: Syncing filesystems...\n");

	sys_sync();

	if (debug_mask & DEBUG_SUSPEND)
		pr_info("sync done.\n");

	spin_lock(&suspend_sys_sync_lock);
	suspend_sys_sync_count--;
	spin_unlock(&suspend_sys_sync_lock);
}
static DECLARE_WORK(suspend_sys_sync_work, suspend_sys_sync);

所以調用suspend_sys_sync_queue,其實最後還是調用sys_sync,只是增加對sys_sync執行次數的計數,以及隊列方式執行並非立即執行。

隊列方式執行,是對執行的時間上有要求,那麼它到底需要等待多少時間呢?相對什麼等待呢?

待機,對系統而言,就是凍結,退出待機時候,對系統而言相當於什麼事情都沒發生。

標準linux待機在待機的開始階段,就會凍結所有的進程和任務,在喚醒時候恢復它們。

由於進程在kernel空間處理的複雜度,linux在凍結進程和任務的時候,不支持進程還跑在kernel空間,否則就是abort處理。

問題就出來了,最後一次按鍵進入待機,按鍵時間從鍵值採集、鍵值上報、上層對上報值的輪詢、對按鍵事件分發、對按鍵時間的處理等,

都需要一定時間,如果在進程未能較快回到用戶空間,freeze_processes的時候,如下

	printk("Freezing user space processes ... ");
	error = try_to_freeze_tasks(true);
	if (error)
		goto Exit;
	printk("done.\n");

在freezing user space processes會abort。

補充一下說明:這裏針對的不全是最後一次按鍵,我們知道android爲了增加用戶體驗,在超時進入待機時候,會啓動一個5秒的定時鬧鐘,

從而阻止5秒內真正進入待機,在用戶體驗上,就是屏幕暗下來的5秒內,只要用戶及時點亮屏幕,系統就沒真正待機,不會有鎖屏界面。

開發員在此過程中會看到freezing user space processes abort,是因爲系統啓動了alarm,從而推出待機,系統恢復。

這裏增加了一個timer,用於等待sys_sync執行完畢

	error = suspend_sys_sync_wait();
	if (error)
		goto Exit;

int suspend_sys_sync_wait(void)
{
	suspend_sys_sync_abort = false;

	if (suspend_sys_sync_count != 0) {
		mod_timer(&suspend_sys_sync_timer, jiffies +
				SUSPEND_SYS_SYNC_TIMEOUT);
		wait_for_completion(&suspend_sys_sync_comp);
	}
	if (suspend_sys_sync_abort) {
		pr_info("suspend aborted....while waiting for sys_sync\n");
		return -EAGAIN;
	}

	return 0;
}

static bool suspend_sys_sync_abort;
static void suspend_sys_sync_handler(unsigned long);
static DEFINE_TIMER(suspend_sys_sync_timer, suspend_sys_sync_handler, 0, 0);
/* value should be less then half of input event wake lock timeout value
 * which is currently set to 5*HZ (see drivers/input/evdev.c)
 */
#define SUSPEND_SYS_SYNC_TIMEOUT (HZ/4)
static void suspend_sys_sync_handler(unsigned long arg)
{
	if (suspend_sys_sync_count == 0) {
		complete(&suspend_sys_sync_comp);
	} else if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
		suspend_sys_sync_abort = true;
		complete(&suspend_sys_sync_comp);
	} else {
		mod_timer(&suspend_sys_sync_timer, jiffies +
				SUSPEND_SYS_SYNC_TIMEOUT);
	}
}

等待的時間should be less then half of input event wake lock timeout value,

evdev事件會超時50ms,這裏的timeout需要小於25ms,此處設置爲2.5ms。

對timeout的要求,應該是爲了能夠更快地在evdev超時後執行timer的handler。


以上這些代碼,都可以追蹤CONFIG_SUSPEND_SYNC_WORKQUEUE宏定義。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章