Skip to content

Commit db1caeb

Browse files
feat(fs): partial work for ext4's page cache
Fix page cache's bug, add size check in read function. Add page cache's base operations for ext4, but the cachepage will not be dropped until kernel stop, so we need to call fsync function manually, consider use some strategy such as LRU.
1 parent 082c5c5 commit db1caeb

6 files changed

Lines changed: 101 additions & 40 deletions

File tree

src/fs/ext4.rs

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
22

3-
use crate::kernel::mem::{PageCache, PageCacheBackend};
3+
use crate::kernel::mem::{CachePage, CachePageStream, PageCache, PageCacheBackend};
44
use crate::{
55
io::{Buffer, ByteBuffer, Stream},
66
kernel::{
@@ -83,7 +83,7 @@ impl Vfs for Ext4Fs {
8383
}
8484

8585
fn is_read_only(&self) -> bool {
86-
true
86+
false
8787
}
8888
}
8989

@@ -257,12 +257,12 @@ impl FileInode {
257257
}
258258

259259
impl PageCacheBackend for FileInode {
260-
fn read_page(&self, page: &mut crate::kernel::mem::CachePage, offset: usize) -> KResult<usize> {
260+
fn read_page(&self, page: &mut CachePage, offset: usize) -> KResult<usize> {
261261
self.read_direct(page, offset)
262262
}
263263

264-
fn write_page(&self, page: &crate::kernel::mem::CachePage, offset: usize) -> KResult<usize> {
265-
todo!()
264+
fn write_page(&self, page: &mut CachePageStream, offset: usize) -> KResult<usize> {
265+
self.write_direct(page, offset)
266266
}
267267

268268
fn size(&self) -> usize {
@@ -296,12 +296,6 @@ impl Inode for FileInode {
296296
fn write(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult<usize> {
297297
let _lock = Task::block_on(self.rwsem.write());
298298

299-
let vfs = self.vfs.upgrade().ok_or(EIO)?;
300-
let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
301-
302-
let mut temp_buf = vec![0u8; 4096];
303-
let mut total_written = 0;
304-
305299
let mut store_new_end = None;
306300
let offset = match offset {
307301
WriteOffset::Position(offset) => offset,
@@ -312,6 +306,31 @@ impl Inode for FileInode {
312306
}
313307
};
314308

309+
let total_written = Task::block_on(self.page_cache.write(stream, offset))?;
310+
let cursor_end = offset + total_written;
311+
if let Some(store_end) = store_new_end {
312+
*store_end = cursor_end;
313+
}
314+
315+
let mtime = Instant::now();
316+
*self.mtime.lock() = mtime;
317+
self.size.store(cursor_end as u64, Ordering::Relaxed);
318+
319+
// TODO: change this with some update strategy such as LRU
320+
let _ = Task::block_on(self.page_cache.fsync());
321+
322+
Ok(total_written)
323+
}
324+
325+
fn write_direct(&self, stream: &mut dyn Stream, offset: usize) -> KResult<usize> {
326+
//let _lock = Task::block_on(self.rwsem.write());
327+
328+
let vfs = self.vfs.upgrade().ok_or(EIO)?;
329+
let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
330+
331+
let mut temp_buf = vec![0u8; 4096];
332+
let mut total_written = 0;
333+
315334
while let Some(data) = stream.poll_data(&mut temp_buf)? {
316335
let written = ext4fs
317336
.inner
@@ -323,18 +342,10 @@ impl Inode for FileInode {
323342
}
324343
}
325344

326-
if let Some(store_end) = store_new_end {
327-
*store_end = offset + total_written;
328-
}
329-
let mtime = Instant::now();
330-
*self.mtime.lock() = mtime;
331-
let new_size = (offset + total_written) as u64;
332-
self.size
333-
.store(offset as u64 + total_written as u64, Ordering::Relaxed);
334345
ext4fs.modify_inode_stat(
335346
self.ino as u32,
336-
Some(new_size),
337-
mtime.since_epoch().as_secs() as u32,
347+
Some(self.size() as u64),
348+
self.mtime.lock().since_epoch().as_secs() as u32,
338349
);
339350

340351
Ok(total_written)

src/fs/fat32.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ mod file;
33

44
use crate::io::Stream;
55
use crate::kernel::constants::EIO;
6-
use crate::kernel::mem::AsMemoryBlock;
6+
use crate::kernel::mem::{AsMemoryBlock, CachePageStream};
77
use crate::kernel::vfs::inode::WriteOffset;
88
use crate::{
99
io::{Buffer, ByteBuffer, UninitBuffer},
@@ -308,11 +308,11 @@ impl Inode for FileInode {
308308
Ok(buffer.wrote())
309309
}
310310

311-
fn write(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult<usize> {
311+
fn write(&self, _stream: &mut dyn Stream, _offset: WriteOffset) -> KResult<usize> {
312312
todo!()
313313
}
314314

315-
fn write_direct(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult<usize> {
315+
fn write_direct(&self, _stream: &mut dyn Stream, _offset: usize) -> KResult<usize> {
316316
todo!()
317317
}
318318
}
@@ -322,7 +322,7 @@ impl PageCacheBackend for FileInode {
322322
self.read_direct(page, offset)
323323
}
324324

325-
fn write_page(&self, page: &CachePage, offset: usize) -> KResult<usize> {
325+
fn write_page(&self, _page: &mut CachePageStream, _offset: usize) -> KResult<usize> {
326326
todo!()
327327
}
328328

src/fs/tmpfs.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::io::Stream;
22
use crate::kernel::constants::{EEXIST, EINVAL, EIO, EISDIR, ENOENT, ENOSYS, ENOTDIR};
3-
use crate::kernel::mem::{CachePage, PageCache, PageCacheBackend};
3+
use crate::kernel::mem::{CachePage, CachePageStream, PageCache, PageCacheBackend};
44
use crate::kernel::timer::Instant;
55
use crate::kernel::vfs::inode::InodeData;
66
use crate::kernel::vfs::inode::RenameData;
@@ -496,7 +496,7 @@ impl PageCacheBackend for FileInode {
496496
Ok(PAGE_SIZE)
497497
}
498498

499-
fn write_page(&self, _page: &CachePage, _offset: usize) -> KResult<usize> {
499+
fn write_page(&self, _page: &mut CachePageStream, _offset: usize) -> KResult<usize> {
500500
Ok(PAGE_SIZE)
501501
}
502502

@@ -511,13 +511,13 @@ impl Inode for FileInode {
511511
}
512512

513513
fn read(&self, buffer: &mut dyn Buffer, offset: usize) -> KResult<usize> {
514-
let lock = Task::block_on(self.rwsem.write());
514+
let _lock = Task::block_on(self.rwsem.write());
515515
Task::block_on(self.pages.read(buffer, offset))
516516
}
517517

518518
fn write(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult<usize> {
519519
// TODO: We don't need that strong guarantee, find some way to avoid locks
520-
let lock = Task::block_on(self.rwsem.write());
520+
let _lock = Task::block_on(self.rwsem.write());
521521

522522
let mut store_new_end = None;
523523
let offset = match offset {
@@ -545,7 +545,7 @@ impl Inode for FileInode {
545545
}
546546

547547
fn truncate(&self, length: usize) -> KResult<()> {
548-
let lock = Task::block_on(self.rwsem.write());
548+
let _lock = Task::block_on(self.rwsem.write());
549549
Task::block_on(self.pages.resize(length))?;
550550
self.size.store(length as u64, Ordering::Relaxed);
551551
*self.mtime.lock() = Instant::now();

src/kernel/mem.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ pub use access::{AsMemoryBlock, MemoryBlock, PhysAccess};
1212
pub(self) use mm_area::MMArea;
1313
pub use mm_list::{handle_kernel_page_fault, FileMapping, MMList, Mapping, Permission};
1414
pub use page_alloc::{GlobalPageAlloc, RawPage};
15-
pub use page_cache::{CachePage, PageCache, PageCacheBackend};
15+
pub use page_cache::{CachePage, CachePageStream, PageCache, PageCacheBackend};
1616
pub use paging::{Page, PageBuffer};

src/kernel/mem/page_cache.rs

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,27 +125,32 @@ impl PageCache {
125125

126126
pub async fn read(&self, buffer: &mut dyn Buffer, mut offset: usize) -> KResult<usize> {
127127
let mut pages = self.pages.lock().await;
128+
let size = self.backend.upgrade().unwrap().size();
128129

129130
loop {
131+
if offset >= size {
132+
break;
133+
}
130134
let page_id = offset >> PAGE_SIZE_BITS;
131135
let page = pages.get(&page_id);
132136

133137
match page {
134138
Some(page) => {
135139
let inner_offset = offset % PAGE_SIZE;
140+
let available_in_file = size.saturating_sub(offset);
136141

137142
// TODO: still cause unnecessary IO if valid_size < PAGESIZE
138143
// and fill result is Done
139-
if page.valid_size() == 0
140-
|| buffer
141-
.fill(&page.valid_data()[inner_offset..])?
142-
.should_stop()
144+
let page_data = &page.valid_data()[inner_offset..];
145+
let read_size = page_data.len().min(available_in_file);
146+
147+
if read_size == 0
148+
|| buffer.fill(&page_data[..read_size])?.should_stop()
143149
|| buffer.available() == 0
144150
{
145151
break;
146152
}
147-
148-
offset += PAGE_SIZE - inner_offset;
153+
offset += read_size;
149154
}
150155
None => {
151156
let mut new_page = CachePage::new();
@@ -217,7 +222,7 @@ impl PageCache {
217222
self.backend
218223
.upgrade()
219224
.unwrap()
220-
.write_page(page, page_id << PAGE_SIZE_BITS)?;
225+
.write_page(&mut CachePageStream::new(*page), page_id << PAGE_SIZE_BITS)?;
221226
page.clear_dirty();
222227
}
223228
}
@@ -293,14 +298,59 @@ impl PageCache {
293298
}
294299
}
295300

301+
pub struct CachePageStream {
302+
page: CachePage,
303+
cur: usize,
304+
}
305+
306+
impl CachePageStream {
307+
pub fn new(page: CachePage) -> Self {
308+
Self { page, cur: 0 }
309+
}
310+
311+
pub fn remaining(&self) -> usize {
312+
self.page.valid_size().saturating_sub(self.cur)
313+
}
314+
315+
pub fn is_drained(&self) -> bool {
316+
self.cur >= self.page.valid_size()
317+
}
318+
}
319+
320+
impl Stream for CachePageStream {
321+
fn poll_data<'a>(&mut self, buf: &'a mut [u8]) -> KResult<Option<&'a mut [u8]>> {
322+
if self.cur >= self.page.valid_size() {
323+
return Ok(None);
324+
}
325+
326+
let page_data = &self.page.all()[self.cur..self.page.valid_size()];
327+
let to_read = buf.len().min(page_data.len());
328+
329+
buf[..to_read].copy_from_slice(&page_data[..to_read]);
330+
self.cur += to_read;
331+
332+
Ok(Some(&mut buf[..to_read]))
333+
}
334+
335+
fn ignore(&mut self, len: usize) -> KResult<Option<usize>> {
336+
if self.cur >= self.page.valid_size() {
337+
return Ok(None);
338+
}
339+
340+
let to_ignore = len.min(self.page.valid_size() - self.cur);
341+
self.cur += to_ignore;
342+
Ok(Some(to_ignore))
343+
}
344+
}
345+
296346
// with this trait, "page cache" and "block cache" are unified,
297347
// for fs, offset is file offset (floor algin to PAGE_SIZE)
298348
// for blkdev, offset is block idx (floor align to PAGE_SIZE / BLK_SIZE)
299349
// Oh no, this would make unnecessary cache
300350
pub trait PageCacheBackend {
301351
fn read_page(&self, page: &mut CachePage, offset: usize) -> KResult<usize>;
302352

303-
fn write_page(&self, page: &CachePage, offset: usize) -> KResult<usize>;
353+
fn write_page(&self, page: &mut CachePageStream, offset: usize) -> KResult<usize>;
304354

305355
fn size(&self) -> usize;
306356
}

src/kernel/vfs/inode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ pub trait Inode: Send + Sync + InodeInner + Any {
136136
Err(if self.is_dir() { EISDIR } else { EINVAL })
137137
}
138138

139-
fn write_direct(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult<usize> {
139+
fn write_direct(&self, stream: &mut dyn Stream, offset: usize) -> KResult<usize> {
140140
Err(if self.is_dir() { EISDIR } else { EINVAL })
141141
}
142142

0 commit comments

Comments
 (0)