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

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/os/driver_lyr.c
          +++ new/usr/src/uts/common/os/driver_lyr.c
↓ open down ↓ 13 lines elided ↑ open up ↑
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
  23   23   */
       24 +/*
       25 + * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
       26 + */
  24   27  
  25   28  /*
  26   29   * Layered driver support.
  27   30   */
  28   31  
  29   32  #include <sys/atomic.h>
  30   33  #include <sys/types.h>
  31   34  #include <sys/t_lock.h>
  32   35  #include <sys/param.h>
  33   36  #include <sys/conf.h>
↓ open down ↓ 86 lines elided ↑ open up ↑
 120  123  /*
 121  124   * globals
 122  125   */
 123  126  static kmutex_t                 ldi_ident_hash_lock[LI_HASH_SZ];
 124  127  static struct ldi_ident         *ldi_ident_hash[LI_HASH_SZ];
 125  128  
 126  129  static kmutex_t                 ldi_handle_hash_lock[LH_HASH_SZ];
 127  130  static struct ldi_handle        *ldi_handle_hash[LH_HASH_SZ];
 128  131  static size_t                   ldi_handle_hash_count;
 129  132  
      133 +/*
      134 + * Use of "ldi_ev_callback_list" must be protected by ldi_ev_lock()
      135 + * and ldi_ev_unlock().
      136 + */
 130  137  static struct ldi_ev_callback_list ldi_ev_callback_list;
 131  138  
 132  139  static uint32_t ldi_ev_id_pool = 0;
 133  140  
 134  141  struct ldi_ev_cookie {
 135  142          char *ck_evname;
 136  143          uint_t ck_sync;
 137  144          uint_t ck_ctype;
 138  145  };
 139  146  
↓ open down ↓ 19 lines elided ↑ open up ↑
 159  166                  ldi_ident_hash[i] = NULL;
 160  167          }
 161  168  
 162  169          /*
 163  170           * Initialize the LDI event subsystem
 164  171           */
 165  172          mutex_init(&ldi_ev_callback_list.le_lock, NULL, MUTEX_DEFAULT, NULL);
 166  173          cv_init(&ldi_ev_callback_list.le_cv, NULL, CV_DEFAULT, NULL);
 167  174          ldi_ev_callback_list.le_busy = 0;
 168  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;
 169  178          list_create(&ldi_ev_callback_list.le_head,
 170  179              sizeof (ldi_ev_callback_impl_t),
 171  180              offsetof(ldi_ev_callback_impl_t, lec_list));
 172  181  }
 173  182  
 174  183  /*
 175  184   * LDI ident manipulation functions
 176  185   */
 177  186  static uint_t
 178  187  ident_hash_func(modid_t modid, dev_info_t *dip, dev_t dev)
↓ open down ↓ 3143 lines elided ↑ open up ↑
3322 3331              (spec_type == S_IFCHR || spec_type == S_IFBLK));
3323 3332          ASSERT(event);
3324 3333          ASSERT(ldi_native_event(event));
3325 3334          ASSERT(ldi_ev_sync_event(event));
3326 3335  
3327 3336          LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): entered: dip=%p, ev=%s",
3328 3337              (void *)dip, event));
3329 3338  
3330 3339          ret = LDI_EV_NONE;
3331 3340          ldi_ev_lock();
     3341 +
     3342 +        VERIFY(ldi_ev_callback_list.le_walker_next == NULL);
3332 3343          listp = &ldi_ev_callback_list.le_head;
3333      -        for (lecp = list_head(listp); lecp; lecp = list_next(listp, lecp)) {
     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);
3334 3347  
3335 3348                  /* Check if matching device */
3336 3349                  if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
3337 3350                          continue;
3338 3351  
3339 3352                  if (lecp->lec_lhp == NULL) {
3340 3353                          /*
3341 3354                           * Consumer has unregistered the handle and so
3342 3355                           * is no longer interested in notify events.
3343 3356                           */
↓ open down ↓ 35 lines elided ↑ open up ↑
3379 3392  
3380 3393          if (ret != LDI_EV_FAILURE)
3381 3394                  goto out;
3382 3395  
3383 3396          LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): undoing notify"));
3384 3397  
3385 3398          /*
3386 3399           * Undo notifies already sent
3387 3400           */
3388 3401          lecp = list_prev(listp, lecp);
3389      -        for (; lecp; 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);
3390 3405  
3391 3406                  /*
3392 3407                   * Check if matching device
3393 3408                   */
3394 3409                  if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
3395 3410                          continue;
3396 3411  
3397 3412  
3398 3413                  if (lecp->lec_finalize == NULL) {
3399 3414                          LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): no finalize, "
↓ open down ↓ 30 lines elided ↑ open up ↑
3430 3445                  if (lecp->lec_lhp == NULL &&
3431 3446                      ldi_native_cookie(lecp->lec_cookie)) {
3432 3447                          LDI_EVDBG((CE_NOTE,
3433 3448                              "ldi_invoke_notify(): NULL-ing finalize after "
3434 3449                              "calling 1 finalize following ldi_close"));
3435 3450                          lecp->lec_finalize = NULL;
3436 3451                  }
3437 3452          }
3438 3453  
3439 3454  out:
     3455 +        ldi_ev_callback_list.le_walker_next = NULL;
     3456 +        ldi_ev_callback_list.le_walker_prev = NULL;
3440 3457          ldi_ev_unlock();
3441 3458  
3442 3459          if (ret == LDI_EV_NONE) {
3443 3460                  LDI_EVDBG((CE_NOTE, "ldi_invoke_notify(): no matching "
3444 3461                      "LDI callbacks"));
3445 3462          }
3446 3463  
3447 3464          return (ret);
3448 3465  }
3449 3466  
↓ open down ↓ 95 lines elided ↑ open up ↑
3545 3562          ASSERT((dev == DDI_DEV_T_ANY && spec_type == 0) ||
3546 3563              (spec_type == S_IFCHR || spec_type == S_IFBLK));
3547 3564          ASSERT(event);
3548 3565          ASSERT(ldi_native_event(event));
3549 3566          ASSERT(ldi_result == LDI_EV_SUCCESS || ldi_result == LDI_EV_FAILURE);
3550 3567  
3551 3568          LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): entered: dip=%p, result=%d"
3552 3569              " event=%s", (void *)dip, ldi_result, event));
3553 3570  
3554 3571          ldi_ev_lock();
     3572 +        VERIFY(ldi_ev_callback_list.le_walker_next == NULL);
3555 3573          listp = &ldi_ev_callback_list.le_head;
3556      -        for (lecp = list_head(listp); lecp; lecp = list_next(listp, lecp)) {
     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);
3557 3577  
3558 3578                  if (lecp->lec_finalize == NULL) {
3559 3579                          LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): No "
3560 3580                              "finalize. Skipping"));
3561 3581                          continue;       /* Not interested in finalize */
3562 3582                  }
3563 3583  
3564 3584                  /*
3565 3585                   * Check if matching device
3566 3586                   */
↓ open down ↓ 30 lines elided ↑ open up ↑
3597 3617                   * already called the 1 finalize above allowed in this situation
3598 3618                   */
3599 3619                  if (lecp->lec_lhp == NULL &&
3600 3620                      ldi_native_cookie(lecp->lec_cookie)) {
3601 3621                          LDI_EVDBG((CE_NOTE,
3602 3622                              "ldi_invoke_finalize(): NULLing finalize after "
3603 3623                              "calling 1 finalize following ldi_close"));
3604 3624                          lecp->lec_finalize = NULL;
3605 3625                  }
3606 3626          }
     3627 +        ldi_ev_callback_list.le_walker_next = NULL;
3607 3628          ldi_ev_unlock();
3608 3629  
3609 3630          if (found)
3610 3631                  return;
3611 3632  
3612 3633          LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): no matching callbacks"));
3613 3634  }
3614 3635  
3615 3636  /*
3616 3637   * Framework function to be called from a layered driver to propagate
↓ open down ↓ 60 lines elided ↑ open up ↑
3677 3698          LDI_EVDBG((CE_NOTE, "ldi_ev_remove_callbacks: entered: id=%p",
3678 3699              (void *)id));
3679 3700  
3680 3701          ldi_ev_lock();
3681 3702  
3682 3703          listp = &ldi_ev_callback_list.le_head;
3683 3704          next = found = NULL;
3684 3705          for (lecp = list_head(listp); lecp; lecp = next) {
3685 3706                  next = list_next(listp, lecp);
3686 3707                  if (lecp->lec_id == id) {
3687      -                        ASSERT(found == NULL);
     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 +
3688 3725                          list_remove(listp, lecp);
3689 3726                          found = lecp;
3690 3727                  }
3691 3728          }
3692 3729          ldi_ev_unlock();
3693 3730  
3694 3731          if (found == NULL) {
3695 3732                  cmn_err(CE_WARN, "No LDI event handler for id (%p)",
3696 3733                      (void *)id);
3697 3734                  return (LDI_EV_SUCCESS);
↓ open down ↓ 23 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX