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_est *r) 147 { 148 if (b && !gen_estimator_active(b, r)) 149 return 0; 150 151 if (d->compat_tc_stats) { 152 d->tc_stats.bps = r->bps; 153 d->tc_stats.pps = r->pps; 154 } 155 156 if (d->tail) 157 return gnet_stats_copy(d, TCA_STATS_RATE_EST, r, sizeof(*r)); 158 159 return 0; 160 } 161 EXPORT_SYMBOL(gnet_stats_copy_rate_est); 162 163 /** 164 * gnet_stats_copy_queue - copy queue statistics into statistics TLV 165 * @d: dumping handle 166 * @q: queue statistics 167 * 168 * Appends the queue statistics to the top level TLV created by 169 * gnet_stats_start_copy(). 170 * 171 * Returns 0 on success or -1 with the statistic lock released 172 * if the room in the socket buffer was not sufficient. 173 */ 174 int 175 gnet_stats_copy_queue(struct gnet_dump *d, struct gnet_stats_queue *q) 176 { 177 if (d->compat_tc_stats) { 178 d->tc_stats.drops = q->drops; 179 d->tc_stats.qlen = q->qlen; 180 d->tc_stats.backlog = q->backlog; 181 d->tc_stats.overlimits = q->overlimits; 182 } 183 184 if (d->tail) 185 return gnet_stats_copy(d, TCA_STATS_QUEUE, q, sizeof(*q)); 186 187 return 0; 188 } 189 EXPORT_SYMBOL(gnet_stats_copy_queue); 190 191 /** 192 * gnet_stats_copy_app - copy application specific statistics into statistics TLV 193 * @d: dumping handle 194 * @st: application specific statistics data 195 * @len: length of data 196 * 197 * Appends the application sepecific statistics to the top level TLV created by 198 * gnet_stats_start_copy() and remembers the data for XSTATS if the dumping 199 * handle is in backward compatibility mode. 200 * 201 * Returns 0 on success or -1 with the statistic lock released 202 * if the room in the socket buffer was not sufficient. 203 */ 204 int 205 gnet_stats_copy_app(struct gnet_dump *d, void *st, int len) 206 { 207 if (d->compat_xstats) { 208 d->xstats = st; 209 d->xstats_len = len; 210 } 211 212 if (d->tail) 213 return gnet_stats_copy(d, TCA_STATS_APP, st, len); 214 215 return 0; 216 } 217 EXPORT_SYMBOL(gnet_stats_copy_app); 218 219 /** 220 * gnet_stats_finish_copy - finish dumping procedure 221 * @d: dumping handle 222 * 223 * Corrects the length of the top level TLV to include all TLVs added 224 * by gnet_stats_copy_XXX() calls. Adds the backward compatibility TLVs 225 * if gnet_stats_start_copy_compat() was used and releases the statistics 226 * lock. 227 * 228 * Returns 0 on success or -1 with the statistic lock released 229 * if the room in the socket buffer was not sufficient. 230 */ 231 int 232 gnet_stats_finish_copy(struct gnet_dump *d) 233 { 234 if (d->tail) 235 d->tail->nla_len = skb_tail_pointer(d->skb) - (u8 *)d->tail; 236 237 if (d->compat_tc_stats) 238 if (gnet_stats_copy(d, d->compat_tc_stats, &d->tc_stats, 239 sizeof(d->tc_stats)) < 0) 240 return -1; 241 242 if (d->compat_xstats && d->xstats) { 243 if (gnet_stats_copy(d, d->compat_xstats, d->xstats, 244 d->xstats_len) < 0) 245 return -1; 246 } 247 248 spin_unlock_bh(d->lock); 249 return 0; 250 } 251 EXPORT_SYMBOL(gnet_stats_finish_copy); 252