Skip to main content

fxfs_platform/fuchsia/
layer_pager.rs

1// Copyright 2026 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.
4
5use anyhow::{Context, Error};
6use async_trait::async_trait;
7use fidl_fuchsia_storage_block as fblock;
8use fuchsia_async as fasync;
9use futures::lock::Mutex as AsyncMutex;
10use fxfs::filesystem::LayerPager;
11use fxfs::object_handle::{LayerObject, ObjectHandle, ReadObjectHandle};
12use fxfs::object_store::{DataObjectHandle, FileExtent, ObjectStore};
13use fxfs_crypto::UnwrappedKey;
14use mapping::{
15    Extent, Extents, MAPPING_VMO_SIZE, MappingCommand, PENDING_COMMANDS_CAPACITY, RawMappingCommand,
16};
17use std::sync::Arc;
18use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
19use storage_device::buffer::{BufferFuture, MutableBufferRef};
20use storage_units::BlockSize;
21use vmo_fifo::AsyncSender;
22use zx;
23
24struct MappedVmo {
25    vmo: zx::Vmo,
26    vaddr: usize,
27    len: usize,
28}
29
30impl MappedVmo {
31    fn new(vmo: zx::Vmo, len: usize) -> Result<Self, zx::Status> {
32        let page_size = zx::system_get_page_size() as usize;
33        let aligned_len = len.next_multiple_of(page_size);
34        let vaddr = if aligned_len > 0 {
35            fuchsia_runtime::vmar_root_self().map(
36                0,
37                &vmo,
38                0,
39                aligned_len,
40                zx::VmarFlags::PERM_READ,
41            )?
42        } else {
43            0
44        };
45        Ok(Self { vmo, vaddr, len })
46    }
47
48    fn as_slice(&self) -> &[u8] {
49        if self.vaddr == 0 || self.len == 0 {
50            &[]
51        } else {
52            // SAFETY: self.vaddr is mapped for at least self.len bytes with PERM_READ.  Note:
53            // This creates a `&[u8]` slice over memory backed by a pager-supplied VMO.  In
54            // theory, the driver could write to it which is undefined behavior in Rust, but the
55            // driver only supplies pages once (immutable) and this trade-off is acceptable for
56            // now (we could fix this with kernel changes).
57            unsafe { std::slice::from_raw_parts(self.vaddr as *const u8, self.len) }
58        }
59    }
60
61    fn has_io_error(&self) -> bool {
62        self.vmo
63            .wait_one(zx::Signals::USER_0, zx::MonotonicInstant::INFINITE_PAST)
64            .to_result()
65            .is_ok()
66    }
67
68    fn purge_cached_data(&self) {
69        if self.len > 0 {
70            // `DONT_NEED` doesn't actually purge the data immediately; it only serves as a hint
71            // to the kernel, which is the best we can do here.
72            let _ = self.vmo.op_range(zx::VmoOp::DONT_NEED, 0, self.len as u64);
73        }
74    }
75}
76
77impl Drop for MappedVmo {
78    fn drop(&mut self) {
79        if self.vaddr != 0 {
80            let page_size = zx::system_get_page_size() as usize;
81            let aligned_len = self.len.next_multiple_of(page_size);
82            // SAFETY: self.vaddr was mapped by vmar_root_self().map with aligned_len.
83            unsafe {
84                let _ = fuchsia_runtime::vmar_root_self().unmap(self.vaddr, aligned_len);
85            }
86        }
87    }
88}
89
90struct PagedLayerObject {
91    handle: DataObjectHandle<ObjectStore>,
92    mapped: MappedVmo,
93    sender: Arc<AsyncMutex<AsyncSender<RawMappingCommand>>>,
94    scope: fasync::ScopeHandle,
95    key: u64,
96    closed: AtomicBool,
97}
98
99impl ObjectHandle for PagedLayerObject {
100    fn object_id(&self) -> u64 {
101        self.handle.object_id()
102    }
103
104    fn block_size(&self) -> BlockSize {
105        self.handle.block_size()
106    }
107
108    fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
109        self.handle.allocate_buffer(size)
110    }
111
112    fn set_trace(&self, v: bool) {
113        self.handle.set_trace(v)
114    }
115}
116
117#[async_trait]
118impl ReadObjectHandle for PagedLayerObject {
119    async fn read_aligned(&self, offset: u64, buf: MutableBufferRef<'_>) -> Result<usize, Error> {
120        self.handle.read_aligned(offset, buf).await
121    }
122
123    fn get_size(&self) -> u64 {
124        self.handle.get_size()
125    }
126}
127
128#[async_trait]
129impl LayerObject for PagedLayerObject {
130    fn as_slice(&self) -> Option<&[u8]> {
131        Some(self.mapped.as_slice())
132    }
133
134    fn has_io_error(&self) -> bool {
135        self.mapped.has_io_error()
136    }
137
138    fn purge_cached_data(&self) {
139        self.mapped.purge_cached_data();
140    }
141
142    async fn close(&self) {
143        if !self.closed.swap(true, Ordering::Relaxed) {
144            let mut sender = self.sender.lock().await;
145            let _ = sender.push(MappingCommand::CloseBlob { key: self.key }.into()).await;
146        }
147    }
148}
149
150impl Drop for PagedLayerObject {
151    fn drop(&mut self) {
152        // Layers are only explicitly `close()`d during compaction; when a volume is unmounted or
153        // locked (or if `LayerData::open` fails), the layer is dropped directly.
154        if !*self.closed.get_mut() {
155            let sender = self.sender.clone();
156            let key = self.key;
157            self.scope.spawn(async move {
158                let mut sender = sender.lock().await;
159                let _ = sender.push(MappingCommand::CloseBlob { key }.into()).await;
160            });
161        }
162    }
163}
164
165/// Services registration of layer files with a pager backed by the block mapping driver.
166pub struct LayerPagerImpl {
167    mapper_session: fblock::MapperSessionProxy,
168    sender: Arc<AsyncMutex<AsyncSender<RawMappingCommand>>>,
169    scope: fasync::Scope,
170    next_key: AtomicU64,
171}
172
173impl LayerPagerImpl {
174    pub async fn new(mapper_proxy: &fblock::MapperProxy) -> Result<Self, Error> {
175        let mapping_vmo = zx::Vmo::create(MAPPING_VMO_SIZE)?;
176        let sender = AsyncSender::<RawMappingCommand>::new(
177            mapping_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?,
178            8,
179            PENDING_COMMANDS_CAPACITY,
180        )?;
181
182        let (mapper_session, mapper_session_server) = fidl::endpoints::create_proxy();
183        mapper_proxy
184            .open_session(mapper_session_server, mapping_vmo, None, None)
185            .await
186            .context("FIDL error on Mapper.OpenSession")?
187            .map_err(zx::Status::err_from_raw)
188            .context("Failed to open mapper session")?;
189
190        Ok(Self {
191            mapper_session,
192            sender: Arc::new(AsyncMutex::new(sender)),
193            scope: fasync::Scope::new_with_name("layer_pager"),
194            next_key: AtomicU64::new(1),
195        })
196    }
197
198    async fn register_layer(
199        &self,
200        size: u64,
201        extents: &[FileExtent],
202        raw_key: Option<&[u8]>,
203    ) -> Result<(u64, zx::Vmo), Error> {
204        let key = self.next_key.fetch_add(1, Ordering::Relaxed);
205
206        let mut mapping_extents = Vec::with_capacity(extents.len());
207        let mut current_offset = 0u64;
208        for ext in extents {
209            if ext.logical_offset() > current_offset {
210                mapping_extents.push(Extent::try_new(current_offset..ext.logical_offset(), None)?);
211            }
212            mapping_extents
213                .push(Extent::try_new(ext.logical_range(), Some(ext.device_range().start))?);
214            current_offset = ext.logical_range().end;
215        }
216        let aligned_size = size.next_multiple_of(mapping::BLOCK_SIZE);
217        if current_offset < aligned_size {
218            mapping_extents.push(Extent::try_new(current_offset..aligned_size, None)?);
219        }
220        let data_extents = Extents::try_new(&mapping_extents, 0)?;
221        let extent_count = mapping_extents.len() as u32;
222        let encrypted = raw_key.is_some();
223        let key_bytes_len = if encrypted { 32 } else { 0 };
224        let allocation_size = (extent_count as usize * 8) + key_bytes_len;
225
226        if allocation_size > 0 {
227            let mut sender = self.sender.lock().await;
228            let mut payload = sender.reserve_payload(allocation_size).await?;
229            let offset_in_vmo = payload.offset();
230
231            let extent_bytes = extent_count as usize * 8;
232            for (mut chunk, val) in payload
233                .data()
234                .subslice_mut(0..extent_bytes)
235                .chunks_mut(8)
236                .zip(Extents::encode_extents(&data_extents))
237            {
238                chunk.copy_from_slice(&val.to_le_bytes());
239            }
240
241            if let Some(k) = raw_key {
242                payload.data().subslice_mut(extent_bytes..extent_bytes + 32).copy_from_slice(k);
243            }
244
245            let command = MappingCommand::Mappings {
246                key,
247                offset: offset_in_vmo as u32,
248                stored_size: size,
249                device_offset: 0,
250                metadata_count: 0,
251                extent_count,
252                encrypted,
253            };
254            payload.commit(command.into()).await?;
255        }
256
257        let vmo = self
258            .mapper_session
259            .create_vmo(key, size, fblock::CreateVmoOptions::SUPPLY_ZEROES_ON_ERROR)
260            .await
261            .context("FIDL error calling MapperSession.CreateVmo")?
262            .map_err(zx::Status::err_from_raw)
263            .context("MapperSession.CreateVmo returned error")?;
264
265        Ok((key, vmo))
266    }
267}
268
269#[async_trait]
270impl LayerPager for LayerPagerImpl {
271    async fn open_layer(
272        &self,
273        handle: DataObjectHandle<ObjectStore>,
274        unwrapped_key: Option<UnwrappedKey>,
275    ) -> Result<Arc<dyn LayerObject>, Error> {
276        let size = handle.get_size();
277        if size == 0 || handle.block_size() > BlockSize::SIZE_4KIB {
278            return Ok(Arc::new(handle) as Arc<dyn LayerObject>);
279        }
280        let extents = handle.device_extents().await?;
281        let (key, vmo) = self
282            .register_layer(size, &extents, unwrapped_key.as_deref().map(|k| k.as_slice()))
283            .await?;
284        drop(unwrapped_key);
285        let mapped = MappedVmo::new(vmo, size as usize).context("Failed to map layer VMO")?;
286        Ok(Arc::new(PagedLayerObject {
287            handle,
288            mapped,
289            sender: self.sender.clone(),
290            scope: self.scope.to_handle(),
291            key,
292            closed: AtomicBool::new(false),
293        }) as Arc<dyn LayerObject>)
294    }
295}
296
297#[cfg(test)]
298mod tests {
299    use super::*;
300
301    #[fuchsia::test]
302    fn test_mapped_vmo_io_error_signal() {
303        let vmo = zx::Vmo::create(4096).unwrap();
304        vmo.write(&[0x42u8; 4096], 0).unwrap();
305        let vmo_dup = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
306
307        let mapped = MappedVmo::new(vmo, 4096).unwrap();
308        assert_eq!(mapped.as_slice(), &[0x42u8; 4096]);
309        assert!(!mapped.has_io_error());
310
311        vmo_dup.signal(zx::Signals::empty(), zx::Signals::USER_0).unwrap();
312        assert!(mapped.has_io_error());
313    }
314}