gen_stats.c 9.4 KB

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