FreeBSD ZFS
The Zettabyte File System

rrwlock.c

Go to the documentation of this file.
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 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines