From 12ed40578d996231b6288dda8ce343b6fc743c50 Mon Sep 17 00:00:00 2001 From: Wang Siyuan Date: Mon, 22 Jul 2024 00:01:55 +0800 Subject: [PATCH] Add /proc/meminfo support --- kernel/aster-nix/src/fs/procfs/meminfo.rs | 48 +++++++++++++++++++++++ kernel/aster-nix/src/fs/procfs/mod.rs | 6 +++ ostd/src/mm/mod.rs | 1 + ostd/src/mm/page/allocator.rs | 47 +++++++++++++++++++++- ostd/src/mm/stat/mod.rs | 21 ++++++++++ 5 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 kernel/aster-nix/src/fs/procfs/meminfo.rs create mode 100644 ostd/src/mm/stat/mod.rs diff --git a/kernel/aster-nix/src/fs/procfs/meminfo.rs b/kernel/aster-nix/src/fs/procfs/meminfo.rs new file mode 100644 index 000000000..c9f00abfb --- /dev/null +++ b/kernel/aster-nix/src/fs/procfs/meminfo.rs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! This module offers `/proc/meminfo` file support, which tells the user space +//! about the memory statistics in the entire system. The definition of the +//! fields are similar to that of Linux's but there exist differences. +//! +//! Reference: + +use alloc::format; + +use ostd::mm::stat; + +use crate::{ + fs::{ + procfs::template::{FileOps, ProcFileBuilder}, + utils::Inode, + }, + prelude::*, +}; + +/// Represents the inode at `/proc/meminfo`. +pub struct MemInfoFileOps; + +impl MemInfoFileOps { + pub fn new_inode(parent: Weak) -> Arc { + ProcFileBuilder::new(Self).parent(parent).build().unwrap() + } +} + +/// Total memory in the entire system in bytes. +fn mem_total() -> usize { + stat::mem_total() +} + +/// An estimation of how much memory is available for starting new +/// applications, without disk operations. +fn mem_available() -> usize { + stat::mem_available() +} + +impl FileOps for MemInfoFileOps { + fn data(&self) -> Result> { + let total = mem_total(); + let available = mem_available(); + let output = format!("MemTotal:\t{}\nMemAvailable:\t{}\n", total, available); + Ok(output.into_bytes()) + } +} diff --git a/kernel/aster-nix/src/fs/procfs/mod.rs b/kernel/aster-nix/src/fs/procfs/mod.rs index d698df661..c60247c39 100644 --- a/kernel/aster-nix/src/fs/procfs/mod.rs +++ b/kernel/aster-nix/src/fs/procfs/mod.rs @@ -5,6 +5,7 @@ use core::sync::atomic::{AtomicU64, Ordering}; use sys::SysDirOps; use self::{ + meminfo::MemInfoFileOps, pid::PidDirOps, self_::SelfSymOps, template::{DirOps, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps}, @@ -20,6 +21,7 @@ use crate::{ }; mod filesystems; +mod meminfo; mod pid; mod self_; mod sys; @@ -102,6 +104,8 @@ impl DirOps for RootDirOps { SysDirOps::new_inode(this_ptr.clone()) } else if name == "filesystems" { FileSystemsFileOps::new_inode(this_ptr.clone()) + } else if name == "meminfo" { + MemInfoFileOps::new_inode(this_ptr.clone()) } else if let Ok(pid) = name.parse::() { let process_ref = process_table::get_process(pid).ok_or_else(|| Error::new(Errno::ENOENT))?; @@ -123,6 +127,8 @@ impl DirOps for RootDirOps { cached_children.put_entry_if_not_found("filesystems", || { FileSystemsFileOps::new_inode(this_ptr.clone()) }); + cached_children + .put_entry_if_not_found("meminfo", || MemInfoFileOps::new_inode(this_ptr.clone())); for process in process_table::process_table().iter() { let pid = process.pid().to_string(); diff --git a/ostd/src/mm/mod.rs b/ostd/src/mm/mod.rs index 3d6485ae1..3496a49db 100644 --- a/ostd/src/mm/mod.rs +++ b/ostd/src/mm/mod.rs @@ -17,6 +17,7 @@ mod offset; pub(crate) mod page; pub(crate) mod page_prop; pub(crate) mod page_table; +pub mod stat; pub mod vm_space; use alloc::vec::Vec; diff --git a/ostd/src/mm/page/allocator.rs b/ostd/src/mm/page/allocator.rs index 4aab59519..53f03a627 100644 --- a/ostd/src/mm/page/allocator.rs +++ b/ostd/src/mm/page/allocator.rs @@ -15,7 +15,47 @@ use spin::Once; use super::{cont_pages::ContPages, meta::PageMeta, Page}; use crate::{boot::memory_region::MemoryRegionType, mm::PAGE_SIZE, sync::SpinLock}; -pub(in crate::mm) static PAGE_ALLOCATOR: Once> = Once::new(); +/// FrameAllocator with a counter for allocated memory +pub(in crate::mm) struct CountingFrameAllocator { + allocator: FrameAllocator, + total: usize, + allocated: usize, +} + +impl CountingFrameAllocator { + pub fn new(allocator: FrameAllocator, total: usize) -> Self { + CountingFrameAllocator { + allocator, + total, + allocated: 0, + } + } + + pub fn alloc(&mut self, count: usize) -> Option { + match self.allocator.alloc(count) { + Some(value) => { + self.allocated += count * PAGE_SIZE; + Some(value) + } + None => None, + } + } + + pub fn dealloc(&mut self, start_frame: usize, count: usize) { + self.allocator.dealloc(start_frame, count); + self.allocated -= count * PAGE_SIZE; + } + + pub fn mem_total(&self) -> usize { + self.total + } + + pub fn mem_available(&self) -> usize { + self.total - self.allocated + } +} + +pub(in crate::mm) static PAGE_ALLOCATOR: Once> = Once::new(); /// Allocate a single page. pub(crate) fn alloc_single() -> Option> { @@ -63,6 +103,7 @@ pub(crate) fn alloc(len: usize) -> Option>> { pub(crate) fn init() { let regions = crate::boot::memory_regions(); + let mut total: usize = 0; let mut allocator = FrameAllocator::<32>::new(); for region in regions.iter() { if region.typ() == MemoryRegionType::Usable { @@ -75,6 +116,7 @@ pub(crate) fn init() { } // Add global free pages to the frame allocator. allocator.add_frame(start, end); + total += (end - start) * PAGE_SIZE; info!( "Found usable region, start:{:x}, end:{:x}", region.base(), @@ -82,5 +124,6 @@ pub(crate) fn init() { ); } } - PAGE_ALLOCATOR.call_once(|| SpinLock::new(allocator)); + let counting_allocator = CountingFrameAllocator::new(allocator, total); + PAGE_ALLOCATOR.call_once(|| SpinLock::new(counting_allocator)); } diff --git a/ostd/src/mm/stat/mod.rs b/ostd/src/mm/stat/mod.rs new file mode 100644 index 000000000..47ef1738d --- /dev/null +++ b/ostd/src/mm/stat/mod.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! APIs for memory statistics. + +use crate::mm::page::allocator::PAGE_ALLOCATOR; + +/// Total memory available for any usages in the system (in bytes). +/// +/// It would be only a slightly less than total physical memory of the system +/// in most occasions. For example, bad memory, kernel statically-allocated +/// memory or firmware reserved memories do not count. +pub fn mem_total() -> usize { + PAGE_ALLOCATOR.get().unwrap().lock().mem_total() +} + +/// Current readily available memory (in bytes). +/// +/// Such memory can be directly used for allocation without reclaiming. +pub fn mem_available() -> usize { + PAGE_ALLOCATOR.get().unwrap().lock().mem_available() +}