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();