Modify some related usages of PageCache and VMO

This commit is contained in:
Chen Chengjun 2026-02-11 09:25:01 +00:00
parent 610504e2e9
commit 22053aad99
13 changed files with 183 additions and 248 deletions

View File

@ -27,8 +27,8 @@ use crate::{
exfat::{constants::*, inode::Ino},
registry::{FsProperties, FsType},
utils::{
CachePage, FileSystem, FsEventSubscriberStats, FsFlags, Inode, PageCache,
PageCacheBackend, SuperBlock,
FileSystem, FsEventSubscriberStats, FsFlags, Inode, LockedCachePage, PageCache,
PageCacheBackend, PageCacheOps, SuperBlock,
},
},
prelude::*,
@ -83,7 +83,7 @@ impl ExfatFs {
fat_cache: RwLock::new(LruCache::<ClusterID, ClusterID>::new(
NonZeroUsize::new(FAT_LRU_CACHE_SIZE).unwrap(),
)),
meta_cache: PageCache::with_capacity(fs_size, weak_self.clone() as _).unwrap(),
meta_cache: PageCacheOps::with_capacity(fs_size, weak_self.clone() as _).unwrap(),
mutex: Mutex::new(()),
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
});
@ -153,17 +153,17 @@ impl ExfatFs {
}
pub(super) fn sync_meta_at(&self, range: core::ops::Range<usize>) -> Result<()> {
self.meta_cache.pages().decommit(range)?;
self.meta_cache.flush_range(range)?;
Ok(())
}
pub(super) fn write_meta_at(&self, offset: usize, buf: &[u8]) -> Result<()> {
self.meta_cache.pages().write_bytes(offset, buf)?;
self.meta_cache.write_bytes(offset, buf)?;
Ok(())
}
pub(super) fn read_meta_at(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
self.meta_cache.pages().read_bytes(offset, buf)?;
self.meta_cache.read_bytes(offset, buf)?;
Ok(())
}
@ -422,7 +422,7 @@ impl FileSystem for ExfatFs {
for inode in self.inodes.read().values() {
inode.sync_all()?;
}
self.meta_cache.evict_range(0..self.fs_size())?;
self.meta_cache.flush_range(0..self.fs_size())?;
Ok(())
}

View File

@ -29,8 +29,9 @@ use crate::{
exfat::{dentry::ExfatDentryIterator, fat::ExfatChain, fs::ExfatFs},
path::{is_dot, is_dot_or_dotdot, is_dotdot},
utils::{
CachePage, DirentVisitor, Extension, Inode, InodeIo, InodeMode, InodeType, Metadata,
MknodType, PageCache, PageCacheBackend, StatusFlags, SymbolicLink, mkmod,
DirentVisitor, Extension, Inode, InodeIo, InodeMode, InodeType, LockedCachePage,
Metadata, MknodType, PageCache, PageCacheBackend, PageCacheOps, StatusFlags,
SymbolicLink, mkmod,
},
},
prelude::*,
@ -254,7 +255,7 @@ impl ExfatInodeInner {
return Ok((0, 0));
}
let iterator = ExfatDentryIterator::new(self.page_cache.pages(), 0, Some(self.size))?;
let iterator = ExfatDentryIterator::new(&self.page_cache, 0, Some(self.size))?;
let mut sub_inodes = 0;
let mut sub_dirs = 0;
for dentry_result in iterator {
@ -357,7 +358,7 @@ impl ExfatInodeInner {
page_cache.write_bytes(start_off, &bytes)?;
if sync {
page_cache.decommit(start_off..start_off + bytes.len())?;
page_cache.flush_range(start_off..start_off + bytes.len())?;
}
Ok(())
@ -383,7 +384,7 @@ impl ExfatInodeInner {
let fs = self.fs();
let cluster_size = fs.cluster_size();
let mut iter = ExfatDentryIterator::new(self.page_cache.pages(), offset, None)?;
let mut iter = ExfatDentryIterator::new(&self.page_cache, offset, None)?;
let mut dir_read = 0;
let mut current_off = offset;
@ -590,7 +591,7 @@ impl ExfatInodeInner {
}
fn sync_data(&self, fs_guard: &MutexGuard<()>) -> Result<()> {
self.page_cache.evict_range(0..self.size)?;
self.page_cache.flush_range(0..self.size)?;
Ok(())
}
@ -634,7 +635,7 @@ impl ExfatInode {
let end = file_size.min(offset + writer.avail());
(start, end - start)
};
inner.page_cache.pages().read(read_off, writer)?;
inner.page_cache.read(read_off, writer)?;
inner.upgrade().update_atime()?;
Ok(read_len)
@ -663,7 +664,7 @@ impl ExfatInode {
inner
.page_cache
.discard_range(read_off..read_off + read_len);
.discard_range(read_off..read_off + read_len)?;
let bio_segment = BioSegment::alloc(1, BioDirection::FromDevice);
@ -709,14 +710,14 @@ impl ExfatInode {
if new_size > file_allocated_size {
inner.resize(new_size, &fs_guard)?;
}
inner.page_cache.resize(new_size)?;
inner.page_cache.resize(new_size, file_size)?;
}
new_size.max(file_size)
};
// Locks released here, so that file write can be parallelized.
let inner = self.inner.upread();
inner.page_cache.pages().write(offset, reader)?;
inner.page_cache.write(offset, reader)?;
// Update timestamps and size.
{
@ -754,7 +755,7 @@ impl ExfatInode {
let start = offset.min(file_size);
let end = end_offset.min(file_size);
inner.page_cache.discard_range(start..end);
inner.page_cache.discard_range(start..end)?;
let new_size = {
let mut inner = inner.upgrade();
@ -764,7 +765,7 @@ impl ExfatInode {
if end_offset > file_allocated_size {
inner.resize(end_offset, &fs_guard)?;
}
inner.page_cache.resize(end_offset)?;
inner.page_cache.resize(end_offset, file_size)?;
}
file_size.max(end_offset)
};
@ -813,8 +814,9 @@ impl ExfatInode {
let inner = self.inner.write();
let fs = inner.fs();
let fs_guard = fs.lock();
self.inner.write().resize(0, &fs_guard)?;
self.inner.read().page_cache.resize(0)?;
let mut inner = self.inner.write();
inner.resize(0, &fs_guard)?;
inner.page_cache.resize(0, inner.size)?;
Ok(())
}
@ -845,7 +847,6 @@ impl ExfatInode {
let size = root_chain.num_clusters() as usize * sb.cluster_size as usize;
let name = ExfatName::new();
let inode = Arc::new_cyclic(|weak_self| ExfatInode {
inner: RwMutex::new(ExfatInodeInner {
ino: EXFAT_ROOT_INO,
@ -866,7 +867,7 @@ impl ExfatInode {
is_deleted: false,
parent_hash: 0,
fs: fs_weak,
page_cache: PageCache::with_capacity(size, weak_self.clone() as _).unwrap(),
page_cache: PageCacheOps::with_capacity(size, weak_self.clone() as _).unwrap(),
}),
extension: Extension::new(),
});
@ -976,7 +977,7 @@ impl ExfatInode {
is_deleted: false,
parent_hash,
fs: fs_weak,
page_cache: PageCache::with_capacity(size, weak_self.clone() as _).unwrap(),
page_cache: PageCacheOps::with_capacity(size, weak_self.clone() as _).unwrap(),
}),
extension: Extension::new(),
});
@ -1019,8 +1020,7 @@ impl ExfatInode {
fn find_empty_dentries(&self, num_dentries: usize, fs_guard: &MutexGuard<()>) -> Result<usize> {
let inner = self.inner.upread();
let dentry_iterator =
ExfatDentryIterator::new(inner.page_cache.pages(), 0, Some(inner.size))?;
let dentry_iterator = ExfatDentryIterator::new(&inner.page_cache, 0, Some(inner.size))?;
let mut contiguous_unused = 0;
let mut entry_id = 0;
@ -1063,15 +1063,16 @@ impl ExfatInode {
inner.size_allocated = new_size_allocated;
inner.size = new_size_allocated;
inner.page_cache.resize(new_size_allocated)?;
inner
.page_cache
.resize(new_size_allocated, old_size_allocated)?;
}
let inner = self.inner.read();
// We need to write unused dentries (i.e. 0) to page cache.
inner
.page_cache
.pages()
.clear(old_size_allocated..new_size_allocated)?;
.fill_zeros(old_size_allocated..new_size_allocated)?;
Ok(entry_id)
}
@ -1106,7 +1107,6 @@ impl ExfatInode {
let inner = self.inner.upread();
inner
.page_cache
.pages()
.write_bytes(start_off, &dentry_set.to_le_bytes())?;
let mut inner = inner.upgrade();
@ -1144,11 +1144,7 @@ impl ExfatInode {
let fs = self.inner.read().fs();
let mut buf = vec![0; len];
self.inner
.read()
.page_cache
.pages()
.read_bytes(offset, &mut buf)?;
self.inner.read().page_cache.read_bytes(offset, &mut buf)?;
let num_dentry = len / DENTRY_SIZE;
@ -1166,11 +1162,7 @@ impl ExfatInode {
buf[buf_offset] &= 0x7F;
}
self.inner
.read()
.page_cache
.pages()
.write_bytes(offset, &buf)?;
self.inner.read().page_cache.write_bytes(offset, &buf)?;
// FIXME: We must make sure that there are no spare tailing clusters in a directory.
Ok(())
@ -1242,8 +1234,9 @@ impl ExfatInode {
let is_dir = inode.inner.read().inode_type.is_directory();
if delete_contents {
if is_dir {
inode.inner.write().resize(0, fs_guard)?;
inode.inner.read().page_cache.resize(0)?;
let mut inner = inode.inner.write();
inner.resize(0, fs_guard)?;
inner.page_cache.resize(0, inner.size)?;
}
// Set the delete flag.
inode.inner.write().is_deleted = true;
@ -1344,7 +1337,7 @@ impl Inode for ExfatInode {
// We will delay updating the page_cache size when enlarging an inode until the real write.
if new_size < file_size {
self.inner.read().page_cache.resize(new_size)?;
self.inner.read().page_cache.resize(new_size, file_size)?;
}
// Sync this inode since size has changed.
@ -1449,7 +1442,7 @@ impl Inode for ExfatInode {
}
fn page_cache(&self) -> Option<Arc<Vmo>> {
Some(self.inner.read().page_cache.pages().clone())
Some(self.inner.read().page_cache.clone())
}
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {

View File

@ -9,7 +9,7 @@ use super::{
prelude::*,
super_block::SuperBlock,
};
use crate::fs::utils::IdBitmap;
use crate::fs::utils::{IdBitmap, LockedCachePage};
/// Blocks are clustered into block groups in order to reduce fragmentation and minimise
/// the amount of head seeking when reading a large amount of consecutive data.
@ -91,7 +91,7 @@ impl BlockGroup {
};
let raw_inodes_cache =
PageCache::with_capacity(raw_inodes_size, Arc::downgrade(&bg_impl) as _)?;
PageCacheOps::with_capacity(raw_inodes_size, Arc::downgrade(&bg_impl) as _)?;
Ok(Self {
idx,
@ -134,10 +134,7 @@ impl BlockGroup {
let fs = self.fs();
let raw_inode = {
let offset = (inode_idx as usize) * fs.inode_size();
self.raw_inodes_cache
.pages()
.read_val::<RawInode>(offset)
.unwrap()
self.raw_inodes_cache.read_val::<RawInode>(offset).unwrap()
};
let inode_desc = Dirty::new(InodeDesc::try_from(raw_inode)?);
let ino = inode_idx + self.idx as u32 * fs.inodes_per_group() + 1;
@ -217,10 +214,7 @@ impl BlockGroup {
/// Writes back the raw inode metadata to the raw inode metadata cache.
pub fn sync_raw_inode(&self, inode_idx: u32, raw_inode: &RawInode) {
let offset = (inode_idx as usize) * self.fs().inode_size();
self.raw_inodes_cache
.pages()
.write_val(offset, raw_inode)
.unwrap();
self.raw_inodes_cache.write_val(offset, raw_inode).unwrap();
}
/// Writes back the metadata of this group.
@ -296,8 +290,7 @@ impl BlockGroup {
// Writes back the raw inode metadata.
self.raw_inodes_cache
.pages()
.decommit(0..self.bg_impl.raw_inodes_size)?;
.flush_range(0..self.bg_impl.raw_inodes_size)?;
Ok(())
}

View File

@ -257,7 +257,7 @@ impl<'a> DirEntryReader<'a> {
let name_buf = &mut self.name_buf.as_mut().unwrap()[..name_len];
let offset = entry_item.offset + DirEntry::HEADER_LEN;
self.page_cache.pages().read_bytes(offset, name_buf)?;
self.page_cache.read_bytes(offset, name_buf)?;
Ok(name_buf)
}
}
@ -265,13 +265,12 @@ impl<'a> DirEntryReader<'a> {
impl DirEntryIter<'_> {
/// Reads a `DirEntryItem` at the current offset.
fn read_next_dir_entry(&mut self) -> Option<DirEntryItem> {
if self.offset >= self.page_cache.pages().size() {
if self.offset >= self.page_cache.size() {
return None;
};
let header = self
.page_cache
.pages()
.read_val::<DirEntryHeader>(self.offset)
.ok()?;
@ -391,9 +390,8 @@ impl<'a> DirEntryWriter<'a> {
/// Writes a `DirEntry` at the current offset. The name is written after the header.
pub fn write_entry(&mut self, header: &DirEntryHeader, name: &str) -> Result<()> {
self.page_cache.pages().write_val(self.offset, header)?;
self.page_cache.write_val(self.offset, header)?;
self.page_cache
.pages()
.write_bytes(self.offset + DirEntry::HEADER_LEN, name.as_bytes())?;
self.offset += header.record_len as usize;
@ -402,16 +400,17 @@ impl<'a> DirEntryWriter<'a> {
/// Writes the header of a `DirEntry` at the current offset.
pub fn write_header_only(&mut self, header: &DirEntryHeader) -> Result<()> {
self.page_cache.pages().write_val(self.offset, header)?;
self.page_cache.write_val(self.offset, header)?;
self.offset += header.record_len as usize;
Ok(())
}
/// Initializes two special `DirEntry`s ("." and "..") with the given inode numbers.
pub fn init_dir(&mut self, self_ino: u32, parent_ino: u32) -> Result<()> {
debug_assert!(self.page_cache.pages().size() == 0 && self.offset == 0);
let old_size = self.page_cache.size();
debug_assert!(old_size == 0 && self.offset == 0);
self.page_cache.pages().resize(BLOCK_SIZE)?;
self.page_cache.resize(BLOCK_SIZE, old_size)?;
let self_header = DirEntryHeader::new(self_ino, InodeType::Dir, 1);
self.write_entry(&self_header, ".")?;
@ -480,9 +479,9 @@ impl<'a> DirEntryWriter<'a> {
fn append_entry_in_the_end(&mut self, mut header: DirEntryHeader, name: &str) -> Result<()> {
// Resize and append it at the new block.
let old_size = self.page_cache.pages().size();
let old_size = self.page_cache.size();
let new_size = old_size + BLOCK_SIZE;
self.page_cache.resize(new_size)?;
self.page_cache.resize(new_size, old_size)?;
header.record_len = BLOCK_SIZE as _;
self.offset = old_size;
@ -513,7 +512,7 @@ impl<'a> DirEntryWriter<'a> {
if is_last_entry {
// Shrink the size.
let new_size = pre_offset.align_up(BLOCK_SIZE);
self.page_cache.resize(new_size)?;
self.page_cache.resize(new_size, self.page_cache.size())?;
pre_entry_item.set_record_len(new_size - pre_offset);
self.offset = pre_offset;
self.write_header_only(&pre_entry_item.header)?;
@ -566,7 +565,7 @@ impl<'a> DirEntryWriter<'a> {
let name_buf = &mut self.name_buf.as_mut().unwrap()[..name_len];
let offset = item.offset + DirEntry::HEADER_LEN;
self.page_cache.pages().read_bytes(offset, name_buf)?;
self.page_cache.read_bytes(offset, name_buf)?;
Ok(name_buf)
}

View File

@ -23,8 +23,8 @@ use crate::{
path::{is_dot, is_dot_or_dotdot, is_dotdot},
pipe::Pipe,
utils::{
Extension, FallocMode, Inode as _, InodeMode, Metadata, Permission, XattrName,
XattrNamespace, XattrSetFlags,
Extension, FallocMode, Inode as _, InodeMode, LockedCachePage, Metadata, Permission,
XattrName, XattrNamespace, XattrSetFlags,
},
},
process::{Gid, Uid, posix_thread::AsPosixThread},
@ -99,7 +99,7 @@ impl Inode {
}
pub fn page_cache(&self) -> Arc<Vmo> {
self.inner.read().page_cache.pages().clone()
self.inner.read().page_cache.clone()
}
pub fn metadata(&self) -> Metadata {
@ -976,7 +976,7 @@ impl InodeInner {
let num_page_bytes = desc.num_page_bytes();
let inode_impl = InodeImpl::new(desc, weak_self, fs);
Self {
page_cache: PageCache::with_capacity(
page_cache: PageCacheOps::with_capacity(
num_page_bytes,
Arc::downgrade(&inode_impl.block_manager) as _,
)
@ -986,7 +986,8 @@ impl InodeInner {
}
pub fn resize(&mut self, new_size: usize) -> Result<()> {
self.page_cache.resize(new_size)?;
self.page_cache
.resize(new_size, self.inode_impl.file_size())?;
self.inode_impl.resize(new_size)?;
Ok(())
}
@ -999,7 +1000,7 @@ impl InodeInner {
(start, end - start)
};
self.page_cache.pages().read(offset, writer)?;
self.page_cache.read(offset, writer)?;
Ok(read_len)
}
@ -1016,7 +1017,7 @@ impl InodeInner {
if read_len == 0 {
return Ok(read_len);
}
self.page_cache.discard_range(offset..offset + read_len);
self.page_cache.discard_range(offset..offset + read_len)?;
let start_bid = Bid::from_offset(offset).to_raw() as Ext2Bid;
let buf_nblocks = read_len / BLOCK_SIZE;
@ -1028,15 +1029,16 @@ impl InodeInner {
pub fn write_at(&self, offset: usize, reader: &mut VmReader) -> Result<usize> {
let write_len = reader.remain();
self.page_cache.pages().write(offset, reader)?;
self.page_cache.write(offset, reader)?;
Ok(write_len)
}
pub fn extend_write_at(&mut self, offset: usize, reader: &mut VmReader) -> Result<usize> {
let write_len = reader.remain();
let new_size = offset + write_len;
self.page_cache.resize(new_size.align_up(BLOCK_SIZE))?;
self.page_cache.pages().write(offset, reader)?;
self.page_cache
.resize(new_size.align_up(BLOCK_SIZE), self.inode_impl.file_size())?;
self.page_cache.write(offset, reader)?;
self.inode_impl.resize(new_size)?;
Ok(write_len)
}
@ -1049,7 +1051,7 @@ impl InodeInner {
let start = offset.min(file_size);
let end = end_offset.min(file_size);
self.page_cache.discard_range(start..end);
self.page_cache.discard_range(start..end)?;
if end_offset > file_size {
self.inode_impl.resize(end_offset)?;
@ -1068,12 +1070,14 @@ impl InodeInner {
return self.inode_impl.write_fast_link(target);
}
self.page_cache.resize(target.len())?;
self.page_cache.pages().write_bytes(0, target.as_bytes())?;
let file_size = self.inode_impl.file_size();
if file_size != target.len() {
self.page_cache.resize(target.len(), file_size)?;
self.inode_impl.resize(target.len())?;
}
self.page_cache.write_bytes(0, target.as_bytes())?;
Ok(())
}
@ -1084,9 +1088,7 @@ impl InodeInner {
}
let mut symlink = vec![0u8; file_size];
self.page_cache
.pages()
.read_bytes(0, symlink.as_mut_slice())?;
self.page_cache.read_bytes(0, symlink.as_mut_slice())?;
Ok(String::from_utf8(symlink)?)
}
@ -1125,7 +1127,7 @@ impl InodeInner {
)?;
let file_size = self.file_size();
let page_cache_size = self.page_cache.pages().size();
let page_cache_size = self.page_cache.size();
if page_cache_size > file_size {
self.inode_impl.resize(page_cache_size)?;
}
@ -1141,7 +1143,7 @@ impl InodeInner {
pub fn remove_entry_at(&mut self, name: &str, offset: usize) -> Result<()> {
let removed_entry = DirEntryWriter::new(&self.page_cache, offset).remove_entry(name)?;
let file_size = self.file_size();
let page_cache_size = self.page_cache.pages().size();
let page_cache_size = self.page_cache.size();
if page_cache_size < file_size {
self.inode_impl.resize(page_cache_size)?;
}
@ -1154,7 +1156,7 @@ impl InodeInner {
pub fn rename_entry_at(&mut self, old_name: &str, new_name: &str, offset: usize) -> Result<()> {
DirEntryWriter::new(&self.page_cache, offset).rename_entry(old_name, new_name)?;
let file_size = self.file_size();
let page_cache_size = self.page_cache.pages().size();
let page_cache_size = self.page_cache.size();
if page_cache_size != file_size {
self.inode_impl.resize(page_cache_size)?;
}
@ -1172,7 +1174,7 @@ impl InodeInner {
pub fn sync_data(&self) -> Result<()> {
// Writes back the data in page cache.
let file_size = self.file_size();
self.page_cache.evict_range(0..file_size)?;
self.page_cache.flush_range(0..file_size)?;
Ok(())
}
}
@ -1828,7 +1830,7 @@ impl InodeImpl {
writer: &mut VmWriter,
) -> Result<BioWaiter>;
pub fn read_blocks(&self, bid: Ext2Bid, nblocks: usize, writer: &mut VmWriter) -> Result<()>;
pub fn read_block_async(&self, bid: Ext2Bid, frame: &CachePage) -> Result<BioWaiter>;
pub fn read_block_async(&self, bid: Ext2Bid, frame: LockedCachePage) -> Result<BioWaiter>;
pub fn write_blocks_async(
&self,
bid: Ext2Bid,
@ -1836,7 +1838,7 @@ impl InodeImpl {
reader: &mut VmReader,
) -> Result<BioWaiter>;
pub fn write_blocks(&self, bid: Ext2Bid, nblocks: usize, reader: &mut VmReader) -> Result<()>;
pub fn write_block_async(&self, bid: Ext2Bid, frame: &CachePage) -> Result<BioWaiter>;
pub fn write_block_async(&self, bid: Ext2Bid, frame: LockedCachePage) -> Result<BioWaiter>;
}
/// Manages the inode blocks and block I/O operations.
@ -1965,12 +1967,12 @@ impl InodeBlockManager {
}
impl PageCacheBackend for InodeBlockManager {
fn read_page_async(&self, idx: usize, frame: &CachePage) -> Result<BioWaiter> {
fn read_page_async(&self, idx: usize, frame: LockedCachePage) -> Result<BioWaiter> {
let bid = idx as Ext2Bid;
self.read_block_async(bid, frame)
}
fn write_page_async(&self, idx: usize, frame: &CachePage) -> Result<BioWaiter> {
fn write_page_async(&self, idx: usize, frame: LockedCachePage) -> Result<BioWaiter> {
let bid = idx as Ext2Bid;
self.write_block_async(bid, frame)
}

View File

@ -19,7 +19,7 @@ pub(super) use ostd::{
pub(super) use super::utils::{Dirty, IsPowerOf};
pub(super) use crate::{
fs::utils::{
CStr256, CachePage, DirentVisitor, InodeType, PageCache, PageCacheBackend, Str16, Str64,
CStr256, DirentVisitor, InodeType, PageCache, PageCacheBackend, PageCacheOps, Str16, Str64,
},
prelude::*,
time::UnixTime,

View File

@ -6,14 +6,10 @@ use core::{
};
use align_ext::AlignExt;
use aster_block::bio::BioWaiter;
use aster_util::slot_vec::SlotVec;
use device_id::DeviceId;
use hashbrown::HashMap;
use ostd::{
mm::{HasSize, io_util::HasVmReaderWriter},
sync::{PreemptDisabled, RwLockWriteGuard},
};
use ostd::sync::{PreemptDisabled, RwLockWriteGuard};
use super::{memfd::MemfdInode, xattr::RamXattr, *};
use crate::{
@ -25,16 +21,16 @@ use crate::{
pipe::Pipe,
registry::{FsProperties, FsType},
utils::{
AccessMode, CStr256, CachePage, DirentVisitor, Extension, FallocMode, FileSystem,
AccessMode, CStr256, DirentVisitor, Extension, FallocMode, FileSystem,
FsEventSubscriberStats, FsFlags, Inode, InodeIo, InodeMode, InodeType, Metadata,
MknodType, PageCache, PageCacheBackend, Permission, StatusFlags, SuperBlock,
SymbolicLink, XattrName, XattrNamespace, XattrSetFlags, mkmod,
MknodType, PageCache, PageCacheOps, Permission, StatusFlags, SuperBlock, SymbolicLink,
XattrName, XattrNamespace, XattrSetFlags, mkmod,
},
},
prelude::*,
process::{Gid, Uid},
time::clocks::RealTimeCoarseClock,
vm::vmo::Vmo,
vm::vmo::{Vmo, VmoFlags, VmoOptions},
};
/// A volatile file system whose data and metadata exists only in memory.
@ -136,8 +132,13 @@ impl Inner {
Self::Dir(RwLock::new(DirEntry::new(this, parent)))
}
pub(self) fn new_file(this: Weak<RamInode>) -> Self {
Self::File(PageCache::new(this).unwrap())
pub(self) fn new_file() -> Self {
Self::File(
VmoOptions::new(0)
.flags(VmoFlags::RESIZABLE)
.alloc()
.unwrap(),
)
}
pub(self) fn new_symlink() -> Self {
@ -160,8 +161,13 @@ impl Inner {
Self::NamedPipe(Pipe::new())
}
pub(self) fn new_file_in_memfd(this: Weak<MemfdInode>) -> Self {
Self::File(PageCache::new(this).unwrap())
pub(self) fn new_file_in_memfd() -> Self {
Self::File(
VmoOptions::new(0)
.flags(VmoFlags::RESIZABLE)
.alloc()
.unwrap(),
)
}
fn as_direntry(&self) -> Option<&RwLock<DirEntry>> {
@ -456,7 +462,7 @@ impl RamInode {
fn new_file(fs: &Arc<RamFs>, mode: InodeMode, uid: Uid, gid: Gid) -> Arc<Self> {
Arc::new_cyclic(|weak_self| RamInode {
inner: Inner::new_file(weak_self.clone()),
inner: Inner::new_file(),
metadata: SpinLock::new(InodeMeta::new(mode, uid, gid)),
ino: fs.alloc_id(),
typ: InodeType::File,
@ -475,7 +481,7 @@ impl RamInode {
gid: Gid,
) -> Self {
Self {
inner: Inner::new_file_in_memfd(weak_self.clone()),
inner: Inner::new_file_in_memfd(),
metadata: SpinLock::new(InodeMeta::new(mode, uid, gid)),
ino: weak_self.as_ptr() as u64,
typ: InodeType::File,
@ -566,23 +572,6 @@ impl RamInode {
}
}
impl PageCacheBackend for RamInode {
fn read_page_async(&self, _idx: usize, frame: &CachePage) -> Result<BioWaiter> {
// Initially, any block/page in a RamFs inode contains all zeros
frame.writer().fill_zeros(frame.size());
Ok(BioWaiter::new())
}
fn write_page_async(&self, _idx: usize, _frame: &CachePage) -> Result<BioWaiter> {
// do nothing
Ok(BioWaiter::new())
}
fn npages(&self) -> usize {
self.metadata.lock().blocks
}
}
impl InodeIo for RamInode {
fn read_at(
&self,
@ -598,7 +587,7 @@ impl InodeIo for RamInode {
let end = file_size.min(offset + writer.avail());
(start, end - start)
};
page_cache.pages().read(offset, writer)?;
page_cache.read(offset, writer)?;
read_len
}
_ => return_errno_with_message!(Errno::EISDIR, "read is not supported"),
@ -626,9 +615,9 @@ impl InodeIo for RamInode {
let should_expand_size = new_size > file_size;
let new_size_aligned = new_size.align_up(BLOCK_SIZE);
if should_expand_size {
page_cache.resize(new_size_aligned)?;
page_cache.resize(new_size_aligned, file_size)?;
}
page_cache.pages().write(offset, reader)?;
page_cache.write(offset, reader)?;
let now = now();
let mut inode_meta = self.metadata.lock();
@ -648,9 +637,7 @@ impl InodeIo for RamInode {
impl Inode for RamInode {
fn page_cache(&self) -> Option<Arc<Vmo>> {
self.inner
.as_file()
.map(|page_cache| page_cache.pages().clone())
self.inner.as_file().cloned()
}
fn size(&self) -> usize {
@ -671,7 +658,7 @@ impl Inode for RamInode {
}
let page_cache = self.inner.as_file().unwrap();
page_cache.resize(new_size)?;
page_cache.resize(new_size, file_size)?;
let now = now();
let mut inode_meta = self.metadata.lock();

View File

@ -6,7 +6,6 @@ use alloc::format;
use core::time::Duration;
use align_ext::AlignExt;
use aster_block::bio::BioWaiter;
use aster_rights::Rights;
use inherit_methods_macro::inherit_methods;
use spin::Once;
@ -18,9 +17,8 @@ use crate::{
path::{Mount, Path},
tmpfs::TmpFs,
utils::{
AccessMode, CachePage, Extension, FallocMode, FileSystem, Inode, InodeIo, InodeMode,
InodeType, Metadata, PageCacheBackend, StatusFlags, XattrName, XattrNamespace,
XattrSetFlags, mkmod,
AccessMode, Extension, FallocMode, FileSystem, Inode, InodeIo, InodeMode, InodeType,
Metadata, StatusFlags, XattrName, XattrNamespace, XattrSetFlags, mkmod,
},
},
prelude::*,
@ -91,13 +89,6 @@ impl MemfdInode {
}
}
#[inherit_methods(from = "self.inode")]
impl PageCacheBackend for MemfdInode {
fn read_page_async(&self, idx: usize, frame: &CachePage) -> Result<BioWaiter>;
fn write_page_async(&self, idx: usize, frame: &CachePage) -> Result<BioWaiter>;
fn npages(&self) -> usize;
}
#[inherit_methods(from = "self.inode")]
impl InodeIo for MemfdInode {
fn read_at(

View File

@ -19,7 +19,11 @@ pub use inode_ext::InodeExt;
pub use inode_mode::InodeMode;
pub(crate) use inode_mode::{chmod, mkmod, perms_to_mask, who_and_perms_to_mask, who_to_mask};
pub use open_args::OpenArgs;
pub use page_cache::{CachePage, PageCache, PageCacheBackend};
#[expect(unused_imports)]
pub use page_cache::{
CachePage, CachePageExt, CachePageMeta, LockedCachePage, PageCache, PageCacheBackend,
PageCacheOps, PageState,
};
#[cfg(ktest)]
pub use random_test::{generate_random_operation, new_fs_in_memory};
pub use range_lock::{FileRange, OFFSET_MAX, RangeLockItem, RangeLockList, RangeLockType};

View File

@ -262,7 +262,7 @@ impl Vdso {
Self {
data: SpinLock::new(vdso_data),
vmo: vdso_vmo,
data_frame,
data_frame: data_frame.into(),
}
}

View File

@ -22,14 +22,14 @@ use super::{RssType, Vmar, interval_set::Interval, util::is_intersected, vmar_im
use crate::{
fs::{
path::{Path, PathResolver},
utils::Inode,
utils::{CachePage, Inode},
},
prelude::*,
process::LockedHeap,
thread::exception::PageFaultInfo,
vm::{
perms::VmPerms,
vmo::{CommitFlags, Vmo, VmoCommitError},
vmo::{Vmo, VmoCommitError},
},
};
@ -434,13 +434,18 @@ impl VmMapping {
let (frame, is_readonly) = match self.prepare_page(page_aligned_addr, is_write)
{
Ok((frame, is_readonly)) => (frame, is_readonly),
Err(VmoCommitError::Err(e)) => return Err(e),
Err(VmoCommitError::NeedIo(index)) => {
Err(
VmoCommitError::NeedIo(index) | VmoCommitError::WaitUntilInit(index, _),
) => {
drop(cursor);
drop(preempt_guard);
self.vmo().unwrap().commit_on(index, CommitFlags::empty())?;
self.vmo().unwrap().commit_on(index)?;
continue 'retry;
}
Err(VmoCommitError::Err(e)) => return Err(e),
Err(_) => {
unreachable!("unexpected VmoCommitError");
}
};
let vm_perms = {
@ -549,38 +554,42 @@ impl VmMapping {
let mut cursor = vm_space.cursor_mut(&preempt_guard, &(start_addr..end_addr))?;
let rss_delta_ref = &mut rss_delta;
let operate =
move |commit_fn: &mut dyn FnMut()
-> core::result::Result<UFrame, VmoCommitError>| {
if let (_, None) = cursor.query().unwrap() {
// We regard all the surrounding pages as accessed, no matter
// if it is really so. Then the hardware won't bother to update
// the accessed bit of the page table on following accesses.
let page_flags = PageFlags::from(vm_perms) | PageFlags::ACCESSED;
let page_prop = PageProperty::new_user(page_flags, CachePolicy::Writeback);
let frame = commit_fn()?;
cursor.map(frame, page_prop);
rss_delta_ref.add(self.rss_type(), 1);
} else {
let next_addr = cursor.virt_addr() + PAGE_SIZE;
if next_addr < end_addr {
let _ = cursor.jump(next_addr);
}
let operate = move |commit_fn: &mut dyn FnMut() -> core::result::Result<
(usize, CachePage),
VmoCommitError,
>| {
if let (_, None) = cursor.query().unwrap() {
// We regard all the surrounding pages as accessed, no matter
// if it is really so. Then the hardware won't bother to update
// the accessed bit of the page table on following accesses.
let page_flags = PageFlags::from(vm_perms) | PageFlags::ACCESSED;
let page_prop = PageProperty::new_user(page_flags, CachePolicy::Writeback);
let (_, frame) = commit_fn()?;
cursor.map(frame.into(), page_prop);
rss_delta_ref.add(self.rss_type(), 1);
} else {
let next_addr = cursor.virt_addr() + PAGE_SIZE;
if next_addr < end_addr {
let _ = cursor.jump(next_addr);
}
Ok(())
};
}
Ok(())
};
let start_offset = start_addr - self.map_to_addr;
let end_offset = end_addr - self.map_to_addr;
match vmo.try_operate_on_range(&(start_offset..end_offset), operate) {
Ok(_) => return Ok(()),
Err(VmoCommitError::NeedIo(index)) => {
Err(VmoCommitError::NeedIo(index) | VmoCommitError::WaitUntilInit(index, _)) => {
drop(preempt_guard);
vmo.commit_on(index, CommitFlags::empty())?;
vmo.commit_on(index)?;
start_addr = (index * PAGE_SIZE - vmo.offset()) + self.map_to_addr;
continue 'retry;
}
Err(VmoCommitError::Err(e)) => return Err(e),
Err(_) => {
unreachable!("unexpected VmoCommitError");
}
}
}
}
@ -833,15 +842,17 @@ impl MappedVmo {
page_offset: usize,
) -> core::result::Result<UFrame, VmoCommitError> {
debug_assert!(page_offset.is_multiple_of(PAGE_SIZE));
self.vmo.try_commit_page(self.offset + page_offset)
self.vmo
.try_commit_page(self.offset + page_offset)
.map(|frame| frame.into())
}
/// Commits a page at a specific page index.
///
/// This method may involve I/O operations if the VMO needs to fetch
/// a page from the underlying page cache.
pub fn commit_on(&self, page_idx: usize, commit_flags: CommitFlags) -> Result<UFrame> {
self.vmo.commit_on(page_idx, commit_flags)
pub fn commit_on(&self, page_idx: usize) -> Result<UFrame> {
self.vmo.commit_on(page_idx).map(|frame| frame.into())
}
/// Traverses the indices within a specified range of a VMO sequentially.
@ -857,7 +868,7 @@ impl MappedVmo {
) -> core::result::Result<(), VmoCommitError>
where
F: FnMut(
&mut dyn FnMut() -> core::result::Result<UFrame, VmoCommitError>,
&mut dyn FnMut() -> core::result::Result<(usize, CachePage), VmoCommitError>,
) -> core::result::Result<(), VmoCommitError>,
{
let range = self.offset + range.start..self.offset + range.end;

View File

@ -5,11 +5,14 @@
use core::sync::atomic::AtomicUsize;
use align_ext::AlignExt;
use ostd::mm::{FrameAllocOptions, UFrame, USegment};
use ostd::mm::{FrameAllocOptions, Segment};
use xarray::XArray;
use super::{Pager, Vmo, VmoFlags, WritableMappingStatus};
use crate::prelude::*;
use super::{Vmo, VmoFlags, WritableMappingStatus};
use crate::{
fs::utils::{CachePage, CachePageMeta, PageCacheBackend},
prelude::*,
};
/// Options for allocating a root VMO.
///
@ -38,7 +41,7 @@ use crate::prelude::*;
pub struct VmoOptions {
size: usize,
flags: VmoFlags,
pager: Option<Arc<dyn Pager>>,
backend: Option<Weak<dyn PageCacheBackend>>,
}
impl VmoOptions {
@ -50,7 +53,7 @@ impl VmoOptions {
Self {
size,
flags: VmoFlags::empty(),
pager: None,
backend: None,
}
}
@ -65,8 +68,8 @@ impl VmoOptions {
}
/// Sets the pager of the VMO.
pub fn pager(mut self, pager: Arc<dyn Pager>) -> Self {
self.pager = Some(pager);
pub fn backend(mut self, backend: Weak<dyn PageCacheBackend>) -> Self {
self.backend = Some(backend);
self
}
}
@ -75,19 +78,26 @@ impl VmoOptions {
/// Allocates the VMO according to the specified options.
pub fn alloc(self) -> Result<Arc<Vmo>> {
let VmoOptions {
size, flags, pager, ..
size,
flags,
backend,
..
} = self;
let vmo = alloc_vmo(size, flags, pager)?;
let vmo = alloc_vmo(size, flags, backend)?;
Ok(Arc::new(vmo))
}
}
fn alloc_vmo(size: usize, flags: VmoFlags, pager: Option<Arc<dyn Pager>>) -> Result<Vmo> {
fn alloc_vmo(
size: usize,
flags: VmoFlags,
backend: Option<Weak<dyn PageCacheBackend>>,
) -> Result<Vmo> {
let size = size.align_up(PAGE_SIZE);
let pages = committed_pages_if_continuous(flags, size)?;
let writable_mapping_status = WritableMappingStatus::default();
Ok(Vmo {
pager,
backend,
flags,
pages,
size: AtomicUsize::new(size),
@ -95,11 +105,12 @@ fn alloc_vmo(size: usize, flags: VmoFlags, pager: Option<Arc<dyn Pager>>) -> Res
})
}
fn committed_pages_if_continuous(flags: VmoFlags, size: usize) -> Result<XArray<UFrame>> {
fn committed_pages_if_continuous(flags: VmoFlags, size: usize) -> Result<XArray<CachePage>> {
if flags.contains(VmoFlags::CONTIGUOUS) {
// if the vmo is continuous, we need to allocate frames for the vmo
let frames_num = size / PAGE_SIZE;
let segment: USegment = FrameAllocOptions::new().alloc_segment(frames_num)?.into();
let segment: Segment<CachePageMeta> = FrameAllocOptions::new()
.alloc_segment_with(frames_num, |_| CachePageMeta::default())?;
let committed_pages = XArray::new();
let mut locked_pages = committed_pages.lock();
let mut cursor = locked_pages.cursor_mut(0);
@ -154,16 +165,18 @@ mod test {
#[ktest]
fn resize() {
use crate::fs::utils::PageCacheOps;
let vmo = VmoOptions::new(PAGE_SIZE)
.flags(VmoFlags::RESIZABLE)
.alloc()
.unwrap();
vmo.write_val(10, &42u8).unwrap();
vmo.resize(2 * PAGE_SIZE).unwrap();
vmo.resize(2 * PAGE_SIZE, vmo.size()).unwrap();
assert_eq!(vmo.size(), 2 * PAGE_SIZE);
assert_eq!(vmo.read_val::<u8>(10).unwrap(), 42);
vmo.write_val(PAGE_SIZE + 20, &123u8).unwrap();
vmo.resize(PAGE_SIZE).unwrap();
vmo.resize(PAGE_SIZE, vmo.size()).unwrap();
assert_eq!(vmo.read_val::<u8>(10).unwrap(), 42);
}
}

View File

@ -1,58 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
use ostd::mm::UFrame;
use crate::prelude::*;
/// Pagers provide frame to a VMO.
///
/// A `Pager` object can be attached to a VMO. Whenever the
/// VMO needs more frames (i.e., on commits), it will turn to the pager,
/// which should then provide frames whose data have been initialized properly.
/// Any time a frame is updated through the VMO, the VMO will
/// notify the attached pager that the frame has been updated.
/// Finally, when a frame is no longer needed (i.e., on decommits),
/// the frame pager will also be notified.
pub trait Pager: Send + Sync {
/// Ask the pager to provide a frame at a specified index.
///
/// After a page of a VMO is committed, the VMO shall not call this method
/// again until the page is decommitted. But a robust implementation of
/// `Pager` should not rely on this behavior for its correctness;
/// instead, it should returns the _same_ frame.
///
/// If a VMO page has been previously committed and decommited,
/// and is to be committed again, then the pager is free to return
/// whatever frame that may or may not be the same as the last time.
///
/// It is up to the pager to decide the range of valid indices.
fn commit_page(&self, idx: usize) -> Result<UFrame>;
/// Notify the pager that the frame at a specified index has been updated.
///
/// Being aware of the updates allow the pager (e.g., an inode) to
/// know which pages are dirty and only write back the _dirty_ pages back
/// to disk.
///
/// The VMO will not call this method for an uncommitted page.
/// But a robust implementation of `Pager` should not make
/// such an assumption for its correctness; instead, it should simply ignore the
/// call or return an error.
fn update_page(&self, idx: usize) -> Result<()>;
/// Notify the pager that the frame at the specified index has been decommitted.
///
/// Knowing that a frame is no longer needed, the pager (e.g., an inode)
/// can free the frame after writing back its data to the disk.
///
/// The VMO will not call this method for an uncommitted page.
/// But a robust implementation of `Pager` should not make
/// such an assumption for its correctness; instead, it should simply ignore the
/// call or return an error.
fn decommit_page(&self, idx: usize) -> Result<()>;
/// Ask the pager to provide a frame at a specified index.
/// Notify the pager that the frame will be fully overwritten soon, so pager can
/// choose not to initialize it.
fn commit_overwrite(&self, idx: usize) -> Result<UFrame>;
}