1 // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use std::os::raw::c_ulonglong; 6 use std::sync::atomic::{AtomicUsize, Ordering}; 7 use std::sync::Arc; 8 9 use sys_util::{EventFd, PollContext, PollToken}; 10 use vhost::Vhost; 11 12 use super::{Error, Result}; 13 use crate::virtio::{Queue, INTERRUPT_STATUS_USED_RING}; 14 15 /// Worker that takes care of running the vhost device. This mainly involves forwarding interrupts 16 /// from the vhost driver to the guest VM because crosvm only supports the virtio-mmio transport, 17 /// which requires a bit to be set in the interrupt status register before triggering the interrupt 18 /// and the vhost driver doesn't do this for us. 19 pub struct Worker<T: Vhost> { 20 queues: Vec<Queue>, 21 vhost_handle: T, 22 vhost_interrupt: EventFd, 23 interrupt_status: Arc<AtomicUsize>, 24 interrupt_evt: EventFd, 25 interrupt_resample_evt: EventFd, 26 acked_features: u64, 27 } 28 29 impl<T: Vhost> Worker<T> { new( queues: Vec<Queue>, vhost_handle: T, vhost_interrupt: EventFd, interrupt_status: Arc<AtomicUsize>, interrupt_evt: EventFd, interrupt_resample_evt: EventFd, acked_features: u64, ) -> Worker<T>30 pub fn new( 31 queues: Vec<Queue>, 32 vhost_handle: T, 33 vhost_interrupt: EventFd, 34 interrupt_status: Arc<AtomicUsize>, 35 interrupt_evt: EventFd, 36 interrupt_resample_evt: EventFd, 37 acked_features: u64, 38 ) -> Worker<T> { 39 Worker { 40 queues, 41 vhost_handle, 42 vhost_interrupt, 43 interrupt_status, 44 interrupt_evt, 45 interrupt_resample_evt, 46 acked_features, 47 } 48 } 49 signal_used_queue(&self)50 fn signal_used_queue(&self) { 51 self.interrupt_status 52 .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst); 53 self.interrupt_evt.write(1).unwrap(); 54 } 55 run<F>( &mut self, queue_evts: Vec<EventFd>, queue_sizes: &[u16], kill_evt: EventFd, activate_vqs: F, ) -> Result<()> where F: FnOnce(&T) -> Result<()>,56 pub fn run<F>( 57 &mut self, 58 queue_evts: Vec<EventFd>, 59 queue_sizes: &[u16], 60 kill_evt: EventFd, 61 activate_vqs: F, 62 ) -> Result<()> 63 where 64 F: FnOnce(&T) -> Result<()>, 65 { 66 // Preliminary setup for vhost net. 67 self.vhost_handle 68 .set_owner() 69 .map_err(Error::VhostSetOwner)?; 70 71 let avail_features = self 72 .vhost_handle 73 .get_features() 74 .map_err(Error::VhostGetFeatures)?; 75 76 let features: c_ulonglong = self.acked_features & avail_features; 77 self.vhost_handle 78 .set_features(features) 79 .map_err(Error::VhostSetFeatures)?; 80 81 self.vhost_handle 82 .set_mem_table() 83 .map_err(Error::VhostSetMemTable)?; 84 85 for (queue_index, queue) in self.queues.iter().enumerate() { 86 self.vhost_handle 87 .set_vring_num(queue_index, queue.max_size) 88 .map_err(Error::VhostSetVringNum)?; 89 90 self.vhost_handle 91 .set_vring_addr( 92 queue_sizes[queue_index], 93 queue.actual_size(), 94 queue_index, 95 0, 96 queue.desc_table, 97 queue.used_ring, 98 queue.avail_ring, 99 None, 100 ) 101 .map_err(Error::VhostSetVringAddr)?; 102 self.vhost_handle 103 .set_vring_base(queue_index, 0) 104 .map_err(Error::VhostSetVringBase)?; 105 self.vhost_handle 106 .set_vring_call(queue_index, &self.vhost_interrupt) 107 .map_err(Error::VhostSetVringCall)?; 108 self.vhost_handle 109 .set_vring_kick(queue_index, &queue_evts[queue_index]) 110 .map_err(Error::VhostSetVringKick)?; 111 } 112 113 activate_vqs(&self.vhost_handle)?; 114 115 #[derive(PollToken)] 116 enum Token { 117 VhostIrq, 118 InterruptResample, 119 Kill, 120 } 121 122 let poll_ctx: PollContext<Token> = PollContext::new() 123 .and_then(|pc| pc.add(&self.vhost_interrupt, Token::VhostIrq).and(Ok(pc))) 124 .and_then(|pc| { 125 pc.add(&self.interrupt_resample_evt, Token::InterruptResample) 126 .and(Ok(pc)) 127 }) 128 .and_then(|pc| pc.add(&kill_evt, Token::Kill).and(Ok(pc))) 129 .map_err(Error::CreatePollContext)?; 130 131 'poll: loop { 132 let events = poll_ctx.wait().map_err(Error::PollError)?; 133 134 let mut needs_interrupt = false; 135 for event in events.iter_readable() { 136 match event.token() { 137 Token::VhostIrq => { 138 needs_interrupt = true; 139 self.vhost_interrupt.read().map_err(Error::VhostIrqRead)?; 140 } 141 Token::InterruptResample => { 142 let _ = self.interrupt_resample_evt.read(); 143 if self.interrupt_status.load(Ordering::SeqCst) != 0 { 144 self.interrupt_evt.write(1).unwrap(); 145 } 146 } 147 Token::Kill => break 'poll, 148 } 149 } 150 if needs_interrupt { 151 self.signal_used_queue(); 152 } 153 } 154 Ok(()) 155 } 156 } 157