From 0e7c83dd51b7a439f9050a20f4ade6c47ac55538 Mon Sep 17 00:00:00 2001 From: Thomas Prescher Date: Wed, 4 Feb 2026 13:45:07 +0100 Subject: [PATCH 1/2] Revert "vmm: fix panic in http API when connecting GDB" This reverts commit 3fe978e9e659908c847df756f0e0c54ae7eade84. --- vmm/src/api/http/mod.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/vmm/src/api/http/mod.rs b/vmm/src/api/http/mod.rs index e326bee3b1..533d0a0285 100644 --- a/vmm/src/api/http/mod.rs +++ b/vmm/src/api/http/mod.rs @@ -6,7 +6,6 @@ use std::collections::BTreeMap; use std::error::Error; use std::fs::File; -use std::io::ErrorKind; use std::os::fd::AsRawFd; use std::os::unix::io::{IntoRawFd, RawFd}; use std::os::unix::net::UnixListener; @@ -550,12 +549,7 @@ fn start_http_thread( server.start_server().unwrap(); loop { - let n = match outer_epoll.wait(-1, &mut events) { - Ok(n) => n, - // Can for example happen when connecting a debugger. - Err(e) if e.kind() == ErrorKind::Interrupted => continue, - Err(e) => panic!("failed to wait for events: {e}"), - }; + let n = outer_epoll.wait(-1, &mut events).unwrap(); for ev in events.iter().take(n) { match ev.data() { HTTP_EPOLL_TOKEN => { From 6e6f9232262c40ab8ad919ba11c66639a358c560 Mon Sep 17 00:00:00 2001 From: Thomas Prescher Date: Wed, 4 Feb 2026 13:47:22 +0100 Subject: [PATCH 2/2] Revert "vmm: api: make HTTP Server (API) multithreaded" This reverts commit daaea602a21b95bbfc3c1e277e1b7145377bc561. --- vmm/src/api/http/mod.rs | 248 ++++--------------------------------- vmm/src/seccomp_filters.rs | 1 - 2 files changed, 23 insertions(+), 226 deletions(-) diff --git a/vmm/src/api/http/mod.rs b/vmm/src/api/http/mod.rs index 533d0a0285..2aa52e8e37 100644 --- a/vmm/src/api/http/mod.rs +++ b/vmm/src/api/http/mod.rs @@ -6,25 +6,22 @@ use std::collections::BTreeMap; use std::error::Error; use std::fs::File; -use std::os::fd::AsRawFd; use std::os::unix::io::{IntoRawFd, RawFd}; use std::os::unix::net::UnixListener; use std::panic::AssertUnwindSafe; use std::path::PathBuf; -use std::sync::mpsc::{Receiver, Sender, channel, sync_channel}; -use std::sync::{Arc, LazyLock, Mutex}; +use std::sync::LazyLock; +use std::sync::mpsc::Sender; use std::thread; use hypervisor::HypervisorType; -use log::{debug, error}; +use log::error; use micro_http::{ - Body, HttpServer, MediaType, Method, Request, Response, ServerError, ServerRequest, - ServerResponse, StatusCode, Version, + Body, HttpServer, MediaType, Method, Request, Response, ServerError, StatusCode, Version, }; use seccompiler::{SeccompAction, apply_filter}; use serde_json::Error as SerdeError; use thiserror::Error; -use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet}; use vmm_sys_util::eventfd::EventFd; use self::http_endpoint::{VmActionHandler, VmCreate, VmInfo, VmmPing, VmmShutdown}; @@ -319,153 +316,10 @@ fn handle_http_request( response } -/// Keeps track of the worker threads, and the resources needed to interact -/// with them. -#[derive(Debug)] -struct HttpWorkerThreads { - // The worker threads themselves. - threads: Vec>>, - // An MPSC channel to send server requests to the workers. We put it into - // an option so we can easily drop it in the destructor. - request_tx: Option>, - // An MPSC channel that the workers use to send responses to the HTTP - // server thread. - response_rx: Receiver, - // Workers signal this eventfd when they have a response for the HTTP - // server thread. - response_event: EventFd, -} - -impl HttpWorkerThreads { - fn new( - thread_count: usize, - api_notifier: &EventFd, - api_sender: &Sender, - seccomp_action: &SeccompAction, - hypervisor_type: HypervisorType, - landlock_enable: bool, - exit_evt: &EventFd, - ) -> Result { - let response_event = EventFd::new(libc::EFD_NONBLOCK).map_err(VmmError::EventFdCreate)?; - let (response_tx, response_rx) = sync_channel::(thread_count); - - let mut threads = Vec::new(); - let (request_tx, request_rx) = channel::(); - - let request_rx = Arc::new(Mutex::new(request_rx)); - - // We use the same seccomp filter that we already use for the HTTP server thread. - let api_seccomp_filter = - get_seccomp_filter(seccomp_action, Thread::HttpApi, hypervisor_type) - .map_err(VmmError::CreateSeccompFilter)?; - - for n in 0..thread_count { - let response_event = response_event.try_clone().map_err(VmmError::EventFdClone)?; - - let response_tx = response_tx.clone(); - let request_rx = request_rx.clone(); - - let api_notifier = api_notifier.try_clone().map_err(VmmError::EventFdClone)?; - let api_sender = api_sender.clone(); - - let api_seccomp_filter = api_seccomp_filter.clone(); - let exit_evt = exit_evt.try_clone().map_err(VmmError::EventFdClone)?; - - let thread = thread::Builder::new() - .name(format!("http-worker-{n}").to_string()) - .spawn(move || { - debug!("Spawned HTTP worker thread with id {n}",); - if !api_seccomp_filter.is_empty() { - apply_filter(&api_seccomp_filter) - .map_err(VmmError::ApplySeccompFilter) - .map_err(|e| { - error!("Error applying seccomp filter: {e:?}"); - exit_evt.write(1).ok(); - e - })?; - } - - if landlock_enable { - Landlock::new() - .map_err(VmmError::CreateLandlock)? - .restrict_self() - .map_err(VmmError::ApplyLandlock) - .map_err(|e| { - error!("Error applying landlock to http-worker thread: {e:?}"); - exit_evt.write(1).ok(); - e - })?; - } - - std::panic::catch_unwind(AssertUnwindSafe(move || { - let id = n; - loop { - let request = request_rx.lock().unwrap().recv(); - match request { - Ok(msg) => { - // Process the server request - let response = msg.process(|request| { - handle_http_request(request, &api_notifier, &api_sender) - }); - - // Send the response to the HTTP server thread together with this - // threads id. - if let Err(e) = response_tx.send(response) { - error!( - "HTTP worker thread {id}: error sending response {e}" - ); - break; - } - - // Notify the HTTP server thread. - response_event.write(1).ok(); - } - Err(_) => { - // We assume that the other side of the channel - // closed because the VMM received a shutdown request. - break; - } - } - } - })) - .map_err(|_| { - error!("http-worker thread {n} panicked"); - exit_evt.write(1).ok() - }) - .ok(); - - Ok(()) - }) - .map_err(VmmError::HttpThreadSpawn)?; - - threads.push(thread); - } - - Ok(Self { - threads, - request_tx: Some(request_tx), - response_rx, - response_event, - }) - } -} - -impl Drop for HttpWorkerThreads { - fn drop(&mut self) { - // Dropping the Sender side of the request channels to throw the worker - // threads out of their loops. - drop(self.request_tx.take()); - // Now we can join each thread. - self.threads - .drain(..) - .for_each(|thread| thread.join().unwrap().unwrap()); - } -} - fn start_http_thread( mut server: HttpServer, - api_notifier: &EventFd, - api_sender: &Sender, + api_notifier: EventFd, + api_sender: Sender, seccomp_action: &SeccompAction, exit_evt: EventFd, hypervisor_type: HypervisorType, @@ -482,42 +336,6 @@ fn start_http_thread( .add_kill_switch(api_shutdown_fd_clone) .map_err(VmmError::CreateApiServer)?; - // We use the epoll mechanism to parallelize this. The epoll tokens are - // attached when registering the FDs with epoll. That way we can later - // check why we were notified. - const HTTP_EPOLL_TOKEN: u64 = 1; - const WORKER_EPOLL_TOKEN: u64 = 2; - - // The epoll instance our HTTP server thread will wait on. - let outer_epoll = Epoll::new().unwrap(); - let worker_threads = HttpWorkerThreads::new( - 2, - api_notifier, - api_sender, - seccomp_action, - hypervisor_type, - landlock_enable, - &exit_evt, - )?; - - // Register the fd that the worker threads will signal. - outer_epoll - .ctl( - ControlOperation::Add, - worker_threads.response_event.as_raw_fd(), - EpollEvent::new(EventSet::IN, WORKER_EPOLL_TOKEN), - ) - .unwrap(); - - // Register the HttpServer's fd. - outer_epoll - .ctl( - ControlOperation::Add, - server.epoll().as_raw_fd(), - EpollEvent::new(EventSet::IN, HTTP_EPOLL_TOKEN), - ) - .unwrap(); - let thread = thread::Builder::new() .name("http-server".to_string()) .spawn(move || { @@ -545,42 +363,24 @@ fn start_http_thread( } std::panic::catch_unwind(AssertUnwindSafe(move || { - let mut events = vec![EpollEvent::default(); 32]; server.start_server().unwrap(); - loop { - let n = outer_epoll.wait(-1, &mut events).unwrap(); - for ev in events.iter().take(n) { - match ev.data() { - HTTP_EPOLL_TOKEN => { - // The HttpServer got a request, handle that. - match server.requests() { - Ok(request_vec) => { - for server_request in request_vec { - worker_threads.request_tx.as_ref().unwrap().send(server_request).unwrap(); - } - } - Err(ServerError::ShutdownEvent) => { - server.flush_outgoing_writes(); - return; - } - Err(e) => { - error!( - "HTTP server error on retrieving incoming request. Error: {e}" - ); - } - } - } - WORKER_EPOLL_TOKEN => { - // One of the worker threads has a response. - // We clear the eventfd first. - let _ = worker_threads.response_event.read().unwrap(); - let response = worker_threads.response_rx.recv().unwrap(); - if let Err(e) = server.respond(response){ + match server.requests() { + Ok(request_vec) => { + for server_request in request_vec { + if let Err(e) = server.respond(server_request.process(|request| { + handle_http_request(request, &api_notifier, &api_sender) + })) { error!("HTTP server error on response: {e}"); } } - _ => { } + } + Err(ServerError::ShutdownEvent) => { + server.flush_outgoing_writes(); + return; + } + Err(e) => { + error!("HTTP server error on retrieving incoming request. Error: {e}"); } } } @@ -598,7 +398,6 @@ fn start_http_thread( Ok((thread, api_shutdown_fd)) } -#[allow(clippy::needless_pass_by_value)] pub fn start_http_path_thread( path: &str, api_notifier: EventFd, @@ -616,8 +415,8 @@ pub fn start_http_path_thread( start_http_thread( server, - &api_notifier, - &api_sender, + api_notifier, + api_sender, seccomp_action, exit_evt, hypervisor_type, @@ -625,7 +424,6 @@ pub fn start_http_path_thread( ) } -#[allow(clippy::needless_pass_by_value)] pub fn start_http_fd_thread( fd: RawFd, api_notifier: EventFd, @@ -639,8 +437,8 @@ pub fn start_http_fd_thread( let server = unsafe { HttpServer::new_from_fd(fd) }.map_err(VmmError::CreateApiServer)?; start_http_thread( server, - &api_notifier, - &api_sender, + api_notifier, + api_sender, seccomp_action, exit_evt, hypervisor_type, diff --git a/vmm/src/seccomp_filters.rs b/vmm/src/seccomp_filters.rs index e30f2fab70..671997797a 100644 --- a/vmm/src/seccomp_filters.rs +++ b/vmm/src/seccomp_filters.rs @@ -866,7 +866,6 @@ fn http_api_thread_rules() -> Result)>, BackendError> (libc::SYS_rt_sigprocmask, vec![]), (libc::SYS_getcwd, vec![]), (libc::SYS_clock_nanosleep, vec![]), - (libc::SYS_read, vec![]), ]) }