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