mirror of
https://dev.lirent.ru/Vatrog/vm-automation-signaling.git
synced 2026-06-20 19:06:37 +03:00
vmsig: a neutral signaling layer between sensors/input and controls
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>
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
#ifndef VMSIG_CONTROL_H
|
||||
#define VMSIG_CONTROL_H
|
||||
#include "vmsig_event.h"
|
||||
|
||||
/* vmsig_control.h — control-agnostic seam. Control (an algorithm OR a human)
|
||||
* attaches via ONE neutral interface: a command queue (down) + an event
|
||||
* subscription (up). In-process implements the vtable with direct callbacks
|
||||
* (fd = -1); out-of-process is a socket whose fd is registered with the core like
|
||||
* any source. The core treats both the same. Orchestration is NOT wired in here —
|
||||
* only the seam. */
|
||||
|
||||
/* Subscription filter: which UP events the control WANTS. This is only a
|
||||
* NARROWING; the real ceiling is set by the grant (effective = sub ∩ grant). */
|
||||
typedef struct {
|
||||
uint32_t source_mask; /* bit (1u<<vmsig_source) for the seam of interest */
|
||||
vmsig_prio prio_min; /* drop UP below this priority */
|
||||
uint64_t endpoint_mask; /* 0 = all VMs; otherwise bit (1ull<<endpoint) */
|
||||
} vmsig_sub;
|
||||
|
||||
/* ===== Security layer: a control's grant (capability set) =====
|
||||
* Neutral ceiling of a poller's rights: which VMs, which UP sources, which classes
|
||||
* of DOWN commands. The enforcement mechanism is in the core (admission/pump_up/
|
||||
* emit_down); the policy (who gets what) is set by the embedding program/
|
||||
* orchestrator. Default DENY: an empty grant => not a valid poller (receives and
|
||||
* sends nothing). */
|
||||
#define VMSIG_CAP_OBSERVE 0x1u /* UP of SEAM/generic coherent state (observation) */
|
||||
#define VMSIG_CAP_INPUT 0x2u /* CMD_INPUT */
|
||||
/* (0x4 is the freed bit of the removed CAP_STREAM; the future vgpu-control down-path
|
||||
* returns via write-signaled/MEMWRITE. Do NOT reuse.) */
|
||||
#define VMSIG_CAP_LIFECYCLE 0x8u /* CMD_LIFECYCLE safe ones (pause/resume/wakeup) */
|
||||
/* (0x10 is the freed bit of the removed CAP_MEMREAD; do NOT reuse: a stale grant
|
||||
* with this bit must not silently alias to the privileged memory cap.) */
|
||||
#define VMSIG_CAP_POWER 0x20u /* destructive lifecycle/VM (powerdown/reset/quit) */
|
||||
#define VMSIG_CAP_VM 0x40u /* CMD_VM safe ones (query/cont/stop), VMHOST seam */
|
||||
#define VMSIG_CAP_MEMCTX 0x80u /* SUBSCRIPTION to a coherent AS context (UP MEMCTX*, re-share RO-fd).
|
||||
* NOT an access broker (that is OS-DAC on the fd) — gates RECEIVING the datum. */
|
||||
#define VMSIG_CAP_MEMWRITE 0x100u /* CMD_MEMWRITE: atomic write-signaled mutation of shared guest memory
|
||||
* (separate from the freed CAP_MEMREAD bit — read != write; fresh bit
|
||||
* avoids stale-grant aliasing to this privileged cap). */
|
||||
|
||||
typedef struct {
|
||||
uint32_t principal; /* id for auditing (uid/token) */
|
||||
uint64_t endpoint_mask; /* which VMs (bit 1ull<<endpoint, endpoint<64); 0=none */
|
||||
uint32_t source_mask; /* which UP sources (bit 1u<<vmsig_source) */
|
||||
uint32_t cap_mask; /* VMSIG_CAP_* */
|
||||
uint32_t arb_prio; /* lease arbitration priority: higher=stronger; supervisor=max. */
|
||||
/* Separate from vmsig_prio (on-wire ordering). The default */
|
||||
/* arbitration policy compares it (STRICTLY higher preempts, ties=owner). */
|
||||
} vmsig_grant;
|
||||
|
||||
/* ===== Lease arbitration policy (orchestrator; §5) =====
|
||||
* Signaling owns the MECHANISM (exclusivity, preemption, fencing, finalization);
|
||||
* the POLICY (preempt or deny on conflict) is set by the orchestrator via a
|
||||
* pluggable callback. Default (cb==NULL) = arb_prio comparison: STRICTLY higher ->
|
||||
* PREEMPT, otherwise DENY. */
|
||||
typedef enum {
|
||||
VMSIG_ARB_DENY = 0, /* deny the contender, the owner keeps it */
|
||||
VMSIG_ARB_PREEMPT = 1 /* take it from the owner, give it to the contender (QUEUE — reserved) */
|
||||
} vmsig_arb_decision;
|
||||
|
||||
/* Called ONLY when (endpoint,class) is held by a LIVE owner (incumbent) and an
|
||||
* ACQUIRE arrives from another contender. incumbent/contender are the parties'
|
||||
* grants (live, not copies); incumbent is NEVER NULL (a dead owner is treated as a
|
||||
* free slot and policy is not called). Called on the loop thread. */
|
||||
typedef vmsig_arb_decision (*vmsig_arb_policy)(void* ud, uint32_t endpoint, uint32_t cls,
|
||||
const vmsig_grant* incumbent,
|
||||
const vmsig_grant* contender);
|
||||
|
||||
/* Control endpoint vtable. The core calls deliver() for UP; control sends DOWN via
|
||||
* the emit hook that the core installs in set_emit_down(). */
|
||||
typedef struct vmsig_control_ops {
|
||||
const char* name;
|
||||
|
||||
/* fd for an out-of-process control (socket). -1 => in-process, callbacks only
|
||||
* (no registration in epoll). */
|
||||
int (*fd)(void* ctl);
|
||||
|
||||
/* Declare interest (called once at attach). */
|
||||
int (*subscribe)(void* ctl, vmsig_sub* out);
|
||||
|
||||
/* Core -> control: an UP event for the subscriber. For in-process, a direct
|
||||
* call; for socket-control, serialization onto the wire. Borrowed: whatever
|
||||
* must outlive the call must be copied. */
|
||||
int (*deliver)(void* ctl, const vmsig_event* ev);
|
||||
|
||||
/* Core -> control (socket only): the control-fd is readable; the implementation
|
||||
* parses the wire into DOWN events and calls the installed down-emit. */
|
||||
int (*on_readable)(void* ctl);
|
||||
|
||||
/* The core installs the hook by which control sends DOWN commands; the core
|
||||
* routes them into vmsig_ctx_submit(ctx, VMSIG_DIR_DOWN, ev). */
|
||||
void (*set_emit_down)(void* ctl, int (*emit)(void* token, vmsig_event*),
|
||||
void* token);
|
||||
|
||||
void (*close)(void* ctl);
|
||||
|
||||
/* Core -> control: deliver a coherent address-space context (UP MEMCTX) + RO-fd
|
||||
* of the RAM region. Socket: a vmsig_wire frame (kind=MEMCTX, inln=vmsig_memctx) + fd in cmsg
|
||||
* (SCM_RIGHTS); the segs payload does NOT go on the wire (the holder opens
|
||||
* via `low`). In-proc: direct fd + event (segs in payload, decode with vmsig_memctx_segs).
|
||||
* The fd is BORROWED for the duration of the call (the core closes it afterwards) — the holder
|
||||
* dup's/mmap's it to keep it. Optional: NULL => control does not accept MEMCTX. 0/-1. */
|
||||
int (*attach_memctx)(void* ctl, const vmsig_event* ev, int fd);
|
||||
} vmsig_control_ops;
|
||||
|
||||
/* Reference in-process control: a thin shim turning a C callback into a vtable, for
|
||||
* embedding an algorithm directly. */
|
||||
typedef struct {
|
||||
int (*on_event)(void* user, const vmsig_event* up); /* core -> algorithm */
|
||||
void* user;
|
||||
vmsig_sub sub; /* subscription filter */
|
||||
/* Core -> algorithm: a coherent AS context (UP MEMCTX) + RO-fd as a direct int. The fd
|
||||
* is borrowed (dup/mmap to keep it). NULL => does not accept. 0/-1. */
|
||||
int (*on_memctx)(void* user, const vmsig_event* ev, int fd);
|
||||
} vmsig_inproc_cfg;
|
||||
|
||||
/* Create a reference in-proc control over cfg (which is copied). Returns an opaque
|
||||
* ctl for vmsig_core_add_control(core, vmsig_inproc_control_ops(), ctl). Freed via
|
||||
* ops->close(ctl). NULL on OOM. */
|
||||
const vmsig_control_ops* vmsig_inproc_control_ops(void);
|
||||
void* vmsig_inproc_control_new(const vmsig_inproc_cfg* cfg);
|
||||
|
||||
/* Send a DOWN command from an in-proc control (after attach). 0 — ok, -1 — error. */
|
||||
int vmsig_inproc_send(void* ctl, vmsig_event* down);
|
||||
|
||||
#endif /* VMSIG_CONTROL_H */
|
||||
Reference in New Issue
Block a user