In tcp_input(), we acquire a global write lock at first only if a

segment is likely to trigger a TCP state change (i.e., FIN/RST/SYN).
If we later have to upgrade the lock, we acquire an inpcb reference
and drop both global/inpcb locks before reacquiring in-order.  In
that gap, the connection may transition into TIMEWAIT, so we need
to loop back and reevaluate the inpcb after relocking.

MFC after:	3 days
Reported by:	Kamigishi Rei <spambox at haruhiism.net>
Reviewed by:	bz
This commit is contained in:
Robert Watson 2009-10-05 22:24:13 +00:00
parent 604f19c91e
commit 883e9bc41d

View File

@ -648,6 +648,7 @@ findpcb:
* tried to free the inpcb, in which case we need to loop back and
* try to find a new inpcb to deliver to.
*/
relocked:
if (inp->inp_flags & INP_TIMEWAIT) {
KASSERT(ti_locked == TI_RLOCKED || ti_locked == TI_WLOCKED,
("%s: INP_TIMEWAIT ti_locked %d", __func__, ti_locked));
@ -698,7 +699,8 @@ findpcb:
* We've identified a valid inpcb, but it could be that we need an
* inpcbinfo write lock and have only a read lock. In this case,
* attempt to upgrade/relock using the same strategy as the TIMEWAIT
* case above.
* case above. If we relock, we have to jump back to 'relocked' as
* the connection might now be in TIMEWAIT.
*/
if (tp->t_state != TCPS_ESTABLISHED ||
(thflags & (TH_SYN | TH_FIN | TH_RST)) != 0 ||
@ -720,6 +722,7 @@ findpcb:
goto findpcb;
}
tcp_wlock_relocked++;
goto relocked;
} else {
ti_locked = TI_WLOCKED;
tcp_wlock_upgraded++;