DPDK 26.11.0-rc2
rte_stack_lf_c11.h
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2019 Intel Corporation
3 */
4
5#ifndef _RTE_STACK_LF_C11_H_
6#define _RTE_STACK_LF_C11_H_
7
9#include <rte_prefetch.h>
10
11#ifdef RTE_TOOLCHAIN_MSVC
21static inline int
22rte_atomic128_cmp_exchange(rte_int128_t *dst,
23 rte_int128_t *exp,
24 const rte_int128_t *src,
25 unsigned int weak,
26 int success,
27 int failure)
28{
29 return (int)_InterlockedCompareExchange128(
30 (int64_t volatile *) dst,
31 src->val[1], /* exchange high */
32 src->val[0], /* exchange low */
33 (int64_t *) exp /* comparand result */
34 );
35}
36#endif /* RTE_TOOLCHAIN_MSVC */
37
38static __rte_always_inline unsigned int
39__rte_stack_lf_elems_count(struct rte_stack_lf_list *list)
40{
41 return (unsigned int)rte_atomic_load_explicit(&list->len, rte_memory_order_relaxed);
42}
43
44static __rte_always_inline void
45__rte_stack_lf_push_elems(struct rte_stack_lf_list *list,
46 struct rte_stack_lf_elem *first,
47 struct rte_stack_lf_elem *last,
48 unsigned int num)
49{
50 struct rte_stack_lf_head old_head;
51 int success;
52
53 old_head = list->head;
54
55 do {
56 struct rte_stack_lf_head new_head;
57
58 /* Swing the top pointer to the first element in the list and
59 * make the last element point to the old top.
60 */
61 new_head.top = first;
62 new_head.cnt = old_head.cnt + 1;
63
64 last->next = old_head.top;
65
66 /* Use the release memmodel to ensure the writes to the LF LIFO
67 * elements are visible before the head pointer write.
68 */
70 (rte_int128_t *)&list->head,
71 (rte_int128_t *)&old_head,
72 (rte_int128_t *)&new_head,
73 1, rte_memory_order_release,
74 rte_memory_order_relaxed);
75 } while (success == 0);
76
77 /* Ensure the stack modifications are not reordered with respect
78 * to the LIFO len update.
79 */
80 rte_atomic_fetch_add_explicit(&list->len, num, rte_memory_order_release);
81}
82
83static __rte_always_inline struct rte_stack_lf_elem *
84__rte_stack_lf_pop_elems(struct rte_stack_lf_list *list,
85 unsigned int num,
86 void **obj_table,
87 struct rte_stack_lf_elem **last)
88{
89 struct rte_stack_lf_head old_head;
90 uint64_t len;
91 int success = 0;
92
93 /* Reserve num elements, if available */
94 len = rte_atomic_load_explicit(&list->len, rte_memory_order_relaxed);
95
96 while (1) {
97 /* Does the list contain enough elements? */
98 if (unlikely(len < num))
99 return NULL;
100
101 /* len is updated on failure */
102 if (rte_atomic_compare_exchange_weak_explicit(&list->len,
103 &len, len - num,
104 rte_memory_order_acquire,
105 rte_memory_order_relaxed))
106 break;
107 }
108
109 /* If a torn read occurs, the CAS will fail and set old_head to the
110 * correct/latest value.
111 */
112 old_head = list->head;
113
114 /* Pop num elements */
115 do {
116 struct rte_stack_lf_head new_head;
117 struct rte_stack_lf_elem *tmp;
118 unsigned int i;
119
120 /* Use the acquire memmodel to ensure the reads to the LF LIFO
121 * elements are properly ordered with respect to the head
122 * pointer read.
123 */
124 rte_atomic_thread_fence(rte_memory_order_acquire);
125
126 rte_prefetch0(old_head.top);
127
128 tmp = old_head.top;
129
130 /* Traverse the list to find the new head. A next pointer will
131 * either point to another element or NULL; if a thread
132 * encounters a pointer that has already been popped, the CAS
133 * will fail.
134 */
135 for (i = 0; i < num && tmp != NULL; i++) {
136 rte_prefetch0(tmp->next);
137 if (obj_table)
138 obj_table[i] = tmp->data;
139 if (last)
140 *last = tmp;
141 tmp = tmp->next;
142 }
143
144 /* If NULL was encountered, the list was modified while
145 * traversing it. Retry.
146 */
147 if (i != num) {
148 old_head = list->head;
149 continue;
150 }
151
152 new_head.top = tmp;
153 new_head.cnt = old_head.cnt + 1;
154
155 /*
156 * The CAS should have release semantics to ensure that
157 * items are read before the head is updated.
158 * But this function is internal, and items are read
159 * only when __rte_stack_lf_pop calls this function to
160 * pop items from used list.
161 * Then, those items are pushed to the free list.
162 * Push uses a CAS store-release on head, which makes
163 * sure that items are read before they are pushed to
164 * the free list, without need for a CAS release here.
165 * This CAS could also be used to ensure that the new
166 * length is visible before the head update, but
167 * acquire semantics on the length update is enough.
168 */
170 (rte_int128_t *)&list->head,
171 (rte_int128_t *)&old_head,
172 (rte_int128_t *)&new_head,
173 0, rte_memory_order_relaxed,
174 rte_memory_order_relaxed);
175 } while (success == 0);
176
177 return old_head.top;
178}
179
180#endif /* _RTE_STACK_LF_C11_H_ */
static int rte_atomic128_cmp_exchange(rte_int128_t *dst, rte_int128_t *exp, const rte_int128_t *src, unsigned int weak, int success, int failure)
static void rte_atomic_thread_fence(rte_memory_order memorder)
#define unlikely(x)
#define __rte_always_inline
Definition: rte_common.h:534
static void rte_prefetch0(const volatile void *p)