blob: 4b617e3a8c0ac9ea76194068ca47df0f1c432a36 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! This supports paging for ext4 files. `zx_pager_supply_pages` requires is to transfer pages to
//! the target, hence the need for a transfer VMO. This also uses a static zeroed VMO to transfer
//! pages that should be zeroed.
use fidl_fuchsia_starnix_runner::{
ManagerCreatePagerRequest, ManagerMarker, PagerMarker, PagerRegisterFileRequest,
PagerRegisterFileResponse, PagerSynchronousProxy,
};
use fuchsia_component::client::connect_to_protocol_sync;
use starnix_core::vfs::FsStr;
use starnix_uapi::error;
use starnix_uapi::errors::Errno;
use std::ops::Range;
/// A struct representing a Pager instance running in the starnix_runner.
///
/// The actual pager logic lives outside of the starnix_kernel in order to avoid issues when the
/// container is suspended. Specifically, if the pager threads are suspended before their client
/// threads the clients may become blocked and unsuspendable. This means that the container suspend
/// operation will deadlock.
pub struct Pager {
pager: PagerSynchronousProxy,
}
/// A single extent.
pub struct PagerExtent {
pub logical: Range<u32>,
pub physical_block: u64,
}
impl Pager {
#[allow(clippy::unwrap_in_result, reason = "Force clippy rule in Starnix")]
/// Returns a new pager. `block_size` shouldn't be too big (which might cause overflows) and it
/// should be a power of 2.
pub fn new(backing_vmo: zx::Vmo, block_size: u64) -> Result<Self, Errno> {
if block_size > 1024 * 1024 || !block_size.is_power_of_two() {
return error!(EINVAL, "Bad block size {block_size}");
}
let manager =
connect_to_protocol_sync::<ManagerMarker>().expect("Failed to connect to pager");
let (pager, pager_server) = fidl::endpoints::create_sync_proxy::<PagerMarker>();
manager
.create_pager(ManagerCreatePagerRequest {
backing_vmo: Some(backing_vmo),
block_size: Some(block_size),
pager: Some(pager_server),
..Default::default()
})
.expect("Failed to create pager");
Ok(Self { pager })
}
/// Registers the file with the pager. Returns a child VMO. `extents` should be sorted.
pub fn register(
&self,
name: &FsStr,
inode_num: u32,
size: u64,
extents: &[PagerExtent],
) -> Result<zx::Vmo, zx::Status> {
match self.pager.register_file(
&PagerRegisterFileRequest {
name: Some(name.to_string()),
inode_num: Some(inode_num),
size: Some(size),
extents: Some(
extents
.iter()
.map(|e| fidl_fuchsia_starnix_runner::PagerExtent {
logical_start: e.logical.start,
logical_end: e.logical.end,
physical_block: e.physical_block,
})
.collect(),
),
..Default::default()
},
zx::Instant::INFINITE,
) {
Ok(Ok(PagerRegisterFileResponse { vmo: Some(vmo), .. })) => Ok(vmo),
Ok(Ok(_)) => Err(zx::Status::INTERNAL),
Ok(Err(e)) => Err(zx::Status::from_raw(e)),
Err(_) => Err(zx::Status::INTERNAL),
}
}
}