1 /* SPDX-License-Identifier: BSD-3-Clause
3 * Copyright (c) 2018 Arm Limited
12 #include <rte_common.h>
14 #include <rte_memory.h>
15 #include <rte_malloc.h>
17 #include <rte_atomic.h>
18 #include <rte_per_lcore.h>
19 #include <rte_lcore.h>
20 #include <rte_errno.h>
22 #include "rte_rcu_qsbr.h"
24 /* Get the memory size of QSBR variable */
26 rte_rcu_qsbr_get_memsize(uint32_t max_threads)
30 if (max_threads == 0) {
31 rte_log(RTE_LOG_ERR, rte_rcu_log_type,
32 "%s(): Invalid max_threads %u\n",
33 __func__, max_threads);
39 sz = sizeof(struct rte_rcu_qsbr);
41 /* Add the size of quiescent state counter array */
42 sz += sizeof(struct rte_rcu_qsbr_cnt) * max_threads;
44 /* Add the size of the registered thread ID bitmap array */
45 sz += __RTE_QSBR_THRID_ARRAY_SIZE(max_threads);
50 /* Initialize a quiescent state variable */
52 rte_rcu_qsbr_init(struct rte_rcu_qsbr *v, uint32_t max_threads)
57 rte_log(RTE_LOG_ERR, rte_rcu_log_type,
58 "%s(): Invalid input parameter\n", __func__);
64 sz = rte_rcu_qsbr_get_memsize(max_threads);
68 /* Set all the threads to offline */
70 v->max_threads = max_threads;
71 v->num_elems = RTE_ALIGN_MUL_CEIL(max_threads,
72 __RTE_QSBR_THRID_ARRAY_ELM_SIZE) /
73 __RTE_QSBR_THRID_ARRAY_ELM_SIZE;
74 v->token = __RTE_QSBR_CNT_INIT;
75 v->acked_token = __RTE_QSBR_CNT_INIT - 1;
80 /* Register a reader thread to report its quiescent state
84 rte_rcu_qsbr_thread_register(struct rte_rcu_qsbr *v, unsigned int thread_id)
86 unsigned int i, id, success;
87 uint64_t old_bmap, new_bmap;
89 if (v == NULL || thread_id >= v->max_threads) {
90 rte_log(RTE_LOG_ERR, rte_rcu_log_type,
91 "%s(): Invalid input parameter\n", __func__);
97 __RTE_RCU_IS_LOCK_CNT_ZERO(v, thread_id, ERR, "Lock counter %u\n",
98 v->qsbr_cnt[thread_id].lock_cnt);
100 id = thread_id & __RTE_QSBR_THRID_MASK;
101 i = thread_id >> __RTE_QSBR_THRID_INDEX_SHIFT;
103 /* Make sure that the counter for registered threads does not
104 * go out of sync. Hence, additional checks are required.
106 /* Check if the thread is already registered */
107 old_bmap = __atomic_load_n(__RTE_QSBR_THRID_ARRAY_ELM(v, i),
109 if (old_bmap & 1UL << id)
113 new_bmap = old_bmap | (1UL << id);
114 success = __atomic_compare_exchange(
115 __RTE_QSBR_THRID_ARRAY_ELM(v, i),
116 &old_bmap, &new_bmap, 0,
117 __ATOMIC_RELEASE, __ATOMIC_RELAXED);
120 __atomic_fetch_add(&v->num_threads,
121 1, __ATOMIC_RELAXED);
122 else if (old_bmap & (1UL << id))
123 /* Someone else registered this thread.
124 * Counter should not be incremented.
127 } while (success == 0);
132 /* Remove a reader thread, from the list of threads reporting their
133 * quiescent state on a QS variable.
136 rte_rcu_qsbr_thread_unregister(struct rte_rcu_qsbr *v, unsigned int thread_id)
138 unsigned int i, id, success;
139 uint64_t old_bmap, new_bmap;
141 if (v == NULL || thread_id >= v->max_threads) {
142 rte_log(RTE_LOG_ERR, rte_rcu_log_type,
143 "%s(): Invalid input parameter\n", __func__);
149 __RTE_RCU_IS_LOCK_CNT_ZERO(v, thread_id, ERR, "Lock counter %u\n",
150 v->qsbr_cnt[thread_id].lock_cnt);
152 id = thread_id & __RTE_QSBR_THRID_MASK;
153 i = thread_id >> __RTE_QSBR_THRID_INDEX_SHIFT;
155 /* Make sure that the counter for registered threads does not
156 * go out of sync. Hence, additional checks are required.
158 /* Check if the thread is already unregistered */
159 old_bmap = __atomic_load_n(__RTE_QSBR_THRID_ARRAY_ELM(v, i),
161 if (!(old_bmap & (1UL << id)))
165 new_bmap = old_bmap & ~(1UL << id);
166 /* Make sure any loads of the shared data structure are
167 * completed before removal of the thread from the list of
170 success = __atomic_compare_exchange(
171 __RTE_QSBR_THRID_ARRAY_ELM(v, i),
172 &old_bmap, &new_bmap, 0,
173 __ATOMIC_RELEASE, __ATOMIC_RELAXED);
176 __atomic_fetch_sub(&v->num_threads,
177 1, __ATOMIC_RELAXED);
178 else if (!(old_bmap & (1UL << id)))
179 /* Someone else unregistered this thread.
180 * Counter should not be incremented.
183 } while (success == 0);
188 /* Wait till the reader threads have entered quiescent state. */
190 rte_rcu_qsbr_synchronize(struct rte_rcu_qsbr *v, unsigned int thread_id)
194 RTE_ASSERT(v != NULL);
196 t = rte_rcu_qsbr_start(v);
198 /* If the current thread has readside critical section,
199 * update its quiescent state status.
201 if (thread_id != RTE_QSBR_THRID_INVALID)
202 rte_rcu_qsbr_quiescent(v, thread_id);
204 /* Wait for other readers to enter quiescent state */
205 rte_rcu_qsbr_check(v, t, true);
208 /* Dump the details of a single quiescent state variable to a file. */
210 rte_rcu_qsbr_dump(FILE *f, struct rte_rcu_qsbr *v)
215 if (v == NULL || f == NULL) {
216 rte_log(RTE_LOG_ERR, rte_rcu_log_type,
217 "%s(): Invalid input parameter\n", __func__);
223 fprintf(f, "\nQuiescent State Variable @%p\n", v);
225 fprintf(f, " QS variable memory size = %zu\n",
226 rte_rcu_qsbr_get_memsize(v->max_threads));
227 fprintf(f, " Given # max threads = %u\n", v->max_threads);
228 fprintf(f, " Current # threads = %u\n", v->num_threads);
230 fprintf(f, " Registered thread IDs = ");
231 for (i = 0; i < v->num_elems; i++) {
232 bmap = __atomic_load_n(__RTE_QSBR_THRID_ARRAY_ELM(v, i),
234 id = i << __RTE_QSBR_THRID_INDEX_SHIFT;
236 t = __builtin_ctzl(bmap);
237 fprintf(f, "%u ", id + t);
245 fprintf(f, " Token = %"PRIu64"\n",
246 __atomic_load_n(&v->token, __ATOMIC_ACQUIRE));
248 fprintf(f, " Least Acknowledged Token = %"PRIu64"\n",
249 __atomic_load_n(&v->acked_token, __ATOMIC_ACQUIRE));
251 fprintf(f, "Quiescent State Counts for readers:\n");
252 for (i = 0; i < v->num_elems; i++) {
253 bmap = __atomic_load_n(__RTE_QSBR_THRID_ARRAY_ELM(v, i),
255 id = i << __RTE_QSBR_THRID_INDEX_SHIFT;
257 t = __builtin_ctzl(bmap);
258 fprintf(f, "thread ID = %u, count = %"PRIu64", lock count = %u\n",
261 &v->qsbr_cnt[id + t].cnt,
264 &v->qsbr_cnt[id + t].lock_cnt,
273 int rte_rcu_log_type;
275 RTE_INIT(rte_rcu_register)
277 rte_rcu_log_type = rte_log_register("lib.rcu");
278 if (rte_rcu_log_type >= 0)
279 rte_log_set_level(rte_rcu_log_type, RTE_LOG_ERR);