RoCEv2 setup runbook
Set up lossless RoCEv2 for MemKV on the switch, the host NIC, and MemKV, then confirm that it works.
MemKV moves KV blocks over Remote Direct Memory Access (RDMA). On Ethernet, MemKV uses RoCEv2 (RDMA over Converged Ethernet, version 2). RoCEv2 performs well only when the switch, the host NIC, and MemKV agree on three settings:
- A lossless priority: Priority Flow Control (PFC) and Explicit Congestion Notification (ECN) on one traffic priority.
- A marking: the DSCP value and service level that put RDMA traffic on that priority.
- An MTU that every hop can carry.
If one of them is wrong, RDMA traffic either goes to the best-effort queue (slow and lossy) or is dropped, and the client falls back to TCP.
This page gives one setup path for NVIDIA ConnectX-5 or later (mlx5) NICs on Linux. Adapt the switch steps to your switch vendor.
On a native InfiniBand fabric, this page does not apply. Read the InfiniBand setup runbook. Each MemKV setting named here is described in Configuration.
The three things that must agree
| Layer | Lossless priority | Marking | MTU |
|---|---|---|---|
| Switch | PFC on the RoCE priority | trust DSCP, classify to queue | port MTU ≥ payload + headers |
| Host (NIC) | PFC + ECN on the priority | DSCP→prio map, trust dscp | netdev MTU (jumbo) |
| MemKV | service_level = prio | traffic_class = DSCP << 2 | mtu ≤ port active MTU |
This page uses the common RoCE marking, DSCP 26 on priority 3. MemKV auto-detection prefers this pair when it finds it. Use it unless your fabric already uses another lossless priority.
Why MTU is the common failure
RoCE uses a path MTU of 256, 512, 1024, 2048, or 4096 bytes. The NIC derives it from the Ethernet MTU of the link. A standard 1500-byte Ethernet link gives a RoCE path MTU of 1024. To get 4096, every hop needs jumbo frames: an Ethernet MTU of about 4200 or more. In practice, set 9000.
MemKV uses mtu: 4096 by default. MemKV refuses to start when the configured
mtu is larger than the port's active MTU, and the error names both values.
So either enable jumbo frames end to end, as the steps below do, or lower mtu
to match the port.
A single-node test never puts a packet on the wire, so MTU and PFC errors only show up between two nodes. Always test across nodes.
Switch configuration
Make the RoCE priority lossless, enable ECN, and trust the DSCP marking from the hosts. On NVIDIA Spectrum switches with Cumulus Linux:
# Enable PFC on priority 3 (the RoCE priority)
nv set qos pfc default-global pfc-priority 3
nv set qos pfc default-global state enable
# Trust the incoming DSCP marking and map DSCP 26 -> traffic class 3
nv set qos mapping default-global trust dscp
nv set qos mapping default-global dscp 26 switch-priority 3
# Enable ECN (WRED) on the RoCE traffic class for congestion signaling
nv set qos congestion-control default-global congestion-mode ecn
nv set qos congestion-control default-global traffic-class 3
# Jumbo frames on every RoCE-facing port
nv set interface swp1-swp32 link mtu 9216
nv config applyEnable PFC and ECN on every switch in the RoCE path, not only the leaf switch the host connects to. One hop that drops pause frames or removes the DSCP marking makes the path lossy, and RDMA throughput drops sharply.
Host (NIC) configuration
Run these steps on each host, on the network interface of the RDMA NIC. To find
the interface, run rdma link or ibdev2netdev. The examples assume that
mlx5_0 maps to enp1s0f0.
Trust DSCP and map DSCP 26 to priority 3
mlnx_qos -i enp1s0f0 --trust dscp
mlnx_qos -i enp1s0f0 --dscp2prio set,26,3Make priority 3 lossless with PFC
# bitmask per priority 0..7 — enable PFC on priority 3 only
mlnx_qos -i enp1s0f0 --pfc 0,0,0,1,0,0,0,0Enable ECN for the RoCE priority
echo 1 > /sys/class/net/enp1s0f0/ecn/roce_np/enable/3
echo 1 > /sys/class/net/enp1s0f0/ecn/roce_rp/enable/3Raise the MTU to enable a 4096 RoCE path MTU
ip link set enp1s0f0 mtu 9000These commands do not persist across a reboot. Add them to your NIC startup,
for example a systemd unit, network-scripts, netplan, or the NVIDIA
mlnx-en/openibd configuration.
MemKV configuration
When the fabric is lossless and uses jumbo frames, keep the defaults. With a
value of 0, MemKV reads the DSCP-to-priority map and PFC settings from the NIC
and sets the matching DSCP and service level on outgoing traffic.
rdma:
device: mlx5_0
gid_index: 0 # 0 = auto-detect the routable RoCEv2 GID
traffic_class: 0 # 0 = auto-detect the lossless DSCP (e.g. 104 for DSCP 26)
service_level: 0 # 0 = take the priority paired with the DSCP (e.g. 3)
mtu: 4096 # requires jumbo frames end-to-end (above)Some virtualized or non-mlx5 NICs do not expose these settings to MemKV. On those NICs, set the values yourself:
gid_index: a RoCEv2 entry in the GID table. Find it withshow_gids(see Verification). On mlx5, index 0 is a RoCEv1 entry, which carries no DSCP and cannot reach the lossless queue.traffic_class: the IP ToS byte, which isDSCP << 2. For DSCP 26, it is104.service_level: the lossless PFC priority,3. On mlx5 the NIC queues traffic by service level, not by DSCP, so a wrong service level sends traffic to the best-effort queue even when the DSCP is right.
rdma:
gid_index: 3 # a routable RoCEv2 GID entry (see verification below)
traffic_class: 104 # DSCP 26 << 2
service_level: 3 # matches the lossless PFC priority
mtu: 4096Clients must use the same mtu as the server. Clients set it with
MEMKV_RDMA_MTU (default 4096). If you cannot enable jumbo frames, set
both the server and the clients to 1024. The server and the client
each refuse to start when mtu is larger than the link supports.
Verification
memkv net runs most of these checks with the values MemKV
itself uses:
memkv net showreports the GID index, DSCP and traffic class, service level, and path MTU.memkv net probetakes a snapshot of the PFC counters for each priority.memkv net verify --server <ip:port> --auth-key <hex>runs an RDMA round trip to a running server.
When a check fails, use the manual steps below to find the layer that is wrong.
Confirm the negotiated RoCE path MTU
ibv_devinfo -d mlx5_0 | grep -E 'active_mtu|state'
# active_mtu: 4096 (5) <- jumbo working; 1024 (3) means MTU too lowIf this shows 1024 (3) after you set a 9000 MTU on the interface, a hop in the
path is still at 1500. Check the switch port MTU and any bond or VLAN
interface.
Confirm a routable RoCEv2 GID exists
show_gids mlx5_0
# look for RoCE v2 rows with a v4 (::ffff:a.b.c.d) IP — that index is what
# auto-detect picks; pin it as gid_index if you set it by handConfirm DSCP trust and the lossless priority
mlnx_qos -i enp1s0f0
# trust: dscp ; pfc enabled on prio 3 ; dscp 26 -> prio 3Confirm traffic uses the lossless queue
This is the definitive check. While RDMA traffic runs, the counters for priority 3 must increase and the counters for priority 0 (best effort) must not:
ethtool -S enp1s0f0 | grep -E 'prio3|prio0' > before.txt
# run a MemKV read/write workload, then:
ethtool -S enp1s0f0 | grep -E 'prio3|prio0' > after.txt
diff before.txt after.txt
# tx_prio3_bytes must climb; tx_prio0_bytes should not. If prio0 moves and
# prio3 doesn't, the SL/DSCP isn't landing traffic on the lossless queue.Confirm raw RDMA bandwidth between two nodes
# server node
ib_write_bw -d mlx5_0 -x 3 --report_gbits
# client node
ib_write_bw -d mlx5_0 -x 3 --report_gbits <server-ip>
# -x 3 selects a RoCEv2 GID index; expect near line rate with no retriesTroubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Startup error: configured MTU exceeds the port's active MTU | rdma.mtu is larger than the link negotiated | Enable jumbo frames end to end, or set mtu to the active value |
IBV_WC_RETRY_EXC_ERR / status 12, then downgrading engine to inline-bulk TCP | Path MTU is larger than the link MTU, so packets are dropped | Fix the MTU on the fabric and on both ends |
| RDMA works but throughput is low | Traffic is on the best-effort queue | Check that tx_prio3 increases (above), gid_index is RoCEv2, and SL is the PFC priority |
no RoCEv2 GID found warning, gid_index 0 | The NIC has no RoCEv2 GID, or MemKV did not detect it | Find RoCEv2 GIDs with show_gids and set gid_index to one |
Auto-detect leaves traffic_class=0 with a warning | MemKV could not read the NIC's settings | Set traffic_class (DSCP << 2) and service_level yourself |