0001
0002 #ifndef __NET_SCHED_RED_H
0003 #define __NET_SCHED_RED_H
0004
0005 #include <linux/types.h>
0006 #include <linux/bug.h>
0007 #include <net/pkt_sched.h>
0008 #include <net/inet_ecn.h>
0009 #include <net/dsfield.h>
0010 #include <linux/reciprocal_div.h>
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
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110 #define RED_ONE_PERCENT ((u32)DIV_ROUND_CLOSEST(1ULL<<32, 100))
0111
0112 #define MAX_P_MIN (1 * RED_ONE_PERCENT)
0113 #define MAX_P_MAX (50 * RED_ONE_PERCENT)
0114 #define MAX_P_ALPHA(val) min(MAX_P_MIN, val / 4)
0115
0116 #define RED_STAB_SIZE 256
0117 #define RED_STAB_MASK (RED_STAB_SIZE - 1)
0118
0119 struct red_stats {
0120 u32 prob_drop;
0121 u32 prob_mark;
0122 u32 forced_drop;
0123 u32 forced_mark;
0124 u32 pdrop;
0125 u32 other;
0126 };
0127
0128 struct red_parms {
0129
0130 u32 qth_min;
0131 u32 qth_max;
0132 u32 Scell_max;
0133 u32 max_P;
0134
0135 struct reciprocal_value max_P_reciprocal;
0136 u32 qth_delta;
0137 u32 target_min;
0138 u32 target_max;
0139 u8 Scell_log;
0140 u8 Wlog;
0141 u8 Plog;
0142 u8 Stab[RED_STAB_SIZE];
0143 };
0144
0145 struct red_vars {
0146
0147 int qcount;
0148
0149 u32 qR;
0150
0151 unsigned long qavg;
0152 ktime_t qidlestart;
0153 };
0154
0155 static inline u32 red_maxp(u8 Plog)
0156 {
0157 return Plog < 32 ? (~0U >> Plog) : ~0U;
0158 }
0159
0160 static inline void red_set_vars(struct red_vars *v)
0161 {
0162
0163
0164
0165
0166 v->qavg = 0;
0167
0168 v->qcount = -1;
0169 }
0170
0171 static inline bool red_check_params(u32 qth_min, u32 qth_max, u8 Wlog,
0172 u8 Scell_log, u8 *stab)
0173 {
0174 if (fls(qth_min) + Wlog >= 32)
0175 return false;
0176 if (fls(qth_max) + Wlog >= 32)
0177 return false;
0178 if (Scell_log >= 32)
0179 return false;
0180 if (qth_max < qth_min)
0181 return false;
0182 if (stab) {
0183 int i;
0184
0185 for (i = 0; i < RED_STAB_SIZE; i++)
0186 if (stab[i] >= 32)
0187 return false;
0188 }
0189 return true;
0190 }
0191
0192 static inline int red_get_flags(unsigned char qopt_flags,
0193 unsigned char historic_mask,
0194 struct nlattr *flags_attr,
0195 unsigned char supported_mask,
0196 struct nla_bitfield32 *p_flags,
0197 unsigned char *p_userbits,
0198 struct netlink_ext_ack *extack)
0199 {
0200 struct nla_bitfield32 flags;
0201
0202 if (qopt_flags && flags_attr) {
0203 NL_SET_ERR_MSG_MOD(extack, "flags should be passed either through qopt, or through a dedicated attribute");
0204 return -EINVAL;
0205 }
0206
0207 if (flags_attr) {
0208 flags = nla_get_bitfield32(flags_attr);
0209 } else {
0210 flags.selector = historic_mask;
0211 flags.value = qopt_flags & historic_mask;
0212 }
0213
0214 *p_flags = flags;
0215 *p_userbits = qopt_flags & ~historic_mask;
0216 return 0;
0217 }
0218
0219 static inline int red_validate_flags(unsigned char flags,
0220 struct netlink_ext_ack *extack)
0221 {
0222 if ((flags & TC_RED_NODROP) && !(flags & TC_RED_ECN)) {
0223 NL_SET_ERR_MSG_MOD(extack, "nodrop mode is only meaningful with ECN");
0224 return -EINVAL;
0225 }
0226
0227 return 0;
0228 }
0229
0230 static inline void red_set_parms(struct red_parms *p,
0231 u32 qth_min, u32 qth_max, u8 Wlog, u8 Plog,
0232 u8 Scell_log, u8 *stab, u32 max_P)
0233 {
0234 int delta = qth_max - qth_min;
0235 u32 max_p_delta;
0236
0237 p->qth_min = qth_min << Wlog;
0238 p->qth_max = qth_max << Wlog;
0239 p->Wlog = Wlog;
0240 p->Plog = Plog;
0241 if (delta <= 0)
0242 delta = 1;
0243 p->qth_delta = delta;
0244 if (!max_P) {
0245 max_P = red_maxp(Plog);
0246 max_P *= delta;
0247 }
0248 p->max_P = max_P;
0249 max_p_delta = max_P / delta;
0250 max_p_delta = max(max_p_delta, 1U);
0251 p->max_P_reciprocal = reciprocal_value(max_p_delta);
0252
0253
0254
0255
0256
0257 delta /= 5;
0258 p->target_min = qth_min + 2*delta;
0259 p->target_max = qth_min + 3*delta;
0260
0261 p->Scell_log = Scell_log;
0262 p->Scell_max = (255 << Scell_log);
0263
0264 if (stab)
0265 memcpy(p->Stab, stab, sizeof(p->Stab));
0266 }
0267
0268 static inline int red_is_idling(const struct red_vars *v)
0269 {
0270 return v->qidlestart != 0;
0271 }
0272
0273 static inline void red_start_of_idle_period(struct red_vars *v)
0274 {
0275 v->qidlestart = ktime_get();
0276 }
0277
0278 static inline void red_end_of_idle_period(struct red_vars *v)
0279 {
0280 v->qidlestart = 0;
0281 }
0282
0283 static inline void red_restart(struct red_vars *v)
0284 {
0285 red_end_of_idle_period(v);
0286 v->qavg = 0;
0287 v->qcount = -1;
0288 }
0289
0290 static inline unsigned long red_calc_qavg_from_idle_time(const struct red_parms *p,
0291 const struct red_vars *v)
0292 {
0293 s64 delta = ktime_us_delta(ktime_get(), v->qidlestart);
0294 long us_idle = min_t(s64, delta, p->Scell_max);
0295 int shift;
0296
0297
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312
0313
0314
0315
0316
0317 shift = p->Stab[(us_idle >> p->Scell_log) & RED_STAB_MASK];
0318
0319 if (shift)
0320 return v->qavg >> shift;
0321 else {
0322
0323
0324
0325
0326
0327
0328
0329 us_idle = (v->qavg * (u64)us_idle) >> p->Scell_log;
0330
0331 if (us_idle < (v->qavg >> 1))
0332 return v->qavg - us_idle;
0333 else
0334 return v->qavg >> 1;
0335 }
0336 }
0337
0338 static inline unsigned long red_calc_qavg_no_idle_time(const struct red_parms *p,
0339 const struct red_vars *v,
0340 unsigned int backlog)
0341 {
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351 return v->qavg + (backlog - (v->qavg >> p->Wlog));
0352 }
0353
0354 static inline unsigned long red_calc_qavg(const struct red_parms *p,
0355 const struct red_vars *v,
0356 unsigned int backlog)
0357 {
0358 if (!red_is_idling(v))
0359 return red_calc_qavg_no_idle_time(p, v, backlog);
0360 else
0361 return red_calc_qavg_from_idle_time(p, v);
0362 }
0363
0364
0365 static inline u32 red_random(const struct red_parms *p)
0366 {
0367 return reciprocal_divide(prandom_u32(), p->max_P_reciprocal);
0368 }
0369
0370 static inline int red_mark_probability(const struct red_parms *p,
0371 const struct red_vars *v,
0372 unsigned long qavg)
0373 {
0374
0375
0376
0377
0378
0379
0380
0381
0382
0383
0384
0385
0386
0387
0388
0389
0390 return !(((qavg - p->qth_min) >> p->Wlog) * v->qcount < v->qR);
0391 }
0392
0393 enum {
0394 RED_BELOW_MIN_THRESH,
0395 RED_BETWEEN_TRESH,
0396 RED_ABOVE_MAX_TRESH,
0397 };
0398
0399 static inline int red_cmp_thresh(const struct red_parms *p, unsigned long qavg)
0400 {
0401 if (qavg < p->qth_min)
0402 return RED_BELOW_MIN_THRESH;
0403 else if (qavg >= p->qth_max)
0404 return RED_ABOVE_MAX_TRESH;
0405 else
0406 return RED_BETWEEN_TRESH;
0407 }
0408
0409 enum {
0410 RED_DONT_MARK,
0411 RED_PROB_MARK,
0412 RED_HARD_MARK,
0413 };
0414
0415 static inline int red_action(const struct red_parms *p,
0416 struct red_vars *v,
0417 unsigned long qavg)
0418 {
0419 switch (red_cmp_thresh(p, qavg)) {
0420 case RED_BELOW_MIN_THRESH:
0421 v->qcount = -1;
0422 return RED_DONT_MARK;
0423
0424 case RED_BETWEEN_TRESH:
0425 if (++v->qcount) {
0426 if (red_mark_probability(p, v, qavg)) {
0427 v->qcount = 0;
0428 v->qR = red_random(p);
0429 return RED_PROB_MARK;
0430 }
0431 } else
0432 v->qR = red_random(p);
0433
0434 return RED_DONT_MARK;
0435
0436 case RED_ABOVE_MAX_TRESH:
0437 v->qcount = -1;
0438 return RED_HARD_MARK;
0439 }
0440
0441 BUG();
0442 return RED_DONT_MARK;
0443 }
0444
0445 static inline void red_adaptative_algo(struct red_parms *p, struct red_vars *v)
0446 {
0447 unsigned long qavg;
0448 u32 max_p_delta;
0449
0450 qavg = v->qavg;
0451 if (red_is_idling(v))
0452 qavg = red_calc_qavg_from_idle_time(p, v);
0453
0454
0455 qavg >>= p->Wlog;
0456
0457 if (qavg > p->target_max && p->max_P <= MAX_P_MAX)
0458 p->max_P += MAX_P_ALPHA(p->max_P);
0459 else if (qavg < p->target_min && p->max_P >= MAX_P_MIN)
0460 p->max_P = (p->max_P/10)*9;
0461
0462 max_p_delta = DIV_ROUND_CLOSEST(p->max_P, p->qth_delta);
0463 max_p_delta = max(max_p_delta, 1U);
0464 p->max_P_reciprocal = reciprocal_value(max_p_delta);
0465 }
0466 #endif