// Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use std::os::raw::c_ulonglong; use sys_util::{EventFd, PollContext, PollToken}; use vhost::Vhost; use super::{Error, Result}; use crate::virtio::{Interrupt, Queue}; /// Worker that takes care of running the vhost device. This mainly involves forwarding interrupts /// from the vhost driver to the guest VM because crosvm only supports the virtio-mmio transport, /// which requires a bit to be set in the interrupt status register before triggering the interrupt /// and the vhost driver doesn't do this for us. pub struct Worker { interrupt: Interrupt, queues: Vec, pub vhost_handle: T, pub vhost_interrupt: Vec, acked_features: u64, pub kill_evt: EventFd, } impl Worker { pub fn new( queues: Vec, vhost_handle: T, vhost_interrupt: Vec, interrupt: Interrupt, acked_features: u64, kill_evt: EventFd, ) -> Worker { Worker { interrupt, queues, vhost_handle, vhost_interrupt, acked_features, kill_evt, } } pub fn run( &mut self, queue_evts: Vec, queue_sizes: &[u16], activate_vqs: F1, cleanup_vqs: F2, ) -> Result<()> where F1: FnOnce(&T) -> Result<()>, F2: FnOnce(&T) -> Result<()>, { let avail_features = self .vhost_handle .get_features() .map_err(Error::VhostGetFeatures)?; let features: c_ulonglong = self.acked_features & avail_features; self.vhost_handle .set_features(features) .map_err(Error::VhostSetFeatures)?; self.vhost_handle .set_mem_table() .map_err(Error::VhostSetMemTable)?; for (queue_index, queue) in self.queues.iter().enumerate() { self.vhost_handle .set_vring_num(queue_index, queue.max_size) .map_err(Error::VhostSetVringNum)?; self.vhost_handle .set_vring_addr( queue_sizes[queue_index], queue.actual_size(), queue_index, 0, queue.desc_table, queue.used_ring, queue.avail_ring, None, ) .map_err(Error::VhostSetVringAddr)?; self.vhost_handle .set_vring_base(queue_index, 0) .map_err(Error::VhostSetVringBase)?; self.vhost_handle .set_vring_call(queue_index, &self.vhost_interrupt[queue_index]) .map_err(Error::VhostSetVringCall)?; self.vhost_handle .set_vring_kick(queue_index, &queue_evts[queue_index]) .map_err(Error::VhostSetVringKick)?; } activate_vqs(&self.vhost_handle)?; #[derive(PollToken)] enum Token { VhostIrqi { index: usize }, InterruptResample, Kill, } let poll_ctx: PollContext = PollContext::build_with(&[ (self.interrupt.get_resample_evt(), Token::InterruptResample), (&self.kill_evt, Token::Kill), ]) .map_err(Error::CreatePollContext)?; for (index, vhost_int) in self.vhost_interrupt.iter().enumerate() { poll_ctx .add(vhost_int, Token::VhostIrqi { index }) .map_err(Error::CreatePollContext)?; } 'poll: loop { let events = poll_ctx.wait().map_err(Error::PollError)?; for event in events.iter_readable() { match event.token() { Token::VhostIrqi { index } => { self.vhost_interrupt[index] .read() .map_err(Error::VhostIrqRead)?; self.interrupt.signal_used_queue(self.queues[index].vector); } Token::InterruptResample => { self.interrupt.interrupt_resample(); } Token::Kill => { let _ = self.kill_evt.read(); break 'poll; } } } } cleanup_vqs(&self.vhost_handle)?; Ok(()) } }