xref: /freebsd/sys/contrib/openzfs/module/zfs/zrlock.c (revision bb2d13b686e3ccf6c3ccb36209dfb7dcc108b182)
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