199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 299a2dd95SBruce Richardson * 399a2dd95SBruce Richardson * Copyright (c) 2010-2020 Intel Corporation 499a2dd95SBruce Richardson * Copyright (c) 2007-2009 Kip Macy kmacy@freebsd.org 599a2dd95SBruce Richardson * All rights reserved. 699a2dd95SBruce Richardson * Derived from FreeBSD's bufring.h 799a2dd95SBruce Richardson * Used as BSD-3 Licensed with permission from Kip Macy. 899a2dd95SBruce Richardson */ 999a2dd95SBruce Richardson 1099a2dd95SBruce Richardson #ifndef _RTE_RING_HTS_ELEM_PVT_H_ 1199a2dd95SBruce Richardson #define _RTE_RING_HTS_ELEM_PVT_H_ 1299a2dd95SBruce Richardson 1332faaf30STyler Retzlaff #include <rte_stdatomic.h> 1432faaf30STyler Retzlaff 1599a2dd95SBruce Richardson /** 1699a2dd95SBruce Richardson * @file rte_ring_hts_elem_pvt.h 1799a2dd95SBruce Richardson * It is not recommended to include this file directly, 1899a2dd95SBruce Richardson * include <rte_ring.h> instead. 1999a2dd95SBruce Richardson * Contains internal helper functions for head/tail sync (HTS) ring mode. 2099a2dd95SBruce Richardson * For more information please refer to <rte_ring_hts.h>. 2199a2dd95SBruce Richardson */ 2299a2dd95SBruce Richardson 2399a2dd95SBruce Richardson /** 2499a2dd95SBruce Richardson * @internal update tail with new value. 2599a2dd95SBruce Richardson */ 2699a2dd95SBruce Richardson static __rte_always_inline void 2799a2dd95SBruce Richardson __rte_ring_hts_update_tail(struct rte_ring_hts_headtail *ht, uint32_t old_tail, 2899a2dd95SBruce Richardson uint32_t num, uint32_t enqueue) 2999a2dd95SBruce Richardson { 3099a2dd95SBruce Richardson uint32_t tail; 3199a2dd95SBruce Richardson 3299a2dd95SBruce Richardson RTE_SET_USED(enqueue); 3399a2dd95SBruce Richardson 3499a2dd95SBruce Richardson tail = old_tail + num; 3532faaf30STyler Retzlaff rte_atomic_store_explicit(&ht->ht.pos.tail, tail, rte_memory_order_release); 3699a2dd95SBruce Richardson } 3799a2dd95SBruce Richardson 3899a2dd95SBruce Richardson /** 3999a2dd95SBruce Richardson * @internal waits till tail will become equal to head. 4099a2dd95SBruce Richardson * Means no writer/reader is active for that ring. 4199a2dd95SBruce Richardson * Suppose to work as serialization point. 4299a2dd95SBruce Richardson */ 4399a2dd95SBruce Richardson static __rte_always_inline void 4499a2dd95SBruce Richardson __rte_ring_hts_head_wait(const struct rte_ring_hts_headtail *ht, 4599a2dd95SBruce Richardson union __rte_ring_hts_pos *p) 4699a2dd95SBruce Richardson { 4799a2dd95SBruce Richardson while (p->pos.head != p->pos.tail) { 4899a2dd95SBruce Richardson rte_pause(); 4932faaf30STyler Retzlaff p->raw = rte_atomic_load_explicit(&ht->ht.raw, rte_memory_order_acquire); 5099a2dd95SBruce Richardson } 5199a2dd95SBruce Richardson } 5299a2dd95SBruce Richardson 5399a2dd95SBruce Richardson /** 54*3197a1ffSKonstantin Ananyev * @internal This is a helper function that moves the producer/consumer head 55*3197a1ffSKonstantin Ananyev * 56*3197a1ffSKonstantin Ananyev * @param d 57*3197a1ffSKonstantin Ananyev * A pointer to the headtail structure with head value to be moved 58*3197a1ffSKonstantin Ananyev * @param s 59*3197a1ffSKonstantin Ananyev * A pointer to the counter-part headtail structure. Note that this 60*3197a1ffSKonstantin Ananyev * function only reads tail value from it 61*3197a1ffSKonstantin Ananyev * @param capacity 62*3197a1ffSKonstantin Ananyev * Either ring capacity value (for producer), or zero (for consumer) 63*3197a1ffSKonstantin Ananyev * Indicates whether multi-thread safe path is needed or not 64*3197a1ffSKonstantin Ananyev * @param num 65*3197a1ffSKonstantin Ananyev * The number of elements we want to move head value on 66*3197a1ffSKonstantin Ananyev * @param behavior 67*3197a1ffSKonstantin Ananyev * RTE_RING_QUEUE_FIXED: Move on a fixed number of items 68*3197a1ffSKonstantin Ananyev * RTE_RING_QUEUE_VARIABLE: Move on as many items as possible 69*3197a1ffSKonstantin Ananyev * @param old_head 70*3197a1ffSKonstantin Ananyev * Returns head value as it was before the move 71*3197a1ffSKonstantin Ananyev * @param entries 72*3197a1ffSKonstantin Ananyev * Returns the number of ring entries available BEFORE head was moved 73*3197a1ffSKonstantin Ananyev * @return 74*3197a1ffSKonstantin Ananyev * Actual number of objects the head was moved on 75*3197a1ffSKonstantin Ananyev * If behavior == RTE_RING_QUEUE_FIXED, this will be 0 or n only 7699a2dd95SBruce Richardson */ 77*3197a1ffSKonstantin Ananyev static __rte_always_inline uint32_t 78*3197a1ffSKonstantin Ananyev __rte_ring_hts_move_head(struct rte_ring_hts_headtail *d, 79*3197a1ffSKonstantin Ananyev const struct rte_ring_headtail *s, uint32_t capacity, unsigned int num, 8099a2dd95SBruce Richardson enum rte_ring_queue_behavior behavior, uint32_t *old_head, 81*3197a1ffSKonstantin Ananyev uint32_t *entries) 8299a2dd95SBruce Richardson { 8399a2dd95SBruce Richardson uint32_t n; 8499a2dd95SBruce Richardson union __rte_ring_hts_pos np, op; 8599a2dd95SBruce Richardson 86*3197a1ffSKonstantin Ananyev op.raw = rte_atomic_load_explicit(&d->ht.raw, rte_memory_order_acquire); 8799a2dd95SBruce Richardson 8899a2dd95SBruce Richardson do { 8999a2dd95SBruce Richardson /* Reset n to the initial burst count */ 9099a2dd95SBruce Richardson n = num; 9199a2dd95SBruce Richardson 9299a2dd95SBruce Richardson /* 9399a2dd95SBruce Richardson * wait for tail to be equal to head, 9499a2dd95SBruce Richardson * make sure that we read prod head/tail *before* 9599a2dd95SBruce Richardson * reading cons tail. 9699a2dd95SBruce Richardson */ 97*3197a1ffSKonstantin Ananyev __rte_ring_hts_head_wait(d, &op); 9899a2dd95SBruce Richardson 9999a2dd95SBruce Richardson /* 10099a2dd95SBruce Richardson * The subtraction is done between two unsigned 32bits value 10199a2dd95SBruce Richardson * (the result is always modulo 32 bits even if we have 102*3197a1ffSKonstantin Ananyev * *old_head > cons_tail). So 'entries' is always between 0 10399a2dd95SBruce Richardson * and capacity (which is < size). 10499a2dd95SBruce Richardson */ 105*3197a1ffSKonstantin Ananyev *entries = capacity + s->tail - op.pos.head; 10699a2dd95SBruce Richardson 10799a2dd95SBruce Richardson /* check that we have enough room in ring */ 108*3197a1ffSKonstantin Ananyev if (unlikely(n > *entries)) 10999a2dd95SBruce Richardson n = (behavior == RTE_RING_QUEUE_FIXED) ? 110*3197a1ffSKonstantin Ananyev 0 : *entries; 11199a2dd95SBruce Richardson 11299a2dd95SBruce Richardson if (n == 0) 11399a2dd95SBruce Richardson break; 11499a2dd95SBruce Richardson 11599a2dd95SBruce Richardson np.pos.tail = op.pos.tail; 11699a2dd95SBruce Richardson np.pos.head = op.pos.head + n; 11799a2dd95SBruce Richardson 11899a2dd95SBruce Richardson /* 11999a2dd95SBruce Richardson * this CAS(ACQUIRE, ACQUIRE) serves as a hoist barrier to prevent: 12099a2dd95SBruce Richardson * - OOO reads of cons tail value 12199a2dd95SBruce Richardson * - OOO copy of elems from the ring 12299a2dd95SBruce Richardson */ 123*3197a1ffSKonstantin Ananyev } while (rte_atomic_compare_exchange_strong_explicit(&d->ht.raw, 12432faaf30STyler Retzlaff (uint64_t *)(uintptr_t)&op.raw, np.raw, 125*3197a1ffSKonstantin Ananyev rte_memory_order_acquire, 126*3197a1ffSKonstantin Ananyev rte_memory_order_acquire) == 0); 12799a2dd95SBruce Richardson 12899a2dd95SBruce Richardson *old_head = op.pos.head; 12999a2dd95SBruce Richardson return n; 13099a2dd95SBruce Richardson } 131*3197a1ffSKonstantin Ananyev /** 132*3197a1ffSKonstantin Ananyev * @internal This function updates the producer head for enqueue 133*3197a1ffSKonstantin Ananyev */ 134*3197a1ffSKonstantin Ananyev static __rte_always_inline unsigned int 135*3197a1ffSKonstantin Ananyev __rte_ring_hts_move_prod_head(struct rte_ring *r, unsigned int num, 136*3197a1ffSKonstantin Ananyev enum rte_ring_queue_behavior behavior, uint32_t *old_head, 137*3197a1ffSKonstantin Ananyev uint32_t *free_entries) 138*3197a1ffSKonstantin Ananyev { 139*3197a1ffSKonstantin Ananyev return __rte_ring_hts_move_head(&r->hts_prod, &r->cons, 140*3197a1ffSKonstantin Ananyev r->capacity, num, behavior, old_head, free_entries); 141*3197a1ffSKonstantin Ananyev } 14299a2dd95SBruce Richardson 14399a2dd95SBruce Richardson /** 14499a2dd95SBruce Richardson * @internal This function updates the consumer head for dequeue 14599a2dd95SBruce Richardson */ 14699a2dd95SBruce Richardson static __rte_always_inline unsigned int 14799a2dd95SBruce Richardson __rte_ring_hts_move_cons_head(struct rte_ring *r, unsigned int num, 14899a2dd95SBruce Richardson enum rte_ring_queue_behavior behavior, uint32_t *old_head, 14999a2dd95SBruce Richardson uint32_t *entries) 15099a2dd95SBruce Richardson { 151*3197a1ffSKonstantin Ananyev return __rte_ring_hts_move_head(&r->hts_cons, &r->prod, 152*3197a1ffSKonstantin Ananyev 0, num, behavior, old_head, entries); 15399a2dd95SBruce Richardson } 15499a2dd95SBruce Richardson 15599a2dd95SBruce Richardson /** 15699a2dd95SBruce Richardson * @internal Enqueue several objects on the HTS ring. 15799a2dd95SBruce Richardson * 15899a2dd95SBruce Richardson * @param r 15999a2dd95SBruce Richardson * A pointer to the ring structure. 16099a2dd95SBruce Richardson * @param obj_table 16199a2dd95SBruce Richardson * A pointer to a table of objects. 16299a2dd95SBruce Richardson * @param esize 16399a2dd95SBruce Richardson * The size of ring element, in bytes. It must be a multiple of 4. 16499a2dd95SBruce Richardson * This must be the same value used while creating the ring. Otherwise 16599a2dd95SBruce Richardson * the results are undefined. 16699a2dd95SBruce Richardson * @param n 16799a2dd95SBruce Richardson * The number of objects to add in the ring from the obj_table. 16899a2dd95SBruce Richardson * @param behavior 16999a2dd95SBruce Richardson * RTE_RING_QUEUE_FIXED: Enqueue a fixed number of items from a ring 17099a2dd95SBruce Richardson * RTE_RING_QUEUE_VARIABLE: Enqueue as many items as possible from ring 17199a2dd95SBruce Richardson * @param free_space 17299a2dd95SBruce Richardson * returns the amount of space after the enqueue operation has finished 17399a2dd95SBruce Richardson * @return 17499a2dd95SBruce Richardson * Actual number of objects enqueued. 17599a2dd95SBruce Richardson * If behavior == RTE_RING_QUEUE_FIXED, this will be 0 or n only. 17699a2dd95SBruce Richardson */ 17799a2dd95SBruce Richardson static __rte_always_inline unsigned int 17899a2dd95SBruce Richardson __rte_ring_do_hts_enqueue_elem(struct rte_ring *r, const void *obj_table, 17999a2dd95SBruce Richardson uint32_t esize, uint32_t n, enum rte_ring_queue_behavior behavior, 18099a2dd95SBruce Richardson uint32_t *free_space) 18199a2dd95SBruce Richardson { 18299a2dd95SBruce Richardson uint32_t free, head; 18399a2dd95SBruce Richardson 18499a2dd95SBruce Richardson n = __rte_ring_hts_move_prod_head(r, n, behavior, &head, &free); 18599a2dd95SBruce Richardson 18699a2dd95SBruce Richardson if (n != 0) { 18799a2dd95SBruce Richardson __rte_ring_enqueue_elems(r, head, obj_table, esize, n); 18899a2dd95SBruce Richardson __rte_ring_hts_update_tail(&r->hts_prod, head, n, 1); 18999a2dd95SBruce Richardson } 19099a2dd95SBruce Richardson 19199a2dd95SBruce Richardson if (free_space != NULL) 19299a2dd95SBruce Richardson *free_space = free - n; 19399a2dd95SBruce Richardson return n; 19499a2dd95SBruce Richardson } 19599a2dd95SBruce Richardson 19699a2dd95SBruce Richardson /** 19799a2dd95SBruce Richardson * @internal Dequeue several objects from the HTS ring. 19899a2dd95SBruce Richardson * 19999a2dd95SBruce Richardson * @param r 20099a2dd95SBruce Richardson * A pointer to the ring structure. 20199a2dd95SBruce Richardson * @param obj_table 20299a2dd95SBruce Richardson * A pointer to a table of objects. 20399a2dd95SBruce Richardson * @param esize 20499a2dd95SBruce Richardson * The size of ring element, in bytes. It must be a multiple of 4. 20599a2dd95SBruce Richardson * This must be the same value used while creating the ring. Otherwise 20699a2dd95SBruce Richardson * the results are undefined. 20799a2dd95SBruce Richardson * @param n 20899a2dd95SBruce Richardson * The number of objects to pull from the ring. 20999a2dd95SBruce Richardson * @param behavior 21099a2dd95SBruce Richardson * RTE_RING_QUEUE_FIXED: Dequeue a fixed number of items from a ring 21199a2dd95SBruce Richardson * RTE_RING_QUEUE_VARIABLE: Dequeue as many items as possible from ring 21299a2dd95SBruce Richardson * @param available 21399a2dd95SBruce Richardson * returns the number of remaining ring entries after the dequeue has finished 21499a2dd95SBruce Richardson * @return 21599a2dd95SBruce Richardson * - Actual number of objects dequeued. 21699a2dd95SBruce Richardson * If behavior == RTE_RING_QUEUE_FIXED, this will be 0 or n only. 21799a2dd95SBruce Richardson */ 21899a2dd95SBruce Richardson static __rte_always_inline unsigned int 21999a2dd95SBruce Richardson __rte_ring_do_hts_dequeue_elem(struct rte_ring *r, void *obj_table, 22099a2dd95SBruce Richardson uint32_t esize, uint32_t n, enum rte_ring_queue_behavior behavior, 22199a2dd95SBruce Richardson uint32_t *available) 22299a2dd95SBruce Richardson { 22399a2dd95SBruce Richardson uint32_t entries, head; 22499a2dd95SBruce Richardson 22599a2dd95SBruce Richardson n = __rte_ring_hts_move_cons_head(r, n, behavior, &head, &entries); 22699a2dd95SBruce Richardson 22799a2dd95SBruce Richardson if (n != 0) { 22899a2dd95SBruce Richardson __rte_ring_dequeue_elems(r, head, obj_table, esize, n); 22999a2dd95SBruce Richardson __rte_ring_hts_update_tail(&r->hts_cons, head, n, 0); 23099a2dd95SBruce Richardson } 23199a2dd95SBruce Richardson 23299a2dd95SBruce Richardson if (available != NULL) 23399a2dd95SBruce Richardson *available = entries - n; 23499a2dd95SBruce Richardson return n; 23599a2dd95SBruce Richardson } 23699a2dd95SBruce Richardson 23799a2dd95SBruce Richardson #endif /* _RTE_RING_HTS_ELEM_PVT_H_ */ 238