Skip to content

Commit c02d2b9

Browse files
committed
rust: fs: introduce FileSystem::read_folio
Allow Rust file systems to create regular file inodes backed by the page cache. The contents of such files are read into folios via `read_folio`. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent 5e601b9 commit c02d2b9

File tree

3 files changed

+122
-23
lines changed

3 files changed

+122
-23
lines changed

rust/kernel/folio.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ impl LockedFolio<'_> {
123123
/// Callers must ensure that the folio is valid and locked. Additionally, that the
124124
/// responsibility of unlocking is transferred to the new instance of [`LockedFolio`]. Lastly,
125125
/// that the returned [`LockedFolio`] doesn't outlive the refcount that keeps it alive.
126-
#[allow(dead_code)]
127126
pub(crate) unsafe fn from_raw(folio: *const bindings::folio) -> Self {
128127
let ptr = folio.cast();
129128
// SAFETY: The safety requirements ensure that `folio` (from which `ptr` is derived) is

rust/kernel/fs.rs

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
99
use crate::error::{code::*, from_result, to_result, Error, Result};
1010
use crate::types::{ARef, AlwaysRefCounted, Either, Opaque};
11-
use crate::{bindings, init::PinInit, str::CStr, time::Timespec, try_pin_init, ThisModule};
11+
use crate::{
12+
bindings, folio::LockedFolio, init::PinInit, str::CStr, time::Timespec, try_pin_init,
13+
ThisModule,
14+
};
1215
use core::{marker::PhantomData, marker::PhantomPinned, mem::ManuallyDrop, pin::Pin, ptr};
1316
use macros::{pin_data, pinned_drop};
1417

@@ -36,6 +39,9 @@ pub trait FileSystem {
3639

3740
/// Returns the inode corresponding to the directory entry with the given name.
3841
fn lookup(parent: &INode<Self>, name: &[u8]) -> Result<ARef<INode<Self>>>;
42+
43+
/// Reads the contents of the inode into the given folio.
44+
fn read_folio(inode: &INode<Self>, folio: LockedFolio<'_>) -> Result;
3945
}
4046

4147
/// The types of directory entries reported by [`FileSystem::read_dir`].
@@ -74,6 +80,7 @@ impl From<INodeType> for DirEntryType {
7480
fn from(value: INodeType) -> Self {
7581
match value {
7682
INodeType::Dir => DirEntryType::Dir,
83+
INodeType::Reg => DirEntryType::Reg,
7784
}
7885
}
7986
}
@@ -232,6 +239,15 @@ impl<T: FileSystem + ?Sized> NewINode<T> {
232239
inode.i_op = &Tables::<T>::DIR_INODE_OPERATIONS;
233240
bindings::S_IFDIR
234241
}
242+
INodeType::Reg => {
243+
// SAFETY: `generic_ro_fops` never changes, it's safe to reference it.
244+
inode.__bindgen_anon_3.i_fop = unsafe { &bindings::generic_ro_fops };
245+
inode.i_data.a_ops = &Tables::<T>::FILE_ADDRESS_SPACE_OPERATIONS;
246+
247+
// SAFETY: The `i_mapping` pointer doesn't change and is valid.
248+
unsafe { bindings::mapping_set_large_folios(inode.i_mapping) };
249+
bindings::S_IFREG
250+
}
235251
};
236252

237253
inode.i_mode = (params.mode & 0o777) | u16::try_from(mode)?;
@@ -268,6 +284,9 @@ impl<T: FileSystem + ?Sized> Drop for NewINode<T> {
268284
pub enum INodeType {
269285
/// Directory type.
270286
Dir,
287+
288+
/// Regular file type.
289+
Reg,
271290
}
272291

273292
/// Required inode parameters.
@@ -588,6 +607,55 @@ impl<T: FileSystem + ?Sized> Tables<T> {
588607
},
589608
}
590609
}
610+
611+
const FILE_ADDRESS_SPACE_OPERATIONS: bindings::address_space_operations =
612+
bindings::address_space_operations {
613+
writepage: None,
614+
read_folio: Some(Self::read_folio_callback),
615+
writepages: None,
616+
dirty_folio: None,
617+
readahead: None,
618+
write_begin: None,
619+
write_end: None,
620+
bmap: None,
621+
invalidate_folio: None,
622+
release_folio: None,
623+
free_folio: None,
624+
direct_IO: None,
625+
migrate_folio: None,
626+
launder_folio: None,
627+
is_partially_uptodate: None,
628+
is_dirty_writeback: None,
629+
error_remove_page: None,
630+
swap_activate: None,
631+
swap_deactivate: None,
632+
swap_rw: None,
633+
};
634+
635+
extern "C" fn read_folio_callback(
636+
_file: *mut bindings::file,
637+
folio: *mut bindings::folio,
638+
) -> i32 {
639+
from_result(|| {
640+
// SAFETY: All pointers are valid and stable.
641+
let inode = unsafe {
642+
&*(*(*folio)
643+
.__bindgen_anon_1
644+
.page
645+
.__bindgen_anon_1
646+
.__bindgen_anon_1
647+
.mapping)
648+
.host
649+
.cast::<INode<T>>()
650+
};
651+
652+
// SAFETY: The C contract guarantees that the folio is valid and locked, with ownership
653+
// of the lock transferred to the callee (this function). The folio is also guaranteed
654+
// not to outlive this function.
655+
T::read_folio(inode, unsafe { LockedFolio::from_raw(folio) })?;
656+
Ok(0)
657+
})
658+
}
591659
}
592660

593661
/// Directory entry emitter.
@@ -673,7 +741,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
673741
/// # mod module_fs_sample {
674742
/// use kernel::fs::{DirEmitter, INode, NewSuperBlock, SuperBlock, SuperParams};
675743
/// use kernel::prelude::*;
676-
/// use kernel::{c_str, fs, types::ARef};
744+
/// use kernel::{c_str, folio::LockedFolio, fs, types::ARef};
677745
///
678746
/// kernel::module_fs! {
679747
/// type: MyFs,
@@ -698,6 +766,9 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
698766
/// fn lookup(_: &INode<Self>, _: &[u8]) -> Result<ARef<INode<Self>>> {
699767
/// todo!()
700768
/// }
769+
/// fn read_folio(_: &INode<Self>, _: LockedFolio<'_>) -> Result {
770+
/// todo!()
771+
/// }
701772
/// }
702773
/// # }
703774
/// ```

samples/rust/rust_rofs.rs

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use kernel::fs::{
66
DirEmitter, INode, INodeParams, INodeType, NewSuperBlock, SuperBlock, SuperParams,
77
};
88
use kernel::prelude::*;
9-
use kernel::{c_str, fs, time::UNIX_EPOCH, types::ARef, types::Either};
9+
use kernel::{c_str, folio::LockedFolio, fs, time::UNIX_EPOCH, types::ARef, types::Either};
1010

1111
kernel::module_fs! {
1212
type: RoFs,
@@ -20,23 +20,27 @@ struct Entry {
2020
name: &'static [u8],
2121
ino: u64,
2222
etype: INodeType,
23+
contents: &'static [u8],
2324
}
2425

2526
const ENTRIES: [Entry; 3] = [
2627
Entry {
2728
name: b".",
2829
ino: 1,
2930
etype: INodeType::Dir,
31+
contents: b"",
3032
},
3133
Entry {
3234
name: b"..",
3335
ino: 1,
3436
etype: INodeType::Dir,
37+
contents: b"",
3538
},
3639
Entry {
37-
name: b"subdir",
40+
name: b"test.txt",
3841
ino: 2,
39-
etype: INodeType::Dir,
42+
etype: INodeType::Reg,
43+
contents: b"hello\n",
4044
},
4145
];
4246

@@ -95,23 +99,48 @@ impl fs::FileSystem for RoFs {
9599
return Err(ENOENT);
96100
}
97101

98-
match name {
99-
b"subdir" => match parent.super_block().get_or_create_inode(2)? {
100-
Either::Left(existing) => Ok(existing),
101-
Either::Right(new) => new.init(INodeParams {
102-
typ: INodeType::Dir,
103-
mode: 0o555,
104-
size: 0,
105-
blocks: 1,
106-
nlink: 2,
107-
uid: 0,
108-
gid: 0,
109-
atime: UNIX_EPOCH,
110-
ctime: UNIX_EPOCH,
111-
mtime: UNIX_EPOCH,
112-
}),
113-
},
114-
_ => Err(ENOENT),
102+
for e in &ENTRIES {
103+
if name == e.name {
104+
return match parent.super_block().get_or_create_inode(e.ino)? {
105+
Either::Left(existing) => Ok(existing),
106+
Either::Right(new) => new.init(INodeParams {
107+
typ: e.etype,
108+
mode: 0o444,
109+
size: e.contents.len().try_into()?,
110+
blocks: 1,
111+
nlink: 1,
112+
uid: 0,
113+
gid: 0,
114+
atime: UNIX_EPOCH,
115+
ctime: UNIX_EPOCH,
116+
mtime: UNIX_EPOCH,
117+
}),
118+
};
119+
}
115120
}
121+
122+
Err(ENOENT)
123+
}
124+
125+
fn read_folio(inode: &INode<Self>, mut folio: LockedFolio<'_>) -> Result {
126+
let data = match inode.ino() {
127+
2 => ENTRIES[2].contents,
128+
_ => return Err(EINVAL),
129+
};
130+
131+
let pos = usize::try_from(folio.pos()).unwrap_or(usize::MAX);
132+
let copied = if pos >= data.len() {
133+
0
134+
} else {
135+
let to_copy = core::cmp::min(data.len() - pos, folio.size());
136+
folio.write(0, &data[pos..][..to_copy])?;
137+
to_copy
138+
};
139+
140+
folio.zero_out(copied, folio.size() - copied)?;
141+
folio.mark_uptodate();
142+
folio.flush_dcache();
143+
144+
Ok(())
116145
}
117146
}

0 commit comments

Comments
 (0)