Advertisement
Guest User

Untitled

a guest
Jan 24th, 2017
129
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.94 KB | None | 0 0
  1. #ifndef __NET_SCHED_CODEL_H
  2. #define __NET_SCHED_CODEL_H
  3.  
  4. /*
  5. * Codel - The Controlled-Delay Active Queue Management algorithm
  6. *
  7. * Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com>
  8. * Copyright (C) 2011-2012 Van Jacobson <van@pollere.net>
  9. * Copyright (C) 2016 Michael D. Taht <dave.taht@bufferbloat.net>
  10. * Copyright (C) 2012 Eric Dumazet <edumazet@google.com>
  11. * Copyright (C) 2015 Jonathan Morton <chromatix99@gmail.com>
  12. *
  13. * Redistribution and use in source and binary forms, with or without
  14. * modification, are permitted provided that the following conditions
  15. * are met:
  16. * 1. Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions, and the following disclaimer,
  18. * without modification.
  19. * 2. Redistributions in binary form must reproduce the above copyright
  20. * notice, this list of conditions and the following disclaimer in the
  21. * documentation and/or other materials provided with the distribution.
  22. * 3. The names of the authors may not be used to endorse or promote products
  23. * derived from this software without specific prior written permission.
  24. *
  25. * Alternatively, provided that this notice is retained in full, this
  26. * software may be distributed under the terms of the GNU General
  27. * Public License ("GPL") version 2, in which case the provisions of the
  28. * GPL apply INSTEAD OF those given above.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  31. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  32. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  33. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  34. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  36. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  37. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  38. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  39. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  40. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  41. * DAMAGE.
  42. *
  43. */
  44.  
  45. #include <linux/version.h>
  46. #include <linux/types.h>
  47. #include <linux/ktime.h>
  48. #include <linux/skbuff.h>
  49. #include <net/pkt_sched.h>
  50. #include <net/inet_ecn.h>
  51. #include <linux/reciprocal_div.h>
  52.  
  53. /* Controlling Queue Delay (CoDel) algorithm
  54. * =========================================
  55. * Source : Kathleen Nichols and Van Jacobson
  56. * http://queue.acm.org/detail.cfm?id=2209336
  57. *
  58. * Implemented on linux by Dave Taht and Eric Dumazet
  59. */
  60.  
  61. #if KERNEL_VERSION(3, 15, 0) > LINUX_VERSION_CODE
  62. #include "codel5_compat.h"
  63. #else
  64. #define codel_stats_copy_queue(a, b, c, d) gnet_stats_copy_queue(a, b, c, d)
  65. #define codel_watchdog_schedule_ns(a, b, c) qdisc_watchdog_schedule_ns(a, b, c)
  66. #endif
  67.  
  68.  
  69. /* CoDel5 uses a real clock, unlike codel */
  70.  
  71. typedef u64 codel_time_t;
  72. typedef s64 codel_tdiff_t;
  73.  
  74. #define MS2TIME(a) (a * (u64) NSEC_PER_MSEC)
  75. #define US2TIME(a) (a * (u64) NSEC_PER_USEC)
  76.  
  77. static inline codel_time_t codel_get_time(void)
  78. {
  79. return ktime_get_ns();
  80. }
  81.  
  82. /* Qdiscs using codel plugin must use codel_skb_cb in their own cb[] */
  83. struct codel_skb_cb {
  84. codel_time_t enqueue_time;
  85. };
  86.  
  87. static struct codel_skb_cb *get_codel_cb(const struct sk_buff *skb)
  88. {
  89. qdisc_cb_private_validate(skb, sizeof(struct codel_skb_cb));
  90. return (struct codel_skb_cb *)qdisc_skb_cb(skb)->data;
  91. }
  92.  
  93. static codel_time_t codel_get_enqueue_time(const struct sk_buff *skb)
  94. {
  95. return get_codel_cb(skb)->enqueue_time;
  96. }
  97.  
  98. static inline u32 codel_time_to_us(codel_time_t val)
  99. {
  100. do_div(val, NSEC_PER_USEC);
  101. return (u32)val;
  102. }
  103.  
  104. /*
  105. * struct codel_params - contains codel parameters
  106. * @interval: initial drop rate
  107. * @target: maximum persistent sojourn time
  108. * @threshold: tolerance for product of sojourn time and time above target
  109. */
  110. struct codel_params {
  111. codel_time_t interval;
  112. codel_time_t target;
  113. codel_time_t threshold;
  114. };
  115.  
  116. /**
  117. * struct codel_vars - contains codel variables
  118. * @count: how many drops we've done since the last time we
  119. * entered dropping state
  120. * @dropping: set to > 0 if in dropping state
  121. * @rec_inv_sqrt: reciprocal value of sqrt(count) >> 1
  122. * @first_above_time: when we went (or will go) continuously above target
  123. * for interval
  124. * @drop_next: time to drop next packet, or when we dropped last
  125. * @drop_count: temp count of dropped packets in dequeue()
  126. * @ecn_mark: number of packets we ECN marked instead of dropping
  127. */
  128.  
  129. struct codel_vars {
  130. u32 count;
  131. u32 rec_inv_sqrt;
  132. codel_time_t first_above_time;
  133. codel_time_t drop_next;
  134. u16 drop_count;
  135. u16 ecn_mark;
  136. bool dropping;
  137. };
  138. /* sizeof_in_bits(rec_inv_sqrt) */
  139. #define REC_INV_SQRT_BITS (8 * sizeof(u32))
  140. /* needed shift to get a Q0.32 number from rec_inv_sqrt */
  141. #define REC_INV_SQRT_SHIFT (32 - REC_INV_SQRT_BITS)
  142. #define REC_INV_SQRT_CACHE (16)
  143.  
  144. /* Newton approximation method needs more iterations at small inputs,
  145. * so cache them.
  146. */
  147.  
  148. static u32 codel_rec_inv_sqrt_cache[REC_INV_SQRT_CACHE] = {0};
  149.  
  150. static void codel_vars_init(struct codel_vars *vars)
  151. {
  152. memset(vars, 0, sizeof(*vars));
  153. }
  154.  
  155. /*
  156. * http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Iterative_methods_for_reciprocal_square_roots
  157. * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2)
  158. *
  159. * Here, invsqrt is a fixed point number (< 1.0), 32bit mantissa, aka Q0.32
  160. */
  161. static void codel_Newton_step(struct codel_vars *vars)
  162. {
  163. if (vars->count < REC_INV_SQRT_CACHE &&
  164. likely(codel_rec_inv_sqrt_cache[vars->count])) {
  165. vars->rec_inv_sqrt = codel_rec_inv_sqrt_cache[vars->count];
  166. } else {
  167. u32 invsqrt = ((u32)vars->rec_inv_sqrt) << REC_INV_SQRT_SHIFT;
  168. u32 invsqrt2 = ((u64)invsqrt * invsqrt) >> 32;
  169. u64 val = (3LL << 32) - ((u64)vars->count * invsqrt2);
  170.  
  171. val >>= 2; /* avoid overflow in following multiply */
  172. val = (val * invsqrt) >> (32 - 2 + 1);
  173.  
  174. vars->rec_inv_sqrt = val >> REC_INV_SQRT_SHIFT;
  175. }
  176. }
  177.  
  178. static void codel_cache_init(void)
  179. {
  180. struct codel_vars v;
  181.  
  182. codel_vars_init(&v);
  183. v.rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT;
  184. codel_rec_inv_sqrt_cache[0] = v.rec_inv_sqrt;
  185.  
  186. for (v.count = 1; v.count < REC_INV_SQRT_CACHE; v.count++) {
  187. codel_Newton_step(&v);
  188. codel_Newton_step(&v);
  189. codel_Newton_step(&v);
  190. codel_Newton_step(&v);
  191.  
  192. codel_rec_inv_sqrt_cache[v.count] = v.rec_inv_sqrt;
  193. }
  194. }
  195.  
  196. /*
  197. * CoDel control_law is t + interval/sqrt(count)
  198. * We maintain in rec_inv_sqrt the reciprocal value of sqrt(count) to avoid
  199. * both sqrt() and divide operation.
  200. */
  201. static codel_time_t codel_control_law(codel_time_t t,
  202. codel_time_t interval,
  203. u32 rec_inv_sqrt)
  204. {
  205. return t + reciprocal_scale(interval, rec_inv_sqrt <<
  206. REC_INV_SQRT_SHIFT);
  207. }
  208.  
  209.  
  210. static bool codel_should_drop(const struct sk_buff *skb,
  211. struct Qdisc *sch,
  212. struct codel_vars *vars,
  213. const struct codel_params *p,
  214. codel_time_t now,
  215. bool overloaded)
  216. {
  217. if (!skb) {
  218. vars->first_above_time = 0;
  219. return false;
  220. }
  221.  
  222. sch->qstats.backlog -= qdisc_pkt_len(skb);
  223.  
  224. if (now - codel_get_enqueue_time(skb) < p->target ||
  225. !sch->qstats.backlog) {
  226. /* went below - stay below for at least interval */
  227. vars->first_above_time = 0;
  228. return false;
  229. } else if (vars->dropping) {
  230. return true;
  231. }
  232.  
  233. if (vars->first_above_time == 0) {
  234. /* just went above from below; mark the time */
  235. vars->first_above_time = now;
  236. } else if (overloaded) {
  237. /* this flag is set if the pending queue cannot be cleared within interval */
  238. return true;
  239. } else if (vars->count > 1 && now - vars->drop_next < 8 * p->interval) {
  240. /* we were recently dropping; be more aggressive */
  241. return now > codel_control_law(vars->first_above_time,
  242. p->interval,
  243. vars->rec_inv_sqrt);
  244. } else if (now - vars->first_above_time > p->interval) {
  245. return true;
  246. }
  247.  
  248. return false;
  249. }
  250.  
  251. /* Forward declaration of this for use elsewhere */
  252.  
  253. static inline struct sk_buff *custom_dequeue(struct codel_vars *vars,
  254. struct Qdisc *sch);
  255.  
  256. static struct sk_buff *codel_dequeue(struct Qdisc *sch,
  257. struct codel_vars *vars,
  258. struct codel_params *p,
  259. codel_time_t now,
  260. bool overloaded)
  261. {
  262. struct sk_buff *skb = custom_dequeue(vars, sch);
  263. bool drop;
  264.  
  265. if (!skb) {
  266. vars->dropping = false;
  267. return skb;
  268. }
  269. drop = codel_should_drop(skb, sch, vars, p, now, overloaded);
  270. if (vars->dropping) {
  271. if (!drop) {
  272. /* sojourn time below target - leave dropping state */
  273. vars->dropping = false;
  274. } else if (now >= vars->drop_next) {
  275. /* It's time for the next drop. Drop the current
  276. * packet and dequeue the next. The dequeue might
  277. * take us out of dropping state.
  278. * If not, schedule the next drop.
  279. * A large backlog might result in drop rates so high
  280. * that the next drop should happen now,
  281. * hence the while loop.
  282. */
  283.  
  284. /* saturating increment */
  285. vars->count++;
  286. if (!vars->count)
  287. vars->count--;
  288.  
  289. codel_Newton_step(vars);
  290. vars->drop_next = codel_control_law(vars->drop_next,
  291. p->interval,
  292. vars->rec_inv_sqrt);
  293. do {
  294. if (INET_ECN_set_ce(skb)) {
  295. vars->ecn_mark++;
  296. /* and schedule the next drop */
  297. vars->drop_next = codel_control_law(
  298. vars->drop_next, p->interval,
  299. vars->rec_inv_sqrt);
  300. goto end;
  301. }
  302. qdisc_drop(skb, sch);
  303. vars->drop_count++;
  304. skb = custom_dequeue(vars, sch);
  305. if (skb && !codel_should_drop(skb, sch, vars,
  306. p, now, overloaded)) {
  307. /* leave dropping state */
  308. vars->dropping = false;
  309. } else {
  310. /* schedule the next drop */
  311. vars->drop_next = codel_control_law(
  312. vars->drop_next, p->interval,
  313. vars->rec_inv_sqrt);
  314. }
  315. } while (skb && vars->dropping && now >=
  316. vars->drop_next);
  317.  
  318. /* Mark the packet regardless */
  319. if (skb && INET_ECN_set_ce(skb))
  320. vars->ecn_mark++;
  321. }
  322. } else if (drop) {
  323. if (INET_ECN_set_ce(skb)) {
  324. vars->ecn_mark++;
  325. } else {
  326. qdisc_drop(skb, sch);
  327. vars->drop_count++;
  328.  
  329. skb = custom_dequeue(vars, sch);
  330. drop = codel_should_drop(skb, sch, vars, p, now, overloaded);
  331. if (skb && INET_ECN_set_ce(skb))
  332. vars->ecn_mark++;
  333. }
  334. vars->dropping = true;
  335. /* if min went above target close to when we last went below
  336. * assume that the drop rate that controlled the queue on the
  337. * last cycle is a good starting point to control it now.
  338. */
  339. if (vars->count > 2 &&
  340. now - vars->drop_next < 8 * p->interval) {
  341. /* when count is halved, time interval is
  342. * multiplied by 1.414...
  343. */
  344. vars->count /= 2;
  345. vars->rec_inv_sqrt = (vars->rec_inv_sqrt * 92682) >>
  346. 16;
  347. } else {
  348. vars->count = 1;
  349. vars->rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT;
  350. }
  351. codel_Newton_step(vars);
  352. vars->drop_next = codel_control_law(now, p->interval,
  353. vars->rec_inv_sqrt);
  354. }
  355. end:
  356. return skb;
  357. }
  358. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement