summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/dls
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/dls')
-rw-r--r--usr/src/uts/common/io/dls/dls.c24
-rw-r--r--usr/src/uts/common/io/dls/dls_link.c4
-rw-r--r--usr/src/uts/common/io/dls/dls_mgmt.c106
3 files changed, 89 insertions, 45 deletions
diff --git a/usr/src/uts/common/io/dls/dls.c b/usr/src/uts/common/io/dls/dls.c
index 064217c8f2..53450a45d1 100644
--- a/usr/src/uts/common/io/dls/dls.c
+++ b/usr/src/uts/common/io/dls/dls.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -607,14 +607,6 @@ dls_mac_active_set(dls_link_t *dlp)
* Set the function to start receiving packets.
*/
mac_rx_set(dlp->dl_mch, i_dls_link_rx, dlp);
-
- /*
- * We've got a MAC client for this link now.
- * Push down the flows that were defined on this link
- * hitherto. The flows are added to the active flow table
- * and SRS, softrings etc. are created as needed.
- */
- mac_link_init_flows(dlp->dl_mch);
}
dlp->dl_nactive++;
return (0);
@@ -625,20 +617,6 @@ dls_mac_active_clear(dls_link_t *dlp)
{
if (--dlp->dl_nactive == 0) {
ASSERT(dlp->dl_mah != NULL);
- /*
- * We would have initialized subflows etc. only if we
- * brought up the primary client and set the unicast
- * unicast address etc. Deactivate the flows. The flow
- * entry will be removed from the active flow tables,
- * and the associated SRS, softrings etc will be
- * deleted. But the flow entry itself won't be
- * destroyed, instead it will continue to be
- * archived off the the global flow hash list, for a
- * possible future activation when say
- * IP is plumbed again
- */
-
- mac_link_release_flows(dlp->dl_mch);
(void) mac_unicast_remove(dlp->dl_mch, dlp->dl_mah);
dlp->dl_mah = NULL;
mac_rx_clear(dlp->dl_mch);
diff --git a/usr/src/uts/common/io/dls/dls_link.c b/usr/src/uts/common/io/dls/dls_link.c
index 852b87d24b..85aee7fe86 100644
--- a/usr/src/uts/common/io/dls/dls_link.c
+++ b/usr/src/uts/common/io/dls/dls_link.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -36,7 +36,7 @@
#include <sys/atomic.h>
static kmem_cache_t *i_dls_link_cachep;
-static mod_hash_t *i_dls_link_hash;
+mod_hash_t *i_dls_link_hash;
static uint_t i_dls_link_count;
#define LINK_HASHSZ 67 /* prime */
diff --git a/usr/src/uts/common/io/dls/dls_mgmt.c b/usr/src/uts/common/io/dls/dls_mgmt.c
index bb922423b3..576e13ac2c 100644
--- a/usr/src/uts/common/io/dls/dls_mgmt.c
+++ b/usr/src/uts/common/io/dls/dls_mgmt.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -60,10 +60,15 @@ boolean_t devnet_need_rebuild;
/* Upcall door handle */
static door_handle_t dls_mgmt_dh = NULL;
-#define DD_CONDEMNED 0x1
+#define DD_CONDEMNED 0x1
+#define DD_KSTAT_CHANGING 0x2
/*
* This structure is used to keep the <linkid, macname> mapping.
+ * This structure itself is not protected by the mac perimeter, but is
+ * protected by the dd_mutex and i_dls_devnet_lock. Thus most of the
+ * functions manipulating this structure such as dls_devnet_set/unset etc.
+ * may be called while not holding the mac perimeter.
*/
typedef struct dls_devnet_s {
datalink_id_t dd_linkid;
@@ -614,6 +619,11 @@ dls_devnet_rele_link(dls_dl_handle_t dlh, dls_link_t *dlp)
/*
* Query the "link" kstats.
+ *
+ * We may be called from the kstat subsystem in an arbitrary context.
+ * If the caller is the stack, the context could be an upcall data
+ * thread. Hence we can't acquire the mac perimeter in this function
+ * for fear of deadlock.
*/
static int
dls_devnet_stat_update(kstat_t *ksp, int rw)
@@ -621,21 +631,34 @@ dls_devnet_stat_update(kstat_t *ksp, int rw)
dls_devnet_t *ddp = ksp->ks_private;
dls_link_t *dlp;
int err;
- mac_perim_handle_t mph;
- err = mac_perim_enter_by_macname(ddp->dd_mac, &mph);
- if (err != 0)
- return (err);
+ /*
+ * Check the link is being renamed or if the link is going away
+ * before incrementing dd_tref which in turn prevents the link
+ * from being renamed or deleted until we finish.
+ */
+ mutex_enter(&ddp->dd_mutex);
+ if (ddp->dd_flags & (DD_CONDEMNED | DD_KSTAT_CHANGING)) {
+ mutex_exit(&ddp->dd_mutex);
+ return (ENOENT);
+ }
+ ddp->dd_tref++;
+ mutex_exit(&ddp->dd_mutex);
- err = dls_link_hold(ddp->dd_mac, &dlp);
- if (err != 0) {
- mac_perim_exit(mph);
- return (err);
+ /*
+ * If a device detach happens at this time, it will block in
+ * dls_devnet_unset since the dd_tref has been bumped up above. So the
+ * access to 'dlp' is safe even though we don't hold the mac perimeter.
+ */
+ if (mod_hash_find(i_dls_link_hash, (mod_hash_key_t)ddp->dd_mac,
+ (mod_hash_val_t *)&dlp) != 0) {
+ dls_devnet_rele_tmp(ddp);
+ return (ENOENT);
}
err = dls_stat_update(ksp, dlp, rw);
- dls_link_rele(dlp);
- mac_perim_exit(mph);
+
+ dls_devnet_rele_tmp(ddp);
return (err);
}
@@ -707,6 +730,7 @@ dls_devnet_set(const char *macname, datalink_id_t linkid, dls_devnet_t **ddpp)
dls_devnet_t *ddp = NULL;
datalink_class_t class;
int err;
+ boolean_t stat_create = B_FALSE;
rw_enter(&i_dls_devnet_lock, RW_WRITER);
if ((err = mod_hash_find(i_dls_devnet_hash,
@@ -748,8 +772,7 @@ newphys:
(mod_hash_key_t)(uintptr_t)linkid,
(mod_hash_val_t)ddp) == 0);
devnet_need_rebuild = B_TRUE;
- dls_devnet_stat_create(ddp);
-
+ stat_create = B_TRUE;
mutex_enter(&ddp->dd_mutex);
if (!ddp->dd_prop_loaded && (ddp->dd_prop_taskid == NULL)) {
ddp->dd_prop_taskid = taskq_dispatch(system_taskq,
@@ -761,6 +784,20 @@ newphys:
err = 0;
done:
rw_exit(&i_dls_devnet_lock);
+ /*
+ * 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
+ * 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.
+ *
+ * The kstat subsystem holds its own locks (rather perimeter) before
+ * calling the ks_update (dls_devnet_stat_update) entry point which
+ * in turn grabs the i_dls_devnet_lock. So the lock hierarchy is
+ * kstat locks -> i_dls_devnet_lock.
+ */
+ if (stat_create)
+ dls_devnet_stat_create(ddp);
if (err == 0 && ddpp != NULL)
*ddpp = ddp;
return (err);
@@ -815,7 +852,6 @@ dls_devnet_unset(const char *macname, datalink_id_t *id, boolean_t wait)
VERIFY(mod_hash_remove(i_dls_devnet_id_hash,
(mod_hash_key_t)(uintptr_t)ddp->dd_linkid, &val) == 0);
- dls_devnet_stat_destroy(ddp);
devnet_need_rebuild = B_TRUE;
}
rw_exit(&i_dls_devnet_lock);
@@ -830,6 +866,9 @@ dls_devnet_unset(const char *macname, datalink_id_t *id, boolean_t wait)
ASSERT(ddp->dd_tref == 0 && ddp->dd_prop_taskid == NULL);
}
+ if (ddp->dd_linkid != DATALINK_INVALID_LINKID)
+ dls_devnet_stat_destroy(ddp);
+
ddp->dd_prop_loaded = B_FALSE;
ddp->dd_linkid = DATALINK_INVALID_LINKID;
ddp->dd_zid = GLOBAL_ZONEID;
@@ -1112,6 +1151,7 @@ dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link)
mac_perim_handle_t mph = NULL;
mac_handle_t mh;
mod_hash_val_t val;
+ boolean_t clear_dd_flag = B_FALSE;
/*
* In the second case, id2 must be a REMOVED physical link.
@@ -1134,8 +1174,10 @@ dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link)
* mac perimeter, hence enter the perimeter first. This also waits
* for the property loading to finish.
*/
- if ((err = mac_perim_enter_by_linkid(id1, &mph)) != 0)
- goto done;
+ if ((err = mac_perim_enter_by_linkid(id1, &mph)) != 0) {
+ softmac_rele_device(ddh);
+ return (err);
+ }
rw_enter(&i_dls_devnet_lock, RW_WRITER);
if ((err = mod_hash_find(i_dls_devnet_id_hash,
@@ -1146,13 +1188,22 @@ dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link)
}
/*
- * Return EBUSY if any applications have this link open.
+ * Return EBUSY if any applications have this link open or if any
+ * thread is currently accessing the link kstats. Then set the
+ * DD_KSTAT_CHANGING flag to prevent any access to the kstats
+ * while we delete and recreate kstats below.
*/
+ mutex_enter(&ddp->dd_mutex);
if (ddp->dd_ref > 1) {
+ mutex_exit(&ddp->dd_mutex);
err = EBUSY;
goto done;
}
+ ddp->dd_flags |= DD_KSTAT_CHANGING;
+ clear_dd_flag = B_TRUE;
+ mutex_exit(&ddp->dd_mutex);
+
if (id2 == DATALINK_INVALID_LINKID) {
(void) strlcpy(linkname, link, sizeof (linkname));
@@ -1225,11 +1276,21 @@ dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link)
done:
/*
* Change the name of the kstat based on the new link name.
+ * We can't hold the i_dls_devnet_lock across calls to the kstat
+ * subsystem. Instead the DD_KSTAT_CHANGING flag set above in this
+ * function prevents any access to the dd_ksp while we delete and
+ * recreate it below.
*/
+ rw_exit(&i_dls_devnet_lock);
if (err == 0)
dls_devnet_stat_rename(ddp, linkname);
- rw_exit(&i_dls_devnet_lock);
+ if (clear_dd_flag) {
+ mutex_enter(&ddp->dd_mutex);
+ ddp->dd_flags &= ~DD_KSTAT_CHANGING;
+ mutex_exit(&ddp->dd_mutex);
+ }
+
if (mph != NULL)
mac_perim_exit(mph);
softmac_rele_device(ddh);
@@ -1388,6 +1449,11 @@ dls_devnet_create(mac_handle_t mh, datalink_id_t linkid)
int err;
mac_perim_handle_t mph;
+ /*
+ * Holding the mac perimeter ensures that the downcall from the
+ * dlmgmt daemon which does the property loading does not proceed
+ * until we relinquish the perimeter.
+ */
mac_perim_enter_by_mh(mh, &mph);
/*
@@ -1400,8 +1466,8 @@ dls_devnet_create(mac_handle_t mh, datalink_id_t linkid)
return (err);
}
if ((err = dls_link_hold_create(mac_name(mh), &dlp)) != 0) {
- (void) dls_devnet_unset(mac_name(mh), &linkid, B_TRUE);
mac_perim_exit(mph);
+ (void) dls_devnet_unset(mac_name(mh), &linkid, B_TRUE);
return (err);
}
mac_perim_exit(mph);