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