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
00031 #include "asterisk.h"
00032
00033 #include <sys/timerfd.h>
00034
00035 #include "asterisk/module.h"
00036 #include "asterisk/astobj2.h"
00037 #include "asterisk/timing.h"
00038 #include "asterisk/logger.h"
00039 #include "asterisk/utils.h"
00040 #include "asterisk/time.h"
00041
00042 static void *timing_funcs_handle;
00043
00044 static int timerfd_timer_open(void);
00045 static void timerfd_timer_close(int handle);
00046 static int timerfd_timer_set_rate(int handle, unsigned int rate);
00047 static int timerfd_timer_ack(int handle, unsigned int quantity);
00048 static int timerfd_timer_enable_continuous(int handle);
00049 static int timerfd_timer_disable_continuous(int handle);
00050 static enum ast_timer_event timerfd_timer_get_event(int handle);
00051 static unsigned int timerfd_timer_get_max_rate(int handle);
00052
00053 static struct ast_timing_interface timerfd_timing = {
00054 .name = "timerfd",
00055 .priority = 200,
00056 .timer_open = timerfd_timer_open,
00057 .timer_close = timerfd_timer_close,
00058 .timer_set_rate = timerfd_timer_set_rate,
00059 .timer_ack = timerfd_timer_ack,
00060 .timer_enable_continuous = timerfd_timer_enable_continuous,
00061 .timer_disable_continuous = timerfd_timer_disable_continuous,
00062 .timer_get_event = timerfd_timer_get_event,
00063 .timer_get_max_rate = timerfd_timer_get_max_rate,
00064 };
00065
00066 static struct ao2_container *timerfd_timers;
00067
00068 #define TIMERFD_TIMER_BUCKETS 563
00069 #define TIMERFD_MAX_RATE 1000
00070
00071 struct timerfd_timer {
00072 int handle;
00073 struct itimerspec saved_timer;
00074 unsigned int is_continuous:1;
00075 };
00076
00077 static int timerfd_timer_hash(const void *obj, const int flags)
00078 {
00079 const struct timerfd_timer *timer = obj;
00080
00081 return timer->handle;
00082 }
00083
00084 static int timerfd_timer_cmp(void *obj, void *args, int flags)
00085 {
00086 struct timerfd_timer *timer1 = obj, *timer2 = args;
00087 return timer1->handle == timer2->handle ? CMP_MATCH | CMP_STOP : 0;
00088 }
00089
00090 static void timer_destroy(void *obj)
00091 {
00092 struct timerfd_timer *timer = obj;
00093 close(timer->handle);
00094 timer->handle = -1;
00095 }
00096
00097 static int timerfd_timer_open(void)
00098 {
00099 struct timerfd_timer *timer;
00100 int handle;
00101
00102 if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
00103 ast_log(LOG_ERROR, "Could not allocate memory for timerfd_timer structure\n");
00104 return -1;
00105 }
00106 if ((handle = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
00107 ast_log(LOG_ERROR, "Failed to create timerfd timer: %s\n", strerror(errno));
00108 ao2_ref(timer, -1);
00109 return -1;
00110 }
00111
00112 timer->handle = handle;
00113 ao2_link(timerfd_timers, timer);
00114
00115 ao2_ref(timer, -1);
00116 return handle;
00117 }
00118
00119 static void timerfd_timer_close(int handle)
00120 {
00121 struct timerfd_timer *our_timer, find_helper = {
00122 .handle = handle,
00123 };
00124
00125 if (handle == -1) {
00126 ast_log(LOG_ERROR, "Attempting to close timerfd handle -1");
00127 return;
00128 }
00129
00130 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
00131 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
00132 return;
00133 }
00134
00135 ao2_unlink(timerfd_timers, our_timer);
00136 ao2_ref(our_timer, -1);
00137 }
00138
00139 static int timerfd_timer_set_rate(int handle, unsigned int rate)
00140 {
00141 struct timerfd_timer *our_timer, find_helper = {
00142 .handle = handle,
00143 };
00144 int res = 0;
00145
00146 if (handle == -1) {
00147 ast_log(LOG_ERROR, "Attempting to set rate on timerfd handle -1");
00148 return -1;
00149 }
00150
00151 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
00152 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
00153 return -1;
00154 }
00155 ao2_lock(our_timer);
00156
00157 our_timer->saved_timer.it_value.tv_sec = 0;
00158 our_timer->saved_timer.it_value.tv_nsec = rate ? (long) (1000000000 / rate) : 0L;
00159 our_timer->saved_timer.it_interval.tv_sec = our_timer->saved_timer.it_value.tv_sec;
00160 our_timer->saved_timer.it_interval.tv_nsec = our_timer->saved_timer.it_value.tv_nsec;
00161
00162 if (!our_timer->is_continuous) {
00163 res = timerfd_settime(handle, 0, &our_timer->saved_timer, NULL);
00164 }
00165
00166 ao2_unlock(our_timer);
00167 ao2_ref(our_timer, -1);
00168
00169 return res;
00170 }
00171
00172 static int timerfd_timer_ack(int handle, unsigned int quantity)
00173 {
00174 uint64_t expirations;
00175 int read_result = 0;
00176 int res = 0;
00177 struct timerfd_timer *our_timer, find_helper = {
00178 .handle = handle,
00179 };
00180
00181 if (handle == -1) {
00182 ast_log(LOG_ERROR, "Attempting to ack timerfd handle -1");
00183 return -1;
00184 }
00185
00186 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
00187 ast_log(LOG_ERROR, "Couldn't find a timer with handle %d\n", handle);
00188 return -1;
00189 }
00190
00191 ao2_lock(our_timer);
00192
00193 do {
00194 struct itimerspec timer_status;
00195
00196 if (timerfd_gettime(handle, &timer_status)) {
00197 ast_log(LOG_ERROR, "Call to timerfd_gettime() using handle %d error: %s\n", handle, strerror(errno));
00198 expirations = 0;
00199 res = -1;
00200 break;
00201 }
00202
00203 if (timer_status.it_value.tv_sec == 0 && timer_status.it_value.tv_nsec == 0) {
00204 ast_debug(1, "Avoiding read on disarmed timerfd %d\n", handle);
00205 expirations = 0;
00206 break;
00207 }
00208
00209 read_result = read(handle, &expirations, sizeof(expirations));
00210 if (read_result == -1) {
00211 if (errno == EINTR || errno == EAGAIN) {
00212 continue;
00213 } else {
00214 ast_log(LOG_ERROR, "Read error: %s\n", strerror(errno));
00215 res = -1;
00216 break;
00217 }
00218 }
00219 } while (read_result != sizeof(expirations));
00220
00221 ao2_unlock(our_timer);
00222 ao2_ref(our_timer, -1);
00223
00224 if (expirations != quantity) {
00225 ast_debug(2, "Expected to acknowledge %u ticks but got %llu instead\n", quantity, (unsigned long long) expirations);
00226 }
00227 return res;
00228 }
00229
00230 static int timerfd_timer_enable_continuous(int handle)
00231 {
00232 int res;
00233 struct itimerspec continuous_timer = {
00234 .it_value.tv_nsec = 1L,
00235 };
00236 struct timerfd_timer *our_timer, find_helper = {
00237 .handle = handle,
00238 };
00239
00240 if (handle == -1) {
00241 ast_log(LOG_ERROR, "Attempting to enable timerfd handle -1");
00242 return -1;
00243 }
00244
00245 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
00246 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
00247 return -1;
00248 }
00249 ao2_lock(our_timer);
00250
00251 if (our_timer->is_continuous) {
00252
00253
00254
00255 ao2_unlock(our_timer);
00256 ao2_ref(our_timer, -1);
00257 return 0;
00258 }
00259
00260 res = timerfd_settime(handle, 0, &continuous_timer, &our_timer->saved_timer);
00261 our_timer->is_continuous = 1;
00262 ao2_unlock(our_timer);
00263 ao2_ref(our_timer, -1);
00264 return res;
00265 }
00266
00267 static int timerfd_timer_disable_continuous(int handle)
00268 {
00269 int res;
00270 struct timerfd_timer *our_timer, find_helper = {
00271 .handle = handle,
00272 };
00273
00274 if (handle == -1) {
00275 ast_log(LOG_ERROR, "Attempting to disable timerfd handle -1");
00276 return -1;
00277 }
00278
00279 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
00280 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
00281 return -1;
00282 }
00283 ao2_lock(our_timer);
00284
00285 if (!our_timer->is_continuous) {
00286
00287
00288
00289 ao2_unlock(our_timer);
00290 ao2_ref(our_timer, -1);
00291 return 0;
00292 }
00293
00294 res = timerfd_settime(handle, 0, &our_timer->saved_timer, NULL);
00295 our_timer->is_continuous = 0;
00296 memset(&our_timer->saved_timer, 0, sizeof(our_timer->saved_timer));
00297 ao2_unlock(our_timer);
00298 ao2_ref(our_timer, -1);
00299 return res;
00300 }
00301
00302 static enum ast_timer_event timerfd_timer_get_event(int handle)
00303 {
00304 enum ast_timer_event res;
00305 struct timerfd_timer *our_timer, find_helper = {
00306 .handle = handle,
00307 };
00308
00309 if (handle == -1) {
00310 ast_log(LOG_ERROR, "Attempting to get event from timerfd handle -1");
00311 return -1;
00312 }
00313
00314 if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
00315 ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
00316 return -1;
00317 }
00318 ao2_lock(our_timer);
00319
00320 if (our_timer->is_continuous) {
00321 res = AST_TIMING_EVENT_CONTINUOUS;
00322 } else {
00323 res = AST_TIMING_EVENT_EXPIRED;
00324 }
00325
00326 ao2_unlock(our_timer);
00327 ao2_ref(our_timer, -1);
00328 return res;
00329 }
00330
00331 static unsigned int timerfd_timer_get_max_rate(int handle)
00332 {
00333 return TIMERFD_MAX_RATE;
00334 }
00335
00336 static int load_module(void)
00337 {
00338 int fd;
00339
00340
00341 if ((fd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
00342 ast_log(LOG_ERROR, "timerfd_create() not supported by the kernel. Not loading.\n");
00343 return AST_MODULE_LOAD_DECLINE;
00344 }
00345
00346 close(fd);
00347
00348 if (!(timerfd_timers = ao2_container_alloc(TIMERFD_TIMER_BUCKETS, timerfd_timer_hash, timerfd_timer_cmp))) {
00349 return AST_MODULE_LOAD_DECLINE;
00350 }
00351
00352 if (!(timing_funcs_handle = ast_register_timing_interface(&timerfd_timing))) {
00353 ao2_ref(timerfd_timers, -1);
00354 return AST_MODULE_LOAD_DECLINE;
00355 }
00356
00357 return AST_MODULE_LOAD_SUCCESS;
00358 }
00359
00360 static int unload_module(void)
00361 {
00362 int res;
00363
00364 if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) {
00365 ao2_ref(timerfd_timers, -1);
00366 timerfd_timers = NULL;
00367 }
00368
00369 return res;
00370 }
00371
00372 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Timerfd Timing Interface",
00373 .load = load_module,
00374 .unload = unload_module,
00375 .load_pri = AST_MODPRI_TIMING,
00376 );