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;