Files
k-os/crates/kernel/src/budget.rs

101 lines
3.3 KiB
Rust

//! 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);
}
}
}