I have been trying to optimise my ADSL connections for VOIP. Funny things were happening - for example increasing the ping packet size by 50% had no effect, but then adding one byte had a major effect. It took me a while to figure out that I was seeing the effects of the fixed ATM cell size. This is probably obvious to some of you. For the rest: ADSL uses ATM as its transport. An ATM "packet" is called a cell. A cell has a fixed length of 48 bytes of data, plus 5 bytes of header. If there is not enough data to occupy the cell, padding is added. Thus if you get unlucky there could be up to 47 bytes of padding. This 47 bytes isn't really noticeable for large packets, such as found in Web Traffic, as it is only 3% of the total packet size. It isn't even really noticeable for normal sources of interactive traffic, because typical interactive traffic (eg telnet, ssh & irc) is so low volume, and hence doesn't take up much of your link capacity. Thus in both cases the total percentage of your link capacity devoted to carrying this "padding" (aka wasted bandwidth) is low. In VOIP the situation changes. Firstly, the packets are small, occupying only 2 to 3 cells. Secondly, it saturates the link. Thus if you are doing VOIP, up to 1/3 of your links capacity can be this padding. It is not difficult using tc as it stands to generate a rate table does a fairly good job of estimating the bandwidth used on your ADSL link by large packets, or by small packets of a consistent size. You can't do both. You can play with the figures (overhead and base rate) and get all sorts of trade offs. If you are prepared to overestimate the links carrying capacity at some packet sizes, (a serious error for VOIP), then you can get the worst case error down to around 20%. If you don't want at overestimate link capacity under any circumstances, the worst case error rises to a 40% underestimate. The following patch to tc allows it to perform an exact ATM / ADSL rate calculation. It adds one extra keyword to the "tc class add htb ..." command line: "atm". There isn't a lot of spare bits hanging around to record this, so the patch adds the feature at the expense of always forcing the "overhead" parameter to be even. With the patch, these commands will generate a correct rate table for: PPPoA + VC/Mux: tc class add htb ... overhead 10 atm PPPoA + VC/LLC: tc class add htb ... overhead 18 atm PPPoE + VC/Mux: tc class add htb ... overhead 34 atm PPPoE + VC/LLC: tc class add htb ... overhead 42 atm When using this command lines, you always specify the ADSL link capacity as quoted by the modem. Eg, if you are controlling incoming traffic on a 512k/128k link, you specify the link speed as 512000bps. For those of you running Debian sarge or unstable, you can find a patched version of tc here: http://www.stuart.id.au/russell/files/debian/sarge/iproute diff -Nur iproute-20051007.keep/tc/q_htb.c iproute-20051007/tc/q_htb.c --- iproute-20051007.keep/tc/q_htb.c 2006-03-02 14:50:51.000000000 +1000 +++ iproute-20051007/tc/q_htb.c 2006-03-02 15:50:31.000000000 +1000 @@ -349,6 +349,7 @@ " burst max bytes burst which can be accumulated during idle period {computed}\n" " mpu minimum packet size used in rate computations\n" " overhead per-packet size overhead used in rate computations\n" + " atm include atm cell tax in rate computations\n" " ceil definite upper class rate (no borrows) {rate}\n" " cburst burst but for ceil {computed}\n" @@ -416,7 +417,7 @@ unsigned buffer=0,cbuffer=0; int cell_log=-1,ccell_log = -1; unsigned mtu, mpu; - unsigned char mpu8 = 0, overhead = 0; + unsigned char mpu8 = 0, overhead = 0, atm=0; struct rtattr *tail; memset(&opt, 0, sizeof(opt)); mtu = 1600; /* eth packet len */ @@ -440,9 +441,11 @@ } } else if (matches(*argv, "overhead") == 0) { NEXT_ARG(); - if (get_u8(&overhead, *argv, 10)) { + if (get_u8(&overhead, *argv, 10) || (overhead & 1)) { explain1("overhead"); return -1; } + } else if (matches(*argv, "atm") == 0) { + atm = 1; } else if (matches(*argv, "quantum") == 0) { NEXT_ARG(); if (get_u32(&opt.quantum, *argv, 10)) { @@ -515,7 +518,7 @@ if (!cbuffer) cbuffer = opt.ceil.rate / get_hz() + mtu; /* encode overhead and mpu, 8 bits each, into lower 16 bits */ - mpu = (unsigned)mpu8 | (unsigned)overhead << 8; + mpu = (unsigned)mpu8 | (unsigned)(overhead + atm) << 8; opt.ceil.mpu = mpu; opt.rate.mpu = mpu; if ((cell_log = tc_calc_rtable(opt.rate.rate, rtab, cell_log, mtu, mpu)) < 0) { @@ -575,12 +578,16 @@ sprint_size(buffer, b1), 1<<hopt->rate.cell_log, sprint_size(hopt->rate.mpu&0xFF, b2), - sprint_size((hopt->rate.mpu>>8)&0xFF, b3)); + sprint_size((hopt->rate.mpu>>8)&0xFE, b3)); + if (hopt->rate.mpu & 0x100) + fprintf(f, "atm "); fprintf(f, "cburst %s/%u mpu %s overhead %s ", sprint_size(cbuffer, b1), 1<<hopt->ceil.cell_log, sprint_size(hopt->ceil.mpu&0xFF, b2), - sprint_size((hopt->ceil.mpu>>8)&0xFF, b3)); + sprint_size((hopt->ceil.mpu>>8)&0xFE, b3)); + if (hopt->ceil.mpu & 0x100) + fprintf(f, "atm "); fprintf(f, "level %d ", (int)hopt->level); } else { fprintf(f, "burst %s ", sprint_size(buffer, b1)); diff -Nur iproute-20051007.keep/tc/tc_core.c iproute-20051007/tc/tc_core.c --- iproute-20051007.keep/tc/tc_core.c 2006-03-02 14:50:51.000000000 +1000 +++ iproute-20051007/tc/tc_core.c 2006-03-02 15:48:38.000000000 +1000 @@ -43,6 +43,32 @@ } /* + * Calculate the link layer frame size using into information encoded + * in the mpu. + */ +static unsigned frame_size(unsigned size, unsigned mpu) { + unsigned min_packet_size = mpu & 0xFF; + unsigned overhead = (mpu >> 8) & 0xFE; + unsigned atm_cell_tax = (mpu & 0x100) != 0; + const unsigned atm_header = 5; + const unsigned atm_payload = 48; + + size += overhead; + if (size < min_packet_size) + size = min_packet_size; + if (atm_cell_tax) { + int cells = size / atm_payload; + int tail = size % atm_payload; + if (tail != 0) { + size += atm_payload - tail; + cells += 1; + } + size += atm_header * cells; + } + return size; +} + +/* rtab[pkt_len>>cell_log] = pkt_xmit_time */ @@ -50,23 +76,16 @@ unsigned mpu) { int i; - unsigned overhead = (mpu >> 8) & 0xFF; - mpu = mpu & 0xFF; - - if (mtu == 0) - mtu = 2047; if (cell_log < 0) { + if (mtu == 0) + mtu = 2047; cell_log = 0; while ((mtu>>cell_log) > 255) cell_log++; } for (i=0; i<256; i++) { - unsigned sz = (i<<cell_log); - if (overhead) - sz += overhead; - if (sz < mpu) - sz = mpu; + unsigned sz = frame_size(i<<cell_log, mpu); rtab[i] = tc_core_usec2tick(1000000*((double)sz/bps)); } return cell_log; -- Regards, Russell Stuart _______________________________________________ LARTC mailing list LARTC@xxxxxxxxxxxxxxx http://mailman.ds9a.nl/cgi-bin/mailman/listinfo/lartc