1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/debug.h>
  28 #include <sys/ksynch.h>
  29 #include <sys/cmn_err.h>
  30 #include <sys/kmem.h>
  31 #include <sys/ddi.h>
  32 
  33 #define __NSC_GEN__
  34 #include "nsc_gen.h"
  35 #include "nsc_mem.h"
  36 #include "nsc_rmspin.h"
  37 #include "../nsctl.h"
  38 
  39 
  40 static kmutex_t _nsc_rmspin_slp;
  41 
  42 nsc_rmlock_t _nsc_lock_top;
  43 kmutex_t _nsc_global_lock;
  44 int _nsc_global_lock_init;
  45 
  46 extern nsc_mem_t *_nsc_local_mem;
  47 
  48 /*
  49  * void
  50  * _nsc_init_rmlock (void)
  51  *      Initialise global locks.
  52  *
  53  * Calling/Exit State:
  54  *      Called at driver initialisation time to allocate necessary
  55  *      data structures.
  56  */
  57 void
  58 _nsc_init_rmlock()
  59 {
  60         mutex_init(&_nsc_rmspin_slp, NULL, MUTEX_DRIVER, NULL);
  61 
  62         _nsc_lock_top.next = _nsc_lock_top.prev = &_nsc_lock_top;
  63 
  64         mutex_init(&_nsc_global_lock, NULL, MUTEX_DRIVER, NULL);
  65         _nsc_global_lock_init = 1;
  66 }
  67 
  68 
  69 /*
  70  * void
  71  * _nsc_deinit_rmlock (void)
  72  *      De-initialise global locks.
  73  *
  74  * Calling/Exit State:
  75  *      Called at driver unload time to de-allocate
  76  *      resources.
  77  */
  78 void
  79 _nsc_deinit_rmlock()
  80 {
  81         _nsc_global_lock_init = 0;
  82         mutex_destroy(&_nsc_global_lock);
  83 
  84         ASSERT(_nsc_lock_top.next == &_nsc_lock_top);
  85         ASSERT(_nsc_lock_top.prev == &_nsc_lock_top);
  86 
  87         mutex_destroy(&_nsc_rmspin_slp);
  88 }
  89 
  90 
  91 /*
  92  * int
  93  * _nsc_lock_all_rm (void)
  94  *      Take all global locks in address order.
  95  *
  96  * Calling/Exit State:
  97  *      Returns 0 if _nsc_unlock_all_rm() should be called, or -1.
  98  */
  99 int
 100 _nsc_lock_all_rm()
 101 {
 102         nsc_rmlock_t *lp;
 103 
 104         mutex_enter(&_nsc_rmspin_slp);
 105 
 106         for (lp = _nsc_lock_top.next; lp != &_nsc_lock_top; lp = lp->next) {
 107                 (void) nsc_rm_lock(lp);
 108         }
 109 
 110         return (0);
 111 }
 112 
 113 
 114 /*
 115  * void
 116  * _nsc_unlock_all_rm (void)
 117  *      Release all global locks in reverse address order.
 118  *
 119  * Calling/Exit State:
 120  */
 121 void
 122 _nsc_unlock_all_rm()
 123 {
 124         nsc_rmlock_t *lp;
 125 
 126         for (lp = _nsc_lock_top.prev; lp != &_nsc_lock_top; lp = lp->prev) {
 127                 nsc_rm_unlock(lp);
 128         }
 129 
 130         mutex_exit(&_nsc_rmspin_slp);
 131 }
 132 
 133 
 134 /*
 135  * nsc_rmlock_t *
 136  * nsc_rm_lock_alloc(char *name, int flag, void *arg)
 137  *      Allocate and initialise a global lock.
 138  *
 139  * Calling/Exit State:
 140  *      The 'flag' parameter should be either KM_SLEEP or KM_NOSLEEP,
 141  *      depending on whether the caller is willing to sleep while memory
 142  *      is allocated or not.
 143  *
 144  *      The 'arg' parameter is passed directly to the underlying
 145  *      mutex_init(9f) function call.
 146  *
 147  *      Returns NULL if lock cannot be allocated.
 148  */
 149 nsc_rmlock_t *
 150 nsc_rm_lock_alloc(char *name, int flag, void *arg)
 151 {
 152         nsc_rmlock_t *lp, *lk;
 153 
 154         if ((lk = (nsc_rmlock_t *)nsc_kmem_zalloc(sizeof (*lk),
 155             flag, _nsc_local_mem)) == NULL)
 156                 return (NULL);
 157 
 158         mutex_init(&lk->lockp, NULL, MUTEX_DRIVER, arg);
 159 
 160         mutex_enter(&_nsc_rmspin_slp);
 161 
 162         for (lp = _nsc_lock_top.next; lp != &_nsc_lock_top; lp = lp->next)
 163                 if (strcmp(lp->name, name) == 0)
 164                         break;
 165 
 166         if (lp != &_nsc_lock_top) {
 167                 mutex_exit(&_nsc_rmspin_slp);
 168 
 169                 mutex_destroy(&lk->lockp);
 170                 nsc_kmem_free(lk, sizeof (*lk));
 171 
 172                 cmn_err(CE_WARN, "!nsctl: rmlock double allocation (%s)", name);
 173                 return (NULL);
 174         }
 175 
 176         lk->name = name;
 177 
 178         lk->next = _nsc_lock_top.next;
 179         lk->prev = &_nsc_lock_top;
 180         _nsc_lock_top.next = lk;
 181         lk->next->prev = lk;
 182 
 183         mutex_exit(&_nsc_rmspin_slp);
 184 
 185         return (lk);
 186 }
 187 
 188 
 189 /*
 190  * void
 191  * nsc_rm_lock_destroy(nsc_rmlock_t *rmlockp)
 192  *      Release the global lock.
 193  *
 194  * Remarks:
 195  *      The specified global lock is released and made
 196  *      available for reallocation.
 197  */
 198 void
 199 nsc_rm_lock_dealloc(rmlockp)
 200 nsc_rmlock_t *rmlockp;
 201 {
 202         if (!rmlockp)
 203                 return;
 204 
 205         mutex_enter(&_nsc_rmspin_slp);
 206 
 207         rmlockp->next->prev = rmlockp->prev;
 208         rmlockp->prev->next = rmlockp->next;
 209 
 210         if (rmlockp->child) {
 211                 cmn_err(CE_WARN, "!nsctl: rmlock destroyed when locked (%s)",
 212                     rmlockp->name);
 213                 nsc_do_unlock(rmlockp->child);
 214                 rmlockp->child = NULL;
 215         }
 216 
 217         mutex_destroy(&rmlockp->lockp);
 218         mutex_exit(&_nsc_rmspin_slp);
 219 
 220         nsc_kmem_free(rmlockp, sizeof (*rmlockp));
 221 }
 222 
 223 
 224 /*
 225  * void
 226  * nsc_rm_lock(nsc_rmlock_t *rmlockp)
 227  *      Acquire a global lock.
 228  *
 229  * Calling/Exit State:
 230  *      rmlockp is the lock to be acquired.
 231  *      Returns 0 (success) or errno. Lock is not acquired if rc != 0.
 232  */
 233 int
 234 nsc_rm_lock(nsc_rmlock_t *rmlockp)
 235 {
 236         int rc;
 237 
 238         mutex_enter(&rmlockp->lockp);
 239 
 240         ASSERT(! rmlockp->child);
 241 
 242         /* always use a write-lock */
 243         rc = nsc_do_lock(1, &rmlockp->child);
 244         if (rc) {
 245                 rmlockp->child = NULL;
 246                 mutex_exit(&rmlockp->lockp);
 247         }
 248 
 249         return (rc);
 250 }
 251 
 252 
 253 /*
 254  * static void
 255  * nsc_rm_unlock(nsc_rmlock_t *rmlockp)
 256  *      Unlock a global lock.
 257  *
 258  * Calling/Exit State:
 259  *      rmlockp is the lock to be released.
 260  */
 261 void
 262 nsc_rm_unlock(nsc_rmlock_t *rmlockp)
 263 {
 264         if (rmlockp->child) {
 265                 ASSERT(MUTEX_HELD(&rmlockp->lockp));
 266                 nsc_do_unlock(rmlockp->child);
 267                 rmlockp->child = NULL;
 268                 mutex_exit(&rmlockp->lockp);
 269         }
 270 }