The following reply was made to PR kern/165863; it has been noted by GNATS.

From: dfil...@freebsd.org (dfilter service)
To: bug-follo...@freebsd.org
Cc:  
Subject: Re: kern/165863: commit references a PR
Date: Thu,  2 Aug 2012 13:58:02 +0000 (UTC)

 Author: glebius
 Date: Thu Aug  2 13:57:49 2012
 New Revision: 238990
 URL: http://svn.freebsd.org/changeset/base/238990
 
 Log:
   Fix races between in_lltable_prefix_free(), lla_lookup(),
   llentry_free() and arptimer():
   
   o Use callout_init_rw() for lle timeout, this allows us safely
     disestablish them.
     - This allows us to simplify the arptimer() and make it
       race safe.
   o Consistently use ifp->if_afdata_lock to lock access to
     linked lists in the lle hashes.
   o Introduce new lle flag LLE_LINKED, which marks an entry that
     is attached to the hash.
     - Use LLE_LINKED to avoid double unlinking via consequent
       calls to llentry_free().
     - Mark lle with LLE_DELETED via |= operation istead of =,
       so that other flags won't be lost.
   o Make LLE_ADDREF(), LLE_REMREF() and LLE_FREE_LOCKED() more
     consistent and provide more informative KASSERTs.
   
   The patch is a collaborative work of all submitters and myself.
   
   PR:          kern/165863
   Submitted by:        Andrey Zonov <andrey zonov.org>
   Submitted by:        Ryan Stone <rysto32 gmail.com>
   Submitted by:        Eric van Gyzen <eric_van_gyzen dell.com>
 
 Modified:
   head/sys/net/if_llatbl.c
   head/sys/net/if_llatbl.h
   head/sys/net/if_var.h
   head/sys/netinet/if_ether.c
   head/sys/netinet/in.c
   head/sys/netinet6/in6.c
 
 Modified: head/sys/net/if_llatbl.c
 ==============================================================================
 --- head/sys/net/if_llatbl.c   Thu Aug  2 13:20:44 2012        (r238989)
 +++ head/sys/net/if_llatbl.c   Thu Aug  2 13:57:49 2012        (r238990)
 @@ -106,10 +106,19 @@ llentry_free(struct llentry *lle)
        size_t pkts_dropped;
        struct mbuf *next;
  
 -      pkts_dropped = 0;
 +      IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp);
        LLE_WLOCK_ASSERT(lle);
 +
 +      /* XXX: guard against race with other llentry_free(). */
 +      if (!(lle->la_flags & LLE_LINKED)) {
 +              LLE_FREE_LOCKED(lle);
 +              return (0);
 +      }
 +
        LIST_REMOVE(lle, lle_next);
 +      lle->la_flags &= ~(LLE_VALID | LLE_LINKED);
  
 +      pkts_dropped = 0;
        while ((lle->la_numheld > 0) && (lle->la_hold != NULL)) {
                next = lle->la_hold->m_nextpkt;
                m_freem(lle->la_hold);
 @@ -122,7 +131,6 @@ llentry_free(struct llentry *lle)
                ("%s: la_numheld %d > 0, pkts_droped %zd", __func__,
                 lle->la_numheld, pkts_dropped));
  
 -      lle->la_flags &= ~LLE_VALID;
        LLE_FREE_LOCKED(lle);
  
        return (pkts_dropped);
 @@ -173,17 +181,16 @@ lltable_free(struct lltable *llt)
        SLIST_REMOVE(&V_lltables, llt, lltable, llt_link);
        LLTABLE_WUNLOCK();
  
 +      IF_AFDATA_WLOCK(llt->llt_ifp);
        for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
                LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
 -                      int canceled;
 -
 -                      canceled = callout_drain(&lle->la_timer);
                        LLE_WLOCK(lle);
 -                      if (canceled)
 +                      if (callout_stop(&lle->la_timer))
                                LLE_REMREF(lle);
                        llentry_free(lle);
                }
        }
 +      IF_AFDATA_WUNLOCK(llt->llt_ifp);
  
        free(llt, M_LLTABLE);
  }
 
 Modified: head/sys/net/if_llatbl.h
 ==============================================================================
 --- head/sys/net/if_llatbl.h   Thu Aug  2 13:20:44 2012        (r238989)
 +++ head/sys/net/if_llatbl.h   Thu Aug  2 13:57:49 2012        (r238990)
 @@ -103,26 +103,28 @@ struct llentry {
  #define       LLE_ADDREF(lle) do {                                    \
        LLE_WLOCK_ASSERT(lle);                                  \
        KASSERT((lle)->lle_refcnt >= 0,                         \
 -              ("negative refcnt %d", (lle)->lle_refcnt));     \
 +          ("negative refcnt %d on lle %p",                    \
 +          (lle)->lle_refcnt, (lle)));                         \
        (lle)->lle_refcnt++;                                    \
  } while (0)
  
  #define       LLE_REMREF(lle) do {                                    \
        LLE_WLOCK_ASSERT(lle);                                  \
 -      KASSERT((lle)->lle_refcnt > 1,                          \
 -              ("bogus refcnt %d", (lle)->lle_refcnt));        \
 +      KASSERT((lle)->lle_refcnt > 0,                          \
 +          ("bogus refcnt %d on lle %p",                       \
 +          (lle)->lle_refcnt, (lle)));                         \
        (lle)->lle_refcnt--;                                    \
  } while (0)
  
  #define       LLE_FREE_LOCKED(lle) do {                               \
 -      if ((lle)->lle_refcnt <= 1)                             \
 -              (lle)->lle_free((lle)->lle_tbl, (lle));\
 +      if ((lle)->lle_refcnt == 1)                             \
 +              (lle)->lle_free((lle)->lle_tbl, (lle));         \
        else {                                                  \
 -              (lle)->lle_refcnt--;                            \
 +              LLE_REMREF(lle);                                \
                LLE_WUNLOCK(lle);                               \
        }                                                       \
        /* guard against invalid refs */                        \
 -      lle = NULL;                                             \
 +      (lle) = NULL;                                           \
  } while (0)
  
  #define       LLE_FREE(lle) do {                                      \
 @@ -172,6 +174,7 @@ MALLOC_DECLARE(M_LLTABLE);
  #define       LLE_VALID       0x0008  /* ll_addr is valid */
  #define       LLE_PROXY       0x0010  /* proxy entry ??? */
  #define       LLE_PUB         0x0020  /* publish entry ??? */
 +#define       LLE_LINKED      0x0040  /* linked to lookup structure */
  #define       LLE_EXCLUSIVE   0x2000  /* return lle xlocked  */
  #define       LLE_DELETE      0x4000  /* delete on a lookup - match 
LLE_IFADDR */
  #define       LLE_CREATE      0x8000  /* create on a lookup miss */
 
 Modified: head/sys/net/if_var.h
 ==============================================================================
 --- head/sys/net/if_var.h      Thu Aug  2 13:20:44 2012        (r238989)
 +++ head/sys/net/if_var.h      Thu Aug  2 13:57:49 2012        (r238990)
 @@ -415,6 +415,8 @@ EVENTHANDLER_DECLARE(group_change_event,
  #define       IF_AFDATA_DESTROY(ifp)  rw_destroy(&(ifp)->if_afdata_lock)
  
  #define       IF_AFDATA_LOCK_ASSERT(ifp)      
rw_assert(&(ifp)->if_afdata_lock, RA_LOCKED)
 +#define       IF_AFDATA_RLOCK_ASSERT(ifp)     
rw_assert(&(ifp)->if_afdata_lock, RA_RLOCKED)
 +#define       IF_AFDATA_WLOCK_ASSERT(ifp)     
rw_assert(&(ifp)->if_afdata_lock, RA_WLOCKED)
  #define       IF_AFDATA_UNLOCK_ASSERT(ifp)    
rw_assert(&(ifp)->if_afdata_lock, RA_UNLOCKED)
  
  int   if_handoff(struct ifqueue *ifq, struct mbuf *m, struct ifnet *ifp,
 
 Modified: head/sys/netinet/if_ether.c
 ==============================================================================
 --- head/sys/netinet/if_ether.c        Thu Aug  2 13:20:44 2012        
(r238989)
 +++ head/sys/netinet/if_ether.c        Thu Aug  2 13:57:49 2012        
(r238990)
 @@ -163,49 +163,40 @@ arp_ifscrub(struct ifnet *ifp, uint32_t 
  static void
  arptimer(void *arg)
  {
 +      struct llentry *lle = (struct llentry *)arg;
        struct ifnet *ifp;
 -      struct llentry   *lle;
 -      int pkts_dropped;
 +      size_t pkts_dropped;
 +
 +      if (lle->la_flags & LLE_STATIC) {
 +              LLE_WUNLOCK(lle);
 +              return;
 +      }
  
 -      KASSERT(arg != NULL, ("%s: arg NULL", __func__));
 -      lle = (struct llentry *)arg;
        ifp = lle->lle_tbl->llt_ifp;
        CURVNET_SET(ifp->if_vnet);
 +
 +      if (lle->la_flags != LLE_DELETED) {
 +              int evt;
 +
 +              if (lle->la_flags & LLE_VALID)
 +                      evt = LLENTRY_EXPIRED;
 +              else
 +                      evt = LLENTRY_TIMEDOUT;
 +              EVENTHANDLER_INVOKE(lle_event, lle, evt);
 +      }
 +
 +      callout_stop(&lle->la_timer);
 +
 +      /* XXX: LOR avoidance. We still have ref on lle. */
 +      LLE_WUNLOCK(lle);
        IF_AFDATA_LOCK(ifp);
        LLE_WLOCK(lle);
 -      if (lle->la_flags & LLE_STATIC)
 -              LLE_WUNLOCK(lle);
 -      else {
 -              if (!callout_pending(&lle->la_timer) &&
 -                  callout_active(&lle->la_timer)) {
 -                      callout_stop(&lle->la_timer);
 -                      LLE_REMREF(lle);
 -
 -                      if (lle->la_flags != LLE_DELETED) {
 -                              int evt;
 -
 -                              if (lle->la_flags & LLE_VALID)
 -                                      evt = LLENTRY_EXPIRED;
 -                              else
 -                                      evt = LLENTRY_TIMEDOUT;
 -                              EVENTHANDLER_INVOKE(lle_event, lle, evt);
 -                      }
  
 -                      pkts_dropped = llentry_free(lle);
 -                      ARPSTAT_ADD(dropped, pkts_dropped);
 -                      ARPSTAT_INC(timeouts);
 -              } else {
 -#ifdef DIAGNOSTIC
 -                      struct sockaddr *l3addr = L3_ADDR(lle);
 -                      log(LOG_INFO,
 -                          "arptimer issue: %p, IPv4 address: \"%s\"\n", lle,
 -                          inet_ntoa(
 -                              ((const struct sockaddr_in 
*)l3addr)->sin_addr));
 -#endif
 -                      LLE_WUNLOCK(lle);
 -              }
 -      }
 +      LLE_REMREF(lle);
 +      pkts_dropped = llentry_free(lle);
        IF_AFDATA_UNLOCK(ifp);
 +      ARPSTAT_ADD(dropped, pkts_dropped);
 +      ARPSTAT_INC(timeouts);
        CURVNET_RESTORE();
  }
  
 
 Modified: head/sys/netinet/in.c
 ==============================================================================
 --- head/sys/netinet/in.c      Thu Aug  2 13:20:44 2012        (r238989)
 +++ head/sys/netinet/in.c      Thu Aug  2 13:57:49 2012        (r238990)
 @@ -1280,7 +1280,6 @@ in_lltable_new(const struct sockaddr *l3
        if (lle == NULL)                /* NB: caller generates msg */
                return NULL;
  
 -      callout_init(&lle->base.la_timer, CALLOUT_MPSAFE);
        /*
         * For IPv4 this will trigger "arpresolve" to generate
         * an ARP request.
 @@ -1290,7 +1289,10 @@ in_lltable_new(const struct sockaddr *l3
        lle->base.lle_refcnt = 1;
        lle->base.lle_free = in_lltable_free;
        LLE_LOCK_INIT(&lle->base);
 -      return &lle->base;
 +      callout_init_rw(&lle->base.la_timer, &lle->base.lle_lock,
 +          CALLOUT_RETURNUNLOCKED);
 +
 +      return (&lle->base);
  }
  
  #define IN_ARE_MASKED_ADDR_EQUAL(d, a, m)     (                       \
 @@ -1306,6 +1308,7 @@ in_lltable_prefix_free(struct lltable *l
        int i;
        size_t pkts_dropped;
  
 +      IF_AFDATA_WLOCK(llt->llt_ifp);
        for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
                LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
                        /*
 @@ -1315,17 +1318,15 @@ in_lltable_prefix_free(struct lltable *l
                        if (IN_ARE_MASKED_ADDR_EQUAL(satosin(L3_ADDR(lle)),
                            pfx, msk) && ((flags & LLE_STATIC) ||
                            !(lle->la_flags & LLE_STATIC))) {
 -                              int canceled;
 -
 -                              canceled = callout_drain(&lle->la_timer);
                                LLE_WLOCK(lle);
 -                              if (canceled)
 +                              if (callout_stop(&lle->la_timer))
                                        LLE_REMREF(lle);
                                pkts_dropped = llentry_free(lle);
                                ARPSTAT_ADD(dropped, pkts_dropped);
                        }
                }
        }
 +      IF_AFDATA_WUNLOCK(llt->llt_ifp);
  }
  
  
 @@ -1457,11 +1458,12 @@ in_lltable_lookup(struct lltable *llt, u
  
                lle->lle_tbl  = llt;
                lle->lle_head = lleh;
 +              lle->la_flags |= LLE_LINKED;
                LIST_INSERT_HEAD(lleh, lle, lle_next);
        } else if (flags & LLE_DELETE) {
                if (!(lle->la_flags & LLE_IFADDR) || (flags & LLE_IFADDR)) {
                        LLE_WLOCK(lle);
 -                      lle->la_flags = LLE_DELETED;
 +                      lle->la_flags |= LLE_DELETED;
                        EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED);
                        LLE_WUNLOCK(lle);
  #ifdef DIAGNOSTIC
 
 Modified: head/sys/netinet6/in6.c
 ==============================================================================
 --- head/sys/netinet6/in6.c    Thu Aug  2 13:20:44 2012        (r238989)
 +++ head/sys/netinet6/in6.c    Thu Aug  2 13:57:49 2012        (r238990)
 @@ -2497,23 +2497,22 @@ in6_lltable_prefix_free(struct lltable *
         * (flags & LLE_STATIC) means deleting all entries
         * including static ND6 entries.
         */
 +      IF_AFDATA_WLOCK(llt->llt_ifp);
        for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
                LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
                        if (IN6_ARE_MASKED_ADDR_EQUAL(
 -                                  &((struct sockaddr_in6 
*)L3_ADDR(lle))->sin6_addr,
 -                                  &pfx->sin6_addr,
 -                                  &msk->sin6_addr) &&
 -                          ((flags & LLE_STATIC) || !(lle->la_flags & 
LLE_STATIC))) {
 -                              int canceled;
 -
 -                              canceled = callout_drain(&lle->la_timer);
 +                          &satosin6(L3_ADDR(lle))->sin6_addr,
 +                          &pfx->sin6_addr, &msk->sin6_addr) &&
 +                          ((flags & LLE_STATIC) ||
 +                          !(lle->la_flags & LLE_STATIC))) {
                                LLE_WLOCK(lle);
 -                              if (canceled)
 +                              if (callout_stop(&lle->la_timer))
                                        LLE_REMREF(lle);
                                llentry_free(lle);
                        }
                }
        }
 +      IF_AFDATA_WUNLOCK(llt->llt_ifp);
  }
  
  static int
 @@ -2605,11 +2604,12 @@ in6_lltable_lookup(struct lltable *llt, 
  
                lle->lle_tbl  = llt;
                lle->lle_head = lleh;
 +              lle->la_flags |= LLE_LINKED;
                LIST_INSERT_HEAD(lleh, lle, lle_next);
        } else if (flags & LLE_DELETE) {
                if (!(lle->la_flags & LLE_IFADDR) || (flags & LLE_IFADDR)) {
                        LLE_WLOCK(lle);
 -                      lle->la_flags = LLE_DELETED;
 +                      lle->la_flags |= LLE_DELETED;
                        LLE_WUNLOCK(lle);
  #ifdef DIAGNOSTIC
                        log(LOG_INFO, "ifaddr cache = %p  is deleted\n", lle);
 _______________________________________________
 svn-src-...@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"
 
_______________________________________________
freebsd-net@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "freebsd-net-unsubscr...@freebsd.org"

Reply via email to