]>
Commit | Line | Data |
---|---|---|
aba5acdf SH |
1 | /* |
2 | * q_gred.c GRED. | |
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 | * | |
ae665a52 SH |
9 | * Authors: J Hadi Salim(hadi@nortelnetworks.com) |
10 | * code ruthlessly ripped from | |
11 | * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | |
aba5acdf SH |
12 | * |
13 | */ | |
14 | ||
15 | #include <stdio.h> | |
16 | #include <stdlib.h> | |
17 | #include <unistd.h> | |
18 | #include <syslog.h> | |
19 | #include <fcntl.h> | |
20 | #include <sys/socket.h> | |
21 | #include <netinet/in.h> | |
22 | #include <arpa/inet.h> | |
23 | #include <string.h> | |
1b6f0bb5 | 24 | #include <math.h> |
aba5acdf SH |
25 | |
26 | #include "utils.h" | |
27 | #include "tc_util.h" | |
28 | ||
29 | #include "tc_red.h" | |
30 | ||
31 | ||
32 | #if 0 | |
33 | #define DPRINTF(format,args...) fprintf(stderr,format,##args) | |
34 | #else | |
35 | #define DPRINTF(format,args...) | |
36 | #endif | |
37 | ||
38 | static void explain(void) | |
39 | { | |
40 | fprintf(stderr, "Usage: ... gred DP drop-probability limit BYTES " | |
41 | "min BYTES max BYTES\n"); | |
42 | fprintf(stderr, " avpkt BYTES burst PACKETS probability PROBABILITY " | |
43 | "bandwidth KBPS\n"); | |
44 | fprintf(stderr, " [prio value]\n"); | |
45 | fprintf(stderr," OR ...\n"); | |
46 | fprintf(stderr," gred setup DPs <num of DPs> default <default DP> " | |
47 | "[grio]\n"); | |
48 | } | |
49 | ||
3d0b7439 | 50 | static int init_gred(struct qdisc_util *qu, int argc, char **argv, |
ebde8780 | 51 | struct nlmsghdr *n) |
aba5acdf SH |
52 | { |
53 | ||
54 | struct rtattr *tail; | |
cb4bd0ec | 55 | struct tc_gred_sopt opt = { 0 }; |
ebde8780 SH |
56 | int dps = 0; |
57 | int def_dp = -1; | |
aba5acdf SH |
58 | |
59 | while (argc > 0) { | |
60 | DPRINTF(stderr,"init_gred: invoked with %s\n",*argv); | |
61 | if (strcmp(*argv, "DPs") == 0) { | |
62 | NEXT_ARG(); | |
63 | DPRINTF(stderr,"init_gred: next_arg with %s\n",*argv); | |
ebde8780 SH |
64 | dps = strtol(*argv, (char **)NULL, 10); |
65 | if (dps < 0 || dps >MAX_DPs) { | |
66 | fprintf(stderr, "DPs =%d\n", dps); | |
aba5acdf SH |
67 | fprintf(stderr, "Illegal \"DPs\"\n"); |
68 | fprintf(stderr, "GRED: only %d DPs are " | |
ebde8780 | 69 | "currently supported\n",MAX_DPs); |
aba5acdf SH |
70 | return -1; |
71 | } | |
72 | } else if (strcmp(*argv, "default") == 0) { | |
73 | NEXT_ARG(); | |
ebde8780 | 74 | def_dp = strtol(*argv, (char **)NULL, 10); |
1558971d | 75 | if (dps == 0) { |
aba5acdf | 76 | fprintf(stderr, "\"default DP\" must be " |
ebde8780 | 77 | "defined after DPs\n"); |
aba5acdf SH |
78 | return -1; |
79 | } | |
ebde8780 | 80 | if (def_dp < 0 || def_dp > dps) { |
3d0b7439 | 81 | fprintf(stderr, |
ebde8780 SH |
82 | "\"default DP\" must be less than %d\n", |
83 | opt.DPs); | |
aba5acdf SH |
84 | return -1; |
85 | } | |
86 | } else if (strcmp(*argv, "grio") == 0) { | |
cb4bd0ec | 87 | opt.grio = 1; |
aba5acdf SH |
88 | } else if (strcmp(*argv, "help") == 0) { |
89 | explain(); | |
90 | return -1; | |
91 | } else { | |
92 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
93 | explain(); | |
94 | return -1; | |
95 | } | |
96 | argc--; argv++; | |
ebde8780 | 97 | } |
aba5acdf | 98 | |
ebde8780 SH |
99 | if (!dps || def_dp == -1) { |
100 | fprintf(stderr, "Illegal gred setup parameters \n"); | |
101 | return -1; | |
102 | } | |
103 | ||
ebde8780 SH |
104 | opt.DPs = dps; |
105 | opt.def_DP = def_dp; | |
106 | ||
107 | DPRINTF("TC_GRED: sending DPs=%d default=%d\n",opt.DPs,opt.def_DP); | |
aba5acdf | 108 | n->nlmsg_flags|=NLM_F_CREATE; |
228569c3 | 109 | tail = NLMSG_TAIL(n); |
aba5acdf SH |
110 | addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); |
111 | addattr_l(n, 1024, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt)); | |
228569c3 | 112 | tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; |
ebde8780 | 113 | return 0; |
aba5acdf SH |
114 | } |
115 | /* | |
116 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
117 | */ | |
118 | static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) | |
119 | { | |
120 | int ok=0; | |
121 | struct tc_gred_qopt opt; | |
122 | unsigned burst = 0; | |
123 | unsigned avpkt = 0; | |
124 | double probability = 0.02; | |
125 | unsigned rate = 0; | |
126 | int wlog; | |
127 | __u8 sbuf[256]; | |
128 | struct rtattr *tail; | |
1b6f0bb5 | 129 | __u32 max_P; |
aba5acdf SH |
130 | |
131 | memset(&opt, 0, sizeof(opt)); | |
132 | ||
133 | while (argc > 0) { | |
134 | if (strcmp(*argv, "limit") == 0) { | |
135 | NEXT_ARG(); | |
136 | if (get_size(&opt.limit, *argv)) { | |
137 | fprintf(stderr, "Illegal \"limit\"\n"); | |
138 | return -1; | |
139 | } | |
140 | ok++; | |
141 | } else if (strcmp(*argv, "setup") == 0) { | |
142 | if (ok) { | |
143 | fprintf(stderr, "Illegal \"setup\"\n"); | |
144 | return -1; | |
145 | } | |
146 | return init_gred(qu,argc-1, argv+1,n); | |
ae665a52 | 147 | |
aba5acdf SH |
148 | } else if (strcmp(*argv, "min") == 0) { |
149 | NEXT_ARG(); | |
150 | if (get_size(&opt.qth_min, *argv)) { | |
151 | fprintf(stderr, "Illegal \"min\"\n"); | |
152 | return -1; | |
153 | } | |
154 | ok++; | |
155 | } else if (strcmp(*argv, "max") == 0) { | |
156 | NEXT_ARG(); | |
157 | if (get_size(&opt.qth_max, *argv)) { | |
158 | fprintf(stderr, "Illegal \"max\"\n"); | |
159 | return -1; | |
160 | } | |
161 | ok++; | |
162 | } else if (strcmp(*argv, "DP") == 0) { | |
163 | NEXT_ARG(); | |
164 | opt.DP=strtol(*argv, (char **)NULL, 10); | |
165 | DPRINTF ("\n ******* DP =%u\n",opt.DP); | |
166 | if (opt.DP >MAX_DPs) { /* need a better error check */ | |
167 | fprintf(stderr, "DP =%u \n",opt.DP); | |
168 | fprintf(stderr, "Illegal \"DP\"\n"); | |
169 | fprintf(stderr, "GRED: only %d DPs are currently supported\n",MAX_DPs); | |
170 | return -1; | |
171 | } | |
aba5acdf SH |
172 | ok++; |
173 | } else if (strcmp(*argv, "burst") == 0) { | |
174 | NEXT_ARG(); | |
175 | if (get_unsigned(&burst, *argv, 0)) { | |
176 | fprintf(stderr, "Illegal \"burst\"\n"); | |
177 | return -1; | |
178 | } | |
179 | ok++; | |
180 | } else if (strcmp(*argv, "avpkt") == 0) { | |
181 | NEXT_ARG(); | |
182 | if (get_size(&avpkt, *argv)) { | |
183 | fprintf(stderr, "Illegal \"avpkt\"\n"); | |
184 | return -1; | |
185 | } | |
186 | ok++; | |
187 | } else if (strcmp(*argv, "probability") == 0) { | |
188 | NEXT_ARG(); | |
189 | if (sscanf(*argv, "%lg", &probability) != 1) { | |
190 | fprintf(stderr, "Illegal \"probability\"\n"); | |
191 | return -1; | |
192 | } | |
193 | ok++; | |
194 | } else if (strcmp(*argv, "prio") == 0) { | |
195 | NEXT_ARG(); | |
196 | opt.prio=strtol(*argv, (char **)NULL, 10); | |
197 | /* some error check here */ | |
198 | ok++; | |
199 | } else if (strcmp(*argv, "bandwidth") == 0) { | |
200 | NEXT_ARG(); | |
201 | if (get_rate(&rate, *argv)) { | |
202 | fprintf(stderr, "Illegal \"bandwidth\"\n"); | |
203 | return -1; | |
204 | } | |
205 | ok++; | |
206 | } else if (strcmp(*argv, "help") == 0) { | |
207 | explain(); | |
208 | return -1; | |
209 | } else { | |
210 | fprintf(stderr, "What is \"%s\"?\n", *argv); | |
211 | explain(); | |
212 | return -1; | |
213 | } | |
214 | argc--; argv++; | |
215 | } | |
216 | ||
aba5acdf SH |
217 | if (rate == 0) |
218 | get_rate(&rate, "10Mbit"); | |
219 | ||
ab15aeac | 220 | if (!opt.qth_min || !opt.qth_max || !opt.limit || !avpkt || |
aba5acdf | 221 | (opt.DP<0)) { |
ab15aeac | 222 | fprintf(stderr, "Required parameter (min, max, limit, " |
ddf216c8 | 223 | "avpkt, DP) is missing\n"); |
aba5acdf SH |
224 | return -1; |
225 | } | |
ab15aeac ED |
226 | if (!burst) { |
227 | burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt); | |
228 | fprintf(stderr, "GRED: set burst to %u\n", burst); | |
229 | } | |
aba5acdf SH |
230 | |
231 | if ((wlog = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { | |
232 | fprintf(stderr, "GRED: failed to calculate EWMA constant.\n"); | |
233 | return -1; | |
234 | } | |
235 | if (wlog >= 10) | |
ab15aeac | 236 | fprintf(stderr, "GRED: WARNING. Burst %d seems to be too " |
aba5acdf SH |
237 | "large.\n", burst); |
238 | opt.Wlog = wlog; | |
239 | if ((wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { | |
240 | fprintf(stderr, "GRED: failed to calculate probability.\n"); | |
241 | return -1; | |
242 | } | |
243 | opt.Plog = wlog; | |
244 | if ((wlog = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) | |
245 | { | |
246 | fprintf(stderr, "GRED: failed to calculate idle damping " | |
247 | "table.\n"); | |
248 | return -1; | |
249 | } | |
250 | opt.Scell_log = wlog; | |
251 | ||
228569c3 | 252 | tail = NLMSG_TAIL(n); |
aba5acdf SH |
253 | addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); |
254 | addattr_l(n, 1024, TCA_GRED_PARMS, &opt, sizeof(opt)); | |
255 | addattr_l(n, 1024, TCA_GRED_STAB, sbuf, 256); | |
1b6f0bb5 ED |
256 | max_P = probability * pow(2, 32); |
257 | addattr32(n, 1024, TCA_GRED_MAX_P, max_P); | |
228569c3 | 258 | tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; |
aba5acdf SH |
259 | return 0; |
260 | } | |
261 | ||
262 | static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) | |
263 | { | |
1b6f0bb5 | 264 | struct rtattr *tb[TCA_GRED_MAX + 1]; |
aba5acdf | 265 | struct tc_gred_qopt *qopt; |
1b6f0bb5 | 266 | __u32 *max_p = NULL; |
aba5acdf SH |
267 | int i; |
268 | SPRINT_BUF(b1); | |
269 | SPRINT_BUF(b2); | |
270 | SPRINT_BUF(b3); | |
271 | SPRINT_BUF(b4); | |
272 | SPRINT_BUF(b5); | |
273 | ||
274 | if (opt == NULL) | |
275 | return 0; | |
276 | ||
1b6f0bb5 | 277 | parse_rtattr_nested(tb, TCA_GRED_MAX, opt); |
aba5acdf SH |
278 | |
279 | if (tb[TCA_GRED_PARMS] == NULL) | |
280 | return -1; | |
a5a6f1e8 | 281 | |
1b6f0bb5 ED |
282 | if (tb[TCA_GRED_MAX_P] && |
283 | RTA_PAYLOAD(tb[TCA_GRED_MAX_P]) >= sizeof(__u32) * MAX_DPs) | |
284 | max_p = RTA_DATA(tb[TCA_GRED_MAX_P]); | |
285 | ||
aba5acdf SH |
286 | qopt = RTA_DATA(tb[TCA_GRED_PARMS]); |
287 | if (RTA_PAYLOAD(tb[TCA_GRED_PARMS]) < sizeof(*qopt)*MAX_DPs) { | |
288 | fprintf(f,"\n GRED received message smaller than expected\n"); | |
289 | return -1; | |
290 | } | |
ae665a52 | 291 | |
aba5acdf SH |
292 | /* Bad hack! should really return a proper message as shown above*/ |
293 | ||
294 | for (i=0;i<MAX_DPs;i++, qopt++) { | |
295 | if (qopt->DP >= MAX_DPs) continue; | |
296 | fprintf(f, "\n DP:%d (prio %d) Average Queue %s Measured " | |
297 | "Queue %s ", | |
298 | qopt->DP, | |
299 | qopt->prio, | |
300 | sprint_size(qopt->qave, b4), | |
301 | sprint_size(qopt->backlog, b5)); | |
302 | fprintf(f, "\n\t Packet drops: %d (forced %d early %d) ", | |
303 | qopt->forced+qopt->early, | |
304 | qopt->forced, | |
305 | qopt->early); | |
306 | fprintf(f, "\n\t Packet totals: %u (bytes %u) ", | |
307 | qopt->packets, | |
308 | qopt->bytesin); | |
309 | if (show_details) | |
310 | fprintf(f, "\n limit %s min %s max %s ", | |
311 | sprint_size(qopt->limit, b1), | |
312 | sprint_size(qopt->qth_min, b2), | |
313 | sprint_size(qopt->qth_max, b3)); | |
1b6f0bb5 ED |
314 | fprintf(f, "ewma %u ", qopt->Wlog); |
315 | if (max_p) | |
316 | fprintf(f, "probability %lg ", max_p[i] / pow(2, 32)); | |
317 | else | |
318 | fprintf(f, "Plog %u ", qopt->Plog); | |
319 | fprintf(f, "Scell_log %u", qopt->Scell_log); | |
aba5acdf SH |
320 | } |
321 | return 0; | |
322 | } | |
323 | ||
95812b56 | 324 | struct qdisc_util gred_qdisc_util = { |
f2f99e2e SH |
325 | .id = "gred", |
326 | .parse_qopt = gred_parse_opt, | |
327 | .print_qopt = gred_print_opt, | |
aba5acdf | 328 | }; |