Merge r197795 from head to stable/8:

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.

  Reported by:        Kamigishi Rei <spambox at haruhiism.net>
  Reviewed by:        bz

Approved by:	re (kib)
This commit is contained in:
Robert Watson 2009-10-08 11:07:15 +00:00
parent 8fe1fb2047
commit f41dd6dca9

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++;