00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include "asterisk.h"
00031
00032 #include <sys/timerfd.h>
00033
00034 #include "asterisk/module.h"
00035 #include "asterisk/astobj2.h"
00036 #include "asterisk/timing.h"
00037 #include "asterisk/logger.h"
00038 #include "asterisk/utils.h"
00039 #include "asterisk/time.h"
00040
00041 static void *timing_funcs_handle;
00042
00043 static int timerfd_timer_open(void);
00044 static void timerfd_timer_close(int handle);
00045 static int timerfd_timer_set_rate(int handle, unsigned int rate);
00046 static void timerfd_timer_ack(int handle, unsigned int quantity);
00047 static int timerfd_timer_enable_continuous(int handle);
00048 static int timerfd_timer_disable_continuous(int handle);
00049 static enum ast_timer_event timerfd_timer_get_event(int handle);
00050 static unsigned int timerfd_timer_get_max_rate(int handle);
00051
00052 static struct ast_timing_interface timerfd_timing = {
00053 .name = "timerfd",
00054 .priority = 200,
00055 .timer_open = timerfd_timer_open,
00056 .timer_close = timerfd_timer_close,
00057 .timer_set_rate = timerfd_timer_set_rate,
00058 .timer_ack = timerfd_timer_ack,
00059 .timer_enable_continuous = timerfd_timer_enable_continuous,
00060 .timer_disable_continuous = timerfd_timer_disable_continuous,
00061 .timer_get_event = timerfd_timer_get_event,
00062 .timer_get_max_rate = timerfd_timer_get_max_rate,
00063 };
00064
00065 static struct ao2_container *timerfd_timers;
00066
00067 #define TIMERFD_TIMER_BUCKETS 563
00068 #define TIMERFD_MAX_RATE 1000
00069
00070 struct timerfd_timer {
00071 int handle;
00072 struct itimerspec saved_timer;
00073 unsigned int is_continuous:1;
00074 };
00075
00076 static int timerfd_timer_hash(const void *obj, const int flags)
00077 {
00078 const struct timerfd_timer *timer = obj;
00079
00080 return timer->handle;
00081 }
00082
00083 static int timerfd_timer_cmp(void *obj, void *args, int flags)
00084 {
00085 struct timerfd_timer *timer1 = obj, *timer2 = args;
00086 return timer1->handle == timer2->handle ? CMP_MATCH | CMP_STOP : 0;
00087 }
00088
00089 static void timer_destroy(void *obj)
00090 {
00091 struct timerfd_timer *timer = obj;
00092 close(timer->handle);
00093 }
00094
00095 static int timerfd_timer_open(void)
00096 {
00097 struct timerfd_timer *timer;
00098 int handle;
00099
00100 if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
00101 ast_log(LOG_ERROR, "Could not allocate memory for timerfd_timer structure\n");
00102 return -1;
00103 }
00104 if ((handle = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
00105 ast_log(LOG_ERROR, "Failed to create timerfd timer: %s\n", strerror(errno));
00106 ao2_ref(timer, -1);
00107 return -1;
00108 }
00109
00110 timer->handle = handle;
00111 ao2_link(timerfd_timers, timer);
00112
00113 ao2_ref(timer, -1);
00114 return handle;
00115 }
00116
00117 static void timerfd_timer_close(int handle)
00118 {
00119 struct timerfd_timer *our_timer, find_helper = {
00120 .handle = handle,
00121 };
00122
00123 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
00124 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
00125 return;
00126 }
00127
00128 ao2_unlink(timerfd_timers, our_timer);
00129 ao2_ref(our_timer, -1);
00130 }
00131
00132 static int timerfd_timer_set_rate(int handle, unsigned int rate)
00133 {
00134 struct timerfd_timer *our_timer, find_helper = {
00135 .handle = handle,
00136 };
00137 int res = 0;
00138
00139 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
00140 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
00141 return -1;
00142 }
00143
00144 our_timer->saved_timer.it_value.tv_sec = 0;
00145 our_timer->saved_timer.it_value.tv_nsec = rate ? (long) (1000000000 / rate) : 0L;
00146 our_timer->saved_timer.it_interval.tv_sec = our_timer->saved_timer.it_value.tv_sec;
00147 our_timer->saved_timer.it_interval.tv_nsec = our_timer->saved_timer.it_value.tv_nsec;
00148
00149 if (!our_timer->is_continuous) {
00150 res = timerfd_settime(handle, 0, &our_timer->saved_timer, NULL);
00151 }
00152
00153 ao2_ref(our_timer, -1);
00154
00155 return res;
00156 }
00157
00158 static void timerfd_timer_ack(int handle, unsigned int quantity)
00159 {
00160 uint64_t expirations;
00161 int read_result = 0;
00162
00163 do {
00164 read_result = read(handle, &expirations, sizeof(expirations));
00165 if (read_result == -1) {
00166 if (errno == EINTR) {
00167 continue;
00168 } else {
00169 ast_log(LOG_ERROR, "Read error: %s\n", strerror(errno));
00170 break;
00171 }
00172 }
00173 } while (read_result != sizeof(expirations));
00174
00175 if (expirations != quantity) {
00176 ast_debug(2, "Expected to acknowledge %u ticks but got %llu instead\n", quantity, (unsigned long long) expirations);
00177 }
00178 }
00179
00180 static int timerfd_timer_enable_continuous(int handle)
00181 {
00182 int res;
00183 struct itimerspec continuous_timer = {
00184 .it_value.tv_nsec = 1L,
00185 };
00186 struct timerfd_timer *our_timer, find_helper = {
00187 .handle = handle,
00188 };
00189
00190 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
00191 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
00192 return -1;
00193 }
00194
00195 if (our_timer->is_continuous) {
00196
00197
00198
00199 ao2_ref(our_timer, -1);
00200 return 0;
00201 }
00202
00203 res = timerfd_settime(handle, 0, &continuous_timer, &our_timer->saved_timer);
00204 our_timer->is_continuous = 1;
00205 ao2_ref(our_timer, -1);
00206 return res;
00207 }
00208
00209 static int timerfd_timer_disable_continuous(int handle)
00210 {
00211 int res;
00212 struct timerfd_timer *our_timer, find_helper = {
00213 .handle = handle,
00214 };
00215
00216 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
00217 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
00218 return -1;
00219 }
00220
00221 if(!our_timer->is_continuous) {
00222
00223
00224
00225 ao2_ref(our_timer, -1);
00226 return 0;
00227 }
00228
00229 res = timerfd_settime(handle, 0, &our_timer->saved_timer, NULL);
00230 our_timer->is_continuous = 0;
00231 memset(&our_timer->saved_timer, 0, sizeof(our_timer->saved_timer));
00232 ao2_ref(our_timer, -1);
00233 return res;
00234 }
00235
00236 static enum ast_timer_event timerfd_timer_get_event(int handle)
00237 {
00238 enum ast_timer_event res;
00239 struct timerfd_timer *our_timer, find_helper = {
00240 .handle = handle,
00241 };
00242
00243 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
00244 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
00245 return -1;
00246 }
00247
00248 if (our_timer->is_continuous) {
00249 res = AST_TIMING_EVENT_CONTINUOUS;
00250 } else {
00251 res = AST_TIMING_EVENT_EXPIRED;
00252 }
00253
00254 ao2_ref(our_timer, -1);
00255 return res;
00256 }
00257
00258 static unsigned int timerfd_timer_get_max_rate(int handle)
00259 {
00260 return TIMERFD_MAX_RATE;
00261 }
00262
00263 static int load_module(void)
00264 {
00265 int fd;
00266
00267
00268 if ((fd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
00269 ast_log(LOG_ERROR, "CLOCK_MONOTONIC not supported. Not loading.\n");
00270 return AST_MODULE_LOAD_DECLINE;
00271 }
00272
00273 close(fd);
00274
00275 if (!(timerfd_timers = ao2_container_alloc(TIMERFD_TIMER_BUCKETS, timerfd_timer_hash, timerfd_timer_cmp))) {
00276 return AST_MODULE_LOAD_DECLINE;
00277 }
00278
00279 if (!(timing_funcs_handle = ast_register_timing_interface(&timerfd_timing))) {
00280 ao2_ref(timerfd_timers, -1);
00281 return AST_MODULE_LOAD_DECLINE;
00282 }
00283
00284 return AST_MODULE_LOAD_SUCCESS;
00285 }
00286
00287 static int unload_module(void)
00288 {
00289 int res;
00290
00291 if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) {
00292 ao2_ref(timerfd_timers, -1);
00293 timerfd_timers = NULL;
00294 }
00295
00296 return res;
00297 }
00298
00299 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Timerfd Timing Interface",
00300 .load = load_module,
00301 .unload = unload_module,
00302 .load_pri = 10,
00303 );