Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9604f15
scsd: drive the DLM rebuild-completion (op-04+op-03) to the coordinat…
baron-3dl Sep 1, 2026
ee4d1f3
vmsfs/lock: MOUNT holds the standing F11B$v volume lock for the mount…
baron-3dl Sep 2, 2026
15f40bc
test(acp_dlm): prove the standing F11B$v volume lock is held after $M…
baron-3dl Sep 2, 2026
cda03cb
fix(netbsd): mirror vms_lock_acp_vol_standing decl into the NetBSD he…
baron-3dl Sep 2, 2026
2d2f66b
dlm: VMS_IOCTL_DLM_ENUM_STANDING — enumerate the node's standing syst…
baron-3dl Sep 2, 2026
8bd4192
fix(kif): unbreak the libvmssys build for vms_kif_dlm_enum_standing (…
baron-3dl Sep 2, 2026
8d8a334
scs_member: op-01 ENQ that registers OVMX's REAL standing lock to the…
baron-3dl Sep 2, 2026
d307fff
scs_member/scsd: op-04/op-03 carry OVMX's real handle; disable OPT-A …
baron-3dl Sep 2, 2026
98562d2
scsd: the faithful standing-lock registration FSM (vms-74f) — Layer 3…
baron-3dl Sep 2, 2026
35ef456
fix(scsd): reword enum-stub comment to clear the Rule 9 runtime-targe…
baron-3dl Sep 2, 2026
5772a86
scsd/scs_member: op-01 carries the FULL per-lock DLM record (vms-74f)
baron-3dl Sep 2, 2026
4eeddea
scsd: fix DLM grant-detect — mask the response bit before category co…
baron-3dl Sep 2, 2026
43d33ae
scsd: XITDONE honesty — op-06 burst is admission INVITATION, not coun…
baron-3dl Sep 2, 2026
90b3bbb
executive+scsd: DLM directory learns the REAL cluster identity at run…
baron-3dl Sep 2, 2026
5360671
scsd: op-01 carries honest dir_csid=0 — revert the compute-the-direct…
baron-3dl Sep 2, 2026
7b2e3cd
scsd: op-01 req_lkid carries OVMX's wire-learned cluster node-index (…
baron-3dl Sep 2, 2026
ff30929
scsd: remove the op-06 op-01 origination — it makes the coordinator w…
baron-3dl Sep 2, 2026
2f2025a
scsd: reactive per-op-0d NL registration — the respond-to-rebuild tha…
baron-3dl Sep 2, 2026
a554e7c
scsd: complete the granted reactive registrations (op-04+op-03) — hon…
baron-3dl Sep 2, 2026
6f1748f
Revert "scsd: complete the granted reactive registrations (op-04+op-0…
baron-3dl Sep 2, 2026
3e26962
scsd: faithful DLM foundation — master rebuild-assigned resources + g…
baron-3dl Sep 2, 2026
8a84f43
scsd: answer inbound op-01 with a REAL grant frame from executive loc…
baron-3dl Sep 2, 2026
e7214ff
scsd: ground the inbound op-01 grant + make the faithful DLM the real…
baron-3dl Sep 2, 2026
24fbc64
scsd: grant at the REQUESTED mode — 16-bit master handle stops clobbe…
baron-3dl Sep 2, 2026
73c2a40
scsd/dlm: idempotent cross-node ENQ — a retransmit returns the SAME m…
baron-3dl Sep 2, 2026
fc8540a
scsd: fire flow-1 DLM completion on the peer we registered to — unblo…
baron-3dl Sep 2, 2026
340f94e
scsd/dlm: flow-1 completion from REAL executive state — register with…
baron-3dl Sep 2, 2026
8d0cc65
scsd/dlm: instrument the flow-1 grant-back->completion chain + drop t…
baron-3dl Sep 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 108 additions & 6 deletions src/kernel-core/vms_lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ exec_list_head_t vms_dlm_origin_list;
exec_lock_t vms_dlm_origin_lock;

static int vms_lock_dlm_origin_getlki(uint32_t lkid, uint32_t *granted_mode,
uint32_t *requested_mode, char *resnam,
size_t resnam_len, uint8_t *valblk);
uint32_t *requested_mode, uint32_t *master_lkid,
char *resnam, size_t resnam_len, uint8_t *valblk);

/* ================================================================
* Cluster membership crosses into the executive (rd vms-551,
Expand Down Expand Up @@ -1447,6 +1447,41 @@ uint32_t vms_lock_acp_vol_release(struct vms_proc *proc, uint32_t lkid)
return d.status;
}

/*
* vms_lock_acp_vol_standing - take the STANDING per-volume lock a faithful MOUNT
* holds for the WHOLE mount life (vms-25e). Unlike vms_lock_acp_vol_ex (the
* transient EX synchronization lock a single on-disk-structure write takes and
* immediately drops), this is a NULL-mode (NL) lock held from $MOUNT to
* $DISMOUNT. Its purpose is cluster registration, not serialization: it names the
* volume in the DLM so the connection manager can register "this node has this
* volume mounted" to the coordinator during a directory rebuild -- the standing
* F11B$v<label> lock a real VMS node holds and re-registers on join.
*
* NL asserts PRESENCE, not exclusion: it takes a different resource (F11B$v) than
* the XQP's per-write sync lock (F11B$s), holds nothing exclusive, and so can
* never block the XQP, another writer, or another cluster node -- deadlock-free
* and cluster-safe by construction. Released by vms_lock_acp_vol_release at
* $DISMOUNT (or on the holder's rundown). Returns the executive status; on
* SS$_NORMAL *lkid_out is the standing lock's handle for later registration.
*/
uint32_t vms_lock_acp_vol_standing(struct vms_proc *proc, const char *resnam,
uint32_t *lkid_out)
{
struct vms_enq_args a;

if (lkid_out)
*lkid_out = 0;
memset(&a, 0, sizeof(a));
a.lkmode = LCK_K_NLMODE; /* NL: a presence marker, holds nothing exclusive */
a.flags = LCK_M_SYNC; /* $ENQW; an NL request grants immediately */
strscpy(a.resnam, resnam, sizeof(a.resnam));

vms_enq_core_ex(proc, &a, NULL);
if (a.status == SS__NORMAL && lkid_out)
*lkid_out = a.lkid;
return a.status;
}

/*
* vms_ioctl_convert - Convert lock mode ($ENQ with LCK$M_CONVERT)
*
Expand Down Expand Up @@ -1588,12 +1623,13 @@ long vms_ioctl_getlki(struct vms_proc *proc, unsigned long arg)
* its origin record carries the status the master's GRANT completed. This
* makes the NL->EX flip driven by the remote master observable here.
*/
uint32_t org_granted = 0, org_requested = 0;
uint32_t org_granted = 0, org_requested = 0, org_master = 0;
if (vms_lock_dlm_origin_getlki(args.lkid, &org_granted, &org_requested,
args.resnam, sizeof(args.resnam),
&org_master, args.resnam, sizeof(args.resnam),
args.valblk)) {
args.granted_mode = org_granted;
args.requested_mode = org_requested;
args.master_lkid = org_master; /* vms-16c: the master's granted handle, from real origin state */
args.parent_id = 0;
/* args.valblk was filled by origin_getlki: the master's LVB the
* GRANT delivered (rd vms-eeb), or zeros if none -- the read
Expand Down Expand Up @@ -2388,8 +2424,8 @@ static uint32_t vms_lock_dlm_xnode_blkast_recv(struct vms_proc *proc,
* cross-node request's status flip on the REQUESTER node.
*/
static int vms_lock_dlm_origin_getlki(uint32_t lkid, uint32_t *granted_mode,
uint32_t *requested_mode, char *resnam,
size_t resnam_len, uint8_t *valblk)
uint32_t *requested_mode, uint32_t *master_lkid,
char *resnam, size_t resnam_len, uint8_t *valblk)
{
struct vms_dlm_origin *cur;
int found = 0;
Expand All @@ -2403,6 +2439,8 @@ static int vms_lock_dlm_origin_getlki(uint32_t lkid, uint32_t *granted_mode,
*granted_mode = cur->granted_mode;
if (requested_mode)
*requested_mode = cur->requested_mode;
if (master_lkid)
*master_lkid = cur->master_lkid; /* the master's granted handle (0 until a real GRANT recorded it) */
if (resnam && resnam_len)
strscpy(resnam, cur->resnam, resnam_len);
/*
Expand Down Expand Up @@ -2480,6 +2518,57 @@ static uint32_t vms_lock_dlm_xnode_rebuild(struct vms_proc *proc,
return SS__NORMAL;
}

/*
* vms_lock_dlm_xnode_enq_idempotent - retransmit idempotency for a cross-node
* $ENQ (vms-16c). A cluster member (VAX1/VAX2) that never sees a STABLE granted
* lock re-sends the same op-01 ENQ. Before this, every re-send ran the full
* enqueue core and minted a FRESH master lock record, so the handle OVMX returned
* changed on each reply (0x328 -> 0x329 -> 0x32a ...) and the requester could
* never correlate a held lock -- it re-requested forever (the measured ~35/sec
* storm on LNM$CWLOGICALS + F11B$aSYSDSK1, CN stuck at 2). A real VMS master is
* idempotent to a retransmit: the SAME (req_csid, req_lkid) on the same resource,
* already granted at a mode that covers the request, returns the SAME master
* handle every time.
*
* Returns 1 and fills req->master_lkid / master_csid / valblk (the caller returns
* SS$_NORMAL) when an existing grant satisfies the retransmit; 0 to let the caller
* run the full enqueue core -- a genuinely new lock, or an UP-CONVERSION (existing
* grant weaker than the request) the core must process for real. Cross-node only
* (both ids non-zero); a local $ENQ (both 0) is never deduped here. INV-6: the
* handle returned names a REAL existing lock record on the resource's granted
* queue -- never a fabricated or defaulted grant.
*/
static int vms_lock_dlm_xnode_enq_idempotent(struct vms_dlm_xnode_args *req)
{
struct vms_lock_resource *res;
struct vms_lock_entry *lock;
int handled = 0;

if (req->req_csid == 0 || req->req_lkid == 0)
return 0; /* a local ENQ or unidentified requester: never dedup */

res = resource_find_or_create(req->resnam);
if (!res)
return 0; /* let the core report SS$_INSFMEM honestly */

exec_lock(&res->lock);
exec_list_for_each_entry(lock, &res->granted, res_granted) {
if (lock->req_csid == req->req_csid &&
lock->req_lkid == req->req_lkid &&
lock->granted_mode >= req->lkmode) {
req->master_lkid = lock->lkid; /* the SAME stable handle */
req->master_csid = vms_local_csid;
memcpy(req->valblk, res->valblk, LCK_VALBLK_SIZE);
handled = 1;
break;
}
}
exec_unlock(&res->lock);

resource_release(res);
return handled;
}

/*
* vms_lock_dlm_xnode_dispatch - the cross-node DLM RECEIVE handler
* (vms-94c transport; DLM epic vms-7fa).
Expand Down Expand Up @@ -2553,6 +2642,19 @@ uint32_t vms_lock_dlm_xnode_dispatch(struct vms_proc *proc,
if (req->resnam[0] == '\0')
return SS__BADPARAM;

/*
* Retransmit idempotency (vms-16c). An already-granted cross-node lock
* (same req_csid + req_lkid, mode still covered) returns its EXISTING
* master handle -- a STABLE handle the requester correlates -- instead of
* minting a fresh record per re-send. Without this the returned handle
* changed on every retransmit and the member re-requested forever. A
* genuinely new lock or an up-conversion returns 0 here and runs the core.
*/
if (vms_lock_dlm_xnode_enq_idempotent(req)) {
req->queued = 0;
return SS__NORMAL;
}

/*
* Marshal the decoded cross-node $ENQ into the single-node lock manager
* on this (the mastering) node, in CROSS-NODE mode (xn): the lock is held
Expand Down
117 changes: 105 additions & 12 deletions src/kernel-core/vmsfs_acp.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ struct vms_acp_volume {
*/
uint32_t ibmap_lbn; /* hm2_ibmaplbn: index-bitmap base LBN */
uint32_t maxfiles; /* hm2_maxfiles: index-file capacity */
/*
* The STANDING volume lock a faithful MOUNT holds for the whole mount life
* (vms-25e): a real VMS MOUNT holds the per-volume "F11B$v<label>" lock from
* $MOUNT to $DISMOUNT -- the cluster-wide marker that this node has the volume
* mounted, which the connection manager registers during a directory rebuild.
* Taken at publish, released at $DISMOUNT. 0 = not held (acquire failed or the
* node has no DLM); the volume is still mounted -- the standing lock is a
* cluster-registration facility, never a gate on the mount itself.
*/
uint32_t vol_lkid; /* standing F11B$v<label> lock handle, 0 if unheld */
};

/*
Expand Down Expand Up @@ -267,20 +277,27 @@ static struct vms_acp_volume *acp_vol_find_locked(const char *devnam)
}

/*
* acp_vol_resnam - the per-volume synchronization RESOURCE NAME for the ACP's
* DLM write lock (vms-233, design §4.7). Derived from the ODS-2 volume LABEL so
* a clustered / MSCP-served volume computes the SAME resource name on every node
* that serves it -- the DLM resource is the cluster-wide write sync point. The
* namespace prefix follows VMS's public files-lock naming ("F11B$s..."); the
* exact resource string is an OVMX design choice, not a claim of VMS byte-wire
* fidelity (Rule 8). An unlabelled volume falls back to its backing device
* numbers so it still serializes locally. Host-neutral (fixed-width fields, no
* long/pointer/sizeof) -- kernel-core portable for the Alpha/VAX cross-check.
* acp_vol_resnam_kind - the per-volume Files-11 DLM RESOURCE NAME "F11B$<kind><label>"
* for one of the per-volume locks (vms-233, design §4.7). `kind` selects which:
* 's' -- the XQP volume-SYNCHRONIZATION lock, taken transiently per on-disk-
* structure write (vms_lock_acp_vol_ex); the original single use.
* 'v' -- the standing VOLUME lock a faithful MOUNT holds for the whole mount
* life (vms-25e), the cluster-wide "this volume is mounted here" marker
* the connection manager registers during a directory rebuild.
* 'a' -- the volume-ALLOCATION lock.
* Derived from the ODS-2 volume LABEL so a clustered / MSCP-served volume computes
* the SAME resource name on every node that serves it -- the DLM resource is the
* cluster-wide sync point. The namespace follows VMS's public files-lock naming
* ("F11B$..."); the exact string is an OVMX design choice, not a claim of VMS
* byte-wire fidelity (Rule 8). An unlabelled volume falls back to its backing
* device numbers so it still serializes locally. Host-neutral (fixed-width fields,
* no long/pointer/sizeof) -- kernel-core portable for the Alpha/VAX cross-check.
*/
static void acp_vol_resnam(const struct vms_acp_volume *vol, char *out, size_t outsz)
static void acp_vol_resnam_kind(const struct vms_acp_volume *vol, char kind,
char *out, size_t outsz)
{
size_t n = 0;
static const char pfx[] = "F11B$s";
const char pfx[7] = { 'F', '1', '1', 'B', '$', kind, '\0' };
size_t i;

for (i = 0; pfx[i] != '\0' && n + 1 < outsz; i++)
Expand Down Expand Up @@ -310,6 +327,14 @@ static void acp_vol_resnam(const struct vms_acp_volume *vol, char *out, size_t o
out[n < outsz ? n : outsz - 1] = '\0';
}

/* acp_vol_resnam - the XQP volume-SYNC resource name ("F11B$s<label>"), the
* original per-write serialization lock. Thin wrapper over acp_vol_resnam_kind
* so the transient-lock call sites read unchanged. */
static void acp_vol_resnam(const struct vms_acp_volume *vol, char *out, size_t outsz)
{
acp_vol_resnam_kind(vol, 's', out, outsz);
}

/* Caller holds proc->chan_lock. */
static struct vms_acp_chan *acp_chan_find_locked(struct vms_proc *proc, uint32_t chan)
{
Expand Down Expand Up @@ -682,6 +707,23 @@ long vms_ioctl_acp_mount(struct vms_proc *proc, unsigned long arg)
exec_list_add_tail(&vol->list, &vms_acp_vol_list);
exec_unlock(&vms_acp_vol_lock);

/*
* vms-25e: take the STANDING volume lock a faithful MOUNT holds for the whole
* mount life -- the cluster-registration presence marker the connection
* manager registers to the coordinator on join. Enqueued AFTER dropping
* vms_acp_vol_lock (the enqueue may take DLM locks; never hold the table lock
* across it). Best-effort: a failure (no DLM backend) leaves vol_lkid 0 and
* the volume is mounted regardless -- the standing lock is a cluster facility,
* NEVER a gate on the mount itself (INV-6: honest omission on failure). Owned
* by the mounting process; for the PID-1-mounted system disk that is the
* node's life.
*/
{
char vresnam[32];
acp_vol_resnam_kind(vol, 'v', vresnam, sizeof(vresnam));
vms_lock_acp_vol_standing(proc, vresnam, &vol->vol_lkid);
}

args.status = SS__NORMAL;

out:
Expand All @@ -705,7 +747,6 @@ long vms_ioctl_acp_dmount(struct vms_proc *proc, unsigned long arg)
char devnam[VMS_DEVNAM_SIZE];
uint32_t status;

(void)proc;
memset(&args, 0, sizeof(args));
if (exec_copyin(&args, (const void *)arg, sizeof(args)))
return -EFAULT;
Expand All @@ -731,6 +772,14 @@ long vms_ioctl_acp_dmount(struct vms_proc *proc, unsigned long arg)
}
exec_list_del(&vol->list);
exec_unlock(&vms_acp_vol_lock);
/*
* vms-25e: drop the standing volume lock this mount held (no-op if unheld).
* Best-effort, outside the table lock (the DEQ may touch DLM state). The lock
* was taken by the MOUNTING process; if a different process dismounts, the DEQ
* may not match and the holder's rundown ($DEQ-on-exit) is the backstop -- for
* the PID-1-mounted system disk, dismount and rundown are both PID 1.
*/
vms_lock_acp_vol_release(proc, vol->vol_lkid);
exec_free(vol);

args.status = SS__NORMAL;
Expand All @@ -741,6 +790,50 @@ long vms_ioctl_acp_dmount(struct vms_proc *proc, unsigned long arg)
return 0;
}

/*
* vms_ioctl_dlm_enum_standing - enumerate this node's STANDING cluster-registrable
* system locks (vms-1f4). These are the locks the executive holds for the node's
* LIFE that the connection manager (scsd) registers to the coordinator during a
* directory rebuild: today the per-volume F11B$v<label> lock a MOUNT holds
* (vms-25e), one entry per mounted volume that genuinely holds it (vol_lkid != 0).
* Each entry carries the resource name and this node's LOCAL lock handle -- the
* op-0x01 requester lkid scsd puts on the wire.
*
* INV-6: a READ of REAL lock state. count is exactly 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. The enqueue lock
* is dropped before copyout (copyout may fault); the table lock is held only
* across the in-memory walk.
*/
long vms_ioctl_dlm_enum_standing(struct vms_proc *proc, unsigned long arg)
{
struct vms_dlm_enum_standing_args args;
struct vms_acp_volume *vol;

(void)proc;
memset(&args, 0, sizeof(args));

exec_lock(&vms_acp_vol_lock);
exec_list_for_each_entry(vol, &vms_acp_vol_list, list) {
if (vol->vol_lkid == 0)
continue; /* no standing lock held (best-effort acquire failed) */
args.total++;
if (args.count < VMS_DLM_ENUM_STANDING_MAX) {
struct vms_dlm_standing_ent *e = &args.ent[args.count];
acp_vol_resnam_kind(vol, 'v', e->resnam, sizeof(e->resnam));
e->lkid = vol->vol_lkid;
e->mode = LCK_K_NLMODE; /* the volume presence lock is held at NL */
args.count++;
}
}
exec_unlock(&vms_acp_vol_lock);
args.status = SS__NORMAL;

if (exec_copyout((void *)arg, &args, sizeof(args)))
return -EFAULT;
return 0;
}

/*
* $ASSIGN a FILE-CLASS channel to a mounted ODS-2 volume (design §4.2). The
* channel is the executive's -- bound to the mounted volume, drawn from the
Expand Down
12 changes: 12 additions & 0 deletions src/kernel-netbsd/vms_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,13 @@ long vms_ioctl_getlki(struct vms_proc *proc, unsigned long arg);
uint32_t vms_lock_acp_vol_ex(struct vms_proc *proc, const char *resnam,
uint32_t *lkid_out);
uint32_t vms_lock_acp_vol_release(struct vms_proc *proc, uint32_t lkid);
/* The STANDING per-volume lock a faithful MOUNT holds for the whole mount life
* (vms-25e): an NL-mode $ENQ on the F11B$v<label> resource, held from $MOUNT to
* $DISMOUNT as the cluster-registration presence marker. Released with
* vms_lock_acp_vol_release. Mirror of the src/kernel/vms_internal.h decl (#928:
* kernel-core lock ops must be declared in BOTH the Linux and NetBSD headers). */
uint32_t vms_lock_acp_vol_standing(struct vms_proc *proc, const char *resnam,
uint32_t *lkid_out);
long vms_ioctl_get_resmaster(struct vms_proc *proc, unsigned long arg);
long vms_ioctl_dlm_member_depart(struct vms_proc *proc, unsigned long arg);
long vms_ioctl_dlm_get_granted(struct vms_proc *proc, unsigned long arg);
Expand Down Expand Up @@ -866,6 +873,11 @@ void vms_acp_init(void);
void vms_acp_cleanup(void);
long vms_ioctl_acp_mount(struct vms_proc *proc, unsigned long arg);
long vms_ioctl_acp_dmount(struct vms_proc *proc, unsigned long arg);
/* Enumerate this node's standing cluster-registrable system locks (vms-1f4): one
* entry (resname + local lkid + mode) per mounted volume holding its F11B$v lock.
* A READ of real lock state for scsd's directory-rebuild registration. Mirror of
* the src/kernel/vms_internal.h decl (#928: kernel-core ops in BOTH headers). */
long vms_ioctl_dlm_enum_standing(struct vms_proc *proc, unsigned long arg);
long vms_ioctl_acp_assign(struct vms_proc *proc, unsigned long arg);
long vms_ioctl_acp_access(struct vms_proc *proc, unsigned long arg);
long vms_ioctl_acp_deaccess(struct vms_proc *proc, unsigned long arg);
Expand Down
Loading
Loading