diff options
-rw-r--r-- | include/linux/flex_proportions.h | 101 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/flex_proportions.c | 266 |
3 files changed, 368 insertions, 1 deletions
diff --git a/include/linux/flex_proportions.h b/include/linux/flex_proportions.h new file mode 100644 index 000000000000..4ebc49fae391 --- /dev/null +++ b/include/linux/flex_proportions.h | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * Floating proportions with flexible aging period | ||
3 | * | ||
4 | * Copyright (C) 2011, SUSE, Jan Kara <jack@suse.cz> | ||
5 | */ | ||
6 | |||
7 | #ifndef _LINUX_FLEX_PROPORTIONS_H | ||
8 | #define _LINUX_FLEX_PROPORTIONS_H | ||
9 | |||
10 | #include <linux/percpu_counter.h> | ||
11 | #include <linux/spinlock.h> | ||
12 | #include <linux/seqlock.h> | ||
13 | |||
14 | /* | ||
15 | * When maximum proportion of some event type is specified, this is the | ||
16 | * precision with which we allow limitting. Note that this creates an upper | ||
17 | * bound on the number of events per period like | ||
18 | * ULLONG_MAX >> FPROP_FRAC_SHIFT. | ||
19 | */ | ||
20 | #define FPROP_FRAC_SHIFT 10 | ||
21 | #define FPROP_FRAC_BASE (1UL << FPROP_FRAC_SHIFT) | ||
22 | |||
23 | /* | ||
24 | * ---- Global proportion definitions ---- | ||
25 | */ | ||
26 | struct fprop_global { | ||
27 | /* Number of events in the current period */ | ||
28 | struct percpu_counter events; | ||
29 | /* Current period */ | ||
30 | unsigned int period; | ||
31 | /* Synchronization with period transitions */ | ||
32 | seqcount_t sequence; | ||
33 | }; | ||
34 | |||
35 | int fprop_global_init(struct fprop_global *p); | ||
36 | void fprop_global_destroy(struct fprop_global *p); | ||
37 | bool fprop_new_period(struct fprop_global *p, int periods); | ||
38 | |||
39 | /* | ||
40 | * ---- SINGLE ---- | ||
41 | */ | ||
42 | struct fprop_local_single { | ||
43 | /* the local events counter */ | ||
44 | unsigned long events; | ||
45 | /* Period in which we last updated events */ | ||
46 | unsigned int period; | ||
47 | raw_spinlock_t lock; /* Protect period and numerator */ | ||
48 | }; | ||
49 | |||
50 | #define INIT_FPROP_LOCAL_SINGLE(name) \ | ||
51 | { .lock = __RAW_SPIN_LOCK_UNLOCKED(name.lock), \ | ||
52 | } | ||
53 | |||
54 | int fprop_local_init_single(struct fprop_local_single *pl); | ||
55 | void fprop_local_destroy_single(struct fprop_local_single *pl); | ||
56 | void __fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl); | ||
57 | void fprop_fraction_single(struct fprop_global *p, | ||
58 | struct fprop_local_single *pl, unsigned long *numerator, | ||
59 | unsigned long *denominator); | ||
60 | |||
61 | static inline | ||
62 | void fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl) | ||
63 | { | ||
64 | unsigned long flags; | ||
65 | |||
66 | local_irq_save(flags); | ||
67 | __fprop_inc_single(p, pl); | ||
68 | local_irq_restore(flags); | ||
69 | } | ||
70 | |||
71 | /* | ||
72 | * ---- PERCPU ---- | ||
73 | */ | ||
74 | struct fprop_local_percpu { | ||
75 | /* the local events counter */ | ||
76 | struct percpu_counter events; | ||
77 | /* Period in which we last updated events */ | ||
78 | unsigned int period; | ||
79 | raw_spinlock_t lock; /* Protect period and numerator */ | ||
80 | }; | ||
81 | |||
82 | int fprop_local_init_percpu(struct fprop_local_percpu *pl); | ||
83 | void fprop_local_destroy_percpu(struct fprop_local_percpu *pl); | ||
84 | void __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl); | ||
85 | void __fprop_inc_percpu_max(struct fprop_global *p, struct fprop_local_percpu *pl, | ||
86 | int max_frac); | ||
87 | void fprop_fraction_percpu(struct fprop_global *p, | ||
88 | struct fprop_local_percpu *pl, unsigned long *numerator, | ||
89 | unsigned long *denominator); | ||
90 | |||
91 | static inline | ||
92 | void fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl) | ||
93 | { | ||
94 | unsigned long flags; | ||
95 | |||
96 | local_irq_save(flags); | ||
97 | __fprop_inc_percpu(p, pl); | ||
98 | local_irq_restore(flags); | ||
99 | } | ||
100 | |||
101 | #endif | ||
diff --git a/lib/Makefile b/lib/Makefile index 8c31a0cb75e9..ee837378b107 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
@@ -11,7 +11,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ | |||
11 | rbtree.o radix-tree.o dump_stack.o timerqueue.o\ | 11 | rbtree.o radix-tree.o dump_stack.o timerqueue.o\ |
12 | idr.o int_sqrt.o extable.o prio_tree.o \ | 12 | idr.o int_sqrt.o extable.o prio_tree.o \ |
13 | sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \ | 13 | sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \ |
14 | proportions.o prio_heap.o ratelimit.o show_mem.o \ | 14 | proportions.o flex_proportions.o prio_heap.o ratelimit.o show_mem.o \ |
15 | is_single_threaded.o plist.o decompress.o | 15 | is_single_threaded.o plist.o decompress.o |
16 | 16 | ||
17 | lib-$(CONFIG_MMU) += ioremap.o | 17 | lib-$(CONFIG_MMU) += ioremap.o |
diff --git a/lib/flex_proportions.c b/lib/flex_proportions.c new file mode 100644 index 000000000000..e02a3883ae01 --- /dev/null +++ b/lib/flex_proportions.c | |||
@@ -0,0 +1,266 @@ | |||
1 | /* | ||
2 | * Floating proportions with flexible aging period | ||
3 | * | ||
4 | * Copyright (C) 2011, SUSE, Jan Kara <jack@suse.cz> | ||
5 | * | ||
6 | * The goal of this code is: Given different types of event, measure proportion | ||
7 | * of each type of event over time. The proportions are measured with | ||
8 | * exponentially decaying history to give smooth transitions. A formula | ||
9 | * expressing proportion of event of type 'j' is: | ||
10 | * | ||
11 | * p_{j} = (\Sum_{i>=0} x_{i,j}/2^{i+1})/(\Sum_{i>=0} x_i/2^{i+1}) | ||
12 | * | ||
13 | * Where x_{i,j} is j's number of events in i-th last time period and x_i is | ||
14 | * total number of events in i-th last time period. | ||
15 | * | ||
16 | * Note that p_{j}'s are normalised, i.e. | ||
17 | * | ||
18 | * \Sum_{j} p_{j} = 1, | ||
19 | * | ||
20 | * This formula can be straightforwardly computed by maintaing denominator | ||
21 | * (let's call it 'd') and for each event type its numerator (let's call it | ||
22 | * 'n_j'). When an event of type 'j' happens, we simply need to do: | ||
23 | * n_j++; d++; | ||
24 | * | ||
25 | * When a new period is declared, we could do: | ||
26 | * d /= 2 | ||
27 | * for each j | ||
28 | * n_j /= 2 | ||
29 | * | ||
30 | * To avoid iteration over all event types, we instead shift numerator of event | ||
31 | * j lazily when someone asks for a proportion of event j or when event j | ||
32 | * occurs. This can bit trivially implemented by remembering last period in | ||
33 | * which something happened with proportion of type j. | ||
34 | */ | ||
35 | #include <linux/flex_proportions.h> | ||
36 | |||
37 | int fprop_global_init(struct fprop_global *p) | ||
38 | { | ||
39 | int err; | ||
40 | |||
41 | p->period = 0; | ||
42 | /* Use 1 to avoid dealing with periods with 0 events... */ | ||
43 | err = percpu_counter_init(&p->events, 1); | ||
44 | if (err) | ||
45 | return err; | ||
46 | seqcount_init(&p->sequence); | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | void fprop_global_destroy(struct fprop_global *p) | ||
51 | { | ||
52 | percpu_counter_destroy(&p->events); | ||
53 | } | ||
54 | |||
55 | /* | ||
56 | * Declare @periods new periods. It is upto the caller to make sure period | ||
57 | * transitions cannot happen in parallel. | ||
58 | * | ||
59 | * The function returns true if the proportions are still defined and false | ||
60 | * if aging zeroed out all events. This can be used to detect whether declaring | ||
61 | * further periods has any effect. | ||
62 | */ | ||
63 | bool fprop_new_period(struct fprop_global *p, int periods) | ||
64 | { | ||
65 | u64 events = percpu_counter_sum(&p->events); | ||
66 | |||
67 | /* | ||
68 | * Don't do anything if there are no events. | ||
69 | */ | ||
70 | if (events <= 1) | ||
71 | return false; | ||
72 | write_seqcount_begin(&p->sequence); | ||
73 | if (periods < 64) | ||
74 | events -= events >> periods; | ||
75 | /* Use addition to avoid losing events happening between sum and set */ | ||
76 | percpu_counter_add(&p->events, -events); | ||
77 | p->period += periods; | ||
78 | write_seqcount_end(&p->sequence); | ||
79 | |||
80 | return true; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * ---- SINGLE ---- | ||
85 | */ | ||
86 | |||
87 | int fprop_local_init_single(struct fprop_local_single *pl) | ||
88 | { | ||
89 | pl->events = 0; | ||
90 | pl->period = 0; | ||
91 | raw_spin_lock_init(&pl->lock); | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | void fprop_local_destroy_single(struct fprop_local_single *pl) | ||
96 | { | ||
97 | } | ||
98 | |||
99 | static void fprop_reflect_period_single(struct fprop_global *p, | ||
100 | struct fprop_local_single *pl) | ||
101 | { | ||
102 | unsigned int period = p->period; | ||
103 | unsigned long flags; | ||
104 | |||
105 | /* Fast path - period didn't change */ | ||
106 | if (pl->period == period) | ||
107 | return; | ||
108 | raw_spin_lock_irqsave(&pl->lock, flags); | ||
109 | /* Someone updated pl->period while we were spinning? */ | ||
110 | if (pl->period >= period) { | ||
111 | raw_spin_unlock_irqrestore(&pl->lock, flags); | ||
112 | return; | ||
113 | } | ||
114 | /* Aging zeroed our fraction? */ | ||
115 | if (period - pl->period < BITS_PER_LONG) | ||
116 | pl->events >>= period - pl->period; | ||
117 | else | ||
118 | pl->events = 0; | ||
119 | pl->period = period; | ||
120 | raw_spin_unlock_irqrestore(&pl->lock, flags); | ||
121 | } | ||
122 | |||
123 | /* Event of type pl happened */ | ||
124 | void __fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl) | ||
125 | { | ||
126 | fprop_reflect_period_single(p, pl); | ||
127 | pl->events++; | ||
128 | percpu_counter_add(&p->events, 1); | ||
129 | } | ||
130 | |||
131 | /* Return fraction of events of type pl */ | ||
132 | void fprop_fraction_single(struct fprop_global *p, | ||
133 | struct fprop_local_single *pl, | ||
134 | unsigned long *numerator, unsigned long *denominator) | ||
135 | { | ||
136 | unsigned int seq; | ||
137 | s64 num, den; | ||
138 | |||
139 | do { | ||
140 | seq = read_seqcount_begin(&p->sequence); | ||
141 | fprop_reflect_period_single(p, pl); | ||
142 | num = pl->events; | ||
143 | den = percpu_counter_read_positive(&p->events); | ||
144 | } while (read_seqcount_retry(&p->sequence, seq)); | ||
145 | |||
146 | /* | ||
147 | * Make fraction <= 1 and denominator > 0 even in presence of percpu | ||
148 | * counter errors | ||
149 | */ | ||
150 | if (den <= num) { | ||
151 | if (num) | ||
152 | den = num; | ||
153 | else | ||
154 | den = 1; | ||
155 | } | ||
156 | *denominator = den; | ||
157 | *numerator = num; | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | * ---- PERCPU ---- | ||
162 | */ | ||
163 | #define PROP_BATCH (8*(1+ilog2(nr_cpu_ids))) | ||
164 | |||
165 | int fprop_local_init_percpu(struct fprop_local_percpu *pl) | ||
166 | { | ||
167 | int err; | ||
168 | |||
169 | err = percpu_counter_init(&pl->events, 0); | ||
170 | if (err) | ||
171 | return err; | ||
172 | pl->period = 0; | ||
173 | raw_spin_lock_init(&pl->lock); | ||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | void fprop_local_destroy_percpu(struct fprop_local_percpu *pl) | ||
178 | { | ||
179 | percpu_counter_destroy(&pl->events); | ||
180 | } | ||
181 | |||
182 | static void fprop_reflect_period_percpu(struct fprop_global *p, | ||
183 | struct fprop_local_percpu *pl) | ||
184 | { | ||
185 | unsigned int period = p->period; | ||
186 | unsigned long flags; | ||
187 | |||
188 | /* Fast path - period didn't change */ | ||
189 | if (pl->period == period) | ||
190 | return; | ||
191 | raw_spin_lock_irqsave(&pl->lock, flags); | ||
192 | /* Someone updated pl->period while we were spinning? */ | ||
193 | if (pl->period >= period) { | ||
194 | raw_spin_unlock_irqrestore(&pl->lock, flags); | ||
195 | return; | ||
196 | } | ||
197 | /* Aging zeroed our fraction? */ | ||
198 | if (period - pl->period < BITS_PER_LONG) { | ||
199 | s64 val = percpu_counter_read(&pl->events); | ||
200 | |||
201 | if (val < (nr_cpu_ids * PROP_BATCH)) | ||
202 | val = percpu_counter_sum(&pl->events); | ||
203 | |||
204 | __percpu_counter_add(&pl->events, | ||
205 | -val + (val >> (period-pl->period)), PROP_BATCH); | ||
206 | } else | ||
207 | percpu_counter_set(&pl->events, 0); | ||
208 | pl->period = period; | ||
209 | raw_spin_unlock_irqrestore(&pl->lock, flags); | ||
210 | } | ||
211 | |||
212 | /* Event of type pl happened */ | ||
213 | void __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl) | ||
214 | { | ||
215 | fprop_reflect_period_percpu(p, pl); | ||
216 | __percpu_counter_add(&pl->events, 1, PROP_BATCH); | ||
217 | percpu_counter_add(&p->events, 1); | ||
218 | } | ||
219 | |||
220 | void fprop_fraction_percpu(struct fprop_global *p, | ||
221 | struct fprop_local_percpu *pl, | ||
222 | unsigned long *numerator, unsigned long *denominator) | ||
223 | { | ||
224 | unsigned int seq; | ||
225 | s64 num, den; | ||
226 | |||
227 | do { | ||
228 | seq = read_seqcount_begin(&p->sequence); | ||
229 | fprop_reflect_period_percpu(p, pl); | ||
230 | num = percpu_counter_read_positive(&pl->events); | ||
231 | den = percpu_counter_read_positive(&p->events); | ||
232 | } while (read_seqcount_retry(&p->sequence, seq)); | ||
233 | |||
234 | /* | ||
235 | * Make fraction <= 1 and denominator > 0 even in presence of percpu | ||
236 | * counter errors | ||
237 | */ | ||
238 | if (den <= num) { | ||
239 | if (num) | ||
240 | den = num; | ||
241 | else | ||
242 | den = 1; | ||
243 | } | ||
244 | *denominator = den; | ||
245 | *numerator = num; | ||
246 | } | ||
247 | |||
248 | /* | ||
249 | * Like __fprop_inc_percpu() except that event is counted only if the given | ||
250 | * type has fraction smaller than @max_frac/FPROP_FRAC_BASE | ||
251 | */ | ||
252 | void __fprop_inc_percpu_max(struct fprop_global *p, | ||
253 | struct fprop_local_percpu *pl, int max_frac) | ||
254 | { | ||
255 | if (unlikely(max_frac < FPROP_FRAC_BASE)) { | ||
256 | unsigned long numerator, denominator; | ||
257 | |||
258 | fprop_fraction_percpu(p, pl, &numerator, &denominator); | ||
259 | if (numerator > | ||
260 | (((u64)denominator) * max_frac) >> FPROP_FRAC_SHIFT) | ||
261 | return; | ||
262 | } else | ||
263 | fprop_reflect_period_percpu(p, pl); | ||
264 | __percpu_counter_add(&pl->events, 1, PROP_BATCH); | ||
265 | percpu_counter_add(&p->events, 1); | ||
266 | } | ||