devices/virtio/console/
port.rs

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
7use std::collections::VecDeque;
8use std::sync::Arc;
9
10use anyhow::Context;
11use base::AsRawDescriptor;
12use base::Descriptor;
13use base::Event;
14use base::RawDescriptor;
15use base::WorkerThread;
16use serde::Deserialize;
17use serde::Serialize;
18use sync::Mutex;
19
20use crate::serial::sys::InStreamType;
21use crate::virtio::console::sys::spawn_input_thread;
22
23/// Each port info for multi-port virtio-console
24#[derive(Clone, Debug)]
25pub struct ConsolePortInfo {
26    pub console: bool,
27    pub name: Option<String>,
28}
29
30impl ConsolePortInfo {
31    pub fn name(&self) -> Option<&str> {
32        self.name.as_deref()
33    }
34}
35
36pub 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)]
55pub struct ConsolePortSnapshot {
56    pub(super) input_buffer: Vec<u8>,
57}
58
59impl 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}