diff options
Diffstat (limited to 'lib/event.c')
| -rw-r--r-- | lib/event.c | 384 |
1 files changed, 204 insertions, 180 deletions
diff --git a/lib/event.c b/lib/event.c index a8eb89f48d..fc46a11c0b 100644 --- a/lib/event.c +++ b/lib/event.c @@ -6,6 +6,8 @@ /* #define DEBUG */ #include <zebra.h> + +#include <signal.h> #include <sys/resource.h> #include "frrevent.h" @@ -55,11 +57,6 @@ static int event_timer_cmp(const struct event *a, const struct event *b) DECLARE_HEAP(event_timer_list, struct event, timeritem, event_timer_cmp); -#if defined(__APPLE__) -#include <mach/mach.h> -#include <mach/mach_time.h> -#endif - #define AWAKEN(m) \ do { \ const unsigned char wakebyte = 0x01; \ @@ -75,48 +72,48 @@ static struct list *masters; static void thread_free(struct event_loop *master, struct event *thread); -#ifndef EXCLUDE_CPU_TIME -#define EXCLUDE_CPU_TIME 0 -#endif -#ifndef CONSUMED_TIME_CHECK -#define CONSUMED_TIME_CHECK 0 -#endif - -bool cputime_enabled = !EXCLUDE_CPU_TIME; +bool cputime_enabled = true; unsigned long cputime_threshold = CONSUMED_TIME_CHECK; unsigned long walltime_threshold = CONSUMED_TIME_CHECK; /* CLI start ---------------------------------------------------------------- */ #include "lib/event_clippy.c" -static unsigned int cpu_record_hash_key(const struct cpu_event_history *a) +static uint32_t cpu_record_hash_key(const struct cpu_event_history *a) { int size = sizeof(a->func); return jhash(&a->func, size, 0); } -static bool cpu_record_hash_cmp(const struct cpu_event_history *a, - const struct cpu_event_history *b) +static int cpu_record_hash_cmp(const struct cpu_event_history *a, + const struct cpu_event_history *b) { - return a->func == b->func; + return numcmp((uintptr_t)a->func, (uintptr_t)b->func); } -static void *cpu_record_hash_alloc(struct cpu_event_history *a) +DECLARE_HASH(cpu_records, struct cpu_event_history, item, cpu_record_hash_cmp, + cpu_record_hash_key); + +static struct cpu_event_history *cpu_records_get(struct event_loop *loop, + void (*func)(struct event *e), + const char *funcname) { - struct cpu_event_history *new; + struct cpu_event_history ref = { .func = func }, *res; - new = XCALLOC(MTYPE_EVENT_STATS, sizeof(struct cpu_event_history)); - new->func = a->func; - new->funcname = a->funcname; - return new; + res = cpu_records_find(loop->cpu_records, &ref); + if (!res) { + res = XCALLOC(MTYPE_EVENT_STATS, sizeof(*res)); + res->func = func; + res->funcname = funcname; + cpu_records_add(loop->cpu_records, res); + } + return res; } -static void cpu_record_hash_free(void *a) +static void cpu_records_free(struct cpu_event_history **p) { - struct cpu_event_history *hist = a; - - XFREE(MTYPE_EVENT_STATS, hist); + XFREE(MTYPE_EVENT_STATS, *p); } static void vty_out_cpu_event_history(struct vty *vty, @@ -136,14 +133,11 @@ static void vty_out_cpu_event_history(struct vty *vty, a->types & (1 << EVENT_EXECUTE) ? 'X' : ' ', a->funcname); } -static void cpu_record_hash_print(struct hash_bucket *bucket, void *args[]) +static void cpu_record_print_one(struct vty *vty, uint8_t filter, + struct cpu_event_history *totals, + const struct cpu_event_history *a) { - struct cpu_event_history *totals = args[0]; struct cpu_event_history copy; - struct vty *vty = args[1]; - uint8_t *filter = args[2]; - - struct cpu_event_history *a = bucket->data; copy.total_active = atomic_load_explicit(&a->total_active, memory_order_seq_cst); @@ -165,7 +159,7 @@ static void cpu_record_hash_print(struct hash_bucket *bucket, void *args[]) copy.types = atomic_load_explicit(&a->types, memory_order_seq_cst); copy.funcname = a->funcname; - if (!(copy.types & *filter)) + if (!(copy.types & filter)) return; vty_out_cpu_event_history(vty, ©); @@ -185,7 +179,6 @@ static void cpu_record_hash_print(struct hash_bucket *bucket, void *args[]) static void cpu_record_print(struct vty *vty, uint8_t filter) { struct cpu_event_history tmp; - void *args[3] = {&tmp, vty, &filter}; struct event_loop *m; struct listnode *ln; @@ -220,15 +213,15 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) "Active Runtime(ms) Invoked Avg uSec Max uSecs"); vty_out(vty, " Avg uSec Max uSecs"); vty_out(vty, - " CPU_Warn Wall_Warn Starv_Warn Type Thread\n"); - - if (m->cpu_record->count) - hash_iterate( - m->cpu_record, - (void (*)(struct hash_bucket *, - void *))cpu_record_hash_print, - args); - else + " CPU_Warn Wall_Warn Starv_Warn Type Event\n"); + + if (cpu_records_count(m->cpu_records)) { + struct cpu_event_history *rec; + + frr_each (cpu_records, m->cpu_records, rec) + cpu_record_print_one(vty, filter, &tmp, + rec); + } else vty_out(vty, "No data to display yet.\n"); vty_out(vty, "\n"); @@ -236,47 +229,41 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) } vty_out(vty, "\n"); - vty_out(vty, "Total thread statistics\n"); + vty_out(vty, "Total Event statistics\n"); vty_out(vty, "-------------------------\n"); vty_out(vty, "%30s %18s %18s\n", "", "CPU (user+system):", "Real (wall-clock):"); vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs"); - vty_out(vty, " Avg uSec Max uSecs CPU_Warn Wall_Warn"); - vty_out(vty, " Type Thread\n"); + vty_out(vty, " Avg uSec Max uSecs CPU_Warn Wall_Warn Starv_Warn"); + vty_out(vty, " Type Event\n"); if (tmp.total_calls > 0) vty_out_cpu_event_history(vty, &tmp); } -static void cpu_record_hash_clear(struct hash_bucket *bucket, void *args[]) -{ - uint8_t *filter = args[0]; - struct hash *cpu_record = args[1]; - - struct cpu_event_history *a = bucket->data; - - if (!(a->types & *filter)) - return; - - hash_release(cpu_record, bucket->data); -} - static void cpu_record_clear(uint8_t filter) { - uint8_t *tmp = &filter; struct event_loop *m; struct listnode *ln; frr_with_mutex (&masters_mtx) { for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) { frr_with_mutex (&m->mtx) { - void *args[2] = {tmp, m->cpu_record}; + struct cpu_event_history *item; + struct cpu_records_head old[1]; + + cpu_records_init(old); + cpu_records_swap_all(old, m->cpu_records); + + while ((item = cpu_records_pop(old))) { + if (item->types & filter) + cpu_records_free(&item); + else + cpu_records_add(m->cpu_records, + item); + } - hash_iterate( - m->cpu_record, - (void (*)(struct hash_bucket *, - void *))cpu_record_hash_clear, - args); + cpu_records_fini(old); } } } @@ -317,13 +304,16 @@ static uint8_t parse_filter(const char *filterstr) return filter; } -DEFUN_NOSH (show_thread_cpu, - show_thread_cpu_cmd, - "show thread cpu [FILTER]", - SHOW_STR - "Thread information\n" - "Thread CPU usage\n" - "Display filter (rwtex)\n") +#if CONFDATE > 20240707 + CPP_NOTICE("Remove `show thread ...` commands") +#endif +DEFUN_NOSH (show_event_cpu, + show_event_cpu_cmd, + "show event cpu [FILTER]", + SHOW_STR + "Event information\n" + "Event CPU usage\n" + "Display filter (rwtexb)\n") { uint8_t filter = (uint8_t)-1U; int idx = 0; @@ -342,6 +332,14 @@ DEFUN_NOSH (show_thread_cpu, return CMD_SUCCESS; } +ALIAS(show_event_cpu, + show_thread_cpu_cmd, + "show thread cpu [FILTER]", + SHOW_STR + "Thread information\n" + "Thread CPU usage\n" + "Display filter (rwtex)\n") + DEFPY (service_cputime_stats, service_cputime_stats_cmd, "[no] service cputime-stats", @@ -355,7 +353,7 @@ DEFPY (service_cputime_stats, DEFPY (service_cputime_warning, service_cputime_warning_cmd, - "[no] service cputime-warning (1-4294967295)", + "[no] service cputime-warning ![(1-4294967295)]", NO_STR "Set up miscellaneous service\n" "Warn for tasks exceeding CPU usage threshold\n" @@ -368,16 +366,9 @@ DEFPY (service_cputime_warning, return CMD_SUCCESS; } -ALIAS (service_cputime_warning, - no_service_cputime_warning_cmd, - "no service cputime-warning", - NO_STR - "Set up miscellaneous service\n" - "Warn for tasks exceeding CPU usage threshold\n") - DEFPY (service_walltime_warning, service_walltime_warning_cmd, - "[no] service walltime-warning (1-4294967295)", + "[no] service walltime-warning ![(1-4294967295)]", NO_STR "Set up miscellaneous service\n" "Warn for tasks exceeding total wallclock threshold\n" @@ -390,14 +381,7 @@ DEFPY (service_walltime_warning, return CMD_SUCCESS; } -ALIAS (service_walltime_warning, - no_service_walltime_warning_cmd, - "no service walltime-warning", - NO_STR - "Set up miscellaneous service\n" - "Warn for tasks exceeding total wallclock threshold\n") - -static void show_thread_poll_helper(struct vty *vty, struct event_loop *m) +static void show_event_poll_helper(struct vty *vty, struct event_loop *m) { const char *name = m->name ? m->name : "main"; char underline[strlen(name) + 1]; @@ -438,24 +422,30 @@ static void show_thread_poll_helper(struct vty *vty, struct event_loop *m) } } -DEFUN_NOSH (show_thread_poll, - show_thread_poll_cmd, - "show thread poll", - SHOW_STR - "Thread information\n" - "Show poll FD's and information\n") +DEFUN_NOSH (show_event_poll, + show_event_poll_cmd, + "show event poll", + SHOW_STR + "Event information\n" + "Event Poll Information\n") { struct listnode *node; struct event_loop *m; frr_with_mutex (&masters_mtx) { for (ALL_LIST_ELEMENTS_RO(masters, node, m)) - show_thread_poll_helper(vty, m); + show_event_poll_helper(vty, m); } return CMD_SUCCESS; } +ALIAS(show_event_poll, + show_thread_poll_cmd, + "show thread poll", + SHOW_STR + "Thread information\n" + "Show poll FD's and information\n") DEFUN (clear_thread_cpu, clear_thread_cpu_cmd, @@ -482,7 +472,7 @@ DEFUN (clear_thread_cpu, return CMD_SUCCESS; } -static void show_thread_timers_helper(struct vty *vty, struct event_loop *m) +static void show_event_timers_helper(struct vty *vty, struct event_loop *m) { const char *name = m->name ? m->name : "main"; char underline[strlen(name) + 1]; @@ -499,37 +489,45 @@ static void show_thread_timers_helper(struct vty *vty, struct event_loop *m) } } -DEFPY_NOSH (show_thread_timers, - show_thread_timers_cmd, - "show thread timers", - SHOW_STR - "Thread information\n" - "Show all timers and how long they have in the system\n") +DEFPY_NOSH (show_event_timers, + show_event_timers_cmd, + "show event timers", + SHOW_STR + "Event information\n" + "Show all timers and how long they have in the system\n") { struct listnode *node; struct event_loop *m; frr_with_mutex (&masters_mtx) { for (ALL_LIST_ELEMENTS_RO(masters, node, m)) - show_thread_timers_helper(vty, m); + show_event_timers_helper(vty, m); } return CMD_SUCCESS; } +ALIAS(show_event_timers, + show_thread_timers_cmd, + "show thread timers", + SHOW_STR + "Thread information\n" + "Show all timers and how long they have in the system\n") + void event_cmd_init(void) { install_element(VIEW_NODE, &show_thread_cpu_cmd); + install_element(VIEW_NODE, &show_event_cpu_cmd); install_element(VIEW_NODE, &show_thread_poll_cmd); + install_element(VIEW_NODE, &show_event_poll_cmd); install_element(ENABLE_NODE, &clear_thread_cpu_cmd); install_element(CONFIG_NODE, &service_cputime_stats_cmd); install_element(CONFIG_NODE, &service_cputime_warning_cmd); - install_element(CONFIG_NODE, &no_service_cputime_warning_cmd); install_element(CONFIG_NODE, &service_walltime_warning_cmd); - install_element(CONFIG_NODE, &no_service_walltime_warning_cmd); install_element(VIEW_NODE, &show_thread_timers_cmd); + install_element(VIEW_NODE, &show_event_timers_cmd); } /* CLI end ------------------------------------------------------------------ */ @@ -545,6 +543,7 @@ static void initializer(void) pthread_key_create(&thread_current, NULL); } +#define STUPIDLY_LARGE_FD_SIZE 100000 struct event_loop *event_master_create(const char *name) { struct event_loop *rv; @@ -571,6 +570,13 @@ struct event_loop *event_master_create(const char *name) rv->fd_limit = (int)limit.rlim_cur; } + if (rv->fd_limit > STUPIDLY_LARGE_FD_SIZE) { + zlog_warn("FD Limit set: %u is stupidly large. Is this what you intended? Consider using --limit-fds also limiting size to %u", + rv->fd_limit, STUPIDLY_LARGE_FD_SIZE); + + rv->fd_limit = STUPIDLY_LARGE_FD_SIZE; + } + rv->read = XCALLOC(MTYPE_EVENT_POLL, sizeof(struct event *) * rv->fd_limit); @@ -581,10 +587,7 @@ struct event_loop *event_master_create(const char *name) snprintf(tmhashname, sizeof(tmhashname), "%s - threadmaster event hash", name); - rv->cpu_record = hash_create_size( - 8, (unsigned int (*)(const void *))cpu_record_hash_key, - (bool (*)(const void *, const void *))cpu_record_hash_cmp, - tmhashname); + cpu_records_init(rv->cpu_records); event_list_init(&rv->event); event_list_init(&rv->ready); @@ -702,6 +705,7 @@ void event_master_free_unused(struct event_loop *m) /* Stop thread scheduler. */ void event_master_free(struct event_loop *m) { + struct cpu_event_history *record; struct event *t; frr_with_mutex (&masters_mtx) { @@ -724,7 +728,9 @@ void event_master_free(struct event_loop *m) list_delete(&m->cancel_req); m->cancel_req = NULL; - hash_clean_and_free(&m->cpu_record, cpu_record_hash_free); + while ((record = cpu_records_pop(m->cpu_records))) + cpu_records_free(&record); + cpu_records_fini(m->cpu_records); XFREE(MTYPE_EVENT_MASTER, m->name); XFREE(MTYPE_EVENT_MASTER, m->handler.pfds); @@ -797,7 +803,6 @@ static struct event *thread_get(struct event_loop *m, uint8_t type, const struct xref_eventsched *xref) { struct event *thread = event_list_pop(&m->unuse); - struct cpu_event_history tmp; if (!thread) { thread = XCALLOC(MTYPE_THREAD, sizeof(struct event)); @@ -811,7 +816,12 @@ static struct event *thread_get(struct event_loop *m, uint8_t type, thread->master = m; thread->arg = arg; thread->yield = EVENT_YIELD_TIME_SLOT; /* default */ - thread->ref = NULL; + /* thread->ref is zeroed either by XCALLOC above or by memset before + * being put on the "unuse" list by thread_add_unuse(). + * Setting it here again makes coverity complain about a missing + * lock :( + */ + /* thread->ref = NULL; */ thread->ignore_timer_late = false; /* @@ -825,13 +835,9 @@ static struct event *thread_get(struct event_loop *m, uint8_t type, * hash_get lookups. */ if ((thread->xref && thread->xref->funcname != xref->funcname) - || thread->func != func) { - tmp.func = func; - tmp.funcname = xref->funcname; - thread->hist = - hash_get(m->cpu_record, &tmp, - (void *(*)(void *))cpu_record_hash_alloc); - } + || thread->func != func) + thread->hist = cpu_records_get(m, func, xref->funcname); + thread->hist->total_active++; thread->func = func; thread->xref = xref; @@ -1491,9 +1497,9 @@ void event_cancel(struct event **thread) cr->thread = *thread; listnode_add(master->cancel_req, cr); do_event_cancel(master); - } - *thread = NULL; + *thread = NULL; + } } /** @@ -1620,12 +1626,70 @@ static int thread_process_io_helper(struct event_loop *m, struct event *thread, return 1; } +static inline void thread_process_io_inner_loop(struct event_loop *m, + unsigned int num, + struct pollfd *pfds, nfds_t *i, + uint32_t *ready) +{ + /* no event for current fd? immediately continue */ + if (pfds[*i].revents == 0) + return; + + *ready = *ready + 1; + + /* + * Unless someone has called event_cancel from another + * pthread, the only thing that could have changed in + * m->handler.pfds while we were asleep is the .events + * field in a given pollfd. Barring event_cancel() that + * value should be a superset of the values we have in our + * copy, so there's no need to update it. Similarily, + * barring deletion, the fd should still be a valid index + * into the master's pfds. + * + * We are including POLLERR here to do a READ event + * this is because the read should fail and the + * read function should handle it appropriately + */ + if (pfds[*i].revents & (POLLIN | POLLHUP | POLLERR)) { + thread_process_io_helper(m, m->read[pfds[*i].fd], POLLIN, + pfds[*i].revents, *i); + } + if (pfds[*i].revents & POLLOUT) + thread_process_io_helper(m, m->write[pfds[*i].fd], POLLOUT, + pfds[*i].revents, *i); + + /* + * if one of our file descriptors is garbage, remove the same + * from both pfds + update sizes and index + */ + if (pfds[*i].revents & POLLNVAL) { + memmove(m->handler.pfds + *i, m->handler.pfds + *i + 1, + (m->handler.pfdcount - *i - 1) * sizeof(struct pollfd)); + m->handler.pfdcount--; + m->handler.pfds[m->handler.pfdcount].fd = 0; + m->handler.pfds[m->handler.pfdcount].events = 0; + + memmove(pfds + *i, pfds + *i + 1, + (m->handler.copycount - *i - 1) * sizeof(struct pollfd)); + m->handler.copycount--; + m->handler.copy[m->handler.copycount].fd = 0; + m->handler.copy[m->handler.copycount].events = 0; + + *i = *i - 1; + } +} + /** * Process I/O events. * * Walks through file descriptor array looking for those pollfds whose .revents * field has something interesting. Deletes any invalid file descriptors. * + * Try to impart some impartiality to handling of io. The event + * system will cycle through the fd's available for io + * giving each one a chance to go first. + * * @param m the thread master * @param num the number of active file descriptors (return value of poll()) */ @@ -1633,58 +1697,15 @@ static void thread_process_io(struct event_loop *m, unsigned int num) { unsigned int ready = 0; struct pollfd *pfds = m->handler.copy; + nfds_t i, last_read = m->last_read % m->handler.copycount; - for (nfds_t i = 0; i < m->handler.copycount && ready < num; ++i) { - /* no event for current fd? immediately continue */ - if (pfds[i].revents == 0) - continue; + for (i = last_read; i < m->handler.copycount && ready < num; ++i) + thread_process_io_inner_loop(m, num, pfds, &i, &ready); - ready++; + for (i = 0; i < last_read && ready < num; ++i) + thread_process_io_inner_loop(m, num, pfds, &i, &ready); - /* - * Unless someone has called event_cancel from another - * pthread, the only thing that could have changed in - * m->handler.pfds while we were asleep is the .events - * field in a given pollfd. Barring event_cancel() that - * value should be a superset of the values we have in our - * copy, so there's no need to update it. Similarily, - * barring deletion, the fd should still be a valid index - * into the master's pfds. - * - * We are including POLLERR here to do a READ event - * this is because the read should fail and the - * read function should handle it appropriately - */ - if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR)) { - thread_process_io_helper(m, m->read[pfds[i].fd], POLLIN, - pfds[i].revents, i); - } - if (pfds[i].revents & POLLOUT) - thread_process_io_helper(m, m->write[pfds[i].fd], - POLLOUT, pfds[i].revents, i); - - /* - * if one of our file descriptors is garbage, remove the same - * from both pfds + update sizes and index - */ - if (pfds[i].revents & POLLNVAL) { - memmove(m->handler.pfds + i, m->handler.pfds + i + 1, - (m->handler.pfdcount - i - 1) - * sizeof(struct pollfd)); - m->handler.pfdcount--; - m->handler.pfds[m->handler.pfdcount].fd = 0; - m->handler.pfds[m->handler.pfdcount].events = 0; - - memmove(pfds + i, pfds + i + 1, - (m->handler.copycount - i - 1) - * sizeof(struct pollfd)); - m->handler.copycount--; - m->handler.copy[m->handler.copycount].fd = 0; - m->handler.copy[m->handler.copycount].events = 0; - - i--; - } - } + m->last_read++; } /* Add all timers that have popped to the ready list. */ @@ -1864,12 +1885,6 @@ struct event *event_fetch(struct event_loop *m, struct event *fetch) return fetch; } -static unsigned long timeval_elapsed(struct timeval a, struct timeval b) -{ - return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) - + (a.tv_usec - b.tv_usec)); -} - unsigned long event_consumed_time(RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime) { @@ -1972,6 +1987,7 @@ void event_getrusage(RUSAGE_T *r) void event_call(struct event *thread) { RUSAGE_T before, after; + bool suppress_warnings = EVENT_ARG(thread); /* if the thread being called is the CLI, it may change cputime_enabled * ("service cputime-stats" command), which can result in nonsensical @@ -2032,6 +2048,9 @@ void event_call(struct event *thread) atomic_fetch_or_explicit(&thread->hist->types, 1 << thread->add_type, memory_order_seq_cst); + if (suppress_warnings) + return; + if (cputime_enabled_here && cputime_enabled && cputime_threshold && cputime > cputime_threshold) { /* @@ -2066,10 +2085,15 @@ void event_call(struct event *thread) /* Execute thread */ void _event_execute(const struct xref_eventsched *xref, struct event_loop *m, - void (*func)(struct event *), void *arg, int val) + void (*func)(struct event *), void *arg, int val, + struct event **eref) { struct event *thread; + /* Cancel existing scheduled task TODO -- nice to do in 1 lock cycle */ + if (eref) + event_cancel(eref); + /* Get or allocate new thread to execute. */ frr_with_mutex (&m->mtx) { thread = thread_get(m, EVENT_EVENT, func, arg, xref); |
