On Mon, Oct 31, 2022 at 04:56:44PM +0200, Julian Anastasov wrote:
> Estimating all entries in single list in timer context
> causes large latency with multiple rules.
>
> Spread the estimator structures in multiple chains and
> use kthread(s) for the estimation. Every chain is
> processed under RCU lock. First kthread determines
> parameters to use, eg. maximum number of estimators to
> process per kthread based on chain's length, allowing
> sub-100us cond_resched rate and estimation taking 1/8
> of the CPU.
>
> First kthread also plays the role of distributor of
> added estimators to all kthreads.
>
> We also add delayed work est_reload_work that will
> make sure the kthread tasks are properly started/stopped.
>
> ip_vs_start_estimator() is changed to report errors
> which allows to safely store the estimators in
> allocated structures.
>
> Signed-off-by: Julian Anastasov <ja@xxxxxx>
> ---
Tested-by: Jiri Wiesner <jwiesner@xxxxxxx>
Reviewed-by: Jiri Wiesner <jwiesner@xxxxxxx>
> @@ -953,9 +1005,17 @@ struct netns_ipvs {
> struct ctl_table_header *lblcr_ctl_header;
> struct ctl_table *lblcr_ctl_table;
> /* 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_reload_work;/* Reload kthread tasks */
> + struct mutex est_mutex; /* protect kthread tasks */
> + struct hlist_head est_temp_list; /* Ests during calc phase */
> + struct ip_vs_est_kt_data **est_kt_arr; /* Array of kthread data ptrs */
> + unsigned long est_max_threads;/* rlimit */
Not an rlimit anymore.
> + int est_calc_phase; /* Calculation phase */
> + int est_chain_max; /* Calculated chain_max */
> + int est_kt_count; /* Allocated ptrs */
> + int est_add_ktid; /* ktid where to add ests */
> + atomic_t est_genid; /* kthreads reload genid */
> + atomic_t est_genid_done; /* applied genid */
> /* ip_vs_sync */
> spinlock_t sync_lock;
> struct ipvs_master_sync_state *ms;
> - INIT_LIST_HEAD(&est->list);
> +/* Start estimation for stats */
> +int ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats)
> +{
> + struct ip_vs_estimator *est = &stats->est;
> + int ret;
> +
> + if (!ipvs->est_max_threads && ipvs->enable)
> + ipvs->est_max_threads = 4 * num_possible_cpus();
To avoid the magic number - 4, a symbolic constant could be used. The 4 is
related to the design decision that a fully loaded kthread should take 1/8 of
the CPU time of a CPU.
> +
> + est->ktid = -1;
> + est->ktrow = IPVS_EST_NTICKS - 1; /* Initial delay */
> +
> + /* We prefer this code to be short, kthread 0 will requeue the
> + * estimator to available chain. If tasks are disabled, we
> + * will not allocate much memory, just for kt 0.
> + */
> + ret = 0;
> + if (!ipvs->est_kt_count || !ipvs->est_kt_arr[0])
> + ret = ip_vs_est_add_kthread(ipvs);
> + if (ret >= 0)
> + hlist_add_head(&est->list, &ipvs->est_temp_list);
> + else
> + INIT_HLIST_NODE(&est->list);
> + return ret;
> +}
--
Jiri Wiesner
SUSE Labs
|