Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/console/port.rs
5394 views
1
// Copyright 2024 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
//! Virtio console device per-port functionality.
6
7
use std::collections::VecDeque;
8
use std::sync::Arc;
9
10
use anyhow::Context;
11
use base::AsRawDescriptor;
12
use base::Descriptor;
13
use base::Event;
14
use base::RawDescriptor;
15
use base::WorkerThread;
16
use serde::Deserialize;
17
use serde::Serialize;
18
use sync::Mutex;
19
20
use crate::serial::sys::InStreamType;
21
use crate::virtio::console::sys::spawn_input_thread;
22
23
/// Each port info for multi-port virtio-console
24
#[derive(Clone, Debug)]
25
pub struct ConsolePortInfo {
26
pub console: bool,
27
pub name: Option<String>,
28
}
29
30
impl ConsolePortInfo {
31
pub fn name(&self) -> Option<&str> {
32
self.name.as_deref()
33
}
34
}
35
36
pub struct ConsolePort {
37
pub(crate) input: Option<InStreamType>,
38
pub(crate) output: Option<Box<dyn std::io::Write + Send>>,
39
40
info: Option<ConsolePortInfo>,
41
42
// input_buffer is shared with the input_thread while it is running.
43
input_buffer: Arc<Mutex<VecDeque<u8>>>,
44
45
// `in_avail_evt` will be signaled by the input thread to notify the worker when new input is
46
// available in `input_buffer`.
47
in_avail_evt: Event,
48
49
input_thread: Option<WorkerThread<InStreamType>>,
50
51
keep_descriptors: Vec<Descriptor>,
52
}
53
54
#[derive(Serialize, Deserialize)]
55
pub struct ConsolePortSnapshot {
56
pub(super) input_buffer: Vec<u8>,
57
}
58
59
impl ConsolePort {
60
pub fn new(
61
input: Option<InStreamType>,
62
output: Option<Box<dyn std::io::Write + Send>>,
63
info: Option<ConsolePortInfo>,
64
mut keep_rds: Vec<RawDescriptor>,
65
) -> Self {
66
let input_buffer = Arc::new(Mutex::new(VecDeque::new()));
67
let in_avail_evt = Event::new().expect("Event::new() failed");
68
keep_rds.push(in_avail_evt.as_raw_descriptor());
69
ConsolePort {
70
input,
71
output,
72
info,
73
input_buffer,
74
in_avail_evt,
75
input_thread: None,
76
keep_descriptors: keep_rds.iter().map(|rd| Descriptor(*rd)).collect(),
77
}
78
}
79
80
pub fn clone_in_avail_evt(&self) -> anyhow::Result<Event> {
81
self.in_avail_evt
82
.try_clone()
83
.context("clone_in_avail_evt failed")
84
}
85
86
pub fn clone_input_buffer(&self) -> Arc<Mutex<VecDeque<u8>>> {
87
self.input_buffer.clone()
88
}
89
90
pub fn take_output(&mut self) -> Option<Box<dyn std::io::Write + Send>> {
91
self.output.take()
92
}
93
94
pub fn restore_output(&mut self, output: Box<dyn std::io::Write + Send>) {
95
self.output = Some(output);
96
}
97
98
pub fn port_info(&self) -> Option<&ConsolePortInfo> {
99
self.info.as_ref()
100
}
101
102
pub fn start_input_thread(&mut self) {
103
// Spawn a separate thread to poll input.
104
// A thread is used because io::Read only provides a blocking interface, and there is no
105
// generic way to add an io::Read instance to a poll context (it may not be backed by a
106
// file descriptor). Moving the blocking read call to a separate thread and
107
// sending data back to the main worker thread with an event for
108
// notification bridges this gap.
109
if let Some(input) = self.input.take() {
110
assert!(self.input_thread.is_none());
111
112
let thread_in_avail_evt = self
113
.clone_in_avail_evt()
114
.expect("failed creating input available Event pair");
115
116
let thread = spawn_input_thread(input, thread_in_avail_evt, self.input_buffer.clone());
117
self.input_thread = Some(thread);
118
}
119
}
120
121
pub fn stop_input_thread(&mut self) {
122
if let Some(input_thread) = self.input_thread.take() {
123
let input = input_thread.stop();
124
self.input = Some(input);
125
}
126
}
127
128
pub fn snapshot(&mut self) -> ConsolePortSnapshot {
129
// This is only guaranteed to return a consistent state while the input thread is stopped.
130
self.stop_input_thread();
131
let input_buffer = self.input_buffer.lock().iter().copied().collect();
132
self.start_input_thread();
133
ConsolePortSnapshot { input_buffer }
134
}
135
136
pub fn restore(&mut self, snap: &ConsolePortSnapshot) {
137
self.stop_input_thread();
138
139
// Set the input buffer, discarding any currently buffered data.
140
let mut input_buffer = self.input_buffer.lock();
141
input_buffer.clear();
142
input_buffer.extend(snap.input_buffer.iter());
143
drop(input_buffer);
144
145
self.start_input_thread();
146
}
147
148
pub fn keep_rds(&self) -> Vec<RawDescriptor> {
149
self.keep_descriptors
150
.iter()
151
.map(|descr| descr.as_raw_descriptor())
152
.collect()
153
}
154
}
155
156