suricata
flow-spare-pool.c
Go to the documentation of this file.
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
41
42static uint32_t flow_spare_pool_flow_cnt = 0;
43static FlowSparePool *flow_spare_pool = NULL;
44static SCMutex flow_spare_pool_m = SCMUTEX_INITIALIZER;
45
47{
48 uint32_t size;
49 SCMutexLock(&flow_spare_pool_m);
50 size = flow_spare_pool_flow_cnt;
51 SCMutexUnlock(&flow_spare_pool_m);
52 return size;
53}
54
55static FlowSparePool *FlowSpareGetPool(void)
56{
57 FlowSparePool *p = SCCalloc(1, sizeof(*p));
58 if (p == NULL)
59 return NULL;
60 return p;
61}
62
63static bool FlowSparePoolUpdateBlock(FlowSparePool *p)
64{
65 DEBUG_VALIDATE_BUG_ON(p == NULL);
66
67 for (uint32_t i = p->queue.len; i < FLOW_SPARE_POOL_BLOCK_SIZE; i++) {
68 Flow *f = FlowAlloc();
69 if (f == NULL)
70 return false;
72 }
73 return true;
74}
75
76#ifdef FSP_VALIDATE
77static void Validate(FlowSparePool *top, const uint32_t target)
78{
79 if (top == NULL) {
80 assert(target == 0);
81 return;
82 }
83
84 assert(top->queue.len >= 1);
85 // if (top->next != NULL)
86 // assert(top->next->queue.len == FLOW_SPARE_POOL_BLOCK_SIZE);
87
88 uint32_t cnt = 0;
89 for (FlowSparePool *p = top; p != NULL; p = p->next)
90 {
91 assert(p->queue.len);
92 cnt += p->queue.len;
93 }
94 assert(cnt == target);
95}
96#endif
97
99{
100 SCMutexLock(&flow_spare_pool_m);
101 if (flow_spare_pool == NULL) {
102 flow_spare_pool = FlowSpareGetPool();
103 }
104 DEBUG_VALIDATE_BUG_ON(flow_spare_pool == NULL);
105
106 /* if the top is full, get a new block */
107 if (flow_spare_pool->queue.len >= FLOW_SPARE_POOL_BLOCK_SIZE) {
108 FlowSparePool *p = FlowSpareGetPool();
109 DEBUG_VALIDATE_BUG_ON(p == NULL);
110 p->next = flow_spare_pool;
111 flow_spare_pool = p;
112 }
113 /* add to the (possibly new) top */
114 FlowQueuePrivateAppendFlow(&flow_spare_pool->queue, f);
115 flow_spare_pool_flow_cnt++;
116
117 SCMutexUnlock(&flow_spare_pool_m);
118}
119
121{
122 FlowSparePool *p = FlowSpareGetPool();
123 DEBUG_VALIDATE_BUG_ON(p == NULL);
124 p->queue = *fqp;
125
126 SCMutexLock(&flow_spare_pool_m);
127 flow_spare_pool_flow_cnt += fqp->len;
128 if (flow_spare_pool != NULL) {
130 /* full block insert */
131
132 if (flow_spare_pool->queue.len < FLOW_SPARE_POOL_BLOCK_SIZE) {
133 p->next = flow_spare_pool->next;
134 flow_spare_pool->next = p;
135 p = NULL;
136 } else {
137 p->next = flow_spare_pool;
138 flow_spare_pool = p;
139 p = NULL;
140 }
141 } else {
142 /* incomplete block insert */
143
144 if (p->queue.len + flow_spare_pool->queue.len <= FLOW_SPARE_POOL_BLOCK_SIZE) {
145 FlowQueuePrivateAppendPrivate(&flow_spare_pool->queue, &p->queue);
146 /* free 'p' outside of lock below */
147 } else {
148 // put smallest first
149 if (p->queue.len < flow_spare_pool->queue.len) {
150 p->next = flow_spare_pool;
151 flow_spare_pool = p;
152 } else {
153 p->next = flow_spare_pool->next;
154 flow_spare_pool->next = p;
155 }
156 p = NULL;
157 }
158 }
159 } else {
160 p->next = flow_spare_pool;
161 flow_spare_pool = p;
162 p = NULL;
163 }
164 SCMutexUnlock(&flow_spare_pool_m);
165
166 FlowQueuePrivate empty = { NULL, NULL, 0 };
167 *fqp = empty;
168
169 if (p != NULL)
170 SCFree(p);
171}
172
174{
175 SCMutexLock(&flow_spare_pool_m);
176 if (flow_spare_pool == NULL || flow_spare_pool_flow_cnt == 0) {
177 SCMutexUnlock(&flow_spare_pool_m);
178 FlowQueuePrivate empty = { NULL, NULL, 0 };
179 return empty;
180 }
181
182 /* top if full or its the only block we have */
183 if (flow_spare_pool->queue.len >= FLOW_SPARE_POOL_BLOCK_SIZE || flow_spare_pool->next == NULL) {
184 FlowSparePool *p = flow_spare_pool;
185 flow_spare_pool = p->next;
186 DEBUG_VALIDATE_BUG_ON(flow_spare_pool_flow_cnt < p->queue.len);
187 flow_spare_pool_flow_cnt -= p->queue.len;
188#ifdef FSP_VALIDATE
189 Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
190#endif
191 SCMutexUnlock(&flow_spare_pool_m);
192
193 FlowQueuePrivate ret = p->queue;
194 SCFree(p);
195 return ret;
196 /* next should always be full if it exists */
197 } else if (flow_spare_pool->next != NULL) {
198 FlowSparePool *p = flow_spare_pool->next;
199 flow_spare_pool->next = p->next;
200 DEBUG_VALIDATE_BUG_ON(flow_spare_pool_flow_cnt < p->queue.len);
201 flow_spare_pool_flow_cnt -= p->queue.len;
202#ifdef FSP_VALIDATE
203 Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
204#endif
205 SCMutexUnlock(&flow_spare_pool_m);
206
207 FlowQueuePrivate ret = p->queue;
208 SCFree(p);
209 return ret;
210 }
211
212 SCMutexUnlock(&flow_spare_pool_m);
213 FlowQueuePrivate empty = { NULL, NULL, 0 };
214 return empty;
215}
216
217void FlowSparePoolUpdate(uint32_t size)
218{
219 const int64_t todo = (int64_t)flow_config.prealloc - (int64_t)size;
220 if (todo < 0) {
221 uint32_t to_remove = (uint32_t)(todo * -1) / 10;
222 while (to_remove) {
223 if (to_remove < FLOW_SPARE_POOL_BLOCK_SIZE)
224 return;
225
226 FlowSparePool *p = NULL;
227 SCMutexLock(&flow_spare_pool_m);
228 p = flow_spare_pool;
229 if (p != NULL) {
230 flow_spare_pool = p->next;
231 flow_spare_pool_flow_cnt -= p->queue.len;
232 to_remove -= p->queue.len;
233 }
234 SCMutexUnlock(&flow_spare_pool_m);
235
236 if (p != NULL) {
237 Flow *f;
238 while ((f = FlowQueuePrivateGetFromTop(&p->queue))) {
239 FlowFree(f);
240 }
241 SCFree(p);
242 }
243 }
244 } else if (todo > 0) {
245 FlowSparePool *head = NULL, *tail = NULL;
246
247 uint32_t blocks = ((uint32_t)todo / FLOW_SPARE_POOL_BLOCK_SIZE) + 1;
248
249 uint32_t flow_cnt = 0;
250 for (uint32_t cnt = 0; cnt < blocks; cnt++) {
251 FlowSparePool *p = FlowSpareGetPool();
252 if (p == NULL) {
253 break;
254 }
255 const bool ok = FlowSparePoolUpdateBlock(p);
256 if (p->queue.len == 0) {
257 SCFree(p);
258 break;
259 }
260 flow_cnt += p->queue.len;
261
262 /* prepend to list */
263 p->next = head;
264 head = p;
265 if (tail == NULL)
266 tail = p;
267 if (!ok)
268 break;
269 }
270 if (head) {
271 SCMutexLock(&flow_spare_pool_m);
272 if (flow_spare_pool == NULL) {
273 flow_spare_pool = head;
274 } else if (tail != NULL) {
275 /* since these are 'full' buckets we don't put them
276 * at the top but right after as the top is likely not
277 * full. */
278 tail->next = flow_spare_pool->next;
279 flow_spare_pool->next = head;
280 }
281
282 flow_spare_pool_flow_cnt += flow_cnt;
283#ifdef FSP_VALIDATE
284 Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
285#endif
286 SCMutexUnlock(&flow_spare_pool_m);
287 }
288 }
289}
290
292{
293 SCMutexLock(&flow_spare_pool_m);
294 for (uint32_t cnt = 0; cnt < flow_config.prealloc; ) {
295 FlowSparePool *p = FlowSpareGetPool();
296 if (p == NULL) {
297 FatalError("failed to initialize flow pool");
298 }
299 FlowSparePoolUpdateBlock(p);
300 cnt += p->queue.len;
301
302 /* prepend to list */
303 p->next = flow_spare_pool;
304 flow_spare_pool = p;
305 flow_spare_pool_flow_cnt = cnt;
306 }
307 SCMutexUnlock(&flow_spare_pool_m);
308}
309
311{
312 SCMutexLock(&flow_spare_pool_m);
313 for (FlowSparePool *p = flow_spare_pool; p != NULL; ) {
314 uint32_t cnt = 0;
315 Flow *f;
316 while ((f = FlowQueuePrivateGetFromTop(&p->queue))) {
317 FlowFree(f);
318 cnt++;
319 }
320 flow_spare_pool_flow_cnt -= cnt;
321 FlowSparePool *next = p->next;
322 SCFree(p);
323 p = next;
324 }
325 flow_spare_pool = NULL;
326 SCMutexUnlock(&flow_spare_pool_m);
327}
struct HtpBodyChunk_ * next
Flow * head
Definition flow-hash.h:1
FlowConfig flow_config
Definition flow.c:93
void FlowQueuePrivateAppendFlow(FlowQueuePrivate *fqc, Flow *f)
Definition flow-queue.c:65
Flow * FlowQueuePrivateGetFromTop(FlowQueuePrivate *fqc)
Definition flow-queue.c:151
void FlowQueuePrivateAppendPrivate(FlowQueuePrivate *dest, FlowQueuePrivate *src)
Definition flow-queue.c:88
void FlowSparePoolReturnFlow(Flow *f)
void FlowSparePoolDestroy(void)
uint32_t FlowSpareGetPoolSize(void)
FlowQueuePrivate FlowSpareGetFromPool(void)
void FlowSparePoolUpdate(uint32_t size)
void FlowSparePoolReturnFlows(FlowQueuePrivate *fqp)
void FlowSparePoolInit(void)
#define FLOW_SPARE_POOL_BLOCK_SIZE
Flow * FlowAlloc(void)
allocate a flow
Definition flow-util.c:56
void FlowFree(Flow *f)
cleanup & free the memory of a flow
Definition flow-util.c:84
Host * tail
Definition host.h:2
uint32_t prealloc
Definition flow.h:295
FlowQueuePrivate queue
struct FlowSparePool * next
Flow data structure.
Definition flow.h:356
#define SCMUTEX_INITIALIZER
#define SCMutex
#define SCMutexUnlock(mut)
#define SCMutexLock(mut)
uint32_t cnt
#define FatalError(...)
Definition util-debug.h:510
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define DEBUG_VALIDATE_BUG_ON(exp)