1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2019 Arm Limited 3 */ 4 5 #ifndef _RTE_TICKETLOCK_H_ 6 #define _RTE_TICKETLOCK_H_ 7 8 /** 9 * @file 10 * 11 * RTE ticket locks 12 * 13 * This file defines an API for ticket locks, which give each waiting 14 * thread a ticket and take the lock one by one, first come, first 15 * serviced. 16 * 17 * All locks must be initialised before use, and only initialised once. 18 * 19 */ 20 21 #ifdef __cplusplus 22 extern "C" { 23 #endif 24 25 #include <rte_common.h> 26 #include <rte_lcore.h> 27 #include <rte_pause.h> 28 29 /** 30 * The rte_ticketlock_t type. 31 */ 32 typedef union { 33 uint32_t tickets; 34 struct { 35 uint16_t current; 36 uint16_t next; 37 } s; 38 } rte_ticketlock_t; 39 40 /** 41 * A static ticketlock initializer. 42 */ 43 #define RTE_TICKETLOCK_INITIALIZER { 0 } 44 45 /** 46 * Initialize the ticketlock to an unlocked state. 47 * 48 * @param tl 49 * A pointer to the ticketlock. 50 */ 51 static inline void 52 rte_ticketlock_init(rte_ticketlock_t *tl) 53 { 54 __atomic_store_n(&tl->tickets, 0, __ATOMIC_RELAXED); 55 } 56 57 /** 58 * Take the ticketlock. 59 * 60 * @param tl 61 * A pointer to the ticketlock. 62 */ 63 static inline void 64 rte_ticketlock_lock(rte_ticketlock_t *tl) 65 { 66 uint16_t me = __atomic_fetch_add(&tl->s.next, 1, __ATOMIC_RELAXED); 67 rte_wait_until_equal_16(&tl->s.current, me, __ATOMIC_ACQUIRE); 68 } 69 70 /** 71 * Release the ticketlock. 72 * 73 * @param tl 74 * A pointer to the ticketlock. 75 */ 76 static inline void 77 rte_ticketlock_unlock(rte_ticketlock_t *tl) 78 { 79 uint16_t i = __atomic_load_n(&tl->s.current, __ATOMIC_RELAXED); 80 __atomic_store_n(&tl->s.current, i + 1, __ATOMIC_RELEASE); 81 } 82 83 /** 84 * Try to take the lock. 85 * 86 * @param tl 87 * A pointer to the ticketlock. 88 * @return 89 * 1 if the lock is successfully taken; 0 otherwise. 90 */ 91 static inline int 92 rte_ticketlock_trylock(rte_ticketlock_t *tl) 93 { 94 rte_ticketlock_t oldl, newl; 95 oldl.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_RELAXED); 96 newl.tickets = oldl.tickets; 97 newl.s.next++; 98 if (oldl.s.next == oldl.s.current) { 99 if (__atomic_compare_exchange_n(&tl->tickets, &oldl.tickets, 100 newl.tickets, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) 101 return 1; 102 } 103 104 return 0; 105 } 106 107 /** 108 * Test if the lock is taken. 109 * 110 * @param tl 111 * A pointer to the ticketlock. 112 * @return 113 * 1 if the lock is currently taken; 0 otherwise. 114 */ 115 static inline int 116 rte_ticketlock_is_locked(rte_ticketlock_t *tl) 117 { 118 rte_ticketlock_t tic; 119 tic.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_ACQUIRE); 120 return (tic.s.current != tic.s.next); 121 } 122 123 /** 124 * The rte_ticketlock_recursive_t type. 125 */ 126 #define TICKET_LOCK_INVALID_ID -1 127 128 typedef struct { 129 rte_ticketlock_t tl; /**< the actual ticketlock */ 130 int user; /**< core id using lock, TICKET_LOCK_INVALID_ID for unused */ 131 unsigned int count; /**< count of time this lock has been called */ 132 } rte_ticketlock_recursive_t; 133 134 /** 135 * A static recursive ticketlock initializer. 136 */ 137 #define RTE_TICKETLOCK_RECURSIVE_INITIALIZER {RTE_TICKETLOCK_INITIALIZER, \ 138 TICKET_LOCK_INVALID_ID, 0} 139 140 /** 141 * Initialize the recursive ticketlock to an unlocked state. 142 * 143 * @param tlr 144 * A pointer to the recursive ticketlock. 145 */ 146 static inline void 147 rte_ticketlock_recursive_init(rte_ticketlock_recursive_t *tlr) 148 { 149 rte_ticketlock_init(&tlr->tl); 150 __atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID, __ATOMIC_RELAXED); 151 tlr->count = 0; 152 } 153 154 /** 155 * Take the recursive ticketlock. 156 * 157 * @param tlr 158 * A pointer to the recursive ticketlock. 159 */ 160 static inline void 161 rte_ticketlock_recursive_lock(rte_ticketlock_recursive_t *tlr) 162 { 163 int id = rte_gettid(); 164 165 if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) { 166 rte_ticketlock_lock(&tlr->tl); 167 __atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED); 168 } 169 tlr->count++; 170 } 171 172 /** 173 * Release the recursive ticketlock. 174 * 175 * @param tlr 176 * A pointer to the recursive ticketlock. 177 */ 178 static inline void 179 rte_ticketlock_recursive_unlock(rte_ticketlock_recursive_t *tlr) 180 { 181 if (--(tlr->count) == 0) { 182 __atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID, 183 __ATOMIC_RELAXED); 184 rte_ticketlock_unlock(&tlr->tl); 185 } 186 } 187 188 /** 189 * Try to take the recursive lock. 190 * 191 * @param tlr 192 * A pointer to the recursive ticketlock. 193 * @return 194 * 1 if the lock is successfully taken; 0 otherwise. 195 */ 196 static inline int 197 rte_ticketlock_recursive_trylock(rte_ticketlock_recursive_t *tlr) 198 { 199 int id = rte_gettid(); 200 201 if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) { 202 if (rte_ticketlock_trylock(&tlr->tl) == 0) 203 return 0; 204 __atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED); 205 } 206 tlr->count++; 207 return 1; 208 } 209 210 #ifdef __cplusplus 211 } 212 #endif 213 214 #endif /* _RTE_TICKETLOCK_H_ */ 215