diff mbox

[ovs-dev,poll,group,RFC,2/6] lib: Introduce poll group provider class

Message ID 1455855656-112840-3-git-send-email-azhou@ovn.org
State Not Applicable
Headers show

Commit Message

Andy Zhou Feb. 19, 2016, 4:20 a.m. UTC
Poll group is a new poll class that sits between application and
the stream class. Poll group compliments the poll loop facility and the
stream class to make main loop more efficient when dealing with
large number of current connections.

See comments in poll-group-provider.h for more details.

Signed-off-by: Andy Zhou <azhou@ovn.org>
---
 lib/automake.mk           |   3 +
 lib/poll-group-provider.h | 153 ++++++++++++++++++++++++++++++++
 lib/poll-group.c          | 221 ++++++++++++++++++++++++++++++++++++++++++++++
 lib/poll-group.h          |  51 +++++++++++
 4 files changed, 428 insertions(+)
 create mode 100644 lib/poll-group-provider.h
 create mode 100644 lib/poll-group.c
 create mode 100644 lib/poll-group.h
diff mbox

Patch

diff --git a/lib/automake.mk b/lib/automake.mk
index 27a1669..dfda1bf 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -197,6 +197,9 @@  lib_libopenvswitch_la_SOURCES = \
 	lib/perf-counter.c \
 	lib/pktbuf.c \
 	lib/pktbuf.h \
+	lib/poll-group.c \
+	lib/poll-group.h \
+	lib/poll-group-provider.h \
 	lib/poll-loop.c \
 	lib/poll-loop.h \
 	lib/process.c \
diff --git a/lib/poll-group-provider.h b/lib/poll-group-provider.h
new file mode 100644
index 0000000..0875670
--- /dev/null
+++ b/lib/poll-group-provider.h
@@ -0,0 +1,153 @@ 
+/*
+ * Copyright (c) 2016 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef POLL_GROUP_PROVIDER_H
+#define POLL_GROUP_PROVIDER_H 1
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include "util.h"
+
+struct poll_group;
+/*
+ * Poll group: An application aware poll interface
+ *
+ * Poll group implements a higher level interface than those offered by
+ * poll loop and aims to improve efficiency with high number concurrent
+ * connections.
+ *
+ * Poll group sits between the application and the stream layer. Same kind
+ * of application sessions can ask their individual streams to join
+ * a common poll group, which is created by the application.
+ * In addition, the application session is required to provide an
+ * unique pointer that is only associated with the session, called
+ * 'caller_event' when joining a poll group. Each poll group then
+ * in term registers with the main poll loop.
+ *
+ * When poll_block() wakes up, each poll group delivers only a set of
+ * 'caller_event's that correspond to the sessions that are responsible
+ * for poll_block waken up.  Thus, the main loop can avoid interrogate all
+ * sessions which is required when using poll loop directly.
+ *
+ * On Linux, Poll group maps well into the epoll(2) facility. Each
+ * poll group maps directly to an epoll_fd that is created
+ * with epoll_create() system call. epoll implementation can provide
+ * additional performance benefits by waking up the main loop less
+ * frequently.
+ *
+ * Poll group is designed to be closely integrated with the stream class.
+ * An newly created application session can join a poll group at any time
+ * during its life cycle. Internally, a stream only turns on 'poll group'
+ * when a stream is fully connected.  Stream connection set up and tear down
+ * is still managed by the poll loop, but those mode switches are managed
+ * internally with the stream class, transparent to the application.
+ *
+ * An application session can always leave poll group at any time, or
+ * simply never join one. In both cases, the stream will fall back to
+ * use poll loop directly.
+ */
+
+struct poll_group_class {
+    /* Prefix of a poll group name, e.g. "default", "epoll" . */
+    const char *name;
+
+    /* The following APIs are required to be implemented in any
+     * class.
+     */
+
+    /* Create a new poll group. */
+    struct poll_group *(*create)(const char *name);
+
+    /* Shut down a poll group, release memory and other resources. */
+    void (*close)(struct poll_group *group);
+
+    /* 'join' allows a new 'fd' to join a poll group. 'caller_event' are user
+     * provided pointer that is opaque to poll group. Those pointers
+     * are passed back to the user when poll_group_get_events() are
+     * called. */
+    int (*join)(struct poll_group *group, int fd, void *caller_event);
+
+    /* 'updates' allows application to inform whether the 'fd' is
+     * interested to be waken up when 'fd' is ready for transmission.
+     */
+    int (*update)(struct poll_group *group, int fd, bool write,
+                  void *caller_event);
+
+    /* 'leave' is the opposite of 'join'. 'fd' will no longer be watched
+     * by poll group. */
+    int (*leave)(struct poll_group *group, int fd);
+
+    /* 'poll_wait' is poll group's interface to poll loop, so that the
+     * poll group itself can be waken up by poll loop. */
+    void (*poll_wait)(struct poll_group *group);
+
+    /* The following APIs are optional and can be NULL. */
+
+    /* 'get_events', if implemented is used for an implementation to fill
+     * up the poll_group's 'events' buffer, using poll_group_notify().
+     * It will be invoked when poll_group_get_events() is called. */
+    void (*get_events)(struct poll_group *group);
+};
+
+static inline bool
+poll_group_class_is_valid(const struct poll_group_class *class)
+{
+    return (class->name && class->create && class->close
+            && class->join && class->update && class->leave
+            && class->poll_wait);
+}
+
+/* poll_group.
+ *
+ * This structure should be treated as opaque by implementation. */
+struct poll_group {
+    const struct poll_group_class *class;
+    char *name;         /* Name of the poll group, useful for logging. */
+    size_t n_joined;    /* Total number of joined streams.  */
+
+
+    /* Data storage provide for implementing poll_group_get_events().
+     * The size of **events buffer will be managed such that its size can
+     * accommodate at least up to 'n_events' of void pointer array.
+     *
+     * The events are filled by 'poll_group_notify()', in sequential order.
+     * 'n_events' are used to track the number of events added by
+     * poll_group_notify().
+     *
+     * 'poll_group_get_events()' allows caller to retrieve the 'events'
+     * array and 'n_evnets' stored. However, this call in one short; it
+     * resets 'n_events'. The next poll_group_notify() call will start
+     * to add 'caller_event' into an empty array.
+     */
+    void **events;       /* Events buffer array, stores 'call_events'
+                            pointers user provided to poll_group_join() */
+    size_t n_events;     /* Number of events received from
+                            poll_group_notify() so far, but has not been
+                            retrieved by poll_group_get_events() yet.  */
+    size_t n_allocated;  /* Used internally for x2nrealloc().   */
+};
+
+static inline void
+poll_group_assert_class(const struct poll_group *group,
+                        const struct poll_group_class *class)
+{
+    ovs_assert(group->class == class);
+}
+
+void poll_group_init(struct poll_group *, const char *name,
+                     const struct poll_group_class *);
+
+#endif /* poll-group-provider.h */
diff --git a/lib/poll-group.c b/lib/poll-group.c
new file mode 100644
index 0000000..547867d
--- /dev/null
+++ b/lib/poll-group.c
@@ -0,0 +1,221 @@ 
+/*
+ * Copyright (c) 2016 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+#include "dynamic-string.h"
+#include "poll-loop.h"
+#include "util.h"
+#include "openvswitch/vlog.h"
+#include "poll-group.h"
+#include "poll-group-provider.h"
+
+VLOG_DEFINE_THIS_MODULE(poll_group);
+
+static void add_caller_event(struct poll_group *group, void *caller_event);
+
+static const struct poll_group_class *
+poll_group_select_class(const char *class_name OVS_UNUSED, size_t len OVS_UNUSED)
+{
+    /* Nothing has being impemented yet. */
+    return NULL;
+}
+
+static int
+poll_group_is_valid(struct poll_group *group)
+{
+     const struct poll_group_class *class;
+     if (!group) {
+         return PG_ERROR_NO_GROUP;
+     }
+
+     class = group->class;
+     if (!class) {
+         return PG_ERROR_NO_CLASS;
+     }
+
+     if (!poll_group_class_is_valid(class)) {
+         return PG_ERROR_INVALID_CLASS;
+     }
+
+    return 0;
+}
+
+
+const char *
+poll_group_get_name(const struct poll_group *group)
+{
+    return group->name;
+}
+
+void
+poll_group_init(struct poll_group *group,
+                const char *name,
+                const struct poll_group_class *class)
+{
+    group->name = xstrdup(name);
+    group->class = class;
+    group->n_joined = 0;
+
+    group->events = NULL;
+    group->n_events = 0;
+    group->n_allocated = 0;
+}
+
+/* poll_group name is specified in tow parts "<class>:<name>",
+ * The class is used by 'poll_group_select_class' to select
+ * poll group implementation. 'name' is the name of the poll group
+ * mostly for logging purpose.   */
+struct poll_group *
+poll_group_create(const char *name)
+{
+    const struct poll_group_class *class;
+    size_t prefix_len;
+    struct poll_group *group = NULL;
+
+    prefix_len = strcspn(name, ":");
+    class = poll_group_select_class(name, prefix_len);
+
+    if (poll_group_class_is_valid(class)) {
+        const char *group_name;
+        if (name[prefix_len] == '\0') {
+            group_name = name;
+        } else {
+            group_name = &name[prefix_len + 1];
+        }
+        group = class->create(group_name);
+    }
+
+    return group;
+}
+
+void
+poll_group_close(struct poll_group *group)
+{
+    int ret = poll_group_is_valid(group);
+
+    if (ret) {
+        group->class->close(group);
+        group->n_allocated = 0;
+        group->n_allocated = 0;
+        group->n_joined = 0;
+
+        free(group->events);
+        group->events = NULL;
+
+        free(group->name);
+        group->name = NULL;
+    }
+
+    free(group);
+}
+
+int
+poll_group_join(struct poll_group *group, int fd, void *caller_event)
+{
+    int ret = poll_group_is_valid(group);
+
+    if (!ret) {
+        ret = group->class->join(group, fd, caller_event);
+        if (!ret) {
+            group->n_joined++;
+        }
+    }
+
+    return ret;
+}
+
+int
+poll_group_update(struct poll_group *group, int fd, bool write,
+                  void *caller_event)
+{
+    int ret = poll_group_is_valid(group);
+
+    if (!ret) {
+        ret = group->class->update(group, fd, write, caller_event);
+    }
+
+    return ret;
+}
+
+int
+poll_group_leave(struct poll_group *group, int fd)
+{
+    int ret = poll_group_is_valid(group);
+
+    if (!ret) {
+        ret = group->class->leave(group, fd);
+        if (!ret) {
+            group->n_joined--;
+        }
+    }
+
+    return ret;
+}
+
+void
+poll_group_poll_wait(struct poll_group *group)
+{
+    int ret = poll_group_is_valid(group);
+
+    if (!ret) {
+        group->n_events = 0;
+        group->class->poll_wait(group);
+    }
+}
+
+void
+poll_group_notify(struct poll_group *group, void *caller_event)
+{
+    if (group && caller_event) {
+        add_caller_event(group, caller_event);
+    }
+}
+
+void
+poll_group_get_events(struct poll_group *group, void ***caller_event,
+                      size_t *n)
+{
+    if (group && group->class->get_events) {
+        group->class->get_events(group);
+    }
+    *n = group->n_events;
+    *caller_event = group->events;
+}
+
+
+static void
+add_caller_event(struct poll_group *group, void *caller_event)
+{
+     size_t n_events = group->n_events;
+     size_t n_allocated = group->n_allocated;
+
+     ovs_assert(n_events <= group->n_joined);
+
+     if (n_events >= n_allocated) {
+         void **events = group->events;
+         events = x2nrealloc(events, &group->n_allocated, sizeof events[0]);
+         group->events = events;
+     }
+
+     group->events[group->n_events++] = caller_event;
+
+}
diff --git a/lib/poll-group.h b/lib/poll-group.h
new file mode 100644
index 0000000..e03cde7
--- /dev/null
+++ b/lib/poll-group.h
@@ -0,0 +1,51 @@ 
+/*
+ * Copyright (c) 2016 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef POLL_GROUP_H
+#define POLL_GROUP_H 1
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include "openvswitch/types.h"
+
+struct poll_group;
+
+enum {
+    PG_ERROR_NO_ERROR,       /* Success */
+    PG_ERROR_NO_GROUP,
+    PG_ERROR_NO_CLASS,
+    PG_ERROR_INVALID_CLASS,
+    PG_ERROR_DUPLICATED_FD,
+    PG_ERROR_FD_NOT_FOUND,
+};
+
+const char *poll_group_get_name(const struct poll_group *);
+struct poll_group *poll_group_create(const char *name);
+void poll_group_close(struct poll_group *group);
+
+int poll_group_join(struct poll_group *group, int fd, void *caller_event);
+int poll_group_update(struct poll_group *group, int fd, bool write,
+                      void *caller_event);
+int poll_group_leave(struct poll_group *group, int fd);
+
+void poll_group_poll_wait(struct poll_group *group);
+
+void poll_group_notify(struct poll_group *, void *caller_event);
+void poll_group_get_events(struct poll_group *, void ***caller_event, size_t *n);
+
+#endif /* poll-group.h */