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 2017 Nexenta Systems, Inc.  All rights reserved.
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  *
  26  *      Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
  27  *      All rights reserved.
  28  */
  29 
  30 /*
  31  * A homegrown reader/writer lock implementation.  It addresses
  32  * two requirements not addressed by the system primitives.  They
  33  * are that the `enter" operation is optionally interruptible and
  34  * that that they can be re`enter'ed by writers without deadlock.
  35  *
  36  * All of this was borrowed from NFS.
  37  * See: uts/common/fs/nfs/nfs_subr.c
  38  *
  39  * XXX: Could we make this serve our needs instead?
  40  * See: uts/common/os/rwstlock.c
  41  * (and then use it for NFS too)
  42  */
  43 
  44 #include <sys/param.h>
  45 #include <sys/systm.h>
  46 #include <sys/errno.h>
  47 #include <sys/time.h>
  48 #include <sys/vnode.h>
  49 
  50 #include <smbfs/smbfs.h>
  51 #include <smbfs/smbfs_node.h>
  52 #include <smbfs/smbfs_subr.h>
  53 
  54 
  55 /*
  56  * Only can return non-zero if intr != 0.
  57  */
  58 int
  59 smbfs_rw_enter_sig(smbfs_rwlock_t *l, krw_t rw, int intr)
  60 {
  61 
  62         mutex_enter(&l->lock);
  63 
  64         /*
  65          * If this is a nested enter, then allow it.  There
  66          * must be as many exits as enters through.
  67          */
  68         if (l->owner == curthread) {
  69                 /* lock is held for writing by current thread */
  70                 ASSERT(rw == RW_READER || rw == RW_WRITER);
  71                 l->count--;
  72         } else if (rw == RW_READER) {
  73                 /*
  74                  * While there is a writer active or writers waiting,
  75                  * then wait for them to finish up and move on.  Then,
  76                  * increment the count to indicate that a reader is
  77                  * active.
  78                  */
  79                 while (l->count < 0 || l->waiters > 0) {
  80                         if (intr) {
  81                                 // lwp_nostop stuff...
  82                                 (void) cv_wait_sig(&l->cv, &l->lock);
  83                         } else
  84                                 cv_wait(&l->cv, &l->lock);
  85                 }
  86                 ASSERT(l->count < INT_MAX);
  87 #ifdef SMBDEBUG
  88                 if ((l->count % 10000) == 9999)
  89                         cmn_err(CE_WARN, "smbfs_rw_enter_sig: count %d on"
  90                             "rwlock @ %p\n", l->count, (void *)&l);
  91 #endif
  92                 l->count++;
  93         } else {
  94                 ASSERT(rw == RW_WRITER);
  95                 /*
  96                  * While there are readers active or a writer
  97                  * active, then wait for all of the readers
  98                  * to finish or for the writer to finish.
  99                  * Then, set the owner field to curthread and
 100                  * decrement count to indicate that a writer
 101                  * is active.
 102                  */
 103                 while (l->count > 0 || l->owner != NULL) {
 104                         l->waiters++;
 105                         if (intr) {
 106                                 // lwp_nostop stuff...
 107                                 if (!cv_wait_sig(&l->cv, &l->lock)) {
 108                                         l->waiters--;
 109                                         cv_broadcast(&l->cv);
 110                                         mutex_exit(&l->lock);
 111                                         return (EINTR);
 112                                 }
 113                         } else
 114                                 cv_wait(&l->cv, &l->lock);
 115                         l->waiters--;
 116                 }
 117                 l->owner = curthread;
 118                 l->count--;
 119         }
 120 
 121         mutex_exit(&l->lock);
 122 
 123         return (0);
 124 }
 125 
 126 /*
 127  * If the lock is available, obtain it and return non-zero.  If there is
 128  * already a conflicting lock, return 0 immediately.
 129  */
 130 
 131 int
 132 smbfs_rw_tryenter(smbfs_rwlock_t *l, krw_t rw)
 133 {
 134         mutex_enter(&l->lock);
 135 
 136         /*
 137          * If this is a nested enter, then allow it.  There
 138          * must be as many exits as enters through.
 139          */
 140         if (l->owner == curthread) {
 141                 /* lock is held for writing by current thread */
 142                 ASSERT(rw == RW_READER || rw == RW_WRITER);
 143                 l->count--;
 144         } else if (rw == RW_READER) {
 145                 /*
 146                  * If there is a writer active or writers waiting, deny the
 147                  * lock.  Otherwise, bump the count of readers.
 148                  */
 149                 if (l->count < 0 || l->waiters > 0) {
 150                         mutex_exit(&l->lock);
 151                         return (0);
 152                 }
 153                 l->count++;
 154         } else {
 155                 ASSERT(rw == RW_WRITER);
 156                 /*
 157                  * If there are readers active or a writer active, deny the
 158                  * lock.  Otherwise, set the owner field to curthread and
 159                  * decrement count to indicate that a writer is active.
 160                  */
 161                 if (l->count > 0 || l->owner != NULL) {
 162                         mutex_exit(&l->lock);
 163                         return (0);
 164                 }
 165                 l->owner = curthread;
 166                 l->count--;
 167         }
 168 
 169         mutex_exit(&l->lock);
 170 
 171         return (1);
 172 }
 173 
 174 void
 175 smbfs_rw_exit(smbfs_rwlock_t *l)
 176 {
 177 
 178         mutex_enter(&l->lock);
 179         /*
 180          * If this is releasing a writer lock, then increment count to
 181          * indicate that there is one less writer active.  If this was
 182          * the last of possibly nested writer locks, then clear the owner
 183          * field as well to indicate that there is no writer active
 184          * and wakeup any possible waiting writers or readers.
 185          *
 186          * If releasing a reader lock, then just decrement count to
 187          * indicate that there is one less reader active.  If this was
 188          * the last active reader and there are writer(s) waiting,
 189          * then wake up the first.
 190          */
 191         if (l->owner != NULL) {
 192                 ASSERT(l->owner == curthread);
 193                 l->count++;
 194                 if (l->count == 0) {
 195                         l->owner = NULL;
 196                         cv_broadcast(&l->cv);
 197                 }
 198         } else {
 199                 ASSERT(l->count > 0);
 200                 l->count--;
 201                 if (l->count == 0 && l->waiters > 0)
 202                         cv_broadcast(&l->cv);
 203         }
 204         mutex_exit(&l->lock);
 205 }
 206 
 207 int
 208 smbfs_rw_lock_held(smbfs_rwlock_t *l, krw_t rw)
 209 {
 210 
 211         if (rw == RW_READER)
 212                 return (l->count > 0);
 213         ASSERT(rw == RW_WRITER);
 214         return (l->count < 0);
 215 }
 216 
 217 /* ARGSUSED */
 218 void
 219 smbfs_rw_init(smbfs_rwlock_t *l, char *name, krw_type_t type, void *arg)
 220 {
 221 
 222         l->count = 0;
 223         l->waiters = 0;
 224         l->owner = NULL;
 225         mutex_init(&l->lock, NULL, MUTEX_DEFAULT, NULL);
 226         cv_init(&l->cv, NULL, CV_DEFAULT, NULL);
 227 }
 228 
 229 void
 230 smbfs_rw_destroy(smbfs_rwlock_t *l)
 231 {
 232 
 233         mutex_destroy(&l->lock);
 234         cv_destroy(&l->cv);
 235 }