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