xref: /aosp_15_r20/external/crosvm/devices/src/virtio/console/port.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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 {
name(&self) -> Option<&str>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     input_buffer: Vec<u8>,
57 }
58 
59 impl ConsolePort {
new( input: Option<InStreamType>, output: Option<Box<dyn std::io::Write + Send>>, info: Option<ConsolePortInfo>, mut keep_rds: Vec<RawDescriptor>, ) -> Self60     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 
clone_in_avail_evt(&self) -> anyhow::Result<Event>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 
clone_input_buffer(&self) -> Arc<Mutex<VecDeque<u8>>>86     pub fn clone_input_buffer(&self) -> Arc<Mutex<VecDeque<u8>>> {
87         self.input_buffer.clone()
88     }
89 
take_output(&mut self) -> Option<Box<dyn std::io::Write + Send>>90     pub fn take_output(&mut self) -> Option<Box<dyn std::io::Write + Send>> {
91         self.output.take()
92     }
93 
restore_output(&mut self, output: Box<dyn std::io::Write + Send>)94     pub fn restore_output(&mut self, output: Box<dyn std::io::Write + Send>) {
95         self.output = Some(output);
96     }
97 
port_info(&self) -> Option<&ConsolePortInfo>98     pub fn port_info(&self) -> Option<&ConsolePortInfo> {
99         self.info.as_ref()
100     }
101 
start_input_thread(&mut self)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 
stop_input_thread(&mut self)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 
snapshot(&mut self) -> ConsolePortSnapshot128     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 
restore(&mut self, snap: &ConsolePortSnapshot)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 
keep_rds(&self) -> Vec<RawDescriptor>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