Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/tcp_input.c
Show First 20 Lines • Show All 1,466 Lines • ▼ Show 20 Lines | if (V_tcp_do_autorcvbuf && (so->so_rcv.sb_flags & SB_AUTOSIZE) && | ||||
tp->rfbuf_cnt = 0; | tp->rfbuf_cnt = 0; | ||||
} else { | } else { | ||||
tp->rfbuf_cnt += tlen; /* add up */ | tp->rfbuf_cnt += tlen; /* add up */ | ||||
} | } | ||||
return (newsize); | return (newsize); | ||||
} | } | ||||
void | void | ||||
tcp_handle_wakeup(struct tcpcb *tp, struct socket *so) | |||||
{ | |||||
/* | |||||
* Since tp might be gone if the session entered | |||||
* the TIME_WAIT state before coming here, we need | |||||
* to check if the socket is still connected. | |||||
*/ | |||||
if ((so->so_state & SS_ISCONNECTED) == 0) | |||||
return; | |||||
INP_UNLOCK_ASSERT(tp->t_inpcb); | |||||
if (tp->t_flags & TF_WAKESOR) { | |||||
rrs: How is it safe to reference the tp like this without a lock?
I think if you are wrong it just… | |||||
tp->t_flags &= ~TF_WAKESOR; | |||||
SOCKBUF_UNLOCK_ASSERT(&so->so_rcv); | |||||
sorwakeup(so); | |||||
} | |||||
if (tp->t_flags & TF_WAKESOW) { | |||||
tp->t_flags &= ~TF_WAKESOW; | |||||
SOCKBUF_UNLOCK_ASSERT(&so->so_snd); | |||||
sowwakeup(so); | |||||
} | |||||
} | |||||
void | |||||
tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, | tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, | ||||
struct tcpcb *tp, int drop_hdrlen, int tlen, uint8_t iptos) | struct tcpcb *tp, int drop_hdrlen, int tlen, uint8_t iptos) | ||||
{ | { | ||||
int thflags, acked, ourfinisacked, needoutput = 0, sack_changed; | int thflags, acked, ourfinisacked, needoutput = 0, sack_changed; | ||||
int rstreason, todrop, win, incforsyn = 0; | int rstreason, todrop, win, incforsyn = 0; | ||||
uint32_t tiwin; | uint32_t tiwin; | ||||
uint16_t nsegs; | uint16_t nsegs; | ||||
char *s; | char *s; | ||||
▲ Show 20 Lines • Show All 334 Lines • ▼ Show 20 Lines | #ifdef TCPDEBUG | ||||
&tcp_savetcp, 0); | &tcp_savetcp, 0); | ||||
#endif | #endif | ||||
TCP_PROBE3(debug__input, tp, th, m); | TCP_PROBE3(debug__input, tp, th, m); | ||||
if (tp->snd_una == tp->snd_max) | if (tp->snd_una == tp->snd_max) | ||||
tcp_timer_activate(tp, TT_REXMT, 0); | tcp_timer_activate(tp, TT_REXMT, 0); | ||||
else if (!tcp_timer_active(tp, TT_PERSIST)) | else if (!tcp_timer_active(tp, TT_PERSIST)) | ||||
tcp_timer_activate(tp, TT_REXMT, | tcp_timer_activate(tp, TT_REXMT, | ||||
tp->t_rxtcur); | tp->t_rxtcur); | ||||
sowwakeup(so); | tp->t_flags |= TF_WAKESOW; | ||||
if (sbavail(&so->so_snd)) | if (sbavail(&so->so_snd)) | ||||
(void) tp->t_fb->tfb_tcp_output(tp); | (void) tp->t_fb->tfb_tcp_output(tp); | ||||
goto check_delack; | goto check_delack; | ||||
} | } | ||||
} else if (th->th_ack == tp->snd_una && | } else if (th->th_ack == tp->snd_una && | ||||
tlen <= sbspace(&so->so_rcv)) { | tlen <= sbspace(&so->so_rcv)) { | ||||
int newsize = 0; /* automatic sockbuf scaling */ | int newsize = 0; /* automatic sockbuf scaling */ | ||||
Show All 39 Lines | #endif | ||||
*/ | */ | ||||
if (newsize) | if (newsize) | ||||
if (!sbreserve_locked(&so->so_rcv, | if (!sbreserve_locked(&so->so_rcv, | ||||
newsize, so, NULL)) | newsize, so, NULL)) | ||||
so->so_rcv.sb_flags &= ~SB_AUTOSIZE; | so->so_rcv.sb_flags &= ~SB_AUTOSIZE; | ||||
m_adj(m, drop_hdrlen); /* delayed header drop */ | m_adj(m, drop_hdrlen); /* delayed header drop */ | ||||
sbappendstream_locked(&so->so_rcv, m, 0); | sbappendstream_locked(&so->so_rcv, m, 0); | ||||
} | } | ||||
/* NB: sorwakeup_locked() does an implicit unlock. */ | SOCKBUF_UNLOCK(&so->so_rcv); | ||||
sorwakeup_locked(so); | tp->t_flags |= TF_WAKESOR; | ||||
if (DELAY_ACK(tp, tlen)) { | if (DELAY_ACK(tp, tlen)) { | ||||
tp->t_flags |= TF_DELACK; | tp->t_flags |= TF_DELACK; | ||||
} else { | } else { | ||||
tp->t_flags |= TF_ACKNOW; | tp->t_flags |= TF_ACKNOW; | ||||
tp->t_fb->tfb_tcp_output(tp); | tp->t_fb->tfb_tcp_output(tp); | ||||
} | } | ||||
goto check_delack; | goto check_delack; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 914 Lines • ▼ Show 20 Lines | process_ACK: | ||||
} else { | } else { | ||||
mfree = sbcut_locked(&so->so_snd, acked); | mfree = sbcut_locked(&so->so_snd, acked); | ||||
if (tp->snd_wnd >= (uint32_t) acked) | if (tp->snd_wnd >= (uint32_t) acked) | ||||
tp->snd_wnd -= acked; | tp->snd_wnd -= acked; | ||||
else | else | ||||
tp->snd_wnd = 0; | tp->snd_wnd = 0; | ||||
ourfinisacked = 0; | ourfinisacked = 0; | ||||
} | } | ||||
/* NB: sowwakeup_locked() does an implicit unlock. */ | SOCKBUF_UNLOCK(&so->so_snd); | ||||
sowwakeup_locked(so); | tp->t_flags |= TF_WAKESOW; | ||||
m_freem(mfree); | m_freem(mfree); | ||||
/* Detect una wraparound. */ | /* Detect una wraparound. */ | ||||
if (!IN_RECOVERY(tp->t_flags) && | if (!IN_RECOVERY(tp->t_flags) && | ||||
SEQ_GT(tp->snd_una, tp->snd_recover) && | SEQ_GT(tp->snd_una, tp->snd_recover) && | ||||
SEQ_LEQ(th->th_ack, tp->snd_recover)) | SEQ_LEQ(th->th_ack, tp->snd_recover)) | ||||
tp->snd_recover = th->th_ack - 1; | tp->snd_recover = th->th_ack - 1; | ||||
/* XXXLAS: Can this be moved up into cc_post_recovery? */ | /* XXXLAS: Can this be moved up into cc_post_recovery? */ | ||||
if (IN_RECOVERY(tp->t_flags) && | if (IN_RECOVERY(tp->t_flags) && | ||||
▲ Show 20 Lines • Show All 196 Lines • ▼ Show 20 Lines | if (th->th_seq == tp->rcv_nxt && | ||||
thflags = th->th_flags & TH_FIN; | thflags = th->th_flags & TH_FIN; | ||||
TCPSTAT_INC(tcps_rcvpack); | TCPSTAT_INC(tcps_rcvpack); | ||||
TCPSTAT_ADD(tcps_rcvbyte, tlen); | TCPSTAT_ADD(tcps_rcvbyte, tlen); | ||||
SOCKBUF_LOCK(&so->so_rcv); | SOCKBUF_LOCK(&so->so_rcv); | ||||
if (so->so_rcv.sb_state & SBS_CANTRCVMORE) | if (so->so_rcv.sb_state & SBS_CANTRCVMORE) | ||||
m_freem(m); | m_freem(m); | ||||
else | else | ||||
sbappendstream_locked(&so->so_rcv, m, 0); | sbappendstream_locked(&so->so_rcv, m, 0); | ||||
/* NB: sorwakeup_locked() does an implicit unlock. */ | SOCKBUF_UNLOCK(&so->so_rcv); | ||||
sorwakeup_locked(so); | tp->t_flags |= TF_WAKESOR; | ||||
} else { | } else { | ||||
/* | /* | ||||
* XXX: Due to the header drop above "th" is | * XXX: Due to the header drop above "th" is | ||||
* theoretically invalid by now. Fortunately | * theoretically invalid by now. Fortunately | ||||
* m_adj() doesn't actually frees any mbufs | * m_adj() doesn't actually frees any mbufs | ||||
* when trimming from the head. | * when trimming from the head. | ||||
*/ | */ | ||||
tcp_seq temp = save_start; | tcp_seq temp = save_start; | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | #endif | ||||
/* | /* | ||||
* If FIN is received ACK the FIN and let the user know | * If FIN is received ACK the FIN and let the user know | ||||
* that the connection is closing. | * that the connection is closing. | ||||
*/ | */ | ||||
if (thflags & TH_FIN) { | if (thflags & TH_FIN) { | ||||
if (TCPS_HAVERCVDFIN(tp->t_state) == 0) { | if (TCPS_HAVERCVDFIN(tp->t_state) == 0) { | ||||
socantrcvmore(so); | socantrcvmore(so); | ||||
/* The socket upcall is handled by socantrcvmore. */ | |||||
tp->t_flags &= ~TF_WAKESOR; | |||||
/* | /* | ||||
* If connection is half-synchronized | * If connection is half-synchronized | ||||
* (ie NEEDSYN flag on) then delay ACK, | * (ie NEEDSYN flag on) then delay ACK, | ||||
* so it may be piggybacked when SYN is sent. | * so it may be piggybacked when SYN is sent. | ||||
* Otherwise, since we received a FIN then no | * Otherwise, since we received a FIN then no | ||||
* more input can be expected, send ACK now. | * more input can be expected, send ACK now. | ||||
*/ | */ | ||||
if (tp->t_flags & TF_NEEDSYN) | if (tp->t_flags & TF_NEEDSYN) | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | |||||
check_delack: | check_delack: | ||||
INP_WLOCK_ASSERT(tp->t_inpcb); | INP_WLOCK_ASSERT(tp->t_inpcb); | ||||
if (tp->t_flags & TF_DELACK) { | if (tp->t_flags & TF_DELACK) { | ||||
tp->t_flags &= ~TF_DELACK; | tp->t_flags &= ~TF_DELACK; | ||||
tcp_timer_activate(tp, TT_DELACK, tcp_delacktime); | tcp_timer_activate(tp, TT_DELACK, tcp_delacktime); | ||||
} | } | ||||
INP_WUNLOCK(tp->t_inpcb); | INP_WUNLOCK(tp->t_inpcb); | ||||
tcp_handle_wakeup(tp, so); | |||||
return; | return; | ||||
dropafterack: | dropafterack: | ||||
/* | /* | ||||
* Generate an ACK dropping incoming segment if it occupies | * Generate an ACK dropping incoming segment if it occupies | ||||
* sequence space, where the ACK reflects our state. | * sequence space, where the ACK reflects our state. | ||||
* | * | ||||
* We can now skip the test for the RST flag since all | * We can now skip the test for the RST flag since all | ||||
Show All 17 Lines | #ifdef TCPDEBUG | ||||
if (so->so_options & SO_DEBUG) | if (so->so_options & SO_DEBUG) | ||||
tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen, | tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen, | ||||
&tcp_savetcp, 0); | &tcp_savetcp, 0); | ||||
#endif | #endif | ||||
TCP_PROBE3(debug__input, tp, th, m); | TCP_PROBE3(debug__input, tp, th, m); | ||||
tp->t_flags |= TF_ACKNOW; | tp->t_flags |= TF_ACKNOW; | ||||
(void) tp->t_fb->tfb_tcp_output(tp); | (void) tp->t_fb->tfb_tcp_output(tp); | ||||
INP_WUNLOCK(tp->t_inpcb); | INP_WUNLOCK(tp->t_inpcb); | ||||
tcp_handle_wakeup(tp, so); | |||||
m_freem(m); | m_freem(m); | ||||
return; | return; | ||||
dropwithreset: | dropwithreset: | ||||
if (tp != NULL) { | if (tp != NULL) { | ||||
tcp_dropwithreset(m, th, tp, tlen, rstreason); | tcp_dropwithreset(m, th, tp, tlen, rstreason); | ||||
INP_WUNLOCK(tp->t_inpcb); | INP_WUNLOCK(tp->t_inpcb); | ||||
tcp_handle_wakeup(tp, so); | |||||
} else | } else | ||||
tcp_dropwithreset(m, th, NULL, tlen, rstreason); | tcp_dropwithreset(m, th, NULL, tlen, rstreason); | ||||
return; | return; | ||||
drop: | drop: | ||||
/* | /* | ||||
* Drop space held by incoming segment and return. | * Drop space held by incoming segment and return. | ||||
*/ | */ | ||||
#ifdef TCPDEBUG | #ifdef TCPDEBUG | ||||
if (tp == NULL || (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) | if (tp == NULL || (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) | ||||
tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen, | tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen, | ||||
&tcp_savetcp, 0); | &tcp_savetcp, 0); | ||||
#endif | #endif | ||||
TCP_PROBE3(debug__input, tp, th, m); | TCP_PROBE3(debug__input, tp, th, m); | ||||
if (tp != NULL) | if (tp != NULL) { | ||||
INP_WUNLOCK(tp->t_inpcb); | INP_WUNLOCK(tp->t_inpcb); | ||||
tcp_handle_wakeup(tp, so); | |||||
} | |||||
m_freem(m); | m_freem(m); | ||||
} | } | ||||
/* | /* | ||||
* Issue RST and make ACK acceptable to originator of segment. | * Issue RST and make ACK acceptable to originator of segment. | ||||
* The mbuf must still include the original packet header. | * The mbuf must still include the original packet header. | ||||
* tp may be NULL. | * tp may be NULL. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 645 Lines • Show Last 20 Lines |
How is it safe to reference the tp like this without a lock?
I think if you are wrong it just means you will do a wakeup (possibly) when
you should not.. so I guess thats ok.. note the same comment applies to
the so_state check since you don't hold a socket lock either.