Date: Wed, 9 Jun 2010 16:42:42 +0000 (UTC) From: Randall Stewart <rrs@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r208953 - head/sys/netinet Message-ID: <201006091642.o59Ggg0u078786@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: rrs Date: Wed Jun 9 16:42:42 2010 New Revision: 208953 URL: http://svn.freebsd.org/changeset/base/208953 Log: Fix serveral bugs all having to do with freeing an sctp_inpcb: 1) Make sure not to remove the flag on the PCB until after the close() caller is back in control with the lock. Otherwise a quickly freeing assoc could kill the inpcb and cause a panic. 2) Make sure all calls to log_closing have not released the locks before calling the log function, we don't want the logging function to crash us due to a freed inpcb. 3) Make sure that when we get to the end, we release all locks (after removing them from view) and as long as we are NOT the inp-kill timer removing the inp, call the callout_drain() function so a racing timer won't later call in and cause a racing crash. MFC after: 1 week Modified: head/sys/netinet/sctp_pcb.c Modified: head/sys/netinet/sctp_pcb.c ============================================================================== --- head/sys/netinet/sctp_pcb.c Wed Jun 9 16:39:18 2010 (r208952) +++ head/sys/netinet/sctp_pcb.c Wed Jun 9 16:42:42 2010 (r208953) @@ -3114,12 +3114,6 @@ sctp_inpcb_free(struct sctp_inpcb *inp, * via the sockets layer. */ SCTP_ITERATOR_LOCK(); - inp->sctp_flags &= ~SCTP_PCB_FLAGS_CLOSE_IP; - /* socket is gone, so no more wakeups allowed */ - inp->sctp_flags |= SCTP_PCB_FLAGS_DONT_WAKE; - inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEINPUT; - inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEOUTPUT; - /* mark any iterators on the list or being processed */ sctp_iterator_inp_being_freed(inp); SCTP_ITERATOR_UNLOCK(); @@ -3137,6 +3131,14 @@ sctp_inpcb_free(struct sctp_inpcb *inp, SCTP_INP_INFO_WLOCK(); SCTP_INP_WLOCK(inp); + if (from == SCTP_CALLED_AFTER_CMPSET_OFCLOSE) { + inp->sctp_flags &= ~SCTP_PCB_FLAGS_CLOSE_IP; + /* socket is gone, so no more wakeups allowed */ + inp->sctp_flags |= SCTP_PCB_FLAGS_DONT_WAKE; + inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEINPUT; + inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEOUTPUT; + + } /* First time through we have the socket lock, after that no more. */ sctp_timer_stop(SCTP_TIMER_TYPE_NEWCOOKIE, inp, NULL, NULL, SCTP_FROM_SCTP_PCB + SCTP_LOC_1); @@ -3334,13 +3336,13 @@ sctp_inpcb_free(struct sctp_inpcb *inp, } /* now is there some left in our SHUTDOWN state? */ if (cnt_in_sd) { - SCTP_INP_WUNLOCK(inp); - SCTP_ASOC_CREATE_UNLOCK(inp); - SCTP_INP_INFO_WUNLOCK(); #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 2); #endif inp->sctp_socket = NULL; + SCTP_INP_WUNLOCK(inp); + SCTP_ASOC_CREATE_UNLOCK(inp); + SCTP_INP_INFO_WUNLOCK(); return; } } @@ -3415,12 +3417,12 @@ sctp_inpcb_free(struct sctp_inpcb *inp, if (cnt) { /* Ok we have someone out there that will kill us */ (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer); - SCTP_INP_WUNLOCK(inp); - SCTP_ASOC_CREATE_UNLOCK(inp); - SCTP_INP_INFO_WUNLOCK(); #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 3); #endif + SCTP_INP_WUNLOCK(inp); + SCTP_ASOC_CREATE_UNLOCK(inp); + SCTP_INP_INFO_WUNLOCK(); return; } if (SCTP_INP_LOCK_CONTENDED(inp)) @@ -3434,26 +3436,43 @@ sctp_inpcb_free(struct sctp_inpcb *inp, (being_refed) || (inp->sctp_flags & SCTP_PCB_FLAGS_CLOSE_IP)) { (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer); +#ifdef SCTP_LOG_CLOSING + sctp_log_closing(inp, NULL, 4); +#endif sctp_timer_start(SCTP_TIMER_TYPE_INPKILL, inp, NULL, NULL); SCTP_INP_WUNLOCK(inp); SCTP_ASOC_CREATE_UNLOCK(inp); SCTP_INP_INFO_WUNLOCK(); -#ifdef SCTP_LOG_CLOSING - sctp_log_closing(inp, NULL, 4); -#endif return; } - (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer); inp->sctp_ep.signature_change.type = 0; inp->sctp_flags |= SCTP_PCB_FLAGS_SOCKET_ALLGONE; + /* + * Remove it from the list .. last thing we need a lock for. + */ + LIST_REMOVE(inp, sctp_list); + SCTP_INP_WUNLOCK(inp); + SCTP_ASOC_CREATE_UNLOCK(inp); + SCTP_INP_INFO_WUNLOCK(); + /* + * Now we release all locks. Since this INP cannot be found anymore + * except possbily by the kill timer that might be running. We call + * the drain function here. It should hit the case were it sees the + * ACTIVE flag cleared and exit out freeing us to proceed and + * destroy everything. + */ + if (from != SCTP_CALLED_FROM_INPKILL_TIMER) { + (void)SCTP_OS_TIMER_STOP_DRAIN(&inp->sctp_ep.signature_change.timer); + } else { + /* Probably un-needed */ + (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer); + } #ifdef SCTP_LOG_CLOSING sctp_log_closing(inp, NULL, 5); #endif - (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer); - inp->sctp_ep.signature_change.type = SCTP_TIMER_TYPE_NONE; - /* Clear the read queue */ + if ((inp->sctp_asocidhash) != NULL) { SCTP_HASH_FREE(inp->sctp_asocidhash, inp->hashasocidmark); inp->sctp_asocidhash = NULL; @@ -3524,8 +3543,6 @@ sctp_inpcb_free(struct sctp_inpcb *inp, shared_key = LIST_FIRST(&inp->sctp_ep.shared_keys); } - LIST_REMOVE(inp, sctp_list); - /* * if we have an address list the following will free the list of * ifaddr's that are set into this ep. Again macro limitations here, @@ -3558,7 +3575,6 @@ sctp_inpcb_free(struct sctp_inpcb *inp, SCTP_INP_LOCK_DESTROY(inp); SCTP_INP_READ_DESTROY(inp); SCTP_ASOC_CREATE_LOCK_DESTROY(inp); - SCTP_INP_INFO_WUNLOCK(); SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp); SCTP_DECR_EP_COUNT(); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201006091642.o59Ggg0u078786>