Linux/Android
[Android] stop_drawing_early_suspend: timeout waiting for userspace to stop drawing
뭉기
2013. 1. 31. 22:38
출처 : http://iamyooon.tistory.com/86
안드로이드 디바이스를 사용하는 도중
터치는 동작하지만 화면이 refresh되지 않는 문제를 경험한 적이있다.
디버그메시지를 살펴보니 stop_drawing_early_suspend: timeout waiting for userspace to stop drawing
라고 warning메시지를 띄우는것을 볼 수있었다.
kernel/kernel/power/fbearlysusepnd.c에는
early suspend 진입시에 호출되는 stop_drawing_early_susepnd()가 있다.
이 함수에서는 fb_state변수를 FB_STATE_REQUEST_STOP_DRAWING으로 설정한 다음
HZ시간 동안 해당 변수가 FB_STATE_STOP_DRAWING으로 변경되길 기다린다.
시간 안에 값이 변경되지 않을 경우 위 에러메시지를 출력하게 된다.
변수 fb_state는 동일 파일내에 있는 wait_for_fb_wake_show()에 의해 변경된다.
좀 더 자세히 이야기하자면 wait_for_fb_wake_show()는
/sys/power/wait_for_fb_wake 파일을 read하였을 때 호출되도록 아래와 같이 정의되어 있다.(동일한 파일에 정의됨)
#define power_ro_attr(_name) \ static struct kobj_attribute _name##_attr = { \ .attr = { \ .name = __stringify(_name), \ .mode = 0444, \ }, \ .show = _name##_show, \ .store = NULL, \ } power_ro_attr(wait_for_fb_sleep); power_ro_attr(wait_for_fb_wake); |
wait_for_fb_wake파일은
frameworks/base/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp의
threadLoop() 메소드에서 아래와 같이 사용한다.(read된다.)
static char const * kSleepFileName = "/sys/power/wait_for_fb_sleep"; static char const * kWakeFileName = "/sys/power/wait_for_fb_wake"; bool DisplayHardwareBase::DisplayEventThread::threadLoop() { fd = open(kSleepFileName, O_RDONLY, 0); ... err = read(fd, &buf, 1); ... LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); if (err >= 0) { ... flinger->screenReleased(0); ... } fd = open(kWakeFileName, O_RDONLY, 0); ... err = read(fd, &buf, 1); ... LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); if (err >= 0) { ... flinger->screenAcquired(0); ... } } |
두번째 read() 호출에 의해 wait_for_fb_wake파일이 read되고 wait_for_fb_wake_show()를 호출하게 되는 것이다.
코드를 봐서 알겠지만 screenAcquired()가 본 문제와 밀접한 관계가 있다.
확실하진 않지만 screenAcquired()는 결국 acquireScreen()를 호출하여 mCanDraw를 true로 설정하는 것으로 보인다.
안드로이드에서는 변수 mCanDraw가 true로 변경되어야 surface flinger가 화면업데이트를 할 수 있는 것이다.
read()호출이 반드시 성공하여야 screenAcquired()가 호출되고 화면이 갱신되는 구조는
개인적으로는 바람직해 보이지 않아 보인다.
실패할 경우 디바이스를 재부팅하지 않고서는 정상적인 사용이 불가능하기 때문이다.
좀 더 근본적인 문제는 open()과 read()가 정상적으로 작동하지 않는 것에 있다.
suspend에 진입하면서 filesystem에 문제가 생기는 것인지, 어떤 타이밍 문제인지는 모르겠지만
가끔 open()이 0을 리턴하고 이 file descriptor를 이용해 read()를 호출하니 에러가 발생한다.
이유를 못찾고 임시방편으로 read()결과와 상관없이 screenAcquired()를 호출하도록 수정하였다.
하루종일 테스트하였지만 아직까지는 문제가 없다.