1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2010-2014 Intel Corporation
11 #include <sys/timerfd.h>
13 #include <rte_memory.h>
14 #include <rte_interrupts.h>
15 #include <rte_alarm.h>
16 #include <rte_common.h>
17 #include <rte_per_lcore.h>
19 #include <rte_launch.h>
20 #include <rte_lcore.h>
21 #include <rte_errno.h>
22 #include <rte_spinlock.h>
23 #include <eal_private.h>
27 #define TFD_NONBLOCK O_NONBLOCK
30 #define NS_PER_US 1000
31 #define US_PER_MS 1000
34 #define US_PER_S (US_PER_MS * MS_PER_S)
37 #ifdef CLOCK_MONOTONIC_RAW /* Defined in glibc bits/time.h */
38 #define CLOCK_TYPE_ID CLOCK_MONOTONIC_RAW
40 #define CLOCK_TYPE_ID CLOCK_MONOTONIC
44 LIST_ENTRY(alarm_entry
) next
;
46 rte_eal_alarm_callback cb_fn
;
48 volatile uint8_t executing
;
49 volatile pthread_t executing_id
;
52 static LIST_HEAD(alarm_list
, alarm_entry
) alarm_list
= LIST_HEAD_INITIALIZER();
53 static rte_spinlock_t alarm_list_lk
= RTE_SPINLOCK_INITIALIZER
;
55 static struct rte_intr_handle intr_handle
= {.fd
= -1 };
56 static int handler_registered
= 0;
57 static void eal_alarm_callback(void *arg
);
60 rte_eal_alarm_init(void)
62 intr_handle
.type
= RTE_INTR_HANDLE_ALARM
;
63 /* create a timerfd file descriptor */
64 intr_handle
.fd
= timerfd_create(CLOCK_MONOTONIC
, TFD_NONBLOCK
);
65 if (intr_handle
.fd
== -1)
76 eal_alarm_callback(void *arg __rte_unused
)
79 struct alarm_entry
*ap
;
81 rte_spinlock_lock(&alarm_list_lk
);
82 while ((ap
= LIST_FIRST(&alarm_list
)) !=NULL
&&
83 clock_gettime(CLOCK_TYPE_ID
, &now
) == 0 &&
84 (ap
->time
.tv_sec
< now
.tv_sec
|| (ap
->time
.tv_sec
== now
.tv_sec
&&
85 (ap
->time
.tv_usec
* NS_PER_US
) <= now
.tv_nsec
))) {
87 ap
->executing_id
= pthread_self();
88 rte_spinlock_unlock(&alarm_list_lk
);
90 ap
->cb_fn(ap
->cb_arg
);
92 rte_spinlock_lock(&alarm_list_lk
);
94 LIST_REMOVE(ap
, next
);
98 if (!LIST_EMPTY(&alarm_list
)) {
99 struct itimerspec atime
= { .it_interval
= { 0, 0 } };
101 ap
= LIST_FIRST(&alarm_list
);
102 atime
.it_value
.tv_sec
= ap
->time
.tv_sec
;
103 atime
.it_value
.tv_nsec
= ap
->time
.tv_usec
* NS_PER_US
;
104 /* perform borrow for subtraction if necessary */
105 if (now
.tv_nsec
> (ap
->time
.tv_usec
* NS_PER_US
))
106 atime
.it_value
.tv_sec
--, atime
.it_value
.tv_nsec
+= US_PER_S
* NS_PER_US
;
108 atime
.it_value
.tv_sec
-= now
.tv_sec
;
109 atime
.it_value
.tv_nsec
-= now
.tv_nsec
;
110 timerfd_settime(intr_handle
.fd
, 0, &atime
, NULL
);
112 rte_spinlock_unlock(&alarm_list_lk
);
116 rte_eal_alarm_set(uint64_t us
, rte_eal_alarm_callback cb_fn
, void *cb_arg
)
120 struct alarm_entry
*ap
, *new_alarm
;
122 /* Check parameters, including that us won't cause a uint64_t overflow */
123 if (us
< 1 || us
> (UINT64_MAX
- US_PER_S
) || cb_fn
== NULL
)
126 new_alarm
= calloc(1, sizeof(*new_alarm
));
127 if (new_alarm
== NULL
)
130 /* use current time to calculate absolute time of alarm */
131 clock_gettime(CLOCK_TYPE_ID
, &now
);
133 new_alarm
->cb_fn
= cb_fn
;
134 new_alarm
->cb_arg
= cb_arg
;
135 new_alarm
->time
.tv_usec
= ((now
.tv_nsec
/ NS_PER_US
) + us
) % US_PER_S
;
136 new_alarm
->time
.tv_sec
= now
.tv_sec
+ (((now
.tv_nsec
/ NS_PER_US
) + us
) / US_PER_S
);
138 rte_spinlock_lock(&alarm_list_lk
);
139 if (!handler_registered
) {
140 ret
|= rte_intr_callback_register(&intr_handle
,
141 eal_alarm_callback
, NULL
);
142 handler_registered
= (ret
== 0) ? 1 : 0;
145 if (LIST_EMPTY(&alarm_list
))
146 LIST_INSERT_HEAD(&alarm_list
, new_alarm
, next
);
148 LIST_FOREACH(ap
, &alarm_list
, next
) {
149 if (ap
->time
.tv_sec
> new_alarm
->time
.tv_sec
||
150 (ap
->time
.tv_sec
== new_alarm
->time
.tv_sec
&&
151 ap
->time
.tv_usec
> new_alarm
->time
.tv_usec
)){
152 LIST_INSERT_BEFORE(ap
, new_alarm
, next
);
155 if (LIST_NEXT(ap
, next
) == NULL
) {
156 LIST_INSERT_AFTER(ap
, new_alarm
, next
);
162 if (LIST_FIRST(&alarm_list
) == new_alarm
) {
163 struct itimerspec alarm_time
= {
164 .it_interval
= {0, 0},
166 .tv_sec
= us
/ US_PER_S
,
167 .tv_nsec
= (us
% US_PER_S
) * NS_PER_US
,
170 ret
|= timerfd_settime(intr_handle
.fd
, 0, &alarm_time
, NULL
);
172 rte_spinlock_unlock(&alarm_list_lk
);
178 rte_eal_alarm_cancel(rte_eal_alarm_callback cb_fn
, void *cb_arg
)
180 struct alarm_entry
*ap
, *ap_prev
;
192 rte_spinlock_lock(&alarm_list_lk
);
193 /* remove any matches at the start of the list */
194 while ((ap
= LIST_FIRST(&alarm_list
)) != NULL
&&
195 cb_fn
== ap
->cb_fn
&&
196 (cb_arg
== (void *)-1 || cb_arg
== ap
->cb_arg
)) {
198 if (ap
->executing
== 0) {
199 LIST_REMOVE(ap
, next
);
203 /* If calling from other context, mark that alarm is executing
204 * so loop can spin till it finish. Otherwise we are trying to
205 * cancel our self - mark it by EINPROGRESS */
206 if (pthread_equal(ap
->executing_id
, pthread_self()) == 0)
216 /* now go through list, removing entries not at start */
217 LIST_FOREACH(ap
, &alarm_list
, next
) {
218 /* this won't be true first time through */
219 if (cb_fn
== ap
->cb_fn
&&
220 (cb_arg
== (void *)-1 || cb_arg
== ap
->cb_arg
)) {
222 if (ap
->executing
== 0) {
223 LIST_REMOVE(ap
, next
);
227 } else if (pthread_equal(ap
->executing_id
, pthread_self()) == 0)
234 rte_spinlock_unlock(&alarm_list_lk
);
235 } while (executing
!= 0);
237 if (count
== 0 && err
== 0)