]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#893] added multi-threading parameters
authorRazvan Becheriu <razvan@isc.org>
Thu, 2 Apr 2020 16:49:27 +0000 (19:49 +0300)
committerRazvan Becheriu <razvan@isc.org>
Tue, 14 Apr 2020 19:20:29 +0000 (22:20 +0300)
24 files changed:
doc/examples/kea4/all-keys.json
doc/examples/kea6/all-keys.json
src/bin/dhcp4/ctrl_dhcp4_srv.cc
src/bin/dhcp4/dhcp4_lexer.ll
src/bin/dhcp4/dhcp4_parser.yy
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/dhcp4_srv.h
src/bin/dhcp4/json_config_parser.cc
src/bin/dhcp4/json_config_parser.h
src/bin/dhcp4/main.cc
src/bin/dhcp4/tests/config_parser_unittest.cc
src/bin/dhcp6/ctrl_dhcp6_srv.cc
src/bin/dhcp6/dhcp6_lexer.ll
src/bin/dhcp6/dhcp6_parser.yy
src/bin/dhcp6/dhcp6_srv.cc
src/bin/dhcp6/dhcp6_srv.h
src/bin/dhcp6/json_config_parser.cc
src/bin/dhcp6/json_config_parser.h
src/bin/dhcp6/main.cc
src/bin/dhcp6/tests/config_parser_unittest.cc
src/lib/dhcpsrv/parsers/simple_parser4.cc
src/lib/dhcpsrv/parsers/simple_parser6.cc
src/lib/dhcpsrv/srv_config.cc
src/lib/dhcpsrv/srv_config.h

index 4289c8d95845ced93b672fcfded0d1dfd0146fbf..098b9a3906c19ee408f50b899ba052d9632858b4 100644 (file)
        // When the maximum count is 0 the maximum age (in seconds) applies.
        "statistic-default-sample-age": 60,
 
+       // By default Kea processes packets on a single thread (default 'false'
+        // value for this option). To enable multi-threading, this option can be
+        // set ('true' value).
+        "enable-multi-threading": false,
+
+        // When multi-threading is enabled, Kea will process packets on a number of
+        // multiple threads configurable through this option. The value must be a
+        // positive integer (0 means auto detect).
+        "packet-thread-pool-size": 0,
+
+        // When multi-threading is enabled, Kea will read packets from the interface
+        // and append a working item to the thread pool. This option configures the
+        // maximum number of items that can be queued for each processing thread.
+        // The value must be a positive integer (0 means unlimited).
+        "packet-thread-queue-size": 0,
+
         // Governs how the Kea DHCPv4 server should deal with the invalid
         // data received from the client.
         "sanity-checks": {
index 34a8ab06757f35f7ae083d6d010cf5e87daa7bcc..7b87fbeac1098235478b92b6d1cdfe4c34649325 100644 (file)
        // When the maximum count is 0 the maximum age (in seconds) applies.
        "statistic-default-sample-age": 60,
 
+       // By default Kea processes packets on a single thread (default 'false'
+        // value for this option). To enable multi-threading, this option can be
+        // set ('true' value).
+        "enable-multi-threading": false,
+
+        // When multi-threading is enabled, Kea will process packets on a number of
+        // multiple threads configurable through this option. The value must be a
+        // positive integer (0 means auto detect).
+        "packet-thread-pool-size": 0,
+
+        // When multi-threading is enabled, Kea will read packets from the interface
+        // and append a working item to the thread pool. This option configures the
+        // maximum number of items that can be queued for each processing thread.
+        // The value must be a positive integer (0 means unlimited).
+        "packet-thread-queue-size": 0,
+
         // Governs how the Kea DHCPv6 server should deal with the invalid
         // data received from the client.
         "sanity-checks": {
index ebbb15ecd78040ff2b75525cdd386f456ecaac2b..25554103a64406fa46d6b4e649097fda82dcca98 100644 (file)
@@ -169,17 +169,20 @@ ControlledDhcpv4Srv::loadConfigFile(const std::string& file_name) {
                       "processCommand(\"config-set\", json)");
         }
 
+        // command line parameters overwrite file and database configuration
         bool enabled = false;
-        if (srv_thread_count >= 0) {
+        if (Dhcpv4Srv::srv_thread_count_ >= 0) {
             enabled = true;
         }
         if (enabled) {
-            CfgMgr::instance().getCurrentCfg()->setServerThreadCount(srv_thread_count);
-            CfgMgr::instance().getCurrentCfg()->setServerMaxThreadQueueSize(4);
+            CfgMgr::instance().getCurrentCfg()->setPktThreadPoolSize(Dhcpv4Srv::srv_thread_count_);
+            CfgMgr::instance().getCurrentCfg()->setPktThreadQueueSize(0);
             LOG_FATAL(dhcp4_logger, DHCP4_MULTI_THREADING_WARNING);
+        } else {
+            enabled = CfgMgr::instance().getCurrentCfg()->getEnableMultiThreading();
         }
         MultiThreadingMgr::instance().apply(enabled,
-            CfgMgr::instance().getCurrentCfg()->getServerThreadCount());
+            CfgMgr::instance().getCurrentCfg()->getPktThreadPoolSize());
 
         // Now check is the returned result is successful (rcode=0) or not
         // (see @ref isc::config::parseAnswer).
@@ -204,7 +207,7 @@ ControlledDhcpv4Srv::loadConfigFile(const std::string& file_name) {
     LOG_WARN(dhcp4_logger, DHCP4_MULTI_THREADING_INFO)
         .arg(MultiThreadingMgr::instance().getMode() ? "yes" : "no")
         .arg(MultiThreadingMgr::instance().getPktThreadPoolSize())
-        .arg(CfgMgr::instance().getCurrentCfg()->getServerMaxThreadQueueSize());
+        .arg(CfgMgr::instance().getCurrentCfg()->getPktThreadQueueSize());
 
     return (result);
 }
index 3ac2fbd4d28f98e5d8f17b55e94014ac0d94d999..688325daff1e19a0c03e29cbb498b90e82bee95c 100644 (file)
@@ -1418,6 +1418,33 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
     }
 }
 
+\"enable-multi-threading\" {
+    switch(driver.ctx_) {
+    case isc::dhcp::Parser4Context::DHCP4:
+        return isc::dhcp::Dhcp4Parser::make_ENABLE_MULTI_THREADING(driver.loc_);
+    default:
+        return isc::dhcp::Dhcp4Parser::make_STRING("enable-multi-threading", driver.loc_);
+    }
+}
+
+\"packet-thread-pool-size\" {
+    switch(driver.ctx_) {
+    case isc::dhcp::Parser4Context::DHCP4:
+        return isc::dhcp::Dhcp4Parser::make_PACKET_THREAD_POOL_SIZE(driver.loc_);
+    default:
+        return isc::dhcp::Dhcp4Parser::make_STRING("packet-thread-pool-size", driver.loc_);
+    }
+}
+
+\"packet-thread-queue-size\" {
+    switch(driver.ctx_) {
+    case isc::dhcp::Parser4Context::DHCP4:
+        return isc::dhcp::Dhcp4Parser::make_PACKET_THREAD_QUEUE_SIZE(driver.loc_);
+    default:
+        return isc::dhcp::Dhcp4Parser::make_STRING("packet-thread-queue-size", driver.loc_);
+    }
+}
+
 \"control-socket\" {
     switch(driver.ctx_) {
     case isc::dhcp::Parser4Context::DHCP4:
@@ -1845,8 +1872,6 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
     }
 }
 
-
-
 {JSONString} {
     /* A string has been matched. It contains the actual string and single quotes.
        We need to get those quotes out of the way and just use its content, e.g.
index 6d26fabb618ab9e3bf38a36e4456adf7d3582390..8d185696afb341a70b344e6c2ccf82f8597e4132 100644 (file)
@@ -187,6 +187,10 @@ using namespace std;
 
   DHCP4O6_PORT "dhcp4o6-port"
 
+  ENABLE_MULTI_THREADING "enable-multi-threading"
+  PACKET_THREAD_POOL_SIZE "packet-thread-pool-size"
+  PACKET_THREAD_QUEUE_SIZE "packet-thread-queue-size"
+
   CONTROL_SOCKET "control-socket"
   SOCKET_TYPE "socket-type"
   SOCKET_NAME "socket-name"
@@ -507,6 +511,9 @@ global_param: valid_lifetime
             | statistic_default_sample_count
             | statistic_default_sample_age
             | unknown_map_entry
+            | enable_multi_threading
+            | packet_thread_pool_size
+            | packet_thread_queue_size
             ;
 
 valid_lifetime: VALID_LIFETIME COLON INTEGER {
@@ -1029,6 +1036,21 @@ flex_id: FLEX_ID {
     ctx.stack_.back()->add(flex_id);
 };
 
+enable_multi_threading: ENABLE_MULTI_THREADING COLON BOOLEAN {
+    ElementPtr b(new BoolElement($3, ctx.loc2pos(@3)));
+    ctx.stack_.back()->set("enable-multi-threading", b);
+};
+
+packet_thread_pool_size: PACKET_THREAD_POOL_SIZE COLON INTEGER {
+    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
+    ctx.stack_.back()->set("packet-thread-pool-size", prf);
+};
+
+packet_thread_queue_size: PACKET_THREAD_QUEUE_SIZE COLON INTEGER {
+    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
+    ctx.stack_.back()->set("packet-thread-queue-size", prf);
+};
+
 hooks_libraries: HOOKS_LIBRARIES {
     ElementPtr l(new ListElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("hooks-libraries", l);
@@ -1995,8 +2017,6 @@ only_if_required: ONLY_IF_REQUIRED COLON BOOLEAN {
 
 // --- end of client classes ---------------------------------
 
-// was server-id but in is DHCPv6-only
-
 dhcp4o6_port: DHCP4O6_PORT COLON INTEGER {
     ElementPtr time(new IntElement($3, ctx.loc2pos(@3)));
     ctx.stack_.back()->set("dhcp4o6-port", time);
@@ -2277,6 +2297,8 @@ control_agent_json_object: CONTROL_AGENT {
     ctx.leave();
 };
 
+// Config control information element
+
 config_control: CONFIG_CONTROL {
     ElementPtr i(new MapElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("config-control", i);
@@ -2472,7 +2494,6 @@ pattern: PATTERN {
     ctx.leave();
 };
 
-
 %%
 
 void
index b6041748aeb8d6c01211198e991466aae3aa116d..9e8e1d5201914ff1c7a2131f5b21775ceff220b5 100644 (file)
@@ -588,6 +588,8 @@ void Dhcpv4Exchange::evaluateClasses(const Pkt4Ptr& pkt, bool depend_on_known) {
 
 const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
 
+int Dhcpv4Srv::srv_thread_count_ = -1;
+
 Dhcpv4Srv::Dhcpv4Srv(uint16_t server_port, uint16_t client_port,
                      const bool use_bcast, const bool direct_response_desired)
     : io_service_(new IOService()), server_port_(server_port),
@@ -937,12 +939,13 @@ Dhcpv4Srv::run_one() {
     try {
         bool read_pkt = true;
 
-        // Do not read more packets from socket if there are enough
-        // packets to be processed in the packet thread pool queue
-        const int max_queue_size = CfgMgr::instance().getCurrentCfg()->getServerMaxThreadQueueSize();
+        // Do not read more packets from socket if there are enough packets to
+        // be processed in the packet thread pool queue
+        // max_queue_size = 0 means no limit
+        const int max_queue_size = CfgMgr::instance().getCurrentCfg()->getPktThreadQueueSize();
         const int thread_count = MultiThreadingMgr::instance().getPktThreadPoolSize();
         size_t pkt_queue_size = MultiThreadingMgr::instance().getPktThreadPool().count();
-        if (thread_count && (pkt_queue_size >= thread_count * max_queue_size)) {
+        if (thread_count && max_queue_size && (pkt_queue_size >= thread_count * max_queue_size)) {
             read_pkt = false;
         }
 
index 106a3de794e5a78b3119403f432304320bf6565b..29db9b61a33b06c2f5b876e6707bc1173be04aa8 100644 (file)
@@ -1061,6 +1061,12 @@ protected:
     CBControlDHCPv4Ptr cb_control_;
 
 public:
+    /// @brief command line parameter thread count
+    /// when parameter is not specified, the default value is used
+    /// the default value is: -1 means disabled (single-threaded),
+    /// 0 means auto-detect, other values set thread count explicitly
+    static int srv_thread_count_;
+
     /// Class methods for DHCPv4-over-DHCPv6 handler
 
     /// @brief Updates statistics for received packets
index 0cec538b4f1de445c15ff9476886ff1fc0a44f81..6f89b9f3c002ebec743f1384ba133dad27f78223 100644 (file)
 #include <database/dbaccess_parser.h>
 #include <database/backend_selector.h>
 #include <database/server_selector.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option_definition.h>
 #include <dhcp4/dhcp4_log.h>
 #include <dhcp4/dhcp4_srv.h>
 #include <dhcp4/json_config_parser.h>
-#include <dhcp/libdhcp++.h>
-#include <dhcp/option_definition.h>
 #include <dhcpsrv/cb_ctl_dhcp4.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/config_backend_dhcp4_mgr.h>
 #include <dhcpsrv/db_type.h>
+#include <dhcpsrv/host_data_source_factory.h>
 #include <dhcpsrv/parsers/client_class_def_parser.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/expiration_config_parser.h>
@@ -31,7 +32,6 @@
 #include <dhcpsrv/parsers/simple_parser4.h>
 #include <dhcpsrv/parsers/shared_networks_list_parser.h>
 #include <dhcpsrv/parsers/sanity_checks_parser.h>
-#include <dhcpsrv/host_data_source_factory.h>
 #include <dhcpsrv/timer_mgr.h>
 #include <process/config_ctl_parser.h>
 #include <hooks/hooks_parser.h>
 #include <util/encode/hex.h>
 #include <util/strutil.h>
 
+#include <boost/algorithm/string.hpp>
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
-#include <boost/algorithm/string.hpp>
 
-#include <limits>
 #include <iostream>
-#include <iomanip>
+#include <limits>
+#include <map>
 #include <netinet/in.h>
 #include <vector>
-#include <map>
 
 using namespace std;
 using namespace isc;
-using namespace isc::dhcp;
 using namespace isc::data;
+using namespace isc::dhcp;
 using namespace isc::asiolink;
 using namespace isc::hooks;
 using namespace isc::process;
@@ -103,6 +102,18 @@ public:
         uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port");
         cfg->setDhcp4o6Port(dhcp4o6_port);
 
+        // Set enable multi threading flag.
+        bool enable_multi_threading = getBoolean(global, "enable-multi-threading");
+        cfg->setEnableMultiThreading(enable_multi_threading);
+
+        // Set packet thread pool size.
+        uint32_t packet_thread_pool_size = getUint32(global, "packet-thread-pool-size");
+        cfg->setPktThreadPoolSize(packet_thread_pool_size);
+
+        // Set packet thread queue size.
+        uint32_t packet_thread_queue_size = getUint32(global, "packet-thread-queue-size");
+        cfg->setPktThreadQueueSize(packet_thread_queue_size);
+
         // Set the global user context.
         ConstElementPtr user_context = global->get("user-context");
         if (user_context) {
@@ -297,7 +308,6 @@ void configureCommandChannel() {
     }
 }
 
-
 isc::data::ConstElementPtr
 configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
                      bool check_only) {
@@ -336,13 +346,13 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
     // have to be restored to global storages.
     bool rollback = false;
     // config_pair holds the details of the current parser when iterating over
-    // the parsers.  It is declared outside the loops so in case of an error,
-    // the name of the failing parser can be retrieved in the "catch" clause.
+    // the parsers.  It is declared outside the loop so in case of error, the
+    // name of the failing parser can be retrieved within the "catch" clause.
     ConfigPair config_pair;
     ElementPtr mutable_cfg;
     SrvConfigPtr srv_cfg;
     try {
-        // Get the staging configuration
+        // Get the staging configuration.
         srv_cfg = CfgMgr::instance().getStagingCfg();
 
         // This is a way to convert ConstElementPtr to ElementPtr.
@@ -353,7 +363,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
         // Relocate dhcp-ddns parameters that have moved to global scope.
         // Rule is that a global value overrides the dhcp-ddns value, so
         // we need to do this before we apply global defaults.
-        // Note this is done for backward compatibilty.
+        // Note this is done for backward compatibility.
         srv_cfg->moveDdnsParams(mutable_cfg);
 
         // Set all default values if not specified by the user.
@@ -505,7 +515,6 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
             }
 
             if (config_pair.first == "shared-networks") {
-
                 /// We need to create instance of SharedNetworks4ListParser
                 /// and parse the list of the shared networks into the
                 /// CfgSharedNetworks4 object. One additional step is then to
@@ -578,11 +587,13 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
                  (config_pair.first == "ddns-qualifying-suffix") ||
                  (config_pair.first == "store-extended-info") ||
                  (config_pair.first == "statistic-default-sample-count") ||
-                 (config_pair.first == "statistic-default-sample-age")) {
+                 (config_pair.first == "statistic-default-sample-age") ||
+                 (config_pair.first == "enable-multi-threading") ||
+                 (config_pair.first == "packet-thread-pool-size") ||
+                 (config_pair.first == "packet-thread-queue-size")) {
                 CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
                                                                         config_pair.second);
                 continue;
-
             }
 
             // Nothing to configure for the user-context.
@@ -604,7 +615,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
         // defined as part of shared networks.
         global_parser.sanityChecks(srv_cfg, mutable_cfg);
 
-        // Validate D2 client confuguration.
+        // Validate D2 client configuration.
         if (!d2_client_cfg) {
             d2_client_cfg.reset(new D2ClientConfig());
         }
@@ -655,7 +666,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
             cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
             CfgMgr::instance().setD2ClientConfig(cfg);
 
-            // This occurs last as if it succeeds, there is no easy way
+            // This occurs last as if it succeeds, there is no easy way to
             // revert it.  As a result, the failure to commit a subsequent
             // change causes problems when trying to roll back.
             const HooksConfig& libraries =
@@ -665,12 +676,14 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
         catch (const isc::Exception& ex) {
             LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
             answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
+            // An error occurred, so make sure to restore the original data.
             rollback = true;
         } catch (...) {
             // For things like bad_cast in boost::lexical_cast
             LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
             answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
                                                " parsing error");
+            // An error occurred, so make sure to restore the original data.
             rollback = true;
         }
     }
@@ -687,6 +700,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
             err << "during update from config backend database: " << ex.what();
             LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(err.str());
             answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
+            // An error occurred, so make sure to restore the original data.
             rollback = true;
         } catch (...) {
             // For things like bad_cast in boost::lexical_cast
@@ -695,6 +709,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
                 << "undefined configuration parsing error";
             LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(err.str());
             answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
+            // An error occurred, so make sure to restore the original data.
             rollback = true;
         }
     }
@@ -716,7 +731,5 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
     return (answer);
 }
 
-int srv_thread_count = -1;
-
 }  // namespace dhcp
 }  // namespace isc
index 7d6d00a4cf58ed31cc63923780b66f43f50a2f4d..73fae45ac16d80d6bae9d8a65389fc0f0a826490 100644 (file)
@@ -60,8 +60,6 @@ configureDhcp4Server(Dhcpv4Srv&,
                      isc::data::ConstElementPtr config_set,
                      bool check_only = false);
 
-extern int srv_thread_count;
-
 }  // namespace dhcp
 }  // namespace isc
 
index 98d3b40fb7e6c8c29190dbd63fb26e0daf289ecb..111a4bdab63843ac8db29723dc6a26d641c4b414 100644 (file)
@@ -149,7 +149,7 @@ main(int argc, char* argv[]) {
                      << "], 0-65535 allowed." << endl;
                 usage();
             } else {
-                srv_thread_count = thread_count;
+                Dhcpv4Srv::srv_thread_count_ = thread_count;
             }
             break;
 
index 7114459825d7a5004efa8100bd26abbb41337e6f..3492c24564a466b4716591490a45a9cd35ddcd5a 100644 (file)
@@ -57,6 +57,7 @@ using namespace isc::hooks;
 using namespace std;
 
 namespace {
+
 const char* PARSER_CONFIGS[] = {
     // CONFIGURATION 0: one subnet with one pool, no user contexts
     "{"
@@ -1483,7 +1484,6 @@ TEST_F(Dhcp4ParserTest, nextServerNegative) {
         "    \"subnet\": \"192.0.2.0/24\" } ],"
         "\"valid-lifetime\": 4000 }";
 
-
     ConstElementPtr json1;
     ASSERT_NO_THROW(json1 = parseDHCP4(config_bogus1));
     ConstElementPtr json2;
@@ -2763,7 +2763,6 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
     EXPECT_EQ(170, def->getCode());
     EXPECT_EQ(OPT_STRING_TYPE, def->getType());
     EXPECT_FALSE(def->getArrayType());
-
 }
 
 // Goal of this test is to verify that global option data is configured
@@ -3487,7 +3486,6 @@ TEST_F(Dhcp4ParserTest, optionDataMultiplePools) {
     testOption(*range2.first, 23, foo2_expected, sizeof(foo2_expected));
 }
 
-
 // Verify that empty option name is rejected in the configuration.
 TEST_F(Dhcp4ParserTest, optionNameEmpty) {
     // Empty option names not allowed.
@@ -3851,7 +3849,6 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
         " } ]"
         "}";
 
-
     ASSERT_NO_THROW(json = parseDHCP4(config));
     extractConfig(config);
 
@@ -4009,7 +4006,6 @@ TEST_F(Dhcp4ParserTest, vendorOptionsCsv) {
     ASSERT_FALSE(desc2.option_);
 }
 
-
 // Tests of the hooks libraries configuration.  All tests have the pre-
 // condition (checked in the test fixture's SetUp() method) that no hooks
 // libraries are loaded at the start of the tests.
@@ -4080,7 +4076,6 @@ buildHooksLibrariesConfig(const char* library1 = NULL,
     return (buildHooksLibrariesConfig(libraries));
 }
 
-
 // The goal of this test is to verify the configuration of hooks libraries if
 // none are specified.
 TEST_F(Dhcp4ParserTest, NoHooksLibraries) {
@@ -4617,7 +4612,6 @@ TEST_F(Dhcp4ParserTest, subnetRelayInfoList) {
     EXPECT_TRUE(subnet->hasRelayAddress(IOAddress("192.0.3.124")));
 }
 
-
 // Goal of this test is to verify that multiple subnets can be configured
 // with defined client classes.
 TEST_F(Dhcp4ParserTest, classifySubnets) {
@@ -4964,7 +4958,6 @@ TEST_F(Dhcp4ParserTest, reservations) {
     EXPECT_FALSE(hosts_cfg->get4(234, Host::IDENT_CIRCUIT_ID,
                                  &circuit_id[0], circuit_id.size()));
 
-
     // Repeat the test for the DUID based reservation in this subnet.
     std::vector<uint8_t> duid_r(duid.rbegin(), duid.rend());
     host = hosts_cfg->get4(542, Host::IDENT_DUID, &duid_r[0], duid_r.size());
@@ -5321,6 +5314,41 @@ TEST_F(Dhcp4ParserTest, hostReservationGlobal) {
     EXPECT_EQ(Network::HR_OUT_OF_POOL, subnet->getHostReservationMode());
 }
 
+/// Check that the multi-threading settings have a default value when not
+/// specified.
+TEST_F(Dhcp4ParserTest, multiThreadingDefaultSettings) {
+    ConstElementPtr status;
+
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"subnet4\": [ ]"
+        "}";
+
+    ConstElementPtr json;
+    ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // returned value should be 0 (success)
+    checkResult(status, 0);
+
+    // The value of enable-multi-threading must be equal to the default value
+    // (false). The default value is defined in GLOBAL4_DEFAULTS in
+    // simple_parser4.cc.
+    EXPECT_EQ(false,
+        CfgMgr::instance().getStagingCfg()->getEnableMultiThreading());
+
+    // The value of packet-thread-pool-size must be equal to the default value
+    // (0). The default value is defined in GLOBAL4_DEFAULTS in
+    // simple_parser4.cc.
+    EXPECT_EQ(0, CfgMgr::instance().getStagingCfg()->getPktThreadPoolSize());
+
+    // The value of packet-thread-queue-size must be equal to the default value
+    // (4). The default value is defined in GLOBAL4_DEFAULTS in
+    // simple_parser4.cc.
+    EXPECT_EQ(4, CfgMgr::instance().getStagingCfg()->getPktThreadQueueSize());
+}
+
 /// Check that the decline-probation-period has a default value when not
 /// specified.
 TEST_F(Dhcp4ParserTest, declineTimerDefault) {
@@ -5340,8 +5368,8 @@ TEST_F(Dhcp4ParserTest, declineTimerDefault) {
     checkResult(status, 0);
 
     // The value of decline-probation-period must be equal to the
-    // default value (86400). The default value is defined in GLOBAL6_DEFAULTS in
-    // simple_parser6.cc.
+    // default value (86400). The default value is defined in GLOBAL4_DEFAULTS in
+    // simple_parser4.cc.
     EXPECT_EQ(86400, CfgMgr::instance().getStagingCfg()->getDeclinePeriod());
 }
 
@@ -5368,7 +5396,6 @@ TEST_F(Dhcp4ParserTest, dhcp4o6portDefault) {
     EXPECT_EQ(0, CfgMgr::instance().getStagingCfg()->getDhcp4o6Port());
 }
 
-
 /// Check that the decline-probation-period value can be set properly.
 TEST_F(Dhcp4ParserTest, declineTimer) {
     ConstElementPtr status;
@@ -5489,7 +5516,6 @@ TEST_F(Dhcp4ParserTest, expiredLeasesProcessingError) {
     EXPECT_TRUE(errorContainsPosition(status, "<string>"));
 }
 
-
 // Checks if the DHCPv4 is able to parse the configuration without 4o6 parameters
 // and does not set 4o6 fields at all.
 TEST_F(Dhcp4ParserTest, 4o6default) {
@@ -5620,7 +5646,6 @@ TEST_F(Dhcp4ParserTest, 4o6subnetBogus) {
     checkResult(status, 1);
 }
 
-
 // Checks if the DHCPv4 is able to parse the configuration with 4o6 network
 // interface defined.
 TEST_F(Dhcp4ParserTest, 4o6iface) {
@@ -5763,7 +5788,6 @@ TEST_F(Dhcp4ParserTest, validClientClassDictionary) {
         " } ] \n"
         "} \n";
 
-
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     extractConfig(config);
@@ -6217,7 +6241,6 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) {
         "        \"pools\": [ { \"pool\": \"192.0.3.1-192.0.3.10\" } ]\n"
         "    }\n"
         "    ]\n"
-
         " } ]\n"
         "} \n";
 
@@ -6900,8 +6923,10 @@ TEST_F(Dhcp4ParserTest, dhcpQueueControl) {
 
             // Fetch the queue control info.
             staged_control = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
+
             // Make sure the staged queue config exists.
             ASSERT_TRUE(staged_control);
+
             // Now build the expected queue control content.
             if (scenario.json_.empty()) {
                 exp_control = Element::createMap();
@@ -7169,5 +7194,35 @@ TEST_F(Dhcp4ParserTest, statsDefaultLimits) {
     EXPECT_EQ("00:00:05",
               util::durationToText(stats_mgr.getMaxSampleAgeDefault(), 0));
 }
+    
+/// Check that the multi threading settings can be set properly.
+TEST_F(Dhcp4ParserTest, multiThreadingSettings) {
+    ConstElementPtr status;
+
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"enable-multi-threading\": true,"
+        "\"packet-thread-pool-size\": 256,"
+        "\"packet-thread-queue-size\": 256,"
+        "\"subnet4\": [ ]"
+        "}";
+
+    ConstElementPtr json;
+    ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // returned value should be 0 (success)
+    checkResult(status, 0);
+
+    // The value of multi-threading settings must be equal to the specified
+    // values
+    EXPECT_EQ(true,
+              CfgMgr::instance().getStagingCfg()->getEnableMultiThreading());
+    EXPECT_EQ(256,
+              CfgMgr::instance().getStagingCfg()->getPktThreadPoolSize());
+    EXPECT_EQ(256,
+              CfgMgr::instance().getStagingCfg()->getPktThreadQueueSize());
+}
 
 }
index c54256b15a09feb824f48e9fb1dc89e42cdbd605..0aed8829028188db344052ab4f7ae7c88e68cf8d 100644 (file)
@@ -140,17 +140,20 @@ ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) {
                       "processCommand(\"config-set\", json)");
         }
 
+        // command line parameters overwrite file and database configuration
         bool enabled = false;
-        if (srv_thread_count >= 0) {
+        if (Dhcpv6Srv::srv_thread_count_ >= 0) {
             enabled = true;
         }
         if (enabled) {
-            CfgMgr::instance().getCurrentCfg()->setServerThreadCount(srv_thread_count);
-            CfgMgr::instance().getCurrentCfg()->setServerMaxThreadQueueSize(4);
+            CfgMgr::instance().getCurrentCfg()->setPktThreadPoolSize(Dhcpv6Srv::srv_thread_count_);
+            CfgMgr::instance().getCurrentCfg()->setPktThreadQueueSize(0);
             LOG_FATAL(dhcp6_logger, DHCP6_MULTI_THREADING_WARNING);
+        } else {
+            enabled = CfgMgr::instance().getCurrentCfg()->getEnableMultiThreading();
         }
         MultiThreadingMgr::instance().apply(enabled,
-            CfgMgr::instance().getCurrentCfg()->getServerThreadCount());
+            CfgMgr::instance().getCurrentCfg()->getPktThreadPoolSize());
 
         // Now check is the returned result is successful (rcode=0) or not
         // (see @ref isc::config::parseAnswer).
@@ -175,7 +178,7 @@ ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) {
     LOG_WARN(dhcp6_logger, DHCP6_MULTI_THREADING_INFO)
         .arg(MultiThreadingMgr::instance().getMode() ? "yes" : "no")
         .arg(MultiThreadingMgr::instance().getPktThreadPoolSize())
-        .arg(CfgMgr::instance().getCurrentCfg()->getServerMaxThreadQueueSize());
+        .arg(CfgMgr::instance().getCurrentCfg()->getPktThreadQueueSize());
 
     return (result);
 }
index c2425e5e82e3534dee9a4c910041dda6a9b38f0e..a084532801e25cfa754cbec1e8f33b3e2364a517 100644 (file)
@@ -1647,7 +1647,6 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
     }
 }
 
-
 \"parameters\" {
     switch(driver.ctx_) {
     case isc::dhcp::Parser6Context::HOOKS_LIBRARIES:
@@ -1810,6 +1809,33 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
     }
 }
 
+\"enable-multi-threading\" {
+    switch(driver.ctx_) {
+    case isc::dhcp::Parser6Context::DHCP6:
+        return isc::dhcp::Dhcp6Parser::make_ENABLE_MULTI_THREADING(driver.loc_);
+    default:
+        return isc::dhcp::Dhcp6Parser::make_STRING("enable-multi-threading", driver.loc_);
+    }
+}
+
+\"packet-thread-pool-size\" {
+    switch(driver.ctx_) {
+    case isc::dhcp::Parser6Context::DHCP6:
+        return isc::dhcp::Dhcp6Parser::make_PACKET_THREAD_POOL_SIZE(driver.loc_);
+    default:
+        return isc::dhcp::Dhcp6Parser::make_STRING("packet-thread-pool-size", driver.loc_);
+    }
+}
+
+\"packet-thread-queue-size\" {
+    switch(driver.ctx_) {
+    case isc::dhcp::Parser6Context::DHCP6:
+        return isc::dhcp::Dhcp6Parser::make_PACKET_THREAD_QUEUE_SIZE(driver.loc_);
+    default:
+        return isc::dhcp::Dhcp6Parser::make_STRING("packet-thread-queue-size", driver.loc_);
+    }
+}
+
 \"control-socket\" {
     switch(driver.ctx_) {
     case isc::dhcp::Parser6Context::DHCP6:
@@ -1909,7 +1935,6 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
     }
 }
 
-
 {JSONString} {
     /* A string has been matched. It contains the actual string and single quotes.
        We need to get those quotes out of the way and just use its content, e.g.
index 3cd1780f13e46296aa8c83abad43b5ed6a93001f..723ecff2357553211456154916c70846815bf570 100644 (file)
@@ -50,6 +50,7 @@ using namespace std;
   NULL_TYPE "null"
 
   DHCP6 "Dhcp6"
+
   DATA_DIRECTORY "data-directory"
   CONFIG_CONTROL "config-control"
   CONFIG_DATABASES "config-databases"
@@ -191,6 +192,10 @@ using namespace std;
 
   DHCP4O6_PORT "dhcp4o6-port"
 
+  ENABLE_MULTI_THREADING "enable-multi-threading"
+  PACKET_THREAD_POOL_SIZE "packet-thread-pool-size"
+  PACKET_THREAD_QUEUE_SIZE "packet-thread-queue-size"
+
   CONTROL_SOCKET "control-socket"
   SOCKET_TYPE "socket-type"
   SOCKET_NAME "socket-name"
@@ -417,7 +422,8 @@ syntax_map: LCURLY_BRACKET {
     ctx.require("Dhcp6", ctx.loc2pos(@1), ctx.loc2pos(@4));
 };
 
-// This represents top-level entries: Dhcp6, Dhcp4, DhcpDdns, Logging
+// This represents top-level entries: Control-agent, Dhcp6, Dhcp4,
+// DhcpDdns, Logging
 global_objects: global_object
               | global_objects COMMA global_object
               ;
@@ -513,6 +519,9 @@ global_param: data_directory
             | statistic_default_sample_count
             | statistic_default_sample_age
             | unknown_map_entry
+            | enable_multi_threading
+            | packet_thread_pool_size
+            | packet_thread_queue_size
             ;
 
 data_directory: DATA_DIRECTORY {
@@ -725,7 +734,6 @@ re_detect: RE_DETECT COLON BOOLEAN {
     ctx.stack_.back()->set("re-detect", b);
 };
 
-
 lease_database: LEASE_DATABASE {
     ElementPtr i(new MapElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("lease-database", i);
@@ -1039,6 +1047,21 @@ relay_supplied_options: RELAY_SUPPLIED_OPTIONS {
     ctx.leave();
 };
 
+enable_multi_threading: ENABLE_MULTI_THREADING COLON BOOLEAN {
+    ElementPtr b(new BoolElement($3, ctx.loc2pos(@3)));
+    ctx.stack_.back()->set("enable-multi-threading", b);
+};
+
+packet_thread_pool_size: PACKET_THREAD_POOL_SIZE COLON INTEGER {
+    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
+    ctx.stack_.back()->set("packet-thread-pool-size", prf);
+};
+
+packet_thread_queue_size: PACKET_THREAD_QUEUE_SIZE COLON INTEGER {
+    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
+    ctx.stack_.back()->set("packet-thread-queue-size", prf);
+};
+
 hooks_libraries: HOOKS_LIBRARIES {
     ElementPtr l(new ListElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("hooks-libraries", l);
@@ -1328,7 +1351,6 @@ rapid_commit: RAPID_COMMIT COLON BOOLEAN {
     ctx.stack_.back()->set("rapid-commit", rc);
 };
 
-
 // ---- shared-networks ---------------------
 
 shared_networks: SHARED_NETWORKS {
@@ -1670,6 +1692,7 @@ sub_pool6: LCURLY_BRACKET {
 } pool_params RCURLY_BRACKET {
     // The pool parameter is required.
     ctx.require("pool", ctx.loc2pos(@1), ctx.loc2pos(@4));
+    // parsing completed
 };
 
 pool_params: pool_param
@@ -2158,6 +2181,7 @@ socket_name: SOCKET_NAME {
     ctx.leave();
 };
 
+
 // --- dhcp-queue-control ---------------------------------------------
 
 dhcp_queue_control: DHCP_QUEUE_CONTROL {
@@ -2368,6 +2392,7 @@ dep_hostname_char_replacement: HOSTNAME_CHAR_REPLACEMENT {
     ctx.leave();
 };
 
+
 // JSON entries for Dhcp4 and DhcpDdns
 
 dhcp4_json_object: DHCP4 {
index abcfe4b25c8e470fe5e0b14d72c3db376d520a5a..6d0be0cfbed17551b2e2496b7d8c236f597fc871 100644 (file)
@@ -204,6 +204,8 @@ namespace dhcp {
 
 const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
 
+int Dhcpv6Srv::srv_thread_count_ = -1;
+
 Dhcpv6Srv::Dhcpv6Srv(uint16_t server_port, uint16_t client_port)
     : io_service_(new IOService()), server_port_(server_port),
       client_port_(client_port), serverid_(), shutdown_(true),
@@ -523,12 +525,13 @@ void Dhcpv6Srv::run_one() {
     try {
         bool read_pkt = true;
 
-        // Do not read more packets from socket if there are enough
-        // packets to be processed in the packet thread pool queue
-        const int max_queue_size = CfgMgr::instance().getCurrentCfg()->getServerMaxThreadQueueSize();
+        // Do not read more packets from socket if there are enough packets to
+        // be processed in the packet thread pool queue
+        // max_queue_size = 0 means no limit
+        const int max_queue_size = CfgMgr::instance().getCurrentCfg()->getPktThreadQueueSize();
         const int thread_count = MultiThreadingMgr::instance().getPktThreadPoolSize();
         size_t pkt_queue_size = MultiThreadingMgr::instance().getPktThreadPool().count();
-        if (thread_count && (pkt_queue_size >= thread_count * max_queue_size)) {
+        if (thread_count && max_queue_size && (pkt_queue_size >= thread_count * max_queue_size)) {
             read_pkt = false;
         }
 
index 80b8d2a141dfd45db6772bdf14871a715d4deec1..a9571d2f5c5de7e058450df7d58837c820fe340f 100644 (file)
@@ -1061,6 +1061,12 @@ protected:
     uint16_t client_port_;
 
 public:
+    /// @brief command line parameter thread count
+    /// when parameter is not specified, the default value is used
+    /// the default value is: -1 means disabled (single-threaded),
+    /// 0 means auto-detect, other values set thread count explicitly
+    static int srv_thread_count_;
+
     /// @note used by DHCPv4-over-DHCPv6 so must be public and static
 
     /// @brief Updates statistics for transmitted packets
index 7ee22b648af50ce990ecb7ab5f7be388aaf6f9f5..fbe5a10c01c690502ed8d84a587dee85b88a398d 100644 (file)
 #include <cc/command_interpreter.h>
 #include <config/command_mgr.h>
 #include <database/dbaccess_parser.h>
+#include <dhcp/iface_mgr.h>
 #include <dhcp/libdhcp++.h>
-#include <dhcp6/json_config_parser.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/dhcp6_srv.h>
-#include <dhcp/iface_mgr.h>
+#include <dhcp6/json_config_parser.h>
 #include <dhcpsrv/cb_ctl_dhcp4.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <hooks/hooks_parser.h>
 #include <log/logger_support.h>
 #include <process/config_ctl_parser.h>
+
 #include <util/encode/hex.h>
 #include <util/strutil.h>
 
 #include <boost/algorithm/string.hpp>
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/shared_ptr.hpp>
 
 #include <iostream>
 #include <limits>
 #include <netinet/in.h>
 #include <vector>
 
-#include <stdint.h>
-
 using namespace std;
 using namespace isc;
 using namespace isc::data;
 using namespace isc::dhcp;
 using namespace isc::asiolink;
 using namespace isc::hooks;
+using namespace isc::process;
+using namespace isc::config;
+using namespace isc::db;
 
 namespace {
 
@@ -188,6 +188,18 @@ public:
         uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port");
         srv_config->setDhcp4o6Port(dhcp4o6_port);
 
+        // Set enable multi threading flag.
+        bool enable_multi_threading = getBoolean(global, "enable-multi-threading");
+        srv_config->setEnableMultiThreading(enable_multi_threading);
+
+        // Set packet thread pool size.
+        uint32_t packet_thread_pool_size = getUint32(global, "packet-thread-pool-size");
+        srv_config->setPktThreadPoolSize(packet_thread_pool_size);
+
+        // Set packet thread queue size.
+        uint32_t packet_thread_queue_size = getUint32(global, "packet-thread-queue-size");
+        srv_config->setPktThreadQueueSize(packet_thread_queue_size);
+
         // Set the global user context.
         ConstElementPtr user_context = global->get("user-context");
         if (user_context) {
@@ -311,7 +323,6 @@ public:
                         }
                     }
 
-
                     if (iface.empty()) {
                         iface = (*subnet)->getIface();
                         continue;
@@ -349,8 +360,6 @@ public:
 
         }
     }
-
-
 };
 
 } // anonymous namespace
@@ -627,12 +636,11 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
             }
 
             if (config_pair.first == "shared-networks") {
-                /// We need to create instance of SharedNetworks4ListParser
+                /// We need to create instance of SharedNetworks6ListParser
                 /// and parse the list of the shared networks into the
-                /// CfgSharedNetworks4 object. One additional step is then to
+                /// CfgSharedNetworks6 object. One additional step is then to
                 /// add subnets from the CfgSharedNetworks6 into CfgSubnets6
                 /// as well.
-
                 SharedNetworks6ListParser parser;
                 CfgSharedNetworks6Ptr cfg = srv_config->getCfgSharedNetworks6();
                 parser.parse(cfg, config_pair.second);
@@ -697,7 +705,10 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
                  (config_pair.first == "ddns-qualifying-suffix") ||
                  (config_pair.first == "store-extended-info") ||
                  (config_pair.first == "statistic-default-sample-count") ||
-                 (config_pair.first == "statistic-default-sample-age")) {
+                 (config_pair.first == "statistic-default-sample-age") ||
+                 (config_pair.first == "enable-multi-threading") ||
+                 (config_pair.first == "packet-thread-pool-size") ||
+                 (config_pair.first == "packet-thread-queue-size")) {
                 CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
                                                                         config_pair.second);
                 continue;
@@ -728,7 +739,7 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
         // defined as part of shared networks.
         global_parser.sanityChecks(srv_config, mutable_cfg);
 
-        // Validate D2 client confuguration.
+        // Validate D2 client configuration.
         if (!d2_client_cfg) {
             d2_client_cfg.reset(new D2ClientConfig());
         }
@@ -844,7 +855,5 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
     return (answer);
 }
 
-int srv_thread_count = -1;
-
 }  // namespace dhcp
 }  // namespace isc
index 981ed8b9b820efc169641a3d43a92bc511d057b2..8230d3550e7a4cc9c4880c1a9a92ab7a9ced6b44 100644 (file)
@@ -44,8 +44,6 @@ isc::data::ConstElementPtr
 configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
                      bool check_only = false);
 
-extern int srv_thread_count;
-
 }  // namespace dhcp
 }  // namespace isc
 
index 2f8b5940a47331784dd7ccaa70a92edddd0c0220..e6f8cdb6c8eb69cd2f8f30dbe9f94fe24b607e6a 100644 (file)
@@ -149,7 +149,7 @@ main(int argc, char* argv[]) {
                      << "], 0-65535 allowed." << endl;
                 usage();
             } else {
-                srv_thread_count = thread_count;
+                Dhcpv6Srv::srv_thread_count_ = thread_count;
             }
             break;
 
index a3cd8b5ca23fb0e94fac67197d678c738115849b..1a3421e0727f1a5d132f05d55a01aff820691d0d 100644 (file)
@@ -510,8 +510,8 @@ public:
     /// @brief Create the simple configuration with single option.
     ///
     /// This function allows to set one of the parameters that configure
-    /// option value. These parameters are: "name", "code", "data" and
-    /// "csv-format".
+    /// option value. These parameters are: "name", "code", "data",
+    /// "csv-format" and "space".
     ///
     /// @param param_value string holding option parameter value to be
     /// injected into the configuration string.
@@ -784,7 +784,6 @@ public:
         return (ReturnType());
     }
 
-
     /// @brief Test invalid option parameter value.
     ///
     /// This test function constructs the simple configuration
@@ -1203,7 +1202,6 @@ TEST_F(Dhcp6ParserTest, emptySubnet) {
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
 
-
     // returned value should be 0 (success)
     checkResult(status, 0);
 }
@@ -1311,7 +1309,7 @@ TEST_F(Dhcp6ParserTest, multipleSubnets) {
     } while (++cnt < 10);
 }
 
-// This checks that it is possible to assign arbitrary ids for subnets.
+// This test checks that it is possible to assign arbitrary ids for subnets.
 TEST_F(Dhcp6ParserTest, multipleSubnetsExplicitIDs) {
     ConstElementPtr x;
     // Four subnets with arbitrary subnet ids.
@@ -1407,7 +1405,6 @@ TEST_F(Dhcp6ParserTest, multipleSubnetsOverlappingIDs) {
     EXPECT_TRUE(errorContainsPosition(x, "<string>"));
 }
 
-
 // Goal of this test is to verify that a previously configured subnet can be
 // deleted in subsequent reconfiguration.
 TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
@@ -1541,8 +1538,6 @@ TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
     EXPECT_EQ(4, subnets->at(2)->getID());
 }
 
-
-
 // This test checks if it is possible to override global values
 // on a per subnet basis.
 TEST_F(Dhcp6ParserTest, subnetLocal) {
@@ -1658,7 +1653,6 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceBogus) {
     EXPECT_FALSE(subnet);
 }
 
-
 // This test checks if it is not allowed to define global interface
 // parameter.
 TEST_F(Dhcp6ParserTest, interfaceGlobal) {
@@ -1686,7 +1680,6 @@ TEST_F(Dhcp6ParserTest, interfaceGlobal) {
     EXPECT_THROW(parseDHCP6(config), Dhcp6ParseError);
 }
 
-
 // This test checks if it is possible to define a subnet with an
 // interface-id option defined.
 TEST_F(Dhcp6ParserTest, subnetInterfaceId) {
@@ -1734,7 +1727,6 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceId) {
     EXPECT_TRUE(selector.interface_id_->equals(subnet->getInterfaceId()));
 }
 
-
 // This test checks if it is not allowed to define global interface
 // parameter.
 TEST_F(Dhcp6ParserTest, interfaceIdGlobal) {
@@ -1958,7 +1950,6 @@ TEST_F(Dhcp6ParserTest, multiplePools) {
     EXPECT_TRUE(subnets->at(0)->getPools(Lease::TYPE_PD).empty());
 }
 
-
 // Test verifies that a subnet with pool values that do not belong to that
 // pool are rejected.
 TEST_F(Dhcp6ParserTest, poolOutOfSubnet) {
@@ -1972,7 +1963,6 @@ TEST_F(Dhcp6ParserTest, poolOutOfSubnet) {
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
         "\"valid-lifetime\": 4000 }";
 
-
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
 
@@ -2409,7 +2399,6 @@ TEST_F(Dhcp6ParserTest, subnetAndPrefixDelegated) {
     EXPECT_EQ(lastAddrInPrefix(prefixAddress, 64), p6->getLastAddress());
 }
 
-
 // Goal of this test is check for proper handling of invalid prefix delegation
 // pool configuration.  It uses an array of invalid configurations to check
 // a variety of configuration errors.
@@ -2903,7 +2892,6 @@ TEST_F(Dhcp6ParserTest, optionIntegerTypes) {
     checkResult(status, 0);
 }
 
-
 /// The goal of this test is to verify that the invalid encapsulated
 /// option space name is not accepted.
 TEST_F(Dhcp6ParserTest, optionDefInvalidEncapsulatedSpace) {
@@ -3640,7 +3628,6 @@ TEST_F(Dhcp6ParserTest, optionDataMultiplePools) {
     testOption(*range3.first, D6O_SUBSCRIBER_ID, subscriber_id_expected2,
                sizeof(subscriber_id_expected2));
 
-
     pool = subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1::300"));
     ASSERT_TRUE(pool);
     pool6 = boost::dynamic_pointer_cast<Pool6>(pool);
@@ -3957,7 +3944,6 @@ TEST_F(Dhcp6ParserTest, rdnssOption) {
     EXPECT_EQ("example.com.", optionCustom->readFqdn(4));
 }
 
-
 // This test checks if vendor options can be specified in the config file
 // (in hex format), and later retrieved from configured subnet
 TEST_F(Dhcp6ParserTest, vendorOptionsHex) {
@@ -4224,7 +4210,6 @@ TEST_F(Dhcp6ParserTest, DISABLED_stdOptionDataEncapsulate) {
 // condition (checked in the test fixture's SetUp() method) that no hooks
 // libraries are loaded at the start of the tests.
 
-
 // Helper function to return a configuration containing an arbitrary number
 // of hooks libraries.
 std::string
@@ -4294,7 +4279,6 @@ buildHooksLibrariesConfig(const char* library1 = NULL,
     return (buildHooksLibrariesConfig(libraries));
 }
 
-
 // The goal of this test is to verify the configuration of hooks libraries if
 // none are specified.
 TEST_F(Dhcp6ParserTest, NoHooksLibraries) {
@@ -4364,7 +4348,6 @@ TEST_F(Dhcp6ParserTest, LibrariesSpecified) {
 
 }
 
-
 // This test verifies that it is possible to select subset of interfaces on
 // which server should listen.
 TEST_F(Dhcp6ParserTest, selectedInterfaces) {
@@ -4382,7 +4365,6 @@ TEST_F(Dhcp6ParserTest, selectedInterfaces) {
         "\"renew-timer\": 1000, "
         "\"valid-lifetime\": 4000 }";
 
-
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     extractConfig(config);
@@ -4421,7 +4403,6 @@ TEST_F(Dhcp6ParserTest, allInterfaces) {
         "\"renew-timer\": 1000, "
         "\"valid-lifetime\": 4000 }";
 
-
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     extractConfig(config);
@@ -4437,7 +4418,6 @@ TEST_F(Dhcp6ParserTest, allInterfaces) {
     EXPECT_TRUE(test_config.socketOpen("eth1", AF_INET6));
 }
 
-
 // This test checks if it is possible to specify relay information
 TEST_F(Dhcp6ParserTest, subnetRelayInfo) {
 
@@ -5233,7 +5213,6 @@ TEST_F(Dhcp6ParserTest, reservations) {
     ASSERT_TRUE(opt_prf);
     EXPECT_EQ(11, static_cast<int>(opt_prf->getValue()));
 
-
     // The HW address used for one of the reservations in the subnet 542
     // consists of numbers from 6 to 1. So, let's just reverse the order
     // of the address from the previous test.
@@ -5532,7 +5511,6 @@ TEST_F(Dhcp6ParserTest, macSources2) {
     EXPECT_EQ(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID, sources[2]);
 }
 
-
 /// The goal of this test is to verify that empty MAC sources configuration
 /// is rejected. If specified, this has to have at least one value.
 TEST_F(Dhcp6ParserTest, macSourcesEmpty) {
@@ -5911,6 +5889,41 @@ TEST_F(Dhcp6ParserTest, testDataDir) {
     EXPECT_NE(original_datadir, string(CfgMgr::instance().getDataDir()));
 }
 
+/// Check that the multi-threading settings have a default value when not
+/// specified.
+TEST_F(Dhcp6ParserTest, multiThreadingDefaultSettings) {
+    ConstElementPtr status;
+
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"subnet6\": [ ]"
+        "}";
+
+    ConstElementPtr json;
+    ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
+
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+
+    // returned value should be 0 (success)
+    checkResult(status, 0);
+
+    // The value of enable-multi-threading must be equal to the default value
+    // (false). The default value is defined in GLOBAL6_DEFAULTS in
+    // simple_parser6.cc.
+    EXPECT_EQ(false,
+        CfgMgr::instance().getStagingCfg()->getEnableMultiThreading());
+
+    // The value of packet-thread-pool-size must be equal to the default value
+    // (0). The default value is defined in GLOBAL6_DEFAULTS in
+    // simple_parser6.cc.
+    EXPECT_EQ(0, CfgMgr::instance().getStagingCfg()->getPktThreadPoolSize());
+
+    // The value of packet-thread-queue-size must be equal to the default value
+    // (4). The default value is defined in GLOBAL6_DEFAULTS in
+    // simple_parser6.cc.
+    EXPECT_EQ(4, CfgMgr::instance().getStagingCfg()->getPktThreadQueueSize());
+}
+
 /// Check that the decline-probation-period value has a default value if not
 /// specified explicitly.
 TEST_F(Dhcp6ParserTest, declineTimerDefault) {
@@ -6077,7 +6090,6 @@ TEST_F(Dhcp6ParserTest, expiredLeasesProcessingError) {
 // Verifies that simple list of valid classes parses and
 // is staged for commit.
 TEST_F(Dhcp6ParserTest, validClientClassDictionary) {
-
     string config = "{ " + genIfaceConfig() + ","
         "\"preferred-lifetime\": 3000, \n"
         "\"rebind-timer\": 2000,  \n"
@@ -6826,7 +6838,6 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDeriveInterfaces) {
     EXPECT_EQ("", s->getIface().get());
 }
 
-
 // It is not allowed to have different values for interfaces names is subnets
 // in the same shared network.
 TEST_F(Dhcp6ParserTest, sharedNetworksInterfacesMixed) {
@@ -6894,7 +6905,7 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDeriveClientClass) {
     CfgSharedNetworks6Ptr cfg_net = CfgMgr::instance().getStagingCfg()
         ->getCfgSharedNetworks6();
 
-    // Two shared networks are expeced.
+    // Two shared networks are expected.
     ASSERT_TRUE(cfg_net);
     const SharedNetwork6Collection* nets = cfg_net->getAll();
     ASSERT_TRUE(nets);
@@ -7118,7 +7129,7 @@ TEST_F(Dhcp6ParserTest, comments) {
     ASSERT_TRUE(ctx_opt_desc->get("comment"));
     EXPECT_EQ("\"Set option value\"", ctx_opt_desc->get("comment")->str());
 
-    // And there there are some client classes.
+    // And there are some client classes.
     const ClientClassDictionaryPtr& dict =
         CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
     ASSERT_TRUE(dict);
@@ -7428,6 +7439,7 @@ TEST_F(Dhcp6ParserTest, configControlInfo) {
                 registerBackendType(ConfigBackendDHCPv6Mgr::instance(),
                                     "mysql"));
 
+    // Should parse ok, now that the factory has been registered.
     configure(config, CONTROL_RESULT_SUCCESS, "");
 
     // Make sure the config control info is there.
@@ -7454,7 +7466,6 @@ TEST_F(Dhcp6ParserTest, configControlInfo) {
 
 // Check whether it is possible to configure server-tag
 TEST_F(Dhcp6ParserTest, serverTag) {
-
     // Config without server-tag
     string config_no_tag = "{ " + genIfaceConfig() + "," +
         "\"subnet6\": [  ] "
@@ -7617,7 +7628,7 @@ TEST_F(Dhcp6ParserTest, dhcpQueueControlInvalid) {
         "expecting boolean"
         },
         {
-        "queue type not a string",
+        "queue enabled, type not a string",
         "{ \n"
         "   \"enable-queue\": true, \n"
         "   \"queue-type\": 7777 \n"
@@ -7752,5 +7763,34 @@ TEST_F(Dhcp6ParserTest, statsDefaultLimits) {
               util::durationToText(stats_mgr.getMaxSampleAgeDefault(), 0));
 }
 
+/// Check that the multi threading settings can be set properly.
+TEST_F(Dhcp6ParserTest, multiThreadingSettings) {
+    ConstElementPtr status;
+
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"enable-multi-threading\": true,"
+        "\"packet-thread-pool-size\": 256,"
+        "\"packet-thread-queue-size\": 256,"
+        "\"subnet6\": [ ]"
+        "}";
+
+    ConstElementPtr json;
+    ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
+
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+
+    // returned value should be 0 (success)
+    checkResult(status, 0);
+
+    // The value of multi-threading settings must be equal to the specified
+    // values
+    EXPECT_EQ(true,
+              CfgMgr::instance().getStagingCfg()->getEnableMultiThreading());
+    EXPECT_EQ(256,
+              CfgMgr::instance().getStagingCfg()->getPktThreadPoolSize());
+    EXPECT_EQ(256,
+              CfgMgr::instance().getStagingCfg()->getPktThreadQueueSize());
+}
 
 }
index 0b38c7c0fb15258ed71395d3b224d7d9fee7a825..b86087e607322f1d2d80cab152a6af8b3928e53d 100644 (file)
@@ -85,7 +85,10 @@ const SimpleKeywords SimpleParser4::GLOBAL4_PARAMETERS = {
     { "ddns-qualifying-suffix",         Element::string },
     { "store-extended-info",            Element::boolean },
     { "statistic-default-sample-count", Element::integer },
-    { "statistic-default-sample-age",   Element::integer }
+    { "statistic-default-sample-age",   Element::integer },
+    { "enable-multi-threading",       Element::boolean },
+    { "packet-thread-pool-size",      Element::integer },
+    { "packet-thread-queue-size",     Element::integer }
 };
 
 /// @brief This table defines default global values for DHCPv4
@@ -118,7 +121,10 @@ const SimpleDefaults SimpleParser4::GLOBAL4_DEFAULTS = {
     { "hostname-char-replacement",      Element::string,  "" },
     { "store-extended-info",            Element::boolean, "false" },
     { "statistic-default-sample-count", Element::integer, "20" },
-    { "statistic-default-sample-age",   Element::integer, "0" }
+    { "statistic-default-sample-age",   Element::integer, "0" },
+    { "enable-multi-threading",         Element::boolean, "false" },
+    { "packet-thread-pool-size",        Element::integer, "0" },
+    { "packet-thread-queue-size",       Element::integer, "4" }
 };
 
 /// @brief This table defines all option definition parameters.
@@ -474,5 +480,5 @@ size_t SimpleParser4::deriveParameters(ElementPtr global) {
     return (cnt);
 }
 
-};
-};
+}  // namespace dhcp
+}  // namespace isc
index 78f19ab9408a1bf9329dfe42d08654fee9109c0e..96375bf4cbd3f35b0e324b962c2e57a3ce46815d 100644 (file)
@@ -85,7 +85,10 @@ const SimpleKeywords SimpleParser6::GLOBAL6_PARAMETERS = {
     { "ddns-qualifying-suffix",         Element::string },
     { "store-extended-info",            Element::boolean },
     { "statistic-default-sample-count", Element::integer },
-    { "statistic-default-sample-age",   Element::integer }
+    { "statistic-default-sample-age",   Element::integer },
+    { "enable-multi-threading",       Element::boolean },
+    { "packet-thread-pool-size",      Element::integer },
+    { "packet-thread-queue-size",     Element::integer }
 };
 
 /// @brief This table defines default global values for DHCPv6
@@ -113,7 +116,10 @@ const SimpleDefaults SimpleParser6::GLOBAL6_DEFAULTS = {
     { "hostname-char-replacement",      Element::string,  "" },
     { "store-extended-info",            Element::boolean, "false" },
     { "statistic-default-sample-count", Element::integer, "20" },
-    { "statistic-default-sample-age",   Element::integer, "0" }
+    { "statistic-default-sample-age",   Element::integer, "0" },
+    { "enable-multi-threading",         Element::boolean, "false" },
+    { "packet-thread-pool-size",        Element::integer, "0" },
+    { "packet-thread-queue-size",       Element::integer, "4" }
 };
 
 /// @brief This table defines all option definition parameters.
@@ -476,5 +482,5 @@ size_t SimpleParser6::deriveParameters(ElementPtr global) {
     return (cnt);
 }
 
-};
-};
+}  // namespace dhcp
+}  // namespace isc
index d5be04a4f155542c40016e540435afd5b36b1900..391fc396e3d053c85f1ac7d62c807a9879c908fb 100644 (file)
@@ -42,8 +42,8 @@ SrvConfig::SrvConfig()
       cfg_host_operations6_(CfgHostOperations::createConfig6()),
       class_dictionary_(new ClientClassDictionary()),
       decline_timer_(0), echo_v4_client_id_(true), dhcp4o6_port_(0),
-      server_threads_(0),
-      server_max_thread_queue_size_(0),
+      enable_multi_threading_(false),
+      pkt_thread_pool_size_(0), pkt_thread_queue_size_(0),
       d2_client_config_(new D2ClientConfig()),
       configured_globals_(Element::createMap()),
       cfg_consist_(new CfgConsistency()) {
@@ -62,8 +62,8 @@ SrvConfig::SrvConfig(const uint32_t sequence)
       cfg_host_operations6_(CfgHostOperations::createConfig6()),
       class_dictionary_(new ClientClassDictionary()),
       decline_timer_(0), echo_v4_client_id_(true), dhcp4o6_port_(0),
-      server_threads_(0),
-      server_max_thread_queue_size_(0),
+      enable_multi_threading_(false),
+      pkt_thread_pool_size_(0), pkt_thread_queue_size_(0),
       d2_client_config_(new D2ClientConfig()),
       configured_globals_(Element::createMap()),
       cfg_consist_(new CfgConsistency()) {
index 3107ffeb2eb4f12be5b248f5fd922b80ec187589..81308f495cfb77b7150c1c2ba514e6939318ec23 100644 (file)
@@ -705,32 +705,46 @@ public:
         return (dhcp4o6_port_);
     }
 
-    /// @brief Sets the server thread count.
+    /// @brief Sets the enable multi threading flag.
     ///
-    /// @param threads value of the server thread count
-    void setServerThreadCount(uint32_t threads) {
-        server_threads_ = threads;
+    /// @param size value of the enable multi threading flag
+    void setEnableMultiThreading(bool enabled) {
+        enable_multi_threading_ = enabled;
     }
 
-    /// @brief Retrieves the server thread count.
+    /// @brief Retrieves the enable multi threading flag.
     ///
-    /// @return value of the server thread count
-    uint32_t getServerThreadCount() const {
-        return (server_threads_);
+    /// @return value of the enable multi threading flag
+    uint32_t getEnableMultiThreading() const {
+        return (enable_multi_threading_);
     }
 
-    /// @brief Sets the server max thread queue size.
+    /// @brief Sets the packet thread pool size.
     ///
-    /// @param size max thread queue size
-    void setServerMaxThreadQueueSize(uint32_t size) {
-        server_max_thread_queue_size_ = size;
+    /// @param size value of the packet thread pool size
+    void setPktThreadPoolSize(uint32_t size) {
+        pkt_thread_pool_size_ = size;
     }
 
-    /// @brief Retrieves the server max thread queue size.
+    /// @brief Retrieves the packet thread pool size.
     ///
-    /// @return value of the max thread queue size
-    uint32_t getServerMaxThreadQueueSize() const {
-        return (server_max_thread_queue_size_);
+    /// @return value of the packet thread pool size
+    uint32_t getPktThreadPoolSize() const {
+        return (pkt_thread_pool_size_);
+    }
+
+    /// @brief Sets the packet thread queue size.
+    ///
+    /// @param size value of the packet thread queue size
+    void setPktThreadQueueSize(uint32_t size) {
+        pkt_thread_queue_size_ = size;
+    }
+
+    /// @brief Retrieves the packet thread queue size.
+    ///
+    /// @return value of the packet thread queue size
+    uint32_t getPktThreadQueueSize() const {
+        return (pkt_thread_queue_size_);
     }
 
     /// @brief Returns pointer to the D2 client configuration
@@ -951,11 +965,14 @@ private:
     /// this socket is bound and connected to this port and port + 1
     uint16_t dhcp4o6_port_;
 
-    /// @brief The server thread count.
-    uint32_t server_threads_;
+    /// @brief The enable multi threading flag.
+    bool enable_multi_threading_;
+
+    /// @brief The packet thread pool size.
+    uint32_t pkt_thread_pool_size_;
 
-    /// @brief The server max thread queue size.
-    uint32_t server_max_thread_queue_size_;
+    /// @brief The packet thread queue size.
+    uint32_t pkt_thread_queue_size_;
 
     /// @brief Stores D2 client configuration
     D2ClientConfigPtr d2_client_config_;