mirror of
https://dev.lirent.ru/Vatrog/vm-automation-signaling.git
synced 2026-06-20 19:06:37 +03:00
709f4b586a
An epoll-driven, neutral transfer-event bus that connects sensors and input actuators to one or more controls, bidirectionally. It owns the transfer context and events — delivery order, priority, protocol-level timing, and an interrupt-driven event model over fd sources (eventfd/timerfd/sockets) — and stays agnostic to both the sensor/input drivers and the control. What lives here: - memctx: a coherent address-space context per endpoint — the guest address-space root paired with a pre-opened read-only RAM-region fd, with per-endpoint epoch invalidation and retained replay to late subscribers. Perception lives in out-of-tree sensor libraries that consume this datum read-only. - exclusive-ownership leases for destructive resource classes (input, power, memory-write). - write-signaled memory writes (MEMWRITE): an atomic write to guest memory routed through the seam under an exclusive lease, never a writable mapping. - a host-management seam for VM lifecycle/status and a neutral input-injection command path. - multi-VM endpoints; capability-gated, audited control authorization over an in-process or unix-socket transport. Builds against headers only by default (a stub mode that exercises the seam without a VM); armed builds link the real sensor/input libraries behind flags. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
171 lines
7.8 KiB
C
171 lines
7.8 KiB
C
#ifndef VMSIG_CORE_INTERNAL_H
|
|
#define VMSIG_CORE_INTERNAL_H
|
|
#include "vmsig_core.h"
|
|
#include <signal.h>
|
|
|
|
/* Private internals of the epoll core. Each registered fd carries a
|
|
* core_slot* in epoll_event.data.ptr; the slot's role decides how to handle it. */
|
|
|
|
#define VMSIG_MAX_EVENTS 64
|
|
#define VMSIG_MAX_ADAPTERS 256 /* up to ~64 VMs * 3 adapters + slack (mode A) */
|
|
#define VMSIG_MAX_CONTROLS 64 /* concurrent pollers; more => processes (C) */
|
|
#define VMSIG_ADAPTER_FDS 8 /* max fds per adapter */
|
|
#define VMSIG_DOWN_PENDING_MAX 256 /* ceiling of DOWN commands per poller in ctx (fairness) */
|
|
|
|
typedef enum {
|
|
SLOT_WAKEUP, /* wake/stop eventfd */
|
|
SLOT_ADAPTER, /* adapter fd (timerfd/eventfd/socket) */
|
|
SLOT_CTX_TIMING, /* context pacing timerfd */
|
|
SLOT_CONTROL, /* out-of-process control socket */
|
|
SLOT_SOURCE, /* arbitrary fd + callback (e.g. listen-fd) */
|
|
SLOT_DEAD /* detached (reaped); loop ignores it */
|
|
} slot_role;
|
|
|
|
typedef struct core_slot {
|
|
slot_role role;
|
|
int fd;
|
|
/* for SLOT_ADAPTER */
|
|
const vmsig_adapter_ops* ops;
|
|
vmsig_adapter* adapter;
|
|
uint32_t cookie;
|
|
/* for SLOT_CONTROL */
|
|
const vmsig_control_ops* cops;
|
|
void* ctl;
|
|
/* for SLOT_SOURCE */
|
|
void (*on_source)(void* user, uint32_t events);
|
|
void (*on_free)(void* user); /* invoked at core_free (source cleanup) */
|
|
void* source_user;
|
|
} core_slot;
|
|
|
|
typedef struct {
|
|
const vmsig_adapter_ops* ops;
|
|
vmsig_adapter* a;
|
|
uint32_t endpoint;
|
|
} core_adapter_ent;
|
|
|
|
|
|
/* ===== Retained address-space context (MEMCTX seam) =====
|
|
* The core retains per-endpoint "a current context exists in the current epoch" + the
|
|
* adapter's reg pointer (describe/share_fd/invalidate). Replays to a late qualified
|
|
* subscriber (CAP_MEMCTX + source_mask + endpoint) re-sharing the RO-fd. Does NOT store a
|
|
* copy of the locator: on delivery/replay it calls reg.describe (adapter snapshot) +
|
|
* reg.share_fd (fresh RO-fd). Invalidated on epoch change; cleared on unregister/free. */
|
|
typedef struct {
|
|
int registered; /* adapter called register_memctx (reg valid) */
|
|
int valid; /* a published context exists in the current epoch */
|
|
uint32_t epoch; /* snapshot epoch (== core epoch[ep] when valid) */
|
|
vmsig_memctx_reg reg; /* valid when registered */
|
|
} core_memctx_cell;
|
|
|
|
/* ===== Lease layer (arbitration of exclusive ownership of destructive resources) =====
|
|
* One cell per (endpoint, lease-class): who owns it (origin) + a snapshot of arb_prio at
|
|
* acquisition time. owner=0 => free. The snapshot (rather than the live grant) makes the
|
|
* policy resilient to the owner's grant changing after acquisition. */
|
|
#define VMSIG_LEASE_CLASSES 3 /* INPUT, POWER, MEMWRITE (== VMSIG_LEASE_CLASS_MAX) */
|
|
typedef struct {
|
|
uint32_t owner; /* origin (gen<<16)|(id+1) of the owner; 0 = free */
|
|
uint32_t owner_prio; /* owner's arb_prio at acquisition time (snapshot) */
|
|
} core_lease_cell;
|
|
|
|
struct vmsig_core; /* fwd for core_down_ctx */
|
|
|
|
/* DOWN emission context: handed to a control in set_emit_down so emit_down knows WHICH
|
|
* control issued the command (for grant lookup and enforcement). Stable: lives in the
|
|
* fixed controls[] array. */
|
|
typedef struct {
|
|
struct vmsig_core* core;
|
|
int ctl_id;
|
|
} core_down_ctx;
|
|
|
|
typedef struct {
|
|
const vmsig_control_ops* ops;
|
|
void* ctl;
|
|
vmsig_sub sub;
|
|
vmsig_grant grant; /* poller's rights ceiling (default-deny) */
|
|
core_down_ctx dctx; /* token for emit_down */
|
|
int active; /* 0 = detached/reaped (slot free) */
|
|
int reap; /* reap requested (deferred) */
|
|
core_slot* slot; /* SLOT_CONTROL fd slot (or NULL) */
|
|
uint32_t pending; /* DOWN commands of this poller in ctx (fairness cap) */
|
|
uint16_t gen; /* slot generation: +1 on each (re)use */
|
|
} core_control_ent;
|
|
|
|
struct vmsig_core {
|
|
int epfd;
|
|
int wake_fd; /* eventfd: nudge + stop */
|
|
vmsig_ctx* ctx;
|
|
volatile sig_atomic_t stopping;
|
|
|
|
core_adapter_ent adapters[VMSIG_MAX_ADAPTERS];
|
|
int nadapters;
|
|
core_control_ent controls[VMSIG_MAX_CONTROLS];
|
|
int ncontrols;
|
|
|
|
core_slot** slots; /* all allocated slots (for free) */
|
|
int nslots;
|
|
int cap_slots;
|
|
|
|
|
|
uint32_t epoch[64]; /* per-endpoint VM session epoch */
|
|
core_memctx_cell memctx[64]; /* per-endpoint retained context */
|
|
|
|
core_lease_cell lease[64][VMSIG_LEASE_CLASSES]; /* lease per (endpoint, class) */
|
|
vmsig_arb_policy arb_cb; /* preemption policy (NULL=default) */
|
|
void* arb_ud;
|
|
|
|
void (*audit_cb)(void* ud, const vmsig_audit* a);
|
|
void* audit_ud;
|
|
};
|
|
|
|
/* Emit an audit record (no-op if no callback is set). Defined in core.c. */
|
|
void core_audit(vmsig_core* c, const vmsig_audit* a);
|
|
|
|
/* Register an fd in epoll + create a slot (see core.c). */
|
|
core_slot* core_register_fd(vmsig_core* c, int fd, uint32_t epoll_events, slot_role role);
|
|
|
|
/* Register an arbitrary fd source with a callback (e.g. a socket listen-fd).
|
|
* The callback is called on the loop thread when the fd is ready. on_free (may be NULL)
|
|
* is called at vmsig_core_free to clean up the source's resource. 0/-1. */
|
|
int core_add_source(vmsig_core* c, int fd, void (*cb)(void* user, uint32_t events),
|
|
void* user, void (*on_free)(void* user));
|
|
|
|
/* Request detaching a control by id (deferred reap after the batch: epoll DEL,
|
|
* close fd, ops->close). Safe to call from the control's own on_readable. */
|
|
void core_request_drop(vmsig_core* c, int ctl_id);
|
|
|
|
/* emit hooks handed to adapters (UP) and controls (DOWN). Defined in loop.c. */
|
|
int core_emit_up (void* token, vmsig_event* ev);
|
|
int core_emit_down(void* token, vmsig_event* ev);
|
|
|
|
/* ===== Address-space context (MEMCTX seam; retained context) ===== */
|
|
/* Context registration hooks (handed to the adapter in vmsig_emit; defined in core.c). */
|
|
int core_register_memctx (void* token, const vmsig_memctx_reg* reg);
|
|
void core_unregister_memctx(void* token, uint32_t endpoint);
|
|
|
|
/* Multicast MEMCTX to qualified subscribers + mark the retain cell valid
|
|
* (from pump_up on the VMSIG_EV_MEMCTX trigger; defined in loop.c). */
|
|
void core_memctx_route(vmsig_core* c, const vmsig_event* trigger);
|
|
|
|
/* Replay retained MEMCTX to a single (late) subscriber (from vmsig_core_add_control;
|
|
* defined in loop.c). */
|
|
void core_memctx_replay(vmsig_core* c, int ctl_id);
|
|
|
|
/* Bump the endpoint's epoch on a destructive lifecycle transition: epoch++, invalidate
|
|
* the retain cell, emit MEMCTX_INVALIDATED, request re-bootstrap from the adapter.
|
|
* Observed by the core in pump_up on UP VM_LIFECYCLE (defined in loop.c). */
|
|
void core_epoch_bump(vmsig_core* c, uint32_t endpoint);
|
|
|
|
/* ===== Lease layer (defined in loop.c) ===== */
|
|
/* Intercept CMD_ACQUIRE/RELEASE/LEASE_STATUS (synchronously from core_emit_down, not in ctx). */
|
|
void core_lease_acquire(vmsig_core* c, int ctl_id, const vmsig_event* ev);
|
|
void core_lease_release(vmsig_core* c, int ctl_id, const vmsig_event* ev);
|
|
void core_lease_status (vmsig_core* c, int ctl_id, const vmsig_event* ev);
|
|
|
|
/* Reclaim the lease of a dead control (from core_reap, BEFORE e->active=0). */
|
|
void core_lease_reap_control(vmsig_core* c, int ctl_id);
|
|
|
|
/* Wake the loop (eventfd nudge). Defined in loop.c. */
|
|
void core_wake(vmsig_core* c);
|
|
|
|
#endif /* VMSIG_CORE_INTERNAL_H */
|