2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2008, Digium, Inc.
6 * Mark Michelson <mmichelson@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \author Mark Michelson <mmichelson@digium.com>
23 * \brief timerfd timing interface
27 <depend>timerfd</depend>
28 <conflict>res_timing_pthread</conflict>
29 <conflict>res_timing_dahdi</conflict>
34 #include <sys/timerfd.h>
36 #include "asterisk/module.h"
37 #include "asterisk/astobj2.h"
38 #include "asterisk/timing.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/utils.h"
41 #include "asterisk/time.h"
43 static void *timing_funcs_handle;
45 static int timerfd_timer_open(void);
46 static void timerfd_timer_close(int handle);
47 static int timerfd_timer_set_rate(int handle, unsigned int rate);
48 static void timerfd_timer_ack(int handle, unsigned int quantity);
49 static int timerfd_timer_enable_continuous(int handle);
50 static int timerfd_timer_disable_continuous(int handle);
51 static enum ast_timing_event timerfd_timer_get_event(int handle);
52 static unsigned int timerfd_timer_get_max_rate(int handle);
54 static struct ast_timing_functions timerfd_timing_functions = {
55 .timer_open = timerfd_timer_open,
56 .timer_close = timerfd_timer_close,
57 .timer_set_rate = timerfd_timer_set_rate,
58 .timer_ack = timerfd_timer_ack,
59 .timer_enable_continuous = timerfd_timer_enable_continuous,
60 .timer_disable_continuous = timerfd_timer_disable_continuous,
61 .timer_get_event = timerfd_timer_get_event,
62 .timer_get_max_rate = timerfd_timer_get_max_rate,
65 static struct ao2_container *timerfd_timers;
67 #define TIMERFD_TIMER_BUCKETS 563
68 #define TIMERFD_MAX_RATE 1000
70 struct timerfd_timer {
72 struct itimerspec saved_timer;
73 unsigned int is_continuous:1;
76 static int timerfd_timer_hash(const void *obj, const int flags)
78 const struct timerfd_timer *timer = obj;
83 static int timerfd_timer_cmp(void *obj, void *args, void *data, int flags)
85 struct timerfd_timer *timer1 = obj, *timer2 = args;
86 return timer1->handle == timer2->handle ? CMP_MATCH | CMP_STOP : 0;
89 static void timer_destroy(void *obj)
91 struct timerfd_timer *timer = obj;
95 static int timerfd_timer_open(void)
97 struct timerfd_timer *timer;
100 if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
101 ast_log(LOG_ERROR, "Could not allocate memory for timerfd_timer structure\n");
104 if ((handle = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
105 ast_log(LOG_ERROR, "Failed to create timerfd timer: %s\n", strerror(errno));
110 timer->handle = handle;
111 ao2_link(timerfd_timers, timer);
112 /* Get rid of the reference from the allocation */
117 static void timerfd_timer_close(int handle)
119 struct timerfd_timer *our_timer, find_helper = {
123 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, NULL, OBJ_POINTER))) {
124 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
128 ao2_unlink(timerfd_timers, our_timer);
129 ao2_ref(our_timer, -1);
132 static int timerfd_timer_set_rate(int handle, unsigned int rate)
134 struct itimerspec itspec;
135 itspec.it_value.tv_sec = 0;
136 itspec.it_value.tv_nsec = rate ? (long) (1000000000 / rate) : 0L;
137 itspec.it_interval.tv_sec = itspec.it_value.tv_sec;
138 itspec.it_interval.tv_nsec = itspec.it_value.tv_nsec;
140 return timerfd_settime(handle, 0, &itspec, NULL);
143 static void timerfd_timer_ack(int handle, unsigned int quantity)
145 uint64_t expirations;
149 read_result = read(handle, &expirations, sizeof(expirations));
150 if (read_result == -1) {
151 if (errno == EINTR) {
154 ast_log(LOG_ERROR, "Read error: %s\n", strerror(errno));
158 } while (read_result != sizeof(expirations));
160 if (expirations != quantity) {
161 ast_debug(2, "Expected to acknowledge %u ticks but got %llu instead\n", quantity, (unsigned long long) expirations);
165 static int timerfd_timer_enable_continuous(int handle)
168 struct itimerspec continuous_timer = {
169 .it_value.tv_nsec = 1L,
171 struct timerfd_timer *our_timer, find_helper = {
175 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, NULL, OBJ_POINTER))) {
176 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
180 if (our_timer->is_continuous) {
181 /*It's already in continous mode, no need to do
184 ao2_ref(our_timer, -1);
188 res = timerfd_settime(handle, 0, &continuous_timer, &our_timer->saved_timer);
189 our_timer->is_continuous = 1;
190 ao2_ref(our_timer, -1);
194 static int timerfd_timer_disable_continuous(int handle)
197 struct timerfd_timer *our_timer, find_helper = {
201 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, NULL, OBJ_POINTER))) {
202 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
206 if(!our_timer->is_continuous) {
207 /* No reason to do anything if we're not
210 ao2_ref(our_timer, -1);
214 res = timerfd_settime(handle, 0, &our_timer->saved_timer, NULL);
215 our_timer->is_continuous = 0;
216 memset(&our_timer->saved_timer, 0, sizeof(our_timer->saved_timer));
217 ao2_ref(our_timer, -1);
221 static enum ast_timing_event timerfd_timer_get_event(int handle)
223 enum ast_timing_event res;
224 struct timerfd_timer *our_timer, find_helper = {
228 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, NULL, OBJ_POINTER))) {
229 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
233 if (our_timer->is_continuous) {
234 res = AST_TIMING_EVENT_CONTINUOUS;
236 res = AST_TIMING_EVENT_EXPIRED;
239 ao2_ref(our_timer, -1);
243 static unsigned int timerfd_timer_get_max_rate(int handle)
245 return TIMERFD_MAX_RATE;
248 static int load_module(void)
250 if (!(timerfd_timers = ao2_container_alloc(TIMERFD_TIMER_BUCKETS, timerfd_timer_hash, timerfd_timer_cmp))) {
251 return AST_MODULE_LOAD_DECLINE;
254 if (!(timing_funcs_handle = ast_install_timing_functions(&timerfd_timing_functions))) {
255 ao2_ref(timerfd_timers, -1);
256 return AST_MODULE_LOAD_DECLINE;
259 return AST_MODULE_LOAD_SUCCESS;
262 static int unload_module(void)
264 /* ast_uninstall_timing_functions(timing_funcs_handle); */
266 /* This module can not currently be unloaded. No use count handling is being done. */
271 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Timerfd Timing Interface");