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