xref: /dragonfly/sys/sys/exislock2.h (revision c9c5aa9e)
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
46  */
47 static __inline void
48 exis_init(exislock_t *xlk)
49 {
50 	xlk->pseudo_ticks = 0;
51 }
52 
53 /*
54  * pcpu exis lock API.  Enter and and exit a type-safe critical section.
55  */
56 static __inline void
57 exis_hold_gd(globaldata_t gd)
58 {
59 	++gd->gd_exislockcnt;
60 }
61 
62 static __inline void
63 exis_drop_gd(globaldata_t gd)
64 {
65 	if (--gd->gd_exislockcnt == 0)
66 		gd->gd_exisarmed = 1;
67 }
68 
69 static __inline void
70 exis_hold(void)
71 {
72 	exis_hold_gd(mycpu);
73 }
74 
75 static __inline void
76 exis_drop(void)
77 {
78 	exis_drop_gd(mycpu);
79 }
80 
81 /*
82  * poll whether the object is usable or not.  A value >= 0 indicates that
83  * the (possibly cached) object is usable.
84  *
85  * This call returns the approximate number of pseudo_ticks remaining until
86  * the object becomes unusable, +/- one.
87  *
88  * The actual value returns is either >= 0, or a negative number.  Caller
89  * should refrain from trying to interpret values >= 0 other than the fact
90  * that they are >= 0.
91  *
92  * Negative numbers indicate the number of pseudo_ticks which have occurred
93  * since the object became unusable.  Various negative values trigger
94  * different actions.
95  */
96 static __inline long
97 exis_poll(exislock_t *xlk)
98 {
99 	long val = xlk->pseudo_ticks;
100 
101 	cpu_ccfence();
102 	if (val == 0)
103 		return val;
104 	return (val - pseudo_ticks);
105 }
106 
107 /*
108  * Return the current state.  Note that the NOTCACHED state persists for
109  * two pseudo_ticks.  This is done because the global pseudo_ticks counter
110  * can concurrently increment by 1 (but no more than 1) during a type-safe
111  * critical section.
112  *
113  * The state can transition even while holding a type-safe critical section,
114  * but sequencing is designed such that this does not cause any problems.
115  */
116 static __inline int
117 exis_state(exislock_t *xlk)
118 {
119 	long val = xlk->pseudo_ticks;
120 
121 	cpu_ccfence();
122 	if (val == 0)
123 		return EXIS_LIVE;
124 	val = val - pseudo_ticks;
125 	if (val >= 0)
126 		return EXIS_CACHED;
127 	if (val >= -2)
128 		return EXIS_NOTCACHED;
129 	return EXIS_TERMINATE;
130 }
131 
132 /*
133  * Returns non-zero if the structure is usable (either LIVE or CACHED).
134  *
135  * WARNING! The structure is not considered to be usable if it is in
136  *	    an UNCACHED state, but if it is CACHED and transitions to
137  *	    UNCACHED during a type-safe critical section it does remain
138  *	    usable for the duration of that type-safe critical section.
139  */
140 static __inline int
141 exis_usable(exislock_t *xlk)
142 {
143 	return (exis_poll(xlk) >= 0);
144 }
145 
146 /*
147  * Returns non-zero if the structure can be destroyed
148  */
149 static __inline int
150 exis_freeable(exislock_t *xlk)
151 {
152 	return (exis_poll(xlk) <= -2);
153 }
154 
155 /*
156  * If the structure is in a LIVE or CACHED state, or if it was CACHED and
157  * concurrently transitioned to NOTCACHED in the same type-safe critical
158  * section, the state will be reset to a CACHED(n) state and non-zero is
159  * returned.
160  *
161  * Otherwise 0 is returned and no action is taken.
162  */
163 static __inline int
164 exis_cache(exislock_t *xlk, long n)
165 {
166 	long val = xlk->pseudo_ticks;
167 	long pticks = pseudo_ticks;
168 
169 	cpu_ccfence();
170 	if (val)
171 		val = val - pticks;
172 	if (val >= -1) {
173 		/*
174 		 * avoid cache line ping-pong
175 		 */
176 		pticks += n + 1;
177 		if (xlk->pseudo_ticks != pticks) {
178 			cpu_ccfence();
179 			xlk->pseudo_ticks = pticks;
180 		}
181 		return 1;
182 	}
183 	return 0;
184 }
185 
186 /*
187  * Termination sequencing.
188  *
189  * The structure is placed in a CACHED(0) state if LIVE or CACHED.
190  * The NOTCACHED state should not be acted upon by the caller until
191  * and unless it transitions to TERMINATE.
192  *
193  * Upon returning EXIS_TERMINATE, the structure is returned to a
194  * NOTCACHED state and another 1-2 pseudo ticks will pass until it goes
195  * back to EXIS_TERMINATE (if needed by the caller).  Once the caller
196  * is fully satisfied, it may repurpose or destroy the structure.
197  *
198  * Caller should hold a strong interlock on the structure in addition
199  * to being in a type-safe critical section.
200  */
201 static __inline exis_state_t
202 exis_terminate(exislock_t *xlk)
203 {
204 	exis_state_t state;
205 
206 	state = exis_state(xlk);
207 	switch(state) {
208 	case EXIS_TERMINATE:
209 		/*
210 		 * Set to NOTCACHED state and return EXIS_TERMINATE.
211 		 * due to pseudo_ticks races, the NOTCACHED state will
212 		 * persist for 1-2 pseudo ticks.
213 		 */
214 		xlk->pseudo_ticks = pseudo_ticks - 1;
215 		state = EXIS_TERMINATE;
216 		break;
217 	case EXIS_NOTCACHED:
218 		break;
219 	case EXIS_CACHED:
220 	case EXIS_LIVE:
221 		xlk->pseudo_ticks = pseudo_ticks;
222 		break;
223 	}
224 	return state;
225 }
226 
227 #endif /* !_SYS_EXISLOCK2_H_ */
228