Print this page
OS-1988 Make ldi_ev_remove_callbacks safe to use in LDI callbacks

@@ -20,16 +20,17 @@
  */
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
+/*
+ * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
+ */
 
 #ifndef _SYS_SUNLDI_IMPL_H
 #define _SYS_SUNLDI_IMPL_H
 
-#pragma ident   "%Z%%M% %I%     %E% SMI"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 #include <sys/dditypes.h>

@@ -139,16 +140,33 @@
         void                    *lec_cookie;
         void                    *lec_id;
         list_node_t             lec_list;
 } ldi_ev_callback_impl_t;
 
+/*
+ * Members of "struct ldi_ev_callback_list" are protected by their le_lock
+ * member.  The struct is currently only used once, as a file-level global,
+ * and the locking protocol is currently implemented in ldi_ev_lock() and
+ * ldi_ev_unlock().
+ *
+ * When delivering events to subscribers, ldi_invoke_notify() and
+ * ldi_invoke_finalize() will walk the list of callbacks: le_head.  It is
+ * possible that an invoked callback function will need to unregister an
+ * arbitrary number of callbacks from this list.
+ *
+ * To enable ldi_ev_remove_callbacks() to remove elements from the list
+ * without breaking the walk-in-progress, we store the next element in the
+ * walk direction on the struct as le_walker_next and le_walker_prev.
+ */
 struct ldi_ev_callback_list {
         kmutex_t        le_lock;
         kcondvar_t      le_cv;
         int             le_busy;
         void            *le_thread;
         list_t          le_head;
+        ldi_ev_callback_impl_t  *le_walker_next;
+        ldi_ev_callback_impl_t  *le_walker_prev;
 };
 
 int ldi_invoke_notify(dev_info_t *dip, dev_t dev, int spec_type, char *event,
     void *ev_data);
 void ldi_invoke_finalize(dev_info_t *dip, dev_t dev, int spec_type, char *event,