sysutils: add cpu/memory information to top
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
use core::{any::Any, marker::PhantomData, sync::atomic::AtomicBool};
|
||||
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{FileMode, OpenOptions},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
fs::sysfs::{attribute::Attribute, object::KObject},
|
||||
vfs::{CommonImpl, Filename, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl},
|
||||
};
|
||||
|
||||
pub trait IntegerAttributeValue: Sized + Send + Sync + 'static {
|
||||
fn from_raw(data: &[u8]) -> Result<Self, Error>;
|
||||
fn into_raw(&self, format: IntegerAttributeFormat) -> Result<Vec<u8>, Error>;
|
||||
}
|
||||
|
||||
pub enum IntegerAttributeFormat {
|
||||
Decimal,
|
||||
Hex,
|
||||
Octal,
|
||||
}
|
||||
|
||||
pub trait IntegerAttributeOps<N: IntegerAttributeValue>: Sync + Send + 'static {
|
||||
type Data: Send + 'static = ();
|
||||
|
||||
const WRITEABLE: bool = false;
|
||||
const NAME: &'static str;
|
||||
const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Decimal;
|
||||
|
||||
fn read(state: &Self::Data) -> Result<N, Error> {
|
||||
let _ = state;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
fn write(state: &Self::Data, value: N) -> Result<(), Error> {
|
||||
let _ = state;
|
||||
let _ = value;
|
||||
Err(Error::ReadOnly)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IntegerAttribute<N: IntegerAttributeValue, V: IntegerAttributeOps<N>>(
|
||||
PhantomData<(V, N)>,
|
||||
);
|
||||
|
||||
struct IntegerAttributeNode<N: IntegerAttributeValue, V: IntegerAttributeOps<N>> {
|
||||
object: Arc<KObject<V::Data>>,
|
||||
_pd: PhantomData<(V, N)>,
|
||||
}
|
||||
|
||||
struct IntegerAttributeState {
|
||||
value: IrqSafeRwLock<Vec<u8>>,
|
||||
#[allow(unused)]
|
||||
modified: AtomicBool,
|
||||
}
|
||||
|
||||
macro_rules! impl_integer_value {
|
||||
($($ty:ty),+ $(,)?) => {
|
||||
$(
|
||||
impl IntegerAttributeValue for $ty {
|
||||
fn from_raw(_data: &[u8]) -> Result<Self, Error> {
|
||||
todo!("IntegerAttributeValue::from_raw()")
|
||||
}
|
||||
fn into_raw(&self, format: IntegerAttributeFormat) -> Result<Vec<u8>, Error> {
|
||||
let string = match format {
|
||||
IntegerAttributeFormat::Decimal => alloc::format!("{self}\n"),
|
||||
IntegerAttributeFormat::Hex => alloc::format!("{self:#x}\n"),
|
||||
IntegerAttributeFormat::Octal => alloc::format!("{self:#o}\n")
|
||||
};
|
||||
Ok(string.into_bytes())
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
impl_integer_value!(u8, u16, u32, u64, i8, i16, i32, i64);
|
||||
|
||||
impl<N: IntegerAttributeValue, V: IntegerAttributeOps<N>> CommonImpl
|
||||
for IntegerAttributeNode<N, V>
|
||||
{
|
||||
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: IntegerAttributeValue, V: IntegerAttributeOps<N>> RegularImpl
|
||||
for IntegerAttributeNode<N, V>
|
||||
{
|
||||
fn open(
|
||||
&self,
|
||||
_node: &NodeRef,
|
||||
opts: OpenOptions,
|
||||
) -> Result<(u64, Option<InstanceData>), Error> {
|
||||
if opts.contains(OpenOptions::WRITE) {
|
||||
// && !V::WRITEABLE {
|
||||
return Err(Error::ReadOnly);
|
||||
}
|
||||
|
||||
let value = V::read(self.object.data())?.into_raw(V::FORMAT)?;
|
||||
|
||||
let instance = IntegerAttributeState {
|
||||
value: IrqSafeRwLock::new(value),
|
||||
modified: AtomicBool::new(false),
|
||||
};
|
||||
|
||||
Ok((0, Some(Arc::new(instance))))
|
||||
}
|
||||
|
||||
fn close(&self, _node: &NodeRef, _instance: Option<&InstanceData>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
// if V::WRITEABLE {
|
||||
// let instance = instance.ok_or(Error::InvalidFile)?;
|
||||
// let instance = instance
|
||||
// .downcast_ref::<StringAttributeState>()
|
||||
// .ok_or(Error::InvalidFile)?;
|
||||
|
||||
// if instance.modified.load(Ordering::Acquire) {
|
||||
// let value = instance.value.read();
|
||||
// let value_str =
|
||||
// core::str::from_utf8(&value[..]).map_err(|_| Error::InvalidArgument)?;
|
||||
|
||||
// // Trim whitespace and newlines
|
||||
// V::write(&self.object.data, value_str.trim())?;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Ok(())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
_node: &NodeRef,
|
||||
instance: Option<&InstanceData>,
|
||||
pos: u64,
|
||||
buf: &mut [u8],
|
||||
) -> Result<usize, Error> {
|
||||
let instance = instance.ok_or(Error::InvalidFile)?;
|
||||
let instance = instance
|
||||
.downcast_ref::<IntegerAttributeState>()
|
||||
.ok_or(Error::InvalidFile)?;
|
||||
|
||||
let value = instance.value.read();
|
||||
let len = value.len();
|
||||
if pos >= len as u64 {
|
||||
return Ok(0);
|
||||
}
|
||||
let pos = pos as usize;
|
||||
let amount = (len - pos).min(buf.len());
|
||||
buf[..amount].copy_from_slice(&value[pos..pos + amount]);
|
||||
|
||||
Ok(amount)
|
||||
}
|
||||
|
||||
fn write(
|
||||
&self,
|
||||
_node: &NodeRef,
|
||||
_instance: Option<&InstanceData>,
|
||||
_pos: u64,
|
||||
_buf: &[u8],
|
||||
) -> Result<usize, Error> {
|
||||
todo!("Integer attribute write")
|
||||
}
|
||||
|
||||
fn truncate(&self, _node: &NodeRef, _new_size: u64) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: IntegerAttributeValue, V: IntegerAttributeOps<N>> From<V> for IntegerAttribute<N, V> {
|
||||
fn from(_value: V) -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: IntegerAttributeValue, V: IntegerAttributeOps<N>> Attribute<V::Data>
|
||||
for IntegerAttribute<N, V>
|
||||
{
|
||||
fn instantiate(&self, parent: &Arc<KObject<V::Data>>) -> Result<Arc<Node>, Error> {
|
||||
let mode = match V::WRITEABLE {
|
||||
false => FileMode::new(0o444),
|
||||
true => FileMode::new(0o644),
|
||||
};
|
||||
|
||||
Ok(Node::regular(
|
||||
IntegerAttributeNode {
|
||||
object: parent.clone(),
|
||||
_pd: PhantomData::<(V, N)>,
|
||||
},
|
||||
NodeFlags::IN_MEMORY_PROPS,
|
||||
Some(Metadata::now_root(mode, 0)),
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
// TODO implement this properly
|
||||
fn name(&self) -> &Filename {
|
||||
unsafe { Filename::from_str_unchecked(V::NAME) }
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use crate::vfs::{Filename, NodeRef};
|
||||
use super::object::KObject;
|
||||
|
||||
mod bytes;
|
||||
mod integer;
|
||||
mod string;
|
||||
|
||||
pub trait Attribute<D>: Sync + Send {
|
||||
@@ -14,4 +15,7 @@ pub trait Attribute<D>: Sync + Send {
|
||||
}
|
||||
|
||||
pub use bytes::{BytesAttribute, BytesAttributeOps};
|
||||
pub use integer::{
|
||||
IntegerAttribute, IntegerAttributeFormat, IntegerAttributeOps, IntegerAttributeValue,
|
||||
};
|
||||
pub use string::{StringAttribute, StringAttributeOps};
|
||||
|
||||
@@ -11,9 +11,14 @@ use kernel_arch::{
|
||||
task::{Scheduler, TaskContext},
|
||||
};
|
||||
use libk_util::{OneTimeInit, sync::IrqGuard};
|
||||
use yggdrasil_abi::time::SystemTime;
|
||||
use yggdrasil_abi::{error::Error, time::SystemTime};
|
||||
|
||||
use crate::{
|
||||
fs::sysfs::{
|
||||
self,
|
||||
attribute::{IntegerAttribute, IntegerAttributeOps},
|
||||
object::KObject,
|
||||
},
|
||||
task::{TaskContextImpl, ThreadId, ThreadState, thread::Thread},
|
||||
time::monotonic_time,
|
||||
};
|
||||
@@ -131,7 +136,7 @@ impl CpuQueue {
|
||||
&self.stats.idle
|
||||
};
|
||||
|
||||
let t = delta.as_millis() as u64;
|
||||
let t = delta.as_nanos() as u64;
|
||||
|
||||
self.stats.total.fetch_add(t, Ordering::Relaxed);
|
||||
counter.fetch_add(t, Ordering::Relaxed);
|
||||
@@ -254,6 +259,74 @@ impl Scheduler for CpuQueue {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_sysfs() {
|
||||
fn register_cpu_node(cpu: usize, node: Arc<KObject<usize>>) {
|
||||
static KERNEL_SCHED: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||
|
||||
let Ok(kernel_sched) = KERNEL_SCHED.or_try_init_with(|| {
|
||||
let kernel = sysfs::kernel().ok_or(Error::DoesNotExist)?;
|
||||
let node = KObject::new(());
|
||||
kernel.add_object("sched", node.clone())?;
|
||||
Ok(node)
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let name = alloc::format!("{cpu}");
|
||||
kernel_sched.add_object(name, node).ok();
|
||||
}
|
||||
|
||||
struct CpuIdle;
|
||||
struct CpuKernel;
|
||||
struct CpuUser;
|
||||
struct CpuTotal;
|
||||
|
||||
impl IntegerAttributeOps<u64> for CpuIdle {
|
||||
type Data = usize;
|
||||
const NAME: &'static str = "idle";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<u64, Error> {
|
||||
Ok(stats(*state).idle.load(Ordering::Acquire))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntegerAttributeOps<u64> for CpuTotal {
|
||||
type Data = usize;
|
||||
const NAME: &'static str = "total";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<u64, Error> {
|
||||
Ok(stats(*state).total.load(Ordering::Acquire))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntegerAttributeOps<u64> for CpuUser {
|
||||
type Data = usize;
|
||||
const NAME: &'static str = "user";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<u64, Error> {
|
||||
Ok(stats(*state).user.load(Ordering::Acquire))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntegerAttributeOps<u64> for CpuKernel {
|
||||
type Data = usize;
|
||||
const NAME: &'static str = "kernel";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<u64, Error> {
|
||||
Ok(stats(*state).kernel.load(Ordering::Acquire))
|
||||
}
|
||||
}
|
||||
|
||||
for cpu in 0..ArchitectureImpl::cpu_count() {
|
||||
let node = KObject::new(cpu);
|
||||
node.add_attribute(IntegerAttribute::from(CpuIdle)).ok();
|
||||
node.add_attribute(IntegerAttribute::from(CpuTotal)).ok();
|
||||
node.add_attribute(IntegerAttribute::from(CpuUser)).ok();
|
||||
node.add_attribute(IntegerAttribute::from(CpuKernel)).ok();
|
||||
register_cpu_node(cpu, node);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stats(index: usize) -> &'static SchedulerStats {
|
||||
&QUEUES.get()[index].stats
|
||||
}
|
||||
|
||||
+3
-1
@@ -6,7 +6,7 @@ use libk::{
|
||||
device::display::console,
|
||||
fs::devfs,
|
||||
random,
|
||||
task::{binary::LoadOptions, process::Process, runtime, thread::Thread},
|
||||
task::{binary::LoadOptions, process::Process, runtime, sched, thread::Thread},
|
||||
vfs::{IoContext, NodeRef, OwnedFilename, impls::fn_hardlink},
|
||||
};
|
||||
use memfs::MemoryFilesystem;
|
||||
@@ -69,6 +69,8 @@ pub fn kinit() -> Result<(), Error> {
|
||||
runtime::spawn(ygg_driver_usb::bus::bus_handler())?;
|
||||
runtime::spawn(console::flush_consoles_task()).ok();
|
||||
|
||||
sched::setup_sysfs();
|
||||
|
||||
devfs::root()
|
||||
.add_child(
|
||||
OwnedFilename::new("tty").unwrap(),
|
||||
|
||||
Reference in New Issue
Block a user