Instead of calling tcp_usr_detach() from tcp_usr_abort(), break out

common pcb tear-down logic into tcp_detach(), which is called from
either.  Invoke tcp_drop() from the tcp_usr_abort() path rather than
tcp_disconnect(), as we want to drop it immediately not perform a
FIN sequence.  This is one reason why some people were experiencing
panics in sodealloc(), as the netisr and aborting thread were
simultaneously trying to tear down the socket.  This bug could often
be reproduced using repeated runs of the listenclose regression test.

MFC after:	3 months
PR:		96090
Reported by:	Peter Kostouros <kpeter at melbpc dot org dot au>, kris
Tested by:	Peter Kostouros <kpeter at melbpc dot org dot au>, kris
This commit is contained in:
rwatson 2006-04-24 08:20:02 +00:00
parent 2e48f3c964
commit 685d68a523

View File

@ -139,46 +139,27 @@ tcp_usr_attach(struct socket *so, int proto, struct thread *td)
}
/*
* pru_detach() detaches the TCP protocol from the socket.
* If the protocol state is non-embryonic, then can't
* do this directly: have to initiate a pru_disconnect(),
* which may finish later; embryonic TCB's can just
* be discarded here.
* tcp_detach() releases any protocol state that can be reasonably released
* when a socket shutdown is requested, and is a shared code path for
* tcp_usr_detach() and tcp_usr_abort(), the two socket close entry points.
*
* Accepts pcbinfo, inpcb locked, will unlock the inpcb (if needed) on
* return.
*/
static void
tcp_usr_detach(struct socket *so)
tcp_detach(struct socket *so, struct inpcb *inp)
{
struct inpcb *inp;
struct tcpcb *tp;
#ifdef INET6
int isipv6 = INP_CHECK_SOCKAF(so, AF_INET6) != 0;
#endif
TCPDEBUG0;
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("tcp_usr_detach: inp == NULL"));
INP_INFO_WLOCK(&tcbinfo);
INP_LOCK(inp);
KASSERT(inp->inp_socket != NULL,
("tcp_usr_detach: inp_socket == NULL"));
TCPDEBUG1();
INP_INFO_WLOCK_ASSERT(&tcbinfo);
INP_LOCK_ASSERT(inp);
/*
* First, if we still have full TCP state, and we're not dropped,
* initiate a disconnect.
*/
if (!(inp->inp_vflag & INP_TIMEWAIT) &&
!(inp->inp_vflag & INP_DROPPED)) {
tp = intotcpcb(inp);
tcp_disconnect(tp);
}
KASSERT(so->so_pcb == inp, ("tcp_detach: so_pcb != inp"));
KASSERT(inp->inp_socket == so, ("tcp_detach: inp_socket != so"));
/*
* Second, release any protocol state that we can reasonably release.
* Note that the call to tcp_disconnect() may actually have changed
* the TCP state, so we have to re-evaluate INP_TIMEWAIT and
* INP_DROPPED.
*/
if (inp->inp_vflag & INP_TIMEWAIT) {
if (inp->inp_vflag & INP_DROPPED) {
/*
@ -248,6 +229,40 @@ tcp_usr_detach(struct socket *so)
INP_UNLOCK(inp);
}
}
}
/*
* pru_detach() detaches the TCP protocol from the socket.
* If the protocol state is non-embryonic, then can't
* do this directly: have to initiate a pru_disconnect(),
* which may finish later; embryonic TCB's can just
* be discarded here.
*/
static void
tcp_usr_detach(struct socket *so)
{
struct inpcb *inp;
struct tcpcb *tp;
TCPDEBUG0;
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("tcp_usr_detach: inp == NULL"));
INP_INFO_WLOCK(&tcbinfo);
INP_LOCK(inp);
KASSERT(inp->inp_socket != NULL,
("tcp_usr_detach: inp_socket == NULL"));
TCPDEBUG1();
/*
* First, if we still have full TCP state, and we're not dropped,
* initiate a disconnect.
*/
if (!(inp->inp_vflag & INP_TIMEWAIT) &&
!(inp->inp_vflag & INP_DROPPED)) {
tp = intotcpcb(inp);
tcp_disconnect(tp);
}
tcp_detach(so, inp);
tp = NULL;
TCPDEBUG2(PRU_DETACH);
INP_INFO_WUNLOCK(&tcbinfo);
@ -916,41 +931,38 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
/*
* Abort the TCP.
*
* First, drop the connection. Then collect state if possible.
*/
static void
tcp_usr_abort(struct socket *so)
{
#if 0
struct inpcb *inp;
struct tcpcb *tp;
#endif
/*
* XXXRW: This is not really quite the same, as we want to tcp_drop()
* rather than tcp_disconnect(), I think, but for now I'll avoid
* replicating all the tear-down logic here.
*/
tcp_usr_detach(so);
#if 0
TCPDEBUG0;
INP_INFO_WLOCK(&tcbinfo);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("tcp_usr_abort: inp == NULL"));
INP_INFO_WLOCK(&tcbinfo);
INP_LOCK(inp);
/*
* Do we need to handle timewait here? Aborted connections should
* never generate a FIN?
*/
KASSERT((inp->inp_vflag & INP_TIMEWAIT) == 0,
("tcp_usr_abort: timewait"));
tp = intotcpcb(inp);
KASSERT(inp->inp_socket != NULL,
("tcp_usr_abort: inp_socket == NULL"));
TCPDEBUG1();
tp = tcp_drop(tp, ECONNABORTED);
TCPDEBUG2(PRU_ABORT);
if (tp != NULL)
INP_UNLOCK(inp);
/*
* First, if we still have full TCP state, and we're not dropped,
* drop.
*/
if (!(inp->inp_vflag & INP_TIMEWAIT) &&
!(inp->inp_vflag & INP_DROPPED)) {
tp = intotcpcb(inp);
tcp_drop(tp, ECONNABORTED);
}
tcp_detach(so, inp);
tp = NULL;
TCPDEBUG2(PRU_DETACH);
INP_INFO_WUNLOCK(&tcbinfo);
#endif
}
/*