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 int 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 int 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 -1;
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 ao2_ref(our_timer, -1);
00210 return -1;
00211 } else {
00212 our_timer->unacked -= quantity;
00213 }
00214
00215 ao2_ref(our_timer, -1);
00216 return 0;
00217 }
00218
00219 static int kqueue_timer_enable_continuous(int handle)
00220 {
00221 struct kqueue_timer *our_timer;
00222
00223 if (!(our_timer = lookup_timer(handle))) {
00224 return -1;
00225 }
00226
00227 kqueue_set_nsecs(our_timer, 1);
00228 our_timer->is_continuous = 1;
00229 our_timer->unacked = 0;
00230 ao2_ref(our_timer, -1);
00231 return 0;
00232 }
00233
00234 static int kqueue_timer_disable_continuous(int handle)
00235 {
00236 struct kqueue_timer *our_timer;
00237
00238 if (!(our_timer = lookup_timer(handle))) {
00239 return -1;
00240 }
00241
00242 kqueue_set_nsecs(our_timer, our_timer->nsecs);
00243 our_timer->is_continuous = 0;
00244 our_timer->unacked = 0;
00245 ao2_ref(our_timer, -1);
00246 return 0;
00247 }
00248
00249 static enum ast_timer_event kqueue_timer_get_event(int handle)
00250 {
00251 enum ast_timer_event res = -1;
00252 struct kqueue_timer *our_timer;
00253 struct timespec sixty_seconds = { 60, 0 };
00254 struct kevent kev;
00255
00256 if (!(our_timer = lookup_timer(handle))) {
00257 return -1;
00258 }
00259
00260
00261 if (our_timer->unacked == 0) {
00262 if (kevent(handle, NULL, 0, &kev, 1, &sixty_seconds) > 0) {
00263 our_timer->unacked += kev.data;
00264 }
00265 }
00266
00267 if (our_timer->unacked > 0) {
00268 res = our_timer->is_continuous ? AST_TIMING_EVENT_CONTINUOUS : AST_TIMING_EVENT_EXPIRED;
00269 }
00270
00271 ao2_ref(our_timer, -1);
00272 return res;
00273 }
00274
00275 static unsigned int kqueue_timer_get_max_rate(int handle)
00276 {
00277
00278 return UINT_MAX;
00279 }
00280
00281 #ifdef TEST_FRAMEWORK
00282 AST_TEST_DEFINE(test_kqueue_timing)
00283 {
00284 int res = AST_TEST_PASS, handle, i;
00285 uint64_t diff;
00286 struct pollfd pfd = { 0, POLLIN, 0 };
00287 struct kqueue_timer *kt;
00288 struct timeval start;
00289
00290 switch (cmd) {
00291 case TEST_INIT:
00292 info->name = "test_kqueue_timing";
00293 info->category = "/res/res_timing_kqueue/";
00294 info->summary = "Test KQueue timing interface";
00295 info->description = "Verify that the KQueue timing interface correctly generates timing events";
00296 return AST_TEST_NOT_RUN;
00297 case TEST_EXECUTE:
00298 break;
00299 }
00300
00301 if (!(handle = kqueue_timer_open())) {
00302 ast_test_status_update(test, "Cannot open timer!\n");
00303 return AST_TEST_FAIL;
00304 }
00305
00306 do {
00307 pfd.fd = handle;
00308 if (kqueue_timer_set_rate(handle, 1000)) {
00309 ast_test_status_update(test, "Cannot set timer rate to 1000/s\n");
00310 res = AST_TEST_FAIL;
00311 break;
00312 }
00313 if (ast_poll(&pfd, 1, 1000) < 1) {
00314 ast_test_status_update(test, "Polling on a kqueue doesn't work\n");
00315 res = AST_TEST_FAIL;
00316 break;
00317 }
00318 if (pfd.revents != POLLIN) {
00319 ast_test_status_update(test, "poll() should have returned POLLIN, but instead returned %hd\n", pfd.revents);
00320 res = AST_TEST_FAIL;
00321 break;
00322 }
00323 if (!(kt = lookup_timer(handle))) {
00324 ast_test_status_update(test, "Could not find timer structure in container?!!\n");
00325 res = AST_TEST_FAIL;
00326 break;
00327 }
00328 if (kqueue_timer_get_event(handle) <= 0) {
00329 ast_test_status_update(test, "No events generated after a poll returned successfully?!!\n");
00330 res = AST_TEST_FAIL;
00331 break;
00332 }
00333 #if 0
00334 if (kt->unacked == 0) {
00335 ast_test_status_update(test, "Unacked events is 0, but there should be at least 1.\n");
00336 res = AST_TEST_FAIL;
00337 break;
00338 }
00339 #endif
00340 kqueue_timer_enable_continuous(handle);
00341 start = ast_tvnow();
00342 for (i = 0; i < 100; i++) {
00343 if (ast_poll(&pfd, 1, 1000) < 1) {
00344 ast_test_status_update(test, "Polling on a kqueue doesn't work\n");
00345 res = AST_TEST_FAIL;
00346 break;
00347 }
00348 if (kqueue_timer_get_event(handle) <= 0) {
00349 ast_test_status_update(test, "No events generated in continuous mode after 1 microsecond?!!\n");
00350 res = AST_TEST_FAIL;
00351 break;
00352 }
00353 }
00354 diff = ast_tvdiff_us(ast_tvnow(), start);
00355 ast_test_status_update(test, "diff is %llu\n", diff);
00356
00357
00358
00359
00360
00361
00362 } while (0);
00363 kqueue_timer_close(handle);
00364 return res;
00365 }
00366 #endif
00367
00368 static int load_module(void)
00369 {
00370 if (!(kqueue_timers = ao2_container_alloc(563, kqueue_timer_hash, kqueue_timer_cmp))) {
00371 return AST_MODULE_LOAD_DECLINE;
00372 }
00373
00374 if (!(timing_funcs_handle = ast_register_timing_interface(&kqueue_timing))) {
00375 ao2_ref(kqueue_timers, -1);
00376 return AST_MODULE_LOAD_DECLINE;
00377 }
00378
00379 AST_TEST_REGISTER(test_kqueue_timing);
00380 return AST_MODULE_LOAD_SUCCESS;
00381 }
00382
00383 static int unload_module(void)
00384 {
00385 int res;
00386
00387 AST_TEST_UNREGISTER(test_kqueue_timing);
00388 if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) {
00389 ao2_ref(kqueue_timers, -1);
00390 kqueue_timers = NULL;
00391 }
00392
00393 return res;
00394 }
00395
00396 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "KQueue Timing Interface",
00397 .load = load_module,
00398 .unload = unload_module,
00399 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
00400 );