14 июля свет увидел новый 6.10 релиз ядра Linux. Завершившийся цикл разработки выдался продуктивным и содержит много интересного. С момента предыдущего релиза прошло 2 месяца, за которые изменилось больше 12 000 файлов.
Сам Линукс подвёл итоги сообщением:
«So the final week was perhaps not quote as quiet as the preceding ones, which I don't love - but it also wasn't noisy enough to warrant an extra rc. And much of the noise this last week was bcachefs again (with netfs a close second), so it was all pretty compartmentalized.
In fact, about a third of the patch for the last week was filesystem-related (there were also some btrfs latency fixes and other noise), which is unusual, but none of it looks particularly scary. Another third was drivers, and the rest is "random".
Anyway, this obviously means that the merge window for 6.11 opens up tomorrow. Let's see how that goes, with much of Europe probably making ready for summer vacation.»
* Треть изменений пришлась на файловую систему, другая треть на драйверы
Обзор изменений в Linux 6.10
Ускорение линейных карт, userfaultfd и CFI для ARM
Для архитектуры ARM64 ускорили создание линейных карт — инструмента ядра для отображения виртуальной памяти на физическую. Линейные карты создаются при запуске. В новом релизе уменьшили количество ресурсоёмких удалений записей из TLB (кэш переводов виртуальных адресов в физические) во время генерации карт. Это сделало генерацию на 50-95% быстрее.
Теперь для ARM64 поддерживается userfaultfd, который используется для ловли вызовов mmap, munmap, mremap и fault'ов страниц памяти из userspace'а. С Linux 5.7 userfaultfd может защищать страницы от записи, ещё и работает быстрее mprotect, ведь не берёт mmap_lock и не использует тяжёлые структуры ядра, такие как vma [1].
Для ARM32 введена поддержка Clang Control Flow Integrity. Для связи между разными частями в ядре используются непрямые вызовы (Indirect Function Calls) при помощи переходов по зашитым во время компиляции адресам. CFI валидирует адреса переходов и при попытке перейти на адрес злоумышленника «наводит панику», спасая ядро.
Эти изменения поднимают производительность и безопасность смартфонов-телефонов, маршрутизаторов, встроенных систем и всего остального, что использует ARM.
BPF JIT для ARCv2 и kfuncs для PowerPC
Архитектура ARCv2 получила Just-In-Time компилятор для Berkeley Packet Filter (на хабре есть хороший цикл статей, как и на kernel.org). BPF это мощный инструмент для перехвата пакетов в сети, который используется в tcpdump, libseccomp и strace.
Архитектура PowerPC получила поддержку kfuncs — функций ядра, доступных BPF программам. Среди них функции для работы с изолированными группами процессов cgroups, обычными процессами через task_struct и CPU масками через struct bpf_cpumask.
Поддержка Rust
Идея использования Rust в ядре была темой для горячих (и не очень) обсуждений ещё с 2021 года. Расширение поддержки в 6.10 на архитектуру RISC-V (читается, кстати, как "риск 5" [2]) не может не радовать.
Ядро перешло на версию Rust 1.78.0. Это позволяет использовать alloc. Также появились абстракции для работы со временем внутри ядра.
Ускоренный AES-XTS для x86
Для архитектуры x86 через расширения VAES и AES-NI + AVX ускорили работу алгоритма AES в режиме XTS, который разработали IEEE Security для шифрования данных на жёстких (и не только!) дисках [3].
Shadow stack для подархитектуры x32
Подархитектура x32 обзавелась поддержкой теневых стеков (Shadow stacks). Это защита от атак вида stack buffer overflow (перезапись адреса возврата через переполнение буфера на стеке). Помимо обычного стека, выделяется дополнительный, закрытый от изменения пользователем. При обработке call инструкций процессор вносит адрес возврата в оба стека, а позже сравнивает адреса. При несоответствии возникает fault.
Дропнута поддержка EV5 и ниже
В новом релизе дропнута поддержка микропроцессоров EV5 и более старых серий из-за отсутствия многих стандартных функций.
Линукс прокомментировал это так: «So while it's a bit sad to see the support for my first alpha go away, if you want to run museum hardware, maybe you should use museum kernels.»
mmap и Ring буферы
Для функции mmap, создающей маппинги файлов и устройств в память, введена поддержка "кольцевых" Ring-буферов: если данные при записи переполняют буфер, то не поместившийся остаток записывается в начало.
В самом начале файловая система ещё не инициализирована и дабы не потерять логи, они записываются в кольцевой буфер в памяти, а уже позже скидываются на диск.
HugeTLBFS и THP
Бо́льшую часть функционала (8 из 10 функций) перенесли из HugeTLBFS в Transparent Hugepages, чтобы сократить время на поддержку HTLBFS как более старого решения.
HugeTLBFS разрабатывался до THP и решал проблему с выделением большого объёма памяти. На 1 гигабайт требуется больше 100,000 страниц обычного размера. Это просто забивает таблицу страниц и TLB. HugeTLBFS может выделять страницы большого размера (например, тот же 1 гигабайт), которые можно сохранять целиком в виде всего 1 записи. Но позже появилась альтернатива THP: ядро само выбирает подходящий размер страницы в зависимости от запрошенного объёма памяти, и HTLBFS остался ради совместимости и эксклюзивных функций.
NTSYNC
Для совместимости с WindowsNT появился драйвер NTSYNC, который эмулирует примитивы синхронизации. Это улучшило работу игр через Wine и стимовский Proton. Бенчмарки показывают значительный буст производительности.
Игра | Upstream | NTSYNC | Улучшение |
Anger Foot | 69 | 99 | 43% |
Call of Juarez | 99.8 | 224.1 | 125% |
Dirt 3 | 110.6 | 860.7 | 678% |
Forza Horizon 5 | 108 | 160 | 48% |
Lara Croft: Temple of Osiris | 141 | 326 | 131% |
Metro 2033 | 164.4 | 199.2 | 21% |
Resident Evil 2 | 26 | 77 | 196% |
The Crew | 26 | 51 | 96% |
Tiny Tina's Wonderlands | 130 | 360 | 177% |
Total War Saga: Troy | 109 | 146 | 34% |
mseal
Релиз 6.10 добавил функцию mseal, которая является портом флага VM_FLAGS_PERMANENT с XNU Kernel и вызова mimmutable с OpenBSD.
Сама функция доступна только для 64-битных процессоров и «запечатывает» блоки памяти через memory permissions в процессоре, предотвращая запись и использование функций mprotect, pkey_mprotect, madvice, munmap и mremap.
mseal закрывает уязвимости, которые основаны на перезаписи доверенных блоков памяти, на которые позже передаётся управление. Рантайм может автоматически защитить .text, .rodata и прочие read-only секции.
На данный момент сигнатура функции выглядит как int mseal(void addr, size_t len, unsigned long flags). Параметр flags зарезервирован на будущее. К параметрам addr и len применяются ограничения, которые описаны (как и сама функция) в статье на kernel.org.
Print'ов нет
Для вывода сообщений в ядре используется функция printk. Иногда в коде нужно просто оставить сообщение, которое можно быстро «включить» (для отладки, например). Для этого используется макро no_printk, которое не должно попадать в скомпилированный образ.
Для автоматического анализа логов используется Print Indexing. Каждый printk создаёт struct pi_entry, содержащий информацию о сообщении и самом printk. В связи с багом no_printk влиял на скомпилированный образ, ведь всё ещё генерировал indexing данные. В 6.10 это исправлено.
Включение и выключение работ в BHWQ
Workqueue — это интерфейс для управления асинхронностью, у которого есть несколько реализаций в ядре. Чтобы сделать возможной перенос программ со старого tasklet на новый BH workqueues, в релизе добавили возможность включать и выключать "работы" (work item) функциями disable_work, enable_work и их вариациями. Ядро удалит выключенную "работу" из очереди и запретит повторное добавление. Документация по workqueues и всем функциям доступна на kernel.org.
Waitqueue и Preemption для BPF
BPF-программы, которые обсуждались ранее, получили доступ к waitqueue, — механизму ожидания по условию. Через функции wait_event, wait_event_timeout, wait_event_interruptible и иже с ними, процесс может заснуть "на" определённой waitqueue до выполнения заданного условия (или завершения таймаута). Условие проверяется каждый раз, когда waitqueue "пробуждается" функциями wake_up, wake_up_all, wake_up_interruptible и другими.
Также BPF программы теперь могут включать и выключать Preemption. Когда Preemption включен, процессор может остановить выполнение на середине и отложить задачу на потом: перейти к выполнению другой задачи, а позже вернуться к изначальной.
Memory profiling для ядра
Memory профилирование помогает оптимизировать расход памяти и находить утечки. Ещё в 2023 году Сурен Багдасарян и Кент Оверстрит представили свою работу по профилированию. Вокруг неё возникло много споров. На саммите Linux Storage | Filesystem | MM | BPF 2024 вопросы уладили, и все сосредоточились на улучшении реализации. Наконец, в 6.10 подсистема попала в релиз.
Использование новой подсистемы — тема для отдельной статьи, поэтому оставлю лишь ссылки на документацию, на статью о разработке и на коммиты 1 2 3 4 5 6 7 8.
Быстрые zero-copy send операции
Теперь можно отправлять данные через сокет быстрее за счёт принципа zero-copy и кольцевых буферов, появившихся в функции io_uring. Обычно задача копирования данных из одной области в другую ложится на процессор. Zero-copy подразумевает, что процессор не копируют данные напрямую. Например, можно передать адрес сетевой карте, чтобы с копированием разбиралась она.
FD + UNIX сокеты
Ранее с отправкой файловых дескрипторов через UNIX сокеты был связан неприятный баг: при такой передаче мог возникнуть цикл референсов. Тогда закрыть сокет нельзя, ведь для этого надо очистить все референсы, что невозможно из-за цикла. Теперь же сборку мусора переработали и проблема не возникает.
Базовая поддержка PFCP
Одной из зависимостей при сборке Linux стала шапочка из фольги! Это всё, конечно, шутки. Но теперь в ядре появилась очень базовая и очень ранняя поддержка для Packet Forwarding Control протокола для общения с вышками 4G и 5G. Сейчас реализация работает только с IPv4.
SO_PEEK_OFF для TCP сокетов
Опцию SO_PEEK_OFF раньше поддерживали только UNIX-сокеты, а сейчас ещё TCP-сокеты. Она влияет на вызовы recv с флагом MSG_PEEK. Любые значения больше или равные 0 используются в recv как сдвиг. При этом сдвиг увеличивается на количество прочитанных байтов, так что в отличие от обычного поведения, с опцией SO_PEEK_OFF 2 вызова recv будут возвращать разные данные.
io_uring и IORING_CQE_F_SOCK_NONEMPTY
Для событий completion queue в io_uring появился новый флаг IORING_CQE_F_SOCK_NONEMPTY, который устанавливается, если для запроса ещё остались непрочитанные данные. Документация по io_uring и флагу доступна на Арче.
NFSDCTL
В Linux сервером для сетевой файловой системы (Network File System) служит NFSD. В целях конфигурации этого сервера в 6.10 реализовали новый Netlink протокол, а в соответствующий пакет впилили соответствующую утилиту nfsdctl. Пока через неё можно настраивать версию NFS, потоки и RPC, а также стартовать NFS-сервер.
Поддержка FS-VERITY в FUSE
Новый релиз принёс нам в userspace поддержку механизма проверки целостности fs-verity (veriTy — истина), который появился ещё в Linux v5. FS-Verity использует деревья Меркла и хэширование для нахождения случайно или намеренно повреждённых файлов. Документация есть на kernel.org.
Временные файлы в OverlayFS
OverlayFS — файловая система, объединяющая множество FS в одну, — теперь поддерживает создание временных файлов при помощи функции open с флагом O_TMPFILE.
XFS и восстановление данных
В структуру inode файловой системы XFS добавили указатель на родительскую ноду. Это открывает простор для будущей оптимизации shrub, shrink и всех других операций, которым может понадобиться быстро построить путь по inode.
FCNTL и F_DUPFD_QUERY
И опять нам в userspace насыпали новых возможностей. Среди них вот этот коммит, который добавляет функции fcntl операцию F_DUPFD_QUERY. Она проверяет, указывают ли 2 файловых дескриптора на одно и то же.
Тумблер для BLK-throttle
В 6.10 теперь можно включать и отключать механизм block throttling'а, позволявший искусственно уменьшать пропускную способность I/O. Реализация делает blk-throttle lazy-инициализируемым. Если он не настроен, то инициализация не запустится вообще, это сохраняет память и процессорное время. Соответствующий коммит доступен здесь.
Также удалена экспериментальная (с 2017 года и прямиком до сегодняшнего момента) настройка CONFIG_BLK_DEV_THROTTLING_LOW. Причина — её никто не использует.
Zstandard сжатие для EROFS
Enhanced Read-Only FileSystem обзавёлся поддержкой быстрого алгоритма сжатия Zstd, разработанного Янном Коллетом из Facebook (ныне холдинг Meta, *признан экстремистской организацией на территории РФ). Zstd балансирует между существующими алгоритмами в EROFS: его сжатие сильнее, чем у LZ4, а скорость работы выше, чем у deflate. Такое изменение улучшает производительность андроидов. Вот коммит.
High Priority для dm-crypt
Система шифрования диска dm-crypt получила новый флаг high_priority. Он управляет использованием флага WQ_HIGHPRI при работе с workqueue. Ранее использование WQ_HIGHPRI заревёртили из-за проблем с производительностью на рядовых системах, но, как оказалось, WQ_HIGHPRI даёт высокий прирост на хайэнд системах. Флаг добавлен в этом коммите.
Обновления Perf tools
Обновился встроенный профайлер ядра Perf tools. Линус смержил большое количество изменений от Арнальдо Карвалью де Мело вот в этом коммите. Изменений тут слишком много (патч весит больше 2 мегабайт), да и сами изменения достаточно сложные и требуют объяснений, потому заслуживают своей собственной статьи.
vmalloc стал ещё быстрее
В релизе 6.9 у функции выделения виртуальной памяти в ядре vmalloc была проблема: всего 1 lock. В случае, если несколько ядер процессора одновременно запрашивали память, им приходилось ждать, что замедляло работу. В новом релизе 6.10 проблему решили делением памяти на куски. Каждому ядру даётся кусок с собственным lock'ом и караванами.
Помимо этого, 1 вызов vmalloc на самом деле забирал lock дважды, что делало vmalloc медленнее на 4%. Такое поведение исправили в этом коммите и vmalloc стал работать ещё быстрее.
Замена RB деревьев на XArray в ZSwap
Реализация кэша swap-страниц со сжатием zswap перешла с красно-чёрных деревьев (RB trees) на использование типа данных XArray. Основной причиной стала проблема с постоянно возникающей необходимостью ресурсоёмкой ребалансировки RB деревьев. XArray имеет ту же самую производительность без ребалансировок. Переход уменьшил потребление памяти и избавился от лишних lookup'ов. Общий прирост производительности оказался меньше 1%. Подробное описание и бенчмарк доступны в коммите.
Шифрованая работа с TPM
Теперь работа с устройствами TPM (Trusted Platform Module), которые хранят ключи, стала намного безопаснее, а параноики стали ещё параноидальнее, благодаря целому параду коммитов 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23.
Удаление статистики crypto
Удалили фичу CONFIG_CRYPTO_STATS, которая отвечала за статистику криптографического модуля ядра. Причиной послужили неиспользуемость и отпечаток на производительности.
Статистику можно получить только через netlink и, как отмечает автор коммита, найти код, который использовал бы эту статистику, он не смог. К тому же она заметно понижала производительность (даже если статистику никто не запрашивал), особенно на системах с большим количеством ядер.
Помимо этого, подсчёт статистики занимал больше 1000 строк, распределённых между 32 файлами, и затруднял поддержку кода crypto API.
BPF получает доступ... опять!
Уже много раз упоминался Berkeley Packet Filter в этой статье. Никогда такого не было и вот опять BPF-программы получили доступ к crypto фреймворку ядра. Это изменение впилено в коммитах 1 2 3 4 и 5.
Landlock подружился с ioctl
Теперь система контроля доступа Landlock, которая используется для изоляции и создания песочниц, научилась работать с функцией ioctl, используемой для общения с устройствами и совершения устройство-зависимых операций. Это позволит сделать существующие песочницы (например, в браузерах) безопаснее.
Новая опция запуска INIT_MLOCKED_ON_FREE
В этом коммите появилась опция запуска INIT_MLOCKED_ON_FREE. Когда она указана, ядро автоматически заполняет нулями страницы памяти, на которые применили функцию mlock. Mlock запрещает переносить заданную страницу в swap.
Если процесс, который залочил mlock'ом память, крашился, то выделенная процессу память просто отмечалась свободной и передавалась нуждающимся. Это создавало угрозу для безопасности, ведь такая "legacy"-память могла содержать важную закрытую информацию.