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