Print this page
Warning about DCE lifetimes
coyright fix
gdamore's feedback
7185 IP DCEs leak from halted non-global zones

@@ -20,10 +20,11 @@
  */
 
 /*
  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright 2017, OmniTI Computer Consulting, Inc. All rights reserved.
  */
 
 #include <sys/types.h>
 #include <sys/stream.h>
 #include <sys/strsun.h>

@@ -88,10 +89,14 @@
  *                      return failure;
  *      }
  *
  * Note that for IPv6 link-local addresses we record the ifindex since the
  * link-locals are not globally unique.
+ *
+ * DCEs can remain for an arbitrarily long time, until memory pressure or
+ * too-deep hash buckets (see dce_lookup_and_add*()) enable the reclaim thread
+ * to actually remove DCEs from the cache.
  */
 
 /*
  * Hash bucket structure for DCEs
  */

@@ -324,16 +329,41 @@
                 rw_init(&ipst->ips_dce_hash_v6[i].dcb_lock, NULL, RW_DEFAULT,
                     NULL);
         }
 }
 
+/*
+ * Given a DCE hash bucket, unlink DCE entries from it. Some callers need
+ * ifindex-specific matching, others don't. Don't overload ifindex to indicate
+ * specificity, just indicate so explicitly.
+ */
+static void
+dce_bucket_clean(dcb_t *dcb, boolean_t specific_ifindex, uint_t ifindex)
+{
+        dce_t   *dce, *nextdce;
+
+        rw_enter(&dcb->dcb_lock, RW_WRITER);
+
+        for (dce = dcb->dcb_dce; dce != NULL; dce = nextdce) {
+                nextdce = dce->dce_next;
+                if ((!specific_ifindex) || dce->dce_ifindex == ifindex) {
+                        dce_delete_locked(dcb, dce);
+                        dce_refrele(dce);
+                }
+        }
+
+        rw_exit(&dcb->dcb_lock);
+}
+
 void
 dce_stack_destroy(ip_stack_t *ipst)
 {
         int i;
         for (i = 0; i < ipst->ips_dce_hashsize; i++) {
+                dce_bucket_clean(&ipst->ips_dce_hash_v4[i], B_FALSE, 0);
                 rw_destroy(&ipst->ips_dce_hash_v4[i].dcb_lock);
+                dce_bucket_clean(&ipst->ips_dce_hash_v6[i], B_FALSE, 0);
                 rw_destroy(&ipst->ips_dce_hash_v6[i].dcb_lock);
         }
         kmem_free(ipst->ips_dce_hash_v4,
             ipst->ips_dce_hashsize * sizeof (dcb_t));
         ipst->ips_dce_hash_v4 = NULL;

@@ -953,22 +983,9 @@
  */
 void
 dce_cleanup(uint_t ifindex, ip_stack_t *ipst)
 {
         uint_t  i;
-        dcb_t   *dcb;
-        dce_t   *dce, *nextdce;
 
-        for (i = 0; i < ipst->ips_dce_hashsize; i++) {
-                dcb = &ipst->ips_dce_hash_v6[i];
-                rw_enter(&dcb->dcb_lock, RW_WRITER);
-
-                for (dce = dcb->dcb_dce; dce != NULL; dce = nextdce) {
-                        nextdce = dce->dce_next;
-                        if (dce->dce_ifindex == ifindex) {
-                                dce_delete_locked(dcb, dce);
-                                dce_refrele(dce);
-                        }
-                }
-                rw_exit(&dcb->dcb_lock);
-        }
+        for (i = 0; i < ipst->ips_dce_hashsize; i++)
+                dce_bucket_clean(&ipst->ips_dce_hash_v6[i], B_TRUE, ifindex);
 }