From 3d7ae10a541629727844163f7d64507baedd6c78 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Mon, 22 Jun 2020 20:20:08 +0200 Subject: Add a shared timer, remove timerfd usage from idle and announce --- announce.c | 91 +++++++++--------------- idle.c | 48 ++++--------- main.c | 4 ++ main.h | 1 + meson.build | 1 + ptimer.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ ptimer.h | 25 +++++++ signal-handler.c | 2 + utils.h | 4 ++ 9 files changed, 289 insertions(+), 95 deletions(-) create mode 100644 ptimer.c create mode 100644 ptimer.h diff --git a/announce.c b/announce.c index 2510d5d..eea668c 100644 --- a/announce.c +++ b/announce.c @@ -1,48 +1,38 @@ #include -#include #include #include #include #include #include -#include #include "main.h" #include "uring.h" #include "announce.h" #include "server.h" +#include "ptimer.h" struct announce { uint64_t value; struct uring_task task; + struct ptimer_task ptask; int mcast_fd; - time_t until; + bool active; }; +#define ANNOUNCE_INTERVAL 3 + static void -announce_cb(struct uring_task *task, int res) +announce_cb(struct ptimer_task *ptask) { - struct announce *announce = container_of(task, struct announce, task); + struct announce *announce = container_of(ptask, struct announce, ptask); struct server *server; - assert_return(task); - assert_task_alive(DBG_ANN, task); - - if (res != sizeof(announce->value)) { - error("timerfd_read: %m"); - return; - } + assert_return(ptask); + assert_task_alive(DBG_ANN, &announce->task); - if (announce->until != 0 && time(NULL) > announce->until) { - debug(DBG_ANN, "stopping announcements"); - announce_stop(); - } else { - debug(DBG_ANN, "announcing servers"); - list_for_each_entry(server, &cfg->servers, list) - server_announce(server, announce->mcast_fd); - } - - uring_read(&announce->task, &announce->value, sizeof(announce->value), announce_cb); + debug(DBG_ANN, "announcing servers"); + list_for_each_entry(server, &cfg->servers, list) + server_announce(server, announce->mcast_fd); } static void @@ -70,7 +60,8 @@ announce_delete() { assert_return(cfg->announce); - debug(DBG_ANN, "closing fd %i", cfg->announce->task.fd); + debug(DBG_ANN, "called"); + announce_stop(); uring_task_destroy(&cfg->announce->task); cfg->announce = NULL; } @@ -78,53 +69,38 @@ announce_delete() void announce_stop() { - struct itimerspec tspec = { - .it_interval = { - .tv_sec = 0, - .tv_nsec = 0 - }, - .it_value = { - .tv_sec = 0, - .tv_nsec = 0 - } - }; + struct announce *announce = cfg->announce; - assert_return(cfg->announce); + assert_return_silent(announce && announce->active); - if (timerfd_settime(cfg->announce->task.fd, 0, &tspec, NULL) != 0) - error("timerfd_settime: %m"); + ptimer_del_task(&announce->ptask); } void announce_start(unsigned duration) { - struct itimerspec tspec = { - .it_interval = { - .tv_sec = 3, - .tv_nsec = 0 - }, - .it_value = { - .tv_sec = 3, - .tv_nsec = 0 - } - }; + struct announce *announce = cfg->announce; + unsigned times; - assert_return(cfg->announce); + assert_return_silent(announce); - if (duration > 0) - cfg->announce->until = time(NULL) + duration; + if (duration == 0) + times = 0; else - cfg->announce->until = 0; + times = MAX(announce->ptask.times, + DIV_ROUND_UP(duration, ANNOUNCE_INTERVAL)); - if (timerfd_settime(cfg->announce->task.fd, 0, &tspec, NULL) != 0) - error("timerfd_settime: %m"); + announce->ptask.times = times; + if (!announce->active) { + ptimer_add_task(&announce->ptask); + announce->active = true; + } } void announce_init() { struct announce *announce; - int afd; int sfd; assert_return(!cfg->announce); @@ -133,18 +109,15 @@ announce_init() if (!announce) die("malloc: %m"); - afd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); - if (afd < 0) - die("timerfd_create: %m"); - sfd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (sfd < 0) die("socket: %m"); uring_task_init(&announce->task, "announce", uring_parent(), announce_free); - uring_task_set_fd(&announce->task, afd); + announce->ptask.interval = ANNOUNCE_INTERVAL; + announce->ptask.cb = announce_cb; + announce->active = false; announce->mcast_fd = sfd; cfg->announce = announce; - uring_read(&announce->task, &announce->value, sizeof(announce->value), announce_cb); } diff --git a/idle.c b/idle.c index e0eb525..0d13b31 100644 --- a/idle.c +++ b/idle.c @@ -1,6 +1,5 @@ #define _GNU_SOURCE #include -#include #include #include #include @@ -12,10 +11,11 @@ #include "uring.h" #include "server.h" #include "idle.h" +#include "ptimer.h" struct idle { + struct ptimer_task ptask; struct uring_task task; - uint64_t value; }; static inline void @@ -315,25 +315,18 @@ idle_check_get_player_count(struct server *server, struct connection *conn) } static void -idle_cb(struct uring_task *task, int res) +idle_cb(struct ptimer_task *ptask) { - struct idle *idle = container_of(task, struct idle, task); + struct idle *idle = container_of(ptask, struct idle, ptask); struct server *server; - assert_return(task); - assert_task_alive(DBG_IDLE, task); - - if (res != sizeof(idle->value)) { - error("timerfd_read: %i", res); - return; - } + assert_return(ptask); + assert_task_alive(DBG_IDLE, &idle->task); - debug(DBG_IDLE, "timer fired (value: %" PRIu64 ")", idle->value); + debug(DBG_IDLE, "timer fired"); list_for_each_entry(server, &cfg->servers, list) server_idle_check(server); - - uring_read(&idle->task, &idle->value, sizeof(idle->value), idle_cb); } static void @@ -360,6 +353,7 @@ idle_delete() assert_return(cfg->idle); debug(DBG_IDLE, "closing fd %i", cfg->idle->task.fd); + ptimer_del_task(&cfg->idle->ptask); uring_task_destroy(&cfg->idle->task); cfg->idle = NULL; } @@ -368,18 +362,6 @@ void idle_init() { struct idle *idle; - int ifd; - struct itimerspec tspec = { - .it_interval = { - .tv_sec = 60, - .tv_nsec = 0 - }, - .it_value = { - /* FIXME: change to 60 */ - .tv_sec = 4, - .tv_nsec = 0 - } - }; assert_return(!cfg->idle); @@ -387,17 +369,11 @@ idle_init() if (!idle) die("malloc: %m"); - ifd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); - if (ifd < 0) - die("timerfd_create: %m"); - - if (timerfd_settime(ifd, 0, &tspec, NULL) != 0) - die("timerfd_settime: %m"); - + idle->ptask.interval = 60; + idle->ptask.times = 0; + idle->ptask.cb = idle_cb; uring_task_init(&idle->task, "idle", uring_parent(), idle_free); - uring_task_set_fd(&idle->task, ifd); + ptimer_add_task(&idle->ptask); cfg->idle = idle; - - uring_read(&idle->task, &idle->value, sizeof(idle->value), idle_cb); } diff --git a/main.c b/main.c index 6a8ab3a..22c14af 100644 --- a/main.c +++ b/main.c @@ -27,6 +27,7 @@ #include "systemd.h" #include "igmp.h" #include "idle.h" +#include "ptimer.h" #define DEFAULT_HOMEDIR_PATH "/home/david/intest" #define DEFAULT_MAIN_CONFIG_FILE_PATH "./mcproxy.conf" @@ -553,6 +554,7 @@ dump_tree() uring_task_refdump(&cfg->task); uring_refdump(); signal_refdump(); + ptimer_refdump(); idle_refdump(); igmp_refdump(); announce_refdump(); @@ -595,6 +597,8 @@ main(int argc, char **argv) uring_init(); + ptimer_init(); + igmp_init(); /* Drop CAP_NET_RAW (if we have it), only used for igmp */ diff --git a/main.h b/main.h index 84d0a1c..c94954a 100644 --- a/main.h +++ b/main.h @@ -152,6 +152,7 @@ struct cfg { struct server_cfg_monitor *server_cfg_monitor; struct signal_ev *signal; struct announce *announce; + struct ptimer *ptimer; struct igmp *igmp; struct idle *idle; struct sd_bus *sd_bus; diff --git a/meson.build b/meson.build index d07a923..d58cb36 100644 --- a/meson.build +++ b/meson.build @@ -15,6 +15,7 @@ mcproxy_sources = [ 'config-parser.c', 'rcon.c', 'idle.c', + 'ptimer.c', 'igmp.c', 'systemd.c', 'utils.c'] diff --git a/ptimer.c b/ptimer.c new file mode 100644 index 0000000..7d8662d --- /dev/null +++ b/ptimer.c @@ -0,0 +1,208 @@ +#include +#include +#include +#include +#include + +#include "main.h" +#include "uring.h" +#include "ptimer.h" + +struct ptimer { + uint64_t value; + time_t previous_time; + struct uring_task task; + unsigned task_count; + struct list_head ptasks; +}; + +static void +ptimer_set(unsigned value, unsigned interval) +{ + struct itimerspec tspec = { + .it_interval = { + .tv_sec = interval, + .tv_nsec = 0 + }, + .it_value = { + .tv_sec = value, + .tv_nsec = 0 + } + }; + + assert_return(cfg->ptimer && cfg->ptimer->task.fd >= 0); + + if (timerfd_settime(cfg->ptimer->task.fd, 0, &tspec, NULL) != 0) + error("timerfd_settime: %m"); +} + +static unsigned +gcd(unsigned a, unsigned b) +{ + if (a == 0) + return b; + return gcd(b % a, a); +} + +static unsigned +array_gcd(unsigned arr[], unsigned n) +{ + unsigned result = arr[0]; + + for (unsigned i = 1; i < n; i++) + result = gcd(arr[i], result); + + return result; +} + +static void +ptimer_tick(struct ptimer *ptimer) +{ + time_t now = time(NULL); + unsigned diff = (unsigned)(now - ptimer->previous_time); + struct ptimer_task *ptask, *ptmp; + + fprintf(stderr, "Got a tick of %u secs\n", diff); + list_for_each_entry_safe(ptask, ptmp, &ptimer->ptasks, list) { + if (ptask->remain > diff) { + ptask->remain -= diff; + continue; + } + + ptask->cb(ptask); + ptask->remain = ptask->interval; + if (ptask->times > 0) { + ptask->times--; + if (ptask->times == 0) + list_del(&ptask->list); + } + } + + ptimer->previous_time = now; +} + +static void +ptimer_reconfig(struct ptimer *ptimer) +{ + struct ptimer_task *ptask; + unsigned i = 0; + unsigned lowest = ~0; + + if (list_empty(&ptimer->ptasks)) { + ptimer_set(0, 0); + return; + } + + unsigned intervals[ptimer->task_count]; + + list_for_each_entry(ptask, &ptimer->ptasks, list) { + if (ptask->remain < lowest) + lowest = ptask->remain; + intervals[i++] = ptask->interval; + } + + fprintf(stderr, "Lowest %u GCD is %u\n", lowest, array_gcd(intervals, i)); + ptimer_set(lowest, array_gcd(intervals, i)); +} + +void +ptimer_del_task(struct ptimer_task *ptask) +{ + struct ptimer *ptimer = cfg->ptimer; + + assert_return(ptask && ptimer && ptimer->task_count > 0); + + list_del(&ptask->list); + ptimer->task_count--; + ptimer_tick(ptimer); + ptimer_reconfig(ptimer); + uring_task_put(&ptimer->task); +} + +void +ptimer_add_task(struct ptimer_task *ptask) +{ + struct ptimer *ptimer = cfg->ptimer; + + assert_return(ptask && ptask->interval > 0 && ptask->cb && ptimer); + + uring_task_get(&ptimer->task); + ptask->remain = ptask->interval; + ptimer_tick(ptimer); + list_add(&ptask->list, &ptimer->ptasks); + ptimer->task_count++; + ptimer_reconfig(ptimer); +} + +void +ptimer_refdump() +{ + assert_return(cfg->ptimer); + + uring_task_refdump(&cfg->ptimer->task); +} + +static void +ptimer_free(struct uring_task *task) +{ + struct ptimer *ptimer = container_of(task, struct ptimer, task); + + assert_return(task); + + debug(DBG_ANN, "task %p, ptimer %p", task, ptimer); + xfree(ptimer); + cfg->ptimer = NULL; +} + +void +ptimer_delete() +{ + assert_return(cfg->ptimer); + + debug(DBG_ANN, "closing fd %i", cfg->ptimer->task.fd); + uring_task_destroy(&cfg->ptimer->task); +} + +static void +ptimer_cb(struct uring_task *task, int res) +{ + struct ptimer *ptimer = container_of(task, struct ptimer, task); + + assert_return(task); + assert_task_alive(DBG_IGMP, task); + + if (res != sizeof(ptimer->value)) { + error("timerfd_read: res: %i, %m", res); + return; + } + + debug(DBG_IGMP, "Called"); + ptimer_tick(ptimer); + uring_read(&ptimer->task, &ptimer->value, sizeof(ptimer->value), ptimer_cb); +} + +void +ptimer_init() +{ + struct ptimer *ptimer; + int tfd; + + assert_return(!cfg->ptimer); + + ptimer = zmalloc(sizeof(*ptimer)); + if (!ptimer) + die("malloc: %m"); + + tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + if (tfd < 0) + die("timerfd_create: %m"); + + ptimer->task_count = 0; + ptimer->previous_time = time(NULL); + list_init(&ptimer->ptasks); + uring_task_init(&ptimer->task, "ptimer", uring_parent(), ptimer_free); + uring_task_set_fd(&ptimer->task, tfd); + cfg->ptimer = ptimer; + uring_read(&ptimer->task, &ptimer->value, sizeof(ptimer->value), ptimer_cb); +} + diff --git a/ptimer.h b/ptimer.h new file mode 100644 index 0000000..9ac53f7 --- /dev/null +++ b/ptimer.h @@ -0,0 +1,25 @@ +#ifndef fooptimerhfoo +#define fooptimerhfoo + +struct ptimer_task { + /* to be set by caller */ + unsigned interval; + unsigned times; + void (*cb)(struct ptimer_task *); + + /* internal */ + unsigned remain; + struct list_head list; +}; + +void ptimer_del_task(struct ptimer_task *ptask); + +void ptimer_add_task(struct ptimer_task *ptask); + +void ptimer_refdump(); + +void ptimer_delete(); + +void ptimer_init(); + +#endif diff --git a/signal-handler.c b/signal-handler.c index 13747dd..67c2e0b 100644 --- a/signal-handler.c +++ b/signal-handler.c @@ -14,6 +14,7 @@ #include "igmp.h" #include "announce.h" #include "idle.h" +#include "ptimer.h" struct signal_ev { struct uring_task task; @@ -90,6 +91,7 @@ signalfd_read(struct uring_task *task, int res) sd_notifyf(0, "STOPPING=1\nSTATUS=Received signal, exiting"); dump_tree(); signal_delete(); + ptimer_delete(); igmp_delete(); announce_delete(); idle_delete(); diff --git a/utils.h b/utils.h index ff29d6c..c36a36c 100644 --- a/utils.h +++ b/utils.h @@ -160,6 +160,10 @@ static inline bool list_empty(struct list_head *list) #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define container_of(ptr, type, member) ({ \ -- cgit v1.2.3