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