xref: /dragonfly/sys/sys/exislock2.h (revision 631c21f2)
1 /*
2  * Copyright (c) 2020 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 #ifndef _SYS_EXISLOCK2_H_
35 #define	_SYS_EXISLOCK2_H_
36 
37 #ifndef _SYS_GLOBALDATA_H_
38 #include <sys/globaldata.h>
39 #endif
40 #ifndef _MACHINE_THREAD_H_
41 #include <machine/thread.h>
42 #endif
43 
44 /*
45  * Initialize the structure.  To reduce confusion we also have exis_setlive()
46  * when repurposing a structure, which does the same thing.
47  */
48 static __inline void
49 exis_init(exislock_t *xlk)
50 {
51 	xlk->pseudo_ticks = 0;
52 }
53 
54 static __inline void
55 exis_setlive(exislock_t *xlk)
56 {
57 	xlk->pseudo_ticks = 0;
58 }
59 
60 /*
61  * pcpu exis lock API.  Enter and and exit a type-safe critical section.
62  */
63 static __inline void
64 exis_hold_gd(globaldata_t gd)
65 {
66 	++gd->gd_exislockcnt;
67 }
68 
69 static __inline void
70 exis_drop_gd(globaldata_t gd)
71 {
72 	if (--gd->gd_exislockcnt == 0)
73 		gd->gd_exisarmed = 1;
74 }
75 
76 static __inline void
77 exis_hold(void)
78 {
79 	exis_hold_gd(mycpu);
80 }
81 
82 static __inline void
83 exis_drop(void)
84 {
85 	exis_drop_gd(mycpu);
86 }
87 
88 /*
89  * poll whether the object is usable or not.  A value >= 0 indicates that
90  * the (possibly cached) object is usable.
91  *
92  * This call returns the approximate number of pseudo_ticks remaining until
93  * the object becomes unusable, +/- one.
94  *
95  * The actual value returns is either >= 0, or a negative number.  Caller
96  * should refrain from trying to interpret values >= 0 other than the fact
97  * that they are >= 0.
98  *
99  * Negative numbers indicate the number of pseudo_ticks which have occurred
100  * since the object became unusable.  Various negative values trigger
101  * different actions.
102  */
103 static __inline long
104 exis_poll(exislock_t *xlk)
105 {
106 	long val = xlk->pseudo_ticks;
107 
108 	cpu_ccfence();
109 	if (val == 0)
110 		return val;
111 	return (val - pseudo_ticks);
112 }
113 
114 /*
115  * Return the current state.  Note that the NOTCACHED state persists for
116  * two pseudo_ticks.  This is done because the global pseudo_ticks counter
117  * can concurrently increment by 1 (but no more than 1) during a type-safe
118  * critical section.
119  *
120  * The state can transition even while holding a type-safe critical section,
121  * but sequencing is designed such that this does not cause any problems.
122  */
123 static __inline int
124 exis_state(exislock_t *xlk)
125 {
126 	long val = xlk->pseudo_ticks;
127 
128 	cpu_ccfence();
129 	if (val == 0)
130 		return EXIS_LIVE;
131 	val = val - pseudo_ticks;
132 	if (val >= 0)
133 		return EXIS_CACHED;
134 	if (val >= -2)
135 		return EXIS_NOTCACHED;
136 	return EXIS_TERMINATE;
137 }
138 
139 /*
140  * Returns non-zero if the structure is usable (either LIVE or CACHED).
141  *
142  * WARNING! The structure is not considered to be usable if it is in
143  *	    an UNCACHED state, but if it is CACHED and transitions to
144  *	    UNCACHED during a type-safe critical section it does remain
145  *	    usable for the duration of that type-safe critical section.
146  */
147 static __inline int
148 exis_usable(exislock_t *xlk)
149 {
150 	return (exis_poll(xlk) >= 0);
151 }
152 
153 /*
154  * Returns non-zero if the structure can be destroyed
155  */
156 static __inline int
157 exis_freeable(exislock_t *xlk)
158 {
159 	return (exis_poll(xlk) <= -2);
160 }
161 
162 /*
163  * If the structure is in a LIVE or CACHED state, or if it was CACHED and
164  * concurrently transitioned to NOTCACHED in the same type-safe critical
165  * section, the state will be reset to a CACHED(n) state and non-zero is
166  * returned.
167  *
168  * Otherwise 0 is returned and no action is taken.
169  */
170 static __inline int
171 exis_cache(exislock_t *xlk, long n)
172 {
173 	long val = xlk->pseudo_ticks;
174 	long pticks = pseudo_ticks;
175 
176 	cpu_ccfence();
177 	if (val)
178 		val = val - pticks;
179 	if (val >= -1) {
180 		/*
181 		 * avoid cache line ping-pong
182 		 */
183 		pticks += n + 1;
184 		if (xlk->pseudo_ticks != pticks) {
185 			cpu_ccfence();
186 			xlk->pseudo_ticks = pticks;
187 		}
188 		return 1;
189 	}
190 	return 0;
191 }
192 
193 /*
194  * The current state of the structure is ignored and the srtucture is
195  * placed in a CACHED(0) state.  It will automatically sequence through
196  * the NOTCACHED and TERMINATE states as psuedo_ticks increments.
197  *
198  * The NOTCACHED state is an indeterminant state, since the pseudo_ticks
199  * counter might already be armed for increment, it can increment at least
200  * once while code is inside an exis_hold().  The TERMINATE state occurs
201  * at the second tick.
202  *
203  * If the caller repurposes the structure, it is usually a good idea to
204  * place it back into a LIVE state by calling exis_setlive().
205  */
206 static __inline void
207 exis_terminate(exislock_t *xlk)
208 {
209 	xlk->pseudo_ticks = pseudo_ticks - 1;
210 }
211 
212 #endif /* !_SYS_EXISLOCK2_H_ */
213