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