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