xref: /dpdk/lib/stack/rte_stack_lf_c11.h (revision 283d843722f11c4cb4714fa8661f4cfb7986b0e6)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2019 Intel Corporation
399a2dd95SBruce Richardson  */
499a2dd95SBruce Richardson 
599a2dd95SBruce Richardson #ifndef _RTE_STACK_LF_C11_H_
699a2dd95SBruce Richardson #define _RTE_STACK_LF_C11_H_
799a2dd95SBruce Richardson 
899a2dd95SBruce Richardson #include <rte_branch_prediction.h>
999a2dd95SBruce Richardson #include <rte_prefetch.h>
1099a2dd95SBruce Richardson 
1199a2dd95SBruce Richardson static __rte_always_inline unsigned int
__rte_stack_lf_count(struct rte_stack * s)1299a2dd95SBruce Richardson __rte_stack_lf_count(struct rte_stack *s)
1399a2dd95SBruce Richardson {
1499a2dd95SBruce Richardson 	/* stack_lf_push() and stack_lf_pop() do not update the list's contents
1599a2dd95SBruce Richardson 	 * and stack_lf->len atomically, which can cause the list to appear
1699a2dd95SBruce Richardson 	 * shorter than it actually is if this function is called while other
1799a2dd95SBruce Richardson 	 * threads are modifying the list.
1899a2dd95SBruce Richardson 	 *
1999a2dd95SBruce Richardson 	 * However, given the inherently approximate nature of the get_count
2099a2dd95SBruce Richardson 	 * callback -- even if the list and its size were updated atomically,
2199a2dd95SBruce Richardson 	 * the size could change between when get_count executes and when the
2299a2dd95SBruce Richardson 	 * value is returned to the caller -- this is acceptable.
2399a2dd95SBruce Richardson 	 *
2499a2dd95SBruce Richardson 	 * The stack_lf->len updates are placed such that the list may appear to
2599a2dd95SBruce Richardson 	 * have fewer elements than it does, but will never appear to have more
2699a2dd95SBruce Richardson 	 * elements. If the mempool is near-empty to the point that this is a
2799a2dd95SBruce Richardson 	 * concern, the user should consider increasing the mempool size.
2899a2dd95SBruce Richardson 	 */
29b6a19ec1STyler Retzlaff 	return (unsigned int)rte_atomic_load_explicit(&s->stack_lf.used.len,
30b6a19ec1STyler Retzlaff 					     rte_memory_order_relaxed);
3199a2dd95SBruce Richardson }
3299a2dd95SBruce Richardson 
3399a2dd95SBruce Richardson static __rte_always_inline void
__rte_stack_lf_push_elems(struct rte_stack_lf_list * list,struct rte_stack_lf_elem * first,struct rte_stack_lf_elem * last,unsigned int num)3499a2dd95SBruce Richardson __rte_stack_lf_push_elems(struct rte_stack_lf_list *list,
3599a2dd95SBruce Richardson 			  struct rte_stack_lf_elem *first,
3699a2dd95SBruce Richardson 			  struct rte_stack_lf_elem *last,
3799a2dd95SBruce Richardson 			  unsigned int num)
3899a2dd95SBruce Richardson {
3999a2dd95SBruce Richardson 	struct rte_stack_lf_head old_head;
4099a2dd95SBruce Richardson 	int success;
4199a2dd95SBruce Richardson 
4299a2dd95SBruce Richardson 	old_head = list->head;
4399a2dd95SBruce Richardson 
4499a2dd95SBruce Richardson 	do {
4599a2dd95SBruce Richardson 		struct rte_stack_lf_head new_head;
4699a2dd95SBruce Richardson 
4799a2dd95SBruce Richardson 		/* Swing the top pointer to the first element in the list and
4899a2dd95SBruce Richardson 		 * make the last element point to the old top.
4999a2dd95SBruce Richardson 		 */
5099a2dd95SBruce Richardson 		new_head.top = first;
5199a2dd95SBruce Richardson 		new_head.cnt = old_head.cnt + 1;
5299a2dd95SBruce Richardson 
5399a2dd95SBruce Richardson 		last->next = old_head.top;
5499a2dd95SBruce Richardson 
5599a2dd95SBruce Richardson 		/* Use the release memmodel to ensure the writes to the LF LIFO
5699a2dd95SBruce Richardson 		 * elements are visible before the head pointer write.
5799a2dd95SBruce Richardson 		 */
5899a2dd95SBruce Richardson 		success = rte_atomic128_cmp_exchange(
5999a2dd95SBruce Richardson 				(rte_int128_t *)&list->head,
6099a2dd95SBruce Richardson 				(rte_int128_t *)&old_head,
6199a2dd95SBruce Richardson 				(rte_int128_t *)&new_head,
62b6a19ec1STyler Retzlaff 				1, rte_memory_order_release,
63b6a19ec1STyler Retzlaff 				rte_memory_order_relaxed);
6499a2dd95SBruce Richardson 	} while (success == 0);
6599a2dd95SBruce Richardson 
6699a2dd95SBruce Richardson 	/* Ensure the stack modifications are not reordered with respect
6799a2dd95SBruce Richardson 	 * to the LIFO len update.
6899a2dd95SBruce Richardson 	 */
69b6a19ec1STyler Retzlaff 	rte_atomic_fetch_add_explicit(&list->len, num, rte_memory_order_release);
7099a2dd95SBruce Richardson }
7199a2dd95SBruce Richardson 
7299a2dd95SBruce Richardson static __rte_always_inline struct rte_stack_lf_elem *
__rte_stack_lf_pop_elems(struct rte_stack_lf_list * list,unsigned int num,void ** obj_table,struct rte_stack_lf_elem ** last)7399a2dd95SBruce Richardson __rte_stack_lf_pop_elems(struct rte_stack_lf_list *list,
7499a2dd95SBruce Richardson 			 unsigned int num,
7599a2dd95SBruce Richardson 			 void **obj_table,
7699a2dd95SBruce Richardson 			 struct rte_stack_lf_elem **last)
7799a2dd95SBruce Richardson {
7899a2dd95SBruce Richardson 	struct rte_stack_lf_head old_head;
7999a2dd95SBruce Richardson 	uint64_t len;
8099a2dd95SBruce Richardson 	int success;
8199a2dd95SBruce Richardson 
8299a2dd95SBruce Richardson 	/* Reserve num elements, if available */
83b6a19ec1STyler Retzlaff 	len = rte_atomic_load_explicit(&list->len, rte_memory_order_relaxed);
8499a2dd95SBruce Richardson 
8599a2dd95SBruce Richardson 	while (1) {
8699a2dd95SBruce Richardson 		/* Does the list contain enough elements? */
8799a2dd95SBruce Richardson 		if (unlikely(len < num))
8899a2dd95SBruce Richardson 			return NULL;
8999a2dd95SBruce Richardson 
9099a2dd95SBruce Richardson 		/* len is updated on failure */
91b6a19ec1STyler Retzlaff 		if (rte_atomic_compare_exchange_weak_explicit(&list->len,
9299a2dd95SBruce Richardson 						&len, len - num,
93b6a19ec1STyler Retzlaff 						rte_memory_order_acquire,
94b6a19ec1STyler Retzlaff 						rte_memory_order_relaxed))
9599a2dd95SBruce Richardson 			break;
9699a2dd95SBruce Richardson 	}
9799a2dd95SBruce Richardson 
9899a2dd95SBruce Richardson 	/* If a torn read occurs, the CAS will fail and set old_head to the
9999a2dd95SBruce Richardson 	 * correct/latest value.
10099a2dd95SBruce Richardson 	 */
10199a2dd95SBruce Richardson 	old_head = list->head;
10299a2dd95SBruce Richardson 
10399a2dd95SBruce Richardson 	/* Pop num elements */
10499a2dd95SBruce Richardson 	do {
10599a2dd95SBruce Richardson 		struct rte_stack_lf_head new_head;
10699a2dd95SBruce Richardson 		struct rte_stack_lf_elem *tmp;
10799a2dd95SBruce Richardson 		unsigned int i;
10899a2dd95SBruce Richardson 
10999a2dd95SBruce Richardson 		/* Use the acquire memmodel to ensure the reads to the LF LIFO
11099a2dd95SBruce Richardson 		 * elements are properly ordered with respect to the head
11199a2dd95SBruce Richardson 		 * pointer read.
11299a2dd95SBruce Richardson 		 */
113*283d8437STyler Retzlaff 		rte_atomic_thread_fence(rte_memory_order_acquire);
11499a2dd95SBruce Richardson 
11599a2dd95SBruce Richardson 		rte_prefetch0(old_head.top);
11699a2dd95SBruce Richardson 
11799a2dd95SBruce Richardson 		tmp = old_head.top;
11899a2dd95SBruce Richardson 
11999a2dd95SBruce Richardson 		/* Traverse the list to find the new head. A next pointer will
12099a2dd95SBruce Richardson 		 * either point to another element or NULL; if a thread
12199a2dd95SBruce Richardson 		 * encounters a pointer that has already been popped, the CAS
12299a2dd95SBruce Richardson 		 * will fail.
12399a2dd95SBruce Richardson 		 */
12499a2dd95SBruce Richardson 		for (i = 0; i < num && tmp != NULL; i++) {
12599a2dd95SBruce Richardson 			rte_prefetch0(tmp->next);
12699a2dd95SBruce Richardson 			if (obj_table)
12799a2dd95SBruce Richardson 				obj_table[i] = tmp->data;
12899a2dd95SBruce Richardson 			if (last)
12999a2dd95SBruce Richardson 				*last = tmp;
13099a2dd95SBruce Richardson 			tmp = tmp->next;
13199a2dd95SBruce Richardson 		}
13299a2dd95SBruce Richardson 
13399a2dd95SBruce Richardson 		/* If NULL was encountered, the list was modified while
13499a2dd95SBruce Richardson 		 * traversing it. Retry.
13599a2dd95SBruce Richardson 		 */
13699a2dd95SBruce Richardson 		if (i != num) {
13799a2dd95SBruce Richardson 			old_head = list->head;
13899a2dd95SBruce Richardson 			continue;
13999a2dd95SBruce Richardson 		}
14099a2dd95SBruce Richardson 
14199a2dd95SBruce Richardson 		new_head.top = tmp;
14299a2dd95SBruce Richardson 		new_head.cnt = old_head.cnt + 1;
14399a2dd95SBruce Richardson 
14499a2dd95SBruce Richardson 		/*
14599a2dd95SBruce Richardson 		 * The CAS should have release semantics to ensure that
14699a2dd95SBruce Richardson 		 * items are read before the head is updated.
14799a2dd95SBruce Richardson 		 * But this function is internal, and items are read
14899a2dd95SBruce Richardson 		 * only when __rte_stack_lf_pop calls this function to
14999a2dd95SBruce Richardson 		 * pop items from used list.
15099a2dd95SBruce Richardson 		 * Then, those items are pushed to the free list.
15199a2dd95SBruce Richardson 		 * Push uses a CAS store-release on head, which makes
15299a2dd95SBruce Richardson 		 * sure that items are read before they are pushed to
15399a2dd95SBruce Richardson 		 * the free list, without need for a CAS release here.
15499a2dd95SBruce Richardson 		 * This CAS could also be used to ensure that the new
15599a2dd95SBruce Richardson 		 * length is visible before the head update, but
15699a2dd95SBruce Richardson 		 * acquire semantics on the length update is enough.
15799a2dd95SBruce Richardson 		 */
15899a2dd95SBruce Richardson 		success = rte_atomic128_cmp_exchange(
15999a2dd95SBruce Richardson 				(rte_int128_t *)&list->head,
16099a2dd95SBruce Richardson 				(rte_int128_t *)&old_head,
16199a2dd95SBruce Richardson 				(rte_int128_t *)&new_head,
162b6a19ec1STyler Retzlaff 				0, rte_memory_order_relaxed,
163b6a19ec1STyler Retzlaff 				rte_memory_order_relaxed);
16499a2dd95SBruce Richardson 	} while (success == 0);
16599a2dd95SBruce Richardson 
16699a2dd95SBruce Richardson 	return old_head.top;
16799a2dd95SBruce Richardson }
16899a2dd95SBruce Richardson 
16999a2dd95SBruce Richardson #endif /* _RTE_STACK_LF_C11_H_ */
170