Print this page
3746 ZRLs are racy
Reviewed by: Boris Protopopov <bprotopopov@hotmail.com>
Reviewed by: Pavel Zakharov <pavel.zakha@gmail.com>
Reviewed by: Yuri Pankov <yuri.pankov@gmail.com>
Reviewed by: Justin T. Gibbs <gibbs@scsiguy.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/fs/zfs/zrlock.c
+++ new/usr/src/uts/common/fs/zfs/zrlock.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
↓ open down ↓ |
13 lines elided |
↑ open up ↑ |
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 23 * Copyright (c) 2014, 2015 by Delphix. All rights reserved.
24 + * Copyright 2016 Spectra Logic Corporation. All rights reserved.
25 + * Copyright 2016 The MathWorks, Inc. All rights reserved.
24 26 */
25 27
26 28 /*
27 29 * A Zero Reference Lock (ZRL) is a reference count that can lock out new
28 30 * references only when the count is zero and only without waiting if the count
29 31 * is not already zero. It is similar to a read-write lock in that it allows
30 32 * multiple readers and only a single writer, but it does not allow a writer to
31 33 * block while waiting for readers to exit, and therefore the question of
32 34 * reader/writer priority is moot (no WRWANT bit). Since the equivalent of
33 35 * rw_enter(&lock, RW_WRITER) is disallowed and only tryenter() is allowed, it
34 36 * is perfectly safe for the same reader to acquire the same lock multiple
35 37 * times. The fact that a ZRL is reentrant for readers (through multiple calls
36 38 * to zrl_add()) makes it convenient for determining whether something is
37 39 * actively referenced without the fuss of flagging lock ownership across
38 40 * function calls.
39 41 */
40 42 #include <sys/zrlock.h>
41 43
42 44 /*
43 45 * A ZRL can be locked only while there are zero references, so ZRL_LOCKED is
44 46 * treated as zero references.
45 47 */
46 48 #define ZRL_LOCKED -1
47 49 #define ZRL_DESTROYED -2
48 50
49 51 void
50 52 zrl_init(zrlock_t *zrl)
51 53 {
52 54 mutex_init(&zrl->zr_mtx, NULL, MUTEX_DEFAULT, NULL);
53 55 zrl->zr_refcount = 0;
54 56 cv_init(&zrl->zr_cv, NULL, CV_DEFAULT, NULL);
55 57 #ifdef ZFS_DEBUG
56 58 zrl->zr_owner = NULL;
57 59 zrl->zr_caller = NULL;
58 60 #endif
59 61 }
60 62
61 63 void
62 64 zrl_destroy(zrlock_t *zrl)
63 65 {
↓ open down ↓ |
30 lines elided |
↑ open up ↑ |
64 66 ASSERT0(zrl->zr_refcount);
65 67
66 68 mutex_destroy(&zrl->zr_mtx);
67 69 zrl->zr_refcount = ZRL_DESTROYED;
68 70 cv_destroy(&zrl->zr_cv);
69 71 }
70 72
71 73 void
72 74 zrl_add_impl(zrlock_t *zrl, const char *zc)
73 75 {
74 - uint32_t n = (uint32_t)zrl->zr_refcount;
76 + for (;;) {
77 + uint32_t n = (uint32_t)zrl->zr_refcount;
75 78
76 - while (n != ZRL_LOCKED) {
77 - uint32_t cas = atomic_cas_32(
78 - (uint32_t *)&zrl->zr_refcount, n, n + 1);
79 - if (cas == n) {
80 - ASSERT3S((int32_t)n, >=, 0);
79 + while (n != ZRL_LOCKED) {
80 + uint32_t cas = atomic_cas_32(
81 + (uint32_t *)&zrl->zr_refcount, n, n + 1);
82 +
83 + if (cas == n) {
84 + ASSERT3S((int32_t)n, >=, 0);
81 85 #ifdef ZFS_DEBUG
82 - if (zrl->zr_owner == curthread) {
83 - DTRACE_PROBE2(zrlock__reentry,
84 - zrlock_t *, zrl, uint32_t, n);
85 - }
86 - zrl->zr_owner = curthread;
87 - zrl->zr_caller = zc;
86 + if (zrl->zr_owner == curthread) {
87 + DTRACE_PROBE2(zrlock__reentry,
88 + zrlock_t *, zrl, uint32_t, n);
89 + }
90 + zrl->zr_owner = curthread;
91 + zrl->zr_caller = zc;
88 92 #endif
89 - return;
93 + return;
94 + }
95 + n = cas;
90 96 }
91 - n = cas;
92 - }
93 97
94 - mutex_enter(&zrl->zr_mtx);
95 - while (zrl->zr_refcount == ZRL_LOCKED) {
96 - cv_wait(&zrl->zr_cv, &zrl->zr_mtx);
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);
97 103 }
98 - ASSERT3S(zrl->zr_refcount, >=, 0);
99 - zrl->zr_refcount++;
100 -#ifdef ZFS_DEBUG
101 - zrl->zr_owner = curthread;
102 - zrl->zr_caller = zc;
103 -#endif
104 - mutex_exit(&zrl->zr_mtx);
105 104 }
106 105
107 106 void
108 107 zrl_remove(zrlock_t *zrl)
109 108 {
110 109 uint32_t n;
111 110
112 111 #ifdef ZFS_DEBUG
113 112 if (zrl->zr_owner == curthread) {
114 113 zrl->zr_owner = NULL;
115 114 zrl->zr_caller = NULL;
116 115 }
117 116 #endif
118 117 n = atomic_dec_32_nv((uint32_t *)&zrl->zr_refcount);
119 118 ASSERT3S((int32_t)n, >=, 0);
120 119 }
121 120
122 121 int
123 122 zrl_tryenter(zrlock_t *zrl)
124 123 {
125 124 uint32_t n = (uint32_t)zrl->zr_refcount;
126 125
127 126 if (n == 0) {
128 127 uint32_t cas = atomic_cas_32(
129 128 (uint32_t *)&zrl->zr_refcount, 0, ZRL_LOCKED);
130 129 if (cas == 0) {
131 130 #ifdef ZFS_DEBUG
132 131 ASSERT3P(zrl->zr_owner, ==, NULL);
133 132 zrl->zr_owner = curthread;
134 133 #endif
135 134 return (1);
136 135 }
137 136 }
138 137
139 138 ASSERT3S((int32_t)n, >, ZRL_DESTROYED);
140 139
141 140 return (0);
142 141 }
143 142
144 143 void
145 144 zrl_exit(zrlock_t *zrl)
146 145 {
147 146 ASSERT3S(zrl->zr_refcount, ==, ZRL_LOCKED);
148 147
149 148 mutex_enter(&zrl->zr_mtx);
150 149 #ifdef ZFS_DEBUG
151 150 ASSERT3P(zrl->zr_owner, ==, curthread);
152 151 zrl->zr_owner = NULL;
153 152 membar_producer(); /* make sure the owner store happens first */
154 153 #endif
155 154 zrl->zr_refcount = 0;
156 155 cv_broadcast(&zrl->zr_cv);
157 156 mutex_exit(&zrl->zr_mtx);
158 157 }
159 158
160 159 int
161 160 zrl_refcount(zrlock_t *zrl)
162 161 {
163 162 ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED);
164 163
165 164 int n = (int)zrl->zr_refcount;
166 165 return (n <= 0 ? 0 : n);
167 166 }
168 167
169 168 int
170 169 zrl_is_zero(zrlock_t *zrl)
171 170 {
172 171 ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED);
173 172
174 173 return (zrl->zr_refcount <= 0);
175 174 }
176 175
177 176 int
178 177 zrl_is_locked(zrlock_t *zrl)
179 178 {
180 179 ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED);
181 180
182 181 return (zrl->zr_refcount == ZRL_LOCKED);
183 182 }
184 183
185 184 #ifdef ZFS_DEBUG
186 185 kthread_t *
187 186 zrl_owner(zrlock_t *zrl)
188 187 {
189 188 return (zrl->zr_owner);
190 189 }
191 190 #endif
↓ open down ↓ |
77 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX