From 27c982b75684c6b5182ebd25294f5bbf80baee77 Mon Sep 17 00:00:00 2001 From: Dust Li Date: Tue, 1 Dec 2020 14:30:12 +0800 Subject: [PATCH 2438/2944] net: ipvs: add sysctl_run_estimation to support disable estimation fix #31562403 Although we moved estimation from timer softirq to kworker, it may still running on a single CPU for long time(>200ms) without scheduling. And for some cases, estimation is not needed at all. So we provide an sysctl blob to disable estimation completely. Default is: 1 (enable) Signed-off-by: Dust Li Acked-by: Tony Lu --- Documentation/networking/ipvs-sysctl.txt | 17 ++++++++ include/net/ip_vs.h | 3 ++ net/netfilter/ipvs/ip_vs_est.c | 74 +++++++++++++++++++++++++++++++- 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/Documentation/networking/ipvs-sysctl.txt b/Documentation/networking/ipvs-sysctl.txt index 46d2f9b..ba4f247 100644 --- a/Documentation/networking/ipvs-sysctl.txt +++ b/Documentation/networking/ipvs-sysctl.txt @@ -315,3 +315,20 @@ sync_version - INTEGER Kernels with this sync_version entry are able to receive messages of both version 1 and version 2 of the synchronisation protocol. + +run_estimation - BOOLEAN + 0 - disabled + not 0 - enabled (default) + + If disabled, the estimation will be stop, and you can't see + any update on speed estimation data. + + For example + 'Conns/s Pkts/s Pkts/s Bytes/s Bytes/s' + those data in /proc/net/ip_vs_stats will always be zero. + Note, this only affect the speed estimation, the total data + will still be updated. + + You can always re-enable estimation by setting this value to 1. + But be carefull, the first estimation after re-enable is not + accurate. diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 9bcff1b..3431331 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -937,6 +937,9 @@ struct netns_ipvs { struct ctl_table_header *lblcr_ctl_header; struct ctl_table *lblcr_ctl_table; /* ip_vs_est */ + int sysctl_run_estimation; + struct ctl_table_header *est_ctl_header; + struct ctl_table *est_ctl_table; struct list_head est_list; /* estimator list */ spinlock_t est_lock; struct delayed_work est_work; /* Estimation timer */ diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index fb79838..7f2362b 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -58,7 +58,6 @@ * A lot of code is taken from net/core/gen_estimator.c */ - /* * Make a summary from each cpu */ @@ -110,6 +109,9 @@ static void estimation_work(struct work_struct *work) struct netns_ipvs *ipvs = container_of(dwork, struct netns_ipvs, est_work); + if (!ipvs->sysctl_run_estimation) + goto reschedule; + spin_lock(&ipvs->est_lock); list_for_each_entry(e, &ipvs->est_list, list) { s = container_of(e, struct ip_vs_stats, est); @@ -141,6 +143,8 @@ static void estimation_work(struct work_struct *work) spin_unlock(&s->lock); } spin_unlock(&ipvs->est_lock); + +reschedule: schedule_delayed_work(&ipvs->est_work, 2 * HZ); } @@ -194,11 +198,78 @@ void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats) dst->outbps = (e->outbps + 0xF) >> 5; } +#ifdef CONFIG_SYSCTL +/* IPVS ESTIMATION sysctl table */ +static struct ctl_table vs_vars_table[] = { + { + .procname = "run_estimation", + .data = NULL, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { } +}; + +static int ip_vs_est_sysctl_init(struct netns_ipvs *ipvs) +{ + struct net *net = ipvs->net; + + if (!net_eq(net, &init_net)) { + ipvs->est_ctl_table = kmemdup(vs_vars_table, + sizeof(vs_vars_table), + GFP_KERNEL); + if (!ipvs->est_ctl_table) + return -ENOMEM; + + /* Don't export sysctls to unprivileged users */ + if (net->user_ns != &init_user_ns) + ipvs->est_ctl_table[0].procname = NULL; + + } else { + ipvs->est_ctl_table = vs_vars_table; + } + + ipvs->sysctl_run_estimation = 1; + ipvs->est_ctl_table[0].data = &ipvs->sysctl_run_estimation; + + ipvs->est_ctl_header = + register_net_sysctl(net, "net/ipv4/vs", ipvs->est_ctl_table); + if (!ipvs->est_ctl_header) { + if (!net_eq(net, &init_net)) + kfree(ipvs->est_ctl_table); + return -ENOMEM; + } + + return 0; +} + +static void ip_vs_est_sysctl_cleanup(struct netns_ipvs *ipvs) +{ + unregister_net_sysctl_table(ipvs->est_ctl_header); + + if (!net_eq(ipvs->net, &init_net)) + kfree(ipvs->est_ctl_table); +} + +#else + +static int ip_vs_est_sysctl_init(struct netns_ipvs *ipvs) +{ + ipvs->sysctl_run_estimation = 1; + return 0; +} + +static void ip_vs_est_sysctl_cleanup(struct netns_ipvs *ipvs) { } + +#endif + int __net_init ip_vs_estimator_net_init(struct netns_ipvs *ipvs) { INIT_LIST_HEAD(&ipvs->est_list); spin_lock_init(&ipvs->est_lock); INIT_DELAYED_WORK(&ipvs->est_work, estimation_work); + ip_vs_est_sysctl_init(ipvs); schedule_delayed_work(&ipvs->est_work, 2 * HZ); return 0; } @@ -206,4 +277,5 @@ int __net_init ip_vs_estimator_net_init(struct netns_ipvs *ipvs) void __net_exit ip_vs_estimator_net_cleanup(struct netns_ipvs *ipvs) { cancel_delayed_work(&ipvs->est_work); + ip_vs_est_sysctl_cleanup(ipvs); } -- 1.8.3.1