mirror of https://github.com/OISF/suricata
stream: reduce pool locking overhead
Add thread local cache to avoid locking overhead for ssns and segments. A thread will return segments/ssns to a local cache first, and if that is full, to a return queue where the actual return to the pool returns a batch, to amortize locking overhead. Adds segment and session pool/cache counters to see where how effective the cache is.pull/7957/head
parent
536d66e344
commit
235f369ab9
@ -0,0 +1,196 @@
|
|||||||
|
/* Copyright (C) 2007-2022 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>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "suricata-common.h"
|
||||||
|
#include "stream-tcp-private.h"
|
||||||
|
#include "stream-tcp-cache.h"
|
||||||
|
|
||||||
|
typedef struct TcpPoolCache {
|
||||||
|
bool cache_enabled; /**< cache should only be enabled for worker threads */
|
||||||
|
TcpSegment *segs_cache[64];
|
||||||
|
uint32_t segs_cache_idx;
|
||||||
|
uint32_t segs_returns_idx;
|
||||||
|
TcpSegment *segs_returns[64];
|
||||||
|
|
||||||
|
TcpSession *ssns_cache[64];
|
||||||
|
uint32_t ssns_cache_idx;
|
||||||
|
uint32_t ssns_returns_idx;
|
||||||
|
TcpSession *ssns_returns[64];
|
||||||
|
} TcpPoolCache;
|
||||||
|
|
||||||
|
static thread_local TcpPoolCache tcp_pool_cache;
|
||||||
|
extern PoolThread *ssn_pool;
|
||||||
|
extern PoolThread *segment_thread_pool;
|
||||||
|
|
||||||
|
/** \brief enable segment cache. Should only be done for worker threads */
|
||||||
|
void StreamTcpThreadCacheEnable(void)
|
||||||
|
{
|
||||||
|
tcp_pool_cache.cache_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamTcpThreadCacheReturnSegment(TcpSegment *seg)
|
||||||
|
{
|
||||||
|
SCEnter();
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
if (RunmodeIsUnittests()) {
|
||||||
|
PoolThreadReturn(segment_thread_pool, seg);
|
||||||
|
SCReturn;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* cache can have segs from any pool id */
|
||||||
|
if (tcp_pool_cache.cache_enabled && tcp_pool_cache.segs_cache_idx < 64) {
|
||||||
|
tcp_pool_cache.segs_cache[tcp_pool_cache.segs_cache_idx++] = seg;
|
||||||
|
} else {
|
||||||
|
/* segs_returns should only have a single pool id. If ours is different,
|
||||||
|
* flush it. */
|
||||||
|
bool flush = false;
|
||||||
|
if (tcp_pool_cache.segs_returns_idx &&
|
||||||
|
tcp_pool_cache.segs_returns[0]->pool_id != seg->pool_id) {
|
||||||
|
flush = true;
|
||||||
|
}
|
||||||
|
if (tcp_pool_cache.segs_returns_idx == 64) {
|
||||||
|
flush = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flush) {
|
||||||
|
PoolThreadId pool_id = tcp_pool_cache.segs_returns[0]->pool_id;
|
||||||
|
PoolThreadLock(segment_thread_pool, pool_id);
|
||||||
|
for (uint32_t i = 0; i < tcp_pool_cache.segs_returns_idx; i++) {
|
||||||
|
TcpSegment *ret_seg = tcp_pool_cache.segs_returns[i];
|
||||||
|
PoolThreadReturnRaw(segment_thread_pool, pool_id, ret_seg);
|
||||||
|
}
|
||||||
|
PoolThreadUnlock(segment_thread_pool, pool_id);
|
||||||
|
tcp_pool_cache.segs_returns_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp_pool_cache.segs_returns[tcp_pool_cache.segs_returns_idx++] = seg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamTcpThreadCacheReturnSession(TcpSession *ssn)
|
||||||
|
{
|
||||||
|
SCEnter();
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
if (RunmodeIsUnittests()) {
|
||||||
|
PoolThreadReturn(ssn_pool, ssn);
|
||||||
|
SCReturn;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* cache can have ssns from any pool id */
|
||||||
|
if (tcp_pool_cache.cache_enabled && tcp_pool_cache.ssns_cache_idx < 64) {
|
||||||
|
tcp_pool_cache.ssns_cache[tcp_pool_cache.ssns_cache_idx++] = ssn;
|
||||||
|
} else {
|
||||||
|
/* ssns_returns should only have a single pool id. If ours is different,
|
||||||
|
* flush it. */
|
||||||
|
bool flush = false;
|
||||||
|
if (tcp_pool_cache.ssns_returns_idx &&
|
||||||
|
tcp_pool_cache.ssns_returns[0]->pool_id != ssn->pool_id) {
|
||||||
|
flush = true;
|
||||||
|
}
|
||||||
|
if (tcp_pool_cache.ssns_returns_idx == 64) {
|
||||||
|
flush = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flush) {
|
||||||
|
PoolThreadId pool_id = tcp_pool_cache.ssns_returns[0]->pool_id;
|
||||||
|
PoolThreadLock(ssn_pool, pool_id);
|
||||||
|
for (uint32_t i = 0; i < tcp_pool_cache.ssns_returns_idx; i++) {
|
||||||
|
TcpSession *ret_ssn = tcp_pool_cache.ssns_returns[i];
|
||||||
|
PoolThreadReturnRaw(ssn_pool, pool_id, ret_ssn);
|
||||||
|
}
|
||||||
|
PoolThreadUnlock(ssn_pool, pool_id);
|
||||||
|
tcp_pool_cache.ssns_returns_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp_pool_cache.ssns_returns[tcp_pool_cache.ssns_returns_idx++] = ssn;
|
||||||
|
}
|
||||||
|
SCReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamTcpThreadCacheCleanup(void)
|
||||||
|
{
|
||||||
|
SCEnter();
|
||||||
|
|
||||||
|
/* segments */
|
||||||
|
SCLogDebug("tcp_pool_cache.segs_cache_idx %u", tcp_pool_cache.segs_cache_idx);
|
||||||
|
for (uint32_t i = 0; i < tcp_pool_cache.segs_cache_idx; i++) {
|
||||||
|
PoolThreadReturn(segment_thread_pool, tcp_pool_cache.segs_cache[i]);
|
||||||
|
}
|
||||||
|
tcp_pool_cache.segs_cache_idx = 0;
|
||||||
|
|
||||||
|
SCLogDebug("tcp_pool_cache.segs_returns_idx %u", tcp_pool_cache.segs_returns_idx);
|
||||||
|
if (tcp_pool_cache.segs_returns_idx) {
|
||||||
|
PoolThreadId pool_id = tcp_pool_cache.segs_returns[0]->pool_id;
|
||||||
|
PoolThreadLock(segment_thread_pool, pool_id);
|
||||||
|
for (uint32_t i = 0; i < tcp_pool_cache.segs_returns_idx; i++) {
|
||||||
|
TcpSegment *ret_seg = tcp_pool_cache.segs_returns[i];
|
||||||
|
PoolThreadReturnRaw(segment_thread_pool, pool_id, ret_seg);
|
||||||
|
}
|
||||||
|
PoolThreadUnlock(segment_thread_pool, pool_id);
|
||||||
|
tcp_pool_cache.segs_returns_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sessions */
|
||||||
|
SCLogDebug("tcp_pool_cache.ssns_cache_idx %u", tcp_pool_cache.ssns_cache_idx);
|
||||||
|
for (uint32_t i = 0; i < tcp_pool_cache.ssns_cache_idx; i++) {
|
||||||
|
PoolThreadReturn(segment_thread_pool, tcp_pool_cache.ssns_cache[i]);
|
||||||
|
}
|
||||||
|
tcp_pool_cache.ssns_cache_idx = 0;
|
||||||
|
|
||||||
|
SCLogDebug("tcp_pool_cache.ssns_returns_idx %u", tcp_pool_cache.ssns_returns_idx);
|
||||||
|
if (tcp_pool_cache.ssns_returns_idx) {
|
||||||
|
PoolThreadId pool_id = tcp_pool_cache.ssns_returns[0]->pool_id;
|
||||||
|
PoolThreadLock(segment_thread_pool, pool_id);
|
||||||
|
for (uint32_t i = 0; i < tcp_pool_cache.ssns_returns_idx; i++) {
|
||||||
|
TcpSession *ret_ssn = tcp_pool_cache.ssns_returns[i];
|
||||||
|
PoolThreadReturnRaw(segment_thread_pool, pool_id, ret_ssn);
|
||||||
|
}
|
||||||
|
PoolThreadUnlock(segment_thread_pool, pool_id);
|
||||||
|
tcp_pool_cache.ssns_returns_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
TcpSegment *StreamTcpThreadCacheGetSegment(void)
|
||||||
|
{
|
||||||
|
if (tcp_pool_cache.segs_cache_idx) {
|
||||||
|
TcpSegment *seg = tcp_pool_cache.segs_cache[tcp_pool_cache.segs_cache_idx - 1];
|
||||||
|
tcp_pool_cache.segs_cache_idx--;
|
||||||
|
memset(&seg->sbseg, 0, sizeof(seg->sbseg));
|
||||||
|
return seg;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
TcpSession *StreamTcpThreadCacheGetSession(void)
|
||||||
|
{
|
||||||
|
if (tcp_pool_cache.ssns_cache_idx) {
|
||||||
|
TcpSession *ssn = tcp_pool_cache.ssns_cache[tcp_pool_cache.ssns_cache_idx - 1];
|
||||||
|
tcp_pool_cache.ssns_cache_idx--;
|
||||||
|
return ssn;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/* Copyright (C) 2007-2022 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>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __STREAM_TCP_CACHE_H__
|
||||||
|
#define __STREAM_TCP_CACHE_H__
|
||||||
|
|
||||||
|
#include "suricata.h"
|
||||||
|
#include "flow.h"
|
||||||
|
#include "stream-tcp-private.h"
|
||||||
|
|
||||||
|
void StreamTcpThreadCacheEnable(void);
|
||||||
|
void StreamTcpThreadCacheReturnSegment(TcpSegment *seg);
|
||||||
|
void StreamTcpThreadCacheReturnSession(TcpSession *ssn);
|
||||||
|
void StreamTcpThreadCacheCleanup(void);
|
||||||
|
|
||||||
|
TcpSegment *StreamTcpThreadCacheGetSegment(void);
|
||||||
|
TcpSession *StreamTcpThreadCacheGetSession(void);
|
||||||
|
|
||||||
|
#endif /* __STREAM_TCP_CACHE_H__ */
|
Loading…
Reference in New Issue