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()를 호출하도록 수정하였다.

하루종일 테스트하였지만 아직까지는 문제가 없다.