eal: get/set thread priority per thread identifier
[dpdk.git] / lib / eal / include / rte_seqcount.h
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2022 Ericsson AB
3  */
4
5 #ifndef _RTE_SEQCOUNT_H_
6 #define _RTE_SEQCOUNT_H_
7
8 #ifdef __cplusplus
9 extern "C" {
10 #endif
11
12 /**
13  * @file
14  * RTE Seqcount
15  *
16  * The sequence counter synchronizes a single writer with multiple,
17  * parallel readers. It is used as the basis for the RTE sequence
18  * lock.
19  *
20  * @see rte_seqlock.h
21  */
22
23 #include <stdbool.h>
24 #include <stdint.h>
25
26 #include <rte_atomic.h>
27 #include <rte_branch_prediction.h>
28 #include <rte_compat.h>
29
30 /**
31  * The RTE seqcount type.
32  */
33 typedef struct {
34         uint32_t sn; /**< A sequence number for the protected data. */
35 } rte_seqcount_t;
36
37 /**
38  * A static seqcount initializer.
39  */
40 #define RTE_SEQCOUNT_INITIALIZER { .sn = 0 }
41
42 /**
43  * @warning
44  * @b EXPERIMENTAL: this API may change without prior notice.
45  *
46  * Initialize the sequence counter.
47  *
48  * @param seqcount
49  *   A pointer to the sequence counter.
50  */
51 __rte_experimental
52 static inline void
53 rte_seqcount_init(rte_seqcount_t *seqcount)
54 {
55         seqcount->sn = 0;
56 }
57
58 /**
59  * @warning
60  * @b EXPERIMENTAL: this API may change without prior notice.
61  *
62  * Begin a read-side critical section.
63  *
64  * A call to this function marks the beginning of a read-side critical
65  * section, for @p seqcount.
66  *
67  * rte_seqcount_read_begin() returns a sequence number, which is later
68  * used in rte_seqcount_read_retry() to check if the protected data
69  * underwent any modifications during the read transaction.
70  *
71  * After (in program order) rte_seqcount_read_begin() has been called,
72  * the calling thread reads the protected data, for later use. The
73  * protected data read *must* be copied (either in pristine form, or
74  * in the form of some derivative), since the caller may only read the
75  * data from within the read-side critical section (i.e., after
76  * rte_seqcount_read_begin() and before rte_seqcount_read_retry()),
77  * but must not act upon the retrieved data while in the critical
78  * section, since it does not yet know if it is consistent.
79  *
80  * The protected data may be read using atomic and/or non-atomic
81  * operations.
82  *
83  * After (in program order) all required data loads have been
84  * performed, rte_seqcount_read_retry() should be called, marking
85  * the end of the read-side critical section.
86  *
87  * If rte_seqcount_read_retry() returns true, the just-read data is
88  * inconsistent and should be discarded. The caller has the option to
89  * either restart the whole procedure right away (i.e., calling
90  * rte_seqcount_read_begin() again), or do the same at some later time.
91  *
92  * If rte_seqcount_read_retry() returns false, the data was read
93  * atomically and the copied data is consistent.
94  *
95  * @param seqcount
96  *   A pointer to the sequence counter.
97  * @return
98  *   The seqcount sequence number for this critical section, to
99  *   later be passed to rte_seqcount_read_retry().
100  *
101  * @see rte_seqcount_read_retry()
102  */
103
104 __rte_experimental
105 static inline uint32_t
106 rte_seqcount_read_begin(const rte_seqcount_t *seqcount)
107 {
108         /* __ATOMIC_ACQUIRE to prevent loads after (in program order)
109          * from happening before the sn load. Synchronizes-with the
110          * store release in rte_seqcount_write_end().
111          */
112         return __atomic_load_n(&seqcount->sn, __ATOMIC_ACQUIRE);
113 }
114
115 /**
116  * @warning
117  * @b EXPERIMENTAL: this API may change without prior notice.
118  *
119  * End a read-side critical section.
120  *
121  * A call to this function marks the end of a read-side critical
122  * section, for @p seqcount. The application must supply the sequence
123  * number produced by the corresponding rte_seqcount_read_begin() call.
124  *
125  * After this function has been called, the caller should not access
126  * the protected data.
127  *
128  * In case rte_seqcount_read_retry() returns true, the just-read data
129  * was modified as it was being read and may be inconsistent, and thus
130  * should be discarded.
131  *
132  * In case this function returns false, the data is consistent and the
133  * set of atomic and non-atomic load operations performed between
134  * rte_seqcount_read_begin() and rte_seqcount_read_retry() were atomic,
135  * as a whole.
136  *
137  * @param seqcount
138  *   A pointer to the sequence counter.
139  * @param begin_sn
140  *   The sequence number returned by rte_seqcount_read_begin().
141  * @return
142  *   true or false, if the just-read seqcount-protected data was
143  *   inconsistent or consistent, respectively, at the time it was
144  *   read.
145  *
146  * @see rte_seqcount_read_begin()
147  */
148
149 __rte_experimental
150 static inline bool
151 rte_seqcount_read_retry(const rte_seqcount_t *seqcount, uint32_t begin_sn)
152 {
153         uint32_t end_sn;
154
155         /* An odd sequence number means the protected data was being
156          * modified already at the point of the rte_seqcount_read_begin()
157          * call.
158          */
159         if (unlikely(begin_sn & 1))
160                 return true;
161
162         /* make sure the data loads happens before the sn load */
163         rte_atomic_thread_fence(__ATOMIC_ACQUIRE);
164
165         end_sn = __atomic_load_n(&seqcount->sn, __ATOMIC_RELAXED);
166
167         /* A writer incremented the sequence number during this read
168          * critical section.
169          */
170         return begin_sn != end_sn;
171 }
172
173 /**
174  * @warning
175  * @b EXPERIMENTAL: this API may change without prior notice.
176  *
177  * Begin a write-side critical section.
178  *
179  * A call to this function marks the beginning of a write-side
180  * critical section, after which the caller may go on to modify (both
181  * read and write) the protected data, in an atomic or non-atomic
182  * manner.
183  *
184  * After the necessary updates have been performed, the application
185  * calls rte_seqcount_write_end().
186  *
187  * Multiple, parallel writers must use some external serialization.
188  *
189  * This function is not preemption-safe in the sense that preemption
190  * of the calling thread may block reader progress until the writer
191  * thread is rescheduled.
192  *
193  * @param seqcount
194  *   A pointer to the sequence counter.
195  *
196  * @see rte_seqcount_write_end()
197  */
198
199 __rte_experimental
200 static inline void
201 rte_seqcount_write_begin(rte_seqcount_t *seqcount)
202 {
203         uint32_t sn;
204
205         sn = seqcount->sn + 1;
206
207         __atomic_store_n(&seqcount->sn, sn, __ATOMIC_RELAXED);
208
209         /* __ATOMIC_RELEASE to prevent stores after (in program order)
210          * from happening before the sn store.
211          */
212         rte_atomic_thread_fence(__ATOMIC_RELEASE);
213 }
214
215 /**
216  * @warning
217  * @b EXPERIMENTAL: this API may change without prior notice.
218  *
219  * End a write-side critical section.
220  *
221  * A call to this function marks the end of the write-side critical
222  * section, for @p seqcount. After this call has been made, the
223  * protected data may no longer be modified.
224  *
225  * @param seqcount
226  *   A pointer to the sequence counter.
227  *
228  * @see rte_seqcount_write_begin()
229  */
230 __rte_experimental
231 static inline void
232 rte_seqcount_write_end(rte_seqcount_t *seqcount)
233 {
234         uint32_t sn;
235
236         sn = seqcount->sn + 1;
237
238         /* Synchronizes-with the load acquire in rte_seqcount_read_begin(). */
239         __atomic_store_n(&seqcount->sn, sn, __ATOMIC_RELEASE);
240 }
241
242 #ifdef __cplusplus
243 }
244 #endif
245
246 #endif /* _RTE_SEQCOUNT_H_ */