habrahabr

История еще одного патча: зависшая батарея

  • вторник, 11 марта 2025 г. в 00:00:11
https://habr.com/ru/articles/889058/

Ноутбук засыпает, ноутбук просыпается, батарея «зависает» — более не отдает ни уровень заряда ни другие показатели, вне зависимости от подключения к сети.

Патч ядра Linux и три года изысканий, рассказываю как это было.

Божественные Вайнона Райдер и Натали Портман, работы нейросети. Ну и пропатченное ядро.
Божественные Вайнона Райдер и Натали Портман, работы нейросети. Ну и пропатченное ядро.

Вводная

Автор очень давно использует самые разнообразные версии и вариации Linux и UNIX‑систем для работы и диких развлечений, в том числе на ноутбуках, поэтому старается решать все найденные проблемы, по мере сил.

Временами проблема возвращается заново в новых версиях ядра, будучи решенной в прошлом, временами происходит наоборот и проблема проявляется только в самых свежих версиях Linux.

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

Описываемая история — как раз из последних.

Проблема

Вкратце проблема заключалась в абзаце, вынесенном в заголовок:

ноутбук засыпает, ноутбук просыпается, после чего батарея «зависает» — более не отдает свой актуальный уровень заряда и все показатели, вне зависимости от подключения к электросети.

Естественно в Windows все работало правильно и стабильно, в любых режимах засыпания.

Ноутбук редкий, ноутбук старый, от вендора, который в гробу видал никогда не любил альтернативные ОС и тем более в страшном сне не мог представить, что одна из топовых моделей (на свое время) вместо подсчета прибылей успешному менеджеру, стала бы использоваться для компиляции ядра из исходников и прочих гиковских непотребств.

Никакая отладка ядра и никакие отладочные сообщения не помогли, что неудивительно:

Работа с ACPI — традиционно самая замороченная область ядра Linux, а процессы засыпания и возвращения к работе — сложны и нестабильны по своей сути.

Так что оно глючило, глючит и будет глючить, в любой ОС и на любом оборудовании при любой погоде.

Процесс отлова ошибок связанных с ACPI усложнен тем, что такие ошибки чаще всего «плавающие» — могут появиться не через один цикл «засыпания‑пробуждения» а например через десять, т. е. вам надо десять раз подряд погрузить ноутбук в сон и затем пробудить чтобы отловить ошибку.

Правда ведь отладка это весело?

Решение

Далеко не сразу (ушло примерно три года), путем хитрых запросов к поисковикам и изучения исходников ядра, автор все же смог отыскать концы этой проблемы.

И помог в этом случайный комментарий неизвестного китайского разработчика:

I have few reputation so I can't tell others about solution in their questions, so I will post it here.

Solution : build kernel with this PATCH

Description : I tracked the issue ,I discovered that all was working good until the 4.19.86 kernel.

So I checked DIFF and After many tries maybe 50 or more.

I found the cause : It was (if statement was added in 4.19.86 COMMIT) ,particularly checking if spaceid == 0 [ACPI_ADR_SPACE_SYSTEM_MEMORY]. Commit Link

Разумеется патч китайского автора оказался нерабочий, сообщение было написано про другую модель ноутбука, с другим поведением при сбое и с неверной фиксацией на типе батареи в качестве источника проблемы.

Еще речь шла про совсем уж древнюю версию ядра (4.19, релиз в 2018м году) а само сообщение датировалось 2022 годом.

Но вы ведь и не думали, что все будет настолько просто, верно?

Решение

Посмотрим на код «виновника торжества», файл drivers/acpi/acpica/evregion.c, метод acpi_ev_execute_reg_methods, а еще точнее — вот этот замечательный комментарий:

/*
* These address spaces do not need a call to _REG, since the ACPI
* specification defines them as: "must always be accessible". Since
* they never change state (never become unavailable), no need to ever
* call _REG on them. Also, a data_table is not a "real" address space,
* so do not call _REG. September 2018.
*/

Обратите внимание на дату — 2018й год, год выпуска версии ядра 4.19, в которой и проявилась данная проблема.

По сути самого комментария и исходя из логики, один из коммитеров ядра Linux в далеком 2018 году хотел «сделать как лучше»:

if ((space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) ||
	    (space_id == ACPI_ADR_SPACE_SYSTEM_IO) ||
	    (space_id == ACPI_ADR_SPACE_DATA_TABLE)) {
		return_VOID;
}

Полагая что строгое соотвествие спецификации ACPI в данном случае бывает всегда — он вставил заглушку, убирающую вызов _REG метода для вроде как системных частей ACPI‑прошивки.

Чем и поломал восстановление статуса батареи при пробуждении ноутбука.

Благими намерениями выстелена дорога в Ад. (ц)

Исправление

Все что нужно сделать для исправления ситуации, это убрать из условия проверки константу ACPI_ADR_SPACE_SYSTEM_MEMORY:

if (
        //    (space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) ||
	    (space_id == ACPI_ADR_SPACE_SYSTEM_IO) ||
	    (space_id == ACPI_ADR_SPACE_DATA_TABLE)) {
		return_VOID;
}

Ну и опционально добавить отладку, чтобы убедиться в правильности работы:

if (space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
		printk(KERN_DEBUG "PRO HEHE : Bypassing for battery is done");
}

Отладка была включена в текущем ядре, само сообщение можно наблюдать на стартовой картинке к статье.

Для того чтобы убедиться в правильности работы, достаточно усыпить ноутбук, пробудить и подергать состояние батареи несколько раз:

Эпилог

Такой патч никогда не примут в аппстрим ядра, потому что он неправилен с точки зрения спецификации ACPI.

Собственно сама проблема с зависшей батареей появилась из-за того что некоторые вендоры класть хотели на стандарты и спецификации.

К сожалению такого рода проблемы в новых версиях ядра Linux появляются все чаще, поэтому и ситуация и ее решение — уже можно сказать типовые и подобным образом решаются ныне и многие другие проблемы с оборудованием.

Так что если на вашем ноутбуке также проявляется описанная проблема с «зависанием» батареи — теперь будете знать в какую сторону копать.

P.S.

Если вы внимательно смотрели на заглавную картинку к статье, могли заметить что автор использовал нестандартное ядро:

XanMod is a general-purpose Linux kernel distribution with custom settings and new features. Built to provide a stable, smooth and solid system experience.

The real-time version is recommended for critical runtime applications such as Linux gaming server / client for eSports, streaming, live productions and
ultra-low latency enthusiasts.

Подтверждаю, этот набор патчей ядра Linux действительно сильно ускоряет работу, что особенно заметно на некрожелезе времен молодости Брежнева, которое автор регулярно использует для укрепления стойкости духа.

P.P.S.

На март 2025 проблема все также актуальна и вряд ли будет решена в апстриме ядра в обозримом будущем.

Поэтому автор продолжает накладывать описанный патч (и еще несколько) при каждой пересборке ядра.

Еще добавлю, что это немного почищенная и обновленная версия статьи, оригинал которой находится в нашем блоге.