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