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 */
|