nvx-rs: signal an execution's program without ending it, for graceful stops

Chiusa
#858 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
54/100
Tipo di issue
Funzionalità
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
linux, rust

Direzione di ricerca

Start with nvx-rs/src/guest/exec.rs, especially Execution::signal_program, end, and the process group fallback at line 1028; then read nvx-rs/src/guest/sys/process.rs for pidfd signaling and nvx-rs/src/guest/exec/binding.rs for the passthrough. Find the existing execution and binding tests for signal, process group, caller reaping, and adopted programs. Done means the acceptance cases pass and the module documentation explains signal, wait, then terminate or cancel.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Related: #620 (the host supervisor's graceful stop), #623 (the shared execution engine), #831 (adopted programs). Needs: nothing.

Goal. A guest caller can send an execution's program a signal other than SIGKILL through nvx::guest::exec, without ending the execution. The signal reaches the same targets as the execution's own kill, with the same protection against reused process IDs. A guest agent can then stop its workloads gracefully, as container runtimes do: SIGTERM, a grace period, then SIGKILL. It can also forward a signal that its host asks for, such as an interrupt or a hangup, through the library instead of through a process handle of its own.

Today (dev d04d9fb):

  • An Execution ends its program only with SIGKILL, as its module documentation says (nvx-rs/src/guest/exec.rs:61-69).
    • cancel, terminate, disconnect and the timeout (:883) all go through end (:972) and signal_program (:1011).
    • That sends SIGKILL through the program's pidfd (sys::kill_through_pidfd, nvx-rs/src/guest/sys/process.rs:582-600, which hard-codes the signal at :594).
    • The kill reaches the program alone, or also the process group that the program leads (Options::process_group, exec.rs:267). Before Linux 6.9, it signals the group by its ID while the program is unreaped (:1028).
    • It also kills the processes of the execution's cgroup (Options::cgroup, :277).
  • No call sends another signal. A caller that needs one brings its own handle to the program, and repeats the execution's targeting:
    • When the execution collects the program's status, the program's pidfd shows only as a poll descriptor (Source::Exit, exec.rs:314; descriptors, :815).
    • With Options::caller_reaps (:297), as a PID 1 uses it, the execution exposes no pidfd at all. The caller opens or keeps one of its own, or signals by process ID, and must itself make sure that no collector has taken the program's status yet.
    • Either way, the caller repeats the process-group flag, the fallback before Linux 6.9, and the handling of ESRCH.
  • A caller that drives the execution through the engine's Binding gets only &Execution (nvx-rs/src/guest/exec/binding.rs:150). Every termination that the runner requests maps to cancel, disconnect or terminate (binding.rs:316-322), and so to SIGKILL.
  • The engine's grace (Timers) lets pending output flow after the runner ends the workload (nvx-rs/src/exec/mod.rs:74-81). It doesn't give the workload time to handle a signal first.
  • On the host, the supervisor already stops a VMM gracefully, with a forced step that the caller chooses (#620). The guest library's executions have no such step.

Scope (one PR):

  1. Execution::signal (the name is open):
    • It sends a signal that the caller chooses through the program's pidfd. The signal reaches what the execution's kill reaches, except the cgroup: the program, and the process group that the program leads when Options::process_group is set.
    • It uses the same fallback before Linux 6.9. Once a collector has taken the program's status, it sends nothing, and tells the caller that the program is gone.
    • It returns whether the program was unreaped, and reports a signal that the kernel refuses as an error.
    • It doesn't end the execution: it records no Cause, closes no input, and leaves the timeout in place. A program that exits after the signal ends with its own status, and Outcome::cause reports no cause, as for a program that ended by itself.
    • Suggested: it refuses SIGKILL, which stays with cancel and terminate, so that the outcome always says why the execution ended its program.
  2. The same through Binding, as a passthrough, since the binding exposes only &Execution. The runner sees nothing until the program exits.
  3. Documentation: the module's "Status and ending" section describes a graceful stop: signal, a wait until the caller's own deadline, then terminate or cancel.

Acceptance:

  • A program that handles SIGTERM and exits with 0 ends with a successful status and no cause, and loses no output.
  • A program that ignores SIGTERM keeps running, and terminate then ends it with Cause::Terminated.
  • With Options::process_group, a process of the group other than the program receives the signal. Before Linux 6.9, the fallback (exec.rs:1028) signals the group by its ID in the same way.
  • Once a collector has taken the program's status (Options::caller_reaps with observe), signal sends nothing and says that the program is gone, so it can't reach a process that later takes the same ID.
  • Both modes are covered: the execution collects the program's status, or the caller does. The same holds for an adopted program (#831) as for a spawned one.
  • If the suggestion above is taken, signal refuses SIGKILL.

Not in this issue:

  • A signal that the host requests through the execution engine (nvx::exec) and an adapter's framing. A separate issue can follow if an adapter needs one.
  • Signals other than SIGKILL to the processes of the execution's cgroup. cgroup.kill sends only SIGKILL, and signalling each process that cgroup.procs lists would race with forks.
  • Changes to how NVX's managed agent or the test adapter stop their executions.
Lingua principale
Rust
Stelle
758
Fork
48
Merge medio
17h 6m
PR unite (30g)
452

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di microsoft/nvx

Tutte le issue di microsoft/nvx

Issue simili

Altre issue su Rust

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.