1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//! Per-thread perf admission and scheduler-list ownership.
//!
//! Linux serializes `perf_event_open()` against `perf_event_exit_task()` with
//! the task perf mutex and a tombstoned context. This object is the equivalent
//! Starry boundary: attach either commits before the exit snapshot or observes
//! the tombstone and returns `ESRCH`.
use alloc::sync::Arc;
use core::sync::atomic::{AtomicUsize, Ordering};
use super::{
task::PerTaskCounter,
task_context_state::{PerfAttachError, PerfTaskContextState},
};
use crate::sync::IrqMutex;
const PERF_COUNTER_CAPACITY: usize = 32;
/// Number of task counters with a committed scheduler-list reservation.
pub(super) static PERF_TASK_ACTIVE: AtomicUsize = AtomicUsize::new(0);
/// One task's fixed, IRQ-safe scheduler list and exit admission state.
pub(crate) struct ThreadPerfContext {
state: IrqMutex<PerfTaskContextState<Arc<PerTaskCounter>, PERF_COUNTER_CAPACITY>>,
}
impl ThreadPerfContext {
/// Creates an empty context that accepts event installation.
pub(crate) const fn new() -> Self {
Self {
state: IrqMutex::new(PerfTaskContextState::new()),
}
}
/// Commits one scheduler-visible counter or rejects a tombstoned task.
pub(crate) fn attach(&self, counter: Arc<PerTaskCounter>) -> crate::StarryResult<()> {
let mut state = self.state.lock();
// Closed events may remain family-owned for aggregate reads. Reclaim
// their list slots in task context before admitting a new live event.
state.retain(|counter| !counter.resources_released());
state
.attach(Arc::clone(&counter))
.map_err(|error| match error {
PerfAttachError::Closed => crate::StarryError::NoSuchProcess,
PerfAttachError::Full => crate::StarryError::NoMemory,
})?;
assert!(
counter.publish_scheduler_registration(),
"only a reserved PMU counter may enter a task perf context"
);
// Publish the global fast-path key while the list lock is still held.
// A scheduler that observes the increment then either sees the entry or
// waits for this bounded publication transaction to finish.
PERF_TASK_ACTIVE.fetch_add(1, Ordering::AcqRel);
Ok(())
}
/// Withdraws an attached counter whose wider publication failed.
pub(crate) fn detach_unpublished(&self, counter: &Arc<PerTaskCounter>) {
let removed = self
.state
.lock()
.remove(|candidate| Arc::ptr_eq(candidate, counter));
// A later attach may reclaim this list slot after `free_hw` completes
// but before the failed opener reaches its final detach. Missing is
// therefore valid only after the exact PMU resource was quiesced.
assert!(
removed || counter.resources_released(),
"a live unpublished perf counter lost its local reservation"
);
}
/// Snapshots counters for task-context sideband/control work.
pub(crate) fn snapshot(&self) -> heapless::Vec<Arc<PerTaskCounter>, PERF_COUNTER_CAPACITY> {
self.state.lock().snapshot()
}
/// Snapshots a live parent context for pre-publication inheritance.
pub(crate) fn snapshot_for_inherit(
&self,
) -> Option<heapless::Vec<Arc<PerTaskCounter>, PERF_COUNTER_CAPACITY>> {
self.state.lock().snapshot_if_accepting()
}
/// Permanently rejects later opens and returns the complete exit snapshot.
pub(crate) fn close_and_snapshot(
&self,
) -> heapless::Vec<Arc<PerTaskCounter>, PERF_COUNTER_CAPACITY> {
self.state.lock().close_snapshot()
}
/// Runs one bounded scheduler hook while retaining list stability.
pub(crate) fn with_counters<R>(
&self,
operation: impl FnOnce(&[Arc<PerTaskCounter>]) -> R,
) -> R {
let state = self.state.lock();
operation(state.counters())
}
}