use super::*;
pub fn perf_sched_in(thr: &Thread) {
if PERF_TASK_ACTIVE.load(Ordering::Acquire) == 0 {
return;
}
thr.perf_context().with_counters(perf_sched_in_counters);
}
fn perf_sched_in_counters(counters: &[Arc<PerTaskCounter>]) {
if counters.is_empty() {
return;
}
let now = now_ns();
let current_cpu = PerfCpuId::new(ax_hal::percpu::this_cpu_id());
for ptc in counters.iter() {
if !ptc.enabled.load(Ordering::Acquire) {
continue;
}
if ptc.cpu_filter.is_some_and(|cpu| cpu != current_cpu) {
continue;
}
let sample_output = if ptc.is_sampling {
let Some(output) = ptc.sample_output() else {
continue;
};
Some(output)
} else {
None
};
let mut run_state = ptc.run_state.lock();
let Some(ticket) = run_state.begin_arm(current_cpu) else {
continue;
};
if let Some(output) = sample_output {
let n = ptc.programmable_index();
if let Err(error) = sampling::enable_local_pmu_irq() {
run_state.cancel_arm(ticket);
warn!(
"perf: failed to enable the PMU IRQ on CPU {}: {error:?}",
current_cpu.as_usize()
);
continue;
}
ptc.counter
.configure(ptc.programmed_event(), ptc.exclude_user, ptc.exclude_kernel)
.expect("validated task PMU counter/event pairing");
ax_cpu::pmu::counter::preload(n, ptc.sample_period);
let registration = match sampling::register(
n,
SampleSlot::new(
output,
SampleSlotConfig {
period: ptc.sample_period,
sample_type: ptc.sample_type,
id: ptc.sample_id.load(Ordering::Relaxed),
observer: ptc.observer,
freq: ptc.freq,
target_freq: ptc.freq_target,
last_time: 0,
},
),
) {
Ok(registration) => registration,
Err(error) => {
run_state.cancel_arm(ticket);
warn!(
"perf: failed to register counter {} on CPU {}: {error:?}",
n,
current_cpu.as_usize()
);
continue;
}
};
run_state.publish_registration(ticket, registration);
ax_cpu::pmu::overflow::enable_irq(n);
ax_cpu::pmu::counter::enable(n);
} else {
ptc.counter
.configure(ptc.programmed_event(), ptc.exclude_user, ptc.exclude_kernel)
.expect("validated task PMU counter/event pairing");
ptc.counter.enable();
}
ptc.last_in_ns.store(now, Ordering::Release);
run_state.finish_arm(ticket);
ptc.publish_rdpmc_active();
drop(run_state);
}
}
pub fn perf_sched_out(thr: &Thread) {
if PERF_TASK_ACTIVE.load(Ordering::Acquire) == 0 {
return;
}
thr.perf_context().with_counters(perf_sched_out_counters);
}
fn perf_sched_out_counters(counters: &[Arc<PerTaskCounter>]) {
if counters.is_empty() {
return;
}
for ptc in counters.iter() {
let Some(lease) = ptc.run_state.lock().claim_schedule_out() else {
continue;
};
stop_hardware_on_owner(ptc, lease)
.unwrap_or_else(|error| panic!("scheduler PMU stop failed: {error}"));
ptc.run_state.lock().finish_owner_stop(lease);
}
}
fn stop_hardware_on_owner(ptc: &PerTaskCounter, lease: PmuRunLease) -> crate::StarryResult<()> {
if lease.owner().as_usize() != ax_hal::percpu::this_cpu_id() {
return Err(crate::StarryError::BadState);
}
if let Some(registration) = lease.registration() {
let n = ptc.programmable_index();
if registration.counter() != n {
return Err(crate::StarryError::BadState);
}
ax_cpu::pmu::overflow::disable_irq(n);
ax_cpu::pmu::counter::disable(n);
ax_cpu::pmu::overflow::clear(1 << n);
sampling::unregister(registration).map_err(|_| crate::StarryError::BadState)?;
} else {
ptc.counter.disable();
let delta = ptc.counter.read();
ptc.accumulated.fetch_add(delta, Ordering::AcqRel);
}
let dt = now_ns().saturating_sub(ptc.last_in_ns.load(Ordering::Acquire));
ptc.time_enabled_ns.fetch_add(dt, Ordering::AcqRel);
ptc.time_running_ns.fetch_add(dt, Ordering::AcqRel);
ptc.publish_rdpmc_inactive();
Ok(())
}
pub(crate) fn stop_requested_on_owner(
ptc: &PerTaskCounter,
lease: PmuRunLease,
) -> crate::StarryResult<()> {
let claim = ptc.run_state.lock().claim_requested_stop(lease);
match claim {
PmuStopClaim::Claimed(claimed) => {
if let Err(error) = stop_hardware_on_owner(ptc, claimed) {
ptc.run_state.lock().abort_owner_stop(claimed);
return Err(error);
}
ptc.run_state.lock().finish_owner_stop(claimed);
Ok(())
}
PmuStopClaim::AlreadyComplete => Ok(()),
PmuStopClaim::InProgress => Err(crate::StarryError::ResourceBusy),
PmuStopClaim::Stale => Err(crate::StarryError::BadState),
}
}