]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #1873 in SNORT/snort3 from ~MMATIRKO/snort3:perfmon_reload_tune...
authorMike Stepanek (mstepane) <mstepane@cisco.com>
Mon, 16 Dec 2019 20:39:58 +0000 (20:39 +0000)
committerMike Stepanek (mstepane) <mstepane@cisco.com>
Mon, 16 Dec 2019 20:39:58 +0000 (20:39 +0000)
Squashed commit of the following:

commit 1330041efbc4410e003415c46d96dfff98929d60
Author: Michael Matirko <mmatirko@cisco.com>
Date:   Mon Nov 25 15:01:39 2019 -0500

    perf_monitor: tuning for flow_ip_memcap on reload

src/hash/xhash.cc
src/hash/xhash.h
src/main/snort.h
src/network_inspectors/perf_monitor/CMakeLists.txt
src/network_inspectors/perf_monitor/flow_ip_tracker.cc
src/network_inspectors/perf_monitor/flow_ip_tracker.h
src/network_inspectors/perf_monitor/perf_module.cc
src/network_inspectors/perf_monitor/perf_module.h
src/network_inspectors/perf_monitor/perf_monitor.cc
src/network_inspectors/perf_monitor/perf_pegs.h [new file with mode: 0644]
src/network_inspectors/perf_monitor/perf_reload_tuner.h [new file with mode: 0644]

index 6c001e593eeda47fac498eeb55732528b883fcea..72338cbb7a0ffd8e941f73b7fcf428bb5b3d72a5 100644 (file)
@@ -261,7 +261,7 @@ static void xhash_delete_free_list(XHash* t)
 }
 
 /*!
- * Try to change the memcap
+ *  Try to change the memcap
  *  Behavior is undefined when t->usrfree is set
  *
  * t             SFXHASH table pointer
@@ -309,11 +309,10 @@ int xhash_change_memcap(XHash *t, unsigned long new_memcap, unsigned *max_work)
     return XHASH_OK;
 }
 
-
 /*!
  *  Delete the hash Table
  *
- *  free key's, free node's, and free the users data.
+ *  free keys, free nodes, and free the users data.
  *
  * h XHash table pointer
  *
@@ -548,7 +547,7 @@ static void movetofront(XHash* t, XHashNode* n)
 }
 
 /*
- * Allocat a new hash node, uses Auto Node Recovery if needed and enabled.
+ * Allocate a new hash node, uses Auto Node Recovery if needed and enabled.
  *
  * The oldest node is the one with the longest time since it was last touched,
  * and does not have any direct indication of how long the node has been around.
@@ -747,10 +746,8 @@ int xhash_add(XHash* t, void* key, void* data)
  * t XHash table pointer
  * key  users key pointer
  *
- * return integer
- * retval XHASH_OK      success
- * retval XHASH_INTABLE already in the table, t->cnode points to the node
- * retval XHASH_NOMEM   not enough memory
+ * return XHashNode*
+ * retval nullptr      failed to add node
  */
 XHashNode* xhash_get_node(XHash* t, const void* key)
 {
@@ -806,6 +803,38 @@ XHashNode* xhash_get_node(XHash* t, const void* key)
     return hnode;
 }
 
+/*!
+ * Add a key to the hash table, return the hash node
+ * If space is not available, it will remove a single
+ * node (the LRU) and replace it with a new node.
+ *
+ * This function handles the scenario that we don't
+ * have enough room to allocate a new node - if this is the
+ * case, additional pruning should be done
+ *
+ * t XHash table pointer
+ * key  users key pointer
+ * prune_performed  bool pointer, 1 = successful prune, 0 = no/failed prune
+ *
+ * return XHashNode*
+ * retval XhashNode*    successfully allocated
+ * retval nullptr       failed to allocate
+ */
+XHashNode* xhash_get_node_with_prune(XHash* t, const void* key, bool* prune_performed)
+{
+    size_t mem_after_alloc = t->mc.memused + xhash_required_mem(t);
+    bool over_capacity = (t->mc.memcap < mem_after_alloc);
+    XHashNode* hnode = nullptr;
+
+    if (over_capacity)
+        *prune_performed = (xhash_free_anr_lru(t) == XHASH_OK);
+
+    if (*prune_performed or !over_capacity)
+        hnode = xhash_get_node(t, key);
+
+    return hnode;
+}
+
 /*!
  * Find a Node based on the key
  *
@@ -1065,6 +1094,33 @@ int xhash_free_anr_lru(XHash *t)
     return XHASH_ERR;
 }
 
+/*!
+ * Unlink and free nodes if the xhash is exceeding the
+ * specified memcap
+ *
+ * t            XHash table pointer
+ * num_freed    set to the number of nodes freed
+ * work_limit   the max amount of work to do
+ *
+ * returns
+ * XHASH_ERR if error occurs
+ * XHASH_OK  if node(s) freed and memused is within memcap
+ * XHASH_PENDING if additional pending is required
+ */
+int xhash_free_overallocations(XHash* t, unsigned work_limit, unsigned* num_freed)
+{
+
+    while (t->mc.memcap < t->mc.memused and work_limit--)
+    {
+        if (xhash_free_anr_lru(t) != XHASH_OK)
+            return XHASH_ERR;
+
+        ++*num_freed;
+    }
+
+    return (t->mc.memcap > t->mc.memused) ? XHASH_OK : XHASH_PENDING;
+}
+
 /*!
  * Find and return the first hash table node
  *
index 977f2f5eaa954f4f97ba213dc1eaeef869ee6054..ff530e383159eb716468812f8d893d0575309a83 100644 (file)
@@ -101,11 +101,13 @@ SO_PUBLIC XHash* xhash_new(int nrows, int keysize, int datasize, unsigned long m
 
 SO_PUBLIC void xhash_set_max_nodes(XHash* h, int max_nodes);
 SO_PUBLIC int xhash_change_memcap(XHash *t, unsigned long new_memcap, unsigned *max_work);
+SO_PUBLIC int xhash_free_overallocations(XHash* t, unsigned work_limit, unsigned* num_freed);
 SO_PUBLIC void xhash_delete(XHash* h);
 SO_PUBLIC int xhash_make_empty(XHash*);
 
 SO_PUBLIC int xhash_add(XHash* h, void* key, void* data);
 SO_PUBLIC XHashNode* xhash_get_node(XHash* t, const void* key);
+SO_PUBLIC XHashNode* xhash_get_node_with_prune(XHash* t, const void* key, bool* prune_performed);
 SO_PUBLIC int xhash_remove(XHash* h, void* key);
 
 //  Get the # of Nodes in HASH the table
@@ -136,6 +138,10 @@ inline unsigned xhash_overhead_bytes(XHash* t)
 inline unsigned xhash_overhead_blocks(XHash* t)
 { return t->overhead_blocks; }
 
+// Get the amount of space required to allocate a new node in the xhash t.
+inline size_t xhash_required_mem(XHash *t)
+{ return sizeof(XHashNode) + t->pad + t->keysize + t->datasize; }
+
 SO_PUBLIC int xhash_free_anr_lru(XHash* t);
 SO_PUBLIC void* xhash_mru(XHash* t);
 SO_PUBLIC void* xhash_lru(XHash* t);
index 0d51b1686ab5f1f9b9abd0ccaf2ba8527f444174..1ca9725d488763203ede7bb5b47025c12813cb31 100644 (file)
@@ -47,8 +47,8 @@ public:
     static void cleanup();
 
     static bool is_starting();
-    static bool is_reloading();
     static bool has_dropped_privileges();
+    SO_PUBLIC static bool is_reloading();
 
 private:
     static void init(int, char**);
index 0c8c38e9db8ec0d6ffafc7bdd559e77c88bc7571..f13548656651a3c9737baa5dd33d8deb7a1458ce 100644 (file)
@@ -21,6 +21,8 @@ set ( FILE_LIST
     perf_module.cc
     perf_module.h
     perf_monitor.cc
+    perf_pegs.h
+    perf_reload_tuner.h
     perf_tracker.cc
     perf_tracker.h
     text_formatter.cc
index 8f074a209a6478a27e327e8f958f89b92b64cb1b..d64798d3ba83256bfe50c048d2ca28cc4b6276eb 100644 (file)
 #include "log/messages.h"
 #include "protocols/packet.h"
 
+#include "perf_pegs.h"
+
 using namespace snort;
 
+// The default number of rows used for the xhash ip_map
+#define DEFAULT_XHASH_NROWS 1021
 #define TRACKER_NAME PERF_NAME "_flow_ip"
 
 struct FlowStateKey
@@ -42,6 +46,7 @@ FlowStateValue* FlowIPTracker::find_stats(const SfIp* src_addr, const SfIp* dst_
 {
     FlowStateKey key;
     FlowStateValue* value = nullptr;
+    bool prune_required = false;
 
     if (src_addr->less_than(*dst_addr))
     {
@@ -59,12 +64,19 @@ FlowStateValue* FlowIPTracker::find_stats(const SfIp* src_addr, const SfIp* dst_
     value = (FlowStateValue*)xhash_find(ip_map, &key);
     if (!value)
     {
-        XHashNode* node = xhash_get_node(ip_map, &key);
+        XHashNode* node = xhash_get_node_with_prune(ip_map, &key, &prune_required);
 
         if (!node)
         {
             return nullptr;
         }
+
+        if (prune_required)
+        {
+            ++pmstats.total_frees;
+            ++pmstats.alloc_prunes;
+        }
+
         memset(node->data, 0, sizeof(FlowStateValue));
         value = (FlowStateValue*)node->data;
     }
@@ -72,8 +84,27 @@ FlowStateValue* FlowIPTracker::find_stats(const SfIp* src_addr, const SfIp* dst_
     return value;
 }
 
+bool FlowIPTracker::initialize(size_t new_memcap)
+{
+    bool need_pruning = false;
+
+    if (!ip_map)
+    {
+        ip_map = xhash_new(DEFAULT_XHASH_NROWS, sizeof(FlowStateKey), sizeof(FlowStateValue),
+            new_memcap, 1, nullptr, nullptr, 1);
+    }
+    else
+    {
+        need_pruning = (new_memcap < memcap);
+        memcap = new_memcap;
+        ip_map->mc.memcap = memcap;
+    }
+
+    return need_pruning;
+}
+
 FlowIPTracker::FlowIPTracker(PerfConfig* perf) : PerfTracker(perf, TRACKER_NAME),
-    perf_flags(perf->perf_flags)
+    perf_flags(perf->perf_flags), perf_conf(perf)
 {
     formatter->register_section("flow_ip");
     formatter->register_field("ip_a", ip_a);
@@ -110,8 +141,10 @@ FlowIPTracker::FlowIPTracker(PerfConfig* perf) : PerfTracker(perf, TRACKER_NAME)
         &stats.state_changes[SFS_STATE_UDP_CREATED]);
     formatter->finalize_fields();
 
-    ip_map = xhash_new(1021, sizeof(FlowStateKey), sizeof(FlowStateValue),
-        perf->flowip_memcap, 1, nullptr, nullptr, 1);
+    memcap = perf->flowip_memcap;
+
+    ip_map = xhash_new(DEFAULT_XHASH_NROWS, sizeof(FlowStateKey), sizeof(FlowStateValue),
+        memcap, 1, nullptr, nullptr, 1);
 
     if (!ip_map)
         FatalError("Unable to allocate memory for FlowIP stats\n");
index 0ee16f0f4f4f3b07d7e6e2a168b02c871ffa99f9..ca5a869639cd4727cfcd67ab461559e057f4a660 100644 (file)
 #ifndef FLOW_IP_TRACKER_H
 #define FLOW_IP_TRACKER_H
 
-#include "perf_tracker.h"
-
 #include "hash/xhash.h"
 
+#include "perf_tracker.h"
+
 enum FlowState
 {
     SFS_STATE_TCP_ESTABLISHED = 0,
@@ -63,21 +63,25 @@ public:
     FlowIPTracker(PerfConfig* perf);
     ~FlowIPTracker() override;
 
+    bool initialize(size_t new_memcap);
     void reset() override;
     void update(snort::Packet*) override;
     void process(bool) override;
-
     int update_state(const snort::SfIp* src_addr, const snort::SfIp* dst_addr, FlowState);
+    snort::XHash* get_ip_map()
+        { return ip_map; }
 
 private:
     FlowStateValue stats;
     snort::XHash* ip_map;
     char ip_a[41], ip_b[41];
     int perf_flags;
-
+    PerfConfig* perf_conf;
+    size_t memcap;
     FlowStateValue* find_stats(const snort::SfIp* src_addr, const snort::SfIp* dst_addr, int* swapped);
     void write_stats();
     void display_stats();
+
 };
 #endif
 
index b9de75f84fa78a550f76fd5d1629336a618c209b..31eec38ec2c24711d5f32c6e672a0b5639736088 100644 (file)
 #include "perf_module.h"
 
 #include "log/messages.h"
+#include "main/snort.h"
 #include "managers/module_manager.h"
 
+#include "perf_pegs.h"
+#include "perf_reload_tuner.h"
+
 using namespace snort;
 
 //-------------------------------------------------------------------------
@@ -201,8 +205,16 @@ bool PerfMonModule::begin(const char* fqn, int idx, SnortConfig*)
     return true;
 }
 
-bool PerfMonModule::end(const char* fqn, int idx, SnortConfig*)
+bool PerfMonModule::end(const char* fqn, int idx, SnortConfig* sc)
 {
+
+    perfmon_rrt.set_memcap(config->flowip_memcap);
+
+    if ( Snort::is_reloading() )
+    {
+        sc->register_reload_resource_tuner(perfmon_rrt);
+    }
+
     if ( idx != 0 && strcmp(fqn, "perf_monitor.modules") == 0 )
         return config->modules.back().confirm_parse();
 
@@ -210,14 +222,14 @@ bool PerfMonModule::end(const char* fqn, int idx, SnortConfig*)
 }
 
 PerfConfig* PerfMonModule::get_config()
-{ 
+{
     PerfConfig* tmp = config;
     config = nullptr;
-    return tmp; 
+    return tmp;
 }
 
 const PegInfo* PerfMonModule::get_pegs() const
-{ return simple_pegs; }
+{ return perf_module_pegs; }
 
 PegCount* PerfMonModule::get_counts() const
 { return (PegCount*)&pmstats; }
@@ -291,7 +303,7 @@ bool PerfConfig::resolve()
 {
     if ( modules.empty() )
     {
-        auto all_modules = ModuleManager::get_all_modules();       
+        auto all_modules = ModuleManager::get_all_modules();
         for ( auto& mod : all_modules )
         {
             ModuleConfig cfg;
index 14353deef2deb94b78b2e9e19032b60ec42e1f72..6091ccf1fbe61a6c828e1e260f435919b5527ff4 100644 (file)
@@ -25,6 +25,9 @@
 
 #include "framework/module.h"
 
+#include "perf_pegs.h"
+#include "perf_reload_tuner.h"
+
 #define PERF_NAME "perf_monitor"
 #define PERF_HELP "performance monitoring and flow statistics collection"
 
@@ -114,9 +117,10 @@ public:
 
 private:
     PerfConfig* config = nullptr;
+    PerfMonReloadTuner perfmon_rrt;
 };
 
-extern THREAD_LOCAL SimpleStats pmstats;
+extern THREAD_LOCAL PerfPegStats pmstats;
 extern THREAD_LOCAL snort::ProfileStats perfmonStats;
 
 #endif
index c7a7f03965ce51ce7e2bcec4783c695a99b4b5e8..0b5e913d09d96d6197d979aabe4bb7ac41154f4e 100644 (file)
@@ -29,6 +29,7 @@
 #endif
 
 #include "framework/data_bus.h"
+#include "hash/xhash.h"
 #include "log/messages.h"
 #include "managers/inspector_manager.h"
 #include "profiler/profiler.h"
 #include "flow_tracker.h"
 #include "perf_module.h"
 
+
 #ifdef UNIT_TEST
 #include "catch/snort_catch.h"
 #endif
 
 using namespace snort;
 
-THREAD_LOCAL SimpleStats pmstats;
+THREAD_LOCAL PerfPegStats pmstats;
 THREAD_LOCAL ProfileStats perfmonStats;
 
 static THREAD_LOCAL std::vector<PerfTracker*>* trackers;
 static THREAD_LOCAL FlowIPTracker* flow_ip_tracker = nullptr;
+
 //-------------------------------------------------------------------------
 // class stuff
 //-------------------------------------------------------------------------
@@ -77,7 +80,6 @@ public:
 
 private:
     PerfConfig* const config;
-
     void disable_tracker(size_t);
 };
 
@@ -256,6 +258,30 @@ void PerfMonitor::tinit()
         tracker->reset();
 }
 
+bool PerfMonReloadTuner::tinit()
+{
+    if (flow_ip_tracker)
+        return flow_ip_tracker->initialize(memcap);
+    else
+        return false;
+}
+
+bool PerfMonReloadTuner::tune_resources(unsigned work_limit)
+{
+    if (flow_ip_tracker)
+    {
+        unsigned num_freed = 0;
+        int result = xhash_free_overallocations(flow_ip_tracker->get_ip_map(), work_limit, &num_freed);
+        pmstats.total_frees += num_freed;
+        pmstats.reload_frees += num_freed;
+        return (result == XHASH_OK);
+    }
+    else
+    {
+        return false;
+    }
+}
+
 void PerfMonitor::tterm()
 {
     if (trackers)
@@ -408,7 +434,7 @@ const BaseApi* nin_perf_monitor[] =
 #ifdef UNIT_TEST
 TEST_CASE("Process timing logic", "[perfmon]")
 {
-    PerfMonModule mod; 
+    PerfMonModule mod;
     PerfConfig* config = new PerfConfig;
     mod.set_config(config);
     PerfMonitor perfmon(mod.get_config());
diff --git a/src/network_inspectors/perf_monitor/perf_pegs.h b/src/network_inspectors/perf_monitor/perf_pegs.h
new file mode 100644 (file)
index 0000000..3c8a5c4
--- /dev/null
@@ -0,0 +1,47 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// 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 along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// perf_pegs.h author Michael Matirko <mmatirko@cisco.com>
+
+
+#ifndef PERF_PEGS_H
+#define PERF_PEGS_H
+
+#include "framework/counts.h"
+#include "main/snort_types.h"
+
+static const PegInfo perf_module_pegs[] =
+{
+
+    { CountType::SUM, "packets", "total packets processed by performance monitor" },
+    { CountType::SUM, "total_frees", "total flows pruned or freed by performance monitor" },
+    { CountType::SUM, "reload_frees", "flows freed on reload with changed memcap" },
+    { CountType::SUM, "alloc_prunes", "flows pruned on allocation of IP flows" },
+    { CountType::END, nullptr, nullptr },
+};
+
+struct PerfPegStats
+{
+    PegCount total_packets;
+    PegCount total_frees;
+    PegCount reload_frees;
+    PegCount alloc_prunes;
+};
+
+#endif
+
diff --git a/src/network_inspectors/perf_monitor/perf_reload_tuner.h b/src/network_inspectors/perf_monitor/perf_reload_tuner.h
new file mode 100644 (file)
index 0000000..9cd8eaa
--- /dev/null
@@ -0,0 +1,53 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// 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 along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// perf_reload_tuner.h author Michael Matirko <mmatirko@cisco.com>
+
+#ifndef PERF_RELOAD_TUNER_H
+#define PERF_RELOAD_TUNER_H
+
+#include "main/snort_config.h"
+
+class PerfMonReloadTuner : public snort::ReloadResourceTuner
+{
+public:
+    PerfMonReloadTuner() = default;
+
+    bool tinit() override;
+
+    bool tune_idle_context() override
+        { return tune_resources(max_work_idle); }
+
+    bool tune_packet_context() override
+        { return tune_resources(max_work); }
+
+    bool tune_resources(unsigned work_limit);
+
+    void set_memcap(size_t new_memcap)
+        { memcap = new_memcap; }
+
+    size_t get_memcap()
+        { return memcap; }
+
+private:
+    size_t memcap = 0;
+
+};
+
+#endif
+