1*1fb62fb0SOlivier Houchard /*
2*1fb62fb0SOlivier Houchard * Copyright 2013-2015 Samy Al Bahra.
3*1fb62fb0SOlivier Houchard * All rights reserved.
4*1fb62fb0SOlivier Houchard *
5*1fb62fb0SOlivier Houchard * Redistribution and use in source and binary forms, with or without
6*1fb62fb0SOlivier Houchard * modification, are permitted provided that the following conditions
7*1fb62fb0SOlivier Houchard * are met:
8*1fb62fb0SOlivier Houchard * 1. Redistributions of source code must retain the above copyright
9*1fb62fb0SOlivier Houchard * notice, this list of conditions and the following disclaimer.
10*1fb62fb0SOlivier Houchard * 2. Redistributions in binary form must reproduce the above copyright
11*1fb62fb0SOlivier Houchard * notice, this list of conditions and the following disclaimer in the
12*1fb62fb0SOlivier Houchard * documentation and/or other materials provided with the distribution.
13*1fb62fb0SOlivier Houchard *
14*1fb62fb0SOlivier Houchard * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*1fb62fb0SOlivier Houchard * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*1fb62fb0SOlivier Houchard * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*1fb62fb0SOlivier Houchard * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*1fb62fb0SOlivier Houchard * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*1fb62fb0SOlivier Houchard * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*1fb62fb0SOlivier Houchard * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*1fb62fb0SOlivier Houchard * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*1fb62fb0SOlivier Houchard * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*1fb62fb0SOlivier Houchard * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*1fb62fb0SOlivier Houchard * SUCH DAMAGE.
25*1fb62fb0SOlivier Houchard */
26*1fb62fb0SOlivier Houchard
27*1fb62fb0SOlivier Houchard #ifndef CK_ELIDE_H
28*1fb62fb0SOlivier Houchard #define CK_ELIDE_H
29*1fb62fb0SOlivier Houchard
30*1fb62fb0SOlivier Houchard /*
31*1fb62fb0SOlivier Houchard * As RTM is currently only supported on TSO x86 architectures,
32*1fb62fb0SOlivier Houchard * fences have been omitted. They will be necessary for other
33*1fb62fb0SOlivier Houchard * non-TSO architectures with TM support.
34*1fb62fb0SOlivier Houchard */
35*1fb62fb0SOlivier Houchard
36*1fb62fb0SOlivier Houchard #include <ck_cc.h>
37*1fb62fb0SOlivier Houchard #include <ck_pr.h>
38*1fb62fb0SOlivier Houchard #include <ck_string.h>
39*1fb62fb0SOlivier Houchard
40*1fb62fb0SOlivier Houchard /*
41*1fb62fb0SOlivier Houchard * skip_-prefixed counters represent the number of consecutive
42*1fb62fb0SOlivier Houchard * elisions to forfeit. retry_-prefixed counters represent the
43*1fb62fb0SOlivier Houchard * number of elision retries to attempt before forfeit.
44*1fb62fb0SOlivier Houchard *
45*1fb62fb0SOlivier Houchard * _busy: Lock was busy
46*1fb62fb0SOlivier Houchard * _other: Unknown explicit abort
47*1fb62fb0SOlivier Houchard * _conflict: Data conflict in elision section
48*1fb62fb0SOlivier Houchard */
49*1fb62fb0SOlivier Houchard struct ck_elide_config {
50*1fb62fb0SOlivier Houchard unsigned short skip_busy;
51*1fb62fb0SOlivier Houchard short retry_busy;
52*1fb62fb0SOlivier Houchard unsigned short skip_other;
53*1fb62fb0SOlivier Houchard short retry_other;
54*1fb62fb0SOlivier Houchard unsigned short skip_conflict;
55*1fb62fb0SOlivier Houchard short retry_conflict;
56*1fb62fb0SOlivier Houchard };
57*1fb62fb0SOlivier Houchard
58*1fb62fb0SOlivier Houchard #define CK_ELIDE_CONFIG_DEFAULT_INITIALIZER { \
59*1fb62fb0SOlivier Houchard .skip_busy = 5, \
60*1fb62fb0SOlivier Houchard .retry_busy = 256, \
61*1fb62fb0SOlivier Houchard .skip_other = 3, \
62*1fb62fb0SOlivier Houchard .retry_other = 3, \
63*1fb62fb0SOlivier Houchard .skip_conflict = 2, \
64*1fb62fb0SOlivier Houchard .retry_conflict = 5 \
65*1fb62fb0SOlivier Houchard }
66*1fb62fb0SOlivier Houchard
67*1fb62fb0SOlivier Houchard struct ck_elide_stat {
68*1fb62fb0SOlivier Houchard unsigned int n_fallback;
69*1fb62fb0SOlivier Houchard unsigned int n_elide;
70*1fb62fb0SOlivier Houchard unsigned short skip;
71*1fb62fb0SOlivier Houchard };
72*1fb62fb0SOlivier Houchard typedef struct ck_elide_stat ck_elide_stat_t;
73*1fb62fb0SOlivier Houchard
74*1fb62fb0SOlivier Houchard #define CK_ELIDE_STAT_INITIALIZER { 0, 0, 0 }
75*1fb62fb0SOlivier Houchard
76*1fb62fb0SOlivier Houchard CK_CC_INLINE static void
ck_elide_stat_init(ck_elide_stat_t * st)77*1fb62fb0SOlivier Houchard ck_elide_stat_init(ck_elide_stat_t *st)
78*1fb62fb0SOlivier Houchard {
79*1fb62fb0SOlivier Houchard
80*1fb62fb0SOlivier Houchard memset(st, 0, sizeof(*st));
81*1fb62fb0SOlivier Houchard return;
82*1fb62fb0SOlivier Houchard }
83*1fb62fb0SOlivier Houchard
84*1fb62fb0SOlivier Houchard #ifdef CK_F_PR_RTM
85*1fb62fb0SOlivier Houchard enum _ck_elide_hint {
86*1fb62fb0SOlivier Houchard CK_ELIDE_HINT_RETRY = 0,
87*1fb62fb0SOlivier Houchard CK_ELIDE_HINT_SPIN,
88*1fb62fb0SOlivier Houchard CK_ELIDE_HINT_STOP
89*1fb62fb0SOlivier Houchard };
90*1fb62fb0SOlivier Houchard
91*1fb62fb0SOlivier Houchard #define CK_ELIDE_LOCK_BUSY 0xFF
92*1fb62fb0SOlivier Houchard
93*1fb62fb0SOlivier Houchard static enum _ck_elide_hint
_ck_elide_fallback(int * retry,struct ck_elide_stat * st,struct ck_elide_config * c,unsigned int status)94*1fb62fb0SOlivier Houchard _ck_elide_fallback(int *retry,
95*1fb62fb0SOlivier Houchard struct ck_elide_stat *st,
96*1fb62fb0SOlivier Houchard struct ck_elide_config *c,
97*1fb62fb0SOlivier Houchard unsigned int status)
98*1fb62fb0SOlivier Houchard {
99*1fb62fb0SOlivier Houchard
100*1fb62fb0SOlivier Houchard st->n_fallback++;
101*1fb62fb0SOlivier Houchard if (*retry > 0)
102*1fb62fb0SOlivier Houchard return CK_ELIDE_HINT_RETRY;
103*1fb62fb0SOlivier Houchard
104*1fb62fb0SOlivier Houchard if (st->skip != 0)
105*1fb62fb0SOlivier Houchard return CK_ELIDE_HINT_STOP;
106*1fb62fb0SOlivier Houchard
107*1fb62fb0SOlivier Houchard if (status & CK_PR_RTM_EXPLICIT) {
108*1fb62fb0SOlivier Houchard if (CK_PR_RTM_CODE(status) == CK_ELIDE_LOCK_BUSY) {
109*1fb62fb0SOlivier Houchard st->skip = c->skip_busy;
110*1fb62fb0SOlivier Houchard *retry = c->retry_busy;
111*1fb62fb0SOlivier Houchard return CK_ELIDE_HINT_SPIN;
112*1fb62fb0SOlivier Houchard }
113*1fb62fb0SOlivier Houchard
114*1fb62fb0SOlivier Houchard st->skip = c->skip_other;
115*1fb62fb0SOlivier Houchard return CK_ELIDE_HINT_STOP;
116*1fb62fb0SOlivier Houchard }
117*1fb62fb0SOlivier Houchard
118*1fb62fb0SOlivier Houchard if ((status & CK_PR_RTM_RETRY) &&
119*1fb62fb0SOlivier Houchard (status & CK_PR_RTM_CONFLICT)) {
120*1fb62fb0SOlivier Houchard st->skip = c->skip_conflict;
121*1fb62fb0SOlivier Houchard *retry = c->retry_conflict;
122*1fb62fb0SOlivier Houchard return CK_ELIDE_HINT_RETRY;
123*1fb62fb0SOlivier Houchard }
124*1fb62fb0SOlivier Houchard
125*1fb62fb0SOlivier Houchard /*
126*1fb62fb0SOlivier Houchard * Capacity, debug and nesting abortions are likely to be
127*1fb62fb0SOlivier Houchard * invariant conditions for the acquisition, execute regular
128*1fb62fb0SOlivier Houchard * path instead. If retry bit is not set, then take the hint.
129*1fb62fb0SOlivier Houchard */
130*1fb62fb0SOlivier Houchard st->skip = USHRT_MAX;
131*1fb62fb0SOlivier Houchard return CK_ELIDE_HINT_STOP;
132*1fb62fb0SOlivier Houchard }
133*1fb62fb0SOlivier Houchard
134*1fb62fb0SOlivier Houchard /*
135*1fb62fb0SOlivier Houchard * Defines an elision implementation according to the following variables:
136*1fb62fb0SOlivier Houchard * N - Namespace of elision implementation.
137*1fb62fb0SOlivier Houchard * T - Typename of mutex.
138*1fb62fb0SOlivier Houchard * L_P - Lock predicate, returns false if resource is available.
139*1fb62fb0SOlivier Houchard * L - Function to call if resource is unavailable of transaction aborts.
140*1fb62fb0SOlivier Houchard * U_P - Unlock predicate, returns false if elision failed.
141*1fb62fb0SOlivier Houchard * U - Function to call if transaction failed.
142*1fb62fb0SOlivier Houchard */
143*1fb62fb0SOlivier Houchard #define CK_ELIDE_PROTOTYPE(N, T, L_P, L, U_P, U) \
144*1fb62fb0SOlivier Houchard CK_CC_INLINE static void \
145*1fb62fb0SOlivier Houchard ck_elide_##N##_lock_adaptive(T *lock, \
146*1fb62fb0SOlivier Houchard struct ck_elide_stat *st, \
147*1fb62fb0SOlivier Houchard struct ck_elide_config *c) \
148*1fb62fb0SOlivier Houchard { \
149*1fb62fb0SOlivier Houchard enum _ck_elide_hint hint; \
150*1fb62fb0SOlivier Houchard int retry; \
151*1fb62fb0SOlivier Houchard \
152*1fb62fb0SOlivier Houchard if (CK_CC_UNLIKELY(st->skip != 0)) { \
153*1fb62fb0SOlivier Houchard st->skip--; \
154*1fb62fb0SOlivier Houchard goto acquire; \
155*1fb62fb0SOlivier Houchard } \
156*1fb62fb0SOlivier Houchard \
157*1fb62fb0SOlivier Houchard retry = c->retry_conflict; \
158*1fb62fb0SOlivier Houchard do { \
159*1fb62fb0SOlivier Houchard unsigned int status = ck_pr_rtm_begin(); \
160*1fb62fb0SOlivier Houchard if (status == CK_PR_RTM_STARTED) { \
161*1fb62fb0SOlivier Houchard if (L_P(lock) == true) \
162*1fb62fb0SOlivier Houchard ck_pr_rtm_abort(CK_ELIDE_LOCK_BUSY); \
163*1fb62fb0SOlivier Houchard \
164*1fb62fb0SOlivier Houchard return; \
165*1fb62fb0SOlivier Houchard } \
166*1fb62fb0SOlivier Houchard \
167*1fb62fb0SOlivier Houchard hint = _ck_elide_fallback(&retry, st, c, status); \
168*1fb62fb0SOlivier Houchard if (hint == CK_ELIDE_HINT_RETRY) \
169*1fb62fb0SOlivier Houchard continue; \
170*1fb62fb0SOlivier Houchard \
171*1fb62fb0SOlivier Houchard if (hint == CK_ELIDE_HINT_SPIN) { \
172*1fb62fb0SOlivier Houchard while (--retry != 0) { \
173*1fb62fb0SOlivier Houchard if (L_P(lock) == false) \
174*1fb62fb0SOlivier Houchard break; \
175*1fb62fb0SOlivier Houchard \
176*1fb62fb0SOlivier Houchard ck_pr_stall(); \
177*1fb62fb0SOlivier Houchard } \
178*1fb62fb0SOlivier Houchard \
179*1fb62fb0SOlivier Houchard continue; \
180*1fb62fb0SOlivier Houchard } \
181*1fb62fb0SOlivier Houchard \
182*1fb62fb0SOlivier Houchard if (hint == CK_ELIDE_HINT_STOP) \
183*1fb62fb0SOlivier Houchard break; \
184*1fb62fb0SOlivier Houchard } while (CK_CC_LIKELY(--retry > 0)); \
185*1fb62fb0SOlivier Houchard \
186*1fb62fb0SOlivier Houchard acquire: \
187*1fb62fb0SOlivier Houchard L(lock); \
188*1fb62fb0SOlivier Houchard return; \
189*1fb62fb0SOlivier Houchard } \
190*1fb62fb0SOlivier Houchard CK_CC_INLINE static void \
191*1fb62fb0SOlivier Houchard ck_elide_##N##_unlock_adaptive(struct ck_elide_stat *st, T *lock) \
192*1fb62fb0SOlivier Houchard { \
193*1fb62fb0SOlivier Houchard \
194*1fb62fb0SOlivier Houchard if (U_P(lock) == false) { \
195*1fb62fb0SOlivier Houchard ck_pr_rtm_end(); \
196*1fb62fb0SOlivier Houchard st->skip = 0; \
197*1fb62fb0SOlivier Houchard st->n_elide++; \
198*1fb62fb0SOlivier Houchard } else { \
199*1fb62fb0SOlivier Houchard U(lock); \
200*1fb62fb0SOlivier Houchard } \
201*1fb62fb0SOlivier Houchard \
202*1fb62fb0SOlivier Houchard return; \
203*1fb62fb0SOlivier Houchard } \
204*1fb62fb0SOlivier Houchard CK_CC_INLINE static void \
205*1fb62fb0SOlivier Houchard ck_elide_##N##_lock(T *lock) \
206*1fb62fb0SOlivier Houchard { \
207*1fb62fb0SOlivier Houchard \
208*1fb62fb0SOlivier Houchard if (ck_pr_rtm_begin() != CK_PR_RTM_STARTED) { \
209*1fb62fb0SOlivier Houchard L(lock); \
210*1fb62fb0SOlivier Houchard return; \
211*1fb62fb0SOlivier Houchard } \
212*1fb62fb0SOlivier Houchard \
213*1fb62fb0SOlivier Houchard if (L_P(lock) == true) \
214*1fb62fb0SOlivier Houchard ck_pr_rtm_abort(CK_ELIDE_LOCK_BUSY); \
215*1fb62fb0SOlivier Houchard \
216*1fb62fb0SOlivier Houchard return; \
217*1fb62fb0SOlivier Houchard } \
218*1fb62fb0SOlivier Houchard CK_CC_INLINE static void \
219*1fb62fb0SOlivier Houchard ck_elide_##N##_unlock(T *lock) \
220*1fb62fb0SOlivier Houchard { \
221*1fb62fb0SOlivier Houchard \
222*1fb62fb0SOlivier Houchard if (U_P(lock) == false) { \
223*1fb62fb0SOlivier Houchard ck_pr_rtm_end(); \
224*1fb62fb0SOlivier Houchard } else { \
225*1fb62fb0SOlivier Houchard U(lock); \
226*1fb62fb0SOlivier Houchard } \
227*1fb62fb0SOlivier Houchard \
228*1fb62fb0SOlivier Houchard return; \
229*1fb62fb0SOlivier Houchard }
230*1fb62fb0SOlivier Houchard
231*1fb62fb0SOlivier Houchard #define CK_ELIDE_TRYLOCK_PROTOTYPE(N, T, TL_P, TL) \
232*1fb62fb0SOlivier Houchard CK_CC_INLINE static bool \
233*1fb62fb0SOlivier Houchard ck_elide_##N##_trylock(T *lock) \
234*1fb62fb0SOlivier Houchard { \
235*1fb62fb0SOlivier Houchard \
236*1fb62fb0SOlivier Houchard if (ck_pr_rtm_begin() != CK_PR_RTM_STARTED) \
237*1fb62fb0SOlivier Houchard return false; \
238*1fb62fb0SOlivier Houchard \
239*1fb62fb0SOlivier Houchard if (TL_P(lock) == true) \
240*1fb62fb0SOlivier Houchard ck_pr_rtm_abort(CK_ELIDE_LOCK_BUSY); \
241*1fb62fb0SOlivier Houchard \
242*1fb62fb0SOlivier Houchard return true; \
243*1fb62fb0SOlivier Houchard }
244*1fb62fb0SOlivier Houchard #else
245*1fb62fb0SOlivier Houchard /*
246*1fb62fb0SOlivier Houchard * If RTM is not enabled on the target platform (CK_F_PR_RTM) then these
247*1fb62fb0SOlivier Houchard * elision wrappers directly calls into the user-specified lock operations.
248*1fb62fb0SOlivier Houchard * Unfortunately, the storage cost of both ck_elide_config and ck_elide_stat
249*1fb62fb0SOlivier Houchard * are paid (typically a storage cost that is a function of lock objects and
250*1fb62fb0SOlivier Houchard * thread count).
251*1fb62fb0SOlivier Houchard */
252*1fb62fb0SOlivier Houchard #define CK_ELIDE_PROTOTYPE(N, T, L_P, L, U_P, U) \
253*1fb62fb0SOlivier Houchard CK_CC_INLINE static void \
254*1fb62fb0SOlivier Houchard ck_elide_##N##_lock_adaptive(T *lock, \
255*1fb62fb0SOlivier Houchard struct ck_elide_stat *st, \
256*1fb62fb0SOlivier Houchard struct ck_elide_config *c) \
257*1fb62fb0SOlivier Houchard { \
258*1fb62fb0SOlivier Houchard \
259*1fb62fb0SOlivier Houchard (void)st; \
260*1fb62fb0SOlivier Houchard (void)c; \
261*1fb62fb0SOlivier Houchard L(lock); \
262*1fb62fb0SOlivier Houchard return; \
263*1fb62fb0SOlivier Houchard } \
264*1fb62fb0SOlivier Houchard CK_CC_INLINE static void \
265*1fb62fb0SOlivier Houchard ck_elide_##N##_unlock_adaptive(struct ck_elide_stat *st, \
266*1fb62fb0SOlivier Houchard T *lock) \
267*1fb62fb0SOlivier Houchard { \
268*1fb62fb0SOlivier Houchard \
269*1fb62fb0SOlivier Houchard (void)st; \
270*1fb62fb0SOlivier Houchard U(lock); \
271*1fb62fb0SOlivier Houchard return; \
272*1fb62fb0SOlivier Houchard } \
273*1fb62fb0SOlivier Houchard CK_CC_INLINE static void \
274*1fb62fb0SOlivier Houchard ck_elide_##N##_lock(T *lock) \
275*1fb62fb0SOlivier Houchard { \
276*1fb62fb0SOlivier Houchard \
277*1fb62fb0SOlivier Houchard L(lock); \
278*1fb62fb0SOlivier Houchard return; \
279*1fb62fb0SOlivier Houchard } \
280*1fb62fb0SOlivier Houchard CK_CC_INLINE static void \
281*1fb62fb0SOlivier Houchard ck_elide_##N##_unlock(T *lock) \
282*1fb62fb0SOlivier Houchard { \
283*1fb62fb0SOlivier Houchard \
284*1fb62fb0SOlivier Houchard U(lock); \
285*1fb62fb0SOlivier Houchard return; \
286*1fb62fb0SOlivier Houchard }
287*1fb62fb0SOlivier Houchard
288*1fb62fb0SOlivier Houchard #define CK_ELIDE_TRYLOCK_PROTOTYPE(N, T, TL_P, TL) \
289*1fb62fb0SOlivier Houchard CK_CC_INLINE static bool \
290*1fb62fb0SOlivier Houchard ck_elide_##N##_trylock(T *lock) \
291*1fb62fb0SOlivier Houchard { \
292*1fb62fb0SOlivier Houchard \
293*1fb62fb0SOlivier Houchard return TL_P(lock); \
294*1fb62fb0SOlivier Houchard }
295*1fb62fb0SOlivier Houchard #endif /* !CK_F_PR_RTM */
296*1fb62fb0SOlivier Houchard
297*1fb62fb0SOlivier Houchard /*
298*1fb62fb0SOlivier Houchard * Best-effort elision lock operations. First argument is name (N)
299*1fb62fb0SOlivier Houchard * associated with implementation and the second is a pointer to
300*1fb62fb0SOlivier Houchard * the type specified above (T).
301*1fb62fb0SOlivier Houchard *
302*1fb62fb0SOlivier Houchard * Unlike the adaptive variant, this interface does not have any retry
303*1fb62fb0SOlivier Houchard * semantics. In environments where jitter is low, this may yield a tighter
304*1fb62fb0SOlivier Houchard * fast path.
305*1fb62fb0SOlivier Houchard */
306*1fb62fb0SOlivier Houchard #define CK_ELIDE_LOCK(NAME, LOCK) ck_elide_##NAME##_lock(LOCK)
307*1fb62fb0SOlivier Houchard #define CK_ELIDE_UNLOCK(NAME, LOCK) ck_elide_##NAME##_unlock(LOCK)
308*1fb62fb0SOlivier Houchard #define CK_ELIDE_TRYLOCK(NAME, LOCK) ck_elide_##NAME##_trylock(LOCK)
309*1fb62fb0SOlivier Houchard
310*1fb62fb0SOlivier Houchard /*
311*1fb62fb0SOlivier Houchard * Adaptive elision lock operations. In addition to name and pointer
312*1fb62fb0SOlivier Houchard * to the lock, you must pass in a pointer to an initialized
313*1fb62fb0SOlivier Houchard * ck_elide_config structure along with a per-thread stat structure.
314*1fb62fb0SOlivier Houchard */
315*1fb62fb0SOlivier Houchard #define CK_ELIDE_LOCK_ADAPTIVE(NAME, STAT, CONFIG, LOCK) \
316*1fb62fb0SOlivier Houchard ck_elide_##NAME##_lock_adaptive(LOCK, STAT, CONFIG)
317*1fb62fb0SOlivier Houchard
318*1fb62fb0SOlivier Houchard #define CK_ELIDE_UNLOCK_ADAPTIVE(NAME, STAT, LOCK) \
319*1fb62fb0SOlivier Houchard ck_elide_##NAME##_unlock_adaptive(STAT, LOCK)
320*1fb62fb0SOlivier Houchard
321*1fb62fb0SOlivier Houchard #endif /* CK_ELIDE_H */
322