From a957ff08ee42feb1cb2594073b802be0ea6d16f1 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sun, 21 Jan 2024 03:09:43 +0100 Subject: [PATCH] Throttle switching kernel acceleration Creating/destroying interfaces etc. does take some time. When e.g. receiving a lot of sessions as new slave, we don't want to stay stuck creating hundreds of interfaces while we are already receiving control messages that we have to forward to master not too late. Switching kernel acceleration can wait a bit most of the time. --- l2tpns.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++---------- l2tpns.h | 3 +++ 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/l2tpns.c b/l2tpns.c index e5e877a..04ad4e0 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -137,6 +137,8 @@ static char time_now_string[64] = {0}; // Current time as a string. static int time_changed = 0; // time_now changed char main_quit = 0; // True if we're in the process of exiting. static char main_reload = 0; // Re-load pending +#define MAX_KERNEL_SWITCHES 20 // Maximum number of kernel switches per 1/10th second +static int kernel_switches = 0; // How many kernel switches we performed since last cleanup linked_list *loaded_plugins; linked_list *plugins[MAX_PLUGIN_TYPES]; @@ -1280,7 +1282,8 @@ static int delete_kernel_accel(sessionidt s) // // Enable (set=1) or disable (set=0) kernel PPP acceleration // This basically calls create/delete_kernel_accel, but also updates routes -static void set_kernel_accel(sessionidt s, int set) +// If now is 0, we may delay this if we have already made a lot of switches since last cleanup +static void set_kernel_accel(sessionidt s, int set, int now) { if (set && !can_kernel_accel(s)) /* Still cannot enable it */ @@ -1291,6 +1294,16 @@ static void set_kernel_accel(sessionidt s, int set) /* We don't know the tunnel yet */ return; + if (set && !now && kernel_switches >= MAX_KERNEL_SWITCHES) + { + // We already performed many switches, throttle a bit by just + // marking as pending + sess_local[s].needs_switch = 1; + return; + } + kernel_switches++; + sess_local[s].needs_switch = 0; + routesset(s, &session[s], 0); if (session[s].ppp.ipv6cp == Opened) routes6set(s, &session[s], 0); @@ -1323,7 +1336,7 @@ void switch_kernel_accel(sessionidt s) return; /* Try to enable */ - set_kernel_accel(s, 1); + set_kernel_accel(s, 1, 0); } else { @@ -1334,7 +1347,7 @@ void switch_kernel_accel(sessionidt s) return; /* Has to disable it */ - set_kernel_accel(s, 0); + set_kernel_accel(s, 0, 1); } } @@ -5805,7 +5818,7 @@ static void mainloop(void) sessionidt sid; for (sid = 1; sid <= config->cluster_highest_sessionid ; ++sid) if (session[sid].tunnel == tid) - set_kernel_accel(sid, 0); + set_kernel_accel(sid, 0, 1); delete_kernel_tunnel(tid); } @@ -5833,12 +5846,12 @@ static void mainloop(void) if (s < 0) { LOG(1, sid, tid, "Error on pppox socket: %s\n", strerror(errno)); - set_kernel_accel(sid, 0); + set_kernel_accel(sid, 0, 1); } else if (s == 0) { LOG(1, sid, tid, "EOF on pppox socket\n"); - set_kernel_accel(sid, 0); + set_kernel_accel(sid, 0, 1); } else { @@ -5857,12 +5870,12 @@ static void mainloop(void) if (s < 0) { LOG(1, sid, tid, "Error on ppp channel: %s\n", strerror(errno)); - set_kernel_accel(sid, 0); + set_kernel_accel(sid, 0, 1); } else if (s == 0) { LOG(1, sid, tid, "EOF on ppp channel\n"); - set_kernel_accel(sid, 0); + set_kernel_accel(sid, 0, 1); } else processppp_from_kernel(sid, p, s, NULL); @@ -5878,12 +5891,12 @@ static void mainloop(void) if (s < 0) { LOG(1, sid, tid, "Error on ppp if: %s\n", strerror(errno)); - set_kernel_accel(sid, 0); + set_kernel_accel(sid, 0, 1); } else if (s == 0) { LOG(1, sid, tid, "EOF on ppp if\n"); - set_kernel_accel(sid, 0); + set_kernel_accel(sid, 0, 1); } else processppp_from_kernel(sid, p, s, NULL); @@ -6051,6 +6064,32 @@ static void mainloop(void) next_cluster_ping = TIME + config->cluster_hb_interval; } + // Handle trying to enable kernel accel + { + static double last_switch = 0; + double this_switch; + double diff; + + TIME = now(&this_switch); + diff = this_switch - last_switch; + + // Run during idle time (after we've handled + // all incoming packets) or every 1/10th sec + if (!more || diff > 0.1) + { + kernel_switches = 0; + + for (i = 1; i <= config->cluster_highest_sessionid; i++) + { + // Delayed kernel switch + if (session[i].ppp.lcp == Opened && sess_local[i].needs_switch) + set_kernel_accel(i, can_kernel_accel(i), 0); + } + + last_switch = this_switch; + } + } + if (!config->cluster_iam_master) continue; diff --git a/l2tpns.h b/l2tpns.h index 7c5ad72..59852b9 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -452,6 +452,9 @@ typedef struct // time in milliseconds of the last fragment. uint64_t prev_time; + // Pending kernel switch + int needs_switch; + // l2tp PPPoL2TP socket int pppox_fd; struct pppol2tp_ioc_stats last_stats;