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