/src/openvswitch/lib/pvector.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2014, 2016 Nicira, Inc. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at: |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #ifndef PVECTOR_H |
18 | | #define PVECTOR_H 1 |
19 | | |
20 | | #include <stdbool.h> |
21 | | #include <stdint.h> |
22 | | #include <stdlib.h> |
23 | | #include "ovs-rcu.h" |
24 | | #include "util.h" |
25 | | |
26 | | /* Concurrent Priority Vector |
27 | | * ========================== |
28 | | * |
29 | | * Concurrent priority vector holds non-NULL pointers to objects in a |
30 | | * nondecreasing priority order and allows readers to traverse the vector |
31 | | * without being concerned about writers modifying the vector as they are |
32 | | * traversing it. |
33 | | * |
34 | | * Multiple elements of a given priority are allowed. |
35 | | * |
36 | | * The priority order is maintained as a linear vector of elements to allow |
37 | | * for efficient memory prefetching. |
38 | | * |
39 | | * Concurrency is implemented with OVS RCU so that the readers can assume |
40 | | * that once they have taken a pointer to the vector with |
41 | | * pvector_cursor_init(), the 'size' member will not decrease, so that |
42 | | * they can safely read 'size' entries from 'vector', and find that each |
43 | | * entry has a valid, non-NULL 'ptr', and the vector is in order from highest |
44 | | * to lowest 'priority'. The 'priority' values can change any time, but only |
45 | | * so that the order of the entries does not change, so readers can use |
46 | | * 'priority' values read at any time after acquisition of the vector pointer. |
47 | | * |
48 | | * Writers can concurrently add entries to the end of the vector, incrementing |
49 | | * 'size', or update the 'priority' value of an entry, but only if that does |
50 | | * not change the ordering of the entries. Writers will never change the 'ptr' |
51 | | * values, or decrement the 'size' on a copy that readers have access to. |
52 | | * |
53 | | * Most modifications are internally staged at the 'temp' vector, from which |
54 | | * they can be published at 'impl' by calling pvector_publish(). This saves |
55 | | * unnecessary memory allocations when many changes are done back-to-back. |
56 | | * 'temp' may contain NULL pointers and it may be in unsorted order. It is |
57 | | * sorted before it is published at 'impl', which also removes the NULLs from |
58 | | * the published vector. |
59 | | * |
60 | | * Since the vector is RCU protected, the entry destruction after removal must |
61 | | * be RCU postponed. Also, if it happens before changes published with |
62 | | * pvector_publish(), destruction must be double postponed, i.e., the second |
63 | | * ovsrcu_postpone() call to destruct the entry should be called from the first |
64 | | * RCU callback. This is required because readers could still obtain the |
65 | | * unmodified vector until updated version is published. |
66 | | */ |
67 | | |
68 | | struct pvector_entry { |
69 | | int priority; |
70 | | void *ptr; |
71 | | }; |
72 | | |
73 | | struct pvector_impl { |
74 | | atomic_size_t size; /* Number of entries in the vector. */ |
75 | | size_t allocated; /* Number of allocated entries. */ |
76 | | struct pvector_entry vector[]; |
77 | | }; |
78 | | |
79 | | /* Concurrent priority vector. */ |
80 | | struct pvector { |
81 | | OVSRCU_TYPE(struct pvector_impl *) impl; |
82 | | struct pvector_impl *temp; |
83 | | }; |
84 | | |
85 | | /* Initialization. */ |
86 | | void pvector_init(struct pvector *); |
87 | | void pvector_destroy(struct pvector *); |
88 | | |
89 | | /* Insertion and deletion. These work on 'temp'. */ |
90 | | void pvector_insert(struct pvector *, void *, int priority); |
91 | | void pvector_change_priority(struct pvector *, void *, int priority); |
92 | | void pvector_remove(struct pvector *, void *); |
93 | | |
94 | | /* Make the modified pvector available for iteration. */ |
95 | | static inline void pvector_publish(struct pvector *); |
96 | | |
97 | | /* Count. These operate on the published pvector. */ |
98 | | static inline size_t pvector_count(const struct pvector *); |
99 | | static inline bool pvector_is_empty(const struct pvector *); |
100 | | |
101 | | /* Iteration. |
102 | | * |
103 | | * |
104 | | * Thread-safety |
105 | | * ============= |
106 | | * |
107 | | * Iteration is safe even in a pvector that is changing concurrently. |
108 | | * Multiple writers must exclude each other via e.g., a mutex. |
109 | | * |
110 | | * Example |
111 | | * ======= |
112 | | * |
113 | | * struct my_node { |
114 | | * int data; |
115 | | * }; |
116 | | * |
117 | | * struct my_node elem1, elem2, *iter; |
118 | | * struct pvector my_pvector; |
119 | | * |
120 | | * pvector_init(&my_pvector); |
121 | | * ...add data... |
122 | | * pvector_insert(&my_pvector, &elem1, 1); |
123 | | * pvector_insert(&my_pvector, &elem2, 2); |
124 | | * ... |
125 | | * PVECTOR_FOR_EACH (iter, &my_pvector) { |
126 | | * ...operate on '*iter'... |
127 | | * ...elem2 to be seen before elem1... |
128 | | * } |
129 | | * ... |
130 | | * pvector_destroy(&my_pvector); |
131 | | * |
132 | | * There is no PVECTOR_FOR_EACH_SAFE variant as iteration is performed on RCU |
133 | | * protected instance of the priority vector. Any concurrent modifications |
134 | | * that would be disruptive for readers (such as deletions), will be performed |
135 | | * on a new instance. To see any of the modifications, a new iteration loop |
136 | | * has to be started. |
137 | | * |
138 | | * The PVECTOR_FOR_EACH_PRIORITY limits the iteration to entries with higher |
139 | | * than or equal to the given priority and allows for object lookahead. |
140 | | * |
141 | | * The iteration loop must be completed without entering the OVS RCU quiescent |
142 | | * period. That is, an old iteration loop must not be continued after any |
143 | | * blocking IO (VLOG is non-blocking, so that is OK). |
144 | | */ |
145 | | struct pvector_cursor { |
146 | | size_t size; /* Number of entries in the vector. */ |
147 | | size_t entry_idx; /* Current index. */ |
148 | | const struct pvector_entry *vector; |
149 | | }; |
150 | | |
151 | | static inline struct pvector_cursor pvector_cursor_init(const struct pvector *, |
152 | | size_t n_ahead, |
153 | | size_t obj_size); |
154 | | static inline void *pvector_cursor_next(struct pvector_cursor *, |
155 | | int lowest_priority, |
156 | | size_t n_ahead, size_t obj_size); |
157 | | static inline void pvector_cursor_lookahead(const struct pvector_cursor *, |
158 | | int n, size_t size); |
159 | | |
160 | | #define PVECTOR_FOR_EACH(PTR, PVECTOR) \ |
161 | 0 | for (struct pvector_cursor cursor__ = pvector_cursor_init(PVECTOR, 0, 0); \ |
162 | 0 | ((PTR) = pvector_cursor_next(&cursor__, INT_MIN, 0, 0)) != NULL; ) |
163 | | |
164 | | /* Loop while priority is higher than or equal to 'PRIORITY' and prefetch |
165 | | * objects of size 'SZ' 'N' objects ahead from the current object. */ |
166 | | #define PVECTOR_FOR_EACH_PRIORITY(PTR, PRIORITY, N, SZ, PVECTOR) \ |
167 | 0 | for (struct pvector_cursor cursor__ = pvector_cursor_init(PVECTOR, N, SZ); \ |
168 | 0 | ((PTR) = pvector_cursor_next(&cursor__, PRIORITY, N, SZ)) != NULL; ) |
169 | | |
170 | | #define PVECTOR_CURSOR_FOR_EACH(PTR, CURSOR, PVECTOR) \ |
171 | 0 | for (*(CURSOR) = pvector_cursor_init(PVECTOR, 0, 0); \ |
172 | 0 | ((PTR) = pvector_cursor_next(CURSOR, INT_MIN, 0, 0)) != NULL; ) |
173 | | |
174 | | #define PVECTOR_CURSOR_FOR_EACH_CONTINUE(PTR, CURSOR) \ |
175 | 0 | for (; ((PTR) = pvector_cursor_next(CURSOR, INT_MIN, 0, 0)) != NULL; ) |
176 | | |
177 | | |
178 | | /* Inline implementations. */ |
179 | | |
180 | | static inline struct pvector_cursor |
181 | | pvector_cursor_init(const struct pvector *pvec, |
182 | | size_t n_ahead, size_t obj_size) |
183 | 0 | { |
184 | 0 | const struct pvector_impl *impl; |
185 | 0 | struct pvector_cursor cursor; |
186 | 0 | size_t size; |
187 | |
|
188 | 0 | impl = ovsrcu_get(struct pvector_impl *, &pvec->impl); |
189 | | |
190 | | /* Use memory_order_acquire to ensure entry access can not be |
191 | | * reordered to happen before size read. */ |
192 | 0 | atomic_read_explicit(&CONST_CAST(struct pvector_impl *, impl)->size, |
193 | 0 | &size, memory_order_acquire); |
194 | 0 | ovs_prefetch_range(impl->vector, size * sizeof impl->vector[0]); |
195 | |
|
196 | 0 | cursor.size = size; |
197 | 0 | cursor.vector = impl->vector; |
198 | 0 | cursor.entry_idx = -1; |
199 | |
|
200 | 0 | for (size_t i = 0; i < n_ahead; i++) { |
201 | | /* Prefetch the first objects. */ |
202 | 0 | pvector_cursor_lookahead(&cursor, i, obj_size); |
203 | 0 | } |
204 | 0 | return cursor; |
205 | 0 | } Unexecuted instantiation: meta-flow.c:pvector_cursor_init Unexecuted instantiation: nx-match.c:pvector_cursor_init Unexecuted instantiation: ofp-util.c:pvector_cursor_init Unexecuted instantiation: ovs-router.c:pvector_cursor_init Unexecuted instantiation: pvector.c:pvector_cursor_init Unexecuted instantiation: tnl-ports.c:pvector_cursor_init Unexecuted instantiation: classifier.c:pvector_cursor_init Unexecuted instantiation: dpif-netdev.c:pvector_cursor_init Unexecuted instantiation: dpif-netdev-lookup-generic.c:pvector_cursor_init |
206 | | |
207 | | static inline void *pvector_cursor_next(struct pvector_cursor *cursor, |
208 | | int lowest_priority, |
209 | | size_t n_ahead, size_t obj_size) |
210 | 0 | { |
211 | 0 | if (++cursor->entry_idx < cursor->size && |
212 | 0 | cursor->vector[cursor->entry_idx].priority >= lowest_priority) { |
213 | 0 | if (n_ahead) { |
214 | 0 | pvector_cursor_lookahead(cursor, n_ahead, obj_size); |
215 | 0 | } |
216 | 0 | return cursor->vector[cursor->entry_idx].ptr; |
217 | 0 | } |
218 | 0 | return NULL; |
219 | 0 | } Unexecuted instantiation: meta-flow.c:pvector_cursor_next Unexecuted instantiation: nx-match.c:pvector_cursor_next Unexecuted instantiation: ofp-util.c:pvector_cursor_next Unexecuted instantiation: ovs-router.c:pvector_cursor_next Unexecuted instantiation: pvector.c:pvector_cursor_next Unexecuted instantiation: tnl-ports.c:pvector_cursor_next Unexecuted instantiation: classifier.c:pvector_cursor_next Unexecuted instantiation: dpif-netdev.c:pvector_cursor_next Unexecuted instantiation: dpif-netdev-lookup-generic.c:pvector_cursor_next |
220 | | |
221 | | static inline void pvector_cursor_lookahead(const struct pvector_cursor *cursor, |
222 | | int n, size_t size) |
223 | 0 | { |
224 | 0 | if (cursor->entry_idx + n < cursor->size) { |
225 | 0 | ovs_prefetch_range(cursor->vector[cursor->entry_idx + n].ptr, size); |
226 | 0 | } |
227 | 0 | } Unexecuted instantiation: meta-flow.c:pvector_cursor_lookahead Unexecuted instantiation: nx-match.c:pvector_cursor_lookahead Unexecuted instantiation: ofp-util.c:pvector_cursor_lookahead Unexecuted instantiation: ovs-router.c:pvector_cursor_lookahead Unexecuted instantiation: pvector.c:pvector_cursor_lookahead Unexecuted instantiation: tnl-ports.c:pvector_cursor_lookahead Unexecuted instantiation: classifier.c:pvector_cursor_lookahead Unexecuted instantiation: dpif-netdev.c:pvector_cursor_lookahead Unexecuted instantiation: dpif-netdev-lookup-generic.c:pvector_cursor_lookahead |
228 | | |
229 | | static inline size_t pvector_count(const struct pvector *pvec) |
230 | 0 | { |
231 | 0 | return ovsrcu_get(struct pvector_impl *, &pvec->impl)->size; |
232 | 0 | } Unexecuted instantiation: meta-flow.c:pvector_count Unexecuted instantiation: nx-match.c:pvector_count Unexecuted instantiation: ofp-util.c:pvector_count Unexecuted instantiation: ovs-router.c:pvector_count Unexecuted instantiation: pvector.c:pvector_count Unexecuted instantiation: tnl-ports.c:pvector_count Unexecuted instantiation: classifier.c:pvector_count Unexecuted instantiation: dpif-netdev.c:pvector_count Unexecuted instantiation: dpif-netdev-lookup-generic.c:pvector_count |
233 | | |
234 | | static inline bool pvector_is_empty(const struct pvector *pvec) |
235 | 0 | { |
236 | 0 | return pvector_count(pvec) == 0; |
237 | 0 | } Unexecuted instantiation: meta-flow.c:pvector_is_empty Unexecuted instantiation: nx-match.c:pvector_is_empty Unexecuted instantiation: ofp-util.c:pvector_is_empty Unexecuted instantiation: ovs-router.c:pvector_is_empty Unexecuted instantiation: pvector.c:pvector_is_empty Unexecuted instantiation: tnl-ports.c:pvector_is_empty Unexecuted instantiation: classifier.c:pvector_is_empty Unexecuted instantiation: dpif-netdev.c:pvector_is_empty Unexecuted instantiation: dpif-netdev-lookup-generic.c:pvector_is_empty |
238 | | |
239 | | void pvector_publish__(struct pvector *); |
240 | | |
241 | | /* Make the modified pvector available for iteration. */ |
242 | | static inline void pvector_publish(struct pvector *pvec) |
243 | 0 | { |
244 | 0 | if (pvec->temp) { |
245 | 0 | pvector_publish__(pvec); |
246 | 0 | } |
247 | 0 | } Unexecuted instantiation: meta-flow.c:pvector_publish Unexecuted instantiation: nx-match.c:pvector_publish Unexecuted instantiation: ofp-util.c:pvector_publish Unexecuted instantiation: ovs-router.c:pvector_publish Unexecuted instantiation: pvector.c:pvector_publish Unexecuted instantiation: tnl-ports.c:pvector_publish Unexecuted instantiation: classifier.c:pvector_publish Unexecuted instantiation: dpif-netdev.c:pvector_publish Unexecuted instantiation: dpif-netdev-lookup-generic.c:pvector_publish |
248 | | |
249 | | #endif /* pvector.h */ |