Due to a poorly chosen initial state, we might jump immediately
into a CONTROL state bypassing the DROPPING one when the initial
state is DROPPING. The fix is easy: the initial state must be
something neutral, so that we would transition INITIAL->DROPPING
right off the bat in the beggining of the loop and then perfrom
a DROPPING->CONTROL transition and either CONTROL->DROPPING and
restart the loop or CONTROL->RECOVERY/ACCEPTING.
The change in codel_state_change is necessary because valid
transitions are only INITIAL->DROPPING and DROPPING->CONTROL
in this case.
Does this look good?
diff --git sys/net/fq_codel.c sys/net/fq_codel.c
index 1c380f49f3d..c7fe38a7094 100644
--- sys/net/fq_codel.c
+++ sys/net/fq_codel.c
@@ -370,11 +370,11 @@ codel_next_packet(struct codel *cd, struct codel_params
*cp, int64_t now,
*drop = 1;
}
return (m);
}
-enum { ACCEPTING, FIRSTDROP, DROPPING, CONTROL, RECOVERY };
+enum { INITIAL, ACCEPTING, FIRSTDROP, DROPPING, CONTROL, RECOVERY };
static inline int
codel_state_change(struct codel *cd, int64_t now, struct mbuf *m, int drop,
int state)
{
@@ -383,11 +383,11 @@ codel_state_change(struct codel *cd, int64_t now, struct
mbuf *m, int drop,
if (cd->dropping) {
if (!drop)
return (RECOVERY);
else if (now >= cd->next)
- return (state == CONTROL ? DROPPING : CONTROL);
+ return (state == DROPPING ? CONTROL : DROPPING);
} else if (drop)
return (FIRSTDROP);
if (m == NULL)
return (RECOVERY);
@@ -403,11 +403,11 @@ codel_dequeue(struct codel *cd, struct codel_params *cp,
int64_t now,
unsigned short delta;
int drop, state, done = 0;
*dpkts = *dbytes = 0;
- state = cd->dropping ? DROPPING : ACCEPTING;
+ state = INITIAL;
while (!done) {
m = codel_next_packet(cd, cp, now, &drop);
state = codel_state_change(cd, now, m, drop, state);