Диагностируя скачки задержки в высокоскоростных сетевых соединениях, администраторы традиционно анализируют транзитные узлы, джиттер беспроводных каналов или буферы маршрутизаторов. Однако в операционных системах Linux значительная часть паразитной задержки может возникать непосредственно в локальном ядре до того, как пакеты попадут в сетевой интерфейс. Это явление, известное как локальный bufferbloat на стороне хоста, исторически приводило к деградации интерактивного трафика при наличии параллельных объемных загрузок.

Для устранения многомегабайтных очередей в драйверах без снижения пропускной способности разработчик ядра Linux Эрик Дюмазе (Eric Dumazet) внедрил механизм TCP Small Queues (TSQ). Устанавливая динамический лимит байтов на уровне каждого сокета между транспортным уровнем и дисциплиной очереди (qdisc), TSQ кардинально изменил организацию сетевого конвейера. Понимание принципов TSQ необходимо инженерам, проектирующим зашифрованные оверлеи, сетевые шлюзы и высоконагруженные VPN-соединения.

Механика локального Bufferbloat до внедрения TSQ

До выхода ядра Linux 3.6 стек TCP передавал данные сетевым адаптерам с максимальной скоростью, допускаемой окном перегрузки (snd_cwnd). Пока в буфере сокета оставалось свободное место, транспортный уровень упаковывал байты в структуры sk_buff и немедленно сбрасывал их в подсистему управления трафиком (qdisc).

При передаче крупных массивов данных такая архитектура приводила к накоплению сотен и тысяч непереданных пакетов в кольцевом буфере драйвера сетевой карты. Один активный поток TCP мог легко удерживать от 2 МБ до 8 МБ данных в очередях оперативной памяти в ожидании сериализации по каналу связи.

Такая избыточная буферизация вызывала три критические проблемы:

  1. Искусственное увеличение RTT: Пакеты простаивали десятки и сотни миллисекунд в локальной очереди хоста, искажая вычисление RTT и замедляя реакцию алгоритмов контроля перегрузки.
  2. Блокировка очереди при потерях: Если в сети происходила потеря пакета, мегабайты устаревших данных в буфере драйвера все равно должны были передаться перед тем, как пакет повторной передачи мог выйти на линию.
  3. Вытеснение чувствительного трафика: Интерактивные сессии SSH, DNS-запросы и голосовые пакеты VoIP застревали позади огромных пакетов объемных передач.

Хотя алгоритмы активного управления очередями (AQM), такие как CoDel и FQ-CoDel, успешно устраняют очереди на уровне интерфейса, они теряют эффективность, если транспортный уровень выбрасывает в очередь гигантские пачки данных. Требовался регулятор непосредственно на границе стека TCP.

Принцип динамического регулирования в TCP Small Queues

TCP Small Queues реализует детерминированную обратную связь между завершением физической передачи пакета и генерацией новых пакетов сокетом. Вместо восприятия интерфейса как бесконечного буфера, TSQ отслеживает точный объем непереданных байтов для каждого отдельного соединения.

Контроль счетчика sk_wmem_alloc

Каждый буфер sk_buff, выделенный сокетом, увеличивает счетчик распределенной памяти sk->sk_wmem_alloc. Этот счетчик учитывает полезную нагрузку, служебные заголовки и метаданные страниц ядра. Когда сетевой адаптер или виртуальный интерфейс TUN отправляет кадр в канал, прерывание драйвера освобождает память структуры (kfree_skb), уменьшая значение sk_wmem_alloc.

Перед сегментацией или отправкой новых данных стек TCP проверяет, не превышает ли текущий объем очереди лимит TSQ:

static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb, unsigned int factor)
{
    unsigned int limit = max(2 * skb->truesize, sk->sk_pacing_shift);
    limit = min_t(unsigned int, limit, sysctl_tcp_limit_output_bytes);
    
    if (atomic_read(&sk->sk_wmem_alloc) > limit) {
        set_bit(TSQ_THROTTLED, &tcp_sk(sk)->tsq_flags);
        return true; /* Приостановка передачи */
    }
    return false;
}

Если значение sk_wmem_alloc превышает лимит, ядро устанавливает бит TSQ_THROTTLED для данного сокета и прекращает передачу новых пакетов вниз по конвейеру. Сокет уступает квант времени процессора, предотвращая переполнение очередей драйвера.

Пробуждение сокета через обратный вызов прерывания

Когда драйвер сетевой карты завершает физическую отправку кадра и освобождает память через skb_destroy(), ядро проверяет флаг TSQ_THROTTLED. Если сокет был приостановлен, планировщик задач процессора (tsq_tasklet) немедленно вызывает обработчик tcp_tsq_handler().

Этот обработчик сбрасывает флаг TSQ_THROTTLED и вызывает процедуру tcp_write_xmit(), отправляя следующую порцию данных. Так как цикл пробуждения выполняется на аппаратной скорости за доли миллисекунды, передача сохраняет максимальную скорость линии, а локальная очередь пакетов остается минимальной.

Синергия TSQ с контролем темпа (Pacing) и планировщиком Sch_FQ

Наивысшая производительность TSQ достигается при совместной работе с контролем темпа отправки (pacing) и планировщиком честных очередей (sch_fq). Pacing распределяет передачу пакетов равномерно на протяжении расчетного интервала RTT, устраняя всплески трафика при получении подтверждений ACK.

По умолчанию ядро Linux масштабирует порог TSQ на основе текущей расчетной скорости сокета:

  • Для соединения 1 Гбит/с объем трафика за 1 миллисекунду составляет около 125 КБ.
  • Для канала 100 Мбит/с лимит автоматически сужается примерно до 12,5 КБ.

Благодаря этому TSQ динамически адаптирует глубину очереди к пропускной способности конкретного сетевого маршрута. В сочетании с sch_fq каждое соединение получает изолированную виртуальную очередь минимальной длины, предотвращая джиттер между параллельными сессиями.

Влияние на оверлейные сети и виртуальные интерфейсы TUN

Проблема локального bufferbloat представляет повышенную опасность в архитектуре виртуальных частных сетей. В туннелях (таких как WireGuard или OpenVPN) исходящий зашифрованный трафик проходит через виртуальное сетевое устройство (tun0 или wg0), прежде чем попасть в физический интерфейс Ethernet или Wi-Fi.

Риск двойной буферизации

Без механизма TSQ виртуальные туннели сталкиваются с эффектом наложения буферов:

  1. Буферизация на виртуальном интерфейсе: Внутренний поток TCP переполняет очередь устройства tun пакетами, ожидающими шифрования.
  2. Буферизация на физическом адаптере: Внешний сокет UDP заполняет кольцевой буфер физической сетевой карты инкапсулированными датаграммами.

Такая многоуровневая задержка может увеличить задержку с 20 мс до более чем 350 мс при интенсивной передаче данных. Любая потеря пакета во внешнем канале приводит к блокировке повторных передач во внутреннем туннеле.

Оптимизация задержки в RiceGuard

В современной сетевой инфраструктуре RiceGuard обработка пакетов глубоко интегрирована с учетом памяти сокетов в ядре Linux. За счет согласования внутренних параметров MTU с границами сегментации и строгого соблюдения лимитов TSQ виртуальные дескрипторы туннеля не накапливают избыточных данных.

В сочетании с протоколом Core L3 VPN и архитектурой передачи с нулевым копированием (zero-copy), TSQ позволяет пакетам голосовой связи и онлайн-игр опережать массивные потоки скачивания файлов за считанные микросекунды, обеспечивая минимальную задержку.

Практический мониторинг и настройка параметров ядра

Системные администраторы Linux могут проверять и настраивать параметры TSQ с помощью стандартных сетевых утилит.

Проверка глобального лимита TSQ

Максимальный объем данных, который сокет может удерживать в очередях интерфейса, настраивается через подсистему sysctl:

# Просмотр текущего глобального лимита TSQ (по умолчанию 262144 байта / 256 КБ)
sysctl net.ipv4.tcp_limit_output_bytes

# Установка лимита 128 КБ для туннельных шлюзов с повышенными требованиями к задержке
sudo sysctl -w "net.ipv4.tcp_limit_output_bytes = 131072"

Диагностика активности TSQ через Socket Statistics

Утилита ss позволяет в реальном времени отслеживать срабатывание механизма TSQ для активных соединений:

# Проверка детальных метрик TCP для соединений по порту 443
ss -tin '( dport = :443 or sport = :443 )'

Ключевые показатели в выводе команды:

  • wmem_alloc: Объем памяти в буфере ожидания передачи сетевой картой.
  • tsq: Указывает на то, что сокет в данный момент ограничен механизмом TCP Small Queues.
  • pacing_rate: Скорость отправки пакетов, рассчитанная алгоритмом перегрузки.
  • bytes_acked: Суммарный объем успешно подтвержденных данных.

Если показатель wmem_alloc стабильно удерживается ниже 150 КБ при максимальной скорости загрузки, TSQ эффективно предотвращает образование очередей.

Заключение

TCP Small Queues представляет собой фундаментальное архитектурное улучшение сетевого стека Linux. Ограничивая накопление пакетов непосредственно на транспортном уровне, TSQ надежно устранил застарелую проблему локального bufferbloat на хостах.

В инфраструктуре современных VPN-соединений TSQ превращает виртуальные адаптеры из узких мест в эффективные каналы передачи данных с минимальной задержкой. В связке с честной диспетчеризацией очередей TSQ обеспечивает стабильную одновременную работу тяжелых загрузок и интерактивных сервисов реального времени.