Repository navigation
vms-c55: the executive holds cross-node lock traffic for the duration of a cluster state transition - #1734
vms-c55: the executive holds cross-node lock traffic for the duration of a cluster state transition#1734baron-3dl wants to merge 4 commits into
Conversation
|
FIRE DISPATCHED 2026-10-16 ~01:38Z, scoring Four preflights, each three cases (
Node B was rebuilt from THIS head first (vms run CI on this PR: 115 success / 51 skipped / 0 failures, including How to score, in this order (P1 carries the argument, not P5):
A recurrence is not a dead end: |
FIRE SCORED — 24 case-runs across two arms. Committed as
|
| hold (18 case-runs) | control / main (6 case-runs) |
|
|---|---|---|
| OVMX cat-0x02 originated inside a transition (P1) | 0 | 0 |
| the same, outside one (P2) | 232+ | 28 |
| a new emitted (cat, op) pair (P4) | none | none |
| LOCKMGRERR (P5) | 0 | 0 |
refusing a lock message (cat 0x02 op 0x19) |
0 | 0 |
| CN=3 | 18 of 18 | 6 of 6 |
OVMXB reaches Username: (P3) |
no, 17 of 18 | no, 6 of 6 |
What it settles
- The hold is live. In
38846164733/ b-first the OVMX/VAX node's console carries, once each,%DLM, holding cross-node lock requests until the VAXcluster state transition completesand%DLM, the VAXcluster state transition completed; held cross-node lock requests resume. The mechanism is exercised by real traffic, not only by its tests, and the node went on serving afterwards. - No regression. OVMXB never reaching
Username:is present identically on main — 6 of 6 — so it is rd vms-97c2 (P0, open; vms-97c2 (PARTIAL): a cross-node lock request whose frame could not go out is retried, never dropped on a sleeping $ENQW — measured NOT to be the demo stall #1729 was partial), made visible for the first time by rd vms-4f4 putting a real Node B in the gate. Without the control arm this fire would have read as a regression caused by the hold. - No peer harmed in either arm.
What it does NOT settle, said plainly
That the LOCKMGRERR is fixed. The control did not reproduce it either (0 in 6, where the baseline is 1 in 9–12), so 0 of 18 is consistent with the fix working and with the crash simply not firing. And P1 is not a discrimination in these runs — the control also shows 0 in-transition originations, so the condition the hold guards against did not arise there. P1's categorical content is a property of the code, proven at R1 and R2 where the condition is forced.
So the change stands on what it was defended on and nothing more: real VMS nodes do not put lock traffic into a rebuild; OVMX did, in 3 of 3 crash captures and 0 of 11 clean ones; this removes it, harms nothing and emits no new shape. vms-c55 stays open, and vms-8cb5's question — what cat-0x02 op-0x19 IS — is still unanswered and still not guessed.
Also added since the first review: the R2 leg (the REAL vms_lock.c engine's $ENQ waits across a transition and is granted when it lifts), the console diagnostic and its fifth negative control. Full host negctl 135/135, cluster ctest 110/110, CI green including NetBSD/vax vms module cross-compiles.
Queuing for merge.
… of a cluster state transition
A real OpenVMS VAX V7.3 bugchecks LOCKMGRERR in the cluster demo about once in
nine fires. The previous lead -- "OVMX silently refuses what it does not
recognise" -- is FALSIFIED here, and a different signal, on the CAUSE side,
discriminates just as sharply.
THE REFUSALS, measured everywhere they exist (two committed crash captures plus
the twelve case-runs of openvmx-site 38811815171 / 38811829040 / 38811843112 /
38814006004, frames + all three consoles each):
- "%DLM, refusing a lock message" appears ONCE in the whole corpus: cat 0x02
op 0x19, in the one crashing case-run.
- "%CNXMAN, an unroutable VMS$VAXcluster frame was received" -- the other
place an inbound frame can be dropped -- appears ZERO times in all fourteen.
- Every other inbound frame is answered, or is one the spec says is never
answered (the 01/0a GO, the 01/0c release, the 01/06 burst).
So the refusal set is {02/19} and nothing OVMX refused can have provoked it.
WHAT DOES DISCRIMINATE: OVMX originates a cat-0x02 lock request WHILE A CLUSTER
STATE TRANSITION IS RUNNING. 3 of 3 crash runs (a directory removal at BARRIER
STEP 1, twice also a lookup at step 2); 0 of 11 clean runs, which carry 26
OVMX-originated requests, all of them outside a transition.
AND REAL VAXES DO NOT DO IT. Over all 122 committed pcaps, real DEC-OUI senders
only: inside a transition the DLM traffic is the op-0x0d rebuild record and its
echo (2797 + 1240) and essentially nothing else; every non-0x0d cat-0x02 frame
a real node put inside one flew at barrier step 10, 11 or 12 OF 12 -- ZERO at
steps 1-5. In the one capture holding a whole 12-step barrier the in-window
breakdown is exactly 307 x 02/0d, 307 x 82/0d and the barrier frames. That is
the published behaviour measured: a transition quiesces the lock manager --
Phase 1 discards all directory information cluster-wide, Phase 2 rebuilds it
(Davis, VAXcluster Principles, pp. 7-40/7-41, p. 6-33).
WHY OVMX DID IT: there was no stall. dlm_arm_transition_begin() only COUNTED
the boundary, and dq_may_transmit() -- the single gate every outbound lock
frame passes -- had no transition condition. The quorum hang deliberately does
not cover it (vms_dlm_quorum.h limit 1: "this node does not stall the POST of
such a request ... deferring the post until resume is a queue this rung does
not build"). This is that queue.
THE CHANGE, executive-resident and reading only state the executive holds:
- struct dlm_req_ops gains transition_active, a LIVE read of the CLUB's own
`transition_active` -- the flag the barrier and coordinator FSMs already
maintain and cnxman_dlm_seq_learn already gates on. An op, not a copy, for
the same INV-6 reason dir_generation is one.
- dq_may_transmit() holds an ENQ or a CONVERT while it reads true. The request
block is RETAINED exactly as rd vms-97c2 retains a frame the connection
manager could not take, and the hold does NOT spend a rung of the retransmit
ladder: a transition makes lock activity WAIT, it does not fail it.
- a DIRECTORY-ENTRY REMOVAL raised during one is DROPPED, counted. It has no
sleeper behind it and nothing to re-read, and Phase 1 discards the entry
cluster-wide anyway -- so holding it would mean asserting, after the
rebuild, about a directory entry this executive no longer has a fact about.
- a $DEQ still goes: giving a lock back is how the cluster is left in a state
the rebuild can use, the same carve-out vms_dlm_quorum.h makes for a release
during a quorum hang.
- dlm_arm_transition_end() releases the hold LAST, after the new directory
vector has been applied, through the ordinary ladder beat -- so a released
request is built from a fresh refill_post read under the NEW vector, never
from a remembered frame.
NO NEW WIRE SHAPE IS EMITTED. The change moves WHEN two existing shapes go out.
PROOF
- R1 tests/cluster/host/test_dlm_requester.c: 410 checks, 0 failures (41 new)
-- a held request waits out three ladders' worth of beats without being
failed, then goes out carrying the handle the LKB holds NOW; a removal in a
transition is dropped and NOT re-sent afterwards; a $DEQ is not held; an
unbound op holds nothing.
- test_dlm_scs_arm.c: the two bindings and the ORDER of the release.
- FOUR new negative controls, each reddening exactly its require_fail set:
dlm-request-not-held-in-transition, dlm-dir-remove-sent-in-transition,
dlm-transition-hold-spends-the-ladder, arm-transition-never-releases-the-hold.
Full host negctl suite: 134/134 PASS.
- tools/cluster/transition_dlm_census.py --selftest, wired into ctest
(cluster_host_transition_dlm_census), re-derives every figure above from the
committed corpus so none of them can drift.
- ctest 355/355; cluster 110/110; vms.ko builds clean.
EVIDENCE: tests/lab/captures/vms-c55-transition-hold-20261016 -- the THIRD
frame-level LOCKMGRERR specimen (openvmx-site 38811843112, page-order-
throttled4), the census, and PREDICTION.md pre-registering the fire.
NOT CLAIMED: that this is the cause. What cat-0x02 op-0x19 IS remains unknown
and is still not guessed -- no answer frame invented, RULE C not relaxed, the
removal not suppressed outside a transition. The demo-gate fire scored against
PREDICTION.md is what says whether the crash stops.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The R1 leg proves the FSM holds and releases against a fake engine. What one node cannot prove there is the thing this change could plausibly break: that holding a frame does not strand the $ENQW the shipping vms_lock.c put to sleep on its proxy LKB. scenarios/dlm_requester.c gains an arm with the REAL engine, a REAL CLUB and the shipping FSM: the barrier's own `club.transition_active` is set, a $ENQ for a cross-node resource puts NOTHING on the LAN, fifteen ladder beats go by without the request being failed, $GETLKI still shows it pending (not errored), and when the flag clears dlm_req_fsm_transition_ended() puts the frame out and the real engine grants the lock at EX. 89 checks, 0 failures. Cluster ctest 110/110. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…isode The twelve-case fire of the hold met P1 (0 of 12 case-runs put an OVMX cat-0x02 request inside a transition, against 3 of 3 crash runs), P2, P4 and P5 (0 LOCKMGRERR, CN=3 in 12 of 12, and no op-0x19 refusal on any console) -- but OVMXB, which is the build under test for the first time since rd vms-4f4, never offered Username: and its console stops between the STARTUP.COM hand-off and the site-startup line. NOTHING on that console says whether a lock request was WAITING for a rebuild or had been lost, so the stall cannot be attributed from the evidence the run produced. That is the #1714 lesson again. So the hold says so: one line at the first held transmission of an episode and one at its release, bounded by `xition_hold_said` so a storm of held requests inside one rebuild is still one line. A console now answers the question by itself. Tested: the R1 suite asserts exactly one line across sixteen held transmissions and exactly one more at the release (404 checks, 0 failures), and a fifth negative control -- dlm-transition-hold-is-silent -- reddens exactly those two assertions. Cluster ctest 110/110. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
24 case-runs across two arms, same instrument, one variable.
HOLD (ref=work/vms-c55-transition-hold, 18 case-runs, Node B from the ref):
0 OVMX cat-0x02 originated inside a transition, 0 LOCKMGRERR, 0 op-0x19
refusals, CN=3 in 18 of 18, no new emitted shape.
CONTROL (ref=main, this branch's exact base, 6 case-runs, Node B from main):
the same -- 0 and 0 and CN=3 in 6 of 6.
WHAT IT SETTLES.
(1) The hold is LIVE. In 38846164733/b-first the OVMX/VAX node's console
carries the two new lines once each -- it held cross-node lock requests
for a transition and released them when it completed -- so the mechanism
is exercised by real traffic, not only by its tests, and the node went on
serving.
(2) The change causes NO regression. OVMXB never reaching Username: is
present IDENTICALLY on main (6 of 6 against 17 of 18): it is rd vms-97c2,
still open, and rd vms-4f4 is why it became visible at all -- every
earlier case-filtered run booted the deployed V0.7-7 image. Without the
control this fire would have read as a regression caused by the hold.
(3) No peer was harmed in either arm.
WHAT IT DOES NOT SETTLE, said plainly: that the LOCKMGRERR is fixed. The
CONTROL did not reproduce it either (0 in 6 where the baseline is 1 in 9-12),
so 0 of 18 is consistent with the fix working AND with the crash not firing;
and P1 is not a discrimination in these runs, because the control also shows 0
in-transition originations. P1's categorical content is a property of the CODE,
proven at R1/R2 where the condition is forced. The change stands on what it was
defended on: real nodes do not put lock traffic into a rebuild, OVMX did in 3
of 3 crash captures and 0 of 11 clean ones, and this removes it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
41965b6 to
3ddb673
Compare
What this is
A real OpenVMS VAX V7.3 bugchecks LOCKMGRERR in the cluster demo at about 1 in 9 fires. The previous lead — "OVMX is a member the VAX admitted that silently refuses what it doesn't recognise" — is falsified here, and a different signal, on the cause side, discriminates just as sharply.
The refusals, measured everywhere they exist
Two committed crash captures plus the twelve case-runs of openvmx-site
38811815171/38811829040/38811843112/38814006004(frames + all three consoles each):%DLM, refusing a lock messageappears once in the whole corpus — cat0x02op0x19, in the one crashing case-run.%CNXMAN, an unroutable VMS$VAXcluster frame was received— the other place an inbound frame can be dropped — appears zero times in all fourteen runs.01/0aGO, the01/0crelease, the01/06burst — all consumed by the barrier/join FSMs).So the refusal set is
{02/19}, and nothing OVMX refused can have provoked it.What does discriminate
OVMX originates a cat-0x02 lock request while a cluster state transition is running.
02/04@step1×3,02/01@step2×2)And over all 122 committed pcaps, real DEC-OUI senders only: inside a transition the DLM traffic is the
op-0x0drebuild record and its echo (2797 + 1240) and essentially nothing else; every non-0x0dcat-0x02 frame a real node put inside one flew at barrier step 10, 11 or 12 of 12 — zero at steps 1–5. In the one capture holding a whole 12-step barrier the in-window breakdown is exactly 307 ×02/0d, 307 ×82/0dand the barrier frames.That is the published behaviour, measured: a transition quiesces the lock manager — Phase 1 discards all directory information cluster-wide, Phase 2 rebuilds it (Davis, VAXcluster Principles, pp. 7-40/7-41, p. 6-33).
Why OVMX did it
There was no stall.
dlm_arm_transition_begin()only counted the boundary, anddq_may_transmit()— the single gate every outbound lock frame passes — had no transition condition. The quorum hang deliberately does not cover it (vms_dlm_quorum.hlimit 1: "this node does not stall the POST of such a request … deferring the post until resume is a queue this rung does not build"). This is that queue.The change
Executive-resident, reading only state the executive already holds:
struct dlm_req_opsgainstransition_active— a live read of the CLUB's owntransition_active, the flag the barrier and coordinator FSMs already maintain andcnxman_dlm_seq_learnalready gates on. An injected op, not a copy, for the same INV-6 reasondir_generationis one.dq_may_transmit()holds an ENQ or a CONVERT while it reads true. The block is retained exactly as vms-97c2 retains a frame the connection manager could not take, and the hold does not spend a rung of the retransmit ladder: a transition makes lock activity wait, it does not fail it.vms_dlm_quorum.hmakes for a release during a quorum hang.dlm_arm_transition_end()releases the hold last, after the new directory vector has been applied, through the ordinary ladder beat — so a released request is built from a freshrefill_postread under the new vector.No new wire shape is emitted. The change moves when two existing shapes go out.
Proof
test_dlm_requester.ctest_dlm_scs_arm.crequire_failset; full host negctl 134/134 PASStools/cluster/transition_dlm_census.py --selftest, wired into ctest ascluster_host_transition_dlm_census— re-derives every figure above from the committed corpusvms.kobuilds cleanEvidence:
tests/lab/captures/vms-c55-transition-hold-20261016— the THIRD frame-level LOCKMGRERR specimen (openvmx-site38811843112, page-order-throttled4, the recurrence that carried a nonzero PID cell), the census, andPREDICTION.mdpre-registering the demo-gate fire.Not claimed
That this is the cause. What
cat-0x02 op-0x19IS remains unknown and is still not guessed — no answer frame invented, RULE C not relaxed, the removal not suppressed outside a transition (vms-e09's 8,106 real removals stand). The demo-gate fire scored againstPREDICTION.mdis what says whether the crash stops.🤖 Generated with Claude Code