18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Floating proportions with flexible aging period 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011, SUSE, Jan Kara <jack@suse.cz> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * The goal of this code is: Given different types of event, measure proportion 88c2ecf20Sopenharmony_ci * of each type of event over time. The proportions are measured with 98c2ecf20Sopenharmony_ci * exponentially decaying history to give smooth transitions. A formula 108c2ecf20Sopenharmony_ci * expressing proportion of event of type 'j' is: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * p_{j} = (\Sum_{i>=0} x_{i,j}/2^{i+1})/(\Sum_{i>=0} x_i/2^{i+1}) 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Where x_{i,j} is j's number of events in i-th last time period and x_i is 158c2ecf20Sopenharmony_ci * total number of events in i-th last time period. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Note that p_{j}'s are normalised, i.e. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * \Sum_{j} p_{j} = 1, 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * This formula can be straightforwardly computed by maintaining denominator 228c2ecf20Sopenharmony_ci * (let's call it 'd') and for each event type its numerator (let's call it 238c2ecf20Sopenharmony_ci * 'n_j'). When an event of type 'j' happens, we simply need to do: 248c2ecf20Sopenharmony_ci * n_j++; d++; 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * When a new period is declared, we could do: 278c2ecf20Sopenharmony_ci * d /= 2 288c2ecf20Sopenharmony_ci * for each j 298c2ecf20Sopenharmony_ci * n_j /= 2 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * To avoid iteration over all event types, we instead shift numerator of event 328c2ecf20Sopenharmony_ci * j lazily when someone asks for a proportion of event j or when event j 338c2ecf20Sopenharmony_ci * occurs. This can bit trivially implemented by remembering last period in 348c2ecf20Sopenharmony_ci * which something happened with proportion of type j. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci#include <linux/flex_proportions.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ciint fprop_global_init(struct fprop_global *p, gfp_t gfp) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci int err; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci p->period = 0; 438c2ecf20Sopenharmony_ci /* Use 1 to avoid dealing with periods with 0 events... */ 448c2ecf20Sopenharmony_ci err = percpu_counter_init(&p->events, 1, gfp); 458c2ecf20Sopenharmony_ci if (err) 468c2ecf20Sopenharmony_ci return err; 478c2ecf20Sopenharmony_ci seqcount_init(&p->sequence); 488c2ecf20Sopenharmony_ci return 0; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_civoid fprop_global_destroy(struct fprop_global *p) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci percpu_counter_destroy(&p->events); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * Declare @periods new periods. It is upto the caller to make sure period 588c2ecf20Sopenharmony_ci * transitions cannot happen in parallel. 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * The function returns true if the proportions are still defined and false 618c2ecf20Sopenharmony_ci * if aging zeroed out all events. This can be used to detect whether declaring 628c2ecf20Sopenharmony_ci * further periods has any effect. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cibool fprop_new_period(struct fprop_global *p, int periods) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci s64 events; 678c2ecf20Sopenharmony_ci unsigned long flags; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci local_irq_save(flags); 708c2ecf20Sopenharmony_ci events = percpu_counter_sum(&p->events); 718c2ecf20Sopenharmony_ci /* 728c2ecf20Sopenharmony_ci * Don't do anything if there are no events. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci if (events <= 1) { 758c2ecf20Sopenharmony_ci local_irq_restore(flags); 768c2ecf20Sopenharmony_ci return false; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci write_seqcount_begin(&p->sequence); 798c2ecf20Sopenharmony_ci if (periods < 64) 808c2ecf20Sopenharmony_ci events -= events >> periods; 818c2ecf20Sopenharmony_ci /* Use addition to avoid losing events happening between sum and set */ 828c2ecf20Sopenharmony_ci percpu_counter_add(&p->events, -events); 838c2ecf20Sopenharmony_ci p->period += periods; 848c2ecf20Sopenharmony_ci write_seqcount_end(&p->sequence); 858c2ecf20Sopenharmony_ci local_irq_restore(flags); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return true; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * ---- SINGLE ---- 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ciint fprop_local_init_single(struct fprop_local_single *pl) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci pl->events = 0; 978c2ecf20Sopenharmony_ci pl->period = 0; 988c2ecf20Sopenharmony_ci raw_spin_lock_init(&pl->lock); 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_civoid fprop_local_destroy_single(struct fprop_local_single *pl) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void fprop_reflect_period_single(struct fprop_global *p, 1078c2ecf20Sopenharmony_ci struct fprop_local_single *pl) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci unsigned int period = p->period; 1108c2ecf20Sopenharmony_ci unsigned long flags; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Fast path - period didn't change */ 1138c2ecf20Sopenharmony_ci if (pl->period == period) 1148c2ecf20Sopenharmony_ci return; 1158c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pl->lock, flags); 1168c2ecf20Sopenharmony_ci /* Someone updated pl->period while we were spinning? */ 1178c2ecf20Sopenharmony_ci if (pl->period >= period) { 1188c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pl->lock, flags); 1198c2ecf20Sopenharmony_ci return; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci /* Aging zeroed our fraction? */ 1228c2ecf20Sopenharmony_ci if (period - pl->period < BITS_PER_LONG) 1238c2ecf20Sopenharmony_ci pl->events >>= period - pl->period; 1248c2ecf20Sopenharmony_ci else 1258c2ecf20Sopenharmony_ci pl->events = 0; 1268c2ecf20Sopenharmony_ci pl->period = period; 1278c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pl->lock, flags); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* Event of type pl happened */ 1318c2ecf20Sopenharmony_civoid __fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci fprop_reflect_period_single(p, pl); 1348c2ecf20Sopenharmony_ci pl->events++; 1358c2ecf20Sopenharmony_ci percpu_counter_add(&p->events, 1); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* Return fraction of events of type pl */ 1398c2ecf20Sopenharmony_civoid fprop_fraction_single(struct fprop_global *p, 1408c2ecf20Sopenharmony_ci struct fprop_local_single *pl, 1418c2ecf20Sopenharmony_ci unsigned long *numerator, unsigned long *denominator) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci unsigned int seq; 1448c2ecf20Sopenharmony_ci s64 num, den; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci do { 1478c2ecf20Sopenharmony_ci seq = read_seqcount_begin(&p->sequence); 1488c2ecf20Sopenharmony_ci fprop_reflect_period_single(p, pl); 1498c2ecf20Sopenharmony_ci num = pl->events; 1508c2ecf20Sopenharmony_ci den = percpu_counter_read_positive(&p->events); 1518c2ecf20Sopenharmony_ci } while (read_seqcount_retry(&p->sequence, seq)); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* 1548c2ecf20Sopenharmony_ci * Make fraction <= 1 and denominator > 0 even in presence of percpu 1558c2ecf20Sopenharmony_ci * counter errors 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci if (den <= num) { 1588c2ecf20Sopenharmony_ci if (num) 1598c2ecf20Sopenharmony_ci den = num; 1608c2ecf20Sopenharmony_ci else 1618c2ecf20Sopenharmony_ci den = 1; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci *denominator = den; 1648c2ecf20Sopenharmony_ci *numerator = num; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* 1688c2ecf20Sopenharmony_ci * ---- PERCPU ---- 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci#define PROP_BATCH (8*(1+ilog2(nr_cpu_ids))) 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciint fprop_local_init_percpu(struct fprop_local_percpu *pl, gfp_t gfp) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci int err; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci err = percpu_counter_init(&pl->events, 0, gfp); 1778c2ecf20Sopenharmony_ci if (err) 1788c2ecf20Sopenharmony_ci return err; 1798c2ecf20Sopenharmony_ci pl->period = 0; 1808c2ecf20Sopenharmony_ci raw_spin_lock_init(&pl->lock); 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_civoid fprop_local_destroy_percpu(struct fprop_local_percpu *pl) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci percpu_counter_destroy(&pl->events); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic void fprop_reflect_period_percpu(struct fprop_global *p, 1908c2ecf20Sopenharmony_ci struct fprop_local_percpu *pl) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci unsigned int period = p->period; 1938c2ecf20Sopenharmony_ci unsigned long flags; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Fast path - period didn't change */ 1968c2ecf20Sopenharmony_ci if (pl->period == period) 1978c2ecf20Sopenharmony_ci return; 1988c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pl->lock, flags); 1998c2ecf20Sopenharmony_ci /* Someone updated pl->period while we were spinning? */ 2008c2ecf20Sopenharmony_ci if (pl->period >= period) { 2018c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pl->lock, flags); 2028c2ecf20Sopenharmony_ci return; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci /* Aging zeroed our fraction? */ 2058c2ecf20Sopenharmony_ci if (period - pl->period < BITS_PER_LONG) { 2068c2ecf20Sopenharmony_ci s64 val = percpu_counter_read(&pl->events); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (val < (nr_cpu_ids * PROP_BATCH)) 2098c2ecf20Sopenharmony_ci val = percpu_counter_sum(&pl->events); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci percpu_counter_add_batch(&pl->events, 2128c2ecf20Sopenharmony_ci -val + (val >> (period-pl->period)), PROP_BATCH); 2138c2ecf20Sopenharmony_ci } else 2148c2ecf20Sopenharmony_ci percpu_counter_set(&pl->events, 0); 2158c2ecf20Sopenharmony_ci pl->period = period; 2168c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pl->lock, flags); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* Event of type pl happened */ 2208c2ecf20Sopenharmony_civoid __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci fprop_reflect_period_percpu(p, pl); 2238c2ecf20Sopenharmony_ci percpu_counter_add_batch(&pl->events, 1, PROP_BATCH); 2248c2ecf20Sopenharmony_ci percpu_counter_add(&p->events, 1); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_civoid fprop_fraction_percpu(struct fprop_global *p, 2288c2ecf20Sopenharmony_ci struct fprop_local_percpu *pl, 2298c2ecf20Sopenharmony_ci unsigned long *numerator, unsigned long *denominator) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci unsigned int seq; 2328c2ecf20Sopenharmony_ci s64 num, den; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci do { 2358c2ecf20Sopenharmony_ci seq = read_seqcount_begin(&p->sequence); 2368c2ecf20Sopenharmony_ci fprop_reflect_period_percpu(p, pl); 2378c2ecf20Sopenharmony_ci num = percpu_counter_read_positive(&pl->events); 2388c2ecf20Sopenharmony_ci den = percpu_counter_read_positive(&p->events); 2398c2ecf20Sopenharmony_ci } while (read_seqcount_retry(&p->sequence, seq)); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* 2428c2ecf20Sopenharmony_ci * Make fraction <= 1 and denominator > 0 even in presence of percpu 2438c2ecf20Sopenharmony_ci * counter errors 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci if (den <= num) { 2468c2ecf20Sopenharmony_ci if (num) 2478c2ecf20Sopenharmony_ci den = num; 2488c2ecf20Sopenharmony_ci else 2498c2ecf20Sopenharmony_ci den = 1; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci *denominator = den; 2528c2ecf20Sopenharmony_ci *numerator = num; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* 2568c2ecf20Sopenharmony_ci * Like __fprop_inc_percpu() except that event is counted only if the given 2578c2ecf20Sopenharmony_ci * type has fraction smaller than @max_frac/FPROP_FRAC_BASE 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_civoid __fprop_inc_percpu_max(struct fprop_global *p, 2608c2ecf20Sopenharmony_ci struct fprop_local_percpu *pl, int max_frac) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci if (unlikely(max_frac < FPROP_FRAC_BASE)) { 2638c2ecf20Sopenharmony_ci unsigned long numerator, denominator; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci fprop_fraction_percpu(p, pl, &numerator, &denominator); 2668c2ecf20Sopenharmony_ci if (numerator > 2678c2ecf20Sopenharmony_ci (((u64)denominator) * max_frac) >> FPROP_FRAC_SHIFT) 2688c2ecf20Sopenharmony_ci return; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci __fprop_inc_percpu(p, pl); 2728c2ecf20Sopenharmony_ci} 273