changeset 5029:ffbab30c82bd

6592150 PICL SNMP and PRI plug-ins don't handle picld being sent a SIGHUP
author fw157321
date Tue, 11 Sep 2007 16:39:42 -0700
parents 71f71fe9e4b9
children 15cab3a79834
files usr/src/cmd/picl/picld/picl.xml usr/src/cmd/picl/plugins/sun4v/pri/init.c usr/src/cmd/picl/plugins/sun4v/pri/io_dev_label.c usr/src/cmd/picl/plugins/sun4v/pri/mem_prop_update.c usr/src/cmd/picl/plugins/sun4v/pri/priplugin.c usr/src/cmd/picl/plugins/sun4v/pri/priplugin.h usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.c
diffstat 7 files changed, 346 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/picl/picld/picl.xml	Tue Sep 11 16:36:53 2007 -0700
+++ b/usr/src/cmd/picl/picld/picl.xml	Tue Sep 11 16:39:42 2007 -0700
@@ -60,6 +60,12 @@
 
 	<exec_method
 		type='method'
+		name='refresh'
+		exec=':kill -1'
+		timeout_seconds='60' />
+
+	<exec_method
+		type='method'
 		name='start'
 		exec='/usr/lib/picl/picld'
 		timeout_seconds='60' />
--- a/usr/src/cmd/picl/plugins/sun4v/pri/init.c	Tue Sep 11 16:36:53 2007 -0700
+++ b/usr/src/cmd/picl/plugins/sun4v/pri/init.c	Tue Sep 11 16:39:42 2007 -0700
@@ -35,29 +35,40 @@
 
 static void pri_free(void *bufp, size_t size);
 static uint64_t *md_bufp = NULL;
+static uint64_t *new_md_bufp;
 
-md_t *
-pri_devinit(uint64_t *tok, md_t *mdp)
+int
+pri_devinit(uint64_t *tok)
 {
-	uint64_t *new_md_bufp;
+	int status;
 
-
+	new_md_bufp = NULL;
+	status = 0;
 	if (pri_get(PRI_WAITGET, tok, &new_md_bufp, malloc, pri_free) ==
 	    (ssize_t)-1) {
 		pri_debug(LOG_NOTICE, "pri_devinit: can'r read from "
 		    "the PRI: %d\n", errno);
+		status = -1;
 	}
 	if (new_md_bufp == NULL) {
-		pri_debug(LOG_NOTICE, "pri_devinit: pri_get returned"
+		pri_debug(LOG_NOTICE, "pri_devinit: pri_get returned "
 		    "NULL buffer!\n");
+		status = -1;
 	}
+	return (status);
+}
+
+md_t *
+pri_bufinit(md_t *mdp)
+{
+
 	if (mdp)
 		md_fini(mdp);
 	if (md_bufp)
 		free(md_bufp);
 	md_bufp = new_md_bufp;
 
-	pri_debug(LOG_NOTICE, "pri_devinit: done reading PRI\n");
+	pri_debug(LOG_NOTICE, "pri_bufinit: done reading PRI\n");
 
 	/*
 	 * The PRI and the MD use the same data format so they can be
@@ -66,18 +77,18 @@
 	if (md_bufp) {
 		mdp = md_init_intern(md_bufp, malloc, pri_free);
 		if (mdp == NULL) {
-			pri_debug(LOG_NOTICE, "pri_devinit: md_init_intern "
+			pri_debug(LOG_NOTICE, "pri_bufinit: md_init_intern "
 			"failed\n");
 			free(md_bufp);
 			md_bufp = NULL;
 		} else {
-			pri_debug(LOG_NOTICE, "pri_devinit: mdi_init_intern "
+			pri_debug(LOG_NOTICE, "pri_bufinit: mdi_init_intern "
 			    "completed successfully\n");
 		}
 	} else
 		mdp = NULL;
 
-	pri_debug(LOG_NOTICE, "pri_devinit: returning\n");
+	pri_debug(LOG_NOTICE, "pri_bufinit: returning\n");
 
 	return (mdp);
 }
@@ -99,5 +110,4 @@
 	if (md_bufp)
 		free(md_bufp);
 	md_bufp = NULL;
-	pri_fini();
 }
--- a/usr/src/cmd/picl/plugins/sun4v/pri/io_dev_label.c	Tue Sep 11 16:36:53 2007 -0700
+++ b/usr/src/cmd/picl/plugins/sun4v/pri/io_dev_label.c	Tue Sep 11 16:39:42 2007 -0700
@@ -28,12 +28,6 @@
 
 #include "priplugin.h"
 
-/*
- * These 3 variable are defined and set in mdescplugin.c
- */
-extern picl_nodehdl_t	root_node;
-extern mde_cookie_t	rootnode;
-
 static int
 find_node_by_string_prop(picl_nodehdl_t rooth, const char *pname,
     const char *pval, picl_nodehdl_t *nodeh);
@@ -55,12 +49,14 @@
 	picl_nodehdl_t platnode, tpn;
 	char busaddr[PICL_PROPNAMELEN_MAX], *p, *q;
 	char path[PICL_PROPNAMELEN_MAX];
-	mde_cookie_t *components;
+	mde_cookie_t *components, md_rootnode;
 	char *type, *nac, *pri_path, *saved_path;
 
 	if (mdp == NULL)
 		return;
 
+	md_rootnode = md_root_node(mdp);
+
 	/*
 	 * Find and remember the roots of the /frutree and /platform trees.
 	 */
@@ -86,7 +82,7 @@
 		return;
 	}
 
-	component_count = md_scan_dag(mdp, rootnode,
+	component_count = md_scan_dag(mdp, md_rootnode,
 	    md_find_name(mdp, "component"),
 	    md_find_name(mdp, "fwd"), components);
 
--- a/usr/src/cmd/picl/plugins/sun4v/pri/mem_prop_update.c	Tue Sep 11 16:36:53 2007 -0700
+++ b/usr/src/cmd/picl/plugins/sun4v/pri/mem_prop_update.c	Tue Sep 11 16:39:42 2007 -0700
@@ -36,12 +36,6 @@
 #include "priplugin.h"
 #include "../../common/memcfg/piclmemcfg.h"
 
-/*
- * These 3 variable are defined and set in mdescplugin.c
- */
-extern picl_nodehdl_t	root_node;
-extern mde_cookie_t	rootnode;
-
 static void
 add_memory_props(picl_nodehdl_t node, mde_cookie_t memorylistp, md_t *mdp,
 	uint64_t size);
@@ -65,7 +59,7 @@
 {
 	mde_cookie_t *memorylistp, *segmentlistp, *banklistp;
 	picl_prophdl_t memh, segmenth, bankh;
-	mde_cookie_t *buf;
+	mde_cookie_t *buf, md_rootnode;
 	int j, k, num_nodes, interleave, err;
 	int nsegments, nbanks, nmemory;
 	uint64_t memsize, segsize, segbase;
@@ -75,6 +69,8 @@
 	if (mdp == NULL)
 		return (PICL_WALK_CONTINUE);
 
+	md_rootnode = md_root_node(mdp);
+
 	/*
 	 * An absence of nodes or failure to obtain memory for searches
 	 * or absence of the /memory node will cause this to fail.
@@ -108,7 +104,7 @@
 	 * return PICL_PROPNOTFOUND to get the caller to re-try with
 	 * a different property name.
 	 */
-	nmemory = md_scan_dag(mdp, rootnode, md_find_name(mdp,
+	nmemory = md_scan_dag(mdp, md_rootnode, md_find_name(mdp,
 	    "memory-segments"), md_find_name(mdp, "fwd"), memorylistp);
 	if (nmemory != 1) {
 		pri_debug(LOG_NOTICE,
--- a/usr/src/cmd/picl/plugins/sun4v/pri/priplugin.c	Tue Sep 11 16:36:53 2007 -0700
+++ b/usr/src/cmd/picl/plugins/sun4v/pri/priplugin.c	Tue Sep 11 16:39:42 2007 -0700
@@ -31,15 +31,22 @@
 
 #pragma init(priplugin_register)	/* place in .init section */
 
-picl_nodehdl_t	root_node;
-static md_t	*mdp;
-mde_cookie_t	rootnode;
+static md_t *mdp;
+
+static mutex_t	rebuild_lock;
+static cond_t	rebuild_cv;
 
-void priplugin_init(void);
-void priplugin_fini(void);
+static thread_t pri_worker_thread_id, pri_reader_thread_id;
+static boolean_t all_thr_exit = B_FALSE;
+
+static void priplugin_init(void);
+static void priplugin_fini(void);
 static void
 event_handler(const char *ename, const void *earg, size_t size, void *cookie);
-static void *priplugin_main(void *);
+static void *pri_worker_thread(void *arg);
+static void *pri_reader_thread(void *arg);
+static int remove_old_segments(picl_nodehdl_t node, void *args);
+
 
 picld_plugin_reg_t priplugin_reg = {
 	PICLD_PLUGIN_VERSION_1,
@@ -49,7 +56,7 @@
 	priplugin_fini
 };
 
-void
+static void
 set_prop_info(ptree_propinfo_t *propinfo, int size, char *name, int type)
 {
 	propinfo->version = PICLD_PLUGIN_VERSION_1;
@@ -89,88 +96,266 @@
 	}
 }
 
-void
+/*ARGSUSED*/
+static int
+remove_old_segments(picl_nodehdl_t node, void *args)
+{
+	int status;
+
+	if ((status = ptree_delete_node(node)) == PICL_SUCCESS)
+		ptree_destroy_node(node);
+	else
+		pri_debug(LOG_NOTICE, "remove_old_segments: can't delete "
+		    "segment node: %s\n", picl_strerror(status));
+
+	return (PICL_WALK_CONTINUE);
+}
+
+static void
 priplugin_init(void)
 {
 	int status;
 
-
 	pri_debug(LOG_NOTICE, "priplugin: mem tree and io label thread "
 	    "being created; callbacks being registered\n");
 
-	/*
-	 * register event_handler for both "sysevent-device-added" and
-	 * and for "sysevent-device-removed" PICL events
-	 */
-	(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
-	    event_handler, NULL);
-	(void) ptree_register_handler(PICLEVENT_DR_AP_STATE_CHANGE,
-	    event_handler, NULL);
+	all_thr_exit = B_FALSE;
+
+	(void) mutex_init(&rebuild_lock, USYNC_THREAD, NULL);
+	(void) cond_init(&rebuild_cv, USYNC_THREAD, NULL);
 
-	if ((status = thr_create(NULL, NULL, priplugin_main, NULL, THR_BOUND,
-	    NULL)) < 0) {
-		pri_debug(LOG_NOTICE, "priplugin: can't create main thread: "
+	if ((status = thr_create(NULL, NULL, pri_worker_thread, NULL, THR_BOUND,
+	    &pri_worker_thread_id)) < 0) {
+		pri_debug(LOG_NOTICE, "priplugin: can't create worker thread: "
+		    "%d\n", status);
+		all_thr_exit = B_TRUE;
+		(void) mutex_destroy(&rebuild_lock);
+		(void) cond_destroy(&rebuild_cv);
+	} else if ((status = thr_create(NULL, NULL, pri_reader_thread, NULL,
+	    THR_BOUND, &pri_reader_thread_id)) < 0) {
+		pri_debug(LOG_NOTICE, "priplugin: can't create reader thread: "
 		    "%d\n", status);
-		priplugin_fini();
+		(void) mutex_lock(&rebuild_lock);
+		all_thr_exit = B_TRUE;
+		(void) cond_signal(&rebuild_cv);
+		(void) mutex_unlock(&rebuild_lock);
+		(void) thr_join(pri_worker_thread_id, NULL, NULL);
+		(void) mutex_destroy(&rebuild_lock);
+		(void) cond_destroy(&rebuild_cv);
+	} else {
+		pri_debug(LOG_NOTICE, "priplugin_init: worker and reader "
+		    "threads created - registering event handlers\n");
+		/*
+		 * register event_handler for both "sysevent-device-added",
+		 * "sysevent_device_removed", and for
+		 * "sysevent-dr-app-state-change" PICL events
+		 */
+		(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
+		    event_handler, NULL);
+		(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
+		    event_handler, NULL);
+		(void) ptree_register_handler(PICLEVENT_DR_AP_STATE_CHANGE,
+		    event_handler, NULL);
 	}
 }
 
+/*
+ * This thread handles the main processing of PRI data.  It is woken
+ * up by either the event handler, to process a PICL event, or it is
+ * woken up by the PRI reader thread which has just fetched a new
+ * copy of the PRI.
+ */
 /*ARGSUSED*/
 static void *
-priplugin_main(void *arg)
+pri_worker_thread(void *arg)
 {
 	int status;
+	picl_nodehdl_t picl_root_node;
+
+	pri_debug(LOG_NOTICE, "pri_worker_thread: start\n");
+
+	(void) mutex_lock(&rebuild_lock);
+	while (1) {
+		(void) cond_wait(&rebuild_cv, &rebuild_lock);
+
+		if (all_thr_exit == B_TRUE) {
+			(void) mutex_unlock(&rebuild_lock);
+			pri_debug(LOG_NOTICE, "pri_worker_thread: time to "
+			    "exit\n");
+			break;
+		}
+
+		status = ptree_get_root(&picl_root_node);
+		if (status != PICL_SUCCESS) {
+			pri_debug(LOG_NOTICE, "pri_worker_thread: "
+			    "can't get picl tree root node: %s\n",
+			    picl_strerror(status));
+			continue;
+		}
+
+		pri_debug(LOG_NOTICE, "pri_worker_thread: have root picl "
+		    "and PRI nodes\n");
+
+		status = ptree_walk_tree_by_class(picl_root_node,
+		    "memory-segment", NULL, remove_old_segments);
+		if (status != PICL_SUCCESS) {
+			pri_debug(LOG_NOTICE, "pri_worker_thread: can't remove "
+			    "old memory segments: \n", picl_strerror(status));
+		} else
+			pri_debug(LOG_NOTICE, "pri_worker_thread: old memory "
+			    "segments removed\n");
+
+		status = ptree_walk_tree_by_class(picl_root_node, "memory",
+		    (void *) mdp, add_mem_prop);
+		if (status != PICL_SUCCESS) {
+			pri_debug(LOG_NOTICE, "pri_worker_thread: memory "
+			    "segments walk failed: \n", picl_strerror(status));
+		} else
+			pri_debug(LOG_NOTICE, "pri_worker_thread: success "
+			    "walking memory node\n");
+
+		io_dev_addlabel(mdp);
+	}
+	pri_debug(LOG_NOTICE, "pri_worker_thread: exiting\n");
+	return (NULL);
+}
+
+/*
+ * This thread camps out in the PRI driver, waiting for it to return
+ * the contents of a new PRI.  When the PRI is changed this thread
+ * reads that data and prepares it for processing by the worker thread.
+ * It then signals the worker thread to process the new PRI data.
+ */
+/*ARGSUSED*/
+static void *
+pri_reader_thread(void *arg)
+{
 	uint64_t tok;
+	int status, count;
+
+	pri_debug(LOG_NOTICE, "pri_reader_thread: thread start\n");
 
 	if (pri_init() != 0) {
-		pri_debug(LOG_NOTICE, "priplugin_main: pri_init failed\n");
+		pri_debug(LOG_NOTICE, "pri_reader_thread: pri_init failed\n");
 		return (NULL);
 	}
 
-	pri_debug(LOG_NOTICE, "priplugin: thread entered\n");
-	status = ptree_get_root(&root_node);
-	if (status != PICL_SUCCESS) {
-		pri_debug(LOG_NOTICE, "priplugin: can't get picl root "
-		    "node\n");
-		priplugin_fini();
-		return (NULL);
+	/*
+	 * It's entirely possible that a new PRI may get pushed while
+	 * the worker thread is processing the previous PRI.  We will
+	 * wait until the worker is finished, then flush the old contents
+	 * and wake up the worker again to process the new data.
+	 */
+	mdp = NULL;
+	tok = 0;
+	count = 0;
+	while (1) {
+		/*
+		 * The _fini() function will close the PRI's fd, which will
+		 * cause this function to break out of waiting in the PRI
+		 * driver and return an error.
+		 */
+		status = pri_devinit(&tok);
+
+		(void) mutex_lock(&rebuild_lock);
+		if (all_thr_exit == B_TRUE) {
+			(void) mutex_unlock(&rebuild_lock);
+			pri_debug(LOG_NOTICE, "pri_reader_thread: time to "
+			    "exit\n");
+			break;
+		}
+
+		/*
+		 * Wait until the worker is idle before swapping in the
+		 * new PRI contents, then signal the worker to process
+		 * that new data.
+		 */
+		if (status == 0) {
+			pri_debug(LOG_NOTICE, "pri_reader_thread: got PRI\n");
+
+			/* old buffer will be freed by pri_bufinit() */
+			mdp = pri_bufinit(mdp);
+			if (mdp != NULL) {
+				(void) cond_signal(&rebuild_cv);
+				count = 0;
+			} else {
+				pri_debug(LOG_NOTICE, "pri_reader_thread: "
+				    "NULL mdp!\n");
+				status = -1;
+			}
+		}
+
+		/*
+		 * Try to handle SP resets or other unexplained errors
+		 * from ds by closing down and re-opening the PRI driver.
+		 */
+		if (status == -1) {
+			if (errno != 0) {
+				pri_debug(LOG_NOTICE, "pri_reader_thread: "
+				    "can't get PRI contents: %s\n",
+				    strerror(errno));
+			}
+			if (++count > 6) {
+				pri_debug(LOG_NOTICE, "pci_reader_thread: "
+				    "can't process PRI data\n");
+				(void) mutex_unlock(&rebuild_lock);
+				break;
+			}
+			/* old buffer will be freed by pri_fini() */
+			pri_fini();
+			tok = 0;
+			sleep(10);
+			if (pri_init() != 0) {
+				pri_debug(LOG_NOTICE, "pci_reader_thread: "
+				    "can't reinitialize PRI driver\n");
+				(void) mutex_unlock(&rebuild_lock);
+				break;
+			}
+		}
+		(void) mutex_unlock(&rebuild_lock);
 	}
 
-	for (tok = 0; mdp = pri_devinit(&tok, mdp); ) {
-		rootnode = md_root_node(mdp);
-
-		pri_debug(LOG_NOTICE, "priplugin: have root picl and PRI "
-		    "nodes\n");
-
-		status = ptree_walk_tree_by_class(root_node, "memory",
-		    (void *) mdp, add_mem_prop);
-		if (status != PICL_SUCCESS) {
-			pri_debug(LOG_NOTICE, "pri: memory-segments walk "
-			    "failed\n");
-		} else
-			pri_debug(LOG_NOTICE, "pri: success walking memory "
-			    "node\n");
-
-		io_dev_addlabel(mdp);
-	}
-
-	pri_debug(LOG_NOTICE, "priplugin: cannot init pri: " "%d\n", errno);
-
-	priplugin_fini();
-
-	/*NOTREACHED*/
+	pri_debug(LOG_NOTICE, "pri_reader_thread: thread exiting\n");
 	return (NULL);
 }
 
-void
+static void
 priplugin_fini(void)
 {
-	pri_devfini(mdp);
-	/* unregister the event handler */
+	pri_debug(LOG_NOTICE, "priplugin_fini: called\n");
+
+	if (all_thr_exit == B_TRUE)
+		return;
+
+	/* unregister the event handlers */
 	(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
 	    event_handler, NULL);
+	(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
+	    event_handler, NULL);
 	(void) ptree_unregister_handler(PICLEVENT_DR_AP_STATE_CHANGE,
 	    event_handler, NULL);
+
+	/*
+	 * Set the exit flag to tell the worker thread to quit and wake
+	 * up that thread.  Once that thread is reaped then pull the rug
+	 * out from the PRI reader thread by calling pri_fini(), which
+	 * closes the PRI fd.  That wakes the PRI reader thread and it
+	 * will then exit as well.
+	 */
+	(void) mutex_lock(&rebuild_lock);
+	all_thr_exit = B_TRUE;
+	(void) cond_signal(&rebuild_cv);
+	(void) mutex_unlock(&rebuild_lock);
+
+	(void) thr_join(pri_worker_thread_id, NULL, NULL);
+
+	pri_devfini(mdp);
+	mdp = NULL;
+	pri_fini();
+	(void) thr_join(pri_reader_thread_id, NULL, NULL);
+
+	(void) mutex_destroy(&rebuild_lock);
+	(void) cond_destroy(&rebuild_cv);
 }
 
 void
@@ -183,6 +368,7 @@
  * Discovery event handler
  * respond to the picl events:
  *      PICLEVENT_SYSEVENT_DEVICE_ADDED
+ *      PICLEVENT_SYSEVENT_DEVICE_REMOVED
  *      PICLEVENT_DR_AP_STATE_CHANGE
  *
  * We can't do much of anything fancy since the event data doesn't contain
@@ -193,12 +379,19 @@
 static void
 event_handler(const char *ename, const void *earg, size_t size, void *cookie)
 {
-
+	pri_debug(LOG_NOTICE, "pri: event_handler: caught event "
+	    "%s\n", ename);
 	if ((strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_ADDED) == 0) ||
+	    (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_REMOVED) == 0) ||
 	    (strcmp(ename, PICLEVENT_DR_AP_STATE_CHANGE) == 0)) {
-		pri_debug(LOG_NOTICE, "pri: event_handler: caught event "
-		    "%s; adding labels\n", ename);
-		io_dev_addlabel(mdp);
+		pri_debug(LOG_NOTICE, "pri: event_handler: handle event "
+		    "%s; waking worker thread\n", ename);
+		(void) mutex_lock(&rebuild_lock);
+
+		if (all_thr_exit == B_FALSE)
+			(void) cond_signal(&rebuild_cv);
+
+		(void) mutex_unlock(&rebuild_lock);
 	}
 }
 
--- a/usr/src/cmd/picl/plugins/sun4v/pri/priplugin.h	Tue Sep 11 16:36:53 2007 -0700
+++ b/usr/src/cmd/picl/plugins/sun4v/pri/priplugin.h	Tue Sep 11 16:39:42 2007 -0700
@@ -65,12 +65,9 @@
 #define	PRI_DEBUG 0
 #endif
 
-/* These 3 variable are defined and set in mdescplugin.c */
-extern picl_nodehdl_t	root_node;
-extern mde_cookie_t	rootnode;
-
 int add_mem_prop(picl_nodehdl_t node, void *args);
-md_t *pri_devinit(uint64_t *, md_t *);
+int pri_devinit(uint64_t *);
+md_t *pri_bufinit(md_t *mdp);
 void pri_devfini(md_t *mdp);
 void pri_debug(int level, char *fmt, ...);
 void add_md_prop(picl_nodehdl_t node, int size, char *name, void* value,
--- a/usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.c	Tue Sep 11 16:36:53 2007 -0700
+++ b/usr/src/cmd/picl/plugins/sun4v/snmp/snmpplugin.c	Tue Sep 11 16:39:42 2007 -0700
@@ -96,6 +96,8 @@
 static mutex_t		rebuild_tree_lock;
 static cond_t		rebuild_tree_cv;
 static boolean_t	rebuild_tree = B_TRUE;
+static boolean_t	tree_builder_thr_exit = B_FALSE;
+static thread_t		tree_builder_thr_id;
 
 /*
  * These two should really not be global
@@ -270,6 +272,8 @@
 	(void) mutex_init(&rebuild_tree_lock, USYNC_THREAD, NULL);
 	(void) cond_init(&rebuild_tree_cv, USYNC_THREAD, NULL);
 	(void) rwlock_init(&stale_tree_rwlp, USYNC_THREAD, NULL);
+	tree_builder_thr_exit = B_FALSE;
+
 	LOGINIT();
 
 	/*
@@ -277,18 +281,57 @@
 	 */
 	LOGPRINTF("Tree-builder thread being created.\n");
 	if ((ret = thr_create(NULL, NULL, tree_builder, NULL,
-	    THR_BOUND, NULL)) < 0) {
+	    THR_BOUND, &tree_builder_thr_id)) < 0) {
 		log_msg(LOG_ERR, SNMPP_CANT_CREATE_TREE_BUILDER, ret);
 		snmp_fini(hdl);
-		return;
+		hdl = NULL;
+		(void) rwlock_destroy(&stale_tree_rwlp);
+		(void) cond_destroy(&rebuild_tree_cv);
+		(void) mutex_destroy(&rebuild_tree_lock);
+		tree_builder_thr_exit = B_TRUE;
 	}
 }
 
 void
 snmpplugin_fini(void)
 {
-	snmp_fini(hdl);
+
+	if (tree_builder_thr_exit == B_TRUE)
+		return;
 
+	/*
+	 * Make reads of volatile properties return PICL_PROPUNAVAILABLE
+	 * since we're about to recycle the plug-in.  No need to worry
+	 * about removing /physical-platform since tree_builder() will
+	 * take care of recycling it for us.
+	 */
+	(void) rw_wrlock(&stale_tree_rwlp);
+	stale_tree = B_TRUE;
+	if (vol_props) {
+		free(vol_props);
+	}
+	vol_props = NULL;
+	volprop_ndx = 0;
+	n_vol_props = 0;
+	(void) rw_unlock(&stale_tree_rwlp);
+
+	/* wake up the tree_builder thread, tell it to exit */
+	(void) mutex_lock(&rebuild_tree_lock);
+	rebuild_tree = B_TRUE;
+	tree_builder_thr_exit = B_TRUE;
+	cond_signal(&rebuild_tree_cv);
+	(void) mutex_unlock(&rebuild_tree_lock);
+
+	/* reap the thread */
+	(void) thr_join(tree_builder_thr_id, NULL, NULL);
+
+	/* close the channel */
+	if (hdl != NULL) {
+		snmp_fini(hdl);
+		hdl = NULL;
+	}
+
+	/* finish cleanup... */
 	(void) rwlock_destroy(&stale_tree_rwlp);
 	(void) cond_destroy(&rebuild_tree_cv);
 	(void) mutex_destroy(&rebuild_tree_lock);
@@ -330,11 +373,18 @@
 
 		LOGPRINTF("tree_builder: woke up\n");
 
+		if (tree_builder_thr_exit == B_TRUE) {
+			(void) mutex_unlock(&rebuild_tree_lock);
+			LOGPRINTF("tree_builder: time to exit\n");
+			return (NULL);
+		}
+
 		old_physplat_root = NULL;
 		physplat_root = NULL;
 
 		LOGPRINTF("tree_builder: getting root node\n");
 		if ((ret = ptree_get_root(&root_node)) != PICL_SUCCESS) {
+			(void) mutex_unlock(&rebuild_tree_lock);
 			log_msg(LOG_ERR, SNMPP_NO_ROOT, ret);
 			return ((void *)-2);
 		}
@@ -346,8 +396,10 @@
 
 		LOGPRINTF("tree_builder: building physical-platform\n");
 		if ((ret = build_physplat(&physplat_root)) < 0) {
+			(void) mutex_unlock(&rebuild_tree_lock);
 			log_msg(LOG_ERR, SNMPP_CANT_CREATE_PHYSPLAT, ret);
 			snmp_fini(hdl);
+			hdl = NULL;
 			return ((void *)-3);
 		}
 
@@ -359,9 +411,11 @@
 
 		LOGPRINTF("tree_builder: attaching new subtree\n");
 		if ((ret = ptree_add_node(root_node, physplat_root)) < 0) {
+			(void) mutex_unlock(&rebuild_tree_lock);
 			free_resources(physplat_root);
 			log_msg(LOG_ERR, SNMPP_CANT_CREATE_PHYSPLAT, ret);
 			snmp_fini(hdl);
+			hdl = NULL;
 			return ((void *)-4);
 		}