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