devices/virtio/vhost_user_backend/
block.rs

1// Copyright 2021 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
5mod sys;
6
7use anyhow::Context;
8use cros_async::Executor;
9use serde::Deserialize;
10use serde::Serialize;
11use snapshot::AnySnapshot;
12pub use sys::start_device as run_block_device;
13pub use sys::Options;
14use vm_memory::GuestMemory;
15use vmm_vhost::message::*;
16
17use crate::virtio;
18use crate::virtio::block::asynchronous::BlockAsync;
19use crate::virtio::vhost_user_backend::handler::DeviceRequestHandler;
20use crate::virtio::vhost_user_backend::handler::VhostUserDevice;
21use crate::virtio::vhost_user_backend::VhostUserDeviceBuilder;
22use crate::virtio::VirtioDevice;
23
24const NUM_QUEUES: u16 = 16;
25
26struct BlockBackend {
27    inner: Box<BlockAsync>,
28
29    avail_features: u64,
30}
31
32#[derive(Serialize, Deserialize)]
33struct BlockBackendSnapshot {
34    // `avail_features` don't need to be snapshotted, but they are
35    // to be used to make sure that the proper features are used on `restore`.
36    avail_features: u64,
37}
38
39impl VhostUserDeviceBuilder for BlockAsync {
40    fn build(self: Box<Self>, _ex: &Executor) -> anyhow::Result<Box<dyn vmm_vhost::Backend>> {
41        let avail_features = self.features() | 1 << VHOST_USER_F_PROTOCOL_FEATURES;
42        let backend = BlockBackend {
43            inner: self,
44            avail_features,
45        };
46        let handler = DeviceRequestHandler::new(backend);
47        Ok(Box::new(handler))
48    }
49}
50
51impl VhostUserDevice for BlockBackend {
52    fn max_queue_num(&self) -> usize {
53        NUM_QUEUES as usize
54    }
55
56    fn features(&self) -> u64 {
57        self.avail_features
58    }
59
60    fn protocol_features(&self) -> VhostUserProtocolFeatures {
61        VhostUserProtocolFeatures::CONFIG
62            | VhostUserProtocolFeatures::MQ
63            | VhostUserProtocolFeatures::BACKEND_REQ
64            | VhostUserProtocolFeatures::DEVICE_STATE
65    }
66
67    fn read_config(&self, offset: u64, data: &mut [u8]) {
68        self.inner.read_config(offset, data)
69    }
70
71    fn reset(&mut self) {
72        if let Err(e) = self.inner.reset() {
73            base::error!("reset failed: {:#}", e);
74        }
75    }
76
77    fn start_queue(
78        &mut self,
79        idx: usize,
80        queue: virtio::Queue,
81        mem: GuestMemory,
82    ) -> anyhow::Result<()> {
83        self.inner.start_queue(idx, queue, mem)
84    }
85
86    fn stop_queue(&mut self, idx: usize) -> anyhow::Result<virtio::Queue> {
87        self.inner.stop_queue(idx)
88    }
89
90    fn enter_suspended_state(&mut self) -> anyhow::Result<()> {
91        // TODO: This assumes that `reset` only stops workers which might not be true in the
92        // future. Consider moving the `reset` code into a `stop_all_workers` method or, maybe,
93        // make `stop_queue` implicitly stop a worker thread when there is no active queue.
94        self.inner.reset()?;
95        Ok(())
96    }
97
98    fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
99        // The queue states are being snapshotted in the device handler.
100        AnySnapshot::to_any(BlockBackendSnapshot {
101            avail_features: self.avail_features,
102        })
103        .context("Failed to serialize BlockBackendSnapshot")
104    }
105
106    fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
107        let block_backend_snapshot: BlockBackendSnapshot =
108            AnySnapshot::from_any(data).context("Failed to deserialize BlockBackendSnapshot")?;
109        anyhow::ensure!(
110            self.avail_features == block_backend_snapshot.avail_features,
111            "Vhost user block restored avail_features do not match. Live: {:?}, snapshot: {:?}",
112            self.avail_features,
113            block_backend_snapshot.avail_features,
114        );
115        Ok(())
116    }
117}