Skip to main content

gpt_component/
partition.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4use crate::gpt::GptPartition;
5use anyhow::Error;
6use block_client::{ReadOptions, VmoId, WriteOptions};
7use block_server::async_interface::{PassthroughSession, SessionManager};
8use block_server::{DeviceInfo, OffsetMap};
9use fidl_fuchsia_storage_block as fblock;
10use fuchsia_async as fasync;
11
12use fuchsia_sync::Mutex;
13use std::borrow::Cow;
14use std::collections::BTreeMap;
15use std::num::NonZero;
16use std::sync::{Arc, Weak};
17
18/// A wrapper around a VmoId which keeps it active until all requests which use the Vmoid are
19/// complete.  Strong references are held by ongoing requests.
20pub struct VmoIdWrapper {
21    partition: Weak<GptPartition>,
22    vmo_id: VmoId,
23}
24
25impl std::ops::Deref for VmoIdWrapper {
26    type Target = VmoId;
27    fn deref(&self) -> &Self::Target {
28        &self.vmo_id
29    }
30}
31
32impl Drop for VmoIdWrapper {
33    fn drop(&mut self) {
34        // Turn it into an ID so that if the spawned task is dropped, the assertion in VmoId::drop
35        // doesn't fire.  It will mean the ID is leaked, but it's most likely that the server is
36        // being shut down anyway so it shouldn't matter.
37        let vmo_id = self.vmo_id.take().into_id();
38        if let Some(partition) = self.partition.upgrade() {
39            fasync::Task::spawn(async move {
40                if let Err(e) = partition.detach_vmo(VmoId::new(vmo_id)).await {
41                    log::error!("detach_vmo failed: {:?}", e);
42                }
43            })
44            .detach();
45        }
46    }
47}
48
49/// PartitionBackend is an implementation of block_server's Interface which is backed by a windowed
50/// view of the underlying GPT device.
51pub struct PartitionBackend {
52    partition: Arc<GptPartition>,
53    vmo_keys_to_vmoids_map: Mutex<BTreeMap<usize, Arc<VmoIdWrapper>>>,
54    passthrough: bool,
55}
56
57impl block_server::async_interface::Interface for PartitionBackend {
58    async fn open_session(
59        &self,
60        session_manager: Arc<SessionManager<Self>>,
61        stream: fblock::SessionRequestStream,
62        offset_map: OffsetMap,
63        block_size: u32,
64    ) -> Result<(), Error> {
65        if !self.passthrough || !offset_map.is_empty() {
66            // For now, we don't support double-passthrough.  We could as needed for nested GPT.
67            // If we support this, we can remove I/O and vmoid management from this struct.
68            return session_manager.serve_session(stream, offset_map, block_size).await;
69        }
70        let (proxy, server_end) = fidl::endpoints::create_proxy::<fblock::SessionMarker>();
71        self.partition.open_passthrough_session(server_end);
72        let passthrough = PassthroughSession::new(proxy);
73        passthrough.serve(stream).await
74    }
75
76    async fn on_attach_vmo(&self, vmo: &zx::Vmo) -> Result<(), zx::Status> {
77        let key = std::ptr::from_ref(vmo) as usize;
78        // SAFETY: GPT does not map VMOs in its own process, so it cannot violate Rust's aliasing
79        // guarantees.  Safety is delegated to the client process that mapped the VMO.
80        let vmo_id = unsafe { self.partition.attach_vmo(vmo) }.await?;
81        self.vmo_keys_to_vmoids_map.lock().insert(
82            key,
83            Arc::new(VmoIdWrapper { partition: Arc::downgrade(&self.partition), vmo_id }),
84        );
85        Ok(())
86    }
87
88    fn on_detach_vmo(&self, vmo: &zx::Vmo) {
89        // Note that we will not immediately detach the VMO.  This happens when the last reference
90        // to it is dropped (in [`VmoIdWrapper::drop`]).
91        let key = std::ptr::from_ref(vmo) as usize;
92        self.vmo_keys_to_vmoids_map.lock().remove(&key);
93    }
94
95    fn get_info(&self) -> Cow<'_, DeviceInfo> {
96        Cow::Owned(self.partition.get_info())
97    }
98
99    async fn read(
100        &self,
101        device_block_offset: u64,
102        block_count: u32,
103        vmo: &Arc<zx::Vmo>,
104        vmo_offset: u64, // *bytes* not blocks
105        opts: ReadOptions,
106        trace_flow_id: Option<NonZero<u64>>,
107    ) -> Result<(), zx::Status> {
108        let vmo_id = self.get_vmoid(vmo)?;
109        self.partition
110            .read(device_block_offset, block_count, &vmo_id, vmo_offset, opts, trace_flow_id)
111            .await
112    }
113
114    async fn write(
115        &self,
116        device_block_offset: u64,
117        length: u32,
118        vmo: &Arc<zx::Vmo>,
119        vmo_offset: u64, // *bytes* not blocks
120        opts: WriteOptions,
121        trace_flow_id: Option<NonZero<u64>>,
122    ) -> Result<(), zx::Status> {
123        let vmo_id = self.get_vmoid(vmo)?;
124        self.partition
125            .write(device_block_offset, length, &vmo_id, vmo_offset, opts, trace_flow_id)
126            .await
127    }
128
129    async fn flush(&self, trace_flow_id: Option<NonZero<u64>>) -> Result<(), zx::Status> {
130        self.partition.flush(trace_flow_id).await
131    }
132
133    async fn trim(
134        &self,
135        device_block_offset: u64,
136        block_count: u32,
137        trace_flow_id: Option<NonZero<u64>>,
138    ) -> Result<(), zx::Status> {
139        self.partition.trim(device_block_offset, block_count, trace_flow_id).await
140    }
141}
142
143impl PartitionBackend {
144    #[cfg(test)]
145    pub fn vmo_count(&self) -> usize {
146        self.vmo_keys_to_vmoids_map.lock().len()
147    }
148
149    pub fn new(partition: Arc<GptPartition>, passthrough: bool) -> Arc<Self> {
150        Arc::new(Self {
151            partition,
152            vmo_keys_to_vmoids_map: Mutex::new(BTreeMap::new()),
153            passthrough,
154        })
155    }
156
157    /// Returns the old info.
158    pub fn update_info(&self, info: gpt::PartitionInfo) -> gpt::PartitionInfo {
159        self.partition.update_info(info)
160    }
161
162    fn get_vmoid(&self, vmo: &zx::Vmo) -> Result<Arc<VmoIdWrapper>, zx::Status> {
163        let key = std::ptr::from_ref(vmo) as usize;
164        self.vmo_keys_to_vmoids_map.lock().get(&key).map(Arc::clone).ok_or(zx::Status::NOT_FOUND)
165    }
166}