@@ -63,6 +63,8 @@ const volatile u64 kprobe_delays_max_ns = 2;
6363#define MIN (x , y ) ((x) < (y) ? (x) : (y))
6464#define MAX (x , y ) ((x) > (y) ? (x) : (y))
6565
66+ #define U64_MAX ((u64)~0ULL)
67+
6668enum chaos_timer_callbacks {
6769 CHAOS_TIMER_CHECK_QUEUES ,
6870 CHAOS_MAX_TIMERS ,
@@ -143,6 +145,46 @@ static __always_inline void chaos_stat_inc(enum chaos_stat_idx stat)
143145 (* cnt_p )++ ;
144146}
145147
148+ /*
149+ * Get the next time a delay DSQ needs processing.
150+ *
151+ * Safe for delay DSQs which use monotonic time (vtimes won't wrap to U64_MAX).
152+ * Must be called with RCU read lock held.
153+ */
154+ static __always_inline u64 delay_dsq_next_time (u64 dsq_id )
155+ {
156+ struct task_struct * first_p ;
157+ u64 vtime ;
158+
159+ // If we don't have native peek, fall back to always iterating
160+ if (!bpf_ksym_exists (scx_bpf_dsq_peek )) {
161+ chaos_stat_inc (CHAOS_STAT_PEEK_NEEDS_PROCESSING );
162+ return 0 ;
163+ }
164+
165+ first_p = scx_bpf_dsq_peek (dsq_id );
166+ if (!first_p ) {
167+ chaos_stat_inc (CHAOS_STAT_PEEK_EMPTY_DSQ );
168+ return U64_MAX ;
169+ }
170+
171+ first_p = bpf_task_from_pid (first_p -> pid );
172+ if (!first_p )
173+ return 0 ;
174+
175+ vtime = first_p -> scx .dsq_vtime ;
176+ bpf_task_release (first_p );
177+
178+ if (vtime > bpf_ktime_get_ns ()) {
179+ chaos_stat_inc (CHAOS_STAT_PEEK_NOT_READY );
180+ return vtime ;
181+ }
182+
183+ // First task is ready, need to iterate
184+ chaos_stat_inc (CHAOS_STAT_PEEK_NEEDS_PROCESSING );
185+ return 0 ;
186+ }
187+
146188static __always_inline enum chaos_trait_kind
147189choose_chaos (struct chaos_task_ctx * taskc )
148190{
@@ -362,9 +404,22 @@ __weak u64 check_dsq_times(int cpu_idx)
362404 u64 next_trigger_time = 0 ;
363405 u64 now = bpf_ktime_get_ns ();
364406 bool has_kicked = false;
407+ u64 dsq_id = get_cpu_delay_dsq (cpu_idx );
365408
366409 bpf_rcu_read_lock ();
367- bpf_for_each (scx_dsq , p , get_cpu_delay_dsq (cpu_idx ), 0 ) {
410+
411+ next_trigger_time = delay_dsq_next_time (dsq_id );
412+ if (next_trigger_time > now + chaos_timer_check_queues_slack_ns ) {
413+ // DSQ empty (U64_MAX) or first task beyond slack window
414+ bpf_rcu_read_unlock ();
415+ return next_trigger_time == U64_MAX ? 0 : next_trigger_time ;
416+ }
417+
418+ // Need to iterate: no peek support (0), task ready, or task within slack window
419+ next_trigger_time = 0 ;
420+
421+ // Need to iterate to handle ready tasks
422+ bpf_for_each (scx_dsq , p , dsq_id , 0 ) {
368423 p = bpf_task_from_pid (p -> pid );
369424 if (!p )
370425 break ;
@@ -387,8 +442,8 @@ __weak u64 check_dsq_times(int cpu_idx)
387442 if (next_trigger_time > now + chaos_timer_check_queues_slack_ns )
388443 break ;
389444 }
390- bpf_rcu_read_unlock ();
391445
446+ bpf_rcu_read_unlock ();
392447 return next_trigger_time ;
393448}
394449
@@ -531,9 +586,14 @@ void BPF_STRUCT_OPS(chaos_dispatch, s32 cpu, struct task_struct *prev)
531586 struct enqueue_promise promise ;
532587 struct chaos_task_ctx * taskc ;
533588 struct task_struct * p ;
534- u64 now = bpf_ktime_get_ns ();
589+ u64 now = bpf_ktime_get_ns ();
590+ u64 dsq_id = get_cpu_delay_dsq (-1 );
535591
536- bpf_for_each (scx_dsq , p , get_cpu_delay_dsq (-1 ), 0 ) {
592+ // Check if we need to process the delay DSQ
593+ if (delay_dsq_next_time (dsq_id ) > now )
594+ goto p2dq ;
595+
596+ bpf_for_each (scx_dsq , p , dsq_id , 0 ) {
537597 p = bpf_task_from_pid (p -> pid );
538598 if (!p )
539599 continue ;
@@ -557,6 +617,7 @@ void BPF_STRUCT_OPS(chaos_dispatch, s32 cpu, struct task_struct *prev)
557617 bpf_task_release (p );
558618 }
559619
620+ p2dq :
560621 return p2dq_dispatch_impl (cpu , prev );
561622}
562623
0 commit comments