xref: /dpdk/lib/stack/rte_stack_lf_generic.h (revision 99a2dd955fba6e4cc23b77d590a033650ced9c45)
1*99a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
2*99a2dd95SBruce Richardson  * Copyright(c) 2019 Intel Corporation
3*99a2dd95SBruce Richardson  */
4*99a2dd95SBruce Richardson 
5*99a2dd95SBruce Richardson #ifndef _RTE_STACK_LF_GENERIC_H_
6*99a2dd95SBruce Richardson #define _RTE_STACK_LF_GENERIC_H_
7*99a2dd95SBruce Richardson 
8*99a2dd95SBruce Richardson #include <rte_branch_prediction.h>
9*99a2dd95SBruce Richardson #include <rte_prefetch.h>
10*99a2dd95SBruce Richardson 
11*99a2dd95SBruce Richardson static __rte_always_inline unsigned int
12*99a2dd95SBruce Richardson __rte_stack_lf_count(struct rte_stack *s)
13*99a2dd95SBruce Richardson {
14*99a2dd95SBruce Richardson 	/* stack_lf_push() and stack_lf_pop() do not update the list's contents
15*99a2dd95SBruce Richardson 	 * and stack_lf->len atomically, which can cause the list to appear
16*99a2dd95SBruce Richardson 	 * shorter than it actually is if this function is called while other
17*99a2dd95SBruce Richardson 	 * threads are modifying the list.
18*99a2dd95SBruce Richardson 	 *
19*99a2dd95SBruce Richardson 	 * However, given the inherently approximate nature of the get_count
20*99a2dd95SBruce Richardson 	 * callback -- even if the list and its size were updated atomically,
21*99a2dd95SBruce Richardson 	 * the size could change between when get_count executes and when the
22*99a2dd95SBruce Richardson 	 * value is returned to the caller -- this is acceptable.
23*99a2dd95SBruce Richardson 	 *
24*99a2dd95SBruce Richardson 	 * The stack_lf->len updates are placed such that the list may appear to
25*99a2dd95SBruce Richardson 	 * have fewer elements than it does, but will never appear to have more
26*99a2dd95SBruce Richardson 	 * elements. If the mempool is near-empty to the point that this is a
27*99a2dd95SBruce Richardson 	 * concern, the user should consider increasing the mempool size.
28*99a2dd95SBruce Richardson 	 */
29*99a2dd95SBruce Richardson 	return (unsigned int)rte_atomic64_read((rte_atomic64_t *)
30*99a2dd95SBruce Richardson 			&s->stack_lf.used.len);
31*99a2dd95SBruce Richardson }
32*99a2dd95SBruce Richardson 
33*99a2dd95SBruce Richardson static __rte_always_inline void
34*99a2dd95SBruce Richardson __rte_stack_lf_push_elems(struct rte_stack_lf_list *list,
35*99a2dd95SBruce Richardson 			  struct rte_stack_lf_elem *first,
36*99a2dd95SBruce Richardson 			  struct rte_stack_lf_elem *last,
37*99a2dd95SBruce Richardson 			  unsigned int num)
38*99a2dd95SBruce Richardson {
39*99a2dd95SBruce Richardson 	struct rte_stack_lf_head old_head;
40*99a2dd95SBruce Richardson 	int success;
41*99a2dd95SBruce Richardson 
42*99a2dd95SBruce Richardson 	old_head = list->head;
43*99a2dd95SBruce Richardson 
44*99a2dd95SBruce Richardson 	do {
45*99a2dd95SBruce Richardson 		struct rte_stack_lf_head new_head;
46*99a2dd95SBruce Richardson 
47*99a2dd95SBruce Richardson 		/* An acquire fence (or stronger) is needed for weak memory
48*99a2dd95SBruce Richardson 		 * models to establish a synchronized-with relationship between
49*99a2dd95SBruce Richardson 		 * the list->head load and store-release operations (as part of
50*99a2dd95SBruce Richardson 		 * the rte_atomic128_cmp_exchange()).
51*99a2dd95SBruce Richardson 		 */
52*99a2dd95SBruce Richardson 		rte_smp_mb();
53*99a2dd95SBruce Richardson 
54*99a2dd95SBruce Richardson 		/* Swing the top pointer to the first element in the list and
55*99a2dd95SBruce Richardson 		 * make the last element point to the old top.
56*99a2dd95SBruce Richardson 		 */
57*99a2dd95SBruce Richardson 		new_head.top = first;
58*99a2dd95SBruce Richardson 		new_head.cnt = old_head.cnt + 1;
59*99a2dd95SBruce Richardson 
60*99a2dd95SBruce Richardson 		last->next = old_head.top;
61*99a2dd95SBruce Richardson 
62*99a2dd95SBruce Richardson 		/* old_head is updated on failure */
63*99a2dd95SBruce Richardson 		success = rte_atomic128_cmp_exchange(
64*99a2dd95SBruce Richardson 				(rte_int128_t *)&list->head,
65*99a2dd95SBruce Richardson 				(rte_int128_t *)&old_head,
66*99a2dd95SBruce Richardson 				(rte_int128_t *)&new_head,
67*99a2dd95SBruce Richardson 				1, __ATOMIC_RELEASE,
68*99a2dd95SBruce Richardson 				__ATOMIC_RELAXED);
69*99a2dd95SBruce Richardson 	} while (success == 0);
70*99a2dd95SBruce Richardson 
71*99a2dd95SBruce Richardson 	rte_atomic64_add((rte_atomic64_t *)&list->len, num);
72*99a2dd95SBruce Richardson }
73*99a2dd95SBruce Richardson 
74*99a2dd95SBruce Richardson static __rte_always_inline struct rte_stack_lf_elem *
75*99a2dd95SBruce Richardson __rte_stack_lf_pop_elems(struct rte_stack_lf_list *list,
76*99a2dd95SBruce Richardson 			 unsigned int num,
77*99a2dd95SBruce Richardson 			 void **obj_table,
78*99a2dd95SBruce Richardson 			 struct rte_stack_lf_elem **last)
79*99a2dd95SBruce Richardson {
80*99a2dd95SBruce Richardson 	struct rte_stack_lf_head old_head;
81*99a2dd95SBruce Richardson 	int success = 0;
82*99a2dd95SBruce Richardson 
83*99a2dd95SBruce Richardson 	/* Reserve num elements, if available */
84*99a2dd95SBruce Richardson 	while (1) {
85*99a2dd95SBruce Richardson 		uint64_t len = rte_atomic64_read((rte_atomic64_t *)&list->len);
86*99a2dd95SBruce Richardson 
87*99a2dd95SBruce Richardson 		/* Does the list contain enough elements? */
88*99a2dd95SBruce Richardson 		if (unlikely(len < num))
89*99a2dd95SBruce Richardson 			return NULL;
90*99a2dd95SBruce Richardson 
91*99a2dd95SBruce Richardson 		if (rte_atomic64_cmpset((volatile uint64_t *)&list->len,
92*99a2dd95SBruce Richardson 					len, len - num))
93*99a2dd95SBruce Richardson 			break;
94*99a2dd95SBruce Richardson 	}
95*99a2dd95SBruce Richardson 
96*99a2dd95SBruce Richardson 	old_head = list->head;
97*99a2dd95SBruce Richardson 
98*99a2dd95SBruce Richardson 	/* Pop num elements */
99*99a2dd95SBruce Richardson 	do {
100*99a2dd95SBruce Richardson 		struct rte_stack_lf_head new_head;
101*99a2dd95SBruce Richardson 		struct rte_stack_lf_elem *tmp;
102*99a2dd95SBruce Richardson 		unsigned int i;
103*99a2dd95SBruce Richardson 
104*99a2dd95SBruce Richardson 		/* An acquire fence (or stronger) is needed for weak memory
105*99a2dd95SBruce Richardson 		 * models to ensure the LF LIFO element reads are properly
106*99a2dd95SBruce Richardson 		 * ordered with respect to the head pointer read.
107*99a2dd95SBruce Richardson 		 */
108*99a2dd95SBruce Richardson 		rte_smp_mb();
109*99a2dd95SBruce Richardson 
110*99a2dd95SBruce Richardson 		rte_prefetch0(old_head.top);
111*99a2dd95SBruce Richardson 
112*99a2dd95SBruce Richardson 		tmp = old_head.top;
113*99a2dd95SBruce Richardson 
114*99a2dd95SBruce Richardson 		/* Traverse the list to find the new head. A next pointer will
115*99a2dd95SBruce Richardson 		 * either point to another element or NULL; if a thread
116*99a2dd95SBruce Richardson 		 * encounters a pointer that has already been popped, the CAS
117*99a2dd95SBruce Richardson 		 * will fail.
118*99a2dd95SBruce Richardson 		 */
119*99a2dd95SBruce Richardson 		for (i = 0; i < num && tmp != NULL; i++) {
120*99a2dd95SBruce Richardson 			rte_prefetch0(tmp->next);
121*99a2dd95SBruce Richardson 			if (obj_table)
122*99a2dd95SBruce Richardson 				obj_table[i] = tmp->data;
123*99a2dd95SBruce Richardson 			if (last)
124*99a2dd95SBruce Richardson 				*last = tmp;
125*99a2dd95SBruce Richardson 			tmp = tmp->next;
126*99a2dd95SBruce Richardson 		}
127*99a2dd95SBruce Richardson 
128*99a2dd95SBruce Richardson 		/* If NULL was encountered, the list was modified while
129*99a2dd95SBruce Richardson 		 * traversing it. Retry.
130*99a2dd95SBruce Richardson 		 */
131*99a2dd95SBruce Richardson 		if (i != num)
132*99a2dd95SBruce Richardson 			continue;
133*99a2dd95SBruce Richardson 
134*99a2dd95SBruce Richardson 		new_head.top = tmp;
135*99a2dd95SBruce Richardson 		new_head.cnt = old_head.cnt + 1;
136*99a2dd95SBruce Richardson 
137*99a2dd95SBruce Richardson 		/* old_head is updated on failure */
138*99a2dd95SBruce Richardson 		success = rte_atomic128_cmp_exchange(
139*99a2dd95SBruce Richardson 				(rte_int128_t *)&list->head,
140*99a2dd95SBruce Richardson 				(rte_int128_t *)&old_head,
141*99a2dd95SBruce Richardson 				(rte_int128_t *)&new_head,
142*99a2dd95SBruce Richardson 				1, __ATOMIC_RELEASE,
143*99a2dd95SBruce Richardson 				__ATOMIC_RELAXED);
144*99a2dd95SBruce Richardson 	} while (success == 0);
145*99a2dd95SBruce Richardson 
146*99a2dd95SBruce Richardson 	return old_head.top;
147*99a2dd95SBruce Richardson }
148*99a2dd95SBruce Richardson 
149*99a2dd95SBruce Richardson #endif /* _RTE_STACK_LF_GENERIC_H_ */
150