Rebase USB4 GDA DV branch and add smoke probes - #52
georgewhewell wants to merge 14 commits into
Conversation
|
P2P/GDA smoke update from live Strix testing:
Caveats: this is intentionally gated behind |
|
HIP receiver smoke test update from
Conclusion: destination GPU dmabuf import/registration works, but this is not true RX-side GPU DMA yet. The current RDMA WRITE receive path still lands in kernel RX buffers and then calls the CPU-copy path ( |
Introduce the software-RNIC ABI surface that the upcoming GDA (GPU
Direct Async) work will hang off. v1 of this PR series only wires the
QUERY_CAPS method so userspace consumers can detect ABI presence and
size assumptions; CREATE_QUEUE / DESTROY_QUEUE / KICK and the
GPU-produced WQE consumer come in follow-ups, each gated by a bit in
struct usb4_rdma_dv_query_caps_resp::caps.
What lands here:
- userspace/usb4_rdma/usb4_rdma_dv.h: the full v2 ABI header. Declares
all opcodes, the 128-byte producer/consumer split doorbell record,
WQE/CQE structs, the generation protocol, and the queue-memory
coherence requirements. Reviewing this header is the single most
important part of this PR — it pins the contract every subsequent
GDA commit will build against.
- kernel/dv.c: the QUERY_CAPS handler plus the uverbs object/method
declarations and the uapi_definition table. Caps is intentionally 0
for now; later commits OR in USB4_RDMA_DV_CAP_* bits as the
matching kernel paths are wired.
- kernel/ibdev.c: assigns tbv_uapi_defs to ib_device.driver_def under
CONFIG_INFINIBAND_USER_ACCESS so the private uverbs methods are
exposed.
- userspace/bench/tbv_dv_caps_probe.c: standalone QUERY_CAPS probe.
Uses the raw RDMA_VERBS_IOCTL ABI directly rather than rdma-core's
private execute_ioctl() helper, so it can be built outside the
provider tree. Also reports the exact byte layout of the doorbell
record and WQE generation field so the contract is self-documenting
at runtime.
- nix/dv-probes.nix + flake.nix: package the probe as
tbv-dv-caps-probe (also wired into apps/checks/hydraJobs).
- nix/module.nix: extend the kernel source filter to include the DV
ABI header so the out-of-tree module build can pick it up.
Build-time guarantees in kernel/dv.c BUILD_BUG_ON the on-the-wire
struct sizes (WQE/CQE/doorbell lines/record) against the corresponding
USB4_RDMA_DV_*_SIZE constants, so any future field addition without an
ABI version bump fails to compile rather than silently desyncing kernel
and userspace.
Verified:
- nix flake check --no-build passes
- nix build .#thunderbolt-ibverbs --builders '' builds the module
- nix build .#tbv-dv-caps-probe --builders '' builds the probe
- tbv-dv-caps-probe -l prints the doorbell layout matching the header:
producer line (gpu→kernel): sq_tail@0/4, cq_head@4/4, generation@8/4
consumer line (kernel→gpu): sq_head@64/4, cq_tail@68/4, qp_state@72/4, generation@76/4
wqe generation field at offset 44
No DV transport opcodes are wired in this commit; this is the ABI
introduction only. Runtime QUERY_CAPS returns caps=0 to reflect that.
Wire the QP-lifecycle half of the DV ABI: userspace can attach a host-pinned SQ/CQ/doorbell triple to a QP and detach it. No transport opcodes are wired through the DV consumer yet, so caps stays 0; KICK and the actual WQE consumer come in a follow-up commit. Per-QP DV state is held in a new struct tbv_dv_qp_state embedded in tbv_qp. ibdev.c owns the embedding and exposes two thin accessors (tbv_qp_from_ibqp, tbv_qp_dv_state); dv.c works with the substate without needing the full tbv_qp layout. dv.c provides the lifecycle helpers (init, teardown, active-check) and the uverbs handlers. GDA-exclusive QP semantics are enforced at post_send: once a DV queue is active on a QP, standard ibv_post_send returns -EBUSY. post_recv is intentionally unaffected — v1 keeps the receive queue kernel-owned and the peer continues to post recvs through standard verbs while the QP has an active DV queue (matches the design in the ABI header). CREATE_QUEUE: - Validates abi_version, alignment, entry counts, stride against the ABI constants, and overflow-checks the address ranges. - ib_umem_get pins SQ/CQ/doorbell from userspace. - Bumps the QP's DV generation, stores the umem references, and publishes the LIVE state into the doorbell consumer cacheline with packed (index=0, generation) tail words. - Returns qp_num + generation. Rolls back on any later failure by bumping generation again and releasing the umem. - Second CREATE_QUEUE on a still-attached QP is rejected with -EBUSY. DESTROY_QUEUE: - Bumps generation under dv->mutex (so any stale producer write landing on the doorbell after this point is generation-mismatched and detectable). - Releases SQ/CQ umem, keeps doorbell umem long enough to write the QP_DEAD sentinel + bumped generation, then releases that too. - Second DESTROY_QUEUE returns -ENOENT. QP destroy (tbv_destroy_qp) calls tbv_dv_qp_state_teardown early so the umem pins are released even if userspace never called DESTROY_QUEUE explicitly. This is the safety net for crashed clients. The kernel doesn't export a public `ib_umem_copy_to` (only `_from`), so dv.c carries a small `tbv_dv_umem_write` helper that walks the umem's scatterlist and kmap_local_page()s each affected page. Today the only writer is the doorbell consumer-line update (16 bytes); the implementation is general so subsequent commits can reuse it for CQE writes without revisiting the umem API. tbv_dv_caps_probe gains a -q mode that: - Allocates aligned SQ/CQ/doorbell host memory. - Creates a standard verbs PD/CQ/QP. - Issues CREATE_QUEUE, verifies qp_num matches and generation is nonzero. - Reads back the doorbell consumer line and verifies it matches the documented LIVE state (sq_head/cq_tail packed with generation, qp_state=LIVE). - Confirms a second CREATE_QUEUE returns -EBUSY. - DESTROY_QUEUE, verifies the consumer line transitions to DEAD with bumped generation. - Confirms a second DESTROY_QUEUE returns -ENOENT. Verified: - nix flake check --no-build passes - nix build .#thunderbolt-ibverbs --builders '' builds the module - nix build .#tbv-dv-caps-probe --builders '' builds the probe - tbv-dv-caps-probe -h / -l still work (no device required) Runtime probe (-q) requires a usb4_rdma device + matching module loaded; that lands as part of the hardware smoke after this branch hits Hydra.
3cd65ab to
164961c
Compare
The device was reporting IB_LINK_LAYER_ETHERNET with RDMA_CORE_PORT_IBA_ROCE_UDP_ENCAP, which routed every QP through the RoCE code path. The RoCE path requires get_eth_mac/update_qp_av/set_roce_udp_encap callbacks this driver does not implement, so every ibv_modify_qp(RTR/RTS) returned -ENODATA: no AV resolution could be constructed for an Ethernet device without Ethernet AV resolvers. USB4 native RDMA is IB semantics over a point-to-point link, not RoCE. Switch to IB_LINK_LAYER_INFINIBAND + RDMA_CORE_CAP_IB_MAD. The pre-existing tbv_query_port() already reports lid=0, gid_tbl_len=TBV_IBDEV_GID_TBL_LEN, pkey_tbl_len=1, and the GID table is already IB-mode (fe80 + node-GUID interface_id), so no other port-data changes are required. Validated: ib_write_bw 1309 MiB/s, ib_send_bw 1322 MiB/s, ib_read_bw 1305 MiB/s, ib_write_bw --bidirectional 1925 MiB/s, ib_write_lat 6.15us typical, uc_oneway 10853 Mbit/s with 1000/1000 done, rc_write_verify recv_check=0. 41001 RDMA WRs across both nodes, zero errors on every error counter.
When the kernel RX path lands a WRITE fragment into a dmabuf MR, the
existing bounce-buffer path (kmap_local_page + memcpy in
tbv_umem_copy_to_iova) fails on ZONE_DEVICE pages because the CPU cannot
kmap them. The fragment never lands in GPU memory, and the WC is pushed
with LOC_PROT_ERR.
The fix is a parallel RX path: when a fragment's destination MR is a
dmabuf MR (mr->umem->is_dmabuf == true), DMA the ring payload directly
into the MR page via dma_map_sg_attrs + per-fragment descriptors. The
bounce-buffer path is unchanged for CPU MRs.
Concrete changes:
- tbv_rx_reorder_msg gains zcopy_sgt / zcopy_mr / zcopy_dma_dev /
zcopy_sgt_nents / zcopy_mapped / refs (refcount_t). Per-fragment
zcopy DMAs bump refs so the msg stays alive until every completion
is processed; the SGL is dma_unmap_sg'd on last put.
- tbv_rx_buffer_write_fragment_locked() probes the destination MR with
tbv_rx_zcopy_try_lock(). If it is a dmabuf MR, the new path is taken:
tbv_rx_zcopy_prepare_msg() dma_map_sg's the destination SGL,
tbv_rx_zcopy_post_fragment() walks the SGL to the page that contains
dest_iova, and tbv_path_post_rx_zcopy_frame() programs one
descriptor that DMAs the wire payload into the MR page. Completion
runs on the QP workqueue via tbv_rx_zcopy_drain_work, marks the
fragment seen, and pushes the WC when the last fragment finishes.
- The per-frame struct tbv_data_frame gains zcopy_rx / zcopy_page /
zcopy_page_off so tbv_path_post_rx_zcopy_frame() can submit a
one-shot frame that uses no bounce buffer and is freed in
tbv_path_zcopy_rx_complete() after dma_unmap_page.
- Two new counters in tbv_state:
data_rx_dmabuf_zcopy (successes)
data_rx_dmabuf_zcopy_error (mapping / DMA failures)
Visible in /sys/kernel/debug/tbv/usb4_rdma*/summary.
- The UC oneway bench gains a trailing newline (cosmetic, no behavior
change).
Failure handling: dma_map_sg returning 0, dma_map_page errors, and
SGL walk failures all fall through to the existing drop+ack-error path,
the same as for bad rkey. No new error categories.
CPU-MR behavior is unchanged: tbv_rx_zcopy_try_lock() returns false
when mr->umem->is_dmabuf is unset, and the bounce-buffer path runs.
The implementation matches docs/RX_SIDE_DMA.md: per-fragment DMA,
no reorder/coalesce, no per-MR DMA context, NHI ring untouched.
The dmabuf_mr_probe.c file was never committed to the GDA branch and not wired into the bench-tools flake output. The RX-side DMA path needs this client to verify the kernel lands fragments directly into a dmabuf MR (data_rx_dmabuf_zcopy counter) instead of failing the bounce-buffer path on ZONE_DEVICE pages.
|
Hydra reported failures for this PR.
Failed builds:
|
Summary
origin/mainKICKpath so CREATE_QUEUE/doorbell/CQE behavior can be smoked without advertising transport SEND/WRITE capsValidation
nix build .#thunderbolt-ibverbs -Lnix build .#tbv-dv-caps-probe -L./result-dv-probes/bin/tbv-dv-caps-probe -ltools/tbv-target-module.sh strix-1 --booted-kernel --copyLive smoke note
Tried reloading this branch on
strix-1with the current module parameters preserved. The module loaded, but native HELLO failed with-EINVALbefore anyusb4_rdma*devices were published, so the live DVtbv-dv-caps-probe -qsmoke could not run. I restoredstrix-1to the booted-system module afterward and verified theusb4_rdma*devices came back.This PR is draft until that native HELLO reload regression is understood.