panic in tcp_do_segment()
Peter Holm
peter at holm.cc
Fri Apr 12 15:43:37 UTC 2013
On Fri, Apr 12, 2013 at 10:14:40AM -0400, Juan Mojica wrote:
Glad I could help.
- Peter
> I'm a little late to get back to the email thread, but this is great to
> hear. Changes look good (assuming the goto drop is changed
> dropunlock). Thanks guys.
>
> On Tue, Apr 9, 2013 at 2:44 PM, Peter Holm <[1]peter at holm.cc> wrote:
>
> On Tue, Apr 09, 2013 at 10:35:30AM +0200, Andre Oppermann wrote:
> > On 09.04.2013 10:16, Peter Holm wrote:
> > > On Mon, Apr 08, 2013 at 02:13:40PM +0200, Andre Oppermann wrote:
> > >> On 05.04.2013 13:09, Matt Miller wrote:
> > >>> Hey Rick,
> > >>>
> > >>> I believe Juan and I have root caused this crash recently. The
> t_state =
> > >>> 0x1, TCPS_LISTEN, in the link provided at the time of the
> assertion.
> > >>>
> > >>> In tcp_input(), if we're in TCPS_LISTEN, SO_ACCEPTCONN should be
> set on the
> > >>> socket and we should never enter tcp_do_segment() for this state.
> I think
> > >>> if you look in your corefile, you'll see the socket *doesn't*
> have this
> > >>> flag set in your case.
> > >>>
> > >>> 1043 /*
> > >>> 1044 * When the socket is accepting connections (the
> INPCB is in
> > >>> LISTEN
> > >>> 1045 * state) we look into the SYN cache if this is a
> new
> > >>> connection
> > >>> 1046 * attempt or the completion of a previous one.
> Because listen
> > >>> 1047 * sockets are never in TCPS_ESTABLISHED, the
> V_tcbinfo lock
> > >>> will be
> > >>> 1048 * held in this case.
> > >>> 1049 */
> > >>> 1050 if (so->so_options & SO_ACCEPTCONN) {
> > >>> 1051 struct in_conninfo inc;
> > >>> 1052
> > >>> 1053 KASSERT(tp->t_state == TCPS_LISTEN, ("%s: so
> accepting
> > >>> but "
> > >>> 1054 "tp not listening", __func__));
> > >>> ...
> > >>> 1356 syncache_add(&inc, &to, th, inp, &so, m,
> NULL, NULL);
> > >>> 1357 /*
> > >>> 1358 * Entry added to syncache and mbuf
> consumed.
> > >>> 1359 * Everything already unlocked by
> syncache_add().
> > >>> 1360 */
> > >>> 1361 INP_INFO_UNLOCK_ASSERT(&V_tcbinfo);
> > >>> 1362 return;
> > >>> 1363 }
> > >>> ...
> > >>> 1384 /*
> > >>> 1385 * Segment belongs to a connection in SYN_SENT,
> ESTABLISHED or
> > >>> later
> > >>> 1386 * state. tcp_do_segment() always consumes the mbuf
> chain,
> > >>> unlocks
> > >>> 1387 * the inpcb, and unlocks pcbinfo.
> > >>> 1388 */
> > >>> 1389 tcp_do_segment(m, th, so, tp, drop_hdrlen, tlen,
> iptos,
> > >>> ti_locked);
> > >>>
> > >>> I think this has to do with this patch in soclose() where
> SO_ACCEPTCONN is
> > >>> being turned off in soclose(). I suspect if you look at the
> other threads
> > >>> in your corefile, you'll see one at this point in soclose()
> operating on
> > >>> the same socket as the one in the tcp_do_segment() thread.
> > >>>
> > >>> [2]http://svnweb.freebsd.org/base?view=revision&revision=243627
> > >>>
> > >>> 817 /*
> > >>> 818 * Prevent new additions to the accept
> queues due
> > >>> 819 * to ACCEPT_LOCK races while we are
> draining them.
> > >>> 820 */
> > >>> 821 so->so_options &= ~SO_ACCEPTCONN;
> > >>> 822 while ((sp = TAILQ_FIRST(&so->so_incomp))
> != NULL) {
> > >>> 823 TAILQ_REMOVE(&so->so_incomp, sp,
> so_list);
> > >>> 824 so->so_incqlen--;
> > >>> 825 sp->so_qstate &= ~SQ_INCOMP;
> > >>> 826 sp->so_head = NULL;
> > >>> 827 ACCEPT_UNLOCK();
> > >>> 828 soabort(sp);
> > >>> 829 ACCEPT_LOCK();
> > >>> 830 }
> > >>>
> > >>> Juan had evaluated this code path and it seemed safe to just drop
> the
> > >>> packet in this case:
> > >>>
> > >>> + /*
> > >>> + * In closing down the socket, the SO_ACCEPTCONN flag is
> removed to
> > >>> + * prevent new connections from being established. This
> means that
> > >>> + * any frames in that were in the midst of being processed
> could
> > >>> + * make it here. Need to just drop the frame.
> > >>> + */
> > >>> + if (TCPS_LISTEN == tp->t_state) {
> > >>> + TCPSTAT_INC(tcps_rcvwhileclosing);
> > >>> + goto drop;
> > >>> + }
> > >>> KASSERT(tp->t_state > TCPS_LISTEN, ("%s: TCPS_LISTEN",
> > >>> __func__));
> > >>>
> > >>> Or, if there's someone more familiar with the locking in these
> paths, they
> > >>> may be able to come up with a way to restructure the locks and
> logic to
> > >>> close this window.
> > >>
> > >> Matt, Juan,
> > >>
> > >> excellent analysis. I don't see a better approach to handle this
> > >> under the current ACCEPT_LOCK model.
> > >>
> > >> Compared to your patch I'd like to handle this race earlier before
> > >> we hit tcp_do_segment().
> > >>
> > >> Could you please review the attached patch which handles it right
> > >> after the SO_ACCEPTCONN / syncache check?
> > >>
> > >> --
> > >> Andre
> > >>
> > >> Index: netinet/tcp_input.c
> > >>
> ===================================================================
> > >> --- netinet/tcp_input.c (revision 249253)
> > >> +++ netinet/tcp_input.c (working copy)
> > >> @@ -1351,6 +1351,16 @@
> > >> */
> > >> INP_INFO_UNLOCK_ASSERT(&V_tcbinfo);
> > >> return;
> > >> + } else if (tp->t_state == TCPS_LISTEN) {
> > >> + /*
> > >> + * When a listen socket is torn down the SO_ACCEPTCONN
> > >> + * flag is removed first while connections are drained
> > >> + * from the accept queue in a unlock/lock cycle of the
> > >> + * ACCEPT_LOCK, opening a race condition allowing a SYN
> > >> + * attempt go through unhandled.
> > >> + */
> > >> + TCPSTAT_INC(tcps_rcvdwhileclosing);
> > >> + goto drop;
> > >> }
> > >>
> > >> #ifdef TCP_SIGNATURE
> > >
> > > I was able to reproduce the original "panic: tcp_do_segment:
> > > TCPS_LISTEN" with ease; see
> > > [3]http://people.freebsd.org/~pho/stress/log/tcp.txt.
> > >
> > > With your patch (minus the TCPSTAT_INC) I got this "panic: Lock
> (rw)
> > > tcp locked @ netinet/tcp_input.c:1432."
> > >
> > > [4]http://people.freebsd.org/~pho/stress/log/tcp2.txt
> >
> > Please replace the 'goto drop' with 'goto dropunlock' to fix the
> panic.
> >
>
> Yes, this seems to take care of the two problems reported. Tested
> for
> 5 hours without any repeats.
> - Peter
>
> --
> Juan Mojica
> Email: [5]jmojica at gmail.com
>
> References
>
> 1. mailto:peter at holm.cc
> 2. http://svnweb.freebsd.org/base?view=revision&revision=243627
> 3. http://people.freebsd.org/~pho/stress/log/tcp.txt
> 4. http://people.freebsd.org/~pho/stress/log/tcp2.txt
> 5. mailto:jmojica at gmail.com
More information about the freebsd-net
mailing list