Author: tuexen
Date: Tue Feb 16 19:36:25 2016
New Revision: 295668
URL: https://svnweb.freebsd.org/changeset/base/295668

Log:
  Improve the teardown of the SCTP stack.
  
  Obtained from:        bz@
  MFC after: 1 week

Modified:
  head/sys/netinet/sctp_asconf.c
  head/sys/netinet/sctp_bsd_addr.c
  head/sys/netinet/sctp_pcb.c
  head/sys/netinet/sctp_pcb.h
  head/sys/netinet/sctputil.c

Modified: head/sys/netinet/sctp_asconf.c
==============================================================================
--- head/sys/netinet/sctp_asconf.c      Tue Feb 16 19:11:17 2016        
(r295667)
+++ head/sys/netinet/sctp_asconf.c      Tue Feb 16 19:36:25 2016        
(r295668)
@@ -3248,6 +3248,7 @@ sctp_addr_mgmt_ep_sa(struct sctp_inpcb *
                } else {
                        struct sctp_asconf_iterator *asc;
                        struct sctp_laddr *wi;
+                       int ret;
 
                        SCTP_MALLOC(asc, struct sctp_asconf_iterator *,
                            sizeof(struct sctp_asconf_iterator),
@@ -3269,7 +3270,7 @@ sctp_addr_mgmt_ep_sa(struct sctp_inpcb *
                        wi->action = type;
                        atomic_add_int(&ifa->refcount, 1);
                        LIST_INSERT_HEAD(&asc->list_of_work, wi, sctp_nxt_addr);
-                       (void)sctp_initiate_iterator(sctp_asconf_iterator_ep,
+                       ret = sctp_initiate_iterator(sctp_asconf_iterator_ep,
                            sctp_asconf_iterator_stcb,
                            sctp_asconf_iterator_ep_end,
                            SCTP_PCB_ANY_FLAGS,
@@ -3277,6 +3278,12 @@ sctp_addr_mgmt_ep_sa(struct sctp_inpcb *
                            SCTP_ASOC_ANY_STATE,
                            (void *)asc, 0,
                            sctp_asconf_iterator_end, inp, 0);
+                       if (ret)  {
+                               SCTP_PRINTF("Failed to initiate iterator for 
addr_mgmt_ep_sa\n");
+                               SCTP_LTRACE_ERR_RET(inp, NULL, NULL, 
SCTP_FROM_SCTP_ASCONF, EFAULT);
+                               sctp_asconf_iterator_end(asc, 0);
+                               return (EFAULT);
+                       }
                }
                return (0);
        } else {

Modified: head/sys/netinet/sctp_bsd_addr.c
==============================================================================
--- head/sys/netinet/sctp_bsd_addr.c    Tue Feb 16 19:11:17 2016        
(r295667)
+++ head/sys/netinet/sctp_bsd_addr.c    Tue Feb 16 19:36:25 2016        
(r295668)
@@ -293,6 +293,10 @@ sctp_addr_change(struct ifaddr *ifa, int
 {
        uint32_t ifa_flags = 0;
 
+       if (SCTP_BASE_VAR(sctp_pcb_initialized) == 0) {
+               return;
+       }
+
        /*
         * BSD only has one VRF, if this changes we will need to hook in the
         * right things here to get the id to pass to the address managment

Modified: head/sys/netinet/sctp_pcb.c
==============================================================================
--- head/sys/netinet/sctp_pcb.c Tue Feb 16 19:11:17 2016        (r295667)
+++ head/sys/netinet/sctp_pcb.c Tue Feb 16 19:36:25 2016        (r295668)
@@ -2781,8 +2781,45 @@ sctp_move_pcb_and_assoc(struct sctp_inpc
        SCTP_INP_WUNLOCK(old_inp);
 }
 
+/*
+ * insert an laddr entry with the given ifa for the desired list
+ */
+static int
+sctp_insert_laddr(struct sctpladdr *list, struct sctp_ifa *ifa, uint32_t act)
+{
+       struct sctp_laddr *laddr;
 
+       laddr = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct 
sctp_laddr);
+       if (laddr == NULL) {
+               /* out of memory? */
+               SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_PCB, 
EINVAL);
+               return (EINVAL);
+       }
+       SCTP_INCR_LADDR_COUNT();
+       bzero(laddr, sizeof(*laddr));
+       (void)SCTP_GETTIME_TIMEVAL(&laddr->start_time);
+       laddr->ifa = ifa;
+       laddr->action = act;
+       atomic_add_int(&ifa->refcount, 1);
+       /* insert it */
+       LIST_INSERT_HEAD(list, laddr, sctp_nxt_addr);
 
+       return (0);
+}
+
+/*
+ * Remove an laddr entry from the local address list (on an assoc)
+ */
+static void
+sctp_remove_laddr(struct sctp_laddr *laddr)
+{
+
+       /* remove from the list */
+       LIST_REMOVE(laddr, sctp_nxt_addr);
+       sctp_free_ifa(laddr->ifa);
+       SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_laddr), laddr);
+       SCTP_DECR_LADDR_COUNT();
+}
 
 /* sctp_ifap is used to bypass normal local address validation checks */
 int
@@ -5509,46 +5546,6 @@ sctp_add_local_addr_restricted(struct sc
 }
 
 /*
- * insert an laddr entry with the given ifa for the desired list
- */
-int
-sctp_insert_laddr(struct sctpladdr *list, struct sctp_ifa *ifa, uint32_t act)
-{
-       struct sctp_laddr *laddr;
-
-       laddr = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct 
sctp_laddr);
-       if (laddr == NULL) {
-               /* out of memory? */
-               SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_PCB, 
EINVAL);
-               return (EINVAL);
-       }
-       SCTP_INCR_LADDR_COUNT();
-       bzero(laddr, sizeof(*laddr));
-       (void)SCTP_GETTIME_TIMEVAL(&laddr->start_time);
-       laddr->ifa = ifa;
-       laddr->action = act;
-       atomic_add_int(&ifa->refcount, 1);
-       /* insert it */
-       LIST_INSERT_HEAD(list, laddr, sctp_nxt_addr);
-
-       return (0);
-}
-
-/*
- * Remove an laddr entry from the local address list (on an assoc)
- */
-void
-sctp_remove_laddr(struct sctp_laddr *laddr)
-{
-
-       /* remove from the list */
-       LIST_REMOVE(laddr, sctp_nxt_addr);
-       sctp_free_ifa(laddr->ifa);
-       SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_laddr), laddr);
-       SCTP_DECR_LADDR_COUNT();
-}
-
-/*
  * Remove a local address from the TCB local address restricted list
  */
 void
@@ -5918,12 +5915,33 @@ sctp_pcb_finish(void)
        int i;
        struct sctp_iterator *it, *nit;
 
+       if (SCTP_BASE_VAR(sctp_pcb_initialized) == 0) {
+               SCTP_PRINTF("%s: race condition on teardown.\n", __func__);
+               return;
+       }
+       SCTP_BASE_VAR(sctp_pcb_initialized) = 0;
+
        /*
         * In FreeBSD the iterator thread never exits but we do clean up.
         * The only way FreeBSD reaches here is if we have VRF's but we
         * still add the ifdef to make it compile on old versions.
         */
+retry:
        SCTP_IPI_ITERATOR_WQ_LOCK();
+       /*
+        * sctp_iterator_worker() might be working on an it entry without
+        * holding the lock.  We won't find it on the list either and
+        * continue and free/destroy it.  While holding the lock, spin, to
+        * avoid the race condition as sctp_iterator_worker() will have to
+        * wait to re-aquire the lock.
+        */
+       if (sctp_it_ctl.iterator_running != 0 || sctp_it_ctl.cur_it != NULL) {
+               SCTP_IPI_ITERATOR_WQ_UNLOCK();
+               SCTP_PRINTF("%s: Iterator running while we held the lock. 
Retry. "
+                   "cur_it=%p\n", __func__, sctp_it_ctl.cur_it);
+               DELAY(10);
+               goto retry;
+       }
        TAILQ_FOREACH_SAFE(it, &sctp_it_ctl.iteratorhead, sctp_nxt_itr, nit) {
                if (it->vn != curvnet) {
                        continue;
@@ -5941,7 +5959,7 @@ sctp_pcb_finish(void)
                sctp_it_ctl.iterator_flags |= SCTP_ITERATOR_STOP_CUR_IT;
        }
        SCTP_ITERATOR_UNLOCK();
-       SCTP_OS_TIMER_STOP(&SCTP_BASE_INFO(addr_wq_timer.timer));
+       SCTP_OS_TIMER_STOP_DRAIN(&SCTP_BASE_INFO(addr_wq_timer.timer));
        SCTP_WQ_ADDR_LOCK();
        LIST_FOREACH_SAFE(wi, &SCTP_BASE_INFO(addr_wq), sctp_nxt_addr, nwi) {
                LIST_REMOVE(wi, sctp_nxt_addr);
@@ -6008,6 +6026,14 @@ sctp_pcb_finish(void)
 
        SCTP_WQ_ADDR_DESTROY();
 
+       /* Get rid of other stuff too. */
+       if (SCTP_BASE_INFO(sctp_asochash) != NULL)
+               SCTP_HASH_FREE(SCTP_BASE_INFO(sctp_asochash), 
SCTP_BASE_INFO(hashasocmark));
+       if (SCTP_BASE_INFO(sctp_ephash) != NULL)
+               SCTP_HASH_FREE(SCTP_BASE_INFO(sctp_ephash), 
SCTP_BASE_INFO(hashmark));
+       if (SCTP_BASE_INFO(sctp_tcpephash) != NULL)
+               SCTP_HASH_FREE(SCTP_BASE_INFO(sctp_tcpephash), 
SCTP_BASE_INFO(hashtcpmark));
+
        SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_ep));
        SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_asoc));
        SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_laddr));
@@ -6017,13 +6043,7 @@ sctp_pcb_finish(void)
        SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_strmoq));
        SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_asconf));
        SCTP_ZONE_DESTROY(SCTP_BASE_INFO(ipi_zone_asconf_ack));
-       /* Get rid of other stuff to */
-       if (SCTP_BASE_INFO(sctp_asochash) != NULL)
-               SCTP_HASH_FREE(SCTP_BASE_INFO(sctp_asochash), 
SCTP_BASE_INFO(hashasocmark));
-       if (SCTP_BASE_INFO(sctp_ephash) != NULL)
-               SCTP_HASH_FREE(SCTP_BASE_INFO(sctp_ephash), 
SCTP_BASE_INFO(hashmark));
-       if (SCTP_BASE_INFO(sctp_tcpephash) != NULL)
-               SCTP_HASH_FREE(SCTP_BASE_INFO(sctp_tcpephash), 
SCTP_BASE_INFO(hashtcpmark));
+
 #if defined(__FreeBSD__) && defined(SMP) && defined(SCTP_USE_PERCPU_STAT)
        SCTP_FREE(SCTP_BASE_STATS, SCTP_M_MCORE);
 #endif
@@ -6987,6 +7007,11 @@ sctp_initiate_iterator(inp_func inpf,
        if (af == NULL) {
                return (-1);
        }
+       if (SCTP_BASE_VAR(sctp_pcb_initialized) == 0) {
+               SCTP_PRINTF("%s: abort on initialize being %d\n", __func__,
+                   SCTP_BASE_VAR(sctp_pcb_initialized));
+               return (-1);
+       }
        SCTP_MALLOC(it, struct sctp_iterator *, sizeof(struct sctp_iterator),
            SCTP_M_ITER);
        if (it == NULL) {
@@ -7025,6 +7050,13 @@ sctp_initiate_iterator(inp_func inpf,
 
        }
        SCTP_IPI_ITERATOR_WQ_LOCK();
+       if (SCTP_BASE_VAR(sctp_pcb_initialized) == 0) {
+               SCTP_IPI_ITERATOR_WQ_UNLOCK();
+               SCTP_PRINTF("%s: rollback on initialize being %d it=%p\n", 
__func__,
+                   SCTP_BASE_VAR(sctp_pcb_initialized), it);
+               SCTP_FREE(it, SCTP_M_ITER);
+               return (-1);
+       }
 
        TAILQ_INSERT_TAIL(&sctp_it_ctl.iteratorhead, it, sctp_nxt_itr);
        if (sctp_it_ctl.iterator_running == 0) {

Modified: head/sys/netinet/sctp_pcb.h
==============================================================================
--- head/sys/netinet/sctp_pcb.h Tue Feb 16 19:11:17 2016        (r295667)
+++ head/sys/netinet/sctp_pcb.h Tue Feb 16 19:36:25 2016        (r295668)
@@ -598,10 +598,6 @@ void
 
 void sctp_add_local_addr_ep(struct sctp_inpcb *, struct sctp_ifa *, uint32_t);
 
-int sctp_insert_laddr(struct sctpladdr *, struct sctp_ifa *, uint32_t);
-
-void sctp_remove_laddr(struct sctp_laddr *);
-
 void sctp_del_local_addr_ep(struct sctp_inpcb *, struct sctp_ifa *);
 
 int sctp_add_remote_addr(struct sctp_tcb *, struct sockaddr *, struct 
sctp_nets **, int, int);

Modified: head/sys/netinet/sctputil.c
==============================================================================
--- head/sys/netinet/sctputil.c Tue Feb 16 19:11:17 2016        (r295667)
+++ head/sys/netinet/sctputil.c Tue Feb 16 19:36:25 2016        (r295668)
@@ -1470,7 +1470,9 @@ sctp_handle_addr_wq(void)
        if (asc->cnt == 0) {
                SCTP_FREE(asc, SCTP_M_ASC_IT);
        } else {
-               (void)sctp_initiate_iterator(sctp_asconf_iterator_ep,
+               int ret;
+
+               ret = sctp_initiate_iterator(sctp_asconf_iterator_ep,
                    sctp_asconf_iterator_stcb,
                    NULL,       /* No ep end for boundall */
                    SCTP_PCB_FLAGS_BOUNDALL,
@@ -1478,6 +1480,20 @@ sctp_handle_addr_wq(void)
                    SCTP_ASOC_ANY_STATE,
                    (void *)asc, 0,
                    sctp_asconf_iterator_end, NULL, 0);
+               if (ret) {
+                       SCTP_PRINTF("Failed to initiate iterator for 
handle_addr_wq\n");
+                       /* Freeing if we are stopping or put back on the 
addr_wq. */
+                       if (SCTP_BASE_VAR(sctp_pcb_initialized) == 0) {
+                               sctp_asconf_iterator_end(asc, 0);
+                       } else {
+                               SCTP_WQ_ADDR_LOCK();
+                               LIST_FOREACH(wi, &asc->list_of_work, 
sctp_nxt_addr) {
+                                       
LIST_INSERT_HEAD(&SCTP_BASE_INFO(addr_wq), wi, sctp_nxt_addr);
+                               }
+                               SCTP_WQ_ADDR_UNLOCK();
+                               SCTP_FREE(asc, SCTP_M_ASC_IT);
+                       }
+               }
        }
 }
 
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to