xref: /dpdk/lib/ring/rte_ring_hts_elem_pvt.h (revision 3197a1ff2a2a6bef224cd51f835f135be3776f23)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  *
399a2dd95SBruce Richardson  * Copyright (c) 2010-2020 Intel Corporation
499a2dd95SBruce Richardson  * Copyright (c) 2007-2009 Kip Macy kmacy@freebsd.org
599a2dd95SBruce Richardson  * All rights reserved.
699a2dd95SBruce Richardson  * Derived from FreeBSD's bufring.h
799a2dd95SBruce Richardson  * Used as BSD-3 Licensed with permission from Kip Macy.
899a2dd95SBruce Richardson  */
999a2dd95SBruce Richardson 
1099a2dd95SBruce Richardson #ifndef _RTE_RING_HTS_ELEM_PVT_H_
1199a2dd95SBruce Richardson #define _RTE_RING_HTS_ELEM_PVT_H_
1299a2dd95SBruce Richardson 
1332faaf30STyler Retzlaff #include <rte_stdatomic.h>
1432faaf30STyler Retzlaff 
1599a2dd95SBruce Richardson /**
1699a2dd95SBruce Richardson  * @file rte_ring_hts_elem_pvt.h
1799a2dd95SBruce Richardson  * It is not recommended to include this file directly,
1899a2dd95SBruce Richardson  * include <rte_ring.h> instead.
1999a2dd95SBruce Richardson  * Contains internal helper functions for head/tail sync (HTS) ring mode.
2099a2dd95SBruce Richardson  * For more information please refer to <rte_ring_hts.h>.
2199a2dd95SBruce Richardson  */
2299a2dd95SBruce Richardson 
2399a2dd95SBruce Richardson /**
2499a2dd95SBruce Richardson  * @internal update tail with new value.
2599a2dd95SBruce Richardson  */
2699a2dd95SBruce Richardson static __rte_always_inline void
2799a2dd95SBruce Richardson __rte_ring_hts_update_tail(struct rte_ring_hts_headtail *ht, uint32_t old_tail,
2899a2dd95SBruce Richardson 	uint32_t num, uint32_t enqueue)
2999a2dd95SBruce Richardson {
3099a2dd95SBruce Richardson 	uint32_t tail;
3199a2dd95SBruce Richardson 
3299a2dd95SBruce Richardson 	RTE_SET_USED(enqueue);
3399a2dd95SBruce Richardson 
3499a2dd95SBruce Richardson 	tail = old_tail + num;
3532faaf30STyler Retzlaff 	rte_atomic_store_explicit(&ht->ht.pos.tail, tail, rte_memory_order_release);
3699a2dd95SBruce Richardson }
3799a2dd95SBruce Richardson 
3899a2dd95SBruce Richardson /**
3999a2dd95SBruce Richardson  * @internal waits till tail will become equal to head.
4099a2dd95SBruce Richardson  * Means no writer/reader is active for that ring.
4199a2dd95SBruce Richardson  * Suppose to work as serialization point.
4299a2dd95SBruce Richardson  */
4399a2dd95SBruce Richardson static __rte_always_inline void
4499a2dd95SBruce Richardson __rte_ring_hts_head_wait(const struct rte_ring_hts_headtail *ht,
4599a2dd95SBruce Richardson 		union __rte_ring_hts_pos *p)
4699a2dd95SBruce Richardson {
4799a2dd95SBruce Richardson 	while (p->pos.head != p->pos.tail) {
4899a2dd95SBruce Richardson 		rte_pause();
4932faaf30STyler Retzlaff 		p->raw = rte_atomic_load_explicit(&ht->ht.raw, rte_memory_order_acquire);
5099a2dd95SBruce Richardson 	}
5199a2dd95SBruce Richardson }
5299a2dd95SBruce Richardson 
5399a2dd95SBruce Richardson /**
54*3197a1ffSKonstantin Ananyev  * @internal This is a helper function that moves the producer/consumer head
55*3197a1ffSKonstantin Ananyev  *
56*3197a1ffSKonstantin Ananyev  * @param d
57*3197a1ffSKonstantin Ananyev  *   A pointer to the headtail structure with head value to be moved
58*3197a1ffSKonstantin Ananyev  * @param s
59*3197a1ffSKonstantin Ananyev  *   A pointer to the counter-part headtail structure. Note that this
60*3197a1ffSKonstantin Ananyev  *   function only reads tail value from it
61*3197a1ffSKonstantin Ananyev  * @param capacity
62*3197a1ffSKonstantin Ananyev  *   Either ring capacity value (for producer), or zero (for consumer)
63*3197a1ffSKonstantin Ananyev  *   Indicates whether multi-thread safe path is needed or not
64*3197a1ffSKonstantin Ananyev  * @param num
65*3197a1ffSKonstantin Ananyev  *   The number of elements we want to move head value on
66*3197a1ffSKonstantin Ananyev  * @param behavior
67*3197a1ffSKonstantin Ananyev  *   RTE_RING_QUEUE_FIXED:    Move on a fixed number of items
68*3197a1ffSKonstantin Ananyev  *   RTE_RING_QUEUE_VARIABLE: Move on as many items as possible
69*3197a1ffSKonstantin Ananyev  * @param old_head
70*3197a1ffSKonstantin Ananyev  *   Returns head value as it was before the move
71*3197a1ffSKonstantin Ananyev  * @param entries
72*3197a1ffSKonstantin Ananyev  *   Returns the number of ring entries available BEFORE head was moved
73*3197a1ffSKonstantin Ananyev  * @return
74*3197a1ffSKonstantin Ananyev  *   Actual number of objects the head was moved on
75*3197a1ffSKonstantin Ananyev  *   If behavior == RTE_RING_QUEUE_FIXED, this will be 0 or n only
7699a2dd95SBruce Richardson  */
77*3197a1ffSKonstantin Ananyev static __rte_always_inline uint32_t
78*3197a1ffSKonstantin Ananyev __rte_ring_hts_move_head(struct rte_ring_hts_headtail *d,
79*3197a1ffSKonstantin Ananyev 	const struct rte_ring_headtail *s, uint32_t capacity, unsigned int num,
8099a2dd95SBruce Richardson 	enum rte_ring_queue_behavior behavior, uint32_t *old_head,
81*3197a1ffSKonstantin Ananyev 	uint32_t *entries)
8299a2dd95SBruce Richardson {
8399a2dd95SBruce Richardson 	uint32_t n;
8499a2dd95SBruce Richardson 	union __rte_ring_hts_pos np, op;
8599a2dd95SBruce Richardson 
86*3197a1ffSKonstantin Ananyev 	op.raw = rte_atomic_load_explicit(&d->ht.raw, rte_memory_order_acquire);
8799a2dd95SBruce Richardson 
8899a2dd95SBruce Richardson 	do {
8999a2dd95SBruce Richardson 		/* Reset n to the initial burst count */
9099a2dd95SBruce Richardson 		n = num;
9199a2dd95SBruce Richardson 
9299a2dd95SBruce Richardson 		/*
9399a2dd95SBruce Richardson 		 * wait for tail to be equal to head,
9499a2dd95SBruce Richardson 		 * make sure that we read prod head/tail *before*
9599a2dd95SBruce Richardson 		 * reading cons tail.
9699a2dd95SBruce Richardson 		 */
97*3197a1ffSKonstantin Ananyev 		__rte_ring_hts_head_wait(d, &op);
9899a2dd95SBruce Richardson 
9999a2dd95SBruce Richardson 		/*
10099a2dd95SBruce Richardson 		 *  The subtraction is done between two unsigned 32bits value
10199a2dd95SBruce Richardson 		 * (the result is always modulo 32 bits even if we have
102*3197a1ffSKonstantin Ananyev 		 * *old_head > cons_tail). So 'entries' is always between 0
10399a2dd95SBruce Richardson 		 * and capacity (which is < size).
10499a2dd95SBruce Richardson 		 */
105*3197a1ffSKonstantin Ananyev 		*entries = capacity + s->tail - op.pos.head;
10699a2dd95SBruce Richardson 
10799a2dd95SBruce Richardson 		/* check that we have enough room in ring */
108*3197a1ffSKonstantin Ananyev 		if (unlikely(n > *entries))
10999a2dd95SBruce Richardson 			n = (behavior == RTE_RING_QUEUE_FIXED) ?
110*3197a1ffSKonstantin Ananyev 					0 : *entries;
11199a2dd95SBruce Richardson 
11299a2dd95SBruce Richardson 		if (n == 0)
11399a2dd95SBruce Richardson 			break;
11499a2dd95SBruce Richardson 
11599a2dd95SBruce Richardson 		np.pos.tail = op.pos.tail;
11699a2dd95SBruce Richardson 		np.pos.head = op.pos.head + n;
11799a2dd95SBruce Richardson 
11899a2dd95SBruce Richardson 	/*
11999a2dd95SBruce Richardson 	 * this CAS(ACQUIRE, ACQUIRE) serves as a hoist barrier to prevent:
12099a2dd95SBruce Richardson 	 *  - OOO reads of cons tail value
12199a2dd95SBruce Richardson 	 *  - OOO copy of elems from the ring
12299a2dd95SBruce Richardson 	 */
123*3197a1ffSKonstantin Ananyev 	} while (rte_atomic_compare_exchange_strong_explicit(&d->ht.raw,
12432faaf30STyler Retzlaff 			(uint64_t *)(uintptr_t)&op.raw, np.raw,
125*3197a1ffSKonstantin Ananyev 			rte_memory_order_acquire,
126*3197a1ffSKonstantin Ananyev 			rte_memory_order_acquire) == 0);
12799a2dd95SBruce Richardson 
12899a2dd95SBruce Richardson 	*old_head = op.pos.head;
12999a2dd95SBruce Richardson 	return n;
13099a2dd95SBruce Richardson }
131*3197a1ffSKonstantin Ananyev /**
132*3197a1ffSKonstantin Ananyev  * @internal This function updates the producer head for enqueue
133*3197a1ffSKonstantin Ananyev  */
134*3197a1ffSKonstantin Ananyev static __rte_always_inline unsigned int
135*3197a1ffSKonstantin Ananyev __rte_ring_hts_move_prod_head(struct rte_ring *r, unsigned int num,
136*3197a1ffSKonstantin Ananyev 	enum rte_ring_queue_behavior behavior, uint32_t *old_head,
137*3197a1ffSKonstantin Ananyev 	uint32_t *free_entries)
138*3197a1ffSKonstantin Ananyev {
139*3197a1ffSKonstantin Ananyev 	return __rte_ring_hts_move_head(&r->hts_prod, &r->cons,
140*3197a1ffSKonstantin Ananyev 			r->capacity, num, behavior, old_head, free_entries);
141*3197a1ffSKonstantin Ananyev }
14299a2dd95SBruce Richardson 
14399a2dd95SBruce Richardson /**
14499a2dd95SBruce Richardson  * @internal This function updates the consumer head for dequeue
14599a2dd95SBruce Richardson  */
14699a2dd95SBruce Richardson static __rte_always_inline unsigned int
14799a2dd95SBruce Richardson __rte_ring_hts_move_cons_head(struct rte_ring *r, unsigned int num,
14899a2dd95SBruce Richardson 	enum rte_ring_queue_behavior behavior, uint32_t *old_head,
14999a2dd95SBruce Richardson 	uint32_t *entries)
15099a2dd95SBruce Richardson {
151*3197a1ffSKonstantin Ananyev 	return __rte_ring_hts_move_head(&r->hts_cons, &r->prod,
152*3197a1ffSKonstantin Ananyev 			0, num, behavior, old_head, entries);
15399a2dd95SBruce Richardson }
15499a2dd95SBruce Richardson 
15599a2dd95SBruce Richardson /**
15699a2dd95SBruce Richardson  * @internal Enqueue several objects on the HTS ring.
15799a2dd95SBruce Richardson  *
15899a2dd95SBruce Richardson  * @param r
15999a2dd95SBruce Richardson  *   A pointer to the ring structure.
16099a2dd95SBruce Richardson  * @param obj_table
16199a2dd95SBruce Richardson  *   A pointer to a table of objects.
16299a2dd95SBruce Richardson  * @param esize
16399a2dd95SBruce Richardson  *   The size of ring element, in bytes. It must be a multiple of 4.
16499a2dd95SBruce Richardson  *   This must be the same value used while creating the ring. Otherwise
16599a2dd95SBruce Richardson  *   the results are undefined.
16699a2dd95SBruce Richardson  * @param n
16799a2dd95SBruce Richardson  *   The number of objects to add in the ring from the obj_table.
16899a2dd95SBruce Richardson  * @param behavior
16999a2dd95SBruce Richardson  *   RTE_RING_QUEUE_FIXED:    Enqueue a fixed number of items from a ring
17099a2dd95SBruce Richardson  *   RTE_RING_QUEUE_VARIABLE: Enqueue as many items as possible from ring
17199a2dd95SBruce Richardson  * @param free_space
17299a2dd95SBruce Richardson  *   returns the amount of space after the enqueue operation has finished
17399a2dd95SBruce Richardson  * @return
17499a2dd95SBruce Richardson  *   Actual number of objects enqueued.
17599a2dd95SBruce Richardson  *   If behavior == RTE_RING_QUEUE_FIXED, this will be 0 or n only.
17699a2dd95SBruce Richardson  */
17799a2dd95SBruce Richardson static __rte_always_inline unsigned int
17899a2dd95SBruce Richardson __rte_ring_do_hts_enqueue_elem(struct rte_ring *r, const void *obj_table,
17999a2dd95SBruce Richardson 	uint32_t esize, uint32_t n, enum rte_ring_queue_behavior behavior,
18099a2dd95SBruce Richardson 	uint32_t *free_space)
18199a2dd95SBruce Richardson {
18299a2dd95SBruce Richardson 	uint32_t free, head;
18399a2dd95SBruce Richardson 
18499a2dd95SBruce Richardson 	n =  __rte_ring_hts_move_prod_head(r, n, behavior, &head, &free);
18599a2dd95SBruce Richardson 
18699a2dd95SBruce Richardson 	if (n != 0) {
18799a2dd95SBruce Richardson 		__rte_ring_enqueue_elems(r, head, obj_table, esize, n);
18899a2dd95SBruce Richardson 		__rte_ring_hts_update_tail(&r->hts_prod, head, n, 1);
18999a2dd95SBruce Richardson 	}
19099a2dd95SBruce Richardson 
19199a2dd95SBruce Richardson 	if (free_space != NULL)
19299a2dd95SBruce Richardson 		*free_space = free - n;
19399a2dd95SBruce Richardson 	return n;
19499a2dd95SBruce Richardson }
19599a2dd95SBruce Richardson 
19699a2dd95SBruce Richardson /**
19799a2dd95SBruce Richardson  * @internal Dequeue several objects from the HTS ring.
19899a2dd95SBruce Richardson  *
19999a2dd95SBruce Richardson  * @param r
20099a2dd95SBruce Richardson  *   A pointer to the ring structure.
20199a2dd95SBruce Richardson  * @param obj_table
20299a2dd95SBruce Richardson  *   A pointer to a table of objects.
20399a2dd95SBruce Richardson  * @param esize
20499a2dd95SBruce Richardson  *   The size of ring element, in bytes. It must be a multiple of 4.
20599a2dd95SBruce Richardson  *   This must be the same value used while creating the ring. Otherwise
20699a2dd95SBruce Richardson  *   the results are undefined.
20799a2dd95SBruce Richardson  * @param n
20899a2dd95SBruce Richardson  *   The number of objects to pull from the ring.
20999a2dd95SBruce Richardson  * @param behavior
21099a2dd95SBruce Richardson  *   RTE_RING_QUEUE_FIXED:    Dequeue a fixed number of items from a ring
21199a2dd95SBruce Richardson  *   RTE_RING_QUEUE_VARIABLE: Dequeue as many items as possible from ring
21299a2dd95SBruce Richardson  * @param available
21399a2dd95SBruce Richardson  *   returns the number of remaining ring entries after the dequeue has finished
21499a2dd95SBruce Richardson  * @return
21599a2dd95SBruce Richardson  *   - Actual number of objects dequeued.
21699a2dd95SBruce Richardson  *     If behavior == RTE_RING_QUEUE_FIXED, this will be 0 or n only.
21799a2dd95SBruce Richardson  */
21899a2dd95SBruce Richardson static __rte_always_inline unsigned int
21999a2dd95SBruce Richardson __rte_ring_do_hts_dequeue_elem(struct rte_ring *r, void *obj_table,
22099a2dd95SBruce Richardson 	uint32_t esize, uint32_t n, enum rte_ring_queue_behavior behavior,
22199a2dd95SBruce Richardson 	uint32_t *available)
22299a2dd95SBruce Richardson {
22399a2dd95SBruce Richardson 	uint32_t entries, head;
22499a2dd95SBruce Richardson 
22599a2dd95SBruce Richardson 	n = __rte_ring_hts_move_cons_head(r, n, behavior, &head, &entries);
22699a2dd95SBruce Richardson 
22799a2dd95SBruce Richardson 	if (n != 0) {
22899a2dd95SBruce Richardson 		__rte_ring_dequeue_elems(r, head, obj_table, esize, n);
22999a2dd95SBruce Richardson 		__rte_ring_hts_update_tail(&r->hts_cons, head, n, 0);
23099a2dd95SBruce Richardson 	}
23199a2dd95SBruce Richardson 
23299a2dd95SBruce Richardson 	if (available != NULL)
23399a2dd95SBruce Richardson 		*available = entries - n;
23499a2dd95SBruce Richardson 	return n;
23599a2dd95SBruce Richardson }
23699a2dd95SBruce Richardson 
23799a2dd95SBruce Richardson #endif /* _RTE_RING_HTS_ELEM_PVT_H_ */
238