4 * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * * Neither the name of Intel Corporation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #include <sys/queue.h>
40 #include <sys/timerfd.h>
42 #include <rte_memory.h>
43 #include <rte_memzone.h>
44 #include <rte_interrupts.h>
45 #include <rte_alarm.h>
46 #include <rte_common.h>
47 #include <rte_per_lcore.h>
49 #include <rte_launch.h>
50 #include <rte_lcore.h>
51 #include <rte_errno.h>
52 #include <rte_malloc.h>
53 #include <rte_spinlock.h>
54 #include <eal_private.h>
58 #define TFD_NONBLOCK O_NONBLOCK
61 #define NS_PER_US 1000
62 #define US_PER_MS 1000
64 #define US_PER_S (US_PER_MS * MS_PER_S)
66 #ifdef CLOCK_MONOTONIC_RAW /* Defined in glibc bits/time.h */
67 #define CLOCK_TYPE_ID CLOCK_MONOTONIC_RAW
69 #define CLOCK_TYPE_ID CLOCK_MONOTONIC
73 LIST_ENTRY(alarm_entry
) next
;
75 rte_eal_alarm_callback cb_fn
;
77 volatile uint8_t executing
;
78 volatile pthread_t executing_id
;
81 static LIST_HEAD(alarm_list
, alarm_entry
) alarm_list
= LIST_HEAD_INITIALIZER();
82 static rte_spinlock_t alarm_list_lk
= RTE_SPINLOCK_INITIALIZER
;
84 static struct rte_intr_handle intr_handle
= {.fd
= -1 };
85 static int handler_registered
= 0;
86 static void eal_alarm_callback(struct rte_intr_handle
*hdl
, void *arg
);
89 rte_eal_alarm_init(void)
91 intr_handle
.type
= RTE_INTR_HANDLE_ALARM
;
92 /* create a timerfd file descriptor */
93 intr_handle
.fd
= timerfd_create(CLOCK_MONOTONIC
, TFD_NONBLOCK
);
94 if (intr_handle
.fd
== -1)
105 eal_alarm_callback(struct rte_intr_handle
*hdl __rte_unused
,
106 void *arg __rte_unused
)
109 struct alarm_entry
*ap
;
111 rte_spinlock_lock(&alarm_list_lk
);
112 while ((ap
= LIST_FIRST(&alarm_list
)) !=NULL
&&
113 clock_gettime(CLOCK_TYPE_ID
, &now
) == 0 &&
114 (ap
->time
.tv_sec
< now
.tv_sec
|| (ap
->time
.tv_sec
== now
.tv_sec
&&
115 (ap
->time
.tv_usec
* NS_PER_US
) <= now
.tv_nsec
))) {
117 ap
->executing_id
= pthread_self();
118 rte_spinlock_unlock(&alarm_list_lk
);
120 ap
->cb_fn(ap
->cb_arg
);
122 rte_spinlock_lock(&alarm_list_lk
);
124 LIST_REMOVE(ap
, next
);
128 if (!LIST_EMPTY(&alarm_list
)) {
129 struct itimerspec atime
= { .it_interval
= { 0, 0 } };
131 ap
= LIST_FIRST(&alarm_list
);
132 atime
.it_value
.tv_sec
= ap
->time
.tv_sec
;
133 atime
.it_value
.tv_nsec
= ap
->time
.tv_usec
* NS_PER_US
;
134 /* perform borrow for subtraction if necessary */
135 if (now
.tv_nsec
> (ap
->time
.tv_usec
* NS_PER_US
))
136 atime
.it_value
.tv_sec
--, atime
.it_value
.tv_nsec
+= US_PER_S
* NS_PER_US
;
138 atime
.it_value
.tv_sec
-= now
.tv_sec
;
139 atime
.it_value
.tv_nsec
-= now
.tv_nsec
;
140 timerfd_settime(intr_handle
.fd
, 0, &atime
, NULL
);
142 rte_spinlock_unlock(&alarm_list_lk
);
146 rte_eal_alarm_set(uint64_t us
, rte_eal_alarm_callback cb_fn
, void *cb_arg
)
150 struct alarm_entry
*ap
, *new_alarm
;
152 /* Check parameters, including that us won't cause a uint64_t overflow */
153 if (us
< 1 || us
> (UINT64_MAX
- US_PER_S
) || cb_fn
== NULL
)
156 new_alarm
= rte_zmalloc(NULL
, sizeof(*new_alarm
), 0);
157 if (new_alarm
== NULL
)
160 /* use current time to calculate absolute time of alarm */
161 clock_gettime(CLOCK_TYPE_ID
, &now
);
163 new_alarm
->cb_fn
= cb_fn
;
164 new_alarm
->cb_arg
= cb_arg
;
165 new_alarm
->time
.tv_usec
= ((now
.tv_nsec
/ NS_PER_US
) + us
) % US_PER_S
;
166 new_alarm
->time
.tv_sec
= now
.tv_sec
+ (((now
.tv_nsec
/ NS_PER_US
) + us
) / US_PER_S
);
168 rte_spinlock_lock(&alarm_list_lk
);
169 if (!handler_registered
) {
170 ret
|= rte_intr_callback_register(&intr_handle
,
171 eal_alarm_callback
, NULL
);
172 handler_registered
= (ret
== 0) ? 1 : 0;
175 if (LIST_EMPTY(&alarm_list
))
176 LIST_INSERT_HEAD(&alarm_list
, new_alarm
, next
);
178 LIST_FOREACH(ap
, &alarm_list
, next
) {
179 if (ap
->time
.tv_sec
> new_alarm
->time
.tv_sec
||
180 (ap
->time
.tv_sec
== new_alarm
->time
.tv_sec
&&
181 ap
->time
.tv_usec
> new_alarm
->time
.tv_usec
)){
182 LIST_INSERT_BEFORE(ap
, new_alarm
, next
);
185 if (LIST_NEXT(ap
, next
) == NULL
) {
186 LIST_INSERT_AFTER(ap
, new_alarm
, next
);
192 if (LIST_FIRST(&alarm_list
) == new_alarm
) {
193 struct itimerspec alarm_time
= {
194 .it_interval
= {0, 0},
196 .tv_sec
= us
/ US_PER_S
,
197 .tv_nsec
= (us
% US_PER_S
) * NS_PER_US
,
200 ret
|= timerfd_settime(intr_handle
.fd
, 0, &alarm_time
, NULL
);
202 rte_spinlock_unlock(&alarm_list_lk
);
208 rte_eal_alarm_cancel(rte_eal_alarm_callback cb_fn
, void *cb_arg
)
210 struct alarm_entry
*ap
, *ap_prev
;
222 rte_spinlock_lock(&alarm_list_lk
);
223 /* remove any matches at the start of the list */
224 while ((ap
= LIST_FIRST(&alarm_list
)) != NULL
&&
225 cb_fn
== ap
->cb_fn
&&
226 (cb_arg
== (void *)-1 || cb_arg
== ap
->cb_arg
)) {
228 if (ap
->executing
== 0) {
229 LIST_REMOVE(ap
, next
);
233 /* If calling from other context, mark that alarm is executing
234 * so loop can spin till it finish. Otherwise we are trying to
235 * cancel our self - mark it by EINPROGRESS */
236 if (pthread_equal(ap
->executing_id
, pthread_self()) == 0)
246 /* now go through list, removing entries not at start */
247 LIST_FOREACH(ap
, &alarm_list
, next
) {
248 /* this won't be true first time through */
249 if (cb_fn
== ap
->cb_fn
&&
250 (cb_arg
== (void *)-1 || cb_arg
== ap
->cb_arg
)) {
252 if (ap
->executing
== 0) {
253 LIST_REMOVE(ap
, next
);
257 } else if (pthread_equal(ap
->executing_id
, pthread_self()) == 0)
264 rte_spinlock_unlock(&alarm_list_lk
);
265 } while (executing
!= 0);
267 if (count
== 0 && err
== 0)