Print this page
usr/src/cmd/dlmgmtd/dlmgmt_door.c
        
*** 926,935 ****
--- 926,943 ----
                  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,956 ****
                  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
--- 948,958 ----
*** 957,966 ****
--- 959,989 ----
           * 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,995 ****
                          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) {
                          ddp->dd_prop_taskid = taskq_dispatch(system_taskq,
                              dls_devnet_prop_task, ddp, TQ_SLEEP);
                  }
                  mutex_exit(&ddp->dd_mutex);
  
--- 1007,1018 ----
                          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 == 0) {
                          ddp->dd_prop_taskid = taskq_dispatch(system_taskq,
                              dls_devnet_prop_task, ddp, TQ_SLEEP);
                  }
                  mutex_exit(&ddp->dd_mutex);
  
*** 1046,1056 ****
                   * 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
                   */
                  if (ddp->dd_ref > 1 && ddp->dd_zid != GLOBAL_ZONEID) {
                          zone_t  *zp;
  
                          if ((zp = zone_find_by_id(ddp->dd_zid)) == NULL) {
--- 1069,1079 ----
                   * 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), 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,1086 ****
                  }
  
                  /*
                   * We want to delete the link, reset ref to 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--;
--- 1096,1110 ----
                  }
  
                  /*
                   * We want to delete the link, reset ref to 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,1119 ****
--- 1133,1157 ----
           * 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,1136 ****
                  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);
          }
  
          if (ddp->dd_linkid != DATALINK_INVALID_LINKID) {
                  dls_devnet_stat_destroy(ddp, ddp->dd_owner_zid);
          }
--- 1164,1174 ----
                  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 == 0);
          }
  
          if (ddp->dd_linkid != DATALINK_INVALID_LINKID) {
                  dls_devnet_stat_destroy(ddp, ddp->dd_owner_zid);
          }
*** 1161,1172 ****
                  rw_exit(&i_dls_devnet_lock);
                  return (ENOENT);
          }
  
          mutex_enter(&ddp->dd_mutex);
!         ASSERT(ddp->dd_ref > 0);
!         if (ddp->dd_flags & DD_CONDEMNED) {
                  mutex_exit(&ddp->dd_mutex);
                  rw_exit(&i_dls_devnet_lock);
                  return (ENOENT);
          }
          ddp->dd_tref++;
--- 1199,1210 ----
                  rw_exit(&i_dls_devnet_lock);
                  return (ENOENT);
          }
  
          mutex_enter(&ddp->dd_mutex);
!         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,1945 ****
           * 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) {
                  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;
--- 1971,1982 ----
           * 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)
                  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;