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