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 * 9 * Authors: J Hadi Salim(hadi (at) nortelnetworks.com) 10 * code ruthlessly ripped from 11 * Alexey Kuznetsov, <kuznet (at) ms2.inr.ac.ru> 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> 24 #include <math.h> 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: tc qdisc { add | replace | change } ... gred setup vqs NUMBER\n"); 41 fprintf(stderr, " default DEFAULT_VQ [ grio ] [ limit BYTES ]\n"); 42 fprintf(stderr, " tc qdisc change ... gred vq VQ [ prio VALUE ] limit BYTES\n"); 43 fprintf(stderr, " min BYTES max BYTES avpkt BYTES [ burst PACKETS ]\n"); 44 fprintf(stderr, " [ probability PROBABILITY ] [ bandwidth KBPS ]\n"); 45 } 46 47 static int init_gred(struct qdisc_util *qu, int argc, char **argv, 48 struct nlmsghdr *n) 49 { 50 51 struct rtattr *tail; 52 struct tc_gred_sopt opt = { 0 }; 53 __u32 limit = 0; 54 55 opt.def_DP = MAX_DPs; 56 57 while (argc > 0) { 58 DPRINTF(stderr, "init_gred: invoked with %s\n", *argv); 59 if (strcmp(*argv, "vqs") == 0 || 60 strcmp(*argv, "DPs") == 0) { 61 NEXT_ARG(); 62 if (get_unsigned(&opt.DPs, *argv, 10)) { 63 fprintf(stderr, "Illegal \"vqs\"\n"); 64 return -1; 65 } else if (opt.DPs > MAX_DPs) { 66 fprintf(stderr, "GRED: only %u VQs are currently supported\n", 67 MAX_DPs); 68 return -1; 69 } 70 } else if (strcmp(*argv, "default") == 0) { 71 if (opt.DPs == 0) { 72 fprintf(stderr, "\"default\" must be defined after \"vqs\"\n"); 73 return -1; 74 } 75 NEXT_ARG(); 76 if (get_unsigned(&opt.def_DP, *argv, 10)) { 77 fprintf(stderr, "Illegal \"default\"\n"); 78 return -1; 79 } else if (opt.def_DP >= opt.DPs) { 80 fprintf(stderr, "\"default\" must be less than \"vqs\"\n"); 81 return -1; 82 } 83 } else if (strcmp(*argv, "grio") == 0) { 84 opt.grio = 1; 85 } else if (strcmp(*argv, "limit") == 0) { 86 NEXT_ARG(); 87 if (get_size(&limit, *argv)) { 88 fprintf(stderr, "Illegal \"limit\"\n"); 89 return -1; 90 } 91 } else if (strcmp(*argv, "help") == 0) { 92 explain(); 93 return -1; 94 } else { 95 fprintf(stderr, "What is \"%s\"?\n", *argv); 96 explain(); 97 return -1; 98 } 99 argc--; argv++; 100 } 101 102 if (!opt.DPs || opt.def_DP == MAX_DPs) { 103 fprintf(stderr, "Illegal gred setup parameters\n"); 104 return -1; 105 } 106 107 DPRINTF("TC_GRED: sending DPs=%u def_DP=%u\n", opt.DPs, opt.def_DP); 108 n->nlmsg_flags |= NLM_F_CREATE; 109 tail = NLMSG_TAIL(n); 110 addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); 111 addattr_l(n, 1024, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt)); 112 if (limit) 113 addattr32(n, 1024, TCA_GRED_LIMIT, limit); 114 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; 115 return 0; 116 } 117 /* 118 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 119 */ 120 static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) 121 { 122 int ok = 0; 123 struct tc_gred_qopt opt = { 0 }; 124 unsigned int burst = 0; 125 unsigned int avpkt = 0; 126 double probability = 0.02; 127 unsigned int rate = 0; 128 int parm; 129 __u8 sbuf[256]; 130 struct rtattr *tail; 131 __u32 max_P; 132 133 opt.DP = MAX_DPs; 134 135 while (argc > 0) { 136 if (strcmp(*argv, "limit") == 0) { 137 NEXT_ARG(); 138 if (get_size(&opt.limit, *argv)) { 139 fprintf(stderr, "Illegal \"limit\"\n"); 140 return -1; 141 } 142 ok++; 143 } else if (strcmp(*argv, "setup") == 0) { 144 if (ok) { 145 fprintf(stderr, "Illegal \"setup\"\n"); 146 return -1; 147 } 148 return init_gred(qu, argc-1, argv+1, n); 149 } else if (strcmp(*argv, "min") == 0) { 150 NEXT_ARG(); 151 if (get_size(&opt.qth_min, *argv)) { 152 fprintf(stderr, "Illegal \"min\"\n"); 153 return -1; 154 } 155 ok++; 156 } else if (strcmp(*argv, "max") == 0) { 157 NEXT_ARG(); 158 if (get_size(&opt.qth_max, *argv)) { 159 fprintf(stderr, "Illegal \"max\"\n"); 160 return -1; 161 } 162 ok++; 163 } else if (strcmp(*argv, "vq") == 0 || 164 strcmp(*argv, "DP") == 0) { 165 NEXT_ARG(); 166 if (get_unsigned(&opt.DP, *argv, 10)) { 167 fprintf(stderr, "Illegal \"vq\"\n"); 168 return -1; 169 } else if (opt.DP >= MAX_DPs) { 170 fprintf(stderr, "GRED: only %u VQs are currently supported\n", 171 MAX_DPs); 172 return -1; 173 } /* need a better error check */ 174 ok++; 175 } else if (strcmp(*argv, "burst") == 0) { 176 NEXT_ARG(); 177 if (get_unsigned(&burst, *argv, 0)) { 178 fprintf(stderr, "Illegal \"burst\"\n"); 179 return -1; 180 } 181 ok++; 182 } else if (strcmp(*argv, "avpkt") == 0) { 183 NEXT_ARG(); 184 if (get_size(&avpkt, *argv)) { 185 fprintf(stderr, "Illegal \"avpkt\"\n"); 186 return -1; 187 } 188 ok++; 189 } else if (strcmp(*argv, "probability") == 0) { 190 NEXT_ARG(); 191 if (sscanf(*argv, "%lg", &probability) != 1) { 192 fprintf(stderr, "Illegal \"probability\"\n"); 193 return -1; 194 } 195 ok++; 196 } else if (strcmp(*argv, "prio") == 0) { 197 NEXT_ARG(); 198 opt.prio = strtol(*argv, (char **)NULL, 10); 199 /* some error check here */ 200 ok++; 201 } else if (strcmp(*argv, "bandwidth") == 0) { 202 NEXT_ARG(); 203 if (get_rate(&rate, *argv)) { 204 fprintf(stderr, "Illegal \"bandwidth\"\n"); 205 return -1; 206 } 207 ok++; 208 } else if (strcmp(*argv, "help") == 0) { 209 explain(); 210 return -1; 211 } else { 212 fprintf(stderr, "What is \"%s\"?\n", *argv); 213 explain(); 214 return -1; 215 } 216 argc--; argv++; 217 } 218 219 if (!ok) { 220 explain(); 221 return -1; 222 } 223 if (opt.DP == MAX_DPs || !opt.limit || !opt.qth_min || !opt.qth_max || 224 !avpkt) { 225 fprintf(stderr, "Required parameter (vq, limit, min, max, avpkt) is missing\n"); 226 return -1; 227 } 228 if (!burst) { 229 burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt); 230 fprintf(stderr, "GRED: set burst to %u\n", burst); 231 } 232 if (!rate) { 233 get_rate(&rate, "10Mbit"); 234 fprintf(stderr, "GRED: set bandwidth to 10Mbit\n"); 235 } 236 if ((parm = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { 237 fprintf(stderr, "GRED: failed to calculate EWMA constant.\n"); 238 return -1; 239 } 240 if (parm >= 10) 241 fprintf(stderr, "GRED: WARNING. Burst %u seems to be too large.\n", 242 burst); 243 opt.Wlog = parm; 244 if ((parm = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { 245 fprintf(stderr, "GRED: failed to calculate probability.\n"); 246 return -1; 247 } 248 opt.Plog = parm; 249 if ((parm = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) 250 { 251 fprintf(stderr, "GRED: failed to calculate idle damping table.\n"); 252 return -1; 253 } 254 opt.Scell_log = parm; 255 256 tail = NLMSG_TAIL(n); 257 addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); 258 addattr_l(n, 1024, TCA_GRED_PARMS, &opt, sizeof(opt)); 259 addattr_l(n, 1024, TCA_GRED_STAB, sbuf, 256); 260 max_P = probability * pow(2, 32); 261 addattr32(n, 1024, TCA_GRED_MAX_P, max_P); 262 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; 263 return 0; 264 } 265 266 static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) 267 { 268 struct rtattr *tb[TCA_GRED_MAX + 1]; 269 struct tc_gred_sopt *sopt; 270 struct tc_gred_qopt *qopt; 271 __u32 *max_p = NULL; 272 __u32 *limit = NULL; 273 unsigned int i; 274 275 SPRINT_BUF(b1); 276 SPRINT_BUF(b2); 277 SPRINT_BUF(b3); 278 279 if (opt == NULL) 280 return 0; 281 282 parse_rtattr_nested(tb, TCA_GRED_MAX, opt); 283 284 if (tb[TCA_GRED_PARMS] == NULL) 285 return -1; 286 287 if (tb[TCA_GRED_MAX_P] && 288 RTA_PAYLOAD(tb[TCA_GRED_MAX_P]) >= sizeof(__u32) * MAX_DPs) 289 max_p = RTA_DATA(tb[TCA_GRED_MAX_P]); 290 291 if (tb[TCA_GRED_LIMIT] && 292 RTA_PAYLOAD(tb[TCA_GRED_LIMIT]) == sizeof(__u32)) 293 limit = RTA_DATA(tb[TCA_GRED_LIMIT]); 294 295 sopt = RTA_DATA(tb[TCA_GRED_DPS]); 296 qopt = RTA_DATA(tb[TCA_GRED_PARMS]); 297 if (RTA_PAYLOAD(tb[TCA_GRED_DPS]) < sizeof(*sopt) || 298 RTA_PAYLOAD(tb[TCA_GRED_PARMS]) < sizeof(*qopt)*MAX_DPs) { 299 fprintf(f, "\n GRED received message smaller than expected\n"); 300 return -1; 301 } 302 303 /* Bad hack! should really return a proper message as shown above*/ 304 305 fprintf(f, "vqs %u default %u %s", 306 sopt->DPs, 307 sopt->def_DP, 308 sopt->grio ? "grio " : ""); 309 310 if (limit) 311 fprintf(f, "limit %s ", 312 sprint_size(*limit, b1)); 313 314 for (i = 0; i < MAX_DPs; i++, qopt++) { 315 if (qopt->DP >= MAX_DPs) continue; 316 fprintf(f, "\n vq %u prio %hhu limit %s min %s max %s ", 317 qopt->DP, 318 qopt->prio, 319 sprint_size(qopt->limit, b1), 320 sprint_size(qopt->qth_min, b2), 321 sprint_size(qopt->qth_max, b3)); 322 if (show_details) { 323 fprintf(f, "ewma %u ", qopt->Wlog); 324 if (max_p) 325 fprintf(f, "probability %lg ", max_p[i] / pow(2, 32)); 326 else 327 fprintf(f, "Plog %u ", qopt->Plog); 328 fprintf(f, "Scell_log %u ", qopt->Scell_log); 329 } 330 if (show_stats) { 331 fprintf(f, "\n Queue size: average %s current %s ", 332 sprint_size(qopt->qave, b1), 333 sprint_size(qopt->backlog, b2)); 334 fprintf(f, "\n Dropped packets: forced %u early %u pdrop %u other %u ", 335 qopt->forced, 336 qopt->early, 337 qopt->pdrop, 338 qopt->other); 339 fprintf(f, "\n Total packets: %u (%s) ", 340 qopt->packets, 341 sprint_size(qopt->bytesin, b1)); 342 } 343 } 344 return 0; 345 } 346 347 struct qdisc_util gred_qdisc_util = { 348 .id = "gred", 349 .parse_qopt = gred_parse_opt, 350 .print_qopt = gred_print_opt, 351 }; 352