xref: /aosp_15_r20/external/crosvm/devices/src/virtio/console/sys/linux.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 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::VecDeque;
6 use std::io;
7 use std::sync::Arc;
8 use std::time::Duration;
9 use std::time::Instant;
10 
11 use anyhow::Context;
12 use base::error;
13 use base::Event;
14 use base::EventToken;
15 use base::FileSync;
16 use base::RawDescriptor;
17 use base::WaitContext;
18 use base::WorkerThread;
19 use sync::Mutex;
20 
21 use crate::serial::sys::InStreamType;
22 use crate::serial_device::SerialInput;
23 use crate::serial_device::SerialOptions;
24 use crate::virtio::console::device::ConsoleDevice;
25 use crate::virtio::console::port::ConsolePort;
26 use crate::virtio::console::port::ConsolePortInfo;
27 use crate::virtio::console::Console;
28 use crate::virtio::ProtectionType;
29 use crate::SerialDevice;
30 
31 impl SerialDevice for Console {
new( protection_type: ProtectionType, _event: Event, input: Option<Box<dyn SerialInput>>, output: Option<Box<dyn io::Write + Send>>, _sync: Option<Box<dyn FileSync + Send>>, options: SerialOptions, keep_rds: Vec<RawDescriptor>, ) -> Console32     fn new(
33         protection_type: ProtectionType,
34         _event: Event,
35         input: Option<Box<dyn SerialInput>>,
36         output: Option<Box<dyn io::Write + Send>>,
37         // TODO(b/171331752): connect filesync functionality.
38         _sync: Option<Box<dyn FileSync + Send>>,
39         options: SerialOptions,
40         keep_rds: Vec<RawDescriptor>,
41     ) -> Console {
42         Console::new(
43             protection_type,
44             input,
45             output,
46             keep_rds,
47             options.pci_address,
48         )
49     }
50 }
51 
52 impl SerialDevice for ConsoleDevice {
new( protection_type: ProtectionType, _event: Event, input: Option<Box<dyn SerialInput>>, output: Option<Box<dyn io::Write + Send>>, _sync: Option<Box<dyn FileSync + Send>>, options: SerialOptions, keep_rds: Vec<RawDescriptor>, ) -> ConsoleDevice53     fn new(
54         protection_type: ProtectionType,
55         _event: Event,
56         input: Option<Box<dyn SerialInput>>,
57         output: Option<Box<dyn io::Write + Send>>,
58         _sync: Option<Box<dyn FileSync + Send>>,
59         options: SerialOptions,
60         keep_rds: Vec<RawDescriptor>,
61     ) -> ConsoleDevice {
62         let info = ConsolePortInfo {
63             name: options.name,
64             console: options.console,
65         };
66         let port = ConsolePort::new(input, output, Some(info), keep_rds);
67         ConsoleDevice::new_single_port(protection_type, port)
68     }
69 }
70 
71 impl SerialDevice for ConsolePort {
new( _protection_type: ProtectionType, _event: Event, input: Option<Box<dyn SerialInput>>, output: Option<Box<dyn io::Write + Send>>, _sync: Option<Box<dyn FileSync + Send>>, options: SerialOptions, keep_rds: Vec<RawDescriptor>, ) -> ConsolePort72     fn new(
73         _protection_type: ProtectionType,
74         _event: Event,
75         input: Option<Box<dyn SerialInput>>,
76         output: Option<Box<dyn io::Write + Send>>,
77         // TODO(b/171331752): connect filesync functionality.
78         _sync: Option<Box<dyn FileSync + Send>>,
79         options: SerialOptions,
80         keep_rds: Vec<RawDescriptor>,
81     ) -> ConsolePort {
82         let info = ConsolePortInfo {
83             name: options.name,
84             console: options.console,
85         };
86         ConsolePort::new(input, output, Some(info), keep_rds)
87     }
88 }
89 
90 /// Starts a thread that reads input and sends the input back via the provided buffer.
91 ///
92 /// The caller should listen on `in_avail_evt` for events. When `in_avail_evt` signals that data
93 /// is available, the caller should lock `input_buffer` and read data out of the inner
94 /// `VecDeque`. The data should be removed from the beginning of the `VecDeque` as it is processed.
95 ///
96 /// # Arguments
97 ///
98 /// * `input` - Data source that the reader thread will wait on to send data back to the buffer
99 /// * `in_avail_evt` - Event triggered by the thread when new input is available on the buffer
spawn_input_thread( mut input: InStreamType, in_avail_evt: Event, input_buffer: Arc<Mutex<VecDeque<u8>>>, ) -> WorkerThread<InStreamType>100 pub(in crate::virtio::console) fn spawn_input_thread(
101     mut input: InStreamType,
102     in_avail_evt: Event,
103     input_buffer: Arc<Mutex<VecDeque<u8>>>,
104 ) -> WorkerThread<InStreamType> {
105     WorkerThread::start("v_console_input", move |kill_evt| {
106         // If there is already data, signal immediately.
107         if !input_buffer.lock().is_empty() {
108             in_avail_evt.signal().unwrap();
109         }
110         if let Err(e) = read_input(&mut input, &in_avail_evt, input_buffer, kill_evt) {
111             error!("console input thread exited with error: {:#}", e);
112         }
113         input
114     })
115 }
116 
read_input( input: &mut InStreamType, thread_in_avail_evt: &Event, buffer: Arc<Mutex<VecDeque<u8>>>, kill_evt: Event, ) -> anyhow::Result<()>117 fn read_input(
118     input: &mut InStreamType,
119     thread_in_avail_evt: &Event,
120     buffer: Arc<Mutex<VecDeque<u8>>>,
121     kill_evt: Event,
122 ) -> anyhow::Result<()> {
123     #[derive(EventToken)]
124     enum Token {
125         ConsoleEvent,
126         Kill,
127     }
128 
129     let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
130         (&kill_evt, Token::Kill),
131         (input.get_read_notifier(), Token::ConsoleEvent),
132     ])
133     .context("failed creating WaitContext")?;
134 
135     let mut kill_timeout = None;
136     let mut rx_buf = [0u8; 1 << 12];
137     'wait: loop {
138         let events = wait_ctx.wait().context("Failed to wait for events")?;
139         for event in events.iter() {
140             match event.token {
141                 Token::Kill => {
142                     // Ignore the kill event until there are no other events to process so that
143                     // we drain `input` as much as possible. The next `wait_ctx.wait()` call will
144                     // immediately re-entry this case since we don't call `kill_evt.wait()`.
145                     if events.iter().all(|e| matches!(e.token, Token::Kill)) {
146                         break 'wait;
147                     }
148                     const TIMEOUT_DURATION: Duration = Duration::from_millis(500);
149                     match kill_timeout {
150                         None => {
151                             kill_timeout = Some(Instant::now() + TIMEOUT_DURATION);
152                         }
153                         Some(t) => {
154                             if Instant::now() >= t {
155                                 error!(
156                                     "failed to drain console input within {:?}, giving up",
157                                     TIMEOUT_DURATION
158                                 );
159                                 break 'wait;
160                             }
161                         }
162                     }
163                 }
164                 Token::ConsoleEvent => {
165                     match input.read(&mut rx_buf) {
166                         Ok(0) => break 'wait, // Assume the stream of input has ended.
167                         Ok(size) => {
168                             buffer.lock().extend(&rx_buf[0..size]);
169                             thread_in_avail_evt.signal().unwrap();
170                         }
171                         // Being interrupted is not an error, but everything else is.
172                         Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
173                         Err(e) => {
174                             return Err(e).context("failed to read console input");
175                         }
176                     }
177                 }
178             }
179         }
180     }
181 
182     Ok(())
183 }
184