use super::cpu_id::PerfCpuId;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct SampleRegistration {
owner: PerfCpuId,
counter: usize,
generation: u64,
}
impl SampleRegistration {
pub(crate) const fn new(owner: PerfCpuId, counter: usize, generation: u64) -> Self {
Self {
owner,
counter,
generation,
}
}
pub(crate) const fn owner(self) -> PerfCpuId {
self.owner
}
pub(crate) const fn counter(self) -> usize {
self.counter
}
pub(crate) const fn generation(self) -> u64 {
self.generation
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct PmuArmTicket {
owner: PerfCpuId,
generation: u64,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct PmuRunLease {
owner: PerfCpuId,
generation: u64,
registration: Option<SampleRegistration>,
}
impl PmuRunLease {
pub(crate) const fn owner(self) -> PerfCpuId {
self.owner
}
pub(crate) const fn registration(self) -> Option<SampleRegistration> {
self.registration
}
const fn generation(self) -> u64 {
self.generation
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum PmuCloseAction {
AlreadyClosed,
Complete,
Stop(PmuRunLease),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum PmuStopClaim {
Claimed(PmuRunLease),
AlreadyComplete,
InProgress,
Stale,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum PmuStopGoal {
Detach,
Close,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum PmuRunPhase {
Detached,
Arming(PmuArmTicket),
Registered(PmuArmTicket, SampleRegistration),
Running(PmuRunLease),
StopRequested(PmuRunLease, PmuStopGoal),
Stopping(PmuRunLease, PmuStopGoal),
Closed,
}
#[derive(Debug)]
pub(crate) struct PmuRunState {
phase: PmuRunPhase,
next_generation: u64,
last_stopped_generation: u64,
}
impl PmuRunState {
pub(crate) const fn new() -> Self {
Self {
phase: PmuRunPhase::Detached,
next_generation: 0,
last_stopped_generation: 0,
}
}
pub(crate) fn begin_arm(&mut self, owner: PerfCpuId) -> Option<PmuArmTicket> {
if self.phase != PmuRunPhase::Detached {
return None;
}
self.next_generation = self
.next_generation
.checked_add(1)
.expect("PMU run generation exhausted");
let ticket = PmuArmTicket {
owner,
generation: self.next_generation,
};
self.phase = PmuRunPhase::Arming(ticket);
Some(ticket)
}
pub(crate) fn publish_registration(
&mut self,
ticket: PmuArmTicket,
registration: SampleRegistration,
) {
assert_eq!(self.phase, PmuRunPhase::Arming(ticket));
self.phase = PmuRunPhase::Registered(ticket, registration);
}
pub(crate) fn finish_arm(&mut self, ticket: PmuArmTicket) {
let (observed, registration) = match self.phase {
PmuRunPhase::Arming(observed) => (observed, None),
PmuRunPhase::Registered(observed, registration) => (observed, Some(registration)),
_ => panic!("PMU arm completed from an invalid lifecycle phase"),
};
assert_eq!(observed, ticket);
self.phase = PmuRunPhase::Running(PmuRunLease {
owner: ticket.owner,
generation: ticket.generation,
registration,
});
}
pub(crate) fn cancel_arm(&mut self, ticket: PmuArmTicket) {
assert_eq!(self.phase, PmuRunPhase::Arming(ticket));
self.phase = PmuRunPhase::Detached;
}
pub(crate) const fn running(&self) -> Option<PmuRunLease> {
match self.phase {
PmuRunPhase::Running(lease)
| PmuRunPhase::StopRequested(lease, _)
| PmuRunPhase::Stopping(lease, _) => Some(lease),
_ => None,
}
}
pub(crate) fn claim_schedule_out(&mut self) -> Option<PmuRunLease> {
let (lease, goal) = match self.phase {
PmuRunPhase::Running(lease) => (lease, PmuStopGoal::Detach),
PmuRunPhase::StopRequested(lease, goal) => (lease, goal),
_ => return None,
};
self.phase = PmuRunPhase::Stopping(lease, goal);
Some(lease)
}
pub(crate) fn claim_requested_stop(&mut self, lease: PmuRunLease) -> PmuStopClaim {
match self.phase {
PmuRunPhase::StopRequested(observed, goal) if observed == lease => {
self.phase = PmuRunPhase::Stopping(observed, goal);
PmuStopClaim::Claimed(observed)
}
PmuRunPhase::Stopping(observed, _) if observed == lease => PmuStopClaim::InProgress,
PmuRunPhase::Detached | PmuRunPhase::Closed
if self.last_stopped_generation == lease.generation() =>
{
PmuStopClaim::AlreadyComplete
}
_ => PmuStopClaim::Stale,
}
}
pub(crate) fn finish_owner_stop(&mut self, lease: PmuRunLease) {
let goal = match self.phase {
PmuRunPhase::Stopping(observed, goal) if observed == lease => goal,
_ => panic!("PMU stop completed from an invalid lifecycle phase"),
};
self.last_stopped_generation = lease.generation();
self.phase = match goal {
PmuStopGoal::Detach => PmuRunPhase::Detached,
PmuStopGoal::Close => PmuRunPhase::Closed,
};
}
pub(crate) fn abort_owner_stop(&mut self, lease: PmuRunLease) {
let goal = match self.phase {
PmuRunPhase::Stopping(observed, goal) if observed == lease => goal,
_ => panic!("PMU stop aborted from an invalid lifecycle phase"),
};
self.phase = PmuRunPhase::StopRequested(lease, goal);
}
pub(crate) const fn is_stopping(&self) -> bool {
matches!(
self.phase,
PmuRunPhase::StopRequested(_, PmuStopGoal::Close)
| PmuRunPhase::Stopping(_, PmuStopGoal::Close)
| PmuRunPhase::Closed
)
}
pub(crate) fn begin_disable(&mut self) -> PmuCloseAction {
self.request_stop(PmuStopGoal::Detach)
}
pub(crate) fn begin_close(&mut self) -> PmuCloseAction {
self.request_stop(PmuStopGoal::Close)
}
fn request_stop(&mut self, requested_goal: PmuStopGoal) -> PmuCloseAction {
let (lease, phase_is_stopping, current_goal) = match self.phase {
PmuRunPhase::Registered(ticket, registration) => (
PmuRunLease {
owner: ticket.owner,
generation: ticket.generation,
registration: Some(registration),
},
false,
requested_goal,
),
PmuRunPhase::Running(lease) => (lease, false, requested_goal),
PmuRunPhase::StopRequested(lease, goal) => (lease, false, goal),
PmuRunPhase::Stopping(lease, goal) => (lease, true, goal),
PmuRunPhase::Detached => {
if requested_goal == PmuStopGoal::Close {
self.phase = PmuRunPhase::Closed;
}
return PmuCloseAction::Complete;
}
PmuRunPhase::Closed => return PmuCloseAction::AlreadyClosed,
PmuRunPhase::Arming(_) => {
panic!("PMU stop observed an arm before registry publication")
}
};
let goal = if requested_goal == PmuStopGoal::Close {
PmuStopGoal::Close
} else {
current_goal
};
self.phase = if phase_is_stopping {
PmuRunPhase::Stopping(lease, goal)
} else {
PmuRunPhase::StopRequested(lease, goal)
};
PmuCloseAction::Stop(lease)
}
}