blob: 7c3b2d42061ee97b1d80c89122e1179faaf915f2 [file] [log] [blame]
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +02001/* gprs_codel.cpp
2 *
3 * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
4 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21#include "gprs_codel.h"
22#include "gprs_debug.h"
23
24#include <osmocom/core/utils.h>
Pau Espin Pedrol1de68732020-03-11 14:04:52 +010025#include <osmocom/core/timer_compat.h>
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +020026
27#include <stdint.h>
28#include <stdlib.h>
29#include <math.h>
30
Pau Espin Pedrol1de68732020-03-11 14:04:52 +010031static void control_law(struct gprs_codel *state, struct timespec *delta)
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +020032{
33 /* 256 / sqrt(x), limited to 255 */
34 static uint8_t inv_sqrt_tab[] = {255,
35 255, 181, 147, 128, 114, 104, 96, 90, 85, 80, 77, 73, 71, 68,
36 66, 64, 62, 60, 58, 57, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
37 45, 45, 44, 43, 43, 42, 42, 41, 40, 40, 39, 39, 39, 38, 38, 37,
38 37, 36, 36, 36, 35, 35, 35, 34, 34, 34, 33, 33, 33, 33, 32, 32,
39 32, 32, 31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29, 29, 28,
40 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26,
41 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24,
42 24, 24, 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 22, 22, 22,
43 22, 22, 22, 22, 22, 22, 22, 22, 22, 21, 21, 21, 21, 21, 21, 21,
44 21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
45 20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
46 19, 19, 19, 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, 18, 18, 18,
47 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17,
48 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
49 17, 17, 17, 17
50 };
51 uint_fast32_t delta_usecs;
52 uint_fast32_t inv_sqrt;
53 div_t q;
54
55 if (state->count >= ARRAY_SIZE(inv_sqrt_tab))
56 inv_sqrt = 16;
57 else
58 inv_sqrt = inv_sqrt_tab[state->count];
59
60 /* delta = state->interval / sqrt(count) */
Pau Espin Pedrol1de68732020-03-11 14:04:52 +010061 delta_usecs = state->interval.tv_sec * 1000000 + state->interval.tv_nsec/1000;
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +020062 delta_usecs = delta_usecs * inv_sqrt / 256;
63
64 q = div(delta_usecs, 1000000);
65 delta->tv_sec = q.quot;
Pau Espin Pedrol1de68732020-03-11 14:04:52 +010066 delta->tv_nsec = q.rem * 1000;
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +020067}
68
69void gprs_codel_init(struct gprs_codel *state)
70{
71 static const struct gprs_codel init_state = {0};
72
73 *state = init_state;
74 gprs_codel_set_interval(state, -1);
75 gprs_codel_set_maxpacket(state, -1);
76}
77
78void gprs_codel_set_interval(struct gprs_codel *state, int interval_ms)
79{
80 div_t q;
81
82 if (interval_ms <= 0)
83 interval_ms = GPRS_CODEL_DEFAULT_INTERVAL_MS;
84
85 q = div(interval_ms, 1000);
86 state->interval.tv_sec = q.quot;
Pau Espin Pedrol1de68732020-03-11 14:04:52 +010087 state->interval.tv_nsec = q.rem * 1000000;
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +020088
89 /* target ~ 5% of interval */
90 q = div(interval_ms * 13 / 256, 1000);
91 state->target.tv_sec = q.quot;
Pau Espin Pedrol1de68732020-03-11 14:04:52 +010092 state->target.tv_nsec = q.rem * 1000000;
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +020093}
94
95void gprs_codel_set_maxpacket(struct gprs_codel *state, int maxpacket)
96{
97
98 if (maxpacket < 0)
99 maxpacket = GPRS_CODEL_DEFAULT_MAXPACKET;
100
101 state->maxpacket = maxpacket;
102}
103
104/*
105 * This is an broken up variant of the algorithm being described in
106 * http://queue.acm.org/appendices/codel.html
107 */
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100108int gprs_codel_control(struct gprs_codel *state, const struct timespec *recv,
109 const struct timespec *now, int bytes)
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +0200110{
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100111 struct timespec sojourn_time;
112 struct timespec delta;
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +0200113
114 if (recv == NULL)
115 goto stop_dropping;
116
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100117 timespecsub(now, recv, &sojourn_time);
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +0200118
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100119 if (timespeccmp(&sojourn_time, &state->target, <))
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +0200120 goto stop_dropping;
121
122 if (bytes >= 0 && (unsigned)bytes <= state->maxpacket)
123 goto stop_dropping;
124
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100125 if (!timespecisset(&state->first_above_time)) {
126 timespecadd(now, &state->interval, &state->first_above_time);
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +0200127 goto not_ok_to_drop;
128 }
129
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100130 if (timespeccmp(now, &state->first_above_time, <))
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +0200131 goto not_ok_to_drop;
132
133 /* Ok to drop */
134
135 if (!state->dropping) {
136 int recently = 0;
137 int in_drop_cycle = 0;
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100138 if (timespecisset(&state->drop_next)) {
139 timespecsub(now, &state->drop_next, &delta);
140 in_drop_cycle = timespeccmp(&delta, &state->interval, <);
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +0200141 recently = in_drop_cycle;
142 }
143 if (!recently) {
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100144 timespecsub(now, &state->first_above_time, &delta);
145 recently = !timespeccmp(&delta, &state->interval, <);
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +0200146 };
147 if (!recently)
148 return 0;
149
150 state->dropping = 1;
151
152 if (in_drop_cycle && state->count > 2)
153 state->count -= 2;
154 else
155 state->count = 1;
156
157 state->drop_next = *now;
158 } else {
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100159 if (timespeccmp(now, &state->drop_next, <))
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +0200160 return 0;
161
162 state->count += 1;
163 }
164
165 control_law(state, &delta);
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100166 timespecadd(&state->drop_next, &delta, &state->drop_next);
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +0200167
168#if 1
169 LOGP(DRLCMAC, LOGL_INFO,
170 "CoDel decided to drop packet, window = %d.%03dms, count = %d\n",
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100171 (int)delta.tv_sec, (int)(delta.tv_nsec / 1000000), state->count);
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +0200172#endif
173 return 1;
174
175stop_dropping:
Pau Espin Pedrol1de68732020-03-11 14:04:52 +0100176 timespecclear(&state->first_above_time);
Jacob Erlbeck4f666bc2015-07-20 12:40:42 +0200177not_ok_to_drop:
178 state->dropping = 0;
179 return 0;
180}