• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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