FreeBSD ZFS
The Zettabyte File System
|
00001 /* 00002 * CDDL HEADER START 00003 * 00004 * The contents of this file are subject to the terms of the 00005 * Common Development and Distribution License (the "License"). 00006 * You may not use this file except in compliance with the License. 00007 * 00008 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 00009 * or http://www.opensolaris.org/os/licensing. 00010 * See the License for the specific language governing permissions 00011 * and limitations under the License. 00012 * 00013 * When distributing Covered Code, include this CDDL HEADER in each 00014 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 00015 * If applicable, add the following below this CDDL HEADER, with the 00016 * fields enclosed by brackets "[]" replaced with your own identifying 00017 * information: Portions Copyright [yyyy] [name of copyright owner] 00018 * 00019 * CDDL HEADER END 00020 */ 00021 /* 00022 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 00023 * Use is subject to license terms. 00024 */ 00025 00026 #include <sys/refcount.h> 00027 #include <sys/rrwlock.h> 00028 00072 uint_t rrw_tsd_key; 00073 00074 typedef struct rrw_node { 00075 struct rrw_node *rn_next; 00076 rrwlock_t *rn_rrl; 00077 } rrw_node_t; 00078 00079 static rrw_node_t * 00080 rrn_find(rrwlock_t *rrl) 00081 { 00082 rrw_node_t *rn; 00083 00084 if (refcount_count(&rrl->rr_linked_rcount) == 0) 00085 return (NULL); 00086 00087 for (rn = tsd_get(rrw_tsd_key); rn != NULL; rn = rn->rn_next) { 00088 if (rn->rn_rrl == rrl) 00089 return (rn); 00090 } 00091 return (NULL); 00092 } 00093 00097 static void 00098 rrn_add(rrwlock_t *rrl) 00099 { 00100 rrw_node_t *rn; 00101 00102 rn = kmem_alloc(sizeof (*rn), KM_SLEEP); 00103 rn->rn_rrl = rrl; 00104 rn->rn_next = tsd_get(rrw_tsd_key); 00105 VERIFY(tsd_set(rrw_tsd_key, rn) == 0); 00106 } 00107 00112 static boolean_t 00113 rrn_find_and_remove(rrwlock_t *rrl) 00114 { 00115 rrw_node_t *rn; 00116 rrw_node_t *prev = NULL; 00117 00118 if (refcount_count(&rrl->rr_linked_rcount) == 0) 00119 return (B_FALSE); 00120 00121 for (rn = tsd_get(rrw_tsd_key); rn != NULL; rn = rn->rn_next) { 00122 if (rn->rn_rrl == rrl) { 00123 if (prev) 00124 prev->rn_next = rn->rn_next; 00125 else 00126 VERIFY(tsd_set(rrw_tsd_key, rn->rn_next) == 0); 00127 kmem_free(rn, sizeof (*rn)); 00128 return (B_TRUE); 00129 } 00130 prev = rn; 00131 } 00132 return (B_FALSE); 00133 } 00134 00135 void 00136 rrw_init(rrwlock_t *rrl) 00137 { 00138 mutex_init(&rrl->rr_lock, NULL, MUTEX_DEFAULT, NULL); 00139 cv_init(&rrl->rr_cv, NULL, CV_DEFAULT, NULL); 00140 rrl->rr_writer = NULL; 00141 refcount_create(&rrl->rr_anon_rcount); 00142 refcount_create(&rrl->rr_linked_rcount); 00143 rrl->rr_writer_wanted = B_FALSE; 00144 } 00145 00146 void 00147 rrw_destroy(rrwlock_t *rrl) 00148 { 00149 mutex_destroy(&rrl->rr_lock); 00150 cv_destroy(&rrl->rr_cv); 00151 ASSERT(rrl->rr_writer == NULL); 00152 refcount_destroy(&rrl->rr_anon_rcount); 00153 refcount_destroy(&rrl->rr_linked_rcount); 00154 } 00155 00156 static void 00157 rrw_enter_read(rrwlock_t *rrl, void *tag) 00158 { 00159 mutex_enter(&rrl->rr_lock); 00160 #if !defined(DEBUG) && defined(_KERNEL) 00161 if (!rrl->rr_writer && !rrl->rr_writer_wanted) { 00162 rrl->rr_anon_rcount.rc_count++; 00163 mutex_exit(&rrl->rr_lock); 00164 return; 00165 } 00166 DTRACE_PROBE(zfs__rrwfastpath__rdmiss); 00167 #endif 00168 ASSERT(rrl->rr_writer != curthread); 00169 ASSERT(refcount_count(&rrl->rr_anon_rcount) >= 0); 00170 00171 while (rrl->rr_writer || (rrl->rr_writer_wanted && 00172 refcount_is_zero(&rrl->rr_anon_rcount) && 00173 rrn_find(rrl) == NULL)) 00174 cv_wait(&rrl->rr_cv, &rrl->rr_lock); 00175 00176 if (rrl->rr_writer_wanted) { 00177 /* may or may not be a re-entrant enter */ 00178 rrn_add(rrl); 00179 (void) refcount_add(&rrl->rr_linked_rcount, tag); 00180 } else { 00181 (void) refcount_add(&rrl->rr_anon_rcount, tag); 00182 } 00183 ASSERT(rrl->rr_writer == NULL); 00184 mutex_exit(&rrl->rr_lock); 00185 } 00186 00187 static void 00188 rrw_enter_write(rrwlock_t *rrl) 00189 { 00190 mutex_enter(&rrl->rr_lock); 00191 ASSERT(rrl->rr_writer != curthread); 00192 00193 while (refcount_count(&rrl->rr_anon_rcount) > 0 || 00194 refcount_count(&rrl->rr_linked_rcount) > 0 || 00195 rrl->rr_writer != NULL) { 00196 rrl->rr_writer_wanted = B_TRUE; 00197 cv_wait(&rrl->rr_cv, &rrl->rr_lock); 00198 } 00199 rrl->rr_writer_wanted = B_FALSE; 00200 rrl->rr_writer = curthread; 00201 mutex_exit(&rrl->rr_lock); 00202 } 00203 00209 void 00210 rrw_enter(rrwlock_t *rrl, krw_t rw, void *tag) 00211 { 00212 if (rw == RW_READER) 00213 rrw_enter_read(rrl, tag); 00214 else 00215 rrw_enter_write(rrl); 00216 } 00217 00223 void 00224 rrw_exit(rrwlock_t *rrl, void *tag) 00225 { 00226 mutex_enter(&rrl->rr_lock); 00227 #if !defined(DEBUG) && defined(_KERNEL) 00228 if (!rrl->rr_writer && rrl->rr_linked_rcount.rc_count == 0) { 00229 rrl->rr_anon_rcount.rc_count--; 00230 if (rrl->rr_anon_rcount.rc_count == 0) 00231 cv_broadcast(&rrl->rr_cv); 00232 mutex_exit(&rrl->rr_lock); 00233 return; 00234 } 00235 DTRACE_PROBE(zfs__rrwfastpath__exitmiss); 00236 #endif 00237 ASSERT(!refcount_is_zero(&rrl->rr_anon_rcount) || 00238 !refcount_is_zero(&rrl->rr_linked_rcount) || 00239 rrl->rr_writer != NULL); 00240 00241 if (rrl->rr_writer == NULL) { 00242 int64_t count; 00243 if (rrn_find_and_remove(rrl)) 00244 count = refcount_remove(&rrl->rr_linked_rcount, tag); 00245 else 00246 count = refcount_remove(&rrl->rr_anon_rcount, tag); 00247 if (count == 0) 00248 cv_broadcast(&rrl->rr_cv); 00249 } else { 00250 ASSERT(rrl->rr_writer == curthread); 00251 ASSERT(refcount_is_zero(&rrl->rr_anon_rcount) && 00252 refcount_is_zero(&rrl->rr_linked_rcount)); 00253 rrl->rr_writer = NULL; 00254 cv_broadcast(&rrl->rr_cv); 00255 } 00256 mutex_exit(&rrl->rr_lock); 00257 } 00258 00259 boolean_t 00260 rrw_held(rrwlock_t *rrl, krw_t rw) 00261 { 00262 boolean_t held; 00263 00264 mutex_enter(&rrl->rr_lock); 00265 if (rw == RW_WRITER) { 00266 held = (rrl->rr_writer == curthread); 00267 } else { 00268 held = (!refcount_is_zero(&rrl->rr_anon_rcount) || 00269 !refcount_is_zero(&rrl->rr_linked_rcount)); 00270 } 00271 mutex_exit(&rrl->rr_lock); 00272 00273 return (held); 00274 }