1 /* SPDX-License-Identifier: GPL-2.0-only */
5 #define MAX_TIMER_QUEUE_ENTRIES 64
7 /* The timer queue is implemented using a min heap. Therefore the first
8 * element is the one with smallest time to expiration. */
12 struct timeout_callback
*queue
[MAX_TIMER_QUEUE_ENTRIES
];
15 static struct timer_queue global_timer_queue
= {
17 .max_entries
= MAX_TIMER_QUEUE_ENTRIES
,
21 static inline int timer_queue_empty(struct timer_queue
*tq
)
23 return tq
->num_entries
== 0;
26 static inline int timer_queue_full(struct timer_queue
*tq
)
28 return tq
->num_entries
== tq
->max_entries
;
31 static inline struct timeout_callback
*timer_queue_head(struct timer_queue
*tq
)
33 if (timer_queue_empty(tq
))
38 static int timer_queue_insert(struct timer_queue
*tq
,
39 struct timeout_callback
*tocb
)
44 if (timer_queue_full(tq
))
47 index
= tq
->num_entries
;
49 tq
->queue
[index
] = tocb
;
52 struct timeout_callback
*parent
;
55 parent_index
= (index
- 1) / 2;
56 parent
= tq
->queue
[parent_index
];
58 /* All other ancestors are less than or equal to the current. */
59 if (mono_time_cmp(&parent
->expiration
, &tocb
->expiration
) <= 0)
62 /* The parent is greater than current. Swap them. */
63 tq
->queue
[parent_index
] = tocb
;
64 tq
->queue
[index
] = parent
;
72 /* Get the index containing the entry with smallest value. */
73 static int timer_queue_min_child_index(struct timer_queue
*tq
, int index
)
76 int right_child_index
;
78 left_child_index
= 2 * index
+ 1;
80 if (left_child_index
>= tq
->num_entries
)
83 right_child_index
= left_child_index
+ 1;
85 if (right_child_index
>= tq
->num_entries
)
86 return left_child_index
;
88 if (mono_time_cmp(&tq
->queue
[left_child_index
]->expiration
,
89 &tq
->queue
[right_child_index
]->expiration
) < 0) {
90 return left_child_index
;
92 return right_child_index
;
95 static void timer_queue_remove_head(struct timer_queue
*tq
)
98 struct timeout_callback
*tocb
;
100 /* In order to remove the head the deepest child is replaced in the
101 * head slot and bubbled down the tree. */
103 tocb
= tq
->queue
[tq
->num_entries
];
109 struct timeout_callback
*child
;
111 min_child_index
= timer_queue_min_child_index(tq
, index
);
113 /* No more entries to compare against. */
114 if (min_child_index
< 0)
117 child
= tq
->queue
[min_child_index
];
119 /* Current index is the correct place since it is smaller or
120 * equal to the smallest child. */
121 if (mono_time_cmp(&tocb
->expiration
, &child
->expiration
) <= 0)
124 /* Need to swap with smallest child. */
125 tq
->queue
[min_child_index
] = tocb
;
126 tq
->queue
[index
] = child
;
128 index
= min_child_index
;
132 static struct timeout_callback
*
133 timer_queue_expired(struct timer_queue
*tq
, struct mono_time
*current_time
)
135 struct timeout_callback
*tocb
;
137 tocb
= timer_queue_head(tq
);
142 /* The timeout callback hasn't expired yet. */
143 if (mono_time_before(current_time
, &tocb
->expiration
))
146 timer_queue_remove_head(tq
);
151 int timer_sched_callback(struct timeout_callback
*tocb
, uint64_t us
)
153 struct mono_time current_time
;
158 timer_monotonic_get(¤t_time
);
159 tocb
->expiration
= current_time
;
160 mono_time_add_usecs(&tocb
->expiration
, us
);
162 /* The expiration overflowed. */
163 if (us
!= 0 && !mono_time_before(¤t_time
, &tocb
->expiration
))
166 return timer_queue_insert(&global_timer_queue
, tocb
);
171 struct timeout_callback
*tocb
;
172 struct mono_time current_time
;
174 timer_monotonic_get(¤t_time
);
175 tocb
= timer_queue_expired(&global_timer_queue
, ¤t_time
);
178 tocb
->callback(tocb
);
180 return !timer_queue_empty(&global_timer_queue
);