summary refs log tree commit diff
path: root/devices/src
diff options
context:
space:
mode:
authorDaniel Verkamp <dverkamp@chromium.org>2018-10-24 17:06:07 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-10-29 21:18:14 -0700
commit120d95e03123ede9cc3aa05a8ce976fd4678a890 (patch)
tree011d7e856f3085de6f024a08674b7ef499cfcf69 /devices/src
parent510c783c847b6d0c18516f31fbe3dbdc782f1252 (diff)
downloadcrosvm-120d95e03123ede9cc3aa05a8ce976fd4678a890.tar
crosvm-120d95e03123ede9cc3aa05a8ce976fd4678a890.tar.gz
crosvm-120d95e03123ede9cc3aa05a8ce976fd4678a890.tar.bz2
crosvm-120d95e03123ede9cc3aa05a8ce976fd4678a890.tar.lz
crosvm-120d95e03123ede9cc3aa05a8ce976fd4678a890.tar.xz
crosvm-120d95e03123ede9cc3aa05a8ce976fd4678a890.tar.zst
crosvm-120d95e03123ede9cc3aa05a8ce976fd4678a890.zip
devices: pci: support level-triggered interrupts
Register the irqfd with resample support so that we can correctly
emulate level-triggered interrupts.  This requires each PciDevice to
listen for interrupt_resample events and re-assert the IRQ eventfd if it
should still be active.

BUG=None
TEST=Boot crosvm on x86-64 and arm devices

Change-Id: I5cf8d1d1705cf675b453962c00d2d606801fee91
Signed-off-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1298654
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Diffstat (limited to 'devices/src')
-rw-r--r--devices/src/pci/pci_device.rs21
-rw-r--r--devices/src/virtio/balloon.rs13
-rw-r--r--devices/src/virtio/block.rs15
-rw-r--r--devices/src/virtio/gpu/mod.rs13
-rw-r--r--devices/src/virtio/mmio.rs23
-rw-r--r--devices/src/virtio/net.rs16
-rw-r--r--devices/src/virtio/p9.rs16
-rw-r--r--devices/src/virtio/rng.rs15
-rw-r--r--devices/src/virtio/vhost/net.rs3
-rw-r--r--devices/src/virtio/vhost/vsock.rs2
-rw-r--r--devices/src/virtio/vhost/worker.rs15
-rw-r--r--devices/src/virtio/virtio_device.rs1
-rw-r--r--devices/src/virtio/virtio_pci_common_config.rs1
-rw-r--r--devices/src/virtio/virtio_pci_device.rs35
-rw-r--r--devices/src/virtio/wl.rs17
15 files changed, 178 insertions, 28 deletions
diff --git a/devices/src/pci/pci_device.rs b/devices/src/pci/pci_device.rs
index 395caf1..275d8d3 100644
--- a/devices/src/pci/pci_device.rs
+++ b/devices/src/pci/pci_device.rs
@@ -29,7 +29,16 @@ pub trait PciDevice: Send {
     /// after jailing. Must be called before the process is jailed.
     fn keep_fds(&self) -> Vec<RawFd>;
     /// Assign a legacy PCI IRQ to this device.
-    fn assign_irq(&mut self, _irq_evt: EventFd, _irq_num: u32, _irq_pin: PciInterruptPin) {}
+    /// The device may write to `irq_evt` to trigger an interrupt.
+    /// When `irq_resample_evt` is signaled, the device should re-assert `irq_evt` if necessary.
+    fn assign_irq(
+        &mut self,
+        _irq_evt: EventFd,
+        _irq_resample_evt: EventFd,
+        _irq_num: u32,
+        _irq_pin: PciInterruptPin,
+    ) {
+    }
     /// Allocates the needed IO BAR space using the `allocate` function which takes a size and
     /// returns an address. Returns a Vec of (address, length) tuples.
     fn allocate_io_bars(&mut self, _resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {
@@ -90,8 +99,14 @@ impl<T: PciDevice + ?Sized> PciDevice for Box<T> {
     fn keep_fds(&self) -> Vec<RawFd> {
         (**self).keep_fds()
     }
-    fn assign_irq(&mut self, irq_evt: EventFd, irq_num: u32, irq_pin: PciInterruptPin) {
-        (**self).assign_irq(irq_evt, irq_num, irq_pin)
+    fn assign_irq(
+        &mut self,
+        irq_evt: EventFd,
+        irq_resample_evt: EventFd,
+        irq_num: u32,
+        irq_pin: PciInterruptPin,
+    ) {
+        (**self).assign_irq(irq_evt, irq_resample_evt, irq_num, irq_pin)
     }
     /// Allocates the needed IO BAR space using the `allocate` function which takes a size and
     /// returns an address. Returns a Vec of (address, length) tuples.
diff --git a/devices/src/virtio/balloon.rs b/devices/src/virtio/balloon.rs
index 76cbc33..2bd1ba3 100644
--- a/devices/src/virtio/balloon.rs
+++ b/devices/src/virtio/balloon.rs
@@ -53,6 +53,7 @@ struct Worker {
     deflate_queue: Queue,
     interrupt_status: Arc<AtomicUsize>,
     interrupt_evt: EventFd,
+    interrupt_resample_evt: EventFd,
     config: Arc<BalloonConfig>,
     command_socket: UnixDatagram,
 }
@@ -127,6 +128,7 @@ impl Worker {
             Inflate,
             Deflate,
             CommandSocket,
+            InterruptResample,
             Kill,
         }
 
@@ -139,6 +141,9 @@ impl Worker {
             .and_then(|pc| {
                 pc.add(&self.command_socket, Token::CommandSocket)
                     .and(Ok(pc))
+            }).and_then(|pc| {
+                pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+                    .and(Ok(pc))
             }).and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
         {
             Ok(pc) => pc,
@@ -188,6 +193,12 @@ impl Worker {
                             }
                         }
                     }
+                    Token::InterruptResample => {
+                        let _ = self.interrupt_resample_evt.read();
+                        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+                            self.interrupt_evt.write(1).unwrap();
+                        }
+                    }
                     Token::Kill => break 'poll,
                 }
             }
@@ -302,6 +313,7 @@ impl VirtioDevice for Balloon {
         &mut self,
         mem: GuestMemory,
         interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
         status: Arc<AtomicUsize>,
         mut queues: Vec<Queue>,
         queue_evts: Vec<EventFd>,
@@ -330,6 +342,7 @@ impl VirtioDevice for Balloon {
                     deflate_queue: queues.remove(0),
                     interrupt_status: status,
                     interrupt_evt,
+                    interrupt_resample_evt,
                     command_socket,
                     config,
                 };
diff --git a/devices/src/virtio/block.rs b/devices/src/virtio/block.rs
index 9e0fc7e..c1e7be8 100644
--- a/devices/src/virtio/block.rs
+++ b/devices/src/virtio/block.rs
@@ -469,6 +469,7 @@ struct Worker<T: DiskFile> {
     read_only: bool,
     interrupt_status: Arc<AtomicUsize>,
     interrupt_evt: EventFd,
+    interrupt_resample_evt: EventFd,
 }
 
 impl<T: DiskFile> Worker<T> {
@@ -535,6 +536,7 @@ impl<T: DiskFile> Worker<T> {
         enum Token {
             FlushTimer,
             QueueAvailable,
+            InterruptResample,
             Kill,
         }
 
@@ -550,7 +552,10 @@ impl<T: DiskFile> Worker<T> {
         let poll_ctx: PollContext<Token> = match PollContext::new()
             .and_then(|pc| pc.add(&flush_timer, Token::FlushTimer).and(Ok(pc)))
             .and_then(|pc| pc.add(&queue_evt, Token::QueueAvailable).and(Ok(pc)))
-            .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+            .and_then(|pc| {
+                pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+                    .and(Ok(pc))
+            }).and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
         {
             Ok(pc) => pc,
             Err(e) => {
@@ -589,6 +594,12 @@ impl<T: DiskFile> Worker<T> {
                         needs_interrupt |=
                             self.process_queue(0, &mut flush_timer, &mut flush_timer_armed);
                     }
+                    Token::InterruptResample => {
+                        let _ = self.interrupt_resample_evt.read();
+                        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+                            self.interrupt_evt.write(1).unwrap();
+                        }
+                    }
                     Token::Kill => break 'poll,
                 }
             }
@@ -709,6 +720,7 @@ impl<T: 'static + AsRawFd + DiskFile + Send> VirtioDevice for Block<T> {
         &mut self,
         mem: GuestMemory,
         interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
         status: Arc<AtomicUsize>,
         queues: Vec<Queue>,
         mut queue_evts: Vec<EventFd>,
@@ -739,6 +751,7 @@ impl<T: 'static + AsRawFd + DiskFile + Send> VirtioDevice for Block<T> {
                             read_only,
                             interrupt_status: status,
                             interrupt_evt,
+                            interrupt_resample_evt,
                         };
                         worker.run(queue_evts.remove(0), kill_evt);
                     });
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs
index ec60634..5ef90c9 100644
--- a/devices/src/virtio/gpu/mod.rs
+++ b/devices/src/virtio/gpu/mod.rs
@@ -456,6 +456,7 @@ struct Worker {
     exit_evt: EventFd,
     mem: GuestMemory,
     interrupt_evt: EventFd,
+    interrupt_resample_evt: EventFd,
     interrupt_status: Arc<AtomicUsize>,
     ctrl_queue: Queue,
     ctrl_evt: EventFd,
@@ -478,6 +479,7 @@ impl Worker {
             CtrlQueue,
             CursorQueue,
             Display,
+            InterruptResample,
             Kill,
         }
 
@@ -487,6 +489,9 @@ impl Worker {
             .and_then(|pc| {
                 pc.add(&*self.state.display().borrow(), Token::Display)
                     .and(Ok(pc))
+            }).and_then(|pc| {
+                pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+                    .and(Ok(pc))
             }).and_then(|pc| pc.add(&self.kill_evt, Token::Kill).and(Ok(pc)))
         {
             Ok(pc) => pc,
@@ -530,6 +535,12 @@ impl Worker {
                             let _ = self.exit_evt.write(1);
                         }
                     }
+                    Token::InterruptResample => {
+                        let _ = self.interrupt_resample_evt.read();
+                        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+                            self.interrupt_evt.write(1).unwrap();
+                        }
+                    }
                     Token::Kill => {
                         break 'poll;
                     }
@@ -664,6 +675,7 @@ impl VirtioDevice for Gpu {
         &mut self,
         mem: GuestMemory,
         interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
         interrupt_status: Arc<AtomicUsize>,
         mut queues: Vec<Queue>,
         mut queue_evts: Vec<EventFd>,
@@ -732,6 +744,7 @@ impl VirtioDevice for Gpu {
                 exit_evt,
                 mem,
                 interrupt_evt,
+                interrupt_resample_evt,
                 interrupt_status,
                 ctrl_queue,
                 ctrl_evt,
diff --git a/devices/src/virtio/mmio.rs b/devices/src/virtio/mmio.rs
index 400afd9..bca6de0 100644
--- a/devices/src/virtio/mmio.rs
+++ b/devices/src/virtio/mmio.rs
@@ -39,6 +39,7 @@ pub struct MmioDevice {
     queue_select: u32,
     interrupt_status: Arc<AtomicUsize>,
     interrupt_evt: Option<EventFd>,
+    interrupt_resample_evt: Option<EventFd>,
     driver_status: u32,
     config_generation: u32,
     queues: Vec<Queue>,
@@ -66,6 +67,7 @@ impl MmioDevice {
             queue_select: 0,
             interrupt_status: Arc::new(AtomicUsize::new(0)),
             interrupt_evt: Some(EventFd::new()?),
+            interrupt_resample_evt: Some(EventFd::new()?),
             driver_status: 0,
             config_generation: 0,
             queues,
@@ -209,15 +211,18 @@ impl BusDevice for MmioDevice {
 
         if !self.device_activated && self.is_driver_ready() && self.are_queues_valid() {
             if let Some(interrupt_evt) = self.interrupt_evt.take() {
-                if let Some(mem) = self.mem.take() {
-                    self.device.activate(
-                        mem,
-                        interrupt_evt,
-                        self.interrupt_status.clone(),
-                        self.queues.clone(),
-                        self.queue_evts.split_off(0),
-                    );
-                    self.device_activated = true;
+                if let Some(interrupt_resample_evt) = self.interrupt_resample_evt.take() {
+                    if let Some(mem) = self.mem.take() {
+                        self.device.activate(
+                            mem,
+                            interrupt_evt,
+                            interrupt_resample_evt,
+                            self.interrupt_status.clone(),
+                            self.queues.clone(),
+                            self.queue_evts.split_off(0),
+                        );
+                        self.device_activated = true;
+                    }
                 }
             }
         }
diff --git a/devices/src/virtio/net.rs b/devices/src/virtio/net.rs
index e5b9650..97feb4c 100644
--- a/devices/src/virtio/net.rs
+++ b/devices/src/virtio/net.rs
@@ -60,6 +60,7 @@ struct Worker<T: TapT> {
     tap: T,
     interrupt_status: Arc<AtomicUsize>,
     interrupt_evt: EventFd,
+    interrupt_resample_evt: EventFd,
     rx_buf: [u8; MAX_BUFFER_SIZE],
     rx_count: usize,
     deferred_rx: bool,
@@ -234,6 +235,8 @@ where
             RxQueue,
             // The transmit queue has a frame that is ready to send from the guest.
             TxQueue,
+            // Check if any interrupts need to be re-asserted.
+            InterruptResample,
             // crosvm has requested the device to shut down.
             Kill,
         }
@@ -242,7 +245,10 @@ where
             .and_then(|pc| pc.add(&self.tap, Token::RxTap).and(Ok(pc)))
             .and_then(|pc| pc.add(&rx_queue_evt, Token::RxQueue).and(Ok(pc)))
             .and_then(|pc| pc.add(&tx_queue_evt, Token::TxQueue).and(Ok(pc)))
-            .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+            .and_then(|pc| {
+                pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+                    .and(Ok(pc))
+            }).and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
             .map_err(NetError::CreatePollContext)?;
 
         'poll: loop {
@@ -278,6 +284,12 @@ where
                         }
                         self.process_tx();
                     }
+                    Token::InterruptResample => {
+                        let _ = self.interrupt_resample_evt.read();
+                        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+                            self.interrupt_evt.write(1).unwrap();
+                        }
+                    }
                     Token::Kill => break 'poll,
                 }
             }
@@ -426,6 +438,7 @@ where
         &mut self,
         mem: GuestMemory,
         interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
         status: Arc<AtomicUsize>,
         mut queues: Vec<Queue>,
         mut queue_evts: Vec<EventFd>,
@@ -452,6 +465,7 @@ where
                                 tap,
                                 interrupt_status: status,
                                 interrupt_evt,
+                                interrupt_resample_evt,
                                 rx_buf: [0u8; MAX_BUFFER_SIZE],
                                 rx_count: 0,
                                 deferred_rx: false,
diff --git a/devices/src/virtio/p9.rs b/devices/src/virtio/p9.rs
index 12e4911..2237b0d 100644
--- a/devices/src/virtio/p9.rs
+++ b/devices/src/virtio/p9.rs
@@ -222,6 +222,7 @@ struct Worker {
     server: p9::Server,
     irq_status: Arc<AtomicUsize>,
     irq_evt: EventFd,
+    interrupt_resample_evt: EventFd,
 }
 
 impl Worker {
@@ -270,13 +271,18 @@ impl Worker {
         enum Token {
             // A request is ready on the queue.
             QueueReady,
+            // Check if any interrupts need to be re-asserted.
+            InterruptResample,
             // The parent thread requested an exit.
             Kill,
         }
 
         let poll_ctx: PollContext<Token> = PollContext::new()
             .and_then(|pc| pc.add(&queue_evt, Token::QueueReady).and(Ok(pc)))
-            .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+            .and_then(|pc| {
+                pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+                    .and(Ok(pc))
+            }).and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
             .map_err(P9Error::CreatePollContext)?;
 
         loop {
@@ -287,6 +293,12 @@ impl Worker {
                         queue_evt.read().map_err(P9Error::ReadQueueEventFd)?;
                         self.process_queue()?;
                     }
+                    Token::InterruptResample => {
+                        let _ = self.interrupt_resample_evt.read();
+                        if self.irq_status.load(Ordering::SeqCst) != 0 {
+                            self.irq_evt.write(1).unwrap();
+                        }
+                    }
                     Token::Kill => return Ok(()),
                 }
             }
@@ -398,6 +410,7 @@ impl VirtioDevice for P9 {
         &mut self,
         guest_mem: GuestMemory,
         interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
         status: Arc<AtomicUsize>,
         mut queues: Vec<Queue>,
         mut queue_evts: Vec<EventFd>,
@@ -426,6 +439,7 @@ impl VirtioDevice for P9 {
                             server,
                             irq_status: status,
                             irq_evt: interrupt_evt,
+                            interrupt_resample_evt,
                         };
 
                         worker.run(queue_evts.remove(0), kill_evt)
diff --git a/devices/src/virtio/rng.rs b/devices/src/virtio/rng.rs
index 0c0dd13..2aec07e 100644
--- a/devices/src/virtio/rng.rs
+++ b/devices/src/virtio/rng.rs
@@ -30,6 +30,7 @@ struct Worker {
     random_file: File,
     interrupt_status: Arc<AtomicUsize>,
     interrupt_evt: EventFd,
+    interrupt_resample_evt: EventFd,
 }
 
 impl Worker {
@@ -76,12 +77,16 @@ impl Worker {
         #[derive(PollToken)]
         enum Token {
             QueueAvailable,
+            InterruptResample,
             Kill,
         }
 
         let poll_ctx: PollContext<Token> = match PollContext::new()
             .and_then(|pc| pc.add(&queue_evt, Token::QueueAvailable).and(Ok(pc)))
-            .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+            .and_then(|pc| {
+                pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+                    .and(Ok(pc))
+            }).and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
         {
             Ok(pc) => pc,
             Err(e) => {
@@ -109,6 +114,12 @@ impl Worker {
                         }
                         needs_interrupt |= self.process_queue();
                     }
+                    Token::InterruptResample => {
+                        let _ = self.interrupt_resample_evt.read();
+                        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+                            self.interrupt_evt.write(1).unwrap();
+                        }
+                    }
                     Token::Kill => break 'poll,
                 }
             }
@@ -168,6 +179,7 @@ impl VirtioDevice for Rng {
         &mut self,
         mem: GuestMemory,
         interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
         status: Arc<AtomicUsize>,
         mut queues: Vec<Queue>,
         mut queue_evts: Vec<EventFd>,
@@ -198,6 +210,7 @@ impl VirtioDevice for Rng {
                             random_file,
                             interrupt_status: status,
                             interrupt_evt,
+                            interrupt_resample_evt,
                         };
                         worker.run(queue_evts.remove(0), kill_evt);
                     });
diff --git a/devices/src/virtio/vhost/net.rs b/devices/src/virtio/vhost/net.rs
index 1f73863..96c8a27 100644
--- a/devices/src/virtio/vhost/net.rs
+++ b/devices/src/virtio/vhost/net.rs
@@ -180,6 +180,7 @@ where
         &mut self,
         _: GuestMemory,
         interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
         status: Arc<AtomicUsize>,
         queues: Vec<Queue>,
         queue_evts: Vec<EventFd>,
@@ -203,6 +204,7 @@ where
                                     vhost_interrupt,
                                     status,
                                     interrupt_evt,
+                                    interrupt_resample_evt,
                                     acked_features,
                                 );
                                 let activate_vqs = |handle: &U| -> Result<()> {
@@ -291,6 +293,7 @@ pub mod tests {
         net.activate(
             guest_memory,
             EventFd::new().unwrap(),
+            EventFd::new().unwrap(),
             Arc::new(AtomicUsize::new(0)),
             vec![Queue::new(1)],
             vec![EventFd::new().unwrap()],
diff --git a/devices/src/virtio/vhost/vsock.rs b/devices/src/virtio/vhost/vsock.rs
index 694ea25..31dd61e 100644
--- a/devices/src/virtio/vhost/vsock.rs
+++ b/devices/src/virtio/vhost/vsock.rs
@@ -170,6 +170,7 @@ impl VirtioDevice for Vsock {
         &mut self,
         _: GuestMemory,
         interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
         status: Arc<AtomicUsize>,
         queues: Vec<Queue>,
         queue_evts: Vec<EventFd>,
@@ -196,6 +197,7 @@ impl VirtioDevice for Vsock {
                                 interrupt,
                                 status,
                                 interrupt_evt,
+                                interrupt_resample_evt,
                                 acked_features,
                             );
                             let activate_vqs = |handle: &VhostVsockHandle| -> Result<()> {
diff --git a/devices/src/virtio/vhost/worker.rs b/devices/src/virtio/vhost/worker.rs
index 529ebc0..abd063b 100644
--- a/devices/src/virtio/vhost/worker.rs
+++ b/devices/src/virtio/vhost/worker.rs
@@ -22,6 +22,7 @@ pub struct Worker<T: Vhost> {
     vhost_interrupt: EventFd,
     interrupt_status: Arc<AtomicUsize>,
     interrupt_evt: EventFd,
+    interrupt_resample_evt: EventFd,
     acked_features: u64,
 }
 
@@ -32,6 +33,7 @@ impl<T: Vhost> Worker<T> {
         vhost_interrupt: EventFd,
         interrupt_status: Arc<AtomicUsize>,
         interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
         acked_features: u64,
     ) -> Worker<T> {
         Worker {
@@ -40,6 +42,7 @@ impl<T: Vhost> Worker<T> {
             vhost_interrupt,
             interrupt_status,
             interrupt_evt,
+            interrupt_resample_evt,
             acked_features,
         }
     }
@@ -111,12 +114,16 @@ impl<T: Vhost> Worker<T> {
         #[derive(PollToken)]
         enum Token {
             VhostIrq,
+            InterruptResample,
             Kill,
         }
 
         let poll_ctx: PollContext<Token> = PollContext::new()
             .and_then(|pc| pc.add(&self.vhost_interrupt, Token::VhostIrq).and(Ok(pc)))
-            .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
+            .and_then(|pc| {
+                pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+                    .and(Ok(pc))
+            }).and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
             .map_err(Error::CreatePollContext)?;
 
         'poll: loop {
@@ -129,6 +136,12 @@ impl<T: Vhost> Worker<T> {
                         needs_interrupt = true;
                         self.vhost_interrupt.read().map_err(Error::VhostIrqRead)?;
                     }
+                    Token::InterruptResample => {
+                        let _ = self.interrupt_resample_evt.read();
+                        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+                            self.interrupt_evt.write(1).unwrap();
+                        }
+                    }
                     Token::Kill => break 'poll,
                 }
             }
diff --git a/devices/src/virtio/virtio_device.rs b/devices/src/virtio/virtio_device.rs
index 3c6d868..8f790d9 100644
--- a/devices/src/virtio/virtio_device.rs
+++ b/devices/src/virtio/virtio_device.rs
@@ -56,6 +56,7 @@ pub trait VirtioDevice: Send {
         &mut self,
         mem: GuestMemory,
         interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
         status: Arc<AtomicUsize>,
         queues: Vec<Queue>,
         queue_evts: Vec<EventFd>,
diff --git a/devices/src/virtio/virtio_pci_common_config.rs b/devices/src/virtio/virtio_pci_common_config.rs
index 2793052..8596563 100644
--- a/devices/src/virtio/virtio_pci_common_config.rs
+++ b/devices/src/virtio/virtio_pci_common_config.rs
@@ -236,6 +236,7 @@ mod tests {
             &mut self,
             _mem: GuestMemory,
             _interrupt_evt: EventFd,
+            _interrupt_resample_evt: EventFd,
             _status: Arc<AtomicUsize>,
             _queues: Vec<Queue>,
             _queue_evts: Vec<EventFd>,
diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs
index 43d2b53..c362477 100644
--- a/devices/src/virtio/virtio_pci_device.rs
+++ b/devices/src/virtio/virtio_pci_device.rs
@@ -148,6 +148,7 @@ pub struct VirtioPciDevice {
 
     interrupt_status: Arc<AtomicUsize>,
     interrupt_evt: Option<EventFd>,
+    interrupt_resample_evt: Option<EventFd>,
     queues: Vec<Queue>,
     queue_evts: Vec<EventFd>,
     mem: Option<GuestMemory>,
@@ -188,6 +189,7 @@ impl VirtioPciDevice {
             device_activated: false,
             interrupt_status: Arc::new(AtomicUsize::new(0)),
             interrupt_evt: None,
+            interrupt_resample_evt: None,
             queues,
             queue_evts,
             mem: Some(mem),
@@ -279,12 +281,22 @@ impl PciDevice for VirtioPciDevice {
         if let Some(ref interrupt_evt) = self.interrupt_evt {
             fds.push(interrupt_evt.as_raw_fd());
         }
+        if let Some(ref interrupt_resample_evt) = self.interrupt_resample_evt {
+            fds.push(interrupt_resample_evt.as_raw_fd());
+        }
         fds
     }
 
-    fn assign_irq(&mut self, irq_evt: EventFd, irq_num: u32, irq_pin: PciInterruptPin) {
+    fn assign_irq(
+        &mut self,
+        irq_evt: EventFd,
+        irq_resample_evt: EventFd,
+        irq_num: u32,
+        irq_pin: PciInterruptPin,
+    ) {
         self.config_regs.set_irq(irq_num as u8, irq_pin);
         self.interrupt_evt = Some(irq_evt);
+        self.interrupt_resample_evt = Some(irq_resample_evt);
     }
 
     fn allocate_io_bars(
@@ -402,15 +414,18 @@ impl PciDevice for VirtioPciDevice {
 
         if !self.device_activated && self.is_driver_ready() && self.are_queues_valid() {
             if let Some(interrupt_evt) = self.interrupt_evt.take() {
-                if let Some(mem) = self.mem.take() {
-                    self.device.activate(
-                        mem,
-                        interrupt_evt,
-                        self.interrupt_status.clone(),
-                        self.queues.clone(),
-                        self.queue_evts.split_off(0),
-                    );
-                    self.device_activated = true;
+                if let Some(interrupt_resample_evt) = self.interrupt_resample_evt.take() {
+                    if let Some(mem) = self.mem.take() {
+                        self.device.activate(
+                            mem,
+                            interrupt_evt,
+                            interrupt_resample_evt,
+                            self.interrupt_status.clone(),
+                            self.queues.clone(),
+                            self.queue_evts.split_off(0),
+                        );
+                        self.device_activated = true;
+                    }
                 }
             }
         }
diff --git a/devices/src/virtio/wl.rs b/devices/src/virtio/wl.rs
index e1a9025..1933be5 100644
--- a/devices/src/virtio/wl.rs
+++ b/devices/src/virtio/wl.rs
@@ -1352,6 +1352,7 @@ impl WlState {
 struct Worker {
     mem: GuestMemory,
     interrupt_evt: EventFd,
+    interrupt_resample_evt: EventFd,
     interrupt_status: Arc<AtomicUsize>,
     in_queue: Queue,
     out_queue: Queue,
@@ -1363,6 +1364,7 @@ impl Worker {
     fn new(
         mem: GuestMemory,
         interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
         interrupt_status: Arc<AtomicUsize>,
         in_queue: Queue,
         out_queue: Queue,
@@ -1373,6 +1375,7 @@ impl Worker {
         Worker {
             mem,
             interrupt_evt,
+            interrupt_resample_evt,
             interrupt_status,
             in_queue,
             out_queue,
@@ -1396,6 +1399,7 @@ impl Worker {
             OutQueue,
             Kill,
             State,
+            InterruptResample,
         }
 
         let poll_ctx: PollContext<Token> = match PollContext::new()
@@ -1403,7 +1407,10 @@ impl Worker {
             .and_then(|pc| pc.add(&out_queue_evt, Token::OutQueue).and(Ok(pc)))
             .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc)))
             .and_then(|pc| pc.add(&self.state.poll_ctx, Token::State).and(Ok(pc)))
-        {
+            .and_then(|pc| {
+                pc.add(&self.interrupt_resample_evt, Token::InterruptResample)
+                    .and(Ok(pc))
+            }) {
             Ok(pc) => pc,
             Err(e) => {
                 error!("failed creating PollContext: {:?}", e);
@@ -1496,6 +1503,12 @@ impl Worker {
                     }
                     Token::Kill => break 'poll,
                     Token::State => self.state.process_poll_context(),
+                    Token::InterruptResample => {
+                        let _ = self.interrupt_resample_evt.read();
+                        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
+                            self.interrupt_evt.write(1).unwrap();
+                        }
+                    }
                 }
             }
 
@@ -1602,6 +1615,7 @@ impl VirtioDevice for Wl {
         &mut self,
         mem: GuestMemory,
         interrupt_evt: EventFd,
+        interrupt_resample_evt: EventFd,
         status: Arc<AtomicUsize>,
         mut queues: Vec<Queue>,
         queue_evts: Vec<EventFd>,
@@ -1629,6 +1643,7 @@ impl VirtioDevice for Wl {
                         Worker::new(
                             mem,
                             interrupt_evt,
+                            interrupt_resample_evt,
                             status,
                             queues.remove(0),
                             queues.remove(0),