Faithful cluster DLM registration — Layer 1: MOUNT holds the standing volume lock (vms-3eb/25e) - #1035
Draft
baron-3dl wants to merge 28 commits into
Draft
Faithful cluster DLM registration — Layer 1: MOUNT holds the standing volume lock (vms-3eb/25e)#1035baron-3dl wants to merge 28 commits into
baron-3dl wants to merge 28 commits into
Conversation
…or (vms-3eb) The coordinator counts a joiner once it COMPLETES the light-mode DLM directory-rebuild handshake on the coordinator's VMS$VAXcluster VC. OVMX reaches admission (receives the coordinator's op-06 membership burst, CSB member bit sets) and holds full connectivity, but stays BRK_NON / CLUSTER_NODES=2 because it drives ~zero cat-02 rebuild to the coordinator: it sends only the op-0d self-registration and never CLOSES that rebuild transaction the way a real joiner does (op-01 registrations → op-04 → op-03 COMMIT). This adds the completion OVMX was missing. On receiving the coordinator's op-06 admission burst, OVMX drives the op-04 completion + op-03 COMMIT to the coordinator (one-shot per epoch, re-armed on each new transition open), closing the directory-rebuild transaction its self-registration opened. HONEST BY CONSTRUCTION (INV-6): OVMX holds no persistent cluster lock (its DLM takes only transient RMS/ACP locks — scsd.c:1631 "OVMX holds no locks... revisit when it has a real lock manager to answer FROM"). So the completion registers NOTHING HELD — the builders zero the resource name (body[48:]) and the per-lock handle words (body[20:28]) and keep mode NL (body[30]=0): the honest "my rebuild contribution is complete, I hold nothing" signal. It fabricates no lock state and names no resource OVMX does not own. Frame structure reproduces the JOIN→COORD op-04/op-03 specimens on db20-b's validated member-body layout (Rule 8). This is the minimal A/B test for the CN=3 blocker: if the coordinator counts OVMX off a content-free completion, completion alone suffices; if not, the standing system-lock set is genuinely required — an executive completeness question (make OVMX's mount/XQP take real F11B$/MOU$/LNM$ locks), escalated separately. test_scs_member: new test_dlm_completion_holds_nothing pins the honesty guardrail (resname + handles + mode all zeroed). SEND SITE census updated (cm_send_dlm_completion). Both files -fsyntax-only clean; member test + census green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
… life (vms-25e) [Layer 1, test pending]
Layer 1 of faithful cluster DLM registration (vms-3eb). A real VMS MOUNT holds
the per-volume lock from $MOUNT to $DISMOUNT; that standing lock is the
cluster-wide "this node has this volume mounted" marker the connection manager
re-registers to the coordinator during a directory rebuild. OVMX's executive
previously held only the TRANSIENT XQP sync lock (F11B$s, taken per on-disk
write and dropped) — nothing standing to register. This adds the standing one:
- Generalize acp_vol_resnam -> acp_vol_resnam_kind(vol, kind, ...) so the
per-volume resource name is built for any sub-lock ('s' sync, 'v' volume,
'a' allocation); acp_vol_resnam stays a 's' wrapper (call sites unchanged).
- vms_lock_acp_vol_standing() — an NL-mode $ENQ on F11B$v<label>, held (not
dropped) for the mount life. NL asserts PRESENCE not exclusion: different
resource from the XQP sync lock, holds nothing exclusive, so it never blocks
the XQP, another writer, or another node — deadlock-free, cluster-safe.
- Taken at mount publish (best-effort, AFTER dropping the table lock; a DLM-less
node just leaves vol_lkid 0 — the standing lock is a cluster facility, NEVER a
gate on the mount, INV-6 honest-omission), released at $DISMOUNT (rundown is
the backstop). vol_lkid stored on the executive-global volume row.
INV-6: this is a REAL lock the executive genuinely holds on a REAL mounted
volume — no fabrication. Mirrors the validated vms_lock_acp_vol_ex pattern
(only the mode differs, NL vs EX).
NOT DONE: vms-25e's done-condition needs the QEMU test (mount -> lock held via
GETLKI on F11B$v<label>; $DISMOUNT -> released). Next commit. Layers 2 (scsd
enumeration accessor, vms-1f4) + 3 (scsd registration FSM, vms-74f) follow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…OUNT (vms-25e) Layer 1's done-condition (vms-3eb): assert the executive genuinely holds the standing volume lock a faithful MOUNT holds for the mount life. After $MOUNT of the ODS-2 VDA0:, read the mounted volume's label via GETVOL (the same vol->volname the executive built the resource name from -- getvol copies it verbatim, so the reconstruction is byte-exact, trailing spaces and all), form the F11B$v<label> resource name, and assert GET_RESMASTER reports it FOUND with n_granted >= 1. This is a READ of real lock-manager state (GET_RESMASTER counts all granted locks including the local NL standing lock, unlike DLM_GET_GRANTED which filters to remote-held) -- never a fabricated grant (INV-6). It holds whether the lock was taken by PID 1 at the boot mount or by the test's idempotent re-mount; the resource name matches either way (both from vol->volname). Skips cleanly when /dev/vms is absent, like the rest of this executive-resident test. Layer 1 (executive holds the standing lock, ee4d1f3) is now code + test. The release-at-$DISMOUNT path is the symmetric dismount code; VDA0: is the live system disk and is not dismounted in-test. Next: Layer 2 (scsd enumeration accessor, vms-1f4) + Layer 3 (scsd registration FSM, vms-74f). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…ader (vms-25e) The Layer 1 change (ee4d1f3) declared vms_lock_acp_vol_standing only in src/kernel/vms_internal.h (Linux). kernel-core's vmsfs_acp.c resolves "vms_internal.h" per build, so the NetBSD/vax cross-compile hit vmsfs_acp.c:724: implicit declaration of 'vms_lock_acp_vol_standing' (-Werror). Mirror the declaration into src/kernel-netbsd/vms_internal.h next to vms_lock_acp_vol_ex, exactly as #928 requires for kernel-core lock ops. Linux build + the QEMU standing-lock test were already green; this restores the VAX arch leg. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…em locks (vms-1f4) [Layer 2]
Layer 2 of faithful cluster DLM registration (vms-3eb): the clean seam between
the F11 layer that HOLDS the standing volume lock (Layer 1, vms-25e) and the
cluster layer (scsd) that REGISTERS it to the coordinator. A new read-only ioctl
returns this node's standing cluster-registrable locks -- today one entry per
mounted volume holding its F11B$v<label> lock: {resname, local lkid, mode}. The
lkid is exactly the op-0x01 requester handle scsd puts on the wire (Layer 3).
INV-6: a READ of REAL lock state. count is the number of standing locks the
executive genuinely holds; a volume whose best-effort acquire failed
(vol_lkid == 0) contributes nothing; never a fabricated lock.
Wired across all nine places a new /dev/vms ioctl touches (the "new-ioctl -> N
places" trap), each verified consistent:
- struct + ioctl def (NR 0x3c, next free after the 0x30-0x3b DLM/cluster group --
0x39 was already CLUSTER_MEMBER_SET, caught before it collided) + size/encoding
asserts, in BOTH src/kernel/vms_ioctl.h and src/kernel-netbsd/vms_lock_nb.h,
byte-identical (656 bytes);
- handler vms_ioctl_dlm_enum_standing() in kernel-core/vmsfs_acp.c (walks the
executive-global mounted-volume table under vms_acp_vol_lock, copyout outside
the lock); handler decl in BOTH vms_internal.h (#928);
- dispatch in kernel/vms_module.c and both switches of kernel-netbsd/vms_netbsd.c;
- kif wrapper vms_kif_dlm_enum_standing() decl + impl (libvmssys), fail-honest.
Test (test_syssvc_acp_dlm): after $MOUNT, DLM_ENUM_STANDING surfaces the
F11B$v<label> lock with a real (nonzero) handle -- the (resname, lkid) pair scsd
will register. LCK_K_NLMODE + the struct are reachable in both the Linux and
NetBSD builds of the shared TU (verified, not assumed). Layer 3 (vms-74f) wires
scsd to register these to the coordinator.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…vms-1f4) Layer 2 (2d2f66b) added the enum-standing kif wrapper, whose header touch re-armed the VMS-native toolchain legs (Layer 1's kernel-core-only diff had let the path filter skip them). Two regressions surfaced, both here, both fixed: 1. kif_caller_census (INV-6 gate, "Build & Test" ctest): vms_kif_dlm_enum_standing had no product caller (only the QEMU test) and no unwired marker. Its product caller is Layer 3 (vms-74f, scsd registration). Add the OVMX-UNWIRED: marker on the decl, matching the existing pattern -- to be REMOVED when Layer 3 wires it. 2. LIBVMSSYS$SHR.EXE link (every *VMS-native Migration leg): the wrapper ended `*out = args;`, a 656-byte struct assignment GCC lowers to a libc memcpy() the freestanding libvmssys shareable cannot resolve (DECC$SHR exports no memcpy, vms-61f). Rewrite to write STRAIGHT into *out (no local + no struct copy) -- consistent with how the file already avoids libc via the vms_memset shim. Add an SS$_BADPARAM null-guard, matching vms_kif_get_resmaster. Census gate re-run locally: PASS. Main is green on all these legs (33550492582), confirming this was my Layer-2 regression, not pre-existing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
… coordinator (vms-74f) Layer 3 (faithful cluster DLM registration, vms-3eb) frame builder #1: scs_member_build_dlm_reg_enq -- the cat 0x02 op 0x01 ENQ that registers one of OVMX's REAL standing system locks (the F11B$v<label> volume lock a MOUNT holds, Layer 1) to the COORDINATOR during a directory rebuild. Unlike db20-b's NL-only presence ENQ (which zeroed the id fields because it held nothing), this carries OVMX's OWN real values, at the reference-confirmed offsets (conductor trace of F11B$aSYSDSK1 op-01): - req_lkid @ body[4:8] = OVMX's real local lock handle (from the vms-1f4 DLM_ENUM_STANDING accessor -- a lock the executive genuinely holds); - mst_csid @ body[20:24] = the real coordinator's csid; - resname @ body[48], NL mode @ body[30] (the mode OVMX genuinely holds). Built on db20-b's VALIDATED frame (VAX1 granted 48/48). INV-6: only OVMX's own real values; the ungrounded per-lock lock-mgmt fields (body[24:30]) stay ZERO -- VAX3's kernel-assigned handles are un-replayable and inventing them would be the fabrication trap inverted (conductor's principle -- the coordinator keys on the resource + requester identity, not a foreign handle match). test_scs_member: new test_dlm_reg_enq_carries_real_values pins req_lkid@[4:8], mst_csid@[20:24], cat/op, NL mode, resname, and the ungrounded-fields-zero guarantee. Member suite ALL PASSED. Next: op-04/op-03 real-handle builders + the await-grant receive FSM (scsd calls the accessor, sends op-01 per lock, and on the coord's cat-82 op-01 grant ARRIVAL -- a pure trigger, no value extracted -- sends op-04 -> op-03 with OVMX's own handles). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…fire (vms-74f) Layer 3 frame builders #2/#3 + the safe scsd checkpoint before the FSM: - scs_member_build_dlm_op04 / _commit now take OVMX's OWN real per-lock handle (lkid) and write it at body[20:24] -- the handle the executive DLM holds for the lock (from the vms-1f4 accessor). Per the conductor's handle-chain trace, op-04 and op-03 carry the JOINER's own node-local handle, NEVER VAX3's un-replayable kernel bytes nor the coordinator's granted mst_lkid; the ungrounded second handle word @[24:28] stays ZERO (INV-6 -- don't invent). lkid==0 reproduces the old content-free frame (null case). test_scs_member pins lkid@[20:24], the zeroed second word, NL mode, and no-resname; suite ALL PASSED. - cm_send_dlm_completion is now lkid-aware (function-pointer signature updated). - The OPT-A content-free completion FIRE (post-op-06) is DISABLED: it destabilized the cluster (op-03 with no real op-01 = dangling transaction -> 2/2 reformations, lab-proven). Firing nothing keeps the branch safe until Layer 3's registration FSM lands. That FSM (next commit): post-op-06 enumerate the standing locks, send op-01 per lock to the coordinator, and on the coordinator's cat-82 op-01 grant arrival send op-04 -> op-03 with OVMX's real handle. scsd.c -fsyntax-only clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
… complete The receive-driven FSM that makes a capless OVMX node register its REAL standing system locks to the coordinator and complete the rebuild transaction -- the honest CN=3 path, replacing OPT-A's content-free completion that reformed the cluster. - scsd_enum_standing_locks(): reads the node's standing cluster-registrable locks from the executive over /dev/vms (VMS_IOCTL_DLM_ENUM_STANDING, Layer 2) by direct ioctl (scsd is glibc, not the freestanding vms_kif client). Fail-honest: 0 on any failure; registers ONLY what the executive genuinely reports it holds (INV-6). SCSD_UNIT_TEST stub returns 0 (no /dev/vms in the wire seam). - cm_dlm_frame_common(): factors the per-send SCS/SYSAP envelope. - cm_send_dlm_registration(): post-op-06, one cat-02 op-01 ENQ per standing lock carrying OVMX's OWN real handle (req_lkid) + real resname; mst_csid=0 (resolve -- OVMX lacks the coord's full csid, and 0 was granted for db20-b; INV-6 no-invent). - cm_send_dlm_completion(): rewritten per-lock -- op-04 + op-03 COMMIT carrying OVMX's own real handle, fired on the coordinator's cat-82 op-01 GRANT arrival. - Grant-detect (scsd_sysap_msg_input): mv.is_response && cat DLM && op 0x01 from the coordinator, gated on dlm_reg_sent, one-shot -- a PURE trigger (no handle parsed; op-04/op-03 carry OVMX's own). The await-grant gate is the OPT-A insurance. - dlm_reg_sent one-shot + epoch re-arm; SEND SITE census updated. FSM shape (op-01 -> await grant -> op-04 -> op-03, OVMX's own handles throughout) grounded via the conductor's handle-chain trace. Both build paths -fsyntax-only clean; send-site census 123/0; member builder suite green. The lab is the final validator: fresh vaxlab-2 re-fire grades the 3 signals (real registration fires / reformation GONE / BRK_NON -> counted + CN 2->3). Known lab-reject suspects (fill honestly if VAX2 chokes): op-04's 2nd handle word @[24:28] (zeroed) and mst_csid=0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…t gate (vms-74f) The Layer-3 FSM (98562d2) tripped the Rule 9 runtime_target gate (tests/ integration/test_runtime_target.sh check 3): it greps for absence-words within 24 chars of "/dev/vms" as a "silent userspace fallback" tell, EXCLUDING lines that start with a comment marker. My SCSD_UNIT_TEST stub's TRAILING comment on a code line -- `return 0; /* ... no /dev/vms here */` -- is not a comment-leading line, so the "no /dev/vms" phrase was flagged (a phantom; the code fails honest, it does not fall back). Reword the trailing comment to drop the /dev/vms-absence phrasing. No behaviour change -- the stub still returns 0 (executive-resident; scsd registers only what the executive genuinely reports). Gate grep now CLEAN across src. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
Run 9c: the coordinator DROPS a skeletal op-01 (req_lkid + resname, rest zero); a granted reference ENQ carries a full per-lock record. Fill it entirely from OVMX's OWN genuine lock state (INV-6, no fabrication): scs_member_build_dlm_reg_enq now takes a scs_dlm_reg_fields struct and writes req_lkid@[4:8], dir_csid@[20:24], lock_id@[24:28], flags@[28:30], mode@[30], lockmgmt@[32:36]; req_csid@[16:20] stays 0 (846/846 ref). cm_send_dlm_registration sources dir_csid + n_granted per lock from the executive via a new scsd_res_dir_info() (GET_RESMASTER over /dev/vms); req_lkid/lock_id from the DLM_ENUM_STANDING accessor; mode NL. body[20:24]=dir_csid is OVMX's OWN encoding -- the measured operator-scope decider on re-fire: grant off it => honest CN=3, no cluster-DLM interop; drop => interop scope (SCS$DIRECTORY-agreed id). flags/lockmgmt are the structural lock-mgmt words, flagged secondary iterate-suspects. Self-test: member suite ALL PASSED, send-site census 123/0, scsd.c syntax-clean both arch paths, Rule-9 runtime-target gate 31/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…mpare (vms-74f) The cat-0x82 op-01 GRANT trigger that fires cm_send_dlm_completion tested `mv.is_response && mv.category == SCS_MEMBER_CAT_DLM`. scs_member_parse stores the RAW category byte (body[8]), so a response carries the 0x80 bit IN mv.category (0x82) and mv.is_response is derived from it -- making the condition UNSATISFIABLE (0x82 != 0x02). The grant was never detected, so op-04/ op-03 never fired: a GRANT would dangle indistinguishably from a DROP, making the milestone re-fire uninterpretable. Mask with & 0x7f, matching the parser's own convention (scs_member_parse lines 482/490) and every other DLM-response site. Regression: test_dlm_grant_response_needs_masking asserts a cat-82 op-01 frame parses to is_response=1 with raw category 0x82 != CAT_DLM but (category & 0x7f) == CAT_DLM -- locking the exact trap. Self-test: member suite ALL PASSED, send-site census 123/0, scsd.c syntax-clean both arch paths, Rule-9 gate 31/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…ted membership (vms-655) The SCSD-I-XITDONE self-report claimed "cluster membership COMMITTED ... OVMX is now an admitted cluster member" on receipt of the coordinator's op-06 MEMBERSHIP burst, asserting a 1:1 relationship with the F$GETSYI(CLUSTER_NODES) 2->3 oracle. The 2026-09-02 re-fire refutes that: op-06 arrived, OVMX drove its DLM registration, but VAX2 dropped the op-01 (dir_csid gap, vms-655) and CLUSTER_NODES stayed 2. The 1:1 analysis predates the DLM-registration gate (vms-74f) that op-06 now triggers -- op-06 is NECESSARY but NOT SUFFICIENT for counting. Reword the log + correct the comment: op-06 is the admission INVITATION that opens the transition; counted membership requires the DLM registration to COMMIT cluster-wide (op-01 GRANT -> op-04 -> op-03). Facility tag SCSD-I-XITDONE and the membership_committed latch are KEPT (lab grep + oracle compat, tests/lab/tools/ abrun.sh keys the tag not the body); only the false "admitted" claim is removed. Log/comment only -- no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…time (vms-655)
Root cause of the op-01 DROP (measured 2026-09-02): the executive's DLM directory
(dlm_directory_csid) hashes a resource name over vms_local_csid + dlm_member_csids
to pick the master, but both are insmod module params (0444) defaulting to a
cluster-of-one {1} -- a joining node cannot know its SCSSYSTEMID or the members at
insmod (learned only during the SCS join), and STARTUP.EXE loads vms.ko with an
empty param string. So GET_RESMASTER returned dir_csid=1 (the phantom local id),
which does not match the CSID (1028) the cluster knows OVMX by, and VAX2 dropped
the registration. scsd's op-01 field was faithful -- it reported a mis-configured
executive.
Fix: the live connection-manager feed the static-vector comment in vms_lock.c
already anticipates. New VMS_IOCTL_DLM_DIRECTORY_SET (NR 0x3d, DLM family) pushes
{local_csid, member CSIDs} into the directory; the handler (vms_lock.c) mirrors
DLM_MEMBER_DEPART's locking + directory-cache invalidation and clears the runtime
departed-set (fresh membership). scsd_push_dlm_directory() sources local from
resolve_scssystemid() (=1028) and members from peer_node_number() per live peer
(=each peer's SCSSYSTEMID = its DLM csid, as peer_by_csid reads it), and fires in
the op-06 admission handler BEFORE cm_send_dlm_registration -- so GET_RESMASTER
resolves over the real {1025,1026,1028} set and the op-01 carries the master VAX2
agrees with. Confirmed by wire (conductor): this lab is CSID==SCSSYSTEMID, raw ids,
no 0x2060 form.
INV-6: local is OVMX's resolved SCSSYSTEMID, members are CSIDs the CM actually
sees -- never fabricated; a malformed vector is rejected (SS$_BADPARAM).
Full cascade: vms_ioctl.h (struct + sizeof/encoding asserts, 0xC04C563D),
vms_module.c dispatch, vms_internal.h (Linux+NetBSD prototypes), vms_lock_nb.h
(struct + sizeof assert), vms_netbsd.c (outer group + inner switch), and the
required vms_kif_dlm_directory_set wrapper (OVMX-UNWIRED; kif caller census).
Self-test: member suite ALL PASSED, send-site census 123/0, kif caller census
PASS, scsd.c syntax-clean both arch paths, Rule-9 gate 31/0 + absence scan clean,
NetBSD struct sizeof 76.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…ory hypothesis (vms-655) Revert 90b3bbb (VMS_IOCTL_DLM_DIRECTORY_SET + scsd_push_dlm_directory + the GET_RESMASTER->dir_csid wiring) and set the op-01 dir_csid to a hard 0. The lab disproved "OVMX computes the directory": 90b3bbb armed correctly (op-01 carried dir_csid=0x00010003=OVMX) but BROKE the cluster (CLUSTER_NODES 2->1, VAX2->BRK_NON ~4s after OVMX's op-01) because OVMX asserted ITSELF as master of F11B$vOVMXSYS. Root reason it cannot work: the executive's dlm_directory_csid() uses exec_jhash (vms_lock.c:527), which is explicitly NOT VMS's directory hash (unpublished, Rule 8). Any master OVMX computes is uncorrelated with VMS's choice -> directory conflict -> reformation. Feeding real cluster CSIDs doesn't help: right values, wrong SELECTION. Measured-correct behavior (conductor's ref.pcap + db20-b grant decode): - A joiner NEVER self-masters mid-transition; it registers each resource to a member, which masters it. The GRANT carries no master field -- the master is IMPLICIT (the granting node). - db20-b PROVED a dir_csid=0 op-01 is GRANTED 48/48. It was granted-but- insufficient only because it carried ZERO handles -- NOT wrong target (the reference registers more to the non-coordinator than the coordinator). So: op-01 carries OVMX's REAL held-lock handles (req_lkid/lock_id from DLM_ENUM_STANDING, kept from 5772a86) with dir_csid=0 (OVMX asserts no directory opinion), to the currently-connected member. exec_jhash never runs on this path; OVMX never self-masters. GET_RESMASTER is read only for the honest n_granted count. Keeps 4eeddea (grant-detect masking) + 43d33ae (XITDONE honesty). Self-test: member suite ALL PASSED, send-sites 123/0, kif caller census PASS (balanced after the DIRECTORY_SET revert), scsd.c both arch paths syntax-clean, Rule-9 gate 31/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…vms-655) 5360671 re-fire: no regression (dir_csid=0 held CN stable at 2) but still DROPPED. Conductor's byte-diff of the granted db20-b op-01 vs the dropped one pinned the cause to req_lkid (body[4]): a granted frame encodes OVMX's cluster node-index in the low 16 bits; 5360671 sent DLM_ENUM_STANDING's raw lkid=1 (low word 0x0001 -- a wrong node), and a VAX rejects a lock id whose node-index isn't the sender's. Fix, honest end-to-end (no hardcode -- a fixed node-index would be a fake CN=3): - Learn OVMX's OWN cluster-assigned CSID (0x000X000X) by IDENTITY from the cat-01 op-05 per-member membership record {sysid@body[20], csid@body[36]}, selecting the record whose sysid == our SCSSYSTEMID. Matching on our own identity makes it immune to offset drift (a wrong offset never matches -> we fail honest, no CSID, rather than fabricate). Stored in ovmx_cluster .assigned_csid; low 16 bits = OVMX's node-index. Logged SCSD-I-CSIDLEARN. op-05 precedes op-06 in the choreography, so it is known by registration time. - req_lkid = (OVMX's real lock handle << 16) | node_index. Real handle, real wire-learned node-index -- INV-6 clean. - Zero lock_id/flags/lockmgmt to match db20-b's GRANTED baseline exactly (add back only if the completion needs them). dir_csid stays 0. - Fire the registration ONLY once assigned_csid is learned; otherwise defer (SCSD-I-DLMREGDEFER) rather than send a droppable/fabricated op-01. - Removed the now-unused scsd_res_dir_info (GET_RESMASTER) helper. Self-test: member suite ALL PASSED, send-sites 123/0, kif caller census PASS, scsd.c both arch paths syntax-clean, Rule-9 31/0, absence-word scan clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…ithhold XITGO (vms-655) DETERMINISTIC finding (2026-09-02, reproduced byte-for-byte across two fresh clusters): OVMX's unsolicited pre-barrier cat-02 op-01 origination, fired in the op-06 handler after VAXCLMEMBER but before XITGO, makes the coordinator WITHHOLD XITGO. Result: 0 XITGO, 0 op-0b/0c barrier frames, the SCS$DIRECTORY selfreg (gated at barrier step 5) never fires, VAX1 never pushes its directory rebuild, 0 op-0d, 0 grants, CLUSTATE members=1 (unstable). db20-b, which sent NO such pre-barrier op-01, got XITGO -> 116 barrier frames -> selfreg -> 222 op-0d -> 48 grants. The honest granted model is the REVERSE of origination: stay quiet after op-06, let the barrier run, and register REACTIVELY to VAX1's op-0d rebuild requests (respond-to-rebuild). This removes the perturbing origination fire; the reactive per-op-0d registration lands next in the CM_RSP_DLM echo path. This is a single-variable diagnostic: it isolates whether removing the origination alone restores XITGO -> the barrier -> the selfreg -> op-0d, before the larger reactive-registration port. Self-test: scsd.c both arch paths syntax-clean, send-site census 123/0, member suite ALL PASSED, Rule-9 gate 31/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…t drains VAX1 (vms-655) Step 2 of the port (ff30929 was step 1: remove the XITGO-perturbing origination). ff30929's re-fire CONFIRMED the chain flips with the origination gone: XITGO arrives, the op-0c barrier runs (119 frames), the SCS$DIRECTORY selfreg fires (tag=0x0003), VAX1 pushes 229 op-0d rebuild requests, and VAX1's SDA CSB shows OVMXJ0 admitted MEMBER (sustained) -- first time ever. But grants=0 because OVMX echoed the 229 op-0d without registering, so the rebuild never drained. This ports db20-b's respond-to-rebuild (its line's e08d4f5/5bdbad43, never inherited by HEAD which forked at 160c5f1): in the CM_RSP_DLM echo path, after echoing a NON-COORDINATOR member's op-0d record, originate an honest NULL-mode op-01 registration for the resource it SHOWED us: - scs_member_build_dlm_nl_enq: the proven granted frame (body[4:6]=txn tag, body[6:8]=per-VC counter, mode HARD-PINNED NL, dir_hash honest 0). NOT the node-index construction (that was a coincidental misread of 0x0003=DIR_TAG). - dlm_op0d_resname reads the resource name from body[48] (SCS_DLM_B_RESNAM); dlm_nl_reg_add dedups so each shown resource registers once (cap OVMX_DLM_NL_MAX). - cm_send_dlm_nl_register builds via cm_dlm_frame_common + sends CHOKED. INV-6: OVMX holds NOTHING (NL); it invents nothing -- resource name from the shown record, member_count is OVMX's own true count, dir_hash omitted (0) not invented. This is directory-node participation (tracking, not holding) -- the honest role the rebuild assigns, which VAX1 granted db20-b 48/48 for. The op-06 origination stays removed; its completion FSM is inert (gated on dlm_reg_sent, never set now) -- matching db20-b, which fired no completion. Self-test: scsd.c both arch paths syntax-clean, member suite ALL PASSED, send-site census 125/0 (cm_send_dlm_nl_register added to the SEND SITE TABLE), Rule-9 gate 31/0, kif caller census PASS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…est, echoing the grant handle (vms-655) 2f2025a reproduced db20-b's dead-end: 48 reactive registrations, VAX1 granted all 48, clean CSB MEMBER sustained -- but CLUSTER_NODES stayed 2. The conductor's reference decode showed the counted joiner does what OVMX skipped: after each grant it drives op-04 completion + op-03 COMMIT. OVMX registered and stopped. Completing OVMX's OWN granted registration is HONEST and derivable -- distinct from reciprocal-grant (which the executive-master subagent confirmed is INV-6-forbidden fabrication: OVMX masters none of these resources). The op-04/op-03 carry {handle@body[20:28], resname@body[48], constant status 0x00030001@body[12]} (measured reference layout), and the handle is the one OVMX RECEIVED in the cat-82 op-01 grant (gb[28:36], the master's handle) -- echoed, never synthesized from a lock DB OVMX lacks. OVMX finishes its own handshake and masters nothing. - scs_member_build_dlm_op04_res / _commit_res: the resname-carrying completion builders (the existing op04/commit ZERO resname -- wrong for the reactive flow). - On inbound cat-82 op-01 from a NON-coordinator member for a resource OVMX registered (in dlm_nl_reg), once each (dlm_nl_done dedup): capture the grant's handle + resname and send op-04 then op-03 (cm_send_dlm_completion_res). Honest by construction: if the completion needs a handle NOT in the grant, VAX won't advance -- OVMX never fabricates one (INV-6). OPT-A's dangling-commit reformation doesn't apply -- 48 real grants precede these. Self-test: scsd.c both arch paths syntax-clean, member suite ALL PASSED, send-site census 127/0 (cm_send_dlm_completion_res added to the SEND SITE TABLE), Rule-9 31/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…3) — honest, echoing the grant handle (vms-655)" This reverts commit a554e7c.
…rant inbound op-01 from REAL lock state (vms-16c) Operator-directed (Julie): stop the wire-imitation (reactive NL-registration + echoed completion) and build the VMS-faithful DLM. The executive DLM is already real (vms_lock.c $ENQ/master-on-first-use/grants; VMS_IOCTL_DLM_XNODE real cross-node grants H4-H11) — the gap was scsd wiring the CM protocol to it. - scsd_dlm_master_local: on the op-0d rebuild assignment, OVMX's executive genuinely MASTERS the resource (a real vms_lock_resource, master_csid=local via master-on-first-use) with a local NL $ENQ — holds nothing, but a later inbound ENQ now grants from REAL lock state. Replaces the wire-only "master nothing". - cm_dlm_grant_inbound_op01: an inbound cat-02 op-01 lock REQUEST (previously SILENCED in cm_response_shape "until OVMX has a real lock manager to answer from") is routed to the executive via VMS_IOCTL_DLM_XNODE and GRANTED/QUEUED from real state — the same path the OVMX$DLM SYSAP server already uses. Gated on OVMX_DLM_REAL; flag-off is byte-identical to today (no regression). This commit lands the real grant PATH + proves it on the wire (SCSD-I-DLMMASTER / SCSD-I-DLMGRANT). The cat-82 op-01 RESPONSE frame — whose body[20:56] lock-DB window must be rewritten from real state, NOT echoed — is DEFERRED until the window is grounded against a reference specimen (Rule 10; a guessed window regressed the run in a554e7c). Not building imitation, not guessing frame bytes. vms_lock.c: UNCHANGED — the real DLM engine already exists. Self-test: scsd.c both arch paths syntax-clean, member suite ALL PASSED, send-site census 125/0, Rule-9 gate 31/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…k state (vms-16c) Completes the faithful DLM (3e26962 was the foundation): on an inbound cat-02 op-01 that the executive GRANTS from real lock state, OVMX now SENDS the cat-82 op-01 GRANT reply -- the response cm_response_shape had to silence "until OVMX has a real lock manager to answer from". scs_member_build_dlm_enq_response builds it from OVMX's REAL master state, measured against VAX1's OWN accepted grant format (the 2f2025a ENQ->GRANT diff, NOT the varied reference): body[8]|=0x80 (granted bit), body[12:16]=0x00030001 (granted status), body[28]=master_lkid for a held mode / 0 for NL (the rebuild grant is NL, matching VAX1's NL grants), body[32:36]=0x00fe0000 master-state marker; body[0:4] = OVMX's own SYSAP send/ack counters (scs_member.h: body[0:2]=send-msg#, [2:4]= ack-msg# -- NOT a lock handle, correcting a recurring misread), req_lkid + resname echoed. INV-6: every rewritten field is OVMX's real executive lock state, never fabricated; body[28]=0 for NL carries NO dangling handle (unlike a554e7c's garbage handles that crumbled because they referenced nothing). Sent ONLY on a real executive grant (status==1). Gated on OVMX_DLM_REAL; flag-off byte-identical. vms_lock.c unchanged. Self-test: scsd.c both arch paths syntax-clean, member suite ALL PASSED, send-site census 125/0, Rule-9 gate 31/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
… default (vms-16c)
Two fixes:
1. ANTI-LARP (operator, Julie): the faithful DLM was gated behind a Linux env var
(OVMX_DLM_REAL) injected via the kernel cmdline -- so the SHIPPED OVMX ran the
imitation and only a test-flagged boot did the real thing, and it gated VMS
behaviour on a Linux env var (itself un-faithful). Gate REMOVED: a normally
booted SCSD.EXE now masters rebuild-assigned resources for real + grants inbound
op-01 from real lock state, unconditionally.
2. GROUNDING (the count-gate lead): the flag-on run showed 14,326 SCSD-W-CMUNGROUNDED
-- OVMX's executive granted the inbound op-01 from real lock state and SENT a
grant, but the CM layer sent it OUTSIDE the response machinery (a separate
origination with its own send_seq), so it lacked the request's txn/send_seq
correlation -> VAX rejected it -> re-requested (a 14K retry storm on 2 real
system locks LNM$CWLOGICALS + F11B$aSYSDSK1, ~34/s). Now op-01 returns
CM_RSP_DLM_ENQ from cm_response_shape ("revisit when OVMX has a real lock
manager" -- it now does), and the build dispatch GRANTS from the executive +
builds the cat-82 response with the correlated mp (the request's txn echo,
send_seq, ack), sent through the same grounded machinery as the op-0d echo.
Only on a real executive grant (status==1); otherwise silent (no fake grant).
Removed the separate ungrounded send.
vms_lock.c unchanged. Self-test: scsd.c both arch paths syntax-clean, member suite
ALL PASSED, send-site census 125/0, Rule-9 gate 31/0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…ring the mode byte (vms-16c) The faithful-DLM grant (scs_member_build_dlm_enq_response) wrote the master lock handle with put_le32(body+28, master_lkid). body[28:30] is the 16-bit handle field but body[30] is the MODE byte — so for every handle < 0x10000 (all of them; the storm ran at 0x328) the le32 write pushed the handle's high zero byte into body[30] and clobbered the granted mode to NL. VAX asked for PR (0x03) / CR (0x01), read NL (0x00) in the grant, treated the lock as unsatisfied, and re-requested forever — the measured 34/sec retransmit storm on LNM$CWLOGICALS + F11B$aSYSDSK1 (SCSD-W-CMUNGROUNDED already 0 after the grounding fix, but CN still 2). The grant frame the conductor measured (body[28]=0x328, body[30]=0x00) is exactly what this arithmetic produces. Write the handle as le16 at body[28:30] and carry the granted mode explicitly at body[30] (still OVMX's real executive lock state — the mode the master granted; INV-6 intact). Add test_dlm_enq_grant_honors_mode pinning the regression: a handle < 0x10000 must not zero the mode byte, and the NL grant carries no dangling handle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…aster handle (vms-16c) The mode fix (24fbc64) landed on the wire (grant body[30]=0x03 PR confirmed), but the retransmit storm persisted: OVMX minted a FRESH master lock record for every re-send of the same op-01, so the handle it returned changed each time (0x328 -> 0x329 -> 0x32a ...). A cluster member never saw a STABLE granted lock to correlate, so it re-requested forever (~35/sec on LNM$CWLOGICALS + F11B$aSYSDSK1, CN stuck at 2). A real VMS master is idempotent to a retransmit. In vms_lock_dlm_xnode_dispatch's ENQ case, before running the enqueue core, look up an existing grant keyed by (req_csid, req_lkid) on the resource; if one is granted at a mode that already covers the request, return its EXISTING master handle (SS$_NORMAL) instead of creating a new record. Cross-node only (both ids non-zero); a genuinely new lock or an up-conversion (existing grant weaker than the request) falls through to the core unchanged. INV-6: the handle returned names a REAL lock on the resource's granted queue, never a fabricated one. Ground-source test (test_syssvc_dlm_xnode 1c, through the real /dev/vms): a retransmit of the granted ENQ returns the SAME lkid and leaves exactly one grant on the resource -- no duplicate record. vms.ko builds clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
…cks CLUSTER_NODES 2->3 (vms-16c)
Lab routing (conductor pcap) proved the CN=3 gate is FLOW 1 (OVMX-as-requester),
and it stalls at COMMIT: OVMX sends its 48 own standing-lock op-01 ENQs to VAX1
(1025, the directory node for its file locks), VAX1 grants all 48 back, but OVMX
sends op-04=0 / op-03=0 — cm_send_dlm_completion never fires. The count only
commits once that completion lands (scsd.c XITDONE scope note).
Two-part root cause, both from cm_send_dlm_registration being retired (it perturbed
XITGO) without rewiring the completion:
1. dlm_reg_sent was armed inside cm_send_dlm_registration; with it retired,
NOTHING set the flag — yet the completion gate still required it, so it was
permanently 0 (48 grants, 0 completions).
2. The gate also hard-required cm_peer_is_coordinator, but measured topology is
that OVMX registers with the resource's DIRECTORY node VAX1 (the non-coordinator
member) and the coordinator VAX2 grants OVMX nothing — a coordinator-keyed gate
could never fire here.
Fix (both halves): arm ps->dlm_reg_sent=1 at the live registration origination
(cm_send_dlm_nl_register site), on the peer we register to; and re-key the completion
gate to fire on that same peer (drop the coordinator requirement — the per-peer
dlm_reg_sent flag IS the "peer we registered to" discriminator, set only on that ps).
Faithful VMS: register with, and complete op-04/op-03 to, the directory node that
granted the registration. Both one-shots already re-arm together at transition-open,
so re-joins work.
Verification is the lab re-fire (ground source): op-04/op-03 from OVMX->VAX1 go 0->48,
flow-1 commits cluster-wide, CLUSTER_NODES 2->3. scsd.c syntax-clean; send-site census
125/0 (no new send site — an existing completion is now correctly gated). Flow-2 storm
(inbound grants, incl. the not-assigned LNM$CWLOGICALS) is separate and non-gating,
left for a follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PhM3QcmFEY3p8YNnHGaYwP
… a real lkid, record the grant, complete from GETLKI (vms-16c) fc8540a proved the architecture (VAX2 completed the state transition, OVMX being admitted) but crashed VAX1 with a PLACEHOLDER lock id in the completion. The deeper problem the operator flagged: flow-1 was wire-plumbing with no executive lock state behind it — a NL "holds nothing" registration, a grant never recorded, a completion built from a template. This routes the whole chain through OVMX's executive. Root of the crash (conductor's named VAX<->VAX chain): OVMX's ENQ was DEGENERATE — body[24:28] (the requester's own lock id) was zeroed, so VAX1 had no id to build the normal grant pair and returned only a bare handle; OVMX's completion then had no master id to reference. Fix, all executive-backed: - ENQ: cm_send_dlm_registration now carries OVMX's REAL requester lkid at body[24:28] (from enum_standing, VMS_IOCTL_DLM_ENUM_STANDING — real held locks), and is fired ONCE reactively post-barrier (not op-06-gated → no XITGO perturbation). The NL respond-to-rebuild drain is kept for the member's directory rebuild (db20-b). - Grant-back: OVMX feeds VAX1's grant into its executive via VMS_DLM_OP_GRANT (grant_recv) — a real vms_dlm_origin record keyed by OVMX's own req_lkid, carrying the master's handle + CSID (the same H5 requester-side receive OVMX$DLM uses). OVMX now genuinely tracks the cross-node lock (real BLKAST/LVB/remaster), not a dead echo. - Completion: read master_lkid BACK from the executive (GETLKI, new master_lkid field on the origin accessor) and send op-04/op-03 with it at body[20:24] + OVMX's req_lkid at body[24:28], PER grant-back. Fire ONLY when the executive holds a real master handle — honest omission otherwise (INV-6), so a placeholder can never reach VAX1: crash-safe by construction (worst case CN stays 2, no bugcheck). Every completion field traces to an executive read (enum_standing lkid, GETLKI master_lkid), never plumbed frame->frame. Rewrote test_dlm_completion to the faithful semantics (master handle @[20:24], requester lkid @[24:28]); removed the stale "holds nothing" doc. vms.ko + scsd build clean; member suite ALL PASSED; send-site census 125/0. The named-resource completion OFFSET is the one open question the lab re-fire resolves; the gate makes that resolution safe. DO NOT MERGE fc8540a. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…he fragile dlm_reg_sent gate (vms-16c) 340f94e graded: no crash, ENQ fix confirmed (body[24:28]=0x00010003), VAX1 grants back a real master handle (0x4abc) -- but OVMX's completion never fires and there is NO log to say where the chain dies (grant_recv? GETLKI? or the handler never runs?). Two changes, both to pinpoint and un-stick the break: 1. Drop `&& ps->dlm_reg_sent` from the grant-back gate. The real discriminator is the non-zero echoed requester lkid (body[24:28]) -- a genuine flow-1 grant-back, vs the NL drain which echoes 0. dlm_reg_sent is a per-peer one-shot that a mid-storm transition re-open clears (dlm_reg_sent=0 at transition-open), which would silently drop the completion after the registration but before the grant-back lands. 2. Instrument every step so the next re-fire shows exactly where it dies: - SCSD-I-DLMGRANTBACK: every cat-82 op-01 grant-back, with the extracted reqlkid, master handle, and dlm_reg_sent state. - SCSD-I-DLMGRANTREC: grant_recv status + the GETLKI master handle read back. - SCSD-I-DLMNOCOMMIT: the honest-omission branch (executive holds no master handle). With DLMCOMPLETE (on send) that covers the whole chain. Confirmed not the cause (checked): origin records live on a global list freed only by vms_lock_cleanup (module init-failure / exit), never on fd close -- so the fresh open/REGISTER/close in each helper does not tear down the grant_recv origin. scsd builds clean; send-site census 125/0; member suite ALL PASSED. Still crash-safe (completion only on a real executive master handle). DO NOT MERGE fc8540a. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Faithful cluster DLM registration (WIP, building through layers)
Reaching CN=3 honestly requires OVMX to register the real standing system locks a faithful VMS node holds — proven necessary by the lab (a content-free completion destabilizes the cluster). This PR builds that, layer by layer, each tested.
Layer 1 (vms-25e) — the executive genuinely holds the standing volume lock ✅ code + test
A real VMS MOUNT holds the per-volume lock from
$MOUNTto$DISMOUNT; OVMX previously held only the transient XQP sync lock. Now:acp_vol_resnamgeneralized to_kind(s/v/a);vms_lock_acp_vol_standing()takes an NL lock onF11B$v<label>, held for the mount life (presence marker, not serialization — never blocks the XQP or another node).$DISMOUNT.test_syssvc_acp_dlm): after$MOUNT,GET_RESMASTERon the reconstructedF11B$v<label>reports found +n_granted ≥ 1— a read of real lock state, no fabrication.Next
Draft until all layers land + the lab re-fire confirms CN 2→3. INV-6 throughout: only real locks the executive genuinely holds.
🤖 Generated with Claude Code