Describe OOM session interruption and normalizer ownership precisely
This commit is contained in:
1 parent
bbe43b9842
commit
b1e94eff34
3 files changed
+36
-11
No files matched your search
@@ -324,3 +324,22 @@ only after STCP handshake. It completes the same session confirmation before INI
|
||||
REINIT notifies stream consumers without tearing down physical ownership. The
|
||||
router retains end-to-end inflight; BGP repeats JOIN_GROUP and TABLE_REQUEST because
|
||||
its previous control messages may have been discarded by the stream reset.
|
||||
|
||||
Отказы памяти и прерывание потока:
|
||||
- OOM до сохранения RX DATA в recv_q не создаёт ACK; отправитель повторяет DATA.
|
||||
- ACK означает принятие в текущий транспортный поток, а успешный etcp_send —
|
||||
передачу владения транспорту. Оба не гарантируют завершение операции сервиса
|
||||
после прерывания сессии. Надёжность операции через REINIT обеспечивает её владелец.
|
||||
- OOM после принятия данных в normalizer или при подготовке TX inflight вызывает
|
||||
fatal REINIT: новая локальная эпоха, блокировка DATA, отбрасывание старых очередей,
|
||||
частичных кодограмм и физических попыток. Старые payload не воспроизводятся.
|
||||
Физические линки сохраняются; очистка очередей/reset не выделяет память.
|
||||
- Активный обработчик освобождает собственный извлечённый фрагмент ровно один раз;
|
||||
после reset/close из callback он прекращает разбор старой кодограммы/пакета.
|
||||
- Согласование новой эпохи выполняется после очистки. OOM control-пакета оставляет
|
||||
повтор активным, без продления исходного срока 10с. Отказ обязательного session-
|
||||
таймера или генерации локальной эпохи закрывает соединение с диагностикой.
|
||||
Если невозможно выделить даже обязательный deferred-close token внутри callback,
|
||||
процесс завершается через abort: соединение не оставляется в зависшем состоянии.
|
||||
- После подтверждения эпох владельцы получают REINIT и восстанавливают операции
|
||||
своего протокола; при невозможности согласования соединение закрывается.
|
||||
@@ -15,7 +15,8 @@ struct ETCP_CONN;
|
||||
#define ETCP_SESSION_HEADER_SIZE 17
|
||||
#define ETCP_SESSION_CONTROL_SIZE 25
|
||||
|
||||
/* Запустить обязательное подтверждение; при таймауте неподтверждённое соединение закрывается. */
|
||||
/* Запустить обязательное подтверждение: повтор через 500мс, общий срок 10с от start.
|
||||
* OOM control-пакета оставляет повтор активным; OOM таймера или истечение срока закрывает соединение. */
|
||||
void etcp_session_start(struct ETCP_CONN* conn);
|
||||
/* Предложить ненулевую эпоху пира для challenge; это ещё не разрешает приём DATA новой эпохи. */
|
||||
void etcp_session_observe(struct ETCP_CONN* conn, uint64_t peer_epoch);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Packet Normalizer (фрагментация/дефрагментация)
|
||||
|
||||
## 1. Назначение
|
||||
Модуль адаптирует произвольные IP-пакеты к передаче через ETCP-канал с ограниченным MTU. Состоит из двух половин:
|
||||
Модуль адаптирует кодограммы сервисов к передаче через ETCP-канал с ограниченным MTU. Содержимое кодограммы непрозрачно для normalizer. Состоит из двух половин:
|
||||
|
||||
- **Packer** (упаковщик) — разбивает входящие пакеты на фрагменты размером `frag_size` и помещает их в `etcp->input_queue`. Фрагменты собираются в общий буфер пачками для экономии накладных расходов, сброс по таймеру (call_soon) при пустой входной очереди.
|
||||
- **Unpacker** (распаковщик) — собирает фрагменты из `etcp->output_queue` обратно в исходные пакеты по заголовку длины (2 байта LE), помещает результат в `pn->output` (который читает ETCP router/клиент).
|
||||
|
||||
Каждое ETCP-соединение имеет свой экземпляр `PKTNORM` (создаётся в `etcp.c:247`).
|
||||
Каждое ETCP-соединение владеет своим экземпляром `PKTNORM`.
|
||||
|
||||
## 2. Как пользоваться
|
||||
|
||||
@@ -32,13 +32,18 @@ queue_data_put(pn->input, entry);
|
||||
```
|
||||
|
||||
### Формат фрагментов
|
||||
Каждый фрагмент, уходящий в ETCP, содержит конкатенацию пакетов, каждый предварён 2-байтовым LE-заголовком длины:
|
||||
Поток содержит конкатенацию кодограмм, каждая предварена 2-байтовым LE-заголовком длины:
|
||||
```
|
||||
[packet1_len:2 LE][packet1_data][packet2_len:2 LE][packet2_data]...
|
||||
```
|
||||
Поток разделяется на фрагменты ETCP: данные одной кодограммы могут продолжаться в следующем фрагменте. Заголовок длины packer записывает целиком; максимально допустимая длина кодограммы — `PKTNORM_MAX_DGRAM_SIZE`.
|
||||
|
||||
### Сброс состояния
|
||||
При разрыве/переподключении вызывать `pn_reset()`. При ошибке сборки (некорректный заголовок) вызывается `etcp_conn_reinit()` — полный перезапуск соединения.
|
||||
Владелец вызывает `pn_reset()` при сбросе ETCP-потока. Он отменяет flush/waiter, очищает обе очереди normalizer и частичные буферы без выделения памяти. Сам `pn_reset()` не запускает handshake.
|
||||
|
||||
Неверный заголовок/длина и OOM после принятия данных вызывают `etcp_conn_fatal_reinit()`: новая локальная эпоха, очистка старого потока и согласование с пиром при сохранённых физических линках. Старые кодограммы не воспроизводятся; после согласования владельцы получают REINIT и восстанавливают операции своего протокола. До сохранения RX DATA в `recv_q` OOM обрабатывает сам ETCP: отбрасывает фрагмент без ACK.
|
||||
|
||||
Normalizеr не удерживает отдельную OOM-очередь или retry-таймер. После reset/close из пользовательского callback обработчик прекращает старый разбор; извлечённый им фрагмент остаётся его собственностью до однократного освобождения. Счётчик активных callbacks откладывает освобождение normalizer при закрытии.
|
||||
|
||||
## 3. API
|
||||
|
||||
@@ -50,7 +55,7 @@ queue_data_put(pn->input, entry);
|
||||
|------|----------|
|
||||
| `input` | Входная очередь packer (`pn_input`) — сюда кладут пакеты для отправки |
|
||||
| `output` | Выходная очередь unpacker (`pn_output`) — отсюда читают собранные пакеты |
|
||||
| `frag_size` | Максимальный размер одного фрагмента ETCP (`mtu - ACK_REZERV - UDP_HDR - UDP_SC_HDR`) |
|
||||
| `frag_size` | Размер DATA-фрагмента: MTU минус IP/UDP overhead, `SC_ENCRYPT_OVERHEAD`, `ETCP_PAYLOAD_HEADER_SIZE` и `ACK_REZERV`; overhead учитывает IPv6-линки |
|
||||
| `data` / `data_ptr` / `data_size` | Буфер-накопитель packer (из `data_pool`) |
|
||||
| `recvpart` | Частично собранный пакет unpacker |
|
||||
| `flush_timer` | Таймер сброса накопленного буфера (call_soon) |
|
||||
@@ -66,7 +71,7 @@ queue_data_put(pn->input, entry);
|
||||
|
||||
**`pn_deinit(pn)`** — освобождает все ресурсы: очереди, буферы, таймеры, waiter-ы. Вызывает `routing_del_conn()` (deprecated, no-op). Снимает callback с `etcp->output_queue` для предотвращения use-after-free.
|
||||
|
||||
**`pn_reset(pn)`** — сбрасывает состояние packer и unpacker (буферы, pending, recvpart, таймер). Используется при переподключении.
|
||||
**`pn_reset(pn)`** — отбрасывает `input`/`output`, буферы packer/unpacker, pending и recvpart; отменяет flush и waiter. Не выделяет память, допускает NULL; используется владельцем при сбросе потока. Обычные callbacks очередей сохраняются.
|
||||
|
||||
**`pn_unpacker_reset_state(pn)`** — сбрасывает только unpacker (recvpart).
|
||||
|
||||
@@ -74,17 +79,17 @@ queue_data_put(pn->input, entry);
|
||||
|
||||
### Внутренние callback-и
|
||||
|
||||
**`packer_cb`** — callback на `pn->input`. Ждёт освобождения `etcp->input_queue` через `queue_waiter_wait`, затем вызывает `etcp_input_ready_cb`.
|
||||
**`packer_cb`** — callback на `pn->input`. Пока сессия согласуется или закрывается, обработку не запускает. Ждёт освобождения `etcp->input_queue` через `queue_waiter_wait`, затем вызывает `etcp_input_ready_cb`. Отказ ожидания вызывает fatal REINIT.
|
||||
|
||||
**`etcp_input_ready_cb`** — основной цикл packer. Извлекает пакет из `pn->input`, записывает 2-байтовый заголовок длины, копирует данные в буфер `pn->data`. При заполнении буфера (или если осталось <3 байт) вызывает `pn_send_to_etcp`. Если входная очередь пуста — ставит `flush_timer` через `uasync_call_soon` для сброса хвоста.
|
||||
|
||||
**`pn_flush_cb`** — таймерный callback, вызывает `pn_send_to_etcp` при простое.
|
||||
|
||||
**`pn_send_to_etcp(pn)`** — выделяет `ETCP_FRAGMENT` из `io_pool`, передаёт в него накопленный буфер `pn->data`, отправляет в `etcp->input_queue`. Буфер после этого обнуляется (передан во владение фрагмента).
|
||||
**`pn_send_to_etcp(pn)`** — выделяет `ETCP_FRAGMENT` из `io_pool`, передаёт в него накопленный буфер `pn->data`, отправляет в `etcp->input_queue`. Указатель normalizer обнуляется до синхронного callback очереди. Возвращает 0 при успехе/пустом буфере, -1 при ошибке или прерывании сессии; ошибку выделения/постановки обрабатывает fatal REINIT.
|
||||
|
||||
**`pn_unpacker_cb`** — callback на `etcp->output_queue`. В цикле извлекает фрагменты, парсит 2-байтовые заголовки длины, копирует данные в `pn->recvpart`. При полной сборке пакета помещает его в `pn->output`. При ошибке вызывает `etcp_conn_reinit`.
|
||||
**`pn_unpacker_cb`** — callback на `etcp->output_queue`. Извлекает фрагменты, парсит длины, копирует данные в `pn->recvpart`. Перед выдачей целой кодограммы в `pn->output` снимает `recvpart` и обновляет статистику. OOM, неверный заголовок или отказ очереди вызывают fatal REINIT. После каждого пользовательского callback проверяет reset_count/close и прекращает старый разбор при изменении сессии.
|
||||
|
||||
**`pn_buf_renew(pn)`** — выделяет новый буфер из `data_pool` если `pn->data == NULL`. При нехватке места (<3 байт) вызывает `pn_send_to_etcp` для сброса текущего буфера.
|
||||
**`pn_buf_renew(pn)`** — выделяет буфер из `data_pool`, если `pn->data == NULL`; при нехватке места (<3 байт) сначала вызывает `pn_send_to_etcp`. Возвращает 0 при успехе, -1 при ошибке/прерывании. OOM вызывает fatal REINIT.
|
||||
|
||||
### Зависимости
|
||||
- `etcp.h` / `etcp_api.h` — структуры `ETCP_CONN`, `ETCP_FRAGMENT`, `etcp_int_recv`
|
||||
|
||||
Reference in new issue
Block a user