101 lines
3.3 KiB
Rust
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);
|
|
}
|
|
}
|
|
}
|