Skip to content

Commit 4373a0c

Browse files
committed
virtio-console: Rewrite input/output handling to use threads
Signed-off-by: Matej Hrica <[email protected]>
1 parent a9f1731 commit 4373a0c

File tree

15 files changed

+624
-286
lines changed

15 files changed

+624
-286
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/devices/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ crossbeam-channel = "0.5"
1515
env_logger = "0.9.0"
1616
libc = ">=0.2.39"
1717
log = "0.4.0"
18-
nix = "0.24.1"
18+
nix = { version = "0.24.1", features = ["poll"] }
1919
rand = "0.8.5"
2020
vm-memory = { version = ">=0.13", features = ["backend-mmap"] }
2121

src/devices/src/virtio/console/console_control.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use utils::eventfd::EventFd;
77
use vm_memory::{ByteValued, GuestMemoryMmap};
88

99
use crate::virtio::console::defs::control_event::{
10-
VIRTIO_CONSOLE_CONSOLE_PORT, VIRTIO_CONSOLE_PORT_ADD, VIRTIO_CONSOLE_RESIZE,
10+
VIRTIO_CONSOLE_CONSOLE_PORT, VIRTIO_CONSOLE_PORT_ADD, VIRTIO_CONSOLE_PORT_OPEN,
11+
VIRTIO_CONSOLE_RESIZE,
1112
};
1213

1314
#[derive(Copy, Clone, Debug, Default)]
@@ -99,6 +100,14 @@ impl ConsoleControl {
99100
})
100101
}
101102

103+
pub fn port_open(&self, port_id: u32, open: bool) {
104+
self.push_msg(VirtioConsoleControl {
105+
id: port_id,
106+
event: VIRTIO_CONSOLE_PORT_OPEN,
107+
value: open as u16,
108+
})
109+
}
110+
102111
pub fn queue_pop(&self) -> Option<Payload> {
103112
let mut queue = self.queue.lock().expect("Poisoned lock");
104113
queue.pop_front()

src/devices/src/virtio/console/device.rs

Lines changed: 47 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,29 @@
11
use std::cmp;
2-
use std::collections::VecDeque;
3-
use std::io;
42
use std::io::Write;
53
use std::mem::{size_of, size_of_val};
64
use std::os::unix::io::{AsRawFd, RawFd};
7-
use std::result;
8-
use std::sync::atomic::{AtomicUsize, Ordering};
5+
use std::sync::atomic::AtomicUsize;
96
use std::sync::{Arc, Mutex};
107

118
use libc::TIOCGWINSZ;
129
use utils::eventfd::EventFd;
13-
use vm_memory::{ByteValued, Bytes, GuestMemory, GuestMemoryMmap};
10+
use vm_memory::{ByteValued, Bytes, GuestMemoryMmap};
1411

15-
use super::super::super::legacy::ReadableFd;
1612
use super::super::{
1713
ActivateError, ActivateResult, ConsoleError, DeviceState, Queue as VirtQueue, VirtioDevice,
18-
VIRTIO_MMIO_INT_CONFIG, VIRTIO_MMIO_INT_VRING,
1914
};
2015
use super::{defs, defs::control_event, defs::uapi};
2116
use crate::legacy::Gic;
2217
use crate::virtio::console::console_control::{
2318
ConsoleControl, VirtioConsoleControl, VirtioConsoleResize,
2419
};
2520
use crate::virtio::console::defs::NUM_PORTS;
26-
use crate::Error as DeviceError;
21+
use crate::virtio::console::irq_signaler::IRQSignaler;
22+
use crate::virtio::console::port::Port;
23+
use crate::virtio::PortDescription;
2724

2825
pub(crate) const RXQ_INDEX: usize = 0;
2926
pub(crate) const TXQ_INDEX: usize = 1;
30-
3127
pub(crate) const CONTROL_RXQ_INDEX: usize = 2;
3228
pub(crate) const CONTROL_TXQ_INDEX: usize = 3;
3329

@@ -66,131 +62,79 @@ pub struct VirtioConsoleConfig {
6662
unsafe impl ByteValued for VirtioConsoleConfig {}
6763

6864
impl VirtioConsoleConfig {
69-
pub fn new(cols: u16, rows: u16) -> Self {
65+
pub fn new(cols: u16, rows: u16, max_nr_ports: u32) -> Self {
7066
VirtioConsoleConfig {
7167
cols,
7268
rows,
73-
max_nr_ports: NUM_PORTS as u32,
69+
max_nr_ports,
7470
emerg_wr: 0u32,
7571
}
7672
}
7773
}
7874

7975
pub struct Console {
76+
pub(crate) device_state: DeviceState,
77+
pub(crate) irq: IRQSignaler,
78+
pub(crate) control: Arc<ConsoleControl>,
79+
pub(crate) port: Port,
80+
8081
pub(crate) queues: Vec<VirtQueue>,
8182
pub(crate) queue_events: Vec<EventFd>,
82-
pub(crate) control: Arc<ConsoleControl>,
83+
8384
pub(crate) avail_features: u64,
8485
pub(crate) acked_features: u64,
85-
pub(crate) interrupt_status: Arc<AtomicUsize>,
86-
pub(crate) interrupt_evt: EventFd,
86+
8787
pub(crate) activate_evt: EventFd,
8888
pub(crate) sigwinch_evt: EventFd,
89-
pub(crate) device_state: DeviceState,
90-
pub(crate) in_buffer: VecDeque<u8>,
89+
9190
config: VirtioConsoleConfig,
92-
pub(crate) input: Box<dyn ReadableFd + Send>,
93-
output: Box<dyn io::Write + Send>,
94-
configured: bool,
95-
pub(crate) interactive: bool,
96-
intc: Option<Arc<Mutex<Gic>>>,
97-
irq_line: Option<u32>,
9891
}
9992

10093
impl Console {
101-
pub(crate) fn with_queues(
102-
input: Box<dyn ReadableFd + Send>,
103-
output: Box<dyn io::Write + Send>,
104-
queues: Vec<VirtQueue>,
105-
) -> super::Result<Console> {
94+
pub fn new(port: PortDescription) -> super::Result<Console> {
95+
let queues: Vec<VirtQueue> = defs::QUEUE_SIZES
96+
.iter()
97+
.map(|&max_size| VirtQueue::new(max_size))
98+
.collect();
99+
106100
let mut queue_events = Vec::new();
107101
for _ in 0..queues.len() {
108102
queue_events
109103
.push(EventFd::new(utils::eventfd::EFD_NONBLOCK).map_err(ConsoleError::EventFd)?);
110104
}
111105

112106
let (cols, rows) = get_win_size();
113-
let config = VirtioConsoleConfig::new(cols, rows);
107+
let config = VirtioConsoleConfig::new(cols, rows, NUM_PORTS as u32);
114108

115109
Ok(Console {
110+
irq: IRQSignaler::new(),
116111
control: ConsoleControl::new(),
112+
port: Port::new(0, port),
117113
queues,
118114
queue_events,
119115
avail_features: AVAIL_FEATURES,
120116
acked_features: 0,
121-
interrupt_status: Arc::new(AtomicUsize::new(0)),
122-
interrupt_evt: EventFd::new(utils::eventfd::EFD_NONBLOCK)
123-
.map_err(ConsoleError::EventFd)?,
124117
activate_evt: EventFd::new(utils::eventfd::EFD_NONBLOCK)
125118
.map_err(ConsoleError::EventFd)?,
126119
sigwinch_evt: EventFd::new(utils::eventfd::EFD_NONBLOCK)
127120
.map_err(ConsoleError::EventFd)?,
128121
device_state: DeviceState::Inactive,
129-
in_buffer: VecDeque::new(),
130122
config,
131-
input,
132-
output,
133-
configured: false,
134-
interactive: true,
135-
intc: None,
136-
irq_line: None,
137123
})
138124
}
139125

140-
pub fn new(
141-
input: Box<dyn ReadableFd + Send>,
142-
output: Box<dyn io::Write + Send>,
143-
) -> super::Result<Console> {
144-
let queues: Vec<VirtQueue> = defs::QUEUE_SIZES
145-
.iter()
146-
.map(|&max_size| VirtQueue::new(max_size))
147-
.collect();
148-
Self::with_queues(input, output, queues)
149-
}
150-
151126
pub fn id(&self) -> &str {
152127
defs::CONSOLE_DEV_ID
153128
}
154129

155130
pub fn set_intc(&mut self, intc: Arc<Mutex<Gic>>) {
156-
self.intc = Some(intc);
131+
self.irq.set_intc(intc)
157132
}
158133

159134
pub fn get_sigwinch_fd(&self) -> RawFd {
160135
self.sigwinch_evt.as_raw_fd()
161136
}
162137

163-
pub fn set_interactive(&mut self, interactive: bool) {
164-
self.interactive = interactive;
165-
}
166-
167-
/// Signal the guest driver that we've used some virtio buffers that it had previously made
168-
/// available.
169-
pub fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
170-
debug!("console: raising IRQ");
171-
self.interrupt_status
172-
.fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst);
173-
if let Some(intc) = &self.intc {
174-
intc.lock().unwrap().set_irq(self.irq_line.unwrap());
175-
Ok(())
176-
} else {
177-
self.interrupt_evt.write(1).map_err(|e| {
178-
error!("Failed to signal used queue: {:?}", e);
179-
DeviceError::FailedSignalingUsedQueue(e)
180-
})
181-
}
182-
}
183-
184-
pub fn signal_config_update(&self) -> result::Result<(), DeviceError> {
185-
debug!("console: raising IRQ for config update");
186-
self.interrupt_status
187-
.fetch_or(VIRTIO_MMIO_INT_CONFIG as usize, Ordering::SeqCst);
188-
self.interrupt_evt.write(1).map_err(|e| {
189-
error!("Failed to signal used queue: {:?}", e);
190-
DeviceError::FailedSignalingUsedQueue(e)
191-
})
192-
}
193-
194138
pub fn update_console_size(&mut self, cols: u16, rows: u16) {
195139
log::debug!("update_console_size: {} {}", cols, rows);
196140
// Note that we currently only support resizing on the first/main console
@@ -229,15 +173,16 @@ impl Console {
229173
}
230174

231175
pub(crate) fn process_control_tx(&mut self) -> bool {
232-
let mem = match self.device_state {
233-
DeviceState::Activated(ref mem) => mem,
234-
// This should never happen, it's been already validated in the event handler.
235-
DeviceState::Inactive => unreachable!(),
176+
log::trace!("process_control_tx");
177+
let DeviceState::Activated(ref mem) = self.device_state else {
178+
unreachable!()
236179
};
237180

238181
let tx_queue = &mut self.queues[CONTROL_TXQ_INDEX];
239182
let mut raise_irq = false;
240183

184+
let mut start_port = false;
185+
241186
while let Some(head) = tx_queue.pop(mem) {
242187
raise_irq = true;
243188

@@ -268,9 +213,7 @@ impl Console {
268213
log::error!("Port initialization failed: {:?}", cmd);
269214
continue;
270215
}
271-
if cmd.id == 0 {
272-
self.control.mark_console_port(mem, 0);
273-
}
216+
self.control.mark_console_port(mem, cmd.id);
274217
}
275218
control_event::VIRTIO_CONSOLE_PORT_OPEN => {
276219
let opened = match cmd.value {
@@ -290,76 +233,25 @@ impl Console {
290233
log::debug!("Guest closed port {}", cmd.id);
291234
continue;
292235
}
236+
237+
start_port = true;
293238
}
294239
_ => log::warn!("Unknown console control event {:x}", cmd.event),
295240
}
296241
}
297242

298-
raise_irq
299-
}
300-
301-
pub(crate) fn process_rx(&mut self) -> bool {
302-
//debug!("console: RXQ queue event");
303-
let mem = match self.device_state {
304-
DeviceState::Activated(ref mem) => mem,
305-
// This should never happen, it's been already validated in the event handler.
306-
DeviceState::Inactive => unreachable!(),
307-
};
308-
309-
if self.in_buffer.is_empty() {
310-
return false;
311-
}
312-
313-
let queue = &mut self.queues[RXQ_INDEX];
314-
let mut used_any = false;
315-
while let Some(head) = queue.pop(mem) {
316-
let len = cmp::min(head.len, self.in_buffer.len() as u32);
317-
let source_slice = self.in_buffer.drain(..len as usize).collect::<Vec<u8>>();
318-
if let Err(e) = mem.write_slice(&source_slice[..], head.addr) {
319-
error!("Failed to write slice: {:?}", e);
320-
queue.go_to_previous_position();
321-
break;
322-
}
323-
324-
queue.add_used(mem, head.index, len);
325-
used_any = true;
326-
327-
if self.in_buffer.is_empty() {
328-
break;
329-
}
330-
}
331-
332-
used_any
333-
}
334-
335-
pub(crate) fn process_tx(&mut self) -> bool {
336-
//debug!("console: TXQ queue event");
337-
let mem = match self.device_state {
338-
DeviceState::Activated(ref mem) => mem,
339-
// This should never happen, it's been already validated in the event handler.
340-
DeviceState::Inactive => unreachable!(),
341-
};
342-
343-
// This won't be needed once we support multiport
344-
if !self.configured {
345-
self.configured = true;
346-
self.signal_config_update().unwrap();
347-
}
348-
349-
let queue = &mut self.queues[TXQ_INDEX];
350-
let mut used_any = false;
351-
while let Some(head) = queue.pop(mem) {
352-
let mut buf = vec![0; head.len as usize];
353-
mem.write_volatile_to(head.addr, &mut buf, head.len as usize)
354-
.unwrap();
355-
self.output.write_all(&buf).unwrap();
356-
self.output.flush().unwrap();
357-
358-
queue.add_used(mem, head.index, head.len);
359-
used_any = true;
243+
if start_port {
244+
log::trace!("Starting port");
245+
self.port.start(
246+
mem.clone(),
247+
self.queues[RXQ_INDEX].clone(),
248+
self.queues[TXQ_INDEX].clone(),
249+
self.irq.clone(),
250+
self.control.clone(),
251+
);
360252
}
361253

362-
used_any
254+
raise_irq
363255
}
364256
}
365257

@@ -393,15 +285,15 @@ impl VirtioDevice for Console {
393285
}
394286

395287
fn interrupt_evt(&self) -> &EventFd {
396-
&self.interrupt_evt
288+
self.irq.interrupt_evt()
397289
}
398290

399291
fn interrupt_status(&self) -> Arc<AtomicUsize> {
400-
self.interrupt_status.clone()
292+
self.irq.interrupt_status()
401293
}
402294

403295
fn set_irq_line(&mut self, irq: u32) {
404-
self.irq_line = Some(irq);
296+
self.irq.set_irq_line(irq)
405297
}
406298

407299
fn read_config(&self, offset: u64, mut data: &mut [u8]) {
@@ -427,17 +319,8 @@ impl VirtioDevice for Console {
427319
}
428320

429321
fn activate(&mut self, mem: GuestMemoryMmap) -> ActivateResult {
430-
if self.queues.len() != defs::NUM_QUEUES {
431-
error!(
432-
"Cannot perform activate. Expected {} queue(s), got {}",
433-
defs::NUM_QUEUES,
434-
self.queues.len()
435-
);
436-
return Err(ActivateError::BadActivate);
437-
}
438-
439322
if self.activate_evt.write(1).is_err() {
440-
error!("Cannot write to activate_evt",);
323+
error!("Cannot write to activate_evt");
441324
return Err(ActivateError::BadActivate);
442325
}
443326

0 commit comments

Comments
 (0)