devices/virtio/
p9.rs

1// Copyright 2018 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::collections::BTreeMap;
6use std::io;
7use std::io::Write;
8use std::mem;
9use std::result;
10
11use anyhow::anyhow;
12use anyhow::Context;
13use base::error;
14use base::warn;
15use base::Error as SysError;
16use base::Event;
17use base::EventToken;
18use base::RawDescriptor;
19use base::WaitContext;
20use base::WorkerThread;
21use remain::sorted;
22use thiserror::Error;
23use vm_memory::GuestMemory;
24
25use super::copy_config;
26use super::queue::Queue;
27use super::DeviceType;
28use super::Interrupt;
29use super::VirtioDevice;
30
31const QUEUE_SIZE: u16 = 128;
32const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
33
34// The only virtio_9p feature.
35const VIRTIO_9P_MOUNT_TAG: u8 = 0;
36
37/// Errors that occur during operation of a virtio 9P device.
38#[sorted]
39#[derive(Error, Debug)]
40pub enum P9Error {
41    /// Failed to create a 9p server.
42    #[error("failed to create 9p server: {0}")]
43    CreateServer(io::Error),
44    /// Creating WaitContext failed.
45    #[error("failed to create WaitContext: {0}")]
46    CreateWaitContext(SysError),
47    /// An internal I/O error occurred.
48    #[error("P9 internal server error: {0}")]
49    Internal(io::Error),
50    /// A request is missing readable descriptors.
51    #[error("request does not have any readable descriptors")]
52    NoReadableDescriptors,
53    /// A request is missing writable descriptors.
54    #[error("request does not have any writable descriptors")]
55    NoWritableDescriptors,
56    /// Error while reading from the virtio queue's Event.
57    #[error("failed to read from virtio queue Event: {0}")]
58    ReadQueueEvent(SysError),
59    /// Failed to signal the virio used queue.
60    #[error("failed to signal used queue: {0}")]
61    SignalUsedQueue(SysError),
62    /// The tag for the 9P device was too large to fit in the config space.
63    #[error("P9 device tag is too long: len = {0}, max = {max}", max = u16::MAX)]
64    TagTooLong(usize),
65    /// Error while polling for events.
66    #[error("failed to wait for events: {0}")]
67    WaitError(SysError),
68}
69
70pub type P9Result<T> = result::Result<T, P9Error>;
71
72struct Worker {
73    queue: Queue,
74    server: p9::Server,
75}
76
77impl Worker {
78    fn process_queue(&mut self) -> P9Result<()> {
79        while let Some(mut avail_desc) = self.queue.pop() {
80            self.server
81                .handle_message(&mut avail_desc.reader, &mut avail_desc.writer)
82                .map_err(P9Error::Internal)?;
83
84            self.queue.add_used(avail_desc);
85        }
86        self.queue.trigger_interrupt();
87
88        Ok(())
89    }
90
91    fn run(&mut self, kill_evt: Event) -> P9Result<()> {
92        #[derive(EventToken)]
93        enum Token {
94            // A request is ready on the queue.
95            QueueReady,
96            // The parent thread requested an exit.
97            Kill,
98        }
99
100        let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
101            (self.queue.event(), Token::QueueReady),
102            (&kill_evt, Token::Kill),
103        ])
104        .map_err(P9Error::CreateWaitContext)?;
105
106        loop {
107            let events = wait_ctx.wait().map_err(P9Error::WaitError)?;
108            for event in events.iter().filter(|e| e.is_readable) {
109                match event.token {
110                    Token::QueueReady => {
111                        self.queue.event().wait().map_err(P9Error::ReadQueueEvent)?;
112                        self.process_queue()?;
113                    }
114                    Token::Kill => return Ok(()),
115                }
116            }
117        }
118    }
119}
120
121/// Virtio device for sharing specific directories on the host system with the guest VM.
122pub struct P9 {
123    config: Vec<u8>,
124    server: Option<p9::Server>,
125    avail_features: u64,
126    acked_features: u64,
127    worker: Option<WorkerThread<()>>,
128}
129
130impl P9 {
131    pub fn new(base_features: u64, tag: &str, p9_cfg: p9::Config) -> P9Result<P9> {
132        if tag.len() > u16::MAX as usize {
133            return Err(P9Error::TagTooLong(tag.len()));
134        }
135
136        let len = tag.len() as u16;
137        let mut cfg = Vec::with_capacity(tag.len() + mem::size_of::<u16>());
138        cfg.push(len as u8);
139        cfg.push((len >> 8) as u8);
140
141        cfg.write_all(tag.as_bytes()).map_err(P9Error::Internal)?;
142
143        let server = p9::Server::with_config(p9_cfg).map_err(P9Error::CreateServer)?;
144        Ok(P9 {
145            config: cfg,
146            server: Some(server),
147            avail_features: base_features | 1 << VIRTIO_9P_MOUNT_TAG,
148            acked_features: 0,
149            worker: None,
150        })
151    }
152}
153
154impl VirtioDevice for P9 {
155    fn keep_rds(&self) -> Vec<RawDescriptor> {
156        self.server
157            .as_ref()
158            .map(p9::Server::keep_fds)
159            .unwrap_or_default()
160    }
161
162    fn device_type(&self) -> DeviceType {
163        DeviceType::P9
164    }
165
166    fn queue_max_sizes(&self) -> &[u16] {
167        QUEUE_SIZES
168    }
169
170    fn features(&self) -> u64 {
171        self.avail_features
172    }
173
174    fn ack_features(&mut self, value: u64) {
175        let mut v = value;
176
177        // Check if the guest is ACK'ing a feature that we didn't claim to have.
178        let unrequested_features = v & !self.avail_features;
179        if unrequested_features != 0 {
180            warn!("virtio_9p got unknown feature ack: {:x}", v);
181
182            // Don't count these features as acked.
183            v &= !unrequested_features;
184        }
185        self.acked_features |= v;
186    }
187
188    fn read_config(&self, offset: u64, data: &mut [u8]) {
189        copy_config(data, 0, self.config.as_slice(), offset);
190    }
191
192    fn activate(
193        &mut self,
194        _guest_mem: GuestMemory,
195        _interrupt: Interrupt,
196        mut queues: BTreeMap<usize, Queue>,
197    ) -> anyhow::Result<()> {
198        if queues.len() != 1 {
199            return Err(anyhow!("expected 1 queue, got {}", queues.len()));
200        }
201
202        let queue = queues.remove(&0).unwrap();
203
204        let server = self.server.take().context("missing server")?;
205
206        self.worker = Some(WorkerThread::start("v_9p", move |kill_evt| {
207            let mut worker = Worker { queue, server };
208            if let Err(e) = worker.run(kill_evt) {
209                error!("p9 worker failed: {e:#}");
210            }
211        }));
212
213        Ok(())
214    }
215}