Date: Fri, 4 Aug 2000 17:31:44 -0400 (EDT) From: Garrett Wollman <wollman@khavrinen.lcs.mit.edu> To: bmah@cisco.com Cc: freebsd-net@FreeBSD.ORG Subject: Re: cvs commit: src/sys/netinet tcp_output.c Message-ID: <200008042131.RAA33779@khavrinen.lcs.mit.edu> In-Reply-To: <200008042107.e74L7Tu13995@bmah-freebsd-0.cisco.com> References: <200008042014.QAA05452@cholla.INRS-Telecom.UQuebec.CA> <200008042107.e74L7Tu13995@bmah-freebsd-0.cisco.com>
next in thread | previous in thread | raw e-mail | index | archive | help
<<On Fri, 04 Aug 2000 14:07:29 -0700, bmah@cisco.com (Bruce A. Mah) said:
> A RED queue would drop a random packet from the full queue.
Actually, no. A RED queue would drop a packet as it is enqueued, at
random intervals which depend on the current length of the queue.
Many people get this wrong when the idea is first explained to them; I
know I did. Read the paper for a full explanation of how this works.[1]
> If you can get those flows to back off (e.g. TCP congestion
> control),
Specifically, I think the problem with Archie's patch is that it might
result in TCP not backing off at all. RED doesn't deal well with
unresponsive flows.
-GAWollman
[1] In essence, when the average length x [2] exceeds threshold
th_min, start dropping packets at probability proportional to (x -
th_min). When x exceeds threshold th_max, drop all packets. There
are good mathematical tricks to make this very efficient for packets
which are not dropped.[3]
[2] As computed using a low-pass filter (exponential weighted moving
average).
[3] Here is some code I wrote a long time ago, written along the lines
suggested in the paper. Some of it is probably wrong, since I never
finished testing it:
static inline int
if_enq_drop(struct ifqueue *ifq, struct mbuf *m)
{
int marked;
u_int newlen, new_avglen;
if (ifq->ifq_instlen) {
newlen = ifq->ifq_instlen + 1;
new_avglen = ifq->ifq_avg +
((newlen << (RED_AVG_SCALE - RED_W_Q_SHIFT))
- (ifq->ifq_avg >> RED_W_Q_SHIFT));
} else {
newlen = 1;
new_avglen = if_red_unidle(ifq);
}
if (new_avglen < RED_MIN_TH_SCALED) {
ifq->ifq_red_count = -1;
marked = 0;
} else {
marked = if_red_domark(new_avglen, ifq);
}
if (marked) {
ifq->ifq_drops++;
/* XXX DEBUG */
printf("%p: marked a packet, instlen %u, avglen %u\n",
(void *)ifq,
ifq->ifq_instlen, ifq->ifq_avg);
/* XXX END DEBUG */
return 0;
}
m->m_nextpkt = 0;
if (ifq->ifq_tail == 0)
ifq->ifq_head = m;
else
ifq->ifq_tail->m_nextpkt = m;
ifq->ifq_tail = m;
ifq->ifq_instlen = newlen;
if (newlen > ifq->ifq_max_instlen)
ifq->ifq_max_instlen = newlen;
ifq->ifq_avg = new_avglen;
return 1;
}
/*
* We are called knowing that avglen is already over the threshold.
* NB: the calculations involving R_OVER_PB are a bit suspect. XXX
* should test.
*/
int
if_red_domark(avglen, ifq)
u_int avglen;
struct ifqueue *ifq;
{
u_int mark_prob;
int mark_shift;
int marked = 0;
if (avglen < RED_MAX_TH_SCALED) {
ifq->ifq_red_count++;
mark_prob = (ifq->ifq_avg >> RED_PROB_C_1_SHIFT)
- RED_PROB_C_2_SCALED;
/*
* We want to quickly approximate R/p_b, where
* R is a fixed-point between 0 and 1 with scale shift
* RED_RANDOM_SCALE, and p_b is a fixed-point between
* 0 and 1 with scale shift RED_AVG_SCALE.
* fls() gives us the highest-order bit set (numbered
* 1(LSB) to 32(MSB)) or zero if no bits are set. This
* -log2(p_b), but for the scale factor. We avoid using
* a zero value directly to make things less complicated.
*/
if (mark_prob == 0) mark_prob = 1; /* smallest representable */
mark_shift = fls(mark_prob) - 1;
/*
* Now, mark_shift is in the range [0..RED_AVG_SCALE];
* log2(p_b) would be in the range [-RED_AVG_SCALE..0],
* which is to say, it is mark_shift - RED_AVG_SCALE.
* Still with me? C shifts only work in one direction,
* so what we really need is -log2(p_b), which is thus
* RED_AVG_SCALE - mark_shift. But, at the same time,
* we also need to unscale R, so we subtract RED_RANDOM_SCALE
* from that shift factor (to avoid overflow). That can
* sometimes turn the sign negative, so we have to be able
* to do two-way shifts anyway.
*/
mark_shift = RED_AVG_SCALE - mark_shift - RED_RANDOM_SCALE;
#define R_OVER_PB(R) (int)(mark_shift > 0 ? \
(R) << mark_shift : (R) >> -mark_shift)
if (ifq->ifq_red_count > 0
&& ifq->ifq_red_count >= R_OVER_PB(ifq->ifq_random)) {
marked = 1;
ifq->ifq_red_count = 0;
}
if (ifq->ifq_red_count == 0)
ifq->ifq_random = random() & RED_RANDOM_MASK;
} else {
marked = 1;
ifq->ifq_red_count = -1;
}
return marked;
}
--
Garrett A. Wollman | O Siem / We are all family / O Siem / We're all the same
wollman@lcs.mit.edu | O Siem / The fires of freedom
Opinions not those of| Dance in the burning flame
MIT, LCS, CRS, or NSA| - Susan Aglukark and Chad Irschick
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-net" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200008042131.RAA33779>
