Skip to content

Commit c6f5918

Browse files
committed
feat(stack-coexist): route readv/writev/ioctl/dup to kernel stack under FF_KERNEL_COEXIST
Add ff_host_readv/writev/ioctl/dup/dup2 bridges and route managed kernel fds through them (ioctl uses the raw Linux request). select/poll stay kernel-fd-unaware by design (use epoll/kqueue). All macro-gated, macro-off byte-identical; adds cmocka tests and syncs zh/en spec + three-layer docs.
1 parent 03f244a commit c6f5918

36 files changed

Lines changed: 642 additions & 79 deletions

docs/01-LAYER1-ARCHITECTURE.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ NIC Hardware
5555
│ ├── ff_dpdk_if.c (2907 lines) # DPDK NIC interface layer - most critical
5656
│ ├── ff_glue.c (1467 lines) # FreeBSD glue layer
5757
│ ├── ff_config.c (1694 lines) # Configuration parsing
58-
│ ├── ff_syscall_wrapper.c (2212 lines) # Linux→FreeBSD system call adaptation (+ R9 kqueue coexist + IPV6_V6ONLY)
59-
│ ├── ff_host_interface.c (583 lines) # Host interface (pthread/mmap/time) + FF_KERNEL_COEXIST bridges (27 ff_host_*)
58+
│ ├── ff_syscall_wrapper.c (2265 lines) # Linux→FreeBSD system call adaptation (+ R9 kqueue coexist + IPV6_V6ONLY + R10 readv/writev/ioctl/dup/dup2 kernel-fd routing)
59+
│ ├── ff_host_interface.c (617 lines) # Host interface (pthread/mmap/time) + FF_KERNEL_COEXIST bridges (32 ff_host_*)
6060
│ ├── ff_init.c (70 lines) # Initialization coordination
6161
│ ├── ff_epoll.c (289 lines) # epoll → kqueue conversion (unified F-Stack + kernel)
6262
│ ├── ff_dpdk_kni.c # Virtual NIC support (via virtio_user, no longer depends on rte_kni.ko)
@@ -119,10 +119,10 @@ NIC Hardware
119119
| **ff_dpdk_if.c** | 2907 | NIC driver/DPDK operations/core TX/RX logic | DPDK, ff_glue |
120120
| **ff_glue.c** | 1467 | FreeBSD kernel emulation/memory/locks/interrupts (8-category 14.0+ ABI fixes at M4) | FreeBSD headers, DPDK |
121121
| **ff_config.c** | 1694 | INI configuration file parsing | ff_ini_parser |
122-
| **ff_syscall_wrapper.c** | 2212 | Linux system call → FreeBSD adaptation (sockaddr update at M4; FF_KERNEL_COEXIST routing; R9 kqueue coexist + IPV6_V6ONLY) | FreeBSD sys |
122+
| **ff_syscall_wrapper.c** | 2265 | Linux system call → FreeBSD adaptation (sockaddr update at M4; FF_KERNEL_COEXIST routing; R9 kqueue coexist + IPV6_V6ONLY; R10 readv/writev/ioctl/dup/dup2 kernel-fd routing) | FreeBSD sys |
123123
| **ff_init.c** | 70 | Initialization flow coordination | All above modules |
124124
| **ff_epoll.c** | 289 | Linux epoll → FreeBSD kqueue conversion (unified F-Stack + kernel; ff_epoll_host_ep shared with kqueue path) | FreeBSD kqueue |
125-
| **ff_host_interface.c** | 583 | Host OS interface (mmap/pthread/rand) + FF_KERNEL_COEXIST host-stack bridges (27 ff_host_*) | System libraries |
125+
| **ff_host_interface.c** | 617 | Host OS interface (mmap/pthread/rand) + FF_KERNEL_COEXIST host-stack bridges (32 ff_host_*) | System libraries |
126126
| **ff_dpdk_kni.c** | ~441 | Virtual NIC support (via virtio_user, no longer depends on rte_kni.ko) | DPDK virtio_user |
127127
| **ff_route.c** | 1604 | Route socket / RIB hooks (rtsock partial port; 5-category 14.0+ ABI fixes at M4) | FreeBSD net/route |
128128
| **ff_veth.c** | 1132 | Virtual ethernet device (28 if_t accessor rewrites at M4) | FreeBSD net/if |
@@ -312,7 +312,8 @@ An optional mode (compile-time macro `FF_KERNEL_COEXIST` + runtime `config.ini [
312312
- A kernel-stack fd is returned as `host_fd + 0x40000000` (`FF_KERNEL_FD_BASE`), which never collides with FreeBSD fds (`< 65536`); entries route such fds to thin `ff_host_*` host-libc bridges.
313313
- The F-Stack ↔ host fd pairing is held in `ff_native_fd_map`; `ff_epoll_*` lazily pairs a host `epoll` per kqueue for unified event delivery. A dual-built `AF_INET6` socket gets `IPV6_V6ONLY=1` on its host counterpart (`ff_host_set_v6only`, R9) so v4+v6 coexist on the same port (fixes the prior `-DINET6` `errno=98` startup failure).
314314
- **R9** extends unified events to the native `ff_kqueue`/`ff_kevent` interface (shared `ff_epoll_host_ep`): `ff_kevent` registers a kernel/dual-stack fd's `EVFILT_READ/WRITE` into the kqueue-paired host epoll and synthesizes `struct kevent` (`ident`=app-side fd) from a non-blocking host-epoll poll before merging F-Stack events — a pure-kqueue app (`example/main.c`) now reaches the kernel-side listener (`curl 127.0.0.1:80`=200, was 000).
315-
- When the macro is off the library is byte-for-byte identical to the pure-F-Stack build. Known limitations: `ff_readv`/`ff_writev`/`ff_ioctl` are not yet kernel-routed; kernel fds via kqueue support `EVFILT_READ/WRITE` only. See `docs/kernel_event_support_spec/`.
315+
- **R10** completes residual-entry kernel-fd routing: `ff_readv`/`ff_writev` kernel fd via `ff_host_readv/writev` (mimic read/write); `ff_ioctl` kernel fd uses the **raw Linux request** straight to `ff_host_ioctl` (dual-stack fd same-driver NOT implemented, only the encode kernel fd routed); `ff_dup``ff_host_dup`+encode, `ff_dup2` both-kernel→`ff_host_dup2`+encode / cross-stack rejected `errno=EINVAL`.
316+
- When the macro is off the library is byte-for-byte identical to the pure-F-Stack build. Known limitations: kernel fds via kqueue support `EVFILT_READ/WRITE` only; `ff_select` (encode kernel fd ≫ `FD_SETSIZE` hard limit) and `ff_poll` (conservatively not implemented) do not support kernel-fd coexistence — use `ff_epoll_*`/`ff_kqueue` for kernel-fd multiplexing. See `docs/kernel_event_support_spec/`.
316317

317318
## 7. Technology Selection Analysis
318319

docs/02-LAYER2-INTERFACES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ int ff = ff_socket(AF_INET, SOCK_STREAM | SOCK_FSTACK, 0); // F-Stack stack
186186
int du = ff_socket(AF_INET, SOCK_STREAM, 0); // dual-created (default)
187187
```
188188

189-
A kernel-stack fd is returned as `host_fd + 0x40000000` and is accepted transparently by all `ff_*` calls (forwarded to `ff_host_*` bridges), including `ff_epoll_*` and — since **R9**`ff_kqueue`/`ff_kevent`: each kqueue lazily pairs one host epoll (shared `ff_epoll_host_ep`), `ff_kevent` registers a kernel/dual-stack fd's `EVFILT_READ/WRITE` there and synthesizes `struct kevent` (`ident`=app-side fd) from a non-blocking host-epoll poll before merging F-Stack events — so a pure-kqueue app (`example/main.c`) reaches the kernel-side listener (`curl 127.0.0.1:80`=200). A dual-built `AF_INET6` socket gets `IPV6_V6ONLY=1` on its host counterpart so a `-DINET6` build starts with v4+v6 on the same port. Off by default → byte-for-byte identical build. Known limitations: `ff_readv` / `ff_writev` / `ff_ioctl` are not yet kernel-routed; kernel fds via kqueue support `EVFILT_READ/WRITE` only. See `docs/kernel_event_support_spec/`.
189+
A kernel-stack fd is returned as `host_fd + 0x40000000` and is accepted transparently by all `ff_*` calls (forwarded to `ff_host_*` bridges), including `ff_epoll_*` and — since **R9** — `ff_kqueue`/`ff_kevent`: each kqueue lazily pairs one host epoll (shared `ff_epoll_host_ep`), `ff_kevent` registers a kernel/dual-stack fd's `EVFILT_READ/WRITE` there and synthesizes `struct kevent` (`ident`=app-side fd) from a non-blocking host-epoll poll before merging F-Stack events — so a pure-kqueue app (`example/main.c`) reaches the kernel-side listener (`curl 127.0.0.1:80`=200). A dual-built `AF_INET6` socket gets `IPV6_V6ONLY=1` on its host counterpart so a `-DINET6` build starts with v4+v6 on the same port. Since **R10**, `ff_readv`/`ff_writev`/`ff_ioctl`/`ff_dup`/`ff_dup2` also support kernel-fd routing (`ff_readv/writev` via `ff_host_readv/writev`; `ff_ioctl` kernel fd uses the raw Linux request straight to `ff_host_ioctl`, dual-stack fd same-driver NOT implemented; `ff_dup2` cross-stack rejected `errno=EINVAL`). Off by default → byte-for-byte identical build. Known limitations: kernel fds via kqueue support `EVFILT_READ/WRITE` only; `ff_select` (encode kernel fd ≫ `FD_SETSIZE` hard limit) / `ff_poll` (conservatively not implemented) do not support kernel-fd coexistence — use `ff_epoll_*`/`ff_kqueue`. See `docs/kernel_event_support_spec/`.
190190

191191
## 3. Application Development Guidelines
192192

docs/03-LAYER3-FUNCTIONS.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ struct ff_dispatcher_context {
308308

309309
## 3. Three Key Source File Analyses
310310

311-
### 3.1 ff_syscall_wrapper.c (2212 Lines) - Linux/FreeBSD Adaptation
311+
### 3.1 ff_syscall_wrapper.c (2265 Lines) - Linux/FreeBSD Adaptation
312312

313313
**Main Responsibility**: Convert Linux system call parameters/options to FreeBSD equivalents
314314

@@ -564,11 +564,12 @@ struct prison prison0; // Namespace
564564
### 3.4 Kernel-Stack Coexistence Files (`FF_KERNEL_COEXIST`, optional)
565565

566566
Compiled only with `FF_KERNEL_COEXIST=1`:
567-
- **`ff_host_interface.c` (583 L) / `.h` (182 L)**: `FF_KERNEL_FD_BASE=0x40000000` + inline `ff_is_kernel_fd/ff_kernel_fd_encode/ff_kernel_fd_real`; `ff_native_fd_map[65536]` + `ff_native_map_get/set/clear`; **27 `ff_host_*` host-libc bridges** (R9 added `ff_host_set_v6only`, `ff_host_kqueue_ctl`, `ff_host_kqueue_poll`).
567+
- **`ff_host_interface.c` (617 L) / `.h` (187 L)**: `FF_KERNEL_FD_BASE=0x40000000` + inline `ff_is_kernel_fd/ff_kernel_fd_encode/ff_kernel_fd_real`; `ff_native_fd_map[65536]` + `ff_native_map_get/set/clear`; **32 `ff_host_*` host-libc bridges** (R9 added `ff_host_set_v6only`, `ff_host_kqueue_ctl`, `ff_host_kqueue_poll`; R10 added `ff_host_readv`, `ff_host_writev`, `ff_host_ioctl`, `ff_host_dup`, `ff_host_dup2`).
568568
- **`ff_epoll.c` (289 L)**: `ff_epoll_pairs[64]` lazily pairs a host `epoll` per kqueue; `ff_epoll_wait` polls host epoll (non-blocking) then merges kqueue events (single-threaded, no lock). `ff_epoll_host_ep` is shared (promoted from `static`) so the R9 kqueue path reuses the same pairing table.
569569
- **R9 kqueue/kevent coexistence (`ff_syscall_wrapper.c`)**: `ff_kqueue`/`ff_kevent` mirror the epoll path — register a kernel/dual-stack fd's `EVFILT_READ/WRITE` into the kqueue-paired host epoll (`ff_host_kqueue_ctl`), synthesize `struct kevent` from a non-blocking `ff_host_kqueue_poll` (`ident`=app-side fd, `EV_EOF``EPOLLHUP|ERR`), then merge `ff_kevent_do_each` F-Stack events. Fixes the `example/main.c` kqueue model (kernel-side `curl 127.0.0.1:80`=200, was 000). Kernel fds: `EVFILT_READ/WRITE` only.
570570
- **R9 IPv6**: a dual-built `AF_INET6` socket gets `IPV6_V6ONLY=1` on its host counterpart (`ff_host_set_v6only`), so a `-DINET6` build starts with v4+v6 on the same port (fixes the prior `errno=98 EADDRINUSE`).
571-
- **`ff_syscall_wrapper.c`**: `ff_socket` dual-create + per-entry kernel-fd routing. Not routed: `ff_readv`/`ff_writev`/`ff_ioctl`.
571+
- **R10 residual-entry coexistence (`ff_syscall_wrapper.c`)**: `ff_ioctl` (L1067) kernel fd uses the **raw Linux request** straight to `ff_host_ioctl` (NOT via `linux2freebsd_ioctl`; dual-stack fd same-driver NOT implemented, only the encode kernel fd routed); `ff_readv` (L1189)/`ff_writev` (L1251) kernel fd via `ff_host_readv/writev` (mimic read/write, connection fds single-stack hot path); `ff_dup` (L2130) kernel fd → `ff_host_dup`+encode; `ff_dup2` (L2156) both-kernel → `ff_host_dup2`+encode / cross-stack rejected `errno=EINVAL`. Known limits: `ff_select` (encode kernel fd ≫ `FD_SETSIZE` hard limit) / `ff_poll` (conservatively not implemented) do not support kernel-fd coexistence — use `ff_epoll_*`/`ff_kqueue`.
572+
- **`ff_syscall_wrapper.c`**: `ff_socket` dual-create + per-entry kernel-fd routing (incl. R10 readv/writev/ioctl/dup/dup2).
572573

573574
## 4. Key Header File Overview
574575

@@ -578,7 +579,7 @@ Compiled only with `FF_KERNEL_COEXIST=1`:
578579
| `ff_config.h` | ~100 | Configuration structure definitions |
579580
| `ff_event.h` | ~150 | kevent structures and macros |
580581
| `ff_errno.h` | ~100 | 96 errno mappings |
581-
| `ff_host_interface.h` | 182 | OS abstraction layer (pthread/mmap) + `FF_KERNEL_COEXIST` kernel-fd helpers & 27 `ff_host_*` bridge decls (incl. R9 set_v6only/kqueue_ctl/kqueue_poll) |
582+
| `ff_host_interface.h` | 187 | OS abstraction layer (pthread/mmap) + `FF_KERNEL_COEXIST` kernel-fd helpers & 32 `ff_host_*` bridge decls (incl. R9 set_v6only/kqueue_ctl/kqueue_poll, R10 readv/writev/ioctl/dup/dup2) |
582583
| `ff_dpdk_if.h` | ~50 | DPDK initialization interface |
583584
| `ff_veth.h` | ~100 | Virtual Ethernet and mbuf operations |
584585
| `ff_log.h` | ~50 | Log levels and macros |

docs/F-Stack_Architecture_Layer1_System_Overview.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ Actual data on 10GbE link:
8484
│ ├── ff_dpdk_if.c (2907 lines) # ⭐ Most critical: DPDK/NIC driver
8585
│ ├── ff_glue.c (1467 lines) # Kernel emulation layer
8686
│ ├── ff_config.c (1694 lines) # Configuration parsing
87-
│ ├── ff_syscall_wrapper.c (2212 lines) # Linux↔FreeBSD adaptation (+ R9 kqueue coexist + IPV6_V6ONLY)
87+
│ ├── ff_syscall_wrapper.c (2265 lines) # Linux↔FreeBSD adaptation (+ R9 kqueue coexist + IPV6_V6ONLY + R10 readv/writev/ioctl/dup/dup2 kernel-fd routing)
8888
│ ├── ff_init.c (69 lines) # Initialization coordination
8989
│ ├── ff_epoll.c (289 lines) # Epoll compat (unified F-Stack+kernel)
90-
│ ├── ff_host_interface.c (583 lines) # Host OS iface + FF_KERNEL_COEXIST bridges (27 ff_host_*)
90+
│ ├── ff_host_interface.c (617 lines) # Host OS iface + FF_KERNEL_COEXIST bridges (32 ff_host_*)
9191
│ ├── ff_dpdk_kni.c # Virtual NIC support
9292
│ ├── ff_*.h # API and data structure definitions
9393
│ └── Makefile (765 lines) # Build system
@@ -157,7 +157,7 @@ Actual data on 10GbE link:
157157
| **NIC Driver Layer** | ff_dpdk_if.c | 2907 | DPDK initialization, NIC operations, core TX/RX logic | DPDK, ff_glue |
158158
| **Glue Layer** | ff_glue.c | 1467 | Kernel API emulation (locks, memory, interrupts; M4 8-category 14.0+ ABI fixes) | FreeBSD sys, pthread |
159159
| **Configuration System** | ff_config.c | 1694 | INI file parsing, runtime parameter management | ff_ini_parser |
160-
| **Linux Compatibility** | ff_syscall_wrapper.c | 2212 | Socket option/errno mapping (M4 sockaddr update; FF_KERNEL_COEXIST routing; R9 kqueue coexist + IPV6_V6ONLY) | FreeBSD API |
160+
| **Linux Compatibility** | ff_syscall_wrapper.c | 2265 | Socket option/errno mapping (M4 sockaddr update; FF_KERNEL_COEXIST routing; R9 kqueue coexist + IPV6_V6ONLY; R10 readv/writev/ioctl/dup/dup2 kernel-fd routing) | FreeBSD API |
161161
| **Epoll Compatibility** | ff_epoll.c | 289 | Linux epoll → FreeBSD kqueue (unified F-Stack + kernel epoll; ff_epoll_host_ep shared with kqueue path) | ff_kqueue |
162162
| **Initialization Coordination** | ff_init.c | 69 | Startup flow orchestration | All other modules |
163163
| **Host Interface** | ff_host_interface.c | - | mmap/pthread/time interfaces | System libraries |
@@ -436,8 +436,9 @@ By default every socket lives purely in the F-Stack user-space stack. The option
436436
- **Managed kernel-fd space.** A kernel-stack fd is handed to the application as `host_fd + FF_KERNEL_FD_BASE` (`0x40000000`), far above the maximum FreeBSD fd (`kern.maxfiles <= 65536`), so the two fd ranges never collide. F-Stack entry points recognise such an fd and route it to a thin host-libc bridge; the default F-Stack path is left untouched.
437437
- **Dual-stack fd pairing.** For a dual-created socket the F-Stack fd ↔ host fd pairing is tracked in `ff_native_fd_map`, so control/data operations drive both stacks where it matters (e.g. `bind`/`listen` on both, `shutdown`/`close` on both). For a dual-created `AF_INET6` socket the host counterpart is set to `IPV6_V6ONLY=1` (`ff_host_set_v6only`, R9) so it coexists with the same-port host IPv4 socket; this fixes the prior `-DINET6` startup failure (`ff_bind` `errno=98 EADDRINUSE`).
438438
- **Unified event loop.** `ff_epoll_*` lazily pairs one host `epoll` fd per kqueue, so kernel-fd and F-Stack events are delivered from the single `ff_epoll_wait()` the application already runs. **R9** extends the same mechanism to the native `ff_kqueue`/`ff_kevent` interface (shared `ff_epoll_host_ep` pairing): `ff_kevent` registers a kernel/dual-stack fd's `EVFILT_READ/WRITE` into the kqueue-paired host epoll and synthesizes `struct kevent` (`ident`=the app-side fd) from a non-blocking host-epoll poll before merging F-Stack kqueue events — so a pure-kqueue app (`example/main.c`) now reaches the kernel-side listener (measured `curl 127.0.0.1:80` = 200 size=438, was 000).
439+
- **R10 residual-entry coexistence.** `ff_readv`/`ff_writev` kernel fd via `ff_host_readv/writev` (mimic read/write); `ff_ioctl` kernel fd uses the **raw Linux request** straight to `ff_host_ioctl` (dual-stack fd same-driver NOT implemented, only the encode kernel fd routed); `ff_dup`→`ff_host_dup`+encode, `ff_dup2` both-kernel→`ff_host_dup2`+encode / cross-stack rejected `errno=EINVAL`.
439440
440-
**Known limitations (this release):** `ff_readv` / `ff_writev` / `ff_ioctl` are not yet routed to the kernel stack — use `ff_read` / `ff_write` for dual-stack fds; kernel fds via kqueue support `EVFILT_READ/WRITE` only. Full design, test, and review-gate record: `docs/kernel_event_support_spec/` (+ `zh_cn/`).
441+
**Known limitations (this release):** kernel fds via kqueue support `EVFILT_READ/WRITE` only; `ff_select` (encode kernel fd ≥ `0x40000000` ≫ `FD_SETSIZE`(1024), hard limit) and `ff_poll` (conservatively not implemented) do not support kernel-fd coexistence — use `ff_epoll_*` / `ff_kqueue` for kernel-fd multiplexing. Full design, test, and review-gate record: `docs/kernel_event_support_spec/` (+ `zh_cn/`).
441442
442443
---
443444

docs/F-Stack_Architecture_Layer2_Interface_Specification.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,10 +635,12 @@ int du = ff_socket(AF_INET, SOCK_STREAM, 0); // dual-created (def
635635

636636
Priority: per-socket marker > `config.ini [stack] kernel_coexist` > F-Stack. When the macro is not compiled in, the markers are undefined and `ff_socket` behaves exactly as before.
637637

638-
**Transparent routing.** All standard `ff_*` calls accept a managed kernel fd transparently: `ff_bind/listen/connect/accept[4]/close/read/write/recv*/send*/sendmsg/recvmsg/getpeername/getsockname/shutdown/setsockopt/getsockopt/fcntl`, and `ff_epoll_ctl/wait`. Internally each kernel fd is forwarded to a thin `ff_host_*` host-libc bridge (`lib/ff_host_interface.c`). **Not yet routed (known limitation):** `ff_readv` / `ff_writev` / `ff_ioctl` — use `ff_read` / `ff_write` for kernel/dual-stack fds. Full design and tests: `docs/kernel_event_support_spec/`.
638+
**Transparent routing.** All standard `ff_*` calls accept a managed kernel fd transparently: `ff_bind/listen/connect/accept[4]/close/read/write/recv*/send*/sendmsg/recvmsg/getpeername/getsockname/shutdown/setsockopt/getsockopt/fcntl`, and `ff_epoll_ctl/wait`. Internally each kernel fd is forwarded to a thin `ff_host_*` host-libc bridge (`lib/ff_host_interface.c`). **Since R10 also accepted transparently:** `ff_readv` / `ff_writev` / `ff_ioctl` / `ff_dup` / `ff_dup2` (kernel fd via the matching `ff_host_*` bridge; `ff_ioctl` uses the raw Linux request; `ff_dup2` cross-stack rejected `errno=EINVAL`). Full design and tests: `docs/kernel_event_support_spec/`.
639639

640640
**R9 — kqueue/kevent coexistence + IPv6.** The unified-event support now also covers the native `ff_kqueue` / `ff_kevent` interface (previously only `ff_epoll_*`): each kqueue lazily pairs one host epoll (shared `ff_epoll_host_ep`, reusing the `ff_epoll_pairs` table). `ff_kevent` registers a kernel/dual-stack fd's `EVFILT_READ/WRITE` into that host epoll (kernel-only changes are not forwarded to the F-Stack kqueue), and on wait it synthesizes `struct kevent` (`ident`=the app-side fd, `EV_EOF``EPOLLHUP|ERR`) from a non-blocking host-epoll poll before merging F-Stack kqueue events. This makes a pure-kqueue application (e.g. `example/main.c`) reach the kernel-side listener — measured `curl 127.0.0.1:80` = 200 size=438. Kernel fds via kqueue support `EVFILT_READ/WRITE` only. On the IPv6 side, a dual-built `AF_INET6` socket has its host counterpart set to `IPV6_V6ONLY=1` (`ff_host_set_v6only`) so a `-DINET6` build starts cleanly with v4+v6 on the same port (fixes the prior host-IPv6 `errno=98 EADDRINUSE`).
641641

642+
**R10 — residual-entry coexistence.** `ff_readv`/`ff_writev` (kernel fd → `ff_host_readv/writev`, mimic read/write, connection fds single-stack hot path), `ff_ioctl` (kernel fd uses the **raw Linux request** straight to `ff_host_ioctl`, NOT via `linux2freebsd_ioctl`; dual-stack fd same-driver NOT implemented, only the encode kernel fd is routed), `ff_dup` (kernel fd → `ff_host_dup`+encode), `ff_dup2` (both-kernel → `ff_host_dup2`+encode; cross-stack rejected `errno=EINVAL`). Adds 5 host bridges `ff_host_readv/writev/ioctl/dup/dup2`. Known limitation: `ff_select` (encode kernel fd ≫ `FD_SETSIZE` hard limit) / `ff_poll` (conservatively not implemented) do not support kernel-fd coexistence — use `ff_epoll_*`/`ff_kqueue`.
643+
642644
---
643645

644646
## 4. Multi-Process and Multi-Thread Interfaces

0 commit comments

Comments
 (0)