netdev-dpdk: fix mbuf leaks
[cascardo/ovs.git] / lib / pvector.h
1 /*
2  * Copyright (c) 2014 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 an
30  * increasing priority order and allows readers to traverse the vector without
31  * being concerned about writers modifying the vector as they are traversing
32  * it.
33  *
34  * The priority order is maintained as a linear vector of elements to allow
35  * for efficient memory prefetching.
36  *
37  * Concurrency is implemented with OVS RCU so that the readers can assume
38  * that once they have taken a pointer to the vector with
39  * pvector_cursor_init(), the 'size' member will not decrease, so that
40  * they can safely read 'size' entries from 'vector', and find that each
41  * entry has a valid, non-NULL 'ptr', and the vector is in order from highest
42  * to lowest 'priority'.  The 'priority' values can change any time, but only
43  * so that the order of the entries does not change, so readers can use
44  * 'priority' values read at any time after acquisition of the vector pointer.
45  *
46  * Writers can concurrently add entries to the end of the vector, incrementing
47  * 'size', or update the 'priority' value of an entry, but only if that does
48  * not change the ordering of the entries.  Writers will never change the 'ptr'
49  * values, or decrement the 'size' on a copy that readers have access to.
50  *
51  * Most modifications are internally staged at the 'temp' vector, from which
52  * they can be published at 'impl' by calling pvector_publish().  This saves
53  * unnecessary memory allocations when many changes are done back-to-back.
54  * 'temp' may contain NULL pointers and it may be in unsorted order.  It is
55  * sorted before it is published at 'impl', which also removes the NULLs from
56  * the published vector.
57  *
58  * Clients should not use priority INT_MIN.
59  */
60
61 struct pvector_entry {
62     int priority;
63     void *ptr;
64 };
65
66 /* Writers will preallocate space for some entries at the end to avoid future
67  * reallocations. */
68 enum { PVECTOR_EXTRA_ALLOC = 4 };
69
70 struct pvector_impl {
71     size_t size;       /* Number of entries in the vector. */
72     size_t allocated;  /* Number of allocated entries. */
73     struct pvector_entry vector[];
74 };
75
76 /* Concurrent priority vector. */
77 struct pvector {
78     OVSRCU_TYPE(struct pvector_impl *) impl;
79     struct pvector_impl *temp;
80 };
81
82 /* Initialization. */
83 void pvector_init(struct pvector *);
84 void pvector_destroy(struct pvector *);
85
86 /* Insertion and deletion.  These work on 'temp'.  */
87 void pvector_insert(struct pvector *, void *, int priority);
88 void pvector_change_priority(struct pvector *, void *, int priority);
89 void pvector_remove(struct pvector *, void *);
90
91 /* Make the modified pvector available for iteration. */
92 static inline void pvector_publish(struct pvector *);
93
94 /* Count.  These operate on the published pvector. */
95 static inline size_t pvector_count(const struct pvector *);
96 static inline bool pvector_is_empty(const struct pvector *);
97
98 /* Iteration.
99  *
100  *
101  * Thread-safety
102  * =============
103  *
104  * Iteration is safe even in a pvector that is changing concurrently.
105  * Multiple writers must exclude each other via e.g., a mutex.
106  *
107  * Example
108  * =======
109  *
110  *     struct my_node {
111  *         int data;
112  *     };
113  *
114  *     struct my_node elem1, elem2, *iter;
115  *     struct pvector my_pvector;
116  *
117  *     pvector_init(&my_pvector);
118  *     ...add data...
119  *     pvector_insert(&my_pvector, &elem1, 1);
120  *     pvector_insert(&my_pvector, &elem2, 2);
121  *     ...
122  *     PVECTOR_FOR_EACH (iter, &my_pvector) {
123  *         ...operate on '*iter'...
124  *         ...elem2 to be seen before elem1...
125  *     }
126  *     ...
127  *     pvector_destroy(&my_pvector);
128  *
129  * There is no PVECTOR_FOR_EACH_SAFE variant as iteration is performed on RCU
130  * protected instance of the priority vector.  Any concurrent modifications
131  * that would be disruptive for readers (such as deletions), will be performed
132  * on a new instance.  To see any of the modifications, a new iteration loop
133  * has to be started.
134  *
135  * The PVECTOR_FOR_EACH_PRIORITY limits the iteration to entries with higher
136  * than given priority and allows for object lookahead.
137  *
138  * The iteration loop must be completed without entering the OVS RCU quiescent
139  * period.  That is, an old iteration loop must not be continued after any
140  * blocking IO (VLOG is non-blocking, so that is OK).
141  */
142 struct pvector_cursor {
143     size_t size;        /* Number of entries in the vector. */
144     size_t entry_idx;   /* Current index. */
145     const struct pvector_entry *vector;
146 };
147
148 static inline struct pvector_cursor pvector_cursor_init(const struct pvector *,
149                                                         size_t n_ahead,
150                                                         size_t obj_size);
151 static inline void *pvector_cursor_next(struct pvector_cursor *,
152                                         int stop_at_priority,
153                                         size_t n_ahead, size_t obj_size);
154 static inline void pvector_cursor_lookahead(const struct pvector_cursor *,
155                                             int n, size_t size);
156
157 #define PVECTOR_FOR_EACH(PTR, PVECTOR)                                  \
158     for (struct pvector_cursor cursor__ = pvector_cursor_init(PVECTOR, 0, 0); \
159          ((PTR) = pvector_cursor_next(&cursor__, INT_MIN, 0, 0)) != NULL; )
160
161 /* Loop while priority is higher than 'PRIORITY' and prefetch objects
162  * of size 'SZ' 'N' objects ahead from the current object. */
163 #define PVECTOR_FOR_EACH_PRIORITY(PTR, PRIORITY, N, SZ, PVECTOR)        \
164     for (struct pvector_cursor cursor__ = pvector_cursor_init(PVECTOR, N, SZ); \
165          ((PTR) = pvector_cursor_next(&cursor__, PRIORITY, N, SZ)) != NULL; )
166
167 #define PVECTOR_CURSOR_FOR_EACH(PTR, CURSOR, PVECTOR)                \
168     for (*(CURSOR) = pvector_cursor_init(PVECTOR, 0, 0);             \
169          ((PTR) = pvector_cursor_next(CURSOR, INT_MIN, 0, 0)) != NULL; )
170
171 #define PVECTOR_CURSOR_FOR_EACH_CONTINUE(PTR, CURSOR)                   \
172     for (; ((PTR) = pvector_cursor_next(CURSOR, INT_MIN, 0, 0)) != NULL; )
173
174 \f
175 /* Inline implementations. */
176
177 static inline struct pvector_cursor
178 pvector_cursor_init(const struct pvector *pvec,
179                     size_t n_ahead, size_t obj_size)
180 {
181     const struct pvector_impl *impl;
182     struct pvector_cursor cursor;
183
184     impl = ovsrcu_get(struct pvector_impl *, &pvec->impl);
185
186     ovs_prefetch_range(impl->vector, impl->size * sizeof impl->vector[0]);
187
188     cursor.size = impl->size;
189     cursor.vector = impl->vector;
190     cursor.entry_idx = -1;
191
192     for (size_t i = 0; i < n_ahead; i++) {
193         /* Prefetch the first objects. */
194         pvector_cursor_lookahead(&cursor, i, obj_size);
195     }
196     return cursor;
197 }
198
199 static inline void *pvector_cursor_next(struct pvector_cursor *cursor,
200                                         int stop_at_priority,
201                                         size_t n_ahead, size_t obj_size)
202 {
203     if (++cursor->entry_idx < cursor->size &&
204         cursor->vector[cursor->entry_idx].priority > stop_at_priority) {
205         if (n_ahead) {
206             pvector_cursor_lookahead(cursor, n_ahead, obj_size);
207         }
208         return cursor->vector[cursor->entry_idx].ptr;
209     }
210     return NULL;
211 }
212
213 static inline void pvector_cursor_lookahead(const struct pvector_cursor *cursor,
214                                             int n, size_t size)
215 {
216     if (cursor->entry_idx + n < cursor->size) {
217         ovs_prefetch_range(cursor->vector[cursor->entry_idx + n].ptr, size);
218     }
219 }
220
221 static inline size_t pvector_count(const struct pvector *pvec)
222 {
223     return ovsrcu_get(struct pvector_impl *, &pvec->impl)->size;
224 }
225
226 static inline bool pvector_is_empty(const struct pvector *pvec)
227 {
228     return pvector_count(pvec) == 0;
229 }
230
231 void pvector_publish__(struct pvector *);
232
233 /* Make the modified pvector available for iteration. */
234 static inline void pvector_publish(struct pvector *pvec)
235 {
236     if (pvec->temp) {
237         pvector_publish__(pvec);
238     }
239 }
240
241 #endif /* pvector.h */