diff options
| author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2011-05-10 06:01:20 -0700 |
|---|---|---|
| committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2011-05-10 06:01:20 -0700 |
| commit | b2cc8bf6a562b0c924b45a09d8d8754403037810 (patch) | |
| tree | 13986a3a14245cf553ec356368cecb50e47727e5 /usr/src | |
| parent | 9230f57fd5dc4b0898a592a83cfa3f7b18b26718 (diff) | |
| download | illumos-joyent-b2cc8bf6a562b0c924b45a09d8d8754403037810.tar.gz | |
OS-406 dlmgmtd deadlock when shutting down large numbers of zones
Diffstat (limited to 'usr/src')
| -rw-r--r-- | usr/src/cmd/dladm/dladm.c | 6 | ||||
| -rw-r--r-- | usr/src/cmd/zoneadmd/vplat.c | 5 | ||||
| -rw-r--r-- | usr/src/lib/libdladm/common/libdladm.h | 6 | ||||
| -rw-r--r-- | usr/src/lib/libdladm/common/libdlvnic.c | 15 | ||||
| -rw-r--r-- | usr/src/lib/libdladm/common/linkprop.c | 4 | ||||
| -rw-r--r-- | usr/src/uts/common/io/dld/dld_drv.c | 3 | ||||
| -rw-r--r-- | usr/src/uts/common/io/dls/dls_mgmt.c | 76 | ||||
| -rw-r--r-- | usr/src/uts/common/sys/dld.h | 1 | ||||
| -rw-r--r-- | usr/src/uts/common/sys/dls.h | 2 |
9 files changed, 90 insertions, 28 deletions
diff --git a/usr/src/cmd/dladm/dladm.c b/usr/src/cmd/dladm/dladm.c index c368a8c21b..cc9bd0097a 100644 --- a/usr/src/cmd/dladm/dladm.c +++ b/usr/src/cmd/dladm/dladm.c @@ -4754,6 +4754,12 @@ do_create_vnic(int argc, char *argv[], const char *use) if ((flags & DLADM_OPT_FORCE) != 0 && vid == 0) die("-f option can only be used with -v"); + /* + * If creating a transient VNIC for a zone, mark it in the kernel. + */ + if (strstr(propstr, "zone=") != NULL && !(flags & DLADM_OPT_PERSIST)) + flags |= DLADM_OPT_TRANSIENT; + if (mac_prefix_len != 0 && mac_addr_type != VNIC_MAC_ADDR_TYPE_RANDOM && mac_addr_type != VNIC_MAC_ADDR_TYPE_FIXED) usage(); diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c index 54aa111bf3..d2b8c79964 100644 --- a/usr/src/cmd/zoneadmd/vplat.c +++ b/usr/src/cmd/zoneadmd/vplat.c @@ -5191,11 +5191,6 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting, } break; case ZS_EXCLUSIVE: - if (unconfigure_exclusive_network_interfaces(zlogp, - zoneid) != 0) { - zerror(zlogp, B_FALSE, "unable to unconfigure " - "network interfaces in zone"); - } status = dladm_zone_halt(dld_handle, zoneid); if (status != DLADM_STATUS_OK) { zerror(zlogp, B_FALSE, "unable to notify " diff --git a/usr/src/lib/libdladm/common/libdladm.h b/usr/src/lib/libdladm/common/libdladm.h index f0811ae5df..1cfb927a41 100644 --- a/usr/src/lib/libdladm/common/libdladm.h +++ b/usr/src/lib/libdladm/common/libdladm.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent, Inc. All rights reserved. */ #ifndef _LIBDLADM_H @@ -71,6 +72,10 @@ extern "C" { * - DLADM_OPT_BOOT: * Bypass check functions during boot (used by pool property since pools * can come up after link properties are set) + * + * - DLADM_OPT_TRANSIENT: + * Indicates that the link assigned to a zone is transient and will be + * removed when the zone shuts down. */ #define DLADM_OPT_ACTIVE 0x00000001 #define DLADM_OPT_PERSIST 0x00000002 @@ -81,6 +86,7 @@ extern "C" { #define DLADM_OPT_VLAN 0x00000040 #define DLADM_OPT_NOREFRESH 0x00000080 #define DLADM_OPT_BOOT 0x00000100 +#define DLADM_OPT_TRANSIENT 0x00000200 #define DLADM_WALK_TERMINATE 0 #define DLADM_WALK_CONTINUE -1 diff --git a/usr/src/lib/libdladm/common/libdlvnic.c b/usr/src/lib/libdladm/common/libdlvnic.c index a5655cddae..1a866dcb06 100644 --- a/usr/src/lib/libdladm/common/libdlvnic.c +++ b/usr/src/lib/libdladm/common/libdlvnic.c @@ -546,13 +546,22 @@ dladm_vnic_create(dladm_handle_t handle, const char *vnic, datalink_id_t linkid, done: if (status == DLADM_STATUS_OK && proplist != NULL) { + uint32_t flg; + + flg = (flags & DLADM_OPT_PERSIST) ? + DLADM_OPT_PERSIST : DLADM_OPT_ACTIVE; + for (i = 0; i < proplist->al_count; i++) { dladm_arg_info_t *aip = &proplist->al_info[i]; + if (strcmp(aip->ai_name, "zone") == 0 && + flags & DLADM_OPT_TRANSIENT) + flg |= DLADM_OPT_TRANSIENT; + else + flg &= ~DLADM_OPT_TRANSIENT; + status = dladm_set_linkprop(handle, vnic_id, - aip->ai_name, aip->ai_val, aip->ai_count, - ((flags & DLADM_OPT_PERSIST) ? - DLADM_OPT_PERSIST : DLADM_OPT_ACTIVE)); + aip->ai_name, aip->ai_val, aip->ai_count, flg); if (status != DLADM_STATUS_OK) break; } diff --git a/usr/src/lib/libdladm/common/linkprop.c b/usr/src/lib/libdladm/common/linkprop.c index 229a5fd83f..21d93b5932 100644 --- a/usr/src/lib/libdladm/common/linkprop.c +++ b/usr/src/lib/libdladm/common/linkprop.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent, Inc. All rights reserved. */ #include <stdlib.h> @@ -1513,6 +1514,9 @@ set_zone(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid, if (zid_new == zid_old) return (DLADM_STATUS_OK); + if (flags & DLADM_OPT_TRANSIENT) + dzp->diz_transient = B_TRUE; + if ((status = set_public_prop(handle, pdp, linkid, vdp, val_cnt, flags, media)) != DLADM_STATUS_OK) return (status); diff --git a/usr/src/uts/common/io/dld/dld_drv.c b/usr/src/uts/common/io/dld/dld_drv.c index 0c297fac44..2152ce0baa 100644 --- a/usr/src/uts/common/io/dld/dld_drv.c +++ b/usr/src/uts/common/io/dld/dld_drv.c @@ -702,7 +702,8 @@ drv_ioc_prop_common(dld_ioc_macprop_t *prop, intptr_t arg, boolean_t set, err = EACCES; goto done; } - err = dls_devnet_setzid(dlh, dzp->diz_zid); + err = dls_devnet_setzid(dlh, dzp->diz_zid, + dzp->diz_transient); } else { kprop->pr_perm_flags = MAC_PROP_PERM_RW; (*(zoneid_t *)kprop->pr_val) = dls_devnet_getzid(dlh); diff --git a/usr/src/uts/common/io/dls/dls_mgmt.c b/usr/src/uts/common/io/dls/dls_mgmt.c index acc277f02a..0281091080 100644 --- a/usr/src/uts/common/io/dls/dls_mgmt.c +++ b/usr/src/uts/common/io/dls/dls_mgmt.c @@ -106,12 +106,13 @@ typedef struct dls_devnet_s { zoneid_t dd_zid; /* current zone */ boolean_t dd_prop_loaded; taskqid_t dd_prop_taskid; + boolean_t dd_transient; /* link goes away when zone does */ } dls_devnet_t; static int i_dls_devnet_create_iptun(const char *, const char *, datalink_id_t *); static int i_dls_devnet_destroy_iptun(datalink_id_t); -static int i_dls_devnet_setzid(dls_devnet_t *, zoneid_t, boolean_t); +static int i_dls_devnet_setzid(dls_devnet_t *, zoneid_t, boolean_t, boolean_t); static int dls_devnet_unset(const char *, datalink_id_t *, boolean_t); /*ARGSUSED*/ @@ -146,7 +147,12 @@ dls_zone_remove(datalink_id_t linkid, void *arg) dls_devnet_t *ddp; if (dls_devnet_hold_tmp(linkid, &ddp) == 0) { - (void) dls_devnet_setzid(ddp, GLOBAL_ZONEID); + /* + * Don't bother moving transient links back to the global zone + * since we will simply delete them in dls_devnet_unset. + */ + if (!ddp->dd_transient) + (void) dls_devnet_setzid(ddp, GLOBAL_ZONEID, B_FALSE); dls_devnet_rele_tmp(ddp); } return (0); @@ -901,7 +907,8 @@ done: rw_exit(&i_dls_devnet_lock); if (err == 0) { if (zoneid != GLOBAL_ZONEID && - (err = i_dls_devnet_setzid(ddp, zoneid, B_FALSE)) != 0) + (err = i_dls_devnet_setzid(ddp, zoneid, B_FALSE, + B_FALSE)) != 0) (void) dls_devnet_unset(macname, &linkid, B_TRUE); /* * The kstat subsystem holds its own locks (rather perimeter) @@ -947,23 +954,53 @@ dls_devnet_unset(const char *macname, datalink_id_t *id, boolean_t wait) ASSERT(ddp->dd_ref != 0); if ((ddp->dd_ref != 1) || (!wait && (ddp->dd_tref != 0 || ddp->dd_prop_taskid != NULL))) { + int zstatus = 0; + /* - * Its possible that we're trying to clean up an orphaned - * vnic that was delegated to a zone and which wasn't cleaned - * up properly when the zone went away. Check for this - * case before we return EBUSY. + * There are a couple of alternatives that might be going on + * here; a) the zone is shutting down and it has a transient + * link assigned, in which case we want to clean it up instead + * of moving it back to the global zone, or b) its possible + * that we're trying to clean up an orphaned vnic that was + * delegated to a zone and which wasn't cleaned up properly + * when the zone went away. Check for either of these cases + * 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_find_by_id(ddp->dd_zid) == NULL) { - /* Log a warning, but continue in this case */ - cmn_err(CE_WARN, "clear orphaned datalink: %s\n", - ddp->dd_linkname); - ddp->dd_ref = 1; - } else { + if (ddp->dd_ref > 1 && ddp->dd_zid != GLOBAL_ZONEID) { + zone_t *zp; + + if ((zp = zone_find_by_id(ddp->dd_zid)) == NULL) { + zstatus = -1; + } else { + if (ddp->dd_transient) { + zone_status_t s = zone_status_get(zp); + + if (s >= ZONE_IS_SHUTTING_DOWN) + zstatus = 1; + } + zone_rele(zp); + } + } + + if (zstatus == 0) { mutex_exit(&ddp->dd_mutex); rw_exit(&i_dls_devnet_lock); return (EBUSY); } + + /* + * 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; @@ -971,7 +1008,8 @@ dls_devnet_unset(const char *macname, datalink_id_t *id, boolean_t wait) *id = ddp->dd_linkid; if (ddp->dd_zid != GLOBAL_ZONEID) - (void) i_dls_devnet_setzid(ddp, GLOBAL_ZONEID, B_FALSE); + (void) i_dls_devnet_setzid(ddp, GLOBAL_ZONEID, B_FALSE, + B_FALSE); /* * Remove this dls_devnet_t from the hash table. @@ -1475,7 +1513,8 @@ done: } static int -i_dls_devnet_setzid(dls_devnet_t *ddp, zoneid_t new_zoneid, boolean_t setprop) +i_dls_devnet_setzid(dls_devnet_t *ddp, zoneid_t new_zoneid, boolean_t setprop, + boolean_t transient) { int err; mac_perim_handle_t mph; @@ -1508,6 +1547,7 @@ i_dls_devnet_setzid(dls_devnet_t *ddp, zoneid_t new_zoneid, boolean_t setprop) } if ((err = dls_link_setzid(ddp->dd_mac, new_zoneid)) == 0) { ddp->dd_zid = new_zoneid; + ddp->dd_transient = transient; devnet_need_rebuild = B_TRUE; } @@ -1522,7 +1562,7 @@ done: } int -dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid) +dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid, boolean_t transient) { dls_devnet_t *ddp; int err; @@ -1544,7 +1584,7 @@ dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid) refheld = B_TRUE; } - if ((err = i_dls_devnet_setzid(ddh, new_zid, B_TRUE)) != 0) { + if ((err = i_dls_devnet_setzid(ddh, new_zid, B_TRUE, transient)) != 0) { if (refheld) dls_devnet_rele(ddp); return (err); diff --git a/usr/src/uts/common/sys/dld.h b/usr/src/uts/common/sys/dld.h index 50cb34260b..303a9c7e45 100644 --- a/usr/src/uts/common/sys/dld.h +++ b/usr/src/uts/common/sys/dld.h @@ -205,6 +205,7 @@ typedef struct dld_ioc_rename { typedef struct dld_ioc_zid { zoneid_t diz_zid; datalink_id_t diz_linkid; + boolean_t diz_transient; } dld_ioc_zid_t; /* diff --git a/usr/src/uts/common/sys/dls.h b/usr/src/uts/common/sys/dls.h index e0df775deb..adcfe76c08 100644 --- a/usr/src/uts/common/sys/dls.h +++ b/usr/src/uts/common/sys/dls.h @@ -128,7 +128,7 @@ extern uint16_t dls_devnet_vid(dls_dl_handle_t); extern datalink_id_t dls_devnet_linkid(dls_dl_handle_t); extern int dls_devnet_dev2linkid(dev_t, datalink_id_t *); extern int dls_devnet_phydev(datalink_id_t, dev_t *); -extern int dls_devnet_setzid(dls_dl_handle_t, zoneid_t); +extern int dls_devnet_setzid(dls_dl_handle_t, zoneid_t, boolean_t); extern zoneid_t dls_devnet_getzid(dls_dl_handle_t); extern zoneid_t dls_devnet_getownerzid(dls_dl_handle_t); extern boolean_t dls_devnet_islinkvisible(datalink_id_t, zoneid_t); |
