Mercurial > hg > nginx-quic
view src/event/modules/ngx_kqueue_module.c @ 4569:1db899642518
Upstream: reject upstreams without normal servers.
Such upstreams cause CPU hog later in the code as number of peers isn't
expected to be 0. Currently this may happen either if there are only backup
servers defined in an upstream block, or if server with ipv6 address used
in an upstream block.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 02 Apr 2012 21:29:35 +0000 |
parents | d620f497c50f |
children | 67653855682e |
line wrap: on
line source
/* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_event.h> typedef struct { ngx_uint_t changes; ngx_uint_t events; } ngx_kqueue_conf_t; static ngx_int_t ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer); static void ngx_kqueue_done(ngx_cycle_t *cycle); static ngx_int_t ngx_kqueue_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); static ngx_int_t ngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); static ngx_int_t ngx_kqueue_set_event(ngx_event_t *ev, ngx_int_t filter, ngx_uint_t flags); static ngx_int_t ngx_kqueue_process_changes(ngx_cycle_t *cycle, ngx_uint_t try); static ngx_int_t ngx_kqueue_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); static ngx_inline void ngx_kqueue_dump_event(ngx_log_t *log, struct kevent *kev); static void *ngx_kqueue_create_conf(ngx_cycle_t *cycle); static char *ngx_kqueue_init_conf(ngx_cycle_t *cycle, void *conf); int ngx_kqueue = -1; /* * The "change_list" should be declared as ngx_thread_volatile. * However, the use of the change_list is localized in kqueue functions and * is protected by the mutex so even the "icc -ipo" should not build the code * with the race condition. Thus we avoid the declaration to make a more * readable code. */ static struct kevent *change_list, *change_list0, *change_list1; static struct kevent *event_list; static ngx_uint_t max_changes, nchanges, nevents; #if (NGX_THREADS) static ngx_mutex_t *list_mutex; static ngx_mutex_t *kevent_mutex; #endif static ngx_str_t kqueue_name = ngx_string("kqueue"); static ngx_command_t ngx_kqueue_commands[] = { { ngx_string("kqueue_changes"), NGX_EVENT_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, 0, offsetof(ngx_kqueue_conf_t, changes), NULL }, { ngx_string("kqueue_events"), NGX_EVENT_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, 0, offsetof(ngx_kqueue_conf_t, events), NULL }, ngx_null_command }; ngx_event_module_t ngx_kqueue_module_ctx = { &kqueue_name, ngx_kqueue_create_conf, /* create configuration */ ngx_kqueue_init_conf, /* init configuration */ { ngx_kqueue_add_event, /* add an event */ ngx_kqueue_del_event, /* delete an event */ ngx_kqueue_add_event, /* enable an event */ ngx_kqueue_del_event, /* disable an event */ NULL, /* add an connection */ NULL, /* delete an connection */ ngx_kqueue_process_changes, /* process the changes */ ngx_kqueue_process_events, /* process the events */ ngx_kqueue_init, /* init the events */ ngx_kqueue_done /* done the events */ } }; ngx_module_t ngx_kqueue_module = { NGX_MODULE_V1, &ngx_kqueue_module_ctx, /* module context */ ngx_kqueue_commands, /* module directives */ NGX_EVENT_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; static ngx_int_t ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer) { ngx_kqueue_conf_t *kcf; struct timespec ts; #if (NGX_HAVE_TIMER_EVENT) struct kevent kev; #endif kcf = ngx_event_get_conf(cycle->conf_ctx, ngx_kqueue_module); if (ngx_kqueue == -1) { ngx_kqueue = kqueue(); if (ngx_kqueue == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "kqueue() failed"); return NGX_ERROR; } #if (NGX_THREADS) list_mutex = ngx_mutex_init(cycle->log, 0); if (list_mutex == NULL) { return NGX_ERROR; } kevent_mutex = ngx_mutex_init(cycle->log, 0); if (kevent_mutex == NULL) { return NGX_ERROR; } #endif } if (max_changes < kcf->changes) { if (nchanges) { ts.tv_sec = 0; ts.tv_nsec = 0; if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "kevent() failed"); return NGX_ERROR; } nchanges = 0; } if (change_list0) { ngx_free(change_list0); } change_list0 = ngx_alloc(kcf->changes * sizeof(struct kevent), cycle->log); if (change_list0 == NULL) { return NGX_ERROR; } if (change_list1) { ngx_free(change_list1); } change_list1 = ngx_alloc(kcf->changes * sizeof(struct kevent), cycle->log); if (change_list1 == NULL) { return NGX_ERROR; } change_list = change_list0; } max_changes = kcf->changes; if (nevents < kcf->events) { if (event_list) { ngx_free(event_list); } event_list = ngx_alloc(kcf->events * sizeof(struct kevent), cycle->log); if (event_list == NULL) { return NGX_ERROR; } } ngx_event_flags = NGX_USE_ONESHOT_EVENT |NGX_USE_KQUEUE_EVENT |NGX_USE_VNODE_EVENT; #if (NGX_HAVE_TIMER_EVENT) if (timer) { kev.ident = 0; kev.filter = EVFILT_TIMER; kev.flags = EV_ADD|EV_ENABLE; kev.fflags = 0; kev.data = timer; kev.udata = 0; ts.tv_sec = 0; ts.tv_nsec = 0; if (kevent(ngx_kqueue, &kev, 1, NULL, 0, &ts) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "kevent(EVFILT_TIMER) failed"); return NGX_ERROR; } ngx_event_flags |= NGX_USE_TIMER_EVENT; } #endif #if (NGX_HAVE_CLEAR_EVENT) ngx_event_flags |= NGX_USE_CLEAR_EVENT; #else ngx_event_flags |= NGX_USE_LEVEL_EVENT; #endif #if (NGX_HAVE_LOWAT_EVENT) ngx_event_flags |= NGX_USE_LOWAT_EVENT; #endif nevents = kcf->events; ngx_io = ngx_os_io; ngx_event_actions = ngx_kqueue_module_ctx.actions; return NGX_OK; } static void ngx_kqueue_done(ngx_cycle_t *cycle) { if (close(ngx_kqueue) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "kqueue close() failed"); } ngx_kqueue = -1; #if (NGX_THREADS) ngx_mutex_destroy(kevent_mutex); ngx_mutex_destroy(list_mutex); #endif ngx_free(change_list1); ngx_free(change_list0); ngx_free(event_list); change_list1 = NULL; change_list0 = NULL; change_list = NULL; event_list = NULL; max_changes = 0; nchanges = 0; nevents = 0; } static ngx_int_t ngx_kqueue_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { ngx_int_t rc; #if 0 ngx_event_t *e; ngx_connection_t *c; #endif ev->active = 1; ev->disabled = 0; ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0; ngx_mutex_lock(list_mutex); #if 0 if (ev->index < nchanges && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1) == (uintptr_t) ev) { if (change_list[ev->index].flags == EV_DISABLE) { /* * if the EV_DISABLE is still not passed to a kernel * we will not pass it */ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, "kevent activated: %d: ft:%i", ngx_event_ident(ev->data), event); if (ev->index < --nchanges) { e = (ngx_event_t *) ((uintptr_t) change_list[nchanges].udata & (uintptr_t) ~1); change_list[ev->index] = change_list[nchanges]; e->index = ev->index; } ngx_mutex_unlock(list_mutex); return NGX_OK; } c = ev->data; ngx_log_error(NGX_LOG_ALERT, ev->log, 0, "previous event on #%d were not passed in kernel", c->fd); ngx_mutex_unlock(list_mutex); return NGX_ERROR; } #endif rc = ngx_kqueue_set_event(ev, event, EV_ADD|EV_ENABLE|flags); ngx_mutex_unlock(list_mutex); return rc; } static ngx_int_t ngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { ngx_int_t rc; ngx_event_t *e; ev->active = 0; ev->disabled = 0; ngx_mutex_lock(list_mutex); if (ev->index < nchanges && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1) == (uintptr_t) ev) { ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, "kevent deleted: %d: ft:%i", ngx_event_ident(ev->data), event); /* if the event is still not passed to a kernel we will not pass it */ nchanges--; if (ev->index < nchanges) { e = (ngx_event_t *) ((uintptr_t) change_list[nchanges].udata & (uintptr_t) ~1); change_list[ev->index] = change_list[nchanges]; e->index = ev->index; } ngx_mutex_unlock(list_mutex); return NGX_OK; } /* * when the file descriptor is closed the kqueue automatically deletes * its filters so we do not need to delete explicity the event * before the closing the file descriptor. */ if (flags & NGX_CLOSE_EVENT) { ngx_mutex_unlock(list_mutex); return NGX_OK; } if (flags & NGX_DISABLE_EVENT) { ev->disabled = 1; } else { flags |= EV_DELETE; } rc = ngx_kqueue_set_event(ev, event, flags); ngx_mutex_unlock(list_mutex); return rc; } static ngx_int_t ngx_kqueue_set_event(ngx_event_t *ev, ngx_int_t filter, ngx_uint_t flags) { struct kevent *kev; struct timespec ts; ngx_connection_t *c; c = ev->data; ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0, "kevent set event: %d: ft:%i fl:%04Xi", c->fd, filter, flags); if (nchanges >= max_changes) { ngx_log_error(NGX_LOG_WARN, ev->log, 0, "kqueue change list is filled up"); ts.tv_sec = 0; ts.tv_nsec = 0; if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts) == -1) { ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent() failed"); return NGX_ERROR; } nchanges = 0; } kev = &change_list[nchanges]; kev->ident = c->fd; kev->filter = (short) filter; kev->flags = (u_short) flags; kev->udata = NGX_KQUEUE_UDATA_T ((uintptr_t) ev | ev->instance); if (filter == EVFILT_VNODE) { kev->fflags = NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND |NOTE_ATTRIB|NOTE_RENAME #if (__FreeBSD__ == 4 && __FreeBSD_version >= 430000) \ || __FreeBSD_version >= 500018 |NOTE_REVOKE #endif ; kev->data = 0; } else { #if (NGX_HAVE_LOWAT_EVENT) if (flags & NGX_LOWAT_EVENT) { kev->fflags = NOTE_LOWAT; kev->data = ev->available; } else { kev->fflags = 0; kev->data = 0; } #else kev->fflags = 0; kev->data = 0; #endif } ev->index = nchanges; nchanges++; if (flags & NGX_FLUSH_EVENT) { ts.tv_sec = 0; ts.tv_nsec = 0; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "kevent flush"); if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts) == -1) { ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent() failed"); return NGX_ERROR; } nchanges = 0; } return NGX_OK; } static ngx_int_t ngx_kqueue_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) { int events, n; ngx_int_t i, instance; ngx_uint_t level; ngx_err_t err; ngx_event_t *ev, **queue; struct timespec ts, *tp; if (ngx_threaded) { if (ngx_kqueue_process_changes(cycle, 0) == NGX_ERROR) { return NGX_ERROR; } n = 0; } else { n = (int) nchanges; nchanges = 0; } if (timer == NGX_TIMER_INFINITE) { tp = NULL; } else { ts.tv_sec = timer / 1000; ts.tv_nsec = (timer % 1000) * 1000000; /* * 64-bit Darwin kernel has the bug: kernel level ts.tv_nsec is * the int32_t while user level ts.tv_nsec is the long (64-bit), * so on the big endian PowerPC all nanoseconds are lost. */ #if (NGX_DARWIN_KEVENT_BUG) ts.tv_nsec <<= 32; #endif tp = &ts; } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "kevent timer: %M, changes: %d", timer, n); events = kevent(ngx_kqueue, change_list, n, event_list, (int) nevents, tp); err = (events == -1) ? ngx_errno : 0; if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { ngx_time_update(); } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "kevent events: %d", events); if (err) { if (err == NGX_EINTR) { if (ngx_event_timer_alarm) { ngx_event_timer_alarm = 0; return NGX_OK; } level = NGX_LOG_INFO; } else { level = NGX_LOG_ALERT; } ngx_log_error(level, cycle->log, err, "kevent() failed"); return NGX_ERROR; } if (events == 0) { if (timer != NGX_TIMER_INFINITE) { return NGX_OK; } ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "kevent() returned no events without timeout"); return NGX_ERROR; } ngx_mutex_lock(ngx_posted_events_mutex); for (i = 0; i < events; i++) { ngx_kqueue_dump_event(cycle->log, &event_list[i]); if (event_list[i].flags & EV_ERROR) { ngx_log_error(NGX_LOG_ALERT, cycle->log, event_list[i].data, "kevent() error on %d filter:%d flags:%04Xd", event_list[i].ident, event_list[i].filter, event_list[i].flags); continue; } #if (NGX_HAVE_TIMER_EVENT) if (event_list[i].filter == EVFILT_TIMER) { ngx_time_update(); continue; } #endif ev = (ngx_event_t *) event_list[i].udata; switch (event_list[i].filter) { case EVFILT_READ: case EVFILT_WRITE: instance = (uintptr_t) ev & 1; ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1); if (ev->closed || ev->instance != instance) { /* * the stale event from a file descriptor * that was just closed in this iteration */ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "kevent: stale event %p", ev); continue; } if (ev->log && (ev->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { ngx_kqueue_dump_event(ev->log, &event_list[i]); } if (ev->oneshot) { ev->active = 0; } #if (NGX_THREADS) if ((flags & NGX_POST_THREAD_EVENTS) && !ev->accept) { ev->posted_ready = 1; ev->posted_available = event_list[i].data; if (event_list[i].flags & EV_EOF) { ev->posted_eof = 1; ev->posted_errno = event_list[i].fflags; } ngx_locked_post_event(ev, &ngx_posted_events); continue; } #endif ev->available = event_list[i].data; if (event_list[i].flags & EV_EOF) { ev->pending_eof = 1; ev->kq_errno = event_list[i].fflags; } ev->ready = 1; break; case EVFILT_VNODE: ev->kq_vnode = 1; break; case EVFILT_AIO: ev->complete = 1; ev->ready = 1; break; default: ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unexpected kevent() filter %d", event_list[i].filter); continue; } if (flags & NGX_POST_EVENTS) { queue = (ngx_event_t **) (ev->accept ? &ngx_posted_accept_events: &ngx_posted_events); ngx_locked_post_event(ev, queue); continue; } ev->handler(ev); } ngx_mutex_unlock(ngx_posted_events_mutex); return NGX_OK; } static ngx_int_t ngx_kqueue_process_changes(ngx_cycle_t *cycle, ngx_uint_t try) { int n; ngx_int_t rc; ngx_err_t err; struct timespec ts; struct kevent *changes; ngx_mutex_lock(kevent_mutex); ngx_mutex_lock(list_mutex); if (nchanges == 0) { ngx_mutex_unlock(list_mutex); ngx_mutex_unlock(kevent_mutex); return NGX_OK; } changes = change_list; if (change_list == change_list0) { change_list = change_list1; } else { change_list = change_list0; } n = (int) nchanges; nchanges = 0; ngx_mutex_unlock(list_mutex); ts.tv_sec = 0; ts.tv_nsec = 0; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "kevent changes: %d", n); if (kevent(ngx_kqueue, changes, n, NULL, 0, &ts) == -1) { err = ngx_errno; ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT, cycle->log, err, "kevent() failed"); rc = NGX_ERROR; } else { rc = NGX_OK; } ngx_mutex_unlock(kevent_mutex); return rc; } static ngx_inline void ngx_kqueue_dump_event(ngx_log_t *log, struct kevent *kev) { ngx_log_debug6(NGX_LOG_DEBUG_EVENT, log, 0, (kev->ident > 0x8000000 && kev->ident != (unsigned) -1) ? "kevent: %p: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p": "kevent: %d: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p", kev->ident, kev->filter, kev->flags, kev->fflags, kev->data, kev->udata); } static void * ngx_kqueue_create_conf(ngx_cycle_t *cycle) { ngx_kqueue_conf_t *kcf; kcf = ngx_palloc(cycle->pool, sizeof(ngx_kqueue_conf_t)); if (kcf == NULL) { return NULL; } kcf->changes = NGX_CONF_UNSET; kcf->events = NGX_CONF_UNSET; return kcf; } static char * ngx_kqueue_init_conf(ngx_cycle_t *cycle, void *conf) { ngx_kqueue_conf_t *kcf = conf; ngx_conf_init_uint_value(kcf->changes, 512); ngx_conf_init_uint_value(kcf->events, 512); return NGX_CONF_OK; }