Перевод Linux Power Management Support

Данная статья представляет собой перевод файла Documentation/pm.txt из дистрибутива ядра Linux 2.4.23. Информация применима и к ядрам серии 2.6.

[San АНДРЕЕВ]

Перевод Linux Power Management Support

1. Введение. Всплыла тут из глубин времен тема про управление питанием в линухах, полез в kernel documentation поискать чего-нибудь на эту тему. Нашел, но, как оказалось, не совсем то. Но я решил, что перевод все равно будет полезен, как минимум мне в качестве практики английского :). Так как с английским у меня не очень, то обоснованные поправки в переводу принимаются и вносятся.

2. Перевод Documentation/pm.txt из дистрибутива ядра 2.4.23.

Поддержка управления питанием в Linux

Этот документ поверхностно описывает использование управления питанием в вашей Linux-системе и внедрение этой поддержки в драйверы.

APM или ACPI?
-------------
Если у вас относительно новые x86 ноутбук, настольная система или сервер, то скорее всего они поддерживают Расширенное Управление Питанием (Advanced Power Management, APM) или Расширенные Конфигурацию и Интерфейс Питания (Advanced Configuration and Power Interface, ACPI). Из этих двух технологий ACPI посвежее и переносит систему управления питанием в руки операционной системы, что позволяет управлять ею эффективнее, чем это возможно через BIOS при помощи APM.

Лучшим способом опеределить, которая из двух технологий поддерживается вашей системой, является сборка ядра с поддержкой обеих (с версии 2.3.х поддержка ACPI включена по умолчанию). Если будет найдена работающая аппаратная поддержка ACPI, то ACPI-драйвер "перекроет" и отключит APM, в противном случае будет использоваться APM.

К сожалению, у вас нет возможности использовать ACPI и APM одновременно. Некоторые товарищи, имеющие неполную или местами некорректную аппаратную поддержку ACPI или APM, хотели бы использовать обе технологии чтобы включить весь набор возможностей, но не существуюет возможности добиться этого. Машиной можно управлять только через какой-то один интерфейс (их можно переключать, но не использовать одновременно).

User-space Daemons
------------------
Как APM, так и ACPI нуждаются в поддержке программ-демонов, работающих в пространстве пользователя и называющихся apmd и acpid соответственно. Обоих можно найти в вашем дистрибутиве Linux или получить через Internet (см. ссылки ниже) и вам нужно сделать так, чтобы они поднимались в процессе загрузки системы. Если аппаратной поддержки APM или ACPI в вашей системе нет, то демон просто завершит свою работу.

apmd: worldvisions.ca/~apenwarr/apmd/
acpid: acpid.sf.net/

Интерфейс драйвера
------------------
Если вы пишете новый драйвер для устройства или поддерживаете имеющийся, то он должен включать в себя поддержку управления питанием. Без этого такой драйвер даже в системах с ее поддержкой может исключить возможность безопасного засыпания.

В двух словах:
1) зарегистрируйте каждую часть устройства через pm_register
2) вызовите pm_access перед обращением к оборудованию
(это гарантирует, что устройство включено и готово)
3) ваша функция pm_callback будет вызвана перед переходом в
состояние "сна" (ACPI D1-D3) или после возврата из него (ACPI D0)
4) вызовите pm_dev_idle когда устройство не используется какое-то время
(необязательно, но поможет выявлению простоя устройства)
5) при выгрузке модуля (драйвера) не забудьте разрегистрировать его при помощи pm_unregister

/*
 * Описание: Регистрация устройства в подсистеме управления питанием
 *
 * Параметры:
 *   type - тип устройства (PCI-устройство, системное устройство, ...)
 *   id - номер функционально независимой части устройства или уникальный 
 *   идентификатор
 *   cback - обработчик запроса "обратного вызова" aka callback (засыпание, 
 *   возврат, ...)
 *
 * Возвращает: Зарегистрированное PM-устройство или NULL при ошибке
 *
 * Примеры:
 *   dev = pm_register(PM_SYS_DEV, PM_SYS_VGA, vga_callback);
 *
 *   struct pci_dev *pci_dev = pci_find_dev(...);
 *   dev = pm_register(PM_PCI_DEV, PM_PCI_ID(pci_dev), callback);
 */
struct pm_dev *pm_register(pm_dev_t type, unsigned long id, pm_callback cback);

/*
 * Описание: Исключает устройство из подсистемы управления питанием
 *
 * Параметры:
 *   dev - PM-устройство, ранее возвращенное pm_register
 */
void pm_unregister(struct pm_dev *dev);

/*
 * Описание: Исключает все устройства с указанным callback
 *
 * Параметры:
 *   cback - ранее зарегистрированный callback
 *
 * Замечание: Используется для облегчения портирования со старого интерфейса APM
 */
void pm_unregister_all(pm_callback cback);

/*
 * Обнаружение использования/простоя устройства
 *
 * В общем случае драйверы для всех устройств должны вызывать
 * pm_access перед обращением к оборудованию (т.е. перед чтением
 * или изменением аппаратных регистров). Пакетно-управляемые
 * или "запросные" драйверы должны дополнительно вызывать
 * pm_dev_idle в те моменты когда устройство не используется.
 *
 * Примеры:
 * 1) Клавиатурный драйвер должен вызывать pm_access при каждом нажатии 
 * клавиши.
 * 2) Сетевой драйвер должен вызвать pm_access перед подтверждением
 *    передачи или приема пакета и pm_dev_idle когда его очереди
 *    передачи или приема пусты.
 * 3) VGA-драйвер должен вызывать pm_access перед обращением к регистрам
 *    видеоконтроллера.
 *
 * В конечном счете, управляющий политикой PM использует информацию о доступе к 
 * устройствам и их простое для принятия решений о "замораживании" отдельных 
 * устройств или системы в целом.
 */

/*
 * Описание: Изменяет время последнего обращения к устройству и "будит" его при 
 * необходимости.
 *
 * Параметры:
 *   dev - PM-устройтво, возвращенное ранее pm_register
 * 
 * Подробности: При вызове из обработчика прерывания pm_acces изменяет
 *          время обращения к устройству, но не выполняет "подъема" устройства
 *          (если устройство генерирует прерывания, то оно предполагается
 *          работающим). Это важно и поэтому мы не сможем "будить" устройства
 *          из обработчиков прерываний.
 */
void pm_access(struct pm_dev *dev);

/*
 * Описание: Помечает устройство как простаивающее
 *
 * Параметры:
 *   dev - PM-устройство, возвращенное ранее pm_register
 *
 * Подробности: Вызов pm_dev_idle может послужить сигналом управляющему 
 *          политикой "усыпить" устройство. Если запрос к новому устройству 
 *          будет получен между вызовом pm_dev_idle и "обратным вызовом" 
 *          pm_callback, то драйвер должен проигнорировать запрос pm_callback.
 */
void pm_dev_idle(struct pm_dev *dev);

/*
 * Запрос "обратного вызова" управления питанием
 *
 * Параметры:
 *   dev - PM-устройтво, возвращенное ранее pm_register
 *   rqst - тип запроса
 *   data - произвольные данные, связанные с запросом
 *   
 * Возвращает: 0 при успешном выполнении запроса
 *          EINVAL если запрос не поддерживатся
 *          EBUSY если устройство сейчас занято и не может обработать запрос
 *          ENOMEM если устройство не может обработать запрос из-за недостатка 
 *          памяти
 *          
 * Подробности: callback запроса устройства будет вызван перед тем, как
 *          устройство/система войдут в состояние "сна" (ACPI D1-D3) или
 *          после того, как устройство/система будут "разбужены" (ACPI D0).
 *          Для PM_SUSPEND введенное D-состояние ACPI будет передано в качестве
 *          аргумента "data" callback'у. Драйвер устройства должен сохранить
 *          (PM_SUSPEND) или восстановить (PM_RESUME) контекст устройства
 *          когда будет получен запрос на вызов callback'а.
 *          
 *          После успешного выполнения запроса на "пробуждение" устройства
 *          драйвер должен игнорировать любой доступ и запросы к оборудованию 
 *          до тех пор, пока не будет сделан вызов pm_access.
 */
typedef int (*pm_callback)(struct pm_dev *dev, pm_request_t rqst, void *data);

Подробности
-----------
Ниже приводится небольшой список ЧаВо, который будет со временем заменен полноценным руководством по написанию драйверов.

В: Когда устройства "засыпают"?
О: Устройства могут быть "усыплены" по прямому запросу пользователя (закрытие крышки ноутбука), из-за системной политики управления питанием (после 30 минут неактивности на консоли) или из-за политики управления питанием устройства (выключение питания устройства после 5 минут простоя).

В: Должен ли драйвер всегда выполнять запрос на отключение питания?
О: Нет, драйвер может вернуть -EBUSY в ответ на запрос и это остановит отключение питания системы. В этом случае все отключенные устройства "будятся" и система продолжает работать. Попытку можно повторить позже.

В: Может ли драйвер блокировать запросы отключения/включения питания?
О: Да, драйвер может задерживать выдачу ответа на такой запрос до готовности устройства обработать этот запрос. Однако лучше реагировать на запросы настолько быстро, насколько это возможно.

В: В каком контексте порождаются запросы на отключение/включение?
О: Запросы на отключение/питанию порождаются в контексте потока ядра. Поэтому во время "сна" системы неопасно блокировать и выделять память, инициировать запросы и делать все, что угодно, что можно делать по отношению к ядру.

В: Будет ли драйвер обрабатывать запросы "во сне"?
О: Возможно. Обработка запросов после "засыпания" целиком зависит от драйвера - он может как выполнять их, так и отбрасывать или просто игнорировать. Важно здесь то, что драйвер не получит доступа к устройству из-за того, что шина устройства может быть неактивна.
(*) Если драйвер образует очередь из запросов к устройству в то время, пока оно "спит", то вам следует помнить о том, что состояние этого устройства и связанных с ним объектов (напр. локальной сети) к моменту пробуждения может измениться. Представляется разумным игнорировать все запросы, только если это не драйвер устройства хранения информации.

В: Могу ли я управлять шинно-специфичными регистрами управления питанием?
О: Нет. Этим занимается драйвер соответствующей шины. Этот драйвер или подсистема управления питанием в целом самостоятельно задействуют доступную функциональность по управлению питанием, если она имеется.

В: Так что мне на самом деле необходимо для включения поддержки отключения/включения питания?
О: Вам нужно сохранить любые данные о состоянии устройства (контекст), которые могут быть утеряны при отключении устройства. При использовании ACPI доступны три уровня отключения: D1, D2 и D3 (эти уровни передаются в качестве аргумента "data" callback'у устройства). На уровне D3 устройство отключается полностью и теряет весь контекст, D1 и D2 просто в разной степени понижают энергоптребление и в этом случае теряется только часть контекста. Чтобы просто и полностью обезопасить себя, достаточно просто сохранять весь контекст и потом полностью его восстанавливать.

В: Где я могу сохранять контекст устройства?
О: Скорее всего в памяти. Выделите буфер при помощи kmalloc или сохраните контекст в дескрипторе устройства. Это гарантирует, что содержимое памяти будет восстановлено и доступно перед включением, даже если система сбрасывала свое состояние диск перед отключением.

В: Что мне нужно для использования ACPI по сранению с APM (и т.д.)?
О: Драйверы не должны беспокоиться о том, какая технология управления питанием используется в системе. Для них гланое не забывать обрабатывать запросы на отключение/включение.

В: Как быть с зависимостями между устройствами?
О: Когда драйвер регистрирует устройство, подсистема управления питанием использует предоставленную информацию для построения дерева зависимостей между устройствами (вроде USB-устройство А подключено к USB-контроллеру Б, который подключен к PCI-шине В). Когда подсистема хочет "усыпить" устройство, она сначала посылает соответствующий запрос его драйверу, потому драйверу контроллера и так далее до системной шины. Во время пробуждения устройства все происходит в обратном порядке.

В: Где я могу получить дополнительную информацию о включении управления питанием для моего особенного драйвера или устройства?
О: Попробуйте обратиться в лист рассылки разработчиков ACPI
acpi-devel@lists.sourceforge.net

Системный интерфейс
-------------------
Если вы разрабатываете новую систему управления питанием для Linux (что-то вроде APM или ACPI), то вам необходимо взаимодействовать с драйверами через существующий интерфейс управления питанием.
/*
 * Посылает запрос отдельному устройству
 *
 * Параметры:
 *   dev - PM-устройство, возвращенное ранее pm_register или pm_find
 *   rqst - типа запроса
 *   data - любые данные, связанные с запросом
 *
 * Возвращает: 0 при успешном выполнении запроса
 *             См. описание pm_callback в случае возникновения ошибки
 *
 * Подробности: "Пробрасывает" запрос к callback'у устройства и, в случае 
 *              запроса на отключение/включение, присваивает соответствующее 
 *              значение полю "state" структуры pm_dev.
 */
int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data);

/*
 * Посылает запрос всем устройствам
 *
 * Параметры:
 *   rqst - тип запроса
 *   data - любые данные, связанные с запросом
 *
 * Возвращает: 0 при успешном выполнении запроса
 *             См. описание pm_callback в случае возникновения ошибки
 * 
 * Подробности: Перебирает список зарегистрированных устройств и вызывает
 *              pm_send для каждого пока не достигнет конца списка или
 *              не возникнет ошибка. Если ошибка возникнет при запросе на 
 *              отключение, то возвращает все "пройденные" устройства в 
 *              состояние, в котором они были до этого.
 */
int pm_send_all(pm_request_t rqst, void *data);

/*
 * Находит соответствующее устройство
 *
 * Параметры:
 *   type - тип устройства (PCI-устройство, системное устройство или 0 для 
 *          выбора всех устройств)
 *   from - предыдущее соответствие или NULL для запуска с начала
 *
 * Возвращает: Соотствествующее устройство или NULL, если ничего не найдено
 */
struct pm_dev *pm_find(pm_dev_t type, struct pm_dev *from);


3. Примечания
В том же файле в дистрибутиве ядра 2.6.0 изменений не обнаружено.

4. Перевод
San АНДРЕЕВ

Оригинал статьи расположен по адресу: http://linuxportal.ru/entry.php/P952_0_3_0/.

[ опубликовано 01/11/2004 ]

San АНДРЕЕВ - Перевод Linux Power Management Support   Версия для печати