flex_proportions.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Floating proportions with flexible aging period
  4. *
  5. * Copyright (C) 2011, SUSE, Jan Kara <jack@suse.cz>
  6. *
  7. * The goal of this code is: Given different types of event, measure proportion
  8. * of each type of event over time. The proportions are measured with
  9. * exponentially decaying history to give smooth transitions. A formula
  10. * expressing proportion of event of type 'j' is:
  11. *
  12. * p_{j} = (\Sum_{i>=0} x_{i,j}/2^{i+1})/(\Sum_{i>=0} x_i/2^{i+1})
  13. *
  14. * Where x_{i,j} is j's number of events in i-th last time period and x_i is
  15. * total number of events in i-th last time period.
  16. *
  17. * Note that p_{j}'s are normalised, i.e.
  18. *
  19. * \Sum_{j} p_{j} = 1,
  20. *
  21. * This formula can be straightforwardly computed by maintaining denominator
  22. * (let's call it 'd') and for each event type its numerator (let's call it
  23. * 'n_j'). When an event of type 'j' happens, we simply need to do:
  24. * n_j++; d++;
  25. *
  26. * When a new period is declared, we could do:
  27. * d /= 2
  28. * for each j
  29. * n_j /= 2
  30. *
  31. * To avoid iteration over all event types, we instead shift numerator of event
  32. * j lazily when someone asks for a proportion of event j or when event j
  33. * occurs. This can bit trivially implemented by remembering last period in
  34. * which something happened with proportion of type j.
  35. */
  36. #include <linux/flex_proportions.h>
  37. int fprop_global_init(struct fprop_global *p, gfp_t gfp)
  38. {
  39. int err;
  40. p->period = 0;
  41. /* Use 1 to avoid dealing with periods with 0 events... */
  42. err = percpu_counter_init(&p->events, 1, gfp);
  43. if (err)
  44. return err;
  45. seqcount_init(&p->sequence);
  46. return 0;
  47. }
  48. void fprop_global_destroy(struct fprop_global *p)
  49. {
  50. percpu_counter_destroy(&p->events);
  51. }
  52. /*
  53. * Declare @periods new periods. It is upto the caller to make sure period
  54. * transitions cannot happen in parallel.
  55. *
  56. * The function returns true if the proportions are still defined and false
  57. * if aging zeroed out all events. This can be used to detect whether declaring
  58. * further periods has any effect.
  59. */
  60. bool fprop_new_period(struct fprop_global *p, int periods)
  61. {
  62. s64 events = percpu_counter_sum(&p->events);
  63. unsigned long flags;
  64. /*
  65. * Don't do anything if there are no events.
  66. */
  67. if (events <= 1)
  68. return false;
  69. local_irq_save(flags);
  70. write_seqcount_begin(&p->sequence);
  71. if (periods < 64)
  72. events -= events >> periods;
  73. /* Use addition to avoid losing events happening between sum and set */
  74. percpu_counter_add(&p->events, -events);
  75. p->period += periods;
  76. write_seqcount_end(&p->sequence);
  77. local_irq_restore(flags);
  78. return true;
  79. }
  80. /*
  81. * ---- PERCPU ----
  82. */
  83. #define PROP_BATCH (8*(1+ilog2(nr_cpu_ids)))
  84. int fprop_local_init_percpu(struct fprop_local_percpu *pl, gfp_t gfp)
  85. {
  86. int err;
  87. err = percpu_counter_init(&pl->events, 0, gfp);
  88. if (err)
  89. return err;
  90. pl->period = 0;
  91. raw_spin_lock_init(&pl->lock);
  92. return 0;
  93. }
  94. void fprop_local_destroy_percpu(struct fprop_local_percpu *pl)
  95. {
  96. percpu_counter_destroy(&pl->events);
  97. }
  98. static void fprop_reflect_period_percpu(struct fprop_global *p,
  99. struct fprop_local_percpu *pl)
  100. {
  101. unsigned int period = p->period;
  102. unsigned long flags;
  103. /* Fast path - period didn't change */
  104. if (pl->period == period)
  105. return;
  106. raw_spin_lock_irqsave(&pl->lock, flags);
  107. /* Someone updated pl->period while we were spinning? */
  108. if (pl->period >= period) {
  109. raw_spin_unlock_irqrestore(&pl->lock, flags);
  110. return;
  111. }
  112. /* Aging zeroed our fraction? */
  113. if (period - pl->period < BITS_PER_LONG) {
  114. s64 val = percpu_counter_read(&pl->events);
  115. if (val < (nr_cpu_ids * PROP_BATCH))
  116. val = percpu_counter_sum(&pl->events);
  117. percpu_counter_add_batch(&pl->events,
  118. -val + (val >> (period-pl->period)), PROP_BATCH);
  119. } else
  120. percpu_counter_set(&pl->events, 0);
  121. pl->period = period;
  122. raw_spin_unlock_irqrestore(&pl->lock, flags);
  123. }
  124. /* Event of type pl happened */
  125. void __fprop_add_percpu(struct fprop_global *p, struct fprop_local_percpu *pl,
  126. long nr)
  127. {
  128. fprop_reflect_period_percpu(p, pl);
  129. percpu_counter_add_batch(&pl->events, nr, PROP_BATCH);
  130. percpu_counter_add(&p->events, nr);
  131. }
  132. void fprop_fraction_percpu(struct fprop_global *p,
  133. struct fprop_local_percpu *pl,
  134. unsigned long *numerator, unsigned long *denominator)
  135. {
  136. unsigned int seq;
  137. s64 num, den;
  138. do {
  139. seq = read_seqcount_begin(&p->sequence);
  140. fprop_reflect_period_percpu(p, pl);
  141. num = percpu_counter_read_positive(&pl->events);
  142. den = percpu_counter_read_positive(&p->events);
  143. } while (read_seqcount_retry(&p->sequence, seq));
  144. /*
  145. * Make fraction <= 1 and denominator > 0 even in presence of percpu
  146. * counter errors
  147. */
  148. if (den <= num) {
  149. if (num)
  150. den = num;
  151. else
  152. den = 1;
  153. }
  154. *denominator = den;
  155. *numerator = num;
  156. }
  157. /*
  158. * Like __fprop_add_percpu() except that event is counted only if the given
  159. * type has fraction smaller than @max_frac/FPROP_FRAC_BASE
  160. */
  161. void __fprop_add_percpu_max(struct fprop_global *p,
  162. struct fprop_local_percpu *pl, int max_frac, long nr)
  163. {
  164. if (unlikely(max_frac < FPROP_FRAC_BASE)) {
  165. unsigned long numerator, denominator;
  166. s64 tmp;
  167. fprop_fraction_percpu(p, pl, &numerator, &denominator);
  168. /* Adding 'nr' to fraction exceeds max_frac/FPROP_FRAC_BASE? */
  169. tmp = (u64)denominator * max_frac -
  170. ((u64)numerator << FPROP_FRAC_SHIFT);
  171. if (tmp < 0) {
  172. /* Maximum fraction already exceeded? */
  173. return;
  174. } else if (tmp < nr * (FPROP_FRAC_BASE - max_frac)) {
  175. /* Add just enough for the fraction to saturate */
  176. nr = div_u64(tmp + FPROP_FRAC_BASE - max_frac - 1,
  177. FPROP_FRAC_BASE - max_frac);
  178. }
  179. }
  180. __fprop_add_percpu(p, pl, nr);
  181. }