vms-dca9: DLM rung H10b — a cross-node lock survives its master's departure (remaster rebuild) - #928
Merged
Conversation
…5 REBUILD + GET_GRANTED)
The executive half of "a cross-node lock survives the remaster": a surviving
holder re-registers its lock on the NEW master, which reconstructs it directly
into res->granted from the holder's REAL origin state.
- New DLM wire op SCS_DLM_OP_REBUILD / VMS_DLM_OP_REBUILD = 5 (an OVMX design
choice, Rule 8 -- the DLM SYSAP wire is OVMX's own). Registered in every place:
the scs_dlm.h + vms_ioctl.h op enums, the scs_dlm.c codec validator +
scs_dlm_op_name, the scsd.c static_assert pinning the two enums, and the
vms_lock_dlm_xnode_dispatch switch. Reuses the existing frame (resnam +
req_lkid + mode + req_csid -- no new field).
- vms_lock_dlm_xnode_rebuild(): RECONSTRUCTS the holder's exact prior grant --
resource_find_or_create, a lock entry stamped req_csid=holder, req_lkid=the
holder's own handle, granted_mode=the mode it really held, added DIRECTLY to
res->granted (like the DEQ handler manipulates the queues directly), and this
node becomes the master. It does NOT re-run the enq/grant core, which would
re-derive a grant against this (new) node's empty queue rather than preserve
the holder's real lock. INV-6: the values are the holder's REAL origin state
transported over SCS; a zero/NL rebuild is refused (SS$_BADPARAM).
- New readback ioctl VMS_IOCTL_DLM_GET_GRANTED (0x37, additive): reports the
first remote-held granted lock's {holder_csid, holder_req_lkid, granted_mode}
so the harness can VALUE-VERIFY the rebuilt lock equals the one held
pre-departure (GET_RESMASTER exposes neither mode nor handle). ABI + asserts in
both headers; dispatch both backends; census-reconciled up-front -- the
vms_kif_dlm_get_granted wrapper is declared OVMX-UNWIRED (scsd issues it
directly), so kif_caller_census stays green.
vms.ko + userspace build clean; ctest kif_caller_census passes. The scsd collect
send (peer_by_csid + REBUILD, SEND SITE TABLE) + the 3-node h10b harness follow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…+ 3-node harness (a cross-node lock survives its master's departure) The scsd userspace half of H10b, on top of the already-committed executive (op-5 REBUILD dispatch + VMS_IOCTL_DLM_GET_GRANTED, 8ea2c98): node A holds a cross-node lock on RES_C (mastered by C); when C departs gracefully, A re-registers that SAME lock -- its REAL req_lkid/mode/req_csid, read fresh via GETLKI, never assumed -- on RES_C's new directory master via a TARGETED SCS_DLM_OP_REBUILD send. The new master reconstructs it directly into res->granted and the harness value-verifies the readback three-way against what A sent. scsd.c (src/vmsscs/scsd.c): - peer_by_csid(): the first DLM sender that must target a peer OTHER than the one whose join-tick context drove the call (mirrors scsd_peer_by_sys's by-identity lookup, by CSID instead). - scsd_dlm_client_send_rebuild(): the targeted REBUILD send -- a NEW send site, registered in the CHOKED SEND SITE TABLE (scs_send_sites census green). - scsd_dlm_h10b_read_own_mode() / scsd_dlm_h10b_get_granted(): direct-ioctl GETLKI / VMS_IOCTL_DLM_GET_GRANTED readbacks, mirroring the existing H9/H10a idiom (scsd is a glibc process; these are OVMX-UNWIRED like their kif siblings, so kif_caller_census stays green). - scsd_dlm_h10b_rebuild_on_depart(): the driver, hooked into the SAME class-0x04 self-departure receive path H10a's ingress uses -- runs only on the node that actually holds the lock (dlm_h10b_hold_established). - scsd_dlm_send_enq(): new optional OVMX_DLM_ENQ_CSID targeting env. Measured necessity: the receive-side ENQ dispatch masters UNCONDITIONALLY whatever it gets (no directory check), so in a 3-node cluster an untargeted send (every prior 2-node DLM harness never exercised this) would also hand a bogus master role to the wrong peer. Additive; absent, every peer is still a legal target as before. - scsd_dlm_cli_msg_input(): the GRANT branch that establishes A's real pre-departure hold (dispatches into A's own executive, so GETLKI reads a genuine origin record). - scsd_dlm_srv_msg_input(): the REBUILD receive branch (new master side) -- dispatch + VMS_IOCTL_DLM_GET_GRANTED readback + SCSD-I-DLMREBUILT marker. Harness (copy-extend of the H10a rung, #927): - tests/qemu/init_dlm_h10b.sh, run_dlm_harness_h10b.sh, Dockerfile.dlm-harness-h10b -- 3 real-executive QEMU nodes A/B/C on a shared mcast fabric; A holds RES_C EX via C (SCSD-I-DLMHOLDOK), C departs gracefully, A rebuilds onto the survivor B (SCSD-I-DLMREBUILDSENT), B reconstructs + reads back (SCSD-I-DLMREBUILT). Runner asserts three-way value equality (sent lkid/mode == read-back lkid/mode, read-back holder_csid == A's CSID) and the new-master target is the survivor, never the departed node. - .github/workflows/ci.yml: new dlm-harness-h10b job + path filters, mirroring the h10a job shape. PROVEN on a real 3-node QEMU cluster (KVM, docker as build/test tooling only, Rule 9): DLM HARNESS H10b PASSED, exit 0. SCSD-I-DLMHOLDOK master_csid=1032 mode=EX; SCSD-I-DLMREBUILDSENT csid=1031 lkid=0x00000001 mode=EX sent=1; SCSD-I-DLMREBUILT found=1 n_granted=1 holder_csid=1030 lkid=0x00000001 mode=EX. scs_send_sites (113 checks) and kif_caller_census both green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The shared src/kernel-core/vms_lock.c dispatch references VMS_DLM_OP_REBUILD on BOTH backends, but the executive commit added the define only to the Linux vms_ioctl.h — amd64-Linux built clean while the NetBSD/amd64 cross-compile failed with 'use of undeclared identifier VMS_DLM_OP_REBUILD' (vms_lock.c:2298). The amd64-green-not-twin-proven trap: the NetBSD full-module cross-compile is the authoritative gate. Symbol-diffed every VMS_DLM_OP_* / VMS_IOCTL_DLM_* / struct vms_dlm_* the shared TU uses against the mirror — REBUILD was the only gap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 30, 2026
…(the finale) (#930) * vms-ec75: design note — DLM distributed deadlock detection (rung H11) Edge-chasing deadlock search over the distributed wait-for graph: a new SCS_DLM_OP_DLKSRCH probe follows the wait-for edges the master already computes (blocking_csid/blocking_req_lkid), detects a REAL cross-node cycle, and aborts one deterministic victim (the initiator) with SS$_DEADLOCK via the existing GRANT-reply path. Clean-room: structure from standard edge-chasing + public OpenVMS lock-management docs; the byte-level frame is an OVMX design choice (Rule 8). Implementation + 2-node cycle harness follow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-ec75: fix design — GLOBAL-min victim selection, not local-initiator (double-victim hazard) 'Victim = the initiator' is unsafe: in a 2-node cycle both ends can initiate a search concurrently, each probe returns to its own initiator, and BOTH abort -> two processes killed when exactly one should be (the data-integrity failure this rung prevents). Fixed: the probe carries a running MIN (csid,lkid) over every request it chases; edge-chasing visits the whole cycle before closing, so every probe computes the SAME global-min victim regardless of who initiated. Cycle-close tests the initiator; the ABORTED request is the global min. Victim delivery is idempotent (a second VICTIM for an already-aborted request is a no-op). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-ec75: revise chase — CSID-keyed master-and-home-centric, not proc-based (H11 IS buildable) The Opus impl pass correctly found the proc-based chase framing (find the holder's process P, does P have a waiting request) unbuildable: a cross-node lock has no owning process. But that walled framing was the design's error, not a real prerequisite. The distributed wait-for graph is READABLE today, CSID-keyed and split across two authorities that existing state already holds: - HOME: grant_recv creates a vms_dlm_origin even for a QUEUED reply (granted_mode==NL, carrying resnam+master_csid) — so a node's pending cross-node waits are enumerable (confirmed: scsd's queued-reply path, dlm_pend_seen). New readback ioctl reads the existing origin list. - MASTER: res->granted is keyed by req_csid; H10b's VMS_IOCTL_DLM_GET_GRANTED already returns holder_csid/holder_req_lkid for a resource — reuse it. The chase queries home (what does CSID H wait for) then master (who holds it), hop by hop, reading only real state. No P2 app-acquisition path, no stored blocker edge — the 2-node cycle is daemon-choreographed + CSID-keyed like every DLM rung. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * vms-ec75: DLM rung H11 -- cluster-wide distributed deadlock detection The LAST DLM rung. Extends the local wait-for-graph detector to a cluster-wide one: a deadlock CYCLE whose edges cross nodes is detected by an edge-chasing search (Chandy-Misra-Haas) over REAL executive state, and EXACTLY ONE deterministic victim's queued $ENQ is aborted with SS$_DEADLOCK (3594), breaking the cycle. Executive (shared kernel-core + both backends): - SCS_DLM_OP_DLKSRCH = 6 wire op, registered in all N sites: scs_dlm.h enum + scs_dlm.c codec/op_name, vms_ioctl.h VMS_DLM_OP_* mirror AND the NetBSD mirror vms_lock_nb.h (#928 twin trap), scsd.c static_assert, vms_lock.c dispatch. Phase (SEARCH-HOLDER / SEARCH-RESOURCE / VICTIM) rides the frame `flags`; ttl rides `status`; the running global-min victim rides the valblk slots. - New readback ioctl VMS_IOCTL_DLM_ENUM_WAITS (0x38): the HOME authority -- enumerate this node's pending (NL) cross-node origins (resnam + master + handle), one outgoing wait-for edge each. Reuses GET_GRANTED (0x37) as the MASTER authority ("who holds R?"). Both wired on Linux + NetBSD; a vms_kif_dlm_enum_waits wrapper holds the kernel-floor. - The VICTIM leg is dispatched into the executive (vms_lock_dlm_xnode_ dlksrch): find the queued cross-node waiter (req_csid,req_lkid) on res->waiting, remove it, complete with SS$_DEADLOCK. Idempotent. Daemon (scsd): the initiate (at the master where a cross-node $ENQ queues behind a REMOTE holder), the two-phase chase orchestration bouncing home<->master by CSID (peer_by_csid) over the two readback authorities, the GLOBAL-min victim selection (not local-initiator -- avoids the double-abort hazard), and the abort + unprompted GRANT(SS$_DEADLOCK) to the victim's $ENQ. Two new CHOKED senders (scsd_dlm_send_dlksrch probe forward + scsd_dlm_send_victim_grant victim signal), both in the SEND SITE TABLE. Harness (3-node A/B/C, C masters both contended resources so both holds and both waits are cross-node): init_dlm_ec75.sh + run_dlm_harness_ec75.sh + Dockerfile.dlm-harness-ec75, and the dlm-harness-ec75 CI job. PROVEN ON A REAL 3-NODE QEMU CLUSTER (KVM): a genuine cross-node cycle (A waits-for B on RES_E, B waits-for A on RES_D) was DETECTED -- SCSD-I-DLKCYCLE on C (initiator=1030, closed on holder 1030) -- and EXACTLY ONE victim aborted: node A's WAIT $ENQ returned status=0x00000E0A (SS$_DEADLOCK), node B's request stayed queued. Every hop reads real state (enum/get_granted return SS$_NORMAL off live ioctls); INV-6 holds -- a dropped/ttl-expired probe reports no deadlock, never a fabricated cycle. Censuses green: send-sites 117/0, kif caller PASS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 30, 2026
…der is COMPLETE (H0→H11) (#931) The cluster-dlm.yaml was last_reviewed 2026-08-28, before H10b/e84/H11 landed — it still read remaster/LVB/deadlock 'absent' and BLKAST 'deferred', which the merges (#918 BLKAST H6, #923/#925 LVB H8/H9, #927/#928 remaster H10a/H10b, #929 e84 directory-guard, #930 H11 deadlock — all on main) made false. Update all four items partial→verified with verified_against pointing at the multi-node /dev/vms harnesses that prove them, rewrite the summary to the complete ladder + the honest remaining DEPTH (vms-04f concurrent-initiate proof, vms-b96 directory REDIRECT, vms-d1f post-1.0 app-acquisition), add REBUILD=5/DLKSRCH=6 to the opcode set, and render_compat.py (docs/compatibility-surface.md regenerated, drift gate clean). Closes the vms-7fa epic on the compat surface. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 30, 2026
* vms-0dd: executive parent-lock linkage + record-lock design (foundation)
Half (a) of RMS record locking: the DLM engine now HONORS the parent lock. The ABI
already carried it (vms_kif_enq parid, vms_enq_args.parid, vms_getlki_args
parent_id) but the executive dropped it (args.parent_id = 0 /* TODO */). Now:
- vms_lock_entry gains a parent_id field (BOTH src/kernel/vms_internal.h and the
NetBSD mirror src/kernel-netbsd/vms_internal.h -- the #928 twin trap),
- vms_enq_core_ex stores it from args.parid at lock creation,
- GETLKI reports lock->parent_id instead of 0.
Purely additive: every existing passes parid=0 (a root lock), so the H0->H11
DLM harness + the vms-50e file-lock (both parentless) are unchanged -- the no-parent
path must stay green (the regression net). The cross-node origin GETLKI path keeps
parent_id=0 (an origin record has no local parent). Parent-child AUTO-RELEASE
cascade is deferred (vms-489); RMS releases records explicitly, so it is not needed.
Plus docs/design-rms-record-lock.md: the record-lock protocol (half b) -- RFA-named
per-record , parent = the file-access lock (vms-50e), mode from rab$l_rop
(NLK->none / RLK->PR / default->EX), seam at the rms_record.c dispatch level
(one record lock per stream), conflict -> RMS$_RLK. Implementation of half (b)
follows.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* vms-0dd: fix record-lock design — RLK/NLK are READ MODIFIERS, not lock modes
Conductor caught a plausible-but-wrong mapping (my first cut had RLK→PR). The RMS
status codes are the oracle: RMS$_OK_RLK 'record successfully read, record locked'
proves RAB$M_RLK = READ-THROUGH of a locked record, not a shared read lock.
Corrected: DEFAULT $get → EX (the locking read; second stream → RMS$_RLK); NLK →
NO lock (non-locking read); RLK → read-through (probe the record's real lock state
via NOQUEUE EX; locked-by-another → read + RMS$_OK_RLK, else read + NORMAL) — the
OK_RLK-vs-NORMAL split decided by REAL DLM state (INV-6). The proof must exercise
the RLK read-through + NLK VARIANTS, not just the default→RMS$_RLK conflict.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* vms-0dd half (b): RMS RECORD-level locking behind the DLM
Completes RMS-behind-DLM (vms-407-B) after vms-50e's FILE-level share
arbitration and this item's half (a) executive parent_id linkage
(ca673c87): a RAB's record-locking intent (rab$l_rop) now takes a real
per-record $ENQ, a CHILD of the FAB's file-access lock, so two streams
contending for one record are arbitrated by the real executive lock
manager -- never a userspace record table (INV-6).
Corrected read-modifier model (RAB$M_NLK/RAB$M_RLK are modifiers, not
lock modes -- the RMS status codes are the oracle), wired centrally at
the rms_record.c dispatch level:
- default $get/$find: EX $ENQ (parid = file lock). Conflict -> RMS$_RLK.
- RAB$M_NLK: no $ENQ at all, a dirty read.
- RAB$M_RLK: throwaway EX/NOQUEUE probe decides RMS$_OK_RLK (read-through
a genuinely locked record) vs normal (probe granted, immediately DEQ'd).
- $put: EX-locks the freshly written record.
- $update/$delete: gated on the stream's own stashed record lkid ->
RMS$_CUR if it holds none.
- Release: next $get/$find, RAB$M_NLK, sys$connect/sys$disconnect.
New RAB._rec_lock_lkid field (rab.h) holds one record lock per stream.
The record locator is the byte offset every org get/find handler already
computes (rab->_last_rec_offset) -- no org handler in this tree populates
rab$w_rfa, so this is OVMX's own construction over the public "one
resource per record" contract, not a disclosed RFA encoding (Rule 8).
vms_kif_getlki_parent (vms_kif.c/.h) is a new sibling of vms_kif_getlki
that also surfaces the GETLKI parent_id field ca673c87 wired into the
kernel but the original wrapper never exposed -- same ioctl, no new op,
every existing vms_kif_getlki call site untouched.
Upgraded rms_record.c's and rms_core.c's OVMX userspace-service-register
declarations (sys$get/put/update/delete/find, sys$connect/disconnect) to
reflect the new executive reach; both standing gates
(test_userspace_service_register.sh, test_kif_caller_census.sh) pass.
Proof: tests/qemu/test_syssvc_rms_reclock.c, run against a real vms.ko +
/dev/vms + the writable real-VAX ODS-2 fixture on DKA0: (a hand-built
minimal QEMU harness mirroring tests/qemu/run_tests.sh, given constrained
shared-host disk) -- 32/32 assertions PASS, rc=0. The sibling
test_syssvc_rms_filelock.c (vms-50e, parentless path) stayed 16/16 PASS,
rc=0 in the same boot -- the regression net holds. Full host debug build
(all targets) is clean; ctest -R 'rms|dcl' is 22/22 passed (1 unrelated
pre-existing honest skip, dcl_acceptance_e2e, no /dev/vms on host).
Deviations from the original design draft (superseded mid-session by the
corrected read-modifier model, and independently during implementation):
rab$w_rfa is not populated (nothing in the tree reads it; the byte-offset
locator is used directly instead); sys$put's record-lock is best-effort
(a fresh record cannot realistically conflict); sys$close has no RAB
back-pointer so its record-lock release seam is sys$connect/disconnect
only, matching the existing sys$close/RAB architecture.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* vms-0dd fix: $PUT holds no record lock (un-break test_syssvc_rms_acp regression)
CI caught a real regression my local harness missed: test_syssvc_rms_acp (write
8 records, close, reopen, read back) + test_syssvc_mmk_drive went red. Root cause:
rms_reclock_after_put stashed an EX record lock, but (1) rms_seq_put never sets
_last_rec_offset so every sequential $PUT collided on resource RMSR<fid>.0.0, and
(2) a record lock outlives the file lock (auto-release cascade deferred, vms-489)
while $CLOSE cannot reach the RAB to release it (vms-3ce) AND the test re-inits the
RAB (rab = cc$rms_rab) after reopen, zeroing _rec_lock_lkid without releasing. So
the $PUT lock on R.0.0 LEAKED, and the read-back's $GET of record 1 (offset 0) hit
it -> RMS$_RLK -> byte-exact read failed.
Fix: $PUT holds NO persistent record lock -- rms_reclock_after_put now only
RELEASES the stream's current-record lock and takes none. A $PUT lock would name a
stale/colliding resource anyway (no per-record put locator), and holding it is
what leaks. A record lock a later $UPDATE needs is taken by the preceding $GET (the
VMS pattern the done-condition exercises). $PUT-then-$UPDATE-same-stream without an
intervening $GET waits on per-record put locators + vms-489 (tracked in vms-3ce).
$GET/$UPDATE/$DELETE record locking + the reclock proof are unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* vms-0dd fix: implement the parent-child auto-release cascade (un-break dcl_acp get-lock leak)
The $PUT-lock fix cleared rms_acp + mmk_drive, but CI then caught a SEPARATE
get-lock leak: test_syssvc_dcl_acp (COPY a file, close, reopen, re-read byte-exact)
red. A $GET record lock is released on the next $GET (one per stream), but the LAST
one held survives $CLOSE ($CLOSE can't reach the RAB, vms-3ce), and the test's RAB
re-init on reopen (rab = cc$rms_rab) drops the lkid WITHOUT releasing -> the record
lock leaks and blocks the file's own re-read (RMS$_RLK). A leaked lock blocking
legitimate access is exactly the failure this rung exists to prevent.
Fix = the vms-489 cascade, now required (not deferrable): release_child_locks()
in vms_deq_core releases a lock's child sublocks (locks whose parent_id == the
deq'd lkid) on the same process. RMS record locks carry their file-access lock as
parent, so $CLOSE (which $DEQs the file lock) now releases every record lock still
held under it -- the record lock no longer outlives its file. Mirrors
vms_proc_release_locks' safe iteration over proc->locks; a leaf lock (no children)
makes it a cheap no-op, so the parentless H0->H11 + reclock + file-lock paths
(parid=0, no lock has parent_id==their lkid) are byte-unchanged. Shared kernel-core
-> both substrates; no header/ABI change. INV-6: real teardown, never a fake clear.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Aug 30, 2026
… executive (fix CREPRC in booted runtime) (#961) SPAWN failed with %DCL-F-CREPRC in the booted runtime: interactive DCL runs non-root (LOGINOUT drops to the user's UIC), so its $CREPRC child touched the executive before image activation and registered FRESH -> unprivileged, empty username -> then tried vms_kif_setident("SYSTEM", full-privs), which the executive correctly refused with SS$_NOPRIV (a non-root caller cannot self-declare a privileged name). The kernel spawn suites passed only because their creator was root and unnamed, so they never hit this branch (green-in-isolation, broken-in-runtime). Fix: the subprocess obtains identity by CONTINUATION from its unforgeable real_parent, not by re-stamping. New VMS_IOCTL_REGISTER_SUBPROCESS path: the executive reads the SPAWNing DCL's row and copies its CURRENT (possibly reduced) UIC/username/privilege masks onto the child, giving the subprocess its OWN PID (unlike image-activation _CONTINUE which shares the parent PID). Identity stays parent-derived, never caller-declared (INV-6 / executive boundary). The SS$_NOPRIV setident guard is UNCHANGED — a non-root caller still cannot self-award a privileged name; we fixed how the child gets its identity, not the guard. New symbol wired through all required places: VMS_IOCTL_REGISTER_SUBPROCESS (vms_ioctl.h), vms_kif_register_subprocess (vms_kif.c/.h + libvmssys_shr.vec), handler in vms_module.c, and BOTH NetBSD twins (vms_netbsd.c + vms_proctab_nb.h, #928 twin trap). Coverage: tests/qemu/test_syssvc_creprc_inherit.c reproduces the runtime condition the old suites missed — a NAMED, NON-ROOT creator whose $CREPRC child must inherit the creator's identity. Fails before this change, passes after. VMS User Acceptance battery goes 62/4 -> 66/0 under KVM (the 4 SPAWN assertions now pass). Executive-boundary/kernel-module-interface change -> Design Change Cascade: API compat = additive (new ioctl, no existing ABI changed); test coverage = new regression test above; doc impact = minimal. Claude-Session: https://claude.ai/code/session_01SwfchU4UcPzfdkLL2JSV4T Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl
added a commit
that referenced
this pull request
Sep 2, 2026
…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
baron-3dl
added a commit
that referenced
this pull request
Sep 2, 2026
…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
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.
DLM rung H10b (rd vms-dca9)
Proves the remaster invariant: a cross-node lock SURVIVES the departure of its master node. The surviving holder re-registers its lock on the NEW directory master, which rebuilds
res->grantedfrom the holder's real origin state — never a plausible default (INV-6) — and the harness value-verifies the rebuilt lock equals the one held pre-departure.Executive (
8ea2c987)SCS_DLM_OP_REBUILD/VMS_DLM_OP_REBUILD= 5, registered in every place the "op in N places" trap requires (scs_dlm.h + vms_ioctl.h enums, scs_dlm.c codec validator + op_name, scsd.c static_assert, vms_lock.c dispatch). Reuses the existing DLM frame (resnam + req_lkid + mode + req_csid) — an OVMX design choice (Rule 8; the DLM SYSAP wire is OVMX's own).vms_lock_dlm_xnode_rebuild()RECONSTRUCTS the holder's exact prior grant directly intores->granted(req_csid=holder, req_lkid=holder's handle, granted_mode=real mode) and makes this node the master. It does not re-run the enq/grant core (which would re-derive a grant against this node's empty queue). Refuses a zero/NL rebuild (SS$_BADPARAM).VMS_IOCTL_DLM_GET_GRANTED(0x37, additive) surfaces the first remote-held grant's{holder_csid, holder_req_lkid, granted_mode}so the harness can value-verify (GET_RESMASTER exposes neither mode nor handle). Census-reconciled up-front: thevms_kif_dlm_get_grantedwrapper is declared OVMX-UNWIRED (scsd issues it directly).Userspace drive + harness (
97a88907)peer_by_csid()— first DLM sender that must reach a peer other than the one whose join tick drove the call (the resource's new master, not the departed one).scsd_dlm_client_send_rebuild()— targetedSCS_DLM_OP_REBUILD, registered in the CHOKED SEND SITE TABLE (labeled new-in-vms-dca9).OVMX_DLM_H10B): holder A, on the master's class-0x04 departure, reads its origin's REAL mode via GETLKI, re-resolves N via local GET_RESMASTER, and sends the REBUILD to N; new master B dispatches it into the executive then reads back via GET_GRANTED — every marker carries real values,sent=0reported honestly if a read fails.dlm-harness-h10bCI job.Proof — real 3-node QEMU/KVM cluster, exit 0
Three-way value equality held: A_sent(lkid=1,mode=EX) == B_readback(lkid=1,mode=EX), B_readback holder_csid==1030(A). New master targeted was B (survivor), never C (departed).
scs_send_sitescensus green (113 checks);kif_caller_censusgreen.Observed gap (out of scope, flag for tracking)
The executive's cross-node ENQ receive path (
vms_lock_dlm_xnode_dispatch) masters unconditionally whatever ENQ it receives — no receive-side directory check. Every prior DLM rung was 2-node so this never surfaced; H10b sidesteps it with an opt-inOVMX_DLM_ENQ_CSIDtargeting env (additive, byte-identical when absent) so the harness ENQ reaches RES_C's real master. The underlying receive-side-mastership check is worth its own item.🤖 Generated with Claude Code