Print this page
OS-1988 Make ldi_ev_remove_callbacks safe to use in LDI callbacks
*** 19,28 ****
--- 19,31 ----
* CDDL HEADER END
*/
/*
* Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
*/
+ /*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
/*
* Layered driver support.
*/
*** 125,134 ****
--- 128,141 ----
static kmutex_t ldi_handle_hash_lock[LH_HASH_SZ];
static struct ldi_handle *ldi_handle_hash[LH_HASH_SZ];
static size_t ldi_handle_hash_count;
+ /*
+ * Use of "ldi_ev_callback_list" must be protected by ldi_ev_lock()
+ * and ldi_ev_unlock().
+ */
static struct ldi_ev_callback_list ldi_ev_callback_list;
static uint32_t ldi_ev_id_pool = 0;
struct ldi_ev_cookie {
*** 164,173 ****
--- 171,182 ----
*/
mutex_init(&ldi_ev_callback_list.le_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&ldi_ev_callback_list.le_cv, NULL, CV_DEFAULT, NULL);
ldi_ev_callback_list.le_busy = 0;
ldi_ev_callback_list.le_thread = NULL;
+ ldi_ev_callback_list.le_walker_next = NULL;
+ ldi_ev_callback_list.le_walker_prev = NULL;
list_create(&ldi_ev_callback_list.le_head,
sizeof (ldi_ev_callback_impl_t),
offsetof(ldi_ev_callback_impl_t, lec_list));
}
*** 3327,3338 ****
LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): entered: dip=%p, ev=%s",
(void *)dip, event));
ret = LDI_EV_NONE;
ldi_ev_lock();
listp = &ldi_ev_callback_list.le_head;
! for (lecp = list_head(listp); lecp; lecp = list_next(listp, lecp)) {
/* Check if matching device */
if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
continue;
--- 3336,3351 ----
LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): entered: dip=%p, ev=%s",
(void *)dip, event));
ret = LDI_EV_NONE;
ldi_ev_lock();
+
+ VERIFY(ldi_ev_callback_list.le_walker_next == NULL);
listp = &ldi_ev_callback_list.le_head;
! for (lecp = list_head(listp); lecp; lecp =
! ldi_ev_callback_list.le_walker_next) {
! ldi_ev_callback_list.le_walker_next = list_next(listp, lecp);
/* Check if matching device */
if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
continue;
*** 3384,3394 ****
/*
* Undo notifies already sent
*/
lecp = list_prev(listp, lecp);
! for (; lecp; lecp = list_prev(listp, lecp)) {
/*
* Check if matching device
*/
if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
--- 3397,3409 ----
/*
* Undo notifies already sent
*/
lecp = list_prev(listp, lecp);
! VERIFY(ldi_ev_callback_list.le_walker_prev == NULL);
! for (; lecp; lecp = ldi_ev_callback_list.le_walker_prev) {
! ldi_ev_callback_list.le_walker_prev = list_prev(listp, lecp);
/*
* Check if matching device
*/
if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
*** 3435,3444 ****
--- 3450,3461 ----
lecp->lec_finalize = NULL;
}
}
out:
+ ldi_ev_callback_list.le_walker_next = NULL;
+ ldi_ev_callback_list.le_walker_prev = NULL;
ldi_ev_unlock();
if (ret == LDI_EV_NONE) {
LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): no matching "
"LDI callbacks"));
*** 3550,3561 ****
LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): entered: dip=%p, result=%d"
" event=%s", (void *)dip, ldi_result, event));
ldi_ev_lock();
listp = &ldi_ev_callback_list.le_head;
! for (lecp = list_head(listp); lecp; lecp = list_next(listp, lecp)) {
if (lecp->lec_finalize == NULL) {
LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): No "
"finalize. Skipping"));
continue; /* Not interested in finalize */
--- 3567,3581 ----
LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): entered: dip=%p, result=%d"
" event=%s", (void *)dip, ldi_result, event));
ldi_ev_lock();
+ VERIFY(ldi_ev_callback_list.le_walker_next == NULL);
listp = &ldi_ev_callback_list.le_head;
! for (lecp = list_head(listp); lecp; lecp =
! ldi_ev_callback_list.le_walker_next) {
! ldi_ev_callback_list.le_walker_next = list_next(listp, lecp);
if (lecp->lec_finalize == NULL) {
LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): No "
"finalize. Skipping"));
continue; /* Not interested in finalize */
*** 3602,3611 ****
--- 3622,3632 ----
"ldi_invoke_finalize(): NULLing finalize after "
"calling 1 finalize following ldi_close"));
lecp->lec_finalize = NULL;
}
}
+ ldi_ev_callback_list.le_walker_next = NULL;
ldi_ev_unlock();
if (found)
return;
*** 3682,3692 ****
listp = &ldi_ev_callback_list.le_head;
next = found = NULL;
for (lecp = list_head(listp); lecp; lecp = next) {
next = list_next(listp, lecp);
if (lecp->lec_id == id) {
! ASSERT(found == NULL);
list_remove(listp, lecp);
found = lecp;
}
}
ldi_ev_unlock();
--- 3703,3729 ----
listp = &ldi_ev_callback_list.le_head;
next = found = NULL;
for (lecp = list_head(listp); lecp; lecp = next) {
next = list_next(listp, lecp);
if (lecp->lec_id == id) {
! VERIFY(found == NULL);
!
! /*
! * If there is a walk in progress, shift that walk
! * along to the next element so that we can remove
! * this one. This allows us to unregister an arbitrary
! * number of callbacks from within a callback.
! *
! * See the struct definition (in sunldi_impl.h) for
! * more information.
! */
! if (ldi_ev_callback_list.le_walker_next == lecp)
! ldi_ev_callback_list.le_walker_next = next;
! if (ldi_ev_callback_list.le_walker_prev == lecp)
! ldi_ev_callback_list.le_walker_prev = list_prev(
! listp, ldi_ev_callback_list.le_walker_prev);
!
list_remove(listp, lecp);
found = lecp;
}
}
ldi_ev_unlock();