Document per-path attempt ACKs, loss timers, pacing and memory bounds
This commit is contained in:
1 parent
b442d131c9
commit
7bb43c42d6
2 files changed
+64
-38
No files matched your search
+49
-32
@@ -13,48 +13,55 @@ HELLO/CHALLENGE/CONFIRM и keepalive передаются своим незав
|
||||
2. Выбрать инициализированный UP-линк с готовым shaper и местом для целого DATA:
|
||||
inflight_bytes + длина DATA <= inflight_lim_bytes. Проверка выполняется без переполнения.
|
||||
Среди допустимых выбирается минимум inflight_bytes, при равенстве — round-robin.
|
||||
Для повтора обход начинается после last_link самого фрагмента: первый готовый другой канал,
|
||||
прежний канал — последним. Чужие DATA/ACK не меняют курсор фрагмента; удаления линка обнуляют его до free.
|
||||
3. Если DATA не помещается, он остаётся в очереди; для накопленных ACK выбирается
|
||||
рабочий линк без ограничения DATA-cwnd/pacing. ACK-only не увеличивает нагрузку DATA-shaper;
|
||||
тот же линк, который принял соответствующий DATA, без ограничения DATA-cwnd/pacing.
|
||||
Кумулятивный till можно отправить по любому рабочему пути. ACK-only не увеличивает нагрузку DATA-shaper;
|
||||
физическая очередь канала остаётся общей, её накопление ограничивают DATA-cwnd/pacing BBR.
|
||||
4. Выделить ETCP_DGRAM до удаления DATA из очереди и изменения счётчиков.
|
||||
Отправляемый DATA занимает окно выбранного линка и переходит в input_wait_ack.
|
||||
Выделить отдельную физическую попытку с новым attempt_id и зарезервировать окно выбранного линка.
|
||||
Только успешное принятие транспортом фиксирует время, last_link и перенос DATA в input_wait_ack.
|
||||
Ошибка send снимает резерв; логический DATA остаётся готовым без сетевого loss sample.
|
||||
Пакет содержит ACK, опциональные timestamp/измерительные секции и последнюю секцию PAYLOAD.
|
||||
5. В активной ограниченной burst-серии pacing обходится; DATA всё равно должен
|
||||
помещаться в окно, иначе серия отправляет FILLER. ACK/FILLER окно DATA не занимают.
|
||||
При приёме пакета (от etcp_connections):
|
||||
- последовательно сканируем секции и отдаём их на обработку нужным обработчикам:
|
||||
- ack (0x01): помечаем в inflight пакеты как подтверждённые и проставляем время подтверждения. также снимаем блокировку отправки
|
||||
- ack (0x01): attempt_id подтверждает физическую попытку только принявшего ACK линка;
|
||||
seq отдельно подтверждает логическую доставку. till освобождает логические записи, без BBR/снятия чужого flight.
|
||||
- timestamp (0x06): обновляем last RTT, пересчитываем RTT avg10 и jitter по каждому линку; обновляем recv_dt_avg для раздельного измерения времени передачи/приёма
|
||||
- [0x00] payload: добавляем пакет в нужное место сборочного linked-list (сверяем по ID если дубликат - игнорируем). также добавляем ACK запись в ack_q для отправки.
|
||||
- [0x00] payload: сохраняем фрагмент в recv_q либо распознаём уже сохранённый/доставленный seq.
|
||||
Только после этого ставим ACK с seq+attempt_id в ack_q принявшего линка; OOM до сохранения не подтверждается.
|
||||
после сканирования проверяем появились ли в linked-list собранные данные которые можно переместить в выходную очередь. и перемещаем если есть.
|
||||
|
||||
пересканирование inflight:
|
||||
- запускается при добавлении пакета в очередь ожидания ACK (wait_ack_cb). устанавливается таймер на первый неистекший пакет.
|
||||
- возраст последней отправки сравнивается с (RTT_avg10*32+jitter*32)/16,
|
||||
ограниченным диапазоном 100..10000 таймбаз (10ms..1s).
|
||||
RTT_avg10 — историческое имя максимального RTT среди links; jitter — среднее
|
||||
по links значение link->jitter/32768. Оба значения здесь в единицах 0.1ms.
|
||||
При текущих коэффициентах формула равна 2*(RTT_avg10+jitter), включая запас.
|
||||
Проверка таймером назначается с дополнительной 1ms и округлением uasync вверх
|
||||
до миллисекунды; фактический callback может выполняться позже расчётного RTO.
|
||||
- истёкший пакет освобождает окно прежнего линка и переходит в input_send_q;
|
||||
его новая попытка проходит обычный допуск по окну и pacing выбранного линка.
|
||||
- очередь FIFO (сортирована по timestamp добавления = времени отправки), сканирование прекращается на первом неистекшем пакете
|
||||
- по истечению таймера (ack_timeout_cb) снова вызывается проверка очереди
|
||||
Таймер попыток каждого линка (etcp_tx.c):
|
||||
- RTO = clamp(2*(data_rtt_us+data_jitter_us), 10000, 1000000) мкс.
|
||||
RTT использует собственный DATA ACK минус receiver_delay; EWMA RTT=1/8, jitter=1/4 |RTT-EWMA|.
|
||||
Первый RTT задаёт jitter=RTT/2. До DATA ACK используется RTT keepalive этого же пути, без него RTO=1с.
|
||||
- FIFO содержит физические попытки в порядке принятия транспортом. Таймер назначается по старейшей;
|
||||
при изменении RTO проверяется новый срок. Допускается оставить уже запланированную более раннюю проверку.
|
||||
uasync округляет задержку вверх до миллисекунды; callback может выполниться позже расчётного срока.
|
||||
- Истечение снимает только собственный физический flight и сообщает loss собственному BBR.
|
||||
Если логический seq ещё не доставлен и других активных копий нет, он возвращается в input_send_q.
|
||||
Уже доставленный seq не возвращается, даже если его физическая копия истекла позже.
|
||||
|
||||
inflight - две очереди (ll_queue):
|
||||
- input_send_q: список пакетов ожидающих отправку
|
||||
- input_wait_ack: список неподтвержденных пакетов ожидающих ack
|
||||
При отправке пакет перемещается из input_send_q в input_wait_ack.
|
||||
При ретрансмиссии пакет перемещается из input_wait_ack обратно в input_send_q.
|
||||
При принятии отправки транспортом пакет перемещается из input_send_q в input_wait_ack.
|
||||
При необходимости повтора пакет перемещается из input_wait_ack обратно в input_send_q.
|
||||
доп. свойства пакетов (struct INFLIGHT_PACKET):
|
||||
- указатель на ETC_LINK последней отправки (last_link)
|
||||
- timestamp последней отправки (last_timestamp)
|
||||
- число отправок (send_count)
|
||||
- send_hist[8] - история через какие линки отправлялся (индекс линка)
|
||||
- inflight_bytes/inflight_packets линка равны сумме длин/числу WAIT_ACK с этим last_link.
|
||||
WAIT_SEND окно не занимает, в том числе после RTO. last_link сохраняет историю попытки.
|
||||
Поздний ACK удаляет WAIT_SEND без повторного вычитания; duplicate ACK учёт не меняет.
|
||||
- send_hist[8] — local_link_id последних попыток, только диагностика
|
||||
- last_attempt_id и active_attempts — последняя отправка и число ещё учитываемых физических копий
|
||||
- Payload принадлежит только логической записи. Попытка ETCP_TX_ATTEMPT хранит seq и неизменные снимки, без payload.
|
||||
inflight_bytes/inflight_packets линка равны сумме DATA-длин/числу записей в его attempts, включая резерв send.
|
||||
ACK/till может уже удалить логический объект, пока другая физическая копия продолжает занимать своё окно.
|
||||
Повторный ACK неизвестной/истёкшей попытки не вычитает flight и не создаёт BBR sample.
|
||||
ACK известного ID с другим seq отклоняется целиком; неизвестный ID допускает только логическое подтверждение seq.
|
||||
Удаление линка обнуляет last_link в обеих очередях до освобождения его памяти.
|
||||
Общий optimal_inflight равен сумме лимитов всех линков, с насыщением UINT32_MAX;
|
||||
это дополнительный backpressure подачи новых фрагментов, не замена индивидуальных окон.
|
||||
@@ -63,6 +70,11 @@ inflight - две очереди (ll_queue):
|
||||
Selective ACK освобождает отдельную inflight-запись, но не продвигает эту границу:
|
||||
приёмник пока не может освободить пакеты за дыркой. Кумулятивный ACK возобновляет
|
||||
input_queue даже если подтверждённые записи уже удалены по selective ACK.
|
||||
При заполненном sequence-окне собственный таймер запрашивает актуальный till секцией ACK_REQUEST.
|
||||
Получатель отвечает через запрашивающий путь, даже без ожидающих DATA/ACK-элементов.
|
||||
При продвижении frontier, reset/close таймер отменяется. Это восстанавливает поток при потере последнего till.
|
||||
Память физических попыток ограничена: 16384 на линк, 32768 на соединение; исчерпание блокирует DATA до освобождения.
|
||||
Очередь ACK каждого линка ограничена 16384 записями; переполнение логируется, DATA остаётся сохранённым для повтора/till.
|
||||
ACK ограничивается последним назначенным seq (next_tx_id - 1); сравнения учитывают uint32 wrap.
|
||||
BBR каждого линка получает ACK/loss samples и возвращает cwnd и pacing rate.
|
||||
Скорость — доставленные байты за max(интервал отправки, интервал доставки), по снимкам при отправке.
|
||||
@@ -73,7 +85,9 @@ BBR каждого линка получает ACK/loss samples и возвра
|
||||
PROBE_RTT осушает flight до max(4*MSS, 0.5*BDP), выдерживает >=200мс и раунд ACK, затем восстанавливает окно.
|
||||
MSS — размер DATA-фрагмента normalizer без заголовков; начальное окно UDP и STCP равно 4*MSS с верхним лимитом конфига.
|
||||
В PROBE_BW действует дополнительный целевой бюджет очереди 20мс: всё окно, включая резервы ACK и PROBE_UP,
|
||||
ограничивается max(4*MSS, BDP + BW*20мс) по min_rtt. STARTUP/DRAIN не ограничиваются этим бюджетом.
|
||||
ограничивается max(4*MSS, BDP + BW*20мс) по min_rtt. STARTUP/DRAIN не ограничивают окно этой формулой,
|
||||
но целый раунд RTT выше min_rtt+бюджет завершает STARTUP и переводит его в DRAIN.
|
||||
Shaper списывает только DATA-байты: единицы совпадают с delivery sample и pacing BBR.
|
||||
Это цель по оценкам BBR; минимум окна, изменение полосы и очереди обратного пути могут давать большую задержку.
|
||||
Отказ из-за нехватки окна сохраняет cwnd_limited до следующего ACK: вычитание ACK
|
||||
не должно скрывать этот факт от BBR. app_limited учитывает ожидающие очереди и
|
||||
@@ -99,14 +113,15 @@ pending/частичный буфер normalizer; ограничение окн
|
||||
Формат и подтверждение эпох описаны в разделе «Подтверждение транспортной сессии» ниже.
|
||||
2. Опциональные секции (одна или несколько) добавляются в etcp.c внутри DATA/ACK после заголовка сессии:
|
||||
а) Подтверждения (ACK) - заголовок 0x01:
|
||||
[0x01][count][last_delivered_id(4 байта LE)][rx_dup_count(2 байта LE)][[seq(4 LE)][recv_ts(2 LE)][dly(2 LE)] × count]
|
||||
- count: количество ACK записей (1 байт; до 32 ограничено размером пакета)
|
||||
[0x01][count][last_delivered_id(4 байта LE)][rx_dup_count(2 байта LE)][[seq(4 LE)][attempt_id(8 LE)][delay_us(4 LE)] × count]
|
||||
- count: количество ACK записей (1 байт; до 255, дополнительно ограничено MTU и оставшимся местом)
|
||||
- last_delivered_id: uint32_t — последний ID, доставленный получателю (по этот ID всё собрано подряд)
|
||||
- rx_dup_count: uint16_t — счётчик принятых дубликатов (локальный, для вычисления tx_dup_count на удалённой стороне)
|
||||
- seq: uint32_t — номер подтверждаемого пакета
|
||||
- recv_ts: uint16_t — локальный timestamp приёма пакета (в единицах 0.1 мс)
|
||||
- dly: uint16_t — задержка между приёмом пакета и отправкой ACK (для расчёта RTT)
|
||||
Каждая запись = 8 байт.
|
||||
- attempt_id: uint64_t — ID конкретной физической попытки DATA; возвращается только тем же путём
|
||||
- delay_us: uint32_t — задержка между приёмом DATA и формированием ACK, мкс; UINT32_MAX исключает RTT sample
|
||||
Каждая запись = 16 байт, база ACK = 8 байт; count=0 допустим для передачи till.
|
||||
ACK_REQUEST: [0x0a], одна секция длиной 1 байт, запрос повторной отправки актуального till.
|
||||
в) channel timestamp (добавляется после выбора канала передачи, если есть свежие данные):
|
||||
[0x06] [RET_Timestamp(2 байта LE)] [RECV_Rel_Timestamp(2 байта LE)]
|
||||
- RET_Timestamp: uint16_t = last_recv_timestamp + (now - last_recv_local_time)
|
||||
@@ -114,12 +129,14 @@ pending/частичный буфер normalizer; ограничение окн
|
||||
- RECV_Rel_Timestamp: uint16_t = last_recv_local_time - last_recv_timestamp
|
||||
(относительное смещение между локальным и удалённым временем)
|
||||
Всего 5 байт. Добавляется только если last_recv_updated=1 (был принят пакет с момента последней отправки)
|
||||
и если прошло не более ~100ms (dt < 1000000 таймбаз).
|
||||
и если прошло менее 100с (dt < 1000000 таймбаз). Секция не является источником DATA RTT для BBR/RTO.
|
||||
3. Полезная нагрузка - заголовок 0x00 (одна секция и всегда последняя):
|
||||
[0x00] [ID 4 байта LE] [данные...]
|
||||
[0x00] [ID 4 байта LE] [attempt_id 8 байт LE] [данные...]
|
||||
- ID: uint32_t, 32-битный циклический номер пакета
|
||||
- attempt_id: uint64_t, монотонный ненулевой ID; не сбрасывается при reset или пересоздании линка.
|
||||
Переполнение закрывает соединение. Заголовок PAYLOAD занимает 13 байт; при повторе attempt_id новый, seq прежний.
|
||||
Пакет может содержать несколько опциональных секций (например, ACK + channel_timestamp + данные),
|
||||
всегда в порядке: ACK, channel_timestamp, payload; payload всегда последний.
|
||||
Порядок: ACK, возможный ACK_REQUEST, измерительные/временные секции и FILLER, затем payload последним.
|
||||
Разбор секций — последовательное сканирование с известными размерами (нет явного поля длины секции).
|
||||
|
||||
цель протокола:
|
||||
|
||||
@@ -39,11 +39,16 @@ bbr_tx_start(&bbr_state); // вызывать при возобновлени
|
||||
```
|
||||
|
||||
### Интеграция с ETCP_LINK
|
||||
BBR создаётся в `etcp_link_new()` как `link->bbr`. `etcp_ack_recv()` передаёт новые ACK и измерительный sample.
|
||||
Sample использует снимки delivered и времени доставки/отправки, сохранённые в INFLIGHT_PACKET.
|
||||
BBR создаётся в `etcp_link_new()` как `link->bbr`. `etcp_tx_ack()` передаёт sample только известной попытки своего линка.
|
||||
Sample использует неизменные снимки delivered и времени доставки/отправки из `ETCP_TX_ATTEMPT`.
|
||||
DATA несёт `seq+attempt_id`, ACK возвращает их и задержку формирования ACK тем же каналом.
|
||||
RTT попытки равен времени до ACK минус эта задержка. Неизвестный/истёкший ID не обновляет BBR/RTT или физическое окно.
|
||||
`etcp_ack_recv()` освобождает только логическую запись; кумулятивный till не создаёт BBR samples.
|
||||
Интервал — максимум времени отправки и подтверждения; ACK одной пачки не создают искусственную сверхскорость.
|
||||
`acked_sacked` увеличивает общий счётчик, `delivered` описывает весь интервал и может включать предыдущие ACK.
|
||||
Окно применяется через `etcp_link_update_inflight_lim`, pacing — к исходящим DATA.
|
||||
Окно применяется через `etcp_link_update_inflight_lim`, точный pacing B/s — через `etcp_loadbalancer_set_pacing_rate`.
|
||||
Shaper списывает DATA-байты, без заголовков и piggyback ACK. При смене ставки остаток долга пересчитывается;
|
||||
сохраняется не более 1мс накопленного кредита для компенсации округления таймера, без неограниченного burst после idle.
|
||||
BBR определяет минимум 4*MSS; транспорт ограничивает окно только сверху. `INFLIGHT_LIM_MIN=8192`
|
||||
задаёт допустимый минимум настройки максимального окна, а не текущее окно.
|
||||
PROBE_RTT осушает flight до `max(4*MSS, 0.5*BDP)`, выдерживает не менее 200мс и раунд ACK, затем восстанавливает окно.
|
||||
@@ -60,10 +65,14 @@ ACK-only не ограничивается этим pacing, но проходи
|
||||
в uasync-потоке, `0` отключает политику. `queue_cwnd_cap` показывает действующий предел в DATA-байтах;
|
||||
`0` означает, что ограничение не активно. Callback окна получает уже ограниченный результат, включая fast path.
|
||||
|
||||
Это целевая задержка по оценке полосы, а не гарантия физического времени очереди. STARTUP и DRAIN
|
||||
сохраняют свои правила поиска полосы/осушения. Минимум 4*MSS может требовать больше 20мс сериализации;
|
||||
В STARTUP минимум достоверных RTT целого ACK-раунда сравнивается с `min_rtt+queue_budget_us`.
|
||||
Устойчивое превышение завершает разгон и включает DRAIN; одиночный пик или отключённый бюджет этого не делает.
|
||||
Первый достоверный RTT задаёт начальный pacing по окну `4*MSS`, независимо от размера первого служебного DATA.
|
||||
|
||||
Это целевая задержка по оценке полосы, а не гарантия физического времени очереди. Окно STARTUP и DRAIN
|
||||
не ограничивается формулой бюджета. Минимум 4*MSS может требовать больше 20мс сериализации;
|
||||
при неизвестном min_rtt или нулевой оценке полосы дополнительный предел не применяется.
|
||||
Изменение пропускной способности и другой путь возврата ACK могут делать оценку неточной.
|
||||
Изменение пропускной способности, jitter, обратная очередь и потери ACK могут делать оценку неточной.
|
||||
После изучения полосы меньший предел может ограничивать скорость; реальную очередь отдельно измеряет multipath-тест.
|
||||
|
||||
### Тестирование
|
||||
|
||||
Reference in new issue
Block a user