1use alloc::sync::Arc;
2use core::sync::atomic::{AtomicBool, AtomicU8, AtomicU64, AtomicUsize, Ordering};
3
4use super::{
5 QueueNotification, STATE_DISABLED, STATE_IDLE, STATE_MASK, STATE_MISSED, STATE_POLLING,
6 STATE_SCHEDULED,
7};
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub struct NetQueueStats {
12 pub irq: u64,
13 pub schedule: u64,
14 pub missed: u64,
15 pub poll_batches: u64,
16 pub budget_exhaustion: u64,
17 pub spurious: u64,
18 pub probe_deferred: u64,
19 pub rearm_race: u64,
20 pub owner_cpu: usize,
21 pub last_irq_cpu: Option<usize>,
22 pub last_poll_cpu: Option<usize>,
23 pub irq_to_poll_remote_wake: u64,
24}
25
26pub(super) struct QueueStatsAtomic {
27 pub(super) irq: AtomicU64,
28 pub(super) schedule: AtomicU64,
29 pub(super) missed: AtomicU64,
30 pub(super) poll_batches: AtomicU64,
31 pub(super) budget_exhaustion: AtomicU64,
32 pub(super) spurious: AtomicU64,
33 pub(super) probe_deferred: AtomicU64,
34 pub(super) rearm_race: AtomicU64,
35 pub(super) last_irq_cpu: AtomicUsize,
36 pub(super) last_poll_cpu: AtomicUsize,
37 pub(super) irq_to_poll_remote_wake: AtomicU64,
38}
39
40impl QueueStatsAtomic {
41 const fn new() -> Self {
42 Self {
43 irq: AtomicU64::new(0),
44 schedule: AtomicU64::new(0),
45 missed: AtomicU64::new(0),
46 poll_batches: AtomicU64::new(0),
47 budget_exhaustion: AtomicU64::new(0),
48 spurious: AtomicU64::new(0),
49 probe_deferred: AtomicU64::new(0),
50 rearm_race: AtomicU64::new(0),
51 last_irq_cpu: AtomicUsize::new(usize::MAX),
52 last_poll_cpu: AtomicUsize::new(usize::MAX),
53 irq_to_poll_remote_wake: AtomicU64::new(0),
54 }
55 }
56
57 pub(super) fn snapshot(&self, owner_cpu: usize) -> NetQueueStats {
58 let optional_cpu = |cpu| (cpu != usize::MAX).then_some(cpu);
59 NetQueueStats {
60 irq: self.irq.load(Ordering::Relaxed),
61 schedule: self.schedule.load(Ordering::Relaxed),
62 missed: self.missed.load(Ordering::Relaxed),
63 poll_batches: self.poll_batches.load(Ordering::Relaxed),
64 budget_exhaustion: self.budget_exhaustion.load(Ordering::Relaxed),
65 spurious: self.spurious.load(Ordering::Relaxed),
66 probe_deferred: self.probe_deferred.load(Ordering::Relaxed),
67 rearm_race: self.rearm_race.load(Ordering::Relaxed),
68 owner_cpu,
69 last_irq_cpu: optional_cpu(self.last_irq_cpu.load(Ordering::Acquire)),
70 last_poll_cpu: optional_cpu(self.last_poll_cpu.load(Ordering::Acquire)),
71 irq_to_poll_remote_wake: self.irq_to_poll_remote_wake.load(Ordering::Relaxed),
72 }
73 }
74}
75
76pub(super) struct PollGroupState {
78 pub(super) state: AtomicU8,
79 startup_absent: AtomicBool,
80 pub(super) owner_cpu: usize,
81 notify: Arc<QueueNotification>,
82 pub(super) stats: QueueStatsAtomic,
83 rx_drops: AtomicU64,
84}
85
86impl PollGroupState {
87 pub(super) fn new(owner_cpu: usize, notify: Arc<QueueNotification>) -> Self {
88 Self {
89 state: AtomicU8::new(STATE_DISABLED),
90 startup_absent: AtomicBool::new(false),
91 owner_cpu,
92 notify,
93 stats: QueueStatsAtomic::new(),
94 rx_drops: AtomicU64::new(0),
95 }
96 }
97
98 pub(super) fn mark_startup_absent(&self) {
99 self.startup_absent.store(true, Ordering::Release);
103 }
104
105 pub(super) fn startup_absent(&self) -> bool {
106 self.startup_absent.load(Ordering::Acquire)
107 }
108
109 pub(super) fn record_rx_drop(&self) {
110 self.rx_drops.fetch_add(1, Ordering::Relaxed);
112 }
113
114 pub(super) fn take_rx_drops(&self) -> u64 {
115 self.rx_drops.swap(0, Ordering::Relaxed)
116 }
117
118 pub(super) fn activate(&self, pending: bool) {
119 self.state.store(STATE_IDLE, Ordering::Release);
120 if pending {
121 self.schedule_task();
122 }
123 }
124
125 pub(super) fn schedule_irq(&self) {
126 let cpu = ax_hal::percpu::this_cpu_id();
127 self.stats.irq.fetch_add(1, Ordering::Relaxed);
128 self.stats.last_irq_cpu.store(cpu, Ordering::Release);
129 if self.startup_absent() {
130 return;
131 }
132 if cpu != self.owner_cpu {
133 self.stats
134 .irq_to_poll_remote_wake
135 .fetch_add(1, Ordering::Relaxed);
136 self.disable();
137 return;
138 }
139 if self.is_disabled() {
140 self.notify.notify();
143 } else if self.publish_schedule() {
144 self.notify.notify();
145 }
146 }
147
148 pub(super) fn wait_startup_irq(&self, waiter: &ax_task::sync::irq::IrqWorkerWaiter) {
149 self.notify.wait(waiter);
150 }
151
152 pub(super) fn wait_startup_deadline(
153 &self,
154 waiter: &ax_task::sync::irq::IrqWorkerWaiter,
155 deadline_nanos: u64,
156 ) {
157 let now = ax_hal::time::monotonic_time_nanos();
158 if deadline_nanos > now {
159 let duration = core::time::Duration::from_nanos(deadline_nanos - now);
160 self.notify.wait_timeout(waiter, duration);
161 }
162 }
163
164 pub(super) fn schedule_task(&self) {
165 self.publish_schedule();
166 if !self.is_disabled() {
171 self.notify.notify();
172 }
173 }
174
175 fn publish_schedule(&self) -> bool {
176 loop {
177 let old = self.state.load(Ordering::Acquire);
178 match old & STATE_MASK {
179 STATE_DISABLED => return false,
180 STATE_IDLE => {
181 if self
182 .state
183 .compare_exchange(old, STATE_SCHEDULED, Ordering::AcqRel, Ordering::Acquire)
184 .is_ok()
185 {
186 self.stats.schedule.fetch_add(1, Ordering::Relaxed);
187 return true;
188 }
189 }
190 STATE_SCHEDULED | STATE_POLLING => {
191 if old & STATE_MISSED != 0 {
192 return false;
193 }
194 if self
195 .state
196 .compare_exchange(
197 old,
198 old | STATE_MISSED,
199 Ordering::AcqRel,
200 Ordering::Acquire,
201 )
202 .is_ok()
203 {
204 self.stats.missed.fetch_add(1, Ordering::Relaxed);
205 return false;
206 }
207 }
208 _ => return false,
209 }
210 }
211 }
212
213 pub(super) fn claim(&self) -> bool {
214 let current_cpu = ax_hal::percpu::this_cpu_id();
215 if current_cpu != self.owner_cpu {
216 self.disable();
217 return false;
218 }
219 loop {
220 let old = self.state.load(Ordering::Acquire);
221 let claimable = (old & STATE_MASK == STATE_SCHEDULED)
222 || (old & STATE_MASK == STATE_POLLING && old & STATE_MISSED != 0);
223 if !claimable {
224 return false;
225 }
226 if self
227 .state
228 .compare_exchange(old, STATE_POLLING, Ordering::AcqRel, Ordering::Acquire)
229 .is_ok()
230 {
231 self.stats
232 .last_poll_cpu
233 .store(current_cpu, Ordering::Release);
234 self.stats.poll_batches.fetch_add(1, Ordering::Relaxed);
235 return true;
236 }
237 }
238 }
239
240 pub(super) fn finish_more(&self) {
241 loop {
242 let old = self.state.load(Ordering::Acquire);
243 if old & STATE_MASK != STATE_POLLING {
244 return;
245 }
246 if self
247 .state
248 .compare_exchange(old, STATE_SCHEDULED, Ordering::AcqRel, Ordering::Acquire)
249 .is_ok()
250 {
251 return;
252 }
253 }
254 }
255
256 pub(super) fn begin_rearm(&self) -> bool {
257 loop {
258 let old = self.state.load(Ordering::Acquire);
259 if old & STATE_MASK != STATE_POLLING {
260 return false;
261 }
262 if old & STATE_MISSED != 0 {
263 if self
264 .state
265 .compare_exchange(old, STATE_SCHEDULED, Ordering::AcqRel, Ordering::Acquire)
266 .is_ok()
267 {
268 return false;
269 }
270 continue;
271 }
272 if self
273 .state
274 .compare_exchange(old, STATE_IDLE, Ordering::AcqRel, Ordering::Acquire)
275 .is_ok()
276 {
277 return true;
278 }
279 }
280 }
281
282 pub(super) fn disable(&self) {
283 self.state.store(STATE_DISABLED, Ordering::Release);
284 self.notify.notify();
285 }
286
287 pub(super) fn is_disabled(&self) -> bool {
288 self.state.load(Ordering::Acquire) & STATE_MASK == STATE_DISABLED
289 }
290}