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 if (nla_put(d->skb, type, size, buf)) 31 goto nla_put_failure; 32 return 0; 33 34 nla_put_failure: 35 spin_unlock_bh(d->lock); 36 return -1; 37 } 38 39 /** 40 * gnet_stats_start_copy_compat - start dumping procedure in compatibility mode 41 * @skb: socket buffer to put statistics TLVs into 42 * @type: TLV type for top level statistic TLV 43 * @tc_stats_type: TLV type for backward compatibility struct tc_stats TLV 44 * @xstats_type: TLV type for backward compatibility xstats TLV 45 * @lock: statistics lock 46 * @d: dumping handle 47 * 48 * Initializes the dumping handle, grabs the statistic lock and appends 49 * an empty TLV header to the socket buffer for use a container for all 50 * other statistic TLVS. 51 * 52 * The dumping handle is marked to be in backward compatibility mode telling 53 * all gnet_stats_copy_XXX() functions to fill a local copy of struct tc_stats. 54 * 55 * Returns 0 on success or -1 if the room in the socket buffer was not sufficient. 56 */ 57 int 58 gnet_stats_start_copy_compat(struct sk_buff *skb, int type, int tc_stats_type, 59 int xstats_type, spinlock_t *lock, struct gnet_dump *d) 60 __acquires(lock) 61 { 62 memset(d, 0, sizeof(*d)); 63 64 spin_lock_bh(lock); 65 d->lock = lock; 66 if (type) 67 d->tail = (struct nlattr *)skb_tail_pointer(skb); 68 d->skb = skb; 69 d->compat_tc_stats = tc_stats_type; 70 d->compat_xstats = xstats_type; 71 72 if (d->tail) 73 return gnet_stats_copy(d, type, NULL, 0); 74 75 return 0; 76 } 77 EXPORT_SYMBOL(gnet_stats_start_copy_compat); 78 79 /** 80 * gnet_stats_start_copy_compat - start dumping procedure in compatibility mode 81 * @skb: socket buffer to put statistics TLVs into 82 * @type: TLV type for top level statistic TLV 83 * @lock: statistics lock 84 * @d: dumping handle 85 * 86 * Initializes the dumping handle, grabs the statistic lock and appends 87 * an empty TLV header to the socket buffer for use a container for all 88 * other statistic TLVS. 89 * 90 * Returns 0 on success or -1 if the room in the socket buffer was not sufficient. 91 */ 92 int 93 gnet_stats_start_copy(struct sk_buff *skb, int type, spinlock_t *lock, 94 struct gnet_dump *d) 95 { 96 return gnet_stats_start_copy_compat(skb, type, 0, 0, lock, d); 97 } 98 EXPORT_SYMBOL(gnet_stats_start_copy); 99 100 /** 101 * gnet_stats_copy_basic - copy basic statistics into statistic TLV 102 * @d: dumping handle 103 * @b: basic statistics 104 * 105 * Appends the basic statistics to the top level TLV created by 106 * gnet_stats_start_copy(). 107 * 108 * Returns 0 on success or -1 with the statistic lock released 109 * if the room in the socket buffer was not sufficient. 110 */ 111 int 112 gnet_stats_copy_basic(struct gnet_dump *d, struct gnet_stats_basic_packed *b) 113 { 114 if (d->compat_tc_stats) { 115 d->tc_stats.bytes = b->bytes; 116 d->tc_stats.packets = b->packets; 117 } 118 119 if (d->tail) { 120 struct gnet_stats_basic sb; 121 122 memset(&sb, 0, sizeof(sb)); 123 sb.bytes = b->bytes; 124 sb.packets = b->packets; 125 return gnet_stats_copy(d, TCA_STATS_BASIC, &sb, sizeof(sb)); 126 } 127 return 0; 128 } 129 EXPORT_SYMBOL(gnet_stats_copy_basic); 130 131 /** 132 * gnet_stats_copy_rate_est - copy rate estimator statistics into statistics TLV 133 * @d: dumping handle 134 * @b: basic statistics 135 * @r: rate estimator statistics 136 * 137 * Appends the rate estimator statistics to the top level TLV created by 138 * gnet_stats_start_copy(). 139 * 140 * Returns 0 on success or -1 with the statistic lock released 141 * if the room in the socket buffer was not sufficient. 142 */ 143 int 144 gnet_stats_copy_rate_est(struct gnet_dump *d, 145 const struct gnet_stats_basic_packed *b, 146 struct gnet_stats_rate_est64 *r) 147 { 148 struct gnet_stats_rate_est est; 149 int res; 150 151 if (b && !gen_estimator_active(b, r)) 152 return 0; 153 154 est.bps = min_t(u64, UINT_MAX, r->bps); 155 /* we have some time before reaching 2^32 packets per second */ 156 est.pps = r->pps; 157 158 if (d->compat_tc_stats) { 159 d->tc_stats.bps = est.bps; 160 d->tc_stats.pps = est.pps; 161 } 162 163 if (d->tail) { 164 res = gnet_stats_copy(d, TCA_STATS_RATE_EST, &est, sizeof(est)); 165 if (res < 0 || est.bps == r->bps) 166 return res; 167 /* emit 64bit stats only if needed */ 168 return gnet_stats_copy(d, TCA_STATS_RATE_EST64, r, sizeof(*r)); 169 } 170 171 return 0; 172 } 173 EXPORT_SYMBOL(gnet_stats_copy_rate_est); 174 175 /** 176 * gnet_stats_copy_queue - copy queue statistics into statistics TLV 177 * @d: dumping handle 178 * @q: queue statistics 179 * 180 * Appends the queue statistics to the top level TLV created by 181 * gnet_stats_start_copy(). 182 * 183 * Returns 0 on success or -1 with the statistic lock released 184 * if the room in the socket buffer was not sufficient. 185 */ 186 int 187 gnet_stats_copy_queue(struct gnet_dump *d, struct gnet_stats_queue *q) 188 { 189 if (d->compat_tc_stats) { 190 d->tc_stats.drops = q->drops; 191 d->tc_stats.qlen = q->qlen; 192 d->tc_stats.backlog = q->backlog; 193 d->tc_stats.overlimits = q->overlimits; 194 } 195 196 if (d->tail) 197 return gnet_stats_copy(d, TCA_STATS_QUEUE, q, sizeof(*q)); 198 199 return 0; 200 } 201 EXPORT_SYMBOL(gnet_stats_copy_queue); 202 203 /** 204 * gnet_stats_copy_app - copy application specific statistics into statistics TLV 205 * @d: dumping handle 206 * @st: application specific statistics data 207 * @len: length of data 208 * 209 * Appends the application sepecific statistics to the top level TLV created by 210 * gnet_stats_start_copy() and remembers the data for XSTATS if the dumping 211 * handle is in backward compatibility mode. 212 * 213 * Returns 0 on success or -1 with the statistic lock released 214 * if the room in the socket buffer was not sufficient. 215 */ 216 int 217 gnet_stats_copy_app(struct gnet_dump *d, void *st, int len) 218 { 219 if (d->compat_xstats) { 220 d->xstats = st; 221 d->xstats_len = len; 222 } 223 224 if (d->tail) 225 return gnet_stats_copy(d, TCA_STATS_APP, st, len); 226 227 return 0; 228 } 229 EXPORT_SYMBOL(gnet_stats_copy_app); 230 231 /** 232 * gnet_stats_finish_copy - finish dumping procedure 233 * @d: dumping handle 234 * 235 * Corrects the length of the top level TLV to include all TLVs added 236 * by gnet_stats_copy_XXX() calls. Adds the backward compatibility TLVs 237 * if gnet_stats_start_copy_compat() was used and releases the statistics 238 * lock. 239 * 240 * Returns 0 on success or -1 with the statistic lock released 241 * if the room in the socket buffer was not sufficient. 242 */ 243 int 244 gnet_stats_finish_copy(struct gnet_dump *d) 245 { 246 if (d->tail) 247 d->tail->nla_len = skb_tail_pointer(d->skb) - (u8 *)d->tail; 248 249 if (d->compat_tc_stats) 250 if (gnet_stats_copy(d, d->compat_tc_stats, &d->tc_stats, 251 sizeof(d->tc_stats)) < 0) 252 return -1; 253 254 if (d->compat_xstats && d->xstats) { 255 if (gnet_stats_copy(d, d->compat_xstats, d->xstats, 256 d->xstats_len) < 0) 257 return -1; 258 } 259 260 spin_unlock_bh(d->lock); 261 return 0; 262 } 263 EXPORT_SYMBOL(gnet_stats_finish_copy); 264