Современный процессор знает о себе такое, чего не видно ни в одном файле /proc: какое ядро вот-вот упрется в лимит мощности, какое остывает и готово принять работу, а какое переварит фоновый поток за половину ватт. Intel Hardware Feedback Interface, коротко HFI, отдает эти подсказки операционной системе напрямую: аппаратура сама заполняет таблицу возможностей и сообщает ядру, что данные обновились. С релиза 5.18 в Linux появился драйвер intel_hfi, который читает таблицу и раздает ее значения планировщику и тепловой подсистеме. Для гибридных процессоров с производительными и эффективными ядрами разница ощутима: фоновые задачи уходят на экономичные ядра, быстрые освобождаются для интерактивной нагрузки, а частоты перестают прыгать вслепую. Ниже разобраны устройство таблицы, регистры, через которые она обновляется, проверка поддержки на живой машине и числовые примеры того, как планировщик распоряжается подсказками.

Обратный канал от процессора к планировщику ядра

Годами ядро судило о возможностях процессора по статичным источникам: частотным таблицам, маркерам модели, данным из ACPI. Пока все ядра чипа были равными, а бюджет тепла оставался заботой кулера, этого хватало. Гибридные схемы перевернули расклад: в одном пакете соседствуют быстрые и экономичные ядра, доступная производительность плавает вместе с температурой, лимитом мощности и характером нагрузки, и прошитая однажды таблица стремительно устаревает. Intel Hardware Feedback Interface закрывает разрыв живым каналом связи: процессор сам измеряет свое состояние и периодически перезаписывает таблицу возможностей в памяти, доступной операционной системе. Опрашивать ничего не требуется, обновление приходит асинхронно, и ядро не тратит циклы на постоянные запросы. Роли разделены честно: железо отвечает за факты, а решение, куда поставить поток, по-прежнему принимает планировщик Linux. Для администратора это означает, что странное на первый взгляд поведение задач на свежих процессорах Intel складывается в логичную картину, а не в набор совпадений.

Устройство таблицы возможностей, два числа в строке и регистры MSR

Таблица HFI живет в обычной оперативной памяти. Ядро выделяет буфер и записывает его физический адрес в регистр IA32_HW_FEEDBACK_PTR, включение обновлений выполняется через соседний регистр IA32_HW_FEEDBACK_CONFIG. Дальше процессор работает сам: дополняет таблицу свежими значениями и сигнализирует об обновлении битом 26 в регистре состояния температуры пакета IA32_PACKAGE_THERM_STATUS, откуда сигнал подхватывает обработчик прерывания. Три адреса, на которых держится вся механика, выглядят так:

IA32_HW_FEEDBACK_PTR       0x17d0   адрес буфера таблицы в памяти
IA32_HW_FEEDBACK_CONFIG    0x17d1   включение и настройка обновлений
IA32_PACKAGE_THERM_STATUS  0x1b1    бит 26 сообщает о свежих данных

Смысл таблицы прост: строки привязаны к составу процессорного пакета, и в каждой строке два восьмибитных числа. Первое называется performance capability и показывает, сколько запаса производительности у ядра осталось прямо сейчас. Второе, energy efficiency, оценивает выгодность ядра с точки зрения расхода энергии. Шкала у обоих одинаковая, от 0 до 255, чем больше, тем лучше. Условный фрагмент для гибридного чипа с быстрыми и экономичными ядрами выглядит так:

строка  ядро    производительность  эффективность
0       P       140                 70
1       P       138                 72
8       E       210                 235
9       E       204                 228

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

У реальной машины числа живые: они меняются вслед за температурой и нагрузкой, и темп обновлений процессор выбирает сам, ускоряясь там, где условия меняются быстро. На гибридных моделях классификатор Thread Director добавляет к таблице разбиение по классам потоков, но базовая структура с двумя числами на строку сохраняется. Таблица общая для всего процессорного пакета, а на серверных образцах с несколькими кристаллами у каждого кристалла своя копия.

Появление драйвера intel_hfi в ядре 5.18 и поддержка оборудования

Драйвер intel_hfi вошел в ядро 5.18, выпущенное в мае 2022 года, и поселился среди тепловых драйверов Intel под опцией сборки CONFIG_INTEL_HFI_THERMAL. Первым железом с поддержкой интерфейса стали гибридные процессоры Alder Lake, двенадцатое поколение Core, где рядом с быстрыми ядрами трудятся экономичные, а классификатор Thread Director раскладывает потоки по классам. Позже механизм добрался до серверных чипов и перекочевал в следующие поколения настольных и мобильных моделей. Драйвер встроен в тепловую подсистему: он слушает то же прерывание пакета, которое сообщает о перегреве и температурных порогах, и при обновлении таблицы пересчитывает приоритеты ядер для планировщика, в том числе для механизма ITMT, ранжирующего ядра по предпочтительности. Подхватывается драйвер автоматически, как только процессор объявляет поддержку канала, а последующие релизы ядра поэтапно достраивали связь таблицы с решениями о размещении, поэтому на свежем ядре картина точнее, чем на 5.18. Собирается он и модулем, и прямо в ядро, способ сборки зависит от вкусов дистрибутива.

До появления драйвера ранжирование ядер бралось из статичных таблиц ACPI: предпочтительные ядра были известны раз и навсегда, и никакая жара или лимит мощности их не двигали. Таблица HFI сделала картину подвижной: оценки приходят живыми, и планировщик реагирует на состояние чипа здесь и сейчас, а не по паспорту.

Проверка поддержки HFI на работающей системе

Проверка на живой машине укладывается в короткий маршрут:

  1. убедиться, что ядро не старее 5.18, командой uname -r;
  2. найти флаг hfi в строке flags файла /proc/cpuinfo;
  3. сверить опцию сборки CONFIG_INTEL_HFI_THERMAL в конфиге загруженного ядра;
  4. убедиться, что драйвер на месте, модулем или встроенным в ядро;
  5. при желании прочитать адрес буфера из MSR и понаблюдать за событиями через netlink.

Первые три шага занимают секунды:

uname -r
grep -m1 flags /proc/cpuinfo | grep -o hfi
grep CONFIG_INTEL_HFI_THERMAL /boot/config-$(uname -r)

На подходящей машине вывод будет таким:

6.1.0-18-amd64
hfi
CONFIG_INTEL_HFI_THERMAL=m

Пустой ответ второй команды означает, что процессор канала не имеет либо он выключен в прошивке, и идти дальше незачем: ядро не увидит таблицы, на что ни намекай параметрами. Отсутствие опции сборки лечится обновлением ядра или пересборкой с включенным CONFIG_INTEL_HFI_THERMAL. Если опция собрана модулем, проверка привычна:

lsmod | grep intel_hfi
modprobe intel_hfi

На дистрибутивах, где драйвер встроен в ядро, lsmod честно вернет пустоту, и это не поломка. Последний штрих, адрес зарегистрированного буфера, читается из MSR утилитой rdmsr из пакета msr-tools:

modprobe msr
rdmsr 0x17d0

Пример вывода на активном интерфейсе:

3c6d5000

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

Обновления через тепловую подсистему и netlink-события

Место прописки драйвера выбрано не случайно: канал HFI обслуживается той же тепловой подсистемой ядра, которая и так следит за температурой пакетов. Прерывание, сообщающее о перегреве и порогах, приносит и сигнал об обновлении таблицы, поэтому данные HFI текут в ядро по уже проложенному руслу, без новых путей и лишних копий. Наверх, в пространство пользователя, обновления уходят через generic netlink семейство thermal: ядро рассылает события с индексами ядер, а слушатели решают, что с ними делать. Наличие семейства подтверждает genl:

genl ctrl list | grep -i thermal

В исходниках ядра есть готовая заготовка для наблюдения, файл mon.c в каталоге tools/thermal/thermal-genl. Его собирают одной командой и запускают с правами администратора:

gcc -O2 -o mon mon.c
./mon

Монитор печатает приходящие тепловые события, и среди них мелькают обновления таблицы HFI со строками индексов. Для прикидки этого достаточно: видно, как оценки ядер дышат вслед за нагрузкой, стоит лишь запустить сборку проекта или тест производительности в соседнем терминале. Такой же слушатель несложно написать и самостоятельно, формат событий стабилен от релиза к релизу, а семейство thermal служит для тепловых уведомлений общим входом.

Числовая модель выбора ядра по индексам эффективности

Как подсказки превращаются в решения, удобнее всего показать на упрощенной модели. Пусть у быстрого ядра максимальная емкость равна 1024 условным единицам, у экономичного 512, и в какой-то момент таблица выдала быстрому ядру performance capability 140, а экономичному 210. Запас производительности считается пропорцией: 1024 умножить на 140 и разделить на 255, выходит около 562 единиц для быстрого ядра; 512 умножить на 210 и разделить на 255, около 422 единиц для экономичного. Фоновая задача с утилизацией 307 из 1024, то есть примерно 30 процентов, помещается в оба ядра, и тогда решает второй столбец: эффективность экономичного ядра 235 против 70 у быстрого, выигрыш в 235 делить на 70, примерно в 3,4 раза. Планировщик отправляет поток на экономичное ядро, а быстрое держит свободным для интерактива. Совсем другая история с задачей, требующей 900 единиц: ни 562, ни 422 ее не вместят, и она остается на быстром ядре, как бы соблазнительны ни были чужие индексы. Третий случай, две фоновые задачи по 200 единиц каждая: суммарно 400 едва влезают в экономичное ядро с запасом всего в 22 единицы, и одну из них планировщик уносит на быстрое, потому что толкучка на одном ядре съедает выигрыш по энергии.

Существенная деталь: индексы не приказ, а подсказка. Ядро взвешивает их против требований потока, его приоритета и родства к ядрам, поэтому модель рисует логику решения, а не дословный код планировщика. Реальные емкости ядер Linux вычисляет тоньше, с поправками на частоты и классы Thread Director, но пропорция решения остается той же.

Практическая польза для тюнинга и границы механизма

Практика начинается с переключателя ITMT: значение 1 в файле /proc/sys/kernel/sched_itmt_enabled разрешает планировщику опираться на ранжирование ядер, которое пополняет и драйвер HFI. Проверка занимает секунду:

cat /proc/sys/kernel/sched_itmt_enabled

Фоновым задачам помогает честный пониженный приоритет: команда renice 19 -p с номером процесса сдвигает поток в хвост очередей, и индексы эффективности дожимают его на экономичное ядро. Посмотреть, где поток живет сейчас, можно одной командой:

ps -o pid,psr,comm -p 4321

Число в столбце PSR и есть номер текущего ядра, и наблюдение за ним во время фоновой нагрузки наглядно показывает переброску задач между быстрыми и экономичными ядрами. Запускать фоновое задание сразу с низким приоритетом удобнее на старте: nice -n 19 make -j4 ставит сборку в самый деликатный режим с первой секунды, и планировщику не приходится догонять задним числом. Экономию измеряют через RAPL, счетчик энергии пакета доступен в perf, измерение идет с правами администратора:

perf stat -a -e power/energy-pkg/ -- sleep 60

Значение в джоулях за минуту до и после переноса фоновой нагрузки на экономичные ядра дает измеримую разницу без домыслов. Типичный кейс: сервер по ночам собирает пакеты и гонит резервное копирование, процессор при этом долго сидит в средних частотах, и перенос этих задач на экономичные ядра снижает расход энергии заметнее, чем любые частотные игры. Границы у механизма тоже есть. HFI не управляет частотами, этим занимается intel_pstate, он не отменяет требований задач и не работает на процессорах без канала, а в виртуальных машинах гостю интерфейс обычно не пробрасывается. И все же механизм стоит знать в лицо: когда планировщик делает движения, кажущиеся странными, таблица HFI чаще всего оказывается тем тихим советчиком, который за кадром подсказывает, куда поставить поток, чтобы чип работал и быстро, и экономно.