diff --git a/doc/cpp/perf_notes.md b/doc/cpp/perf_notes.md
new file mode 100644
index 0000000000000000000000000000000000000000..c87557558dd0fc81d2839e92312c0fc817d0f257
--- /dev/null
+++ b/doc/cpp/perf_notes.md
@@ -0,0 +1,29 @@
+# C++ Performance Notes
+
+## Streaming write buffering
+
+Generally, each write operation (Write(), WritesDone()) implies a syscall.
+gRPC will try to batch together separate write operations from different
+threads, but currently cannot automatically infer batching in a single stream.
+
+If message k+1 in a stream does not rely on responses from message k, it's
+possible to enable write batching by passing a WriteOptions argument to Write
+with the buffer_hint set:
+
+~~~{.cpp}
+stream_writer->Write(message, WriteOptions().set_buffer_hint());
+~~~
+
+The write will be buffered until one of the following is true:
+- the per-stream buffer is filled (controllable with the channel argument
+  GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE) - this prevents infinite buffering leading
+  to OOM
+- a subsequent Write without buffer_hint set is posted
+- the call is finished for writing (WritesDone() called on the client,
+  or Finish() called on an async server stream, or the service handler returns
+  for a sync server stream)
+
+## Completion Queues and Threading in the Async API
+
+Right now, the best performance trade-off is having numcpu's threads and one
+completion queue per thread.
diff --git a/doc/interop-test-descriptions.md b/doc/interop-test-descriptions.md
index 96d1f2bd7526c3421e867ed94ba9986a10f2b358..da3b97674438b5f36d0b3fe6512f5b0f70a4d156 100644
--- a/doc/interop-test-descriptions.md
+++ b/doc/interop-test-descriptions.md
@@ -716,7 +716,9 @@ Procedure:
 
     ```
     {
-      response_size: 314159
+      response_parameters:{
+        size: 314159
+      }
       payload:{
         body: 271828 bytes of zeros
       }
diff --git a/include/grpc++/grpc++.h b/include/grpc++/grpc++.h
index 36d65d6ee1eb691098d8019feecde9bc11768cba..daecfea40696600a89958bbc89cb9c206967a1c2 100644
--- a/include/grpc++/grpc++.h
+++ b/include/grpc++/grpc++.h
@@ -44,6 +44,10 @@
 /// peer, compression settings, authentication, etc.
 /// - grpc::Server, representing a gRPC server, created by grpc::ServerBuilder.
 ///
+/// Streaming calls are handled with the streaming classes in
+/// \ref sync_stream.h and
+/// \ref async_stream.h.
+///
 /// Refer to the
 /// [examples](https://github.com/grpc/grpc/blob/master/examples/cpp)
 /// for code putting these pieces into play.
diff --git a/include/grpc++/impl/codegen/completion_queue.h b/include/grpc++/impl/codegen/completion_queue.h
index 944f2c39198635d444c4c58af873925437adf459..03cecdc21c61e0c51b9fceacc38e1715b3c78c21 100644
--- a/include/grpc++/impl/codegen/completion_queue.h
+++ b/include/grpc++/impl/codegen/completion_queue.h
@@ -94,8 +94,10 @@ class ServerContext;
 
 extern CoreCodegenInterface* g_core_codegen_interface;
 
-/// A thin wrapper around \a grpc_completion_queue (see / \a
-/// src/core/surface/completion_queue.h).
+/// A thin wrapper around \ref grpc_completion_queue (see \ref
+/// src/core/lib/surface/completion_queue.h).
+/// See \ref doc/cpp/perf_notes.md for notes on best practices for high
+/// performance servers.
 class CompletionQueue : private GrpcLibraryCodegen {
  public:
   /// Default constructor. Implicitly creates a \a grpc_completion_queue
diff --git a/src/core/ext/README.md b/src/core/ext/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..0812b20823c55edd47657e8e37356097ec4c37ce
--- /dev/null
+++ b/src/core/ext/README.md
@@ -0,0 +1,5 @@
+Optional plugins for gRPC Core: Modules in this directory extend gRPC Core in
+useful ways. All optional code belongs here.
+
+NOTE: The movement of code between lib and ext is an ongoing effort, so this
+directory currently contains too much of the core library.
diff --git a/src/core/ext/client_channel/channel_connectivity.c b/src/core/ext/client_channel/channel_connectivity.c
index b10f444b63fb7a58d15b8b741d5206bbbec6811f..dd70bc2c6c8997b075514f9641b37fa151daa6c1 100644
--- a/src/core/ext/client_channel/channel_connectivity.c
+++ b/src/core/ext/client_channel/channel_connectivity.c
@@ -76,6 +76,7 @@ typedef struct {
   gpr_mu mu;
   callback_phase phase;
   grpc_closure on_complete;
+  grpc_closure on_timeout;
   grpc_timer alarm;
   grpc_connectivity_state state;
   grpc_completion_queue *cq;
@@ -200,6 +201,8 @@ void grpc_channel_watch_connectivity_state(
   gpr_mu_init(&w->mu);
   grpc_closure_init(&w->on_complete, watch_complete, w,
                     grpc_schedule_on_exec_ctx);
+  grpc_closure_init(&w->on_timeout, timeout_complete, w,
+                    grpc_schedule_on_exec_ctx);
   w->phase = WAITING;
   w->state = last_observed_state;
   w->cq = cq;
@@ -208,7 +211,7 @@ void grpc_channel_watch_connectivity_state(
 
   grpc_timer_init(&exec_ctx, &w->alarm,
                   gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  timeout_complete, w, gpr_now(GPR_CLOCK_MONOTONIC));
+                  &w->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC));
 
   if (client_channel_elem->filter == &grpc_client_channel_filter) {
     GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity");
diff --git a/src/core/ext/client_channel/subchannel.c b/src/core/ext/client_channel/subchannel.c
index f8f48f289422f920b26af6fbc4a2c157437f2dcf..54764ea7f87f3b7b10e4568b72636047e13c9732 100644
--- a/src/core/ext/client_channel/subchannel.c
+++ b/src/core/ext/client_channel/subchannel.c
@@ -109,6 +109,9 @@ struct grpc_subchannel {
   /** callback for connection finishing */
   grpc_closure connected;
 
+  /** callback for our alarm */
+  grpc_closure on_alarm;
+
   /** pollset_set tracking who's interested in a connection
       being setup */
   grpc_pollset_set *pollset_set;
@@ -483,7 +486,8 @@ static void maybe_start_connecting_locked(grpc_exec_ctx *exec_ctx,
       gpr_log(GPR_INFO, "Retry in %" PRId64 ".%09d seconds",
               time_til_next.tv_sec, time_til_next.tv_nsec);
     }
-    grpc_timer_init(exec_ctx, &c->alarm, c->next_attempt, on_alarm, c, now);
+    grpc_closure_init(&c->on_alarm, on_alarm, c, grpc_schedule_on_exec_ctx);
+    grpc_timer_init(exec_ctx, &c->alarm, c->next_attempt, &c->on_alarm, now);
   }
 }
 
diff --git a/src/core/ext/lb_policy/grpclb/grpclb.c b/src/core/ext/lb_policy/grpclb/grpclb.c
index 390c6ff81711ad02b8f9bbdb653b6990ac030597..3cdd96e7ae9d0608d17264793e9eca7fdf33cc6c 100644
--- a/src/core/ext/lb_policy/grpclb/grpclb.c
+++ b/src/core/ext/lb_policy/grpclb/grpclb.c
@@ -327,6 +327,9 @@ typedef struct glb_lb_policy {
   /* A response from the LB server has been received. Process it */
   grpc_closure lb_on_response_received;
 
+  /* LB call retry timer callback. */
+  grpc_closure lb_on_call_retry;
+
   grpc_call *lb_call; /* streaming call to the LB server, */
 
   grpc_metadata_array lb_initial_metadata_recv; /* initial MD from LB server */
@@ -1368,8 +1371,10 @@ static void lb_on_server_status_received(grpc_exec_ctx *exec_ctx, void *arg,
       }
     }
     GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "grpclb_retry_timer");
+    grpc_closure_init(&glb_policy->lb_on_call_retry, lb_call_on_retry_timer,
+                      glb_policy, grpc_schedule_on_exec_ctx);
     grpc_timer_init(exec_ctx, &glb_policy->lb_call_retry_timer, next_try,
-                    lb_call_on_retry_timer, glb_policy, now);
+                    &glb_policy->lb_on_call_retry, now);
   }
   gpr_mu_unlock(&glb_policy->mu);
   GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
diff --git a/src/core/ext/resolver/README.md b/src/core/ext/resolver/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..b0e234e96a561460707c9afed27e80b92390862a
--- /dev/null
+++ b/src/core/ext/resolver/README.md
@@ -0,0 +1,4 @@
+# Resolver
+
+Implementations of various name resolution schemes.
+See the [naming spec](/doc/naming.md).
diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c
index 124b16bbc388ec90df50a67fdcba3fb9badc7972..bb2b01250729152f6eaa3a0ec6849f8a02dff98f 100644
--- a/src/core/ext/resolver/dns/native/dns_resolver.c
+++ b/src/core/ext/resolver/dns/native/dns_resolver.c
@@ -81,6 +81,7 @@ typedef struct {
   /** retry timer */
   bool have_retry_timer;
   grpc_timer retry_timer;
+  grpc_closure on_retry;
   /** retry backoff state */
   gpr_backoff backoff_state;
 
@@ -199,8 +200,9 @@ static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg,
     } else {
       gpr_log(GPR_DEBUG, "retrying immediately");
     }
-    grpc_timer_init(exec_ctx, &r->retry_timer, next_try, dns_on_retry_timer, r,
-                    now);
+    grpc_closure_init(&r->on_retry, dns_on_retry_timer, r,
+                      grpc_schedule_on_exec_ctx);
+    grpc_timer_init(exec_ctx, &r->retry_timer, next_try, &r->on_retry, now);
   }
   if (r->resolved_result != NULL) {
     grpc_channel_args_destroy(exec_ctx, r->resolved_result);
diff --git a/src/core/ext/transport/README.md b/src/core/ext/transport/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2290568784d5e403257ddb9ed3ddcfcc1868dc87
--- /dev/null
+++ b/src/core/ext/transport/README.md
@@ -0,0 +1 @@
+Transports for gRPC
diff --git a/src/core/ext/transport/chttp2/README.md b/src/core/ext/transport/chttp2/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..8880a47460a13ae10bc8a562cb0cf829d57998af
--- /dev/null
+++ b/src/core/ext/transport/chttp2/README.md
@@ -0,0 +1 @@
+CHTTP2 - gRPC's implementation of a HTTP2 based transport
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index 0f762dbceb2303f895d4701b206fc71fa68aebf9..1bf6dd4c285c47edcb53473ed381635855871cd1 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -47,6 +47,7 @@
 #include "src/core/ext/transport/chttp2/transport/http2_errors.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 #include "src/core/ext/transport/chttp2/transport/status_conversion.h"
+#include "src/core/ext/transport/chttp2/transport/varint.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/http/parser.h"
 #include "src/core/lib/iomgr/workqueue.h"
@@ -1677,8 +1678,9 @@ static void close_from_api(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
 
     if (optional_message != NULL) {
       size_t msg_len = strlen(optional_message);
-      GPR_ASSERT(msg_len < 127);
-      message_pfx = grpc_slice_malloc(15);
+      GPR_ASSERT(msg_len <= UINT32_MAX);
+      uint32_t msg_len_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)msg_len, 0);
+      message_pfx = grpc_slice_malloc(14 + msg_len_len);
       p = GRPC_SLICE_START_PTR(message_pfx);
       *p++ = 0x40;
       *p++ = 12; /* len(grpc-message) */
@@ -1694,7 +1696,9 @@ static void close_from_api(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
       *p++ = 'a';
       *p++ = 'g';
       *p++ = 'e';
-      *p++ = (uint8_t)msg_len;
+      GRPC_CHTTP2_WRITE_VARINT((uint32_t)msg_len, 0, 0, p,
+                               (uint32_t)msg_len_len);
+      p += msg_len_len;
       GPR_ASSERT(p == GRPC_SLICE_END_PTR(message_pfx));
       len += (uint32_t)GRPC_SLICE_LENGTH(message_pfx);
       len += (uint32_t)msg_len;
diff --git a/src/core/lib/README.md b/src/core/lib/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..69b6bce2d9ff5d1ee1b238dc68e11f09a1673c95
--- /dev/null
+++ b/src/core/lib/README.md
@@ -0,0 +1,6 @@
+Required elements of gRPC Core: Each module in this directory is required to
+build gRPC. If it's possible to envisage a configuration where code is not
+required, then that code belongs in ext/ instead.
+
+NOTE: The movement of code between lib and ext is an ongoing effort, so this
+directory currently contains too much of the core library.
diff --git a/src/core/lib/channel/README.md b/src/core/lib/channel/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2dfcfe6e662322fc13a246bab166a88925f85694
--- /dev/null
+++ b/src/core/lib/channel/README.md
@@ -0,0 +1,4 @@
+# Channel
+
+Provides channel/call stack implementation, and implementation of common filters
+for that implementation.
diff --git a/src/core/lib/channel/deadline_filter.c b/src/core/lib/channel/deadline_filter.c
index 8dd6d099e1d82c13ba19b6f40c6987533122c2c2..a45a4d4b8206787868a43a600c8502a31158fd93 100644
--- a/src/core/lib/channel/deadline_filter.c
+++ b/src/core/lib/channel/deadline_filter.c
@@ -83,8 +83,11 @@ static void start_timer_if_needed_locked(grpc_exec_ctx* exec_ctx,
     // Take a reference to the call stack, to be owned by the timer.
     GRPC_CALL_STACK_REF(deadline_state->call_stack, "deadline_timer");
     deadline_state->timer_pending = true;
-    grpc_timer_init(exec_ctx, &deadline_state->timer, deadline, timer_callback,
-                    elem, gpr_now(GPR_CLOCK_MONOTONIC));
+    grpc_closure_init(&deadline_state->timer_callback, timer_callback, elem,
+                      grpc_schedule_on_exec_ctx);
+    grpc_timer_init(exec_ctx, &deadline_state->timer, deadline,
+                    &deadline_state->timer_callback,
+                    gpr_now(GPR_CLOCK_MONOTONIC));
   }
 }
 static void start_timer_if_needed(grpc_exec_ctx* exec_ctx,
diff --git a/src/core/lib/channel/deadline_filter.h b/src/core/lib/channel/deadline_filter.h
index 716a8525653b8b76cb76227a64d41d5aabb16bfd..bd2b84f79e13a3fca4d03f65cd3aa1bd0173dfea 100644
--- a/src/core/lib/channel/deadline_filter.h
+++ b/src/core/lib/channel/deadline_filter.h
@@ -46,6 +46,7 @@ typedef struct grpc_deadline_state {
   bool timer_pending;
   // The deadline timer.
   grpc_timer timer;
+  grpc_closure timer_callback;
   // Closure to invoke when the call is complete.
   // We use this to cancel the timer.
   grpc_closure on_complete;
diff --git a/src/core/lib/channel/handshaker.c b/src/core/lib/channel/handshaker.c
index ff827527b3ec4137085e2aff670ce7d655bc6420..c052ca538526861df51bf56c5cd69cbeb41c7c72 100644
--- a/src/core/lib/channel/handshaker.c
+++ b/src/core/lib/channel/handshaker.c
@@ -86,6 +86,7 @@ struct grpc_handshake_manager {
   grpc_tcp_server_acceptor* acceptor;
   // Deadline timer across all handshakers.
   grpc_timer deadline_timer;
+  grpc_closure on_timeout;
   // The final callback and user_data to invoke after the last handshaker.
   grpc_closure on_handshake_done;
   void* user_data;
@@ -224,9 +225,11 @@ void grpc_handshake_manager_do_handshake(
                     grpc_schedule_on_exec_ctx);
   // Start deadline timer, which owns a ref.
   gpr_ref(&mgr->refs);
+  grpc_closure_init(&mgr->on_timeout, on_timeout, mgr,
+                    grpc_schedule_on_exec_ctx);
   grpc_timer_init(exec_ctx, &mgr->deadline_timer,
                   gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  on_timeout, mgr, gpr_now(GPR_CLOCK_MONOTONIC));
+                  &mgr->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC));
   // Start first handshaker, which also owns a ref.
   gpr_ref(&mgr->refs);
   bool done = call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_NONE);
diff --git a/src/core/lib/iomgr/README.md b/src/core/lib/iomgr/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..9b22b76cebf96637ea7dbc793e53a69ce870ef6c
--- /dev/null
+++ b/src/core/lib/iomgr/README.md
@@ -0,0 +1,6 @@
+# iomgr
+
+Platform abstractions for I/O (mostly network).
+
+Provides abstractions over TCP/UDP I/O, file loading, polling, and concurrency
+management for various operating systems.
diff --git a/src/core/lib/iomgr/tcp_client_posix.c b/src/core/lib/iomgr/tcp_client_posix.c
index c8237dc38f1c912817008e288cb304977de06132..9a77c92016b7076840cd485355604a4fcbd0ba8a 100644
--- a/src/core/lib/iomgr/tcp_client_posix.c
+++ b/src/core/lib/iomgr/tcp_client_posix.c
@@ -65,6 +65,7 @@ typedef struct {
   grpc_fd *fd;
   gpr_timespec deadline;
   grpc_timer alarm;
+  grpc_closure on_alarm;
   int refs;
   grpc_closure write_closure;
   grpc_pollset_set *interested_parties;
@@ -352,9 +353,10 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
   }
 
   gpr_mu_lock(&ac->mu);
+  grpc_closure_init(&ac->on_alarm, tc_on_alarm, ac, grpc_schedule_on_exec_ctx);
   grpc_timer_init(exec_ctx, &ac->alarm,
                   gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  tc_on_alarm, ac, gpr_now(GPR_CLOCK_MONOTONIC));
+                  &ac->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC));
   grpc_fd_notify_on_write(exec_ctx, ac->fd, &ac->write_closure);
   gpr_mu_unlock(&ac->mu);
 
diff --git a/src/core/lib/iomgr/tcp_client_uv.c b/src/core/lib/iomgr/tcp_client_uv.c
index ed0de50fc12abb3e85a8d6a18186e862c1688ec2..5225a5402b2c99dbcb76eb1d832f1edfcc457c08 100644
--- a/src/core/lib/iomgr/tcp_client_uv.c
+++ b/src/core/lib/iomgr/tcp_client_uv.c
@@ -49,6 +49,7 @@
 typedef struct grpc_uv_tcp_connect {
   uv_connect_t connect_req;
   grpc_timer alarm;
+  grpc_closure on_alarm;
   uv_tcp_t *tcp_handle;
   grpc_closure *closure;
   grpc_endpoint **endpoint;
@@ -148,9 +149,11 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
   uv_tcp_connect(&connect->connect_req, connect->tcp_handle,
                  (const struct sockaddr *)resolved_addr->addr,
                  uv_tc_on_connect);
+  grpc_closure_init(&connect->on_alarm, uv_tc_on_alarm, connect,
+                    grpc_schedule_on_exec_ctx);
   grpc_timer_init(exec_ctx, &connect->alarm,
                   gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  uv_tc_on_alarm, connect, gpr_now(GPR_CLOCK_MONOTONIC));
+                  &connect->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC));
 }
 
 // overridden by api_fuzzer.c
diff --git a/src/core/lib/iomgr/tcp_client_windows.c b/src/core/lib/iomgr/tcp_client_windows.c
index 275258ebb5d9a35b9538d6151f9adba93a3b9ff9..1e84ec3a1ec39d91cb6fd4929eaa33db04cfe732 100644
--- a/src/core/lib/iomgr/tcp_client_windows.c
+++ b/src/core/lib/iomgr/tcp_client_windows.c
@@ -58,6 +58,7 @@ typedef struct {
   grpc_winsocket *socket;
   gpr_timespec deadline;
   grpc_timer alarm;
+  grpc_closure on_alarm;
   char *addr_name;
   int refs;
   grpc_closure on_connect;
@@ -229,7 +230,8 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done,
   ac->resource_quota = resource_quota;
   grpc_closure_init(&ac->on_connect, on_connect, ac, grpc_schedule_on_exec_ctx);
 
-  grpc_timer_init(exec_ctx, &ac->alarm, deadline, on_alarm, ac,
+  grpc_closure_init(&ac->on_alarm, on_alarm, ac, grpc_schedule_on_exec_ctx);
+  grpc_timer_init(exec_ctx, &ac->alarm, deadline, &ac->on_alarm,
                   gpr_now(GPR_CLOCK_MONOTONIC));
   grpc_socket_notify_on_write(exec_ctx, socket, &ac->on_connect);
   return;
diff --git a/src/core/lib/iomgr/timer.h b/src/core/lib/iomgr/timer.h
index 20fe98c4a7d6588f8b187e8d28cc7ae44e67604f..d84a278b183d2c2dc95af3faa7ab053e84c661ca 100644
--- a/src/core/lib/iomgr/timer.h
+++ b/src/core/lib/iomgr/timer.h
@@ -49,15 +49,15 @@
 
 typedef struct grpc_timer grpc_timer;
 
-/* Initialize *timer. When expired or canceled, timer_cb will be called with
-   *timer_cb_arg and error set to indicate if it expired (GRPC_ERROR_NONE) or
-   was canceled (GRPC_ERROR_CANCELLED). timer_cb is guaranteed to be called
-   exactly once, and application code should check the error to determine
-   how it was invoked. The application callback is also responsible for
-   maintaining information about when to free up any user-level state. */
+/* Initialize *timer. When expired or canceled, closure will be called with
+   error set to indicate if it expired (GRPC_ERROR_NONE) or was canceled
+   (GRPC_ERROR_CANCELLED). timer_cb is guaranteed to be called exactly once, and
+   application code should check the error to determine how it was invoked. The
+   application callback is also responsible for maintaining information about
+   when to free up any user-level state. */
 void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
-                     gpr_timespec deadline, grpc_iomgr_cb_func timer_cb,
-                     void *timer_cb_arg, gpr_timespec now);
+                     gpr_timespec deadline, grpc_closure *closure,
+                     gpr_timespec now);
 
 /* Note that there is no timer destroy function. This is because the
    timer is a one-time occurrence with a guarantee that the callback will
diff --git a/src/core/lib/iomgr/timer_generic.c b/src/core/lib/iomgr/timer_generic.c
index ecd3b284dc2eddaf2a801d0368bc1d47ce4a048e..40c83514724c61daef6521ee66540891236bb151 100644
--- a/src/core/lib/iomgr/timer_generic.c
+++ b/src/core/lib/iomgr/timer_generic.c
@@ -178,28 +178,27 @@ static void note_deadline_change(shard_type *shard) {
 }
 
 void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
-                     gpr_timespec deadline, grpc_iomgr_cb_func timer_cb,
-                     void *timer_cb_arg, gpr_timespec now) {
+                     gpr_timespec deadline, grpc_closure *closure,
+                     gpr_timespec now) {
   int is_first_timer = 0;
   shard_type *shard = &g_shards[shard_idx(timer)];
   GPR_ASSERT(deadline.clock_type == g_clock_type);
   GPR_ASSERT(now.clock_type == g_clock_type);
-  grpc_closure_init(&timer->closure, timer_cb, timer_cb_arg,
-                    grpc_schedule_on_exec_ctx);
+  timer->closure = closure;
   timer->deadline = deadline;
   timer->triggered = 0;
 
   if (!g_initialized) {
     timer->triggered = 1;
     grpc_closure_sched(
-        exec_ctx, &timer->closure,
+        exec_ctx, timer->closure,
         GRPC_ERROR_CREATE("Attempt to create timer before initialization"));
     return;
   }
 
   if (gpr_time_cmp(deadline, now) <= 0) {
     timer->triggered = 1;
-    grpc_closure_sched(exec_ctx, &timer->closure, GRPC_ERROR_NONE);
+    grpc_closure_sched(exec_ctx, timer->closure, GRPC_ERROR_NONE);
     return;
   }
 
@@ -251,7 +250,7 @@ void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) {
   shard_type *shard = &g_shards[shard_idx(timer)];
   gpr_mu_lock(&shard->mu);
   if (!timer->triggered) {
-    grpc_closure_sched(exec_ctx, &timer->closure, GRPC_ERROR_CANCELLED);
+    grpc_closure_sched(exec_ctx, timer->closure, GRPC_ERROR_CANCELLED);
     timer->triggered = 1;
     if (timer->heap_index == INVALID_HEAP_INDEX) {
       list_remove(timer);
@@ -317,7 +316,7 @@ static size_t pop_timers(grpc_exec_ctx *exec_ctx, shard_type *shard,
   grpc_timer *timer;
   gpr_mu_lock(&shard->mu);
   while ((timer = pop_one(shard, now))) {
-    grpc_closure_sched(exec_ctx, &timer->closure, GRPC_ERROR_REF(error));
+    grpc_closure_sched(exec_ctx, timer->closure, GRPC_ERROR_REF(error));
     n++;
   }
   *new_min_deadline = compute_min_deadline(shard);
diff --git a/src/core/lib/iomgr/timer_generic.h b/src/core/lib/iomgr/timer_generic.h
index e4494adb5fe18941e313a9b20881217934a89590..9d901c7e68ff96a272f7d723f052926336cc7663 100644
--- a/src/core/lib/iomgr/timer_generic.h
+++ b/src/core/lib/iomgr/timer_generic.h
@@ -43,7 +43,7 @@ struct grpc_timer {
   int triggered;
   struct grpc_timer *next;
   struct grpc_timer *prev;
-  grpc_closure closure;
+  grpc_closure *closure;
 };
 
 #endif /* GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H */
diff --git a/src/core/lib/iomgr/timer_uv.c b/src/core/lib/iomgr/timer_uv.c
index 00b835ffb80240d2e9d4aeeadc0bd5dba5439ac2..fa2cdee9646afae7af224baa769e5665c6cf0a13 100644
--- a/src/core/lib/iomgr/timer_uv.c
+++ b/src/core/lib/iomgr/timer_uv.c
@@ -55,21 +55,20 @@ void run_expired_timer(uv_timer_t *handle) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   GPR_ASSERT(!timer->triggered);
   timer->triggered = 1;
-  grpc_closure_sched(&exec_ctx, &timer->closure, GRPC_ERROR_NONE);
+  grpc_closure_sched(&exec_ctx, timer->closure, GRPC_ERROR_NONE);
   stop_uv_timer(handle);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
 void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
-                     gpr_timespec deadline, grpc_iomgr_cb_func timer_cb,
-                     void *timer_cb_arg, gpr_timespec now) {
+                     gpr_timespec deadline, grpc_closure *closure,
+                     gpr_timespec now) {
   uint64_t timeout;
   uv_timer_t *uv_timer;
-  grpc_closure_init(&timer->closure, timer_cb, timer_cb_arg,
-                    grpc_schedule_on_exec_ctx);
+  timer->closure = closure;
   if (gpr_time_cmp(deadline, now) <= 0) {
     timer->triggered = 1;
-    grpc_closure_sched(exec_ctx, &timer->closure, GRPC_ERROR_NONE);
+    grpc_closure_sched(exec_ctx, timer->closure, GRPC_ERROR_NONE);
     return;
   }
   timer->triggered = 0;
@@ -84,7 +83,7 @@ void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
 void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) {
   if (!timer->triggered) {
     timer->triggered = 1;
-    grpc_closure_sched(exec_ctx, &timer->closure, GRPC_ERROR_CANCELLED);
+    grpc_closure_sched(exec_ctx, timer->closure, GRPC_ERROR_CANCELLED);
     stop_uv_timer((uv_timer_t *)timer->uv_timer);
   }
 }
diff --git a/src/core/lib/iomgr/timer_uv.h b/src/core/lib/iomgr/timer_uv.h
index 3de383ebd5010e79c2f74c28828b8f8d730966f2..13cf8bd4fa0f593876a314f4920711013d712970 100644
--- a/src/core/lib/iomgr/timer_uv.h
+++ b/src/core/lib/iomgr/timer_uv.h
@@ -37,7 +37,7 @@
 #include "src/core/lib/iomgr/exec_ctx.h"
 
 struct grpc_timer {
-  grpc_closure closure;
+  grpc_closure *closure;
   /* This is actually a uv_timer_t*, but we want to keep platform-specific
      types out of headers */
   void *uv_timer;
diff --git a/src/core/lib/surface/README.md b/src/core/lib/surface/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..74cbd7113150738e06d2d81fb10836176e308ec7
--- /dev/null
+++ b/src/core/lib/surface/README.md
@@ -0,0 +1,4 @@
+# Surface
+
+Surface provides the bulk of the gRPC Core public API, and translates it into
+calls against core components.
diff --git a/src/core/lib/surface/alarm.c b/src/core/lib/surface/alarm.c
index aa9d60ee6a899442572ce9362d4023a8343018c2..e71c0ebfc57c557854098774251af31560c925c2 100644
--- a/src/core/lib/surface/alarm.c
+++ b/src/core/lib/surface/alarm.c
@@ -38,6 +38,7 @@
 
 struct grpc_alarm {
   grpc_timer alarm;
+  grpc_closure on_alarm;
   grpc_cq_completion completion;
   /** completion queue where events about this alarm will be posted */
   grpc_completion_queue *cq;
@@ -64,9 +65,11 @@ grpc_alarm *grpc_alarm_create(grpc_completion_queue *cq, gpr_timespec deadline,
   alarm->tag = tag;
 
   grpc_cq_begin_op(cq, tag);
+  grpc_closure_init(&alarm->on_alarm, alarm_cb, alarm,
+                    grpc_schedule_on_exec_ctx);
   grpc_timer_init(&exec_ctx, &alarm->alarm,
                   gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  alarm_cb, alarm, gpr_now(GPR_CLOCK_MONOTONIC));
+                  &alarm->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC));
   grpc_exec_ctx_finish(&exec_ctx);
   return alarm;
 }
diff --git a/src/core/lib/transport/README.md b/src/core/lib/transport/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e7e135edeb134db20477618c05be90b672abe57b
--- /dev/null
+++ b/src/core/lib/transport/README.md
@@ -0,0 +1,7 @@
+# Transport
+
+Common implementation details for gRPC Transports.
+
+Transports multiplex messages across some single connection. In ext/ there are
+implementations atop [a custom http2 implementation](/src/core/ext/transport/chttp2/README.md)
+and atop [cronet](/src/core/ext/transport/cronet/README.md).
diff --git a/src/core/lib/tsi/README.md b/src/core/lib/tsi/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..3ca3c1ef38cc53080e2abf6e532203060ddf88fa
--- /dev/null
+++ b/src/core/lib/tsi/README.md
@@ -0,0 +1,2 @@
+# Transport Security Interface
+An abstraction library over crypto and auth modules (typically OpenSSL)
diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js
index 46ddecfb1f630711bffac1eac0240488784d6f57..75cfb41342faaa9cc50c91ee9d6b82f7a8540416 100644
--- a/src/node/interop/interop_client.js
+++ b/src/node/interop/interop_client.js
@@ -312,6 +312,9 @@ function customMetadata(client, done) {
     }
   };
   var streaming_arg = {
+    response_parameters: [
+     {size: 314159}
+    ],
     payload: {
       body: zeroBuffer(271828)
     }
diff --git a/src/php/tests/interop/interop_client.php b/src/php/tests/interop/interop_client.php
index d201915d668e2f05560561836b1fd7915d45b0e0..2acf5612c7e3b1b50d20686fec73d56add6782da 100755
--- a/src/php/tests/interop/interop_client.php
+++ b/src/php/tests/interop/interop_client.php
@@ -451,11 +451,22 @@ function customMetadata($stub)
 
     $streaming_request = new grpc\testing\StreamingOutputCallRequest();
     $streaming_request->setPayload($payload);
+    $response_parameters = new grpc\testing\ResponseParameters();
+    $response_parameters->setSize($response_len);
+    $streaming_request->getResponseParameters()[] = $response_parameters;
     $streaming_call->write($streaming_request);
     $streaming_call->writesDone();
+    $result = $streaming_call->read();
 
     hardAssertIfStatusOk($streaming_call->getStatus());
 
+    $streaming_initial_metadata = $streaming_call->getMetadata();
+    hardAssert(array_key_exists($ECHO_INITIAL_KEY, $streaming_initial_metadata),
+               'Initial metadata does not contain expected key');
+    hardAssert(
+        $streaming_initial_metadata[$ECHO_INITIAL_KEY][0] === $ECHO_INITIAL_VALUE,
+        'Incorrect initial metadata value');
+
     $streaming_trailing_metadata = $streaming_call->getTrailingMetadata();
     hardAssert(array_key_exists($ECHO_TRAILING_KEY,
                                 $streaming_trailing_metadata),
diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py
index 41e9163cd66465e29fd867c3e94a9aa542e87971..e8c6a99cb18843055fa987801be14433f52a37a1 100644
--- a/src/python/grpcio/grpc/_channel.py
+++ b/src/python/grpcio/grpc/_channel.py
@@ -32,6 +32,7 @@
 import sys
 import threading
 import time
+import logging
 
 import grpc
 from grpc import _common
@@ -197,7 +198,16 @@ def _consume_request_iterator(
   event_handler = _event_handler(state, call, None)
 
   def consume_request_iterator():
-    for request in request_iterator:
+    while True:
+      try:
+        request = next(request_iterator)
+      except StopIteration:
+        break
+      except Exception as e:
+        logging.exception("Exception iterating requests!")
+        call.cancel()
+        _abort(state, grpc.StatusCode.UNKNOWN, "Exception iterating requests!")
+        return
       serialized_request = _common.serialize(request, request_serializer)
       with state.condition:
         if state.code is None and not state.cancelled:
diff --git a/src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py b/src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py
index c7bfeaeb959c57d7f5c62324f3c06149a148be98..43d6c971b5292ca1430e7e5198b0d3ad0640fbc6 100644
--- a/src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py
+++ b/src/python/grpcio_tests/tests/reflection/_reflection_servicer_test.py
@@ -75,7 +75,7 @@ class ReflectionServicerTest(unittest.TestCase):
         file_by_filename='i-donut-exist'
       ),
     )
-    responses = tuple(self._stub.ServerReflectionInfo(requests))
+    responses = tuple(self._stub.ServerReflectionInfo(iter(requests)))
     expected_responses = (
       reflection_pb2.ServerReflectionResponse(
         valid_host='',
@@ -93,7 +93,7 @@ class ReflectionServicerTest(unittest.TestCase):
         )
       ),
     )
-    self.assertEqual(expected_responses, responses)
+    self.assertSequenceEqual(expected_responses, responses)
 
   def testFileBySymbol(self):
     requests = (
@@ -104,7 +104,7 @@ class ReflectionServicerTest(unittest.TestCase):
         file_containing_symbol='i.donut.exist.co.uk.org.net.me.name.foo'
       ),
     )
-    responses = tuple(self._stub.ServerReflectionInfo(requests))
+    responses = tuple(self._stub.ServerReflectionInfo(iter(requests)))
     expected_responses = (
       reflection_pb2.ServerReflectionResponse(
         valid_host='',
@@ -122,7 +122,7 @@ class ReflectionServicerTest(unittest.TestCase):
         )
       ),
     )
-    self.assertEqual(expected_responses, responses)
+    self.assertSequenceEqual(expected_responses, responses)
 
   @unittest.skip('TODO(atash): implement file-containing-extension reflection '
                  '(see https://github.com/google/protobuf/issues/2248)')
@@ -141,7 +141,7 @@ class ReflectionServicerTest(unittest.TestCase):
         ),
       ),
     )
-    responses = tuple(self._stub.ServerReflectionInfo(requests))
+    responses = tuple(self._stub.ServerReflectionInfo(iter(requests)))
     expected_responses = (
       reflection_pb2.ServerReflectionResponse(
         valid_host='',
@@ -159,7 +159,7 @@ class ReflectionServicerTest(unittest.TestCase):
         )
       ),
     )
-    self.assertEqual(expected_responses, responses)
+    self.assertSequenceEqual(expected_responses, responses)
 
   def testListServices(self):
     requests = (
@@ -167,7 +167,7 @@ class ReflectionServicerTest(unittest.TestCase):
         list_services='',
       ),
     )
-    responses = tuple(self._stub.ServerReflectionInfo(requests))
+    responses = tuple(self._stub.ServerReflectionInfo(iter(requests)))
     expected_responses = (
       reflection_pb2.ServerReflectionResponse(
         valid_host='',
@@ -179,7 +179,7 @@ class ReflectionServicerTest(unittest.TestCase):
         )
       ),
     )
-    self.assertEqual(expected_responses, responses)
+    self.assertSequenceEqual(expected_responses, responses)
 
 if __name__ == '__main__':
   unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json
index 0109ee2173db9699464402557b142a4e8bbc1492..70d965d3ca8e232ff58be15b82739f1aafe7d2ad 100644
--- a/src/python/grpcio_tests/tests/tests.json
+++ b/src/python/grpcio_tests/tests/tests.json
@@ -28,6 +28,7 @@
   "unit._empty_message_test.EmptyMessageTest",
   "unit._exit_test.ExitTest",
   "unit._invalid_metadata_test.InvalidMetadataTest",
+  "unit._invocation_defects_test.InvocationDefectsTest",
   "unit._metadata_code_details_test.MetadataCodeDetailsTest",
   "unit._metadata_test.MetadataTest",
   "unit._rpc_test.RPCTest",
diff --git a/src/python/grpcio_tests/tests/unit/_compression_test.py b/src/python/grpcio_tests/tests/unit/_compression_test.py
index 83b91094666cd5a0be5e879cf0f3309b622c4532..4d3f02e917551e0cca52e04153a677c4953ddace 100644
--- a/src/python/grpcio_tests/tests/unit/_compression_test.py
+++ b/src/python/grpcio_tests/tests/unit/_compression_test.py
@@ -125,7 +125,7 @@ class CompressionTest(unittest.TestCase):
     compressed_channel = grpc.insecure_channel('localhost:%d' % self._port,
         options=[('grpc.default_compression_algorithm', 1)])
     multi_callable = compressed_channel.stream_stream(_STREAM_STREAM)
-    call = multi_callable([request] * test_constants.STREAM_LENGTH)
+    call = multi_callable(iter([request] * test_constants.STREAM_LENGTH))
     for response in call:
       self.assertEqual(request, response)
 
diff --git a/src/python/grpcio_tests/tests/unit/_empty_message_test.py b/src/python/grpcio_tests/tests/unit/_empty_message_test.py
index 131f6e945253acae867894f34cedf1c6c60c5705..69f468927905a5d42e94c04e119760932e05dc78 100644
--- a/src/python/grpcio_tests/tests/unit/_empty_message_test.py
+++ b/src/python/grpcio_tests/tests/unit/_empty_message_test.py
@@ -123,12 +123,12 @@ class EmptyMessageTest(unittest.TestCase):
 
   def testStreamUnary(self):
     response = self._channel.stream_unary(_STREAM_UNARY)(
-        [_REQUEST] * test_constants.STREAM_LENGTH)
+        iter([_REQUEST] * test_constants.STREAM_LENGTH))
     self.assertEqual(_RESPONSE, response)
 
   def testStreamStream(self):
     response_iterator = self._channel.stream_stream(_STREAM_STREAM)(
-        [_REQUEST] * test_constants.STREAM_LENGTH)
+        iter([_REQUEST] * test_constants.STREAM_LENGTH))
     self.assertSequenceEqual(
         [_RESPONSE] * test_constants.STREAM_LENGTH, list(response_iterator))
 
diff --git a/src/python/grpcio_tests/tests/unit/_exit_scenarios.py b/src/python/grpcio_tests/tests/unit/_exit_scenarios.py
index b33802bf57294d7e45edbc5cf739cee43ad65b4d..777527137f0f29ba520570b9d1bb61a05d6d3a4c 100644
--- a/src/python/grpcio_tests/tests/unit/_exit_scenarios.py
+++ b/src/python/grpcio_tests/tests/unit/_exit_scenarios.py
@@ -240,7 +240,7 @@ if __name__ == '__main__':
       multi_callable = channel.stream_unary(method)
       future = multi_callable.future(infinite_request_iterator())
       result, call = multi_callable.with_call(
-          [REQUEST] * test_constants.STREAM_LENGTH)
+          iter([REQUEST] * test_constants.STREAM_LENGTH))
     elif (args.scenario == IN_FLIGHT_STREAM_STREAM_CALL or
           args.scenario == IN_FLIGHT_PARTIAL_STREAM_STREAM_CALL):
       multi_callable = channel.stream_stream(method)
diff --git a/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py b/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..4312679bb96640ee3d2d52c8c63661c7f9072720
--- /dev/null
+++ b/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py
@@ -0,0 +1,247 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import itertools
+import threading
+import unittest
+from concurrent import futures
+
+import grpc
+from grpc.framework.foundation import logging_pool
+
+from tests.unit.framework.common import test_constants
+from tests.unit.framework.common import test_control
+
+_SERIALIZE_REQUEST = lambda bytestring: bytestring * 2
+_DESERIALIZE_REQUEST = lambda bytestring: bytestring[len(bytestring) // 2:]
+_SERIALIZE_RESPONSE = lambda bytestring: bytestring * 3
+_DESERIALIZE_RESPONSE = lambda bytestring: bytestring[:len(bytestring) // 3]
+
+_UNARY_UNARY = '/test/UnaryUnary'
+_UNARY_STREAM = '/test/UnaryStream'
+_STREAM_UNARY = '/test/StreamUnary'
+_STREAM_STREAM = '/test/StreamStream'
+
+
+class _Callback(object):
+  def __init__(self):
+    self._condition = threading.Condition()
+    self._value = None
+    self._called = False
+
+  def __call__(self, value):
+    with self._condition:
+      self._value = value
+      self._called = True
+      self._condition.notify_all()
+
+  def value(self):
+    with self._condition:
+      while not self._called:
+        self._condition.wait()
+      return self._value
+
+
+class _Handler(object):
+  def __init__(self, control):
+    self._control = control
+
+  def handle_unary_unary(self, request, servicer_context):
+    self._control.control()
+    if servicer_context is not None:
+      servicer_context.set_trailing_metadata((('testkey', 'testvalue',),))
+    return request
+
+  def handle_unary_stream(self, request, servicer_context):
+    for _ in range(test_constants.STREAM_LENGTH):
+      self._control.control()
+      yield request
+    self._control.control()
+    if servicer_context is not None:
+      servicer_context.set_trailing_metadata((('testkey', 'testvalue',),))
+
+  def handle_stream_unary(self, request_iterator, servicer_context):
+    if servicer_context is not None:
+      servicer_context.invocation_metadata()
+    self._control.control()
+    response_elements = []
+    for request in request_iterator:
+      self._control.control()
+      response_elements.append(request)
+    self._control.control()
+    if servicer_context is not None:
+      servicer_context.set_trailing_metadata((('testkey', 'testvalue',),))
+    return b''.join(response_elements)
+
+  def handle_stream_stream(self, request_iterator, servicer_context):
+    self._control.control()
+    if servicer_context is not None:
+      servicer_context.set_trailing_metadata((('testkey', 'testvalue',),))
+    for request in request_iterator:
+      self._control.control()
+      yield request
+    self._control.control()
+
+
+class _MethodHandler(grpc.RpcMethodHandler):
+  def __init__(
+    self, request_streaming, response_streaming, request_deserializer,
+    response_serializer, unary_unary, unary_stream, stream_unary,
+    stream_stream):
+    self.request_streaming = request_streaming
+    self.response_streaming = response_streaming
+    self.request_deserializer = request_deserializer
+    self.response_serializer = response_serializer
+    self.unary_unary = unary_unary
+    self.unary_stream = unary_stream
+    self.stream_unary = stream_unary
+    self.stream_stream = stream_stream
+
+
+class _GenericHandler(grpc.GenericRpcHandler):
+  def __init__(self, handler):
+    self._handler = handler
+
+  def service(self, handler_call_details):
+    if handler_call_details.method == _UNARY_UNARY:
+      return _MethodHandler(
+        False, False, None, None, self._handler.handle_unary_unary, None,
+        None, None)
+    elif handler_call_details.method == _UNARY_STREAM:
+      return _MethodHandler(
+        False, True, _DESERIALIZE_REQUEST, _SERIALIZE_RESPONSE, None,
+        self._handler.handle_unary_stream, None, None)
+    elif handler_call_details.method == _STREAM_UNARY:
+      return _MethodHandler(
+        True, False, _DESERIALIZE_REQUEST, _SERIALIZE_RESPONSE, None, None,
+        self._handler.handle_stream_unary, None)
+    elif handler_call_details.method == _STREAM_STREAM:
+      return _MethodHandler(
+        True, True, None, None, None, None, None,
+        self._handler.handle_stream_stream)
+    else:
+      return None
+
+
+class FailAfterFewIterationsCounter(object):
+    def __init__(self, high, bytestring):
+        self._current = 0
+        self._high = high
+        self._bytestring = bytestring
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        if self._current >= self._high:
+            raise Exception("This is a deliberate failure in a unit test.")
+        else:
+            self._current += 1
+            return self._bytestring
+
+
+def _unary_unary_multi_callable(channel):
+  return channel.unary_unary(_UNARY_UNARY)
+
+
+def _unary_stream_multi_callable(channel):
+  return channel.unary_stream(
+    _UNARY_STREAM,
+    request_serializer=_SERIALIZE_REQUEST,
+    response_deserializer=_DESERIALIZE_RESPONSE)
+
+
+def _stream_unary_multi_callable(channel):
+  return channel.stream_unary(
+    _STREAM_UNARY,
+    request_serializer=_SERIALIZE_REQUEST,
+    response_deserializer=_DESERIALIZE_RESPONSE)
+
+
+def _stream_stream_multi_callable(channel):
+  return channel.stream_stream(_STREAM_STREAM)
+
+
+class InvocationDefectsTest(unittest.TestCase):
+  def setUp(self):
+    self._control = test_control.PauseFailControl()
+    self._handler = _Handler(self._control)
+    self._server_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY)
+
+    self._server = grpc.server(self._server_pool)
+    port = self._server.add_insecure_port('[::]:0')
+    self._server.add_generic_rpc_handlers((_GenericHandler(self._handler),))
+    self._server.start()
+
+    self._channel = grpc.insecure_channel('localhost:%d' % port)
+
+  def tearDown(self):
+    self._server.stop(0)
+
+  def testIterableStreamRequestBlockingUnaryResponse(self):
+    requests = [b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH)]
+    multi_callable = _stream_unary_multi_callable(self._channel)
+
+    with self.assertRaises(grpc.RpcError):
+      response = multi_callable(
+        requests,
+        metadata=(('test', 'IterableStreamRequestBlockingUnaryResponse'),))
+
+  def testIterableStreamRequestFutureUnaryResponse(self):
+    requests = [b'\x07\x08' for _ in range(test_constants.STREAM_LENGTH)]
+    multi_callable = _stream_unary_multi_callable(self._channel)
+    response_future = multi_callable.future(
+      requests,
+      metadata=(
+        ('test', 'IterableStreamRequestFutureUnaryResponse'),))
+
+    with self.assertRaises(grpc.RpcError):
+      response = response_future.result()
+
+  def testIterableStreamRequestStreamResponse(self):
+    requests = [b'\x77\x58' for _ in range(test_constants.STREAM_LENGTH)]
+    multi_callable = _stream_stream_multi_callable(self._channel)
+    response_iterator = multi_callable(
+      requests,
+      metadata=(('test', 'IterableStreamRequestStreamResponse'),))
+
+    with self.assertRaises(grpc.RpcError):
+      next(response_iterator)
+
+  def testIteratorStreamRequestStreamResponse(self):
+    requests_iterator = FailAfterFewIterationsCounter(
+      test_constants.STREAM_LENGTH // 2, b'\x07\x08')
+    multi_callable = _stream_stream_multi_callable(self._channel)
+    response_iterator = multi_callable(
+      requests_iterator,
+      metadata=(('test', 'IteratorStreamRequestStreamResponse'),))
+
+    with self.assertRaises(grpc.RpcError):
+      for _ in range(test_constants.STREAM_LENGTH // 2 + 1):
+        next(response_iterator)
diff --git a/src/python/grpcio_tests/tests/unit/_metadata_test.py b/src/python/grpcio_tests/tests/unit/_metadata_test.py
index da7347692998d8e0fa35b5a04db790e23cde8850..caba53ffcc9030e7b5cfb48e5df9b62e855acea0 100644
--- a/src/python/grpcio_tests/tests/unit/_metadata_test.py
+++ b/src/python/grpcio_tests/tests/unit/_metadata_test.py
@@ -193,7 +193,7 @@ class MetadataTest(unittest.TestCase):
   def testStreamUnary(self):
     multi_callable = self._channel.stream_unary(_STREAM_UNARY)
     unused_response, call = multi_callable.with_call(
-        [_REQUEST] * test_constants.STREAM_LENGTH,
+        iter([_REQUEST] * test_constants.STREAM_LENGTH),
         metadata=_CLIENT_METADATA)
     self.assertTrue(test_common.metadata_transmitted(
         _SERVER_INITIAL_METADATA, call.initial_metadata()))
@@ -202,7 +202,7 @@ class MetadataTest(unittest.TestCase):
 
   def testStreamStream(self):
     multi_callable = self._channel.stream_stream(_STREAM_STREAM)
-    call = multi_callable([_REQUEST] * test_constants.STREAM_LENGTH,
+    call = multi_callable(iter([_REQUEST] * test_constants.STREAM_LENGTH),
                           metadata=_CLIENT_METADATA)
     self.assertTrue(test_common.metadata_transmitted(
         _SERVER_INITIAL_METADATA, call.initial_metadata()))
diff --git a/templates/tools/dockerfile/test/bazel/Dockerfile.template b/templates/tools/dockerfile/test/bazel/Dockerfile.template
new file mode 100644
index 0000000000000000000000000000000000000000..82f90bef68153074618c64516ee187421a0095db
--- /dev/null
+++ b/templates/tools/dockerfile/test/bazel/Dockerfile.template
@@ -0,0 +1,48 @@
+%YAML 1.2
+--- |
+  # Copyright 2015, Google Inc.
+  # All rights reserved.
+  #
+  # Redistribution and use in source and binary forms, with or without
+  # modification, are permitted provided that the following conditions are
+  # met:
+  #
+  #     * Redistributions of source code must retain the above copyright
+  # notice, this list of conditions and the following disclaimer.
+  #     * Redistributions in binary form must reproduce the above
+  # copyright notice, this list of conditions and the following disclaimer
+  # in the documentation and/or other materials provided with the
+  # distribution.
+  #     * Neither the name of Google Inc. nor the names of its
+  # contributors may be used to endorse or promote products derived from
+  # this software without specific prior written permission.
+  #
+  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  
+  FROM ubuntu:15.10
+  
+  <%include file="../../apt_get_basic.include"/>
+
+  #========================
+  # Bazel installation
+  RUN apt-get install -y software-properties-common g++
+  RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" > /etc/apt/sources.list.d/bazel.list
+  RUN curl https://bazel.build/bazel-release.pub.gpg | apt-key add -
+  RUN apt-get -y update
+  RUN apt-get -y install bazel
+
+  RUN mkdir -p /var/local/jenkins
+  
+  # Define the default command.
+  CMD ["bash"]
+  
diff --git a/templates/tools/doxygen/Doxyfile.include b/templates/tools/doxygen/Doxyfile.include
index 8b0c528c1f68f5eee14b6609d5cab25c6fe0fc92..a22f07f33fbf23013ce9f63b2afefe95bfe9429d 100644
--- a/templates/tools/doxygen/Doxyfile.include
+++ b/templates/tools/doxygen/Doxyfile.include
@@ -8,7 +8,10 @@
 <%def name="gen_doxyfile(libnames, packagename, collection, internal)">
 <%
   import itertools
+  import glob
+  import os
   targets = []
+  docpackage = packagename.replace('+', 'p').lower()
   for libname in libnames:
     target = None
     for p in collection:
@@ -16,6 +19,11 @@
         target = p
     assert(target)
     targets.append(target)
+  srcdoc = []
+  for dirpath, dirnames, filenames in os.walk('src/%s' % docpackage):
+    for filename in filenames:
+      if os.path.splitext(filename)[1] == '.md':
+        srcdoc.append(os.path.join(dirpath, filename))
 %>
 # Doxyfile 1.8.9.1
 
@@ -778,13 +786,20 @@ WARN_LOGFILE           =
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = ${' \\\n'.join(
-                               itertools.chain.from_iterable(
-			           target.public_headers +
-				   ([]
-				    if not internal
-				    else target.headers + target.src)
-			       for target in targets))}
+INPUT                  = ${
+    ' \\\n'.join(
+        itertools.chain(
+            itertools.chain.from_iterable(
+    			      target.public_headers +
+    				    ([]
+    				     if not internal
+    				     else target.headers + target.src)
+    			      for target in targets),
+            glob.glob('doc/*.md'),
+            glob.glob('doc/%s/*.md' % docpackage),
+            [] if not internal else srcdoc)
+    )
+}
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/test/core/end2end/fuzzers/api_fuzzer.c b/test/core/end2end/fuzzers/api_fuzzer.c
index 9e32c13887ae16d6380e3dbef79a7b13365eaf2d..30d814b01bb8a95611f16211cd2f64cb4dd93815 100644
--- a/test/core/end2end/fuzzers/api_fuzzer.c
+++ b/test/core/end2end/fuzzers/api_fuzzer.c
@@ -408,10 +408,11 @@ void my_resolve_address(grpc_exec_ctx *exec_ctx, const char *addr,
   r->addr = gpr_strdup(addr);
   r->on_done = on_done;
   r->addrs = addresses;
-  grpc_timer_init(exec_ctx, &r->timer,
-                  gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
-                               gpr_time_from_seconds(1, GPR_TIMESPAN)),
-                  finish_resolve, r, gpr_now(GPR_CLOCK_MONOTONIC));
+  grpc_timer_init(
+      exec_ctx, &r->timer, gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                                        gpr_time_from_seconds(1, GPR_TIMESPAN)),
+      grpc_closure_create(finish_resolve, r, grpc_schedule_on_exec_ctx),
+      gpr_now(GPR_CLOCK_MONOTONIC));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -469,10 +470,11 @@ static void sched_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
   fc->closure = closure;
   fc->ep = ep;
   fc->deadline = deadline;
-  grpc_timer_init(exec_ctx, &fc->timer,
-                  gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
-                               gpr_time_from_millis(1, GPR_TIMESPAN)),
-                  do_connect, fc, gpr_now(GPR_CLOCK_MONOTONIC));
+  grpc_timer_init(
+      exec_ctx, &fc->timer, gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                                         gpr_time_from_millis(1, GPR_TIMESPAN)),
+      grpc_closure_create(do_connect, fc, grpc_schedule_on_exec_ctx),
+      gpr_now(GPR_CLOCK_MONOTONIC));
 }
 
 static void my_tcp_client_connect(grpc_exec_ctx *exec_ctx,
diff --git a/test/core/iomgr/timer_list_test.c b/test/core/iomgr/timer_list_test.c
index be8988ab752f2ee940e7517d6676e3464e18ec60..8d7ac3fdaa1be1548e61be90faf291ec159f4e8f 100644
--- a/test/core/iomgr/timer_list_test.c
+++ b/test/core/iomgr/timer_list_test.c
@@ -57,17 +57,20 @@ static void add_test(void) {
 
   /* 10 ms timers.  will expire in the current epoch */
   for (i = 0; i < 10; i++) {
-    grpc_timer_init(&exec_ctx, &timers[i],
-                    gpr_time_add(start, gpr_time_from_millis(10, GPR_TIMESPAN)),
-                    cb, (void *)(intptr_t)i, start);
+    grpc_timer_init(
+        &exec_ctx, &timers[i],
+        gpr_time_add(start, gpr_time_from_millis(10, GPR_TIMESPAN)),
+        grpc_closure_create(cb, (void *)(intptr_t)i, grpc_schedule_on_exec_ctx),
+        start);
   }
 
   /* 1010 ms timers.  will expire in the next epoch */
   for (i = 10; i < 20; i++) {
     grpc_timer_init(
         &exec_ctx, &timers[i],
-        gpr_time_add(start, gpr_time_from_millis(1010, GPR_TIMESPAN)), cb,
-        (void *)(intptr_t)i, start);
+        gpr_time_add(start, gpr_time_from_millis(1010, GPR_TIMESPAN)),
+        grpc_closure_create(cb, (void *)(intptr_t)i, grpc_schedule_on_exec_ctx),
+        start);
   }
 
   /* collect timers.  Only the first batch should be ready. */
@@ -125,16 +128,26 @@ void destruction_test(void) {
   grpc_timer_list_init(gpr_time_0(GPR_CLOCK_REALTIME));
   memset(cb_called, 0, sizeof(cb_called));
 
-  grpc_timer_init(&exec_ctx, &timers[0], tfm(100), cb, (void *)(intptr_t)0,
-                  gpr_time_0(GPR_CLOCK_REALTIME));
-  grpc_timer_init(&exec_ctx, &timers[1], tfm(3), cb, (void *)(intptr_t)1,
-                  gpr_time_0(GPR_CLOCK_REALTIME));
-  grpc_timer_init(&exec_ctx, &timers[2], tfm(100), cb, (void *)(intptr_t)2,
-                  gpr_time_0(GPR_CLOCK_REALTIME));
-  grpc_timer_init(&exec_ctx, &timers[3], tfm(3), cb, (void *)(intptr_t)3,
-                  gpr_time_0(GPR_CLOCK_REALTIME));
-  grpc_timer_init(&exec_ctx, &timers[4], tfm(1), cb, (void *)(intptr_t)4,
-                  gpr_time_0(GPR_CLOCK_REALTIME));
+  grpc_timer_init(
+      &exec_ctx, &timers[0], tfm(100),
+      grpc_closure_create(cb, (void *)(intptr_t)0, grpc_schedule_on_exec_ctx),
+      gpr_time_0(GPR_CLOCK_REALTIME));
+  grpc_timer_init(
+      &exec_ctx, &timers[1], tfm(3),
+      grpc_closure_create(cb, (void *)(intptr_t)1, grpc_schedule_on_exec_ctx),
+      gpr_time_0(GPR_CLOCK_REALTIME));
+  grpc_timer_init(
+      &exec_ctx, &timers[2], tfm(100),
+      grpc_closure_create(cb, (void *)(intptr_t)2, grpc_schedule_on_exec_ctx),
+      gpr_time_0(GPR_CLOCK_REALTIME));
+  grpc_timer_init(
+      &exec_ctx, &timers[3], tfm(3),
+      grpc_closure_create(cb, (void *)(intptr_t)3, grpc_schedule_on_exec_ctx),
+      gpr_time_0(GPR_CLOCK_REALTIME));
+  grpc_timer_init(
+      &exec_ctx, &timers[4], tfm(1),
+      grpc_closure_create(cb, (void *)(intptr_t)4, grpc_schedule_on_exec_ctx),
+      gpr_time_0(GPR_CLOCK_REALTIME));
   GPR_ASSERT(1 == grpc_timer_check(&exec_ctx, tfm(2), NULL));
   grpc_exec_ctx_finish(&exec_ctx);
   GPR_ASSERT(1 == cb_called[4][1]);
diff --git a/tools/dockerfile/test/bazel/Dockerfile b/tools/dockerfile/test/bazel/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..cc41384833796c76186f054d842cbea4f02a2d9a
--- /dev/null
+++ b/tools/dockerfile/test/bazel/Dockerfile
@@ -0,0 +1,78 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+FROM ubuntu:15.10
+
+# Install Git and basic packages.
+RUN apt-get update && apt-get install -y \
+  autoconf \
+  autotools-dev \
+  build-essential \
+  bzip2 \
+  ccache \
+  curl \
+  gcc \
+  gcc-multilib \
+  git \
+  golang \
+  gyp \
+  lcov \
+  libc6 \
+  libc6-dbg \
+  libc6-dev \
+  libgtest-dev \
+  libtool \
+  make \
+  perl \
+  strace \
+  python-dev \
+  python-setuptools \
+  python-yaml \
+  telnet \
+  unzip \
+  wget \
+  zip && apt-get clean
+
+#================
+# Build profiling
+RUN apt-get update && apt-get install -y time && apt-get clean
+
+
+#========================
+# Bazel installation
+RUN apt-get install -y software-properties-common g++
+RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" > /etc/apt/sources.list.d/bazel.list
+RUN curl https://bazel.build/bazel-release.pub.gpg | apt-key add -
+RUN apt-get -y update
+RUN apt-get -y install bazel
+
+RUN mkdir -p /var/local/jenkins
+
+# Define the default command.
+CMD ["bash"]
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index 0f3ebde666a73445be102ad3f7c9d8daf65cf0fb..2c16f6f24a02468e50f61c514b3726338ae7e11c 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -847,7 +847,35 @@ include/grpc/impl/codegen/slice.h \
 include/grpc/impl/codegen/sync.h \
 include/grpc/impl/codegen/sync_generic.h \
 include/grpc/impl/codegen/sync_posix.h \
-include/grpc/impl/codegen/sync_windows.h
+include/grpc/impl/codegen/sync_windows.h \
+doc/fail_fast.md \
+doc/compression.md \
+doc/environment_variables.md \
+doc/stress_test_framework.md \
+doc/PROTOCOL-WEB.md \
+doc/cpp-style-guide.md \
+doc/http-grpc-status-mapping.md \
+doc/wait-for-ready.md \
+doc/command_line_tool.md \
+doc/c-style-guide.md \
+doc/server_reflection_tutorial.md \
+doc/health-checking.md \
+doc/connection-backoff-interop-test-description.md \
+doc/epoll-polling-engine.md \
+doc/naming.md \
+doc/binary-logging.md \
+doc/connectivity-semantics-and-api.md \
+doc/connection-backoff.md \
+doc/compression_cookbook.md \
+doc/PROTOCOL-HTTP2.md \
+doc/load-balancing.md \
+doc/negative-http2-interop-test-descriptions.md \
+doc/server-reflection.md \
+doc/interop-test-descriptions.md \
+doc/statuscodes.md \
+doc/g_stands_for.md \
+doc/cpp/perf_notes.md \
+doc/cpp/pending_api_cleanups.md
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 73a4a090e19c380ac4f323c6c1eebd9da22d8c5b..89915d7b08e1e2d80c72c1d22a10918911470ddd 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -893,7 +893,36 @@ src/cpp/util/slice_cc.cc \
 src/cpp/util/status.cc \
 src/cpp/util/string_ref.cc \
 src/cpp/util/time_cc.cc \
-src/cpp/codegen/codegen_init.cc
+src/cpp/codegen/codegen_init.cc \
+doc/fail_fast.md \
+doc/compression.md \
+doc/environment_variables.md \
+doc/stress_test_framework.md \
+doc/PROTOCOL-WEB.md \
+doc/cpp-style-guide.md \
+doc/http-grpc-status-mapping.md \
+doc/wait-for-ready.md \
+doc/command_line_tool.md \
+doc/c-style-guide.md \
+doc/server_reflection_tutorial.md \
+doc/health-checking.md \
+doc/connection-backoff-interop-test-description.md \
+doc/epoll-polling-engine.md \
+doc/naming.md \
+doc/binary-logging.md \
+doc/connectivity-semantics-and-api.md \
+doc/connection-backoff.md \
+doc/compression_cookbook.md \
+doc/PROTOCOL-HTTP2.md \
+doc/load-balancing.md \
+doc/negative-http2-interop-test-descriptions.md \
+doc/server-reflection.md \
+doc/interop-test-descriptions.md \
+doc/statuscodes.md \
+doc/g_stands_for.md \
+doc/cpp/perf_notes.md \
+doc/cpp/pending_api_cleanups.md \
+src/cpp/README.md
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/tools/doxygen/Doxyfile.core b/tools/doxygen/Doxyfile.core
index 6b6034a93488cefbfa83355255a9d892b629549d..e7fc1dbc5739b184539d18a59dac7f704a9ebece 100644
--- a/tools/doxygen/Doxyfile.core
+++ b/tools/doxygen/Doxyfile.core
@@ -825,7 +825,34 @@ include/grpc/impl/codegen/slice.h \
 include/grpc/impl/codegen/sync.h \
 include/grpc/impl/codegen/sync_generic.h \
 include/grpc/impl/codegen/sync_posix.h \
-include/grpc/impl/codegen/sync_windows.h
+include/grpc/impl/codegen/sync_windows.h \
+doc/fail_fast.md \
+doc/compression.md \
+doc/environment_variables.md \
+doc/stress_test_framework.md \
+doc/PROTOCOL-WEB.md \
+doc/cpp-style-guide.md \
+doc/http-grpc-status-mapping.md \
+doc/wait-for-ready.md \
+doc/command_line_tool.md \
+doc/c-style-guide.md \
+doc/server_reflection_tutorial.md \
+doc/health-checking.md \
+doc/connection-backoff-interop-test-description.md \
+doc/epoll-polling-engine.md \
+doc/naming.md \
+doc/binary-logging.md \
+doc/connectivity-semantics-and-api.md \
+doc/connection-backoff.md \
+doc/compression_cookbook.md \
+doc/PROTOCOL-HTTP2.md \
+doc/load-balancing.md \
+doc/negative-http2-interop-test-descriptions.md \
+doc/server-reflection.md \
+doc/interop-test-descriptions.md \
+doc/statuscodes.md \
+doc/g_stands_for.md \
+doc/core/pending_api_cleanups.md
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 3dc8b6faec758eea5967488a8c80967f8363233a..34361a8eb3a06413c7b3417c2824336ea949c89a 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -1277,7 +1277,55 @@ src/core/lib/support/tls_pthread.c \
 src/core/lib/support/tmpfile_msys.c \
 src/core/lib/support/tmpfile_posix.c \
 src/core/lib/support/tmpfile_windows.c \
-src/core/lib/support/wrap_memcpy.c
+src/core/lib/support/wrap_memcpy.c \
+doc/fail_fast.md \
+doc/compression.md \
+doc/environment_variables.md \
+doc/stress_test_framework.md \
+doc/PROTOCOL-WEB.md \
+doc/cpp-style-guide.md \
+doc/http-grpc-status-mapping.md \
+doc/wait-for-ready.md \
+doc/command_line_tool.md \
+doc/c-style-guide.md \
+doc/server_reflection_tutorial.md \
+doc/health-checking.md \
+doc/connection-backoff-interop-test-description.md \
+doc/epoll-polling-engine.md \
+doc/naming.md \
+doc/binary-logging.md \
+doc/connectivity-semantics-and-api.md \
+doc/connection-backoff.md \
+doc/compression_cookbook.md \
+doc/PROTOCOL-HTTP2.md \
+doc/load-balancing.md \
+doc/negative-http2-interop-test-descriptions.md \
+doc/server-reflection.md \
+doc/interop-test-descriptions.md \
+doc/statuscodes.md \
+doc/g_stands_for.md \
+doc/core/pending_api_cleanups.md \
+src/core/README.md \
+src/core/ext/README.md \
+src/core/ext/transport/README.md \
+src/core/ext/transport/chttp2/README.md \
+src/core/ext/transport/chttp2/client/secure/README.md \
+src/core/ext/transport/chttp2/client/insecure/README.md \
+src/core/ext/transport/chttp2/transport/README.md \
+src/core/ext/transport/chttp2/server/secure/README.md \
+src/core/ext/transport/chttp2/server/insecure/README.md \
+src/core/ext/client_channel/README.md \
+src/core/ext/resolver/README.md \
+src/core/ext/resolver/sockaddr/README.md \
+src/core/ext/resolver/dns/native/README.md \
+src/core/ext/census/README.md \
+src/core/ext/census/gen/README.md \
+src/core/lib/README.md \
+src/core/lib/tsi/README.md \
+src/core/lib/channel/README.md \
+src/core/lib/transport/README.md \
+src/core/lib/iomgr/README.md \
+src/core/lib/surface/README.md
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/tools/jenkins/run_bazel_basic.sh b/tools/jenkins/run_bazel_basic.sh
new file mode 100755
index 0000000000000000000000000000000000000000..648bc791bdca2c47c1d47d523a595bfe9dd7a569
--- /dev/null
+++ b/tools/jenkins/run_bazel_basic.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+# Copyright 2017, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Test basic Bazel features
+#
+# NOTE: No empty lines should appear in this file before igncr is set!
+set -ex -o igncr || set -ex
+
+export DOCKERFILE_DIR=tools/dockerfile/test/bazel
+export DOCKER_RUN_SCRIPT=tools/jenkins/run_bazel_basic_in_docker.sh
+exec tools/run_tests/dockerize/build_and_run_docker.sh
diff --git a/tools/jenkins/run_bazel_basic_in_docker.sh b/tools/jenkins/run_bazel_basic_in_docker.sh
new file mode 100755
index 0000000000000000000000000000000000000000..51aaa90ff82e7244829917dc2bf97434efd1a087
--- /dev/null
+++ b/tools/jenkins/run_bazel_basic_in_docker.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# Copyright 2017, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Test basic Bazel features
+#
+# NOTE: No empty lines should appear in this file before igncr is set!
+set -ex -o igncr || set -ex
+
+mkdir -p /var/local/git
+git clone /var/local/jenkins/grpc /var/local/git/grpc
+(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
+&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
+${name}')
+cd /var/local/git/grpc
+bazel build --spawn_strategy=standalone --genrule_strategy=standalone :all test/...
diff --git a/tools/jenkins/run_bazel_full.sh b/tools/jenkins/run_bazel_full.sh
new file mode 100755
index 0000000000000000000000000000000000000000..53ed360c077722e31337e7ce497e7f3c5de87ca8
--- /dev/null
+++ b/tools/jenkins/run_bazel_full.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+# Copyright 2017, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Test full Bazel
+#
+# NOTE: No empty lines should appear in this file before igncr is set!
+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
+exec tools/run_tests/dockerize/build_and_run_docker.sh
diff --git a/tools/jenkins/run_bazel_full_in_docker.sh b/tools/jenkins/run_bazel_full_in_docker.sh
new file mode 100755
index 0000000000000000000000000000000000000000..19502f19b75ce447c7bb76bb3572a76678be00e6
--- /dev/null
+++ b/tools/jenkins/run_bazel_full_in_docker.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# Copyright 2017, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Test full Bazel
+#
+# NOTE: No empty lines should appear in this file before igncr is set!
+set -ex -o igncr || set -ex
+
+mkdir -p /var/local/git
+git clone /var/local/jenkins/grpc /var/local/git/grpc
+(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \
+&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \
+${name}')
+cd /var/local/git/grpc/test
+bazel test --spawn_strategy=standalone --genrule_strategy=standalone ...
diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py
index c14f18af818f169c2474ebe3d764c1852e7661c9..981e38b813dec01b52432ca54d6ed02fefbaea53 100755
--- a/tools/run_tests/run_interop_tests.py
+++ b/tools/run_tests/run_interop_tests.py
@@ -179,10 +179,10 @@ class JavaLanguage:
     return {}
 
   def unimplemented_test_cases(self):
-    return _SKIP_ADVANCED + _SKIP_COMPRESSION
+    return _SKIP_COMPRESSION
 
   def unimplemented_test_cases_server(self):
-    return _SKIP_ADVANCED + _SKIP_COMPRESSION
+    return _SKIP_COMPRESSION
 
   def __str__(self):
     return 'java'