0001 #ifndef __NET_SCHED_CODEL_IMPL_H
0002 #define __NET_SCHED_CODEL_IMPL_H
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052 #include <net/inet_ecn.h>
0053
0054 static void codel_params_init(struct codel_params *params)
0055 {
0056 params->interval = MS2TIME(100);
0057 params->target = MS2TIME(5);
0058 params->ce_threshold = CODEL_DISABLED_THRESHOLD;
0059 params->ce_threshold_mask = 0;
0060 params->ce_threshold_selector = 0;
0061 params->ecn = false;
0062 }
0063
0064 static void codel_vars_init(struct codel_vars *vars)
0065 {
0066 memset(vars, 0, sizeof(*vars));
0067 }
0068
0069 static void codel_stats_init(struct codel_stats *stats)
0070 {
0071 stats->maxpacket = 0;
0072 }
0073
0074
0075
0076
0077
0078
0079
0080 static void codel_Newton_step(struct codel_vars *vars)
0081 {
0082 u32 invsqrt = ((u32)vars->rec_inv_sqrt) << REC_INV_SQRT_SHIFT;
0083 u32 invsqrt2 = ((u64)invsqrt * invsqrt) >> 32;
0084 u64 val = (3LL << 32) - ((u64)vars->count * invsqrt2);
0085
0086 val >>= 2;
0087 val = (val * invsqrt) >> (32 - 2 + 1);
0088
0089 vars->rec_inv_sqrt = val >> REC_INV_SQRT_SHIFT;
0090 }
0091
0092
0093
0094
0095
0096
0097 static codel_time_t codel_control_law(codel_time_t t,
0098 codel_time_t interval,
0099 u32 rec_inv_sqrt)
0100 {
0101 return t + reciprocal_scale(interval, rec_inv_sqrt << REC_INV_SQRT_SHIFT);
0102 }
0103
0104 static bool codel_should_drop(const struct sk_buff *skb,
0105 void *ctx,
0106 struct codel_vars *vars,
0107 struct codel_params *params,
0108 struct codel_stats *stats,
0109 codel_skb_len_t skb_len_func,
0110 codel_skb_time_t skb_time_func,
0111 u32 *backlog,
0112 codel_time_t now)
0113 {
0114 bool ok_to_drop;
0115 u32 skb_len;
0116
0117 if (!skb) {
0118 vars->first_above_time = 0;
0119 return false;
0120 }
0121
0122 skb_len = skb_len_func(skb);
0123 vars->ldelay = now - skb_time_func(skb);
0124
0125 if (unlikely(skb_len > stats->maxpacket))
0126 stats->maxpacket = skb_len;
0127
0128 if (codel_time_before(vars->ldelay, params->target) ||
0129 *backlog <= params->mtu) {
0130
0131 vars->first_above_time = 0;
0132 return false;
0133 }
0134 ok_to_drop = false;
0135 if (vars->first_above_time == 0) {
0136
0137
0138
0139 vars->first_above_time = now + params->interval;
0140 } else if (codel_time_after(now, vars->first_above_time)) {
0141 ok_to_drop = true;
0142 }
0143 return ok_to_drop;
0144 }
0145
0146 static struct sk_buff *codel_dequeue(void *ctx,
0147 u32 *backlog,
0148 struct codel_params *params,
0149 struct codel_vars *vars,
0150 struct codel_stats *stats,
0151 codel_skb_len_t skb_len_func,
0152 codel_skb_time_t skb_time_func,
0153 codel_skb_drop_t drop_func,
0154 codel_skb_dequeue_t dequeue_func)
0155 {
0156 struct sk_buff *skb = dequeue_func(vars, ctx);
0157 codel_time_t now;
0158 bool drop;
0159
0160 if (!skb) {
0161 vars->dropping = false;
0162 return skb;
0163 }
0164 now = codel_get_time();
0165 drop = codel_should_drop(skb, ctx, vars, params, stats,
0166 skb_len_func, skb_time_func, backlog, now);
0167 if (vars->dropping) {
0168 if (!drop) {
0169
0170 vars->dropping = false;
0171 } else if (codel_time_after_eq(now, vars->drop_next)) {
0172
0173
0174
0175
0176
0177
0178
0179
0180 while (vars->dropping &&
0181 codel_time_after_eq(now, vars->drop_next)) {
0182 vars->count++;
0183
0184
0185 codel_Newton_step(vars);
0186 if (params->ecn && INET_ECN_set_ce(skb)) {
0187 stats->ecn_mark++;
0188 vars->drop_next =
0189 codel_control_law(vars->drop_next,
0190 params->interval,
0191 vars->rec_inv_sqrt);
0192 goto end;
0193 }
0194 stats->drop_len += skb_len_func(skb);
0195 drop_func(skb, ctx);
0196 stats->drop_count++;
0197 skb = dequeue_func(vars, ctx);
0198 if (!codel_should_drop(skb, ctx,
0199 vars, params, stats,
0200 skb_len_func,
0201 skb_time_func,
0202 backlog, now)) {
0203
0204 vars->dropping = false;
0205 } else {
0206
0207 vars->drop_next =
0208 codel_control_law(vars->drop_next,
0209 params->interval,
0210 vars->rec_inv_sqrt);
0211 }
0212 }
0213 }
0214 } else if (drop) {
0215 u32 delta;
0216
0217 if (params->ecn && INET_ECN_set_ce(skb)) {
0218 stats->ecn_mark++;
0219 } else {
0220 stats->drop_len += skb_len_func(skb);
0221 drop_func(skb, ctx);
0222 stats->drop_count++;
0223
0224 skb = dequeue_func(vars, ctx);
0225 drop = codel_should_drop(skb, ctx, vars, params,
0226 stats, skb_len_func,
0227 skb_time_func, backlog, now);
0228 }
0229 vars->dropping = true;
0230
0231
0232
0233
0234 delta = vars->count - vars->lastcount;
0235 if (delta > 1 &&
0236 codel_time_before(now - vars->drop_next,
0237 16 * params->interval)) {
0238 vars->count = delta;
0239
0240
0241
0242
0243 codel_Newton_step(vars);
0244 } else {
0245 vars->count = 1;
0246 vars->rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT;
0247 }
0248 vars->lastcount = vars->count;
0249 vars->drop_next = codel_control_law(now, params->interval,
0250 vars->rec_inv_sqrt);
0251 }
0252 end:
0253 if (skb && codel_time_after(vars->ldelay, params->ce_threshold)) {
0254 bool set_ce = true;
0255
0256 if (params->ce_threshold_mask) {
0257 int dsfield = skb_get_dsfield(skb);
0258
0259 set_ce = (dsfield >= 0 &&
0260 (((u8)dsfield & params->ce_threshold_mask) ==
0261 params->ce_threshold_selector));
0262 }
0263 if (set_ce && INET_ECN_set_ce(skb))
0264 stats->ce_mark++;
0265 }
0266 return skb;
0267 }
0268
0269 #endif