Skip to content

Commit

Permalink
Merge pull request tock#4039 from tock/kernel-stopped-states
Browse files Browse the repository at this point in the history
kernel: process standard: stop in YieldedFor
  • Loading branch information
ppannuto authored Jun 19, 2024
2 parents 0dcb702 + 7905d40 commit 3ccfe29
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 32 deletions.
6 changes: 1 addition & 5 deletions kernel/src/kernel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -697,11 +697,7 @@ impl Kernel {
// This is a potential security flaw: panic.
panic!("Attempted to schedule an unrunnable process");
}
process::State::StoppedRunning => {
return_reason = process::StoppedExecutingReason::Stopped;
break;
}
process::State::StoppedYielded => {
process::State::Stopped(_) => {
return_reason = process::StoppedExecutingReason::Stopped;
break;
}
Expand Down
46 changes: 30 additions & 16 deletions kernel/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -869,12 +869,12 @@ impl From<Error> for ErrorCode {
/// process states.
///
/// While a process is running, it transitions between the `Running`, `Yielded`,
/// `StoppedRunning`, and `StoppedYielded` states. If an error occurs (e.g., a
/// memory access error), the kernel faults it and either leaves it in the
/// `Faulted` state, restarts it, or takes some other action defined by the
/// kernel fault policy. If the process issues an `exit-terminate` system call,
/// it enters the `Terminated` state. If it issues an `exit-restart` system
/// call, it terminates then tries to back to a runnable state.
/// `YieldedFor`, and `Stopped` states. If an error occurs (e.g., a memory
/// access error), the kernel faults it and either leaves it in the `Faulted`
/// state, restarts it, or takes some other action defined by the kernel fault
/// policy. If the process issues an `exit-terminate` system call, it enters the
/// `Terminated` state. If it issues an `exit-restart` system call, it
/// terminates then tries to back to a runnable state.
///
/// When a process faults, it enters the `Faulted` state. To be restarted, it
/// must first transition to the `Terminated` state, which means that all of its
Expand All @@ -898,16 +898,12 @@ pub enum State {
/// upcall.
YieldedFor(UpcallId),

/// The process is stopped, and its previous state was Running. This is used
/// if the kernel forcibly stops a process when it is in the `Running`
/// state. This state indicates to the kernel not to schedule the process,
/// but if the process is to be resumed later it should be put back in the
/// running state so it will execute correctly.
StoppedRunning,

/// The process is stopped, and it was stopped while it was yielded. If this
/// process needs to be resumed it should be put back in the `Yield` state.
StoppedYielded,
/// The process is stopped and the previous state the process was in when it
/// was stopped. This is used if the kernel forcibly stops a process. This
/// state indicates to the kernel not to schedule the process, but if the
/// process is to be resumed later it should be put back in its previous
/// state so it will execute correctly.
Stopped(StoppedState),

/// The process ran, faulted while running, and is no longer runnable. For a
/// faulted process to be made runnable, it must first be terminated (to
Expand All @@ -920,6 +916,24 @@ pub enum State {
Terminated,
}

/// States a process could previously have been in when stopped.
///
/// This is public so external implementations of `Process` can re-use these
/// process stopped states.
///
/// These are recorded so the process can be returned to its previous state when
/// it is resumed.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum StoppedState {
/// The process was in the running state when it was stopped.
Running,
/// The process was in the yielded state when it was stopped.
Yielded,
/// The process was in the yielded for state when it was stopped with a
/// particular upcall it was waiting for.
YieldedFor(UpcallId),
}

/// The action the kernel should take when a process encounters a fault.
///
/// When an exception occurs during a process's execution (a common example is a
Expand Down
30 changes: 19 additions & 11 deletions kernel/src/process_standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ use crate::platform::chip::Chip;
use crate::platform::mpu::{self, MPU};
use crate::process::BinaryVersion;
use crate::process::ProcessBinary;
use crate::process::{Error, FunctionCall, FunctionCallSource, Process, State, Task};
use crate::process::{Error, FunctionCall, FunctionCallSource, Process, Task};
use crate::process::{FaultAction, ProcessCustomGrantIdentifier, ProcessId};
use crate::process::{ProcessAddresses, ProcessSizes, ShortId};
use crate::process::{State, StoppedState};
use crate::process_loading::ProcessLoadError;
use crate::process_policies::ProcessFaultPolicy;
use crate::processbuffer::{ReadOnlyProcessBuffer, ReadWriteProcessBuffer};
Expand Down Expand Up @@ -317,11 +318,7 @@ impl<C: Chip> Process for ProcessStandard<'_, C> {

fn is_running(&self) -> bool {
match self.state.get() {
State::Running
| State::Yielded
| State::YieldedFor(_)
| State::StoppedRunning
| State::StoppedYielded => true,
State::Running | State::Yielded | State::YieldedFor(_) | State::Stopped(_) => true,
_ => false,
}
}
Expand All @@ -344,16 +341,27 @@ impl<C: Chip> Process for ProcessStandard<'_, C> {

fn stop(&self) {
match self.state.get() {
State::Running => self.state.set(State::StoppedRunning),
State::Yielded => self.state.set(State::StoppedYielded),
_ => {} // Do nothing
State::Running => self.state.set(State::Stopped(StoppedState::Running)),
State::Yielded => self.state.set(State::Stopped(StoppedState::Yielded)),
State::YieldedFor(upcall_id) => self
.state
.set(State::Stopped(StoppedState::YieldedFor(upcall_id))),
State::Stopped(_stopped_state) => {
// Already stopped, nothing to do.
}
State::Faulted | State::Terminated => {
// Stop has no meaning on a inactive process.
}
}
}

fn resume(&self) {
match self.state.get() {
State::StoppedRunning => self.state.set(State::Running),
State::StoppedYielded => self.state.set(State::Yielded),
State::Stopped(stopped_state) => match stopped_state {
StoppedState::Running => self.state.set(State::Running),
StoppedState::Yielded => self.state.set(State::Yielded),
StoppedState::YieldedFor(upcall_id) => self.state.set(State::YieldedFor(upcall_id)),
},
_ => {} // Do nothing
}
}
Expand Down

0 comments on commit 3ccfe29

Please sign in to comment.