1 /* 2 * tc_core.c TC core library. 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: Alexey Kuznetsov, <kuznet (at) ms2.inr.ac.ru> 10 * 11 */ 12 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <stdint.h> 16 #include <unistd.h> 17 #include <syslog.h> 18 #include <fcntl.h> 19 #include <math.h> 20 #include <sys/socket.h> 21 #include <netinet/in.h> 22 #include <arpa/inet.h> 23 #include <string.h> 24 25 #include "tc_core.h" 26 #include <linux/atm.h> 27 28 static double tick_in_usec = 1; 29 static double clock_factor = 1; 30 31 int tc_core_time2big(unsigned int time) 32 { 33 __u64 t = time; 34 35 t *= tick_in_usec; 36 return (t >> 32) != 0; 37 } 38 39 40 unsigned int tc_core_time2tick(unsigned int time) 41 { 42 return time*tick_in_usec; 43 } 44 45 unsigned int tc_core_tick2time(unsigned int tick) 46 { 47 return tick/tick_in_usec; 48 } 49 50 unsigned int tc_core_time2ktime(unsigned int time) 51 { 52 return time * clock_factor; 53 } 54 55 unsigned int tc_core_ktime2time(unsigned int ktime) 56 { 57 return ktime / clock_factor; 58 } 59 60 unsigned int tc_calc_xmittime(__u64 rate, unsigned int size) 61 { 62 return tc_core_time2tick(TIME_UNITS_PER_SEC*((double)size/(double)rate)); 63 } 64 65 unsigned int tc_calc_xmitsize(__u64 rate, unsigned int ticks) 66 { 67 return ((double)rate*tc_core_tick2time(ticks))/TIME_UNITS_PER_SEC; 68 } 69 70 /* 71 * The align to ATM cells is used for determining the (ATM) SAR 72 * alignment overhead at the ATM layer. (SAR = Segmentation And 73 * Reassembly). This is for example needed when scheduling packet on 74 * an ADSL connection. Note that the extra ATM-AAL overhead is _not_ 75 * included in this calculation. This overhead is added in the kernel 76 * before doing the rate table lookup, as this gives better precision 77 * (as the table will always be aligned for 48 bytes). 78 * --Hawk, d.7/11-2004. <hawk (at) diku.dk> 79 */ 80 static unsigned int tc_align_to_atm(unsigned int size) 81 { 82 int linksize, cells; 83 84 cells = size / ATM_CELL_PAYLOAD; 85 if ((size % ATM_CELL_PAYLOAD) > 0) 86 cells++; 87 88 linksize = cells * ATM_CELL_SIZE; /* Use full cell size to add ATM tax */ 89 return linksize; 90 } 91 92 static unsigned int tc_adjust_size(unsigned int sz, unsigned int mpu, enum link_layer linklayer) 93 { 94 if (sz < mpu) 95 sz = mpu; 96 97 switch (linklayer) { 98 case LINKLAYER_ATM: 99 return tc_align_to_atm(sz); 100 case LINKLAYER_ETHERNET: 101 default: 102 /* No size adjustments on Ethernet */ 103 return sz; 104 } 105 } 106 107 /* Notice, the rate table calculated here, have gotten replaced in the 108 * kernel and is no-longer used for lookups. 109 * 110 * This happened in kernel release v3.8 caused by kernel 111 * - commit 56b765b79 ("htb: improved accuracy at high rates"). 112 * This change unfortunately caused breakage of tc overhead and 113 * linklayer parameters. 114 * 115 * Kernel overhead handling got fixed in kernel v3.10 by 116 * - commit 01cb71d2d47 (net_sched: restore "overhead xxx" handling) 117 * 118 * Kernel linklayer handling got fixed in kernel v3.11 by 119 * - commit 8a8e3d84b17 (net_sched: restore "linklayer atm" handling) 120 */ 121 122 /* 123 rtab[pkt_len>>cell_log] = pkt_xmit_time 124 */ 125 126 int tc_calc_rtable(struct tc_ratespec *r, __u32 *rtab, 127 int cell_log, unsigned int mtu, 128 enum link_layer linklayer) 129 { 130 int i; 131 unsigned int sz; 132 unsigned int bps = r->rate; 133 unsigned int mpu = r->mpu; 134 135 if (mtu == 0) 136 mtu = 2047; 137 138 if (cell_log < 0) { 139 cell_log = 0; 140 while ((mtu >> cell_log) > 255) 141 cell_log++; 142 } 143 144 for (i = 0; i < 256; i++) { 145 sz = tc_adjust_size((i + 1) << cell_log, mpu, linklayer); 146 rtab[i] = tc_calc_xmittime(bps, sz); 147 } 148 149 r->cell_align = -1; 150 r->cell_log = cell_log; 151 r->linklayer = (linklayer & TC_LINKLAYER_MASK); 152 return cell_log; 153 } 154 155 /* 156 stab[pkt_len>>cell_log] = pkt_xmit_size>>size_log 157 */ 158 159 int tc_calc_size_table(struct tc_sizespec *s, __u16 **stab) 160 { 161 int i; 162 enum link_layer linklayer = s->linklayer; 163 unsigned int sz; 164 165 if (linklayer <= LINKLAYER_ETHERNET && s->mpu == 0) { 166 /* don't need data table in this case (only overhead set) */ 167 s->mtu = 0; 168 s->tsize = 0; 169 s->cell_log = 0; 170 s->cell_align = 0; 171 *stab = NULL; 172 return 0; 173 } 174 175 if (s->mtu == 0) 176 s->mtu = 2047; 177 if (s->tsize == 0) 178 s->tsize = 512; 179 180 s->cell_log = 0; 181 while ((s->mtu >> s->cell_log) > s->tsize - 1) 182 s->cell_log++; 183 184 *stab = malloc(s->tsize * sizeof(__u16)); 185 if (!*stab) 186 return -1; 187 188 again: 189 for (i = s->tsize - 1; i >= 0; i--) { 190 sz = tc_adjust_size((i + 1) << s->cell_log, s->mpu, linklayer); 191 if ((sz >> s->size_log) > UINT16_MAX) { 192 s->size_log++; 193 goto again; 194 } 195 (*stab)[i] = sz >> s->size_log; 196 } 197 198 s->cell_align = -1; /* Due to the sz calc */ 199 return 0; 200 } 201 202 int tc_core_init(void) 203 { 204 FILE *fp; 205 __u32 clock_res; 206 __u32 t2us; 207 __u32 us2t; 208 209 fp = fopen("/proc/net/psched", "r"); 210 if (fp == NULL) 211 return -1; 212 213 if (fscanf(fp, "%08x%08x%08x", &t2us, &us2t, &clock_res) != 3) { 214 fclose(fp); 215 return -1; 216 } 217 fclose(fp); 218 219 /* compatibility hack: for old iproute binaries (ignoring 220 * the kernel clock resolution) the kernel advertises a 221 * tick multiplier of 1000 in case of nano-second resolution, 222 * which really is 1. */ 223 if (clock_res == 1000000000) 224 t2us = us2t; 225 226 clock_factor = (double)clock_res / TIME_UNITS_PER_SEC; 227 tick_in_usec = (double)t2us / us2t * clock_factor; 228 return 0; 229 } 230