summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJerry Jelinek <jerry.jelinek@joyent.com>2011-04-12 15:46:06 -0700
committerJerry Jelinek <jerry.jelinek@joyent.com>2011-04-12 15:46:06 -0700
commit3925eabb01912ea68ad1a482e7750865d7d8941d (patch)
tree9cfc270832b0bdf4f3ecc948e674e195ca2a5c27
parent16e1bf65c89fb01d6f0a244c67de0e7e90736f17 (diff)
downloadillumos-joyent-3925eabb01912ea68ad1a482e7750865d7d8941d.tar.gz
OS-375 i_dls_mgmt_upcall()/dlmgmt_zfop() deadlock in dlmgmtd
OS-376 vnic_dev_create()/dlmgmt_table_lock()/vnic_dev_delete() deadlock OS-378 dlmgmt_zonehalt()/dlmgmt_upcall_getattr() deadlock
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_door.c37
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_impl.h3
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_main.c2
-rw-r--r--usr/src/lib/brand/joyent/zone/statechange.ksh31
4 files changed, 54 insertions, 19 deletions
diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_door.c b/usr/src/cmd/dlmgmtd/dlmgmt_door.c
index e3e7fccd84..acb44870a8 100644
--- a/usr/src/cmd/dlmgmtd/dlmgmt_door.c
+++ b/usr/src/cmd/dlmgmtd/dlmgmt_door.c
@@ -59,7 +59,9 @@
#include <libsysevent.h>
#include <libdlmgmt.h>
#include <librcm.h>
-#include <thread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include "dlmgmt_impl.h"
typedef void dlmgmt_door_handler_t(void *, void *, size_t *, zoneid_t,
@@ -1318,14 +1320,6 @@ done:
retvalp->lr_err = err;
}
-static void
-do_zonehalt(void *zid)
-{
- dlmgmt_table_lock(B_TRUE);
- dlmgmt_db_fini((zoneid_t)zid);
- dlmgmt_table_unlock();
-}
-
/* ARGSUSED */
static void
dlmgmt_zonehalt(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
@@ -1350,17 +1344,22 @@ dlmgmt_zonehalt(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
* dls_devnet_destroy calls mac_perim_enter_by_mh
* which could lead to deadlock if another process is
* holding the mac perimeter then made an upcall to
- * dlmgmtd. To avoid this, we run the dlmgmt_db_fini
- * in a thread so that we can continue to service
- * upcalls in parallel with cleaning up zones.
+ * dlmgmtd. To try to avoid this, we serialize zone
+ * activity on the /etc/dladm/zone.lck file.
*/
- err = thr_create(NULL, 0,
- (void *(*)(void *))do_zonehalt,
- (void *)(zonehalt->ld_zoneid), THR_DAEMON, NULL);
- if (err != 0)
- dlmgmt_log(LOG_ERR, "unable to create thread "
- "to clean up zone %d: %s",
- zonehalt->ld_zoneid, strerror(err));
+ int fd;
+
+ while ((fd = open(ZONE_LOCK,
+ O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR))
+ < 0)
+ sleep(1);
+ close(fd);
+
+ dlmgmt_table_lock(B_TRUE);
+ dlmgmt_db_fini(zonehalt->ld_zoneid);
+ dlmgmt_table_unlock();
+
+ (void) unlink(ZONE_LOCK);
}
}
retvalp->lr_err = err;
diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_impl.h b/usr/src/cmd/dlmgmtd/dlmgmt_impl.h
index cdfd0d8a4d..780a18ce5a 100644
--- a/usr/src/cmd/dlmgmtd/dlmgmt_impl.h
+++ b/usr/src/cmd/dlmgmtd/dlmgmt_impl.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Joyent Inc. All rights reserved.
*/
/*
@@ -84,6 +85,8 @@ typedef struct dlmgmt_dlconf_s {
avl_node_t ld_node;
} dlmgmt_dlconf_t;
+#define ZONE_LOCK "/etc/dladm/zone.lck"
+
extern boolean_t debug;
extern const char *progname;
extern char cachefile[];
diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_main.c b/usr/src/cmd/dlmgmtd/dlmgmt_main.c
index 75e620a36b..7a26440a3f 100644
--- a/usr/src/cmd/dlmgmtd/dlmgmt_main.c
+++ b/usr/src/cmd/dlmgmtd/dlmgmt_main.c
@@ -287,6 +287,8 @@ dlmgmt_init(void)
return (err);
}
+ (void) unlink(ZONE_LOCK);
+
/*
* First derive the name of the cache file from the FMRI name. This
* cache name is used to keep active datalink configuration.
diff --git a/usr/src/lib/brand/joyent/zone/statechange.ksh b/usr/src/lib/brand/joyent/zone/statechange.ksh
index 5b9fe14aac..6511227691 100644
--- a/usr/src/lib/brand/joyent/zone/statechange.ksh
+++ b/usr/src/lib/brand/joyent/zone/statechange.ksh
@@ -55,6 +55,29 @@ ZONEPATH=$3
state=$4
cmd=$5
+LOCKFILE=/etc/dladm/zone.lck
+
+#
+# Create a lock file which we use to serialize datalink operations across zones.
+#
+lock_file()
+{
+ while true; do
+ if (set -o noclobber; echo "$$" >$LOCKFILE) 2>/dev/null; then
+ trap 'rm -f $LOCKFILE; exit $?' INT TERM EXIT
+ break;
+ else
+ sleep 1
+ fi
+ done
+}
+
+unlock_file()
+{
+ rm -f $LOCKFILE
+ trap - INT TERM EXIT
+}
+
#
# Set up the vnic(s) for the zone.
#
@@ -99,6 +122,8 @@ setup_net()
exit 1
fi
+ lock_file
+
#
# Create the vnic.
#
@@ -252,6 +277,8 @@ setup_net()
done
IFS=$OLDIFS
fi
+
+ unlock_file
done
}
@@ -287,12 +314,16 @@ cleanup_net()
# Cleanup any flows that were setup.
for nic in $_ZONECFG_net_resources
do
+ lock_file
+
flowadm remove-flow -t -z $ZONENAME -l $nic
if (( $? != 0 )); then
echo "error removing flows for $nic"
logger -p daemon.err "zone $ZONENAME " \
"error removing flows for $nic"
fi
+
+ unlock_file
done
}