1 /* Copyright (C) 2007-2020 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 "debug.h"
29 #include "flow-private.h"
30 #include "flow-queue.h"
31 #include "flow-util.h"
32 #include "flow-spare-pool.h"
33 #include "util-error.h"
34 #include "util-debug.h"
35 #include "util-print.h"
36 #include "util-validate.h"
37
38 typedef struct FlowSparePool {
39 FlowQueuePrivate queue;
40 struct FlowSparePool *next;
41 } FlowSparePool;
42
43 static uint32_t flow_spare_pool_flow_cnt = 0;
44 static uint32_t flow_spare_pool_block_size = 100;
45 static FlowSparePool *flow_spare_pool = NULL;
46 static SCMutex flow_spare_pool_m = SCMUTEX_INITIALIZER;
47
FlowSpareGetPoolSize(void)48 uint32_t FlowSpareGetPoolSize(void)
49 {
50 uint32_t size;
51 SCMutexLock(&flow_spare_pool_m);
52 size = flow_spare_pool_flow_cnt;
53 SCMutexUnlock(&flow_spare_pool_m);
54 return size;
55 }
56
FlowSpareGetPool(void)57 static FlowSparePool *FlowSpareGetPool(void)
58 {
59 FlowSparePool *p = SCCalloc(1, sizeof(*p));
60 if (p == NULL)
61 return NULL;
62 return p;
63 }
64
FlowSparePoolUpdateBlock(FlowSparePool * p)65 static bool FlowSparePoolUpdateBlock(FlowSparePool *p)
66 {
67 DEBUG_VALIDATE_BUG_ON(p == NULL);
68
69 for (uint32_t i = p->queue.len; i < flow_spare_pool_block_size; i++)
70 {
71 Flow *f = FlowAlloc();
72 if (f == NULL)
73 return false;
74 FlowQueuePrivateAppendFlow(&p->queue, f);
75 }
76 return true;
77 }
78
79 #ifdef FSP_VALIDATE
Validate(FlowSparePool * top,const uint32_t target)80 static void Validate(FlowSparePool *top, const uint32_t target)
81 {
82 if (top == NULL) {
83 assert(target == 0);
84 return;
85 }
86
87 assert(top->queue.len >= 1);
88 //if (top->next != NULL)
89 // assert(top->next->queue.len == flow_spare_pool_block_size);
90
91 uint32_t cnt = 0;
92 for (FlowSparePool *p = top; p != NULL; p = p->next)
93 {
94 assert(p->queue.len);
95 cnt += p->queue.len;
96 }
97 assert(cnt == target);
98 }
99 #endif
100
FlowSparePoolReturnFlow(Flow * f)101 void FlowSparePoolReturnFlow(Flow *f)
102 {
103 SCMutexLock(&flow_spare_pool_m);
104 if (flow_spare_pool == NULL) {
105 flow_spare_pool = FlowSpareGetPool();
106 }
107 DEBUG_VALIDATE_BUG_ON(flow_spare_pool == NULL);
108
109 /* if the top is full, get a new block */
110 if (flow_spare_pool->queue.len >= flow_spare_pool_block_size) {
111 FlowSparePool *p = FlowSpareGetPool();
112 DEBUG_VALIDATE_BUG_ON(p == NULL);
113 p->next = flow_spare_pool;
114 flow_spare_pool = p;
115 }
116 /* add to the (possibly new) top */
117 FlowQueuePrivateAppendFlow(&flow_spare_pool->queue, f);
118 flow_spare_pool_flow_cnt++;
119
120 SCMutexUnlock(&flow_spare_pool_m);
121 }
122
FlowSparePoolReturnFlows(FlowQueuePrivate * fqp)123 void FlowSparePoolReturnFlows(FlowQueuePrivate *fqp)
124 {
125
126 }
127
FlowSpareGetFromPool(void)128 FlowQueuePrivate FlowSpareGetFromPool(void)
129 {
130 SCMutexLock(&flow_spare_pool_m);
131 if (flow_spare_pool == NULL || flow_spare_pool_flow_cnt == 0) {
132 SCMutexUnlock(&flow_spare_pool_m);
133 FlowQueuePrivate empty = { NULL, NULL, 0 };
134 return empty;
135 }
136
137 /* top if full or its the only block we have */
138 if (flow_spare_pool->queue.len >= flow_spare_pool_block_size || flow_spare_pool->next == NULL) {
139 FlowSparePool *p = flow_spare_pool;
140 flow_spare_pool = p->next;
141 DEBUG_VALIDATE_BUG_ON(flow_spare_pool_flow_cnt < p->queue.len);
142 flow_spare_pool_flow_cnt -= p->queue.len;
143 #ifdef FSP_VALIDATE
144 Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
145 #endif
146 SCMutexUnlock(&flow_spare_pool_m);
147
148 FlowQueuePrivate ret = p->queue;
149 SCFree(p);
150 return ret;
151 /* next should always be full if it exists */
152 } else if (flow_spare_pool->next != NULL) {
153 FlowSparePool *p = flow_spare_pool->next;
154 flow_spare_pool->next = p->next;
155 DEBUG_VALIDATE_BUG_ON(flow_spare_pool_flow_cnt < p->queue.len);
156 flow_spare_pool_flow_cnt -= p->queue.len;
157 #ifdef FSP_VALIDATE
158 Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
159 #endif
160 SCMutexUnlock(&flow_spare_pool_m);
161
162 FlowQueuePrivate ret = p->queue;
163 SCFree(p);
164 return ret;
165 }
166
167 SCMutexUnlock(&flow_spare_pool_m);
168 FlowQueuePrivate empty = { NULL, NULL, 0 };
169 return empty;
170 }
171
FlowSparePoolUpdate(uint32_t size)172 void FlowSparePoolUpdate(uint32_t size)
173 {
174 const int64_t todo = (int64_t)flow_config.prealloc - (int64_t)size;
175 if (todo < 0) {
176 uint32_t to_remove = (uint32_t)(todo * -1) / 10;
177 while (to_remove) {
178 if (to_remove < flow_spare_pool_block_size)
179 return;
180
181 FlowSparePool *p = NULL;
182 SCMutexLock(&flow_spare_pool_m);
183 p = flow_spare_pool;
184 if (p != NULL) {
185 flow_spare_pool = p->next;
186 flow_spare_pool_flow_cnt -= p->queue.len;
187 to_remove -= p->queue.len;
188 }
189 SCMutexUnlock(&flow_spare_pool_m);
190
191 if (p != NULL) {
192 Flow *f;
193 while ((f = FlowQueuePrivateGetFromTop(&p->queue))) {
194 FlowFree(f);
195 }
196 SCFree(p);
197 }
198 }
199 } else if (todo > 0) {
200 FlowSparePool *head = NULL, *tail = NULL;
201
202 uint32_t blocks = ((uint32_t)todo / flow_spare_pool_block_size) + 1;
203
204 uint32_t flow_cnt = 0;
205 for (uint32_t cnt = 0; cnt < blocks; cnt++) {
206 FlowSparePool *p = FlowSpareGetPool();
207 if (p == NULL) {
208 break;
209 }
210 const bool ok = FlowSparePoolUpdateBlock(p);
211 if (p->queue.len == 0) {
212 SCFree(p);
213 break;
214 }
215 flow_cnt += p->queue.len;
216
217 /* prepend to list */
218 p->next = head;
219 head = p;
220 if (tail == NULL)
221 tail = p;
222 if (!ok)
223 break;
224 }
225 if (head) {
226 SCMutexLock(&flow_spare_pool_m);
227 if (flow_spare_pool == NULL) {
228 flow_spare_pool = head;
229 } else if (tail != NULL) {
230 /* since these are 'full' buckets we don't put them
231 * at the top but right after as the top is likely not
232 * full. */
233 tail->next = flow_spare_pool->next;
234 flow_spare_pool->next = head;
235 }
236
237 flow_spare_pool_flow_cnt += flow_cnt;
238 #ifdef FSP_VALIDATE
239 Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
240 #endif
241 SCMutexUnlock(&flow_spare_pool_m);
242 }
243 }
244 }
245
FlowSparePoolInit(void)246 void FlowSparePoolInit(void)
247 {
248 SCMutexLock(&flow_spare_pool_m);
249 for (uint32_t cnt = 0; cnt < flow_config.prealloc; ) {
250 FlowSparePool *p = FlowSpareGetPool();
251 if (p == NULL) {
252 FatalError(SC_ERR_FLOW_INIT, "failed to initialize flow pool");
253 }
254 FlowSparePoolUpdateBlock(p);
255 cnt += p->queue.len;
256
257 /* prepend to list */
258 p->next = flow_spare_pool;
259 flow_spare_pool = p;
260 flow_spare_pool_flow_cnt = cnt;
261 }
262 SCMutexUnlock(&flow_spare_pool_m);
263 }
264
FlowSparePoolDestroy(void)265 void FlowSparePoolDestroy(void)
266 {
267 SCMutexLock(&flow_spare_pool_m);
268 for (FlowSparePool *p = flow_spare_pool; p != NULL; ) {
269 uint32_t cnt = 0;
270 Flow *f;
271 while ((f = FlowQueuePrivateGetFromTop(&p->queue))) {
272 FlowFree(f);
273 cnt++;
274 }
275 flow_spare_pool_flow_cnt -= cnt;
276 FlowSparePool *next = p->next;
277 SCFree(p);
278 p = next;
279 }
280 flow_spare_pool = NULL;
281 SCMutexUnlock(&flow_spare_pool_m);
282 }
283