xref: /freebsd-src/sys/contrib/ck/src/ck_ec.c (revision 74e9b5f29ad0056bbe11a30c91dfa0705fa19cd5)
1*74e9b5f2SOlivier Houchard #include <ck_ec.h>
2*74e9b5f2SOlivier Houchard #include <ck_limits.h>
3*74e9b5f2SOlivier Houchard 
4*74e9b5f2SOlivier Houchard #include "ck_ec_timeutil.h"
5*74e9b5f2SOlivier Houchard 
6*74e9b5f2SOlivier Houchard #define DEFAULT_BUSY_LOOP_ITER 100U
7*74e9b5f2SOlivier Houchard 
8*74e9b5f2SOlivier Houchard /*
9*74e9b5f2SOlivier Houchard  * The 2ms, 8x/iter default parameter hit 1.024 seconds after 3
10*74e9b5f2SOlivier Houchard  * iterations.
11*74e9b5f2SOlivier Houchard  */
12*74e9b5f2SOlivier Houchard #define DEFAULT_INITIAL_WAIT_NS 2000000L  /* Start at 2 ms */
13*74e9b5f2SOlivier Houchard /* Grow the wait time 8x/iteration. */
14*74e9b5f2SOlivier Houchard #define DEFAULT_WAIT_SCALE_FACTOR 8
15*74e9b5f2SOlivier Houchard #define DEFAULT_WAIT_SHIFT_COUNT 0
16*74e9b5f2SOlivier Houchard 
17*74e9b5f2SOlivier Houchard struct ck_ec32_slow_path_state {
18*74e9b5f2SOlivier Houchard 	struct ck_ec32 *ec;
19*74e9b5f2SOlivier Houchard 	uint32_t flagged_word;
20*74e9b5f2SOlivier Houchard };
21*74e9b5f2SOlivier Houchard 
22*74e9b5f2SOlivier Houchard #ifdef CK_F_EC64
23*74e9b5f2SOlivier Houchard struct ck_ec64_slow_path_state {
24*74e9b5f2SOlivier Houchard 	struct ck_ec64 *ec;
25*74e9b5f2SOlivier Houchard 	uint64_t flagged_word;
26*74e9b5f2SOlivier Houchard };
27*74e9b5f2SOlivier Houchard #endif
28*74e9b5f2SOlivier Houchard 
29*74e9b5f2SOlivier Houchard /* Once we've waited for >= 1 sec, go for the full deadline. */
30*74e9b5f2SOlivier Houchard static const struct timespec final_wait_time = {
31*74e9b5f2SOlivier Houchard 	.tv_sec = 1
32*74e9b5f2SOlivier Houchard };
33*74e9b5f2SOlivier Houchard 
34*74e9b5f2SOlivier Houchard void
ck_ec32_wake(struct ck_ec32 * ec,const struct ck_ec_ops * ops)35*74e9b5f2SOlivier Houchard ck_ec32_wake(struct ck_ec32 *ec, const struct ck_ec_ops *ops)
36*74e9b5f2SOlivier Houchard {
37*74e9b5f2SOlivier Houchard 	/* Spurious wake-ups are OK. Clear the flag before futexing. */
38*74e9b5f2SOlivier Houchard 	ck_pr_and_32(&ec->counter, (1U << 31) - 1);
39*74e9b5f2SOlivier Houchard 	ops->wake32(ops, &ec->counter);
40*74e9b5f2SOlivier Houchard 	return;
41*74e9b5f2SOlivier Houchard }
42*74e9b5f2SOlivier Houchard 
43*74e9b5f2SOlivier Houchard int
ck_ec32_wait_slow(struct ck_ec32 * ec,const struct ck_ec_ops * ops,uint32_t old_value,const struct timespec * deadline)44*74e9b5f2SOlivier Houchard ck_ec32_wait_slow(struct ck_ec32 *ec,
45*74e9b5f2SOlivier Houchard     const struct ck_ec_ops *ops,
46*74e9b5f2SOlivier Houchard     uint32_t old_value,
47*74e9b5f2SOlivier Houchard     const struct timespec *deadline)
48*74e9b5f2SOlivier Houchard {
49*74e9b5f2SOlivier Houchard 	return ck_ec32_wait_pred_slow(ec, ops, old_value,
50*74e9b5f2SOlivier Houchard 				      NULL, NULL, deadline);
51*74e9b5f2SOlivier Houchard }
52*74e9b5f2SOlivier Houchard 
53*74e9b5f2SOlivier Houchard #ifdef CK_F_EC64
54*74e9b5f2SOlivier Houchard void
ck_ec64_wake(struct ck_ec64 * ec,const struct ck_ec_ops * ops)55*74e9b5f2SOlivier Houchard ck_ec64_wake(struct ck_ec64 *ec, const struct ck_ec_ops *ops)
56*74e9b5f2SOlivier Houchard {
57*74e9b5f2SOlivier Houchard 	ck_pr_and_64(&ec->counter, ~1);
58*74e9b5f2SOlivier Houchard 	ops->wake64(ops, &ec->counter);
59*74e9b5f2SOlivier Houchard 	return;
60*74e9b5f2SOlivier Houchard }
61*74e9b5f2SOlivier Houchard 
62*74e9b5f2SOlivier Houchard int
ck_ec64_wait_slow(struct ck_ec64 * ec,const struct ck_ec_ops * ops,uint64_t old_value,const struct timespec * deadline)63*74e9b5f2SOlivier Houchard ck_ec64_wait_slow(struct ck_ec64 *ec,
64*74e9b5f2SOlivier Houchard     const struct ck_ec_ops *ops,
65*74e9b5f2SOlivier Houchard     uint64_t old_value,
66*74e9b5f2SOlivier Houchard     const struct timespec *deadline)
67*74e9b5f2SOlivier Houchard {
68*74e9b5f2SOlivier Houchard 	return ck_ec64_wait_pred_slow(ec, ops, old_value,
69*74e9b5f2SOlivier Houchard 				      NULL, NULL, deadline);
70*74e9b5f2SOlivier Houchard }
71*74e9b5f2SOlivier Houchard #endif
72*74e9b5f2SOlivier Houchard 
73*74e9b5f2SOlivier Houchard int
ck_ec_deadline_impl(struct timespec * new_deadline,const struct ck_ec_ops * ops,const struct timespec * timeout)74*74e9b5f2SOlivier Houchard ck_ec_deadline_impl(struct timespec *new_deadline,
75*74e9b5f2SOlivier Houchard     const struct ck_ec_ops *ops,
76*74e9b5f2SOlivier Houchard     const struct timespec *timeout)
77*74e9b5f2SOlivier Houchard {
78*74e9b5f2SOlivier Houchard 	struct timespec now;
79*74e9b5f2SOlivier Houchard 	int r;
80*74e9b5f2SOlivier Houchard 
81*74e9b5f2SOlivier Houchard 	if (timeout == NULL) {
82*74e9b5f2SOlivier Houchard 		new_deadline->tv_sec = TIME_MAX;
83*74e9b5f2SOlivier Houchard 		new_deadline->tv_nsec = NSEC_MAX;
84*74e9b5f2SOlivier Houchard 		return 0;
85*74e9b5f2SOlivier Houchard 	}
86*74e9b5f2SOlivier Houchard 
87*74e9b5f2SOlivier Houchard 	r = ops->gettime(ops, &now);
88*74e9b5f2SOlivier Houchard 	if (r != 0) {
89*74e9b5f2SOlivier Houchard 		return -1;
90*74e9b5f2SOlivier Houchard 	}
91*74e9b5f2SOlivier Houchard 
92*74e9b5f2SOlivier Houchard 	*new_deadline = timespec_add(now, *timeout);
93*74e9b5f2SOlivier Houchard 	return 0;
94*74e9b5f2SOlivier Houchard }
95*74e9b5f2SOlivier Houchard 
96*74e9b5f2SOlivier Houchard /* The rest of the file implements wait_pred_slow. */
97*74e9b5f2SOlivier Houchard 
98*74e9b5f2SOlivier Houchard /*
99*74e9b5f2SOlivier Houchard  * Returns a timespec value for deadline_ptr. If deadline_ptr is NULL,
100*74e9b5f2SOlivier Houchard  * returns a timespec far in the future.
101*74e9b5f2SOlivier Houchard  */
102*74e9b5f2SOlivier Houchard static struct timespec
canonical_deadline(const struct timespec * deadline_ptr)103*74e9b5f2SOlivier Houchard canonical_deadline(const struct timespec *deadline_ptr)
104*74e9b5f2SOlivier Houchard {
105*74e9b5f2SOlivier Houchard 
106*74e9b5f2SOlivier Houchard 	if (deadline_ptr == NULL) {
107*74e9b5f2SOlivier Houchard 		return (struct timespec) { .tv_sec = TIME_MAX };
108*74e9b5f2SOlivier Houchard 	}
109*74e9b5f2SOlivier Houchard 
110*74e9b5f2SOlivier Houchard 	return *deadline_ptr;
111*74e9b5f2SOlivier Houchard }
112*74e9b5f2SOlivier Houchard 
113*74e9b5f2SOlivier Houchard /*
114*74e9b5f2SOlivier Houchard  * Really slow (sleeping) path for ck_ec_wait.	Drives the exponential
115*74e9b5f2SOlivier Houchard  * backoff scheme to sleep for longer and longer periods of time,
116*74e9b5f2SOlivier Houchard  * until either the sleep function returns true (the eventcount's
117*74e9b5f2SOlivier Houchard  * value has changed), or the predicate returns non-0 (something else
118*74e9b5f2SOlivier Houchard  * has changed).
119*74e9b5f2SOlivier Houchard  *
120*74e9b5f2SOlivier Houchard  * If deadline is ever reached, returns -1 (timeout).
121*74e9b5f2SOlivier Houchard  *
122*74e9b5f2SOlivier Houchard  * TODO: add some form of randomisation to the intermediate timeout
123*74e9b5f2SOlivier Houchard  * values.
124*74e9b5f2SOlivier Houchard  */
125*74e9b5f2SOlivier Houchard static int
exponential_backoff(struct ck_ec_wait_state * wait_state,bool (* sleep)(const void * sleep_state,const struct ck_ec_wait_state * wait_state,const struct timespec * partial_deadline),const void * sleep_state,int (* pred)(const struct ck_ec_wait_state * state,struct timespec * deadline),const struct timespec * deadline)126*74e9b5f2SOlivier Houchard exponential_backoff(struct ck_ec_wait_state *wait_state,
127*74e9b5f2SOlivier Houchard     bool (*sleep)(const void *sleep_state,
128*74e9b5f2SOlivier Houchard 	const struct ck_ec_wait_state *wait_state,
129*74e9b5f2SOlivier Houchard 	const struct timespec *partial_deadline),
130*74e9b5f2SOlivier Houchard     const void *sleep_state,
131*74e9b5f2SOlivier Houchard     int (*pred)(const struct ck_ec_wait_state *state,
132*74e9b5f2SOlivier Houchard 	struct timespec *deadline),
133*74e9b5f2SOlivier Houchard     const struct timespec *deadline)
134*74e9b5f2SOlivier Houchard {
135*74e9b5f2SOlivier Houchard 	struct timespec begin;
136*74e9b5f2SOlivier Houchard 	struct timespec stop_backoff;
137*74e9b5f2SOlivier Houchard 	const struct ck_ec_ops *ops = wait_state->ops;
138*74e9b5f2SOlivier Houchard 	const uint32_t scale_factor = (ops->wait_scale_factor != 0)
139*74e9b5f2SOlivier Houchard 	    ? ops->wait_scale_factor
140*74e9b5f2SOlivier Houchard 	    : DEFAULT_WAIT_SCALE_FACTOR;
141*74e9b5f2SOlivier Houchard 	const uint32_t shift_count = (ops->wait_shift_count != 0)
142*74e9b5f2SOlivier Houchard 	    ? ops->wait_shift_count
143*74e9b5f2SOlivier Houchard 	    : DEFAULT_WAIT_SHIFT_COUNT;
144*74e9b5f2SOlivier Houchard 	uint32_t wait_ns = (ops->initial_wait_ns != 0)
145*74e9b5f2SOlivier Houchard 	    ? ops->initial_wait_ns
146*74e9b5f2SOlivier Houchard 	    : DEFAULT_INITIAL_WAIT_NS;
147*74e9b5f2SOlivier Houchard 	bool first = true;
148*74e9b5f2SOlivier Houchard 
149*74e9b5f2SOlivier Houchard 	for (;;) {
150*74e9b5f2SOlivier Houchard 		struct timespec now;
151*74e9b5f2SOlivier Houchard 		struct timespec partial_deadline;
152*74e9b5f2SOlivier Houchard 
153*74e9b5f2SOlivier Houchard 		if (check_deadline(&now, ops, *deadline) == true) {
154*74e9b5f2SOlivier Houchard 			/* Timeout. Bail out. */
155*74e9b5f2SOlivier Houchard 			return -1;
156*74e9b5f2SOlivier Houchard 		}
157*74e9b5f2SOlivier Houchard 
158*74e9b5f2SOlivier Houchard 		if (first) {
159*74e9b5f2SOlivier Houchard 			begin = now;
160*74e9b5f2SOlivier Houchard 			wait_state->start = begin;
161*74e9b5f2SOlivier Houchard 			stop_backoff = timespec_add(begin, final_wait_time);
162*74e9b5f2SOlivier Houchard 			first = false;
163*74e9b5f2SOlivier Houchard 		}
164*74e9b5f2SOlivier Houchard 
165*74e9b5f2SOlivier Houchard 		wait_state->now = now;
166*74e9b5f2SOlivier Houchard 		if (timespec_cmp(now, stop_backoff) >= 0) {
167*74e9b5f2SOlivier Houchard 			partial_deadline = *deadline;
168*74e9b5f2SOlivier Houchard 		} else {
169*74e9b5f2SOlivier Houchard 			do {
170*74e9b5f2SOlivier Houchard 				partial_deadline =
171*74e9b5f2SOlivier Houchard 				    timespec_add_ns(begin, wait_ns);
172*74e9b5f2SOlivier Houchard 				wait_ns =
173*74e9b5f2SOlivier Houchard 				    wait_time_scale(wait_ns,
174*74e9b5f2SOlivier Houchard 						    scale_factor,
175*74e9b5f2SOlivier Houchard 						    shift_count);
176*74e9b5f2SOlivier Houchard 			} while (timespec_cmp(partial_deadline, now) <= 0);
177*74e9b5f2SOlivier Houchard 		}
178*74e9b5f2SOlivier Houchard 
179*74e9b5f2SOlivier Houchard 		if (pred != NULL) {
180*74e9b5f2SOlivier Houchard 			int r = pred(wait_state, &partial_deadline);
181*74e9b5f2SOlivier Houchard 			if (r != 0) {
182*74e9b5f2SOlivier Houchard 				return r;
183*74e9b5f2SOlivier Houchard 			}
184*74e9b5f2SOlivier Houchard 		}
185*74e9b5f2SOlivier Houchard 
186*74e9b5f2SOlivier Houchard 		/* Canonicalize deadlines in the far future to NULL. */
187*74e9b5f2SOlivier Houchard 		if (sleep(sleep_state, wait_state,
188*74e9b5f2SOlivier Houchard 			  ((partial_deadline.tv_sec == TIME_MAX)
189*74e9b5f2SOlivier Houchard 			   ? NULL :  &partial_deadline)) == true) {
190*74e9b5f2SOlivier Houchard 			return 0;
191*74e9b5f2SOlivier Houchard 		}
192*74e9b5f2SOlivier Houchard 	}
193*74e9b5f2SOlivier Houchard }
194*74e9b5f2SOlivier Houchard 
195*74e9b5f2SOlivier Houchard /*
196*74e9b5f2SOlivier Houchard  * Loops up to BUSY_LOOP_ITER times, or until ec's counter value
197*74e9b5f2SOlivier Houchard  * (including the flag) differs from old_value.
198*74e9b5f2SOlivier Houchard  *
199*74e9b5f2SOlivier Houchard  * Returns the new value in ec.
200*74e9b5f2SOlivier Houchard  */
201*74e9b5f2SOlivier Houchard #define DEF_WAIT_EASY(W)						\
202*74e9b5f2SOlivier Houchard 	static uint##W##_t ck_ec##W##_wait_easy(struct ck_ec##W* ec,	\
203*74e9b5f2SOlivier Houchard 						const struct ck_ec_ops *ops, \
204*74e9b5f2SOlivier Houchard 						uint##W##_t expected)	\
205*74e9b5f2SOlivier Houchard 	{								\
206*74e9b5f2SOlivier Houchard 		uint##W##_t current = ck_pr_load_##W(&ec->counter);	\
207*74e9b5f2SOlivier Houchard 		size_t n = (ops->busy_loop_iter != 0)			\
208*74e9b5f2SOlivier Houchard 		    ? ops->busy_loop_iter				\
209*74e9b5f2SOlivier Houchard 		    : DEFAULT_BUSY_LOOP_ITER;				\
210*74e9b5f2SOlivier Houchard 									\
211*74e9b5f2SOlivier Houchard 		for (size_t i = 0;					\
212*74e9b5f2SOlivier Houchard 		     i < n && current == expected;			\
213*74e9b5f2SOlivier Houchard 		     i++) {						\
214*74e9b5f2SOlivier Houchard 			ck_pr_stall();					\
215*74e9b5f2SOlivier Houchard 			current = ck_pr_load_##W(&ec->counter);		\
216*74e9b5f2SOlivier Houchard 		}							\
217*74e9b5f2SOlivier Houchard 									\
218*74e9b5f2SOlivier Houchard 		return current;						\
219*74e9b5f2SOlivier Houchard 	}
220*74e9b5f2SOlivier Houchard 
221*74e9b5f2SOlivier Houchard DEF_WAIT_EASY(32)
222*74e9b5f2SOlivier Houchard #ifdef CK_F_EC64
223*74e9b5f2SOlivier Houchard DEF_WAIT_EASY(64)
224*74e9b5f2SOlivier Houchard #endif
225*74e9b5f2SOlivier Houchard #undef DEF_WAIT_EASY
226*74e9b5f2SOlivier Houchard /*
227*74e9b5f2SOlivier Houchard  * Attempts to upgrade ec->counter from unflagged to flagged.
228*74e9b5f2SOlivier Houchard  *
229*74e9b5f2SOlivier Houchard  * Returns true if the event count has changed. Otherwise, ec's
230*74e9b5f2SOlivier Houchard  * counter word is equal to flagged on return, or has been at some
231*74e9b5f2SOlivier Houchard  * time before the return.
232*74e9b5f2SOlivier Houchard  */
233*74e9b5f2SOlivier Houchard #define DEF_UPGRADE(W)							\
234*74e9b5f2SOlivier Houchard 	static bool ck_ec##W##_upgrade(struct ck_ec##W* ec,		\
235*74e9b5f2SOlivier Houchard 				       uint##W##_t current,		\
236*74e9b5f2SOlivier Houchard 				       uint##W##_t unflagged,		\
237*74e9b5f2SOlivier Houchard 				       uint##W##_t flagged)		\
238*74e9b5f2SOlivier Houchard 	{								\
239*74e9b5f2SOlivier Houchard 		uint##W##_t old_word;					\
240*74e9b5f2SOlivier Houchard 									\
241*74e9b5f2SOlivier Houchard 		if (current == flagged) {				\
242*74e9b5f2SOlivier Houchard 			/* Nothing to do, no change. */			\
243*74e9b5f2SOlivier Houchard 			return false;					\
244*74e9b5f2SOlivier Houchard 		}							\
245*74e9b5f2SOlivier Houchard 									\
246*74e9b5f2SOlivier Houchard 		if (current != unflagged) {				\
247*74e9b5f2SOlivier Houchard 			/* We have a different counter value! */	\
248*74e9b5f2SOlivier Houchard 			return true;					\
249*74e9b5f2SOlivier Houchard 		}							\
250*74e9b5f2SOlivier Houchard 									\
251*74e9b5f2SOlivier Houchard 		/*							\
252*74e9b5f2SOlivier Houchard 		 * Flag the counter value. The CAS only fails if the	\
253*74e9b5f2SOlivier Houchard 		 * counter is already flagged, or has a new value.	\
254*74e9b5f2SOlivier Houchard 		 */							\
255*74e9b5f2SOlivier Houchard 		return (ck_pr_cas_##W##_value(&ec->counter,		\
256*74e9b5f2SOlivier Houchard 					      unflagged, flagged,	\
257*74e9b5f2SOlivier Houchard 					      &old_word) == false &&	\
258*74e9b5f2SOlivier Houchard 			old_word != flagged);				\
259*74e9b5f2SOlivier Houchard 	}
260*74e9b5f2SOlivier Houchard 
261*74e9b5f2SOlivier Houchard DEF_UPGRADE(32)
262*74e9b5f2SOlivier Houchard #ifdef CK_F_EC64
263*74e9b5f2SOlivier Houchard DEF_UPGRADE(64)
264*74e9b5f2SOlivier Houchard #endif
265*74e9b5f2SOlivier Houchard #undef DEF_UPGRADE
266*74e9b5f2SOlivier Houchard 
267*74e9b5f2SOlivier Houchard /*
268*74e9b5f2SOlivier Houchard  * Blocks until partial_deadline on the ck_ec. Returns true if the
269*74e9b5f2SOlivier Houchard  * eventcount's value has changed. If partial_deadline is NULL, wait
270*74e9b5f2SOlivier Houchard  * forever.
271*74e9b5f2SOlivier Houchard  */
272*74e9b5f2SOlivier Houchard static bool
ck_ec32_wait_slow_once(const void * vstate,const struct ck_ec_wait_state * wait_state,const struct timespec * partial_deadline)273*74e9b5f2SOlivier Houchard ck_ec32_wait_slow_once(const void *vstate,
274*74e9b5f2SOlivier Houchard     const struct ck_ec_wait_state *wait_state,
275*74e9b5f2SOlivier Houchard     const struct timespec *partial_deadline)
276*74e9b5f2SOlivier Houchard {
277*74e9b5f2SOlivier Houchard 	const struct ck_ec32_slow_path_state *state = vstate;
278*74e9b5f2SOlivier Houchard 	const struct ck_ec32 *ec = state->ec;
279*74e9b5f2SOlivier Houchard 	const uint32_t flagged_word = state->flagged_word;
280*74e9b5f2SOlivier Houchard 
281*74e9b5f2SOlivier Houchard 	wait_state->ops->wait32(wait_state, &ec->counter,
282*74e9b5f2SOlivier Houchard 				flagged_word, partial_deadline);
283*74e9b5f2SOlivier Houchard 	return ck_pr_load_32(&ec->counter) != flagged_word;
284*74e9b5f2SOlivier Houchard }
285*74e9b5f2SOlivier Houchard 
286*74e9b5f2SOlivier Houchard #ifdef CK_F_EC64
287*74e9b5f2SOlivier Houchard static bool
ck_ec64_wait_slow_once(const void * vstate,const struct ck_ec_wait_state * wait_state,const struct timespec * partial_deadline)288*74e9b5f2SOlivier Houchard ck_ec64_wait_slow_once(const void *vstate,
289*74e9b5f2SOlivier Houchard     const struct ck_ec_wait_state *wait_state,
290*74e9b5f2SOlivier Houchard     const struct timespec *partial_deadline)
291*74e9b5f2SOlivier Houchard {
292*74e9b5f2SOlivier Houchard 	const struct ck_ec64_slow_path_state *state = vstate;
293*74e9b5f2SOlivier Houchard 	const struct ck_ec64 *ec = state->ec;
294*74e9b5f2SOlivier Houchard 	const uint64_t flagged_word = state->flagged_word;
295*74e9b5f2SOlivier Houchard 
296*74e9b5f2SOlivier Houchard 	/* futex_wait will only compare the low 32 bits. Perform a
297*74e9b5f2SOlivier Houchard 	 * full comparison here to maximise the changes of catching an
298*74e9b5f2SOlivier Houchard 	 * ABA in the low 32 bits.
299*74e9b5f2SOlivier Houchard 	 */
300*74e9b5f2SOlivier Houchard 	if (ck_pr_load_64(&ec->counter) != flagged_word) {
301*74e9b5f2SOlivier Houchard 		return true;
302*74e9b5f2SOlivier Houchard 	}
303*74e9b5f2SOlivier Houchard 
304*74e9b5f2SOlivier Houchard 	wait_state->ops->wait64(wait_state, &ec->counter,
305*74e9b5f2SOlivier Houchard 				flagged_word, partial_deadline);
306*74e9b5f2SOlivier Houchard 	return ck_pr_load_64(&ec->counter) != flagged_word;
307*74e9b5f2SOlivier Houchard }
308*74e9b5f2SOlivier Houchard #endif
309*74e9b5f2SOlivier Houchard 
310*74e9b5f2SOlivier Houchard /*
311*74e9b5f2SOlivier Houchard  * The full wait logic is a lot of code (> 1KB). Encourage the
312*74e9b5f2SOlivier Houchard  * compiler to lay this all out linearly with LIKELY annotations on
313*74e9b5f2SOlivier Houchard  * every early exit.
314*74e9b5f2SOlivier Houchard  */
315*74e9b5f2SOlivier Houchard #define WAIT_SLOW_BODY(W, ec, ops, pred, data, deadline_ptr,		\
316*74e9b5f2SOlivier Houchard 		       old_value, unflagged, flagged)			\
317*74e9b5f2SOlivier Houchard 	do {								\
318*74e9b5f2SOlivier Houchard 		struct ck_ec_wait_state wait_state = {			\
319*74e9b5f2SOlivier Houchard 			.ops = ops,					\
320*74e9b5f2SOlivier Houchard 			.data = data					\
321*74e9b5f2SOlivier Houchard 		};							\
322*74e9b5f2SOlivier Houchard 		const struct ck_ec##W##_slow_path_state state = {	\
323*74e9b5f2SOlivier Houchard 			.ec = ec,					\
324*74e9b5f2SOlivier Houchard 			.flagged_word = flagged				\
325*74e9b5f2SOlivier Houchard 		};							\
326*74e9b5f2SOlivier Houchard 		const struct timespec deadline =			\
327*74e9b5f2SOlivier Houchard 			canonical_deadline(deadline_ptr);		\
328*74e9b5f2SOlivier Houchard 									\
329*74e9b5f2SOlivier Houchard 		/* Detect infinite past deadlines. */			\
330*74e9b5f2SOlivier Houchard 		if (CK_CC_LIKELY(deadline.tv_sec <= 0)) {		\
331*74e9b5f2SOlivier Houchard 			return -1;					\
332*74e9b5f2SOlivier Houchard 		}							\
333*74e9b5f2SOlivier Houchard 									\
334*74e9b5f2SOlivier Houchard 		for (;;) {						\
335*74e9b5f2SOlivier Houchard 			uint##W##_t current;				\
336*74e9b5f2SOlivier Houchard 			int r;						\
337*74e9b5f2SOlivier Houchard 									\
338*74e9b5f2SOlivier Houchard 			current = ck_ec##W##_wait_easy(ec, ops, unflagged); \
339*74e9b5f2SOlivier Houchard 									\
340*74e9b5f2SOlivier Houchard 			/*						\
341*74e9b5f2SOlivier Houchard 			 * We're about to wait harder (i.e.,		\
342*74e9b5f2SOlivier Houchard 			 * potentially with futex). Make sure the	\
343*74e9b5f2SOlivier Houchard 			 * counter word is flagged.			\
344*74e9b5f2SOlivier Houchard 			 */						\
345*74e9b5f2SOlivier Houchard 			if (CK_CC_LIKELY(				\
346*74e9b5f2SOlivier Houchard 				ck_ec##W##_upgrade(ec, current,		\
347*74e9b5f2SOlivier Houchard 					unflagged, flagged) == true)) { \
348*74e9b5f2SOlivier Houchard 				ck_pr_fence_acquire();			\
349*74e9b5f2SOlivier Houchard 				return 0;				\
350*74e9b5f2SOlivier Houchard 			}						\
351*74e9b5f2SOlivier Houchard 									\
352*74e9b5f2SOlivier Houchard 			/*						\
353*74e9b5f2SOlivier Houchard 			 * By now, ec->counter == flagged_word (at	\
354*74e9b5f2SOlivier Houchard 			 * some point in the past). Spin some more to	\
355*74e9b5f2SOlivier Houchard 			 * heuristically let any in-flight SP inc/add	\
356*74e9b5f2SOlivier Houchard 			 * to retire. This does not affect		\
357*74e9b5f2SOlivier Houchard 			 * correctness, but practically eliminates	\
358*74e9b5f2SOlivier Houchard 			 * lost wake-ups.				\
359*74e9b5f2SOlivier Houchard 			 */						\
360*74e9b5f2SOlivier Houchard 			current = ck_ec##W##_wait_easy(ec, ops, flagged); \
361*74e9b5f2SOlivier Houchard 			if (CK_CC_LIKELY(current != flagged_word)) {	\
362*74e9b5f2SOlivier Houchard 				ck_pr_fence_acquire();			\
363*74e9b5f2SOlivier Houchard 				return 0;				\
364*74e9b5f2SOlivier Houchard 			}						\
365*74e9b5f2SOlivier Houchard 									\
366*74e9b5f2SOlivier Houchard 			r = exponential_backoff(&wait_state,		\
367*74e9b5f2SOlivier Houchard 						ck_ec##W##_wait_slow_once, \
368*74e9b5f2SOlivier Houchard 						&state,			\
369*74e9b5f2SOlivier Houchard 						pred, &deadline); \
370*74e9b5f2SOlivier Houchard 			if (r != 0) {					\
371*74e9b5f2SOlivier Houchard 				return r;				\
372*74e9b5f2SOlivier Houchard 			}						\
373*74e9b5f2SOlivier Houchard 									\
374*74e9b5f2SOlivier Houchard 			if (ck_ec##W##_value(ec) != old_value) {	\
375*74e9b5f2SOlivier Houchard 				ck_pr_fence_acquire();			\
376*74e9b5f2SOlivier Houchard 				return 0;				\
377*74e9b5f2SOlivier Houchard 			}						\
378*74e9b5f2SOlivier Houchard 									\
379*74e9b5f2SOlivier Houchard 			/* Spurious wake-up. Redo the slow path. */	\
380*74e9b5f2SOlivier Houchard 		}							\
381*74e9b5f2SOlivier Houchard 	} while (0)
382*74e9b5f2SOlivier Houchard 
383*74e9b5f2SOlivier Houchard int
ck_ec32_wait_pred_slow(struct ck_ec32 * ec,const struct ck_ec_ops * ops,uint32_t old_value,int (* pred)(const struct ck_ec_wait_state * state,struct timespec * deadline),void * data,const struct timespec * deadline_ptr)384*74e9b5f2SOlivier Houchard ck_ec32_wait_pred_slow(struct ck_ec32 *ec,
385*74e9b5f2SOlivier Houchard     const struct ck_ec_ops *ops,
386*74e9b5f2SOlivier Houchard     uint32_t old_value,
387*74e9b5f2SOlivier Houchard     int (*pred)(const struct ck_ec_wait_state *state,
388*74e9b5f2SOlivier Houchard 	struct timespec *deadline),
389*74e9b5f2SOlivier Houchard     void *data,
390*74e9b5f2SOlivier Houchard     const struct timespec *deadline_ptr)
391*74e9b5f2SOlivier Houchard {
392*74e9b5f2SOlivier Houchard 	const uint32_t unflagged_word = old_value;
393*74e9b5f2SOlivier Houchard 	const uint32_t flagged_word = old_value | (1UL << 31);
394*74e9b5f2SOlivier Houchard 
395*74e9b5f2SOlivier Houchard 	if (CK_CC_UNLIKELY(ck_ec32_value(ec) != old_value)) {
396*74e9b5f2SOlivier Houchard 		return 0;
397*74e9b5f2SOlivier Houchard 	}
398*74e9b5f2SOlivier Houchard 
399*74e9b5f2SOlivier Houchard 	WAIT_SLOW_BODY(32, ec, ops, pred, data, deadline_ptr,
400*74e9b5f2SOlivier Houchard 		       old_value, unflagged_word, flagged_word);
401*74e9b5f2SOlivier Houchard }
402*74e9b5f2SOlivier Houchard 
403*74e9b5f2SOlivier Houchard #ifdef CK_F_EC64
404*74e9b5f2SOlivier Houchard int
ck_ec64_wait_pred_slow(struct ck_ec64 * ec,const struct ck_ec_ops * ops,uint64_t old_value,int (* pred)(const struct ck_ec_wait_state * state,struct timespec * deadline),void * data,const struct timespec * deadline_ptr)405*74e9b5f2SOlivier Houchard ck_ec64_wait_pred_slow(struct ck_ec64 *ec,
406*74e9b5f2SOlivier Houchard     const struct ck_ec_ops *ops,
407*74e9b5f2SOlivier Houchard     uint64_t old_value,
408*74e9b5f2SOlivier Houchard     int (*pred)(const struct ck_ec_wait_state *state,
409*74e9b5f2SOlivier Houchard 	struct timespec *deadline),
410*74e9b5f2SOlivier Houchard     void *data,
411*74e9b5f2SOlivier Houchard     const struct timespec *deadline_ptr)
412*74e9b5f2SOlivier Houchard {
413*74e9b5f2SOlivier Houchard 	const uint64_t unflagged_word = old_value << 1;
414*74e9b5f2SOlivier Houchard 	const uint64_t flagged_word = unflagged_word | 1;
415*74e9b5f2SOlivier Houchard 
416*74e9b5f2SOlivier Houchard 	if (CK_CC_UNLIKELY(ck_ec64_value(ec) != old_value)) {
417*74e9b5f2SOlivier Houchard 		return 0;
418*74e9b5f2SOlivier Houchard 	}
419*74e9b5f2SOlivier Houchard 
420*74e9b5f2SOlivier Houchard 	WAIT_SLOW_BODY(64, ec, ops, pred, data, deadline_ptr,
421*74e9b5f2SOlivier Houchard 		       old_value, unflagged_word, flagged_word);
422*74e9b5f2SOlivier Houchard }
423*74e9b5f2SOlivier Houchard #endif
424*74e9b5f2SOlivier Houchard 
425*74e9b5f2SOlivier Houchard #undef WAIT_SLOW_BODY
426