stream: configurable segment pools

The stream reassembly engine uses a set of pools in which preallocated
segments are stored. There are various pools each with different packet
sizes. The goal is to lower memory presure. Until now, these pools were
hardcoded.

This patch introduces the ability to configure them fully from the yaml.
There can be at max 256 of these pools.

Yaml layout is as follows:

stream:
  reassemble:
    segments:
      - size: 2048
        prealloc: 3000
      - size: 4
        prealloc: 1000
      - size: 1024
        prealloc: 2000

The size is the packet size. The prealloc value indicates how many
segments are set up at startup.

The pools have no limit wrt how many segments can be used of a certain
size. If the engine needs more than the prealloc size, segments are
malloc'd and free'd. The only limit here is the stream.reassemble.memcap.

If the yaml part if omitted, the default values are the same as before.

Feature #1093
pull/801/head
Victor Julien 12 years ago
parent b27d03e2f9
commit b5f8f386a3

@ -32,6 +32,7 @@
#include "detect.h"
#include "flow.h"
#include "threads.h"
#include "conf.h"
#include "flow-util.h"
@ -43,6 +44,7 @@
#include "util-print.h"
#include "util-host-os-info.h"
#include "util-unittest-helper.h"
#include "util-byte.h"
#include "stream-tcp.h"
#include "stream-tcp-private.h"
@ -72,21 +74,12 @@ static uint64_t segment_pool_memcnt = 0;
/* We define several pools with prealloced segments with fixed size
* payloads. We do this to prevent having to do an SCMalloc call for every
* data segment we receive, which would be a large performance penalty.
* The cost is in memory of course. */
#define segment_pool_num 8
static uint16_t segment_pool_pktsizes[segment_pool_num] = {4, 16, 112, 248, 512,
768, 1448, 0xffff};
//static uint16_t segment_pool_poolsizes[segment_pool_num] = {2048, 3072, 3072,
// 3072, 3072, 8192,
// 8192, 512};
static uint16_t segment_pool_poolsizes[segment_pool_num] = {0, 0, 0,
0, 0, 0,
0, 0};
static uint16_t segment_pool_poolsizes_prealloc[segment_pool_num] = {256, 512, 512,
512, 512, 1024,
1024, 128};
static Pool *segment_pool[segment_pool_num];
static SCMutex segment_pool_mutex[segment_pool_num];
* The cost is in memory of course. The number of pools and the properties
* of the pools are determined by the yaml. */
static int segment_pool_num = 0;
static Pool **segment_pool = NULL;
static SCMutex *segment_pool_mutex = NULL;
static uint16_t *segment_pool_pktsizes = NULL;
#ifdef DEBUG
static SCMutex segment_pool_cnt_mutex;
static uint64_t segment_pool_cnt = 0;
@ -280,36 +273,159 @@ void StreamTcpReturnStreamSegments (TcpStream *stream)
stream->seg_list_tail = NULL;
}
int StreamTcpReassembleInit(char quiet)
typedef struct SegmentSizes_
{
/* init the memcap/use tracker */
SC_ATOMIC_INIT(ra_memuse);
uint16_t pktsize;
uint32_t prealloc;
} SegmentSizes;
StreamMsgQueuesInit();
/* sort small to big */
static int SortByPktsize(const void *a, const void *b)
{
const SegmentSizes *s0 = a;
const SegmentSizes *s1 = b;
return s0->pktsize - s1->pktsize;
}
#ifdef DEBUG
SCMutexInit(&segment_pool_memuse_mutex, NULL);
#endif
uint16_t u16 = 0;
for (u16 = 0; u16 < segment_pool_num; u16++)
{
SCMutexInit(&segment_pool_mutex[u16], NULL);
SCMutexLock(&segment_pool_mutex[u16]);
segment_pool[u16] = PoolInit(segment_pool_poolsizes[u16],
segment_pool_poolsizes_prealloc[u16],
sizeof (TcpSegment),
TcpSegmentPoolAlloc, TcpSegmentPoolInit,
(void *) &segment_pool_pktsizes[u16],
TcpSegmentPoolCleanup, NULL);
SCMutexUnlock(&segment_pool_mutex[u16]);
int StreamTcpReassemblyConfig(char quiet)
{
Pool **my_segment_pool = NULL;
SCMutex *my_segment_lock = NULL;
uint16_t *my_segment_pktsizes = NULL;
SegmentSizes sizes[256];
memset(&sizes, 0x00, sizeof(sizes));
int npools = 0;
ConfNode *segs = ConfGetNode("stream.reassembly.segments");
if (segs != NULL) {
ConfNode *seg;
TAILQ_FOREACH(seg, &segs->head, next) {
ConfNode *segsize = ConfNodeLookupChild(seg,"size");
if (segsize == NULL)
continue;
ConfNode *segpre = ConfNodeLookupChild(seg,"prealloc");
if (segpre == NULL)
continue;
if (npools >= 256) {
SCLogError(SC_ERR_INVALID_ARGUMENT, "too many segment packet "
"pools defined, max is 256");
return -1;
}
SCLogDebug("segsize->val %s", segsize->val);
SCLogDebug("segpre->val %s", segpre->val);
uint16_t pktsize = 0;
if (ByteExtractStringUint16(&pktsize, 10, strlen(segsize->val),
segsize->val) == -1)
{
SCLogError(SC_ERR_INVALID_ARGUMENT, "segment packet size "
"of %s is invalid", segsize->val);
return -1;
}
uint32_t prealloc = 0;
if (ByteExtractStringUint32(&prealloc, 10, strlen(segpre->val),
segpre->val) == -1)
{
SCLogError(SC_ERR_INVALID_ARGUMENT, "segment prealloc of "
"%s is invalid", segpre->val);
return -1;
}
sizes[npools].pktsize = pktsize;
sizes[npools].prealloc = prealloc;
SCLogDebug("pktsize %u, prealloc %u", sizes[npools].pktsize,
sizes[npools].prealloc);
npools++;
}
}
SCLogDebug("npools %d", npools);
if (npools > 0) {
/* sort the array as the index code below relies on it */
qsort(&sizes, npools, sizeof(sizes[0]), SortByPktsize);
if (sizes[npools - 1].pktsize != 0xffff) {
sizes[npools].pktsize = 0xffff;
sizes[npools].prealloc = 8;
npools++;
SCLogInfo("appended a segment pool for pktsize 65536");
}
} else if (npools == 0) {
/* defaults */
sizes[0].pktsize = 4;
sizes[0].prealloc = 256;
sizes[1].pktsize = 16;
sizes[1].prealloc = 512;
sizes[2].pktsize = 112;
sizes[2].prealloc = 512;
sizes[3].pktsize = 248;
sizes[3].prealloc = 512;
sizes[4].pktsize = 512;
sizes[4].prealloc = 512;
sizes[5].pktsize = 768;
sizes[5].prealloc = 1024;
sizes[6].pktsize = 1448;
sizes[6].prealloc = 1024;
sizes[7].pktsize = 0xffff;
sizes[7].prealloc = 128;
npools = 8;
}
int i = 0;
for (i = 0; i < npools; i++) {
SCLogDebug("pktsize %u, prealloc %u", sizes[i].pktsize, sizes[i].prealloc);
}
my_segment_pool = SCMalloc(npools * sizeof(Pool *));
if (my_segment_pool == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "malloc failed");
return -1;
}
my_segment_lock = SCMalloc(npools * sizeof(SCMutex));
if (my_segment_lock == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "malloc failed");
SCFree(my_segment_pool);
return -1;
}
my_segment_pktsizes = SCMalloc(npools * sizeof(uint16_t));
if (my_segment_pktsizes == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "malloc failed");
SCFree(my_segment_lock);
SCFree(my_segment_pool);
return -1;
}
uint32_t my_segment_poolsizes[npools];
for (i = 0; i < npools; i++) {
my_segment_pktsizes[i] = sizes[i].pktsize;
my_segment_poolsizes[i] = sizes[i].prealloc;
SCMutexInit(&my_segment_lock[i], NULL);
/* setup the pool */
SCMutexLock(&my_segment_lock[i]);
my_segment_pool[i] = PoolInit(0, my_segment_poolsizes[i], 0,
TcpSegmentPoolAlloc, TcpSegmentPoolInit,
(void *) &my_segment_pktsizes[i],
TcpSegmentPoolCleanup, NULL);
SCMutexUnlock(&my_segment_lock[i]);
BUG_ON(my_segment_pool[i] == NULL);
SCLogDebug("my_segment_pktsizes[i] %u, my_segment_poolsizes[i] %u",
my_segment_pktsizes[i], my_segment_poolsizes[i]);
if (!quiet)
SCLogInfo("segment pool: pktsize %u, prealloc %u",
my_segment_pktsizes[i], my_segment_poolsizes[i]);
}
uint16_t idx = 0;
u16 = 0;
uint16_t u16 = 0;
while (1) {
if (idx <= segment_pool_pktsizes[u16]) {
if (idx <= my_segment_pktsizes[u16]) {
segment_pool_idx[idx] = u16;
if (segment_pool_pktsizes[u16] == idx)
if (my_segment_pktsizes[u16] == idx)
u16++;
}
@ -318,7 +434,25 @@ int StreamTcpReassembleInit(char quiet)
idx++;
}
/* set the globals */
segment_pool = my_segment_pool;
segment_pool_mutex = my_segment_lock;
segment_pool_pktsizes = my_segment_pktsizes;
segment_pool_num = npools;
return 0;
}
int StreamTcpReassembleInit(char quiet)
{
/* init the memcap/use tracker */
SC_ATOMIC_INIT(ra_memuse);
if (StreamTcpReassemblyConfig(quiet) < 0)
return -1;
StreamMsgQueuesInit();
#ifdef DEBUG
SCMutexInit(&segment_pool_memuse_mutex, NULL);
SCMutexInit(&segment_pool_cnt_mutex, NULL);
#endif
return 0;
@ -348,6 +482,12 @@ void StreamTcpReassembleFree(char quiet)
SCMutexUnlock(&segment_pool_mutex[u16]);
SCMutexDestroy(&segment_pool_mutex[u16]);
}
SCFree(segment_pool);
SCFree(segment_pool_mutex);
SCFree(segment_pool_pktsizes);
segment_pool = NULL;
segment_pool_mutex = NULL;
segment_pool_pktsizes = NULL;
StreamMsgQueuesDeinit(quiet);

Loading…
Cancel
Save