/src/suricata7/src/flow-spare-pool.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2023 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** |
19 | | * \file |
20 | | * |
21 | | * \author Victor Julien <victor@inliniac.net> |
22 | | * |
23 | | * Flow queue handler functions |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "threads.h" |
28 | | #include "flow-private.h" |
29 | | #include "flow-queue.h" |
30 | | #include "flow-util.h" |
31 | | #include "flow-spare-pool.h" |
32 | | #include "util-error.h" |
33 | | #include "util-debug.h" |
34 | | #include "util-print.h" |
35 | | #include "util-validate.h" |
36 | | |
37 | | typedef struct FlowSparePool { |
38 | | FlowQueuePrivate queue; |
39 | | struct FlowSparePool *next; |
40 | | } FlowSparePool; |
41 | | |
42 | | static uint32_t flow_spare_pool_flow_cnt = 0; |
43 | | uint32_t flow_spare_pool_block_size = 100; |
44 | | static FlowSparePool *flow_spare_pool = NULL; |
45 | | static SCMutex flow_spare_pool_m = SCMUTEX_INITIALIZER; |
46 | | |
47 | | uint32_t FlowSpareGetPoolSize(void) |
48 | 0 | { |
49 | 0 | uint32_t size; |
50 | 0 | SCMutexLock(&flow_spare_pool_m); |
51 | 0 | size = flow_spare_pool_flow_cnt; |
52 | 0 | SCMutexUnlock(&flow_spare_pool_m); |
53 | 0 | return size; |
54 | 0 | } |
55 | | |
56 | | static FlowSparePool *FlowSpareGetPool(void) |
57 | 7.40k | { |
58 | 7.40k | FlowSparePool *p = SCCalloc(1, sizeof(*p)); |
59 | 7.40k | if (p == NULL) |
60 | 0 | return NULL; |
61 | 7.40k | return p; |
62 | 7.40k | } |
63 | | |
64 | | static bool FlowSparePoolUpdateBlock(FlowSparePool *p) |
65 | 7.40k | { |
66 | 7.40k | DEBUG_VALIDATE_BUG_ON(p == NULL); |
67 | | |
68 | 747k | for (uint32_t i = p->queue.len; i < flow_spare_pool_block_size; i++) |
69 | 740k | { |
70 | 740k | Flow *f = FlowAlloc(); |
71 | 740k | if (f == NULL) |
72 | 0 | return false; |
73 | 740k | FlowQueuePrivateAppendFlow(&p->queue, f); |
74 | 740k | } |
75 | 7.40k | return true; |
76 | 7.40k | } |
77 | | |
78 | | #ifdef FSP_VALIDATE |
79 | | static void Validate(FlowSparePool *top, const uint32_t target) |
80 | | { |
81 | | if (top == NULL) { |
82 | | assert(target == 0); |
83 | | return; |
84 | | } |
85 | | |
86 | | assert(top->queue.len >= 1); |
87 | | //if (top->next != NULL) |
88 | | // assert(top->next->queue.len == flow_spare_pool_block_size); |
89 | | |
90 | | uint32_t cnt = 0; |
91 | | for (FlowSparePool *p = top; p != NULL; p = p->next) |
92 | | { |
93 | | assert(p->queue.len); |
94 | | cnt += p->queue.len; |
95 | | } |
96 | | assert(cnt == target); |
97 | | } |
98 | | #endif |
99 | | |
100 | | void FlowSparePoolReturnFlow(Flow *f) |
101 | 0 | { |
102 | 0 | SCMutexLock(&flow_spare_pool_m); |
103 | 0 | if (flow_spare_pool == NULL) { |
104 | 0 | flow_spare_pool = FlowSpareGetPool(); |
105 | 0 | } |
106 | 0 | DEBUG_VALIDATE_BUG_ON(flow_spare_pool == NULL); |
107 | | |
108 | | /* if the top is full, get a new block */ |
109 | 0 | if (flow_spare_pool->queue.len >= flow_spare_pool_block_size) { |
110 | 0 | FlowSparePool *p = FlowSpareGetPool(); |
111 | 0 | DEBUG_VALIDATE_BUG_ON(p == NULL); |
112 | 0 | p->next = flow_spare_pool; |
113 | 0 | flow_spare_pool = p; |
114 | 0 | } |
115 | | /* add to the (possibly new) top */ |
116 | 0 | FlowQueuePrivateAppendFlow(&flow_spare_pool->queue, f); |
117 | 0 | flow_spare_pool_flow_cnt++; |
118 | |
|
119 | 0 | SCMutexUnlock(&flow_spare_pool_m); |
120 | 0 | } |
121 | | |
122 | | void FlowSparePoolReturnFlows(FlowQueuePrivate *fqp) |
123 | 0 | { |
124 | 0 | FlowSparePool *p = FlowSpareGetPool(); |
125 | 0 | DEBUG_VALIDATE_BUG_ON(p == NULL); |
126 | 0 | p->queue = *fqp; |
127 | |
|
128 | 0 | SCMutexLock(&flow_spare_pool_m); |
129 | 0 | flow_spare_pool_flow_cnt += fqp->len; |
130 | 0 | if (flow_spare_pool != NULL) { |
131 | 0 | if (p->queue.len == flow_spare_pool_block_size) { |
132 | | /* full block insert */ |
133 | |
|
134 | 0 | if (flow_spare_pool->queue.len < flow_spare_pool_block_size) { |
135 | 0 | p->next = flow_spare_pool->next; |
136 | 0 | flow_spare_pool->next = p; |
137 | 0 | p = NULL; |
138 | 0 | } else { |
139 | 0 | p->next = flow_spare_pool; |
140 | 0 | flow_spare_pool = p; |
141 | 0 | p = NULL; |
142 | 0 | } |
143 | 0 | } else { |
144 | | /* incomplete block insert */ |
145 | |
|
146 | 0 | if (p->queue.len + flow_spare_pool->queue.len <= flow_spare_pool_block_size) { |
147 | 0 | FlowQueuePrivateAppendPrivate(&flow_spare_pool->queue, &p->queue); |
148 | | /* free 'p' outside of lock below */ |
149 | 0 | } else { |
150 | | // put smallest first |
151 | 0 | if (p->queue.len < flow_spare_pool->queue.len) { |
152 | 0 | p->next = flow_spare_pool; |
153 | 0 | flow_spare_pool = p; |
154 | 0 | } else { |
155 | 0 | p->next = flow_spare_pool->next; |
156 | 0 | flow_spare_pool->next = p; |
157 | 0 | } |
158 | 0 | p = NULL; |
159 | 0 | } |
160 | 0 | } |
161 | 0 | } else { |
162 | 0 | p->next = flow_spare_pool; |
163 | 0 | flow_spare_pool = p; |
164 | 0 | p = NULL; |
165 | 0 | } |
166 | 0 | SCMutexUnlock(&flow_spare_pool_m); |
167 | |
|
168 | 0 | FlowQueuePrivate empty = { NULL, NULL, 0 }; |
169 | 0 | *fqp = empty; |
170 | |
|
171 | 0 | if (p != NULL) |
172 | 0 | SCFree(p); |
173 | 0 | } |
174 | | |
175 | | FlowQueuePrivate FlowSpareGetFromPool(void) |
176 | 258k | { |
177 | 258k | SCMutexLock(&flow_spare_pool_m); |
178 | 258k | if (flow_spare_pool == NULL || flow_spare_pool_flow_cnt == 0) { |
179 | 257k | SCMutexUnlock(&flow_spare_pool_m); |
180 | 257k | FlowQueuePrivate empty = { NULL, NULL, 0 }; |
181 | 257k | return empty; |
182 | 257k | } |
183 | | |
184 | | /* top if full or its the only block we have */ |
185 | 400 | if (flow_spare_pool->queue.len >= flow_spare_pool_block_size || flow_spare_pool->next == NULL) { |
186 | 400 | FlowSparePool *p = flow_spare_pool; |
187 | 400 | flow_spare_pool = p->next; |
188 | 400 | DEBUG_VALIDATE_BUG_ON(flow_spare_pool_flow_cnt < p->queue.len); |
189 | 400 | flow_spare_pool_flow_cnt -= p->queue.len; |
190 | | #ifdef FSP_VALIDATE |
191 | | Validate(flow_spare_pool, flow_spare_pool_flow_cnt); |
192 | | #endif |
193 | 400 | SCMutexUnlock(&flow_spare_pool_m); |
194 | | |
195 | 400 | FlowQueuePrivate ret = p->queue; |
196 | 400 | SCFree(p); |
197 | 400 | return ret; |
198 | | /* next should always be full if it exists */ |
199 | 400 | } else if (flow_spare_pool->next != NULL) { |
200 | 0 | FlowSparePool *p = flow_spare_pool->next; |
201 | 0 | flow_spare_pool->next = p->next; |
202 | 0 | DEBUG_VALIDATE_BUG_ON(flow_spare_pool_flow_cnt < p->queue.len); |
203 | 0 | flow_spare_pool_flow_cnt -= p->queue.len; |
204 | | #ifdef FSP_VALIDATE |
205 | | Validate(flow_spare_pool, flow_spare_pool_flow_cnt); |
206 | | #endif |
207 | 0 | SCMutexUnlock(&flow_spare_pool_m); |
208 | |
|
209 | 0 | FlowQueuePrivate ret = p->queue; |
210 | 0 | SCFree(p); |
211 | 0 | return ret; |
212 | 0 | } |
213 | | |
214 | 0 | SCMutexUnlock(&flow_spare_pool_m); |
215 | 0 | FlowQueuePrivate empty = { NULL, NULL, 0 }; |
216 | 0 | return empty; |
217 | 400 | } |
218 | | |
219 | | void FlowSparePoolUpdate(uint32_t size) |
220 | 0 | { |
221 | 0 | const int64_t todo = (int64_t)flow_config.prealloc - (int64_t)size; |
222 | 0 | if (todo < 0) { |
223 | 0 | uint32_t to_remove = (uint32_t)(todo * -1) / 10; |
224 | 0 | while (to_remove) { |
225 | 0 | if (to_remove < flow_spare_pool_block_size) |
226 | 0 | return; |
227 | | |
228 | 0 | FlowSparePool *p = NULL; |
229 | 0 | SCMutexLock(&flow_spare_pool_m); |
230 | 0 | p = flow_spare_pool; |
231 | 0 | if (p != NULL) { |
232 | 0 | flow_spare_pool = p->next; |
233 | 0 | flow_spare_pool_flow_cnt -= p->queue.len; |
234 | 0 | to_remove -= p->queue.len; |
235 | 0 | } |
236 | 0 | SCMutexUnlock(&flow_spare_pool_m); |
237 | |
|
238 | 0 | if (p != NULL) { |
239 | 0 | Flow *f; |
240 | 0 | while ((f = FlowQueuePrivateGetFromTop(&p->queue))) { |
241 | 0 | FlowFree(f); |
242 | 0 | } |
243 | 0 | SCFree(p); |
244 | 0 | } |
245 | 0 | } |
246 | 0 | } else if (todo > 0) { |
247 | 0 | FlowSparePool *head = NULL, *tail = NULL; |
248 | |
|
249 | 0 | uint32_t blocks = ((uint32_t)todo / flow_spare_pool_block_size) + 1; |
250 | |
|
251 | 0 | uint32_t flow_cnt = 0; |
252 | 0 | for (uint32_t cnt = 0; cnt < blocks; cnt++) { |
253 | 0 | FlowSparePool *p = FlowSpareGetPool(); |
254 | 0 | if (p == NULL) { |
255 | 0 | break; |
256 | 0 | } |
257 | 0 | const bool ok = FlowSparePoolUpdateBlock(p); |
258 | 0 | if (p->queue.len == 0) { |
259 | 0 | SCFree(p); |
260 | 0 | break; |
261 | 0 | } |
262 | 0 | flow_cnt += p->queue.len; |
263 | | |
264 | | /* prepend to list */ |
265 | 0 | p->next = head; |
266 | 0 | head = p; |
267 | 0 | if (tail == NULL) |
268 | 0 | tail = p; |
269 | 0 | if (!ok) |
270 | 0 | break; |
271 | 0 | } |
272 | 0 | if (head) { |
273 | 0 | SCMutexLock(&flow_spare_pool_m); |
274 | 0 | if (flow_spare_pool == NULL) { |
275 | 0 | flow_spare_pool = head; |
276 | 0 | } else if (tail != NULL) { |
277 | | /* since these are 'full' buckets we don't put them |
278 | | * at the top but right after as the top is likely not |
279 | | * full. */ |
280 | 0 | tail->next = flow_spare_pool->next; |
281 | 0 | flow_spare_pool->next = head; |
282 | 0 | } |
283 | |
|
284 | 0 | flow_spare_pool_flow_cnt += flow_cnt; |
285 | | #ifdef FSP_VALIDATE |
286 | | Validate(flow_spare_pool, flow_spare_pool_flow_cnt); |
287 | | #endif |
288 | 0 | SCMutexUnlock(&flow_spare_pool_m); |
289 | 0 | } |
290 | 0 | } |
291 | 0 | } |
292 | | |
293 | | void FlowSparePoolInit(void) |
294 | 74 | { |
295 | 74 | SCMutexLock(&flow_spare_pool_m); |
296 | 7.47k | for (uint32_t cnt = 0; cnt < flow_config.prealloc; ) { |
297 | 7.40k | FlowSparePool *p = FlowSpareGetPool(); |
298 | 7.40k | if (p == NULL) { |
299 | 0 | FatalError("failed to initialize flow pool"); |
300 | 0 | } |
301 | 7.40k | FlowSparePoolUpdateBlock(p); |
302 | 7.40k | cnt += p->queue.len; |
303 | | |
304 | | /* prepend to list */ |
305 | 7.40k | p->next = flow_spare_pool; |
306 | 7.40k | flow_spare_pool = p; |
307 | 7.40k | flow_spare_pool_flow_cnt = cnt; |
308 | 7.40k | } |
309 | 74 | SCMutexUnlock(&flow_spare_pool_m); |
310 | 74 | } |
311 | | |
312 | | void FlowSparePoolDestroy(void) |
313 | 0 | { |
314 | 0 | SCMutexLock(&flow_spare_pool_m); |
315 | 0 | for (FlowSparePool *p = flow_spare_pool; p != NULL; ) { |
316 | 0 | uint32_t cnt = 0; |
317 | 0 | Flow *f; |
318 | 0 | while ((f = FlowQueuePrivateGetFromTop(&p->queue))) { |
319 | 0 | FlowFree(f); |
320 | 0 | cnt++; |
321 | 0 | } |
322 | 0 | flow_spare_pool_flow_cnt -= cnt; |
323 | 0 | FlowSparePool *next = p->next; |
324 | 0 | SCFree(p); |
325 | 0 | p = next; |
326 | 0 | } |
327 | 0 | flow_spare_pool = NULL; |
328 | 0 | SCMutexUnlock(&flow_spare_pool_m); |
329 | 0 | } |