1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(C) 2019 Marvell International Ltd.
5 #ifndef __OTX2_TIM_WORKER_H__
6 #define __OTX2_TIM_WORKER_H__
8 #include "otx2_tim_evdev.h"
11 tim_bkt_fetch_lock(uint64_t w1)
13 return (w1 >> TIM_BUCKET_W1_S_LOCK) &
18 tim_bkt_fetch_rem(uint64_t w1)
20 return (w1 >> TIM_BUCKET_W1_S_CHUNK_REMAINDER) &
21 TIM_BUCKET_W1_M_CHUNK_REMAINDER;
25 tim_bkt_get_rem(struct otx2_tim_bkt *bktp)
27 return __atomic_load_n(&bktp->chunk_remainder, __ATOMIC_ACQUIRE);
31 tim_bkt_set_rem(struct otx2_tim_bkt *bktp, uint16_t v)
33 __atomic_store_n(&bktp->chunk_remainder, v, __ATOMIC_RELAXED);
37 tim_bkt_sub_rem(struct otx2_tim_bkt *bktp, uint16_t v)
39 __atomic_fetch_sub(&bktp->chunk_remainder, v, __ATOMIC_RELAXED);
43 tim_bkt_get_hbt(uint64_t w1)
45 return (w1 >> TIM_BUCKET_W1_S_HBT) & TIM_BUCKET_W1_M_HBT;
49 tim_bkt_get_bsk(uint64_t w1)
51 return (w1 >> TIM_BUCKET_W1_S_BSK) & TIM_BUCKET_W1_M_BSK;
54 static inline uint64_t
55 tim_bkt_clr_bsk(struct otx2_tim_bkt *bktp)
57 /* Clear everything except lock. */
58 const uint64_t v = TIM_BUCKET_W1_M_LOCK << TIM_BUCKET_W1_S_LOCK;
60 return __atomic_fetch_and(&bktp->w1, v, __ATOMIC_ACQ_REL);
63 static inline uint64_t
64 tim_bkt_fetch_sema_lock(struct otx2_tim_bkt *bktp)
66 return __atomic_fetch_add(&bktp->w1, TIM_BUCKET_SEMA_WLOCK,
70 static inline uint64_t
71 tim_bkt_fetch_sema(struct otx2_tim_bkt *bktp)
73 return __atomic_fetch_add(&bktp->w1, TIM_BUCKET_SEMA, __ATOMIC_RELAXED);
76 static inline uint64_t
77 tim_bkt_inc_lock(struct otx2_tim_bkt *bktp)
79 const uint64_t v = 1ull << TIM_BUCKET_W1_S_LOCK;
81 return __atomic_fetch_add(&bktp->w1, v, __ATOMIC_ACQUIRE);
85 tim_bkt_dec_lock(struct otx2_tim_bkt *bktp)
87 __atomic_add_fetch(&bktp->lock, 0xff, __ATOMIC_RELEASE);
90 static inline uint32_t
91 tim_bkt_get_nent(uint64_t w1)
93 return (w1 >> TIM_BUCKET_W1_S_NUM_ENTRIES) &
94 TIM_BUCKET_W1_M_NUM_ENTRIES;
98 tim_bkt_inc_nent(struct otx2_tim_bkt *bktp)
100 __atomic_add_fetch(&bktp->nb_entry, 1, __ATOMIC_RELAXED);
104 tim_bkt_add_nent(struct otx2_tim_bkt *bktp, uint32_t v)
106 __atomic_add_fetch(&bktp->nb_entry, v, __ATOMIC_RELAXED);
109 static inline uint64_t
110 tim_bkt_clr_nent(struct otx2_tim_bkt *bktp)
112 const uint64_t v = ~(TIM_BUCKET_W1_M_NUM_ENTRIES <<
113 TIM_BUCKET_W1_S_NUM_ENTRIES);
115 return __atomic_and_fetch(&bktp->w1, v, __ATOMIC_ACQ_REL);
118 static __rte_always_inline struct otx2_tim_bkt *
119 tim_get_target_bucket(struct otx2_tim_ring * const tim_ring,
120 const uint32_t rel_bkt, const uint8_t flag)
122 const uint64_t bkt_cyc = rte_rdtsc() - tim_ring->ring_start_cyc;
123 uint32_t bucket = rte_reciprocal_divide_u64(bkt_cyc,
124 &tim_ring->fast_div) + rel_bkt;
126 if (flag & OTX2_TIM_BKT_MOD)
127 bucket = bucket % tim_ring->nb_bkts;
128 if (flag & OTX2_TIM_BKT_AND)
129 bucket = bucket & (tim_ring->nb_bkts - 1);
131 return &tim_ring->bkt[bucket];
134 static struct otx2_tim_ent *
135 tim_clr_bkt(struct otx2_tim_ring * const tim_ring,
136 struct otx2_tim_bkt * const bkt)
138 struct otx2_tim_ent *chunk;
139 struct otx2_tim_ent *pnext;
141 chunk = ((struct otx2_tim_ent *)(uintptr_t)bkt->first_chunk);
142 chunk = (struct otx2_tim_ent *)(uintptr_t)(chunk +
143 tim_ring->nb_chunk_slots)->w0;
145 pnext = (struct otx2_tim_ent *)(uintptr_t)
146 ((chunk + tim_ring->nb_chunk_slots)->w0);
147 rte_mempool_put(tim_ring->chunk_pool, chunk);
151 return (struct otx2_tim_ent *)(uintptr_t)bkt->first_chunk;
154 static struct otx2_tim_ent *
155 tim_refill_chunk(struct otx2_tim_bkt * const bkt,
156 struct otx2_tim_ring * const tim_ring)
158 struct otx2_tim_ent *chunk;
160 if (bkt->nb_entry || !bkt->first_chunk) {
161 if (unlikely(rte_mempool_get(tim_ring->chunk_pool,
165 *(uint64_t *)(((struct otx2_tim_ent *)(uintptr_t)
166 bkt->current_chunk) +
167 tim_ring->nb_chunk_slots) =
170 bkt->first_chunk = (uintptr_t)chunk;
173 chunk = tim_clr_bkt(tim_ring, bkt);
174 bkt->first_chunk = (uintptr_t)chunk;
176 *(uint64_t *)(chunk + tim_ring->nb_chunk_slots) = 0;
181 static struct otx2_tim_ent *
182 tim_insert_chunk(struct otx2_tim_bkt * const bkt,
183 struct otx2_tim_ring * const tim_ring)
185 struct otx2_tim_ent *chunk;
187 if (unlikely(rte_mempool_get(tim_ring->chunk_pool, (void **)&chunk)))
190 *(uint64_t *)(chunk + tim_ring->nb_chunk_slots) = 0;
192 *(uint64_t *)(((struct otx2_tim_ent *)(uintptr_t)
193 bkt->current_chunk) +
194 tim_ring->nb_chunk_slots) = (uintptr_t)chunk;
196 bkt->first_chunk = (uintptr_t)chunk;
201 static __rte_always_inline int
202 tim_add_entry_sp(struct otx2_tim_ring * const tim_ring,
203 const uint32_t rel_bkt,
204 struct rte_event_timer * const tim,
205 const struct otx2_tim_ent * const pent,
208 struct otx2_tim_ent *chunk;
209 struct otx2_tim_bkt *bkt;
213 bkt = tim_get_target_bucket(tim_ring, rel_bkt, flags);
217 lock_sema = tim_bkt_fetch_sema_lock(bkt);
219 /* Bucket related checks. */
220 if (unlikely(tim_bkt_get_hbt(lock_sema))) {
221 if (tim_bkt_get_nent(lock_sema) != 0) {
223 #ifdef RTE_ARCH_ARM64
225 " ldaxr %[hbt], [%[w1]] \n"
226 " tbz %[hbt], 33, dne%= \n"
229 " ldaxr %[hbt], [%[w1]] \n"
230 " tbnz %[hbt], 33, rty%= \n"
232 : [hbt] "=&r" (hbt_state)
233 : [w1] "r" ((&bkt->w1))
238 hbt_state = __atomic_load_n(&bkt->w1,
240 } while (hbt_state & BIT_ULL(33));
243 if (!(hbt_state & BIT_ULL(34))) {
244 tim_bkt_dec_lock(bkt);
250 /* Insert the work. */
251 rem = tim_bkt_fetch_rem(lock_sema);
254 if (flags & OTX2_TIM_ENA_FB)
255 chunk = tim_refill_chunk(bkt, tim_ring);
256 if (flags & OTX2_TIM_ENA_DFB)
257 chunk = tim_insert_chunk(bkt, tim_ring);
259 if (unlikely(chunk == NULL)) {
260 bkt->chunk_remainder = 0;
261 tim_bkt_dec_lock(bkt);
262 tim->impl_opaque[0] = 0;
263 tim->impl_opaque[1] = 0;
264 tim->state = RTE_EVENT_TIMER_ERROR;
267 bkt->current_chunk = (uintptr_t)chunk;
268 bkt->chunk_remainder = tim_ring->nb_chunk_slots - 1;
270 chunk = (struct otx2_tim_ent *)(uintptr_t)bkt->current_chunk;
271 chunk += tim_ring->nb_chunk_slots - rem;
274 /* Copy work entry. */
277 tim_bkt_inc_nent(bkt);
278 tim_bkt_dec_lock(bkt);
280 tim->impl_opaque[0] = (uintptr_t)chunk;
281 tim->impl_opaque[1] = (uintptr_t)bkt;
282 tim->state = RTE_EVENT_TIMER_ARMED;
287 static __rte_always_inline int
288 tim_add_entry_mp(struct otx2_tim_ring * const tim_ring,
289 const uint32_t rel_bkt,
290 struct rte_event_timer * const tim,
291 const struct otx2_tim_ent * const pent,
294 struct otx2_tim_ent *chunk;
295 struct otx2_tim_bkt *bkt;
300 bkt = tim_get_target_bucket(tim_ring, rel_bkt, flags);
302 lock_sema = tim_bkt_fetch_sema_lock(bkt);
304 /* Bucket related checks. */
305 if (unlikely(tim_bkt_get_hbt(lock_sema))) {
306 if (tim_bkt_get_nent(lock_sema) != 0) {
308 #ifdef RTE_ARCH_ARM64
310 " ldaxr %[hbt], [%[w1]] \n"
311 " tbz %[hbt], 33, dne%= \n"
314 " ldaxr %[hbt], [%[w1]] \n"
315 " tbnz %[hbt], 33, rty%= \n"
317 : [hbt] "=&r" (hbt_state)
318 : [w1] "r" ((&bkt->w1))
323 hbt_state = __atomic_load_n(&bkt->w1,
325 } while (hbt_state & BIT_ULL(33));
328 if (!(hbt_state & BIT_ULL(34))) {
329 tim_bkt_dec_lock(bkt);
335 rem = tim_bkt_fetch_rem(lock_sema);
337 #ifdef RTE_ARCH_ARM64
339 " ldaxrh %w[rem], [%[crem]] \n"
340 " tbz %w[rem], 15, dne%= \n"
343 " ldaxrh %w[rem], [%[crem]] \n"
344 " tbnz %w[rem], 15, rty%= \n"
347 : [crem] "r" (&bkt->chunk_remainder)
351 while (__atomic_load_n(&bkt->chunk_remainder,
352 __ATOMIC_ACQUIRE) < 0)
355 /* Goto diff bucket. */
356 tim_bkt_dec_lock(bkt);
359 /* Only one thread can be here*/
360 if (flags & OTX2_TIM_ENA_FB)
361 chunk = tim_refill_chunk(bkt, tim_ring);
362 if (flags & OTX2_TIM_ENA_DFB)
363 chunk = tim_insert_chunk(bkt, tim_ring);
365 if (unlikely(chunk == NULL)) {
366 tim_bkt_set_rem(bkt, 0);
367 tim_bkt_dec_lock(bkt);
368 tim->impl_opaque[0] = 0;
369 tim->impl_opaque[1] = 0;
370 tim->state = RTE_EVENT_TIMER_ERROR;
374 while (tim_bkt_fetch_lock(lock_sema) !=
375 (-tim_bkt_fetch_rem(lock_sema)))
376 lock_sema = __atomic_load_n(&bkt->w1, __ATOMIC_ACQUIRE);
378 bkt->current_chunk = (uintptr_t)chunk;
379 __atomic_store_n(&bkt->chunk_remainder,
380 tim_ring->nb_chunk_slots - 1, __ATOMIC_RELEASE);
382 chunk = (struct otx2_tim_ent *)bkt->current_chunk;
383 chunk += tim_ring->nb_chunk_slots - rem;
387 /* Copy work entry. */
388 tim_bkt_inc_nent(bkt);
389 tim_bkt_dec_lock(bkt);
390 tim->impl_opaque[0] = (uintptr_t)chunk;
391 tim->impl_opaque[1] = (uintptr_t)bkt;
392 tim->state = RTE_EVENT_TIMER_ARMED;
397 static inline uint16_t
398 tim_cpy_wrk(uint16_t index, uint16_t cpy_lmt,
399 struct otx2_tim_ent *chunk,
400 struct rte_event_timer ** const tim,
401 const struct otx2_tim_ent * const ents,
402 const struct otx2_tim_bkt * const bkt)
404 for (; index < cpy_lmt; index++) {
405 *chunk = *(ents + index);
406 tim[index]->impl_opaque[0] = (uintptr_t)chunk++;
407 tim[index]->impl_opaque[1] = (uintptr_t)bkt;
408 tim[index]->state = RTE_EVENT_TIMER_ARMED;
414 /* Burst mode functions */
416 tim_add_entry_brst(struct otx2_tim_ring * const tim_ring,
417 const uint16_t rel_bkt,
418 struct rte_event_timer ** const tim,
419 const struct otx2_tim_ent *ents,
420 const uint16_t nb_timers, const uint8_t flags)
422 struct otx2_tim_ent *chunk = NULL;
423 struct otx2_tim_bkt *bkt;
424 uint16_t chunk_remainder;
431 bkt = tim_get_target_bucket(tim_ring, rel_bkt, flags);
433 /* Only one thread beyond this. */
434 lock_sema = tim_bkt_inc_lock(bkt);
436 ((lock_sema >> TIM_BUCKET_W1_S_LOCK) & TIM_BUCKET_W1_M_LOCK);
439 tim_bkt_dec_lock(bkt);
443 /* Bucket related checks. */
444 if (unlikely(tim_bkt_get_hbt(lock_sema))) {
445 if (tim_bkt_get_nent(lock_sema) != 0) {
447 #ifdef RTE_ARCH_ARM64
449 " ldaxr %[hbt], [%[w1]] \n"
450 " tbz %[hbt], 33, dne%= \n"
453 " ldaxr %[hbt], [%[w1]] \n"
454 " tbnz %[hbt], 33, rty%= \n"
456 : [hbt] "=&r" (hbt_state)
457 : [w1] "r" ((&bkt->w1))
462 hbt_state = __atomic_load_n(&bkt->w1,
464 } while (hbt_state & BIT_ULL(33));
467 if (!(hbt_state & BIT_ULL(34))) {
468 tim_bkt_dec_lock(bkt);
474 chunk_remainder = tim_bkt_fetch_rem(lock_sema);
475 rem = chunk_remainder - nb_timers;
477 crem = tim_ring->nb_chunk_slots - chunk_remainder;
478 if (chunk_remainder && crem) {
479 chunk = ((struct otx2_tim_ent *)
480 (uintptr_t)bkt->current_chunk) + crem;
482 index = tim_cpy_wrk(index, chunk_remainder, chunk, tim,
484 tim_bkt_sub_rem(bkt, chunk_remainder);
485 tim_bkt_add_nent(bkt, chunk_remainder);
488 if (flags & OTX2_TIM_ENA_FB)
489 chunk = tim_refill_chunk(bkt, tim_ring);
490 if (flags & OTX2_TIM_ENA_DFB)
491 chunk = tim_insert_chunk(bkt, tim_ring);
493 if (unlikely(chunk == NULL)) {
494 tim_bkt_dec_lock(bkt);
496 tim[index]->state = RTE_EVENT_TIMER_ERROR;
499 *(uint64_t *)(chunk + tim_ring->nb_chunk_slots) = 0;
500 bkt->current_chunk = (uintptr_t)chunk;
501 tim_cpy_wrk(index, nb_timers, chunk, tim, ents, bkt);
503 rem = nb_timers - chunk_remainder;
504 tim_bkt_set_rem(bkt, tim_ring->nb_chunk_slots - rem);
505 tim_bkt_add_nent(bkt, rem);
507 chunk = (struct otx2_tim_ent *)(uintptr_t)bkt->current_chunk;
508 chunk += (tim_ring->nb_chunk_slots - chunk_remainder);
510 tim_cpy_wrk(index, nb_timers, chunk, tim, ents, bkt);
511 tim_bkt_sub_rem(bkt, nb_timers);
512 tim_bkt_add_nent(bkt, nb_timers);
515 tim_bkt_dec_lock(bkt);
521 tim_rm_entry(struct rte_event_timer *tim)
523 struct otx2_tim_ent *entry;
524 struct otx2_tim_bkt *bkt;
527 if (tim->impl_opaque[1] == 0 || tim->impl_opaque[0] == 0)
530 entry = (struct otx2_tim_ent *)(uintptr_t)tim->impl_opaque[0];
531 if (entry->wqe != tim->ev.u64) {
532 tim->impl_opaque[0] = 0;
533 tim->impl_opaque[1] = 0;
537 bkt = (struct otx2_tim_bkt *)(uintptr_t)tim->impl_opaque[1];
538 lock_sema = tim_bkt_inc_lock(bkt);
539 if (tim_bkt_get_hbt(lock_sema) || !tim_bkt_get_nent(lock_sema)) {
540 tim_bkt_dec_lock(bkt);
541 tim->impl_opaque[0] = 0;
542 tim->impl_opaque[1] = 0;
548 tim_bkt_dec_lock(bkt);
550 tim->state = RTE_EVENT_TIMER_CANCELED;
551 tim->impl_opaque[0] = 0;
552 tim->impl_opaque[1] = 0;
557 #endif /* __OTX2_TIM_WORKER_H__ */