]>
Commit | Line | Data |
---|---|---|
572e2857 BB |
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 (c) 2010, Oracle and/or its affiliates. All rights reserved. | |
cffd6e11 GM |
23 | * Copyright (c) 2014, 2015 by Delphix. All rights reserved. |
24 | * Copyright 2016 The MathWorks, Inc. All rights reserved. | |
572e2857 BB |
25 | */ |
26 | ||
27 | /* | |
28 | * A Zero Reference Lock (ZRL) is a reference count that can lock out new | |
29 | * references only when the count is zero and only without waiting if the count | |
30 | * is not already zero. It is similar to a read-write lock in that it allows | |
31 | * multiple readers and only a single writer, but it does not allow a writer to | |
32 | * block while waiting for readers to exit, and therefore the question of | |
33 | * reader/writer priority is moot (no WRWANT bit). Since the equivalent of | |
34 | * rw_enter(&lock, RW_WRITER) is disallowed and only tryenter() is allowed, it | |
35 | * is perfectly safe for the same reader to acquire the same lock multiple | |
36 | * times. The fact that a ZRL is reentrant for readers (through multiple calls | |
37 | * to zrl_add()) makes it convenient for determining whether something is | |
38 | * actively referenced without the fuss of flagging lock ownership across | |
39 | * function calls. | |
40 | */ | |
41 | #include <sys/zrlock.h> | |
49ee64e5 | 42 | #include <sys/trace_zrlock.h> |
572e2857 BB |
43 | |
44 | /* | |
45 | * A ZRL can be locked only while there are zero references, so ZRL_LOCKED is | |
46 | * treated as zero references. | |
47 | */ | |
8dd86a10 | 48 | #define ZRL_LOCKED -1 |
572e2857 BB |
49 | #define ZRL_DESTROYED -2 |
50 | ||
51 | void | |
52 | zrl_init(zrlock_t *zrl) | |
53 | { | |
54 | mutex_init(&zrl->zr_mtx, NULL, MUTEX_DEFAULT, NULL); | |
55 | zrl->zr_refcount = 0; | |
56 | cv_init(&zrl->zr_cv, NULL, CV_DEFAULT, NULL); | |
57 | #ifdef ZFS_DEBUG | |
58 | zrl->zr_owner = NULL; | |
59 | zrl->zr_caller = NULL; | |
60 | #endif | |
61 | } | |
62 | ||
63 | void | |
64 | zrl_destroy(zrlock_t *zrl) | |
65 | { | |
8dd86a10 | 66 | ASSERT0(zrl->zr_refcount); |
572e2857 BB |
67 | |
68 | mutex_destroy(&zrl->zr_mtx); | |
69 | zrl->zr_refcount = ZRL_DESTROYED; | |
70 | cv_destroy(&zrl->zr_cv); | |
71 | } | |
72 | ||
73 | void | |
e9aa730c | 74 | zrl_add_impl(zrlock_t *zrl, const char *zc) |
572e2857 | 75 | { |
cffd6e11 GM |
76 | for (;;) { |
77 | uint32_t n = (uint32_t)zrl->zr_refcount; | |
78 | while (n != ZRL_LOCKED) { | |
79 | uint32_t cas = atomic_cas_32( | |
80 | (uint32_t *)&zrl->zr_refcount, n, n + 1); | |
81 | if (cas == n) { | |
82 | ASSERT3S((int32_t)n, >=, 0); | |
572e2857 | 83 | #ifdef ZFS_DEBUG |
cffd6e11 | 84 | if (zrl->zr_owner == curthread) { |
9470cbd4 CC |
85 | DTRACE_PROBE3(zrlock__reentry, |
86 | zrlock_t *, zrl, | |
87 | kthread_t *, curthread, | |
88 | uint32_t, n); | |
cffd6e11 GM |
89 | } |
90 | zrl->zr_owner = curthread; | |
91 | zrl->zr_caller = zc; | |
572e2857 | 92 | #endif |
cffd6e11 GM |
93 | return; |
94 | } | |
95 | n = cas; | |
572e2857 | 96 | } |
572e2857 | 97 | |
cffd6e11 GM |
98 | mutex_enter(&zrl->zr_mtx); |
99 | while (zrl->zr_refcount == ZRL_LOCKED) { | |
100 | cv_wait(&zrl->zr_cv, &zrl->zr_mtx); | |
101 | } | |
102 | mutex_exit(&zrl->zr_mtx); | |
572e2857 | 103 | } |
572e2857 BB |
104 | } |
105 | ||
106 | void | |
107 | zrl_remove(zrlock_t *zrl) | |
108 | { | |
109 | uint32_t n; | |
110 | ||
572e2857 BB |
111 | #ifdef ZFS_DEBUG |
112 | if (zrl->zr_owner == curthread) { | |
113 | zrl->zr_owner = NULL; | |
114 | zrl->zr_caller = NULL; | |
115 | } | |
116 | #endif | |
8dd86a10 MA |
117 | n = atomic_dec_32_nv((uint32_t *)&zrl->zr_refcount); |
118 | ASSERT3S((int32_t)n, >=, 0); | |
572e2857 BB |
119 | } |
120 | ||
121 | int | |
122 | zrl_tryenter(zrlock_t *zrl) | |
123 | { | |
124 | uint32_t n = (uint32_t)zrl->zr_refcount; | |
125 | ||
126 | if (n == 0) { | |
127 | uint32_t cas = atomic_cas_32( | |
128 | (uint32_t *)&zrl->zr_refcount, 0, ZRL_LOCKED); | |
129 | if (cas == 0) { | |
130 | #ifdef ZFS_DEBUG | |
8dd86a10 | 131 | ASSERT3P(zrl->zr_owner, ==, NULL); |
572e2857 BB |
132 | zrl->zr_owner = curthread; |
133 | #endif | |
134 | return (1); | |
135 | } | |
136 | } | |
137 | ||
8dd86a10 | 138 | ASSERT3S((int32_t)n, >, ZRL_DESTROYED); |
572e2857 BB |
139 | |
140 | return (0); | |
141 | } | |
142 | ||
143 | void | |
144 | zrl_exit(zrlock_t *zrl) | |
145 | { | |
8dd86a10 | 146 | ASSERT3S(zrl->zr_refcount, ==, ZRL_LOCKED); |
572e2857 BB |
147 | |
148 | mutex_enter(&zrl->zr_mtx); | |
149 | #ifdef ZFS_DEBUG | |
8dd86a10 | 150 | ASSERT3P(zrl->zr_owner, ==, curthread); |
572e2857 BB |
151 | zrl->zr_owner = NULL; |
152 | membar_producer(); /* make sure the owner store happens first */ | |
153 | #endif | |
154 | zrl->zr_refcount = 0; | |
155 | cv_broadcast(&zrl->zr_cv); | |
156 | mutex_exit(&zrl->zr_mtx); | |
157 | } | |
158 | ||
159 | int | |
160 | zrl_refcount(zrlock_t *zrl) | |
161 | { | |
8dd86a10 | 162 | ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED); |
572e2857 | 163 | |
1c27024e | 164 | int n = (int)zrl->zr_refcount; |
572e2857 BB |
165 | return (n <= 0 ? 0 : n); |
166 | } | |
167 | ||
168 | int | |
169 | zrl_is_zero(zrlock_t *zrl) | |
170 | { | |
8dd86a10 | 171 | ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED); |
572e2857 BB |
172 | |
173 | return (zrl->zr_refcount <= 0); | |
174 | } | |
175 | ||
176 | int | |
177 | zrl_is_locked(zrlock_t *zrl) | |
178 | { | |
8dd86a10 | 179 | ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED); |
572e2857 BB |
180 | |
181 | return (zrl->zr_refcount == ZRL_LOCKED); | |
182 | } | |
183 | ||
184 | #ifdef ZFS_DEBUG | |
185 | kthread_t * | |
186 | zrl_owner(zrlock_t *zrl) | |
187 | { | |
188 | return (zrl->zr_owner); | |
189 | } | |
190 | #endif | |
368f4c10 | 191 | |
93ce2b4c | 192 | #if defined(_KERNEL) |
368f4c10 | 193 | |
e9aa730c | 194 | EXPORT_SYMBOL(zrl_add_impl); |
368f4c10 RC |
195 | EXPORT_SYMBOL(zrl_remove); |
196 | ||
197 | #endif |