Print this page
LOCAL: LDI -- make walks removal-safe


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


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 {


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


 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 static ldi_ev_callback_impl_t *walker_next = NULL;
 148 static ldi_ev_callback_impl_t *walker_prev = NULL;
 149 
 150 void
 151 ldi_init(void)
 152 {
 153         int i;
 154 
 155         ldi_handle_hash_count = 0;
 156         for (i = 0; i < LH_HASH_SZ; i++) {
 157                 mutex_init(&ldi_handle_hash_lock[i], NULL, MUTEX_DEFAULT, NULL);
 158                 ldi_handle_hash[i] = NULL;
 159         }
 160         for (i = 0; i < LI_HASH_SZ; i++) {
 161                 mutex_init(&ldi_ident_hash_lock[i], NULL, MUTEX_DEFAULT, NULL);
 162                 ldi_ident_hash[i] = NULL;
 163         }
 164 
 165         /*
 166          * Initialize the LDI event subsystem
 167          */
 168         mutex_init(&ldi_ev_callback_list.le_lock, NULL, MUTEX_DEFAULT, NULL);
 169         cv_init(&ldi_ev_callback_list.le_cv, NULL, CV_DEFAULT, NULL);


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


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


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


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


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


3680         list_t                  *listp;
3681 
3682         ASSERT(!servicing_interrupt());
3683 
3684         if (id == 0) {
3685                 cmn_err(CE_WARN, "ldi_ev_remove_callbacks: Invalid ID 0");
3686                 return (LDI_EV_FAILURE);
3687         }
3688 
3689         LDI_EVDBG((CE_NOTE, "ldi_ev_remove_callbacks: entered: id=%p",
3690             (void *)id));
3691 
3692         ldi_ev_lock();
3693 
3694         listp = &ldi_ev_callback_list.le_head;
3695         next = found = NULL;
3696         for (lecp = list_head(listp); lecp; lecp = next) {
3697                 next = list_next(listp, lecp);
3698                 if (lecp->lec_id == id) {
3699                         ASSERT(found == NULL);
3700 
3701                         /* If there is a walk in progress, move it along... */
3702                         if (walker_next == lecp)
3703                                 walker_next = next;
3704                         if (walker_prev == lecp)
3705                                 walker_prev = list_prev(listp, walker_prev);
3706 
3707                         list_remove(listp, lecp);
3708                         found = lecp;
3709                 }
3710         }
3711         ldi_ev_unlock();
3712 
3713         if (found == NULL) {
3714                 cmn_err(CE_WARN, "No LDI event handler for id (%p)",
3715                     (void *)id);
3716                 return (LDI_EV_SUCCESS);
3717         }
3718 
3719         if (!ldi_native_cookie(found->lec_cookie)) {
3720                 ASSERT(found->lec_notify == NULL);
3721                 if (ddi_remove_event_handler((ddi_callback_id_t)id)
3722                     != DDI_SUCCESS) {
3723                         cmn_err(CE_WARN, "failed to remove NDI event handler "
3724                             "for id (%p)", (void *)id);
3725                         ldi_ev_lock();
3726                         list_insert_tail(listp, found);