Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/p9.rs
5394 views
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
5
use std::collections::BTreeMap;
6
use std::io;
7
use std::io::Write;
8
use std::mem;
9
use std::result;
10
11
use anyhow::anyhow;
12
use anyhow::Context;
13
use base::error;
14
use base::warn;
15
use base::Error as SysError;
16
use base::Event;
17
use base::EventToken;
18
use base::RawDescriptor;
19
use base::WaitContext;
20
use base::WorkerThread;
21
use remain::sorted;
22
use thiserror::Error;
23
use vm_memory::GuestMemory;
24
25
use super::copy_config;
26
use super::queue::Queue;
27
use super::DeviceType;
28
use super::Interrupt;
29
use super::VirtioDevice;
30
31
const QUEUE_SIZE: u16 = 128;
32
const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
33
34
// The only virtio_9p feature.
35
const VIRTIO_9P_MOUNT_TAG: u8 = 0;
36
37
/// Errors that occur during operation of a virtio 9P device.
38
#[sorted]
39
#[derive(Error, Debug)]
40
pub 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
70
pub type P9Result<T> = result::Result<T, P9Error>;
71
72
struct Worker {
73
queue: Queue,
74
server: p9::Server,
75
}
76
77
impl 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.
122
pub 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
130
impl 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
154
impl 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
}
216
217