|
1 | | -# 00 Overview: F-Stack User-Space Stack + Local Kernel Stack COEXISTENCE (compile-macro gated + per-fd marker selection + unified events) |
| 1 | +# 00 Overview: F-Stack User-Space Stack + Local Kernel Stack AUTOMATIC DUAL-STACK COEXISTENCE (compile-macro gated + default dual-stack + marker single-stack override + unified events) |
2 | 2 |
|
3 | 3 | > **Document ID**: SPEC-KE-00 |
4 | | -> **Version**: v5 (compile-macro gating: `FF_KERNEL_COEXIST` off by default + runtime `kernel_coexist` dual-layer switch) |
| 4 | +> **Version**: v6 (**native automatic dual-stack coexistence** paradigm: with no marker, one `ff_socket` builds BOTH the F-Stack and kernel stacks, dual-drives both, unified events; markers override to single-stack, compatible with v5; still gated by the `FF_KERNEL_COEXIST` compile macro + runtime `kernel_coexist` dual-layer switch) |
5 | 5 | > **Date**: 2026-06-17 |
6 | | -> **Status**: Drafting |
| 6 | +> **Status**: Drafting (v6 design) |
7 | 7 | > **Scope**: Navigation, terminology, and scope statement for the spec in this directory |
8 | 8 |
|
9 | 9 | --- |
10 | 10 |
|
11 | | -## 0. v5 Compile-Macro Gating Background (must read) |
| 11 | +## 0. v6 Background: from "per-fd either/or" to "automatic dual-stack" (must read) |
12 | 12 |
|
13 | | -v4 already landed coexistence (app ON F-Stack + per-fd `SOCK_KERNEL` to the kernel stack + unified events). But all v4 coexistence code in `lib/` is **compiled unconditionally**, so even deployments not using coexistence link it in and cannot guarantee byte-for-byte zero regression vs. upstream F-Stack. |
| 13 | +v5 (commit ba148589d) already gated all `lib/` coexistence code under the compile macro `FF_KERNEL_COEXIST` (`lib/Makefile` off by default), forming a **compile-time + runtime dual-layer switch**, and measured byte-for-byte zero regression when off and per-fd coexistence usable when on (real-machine perf in `10`). The v5 semantics are **per-fd either/or**: |
14 | 14 |
|
15 | | -**v5 new requirement**: gate ALL `lib/` coexistence code under a single compile macro `FF_KERNEL_COEXIST`, **commented off by default in `lib/Makefile`**, forming a **compile-time + runtime dual-layer switch**: |
| 15 | +- `ff_socket(type|SOCK_KERNEL)` → builds **only** a kernel fd, returns `ff_kernel_fd_encode(host_fd)` (≥`FF_KERNEL_FD_BASE`); |
| 16 | +- otherwise → builds **only** an F-Stack fd (raw fd). |
16 | 17 |
|
17 | | -- **Compile-time (`FF_KERNEL_COEXIST`)**: undefined → all coexistence code (managed kernel-fd bridge, fd discrimination, `ff_epoll` merge, `ff_socket` kernel branch, per-`ff_*` routing, config `kernel_coexist`, `SOCK_FSTACK/SOCK_KERNEL` macros) is **not compiled**; `libfstack.a` is **byte-for-byte zero regression** vs. upstream. Defined (`make FF_KERNEL_COEXIST=1` or uncomment) → compiled in. |
18 | | -- **Runtime (config `kernel_coexist`)**: **only when the macro is enabled**, `config.ini [stack] kernel_coexist=1` actually enables per-fd `SOCK_KERNEL` to use the kernel stack; default `=0` stays per-fd F-Stack. |
19 | | -- **Opt-in impact**: `ff_api.h` `SOCK_FSTACK`/`SOCK_KERNEL` macros are also wrapped by `FF_KERNEL_COEXIST`, so a consumer (APP) must likewise define the macro to see these markers — the reasonable semantics of "off by default, explicitly enabled". |
| 18 | +**v6 upgrades the default semantics to "automatic dual-stack"**: when `FF_KERNEL_COEXIST` is compiled in **and** `config.ini [stack] kernel_coexist=1` — |
20 | 19 |
|
21 | | -**The correct paradigm**: within **one F-Stack application process**, business connections use the **F-Stack user-space stack (DPDK + FreeBSD)**, while fds carrying the `SOCK_KERNEL` marker use the **host Linux kernel stack**, and the two **coexist in a single event loop**, gated by the compile macro `FF_KERNEL_COEXIST` (off by default). This is exactly what F-Stack's two existing implementations do: |
22 | | -- the `FF_KERNEL_EVENT` compile mode of `adapter/syscall` (hook/LD_PRELOAD); |
23 | | -- nginx's `kernel_network_stack` config switch. |
| 20 | +- **Default (no marker) = dual-stack**: one `ff_socket` builds BOTH an F-Stack fd and a kernel host fd, registers `ff_native_fd_map[fstack_fd]=host_fd`, and **returns the F-Stack raw fd**; `ff_bind/ff_listen/ff_close/ff_connect` etc. **dual-drive both stacks** on that dual-stack fd — **one `listen(80)` listens on both the F-Stack (DPDK) and Linux kernel stacks simultaneously**. Local `curl 127.0.0.1:80` and remote access to `:80` via the DPDK NIC are both reachable, with no marker required. |
| 21 | +- **Marker single-stack override (compatible with v5)**: `type|SOCK_KERNEL` = kernel only; `type|SOCK_FSTACK` = F-Stack only. |
| 22 | +- **Zero regression**: macro off **or** `kernel_coexist=0` **or** `SOCK_FSTACK` → byte-for-byte zero regression (the original F-Stack path). |
24 | 23 |
|
25 | | -This feature = **solidify that coexistence capability as the primary baseline (hook mode)** + **add unified-event coexistence to the native `ff_api` mode**, gated by the `FF_KERNEL_COEXIST` compile macro, rather than building a side path that bypasses F-Stack. |
| 24 | +> **Implementation-status note (anti-speculation)**: v6's native map `ff_native_fd_map` and the default dual-build/dual-drive logic are **NOT yet landed in `lib/`** (grep confirms `lib/` has no `ff_native_fd_map`). This directory's v6 docs are the **upgraded design**; they explicitly distinguish "v5 already measured/landed" from "v6 to-be-implemented design" and must not present any v6 behavior as a fait accompli. |
26 | 25 |
|
27 | | -> **v3 history**: v3 routed `ff_socket(SOCK_KERNEL)` to `ff_host_socket()` → a raw host `socket()`, completely bypassing the F-Stack user-space stack (a fundamental mistake), reverted in v4 and rewritten to the paradigm above. |
| 26 | +> **v3 historical mistake (reverted)**: v3 routed `ff_socket(SOCK_KERNEL)` to `ff_host_socket()` → raw host `socket()`, completely bypassing F-Stack — a fundamental mistake, reverted (commit 0748eff94). v6's "dual-stack" is never a bypass: F-Stack is always built and always present; the kernel stack is a **parallel additional** second stack. |
| 27 | +
|
| 28 | +--- |
28 | 29 |
|
29 | 30 | ## 1. One-Sentence Goal |
30 | 31 |
|
31 | | -Let an F-Stack application, **while running its business fast path on the F-Stack user-space stack**, route some sockets/listens/connects to the host kernel stack on a per-fd basis (so local `ping`/`curl`/`ssh` can directly reach its kernel-stack services, and the app as a client can `connect` via the kernel stack to local/external kernel services), with both stacks' fds sent/received in the **same epoll/event loop** — **F-Stack is always present and is never replaced by a side path**. |
| 32 | +Let an F-Stack application be **dual-stack by default**: the same socket/listen works on BOTH the F-Stack user-space stack (DPDK+FreeBSD, business fast path) and the host Linux kernel stack — local `ping`/`curl`/`ssh` reach the kernel-stack side, remote peers reach the F-Stack side via the DPDK NIC, and both stacks' events are sent/received in the **same epoll/event loop** — **F-Stack is always present, never replaced by a side path**; use `SOCK_KERNEL`/`SOCK_FSTACK` markers to override to single-stack when needed. |
32 | 33 |
|
33 | 34 | ## 2. Scope Statement (Important) |
34 | 35 |
|
35 | | -- **This feature = dual-stack coexistence**: F-Stack user-space stack (business, default) + host kernel stack (per-fd `SOCK_KERNEL`), in the same process and event loop. |
36 | | -- **Selection method**: per-fd `SOCK_KERNEL`/`SOCK_FSTACK` markers (default F-Stack); one **coexistence-capability switch** in config.ini (whether to enable kernel-stack coexistence, without changing the default per-fd F-Stack semantics). |
37 | | -- **Hook mode (primary baseline, already supported)**: directly reuse `FF_KERNEL_EVENT` — `ff_hook_socket` marker-based selection + `fstack_kernel_fd_map` dual-stack epoll merge; this round solidifies it and provides a correct coexistence demo. |
38 | | -- **Native `ff_api` mode (new design)**: `ff_epoll_*` is currently a pure kqueue wrapper with no kernel-fd awareness; this round adds, inside lib, an fd-ownership table + a kernel-epoll mirror + an `ff_epoll_wait` merge of kqueue⊕epoll, so natively-linked applications can also coexist over both stacks in one process. |
| 36 | +- **This feature = automatic dual-stack coexistence**: with no marker, F-Stack + kernel dual-stack, dual-drive, unified events; markers override to single-stack. |
| 37 | +- **Compile-macro gating (outermost)**: all coexistence code is gated by `FF_KERNEL_COEXIST`, **commented off by default** in `lib/Makefile`; when off, coexistence code is not compiled and is byte-for-byte zero regression vs. upstream. |
| 38 | +- **Runtime switch**: `config.ini [stack] kernel_coexist` (effective only when the macro is on), `=1` enables automatic dual-stack, `=0` degrades to pure F-Stack (zero regression). |
| 39 | +- **Selection markers**: `SOCK_KERNEL` (kernel only) / `SOCK_FSTACK` (F-Stack only) override the default dual-stack. |
| 40 | +- **Hook mode (cross-reference baseline)**: `adapter/syscall`'s `FF_KERNEL_EVENT` already supports "app ON F-Stack + epoll dual-build merge + close linkage", **but socket/listen are NOT dual-built** (kernel listen requires explicit `SOCK_KERNEL`). The native v6 divergence = socket/bind/listen/connect **are also auto dual-built / dual-driven**. |
39 | 41 | - **Explicitly excluded**: |
40 | | - - Do **not** create a side socket that bypasses F-Stack (the v3 `ff_host_socket` approach is deprecated). |
41 | | - - Do **not** add an anti-F-Stack "whole-process default-to-kernel" global switch (the v3 `default_stack=kernel` is deprecated). |
42 | | - - Do **not** create a new `ff_local_*` dual API / mTCP-like dual namespace. |
43 | | - - Do **not** do gazelle-style thread-level selection (the F-Stack multi-process model relies on different config files). |
| 42 | + - Do **not** create a side socket that bypasses F-Stack (v3 `ff_host_socket` raw bypass deprecated). |
| 43 | + - Do **not** add an anti-F-Stack "whole-process default-to-kernel" switch (v3 `default_stack=kernel` deprecated). |
| 44 | + - Do **not** create an `ff_local_*` dual API / mTCP-like dual namespace. |
| 45 | + - Do **not** do gazelle-style thread-level selection (F-Stack multi-process relies on different config files). |
44 | 46 | - Do **not** adopt KNI/`rte_kni`/virtio-user packet reinjection (boundary clarification only). |
45 | 47 |
|
46 | 48 | ## 3. Reading Path |
47 | 49 |
|
48 | 50 | | Order | Document | Purpose | |
49 | 51 | |---|---|---| |
50 | | -| 1 | `plan.md` | Plan, team, gate, rework paradigm | |
51 | | -| 2 | `01-requirements-spec.md` | Requirements and goals/non-goals (coexistence) | |
52 | | -| 3 | `02-current-state-analysis.md` | Current state of hook FF_KERNEL_EVENT / nginx kernel_network_stack / native event layer (code is authoritative) | |
53 | | -| 4 | `03-external-research.md` | External solution research (with URLs) | |
54 | | -| 5 | `04-architecture-design.md` | Coexistence architecture, dual-stack unified events, bidirectional data flow | |
55 | | -| 6 | `05-interface-design.md` | Marker/config contracts, hook and native dual-mode adaptation | |
56 | | -| 7 | `06-milestones.md` | Milestones and coding work list | |
57 | | -| 8 | `07-test-spec.md` | Test and performance-baseline plan | |
58 | | -| 9 | `08-review-gate.md` | Review gate conclusion | |
| 52 | +| 1 | `01-requirements-spec.md` | Requirements and goals/non-goals (auto dual-stack + marker single-stack override) | |
| 53 | +| 2 | `02-current-state-analysis.md` | v5 measured state + native vs hook isomorphism/divergence table + v6 map gap | |
| 54 | +| 3 | `03-external-research.md` | External research (with URLs, low-trust corroboration) | |
| 55 | +| 4 | `04-architecture-design.md` | Dual-drive data flow + fd tri-state routing + dual-stack events + map | |
| 56 | +| 5 | `05-interface-design.md` | socket/bind/listen/connect/accept/close/epoll_* dual-stack contracts + marker semantics + connect draft + exception matrix | |
| 57 | +| 6 | `06-milestones.md` | R6→R7 automatic dual-stack milestones | |
| 58 | +| 7 | `07-test-spec.md` | cmocka dual-mode cases + real-machine dual-stack plan | |
| 59 | +| 8 | `08-review-gate.md` | Gate conclusion (v6 R7 "pending measurement") | |
| 60 | +| 9 | `09-impl-plan.md` | Per-file rework steps | |
| 61 | +| 10 | `10-perf-baseline-report.md` | No-regression criteria of dual-stack on the F-Stack fast path | |
59 | 62 |
|
60 | 63 | ## 4. Glossary |
61 | 64 |
|
62 | 65 | | Term | Meaning | |
63 | 66 | |---|---| |
64 | | -| F-Stack stack | DPDK PMD + user-space FreeBSD protocol stack (business fast path, **default stack**) | |
65 | | -| Kernel stack | Host Linux kernel network protocol stack (local/management/client connecting to local or external kernel services) | |
66 | | -| Coexistence | F-Stack fds and kernel fds present **in the same process and the same event loop**, each using its own stack | |
67 | | -| Stack-selection marker | `SOCK_KERNEL`(0x02000000)/`SOCK_FSTACK`(0x01000000) on socket `type`, per-fd | |
68 | | -| Coexistence-capability switch | A config.ini switch for whether to enable kernel-stack coexistence (does not change the default per-fd F-Stack semantics) | |
69 | | -| fd ownership | The marker at creation fixes whether the fd belongs to F-Stack or the kernel; subsequent syscalls/events route by ownership (`is_fstack_fd`/`CHECK_FD_OWNERSHIP`) | |
70 | | -| Unified events | External epoll style, internally merging F-Stack kqueue events + kernel epoll events | |
71 | | -| Hook mode | LD_PRELOAD takes over the POSIX API (`ff_hook_*`) + `FF_KERNEL_EVENT`; coexistence already supported | |
72 | | -| Native mode | The app directly calls `ff_*` (`ff_api.h`) + the `ff_run` main loop; this round adds unified-event coexistence | |
| 67 | +| F-Stack stack | DPDK PMD + user-space FreeBSD protocol stack (business fast path, **always present**) | |
| 68 | +| Kernel stack | Host Linux kernel network protocol stack (local/management/client to local or external kernel services) | |
| 69 | +| **Automatic dual-stack (v6)** | With no marker, one `ff_socket` builds both an F-Stack fd and a kernel host fd; each `ff_*` dual-drives both stacks | |
| 70 | +| **Dual-stack fd (v6)** | The F-Stack raw fd returned to the app, mapped to one kernel host fd in `ff_native_fd_map` | |
| 71 | +| **native map (v6, to-be-implemented)** | `ff_native_fd_map[fstack_fd]=host_fd` (`ff_host_interface.c`, 65536 entries), modeled on adapter `fstack_kernel_fd_map` | |
| 72 | +| Managed kernel fd (kernel-only) | `ff_kernel_fd_encode(host_fd)` (≥`FF_KERNEL_FD_BASE`=0x40000000), returned by `SOCK_KERNEL` or a kernel-side accepted connection; no map entry | |
| 73 | +| `FF_KERNEL_COEXIST` | **Compile macro** gating whether all coexistence code is compiled; off by default (commented) in `lib/Makefile` (compile-time switch) | |
| 74 | +| Selection marker | `SOCK_KERNEL`(0x02000000)/`SOCK_FSTACK`(0x01000000) on socket `type`, single-stack override of the default dual-stack (wrapped by `FF_KERNEL_COEXIST`, opt-in) | |
| 75 | +| Coexistence-capability switch | `config.ini [stack] kernel_coexist`, **runtime** enabling of automatic dual-stack (effective only when the macro is on) | |
| 76 | +| fd tri-state routing | `ff_is_kernel_fd(fd)`(≥BASE)=kernel only; else the F-Stack path, and if `ff_native_map_get(fd)>0` also dual-drive host_fd | |
| 77 | +| Unified events | External epoll style; internally merges F-Stack kqueue events + kernel epoll events (a dual-stack listen is registered once on each stack) | |
| 78 | +| Hook mode | LD_PRELOAD takes over the POSIX API (`ff_hook_*`) + `FF_KERNEL_EVENT`; epoll dual-build merge, socket/listen NOT dual-built | |
| 79 | +| Native mode | The app directly calls `ff_*` (`ff_api.h`) + the `ff_run` main loop; v6 implements automatic dual-stack | |
73 | 80 |
|
74 | 81 | ## 5. Sources |
75 | 82 |
|
76 | | -- F-Stack actual code (`adapter/syscall/`, `app/nginx-1.28.0/`, `lib/`) — **highest priority; on conflict, code is authoritative**. |
| 83 | +- F-Stack actual code (`lib/`, `adapter/syscall/`, `app/nginx-1.28.0/`) — **highest priority; on conflict, code is authoritative**. |
77 | 84 | - F-Stack three-tier architecture docs and knowledge graph (`docs/`), `adapter/syscall/README.md`. |
78 | | -- Public external materials (GitHub/technical blogs, etc.), all with accessible URLs in `03`. |
| 85 | +- Public external materials (GitHub/technical blogs, etc.), all with accessible URLs in `03`, **low-trust corroboration only**. |
0 commit comments