Page MenuHomeFreeBSD

D36887.id111475.diff
No OneTemporary

D36887.id111475.diff

diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -2864,12 +2864,12 @@
}
while ((inp = inp_next(&inpi)) != NULL)
if (inp->inp_gencnt == params->sop_id) {
- if (inp->inp_flags & INP_DROPPED) {
+ if ((inp->inp_flags & INP_DROPPED) ||
+ inp->inp_socket == NULL) {
INP_WUNLOCK(inp);
return (ECONNRESET);
}
so = inp->inp_socket;
- KASSERT(so != NULL, ("inp_socket == NULL"));
soref(so);
error = (*ctloutput_set)(inp, &sopt);
sorele(so);
diff --git a/sys/netinet/siftr.c b/sys/netinet/siftr.c
--- a/sys/netinet/siftr.c
+++ b/sys/netinet/siftr.c
@@ -896,9 +896,10 @@
/*
* If we can't find the TCP control block (happens occasionaly for a
- * packet sent during the shutdown phase of a TCP connection), bail
+ * packet sent during the shutdown phase of a TCP connection),
+ * or we're in the timewait state, bail
*/
- if (tp == NULL) {
+ if (tp == NULL || tp->t_state == TCPS_TIME_WAIT) {
if (dir == PFIL_IN)
ss->nskip_in_tcpcb++;
else
@@ -1080,9 +1081,10 @@
/*
* If we can't find the TCP control block (happens occasionaly for a
- * packet sent during the shutdown phase of a TCP connection), bail
+ * packet sent during the shutdown phase of a TCP connection),
+ * or we're in the timewait state, bail.
*/
- if (tp == NULL) {
+ if (tp == NULL || tp->t_state == TCPS_TIME_WAIT) {
if (dir == PFIL_IN)
ss->nskip_in_tcpcb++;
else
diff --git a/sys/netinet/tcp_lro.c b/sys/netinet/tcp_lro.c
--- a/sys/netinet/tcp_lro.c
+++ b/sys/netinet/tcp_lro.c
@@ -1359,7 +1359,8 @@
tp = intotcpcb(inp);
/* Check if the inp is dead, Jim. */
- if (tp == NULL || (inp->inp_flags & INP_DROPPED)) {
+ if (tp == NULL || (inp->inp_flags & INP_DROPPED) ||
+ tp->t_state == TCPS_TIME_WAIT) {
INP_WUNLOCK(inp);
return (TCP_LRO_CANNOT);
}
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -2563,18 +2563,29 @@
#endif
in_pcbdrop(inp);
TCPSTAT_INC(tcps_closed);
- if (tp->t_state != TCPS_CLOSED)
+ /*
+ * Connections in TIME_WAIT may have the socket already detached,
+ * but tcp_use_detach() didn't free pcb. In this case it our job
+ * to free it.
+ */
+ if ((so = inp->inp_socket) != NULL) {
+ if (tp->t_state != TCPS_CLOSED)
+ tcp_state_change(tp, TCPS_CLOSED);
+ soisdisconnected(so);
+ if (inp->inp_flags & INP_SOCKREF) {
+ inp->inp_flags &= ~INP_SOCKREF;
+ INP_WUNLOCK(inp);
+ sorele(so);
+ return (NULL);
+ } else
+ return (tp); /* locked */
+ } else {
+ MPASS(tp->t_state == TCPS_TIME_WAIT);
tcp_state_change(tp, TCPS_CLOSED);
- KASSERT(inp->inp_socket != NULL, ("tcp_close: inp_socket NULL"));
- so = inp->inp_socket;
- soisdisconnected(so);
- if (inp->inp_flags & INP_SOCKREF) {
- inp->inp_flags &= ~INP_SOCKREF;
- INP_WUNLOCK(inp);
- sorele(so);
+ tcp_discardcb(tp);
+ in_pcbfree(inp);
return (NULL);
}
- return (tp);
}
/*
@@ -3693,10 +3704,12 @@
#endif
}
if (inp != NULL) {
- if ((inp->inp_flags & INP_DROPPED) == 0 &&
- !SOLISTENING(inp->inp_socket)) {
- tp = intotcpcb(inp);
- tp = tcp_drop(tp, ECONNABORTED);
+ tp = intotcpcb(inp);
+ if ((inp->inp_flags & INP_DROPPED) == 0) {
+ if (tp->t_state == TCPS_TIME_WAIT)
+ tp = tcp_close(tp);
+ else if (!SOLISTENING(inp->inp_socket))
+ tp = tcp_drop(tp, ECONNABORTED);
if (tp != NULL)
INP_WUNLOCK(inp);
} else
diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c
--- a/sys/netinet/tcp_timewait.c
+++ b/sys/netinet/tcp_timewait.c
@@ -116,6 +116,7 @@
tcp_twstart(struct tcpcb *tp)
{
struct inpcb *inp = tp->t_inpcb;
+ struct socket *so = inp->inp_socket;
#ifdef INET6
bool isipv6 = inp->inp_inc.inc_flags & INC_ISIPV6;
#endif
@@ -128,7 +129,7 @@
"(inp->inp_flags & INP_DROPPED) != 0"));
tcp_state_change(tp, TCPS_TIME_WAIT);
- soisdisconnected(inp->inp_socket);
+ soisdisconnected(so);
if (tp->t_flags & TF_ACKNOW)
tcp_output(tp);
@@ -149,7 +150,23 @@
}
tcp_timer_activate(tp, TT_2MSL, 2 * V_tcp_msl);
- INP_WUNLOCK(inp);
+
+ /*
+ * INP_SOCKREF means that the socket had been close(2)d by the
+ * userland and it is the inpcb that keeps socket in memory.
+ * To preserve memory, we want to release the socket now, not
+ * when the TIME_WAIT state expires. Note that tcp_usr_detach()
+ * has a special case to handle a call from here.
+ */
+ if (inp->inp_flags & INP_SOCKREF) {
+ inp->inp_flags &= ~INP_SOCKREF;
+#ifdef TCPHPTS
+ tcp_hpts_remove(tp->t_inpcb);
+#endif
+ INP_WUNLOCK(inp);
+ sorele(so);
+ } else
+ INP_WUNLOCK(inp);
}
/*
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -216,13 +216,27 @@
tp = intotcpcb(inp);
- KASSERT(inp->inp_flags & INP_DROPPED ||
- tp->t_state < TCPS_SYN_SENT,
- ("%s: inp %p not dropped or embryonic", __func__, inp));
-
- tcp_discardcb(tp);
- in_pcbdetach(inp);
- in_pcbfree(inp);
+ /*
+ * Note on connections in TIME_WAIT.
+ *
+ * There are two cases here:
+ * - We went through tcp_close() in our call stack, and the connection
+ * shall be processed normally. Such pcbs are always marked with
+ * INP_DROPPED.
+ * - We are detaching pcb from the socket in tcp_twstart(), for memory
+ * saving purposes. The pcb shall remain.
+ */
+ if (inp->inp_flags & INP_DROPPED) {
+ KASSERT(tp->t_state < TCPS_SYN_SENT,
+ ("%s: dropped inp %p not embryonic", __func__, inp));
+ tcp_discardcb(tp);
+ in_pcbdetach(inp);
+ in_pcbfree(inp);
+ } else {
+ MPASS(tp->t_state == TCPS_TIME_WAIT);
+ in_pcbdetach(inp);
+ INP_WUNLOCK(inp);
+ }
}
#ifdef INET

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 26, 6:05 PM (12 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17807531
Default Alt Text
D36887.id111475.diff (5 KB)

Event Timeline