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


   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */



  24 
  25 /*
  26  * Layered driver support.
  27  */
  28 
  29 #include <sys/atomic.h>
  30 #include <sys/types.h>
  31 #include <sys/t_lock.h>
  32 #include <sys/param.h>
  33 #include <sys/conf.h>
  34 #include <sys/systm.h>
  35 #include <sys/sysmacros.h>
  36 #include <sys/buf.h>
  37 #include <sys/cred.h>
  38 #include <sys/uio.h>
  39 #include <sys/vnode.h>
  40 #include <sys/fs/snode.h>
  41 #include <sys/open.h>
  42 #include <sys/kmem.h>
  43 #include <sys/file.h>


 110 #define NDI_EVENT_SERVICE       "NDI_EVENT_SERVICE"
 111 
 112 static void ldi_ev_lock(void);
 113 static void ldi_ev_unlock(void);
 114 
 115 #ifdef  LDI_OBSOLETE_EVENT
 116 int ldi_remove_event_handler(ldi_handle_t lh, ldi_callback_id_t id);
 117 #endif
 118 
 119 
 120 /*
 121  * globals
 122  */
 123 static kmutex_t                 ldi_ident_hash_lock[LI_HASH_SZ];
 124 static struct ldi_ident         *ldi_ident_hash[LI_HASH_SZ];
 125 
 126 static kmutex_t                 ldi_handle_hash_lock[LH_HASH_SZ];
 127 static struct ldi_handle        *ldi_handle_hash[LH_HASH_SZ];
 128 static size_t                   ldi_handle_hash_count;
 129 




 130 static struct ldi_ev_callback_list ldi_ev_callback_list;
 131 
 132 static uint32_t ldi_ev_id_pool = 0;
 133 
 134 struct ldi_ev_cookie {
 135         char *ck_evname;
 136         uint_t ck_sync;
 137         uint_t ck_ctype;
 138 };
 139 
 140 static struct ldi_ev_cookie ldi_ev_cookies[] = {
 141         { LDI_EV_OFFLINE, 1, CT_DEV_EV_OFFLINE},
 142         { LDI_EV_DEGRADE, 0, CT_DEV_EV_DEGRADED},
 143         { LDI_EV_DEVICE_REMOVE, 0, 0},
 144         { NULL}                 /* must terminate list */
 145 };
 146 
 147 void
 148 ldi_init(void)
 149 {
 150         int i;
 151 
 152         ldi_handle_hash_count = 0;
 153         for (i = 0; i < LH_HASH_SZ; i++) {
 154                 mutex_init(&ldi_handle_hash_lock[i], NULL, MUTEX_DEFAULT, NULL);
 155                 ldi_handle_hash[i] = NULL;
 156         }
 157         for (i = 0; i < LI_HASH_SZ; i++) {
 158                 mutex_init(&ldi_ident_hash_lock[i], NULL, MUTEX_DEFAULT, NULL);
 159                 ldi_ident_hash[i] = NULL;
 160         }
 161 
 162         /*
 163          * Initialize the LDI event subsystem
 164          */
 165         mutex_init(&ldi_ev_callback_list.le_lock, NULL, MUTEX_DEFAULT, NULL);
 166         cv_init(&ldi_ev_callback_list.le_cv, NULL, CV_DEFAULT, NULL);
 167         ldi_ev_callback_list.le_busy = 0;
 168         ldi_ev_callback_list.le_thread = NULL;


 169         list_create(&ldi_ev_callback_list.le_head,
 170             sizeof (ldi_ev_callback_impl_t),
 171             offsetof(ldi_ev_callback_impl_t, lec_list));
 172 }
 173 
 174 /*
 175  * LDI ident manipulation functions
 176  */
 177 static uint_t
 178 ident_hash_func(modid_t modid, dev_info_t *dip, dev_t dev)
 179 {
 180         if (dip != NULL) {
 181                 uintptr_t k = (uintptr_t)dip;
 182                 k >>= (int)highbit(sizeof (struct dev_info));
 183                 return ((uint_t)k);
 184         } else if (dev != DDI_DEV_T_NONE) {
 185                 return (modid + getminor(dev) + getmajor(dev));
 186         } else {
 187                 return (modid);
 188         }


3312 {
3313         ldi_ev_callback_impl_t *lecp;
3314         list_t  *listp;
3315         int     ret;
3316         char    *lec_event;
3317 
3318         ASSERT(dip);
3319         ASSERT(dev != DDI_DEV_T_NONE);
3320         ASSERT(dev != NODEV);
3321         ASSERT((dev == DDI_DEV_T_ANY && spec_type == 0) ||
3322             (spec_type == S_IFCHR || spec_type == S_IFBLK));
3323         ASSERT(event);
3324         ASSERT(ldi_native_event(event));
3325         ASSERT(ldi_ev_sync_event(event));
3326 
3327         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): entered: dip=%p, ev=%s",
3328             (void *)dip, event));
3329 
3330         ret = LDI_EV_NONE;
3331         ldi_ev_lock();


3332         listp = &ldi_ev_callback_list.le_head;
3333         for (lecp = list_head(listp); lecp; lecp = list_next(listp, lecp)) {


3334 
3335                 /* Check if matching device */
3336                 if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
3337                         continue;
3338 
3339                 if (lecp->lec_lhp == NULL) {
3340                         /*
3341                          * Consumer has unregistered the handle and so
3342                          * is no longer interested in notify events.
3343                          */
3344                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): No LDI "
3345                             "handle, skipping"));
3346                         continue;
3347                 }
3348 
3349                 if (lecp->lec_notify == NULL) {
3350                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): No notify "
3351                             "callback. skipping"));
3352                         continue;       /* not interested in notify */
3353                 }


3369                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): notify"
3370                             " FAILURE"));
3371                         break;
3372                 }
3373 
3374                 /* We have a matching callback that allows the event to occur */
3375                 ret = LDI_EV_SUCCESS;
3376 
3377                 LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): 1 consumer success"));
3378         }
3379 
3380         if (ret != LDI_EV_FAILURE)
3381                 goto out;
3382 
3383         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): undoing notify"));
3384 
3385         /*
3386          * Undo notifies already sent
3387          */
3388         lecp = list_prev(listp, lecp);
3389         for (; lecp; lecp = list_prev(listp, lecp)) {


3390 
3391                 /*
3392                  * Check if matching device
3393                  */
3394                 if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
3395                         continue;
3396 
3397 
3398                 if (lecp->lec_finalize == NULL) {
3399                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): no finalize, "
3400                             "skipping"));
3401                         continue;       /* not interested in finalize */
3402                 }
3403 
3404                 /*
3405                  * it is possible that in response to a notify event a
3406                  * layered driver closed its LDI handle so it is ok
3407                  * to have a NULL LDI handle for finalize. The layered
3408                  * driver is expected to maintain state in its "arg"
3409                  * parameter to keep track of the closed device.


3420                 LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): calling finalize"));
3421 
3422                 lecp->lec_finalize(lecp->lec_lhp, lecp->lec_cookie,
3423                     LDI_EV_FAILURE, lecp->lec_arg, ev_data);
3424 
3425                 /*
3426                  * If LDI native event and LDI handle closed in context
3427                  * of notify, NULL out the finalize callback as we have
3428                  * already called the 1 finalize above allowed in this situation
3429                  */
3430                 if (lecp->lec_lhp == NULL &&
3431                     ldi_native_cookie(lecp->lec_cookie)) {
3432                         LDI_EVDBG((CE_NOTE,
3433                             "ldi_invoke_notify(): NULL-ing finalize after "
3434                             "calling 1 finalize following ldi_close"));
3435                         lecp->lec_finalize = NULL;
3436                 }
3437         }
3438 
3439 out:


3440         ldi_ev_unlock();
3441 
3442         if (ret == LDI_EV_NONE) {
3443                 LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): no matching "
3444                     "LDI callbacks"));
3445         }
3446 
3447         return (ret);
3448 }
3449 
3450 /*
3451  * Framework function to be called from a layered driver to propagate
3452  * LDI "notify" events to exported minors.
3453  *
3454  * This function is a public interface exported by the LDI framework
3455  * for use by layered drivers to propagate device events up the software
3456  * stack.
3457  */
3458 int
3459 ldi_ev_notify(dev_info_t *dip, minor_t minor, int spec_type,


3535     int ldi_result, void *ev_data)
3536 {
3537         ldi_ev_callback_impl_t *lecp;
3538         list_t  *listp;
3539         char    *lec_event;
3540         int     found = 0;
3541 
3542         ASSERT(dip);
3543         ASSERT(dev != DDI_DEV_T_NONE);
3544         ASSERT(dev != NODEV);
3545         ASSERT((dev == DDI_DEV_T_ANY && spec_type == 0) ||
3546             (spec_type == S_IFCHR || spec_type == S_IFBLK));
3547         ASSERT(event);
3548         ASSERT(ldi_native_event(event));
3549         ASSERT(ldi_result == LDI_EV_SUCCESS || ldi_result == LDI_EV_FAILURE);
3550 
3551         LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): entered: dip=%p, result=%d"
3552             " event=%s", (void *)dip, ldi_result, event));
3553 
3554         ldi_ev_lock();

3555         listp = &ldi_ev_callback_list.le_head;
3556         for (lecp = list_head(listp); lecp; lecp = list_next(listp, lecp)) {


3557 
3558                 if (lecp->lec_finalize == NULL) {
3559                         LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): No "
3560                             "finalize. Skipping"));
3561                         continue;       /* Not interested in finalize */
3562                 }
3563 
3564                 /*
3565                  * Check if matching device
3566                  */
3567                 if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
3568                         continue;
3569 
3570                 /*
3571                  * It is valid for the LDI handle to be NULL during finalize.
3572                  * The layered driver may have done an LDI close in the notify
3573                  * callback.
3574                  */
3575 
3576                 /*


3587                 LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): calling finalize"));
3588 
3589                 found = 1;
3590 
3591                 lecp->lec_finalize(lecp->lec_lhp, lecp->lec_cookie,
3592                     ldi_result, lecp->lec_arg, ev_data);
3593 
3594                 /*
3595                  * If LDI native event and LDI handle closed in context
3596                  * of notify, NULL out the finalize callback as we have
3597                  * already called the 1 finalize above allowed in this situation
3598                  */
3599                 if (lecp->lec_lhp == NULL &&
3600                     ldi_native_cookie(lecp->lec_cookie)) {
3601                         LDI_EVDBG((CE_NOTE,
3602                             "ldi_invoke_finalize(): NULLing finalize after "
3603                             "calling 1 finalize following ldi_close"));
3604                         lecp->lec_finalize = NULL;
3605                 }
3606         }

3607         ldi_ev_unlock();
3608 
3609         if (found)
3610                 return;
3611 
3612         LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): no matching callbacks"));
3613 }
3614 
3615 /*
3616  * Framework function to be called from a layered driver to propagate
3617  * LDI "finalize" events to exported minors.
3618  *
3619  * This function is a public interface exported by the LDI framework
3620  * for use by layered drivers to propagate device events up the software
3621  * stack.
3622  */
3623 void
3624 ldi_ev_finalize(dev_info_t *dip, minor_t minor, int spec_type, int ldi_result,
3625     ldi_ev_cookie_t cookie, void *ev_data)
3626 {


3667         ldi_ev_callback_impl_t  *found;
3668         list_t                  *listp;
3669 
3670         ASSERT(!servicing_interrupt());
3671 
3672         if (id == 0) {
3673                 cmn_err(CE_WARN, "ldi_ev_remove_callbacks: Invalid ID 0");
3674                 return (LDI_EV_FAILURE);
3675         }
3676 
3677         LDI_EVDBG((CE_NOTE, "ldi_ev_remove_callbacks: entered: id=%p",
3678             (void *)id));
3679 
3680         ldi_ev_lock();
3681 
3682         listp = &ldi_ev_callback_list.le_head;
3683         next = found = NULL;
3684         for (lecp = list_head(listp); lecp; lecp = next) {
3685                 next = list_next(listp, lecp);
3686                 if (lecp->lec_id == id) {
3687                         ASSERT(found == NULL);
















3688                         list_remove(listp, lecp);
3689                         found = lecp;
3690                 }
3691         }
3692         ldi_ev_unlock();
3693 
3694         if (found == NULL) {
3695                 cmn_err(CE_WARN, "No LDI event handler for id (%p)",
3696                     (void *)id);
3697                 return (LDI_EV_SUCCESS);
3698         }
3699 
3700         if (!ldi_native_cookie(found->lec_cookie)) {
3701                 ASSERT(found->lec_notify == NULL);
3702                 if (ddi_remove_event_handler((ddi_callback_id_t)id)
3703                     != DDI_SUCCESS) {
3704                         cmn_err(CE_WARN, "failed to remove NDI event handler "
3705                             "for id (%p)", (void *)id);
3706                         ldi_ev_lock();
3707                         list_insert_tail(listp, found);


   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 /*
  25  * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
  26  */
  27 
  28 /*
  29  * Layered driver support.
  30  */
  31 
  32 #include <sys/atomic.h>
  33 #include <sys/types.h>
  34 #include <sys/t_lock.h>
  35 #include <sys/param.h>
  36 #include <sys/conf.h>
  37 #include <sys/systm.h>
  38 #include <sys/sysmacros.h>
  39 #include <sys/buf.h>
  40 #include <sys/cred.h>
  41 #include <sys/uio.h>
  42 #include <sys/vnode.h>
  43 #include <sys/fs/snode.h>
  44 #include <sys/open.h>
  45 #include <sys/kmem.h>
  46 #include <sys/file.h>


 113 #define NDI_EVENT_SERVICE       "NDI_EVENT_SERVICE"
 114 
 115 static void ldi_ev_lock(void);
 116 static void ldi_ev_unlock(void);
 117 
 118 #ifdef  LDI_OBSOLETE_EVENT
 119 int ldi_remove_event_handler(ldi_handle_t lh, ldi_callback_id_t id);
 120 #endif
 121 
 122 
 123 /*
 124  * globals
 125  */
 126 static kmutex_t                 ldi_ident_hash_lock[LI_HASH_SZ];
 127 static struct ldi_ident         *ldi_ident_hash[LI_HASH_SZ];
 128 
 129 static kmutex_t                 ldi_handle_hash_lock[LH_HASH_SZ];
 130 static struct ldi_handle        *ldi_handle_hash[LH_HASH_SZ];
 131 static size_t                   ldi_handle_hash_count;
 132 
 133 /*
 134  * Use of "ldi_ev_callback_list" must be protected by ldi_ev_lock()
 135  * and ldi_ev_unlock().
 136  */
 137 static struct ldi_ev_callback_list ldi_ev_callback_list;
 138 
 139 static uint32_t ldi_ev_id_pool = 0;
 140 
 141 struct ldi_ev_cookie {
 142         char *ck_evname;
 143         uint_t ck_sync;
 144         uint_t ck_ctype;
 145 };
 146 
 147 static struct ldi_ev_cookie ldi_ev_cookies[] = {
 148         { LDI_EV_OFFLINE, 1, CT_DEV_EV_OFFLINE},
 149         { LDI_EV_DEGRADE, 0, CT_DEV_EV_DEGRADED},
 150         { LDI_EV_DEVICE_REMOVE, 0, 0},
 151         { NULL}                 /* must terminate list */
 152 };
 153 
 154 void
 155 ldi_init(void)
 156 {
 157         int i;
 158 
 159         ldi_handle_hash_count = 0;
 160         for (i = 0; i < LH_HASH_SZ; i++) {
 161                 mutex_init(&ldi_handle_hash_lock[i], NULL, MUTEX_DEFAULT, NULL);
 162                 ldi_handle_hash[i] = NULL;
 163         }
 164         for (i = 0; i < LI_HASH_SZ; i++) {
 165                 mutex_init(&ldi_ident_hash_lock[i], NULL, MUTEX_DEFAULT, NULL);
 166                 ldi_ident_hash[i] = NULL;
 167         }
 168 
 169         /*
 170          * Initialize the LDI event subsystem
 171          */
 172         mutex_init(&ldi_ev_callback_list.le_lock, NULL, MUTEX_DEFAULT, NULL);
 173         cv_init(&ldi_ev_callback_list.le_cv, NULL, CV_DEFAULT, NULL);
 174         ldi_ev_callback_list.le_busy = 0;
 175         ldi_ev_callback_list.le_thread = NULL;
 176         ldi_ev_callback_list.le_walker_next = NULL;
 177         ldi_ev_callback_list.le_walker_prev = NULL;
 178         list_create(&ldi_ev_callback_list.le_head,
 179             sizeof (ldi_ev_callback_impl_t),
 180             offsetof(ldi_ev_callback_impl_t, lec_list));
 181 }
 182 
 183 /*
 184  * LDI ident manipulation functions
 185  */
 186 static uint_t
 187 ident_hash_func(modid_t modid, dev_info_t *dip, dev_t dev)
 188 {
 189         if (dip != NULL) {
 190                 uintptr_t k = (uintptr_t)dip;
 191                 k >>= (int)highbit(sizeof (struct dev_info));
 192                 return ((uint_t)k);
 193         } else if (dev != DDI_DEV_T_NONE) {
 194                 return (modid + getminor(dev) + getmajor(dev));
 195         } else {
 196                 return (modid);
 197         }


3321 {
3322         ldi_ev_callback_impl_t *lecp;
3323         list_t  *listp;
3324         int     ret;
3325         char    *lec_event;
3326 
3327         ASSERT(dip);
3328         ASSERT(dev != DDI_DEV_T_NONE);
3329         ASSERT(dev != NODEV);
3330         ASSERT((dev == DDI_DEV_T_ANY && spec_type == 0) ||
3331             (spec_type == S_IFCHR || spec_type == S_IFBLK));
3332         ASSERT(event);
3333         ASSERT(ldi_native_event(event));
3334         ASSERT(ldi_ev_sync_event(event));
3335 
3336         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): entered: dip=%p, ev=%s",
3337             (void *)dip, event));
3338 
3339         ret = LDI_EV_NONE;
3340         ldi_ev_lock();
3341 
3342         VERIFY(ldi_ev_callback_list.le_walker_next == NULL);
3343         listp = &ldi_ev_callback_list.le_head;
3344         for (lecp = list_head(listp); lecp; lecp =
3345             ldi_ev_callback_list.le_walker_next) {
3346                 ldi_ev_callback_list.le_walker_next = list_next(listp, lecp);
3347 
3348                 /* Check if matching device */
3349                 if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
3350                         continue;
3351 
3352                 if (lecp->lec_lhp == NULL) {
3353                         /*
3354                          * Consumer has unregistered the handle and so
3355                          * is no longer interested in notify events.
3356                          */
3357                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): No LDI "
3358                             "handle, skipping"));
3359                         continue;
3360                 }
3361 
3362                 if (lecp->lec_notify == NULL) {
3363                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): No notify "
3364                             "callback. skipping"));
3365                         continue;       /* not interested in notify */
3366                 }


3382                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): notify"
3383                             " FAILURE"));
3384                         break;
3385                 }
3386 
3387                 /* We have a matching callback that allows the event to occur */
3388                 ret = LDI_EV_SUCCESS;
3389 
3390                 LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): 1 consumer success"));
3391         }
3392 
3393         if (ret != LDI_EV_FAILURE)
3394                 goto out;
3395 
3396         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): undoing notify"));
3397 
3398         /*
3399          * Undo notifies already sent
3400          */
3401         lecp = list_prev(listp, lecp);
3402         VERIFY(ldi_ev_callback_list.le_walker_prev == NULL);
3403         for (; lecp; lecp = ldi_ev_callback_list.le_walker_prev) {
3404                 ldi_ev_callback_list.le_walker_prev = list_prev(listp, lecp);
3405 
3406                 /*
3407                  * Check if matching device
3408                  */
3409                 if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
3410                         continue;
3411 
3412 
3413                 if (lecp->lec_finalize == NULL) {
3414                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): no finalize, "
3415                             "skipping"));
3416                         continue;       /* not interested in finalize */
3417                 }
3418 
3419                 /*
3420                  * it is possible that in response to a notify event a
3421                  * layered driver closed its LDI handle so it is ok
3422                  * to have a NULL LDI handle for finalize. The layered
3423                  * driver is expected to maintain state in its "arg"
3424                  * parameter to keep track of the closed device.


3435                 LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): calling finalize"));
3436 
3437                 lecp->lec_finalize(lecp->lec_lhp, lecp->lec_cookie,
3438                     LDI_EV_FAILURE, lecp->lec_arg, ev_data);
3439 
3440                 /*
3441                  * If LDI native event and LDI handle closed in context
3442                  * of notify, NULL out the finalize callback as we have
3443                  * already called the 1 finalize above allowed in this situation
3444                  */
3445                 if (lecp->lec_lhp == NULL &&
3446                     ldi_native_cookie(lecp->lec_cookie)) {
3447                         LDI_EVDBG((CE_NOTE,
3448                             "ldi_invoke_notify(): NULL-ing finalize after "
3449                             "calling 1 finalize following ldi_close"));
3450                         lecp->lec_finalize = NULL;
3451                 }
3452         }
3453 
3454 out:
3455         ldi_ev_callback_list.le_walker_next = NULL;
3456         ldi_ev_callback_list.le_walker_prev = NULL;
3457         ldi_ev_unlock();
3458 
3459         if (ret == LDI_EV_NONE) {
3460                 LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): no matching "
3461                     "LDI callbacks"));
3462         }
3463 
3464         return (ret);
3465 }
3466 
3467 /*
3468  * Framework function to be called from a layered driver to propagate
3469  * LDI "notify" events to exported minors.
3470  *
3471  * This function is a public interface exported by the LDI framework
3472  * for use by layered drivers to propagate device events up the software
3473  * stack.
3474  */
3475 int
3476 ldi_ev_notify(dev_info_t *dip, minor_t minor, int spec_type,


3552     int ldi_result, void *ev_data)
3553 {
3554         ldi_ev_callback_impl_t *lecp;
3555         list_t  *listp;
3556         char    *lec_event;
3557         int     found = 0;
3558 
3559         ASSERT(dip);
3560         ASSERT(dev != DDI_DEV_T_NONE);
3561         ASSERT(dev != NODEV);
3562         ASSERT((dev == DDI_DEV_T_ANY && spec_type == 0) ||
3563             (spec_type == S_IFCHR || spec_type == S_IFBLK));
3564         ASSERT(event);
3565         ASSERT(ldi_native_event(event));
3566         ASSERT(ldi_result == LDI_EV_SUCCESS || ldi_result == LDI_EV_FAILURE);
3567 
3568         LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): entered: dip=%p, result=%d"
3569             " event=%s", (void *)dip, ldi_result, event));
3570 
3571         ldi_ev_lock();
3572         VERIFY(ldi_ev_callback_list.le_walker_next == NULL);
3573         listp = &ldi_ev_callback_list.le_head;
3574         for (lecp = list_head(listp); lecp; lecp =
3575             ldi_ev_callback_list.le_walker_next) {
3576                 ldi_ev_callback_list.le_walker_next = list_next(listp, lecp);
3577 
3578                 if (lecp->lec_finalize == NULL) {
3579                         LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): No "
3580                             "finalize. Skipping"));
3581                         continue;       /* Not interested in finalize */
3582                 }
3583 
3584                 /*
3585                  * Check if matching device
3586                  */
3587                 if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
3588                         continue;
3589 
3590                 /*
3591                  * It is valid for the LDI handle to be NULL during finalize.
3592                  * The layered driver may have done an LDI close in the notify
3593                  * callback.
3594                  */
3595 
3596                 /*


3607                 LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): calling finalize"));
3608 
3609                 found = 1;
3610 
3611                 lecp->lec_finalize(lecp->lec_lhp, lecp->lec_cookie,
3612                     ldi_result, lecp->lec_arg, ev_data);
3613 
3614                 /*
3615                  * If LDI native event and LDI handle closed in context
3616                  * of notify, NULL out the finalize callback as we have
3617                  * already called the 1 finalize above allowed in this situation
3618                  */
3619                 if (lecp->lec_lhp == NULL &&
3620                     ldi_native_cookie(lecp->lec_cookie)) {
3621                         LDI_EVDBG((CE_NOTE,
3622                             "ldi_invoke_finalize(): NULLing finalize after "
3623                             "calling 1 finalize following ldi_close"));
3624                         lecp->lec_finalize = NULL;
3625                 }
3626         }
3627         ldi_ev_callback_list.le_walker_next = NULL;
3628         ldi_ev_unlock();
3629 
3630         if (found)
3631                 return;
3632 
3633         LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): no matching callbacks"));
3634 }
3635 
3636 /*
3637  * Framework function to be called from a layered driver to propagate
3638  * LDI "finalize" events to exported minors.
3639  *
3640  * This function is a public interface exported by the LDI framework
3641  * for use by layered drivers to propagate device events up the software
3642  * stack.
3643  */
3644 void
3645 ldi_ev_finalize(dev_info_t *dip, minor_t minor, int spec_type, int ldi_result,
3646     ldi_ev_cookie_t cookie, void *ev_data)
3647 {


3688         ldi_ev_callback_impl_t  *found;
3689         list_t                  *listp;
3690 
3691         ASSERT(!servicing_interrupt());
3692 
3693         if (id == 0) {
3694                 cmn_err(CE_WARN, "ldi_ev_remove_callbacks: Invalid ID 0");
3695                 return (LDI_EV_FAILURE);
3696         }
3697 
3698         LDI_EVDBG((CE_NOTE, "ldi_ev_remove_callbacks: entered: id=%p",
3699             (void *)id));
3700 
3701         ldi_ev_lock();
3702 
3703         listp = &ldi_ev_callback_list.le_head;
3704         next = found = NULL;
3705         for (lecp = list_head(listp); lecp; lecp = next) {
3706                 next = list_next(listp, lecp);
3707                 if (lecp->lec_id == id) {
3708                         VERIFY(found == NULL);
3709 
3710                         /*
3711                          * If there is a walk in progress, shift that walk
3712                          * along to the next element so that we can remove
3713                          * this one.  This allows us to unregister an arbitrary
3714                          * number of callbacks from within a callback.
3715                          *
3716                          * See the struct definition (in sunldi_impl.h) for
3717                          * more information.
3718                          */
3719                         if (ldi_ev_callback_list.le_walker_next == lecp)
3720                                 ldi_ev_callback_list.le_walker_next = next;
3721                         if (ldi_ev_callback_list.le_walker_prev == lecp)
3722                                 ldi_ev_callback_list.le_walker_prev = list_prev(
3723                                     listp, ldi_ev_callback_list.le_walker_prev);
3724 
3725                         list_remove(listp, lecp);
3726                         found = lecp;
3727                 }
3728         }
3729         ldi_ev_unlock();
3730 
3731         if (found == NULL) {
3732                 cmn_err(CE_WARN, "No LDI event handler for id (%p)",
3733                     (void *)id);
3734                 return (LDI_EV_SUCCESS);
3735         }
3736 
3737         if (!ldi_native_cookie(found->lec_cookie)) {
3738                 ASSERT(found->lec_notify == NULL);
3739                 if (ddi_remove_event_handler((ddi_callback_id_t)id)
3740                     != DDI_SUCCESS) {
3741                         cmn_err(CE_WARN, "failed to remove NDI event handler "
3742                             "for id (%p)", (void *)id);
3743                         ldi_ev_lock();
3744                         list_insert_tail(listp, found);