summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/linux.rs99
1 files changed, 63 insertions, 36 deletions
diff --git a/src/linux.rs b/src/linux.rs
index 90a2c4d..b1d2732 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -49,6 +49,7 @@ pub enum Error {
     CreateGuestMemory(Box<error::Error>),
     CreateIrqChip(Box<error::Error>),
     CreateKvm(sys_util::Error),
+    CreatePollContext(sys_util::Error),
     CreateSignalFd(sys_util::SignalFdError),
     CreateSocket(io::Error),
     CreateVcpu(sys_util::Error),
@@ -61,6 +62,7 @@ pub enum Error {
     NetDeviceNew(devices::virtio::NetError),
     NoVarEmpty,
     OpenKernel(PathBuf, io::Error),
+    PollContextAdd(sys_util::Error),
     QcowDeviceCreate(qcow::Error),
     RegisterBalloon(device_manager::Error),
     RegisterBlock(device_manager::Error),
@@ -102,6 +104,7 @@ impl fmt::Display for Error {
                 write!(f, "failed to create in-kernel IRQ chip: {:?}", e)
             }
             &Error::CreateKvm(ref e) => write!(f, "failed to open /dev/kvm: {:?}", e),
+            &Error::CreatePollContext(ref e) => write!(f, "failed to create poll context: {:?}", e),
             &Error::CreateSignalFd(ref e) => write!(f, "failed to create signalfd: {:?}", e),
             &Error::CreateSocket(ref e) => write!(f, "failed to create socket: {}", e),
             &Error::CreateVcpu(ref e) => write!(f, "failed to create VCPU: {:?}", e),
@@ -118,6 +121,7 @@ impl fmt::Display for Error {
             &Error::OpenKernel(ref p, ref e) => {
                 write!(f, "failed to open kernel image {:?}: {}", p, e)
             }
+            &Error::PollContextAdd(ref e) => write!(f, "failed to add fd to poll context: {:?}", e),
             &Error::QcowDeviceCreate(ref e) => {
                 write!(f, "failed to read qcow formatted file {:?}", e)
             }
@@ -493,10 +497,13 @@ fn run_control(vm: &mut Vm,
                -> Result<()> {
     const MAX_VM_FD_RECV: usize = 1;
 
-    const EXIT: u32 = 0;
-    const STDIN: u32 = 1;
-    const CHILD_SIGNAL: u32 = 2;
-    const VM_BASE: u32 = 3;
+    #[derive(PollToken)]
+    enum Token {
+        Exit,
+        Stdin,
+        ChildSignal,
+        VmControl { index: usize },
+    }
 
     let stdin_handle = stdin();
     let stdin_lock = stdin_handle.lock();
@@ -504,20 +511,21 @@ fn run_control(vm: &mut Vm,
         .set_raw_mode()
         .expect("failed to set terminal raw mode");
 
-    let mut pollables = Vec::new();
-    pollables.push((EXIT, &exit_evt as &Pollable));
-    pollables.push((STDIN, &stdin_lock as &Pollable));
-    pollables.push((CHILD_SIGNAL, &sigchld_fd as &Pollable));
-    for (i, socket) in control_sockets.iter().enumerate() {
-        pollables.push((VM_BASE + i as u32, socket.as_ref() as &Pollable));
+    let poll_ctx = PollContext::new().map_err(Error::CreatePollContext)?;
+    poll_ctx.add(&exit_evt, Token::Exit).map_err(Error::PollContextAdd)?;
+    if let Err(e) = poll_ctx.add(&stdin_handle, Token::Stdin) {
+        warn!("failed to add stdin to poll context: {:?}", e);
+    }
+    poll_ctx.add(&sigchld_fd, Token::ChildSignal).map_err(Error::PollContextAdd)?;
+    for (index, socket) in control_sockets.iter().enumerate() {
+        poll_ctx.add(socket.as_ref(), Token::VmControl{ index }).map_err(Error::PollContextAdd)?;
     }
 
-    let mut poller = Poller::new(pollables.len());
     let mut scm = Scm::new(MAX_VM_FD_RECV);
 
     'poll: loop {
-        let tokens = {
-            match poller.poll(&pollables[..]) {
+        let events = {
+            match poll_ctx.wait() {
                 Ok(v) => v,
                 Err(e) => {
                     error!("failed to poll: {:?}", e);
@@ -525,22 +533,22 @@ fn run_control(vm: &mut Vm,
                 }
             }
         };
-        for &token in tokens {
-            match token {
-                EXIT => {
+        for event in events.iter_readable() {
+            match event.token() {
+                Token::Exit => {
                     info!("vcpu requested shutdown");
                     break 'poll;
                 }
-                STDIN => {
+                Token::Stdin => {
                     let mut out = [0u8; 64];
                     match stdin_lock.read_raw(&mut out[..]) {
                         Ok(0) => {
                             // Zero-length read indicates EOF. Remove from pollables.
-                            pollables.retain(|&pollable| pollable.0 != STDIN);
+                            let _ = poll_ctx.delete(&stdin_handle);
                         },
                         Err(e) => {
                             warn!("error while reading stdin: {:?}", e);
-                            pollables.retain(|&pollable| pollable.0 != STDIN);
+                            let _ = poll_ctx.delete(&stdin_handle);
                         },
                         Ok(count) => {
                             stdio_serial
@@ -551,7 +559,7 @@ fn run_control(vm: &mut Vm,
                         },
                     }
                 }
-                CHILD_SIGNAL => {
+                Token::ChildSignal => {
                     // Print all available siginfo structs, then exit the loop.
                     loop {
                         let result = sigchld_fd.read().map_err(Error::SignalFd)?;
@@ -565,26 +573,45 @@ fn run_control(vm: &mut Vm,
                         break 'poll;
                     }
                 }
-                t if t >= VM_BASE && t < VM_BASE + (control_sockets.len() as u32) => {
-                    let socket = &control_sockets[(t - VM_BASE) as usize];
-                    match VmRequest::recv(&mut scm, socket.as_ref()) {
-                        Ok(request) => {
-                            let mut running = true;
-                            let response =
-                                request.execute(vm, next_dev_pfn,
-                                                &mut running, &balloon_host_socket);
-                            if let Err(e) = response.send(&mut scm, socket.as_ref()) {
-                                error!("failed to send VmResponse: {:?}", e);
-                            }
-                            if !running {
-                                info!("control socket requested exit");
-                                break 'poll;
+                Token::VmControl { index } => {
+                    if let Some(socket) = control_sockets.get(index as usize) {
+                        match VmRequest::recv(&mut scm, socket.as_ref()) {
+                            Ok(request) => {
+                                let mut running = true;
+                                let response =
+                                    request.execute(vm, next_dev_pfn,
+                                                    &mut running, &balloon_host_socket);
+                                if let Err(e) = response.send(&mut scm, socket.as_ref()) {
+                                    error!("failed to send VmResponse: {:?}", e);
+                                }
+                                if !running {
+                                    info!("control socket requested exit");
+                                    break 'poll;
+                                }
                             }
+                            Err(e) => error!("failed to recv VmRequest: {:?}", e),
                         }
-                        Err(e) => error!("failed to recv VmRequest: {:?}", e),
                     }
                 }
-                _ => {}
+            }
+        }
+        for event in events.iter_hungup() {
+            // It's possible more data is readable and buffered while the socket is hungup, so
+            // don't delete the socket from the poll context until we're sure all the data is
+            // read.
+            if !event.readable() {
+                match event.token() {
+                    Token::Exit => {},
+                    Token::Stdin => {
+                        let _ = poll_ctx.delete(&stdin_handle);
+                    },
+                    Token::ChildSignal => {},
+                    Token::VmControl { index } => {
+                        if let Some(socket) = control_sockets.get(index as usize) {
+                            let _ = poll_ctx.delete(socket.as_ref());
+                        }
+                    },
+                }
             }
         }
     }