On Linux jsonrpc server now users poll group by default. It can
be disabled by using an undocumented --disable-epoll command line
options.

For ovsdb-server to maintain 1000 idle connections over TCP with the
default 5s probe interval, the CPU load dropped from 48% to 14%.

Signed-off-by: Andy Zhou <az...@ovn.org>
---
 NEWS                   |  5 +++-
 ovsdb/jsonrpc-server.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++----
 ovsdb/jsonrpc-server.h |  2 +-
 ovsdb/ovsdb-server.c   | 43 +++++++++++++++++++++--------
 tests/ovsdb-server.at  | 11 ++++++++
 5 files changed, 117 insertions(+), 19 deletions(-)

diff --git a/NEWS b/NEWS
index 18fca10..e5eeba4 100644
--- a/NEWS
+++ b/NEWS
@@ -17,7 +17,10 @@ Post-v2.5.0
    - ovsdb-server:
      * Remove max number of sessions limit, to enable connection scaling
        testing.
-
+   - ovsdb-server:
+     * On Linux, ovsdb server uses poll group, which uses epoll(7) to
+       improve connection scaling. Add an undocumented --disable-epoll
+       switch for testing.
 
 v2.5.0 - xx xxx xxxx
 ---------------------
diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c
index 0d23b77..d2f16c8 100644
--- a/ovsdb/jsonrpc-server.c
+++ b/ovsdb/jsonrpc-server.c
@@ -28,6 +28,7 @@
 #include "ovsdb-error.h"
 #include "ovsdb-parser.h"
 #include "ovsdb.h"
+#include "poll-group.h"
 #include "poll-loop.h"
 #include "reconnect.h"
 #include "row.h"
@@ -103,8 +104,11 @@ struct ovsdb_jsonrpc_server {
     struct ovsdb_server up;
     unsigned int n_sessions;
     struct shash remotes;      /* Contains "struct ovsdb_jsonrpc_remote *"s. */
+    struct poll_group *poll_group;   /* group poll jsonrpc sessions.  */
 };
 
+static void ovsdb_jsonrpc_server_run_poll_group(struct poll_group *);
+
 /* A configured remote.  This is either a passive stream listener plus a list
  * of the currently connected sessions, or a list of exactly one active
  * session. */
@@ -124,13 +128,24 @@ static void ovsdb_jsonrpc_server_del_remote(struct 
shash_node *);
 /* Creates and returns a new server to provide JSON-RPC access to an OVSDB.
  *
  * The caller must call ovsdb_jsonrpc_server_add_db() for each database to
- * which 'server' should provide access. */
+ * which 'server' should provide access.
+ *
+ * On platforms where epoll() is supported, using epoll() can be more
+ * efficient than using poll(), and it will be enabled by default.
+ * To disable it, set 'disable_epoll to false.
+ */
 struct ovsdb_jsonrpc_server *
-ovsdb_jsonrpc_server_create(void)
+ovsdb_jsonrpc_server_create(bool disable_epoll)
 {
     struct ovsdb_jsonrpc_server *server = xzalloc(sizeof *server);
+    struct poll_group *poll_group = NULL;
+
     ovsdb_server_init(&server->up);
     shash_init(&server->remotes);
+    if (!disable_epoll) {
+        poll_group = poll_group_create("jsonrpc-sessions-pg");
+    }
+    server->poll_group = poll_group;
     return server;
 }
 
@@ -318,6 +333,7 @@ void
 ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr)
 {
     struct shash_node *node;
+    struct poll_group *poll_group = svr->poll_group;
 
     SHASH_FOR_EACH (node, &svr->remotes) {
         struct ovsdb_jsonrpc_remote *remote = node->data;
@@ -329,10 +345,13 @@ ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr)
             error = pstream_accept(remote->listener, &stream);
             if (!error) {
                 struct jsonrpc_session *js;
+                struct ovsdb_jsonrpc_session *ovsdb_js;
                 js = jsonrpc_session_open_unreliably(jsonrpc_open(stream),
                                                      remote->dscp);
-                ovsdb_jsonrpc_session_create(remote, js);
-            } else if (error != EAGAIN) {
+                ovsdb_js = ovsdb_jsonrpc_session_create(remote, js);
+                stream_join(stream, svr->poll_group, ovsdb_js);
+            }
+            else if (error != EAGAIN) {
                 VLOG_WARN_RL(&rl, "%s: accept failed: %s",
                              pstream_get_name(remote->listener),
                              ovs_strerror(error));
@@ -341,11 +360,16 @@ ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr)
 
         ovsdb_jsonrpc_session_run_all(remote);
     }
+
+    if (poll_group) {
+        ovsdb_jsonrpc_server_run_poll_group(poll_group);
+    }
 }
 
 void
 ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *svr)
 {
+    struct poll_group *poll_group = svr->poll_group;
     struct shash_node *node;
 
     SHASH_FOR_EACH (node, &svr->remotes) {
@@ -357,6 +381,7 @@ ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *svr)
 
         ovsdb_jsonrpc_session_wait_all(remote);
     }
+    poll_group_poll_wait(poll_group);
 }
 
 /* Adds some memory usage statistics for 'svr' into 'usage', for use with
@@ -491,6 +516,27 @@ ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote 
*remote)
     struct ovsdb_jsonrpc_session *s, *next;
 
     LIST_FOR_EACH_SAFE (s, next, node, &remote->sessions) {
+        if (poll_block_waken_by_timer() ||
+            !jsonrpc_session_joined_poll_group(s->js)) {
+            int error = ovsdb_jsonrpc_session_run(s);
+            if (error) {
+                ovsdb_jsonrpc_session_close(s);
+            }
+        }
+    }
+}
+
+static void
+ovsdb_jsonrpc_server_run_poll_group(struct poll_group *group)
+{
+    void **sessions;
+    size_t n, i;
+
+    poll_group_get_events(group, &sessions, &n);
+
+    for (i = 0; i < n; i++) {
+        struct ovsdb_jsonrpc_session *s;
+        s = sessions[i];
         int error = ovsdb_jsonrpc_session_run(s);
         if (error) {
             ovsdb_jsonrpc_session_close(s);
@@ -499,9 +545,19 @@ ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote 
*remote)
 }
 
 static void
+ovsdb_jsonrpc_session_poll_group_update(struct ovsdb_jsonrpc_session *s)
+{
+    if (ovsdb_jsonrpc_monitor_needs_flush(s) ||
+        jsonrpc_session_get_backlog(s->js) ||
+        jsonrpc_session_has_pending_input(s->js)) {
+
+        jsonrpc_session_poll_group_update(s->js, true);
+    }
+}
+
+static void
 ovsdb_jsonrpc_session_wait(struct ovsdb_jsonrpc_session *s)
 {
-    jsonrpc_session_wait(s->js);
     if (!jsonrpc_session_get_backlog(s->js)) {
         if (ovsdb_jsonrpc_monitor_needs_flush(s)) {
             poll_immediate_wake();
@@ -517,7 +573,14 @@ ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote 
*remote)
     struct ovsdb_jsonrpc_session *s;
 
     LIST_FOR_EACH (s, node, &remote->sessions) {
-        ovsdb_jsonrpc_session_wait(s);
+        jsonrpc_session_wait(s->js);
+
+        bool update_pg = jsonrpc_session_joined_poll_group(s->js);
+        if (update_pg) {
+            ovsdb_jsonrpc_session_poll_group_update(s);
+        } else {
+            ovsdb_jsonrpc_session_wait(s);
+        }
     }
 }
 
diff --git a/ovsdb/jsonrpc-server.h b/ovsdb/jsonrpc-server.h
index 7a0bd31..76c718f 100644
--- a/ovsdb/jsonrpc-server.h
+++ b/ovsdb/jsonrpc-server.h
@@ -23,7 +23,7 @@ struct ovsdb;
 struct shash;
 struct simap;
 
-struct ovsdb_jsonrpc_server *ovsdb_jsonrpc_server_create(void);
+struct ovsdb_jsonrpc_server *ovsdb_jsonrpc_server_create(bool);
 bool ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *,
                                  struct ovsdb *);
 bool ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *,
diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
index fa662b1..6e21c68 100644
--- a/ovsdb/ovsdb-server.c
+++ b/ovsdb/ovsdb-server.c
@@ -87,6 +87,7 @@ struct server_config {
     struct shash *all_dbs;
     FILE *config_tmpfile;
     struct ovsdb_jsonrpc_server *jsonrpc;
+    bool disable_epoll;
 };
 static unixctl_cb_func ovsdb_server_add_remote;
 static unixctl_cb_func ovsdb_server_remove_remote;
@@ -101,7 +102,7 @@ static void close_db(struct db *db);
 
 static void parse_options(int *argc, char **argvp[],
                           struct sset *remotes, char **unixctl_pathp,
-                          char **run_command);
+                          char **run_command, bool *disable_epoll);
 OVS_NO_RETURN static void usage(void);
 
 static char *reconfigure_remotes(struct ovsdb_jsonrpc_server *,
@@ -115,10 +116,11 @@ static void update_remote_status(const struct 
ovsdb_jsonrpc_server *jsonrpc,
                                  struct shash *all_dbs);
 
 static void save_config__(FILE *config_file, const struct sset *remotes,
-                          const struct sset *db_filenames);
+                          const struct sset *db_filenames,
+                          const bool disable_epoll);
 static void save_config(struct server_config *);
 static void load_config(FILE *config_file, struct sset *remotes,
-                        struct sset *db_filenames);
+                        struct sset *db_filenames, bool *disable_epoll);
 
 static void
 main_loop(struct ovsdb_jsonrpc_server *jsonrpc, struct shash *all_dbs,
@@ -203,6 +205,7 @@ main(int argc, char *argv[])
 {
     char *unixctl_path = NULL;
     char *run_command = NULL;
+    bool disable_epoll = false;  /* Use epoll() by default.  */
     struct unixctl_server *unixctl;
     struct ovsdb_jsonrpc_server *jsonrpc;
     struct sset remotes, db_filenames;
@@ -223,7 +226,8 @@ main(int argc, char *argv[])
     fatal_ignore_sigpipe();
     process_init();
 
-    parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command);
+    parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command,
+                  &disable_epoll);
     daemon_become_new_user(false);
 
     /* Create and initialize 'config_tmpfile' as a temporary file to hold
@@ -247,16 +251,17 @@ main(int argc, char *argv[])
         free(default_db);
     }
 
+    server_config.disable_epoll = disable_epoll;
     server_config.remotes = &remotes;
     server_config.config_tmpfile = config_tmpfile;
 
-    save_config__(config_tmpfile, &remotes, &db_filenames);
+    save_config__(config_tmpfile, &remotes, &db_filenames, disable_epoll);
 
     daemonize_start(false);
 
     /* Load the saved config. */
-    load_config(config_tmpfile, &remotes, &db_filenames);
-    jsonrpc = ovsdb_jsonrpc_server_create();
+    load_config(config_tmpfile, &remotes, &db_filenames, &disable_epoll);
+    jsonrpc = ovsdb_jsonrpc_server_create(disable_epoll);
 
     shash_init(&all_dbs);
     server_config.all_dbs = &all_dbs;
@@ -1270,12 +1275,14 @@ ovsdb_server_list_databases(struct unixctl_conn *conn, 
int argc OVS_UNUSED,
 
 static void
 parse_options(int *argcp, char **argvp[],
-              struct sset *remotes, char **unixctl_pathp, char **run_command)
+              struct sset *remotes, char **unixctl_pathp, char **run_command,
+              bool *disable_epoll)
 {
     enum {
         OPT_REMOTE = UCHAR_MAX + 1,
         OPT_UNIXCTL,
         OPT_RUN,
+        OPT_DISABLE_EPOLL,
         OPT_BOOTSTRAP_CA_CERT,
         OPT_PEER_CA_CERT,
         VLOG_OPTION_ENUMS,
@@ -1284,6 +1291,7 @@ parse_options(int *argcp, char **argvp[],
     static const struct option long_options[] = {
         {"remote",      required_argument, NULL, OPT_REMOTE},
         {"unixctl",     required_argument, NULL, OPT_UNIXCTL},
+        {"disable-epoll", no_argument, NULL, OPT_DISABLE_EPOLL},
 #ifndef _WIN32
         {"run",         required_argument, NULL, OPT_RUN},
 #endif
@@ -1301,6 +1309,7 @@ parse_options(int *argcp, char **argvp[],
     char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
     int argc = *argcp;
     char **argv = *argvp;
+    bool disable_epoll_ = false;
 
     sset_init(remotes);
     for (;;) {
@@ -1324,6 +1333,10 @@ parse_options(int *argcp, char **argvp[],
             *run_command = optarg;
             break;
 
+        case OPT_DISABLE_EPOLL:
+            disable_epoll_ = true;
+            break;
+
         case 'h':
             usage();
 
@@ -1363,6 +1376,8 @@ parse_options(int *argcp, char **argvp[],
             abort();
         }
     }
+
+    *disable_epoll = disable_epoll_;
     free(short_options);
 
     *argcp -= optind;
@@ -1407,7 +1422,7 @@ sset_to_json(const struct sset *sset)
  * 'remotes' and 'db_filenames'. */
 static void
 save_config__(FILE *config_file, const struct sset *remotes,
-              const struct sset *db_filenames)
+              const struct sset *db_filenames, bool disable_epoll)
 {
     struct json *obj;
     char *s;
@@ -1420,6 +1435,7 @@ save_config__(FILE *config_file, const struct sset 
*remotes,
     obj = json_object_create();
     json_object_put(obj, "remotes", sset_to_json(remotes));
     json_object_put(obj, "db_filenames", sset_to_json(db_filenames));
+    json_object_put(obj, "disable_epoll", json_boolean_create(disable_epoll));
     s = json_to_string(obj, 0);
     json_destroy(obj);
 
@@ -1445,7 +1461,8 @@ save_config(struct server_config *config)
         sset_add(&db_filenames, db->filename);
     }
 
-    save_config__(config->config_tmpfile, config->remotes, &db_filenames);
+    save_config__(config->config_tmpfile, config->remotes, &db_filenames,
+                  config->disable_epoll);
 
     sset_destroy(&db_filenames);
 }
@@ -1467,7 +1484,8 @@ sset_from_json(struct sset *sset, const struct json 
*array)
 /* Clears and replaces 'remotes' and 'dbnames' by a configuration read from
  * 'config_file', which must have been previously written by save_config(). */
 static void
-load_config(FILE *config_file, struct sset *remotes, struct sset *db_filenames)
+load_config(FILE *config_file, struct sset *remotes,
+            struct sset *db_filenames, bool *disable_epoll)
 {
     struct json *json;
 
@@ -1483,5 +1501,8 @@ load_config(FILE *config_file, struct sset *remotes, 
struct sset *db_filenames)
     sset_from_json(remotes, shash_find_data(json_object(json), "remotes"));
     sset_from_json(db_filenames,
                    shash_find_data(json_object(json), "db_filenames"));
+    bool b = json_boolean(shash_find_data(json_object(json), "disable_epoll"));
+    *disable_epoll = b;
+
     json_destroy(json);
 }
diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
index c869d6f..9244382 100644
--- a/tests/ovsdb-server.at
+++ b/tests/ovsdb-server.at
@@ -277,6 +277,17 @@ ordinals
 ])
 AT_CLEANUP
 
+AT_SETUP([ovsdb-server with --disable-epoll])
+AT_KEYWORDS([ovsdb server positive])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+# Start ovsdb-server, initially with one db, not using epoll poll group
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool create db1 schema], [0], [ignore], [ignore])
+on_exit 'kill `cat *.pid`'
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --disable-epoll 
--remote=punix:socket db1], [0])
+AT_CHECK([ovsdb-client list-dbs unix:socket] , [0], [ignore], [ignore])
+AT_CLEANUP
+
 AT_SETUP([ovsdb-server/add-db and remove-db with --monitor])
 AT_KEYWORDS([ovsdb server positive])
 AT_SKIP_IF([test "$IS_WIN32" = "yes"])
-- 
1.9.1

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to