Change struct ipqe to use TAILQ's instead of LIST's (primarily for TCP's

benefit currently).  Rework tcp_reass code to optimize the 4 most likely causes
of out-of-order packets: first OoO pkt, next OoO pkt in seq, OoO pkt is part
of new chuck of OoO packets, and the OoO pkt fills the first hole.  Add evcnts
to instrument tcp_reass (enabled by the options TCP_REASS_COUNTERS).  This is
part 1/2 of tcp_reass changes.
This commit is contained in:
matt 2002-05-07 02:59:38 +00:00
parent 119f33b8a8
commit e5555e5c26
4 changed files with 211 additions and 46 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: ip_input.c,v 1.147 2002/04/18 22:33:21 matt Exp $ */
/* $NetBSD: ip_input.c,v 1.148 2002/05/07 02:59:38 matt Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -102,7 +102,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.147 2002/04/18 22:33:21 matt Exp $");
__KERNEL_RCSID(0, "$NetBSD: ip_input.c,v 1.148 2002/05/07 02:59:38 matt Exp $");
#include "opt_gateway.h"
#include "opt_pfil_hooks.h"
@ -858,7 +858,7 @@ ip_reass(ipqe, fp)
fp->ipq_ttl = IPFRAGTTL;
fp->ipq_p = ipqe->ipqe_ip->ip_p;
fp->ipq_id = ipqe->ipqe_ip->ip_id;
LIST_INIT(&fp->ipq_fragq);
TAILQ_INIT(&fp->ipq_fragq);
fp->ipq_src = ipqe->ipqe_ip->ip_src;
fp->ipq_dst = ipqe->ipqe_ip->ip_dst;
p = NULL;
@ -868,8 +868,8 @@ ip_reass(ipqe, fp)
/*
* Find a segment which begins after this one does.
*/
for (p = NULL, q = LIST_FIRST(&fp->ipq_fragq); q != NULL;
p = q, q = LIST_NEXT(q, ipqe_q))
for (p = NULL, q = TAILQ_FIRST(&fp->ipq_fragq); q != NULL;
p = q, q = TAILQ_NEXT(q, ipqe_q))
if (q->ipqe_ip->ip_off > ipqe->ipqe_ip->ip_off)
break;
@ -904,9 +904,9 @@ ip_reass(ipqe, fp)
m_adj(q->ipqe_m, i);
break;
}
nq = LIST_NEXT(q, ipqe_q);
nq = TAILQ_NEXT(q, ipqe_q);
m_freem(q->ipqe_m);
LIST_REMOVE(q, ipqe_q);
TAILQ_REMOVE(&fp->ipq_fragq, q, ipqe_q);
pool_put(&ipqent_pool, q);
}
@ -916,13 +916,13 @@ insert:
* check for complete reassembly.
*/
if (p == NULL) {
LIST_INSERT_HEAD(&fp->ipq_fragq, ipqe, ipqe_q);
TAILQ_INSERT_HEAD(&fp->ipq_fragq, ipqe, ipqe_q);
} else {
LIST_INSERT_AFTER(p, ipqe, ipqe_q);
TAILQ_INSERT_AFTER(&fp->ipq_fragq, p, ipqe, ipqe_q);
}
next = 0;
for (p = NULL, q = LIST_FIRST(&fp->ipq_fragq); q != NULL;
p = q, q = LIST_NEXT(q, ipqe_q)) {
for (p = NULL, q = TAILQ_FIRST(&fp->ipq_fragq); q != NULL;
p = q, q = TAILQ_NEXT(q, ipqe_q)) {
if (q->ipqe_ip->ip_off != next)
return (0);
next += q->ipqe_ip->ip_len;
@ -934,7 +934,7 @@ insert:
* Reassembly is complete. Check for a bogus message size and
* concatenate fragments.
*/
q = LIST_FIRST(&fp->ipq_fragq);
q = TAILQ_FIRST(&fp->ipq_fragq);
ip = q->ipqe_ip;
if ((next + (ip->ip_hl << 2)) > IP_MAXPACKET) {
ipstat.ips_toolong++;
@ -945,11 +945,11 @@ insert:
t = m->m_next;
m->m_next = 0;
m_cat(m, t);
nq = LIST_NEXT(q, ipqe_q);
nq = TAILQ_NEXT(q, ipqe_q);
pool_put(&ipqent_pool, q);
for (q = nq; q != NULL; q = nq) {
t = q->ipqe_m;
nq = LIST_NEXT(q, ipqe_q);
nq = TAILQ_NEXT(q, ipqe_q);
pool_put(&ipqent_pool, q);
m_cat(m, t);
}
@ -996,10 +996,10 @@ ip_freef(fp)
IPQ_LOCK_CHECK();
for (q = LIST_FIRST(&fp->ipq_fragq); q != NULL; q = p) {
p = LIST_NEXT(q, ipqe_q);
for (q = TAILQ_FIRST(&fp->ipq_fragq); q != NULL; q = p) {
p = TAILQ_NEXT(q, ipqe_q);
m_freem(q->ipqe_m);
LIST_REMOVE(q, ipqe_q);
TAILQ_REMOVE(&fp->ipq_fragq, q, ipqe_q);
pool_put(&ipqent_pool, q);
}
LIST_REMOVE(fp, ipq_q);

View File

@ -1,4 +1,4 @@
/* $NetBSD: ip_var.h,v 1.46 2001/12/21 02:51:47 itojun Exp $ */
/* $NetBSD: ip_var.h,v 1.47 2002/05/07 02:59:38 matt Exp $ */
/*
* Copyright (c) 1982, 1986, 1993
@ -63,19 +63,20 @@ struct ipovly {
* port numbers (which are no longer needed once we've located the
* tcpcb) are overlayed with an mbuf pointer.
*/
LIST_HEAD(ipqehead, ipqent);
TAILQ_HEAD(ipqehead, ipqent);
struct ipqent {
LIST_ENTRY(ipqent) ipqe_q;
TAILQ_ENTRY(ipqent) ipqe_q;
union {
struct ip *_ip;
struct tcpiphdr *_tcp;
} _ipqe_u1;
struct mbuf *ipqe_m; /* mbuf contains packet */
struct mbuf *ipqe_m; /* point to first mbuf */
struct mbuf *ipre_mlast; /* point to last mbuf */
u_int8_t ipqe_mff; /* for IP fragmentation */
/*
* The following are used in TCP reassembly
*/
LIST_ENTRY(ipqent) ipqe_timeq;
TAILQ_ENTRY(ipqent) ipqe_timeq;
u_int32_t ipqe_seq;
u_int32_t ipqe_len;
u_int32_t ipqe_flags;

View File

@ -1,4 +1,4 @@
/* $NetBSD: tcp_input.c,v 1.140 2002/03/24 17:09:01 christos Exp $ */
/* $NetBSD: tcp_input.c,v 1.141 2002/05/07 02:59:38 matt Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -152,7 +152,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tcp_input.c,v 1.140 2002/03/24 17:09:01 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: tcp_input.c,v 1.141 2002/05/07 02:59:38 matt Exp $");
#include "opt_inet.h"
#include "opt_ipsec.h"
@ -292,6 +292,31 @@ extern struct evcnt tcp_swcsum;
#endif /* TCP_CSUM_COUNTERS */
#ifdef TCP_REASS_COUNTERS
#include <sys/device.h>
extern struct evcnt tcp_reass_;
extern struct evcnt tcp_reass_empty;
extern struct evcnt tcp_reass_iteration[8];
extern struct evcnt tcp_reass_prependfirst;
extern struct evcnt tcp_reass_prepend;
extern struct evcnt tcp_reass_insert;
extern struct evcnt tcp_reass_inserttail;
extern struct evcnt tcp_reass_append;
extern struct evcnt tcp_reass_appendtail;
extern struct evcnt tcp_reass_overlaptail;
extern struct evcnt tcp_reass_overlapfront;
extern struct evcnt tcp_reass_segdup;
extern struct evcnt tcp_reass_fragdup;
#define TCP_REASS_COUNTER_INCR(ev) (ev)->ev_count++
#else
#define TCP_REASS_COUNTER_INCR(ev) /* nothing */
#endif /* TCP_REASS_COUNTERS */
int
tcp_reass(tp, th, m, tlen)
struct tcpcb *tp;
@ -306,6 +331,9 @@ tcp_reass(tp, th, m, tlen)
unsigned pkt_len;
u_long rcvpartdupbyte = 0;
u_long rcvoobyte;
#ifdef TCP_REASS_COUNTERS
u_int count = 0;
#endif
if (tp->t_inpcb)
so = tp->t_inpcb->inp_socket;
@ -331,11 +359,64 @@ tcp_reass(tp, th, m, tlen)
pkt_seq = th->th_seq;
pkt_len = *tlen;
pkt_flags = th->th_flags;
TCP_REASS_COUNTER_INCR(&tcp_reass_);
if ((p = TAILQ_LAST(&tp->segq, ipqehead)) != NULL) {
/*
* When we miss a packet, the vast majority of time we get
* packets that follow it in order. So optimize for that.
*/
if (pkt_seq == p->ipqe_seq + p->ipqe_len) {
p->ipqe_len += pkt_len;
p->ipqe_flags |= pkt_flags;
m_cat(p->ipqe_m, m);
tiqe = p;
TAILQ_REMOVE(&tp->timeq, p, ipqe_timeq);
TCP_REASS_COUNTER_INCR(&tcp_reass_appendtail);
goto skip_replacement;
}
/*
* While we're here, if the pkt is completely beyond
* anything we have, just insert it at the tail.
*/
if (SEQ_GT(pkt_seq, p->ipqe_seq + p->ipqe_len)) {
TCP_REASS_COUNTER_INCR(&tcp_reass_inserttail);
goto insert_it;
}
}
q = TAILQ_FIRST(&tp->segq);
if (q != NULL) {
/*
* If this segment immediately precedes the first out-of-order
* block, simply slap the segment in front of it and (mostly)
* skip the complicated logic.
*/
if (pkt_seq + pkt_len == q->ipqe_seq) {
q->ipqe_seq = pkt_seq;
q->ipqe_len += pkt_len;
q->ipqe_flags |= pkt_flags;
m_cat(m, q->ipqe_m);
q->ipqe_m = m;
tiqe = q;
TAILQ_REMOVE(&tp->timeq, q, ipqe_timeq);
TCP_REASS_COUNTER_INCR(&tcp_reass_prependfirst);
goto skip_replacement;
}
} else {
TCP_REASS_COUNTER_INCR(&tcp_reass_empty);
}
/*
* Find a segment which begins after this one does.
*/
for (p = NULL, q = LIST_FIRST(&tp->segq); q != NULL; q = nq) {
nq = LIST_NEXT(q, ipqe_q);
for (p = NULL; q != NULL; q = nq) {
nq = TAILQ_NEXT(q, ipqe_q);
#ifdef TCP_REASS_COUNTERS
count++;
#endif
/*
* If the received segment is just right after this
* fragment, merge the two together and then check
@ -352,6 +433,7 @@ tcp_reass(tp, th, m, tlen)
pkt_seq = q->ipqe_seq;
m_cat(q->ipqe_m, m);
m = q->ipqe_m;
TCP_REASS_COUNTER_INCR(&tcp_reass_append);
goto free_ipqe;
}
/*
@ -366,8 +448,11 @@ tcp_reass(tp, th, m, tlen)
* If the fragment is past the received segment,
* it (or any following) can't be concatenated.
*/
if (SEQ_GT(q->ipqe_seq, pkt_seq + pkt_len))
if (SEQ_GT(q->ipqe_seq, pkt_seq + pkt_len)) {
TCP_REASS_COUNTER_INCR(&tcp_reass_insert);
break;
}
/*
* We've received all the data in this segment before.
* mark it as a duplicate and return.
@ -379,6 +464,7 @@ tcp_reass(tp, th, m, tlen)
m_freem(m);
if (tiqe != NULL)
pool_put(&ipqent_pool, tiqe);
TCP_REASS_COUNTER_INCR(&tcp_reass_segdup);
return (0);
}
/*
@ -390,6 +476,7 @@ tcp_reass(tp, th, m, tlen)
SEQ_LEQ(q->ipqe_seq + q->ipqe_len, pkt_seq + pkt_len)) {
rcvpartdupbyte += q->ipqe_len;
m_freem(q->ipqe_m);
TCP_REASS_COUNTER_INCR(&tcp_reass_fragdup);
goto free_ipqe;
}
/*
@ -413,6 +500,7 @@ tcp_reass(tp, th, m, tlen)
pkt_seq = q->ipqe_seq;
pkt_len += q->ipqe_len - overlap;
rcvoobyte -= overlap;
TCP_REASS_COUNTER_INCR(&tcp_reass_overlaptail);
goto free_ipqe;
}
/*
@ -432,6 +520,7 @@ tcp_reass(tp, th, m, tlen)
m_adj(m, -overlap);
pkt_len -= overlap;
rcvpartdupbyte += overlap;
TCP_REASS_COUNTER_INCR(&tcp_reass_overlapfront);
rcvoobyte -= overlap;
}
/*
@ -448,13 +537,14 @@ tcp_reass(tp, th, m, tlen)
pkt_len += q->ipqe_len;
pkt_flags |= q->ipqe_flags;
m_cat(m, q->ipqe_m);
LIST_REMOVE(q, ipqe_q);
LIST_REMOVE(q, ipqe_timeq);
TAILQ_REMOVE(&tp->segq, q, ipqe_q);
TAILQ_REMOVE(&tp->timeq, q, ipqe_timeq);
if (tiqe == NULL) {
tiqe = q;
} else {
pool_put(&ipqent_pool, q);
}
TCP_REASS_COUNTER_INCR(&tcp_reass_prepend);
break;
}
/*
@ -473,8 +563,8 @@ tcp_reass(tp, th, m, tlen)
* to save doing a malloc/free in most instances.
*/
free_ipqe:
LIST_REMOVE(q, ipqe_q);
LIST_REMOVE(q, ipqe_timeq);
TAILQ_REMOVE(&tp->segq, q, ipqe_q);
TAILQ_REMOVE(&tp->timeq, q, ipqe_timeq);
if (tiqe == NULL) {
tiqe = q;
} else {
@ -482,6 +572,15 @@ tcp_reass(tp, th, m, tlen)
}
}
#ifdef TCP_REASS_COUNTERS
if (count > 7)
TCP_REASS_COUNTER_INCR(&tcp_reass_iteration[0]);
else if (count > 0)
TCP_REASS_COUNTER_INCR(&tcp_reass_iteration[count]);
#endif
insert_it:
/*
* Allocate a new queue entry since the received segment did not
* collapse onto any other out-of-order block; thus we are allocating
@ -516,14 +615,14 @@ tcp_reass(tp, th, m, tlen)
tiqe->ipqe_len = pkt_len;
tiqe->ipqe_flags = pkt_flags;
if (p == NULL) {
LIST_INSERT_HEAD(&tp->segq, tiqe, ipqe_q);
TAILQ_INSERT_HEAD(&tp->segq, tiqe, ipqe_q);
#ifdef TCPREASS_DEBUG
if (tiqe->ipqe_seq != tp->rcv_nxt)
printf("tcp_reass[%p]: insert %u:%u(%u) at front\n",
tp, pkt_seq, pkt_seq + pkt_len, pkt_len);
#endif
} else {
LIST_INSERT_AFTER(p, tiqe, ipqe_q);
TAILQ_INSERT_AFTER(&tp->segq, p, tiqe, ipqe_q);
#ifdef TCPREASS_DEBUG
printf("tcp_reass[%p]: insert %u:%u(%u) after %u:%u(%u)\n",
tp, pkt_seq, pkt_seq + pkt_len, pkt_len,
@ -531,7 +630,9 @@ tcp_reass(tp, th, m, tlen)
#endif
}
LIST_INSERT_HEAD(&tp->timeq, tiqe, ipqe_timeq);
skip_replacement:
TAILQ_INSERT_HEAD(&tp->timeq, tiqe, ipqe_timeq);
present:
/*
@ -540,7 +641,7 @@ present:
*/
if (TCPS_HAVEESTABLISHED(tp->t_state) == 0)
return (0);
q = LIST_FIRST(&tp->segq);
q = TAILQ_FIRST(&tp->segq);
if (q == NULL || q->ipqe_seq != tp->rcv_nxt)
return (0);
if (tp->t_state == TCPS_SYN_RECEIVED && q->ipqe_len)
@ -550,8 +651,8 @@ present:
pkt_flags = q->ipqe_flags & TH_FIN;
ND6_HINT(tp);
LIST_REMOVE(q, ipqe_q);
LIST_REMOVE(q, ipqe_timeq);
TAILQ_REMOVE(&tp->segq, q, ipqe_q);
TAILQ_REMOVE(&tp->timeq, q, ipqe_timeq);
if (so->so_state & SS_CANTRCVMORE)
m_freem(q->ipqe_m);
else
@ -1375,7 +1476,7 @@ after_listen:
return;
}
} else if (th->th_ack == tp->snd_una &&
LIST_FIRST(&tp->segq) == NULL &&
TAILQ_FIRST(&tp->segq) == NULL &&
tlen <= sbspace(&so->so_rcv)) {
/*
* this is a pure, in-sequence data packet
@ -2122,7 +2223,7 @@ dodata: /* XXX */
/* NOTE: this was TCP_REASS() macro, but used only once */
TCP_REASS_LOCK(tp);
if (th->th_seq == tp->rcv_nxt &&
LIST_FIRST(&tp->segq) == NULL &&
TAILQ_FIRST(&tp->segq) == NULL &&
tp->t_state == TCPS_ESTABLISHED) {
TCP_SETUP_ACK(tp, th);
tp->rcv_nxt += tlen;

View File

@ -1,4 +1,4 @@
/* $NetBSD: tcp_subr.c,v 1.125 2002/04/27 01:47:58 thorpej Exp $ */
/* $NetBSD: tcp_subr.c,v 1.126 2002/05/07 02:59:39 matt Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -102,7 +102,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tcp_subr.c,v 1.125 2002/04/27 01:47:58 thorpej Exp $");
__KERNEL_RCSID(0, "$NetBSD: tcp_subr.c,v 1.126 2002/05/07 02:59:39 matt Exp $");
#include "opt_inet.h"
#include "opt_ipsec.h"
@ -240,6 +240,46 @@ struct evcnt tcp_output_refbig = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
NULL, "tcp", "output reference big");
#endif /* TCP_OUTPUT_COUNTERS */
#ifdef TCP_REASS_COUNTERS
#include <sys/device.h>
struct evcnt tcp_reass_ = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
NULL, "tcp_reass", "calls");
struct evcnt tcp_reass_empty = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
&tcp_reass_, "tcp_reass", "insert into empty queue");
struct evcnt tcp_reass_iteration[8] = {
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &tcp_reass_, "tcp_reass", ">7 iterations"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &tcp_reass_, "tcp_reass", "1 iteration"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &tcp_reass_, "tcp_reass", "2 iterations"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &tcp_reass_, "tcp_reass", "3 iterations"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &tcp_reass_, "tcp_reass", "4 iterations"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &tcp_reass_, "tcp_reass", "5 iterations"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &tcp_reass_, "tcp_reass", "6 iterations"),
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, &tcp_reass_, "tcp_reass", "7 iterations"),
};
struct evcnt tcp_reass_prependfirst = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
&tcp_reass_, "tcp_reass", "prepend to first");
struct evcnt tcp_reass_prepend = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
&tcp_reass_, "tcp_reass", "prepend");
struct evcnt tcp_reass_insert = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
&tcp_reass_, "tcp_reass", "insert");
struct evcnt tcp_reass_inserttail = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
&tcp_reass_, "tcp_reass", "insert at tail");
struct evcnt tcp_reass_append = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
&tcp_reass_, "tcp_reass", "append");
struct evcnt tcp_reass_appendtail = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
&tcp_reass_, "tcp_reass", "append to tail fragment");
struct evcnt tcp_reass_overlaptail = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
&tcp_reass_, "tcp_reass", "overlap at end");
struct evcnt tcp_reass_overlapfront = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
&tcp_reass_, "tcp_reass", "overlap at start");
struct evcnt tcp_reass_segdup = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
&tcp_reass_, "tcp_reass", "duplicate segment");
struct evcnt tcp_reass_fragdup = EVCNT_INITIALIZER(EVCNT_TYPE_MISC,
&tcp_reass_, "tcp_reass", "duplicate fragment");
#endif /* TCP_REASS_COUNTERS */
/*
* Tcp initialization
*/
@ -291,6 +331,29 @@ tcp_init()
evcnt_attach_static(&tcp_output_copybig);
evcnt_attach_static(&tcp_output_refbig);
#endif /* TCP_OUTPUT_COUNTERS */
#ifdef TCP_REASS_COUNTERS
evcnt_attach_static(&tcp_reass_);
evcnt_attach_static(&tcp_reass_empty);
evcnt_attach_static(&tcp_reass_iteration[0]);
evcnt_attach_static(&tcp_reass_iteration[1]);
evcnt_attach_static(&tcp_reass_iteration[2]);
evcnt_attach_static(&tcp_reass_iteration[3]);
evcnt_attach_static(&tcp_reass_iteration[4]);
evcnt_attach_static(&tcp_reass_iteration[5]);
evcnt_attach_static(&tcp_reass_iteration[6]);
evcnt_attach_static(&tcp_reass_iteration[7]);
evcnt_attach_static(&tcp_reass_prependfirst);
evcnt_attach_static(&tcp_reass_prepend);
evcnt_attach_static(&tcp_reass_insert);
evcnt_attach_static(&tcp_reass_inserttail);
evcnt_attach_static(&tcp_reass_append);
evcnt_attach_static(&tcp_reass_appendtail);
evcnt_attach_static(&tcp_reass_overlaptail);
evcnt_attach_static(&tcp_reass_overlapfront);
evcnt_attach_static(&tcp_reass_segdup);
evcnt_attach_static(&tcp_reass_fragdup);
#endif /* TCP_REASS_COUNTERS */
}
/*
@ -823,8 +886,8 @@ tcp_newtcpcb(family, aux)
if (tp == NULL)
return (NULL);
bzero((caddr_t)tp, sizeof(struct tcpcb));
LIST_INIT(&tp->segq);
LIST_INIT(&tp->timeq);
TAILQ_INIT(&tp->segq);
TAILQ_INIT(&tp->timeq);
tp->t_family = family; /* may be overridden later on */
tp->t_peermss = tcp_mssdflt;
tp->t_ourmss = tcp_mssdflt;
@ -1080,14 +1143,14 @@ tcp_freeq(tp)
TCP_REASS_LOCK_CHECK(tp);
while ((qe = LIST_FIRST(&tp->segq)) != NULL) {
while ((qe = TAILQ_FIRST(&tp->segq)) != NULL) {
#ifdef TCPREASS_DEBUG
printf("tcp_freeq[%p,%d]: %u:%u(%u) 0x%02x\n",
tp, i++, qe->ipqe_seq, qe->ipqe_seq + qe->ipqe_len,
qe->ipqe_len, qe->ipqe_flags & (TH_SYN|TH_FIN|TH_RST));
#endif
LIST_REMOVE(qe, ipqe_q);
LIST_REMOVE(qe, ipqe_timeq);
TAILQ_REMOVE(&tp->segq, qe, ipqe_q);
TAILQ_REMOVE(&tp->timeq, qe, ipqe_timeq);
m_freem(qe->ipqe_m);
pool_put(&ipqent_pool, qe);
rv = 1;