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
00032 #include "asterisk.h"
00033
00034 #include <sys/types.h>
00035 #include <sys/event.h>
00036 #include <sys/time.h>
00037
00038 #include "asterisk/module.h"
00039 #include "asterisk/astobj2.h"
00040 #include "asterisk/timing.h"
00041 #include "asterisk/logger.h"
00042 #include "asterisk/utils.h"
00043 #include "asterisk/time.h"
00044 #include "asterisk/test.h"
00045 #include "asterisk/poll-compat.h"
00046
00047 static void *timing_funcs_handle;
00048
00049 static int kqueue_timer_open(void);
00050 static void kqueue_timer_close(int handle);
00051 static int kqueue_timer_set_rate(int handle, unsigned int rate);
00052 static void kqueue_timer_ack(int handle, unsigned int quantity);
00053 static int kqueue_timer_enable_continuous(int handle);
00054 static int kqueue_timer_disable_continuous(int handle);
00055 static enum ast_timer_event kqueue_timer_get_event(int handle);
00056 static unsigned int kqueue_timer_get_max_rate(int handle);
00057
00058 static struct ast_timing_interface kqueue_timing = {
00059 .name = "kqueue",
00060 .priority = 150,
00061 .timer_open = kqueue_timer_open,
00062 .timer_close = kqueue_timer_close,
00063 .timer_set_rate = kqueue_timer_set_rate,
00064 .timer_ack = kqueue_timer_ack,
00065 .timer_enable_continuous = kqueue_timer_enable_continuous,
00066 .timer_disable_continuous = kqueue_timer_disable_continuous,
00067 .timer_get_event = kqueue_timer_get_event,
00068 .timer_get_max_rate = kqueue_timer_get_max_rate,
00069 };
00070
00071 static struct ao2_container *kqueue_timers;
00072
00073 struct kqueue_timer {
00074 int handle;
00075 uint64_t nsecs;
00076 uint64_t unacked;
00077 unsigned int is_continuous:1;
00078 };
00079
00080 static int kqueue_timer_hash(const void *obj, const int flags)
00081 {
00082 const struct kqueue_timer *timer = obj;
00083
00084 return timer->handle;
00085 }
00086
00087 static int kqueue_timer_cmp(void *obj, void *args, int flags)
00088 {
00089 struct kqueue_timer *timer1 = obj, *timer2 = args;
00090 return timer1->handle == timer2->handle ? CMP_MATCH | CMP_STOP : 0;
00091 }
00092
00093 static void timer_destroy(void *obj)
00094 {
00095 struct kqueue_timer *timer = obj;
00096 close(timer->handle);
00097 }
00098
00099 #define lookup_timer(a) _lookup_timer(a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
00100 static struct kqueue_timer *_lookup_timer(int handle, const char *file, int line, const char *func)
00101 {
00102 struct kqueue_timer *our_timer, find_helper = {
00103 .handle = handle,
00104 };
00105
00106 if (!(our_timer = ao2_find(kqueue_timers, &find_helper, OBJ_POINTER))) {
00107 ast_log(__LOG_ERROR, file, line, func, "Couldn't find timer with handle %d\n", handle);
00108
00109 errno = ESRCH;
00110 return NULL;
00111 }
00112 return our_timer;
00113 }
00114
00115 static int kqueue_timer_open(void)
00116 {
00117 struct kqueue_timer *timer;
00118 int handle;
00119
00120 if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
00121 ast_log(LOG_ERROR, "Could not allocate memory for kqueue_timer structure\n");
00122 return -1;
00123 }
00124 if ((handle = kqueue()) < 0) {
00125 ast_log(LOG_ERROR, "Failed to create kqueue timer: %s\n", strerror(errno));
00126 ao2_ref(timer, -1);
00127 return -1;
00128 }
00129
00130 timer->handle = handle;
00131 ao2_link(kqueue_timers, timer);
00132
00133 ao2_ref(timer, -1);
00134 return handle;
00135 }
00136
00137 static void kqueue_timer_close(int handle)
00138 {
00139 struct kqueue_timer *our_timer;
00140
00141 if (!(our_timer = lookup_timer(handle))) {
00142 return;
00143 }
00144
00145 ao2_unlink(kqueue_timers, our_timer);
00146 ao2_ref(our_timer, -1);
00147 }
00148
00149 static void kqueue_set_nsecs(struct kqueue_timer *our_timer, uint64_t nsecs)
00150 {
00151 struct timespec nowait = { 0, 1 };
00152 #ifdef HAVE_KEVENT64
00153 struct kevent64_s kev;
00154
00155 EV_SET64(&kev, our_timer->handle, EVFILT_TIMER, EV_ADD | EV_ENABLE, NOTE_NSECONDS,
00156 nsecs, 0, 0, 0);
00157 kevent64(our_timer->handle, &kev, 1, NULL, 0, 0, &nowait);
00158 #else
00159 struct kevent kev;
00160
00161 EV_SET(&kev, our_timer->handle, EVFILT_TIMER, EV_ADD | EV_ENABLE,
00162 #ifdef NOTE_NSECONDS
00163 nsecs <= 0xFFffFFff ? NOTE_NSECONDS :
00164 #endif
00165 #ifdef NOTE_USECONDS
00166 NOTE_USECONDS
00167 #else
00168 0
00169 #endif
00170 ,
00171 #ifdef NOTE_NSECONDS
00172 nsecs <= 0xFFffFFff ? nsecs :
00173 #endif
00174 #ifdef NOTE_USECONDS
00175 nsecs / 1000
00176 #else
00177 nsecs / 1000000
00178 #endif
00179 , NULL);
00180 kevent(our_timer->handle, &kev, 1, NULL, 0, &nowait);
00181 #endif
00182 }
00183
00184 static int kqueue_timer_set_rate(int handle, unsigned int rate)
00185 {
00186 struct kqueue_timer *our_timer;
00187
00188 if (!(our_timer = lookup_timer(handle))) {
00189 return -1;
00190 }
00191
00192 kqueue_set_nsecs(our_timer, (our_timer->nsecs = rate ? (long) (1000000000 / rate) : 0L));
00193 ao2_ref(our_timer, -1);
00194
00195 return 0;
00196 }
00197
00198 static void kqueue_timer_ack(int handle, unsigned int quantity)
00199 {
00200 struct kqueue_timer *our_timer;
00201
00202 if (!(our_timer = lookup_timer(handle))) {
00203 return;
00204 }
00205
00206 if (our_timer->unacked < quantity) {
00207 ast_debug(1, "Acking more events than have expired?!!\n");
00208 our_timer->unacked = 0;
00209 } else {
00210 our_timer->unacked -= quantity;
00211 }
00212 }
00213
00214 static int kqueue_timer_enable_continuous(int handle)
00215 {
00216 struct kqueue_timer *our_timer;
00217
00218 if (!(our_timer = lookup_timer(handle))) {
00219 return -1;
00220 }
00221
00222 kqueue_set_nsecs(our_timer, 1);
00223 our_timer->is_continuous = 1;
00224 our_timer->unacked = 0;
00225 ao2_ref(our_timer, -1);
00226 return 0;
00227 }
00228
00229 static int kqueue_timer_disable_continuous(int handle)
00230 {
00231 struct kqueue_timer *our_timer;
00232
00233 if (!(our_timer = lookup_timer(handle))) {
00234 return -1;
00235 }
00236
00237 kqueue_set_nsecs(our_timer, our_timer->nsecs);
00238 our_timer->is_continuous = 0;
00239 our_timer->unacked = 0;
00240 ao2_ref(our_timer, -1);
00241 return 0;
00242 }
00243
00244 static enum ast_timer_event kqueue_timer_get_event(int handle)
00245 {
00246 enum ast_timer_event res = -1;
00247 struct kqueue_timer *our_timer;
00248 struct timespec sixty_seconds = { 60, 0 };
00249 struct kevent kev;
00250
00251 if (!(our_timer = lookup_timer(handle))) {
00252 return -1;
00253 }
00254
00255
00256 if (our_timer->unacked == 0) {
00257 if (kevent(handle, NULL, 0, &kev, 1, &sixty_seconds) > 0) {
00258 our_timer->unacked += kev.data;
00259 }
00260 }
00261
00262 if (our_timer->unacked > 0) {
00263 res = our_timer->is_continuous ? AST_TIMING_EVENT_CONTINUOUS : AST_TIMING_EVENT_EXPIRED;
00264 }
00265
00266 ao2_ref(our_timer, -1);
00267 return res;
00268 }
00269
00270 static unsigned int kqueue_timer_get_max_rate(int handle)
00271 {
00272
00273 return UINT_MAX;
00274 }
00275
00276 #ifdef TEST_FRAMEWORK
00277 AST_TEST_DEFINE(test_kqueue_timing)
00278 {
00279 int res = AST_TEST_PASS, handle, i;
00280 uint64_t diff;
00281 struct pollfd pfd = { 0, POLLIN, 0 };
00282 struct kqueue_timer *kt;
00283 struct timeval start;
00284
00285 switch (cmd) {
00286 case TEST_INIT:
00287 info->name = "test_kqueue_timing";
00288 info->category = "/res/res_timing_kqueue/";
00289 info->summary = "Test KQueue timing interface";
00290 info->description = "Verify that the KQueue timing interface correctly generates timing events";
00291 return AST_TEST_NOT_RUN;
00292 case TEST_EXECUTE:
00293 break;
00294 }
00295
00296 if (!(handle = kqueue_timer_open())) {
00297 ast_test_status_update(test, "Cannot open timer!\n");
00298 return AST_TEST_FAIL;
00299 }
00300
00301 do {
00302 pfd.fd = handle;
00303 if (kqueue_timer_set_rate(handle, 1000)) {
00304 ast_test_status_update(test, "Cannot set timer rate to 1000/s\n");
00305 res = AST_TEST_FAIL;
00306 break;
00307 }
00308 if (ast_poll(&pfd, 1, 1000) < 1) {
00309 ast_test_status_update(test, "Polling on a kqueue doesn't work\n");
00310 res = AST_TEST_FAIL;
00311 break;
00312 }
00313 if (pfd.revents != POLLIN) {
00314 ast_test_status_update(test, "poll() should have returned POLLIN, but instead returned %hd\n", pfd.revents);
00315 res = AST_TEST_FAIL;
00316 break;
00317 }
00318 if (!(kt = lookup_timer(handle))) {
00319 ast_test_status_update(test, "Could not find timer structure in container?!!\n");
00320 res = AST_TEST_FAIL;
00321 break;
00322 }
00323 if (kqueue_timer_get_event(handle) <= 0) {
00324 ast_test_status_update(test, "No events generated after a poll returned successfully?!!\n");
00325 res = AST_TEST_FAIL;
00326 break;
00327 }
00328 #if 0
00329 if (kt->unacked == 0) {
00330 ast_test_status_update(test, "Unacked events is 0, but there should be at least 1.\n");
00331 res = AST_TEST_FAIL;
00332 break;
00333 }
00334 #endif
00335 kqueue_timer_enable_continuous(handle);
00336 start = ast_tvnow();
00337 for (i = 0; i < 100; i++) {
00338 if (ast_poll(&pfd, 1, 1000) < 1) {
00339 ast_test_status_update(test, "Polling on a kqueue doesn't work\n");
00340 res = AST_TEST_FAIL;
00341 break;
00342 }
00343 if (kqueue_timer_get_event(handle) <= 0) {
00344 ast_test_status_update(test, "No events generated in continuous mode after 1 microsecond?!!\n");
00345 res = AST_TEST_FAIL;
00346 break;
00347 }
00348 }
00349 diff = ast_tvdiff_us(ast_tvnow(), start);
00350 ast_test_status_update(test, "diff is %llu\n", diff);
00351
00352
00353
00354
00355
00356
00357 } while (0);
00358 kqueue_timer_close(handle);
00359 return res;
00360 }
00361 #endif
00362
00363 static int load_module(void)
00364 {
00365 if (!(kqueue_timers = ao2_container_alloc(563, kqueue_timer_hash, kqueue_timer_cmp))) {
00366 return AST_MODULE_LOAD_DECLINE;
00367 }
00368
00369 if (!(timing_funcs_handle = ast_register_timing_interface(&kqueue_timing))) {
00370 ao2_ref(kqueue_timers, -1);
00371 return AST_MODULE_LOAD_DECLINE;
00372 }
00373
00374 AST_TEST_REGISTER(test_kqueue_timing);
00375 return AST_MODULE_LOAD_SUCCESS;
00376 }
00377
00378 static int unload_module(void)
00379 {
00380 int res;
00381
00382 AST_TEST_UNREGISTER(test_kqueue_timing);
00383 if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) {
00384 ao2_ref(kqueue_timers, -1);
00385 kqueue_timers = NULL;
00386 }
00387
00388 return res;
00389 }
00390
00391 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "KQueue Timing Interface",
00392 .load = load_module,
00393 .unload = unload_module,
00394 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
00395 );