Thunderbolt 5 RDMA for Linux β kernel driver, libibverbs provider, NCCL/RCCL plugins
OdinLink turns a Thunderbolt cable into a high-speed RDMA interconnect between machines. It provides the full ibv_* verbs API so any verbs-aware application (NCCL, MPI, PyTorch DDP) can use Thunderbolt DMA without code changes.
80 Gbps Β· sub-Β΅s latency Β· zero-copy GPU Β· standard ibv_verbs API
#Thunderbolt 4 Test / USB4v1
| Layer | Component | Status |
|---|---|---|
| π’ | Kernel module (odl_tb5.ko) |
NHI ring DMA, XDomain handshake, loopback mode |
| π’ | Userspace library (libodl_tb5.so) |
C API, stream I/O, mmap, DMA-buf |
| π’ | Verbs provider (libodl_tb5_verbs.so) |
ibv_open_device, ibv_reg_dmabuf_mr, QP/CQ lifecycle |
| π’ | rdma-core plugin (libodl_tb5-rdmav34.so) |
Auto-discovered by ibv_devinfo |
| π’ | Async I/O | poll() + O_NONBLOCK ioctls end-to-end |
| π’ | No-cable testing | loopback=1 module param + mock library |
| π’ | NCCL verbs transport | NCCL's built-in NCCL_NET_PLUGIN=IB transport auto-discovers ODL via ibv_get_device_list |
| π‘ | NCCL custom plugin | DMA-buf zero-copy path (legacy, use verbs transport instead) |
| π‘ | Async DMA-buf | Needs callback-based cleanup β stream path is already async via poll() |
sudo apt install build-essential cmake linux-headers-$(uname -r) libibverbs-dev rdma-core pkg-config gcc-14
git clone https://github.com/johndpope/OdinLink-Five.git
cd OdinLink-Five && mkdir build && cd build
cmake .. -DBUILD_VERBS=ON && make -j$(nproc) odl_tb5_verbs odl_tb5_verbs_provider
# Test without cable:
sudo insmod driver/odl_tb5.ko loopback=1
ibv_devinfo # Should show odl_tb5 device
build/verbs/tests/test_verbs_basic# Machine A:
sudo insmod driver/odl_tb5.ko
build/cli/odl_tb5_cli --server --device 0
# Machine B:
sudo insmod driver/odl_tb5.ko
build/cli/odl_tb5_cli --client --device 0 --test bandwidthFull install guide β docs/INSTALL.md
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Application (NCCL, MPI, PyTorch, ibv_* API) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β libibverbs (libibverbs.so.1) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β libodl_tb5-rdmav34.so (verbs provider plugin) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β libodl_tb5.so (OdinLink C API) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β odl_tb5.ko (kernel module β NHI DMA) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Thunderbolt 5 NHI DMA Engine β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Component | Binary | Description |
|---|---|---|
| Kernel driver | odl_tb5.ko |
NHI ring DMA, XDomain handshake, char device |
| Library | libodl_tb5.so |
C API wrapping ioctls, streams, mmap |
| Verbs provider | libodl_tb5_verbs.so |
Standalone ibv_* via symbol interposition |
| Verbs plugin | libodl_tb5-rdmav34.so |
rdma-core provider plugin (ibv_devinfo) |
| NCCL plugin | libnccl-net-ODL_TB5.so |
NVIDIA GPU collectives |
| RCCL plugin | librccl_net_odl_tb5.so |
AMD GPU collectives |
| CLI tool | odl_tb5_cli |
Bandwidth, latency, jitter, MIMO tests |
| Loopback module | loopback=1 param |
Fake peer for no-cable testing |
| Mock library | libodl_tb5_mock.so |
LD_PRELOAD simulation (no kernel needed) |
GPU and daemon/tray β docs/GPU.md, docs/INSTALL.md
| Operation | Status | Notes |
|---|---|---|
ibv_open_device |
β | Symbol interposition + rdma-core plugin |
ibv_query_device |
β | Attributes from peer info |
ibv_query_port |
β | Port state from peer connection |
ibv_alloc_pd / ibv_dealloc_pd |
β | Protection domains |
ibv_reg_mr / ibv_dereg_mr |
β | Host memory registration |
ibv_reg_dmabuf_mr |
β | Zero-copy GPU memory (Linux DMA-buf) |
ibv_create_cq / ibv_destroy_cq |
β | Eventfd-based completion queues |
ibv_poll_cq / ibv_req_notify_cq |
β | Poll + eventfd notification |
ibv_create_qp / ibv_destroy_qp |
β | RC QP β stream mapping |
ibv_modify_qp |
β | RESET β INIT β RTR β RTS |
ibv_post_send |
β | Async via workqueue + poll() |
ibv_post_recv |
β | Non-blocking via poll() |
ibv_query_qp |
β | State + capabilities |
ibv_post_send(qp, wr, NULL)
β
βΌ (non-blocking, returns immediately)
Enqueue WR β per-QP submission queue
β
βΌ (worker thread)
poll(fd, POLLOUT) β kernel signals TX readiness
β
βΌ
ioctl(STREAM_SEND) β O_NONBLOCK, never blocks
β
βββ -EAGAIN β re-queue WR, poll again
βββ success β post struct ibv_wc β CQ β eventfd
# Option 1: kernel loopback (real module, fake peer)
sudo insmod driver/odl_tb5.ko loopback=1
build/verbs/tests/test_verbs_basic
# Option 2: user-space mock (no kernel module at all)
mkfifo /dev/odl_tb5_0
LD_PRELOAD=verbs/tests/libodl_tb5_mock.so \
LD_LIBRARY_PATH=build/verbs:build/lib \
build/verbs/tests/test_verbs_mock_loopbackVerbose logging is essential for diagnosing failures. Use the provided helper script which captures everything automatically:
# Full smoke test suite β all logs go to smoke-test-<timestamp>/:
./scripts/smoke-test.sh
# Run only the verbs provider test:
./scripts/smoke-test.sh -t verbs
# Bandwidth test (two machines, machine A first):
sudo ./scripts/smoke-test.sh -t bandwidth -m server # Machine A
sudo ./scripts/smoke-test.sh -t bandwidth -m client # Machine B
# Custom output directory:
./scripts/smoke-test.sh -o /tmp/odl-debugVerbose logging is also available manually:
# Watch kernel driver logs (run in a separate terminal):
sudo dmesg -w | grep odl_tb5
# Trace all verbs calls β set level 1β5 (5 = most verbose):
export ODL_VERBS_DEBUG=51. Kernel module + device node
sudo insmod driver/odl_tb5.ko
sudo chmod 666 /dev/odl_tb5_0 # allow non-root access
ls -l /dev/odl_tb5_0 # appears only when a TB5 peer is connected2. Full test suite (3 suites: device, lib API, plugin)
build/tests/odl_tb5_test3. Verbs provider lifecycle test
build/verbs/tests/test_verbs_basicExercises: device discovery, context open, PD/MR/CQ/QP lifecycle, post_send/post_recv.
4. End-to-end bandwidth (two machines)
# Machine A:
build/cli/odl_tb5_cli --server --device 0
# Machine B (wait for server to be ready):
build/cli/odl_tb5_cli --client --device 0 --test bandwidth5. ibv_devinfo discovery
ibv_devinfo # should list an odl_tb5 device| Param | Default | What it does |
|---|---|---|
e2e=0 |
1 (on) | Disables end-to-end flow control handshake. Only needed for old TB3 controllers that choke on E2E. TB4/TB5 leave this alone. |
loopback=1 |
0 (off) | Creates fake devices with no cable β data loops back inside your own machine. For testing without a peer. |
protocol=1 |
0 (OdinLink) | Switches to Apple's protocol ID (0xFA57) so macOS peers can discover OdinLink. For MacβLinux only. |
ring_size=1024 |
4096 | Number of DMA packet slots per ring. Larger = smoother bursts, more RAM. Fine at 4096 for all TB generations. Lower for RAM-constrained machines. |
# Examples:
sudo insmod driver/odl_tb5.ko # TB4/TB5, default everything
sudo insmod driver/odl_tb5.ko e2e=0 # old TB3 controller
sudo insmod driver/odl_tb5.ko loopback=1 # no cable, just testing
sudo insmod driver/odl_tb5.ko protocol=1 # talk to macOSexport ODL_VERBS_DEBUG=5 # Trace all verbs calls
sudo dmesg -w | grep odl_tb5 # Kernel driver logsTroubleshooting β docs/TROUBLESHOOTING.md
Apple ships libthunderboltrdma.dylib + libibverbs on macOS 26.5, but
the kernel extension is a stub β IORDMAFamily is not shipped. Mac
Thunderbolt RDMA is not currently functional. OdinLink is the only
working implementation. See COMPAT.md.
| Resource | Link |
|---|---|
| Install guide | docs/INSTALL.md |
| GPU / NCCL / RCCL | docs/GPU.md |
| Troubleshooting | docs/TROUBLESHOOTING.md |
| Packaging / .deb | docs/PACKAGING.md |
| Agent instructions | AGENTS.md |
| Verbs provider manual | verbs/VERBS_PROVIDER.md |
| Cross-platform compat | COMPAT.md |
- Kernel driver (
odl_tb5.ko): GPL v2 - All userspace: MIT

