Skip to content

Partitions and PKeys: the most common mistake in IB

S2·E3The tenant partition went in and half the cluster stopped talking · Hotel lobby, 02:20, a change window that was supposed to end at midnight

S2·E3Analyze~35 minsources checked todaylab mutates hardwareverified against opensm(8) man page (linux-rdma master) re-fetched 2026-09-07 (default-partition paragraph, grammar, defmember default limited, part_enforce both, allow_both_pkeys off); NVIDIA InfiniBand Security overview re-fetched 2026-09-07 (P_Key bit 15 membership, full-versus-limited match rule)

Builds on: Bringing up OpenSM

Before you read: what do you already know?

3 quick questions. Wrong answers are fine and expected; trying first makes the lesson stick.

After this lesson you can

  • Write a partitions.conf that produces the membership you intended using the documented grammar and defaults.
  • Analyse the default-partition asymmetry and predict which ports become limited members when a partition file appears.
  • Derive who can talk to whom from full and limited membership and explain the pair that cannot.
  • Verify a partition design against the fabric with smpquery and ibdiagnet instead of inferring it from the file.

Episode 3 — The tenant partition went in and half the cluster stopped talking

The situation · Hotel lobby, 02:20, a change window that was supposed to end at midnight

Six hours ago this fabric was fine. Then the co-op’s tenant isolation went in - one partitions.conf, one SM reload - and the sentence on the bridge is one you have heard before: half the nodes joined, ibstat says Active, MPI hangs. The change owner wants to roll back storage firmware that was never in the change. Acceptance is Monday.

Partitions exist because one physical subnet carries traffic that should not mix: tenants, storage, management. A P_Key is the token that decides who may talk to whom, and it carries two levels in one value - the high bit is the membership type, ‘1’ full and ‘0’ limited, which is why 0xFFFF and 0x7FFF are the same partition seen at two levels.[2] Tonight’s outage is one sentence deep: a match occurs only if at least one of the two nodes is a full member, so two limited members of the same partition cannot exchange traffic at all.[2]

Someone reads the new file onto the call: a tenant line per hospital group, and no Default rule. The moment that file appeared, every end port not named in it became a limited member of 0x7fff - the only partition the compute nodes still share.[1] On the floor, the night-shift operator is reading labels aloud to find the SM host; the one that says DO NOT POWER OFF is on a staging box that has not been master since Tuesday.

A partition file is a list of exceptions: whatever it does not name, it demotes.

Segment 1 reads the grammar that wrote tonight.

1What a P_Key is, and the bit that decides everything

NVIDIA frames partitions as the IB analogue of VLANs: “InfiniBand (IB) provides network isolation through network partitions, like Ethernet VLANs (802.1Q).”[2] The mechanism is a 16-bit Partition Key carried in the packet’s BTH — “The P_Key is embedded in the packet’s BTH (L4 header) and is enforced by both the HCA and the switch” — and it applies to “all IB packets, except for SMP MADs.”[2] That exception is why a fabric with a broken partition design still answers smpquery and ibdiagnet while applications hang.

The layout is where partitions stop resembling VLANs. “15 LSB (P_Key[14:0]): This field defines the P_KEY value… 1 MSB (P_Key[15]): This field defines the membership type. ‘1’ means full member, ‘0’ means limited member.”[2] So 0xFFFF and 0x7FFF are the same partition at two membership levels — “The default P_Key value is 0xFFFF/0x7FFF, depending on the membership type.”[2] OpenSM’s config file takes only the value half: “PKey — P_Key value for this partition. Only low 15 bits will be used. When omitted will be autogenerated.”[1]

And here is the rule that produces most of the tickets: “A P_Key match occurs only if at least one of the nodes (sending / receiving) is a full member. Full members can send packets to both full and limited members; limited members can only send packets to full members.”[2] Two limited members of the same partition therefore cannot exchange traffic at all. Worth knowing where that fact lives: the opensm man page never states it, and only NVIDIA’s own security page spells it out — so cite that page, not the man page, when a customer asks you to prove it.[2][1]

2The asymmetry that breaks working clusters

OpenSM always has a default partition: “The default partition will be created by OpenSM unconditionally even when partition configuration file does not exist or cannot be accessed. The default partition has P_Key value 0x7fff. OpenSM’s port will always have full membership in default partition.”[1]

The trap is what happens to everyone else, and the man page states it in one sentence. All other end ports “will have full membership if the partition configuration file is not found or cannot be accessed, or limited membership if the file exists and can be accessed but there is no rule for the Default partition.”[1] Written as the two implicit rules it means:[1]

# no /etc/opensm/partitions.conf at all  ->  behaves as:
Default=0x7fff : ALL=full ;

# a partitions.conf exists with no Default rule  ->  behaves as:
Default=0x7fff : ALL=limited, SELF=full ;

Combine that with the match rule from segment 1 and you have the failure in full. Every compute node is now a limited member of 0x7fff; limited members can only talk to full members; the only full member is the SM’s own port. The fabric is Active everywhere, ibstat is clean, ibdiagnet runs — and MPI hangs. Nothing looks broken because nothing is broken at the link layer.

The grammar you need to avoid it is short. A definition is [PartitionName][=PKey][,indx0][,ipoib_bc_flags][,defmember=full|limited] followed by a colon, a port-GUID list, and a semicolon.[1] defmember=full|limited|both “specifies default membership for port guid list. Default is limited”, and a port written as <PortGUID>[=[full|limited|both]] with the suffix “omitted (or unrecognized)” is assumed limited — so a typo demotes a node just as effectively as an omission.[1] The keywords are ALL, ALL_CAS, ALL_SWITCHES, ALL_ROUTERS and SELF, the last meaning the subnet manager’s port, and “Empty list means no ports in this partition.”[1]

Two more rules that bite on copy-paste. “PartitionName does not need to be unique, PKey does need to be unique. If PKey is repeated then those partition configurations will be merged and first PartitionName will be used.”[1] And the man page gives the safe first line outright: “The following rule is equivalent to how OpenSM used to run prior to the partition manager: Default=0x7fff,ipoib:ALL=full;”.[1]

6 pairs cannot talk
Default (no …0x7fff · ipoibtenant-a0x7ff1 · indx0 · ipoibgpu01tenant A computelimitedlimitedgpu02tenant A computelimitedlimitedstor01shared storagelimitedviz01tenant Blimited
Click a cell: not a member → default → full → limited. * = inherited from defmember.
  • errorThe file exists but has no rule for the Default partition: every end port is silently demoted to LIMITED membership on 0x7fff. Nothing changed on the hosts — the file appearing is the event.
    End ports "will have full membership if the partition configuration file is not found or cannot be accessed, or limited membership if the file exists and can be accessed but there is no rule for the Default partition."
  • errorgpu01 is a LIMITED member of ipoib partition Default (no rule) (0x7fff): the interface comes up and never resolves ARP. RFC 4391 §4.1 requires a full-membership P_Key.
    RFC 4391 §4.1: "a 'Full Membership' P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another"; every IPoIB interface MUST FullMember-join the broadcast-GID group.
  • errorgpu02 is a LIMITED member of ipoib partition Default (no rule) (0x7fff): the interface comes up and never resolves ARP. RFC 4391 §4.1 requires a full-membership P_Key.
    RFC 4391 §4.1: "a 'Full Membership' P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another"; every IPoIB interface MUST FullMember-join the broadcast-GID group.
  • errorstor01 is a LIMITED member of ipoib partition Default (no rule) (0x7fff): the interface comes up and never resolves ARP. RFC 4391 §4.1 requires a full-membership P_Key.
    RFC 4391 §4.1: "a 'Full Membership' P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another"; every IPoIB interface MUST FullMember-join the broadcast-GID group.
  • errorviz01 is a LIMITED member of ipoib partition Default (no rule) (0x7fff): the interface comes up and never resolves ARP. RFC 4391 §4.1 requires a full-membership P_Key.
    RFC 4391 §4.1: "a 'Full Membership' P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another"; every IPoIB interface MUST FullMember-join the broadcast-GID group.
  • errorgpu01 is a LIMITED member of ipoib partition tenant-a (0x7ff1): the interface comes up and never resolves ARP. RFC 4391 §4.1 requires a full-membership P_Key.
    RFC 4391 §4.1: "a 'Full Membership' P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another"; every IPoIB interface MUST FullMember-join the broadcast-GID group.
  • errorgpu02 is a LIMITED member of ipoib partition tenant-a (0x7ff1): the interface comes up and never resolves ARP. RFC 4391 §4.1 requires a full-membership P_Key.
    RFC 4391 §4.1: "a 'Full Membership' P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another"; every IPoIB interface MUST FullMember-join the broadcast-GID group.
tenant-a 0x7ff1

"defmember=full|limited|both — specifies default membership for port guid list. Default is limited." A GUID written without "=full" inherits it; an unrecognised suffix is also read as limited.

"indx0 — indicates that this pkey should be inserted in block 0 index 0." That slot is what the IPoIB driver reads: it "creates one interface for each port using the P_Key at index 0".

FAE angle. Three lines of answer: own PKey 0x7ff1 with defmember=full inside the tenant, limited membership on 0x7fff outside it, part_enforce=both so the leaf switch drops the mismatch instead of trusting the HCA.

Switch-embedded SM (MLNX-OS): ib partition my-partition pkey 0x7ff2 → partition my-partition → member all; read back with show ib partition. It applies "only when the SM is enabled and running on the system."

# /etc/opensm/partitions.conf   —   opensm -P /etc/opensm/partitions.conf -Z both
tenant-a=0x7ff1,indx0,ipoib : 0x0002c90300a1b201, 0x0002c90300a1b301 ;

Definition: [PartitionName][=PKey][,indx0][,ipoib_bc_flags][,defmember=full|limited] then " : " a port-GUID list and " ; ". Keywords: ALL, ALL_CAS, ALL_SWITCHES, ALL_ROUTERS, SELF (the subnet manager's port). "Empty list means no ports in this partition."

"The following rule is equivalent to how OpenSM used to run prior to the partition manager: Default=0x7fff,ipoib:ALL=full;"

This is the broken design: a tenant stanza whose members inherit defmember=limited and no Default rule. Read the can-talk-to matrix, then switch the Default rule back on and change defmember to full and watch the matrix fill in.

3Designing two tenants, and what NVIDIA recommends

The multi-tenant answer is three decisions, and they follow directly from the match rule.

First, keep 0x7fff as the management partition but demote the tenants in it. NVIDIA’s own hardening guidance is to “Define all end nodes as limited members on the default PKEY” with default_membership = limited.[4] Management stays reachable because the management node is a full member; tenants cannot reach each other over the default partition because two limited members never match.[2]

Second, give each tenant its own PKey with full membership inside it, so that tenant nodes can talk to their own peers:[1]

Default=0x7fff, ipoib : ALL=limited, SELF=full ;
tenant-a=0x7ff1, ipoib, indx0, defmember=full : 0x0002c90300a1b201, 0x0002c90300a1b301 ;
tenant-b=0x7ff2, ipoib, defmember=full : 0x0002c90300c7d101 ;
storage=0x7ff8, defmember=limited : 0x0002c90300a1b201, 0x0002c90300c7d101, 0x0002c90300b4c101=full ;

Read the last line carefully: the storage target is the only full member, every client is limited, so clients reach storage and not each other. That is the hub-and-spoke pattern the membership bit was designed for.

Third, make the switch do the enforcement rather than trusting the hosts. -Z/--part_enforce [both | in | out | off] sets “the partition enforcement type (for switches)… Default is both”, and UFM ships part_enforce = both (default- outbound and inbound) marked read-only.[1][11] Leave it at both. Related and off by default: -W/--allow_both_pkeys decides “whether both full and limited membership on the same partition can be configured in the PKeyTable. Default is not to allow both pkeys.”[1]

Two side effects to plan for. If a partition will carry IPoIB it needs the ipoib flag, which “indicates that this partition may be used for IPoIB, as a result the IPoIB broadcast group will be created with the mgroup_flag flags given, if any” — and the group’s defaults are rate=3, mtu=4 (2048), sl=0, scope=2, Q_Key=0x0b1b for IP groups, with an explicit “WARNING: changing this [Q_Key] for the broadcast group may break IPoIB on client nodes!!”[1] And RFC 4391 requires that “a ‘Full Membership’ P_Key (high-order bit is set to 1) MUST be used so that all members may communicate with one another”, with every IPoIB interface FullMember-joining the broadcast group.[8] A tenant partition of limited members with the ipoib flag therefore produces an interface that comes up and never resolves ARP.[8][9]

On the switch side the same objects exist in MLNX-OS — ib partition my-partition pkey 0x7ff2, then partition my-partition and member all, inspected with show ib partition — with the caveat that “The partitions configuration is applicable and to be used only when the SM is enabled and running on the system.”[5] The UFM appliance CLI exposes the management partition’s default membership as ib partition management defmember <full|limited>, and “It is not possible to modify the defmember in case OpenSM or UFM are running.”[10]

4Verify against the fabric, never against the file

A partition design is only real once the SM has programmed it, and there are three read-back paths.

Per port, from the SMA and with no SA involvement, smpquery takes an operation name and a LID and port: smpquery PKeys <lid> <port> returns that port’s PKey table, in the same family as smpquery SL2VL and smpquery VLArb.[6] This is the narrowest possible check and the one to run on the one node that cannot reach its peer.

Fabric-wide, ibdiagnet writes ibdiagnet2.pkey — “pkey tables” — alongside ibdiagnet2.slvl and ibdiagnet2.sm.[7] Ask a customer for that file rather than for their partitions.conf, because the file states intent and the dump states outcome, and the whole class of bug in this lesson lives in the gap between the two.

On the host, IPoIB exposes what it is actually using: the driver “creates one interface for each port using the P_Key at index 0”, child interfaces are created with echo 0x8001 > /sys/class/net/ib0/create_child producing ib0.8001, and /sys/class/net/ib0/pkey reports the key in force.[12] NVIDIA’s DOCA page documents the same mechanism in shorthand — echo 1 also yields ib0.8001, because “the actual PKey used is a 16-bit number with the most significant bit set”.[9] That is where indx0 connects to something visible: the partition marked indx0 is the one the base interface picks up.[1][12]

Diagnose and fix a partition outage

Case: a customer added a storage tenant last night. This morning 24 of 48 nodes cannot run MPI jobs with each other. All ports are Active, ibdiagnet runs clean, IPoIB interfaces exist on every host.

  1. Read intent first, on the SM host:
    cat /etc/opensm/partitions.conf
    Expected in this failure: a storage=0x7ff8 : … stanza and no Default rule. That single omission demotes every end port to limited membership of 0x7fff.[1]
  2. Confirm the SM is loading the file you just read. Check the command line for -P/--Pconfig, or the config file for the partition path; MLNX_OFED documents the default as /etc/opensm/partitions.conf, and a UFM-managed fabric keeps its own copy under /opt/ufm/files/conf/opensm/.[1][11]
  3. Read reality fabric-wide:
    ibdiagnet
    sed -n '1,60p' /var/tmp/ibdiagnet2/ibdiagnet2.pkey
    grep -n -i '0x7fff' /var/tmp/ibdiagnet2/ibdiagnet2.pkey | head -20
    Expected: ibdiagnet2.pkey shows compute ports holding 0x7fff as limited members. Note the output directory the run printed — the documentation disagrees between /var/tmp/ibdiagnet2/ and /var/tmp/ibdiagpath/, so take the path from the run.[7]
  4. Narrow to one port to make the evidence undeniable:
    smpquery PKeys <lid> 1
    Expected: the table contains 0x7fff without the membership bit set, matching the dump.[6]
  5. Explain the symptom from the rule, not from a guess: every compute port is now a limited member of the only partition they share, and a match requires at least one full member — so compute-to-compute traffic on 0x7fff has no valid key pair.[2]
  6. Fix minimally. Add the Default rule the file was missing, deciding deliberately between the permissive form and the hardened one:
    # restore prior behaviour exactly
    Default=0x7fff, ipoib : ALL=full ;
    # or the hardened multi-tenant posture
    Default=0x7fff, ipoib : ALL=limited, SELF=full ;
    The second is NVIDIA’s guidance for multi-tenancy and requires that each tenant also has its own partition with defmember=full, or you have re-created the outage on purpose.[4][1]
  7. Apply and verify in the same order you diagnosed. Reload the SM, then re-run ibdiagnet and re-read ibdiagnet2.pkey, then smpquery PKeys on the same port as step 4, then run the MPI job.[7][6] Rollback: restore the previous partitions.conf from backup and reload.
  8. Close the case with the two artifacts that prove it: the before-and-after ibdiagnet2.pkey and the diff of partitions.conf.[7]

What the dump said and the file did not

How it ended

ibdiagnet2.pkey and a single smpquery PKeys on one stranded port agree: 0x7fff present, membership bit clear.[7][6] The Default rule goes back in ahead of the tenant lines, the SM reloads, MPI runs, and the case notes close on two artifacts - the before-and-after dumps and the diff of partitions.conf.[1][7]

What you say at 03:10: “Your file said what you intended. The fabric did what the file left out.”

At 03:40 the storage group posts in the same channel. Since the tenants went in, their reads collapse every time a training job ramps - and they have a printed QoS policy that says this cannot happen.

Lab

Mutating steps ahead. Needs a maintenance window, out-of-band access (BMC/iDRAC/rshim console) and a rollback path. Record the pre-flight inventory before changing anything. Never on a production host.

Goal: apply a two-tenant partition file on the Dell lab and read it back off the ports. Step 4 changes fabric-wide membership; its rollback is named and must be tested before you start.

  1. Pre-flight inventory, read-only:
    sudo cp /etc/opensm/partitions.conf /root/partitions.conf.bak 2>/dev/null || echo "no existing file - that itself is the ALL=full case"
    ibstat -p
    ibstat | grep -E "Base lid|State:"
    smpquery PKeys <lid> 1 | tee /tmp/pkeys-before.txt
    cat /sys/class/net/ib0/pkey 2>/dev/null
    Expected: port GUIDs and LIDs for both hosts, Active ports, a PKey table containing 0x7fff with full membership, and 0x7fff from sysfs if IPoIB is up.[6][12] If there was no existing partitions.conf, note it: your rollback is deleting the file, not restoring one.
  2. Write the file using the real GUIDs from step 1, keeping the Default rule first:
    Default=0x7fff, ipoib : ALL=full ;
    tenant-a=0x7ff1, ipoib, indx0, defmember=full : <host_a_guid>, <host_b_guid> ;
    Note that this Default line is the permissive one on purpose: change one thing per lab.[1]
  3. Dry-check before applying: re-read your file against the grammar and confirm both GUIDs are port GUIDs from ibstat -p, not node GUIDs. A node GUID here produces a partition with no members and no error.[1]
  4. Mutating — point the SM at the file and reload. Rollback: restore /root/partitions.conf.bak (or delete the file if there was none) and reload the SM the same way.
    sudo kill $(cat /var/run/opensm.pid)
    sudo opensm -g <port_guid> -p 13 -P /etc/opensm/partitions.conf -B -J /var/run/opensm.pid
    sleep 20
    grep -c "SUBNET UP" /var/log/opensm.log
    Expected: one further SUBNET UP.[1]
  5. Read back what was programmed, read-only:
    smpquery PKeys <lid> 1 | tee /tmp/pkeys-after.txt
    diff /tmp/pkeys-before.txt /tmp/pkeys-after.txt
    Expected: 0x7ff1 now present with full membership alongside 0x7fff.[6]
  6. Bring up the tenant’s IPoIB child interface and use it, read-only in effect:
    echo 0xfff1 | sudo tee /sys/class/net/ib0/create_child
    ip addr add 10.10.1.1/24 dev ib0.fff1 && ip link set ib0.fff1 up
    ping -c3 10.10.1.2
    Expected: ib0.fff1 exists — 0xfff1 is the full-membership form of the tenant key 0x7ff1, because “the actual PKey used is a 16-bit number with the most significant bit set”, which is also what RFC 4391 requires of an IPoIB partition — and it pings its peer once both sides are configured.[9][8] Rollback: ip link set ib0.fff1 down then echo 0xfff1 | sudo tee /sys/class/net/ib0/delete_child — “when deleting the interface you must use the PKey value with the most significant bit set”.[9]
  7. Reproduce the outage deliberately, mutating. Comment out the Default line, reload the SM, and re-read smpquery PKeys and the ping. Expected: 0x7fff now appears as a limited membership and cross-node traffic on the default partition stops while the tenant partition keeps working.[1][2] Rollback: uncomment the line and reload.
  8. Fabric-wide evidence, read-only: ibdiagnet, then read ibdiagnet2.pkey from the directory the run printed and confirm it agrees with smpquery.[7] Keep the before-and-after copies — that pair is the artifact you would attach to a customer case.
  9. Restore: put back the original file (or remove it), reload the SM, re-run step 5’s diff until it is empty, and delete the child interface if you have not already.

Retrieval check

10 questions from memory. Answer before looking anything up; misses become flashcards.

Explain it to a Dell SE

A Dell customer wants two tenants isolated on one InfiniBand fabric and asks whether it works like VLANs. Explain in five sentences how partitions differ and name the one mistake that will break their cluster.

14 flashcards for this lesson — 0 in deck. Spaced review lives at /review.

Sources

Facts in this lesson were checked against opensm(8) man page (linux-rdma master) re-fetched 2026-09-07 (default-partition paragraph, grammar, defmember default limited, part_enforce both, allow_both_pkeys off); NVIDIA InfiniBand Security overview re-fetched 2026-09-07 (P_Key bit 15 membership, full-versus-limited match rule). Dates are when each page was fetched.

  1. opensm(8) man page source (linux-rdma/opensm master) · fetched 2026-09-07
  2. Security in InfiniBand - NVIDIA InfiniBand Security Overview and Guidelines · fetched 2026-09-07
  3. OpenSM partitions.conf configuration reference · fetched 2026-09-07
  4. NVIDIA InfiniBand Security: Practical Guidelines · fetched 2026-09-07
  5. Subnet Manager - NVIDIA MLNX-OS User Manual v3.12.6200 LTS · fetched 2026-09-07
  6. smpquery(8) man page (rdma-core master) · fetched 2026-09-07
  7. ibdiagnet Dump Files - IBUtils2 Utility Documentation · fetched 2026-09-07
  8. RFC 4391 - Transmission of IP over InfiniBand (IPoIB) · fetched 2026-09-07
  9. IP Over InfiniBand - DOCA 3.5.0 · fetched 2026-09-07
  10. InfiniBand Commands - UFM Enterprise Appliance Software 1.6.0 · fetched 2026-09-07
  11. UFM Enterprise 6.24.1: UFM Subnet Manager Default Properties · fetched 2026-09-07
  12. Linux kernel IPoIB documentation - Partitions and P_Keys · fetched 2026-09-07

The same idea elsewhere

Other lessons that cover this ground, sometimes from another course's angle.