Powered By Blogger

Sunday, November 11, 2007

Toshiba Satellite A100-811: Core 2 Duo и частота ядер после возврата из suspend-to-RAM (solved).

Довольно долгое время у меня на ноуте под управлением Ubuntu 7.04 наблюдался такой странный глюк. Во время работы от аккумулятора при засыпании ноута в память (suspend-to-RAM) и по возвращении из сна одно ядро процессора работало на пониженной частоте, как и положено в режиме экономии, а другое - почему-то начинало работать на полных оборотах. На просторах интернета я нашёл следующее решение: накатать скрипт следующего содержания:

#!/bin/sh

SYS_DIR=/sys/devices/system/cpu
POLICY=powersave

# ondemand

for CPU in `ls $SYS_DIR`
do
    echo -n "$POLICY" > $SYS_DIR/$CPU/cpufreq/scaling_governor
done
и положить его под именем <priority>-<name> в папку /etc/acpi/resume.d. Здесь priority - это число, определяющее порядок выполнения скрипта. Как и в директориях rc?.d файлы обрабатываются демоном acpid в алфавитном порядке и чтобы задать приоритет, необходимо приписать соответствующий префикс. Пусть в нашем случае это будет 10-core2duo-freq-fix.sh

Не понадобилось много времени, чтобы заметить, что данный способ не работает. Долго и упорно я пытался найти решение, копаясь в спецификации ACPI, в частности, производя поиски на предмет генерируемых событий. Много чего нового узнал, но того, что надо, так и не нашёл. Я не мог понять, почему в моей системе не генерируется событие по выходу из сна - resume... И не мог понять, отдаётся ли такое событие в принципе и предусмотрено ли оно самим ACPI. И хотя ответ на этот вопрос сразу найден не был, решение задачи оказалось гораздо проще и лежало оно в немного другой области. Собственно, решение мне подсказали на форуме и было даже немного обидно, до того всё оказалось очевидным :)
Чтобы понять все "что, как и почему" и вникнуть в суть, давайте немного пройдёмся по матчасти.

ACPI
Во времена оные всё было иначе - солнышко светило ярче, трава была зеленее и забористее, деревья большие, а для управления питанием использовался APM - интерфейс Advanced Power Management. APM - не что иное, как "расширение" BIOS'а по управлению питанием. Или говоря иначе, в системах с APM было устройство /dev/apm, на котором можно было сделать ioctl(), а остальное уже выполнялось железом. APM работал не идеально, но работал. Кто-то пришёл к выводу, что это не есть хорошо и так появился ACPI - Advanced Configuration and Power Interface. В отличие от APM, ACPI возлагает часть работы на операционную систему. Поэтому, строго говоря, если с APM всё было достаточно просто, т.к. со стороны ОС требовалась лишь поддержка интерфейса APM и вся работа выполнялась уже вне ядра ОС аппаратным обеспечением, то для нормальной работы энергосбережения через ACPI необходима корректная реализация этого самого ACPI в ОС, что несколько усложняет вещи. Но с другой стороны у ACPI есть преимущество перед APM. Если APM - это функция железа, то ACPI - это функция во многом ОС. Что легче заменить в случае неправильной работы? Железо или ядро ОС? Не будем углубляться в тонкости работы обоих интерфейсов. Сосредоточимся на ACPI. Как было упомянуто, тут ядро выполняет гораздо больше работы, чем в случае с APM. В частности, это позволяет переопределить поведение системы в некоторых случаях, где это было сложно или невозможно с APM. Работа ACPI управляется во многом событиями. Допустим такой пример: пользователь нажимает кнопку "power", это генерирует прерывание, которое перехватывается встроенным контроллером. Реакцией на прерывание становится выставление флагов. Таким образом, ОС уведомляется о том, что что-то произошло. Далее, ОС смотрит, что должно произойти дальше согласно DSDT и передаёт сообщение пользовательскому процессу в виде события. В роли процесса обычно выступает демон acpid, который читает файл /proc/acpi/event. Далее, acpid выполняет какие-то предопределённые конфигурацией действия. В общих чертах схема такова.

Suspend, как он есть.
Спецификация ACPI определяет набор так называемых состояний (глобальных - Gx и сна - Sx). suspend-2-ram - одно из S-состояний системы. В случае с переходом в suspend дела могут обстоять несколько иначе, чем было описано выше. В частности, если пользователь нажимает какую-то кнопку в диалоге выхода из системы, то процесс идёт в обратном направлении. Сам по себе процесс засыпания на этот раз инициируется пользовательским процессом - всё тем же acpid, допустим. Он выполняет некие действия, предусмотренные конфигурацией. Пусть была нажата кнопка перехода в состояние suspend-2-ram, происходит приблизительно следующее:

  1. acpid поучает сообщение через unix-сокет. Как гласит файл /etc/acpi/events/sleepbtn, обработчиком события является скрипт /etc/acpi/sleep.sh:
    # /etc/acpi/events/sleepbtn
    # Called when the user presses the sleep button
    # [ Здесь-то мы и определяем обработчик события ]
    
    event=button[ /]sleep
    action=/etc/acpi/sleep.sh
  2. Далее управление передаётся скрипту /etc/acpi/sleep.sh. Мы не будем рассматривать его здесь очень детально, скажем лишь, что этот скрипт вполне может умыть руки и передать управление gnome-power-manager'у или klaptopdaemon'у например, если кто-то из них запущен и не определено форсирование выполнения обработчика ACPI - /etc/acpi/sleep.sh. В противном случае, если демоны Gnome или KDE не работают или необходимо форсировать выполнение /etc/acpi/sleep.sh, далее по очереди вызывается скрипт prepare.sh. Он подготавливает пользовательское окружение к переходу в S-состояние. Всё, что делает этот скрипт - выполняет скрипты, сложенные в директории /etc/acpi/suspend.d:
    #!/bin/bash
    
    for SCRIPT in /etc/acpi/suspend.d/*.sh; do
      . $SCRIPT
    done
  3. Далее идёт кое-какая рутина. И, наконец, самое интересное, в конце... То, чего я не заметил и то, что оказалось так очевидно. Вызывается скрипт /etc/acpi/resume.sh, который тянет в свою очередь всё, что лежит в директории /etc/acpi/resume.d. Знакомое имя? :)
  4. acpid просит ядро заснуть. И наконец, очередь доходит до ядра, которое выполняет всю чёрную работу - останавливает и замораживает процессы и переводит железо в S-состояние. Теперь мы спим.

Хотя, на самом деле, есть событие ACPI, которое генерируется при пробуждении (Wakeup) - WAK, но как мы увидели, обработчика такого события у acpid нет. Всё сделано гораздо проще. Выполнение скрипта приостанавливается как раз в той точке, где в специальный файл записывается значение, определяющее тип сна - echo -n $ACPI_SLEEP_MODE >/sys/power/state. При пробуждении нам не нужны никакие обработчики, потому что скрипт продолжает выполняться со следующей строки и вполне логично доходит до вызова скриптов, что находятся в директории /etc/acpi/resume.d. Довольно красиво и просто. Итак, мы ответили на два вопроса - "что?" и "как?". Остался ещё один - почему не работает наш скрипт, помещённый в /etc/acpi/resume.d? Нет, конечно же он работает, если запускать его вручную. Но это не наш метод. Ответ на самом деле уже был дан. Осталось придать ему более чёткую формулировку.

KDE, Gnome и HAL
HAL - Hardware Abstraction Layer - предоставляет ещё один метод абстракции от железа и управления питанием. В десктоп-менеджерах, Gnome и KDE часто работают свои менеджеры питания. У меня, в частности, работает утилитка из kde-guidance-powermanager. Она не пользуется услугами acpid, используя вместо него HAL. HAL использует и gnome-power-manager. Дальше всё просто. Можно считать, решение задачи найдено. Мы уже знаем, где находятся скрипты HAL: /usr/lib/hal/scripts/linux. Здесь нам нужен скрипт hal-system-power-suspend-linux. Выполняется он по такому же принципу, как и скрипт-обработчик ACPI - т.е., доходит до определённой точки, усыпляет систему и продолжает выполнение далее по пробуждении. Вот этим-то мы и воспользуемся, добавив в конец скрипта такие строки:

for x in /proc/acpi/ac_adapter/*; do
    grep -q off-line $x/state

    if [ $? = 0 ] && [ x$1 != xstop ]; then
        /etc/acpi/resume.d/10-dualcore-freq.sh
    fi
done
Здесь мы проверяем, работает ли ноутбук от аккумулятора. Если мы работаем от сети, то ничего делать не надо. Профиль производительности cpufreq как был performance, так и остаётся. В противном случае, если мы работаем на аккумуляторе, сбиваем частоту ядер записью профиля powersave в /sys/devices/system/cpu/*/cpufreq/scaling_governor. Выравниваем частоту обоих ядер, чтобы не ошибиться :), так как ничего страшного в этом нет и искать ядро-спринтер нет нужды. Теперь всё должно работать. Мысленно поздравьте себя ;)


При написании частично использовалась статья http://www.advogato.org/. Большое спасибо аффтару.

No comments:

ПОСЕТИТЕЛИ

free counters