1/* 2 * Fair Queue 3 * 4 * Copyright (C) 2013-2015 Eric Dumazet <edumazet@google.com> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions, and the following disclaimer, 11 * without modification. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The names of the authors may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * Alternatively, provided that this notice is retained in full, this 19 * software may be distributed under the terms of the GNU General 20 * Public License ("GPL") version 2, in which case the provisions of the 21 * GPL apply INSTEAD OF those given above. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 34 * DAMAGE. 35 * 36 */ 37 38#include <stdio.h> 39#include <stdlib.h> 40#include <unistd.h> 41#include <syslog.h> 42#include <fcntl.h> 43#include <sys/socket.h> 44#include <netinet/in.h> 45#include <arpa/inet.h> 46#include <string.h> 47#include <stdbool.h> 48 49#include "utils.h" 50#include "tc_util.h" 51 52static void explain(void) 53{ 54 fprintf(stderr, "Usage: ... fq [ limit PACKETS ] [ flow_limit PACKETS ]\n"); 55 fprintf(stderr, " [ quantum BYTES ] [ initial_quantum BYTES ]\n"); 56 fprintf(stderr, " [ maxrate RATE ] [ buckets NUMBER ]\n"); 57 fprintf(stderr, " [ [no]pacing ] [ refill_delay TIME ]\n"); 58 fprintf(stderr, " [ orphan_mask MASK]\n"); 59} 60 61static unsigned int ilog2(unsigned int val) 62{ 63 unsigned int res = 0; 64 65 val--; 66 while (val) { 67 res++; 68 val >>= 1; 69 } 70 return res; 71} 72 73static int fq_parse_opt(struct qdisc_util *qu, int argc, char **argv, 74 struct nlmsghdr *n) 75{ 76 unsigned int plimit; 77 unsigned int flow_plimit; 78 unsigned int quantum; 79 unsigned int initial_quantum; 80 unsigned int buckets = 0; 81 unsigned int maxrate; 82 unsigned int defrate; 83 unsigned int refill_delay; 84 unsigned int orphan_mask; 85 bool set_plimit = false; 86 bool set_flow_plimit = false; 87 bool set_quantum = false; 88 bool set_initial_quantum = false; 89 bool set_maxrate = false; 90 bool set_defrate = false; 91 bool set_refill_delay = false; 92 bool set_orphan_mask = false; 93 int pacing = -1; 94 struct rtattr *tail; 95 96 while (argc > 0) { 97 if (strcmp(*argv, "limit") == 0) { 98 NEXT_ARG(); 99 if (get_unsigned(&plimit, *argv, 0)) { 100 fprintf(stderr, "Illegal \"limit\"\n"); 101 return -1; 102 } 103 set_plimit = true; 104 } else if (strcmp(*argv, "flow_limit") == 0) { 105 NEXT_ARG(); 106 if (get_unsigned(&flow_plimit, *argv, 0)) { 107 fprintf(stderr, "Illegal \"flow_limit\"\n"); 108 return -1; 109 } 110 set_flow_plimit = true; 111 } else if (strcmp(*argv, "buckets") == 0) { 112 NEXT_ARG(); 113 if (get_unsigned(&buckets, *argv, 0)) { 114 fprintf(stderr, "Illegal \"buckets\"\n"); 115 return -1; 116 } 117 } else if (strcmp(*argv, "maxrate") == 0) { 118 NEXT_ARG(); 119 if (get_rate(&maxrate, *argv)) { 120 fprintf(stderr, "Illegal \"maxrate\"\n"); 121 return -1; 122 } 123 set_maxrate = true; 124 } else if (strcmp(*argv, "defrate") == 0) { 125 NEXT_ARG(); 126 if (get_rate(&defrate, *argv)) { 127 fprintf(stderr, "Illegal \"defrate\"\n"); 128 return -1; 129 } 130 set_defrate = true; 131 } else if (strcmp(*argv, "quantum") == 0) { 132 NEXT_ARG(); 133 if (get_unsigned(&quantum, *argv, 0)) { 134 fprintf(stderr, "Illegal \"quantum\"\n"); 135 return -1; 136 } 137 set_quantum = true; 138 } else if (strcmp(*argv, "initial_quantum") == 0) { 139 NEXT_ARG(); 140 if (get_unsigned(&initial_quantum, *argv, 0)) { 141 fprintf(stderr, "Illegal \"initial_quantum\"\n"); 142 return -1; 143 } 144 set_initial_quantum = true; 145 } else if (strcmp(*argv, "orphan_mask") == 0) { 146 NEXT_ARG(); 147 if (get_unsigned(&orphan_mask, *argv, 0)) { 148 fprintf(stderr, "Illegal \"initial_quantum\"\n"); 149 return -1; 150 } 151 set_orphan_mask = true; 152 } else if (strcmp(*argv, "refill_delay") == 0) { 153 NEXT_ARG(); 154 if (get_time(&refill_delay, *argv)) { 155 fprintf(stderr, "Illegal \"refill_delay\"\n"); 156 return -1; 157 } 158 set_refill_delay = true; 159 } else if (strcmp(*argv, "pacing") == 0) { 160 pacing = 1; 161 } else if (strcmp(*argv, "nopacing") == 0) { 162 pacing = 0; 163 } else if (strcmp(*argv, "help") == 0) { 164 explain(); 165 return -1; 166 } else { 167 fprintf(stderr, "What is \"%s\"?\n", *argv); 168 explain(); 169 return -1; 170 } 171 argc--; argv++; 172 } 173 174 tail = NLMSG_TAIL(n); 175 addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); 176 if (buckets) { 177 unsigned int log = ilog2(buckets); 178 179 addattr_l(n, 1024, TCA_FQ_BUCKETS_LOG, 180 &log, sizeof(log)); 181 } 182 if (set_plimit) 183 addattr_l(n, 1024, TCA_FQ_PLIMIT, 184 &plimit, sizeof(plimit)); 185 if (set_flow_plimit) 186 addattr_l(n, 1024, TCA_FQ_FLOW_PLIMIT, 187 &flow_plimit, sizeof(flow_plimit)); 188 if (set_quantum) 189 addattr_l(n, 1024, TCA_FQ_QUANTUM, &quantum, sizeof(quantum)); 190 if (set_initial_quantum) 191 addattr_l(n, 1024, TCA_FQ_INITIAL_QUANTUM, 192 &initial_quantum, sizeof(initial_quantum)); 193 if (pacing != -1) 194 addattr_l(n, 1024, TCA_FQ_RATE_ENABLE, 195 &pacing, sizeof(pacing)); 196 if (set_maxrate) 197 addattr_l(n, 1024, TCA_FQ_FLOW_MAX_RATE, 198 &maxrate, sizeof(maxrate)); 199 if (set_defrate) 200 addattr_l(n, 1024, TCA_FQ_FLOW_DEFAULT_RATE, 201 &defrate, sizeof(defrate)); 202 if (set_refill_delay) 203 addattr_l(n, 1024, TCA_FQ_FLOW_REFILL_DELAY, 204 &refill_delay, sizeof(refill_delay)); 205 if (set_orphan_mask) 206 addattr_l(n, 1024, TCA_FQ_ORPHAN_MASK, 207 &orphan_mask, sizeof(refill_delay)); 208 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; 209 return 0; 210} 211 212static int fq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) 213{ 214 struct rtattr *tb[TCA_FQ_MAX + 1]; 215 unsigned int plimit, flow_plimit; 216 unsigned int buckets_log; 217 int pacing; 218 unsigned int rate, quantum; 219 unsigned int refill_delay; 220 unsigned int orphan_mask; 221 SPRINT_BUF(b1); 222 223 if (opt == NULL) 224 return 0; 225 226 parse_rtattr_nested(tb, TCA_FQ_MAX, opt); 227 228 if (tb[TCA_FQ_PLIMIT] && 229 RTA_PAYLOAD(tb[TCA_FQ_PLIMIT]) >= sizeof(__u32)) { 230 plimit = rta_getattr_u32(tb[TCA_FQ_PLIMIT]); 231 fprintf(f, "limit %up ", plimit); 232 } 233 if (tb[TCA_FQ_FLOW_PLIMIT] && 234 RTA_PAYLOAD(tb[TCA_FQ_FLOW_PLIMIT]) >= sizeof(__u32)) { 235 flow_plimit = rta_getattr_u32(tb[TCA_FQ_FLOW_PLIMIT]); 236 fprintf(f, "flow_limit %up ", flow_plimit); 237 } 238 if (tb[TCA_FQ_BUCKETS_LOG] && 239 RTA_PAYLOAD(tb[TCA_FQ_BUCKETS_LOG]) >= sizeof(__u32)) { 240 buckets_log = rta_getattr_u32(tb[TCA_FQ_BUCKETS_LOG]); 241 fprintf(f, "buckets %u ", 1U << buckets_log); 242 } 243 if (tb[TCA_FQ_ORPHAN_MASK] && 244 RTA_PAYLOAD(tb[TCA_FQ_ORPHAN_MASK]) >= sizeof(__u32)) { 245 orphan_mask = rta_getattr_u32(tb[TCA_FQ_ORPHAN_MASK]); 246 fprintf(f, "orphan_mask %u ", orphan_mask); 247 } 248 if (tb[TCA_FQ_RATE_ENABLE] && 249 RTA_PAYLOAD(tb[TCA_FQ_RATE_ENABLE]) >= sizeof(int)) { 250 pacing = rta_getattr_u32(tb[TCA_FQ_RATE_ENABLE]); 251 if (pacing == 0) 252 fprintf(f, "nopacing "); 253 } 254 if (tb[TCA_FQ_QUANTUM] && 255 RTA_PAYLOAD(tb[TCA_FQ_QUANTUM]) >= sizeof(__u32)) { 256 quantum = rta_getattr_u32(tb[TCA_FQ_QUANTUM]); 257 fprintf(f, "quantum %u ", quantum); 258 } 259 if (tb[TCA_FQ_INITIAL_QUANTUM] && 260 RTA_PAYLOAD(tb[TCA_FQ_INITIAL_QUANTUM]) >= sizeof(__u32)) { 261 quantum = rta_getattr_u32(tb[TCA_FQ_INITIAL_QUANTUM]); 262 fprintf(f, "initial_quantum %u ", quantum); 263 } 264 if (tb[TCA_FQ_FLOW_MAX_RATE] && 265 RTA_PAYLOAD(tb[TCA_FQ_FLOW_MAX_RATE]) >= sizeof(__u32)) { 266 rate = rta_getattr_u32(tb[TCA_FQ_FLOW_MAX_RATE]); 267 268 if (rate != ~0U) 269 fprintf(f, "maxrate %s ", sprint_rate(rate, b1)); 270 } 271 if (tb[TCA_FQ_FLOW_DEFAULT_RATE] && 272 RTA_PAYLOAD(tb[TCA_FQ_FLOW_DEFAULT_RATE]) >= sizeof(__u32)) { 273 rate = rta_getattr_u32(tb[TCA_FQ_FLOW_DEFAULT_RATE]); 274 275 if (rate != 0) 276 fprintf(f, "defrate %s ", sprint_rate(rate, b1)); 277 } 278 if (tb[TCA_FQ_FLOW_REFILL_DELAY] && 279 RTA_PAYLOAD(tb[TCA_FQ_FLOW_REFILL_DELAY]) >= sizeof(__u32)) { 280 refill_delay = rta_getattr_u32(tb[TCA_FQ_FLOW_REFILL_DELAY]); 281 fprintf(f, "refill_delay %s ", sprint_time(refill_delay, b1)); 282 } 283 284 return 0; 285} 286 287static int fq_print_xstats(struct qdisc_util *qu, FILE *f, 288 struct rtattr *xstats) 289{ 290 struct tc_fq_qd_stats *st; 291 292 if (xstats == NULL) 293 return 0; 294 295 if (RTA_PAYLOAD(xstats) < sizeof(*st)) 296 return -1; 297 298 st = RTA_DATA(xstats); 299 300 fprintf(f, " %u flows (%u inactive, %u throttled)", 301 st->flows, st->inactive_flows, st->throttled_flows); 302 303 if (st->time_next_delayed_flow > 0) 304 fprintf(f, ", next packet delay %llu ns", st->time_next_delayed_flow); 305 306 fprintf(f, "\n %llu gc, %llu highprio", 307 st->gc_flows, st->highprio_packets); 308 309 if (st->tcp_retrans) 310 fprintf(f, ", %llu retrans", st->tcp_retrans); 311 312 fprintf(f, ", %llu throttled", st->throttled); 313 314 if (st->flows_plimit) 315 fprintf(f, ", %llu flows_plimit", st->flows_plimit); 316 317 if (st->pkts_too_long || st->allocation_errors) 318 fprintf(f, "\n %llu too long pkts, %llu alloc errors\n", 319 st->pkts_too_long, st->allocation_errors); 320 321 return 0; 322} 323 324struct qdisc_util fq_qdisc_util = { 325 .id = "fq", 326 .parse_qopt = fq_parse_opt, 327 .print_qopt = fq_print_opt, 328 .print_xstats = fq_print_xstats, 329}; 330