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
exis_init(exislock_t * xlk)49 exis_init(exislock_t *xlk)
50 {
51 xlk->pseudo_ticks = 0;
52 }
53
54 static __inline void
exis_setlive(exislock_t * xlk)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
exis_hold_gd(globaldata_t gd)64 exis_hold_gd(globaldata_t gd)
65 {
66 ++gd->gd_exislockcnt;
67 }
68
69 static __inline void
exis_drop_gd(globaldata_t gd)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
exis_hold(void)77 exis_hold(void)
78 {
79 exis_hold_gd(mycpu);
80 }
81
82 static __inline void
exis_drop(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
exis_poll(exislock_t * xlk)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
exis_state(exislock_t * xlk)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
exis_usable(exislock_t * xlk)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
exis_freeable(exislock_t * xlk)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
exis_cache(exislock_t * xlk,long n)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
exis_terminate(exislock_t * xlk)207 exis_terminate(exislock_t *xlk)
208 {
209 xlk->pseudo_ticks = pseudo_ticks - 1;
210 }
211
212 #endif /* !_SYS_EXISLOCK2_H_ */
213