3302         return (1);
3303 }
3304 
3305 /*
3306  * LDI framework function to post a "notify" event to all layered drivers
3307  * that have registered for that event
3308  *
3309  * Returns:
3310  *              LDI_EV_SUCCESS - registered callbacks allow event
3311  *              LDI_EV_FAILURE - registered callbacks block event
3312  *              LDI_EV_NONE    - No matching LDI callbacks
3313  *
3314  * This function is *not* to be called by layered drivers. It is for I/O
3315  * framework code in Solaris, such as the I/O retire code and DR code
3316  * to call while servicing a device event such as offline or degraded.
3317  */
3318 int
3319 ldi_invoke_notify(dev_info_t *dip, dev_t dev, int spec_type, char *event,
3320     void *ev_data)
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                 }
3367 
3368                 /*
3369                  * Check if matching event
3370                  */
3371                 lec_event = ldi_ev_get_type(lecp->lec_cookie);
3372                 if (strcmp(event, lec_event) != 0) {
3373                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): Not matching"
3374                             " event {%s,%s}. skipping", event, lec_event));
3375                         continue;
3376                 }
3377 
3378                 lecp->lec_lhp->lh_flags |= LH_FLAGS_NOTIFY;
3379                 if (lecp->lec_notify(lecp->lec_lhp, lecp->lec_cookie,
3380                     lecp->lec_arg, ev_data) != LDI_EV_SUCCESS) {
3381                         ret = LDI_EV_FAILURE;
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.
3425                  */
3426 
3427                 /* Check if matching event */
3428                 lec_event = ldi_ev_get_type(lecp->lec_cookie);
3429                 if (strcmp(event, lec_event) != 0) {
3430                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): not matching "
3431                             "event: %s,%s, skipping", event, lec_event));
3432                         continue;
3433                 }
3434 
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  */
 
 | 
 
 
3302         return (1);
3303 }
3304 
3305 /*
3306  * LDI framework function to post a "notify" event to all layered drivers
3307  * that have registered for that event
3308  *
3309  * Returns:
3310  *              LDI_EV_SUCCESS - registered callbacks allow event
3311  *              LDI_EV_FAILURE - registered callbacks block event
3312  *              LDI_EV_NONE    - No matching LDI callbacks
3313  *
3314  * This function is *not* to be called by layered drivers. It is for I/O
3315  * framework code in Solaris, such as the I/O retire code and DR code
3316  * to call while servicing a device event such as offline or degraded.
3317  */
3318 int
3319 ldi_invoke_notify(dev_info_t *dip, dev_t dev, int spec_type, char *event,
3320     void *ev_data)
3321 {
3322         ldi_ev_callback_impl_t  *lecp,
3323                                 *saved_lecp;
3324         list_t  *listp;
3325         int     ret;
3326         char    *lec_event;
3327 
3328         ASSERT(dip);
3329         ASSERT(dev != DDI_DEV_T_NONE);
3330         ASSERT(dev != NODEV);
3331         ASSERT((dev == DDI_DEV_T_ANY && spec_type == 0) ||
3332             (spec_type == S_IFCHR || spec_type == S_IFBLK));
3333         ASSERT(event);
3334         ASSERT(ldi_native_event(event));
3335         ASSERT(ldi_ev_sync_event(event));
3336 
3337         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): entered: dip=%p, ev=%s",
3338             (void *)dip, event));
3339 
3340         ret = LDI_EV_NONE;
3341         ldi_ev_lock();
3342 
3343         VERIFY(ldi_ev_callback_list.le_walker_next == NULL);
3344 restart_loop:
3345         listp = &ldi_ev_callback_list.le_head;
3346         for (lecp = list_head(listp); lecp; lecp =
3347             ldi_ev_callback_list.le_walker_next) {
3348                 ldi_ev_callback_list.le_walker_next = list_next(listp, lecp);
3349 
3350                 /* Check if matching device */
3351                 if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
3352                         continue;
3353 
3354                 if (lecp->lec_lhp == NULL) {
3355                         /*
3356                          * Consumer has unregistered the handle and so
3357                          * is no longer interested in notify events.
3358                          */
3359                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): No LDI "
3360                             "handle, skipping"));
3361                         continue;
3362                 }
3363 
3364                 if (lecp->lec_notify == NULL) {
3365                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): No notify "
3366                             "callback. skipping"));
3367                         continue;       /* not interested in notify */
3368                 }
3369 
3370                 /*
3371                  * Check if matching event
3372                  */
3373                 lec_event = ldi_ev_get_type(lecp->lec_cookie);
3374                 if (strcmp(event, lec_event) != 0) {
3375                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): Not matching"
3376                             " event {%s,%s}. skipping", event, lec_event));
3377                         continue;
3378                 }
3379 
3380                 /*
3381                  * To keep the sematics of only calling the notify callback
3382                  * once ignore this entry if during a previous loop start this
3383                  * bit was set. This bit will be cleared from all entries
3384                  * at the end of this routine.
3385                  */
3386                 if (lecp->lec_lhp->lh_flags & LH_FLAGS_NOTIFY_NOTIFY)
3387                         continue;
3388 
3389                 lecp->lec_lhp->lh_flags |= LH_FLAGS_NOTIFY |
3390                     LH_FLAGS_NOTIFY_NOTIFY;
3391 
3392                 /*
3393                  * Need to drop the lock here before starting the notify
3394                  * callback. A problem was found where the callback is
3395                  * for ZFS which attempts to grab an internal lock of it's own.
3396                  * Unfortunately there's another thread which starts with
3397                  * ZFS that grabs the internal lock and then calls into the
3398                  * LDI layer which attempts to grab this lock. Deadlock.
3399                  * So, drop and reaquire later which means the loop must
3400                  * be restarted since there's no telling what happened to
3401                  * the linked list.
3402                  */
3403                 ldi_ev_unlock();
3404                 ret = lecp->lec_notify(lecp->lec_lhp, lecp->lec_cookie,
3405                     lecp->lec_arg, ev_data);
3406                 ldi_ev_lock();
3407 
3408                 if (ret != LDI_EV_SUCCESS) {
3409                         ret = LDI_EV_FAILURE;
3410                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): notify"
3411                             " FAILURE"));
3412                         break;
3413                 }
3414 
3415                 /* We have a matching callback that allows the event to occur */
3416                 LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): 1 consumer success"));
3417                 goto restart_loop;
3418         }
3419 
3420         if (ret != LDI_EV_FAILURE)
3421                 goto out;
3422 
3423         saved_lecp = list_prev(listp, lecp);
3424 
3425         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): undoing notify"));
3426 
3427         /*
3428          * Undo notifies already sent
3429          */
3430         VERIFY(ldi_ev_callback_list.le_walker_prev == NULL);
3431 restart_2nd_loop:
3432         for (lecp = saved_lecp; lecp;
3433             lecp = ldi_ev_callback_list.le_walker_prev) {
3434                 ldi_ev_callback_list.le_walker_prev = list_prev(listp, lecp);
3435 
3436                 /*
3437                  * Check if matching device
3438                  */
3439                 if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
3440                         continue;
3441 
3442 
3443                 if (lecp->lec_finalize == NULL) {
3444                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): no finalize, "
3445                             "skipping"));
3446                         continue;       /* not interested in finalize */
3447                 }
3448 
3449                 /*
3450                  * it is possible that in response to a notify event a
3451                  * layered driver closed its LDI handle so it is ok
3452                  * to have a NULL LDI handle for finalize. The layered
3453                  * driver is expected to maintain state in its "arg"
3454                  * parameter to keep track of the closed device.
3455                  */
3456 
3457                 /* Check if matching event */
3458                 lec_event = ldi_ev_get_type(lecp->lec_cookie);
3459                 if (strcmp(event, lec_event) != 0) {
3460                         LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): not matching "
3461                             "event: %s,%s, skipping", event, lec_event));
3462                         continue;
3463                 }
3464 
3465                 LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): calling finalize"));
3466 
3467                 if (lecp->lec_lhp->lh_flags & LH_FLAGS_NOTIFY_FINALIZE)
3468                         continue;
3469                 lecp->lec_lhp->lh_flags |= LH_FLAGS_NOTIFY_FINALIZE;
3470 
3471                 ldi_ev_unlock();
3472                 lecp->lec_finalize(lecp->lec_lhp, lecp->lec_cookie,
3473                     LDI_EV_FAILURE, lecp->lec_arg, ev_data);
3474                 ldi_ev_lock();
3475 
3476                 /*
3477                  * If LDI native event and LDI handle closed in context
3478                  * of notify, NULL out the finalize callback as we have
3479                  * already called the 1 finalize above allowed in this situation
3480                  */
3481                 if (lecp->lec_lhp == NULL &&
3482                     ldi_native_cookie(lecp->lec_cookie)) {
3483                         LDI_EVDBG((CE_NOTE,
3484                             "ldi_invoke_notify(): NULL-ing finalize after "
3485                             "calling 1 finalize following ldi_close"));
3486                         lecp->lec_finalize = NULL;
3487                 }
3488                 goto restart_2nd_loop;
3489         }
3490 out:
3491         /*
3492          * Clear out the temporary flag for future runs.
3493          */
3494         for (lecp = list_head(listp); lecp; lecp = list_next(listp, lecp)) {
3495                 if (lecp->lec_lhp == NULL)
3496                         continue;
3497                 lecp->lec_lhp->lh_flags &= ~(LH_FLAGS_NOTIFY_NOTIFY |
3498                                              LH_FLAGS_NOTIFY_FINALIZE);
3499         }
3500         ldi_ev_callback_list.le_walker_next = NULL;
3501         ldi_ev_callback_list.le_walker_prev = NULL;
3502         ldi_ev_unlock();
3503 
3504         if (ret == LDI_EV_NONE) {
3505                 LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): no matching "
3506                     "LDI callbacks"));
3507         }
3508 
3509         return (ret);
3510 }
3511 
3512 /*
3513  * Framework function to be called from a layered driver to propagate
3514  * LDI "notify" events to exported minors.
3515  *
3516  * This function is a public interface exported by the LDI framework
3517  * for use by layered drivers to propagate device events up the software
3518  * stack.
3519  */
 
 |