Blame view

net/core/gen_stats.c 10.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  /*
   * net/core/gen_stats.c
   *
   *             This program is free software; you can redistribute it and/or
   *             modify it under the terms of the GNU General Public License
   *             as published by the Free Software Foundation; either version
   *             2 of the License, or (at your option) any later version.
   *
   * Authors:  Thomas Graf <tgraf@suug.ch>
   *           Jamal Hadi Salim
   *           Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   *
   * See Documentation/networking/gen_stats.txt
   */
  
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/interrupt.h>
  #include <linux/socket.h>
  #include <linux/rtnetlink.h>
  #include <linux/gen_stats.h>
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
23
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
  #include <net/gen_stats.h>
  
  
  static inline int
9854518ea   Nicolas Dichtel   sched: align nlat...
28
  gnet_stats_copy(struct gnet_dump *d, int type, void *buf, int size, int padattr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
  {
9854518ea   Nicolas Dichtel   sched: align nlat...
30
  	if (nla_put_64bit(d->skb, type, size, buf, padattr))
14ad6647f   David S. Miller   gen_stats: Stop u...
31
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
  	return 0;
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
33
  nla_put_failure:
edb09eb17   Eric Dumazet   net: sched: do no...
34
35
  	if (d->lock)
  		spin_unlock_bh(d->lock);
1c4cff0cf   Ignacy Gawędzki   gen_stats.c: Dupl...
36
37
38
  	kfree(d->xstats);
  	d->xstats = NULL;
  	d->xstats_len = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
43
44
45
46
47
48
49
  	return -1;
  }
  
  /**
   * gnet_stats_start_copy_compat - start dumping procedure in compatibility mode
   * @skb: socket buffer to put statistics TLVs into
   * @type: TLV type for top level statistic TLV
   * @tc_stats_type: TLV type for backward compatibility struct tc_stats TLV
   * @xstats_type: TLV type for backward compatibility xstats TLV
   * @lock: statistics lock
   * @d: dumping handle
e0d194adf   Eric Dumazet   net_sched: add mi...
50
   * @padattr: padding attribute
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
55
56
57
58
59
60
61
62
   *
   * Initializes the dumping handle, grabs the statistic lock and appends
   * an empty TLV header to the socket buffer for use a container for all
   * other statistic TLVS.
   *
   * The dumping handle is marked to be in backward compatibility mode telling
   * all gnet_stats_copy_XXX() functions to fill a local copy of struct tc_stats.
   *
   * Returns 0 on success or -1 if the room in the socket buffer was not sufficient.
   */
  int
  gnet_stats_start_copy_compat(struct sk_buff *skb, int type, int tc_stats_type,
9854518ea   Nicolas Dichtel   sched: align nlat...
63
64
  			     int xstats_type, spinlock_t *lock,
  			     struct gnet_dump *d, int padattr)
9a429c498   Eric Dumazet   [NET]: Add some a...
65
  	__acquires(lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
  {
  	memset(d, 0, sizeof(*d));
4ec93edb1   YOSHIFUJI Hideaki   [NET] CORE: Fix w...
68

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  	if (type)
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
70
  		d->tail = (struct nlattr *)skb_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
  	d->skb = skb;
  	d->compat_tc_stats = tc_stats_type;
  	d->compat_xstats = xstats_type;
9854518ea   Nicolas Dichtel   sched: align nlat...
74
  	d->padattr = padattr;
edb09eb17   Eric Dumazet   net: sched: do no...
75
76
77
78
  	if (lock) {
  		d->lock = lock;
  		spin_lock_bh(lock);
  	}
c8347d91c   Toke Høiland-Jørgensen   gen_stats: Fix ne...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  	if (d->tail) {
  		int ret = gnet_stats_copy(d, type, NULL, 0, padattr);
  
  		/* The initial attribute added in gnet_stats_copy() may be
  		 * preceded by a padding attribute, in which case d->tail will
  		 * end up pointing at the padding instead of the real attribute.
  		 * Fix this so gnet_stats_finish_copy() adjusts the length of
  		 * the right attribute.
  		 */
  		if (ret == 0 && d->tail->nla_type == padattr)
  			d->tail = (struct nlattr *)((char *)d->tail +
  						    NLA_ALIGN(d->tail->nla_len));
  		return ret;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
  
  	return 0;
  }
9e34a5b51   Eric Dumazet   net/core: EXPORT_...
96
  EXPORT_SYMBOL(gnet_stats_start_copy_compat);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
  
  /**
9854518ea   Nicolas Dichtel   sched: align nlat...
99
   * gnet_stats_start_copy - start dumping procedure in compatibility mode
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
   * @skb: socket buffer to put statistics TLVs into
   * @type: TLV type for top level statistic TLV
   * @lock: statistics lock
   * @d: dumping handle
e0d194adf   Eric Dumazet   net_sched: add mi...
104
   * @padattr: padding attribute
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
107
108
109
110
111
112
113
   *
   * Initializes the dumping handle, grabs the statistic lock and appends
   * an empty TLV header to the socket buffer for use a container for all
   * other statistic TLVS.
   *
   * Returns 0 on success or -1 if the room in the socket buffer was not sufficient.
   */
  int
  gnet_stats_start_copy(struct sk_buff *skb, int type, spinlock_t *lock,
9854518ea   Nicolas Dichtel   sched: align nlat...
114
  		      struct gnet_dump *d, int padattr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
  {
9854518ea   Nicolas Dichtel   sched: align nlat...
116
  	return gnet_stats_start_copy_compat(skb, type, 0, 0, lock, d, padattr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
  }
9e34a5b51   Eric Dumazet   net/core: EXPORT_...
118
  EXPORT_SYMBOL(gnet_stats_start_copy);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119

22e0f8b93   John Fastabend   net: sched: make ...
120
121
122
123
124
125
126
127
128
  static void
  __gnet_stats_copy_basic_cpu(struct gnet_stats_basic_packed *bstats,
  			    struct gnet_stats_basic_cpu __percpu *cpu)
  {
  	int i;
  
  	for_each_possible_cpu(i) {
  		struct gnet_stats_basic_cpu *bcpu = per_cpu_ptr(cpu, i);
  		unsigned int start;
02c0fc1b8   WANG Cong   net_sched: fix un...
129
130
  		u64 bytes;
  		u32 packets;
22e0f8b93   John Fastabend   net: sched: make ...
131
132
133
134
135
136
  
  		do {
  			start = u64_stats_fetch_begin_irq(&bcpu->syncp);
  			bytes = bcpu->bstats.bytes;
  			packets = bcpu->bstats.packets;
  		} while (u64_stats_fetch_retry_irq(&bcpu->syncp, start));
02c0fc1b8   WANG Cong   net_sched: fix un...
137
138
  		bstats->bytes += bytes;
  		bstats->packets += packets;
22e0f8b93   John Fastabend   net: sched: make ...
139
140
141
142
  	}
  }
  
  void
edb09eb17   Eric Dumazet   net: sched: do no...
143
144
  __gnet_stats_copy_basic(const seqcount_t *running,
  			struct gnet_stats_basic_packed *bstats,
22e0f8b93   John Fastabend   net: sched: make ...
145
146
147
  			struct gnet_stats_basic_cpu __percpu *cpu,
  			struct gnet_stats_basic_packed *b)
  {
edb09eb17   Eric Dumazet   net: sched: do no...
148
  	unsigned int seq;
22e0f8b93   John Fastabend   net: sched: make ...
149
150
  	if (cpu) {
  		__gnet_stats_copy_basic_cpu(bstats, cpu);
edb09eb17   Eric Dumazet   net: sched: do no...
151
152
153
154
155
  		return;
  	}
  	do {
  		if (running)
  			seq = read_seqcount_begin(running);
22e0f8b93   John Fastabend   net: sched: make ...
156
157
  		bstats->bytes = b->bytes;
  		bstats->packets = b->packets;
edb09eb17   Eric Dumazet   net: sched: do no...
158
  	} while (running && read_seqcount_retry(running, seq));
22e0f8b93   John Fastabend   net: sched: make ...
159
160
  }
  EXPORT_SYMBOL(__gnet_stats_copy_basic);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
162
  /**
   * gnet_stats_copy_basic - copy basic statistics into statistic TLV
123b36526   Eric Dumazet   net: sched: fix m...
163
   * @running: seqcount_t pointer
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
   * @d: dumping handle
b002fdcc8   Luis de Bethencourt   gen_stats.c: Add ...
165
   * @cpu: copy statistic per cpu
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
169
170
171
172
173
174
   * @b: basic statistics
   *
   * Appends the basic statistics to the top level TLV created by
   * gnet_stats_start_copy().
   *
   * Returns 0 on success or -1 with the statistic lock released
   * if the room in the socket buffer was not sufficient.
   */
  int
edb09eb17   Eric Dumazet   net: sched: do no...
175
176
  gnet_stats_copy_basic(const seqcount_t *running,
  		      struct gnet_dump *d,
22e0f8b93   John Fastabend   net: sched: make ...
177
178
  		      struct gnet_stats_basic_cpu __percpu *cpu,
  		      struct gnet_stats_basic_packed *b)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
  {
22e0f8b93   John Fastabend   net: sched: make ...
180
  	struct gnet_stats_basic_packed bstats = {0};
edb09eb17   Eric Dumazet   net: sched: do no...
181
  	__gnet_stats_copy_basic(running, &bstats, cpu, b);
22e0f8b93   John Fastabend   net: sched: make ...
182

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  	if (d->compat_tc_stats) {
22e0f8b93   John Fastabend   net: sched: make ...
184
185
  		d->tc_stats.bytes = bstats.bytes;
  		d->tc_stats.packets = bstats.packets;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
  	}
c1a8f1f1c   Eric Dumazet   net: restore gnet...
187
188
  	if (d->tail) {
  		struct gnet_stats_basic sb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189

c1a8f1f1c   Eric Dumazet   net: restore gnet...
190
  		memset(&sb, 0, sizeof(sb));
22e0f8b93   John Fastabend   net: sched: make ...
191
192
  		sb.bytes = bstats.bytes;
  		sb.packets = bstats.packets;
9854518ea   Nicolas Dichtel   sched: align nlat...
193
194
  		return gnet_stats_copy(d, TCA_STATS_BASIC, &sb, sizeof(sb),
  				       TCA_STATS_PAD);
c1a8f1f1c   Eric Dumazet   net: restore gnet...
195
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
  	return 0;
  }
9e34a5b51   Eric Dumazet   net/core: EXPORT_...
198
  EXPORT_SYMBOL(gnet_stats_copy_basic);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
202
  
  /**
   * gnet_stats_copy_rate_est - copy rate estimator statistics into statistics TLV
   * @d: dumping handle
1c0d32fde   Eric Dumazet   net_sched: gen_es...
203
   * @rate_est: rate estimator
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
205
206
207
208
209
210
211
   *
   * Appends the rate estimator statistics to the top level TLV created by
   * gnet_stats_start_copy().
   *
   * Returns 0 on success or -1 with the statistic lock released
   * if the room in the socket buffer was not sufficient.
   */
  int
d250a5f90   Eric Dumazet   pkt_sched: gen_es...
212
  gnet_stats_copy_rate_est(struct gnet_dump *d,
1c0d32fde   Eric Dumazet   net_sched: gen_es...
213
  			 struct net_rate_estimator __rcu **rate_est)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  {
1c0d32fde   Eric Dumazet   net_sched: gen_es...
215
  	struct gnet_stats_rate_est64 sample;
45203a3b3   Eric Dumazet   net_sched: add 64...
216
217
  	struct gnet_stats_rate_est est;
  	int res;
1c0d32fde   Eric Dumazet   net_sched: gen_es...
218
  	if (!gen_estimator_read(rate_est, &sample))
d250a5f90   Eric Dumazet   pkt_sched: gen_es...
219
  		return 0;
1c0d32fde   Eric Dumazet   net_sched: gen_es...
220
  	est.bps = min_t(u64, UINT_MAX, sample.bps);
45203a3b3   Eric Dumazet   net_sched: add 64...
221
  	/* we have some time before reaching 2^32 packets per second */
1c0d32fde   Eric Dumazet   net_sched: gen_es...
222
  	est.pps = sample.pps;
45203a3b3   Eric Dumazet   net_sched: add 64...
223

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  	if (d->compat_tc_stats) {
45203a3b3   Eric Dumazet   net_sched: add 64...
225
226
  		d->tc_stats.bps = est.bps;
  		d->tc_stats.pps = est.pps;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
  	}
45203a3b3   Eric Dumazet   net_sched: add 64...
228
  	if (d->tail) {
9854518ea   Nicolas Dichtel   sched: align nlat...
229
230
  		res = gnet_stats_copy(d, TCA_STATS_RATE_EST, &est, sizeof(est),
  				      TCA_STATS_PAD);
1c0d32fde   Eric Dumazet   net_sched: gen_es...
231
  		if (res < 0 || est.bps == sample.bps)
45203a3b3   Eric Dumazet   net_sched: add 64...
232
233
  			return res;
  		/* emit 64bit stats only if needed */
1c0d32fde   Eric Dumazet   net_sched: gen_es...
234
235
  		return gnet_stats_copy(d, TCA_STATS_RATE_EST64, &sample,
  				       sizeof(sample), TCA_STATS_PAD);
45203a3b3   Eric Dumazet   net_sched: add 64...
236
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
239
  
  	return 0;
  }
9e34a5b51   Eric Dumazet   net/core: EXPORT_...
240
  EXPORT_SYMBOL(gnet_stats_copy_rate_est);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241

b0ab6f927   John Fastabend   net: sched: enabl...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
  static void
  __gnet_stats_copy_queue_cpu(struct gnet_stats_queue *qstats,
  			    const struct gnet_stats_queue __percpu *q)
  {
  	int i;
  
  	for_each_possible_cpu(i) {
  		const struct gnet_stats_queue *qcpu = per_cpu_ptr(q, i);
  
  		qstats->qlen = 0;
  		qstats->backlog += qcpu->backlog;
  		qstats->drops += qcpu->drops;
  		qstats->requeues += qcpu->requeues;
  		qstats->overlimits += qcpu->overlimits;
  	}
  }
  
  static void __gnet_stats_copy_queue(struct gnet_stats_queue *qstats,
  				    const struct gnet_stats_queue __percpu *cpu,
  				    const struct gnet_stats_queue *q,
  				    __u32 qlen)
  {
  	if (cpu) {
  		__gnet_stats_copy_queue_cpu(qstats, cpu);
  	} else {
  		qstats->qlen = q->qlen;
  		qstats->backlog = q->backlog;
  		qstats->drops = q->drops;
  		qstats->requeues = q->requeues;
  		qstats->overlimits = q->overlimits;
  	}
  
  	qstats->qlen = qlen;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276
277
278
  /**
   * gnet_stats_copy_queue - copy queue statistics into statistics TLV
   * @d: dumping handle
b0ab6f927   John Fastabend   net: sched: enabl...
279
   * @cpu_q: per cpu queue statistics
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
   * @q: queue statistics
640158536   John Fastabend   net: sched: restr...
281
   * @qlen: queue length statistics
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
   *
   * Appends the queue statistics to the top level TLV created by
b0ab6f927   John Fastabend   net: sched: enabl...
284
285
   * gnet_stats_start_copy(). Using per cpu queue statistics if
   * they are available.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
288
289
290
   *
   * Returns 0 on success or -1 with the statistic lock released
   * if the room in the socket buffer was not sufficient.
   */
  int
640158536   John Fastabend   net: sched: restr...
291
  gnet_stats_copy_queue(struct gnet_dump *d,
b0ab6f927   John Fastabend   net: sched: enabl...
292
  		      struct gnet_stats_queue __percpu *cpu_q,
640158536   John Fastabend   net: sched: restr...
293
  		      struct gnet_stats_queue *q, __u32 qlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
  {
b0ab6f927   John Fastabend   net: sched: enabl...
295
296
297
  	struct gnet_stats_queue qstats = {0};
  
  	__gnet_stats_copy_queue(&qstats, cpu_q, q, qlen);
640158536   John Fastabend   net: sched: restr...
298

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
  	if (d->compat_tc_stats) {
b0ab6f927   John Fastabend   net: sched: enabl...
300
301
302
303
  		d->tc_stats.drops = qstats.drops;
  		d->tc_stats.qlen = qstats.qlen;
  		d->tc_stats.backlog = qstats.backlog;
  		d->tc_stats.overlimits = qstats.overlimits;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
  	}
  
  	if (d->tail)
b0ab6f927   John Fastabend   net: sched: enabl...
307
  		return gnet_stats_copy(d, TCA_STATS_QUEUE,
9854518ea   Nicolas Dichtel   sched: align nlat...
308
309
  				       &qstats, sizeof(qstats),
  				       TCA_STATS_PAD);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
311
312
  
  	return 0;
  }
9e34a5b51   Eric Dumazet   net/core: EXPORT_...
313
  EXPORT_SYMBOL(gnet_stats_copy_queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
317
318
319
320
  
  /**
   * gnet_stats_copy_app - copy application specific statistics into statistics TLV
   * @d: dumping handle
   * @st: application specific statistics data
   * @len: length of data
   *
e793c0f70   Masanari Iida   net: treewide: Fi...
321
   * Appends the application specific statistics to the top level TLV created by
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
324
325
326
327
328
329
330
331
   * gnet_stats_start_copy() and remembers the data for XSTATS if the dumping
   * handle is in backward compatibility mode.
   *
   * Returns 0 on success or -1 with the statistic lock released
   * if the room in the socket buffer was not sufficient.
   */
  int
  gnet_stats_copy_app(struct gnet_dump *d, void *st, int len)
  {
  	if (d->compat_xstats) {
1c4cff0cf   Ignacy Gawędzki   gen_stats.c: Dupl...
332
333
334
  		d->xstats = kmemdup(st, len, GFP_ATOMIC);
  		if (!d->xstats)
  			goto err_out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
  		d->xstats_len = len;
  	}
  
  	if (d->tail)
9854518ea   Nicolas Dichtel   sched: align nlat...
339
340
  		return gnet_stats_copy(d, TCA_STATS_APP, st, len,
  				       TCA_STATS_PAD);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
  
  	return 0;
1c4cff0cf   Ignacy Gawędzki   gen_stats.c: Dupl...
343
344
  
  err_out:
edb09eb17   Eric Dumazet   net: sched: do no...
345
346
  	if (d->lock)
  		spin_unlock_bh(d->lock);
1c4cff0cf   Ignacy Gawędzki   gen_stats.c: Dupl...
347
  	d->xstats_len = 0;
1c4cff0cf   Ignacy Gawędzki   gen_stats.c: Dupl...
348
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
  }
9e34a5b51   Eric Dumazet   net/core: EXPORT_...
350
  EXPORT_SYMBOL(gnet_stats_copy_app);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
  
  /**
   * gnet_stats_finish_copy - finish dumping procedure
   * @d: dumping handle
   *
   * Corrects the length of the top level TLV to include all TLVs added
   * by gnet_stats_copy_XXX() calls. Adds the backward compatibility TLVs
   * if gnet_stats_start_copy_compat() was used and releases the statistics
   * lock.
   *
   * Returns 0 on success or -1 with the statistic lock released
   * if the room in the socket buffer was not sufficient.
   */
  int
  gnet_stats_finish_copy(struct gnet_dump *d)
  {
  	if (d->tail)
1e90474c3   Patrick McHardy   [NET_SCHED]: Conv...
368
  		d->tail->nla_len = skb_tail_pointer(d->skb) - (u8 *)d->tail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
371
  
  	if (d->compat_tc_stats)
  		if (gnet_stats_copy(d, d->compat_tc_stats, &d->tc_stats,
9854518ea   Nicolas Dichtel   sched: align nlat...
372
  				    sizeof(d->tc_stats), d->padattr) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
374
375
376
  			return -1;
  
  	if (d->compat_xstats && d->xstats) {
  		if (gnet_stats_copy(d, d->compat_xstats, d->xstats,
9854518ea   Nicolas Dichtel   sched: align nlat...
377
  				    d->xstats_len, d->padattr) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
  			return -1;
  	}
edb09eb17   Eric Dumazet   net: sched: do no...
380
381
  	if (d->lock)
  		spin_unlock_bh(d->lock);
1c4cff0cf   Ignacy Gawędzki   gen_stats.c: Dupl...
382
383
384
  	kfree(d->xstats);
  	d->xstats = NULL;
  	d->xstats_len = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
  EXPORT_SYMBOL(gnet_stats_finish_copy);