Print this page
More stats to SIOCIPFCFWCFG and add SIOCIPFCFWNEWSZ to affect ring-buffer size.

@@ -95,18 +95,22 @@
  * but for now keep the ring buffer simple and stupid.
  */
 
 /* Must be a power of 2, to be bitmaskable, and must be countable by a uint_t */
 
-#define IPF_CFW_RING_BUFS       1024
-#define IPF_CFW_RING_MASK (IPF_CFW_RING_BUFS - 1)
+#define IPF_CFW_DEFAULT_RING_BUFS       1024
+#define IPF_CFW_MIN_RING_BUFS           8
+#define IPF_CFW_MAX_RING_BUFS           (1U << 31U)
 
 /* Assume C's init-to-zero is sufficient for these types... */
 static kmutex_t cfw_ringlock;
 static kcondvar_t cfw_ringcv;
 
-static cfwev_t cfw_evring[IPF_CFW_RING_BUFS];
+static cfwev_t *cfw_ring;       /* NULL by default. */
+static uint32_t cfw_ringsize;   /* 0 by default, number of array elements. */
+static uint32_t cfw_ringmask;   /* 0 by default. */
+
 /* If these are equal, we're either empty or full. */
 static uint_t cfw_ringstart, cfw_ringend;
 static boolean_t cfw_ringfull;  /* Tell the difference here! */
 static uint64_t cfw_evreports;
 static uint64_t cfw_evdrops;

@@ -121,20 +125,20 @@
 ipf_cfwev_report(cfwev_t *event)
 {
         mutex_enter(&cfw_ringlock);
         if (cfw_ringfull) {
                 cfw_ringstart++;
-                cfw_ringstart &= IPF_CFW_RING_MASK;
+                cfw_ringstart &= cfw_ringmask;
                 cfw_ringend++;
-                cfw_ringend &= IPF_CFW_RING_MASK;
+                cfw_ringend &= cfw_ringmask;
                 DTRACE_PROBE(ipf__cfw__evdrop);
                 cfw_evdrops++;
-                cfw_evring[cfw_ringend] = *event;
+                cfw_ring[cfw_ringend] = *event;
         } else {
-                cfw_evring[cfw_ringend] = *event;
+                cfw_ring[cfw_ringend] = *event;
                 cfw_ringend++;
-                cfw_ringend &= IPF_CFW_RING_MASK;
+                cfw_ringend &= cfw_ringmask;
                 cfw_ringfull = (cfw_ringend == cfw_ringstart);
         }
         cfw_evreports++;
         cv_broadcast(&cfw_ringcv);
         mutex_exit(&cfw_ringlock);

@@ -162,11 +166,11 @@
                         mutex_exit(&cfw_ringlock);
                         return (B_FALSE);
                 }
         }
 
-        *event = cfw_evring[cfw_ringstart];
+        *event = cfw_ring[cfw_ringstart];
         cfw_ringstart++;
         cfw_ringstart &= IPF_CFW_RING_MASK;
         cfw_ringfull = B_FALSE;
         mutex_exit(&cfw_ringlock);
         return (B_TRUE);

@@ -212,21 +216,21 @@
         int timeout_tries = cfw_timeout_tries;
 
         mutex_enter(&cfw_ringlock);
 
         /* Silly reality checks */
-        ASSERT3U(cfw_ringstart, <, IPF_CFW_RING_BUFS);
-        ASSERT3U(cfw_ringend, <, IPF_CFW_RING_BUFS);
+        ASSERT3U(cfw_ringstart, <, cfw_ringsize);
+        ASSERT3U(cfw_ringend, <, cfw_ringsize);
 
         /*
          * Can goto here again if caller wants blocking. NOTE that
          * num_requested may have been decremented and consumed may have been
          * incremented if we arrive here via a goto after a cv_wait.
          */
 from_the_top:
         if (cfw_ringstart > cfw_ringend || cfw_ringfull)
-                contig_size = IPF_CFW_RING_BUFS - cfw_ringstart;
+                contig_size = cfw_ringsize - cfw_ringstart;
         else if (cfw_ringstart < cfw_ringend)
                 contig_size = cfw_ringend - cfw_ringstart;
         else if (block && cv_wait_sig(&cfw_ringcv, &cfw_ringlock)) {
                 /* Maybe something to consume now, try again. */
                 goto from_the_top;

@@ -234,38 +238,38 @@
                 /* Nothing (more) to consume, return! */
                 goto bail;
         }
 
         ASSERT(contig_size + cfw_ringstart == cfw_ringend ||
-            contig_size + cfw_ringstart == IPF_CFW_RING_BUFS);
+            contig_size + cfw_ringstart == cfw_ringsize);
 
         if (num_requested < contig_size)
                 contig_size = num_requested;
 
-        cb_consumed = cfw_many_cb(&(cfw_evring[cfw_ringstart]), contig_size,
+        cb_consumed = cfw_many_cb(&(cfw_ring[cfw_ringstart]), contig_size,
             cbarg);
         ASSERT(cb_consumed <= contig_size);
         cfw_ringstart += cb_consumed;
         consumed += cb_consumed;
         cfw_ringfull = (cfw_ringfull && cb_consumed == 0);
         if (cb_consumed < contig_size) {
                 /* Caller clearly had a problem. Reality check and bail. */
-                ASSERT((cfw_ringstart & IPF_CFW_RING_MASK) == cfw_ringstart);
+                ASSERT((cfw_ringstart & cfw_ringmask) == cfw_ringstart);
                 goto bail;
         }
         ASSERT(cb_consumed == contig_size);
-        cfw_ringstart &= IPF_CFW_RING_MASK;     /* In case of wraparound. */
+        cfw_ringstart &= cfw_ringmask;  /* In case of wraparound. */
         num_requested -= contig_size;
 
         if (num_requested > 0 && cfw_ringstart != cfw_ringend) {
                 /* We must have wrapped around the end of the buffer! */
                 ASSERT(cfw_ringstart == 0);
                 ASSERT(!cfw_ringfull);
                 contig_size = cfw_ringend;
                 if (num_requested < contig_size)
                         contig_size = num_requested;
-                cb_consumed = cfw_many_cb(&(cfw_evring[cfw_ringstart]),
+                cb_consumed = cfw_many_cb(&(cfw_ring[cfw_ringstart]),
                     contig_size, cbarg);
                 cfw_ringstart += cb_consumed;
                 consumed += cb_consumed;
                 if (cb_consumed < contig_size) {
                         /*

@@ -497,34 +501,96 @@
                 return (0);
 
         return (num_avail);
 }
 
+int
+ipf_cfw_ring_resize(uint32_t newsize)
+{
+        ASSERT(MUTEX_HELD(&cfw_ringlock) || newsize == IPF_CFW_RING_ALLOCATE ||
+            newsize == IPF_CFW_RING_DESTROY);
+
+        if (newsize == IPF_CFW_RING_ALLOCATE) {
+                if (cfw_ring != NULL)
+                        return (EBUSY);
+                newsize = IPF_CFW_DEFAULT_RING_BUFS;
+                /* Fall through to allocating a new ring buffer. */
+        } else {
+                /* We may be called during error cleanup, so be liberal here. */
+                if (cfw_ring == NULL && newsize == IPF_CFW_RING_DESTROY)
+                        return (0);
+                kmem_free(cfw_ring, cfw_ringsize * sizeof (cfwev_t));
+                cfw_ring = NULL;
+                if (cfw_ringfull) {
+                        cfw_evdrops += cfw_ringsize;
+                } else if (cfw_ringstart > cfw_ringend) {
+                        cfw_evdrops += cfw_ringend +
+                            (cfw_ringsize - cfw_ringstart);
+                } else {
+                        cfw_evdrops += cfw_ringend - cfw_ringstart;
+                }
+                cfw_ringsize = cfw_ringmask = cfw_ringstart = cfw_ringend = 0;
+                cfw_ringfull = B_FALSE;
+
+                if (newsize == IPF_CFW_RING_DESTROY)
+                        return (0);
+                /*
+                 * Keep the reports & drops around because if we're just
+                 * resizing, we need to know what we lost.
+                 */
+        }
+
+        ASSERT(ISP2(newsize));
+        cfw_ring = kmem_alloc(newsize * sizeof (cfwev_t), KM_SLEEP);
+        /* KM_SLEEP means we always succeed. */
+        cfw_ringsize = newsize;
+        cfw_ringmask = cfw_ringsize - 1;
+
+        return (0);
+}
+
 /* ARGSUSED */
 int
 ipf_cfwlog_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *cp,
     int *rp)
 {
         ipfcfwcfg_t cfginfo;
         int error;
 
-        if (cmd != SIOCIPFCFWCFG)
+        if (cmd != SIOCIPFCFWCFG && cmd != SIOCIPFCFWNEWSZ)
                 return (EIO);
 
         if (crgetzoneid(cp) != GLOBAL_ZONEID)
                 return (EACCES);
 
-#ifdef notyet
         error = COPYIN((caddr_t)data, (caddr_t)&cfginfo, sizeof (cfginfo));
         if (error != 0)
                 return (EFAULT);
-        /* TODO: Resize ring buffer based on cfginfo.ipfcfwc_evringsize. */
-#endif
 
         cfginfo.ipfcfwc_maxevsize = sizeof (cfwev_t);
-        cfginfo.ipfcfwc_evringsize = IPF_CFW_RING_BUFS;
+        mutex_enter(&cfw_ringlock);
+        cfginfo.ipfcfwc_evreports = cfw_evreports;
+        cfginfo.ipfcfwc_evdrops = cfw_evdrops;
+        if (cmd == SIOCIPFCFWNEWSZ) {
+                uint32_t newsize = cfginfo.ipfcfwc_evringsize;
 
+                /* Do ioctl parameter checking here, then call the resizer. */
+                if (newsize < IPF_CFW_MIN_RING_BUFS ||
+                    newsize > IPF_CFW_MAX_RING_BUFS || !ISP2(newsize)) {
+                        error = EINVAL;
+                } else {
+                        error = ipf_cfw_ring_resize(cfginfo.ipfcfwc_evringsize);
+                }
+        } else {
+                error = 0;
+        }
+        cfginfo.ipfcfwc_evringsize = cfw_ringsize;
+        mutex_exit(&cfw_ringlock);
+
+        if (error != 0)
+                return (error);
+
         error = COPYOUT((caddr_t)&cfginfo, (caddr_t)data, sizeof (cfginfo));
         if (error != 0)
                 return (EFAULT);
 
         return (0);

@@ -575,10 +641,16 @@
 
 #else
 
 /* Blank stubs to satisfy userland's test compilations. */
 
+int
+ipf_cfw_ring_resize(uint32_t a)
+{
+        return (0);
+}
+
 void
 ipf_log_cfwlog(struct ipstate *a, uint_t b, ipf_stack_t *c)
 {
 }