diff --git a/kernel/src/fs/configfs/test.rs b/kernel/src/fs/configfs/test.rs new file mode 100644 index 000000000..c18e8ae77 --- /dev/null +++ b/kernel/src/fs/configfs/test.rs @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Testing configfs by adding to it a top-level directory called `demo_set`, +//! whose structure is illustrated as follows: +//! +//! ``` +//! configfs/ +//! demo_set/ +//! demo_foo/ +//! attr_a +//! attr_b +//! demo_bar/ +//! attr_a +//! attr_b +//! ``` +//! +//! The `demo_set` is initially empty. One can create directories to trigger creating an in-kernel object +//! that represents a new demo. Each demo has two attributes called `attr_a` and `attr_b`. + +use alloc::{string::ToString, sync::Arc}; +use core::{ + fmt::Debug, + mem::size_of, + sync::atomic::{AtomicU32, Ordering}, +}; + +use aster_systree::{ + inherit_sys_branch_node, inherit_sys_leaf_node, BranchNodeFields, Error, NormalNodeFields, + Result, SysAttrSet, SysAttrSetBuilder, SysObj, SysPerms, SysStr, +}; +use inherit_methods_macro::inherit_methods; +use ostd::{ + mm::{VmReader, VmWriter}, + prelude::ktest, + Pod, +}; +use spin::Once; + +use crate::{ + fs::utils::{FileSystem, InodeMode, InodeType}, + time::clocks::init_for_ktest as time_init_for_ktest, +}; + +/// A demo subsystem for testing configfs functionality. +#[derive(Debug)] +struct DemoSet { + fields: BranchNodeFields, +} + +#[inherit_methods(from = "self.fields")] +impl DemoSet { + fn new() -> Arc { + let name = SysStr::from("demo_set"); + let attrs = SysAttrSet::new_empty(); + + Arc::new_cyclic(|weak_self| { + let fields = BranchNodeFields::new(name, attrs, weak_self.clone()); + DemoSet { fields } + }) + } + + fn add_child(&self, new_child: Arc) -> Result<()>; +} + +inherit_sys_branch_node!(DemoSet, fields, { + fn perms(&self) -> SysPerms { + SysPerms::DEFAULT_RW_PERMS + } + + fn create_child(&self, name: &str) -> Result> { + let demo_obj = DemoObject::new(SysStr::from(name.to_string())); + self.add_child(demo_obj.clone())?; + Ok(demo_obj) + } +}); + +/// A demo object that can be created dynamically in the configfs. +/// +/// Each demo object has two configurable attributes: `attr_a` and `attr_b`. +#[derive(Debug)] +struct DemoObject { + fields: NormalNodeFields, + attr_a: AtomicU32, + attr_b: AtomicU32, +} + +impl DemoObject { + fn new(name: SysStr) -> Arc { + let mut builder = SysAttrSetBuilder::new(); + + builder.add(SysStr::from("attr_a"), SysPerms::DEFAULT_RW_ATTR_PERMS); + builder.add(SysStr::from("attr_b"), SysPerms::DEFAULT_RW_ATTR_PERMS); + + let attrs = builder.build().expect("Failed to build attribute set"); + + Arc::new_cyclic(|weak_self| { + let fields = NormalNodeFields::new(name, attrs, weak_self.clone()); + DemoObject { + fields, + attr_a: AtomicU32::new(0), + attr_b: AtomicU32::new(0), + } + }) + } +} + +inherit_sys_leaf_node!(DemoObject, fields, { + fn perms(&self) -> SysPerms { + SysPerms::DEFAULT_RW_PERMS + } + + fn read_attr_at(&self, name: &str, _offset: usize, writer: &mut VmWriter) -> Result { + match name { + "attr_a" => { + let value = self.attr_a.load(Ordering::Relaxed); + writer.write_val(&value).unwrap(); + Ok(size_of::()) + } + "attr_b" => { + let value = self.attr_b.load(Ordering::Relaxed); + writer.write_val(&value).unwrap(); + Ok(size_of::()) + } + _ => Err(Error::AttributeError), + } + } + + fn write_attr(&self, name: &str, reader: &mut VmReader) -> Result { + match name { + "attr_a" => { + let value = reader.read_val::().unwrap(); + self.attr_a.store(value, Ordering::Relaxed); + Ok(size_of::()) + } + "attr_b" => { + let value = reader.read_val::().unwrap(); + self.attr_b.store(value, Ordering::Relaxed); + Ok(size_of::()) + } + _ => Err(Error::AttributeError), + } + } +}); + +// --- Test Setup --- + +static DEMO_SET_SUBSYSTEM: Once> = Once::new(); + +fn init_demo_subsystem() { + DEMO_SET_SUBSYSTEM.call_once(|| { + time_init_for_ktest(); + super::init_for_ktest(); + + let demo_set = DemoSet::new(); + super::register_subsystem(demo_set.clone()).unwrap(); + + demo_set + }); +} + +#[ktest] +fn test_config_fs() { + init_demo_subsystem(); + let config_fs = super::fs::ConfigFs::singleton(); + + // Access the root of configfs: /sys/kernel/config + let root_inode = config_fs.root_inode(); + + // --- Navigate to demo_set directory --- + // path: /sys/kernel/config/demo_set + let demo_set_inode = root_inode + .lookup("demo_set") + .expect("lookup demo_set failed"); + + // --- Create demo objects --- + // path: /sys/kernel/config/demo_set/demo_foo + let demo_foo = demo_set_inode + .create( + "demo_foo", + InodeType::Dir, + InodeMode::from_bits_truncate(0o755), + ) + .expect("creating demo 'demo_foo' fails"); + + // path: /sys/kernel/config/demo_set/demo_bar + let demo_bar = demo_set_inode + .create( + "demo_bar", + InodeType::Dir, + InodeMode::from_bits_truncate(0o755), + ) + .expect("creating demo 'demo_bar' fails"); + + // --- Test attribute access for demo_foo --- + let attr_a_foo = demo_foo.lookup("attr_a").expect("lookup attr_a failed"); + let attr_b_foo = demo_foo.lookup("attr_b").expect("lookup attr_b failed"); + + let mut read_buffer: u32 = 0; + + // Test attr_a read/write on demo_foo + assert!(attr_a_foo + .read_bytes_at(0, read_buffer.as_bytes_mut()) + .is_ok()); + assert_eq!(read_buffer, 0); + + let write_value_a: u32 = 42; + assert!(attr_a_foo + .write_bytes_at(0, write_value_a.as_bytes()) + .is_ok()); + assert!(attr_a_foo + .read_bytes_at(0, read_buffer.as_bytes_mut()) + .is_ok()); + assert_eq!(read_buffer, 42); + + // Test attr_b read/write on demo_foo + assert!(attr_b_foo + .read_bytes_at(0, read_buffer.as_bytes_mut()) + .is_ok()); + assert_eq!(read_buffer, 0); + + let write_value_b: u32 = 100; + assert!(attr_b_foo + .write_bytes_at(0, write_value_b.as_bytes()) + .is_ok()); + assert!(attr_b_foo + .read_bytes_at(0, read_buffer.as_bytes_mut()) + .is_ok()); + assert_eq!(read_buffer, 100); + + // --- Test attribute access for demo_bar --- + let attr_a_bar = demo_bar.lookup("attr_a").expect("lookup attr_a failed"); + + // Verify that demo_bar has independent state from demo_foo + assert!(attr_a_bar + .read_bytes_at(0, read_buffer.as_bytes_mut()) + .is_ok()); + assert_eq!(read_buffer, 0); // Should be 0, not 42 like demo_foo + + let write_value_bar: u32 = 200; + assert!(attr_a_bar + .write_bytes_at(0, write_value_bar.as_bytes()) + .is_ok()); + assert!(attr_a_bar + .read_bytes_at(0, read_buffer.as_bytes_mut()) + .is_ok()); + assert_eq!(read_buffer, 200); + + // Verify demo_foo's attr_a is still 42 + assert!(attr_a_foo + .read_bytes_at(0, read_buffer.as_bytes_mut()) + .is_ok()); + assert_eq!(read_buffer, 42); +}