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

Патч ядра 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 проблема все также актуальна и вряд ли будет решена в апстриме ядра в обозримом будущем.

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

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