From 265287e4c2d2ca3eedd0d3c7c91f575225afd70f Mon Sep 17 00:00:00 2001 From: Dust Li Date: Thu, 26 Nov 2020 11:08:16 +0800 Subject: [PATCH 2437/2944] ipvs: move estimation from timer to kworker fix #31562403 estimation_timer will iterater the est_list to do estimation for each ipvs stats. When there are lots of services, the list can be very large. We observiced estimation_timer() run for > 200ms on a machine with 104 CPU and 50K services. yunhong-cgl jiang report the same phenomenon before: https://www.spinics.net/lists/lvs-devel/msg05426.html Moving estimation_timer() into kworker also brings some other problem: - The kworker maybe easily delayed since it's priority is lower, when the system load is high, this maybe inaccurate - It's easier to be interrupted by softirq, the estimation maybe even more inaccurate when the est_list is large. There are also some things can be improved: - The _bh in the spin_lock_bh should be removed since we the run estimation in the kthread, so there should be no softirq preempt - We can use RCU for iteration of the est_list to avoid the spinlock Signed-off-by: Dust Li Acked-by: Tony Lu --- include/net/ip_vs.h | 2 +- net/netfilter/ipvs/ip_vs_est.c | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 33f210c..9bcff1b 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -939,7 +939,7 @@ struct netns_ipvs { /* ip_vs_est */ struct list_head est_list; /* estimator list */ spinlock_t est_lock; - struct timer_list est_timer; /* Estimation timer */ + struct delayed_work est_work; /* Estimation timer */ /* ip_vs_sync */ spinlock_t sync_lock; struct ipvs_master_sync_state *ms; diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 4890550..fb79838 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -14,6 +14,11 @@ * Affected data: est_list and est_lock. * estimation_timer() runs with timer per netns. * get_stats()) do the per cpu summing. + * + * Dust Li + * Run estimation in delayed work rather then timer to + * prevent occuping softirq too long when est_list * CPU + * is large */ #define KMSG_COMPONENT "IPVS" @@ -96,13 +101,14 @@ static void ip_vs_read_cpu_stats(struct ip_vs_kstats *sum, } } - -static void estimation_timer(struct timer_list *t) +static void estimation_work(struct work_struct *work) { struct ip_vs_estimator *e; struct ip_vs_stats *s; u64 rate; - struct netns_ipvs *ipvs = from_timer(ipvs, t, est_timer); + struct delayed_work *dwork = to_delayed_work(work); + struct netns_ipvs *ipvs = container_of(dwork, struct netns_ipvs, + est_work); spin_lock(&ipvs->est_lock); list_for_each_entry(e, &ipvs->est_list, list) { @@ -135,7 +141,7 @@ static void estimation_timer(struct timer_list *t) spin_unlock(&s->lock); } spin_unlock(&ipvs->est_lock); - mod_timer(&ipvs->est_timer, jiffies + 2*HZ); + schedule_delayed_work(&ipvs->est_work, 2 * HZ); } void ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats) @@ -192,12 +198,12 @@ int __net_init ip_vs_estimator_net_init(struct netns_ipvs *ipvs) { INIT_LIST_HEAD(&ipvs->est_list); spin_lock_init(&ipvs->est_lock); - timer_setup(&ipvs->est_timer, estimation_timer, 0); - mod_timer(&ipvs->est_timer, jiffies + 2 * HZ); + INIT_DELAYED_WORK(&ipvs->est_work, estimation_work); + schedule_delayed_work(&ipvs->est_work, 2 * HZ); return 0; } void __net_exit ip_vs_estimator_net_cleanup(struct netns_ipvs *ipvs) { - del_timer_sync(&ipvs->est_timer); + cancel_delayed_work(&ipvs->est_work); } -- 1.8.3.1