mirror of
https://dev.lirent.ru/Vatrog/vm-automation-signaling.git
synced 2026-06-20 19:06:37 +03:00
127 lines
7.2 KiB
C
127 lines
7.2 KiB
C
|
|
#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 */
|