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;