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;