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 27static inline int 28gnet_stats_copy(struct gnet_dump *d, int type, void *buf, int size) 29{ 30 if (nla_put(d->skb, type, size, buf)) 31 goto nla_put_failure; 32 return 0; 33 34nla_put_failure: 35 kfree(d->xstats); 36 d->xstats = NULL; 37 d->xstats_len = 0; 38 spin_unlock_bh(d->lock); 39 return -1; 40} 41 42/** 43 * gnet_stats_start_copy_compat - start dumping procedure in compatibility mode 44 * @skb: socket buffer to put statistics TLVs into 45 * @type: TLV type for top level statistic TLV 46 * @tc_stats_type: TLV type for backward compatibility struct tc_stats TLV 47 * @xstats_type: TLV type for backward compatibility xstats TLV 48 * @lock: statistics lock 49 * @d: dumping handle 50 * 51 * Initializes the dumping handle, grabs the statistic lock and appends 52 * an empty TLV header to the socket buffer for use a container for all 53 * other statistic TLVS. 54 * 55 * The dumping handle is marked to be in backward compatibility mode telling 56 * all gnet_stats_copy_XXX() functions to fill a local copy of struct tc_stats. 57 * 58 * Returns 0 on success or -1 if the room in the socket buffer was not sufficient. 59 */ 60int 61gnet_stats_start_copy_compat(struct sk_buff *skb, int type, int tc_stats_type, 62 int xstats_type, spinlock_t *lock, struct gnet_dump *d) 63 __acquires(lock) 64{ 65 memset(d, 0, sizeof(*d)); 66 67 spin_lock_bh(lock); 68 d->lock = lock; 69 if (type) 70 d->tail = (struct nlattr *)skb_tail_pointer(skb); 71 d->skb = skb; 72 d->compat_tc_stats = tc_stats_type; 73 d->compat_xstats = xstats_type; 74 75 if (d->tail) 76 return gnet_stats_copy(d, type, NULL, 0); 77 78 return 0; 79} 80EXPORT_SYMBOL(gnet_stats_start_copy_compat); 81 82/** 83 * gnet_stats_start_copy_compat - start dumping procedure in compatibility mode 84 * @skb: socket buffer to put statistics TLVs into 85 * @type: TLV type for top level statistic TLV 86 * @lock: statistics lock 87 * @d: dumping handle 88 * 89 * Initializes the dumping handle, grabs the statistic lock and appends 90 * an empty TLV header to the socket buffer for use a container for all 91 * other statistic TLVS. 92 * 93 * Returns 0 on success or -1 if the room in the socket buffer was not sufficient. 94 */ 95int 96gnet_stats_start_copy(struct sk_buff *skb, int type, spinlock_t *lock, 97 struct gnet_dump *d) 98{ 99 return gnet_stats_start_copy_compat(skb, type, 0, 0, lock, d); 100} 101EXPORT_SYMBOL(gnet_stats_start_copy); 102 103static void 104__gnet_stats_copy_basic_cpu(struct gnet_stats_basic_packed *bstats, 105 struct gnet_stats_basic_cpu __percpu *cpu) 106{ 107 int i; 108 109 for_each_possible_cpu(i) { 110 struct gnet_stats_basic_cpu *bcpu = per_cpu_ptr(cpu, i); 111 unsigned int start; 112 u64 bytes; 113 u32 packets; 114 115 do { 116 start = u64_stats_fetch_begin_irq(&bcpu->syncp); 117 bytes = bcpu->bstats.bytes; 118 packets = bcpu->bstats.packets; 119 } while (u64_stats_fetch_retry_irq(&bcpu->syncp, start)); 120 121 bstats->bytes += bytes; 122 bstats->packets += packets; 123 } 124} 125 126void 127__gnet_stats_copy_basic(struct gnet_stats_basic_packed *bstats, 128 struct gnet_stats_basic_cpu __percpu *cpu, 129 struct gnet_stats_basic_packed *b) 130{ 131 if (cpu) { 132 __gnet_stats_copy_basic_cpu(bstats, cpu); 133 } else { 134 bstats->bytes = b->bytes; 135 bstats->packets = b->packets; 136 } 137} 138EXPORT_SYMBOL(__gnet_stats_copy_basic); 139 140/** 141 * gnet_stats_copy_basic - copy basic statistics into statistic TLV 142 * @d: dumping handle 143 * @b: basic statistics 144 * 145 * Appends the basic statistics to the top level TLV created by 146 * gnet_stats_start_copy(). 147 * 148 * Returns 0 on success or -1 with the statistic lock released 149 * if the room in the socket buffer was not sufficient. 150 */ 151int 152gnet_stats_copy_basic(struct gnet_dump *d, 153 struct gnet_stats_basic_cpu __percpu *cpu, 154 struct gnet_stats_basic_packed *b) 155{ 156 struct gnet_stats_basic_packed bstats = {0}; 157 158 __gnet_stats_copy_basic(&bstats, cpu, b); 159 160 if (d->compat_tc_stats) { 161 d->tc_stats.bytes = bstats.bytes; 162 d->tc_stats.packets = bstats.packets; 163 } 164 165 if (d->tail) { 166 struct gnet_stats_basic sb; 167 168 memset(&sb, 0, sizeof(sb)); 169 sb.bytes = bstats.bytes; 170 sb.packets = bstats.packets; 171 return gnet_stats_copy(d, TCA_STATS_BASIC, &sb, sizeof(sb)); 172 } 173 return 0; 174} 175EXPORT_SYMBOL(gnet_stats_copy_basic); 176 177/** 178 * gnet_stats_copy_rate_est - copy rate estimator statistics into statistics TLV 179 * @d: dumping handle 180 * @b: basic statistics 181 * @r: rate estimator statistics 182 * 183 * Appends the rate estimator statistics to the top level TLV created by 184 * gnet_stats_start_copy(). 185 * 186 * Returns 0 on success or -1 with the statistic lock released 187 * if the room in the socket buffer was not sufficient. 188 */ 189int 190gnet_stats_copy_rate_est(struct gnet_dump *d, 191 const struct gnet_stats_basic_packed *b, 192 struct gnet_stats_rate_est64 *r) 193{ 194 struct gnet_stats_rate_est est; 195 int res; 196 197 if (b && !gen_estimator_active(b, r)) 198 return 0; 199 200 est.bps = min_t(u64, UINT_MAX, r->bps); 201 /* we have some time before reaching 2^32 packets per second */ 202 est.pps = r->pps; 203 204 if (d->compat_tc_stats) { 205 d->tc_stats.bps = est.bps; 206 d->tc_stats.pps = est.pps; 207 } 208 209 if (d->tail) { 210 res = gnet_stats_copy(d, TCA_STATS_RATE_EST, &est, sizeof(est)); 211 if (res < 0 || est.bps == r->bps) 212 return res; 213 /* emit 64bit stats only if needed */ 214 return gnet_stats_copy(d, TCA_STATS_RATE_EST64, r, sizeof(*r)); 215 } 216 217 return 0; 218} 219EXPORT_SYMBOL(gnet_stats_copy_rate_est); 220 221static void 222__gnet_stats_copy_queue_cpu(struct gnet_stats_queue *qstats, 223 const struct gnet_stats_queue __percpu *q) 224{ 225 int i; 226 227 for_each_possible_cpu(i) { 228 const struct gnet_stats_queue *qcpu = per_cpu_ptr(q, i); 229 230 qstats->qlen = 0; 231 qstats->backlog += qcpu->backlog; 232 qstats->drops += qcpu->drops; 233 qstats->requeues += qcpu->requeues; 234 qstats->overlimits += qcpu->overlimits; 235 } 236} 237 238static void __gnet_stats_copy_queue(struct gnet_stats_queue *qstats, 239 const struct gnet_stats_queue __percpu *cpu, 240 const struct gnet_stats_queue *q, 241 __u32 qlen) 242{ 243 if (cpu) { 244 __gnet_stats_copy_queue_cpu(qstats, cpu); 245 } else { 246 qstats->qlen = q->qlen; 247 qstats->backlog = q->backlog; 248 qstats->drops = q->drops; 249 qstats->requeues = q->requeues; 250 qstats->overlimits = q->overlimits; 251 } 252 253 qstats->qlen = qlen; 254} 255 256/** 257 * gnet_stats_copy_queue - copy queue statistics into statistics TLV 258 * @d: dumping handle 259 * @cpu_q: per cpu queue statistics 260 * @q: queue statistics 261 * @qlen: queue length statistics 262 * 263 * Appends the queue statistics to the top level TLV created by 264 * gnet_stats_start_copy(). Using per cpu queue statistics if 265 * they are available. 266 * 267 * Returns 0 on success or -1 with the statistic lock released 268 * if the room in the socket buffer was not sufficient. 269 */ 270int 271gnet_stats_copy_queue(struct gnet_dump *d, 272 struct gnet_stats_queue __percpu *cpu_q, 273 struct gnet_stats_queue *q, __u32 qlen) 274{ 275 struct gnet_stats_queue qstats = {0}; 276 277 __gnet_stats_copy_queue(&qstats, cpu_q, q, qlen); 278 279 if (d->compat_tc_stats) { 280 d->tc_stats.drops = qstats.drops; 281 d->tc_stats.qlen = qstats.qlen; 282 d->tc_stats.backlog = qstats.backlog; 283 d->tc_stats.overlimits = qstats.overlimits; 284 } 285 286 if (d->tail) 287 return gnet_stats_copy(d, TCA_STATS_QUEUE, 288 &qstats, sizeof(qstats)); 289 290 return 0; 291} 292EXPORT_SYMBOL(gnet_stats_copy_queue); 293 294/** 295 * gnet_stats_copy_app - copy application specific statistics into statistics TLV 296 * @d: dumping handle 297 * @st: application specific statistics data 298 * @len: length of data 299 * 300 * Appends the application specific statistics to the top level TLV created by 301 * gnet_stats_start_copy() and remembers the data for XSTATS if the dumping 302 * handle is in backward compatibility mode. 303 * 304 * Returns 0 on success or -1 with the statistic lock released 305 * if the room in the socket buffer was not sufficient. 306 */ 307int 308gnet_stats_copy_app(struct gnet_dump *d, void *st, int len) 309{ 310 if (d->compat_xstats) { 311 d->xstats = kmemdup(st, len, GFP_ATOMIC); 312 if (!d->xstats) 313 goto err_out; 314 d->xstats_len = len; 315 } 316 317 if (d->tail) 318 return gnet_stats_copy(d, TCA_STATS_APP, st, len); 319 320 return 0; 321 322err_out: 323 d->xstats_len = 0; 324 spin_unlock_bh(d->lock); 325 return -1; 326} 327EXPORT_SYMBOL(gnet_stats_copy_app); 328 329/** 330 * gnet_stats_finish_copy - finish dumping procedure 331 * @d: dumping handle 332 * 333 * Corrects the length of the top level TLV to include all TLVs added 334 * by gnet_stats_copy_XXX() calls. Adds the backward compatibility TLVs 335 * if gnet_stats_start_copy_compat() was used and releases the statistics 336 * lock. 337 * 338 * Returns 0 on success or -1 with the statistic lock released 339 * if the room in the socket buffer was not sufficient. 340 */ 341int 342gnet_stats_finish_copy(struct gnet_dump *d) 343{ 344 if (d->tail) 345 d->tail->nla_len = skb_tail_pointer(d->skb) - (u8 *)d->tail; 346 347 if (d->compat_tc_stats) 348 if (gnet_stats_copy(d, d->compat_tc_stats, &d->tc_stats, 349 sizeof(d->tc_stats)) < 0) 350 return -1; 351 352 if (d->compat_xstats && d->xstats) { 353 if (gnet_stats_copy(d, d->compat_xstats, d->xstats, 354 d->xstats_len) < 0) 355 return -1; 356 } 357 358 kfree(d->xstats); 359 d->xstats = NULL; 360 d->xstats_len = 0; 361 spin_unlock_bh(d->lock); 362 return 0; 363} 364EXPORT_SYMBOL(gnet_stats_finish_copy); 365