]>
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 GM |
84 | if (zrl->zr_owner == curthread) { |
85 | DTRACE_PROBE2(zrlock__reentry, | |
86 | zrlock_t *, zrl, uint32_t, n); | |
87 | } | |
88 | zrl->zr_owner = curthread; | |
89 | zrl->zr_caller = zc; | |
572e2857 | 90 | #endif |
cffd6e11 GM |
91 | return; |
92 | } | |
93 | n = cas; | |
572e2857 | 94 | } |
572e2857 | 95 | |
cffd6e11 GM |
96 | mutex_enter(&zrl->zr_mtx); |
97 | while (zrl->zr_refcount == ZRL_LOCKED) { | |
98 | cv_wait(&zrl->zr_cv, &zrl->zr_mtx); | |
99 | } | |
100 | mutex_exit(&zrl->zr_mtx); | |
572e2857 | 101 | } |
572e2857 BB |
102 | } |
103 | ||
104 | void | |
105 | zrl_remove(zrlock_t *zrl) | |
106 | { | |
107 | uint32_t n; | |
108 | ||
572e2857 BB |
109 | #ifdef ZFS_DEBUG |
110 | if (zrl->zr_owner == curthread) { | |
111 | zrl->zr_owner = NULL; | |
112 | zrl->zr_caller = NULL; | |
113 | } | |
114 | #endif | |
8dd86a10 MA |
115 | n = atomic_dec_32_nv((uint32_t *)&zrl->zr_refcount); |
116 | ASSERT3S((int32_t)n, >=, 0); | |
572e2857 BB |
117 | } |
118 | ||
119 | int | |
120 | zrl_tryenter(zrlock_t *zrl) | |
121 | { | |
122 | uint32_t n = (uint32_t)zrl->zr_refcount; | |
123 | ||
124 | if (n == 0) { | |
125 | uint32_t cas = atomic_cas_32( | |
126 | (uint32_t *)&zrl->zr_refcount, 0, ZRL_LOCKED); | |
127 | if (cas == 0) { | |
128 | #ifdef ZFS_DEBUG | |
8dd86a10 | 129 | ASSERT3P(zrl->zr_owner, ==, NULL); |
572e2857 BB |
130 | zrl->zr_owner = curthread; |
131 | #endif | |
132 | return (1); | |
133 | } | |
134 | } | |
135 | ||
8dd86a10 | 136 | ASSERT3S((int32_t)n, >, ZRL_DESTROYED); |
572e2857 BB |
137 | |
138 | return (0); | |
139 | } | |
140 | ||
141 | void | |
142 | zrl_exit(zrlock_t *zrl) | |
143 | { | |
8dd86a10 | 144 | ASSERT3S(zrl->zr_refcount, ==, ZRL_LOCKED); |
572e2857 BB |
145 | |
146 | mutex_enter(&zrl->zr_mtx); | |
147 | #ifdef ZFS_DEBUG | |
8dd86a10 | 148 | ASSERT3P(zrl->zr_owner, ==, curthread); |
572e2857 BB |
149 | zrl->zr_owner = NULL; |
150 | membar_producer(); /* make sure the owner store happens first */ | |
151 | #endif | |
152 | zrl->zr_refcount = 0; | |
153 | cv_broadcast(&zrl->zr_cv); | |
154 | mutex_exit(&zrl->zr_mtx); | |
155 | } | |
156 | ||
157 | int | |
158 | zrl_refcount(zrlock_t *zrl) | |
159 | { | |
8dd86a10 | 160 | ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED); |
572e2857 | 161 | |
1c27024e | 162 | int n = (int)zrl->zr_refcount; |
572e2857 BB |
163 | return (n <= 0 ? 0 : n); |
164 | } | |
165 | ||
166 | int | |
167 | zrl_is_zero(zrlock_t *zrl) | |
168 | { | |
8dd86a10 | 169 | ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED); |
572e2857 BB |
170 | |
171 | return (zrl->zr_refcount <= 0); | |
172 | } | |
173 | ||
174 | int | |
175 | zrl_is_locked(zrlock_t *zrl) | |
176 | { | |
8dd86a10 | 177 | ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED); |
572e2857 BB |
178 | |
179 | return (zrl->zr_refcount == ZRL_LOCKED); | |
180 | } | |
181 | ||
182 | #ifdef ZFS_DEBUG | |
183 | kthread_t * | |
184 | zrl_owner(zrlock_t *zrl) | |
185 | { | |
186 | return (zrl->zr_owner); | |
187 | } | |
188 | #endif | |
368f4c10 RC |
189 | |
190 | #if defined(_KERNEL) && defined(HAVE_SPL) | |
191 | ||
e9aa730c | 192 | EXPORT_SYMBOL(zrl_add_impl); |
368f4c10 RC |
193 | EXPORT_SYMBOL(zrl_remove); |
194 | ||
195 | #endif |