Print this page
usr/src/cmd/dlmgmtd/dlmgmt_door.c
        
@@ -926,10 +926,18 @@
                 ddp = kmem_cache_alloc(i_dls_devnet_cachep, KM_SLEEP);
                 ddp->dd_flags = DD_INITIALIZING;
                 ddp->dd_tref = 0;
                 ddp->dd_ref++;
                 ddp->dd_owner_zid = zoneid;
+                /*
+                 * If we are creating a new devnet which will be owned by a NGZ
+                 * then mark it as transient. This link has never been in the
+                 * GZ, the GZ will not have a hold on its reference, and we do
+                 * not want to return it to the GZ when the zone halts.
+                 */
+                if (zoneid != GLOBAL_ZONEID)
+                        ddp->dd_transient = B_TRUE;
                 (void) strlcpy(ddp->dd_mac, macname, sizeof (ddp->dd_mac));
                 VERIFY(mod_hash_insert(i_dls_devnet_hash,
                     (mod_hash_key_t)ddp->dd_mac, (mod_hash_val_t)ddp) == 0);
         }
 
@@ -940,17 +948,11 @@
                 VERIFY(mod_hash_insert(i_dls_devnet_id_hash,
                     (mod_hash_key_t)(uintptr_t)linkid,
                     (mod_hash_val_t)ddp) == 0);
                 devnet_need_rebuild = B_TRUE;
                 stat_create = B_TRUE;
-                mutex_enter(&ddp->dd_mutex);
-                if (!ddp->dd_prop_loaded && (ddp->dd_prop_taskid == 0)) {
-                        ddp->dd_prop_taskid = taskq_dispatch(system_taskq,
-                            dls_devnet_prop_task, ddp, TQ_SLEEP);
                 }
-                mutex_exit(&ddp->dd_mutex);
-        }
         err = 0;
 done:
         /*
          * It is safe to drop the i_dls_devnet_lock at this point. In the case
          * of physical devices, the softmac framework will fail the device
@@ -957,10 +959,31 @@
          * detach based on the smac_state or smac_hold_cnt. Other cases like
          * vnic and aggr use their own scheme to serialize creates and deletes
          * and ensure that *ddp is valid.
          */
         rw_exit(&i_dls_devnet_lock);
+
+        if (err == 0 && zoneid != GLOBAL_ZONEID) {
+                /*
+                 * If this link is being created directly within a non-global
+                 * zone, then flag it as transient so that it will be cleaned
+                 * up when the zone is shut down.
+                 */
+                err = i_dls_devnet_setzid(ddp, zoneid, B_FALSE, B_TRUE);
+                if (err != 0) {
+                        /*
+                         * At this point the link is marked as
+                         * DD_INITIALIZING -- there can be no
+                         * outstanding temp refs and therefore no need
+                         * to wait for them.
+                         */
+                        ASSERT(ddp->dd_flags & DD_INITIALIZING);
+                        (void) dls_devnet_unset(mh, &linkid, B_FALSE);
+                        return (err);
+                }
+        }
+
         if (err == 0) {
                 if (zoneid != GLOBAL_ZONEID &&
                     (err = i_dls_devnet_setzid(ddp, zoneid, B_FALSE,
                     B_FALSE)) != 0) {
                         /*
@@ -984,12 +1007,12 @@
                         dls_devnet_stat_create(ddp, zoneid, zoneid);
                 if (ddpp != NULL)
                         *ddpp = ddp;
 
                 mutex_enter(&ddp->dd_mutex);
-                if (linkid != DATALINK_INVALID_LINKID && !ddp->dd_prop_loaded &&
-                    ddp->dd_prop_taskid == TASKQID_INVALID) {
+                if (linkid != DATALINK_INVALID_LINKID &&
+                    !ddp->dd_prop_loaded && ddp->dd_prop_taskid == 0) {
                         ddp->dd_prop_taskid = taskq_dispatch(system_taskq,
                             dls_devnet_prop_task, ddp, TQ_SLEEP);
                 }
                 mutex_exit(&ddp->dd_mutex);
 
@@ -1046,11 +1069,11 @@
                  * before we simply return EBUSY.
                  *
                  * zstatus indicates which situation we are dealing with:
                  *       0 - means return EBUSY
                  *       1 - means case (a), cleanup transient link
-                 *      -1 - means case (b), orphained VNIC
+                 *      -1 - means case (b), orphaned VNIC
                  */
                 if (ddp->dd_ref > 1 && ddp->dd_zid != GLOBAL_ZONEID) {
                         zone_t  *zp;
 
                         if ((zp = zone_find_by_id(ddp->dd_zid)) == NULL) {
@@ -1073,14 +1096,15 @@
                 }
 
                 /*
                  * We want to delete the link, reset ref to 1;
                  */
-                if (zstatus == -1)
+                if (zstatus == -1) {
                         /* Log a warning, but continue in this case */
                         cmn_err(CE_WARN, "clear orphaned datalink: %s\n",
                             ddp->dd_linkname);
+                }
                 ddp->dd_ref = 1;
         }
 
         ddp->dd_flags |= DD_CONDEMNED;
         ddp->dd_ref--;
@@ -1109,11 +1133,25 @@
          * locking rules state MAC -> DLS order). By performing the
          * setzid outside of the i_dls_devnet_lock consumers can
          * safely call dls_devnet_unset() outside the MAC perim.
          */
         if (ddp->dd_zid != GLOBAL_ZONEID) {
+                /*
+                 * We need to release the dd_mutex before we try and destroy the
+                 * stat. When we destroy it, we'll need to grab the lock for the
+                 * kstat but if there's a concurrent reader of the kstat, we'll
+                 * be blocked on it. This will lead to deadlock because these
+                 * kstats employ a ks_update function (dls_devnet_stat_update)
+                 * which needs the dd_mutex that we currently hold.
+                 *
+                 * Because we've already flagged the dls_devnet_t as
+                 * DD_CONDEMNED and we still have a write lock on
+                 * i_dls_devnet_lock, we should be able to release the dd_mutex.
+                 */
+                mutex_exit(&ddp->dd_mutex);
                 dls_devnet_stat_destroy(ddp, ddp->dd_zid);
+                mutex_enter(&ddp->dd_mutex);
                 (void) i_dls_devnet_setzid(ddp, GLOBAL_ZONEID, B_FALSE,
                     B_FALSE);
         }
 
         if (wait) {
@@ -1126,11 +1164,11 @@
                 ASSERT0(MAC_PERIM_HELD(mh));
                 while ((ddp->dd_tref != 0) || (ddp->dd_prop_taskid != 0))
                         cv_wait(&ddp->dd_cv, &ddp->dd_mutex);
         } else {
                 VERIFY(ddp->dd_tref == 0);
-                VERIFY(ddp->dd_prop_taskid == (taskqid_t)NULL);
+                VERIFY(ddp->dd_prop_taskid == 0);
         }
 
         if (ddp->dd_linkid != DATALINK_INVALID_LINKID) {
                 dls_devnet_stat_destroy(ddp, ddp->dd_owner_zid);
         }
@@ -1161,12 +1199,12 @@
                 rw_exit(&i_dls_devnet_lock);
                 return (ENOENT);
         }
 
         mutex_enter(&ddp->dd_mutex);
-        ASSERT(ddp->dd_ref > 0);
-        if (ddp->dd_flags & DD_CONDEMNED) {
+        VERIFY(ddp->dd_ref > 0);
+        if (DD_NOT_VISIBLE(ddp->dd_flags)) {
                 mutex_exit(&ddp->dd_mutex);
                 rw_exit(&i_dls_devnet_lock);
                 return (ENOENT);
         }
         ddp->dd_tref++;
@@ -1933,13 +1971,12 @@
          * of mac_disable() were modified by Crossbow such that
          * dls_devnet_destroy() needs to be called before
          * mac_disable() can succeed. This is because of the implicit
          * reference that dls has on the mac_impl_t.
          */
-        if (err != 0 && err != ENOENT) {
+        if (err != 0 && err != ENOENT)
                 return (err);
-        }
 
         mac_perim_enter_by_mh(mh, &mph);
         err = dls_link_rele_by_name(mac_name(mh));
         if (err != 0) {
                 dls_devnet_t    *ddp;