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_GENERIC_H_ 699a2dd95SBruce Richardson #define _RTE_STACK_LF_GENERIC_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 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 */ 29*08e29b37STyler Retzlaff /* NOTE: review for potential ordering optimization */ 30*08e29b37STyler Retzlaff return __atomic_load_n(&s->stack_lf.used.len, __ATOMIC_SEQ_CST); 3199a2dd95SBruce Richardson } 3299a2dd95SBruce Richardson 3399a2dd95SBruce Richardson static __rte_always_inline void 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 /* An acquire fence (or stronger) is needed for weak memory 4899a2dd95SBruce Richardson * models to establish a synchronized-with relationship between 4999a2dd95SBruce Richardson * the list->head load and store-release operations (as part of 5099a2dd95SBruce Richardson * the rte_atomic128_cmp_exchange()). 5199a2dd95SBruce Richardson */ 5299a2dd95SBruce Richardson rte_smp_mb(); 5399a2dd95SBruce Richardson 5499a2dd95SBruce Richardson /* Swing the top pointer to the first element in the list and 5599a2dd95SBruce Richardson * make the last element point to the old top. 5699a2dd95SBruce Richardson */ 5799a2dd95SBruce Richardson new_head.top = first; 5899a2dd95SBruce Richardson new_head.cnt = old_head.cnt + 1; 5999a2dd95SBruce Richardson 6099a2dd95SBruce Richardson last->next = old_head.top; 6199a2dd95SBruce Richardson 6299a2dd95SBruce Richardson /* old_head is updated on failure */ 6399a2dd95SBruce Richardson success = rte_atomic128_cmp_exchange( 6499a2dd95SBruce Richardson (rte_int128_t *)&list->head, 6599a2dd95SBruce Richardson (rte_int128_t *)&old_head, 6699a2dd95SBruce Richardson (rte_int128_t *)&new_head, 6799a2dd95SBruce Richardson 1, __ATOMIC_RELEASE, 6899a2dd95SBruce Richardson __ATOMIC_RELAXED); 6999a2dd95SBruce Richardson } while (success == 0); 70*08e29b37STyler Retzlaff /* NOTE: review for potential ordering optimization */ 71*08e29b37STyler Retzlaff __atomic_fetch_add(&list->len, num, __ATOMIC_SEQ_CST); 7299a2dd95SBruce Richardson } 7399a2dd95SBruce Richardson 7499a2dd95SBruce Richardson static __rte_always_inline struct rte_stack_lf_elem * 7599a2dd95SBruce Richardson __rte_stack_lf_pop_elems(struct rte_stack_lf_list *list, 7699a2dd95SBruce Richardson unsigned int num, 7799a2dd95SBruce Richardson void **obj_table, 7899a2dd95SBruce Richardson struct rte_stack_lf_elem **last) 7999a2dd95SBruce Richardson { 8099a2dd95SBruce Richardson struct rte_stack_lf_head old_head; 8199a2dd95SBruce Richardson int success = 0; 8299a2dd95SBruce Richardson 8399a2dd95SBruce Richardson /* Reserve num elements, if available */ 8499a2dd95SBruce Richardson while (1) { 85*08e29b37STyler Retzlaff /* NOTE: review for potential ordering optimization */ 86*08e29b37STyler Retzlaff uint64_t len = __atomic_load_n(&list->len, __ATOMIC_SEQ_CST); 8799a2dd95SBruce Richardson 8899a2dd95SBruce Richardson /* Does the list contain enough elements? */ 8999a2dd95SBruce Richardson if (unlikely(len < num)) 9099a2dd95SBruce Richardson return NULL; 9199a2dd95SBruce Richardson 92*08e29b37STyler Retzlaff /* NOTE: review for potential ordering optimization */ 93*08e29b37STyler Retzlaff if (__atomic_compare_exchange_n(&list->len, &len, len - num, 94*08e29b37STyler Retzlaff 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) 9599a2dd95SBruce Richardson break; 9699a2dd95SBruce Richardson } 9799a2dd95SBruce Richardson 9899a2dd95SBruce Richardson old_head = list->head; 9999a2dd95SBruce Richardson 10099a2dd95SBruce Richardson /* Pop num elements */ 10199a2dd95SBruce Richardson do { 10299a2dd95SBruce Richardson struct rte_stack_lf_head new_head; 10399a2dd95SBruce Richardson struct rte_stack_lf_elem *tmp; 10499a2dd95SBruce Richardson unsigned int i; 10599a2dd95SBruce Richardson 10699a2dd95SBruce Richardson /* An acquire fence (or stronger) is needed for weak memory 10799a2dd95SBruce Richardson * models to ensure the LF LIFO element reads are properly 10899a2dd95SBruce Richardson * ordered with respect to the head pointer read. 10999a2dd95SBruce Richardson */ 11099a2dd95SBruce Richardson rte_smp_mb(); 11199a2dd95SBruce Richardson 11299a2dd95SBruce Richardson rte_prefetch0(old_head.top); 11399a2dd95SBruce Richardson 11499a2dd95SBruce Richardson tmp = old_head.top; 11599a2dd95SBruce Richardson 11699a2dd95SBruce Richardson /* Traverse the list to find the new head. A next pointer will 11799a2dd95SBruce Richardson * either point to another element or NULL; if a thread 11899a2dd95SBruce Richardson * encounters a pointer that has already been popped, the CAS 11999a2dd95SBruce Richardson * will fail. 12099a2dd95SBruce Richardson */ 12199a2dd95SBruce Richardson for (i = 0; i < num && tmp != NULL; i++) { 12299a2dd95SBruce Richardson rte_prefetch0(tmp->next); 12399a2dd95SBruce Richardson if (obj_table) 12499a2dd95SBruce Richardson obj_table[i] = tmp->data; 12599a2dd95SBruce Richardson if (last) 12699a2dd95SBruce Richardson *last = tmp; 12799a2dd95SBruce Richardson tmp = tmp->next; 12899a2dd95SBruce Richardson } 12999a2dd95SBruce Richardson 13099a2dd95SBruce Richardson /* If NULL was encountered, the list was modified while 13199a2dd95SBruce Richardson * traversing it. Retry. 13299a2dd95SBruce Richardson */ 1336ded44bcSJulien Meunier if (i != num) { 1346ded44bcSJulien Meunier old_head = list->head; 13599a2dd95SBruce Richardson continue; 1366ded44bcSJulien Meunier } 13799a2dd95SBruce Richardson 13899a2dd95SBruce Richardson new_head.top = tmp; 13999a2dd95SBruce Richardson new_head.cnt = old_head.cnt + 1; 14099a2dd95SBruce Richardson 14199a2dd95SBruce Richardson /* old_head is updated on failure */ 14299a2dd95SBruce Richardson success = rte_atomic128_cmp_exchange( 14399a2dd95SBruce Richardson (rte_int128_t *)&list->head, 14499a2dd95SBruce Richardson (rte_int128_t *)&old_head, 14599a2dd95SBruce Richardson (rte_int128_t *)&new_head, 14699a2dd95SBruce Richardson 1, __ATOMIC_RELEASE, 14799a2dd95SBruce Richardson __ATOMIC_RELAXED); 14899a2dd95SBruce Richardson } while (success == 0); 14999a2dd95SBruce Richardson 15099a2dd95SBruce Richardson return old_head.top; 15199a2dd95SBruce Richardson } 15299a2dd95SBruce Richardson 15399a2dd95SBruce Richardson #endif /* _RTE_STACK_LF_GENERIC_H_ */ 154