pandora: defconfig: update
[pandora-kernel.git] / net / core / gen_stats.c
1 /*
2  * net/core/gen_stats.c
3  *
4  *             This program is free software; you can redistribute it and/or
5  *             modify it under the terms of the GNU General Public License
6  *             as published by the Free Software Foundation; either version
7  *             2 of the License, or (at your option) any later version.
8  *
9  * Authors:  Thomas Graf <tgraf@suug.ch>
10  *           Jamal Hadi Salim
11  *           Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
12  *
13  * See Documentation/networking/gen_stats.txt
14  */
15
16 #include <linux/types.h>
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/interrupt.h>
20 #include <linux/socket.h>
21 #include <linux/rtnetlink.h>
22 #include <linux/gen_stats.h>
23 #include <net/netlink.h>
24 #include <net/gen_stats.h>
25
26
27 static inline int
28 gnet_stats_copy(struct gnet_dump *d, int type, void *buf, int size)
29 {
30         NLA_PUT(d->skb, type, size, buf);
31         return 0;
32
33 nla_put_failure:
34         kfree(d->xstats);
35         d->xstats = NULL;
36         d->xstats_len = 0;
37         spin_unlock_bh(d->lock);
38         return -1;
39 }
40
41 /**
42  * gnet_stats_start_copy_compat - start dumping procedure in compatibility mode
43  * @skb: socket buffer to put statistics TLVs into
44  * @type: TLV type for top level statistic TLV
45  * @tc_stats_type: TLV type for backward compatibility struct tc_stats TLV
46  * @xstats_type: TLV type for backward compatibility xstats TLV
47  * @lock: statistics lock
48  * @d: dumping handle
49  *
50  * Initializes the dumping handle, grabs the statistic lock and appends
51  * an empty TLV header to the socket buffer for use a container for all
52  * other statistic TLVS.
53  *
54  * The dumping handle is marked to be in backward compatibility mode telling
55  * all gnet_stats_copy_XXX() functions to fill a local copy of struct tc_stats.
56  *
57  * Returns 0 on success or -1 if the room in the socket buffer was not sufficient.
58  */
59 int
60 gnet_stats_start_copy_compat(struct sk_buff *skb, int type, int tc_stats_type,
61         int xstats_type, spinlock_t *lock, struct gnet_dump *d)
62         __acquires(lock)
63 {
64         memset(d, 0, sizeof(*d));
65
66         spin_lock_bh(lock);
67         d->lock = lock;
68         if (type)
69                 d->tail = (struct nlattr *)skb_tail_pointer(skb);
70         d->skb = skb;
71         d->compat_tc_stats = tc_stats_type;
72         d->compat_xstats = xstats_type;
73
74         if (d->tail)
75                 return gnet_stats_copy(d, type, NULL, 0);
76
77         return 0;
78 }
79 EXPORT_SYMBOL(gnet_stats_start_copy_compat);
80
81 /**
82  * gnet_stats_start_copy_compat - start dumping procedure in compatibility mode
83  * @skb: socket buffer to put statistics TLVs into
84  * @type: TLV type for top level statistic TLV
85  * @lock: statistics lock
86  * @d: dumping handle
87  *
88  * Initializes the dumping handle, grabs the statistic lock and appends
89  * an empty TLV header to the socket buffer for use a container for all
90  * other statistic TLVS.
91  *
92  * Returns 0 on success or -1 if the room in the socket buffer was not sufficient.
93  */
94 int
95 gnet_stats_start_copy(struct sk_buff *skb, int type, spinlock_t *lock,
96         struct gnet_dump *d)
97 {
98         return gnet_stats_start_copy_compat(skb, type, 0, 0, lock, d);
99 }
100 EXPORT_SYMBOL(gnet_stats_start_copy);
101
102 /**
103  * gnet_stats_copy_basic - copy basic statistics into statistic TLV
104  * @d: dumping handle
105  * @b: basic statistics
106  *
107  * Appends the basic statistics to the top level TLV created by
108  * gnet_stats_start_copy().
109  *
110  * Returns 0 on success or -1 with the statistic lock released
111  * if the room in the socket buffer was not sufficient.
112  */
113 int
114 gnet_stats_copy_basic(struct gnet_dump *d, struct gnet_stats_basic_packed *b)
115 {
116         if (d->compat_tc_stats) {
117                 d->tc_stats.bytes = b->bytes;
118                 d->tc_stats.packets = b->packets;
119         }
120
121         if (d->tail) {
122                 struct gnet_stats_basic sb;
123
124                 memset(&sb, 0, sizeof(sb));
125                 sb.bytes = b->bytes;
126                 sb.packets = b->packets;
127                 return gnet_stats_copy(d, TCA_STATS_BASIC, &sb, sizeof(sb));
128         }
129         return 0;
130 }
131 EXPORT_SYMBOL(gnet_stats_copy_basic);
132
133 /**
134  * gnet_stats_copy_rate_est - copy rate estimator statistics into statistics TLV
135  * @d: dumping handle
136  * @b: basic statistics
137  * @r: rate estimator statistics
138  *
139  * Appends the rate estimator statistics to the top level TLV created by
140  * gnet_stats_start_copy().
141  *
142  * Returns 0 on success or -1 with the statistic lock released
143  * if the room in the socket buffer was not sufficient.
144  */
145 int
146 gnet_stats_copy_rate_est(struct gnet_dump *d,
147                          const struct gnet_stats_basic_packed *b,
148                          struct gnet_stats_rate_est *r)
149 {
150         if (b && !gen_estimator_active(b, r))
151                 return 0;
152
153         if (d->compat_tc_stats) {
154                 d->tc_stats.bps = r->bps;
155                 d->tc_stats.pps = r->pps;
156         }
157
158         if (d->tail)
159                 return gnet_stats_copy(d, TCA_STATS_RATE_EST, r, sizeof(*r));
160
161         return 0;
162 }
163 EXPORT_SYMBOL(gnet_stats_copy_rate_est);
164
165 /**
166  * gnet_stats_copy_queue - copy queue statistics into statistics TLV
167  * @d: dumping handle
168  * @q: queue statistics
169  *
170  * Appends the queue statistics to the top level TLV created by
171  * gnet_stats_start_copy().
172  *
173  * Returns 0 on success or -1 with the statistic lock released
174  * if the room in the socket buffer was not sufficient.
175  */
176 int
177 gnet_stats_copy_queue(struct gnet_dump *d, struct gnet_stats_queue *q)
178 {
179         if (d->compat_tc_stats) {
180                 d->tc_stats.drops = q->drops;
181                 d->tc_stats.qlen = q->qlen;
182                 d->tc_stats.backlog = q->backlog;
183                 d->tc_stats.overlimits = q->overlimits;
184         }
185
186         if (d->tail)
187                 return gnet_stats_copy(d, TCA_STATS_QUEUE, q, sizeof(*q));
188
189         return 0;
190 }
191 EXPORT_SYMBOL(gnet_stats_copy_queue);
192
193 /**
194  * gnet_stats_copy_app - copy application specific statistics into statistics TLV
195  * @d: dumping handle
196  * @st: application specific statistics data
197  * @len: length of data
198  *
199  * Appends the application sepecific statistics to the top level TLV created by
200  * gnet_stats_start_copy() and remembers the data for XSTATS if the dumping
201  * handle is in backward compatibility mode.
202  *
203  * Returns 0 on success or -1 with the statistic lock released
204  * if the room in the socket buffer was not sufficient.
205  */
206 int
207 gnet_stats_copy_app(struct gnet_dump *d, void *st, int len)
208 {
209         if (d->compat_xstats) {
210                 d->xstats = kmemdup(st, len, GFP_ATOMIC);
211                 if (!d->xstats)
212                         goto err_out;
213                 d->xstats_len = len;
214         }
215
216         if (d->tail)
217                 return gnet_stats_copy(d, TCA_STATS_APP, st, len);
218
219         return 0;
220
221 err_out:
222         d->xstats_len = 0;
223         spin_unlock_bh(d->lock);
224         return -1;
225 }
226 EXPORT_SYMBOL(gnet_stats_copy_app);
227
228 /**
229  * gnet_stats_finish_copy - finish dumping procedure
230  * @d: dumping handle
231  *
232  * Corrects the length of the top level TLV to include all TLVs added
233  * by gnet_stats_copy_XXX() calls. Adds the backward compatibility TLVs
234  * if gnet_stats_start_copy_compat() was used and releases the statistics
235  * lock.
236  *
237  * Returns 0 on success or -1 with the statistic lock released
238  * if the room in the socket buffer was not sufficient.
239  */
240 int
241 gnet_stats_finish_copy(struct gnet_dump *d)
242 {
243         if (d->tail)
244                 d->tail->nla_len = skb_tail_pointer(d->skb) - (u8 *)d->tail;
245
246         if (d->compat_tc_stats)
247                 if (gnet_stats_copy(d, d->compat_tc_stats, &d->tc_stats,
248                         sizeof(d->tc_stats)) < 0)
249                         return -1;
250
251         if (d->compat_xstats && d->xstats) {
252                 if (gnet_stats_copy(d, d->compat_xstats, d->xstats,
253                         d->xstats_len) < 0)
254                         return -1;
255         }
256
257         kfree(d->xstats);
258         d->xstats = NULL;
259         d->xstats_len = 0;
260         spin_unlock_bh(d->lock);
261         return 0;
262 }
263 EXPORT_SYMBOL(gnet_stats_finish_copy);