Hatred's Log Place

DON'T PANIC!

Feb 12, 2022 - 5 minute read - Linux

ThinkPad T530: зависание при выходе из сна на ядрах 5.10+ (опять)

“Никогда такого не было и вот опять” (с). В этот раз зависание имеет место быть, если воткнуто какое-то USB устройство.

Я уже толком не вспомню ход разбирательства, но меня смущал тот факт, что на 5.10 этой проблемы изначально не было и нет на более ранних LTS ядрах, которые имеют место быть в Manjaro (вот тот самый случай, когда несколько ядер это лютый вин).

Первая мысль: помирает железо. Тем более, что в какой-то момент стало невозможно работать с мышкой Logitech MX Master, подключенный по BT. BT на ThinkPad T530 подключен по USB. Невозможность заключалась в том, что она постоянно отваливалась и подключалась опять. Лаги очень раздражали.

Потом что-то меня навело на мысль, посмотреть на параметры модулей-драйверов USB:

  • xhci_hcd - USB 3.0 (или по модному: USB 3.2 Gen1)
  • ehci-pci - USB 2.0

Больше интересовало второе, так как все проблемные подключенцы были на USB 2.0. Выхлоп примерно такой:

$ modinfo ehci-pci  
name:           ehci_pci  
filename:       (builtin)  
license:        GPL  
file:           drivers/usb/host/ehci-pci  
author:         Alan Stern  
author:         David Brownell  
description:    EHCI PCI platform driver

И тут меня заинтересовала эта строчка:

filename:       (builtin)

Это означает, что модуль встроен в ядро. К слову, даже если он встроен, его параметры и интерфейс доступны через /sys/module/.

Решил глянуть, как обстоят дела на 5.4, а там модуль собран отдельно:

$ find /lib/modules/5.4.176-1-MANJARO/ -name '*ehci*.ko*'   
/lib/modules/5.4.176-1-MANJARO/kernel/drivers/usb/host/ehci-pci.ko.xz  
/lib/modules/5.4.176-1-MANJARO/kernel/drivers/usb/host/ehci-hcd.ko.xz  
/lib/modules/5.4.176-1-MANJARO/kernel/drivers/usb/host/ehci-fsl.ko.xz  
/lib/modules/5.4.176-1-MANJARO/kernel/drivers/usb/host/ehci-platform.ko.xz

Что это означает? Всё, что встроено в ядро может инициализировать на самых ранних стадиях старта системы, ещё на этапе работы с RAM-диском (initrd) и даже вообще до подключения корневой файловой системы. А модули уже подгружаются или с initrd образа или уже с основной файловой системы, тут как настроено.

Я уже сталкивался с ситуацией по работе, когда подобное различие влияло на работу системы. Получалось, что модуль, ответственный за работу одной железки стартовал достаточно рано, когда блок, генерирующий тактовый сигнал (“клок”) не неё ещё не был проинициализирован и завершал свою инициализацию с ошибкой. Типичное состояние гонки. Да, на этот случай предусмотрен специальный код возврата EPROBE_DEFER и он не был использован в этот раз чисто по ошибке. Но дело в том, что когда драйвер был собран модулем, то он успешно грузился на более поздних стадиях загрузки системы и клок был проинициализирован и устройство запускалось нормально.

Собственно, на этом месте, у меня, похоже, и зародилась мысль: слишком ранняя инициализация, возможно что-то не стабилизировалось и нужно попытаться как-то переинициализировать железку после старта.

Самый просто способ: вынести драйвера в виде модулей. Но пересобирать ядро не хотелось. Поэтому включил логику: почти все драйвера предлагают функционал bind/unbind. Устройства на PCI шине (по крайней мере на PCIe) могут быть пересканированы (т.е. чисто теоретически PCIe - Hot Plug, но лучше не проверяйте :)). А что станет, если после загрузки, отключить железку от драйвера и потом подключить опять? Почти всё железо содержит функционал Reset настроек в исходное состояяние, что будет если сбросить и сконфигурировать всё, когда система уже готова?

Сказано - сделано. И вы не поверите: после загрузки отключил USB контроллеры, подключил обратно и попытался сделать suspend. И сработало! Ядро 5.15 - resume заработал.

К слову, на 5.16 сломано более сильно. И WA ниже не помогает. Сижу на 5.15.

Более того, вылечился BT и мышка перестала отваливаться при работе (при работе через Logitech Unify Receiver она и так работала хорошо, но теперь и по BT стала - плюс один порт свободен).

Код под катом.

Его я поместил в /etc/rc.local:

# WA: resume from suspend freezes on kernels 5.10+, when echi-pci compiled into kernel.  
# Current Manjaro 5.4 LTS kernel compiled this module as module and suspend work well.  
(  
 # Unbing USB 3.0 controller  
 echo -n "0000:00:14.0" > /sys/bus/pci/drivers/xhci_hcd/unbind  
  
 # Unbind USB 2.0 controller  
 echo -n "0000:00:1d.0" > /sys/bus/pci/drivers/ehci-pci/unbind  
 echo -n "0000:00:1a.0" > /sys/bus/pci/drivers/ehci-pci/unbind  
  
 # Unbing USB 3.0 controller  
 echo -n "0000:00:14.0" > /sys/bus/pci/drivers/xhci_hcd/bind  
  
 # Bind USB 2.0 again  
 echo -n "0000:00:1d.0" > /sys/bus/pci/drivers/ehci-pci/bind  
 echo -n "0000:00:1a.0" > /sys/bus/pci/drivers/ehci-pci/bind  
)

У меня он обрабатывается через юнит systemd - rc-local.service, расположенный /etc/systemd/system/rc-local.service:

[Unit]  
Description=/etc/rc.local Compatibility  
ConditionPathExists=/etc/rc.local  
  
[Service]  
Type=forking  
ExecStart=/etc/rc.local start  
TimeoutSec=0  
StandardOutput=tty  
RemainAfterExit=yes  
SysVStartPriority=99  
  
[Install]  
WantedBy=multi-user.target

У вас в системе возможно уже есть что-то, что обрабатывает это без лишней писанины, ну или делайте свой юнит.

Адреса нужных PCI устройство смотрим в выводе lspci:

$ lspci -Dnn | grep USB  
0000:00:14.0 USB controller [0c03]: Intel Corporation 7 Series/C210 Series Chipset Family USB xHCI Host Controller [8086:1e31] (rev 04)  
0000:00:1a.0 USB controller [0c03]: Intel Corporation 7 Series/C216 Chipset Family USB Enhanced Host Controller #2 [8086:1e2d] (rev 04)  
0000:00:1d.0 USB controller [0c03]: Intel Corporation 7 Series/C216 Chipset Family USB Enhanced Host Controller #1 [8086:1e26] (rev 04)

Параметр -D добавляет вывод домена PCI. Ядро оперирует с полным адресом:

domain:bus:device.function

lspci, по у молчанию, выводит в форме:

bus:device.function

Это, так называемая, BDF нотация, а версия с доменом - Extended BDF Notation. Переход от BDF к Extended BDF происходит путём добавления 0000: в начало записи.

Подробную топологию можно посмотреть через lspci -tv:

$ lspci -tv  
-[0000:00]-+-00.0  Intel Corporation 3rd Gen Core processor DRAM Controller  
          +-01.0-[01]--+-00.0  NVIDIA Corporation GF108M [NVS 5400M]  
          |            \-00.1  NVIDIA Corporation GF108 High Definition Audio Controller  
          +-02.0  Intel Corporation 3rd Gen Core processor Graphics Controller  
          +-14.0  Intel Corporation 7 Series/C210 Series Chipset Family USB xHCI Host Controller  
          +-16.0  Intel Corporation 7 Series/C216 Chipset Family MEI Controller #1  
          +-16.3  Intel Corporation 7 Series/C210 Series Chipset Family KT Controller  
          +-19.0  Intel Corporation 82579LM Gigabit Network Connection (Lewisville)  
          +-1a.0  Intel Corporation 7 Series/C216 Chipset Family USB Enhanced Host Controller #2  
          +-1b.0  Intel Corporation 7 Series/C216 Chipset Family High Definition Audio Controller  
          +-1c.0-[02]--+-00.0  Ricoh Co Ltd PCIe SDXC/MMC Host Controller  
          |            \-00.3  Ricoh Co Ltd R5C832 PCIe IEEE 1394 Controller  
          +-1c.1-[03]----00.0  Intel Corporation Centrino Advanced-N 6205 [Taylor Peak]  
          +-1c.2-[04-0b]--  
          +-1d.0  Intel Corporation 7 Series/C216 Chipset Family USB Enhanced Host Controller #1  
          +-1f.0  Intel Corporation QM77 Express Chipset LPC Controller  
          +-1f.2  Intel Corporation 7 Series Chipset Family 6-port SATA Controller [AHCI mode]  
          \-1f.3  Intel Corporation 7 Series/C216 Chipset Family SMBus Controller

Рекомендую почитать: