feat(kernel): per-process budgets (pages, endpoints, notifs, regions) carved from creator, returned on death; init owns pool
This commit is contained in:
100
crates/kernel/src/budget.rs
Normal file
100
crates/kernel/src/budget.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
//! Per-process budgets (see `domain::budget`): carved from the creator,
|
||||
//! charged as a process takes frames and kernel objects, returned on death.
|
||||
|
||||
use domain::{
|
||||
budget::{Budget, Limits},
|
||||
capability::CapabilityTable,
|
||||
error::KernelError,
|
||||
memory::quota::Quota,
|
||||
};
|
||||
use platform::Platform;
|
||||
use types::id::ProcessId;
|
||||
|
||||
use crate::Kernel;
|
||||
|
||||
/// Picks the part of a budget an object counts against.
|
||||
pub(crate) type Part = fn(&mut Budget) -> &mut Quota;
|
||||
|
||||
pub(crate) const ENDPOINTS: Part = |b| &mut b.endpoints;
|
||||
pub(crate) const NOTIFICATIONS: Part = |b| &mut b.notifications;
|
||||
pub(crate) const REGIONS: Part = |b| &mut b.regions;
|
||||
|
||||
impl<P: Platform> Kernel<P> {
|
||||
pub(crate) fn budget_mut(&mut self, pid: ProcessId) -> Result<&mut Budget, KernelError> {
|
||||
self.budgets
|
||||
.get_mut(pid.raw() as usize)
|
||||
.ok_or(KernelError::InvalidProcess(pid))
|
||||
}
|
||||
|
||||
pub fn limits_of(&self, pid: ProcessId) -> Option<Limits> {
|
||||
self.processes.get(pid).ok()?;
|
||||
self.budgets.get(pid.raw() as usize).map(Budget::limits)
|
||||
}
|
||||
|
||||
/// A new process holding `limits`, carved from `parent`'s unused budget;
|
||||
/// only boot makes one without a parent.
|
||||
pub(crate) fn new_process(
|
||||
&mut self,
|
||||
parent: Option<ProcessId>,
|
||||
limits: Limits,
|
||||
) -> Result<ProcessId, KernelError> {
|
||||
if let Some(p) = parent {
|
||||
self.processes.get(p)?;
|
||||
if !self.budget_mut(p)?.carve(limits) {
|
||||
return Err(KernelError::OutOfMemory);
|
||||
}
|
||||
}
|
||||
let pid = match self.processes.create(CapabilityTable::new(), None) {
|
||||
Ok(pid) => pid,
|
||||
Err(e) => {
|
||||
if let Some(p) = parent {
|
||||
self.budget_mut(p)?.refund(limits);
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
*self.budget_mut(pid)? = Budget::new(limits);
|
||||
if let Some(slot) = self.parents.get_mut(pid.raw() as usize) {
|
||||
*slot = parent;
|
||||
}
|
||||
if let Err(e) = self.create_address_space(pid) {
|
||||
self.release_budget(pid);
|
||||
let _ = self.processes.destroy(pid);
|
||||
return Err(e);
|
||||
}
|
||||
Ok(pid)
|
||||
}
|
||||
|
||||
/// Gives a dead process's limits back to its parent; its children are
|
||||
/// carved from what it held, so they pass to that parent too.
|
||||
pub(crate) fn release_budget(&mut self, pid: ProcessId) {
|
||||
let i = pid.raw() as usize;
|
||||
let parent = self.parents.get_mut(i).and_then(Option::take);
|
||||
let limits = self
|
||||
.budgets
|
||||
.get_mut(i)
|
||||
.map(|b| core::mem::replace(b, Budget::EMPTY).limits());
|
||||
if let (Some(p), Some(limits)) = (parent, limits)
|
||||
&& let Ok(b) = self.budget_mut(p)
|
||||
{
|
||||
b.refund(limits);
|
||||
}
|
||||
for slot in self.parents.iter_mut().filter(|s| **s == Some(pid)) {
|
||||
*slot = parent;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn charge_object(&mut self, pid: ProcessId, part: Part) -> Result<(), KernelError> {
|
||||
if part(self.budget_mut(pid)?).charge(1) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(KernelError::NoFreeSlot)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn uncharge_object(&mut self, pid: ProcessId, part: Part) {
|
||||
if let Ok(b) = self.budget_mut(pid) {
|
||||
part(b).uncharge(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user