Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-or-later
2 : /*
3 : * net/sched/gen_estimator.c Simple rate estimator.
4 : *
5 : * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
6 : * Eric Dumazet <edumazet@google.com>
7 : *
8 : * Changes:
9 : * Jamal Hadi Salim - moved it to net/core and reshulfed
10 : * names to make it usable in general net subsystem.
11 : */
12 :
13 : #include <linux/uaccess.h>
14 : #include <linux/bitops.h>
15 : #include <linux/module.h>
16 : #include <linux/types.h>
17 : #include <linux/kernel.h>
18 : #include <linux/jiffies.h>
19 : #include <linux/string.h>
20 : #include <linux/mm.h>
21 : #include <linux/socket.h>
22 : #include <linux/sockios.h>
23 : #include <linux/in.h>
24 : #include <linux/errno.h>
25 : #include <linux/interrupt.h>
26 : #include <linux/netdevice.h>
27 : #include <linux/skbuff.h>
28 : #include <linux/rtnetlink.h>
29 : #include <linux/init.h>
30 : #include <linux/slab.h>
31 : #include <linux/seqlock.h>
32 : #include <net/sock.h>
33 : #include <net/gen_stats.h>
34 :
35 : /* This code is NOT intended to be used for statistics collection,
36 : * its purpose is to provide a base for statistical multiplexing
37 : * for controlled load service.
38 : * If you need only statistics, run a user level daemon which
39 : * periodically reads byte counters.
40 : */
41 :
42 : struct net_rate_estimator {
43 : struct gnet_stats_basic_packed *bstats;
44 : spinlock_t *stats_lock;
45 : seqcount_t *running;
46 : struct gnet_stats_basic_cpu __percpu *cpu_bstats;
47 : u8 ewma_log;
48 : u8 intvl_log; /* period : (250ms << intvl_log) */
49 :
50 : seqcount_t seq;
51 : u64 last_packets;
52 : u64 last_bytes;
53 :
54 : u64 avpps;
55 : u64 avbps;
56 :
57 : unsigned long next_jiffies;
58 : struct timer_list timer;
59 : struct rcu_head rcu;
60 : };
61 :
62 0 : static void est_fetch_counters(struct net_rate_estimator *e,
63 : struct gnet_stats_basic_packed *b)
64 : {
65 0 : memset(b, 0, sizeof(*b));
66 0 : if (e->stats_lock)
67 0 : spin_lock(e->stats_lock);
68 :
69 0 : __gnet_stats_copy_basic(e->running, b, e->cpu_bstats, e->bstats);
70 :
71 0 : if (e->stats_lock)
72 0 : spin_unlock(e->stats_lock);
73 :
74 0 : }
75 :
76 0 : static void est_timer(struct timer_list *t)
77 : {
78 0 : struct net_rate_estimator *est = from_timer(est, t, timer);
79 0 : struct gnet_stats_basic_packed b;
80 0 : u64 rate, brate;
81 :
82 0 : est_fetch_counters(est, &b);
83 0 : brate = (b.bytes - est->last_bytes) << (10 - est->intvl_log);
84 0 : brate = (brate >> est->ewma_log) - (est->avbps >> est->ewma_log);
85 :
86 0 : rate = (b.packets - est->last_packets) << (10 - est->intvl_log);
87 0 : rate = (rate >> est->ewma_log) - (est->avpps >> est->ewma_log);
88 :
89 0 : write_seqcount_begin(&est->seq);
90 0 : est->avbps += brate;
91 0 : est->avpps += rate;
92 0 : write_seqcount_end(&est->seq);
93 :
94 0 : est->last_bytes = b.bytes;
95 0 : est->last_packets = b.packets;
96 :
97 0 : est->next_jiffies += ((HZ/4) << est->intvl_log);
98 :
99 0 : if (unlikely(time_after_eq(jiffies, est->next_jiffies))) {
100 : /* Ouch... timer was delayed. */
101 0 : est->next_jiffies = jiffies + 1;
102 : }
103 0 : mod_timer(&est->timer, est->next_jiffies);
104 0 : }
105 :
106 : /**
107 : * gen_new_estimator - create a new rate estimator
108 : * @bstats: basic statistics
109 : * @cpu_bstats: bstats per cpu
110 : * @rate_est: rate estimator statistics
111 : * @lock: lock for statistics and control path
112 : * @running: qdisc running seqcount
113 : * @opt: rate estimator configuration TLV
114 : *
115 : * Creates a new rate estimator with &bstats as source and &rate_est
116 : * as destination. A new timer with the interval specified in the
117 : * configuration TLV is created. Upon each interval, the latest statistics
118 : * will be read from &bstats and the estimated rate will be stored in
119 : * &rate_est with the statistics lock grabbed during this period.
120 : *
121 : * Returns 0 on success or a negative error code.
122 : *
123 : */
124 0 : int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
125 : struct gnet_stats_basic_cpu __percpu *cpu_bstats,
126 : struct net_rate_estimator __rcu **rate_est,
127 : spinlock_t *lock,
128 : seqcount_t *running,
129 : struct nlattr *opt)
130 : {
131 0 : struct gnet_estimator *parm = nla_data(opt);
132 0 : struct net_rate_estimator *old, *est;
133 0 : struct gnet_stats_basic_packed b;
134 0 : int intvl_log;
135 :
136 0 : if (nla_len(opt) < sizeof(*parm))
137 : return -EINVAL;
138 :
139 : /* allowed timer periods are :
140 : * -2 : 250ms, -1 : 500ms, 0 : 1 sec
141 : * 1 : 2 sec, 2 : 4 sec, 3 : 8 sec
142 : */
143 0 : if (parm->interval < -2 || parm->interval > 3)
144 : return -EINVAL;
145 :
146 0 : if (parm->ewma_log == 0 || parm->ewma_log >= 31)
147 : return -EINVAL;
148 :
149 0 : est = kzalloc(sizeof(*est), GFP_KERNEL);
150 0 : if (!est)
151 : return -ENOBUFS;
152 :
153 0 : seqcount_init(&est->seq);
154 0 : intvl_log = parm->interval + 2;
155 0 : est->bstats = bstats;
156 0 : est->stats_lock = lock;
157 0 : est->running = running;
158 0 : est->ewma_log = parm->ewma_log;
159 0 : est->intvl_log = intvl_log;
160 0 : est->cpu_bstats = cpu_bstats;
161 :
162 0 : if (lock)
163 0 : local_bh_disable();
164 0 : est_fetch_counters(est, &b);
165 0 : if (lock)
166 0 : local_bh_enable();
167 0 : est->last_bytes = b.bytes;
168 0 : est->last_packets = b.packets;
169 :
170 0 : if (lock)
171 0 : spin_lock_bh(lock);
172 0 : old = rcu_dereference_protected(*rate_est, 1);
173 0 : if (old) {
174 0 : del_timer_sync(&old->timer);
175 0 : est->avbps = old->avbps;
176 0 : est->avpps = old->avpps;
177 : }
178 :
179 0 : est->next_jiffies = jiffies + ((HZ/4) << intvl_log);
180 0 : timer_setup(&est->timer, est_timer, 0);
181 0 : mod_timer(&est->timer, est->next_jiffies);
182 :
183 0 : rcu_assign_pointer(*rate_est, est);
184 0 : if (lock)
185 0 : spin_unlock_bh(lock);
186 0 : if (old)
187 0 : kfree_rcu(old, rcu);
188 : return 0;
189 : }
190 : EXPORT_SYMBOL(gen_new_estimator);
191 :
192 : /**
193 : * gen_kill_estimator - remove a rate estimator
194 : * @rate_est: rate estimator
195 : *
196 : * Removes the rate estimator.
197 : *
198 : */
199 0 : void gen_kill_estimator(struct net_rate_estimator __rcu **rate_est)
200 : {
201 0 : struct net_rate_estimator *est;
202 :
203 0 : est = xchg((__force struct net_rate_estimator **)rate_est, NULL);
204 0 : if (est) {
205 0 : del_timer_sync(&est->timer);
206 0 : kfree_rcu(est, rcu);
207 : }
208 0 : }
209 : EXPORT_SYMBOL(gen_kill_estimator);
210 :
211 : /**
212 : * gen_replace_estimator - replace rate estimator configuration
213 : * @bstats: basic statistics
214 : * @cpu_bstats: bstats per cpu
215 : * @rate_est: rate estimator statistics
216 : * @lock: lock for statistics and control path
217 : * @running: qdisc running seqcount (might be NULL)
218 : * @opt: rate estimator configuration TLV
219 : *
220 : * Replaces the configuration of a rate estimator by calling
221 : * gen_kill_estimator() and gen_new_estimator().
222 : *
223 : * Returns 0 on success or a negative error code.
224 : */
225 0 : int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
226 : struct gnet_stats_basic_cpu __percpu *cpu_bstats,
227 : struct net_rate_estimator __rcu **rate_est,
228 : spinlock_t *lock,
229 : seqcount_t *running, struct nlattr *opt)
230 : {
231 0 : return gen_new_estimator(bstats, cpu_bstats, rate_est,
232 : lock, running, opt);
233 : }
234 : EXPORT_SYMBOL(gen_replace_estimator);
235 :
236 : /**
237 : * gen_estimator_active - test if estimator is currently in use
238 : * @rate_est: rate estimator
239 : *
240 : * Returns true if estimator is active, and false if not.
241 : */
242 0 : bool gen_estimator_active(struct net_rate_estimator __rcu **rate_est)
243 : {
244 0 : return !!rcu_access_pointer(*rate_est);
245 : }
246 : EXPORT_SYMBOL(gen_estimator_active);
247 :
248 0 : bool gen_estimator_read(struct net_rate_estimator __rcu **rate_est,
249 : struct gnet_stats_rate_est64 *sample)
250 : {
251 0 : struct net_rate_estimator *est;
252 0 : unsigned seq;
253 :
254 0 : rcu_read_lock();
255 0 : est = rcu_dereference(*rate_est);
256 0 : if (!est) {
257 0 : rcu_read_unlock();
258 0 : return false;
259 : }
260 :
261 0 : do {
262 0 : seq = read_seqcount_begin(&est->seq);
263 0 : sample->bps = est->avbps >> 8;
264 0 : sample->pps = est->avpps >> 8;
265 0 : } while (read_seqcount_retry(&est->seq, seq));
266 :
267 0 : rcu_read_unlock();
268 0 : return true;
269 : }
270 : EXPORT_SYMBOL(gen_estimator_read);
|