From a56de89a4cd467c5cca7a11032cbf6bd5288b95b Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sun, 30 Mar 2025 12:01:16 +0200 Subject: [PATCH] kernel accel: if switching takes a long time, back off That can happen even with not many sessions, e.g. on a loaded machine with a lot of routes --- l2tpns.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/l2tpns.c b/l2tpns.c index 2e0d89b..febcab0 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -140,6 +140,7 @@ 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 +static double last_poll_time = 0; // Last time we polled fds linked_list *loaded_plugins; linked_list *plugins[MAX_PLUGIN_TYPES]; @@ -1387,7 +1388,7 @@ 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 // 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) +static void set_kernel_accel(sessionidt s, int set, int nodelay) { if (set && !can_kernel_accel(s)) /* Still cannot enable it */ @@ -1418,10 +1419,14 @@ static void set_kernel_accel(sessionidt s, int set, int now) } } - if (set && !now && kernel_switches >= MAX_KERNEL_SWITCHES) + double cur_switch; + now(&cur_switch); + if (set && !nodelay && + (kernel_switches >= MAX_KERNEL_SWITCHES + || cur_switch - last_poll_time > 0.02)) { - // We already performed many switches, throttle a bit by just - // marking as pending + // We have already been switching a lot or working at for 20ms since last poll, + // throttle a bit by just marking as pending sess_local[s].needs_switch = 1; return; } @@ -5999,7 +6004,7 @@ static void mainloop(void) n = epoll_wait(epollfd, events, maxevent, 100); // timeout 100ms (1/10th sec) STAT(select_called); - TIME = now(NULL); + TIME = now(&last_poll_time); if (n < 0) { if (errno == EINTR || @@ -6387,7 +6392,7 @@ static void mainloop(void) // Handle trying to enable kernel accel { static double last_switch = 0; - double this_switch; + double this_switch, cur_switch; double diff; TIME = now(&this_switch); @@ -6399,14 +6404,21 @@ static void mainloop(void) { kernel_switches = 0; + cur_switch = this_switch; 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); + TIME = now(&cur_switch); + if (cur_switch - this_switch > 0.02) + // We have already taken 20ms sec doing switches, backoff + break; + } } - last_switch = this_switch; + last_switch = cur_switch; } }