From: Matthew Dillon Date: Mon, 13 Sep 2010 07:08:53 +0000 (-0700) Subject: network - Fix multiple MP races (2) X-Git-Tag: v2.9.0~214 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/5dfe1a1a7441f844628c48f94bd14b0742970cba network - Fix multiple MP races (2) * MEVENT signaling needs the ssb_token as well as the kq_token for now to prevent blocking inside the predicate. This is a hack for now. * Add missing porttoken protection in in_pcbremlists(). Reported-by: lentferj --- diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c index 11d95a012f..f4c6bc93b8 100644 --- a/sys/kern/uipc_socket2.c +++ b/sys/kern/uipc_socket2.c @@ -247,6 +247,10 @@ soisconnected(struct socket *so) soclrstate(so, SS_INCOMP); lwkt_reltoken(&head->so_rcv.ssb_token); + /* + * XXX head may be on a different protocol thread. + * sorwakeup()->sowakeup() is hacked atm. + */ sorwakeup(head); wakeup_one(&head->so_timeo); } else { @@ -396,6 +400,10 @@ sonewconn(struct socket *head, int connstatus) } lwkt_reltoken(&head->so_rcv.ssb_token); if (connstatus) { + /* + * XXX head may be on a different protocol thread. + * sorwakeup()->sowakeup() is hacked atm. + */ sorwakeup(head); wakeup((caddr_t)&head->so_timeo); sosetstate(so, connstatus); @@ -434,6 +442,10 @@ socantrcvmore(struct socket *so) * For users waiting on send/recv try to avoid unnecessary context switch * thrashing. Particularly for senders of large buffers (needs to be * extended to sel and aio? XXX) + * + * WARNING! Can be called on a foreign socket from the wrong protocol + * thread. aka is called on the 'head' listen socket when + * a new connection comes in. */ void sowakeup(struct socket *so, struct signalsockbuf *ssb) @@ -476,10 +488,17 @@ sowakeup(struct socket *so, struct signalsockbuf *ssb) if (ssb->ssb_flags & SSB_AIO) aio_swake(so, ssb); KNOTE(&kqinfo->ki_note, 0); + + /* + * This is a bit of a hack. Multiple threads can wind up scanning + * ki_mlist concurrently due to the fact that this function can be + * called on a foreign socket, so we can't afford to block here. + */ if (ssb->ssb_flags & SSB_MEVENT) { struct netmsg_so_notify *msg, *nmsg; lwkt_gettoken(&kq_token); + lwkt_gettoken_hard(&ssb->ssb_token); TAILQ_FOREACH_MUTABLE(msg, &kqinfo->ki_mlist, nm_list, nmsg) { if (msg->nm_predicate(&msg->nm_netmsg)) { TAILQ_REMOVE(&kqinfo->ki_mlist, msg, nm_list); @@ -489,6 +508,7 @@ sowakeup(struct socket *so, struct signalsockbuf *ssb) } if (TAILQ_EMPTY(&ssb->ssb_kq.ki_mlist)) atomic_clear_int(&ssb->ssb_flags, SSB_MEVENT); + lwkt_reltoken_hard(&ssb->ssb_token); lwkt_reltoken(&kq_token); } } diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index fe9f0edeaa..3e07cd4934 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1265,14 +1265,23 @@ in_pcbremwildcardhash(struct inpcb *inp) void in_pcbremlists(struct inpcb *inp) { + struct inpcbinfo *pcbinfo; + if (inp->inp_lport) { - struct inpcbport *phd = inp->inp_phd; + struct inpcbport *phd; + + pcbinfo = inp->inp_pcbinfo; + if (pcbinfo->porttoken) + lwkt_gettoken(pcbinfo->porttoken); + phd = inp->inp_phd; LIST_REMOVE(inp, inp_portlist); if (LIST_FIRST(&phd->phd_pcblist) == NULL) { LIST_REMOVE(phd, phd_hash); kfree(phd, M_PCB); } + if (pcbinfo->porttoken) + lwkt_reltoken(pcbinfo->porttoken); } if (inp->inp_flags & INP_WILDCARD) { in_pcbremwildcardhash(inp);