You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/flow-spare-pool.c

282 lines
7.8 KiB
C

/* Copyright (C) 2007-2020 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Victor Julien <victor@inliniac.net>
*
* Flow queue handler functions
*/
#include "suricata-common.h"
#include "threads.h"
#include "debug.h"
#include "flow-private.h"
#include "flow-queue.h"
#include "flow-util.h"
#include "flow-spare-pool.h"
#include "util-error.h"
#include "util-debug.h"
#include "util-print.h"
#include "util-validate.h"
typedef struct FlowSparePool {
FlowQueuePrivate queue;
struct FlowSparePool *next;
} FlowSparePool;
static uint32_t flow_spare_pool_flow_cnt = 0;
static uint32_t flow_spare_pool_block_size = 100;
static FlowSparePool *flow_spare_pool = NULL;
static SCMutex flow_spare_pool_m = SCMUTEX_INITIALIZER;
uint32_t FlowSpareGetPoolSize(void)
{
uint32_t size;
SCMutexLock(&flow_spare_pool_m);
size = flow_spare_pool_flow_cnt;
SCMutexUnlock(&flow_spare_pool_m);
return size;
}
static FlowSparePool *FlowSpareGetPool(void)
{
FlowSparePool *p = SCCalloc(1, sizeof(*p));
if (p == NULL)
return NULL;
return p;
}
static bool FlowSparePoolUpdateBlock(FlowSparePool *p)
{
DEBUG_VALIDATE_BUG_ON(p == NULL);
for (uint32_t i = p->queue.len; i < flow_spare_pool_block_size; i++)
{
Flow *f = FlowAlloc();
if (f == NULL)
return false;
FlowQueuePrivateAppendFlow(&p->queue, f);
}
return true;
}
#ifdef FSP_VALIDATE
static void Validate(FlowSparePool *top, const uint32_t target)
{
if (top == NULL) {
assert(target == 0);
return;
}
assert(top->queue.len >= 1);
//if (top->next != NULL)
// assert(top->next->queue.len == flow_spare_pool_block_size);
uint32_t cnt = 0;
for (FlowSparePool *p = top; p != NULL; p = p->next)
{
assert(p->queue.len);
cnt += p->queue.len;
}
assert(cnt == target);
}
#endif
void FlowSparePoolReturnFlow(Flow *f)
{
SCMutexLock(&flow_spare_pool_m);
if (flow_spare_pool == NULL) {
flow_spare_pool = FlowSpareGetPool();
}
DEBUG_VALIDATE_BUG_ON(flow_spare_pool == NULL);
/* if the top is full, get a new block */
if (flow_spare_pool->queue.len >= flow_spare_pool_block_size) {
FlowSparePool *p = FlowSpareGetPool();
DEBUG_VALIDATE_BUG_ON(p == NULL);
p->next = flow_spare_pool;
flow_spare_pool = p;
}
/* add to the (possibly new) top */
FlowQueuePrivateAppendFlow(&flow_spare_pool->queue, f);
flow_spare_pool_flow_cnt++;
SCMutexUnlock(&flow_spare_pool_m);
}
void FlowSparePoolReturnFlows(FlowQueuePrivate *fqp)
{
}
FlowQueuePrivate FlowSpareGetFromPool(void)
{
SCMutexLock(&flow_spare_pool_m);
if (flow_spare_pool == NULL || flow_spare_pool_flow_cnt == 0) {
SCMutexUnlock(&flow_spare_pool_m);
FlowQueuePrivate empty = { NULL, NULL, 0 };
return empty;
}
/* top if full or its the only block we have */
if (flow_spare_pool->queue.len >= flow_spare_pool_block_size || flow_spare_pool->next == NULL) {
FlowSparePool *p = flow_spare_pool;
flow_spare_pool = p->next;
DEBUG_VALIDATE_BUG_ON(flow_spare_pool_flow_cnt < p->queue.len);
flow_spare_pool_flow_cnt -= p->queue.len;
#ifdef FSP_VALIDATE
Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
#endif
SCMutexUnlock(&flow_spare_pool_m);
FlowQueuePrivate ret = p->queue;
SCFree(p);
return ret;
/* next should always be full if it exists */
} else if (flow_spare_pool->next != NULL) {
FlowSparePool *p = flow_spare_pool->next;
flow_spare_pool->next = p->next;
DEBUG_VALIDATE_BUG_ON(flow_spare_pool_flow_cnt < p->queue.len);
flow_spare_pool_flow_cnt -= p->queue.len;
#ifdef FSP_VALIDATE
Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
#endif
SCMutexUnlock(&flow_spare_pool_m);
FlowQueuePrivate ret = p->queue;
SCFree(p);
return ret;
}
SCMutexUnlock(&flow_spare_pool_m);
FlowQueuePrivate empty = { NULL, NULL, 0 };
return empty;
}
void FlowSparePoolUpdate(uint32_t size)
{
const int64_t todo = (int64_t)flow_config.prealloc - (int64_t)size;
if (todo < 0) {
/* remove one block at most at a time */
uint32_t to_remove = (uint32_t)(todo * -1) / 10;
if (to_remove < flow_spare_pool_block_size)
return;
FlowSparePool *p = NULL;
SCMutexLock(&flow_spare_pool_m);
p = flow_spare_pool;
if (p != NULL) {
flow_spare_pool = p->next;
flow_spare_pool_flow_cnt -= p->queue.len;
}
SCMutexUnlock(&flow_spare_pool_m);
if (p != NULL) {
Flow *f;
while ((f = FlowQueuePrivateGetFromTop(&p->queue))) {
FlowFree(f);
}
SCFree(p);
}
} else if (todo > 0) {
FlowSparePool *head = NULL, *tail = NULL;
uint32_t blocks = ((uint32_t)todo / flow_spare_pool_block_size) + 1;
uint32_t flow_cnt = 0;
for (uint32_t cnt = 0; cnt < blocks; cnt++) {
FlowSparePool *p = FlowSpareGetPool();
if (p == NULL) {
break;
}
const bool ok = FlowSparePoolUpdateBlock(p);
if (p->queue.len == 0) {
SCFree(p);
break;
}
flow_cnt += p->queue.len;
/* prepend to list */
p->next = head;
head = p;
if (tail == NULL)
tail = p;
if (!ok)
break;
}
if (head) {
SCMutexLock(&flow_spare_pool_m);
if (flow_spare_pool == NULL) {
flow_spare_pool = head;
} else if (tail != NULL) {
/* since these are 'full' buckets we don't put them
* at the top but right after as the top is likely not
* full. */
tail->next = flow_spare_pool->next;
flow_spare_pool->next = head;
}
flow_spare_pool_flow_cnt += flow_cnt;
#ifdef FSP_VALIDATE
Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
#endif
SCMutexUnlock(&flow_spare_pool_m);
}
}
}
void FlowSparePoolInit(void)
{
SCMutexLock(&flow_spare_pool_m);
for (uint32_t cnt = 0; cnt < flow_config.prealloc; ) {
FlowSparePool *p = FlowSpareGetPool();
if (p == NULL) {
FatalError(SC_ERR_FLOW_INIT, "failed to initialize flow pool");
}
FlowSparePoolUpdateBlock(p);
cnt += p->queue.len;
/* prepend to list */
p->next = flow_spare_pool;
flow_spare_pool = p;
flow_spare_pool_flow_cnt = cnt;
}
SCMutexUnlock(&flow_spare_pool_m);
}
void FlowSparePoolDestroy(void)
{
SCMutexLock(&flow_spare_pool_m);
for (FlowSparePool *p = flow_spare_pool; p != NULL; ) {
uint32_t cnt = 0;
Flow *f;
while ((f = FlowQueuePrivateGetFromTop(&p->queue))) {
FlowFree(f);
cnt++;
}
flow_spare_pool_flow_cnt -= cnt;
FlowSparePool *next = p->next;
SCFree(p);
p = next;
}
flow_spare_pool = NULL;
SCMutexUnlock(&flow_spare_pool_m);
}