Add a lock around the TCPCB's sequence queue, to prevent tcp_drain()

from corrupting the queue if called from a device's interrupt context.

Similar in nature to the problem reported in PR #5684.
This commit is contained in:
thorpej 1998-12-18 21:38:02 +00:00
parent ca15e01c76
commit 4f177aec90
3 changed files with 86 additions and 3 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: tcp_input.c,v 1.71 1998/10/08 01:19:26 thorpej Exp $ */
/* $NetBSD: tcp_input.c,v 1.72 1998/12/18 21:38:02 thorpej Exp $ */
/*-
* Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
@ -148,6 +148,7 @@ do { \
* when segments are out of order (so fast retransmit can work).
*/
#define TCP_REASS(tp, ti, m, so, flags) { \
TCP_REASS_LOCK((tp)); \
if ((ti)->ti_seq == (tp)->rcv_nxt && \
(tp)->segq.lh_first == NULL && \
(tp)->t_state == TCPS_ESTABLISHED) { \
@ -162,6 +163,7 @@ do { \
(flags) = tcp_reass((tp), (ti), (m)); \
tp->t_flags |= TF_ACKNOW; \
} \
TCP_REASS_UNLOCK((tp)); \
}
int
@ -178,6 +180,8 @@ tcp_reass(tp, ti, m)
u_long rcvpartdupbyte = 0;
u_long rcvoobyte;
TCP_REASS_LOCK_CHECK(tp);
/*
* Call with ti==0 after become established to
* force pre-ESTABLISHED data up to user socket.
@ -858,8 +862,10 @@ after_listen:
tp->snd_scale = tp->requested_s_scale;
tp->rcv_scale = tp->request_r_scale;
}
TCP_REASS_LOCK(tp);
(void) tcp_reass(tp, (struct tcpiphdr *)0,
(struct mbuf *)0);
TCP_REASS_UNLOCK(tp);
/*
* if we didn't have to retransmit the SYN,
* use its rtt as our initial srtt & rtt var.
@ -1124,7 +1130,9 @@ after_listen:
tp->snd_scale = tp->requested_s_scale;
tp->rcv_scale = tp->request_r_scale;
}
TCP_REASS_LOCK(tp);
(void) tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0);
TCP_REASS_UNLOCK(tp);
tp->snd_wl1 = ti->ti_seq - 1;
/* fall into ... */

View File

@ -1,4 +1,4 @@
/* $NetBSD: tcp_subr.c,v 1.62 1998/10/08 01:19:26 thorpej Exp $ */
/* $NetBSD: tcp_subr.c,v 1.63 1998/12/18 21:38:03 thorpej Exp $ */
/*-
* Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
@ -429,7 +429,10 @@ tcp_close(tp)
}
#endif /* RTV_RTT */
/* free the reassembly queue, if any */
TCP_REASS_LOCK(tp);
(void) tcp_freeq(tp);
TCP_REASS_UNLOCK(tp);
TCP_CLEAR_DELACK(tp);
if (tp->t_template)
@ -452,6 +455,8 @@ tcp_freeq(tp)
int i = 0;
#endif
TCP_REASS_LOCK_CHECK(tp);
while ((qe = tp->segq.lh_first) != NULL) {
#ifdef TCPREASS_DEBUG
printf("tcp_freeq[%p,%d]: %u:%u(%u) 0x%02x\n",
@ -484,8 +489,16 @@ tcp_drain()
for (; inp != (struct inpcb *)&tcbtable.inpt_queue;
inp = inp->inp_queue.cqe_next) {
if ((tp = intotcpcb(inp)) != NULL) {
/*
* We may be called from a device's interrupt
* context. If the tcpcb is already busy,
* just bail out now.
*/
if (tcp_reass_lock_try(tp) == 0)
continue;
if (tcp_freeq(tp))
tcpstat.tcps_connsdrained++;
TCP_REASS_UNLOCK(tp);
}
}
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: tcp_var.h,v 1.55 1998/10/06 00:20:45 matt Exp $ */
/* $NetBSD: tcp_var.h,v 1.56 1998/12/18 21:38:03 thorpej Exp $ */
/*-
* Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
@ -107,6 +107,7 @@ struct tcpcb {
#define TF_WILL_SACK 0x0800 /* try to use SACK */
#define TF_CANT_TXSACK 0x1000 /* other side said I could not SACK */
#define TF_IGNR_RXSACK 0x2000 /* ignore received SACK blocks */
#define TF_REASSEMBLING 0x4000 /* we're busy reassembling */
struct tcpiphdr *t_template; /* skeletal packet for transmit */
@ -177,6 +178,67 @@ struct tcpcb {
struct ipqehead timeq; /* time sequenced queue (for SACK) */
};
#ifdef _KERNEL
/*
* TCP reassembly queue locks.
*/
static __inline int tcp_reass_lock_try __P((struct tcpcb *))
__attribute__((__unused__));
static __inline void tcp_reass_unlock __P((struct tcpcb *))
__attribute__((__unused__));
static __inline int
tcp_reass_lock_try(tp)
struct tcpcb *tp;
{
int s;
s = splimp();
if (tp->t_flags & TF_REASSEMBLING) {
splx(s);
return (0);
}
tp->t_flags |= TF_REASSEMBLING;
splx(s);
return (1);
}
static __inline void
tcp_reass_unlock(tp)
struct tcpcb *tp;
{
int s;
s = splimp();
tp->t_flags &= ~TF_REASSEMBLING;
splx(s);
}
#ifdef DIAGNOSTIC
#define TCP_REASS_LOCK(tp) \
do { \
if (tcp_reass_lock_try(tp) == 0) { \
printf("%s:%d: tcpcb %p reass already locked\n", \
__FILE__, __LINE__, tp); \
panic("tcp_reass_lock"); \
} \
} while (0)
#define TCP_REASS_LOCK_CHECK(tp) \
do { \
if (((tp)->t_flags & TF_REASSEMBLING) == 0) { \
printf("%s:%d: tcpcb %p reass lock not held\n", \
__FILE__, __LINE__, tp); \
panic("tcp reass lock check"); \
} \
} while (0)
#else
#define TCP_REASS_LOCK(tp) (void) tcp_reass_lock_try((tp))
#define TCP_REASS_LOCK_CHECK(tp) /* nothing */
#endif
#define TCP_REASS_UNLOCK(tp) tcp_reass_unlock((tp))
#endif /* _KERNEL */
/*
* Queue for delayed ACK processing.
*/