Author: sephe
Date: Wed Jun 15 05:16:37 2016
New Revision: 301913
URL: https://svnweb.freebsd.org/changeset/base/301913

Log:
  MFC 296178
  
      buf_ring/drbr: Add buf_ring_peek_clear_sc and use it in drbr_peek
  
      Unlike buf_ring_peek, it only supports single consumer mode, and it
      clears the cons_head if DEBUG_BUFRING/INVARIANTS is defined.
  
      The normal use case of drbr_peek for network drivers is:
  
      m = drbr_peek(br);
      err = hw_spec_encap(&m); /* could m_defrag/m_collapse */
      (*)
      if (err) {
          if (m == NULL)
              drbr_advance(br);
          else
              drbr_putback(br, m);
          /* break the loop */
      }
      drbr_advance(br);
  
      The race is:
      If hw_spec_encap() m_defrag or m_collapse the mbuf, i.e. the old mbuf
      was freed, or like the Hyper-V's network driver, that transmission-
      done does not even require the TX lock; then on the other CPU at the
      (*) time, the freed mbuf could be recycled and being drbr_enqueue even
      before the current CPU had the chance to call drbr_{advance,putback}.
      This triggers a panic in drbr_enqueue duplicated element check, if
      DEBUG_BUFRING/INVARIANTS is defined.
  
      Use buf_ring_peek_clear_sc() in drbr_peek() to fix the above race.
  
      This change is a NO-OP, if neither DEBUG_BUFRING nor INVARIANTS are
      defined.
  
      MFC after:  1 week
      Sponsored by:       Microsoft OSTC
      Differential Revision:      https://reviews.freebsd.org/D5416

Modified:
  stable/10/sys/net/if_var.h
  stable/10/sys/sys/buf_ring.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/net/if_var.h
==============================================================================
--- stable/10/sys/net/if_var.h  Wed Jun 15 03:48:55 2016        (r301912)
+++ stable/10/sys/net/if_var.h  Wed Jun 15 05:16:37 2016        (r301913)
@@ -705,7 +705,7 @@ drbr_peek(struct ifnet *ifp, struct buf_
                return (m);
        }
 #endif
-       return(buf_ring_peek(br));
+       return(buf_ring_peek_clear_sc(br));
 }
 
 static __inline void

Modified: stable/10/sys/sys/buf_ring.h
==============================================================================
--- stable/10/sys/sys/buf_ring.h        Wed Jun 15 03:48:55 2016        
(r301912)
+++ stable/10/sys/sys/buf_ring.h        Wed Jun 15 05:16:37 2016        
(r301913)
@@ -263,6 +263,37 @@ buf_ring_peek(struct buf_ring *br)
        return (br->br_ring[br->br_cons_head]);
 }
 
+static __inline void *
+buf_ring_peek_clear_sc(struct buf_ring *br)
+{
+#ifdef DEBUG_BUFRING
+       void *ret;
+
+       if (!mtx_owned(br->br_lock))
+               panic("lock not held on single consumer dequeue");
+#endif 
+       /*
+        * I believe it is safe to not have a memory barrier
+        * here because we control cons and tail is worst case
+        * a lagging indicator so we worst case we might
+        * return NULL immediately after a buffer has been enqueued
+        */
+       if (br->br_cons_head == br->br_prod_tail)
+               return (NULL);
+
+#ifdef DEBUG_BUFRING
+       /*
+        * Single consumer, i.e. cons_head will not move while we are
+        * running, so atomic_swap_ptr() is not necessary here.
+        */
+       ret = br->br_ring[br->br_cons_head];
+       br->br_ring[br->br_cons_head] = NULL;
+       return (ret);
+#else
+       return (br->br_ring[br->br_cons_head]);
+#endif
+}
+
 static __inline int
 buf_ring_full(struct buf_ring *br)
 {
_______________________________________________
svn-src-stable-10@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-stable-10
To unsubscribe, send any mail to "svn-src-stable-10-unsubscr...@freebsd.org"

Reply via email to