Разработчики из компании Cloudflare рассказали о проведении работы по оптимизации производительности дискового шифрования в ядре Linux. В результате были подготовлены патчи для подсистемы dm-crypt и Crypto API, позволившие в синтетическом тесте более чем в два раза поднять пропускную способности при чтении и записи, а также в два раза снизить задержки. При тестировании на реальном оборудовании накладные расходы от шифрования удалось снизить практически до уровня, наблюдаемого при работе с диском без применения шифрования данных.
Cloudflare использует dm-crypt для шифрования данных на накопителях, используемых для кэшировании контента в CDN-сети. Dm-crypt работает на уровне блочного устройства и выполняет шифрование запросов ввода/вывода на запись и расшифровку запросов на чтение, выступая в роли прослойки между блочным устройством и драйвером файловой системы.
Для оценки производительности dm-crypt при помощи пакета Flexible I/O tester было произведено измерение скорости работы с шифрованными и незашифрованными разделами на RAM-диске, размещённом в ОЗУ для исключения флуктуаций производительности дисков и фокусировании на производительность кода. Для незашифрованных разделов производительность чтения и записи держалась на уровне 1126 MB/s, но при включении шифрования скорость снизилась в 7 раз и составила 147 MB/s.
Вначале возникло подозрение в использовании неэффективных алгоритмов в криптосистеме ядра. Но в тестах использовался наиболее быстрый алгоритм aes-xts с 256 ключом шифрования, производительность которого при выполнении «cryptsetup benchmark» более чем в два раза выше, чем полученный при тестировании RAM-диска результат. Эксперименты с флагами dm-crypt для тюнинга производительности не дали результата: при использовании флага «—perf-same_cpu_crypt» производительность даже уменьшилась до 136 MB/s, а при указании флага «—perf-submit_from_crypt_cpus» возросла лишь до 166 MB/s.
Более глубокий разбор логики работы показал, что dm-crypt не так прост как кажется — при поступлении от драйвера ФС запроса на запись, dm-crypt не обрабатывает его сразу, а помещает в очередь «kcryptd», которая разбирается не сразу, а при наступлении удобного момента. Из очереди запрос отправляется в Linux Crypto API для выполнения шифрования. Но так как Crypto API использует асинхронную модель выполнения, шифрование также производится не сразу, а минуя ещё одну очередь. После завершения шифрования dm-crypt может пытаться выполнить сортировку ожидающих запросов на запись, используя дерево поиска red-black. В конце отдельный поток ядра опять с определённой задержкой подхватывает накопившиеся запросы ввода/вывода и отправляет их в стек блочного устройства.
При чтении вначале dm-crypt добавляет в очередь «kcryptd_io» запрос на получение данных с накопителя. Через какое-то время данные становятся доступны и помещаются в очередь «kcryptd» для расшифровки. Kcryptd отправляет запрос в Linux Crypto API, которые расшифровывает информацию в асинхронном режиме. Запросы не всегда проходят по всем очередям, но в худшем сценарии запрос на запись оседает в очередях до 4 раз, а запрос на чтение до 3 раз. Каждое попадание в очередь приводит к возникновению задержек, которые и являются ключевой причиной значительного снижения производительности dm-crypt.
Применение очередей обусловлено необходимостью работы в условиях возникновения прерываний. В 2005 году, когда была реализована текущая модель работы dm-crypt на основе очередей, Crypto API ещё не был асинхронным. После перевода Crypto API на асинхронную модель выполнения стала применяться по сути двойная защита. Очереди также вводились для экономии потребления стека ядра, но после его увеличения в 2014 году данные оптимизации потеряли актуальность. Дополнительная очередь «kcryptd_io» была введена для преодоления узкого места, приводящего к ожиданию выделения памяти при поступлении большого числа запросов. В 2015 году дополнительно была введена фаза сортировки, так как запросы шифрования на многопроцессорных системах могли завершаться не соблюдая порядок отправки (вместо последовательного доступа к диску, осуществлялся доступ в случайном порядке, а также не эффективно работал планировщик CFQ). В настоящее время при использовании SSD-накопителей сортировка потеряла смысл, а планировщик CFQ уже не используется в ядре.
Учитывая то, что современные накопители стали быстрее и умнее, система распределения ресурсов в ядре Linux была пересмотрена а некоторые подсистемы переработаны, инженеры Cloudflare добавили в dm-crypt новый режим работы, избавленный от использования лишних очередей и асинхронных вызовов. Режим включается отдельным флагом «force_inline» и приводит dm-crypt к форме простого прокси, шифрующего и расшифровывающего поступающие запросы. Взаимодействие с Crypto API было оптимизировано явным выбором алгоритмов шифрования, работающих в синхронном режиме и не использующих очереди запросов. Для синхронной работы с Crypto API был предложен модуль, позволяющий использовать FPU/AES-NI для ускорения и напрямую пробрасывающий запросы шифрования и расшифровки.
В итоге удалось при тестировании RAM-диска более чем в два раза поднять производительность dm-crypt — производительность возросла с 294 MB/s (2 x 147 MB/s) до 640 MB/s, что очень близко к производительности голого шифрования (696 MB/s).
При тестировании нагрузки на реальных серверах новая реализация показала производительность очень близкую к конфигурации, работающей без шифрования, а включение шифрования на серверах с кэшем Cloudflare никак не повлияло на скорость отклика. В дальнейшем Cloudflare планирует передать подготовленные патчи в состав основного ядра Linux, но перед этим их потребуется переработать, так как они оптимизированы для определённой нагрузки и не охватывают все области применения, например, шифрования на маломощных встраиваемых устройствах.