diff --git a/src/compiler/python_generator.cc b/src/compiler/python_generator.cc
index 1a06d0f21ef8b3d52d3cfaf5d0f0d273cb7a8ba8..31b177c28e6118a9045eebab4d10029ce6604d1d 100644
--- a/src/compiler/python_generator.cc
+++ b/src/compiler/python_generator.cc
@@ -756,6 +756,7 @@ bool PythonGrpcGenerator::Generate(const FileDescriptor* file,
       file->name().find_last_of(".proto") == file->name().size() - 1) {
     grpc::string base =
         file->name().substr(0, file->name().size() - proto_suffix_length);
+    std::replace(base.begin(), base.end(), '-', '_');
     pb2_file_name = base + "_pb2.py";
     pb2_grpc_file_name = base + "_pb2_grpc.py";
   } else {
diff --git a/src/core/ext/filters/client_channel/client_channel_factory.c b/src/core/ext/filters/client_channel/client_channel_factory.c
index 04bb4d5a2d0b9412a74ef115948a3b2aab4e0a05..7220a8639e811a1ba3ca3fb78e025cd1de6b94d2 100644
--- a/src/core/ext/filters/client_channel/client_channel_factory.c
+++ b/src/core/ext/filters/client_channel/client_channel_factory.c
@@ -17,6 +17,7 @@
  */
 
 #include "src/core/ext/filters/client_channel/client_channel_factory.h"
+#include "src/core/lib/channel/channel_args.h"
 
 void grpc_client_channel_factory_ref(grpc_client_channel_factory* factory) {
   factory->vtable->ref(factory);
@@ -63,10 +64,6 @@ static const grpc_arg_pointer_vtable factory_arg_vtable = {
 
 grpc_arg grpc_client_channel_factory_create_channel_arg(
     grpc_client_channel_factory* factory) {
-  grpc_arg arg;
-  arg.type = GRPC_ARG_POINTER;
-  arg.key = GRPC_ARG_CLIENT_CHANNEL_FACTORY;
-  arg.value.pointer.p = factory;
-  arg.value.pointer.vtable = &factory_arg_vtable;
-  return arg;
+  return grpc_channel_arg_pointer_create(GRPC_ARG_CLIENT_CHANNEL_FACTORY,
+                                         factory, &factory_arg_vtable);
 }
diff --git a/src/core/ext/filters/client_channel/client_channel_plugin.c b/src/core/ext/filters/client_channel/client_channel_plugin.c
index 5dc14413441a59c37aaf8ed97ca5343b7f769ec9..60e77d62683f41a397ac5c4505f007666ae84c2b 100644
--- a/src/core/ext/filters/client_channel/client_channel_plugin.c
+++ b/src/core/ext/filters/client_channel/client_channel_plugin.c
@@ -54,10 +54,8 @@ static bool set_default_host_if_unset(grpc_exec_ctx *exec_ctx,
   char *default_authority = grpc_get_default_authority(
       exec_ctx, grpc_channel_stack_builder_get_target(builder));
   if (default_authority != NULL) {
-    grpc_arg arg;
-    arg.type = GRPC_ARG_STRING;
-    arg.key = GRPC_ARG_DEFAULT_AUTHORITY;
-    arg.value.string = default_authority;
+    grpc_arg arg = grpc_channel_arg_string_create(GRPC_ARG_DEFAULT_AUTHORITY,
+                                                  default_authority);
     grpc_channel_args *new_args = grpc_channel_args_copy_and_add(args, &arg, 1);
     grpc_channel_stack_builder_set_channel_arguments(exec_ctx, builder,
                                                      new_args);
diff --git a/src/core/ext/filters/client_channel/http_proxy.c b/src/core/ext/filters/client_channel/http_proxy.c
index b8332980e5252393a6b763aa2f4f8ec5c0a7ce2d..cfb5ec6f00eed71ca66cf103b1ff237480adc438 100644
--- a/src/core/ext/filters/client_channel/http_proxy.c
+++ b/src/core/ext/filters/client_channel/http_proxy.c
@@ -80,10 +80,9 @@ static bool proxy_mapper_map_name(grpc_exec_ctx* exec_ctx,
     grpc_uri_destroy(uri);
     return false;
   }
-  grpc_arg new_arg;
-  new_arg.key = GRPC_ARG_HTTP_CONNECT_SERVER;
-  new_arg.type = GRPC_ARG_STRING;
-  new_arg.value.string = uri->path[0] == '/' ? uri->path + 1 : uri->path;
+  grpc_arg new_arg = grpc_channel_arg_string_create(
+      GRPC_ARG_HTTP_CONNECT_SERVER,
+      uri->path[0] == '/' ? uri->path + 1 : uri->path);
   *new_args = grpc_channel_args_copy_and_add(args, &new_arg, 1);
   grpc_uri_destroy(uri);
   return true;
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c
index cf442ce66980f106e095f2d1c5491f7b643250ba..5a5ff2902d0cb8a5863232c893effd49dea010dd 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c
@@ -974,10 +974,8 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx,
 
   // Make sure that GRPC_ARG_LB_POLICY_NAME is set in channel args,
   // since we use this to trigger the client_load_reporting filter.
-  grpc_arg new_arg;
-  new_arg.key = GRPC_ARG_LB_POLICY_NAME;
-  new_arg.type = GRPC_ARG_STRING;
-  new_arg.value.string = "grpclb";
+  grpc_arg new_arg =
+      grpc_channel_arg_string_create(GRPC_ARG_LB_POLICY_NAME, "grpclb");
   static const char *args_to_remove[] = {GRPC_ARG_LB_POLICY_NAME};
   glb_policy->args = grpc_channel_args_copy_and_add_and_remove(
       args->args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), &new_arg, 1);
diff --git a/src/core/ext/filters/client_channel/lb_policy_factory.c b/src/core/ext/filters/client_channel/lb_policy_factory.c
index abac0fd7b4eb8f20884836b17e13b59d763c87b6..538d8d65ed45acec366a7ae853fb93a8d5d2dabf 100644
--- a/src/core/ext/filters/client_channel/lb_policy_factory.c
+++ b/src/core/ext/filters/client_channel/lb_policy_factory.c
@@ -138,12 +138,8 @@ static const grpc_arg_pointer_vtable lb_addresses_arg_vtable = {
 
 grpc_arg grpc_lb_addresses_create_channel_arg(
     const grpc_lb_addresses* addresses) {
-  grpc_arg arg;
-  arg.type = GRPC_ARG_POINTER;
-  arg.key = GRPC_ARG_LB_ADDRESSES;
-  arg.value.pointer.p = (void*)addresses;
-  arg.value.pointer.vtable = &lb_addresses_arg_vtable;
-  return arg;
+  return grpc_channel_arg_pointer_create(
+      GRPC_ARG_LB_ADDRESSES, (void*)addresses, &lb_addresses_arg_vtable);
 }
 
 grpc_lb_addresses* grpc_lb_addresses_find_channel_arg(
diff --git a/src/core/ext/filters/client_channel/subchannel.c b/src/core/ext/filters/client_channel/subchannel.c
index c85b31002e5b06c5a384fda322248be9a304a664..31a38b08ae7620c73261e38ea64be5dd88d4cfb8 100644
--- a/src/core/ext/filters/client_channel/subchannel.c
+++ b/src/core/ext/filters/client_channel/subchannel.c
@@ -809,10 +809,7 @@ const char *grpc_get_subchannel_address_uri_arg(const grpc_channel_args *args) {
 }
 
 grpc_arg grpc_create_subchannel_address_arg(const grpc_resolved_address *addr) {
-  grpc_arg new_arg;
-  new_arg.key = GRPC_ARG_SUBCHANNEL_ADDRESS;
-  new_arg.type = GRPC_ARG_STRING;
-  new_arg.value.string =
-      addr->len > 0 ? grpc_sockaddr_to_uri(addr) : gpr_strdup("");
-  return new_arg;
+  return grpc_channel_arg_string_create(
+      GRPC_ARG_SUBCHANNEL_ADDRESS,
+      addr->len > 0 ? grpc_sockaddr_to_uri(addr) : gpr_strdup(""));
 }
diff --git a/src/core/ext/filters/load_reporting/load_reporting.c b/src/core/ext/filters/load_reporting/load_reporting.c
index a97322ee1dbb106ea1b9bdf7c7d4bc3938f622df..9745763c91c46c6c461f1313ff710628371e96a5 100644
--- a/src/core/ext/filters/load_reporting/load_reporting.c
+++ b/src/core/ext/filters/load_reporting/load_reporting.c
@@ -50,11 +50,7 @@ static bool maybe_add_load_reporting_filter(grpc_exec_ctx *exec_ctx,
 }
 
 grpc_arg grpc_load_reporting_enable_arg() {
-  grpc_arg arg;
-  arg.type = GRPC_ARG_INTEGER;
-  arg.key = GRPC_ARG_ENABLE_LOAD_REPORTING;
-  arg.value.integer = 1;
-  return arg;
+  return grpc_channel_arg_integer_create(GRPC_ARG_ENABLE_LOAD_REPORTING, 1);
 }
 
 /* Plugin registration */
diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.c b/src/core/ext/transport/chttp2/client/insecure/channel_create.c
index 99bae76237a2cdfcdf810c1b2e8655cfc90effe5..cccb347bf10a1634cc0e62fabcfdb305edd6a6b7 100644
--- a/src/core/ext/transport/chttp2/client/insecure/channel_create.c
+++ b/src/core/ext/transport/chttp2/client/insecure/channel_create.c
@@ -54,11 +54,9 @@ static grpc_channel *client_channel_factory_create_channel(
     return NULL;
   }
   // Add channel arg containing the server URI.
-  grpc_arg arg;
-  arg.type = GRPC_ARG_STRING;
-  arg.key = GRPC_ARG_SERVER_URI;
-  arg.value.string =
-      grpc_resolver_factory_add_default_prefix_if_needed(exec_ctx, target);
+  grpc_arg arg = grpc_channel_arg_string_create(
+      GRPC_ARG_SERVER_URI,
+      grpc_resolver_factory_add_default_prefix_if_needed(exec_ctx, target));
   const char *to_remove[] = {GRPC_ARG_SERVER_URI};
   grpc_channel_args *new_args =
       grpc_channel_args_copy_and_add_and_remove(args, to_remove, 1, &arg, 1);
diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
index 05145aeb2fc4ebdde99b42a8ceede5556575aab1..0346d50b6ce085cf829813f4a4565f090f09c9eb 100644
--- a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
+++ b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c
@@ -41,10 +41,8 @@ grpc_channel *grpc_insecure_channel_create_from_fd(
   GRPC_API_TRACE("grpc_insecure_channel_create(target=%p, fd=%d, args=%p)", 3,
                  (target, fd, args));
 
-  grpc_arg default_authority_arg;
-  default_authority_arg.type = GRPC_ARG_STRING;
-  default_authority_arg.key = GRPC_ARG_DEFAULT_AUTHORITY;
-  default_authority_arg.value.string = "test.authority";
+  grpc_arg default_authority_arg = grpc_channel_arg_string_create(
+      GRPC_ARG_DEFAULT_AUTHORITY, "test.authority");
   grpc_channel_args *final_args =
       grpc_channel_args_copy_and_add(args, &default_authority_arg, 1);
 
diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
index 7b76caba17821f9707a2dbdfc3a5243126441e1c..d4580f15f5e79db15b75d70ac71188187fdac98e 100644
--- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
+++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
@@ -163,11 +163,9 @@ static grpc_channel *client_channel_factory_create_channel(
     return NULL;
   }
   // Add channel arg containing the server URI.
-  grpc_arg arg;
-  arg.type = GRPC_ARG_STRING;
-  arg.key = GRPC_ARG_SERVER_URI;
-  arg.value.string =
-      grpc_resolver_factory_add_default_prefix_if_needed(exec_ctx, target);
+  grpc_arg arg = grpc_channel_arg_string_create(
+      GRPC_ARG_SERVER_URI,
+      grpc_resolver_factory_add_default_prefix_if_needed(exec_ctx, target));
   const char *to_remove[] = {GRPC_ARG_SERVER_URI};
   grpc_channel_args *new_args =
       grpc_channel_args_copy_and_add_and_remove(args, to_remove, 1, &arg, 1);
diff --git a/src/core/lib/channel/channel_args.c b/src/core/lib/channel/channel_args.c
index 4b7f258740a39528aa66b3e4bd1fea9b53d6f0cc..8fdef0bc647401d272030560e3cd1bff70f37148 100644
--- a/src/core/lib/channel/channel_args.c
+++ b/src/core/lib/channel/channel_args.c
@@ -373,3 +373,29 @@ bool grpc_channel_args_want_minimal_stack(const grpc_channel_args *args) {
   return grpc_channel_arg_get_bool(
       grpc_channel_args_find(args, GRPC_ARG_MINIMAL_STACK), false);
 }
+
+grpc_arg grpc_channel_arg_string_create(char *name, char *value) {
+  grpc_arg arg;
+  arg.type = GRPC_ARG_STRING;
+  arg.key = name;
+  arg.value.string = value;
+  return arg;
+}
+
+grpc_arg grpc_channel_arg_integer_create(char *name, int value) {
+  grpc_arg arg;
+  arg.type = GRPC_ARG_INTEGER;
+  arg.key = name;
+  arg.value.integer = value;
+  return arg;
+}
+
+grpc_arg grpc_channel_arg_pointer_create(
+    char *name, void *value, const grpc_arg_pointer_vtable *vtable) {
+  grpc_arg arg;
+  arg.type = GRPC_ARG_POINTER;
+  arg.key = name;
+  arg.value.pointer.p = value;
+  arg.value.pointer.vtable = vtable;
+  return arg;
+}
diff --git a/src/core/lib/channel/channel_args.h b/src/core/lib/channel/channel_args.h
index ba1d234005b01456af70a242676cb48ab89bb777..f649a8d9ec9ca9702b414cf7279b65fd4a990f7c 100644
--- a/src/core/lib/channel/channel_args.h
+++ b/src/core/lib/channel/channel_args.h
@@ -112,4 +112,10 @@ int grpc_channel_arg_get_integer(const grpc_arg *arg,
 
 bool grpc_channel_arg_get_bool(const grpc_arg *arg, bool default_value);
 
+// Helpers for creating channel args.
+grpc_arg grpc_channel_arg_string_create(char *name, char *value);
+grpc_arg grpc_channel_arg_integer_create(char *name, int value);
+grpc_arg grpc_channel_arg_pointer_create(char *name, void *value,
+                                         const grpc_arg_pointer_vtable *vtable);
+
 #endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H */
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c
index 1b7e2cfe68a83048816a07b7f44d585f7b5a35d8..77af7b7c08a115cc0c21edbf2bfd822bd86aaec1 100644
--- a/src/core/lib/http/httpcli.c
+++ b/src/core/lib/http/httpcli.c
@@ -25,6 +25,7 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/useful.h>
 
+#include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/http/format_request.h"
 #include "src/core/lib/http/parser.h"
 #include "src/core/lib/iomgr/endpoint.h"
@@ -215,11 +216,9 @@ static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req,
   addr = &req->addresses->addrs[req->next_address++];
   GRPC_CLOSURE_INIT(&req->connected, on_connected, req,
                     grpc_schedule_on_exec_ctx);
-  grpc_arg arg;
-  arg.key = GRPC_ARG_RESOURCE_QUOTA;
-  arg.type = GRPC_ARG_POINTER;
-  arg.value.pointer.p = req->resource_quota;
-  arg.value.pointer.vtable = grpc_resource_quota_arg_vtable();
+  grpc_arg arg = grpc_channel_arg_pointer_create(
+      GRPC_ARG_RESOURCE_QUOTA, req->resource_quota,
+      grpc_resource_quota_arg_vtable());
   grpc_channel_args args = {1, &arg};
   grpc_tcp_client_connect(exec_ctx, &req->connected, &req->ep,
                           req->context->pollset_set, &args, addr,
diff --git a/src/core/lib/iomgr/ev_epollex_linux.c b/src/core/lib/iomgr/ev_epollex_linux.c
index abcf7b429f61c4ae7f442c0b0ef2b89d2a5c8066..949f8a845d492bb52863155a49114d80f1d540e6 100644
--- a/src/core/lib/iomgr/ev_epollex_linux.c
+++ b/src/core/lib/iomgr/ev_epollex_linux.c
@@ -169,12 +169,20 @@ struct grpc_pollset_worker {
   pollable *pollable;
 };
 
+#define MAX_EPOLL_EVENTS 100
+#define MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL 5
+
 struct grpc_pollset {
   pollable pollable;
   pollable *current_pollable;
+  int kick_alls_pending;
   bool kicked_without_poller;
   grpc_closure *shutdown_closure;
   grpc_pollset_worker *root_worker;
+
+  int event_cursor;
+  int event_count;
+  struct epoll_event events[MAX_EPOLL_EVENTS];
 };
 
 /*******************************************************************************
@@ -437,7 +445,7 @@ static grpc_error *pollable_materialize(pollable *p) {
       return err;
     }
     struct epoll_event ev = {.events = (uint32_t)(EPOLLIN | EPOLLET),
-                             .data.ptr = &p->wakeup};
+                             .data.ptr = (void *)(1 | (intptr_t)&p->wakeup)};
     if (epoll_ctl(new_epfd, EPOLL_CTL_ADD, p->wakeup.read_fd, &ev) != 0) {
       err = GRPC_OS_ERROR(errno, "epoll_ctl");
       close(new_epfd);
@@ -503,8 +511,20 @@ static void pollset_global_shutdown(void) {
   gpr_tls_destroy(&g_current_thread_worker);
 }
 
-static grpc_error *pollset_kick_all(grpc_pollset *pollset) {
+static void pollset_maybe_finish_shutdown(grpc_exec_ctx *exec_ctx,
+                                          grpc_pollset *pollset) {
+  if (pollset->shutdown_closure != NULL && pollset->root_worker == NULL &&
+      pollset->kick_alls_pending == 0) {
+    GRPC_CLOSURE_SCHED(exec_ctx, pollset->shutdown_closure, GRPC_ERROR_NONE);
+    pollset->shutdown_closure = NULL;
+  }
+}
+
+static void do_kick_all(grpc_exec_ctx *exec_ctx, void *arg,
+                        grpc_error *error_unused) {
   grpc_error *error = GRPC_ERROR_NONE;
+  grpc_pollset *pollset = arg;
+  gpr_mu_lock(&pollset->pollable.po.mu);
   if (pollset->root_worker != NULL) {
     grpc_pollset_worker *worker = pollset->root_worker;
     do {
@@ -525,7 +545,17 @@ static grpc_error *pollset_kick_all(grpc_pollset *pollset) {
       worker = worker->links[PWL_POLLSET].next;
     } while (worker != pollset->root_worker);
   }
-  return error;
+  pollset->kick_alls_pending--;
+  pollset_maybe_finish_shutdown(exec_ctx, pollset);
+  gpr_mu_unlock(&pollset->pollable.po.mu);
+  GRPC_LOG_IF_ERROR("kick_all", error);
+}
+
+static void pollset_kick_all(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) {
+  pollset->kick_alls_pending++;
+  GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_CREATE(do_kick_all, pollset,
+                                                   grpc_schedule_on_exec_ctx),
+                     GRPC_ERROR_NONE);
 }
 
 static grpc_error *pollset_kick_inner(grpc_pollset *pollset, pollable *p,
@@ -664,20 +694,12 @@ static grpc_error *fd_become_pollable_locked(grpc_fd *fd) {
   return error;
 }
 
-static void pollset_maybe_finish_shutdown(grpc_exec_ctx *exec_ctx,
-                                          grpc_pollset *pollset) {
-  if (pollset->shutdown_closure != NULL && pollset->root_worker == NULL) {
-    GRPC_CLOSURE_SCHED(exec_ctx, pollset->shutdown_closure, GRPC_ERROR_NONE);
-    pollset->shutdown_closure = NULL;
-  }
-}
-
 /* pollset->po.mu lock must be held by the caller before calling this */
 static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                              grpc_closure *closure) {
   GPR_ASSERT(pollset->shutdown_closure == NULL);
   pollset->shutdown_closure = closure;
-  GRPC_LOG_IF_ERROR("pollset_shutdown", pollset_kick_all(pollset));
+  pollset_kick_all(exec_ctx, pollset);
   pollset_maybe_finish_shutdown(exec_ctx, pollset);
 }
 
@@ -685,6 +707,46 @@ static bool pollset_is_pollable_fd(grpc_pollset *pollset, pollable *p) {
   return p != &g_empty_pollable && p != &pollset->pollable;
 }
 
+static grpc_error *pollset_process_events(grpc_exec_ctx *exec_ctx,
+                                          grpc_pollset *pollset, bool drain) {
+  static const char *err_desc = "pollset_process_events";
+  grpc_error *error = GRPC_ERROR_NONE;
+  for (int i = 0; (drain || i < MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL) &&
+                  pollset->event_cursor != pollset->event_count;
+       i++) {
+    int n = pollset->event_cursor++;
+    struct epoll_event *ev = &pollset->events[n];
+    void *data_ptr = ev->data.ptr;
+    if (1 & (intptr_t)data_ptr) {
+      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        gpr_log(GPR_DEBUG, "PS:%p got pollset_wakeup %p", pollset, data_ptr);
+      }
+      append_error(&error, grpc_wakeup_fd_consume_wakeup(
+                               (void *)((~(intptr_t)1) & (intptr_t)data_ptr)),
+                   err_desc);
+    } else {
+      grpc_fd *fd = (grpc_fd *)data_ptr;
+      bool cancel = (ev->events & (EPOLLERR | EPOLLHUP)) != 0;
+      bool read_ev = (ev->events & (EPOLLIN | EPOLLPRI)) != 0;
+      bool write_ev = (ev->events & EPOLLOUT) != 0;
+      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        gpr_log(GPR_DEBUG,
+                "PS:%p got fd %p: cancel=%d read=%d "
+                "write=%d",
+                pollset, fd, cancel, read_ev, write_ev);
+      }
+      if (read_ev || cancel) {
+        fd_become_readable(exec_ctx, fd, pollset);
+      }
+      if (write_ev || cancel) {
+        fd_become_writable(exec_ctx, fd);
+      }
+    }
+  }
+
+  return error;
+}
+
 /* pollset_shutdown is guaranteed to be called before pollset_destroy. */
 static void pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) {
   pollable_destroy(&pollset->pollable);
@@ -692,16 +754,13 @@ static void pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) {
     UNREF_BY(exec_ctx, (grpc_fd *)pollset->current_pollable, 2,
              "pollset_pollable");
   }
+  GRPC_LOG_IF_ERROR("pollset_process_events",
+                    pollset_process_events(exec_ctx, pollset, true));
 }
 
-#define MAX_EPOLL_EVENTS 100
-
 static grpc_error *pollset_epoll(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                                  pollable *p, gpr_timespec now,
                                  gpr_timespec deadline) {
-  struct epoll_event events[MAX_EPOLL_EVENTS];
-  static const char *err_desc = "pollset_poll";
-
   int timeout = poll_deadline_to_millis_timeout(deadline, now);
 
   if (GRPC_TRACER_ON(grpc_polling_trace)) {
@@ -713,7 +772,7 @@ static grpc_error *pollset_epoll(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
   }
   int r;
   do {
-    r = epoll_wait(p->epfd, events, MAX_EPOLL_EVENTS, timeout);
+    r = epoll_wait(p->epfd, pollset->events, MAX_EPOLL_EVENTS, timeout);
   } while (r < 0 && errno == EINTR);
   if (timeout != 0) {
     GRPC_SCHEDULING_END_BLOCKING_REGION;
@@ -725,35 +784,10 @@ static grpc_error *pollset_epoll(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
     gpr_log(GPR_DEBUG, "PS:%p poll %p got %d events", pollset, p, r);
   }
 
-  grpc_error *error = GRPC_ERROR_NONE;
-  for (int i = 0; i < r; i++) {
-    void *data_ptr = events[i].data.ptr;
-    if (data_ptr == &p->wakeup) {
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
-        gpr_log(GPR_DEBUG, "PS:%p poll %p got pollset_wakeup", pollset, p);
-      }
-      append_error(&error, grpc_wakeup_fd_consume_wakeup(&p->wakeup), err_desc);
-    } else {
-      grpc_fd *fd = (grpc_fd *)data_ptr;
-      bool cancel = (events[i].events & (EPOLLERR | EPOLLHUP)) != 0;
-      bool read_ev = (events[i].events & (EPOLLIN | EPOLLPRI)) != 0;
-      bool write_ev = (events[i].events & EPOLLOUT) != 0;
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
-        gpr_log(GPR_DEBUG,
-                "PS:%p poll %p got fd %p: cancel=%d read=%d "
-                "write=%d",
-                pollset, p, fd, cancel, read_ev, write_ev);
-      }
-      if (read_ev || cancel) {
-        fd_become_readable(exec_ctx, fd, pollset);
-      }
-      if (write_ev || cancel) {
-        fd_become_writable(exec_ctx, fd);
-      }
-    }
-  }
+  pollset->event_cursor = 0;
+  pollset->event_count = r;
 
-  return error;
+  return GRPC_ERROR_NONE;
 }
 
 /* Return true if first in list */
@@ -905,10 +939,13 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
       gpr_mu_unlock(&worker.pollable->po.mu);
     }
     gpr_mu_unlock(&pollset->pollable.po.mu);
-    append_error(&error, pollset_epoll(exec_ctx, pollset, worker.pollable, now,
-                                       deadline),
+    if (pollset->event_cursor == pollset->event_count) {
+      append_error(&error, pollset_epoll(exec_ctx, pollset, worker.pollable,
+                                         now, deadline),
+                   err_desc);
+    }
+    append_error(&error, pollset_process_events(exec_ctx, pollset, false),
                  err_desc);
-    grpc_exec_ctx_flush(exec_ctx);
     gpr_mu_lock(&pollset->pollable.po.mu);
     if (worker.pollable != &pollset->pollable) {
       gpr_mu_lock(&worker.pollable->po.mu);
@@ -921,6 +958,11 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
   if (worker.pollable != &pollset->pollable) {
     gpr_mu_unlock(&worker.pollable->po.mu);
   }
+  if (grpc_exec_ctx_has_work(exec_ctx)) {
+    gpr_mu_unlock(&pollset->pollable.po.mu);
+    grpc_exec_ctx_flush(exec_ctx);
+    gpr_mu_lock(&pollset->pollable.po.mu);
+  }
   return error;
 }
 
@@ -942,7 +984,7 @@ static grpc_error *pollset_add_fd_locked(grpc_exec_ctx *exec_ctx,
               "PS:%p add fd %p; transition pollable from empty to fd", pollset,
               fd);
     /* empty pollable --> single fd pollable */
-    append_error(&error, pollset_kick_all(pollset), err_desc);
+    pollset_kick_all(exec_ctx, pollset);
     pollset->current_pollable = &fd->pollable;
     if (!fd_locked) gpr_mu_lock(&fd->pollable.po.mu);
     append_error(&error, fd_become_pollable_locked(fd), err_desc);
@@ -959,7 +1001,7 @@ static grpc_error *pollset_add_fd_locked(grpc_exec_ctx *exec_ctx,
       gpr_log(GPR_DEBUG,
               "PS:%p add fd %p; transition pollable from fd %p to multipoller",
               pollset, fd, had_fd);
-    append_error(&error, pollset_kick_all(pollset), err_desc);
+    pollset_kick_all(exec_ctx, pollset);
     pollset->current_pollable = &pollset->pollable;
     if (append_error(&error, pollable_materialize(&pollset->pollable),
                      err_desc)) {
@@ -1323,8 +1365,6 @@ static const grpc_event_engine_vtable vtable = {
 
 const grpc_event_engine_vtable *grpc_init_epollex_linux(
     bool explicitly_requested) {
-  if (!explicitly_requested) return NULL;
-
   if (!grpc_has_wakeup_fd()) {
     return NULL;
   }
diff --git a/src/core/lib/iomgr/socket_factory_posix.c b/src/core/lib/iomgr/socket_factory_posix.c
index 7d25bc1265d15b3e91b9df9869ecc898682435d8..0f82dea570fabcaf5644ddf8ca42c955d7838a44 100644
--- a/src/core/lib/iomgr/socket_factory_posix.c
+++ b/src/core/lib/iomgr/socket_factory_posix.c
@@ -20,6 +20,7 @@
 
 #ifdef GRPC_POSIX_SOCKET
 
+#include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/iomgr/socket_factory_posix.h"
 
 #include <grpc/impl/codegen/grpc_types.h>
@@ -84,12 +85,8 @@ static const grpc_arg_pointer_vtable socket_factory_arg_vtable = {
     socket_factory_arg_copy, socket_factory_arg_destroy, socket_factory_cmp};
 
 grpc_arg grpc_socket_factory_to_arg(grpc_socket_factory *factory) {
-  grpc_arg arg;
-  arg.type = GRPC_ARG_POINTER;
-  arg.key = GRPC_ARG_SOCKET_FACTORY;
-  arg.value.pointer.vtable = &socket_factory_arg_vtable;
-  arg.value.pointer.p = factory;
-  return arg;
+  return grpc_channel_arg_pointer_create(GRPC_ARG_SOCKET_FACTORY, factory,
+                                         &socket_factory_arg_vtable);
 }
 
 #endif
diff --git a/src/core/lib/iomgr/socket_mutator.c b/src/core/lib/iomgr/socket_mutator.c
index c4b9a0930b2d0ff771af26002f4c1becf2ec56de..5d6c2c400ebbedd32ea110bd8d639eb8dc8ec9a0 100644
--- a/src/core/lib/iomgr/socket_mutator.c
+++ b/src/core/lib/iomgr/socket_mutator.c
@@ -18,6 +18,8 @@
 
 #include "src/core/lib/iomgr/socket_mutator.h"
 
+#include "src/core/lib/channel/channel_args.h"
+
 #include <grpc/impl/codegen/grpc_types.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/useful.h>
@@ -74,10 +76,6 @@ static const grpc_arg_pointer_vtable socket_mutator_arg_vtable = {
     socket_mutator_arg_copy, socket_mutator_arg_destroy, socket_mutator_cmp};
 
 grpc_arg grpc_socket_mutator_to_arg(grpc_socket_mutator *mutator) {
-  grpc_arg arg;
-  arg.type = GRPC_ARG_POINTER;
-  arg.key = GRPC_ARG_SOCKET_MUTATOR;
-  arg.value.pointer.vtable = &socket_mutator_arg_vtable;
-  arg.value.pointer.p = mutator;
-  return arg;
+  return grpc_channel_arg_pointer_create(GRPC_ARG_SOCKET_MUTATOR, mutator,
+                                         &socket_mutator_arg_vtable);
 }
diff --git a/src/core/lib/security/context/security_context.c b/src/core/lib/security/context/security_context.c
index e7c3dd45c8b06d37a9be7a61ab88f31e58593f9f..dffe6d2e91202778f048e9332220e4062c8ad2f4 100644
--- a/src/core/lib/security/context/security_context.c
+++ b/src/core/lib/security/context/security_context.c
@@ -18,6 +18,7 @@
 
 #include <string.h>
 
+#include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/api_trace.h"
@@ -315,13 +316,8 @@ static const grpc_arg_pointer_vtable auth_context_pointer_vtable = {
     auth_context_pointer_cmp};
 
 grpc_arg grpc_auth_context_to_arg(grpc_auth_context *p) {
-  grpc_arg arg;
-  memset(&arg, 0, sizeof(grpc_arg));
-  arg.type = GRPC_ARG_POINTER;
-  arg.key = GRPC_AUTH_CONTEXT_ARG;
-  arg.value.pointer.p = p;
-  arg.value.pointer.vtable = &auth_context_pointer_vtable;
-  return arg;
+  return grpc_channel_arg_pointer_create(GRPC_AUTH_CONTEXT_ARG, p,
+                                         &auth_context_pointer_vtable);
 }
 
 grpc_auth_context *grpc_auth_context_from_arg(const grpc_arg *arg) {
diff --git a/src/core/lib/security/credentials/credentials.c b/src/core/lib/security/credentials/credentials.c
index 64a7c3e728bff2919502fb62d8a830beeb50f701..b1f1e82076c84b797b651b5af804e8e0b3f72e83 100644
--- a/src/core/lib/security/credentials/credentials.c
+++ b/src/core/lib/security/credentials/credentials.c
@@ -159,12 +159,8 @@ static const grpc_arg_pointer_vtable credentials_pointer_vtable = {
 
 grpc_arg grpc_channel_credentials_to_arg(
     grpc_channel_credentials *credentials) {
-  grpc_arg result;
-  result.type = GRPC_ARG_POINTER;
-  result.key = GRPC_ARG_CHANNEL_CREDENTIALS;
-  result.value.pointer.vtable = &credentials_pointer_vtable;
-  result.value.pointer.p = credentials;
-  return result;
+  return grpc_channel_arg_pointer_create(
+      GRPC_ARG_CHANNEL_CREDENTIALS, credentials, &credentials_pointer_vtable);
 }
 
 grpc_channel_credentials *grpc_channel_credentials_from_arg(
@@ -260,13 +256,8 @@ static const grpc_arg_pointer_vtable cred_ptr_vtable = {
     server_credentials_pointer_cmp};
 
 grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials *p) {
-  grpc_arg arg;
-  memset(&arg, 0, sizeof(grpc_arg));
-  arg.type = GRPC_ARG_POINTER;
-  arg.key = GRPC_SERVER_CREDENTIALS_ARG;
-  arg.value.pointer.p = p;
-  arg.value.pointer.vtable = &cred_ptr_vtable;
-  return arg;
+  return grpc_channel_arg_pointer_create(GRPC_SERVER_CREDENTIALS_ARG, p,
+                                         &cred_ptr_vtable);
 }
 
 grpc_server_credentials *grpc_server_credentials_from_arg(const grpc_arg *arg) {
diff --git a/src/core/lib/security/credentials/fake/fake_credentials.c b/src/core/lib/security/credentials/fake/fake_credentials.c
index 3cbb399429e333a0f0c204287e339c29e792ce21..67e74f7b92141a367de1d81d0e5a176a427e84a6 100644
--- a/src/core/lib/security/credentials/fake/fake_credentials.c
+++ b/src/core/lib/security/credentials/fake/fake_credentials.c
@@ -78,11 +78,8 @@ grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
 }
 
 grpc_arg grpc_fake_transport_expected_targets_arg(char *expected_targets) {
-  grpc_arg arg;
-  arg.type = GRPC_ARG_STRING;
-  arg.key = GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS;
-  arg.value.string = expected_targets;
-  return arg;
+  return grpc_channel_arg_string_create(GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS,
+                                        expected_targets);
 }
 
 const char *grpc_fake_transport_get_expected_targets(
diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.c b/src/core/lib/security/credentials/ssl/ssl_credentials.c
index b94b457d35886b23b7d394c1912c1650fe7be8fc..006db1ec765975203dbe362912357d12090b30af 100644
--- a/src/core/lib/security/credentials/ssl/ssl_credentials.c
+++ b/src/core/lib/security/credentials/ssl/ssl_credentials.c
@@ -52,11 +52,8 @@ static grpc_security_status ssl_create_security_connector(
     grpc_channel_args **new_args) {
   grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
   grpc_security_status status = GRPC_SECURITY_OK;
-  size_t i = 0;
   const char *overridden_target_name = NULL;
-  grpc_arg new_arg;
-
-  for (i = 0; args && i < args->num_args; i++) {
+  for (size_t i = 0; args && i < args->num_args; i++) {
     grpc_arg *arg = &args->args[i];
     if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 &&
         arg->type == GRPC_ARG_STRING) {
@@ -69,9 +66,8 @@ static grpc_security_status ssl_create_security_connector(
   if (status != GRPC_SECURITY_OK) {
     return status;
   }
-  new_arg.type = GRPC_ARG_STRING;
-  new_arg.key = GRPC_ARG_HTTP2_SCHEME;
-  new_arg.value.string = "https";
+  grpc_arg new_arg =
+      grpc_channel_arg_string_create(GRPC_ARG_HTTP2_SCHEME, "https");
   *new_args = grpc_channel_args_copy_and_add(args, &new_arg, 1);
   return status;
 }
diff --git a/src/core/lib/security/transport/lb_targets_info.c b/src/core/lib/security/transport/lb_targets_info.c
index 45bc91b30c62c3ce5976441d5289c07f91b99547..5583a4e0ffcf7e8e0c42b863b9039e2ca80b7016 100644
--- a/src/core/lib/security/transport/lb_targets_info.c
+++ b/src/core/lib/security/transport/lb_targets_info.c
@@ -37,12 +37,9 @@ static const grpc_arg_pointer_vtable server_to_balancer_names_vtable = {
 
 grpc_arg grpc_lb_targets_info_create_channel_arg(
     grpc_slice_hash_table *targets_info) {
-  grpc_arg arg;
-  arg.type = GRPC_ARG_POINTER;
-  arg.key = GRPC_ARG_LB_SECURE_NAMING_MAP;
-  arg.value.pointer.p = targets_info;
-  arg.value.pointer.vtable = &server_to_balancer_names_vtable;
-  return arg;
+  return grpc_channel_arg_pointer_create(GRPC_ARG_LB_SECURE_NAMING_MAP,
+                                         targets_info,
+                                         &server_to_balancer_names_vtable);
 }
 
 grpc_slice_hash_table *grpc_lb_targets_info_find_in_args(
diff --git a/src/core/lib/security/transport/security_connector.c b/src/core/lib/security/transport/security_connector.c
index 5d879e99359379ccf1a5954c0047aac1f9542ec7..3c0c24254b72554ced3a9f765eac69be82e25cc6 100644
--- a/src/core/lib/security/transport/security_connector.c
+++ b/src/core/lib/security/transport/security_connector.c
@@ -202,12 +202,8 @@ static const grpc_arg_pointer_vtable connector_pointer_vtable = {
     connector_pointer_cmp};
 
 grpc_arg grpc_security_connector_to_arg(grpc_security_connector *sc) {
-  grpc_arg result;
-  result.type = GRPC_ARG_POINTER;
-  result.key = GRPC_ARG_SECURITY_CONNECTOR;
-  result.value.pointer.vtable = &connector_pointer_vtable;
-  result.value.pointer.p = sc;
-  return result;
+  return grpc_channel_arg_pointer_create(GRPC_ARG_SECURITY_CONNECTOR, sc,
+                                         &connector_pointer_vtable);
 }
 
 grpc_security_connector *grpc_security_connector_from_arg(const grpc_arg *arg) {
diff --git a/tools/jenkins/run_bazel_full.sh b/tools/jenkins/run_bazel_full.sh
index 3436a8f8b69e6f0583079e9beae3bc1ea7d9a489..edde1bc545146219ca845fa7df55758a7c6057ca 100755
--- a/tools/jenkins/run_bazel_full.sh
+++ b/tools/jenkins/run_bazel_full.sh
@@ -20,4 +20,6 @@ set -ex -o igncr || set -ex
 
 export DOCKERFILE_DIR=tools/dockerfile/test/bazel
 export DOCKER_RUN_SCRIPT=tools/jenkins/run_bazel_full_in_docker.sh
+# Warn PR author if they make a change to the bazel directory
+tools/run_tests/python_utils/check_bazel_dir.py
 exec tools/run_tests/dockerize/build_and_run_docker.sh
diff --git a/tools/run_tests/python_utils/check_bazel_dir.py b/tools/run_tests/python_utils/check_bazel_dir.py
new file mode 100755
index 0000000000000000000000000000000000000000..1daf6ee5959ac04097e20afcaedbde97b5172d6f
--- /dev/null
+++ b/tools/run_tests/python_utils/check_bazel_dir.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+# Copyright 2017 gRPC authors.
+#
+# 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.
+
+"""This sends out a warning if any changes to the bazel dir are made."""
+
+from __future__ import print_function
+from subprocess import check_output
+
+import comment_on_pr
+import os
+
+_WARNING_MESSAGE = 'WARNING: You are making changes in the Bazel subdirectory. ' \
+                   'Please get explicit approval from @nicolasnoble before merging.'
+
+
+def _get_changed_files(base_branch):
+  """
+  Get list of changed files between current branch and base of target merge branch
+  """
+  # Get file changes between branch and merge-base of specified branch
+  base_commit = check_output(["git", "merge-base", base_branch, "HEAD"]).rstrip()
+  return check_output(["git", "diff", base_commit, "--name-only"]).splitlines()
+
+
+# ghprbTargetBranch environment variable only available during a Jenkins PR tests
+if 'ghprbTargetBranch' in os.environ:
+  changed_files = _get_changed_files('origin/%s' % os.environ['ghprbTargetBranch'])
+  if any(file.startswith('bazel/') for file in changed_files):
+    comment_on_pr.comment_on_pr(_WARNING_MESSAGE)
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index a0d84ea9f07c517cbc8c6c96737ff55e75c3bdf1..35fe42c6b79f29820fbeba274afec3ae02ef2edf 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -63,8 +63,8 @@ _FORCE_ENVIRON_FOR_WRAPPERS = {
 }
 
 _POLLING_STRATEGIES = {
-  'linux': ['epollsig', 'poll', 'poll-cv'],
-# TODO(ctiller, sreecha): enable epoll1, epollex, epoll-thread-pool
+  'linux': ['epollex', 'epollsig', 'poll', 'poll-cv'],
+# TODO(ctiller, sreecha): enable epoll1, epoll-thread-pool
   'mac': ['poll'],
 }