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 |
| π’ | RCCL/NCCL net plugin | Supported GPU path β librccl-net.so / libnccl-net-ODL_TB5.so (see docs/GPU.md) |
| π‘ | NCCL verbs / IB transport | Works only with LD_PRELOAD=libodl_tb5_verbs.so health checks; RCCL dlopens libibverbs and bypasses preload β use the net plugin |
| π‘ | rdma-core provider plugin | Needs a sysfs RDMA device to load; inert on OdinLink-only hosts β use LD_PRELOAD for ibv_devinfo |
| π‘ | 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 -d 0
# Machine B (wait for dmesg "entering READY state" first):
sudo insmod driver/odl_tb5.ko
build/cli/odl_tb5_cli client -d 0 -t bandwidth -b 64K,1M,4Mcmake --build . --target rccl_net_odl_tb5
# Build tree already has librccl-net.so symlink; or:
export LD_LIBRARY_PATH=$PWD/rccl:$PWD/lib:$LD_LIBRARY_PATH
# Confirm logs say: Using network ODL_TB5 (NOT Socket)from odinlink import RemoteStore # Ubuntu trainer β Mac RAM over TB-IP
mac = RemoteStore("tb5://<mac-bridge-ip>")
mac.put("kv.42", cuda_tensor)Full install β docs/INSTALL.md Β· GPU β docs/GPU.md Β· Ubuntu + PyTorch + Mac RAM β docs/PYTORCH.md Β· which TB card β docs/HARDWARE.md (scripts/tb-hw-check.sh)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 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 -d 0
# Machine B (wait for dmesg: "odl_tb5: entering READY state"):
build/cli/odl_tb5_cli client -d 0 -t bandwidth5. ibv_devinfo discovery
# Preferred health check on OdinLink-only hosts (no other RDMA NIC):
LD_PRELOAD=build/verbs/libodl_tb5_verbs.so ibv_devinfo
# The rdma-core directory plugin (libodl_tb5-rdmav34.so) only loads when
# sysfs already exposes an unclaimed RDMA device β on pure OdinLink machines
# it is inert. See docs/TROUBLESHOOTING.md.| 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. |
odl_ring_size=1024 |
4096 | Number of DMA packet slots per ring. Larger = smoother bursts, more RAM. On iommu=pt (identity IOMMU) hosts the default may fail to allocate β use 1024, or let probe auto-downgrade. |
# 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 macOS
sudo insmod driver/odl_tb5.ko odl_ring_size=1024 # iommu=pt / RAM-constrainedexport 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