devices/virtio/vhost_user_backend/
gpu.rs1pub mod sys;
6
7use std::cell::RefCell;
8use std::rc::Rc;
9use std::sync::Arc;
10
11use anyhow::anyhow;
12use anyhow::bail;
13use anyhow::Context;
14use base::error;
15use base::warn;
16use base::Tube;
17use cros_async::EventAsync;
18use cros_async::Executor;
19use cros_async::TaskHandle;
20use futures::FutureExt;
21use futures::StreamExt;
22use snapshot::AnySnapshot;
23use sync::Mutex;
24pub use sys::run_gpu_device;
25pub use sys::Options;
26use vm_memory::GuestMemory;
27use vmm_vhost::message::VhostUserProtocolFeatures;
28use vmm_vhost::VHOST_USER_F_PROTOCOL_FEATURES;
29
30use crate::virtio::device_constants::gpu::NUM_QUEUES;
31use crate::virtio::gpu;
32use crate::virtio::gpu::QueueReader;
33use crate::virtio::vhost_user_backend::handler::Error as DeviceError;
34use crate::virtio::vhost_user_backend::handler::VhostBackendReqConnection;
35use crate::virtio::vhost_user_backend::handler::VhostUserDevice;
36use crate::virtio::vhost_user_backend::handler::WorkerState;
37use crate::virtio::DescriptorChain;
38use crate::virtio::Gpu;
39use crate::virtio::Queue;
40use crate::virtio::SharedMemoryMapper;
41use crate::virtio::SharedMemoryRegion;
42use crate::virtio::VirtioDevice;
43
44const MAX_QUEUE_NUM: usize = NUM_QUEUES;
45
46#[derive(Clone)]
47struct SharedReader {
48 queue: Arc<Mutex<Queue>>,
49}
50
51impl gpu::QueueReader for SharedReader {
52 fn pop(&self) -> Option<DescriptorChain> {
53 self.queue.lock().pop()
54 }
55
56 fn add_used(&self, desc_chain: DescriptorChain, len: u32) {
57 self.queue
58 .lock()
59 .add_used_with_bytes_written(desc_chain, len)
60 }
61
62 fn signal_used(&self) {
63 self.queue.lock().trigger_interrupt();
64 }
65}
66
67async fn run_ctrl_queue(
68 reader: SharedReader,
69 mem: GuestMemory,
70 kick_evt: EventAsync,
71 state: Rc<RefCell<gpu::Frontend>>,
72) {
73 loop {
74 if let Err(e) = kick_evt.next_val().await {
75 error!("Failed to read kick event for ctrl queue: {}", e);
76 break;
77 }
78
79 let mut state = state.borrow_mut();
80 let needs_interrupt = state.process_queue(&mem, &reader);
81
82 if needs_interrupt {
83 reader.signal_used();
84 }
85 }
86}
87
88struct GpuBackend {
89 ex: Executor,
90 gpu: Rc<RefCell<Gpu>>,
91 resource_bridges: Arc<Mutex<Vec<Tube>>>,
92 state: Option<Rc<RefCell<gpu::Frontend>>>,
93 fence_state: Arc<Mutex<gpu::FenceState>>,
94 queue_workers: [Option<WorkerState<Arc<Mutex<Queue>>, ()>>; MAX_QUEUE_NUM],
95 platform_worker_tx: futures::channel::mpsc::UnboundedSender<TaskHandle<()>>,
97 platform_worker_rx: futures::channel::mpsc::UnboundedReceiver<TaskHandle<()>>,
98 shmem_mapper: Arc<Mutex<Option<Box<dyn SharedMemoryMapper>>>>,
99 #[cfg(any(target_os = "android", target_os = "linux"))]
102 gpu_control_state: sys::linux::SharedGpuControlState,
103}
104
105impl GpuBackend {
106 fn stop_non_queue_workers(&mut self) -> anyhow::Result<()> {
107 #[cfg(any(target_os = "android", target_os = "linux"))]
110 self.gpu_control_state.borrow_mut().take();
111
112 self.ex
113 .run_until(async {
114 while let Some(Some(handle)) = self.platform_worker_rx.next().now_or_never() {
115 handle.cancel().await;
116 }
117 })
118 .context("stopping the non-queue workers for GPU")?;
119 Ok(())
120 }
121}
122
123impl VhostUserDevice for GpuBackend {
124 fn max_queue_num(&self) -> usize {
125 MAX_QUEUE_NUM
126 }
127
128 fn features(&self) -> u64 {
129 self.gpu.borrow().features() | 1 << VHOST_USER_F_PROTOCOL_FEATURES
130 }
131
132 fn ack_features(&mut self, value: u64) -> anyhow::Result<()> {
133 self.gpu.borrow_mut().ack_features(value);
134 Ok(())
135 }
136
137 fn protocol_features(&self) -> VhostUserProtocolFeatures {
138 VhostUserProtocolFeatures::CONFIG
139 | VhostUserProtocolFeatures::BACKEND_REQ
140 | VhostUserProtocolFeatures::MQ
141 | VhostUserProtocolFeatures::SHMEM
142 | VhostUserProtocolFeatures::DEVICE_STATE
143 }
144
145 fn read_config(&self, offset: u64, dst: &mut [u8]) {
146 self.gpu.borrow().read_config(offset, dst)
147 }
148
149 fn write_config(&self, offset: u64, data: &[u8]) {
150 self.gpu.borrow_mut().write_config(offset, data)
151 }
152
153 fn start_queue(&mut self, idx: usize, queue: Queue, mem: GuestMemory) -> anyhow::Result<()> {
154 if self.queue_workers[idx].is_some() {
155 warn!("Starting new queue handler without stopping old handler");
156 self.stop_queue(idx)?;
157 }
158
159 let doorbell = queue.interrupt().clone();
160
161 let queue = Arc::new(Mutex::new(queue));
167
168 let queue_task = match idx {
170 0 => {
171 let kick_evt = queue
173 .lock()
174 .event()
175 .try_clone()
176 .context("failed to clone queue event")?;
177 let kick_evt = EventAsync::new(kick_evt, &self.ex)
178 .context("failed to create EventAsync for kick_evt")?;
179 let reader = SharedReader {
180 queue: queue.clone(),
181 };
182
183 let state = if let Some(s) = self.state.as_ref() {
184 s.clone()
185 } else {
186 let fence_handler_resources =
187 Arc::new(Mutex::new(Some(gpu::FenceHandlerActivationResources {
188 mem: mem.clone(),
189 ctrl_queue: reader.clone(),
190 })));
191 let fence_handler = gpu::create_fence_handler(
192 fence_handler_resources,
193 self.fence_state.clone(),
194 );
195
196 let state = Rc::new(RefCell::new(
197 self.gpu
198 .borrow_mut()
199 .initialize_frontend(
200 self.fence_state.clone(),
201 fence_handler,
202 Arc::clone(&self.shmem_mapper),
203 )
204 .ok_or_else(|| anyhow!("failed to initialize gpu frontend"))?,
205 ));
206 self.state = Some(state.clone());
207 state
208 };
209
210 self.start_platform_workers(doorbell)?;
212
213 self.ex
215 .spawn_local(run_ctrl_queue(reader, mem, kick_evt, state))
216 }
217 1 => {
218 self.ex.spawn_local(async {})
222 }
223 _ => bail!("attempted to start unknown queue: {}", idx),
224 };
225
226 self.queue_workers[idx] = Some(WorkerState { queue_task, queue });
227 Ok(())
228 }
229
230 fn stop_queue(&mut self, idx: usize) -> anyhow::Result<Queue> {
231 if let Some(worker) = self.queue_workers.get_mut(idx).and_then(Option::take) {
232 let _ = self.ex.run_until(worker.queue_task.cancel());
234
235 if idx == 0 {
236 self.stop_non_queue_workers()?;
238
239 self.state = None;
244 }
245
246 let queue = match Arc::try_unwrap(worker.queue) {
247 Ok(queue_mutex) => queue_mutex.into_inner(),
248 Err(_) => panic!("failed to recover queue from worker"),
249 };
250
251 Ok(queue)
252 } else {
253 Err(anyhow::Error::new(DeviceError::WorkerNotFound))
254 }
255 }
256
257 fn enter_suspended_state(&mut self) -> anyhow::Result<()> {
258 self.stop_non_queue_workers()?;
259 Ok(())
260 }
261
262 fn reset(&mut self) {
263 self.stop_non_queue_workers()
264 .expect("Failed to stop platform workers.");
265
266 for queue_num in 0..self.max_queue_num() {
267 if self.queue_workers[queue_num].is_some() {
270 if let Err(e) = self.stop_queue(queue_num) {
271 error!("Failed to stop_queue during reset: {}", e);
272 }
273 }
274 }
275 }
276
277 fn get_shared_memory_region(&self) -> Option<SharedMemoryRegion> {
278 self.gpu.borrow().get_shared_memory_region()
279 }
280
281 fn set_backend_req_connection(&mut self, conn: VhostBackendReqConnection) {
282 if self
283 .shmem_mapper
284 .lock()
285 .replace(conn.shmem_mapper().unwrap())
286 .is_some()
287 {
288 warn!("Connection already established. Overwriting shmem_mapper");
289 }
290 }
291
292 fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
293 AnySnapshot::to_any(())
296 }
297
298 fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
299 let () = AnySnapshot::from_any(data)?;
300 Ok(())
301 }
302}
303
304impl Drop for GpuBackend {
305 fn drop(&mut self) {
306 self.reset();
310 }
311}