Merge branch 'flowctlN' into flowctl+millis
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 2a4eacd..094e43e 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -3,5 +3,4 @@
 # repository as the source of truth for module ownership.
 /**/OWNERS @markdroth @nicolasnoble @ctiller
 /bazel/** @nicolasnoble @dgquintas @ctiller
-/src/compiler/cpp_generator.cc @vjpai
 /src/core/ext/filters/client_channel/** @markdroth @dgquintas @ctiller
diff --git a/BUILD b/BUILD
index 7b55e66..3102526 100644
--- a/BUILD
+++ b/BUILD
@@ -467,7 +467,6 @@
         "src/core/lib/support/arena.c",
         "src/core/lib/support/atm.c",
         "src/core/lib/support/avl.c",
-        "src/core/lib/support/backoff.c",
         "src/core/lib/support/cmdline.c",
         "src/core/lib/support/cpu_iphone.c",
         "src/core/lib/support/cpu_linux.c",
@@ -514,8 +513,7 @@
         "src/core/lib/support/atomic.h",
         "src/core/lib/support/atomic_with_atm.h",
         "src/core/lib/support/atomic_with_std.h",
-        "src/core/lib/support/backoff.h",
-        "src/core/lib/support/block_annotate.h",
+        "src/core/lib/iomgr/block_annotate.h",
         "src/core/lib/support/env.h",
         "src/core/lib/support/memory.h",
         "src/core/lib/support/mpscq.h",
@@ -574,6 +572,8 @@
         "src/core/lib/compression/compression.c",
         "src/core/lib/compression/message_compress.c",
         "src/core/lib/compression/stream_compression.c",
+        "src/core/lib/compression/stream_compression_gzip.c",
+        "src/core/lib/compression/stream_compression_identity.c",
         "src/core/lib/debug/stats.c",
         "src/core/lib/debug/stats_data.c",
         "src/core/lib/http/format_request.c",
@@ -692,6 +692,7 @@
         "src/core/lib/transport/timeout_encoding.c",
         "src/core/lib/transport/transport.c",
         "src/core/lib/transport/transport_op_string.c",
+        "src/core/lib/backoff/backoff.c",
     ],
     hdrs = [
         "src/core/lib/channel/channel_args.h",
@@ -705,6 +706,8 @@
         "src/core/lib/compression/algorithm_metadata.h",
         "src/core/lib/compression/message_compress.h",
         "src/core/lib/compression/stream_compression.h",
+        "src/core/lib/compression/stream_compression_gzip.h",
+        "src/core/lib/compression/stream_compression_identity.h",
         "src/core/lib/debug/stats.h",
         "src/core/lib/debug/stats_data.h",
         "src/core/lib/http/format_request.h",
@@ -809,6 +812,7 @@
         "src/core/lib/transport/timeout_encoding.h",
         "src/core/lib/transport/transport.h",
         "src/core/lib/transport/transport_impl.h",
+        "src/core/lib/backoff/backoff.h",
     ],
     external_deps = [
         "zlib",
@@ -989,6 +993,7 @@
     name = "grpc_codegen",
     language = "c",
     public_hdrs = [
+        "include/grpc/impl/codegen/byte_buffer.h",
         "include/grpc/impl/codegen/byte_buffer_reader.h",
         "include/grpc/impl/codegen/compression_types.h",
         "include/grpc/impl/codegen/connectivity_state.h",
@@ -1487,6 +1492,7 @@
     public_hdrs = [
         "include/grpc++/impl/codegen/async_stream.h",
         "include/grpc++/impl/codegen/async_unary_call.h",
+        "include/grpc++/impl/codegen/byte_buffer.h",
         "include/grpc++/impl/codegen/call.h",
         "include/grpc++/impl/codegen/call_hook.h",
         "include/grpc++/impl/codegen/channel_interface.h",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a5a7fad..83aac15 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -379,6 +379,7 @@
 add_dependencies(buildtests_c alloc_test)
 add_dependencies(buildtests_c alpn_test)
 add_dependencies(buildtests_c arena_test)
+add_dependencies(buildtests_c backoff_test)
 add_dependencies(buildtests_c bad_server_response_test)
 add_dependencies(buildtests_c bdp_estimator_test)
 add_dependencies(buildtests_c bin_decoder_test)
@@ -428,7 +429,6 @@
 add_dependencies(buildtests_c goaway_server_test)
 endif()
 add_dependencies(buildtests_c gpr_avl_test)
-add_dependencies(buildtests_c gpr_backoff_test)
 add_dependencies(buildtests_c gpr_cmdline_test)
 add_dependencies(buildtests_c gpr_cpu_test)
 add_dependencies(buildtests_c gpr_env_test)
@@ -786,7 +786,6 @@
   src/core/lib/support/arena.c
   src/core/lib/support/atm.c
   src/core/lib/support/avl.c
-  src/core/lib/support/backoff.c
   src/core/lib/support/cmdline.c
   src/core/lib/support/cpu_iphone.c
   src/core/lib/support/cpu_linux.c
@@ -955,6 +954,7 @@
 
 add_library(grpc
   src/core/lib/surface/init.c
+  src/core/lib/backoff/backoff.c
   src/core/lib/channel/channel_args.c
   src/core/lib/channel/channel_stack.c
   src/core/lib/channel/channel_stack_builder.c
@@ -965,6 +965,8 @@
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/compression/stream_compression.c
+  src/core/lib/compression/stream_compression_gzip.c
+  src/core/lib/compression/stream_compression_identity.c
   src/core/lib/debug/stats.c
   src/core/lib/debug/stats_data.c
   src/core/lib/http/format_request.c
@@ -1250,6 +1252,7 @@
 )
 
 foreach(_hdr
+  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
@@ -1303,6 +1306,7 @@
 
 add_library(grpc_cronet
   src/core/lib/surface/init.c
+  src/core/lib/backoff/backoff.c
   src/core/lib/channel/channel_args.c
   src/core/lib/channel/channel_stack.c
   src/core/lib/channel/channel_stack_builder.c
@@ -1313,6 +1317,8 @@
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/compression/stream_compression.c
+  src/core/lib/compression/stream_compression_gzip.c
+  src/core/lib/compression/stream_compression_identity.c
   src/core/lib/debug/stats.c
   src/core/lib/debug/stats_data.c
   src/core/lib/http/format_request.c
@@ -1556,6 +1562,7 @@
 )
 
 foreach(_hdr
+  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
@@ -1619,6 +1626,7 @@
   test/core/util/port_server_client.c
   test/core/util/slice_splitter.c
   test/core/util/trickle_endpoint.c
+  src/core/lib/backoff/backoff.c
   src/core/lib/channel/channel_args.c
   src/core/lib/channel/channel_stack.c
   src/core/lib/channel/channel_stack_builder.c
@@ -1629,6 +1637,8 @@
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/compression/stream_compression.c
+  src/core/lib/compression/stream_compression_gzip.c
+  src/core/lib/compression/stream_compression_identity.c
   src/core/lib/debug/stats.c
   src/core/lib/debug/stats_data.c
   src/core/lib/http/format_request.c
@@ -1832,6 +1842,7 @@
 )
 
 foreach(_hdr
+  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
@@ -1879,6 +1890,7 @@
   test/core/util/port_server_client.c
   test/core/util/slice_splitter.c
   test/core/util/trickle_endpoint.c
+  src/core/lib/backoff/backoff.c
   src/core/lib/channel/channel_args.c
   src/core/lib/channel/channel_stack.c
   src/core/lib/channel/channel_stack_builder.c
@@ -1889,6 +1901,8 @@
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/compression/stream_compression.c
+  src/core/lib/compression/stream_compression_gzip.c
+  src/core/lib/compression/stream_compression_identity.c
   src/core/lib/debug/stats.c
   src/core/lib/debug/stats_data.c
   src/core/lib/http/format_request.c
@@ -2092,6 +2106,7 @@
 )
 
 foreach(_hdr
+  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
@@ -2125,6 +2140,7 @@
 add_library(grpc_unsecure
   src/core/lib/surface/init.c
   src/core/lib/surface/init_unsecure.c
+  src/core/lib/backoff/backoff.c
   src/core/lib/channel/channel_args.c
   src/core/lib/channel/channel_stack.c
   src/core/lib/channel/channel_stack_builder.c
@@ -2135,6 +2151,8 @@
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/compression/stream_compression.c
+  src/core/lib/compression/stream_compression_gzip.c
+  src/core/lib/compression/stream_compression_identity.c
   src/core/lib/debug/stats.c
   src/core/lib/debug/stats_data.c
   src/core/lib/http/format_request.c
@@ -2387,6 +2405,7 @@
 )
 
 foreach(_hdr
+  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
@@ -2696,6 +2715,7 @@
   include/grpc/slice_buffer.h
   include/grpc/status.h
   include/grpc/support/workaround_list.h
+  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
@@ -2706,6 +2726,7 @@
   include/grpc/impl/codegen/status.h
   include/grpc++/impl/codegen/async_stream.h
   include/grpc++/impl/codegen/async_unary_call.h
+  include/grpc++/impl/codegen/byte_buffer.h
   include/grpc++/impl/codegen/call.h
   include/grpc++/impl/codegen/call_hook.h
   include/grpc++/impl/codegen/channel_interface.h
@@ -2877,6 +2898,7 @@
   src/core/ext/transport/chttp2/transport/stream_map.c
   src/core/ext/transport/chttp2/transport/varint.c
   src/core/ext/transport/chttp2/transport/writing.c
+  src/core/lib/backoff/backoff.c
   src/core/lib/channel/channel_args.c
   src/core/lib/channel/channel_stack.c
   src/core/lib/channel/channel_stack_builder.c
@@ -2887,6 +2909,8 @@
   src/core/lib/compression/compression.c
   src/core/lib/compression/message_compress.c
   src/core/lib/compression/stream_compression.c
+  src/core/lib/compression/stream_compression_gzip.c
+  src/core/lib/compression/stream_compression_identity.c
   src/core/lib/debug/stats.c
   src/core/lib/debug/stats_data.c
   src/core/lib/http/format_request.c
@@ -3188,6 +3212,7 @@
   include/grpc/slice_buffer.h
   include/grpc/status.h
   include/grpc/support/workaround_list.h
+  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
@@ -3198,6 +3223,7 @@
   include/grpc/impl/codegen/status.h
   include/grpc++/impl/codegen/async_stream.h
   include/grpc++/impl/codegen/async_unary_call.h
+  include/grpc++/impl/codegen/byte_buffer.h
   include/grpc++/impl/codegen/call.h
   include/grpc++/impl/codegen/call_hook.h
   include/grpc++/impl/codegen/channel_interface.h
@@ -3558,6 +3584,7 @@
 foreach(_hdr
   include/grpc++/impl/codegen/async_stream.h
   include/grpc++/impl/codegen/async_unary_call.h
+  include/grpc++/impl/codegen/byte_buffer.h
   include/grpc++/impl/codegen/call.h
   include/grpc++/impl/codegen/call_hook.h
   include/grpc++/impl/codegen/channel_interface.h
@@ -3585,6 +3612,7 @@
   include/grpc++/impl/codegen/stub_options.h
   include/grpc++/impl/codegen/sync_stream.h
   include/grpc++/impl/codegen/time.h
+  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
@@ -3696,6 +3724,7 @@
 foreach(_hdr
   include/grpc++/impl/codegen/async_stream.h
   include/grpc++/impl/codegen/async_unary_call.h
+  include/grpc++/impl/codegen/byte_buffer.h
   include/grpc++/impl/codegen/call.h
   include/grpc++/impl/codegen/call_hook.h
   include/grpc++/impl/codegen/channel_interface.h
@@ -3723,6 +3752,7 @@
   include/grpc++/impl/codegen/stub_options.h
   include/grpc++/impl/codegen/sync_stream.h
   include/grpc++/impl/codegen/time.h
+  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
@@ -3925,6 +3955,7 @@
   include/grpc/slice_buffer.h
   include/grpc/status.h
   include/grpc/support/workaround_list.h
+  include/grpc/impl/codegen/byte_buffer.h
   include/grpc/impl/codegen/byte_buffer_reader.h
   include/grpc/impl/codegen/compression_types.h
   include/grpc/impl/codegen/connectivity_state.h
@@ -3935,6 +3966,7 @@
   include/grpc/impl/codegen/status.h
   include/grpc++/impl/codegen/async_stream.h
   include/grpc++/impl/codegen/async_unary_call.h
+  include/grpc++/impl/codegen/byte_buffer.h
   include/grpc++/impl/codegen/call.h
   include/grpc++/impl/codegen/call_hook.h
   include/grpc++/impl/codegen/channel_interface.h
@@ -5168,6 +5200,35 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
+add_executable(backoff_test
+  test/core/backoff/backoff_test.c
+)
+
+
+target_include_directories(backoff_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${BORINGSSL_ROOT_DIR}/include
+  PRIVATE ${PROTOBUF_ROOT_DIR}/src
+  PRIVATE ${BENCHMARK_ROOT_DIR}/include
+  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
+  PRIVATE ${CARES_INCLUDE_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
+)
+
+target_link_libraries(backoff_test
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_test_util
+  grpc
+  gpr_test_util
+  gpr
+)
+
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+
 add_executable(bad_server_response_test
   test/core/end2end/bad_server_response_test.c
 )
@@ -6268,33 +6329,6 @@
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
 
-add_executable(gpr_backoff_test
-  test/core/support/backoff_test.c
-)
-
-
-target_include_directories(gpr_backoff_test
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
-  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
-  PRIVATE ${BORINGSSL_ROOT_DIR}/include
-  PRIVATE ${PROTOBUF_ROOT_DIR}/src
-  PRIVATE ${BENCHMARK_ROOT_DIR}/include
-  PRIVATE ${ZLIB_ROOT_DIR}
-  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
-  PRIVATE ${CARES_INCLUDE_DIR}
-  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares
-  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
-)
-
-target_link_libraries(gpr_backoff_test
-  ${_gRPC_ALLTARGETS_LIBRARIES}
-  gpr_test_util
-  gpr
-)
-
-endif (gRPC_BUILD_TESTS)
-if (gRPC_BUILD_TESTS)
-
 add_executable(gpr_cmdline_test
   test/core/support/cmdline_test.c
 )
diff --git a/Makefile b/Makefile
index 61d6cae..27bb047 100644
--- a/Makefile
+++ b/Makefile
@@ -950,6 +950,7 @@
 alpn_test: $(BINDIR)/$(CONFIG)/alpn_test
 api_fuzzer: $(BINDIR)/$(CONFIG)/api_fuzzer
 arena_test: $(BINDIR)/$(CONFIG)/arena_test
+backoff_test: $(BINDIR)/$(CONFIG)/backoff_test
 bad_server_response_test: $(BINDIR)/$(CONFIG)/bad_server_response_test
 bdp_estimator_test: $(BINDIR)/$(CONFIG)/bdp_estimator_test
 bin_decoder_test: $(BINDIR)/$(CONFIG)/bin_decoder_test
@@ -988,7 +989,6 @@
 gen_percent_encoding_tables: $(BINDIR)/$(CONFIG)/gen_percent_encoding_tables
 goaway_server_test: $(BINDIR)/$(CONFIG)/goaway_server_test
 gpr_avl_test: $(BINDIR)/$(CONFIG)/gpr_avl_test
-gpr_backoff_test: $(BINDIR)/$(CONFIG)/gpr_backoff_test
 gpr_cmdline_test: $(BINDIR)/$(CONFIG)/gpr_cmdline_test
 gpr_cpu_test: $(BINDIR)/$(CONFIG)/gpr_cpu_test
 gpr_env_test: $(BINDIR)/$(CONFIG)/gpr_env_test
@@ -1350,6 +1350,7 @@
   $(BINDIR)/$(CONFIG)/alloc_test \
   $(BINDIR)/$(CONFIG)/alpn_test \
   $(BINDIR)/$(CONFIG)/arena_test \
+  $(BINDIR)/$(CONFIG)/backoff_test \
   $(BINDIR)/$(CONFIG)/bad_server_response_test \
   $(BINDIR)/$(CONFIG)/bdp_estimator_test \
   $(BINDIR)/$(CONFIG)/bin_decoder_test \
@@ -1383,7 +1384,6 @@
   $(BINDIR)/$(CONFIG)/fling_test \
   $(BINDIR)/$(CONFIG)/goaway_server_test \
   $(BINDIR)/$(CONFIG)/gpr_avl_test \
-  $(BINDIR)/$(CONFIG)/gpr_backoff_test \
   $(BINDIR)/$(CONFIG)/gpr_cmdline_test \
   $(BINDIR)/$(CONFIG)/gpr_cpu_test \
   $(BINDIR)/$(CONFIG)/gpr_env_test \
@@ -1761,6 +1761,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/alpn_test || ( echo test alpn_test failed ; exit 1 )
 	$(E) "[RUN]     Testing arena_test"
 	$(Q) $(BINDIR)/$(CONFIG)/arena_test || ( echo test arena_test failed ; exit 1 )
+	$(E) "[RUN]     Testing backoff_test"
+	$(Q) $(BINDIR)/$(CONFIG)/backoff_test || ( echo test backoff_test failed ; exit 1 )
 	$(E) "[RUN]     Testing bad_server_response_test"
 	$(Q) $(BINDIR)/$(CONFIG)/bad_server_response_test || ( echo test bad_server_response_test failed ; exit 1 )
 	$(E) "[RUN]     Testing bdp_estimator_test"
@@ -1823,8 +1825,6 @@
 	$(Q) $(BINDIR)/$(CONFIG)/goaway_server_test || ( echo test goaway_server_test failed ; exit 1 )
 	$(E) "[RUN]     Testing gpr_avl_test"
 	$(Q) $(BINDIR)/$(CONFIG)/gpr_avl_test || ( echo test gpr_avl_test failed ; exit 1 )
-	$(E) "[RUN]     Testing gpr_backoff_test"
-	$(Q) $(BINDIR)/$(CONFIG)/gpr_backoff_test || ( echo test gpr_backoff_test failed ; exit 1 )
 	$(E) "[RUN]     Testing gpr_cmdline_test"
 	$(Q) $(BINDIR)/$(CONFIG)/gpr_cmdline_test || ( echo test gpr_cmdline_test failed ; exit 1 )
 	$(E) "[RUN]     Testing gpr_cpu_test"
@@ -2800,7 +2800,6 @@
     src/core/lib/support/arena.c \
     src/core/lib/support/atm.c \
     src/core/lib/support/avl.c \
-    src/core/lib/support/backoff.c \
     src/core/lib/support/cmdline.c \
     src/core/lib/support/cpu_iphone.c \
     src/core/lib/support/cpu_linux.c \
@@ -2946,6 +2945,7 @@
 
 LIBGRPC_SRC = \
     src/core/lib/surface/init.c \
+    src/core/lib/backoff/backoff.c \
     src/core/lib/channel/channel_args.c \
     src/core/lib/channel/channel_stack.c \
     src/core/lib/channel/channel_stack_builder.c \
@@ -2956,6 +2956,8 @@
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/compression/stream_compression.c \
+    src/core/lib/compression/stream_compression_gzip.c \
+    src/core/lib/compression/stream_compression_identity.c \
     src/core/lib/debug/stats.c \
     src/core/lib/debug/stats_data.c \
     src/core/lib/http/format_request.c \
@@ -3206,6 +3208,7 @@
     src/core/plugin_registry/grpc_plugin_registry.c \
 
 PUBLIC_HEADERS_C += \
+    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
@@ -3294,6 +3297,7 @@
 
 LIBGRPC_CRONET_SRC = \
     src/core/lib/surface/init.c \
+    src/core/lib/backoff/backoff.c \
     src/core/lib/channel/channel_args.c \
     src/core/lib/channel/channel_stack.c \
     src/core/lib/channel/channel_stack_builder.c \
@@ -3304,6 +3308,8 @@
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/compression/stream_compression.c \
+    src/core/lib/compression/stream_compression_gzip.c \
+    src/core/lib/compression/stream_compression_identity.c \
     src/core/lib/debug/stats.c \
     src/core/lib/debug/stats_data.c \
     src/core/lib/http/format_request.c \
@@ -3512,6 +3518,7 @@
     src/core/plugin_registry/grpc_cronet_plugin_registry.c \
 
 PUBLIC_HEADERS_C += \
+    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
@@ -3609,6 +3616,7 @@
     test/core/util/port_server_client.c \
     test/core/util/slice_splitter.c \
     test/core/util/trickle_endpoint.c \
+    src/core/lib/backoff/backoff.c \
     src/core/lib/channel/channel_args.c \
     src/core/lib/channel/channel_stack.c \
     src/core/lib/channel/channel_stack_builder.c \
@@ -3619,6 +3627,8 @@
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/compression/stream_compression.c \
+    src/core/lib/compression/stream_compression_gzip.c \
+    src/core/lib/compression/stream_compression_identity.c \
     src/core/lib/debug/stats.c \
     src/core/lib/debug/stats_data.c \
     src/core/lib/http/format_request.c \
@@ -3789,6 +3799,7 @@
     src/core/ext/filters/http/server/http_server_filter.c \
 
 PUBLIC_HEADERS_C += \
+    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
@@ -3860,6 +3871,7 @@
     test/core/util/port_server_client.c \
     test/core/util/slice_splitter.c \
     test/core/util/trickle_endpoint.c \
+    src/core/lib/backoff/backoff.c \
     src/core/lib/channel/channel_args.c \
     src/core/lib/channel/channel_stack.c \
     src/core/lib/channel/channel_stack_builder.c \
@@ -3870,6 +3882,8 @@
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/compression/stream_compression.c \
+    src/core/lib/compression/stream_compression_gzip.c \
+    src/core/lib/compression/stream_compression_identity.c \
     src/core/lib/debug/stats.c \
     src/core/lib/debug/stats_data.c \
     src/core/lib/http/format_request.c \
@@ -4040,6 +4054,7 @@
     src/core/ext/filters/http/server/http_server_filter.c \
 
 PUBLIC_HEADERS_C += \
+    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
@@ -4084,6 +4099,7 @@
 LIBGRPC_UNSECURE_SRC = \
     src/core/lib/surface/init.c \
     src/core/lib/surface/init_unsecure.c \
+    src/core/lib/backoff/backoff.c \
     src/core/lib/channel/channel_args.c \
     src/core/lib/channel/channel_stack.c \
     src/core/lib/channel/channel_stack_builder.c \
@@ -4094,6 +4110,8 @@
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/compression/stream_compression.c \
+    src/core/lib/compression/stream_compression_gzip.c \
+    src/core/lib/compression/stream_compression_identity.c \
     src/core/lib/debug/stats.c \
     src/core/lib/debug/stats_data.c \
     src/core/lib/http/format_request.c \
@@ -4312,6 +4330,7 @@
     src/core/plugin_registry/grpc_unsecure_plugin_registry.c \
 
 PUBLIC_HEADERS_C += \
+    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
@@ -4600,6 +4619,7 @@
     include/grpc/slice_buffer.h \
     include/grpc/status.h \
     include/grpc/support/workaround_list.h \
+    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
@@ -4610,6 +4630,7 @@
     include/grpc/impl/codegen/status.h \
     include/grpc++/impl/codegen/async_stream.h \
     include/grpc++/impl/codegen/async_unary_call.h \
+    include/grpc++/impl/codegen/byte_buffer.h \
     include/grpc++/impl/codegen/call.h \
     include/grpc++/impl/codegen/call_hook.h \
     include/grpc++/impl/codegen/channel_interface.h \
@@ -4819,6 +4840,7 @@
     src/core/ext/transport/chttp2/transport/stream_map.c \
     src/core/ext/transport/chttp2/transport/varint.c \
     src/core/ext/transport/chttp2/transport/writing.c \
+    src/core/lib/backoff/backoff.c \
     src/core/lib/channel/channel_args.c \
     src/core/lib/channel/channel_stack.c \
     src/core/lib/channel/channel_stack_builder.c \
@@ -4829,6 +4851,8 @@
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/compression/stream_compression.c \
+    src/core/lib/compression/stream_compression_gzip.c \
+    src/core/lib/compression/stream_compression_identity.c \
     src/core/lib/debug/stats.c \
     src/core/lib/debug/stats_data.c \
     src/core/lib/http/format_request.c \
@@ -5093,6 +5117,7 @@
     include/grpc/slice_buffer.h \
     include/grpc/status.h \
     include/grpc/support/workaround_list.h \
+    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
@@ -5103,6 +5128,7 @@
     include/grpc/impl/codegen/status.h \
     include/grpc++/impl/codegen/async_stream.h \
     include/grpc++/impl/codegen/async_unary_call.h \
+    include/grpc++/impl/codegen/byte_buffer.h \
     include/grpc++/impl/codegen/call.h \
     include/grpc++/impl/codegen/call_hook.h \
     include/grpc++/impl/codegen/channel_interface.h \
@@ -5456,6 +5482,7 @@
 PUBLIC_HEADERS_CXX += \
     include/grpc++/impl/codegen/async_stream.h \
     include/grpc++/impl/codegen/async_unary_call.h \
+    include/grpc++/impl/codegen/byte_buffer.h \
     include/grpc++/impl/codegen/call.h \
     include/grpc++/impl/codegen/call_hook.h \
     include/grpc++/impl/codegen/channel_interface.h \
@@ -5483,6 +5510,7 @@
     include/grpc++/impl/codegen/stub_options.h \
     include/grpc++/impl/codegen/sync_stream.h \
     include/grpc++/impl/codegen/time.h \
+    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
@@ -5571,6 +5599,7 @@
 PUBLIC_HEADERS_CXX += \
     include/grpc++/impl/codegen/async_stream.h \
     include/grpc++/impl/codegen/async_unary_call.h \
+    include/grpc++/impl/codegen/byte_buffer.h \
     include/grpc++/impl/codegen/call.h \
     include/grpc++/impl/codegen/call_hook.h \
     include/grpc++/impl/codegen/channel_interface.h \
@@ -5598,6 +5627,7 @@
     include/grpc++/impl/codegen/stub_options.h \
     include/grpc++/impl/codegen/sync_stream.h \
     include/grpc++/impl/codegen/time.h \
+    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
@@ -5805,6 +5835,7 @@
     include/grpc/slice_buffer.h \
     include/grpc/status.h \
     include/grpc/support/workaround_list.h \
+    include/grpc/impl/codegen/byte_buffer.h \
     include/grpc/impl/codegen/byte_buffer_reader.h \
     include/grpc/impl/codegen/compression_types.h \
     include/grpc/impl/codegen/connectivity_state.h \
@@ -5815,6 +5846,7 @@
     include/grpc/impl/codegen/status.h \
     include/grpc++/impl/codegen/async_stream.h \
     include/grpc++/impl/codegen/async_unary_call.h \
+    include/grpc++/impl/codegen/byte_buffer.h \
     include/grpc++/impl/codegen/call.h \
     include/grpc++/impl/codegen/call_hook.h \
     include/grpc++/impl/codegen/channel_interface.h \
@@ -8865,6 +8897,38 @@
 endif
 
 
+BACKOFF_TEST_SRC = \
+    test/core/backoff/backoff_test.c \
+
+BACKOFF_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(BACKOFF_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/backoff_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/backoff_test: $(BACKOFF_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(BACKOFF_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/backoff_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/backoff/backoff_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_backoff_test: $(BACKOFF_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(BACKOFF_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 BAD_SERVER_RESPONSE_TEST_SRC = \
     test/core/end2end/bad_server_response_test.c \
 
@@ -10084,38 +10148,6 @@
 endif
 
 
-GPR_BACKOFF_TEST_SRC = \
-    test/core/support/backoff_test.c \
-
-GPR_BACKOFF_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_BACKOFF_TEST_SRC))))
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure targets if you don't have OpenSSL.
-
-$(BINDIR)/$(CONFIG)/gpr_backoff_test: openssl_dep_error
-
-else
-
-
-
-$(BINDIR)/$(CONFIG)/gpr_backoff_test: $(GPR_BACKOFF_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-	$(E) "[LD]      Linking $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(GPR_BACKOFF_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_backoff_test
-
-endif
-
-$(OBJDIR)/$(CONFIG)/test/core/support/backoff_test.o:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-
-deps_gpr_backoff_test: $(GPR_BACKOFF_TEST_OBJS:.o=.dep)
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(GPR_BACKOFF_TEST_OBJS:.o=.dep)
-endif
-endif
-
-
 GPR_CMDLINE_TEST_SRC = \
     test/core/support/cmdline_test.c \
 
diff --git a/binding.gyp b/binding.gyp
index 06dc731..17ea053 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -599,7 +599,6 @@
         'src/core/lib/support/arena.c',
         'src/core/lib/support/atm.c',
         'src/core/lib/support/avl.c',
-        'src/core/lib/support/backoff.c',
         'src/core/lib/support/cmdline.c',
         'src/core/lib/support/cpu_iphone.c',
         'src/core/lib/support/cpu_linux.c',
@@ -657,6 +656,7 @@
       ],
       'sources': [
         'src/core/lib/surface/init.c',
+        'src/core/lib/backoff/backoff.c',
         'src/core/lib/channel/channel_args.c',
         'src/core/lib/channel/channel_stack.c',
         'src/core/lib/channel/channel_stack_builder.c',
@@ -667,6 +667,8 @@
         'src/core/lib/compression/compression.c',
         'src/core/lib/compression/message_compress.c',
         'src/core/lib/compression/stream_compression.c',
+        'src/core/lib/compression/stream_compression_gzip.c',
+        'src/core/lib/compression/stream_compression_identity.c',
         'src/core/lib/debug/stats.c',
         'src/core/lib/debug/stats_data.c',
         'src/core/lib/http/format_request.c',
diff --git a/build.yaml b/build.yaml
index d15cf03..9fac2e1 100644
--- a/build.yaml
+++ b/build.yaml
@@ -66,7 +66,6 @@
   - src/core/lib/support/arena.c
   - src/core/lib/support/atm.c
   - src/core/lib/support/avl.c
-  - src/core/lib/support/backoff.c
   - src/core/lib/support/cmdline.c
   - src/core/lib/support/cpu_iphone.c
   - src/core/lib/support/cpu_linux.c
@@ -143,7 +142,6 @@
   - src/core/lib/support/atomic.h
   - src/core/lib/support/atomic_with_atm.h
   - src/core/lib/support/atomic_with_std.h
-  - src/core/lib/support/backoff.h
   - src/core/lib/support/block_annotate.h
   - src/core/lib/support/env.h
   - src/core/lib/support/memory.h
@@ -185,6 +183,7 @@
   - grpc++_codegen_base
 - name: grpc_base
   src:
+  - src/core/lib/backoff/backoff.c
   - src/core/lib/channel/channel_args.c
   - src/core/lib/channel/channel_stack.c
   - src/core/lib/channel/channel_stack_builder.c
@@ -195,6 +194,8 @@
   - src/core/lib/compression/compression.c
   - src/core/lib/compression/message_compress.c
   - src/core/lib/compression/stream_compression.c
+  - src/core/lib/compression/stream_compression_gzip.c
+  - src/core/lib/compression/stream_compression_identity.c
   - src/core/lib/debug/stats.c
   - src/core/lib/debug/stats_data.c
   - src/core/lib/http/format_request.c
@@ -335,6 +336,7 @@
   - include/grpc/status.h
   - include/grpc/support/workaround_list.h
   headers:
+  - src/core/lib/backoff/backoff.h
   - src/core/lib/channel/channel_args.h
   - src/core/lib/channel/channel_stack.h
   - src/core/lib/channel/channel_stack_builder.h
@@ -346,6 +348,8 @@
   - src/core/lib/compression/algorithm_metadata.h
   - src/core/lib/compression/message_compress.h
   - src/core/lib/compression/stream_compression.h
+  - src/core/lib/compression/stream_compression_gzip.h
+  - src/core/lib/compression/stream_compression_identity.h
   - src/core/lib/debug/stats.h
   - src/core/lib/debug/stats_data.h
   - src/core/lib/http/format_request.h
@@ -502,6 +506,7 @@
   - grpc_deadline_filter
 - name: grpc_codegen
   public_headers:
+  - include/grpc/impl/codegen/byte_buffer.h
   - include/grpc/impl/codegen/byte_buffer_reader.h
   - include/grpc/impl/codegen/compression_types.h
   - include/grpc/impl/codegen/connectivity_state.h
@@ -969,6 +974,7 @@
   public_headers:
   - include/grpc++/impl/codegen/async_stream.h
   - include/grpc++/impl/codegen/async_unary_call.h
+  - include/grpc++/impl/codegen/byte_buffer.h
   - include/grpc++/impl/codegen/call.h
   - include/grpc++/impl/codegen/call_hook.h
   - include/grpc++/impl/codegen/channel_interface.h
@@ -1768,6 +1774,16 @@
   deps:
   - gpr_test_util
   - gpr
+- name: backoff_test
+  build: test
+  language: c
+  src:
+  - test/core/backoff/backoff_test.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
 - name: bad_server_response_test
   build: test
   language: c
@@ -2196,14 +2212,6 @@
   deps:
   - gpr_test_util
   - gpr
-- name: gpr_backoff_test
-  build: test
-  language: c
-  src:
-  - test/core/support/backoff_test.c
-  deps:
-  - gpr_test_util
-  - gpr
 - name: gpr_cmdline_test
   build: test
   language: c
diff --git a/config.m4 b/config.m4
index 656a37e..46939a4 100644
--- a/config.m4
+++ b/config.m4
@@ -45,7 +45,6 @@
     src/core/lib/support/arena.c \
     src/core/lib/support/atm.c \
     src/core/lib/support/avl.c \
-    src/core/lib/support/backoff.c \
     src/core/lib/support/cmdline.c \
     src/core/lib/support/cpu_iphone.c \
     src/core/lib/support/cpu_linux.c \
@@ -86,6 +85,7 @@
     src/core/lib/support/tmpfile_windows.c \
     src/core/lib/support/wrap_memcpy.c \
     src/core/lib/surface/init.c \
+    src/core/lib/backoff/backoff.c \
     src/core/lib/channel/channel_args.c \
     src/core/lib/channel/channel_stack.c \
     src/core/lib/channel/channel_stack_builder.c \
@@ -96,6 +96,8 @@
     src/core/lib/compression/compression.c \
     src/core/lib/compression/message_compress.c \
     src/core/lib/compression/stream_compression.c \
+    src/core/lib/compression/stream_compression_gzip.c \
+    src/core/lib/compression/stream_compression_identity.c \
     src/core/lib/debug/stats.c \
     src/core/lib/debug/stats_data.c \
     src/core/lib/http/format_request.c \
@@ -684,6 +686,7 @@
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/server/secure)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/chttp2/transport)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/transport/inproc)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/backoff)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/channel)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/compression)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/debug)
diff --git a/config.w32 b/config.w32
index 92faad7..5bb973c 100644
--- a/config.w32
+++ b/config.w32
@@ -22,7 +22,6 @@
     "src\\core\\lib\\support\\arena.c " +
     "src\\core\\lib\\support\\atm.c " +
     "src\\core\\lib\\support\\avl.c " +
-    "src\\core\\lib\\support\\backoff.c " +
     "src\\core\\lib\\support\\cmdline.c " +
     "src\\core\\lib\\support\\cpu_iphone.c " +
     "src\\core\\lib\\support\\cpu_linux.c " +
@@ -63,6 +62,7 @@
     "src\\core\\lib\\support\\tmpfile_windows.c " +
     "src\\core\\lib\\support\\wrap_memcpy.c " +
     "src\\core\\lib\\surface\\init.c " +
+    "src\\core\\lib\\backoff\\backoff.c " +
     "src\\core\\lib\\channel\\channel_args.c " +
     "src\\core\\lib\\channel\\channel_stack.c " +
     "src\\core\\lib\\channel\\channel_stack_builder.c " +
@@ -73,6 +73,8 @@
     "src\\core\\lib\\compression\\compression.c " +
     "src\\core\\lib\\compression\\message_compress.c " +
     "src\\core\\lib\\compression\\stream_compression.c " +
+    "src\\core\\lib\\compression\\stream_compression_gzip.c " +
+    "src\\core\\lib\\compression\\stream_compression_identity.c " +
     "src\\core\\lib\\debug\\stats.c " +
     "src\\core\\lib\\debug\\stats_data.c " +
     "src\\core\\lib\\http\\format_request.c " +
@@ -697,6 +699,7 @@
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\transport\\chttp2\\transport");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\transport\\inproc");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib");
+  FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\backoff");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\channel");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\compression");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\debug");
diff --git a/doc/environment_variables.md b/doc/environment_variables.md
index f90f1d5..f775de1 100644
--- a/doc/environment_variables.md
+++ b/doc/environment_variables.md
@@ -58,6 +58,7 @@
     completion queue
   - round_robin - traces the round_robin load balancing policy
   - pick_first - traces the pick first load balancing policy
+  - plugin_credentials - traces plugin credentials
   - resource_quota - trace resource quota objects internals
   - glb - traces the grpclb load balancer
   - queue_pluck
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index 2f1f415..8b7bdfc 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -141,6 +141,7 @@
                       '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/byte_buffer.h',
                       'include/grpc/impl/codegen/byte_buffer_reader.h',
                       'include/grpc/impl/codegen/compression_types.h',
                       'include/grpc/impl/codegen/connectivity_state.h',
@@ -188,7 +189,6 @@
                       'src/core/lib/support/atomic.h',
                       'src/core/lib/support/atomic_with_atm.h',
                       'src/core/lib/support/atomic_with_std.h',
-                      'src/core/lib/support/backoff.h',
                       'src/core/lib/support/block_annotate.h',
                       'src/core/lib/support/env.h',
                       'src/core/lib/support/memory.h',
@@ -206,7 +206,6 @@
                       'src/core/lib/support/arena.c',
                       'src/core/lib/support/atm.c',
                       'src/core/lib/support/avl.c',
-                      'src/core/lib/support/backoff.c',
                       'src/core/lib/support/cmdline.c',
                       'src/core/lib/support/cpu_iphone.c',
                       'src/core/lib/support/cpu_linux.c',
@@ -318,6 +317,7 @@
                       'src/core/ext/filters/deadline/deadline_filter.h',
                       'src/core/ext/transport/chttp2/client/chttp2_connector.h',
                       'src/core/ext/transport/inproc/inproc_transport.h',
+                      'src/core/lib/backoff/backoff.h',
                       'src/core/lib/channel/channel_args.h',
                       'src/core/lib/channel/channel_stack.h',
                       'src/core/lib/channel/channel_stack_builder.h',
@@ -329,6 +329,8 @@
                       'src/core/lib/compression/algorithm_metadata.h',
                       'src/core/lib/compression/message_compress.h',
                       'src/core/lib/compression/stream_compression.h',
+                      'src/core/lib/compression/stream_compression_gzip.h',
+                      'src/core/lib/compression/stream_compression_identity.h',
                       'src/core/lib/debug/stats.h',
                       'src/core/lib/debug/stats_data.h',
                       'src/core/lib/http/format_request.h',
@@ -468,6 +470,7 @@
                       'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h',
                       'src/core/ext/filters/workarounds/workaround_utils.h',
                       'src/core/lib/surface/init.c',
+                      'src/core/lib/backoff/backoff.c',
                       'src/core/lib/channel/channel_args.c',
                       'src/core/lib/channel/channel_stack.c',
                       'src/core/lib/channel/channel_stack_builder.c',
@@ -478,6 +481,8 @@
                       'src/core/lib/compression/compression.c',
                       'src/core/lib/compression/message_compress.c',
                       'src/core/lib/compression/stream_compression.c',
+                      'src/core/lib/compression/stream_compression_gzip.c',
+                      'src/core/lib/compression/stream_compression_identity.c',
                       'src/core/lib/debug/stats.c',
                       'src/core/lib/debug/stats_data.c',
                       'src/core/lib/http/format_request.c',
@@ -729,7 +734,6 @@
                               'src/core/lib/support/atomic.h',
                               'src/core/lib/support/atomic_with_atm.h',
                               'src/core/lib/support/atomic_with_std.h',
-                              'src/core/lib/support/backoff.h',
                               'src/core/lib/support/block_annotate.h',
                               'src/core/lib/support/env.h',
                               'src/core/lib/support/memory.h',
@@ -813,6 +817,7 @@
                               'src/core/ext/filters/deadline/deadline_filter.h',
                               'src/core/ext/transport/chttp2/client/chttp2_connector.h',
                               'src/core/ext/transport/inproc/inproc_transport.h',
+                              'src/core/lib/backoff/backoff.h',
                               'src/core/lib/channel/channel_args.h',
                               'src/core/lib/channel/channel_stack.h',
                               'src/core/lib/channel/channel_stack_builder.h',
@@ -824,6 +829,8 @@
                               'src/core/lib/compression/algorithm_metadata.h',
                               'src/core/lib/compression/message_compress.h',
                               'src/core/lib/compression/stream_compression.h',
+                              'src/core/lib/compression/stream_compression_gzip.h',
+                              'src/core/lib/compression/stream_compression_identity.h',
                               'src/core/lib/debug/stats.h',
                               'src/core/lib/debug/stats_data.h',
                               'src/core/lib/http/format_request.h',
diff --git a/grpc.def b/grpc.def
index a7a1160..558be60 100644
--- a/grpc.def
+++ b/grpc.def
@@ -1,14 +1,4 @@
 EXPORTS
-    grpc_raw_byte_buffer_create
-    grpc_raw_compressed_byte_buffer_create
-    grpc_byte_buffer_copy
-    grpc_byte_buffer_length
-    grpc_byte_buffer_destroy
-    grpc_byte_buffer_reader_init
-    grpc_byte_buffer_reader_destroy
-    grpc_byte_buffer_reader_next
-    grpc_byte_buffer_reader_readall
-    grpc_raw_byte_buffer_from_reader
     census_initialize
     census_shutdown
     census_supported
@@ -145,6 +135,16 @@
     grpc_server_add_secure_http2_port
     grpc_call_set_credentials
     grpc_server_credentials_set_auth_metadata_processor
+    grpc_raw_byte_buffer_create
+    grpc_raw_compressed_byte_buffer_create
+    grpc_byte_buffer_copy
+    grpc_byte_buffer_length
+    grpc_byte_buffer_destroy
+    grpc_byte_buffer_reader_init
+    grpc_byte_buffer_reader_destroy
+    grpc_byte_buffer_reader_next
+    grpc_byte_buffer_reader_readall
+    grpc_raw_byte_buffer_from_reader
     grpc_slice_ref
     grpc_slice_unref
     grpc_slice_copy
diff --git a/grpc.gemspec b/grpc.gemspec
index 96323ae..26b123d 100644
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -88,7 +88,6 @@
   s.files += %w( src/core/lib/support/atomic.h )
   s.files += %w( src/core/lib/support/atomic_with_atm.h )
   s.files += %w( src/core/lib/support/atomic_with_std.h )
-  s.files += %w( src/core/lib/support/backoff.h )
   s.files += %w( src/core/lib/support/block_annotate.h )
   s.files += %w( src/core/lib/support/env.h )
   s.files += %w( src/core/lib/support/memory.h )
@@ -106,7 +105,6 @@
   s.files += %w( src/core/lib/support/arena.c )
   s.files += %w( src/core/lib/support/atm.c )
   s.files += %w( src/core/lib/support/avl.c )
-  s.files += %w( src/core/lib/support/backoff.c )
   s.files += %w( src/core/lib/support/cmdline.c )
   s.files += %w( src/core/lib/support/cpu_iphone.c )
   s.files += %w( src/core/lib/support/cpu_linux.c )
@@ -146,6 +144,7 @@
   s.files += %w( src/core/lib/support/tmpfile_posix.c )
   s.files += %w( src/core/lib/support/tmpfile_windows.c )
   s.files += %w( src/core/lib/support/wrap_memcpy.c )
+  s.files += %w( include/grpc/impl/codegen/byte_buffer.h )
   s.files += %w( include/grpc/impl/codegen/byte_buffer_reader.h )
   s.files += %w( include/grpc/impl/codegen/compression_types.h )
   s.files += %w( include/grpc/impl/codegen/connectivity_state.h )
@@ -251,6 +250,7 @@
   s.files += %w( src/core/ext/filters/deadline/deadline_filter.h )
   s.files += %w( src/core/ext/transport/chttp2/client/chttp2_connector.h )
   s.files += %w( src/core/ext/transport/inproc/inproc_transport.h )
+  s.files += %w( src/core/lib/backoff/backoff.h )
   s.files += %w( src/core/lib/channel/channel_args.h )
   s.files += %w( src/core/lib/channel/channel_stack.h )
   s.files += %w( src/core/lib/channel/channel_stack_builder.h )
@@ -262,6 +262,8 @@
   s.files += %w( src/core/lib/compression/algorithm_metadata.h )
   s.files += %w( src/core/lib/compression/message_compress.h )
   s.files += %w( src/core/lib/compression/stream_compression.h )
+  s.files += %w( src/core/lib/compression/stream_compression_gzip.h )
+  s.files += %w( src/core/lib/compression/stream_compression_identity.h )
   s.files += %w( src/core/lib/debug/stats.h )
   s.files += %w( src/core/lib/debug/stats_data.h )
   s.files += %w( src/core/lib/http/format_request.h )
@@ -405,6 +407,7 @@
   s.files += %w( src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h )
   s.files += %w( src/core/ext/filters/workarounds/workaround_utils.h )
   s.files += %w( src/core/lib/surface/init.c )
+  s.files += %w( src/core/lib/backoff/backoff.c )
   s.files += %w( src/core/lib/channel/channel_args.c )
   s.files += %w( src/core/lib/channel/channel_stack.c )
   s.files += %w( src/core/lib/channel/channel_stack_builder.c )
@@ -415,6 +418,8 @@
   s.files += %w( src/core/lib/compression/compression.c )
   s.files += %w( src/core/lib/compression/message_compress.c )
   s.files += %w( src/core/lib/compression/stream_compression.c )
+  s.files += %w( src/core/lib/compression/stream_compression_gzip.c )
+  s.files += %w( src/core/lib/compression/stream_compression_identity.c )
   s.files += %w( src/core/lib/debug/stats.c )
   s.files += %w( src/core/lib/debug/stats_data.c )
   s.files += %w( src/core/lib/http/format_request.c )
diff --git a/grpc.gyp b/grpc.gyp
index 48fb2ba..b591af4 100644
--- a/grpc.gyp
+++ b/grpc.gyp
@@ -163,7 +163,6 @@
         'src/core/lib/support/arena.c',
         'src/core/lib/support/atm.c',
         'src/core/lib/support/avl.c',
-        'src/core/lib/support/backoff.c',
         'src/core/lib/support/cmdline.c',
         'src/core/lib/support/cpu_iphone.c',
         'src/core/lib/support/cpu_linux.c',
@@ -223,6 +222,7 @@
       ],
       'sources': [
         'src/core/lib/surface/init.c',
+        'src/core/lib/backoff/backoff.c',
         'src/core/lib/channel/channel_args.c',
         'src/core/lib/channel/channel_stack.c',
         'src/core/lib/channel/channel_stack_builder.c',
@@ -233,6 +233,8 @@
         'src/core/lib/compression/compression.c',
         'src/core/lib/compression/message_compress.c',
         'src/core/lib/compression/stream_compression.c',
+        'src/core/lib/compression/stream_compression_gzip.c',
+        'src/core/lib/compression/stream_compression_identity.c',
         'src/core/lib/debug/stats.c',
         'src/core/lib/debug/stats_data.c',
         'src/core/lib/http/format_request.c',
@@ -522,6 +524,7 @@
         'test/core/util/port_server_client.c',
         'test/core/util/slice_splitter.c',
         'test/core/util/trickle_endpoint.c',
+        'src/core/lib/backoff/backoff.c',
         'src/core/lib/channel/channel_args.c',
         'src/core/lib/channel/channel_stack.c',
         'src/core/lib/channel/channel_stack_builder.c',
@@ -532,6 +535,8 @@
         'src/core/lib/compression/compression.c',
         'src/core/lib/compression/message_compress.c',
         'src/core/lib/compression/stream_compression.c',
+        'src/core/lib/compression/stream_compression_gzip.c',
+        'src/core/lib/compression/stream_compression_identity.c',
         'src/core/lib/debug/stats.c',
         'src/core/lib/debug/stats_data.c',
         'src/core/lib/http/format_request.c',
@@ -726,6 +731,7 @@
         'test/core/util/port_server_client.c',
         'test/core/util/slice_splitter.c',
         'test/core/util/trickle_endpoint.c',
+        'src/core/lib/backoff/backoff.c',
         'src/core/lib/channel/channel_args.c',
         'src/core/lib/channel/channel_stack.c',
         'src/core/lib/channel/channel_stack_builder.c',
@@ -736,6 +742,8 @@
         'src/core/lib/compression/compression.c',
         'src/core/lib/compression/message_compress.c',
         'src/core/lib/compression/stream_compression.c',
+        'src/core/lib/compression/stream_compression_gzip.c',
+        'src/core/lib/compression/stream_compression_identity.c',
         'src/core/lib/debug/stats.c',
         'src/core/lib/debug/stats_data.c',
         'src/core/lib/http/format_request.c',
@@ -915,6 +923,7 @@
       'sources': [
         'src/core/lib/surface/init.c',
         'src/core/lib/surface/init_unsecure.c',
+        'src/core/lib/backoff/backoff.c',
         'src/core/lib/channel/channel_args.c',
         'src/core/lib/channel/channel_stack.c',
         'src/core/lib/channel/channel_stack_builder.c',
@@ -925,6 +934,8 @@
         'src/core/lib/compression/compression.c',
         'src/core/lib/compression/message_compress.c',
         'src/core/lib/compression/stream_compression.c',
+        'src/core/lib/compression/stream_compression_gzip.c',
+        'src/core/lib/compression/stream_compression_identity.c',
         'src/core/lib/debug/stats.c',
         'src/core/lib/debug/stats_data.c',
         'src/core/lib/http/format_request.c',
diff --git a/include/grpc++/generic/generic_stub.h b/include/grpc++/generic/generic_stub.h
index 2b3ff59..d506431 100644
--- a/include/grpc++/generic/generic_stub.h
+++ b/include/grpc++/generic/generic_stub.h
@@ -20,6 +20,7 @@
 #define GRPCXX_GENERIC_GENERIC_STUB_H
 
 #include <grpc++/support/async_stream.h>
+#include <grpc++/support/async_unary_call.h>
 #include <grpc++/support/byte_buffer.h>
 
 namespace grpc {
@@ -27,6 +28,7 @@
 class CompletionQueue;
 typedef ClientAsyncReaderWriter<ByteBuffer, ByteBuffer>
     GenericClientAsyncReaderWriter;
+typedef ClientAsyncResponseReader<ByteBuffer> GenericClientAsyncResponseReader;
 
 /// Generic stubs provide a type-unsafe interface to call gRPC methods
 /// by name.
@@ -51,6 +53,14 @@
   std::unique_ptr<GenericClientAsyncReaderWriter> PrepareCall(
       ClientContext* context, const grpc::string& method, CompletionQueue* cq);
 
+  /// Setup a unary call to a named method \a method using \a context, and don't
+  /// start it. Let it be started explicitly with StartCall.
+  /// The return value only indicates whether or not registration of the call
+  /// succeeded (i.e. the call won't proceed if the return value is nullptr).
+  std::unique_ptr<GenericClientAsyncResponseReader> PrepareUnaryCall(
+      ClientContext* context, const grpc::string& method,
+      const ByteBuffer& request, CompletionQueue* cq);
+
  private:
   std::shared_ptr<ChannelInterface> channel_;
 };
diff --git a/include/grpc++/impl/codegen/byte_buffer.h b/include/grpc++/impl/codegen/byte_buffer.h
new file mode 100644
index 0000000..57d731b
--- /dev/null
+++ b/include/grpc++/impl/codegen/byte_buffer.h
@@ -0,0 +1,156 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPCXX_IMPL_CODEGEN_BYTE_BUFFER_H
+#define GRPCXX_IMPL_CODEGEN_BYTE_BUFFER_H
+
+#include <grpc/impl/codegen/byte_buffer.h>
+
+#include <grpc++/impl/codegen/config.h>
+#include <grpc++/impl/codegen/core_codegen_interface.h>
+#include <grpc++/impl/codegen/serialization_traits.h>
+#include <grpc++/impl/codegen/slice.h>
+#include <grpc++/impl/codegen/status.h>
+
+#include <vector>
+
+namespace grpc {
+
+template <class R>
+class CallOpRecvMessage;
+class MethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class RpcMethodHandler;
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler;
+namespace CallOpGenericRecvMessageHelper {
+template <class R>
+class DeserializeFuncType;
+}  // namespace CallOpGenericRecvMessageHelper
+
+/// A sequence of bytes.
+class ByteBuffer final {
+ public:
+  /// Constuct an empty buffer.
+  ByteBuffer() : buffer_(nullptr) {}
+
+  /// Construct buffer from \a slices, of which there are \a nslices.
+  ByteBuffer(const Slice* slices, size_t nslices);
+
+  /// Constuct a byte buffer by referencing elements of existing buffer
+  /// \a buf. Wrapper of core function grpc_byte_buffer_copy
+  ByteBuffer(const ByteBuffer& buf);
+
+  ~ByteBuffer() {
+    if (buffer_) {
+      g_core_codegen_interface->grpc_byte_buffer_destroy(buffer_);
+    }
+  }
+
+  ByteBuffer& operator=(const ByteBuffer&);
+
+  /// Dump (read) the buffer contents into \a slices.
+  Status Dump(std::vector<Slice>* slices) const;
+
+  /// Remove all data.
+  void Clear() {
+    if (buffer_) {
+      g_core_codegen_interface->grpc_byte_buffer_destroy(buffer_);
+      buffer_ = nullptr;
+    }
+  }
+
+  /// Make a duplicate copy of the internals of this byte
+  /// buffer so that we have our own owned version of it.
+  /// bbuf.Duplicate(); is equivalent to bbuf=bbuf; but is actually readable
+  void Duplicate() {
+    buffer_ = g_core_codegen_interface->grpc_byte_buffer_copy(buffer_);
+  }
+
+  /// Forget underlying byte buffer without destroying
+  /// Use this only for un-owned byte buffers
+  void Release() { buffer_ = nullptr; }
+
+  /// Buffer size in bytes.
+  size_t Length() const;
+
+  /// Swap the state of *this and *other.
+  void Swap(ByteBuffer* other);
+
+  /// Is this ByteBuffer valid?
+  bool Valid() const { return (buffer_ != nullptr); }
+
+ private:
+  friend class SerializationTraits<ByteBuffer, void>;
+  friend class CallOpSendMessage;
+  template <class R>
+  friend class CallOpRecvMessage;
+  friend class CallOpGenericRecvMessage;
+  friend class MethodHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class RpcMethodHandler;
+  template <class ServiceType, class RequestType, class ResponseType>
+  friend class ServerStreamingHandler;
+  template <class R>
+  friend class CallOpGenericRecvMessageHelper::DeserializeFuncType;
+
+  grpc_byte_buffer* buffer_;
+
+  // takes ownership
+  void set_buffer(grpc_byte_buffer* buf) {
+    if (buffer_) {
+      Clear();
+    }
+    buffer_ = buf;
+  }
+
+  grpc_byte_buffer* c_buffer() { return buffer_; }
+  grpc_byte_buffer** c_buffer_ptr() { return &buffer_; }
+
+  class ByteBufferPointer {
+   public:
+    ByteBufferPointer(const ByteBuffer* b)
+        : bbuf_(const_cast<ByteBuffer*>(b)) {}
+    operator ByteBuffer*() { return bbuf_; }
+    operator grpc_byte_buffer*() { return bbuf_->buffer_; }
+    operator grpc_byte_buffer**() { return &bbuf_->buffer_; }
+
+   private:
+    ByteBuffer* bbuf_;
+  };
+  ByteBufferPointer bbuf_ptr() const { return ByteBufferPointer(this); }
+};
+
+template <>
+class SerializationTraits<ByteBuffer, void> {
+ public:
+  static Status Deserialize(ByteBuffer* byte_buffer, ByteBuffer* dest) {
+    dest->set_buffer(byte_buffer->buffer_);
+    return Status::OK;
+  }
+  static Status Serialize(const ByteBuffer& source, ByteBuffer* buffer,
+                          bool* own_buffer) {
+    *buffer = source;
+    *own_buffer = true;
+    return Status::OK;
+  }
+};
+
+}  // namespace grpc
+
+#endif  // GRPCXX_IMPL_CODEGEN_BYTE_BUFFER_H
diff --git a/include/grpc++/impl/codegen/call.h b/include/grpc++/impl/codegen/call.h
index 74ed5cb..d9988e5 100644
--- a/include/grpc++/impl/codegen/call.h
+++ b/include/grpc++/impl/codegen/call.h
@@ -25,6 +25,7 @@
 #include <map>
 #include <memory>
 
+#include <grpc++/impl/codegen/byte_buffer.h>
 #include <grpc++/impl/codegen/call_hook.h>
 #include <grpc++/impl/codegen/client_context.h>
 #include <grpc++/impl/codegen/completion_queue_tag.h>
@@ -39,8 +40,6 @@
 #include <grpc/impl/codegen/compression_types.h>
 #include <grpc/impl/codegen/grpc_types.h>
 
-struct grpc_byte_buffer;
-
 namespace grpc {
 
 class ByteBuffer;
@@ -281,7 +280,7 @@
 
 class CallOpSendMessage {
  public:
-  CallOpSendMessage() : send_buf_(nullptr) {}
+  CallOpSendMessage() : send_buf_() {}
 
   /// Send \a message using \a options for the write. The \a options are cleared
   /// after use.
@@ -294,33 +293,35 @@
 
  protected:
   void AddOp(grpc_op* ops, size_t* nops) {
-    if (send_buf_ == nullptr) return;
+    if (!send_buf_.Valid()) return;
     grpc_op* op = &ops[(*nops)++];
     op->op = GRPC_OP_SEND_MESSAGE;
     op->flags = write_options_.flags();
     op->reserved = NULL;
-    op->data.send_message.send_message = send_buf_;
+    op->data.send_message.send_message = send_buf_.c_buffer();
     // Flags are per-message: clear them after use.
     write_options_.Clear();
   }
-  void FinishOp(bool* status) {
-    g_core_codegen_interface->grpc_byte_buffer_destroy(send_buf_);
-    send_buf_ = nullptr;
-  }
+  void FinishOp(bool* status) { send_buf_.Clear(); }
 
  private:
-  grpc_byte_buffer* send_buf_;
+  ByteBuffer send_buf_;
   WriteOptions write_options_;
 };
 
+namespace internal {
+template <class T>
+T Example();
+}  // namespace internal
+
 template <class M>
 Status CallOpSendMessage::SendMessage(const M& message, WriteOptions options) {
   write_options_ = options;
   bool own_buf;
-  Status result =
-      SerializationTraits<M>::Serialize(message, &send_buf_, &own_buf);
+  Status result = SerializationTraits<M>::Serialize(
+      message, send_buf_.bbuf_ptr(), &own_buf);
   if (!own_buf) {
-    send_buf_ = g_core_codegen_interface->grpc_byte_buffer_copy(send_buf_);
+    send_buf_.Duplicate();
   }
   return result;
 }
@@ -352,18 +353,20 @@
     op->op = GRPC_OP_RECV_MESSAGE;
     op->flags = 0;
     op->reserved = NULL;
-    op->data.recv_message.recv_message = &recv_buf_;
+    op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
   }
 
   void FinishOp(bool* status) {
     if (message_ == nullptr) return;
-    if (recv_buf_) {
+    if (recv_buf_.Valid()) {
       if (*status) {
         got_message = *status =
-            SerializationTraits<R>::Deserialize(recv_buf_, message_).ok();
+            SerializationTraits<R>::Deserialize(recv_buf_.bbuf_ptr(), message_)
+                .ok();
+        recv_buf_.Release();
       } else {
         got_message = false;
-        g_core_codegen_interface->grpc_byte_buffer_destroy(recv_buf_);
+        recv_buf_.Clear();
       }
     } else {
       got_message = false;
@@ -376,14 +379,14 @@
 
  private:
   R* message_;
-  grpc_byte_buffer* recv_buf_;
+  ByteBuffer recv_buf_;
   bool allow_not_getting_message_;
 };
 
 namespace CallOpGenericRecvMessageHelper {
 class DeserializeFunc {
  public:
-  virtual Status Deserialize(grpc_byte_buffer* buf) = 0;
+  virtual Status Deserialize(ByteBuffer* buf) = 0;
   virtual ~DeserializeFunc() {}
 };
 
@@ -391,8 +394,8 @@
 class DeserializeFuncType final : public DeserializeFunc {
  public:
   DeserializeFuncType(R* message) : message_(message) {}
-  Status Deserialize(grpc_byte_buffer* buf) override {
-    return SerializationTraits<R>::Deserialize(buf, message_);
+  Status Deserialize(ByteBuffer* buf) override {
+    return SerializationTraits<R>::Deserialize(buf->bbuf_ptr(), message_);
   }
 
   ~DeserializeFuncType() override {}
@@ -428,18 +431,19 @@
     op->op = GRPC_OP_RECV_MESSAGE;
     op->flags = 0;
     op->reserved = NULL;
-    op->data.recv_message.recv_message = &recv_buf_;
+    op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
   }
 
   void FinishOp(bool* status) {
     if (!deserialize_) return;
-    if (recv_buf_) {
+    if (recv_buf_.Valid()) {
       if (*status) {
         got_message = true;
-        *status = deserialize_->Deserialize(recv_buf_).ok();
+        *status = deserialize_->Deserialize(&recv_buf_).ok();
+        recv_buf_.Release();
       } else {
         got_message = false;
-        g_core_codegen_interface->grpc_byte_buffer_destroy(recv_buf_);
+        recv_buf_.Clear();
       }
     } else {
       got_message = false;
@@ -452,7 +456,7 @@
 
  private:
   std::unique_ptr<CallOpGenericRecvMessageHelper::DeserializeFunc> deserialize_;
-  grpc_byte_buffer* recv_buf_;
+  ByteBuffer recv_buf_;
   bool allow_not_getting_message_;
 };
 
diff --git a/include/grpc++/impl/codegen/method_handler_impl.h b/include/grpc++/impl/codegen/method_handler_impl.h
index 15e24bd..e14cb0e 100644
--- a/include/grpc++/impl/codegen/method_handler_impl.h
+++ b/include/grpc++/impl/codegen/method_handler_impl.h
@@ -19,6 +19,7 @@
 #ifndef GRPCXX_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
 #define GRPCXX_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H
 
+#include <grpc++/impl/codegen/byte_buffer.h>
 #include <grpc++/impl/codegen/core_codegen_interface.h>
 #include <grpc++/impl/codegen/rpc_service_method.h>
 #include <grpc++/impl/codegen/sync_stream.h>
@@ -37,8 +38,8 @@
 
   void RunHandler(const HandlerParameter& param) final {
     RequestType req;
-    Status status =
-        SerializationTraits<RequestType>::Deserialize(param.request, &req);
+    Status status = SerializationTraits<RequestType>::Deserialize(
+        param.request.bbuf_ptr(), &req);
     ResponseType rsp;
     if (status.ok()) {
       status = func_(service_, param.server_context, &req, &rsp);
@@ -123,8 +124,8 @@
 
   void RunHandler(const HandlerParameter& param) final {
     RequestType req;
-    Status status =
-        SerializationTraits<RequestType>::Deserialize(param.request, &req);
+    Status status = SerializationTraits<RequestType>::Deserialize(
+        param.request.bbuf_ptr(), &req);
 
     if (status.ok()) {
       ServerWriter<ResponseType> writer(param.call, param.server_context);
diff --git a/include/grpc++/impl/codegen/rpc_service_method.h b/include/grpc++/impl/codegen/rpc_service_method.h
index 7165774..d356012 100644
--- a/include/grpc++/impl/codegen/rpc_service_method.h
+++ b/include/grpc++/impl/codegen/rpc_service_method.h
@@ -25,14 +25,11 @@
 #include <memory>
 #include <vector>
 
+#include <grpc++/impl/codegen/byte_buffer.h>
 #include <grpc++/impl/codegen/config.h>
 #include <grpc++/impl/codegen/rpc_method.h>
 #include <grpc++/impl/codegen/status.h>
 
-extern "C" {
-struct grpc_byte_buffer;
-}
-
 namespace grpc {
 class ServerContext;
 class StreamContextInterface;
@@ -43,11 +40,14 @@
   virtual ~MethodHandler() {}
   struct HandlerParameter {
     HandlerParameter(Call* c, ServerContext* context, grpc_byte_buffer* req)
-        : call(c), server_context(context), request(req) {}
+        : call(c), server_context(context) {
+      request.set_buffer(req);
+    }
+    ~HandlerParameter() { request.Release(); }
     Call* call;
     ServerContext* server_context;
-    // Handler required to grpc_byte_buffer_destroy this
-    grpc_byte_buffer* request;
+    // Handler required to destroy these contents
+    ByteBuffer request;
   };
   virtual void RunHandler(const HandlerParameter& param) = 0;
 };
diff --git a/include/grpc++/impl/codegen/serialization_traits.h b/include/grpc++/impl/codegen/serialization_traits.h
index b72d474..4d91739 100644
--- a/include/grpc++/impl/codegen/serialization_traits.h
+++ b/include/grpc++/impl/codegen/serialization_traits.h
@@ -24,17 +24,26 @@
 /// Defines how to serialize and deserialize some type.
 ///
 /// Used for hooking different message serialization API's into GRPC.
-/// Each SerializationTraits implementation must provide the following
-/// functions:
-///   static Status Serialize(const Message& msg,
-///                           grpc_byte_buffer** buffer,
-///                           bool* own_buffer);
-///   static Status Deserialize(grpc_byte_buffer* buffer,
-///                             Message* msg,
-///                             int max_receive_message_size);
+/// Each SerializationTraits<Message> implementation must provide the
+/// following functions:
+/// 1.  static Status Serialize(const Message& msg,
+///                             ByteBuffer* buffer,
+///                             bool* own_buffer);
+///     OR
+///     static Status Serialize(const Message& msg,
+///                             grpc_byte_buffer** buffer,
+///                             bool* own_buffer);
+///     The former is preferred; the latter is deprecated
 ///
-/// Serialize is required to convert message to a grpc_byte_buffer, and
-/// to store a pointer to that byte buffer at *buffer. *own_buffer should
+/// 2.  static Status Deserialize(ByteBuffer* buffer,
+///                               Message* msg);
+///     OR
+///     static Status Deserialize(grpc_byte_buffer* buffer,
+///                               Message* msg);
+///     The former is preferred; the latter is deprecated
+///
+/// Serialize is required to convert message to a ByteBuffer, and
+/// return that byte buffer through *buffer. *own_buffer should
 /// be set to true if the caller owns said byte buffer, or false if
 /// ownership is retained elsewhere.
 ///
diff --git a/include/grpc++/impl/codegen/slice.h b/include/grpc++/impl/codegen/slice.h
index e682bde..c185bf4 100644
--- a/include/grpc++/impl/codegen/slice.h
+++ b/include/grpc++/impl/codegen/slice.h
@@ -19,11 +19,89 @@
 #ifndef GRPCXX_IMPL_CODEGEN_SLICE_H
 #define GRPCXX_IMPL_CODEGEN_SLICE_H
 
+#include <grpc++/impl/codegen/config.h>
 #include <grpc++/impl/codegen/core_codegen_interface.h>
 #include <grpc++/impl/codegen/string_ref.h>
 
+#include <grpc/impl/codegen/slice.h>
+
 namespace grpc {
 
+/// A wrapper around \a grpc_slice.
+///
+/// A slice represents a contiguous reference counted array of bytes.
+/// It is cheap to take references to a slice, and it is cheap to create a
+/// slice pointing to a subset of another slice.
+class Slice final {
+ public:
+  /// Construct an empty slice.
+  Slice();
+  /// Destructor - drops one reference.
+  ~Slice();
+
+  enum AddRef { ADD_REF };
+  /// Construct a slice from \a slice, adding a reference.
+  Slice(grpc_slice slice, AddRef);
+
+  enum StealRef { STEAL_REF };
+  /// Construct a slice from \a slice, stealing a reference.
+  Slice(grpc_slice slice, StealRef);
+
+  /// Allocate a slice of specified size
+  Slice(size_t len);
+
+  /// Construct a slice from a copied buffer
+  Slice(const void* buf, size_t len);
+
+  /// Construct a slice from a copied string
+  Slice(const grpc::string& str);
+
+  enum StaticSlice { STATIC_SLICE };
+
+  /// Construct a slice from a static buffer
+  Slice(const void* buf, size_t len, StaticSlice);
+
+  /// Copy constructor, adds a reference.
+  Slice(const Slice& other);
+
+  /// Assignment, reference count is unchanged.
+  Slice& operator=(Slice other) {
+    std::swap(slice_, other.slice_);
+    return *this;
+  }
+
+  /// Create a slice pointing at some data. Calls malloc to allocate a refcount
+  /// for the object, and arranges that destroy will be called with the
+  /// user data pointer passed in at destruction. Can be the same as buf or
+  /// different (e.g., if data is part of a larger structure that must be
+  /// destroyed when the data is no longer needed)
+  Slice(void* buf, size_t len, void (*destroy)(void*), void* user_data);
+
+  /// Specialization of above for common case where buf == user_data
+  Slice(void* buf, size_t len, void (*destroy)(void*))
+      : Slice(buf, len, destroy, buf) {}
+
+  /// Similar to the above but has a destroy that also takes slice length
+  Slice(void* buf, size_t len, void (*destroy)(void*, size_t));
+
+  /// Byte size.
+  size_t size() const { return GRPC_SLICE_LENGTH(slice_); }
+
+  /// Raw pointer to the beginning (first element) of the slice.
+  const uint8_t* begin() const { return GRPC_SLICE_START_PTR(slice_); }
+
+  /// Raw pointer to the end (one byte \em past the last element) of the slice.
+  const uint8_t* end() const { return GRPC_SLICE_END_PTR(slice_); }
+
+  /// Raw C slice. Caller needs to call grpc_slice_unref when done.
+  grpc_slice c_slice() const;
+
+ private:
+  friend class ByteBuffer;
+
+  grpc_slice slice_;
+};
+
 inline grpc::string_ref StringRefFromSlice(const grpc_slice* slice) {
   return grpc::string_ref(
       reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(*slice)),
diff --git a/include/grpc++/support/byte_buffer.h b/include/grpc++/support/byte_buffer.h
index df16c6a..81fa3b0 100644
--- a/include/grpc++/support/byte_buffer.h
+++ b/include/grpc++/support/byte_buffer.h
@@ -19,6 +19,7 @@
 #ifndef GRPCXX_SUPPORT_BYTE_BUFFER_H
 #define GRPCXX_SUPPORT_BYTE_BUFFER_H
 
+#include <grpc++/impl/codegen/byte_buffer.h>
 #include <grpc++/impl/serialization_traits.h>
 #include <grpc++/support/config.h>
 #include <grpc++/support/slice.h>
@@ -27,71 +28,4 @@
 #include <grpc/grpc.h>
 #include <grpc/support/log.h>
 
-#include <vector>
-
-namespace grpc {
-
-/// A sequence of bytes.
-class ByteBuffer final {
- public:
-  /// Constuct an empty buffer.
-  ByteBuffer() : buffer_(nullptr) {}
-
-  /// Construct buffer from \a slices, of which there are \a nslices.
-  ByteBuffer(const Slice* slices, size_t nslices);
-
-  /// Constuct a byte buffer by referencing elements of existing buffer
-  /// \a buf. Wrapper of core function grpc_byte_buffer_copy
-  ByteBuffer(const ByteBuffer& buf);
-
-  ~ByteBuffer();
-
-  ByteBuffer& operator=(const ByteBuffer&);
-
-  /// Dump (read) the buffer contents into \a slices.
-  Status Dump(std::vector<Slice>* slices) const;
-
-  /// Remove all data.
-  void Clear();
-
-  /// Buffer size in bytes.
-  size_t Length() const;
-
-  /// Swap the state of *this and *other.
-  void Swap(ByteBuffer* other);
-
- private:
-  friend class SerializationTraits<ByteBuffer, void>;
-
-  // takes ownership
-  void set_buffer(grpc_byte_buffer* buf) {
-    if (buffer_) {
-      Clear();
-    }
-    buffer_ = buf;
-  }
-
-  // For \a SerializationTraits's usage.
-  grpc_byte_buffer* buffer() const { return buffer_; }
-
-  grpc_byte_buffer* buffer_;
-};
-
-template <>
-class SerializationTraits<ByteBuffer, void> {
- public:
-  static Status Deserialize(grpc_byte_buffer* byte_buffer, ByteBuffer* dest) {
-    dest->set_buffer(byte_buffer);
-    return Status::OK;
-  }
-  static Status Serialize(const ByteBuffer& source, grpc_byte_buffer** buffer,
-                          bool* own_buffer) {
-    *buffer = grpc_byte_buffer_copy(source.buffer());
-    *own_buffer = true;
-    return Status::OK;
-  }
-};
-
-}  // namespace grpc
-
 #endif  // GRPCXX_SUPPORT_BYTE_BUFFER_H
diff --git a/include/grpc++/support/channel_arguments.h b/include/grpc++/support/channel_arguments.h
index 7b6befe..9dc505f 100644
--- a/include/grpc++/support/channel_arguments.h
+++ b/include/grpc++/support/channel_arguments.h
@@ -64,6 +64,12 @@
   /// Set the compression algorithm for the channel.
   void SetCompressionAlgorithm(grpc_compression_algorithm algorithm);
 
+  /// Set the grpclb fallback timeout (in ms) for the channel. If this amount
+  /// of time has passed but we have not gotten any non-empty \a serverlist from
+  /// the balancer, we will fall back to use the backend address(es) returned by
+  /// the resolver.
+  void SetGrpclbFallbackTimeout(int fallback_timeout);
+
   /// Set the socket mutator for the channel.
   void SetSocketMutator(grpc_socket_mutator* mutator);
 
diff --git a/include/grpc++/support/slice.h b/include/grpc++/support/slice.h
index bbf97f2..10db10d 100644
--- a/include/grpc++/support/slice.h
+++ b/include/grpc++/support/slice.h
@@ -19,86 +19,8 @@
 #ifndef GRPCXX_SUPPORT_SLICE_H
 #define GRPCXX_SUPPORT_SLICE_H
 
+#include <grpc++/impl/codegen/slice.h>
 #include <grpc++/support/config.h>
 #include <grpc/slice.h>
 
-namespace grpc {
-
-/// A wrapper around \a grpc_slice.
-///
-/// A slice represents a contiguous reference counted array of bytes.
-/// It is cheap to take references to a slice, and it is cheap to create a
-/// slice pointing to a subset of another slice.
-class Slice final {
- public:
-  /// Construct an empty slice.
-  Slice();
-  /// Destructor - drops one reference.
-  ~Slice();
-
-  enum AddRef { ADD_REF };
-  /// Construct a slice from \a slice, adding a reference.
-  Slice(grpc_slice slice, AddRef);
-
-  enum StealRef { STEAL_REF };
-  /// Construct a slice from \a slice, stealing a reference.
-  Slice(grpc_slice slice, StealRef);
-
-  /// Allocate a slice of specified size
-  Slice(size_t len);
-
-  /// Construct a slice from a copied buffer
-  Slice(const void* buf, size_t len);
-
-  /// Construct a slice from a copied string
-  Slice(const grpc::string& str);
-
-  enum StaticSlice { STATIC_SLICE };
-
-  /// Construct a slice from a static buffer
-  Slice(const void* buf, size_t len, StaticSlice);
-
-  /// Copy constructor, adds a reference.
-  Slice(const Slice& other);
-
-  /// Assignment, reference count is unchanged.
-  Slice& operator=(Slice other) {
-    std::swap(slice_, other.slice_);
-    return *this;
-  }
-
-  /// Create a slice pointing at some data. Calls malloc to allocate a refcount
-  /// for the object, and arranges that destroy will be called with the
-  /// user data pointer passed in at destruction. Can be the same as buf or
-  /// different (e.g., if data is part of a larger structure that must be
-  /// destroyed when the data is no longer needed)
-  Slice(void* buf, size_t len, void (*destroy)(void*), void* user_data);
-
-  /// Specialization of above for common case where buf == user_data
-  Slice(void* buf, size_t len, void (*destroy)(void*))
-      : Slice(buf, len, destroy, buf) {}
-
-  /// Similar to the above but has a destroy that also takes slice length
-  Slice(void* buf, size_t len, void (*destroy)(void*, size_t));
-
-  /// Byte size.
-  size_t size() const { return GRPC_SLICE_LENGTH(slice_); }
-
-  /// Raw pointer to the beginning (first element) of the slice.
-  const uint8_t* begin() const { return GRPC_SLICE_START_PTR(slice_); }
-
-  /// Raw pointer to the end (one byte \em past the last element) of the slice.
-  const uint8_t* end() const { return GRPC_SLICE_END_PTR(slice_); }
-
-  /// Raw C slice. Caller needs to call grpc_slice_unref when done.
-  grpc_slice c_slice() const { return grpc_slice_ref(slice_); }
-
- private:
-  friend class ByteBuffer;
-
-  grpc_slice slice_;
-};
-
-}  // namespace grpc
-
 #endif  // GRPCXX_SUPPORT_SLICE_H
diff --git a/include/grpc/byte_buffer.h b/include/grpc/byte_buffer.h
index 55e191d..7669582 100644
--- a/include/grpc/byte_buffer.h
+++ b/include/grpc/byte_buffer.h
@@ -19,69 +19,7 @@
 #ifndef GRPC_BYTE_BUFFER_H
 #define GRPC_BYTE_BUFFER_H
 
-#include <grpc/impl/codegen/grpc_types.h>
+#include <grpc/impl/codegen/byte_buffer.h>
 #include <grpc/slice_buffer.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** Returns a RAW byte buffer instance over the given slices (up to \a nslices).
- *
- * Increases the reference count for all \a slices processed. The user is
- * responsible for invoking grpc_byte_buffer_destroy on the returned instance.*/
-GRPCAPI grpc_byte_buffer *grpc_raw_byte_buffer_create(grpc_slice *slices,
-                                                      size_t nslices);
-
-/** Returns a *compressed* RAW byte buffer instance over the given slices (up to
- * \a nslices). The \a compression argument defines the compression algorithm
- * used to generate the data in \a slices.
- *
- * Increases the reference count for all \a slices processed. The user is
- * responsible for invoking grpc_byte_buffer_destroy on the returned instance.*/
-GRPCAPI grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create(
-    grpc_slice *slices, size_t nslices, grpc_compression_algorithm compression);
-
-/** Copies input byte buffer \a bb.
- *
- * Increases the reference count of all the source slices. The user is
- * responsible for calling grpc_byte_buffer_destroy over the returned copy. */
-GRPCAPI grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb);
-
-/** Returns the size of the given byte buffer, in bytes. */
-GRPCAPI size_t grpc_byte_buffer_length(grpc_byte_buffer *bb);
-
-/** Destroys \a byte_buffer deallocating all its memory. */
-GRPCAPI void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer);
-
-/** Reader for byte buffers. Iterates over slices in the byte buffer */
-struct grpc_byte_buffer_reader;
-typedef struct grpc_byte_buffer_reader grpc_byte_buffer_reader;
-
-/** Initialize \a reader to read over \a buffer.
- * Returns 1 upon success, 0 otherwise. */
-GRPCAPI int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
-                                         grpc_byte_buffer *buffer);
-
-/** Cleanup and destroy \a reader */
-GRPCAPI void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader);
-
-/** Updates \a slice with the next piece of data from from \a reader and returns
- * 1. Returns 0 at the end of the stream. Caller is responsible for calling
- * grpc_slice_unref on the result. */
-GRPCAPI int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
-                                         grpc_slice *slice);
-
-/** Merge all data from \a reader into single slice */
-GRPCAPI grpc_slice
-grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader *reader);
-
-/** Returns a RAW byte buffer instance from the output of \a reader. */
-GRPCAPI grpc_byte_buffer *grpc_raw_byte_buffer_from_reader(
-    grpc_byte_buffer_reader *reader);
-
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* GRPC_BYTE_BUFFER_H */
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index fab7d43..1de289f 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -313,7 +313,7 @@
                                                      void *reserved);
 
 /** Ref a call.
-    THREAD SAFETY: grpc_call_unref is thread-compatible */
+    THREAD SAFETY: grpc_call_ref is thread-compatible */
 GRPCAPI void grpc_call_ref(grpc_call *call);
 
 /** Unref a call.
diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h
index 2005e25..95b1447 100644
--- a/include/grpc/grpc_security.h
+++ b/include/grpc/grpc_security.h
@@ -249,19 +249,40 @@
   void *reserved;
 } grpc_auth_metadata_context;
 
+/** Maximum number of metadata entries returnable by a credentials plugin via
+    a synchronous return. */
+#define GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX 4
+
 /** grpc_metadata_credentials plugin is an API user provided structure used to
    create grpc_credentials objects that can be set on a channel (composed) or
    a call. See grpc_credentials_metadata_create_from_plugin below.
    The grpc client stack will call the get_metadata method of the plugin for
    every call in scope for the credentials created from it. */
 typedef struct {
-  /** The implementation of this method has to be non-blocking.
-     - context is the information that can be used by the plugin to create auth
-       metadata.
-     - cb is the callback that needs to be called when the metadata is ready.
-     - user_data needs to be passed as the first parameter of the callback. */
-  void (*get_metadata)(void *state, grpc_auth_metadata_context context,
-                       grpc_credentials_plugin_metadata_cb cb, void *user_data);
+  /** The implementation of this method has to be non-blocking, but can
+     be performed synchronously or asynchronously.
+
+     If processing occurs synchronously, returns non-zero and populates
+     creds_md, num_creds_md, status, and error_details.  In this case,
+     the caller takes ownership of the entries in creds_md and of
+     error_details.  Note that if the plugin needs to return more than
+     GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX entries in creds_md, it must
+     return asynchronously.
+
+     If processing occurs asynchronously, returns zero and invokes \a cb
+     when processing is completed.  \a user_data will be passed as the
+     first parameter of the callback.  NOTE: \a cb MUST be invoked in a
+     different thread, not from the thread in which \a get_metadata() is
+     invoked.
+
+     \a context is the information that can be used by the plugin to create
+     auth metadata. */
+  int (*get_metadata)(
+      void *state, grpc_auth_metadata_context context,
+      grpc_credentials_plugin_metadata_cb cb, void *user_data,
+      grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+      size_t *num_creds_md, grpc_status_code *status,
+      const char **error_details);
 
   /** Destroys the plugin state. */
   void (*destroy)(void *state);
diff --git a/include/grpc/impl/codegen/atm_gcc_atomic.h b/include/grpc/impl/codegen/atm_gcc_atomic.h
index 1793ec2..76ce863 100644
--- a/include/grpc/impl/codegen/atm_gcc_atomic.h
+++ b/include/grpc/impl/codegen/atm_gcc_atomic.h
@@ -25,6 +25,7 @@
 
 typedef intptr_t gpr_atm;
 #define GPR_ATM_MAX INTPTR_MAX
+#define GPR_ATM_MIN INTPTR_MIN
 
 #ifdef GPR_LOW_LEVEL_COUNTERS
 extern gpr_atm gpr_counter_atm_cas;
diff --git a/include/grpc/impl/codegen/atm_gcc_sync.h b/include/grpc/impl/codegen/atm_gcc_sync.h
index 27ae0f6..a9e4da3 100644
--- a/include/grpc/impl/codegen/atm_gcc_sync.h
+++ b/include/grpc/impl/codegen/atm_gcc_sync.h
@@ -25,6 +25,7 @@
 
 typedef intptr_t gpr_atm;
 #define GPR_ATM_MAX INTPTR_MAX
+#define GPR_ATM_MIN INTPTR_MIN
 
 #define GPR_ATM_COMPILE_BARRIER_() __asm__ __volatile__("" : : : "memory")
 
diff --git a/include/grpc/impl/codegen/atm_windows.h b/include/grpc/impl/codegen/atm_windows.h
index dfcaa4c..b868d79 100644
--- a/include/grpc/impl/codegen/atm_windows.h
+++ b/include/grpc/impl/codegen/atm_windows.h
@@ -24,6 +24,7 @@
 
 typedef intptr_t gpr_atm;
 #define GPR_ATM_MAX INTPTR_MAX
+#define GPR_ATM_MIN INTPTR_MIN
 
 #define gpr_atm_full_barrier MemoryBarrier
 
diff --git a/include/grpc/impl/codegen/byte_buffer.h b/include/grpc/impl/codegen/byte_buffer.h
new file mode 100644
index 0000000..fc33305
--- /dev/null
+++ b/include/grpc/impl/codegen/byte_buffer.h
@@ -0,0 +1,86 @@
+/*
+ *
+ * Copyright 2015 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.
+ *
+ */
+
+#ifndef GRPC_IMPL_CODEGEN_BYTE_BUFFER_H
+#define GRPC_IMPL_CODEGEN_BYTE_BUFFER_H
+
+#include <grpc/impl/codegen/grpc_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Returns a RAW byte buffer instance over the given slices (up to \a nslices).
+ *
+ * Increases the reference count for all \a slices processed. The user is
+ * responsible for invoking grpc_byte_buffer_destroy on the returned instance.*/
+GRPCAPI grpc_byte_buffer *grpc_raw_byte_buffer_create(grpc_slice *slices,
+                                                      size_t nslices);
+
+/** Returns a *compressed* RAW byte buffer instance over the given slices (up to
+ * \a nslices). The \a compression argument defines the compression algorithm
+ * used to generate the data in \a slices.
+ *
+ * Increases the reference count for all \a slices processed. The user is
+ * responsible for invoking grpc_byte_buffer_destroy on the returned instance.*/
+GRPCAPI grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create(
+    grpc_slice *slices, size_t nslices, grpc_compression_algorithm compression);
+
+/** Copies input byte buffer \a bb.
+ *
+ * Increases the reference count of all the source slices. The user is
+ * responsible for calling grpc_byte_buffer_destroy over the returned copy. */
+GRPCAPI grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb);
+
+/** Returns the size of the given byte buffer, in bytes. */
+GRPCAPI size_t grpc_byte_buffer_length(grpc_byte_buffer *bb);
+
+/** Destroys \a byte_buffer deallocating all its memory. */
+GRPCAPI void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer);
+
+/** Reader for byte buffers. Iterates over slices in the byte buffer */
+struct grpc_byte_buffer_reader;
+typedef struct grpc_byte_buffer_reader grpc_byte_buffer_reader;
+
+/** Initialize \a reader to read over \a buffer.
+ * Returns 1 upon success, 0 otherwise. */
+GRPCAPI int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
+                                         grpc_byte_buffer *buffer);
+
+/** Cleanup and destroy \a reader */
+GRPCAPI void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader);
+
+/** Updates \a slice with the next piece of data from from \a reader and returns
+ * 1. Returns 0 at the end of the stream. Caller is responsible for calling
+ * grpc_slice_unref on the result. */
+GRPCAPI int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
+                                         grpc_slice *slice);
+
+/** Merge all data from \a reader into single slice */
+GRPCAPI grpc_slice
+grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader *reader);
+
+/** Returns a RAW byte buffer instance from the output of \a reader. */
+GRPCAPI grpc_byte_buffer *grpc_raw_byte_buffer_from_reader(
+    grpc_byte_buffer_reader *reader);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_IMPL_CODEGEN_BYTE_BUFFER_H */
diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h
index 90f03f4..65463bb 100644
--- a/include/grpc/impl/codegen/grpc_types.h
+++ b/include/grpc/impl/codegen/grpc_types.h
@@ -288,7 +288,11 @@
   "grpc.experimental.tcp_max_read_chunk_size"
 /* Timeout in milliseconds to use for calls to the grpclb load balancer.
    If 0 or unset, the balancer calls will have no deadline. */
-#define GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS "grpc.grpclb_timeout_ms"
+#define GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS "grpc.grpclb_call_timeout_ms"
+/* Timeout in milliseconds to wait for the serverlist from the grpclb load
+   balancer before using fallback backend addresses from the resolver.
+   If 0, fallback will never be used. */
+#define GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS "grpc.grpclb_fallback_timeout_ms"
 /** If non-zero, grpc server's cronet compression workaround will be enabled */
 #define GRPC_ARG_WORKAROUND_CRONET_COMPRESSION \
   "grpc.workaround.cronet_compression"
diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h
index 1904c63..0f4316c 100644
--- a/include/grpc/impl/codegen/port_platform.h
+++ b/include/grpc/impl/codegen/port_platform.h
@@ -183,7 +183,6 @@
 #define _BSD_SOURCE
 #endif
 #if TARGET_OS_IPHONE
-#define GPR_FORBID_UNREACHABLE_CODE 1
 #define GPR_PLATFORM_STRING "ios"
 #define GPR_CPU_IPHONE 1
 #define GPR_PTHREAD_TLS 1
@@ -292,10 +291,6 @@
 #endif
 
 #ifdef _MSC_VER
-#ifdef _PYTHON_MSVC
-// The Python 3.5 Windows runtime is missing InetNtop
-#define GPR_WIN_INET_NTOP
-#endif  // _PYTHON_MSVC
 #if _MSC_VER < 1700
 typedef __int8 int8_t;
 typedef __int16 int16_t;
diff --git a/package.xml b/package.xml
index 0c201af..4c24070 100644
--- a/package.xml
+++ b/package.xml
@@ -100,7 +100,6 @@
     <file baseinstalldir="/" name="src/core/lib/support/atomic.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/atomic_with_atm.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/atomic_with_std.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/support/backoff.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/block_annotate.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/env.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/memory.h" role="src" />
@@ -118,7 +117,6 @@
     <file baseinstalldir="/" name="src/core/lib/support/arena.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/atm.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/avl.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/support/backoff.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/cmdline.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/cpu_iphone.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/cpu_linux.c" role="src" />
@@ -158,6 +156,7 @@
     <file baseinstalldir="/" name="src/core/lib/support/tmpfile_posix.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/tmpfile_windows.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/support/wrap_memcpy.c" role="src" />
+    <file baseinstalldir="/" name="include/grpc/impl/codegen/byte_buffer.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/byte_buffer_reader.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/compression_types.h" role="src" />
     <file baseinstalldir="/" name="include/grpc/impl/codegen/connectivity_state.h" role="src" />
@@ -263,6 +262,7 @@
     <file baseinstalldir="/" name="src/core/ext/filters/deadline/deadline_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/client/chttp2_connector.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/inproc/inproc_transport.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/backoff/backoff.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_args.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_stack.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_stack_builder.h" role="src" />
@@ -274,6 +274,8 @@
     <file baseinstalldir="/" name="src/core/lib/compression/algorithm_metadata.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/message_compress.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/stream_compression.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/compression/stream_compression_gzip.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/compression/stream_compression_identity.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/debug/stats.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/debug/stats_data.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/http/format_request.h" role="src" />
@@ -417,6 +419,7 @@
     <file baseinstalldir="/" name="src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/filters/workarounds/workaround_utils.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/init.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/backoff/backoff.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_args.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_stack.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/channel/channel_stack_builder.c" role="src" />
@@ -427,6 +430,8 @@
     <file baseinstalldir="/" name="src/core/lib/compression/compression.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/message_compress.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/compression/stream_compression.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/compression/stream_compression_gzip.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/compression/stream_compression_identity.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/debug/stats.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/debug/stats_data.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/http/format_request.c" role="src" />
diff --git a/setup.py b/setup.py
index 1288241..90c9316 100644
--- a/setup.py
+++ b/setup.py
@@ -110,8 +110,6 @@
       EXTRA_ENV_COMPILE_ARGS += ' -D_ftime=_ftime32 -D_timeb=__timeb32 -D_ftime_s=_ftime32_s'
     else:
       EXTRA_ENV_COMPILE_ARGS += ' -D_ftime=_ftime64 -D_timeb=__timeb64'
-  elif 'win32' in sys.platform:
-    EXTRA_ENV_COMPILE_ARGS += ' -D_PYTHON_MSVC'
   elif "linux" in sys.platform:
     EXTRA_ENV_COMPILE_ARGS += ' -std=c++11 -std=gnu99 -fvisibility=hidden -fno-wrapv -fno-exceptions'
   elif "darwin" in sys.platform:
@@ -163,7 +161,7 @@
   # TODO(zyc): Re-enble c-ares on x64 and x86 windows after fixing the
   # ares_library_init compilation issue
   DEFINE_MACROS += (('WIN32_LEAN_AND_MEAN', 1), ('CARES_STATICLIB', 1),
-                    ('GRPC_ARES', 0),)
+                    ('GRPC_ARES', 0), ('NTDDI_VERSION', 0x06000000),)
   if '64bit' in platform.architecture()[0]:
     DEFINE_MACROS += (('MS_WIN64', 1),)
   elif sys.version_info >= (3, 5):
diff --git a/src/compiler/OWNERS b/src/compiler/OWNERS
deleted file mode 100644
index 96b89fc..0000000
--- a/src/compiler/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-@vjpai cpp_generator.cc
diff --git a/src/core/ext/filters/client_channel/channel_connectivity.c b/src/core/ext/filters/client_channel/channel_connectivity.c
index 3844b98..f79d3bd 100644
--- a/src/core/ext/filters/client_channel/channel_connectivity.c
+++ b/src/core/ext/filters/client_channel/channel_connectivity.c
@@ -186,8 +186,8 @@
   watcher_timer_init_arg *wa = (watcher_timer_init_arg *)arg;
 
   grpc_timer_init(exec_ctx, &wa->w->alarm,
-                  gpr_convert_clock_type(wa->deadline, GPR_CLOCK_MONOTONIC),
-                  &wa->w->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC));
+                  grpc_timespec_to_millis_round_up(wa->deadline),
+                  &wa->w->on_timeout);
   gpr_free(wa);
 }
 
diff --git a/src/core/ext/filters/client_channel/client_channel.c b/src/core/ext/filters/client_channel/client_channel.c
index 016199b..051419f 100644
--- a/src/core/ext/filters/client_channel/client_channel.c
+++ b/src/core/ext/filters/client_channel/client_channel.c
@@ -68,7 +68,7 @@
 
 typedef struct {
   gpr_refcount refs;
-  gpr_timespec timeout;
+  grpc_millis timeout;
   wait_for_ready_value wait_for_ready;
 } method_parameters;
 
@@ -98,17 +98,18 @@
   return true;
 }
 
-static bool parse_timeout(grpc_json *field, gpr_timespec *timeout) {
+static bool parse_timeout(grpc_json *field, grpc_millis *timeout) {
   if (field->type != GRPC_JSON_STRING) return false;
   size_t len = strlen(field->value);
   if (field->value[len - 1] != 's') return false;
   char *buf = gpr_strdup(field->value);
   buf[len - 1] = '\0';  // Remove trailing 's'.
   char *decimal_point = strchr(buf, '.');
+  int nanos = 0;
   if (decimal_point != NULL) {
     *decimal_point = '\0';
-    timeout->tv_nsec = gpr_parse_nonnegative_int(decimal_point + 1);
-    if (timeout->tv_nsec == -1) {
+    nanos = gpr_parse_nonnegative_int(decimal_point + 1);
+    if (nanos == -1) {
       gpr_free(buf);
       return false;
     }
@@ -127,24 +128,25 @@
         gpr_free(buf);
         return false;
     }
-    timeout->tv_nsec *= multiplier;
+    nanos *= multiplier;
   }
-  timeout->tv_sec = gpr_parse_nonnegative_int(buf);
+  int seconds = gpr_parse_nonnegative_int(buf);
   gpr_free(buf);
-  if (timeout->tv_sec == -1) return false;
+  if (seconds == -1) return false;
+  *timeout = seconds * GPR_MS_PER_SEC + nanos / GPR_NS_PER_MS;
   return true;
 }
 
 static void *method_parameters_create_from_json(const grpc_json *json) {
   wait_for_ready_value wait_for_ready = WAIT_FOR_READY_UNSET;
-  gpr_timespec timeout = {0, 0, GPR_TIMESPAN};
+  grpc_millis timeout = 0;
   for (grpc_json *field = json->child; field != NULL; field = field->next) {
     if (field->key == NULL) continue;
     if (strcmp(field->key, "waitForReady") == 0) {
       if (wait_for_ready != WAIT_FOR_READY_UNSET) return NULL;  // Duplicate.
       if (!parse_wait_for_ready(field, &wait_for_ready)) return NULL;
     } else if (strcmp(field->key, "timeout") == 0) {
-      if (timeout.tv_sec > 0 || timeout.tv_nsec > 0) return NULL;  // Duplicate.
+      if (timeout > 0) return NULL;  // Duplicate.
       if (!parse_timeout(field, &timeout)) return NULL;
     }
   }
@@ -823,7 +825,7 @@
 
   grpc_slice path;  // Request path.
   gpr_timespec call_start_time;
-  gpr_timespec deadline;
+  grpc_millis deadline;
   gpr_arena *arena;
   grpc_call_stack *owning_call;
   grpc_call_combiner *call_combiner;
@@ -976,11 +978,11 @@
       // If the deadline from the service config is shorter than the one
       // from the client API, reset the deadline timer.
       if (chand->deadline_checking_enabled &&
-          gpr_time_cmp(calld->method_params->timeout,
-                       gpr_time_0(GPR_TIMESPAN)) != 0) {
-        const gpr_timespec per_method_deadline =
-            gpr_time_add(calld->call_start_time, calld->method_params->timeout);
-        if (gpr_time_cmp(per_method_deadline, calld->deadline) < 0) {
+          calld->method_params->timeout != 0) {
+        const grpc_millis per_method_deadline =
+            grpc_timespec_to_millis_round_up(calld->call_start_time) +
+            calld->method_params->timeout;
+        if (per_method_deadline < calld->deadline) {
           calld->deadline = per_method_deadline;
           grpc_deadline_state_reset(exec_ctx, elem, calld->deadline);
         }
@@ -1418,7 +1420,7 @@
   // Initialize data members.
   calld->path = grpc_slice_ref_internal(args->path);
   calld->call_start_time = args->start_time;
-  calld->deadline = gpr_convert_clock_type(args->deadline, GPR_CLOCK_MONOTONIC);
+  calld->deadline = args->deadline;
   calld->arena = args->arena;
   calld->owning_call = args->call_stack;
   calld->call_combiner = args->call_combiner;
diff --git a/src/core/ext/filters/client_channel/connector.h b/src/core/ext/filters/client_channel/connector.h
index 7f3d4a1..bf79f26 100644
--- a/src/core/ext/filters/client_channel/connector.h
+++ b/src/core/ext/filters/client_channel/connector.h
@@ -34,7 +34,7 @@
   /** set of pollsets interested in this connection */
   grpc_pollset_set *interested_parties;
   /** deadline for connection */
-  gpr_timespec deadline;
+  grpc_millis deadline;
   /** channel arguments (to be passed to transport) */
   const grpc_channel_args *channel_args;
 } grpc_connect_in_args;
diff --git a/src/core/ext/filters/client_channel/http_proxy.c b/src/core/ext/filters/client_channel/http_proxy.c
index c507a27..a16b44d 100644
--- a/src/core/ext/filters/client_channel/http_proxy.c
+++ b/src/core/ext/filters/client_channel/http_proxy.c
@@ -91,6 +91,7 @@
   char* user_cred = NULL;
   *name_to_resolve = get_http_proxy_server(exec_ctx, &user_cred);
   if (*name_to_resolve == NULL) return false;
+  char* no_proxy_str = NULL;
   grpc_uri* uri =
       grpc_uri_parse(exec_ctx, server_uri, false /* suppress_errors */);
   if (uri == NULL || uri->path[0] == '\0') {
@@ -98,20 +99,14 @@
             "'http_proxy' environment variable set, but cannot "
             "parse server URI '%s' -- not using proxy",
             server_uri);
-    if (uri != NULL) {
-      gpr_free(user_cred);
-      grpc_uri_destroy(uri);
-    }
-    return false;
+    goto no_use_proxy;
   }
   if (strcmp(uri->scheme, "unix") == 0) {
     gpr_log(GPR_INFO, "not using proxy for Unix domain socket '%s'",
             server_uri);
-    gpr_free(user_cred);
-    grpc_uri_destroy(uri);
-    return false;
+    goto no_use_proxy;
   }
-  char* no_proxy_str = gpr_getenv("no_proxy");
+  no_proxy_str = gpr_getenv("no_proxy");
   if (no_proxy_str != NULL) {
     static const char* NO_PROXY_SEPARATOR = ",";
     bool use_proxy = true;
@@ -147,12 +142,7 @@
       gpr_free(no_proxy_hosts);
       gpr_free(server_host);
       gpr_free(server_port);
-      if (!use_proxy) {
-        grpc_uri_destroy(uri);
-        gpr_free(*name_to_resolve);
-        *name_to_resolve = NULL;
-        return false;
-      }
+      if (!use_proxy) goto no_use_proxy;
     }
   }
   grpc_arg args_to_add[2];
@@ -173,9 +163,15 @@
   } else {
     *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 1);
   }
-  gpr_free(user_cred);
   grpc_uri_destroy(uri);
+  gpr_free(user_cred);
   return true;
+no_use_proxy:
+  if (uri != NULL) grpc_uri_destroy(uri);
+  gpr_free(*name_to_resolve);
+  *name_to_resolve = NULL;
+  gpr_free(user_cred);
+  return false;
 }
 
 static bool proxy_mapper_map_address(grpc_exec_ctx* exec_ctx,
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 85ef789..cb5fdd1 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
@@ -102,6 +102,7 @@
 #include "src/core/ext/filters/client_channel/parse_address.h"
 #include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"
 #include "src/core/ext/filters/client_channel/subchannel_index.h"
+#include "src/core/lib/backoff/backoff.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/iomgr/combiner.h"
@@ -111,7 +112,6 @@
 #include "src/core/lib/slice/slice_hash_table.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
-#include "src/core/lib/support/backoff.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/call.h"
 #include "src/core/lib/surface/channel.h"
@@ -123,6 +123,7 @@
 #define GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER 1.6
 #define GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS 120
 #define GRPC_GRPCLB_RECONNECT_JITTER 0.2
+#define GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS 10000
 
 grpc_tracer_flag grpc_lb_glb_trace = GRPC_TRACER_INITIALIZER(false, "glb");
 
@@ -299,6 +300,10 @@
   /** timeout in milliseconds for the LB call. 0 means no deadline. */
   int lb_call_timeout_ms;
 
+  /** timeout in milliseconds for before using fallback backend addresses.
+   * 0 means not using fallback. */
+  int lb_fallback_timeout_ms;
+
   /** for communicating with the LB server */
   grpc_channel *lb_channel;
 
@@ -325,6 +330,9 @@
    * Otherwise, we delegate to the RR policy. */
   size_t serverlist_index;
 
+  /** stores the backend addresses from the resolver */
+  grpc_lb_addresses *fallback_backend_addresses;
+
   /** list of picks that are waiting on RR's policy connectivity */
   pending_pick *pending_picks;
 
@@ -345,6 +353,9 @@
   /** is \a lb_call_retry_timer active? */
   bool retry_timer_active;
 
+  /** is \a lb_fallback_timer active? */
+  bool fallback_timer_active;
+
   /** called upon changes to the LB channel's connectivity. */
   grpc_closure lb_channel_on_connectivity_changed;
 
@@ -354,9 +365,6 @@
   /************************************************************/
   /*  client data associated with the LB server communication */
   /************************************************************/
-  /* Finished sending initial request. */
-  grpc_closure lb_on_sent_initial_request;
-
   /* Status from the LB server has been received. This signals the end of the LB
    * call. */
   grpc_closure lb_on_server_status_received;
@@ -367,6 +375,9 @@
   /* LB call retry timer callback. */
   grpc_closure lb_on_call_retry;
 
+  /* LB fallback timer callback. */
+  grpc_closure lb_on_fallback;
+
   grpc_call *lb_call; /* streaming call to the LB server, */
 
   grpc_metadata_array lb_initial_metadata_recv; /* initial MD from LB server */
@@ -385,19 +396,21 @@
   grpc_slice lb_call_status_details;
 
   /** LB call retry backoff state */
-  gpr_backoff lb_call_backoff_state;
+  grpc_backoff lb_call_backoff_state;
 
   /** LB call retry timer */
   grpc_timer lb_call_retry_timer;
 
-  bool initial_request_sent;
+  /** LB fallback timer */
+  grpc_timer lb_fallback_timer;
+
   bool seen_initial_response;
 
   /* Stats for client-side load reporting. Should be unreffed and
    * recreated whenever lb_call is replaced. */
   grpc_grpclb_client_stats *client_stats;
   /* Interval and timer for next client load report. */
-  gpr_timespec client_stats_report_interval;
+  grpc_millis client_stats_report_interval;
   grpc_timer client_load_report_timer;
   bool client_load_report_timer_pending;
   bool last_client_load_report_counters_were_zero;
@@ -536,6 +549,32 @@
   return lb_addresses;
 }
 
+/* Returns the backend addresses extracted from the given addresses */
+static grpc_lb_addresses *extract_backend_addresses_locked(
+    grpc_exec_ctx *exec_ctx, const grpc_lb_addresses *addresses) {
+  /* first pass: count the number of backend addresses */
+  size_t num_backends = 0;
+  for (size_t i = 0; i < addresses->num_addresses; ++i) {
+    if (!addresses->addresses[i].is_balancer) {
+      ++num_backends;
+    }
+  }
+  /* second pass: actually populate the addresses and (empty) LB tokens */
+  grpc_lb_addresses *backend_addresses =
+      grpc_lb_addresses_create(num_backends, &lb_token_vtable);
+  size_t num_copied = 0;
+  for (size_t i = 0; i < addresses->num_addresses; ++i) {
+    if (addresses->addresses[i].is_balancer) continue;
+    const grpc_resolved_address *addr = &addresses->addresses[i].address;
+    grpc_lb_addresses_set_address(backend_addresses, num_copied, &addr->addr,
+                                  addr->len, false /* is_balancer */,
+                                  NULL /* balancer_name */,
+                                  (void *)GRPC_MDELEM_LB_TOKEN_EMPTY.payload);
+    ++num_copied;
+  }
+  return backend_addresses;
+}
+
 static void update_lb_connectivity_status_locked(
     grpc_exec_ctx *exec_ctx, glb_lb_policy *glb_policy,
     grpc_connectivity_state rr_state, grpc_error *rr_state_error) {
@@ -603,35 +642,38 @@
     grpc_exec_ctx *exec_ctx, glb_lb_policy *glb_policy,
     const grpc_lb_policy_pick_args *pick_args, bool force_async,
     grpc_connected_subchannel **target, wrapped_rr_closure_arg *wc_arg) {
-  // Look at the index into the serverlist to see if we should drop this call.
-  grpc_grpclb_server *server =
-      glb_policy->serverlist->servers[glb_policy->serverlist_index++];
-  if (glb_policy->serverlist_index == glb_policy->serverlist->num_servers) {
-    glb_policy->serverlist_index = 0;  // Wrap-around.
-  }
-  if (server->drop) {
-    // Not using the RR policy, so unref it.
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
-      gpr_log(GPR_INFO, "Unreffing RR for drop (0x%" PRIxPTR ")",
-              (intptr_t)wc_arg->rr_policy);
+  // Check for drops if we are not using fallback backend addresses.
+  if (glb_policy->serverlist != NULL) {
+    // Look at the index into the serverlist to see if we should drop this call.
+    grpc_grpclb_server *server =
+        glb_policy->serverlist->servers[glb_policy->serverlist_index++];
+    if (glb_policy->serverlist_index == glb_policy->serverlist->num_servers) {
+      glb_policy->serverlist_index = 0;  // Wrap-around.
     }
-    GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, "glb_pick_sync");
-    // Update client load reporting stats to indicate the number of
-    // dropped calls.  Note that we have to do this here instead of in
-    // the client_load_reporting filter, because we do not create a
-    // subchannel call (and therefore no client_load_reporting filter)
-    // for dropped calls.
-    grpc_grpclb_client_stats_add_call_dropped_locked(server->load_balance_token,
-                                                     wc_arg->client_stats);
-    grpc_grpclb_client_stats_unref(wc_arg->client_stats);
-    if (force_async) {
-      GPR_ASSERT(wc_arg->wrapped_closure != NULL);
-      GRPC_CLOSURE_SCHED(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_NONE);
+    if (server->drop) {
+      // Not using the RR policy, so unref it.
+      if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+        gpr_log(GPR_INFO, "Unreffing RR for drop (0x%" PRIxPTR ")",
+                (intptr_t)wc_arg->rr_policy);
+      }
+      GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, "glb_pick_sync");
+      // Update client load reporting stats to indicate the number of
+      // dropped calls.  Note that we have to do this here instead of in
+      // the client_load_reporting filter, because we do not create a
+      // subchannel call (and therefore no client_load_reporting filter)
+      // for dropped calls.
+      grpc_grpclb_client_stats_add_call_dropped_locked(
+          server->load_balance_token, wc_arg->client_stats);
+      grpc_grpclb_client_stats_unref(wc_arg->client_stats);
+      if (force_async) {
+        GPR_ASSERT(wc_arg->wrapped_closure != NULL);
+        GRPC_CLOSURE_SCHED(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_NONE);
+        gpr_free(wc_arg->free_when_done);
+        return false;
+      }
       gpr_free(wc_arg->free_when_done);
-      return false;
+      return true;
     }
-    gpr_free(wc_arg->free_when_done);
-    return true;
   }
   // Pick via the RR policy.
   const bool pick_done = grpc_lb_policy_pick_locked(
@@ -669,8 +711,18 @@
 
 static grpc_lb_policy_args *lb_policy_args_create(grpc_exec_ctx *exec_ctx,
                                                   glb_lb_policy *glb_policy) {
-  grpc_lb_addresses *addresses =
-      process_serverlist_locked(exec_ctx, glb_policy->serverlist);
+  grpc_lb_addresses *addresses;
+  if (glb_policy->serverlist != NULL) {
+    GPR_ASSERT(glb_policy->serverlist->num_servers > 0);
+    addresses = process_serverlist_locked(exec_ctx, glb_policy->serverlist);
+  } else {
+    // If rr_handover_locked() is invoked when we haven't received any
+    // serverlist from the balancer, we use the fallback backends returned by
+    // the resolver. Note that the fallback backend list may be empty, in which
+    // case the new round_robin policy will keep the requested picks pending.
+    GPR_ASSERT(glb_policy->fallback_backend_addresses != NULL);
+    addresses = grpc_lb_addresses_copy(glb_policy->fallback_backend_addresses);
+  }
   GPR_ASSERT(addresses != NULL);
   grpc_lb_policy_args *args = (grpc_lb_policy_args *)gpr_zalloc(sizeof(*args));
   args->client_channel_factory = glb_policy->cc_factory;
@@ -776,8 +828,6 @@
 /* glb_policy->rr_policy may be NULL (initial handover) */
 static void rr_handover_locked(grpc_exec_ctx *exec_ctx,
                                glb_lb_policy *glb_policy) {
-  GPR_ASSERT(glb_policy->serverlist != NULL &&
-             glb_policy->serverlist->num_servers > 0);
   if (glb_policy->shutting_down) return;
   grpc_lb_policy_args *args = lb_policy_args_create(exec_ctx, glb_policy);
   GPR_ASSERT(args != NULL);
@@ -926,6 +976,9 @@
   if (glb_policy->serverlist != NULL) {
     grpc_grpclb_destroy_serverlist(glb_policy->serverlist);
   }
+  if (glb_policy->fallback_backend_addresses != NULL) {
+    grpc_lb_addresses_destroy(exec_ctx, glb_policy->fallback_backend_addresses);
+  }
   grpc_fake_resolver_response_generator_unref(glb_policy->response_generator);
   grpc_subchannel_index_unref();
   if (glb_policy->pending_update_args != NULL) {
@@ -1067,12 +1120,28 @@
   GRPC_ERROR_UNREF(error);
 }
 
+static void lb_on_fallback_timer_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                        grpc_error *error);
 static void query_for_backends_locked(grpc_exec_ctx *exec_ctx,
                                       glb_lb_policy *glb_policy);
 static void start_picking_locked(grpc_exec_ctx *exec_ctx,
                                  glb_lb_policy *glb_policy) {
+  /* start a timer to fall back */
+  if (glb_policy->lb_fallback_timeout_ms > 0 &&
+      glb_policy->serverlist == NULL && !glb_policy->fallback_timer_active) {
+    grpc_millis deadline =
+        grpc_exec_ctx_now(exec_ctx) + glb_policy->lb_fallback_timeout_ms;
+    GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "grpclb_fallback_timer");
+    GRPC_CLOSURE_INIT(&glb_policy->lb_on_fallback, lb_on_fallback_timer_locked,
+                      glb_policy,
+                      grpc_combiner_scheduler(glb_policy->base.combiner));
+    glb_policy->fallback_timer_active = true;
+    grpc_timer_init(exec_ctx, &glb_policy->lb_fallback_timer, deadline,
+                    &glb_policy->lb_on_fallback);
+  }
+
   glb_policy->started_picking = true;
-  gpr_backoff_reset(&glb_policy->lb_call_backoff_state);
+  grpc_backoff_reset(&glb_policy->lb_call_backoff_state);
   query_for_backends_locked(exec_ctx, glb_policy);
 }
 
@@ -1173,20 +1242,69 @@
       exec_ctx, &glb_policy->state_tracker, current, notify);
 }
 
+static void lb_call_on_retry_timer_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                          grpc_error *error) {
+  glb_lb_policy *glb_policy = (glb_lb_policy *)arg;
+  glb_policy->retry_timer_active = false;
+  if (!glb_policy->shutting_down && error == GRPC_ERROR_NONE) {
+    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+      gpr_log(GPR_INFO, "Restaring call to LB server (grpclb %p)",
+              (void *)glb_policy);
+    }
+    GPR_ASSERT(glb_policy->lb_call == NULL);
+    query_for_backends_locked(exec_ctx, glb_policy);
+  }
+  GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, "grpclb_retry_timer");
+}
+
+static void maybe_restart_lb_call(grpc_exec_ctx *exec_ctx,
+                                  glb_lb_policy *glb_policy) {
+  if (glb_policy->started_picking && glb_policy->updating_lb_call) {
+    if (glb_policy->retry_timer_active) {
+      grpc_timer_cancel(exec_ctx, &glb_policy->lb_call_retry_timer);
+    }
+    if (!glb_policy->shutting_down) start_picking_locked(exec_ctx, glb_policy);
+    glb_policy->updating_lb_call = false;
+  } else if (!glb_policy->shutting_down) {
+    /* if we aren't shutting down, restart the LB client call after some time */
+    grpc_millis next_try =
+        grpc_backoff_step(exec_ctx, &glb_policy->lb_call_backoff_state);
+    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+      gpr_log(GPR_DEBUG, "Connection to LB server lost (grpclb: %p)...",
+              (void *)glb_policy);
+      grpc_millis timeout = next_try - grpc_exec_ctx_now(exec_ctx);
+      if (timeout > 0) {
+        gpr_log(GPR_DEBUG, "... retry_timer_active in %" PRIdPTR "ms.",
+                timeout);
+      } else {
+        gpr_log(GPR_DEBUG, "... retry_timer_active immediately.");
+      }
+    }
+    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_locked, glb_policy,
+                      grpc_combiner_scheduler(glb_policy->base.combiner));
+    glb_policy->retry_timer_active = true;
+    grpc_timer_init(exec_ctx, &glb_policy->lb_call_retry_timer, next_try,
+                    &glb_policy->lb_on_call_retry);
+  }
+  GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
+                            "lb_on_server_status_received_locked");
+}
+
 static void send_client_load_report_locked(grpc_exec_ctx *exec_ctx, void *arg,
                                            grpc_error *error);
 
 static void schedule_next_client_load_report(grpc_exec_ctx *exec_ctx,
                                              glb_lb_policy *glb_policy) {
-  const gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
-  const gpr_timespec next_client_load_report_time =
-      gpr_time_add(now, glb_policy->client_stats_report_interval);
+  const grpc_millis next_client_load_report_time =
+      grpc_exec_ctx_now(exec_ctx) + glb_policy->client_stats_report_interval;
   GRPC_CLOSURE_INIT(&glb_policy->client_load_report_closure,
                     send_client_load_report_locked, glb_policy,
                     grpc_combiner_scheduler(glb_policy->base.combiner));
   grpc_timer_init(exec_ctx, &glb_policy->client_load_report_timer,
                   next_client_load_report_time,
-                  &glb_policy->client_load_report_closure, now);
+                  &glb_policy->client_load_report_closure);
 }
 
 static void client_load_report_done_locked(grpc_exec_ctx *exec_ctx, void *arg,
@@ -1203,21 +1321,6 @@
   schedule_next_client_load_report(exec_ctx, glb_policy);
 }
 
-static void do_send_client_load_report_locked(grpc_exec_ctx *exec_ctx,
-                                              glb_lb_policy *glb_policy) {
-  grpc_op op;
-  memset(&op, 0, sizeof(op));
-  op.op = GRPC_OP_SEND_MESSAGE;
-  op.data.send_message.send_message = glb_policy->client_load_report_payload;
-  GRPC_CLOSURE_INIT(&glb_policy->client_load_report_closure,
-                    client_load_report_done_locked, glb_policy,
-                    grpc_combiner_scheduler(glb_policy->base.combiner));
-  grpc_call_error call_error = grpc_call_start_batch_and_execute(
-      exec_ctx, glb_policy->lb_call, &op, 1,
-      &glb_policy->client_load_report_closure);
-  GPR_ASSERT(GRPC_CALL_OK == call_error);
-}
-
 static bool load_report_counters_are_zero(grpc_grpclb_request *request) {
   grpc_grpclb_dropped_call_counts *drop_entries =
       (grpc_grpclb_dropped_call_counts *)
@@ -1237,6 +1340,9 @@
     glb_policy->client_load_report_timer_pending = false;
     GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
                               "client_load_report");
+    if (glb_policy->lb_call == NULL) {
+      maybe_restart_lb_call(exec_ctx, glb_policy);
+    }
     return;
   }
   // Construct message payload.
@@ -1260,17 +1366,23 @@
       grpc_raw_byte_buffer_create(&request_payload_slice, 1);
   grpc_slice_unref_internal(exec_ctx, request_payload_slice);
   grpc_grpclb_request_destroy(request);
-  // If we've already sent the initial request, then we can go ahead and
-  // sent the load report.  Otherwise, we need to wait until the initial
-  // request has been sent to send this
-  // (see lb_on_sent_initial_request_locked() below).
-  if (glb_policy->initial_request_sent) {
-    do_send_client_load_report_locked(exec_ctx, glb_policy);
+  // Send load report message.
+  grpc_op op;
+  memset(&op, 0, sizeof(op));
+  op.op = GRPC_OP_SEND_MESSAGE;
+  op.data.send_message.send_message = glb_policy->client_load_report_payload;
+  GRPC_CLOSURE_INIT(&glb_policy->client_load_report_closure,
+                    client_load_report_done_locked, glb_policy,
+                    grpc_combiner_scheduler(glb_policy->base.combiner));
+  grpc_call_error call_error = grpc_call_start_batch_and_execute(
+      exec_ctx, glb_policy->lb_call, &op, 1,
+      &glb_policy->client_load_report_closure);
+  if (call_error != GRPC_CALL_OK) {
+    gpr_log(GPR_ERROR, "call_error=%d", call_error);
+    GPR_ASSERT(GRPC_CALL_OK == call_error);
   }
 }
 
-static void lb_on_sent_initial_request_locked(grpc_exec_ctx *exec_ctx,
-                                              void *arg, grpc_error *error);
 static void lb_on_server_status_received_locked(grpc_exec_ctx *exec_ctx,
                                                 void *arg, grpc_error *error);
 static void lb_on_response_received_locked(grpc_exec_ctx *exec_ctx, void *arg,
@@ -1286,12 +1398,10 @@
    * glb_policy->base.interested_parties, which is comprised of the polling
    * entities from \a client_channel. */
   grpc_slice host = grpc_slice_from_copied_string(glb_policy->server_name);
-  gpr_timespec deadline =
+  grpc_millis deadline =
       glb_policy->lb_call_timeout_ms == 0
-          ? gpr_inf_future(GPR_CLOCK_MONOTONIC)
-          : gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
-                         gpr_time_from_millis(glb_policy->lb_call_timeout_ms,
-                                              GPR_TIMESPAN));
+          ? GRPC_MILLIS_INF_FUTURE
+          : grpc_exec_ctx_now(exec_ctx) + glb_policy->lb_call_timeout_ms;
   glb_policy->lb_call = grpc_channel_create_pollset_set_call(
       exec_ctx, glb_policy->lb_channel, NULL, GRPC_PROPAGATE_DEFAULTS,
       glb_policy->base.interested_parties,
@@ -1315,9 +1425,6 @@
   grpc_slice_unref_internal(exec_ctx, request_payload_slice);
   grpc_grpclb_request_destroy(request);
 
-  GRPC_CLOSURE_INIT(&glb_policy->lb_on_sent_initial_request,
-                    lb_on_sent_initial_request_locked, glb_policy,
-                    grpc_combiner_scheduler(glb_policy->base.combiner));
   GRPC_CLOSURE_INIT(&glb_policy->lb_on_server_status_received,
                     lb_on_server_status_received_locked, glb_policy,
                     grpc_combiner_scheduler(glb_policy->base.combiner));
@@ -1325,14 +1432,13 @@
                     lb_on_response_received_locked, glb_policy,
                     grpc_combiner_scheduler(glb_policy->base.combiner));
 
-  gpr_backoff_init(&glb_policy->lb_call_backoff_state,
-                   GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS,
-                   GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER,
-                   GRPC_GRPCLB_RECONNECT_JITTER,
-                   GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS * 1000,
-                   GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
+  grpc_backoff_init(&glb_policy->lb_call_backoff_state,
+                    GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS,
+                    GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER,
+                    GRPC_GRPCLB_RECONNECT_JITTER,
+                    GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS * 1000,
+                    GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
 
-  glb_policy->initial_request_sent = false;
   glb_policy->seen_initial_response = false;
   glb_policy->last_client_load_report_counters_were_zero = false;
 }
@@ -1349,7 +1455,7 @@
   grpc_byte_buffer_destroy(glb_policy->lb_request_payload);
   grpc_slice_unref_internal(exec_ctx, glb_policy->lb_call_status_details);
 
-  if (!glb_policy->client_load_report_timer_pending) {
+  if (glb_policy->client_load_report_timer_pending) {
     grpc_timer_cancel(exec_ctx, &glb_policy->client_load_report_timer);
   }
 }
@@ -1373,7 +1479,7 @@
   GPR_ASSERT(glb_policy->lb_call != NULL);
 
   grpc_call_error call_error;
-  grpc_op ops[4];
+  grpc_op ops[3];
   memset(ops, 0, sizeof(ops));
 
   grpc_op *op = ops;
@@ -1394,13 +1500,8 @@
   op->flags = 0;
   op->reserved = NULL;
   op++;
-  /* take a weak ref (won't prevent calling of \a glb_shutdown if the strong ref
-   * count goes to zero) to be unref'd in lb_on_sent_initial_request_locked() */
-  GRPC_LB_POLICY_WEAK_REF(&glb_policy->base,
-                          "lb_on_sent_initial_request_locked");
-  call_error = grpc_call_start_batch_and_execute(
-      exec_ctx, glb_policy->lb_call, ops, (size_t)(op - ops),
-      &glb_policy->lb_on_sent_initial_request);
+  call_error = grpc_call_start_batch_and_execute(exec_ctx, glb_policy->lb_call,
+                                                 ops, (size_t)(op - ops), NULL);
   GPR_ASSERT(GRPC_CALL_OK == call_error);
 
   op = ops;
@@ -1437,19 +1538,6 @@
   GPR_ASSERT(GRPC_CALL_OK == call_error);
 }
 
-static void lb_on_sent_initial_request_locked(grpc_exec_ctx *exec_ctx,
-                                              void *arg, grpc_error *error) {
-  glb_lb_policy *glb_policy = (glb_lb_policy *)arg;
-  glb_policy->initial_request_sent = true;
-  // If we attempted to send a client load report before the initial
-  // request was sent, send the load report now.
-  if (glb_policy->client_load_report_payload != NULL) {
-    do_send_client_load_report_locked(exec_ctx, glb_policy);
-  }
-  GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
-                            "lb_on_sent_initial_request_locked");
-}
-
 static void lb_on_response_received_locked(grpc_exec_ctx *exec_ctx, void *arg,
                                            grpc_error *error) {
   glb_lb_policy *glb_policy = (glb_lb_policy *)arg;
@@ -1457,7 +1545,7 @@
   memset(ops, 0, sizeof(ops));
   grpc_op *op = ops;
   if (glb_policy->lb_response_payload != NULL) {
-    gpr_backoff_reset(&glb_policy->lb_call_backoff_state);
+    grpc_backoff_reset(&glb_policy->lb_call_backoff_state);
     /* Received data from the LB server. Look inside
      * glb_policy->lb_response_payload, for a serverlist. */
     grpc_byte_buffer_reader bbr;
@@ -1471,16 +1559,14 @@
         (response = grpc_grpclb_initial_response_parse(response_slice)) !=
             NULL) {
       if (response->has_client_stats_report_interval) {
-        glb_policy->client_stats_report_interval =
-            gpr_time_max(gpr_time_from_seconds(1, GPR_TIMESPAN),
-                         grpc_grpclb_duration_to_timespec(
-                             &response->client_stats_report_interval));
+        glb_policy->client_stats_report_interval = GPR_MAX(
+            GPR_MS_PER_SEC, grpc_grpclb_duration_to_millis(
+                                &response->client_stats_report_interval));
         if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
           gpr_log(GPR_INFO,
                   "received initial LB response message; "
-                  "client load reporting interval = %" PRId64 ".%09d sec",
-                  glb_policy->client_stats_report_interval.tv_sec,
-                  glb_policy->client_stats_report_interval.tv_nsec);
+                  "client load reporting interval = %" PRIdPTR " milliseconds",
+                  glb_policy->client_stats_report_interval);
         }
         /* take a weak ref (won't prevent calling of \a glb_shutdown() if the
          * strong ref count goes to zero) to be unref'd in
@@ -1525,6 +1611,15 @@
             if (glb_policy->serverlist != NULL) {
               /* dispose of the old serverlist */
               grpc_grpclb_destroy_serverlist(glb_policy->serverlist);
+            } else {
+              /* or dispose of the fallback */
+              grpc_lb_addresses_destroy(exec_ctx,
+                                        glb_policy->fallback_backend_addresses);
+              glb_policy->fallback_backend_addresses = NULL;
+              if (glb_policy->fallback_timer_active) {
+                grpc_timer_cancel(exec_ctx, &glb_policy->lb_fallback_timer);
+                glb_policy->fallback_timer_active = false;
+              }
             }
             /* and update the copy in the glb_lb_policy instance. This
              * serverlist instance will be destroyed either upon the next
@@ -1535,9 +1630,7 @@
           }
         } else {
           if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
-            gpr_log(GPR_INFO,
-                    "Received empty server list. Picks will stay pending until "
-                    "a response with > 0 servers is received");
+            gpr_log(GPR_INFO, "Received empty server list, ignoring.");
           }
           grpc_grpclb_destroy_serverlist(serverlist);
         }
@@ -1572,19 +1665,25 @@
   }
 }
 
-static void lb_call_on_retry_timer_locked(grpc_exec_ctx *exec_ctx, void *arg,
-                                          grpc_error *error) {
+static void lb_on_fallback_timer_locked(grpc_exec_ctx *exec_ctx, void *arg,
+                                        grpc_error *error) {
   glb_lb_policy *glb_policy = (glb_lb_policy *)arg;
-  glb_policy->retry_timer_active = false;
-  if (!glb_policy->shutting_down && error == GRPC_ERROR_NONE) {
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
-      gpr_log(GPR_INFO, "Restaring call to LB server (grpclb %p)",
-              (void *)glb_policy);
+  glb_policy->fallback_timer_active = false;
+  /* If we receive a serverlist after the timer fires but before this callback
+   * actually runs, don't fall back. */
+  if (glb_policy->serverlist == NULL) {
+    if (!glb_policy->shutting_down && error == GRPC_ERROR_NONE) {
+      if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+        gpr_log(GPR_INFO,
+                "Falling back to use backends from resolver (grpclb %p)",
+                (void *)glb_policy);
+      }
+      GPR_ASSERT(glb_policy->fallback_backend_addresses != NULL);
+      rr_handover_locked(exec_ctx, glb_policy);
     }
-    GPR_ASSERT(glb_policy->lb_call == NULL);
-    query_for_backends_locked(exec_ctx, glb_policy);
   }
-  GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, "grpclb_retry_timer");
+  GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
+                            "grpclb_fallback_timer");
 }
 
 static void lb_on_server_status_received_locked(grpc_exec_ctx *exec_ctx,
@@ -1603,45 +1702,59 @@
   }
   /* We need to perform cleanups no matter what. */
   lb_call_destroy_locked(exec_ctx, glb_policy);
-  if (glb_policy->started_picking && glb_policy->updating_lb_call) {
-    if (glb_policy->retry_timer_active) {
-      grpc_timer_cancel(exec_ctx, &glb_policy->lb_call_retry_timer);
-    }
-    if (!glb_policy->shutting_down) start_picking_locked(exec_ctx, glb_policy);
-    glb_policy->updating_lb_call = false;
-  } else if (!glb_policy->shutting_down) {
-    /* if we aren't shutting down, restart the LB client call after some time */
-    gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
-    gpr_timespec next_try =
-        gpr_backoff_step(&glb_policy->lb_call_backoff_state, now);
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
-      gpr_log(GPR_DEBUG, "Connection to LB server lost (grpclb: %p)...",
-              (void *)glb_policy);
-      gpr_timespec timeout = gpr_time_sub(next_try, now);
-      if (gpr_time_cmp(timeout, gpr_time_0(timeout.clock_type)) > 0) {
-        gpr_log(GPR_DEBUG,
-                "... retry_timer_active in %" PRId64 ".%09d seconds.",
-                timeout.tv_sec, timeout.tv_nsec);
-      } else {
-        gpr_log(GPR_DEBUG, "... retry_timer_active immediately.");
-      }
-    }
-    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_locked, glb_policy,
-                      grpc_combiner_scheduler(glb_policy->base.combiner));
-    glb_policy->retry_timer_active = true;
-    grpc_timer_init(exec_ctx, &glb_policy->lb_call_retry_timer, next_try,
-                    &glb_policy->lb_on_call_retry, now);
+  // If the load report timer is still pending, we wait for it to be
+  // called before restarting the call.  Otherwise, we restart the call
+  // here.
+  if (!glb_policy->client_load_report_timer_pending) {
+    maybe_restart_lb_call(exec_ctx, glb_policy);
   }
-  GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base,
-                            "lb_on_server_status_received_locked");
+}
+
+static void fallback_update_locked(grpc_exec_ctx *exec_ctx,
+                                   glb_lb_policy *glb_policy,
+                                   const grpc_lb_addresses *addresses) {
+  GPR_ASSERT(glb_policy->fallback_backend_addresses != NULL);
+  grpc_lb_addresses_destroy(exec_ctx, glb_policy->fallback_backend_addresses);
+  glb_policy->fallback_backend_addresses =
+      extract_backend_addresses_locked(exec_ctx, addresses);
+  if (glb_policy->lb_fallback_timeout_ms > 0 &&
+      !glb_policy->fallback_timer_active) {
+    rr_handover_locked(exec_ctx, glb_policy);
+  }
 }
 
 static void glb_update_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
                               const grpc_lb_policy_args *args) {
   glb_lb_policy *glb_policy = (glb_lb_policy *)policy;
-  if (glb_policy->updating_lb_channel) {
+  const grpc_arg *arg =
+      grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
+  if (arg == NULL || arg->type != GRPC_ARG_POINTER) {
+    if (glb_policy->lb_channel == NULL) {
+      // If we don't have a current channel to the LB, go into TRANSIENT
+      // FAILURE.
+      grpc_connectivity_state_set(
+          exec_ctx, &glb_policy->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"),
+          "glb_update_missing");
+    } else {
+      // otherwise, keep using the current LB channel (ignore this update).
+      gpr_log(GPR_ERROR,
+              "No valid LB addresses channel arg for grpclb %p update, "
+              "ignoring.",
+              (void *)glb_policy);
+    }
+    return;
+  }
+  const grpc_lb_addresses *addresses =
+      (const grpc_lb_addresses *)arg->value.pointer.p;
+
+  if (glb_policy->serverlist == NULL) {
+    // If a non-empty serverlist hasn't been received from the balancer,
+    // propagate the update to fallback_backend_addresses.
+    fallback_update_locked(exec_ctx, glb_policy, addresses);
+  } else if (glb_policy->updating_lb_channel) {
+    // If we have recieved serverlist from the balancer, we need to defer update
+    // when there is an in-progress one.
     if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
       gpr_log(GPR_INFO,
               "Update already in progress for grpclb %p. Deferring update.",
@@ -1662,31 +1775,11 @@
   }
 
   glb_policy->updating_lb_channel = true;
-  // Propagate update to lb_channel (pick first).
-  const grpc_arg *arg =
-      grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
-  if (arg == NULL || arg->type != GRPC_ARG_POINTER) {
-    if (glb_policy->lb_channel == NULL) {
-      // If we don't have a current channel to the LB, go into TRANSIENT
-      // FAILURE.
-      grpc_connectivity_state_set(
-          exec_ctx, &glb_policy->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
-          GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"),
-          "glb_update_missing");
-    } else {
-      // otherwise, keep using the current LB channel (ignore this update).
-      gpr_log(GPR_ERROR,
-              "No valid LB addresses channel arg for grpclb %p update, "
-              "ignoring.",
-              (void *)glb_policy);
-    }
-  }
-  const grpc_lb_addresses *addresses =
-      (const grpc_lb_addresses *)arg->value.pointer.p;
   GPR_ASSERT(glb_policy->lb_channel != NULL);
   grpc_channel_args *lb_channel_args = build_lb_channel_args(
       exec_ctx, addresses, glb_policy->response_generator, args->args);
-  /* Propagate updates to the LB channel through the fake resolver */
+  /* Propagate updates to the LB channel (pick first) through the fake resolver
+   */
   grpc_fake_resolver_response_generator_set_response(
       exec_ctx, glb_policy->response_generator, lb_channel_args);
   grpc_channel_args_destroy(exec_ctx, lb_channel_args);
@@ -1789,13 +1882,7 @@
 static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx,
                                   grpc_lb_policy_factory *factory,
                                   grpc_lb_policy_args *args) {
-  /* Count the number of gRPC-LB addresses. There must be at least one.
-   * TODO(roth): For now, we ignore non-balancer addresses, but in the
-   * future, we may change the behavior such that we fall back to using
-   * the non-balancer addresses if we cannot reach any balancers. In the
-   * fallback case, we should use the LB policy indicated by
-   * GRPC_ARG_LB_POLICY_NAME (although if that specifies grpclb or is
-   * unset, we should default to pick_first). */
+  /* Count the number of gRPC-LB addresses. There must be at least one. */
   const grpc_arg *arg =
       grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES);
   if (arg == NULL || arg->type != GRPC_ARG_POINTER) {
@@ -1831,6 +1918,11 @@
   glb_policy->lb_call_timeout_ms =
       grpc_channel_arg_get_integer(arg, (grpc_integer_options){0, 0, INT_MAX});
 
+  arg = grpc_channel_args_find(args->args, GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS);
+  glb_policy->lb_fallback_timeout_ms = grpc_channel_arg_get_integer(
+      arg, (grpc_integer_options){GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS, 0,
+                                  INT_MAX});
+
   // 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 = grpc_channel_arg_string_create(
@@ -1839,6 +1931,11 @@
   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);
 
+  /* Extract the backend addresses (may be empty) from the resolver for
+   * fallback. */
+  glb_policy->fallback_backend_addresses =
+      extract_backend_addresses_locked(exec_ctx, addresses);
+
   /* Create a client channel over them to communicate with a LB service */
   glb_policy->response_generator =
       grpc_fake_resolver_response_generator_create();
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c
index 8ef6dfc..9c6fb94 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c
@@ -299,13 +299,10 @@
   return 0;
 }
 
-gpr_timespec grpc_grpclb_duration_to_timespec(
-    grpc_grpclb_duration *duration_pb) {
-  gpr_timespec duration;
-  duration.tv_sec = duration_pb->has_seconds ? duration_pb->seconds : 0;
-  duration.tv_nsec = duration_pb->has_nanos ? duration_pb->nanos : 0;
-  duration.clock_type = GPR_TIMESPAN;
-  return duration;
+grpc_millis grpc_grpclb_duration_to_millis(grpc_grpclb_duration *duration_pb) {
+  return (duration_pb->has_seconds ? duration_pb->seconds : 0) *
+             GPR_MS_PER_SEC +
+         (duration_pb->has_nanos ? duration_pb->nanos : 0) / GPR_NS_PER_MS;
 }
 
 void grpc_grpclb_initial_response_destroy(
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
index c4a9849..56b9c09 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h
@@ -81,8 +81,7 @@
 int grpc_grpclb_duration_compare(const grpc_grpclb_duration *lhs,
                                  const grpc_grpclb_duration *rhs);
 
-gpr_timespec grpc_grpclb_duration_to_timespec(
-    grpc_grpclb_duration *duration_pb);
+grpc_millis grpc_grpclb_duration_to_millis(grpc_grpclb_duration *duration_pb);
 
 /** Destroy \a initial_response */
 void grpc_grpclb_initial_response_destroy(
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 4d14054..05ab43d 100644
--- a/src/core/ext/filters/client_channel/lb_policy_factory.c
+++ b/src/core/ext/filters/client_channel/lb_policy_factory.c
@@ -56,7 +56,7 @@
 }
 
 void grpc_lb_addresses_set_address(grpc_lb_addresses* addresses, size_t index,
-                                   void* address, size_t address_len,
+                                   const void* address, size_t address_len,
                                    bool is_balancer, const char* balancer_name,
                                    void* user_data) {
   GPR_ASSERT(index < addresses->num_addresses);
diff --git a/src/core/ext/filters/client_channel/lb_policy_factory.h b/src/core/ext/filters/client_channel/lb_policy_factory.h
index 9d9fb14..cf0f8cb 100644
--- a/src/core/ext/filters/client_channel/lb_policy_factory.h
+++ b/src/core/ext/filters/client_channel/lb_policy_factory.h
@@ -73,7 +73,7 @@
  * \a address is a socket address of length \a address_len.
  * Takes ownership of \a balancer_name. */
 void grpc_lb_addresses_set_address(grpc_lb_addresses *addresses, size_t index,
-                                   void *address, size_t address_len,
+                                   const void *address, size_t address_len,
                                    bool is_balancer, const char *balancer_name,
                                    void *user_data);
 
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c
index 9bb229a..34132e6 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c
+++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c
@@ -32,13 +32,13 @@
 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h"
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/lib/backoff/backoff.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/gethostname.h"
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/json/json.h"
-#include "src/core/lib/support/backoff.h"
 #include "src/core/lib/support/env.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/service_config.h"
@@ -89,7 +89,7 @@
   bool have_retry_timer;
   grpc_timer retry_timer;
   /** retry backoff state */
-  gpr_backoff backoff_state;
+  grpc_backoff backoff_state;
 
   /** currently resolving addresses */
   grpc_lb_addresses *lb_addresses;
@@ -137,7 +137,7 @@
                                               grpc_resolver *resolver) {
   ares_dns_resolver *r = (ares_dns_resolver *)resolver;
   if (!r->resolving) {
-    gpr_backoff_reset(&r->backoff_state);
+    grpc_backoff_reset(&r->backoff_state);
     dns_ares_start_resolving_locked(exec_ctx, r);
   }
 }
@@ -271,22 +271,20 @@
   } else {
     const char *msg = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "dns resolution failed: %s", msg);
-    gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
-    gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now);
-    gpr_timespec timeout = gpr_time_sub(next_try, now);
+    grpc_millis next_try = grpc_backoff_step(exec_ctx, &r->backoff_state);
+    grpc_millis timeout = next_try - grpc_exec_ctx_now(exec_ctx);
     gpr_log(GPR_INFO, "dns resolution failed (will retry): %s",
             grpc_error_string(error));
     GPR_ASSERT(!r->have_retry_timer);
     r->have_retry_timer = true;
     GRPC_RESOLVER_REF(&r->base, "retry-timer");
-    if (gpr_time_cmp(timeout, gpr_time_0(timeout.clock_type)) > 0) {
-      gpr_log(GPR_DEBUG, "retrying in %" PRId64 ".%09d seconds", timeout.tv_sec,
-              timeout.tv_nsec);
+    if (timeout > 0) {
+      gpr_log(GPR_DEBUG, "retrying in %" PRIdPTR " milliseconds", timeout);
     } else {
       gpr_log(GPR_DEBUG, "retrying immediately");
     }
     grpc_timer_init(exec_ctx, &r->retry_timer, next_try,
-                    &r->dns_ares_on_retry_timer_locked, now);
+                    &r->dns_ares_on_retry_timer_locked);
   }
   if (r->resolved_result != NULL) {
     grpc_channel_args_destroy(exec_ctx, r->resolved_result);
@@ -307,7 +305,7 @@
   r->next_completion = on_complete;
   r->target_result = target_result;
   if (r->resolved_version == 0 && !r->resolving) {
-    gpr_backoff_reset(&r->backoff_state);
+    grpc_backoff_reset(&r->backoff_state);
     dns_ares_start_resolving_locked(exec_ctx, r);
   } else {
     dns_ares_maybe_finish_next_locked(exec_ctx, r);
@@ -381,11 +379,11 @@
     grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties,
                                      args->pollset_set);
   }
-  gpr_backoff_init(&r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS,
-                   GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER,
-                   GRPC_DNS_RECONNECT_JITTER,
-                   GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS * 1000,
-                   GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
+  grpc_backoff_init(&r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS,
+                    GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER,
+                    GRPC_DNS_RECONNECT_JITTER,
+                    GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS * 1000,
+                    GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
   GRPC_CLOSURE_INIT(&r->dns_ares_on_retry_timer_locked,
                     dns_ares_on_retry_timer_locked, r,
                     grpc_combiner_scheduler(r->base.combiner));
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c
index 7f1f572..c30cc93 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c
+++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c
@@ -20,6 +20,7 @@
 #if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET)
 
 #include <ares.h>
+#include <sys/ioctl.h>
 
 #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h"
 
@@ -37,8 +38,6 @@
 typedef struct fd_node {
   /** the owner of this fd node */
   grpc_ares_ev_driver *ev_driver;
-  /** the grpc_fd owned by this fd node */
-  grpc_fd *fd;
   /** a closure wrapping on_readable_cb, which should be invoked when the
       grpc_fd in this node becomes readable. */
   grpc_closure read_closure;
@@ -50,10 +49,14 @@
 
   /** mutex guarding the rest of the state */
   gpr_mu mu;
+  /** the grpc_fd owned by this fd node */
+  grpc_fd *fd;
   /** if the readable closure has been registered */
   bool readable_registered;
   /** if the writable closure has been registered */
   bool writable_registered;
+  /** if the fd is being shut down */
+  bool shutting_down;
 } fd_node;
 
 struct grpc_ares_ev_driver {
@@ -100,7 +103,6 @@
   GPR_ASSERT(!fdn->readable_registered);
   GPR_ASSERT(!fdn->writable_registered);
   gpr_mu_destroy(&fdn->mu);
-  grpc_pollset_set_del_fd(exec_ctx, fdn->ev_driver->pollset_set, fdn->fd);
   /* c-ares library has closed the fd inside grpc_fd. This fd may be picked up
      immediately by another thread, and should not be closed by the following
      grpc_fd_orphan. */
@@ -109,6 +111,19 @@
   gpr_free(fdn);
 }
 
+static void fd_node_shutdown(grpc_exec_ctx *exec_ctx, fd_node *fdn) {
+  gpr_mu_lock(&fdn->mu);
+  fdn->shutting_down = true;
+  if (!fdn->readable_registered && !fdn->writable_registered) {
+    gpr_mu_unlock(&fdn->mu);
+    fd_node_destroy(exec_ctx, fdn);
+  } else {
+    grpc_fd_shutdown(exec_ctx, fdn->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING(
+                                            "c-ares fd shutdown"));
+    gpr_mu_unlock(&fdn->mu);
+  }
+}
+
 grpc_error *grpc_ares_ev_driver_create(grpc_ares_ev_driver **ev_driver,
                                        grpc_pollset_set *pollset_set) {
   *ev_driver = (grpc_ares_ev_driver *)gpr_malloc(sizeof(grpc_ares_ev_driver));
@@ -175,18 +190,33 @@
   return NULL;
 }
 
+/* Check if \a fd is still readable */
+static bool grpc_ares_is_fd_still_readable(grpc_ares_ev_driver *ev_driver,
+                                           int fd) {
+  size_t bytes_available = 0;
+  return ioctl(fd, FIONREAD, &bytes_available) == 0 && bytes_available > 0;
+}
+
 static void on_readable_cb(grpc_exec_ctx *exec_ctx, void *arg,
                            grpc_error *error) {
   fd_node *fdn = (fd_node *)arg;
   grpc_ares_ev_driver *ev_driver = fdn->ev_driver;
   gpr_mu_lock(&fdn->mu);
+  const int fd = grpc_fd_wrapped_fd(fdn->fd);
   fdn->readable_registered = false;
+  if (fdn->shutting_down && !fdn->writable_registered) {
+    gpr_mu_unlock(&fdn->mu);
+    fd_node_destroy(exec_ctx, fdn);
+    grpc_ares_ev_driver_unref(ev_driver);
+    return;
+  }
   gpr_mu_unlock(&fdn->mu);
 
-  gpr_log(GPR_DEBUG, "readable on %d", grpc_fd_wrapped_fd(fdn->fd));
+  gpr_log(GPR_DEBUG, "readable on %d", fd);
   if (error == GRPC_ERROR_NONE) {
-    ares_process_fd(ev_driver->channel, grpc_fd_wrapped_fd(fdn->fd),
-                    ARES_SOCKET_BAD);
+    do {
+      ares_process_fd(ev_driver->channel, fd, ARES_SOCKET_BAD);
+    } while (grpc_ares_is_fd_still_readable(ev_driver, fd));
   } else {
     // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or
     // timed out. The pending lookups made on this ev_driver will be cancelled
@@ -207,13 +237,19 @@
   fd_node *fdn = (fd_node *)arg;
   grpc_ares_ev_driver *ev_driver = fdn->ev_driver;
   gpr_mu_lock(&fdn->mu);
+  const int fd = grpc_fd_wrapped_fd(fdn->fd);
   fdn->writable_registered = false;
+  if (fdn->shutting_down && !fdn->readable_registered) {
+    gpr_mu_unlock(&fdn->mu);
+    fd_node_destroy(exec_ctx, fdn);
+    grpc_ares_ev_driver_unref(ev_driver);
+    return;
+  }
   gpr_mu_unlock(&fdn->mu);
 
-  gpr_log(GPR_DEBUG, "writable on %d", grpc_fd_wrapped_fd(fdn->fd));
+  gpr_log(GPR_DEBUG, "writable on %d", fd);
   if (error == GRPC_ERROR_NONE) {
-    ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD,
-                    grpc_fd_wrapped_fd(fdn->fd));
+    ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD, fd);
   } else {
     // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or
     // timed out. The pending lookups made on this ev_driver will be cancelled
@@ -256,6 +292,7 @@
           fdn->ev_driver = ev_driver;
           fdn->readable_registered = false;
           fdn->writable_registered = false;
+          fdn->shutting_down = false;
           gpr_mu_init(&fdn->mu);
           GRPC_CLOSURE_INIT(&fdn->read_closure, on_readable_cb, fdn,
                             grpc_schedule_on_exec_ctx);
@@ -296,7 +333,7 @@
   while (ev_driver->fds != NULL) {
     fd_node *cur = ev_driver->fds;
     ev_driver->fds = ev_driver->fds->next;
-    fd_node_destroy(exec_ctx, cur);
+    fd_node_shutdown(exec_ctx, cur);
   }
   ev_driver->fds = new_list;
   // If the ev driver has no working fd, all the tasks are done.
diff --git a/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c b/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c
index 5ea75f0..e4bffbe 100644
--- a/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c
+++ b/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c
@@ -24,11 +24,11 @@
 
 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
 #include "src/core/ext/filters/client_channel/resolver_registry.h"
+#include "src/core/lib/backoff/backoff.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/iomgr/combiner.h"
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/iomgr/timer.h"
-#include "src/core/lib/support/backoff.h"
 #include "src/core/lib/support/env.h"
 #include "src/core/lib/support/string.h"
 
@@ -67,7 +67,7 @@
   grpc_timer retry_timer;
   grpc_closure on_retry;
   /** retry backoff state */
-  gpr_backoff backoff_state;
+  grpc_backoff backoff_state;
 
   /** currently resolving addresses */
   grpc_resolved_addresses *addresses;
@@ -110,7 +110,7 @@
                                          grpc_resolver *resolver) {
   dns_resolver *r = (dns_resolver *)resolver;
   if (!r->resolving) {
-    gpr_backoff_reset(&r->backoff_state);
+    grpc_backoff_reset(&r->backoff_state);
     dns_start_resolving_locked(exec_ctx, r);
   }
 }
@@ -123,7 +123,7 @@
   r->next_completion = on_complete;
   r->target_result = target_result;
   if (r->resolved_version == 0 && !r->resolving) {
-    gpr_backoff_reset(&r->backoff_state);
+    grpc_backoff_reset(&r->backoff_state);
     dns_start_resolving_locked(exec_ctx, r);
   } else {
     dns_maybe_finish_next_locked(exec_ctx, r);
@@ -150,6 +150,9 @@
   grpc_channel_args *result = NULL;
   GPR_ASSERT(r->resolving);
   r->resolving = false;
+  GRPC_ERROR_REF(error);
+  error = grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS,
+                             grpc_slice_from_copied_string(r->name_to_resolve));
   if (r->addresses != NULL) {
     grpc_lb_addresses *addresses = grpc_lb_addresses_create(
         r->addresses->naddrs, NULL /* user_data_vtable */);
@@ -164,23 +167,21 @@
     grpc_resolved_addresses_destroy(r->addresses);
     grpc_lb_addresses_destroy(exec_ctx, addresses);
   } else {
-    gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
-    gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now);
-    gpr_timespec timeout = gpr_time_sub(next_try, now);
+    grpc_millis next_try = grpc_backoff_step(exec_ctx, &r->backoff_state);
+    grpc_millis timeout = next_try - grpc_exec_ctx_now(exec_ctx);
     gpr_log(GPR_INFO, "dns resolution failed (will retry): %s",
             grpc_error_string(error));
     GPR_ASSERT(!r->have_retry_timer);
     r->have_retry_timer = true;
     GRPC_RESOLVER_REF(&r->base, "retry-timer");
-    if (gpr_time_cmp(timeout, gpr_time_0(timeout.clock_type)) > 0) {
-      gpr_log(GPR_DEBUG, "retrying in %" PRId64 ".%09d seconds", timeout.tv_sec,
-              timeout.tv_nsec);
+    if (timeout > 0) {
+      gpr_log(GPR_DEBUG, "retrying in %" PRIdPTR " milliseconds", timeout);
     } else {
       gpr_log(GPR_DEBUG, "retrying immediately");
     }
     GRPC_CLOSURE_INIT(&r->on_retry, dns_on_retry_timer_locked, r,
                       grpc_combiner_scheduler(r->base.combiner));
-    grpc_timer_init(exec_ctx, &r->retry_timer, next_try, &r->on_retry, now);
+    grpc_timer_init(exec_ctx, &r->retry_timer, next_try, &r->on_retry);
   }
   if (r->resolved_result != NULL) {
     grpc_channel_args_destroy(exec_ctx, r->resolved_result);
@@ -188,6 +189,7 @@
   r->resolved_result = result;
   r->resolved_version++;
   dns_maybe_finish_next_locked(exec_ctx, r);
+  GRPC_ERROR_UNREF(error);
 
   GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "dns-resolving");
 }
@@ -251,11 +253,11 @@
     grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties,
                                      args->pollset_set);
   }
-  gpr_backoff_init(&r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS,
-                   GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER,
-                   GRPC_DNS_RECONNECT_JITTER,
-                   GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS * 1000,
-                   GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
+  grpc_backoff_init(&r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS,
+                    GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER,
+                    GRPC_DNS_RECONNECT_JITTER,
+                    GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS * 1000,
+                    GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
   return &r->base;
 }
 
diff --git a/src/core/ext/filters/client_channel/subchannel.c b/src/core/ext/filters/client_channel/subchannel.c
index 40a51c7..bcc41de 100644
--- a/src/core/ext/filters/client_channel/subchannel.c
+++ b/src/core/ext/filters/client_channel/subchannel.c
@@ -30,6 +30,7 @@
 #include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
 #include "src/core/ext/filters/client_channel/subchannel_index.h"
 #include "src/core/ext/filters/client_channel/uri_parser.h"
+#include "src/core/lib/backoff/backoff.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/channel/connected_channel.h"
 #include "src/core/lib/debug/stats.h"
@@ -37,7 +38,6 @@
 #include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_internal.h"
-#include "src/core/lib/support/backoff.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/channel_init.h"
 #include "src/core/lib/transport/connectivity_state.h"
@@ -117,9 +117,9 @@
   external_state_watcher root_external_state_watcher;
 
   /** next connect attempt time */
-  gpr_timespec next_attempt;
+  grpc_millis next_attempt;
   /** backoff state */
-  gpr_backoff backoff_state;
+  grpc_backoff backoff_state;
   /** do we have an active alarm? */
   bool have_alarm;
   /** have we started the backoff loop */
@@ -367,7 +367,7 @@
       }
     }
   }
-  gpr_backoff_init(
+  grpc_backoff_init(
       &c->backoff_state, initial_backoff_ms,
       fixed_reconnect_backoff ? 1.0
                               : GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER,
@@ -431,8 +431,7 @@
   }
   if (error == GRPC_ERROR_NONE) {
     gpr_log(GPR_INFO, "Failed to connect to channel, retrying");
-    c->next_attempt =
-        gpr_backoff_step(&c->backoff_state, gpr_now(GPR_CLOCK_MONOTONIC));
+    c->next_attempt = grpc_backoff_step(exec_ctx, &c->backoff_state);
     continue_connect_locked(exec_ctx, c);
     gpr_mu_unlock(&c->mu);
   } else {
@@ -467,24 +466,22 @@
   c->connecting = true;
   GRPC_SUBCHANNEL_WEAK_REF(c, "connecting");
 
-  gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
   if (!c->backoff_begun) {
     c->backoff_begun = true;
-    c->next_attempt = gpr_backoff_begin(&c->backoff_state, now);
+    c->next_attempt = grpc_backoff_begin(exec_ctx, &c->backoff_state);
     continue_connect_locked(exec_ctx, c);
   } else {
     GPR_ASSERT(!c->have_alarm);
     c->have_alarm = true;
-    gpr_timespec time_til_next = gpr_time_sub(c->next_attempt, now);
-    if (gpr_time_cmp(time_til_next, gpr_time_0(time_til_next.clock_type)) <=
-        0) {
+    const grpc_millis time_til_next =
+        c->next_attempt - grpc_exec_ctx_now(exec_ctx);
+    if (time_til_next <= 0) {
       gpr_log(GPR_INFO, "Retry immediately");
     } else {
-      gpr_log(GPR_INFO, "Retry in %" PRId64 ".%09d seconds",
-              time_til_next.tv_sec, time_til_next.tv_nsec);
+      gpr_log(GPR_INFO, "Retry in %" PRIdPTR " milliseconds", time_til_next);
     }
     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);
+    grpc_timer_init(exec_ctx, &c->alarm, c->next_attempt, &c->on_alarm);
   }
 }
 
diff --git a/src/core/ext/filters/client_channel/subchannel.h b/src/core/ext/filters/client_channel/subchannel.h
index 51d712f..73702d5 100644
--- a/src/core/ext/filters/client_channel/subchannel.h
+++ b/src/core/ext/filters/client_channel/subchannel.h
@@ -103,7 +103,7 @@
   grpc_polling_entity *pollent;
   grpc_slice path;
   gpr_timespec start_time;
-  gpr_timespec deadline;
+  grpc_millis deadline;
   gpr_arena *arena;
   grpc_call_context_element *context;
   grpc_call_combiner *call_combiner;
diff --git a/src/core/ext/filters/deadline/deadline_filter.c b/src/core/ext/filters/deadline/deadline_filter.c
index 1aed488..2b2eac2 100644
--- a/src/core/ext/filters/deadline/deadline_filter.c
+++ b/src/core/ext/filters/deadline/deadline_filter.c
@@ -86,9 +86,8 @@
 // synchronized.
 static void start_timer_if_needed(grpc_exec_ctx* exec_ctx,
                                   grpc_call_element* elem,
-                                  gpr_timespec deadline) {
-  deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
-  if (gpr_time_cmp(deadline, gpr_inf_future(GPR_CLOCK_MONOTONIC)) == 0) {
+                                  grpc_millis deadline) {
+  if (deadline == GRPC_MILLIS_INF_FUTURE) {
     return;
   }
   grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data;
@@ -114,8 +113,7 @@
   }
   GPR_ASSERT(closure != NULL);
   GRPC_CALL_STACK_REF(deadline_state->call_stack, "deadline_timer");
-  grpc_timer_init(exec_ctx, &deadline_state->timer, deadline, closure,
-                  gpr_now(GPR_CLOCK_MONOTONIC));
+  grpc_timer_init(exec_ctx, &deadline_state->timer, deadline, closure);
 }
 
 // Cancels the deadline timer.
@@ -155,7 +153,7 @@
 struct start_timer_after_init_state {
   bool in_call_combiner;
   grpc_call_element* elem;
-  gpr_timespec deadline;
+  grpc_millis deadline;
   grpc_closure closure;
 };
 static void start_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg,
@@ -182,14 +180,13 @@
 void grpc_deadline_state_init(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
                               grpc_call_stack* call_stack,
                               grpc_call_combiner* call_combiner,
-                              gpr_timespec deadline) {
+                              grpc_millis deadline) {
   grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data;
   deadline_state->call_stack = call_stack;
   deadline_state->call_combiner = call_combiner;
   // Deadline will always be infinite on servers, so the timer will only be
   // set on clients with a finite deadline.
-  deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
-  if (gpr_time_cmp(deadline, gpr_inf_future(GPR_CLOCK_MONOTONIC)) != 0) {
+  if (deadline != GRPC_MILLIS_INF_FUTURE) {
     // When the deadline passes, we indicate the failure by sending down
     // an op with cancel_error set.  However, we can't send down any ops
     // until after the call stack is fully initialized.  If we start the
@@ -214,7 +211,7 @@
 }
 
 void grpc_deadline_state_reset(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
-                               gpr_timespec new_deadline) {
+                               grpc_millis new_deadline) {
   grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data;
   cancel_timer_if_needed(exec_ctx, deadline_state);
   start_timer_if_needed(exec_ctx, elem, new_deadline);
diff --git a/src/core/ext/filters/deadline/deadline_filter.h b/src/core/ext/filters/deadline/deadline_filter.h
index 3eb102a..8d835d0 100644
--- a/src/core/ext/filters/deadline/deadline_filter.h
+++ b/src/core/ext/filters/deadline/deadline_filter.h
@@ -52,7 +52,8 @@
 void grpc_deadline_state_init(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
                               grpc_call_stack* call_stack,
                               grpc_call_combiner* call_combiner,
-                              gpr_timespec deadline);
+                              grpc_millis deadline);
+
 void grpc_deadline_state_destroy(grpc_exec_ctx* exec_ctx,
                                  grpc_call_element* elem);
 
@@ -66,7 +67,7 @@
 //
 // Note: Must be called while holding the call combiner.
 void grpc_deadline_state_reset(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
-                               gpr_timespec new_deadline);
+                               grpc_millis new_deadline);
 
 // To be called from the client-side filter's start_transport_stream_op_batch()
 // method.  Ensures that the deadline timer is cancelled when the call
diff --git a/src/core/ext/filters/max_age/max_age_filter.c b/src/core/ext/filters/max_age/max_age_filter.c
index 0ac803e..b7032f2 100644
--- a/src/core/ext/filters/max_age/max_age_filter.c
+++ b/src/core/ext/filters/max_age/max_age_filter.c
@@ -56,11 +56,11 @@
      max_connection_idle */
   grpc_timer max_idle_timer;
   /* Allowed max time a channel may have no outstanding rpcs */
-  gpr_timespec max_connection_idle;
+  grpc_millis max_connection_idle;
   /* Allowed max time a channel may exist */
-  gpr_timespec max_connection_age;
+  grpc_millis max_connection_age;
   /* Allowed grace period after the channel reaches its max age */
-  gpr_timespec max_connection_age_grace;
+  grpc_millis max_connection_age_grace;
   /* Closure to run when the channel's idle duration reaches max_connection_idle
      and should be closed gracefully */
   grpc_closure close_max_idle_channel;
@@ -99,10 +99,9 @@
 static void decrease_call_count(grpc_exec_ctx* exec_ctx, channel_data* chand) {
   if (gpr_atm_full_fetch_add(&chand->call_count, -1) == 1) {
     GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_idle_timer");
-    grpc_timer_init(
-        exec_ctx, &chand->max_idle_timer,
-        gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), chand->max_connection_idle),
-        &chand->close_max_idle_channel, gpr_now(GPR_CLOCK_MONOTONIC));
+    grpc_timer_init(exec_ctx, &chand->max_idle_timer,
+                    grpc_exec_ctx_now(exec_ctx) + chand->max_connection_idle,
+                    &chand->close_max_idle_channel);
   }
 }
 
@@ -123,10 +122,9 @@
   gpr_mu_lock(&chand->max_age_timer_mu);
   chand->max_age_timer_pending = true;
   GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_age_timer");
-  grpc_timer_init(
-      exec_ctx, &chand->max_age_timer,
-      gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), chand->max_connection_age),
-      &chand->close_max_age_channel, gpr_now(GPR_CLOCK_MONOTONIC));
+  grpc_timer_init(exec_ctx, &chand->max_age_timer,
+                  grpc_exec_ctx_now(exec_ctx) + chand->max_connection_age,
+                  &chand->close_max_age_channel);
   gpr_mu_unlock(&chand->max_age_timer_mu);
   grpc_transport_op* op = grpc_make_transport_op(NULL);
   op->on_connectivity_state_change = &chand->channel_connectivity_changed,
@@ -144,11 +142,12 @@
   gpr_mu_lock(&chand->max_age_timer_mu);
   chand->max_age_grace_timer_pending = true;
   GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_age_grace_timer");
-  grpc_timer_init(exec_ctx, &chand->max_age_grace_timer,
-                  gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
-                               chand->max_connection_age_grace),
-                  &chand->force_close_max_age_channel,
-                  gpr_now(GPR_CLOCK_MONOTONIC));
+  grpc_timer_init(
+      exec_ctx, &chand->max_age_grace_timer,
+      chand->max_connection_age_grace == GRPC_MILLIS_INF_FUTURE
+          ? GRPC_MILLIS_INF_FUTURE
+          : grpc_exec_ctx_now(exec_ctx) + chand->max_connection_age_grace,
+      &chand->force_close_max_age_channel);
   gpr_mu_unlock(&chand->max_age_timer_mu);
   GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack,
                            "max_age start_max_age_grace_timer_after_goaway_op");
@@ -249,7 +248,8 @@
    connection storms. Note that the MAX_CONNECTION_AGE option without jitter
    would not create connection storms by itself, but if there happened to be a
    connection storm it could cause it to repeat at a fixed period. */
-static int add_random_max_connection_age_jitter(int value) {
+static grpc_millis
+add_random_max_connection_age_jitter_and_convert_to_grpc_millis(int value) {
   /* generate a random number between 1 - MAX_CONNECTION_AGE_JITTER and
      1 + MAX_CONNECTION_AGE_JITTER */
   double multiplier = rand() * MAX_CONNECTION_AGE_JITTER * 2.0 / RAND_MAX +
@@ -257,7 +257,9 @@
   double result = multiplier * value;
   /* INT_MAX - 0.5 converts the value to float, so that result will not be
      cast to int implicitly before the comparison. */
-  return result > INT_MAX - 0.5 ? INT_MAX : (int)result;
+  return result > ((double)GRPC_MILLIS_INF_FUTURE) - 0.5
+             ? GRPC_MILLIS_INF_FUTURE
+             : (grpc_millis)result;
 }
 
 /* Constructor for call_data. */
@@ -287,30 +289,23 @@
   chand->max_age_grace_timer_pending = false;
   chand->channel_stack = args->channel_stack;
   chand->max_connection_age =
-      DEFAULT_MAX_CONNECTION_AGE_MS == INT_MAX
-          ? gpr_inf_future(GPR_TIMESPAN)
-          : gpr_time_from_millis(add_random_max_connection_age_jitter(
-                                     DEFAULT_MAX_CONNECTION_AGE_MS),
-                                 GPR_TIMESPAN);
+      add_random_max_connection_age_jitter_and_convert_to_grpc_millis(
+          DEFAULT_MAX_CONNECTION_AGE_MS);
   chand->max_connection_age_grace =
       DEFAULT_MAX_CONNECTION_AGE_GRACE_MS == INT_MAX
-          ? gpr_inf_future(GPR_TIMESPAN)
-          : gpr_time_from_millis(DEFAULT_MAX_CONNECTION_AGE_GRACE_MS,
-                                 GPR_TIMESPAN);
-  chand->max_connection_idle =
-      DEFAULT_MAX_CONNECTION_IDLE_MS == INT_MAX
-          ? gpr_inf_future(GPR_TIMESPAN)
-          : gpr_time_from_millis(DEFAULT_MAX_CONNECTION_IDLE_MS, GPR_TIMESPAN);
+          ? GRPC_MILLIS_INF_FUTURE
+          : DEFAULT_MAX_CONNECTION_AGE_GRACE_MS;
+  chand->max_connection_idle = DEFAULT_MAX_CONNECTION_IDLE_MS == INT_MAX
+                                   ? GRPC_MILLIS_INF_FUTURE
+                                   : DEFAULT_MAX_CONNECTION_IDLE_MS;
   for (size_t i = 0; i < args->channel_args->num_args; ++i) {
     if (0 == strcmp(args->channel_args->args[i].key,
                     GRPC_ARG_MAX_CONNECTION_AGE_MS)) {
       const int value = grpc_channel_arg_get_integer(
           &args->channel_args->args[i], MAX_CONNECTION_AGE_INTEGER_OPTIONS);
       chand->max_connection_age =
-          value == INT_MAX
-              ? gpr_inf_future(GPR_TIMESPAN)
-              : gpr_time_from_millis(
-                    add_random_max_connection_age_jitter(value), GPR_TIMESPAN);
+          add_random_max_connection_age_jitter_and_convert_to_grpc_millis(
+              value);
     } else if (0 == strcmp(args->channel_args->args[i].key,
                            GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS)) {
       const int value = grpc_channel_arg_get_integer(
@@ -318,15 +313,13 @@
           (grpc_integer_options){DEFAULT_MAX_CONNECTION_AGE_GRACE_MS, 0,
                                  INT_MAX});
       chand->max_connection_age_grace =
-          value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN)
-                           : gpr_time_from_millis(value, GPR_TIMESPAN);
+          value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value;
     } else if (0 == strcmp(args->channel_args->args[i].key,
                            GRPC_ARG_MAX_CONNECTION_IDLE_MS)) {
       const int value = grpc_channel_arg_get_integer(
           &args->channel_args->args[i], MAX_CONNECTION_IDLE_INTEGER_OPTIONS);
       chand->max_connection_idle =
-          value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN)
-                           : gpr_time_from_millis(value, GPR_TIMESPAN);
+          value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value;
     }
   }
   GRPC_CLOSURE_INIT(&chand->close_max_idle_channel, close_max_idle_channel,
@@ -349,8 +342,7 @@
                     channel_connectivity_changed, chand,
                     grpc_schedule_on_exec_ctx);
 
-  if (gpr_time_cmp(chand->max_connection_age, gpr_inf_future(GPR_TIMESPAN)) !=
-      0) {
+  if (chand->max_connection_age != GRPC_MILLIS_INF_FUTURE) {
     /* When the channel reaches its max age, we send down an op with
        goaway_error set.  However, we can't send down any ops until after the
        channel stack is fully initialized.  If we start the timer here, we have
@@ -367,8 +359,7 @@
   /* Initialize the number of calls as 1, so that the max_idle_timer will not
      start until start_max_idle_timer_after_init is invoked. */
   gpr_atm_rel_store(&chand->call_count, 1);
-  if (gpr_time_cmp(chand->max_connection_idle, gpr_inf_future(GPR_TIMESPAN)) !=
-      0) {
+  if (chand->max_connection_idle != GRPC_MILLIS_INF_FUTURE) {
     GRPC_CHANNEL_STACK_REF(chand->channel_stack,
                            "max_age start_max_idle_timer_after_init");
     GRPC_CLOSURE_SCHED(exec_ctx, &chand->start_max_idle_timer_after_init,
diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.c b/src/core/ext/transport/chttp2/server/chttp2_server.c
index 60244e1..4694375 100644
--- a/src/core/ext/transport/chttp2/server/chttp2_server.c
+++ b/src/core/ext/transport/chttp2/server/chttp2_server.c
@@ -133,8 +133,8 @@
                        connection_state->handshake_mgr);
   // TODO(roth): We should really get this timeout value from channel
   // args instead of hard-coding it.
-  const gpr_timespec deadline = gpr_time_add(
-      gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(120, GPR_TIMESPAN));
+  const grpc_millis deadline =
+      grpc_exec_ctx_now(exec_ctx) + 120 * GPR_MS_PER_SEC;
   grpc_handshake_manager_do_handshake(exec_ctx, connection_state->handshake_mgr,
                                       tcp, state->args, deadline, acceptor,
                                       on_handshake_done, connection_state);
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index 1186cfc..1c575ba 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -357,35 +357,27 @@
                        GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA, 1);
 
   t->ping_policy.max_pings_without_data = g_default_max_pings_without_data;
-  t->ping_policy.min_sent_ping_interval_without_data = gpr_time_from_millis(
-      g_default_min_sent_ping_interval_without_data_ms, GPR_TIMESPAN);
+  t->ping_policy.min_sent_ping_interval_without_data =
+      g_default_min_sent_ping_interval_without_data_ms;
   t->ping_policy.max_ping_strikes = g_default_max_ping_strikes;
-  t->ping_policy.min_recv_ping_interval_without_data = gpr_time_from_millis(
-      g_default_min_recv_ping_interval_without_data_ms, GPR_TIMESPAN);
+  t->ping_policy.min_recv_ping_interval_without_data =
+      g_default_min_recv_ping_interval_without_data_ms;
 
   /* Keepalive setting */
   if (t->is_client) {
-    t->keepalive_time =
-        g_default_client_keepalive_time_ms == INT_MAX
-            ? gpr_inf_future(GPR_TIMESPAN)
-            : gpr_time_from_millis(g_default_client_keepalive_time_ms,
-                                   GPR_TIMESPAN);
-    t->keepalive_timeout =
-        g_default_client_keepalive_timeout_ms == INT_MAX
-            ? gpr_inf_future(GPR_TIMESPAN)
-            : gpr_time_from_millis(g_default_client_keepalive_timeout_ms,
-                                   GPR_TIMESPAN);
+    t->keepalive_time = g_default_client_keepalive_time_ms == INT_MAX
+                            ? GRPC_MILLIS_INF_FUTURE
+                            : g_default_client_keepalive_time_ms;
+    t->keepalive_timeout = g_default_client_keepalive_timeout_ms == INT_MAX
+                               ? GRPC_MILLIS_INF_FUTURE
+                               : g_default_client_keepalive_timeout_ms;
   } else {
-    t->keepalive_time =
-        g_default_server_keepalive_time_ms == INT_MAX
-            ? gpr_inf_future(GPR_TIMESPAN)
-            : gpr_time_from_millis(g_default_server_keepalive_time_ms,
-                                   GPR_TIMESPAN);
-    t->keepalive_timeout =
-        g_default_server_keepalive_timeout_ms == INT_MAX
-            ? gpr_inf_future(GPR_TIMESPAN)
-            : gpr_time_from_millis(g_default_server_keepalive_timeout_ms,
-                                   GPR_TIMESPAN);
+    t->keepalive_time = g_default_server_keepalive_time_ms == INT_MAX
+                            ? GRPC_MILLIS_INF_FUTURE
+                            : g_default_server_keepalive_time_ms;
+    t->keepalive_timeout = g_default_server_keepalive_timeout_ms == INT_MAX
+                               ? GRPC_MILLIS_INF_FUTURE
+                               : g_default_server_keepalive_timeout_ms;
   }
   t->keepalive_permit_without_calls = g_default_keepalive_permit_without_calls;
 
@@ -432,25 +424,21 @@
                      channel_args->args[i].key,
                      GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS)) {
         t->ping_policy.min_sent_ping_interval_without_data =
-            gpr_time_from_millis(
-                grpc_channel_arg_get_integer(
-                    &channel_args->args[i],
-                    (grpc_integer_options){
-                        g_default_min_sent_ping_interval_without_data_ms, 0,
-                        INT_MAX}),
-                GPR_TIMESPAN);
+            grpc_channel_arg_get_integer(
+                &channel_args->args[i],
+                (grpc_integer_options){
+                    g_default_min_sent_ping_interval_without_data_ms, 0,
+                    INT_MAX});
       } else if (0 ==
                  strcmp(
                      channel_args->args[i].key,
                      GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS)) {
         t->ping_policy.min_recv_ping_interval_without_data =
-            gpr_time_from_millis(
-                grpc_channel_arg_get_integer(
-                    &channel_args->args[i],
-                    (grpc_integer_options){
-                        g_default_min_recv_ping_interval_without_data_ms, 0,
-                        INT_MAX}),
-                GPR_TIMESPAN);
+            grpc_channel_arg_get_integer(
+                &channel_args->args[i],
+                (grpc_integer_options){
+                    g_default_min_recv_ping_interval_without_data_ms, 0,
+                    INT_MAX});
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE)) {
         t->write_buffer_size = (uint32_t)grpc_channel_arg_get_integer(
@@ -468,9 +456,7 @@
                                        ? g_default_client_keepalive_time_ms
                                        : g_default_server_keepalive_time_ms,
                                    1, INT_MAX});
-        t->keepalive_time = value == INT_MAX
-                                ? gpr_inf_future(GPR_TIMESPAN)
-                                : gpr_time_from_millis(value, GPR_TIMESPAN);
+        t->keepalive_time = value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value;
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) {
         const int value = grpc_channel_arg_get_integer(
@@ -479,9 +465,8 @@
                                        ? g_default_client_keepalive_timeout_ms
                                        : g_default_server_keepalive_timeout_ms,
                                    0, INT_MAX});
-        t->keepalive_timeout = value == INT_MAX
-                                   ? gpr_inf_future(GPR_TIMESPAN)
-                                   : gpr_time_from_millis(value, GPR_TIMESPAN);
+        t->keepalive_timeout =
+            value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value;
       } else if (0 == strcmp(channel_args->args[i].key,
                              GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)) {
         t->keepalive_permit_without_calls =
@@ -561,17 +546,16 @@
   t->ping_state.pings_before_data_required = 0;
   t->ping_state.is_delayed_ping_timer_set = false;
 
-  t->ping_recv_state.last_ping_recv_time = gpr_inf_past(GPR_CLOCK_MONOTONIC);
+  t->ping_recv_state.last_ping_recv_time = GRPC_MILLIS_INF_PAST;
   t->ping_recv_state.ping_strikes = 0;
 
   /* Start keepalive pings */
-  if (gpr_time_cmp(t->keepalive_time, gpr_inf_future(GPR_TIMESPAN)) != 0) {
+  if (t->keepalive_time != GRPC_MILLIS_INF_FUTURE) {
     t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING;
     GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping");
-    grpc_timer_init(
-        exec_ctx, &t->keepalive_ping_timer,
-        gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time),
-        &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC));
+    grpc_timer_init(exec_ctx, &t->keepalive_ping_timer,
+                    grpc_exec_ctx_now(exec_ctx) + t->keepalive_time,
+                    &t->init_keepalive_ping_locked);
   } else {
     /* Use GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED to indicate there are no
        inflight keeaplive timers */
@@ -579,7 +563,8 @@
   }
 
   grpc_chttp2_act_on_flowctl_action(
-      exec_ctx, grpc_chttp2_flowctl_get_action(&t->flow_control, NULL), t,
+      exec_ctx,
+      grpc_chttp2_flowctl_get_action(exec_ctx, &t->flow_control, NULL), t,
       NULL);
 
   grpc_chttp2_initiate_write(exec_ctx, t,
@@ -692,12 +677,15 @@
   grpc_chttp2_incoming_metadata_buffer_init(&s->metadata_buffer[1], arena);
   grpc_chttp2_data_parser_init(&s->data_parser);
   grpc_slice_buffer_init(&s->flow_controlled_buffer);
-  s->deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  s->deadline = GRPC_MILLIS_INF_FUTURE;
   GRPC_CLOSURE_INIT(&s->complete_fetch_locked, complete_fetch_locked, s,
                     grpc_schedule_on_exec_ctx);
   grpc_slice_buffer_init(&s->unprocessed_incoming_frames_buffer);
   grpc_slice_buffer_init(&s->frame_storage);
+  grpc_slice_buffer_init(&s->compressed_data_buffer);
+  grpc_slice_buffer_init(&s->decompressed_data_buffer);
   s->pending_byte_stream = false;
+  s->decompressed_header_bytes = 0;
   GRPC_CLOSURE_INIT(&s->reset_byte_stream, reset_byte_stream, s,
                     grpc_combiner_scheduler(t->combiner));
 
@@ -731,14 +719,8 @@
   grpc_slice_buffer_destroy_internal(exec_ctx,
                                      &s->unprocessed_incoming_frames_buffer);
   grpc_slice_buffer_destroy_internal(exec_ctx, &s->frame_storage);
-  if (s->compressed_data_buffer) {
-    grpc_slice_buffer_destroy_internal(exec_ctx, s->compressed_data_buffer);
-    gpr_free(s->compressed_data_buffer);
-  }
-  if (s->decompressed_data_buffer) {
-    grpc_slice_buffer_destroy_internal(exec_ctx, s->decompressed_data_buffer);
-    gpr_free(s->decompressed_data_buffer);
-  }
+  grpc_slice_buffer_destroy_internal(exec_ctx, &s->compressed_data_buffer);
+  grpc_slice_buffer_destroy_internal(exec_ctx, &s->decompressed_data_buffer);
 
   grpc_chttp2_list_remove_stalled_by_transport(t, s);
   grpc_chttp2_list_remove_stalled_by_stream(t, s);
@@ -1135,14 +1117,12 @@
     gpr_log(GPR_ERROR,
             "Received a GOAWAY with error code ENHANCE_YOUR_CALM and debug "
             "data equal to \"too_many_pings\"");
-    double current_keepalive_time_ms =
-        gpr_timespec_to_micros(t->keepalive_time) / 1000;
+    double current_keepalive_time_ms = (double)t->keepalive_time;
     t->keepalive_time =
         current_keepalive_time_ms > INT_MAX / KEEPALIVE_TIME_BACKOFF_MULTIPLIER
-            ? gpr_inf_future(GPR_TIMESPAN)
-            : gpr_time_from_millis((int64_t)(current_keepalive_time_ms *
-                                             KEEPALIVE_TIME_BACKOFF_MULTIPLIER),
-                                   GPR_TIMESPAN);
+            ? GRPC_MILLIS_INF_FUTURE
+            : (grpc_millis)(current_keepalive_time_ms *
+                            KEEPALIVE_TIME_BACKOFF_MULTIPLIER);
   }
 
   /* lie: use transient failure from the transport to indicate goaway has been
@@ -1437,12 +1417,14 @@
     on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE;
 
     /* Identify stream compression */
-    if ((s->stream_compression_send_enabled =
-             (op_payload->send_initial_metadata.send_initial_metadata->idx.named
-                  .content_encoding != NULL)) == true) {
-      s->compressed_data_buffer =
-          (grpc_slice_buffer *)gpr_malloc(sizeof(grpc_slice_buffer));
-      grpc_slice_buffer_init(s->compressed_data_buffer);
+    if (op_payload->send_initial_metadata.send_initial_metadata->idx.named
+                .content_encoding == NULL ||
+        grpc_stream_compression_method_parse(
+            GRPC_MDVALUE(
+                op_payload->send_initial_metadata.send_initial_metadata->idx
+                    .named.content_encoding->md),
+            true, &s->stream_compression_method) == 0) {
+      s->stream_compression_method = GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS;
     }
 
     s->send_initial_metadata_finished = add_closure_barrier(on_complete);
@@ -1454,8 +1436,7 @@
         t->settings[GRPC_PEER_SETTINGS]
                    [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE];
     if (t->is_client) {
-      s->deadline =
-          gpr_time_min(s->deadline, s->send_initial_metadata->deadline);
+      s->deadline = GPR_MIN(s->deadline, s->send_initial_metadata->deadline);
     }
     if (metadata_size > metadata_peer_limit) {
       grpc_chttp2_cancel_stream(
@@ -1639,8 +1620,8 @@
             &t->flow_control, &s->flow_control, GRPC_HEADER_SIZE_IN_BYTES,
             already_received);
         grpc_chttp2_act_on_flowctl_action(
-            exec_ctx,
-            grpc_chttp2_flowctl_get_action(&t->flow_control, &s->flow_control),
+            exec_ctx, grpc_chttp2_flowctl_get_action(exec_ctx, &t->flow_control,
+                                                     &s->flow_control),
             t, s);
       }
     }
@@ -1673,16 +1654,14 @@
 
   if (!t->is_client) {
     if (op->send_initial_metadata) {
-      gpr_timespec deadline =
+      grpc_millis deadline =
           op->payload->send_initial_metadata.send_initial_metadata->deadline;
-      GPR_ASSERT(0 ==
-                 gpr_time_cmp(gpr_inf_future(deadline.clock_type), deadline));
+      GPR_ASSERT(deadline == GRPC_MILLIS_INF_FUTURE);
     }
     if (op->send_trailing_metadata) {
-      gpr_timespec deadline =
+      grpc_millis deadline =
           op->payload->send_trailing_metadata.send_trailing_metadata->deadline;
-      GPR_ASSERT(0 ==
-                 gpr_time_cmp(gpr_inf_future(deadline.clock_type), deadline));
+      GPR_ASSERT(deadline == GRPC_MILLIS_INF_FUTURE);
     }
   }
 
@@ -1754,8 +1733,8 @@
   t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED;
   grpc_http2_error_code http_error;
   grpc_slice slice;
-  grpc_error_get_status(error, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL,
-                        &slice, &http_error);
+  grpc_error_get_status(exec_ctx, error, GRPC_MILLIS_INF_FUTURE, NULL, &slice,
+                        &http_error);
   grpc_chttp2_goaway_append(t->last_new_stream_id, (uint32_t)http_error,
                             grpc_slice_ref_internal(slice), &t->qbuf);
   grpc_chttp2_initiate_write(exec_ctx, t,
@@ -1765,7 +1744,7 @@
 
 void grpc_chttp2_add_ping_strike(grpc_exec_ctx *exec_ctx,
                                  grpc_chttp2_transport *t) {
-  gpr_log(GPR_DEBUG, "PING strike");
+  t->ping_recv_state.ping_strikes++;
   if (++t->ping_recv_state.ping_strikes > t->ping_policy.max_ping_strikes &&
       t->ping_policy.max_ping_strikes != 0) {
     send_goaway(exec_ctx, t,
@@ -1883,20 +1862,20 @@
                                  &s->frame_storage);
           s->unprocessed_incoming_frames_decompressed = false;
         }
-        if (s->stream_compression_recv_enabled &&
-            !s->unprocessed_incoming_frames_decompressed) {
-          GPR_ASSERT(s->decompressed_data_buffer->length == 0);
+        if (!s->unprocessed_incoming_frames_decompressed) {
+          GPR_ASSERT(s->decompressed_data_buffer.length == 0);
           bool end_of_context;
           if (!s->stream_decompression_ctx) {
             s->stream_decompression_ctx =
                 grpc_stream_compression_context_create(
-                    GRPC_STREAM_COMPRESSION_DECOMPRESS);
+                    s->stream_decompression_method);
           }
-          if (!grpc_stream_decompress(s->stream_decompression_ctx,
-                                      &s->unprocessed_incoming_frames_buffer,
-                                      s->decompressed_data_buffer, NULL,
-                                      GRPC_HEADER_SIZE_IN_BYTES,
-                                      &end_of_context)) {
+          if (!grpc_stream_decompress(
+                  s->stream_decompression_ctx,
+                  &s->unprocessed_incoming_frames_buffer,
+                  &s->decompressed_data_buffer, NULL,
+                  GRPC_HEADER_SIZE_IN_BYTES - s->decompressed_header_bytes,
+                  &end_of_context)) {
             grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
                                                        &s->frame_storage);
             grpc_slice_buffer_reset_and_unref_internal(
@@ -1904,9 +1883,13 @@
             error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
                 "Stream decompression error.");
           } else {
+            s->decompressed_header_bytes += s->decompressed_data_buffer.length;
+            if (s->decompressed_header_bytes == GRPC_HEADER_SIZE_IN_BYTES) {
+              s->decompressed_header_bytes = 0;
+            }
             error = grpc_deframe_unprocessed_incoming_frames(
-                exec_ctx, &s->data_parser, s, s->decompressed_data_buffer, NULL,
-                s->recv_message);
+                exec_ctx, &s->data_parser, s, &s->decompressed_data_buffer,
+                NULL, s->recv_message);
             if (end_of_context) {
               grpc_stream_compression_context_destroy(
                   s->stream_decompression_ctx);
@@ -1955,15 +1938,14 @@
     }
     bool pending_data = s->pending_byte_stream ||
                         s->unprocessed_incoming_frames_buffer.length > 0;
-    if (s->stream_compression_recv_enabled && s->read_closed &&
-        s->frame_storage.length > 0 && !pending_data && !s->seen_error &&
-        s->recv_trailing_metadata_finished != NULL) {
+    if (s->read_closed && s->frame_storage.length > 0 && !pending_data &&
+        !s->seen_error && s->recv_trailing_metadata_finished != NULL) {
       /* Maybe some SYNC_FLUSH data is left in frame_storage. Consume them and
        * maybe decompress the next 5 bytes in the stream. */
       bool end_of_context;
       if (!s->stream_decompression_ctx) {
         s->stream_decompression_ctx = grpc_stream_compression_context_create(
-            GRPC_STREAM_COMPRESSION_DECOMPRESS);
+            s->stream_decompression_method);
       }
       if (!grpc_stream_decompress(s->stream_decompression_ctx,
                                   &s->frame_storage,
@@ -1976,6 +1958,7 @@
       } else {
         if (s->unprocessed_incoming_frames_buffer.length > 0) {
           s->unprocessed_incoming_frames_decompressed = true;
+          pending_data = true;
         }
         if (end_of_context) {
           grpc_stream_compression_context_destroy(s->stream_decompression_ctx);
@@ -2048,7 +2031,8 @@
   if (!s->read_closed || !s->write_closed) {
     if (s->id != 0) {
       grpc_http2_error_code http_error;
-      grpc_error_get_status(due_to_error, s->deadline, NULL, NULL, &http_error);
+      grpc_error_get_status(exec_ctx, due_to_error, s->deadline, NULL, NULL,
+                            &http_error);
       grpc_slice_buffer_add(
           &t->qbuf, grpc_chttp2_rst_stream_create(s->id, (uint32_t)http_error,
                                                   &s->stats.outgoing));
@@ -2066,7 +2050,7 @@
                              grpc_chttp2_stream *s, grpc_error *error) {
   grpc_status_code status;
   grpc_slice slice;
-  grpc_error_get_status(error, s->deadline, &status, &slice, NULL);
+  grpc_error_get_status(exec_ctx, error, s->deadline, &status, &slice, NULL);
 
   if (status != GRPC_STATUS_OK) {
     s->seen_error = true;
@@ -2231,7 +2215,8 @@
   uint32_t len = 0;
   grpc_status_code grpc_status;
   grpc_slice slice;
-  grpc_error_get_status(error, s->deadline, &grpc_status, &slice, NULL);
+  grpc_error_get_status(exec_ctx, error, s->deadline, &grpc_status, &slice,
+                        NULL);
 
   GPR_ASSERT(grpc_status >= 0 && (int)grpc_status < 100);
 
@@ -2557,7 +2542,8 @@
     grpc_endpoint_read(exec_ctx, t->ep, &t->read_buffer,
                        &t->read_action_locked);
     grpc_chttp2_act_on_flowctl_action(
-        exec_ctx, grpc_chttp2_flowctl_get_action(&t->flow_control, NULL), t,
+        exec_ctx,
+        grpc_chttp2_flowctl_get_action(exec_ctx, &t->flow_control, NULL), t,
         NULL);
     GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keep_reading");
   } else {
@@ -2680,18 +2666,16 @@
                                  GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING);
     } else {
       GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping");
-      grpc_timer_init(
-          exec_ctx, &t->keepalive_ping_timer,
-          gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time),
-          &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC));
+      grpc_timer_init(exec_ctx, &t->keepalive_ping_timer,
+                      grpc_exec_ctx_now(exec_ctx) + t->keepalive_time,
+                      &t->init_keepalive_ping_locked);
     }
   } else if (error == GRPC_ERROR_CANCELLED) {
     /* The keepalive ping timer may be cancelled by bdp */
     GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping");
-    grpc_timer_init(
-        exec_ctx, &t->keepalive_ping_timer,
-        gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time),
-        &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC));
+    grpc_timer_init(exec_ctx, &t->keepalive_ping_timer,
+                    grpc_exec_ctx_now(exec_ctx) + t->keepalive_time,
+                    &t->init_keepalive_ping_locked);
   }
   GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "init keepalive ping");
 }
@@ -2700,10 +2684,9 @@
                                         grpc_error *error) {
   grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg;
   GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive watchdog");
-  grpc_timer_init(
-      exec_ctx, &t->keepalive_watchdog_timer,
-      gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_timeout),
-      &t->keepalive_watchdog_fired_locked, gpr_now(GPR_CLOCK_MONOTONIC));
+  grpc_timer_init(exec_ctx, &t->keepalive_watchdog_timer,
+                  grpc_exec_ctx_now(exec_ctx) + t->keepalive_time,
+                  &t->keepalive_watchdog_fired_locked);
 }
 
 static void finish_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg,
@@ -2714,10 +2697,9 @@
       t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING;
       grpc_timer_cancel(exec_ctx, &t->keepalive_watchdog_timer);
       GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping");
-      grpc_timer_init(
-          exec_ctx, &t->keepalive_ping_timer,
-          gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time),
-          &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC));
+      grpc_timer_init(exec_ctx, &t->keepalive_ping_timer,
+                      grpc_exec_ctx_now(exec_ctx) + t->keepalive_time,
+                      &t->init_keepalive_ping_locked);
     }
   }
   GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keepalive ping end");
@@ -2792,7 +2774,7 @@
     GRPC_ERROR_UNREF(s->byte_stream_error);
     s->byte_stream_error = GRPC_ERROR_NONE;
     grpc_chttp2_cancel_stream(exec_ctx, s->t, s, GRPC_ERROR_REF(error));
-    s->byte_stream_error = error;
+    s->byte_stream_error = GRPC_ERROR_REF(error);
   }
 }
 
@@ -2817,9 +2799,9 @@
                                            bs->next_action.max_size_hint,
                                            cur_length);
     grpc_chttp2_act_on_flowctl_action(
-        exec_ctx,
-        grpc_chttp2_flowctl_get_action(&t->flow_control, &s->flow_control), t,
-        s);
+        exec_ctx, grpc_chttp2_flowctl_get_action(exec_ctx, &t->flow_control,
+                                                 &s->flow_control),
+        t, s);
   }
   GPR_ASSERT(s->unprocessed_incoming_frames_buffer.length == 0);
   if (s->frame_storage.length > 0) {
@@ -2890,24 +2872,23 @@
   grpc_error *error;
 
   if (s->unprocessed_incoming_frames_buffer.length > 0) {
-    if (s->stream_compression_recv_enabled &&
-        !s->unprocessed_incoming_frames_decompressed) {
+    if (!s->unprocessed_incoming_frames_decompressed) {
       bool end_of_context;
       if (!s->stream_decompression_ctx) {
         s->stream_decompression_ctx = grpc_stream_compression_context_create(
-            GRPC_STREAM_COMPRESSION_DECOMPRESS);
+            s->stream_decompression_method);
       }
       if (!grpc_stream_decompress(s->stream_decompression_ctx,
                                   &s->unprocessed_incoming_frames_buffer,
-                                  s->decompressed_data_buffer, NULL, MAX_SIZE_T,
-                                  &end_of_context)) {
+                                  &s->decompressed_data_buffer, NULL,
+                                  MAX_SIZE_T, &end_of_context)) {
         error =
             GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream decompression error.");
         return error;
       }
       GPR_ASSERT(s->unprocessed_incoming_frames_buffer.length == 0);
       grpc_slice_buffer_swap(&s->unprocessed_incoming_frames_buffer,
-                             s->decompressed_data_buffer);
+                             &s->decompressed_data_buffer);
       s->unprocessed_incoming_frames_decompressed = true;
       if (end_of_context) {
         grpc_stream_compression_context_destroy(s->stream_decompression_ctx);
diff --git a/src/core/ext/transport/chttp2/transport/flow_control.c b/src/core/ext/transport/chttp2/transport/flow_control.c
index aa1dd2d..a95b4b3 100644
--- a/src/core/ext/transport/chttp2/transport/flow_control.c
+++ b/src/core/ext/transport/chttp2/transport/flow_control.c
@@ -391,9 +391,10 @@
 
 // Takes in a target and uses the pid controller to return a stabilized
 // guess at the new bdp.
-static double get_pid_controller_guess(grpc_chttp2_transport_flowctl* tfc,
+static double get_pid_controller_guess(grpc_exec_ctx* exec_ctx,
+                                       grpc_chttp2_transport_flowctl* tfc,
                                        double target) {
-  gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
+  grpc_millis now = grpc_exec_ctx_now(exec_ctx);
   if (!tfc->pid_controller_initialized) {
     tfc->last_pid_update = now;
     tfc->pid_controller_initialized = true;
@@ -409,11 +410,7 @@
     return pow(2, target);
   }
   double bdp_error = target - grpc_pid_controller_last(&tfc->pid_controller);
-  gpr_timespec dt_timespec = gpr_time_sub(now, tfc->last_pid_update);
-  double dt = (double)dt_timespec.tv_sec + dt_timespec.tv_nsec * 1e-9;
-  if (dt > 0.1) {
-    dt = 0.1;
-  }
+  double dt = (double)(now - tfc->last_pid_update) * 1e-3;
   double log2_bdp_guess =
       grpc_pid_controller_update(&tfc->pid_controller, bdp_error, dt);
   tfc->last_pid_update = now;
@@ -441,7 +438,8 @@
 }
 
 grpc_chttp2_flowctl_action grpc_chttp2_flowctl_get_action(
-    grpc_chttp2_transport_flowctl* tfc, grpc_chttp2_stream_flowctl* sfc) {
+    grpc_exec_ctx* exec_ctx, grpc_chttp2_transport_flowctl* tfc,
+    grpc_chttp2_stream_flowctl* sfc) {
   grpc_chttp2_flowctl_action action;
   memset(&action, 0, sizeof(action));
   // TODO(ncteisen): tune this
@@ -473,7 +471,7 @@
 
       // run our target through the pid controller to stabilize change.
       // TODO(ncteisen): experiment with other controllers here.
-      double bdp_guess = get_pid_controller_guess(tfc, target);
+      double bdp_guess = get_pid_controller_guess(exec_ctx, tfc, target);
 
       // Though initial window 'could' drop to 0, we keep the floor at 128
       tfc->target_initial_window_size =
diff --git a/src/core/ext/transport/chttp2/transport/frame_data.c b/src/core/ext/transport/chttp2/transport/frame_data.c
index 222d217..73aaab1 100644
--- a/src/core/ext/transport/chttp2/transport/frame_data.c
+++ b/src/core/ext/transport/chttp2/transport/frame_data.c
@@ -210,7 +210,7 @@
 
         if (cur != end) {
           grpc_slice_buffer_undo_take_first(
-              &s->unprocessed_incoming_frames_buffer,
+              slices,
               grpc_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg)));
         }
         grpc_slice_unref_internal(exec_ctx, slice);
@@ -277,7 +277,7 @@
           p->state = GRPC_CHTTP2_DATA_FH_0;
           cur += p->frame_size;
           grpc_slice_buffer_undo_take_first(
-              &s->unprocessed_incoming_frames_buffer,
+              slices,
               grpc_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg)));
           grpc_slice_unref_internal(exec_ctx, slice);
           return GRPC_ERROR_NONE;
diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.c b/src/core/ext/transport/chttp2/transport/frame_ping.c
index d431d6b..1cfa883 100644
--- a/src/core/ext/transport/chttp2/transport/frame_ping.c
+++ b/src/core/ext/transport/chttp2/transport/frame_ping.c
@@ -89,10 +89,10 @@
       grpc_chttp2_ack_ping(exec_ctx, t, p->opaque_8bytes);
     } else {
       if (!t->is_client) {
-        gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
-        gpr_timespec next_allowed_ping =
-            gpr_time_add(t->ping_recv_state.last_ping_recv_time,
-                         t->ping_policy.min_recv_ping_interval_without_data);
+        grpc_millis now = grpc_exec_ctx_now(exec_ctx);
+        grpc_millis next_allowed_ping =
+            t->ping_recv_state.last_ping_recv_time +
+            t->ping_policy.min_recv_ping_interval_without_data;
 
         if (t->keepalive_permit_without_calls == 0 &&
             grpc_chttp2_stream_map_size(&t->stream_map) == 0) {
@@ -100,11 +100,10 @@
              no less than two hours. When there is no outstanding streams, we
              restrict the number of PINGS equivalent to TCP Keep-Alive. */
           next_allowed_ping =
-              gpr_time_add(t->ping_recv_state.last_ping_recv_time,
-                           gpr_time_from_seconds(7200, GPR_TIMESPAN));
+              t->ping_recv_state.last_ping_recv_time + 7200 * GPR_MS_PER_SEC;
         }
 
-        if (gpr_time_cmp(next_allowed_ping, now) > 0) {
+        if (next_allowed_ping > now) {
           grpc_chttp2_add_ping_strike(exec_ctx, t);
         }
 
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.c b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
index 9dfbd3c..c934b4a 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.c
@@ -33,6 +33,7 @@
 #include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
 #include "src/core/ext/transport/chttp2/transport/hpack_table.h"
 #include "src/core/ext/transport/chttp2/transport/varint.h"
+#include "src/core/lib/debug/stats.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/transport/metadata.h"
@@ -271,8 +272,10 @@
   }
 }
 
-static void emit_indexed(grpc_chttp2_hpack_compressor *c, uint32_t elem_index,
+static void emit_indexed(grpc_exec_ctx *exec_ctx,
+                         grpc_chttp2_hpack_compressor *c, uint32_t elem_index,
                          framer_state *st) {
+  GRPC_STATS_INC_HPACK_SEND_INDEXED(exec_ctx);
   uint32_t len = GRPC_CHTTP2_VARINT_LENGTH(elem_index, 1);
   GRPC_CHTTP2_WRITE_VARINT(elem_index, 1, 0x80, add_tiny_header_data(st, len),
                            len);
@@ -284,15 +287,18 @@
   bool insert_null_before_wire_value;
 } wire_value;
 
-static wire_value get_wire_value(grpc_mdelem elem, bool true_binary_enabled) {
+static wire_value get_wire_value(grpc_exec_ctx *exec_ctx, grpc_mdelem elem,
+                                 bool true_binary_enabled) {
   wire_value wire_val;
   if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
     if (true_binary_enabled) {
+      GRPC_STATS_INC_HPACK_SEND_BINARY(exec_ctx);
       wire_val.huffman_prefix = 0x00;
       wire_val.insert_null_before_wire_value = true;
       wire_val.data = grpc_slice_ref_internal(GRPC_MDVALUE(elem));
 
     } else {
+      GRPC_STATS_INC_HPACK_SEND_BINARY_BASE64(exec_ctx);
       wire_val.huffman_prefix = 0x80;
       wire_val.insert_null_before_wire_value = false;
       wire_val.data =
@@ -300,6 +306,7 @@
     }
   } else {
     /* TODO(ctiller): opportunistically compress non-binary headers */
+    GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(exec_ctx);
     wire_val.huffman_prefix = 0x00;
     wire_val.insert_null_before_wire_value = false;
     wire_val.data = grpc_slice_ref_internal(GRPC_MDVALUE(elem));
@@ -316,11 +323,14 @@
   add_header_data(st, v.data);
 }
 
-static void emit_lithdr_incidx(grpc_chttp2_hpack_compressor *c,
+static void emit_lithdr_incidx(grpc_exec_ctx *exec_ctx,
+                               grpc_chttp2_hpack_compressor *c,
                                uint32_t key_index, grpc_mdelem elem,
                                framer_state *st) {
+  GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX(exec_ctx);
   uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2);
-  wire_value value = get_wire_value(elem, st->use_true_binary_metadata);
+  wire_value value =
+      get_wire_value(exec_ctx, elem, st->use_true_binary_metadata);
   size_t len_val = wire_value_length(value);
   uint32_t len_val_len;
   GPR_ASSERT(len_val <= UINT32_MAX);
@@ -332,11 +342,14 @@
   add_wire_value(st, value);
 }
 
-static void emit_lithdr_noidx(grpc_chttp2_hpack_compressor *c,
+static void emit_lithdr_noidx(grpc_exec_ctx *exec_ctx,
+                              grpc_chttp2_hpack_compressor *c,
                               uint32_t key_index, grpc_mdelem elem,
                               framer_state *st) {
+  GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX(exec_ctx);
   uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4);
-  wire_value value = get_wire_value(elem, st->use_true_binary_metadata);
+  wire_value value =
+      get_wire_value(exec_ctx, elem, st->use_true_binary_metadata);
   size_t len_val = wire_value_length(value);
   uint32_t len_val_len;
   GPR_ASSERT(len_val <= UINT32_MAX);
@@ -348,10 +361,14 @@
   add_wire_value(st, value);
 }
 
-static void emit_lithdr_incidx_v(grpc_chttp2_hpack_compressor *c,
+static void emit_lithdr_incidx_v(grpc_exec_ctx *exec_ctx,
+                                 grpc_chttp2_hpack_compressor *c,
                                  grpc_mdelem elem, framer_state *st) {
+  GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V(exec_ctx);
+  GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(exec_ctx);
   uint32_t len_key = (uint32_t)GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
-  wire_value value = get_wire_value(elem, st->use_true_binary_metadata);
+  wire_value value =
+      get_wire_value(exec_ctx, elem, st->use_true_binary_metadata);
   uint32_t len_val = (uint32_t)wire_value_length(value);
   uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1);
   uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1);
@@ -366,10 +383,14 @@
   add_wire_value(st, value);
 }
 
-static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor *c,
+static void emit_lithdr_noidx_v(grpc_exec_ctx *exec_ctx,
+                                grpc_chttp2_hpack_compressor *c,
                                 grpc_mdelem elem, framer_state *st) {
+  GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V(exec_ctx);
+  GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(exec_ctx);
   uint32_t len_key = (uint32_t)GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
-  wire_value value = get_wire_value(elem, st->use_true_binary_metadata);
+  wire_value value =
+      get_wire_value(exec_ctx, elem, st->use_true_binary_metadata);
   uint32_t len_val = (uint32_t)wire_value_length(value);
   uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1);
   uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1);
@@ -422,7 +443,7 @@
     gpr_free(v);
   }
   if (!GRPC_MDELEM_IS_INTERNED(elem)) {
-    emit_lithdr_noidx_v(c, elem, st);
+    emit_lithdr_noidx_v(exec_ctx, c, elem, st);
     return;
   }
 
@@ -444,16 +465,16 @@
   if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_2(elem_hash)], elem) &&
       c->indices_elems[HASH_FRAGMENT_2(elem_hash)] > c->tail_remote_index) {
     /* HIT: complete element (first cuckoo hash) */
-    emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_2(elem_hash)]),
-                 st);
+    emit_indexed(exec_ctx, c,
+                 dynidx(c, c->indices_elems[HASH_FRAGMENT_2(elem_hash)]), st);
     return;
   }
 
   if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_3(elem_hash)], elem) &&
       c->indices_elems[HASH_FRAGMENT_3(elem_hash)] > c->tail_remote_index) {
     /* HIT: complete element (second cuckoo hash) */
-    emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_3(elem_hash)]),
-                 st);
+    emit_indexed(exec_ctx, c,
+                 dynidx(c, c->indices_elems[HASH_FRAGMENT_3(elem_hash)]), st);
     return;
   }
 
@@ -471,11 +492,11 @@
       indices_key > c->tail_remote_index) {
     /* HIT: key (first cuckoo hash) */
     if (should_add_elem) {
-      emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st);
+      emit_lithdr_incidx(exec_ctx, c, dynidx(c, indices_key), elem, st);
       add_elem(exec_ctx, c, elem);
       return;
     } else {
-      emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st);
+      emit_lithdr_noidx(exec_ctx, c, dynidx(c, indices_key), elem, st);
       return;
     }
     GPR_UNREACHABLE_CODE(return );
@@ -487,11 +508,11 @@
       indices_key > c->tail_remote_index) {
     /* HIT: key (first cuckoo hash) */
     if (should_add_elem) {
-      emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st);
+      emit_lithdr_incidx(exec_ctx, c, dynidx(c, indices_key), elem, st);
       add_elem(exec_ctx, c, elem);
       return;
     } else {
-      emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st);
+      emit_lithdr_noidx(exec_ctx, c, dynidx(c, indices_key), elem, st);
       return;
     }
     GPR_UNREACHABLE_CODE(return );
@@ -500,11 +521,11 @@
   /* no elem, key in the table... fall back to literal emission */
 
   if (should_add_elem) {
-    emit_lithdr_incidx_v(c, elem, st);
+    emit_lithdr_incidx_v(exec_ctx, c, elem, st);
     add_elem(exec_ctx, c, elem);
     return;
   } else {
-    emit_lithdr_noidx_v(c, elem, st);
+    emit_lithdr_noidx_v(exec_ctx, c, elem, st);
     return;
   }
   GPR_UNREACHABLE_CODE(return );
@@ -514,12 +535,12 @@
 #define TIMEOUT_KEY "grpc-timeout"
 
 static void deadline_enc(grpc_exec_ctx *exec_ctx,
-                         grpc_chttp2_hpack_compressor *c, gpr_timespec deadline,
+                         grpc_chttp2_hpack_compressor *c, grpc_millis deadline,
                          framer_state *st) {
   char timeout_str[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
   grpc_mdelem mdelem;
-  grpc_http2_encode_timeout(
-      gpr_time_sub(deadline, gpr_now(deadline.clock_type)), timeout_str);
+  grpc_http2_encode_timeout(deadline - grpc_exec_ctx_now(exec_ctx),
+                            timeout_str);
   mdelem = grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_TIMEOUT,
                                    grpc_slice_from_copied_string(timeout_str));
   hpack_enc(exec_ctx, c, mdelem, st);
@@ -639,8 +660,8 @@
   for (grpc_linked_mdelem *l = metadata->list.head; l; l = l->next) {
     hpack_enc(exec_ctx, c, l->md, &st);
   }
-  gpr_timespec deadline = metadata->deadline;
-  if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) != 0) {
+  grpc_millis deadline = metadata->deadline;
+  if (deadline != GRPC_MILLIS_INF_FUTURE) {
     deadline_enc(exec_ctx, c, deadline, &st);
   }
 
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.c b/src/core/ext/transport/chttp2/transport/hpack_parser.c
index 901e741..3d1df19 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.c
@@ -30,6 +30,7 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
+#include "src/core/lib/debug/stats.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/support/string.h"
@@ -777,8 +778,7 @@
   return parse_stream_dep1(exec_ctx, p, cur + 1, end);
 }
 
-/* emit an indexed field; for now just logs it to console; jumps to
-   begin the next field on completion */
+/* emit an indexed field; jumps to begin the next field on completion */
 static grpc_error *finish_indexed_field(grpc_exec_ctx *exec_ctx,
                                         grpc_chttp2_hpack_parser *p,
                                         const uint8_t *cur,
@@ -792,6 +792,7 @@
         GRPC_ERROR_INT_SIZE, (intptr_t)p->table.num_ents);
   }
   GRPC_MDELEM_REF(md);
+  GRPC_STATS_INC_HPACK_RECV_INDEXED(exec_ctx);
   grpc_error *err = on_hdr(exec_ctx, p, md, 0);
   if (err != GRPC_ERROR_NONE) return err;
   return parse_begin(exec_ctx, p, cur, end);
@@ -820,14 +821,14 @@
   return parse_value0(exec_ctx, p, cur + 1, end);
 }
 
-/* finish a literal header with incremental indexing: just log, and jump to '
-   begin */
+/* finish a literal header with incremental indexing */
 static grpc_error *finish_lithdr_incidx(grpc_exec_ctx *exec_ctx,
                                         grpc_chttp2_hpack_parser *p,
                                         const uint8_t *cur,
                                         const uint8_t *end) {
   grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
   GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */
+  GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX(exec_ctx);
   grpc_error *err = on_hdr(
       exec_ctx, p,
       grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)),
@@ -842,6 +843,7 @@
                                           grpc_chttp2_hpack_parser *p,
                                           const uint8_t *cur,
                                           const uint8_t *end) {
+  GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX_V(exec_ctx);
   grpc_error *err = on_hdr(
       exec_ctx, p,
       grpc_mdelem_from_slices(exec_ctx, take_string(exec_ctx, p, &p->key, true),
@@ -898,6 +900,7 @@
                                         const uint8_t *end) {
   grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
   GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */
+  GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX(exec_ctx);
   grpc_error *err = on_hdr(
       exec_ctx, p,
       grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)),
@@ -912,6 +915,7 @@
                                           grpc_chttp2_hpack_parser *p,
                                           const uint8_t *cur,
                                           const uint8_t *end) {
+  GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX_V(exec_ctx);
   grpc_error *err = on_hdr(
       exec_ctx, p,
       grpc_mdelem_from_slices(exec_ctx, take_string(exec_ctx, p, &p->key, true),
@@ -968,6 +972,7 @@
                                         const uint8_t *end) {
   grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
   GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */
+  GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX(exec_ctx);
   grpc_error *err = on_hdr(
       exec_ctx, p,
       grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)),
@@ -982,6 +987,7 @@
                                           grpc_chttp2_hpack_parser *p,
                                           const uint8_t *cur,
                                           const uint8_t *end) {
+  GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX_V(exec_ctx);
   grpc_error *err = on_hdr(
       exec_ctx, p,
       grpc_mdelem_from_slices(exec_ctx, take_string(exec_ctx, p, &p->key, true),
@@ -1310,9 +1316,11 @@
         /* 'true-binary' case */
         ++cur;
         p->binary = NOT_BINARY;
+        GRPC_STATS_INC_HPACK_RECV_BINARY(exec_ctx);
         append_bytes(str, cur, (size_t)(end - cur));
         return GRPC_ERROR_NONE;
       }
+      GRPC_STATS_INC_HPACK_RECV_BINARY_BASE64(exec_ctx);
     /* fallthrough */
     b64_byte0:
     case B64_BYTE0:
@@ -1510,6 +1518,7 @@
                                       grpc_chttp2_hpack_parser_string *str) {
   if (!p->huff && binary == NOT_BINARY && (end - cur) >= (intptr_t)p->strlen &&
       p->current_slice_refcount != NULL) {
+    GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED(exec_ctx);
     str->copied = false;
     str->data.referenced.refcount = p->current_slice_refcount;
     str->data.referenced.data.refcounted.bytes = (uint8_t *)cur;
@@ -1523,6 +1532,20 @@
   p->parsing.str = str;
   p->huff_state = 0;
   p->binary = binary;
+  switch (p->binary) {
+    case NOT_BINARY:
+      if (p->huff) {
+        GRPC_STATS_INC_HPACK_RECV_HUFFMAN(exec_ctx);
+      } else {
+        GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED(exec_ctx);
+      }
+      break;
+    case BINARY_BEGIN:
+      /* stats incremented later: don't know true binary or not */
+      break;
+    default:
+      abort();
+  }
   return parse_string(exec_ctx, p, cur, end);
 }
 
@@ -1660,17 +1683,12 @@
                                         grpc_chttp2_transport *t,
                                         grpc_chttp2_stream *s,
                                         grpc_metadata_batch *initial_metadata) {
-  if (initial_metadata->idx.named.content_encoding != NULL) {
-    grpc_slice content_encoding =
-        GRPC_MDVALUE(initial_metadata->idx.named.content_encoding->md);
-    if (!grpc_slice_eq(content_encoding, GRPC_MDSTR_IDENTITY)) {
-      if (grpc_slice_eq(content_encoding, GRPC_MDSTR_GZIP)) {
-        s->stream_compression_recv_enabled = true;
-        s->decompressed_data_buffer =
-            (grpc_slice_buffer *)gpr_malloc(sizeof(grpc_slice_buffer));
-        grpc_slice_buffer_init(s->decompressed_data_buffer);
-      }
-    }
+  if (initial_metadata->idx.named.content_encoding == NULL ||
+      grpc_stream_compression_method_parse(
+          GRPC_MDVALUE(initial_metadata->idx.named.content_encoding->md), false,
+          &s->stream_decompression_method) == 0) {
+    s->stream_decompression_method =
+        GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS;
   }
 }
 
diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.c b/src/core/ext/transport/chttp2/transport/incoming_metadata.c
index ba680a8..187ce0e 100644
--- a/src/core/ext/transport/chttp2/transport/incoming_metadata.c
+++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.c
@@ -29,7 +29,7 @@
     grpc_chttp2_incoming_metadata_buffer *buffer, gpr_arena *arena) {
   buffer->arena = arena;
   grpc_metadata_batch_init(&buffer->batch);
-  buffer->batch.deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
+  buffer->batch.deadline = GRPC_MILLIS_INF_FUTURE;
 }
 
 void grpc_chttp2_incoming_metadata_buffer_destroy(
@@ -62,7 +62,7 @@
 }
 
 void grpc_chttp2_incoming_metadata_buffer_set_deadline(
-    grpc_chttp2_incoming_metadata_buffer *buffer, gpr_timespec deadline) {
+    grpc_chttp2_incoming_metadata_buffer *buffer, grpc_millis deadline) {
   buffer->batch.deadline = deadline;
 }
 
diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.h b/src/core/ext/transport/chttp2/transport/incoming_metadata.h
index a951d87..0fc90b2 100644
--- a/src/core/ext/transport/chttp2/transport/incoming_metadata.h
+++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.h
@@ -43,6 +43,6 @@
     grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer,
     grpc_mdelem elem) GRPC_MUST_USE_RESULT;
 void grpc_chttp2_incoming_metadata_buffer_set_deadline(
-    grpc_chttp2_incoming_metadata_buffer *buffer, gpr_timespec deadline);
+    grpc_chttp2_incoming_metadata_buffer *buffer, grpc_millis deadline);
 
 #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H */
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index 4b66f7d..dfa5ab9 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -107,19 +107,19 @@
 typedef struct {
   int max_pings_without_data;
   int max_ping_strikes;
-  gpr_timespec min_sent_ping_interval_without_data;
-  gpr_timespec min_recv_ping_interval_without_data;
+  grpc_millis min_sent_ping_interval_without_data;
+  grpc_millis min_recv_ping_interval_without_data;
 } grpc_chttp2_repeated_ping_policy;
 
 typedef struct {
-  gpr_timespec last_ping_sent_time;
+  grpc_millis last_ping_sent_time;
   int pings_before_data_required;
   grpc_timer delayed_ping_timer;
   bool is_delayed_ping_timer_set;
 } grpc_chttp2_repeated_ping_state;
 
 typedef struct {
-  gpr_timespec last_ping_recv_time;
+  grpc_millis last_ping_recv_time;
   int ping_strikes;
 } grpc_chttp2_server_ping_recv_state;
 
@@ -269,7 +269,7 @@
   /* pid controller */
   bool pid_controller_initialized;
   grpc_pid_controller pid_controller;
-  gpr_timespec last_pid_update;
+  grpc_millis last_pid_update;
 
   // pointer back to transport for tracing
   const grpc_chttp2_transport *t;
@@ -451,9 +451,9 @@
   /** watchdog to kill the transport when waiting for the keepalive ping */
   grpc_timer keepalive_watchdog_timer;
   /** time duration in between pings */
-  gpr_timespec keepalive_time;
+  grpc_millis keepalive_time;
   /** grace period for a ping to complete before watchdog kicks in */
-  gpr_timespec keepalive_timeout;
+  grpc_millis keepalive_timeout;
   /** if keepalive pings are allowed when there's no outstanding streams */
   bool keepalive_permit_without_calls;
   /** keep-alive state machine state */
@@ -562,7 +562,7 @@
   grpc_error *byte_stream_error; /* protected by t combiner */
   bool received_last_frame;      /* protected by t combiner */
 
-  gpr_timespec deadline;
+  grpc_millis deadline;
 
   /** saw some stream level error */
   grpc_error *forced_close_error;
@@ -588,25 +588,27 @@
   grpc_chttp2_write_cb *finish_after_write;
   size_t sending_bytes;
 
-  /** Whether stream compression send is enabled */
-  bool stream_compression_recv_enabled;
-  /** Whether stream compression recv is enabled */
-  bool stream_compression_send_enabled;
-  /** Whether bytes stored in unprocessed_incoming_byte_stream is decompressed
-   */
-  bool unprocessed_incoming_frames_decompressed;
+  /* Stream compression method to be used. */
+  grpc_stream_compression_method stream_compression_method;
+  /* Stream decompression method to be used. */
+  grpc_stream_compression_method stream_decompression_method;
   /** Stream compression decompress context */
   grpc_stream_compression_context *stream_decompression_ctx;
   /** Stream compression compress context */
   grpc_stream_compression_context *stream_compression_ctx;
 
   /** Buffer storing data that is compressed but not sent */
-  grpc_slice_buffer *compressed_data_buffer;
+  grpc_slice_buffer compressed_data_buffer;
   /** Amount of uncompressed bytes sent out when compressed_data_buffer is
    * emptied */
   size_t uncompressed_data_size;
   /** Temporary buffer storing decompressed data */
-  grpc_slice_buffer *decompressed_data_buffer;
+  grpc_slice_buffer decompressed_data_buffer;
+  /** Whether bytes stored in unprocessed_incoming_byte_stream is decompressed
+   */
+  bool unprocessed_incoming_frames_decompressed;
+  /** gRPC header bytes that are already decompressed */
+  size_t decompressed_header_bytes;
 };
 
 /** Transport writing call flow:
@@ -748,7 +750,8 @@
 // Reads the flow control data and returns and actionable struct that will tell
 // chttp2 exactly what it needs to do
 grpc_chttp2_flowctl_action grpc_chttp2_flowctl_get_action(
-    grpc_chttp2_transport_flowctl *tfc, grpc_chttp2_stream_flowctl *sfc);
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_flowctl *tfc,
+    grpc_chttp2_stream_flowctl *sfc);
 
 // Takes in a flow control action and performs all the needed operations.
 void grpc_chttp2_act_on_flowctl_action(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/ext/transport/chttp2/transport/parsing.c b/src/core/ext/transport/chttp2/transport/parsing.c
index 3db1ad4..aab11e0 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.c
+++ b/src/core/ext/transport/chttp2/transport/parsing.c
@@ -359,8 +359,9 @@
                                       s == NULL ? NULL : &s->flow_control,
                                       t->incoming_frame_size);
   grpc_chttp2_act_on_flowctl_action(
-      exec_ctx, grpc_chttp2_flowctl_get_action(
-                    &t->flow_control, s == NULL ? NULL : &s->flow_control),
+      exec_ctx,
+      grpc_chttp2_flowctl_get_action(exec_ctx, &t->flow_control,
+                                     s == NULL ? NULL : &s->flow_control),
       t, s);
   if (err != GRPC_ERROR_NONE) {
     goto error_handler;
@@ -385,7 +386,7 @@
     t->parser_data = &s->data_parser;
     t->ping_state.pings_before_data_required =
         t->ping_policy.max_pings_without_data;
-    t->ping_state.last_ping_sent_time = gpr_inf_past(GPR_CLOCK_MONOTONIC);
+    t->ping_state.last_ping_sent_time = GRPC_MILLIS_INF_PAST;
     return GRPC_ERROR_NONE;
   } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) {
     /* handle stream errors by closing the stream */
@@ -430,26 +431,26 @@
   }
 
   if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) {
-    gpr_timespec *cached_timeout =
-        (gpr_timespec *)grpc_mdelem_get_user_data(md, free_timeout);
-    gpr_timespec timeout;
+    grpc_millis *cached_timeout = grpc_mdelem_get_user_data(md, free_timeout);
+    grpc_millis timeout;
     if (cached_timeout == NULL) {
       /* not already parsed: parse it now, and store the result away */
-      cached_timeout = (gpr_timespec *)gpr_malloc(sizeof(gpr_timespec));
+      cached_timeout = (grpc_millis *)gpr_malloc(sizeof(grpc_millis));
       if (!grpc_http2_decode_timeout(GRPC_MDVALUE(md), cached_timeout)) {
         char *val = grpc_slice_to_c_string(GRPC_MDVALUE(md));
         gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val);
         gpr_free(val);
-        *cached_timeout = gpr_inf_future(GPR_TIMESPAN);
+        *cached_timeout = GRPC_MILLIS_INF_FUTURE;
       }
       timeout = *cached_timeout;
       grpc_mdelem_set_user_data(md, free_timeout, cached_timeout);
     } else {
       timeout = *cached_timeout;
     }
-    grpc_chttp2_incoming_metadata_buffer_set_deadline(
-        &s->metadata_buffer[0],
-        gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), timeout));
+    if (timeout != GRPC_MILLIS_INF_FUTURE) {
+      grpc_chttp2_incoming_metadata_buffer_set_deadline(
+          &s->metadata_buffer[0], grpc_exec_ctx_now(exec_ctx) + timeout);
+    }
     GRPC_MDELEM_UNREF(exec_ctx, md);
   } else {
     const size_t new_size = s->metadata_buffer[0].size + GRPC_MDELEM_LENGTH(md);
@@ -564,7 +565,7 @@
 
   t->ping_state.pings_before_data_required =
       t->ping_policy.max_pings_without_data;
-  t->ping_state.last_ping_sent_time = gpr_inf_past(GPR_CLOCK_MONOTONIC);
+  t->ping_state.last_ping_sent_time = GRPC_MILLIS_INF_PAST;
 
   /* could be a new grpc_chttp2_stream or an existing grpc_chttp2_stream */
   s = grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id);
diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c
index f4bf037..f089018 100644
--- a/src/core/ext/transport/chttp2/transport/writing.c
+++ b/src/core/ext/transport/chttp2/transport/writing.c
@@ -70,19 +70,16 @@
     }
     return;
   }
-  gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
-  gpr_timespec next_allowed_ping =
-      gpr_time_add(t->ping_state.last_ping_sent_time,
-                   t->ping_policy.min_sent_ping_interval_without_data);
+  grpc_millis now = grpc_exec_ctx_now(exec_ctx);
+  grpc_millis next_allowed_ping =
+      t->ping_state.last_ping_sent_time +
+      t->ping_policy.min_sent_ping_interval_without_data;
   if (t->keepalive_permit_without_calls == 0 &&
       grpc_chttp2_stream_map_size(&t->stream_map) == 0) {
-    next_allowed_ping = gpr_time_add(t->ping_recv_state.last_ping_recv_time,
-                                     gpr_time_from_seconds(7200, GPR_TIMESPAN));
+    next_allowed_ping =
+        t->ping_recv_state.last_ping_recv_time + 7200 * GPR_MS_PER_SEC;
   }
-  /* gpr_log(GPR_DEBUG, "next_allowed_ping:%d.%09d now:%d.%09d",
-          (int)next_allowed_ping.tv_sec, (int)next_allowed_ping.tv_nsec,
-          (int)now.tv_sec, (int)now.tv_nsec); */
-  if (gpr_time_cmp(next_allowed_ping, now) > 0) {
+  if (next_allowed_ping > now) {
     /* not enough elapsed time between successive pings */
     if (GRPC_TRACER_ON(grpc_http_trace) ||
         GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
@@ -93,8 +90,7 @@
     if (!t->ping_state.is_delayed_ping_timer_set) {
       t->ping_state.is_delayed_ping_timer_set = true;
       grpc_timer_init(exec_ctx, &t->ping_state.delayed_ping_timer,
-                      next_allowed_ping, &t->retry_initiate_ping_locked,
-                      gpr_now(GPR_CLOCK_MONOTONIC));
+                      next_allowed_ping, &t->retry_initiate_ping_locked);
     }
     return;
   }
@@ -279,8 +275,7 @@
                                   s->send_initial_metadata, &hopt, &t->outbuf);
         now_writing = true;
         if (!t->is_client) {
-          t->ping_recv_state.last_ping_recv_time =
-              gpr_inf_past(GPR_CLOCK_MONOTONIC);
+          t->ping_recv_state.last_ping_recv_time = GRPC_MILLIS_INF_PAST;
           t->ping_recv_state.ping_strikes = 0;
         }
         initial_metadata_writes++;
@@ -309,6 +304,7 @@
           exec_ctx, t, s, &s->send_initial_metadata_finished, GRPC_ERROR_NONE,
           "send_initial_metadata_finished");
     }
+
     /* send any window updates */
     uint32_t stream_announce = grpc_chttp2_flowctl_maybe_send_stream_update(
         &t->flow_control, &s->flow_control);
@@ -317,8 +313,7 @@
           &t->outbuf, grpc_chttp2_window_update_create(s->id, stream_announce,
                                                        &s->stats.outgoing));
       if (!t->is_client) {
-        t->ping_recv_state.last_ping_recv_time =
-            gpr_inf_past(GPR_CLOCK_MONOTONIC);
+        t->ping_recv_state.last_ping_recv_time = GRPC_MILLIS_INF_PAST;
         t->ping_recv_state.ping_strikes = 0;
       }
       flow_control_writes++;
@@ -326,8 +321,7 @@
     if (sent_initial_metadata) {
       /* send any body bytes, if allowed by flow control */
       if (s->flow_controlled_buffer.length > 0 ||
-          (s->stream_compression_send_enabled &&
-           s->compressed_data_buffer->length > 0)) {
+          s->compressed_data_buffer.length > 0) {
         uint32_t stream_remote_window = (uint32_t)GPR_MAX(
             0,
             s->flow_control.remote_window_delta +
@@ -341,60 +335,62 @@
           bool is_last_data_frame = false;
           bool is_last_frame = false;
           size_t sending_bytes_before = s->sending_bytes;
-          if (s->stream_compression_send_enabled) {
-            while ((s->flow_controlled_buffer.length > 0 ||
-                    s->compressed_data_buffer->length > 0) &&
-                   max_outgoing > 0) {
-              if (s->compressed_data_buffer->length > 0) {
-                uint32_t send_bytes = (uint32_t)GPR_MIN(
-                    max_outgoing, s->compressed_data_buffer->length);
-                is_last_data_frame =
-                    (send_bytes == s->compressed_data_buffer->length &&
-                     s->flow_controlled_buffer.length == 0 &&
-                     s->fetching_send_message == NULL);
-                is_last_frame =
-                    is_last_data_frame && s->send_trailing_metadata != NULL &&
-                    grpc_metadata_batch_is_empty(s->send_trailing_metadata);
-                grpc_chttp2_encode_data(s->id, s->compressed_data_buffer,
-                                        send_bytes, is_last_frame,
-                                        &s->stats.outgoing, &t->outbuf);
-                grpc_chttp2_flowctl_sent_data(&t->flow_control,
-                                              &s->flow_control, send_bytes);
-                max_outgoing -= send_bytes;
-                if (s->compressed_data_buffer->length == 0) {
-                  s->sending_bytes += s->uncompressed_data_size;
+          while ((s->flow_controlled_buffer.length > 0 ||
+                  s->compressed_data_buffer.length > 0) &&
+                 max_outgoing > 0) {
+            if (s->compressed_data_buffer.length > 0) {
+              uint32_t send_bytes = (uint32_t)GPR_MIN(
+                  max_outgoing, s->compressed_data_buffer.length);
+              is_last_data_frame =
+                  (send_bytes == s->compressed_data_buffer.length &&
+                   s->flow_controlled_buffer.length == 0 &&
+                   s->fetching_send_message == NULL);
+              if (is_last_data_frame && s->send_trailing_metadata != NULL &&
+                  s->stream_compression_ctx != NULL) {
+                if (!grpc_stream_compress(
+                        s->stream_compression_ctx, &s->flow_controlled_buffer,
+                        &s->compressed_data_buffer, NULL, MAX_SIZE_T,
+                        GRPC_STREAM_COMPRESSION_FLUSH_FINISH)) {
+                  gpr_log(GPR_ERROR, "Stream compression failed.");
                 }
-              } else {
-                if (s->stream_compression_ctx == NULL) {
-                  s->stream_compression_ctx =
-                      grpc_stream_compression_context_create(
-                          GRPC_STREAM_COMPRESSION_COMPRESS);
-                }
-                s->uncompressed_data_size = s->flow_controlled_buffer.length;
-                GPR_ASSERT(grpc_stream_compress(
-                    s->stream_compression_ctx, &s->flow_controlled_buffer,
-                    s->compressed_data_buffer, NULL, MAX_SIZE_T,
-                    GRPC_STREAM_COMPRESSION_FLUSH_SYNC));
+                grpc_stream_compression_context_destroy(
+                    s->stream_compression_ctx);
+                s->stream_compression_ctx = NULL;
+                /* After finish, bytes in s->compressed_data_buffer may be
+                 * more than max_outgoing. Start another round of the current
+                 * while loop so that send_bytes and is_last_data_frame are
+                 * recalculated. */
+                continue;
+              }
+              is_last_frame =
+                  is_last_data_frame && s->send_trailing_metadata != NULL &&
+                  grpc_metadata_batch_is_empty(s->send_trailing_metadata);
+              grpc_chttp2_encode_data(s->id, &s->compressed_data_buffer,
+                                      send_bytes, is_last_frame,
+                                      &s->stats.outgoing, &t->outbuf);
+              grpc_chttp2_flowctl_sent_data(&t->flow_control, &s->flow_control,
+                                            send_bytes);
+              max_outgoing -= send_bytes;
+              if (s->compressed_data_buffer.length == 0) {
+                s->sending_bytes += s->uncompressed_data_size;
+              }
+            } else {
+              if (s->stream_compression_ctx == NULL) {
+                s->stream_compression_ctx =
+                    grpc_stream_compression_context_create(
+                        s->stream_compression_method);
+              }
+              s->uncompressed_data_size = s->flow_controlled_buffer.length;
+              if (!grpc_stream_compress(
+                      s->stream_compression_ctx, &s->flow_controlled_buffer,
+                      &s->compressed_data_buffer, NULL, MAX_SIZE_T,
+                      GRPC_STREAM_COMPRESSION_FLUSH_SYNC)) {
+                gpr_log(GPR_ERROR, "Stream compression failed.");
               }
             }
-          } else {
-            uint32_t send_bytes = (uint32_t)GPR_MIN(
-                max_outgoing, s->flow_controlled_buffer.length);
-            is_last_data_frame = s->fetching_send_message == NULL &&
-                                 send_bytes == s->flow_controlled_buffer.length;
-            is_last_frame =
-                is_last_data_frame && s->send_trailing_metadata != NULL &&
-                grpc_metadata_batch_is_empty(s->send_trailing_metadata);
-            grpc_chttp2_encode_data(s->id, &s->flow_controlled_buffer,
-                                    send_bytes, is_last_frame,
-                                    &s->stats.outgoing, &t->outbuf);
-            grpc_chttp2_flowctl_sent_data(&t->flow_control, &s->flow_control,
-                                          send_bytes);
-            s->sending_bytes += send_bytes;
           }
           if (!t->is_client) {
-            t->ping_recv_state.last_ping_recv_time =
-                gpr_inf_past(GPR_CLOCK_MONOTONIC);
+            t->ping_recv_state.last_ping_recv_time = 0;
             t->ping_recv_state.ping_strikes = 0;
           }
           if (is_last_frame) {
@@ -415,8 +411,7 @@
                           &s->flow_controlled_bytes_flowed, GRPC_ERROR_NONE);
           now_writing = true;
           if (s->flow_controlled_buffer.length > 0 ||
-              (s->stream_compression_send_enabled &&
-               s->compressed_data_buffer->length > 0)) {
+              s->compressed_data_buffer.length > 0) {
             GRPC_CHTTP2_STREAM_REF(s, "chttp2_writing:fork");
             grpc_chttp2_list_add_writable_stream(t, s);
           }
@@ -434,8 +429,7 @@
       if (s->send_trailing_metadata != NULL &&
           s->fetching_send_message == NULL &&
           s->flow_controlled_buffer.length == 0 &&
-          (!s->stream_compression_send_enabled ||
-           s->compressed_data_buffer->length == 0)) {
+          s->compressed_data_buffer.length == 0) {
         GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "sending trailing_metadata"));
         if (grpc_metadata_batch_is_empty(s->send_trailing_metadata)) {
           grpc_chttp2_encode_data(s->id, &s->flow_controlled_buffer, 0, true,
@@ -463,8 +457,7 @@
         s->send_trailing_metadata = NULL;
         s->sent_trailing_metadata = true;
         if (!t->is_client) {
-          t->ping_recv_state.last_ping_recv_time =
-              gpr_inf_past(GPR_CLOCK_MONOTONIC);
+          t->ping_recv_state.last_ping_recv_time = GRPC_MILLIS_INF_PAST;
           t->ping_recv_state.ping_strikes = 0;
         }
         if (!t->is_client && !s->read_closed) {
@@ -510,8 +503,7 @@
         &t->outbuf, grpc_chttp2_window_update_create(0, transport_announce,
                                                      &throwaway_stats));
     if (!t->is_client) {
-      t->ping_recv_state.last_ping_recv_time =
-          gpr_inf_past(GPR_CLOCK_MONOTONIC);
+      t->ping_recv_state.last_ping_recv_time = GRPC_MILLIS_INF_PAST;
       t->ping_recv_state.ping_strikes = 0;
     }
   }
diff --git a/src/core/ext/transport/inproc/inproc_transport.c b/src/core/ext/transport/inproc/inproc_transport.c
index 31739d0..1001d74 100644
--- a/src/core/ext/transport/inproc/inproc_transport.c
+++ b/src/core/ext/transport/inproc/inproc_transport.c
@@ -150,7 +150,7 @@
   grpc_metadata_batch write_buffer_initial_md;
   bool write_buffer_initial_md_filled;
   uint32_t write_buffer_initial_md_flags;
-  gpr_timespec write_buffer_deadline;
+  grpc_millis write_buffer_deadline;
   slice_buffer_list write_buffer_message;
   grpc_metadata_batch write_buffer_trailing_md;
   bool write_buffer_trailing_md_filled;
@@ -180,7 +180,7 @@
   grpc_error *cancel_self_error;
   grpc_error *cancel_other_error;
 
-  gpr_timespec deadline;
+  grpc_millis deadline;
 
   bool listed;
   struct inproc_stream *stream_list_prev;
@@ -377,8 +377,8 @@
   s->cancel_self_error = GRPC_ERROR_NONE;
   s->cancel_other_error = GRPC_ERROR_NONE;
   s->write_buffer_cancel_error = GRPC_ERROR_NONE;
-  s->deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
-  s->write_buffer_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  s->deadline = GRPC_MILLIS_INF_FUTURE;
+  s->write_buffer_deadline = GRPC_MILLIS_INF_FUTURE;
 
   s->stream_list_prev = NULL;
   gpr_mu_lock(&t->mu->mu);
@@ -421,7 +421,7 @@
                        cs->write_buffer_initial_md_flags,
                        &s->to_read_initial_md, &s->to_read_initial_md_flags,
                        &s->to_read_initial_md_filled);
-      s->deadline = gpr_time_min(s->deadline, cs->write_buffer_deadline);
+      s->deadline = GPR_MIN(s->deadline, cs->write_buffer_deadline);
       grpc_metadata_batch_clear(exec_ctx, &cs->write_buffer_initial_md);
       cs->write_buffer_initial_md_filled = false;
     }
@@ -956,10 +956,10 @@
               dest, destflags, destfilled);
         }
         if (s->t->is_client) {
-          gpr_timespec *dl =
+          grpc_millis *dl =
               (other == NULL) ? &s->write_buffer_deadline : &other->deadline;
-          *dl = gpr_time_min(*dl, op->payload->send_initial_metadata
-                                      .send_initial_metadata->deadline);
+          *dl = GPR_MIN(*dl, op->payload->send_initial_metadata
+                                 .send_initial_metadata->deadline);
           s->initial_md_sent = true;
         }
       }
diff --git a/src/core/lib/support/backoff.c b/src/core/lib/backoff/backoff.c
similarity index 68%
rename from src/core/lib/support/backoff.c
rename to src/core/lib/backoff/backoff.c
index 6dc0df4..b34c4a8 100644
--- a/src/core/lib/support/backoff.c
+++ b/src/core/lib/backoff/backoff.c
@@ -16,13 +16,14 @@
  *
  */
 
-#include "src/core/lib/support/backoff.h"
+#include "src/core/lib/backoff/backoff.h"
 
 #include <grpc/support/useful.h>
 
-void gpr_backoff_init(gpr_backoff *backoff, int64_t initial_connect_timeout,
-                      double multiplier, double jitter,
-                      int64_t min_timeout_millis, int64_t max_timeout_millis) {
+void grpc_backoff_init(grpc_backoff *backoff,
+                       grpc_millis initial_connect_timeout, double multiplier,
+                       double jitter, grpc_millis min_timeout_millis,
+                       grpc_millis max_timeout_millis) {
   backoff->initial_connect_timeout = initial_connect_timeout;
   backoff->multiplier = multiplier;
   backoff->jitter = jitter;
@@ -31,11 +32,11 @@
   backoff->rng_state = (uint32_t)gpr_now(GPR_CLOCK_REALTIME).tv_nsec;
 }
 
-gpr_timespec gpr_backoff_begin(gpr_backoff *backoff, gpr_timespec now) {
+grpc_millis grpc_backoff_begin(grpc_exec_ctx *exec_ctx, grpc_backoff *backoff) {
   backoff->current_timeout_millis = backoff->initial_connect_timeout;
-  const int64_t first_timeout =
+  const grpc_millis first_timeout =
       GPR_MAX(backoff->current_timeout_millis, backoff->min_timeout_millis);
-  return gpr_time_add(now, gpr_time_from_millis(first_timeout, GPR_TIMESPAN));
+  return grpc_exec_ctx_now(exec_ctx) + first_timeout;
 }
 
 /* Generate a random number between 0 and 1. */
@@ -44,7 +45,7 @@
   return *rng_state / (double)((uint32_t)1 << 31);
 }
 
-gpr_timespec gpr_backoff_step(gpr_backoff *backoff, gpr_timespec now) {
+grpc_millis grpc_backoff_step(grpc_exec_ctx *exec_ctx, grpc_backoff *backoff) {
   const double new_timeout_millis =
       backoff->multiplier * (double)backoff->current_timeout_millis;
   backoff->current_timeout_millis =
@@ -58,15 +59,15 @@
   backoff->current_timeout_millis =
       (int64_t)((double)(backoff->current_timeout_millis) + jitter);
 
-  const gpr_timespec current_deadline = gpr_time_add(
-      now, gpr_time_from_millis(backoff->current_timeout_millis, GPR_TIMESPAN));
+  const grpc_millis current_deadline =
+      grpc_exec_ctx_now(exec_ctx) + backoff->current_timeout_millis;
 
-  const gpr_timespec min_deadline = gpr_time_add(
-      now, gpr_time_from_millis(backoff->min_timeout_millis, GPR_TIMESPAN));
+  const grpc_millis min_deadline =
+      grpc_exec_ctx_now(exec_ctx) + backoff->min_timeout_millis;
 
-  return gpr_time_max(current_deadline, min_deadline);
+  return GPR_MAX(current_deadline, min_deadline);
 }
 
-void gpr_backoff_reset(gpr_backoff *backoff) {
+void grpc_backoff_reset(grpc_backoff *backoff) {
   backoff->current_timeout_millis = backoff->initial_connect_timeout;
 }
diff --git a/src/core/lib/backoff/backoff.h b/src/core/lib/backoff/backoff.h
new file mode 100644
index 0000000..32efdb9
--- /dev/null
+++ b/src/core/lib/backoff/backoff.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2016 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_BACKOFF_BACKOFF_H
+#define GRPC_CORE_LIB_BACKOFF_BACKOFF_H
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+typedef struct {
+  /// const:  how long to wait after the first failure before retrying
+  grpc_millis initial_connect_timeout;
+  /// const: factor with which to multiply backoff after a failed retry
+  double multiplier;
+  /// const: amount to randomize backoffs
+  double jitter;
+  /// const: minimum time between retries in milliseconds
+  grpc_millis min_timeout_millis;
+  /// const: maximum time between retries in milliseconds
+  grpc_millis max_timeout_millis;
+
+  /// random number generator
+  uint32_t rng_state;
+
+  /// current retry timeout in milliseconds
+  grpc_millis current_timeout_millis;
+} grpc_backoff;
+
+/// Initialize backoff machinery - does not need to be destroyed
+void grpc_backoff_init(grpc_backoff *backoff,
+                       grpc_millis initial_connect_timeout, double multiplier,
+                       double jitter, grpc_millis min_timeout_millis,
+                       grpc_millis max_timeout_millis);
+
+/// Begin retry loop: returns a timespec for the NEXT retry
+grpc_millis grpc_backoff_begin(grpc_exec_ctx *exec_ctx, grpc_backoff *backoff);
+/// Step a retry loop: returns a timespec for the NEXT retry
+grpc_millis grpc_backoff_step(grpc_exec_ctx *exec_ctx, grpc_backoff *backoff);
+/// Reset the backoff, so the next grpc_backoff_step will be a
+/// grpc_backoff_begin
+/// instead
+void grpc_backoff_reset(grpc_backoff *backoff);
+
+#endif /* GRPC_CORE_LIB_BACKOFF_BACKOFF_H */
diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h
index f0de80f..5c00c09 100644
--- a/src/core/lib/channel/channel_stack.h
+++ b/src/core/lib/channel/channel_stack.h
@@ -70,7 +70,7 @@
   grpc_call_context_element *context;
   grpc_slice path;
   gpr_timespec start_time;
-  gpr_timespec deadline;
+  grpc_millis deadline;
   gpr_arena *arena;
   grpc_call_combiner *call_combiner;
 } grpc_call_element_args;
diff --git a/src/core/lib/channel/handshaker.c b/src/core/lib/channel/handshaker.c
index 1753da5..b27ee37 100644
--- a/src/core/lib/channel/handshaker.c
+++ b/src/core/lib/channel/handshaker.c
@@ -232,7 +232,7 @@
 void grpc_handshake_manager_do_handshake(
     grpc_exec_ctx* exec_ctx, grpc_handshake_manager* mgr,
     grpc_endpoint* endpoint, const grpc_channel_args* channel_args,
-    gpr_timespec deadline, grpc_tcp_server_acceptor* acceptor,
+    grpc_millis deadline, grpc_tcp_server_acceptor* acceptor,
     grpc_iomgr_cb_func on_handshake_done, void* user_data) {
   gpr_mu_lock(&mgr->mu);
   GPR_ASSERT(mgr->index == 0);
@@ -255,9 +255,7 @@
   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),
-                  &mgr->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC));
+  grpc_timer_init(exec_ctx, &mgr->deadline_timer, deadline, &mgr->on_timeout);
   // 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/channel/handshaker.h b/src/core/lib/channel/handshaker.h
index eb9a59b..09b4a27 100644
--- a/src/core/lib/channel/handshaker.h
+++ b/src/core/lib/channel/handshaker.h
@@ -145,7 +145,7 @@
 void grpc_handshake_manager_do_handshake(
     grpc_exec_ctx* exec_ctx, grpc_handshake_manager* mgr,
     grpc_endpoint* endpoint, const grpc_channel_args* channel_args,
-    gpr_timespec deadline, grpc_tcp_server_acceptor* acceptor,
+    grpc_millis deadline, grpc_tcp_server_acceptor* acceptor,
     grpc_iomgr_cb_func on_handshake_done, void* user_data);
 
 /// Add \a mgr to the server side list of all pending handshake managers, the
diff --git a/src/core/lib/compression/stream_compression.c b/src/core/lib/compression/stream_compression.c
index ba93027..411489f 100644
--- a/src/core/lib/compression/stream_compression.c
+++ b/src/core/lib/compression/stream_compression.c
@@ -16,177 +16,62 @@
  *
  */
 
-#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
 #include "src/core/lib/compression/stream_compression.h"
-#include "src/core/lib/iomgr/exec_ctx.h"
-#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/compression/stream_compression_gzip.h"
 
-#define OUTPUT_BLOCK_SIZE (1024)
-
-static bool gzip_flate(grpc_stream_compression_context *ctx,
-                       grpc_slice_buffer *in, grpc_slice_buffer *out,
-                       size_t *output_size, size_t max_output_size, int flush,
-                       bool *end_of_context) {
-  GPR_ASSERT(flush == 0 || flush == Z_SYNC_FLUSH || flush == Z_FINISH);
-  /* Full flush is not allowed when inflating. */
-  GPR_ASSERT(!(ctx->flate == inflate && (flush == Z_FINISH)));
-
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  int r;
-  bool eoc = false;
-  size_t original_max_output_size = max_output_size;
-  while (max_output_size > 0 && (in->length > 0 || flush) && !eoc) {
-    size_t slice_size = max_output_size < OUTPUT_BLOCK_SIZE ? max_output_size
-                                                            : OUTPUT_BLOCK_SIZE;
-    grpc_slice slice_out = GRPC_SLICE_MALLOC(slice_size);
-    ctx->zs.avail_out = (uInt)slice_size;
-    ctx->zs.next_out = GRPC_SLICE_START_PTR(slice_out);
-    while (ctx->zs.avail_out > 0 && in->length > 0 && !eoc) {
-      grpc_slice slice = grpc_slice_buffer_take_first(in);
-      ctx->zs.avail_in = (uInt)GRPC_SLICE_LENGTH(slice);
-      ctx->zs.next_in = GRPC_SLICE_START_PTR(slice);
-      r = ctx->flate(&ctx->zs, Z_NO_FLUSH);
-      if (r < 0 && r != Z_BUF_ERROR) {
-        gpr_log(GPR_ERROR, "zlib error (%d)", r);
-        grpc_slice_unref_internal(&exec_ctx, slice_out);
-        grpc_exec_ctx_finish(&exec_ctx);
-        return false;
-      } else if (r == Z_STREAM_END && ctx->flate == inflate) {
-        eoc = true;
-      }
-      if (ctx->zs.avail_in > 0) {
-        grpc_slice_buffer_undo_take_first(
-            in,
-            grpc_slice_sub(slice, GRPC_SLICE_LENGTH(slice) - ctx->zs.avail_in,
-                           GRPC_SLICE_LENGTH(slice)));
-      }
-      grpc_slice_unref_internal(&exec_ctx, slice);
-    }
-    if (flush != 0 && ctx->zs.avail_out > 0 && !eoc) {
-      GPR_ASSERT(in->length == 0);
-      r = ctx->flate(&ctx->zs, flush);
-      if (flush == Z_SYNC_FLUSH) {
-        switch (r) {
-          case Z_OK:
-            /* Maybe flush is not complete; just made some partial progress. */
-            if (ctx->zs.avail_out > 0) {
-              flush = 0;
-            }
-            break;
-          case Z_BUF_ERROR:
-          case Z_STREAM_END:
-            flush = 0;
-            break;
-          default:
-            gpr_log(GPR_ERROR, "zlib error (%d)", r);
-            grpc_slice_unref_internal(&exec_ctx, slice_out);
-            grpc_exec_ctx_finish(&exec_ctx);
-            return false;
-        }
-      } else if (flush == Z_FINISH) {
-        switch (r) {
-          case Z_OK:
-          case Z_BUF_ERROR:
-            /* Wait for the next loop to assign additional output space. */
-            GPR_ASSERT(ctx->zs.avail_out == 0);
-            break;
-          case Z_STREAM_END:
-            flush = 0;
-            break;
-          default:
-            gpr_log(GPR_ERROR, "zlib error (%d)", r);
-            grpc_slice_unref_internal(&exec_ctx, slice_out);
-            grpc_exec_ctx_finish(&exec_ctx);
-            return false;
-        }
-      }
-    }
-
-    if (ctx->zs.avail_out == 0) {
-      grpc_slice_buffer_add(out, slice_out);
-    } else if (ctx->zs.avail_out < slice_size) {
-      slice_out.data.refcounted.length -= ctx->zs.avail_out;
-      grpc_slice_buffer_add(out, slice_out);
-    } else {
-      grpc_slice_unref_internal(&exec_ctx, slice_out);
-    }
-    max_output_size -= (slice_size - ctx->zs.avail_out);
-  }
-  grpc_exec_ctx_finish(&exec_ctx);
-  if (end_of_context) {
-    *end_of_context = eoc;
-  }
-  if (output_size) {
-    *output_size = original_max_output_size - max_output_size;
-  }
-  return true;
-}
+extern const grpc_stream_compression_vtable
+    grpc_stream_compression_identity_vtable;
 
 bool grpc_stream_compress(grpc_stream_compression_context *ctx,
                           grpc_slice_buffer *in, grpc_slice_buffer *out,
                           size_t *output_size, size_t max_output_size,
                           grpc_stream_compression_flush flush) {
-  GPR_ASSERT(ctx->flate == deflate);
-  int gzip_flush;
-  switch (flush) {
-    case GRPC_STREAM_COMPRESSION_FLUSH_NONE:
-      gzip_flush = 0;
-      break;
-    case GRPC_STREAM_COMPRESSION_FLUSH_SYNC:
-      gzip_flush = Z_SYNC_FLUSH;
-      break;
-    case GRPC_STREAM_COMPRESSION_FLUSH_FINISH:
-      gzip_flush = Z_FINISH;
-      break;
-    default:
-      gzip_flush = 0;
-  }
-  return gzip_flate(ctx, in, out, output_size, max_output_size, gzip_flush,
-                    NULL);
+  return ctx->vtable->compress(ctx, in, out, output_size, max_output_size,
+                               flush);
 }
 
 bool grpc_stream_decompress(grpc_stream_compression_context *ctx,
                             grpc_slice_buffer *in, grpc_slice_buffer *out,
                             size_t *output_size, size_t max_output_size,
                             bool *end_of_context) {
-  GPR_ASSERT(ctx->flate == inflate);
-  return gzip_flate(ctx, in, out, output_size, max_output_size, Z_SYNC_FLUSH,
-                    end_of_context);
+  return ctx->vtable->decompress(ctx, in, out, output_size, max_output_size,
+                                 end_of_context);
 }
 
 grpc_stream_compression_context *grpc_stream_compression_context_create(
     grpc_stream_compression_method method) {
-  grpc_stream_compression_context *ctx =
-      (grpc_stream_compression_context *)gpr_zalloc(
-          sizeof(grpc_stream_compression_context));
-  int r;
-  if (ctx == NULL) {
-    return NULL;
+  switch (method) {
+    case GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS:
+    case GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS:
+      return grpc_stream_compression_identity_vtable.context_create(method);
+    case GRPC_STREAM_COMPRESSION_GZIP_COMPRESS:
+    case GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS:
+      return grpc_stream_compression_gzip_vtable.context_create(method);
+    default:
+      gpr_log(GPR_ERROR, "Unknown stream compression method: %d", method);
+      return NULL;
   }
-  if (method == GRPC_STREAM_COMPRESSION_DECOMPRESS) {
-    r = inflateInit2(&ctx->zs, 0x1F);
-    ctx->flate = inflate;
-  } else {
-    r = deflateInit2(&ctx->zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 0x1F, 8,
-                     Z_DEFAULT_STRATEGY);
-    ctx->flate = deflate;
-  }
-  if (r != Z_OK) {
-    gpr_free(ctx);
-    return NULL;
-  }
-
-  return ctx;
 }
 
 void grpc_stream_compression_context_destroy(
     grpc_stream_compression_context *ctx) {
-  if (ctx->flate == inflate) {
-    inflateEnd(&ctx->zs);
+  ctx->vtable->context_destroy(ctx);
+}
+
+int grpc_stream_compression_method_parse(
+    grpc_slice value, bool is_compress,
+    grpc_stream_compression_method *method) {
+  if (grpc_slice_eq(value, GRPC_MDSTR_IDENTITY)) {
+    *method = is_compress ? GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS
+                          : GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS;
+    return 1;
+  } else if (grpc_slice_eq(value, GRPC_MDSTR_GZIP)) {
+    *method = is_compress ? GRPC_STREAM_COMPRESSION_GZIP_COMPRESS
+                          : GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS;
+    return 1;
   } else {
-    deflateEnd(&ctx->zs);
+    return 0;
   }
-  gpr_free(ctx);
 }
diff --git a/src/core/lib/compression/stream_compression.h b/src/core/lib/compression/stream_compression.h
index 844dff8..6d07328 100644
--- a/src/core/lib/compression/stream_compression.h
+++ b/src/core/lib/compression/stream_compression.h
@@ -24,15 +24,20 @@
 #include <grpc/slice_buffer.h>
 #include <zlib.h>
 
+#include "src/core/lib/transport/static_metadata.h"
+
+typedef struct grpc_stream_compression_vtable grpc_stream_compression_vtable;
+
 /* Stream compression/decompression context */
 typedef struct grpc_stream_compression_context {
-  z_stream zs;
-  int (*flate)(z_stream *zs, int flush);
+  const grpc_stream_compression_vtable *vtable;
 } grpc_stream_compression_context;
 
 typedef enum grpc_stream_compression_method {
-  GRPC_STREAM_COMPRESSION_COMPRESS = 0,
-  GRPC_STREAM_COMPRESSION_DECOMPRESS,
+  GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS = 0,
+  GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS,
+  GRPC_STREAM_COMPRESSION_GZIP_COMPRESS,
+  GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS,
   GRPC_STREAM_COMPRESSION_METHOD_COUNT
 } grpc_stream_compression_method;
 
@@ -43,6 +48,19 @@
   GRPC_STREAM_COMPRESSION_FLUSH_COUNT
 } grpc_stream_compression_flush;
 
+struct grpc_stream_compression_vtable {
+  bool (*compress)(grpc_stream_compression_context *ctx, grpc_slice_buffer *in,
+                   grpc_slice_buffer *out, size_t *output_size,
+                   size_t max_output_size, grpc_stream_compression_flush flush);
+  bool (*decompress)(grpc_stream_compression_context *ctx,
+                     grpc_slice_buffer *in, grpc_slice_buffer *out,
+                     size_t *output_size, size_t max_output_size,
+                     bool *end_of_context);
+  grpc_stream_compression_context *(*context_create)(
+      grpc_stream_compression_method method);
+  void (*context_destroy)(grpc_stream_compression_context *ctx);
+};
+
 /**
  * Compress bytes provided in \a in with a given context, with an optional flush
  * at the end of compression. Emits at most \a max_output_size compressed bytes
@@ -87,4 +105,10 @@
 void grpc_stream_compression_context_destroy(
     grpc_stream_compression_context *ctx);
 
+/**
+ * Parse stream compression method based on algorithm name
+ */
+int grpc_stream_compression_method_parse(
+    grpc_slice value, bool is_compress, grpc_stream_compression_method *method);
+
 #endif
diff --git a/src/core/lib/compression/stream_compression_gzip.c b/src/core/lib/compression/stream_compression_gzip.c
new file mode 100644
index 0000000..abcbdb3
--- /dev/null
+++ b/src/core/lib/compression/stream_compression_gzip.c
@@ -0,0 +1,228 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/compression/stream_compression_gzip.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+#define OUTPUT_BLOCK_SIZE (1024)
+
+typedef struct grpc_stream_compression_context_gzip {
+  grpc_stream_compression_context base;
+
+  z_stream zs;
+  int (*flate)(z_stream *zs, int flush);
+} grpc_stream_compression_context_gzip;
+
+static bool gzip_flate(grpc_stream_compression_context_gzip *ctx,
+                       grpc_slice_buffer *in, grpc_slice_buffer *out,
+                       size_t *output_size, size_t max_output_size, int flush,
+                       bool *end_of_context) {
+  GPR_ASSERT(flush == 0 || flush == Z_SYNC_FLUSH || flush == Z_FINISH);
+  /* Full flush is not allowed when inflating. */
+  GPR_ASSERT(!(ctx->flate == inflate && (flush == Z_FINISH)));
+
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  int r;
+  bool eoc = false;
+  size_t original_max_output_size = max_output_size;
+  while (max_output_size > 0 && (in->length > 0 || flush) && !eoc) {
+    size_t slice_size = max_output_size < OUTPUT_BLOCK_SIZE ? max_output_size
+                                                            : OUTPUT_BLOCK_SIZE;
+    grpc_slice slice_out = GRPC_SLICE_MALLOC(slice_size);
+    ctx->zs.avail_out = (uInt)slice_size;
+    ctx->zs.next_out = GRPC_SLICE_START_PTR(slice_out);
+    while (ctx->zs.avail_out > 0 && in->length > 0 && !eoc) {
+      grpc_slice slice = grpc_slice_buffer_take_first(in);
+      ctx->zs.avail_in = (uInt)GRPC_SLICE_LENGTH(slice);
+      ctx->zs.next_in = GRPC_SLICE_START_PTR(slice);
+      r = ctx->flate(&ctx->zs, Z_NO_FLUSH);
+      if (r < 0 && r != Z_BUF_ERROR) {
+        gpr_log(GPR_ERROR, "zlib error (%d)", r);
+        grpc_slice_unref_internal(&exec_ctx, slice_out);
+        grpc_exec_ctx_finish(&exec_ctx);
+        return false;
+      } else if (r == Z_STREAM_END && ctx->flate == inflate) {
+        eoc = true;
+      }
+      if (ctx->zs.avail_in > 0) {
+        grpc_slice_buffer_undo_take_first(
+            in,
+            grpc_slice_sub(slice, GRPC_SLICE_LENGTH(slice) - ctx->zs.avail_in,
+                           GRPC_SLICE_LENGTH(slice)));
+      }
+      grpc_slice_unref_internal(&exec_ctx, slice);
+    }
+    if (flush != 0 && ctx->zs.avail_out > 0 && !eoc) {
+      GPR_ASSERT(in->length == 0);
+      r = ctx->flate(&ctx->zs, flush);
+      if (flush == Z_SYNC_FLUSH) {
+        switch (r) {
+          case Z_OK:
+            /* Maybe flush is not complete; just made some partial progress. */
+            if (ctx->zs.avail_out > 0) {
+              flush = 0;
+            }
+            break;
+          case Z_BUF_ERROR:
+          case Z_STREAM_END:
+            flush = 0;
+            break;
+          default:
+            gpr_log(GPR_ERROR, "zlib error (%d)", r);
+            grpc_slice_unref_internal(&exec_ctx, slice_out);
+            grpc_exec_ctx_finish(&exec_ctx);
+            return false;
+        }
+      } else if (flush == Z_FINISH) {
+        switch (r) {
+          case Z_OK:
+          case Z_BUF_ERROR:
+            /* Wait for the next loop to assign additional output space. */
+            GPR_ASSERT(ctx->zs.avail_out == 0);
+            break;
+          case Z_STREAM_END:
+            flush = 0;
+            break;
+          default:
+            gpr_log(GPR_ERROR, "zlib error (%d)", r);
+            grpc_slice_unref_internal(&exec_ctx, slice_out);
+            grpc_exec_ctx_finish(&exec_ctx);
+            return false;
+        }
+      }
+    }
+
+    if (ctx->zs.avail_out == 0) {
+      grpc_slice_buffer_add(out, slice_out);
+    } else if (ctx->zs.avail_out < slice_size) {
+      slice_out.data.refcounted.length -= ctx->zs.avail_out;
+      grpc_slice_buffer_add(out, slice_out);
+    } else {
+      grpc_slice_unref_internal(&exec_ctx, slice_out);
+    }
+    max_output_size -= (slice_size - ctx->zs.avail_out);
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+  if (end_of_context) {
+    *end_of_context = eoc;
+  }
+  if (output_size) {
+    *output_size = original_max_output_size - max_output_size;
+  }
+  return true;
+}
+
+static bool grpc_stream_compress_gzip(grpc_stream_compression_context *ctx,
+                                      grpc_slice_buffer *in,
+                                      grpc_slice_buffer *out,
+                                      size_t *output_size,
+                                      size_t max_output_size,
+                                      grpc_stream_compression_flush flush) {
+  if (ctx == NULL) {
+    return false;
+  }
+  grpc_stream_compression_context_gzip *gzip_ctx =
+      (grpc_stream_compression_context_gzip *)ctx;
+  GPR_ASSERT(gzip_ctx->flate == deflate);
+  int gzip_flush;
+  switch (flush) {
+    case GRPC_STREAM_COMPRESSION_FLUSH_NONE:
+      gzip_flush = 0;
+      break;
+    case GRPC_STREAM_COMPRESSION_FLUSH_SYNC:
+      gzip_flush = Z_SYNC_FLUSH;
+      break;
+    case GRPC_STREAM_COMPRESSION_FLUSH_FINISH:
+      gzip_flush = Z_FINISH;
+      break;
+    default:
+      gzip_flush = 0;
+  }
+  return gzip_flate(gzip_ctx, in, out, output_size, max_output_size, gzip_flush,
+                    NULL);
+}
+
+static bool grpc_stream_decompress_gzip(grpc_stream_compression_context *ctx,
+                                        grpc_slice_buffer *in,
+                                        grpc_slice_buffer *out,
+                                        size_t *output_size,
+                                        size_t max_output_size,
+                                        bool *end_of_context) {
+  if (ctx == NULL) {
+    return false;
+  }
+  grpc_stream_compression_context_gzip *gzip_ctx =
+      (grpc_stream_compression_context_gzip *)ctx;
+  GPR_ASSERT(gzip_ctx->flate == inflate);
+  return gzip_flate(gzip_ctx, in, out, output_size, max_output_size,
+                    Z_SYNC_FLUSH, end_of_context);
+}
+
+static grpc_stream_compression_context *
+grpc_stream_compression_context_create_gzip(
+    grpc_stream_compression_method method) {
+  GPR_ASSERT(method == GRPC_STREAM_COMPRESSION_GZIP_COMPRESS ||
+             method == GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS);
+  grpc_stream_compression_context_gzip *gzip_ctx =
+      (grpc_stream_compression_context_gzip *)gpr_zalloc(
+          sizeof(grpc_stream_compression_context_gzip));
+  int r;
+  if (gzip_ctx == NULL) {
+    return NULL;
+  }
+  if (method == GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS) {
+    r = inflateInit2(&gzip_ctx->zs, 0x1F);
+    gzip_ctx->flate = inflate;
+  } else {
+    r = deflateInit2(&gzip_ctx->zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 0x1F, 8,
+                     Z_DEFAULT_STRATEGY);
+    gzip_ctx->flate = deflate;
+  }
+  if (r != Z_OK) {
+    gpr_free(gzip_ctx);
+    return NULL;
+  }
+
+  gzip_ctx->base.vtable = &grpc_stream_compression_gzip_vtable;
+  return (grpc_stream_compression_context *)gzip_ctx;
+}
+
+static void grpc_stream_compression_context_destroy_gzip(
+    grpc_stream_compression_context *ctx) {
+  if (ctx == NULL) {
+    return;
+  }
+  grpc_stream_compression_context_gzip *gzip_ctx =
+      (grpc_stream_compression_context_gzip *)ctx;
+  if (gzip_ctx->flate == inflate) {
+    inflateEnd(&gzip_ctx->zs);
+  } else {
+    deflateEnd(&gzip_ctx->zs);
+  }
+  gpr_free(ctx);
+}
+
+const grpc_stream_compression_vtable grpc_stream_compression_gzip_vtable = {
+    .compress = grpc_stream_compress_gzip,
+    .decompress = grpc_stream_decompress_gzip,
+    .context_create = grpc_stream_compression_context_create_gzip,
+    .context_destroy = grpc_stream_compression_context_destroy_gzip};
diff --git a/src/core/lib/compression/stream_compression_gzip.h b/src/core/lib/compression/stream_compression_gzip.h
new file mode 100644
index 0000000..7cf49a0
--- /dev/null
+++ b/src/core/lib/compression/stream_compression_gzip.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_GZIP_H
+#define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_GZIP_H
+
+#include "src/core/lib/compression/stream_compression.h"
+
+extern const grpc_stream_compression_vtable grpc_stream_compression_gzip_vtable;
+
+#endif
diff --git a/src/core/lib/compression/stream_compression_identity.c b/src/core/lib/compression/stream_compression_identity.c
new file mode 100644
index 0000000..3dfcf53
--- /dev/null
+++ b/src/core/lib/compression/stream_compression_identity.c
@@ -0,0 +1,94 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/compression/stream_compression_identity.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/slice_internal.h"
+
+#define OUTPUT_BLOCK_SIZE (1024)
+
+/* Singleton context used for all identity streams. */
+static grpc_stream_compression_context identity_ctx = {
+    .vtable = &grpc_stream_compression_identity_vtable};
+
+static void grpc_stream_compression_pass_through(grpc_slice_buffer *in,
+                                                 grpc_slice_buffer *out,
+                                                 size_t *output_size,
+                                                 size_t max_output_size) {
+  if (max_output_size >= in->length) {
+    if (output_size) {
+      *output_size = in->length;
+    }
+    grpc_slice_buffer_move_into(in, out);
+  } else {
+    if (output_size) {
+      *output_size = max_output_size;
+    }
+    grpc_slice_buffer_move_first(in, max_output_size, out);
+  }
+}
+
+static bool grpc_stream_compress_identity(grpc_stream_compression_context *ctx,
+                                          grpc_slice_buffer *in,
+                                          grpc_slice_buffer *out,
+                                          size_t *output_size,
+                                          size_t max_output_size,
+                                          grpc_stream_compression_flush flush) {
+  if (ctx == NULL) {
+    return false;
+  }
+  grpc_stream_compression_pass_through(in, out, output_size, max_output_size);
+  return true;
+}
+
+static bool grpc_stream_decompress_identity(
+    grpc_stream_compression_context *ctx, grpc_slice_buffer *in,
+    grpc_slice_buffer *out, size_t *output_size, size_t max_output_size,
+    bool *end_of_context) {
+  if (ctx == NULL) {
+    return false;
+  }
+  grpc_stream_compression_pass_through(in, out, output_size, max_output_size);
+  if (end_of_context) {
+    *end_of_context = false;
+  }
+  return true;
+}
+
+static grpc_stream_compression_context *
+grpc_stream_compression_context_create_identity(
+    grpc_stream_compression_method method) {
+  GPR_ASSERT(method == GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS ||
+             method == GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS);
+  /* No context needed in this case. Use fake context instead. */
+  return (grpc_stream_compression_context *)&identity_ctx;
+}
+
+static void grpc_stream_compression_context_destroy_identity(
+    grpc_stream_compression_context *ctx) {
+  return;
+}
+
+const grpc_stream_compression_vtable grpc_stream_compression_identity_vtable = {
+    .compress = grpc_stream_compress_identity,
+    .decompress = grpc_stream_decompress_identity,
+    .context_create = grpc_stream_compression_context_create_identity,
+    .context_destroy = grpc_stream_compression_context_destroy_identity};
diff --git a/src/core/lib/compression/stream_compression_identity.h b/src/core/lib/compression/stream_compression_identity.h
new file mode 100644
index 0000000..41926e9
--- /dev/null
+++ b/src/core/lib/compression/stream_compression_identity.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_IDENTITY_H
+#define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_IDENTITY_H
+
+#include "src/core/lib/compression/stream_compression.h"
+
+extern const grpc_stream_compression_vtable
+    grpc_stream_compression_identity_vtable;
+
+#endif
diff --git a/src/core/lib/debug/stats_data.c b/src/core/lib/debug/stats_data.c
index 546e38b..5bd7884 100644
--- a/src/core/lib/debug/stats_data.c
+++ b/src/core/lib/debug/stats_data.c
@@ -78,6 +78,28 @@
     "http2_initiate_write_due_to_ping_response",
     "http2_initiate_write_due_to_force_rst_stream",
     "http2_spurious_writes_begun",
+    "hpack_recv_indexed",
+    "hpack_recv_lithdr_incidx",
+    "hpack_recv_lithdr_incidx_v",
+    "hpack_recv_lithdr_notidx",
+    "hpack_recv_lithdr_notidx_v",
+    "hpack_recv_lithdr_nvridx",
+    "hpack_recv_lithdr_nvridx_v",
+    "hpack_recv_uncompressed",
+    "hpack_recv_huffman",
+    "hpack_recv_binary",
+    "hpack_recv_binary_base64",
+    "hpack_send_indexed",
+    "hpack_send_lithdr_incidx",
+    "hpack_send_lithdr_incidx_v",
+    "hpack_send_lithdr_notidx",
+    "hpack_send_lithdr_notidx_v",
+    "hpack_send_lithdr_nvridx",
+    "hpack_send_lithdr_nvridx_v",
+    "hpack_send_uncompressed",
+    "hpack_send_huffman",
+    "hpack_send_binary",
+    "hpack_send_binary_base64",
     "combiner_locks_initiated",
     "combiner_locks_scheduled_items",
     "combiner_locks_scheduled_final_items",
@@ -88,8 +110,6 @@
     "executor_wakeup_initiated",
     "executor_queue_drained",
     "executor_push_retries",
-    "executor_threads_created",
-    "executor_threads_used",
     "server_requested_calls",
     "server_slowpath_requests_queued",
 };
@@ -159,6 +179,32 @@
     "Number of HTTP2 writes initiated due to 'ping_response'",
     "Number of HTTP2 writes initiated due to 'force_rst_stream'",
     "Number of HTTP2 writes initiated with nothing to write",
+    "Number of HPACK indexed fields received",
+    "Number of HPACK literal headers received with incremental indexing",
+    "Number of HPACK literal headers received with incremental indexing and "
+    "literal keys",
+    "Number of HPACK literal headers received with no indexing",
+    "Number of HPACK literal headers received with no indexing and literal "
+    "keys",
+    "Number of HPACK literal headers received with never-indexing",
+    "Number of HPACK literal headers received with never-indexing and literal "
+    "keys",
+    "Number of uncompressed strings received in metadata",
+    "Number of huffman encoded strings received in metadata",
+    "Number of binary strings received in metadata",
+    "Number of binary strings received encoded in base64 in metadata",
+    "Number of HPACK indexed fields sent",
+    "Number of HPACK literal headers sent with incremental indexing",
+    "Number of HPACK literal headers sent with incremental indexing and "
+    "literal keys",
+    "Number of HPACK literal headers sent with no indexing",
+    "Number of HPACK literal headers sent with no indexing and literal keys",
+    "Number of HPACK literal headers sent with never-indexing",
+    "Number of HPACK literal headers sent with never-indexing and literal keys",
+    "Number of uncompressed strings sent in metadata",
+    "Number of huffman encoded strings sent in metadata",
+    "Number of binary strings received in metadata",
+    "Number of binary strings received encoded in base64 in metadata",
     "Number of combiner lock entries by process (first items queued to a "
     "combiner)",
     "Number of items scheduled against combiner locks",
@@ -173,8 +219,6 @@
     "Number of times an executor queue was drained",
     "Number of times we raced and were forced to retry pushing a closure to "
     "the executor",
-    "Size of the backing thread pool for overflow gRPC Core work",
-    "How many executor threads actually got used",
     "How many calls were requested (not necessarily received) by the server",
     "How many times was the server slow path taken (indicates too few "
     "outstanding requests)",
@@ -192,7 +236,6 @@
     "http2_send_message_per_write",
     "http2_send_trailing_metadata_per_write",
     "http2_send_flowctl_per_write",
-    "executor_closures_per_wakeup",
     "server_cqs_checked",
 };
 const char *grpc_stats_histogram_doc[GRPC_STATS_HISTOGRAM_COUNT] = {
@@ -208,7 +251,6 @@
     "Number of streams whose payload was written per TCP write",
     "Number of streams terminated per TCP write",
     "Number of flow control updates written per TCP write",
-    "Number of closures executed each time an executor wakes up",
     "How many completion queues were checked looking for a CQ that had "
     "requested the incoming call",
 };
@@ -280,7 +322,6 @@
 const int grpc_stats_table_8[9] = {0, 1, 2, 4, 7, 13, 23, 39, 64};
 const uint8_t grpc_stats_table_9[9] = {0, 0, 1, 2, 2, 3, 4, 4, 5};
 void grpc_stats_inc_call_initial_size(grpc_exec_ctx *exec_ctx, int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 262144);
   if (value < 6) {
     GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE,
@@ -306,7 +347,6 @@
                                (exec_ctx), value, grpc_stats_table_0, 64));
 }
 void grpc_stats_inc_poll_events_returned(grpc_exec_ctx *exec_ctx, int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 1024);
   if (value < 29) {
     GRPC_STATS_INC_HISTOGRAM((exec_ctx),
@@ -333,7 +373,6 @@
                                (exec_ctx), value, grpc_stats_table_2, 128));
 }
 void grpc_stats_inc_tcp_write_size(grpc_exec_ctx *exec_ctx, int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 16777216);
   if (value < 5) {
     GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE,
@@ -359,7 +398,6 @@
                                (exec_ctx), value, grpc_stats_table_4, 64));
 }
 void grpc_stats_inc_tcp_write_iov_size(grpc_exec_ctx *exec_ctx, int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 1024);
   if (value < 13) {
     GRPC_STATS_INC_HISTOGRAM((exec_ctx),
@@ -385,7 +423,6 @@
                                (exec_ctx), value, grpc_stats_table_6, 64));
 }
 void grpc_stats_inc_tcp_read_size(grpc_exec_ctx *exec_ctx, int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 16777216);
   if (value < 5) {
     GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE,
@@ -411,7 +448,6 @@
                                (exec_ctx), value, grpc_stats_table_4, 64));
 }
 void grpc_stats_inc_tcp_read_offer(grpc_exec_ctx *exec_ctx, int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 16777216);
   if (value < 5) {
     GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER,
@@ -438,7 +474,6 @@
 }
 void grpc_stats_inc_tcp_read_offer_iov_size(grpc_exec_ctx *exec_ctx,
                                             int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 1024);
   if (value < 13) {
     GRPC_STATS_INC_HISTOGRAM(
@@ -466,7 +501,6 @@
 }
 void grpc_stats_inc_http2_send_message_size(grpc_exec_ctx *exec_ctx,
                                             int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 16777216);
   if (value < 5) {
     GRPC_STATS_INC_HISTOGRAM(
@@ -494,7 +528,6 @@
 }
 void grpc_stats_inc_http2_send_initial_metadata_per_write(
     grpc_exec_ctx *exec_ctx, int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 1024);
   if (value < 13) {
     GRPC_STATS_INC_HISTOGRAM(
@@ -524,7 +557,6 @@
 }
 void grpc_stats_inc_http2_send_message_per_write(grpc_exec_ctx *exec_ctx,
                                                  int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 1024);
   if (value < 13) {
     GRPC_STATS_INC_HISTOGRAM(
@@ -552,7 +584,6 @@
 }
 void grpc_stats_inc_http2_send_trailing_metadata_per_write(
     grpc_exec_ctx *exec_ctx, int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 1024);
   if (value < 13) {
     GRPC_STATS_INC_HISTOGRAM(
@@ -582,7 +613,6 @@
 }
 void grpc_stats_inc_http2_send_flowctl_per_write(grpc_exec_ctx *exec_ctx,
                                                  int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 1024);
   if (value < 13) {
     GRPC_STATS_INC_HISTOGRAM(
@@ -608,36 +638,7 @@
                            grpc_stats_histo_find_bucket_slow(
                                (exec_ctx), value, grpc_stats_table_6, 64));
 }
-void grpc_stats_inc_executor_closures_per_wakeup(grpc_exec_ctx *exec_ctx,
-                                                 int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
-  value = GPR_CLAMP(value, 0, 1024);
-  if (value < 13) {
-    GRPC_STATS_INC_HISTOGRAM(
-        (exec_ctx), GRPC_STATS_HISTOGRAM_EXECUTOR_CLOSURES_PER_WAKEUP, value);
-    return;
-  }
-  union {
-    double dbl;
-    uint64_t uint;
-  } _val, _bkt;
-  _val.dbl = value;
-  if (_val.uint < 4637863191261478912ull) {
-    int bucket =
-        grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13;
-    _bkt.dbl = grpc_stats_table_6[bucket];
-    bucket -= (_val.uint < _bkt.uint);
-    GRPC_STATS_INC_HISTOGRAM(
-        (exec_ctx), GRPC_STATS_HISTOGRAM_EXECUTOR_CLOSURES_PER_WAKEUP, bucket);
-    return;
-  }
-  GRPC_STATS_INC_HISTOGRAM((exec_ctx),
-                           GRPC_STATS_HISTOGRAM_EXECUTOR_CLOSURES_PER_WAKEUP,
-                           grpc_stats_histo_find_bucket_slow(
-                               (exec_ctx), value, grpc_stats_table_6, 64));
-}
 void grpc_stats_inc_server_cqs_checked(grpc_exec_ctx *exec_ctx, int value) {
-  /* Automatically generated by tools/codegen/core/gen_stats_data.py */
   value = GPR_CLAMP(value, 0, 64);
   if (value < 3) {
     GRPC_STATS_INC_HISTOGRAM((exec_ctx),
@@ -662,17 +663,17 @@
                            grpc_stats_histo_find_bucket_slow(
                                (exec_ctx), value, grpc_stats_table_8, 8));
 }
-const int grpc_stats_histo_buckets[14] = {64, 128, 64, 64, 64, 64, 64,
-                                          64, 64,  64, 64, 64, 64, 8};
-const int grpc_stats_histo_start[14] = {0,   64,  192, 256, 320, 384, 448,
-                                        512, 576, 640, 704, 768, 832, 896};
-const int *const grpc_stats_histo_bucket_boundaries[14] = {
+const int grpc_stats_histo_buckets[13] = {64, 128, 64, 64, 64, 64, 64,
+                                          64, 64,  64, 64, 64, 8};
+const int grpc_stats_histo_start[13] = {0,   64,  192, 256, 320, 384, 448,
+                                        512, 576, 640, 704, 768, 832};
+const int *const grpc_stats_histo_bucket_boundaries[13] = {
     grpc_stats_table_0, grpc_stats_table_2, grpc_stats_table_4,
     grpc_stats_table_6, grpc_stats_table_4, grpc_stats_table_4,
     grpc_stats_table_6, grpc_stats_table_4, grpc_stats_table_6,
     grpc_stats_table_6, grpc_stats_table_6, grpc_stats_table_6,
-    grpc_stats_table_6, grpc_stats_table_8};
-void (*const grpc_stats_inc_histogram[14])(grpc_exec_ctx *exec_ctx, int x) = {
+    grpc_stats_table_8};
+void (*const grpc_stats_inc_histogram[13])(grpc_exec_ctx *exec_ctx, int x) = {
     grpc_stats_inc_call_initial_size,
     grpc_stats_inc_poll_events_returned,
     grpc_stats_inc_tcp_write_size,
@@ -685,5 +686,4 @@
     grpc_stats_inc_http2_send_message_per_write,
     grpc_stats_inc_http2_send_trailing_metadata_per_write,
     grpc_stats_inc_http2_send_flowctl_per_write,
-    grpc_stats_inc_executor_closures_per_wakeup,
     grpc_stats_inc_server_cqs_checked};
diff --git a/src/core/lib/debug/stats_data.h b/src/core/lib/debug/stats_data.h
index 5358aef..507e5f7 100644
--- a/src/core/lib/debug/stats_data.h
+++ b/src/core/lib/debug/stats_data.h
@@ -80,6 +80,28 @@
   GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE,
   GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM,
   GRPC_STATS_COUNTER_HTTP2_SPURIOUS_WRITES_BEGUN,
+  GRPC_STATS_COUNTER_HPACK_RECV_INDEXED,
+  GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_INCIDX,
+  GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_INCIDX_V,
+  GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NOTIDX,
+  GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NOTIDX_V,
+  GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NVRIDX,
+  GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NVRIDX_V,
+  GRPC_STATS_COUNTER_HPACK_RECV_UNCOMPRESSED,
+  GRPC_STATS_COUNTER_HPACK_RECV_HUFFMAN,
+  GRPC_STATS_COUNTER_HPACK_RECV_BINARY,
+  GRPC_STATS_COUNTER_HPACK_RECV_BINARY_BASE64,
+  GRPC_STATS_COUNTER_HPACK_SEND_INDEXED,
+  GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_INCIDX,
+  GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_INCIDX_V,
+  GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NOTIDX,
+  GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NOTIDX_V,
+  GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NVRIDX,
+  GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NVRIDX_V,
+  GRPC_STATS_COUNTER_HPACK_SEND_UNCOMPRESSED,
+  GRPC_STATS_COUNTER_HPACK_SEND_HUFFMAN,
+  GRPC_STATS_COUNTER_HPACK_SEND_BINARY,
+  GRPC_STATS_COUNTER_HPACK_SEND_BINARY_BASE64,
   GRPC_STATS_COUNTER_COMBINER_LOCKS_INITIATED,
   GRPC_STATS_COUNTER_COMBINER_LOCKS_SCHEDULED_ITEMS,
   GRPC_STATS_COUNTER_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS,
@@ -90,8 +112,6 @@
   GRPC_STATS_COUNTER_EXECUTOR_WAKEUP_INITIATED,
   GRPC_STATS_COUNTER_EXECUTOR_QUEUE_DRAINED,
   GRPC_STATS_COUNTER_EXECUTOR_PUSH_RETRIES,
-  GRPC_STATS_COUNTER_EXECUTOR_THREADS_CREATED,
-  GRPC_STATS_COUNTER_EXECUTOR_THREADS_USED,
   GRPC_STATS_COUNTER_SERVER_REQUESTED_CALLS,
   GRPC_STATS_COUNTER_SERVER_SLOWPATH_REQUESTS_QUEUED,
   GRPC_STATS_COUNTER_COUNT
@@ -111,7 +131,6 @@
   GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE,
   GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE,
   GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE,
-  GRPC_STATS_HISTOGRAM_EXECUTOR_CLOSURES_PER_WAKEUP,
   GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED,
   GRPC_STATS_HISTOGRAM_COUNT
 } grpc_stats_histograms;
@@ -142,11 +161,9 @@
   GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE_BUCKETS = 64,
   GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE_FIRST_SLOT = 768,
   GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE_BUCKETS = 64,
-  GRPC_STATS_HISTOGRAM_EXECUTOR_CLOSURES_PER_WAKEUP_FIRST_SLOT = 832,
-  GRPC_STATS_HISTOGRAM_EXECUTOR_CLOSURES_PER_WAKEUP_BUCKETS = 64,
-  GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED_FIRST_SLOT = 896,
+  GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED_FIRST_SLOT = 832,
   GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED_BUCKETS = 8,
-  GRPC_STATS_HISTOGRAM_BUCKETS = 904
+  GRPC_STATS_HISTOGRAM_BUCKETS = 840
 } grpc_stats_histogram_constants;
 #define GRPC_STATS_INC_CLIENT_CALLS_CREATED(exec_ctx) \
   GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED)
@@ -313,6 +330,64 @@
 #define GRPC_STATS_INC_HTTP2_SPURIOUS_WRITES_BEGUN(exec_ctx) \
   GRPC_STATS_INC_COUNTER((exec_ctx),                         \
                          GRPC_STATS_COUNTER_HTTP2_SPURIOUS_WRITES_BEGUN)
+#define GRPC_STATS_INC_HPACK_RECV_INDEXED(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_HPACK_RECV_INDEXED)
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                      \
+                         GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_INCIDX)
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX_V(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                        \
+                         GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_INCIDX_V)
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                      \
+                         GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NOTIDX)
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX_V(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                        \
+                         GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NOTIDX_V)
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                      \
+                         GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NVRIDX)
+#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX_V(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                        \
+                         GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NVRIDX_V)
+#define GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_HPACK_RECV_UNCOMPRESSED)
+#define GRPC_STATS_INC_HPACK_RECV_HUFFMAN(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_HPACK_RECV_HUFFMAN)
+#define GRPC_STATS_INC_HPACK_RECV_BINARY(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_HPACK_RECV_BINARY)
+#define GRPC_STATS_INC_HPACK_RECV_BINARY_BASE64(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                      \
+                         GRPC_STATS_COUNTER_HPACK_RECV_BINARY_BASE64)
+#define GRPC_STATS_INC_HPACK_SEND_INDEXED(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_HPACK_SEND_INDEXED)
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                      \
+                         GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_INCIDX)
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                        \
+                         GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_INCIDX_V)
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                      \
+                         GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NOTIDX)
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                        \
+                         GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NOTIDX_V)
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NVRIDX(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                      \
+                         GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NVRIDX)
+#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NVRIDX_V(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                        \
+                         GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NVRIDX_V)
+#define GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_HPACK_SEND_UNCOMPRESSED)
+#define GRPC_STATS_INC_HPACK_SEND_HUFFMAN(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_HPACK_SEND_HUFFMAN)
+#define GRPC_STATS_INC_HPACK_SEND_BINARY(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_HPACK_SEND_BINARY)
+#define GRPC_STATS_INC_HPACK_SEND_BINARY_BASE64(exec_ctx) \
+  GRPC_STATS_INC_COUNTER((exec_ctx),                      \
+                         GRPC_STATS_COUNTER_HPACK_SEND_BINARY_BASE64)
 #define GRPC_STATS_INC_COMBINER_LOCKS_INITIATED(exec_ctx) \
   GRPC_STATS_INC_COUNTER((exec_ctx),                      \
                          GRPC_STATS_COUNTER_COMBINER_LOCKS_INITIATED)
@@ -341,11 +416,6 @@
   GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_EXECUTOR_QUEUE_DRAINED)
 #define GRPC_STATS_INC_EXECUTOR_PUSH_RETRIES(exec_ctx) \
   GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_EXECUTOR_PUSH_RETRIES)
-#define GRPC_STATS_INC_EXECUTOR_THREADS_CREATED(exec_ctx) \
-  GRPC_STATS_INC_COUNTER((exec_ctx),                      \
-                         GRPC_STATS_COUNTER_EXECUTOR_THREADS_CREATED)
-#define GRPC_STATS_INC_EXECUTOR_THREADS_USED(exec_ctx) \
-  GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_EXECUTOR_THREADS_USED)
 #define GRPC_STATS_INC_SERVER_REQUESTED_CALLS(exec_ctx) \
   GRPC_STATS_INC_COUNTER((exec_ctx), GRPC_STATS_COUNTER_SERVER_REQUESTED_CALLS)
 #define GRPC_STATS_INC_SERVER_SLOWPATH_REQUESTS_QUEUED(exec_ctx) \
@@ -392,17 +462,13 @@
   grpc_stats_inc_http2_send_flowctl_per_write((exec_ctx), (int)(value))
 void grpc_stats_inc_http2_send_flowctl_per_write(grpc_exec_ctx *exec_ctx,
                                                  int x);
-#define GRPC_STATS_INC_EXECUTOR_CLOSURES_PER_WAKEUP(exec_ctx, value) \
-  grpc_stats_inc_executor_closures_per_wakeup((exec_ctx), (int)(value))
-void grpc_stats_inc_executor_closures_per_wakeup(grpc_exec_ctx *exec_ctx,
-                                                 int x);
 #define GRPC_STATS_INC_SERVER_CQS_CHECKED(exec_ctx, value) \
   grpc_stats_inc_server_cqs_checked((exec_ctx), (int)(value))
 void grpc_stats_inc_server_cqs_checked(grpc_exec_ctx *exec_ctx, int x);
-extern const int grpc_stats_histo_buckets[14];
-extern const int grpc_stats_histo_start[14];
-extern const int *const grpc_stats_histo_bucket_boundaries[14];
-extern void (*const grpc_stats_inc_histogram[14])(grpc_exec_ctx *exec_ctx,
+extern const int grpc_stats_histo_buckets[13];
+extern const int grpc_stats_histo_start[13];
+extern const int *const grpc_stats_histo_bucket_boundaries[13];
+extern void (*const grpc_stats_inc_histogram[13])(grpc_exec_ctx *exec_ctx,
                                                   int x);
 
 #endif /* GRPC_CORE_LIB_DEBUG_STATS_DATA_H */
diff --git a/src/core/lib/debug/stats_data.yaml b/src/core/lib/debug/stats_data.yaml
index 8ebe321..5c0ab22 100644
--- a/src/core/lib/debug/stats_data.yaml
+++ b/src/core/lib/debug/stats_data.yaml
@@ -191,6 +191,50 @@
   doc: Number of HTTP2 writes initiated due to 'force_rst_stream'
 - counter: http2_spurious_writes_begun
   doc: Number of HTTP2 writes initiated with nothing to write
+- counter: hpack_recv_indexed
+  doc: Number of HPACK indexed fields received
+- counter: hpack_recv_lithdr_incidx
+  doc: Number of HPACK literal headers received with incremental indexing
+- counter: hpack_recv_lithdr_incidx_v
+  doc: Number of HPACK literal headers received with incremental indexing and literal keys
+- counter: hpack_recv_lithdr_notidx
+  doc: Number of HPACK literal headers received with no indexing
+- counter: hpack_recv_lithdr_notidx_v
+  doc: Number of HPACK literal headers received with no indexing and literal keys
+- counter: hpack_recv_lithdr_nvridx
+  doc: Number of HPACK literal headers received with never-indexing
+- counter: hpack_recv_lithdr_nvridx_v
+  doc: Number of HPACK literal headers received with never-indexing and literal keys
+- counter: hpack_recv_uncompressed
+  doc: Number of uncompressed strings received in metadata
+- counter: hpack_recv_huffman
+  doc: Number of huffman encoded strings received in metadata
+- counter: hpack_recv_binary
+  doc: Number of binary strings received in metadata
+- counter: hpack_recv_binary_base64
+  doc: Number of binary strings received encoded in base64 in metadata
+- counter: hpack_send_indexed
+  doc: Number of HPACK indexed fields sent
+- counter: hpack_send_lithdr_incidx
+  doc: Number of HPACK literal headers sent with incremental indexing
+- counter: hpack_send_lithdr_incidx_v
+  doc: Number of HPACK literal headers sent with incremental indexing and literal keys
+- counter: hpack_send_lithdr_notidx
+  doc: Number of HPACK literal headers sent with no indexing
+- counter: hpack_send_lithdr_notidx_v
+  doc: Number of HPACK literal headers sent with no indexing and literal keys
+- counter: hpack_send_lithdr_nvridx
+  doc: Number of HPACK literal headers sent with never-indexing
+- counter: hpack_send_lithdr_nvridx_v
+  doc: Number of HPACK literal headers sent with never-indexing and literal keys
+- counter: hpack_send_uncompressed
+  doc: Number of uncompressed strings sent in metadata
+- counter: hpack_send_huffman
+  doc: Number of huffman encoded strings sent in metadata
+- counter: hpack_send_binary
+  doc: Number of binary strings received in metadata
+- counter: hpack_send_binary_base64
+  doc: Number of binary strings received encoded in base64 in metadata
 # combiner locks
 - counter: combiner_locks_initiated
   doc: Number of combiner lock entries by process
@@ -217,14 +261,6 @@
 - counter: executor_push_retries
   doc: Number of times we raced and were forced to retry pushing a closure to
        the executor
-- counter: executor_threads_created
-  doc: Size of the backing thread pool for overflow gRPC Core work
-- counter: executor_threads_used
-  doc: How many executor threads actually got used
-- histogram: executor_closures_per_wakeup
-  max: 1024
-  buckets: 64
-  doc: Number of closures executed each time an executor wakes up
 # server
 - counter: server_requested_calls
   doc: How many calls were requested (not necessarily received) by the server
@@ -236,3 +272,4 @@
 - counter: server_slowpath_requests_queued
   doc: How many times was the server slow path taken (indicates too few
        outstanding requests)
+
diff --git a/src/core/lib/debug/stats_data_bq_schema.sql b/src/core/lib/debug/stats_data_bq_schema.sql
index 3a73784..5486997 100644
--- a/src/core/lib/debug/stats_data_bq_schema.sql
+++ b/src/core/lib/debug/stats_data_bq_schema.sql
@@ -53,6 +53,28 @@
 http2_initiate_write_due_to_ping_response_per_iteration:FLOAT,
 http2_initiate_write_due_to_force_rst_stream_per_iteration:FLOAT,
 http2_spurious_writes_begun_per_iteration:FLOAT,
+hpack_recv_indexed_per_iteration:FLOAT,
+hpack_recv_lithdr_incidx_per_iteration:FLOAT,
+hpack_recv_lithdr_incidx_v_per_iteration:FLOAT,
+hpack_recv_lithdr_notidx_per_iteration:FLOAT,
+hpack_recv_lithdr_notidx_v_per_iteration:FLOAT,
+hpack_recv_lithdr_nvridx_per_iteration:FLOAT,
+hpack_recv_lithdr_nvridx_v_per_iteration:FLOAT,
+hpack_recv_uncompressed_per_iteration:FLOAT,
+hpack_recv_huffman_per_iteration:FLOAT,
+hpack_recv_binary_per_iteration:FLOAT,
+hpack_recv_binary_base64_per_iteration:FLOAT,
+hpack_send_indexed_per_iteration:FLOAT,
+hpack_send_lithdr_incidx_per_iteration:FLOAT,
+hpack_send_lithdr_incidx_v_per_iteration:FLOAT,
+hpack_send_lithdr_notidx_per_iteration:FLOAT,
+hpack_send_lithdr_notidx_v_per_iteration:FLOAT,
+hpack_send_lithdr_nvridx_per_iteration:FLOAT,
+hpack_send_lithdr_nvridx_v_per_iteration:FLOAT,
+hpack_send_uncompressed_per_iteration:FLOAT,
+hpack_send_huffman_per_iteration:FLOAT,
+hpack_send_binary_per_iteration:FLOAT,
+hpack_send_binary_base64_per_iteration:FLOAT,
 combiner_locks_initiated_per_iteration:FLOAT,
 combiner_locks_scheduled_items_per_iteration:FLOAT,
 combiner_locks_scheduled_final_items_per_iteration:FLOAT,
@@ -63,7 +85,5 @@
 executor_wakeup_initiated_per_iteration:FLOAT,
 executor_queue_drained_per_iteration:FLOAT,
 executor_push_retries_per_iteration:FLOAT,
-executor_threads_created_per_iteration:FLOAT,
-executor_threads_used_per_iteration:FLOAT,
 server_requested_calls_per_iteration:FLOAT,
 server_slowpath_requests_queued_per_iteration:FLOAT
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c
index db99594..c96800b 100644
--- a/src/core/lib/http/httpcli.c
+++ b/src/core/lib/http/httpcli.c
@@ -44,7 +44,7 @@
   grpc_endpoint *ep;
   char *host;
   char *ssl_host_override;
-  gpr_timespec deadline;
+  grpc_millis deadline;
   int have_read_byte;
   const grpc_httpcli_handshaker *handshaker;
   grpc_closure *on_done;
@@ -65,7 +65,7 @@
 
 static void plaintext_handshake(grpc_exec_ctx *exec_ctx, void *arg,
                                 grpc_endpoint *endpoint, const char *host,
-                                gpr_timespec deadline,
+                                grpc_millis deadline,
                                 void (*on_done)(grpc_exec_ctx *exec_ctx,
                                                 void *arg,
                                                 grpc_endpoint *endpoint)) {
@@ -240,7 +240,7 @@
                                    grpc_polling_entity *pollent,
                                    grpc_resource_quota *resource_quota,
                                    const grpc_httpcli_request *request,
-                                   gpr_timespec deadline, grpc_closure *on_done,
+                                   grpc_millis deadline, grpc_closure *on_done,
                                    grpc_httpcli_response *response,
                                    const char *name, grpc_slice request_text) {
   internal_request *req =
@@ -278,9 +278,8 @@
 void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
                       grpc_polling_entity *pollent,
                       grpc_resource_quota *resource_quota,
-                      const grpc_httpcli_request *request,
-                      gpr_timespec deadline, grpc_closure *on_done,
-                      grpc_httpcli_response *response) {
+                      const grpc_httpcli_request *request, grpc_millis deadline,
+                      grpc_closure *on_done, grpc_httpcli_response *response) {
   char *name;
   if (g_get_override &&
       g_get_override(exec_ctx, request, deadline, on_done, response)) {
@@ -298,7 +297,7 @@
                        grpc_resource_quota *resource_quota,
                        const grpc_httpcli_request *request,
                        const char *body_bytes, size_t body_size,
-                       gpr_timespec deadline, grpc_closure *on_done,
+                       grpc_millis deadline, grpc_closure *on_done,
                        grpc_httpcli_response *response) {
   char *name;
   if (g_post_override &&
diff --git a/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h
index 8096186..a3baf51 100644
--- a/src/core/lib/http/httpcli.h
+++ b/src/core/lib/http/httpcli.h
@@ -42,7 +42,7 @@
 typedef struct {
   const char *default_port;
   void (*handshake)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint,
-                    const char *host, gpr_timespec deadline,
+                    const char *host, grpc_millis deadline,
                     void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg,
                                     grpc_endpoint *endpoint));
 } grpc_httpcli_handshaker;
@@ -83,8 +83,8 @@
 void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
                       grpc_polling_entity *pollent,
                       grpc_resource_quota *resource_quota,
-                      const grpc_httpcli_request *request,
-                      gpr_timespec deadline, grpc_closure *on_complete,
+                      const grpc_httpcli_request *request, grpc_millis deadline,
+                      grpc_closure *on_complete,
                       grpc_httpcli_response *response);
 
 /* Asynchronously perform a HTTP POST.
@@ -106,18 +106,18 @@
                        grpc_resource_quota *resource_quota,
                        const grpc_httpcli_request *request,
                        const char *body_bytes, size_t body_size,
-                       gpr_timespec deadline, grpc_closure *on_complete,
+                       grpc_millis deadline, grpc_closure *on_complete,
                        grpc_httpcli_response *response);
 
 /* override functions return 1 if they handled the request, 0 otherwise */
 typedef int (*grpc_httpcli_get_override)(grpc_exec_ctx *exec_ctx,
                                          const grpc_httpcli_request *request,
-                                         gpr_timespec deadline,
+                                         grpc_millis deadline,
                                          grpc_closure *on_complete,
                                          grpc_httpcli_response *response);
 typedef int (*grpc_httpcli_post_override)(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    const char *body_bytes, size_t body_size, gpr_timespec deadline,
+    const char *body_bytes, size_t body_size, grpc_millis deadline,
     grpc_closure *on_complete, grpc_httpcli_response *response);
 
 void grpc_httpcli_set_override(grpc_httpcli_get_override get,
diff --git a/src/core/lib/http/httpcli_security_connector.c b/src/core/lib/http/httpcli_security_connector.c
index c553fa3..355dfce 100644
--- a/src/core/lib/http/httpcli_security_connector.c
+++ b/src/core/lib/http/httpcli_security_connector.c
@@ -156,7 +156,7 @@
 
 static void ssl_handshake(grpc_exec_ctx *exec_ctx, void *arg,
                           grpc_endpoint *tcp, const char *host,
-                          gpr_timespec deadline,
+                          grpc_millis deadline,
                           void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg,
                                           grpc_endpoint *endpoint)) {
   on_done_closure *c = gpr_malloc(sizeof(*c));
diff --git a/src/core/lib/support/block_annotate.h b/src/core/lib/iomgr/block_annotate.h
similarity index 75%
rename from src/core/lib/support/block_annotate.h
rename to src/core/lib/iomgr/block_annotate.h
index 8e3ef7d..cbcb5d9 100644
--- a/src/core/lib/support/block_annotate.h
+++ b/src/core/lib/iomgr/block_annotate.h
@@ -16,8 +16,8 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SUPPORT_BLOCK_ANNOTATE_H
-#define GRPC_CORE_LIB_SUPPORT_BLOCK_ANNOTATE_H
+#ifndef GRPC_CORE_LIB_IOMGR_BLOCK_ANNOTATE_H
+#define GRPC_CORE_LIB_IOMGR_BLOCK_ANNOTATE_H
 
 #ifdef __cplusplus
 extern "C" {
@@ -47,9 +47,13 @@
 #define GRPC_SCHEDULING_START_BLOCKING_REGION \
   do {                                        \
   } while (0)
-#define GRPC_SCHEDULING_END_BLOCKING_REGION \
-  do {                                      \
+#define GRPC_SCHEDULING_END_BLOCKING_REGION_NO_EXEC_CTX \
+  do {                                                  \
+  } while (0)
+#define GRPC_SCHEDULING_END_BLOCKING_REGION_WITH_EXEC_CTX(ec) \
+  do {                                                        \
+    grpc_exec_ctx_invalidate_now((ec));                       \
   } while (0)
 #endif
 
-#endif /* GRPC_CORE_LIB_SUPPORT_BLOCK_ANNOTATE_H */
+#endif /* GRPC_CORE_LIB_IOMGR_BLOCK_ANNOTATE_H */
diff --git a/src/core/lib/iomgr/ev_epoll1_linux.c b/src/core/lib/iomgr/ev_epoll1_linux.c
index 3ac12ab..1b5abdb 100644
--- a/src/core/lib/iomgr/ev_epoll1_linux.c
+++ b/src/core/lib/iomgr/ev_epoll1_linux.c
@@ -25,6 +25,7 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <limits.h>
 #include <poll.h>
 #include <pthread.h>
 #include <string.h>
@@ -40,12 +41,12 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/lib/debug/stats.h"
+#include "src/core/lib/iomgr/block_annotate.h"
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/iomgr/lockfree_event.h"
 #include "src/core/lib/iomgr/wakeup_fd_posix.h"
 #include "src/core/lib/profiling/timers.h"
-#include "src/core/lib/support/block_annotate.h"
 #include "src/core/lib/support/string.h"
 
 static grpc_wakeup_fd global_wakeup_fd;
@@ -562,25 +563,17 @@
   GPR_TIMER_END("pollset_shutdown", 0);
 }
 
-static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
-                                           gpr_timespec now) {
-  gpr_timespec timeout;
-  if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) {
-    return -1;
-  }
-
-  if (gpr_time_cmp(deadline, now) <= 0) {
+static int poll_deadline_to_millis_timeout(grpc_exec_ctx *exec_ctx,
+                                           grpc_millis millis) {
+  if (millis == GRPC_MILLIS_INF_FUTURE) return -1;
+  grpc_millis delta = millis - grpc_exec_ctx_now(exec_ctx);
+  if (delta > INT_MAX) {
+    return INT_MAX;
+  } else if (delta < 0) {
     return 0;
+  } else {
+    return (int)delta;
   }
-
-  static const gpr_timespec round_up = {
-      0,                 /* tv_sec */
-      GPR_NS_PER_MS - 1, /* tv_nsec */
-      GPR_TIMESPAN       /* clock_type */
-  };
-  timeout = gpr_time_sub(deadline, now);
-  int millis = gpr_time_to_millis(gpr_time_add(timeout, round_up));
-  return millis >= 1 ? millis : 1;
 }
 
 /* Process the epoll events found by do_epoll_wait() function.
@@ -637,11 +630,11 @@
    (i.e the designated poller thread) will be calling this function. So there is
    no need for any synchronization when accesing fields in g_epoll_set */
 static grpc_error *do_epoll_wait(grpc_exec_ctx *exec_ctx, grpc_pollset *ps,
-                                 gpr_timespec now, gpr_timespec deadline) {
+                                 grpc_millis deadline) {
   GPR_TIMER_BEGIN("do_epoll_wait", 0);
 
   int r;
-  int timeout = poll_deadline_to_millis_timeout(deadline, now);
+  int timeout = poll_deadline_to_millis_timeout(exec_ctx, deadline);
   if (timeout != 0) {
     GRPC_SCHEDULING_START_BLOCKING_REGION;
   }
@@ -651,7 +644,7 @@
                    timeout);
   } while (r < 0 && errno == EINTR);
   if (timeout != 0) {
-    GRPC_SCHEDULING_END_BLOCKING_REGION;
+    GRPC_SCHEDULING_END_BLOCKING_REGION_WITH_EXEC_CTX(exec_ctx);
   }
 
   if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait");
@@ -669,9 +662,10 @@
   return GRPC_ERROR_NONE;
 }
 
-static bool begin_worker(grpc_pollset *pollset, grpc_pollset_worker *worker,
-                         grpc_pollset_worker **worker_hdl, gpr_timespec *now,
-                         gpr_timespec deadline) {
+static bool begin_worker(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+                         grpc_pollset_worker *worker,
+                         grpc_pollset_worker **worker_hdl,
+                         grpc_millis deadline) {
   GPR_TIMER_BEGIN("begin_worker", 0);
   if (worker_hdl != NULL) *worker_hdl = worker;
   worker->initialized_cv = false;
@@ -756,14 +750,15 @@
                 pollset->shutting_down);
       }
 
-      if (gpr_cv_wait(&worker->cv, &pollset->mu, deadline) &&
+      if (gpr_cv_wait(&worker->cv, &pollset->mu,
+                      grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME)) &&
           worker->state == UNKICKED) {
         /* If gpr_cv_wait returns true (i.e a timeout), pretend that the worker
            received a kick */
         SET_KICK_STATE(worker, KICKED);
       }
     }
-    *now = gpr_now(now->clock_type);
+    grpc_exec_ctx_invalidate_now(exec_ctx);
   }
 
   if (GRPC_TRACER_ON(grpc_polling_trace)) {
@@ -942,7 +937,7 @@
    ensure that it is held by the time the function returns */
 static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *ps,
                                 grpc_pollset_worker **worker_hdl,
-                                gpr_timespec now, gpr_timespec deadline) {
+                                grpc_millis deadline) {
   grpc_pollset_worker worker;
   grpc_error *error = GRPC_ERROR_NONE;
   static const char *err_desc = "pollset_work";
@@ -953,7 +948,7 @@
     return GRPC_ERROR_NONE;
   }
 
-  if (begin_worker(ps, &worker, worker_hdl, &now, deadline)) {
+  if (begin_worker(exec_ctx, ps, &worker, worker_hdl, deadline)) {
     gpr_tls_set(&g_current_thread_pollset, (intptr_t)ps);
     gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker);
     GPR_ASSERT(!ps->shutting_down);
@@ -976,8 +971,7 @@
        designated poller */
     if (gpr_atm_acq_load(&g_epoll_set.cursor) ==
         gpr_atm_acq_load(&g_epoll_set.num_events)) {
-      append_error(&error, do_epoll_wait(exec_ctx, ps, now, deadline),
-                   err_desc);
+      append_error(&error, do_epoll_wait(exec_ctx, ps, deadline), err_desc);
     }
     append_error(&error, process_epoll_events(exec_ctx, ps), err_desc);
 
diff --git a/src/core/lib/iomgr/ev_epollex_linux.c b/src/core/lib/iomgr/ev_epollex_linux.c
index 8eb4de4..8aaad88 100644
--- a/src/core/lib/iomgr/ev_epollex_linux.c
+++ b/src/core/lib/iomgr/ev_epollex_linux.c
@@ -25,6 +25,7 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <limits.h>
 #include <poll.h>
 #include <pthread.h>
 #include <string.h>
@@ -38,7 +39,7 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/lib/debug/stats.h"
-#include "src/core/lib/iomgr/ev_posix.h"
+#include "src/core/lib/iomgr/block_annotate.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/iomgr/is_epollexclusive_available.h"
 #include "src/core/lib/iomgr/lockfree_event.h"
@@ -46,20 +47,18 @@
 #include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/iomgr/wakeup_fd_posix.h"
 #include "src/core/lib/profiling/timers.h"
-#include "src/core/lib/support/block_annotate.h"
 #include "src/core/lib/support/spinlock.h"
 
 /*******************************************************************************
  * Polling object
  */
-
 typedef enum {
   PO_POLLING_GROUP,
   PO_POLLSET_SET,
   PO_POLLSET,
-  PO_FD, /* ordering is important: we always want to lock pollsets before fds:
-            this guarantees that using an fd as a pollable is safe */
-  PO_EMPTY_POLLABLE,
+  PO_FD,
+  /* ordering is important: we always want to lock pollsets before fds:
+            this guarantees that using an fd as a pollable is safe */ PO_EMPTY_POLLABLE,
   PO_COUNT
 } polling_obj_type;
 
@@ -690,32 +689,16 @@
   *mu = &pollset->pollable_obj.po.mu;
 }
 
-/* Convert a timespec to milliseconds:
-   - Very small or negative poll times are clamped to zero to do a non-blocking
-     poll (which becomes spin polling)
-   - Other small values are rounded up to one millisecond
-   - Longer than a millisecond polls are rounded up to the next nearest
-     millisecond to avoid spinning
-   - Infinite timeouts are converted to -1 */
-static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
-                                           gpr_timespec now) {
-  gpr_timespec timeout;
-  if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) {
-    return -1;
-  }
-
-  if (gpr_time_cmp(deadline, now) <= 0) {
+static int poll_deadline_to_millis_timeout(grpc_exec_ctx *exec_ctx,
+                                           grpc_millis millis) {
+  if (millis == GRPC_MILLIS_INF_FUTURE) return -1;
+  grpc_millis delta = millis - grpc_exec_ctx_now(exec_ctx);
+  if (delta > INT_MAX)
+    return INT_MAX;
+  else if (delta < 0)
     return 0;
-  }
-
-  static const gpr_timespec round_up = {
-      0,                 /* tv_sec */
-      GPR_NS_PER_MS - 1, /* tv_nsec */
-      GPR_TIMESPAN       /* clock_type */
-  };
-  timeout = gpr_time_sub(deadline, now);
-  int millis = gpr_time_to_millis(gpr_time_add(timeout, round_up));
-  return millis >= 1 ? millis : 1;
+  else
+    return (int)delta;
 }
 
 static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
@@ -810,9 +793,8 @@
 }
 
 static grpc_error *pollset_epoll(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                                 pollable *p, gpr_timespec now,
-                                 gpr_timespec deadline) {
-  int timeout = poll_deadline_to_millis_timeout(deadline, now);
+                                 pollable *p, grpc_millis deadline) {
+  int timeout = poll_deadline_to_millis_timeout(exec_ctx, deadline);
 
   if (GRPC_TRACER_ON(grpc_polling_trace)) {
     char *desc = pollable_desc(p);
@@ -829,7 +811,7 @@
     r = epoll_wait(p->epfd, pollset->events, MAX_EPOLL_EVENTS, timeout);
   } while (r < 0 && errno == EINTR);
   if (timeout != 0) {
-    GRPC_SCHEDULING_END_BLOCKING_REGION;
+    GRPC_SCHEDULING_END_BLOCKING_REGION_WITH_EXEC_CTX(exec_ctx);
   }
 
   if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait");
@@ -884,9 +866,10 @@
 }
 
 /* Return true if this thread should poll */
-static bool begin_worker(grpc_pollset *pollset, grpc_pollset_worker *worker,
-                         grpc_pollset_worker **worker_hdl, gpr_timespec *now,
-                         gpr_timespec deadline) {
+static bool begin_worker(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+                         grpc_pollset_worker *worker,
+                         grpc_pollset_worker **worker_hdl,
+                         grpc_millis deadline) {
   bool do_poll = true;
   if (worker_hdl != NULL) *worker_hdl = worker;
   worker->initialized_cv = false;
@@ -910,10 +893,11 @@
         worker->pollable_obj->root_worker != worker) {
       gpr_log(GPR_DEBUG, "PS:%p wait %p w=%p for %dms", pollset,
               worker->pollable_obj, worker,
-              poll_deadline_to_millis_timeout(deadline, *now));
+              poll_deadline_to_millis_timeout(exec_ctx, deadline));
     }
     while (do_poll && worker->pollable_obj->root_worker != worker) {
-      if (gpr_cv_wait(&worker->cv, &worker->pollable_obj->po.mu, deadline)) {
+      if (gpr_cv_wait(&worker->cv, &worker->pollable_obj->po.mu,
+                      grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME))) {
         if (GRPC_TRACER_ON(grpc_polling_trace)) {
           gpr_log(GPR_DEBUG, "PS:%p timeout_wait %p w=%p", pollset,
                   worker->pollable_obj, worker);
@@ -936,7 +920,7 @@
       gpr_mu_lock(&pollset->pollable_obj.po.mu);
       gpr_mu_lock(&worker->pollable_obj->po.mu);
     }
-    *now = gpr_now(now->clock_type);
+    grpc_exec_ctx_invalidate_now(exec_ctx);
   }
 
   return do_poll && pollset->shutdown_closure == NULL &&
@@ -967,14 +951,13 @@
    ensure that it is held by the time the function returns */
 static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                                 grpc_pollset_worker **worker_hdl,
-                                gpr_timespec now, gpr_timespec deadline) {
+                                grpc_millis deadline) {
   grpc_pollset_worker worker;
   if (0 && GRPC_TRACER_ON(grpc_polling_trace)) {
-    gpr_log(GPR_DEBUG, "PS:%p work hdl=%p worker=%p now=%" PRId64
-                       ".%09d deadline=%" PRId64 ".%09d kwp=%d root_worker=%p",
-            pollset, worker_hdl, &worker, now.tv_sec, now.tv_nsec,
-            deadline.tv_sec, deadline.tv_nsec, pollset->kicked_without_poller,
-            pollset->root_worker);
+    gpr_log(GPR_DEBUG, "PS:%p work hdl=%p worker=%p now=%" PRIdPTR
+                       " deadline=%" PRIdPTR " kwp=%d root_worker=%p",
+            pollset, worker_hdl, &worker, grpc_exec_ctx_now(exec_ctx), deadline,
+            pollset->kicked_without_poller, pollset->root_worker);
   }
   grpc_error *error = GRPC_ERROR_NONE;
   static const char *err_desc = "pollset_work";
@@ -985,7 +968,7 @@
   if (pollset->current_pollable_obj != &pollset->pollable_obj) {
     gpr_mu_lock(&pollset->current_pollable_obj->po.mu);
   }
-  if (begin_worker(pollset, &worker, worker_hdl, &now, deadline)) {
+  if (begin_worker(exec_ctx, pollset, &worker, worker_hdl, deadline)) {
     gpr_tls_set(&g_current_thread_pollset, (intptr_t)pollset);
     gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker);
     GPR_ASSERT(!pollset->shutdown_closure);
@@ -996,7 +979,7 @@
     gpr_mu_unlock(&pollset->pollable_obj.po.mu);
     if (pollset->event_cursor == pollset->event_count) {
       append_error(&error, pollset_epoll(exec_ctx, pollset, worker.pollable_obj,
-                                         now, deadline),
+                                         deadline),
                    err_desc);
     }
     append_error(&error, pollset_process_events(exec_ctx, pollset, false),
diff --git a/src/core/lib/iomgr/ev_epollsig_linux.c b/src/core/lib/iomgr/ev_epollsig_linux.c
index 4d8bdf1..ba2d448 100644
--- a/src/core/lib/iomgr/ev_epollsig_linux.c
+++ b/src/core/lib/iomgr/ev_epollsig_linux.c
@@ -25,6 +25,7 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <limits.h>
 #include <poll.h>
 #include <pthread.h>
 #include <signal.h>
@@ -40,13 +41,13 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/lib/debug/stats.h"
+#include "src/core/lib/iomgr/block_annotate.h"
 #include "src/core/lib/iomgr/ev_posix.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/iomgr/lockfree_event.h"
 #include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/iomgr/wakeup_fd_posix.h"
 #include "src/core/lib/profiling/timers.h"
-#include "src/core/lib/support/block_annotate.h"
 
 #define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker *)1)
 
@@ -1089,30 +1090,16 @@
   pollset->shutdown_done = NULL;
 }
 
-/* Convert a timespec to milliseconds:
-   - Very small or negative poll times are clamped to zero to do a non-blocking
-     poll (which becomes spin polling)
-   - Other small values are rounded up to one millisecond
-   - Longer than a millisecond polls are rounded up to the next nearest
-     millisecond to avoid spinning
-   - Infinite timeouts are converted to -1 */
-static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
-                                           gpr_timespec now) {
-  gpr_timespec timeout;
-  static const int64_t max_spin_polling_us = 10;
-  if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) {
-    return -1;
-  }
-
-  if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros(
-                                                   max_spin_polling_us,
-                                                   GPR_TIMESPAN))) <= 0) {
+static int poll_deadline_to_millis_timeout(grpc_exec_ctx *exec_ctx,
+                                           grpc_millis millis) {
+  if (millis == GRPC_MILLIS_INF_FUTURE) return -1;
+  grpc_millis delta = millis - grpc_exec_ctx_now(exec_ctx);
+  if (delta > INT_MAX)
+    return INT_MAX;
+  else if (delta < 0)
     return 0;
-  }
-  timeout = gpr_time_sub(deadline, now);
-  int millis = gpr_time_to_millis(gpr_time_add(
-      timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN)));
-  return millis >= 1 ? millis : 1;
+  else
+    return (int)delta;
 }
 
 static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
@@ -1243,7 +1230,7 @@
   GRPC_STATS_INC_SYSCALL_POLL(exec_ctx);
   ep_rv =
       epoll_pwait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms, sig_mask);
-  GRPC_SCHEDULING_END_BLOCKING_REGION;
+  GRPC_SCHEDULING_END_BLOCKING_REGION_WITH_EXEC_CTX(exec_ctx);
   if (ep_rv < 0) {
     if (errno != EINTR) {
       gpr_asprintf(&err_msg,
@@ -1310,10 +1297,10 @@
    ensure that it is held by the time the function returns */
 static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                                 grpc_pollset_worker **worker_hdl,
-                                gpr_timespec now, gpr_timespec deadline) {
+                                grpc_millis deadline) {
   GPR_TIMER_BEGIN("pollset_work", 0);
   grpc_error *error = GRPC_ERROR_NONE;
-  int timeout_ms = poll_deadline_to_millis_timeout(deadline, now);
+  int timeout_ms = poll_deadline_to_millis_timeout(exec_ctx, deadline);
 
   sigset_t new_mask;
 
diff --git a/src/core/lib/iomgr/ev_poll_posix.c b/src/core/lib/iomgr/ev_poll_posix.c
index e170702..036a356 100644
--- a/src/core/lib/iomgr/ev_poll_posix.c
+++ b/src/core/lib/iomgr/ev_poll_posix.c
@@ -24,6 +24,7 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <limits.h>
 #include <poll.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -37,12 +38,11 @@
 #include <grpc/support/useful.h>
 
 #include "src/core/lib/debug/stats.h"
+#include "src/core/lib/iomgr/block_annotate.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
-#include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/iomgr/wakeup_fd_cv.h"
 #include "src/core/lib/iomgr/wakeup_fd_posix.h"
 #include "src/core/lib/profiling/timers.h"
-#include "src/core/lib/support/block_annotate.h"
 #include "src/core/lib/support/murmur_hash.h"
 
 #define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker *)1)
@@ -50,7 +50,6 @@
 /*******************************************************************************
  * FD declarations
  */
-
 typedef struct grpc_fd_watcher {
   struct grpc_fd_watcher *next;
   struct grpc_fd_watcher *prev;
@@ -200,8 +199,8 @@
    - longer than a millisecond polls are rounded up to the next nearest
      millisecond to avoid spinning
    - infinite timeouts are converted to -1 */
-static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
-                                           gpr_timespec now);
+static int poll_deadline_to_millis_timeout(grpc_exec_ctx *exec_ctx,
+                                           grpc_millis deadline);
 
 /* Allow kick to wakeup the currently polling worker */
 #define GRPC_POLLSET_CAN_KICK_SELF 1
@@ -876,7 +875,7 @@
 
 static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                                 grpc_pollset_worker **worker_hdl,
-                                gpr_timespec now, gpr_timespec deadline) {
+                                grpc_millis deadline) {
   grpc_pollset_worker worker;
   if (worker_hdl) *worker_hdl = &worker;
   grpc_error *error = GRPC_ERROR_NONE;
@@ -945,7 +944,7 @@
       grpc_fd_watcher *watchers;
       struct pollfd *pfds;
 
-      timeout = poll_deadline_to_millis_timeout(deadline, now);
+      timeout = poll_deadline_to_millis_timeout(exec_ctx, deadline);
 
       if (pollset->fd_count + 2 <= inline_elements) {
         pfds = pollfd_space;
@@ -991,7 +990,7 @@
       GRPC_SCHEDULING_START_BLOCKING_REGION;
       GRPC_STATS_INC_SYSCALL_POLL(exec_ctx);
       r = grpc_poll_function(pfds, pfd_count, timeout);
-      GRPC_SCHEDULING_END_BLOCKING_REGION;
+      GRPC_SCHEDULING_END_BLOCKING_REGION_WITH_EXEC_CTX(exec_ctx);
 
       if (GRPC_TRACER_ON(grpc_polling_trace)) {
         gpr_log(GPR_DEBUG, "%p poll=%d", pollset, r);
@@ -1068,13 +1067,10 @@
       if (queued_work || worker.kicked_specifically) {
         /* If there's queued work on the list, then set the deadline to be
            immediate so we get back out of the polling loop quickly */
-        deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC);
+        deadline = 0;
       }
       keep_polling = 1;
     }
-    if (keep_polling) {
-      now = gpr_now(now.clock_type);
-    }
   }
   gpr_tls_set(&g_current_thread_poller, 0);
   if (added_worker) {
@@ -1126,21 +1122,14 @@
   }
 }
 
-static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
-                                           gpr_timespec now) {
-  gpr_timespec timeout;
-  static const int64_t max_spin_polling_us = 10;
-  if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) {
-    return -1;
-  }
-  if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros(
-                                                   max_spin_polling_us,
-                                                   GPR_TIMESPAN))) <= 0) {
-    return 0;
-  }
-  timeout = gpr_time_sub(deadline, now);
-  return gpr_time_to_millis(gpr_time_add(
-      timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN)));
+static int poll_deadline_to_millis_timeout(grpc_exec_ctx *exec_ctx,
+                                           grpc_millis deadline) {
+  if (deadline == GRPC_MILLIS_INF_FUTURE) return -1;
+  if (deadline == 0) return 0;
+  grpc_millis n = deadline - grpc_exec_ctx_now(exec_ctx);
+  if (n < 0) return 0;
+  if (n > INT_MAX) return -1;
+  return (int)n;
 }
 
 /*******************************************************************************
diff --git a/src/core/lib/iomgr/ev_posix.c b/src/core/lib/iomgr/ev_posix.c
index 4d3ae22..e4033fa 100644
--- a/src/core/lib/iomgr/ev_posix.c
+++ b/src/core/lib/iomgr/ev_posix.c
@@ -205,9 +205,9 @@
 }
 
 grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                              grpc_pollset_worker **worker, gpr_timespec now,
-                              gpr_timespec deadline) {
-  return g_event_engine->pollset_work(exec_ctx, pollset, worker, now, deadline);
+                              grpc_pollset_worker **worker,
+                              grpc_millis deadline) {
+  return g_event_engine->pollset_work(exec_ctx, pollset, worker, deadline);
 }
 
 grpc_error *grpc_pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
diff --git a/src/core/lib/iomgr/ev_posix.h b/src/core/lib/iomgr/ev_posix.h
index 1ff2ff1..df88ec4 100644
--- a/src/core/lib/iomgr/ev_posix.h
+++ b/src/core/lib/iomgr/ev_posix.h
@@ -52,8 +52,8 @@
                            grpc_closure *closure);
   void (*pollset_destroy)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset);
   grpc_error *(*pollset_work)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                              grpc_pollset_worker **worker, gpr_timespec now,
-                              gpr_timespec deadline);
+                              grpc_pollset_worker **worker,
+                              grpc_millis deadline);
   grpc_error *(*pollset_kick)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                               grpc_pollset_worker *specific_worker);
   void (*pollset_add_fd)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
diff --git a/src/core/lib/iomgr/exec_ctx.c b/src/core/lib/iomgr/exec_ctx.c
index 41c69ad..3d17afc 100644
--- a/src/core/lib/iomgr/exec_ctx.c
+++ b/src/core/lib/iomgr/exec_ctx.c
@@ -104,9 +104,69 @@
   grpc_closure_list_append(&exec_ctx->closure_list, closure, error);
 }
 
-void grpc_exec_ctx_global_init(void) {}
+static gpr_timespec
+    g_start_time[GPR_TIMESPAN + 1];  // assumes GPR_TIMESPAN is the
+                                     // last enum value in
+                                     // gpr_clock_type
+
+void grpc_exec_ctx_global_init(void) {
+  for (int i = 0; i < GPR_TIMESPAN; i++) {
+    g_start_time[i] = gpr_now((gpr_clock_type)i);
+  }
+  // allows uniform treatment in conversion functions
+  g_start_time[GPR_TIMESPAN] = gpr_time_0(GPR_TIMESPAN);
+}
+
 void grpc_exec_ctx_global_shutdown(void) {}
 
+static gpr_atm timespec_to_atm_round_down(gpr_timespec ts) {
+  ts = gpr_time_sub(ts, g_start_time[ts.clock_type]);
+  double x =
+      GPR_MS_PER_SEC * (double)ts.tv_sec + (double)ts.tv_nsec / GPR_NS_PER_MS;
+  if (x < 0) return 0;
+  if (x > GPR_ATM_MAX) return GPR_ATM_MAX;
+  return (gpr_atm)x;
+}
+
+static gpr_atm timespec_to_atm_round_up(gpr_timespec ts) {
+  ts = gpr_time_sub(ts, g_start_time[ts.clock_type]);
+  double x = GPR_MS_PER_SEC * (double)ts.tv_sec +
+             (double)ts.tv_nsec / GPR_NS_PER_MS +
+             (double)(GPR_NS_PER_SEC - 1) / (double)GPR_NS_PER_SEC;
+  if (x < 0) return 0;
+  if (x > GPR_ATM_MAX) return GPR_ATM_MAX;
+  return (gpr_atm)x;
+}
+
+grpc_millis grpc_exec_ctx_now(grpc_exec_ctx *exec_ctx) {
+  if (!exec_ctx->now_is_valid) {
+    exec_ctx->now = timespec_to_atm_round_down(gpr_now(GPR_CLOCK_MONOTONIC));
+    exec_ctx->now_is_valid = true;
+  }
+  return exec_ctx->now;
+}
+
+void grpc_exec_ctx_invalidate_now(grpc_exec_ctx *exec_ctx) {
+  exec_ctx->now_is_valid = false;
+}
+
+gpr_timespec grpc_millis_to_timespec(grpc_millis millis,
+                                     gpr_clock_type clock_type) {
+  if (clock_type == GPR_TIMESPAN) {
+    return gpr_time_from_millis(millis, GPR_TIMESPAN);
+  }
+  return gpr_time_add(g_start_time[clock_type],
+                      gpr_time_from_millis(millis, GPR_TIMESPAN));
+}
+
+grpc_millis grpc_timespec_to_millis_round_down(gpr_timespec ts) {
+  return timespec_to_atm_round_down(ts);
+}
+
+grpc_millis grpc_timespec_to_millis_round_up(gpr_timespec ts) {
+  return timespec_to_atm_round_up(ts);
+}
+
 static const grpc_closure_scheduler_vtable exec_ctx_scheduler_vtable = {
     exec_ctx_run, exec_ctx_sched, "exec_ctx"};
 static grpc_closure_scheduler exec_ctx_scheduler = {&exec_ctx_scheduler_vtable};
diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h
index c89792c..3caf775 100644
--- a/src/core/lib/iomgr/exec_ctx.h
+++ b/src/core/lib/iomgr/exec_ctx.h
@@ -19,10 +19,15 @@
 #ifndef GRPC_CORE_LIB_IOMGR_EXEC_CTX_H
 #define GRPC_CORE_LIB_IOMGR_EXEC_CTX_H
 
+#include <grpc/support/atm.h>
 #include <grpc/support/cpu.h>
+
 #include "src/core/lib/iomgr/closure.h"
 
-/* #define GRPC_EXECUTION_CONTEXT_SANITIZER 1 */
+typedef gpr_atm grpc_millis;
+
+#define GRPC_MILLIS_INF_FUTURE GPR_ATM_MAX
+#define GRPC_MILLIS_INF_PAST GPR_ATM_MIN
 
 /** A workqueue represents a list of work to be executed asynchronously.
     Forward declared here to avoid a circular dependency with workqueue.h. */
@@ -66,6 +71,9 @@
   unsigned starting_cpu;
   void *check_ready_to_finish_arg;
   bool (*check_ready_to_finish)(grpc_exec_ctx *exec_ctx, void *arg);
+
+  bool now_is_valid;
+  grpc_millis now;
 };
 
 /* initializer for grpc_exec_ctx:
@@ -73,7 +81,7 @@
 #define GRPC_EXEC_CTX_INITIALIZER(flags, finish_check, finish_check_arg) \
   {                                                                      \
     GRPC_CLOSURE_LIST_INIT, NULL, NULL, flags, gpr_cpu_current_cpu(),    \
-        finish_check_arg, finish_check                                   \
+        finish_check_arg, finish_check, false, 0                         \
   }
 
 /* initialize an execution context at the top level of an API call into grpc
@@ -106,4 +114,10 @@
 void grpc_exec_ctx_global_init(void);
 void grpc_exec_ctx_global_shutdown(void);
 
+grpc_millis grpc_exec_ctx_now(grpc_exec_ctx *exec_ctx);
+void grpc_exec_ctx_invalidate_now(grpc_exec_ctx *exec_ctx);
+gpr_timespec grpc_millis_to_timespec(grpc_millis millis, gpr_clock_type clock);
+grpc_millis grpc_timespec_to_millis_round_down(gpr_timespec timespec);
+grpc_millis grpc_timespec_to_millis_round_up(gpr_timespec timespec);
+
 #endif /* GRPC_CORE_LIB_IOMGR_EXEC_CTX_H */
diff --git a/src/core/lib/iomgr/executor.c b/src/core/lib/iomgr/executor.c
index 2439f15..58d9daf 100644
--- a/src/core/lib/iomgr/executor.c
+++ b/src/core/lib/iomgr/executor.c
@@ -32,14 +32,16 @@
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/support/spinlock.h"
 
+#define MAX_DEPTH 2
+
 typedef struct {
   gpr_mu mu;
   gpr_cv cv;
   grpc_closure_list elems;
+  size_t depth;
   bool shutdown;
   bool queued_long_job;
   gpr_thd_id id;
-  grpc_closure_list local_elems;
 } thread_state;
 
 static thread_state *g_thread_state;
@@ -54,35 +56,32 @@
 
 static void executor_thread(void *arg);
 
-static void run_closures(grpc_exec_ctx *exec_ctx, grpc_closure_list *list) {
-  int n = 0;  // number of closures executed
+static size_t run_closures(grpc_exec_ctx *exec_ctx, grpc_closure_list list) {
+  size_t n = 0;
 
-  while (!grpc_closure_list_empty(*list)) {
-    grpc_closure *c = list->head;
-    grpc_closure_list_init(list);
-    while (c != NULL) {
-      grpc_closure *next = c->next_data.next;
-      grpc_error *error = c->error_data.error;
-      if (GRPC_TRACER_ON(executor_trace)) {
+  grpc_closure *c = list.head;
+  while (c != NULL) {
+    grpc_closure *next = c->next_data.next;
+    grpc_error *error = c->error_data.error;
+    if (GRPC_TRACER_ON(executor_trace)) {
 #ifndef NDEBUG
-        gpr_log(GPR_DEBUG, "EXECUTOR: run %p [created by %s:%d]", c,
-                c->file_created, c->line_created);
+      gpr_log(GPR_DEBUG, "EXECUTOR: run %p [created by %s:%d]", c,
+              c->file_created, c->line_created);
 #else
-        gpr_log(GPR_DEBUG, "EXECUTOR: run %p", c);
+      gpr_log(GPR_DEBUG, "EXECUTOR: run %p", c);
 #endif
-      }
-#ifndef NDEBUG
-      c->scheduled = false;
-#endif
-      n++;
-      c->cb(exec_ctx, c->cb_arg, error);
-      GRPC_ERROR_UNREF(error);
-      c = next;
-      grpc_exec_ctx_flush(exec_ctx);
     }
+#ifndef NDEBUG
+    c->scheduled = false;
+#endif
+    c->cb(exec_ctx, c->cb_arg, error);
+    GRPC_ERROR_UNREF(error);
+    c = next;
+    n++;
+    grpc_exec_ctx_flush(exec_ctx);
   }
 
-  GRPC_STATS_INC_EXECUTOR_CLOSURES_PER_WAKEUP(exec_ctx, n);
+  return n;
 }
 
 bool grpc_executor_is_threaded() {
@@ -127,7 +126,7 @@
     for (size_t i = 0; i < g_max_threads; i++) {
       gpr_mu_destroy(&g_thread_state[i].mu);
       gpr_cv_destroy(&g_thread_state[i].cv);
-      run_closures(exec_ctx, &g_thread_state[i].elems);
+      run_closures(exec_ctx, g_thread_state[i].elems);
     }
     gpr_free(g_thread_state);
     gpr_tls_destroy(&g_this_thread_state);
@@ -151,14 +150,14 @@
   grpc_exec_ctx exec_ctx =
       GRPC_EXEC_CTX_INITIALIZER(0, grpc_never_ready_to_finish, NULL);
 
-  GRPC_STATS_INC_EXECUTOR_THREADS_CREATED(&exec_ctx);
-
-  bool used = false;
+  size_t subtract_depth = 0;
   for (;;) {
     if (GRPC_TRACER_ON(executor_trace)) {
-      gpr_log(GPR_DEBUG, "EXECUTOR[%d]: step", (int)(ts - g_thread_state));
+      gpr_log(GPR_DEBUG, "EXECUTOR[%d]: step (sub_depth=%" PRIdPTR ")",
+              (int)(ts - g_thread_state), subtract_depth);
     }
     gpr_mu_lock(&ts->mu);
+    ts->depth -= subtract_depth;
     while (grpc_closure_list_empty(ts->elems) && !ts->shutdown) {
       ts->queued_long_job = false;
       gpr_cv_wait(&ts->cv, &ts->mu, gpr_inf_future(GPR_CLOCK_REALTIME));
@@ -171,20 +170,16 @@
       gpr_mu_unlock(&ts->mu);
       break;
     }
-    if (!used) {
-      GRPC_STATS_INC_EXECUTOR_THREADS_USED(&exec_ctx);
-      used = true;
-    }
     GRPC_STATS_INC_EXECUTOR_QUEUE_DRAINED(&exec_ctx);
-    GPR_ASSERT(grpc_closure_list_empty(ts->local_elems));
-    ts->local_elems = ts->elems;
+    grpc_closure_list exec = ts->elems;
     ts->elems = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT;
     gpr_mu_unlock(&ts->mu);
     if (GRPC_TRACER_ON(executor_trace)) {
       gpr_log(GPR_DEBUG, "EXECUTOR[%d]: execute", (int)(ts - g_thread_state));
     }
 
-    run_closures(&exec_ctx, &ts->local_elems);
+    grpc_exec_ctx_invalidate_now(&exec_ctx);
+    subtract_depth = run_closures(&exec_ctx, exec);
   }
   grpc_exec_ctx_finish(&exec_ctx);
 }
@@ -217,10 +212,6 @@
       ts = &g_thread_state[GPR_HASH_POINTER(exec_ctx, cur_thread_count)];
     } else {
       GRPC_STATS_INC_EXECUTOR_SCHEDULED_TO_SELF(exec_ctx);
-      if (is_short) {
-        grpc_closure_list_append(&ts->local_elems, closure, error);
-        return;
-      }
     }
     thread_state *orig_ts = ts;
 
@@ -260,7 +251,8 @@
         gpr_cv_signal(&ts->cv);
       }
       grpc_closure_list_append(&ts->elems, closure, error);
-      try_new_thread = ts->elems.head != closure &&
+      ts->depth++;
+      try_new_thread = ts->depth > MAX_DEPTH &&
                        cur_thread_count < g_max_threads && !ts->shutdown;
       if (!is_short) ts->queued_long_job = true;
       gpr_mu_unlock(&ts->mu);
diff --git a/src/core/lib/iomgr/iocp_windows.c b/src/core/lib/iomgr/iocp_windows.c
index c082179..3c38105 100644
--- a/src/core/lib/iomgr/iocp_windows.c
+++ b/src/core/lib/iomgr/iocp_windows.c
@@ -40,25 +40,17 @@
 
 static HANDLE g_iocp;
 
-static DWORD deadline_to_millis_timeout(gpr_timespec deadline,
-                                        gpr_timespec now) {
+static DWORD deadline_to_millis_timeout(grpc_exec_ctx *exec_ctx,
+                                        grpc_millis deadline) {
   gpr_timespec timeout;
-  static const int64_t max_spin_polling_us = 10;
-  if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) {
+  if (deadline == GRPC_MILLIS_INF_FUTURE) == 0) {
     return INFINITE;
   }
-  if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros(
-                                                   max_spin_polling_us,
-                                                   GPR_TIMESPAN))) <= 0) {
-    return 0;
-  }
-  timeout = gpr_time_sub(deadline, now);
-  return (DWORD)gpr_time_to_millis(gpr_time_add(
-      timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN)));
+  return (DWORD)GPR_MAX(0, deadline - grpc_exec_ctx_now(exec_ctx));
 }
 
 grpc_iocp_work_status grpc_iocp_work(grpc_exec_ctx *exec_ctx,
-                                     gpr_timespec deadline) {
+                                     grpc_millis deadline) {
   BOOL success;
   DWORD bytes = 0;
   DWORD flags = 0;
@@ -67,9 +59,9 @@
   grpc_winsocket *socket;
   grpc_winsocket_callback_info *info;
   GRPC_STATS_INC_SYSCALL_POLL(exec_ctx);
-  success = GetQueuedCompletionStatus(
-      g_iocp, &bytes, &completion_key, &overlapped,
-      deadline_to_millis_timeout(deadline, gpr_now(deadline.clock_type)));
+  success =
+      GetQueuedCompletionStatus(g_iocp, &bytes, &completion_key, &overlapped,
+                                deadline_to_millis_timeout(exec_ctx, deadline));
   if (success == 0 && overlapped == NULL) {
     return GRPC_IOCP_WORK_TIMEOUT;
   }
diff --git a/src/core/lib/iomgr/iocp_windows.h b/src/core/lib/iomgr/iocp_windows.h
index 9c89e86..4314ff2 100644
--- a/src/core/lib/iomgr/iocp_windows.h
+++ b/src/core/lib/iomgr/iocp_windows.h
@@ -30,7 +30,7 @@
 } grpc_iocp_work_status;
 
 grpc_iocp_work_status grpc_iocp_work(grpc_exec_ctx *exec_ctx,
-                                     gpr_timespec deadline);
+                                     grpc_millis deadline);
 void grpc_iocp_init(void);
 void grpc_iocp_kick(void);
 void grpc_iocp_flush(void);
diff --git a/src/core/lib/iomgr/iomgr.c b/src/core/lib/iomgr/iomgr.c
index f63f190..c74e4d6 100644
--- a/src/core/lib/iomgr/iomgr.c
+++ b/src/core/lib/iomgr/iomgr.c
@@ -48,7 +48,7 @@
   gpr_cv_init(&g_rcv);
   grpc_exec_ctx_global_init();
   grpc_executor_init(exec_ctx);
-  grpc_timer_list_init(gpr_now(GPR_CLOCK_MONOTONIC));
+  grpc_timer_list_init(exec_ctx);
   g_root_object.next = g_root_object.prev = &g_root_object;
   g_root_object.name = (char *)"root";
   grpc_network_status_init();
@@ -95,8 +95,9 @@
       }
       last_warning_time = gpr_now(GPR_CLOCK_REALTIME);
     }
-    if (grpc_timer_check(exec_ctx, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL) ==
-        GRPC_TIMERS_FIRED) {
+    exec_ctx->now_is_valid = true;
+    exec_ctx->now = GRPC_MILLIS_INF_FUTURE;
+    if (grpc_timer_check(exec_ctx, NULL) == GRPC_TIMERS_FIRED) {
       gpr_mu_unlock(&g_mu);
       grpc_exec_ctx_flush(exec_ctx);
       grpc_iomgr_platform_flush();
diff --git a/src/core/lib/iomgr/load_file.c b/src/core/lib/iomgr/load_file.c
index 0b4d41e..5cb4099 100644
--- a/src/core/lib/iomgr/load_file.c
+++ b/src/core/lib/iomgr/load_file.c
@@ -25,7 +25,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#include "src/core/lib/support/block_annotate.h"
+#include "src/core/lib/iomgr/block_annotate.h"
 #include "src/core/lib/support/string.h"
 
 grpc_error *grpc_load_file(const char *filename, int add_null_terminator,
@@ -73,6 +73,6 @@
     GRPC_ERROR_UNREF(error);
     error = error_out;
   }
-  GRPC_SCHEDULING_END_BLOCKING_REGION;
+  GRPC_SCHEDULING_END_BLOCKING_REGION_NO_EXEC_CTX;
   return error;
 }
diff --git a/src/core/lib/iomgr/pollset.h b/src/core/lib/iomgr/pollset.h
index a0f6b3a..8f8e37a 100644
--- a/src/core/lib/iomgr/pollset.h
+++ b/src/core/lib/iomgr/pollset.h
@@ -71,8 +71,8 @@
    pollset
    lock */
 grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                              grpc_pollset_worker **worker, gpr_timespec now,
-                              gpr_timespec deadline) GRPC_MUST_USE_RESULT;
+                              grpc_pollset_worker **worker,
+                              grpc_millis deadline) GRPC_MUST_USE_RESULT;
 
 /* Break one polling thread out of polling work for this pollset.
    If specific_worker is non-NULL, then kick that worker. */
diff --git a/src/core/lib/iomgr/pollset_uv.c b/src/core/lib/iomgr/pollset_uv.c
index 2651325..0127770 100644
--- a/src/core/lib/iomgr/pollset_uv.c
+++ b/src/core/lib/iomgr/pollset_uv.c
@@ -116,13 +116,14 @@
 
 grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                               grpc_pollset_worker **worker_hdl,
-                              gpr_timespec now, gpr_timespec deadline) {
+                              grpc_millis deadline) {
   uint64_t timeout;
   GRPC_UV_ASSERT_SAME_THREAD();
   gpr_mu_unlock(&grpc_polling_mu);
   if (grpc_pollset_work_run_loop) {
-    if (gpr_time_cmp(deadline, now) >= 0) {
-      timeout = (uint64_t)gpr_time_to_millis(gpr_time_sub(deadline, now));
+    grpc_millis now = grpc_exec_ctx_now(exec_ctx);
+    if (deadline >= now) {
+      timeout = deadline - now;
     } else {
       timeout = 0;
     }
diff --git a/src/core/lib/iomgr/pollset_windows.c b/src/core/lib/iomgr/pollset_windows.c
index eb295d3..bb4df83 100644
--- a/src/core/lib/iomgr/pollset_windows.c
+++ b/src/core/lib/iomgr/pollset_windows.c
@@ -110,7 +110,7 @@
 
 grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                               grpc_pollset_worker **worker_hdl,
-                              gpr_timespec now, gpr_timespec deadline) {
+                              grpc_millis deadline) {
   grpc_pollset_worker worker;
   if (worker_hdl) *worker_hdl = &worker;
 
@@ -159,7 +159,8 @@
                       &worker);
     added_worker = 1;
     while (!worker.kicked) {
-      if (gpr_cv_wait(&worker.cv, &grpc_polling_mu, deadline)) {
+      if (gpr_cv_wait(&worker.cv, &grpc_polling_mu,
+                      grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME))) {
         break;
       }
     }
diff --git a/src/core/lib/iomgr/resolve_address_posix.c b/src/core/lib/iomgr/resolve_address_posix.c
index 60cfeeb..1b78349 100644
--- a/src/core/lib/iomgr/resolve_address_posix.c
+++ b/src/core/lib/iomgr/resolve_address_posix.c
@@ -33,10 +33,10 @@
 #include <grpc/support/thd.h>
 #include <grpc/support/time.h>
 #include <grpc/support/useful.h>
+#include "src/core/lib/iomgr/block_annotate.h"
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/iomgr/unix_sockets_posix.h"
-#include "src/core/lib/support/block_annotate.h"
 #include "src/core/lib/support/string.h"
 
 static grpc_error *blocking_resolve_address_impl(
@@ -81,7 +81,7 @@
 
   GRPC_SCHEDULING_START_BLOCKING_REGION;
   s = getaddrinfo(host, port, &hints, &result);
-  GRPC_SCHEDULING_END_BLOCKING_REGION;
+  GRPC_SCHEDULING_END_BLOCKING_REGION_NO_EXEC_CTX;
 
   if (s != 0) {
     /* Retry if well-known service name is recognized */
@@ -90,7 +90,7 @@
       if (strcmp(port, svc[i][0]) == 0) {
         GRPC_SCHEDULING_START_BLOCKING_REGION;
         s = getaddrinfo(host, svc[i][1], &hints, &result);
-        GRPC_SCHEDULING_END_BLOCKING_REGION;
+        GRPC_SCHEDULING_END_BLOCKING_REGION_NO_EXEC_CTX;
         break;
       }
     }
diff --git a/src/core/lib/iomgr/resolve_address_windows.c b/src/core/lib/iomgr/resolve_address_windows.c
index 0cb0029..305930d 100644
--- a/src/core/lib/iomgr/resolve_address_windows.c
+++ b/src/core/lib/iomgr/resolve_address_windows.c
@@ -33,10 +33,10 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/thd.h>
 #include <grpc/support/time.h>
+#include "src/core/lib/iomgr/block_annotate.h"
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
-#include "src/core/lib/support/block_annotate.h"
 #include "src/core/lib/support/string.h"
 
 typedef struct {
@@ -86,7 +86,7 @@
 
   GRPC_SCHEDULING_START_BLOCKING_REGION;
   s = getaddrinfo(host, port, &hints, &result);
-  GRPC_SCHEDULING_END_BLOCKING_REGION;
+  GRPC_SCHEDULING_END_BLOCKING_REGION_NO_EXEC_CTX;
   if (s != 0) {
     error = GRPC_WSA_ERROR(WSAGetLastError(), "getaddrinfo");
     goto done;
diff --git a/src/core/lib/iomgr/resource_quota.c b/src/core/lib/iomgr/resource_quota.c
index 8581708..bccda2e 100644
--- a/src/core/lib/iomgr/resource_quota.c
+++ b/src/core/lib/iomgr/resource_quota.c
@@ -22,6 +22,7 @@
 #include <stdint.h>
 #include <string.h>
 
+#include <grpc/slice_buffer.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
diff --git a/src/core/lib/iomgr/socket_utils_windows.c b/src/core/lib/iomgr/socket_utils_windows.c
index 2732c15..6e85e4b 100644
--- a/src/core/lib/iomgr/socket_utils_windows.c
+++ b/src/core/lib/iomgr/socket_utils_windows.c
@@ -26,12 +26,8 @@
 #include <grpc/support/log.h>
 
 const char *grpc_inet_ntop(int af, const void *src, char *dst, size_t size) {
-#ifdef GPR_WIN_INET_NTOP
-  return inet_ntop(af, src, dst, size);
-#else
   /* Windows InetNtopA wants a mutable ip pointer */
   return InetNtopA(af, (void *)src, dst, size);
-#endif /* GPR_WIN_INET_NTOP */
 }
 
 #endif /* GRPC_WINDOWS_SOCKETUTILS */
diff --git a/src/core/lib/iomgr/tcp_client.h b/src/core/lib/iomgr/tcp_client.h
index 6c9e51a..c04fb8b 100644
--- a/src/core/lib/iomgr/tcp_client.h
+++ b/src/core/lib/iomgr/tcp_client.h
@@ -35,6 +35,6 @@
                              grpc_pollset_set *interested_parties,
                              const grpc_channel_args *channel_args,
                              const grpc_resolved_address *addr,
-                             gpr_timespec deadline);
+                             grpc_millis deadline);
 
 #endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H */
diff --git a/src/core/lib/iomgr/tcp_client_posix.c b/src/core/lib/iomgr/tcp_client_posix.c
index 39dbb50..64834d8 100644
--- a/src/core/lib/iomgr/tcp_client_posix.c
+++ b/src/core/lib/iomgr/tcp_client_posix.c
@@ -48,7 +48,6 @@
 typedef struct {
   gpr_mu mu;
   grpc_fd *fd;
-  gpr_timespec deadline;
   grpc_timer alarm;
   grpc_closure on_alarm;
   int refs;
@@ -244,7 +243,7 @@
                                     grpc_pollset_set *interested_parties,
                                     const grpc_channel_args *channel_args,
                                     const grpc_resolved_address *addr,
-                                    gpr_timespec deadline) {
+                                    grpc_millis deadline) {
   int fd;
   grpc_dualstack_mode dsmode;
   int err;
@@ -325,9 +324,7 @@
 
   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),
-                  &ac->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC));
+  grpc_timer_init(exec_ctx, &ac->alarm, deadline, &ac->on_alarm);
   grpc_fd_notify_on_write(exec_ctx, ac->fd, &ac->write_closure);
   gpr_mu_unlock(&ac->mu);
 
@@ -341,14 +338,14 @@
     grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep,
     grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args,
     const grpc_resolved_address *addr,
-    gpr_timespec deadline) = tcp_client_connect_impl;
+    grpc_millis deadline) = tcp_client_connect_impl;
 
 void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
                              grpc_endpoint **ep,
                              grpc_pollset_set *interested_parties,
                              const grpc_channel_args *channel_args,
                              const grpc_resolved_address *addr,
-                             gpr_timespec deadline) {
+                             grpc_millis deadline) {
   grpc_tcp_client_connect_impl(exec_ctx, closure, ep, interested_parties,
                                channel_args, addr, deadline);
 }
diff --git a/src/core/lib/iomgr/tcp_client_windows.c b/src/core/lib/iomgr/tcp_client_windows.c
index fc62105..bf80165 100644
--- a/src/core/lib/iomgr/tcp_client_windows.c
+++ b/src/core/lib/iomgr/tcp_client_windows.c
@@ -41,7 +41,6 @@
   grpc_closure *on_done;
   gpr_mu mu;
   grpc_winsocket *socket;
-  gpr_timespec deadline;
   grpc_timer alarm;
   grpc_closure on_alarm;
   char *addr_name;
@@ -124,7 +123,7 @@
 static void tcp_client_connect_impl(
     grpc_exec_ctx *exec_ctx, grpc_closure *on_done, grpc_endpoint **endpoint,
     grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args,
-    const grpc_resolved_address *addr, gpr_timespec deadline) {
+    const grpc_resolved_address *addr, grpc_millis deadline) {
   SOCKET sock = INVALID_SOCKET;
   BOOL success;
   int status;
@@ -204,8 +203,7 @@
   GRPC_CLOSURE_INIT(&ac->on_connect, on_connect, ac, grpc_schedule_on_exec_ctx);
 
   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_timer_init(exec_ctx, &ac->alarm, deadline, &ac->on_alarm, );
   grpc_socket_notify_on_write(exec_ctx, socket, &ac->on_connect);
   return;
 
@@ -230,14 +228,14 @@
     grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep,
     grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args,
     const grpc_resolved_address *addr,
-    gpr_timespec deadline) = tcp_client_connect_impl;
+    grpc_millis deadline) = tcp_client_connect_impl;
 
 void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
                              grpc_endpoint **ep,
                              grpc_pollset_set *interested_parties,
                              const grpc_channel_args *channel_args,
                              const grpc_resolved_address *addr,
-                             gpr_timespec deadline) {
+                             grpc_millis deadline) {
   grpc_tcp_client_connect_impl(exec_ctx, closure, ep, interested_parties,
                                channel_args, addr, deadline);
 }
diff --git a/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c
index 7e27129..7fcaef7 100644
--- a/src/core/lib/iomgr/tcp_posix.c
+++ b/src/core/lib/iomgr/tcp_posix.c
@@ -135,13 +135,11 @@
     gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p run", p);
   }
   gpr_mu_lock(p->pollset_mu);
-  gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
-  gpr_timespec deadline =
-      gpr_time_add(now, gpr_time_from_seconds(10, GPR_TIMESPAN));
+  grpc_millis deadline = grpc_exec_ctx_now(exec_ctx) + 13 * GPR_MS_PER_SEC;
   GRPC_STATS_INC_TCP_BACKUP_POLLER_POLLS(exec_ctx);
-  GRPC_LOG_IF_ERROR("backup_poller:pollset_work",
-                    grpc_pollset_work(exec_ctx, BACKUP_POLLER_POLLSET(p), NULL,
-                                      now, deadline));
+  GRPC_LOG_IF_ERROR(
+      "backup_poller:pollset_work",
+      grpc_pollset_work(exec_ctx, BACKUP_POLLER_POLLSET(p), NULL, deadline));
   gpr_mu_unlock(p->pollset_mu);
   /* last "uncovered" notification is the ref that keeps us polling, if we get
    * there try a cas to release it */
diff --git a/src/core/lib/iomgr/timer.h b/src/core/lib/iomgr/timer.h
index ac392f8..e51cb44 100644
--- a/src/core/lib/iomgr/timer.h
+++ b/src/core/lib/iomgr/timer.h
@@ -41,8 +41,7 @@
    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_closure *closure,
-                     gpr_timespec now);
+                     grpc_millis deadline, grpc_closure *closure);
 
 /* Initialize *timer without setting it. This can later be passed through
    the regular init or cancel */
@@ -92,8 +91,8 @@
    with high probability at least one thread in the system will see an update
    at any time slice. */
 grpc_timer_check_result grpc_timer_check(grpc_exec_ctx *exec_ctx,
-                                         gpr_timespec now, gpr_timespec *next);
-void grpc_timer_list_init(gpr_timespec now);
+                                         grpc_millis *next);
+void grpc_timer_list_init(grpc_exec_ctx *exec_ctx);
 void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx);
 
 /* Consume a kick issued by grpc_kick_poller */
diff --git a/src/core/lib/iomgr/timer_generic.c b/src/core/lib/iomgr/timer_generic.c
index e9a7236..14330bd 100644
--- a/src/core/lib/iomgr/timer_generic.c
+++ b/src/core/lib/iomgr/timer_generic.c
@@ -79,6 +79,125 @@
  * Access to this is protected by g_shared_mutables.mu */
 static timer_shard *g_shard_queue[NUM_SHARDS];
 
+#ifndef NDEBUG
+
+/* == Hash table for duplicate timer detection == */
+
+#define NUM_HASH_BUCKETS 1009 /* Prime number close to 1000 */
+
+static gpr_mu g_hash_mu[NUM_HASH_BUCKETS]; /* One mutex per bucket */
+static grpc_timer *g_timer_ht[NUM_HASH_BUCKETS] = {NULL};
+
+static void init_timer_ht() {
+  for (int i = 0; i < NUM_HASH_BUCKETS; i++) {
+    gpr_mu_init(&g_hash_mu[i]);
+  }
+}
+
+static bool is_in_ht(grpc_timer *t) {
+  size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS);
+
+  gpr_mu_lock(&g_hash_mu[i]);
+  grpc_timer *p = g_timer_ht[i];
+  while (p != NULL && p != t) {
+    p = p->hash_table_next;
+  }
+  gpr_mu_unlock(&g_hash_mu[i]);
+
+  return (p == t);
+}
+
+static void add_to_ht(grpc_timer *t) {
+  GPR_ASSERT(!t->hash_table_next);
+  size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS);
+
+  gpr_mu_lock(&g_hash_mu[i]);
+  grpc_timer *p = g_timer_ht[i];
+  while (p != NULL && p != t) {
+    p = p->hash_table_next;
+  }
+
+  if (p == t) {
+    grpc_closure *c = t->closure;
+    gpr_log(GPR_ERROR,
+            "** Duplicate timer (%p) being added. Closure: (%p), created at: "
+            "(%s:%d), scheduled at: (%s:%d) **",
+            t, c, c->file_created, c->line_created, c->file_initiated,
+            c->line_initiated);
+    abort();
+  }
+
+  /* Timer not present in the bucket. Insert at head of the list */
+  t->hash_table_next = g_timer_ht[i];
+  g_timer_ht[i] = t;
+  gpr_mu_unlock(&g_hash_mu[i]);
+}
+
+static void remove_from_ht(grpc_timer *t) {
+  size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS);
+  bool removed = false;
+
+  gpr_mu_lock(&g_hash_mu[i]);
+  if (g_timer_ht[i] == t) {
+    g_timer_ht[i] = g_timer_ht[i]->hash_table_next;
+    removed = true;
+  } else if (g_timer_ht[i] != NULL) {
+    grpc_timer *p = g_timer_ht[i];
+    while (p->hash_table_next != NULL && p->hash_table_next != t) {
+      p = p->hash_table_next;
+    }
+
+    if (p->hash_table_next == t) {
+      p->hash_table_next = t->hash_table_next;
+      removed = true;
+    }
+  }
+  gpr_mu_unlock(&g_hash_mu[i]);
+
+  if (!removed) {
+    grpc_closure *c = t->closure;
+    gpr_log(GPR_ERROR,
+            "** Removing timer (%p) that is not added to hash table. Closure "
+            "(%p), created at: (%s:%d), scheduled at: (%s:%d) **",
+            t, c, c->file_created, c->line_created, c->file_initiated,
+            c->line_initiated);
+    abort();
+  }
+
+  t->hash_table_next = NULL;
+}
+
+/* If a timer is added to a timer shard (either heap or a list), it cannot
+ * be pending. A timer is added to hash table only-if it is added to the
+ * timer shard.
+ * Therefore, if timer->pending is false, it cannot be in hash table */
+static void validate_non_pending_timer(grpc_timer *t) {
+  if (!t->pending && is_in_ht(t)) {
+    grpc_closure *c = t->closure;
+    gpr_log(GPR_ERROR,
+            "** gpr_timer_cancel() called on a non-pending timer (%p) which "
+            "is in the hash table. Closure: (%p), created at: (%s:%d), "
+            "scheduled at: (%s:%d) **",
+            t, c, c->file_created, c->line_created, c->file_initiated,
+            c->line_initiated);
+    abort();
+  }
+}
+
+#define INIT_TIMER_HASH_TABLE() init_timer_ht()
+#define ADD_TO_HASH_TABLE(t) add_to_ht((t))
+#define REMOVE_FROM_HASH_TABLE(t) remove_from_ht((t))
+#define VALIDATE_NON_PENDING_TIMER(t) validate_non_pending_timer((t))
+
+#else
+
+#define INIT_TIMER_HASH_TABLE()
+#define ADD_TO_HASH_TABLE(t)
+#define REMOVE_FROM_HASH_TABLE(t)
+#define VALIDATE_NON_PENDING_TIMER(t)
+
+#endif
+
 /* Thread local variable that stores the deadline of the next timer the thread
  * has last-seen. This is an optimization to prevent the thread from checking
  * shared_mutables.min_timer (which requires acquiring shared_mutables.mu lock,
@@ -97,9 +216,6 @@
 
 static struct shared_mutables g_shared_mutables;
 
-static gpr_clock_type g_clock_type;
-static gpr_timespec g_start_time;
-
 static gpr_atm saturating_add(gpr_atm a, gpr_atm b) {
   if (a > GPR_ATM_MAX - b) {
     return GPR_ATM_MAX;
@@ -112,52 +228,19 @@
                                                        gpr_atm *next,
                                                        grpc_error *error);
 
-static gpr_timespec dbl_to_ts(double d) {
-  gpr_timespec ts;
-  ts.tv_sec = (int64_t)d;
-  ts.tv_nsec = (int32_t)(1e9 * (d - (double)ts.tv_sec));
-  ts.clock_type = GPR_TIMESPAN;
-  return ts;
-}
-
-static gpr_atm timespec_to_atm_round_up(gpr_timespec ts) {
-  ts = gpr_time_sub(ts, g_start_time);
-  double x = GPR_MS_PER_SEC * (double)ts.tv_sec +
-             (double)ts.tv_nsec / GPR_NS_PER_MS +
-             (double)(GPR_NS_PER_SEC - 1) / (double)GPR_NS_PER_SEC;
-  if (x < 0) return 0;
-  if (x > GPR_ATM_MAX) return GPR_ATM_MAX;
-  return (gpr_atm)x;
-}
-
-static gpr_atm timespec_to_atm_round_down(gpr_timespec ts) {
-  ts = gpr_time_sub(ts, g_start_time);
-  double x =
-      GPR_MS_PER_SEC * (double)ts.tv_sec + (double)ts.tv_nsec / GPR_NS_PER_MS;
-  if (x < 0) return 0;
-  if (x > GPR_ATM_MAX) return GPR_ATM_MAX;
-  return (gpr_atm)x;
-}
-
-static gpr_timespec atm_to_timespec(gpr_atm x) {
-  return gpr_time_add(g_start_time, dbl_to_ts((double)x / 1000.0));
-}
-
 static gpr_atm compute_min_deadline(timer_shard *shard) {
   return grpc_timer_heap_is_empty(&shard->heap)
              ? saturating_add(shard->queue_deadline_cap, 1)
              : grpc_timer_heap_top(&shard->heap)->deadline;
 }
 
-void grpc_timer_list_init(gpr_timespec now) {
+void grpc_timer_list_init(grpc_exec_ctx *exec_ctx) {
   uint32_t i;
 
   g_shared_mutables.initialized = true;
   g_shared_mutables.checker_mu = GPR_SPINLOCK_INITIALIZER;
   gpr_mu_init(&g_shared_mutables.mu);
-  g_clock_type = now.clock_type;
-  g_start_time = now;
-  g_shared_mutables.min_timer = timespec_to_atm_round_down(now);
+  g_shared_mutables.min_timer = grpc_exec_ctx_now(exec_ctx);
   gpr_tls_init(&g_last_seen_min_timer);
   gpr_tls_set(&g_last_seen_min_timer, 0);
   grpc_register_tracer(&grpc_timer_trace);
@@ -175,6 +258,8 @@
     shard->min_deadline = compute_min_deadline(shard);
     g_shard_queue[i] = shard;
   }
+
+  INIT_TIMER_HASH_TABLE();
 }
 
 void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx) {
@@ -192,10 +277,6 @@
   g_shared_mutables.initialized = false;
 }
 
-static double ts_to_dbl(gpr_timespec ts) {
-  return (double)ts.tv_sec + 1e-9 * ts.tv_nsec;
-}
-
 /* returns true if the first element in the list */
 static void list_join(grpc_timer *head, grpc_timer *timer) {
   timer->next = head;
@@ -236,20 +317,20 @@
 void grpc_timer_init_unset(grpc_timer *timer) { timer->pending = false; }
 
 void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
-                     gpr_timespec deadline, grpc_closure *closure,
-                     gpr_timespec now) {
+                     grpc_millis deadline, grpc_closure *closure) {
   int is_first_timer = 0;
   timer_shard *shard = &g_shards[GPR_HASH_POINTER(timer, NUM_SHARDS)];
-  GPR_ASSERT(deadline.clock_type == g_clock_type);
-  GPR_ASSERT(now.clock_type == g_clock_type);
   timer->closure = closure;
-  gpr_atm deadline_atm = timer->deadline = timespec_to_atm_round_up(deadline);
+  timer->deadline = deadline;
+
+#ifndef NDEBUG
+  timer->hash_table_next = NULL;
+#endif
 
   if (GRPC_TRACER_ON(grpc_timer_trace)) {
-    gpr_log(GPR_DEBUG, "TIMER %p: SET %" PRId64 ".%09d [%" PRIdPTR
-                       "] now %" PRId64 ".%09d [%" PRIdPTR "] call %p[%p]",
-            timer, deadline.tv_sec, deadline.tv_nsec, deadline_atm, now.tv_sec,
-            now.tv_nsec, timespec_to_atm_round_down(now), closure, closure->cb);
+    gpr_log(GPR_DEBUG,
+            "TIMER %p: SET %" PRIdPTR " now %" PRIdPTR " call %p[%p]", timer,
+            deadline, grpc_exec_ctx_now(exec_ctx), closure, closure->cb);
   }
 
   if (!g_shared_mutables.initialized) {
@@ -262,7 +343,8 @@
 
   gpr_mu_lock(&shard->mu);
   timer->pending = true;
-  if (gpr_time_cmp(deadline, now) <= 0) {
+  grpc_millis now = grpc_exec_ctx_now(exec_ctx);
+  if (deadline <= now) {
     timer->pending = false;
     GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_NONE);
     gpr_mu_unlock(&shard->mu);
@@ -271,8 +353,11 @@
   }
 
   grpc_time_averaged_stats_add_sample(&shard->stats,
-                                      ts_to_dbl(gpr_time_sub(deadline, now)));
-  if (deadline_atm < shard->queue_deadline_cap) {
+                                      (double)(deadline - now) / 1000.0);
+
+  ADD_TO_HASH_TABLE(timer);
+
+  if (deadline < shard->queue_deadline_cap) {
     is_first_timer = grpc_timer_heap_add(&shard->heap, timer);
   } else {
     timer->heap_index = INVALID_HEAP_INDEX;
@@ -303,12 +388,12 @@
       gpr_log(GPR_DEBUG, "  .. old shard min_deadline=%" PRIdPTR,
               shard->min_deadline);
     }
-    if (deadline_atm < shard->min_deadline) {
+    if (deadline < shard->min_deadline) {
       gpr_atm old_min_deadline = g_shard_queue[0]->min_deadline;
-      shard->min_deadline = deadline_atm;
+      shard->min_deadline = deadline;
       note_deadline_change(shard);
-      if (shard->shard_queue_index == 0 && deadline_atm < old_min_deadline) {
-        gpr_atm_no_barrier_store(&g_shared_mutables.min_timer, deadline_atm);
+      if (shard->shard_queue_index == 0 && deadline < old_min_deadline) {
+        gpr_atm_no_barrier_store(&g_shared_mutables.min_timer, deadline);
         grpc_kick_poller();
       }
     }
@@ -333,7 +418,10 @@
     gpr_log(GPR_DEBUG, "TIMER %p: CANCEL pending=%s", timer,
             timer->pending ? "true" : "false");
   }
+
   if (timer->pending) {
+    REMOVE_FROM_HASH_TABLE(timer);
+
     GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_CANCELLED);
     timer->pending = false;
     if (timer->heap_index == INVALID_HEAP_INDEX) {
@@ -341,6 +429,8 @@
     } else {
       grpc_timer_heap_remove(&shard->heap, timer);
     }
+  } else {
+    VALIDATE_NON_PENDING_TIMER(timer);
   }
   gpr_mu_unlock(&shard->mu);
 }
@@ -407,8 +497,9 @@
     }
     if (timer->deadline > now) return NULL;
     if (GRPC_TRACER_ON(grpc_timer_trace)) {
-      gpr_log(GPR_DEBUG, "TIMER %p: FIRE %" PRIdPTR "ms late", timer,
-              now - timer->deadline);
+      gpr_log(GPR_DEBUG, "TIMER %p: FIRE %" PRIdPTR "ms late via %s scheduler",
+              timer, now - timer->deadline,
+              timer->closure->scheduler->vtable->name);
     }
     timer->pending = false;
     grpc_timer_heap_pop(&shard->heap);
@@ -424,11 +515,16 @@
   grpc_timer *timer;
   gpr_mu_lock(&shard->mu);
   while ((timer = pop_one(shard, now))) {
+    REMOVE_FROM_HASH_TABLE(timer);
     GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_REF(error));
     n++;
   }
   *new_min_deadline = compute_min_deadline(shard);
   gpr_mu_unlock(&shard->mu);
+  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    gpr_log(GPR_DEBUG, "  .. shard[%d] popped %" PRIdPTR,
+            (int)(shard - g_shards), n);
+  }
   return n;
 }
 
@@ -501,29 +597,27 @@
 }
 
 grpc_timer_check_result grpc_timer_check(grpc_exec_ctx *exec_ctx,
-                                         gpr_timespec now, gpr_timespec *next) {
+                                         grpc_millis *next) {
   // prelude
-  GPR_ASSERT(now.clock_type == g_clock_type);
-  gpr_atm now_atm = timespec_to_atm_round_down(now);
+  grpc_millis now = grpc_exec_ctx_now(exec_ctx);
 
   /* fetch from a thread-local first: this avoids contention on a globally
      mutable cacheline in the common case */
-  gpr_atm min_timer = gpr_tls_get(&g_last_seen_min_timer);
-  if (now_atm < min_timer) {
+  grpc_millis min_timer = gpr_tls_get(&g_last_seen_min_timer);
+  if (now < min_timer) {
     if (next != NULL) {
-      *next =
-          atm_to_timespec(GPR_MIN(timespec_to_atm_round_up(*next), min_timer));
+      *next = GPR_MIN(*next, min_timer);
     }
     if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
       gpr_log(GPR_DEBUG,
-              "TIMER CHECK SKIP: now_atm=%" PRIdPTR " min_timer=%" PRIdPTR,
-              now_atm, min_timer);
+              "TIMER CHECK SKIP: now=%" PRIdPTR " min_timer=%" PRIdPTR, now,
+              min_timer);
     }
     return GRPC_TIMERS_CHECKED_AND_EMPTY;
   }
 
   grpc_error *shutdown_error =
-      gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0
+      now != GRPC_MILLIS_INF_FUTURE
           ? GRPC_ERROR_NONE
           : GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutting down timer system");
 
@@ -533,34 +627,24 @@
     if (next == NULL) {
       next_str = gpr_strdup("NULL");
     } else {
-      gpr_asprintf(&next_str, "%" PRId64 ".%09d [%" PRIdPTR "]", next->tv_sec,
-                   next->tv_nsec, timespec_to_atm_round_down(*next));
+      gpr_asprintf(&next_str, "%" PRIdPTR, *next);
     }
-    gpr_log(GPR_DEBUG, "TIMER CHECK BEGIN: now=%" PRId64 ".%09d [%" PRIdPTR
-                       "] next=%s tls_min=%" PRIdPTR " glob_min=%" PRIdPTR,
-            now.tv_sec, now.tv_nsec, now_atm, next_str,
-            gpr_tls_get(&g_last_seen_min_timer),
+    gpr_log(GPR_DEBUG, "TIMER CHECK BEGIN: now=%" PRIdPTR
+                       " next=%s tls_min=%" PRIdPTR " glob_min=%" PRIdPTR,
+            now, next_str, gpr_tls_get(&g_last_seen_min_timer),
             gpr_atm_no_barrier_load(&g_shared_mutables.min_timer));
     gpr_free(next_str);
   }
   // actual code
-  grpc_timer_check_result r;
-  gpr_atm next_atm;
-  if (next == NULL) {
-    r = run_some_expired_timers(exec_ctx, now_atm, NULL, shutdown_error);
-  } else {
-    next_atm = timespec_to_atm_round_down(*next);
-    r = run_some_expired_timers(exec_ctx, now_atm, &next_atm, shutdown_error);
-    *next = atm_to_timespec(next_atm);
-  }
+  grpc_timer_check_result r =
+      run_some_expired_timers(exec_ctx, now, next, shutdown_error);
   // tracing
   if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
     char *next_str;
     if (next == NULL) {
       next_str = gpr_strdup("NULL");
     } else {
-      gpr_asprintf(&next_str, "%" PRId64 ".%09d [%" PRIdPTR "]", next->tv_sec,
-                   next->tv_nsec, next_atm);
+      gpr_asprintf(&next_str, "%" PRIdPTR, *next);
     }
     gpr_log(GPR_DEBUG, "TIMER CHECK END: r=%d; next=%s", r, next_str);
     gpr_free(next_str);
diff --git a/src/core/lib/iomgr/timer_generic.h b/src/core/lib/iomgr/timer_generic.h
index 72a4ac1..f0597f6 100644
--- a/src/core/lib/iomgr/timer_generic.h
+++ b/src/core/lib/iomgr/timer_generic.h
@@ -29,6 +29,9 @@
   struct grpc_timer *next;
   struct grpc_timer *prev;
   grpc_closure *closure;
+#ifndef NDEBUG
+  struct grpc_timer *hash_table_next;
+#endif
 };
 
 #endif /* GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H */
diff --git a/src/core/lib/iomgr/timer_manager.c b/src/core/lib/iomgr/timer_manager.c
index 04ca445..f5dbe24 100644
--- a/src/core/lib/iomgr/timer_manager.c
+++ b/src/core/lib/iomgr/timer_manager.c
@@ -52,7 +52,7 @@
 static bool g_has_timed_waiter;
 // the deadline of the current timed waiter thread (only relevant if
 // g_has_timed_waiter is true)
-static gpr_timespec g_timed_waiter_deadline;
+static grpc_millis g_timed_waiter_deadline;
 // generation counter to track which thread is waiting for the next timer
 static uint64_t g_timed_waiter_generation;
 
@@ -96,9 +96,8 @@
 
 void grpc_timer_manager_tick() {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  gpr_timespec next = gpr_inf_future(GPR_CLOCK_MONOTONIC);
-  gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
-  grpc_timer_check(&exec_ctx, now, &next);
+  grpc_millis next = GRPC_MILLIS_INF_FUTURE;
+  grpc_timer_check(&exec_ctx, &next);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
@@ -121,6 +120,9 @@
     gpr_mu_unlock(&g_mu);
   }
   // without our lock, flush the exec_ctx
+  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    gpr_log(GPR_DEBUG, "flush exec_ctx");
+  }
   grpc_exec_ctx_flush(exec_ctx);
   gpr_mu_lock(&g_mu);
   // garbage collect any threads hanging out that are dead
@@ -133,8 +135,7 @@
 // wait until 'next' (or forever if there is already a timed waiter in the pool)
 // returns true if the thread should continue executing (false if it should
 // shutdown)
-static bool wait_until(gpr_timespec next) {
-  const gpr_timespec inf_future = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+static bool wait_until(grpc_exec_ctx *exec_ctx, grpc_millis next) {
   gpr_mu_lock(&g_mu);
   // if we're not threaded anymore, leave
   if (!g_threaded) {
@@ -168,30 +169,29 @@
        unless their 'next' is earlier than the current timed-waiter's deadline
        (in which case the thread with earlier 'next' takes over as the new timed
        waiter) */
-    if (gpr_time_cmp(next, inf_future) != 0) {
-      if (!g_has_timed_waiter ||
-          (gpr_time_cmp(next, g_timed_waiter_deadline) < 0)) {
+    if (next != GRPC_MILLIS_INF_FUTURE) {
+      if (!g_has_timed_waiter || (next < g_timed_waiter_deadline)) {
         my_timed_waiter_generation = ++g_timed_waiter_generation;
         g_has_timed_waiter = true;
         g_timed_waiter_deadline = next;
 
         if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
-          gpr_timespec wait_time =
-              gpr_time_sub(next, gpr_now(GPR_CLOCK_MONOTONIC));
-          gpr_log(GPR_DEBUG, "sleep for a %" PRId64 ".%09d seconds",
-                  wait_time.tv_sec, wait_time.tv_nsec);
+          grpc_millis wait_time = next - grpc_exec_ctx_now(exec_ctx);
+          gpr_log(GPR_DEBUG, "sleep for a %" PRIdPTR " milliseconds",
+                  wait_time);
         }
       } else {  // g_timed_waiter == true && next >= g_timed_waiter_deadline
-        next = inf_future;
+        next = GRPC_MILLIS_INF_FUTURE;
       }
     }
 
     if (GRPC_TRACER_ON(grpc_timer_check_trace) &&
-        gpr_time_cmp(next, inf_future) == 0) {
+        next == GRPC_MILLIS_INF_FUTURE) {
       gpr_log(GPR_DEBUG, "sleep until kicked");
     }
 
-    gpr_cv_wait(&g_cv_wait, &g_mu, next);
+    gpr_cv_wait(&g_cv_wait, &g_mu,
+                grpc_millis_to_timespec(next, GPR_CLOCK_REALTIME));
 
     if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
       gpr_log(GPR_DEBUG, "wait ended: was_timed:%d kicked:%d",
@@ -203,7 +203,7 @@
     // there's work to do after checking timers (code above)
     if (my_timed_waiter_generation == g_timed_waiter_generation) {
       g_has_timed_waiter = false;
-      g_timed_waiter_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+      g_timed_waiter_deadline = GRPC_MILLIS_INF_FUTURE;
     }
   }
 
@@ -219,12 +219,11 @@
 }
 
 static void timer_main_loop(grpc_exec_ctx *exec_ctx) {
-  const gpr_timespec inf_future = gpr_inf_future(GPR_CLOCK_MONOTONIC);
   for (;;) {
-    gpr_timespec next = inf_future;
-    gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
+    grpc_millis next = GRPC_MILLIS_INF_FUTURE;
+    grpc_exec_ctx_invalidate_now(exec_ctx);
     // check timer state, updates next to the next time to run a check
-    switch (grpc_timer_check(exec_ctx, now, &next)) {
+    switch (grpc_timer_check(exec_ctx, &next)) {
       case GRPC_TIMERS_FIRED:
         run_some_timers(exec_ctx);
         break;
@@ -241,10 +240,10 @@
         if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
           gpr_log(GPR_DEBUG, "timers not checked: expect another thread to");
         }
-        next = inf_future;
+        next = GRPC_MILLIS_INF_FUTURE;
       /* fall through */
       case GRPC_TIMERS_CHECKED_AND_EMPTY:
-        if (!wait_until(next)) {
+        if (!wait_until(exec_ctx, next)) {
           return;
         }
         break;
@@ -300,7 +299,7 @@
   g_completed_threads = NULL;
 
   g_has_timed_waiter = false;
-  g_timed_waiter_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  g_timed_waiter_deadline = GRPC_MILLIS_INF_FUTURE;
 
   start_threads();
 }
@@ -347,7 +346,7 @@
   gpr_mu_lock(&g_mu);
   g_kicked = true;
   g_has_timed_waiter = false;
-  g_timed_waiter_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  g_timed_waiter_deadline = GRPC_MILLIS_INF_FUTURE;
   ++g_timed_waiter_generation;
   gpr_cv_signal(&g_cv_wait);
   gpr_mu_unlock(&g_mu);
diff --git a/src/core/lib/security/credentials/composite/composite_credentials.c b/src/core/lib/security/credentials/composite/composite_credentials.c
index 09fd60a..b67ff48 100644
--- a/src/core/lib/security/credentials/composite/composite_credentials.c
+++ b/src/core/lib/security/credentials/composite/composite_credentials.c
@@ -87,6 +87,7 @@
   ctx->on_request_metadata = on_request_metadata;
   GRPC_CLOSURE_INIT(&ctx->internal_on_request_metadata,
                     composite_call_metadata_cb, ctx, grpc_schedule_on_exec_ctx);
+  bool synchronous = true;
   while (ctx->creds_index < ctx->composite_creds->inner.num_creds) {
     grpc_call_credentials *inner_creds =
         ctx->composite_creds->inner.creds_array[ctx->creds_index++];
@@ -95,19 +96,12 @@
             ctx->md_array, &ctx->internal_on_request_metadata, error)) {
       if (*error != GRPC_ERROR_NONE) break;
     } else {
+      synchronous = false;  // Async return.
       break;
     }
   }
-  // If we got through all creds synchronously or we got a synchronous
-  // error on one of them, return synchronously.
-  if (ctx->creds_index == ctx->composite_creds->inner.num_creds ||
-      *error != GRPC_ERROR_NONE) {
-    gpr_free(ctx);
-    return true;
-  }
-  // At least one inner cred is returning asynchronously, so we'll
-  // return asynchronously as well.
-  return false;
+  if (synchronous) gpr_free(ctx);
+  return synchronous;
 }
 
 static void composite_call_cancel_get_request_metadata(
diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.c b/src/core/lib/security/credentials/google_default/google_default_credentials.c
index 691d66d..0877818 100644
--- a/src/core/lib/security/credentials/google_default/google_default_credentials.c
+++ b/src/core/lib/security/credentials/google_default/google_default_credentials.c
@@ -96,7 +96,7 @@
 
   /* The http call is local. If it takes more than one sec, it is for sure not
      on compute engine. */
-  gpr_timespec max_detection_delay = gpr_time_from_seconds(1, GPR_TIMESPAN);
+  grpc_millis max_detection_delay = GPR_MS_PER_SEC;
 
   grpc_pollset *pollset = gpr_zalloc(grpc_pollset_size());
   grpc_pollset_init(pollset, &g_polling_mu);
@@ -115,7 +115,7 @@
       grpc_resource_quota_create("google_default_credentials");
   grpc_httpcli_get(
       exec_ctx, &context, &detector.pollent, resource_quota, &request,
-      gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay),
+      grpc_exec_ctx_now(exec_ctx) + max_detection_delay,
       GRPC_CLOSURE_CREATE(on_compute_engine_detection_http_response, &detector,
                           grpc_schedule_on_exec_ctx),
       &detector.response);
@@ -132,8 +132,7 @@
             "pollset_work",
             grpc_pollset_work(exec_ctx,
                               grpc_polling_entity_pollset(&detector.pollent),
-                              &worker, gpr_now(GPR_CLOCK_MONOTONIC),
-                              gpr_inf_future(GPR_CLOCK_MONOTONIC)))) {
+                              &worker, GRPC_MILLIS_INF_FUTURE))) {
       detector.is_done = 1;
       detector.success = 0;
     }
diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.c b/src/core/lib/security/credentials/jwt/jwt_verifier.c
index a27284b..7c24af8 100644
--- a/src/core/lib/security/credentials/jwt/jwt_verifier.c
+++ b/src/core/lib/security/credentials/jwt/jwt_verifier.c
@@ -380,7 +380,7 @@
 gpr_timespec grpc_jwt_verifier_clock_skew = {60, 0, GPR_TIMESPAN};
 
 /* Max delay defaults to one minute. */
-gpr_timespec grpc_jwt_verifier_max_delay = {60, 0, GPR_TIMESPAN};
+grpc_millis grpc_jwt_verifier_max_delay = 60 * GPR_MS_PER_SEC;
 
 typedef struct {
   char *email_domain;
@@ -707,7 +707,7 @@
       grpc_resource_quota_create("jwt_verifier");
   grpc_httpcli_get(
       exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
-      gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
+      grpc_exec_ctx_now(exec_ctx) + grpc_jwt_verifier_max_delay,
       GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx),
       &ctx->responses[HTTP_RESPONSE_KEYS]);
   grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
@@ -833,10 +833,10 @@
      extreme memory pressure. */
   grpc_resource_quota *resource_quota =
       grpc_resource_quota_create("jwt_verifier");
-  grpc_httpcli_get(
-      exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
-      gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
-      http_cb, &ctx->responses[rsp_idx]);
+  grpc_httpcli_get(exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent,
+                   resource_quota, &req,
+                   grpc_exec_ctx_now(exec_ctx) + grpc_jwt_verifier_max_delay,
+                   http_cb, &ctx->responses[rsp_idx]);
   grpc_resource_quota_unref_internal(exec_ctx, resource_quota);
   gpr_free(req.host);
   gpr_free(req.http.path);
diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.h b/src/core/lib/security/credentials/jwt/jwt_verifier.h
index 8fac452..3a3261a 100644
--- a/src/core/lib/security/credentials/jwt/jwt_verifier.h
+++ b/src/core/lib/security/credentials/jwt/jwt_verifier.h
@@ -81,7 +81,7 @@
 
 /* Globals to control the verifier. Not thread-safe. */
 extern gpr_timespec grpc_jwt_verifier_clock_skew;
-extern gpr_timespec grpc_jwt_verifier_max_delay;
+extern grpc_millis grpc_jwt_verifier_max_delay;
 
 /* The verifier can be created with some custom mappings to help with key
    discovery in the case where the issuer is an email address.
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
index 10b270c..5fe1f71 100644
--- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
@@ -117,7 +117,7 @@
 grpc_credentials_status
 grpc_oauth2_token_fetcher_credentials_parse_server_response(
     grpc_exec_ctx *exec_ctx, const grpc_http_response *response,
-    grpc_mdelem *token_md, gpr_timespec *token_lifetime) {
+    grpc_mdelem *token_md, grpc_millis *token_lifetime) {
   char *null_terminated_body = NULL;
   char *new_access_token = NULL;
   grpc_credentials_status status = GRPC_CREDENTIALS_OK;
@@ -183,9 +183,7 @@
     }
     gpr_asprintf(&new_access_token, "%s %s", token_type->value,
                  access_token->value);
-    token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10);
-    token_lifetime->tv_nsec = 0;
-    token_lifetime->clock_type = GPR_TIMESPAN;
+    *token_lifetime = strtol(expires_in->value, NULL, 10) * GPR_MS_PER_SEC;
     if (!GRPC_MDISNULL(*token_md)) GRPC_MDELEM_UNREF(exec_ctx, *token_md);
     *token_md = grpc_mdelem_from_slices(
         exec_ctx,
@@ -214,7 +212,7 @@
   grpc_oauth2_token_fetcher_credentials *c =
       (grpc_oauth2_token_fetcher_credentials *)r->creds;
   grpc_mdelem access_token_md = GRPC_MDNULL;
-  gpr_timespec token_lifetime;
+  grpc_millis token_lifetime;
   grpc_credentials_status status =
       grpc_oauth2_token_fetcher_credentials_parse_server_response(
           exec_ctx, &r->response, &access_token_md, &token_lifetime);
@@ -222,10 +220,9 @@
   gpr_mu_lock(&c->mu);
   c->token_fetch_pending = false;
   c->access_token_md = GRPC_MDELEM_REF(access_token_md);
-  c->token_expiration =
-      status == GRPC_CREDENTIALS_OK
-          ? gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime)
-          : gpr_inf_past(GPR_CLOCK_REALTIME);
+  c->token_expiration = status == GRPC_CREDENTIALS_OK
+                            ? grpc_exec_ctx_now(exec_ctx) + token_lifetime
+                            : 0;
   grpc_oauth2_pending_get_request_metadata *pending_request =
       c->pending_requests;
   c->pending_requests = NULL;
@@ -260,14 +257,12 @@
   grpc_oauth2_token_fetcher_credentials *c =
       (grpc_oauth2_token_fetcher_credentials *)creds;
   // Check if we can use the cached token.
-  gpr_timespec refresh_threshold = gpr_time_from_seconds(
-      GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
+  grpc_millis refresh_threshold =
+      GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS * GPR_MS_PER_SEC;
   grpc_mdelem cached_access_token_md = GRPC_MDNULL;
   gpr_mu_lock(&c->mu);
   if (!GRPC_MDISNULL(c->access_token_md) &&
-      (gpr_time_cmp(
-           gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_REALTIME)),
-           refresh_threshold) > 0)) {
+      (c->token_expiration + grpc_exec_ctx_now(exec_ctx) > refresh_threshold)) {
     cached_access_token_md = GRPC_MDELEM_REF(c->access_token_md);
   }
   if (!GRPC_MDISNULL(cached_access_token_md)) {
@@ -296,10 +291,10 @@
   gpr_mu_unlock(&c->mu);
   if (start_fetch) {
     grpc_call_credentials_ref(creds);
-    c->fetch_func(
-        exec_ctx, grpc_credentials_metadata_request_create(creds),
-        &c->httpcli_context, &c->pollent, on_oauth2_token_fetcher_http_response,
-        gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), refresh_threshold));
+    c->fetch_func(exec_ctx, grpc_credentials_metadata_request_create(creds),
+                  &c->httpcli_context, &c->pollent,
+                  on_oauth2_token_fetcher_http_response,
+                  grpc_exec_ctx_now(exec_ctx) + refresh_threshold);
   }
   return false;
 }
@@ -340,7 +335,7 @@
   c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2;
   gpr_ref_init(&c->base.refcount, 1);
   gpr_mu_init(&c->mu);
-  c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
+  c->token_expiration = 0;
   c->fetch_func = fetch_func;
   c->pollent =
       grpc_polling_entity_create_from_pollset_set(grpc_pollset_set_create());
@@ -358,7 +353,7 @@
 static void compute_engine_fetch_oauth2(
     grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req,
     grpc_httpcli_context *httpcli_context, grpc_polling_entity *pollent,
-    grpc_iomgr_cb_func response_cb, gpr_timespec deadline) {
+    grpc_iomgr_cb_func response_cb, grpc_millis deadline) {
   grpc_http_header header = {"Metadata-Flavor", "Google"};
   grpc_httpcli_request request;
   memset(&request, 0, sizeof(grpc_httpcli_request));
@@ -409,7 +404,7 @@
 static void refresh_token_fetch_oauth2(
     grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req,
     grpc_httpcli_context *httpcli_context, grpc_polling_entity *pollent,
-    grpc_iomgr_cb_func response_cb, gpr_timespec deadline) {
+    grpc_iomgr_cb_func response_cb, grpc_millis deadline) {
   grpc_google_refresh_token_credentials *c =
       (grpc_google_refresh_token_credentials *)metadata_req->creds;
   grpc_http_header header = {"Content-Type",
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
index d9ad669..5d9f243 100644
--- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
@@ -57,7 +57,7 @@
                                        grpc_httpcli_context *http_context,
                                        grpc_polling_entity *pollent,
                                        grpc_iomgr_cb_func cb,
-                                       gpr_timespec deadline);
+                                       grpc_millis deadline);
 
 typedef struct grpc_oauth2_pending_get_request_metadata {
   grpc_credentials_mdelem_array *md_array;
@@ -70,7 +70,7 @@
   grpc_call_credentials base;
   gpr_mu mu;
   grpc_mdelem access_token_md;
-  gpr_timespec token_expiration;
+  grpc_millis token_expiration;
   bool token_fetch_pending;
   grpc_oauth2_pending_get_request_metadata *pending_requests;
   grpc_httpcli_context httpcli_context;
@@ -100,6 +100,6 @@
 grpc_credentials_status
 grpc_oauth2_token_fetcher_credentials_parse_server_response(
     grpc_exec_ctx *exec_ctx, const struct grpc_http_response *response,
-    grpc_mdelem *token_md, gpr_timespec *token_lifetime);
+    grpc_mdelem *token_md, grpc_millis *token_lifetime);
 
 #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H */
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.c b/src/core/lib/security/credentials/plugin/plugin_credentials.c
index 73e0c23..ee20241 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.c
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.c
@@ -31,6 +31,9 @@
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/surface/validate_metadata.h"
 
+grpc_tracer_flag grpc_plugin_credentials_trace =
+    GRPC_TRACER_INITIALIZER(false, "plugin_credentials");
+
 static void plugin_destruct(grpc_exec_ctx *exec_ctx,
                             grpc_call_credentials *creds) {
   grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds;
@@ -53,6 +56,62 @@
   }
 }
 
+// Checks if the request has been cancelled.
+// If not, removes it from the pending list, so that it cannot be
+// cancelled out from under us.
+// When this returns, r->cancelled indicates whether the request was
+// cancelled before completion.
+static void pending_request_complete(
+    grpc_exec_ctx *exec_ctx, grpc_plugin_credentials_pending_request *r) {
+  gpr_mu_lock(&r->creds->mu);
+  if (!r->cancelled) pending_request_remove_locked(r->creds, r);
+  gpr_mu_unlock(&r->creds->mu);
+  // Ref to credentials not needed anymore.
+  grpc_call_credentials_unref(exec_ctx, &r->creds->base);
+}
+
+static grpc_error *process_plugin_result(
+    grpc_exec_ctx *exec_ctx, grpc_plugin_credentials_pending_request *r,
+    const grpc_metadata *md, size_t num_md, grpc_status_code status,
+    const char *error_details) {
+  grpc_error *error = GRPC_ERROR_NONE;
+  if (status != GRPC_STATUS_OK) {
+    char *msg;
+    gpr_asprintf(&msg, "Getting metadata from plugin failed with error: %s",
+                 error_details);
+    error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
+    gpr_free(msg);
+  } else {
+    bool seen_illegal_header = false;
+    for (size_t i = 0; i < num_md; ++i) {
+      if (!GRPC_LOG_IF_ERROR("validate_metadata_from_plugin",
+                             grpc_validate_header_key_is_legal(md[i].key))) {
+        seen_illegal_header = true;
+        break;
+      } else if (!grpc_is_binary_header(md[i].key) &&
+                 !GRPC_LOG_IF_ERROR(
+                     "validate_metadata_from_plugin",
+                     grpc_validate_header_nonbin_value_is_legal(md[i].value))) {
+        gpr_log(GPR_ERROR, "Plugin added invalid metadata value.");
+        seen_illegal_header = true;
+        break;
+      }
+    }
+    if (seen_illegal_header) {
+      error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal metadata");
+    } else {
+      for (size_t i = 0; i < num_md; ++i) {
+        grpc_mdelem mdelem = grpc_mdelem_from_slices(
+            exec_ctx, grpc_slice_ref_internal(md[i].key),
+            grpc_slice_ref_internal(md[i].value));
+        grpc_credentials_mdelem_array_add(r->md_array, mdelem);
+        GRPC_MDELEM_UNREF(exec_ctx, mdelem);
+      }
+    }
+  }
+  return error;
+}
+
 static void plugin_md_request_metadata_ready(void *request,
                                              const grpc_metadata *md,
                                              size_t num_md,
@@ -64,54 +123,24 @@
       NULL, NULL);
   grpc_plugin_credentials_pending_request *r =
       (grpc_plugin_credentials_pending_request *)request;
-  // Check if the request has been cancelled.
-  // If not, remove it from the pending list, so that it cannot be
-  // cancelled out from under us.
-  gpr_mu_lock(&r->creds->mu);
-  if (!r->cancelled) pending_request_remove_locked(r->creds, r);
-  gpr_mu_unlock(&r->creds->mu);
-  grpc_call_credentials_unref(&exec_ctx, &r->creds->base);
+  if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+    gpr_log(GPR_INFO,
+            "plugin_credentials[%p]: request %p: plugin returned "
+            "asynchronously",
+            r->creds, r);
+  }
+  // Remove request from pending list if not previously cancelled.
+  pending_request_complete(&exec_ctx, r);
   // If it has not been cancelled, process it.
   if (!r->cancelled) {
-    if (status != GRPC_STATUS_OK) {
-      char *msg;
-      gpr_asprintf(&msg, "Getting metadata from plugin failed with error: %s",
-                   error_details);
-      GRPC_CLOSURE_SCHED(&exec_ctx, r->on_request_metadata,
-                         GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg));
-      gpr_free(msg);
-    } else {
-      bool seen_illegal_header = false;
-      for (size_t i = 0; i < num_md; ++i) {
-        if (!GRPC_LOG_IF_ERROR("validate_metadata_from_plugin",
-                               grpc_validate_header_key_is_legal(md[i].key))) {
-          seen_illegal_header = true;
-          break;
-        } else if (!grpc_is_binary_header(md[i].key) &&
-                   !GRPC_LOG_IF_ERROR(
-                       "validate_metadata_from_plugin",
-                       grpc_validate_header_nonbin_value_is_legal(
-                           md[i].value))) {
-          gpr_log(GPR_ERROR, "Plugin added invalid metadata value.");
-          seen_illegal_header = true;
-          break;
-        }
-      }
-      if (seen_illegal_header) {
-        GRPC_CLOSURE_SCHED(
-            &exec_ctx, r->on_request_metadata,
-            GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal metadata"));
-      } else {
-        for (size_t i = 0; i < num_md; ++i) {
-          grpc_mdelem mdelem = grpc_mdelem_from_slices(
-              &exec_ctx, grpc_slice_ref_internal(md[i].key),
-              grpc_slice_ref_internal(md[i].value));
-          grpc_credentials_mdelem_array_add(r->md_array, mdelem);
-          GRPC_MDELEM_UNREF(&exec_ctx, mdelem);
-        }
-        GRPC_CLOSURE_SCHED(&exec_ctx, r->on_request_metadata, GRPC_ERROR_NONE);
-      }
-    }
+    grpc_error *error =
+        process_plugin_result(&exec_ctx, r, md, num_md, status, error_details);
+    GRPC_CLOSURE_SCHED(&exec_ctx, r->on_request_metadata, error);
+  } else if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+    gpr_log(GPR_INFO,
+            "plugin_credentials[%p]: request %p: plugin was previously "
+            "cancelled",
+            r->creds, r);
   }
   gpr_free(r);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -125,6 +154,7 @@
                                         grpc_closure *on_request_metadata,
                                         grpc_error **error) {
   grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds;
+  bool retval = true;  // Synchronous return.
   if (c->plugin.get_metadata != NULL) {
     // Create pending_request object.
     grpc_plugin_credentials_pending_request *pending_request =
@@ -142,12 +172,60 @@
     c->pending_requests = pending_request;
     gpr_mu_unlock(&c->mu);
     // Invoke the plugin.  The callback holds a ref to us.
+    if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+      gpr_log(GPR_INFO, "plugin_credentials[%p]: request %p: invoking plugin",
+              c, pending_request);
+    }
     grpc_call_credentials_ref(creds);
-    c->plugin.get_metadata(c->plugin.state, context,
-                           plugin_md_request_metadata_ready, pending_request);
-    return false;
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX];
+    size_t num_creds_md = 0;
+    grpc_status_code status = GRPC_STATUS_OK;
+    const char *error_details = NULL;
+    if (!c->plugin.get_metadata(c->plugin.state, context,
+                                plugin_md_request_metadata_ready,
+                                pending_request, creds_md, &num_creds_md,
+                                &status, &error_details)) {
+      if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+        gpr_log(GPR_INFO,
+                "plugin_credentials[%p]: request %p: plugin will return "
+                "asynchronously",
+                c, pending_request);
+      }
+      return false;  // Asynchronous return.
+    }
+    // Returned synchronously.
+    // Remove request from pending list if not previously cancelled.
+    pending_request_complete(exec_ctx, pending_request);
+    // If the request was cancelled, the error will have been returned
+    // asynchronously by plugin_cancel_get_request_metadata(), so return
+    // false.  Otherwise, process the result.
+    if (pending_request->cancelled) {
+      if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+        gpr_log(GPR_INFO,
+                "plugin_credentials[%p]: request %p was cancelled, error "
+                "will be returned asynchronously",
+                c, pending_request);
+      }
+      retval = false;
+    } else {
+      if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+        gpr_log(GPR_INFO,
+                "plugin_credentials[%p]: request %p: plugin returned "
+                "synchronously",
+                c, pending_request);
+      }
+      *error = process_plugin_result(exec_ctx, pending_request, creds_md,
+                                     num_creds_md, status, error_details);
+    }
+    // Clean up.
+    for (size_t i = 0; i < num_creds_md; ++i) {
+      grpc_slice_unref_internal(exec_ctx, creds_md[i].key);
+      grpc_slice_unref_internal(exec_ctx, creds_md[i].value);
+    }
+    gpr_free((void *)error_details);
+    gpr_free(pending_request);
   }
-  return true;
+  return retval;
 }
 
 static void plugin_cancel_get_request_metadata(
@@ -159,6 +237,10 @@
            c->pending_requests;
        pending_request != NULL; pending_request = pending_request->next) {
     if (pending_request->md_array == md_array) {
+      if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+        gpr_log(GPR_INFO, "plugin_credentials[%p]: cancelling request %p", c,
+                pending_request);
+      }
       pending_request->cancelled = true;
       GRPC_CLOSURE_SCHED(exec_ctx, pending_request->on_request_metadata,
                          GRPC_ERROR_REF(error));
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.h b/src/core/lib/security/credentials/plugin/plugin_credentials.h
index 57266d5..f56df9e 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.h
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.h
@@ -21,6 +21,8 @@
 
 #include "src/core/lib/security/credentials/credentials.h"
 
+extern grpc_tracer_flag grpc_plugin_credentials_trace;
+
 struct grpc_plugin_credentials;
 
 typedef struct grpc_plugin_credentials_pending_request {
diff --git a/src/core/lib/support/backoff.h b/src/core/lib/support/backoff.h
deleted file mode 100644
index 6e0cc3a..0000000
--- a/src/core/lib/support/backoff.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- *
- * Copyright 2016 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.
- *
- */
-
-#ifndef GRPC_CORE_LIB_SUPPORT_BACKOFF_H
-#define GRPC_CORE_LIB_SUPPORT_BACKOFF_H
-
-#include <grpc/support/time.h>
-
-typedef struct {
-  /// const:  how long to wait after the first failure before retrying
-  int64_t initial_connect_timeout;
-  /// const: factor with which to multiply backoff after a failed retry
-  double multiplier;
-  /// const: amount to randomize backoffs
-  double jitter;
-  /// const: minimum time between retries in milliseconds
-  int64_t min_timeout_millis;
-  /// const: maximum time between retries in milliseconds
-  int64_t max_timeout_millis;
-
-  /// random number generator
-  uint32_t rng_state;
-
-  /// current retry timeout in milliseconds
-  int64_t current_timeout_millis;
-} gpr_backoff;
-
-/// Initialize backoff machinery - does not need to be destroyed
-void gpr_backoff_init(gpr_backoff *backoff, int64_t initial_connect_timeout,
-                      double multiplier, double jitter,
-                      int64_t min_timeout_millis, int64_t max_timeout_millis);
-
-/// Begin retry loop: returns a timespec for the NEXT retry
-gpr_timespec gpr_backoff_begin(gpr_backoff *backoff, gpr_timespec now);
-/// Step a retry loop: returns a timespec for the NEXT retry
-gpr_timespec gpr_backoff_step(gpr_backoff *backoff, gpr_timespec now);
-/// Reset the backoff, so the next gpr_backoff_step will be a gpr_backoff_begin
-/// instead
-void gpr_backoff_reset(gpr_backoff *backoff);
-
-#endif /* GRPC_CORE_LIB_SUPPORT_BACKOFF_H */
diff --git a/src/core/lib/support/time_posix.c b/src/core/lib/support/time_posix.c
index 3ead40d..753a629 100644
--- a/src/core/lib/support/time_posix.c
+++ b/src/core/lib/support/time_posix.c
@@ -30,7 +30,6 @@
 #include <grpc/support/atm.h>
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
-#include "src/core/lib/support/block_annotate.h"
 
 static struct timespec timespec_from_gpr(gpr_timespec gts) {
   struct timespec rv;
@@ -157,9 +156,7 @@
 
     delta = gpr_time_sub(until, now);
     delta_ts = timespec_from_gpr(delta);
-    GRPC_SCHEDULING_START_BLOCKING_REGION;
     ns_result = nanosleep(&delta_ts, NULL);
-    GRPC_SCHEDULING_END_BLOCKING_REGION;
     if (ns_result == 0) {
       break;
     }
diff --git a/src/core/lib/support/time_windows.c b/src/core/lib/support/time_windows.c
index 40df376..fb17e5c 100644
--- a/src/core/lib/support/time_windows.c
+++ b/src/core/lib/support/time_windows.c
@@ -28,7 +28,6 @@
 #include <process.h>
 #include <sys/timeb.h>
 
-#include "src/core/lib/support/block_annotate.h"
 #include "src/core/lib/support/time_precise.h"
 
 static LARGE_INTEGER g_start_time;
@@ -92,9 +91,7 @@
     sleep_millis =
         delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS;
     GPR_ASSERT((sleep_millis >= 0) && (sleep_millis <= INT_MAX));
-    GRPC_SCHEDULING_START_BLOCKING_REGION;
     Sleep((DWORD)sleep_millis);
-    GRPC_SCHEDULING_END_BLOCKING_REGION;
   }
 }
 
diff --git a/src/core/lib/surface/alarm.c b/src/core/lib/surface/alarm.c
index 7712f56..e3bcbb9 100644
--- a/src/core/lib/surface/alarm.c
+++ b/src/core/lib/surface/alarm.c
@@ -122,8 +122,7 @@
 
   GPR_ASSERT(grpc_cq_begin_op(cq, tag));
   grpc_timer_init(&exec_ctx, &alarm->alarm,
-                  gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
-                  &alarm->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC));
+                  grpc_timespec_to_millis_round_up(deadline), &alarm->on_alarm);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
index 1390f63..2871dea 100644
--- a/src/core/lib/surface/call.c
+++ b/src/core/lib/surface/call.c
@@ -214,7 +214,7 @@
      server, it's trailing metadata */
   grpc_linked_mdelem send_extra_metadata[MAX_SEND_EXTRA_METADATA_COUNT];
   int send_extra_metadata_count;
-  gpr_timespec send_deadline;
+  grpc_millis send_deadline;
 
   grpc_slice_buffer_stream sending_stream;
 
@@ -281,7 +281,7 @@
                          grpc_error *error);
 static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp,
                                   grpc_error *error);
-static void get_final_status(grpc_call *call,
+static void get_final_status(grpc_exec_ctx *exec_ctx, grpc_call *call,
                              void (*set_value)(grpc_status_code code,
                                                void *user_data),
                              void *set_value_user_data, grpc_slice *details);
@@ -370,11 +370,10 @@
   }
   for (i = 0; i < 2; i++) {
     for (j = 0; j < 2; j++) {
-      call->metadata_batch[i][j].deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+      call->metadata_batch[i][j].deadline = GRPC_MILLIS_INF_FUTURE;
     }
   }
-  gpr_timespec send_deadline =
-      gpr_convert_clock_type(args->send_deadline, GPR_CLOCK_MONOTONIC);
+  grpc_millis send_deadline = args->send_deadline;
 
   bool immediately_cancel = false;
 
@@ -392,10 +391,7 @@
     gpr_mu_lock(&pc->child_list_mu);
 
     if (args->propagation_mask & GRPC_PROPAGATE_DEADLINE) {
-      send_deadline = gpr_time_min(
-          gpr_convert_clock_type(send_deadline,
-                                 args->parent->send_deadline.clock_type),
-          args->parent->send_deadline);
+      send_deadline = GPR_MIN(send_deadline, args->parent->send_deadline);
     }
     /* for now GRPC_PROPAGATE_TRACING_CONTEXT *MUST* be passed with
      * GRPC_PROPAGATE_STATS_CONTEXT */
@@ -550,8 +546,8 @@
     GRPC_CQ_INTERNAL_UNREF(exec_ctx, c->cq, "bind");
   }
 
-  get_final_status(c, set_status_value_directly, &c->final_info.final_status,
-                   NULL);
+  get_final_status(exec_ctx, c, set_status_value_directly,
+                   &c->final_info.final_status, NULL);
   c->final_info.stats.latency =
       gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), c->start_time);
 
@@ -737,13 +733,16 @@
  * FINAL STATUS CODE MANIPULATION
  */
 
-static bool get_final_status_from(
-    grpc_call *call, grpc_error *error, bool allow_ok_status,
-    void (*set_value)(grpc_status_code code, void *user_data),
-    void *set_value_user_data, grpc_slice *details) {
+static bool get_final_status_from(grpc_exec_ctx *exec_ctx, grpc_call *call,
+                                  grpc_error *error, bool allow_ok_status,
+                                  void (*set_value)(grpc_status_code code,
+                                                    void *user_data),
+                                  void *set_value_user_data,
+                                  grpc_slice *details) {
   grpc_status_code code;
   grpc_slice slice = grpc_empty_slice();
-  grpc_error_get_status(error, call->send_deadline, &code, &slice, NULL);
+  grpc_error_get_status(exec_ctx, error, call->send_deadline, &code, &slice,
+                        NULL);
   if (code == GRPC_STATUS_OK && !allow_ok_status) {
     return false;
   }
@@ -755,7 +754,7 @@
   return true;
 }
 
-static void get_final_status(grpc_call *call,
+static void get_final_status(grpc_exec_ctx *exec_ctx, grpc_call *call,
                              void (*set_value)(grpc_status_code code,
                                                void *user_data),
                              void *set_value_user_data, grpc_slice *details) {
@@ -780,8 +779,9 @@
     for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
       if (status[i].is_set &&
           grpc_error_has_clear_grpc_status(status[i].error)) {
-        if (get_final_status_from(call, status[i].error, allow_ok_status != 0,
-                                  set_value, set_value_user_data, details)) {
+        if (get_final_status_from(exec_ctx, call, status[i].error,
+                                  allow_ok_status != 0, set_value,
+                                  set_value_user_data, details)) {
           return;
         }
       }
@@ -789,8 +789,9 @@
     /* If no clearly defined status exists, search for 'anything' */
     for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
       if (status[i].is_set) {
-        if (get_final_status_from(call, status[i].error, allow_ok_status != 0,
-                                  set_value, set_value_user_data, details)) {
+        if (get_final_status_from(exec_ctx, call, status[i].error,
+                                  allow_ok_status != 0, set_value,
+                                  set_value_user_data, details)) {
           return;
         }
       }
@@ -1331,11 +1332,11 @@
     }
 
     if (call->is_client) {
-      get_final_status(call, set_status_value_directly,
+      get_final_status(exec_ctx, call, set_status_value_directly,
                        call->final_op.client.status,
                        call->final_op.client.status_details);
     } else {
-      get_final_status(call, set_cancelled_value,
+      get_final_status(exec_ctx, call, set_cancelled_value,
                        call->final_op.server.cancelled, NULL);
     }
 
@@ -1617,11 +1618,8 @@
     validate_filtered_metadata(exec_ctx, bctl);
     GPR_TIMER_END("validate_filtered_metadata", 0);
 
-    if (gpr_time_cmp(md->deadline, gpr_inf_future(md->deadline.clock_type)) !=
-            0 &&
-        !call->is_client) {
-      call->send_deadline =
-          gpr_convert_clock_type(md->deadline, GPR_CLOCK_MONOTONIC);
+    if (md->deadline != GRPC_MILLIS_INF_FUTURE && !call->is_client) {
+      call->send_deadline = md->deadline;
     }
   }
 
diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h
index c680139..27c2f52 100644
--- a/src/core/lib/surface/call.h
+++ b/src/core/lib/surface/call.h
@@ -49,7 +49,7 @@
   grpc_mdelem *add_initial_metadata;
   size_t add_initial_metadata_count;
 
-  gpr_timespec send_deadline;
+  grpc_millis send_deadline;
 } grpc_call_create_args;
 
 /* Create a new call based on \a args.
diff --git a/src/core/lib/surface/channel.c b/src/core/lib/surface/channel.c
index 48962e5..0b5ca17 100644
--- a/src/core/lib/surface/channel.c
+++ b/src/core/lib/surface/channel.c
@@ -265,7 +265,7 @@
     grpc_exec_ctx *exec_ctx, grpc_channel *channel, grpc_call *parent_call,
     uint32_t propagation_mask, grpc_completion_queue *cq,
     grpc_pollset_set *pollset_set_alternative, grpc_mdelem path_mdelem,
-    grpc_mdelem authority_mdelem, gpr_timespec deadline) {
+    grpc_mdelem authority_mdelem, grpc_millis deadline) {
   grpc_mdelem send_metadata[2];
   size_t num_metadata = 0;
 
@@ -311,7 +311,7 @@
       host != NULL ? grpc_mdelem_from_slices(&exec_ctx, GRPC_MDSTR_AUTHORITY,
                                              grpc_slice_ref_internal(*host))
                    : GRPC_MDNULL,
-      deadline);
+      grpc_timespec_to_millis_round_up(deadline));
   grpc_exec_ctx_finish(&exec_ctx);
   return call;
 }
@@ -319,7 +319,7 @@
 grpc_call *grpc_channel_create_pollset_set_call(
     grpc_exec_ctx *exec_ctx, grpc_channel *channel, grpc_call *parent_call,
     uint32_t propagation_mask, grpc_pollset_set *pollset_set, grpc_slice method,
-    const grpc_slice *host, gpr_timespec deadline, void *reserved) {
+    const grpc_slice *host, grpc_millis deadline, void *reserved) {
   GPR_ASSERT(!reserved);
   return grpc_channel_create_call_internal(
       exec_ctx, channel, parent_call, propagation_mask, NULL, pollset_set,
@@ -375,7 +375,8 @@
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_call *call = grpc_channel_create_call_internal(
       &exec_ctx, channel, parent_call, propagation_mask, completion_queue, NULL,
-      GRPC_MDELEM_REF(rc->path), GRPC_MDELEM_REF(rc->authority), deadline);
+      GRPC_MDELEM_REF(rc->path), GRPC_MDELEM_REF(rc->authority),
+      grpc_timespec_to_millis_round_up(deadline));
   grpc_exec_ctx_finish(&exec_ctx);
   return call;
 }
diff --git a/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h
index 528bb86..827dd99 100644
--- a/src/core/lib/surface/channel.h
+++ b/src/core/lib/surface/channel.h
@@ -43,7 +43,7 @@
 grpc_call *grpc_channel_create_pollset_set_call(
     grpc_exec_ctx *exec_ctx, grpc_channel *channel, grpc_call *parent_call,
     uint32_t propagation_mask, grpc_pollset_set *pollset_set, grpc_slice method,
-    const grpc_slice *host, gpr_timespec deadline, void *reserved);
+    const grpc_slice *host, grpc_millis deadline, void *reserved);
 
 /** Get a (borrowed) pointer to this channels underlying channel stack */
 grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel);
diff --git a/src/core/lib/surface/completion_queue.c b/src/core/lib/surface/completion_queue.c
index fed66e3..a05ac02 100644
--- a/src/core/lib/surface/completion_queue.c
+++ b/src/core/lib/surface/completion_queue.c
@@ -58,8 +58,7 @@
   grpc_error *(*kick)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                       grpc_pollset_worker *specific_worker);
   grpc_error *(*work)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                      grpc_pollset_worker **worker, gpr_timespec now,
-                      gpr_timespec deadline);
+                      grpc_pollset_worker **worker, grpc_millis deadline);
   void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                    grpc_closure *closure);
   void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset);
@@ -97,8 +96,7 @@
 static grpc_error *non_polling_poller_work(grpc_exec_ctx *exec_ctx,
                                            grpc_pollset *pollset,
                                            grpc_pollset_worker **worker,
-                                           gpr_timespec now,
-                                           gpr_timespec deadline) {
+                                           grpc_millis deadline) {
   non_polling_poller *npp = (non_polling_poller *)pollset;
   if (npp->shutdown) return GRPC_ERROR_NONE;
   non_polling_worker w;
@@ -112,7 +110,10 @@
     w.next->prev = w.prev->next = &w;
   }
   w.kicked = false;
-  while (!npp->shutdown && !w.kicked && !gpr_cv_wait(&w.cv, &npp->mu, deadline))
+  gpr_timespec deadline_ts =
+      grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME);
+  while (!npp->shutdown && !w.kicked &&
+         !gpr_cv_wait(&w.cv, &npp->mu, deadline_ts))
     ;
   if (&w == npp->root) {
     npp->root = w.next;
@@ -757,7 +758,7 @@
 typedef struct {
   gpr_atm last_seen_things_queued_ever;
   grpc_completion_queue *cq;
-  gpr_timespec deadline;
+  grpc_millis deadline;
   grpc_cq_completion *stolen_completion;
   void *tag; /* for pluck */
   bool first_loop;
@@ -786,8 +787,7 @@
       return true;
     }
   }
-  return !a->first_loop &&
-         gpr_time_cmp(a->deadline, gpr_now(a->deadline.clock_type)) < 0;
+  return !a->first_loop && a->deadline < grpc_exec_ctx_now(exec_ctx);
 }
 
 #ifndef NDEBUG
@@ -816,7 +816,6 @@
 static grpc_event cq_next(grpc_completion_queue *cq, gpr_timespec deadline,
                           void *reserved) {
   grpc_event ret;
-  gpr_timespec now;
   cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq);
 
   GPR_TIMER_BEGIN("grpc_completion_queue_next", 0);
@@ -833,23 +832,21 @@
 
   dump_pending_tags(cq);
 
-  deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
-
   GRPC_CQ_INTERNAL_REF(cq, "next");
 
+  grpc_millis deadline_millis = grpc_timespec_to_millis_round_up(deadline);
   cq_is_finished_arg is_finished_arg = {
       .last_seen_things_queued_ever =
           gpr_atm_no_barrier_load(&cqd->things_queued_ever),
       .cq = cq,
-      .deadline = deadline,
+      .deadline = deadline_millis,
       .stolen_completion = NULL,
       .tag = NULL,
       .first_loop = true};
   grpc_exec_ctx exec_ctx =
       GRPC_EXEC_CTX_INITIALIZER(0, cq_is_next_finished, &is_finished_arg);
-
   for (;;) {
-    gpr_timespec iteration_deadline = deadline;
+    grpc_millis iteration_deadline = deadline_millis;
 
     if (is_finished_arg.stolen_completion != NULL) {
       grpc_cq_completion *c = is_finished_arg.stolen_completion;
@@ -876,7 +873,7 @@
          attempt at popping. Not doing this can potentially deadlock this
          thread forever (if the deadline is infinity) */
       if (cq_event_queue_num_items(&cqd->queue) > 0) {
-        iteration_deadline = gpr_time_0(GPR_CLOCK_MONOTONIC);
+        iteration_deadline = 0;
       }
     }
 
@@ -897,8 +894,8 @@
       break;
     }
 
-    now = gpr_now(GPR_CLOCK_MONOTONIC);
-    if (!is_finished_arg.first_loop && gpr_time_cmp(now, deadline) >= 0) {
+    if (!is_finished_arg.first_loop &&
+        grpc_exec_ctx_now(&exec_ctx) >= deadline_millis) {
       memset(&ret, 0, sizeof(ret));
       ret.type = GRPC_QUEUE_TIMEOUT;
       dump_pending_tags(cq);
@@ -909,7 +906,7 @@
     gpr_mu_lock(cq->mu);
     cq->num_polls++;
     grpc_error *err = cq->poller_vtable->work(&exec_ctx, POLLSET_FROM_CQ(cq),
-                                              NULL, now, iteration_deadline);
+                                              NULL, iteration_deadline);
     gpr_mu_unlock(cq->mu);
 
     if (err != GRPC_ERROR_NONE) {
@@ -1046,8 +1043,7 @@
     }
     gpr_mu_unlock(cq->mu);
   }
-  return !a->first_loop &&
-         gpr_time_cmp(a->deadline, gpr_now(a->deadline.clock_type)) < 0;
+  return !a->first_loop && a->deadline < grpc_exec_ctx_now(exec_ctx);
 }
 
 static grpc_event cq_pluck(grpc_completion_queue *cq, void *tag,
@@ -1056,7 +1052,6 @@
   grpc_cq_completion *c;
   grpc_cq_completion *prev;
   grpc_pollset_worker *worker = NULL;
-  gpr_timespec now;
   cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq);
 
   GPR_TIMER_BEGIN("grpc_completion_queue_pluck", 0);
@@ -1075,15 +1070,14 @@
 
   dump_pending_tags(cq);
 
-  deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC);
-
   GRPC_CQ_INTERNAL_REF(cq, "pluck");
   gpr_mu_lock(cq->mu);
+  grpc_millis deadline_millis = grpc_timespec_to_millis_round_up(deadline);
   cq_is_finished_arg is_finished_arg = {
       .last_seen_things_queued_ever =
           gpr_atm_no_barrier_load(&cqd->things_queued_ever),
       .cq = cq,
-      .deadline = deadline,
+      .deadline = deadline_millis,
       .stolen_completion = NULL,
       .tag = tag,
       .first_loop = true};
@@ -1135,8 +1129,8 @@
       dump_pending_tags(cq);
       break;
     }
-    now = gpr_now(GPR_CLOCK_MONOTONIC);
-    if (!is_finished_arg.first_loop && gpr_time_cmp(now, deadline) >= 0) {
+    if (!is_finished_arg.first_loop &&
+        grpc_exec_ctx_now(&exec_ctx) >= deadline_millis) {
       del_plucker(cq, tag, &worker);
       gpr_mu_unlock(cq->mu);
       memset(&ret, 0, sizeof(ret));
@@ -1144,10 +1138,9 @@
       dump_pending_tags(cq);
       break;
     }
-
     cq->num_polls++;
     grpc_error *err = cq->poller_vtable->work(&exec_ctx, POLLSET_FROM_CQ(cq),
-                                              &worker, now, deadline);
+                                              &worker, deadline_millis);
     if (err != GRPC_ERROR_NONE) {
       del_plucker(cq, tag, &worker);
       gpr_mu_unlock(cq->mu);
diff --git a/src/core/lib/surface/init_secure.c b/src/core/lib/surface/init_secure.c
index 2366c24..8fbde3d 100644
--- a/src/core/lib/surface/init_secure.c
+++ b/src/core/lib/surface/init_secure.c
@@ -25,6 +25,7 @@
 
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/credentials/plugin/plugin_credentials.h"
 #include "src/core/lib/security/transport/auth_filters.h"
 #include "src/core/lib/security/transport/secure_endpoint.h"
 #include "src/core/lib/security/transport/security_connector.h"
@@ -84,4 +85,7 @@
                                    maybe_prepend_server_auth_filter, NULL);
 }
 
-void grpc_security_init() { grpc_security_register_handshaker_factories(); }
+void grpc_security_init() {
+  grpc_security_register_handshaker_factories();
+  grpc_register_tracer(&grpc_plugin_credentials_trace);
+}
diff --git a/src/core/lib/surface/lame_client.cc b/src/core/lib/surface/lame_client.cc
index 6286f91..88e26cb 100644
--- a/src/core/lib/surface/lame_client.cc
+++ b/src/core/lib/surface/lame_client.cc
@@ -74,7 +74,7 @@
   mdb->list.head = &calld->status;
   mdb->list.tail = &calld->details;
   mdb->list.count = 2;
-  mdb->deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
+  mdb->deadline = GRPC_MILLIS_INF_FUTURE;
 }
 
 static void lame_start_transport_stream_op_batch(
diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c
index 1d0fd47..dd09cb9 100644
--- a/src/core/lib/surface/server.c
+++ b/src/core/lib/surface/server.c
@@ -137,7 +137,7 @@
   bool host_set;
   grpc_slice path;
   grpc_slice host;
-  gpr_timespec deadline;
+  grpc_millis deadline;
 
   grpc_completion_queue *cq_new;
 
@@ -492,11 +492,13 @@
       GPR_ASSERT(calld->path_set);
       rc->data.batch.details->host = grpc_slice_ref_internal(calld->host);
       rc->data.batch.details->method = grpc_slice_ref_internal(calld->path);
-      rc->data.batch.details->deadline = calld->deadline;
+      rc->data.batch.details->deadline =
+          grpc_millis_to_timespec(calld->deadline, GPR_CLOCK_MONOTONIC);
       rc->data.batch.details->flags = calld->recv_initial_metadata_flags;
       break;
     case REGISTERED_CALL:
-      *rc->data.registered.deadline = calld->deadline;
+      *rc->data.registered.deadline =
+          grpc_millis_to_timespec(calld->deadline, GPR_CLOCK_MONOTONIC);
       if (rc->data.registered.optional_payload) {
         *rc->data.registered.optional_payload = calld->payload;
         calld->payload = NULL;
@@ -739,7 +741,7 @@
                                             grpc_error *error) {
   grpc_call_element *elem = (grpc_call_element *)ptr;
   call_data *calld = (call_data *)elem->call_data;
-  gpr_timespec op_deadline;
+  grpc_millis op_deadline;
 
   if (error == GRPC_ERROR_NONE) {
     GPR_ASSERT(calld->recv_initial_metadata->idx.named.path != NULL);
@@ -759,7 +761,7 @@
     GRPC_ERROR_REF(error);
   }
   op_deadline = calld->recv_initial_metadata->deadline;
-  if (0 != gpr_time_cmp(op_deadline, gpr_inf_future(op_deadline.clock_type))) {
+  if (op_deadline != GRPC_MILLIS_INF_FUTURE) {
     calld->deadline = op_deadline;
   }
   if (calld->host_set && calld->path_set) {
@@ -833,7 +835,7 @@
   memset(&args, 0, sizeof(args));
   args.channel = chand->channel;
   args.server_transport_data = transport_server_data;
-  args.send_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  args.send_deadline = GRPC_MILLIS_INF_FUTURE;
   grpc_call *call;
   grpc_error *error = grpc_call_create(exec_ctx, &args, &call);
   grpc_call_element *elem =
@@ -881,7 +883,7 @@
   call_data *calld = (call_data *)elem->call_data;
   channel_data *chand = (channel_data *)elem->channel_data;
   memset(calld, 0, sizeof(call_data));
-  calld->deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
+  calld->deadline = GRPC_MILLIS_INF_FUTURE;
   calld->call = grpc_call_from_top_element(elem);
   gpr_mu_init(&calld->mu_state);
 
diff --git a/src/core/lib/transport/error_utils.c b/src/core/lib/transport/error_utils.c
index 5e3920b..2e3b61b 100644
--- a/src/core/lib/transport/error_utils.c
+++ b/src/core/lib/transport/error_utils.c
@@ -39,8 +39,9 @@
   return NULL;
 }
 
-void grpc_error_get_status(grpc_error *error, gpr_timespec deadline,
-                           grpc_status_code *code, grpc_slice *slice,
+void grpc_error_get_status(grpc_exec_ctx *exec_ctx, grpc_error *error,
+                           grpc_millis deadline, grpc_status_code *code,
+                           grpc_slice *slice,
                            grpc_http2_error_code *http_error) {
   // Start with the parent error and recurse through the tree of children
   // until we find the first one that has a status code.
@@ -63,8 +64,8 @@
     status = (grpc_status_code)integer;
   } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR,
                                 &integer)) {
-    status = grpc_http2_error_to_grpc_status((grpc_http2_error_code)integer,
-                                             deadline);
+    status = grpc_http2_error_to_grpc_status(
+        exec_ctx, (grpc_http2_error_code)integer, deadline);
   }
   if (code != NULL) *code = status;
 
diff --git a/src/core/lib/transport/error_utils.h b/src/core/lib/transport/error_utils.h
index e530884..6211031 100644
--- a/src/core/lib/transport/error_utils.h
+++ b/src/core/lib/transport/error_utils.h
@@ -20,6 +20,7 @@
 #define GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H
 
 #include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/transport/http2_errors.h"
 
 /// A utility function to get the status code and message to be returned
@@ -28,8 +29,9 @@
 /// All attributes are pulled from the same child error. If any of the
 /// attributes (code, msg, http_status) are unneeded, they can be passed as
 /// NULL.
-void grpc_error_get_status(grpc_error *error, gpr_timespec deadline,
-                           grpc_status_code *code, grpc_slice *slice,
+void grpc_error_get_status(grpc_exec_ctx *exec_ctx, grpc_error *error,
+                           grpc_millis deadline, grpc_status_code *code,
+                           grpc_slice *slice,
                            grpc_http2_error_code *http_status);
 
 /// A utility function to check whether there is a clear status code that
diff --git a/src/core/lib/transport/metadata_batch.c b/src/core/lib/transport/metadata_batch.c
index 54388bd..2df9c91 100644
--- a/src/core/lib/transport/metadata_batch.c
+++ b/src/core/lib/transport/metadata_batch.c
@@ -74,7 +74,7 @@
 
 void grpc_metadata_batch_init(grpc_metadata_batch *batch) {
   memset(batch, 0, sizeof(*batch));
-  batch->deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
+  batch->deadline = GRPC_MILLIS_INF_FUTURE;
 }
 
 void grpc_metadata_batch_destroy(grpc_exec_ctx *exec_ctx,
@@ -270,9 +270,7 @@
 }
 
 bool grpc_metadata_batch_is_empty(grpc_metadata_batch *batch) {
-  return batch->list.head == NULL &&
-         gpr_time_cmp(gpr_inf_future(batch->deadline.clock_type),
-                      batch->deadline) == 0;
+  return batch->list.head == NULL && batch->deadline == GRPC_MILLIS_INF_FUTURE;
 }
 
 size_t grpc_metadata_batch_size(grpc_metadata_batch *batch) {
diff --git a/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h
index 57d298c..2adf188 100644
--- a/src/core/lib/transport/metadata_batch.h
+++ b/src/core/lib/transport/metadata_batch.h
@@ -51,9 +51,9 @@
   grpc_mdelem_list list;
   grpc_metadata_batch_callouts idx;
   /** Used to calculate grpc-timeout at the point of sending,
-      or gpr_inf_future if this batch does not need to send a
+      or GRPC_MILLIS_INF_FUTURE if this batch does not need to send a
       grpc-timeout */
-  gpr_timespec deadline;
+  grpc_millis deadline;
 } grpc_metadata_batch;
 
 void grpc_metadata_batch_init(grpc_metadata_batch *batch);
diff --git a/src/core/lib/transport/status_conversion.c b/src/core/lib/transport/status_conversion.c
index a40d333..891c442 100644
--- a/src/core/lib/transport/status_conversion.c
+++ b/src/core/lib/transport/status_conversion.c
@@ -37,8 +37,9 @@
   }
 }
 
-grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error,
-                                                 gpr_timespec deadline) {
+grpc_status_code grpc_http2_error_to_grpc_status(grpc_exec_ctx *exec_ctx,
+                                                 grpc_http2_error_code error,
+                                                 grpc_millis deadline) {
   switch (error) {
     case GRPC_HTTP2_NO_ERROR:
       /* should never be received */
@@ -46,7 +47,7 @@
     case GRPC_HTTP2_CANCEL:
       /* http2 cancel translates to STATUS_CANCELLED iff deadline hasn't been
        * exceeded */
-      return gpr_time_cmp(gpr_now(deadline.clock_type), deadline) >= 0
+      return grpc_exec_ctx_now(exec_ctx) > deadline
                  ? GRPC_STATUS_DEADLINE_EXCEEDED
                  : GRPC_STATUS_CANCELLED;
     case GRPC_HTTP2_ENHANCE_YOUR_CALM:
diff --git a/src/core/lib/transport/status_conversion.h b/src/core/lib/transport/status_conversion.h
index e93f3df..a05ed76 100644
--- a/src/core/lib/transport/status_conversion.h
+++ b/src/core/lib/transport/status_conversion.h
@@ -20,12 +20,14 @@
 #define GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H
 
 #include <grpc/grpc.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/transport/http2_errors.h"
 
 /* Conversion of grpc status codes to http2 error codes (for RST_STREAM) */
 grpc_http2_error_code grpc_status_to_http2_error(grpc_status_code status);
-grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error,
-                                                 gpr_timespec deadline);
+grpc_status_code grpc_http2_error_to_grpc_status(grpc_exec_ctx *exec_ctx,
+                                                 grpc_http2_error_code error,
+                                                 grpc_millis deadline);
 
 /* Conversion of HTTP status codes (:status) to grpc status codes */
 grpc_status_code grpc_http2_status_to_grpc_status(int status);
diff --git a/src/core/lib/transport/timeout_encoding.c b/src/core/lib/transport/timeout_encoding.c
index 02f179d..23a9ef3 100644
--- a/src/core/lib/transport/timeout_encoding.c
+++ b/src/core/lib/transport/timeout_encoding.c
@@ -59,60 +59,27 @@
   }
 }
 
-static void enc_nanos(char *buffer, int64_t x) {
+static void enc_millis(char *buffer, int64_t x) {
   x = round_up_to_three_sig_figs(x);
-  if (x < 100000) {
-    if (x % 1000 == 0) {
-      enc_ext(buffer, x / 1000, 'u');
-    } else {
-      enc_ext(buffer, x, 'n');
-    }
-  } else if (x < 100000000) {
-    if (x % 1000000 == 0) {
-      enc_ext(buffer, x / 1000000, 'm');
-    } else {
-      enc_ext(buffer, x / 1000, 'u');
-    }
-  } else if (x < 1000000000) {
-    enc_ext(buffer, x / 1000000, 'm');
+  if (x < GPR_MS_PER_SEC) {
+    enc_ext(buffer, x, 'm');
   } else {
-    /* note that this is only ever called with times of less than one second,
-       so if we reach here the time must have been rounded up to a whole second
-       (and no more) */
-    memcpy(buffer, "1S", 3);
+    if (x % GPR_MS_PER_SEC == 0) {
+      enc_seconds(buffer, x / GPR_MS_PER_SEC);
+    } else {
+      enc_ext(buffer, x, 'm');
+    }
   }
 }
 
-static void enc_micros(char *buffer, int64_t x) {
-  x = round_up_to_three_sig_figs(x);
-  if (x < 100000) {
-    if (x % 1000 == 0) {
-      enc_ext(buffer, x / 1000, 'm');
-    } else {
-      enc_ext(buffer, x, 'u');
-    }
-  } else if (x < 100000000) {
-    if (x % 1000000 == 0) {
-      enc_ext(buffer, x / 1000000, 'S');
-    } else {
-      enc_ext(buffer, x / 1000, 'm');
-    }
-  } else {
-    enc_ext(buffer, x / 1000000, 'S');
-  }
-}
-
-void grpc_http2_encode_timeout(gpr_timespec timeout, char *buffer) {
-  if (timeout.tv_sec < 0) {
+void grpc_http2_encode_timeout(grpc_millis timeout, char *buffer) {
+  if (timeout <= 0) {
     enc_tiny(buffer);
-  } else if (timeout.tv_sec == 0) {
-    enc_nanos(buffer, timeout.tv_nsec);
-  } else if (timeout.tv_sec < 1000 && timeout.tv_nsec != 0) {
-    enc_micros(buffer,
-               (int64_t)(timeout.tv_sec * 1000000) +
-                   (timeout.tv_nsec / 1000 + (timeout.tv_nsec % 1000 != 0)));
+  } else if (timeout < 1000 * GPR_MS_PER_SEC) {
+    enc_millis(buffer, timeout);
   } else {
-    enc_seconds(buffer, timeout.tv_sec + (timeout.tv_nsec != 0));
+    enc_seconds(buffer,
+                timeout / GPR_MS_PER_SEC + (timeout % GPR_MS_PER_SEC != 0));
   }
 }
 
@@ -121,8 +88,8 @@
   return p == end;
 }
 
-int grpc_http2_decode_timeout(grpc_slice text, gpr_timespec *timeout) {
-  int32_t x = 0;
+int grpc_http2_decode_timeout(grpc_slice text, grpc_millis *timeout) {
+  grpc_millis x = 0;
   const uint8_t *p = GRPC_SLICE_START_PTR(text);
   const uint8_t *end = GRPC_SLICE_END_PTR(text);
   int have_digit = 0;
@@ -136,7 +103,7 @@
     /* spec allows max. 8 digits, but we allow values up to 1,000,000,000 */
     if (x >= (100 * 1000 * 1000)) {
       if (x != (100 * 1000 * 1000) || digit != 0) {
-        *timeout = gpr_inf_future(GPR_TIMESPAN);
+        *timeout = GRPC_MILLIS_INF_FUTURE;
         return 1;
       }
     }
@@ -150,22 +117,22 @@
   /* decode unit specifier */
   switch (*p) {
     case 'n':
-      *timeout = gpr_time_from_nanos(x, GPR_TIMESPAN);
+      *timeout = x / GPR_NS_PER_MS + (x % GPR_NS_PER_MS != 0);
       break;
     case 'u':
-      *timeout = gpr_time_from_micros(x, GPR_TIMESPAN);
+      *timeout = x / GPR_US_PER_MS + (x % GPR_US_PER_MS != 0);
       break;
     case 'm':
-      *timeout = gpr_time_from_millis(x, GPR_TIMESPAN);
+      *timeout = x;
       break;
     case 'S':
-      *timeout = gpr_time_from_seconds(x, GPR_TIMESPAN);
+      *timeout = x * GPR_MS_PER_SEC;
       break;
     case 'M':
-      *timeout = gpr_time_from_minutes(x, GPR_TIMESPAN);
+      *timeout = x * 60 * GPR_MS_PER_SEC;
       break;
     case 'H':
-      *timeout = gpr_time_from_hours(x, GPR_TIMESPAN);
+      *timeout = x * 60 * 60 * GPR_MS_PER_SEC;
       break;
     default:
       return 0;
diff --git a/src/core/lib/transport/timeout_encoding.h b/src/core/lib/transport/timeout_encoding.h
index 7ff35c4..c867771 100644
--- a/src/core/lib/transport/timeout_encoding.h
+++ b/src/core/lib/transport/timeout_encoding.h
@@ -22,13 +22,14 @@
 #include <grpc/slice.h>
 #include <grpc/support/time.h>
 
+#include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/support/string.h"
 
 #define GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE (GPR_LTOA_MIN_BUFSIZE + 1)
 
 /* Encode/decode timeouts to the GRPC over HTTP/2 format;
    encoding may round up arbitrarily */
-void grpc_http2_encode_timeout(gpr_timespec timeout, char *buffer);
-int grpc_http2_decode_timeout(grpc_slice text, gpr_timespec *timeout);
+void grpc_http2_encode_timeout(grpc_millis timeout, char *buffer);
+int grpc_http2_decode_timeout(grpc_slice text, grpc_millis *timeout);
 
 #endif /* GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H */
diff --git a/src/core/lib/transport/transport_op_string.c b/src/core/lib/transport/transport_op_string.c
index 8586647..eec47b2 100644
--- a/src/core/lib/transport/transport_op_string.c
+++ b/src/core/lib/transport/transport_op_string.c
@@ -48,10 +48,9 @@
     if (m != md.list.head) gpr_strvec_add(b, gpr_strdup(", "));
     put_metadata(b, m->md);
   }
-  if (gpr_time_cmp(md.deadline, gpr_inf_future(md.deadline.clock_type)) != 0) {
+  if (md.deadline != GRPC_MILLIS_INF_FUTURE) {
     char *tmp;
-    gpr_asprintf(&tmp, " deadline=%" PRId64 ".%09d", md.deadline.tv_sec,
-                 md.deadline.tv_nsec);
+    gpr_asprintf(&tmp, " deadline=%" PRIdPTR, md.deadline);
     gpr_strvec_add(b, tmp);
   }
 }
diff --git a/src/cpp/client/generic_stub.cc b/src/cpp/client/generic_stub.cc
index de2e449..693b8be 100644
--- a/src/cpp/client/generic_stub.cc
+++ b/src/cpp/client/generic_stub.cc
@@ -47,4 +47,14 @@
   return CallInternal(channel_.get(), context, method, cq, false, nullptr);
 }
 
+// setup a unary call to a named method
+std::unique_ptr<GenericClientAsyncResponseReader> GenericStub::PrepareUnaryCall(
+    ClientContext* context, const grpc::string& method,
+    const ByteBuffer& request, CompletionQueue* cq) {
+  return std::unique_ptr<GenericClientAsyncResponseReader>(
+      GenericClientAsyncResponseReader::Create(
+          channel_.get(), cq, RpcMethod(method.c_str(), RpcMethod::NORMAL_RPC),
+          context, request, false));
+}
+
 }  // namespace grpc
diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc
index 057a058..13bbc30 100644
--- a/src/cpp/client/secure_credentials.cc
+++ b/src/cpp/client/secure_credentials.cc
@@ -21,6 +21,7 @@
 #include <grpc++/impl/grpc_library.h>
 #include <grpc++/support/channel_arguments.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 #include "src/cpp/client/create_channel_internal.h"
 #include "src/cpp/common/secure_auth_context.h"
 
@@ -150,6 +151,18 @@
   return nullptr;
 }
 
+std::shared_ptr<CallCredentials> CompositeCallCredentials(
+    const std::shared_ptr<CallCredentials>& creds1,
+    const std::shared_ptr<CallCredentials>& creds2) {
+  SecureCallCredentials* s_creds1 = creds1->AsSecureCredentials();
+  SecureCallCredentials* s_creds2 = creds2->AsSecureCredentials();
+  if (s_creds1 != nullptr && s_creds2 != nullptr) {
+    return WrapCallCredentials(grpc_composite_call_credentials_create(
+        s_creds1->GetRawCreds(), s_creds2->GetRawCreds(), nullptr));
+  }
+  return nullptr;
+}
+
 void MetadataCredentialsPluginWrapper::Destroy(void* wrapper) {
   if (wrapper == nullptr) return;
   MetadataCredentialsPluginWrapper* w =
@@ -157,28 +170,50 @@
   delete w;
 }
 
-void MetadataCredentialsPluginWrapper::GetMetadata(
+int MetadataCredentialsPluginWrapper::GetMetadata(
     void* wrapper, grpc_auth_metadata_context context,
-    grpc_credentials_plugin_metadata_cb cb, void* user_data) {
+    grpc_credentials_plugin_metadata_cb cb, void* user_data,
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+    size_t* num_creds_md, grpc_status_code* status,
+    const char** error_details) {
   GPR_ASSERT(wrapper);
   MetadataCredentialsPluginWrapper* w =
       reinterpret_cast<MetadataCredentialsPluginWrapper*>(wrapper);
   if (!w->plugin_) {
-    cb(user_data, NULL, 0, GRPC_STATUS_OK, NULL);
-    return;
+    *num_creds_md = 0;
+    *status = GRPC_STATUS_OK;
+    *error_details = nullptr;
+    return true;
   }
   if (w->plugin_->IsBlocking()) {
+    // Asynchronous return.
     w->thread_pool_->Add(
         std::bind(&MetadataCredentialsPluginWrapper::InvokePlugin, w, context,
-                  cb, user_data));
+                  cb, user_data, nullptr, nullptr, nullptr, nullptr));
+    return 0;
   } else {
-    w->InvokePlugin(context, cb, user_data);
+    // Synchronous return.
+    w->InvokePlugin(context, cb, user_data, creds_md, num_creds_md, status,
+                    error_details);
+    return 1;
   }
 }
 
+namespace {
+
+void UnrefMetadata(const std::vector<grpc_metadata>& md) {
+  for (auto it = md.begin(); it != md.end(); ++it) {
+    grpc_slice_unref(it->key);
+    grpc_slice_unref(it->value);
+  }
+}
+
+}  // namespace
+
 void MetadataCredentialsPluginWrapper::InvokePlugin(
     grpc_auth_metadata_context context, grpc_credentials_plugin_metadata_cb cb,
-    void* user_data) {
+    void* user_data, grpc_metadata creds_md[4], size_t* num_creds_md,
+    grpc_status_code* status_code, const char** error_details) {
   std::multimap<grpc::string, grpc::string> metadata;
 
   // const_cast is safe since the SecureAuthContext does not take owndership and
@@ -196,12 +231,31 @@
     md_entry.flags = 0;
     md.push_back(md_entry);
   }
-  cb(user_data, md.empty() ? nullptr : &md[0], md.size(),
-     static_cast<grpc_status_code>(status.error_code()),
-     status.error_message().c_str());
-  for (auto it = md.begin(); it != md.end(); ++it) {
-    grpc_slice_unref(it->key);
-    grpc_slice_unref(it->value);
+  if (creds_md != nullptr) {
+    // Synchronous return.
+    if (md.size() > GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX) {
+      *num_creds_md = 0;
+      *status_code = GRPC_STATUS_INTERNAL;
+      *error_details = gpr_strdup(
+          "blocking plugin credentials returned too many metadata keys");
+      UnrefMetadata(md);
+    } else {
+      for (const auto& elem : md) {
+        creds_md[*num_creds_md].key = elem.key;
+        creds_md[*num_creds_md].value = elem.value;
+        creds_md[*num_creds_md].flags = elem.flags;
+        ++(*num_creds_md);
+      }
+      *status_code = static_cast<grpc_status_code>(status.error_code());
+      *error_details =
+          status.ok() ? nullptr : gpr_strdup(status.error_message().c_str());
+    }
+  } else {
+    // Asynchronous return.
+    cb(user_data, md.empty() ? nullptr : &md[0], md.size(),
+       static_cast<grpc_status_code>(status.error_code()),
+       status.error_message().c_str());
+    UnrefMetadata(md);
   }
 }
 
diff --git a/src/cpp/client/secure_credentials.h b/src/cpp/client/secure_credentials.h
index 66547c7..ed9afb3 100644
--- a/src/cpp/client/secure_credentials.h
+++ b/src/cpp/client/secure_credentials.h
@@ -58,16 +58,23 @@
 class MetadataCredentialsPluginWrapper final : private GrpcLibraryCodegen {
  public:
   static void Destroy(void* wrapper);
-  static void GetMetadata(void* wrapper, grpc_auth_metadata_context context,
-                          grpc_credentials_plugin_metadata_cb cb,
-                          void* user_data);
+  static int GetMetadata(
+      void* wrapper, grpc_auth_metadata_context context,
+      grpc_credentials_plugin_metadata_cb cb, void* user_data,
+      grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+      size_t* num_creds_md, grpc_status_code* status,
+      const char** error_details);
 
   explicit MetadataCredentialsPluginWrapper(
       std::unique_ptr<MetadataCredentialsPlugin> plugin);
 
  private:
-  void InvokePlugin(grpc_auth_metadata_context context,
-                    grpc_credentials_plugin_metadata_cb cb, void* user_data);
+  void InvokePlugin(
+      grpc_auth_metadata_context context,
+      grpc_credentials_plugin_metadata_cb cb, void* user_data,
+      grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+      size_t* num_creds_md, grpc_status_code* status_code,
+      const char** error_details);
   std::unique_ptr<ThreadPoolInterface> thread_pool_;
   std::unique_ptr<MetadataCredentialsPlugin> plugin_;
 };
diff --git a/src/cpp/common/channel_arguments.cc b/src/cpp/common/channel_arguments.cc
index f130aec..f89f5f1 100644
--- a/src/cpp/common/channel_arguments.cc
+++ b/src/cpp/common/channel_arguments.cc
@@ -86,6 +86,10 @@
   SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, algorithm);
 }
 
+void ChannelArguments::SetGrpclbFallbackTimeout(int fallback_timeout) {
+  SetInt(GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS, fallback_timeout);
+}
+
 void ChannelArguments::SetSocketMutator(grpc_socket_mutator* mutator) {
   if (!mutator) {
     return;
diff --git a/src/cpp/server/health/default_health_check_service.cc b/src/cpp/server/health/default_health_check_service.cc
index 815b607..d2cba6d 100644
--- a/src/cpp/server/health/default_health_check_service.cc
+++ b/src/cpp/server/health/default_health_check_service.cc
@@ -20,6 +20,7 @@
 #include <mutex>
 
 #include <grpc++/impl/codegen/method_handler_impl.h>
+#include <grpc/slice.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 
diff --git a/src/cpp/util/byte_buffer_cc.cc b/src/cpp/util/byte_buffer_cc.cc
index b1ff252..180c813 100644
--- a/src/cpp/util/byte_buffer_cc.cc
+++ b/src/cpp/util/byte_buffer_cc.cc
@@ -16,11 +16,15 @@
  *
  */
 
+#include <grpc++/impl/grpc_library.h>
 #include <grpc++/support/byte_buffer.h>
+#include <grpc/byte_buffer.h>
 #include <grpc/byte_buffer_reader.h>
 
 namespace grpc {
 
+static internal::GrpcLibraryInitializer g_gli_initializer;
+
 ByteBuffer::ByteBuffer(const Slice* slices, size_t nslices) {
   // The following assertions check that the representation of a grpc::Slice is
   // identical to that of a grpc_slice:  it has a grpc_slice field, and nothing
@@ -29,6 +33,16 @@
                 "Slice must have same representation as grpc_slice");
   static_assert(sizeof(Slice) == sizeof(grpc_slice),
                 "Slice must have same representation as grpc_slice");
+  // The following assertions check that the representation of a ByteBuffer is
+  // identical to grpc_byte_buffer*:  it has a grpc_byte_buffer* field,
+  // and nothing else.
+  static_assert(std::is_same<decltype(buffer_), grpc_byte_buffer*>::value,
+                "ByteBuffer must have same representation as "
+                "grpc_byte_buffer*");
+  static_assert(sizeof(ByteBuffer) == sizeof(grpc_byte_buffer*),
+                "ByteBuffer must have same representation as "
+                "grpc_byte_buffer*");
+  g_gli_initializer.summon();  // Make sure that initializer linked in
   // The const_cast is legal if grpc_raw_byte_buffer_create() does no more
   // than its advertised side effect of increasing the reference count of the
   // slices it processes, and such an increase does not affect the semantics
@@ -37,19 +51,6 @@
       reinterpret_cast<grpc_slice*>(const_cast<Slice*>(slices)), nslices);
 }
 
-ByteBuffer::~ByteBuffer() {
-  if (buffer_) {
-    grpc_byte_buffer_destroy(buffer_);
-  }
-}
-
-void ByteBuffer::Clear() {
-  if (buffer_) {
-    grpc_byte_buffer_destroy(buffer_);
-    buffer_ = nullptr;
-  }
-}
-
 Status ByteBuffer::Dump(std::vector<Slice>* slices) const {
   slices->clear();
   if (!buffer_) {
@@ -80,7 +81,9 @@
     : buffer_(grpc_byte_buffer_copy(buf.buffer_)) {}
 
 ByteBuffer& ByteBuffer::operator=(const ByteBuffer& buf) {
-  Clear();  // first remove existing data
+  if (this != &buf) {
+    Clear();  // first remove existing data
+  }
   if (buf.buffer_) {
     buffer_ = grpc_byte_buffer_copy(buf.buffer_);  // then copy
   }
diff --git a/src/cpp/util/slice_cc.cc b/src/cpp/util/slice_cc.cc
index 486d0cd..3ae17e8 100644
--- a/src/cpp/util/slice_cc.cc
+++ b/src/cpp/util/slice_cc.cc
@@ -50,4 +50,6 @@
 Slice::Slice(void* buf, size_t len, void (*destroy)(void*, size_t))
     : slice_(grpc_slice_new_with_len(buf, len, destroy)) {}
 
+grpc_slice Slice::c_slice() const { return grpc_slice_ref(slice_); }
+
 }  // namespace grpc
diff --git a/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs
index b56bdbb..a8cb357 100644
--- a/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs
+++ b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs
@@ -61,12 +61,9 @@
 
             try
             {
-                var context = new AuthInterceptorContext(Marshal.PtrToStringAnsi(serviceUrlPtr),
-                                                         Marshal.PtrToStringAnsi(methodNamePtr));
-                // Don't await, we are in a native callback and need to return.
-                #pragma warning disable 4014
-                GetMetadataAsync(context, callbackPtr, userDataPtr);
-                #pragma warning restore 4014
+                var context = new AuthInterceptorContext(Marshal.PtrToStringAnsi(serviceUrlPtr), Marshal.PtrToStringAnsi(methodNamePtr));
+                // Make a guarantee that credentials_notify_from_plugin is invoked async to be compliant with c-core API.
+                ThreadPool.QueueUserWorkItem(async (stateInfo) => await GetMetadataAsync(context, callbackPtr, userDataPtr));
             }
             catch (Exception e)
             {
diff --git a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
index e81157c..eba6276 100644
--- a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
@@ -90,6 +90,54 @@
         }
 
         [Test]
+        public async Task MetadataCredentials_Composed()
+        {
+            var first = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
+                // Attempt to exercise the case where async callback is inlineable/synchronously-runnable.
+                metadata.Add("first_authorization", "FIRST_SECRET_TOKEN");
+                return TaskUtils.CompletedTask;
+            }));
+            var second = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
+                metadata.Add("second_authorization", "SECOND_SECRET_TOKEN");
+                return TaskUtils.CompletedTask;
+            }));
+            var third = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
+                metadata.Add("third_authorization", "THIRD_SECRET_TOKEN");
+                return TaskUtils.CompletedTask;
+            }));
+            var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(),
+                CallCredentials.Compose(first, second, third));
+            channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options);
+            var client = new TestService.TestServiceClient(channel);
+            var call = client.StreamingOutputCall(new StreamingOutputCallRequest { });
+            Assert.IsTrue(await call.ResponseStream.MoveNext());
+            Assert.IsFalse(await call.ResponseStream.MoveNext());
+        }
+
+        [Test]
+        public async Task MetadataCredentials_ComposedPerCall()
+        {
+            channel = new Channel(Host, server.Ports.Single().BoundPort, TestCredentials.CreateSslCredentials(), options);
+            var client = new TestService.TestServiceClient(channel);
+            var first = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
+                metadata.Add("first_authorization", "FIRST_SECRET_TOKEN");
+                return TaskUtils.CompletedTask;
+            }));
+            var second = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
+                metadata.Add("second_authorization", "SECOND_SECRET_TOKEN");
+                return TaskUtils.CompletedTask;
+            }));
+            var third = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => {
+                metadata.Add("third_authorization", "THIRD_SECRET_TOKEN");
+                return TaskUtils.CompletedTask;
+            }));
+            var call = client.StreamingOutputCall(new StreamingOutputCallRequest{ },
+                new CallOptions(credentials: CallCredentials.Compose(first, second, third)));
+            Assert.IsTrue(await call.ResponseStream.MoveNext());
+            Assert.IsFalse(await call.ResponseStream.MoveNext());
+        }
+
+        [Test]
         public void MetadataCredentials_InterceptorLeavesMetadataEmpty()
         {
             var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(),
@@ -125,6 +173,17 @@
                 Assert.AreEqual("SECRET_TOKEN", authToken);
                 return Task.FromResult(new SimpleResponse());
             }
+
+            public override async Task StreamingOutputCall(StreamingOutputCallRequest request, IServerStreamWriter<StreamingOutputCallResponse> responseStream, ServerCallContext context)
+            {
+                var first = context.RequestHeaders.First((entry) => entry.Key == "first_authorization").Value;
+                Assert.AreEqual("FIRST_SECRET_TOKEN", first);
+                var second = context.RequestHeaders.First((entry) => entry.Key == "second_authorization").Value;
+                Assert.AreEqual("SECOND_SECRET_TOKEN", second);
+                var third = context.RequestHeaders.First((entry) => entry.Key == "third_authorization").Value;
+                Assert.AreEqual("THIRD_SECRET_TOKEN", third);
+                await responseStream.WriteAsync(new StreamingOutputCallResponse());
+            }
         }
     }
 }
diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
index aebce36..92291f9 100644
--- a/src/csharp/ext/grpc_csharp_ext.c
+++ b/src/csharp/ext/grpc_csharp_ext.c
@@ -1023,13 +1023,17 @@
     grpc_credentials_plugin_metadata_cb cb, void *user_data,
     int32_t is_destroy);
 
-static void grpcsharp_get_metadata_handler(
+static int grpcsharp_get_metadata_handler(
     void *state, grpc_auth_metadata_context context,
-    grpc_credentials_plugin_metadata_cb cb, void *user_data) {
+    grpc_credentials_plugin_metadata_cb cb, void *user_data,
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+    size_t *num_creds_md, grpc_status_code *status,
+    const char **error_details) {
   grpcsharp_metadata_interceptor_func interceptor =
       (grpcsharp_metadata_interceptor_func)(intptr_t)state;
   interceptor(state, context.service_url, context.method_name, cb, user_data,
               0);
+  return 0; /* Asynchronous return. */
 }
 
 static void grpcsharp_metadata_credentials_destroy_handler(void *state) {
diff --git a/src/node/ext/call_credentials.cc b/src/node/ext/call_credentials.cc
index 4cf3e56..0644a81 100644
--- a/src/node/ext/call_credentials.cc
+++ b/src/node/ext/call_credentials.cc
@@ -238,9 +238,12 @@
   }
 }
 
-void plugin_get_metadata(void *state, grpc_auth_metadata_context context,
-                         grpc_credentials_plugin_metadata_cb cb,
-                         void *user_data) {
+int plugin_get_metadata(
+    void *state, grpc_auth_metadata_context context,
+    grpc_credentials_plugin_metadata_cb cb, void *user_data,
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+    size_t *num_creds_md, grpc_status_code *status,
+    const char **error_details) {
   plugin_state *p_state = reinterpret_cast<plugin_state *>(state);
   plugin_callback_data *data = new plugin_callback_data;
   data->service_url = context.service_url;
@@ -252,6 +255,7 @@
   uv_mutex_unlock(&p_state->plugin_mutex);
 
   uv_async_send(&p_state->plugin_async);
+  return 0;  // Async processing.
 }
 
 void plugin_uv_close_cb(uv_handle_t *handle) {
diff --git a/src/node/ext/call_credentials.h b/src/node/ext/call_credentials.h
index adcff84..3a54bbf 100644
--- a/src/node/ext/call_credentials.h
+++ b/src/node/ext/call_credentials.h
@@ -75,9 +75,11 @@
   uv_async_t plugin_async;
 } plugin_state;
 
-void plugin_get_metadata(void *state, grpc_auth_metadata_context context,
-                         grpc_credentials_plugin_metadata_cb cb,
-                         void *user_data);
+int plugin_get_metadata(
+    void *state, grpc_auth_metadata_context context,
+    grpc_credentials_plugin_metadata_cb cb, void *user_data,
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+    size_t *num_creds_md, grpc_status_code *status, const char **error_details);
 
 void plugin_destroy_state(void *state);
 
diff --git a/src/php/ext/grpc/call_credentials.c b/src/php/ext/grpc/call_credentials.c
index 1eee864..a395d53 100644
--- a/src/php/ext/grpc/call_credentials.c
+++ b/src/php/ext/grpc/call_credentials.c
@@ -35,6 +35,7 @@
 
 #include <grpc/grpc.h>
 #include <grpc/grpc_security.h>
+#include <grpc/support/string_util.h>
 
 zend_class_entry *grpc_ce_call_credentials;
 #if PHP_MAJOR_VERSION >= 7
@@ -143,9 +144,12 @@
 }
 
 /* Callback function for plugin creds API */
-void plugin_get_metadata(void *ptr, grpc_auth_metadata_context context,
-                         grpc_credentials_plugin_metadata_cb cb,
-                         void *user_data) {
+int plugin_get_metadata(
+    void *ptr, grpc_auth_metadata_context context,
+    grpc_credentials_plugin_metadata_cb cb, void *user_data,
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+    size_t *num_creds_md, grpc_status_code *status,
+    const char **error_details) {
   TSRMLS_FETCH();
 
   plugin_state *state = (plugin_state *)ptr;
@@ -175,15 +179,19 @@
   /* call the user callback function */
   zend_call_function(state->fci, state->fci_cache TSRMLS_CC);
 
-  grpc_status_code code = GRPC_STATUS_OK;
+  *num_creds_md = 0;
+  *status = GRPC_STATUS_OK;
+  *error_details = NULL;
+
   grpc_metadata_array metadata;
-  bool cleanup = true;
 
   if (retval == NULL || Z_TYPE_P(retval) != IS_ARRAY) {
-    cleanup = false;
-    code = GRPC_STATUS_INVALID_ARGUMENT;
-  } else if (!create_metadata_array(retval, &metadata)) {
-    code = GRPC_STATUS_INVALID_ARGUMENT;
+    *status = GRPC_STATUS_INVALID_ARGUMENT;
+    return true;  // Synchronous return.
+  }
+  if (!create_metadata_array(retval, &metadata)) {
+    *status = GRPC_STATUS_INVALID_ARGUMENT;
+    return true;  // Synchronous return.
   }
 
   if (retval != NULL) {
@@ -197,14 +205,24 @@
 #endif
   }
 
-  /* Pass control back to core */
-  cb(user_data, metadata.metadata, metadata.count, code, NULL);
-  if (cleanup) {
-    for (int i = 0; i < metadata.count; i++) {
+  if (metadata.count > GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX) {
+    *status = GRPC_STATUS_INTERNAL;
+    *error_details = gpr_strdup(
+        "PHP plugin credentials returned too many metadata entries");
+    for (size_t i = 0; i < metadata.count; i++) {
+      // TODO(stanleycheung): Why don't we need to unref the key here?
       grpc_slice_unref(metadata.metadata[i].value);
     }
-    grpc_metadata_array_destroy(&metadata);
+  } else {
+    // Return data to core.
+    *num_creds_md = metadata.count;
+    for (size_t i = 0; i < metadata.count; ++i) {
+      creds_md[i] = metadata.metadata[i];
+    }
   }
+
+  grpc_metadata_array_destroy(&metadata);
+  return true;  // Synchronous return.
 }
 
 /* Cleanup function for plugin creds API */
diff --git a/src/php/ext/grpc/call_credentials.h b/src/php/ext/grpc/call_credentials.h
index 9be8763..663cc68 100755
--- a/src/php/ext/grpc/call_credentials.h
+++ b/src/php/ext/grpc/call_credentials.h
@@ -65,9 +65,12 @@
 } plugin_state;
 
 /* Callback function for plugin creds API */
-void plugin_get_metadata(void *state, grpc_auth_metadata_context context,
-                         grpc_credentials_plugin_metadata_cb cb,
-                         void *user_data);
+int plugin_get_metadata(
+  void *ptr, grpc_auth_metadata_context context,
+  grpc_credentials_plugin_metadata_cb cb, void *user_data,
+  grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+  size_t *num_creds_md, grpc_status_code *status,
+  const char **error_details);
 
 /* Cleanup function for plugin creds API */
 void plugin_destroy_state(void *ptr);
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi
index a0e69dd..41975cb 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd.pxi
@@ -41,7 +41,8 @@
   cdef object plugin_callback
   cdef bytes plugin_name
 
-  cdef grpc_metadata_credentials_plugin make_c_plugin(self)
+
+cdef grpc_metadata_credentials_plugin _c_plugin(CredentialsMetadataPlugin plugin)
 
 
 cdef class AuthMetadataContext:
@@ -49,8 +50,11 @@
   cdef grpc_auth_metadata_context context
 
 
-cdef void plugin_get_metadata(
+cdef int plugin_get_metadata(
     void *state, grpc_auth_metadata_context context,
-    grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil
+    grpc_credentials_plugin_metadata_cb cb, void *user_data,
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+    size_t *num_creds_md, grpc_status_code *status,
+    const char **error_details) with gil
 
 cdef void plugin_destroy_c_plugin_state(void *state) with gil
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
index 57816f1..0fabda1 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx.pxi
@@ -14,6 +14,7 @@
 
 cimport cpython
 
+import threading
 import traceback
 
 
@@ -89,20 +90,20 @@
     self.plugin_callback = plugin_callback
     self.plugin_name = name
 
-  @staticmethod
-  cdef grpc_metadata_credentials_plugin make_c_plugin(self):
-    cdef grpc_metadata_credentials_plugin result
-    result.get_metadata = plugin_get_metadata
-    result.destroy = plugin_destroy_c_plugin_state
-    result.state = <void *>self
-    result.type = self.plugin_name
-    cpython.Py_INCREF(self)
-    return result
-
   def __dealloc__(self):
     grpc_shutdown()
 
 
+cdef grpc_metadata_credentials_plugin _c_plugin(CredentialsMetadataPlugin plugin):
+  cdef grpc_metadata_credentials_plugin c_plugin
+  c_plugin.get_metadata = plugin_get_metadata
+  c_plugin.destroy = plugin_destroy_c_plugin_state
+  c_plugin.state = <void *>plugin
+  c_plugin.type = plugin.plugin_name
+  cpython.Py_INCREF(plugin)
+  return c_plugin
+
+
 cdef class AuthMetadataContext:
 
   def __cinit__(self):
@@ -122,9 +123,12 @@
     grpc_shutdown()
 
 
-cdef void plugin_get_metadata(
+cdef int plugin_get_metadata(
     void *state, grpc_auth_metadata_context context,
-    grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil:
+    grpc_credentials_plugin_metadata_cb cb, void *user_data,
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+    size_t *num_creds_md, grpc_status_code *status,
+    const char **error_details) with gil:
   called_flag = [False]
   def python_callback(
       Metadata metadata, grpc_status_code status,
@@ -134,12 +138,15 @@
   cdef CredentialsMetadataPlugin self = <CredentialsMetadataPlugin>state
   cdef AuthMetadataContext cy_context = AuthMetadataContext()
   cy_context.context = context
-  try:
-    self.plugin_callback(cy_context, python_callback)
-  except Exception as error:
-    if not called_flag[0]:
-      cb(user_data, NULL, 0, StatusCode.unknown,
-         traceback.format_exc().encode())
+  def async_callback():
+    try:
+      self.plugin_callback(cy_context, python_callback)
+    except Exception as error:
+      if not called_flag[0]:
+        cb(user_data, NULL, 0, StatusCode.unknown,
+           traceback.format_exc().encode())
+  threading.Thread(group=None, target=async_callback).start()
+  return 0  # Asynchronous return
 
 cdef void plugin_destroy_c_plugin_state(void *state) with gil:
   cpython.Py_DECREF(<CredentialsMetadataPlugin>state)
@@ -239,7 +246,7 @@
 
 def call_credentials_metadata_plugin(CredentialsMetadataPlugin plugin):
   cdef CallCredentials credentials = CallCredentials()
-  cdef grpc_metadata_credentials_plugin c_plugin = plugin.make_c_plugin()
+  cdef grpc_metadata_credentials_plugin c_plugin = _c_plugin(plugin)
   with nogil:
     credentials.c_credentials = (
         grpc_metadata_credentials_create_from_plugin(c_plugin, NULL))
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
index 840af5c..f115106 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi
@@ -375,6 +375,10 @@
 
 cdef extern from "grpc/grpc_security.h":
 
+  # Declare this as an enum, this is the only way to make it a const in
+  # cython
+  enum: GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX
+
   ctypedef enum grpc_ssl_roots_override_result:
     GRPC_SSL_ROOTS_OVERRIDE_OK
     GRPC_SSL_ROOTS_OVERRIDE_FAILED_PERMANENTLY
@@ -462,9 +466,12 @@
       grpc_status_code status, const char *error_details)
 
   ctypedef struct grpc_metadata_credentials_plugin:
-    void (*get_metadata)(
+    int (*get_metadata)(
         void *state, grpc_auth_metadata_context context,
-        grpc_credentials_plugin_metadata_cb cb, void *user_data)
+        grpc_credentials_plugin_metadata_cb cb, void *user_data,
+        grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+        size_t *num_creds_md, grpc_status_code *status,
+        const char **error_details)
     void (*destroy)(void *state)
     void *state
     const char *type
diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
index d860173..4f87261 100644
--- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
+++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx.pxi
@@ -171,14 +171,6 @@
         gpr_convert_clock_type(self.c_time, GPR_CLOCK_REALTIME))
     return <double>real_time.seconds + <double>real_time.nanoseconds / 1e9
 
-  @staticmethod
-  def infinite_future():
-    return Timespec(float("+inf"))
-
-  @staticmethod
-  def infinite_past():
-    return Timespec(float("-inf"))
-
   def __richcmp__(Timespec self not None, Timespec other not None, int op):
     cdef gpr_timespec self_c_time = self.c_time
     cdef gpr_timespec other_c_time = other.c_time
@@ -454,7 +446,7 @@
       self.i = self.i + 1
       return result
     else:
-      raise StopIteration
+      raise StopIteration()
 
 
 # TODO(https://github.com/grpc/grpc/issues/7950): Eliminate this; just use an
@@ -518,7 +510,7 @@
 
   def __getitem__(self, size_t i):
     if i >= self.c_metadata_array.count:
-      raise IndexError
+      raise IndexError()
     key = _slice_bytes(self.c_metadata_array.metadata[i].key)
     value = _slice_bytes(self.c_metadata_array.metadata[i].value)
     return Metadatum(key=key, value=value)
@@ -720,7 +712,7 @@
       self.i = self.i + 1
       return result
     else:
-      raise StopIteration
+      raise StopIteration()
 
 
 cdef class Operations:
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index ec642b0..8795e59 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -21,7 +21,6 @@
   'src/core/lib/support/arena.c',
   'src/core/lib/support/atm.c',
   'src/core/lib/support/avl.c',
-  'src/core/lib/support/backoff.c',
   'src/core/lib/support/cmdline.c',
   'src/core/lib/support/cpu_iphone.c',
   'src/core/lib/support/cpu_linux.c',
@@ -62,6 +61,7 @@
   'src/core/lib/support/tmpfile_windows.c',
   'src/core/lib/support/wrap_memcpy.c',
   'src/core/lib/surface/init.c',
+  'src/core/lib/backoff/backoff.c',
   'src/core/lib/channel/channel_args.c',
   'src/core/lib/channel/channel_stack.c',
   'src/core/lib/channel/channel_stack_builder.c',
@@ -72,6 +72,8 @@
   'src/core/lib/compression/compression.c',
   'src/core/lib/compression/message_compress.c',
   'src/core/lib/compression/stream_compression.c',
+  'src/core/lib/compression/stream_compression_gzip.c',
+  'src/core/lib/compression/stream_compression_identity.c',
   'src/core/lib/debug/stats.c',
   'src/core/lib/debug/stats_data.c',
   'src/core/lib/http/format_request.c',
diff --git a/src/python/grpcio/support.py b/src/python/grpcio/support.py
index 510bf42..f2395eb 100644
--- a/src/python/grpcio/support.py
+++ b/src/python/grpcio/support.py
@@ -94,7 +94,7 @@
 
 _ERROR_DIAGNOSES = {
     errors.CompileError: diagnose_compile_error,
-    AttributeError: diagnose_attribute_error
+    AttributeError: diagnose_attribute_error,
 }
 
 
@@ -102,8 +102,10 @@
     diagnostic = _ERROR_DIAGNOSES.get(type(error))
     if diagnostic is None:
         raise commands.CommandError(
-            "\n\nWe could not diagnose your build failure. Please file an issue at "
-            "http://www.github.com/grpc/grpc with `[Python install]` in the title."
-            "\n\n{}".format(formatted))
+            "\n\nWe could not diagnose your build failure. If you are unable to "
+            "proceed, please file an issue at http://www.github.com/grpc/grpc "
+            "with `[Python install]` in the title; please attach the whole log "
+            "(including everything that may have appeared above the Python "
+            "backtrace).\n\n{}".format(formatted))
     else:
         diagnostic(build_ext, error)
diff --git a/src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py b/src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py
index 9f72b1f..6faab94 100644
--- a/src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py
+++ b/src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py
@@ -29,7 +29,7 @@
 _REQUEST_SERIALIZER = lambda unused_request: _SERIALIZED_REQUEST
 _REQUEST_DESERIALIZER = lambda unused_serialized_request: object()
 _RESPONSE_SERIALIZER = lambda unused_response: _SERIALIZED_RESPONSE
-_RESPONSE_DESERIALIZER = lambda unused_serialized_resopnse: object()
+_RESPONSE_DESERIALIZER = lambda unused_serialized_response: object()
 
 _SERVICE = 'test.TestService'
 _UNARY_UNARY = 'UnaryUnary'
diff --git a/src/ruby/end2end/killed_client_thread_client.rb b/src/ruby/end2end/killed_client_thread_client.rb
index 7d6ed8c..493c0eb 100755
--- a/src/ruby/end2end/killed_client_thread_client.rb
+++ b/src/ruby/end2end/killed_client_thread_client.rb
@@ -35,7 +35,7 @@
                                       :this_channel_is_insecure)
     stub.echo(Echo::EchoRequest.new(request: 'hello'))
     fail 'the clients rpc in this test shouldnt complete. ' \
-      'expecting SIGINT to happen in the middle of the call'
+      'expecting SIGTERM to happen in the middle of the call'
   end
   thd.join
 end
diff --git a/src/ruby/end2end/killed_client_thread_driver.rb b/src/ruby/end2end/killed_client_thread_driver.rb
index 09f05a4..fce5d13 100755
--- a/src/ruby/end2end/killed_client_thread_driver.rb
+++ b/src/ruby/end2end/killed_client_thread_driver.rb
@@ -69,9 +69,9 @@
     call_started_cv.wait(call_started_mu) until call_started.val
   end
 
-  # SIGINT the child process now that it's
+  # SIGTERM the child process now that it's
   # in the middle of an RPC (happening on a non-main thread)
-  Process.kill('SIGINT', client_pid)
+  Process.kill('SIGTERM', client_pid)
   STDERR.puts 'sent shutdown'
 
   begin
@@ -88,8 +88,8 @@
   end
 
   client_exit_code = $CHILD_STATUS
-  if client_exit_code.termsig != 2 # SIGINT
-    fail 'expected client exit from SIGINT ' \
+  if client_exit_code.termsig != 15 # SIGTERM
+    fail 'expected client exit from SIGTERM ' \
       "but got child status: #{client_exit_code}"
   end
 
diff --git a/src/ruby/ext/grpc/rb_call_credentials.c b/src/ruby/ext/grpc/rb_call_credentials.c
index 049a869..4214a08 100644
--- a/src/ruby/ext/grpc/rb_call_credentials.c
+++ b/src/ruby/ext/grpc/rb_call_credentials.c
@@ -112,9 +112,12 @@
   gpr_free(params);
 }
 
-static void grpc_rb_call_credentials_plugin_get_metadata(
+static int grpc_rb_call_credentials_plugin_get_metadata(
     void *state, grpc_auth_metadata_context context,
-    grpc_credentials_plugin_metadata_cb cb, void *user_data) {
+    grpc_credentials_plugin_metadata_cb cb, void *user_data,
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+    size_t *num_creds_md, grpc_status_code *status,
+    const char **error_details) {
   callback_params *params = gpr_malloc(sizeof(callback_params));
   params->get_metadata = (VALUE)state;
   params->context = context;
@@ -123,6 +126,7 @@
 
   grpc_rb_event_queue_enqueue(grpc_rb_call_credentials_callback_with_gil,
                               (void *)(params));
+  return 0;  // Async return.
 }
 
 static void grpc_rb_call_credentials_plugin_destroy(void *state) {
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index 57b5439..7083149 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -22,16 +22,6 @@
 
 #include "rb_grpc_imports.generated.h"
 
-grpc_raw_byte_buffer_create_type grpc_raw_byte_buffer_create_import;
-grpc_raw_compressed_byte_buffer_create_type grpc_raw_compressed_byte_buffer_create_import;
-grpc_byte_buffer_copy_type grpc_byte_buffer_copy_import;
-grpc_byte_buffer_length_type grpc_byte_buffer_length_import;
-grpc_byte_buffer_destroy_type grpc_byte_buffer_destroy_import;
-grpc_byte_buffer_reader_init_type grpc_byte_buffer_reader_init_import;
-grpc_byte_buffer_reader_destroy_type grpc_byte_buffer_reader_destroy_import;
-grpc_byte_buffer_reader_next_type grpc_byte_buffer_reader_next_import;
-grpc_byte_buffer_reader_readall_type grpc_byte_buffer_reader_readall_import;
-grpc_raw_byte_buffer_from_reader_type grpc_raw_byte_buffer_from_reader_import;
 census_initialize_type census_initialize_import;
 census_shutdown_type census_shutdown_import;
 census_supported_type census_supported_import;
@@ -168,6 +158,16 @@
 grpc_server_add_secure_http2_port_type grpc_server_add_secure_http2_port_import;
 grpc_call_set_credentials_type grpc_call_set_credentials_import;
 grpc_server_credentials_set_auth_metadata_processor_type grpc_server_credentials_set_auth_metadata_processor_import;
+grpc_raw_byte_buffer_create_type grpc_raw_byte_buffer_create_import;
+grpc_raw_compressed_byte_buffer_create_type grpc_raw_compressed_byte_buffer_create_import;
+grpc_byte_buffer_copy_type grpc_byte_buffer_copy_import;
+grpc_byte_buffer_length_type grpc_byte_buffer_length_import;
+grpc_byte_buffer_destroy_type grpc_byte_buffer_destroy_import;
+grpc_byte_buffer_reader_init_type grpc_byte_buffer_reader_init_import;
+grpc_byte_buffer_reader_destroy_type grpc_byte_buffer_reader_destroy_import;
+grpc_byte_buffer_reader_next_type grpc_byte_buffer_reader_next_import;
+grpc_byte_buffer_reader_readall_type grpc_byte_buffer_reader_readall_import;
+grpc_raw_byte_buffer_from_reader_type grpc_raw_byte_buffer_from_reader_import;
 grpc_slice_ref_type grpc_slice_ref_import;
 grpc_slice_unref_type grpc_slice_unref_import;
 grpc_slice_copy_type grpc_slice_copy_import;
@@ -330,16 +330,6 @@
 gpr_timespec_to_micros_type gpr_timespec_to_micros_import;
 
 void grpc_rb_load_imports(HMODULE library) {
-  grpc_raw_byte_buffer_create_import = (grpc_raw_byte_buffer_create_type) GetProcAddress(library, "grpc_raw_byte_buffer_create");
-  grpc_raw_compressed_byte_buffer_create_import = (grpc_raw_compressed_byte_buffer_create_type) GetProcAddress(library, "grpc_raw_compressed_byte_buffer_create");
-  grpc_byte_buffer_copy_import = (grpc_byte_buffer_copy_type) GetProcAddress(library, "grpc_byte_buffer_copy");
-  grpc_byte_buffer_length_import = (grpc_byte_buffer_length_type) GetProcAddress(library, "grpc_byte_buffer_length");
-  grpc_byte_buffer_destroy_import = (grpc_byte_buffer_destroy_type) GetProcAddress(library, "grpc_byte_buffer_destroy");
-  grpc_byte_buffer_reader_init_import = (grpc_byte_buffer_reader_init_type) GetProcAddress(library, "grpc_byte_buffer_reader_init");
-  grpc_byte_buffer_reader_destroy_import = (grpc_byte_buffer_reader_destroy_type) GetProcAddress(library, "grpc_byte_buffer_reader_destroy");
-  grpc_byte_buffer_reader_next_import = (grpc_byte_buffer_reader_next_type) GetProcAddress(library, "grpc_byte_buffer_reader_next");
-  grpc_byte_buffer_reader_readall_import = (grpc_byte_buffer_reader_readall_type) GetProcAddress(library, "grpc_byte_buffer_reader_readall");
-  grpc_raw_byte_buffer_from_reader_import = (grpc_raw_byte_buffer_from_reader_type) GetProcAddress(library, "grpc_raw_byte_buffer_from_reader");
   census_initialize_import = (census_initialize_type) GetProcAddress(library, "census_initialize");
   census_shutdown_import = (census_shutdown_type) GetProcAddress(library, "census_shutdown");
   census_supported_import = (census_supported_type) GetProcAddress(library, "census_supported");
@@ -476,6 +466,16 @@
   grpc_server_add_secure_http2_port_import = (grpc_server_add_secure_http2_port_type) GetProcAddress(library, "grpc_server_add_secure_http2_port");
   grpc_call_set_credentials_import = (grpc_call_set_credentials_type) GetProcAddress(library, "grpc_call_set_credentials");
   grpc_server_credentials_set_auth_metadata_processor_import = (grpc_server_credentials_set_auth_metadata_processor_type) GetProcAddress(library, "grpc_server_credentials_set_auth_metadata_processor");
+  grpc_raw_byte_buffer_create_import = (grpc_raw_byte_buffer_create_type) GetProcAddress(library, "grpc_raw_byte_buffer_create");
+  grpc_raw_compressed_byte_buffer_create_import = (grpc_raw_compressed_byte_buffer_create_type) GetProcAddress(library, "grpc_raw_compressed_byte_buffer_create");
+  grpc_byte_buffer_copy_import = (grpc_byte_buffer_copy_type) GetProcAddress(library, "grpc_byte_buffer_copy");
+  grpc_byte_buffer_length_import = (grpc_byte_buffer_length_type) GetProcAddress(library, "grpc_byte_buffer_length");
+  grpc_byte_buffer_destroy_import = (grpc_byte_buffer_destroy_type) GetProcAddress(library, "grpc_byte_buffer_destroy");
+  grpc_byte_buffer_reader_init_import = (grpc_byte_buffer_reader_init_type) GetProcAddress(library, "grpc_byte_buffer_reader_init");
+  grpc_byte_buffer_reader_destroy_import = (grpc_byte_buffer_reader_destroy_type) GetProcAddress(library, "grpc_byte_buffer_reader_destroy");
+  grpc_byte_buffer_reader_next_import = (grpc_byte_buffer_reader_next_type) GetProcAddress(library, "grpc_byte_buffer_reader_next");
+  grpc_byte_buffer_reader_readall_import = (grpc_byte_buffer_reader_readall_type) GetProcAddress(library, "grpc_byte_buffer_reader_readall");
+  grpc_raw_byte_buffer_from_reader_import = (grpc_raw_byte_buffer_from_reader_type) GetProcAddress(library, "grpc_raw_byte_buffer_from_reader");
   grpc_slice_ref_import = (grpc_slice_ref_type) GetProcAddress(library, "grpc_slice_ref");
   grpc_slice_unref_import = (grpc_slice_unref_type) GetProcAddress(library, "grpc_slice_unref");
   grpc_slice_copy_import = (grpc_slice_copy_type) GetProcAddress(library, "grpc_slice_copy");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index 55a7eed..868772c 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -25,12 +25,12 @@
 
 #include <windows.h>
 
-#include <grpc/byte_buffer.h>
 #include <grpc/census.h>
 #include <grpc/compression.h>
 #include <grpc/grpc.h>
 #include <grpc/grpc_posix.h>
 #include <grpc/grpc_security.h>
+#include <grpc/impl/codegen/byte_buffer.h>
 #include <grpc/slice.h>
 #include <grpc/slice_buffer.h>
 #include <grpc/support/alloc.h>
@@ -47,36 +47,6 @@
 #include <grpc/support/thd.h>
 #include <grpc/support/time.h>
 
-typedef grpc_byte_buffer *(*grpc_raw_byte_buffer_create_type)(grpc_slice *slices, size_t nslices);
-extern grpc_raw_byte_buffer_create_type grpc_raw_byte_buffer_create_import;
-#define grpc_raw_byte_buffer_create grpc_raw_byte_buffer_create_import
-typedef grpc_byte_buffer *(*grpc_raw_compressed_byte_buffer_create_type)(grpc_slice *slices, size_t nslices, grpc_compression_algorithm compression);
-extern grpc_raw_compressed_byte_buffer_create_type grpc_raw_compressed_byte_buffer_create_import;
-#define grpc_raw_compressed_byte_buffer_create grpc_raw_compressed_byte_buffer_create_import
-typedef grpc_byte_buffer *(*grpc_byte_buffer_copy_type)(grpc_byte_buffer *bb);
-extern grpc_byte_buffer_copy_type grpc_byte_buffer_copy_import;
-#define grpc_byte_buffer_copy grpc_byte_buffer_copy_import
-typedef size_t(*grpc_byte_buffer_length_type)(grpc_byte_buffer *bb);
-extern grpc_byte_buffer_length_type grpc_byte_buffer_length_import;
-#define grpc_byte_buffer_length grpc_byte_buffer_length_import
-typedef void(*grpc_byte_buffer_destroy_type)(grpc_byte_buffer *byte_buffer);
-extern grpc_byte_buffer_destroy_type grpc_byte_buffer_destroy_import;
-#define grpc_byte_buffer_destroy grpc_byte_buffer_destroy_import
-typedef int(*grpc_byte_buffer_reader_init_type)(grpc_byte_buffer_reader *reader, grpc_byte_buffer *buffer);
-extern grpc_byte_buffer_reader_init_type grpc_byte_buffer_reader_init_import;
-#define grpc_byte_buffer_reader_init grpc_byte_buffer_reader_init_import
-typedef void(*grpc_byte_buffer_reader_destroy_type)(grpc_byte_buffer_reader *reader);
-extern grpc_byte_buffer_reader_destroy_type grpc_byte_buffer_reader_destroy_import;
-#define grpc_byte_buffer_reader_destroy grpc_byte_buffer_reader_destroy_import
-typedef int(*grpc_byte_buffer_reader_next_type)(grpc_byte_buffer_reader *reader, grpc_slice *slice);
-extern grpc_byte_buffer_reader_next_type grpc_byte_buffer_reader_next_import;
-#define grpc_byte_buffer_reader_next grpc_byte_buffer_reader_next_import
-typedef grpc_slice(*grpc_byte_buffer_reader_readall_type)(grpc_byte_buffer_reader *reader);
-extern grpc_byte_buffer_reader_readall_type grpc_byte_buffer_reader_readall_import;
-#define grpc_byte_buffer_reader_readall grpc_byte_buffer_reader_readall_import
-typedef grpc_byte_buffer *(*grpc_raw_byte_buffer_from_reader_type)(grpc_byte_buffer_reader *reader);
-extern grpc_raw_byte_buffer_from_reader_type grpc_raw_byte_buffer_from_reader_import;
-#define grpc_raw_byte_buffer_from_reader grpc_raw_byte_buffer_from_reader_import
 typedef int(*census_initialize_type)(int features);
 extern census_initialize_type census_initialize_import;
 #define census_initialize census_initialize_import
@@ -485,6 +455,36 @@
 typedef void(*grpc_server_credentials_set_auth_metadata_processor_type)(grpc_server_credentials *creds, grpc_auth_metadata_processor processor);
 extern grpc_server_credentials_set_auth_metadata_processor_type grpc_server_credentials_set_auth_metadata_processor_import;
 #define grpc_server_credentials_set_auth_metadata_processor grpc_server_credentials_set_auth_metadata_processor_import
+typedef grpc_byte_buffer *(*grpc_raw_byte_buffer_create_type)(grpc_slice *slices, size_t nslices);
+extern grpc_raw_byte_buffer_create_type grpc_raw_byte_buffer_create_import;
+#define grpc_raw_byte_buffer_create grpc_raw_byte_buffer_create_import
+typedef grpc_byte_buffer *(*grpc_raw_compressed_byte_buffer_create_type)(grpc_slice *slices, size_t nslices, grpc_compression_algorithm compression);
+extern grpc_raw_compressed_byte_buffer_create_type grpc_raw_compressed_byte_buffer_create_import;
+#define grpc_raw_compressed_byte_buffer_create grpc_raw_compressed_byte_buffer_create_import
+typedef grpc_byte_buffer *(*grpc_byte_buffer_copy_type)(grpc_byte_buffer *bb);
+extern grpc_byte_buffer_copy_type grpc_byte_buffer_copy_import;
+#define grpc_byte_buffer_copy grpc_byte_buffer_copy_import
+typedef size_t(*grpc_byte_buffer_length_type)(grpc_byte_buffer *bb);
+extern grpc_byte_buffer_length_type grpc_byte_buffer_length_import;
+#define grpc_byte_buffer_length grpc_byte_buffer_length_import
+typedef void(*grpc_byte_buffer_destroy_type)(grpc_byte_buffer *byte_buffer);
+extern grpc_byte_buffer_destroy_type grpc_byte_buffer_destroy_import;
+#define grpc_byte_buffer_destroy grpc_byte_buffer_destroy_import
+typedef int(*grpc_byte_buffer_reader_init_type)(grpc_byte_buffer_reader *reader, grpc_byte_buffer *buffer);
+extern grpc_byte_buffer_reader_init_type grpc_byte_buffer_reader_init_import;
+#define grpc_byte_buffer_reader_init grpc_byte_buffer_reader_init_import
+typedef void(*grpc_byte_buffer_reader_destroy_type)(grpc_byte_buffer_reader *reader);
+extern grpc_byte_buffer_reader_destroy_type grpc_byte_buffer_reader_destroy_import;
+#define grpc_byte_buffer_reader_destroy grpc_byte_buffer_reader_destroy_import
+typedef int(*grpc_byte_buffer_reader_next_type)(grpc_byte_buffer_reader *reader, grpc_slice *slice);
+extern grpc_byte_buffer_reader_next_type grpc_byte_buffer_reader_next_import;
+#define grpc_byte_buffer_reader_next grpc_byte_buffer_reader_next_import
+typedef grpc_slice(*grpc_byte_buffer_reader_readall_type)(grpc_byte_buffer_reader *reader);
+extern grpc_byte_buffer_reader_readall_type grpc_byte_buffer_reader_readall_import;
+#define grpc_byte_buffer_reader_readall grpc_byte_buffer_reader_readall_import
+typedef grpc_byte_buffer *(*grpc_raw_byte_buffer_from_reader_type)(grpc_byte_buffer_reader *reader);
+extern grpc_raw_byte_buffer_from_reader_type grpc_raw_byte_buffer_from_reader_import;
+#define grpc_raw_byte_buffer_from_reader grpc_raw_byte_buffer_from_reader_import
 typedef grpc_slice(*grpc_slice_ref_type)(grpc_slice s);
 extern grpc_slice_ref_type grpc_slice_ref_import;
 #define grpc_slice_ref grpc_slice_ref_import
diff --git a/src/ruby/lib/grpc.rb b/src/ruby/lib/grpc.rb
index 98bfc0a..37b0392 100644
--- a/src/ruby/lib/grpc.rb
+++ b/src/ruby/lib/grpc.rb
@@ -24,6 +24,7 @@
 require_relative 'grpc/generic/client_stub'
 require_relative 'grpc/generic/service'
 require_relative 'grpc/generic/rpc_server'
+require_relative 'grpc/generic/interceptors'
 
 begin
   file = File.open(ssl_roots_path)
diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb
index 10eb70b..8c3aa28 100644
--- a/src/ruby/lib/grpc/generic/active_call.rb
+++ b/src/ruby/lib/grpc/generic/active_call.rb
@@ -154,6 +154,15 @@
       Operation.new(self)
     end
 
+    ##
+    # Returns a restricted view of this ActiveCall for use in interceptors
+    #
+    # @return [InterceptableView]
+    #
+    def interceptable
+      InterceptableView.new(self)
+    end
+
     def receive_and_check_status
       batch_result = @call.run_batch(RECV_STATUS_ON_CLIENT => nil)
       set_input_stream_done
@@ -515,15 +524,27 @@
     # This does not mean that must necessarily be one.  E.g, the replies
     # produced by gen_each_reply could ignore the received_msgs
     #
-    # @param gen_each_reply [Proc] generates the BiDi stream replies
-    def run_server_bidi(gen_each_reply)
-      bd = BidiCall.new(@call,
-                        @marshal,
-                        @unmarshal,
-                        metadata_received: @metadata_received,
-                        req_view: MultiReqView.new(self))
-
-      bd.run_on_server(gen_each_reply, proc { set_input_stream_done })
+    # @param mth [Proc] generates the BiDi stream replies
+    # @param interception_ctx [InterceptionContext]
+    #
+    def run_server_bidi(mth, interception_ctx)
+      view = multi_req_view
+      bidi_call = BidiCall.new(
+        @call,
+        @marshal,
+        @unmarshal,
+        metadata_received: @metadata_received,
+        req_view: view
+      )
+      requests = bidi_call.read_next_loop(proc { set_input_stream_done }, false)
+      interception_ctx.intercept!(
+        :bidi_streamer,
+        call: view,
+        method: mth,
+        requests: requests
+      ) do
+        bidi_call.run_on_server(mth, requests)
+      end
     end
 
     # Waits till an operation completes
@@ -645,5 +666,9 @@
     Operation = view_class(:cancel, :cancelled?, :deadline, :execute,
                            :metadata, :status, :start_call, :wait, :write_flag,
                            :write_flag=, :trailing_metadata)
+
+    # InterceptableView further limits access to an ActiveCall's methods
+    # for use in interceptors on the client, exposing only the deadline
+    InterceptableView = view_class(:deadline)
   end
 end
diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb
index c2239d0..3bdcc00 100644
--- a/src/ruby/lib/grpc/generic/bidi_call.rb
+++ b/src/ruby/lib/grpc/generic/bidi_call.rb
@@ -87,23 +87,32 @@
     # This does not mean that must necessarily be one.  E.g, the replies
     # produced by gen_each_reply could ignore the received_msgs
     #
-    # @param gen_each_reply [Proc] generates the BiDi stream replies.
-    # @param set_input_steam_done [Proc] call back to call when
-    #   the reads have been completely read through.
-    def run_on_server(gen_each_reply, set_input_stream_done)
+    # @param [Proc] gen_each_reply generates the BiDi stream replies.
+    # @param [Enumerable] requests The enumerable of requests to run
+    def run_on_server(gen_each_reply, requests)
+      replies = nil
+
       # Pass in the optional call object parameter if possible
       if gen_each_reply.arity == 1
-        replys = gen_each_reply.call(
-          read_loop(set_input_stream_done, is_client: false))
+        replies = gen_each_reply.call(requests)
       elsif gen_each_reply.arity == 2
-        replys = gen_each_reply.call(
-          read_loop(set_input_stream_done, is_client: false),
-          @req_view)
+        replies = gen_each_reply.call(requests, @req_view)
       else
         fail 'Illegal arity of reply generator'
       end
 
-      write_loop(replys, is_client: false)
+      write_loop(replies, is_client: false)
+    end
+
+    ##
+    # Read the next stream iteration
+    #
+    # @param [Proc] finalize_stream callback to call when the reads have been
+    #   completely read through.
+    # @param [Boolean] is_client If this is a client or server request
+    #
+    def read_next_loop(finalize_stream, is_client = false)
+      read_loop(finalize_stream, is_client: is_client)
     end
 
     private
diff --git a/src/ruby/lib/grpc/generic/client_stub.rb b/src/ruby/lib/grpc/generic/client_stub.rb
index 75a95a4..9a50f8a 100644
--- a/src/ruby/lib/grpc/generic/client_stub.rb
+++ b/src/ruby/lib/grpc/generic/client_stub.rb
@@ -89,17 +89,23 @@
     #     used within a gRPC server.
     # @param channel_args [Hash] the channel arguments. Note: this argument is
     #     ignored if the channel_override argument is provided.
+    # @param interceptors [Array<GRPC::ClientInterceptor>] An array of
+    #     GRPC::ClientInterceptor objects that will be used for
+    #     intercepting calls before they are executed
+    #     Interceptors are an EXPERIMENTAL API.
     def initialize(host, creds,
                    channel_override: nil,
                    timeout: nil,
                    propagate_mask: nil,
-                   channel_args: {})
+                   channel_args: {},
+                   interceptors: [])
       @ch = ClientStub.setup_channel(channel_override, host, creds,
                                      channel_args)
       alt_host = channel_args[Core::Channel::SSL_TARGET]
       @host = alt_host.nil? ? host : alt_host
       @propagate_mask = propagate_mask
       @timeout = timeout.nil? ? DEFAULT_TIMEOUT : timeout
+      @interceptors = InterceptorRegistry.new(interceptors)
     end
 
     # request_response sends a request to a GRPC server, and returns the
@@ -149,16 +155,29 @@
                           deadline: deadline,
                           parent: parent,
                           credentials: credentials)
-      return c.request_response(req, metadata: metadata) unless return_op
-
-      # return the operation view of the active_call; define #execute as a
-      # new method for this instance that invokes #request_response.
-      c.merge_metadata_to_send(metadata)
-      op = c.operation
-      op.define_singleton_method(:execute) do
-        c.request_response(req, metadata: metadata)
+      interception_context = @interceptors.build_context
+      intercept_args = {
+        method: method,
+        request: req,
+        call: c.interceptable,
+        metadata: metadata
+      }
+      if return_op
+        # return the operation view of the active_call; define #execute as a
+        # new method for this instance that invokes #request_response.
+        c.merge_metadata_to_send(metadata)
+        op = c.operation
+        op.define_singleton_method(:execute) do
+          interception_context.intercept!(:request_response, intercept_args) do
+            c.request_response(req, metadata: metadata)
+          end
+        end
+        op
+      else
+        interception_context.intercept!(:request_response, intercept_args) do
+          c.request_response(req, metadata: metadata)
+        end
       end
-      op
     end
 
     # client_streamer sends a stream of requests to a GRPC server, and
@@ -213,16 +232,29 @@
                           deadline: deadline,
                           parent: parent,
                           credentials: credentials)
-      return c.client_streamer(requests, metadata: metadata) unless return_op
-
-      # return the operation view of the active_call; define #execute as a
-      # new method for this instance that invokes #client_streamer.
-      c.merge_metadata_to_send(metadata)
-      op = c.operation
-      op.define_singleton_method(:execute) do
-        c.client_streamer(requests)
+      interception_context = @interceptors.build_context
+      intercept_args = {
+        method: method,
+        requests: requests,
+        call: c.interceptable,
+        metadata: metadata
+      }
+      if return_op
+        # return the operation view of the active_call; define #execute as a
+        # new method for this instance that invokes #client_streamer.
+        c.merge_metadata_to_send(metadata)
+        op = c.operation
+        op.define_singleton_method(:execute) do
+          interception_context.intercept!(:client_streamer, intercept_args) do
+            c.client_streamer(requests)
+          end
+        end
+        op
+      else
+        interception_context.intercept!(:client_streamer, intercept_args) do
+          c.client_streamer(requests, metadata: metadata)
+        end
       end
-      op
     end
 
     # server_streamer sends one request to the GRPC server, which yields a
@@ -292,16 +324,29 @@
                           deadline: deadline,
                           parent: parent,
                           credentials: credentials)
-      return c.server_streamer(req, metadata: metadata, &blk) unless return_op
-
-      # return the operation view of the active_call; define #execute
-      # as a new method for this instance that invokes #server_streamer
-      c.merge_metadata_to_send(metadata)
-      op = c.operation
-      op.define_singleton_method(:execute) do
-        c.server_streamer(req, &blk)
+      interception_context = @interceptors.build_context
+      intercept_args = {
+        method: method,
+        request: req,
+        call: c.interceptable,
+        metadata: metadata
+      }
+      if return_op
+        # return the operation view of the active_call; define #execute
+        # as a new method for this instance that invokes #server_streamer
+        c.merge_metadata_to_send(metadata)
+        op = c.operation
+        op.define_singleton_method(:execute) do
+          interception_context.intercept!(:server_streamer, intercept_args) do
+            c.server_streamer(req, &blk)
+          end
+        end
+        op
+      else
+        interception_context.intercept!(:server_streamer, intercept_args) do
+          c.server_streamer(req, metadata: metadata, &blk)
+        end
       end
-      op
     end
 
     # bidi_streamer sends a stream of requests to the GRPC server, and yields
@@ -405,17 +450,29 @@
                           deadline: deadline,
                           parent: parent,
                           credentials: credentials)
-      return c.bidi_streamer(requests, metadata: metadata,
-                             &blk) unless return_op
-
-      # return the operation view of the active_call; define #execute
-      # as a new method for this instance that invokes #bidi_streamer
-      c.merge_metadata_to_send(metadata)
-      op = c.operation
-      op.define_singleton_method(:execute) do
-        c.bidi_streamer(requests, &blk)
+      interception_context = @interceptors.build_context
+      intercept_args = {
+        method: method,
+        requests: requests,
+        call: c.interceptable,
+        metadata: metadata
+      }
+      if return_op
+        # return the operation view of the active_call; define #execute
+        # as a new method for this instance that invokes #bidi_streamer
+        c.merge_metadata_to_send(metadata)
+        op = c.operation
+        op.define_singleton_method(:execute) do
+          interception_context.intercept!(:bidi_streamer, intercept_args) do
+            c.bidi_streamer(requests, &blk)
+          end
+        end
+        op
+      else
+        interception_context.intercept!(:bidi_streamer, intercept_args) do
+          c.bidi_streamer(requests, metadata: metadata, &blk)
+        end
       end
-      op
     end
 
     private
diff --git a/src/ruby/lib/grpc/generic/interceptor_registry.rb b/src/ruby/lib/grpc/generic/interceptor_registry.rb
new file mode 100644
index 0000000..b241eb9
--- /dev/null
+++ b/src/ruby/lib/grpc/generic/interceptor_registry.rb
@@ -0,0 +1,53 @@
+# 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.
+
+# GRPC contains the General RPC module.
+module GRPC
+  ##
+  # Represents a registry of added interceptors available for enumeration.
+  # The registry can be used for both server and client interceptors.
+  # This class is internal to gRPC and not meant for public usage.
+  #
+  class InterceptorRegistry
+    ##
+    # An error raised when an interceptor is attempted to be added
+    # that does not extend GRPC::Interceptor
+    #
+    class DescendantError < StandardError; end
+
+    ##
+    # Initialize the registry with an empty interceptor list
+    # This is an EXPERIMENTAL API.
+    #
+    def initialize(interceptors = [])
+      @interceptors = []
+      interceptors.each do |i|
+        base = GRPC::Interceptor
+        unless i.class.ancestors.include?(base)
+          fail DescendantError, "Interceptors must descend from #{base}"
+        end
+        @interceptors << i
+      end
+    end
+
+    ##
+    # Builds an interception context from this registry
+    #
+    # @return [InterceptionContext]
+    #
+    def build_context
+      InterceptionContext.new(@interceptors)
+    end
+  end
+end
diff --git a/src/ruby/lib/grpc/generic/interceptors.rb b/src/ruby/lib/grpc/generic/interceptors.rb
new file mode 100644
index 0000000..73faec4
--- /dev/null
+++ b/src/ruby/lib/grpc/generic/interceptors.rb
@@ -0,0 +1,186 @@
+# 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.
+require_relative 'interceptor_registry'
+
+# GRPC contains the General RPC module.
+module GRPC
+  ##
+  # Base class for interception in GRPC
+  #
+  class Interceptor
+    ##
+    # @param [Hash] options A hash of options that will be used
+    #   by the interceptor. This is an EXPERIMENTAL API.
+    #
+    def initialize(options = {})
+      @options = options || {}
+    end
+  end
+
+  ##
+  # ClientInterceptor allows for wrapping outbound gRPC client stub requests.
+  # This is an EXPERIMENTAL API.
+  #
+  class ClientInterceptor < Interceptor
+    ##
+    # Intercept a unary request response call
+    #
+    # @param [Object] request
+    # @param [GRPC::ActiveCall] call
+    # @param [Method] method
+    # @param [Hash] metadata
+    #
+    def request_response(request:, call:, method:, metadata:)
+      GRPC.logger.debug "Intercepting request response method #{method}" \
+        " for request #{request} with call #{call} and metadata: #{metadata}"
+      yield
+    end
+
+    ##
+    # Intercept a client streaming call
+    #
+    # @param [Enumerable] requests
+    # @param [GRPC::ActiveCall] call
+    # @param [Method] method
+    # @param [Hash] metadata
+    #
+    def client_streamer(requests:, call:, method:, metadata:)
+      GRPC.logger.debug "Intercepting client streamer method #{method}" \
+       " for requests #{requests} with call #{call} and metadata: #{metadata}"
+      yield
+    end
+
+    ##
+    # Intercept a server streaming call
+    #
+    # @param [Object] request
+    # @param [GRPC::ActiveCall] call
+    # @param [Method] method
+    # @param [Hash] metadata
+    #
+    def server_streamer(request:, call:, method:, metadata:)
+      GRPC.logger.debug "Intercepting server streamer method #{method}" \
+        " for request #{request} with call #{call} and metadata: #{metadata}"
+      yield
+    end
+
+    ##
+    # Intercept a BiDi streaming call
+    #
+    # @param [Enumerable] requests
+    # @param [GRPC::ActiveCall] call
+    # @param [Method] method
+    # @param [Hash] metadata
+    #
+    def bidi_streamer(requests:, call:, method:, metadata:)
+      GRPC.logger.debug "Intercepting bidi streamer method #{method}" \
+        " for requests #{requests} with call #{call} and metadata: #{metadata}"
+      yield
+    end
+  end
+
+  ##
+  # ServerInterceptor allows for wrapping gRPC server execution handling.
+  # This is an EXPERIMENTAL API.
+  #
+  class ServerInterceptor < Interceptor
+    ##
+    # Intercept a unary request response call.
+    #
+    # @param [Object] request
+    # @param [GRPC::ActiveCall::SingleReqView] call
+    # @param [Method] method
+    #
+    def request_response(request:, call:, method:)
+      GRPC.logger.debug "Intercepting request response method #{method}" \
+        " for request #{request} with call #{call}"
+      yield
+    end
+
+    ##
+    # Intercept a client streaming call
+    #
+    # @param [GRPC::ActiveCall::MultiReqView] call
+    # @param [Method] method
+    #
+    def client_streamer(call:, method:)
+      GRPC.logger.debug "Intercepting client streamer method #{method}" \
+        " with call #{call}"
+      yield
+    end
+
+    ##
+    # Intercept a server streaming call
+    #
+    # @param [Object] request
+    # @param [GRPC::ActiveCall::SingleReqView] call
+    # @param [Method] method
+    #
+    def server_streamer(request:, call:, method:)
+      GRPC.logger.debug "Intercepting server streamer method #{method}" \
+        " for request #{request} with call #{call}"
+      yield
+    end
+
+    ##
+    # Intercept a BiDi streaming call
+    #
+    # @param [Enumerable<Object>] requests
+    # @param [GRPC::ActiveCall::MultiReqView] call
+    # @param [Method] method
+    #
+    def bidi_streamer(requests:, call:, method:)
+      GRPC.logger.debug "Intercepting bidi streamer method #{method}" \
+        " for requests #{requests} with call #{call}"
+      yield
+    end
+  end
+
+  ##
+  # Represents the context in which an interceptor runs. Used to provide an
+  # injectable mechanism for handling interception. This is an EXPERIMENTAL API.
+  #
+  class InterceptionContext
+    ##
+    # @param [Array<GRPC::Interceptor>]
+    #
+    def initialize(interceptors = [])
+      @interceptors = interceptors.dup
+    end
+
+    ##
+    # Intercept the call and fire out to interceptors in a FIFO execution.
+    # This is an EXPERIMENTAL API.
+    #
+    # @param [Symbol] type The request type
+    # @param [Hash] args The arguments for the call
+    #
+    def intercept!(type, args = {})
+      return yield if @interceptors.none?
+
+      i = @interceptors.pop
+      return yield unless i
+
+      i.send(type, args) do
+        if @interceptors.any?
+          intercept!(type, args) do
+            yield
+          end
+        else
+          yield
+        end
+      end
+    end
+  end
+end
diff --git a/src/ruby/lib/grpc/generic/rpc_desc.rb b/src/ruby/lib/grpc/generic/rpc_desc.rb
index 6fb6c41..5fd1805 100644
--- a/src/ruby/lib/grpc/generic/rpc_desc.rb
+++ b/src/ruby/lib/grpc/generic/rpc_desc.rb
@@ -47,43 +47,85 @@
       proc { |o| unmarshal_class.method(unmarshal_method).call(o) }
     end
 
-    def handle_request_response(active_call, mth)
+    def handle_request_response(active_call, mth, inter_ctx)
       req = active_call.read_unary_request
-      resp = mth.call(req, active_call.single_req_view)
-      active_call.server_unary_response(
-        resp, trailing_metadata: active_call.output_metadata)
+      call = active_call.single_req_view
+
+      inter_ctx.intercept!(
+        :request_response,
+        method: mth,
+        call: call,
+        request: req
+      ) do
+        resp = mth.call(req, call)
+        active_call.server_unary_response(
+          resp,
+          trailing_metadata: active_call.output_metadata
+        )
+      end
     end
 
-    def handle_client_streamer(active_call,  mth)
-      resp = mth.call(active_call.multi_req_view)
-      active_call.server_unary_response(
-        resp, trailing_metadata: active_call.output_metadata)
+    def handle_client_streamer(active_call, mth, inter_ctx)
+      call = active_call.multi_req_view
+
+      inter_ctx.intercept!(
+        :client_streamer,
+        method: mth,
+        call: call
+      ) do
+        resp = mth.call(call)
+        active_call.server_unary_response(
+          resp,
+          trailing_metadata: active_call.output_metadata
+        )
+      end
     end
 
-    def handle_server_streamer(active_call, mth)
+    def handle_server_streamer(active_call, mth, inter_ctx)
       req = active_call.read_unary_request
-      replys = mth.call(req, active_call.single_req_view)
-      replys.each { |r| active_call.remote_send(r) }
+      call = active_call.single_req_view
+
+      inter_ctx.intercept!(
+        :server_streamer,
+        method: mth,
+        call: call,
+        request: req
+      ) do
+        replies = mth.call(req, call)
+        replies.each { |r| active_call.remote_send(r) }
+        send_status(active_call, OK, 'OK', active_call.output_metadata)
+      end
+    end
+
+    ##
+    # @param [GRPC::ActiveCall] active_call
+    # @param [Method] mth
+    # @param [Array<GRPC::InterceptionContext>] inter_ctx
+    #
+    def handle_bidi_streamer(active_call, mth, inter_ctx)
+      active_call.run_server_bidi(mth, inter_ctx)
       send_status(active_call, OK, 'OK', active_call.output_metadata)
     end
 
-    def handle_bidi_streamer(active_call, mth)
-      active_call.run_server_bidi(mth)
-      send_status(active_call, OK, 'OK', active_call.output_metadata)
-    end
-
-    def run_server_method(active_call, mth)
+    ##
+    # @param [GRPC::ActiveCall] active_call The current active call object
+    #   for the request
+    # @param [Method] mth The current RPC method being called
+    # @param [GRPC::InterceptionContext] inter_ctx The interception context
+    #   being executed
+    #
+    def run_server_method(active_call, mth, inter_ctx = InterceptionContext.new)
       # While a server method is running, it might be cancelled, its deadline
       # might be reached, the handler could throw an unknown error, or a
       # well-behaved handler could throw a StatusError.
       if request_response?
-        handle_request_response(active_call, mth)
+        handle_request_response(active_call, mth, inter_ctx)
       elsif client_streamer?
-        handle_client_streamer(active_call, mth)
+        handle_client_streamer(active_call, mth, inter_ctx)
       elsif server_streamer?
-        handle_server_streamer(active_call, mth)
+        handle_server_streamer(active_call, mth, inter_ctx)
       else  # is a bidi_stream
-        handle_bidi_streamer(active_call, mth)
+        handle_bidi_streamer(active_call, mth, inter_ctx)
       end
     rescue BadStatus => e
       # this is raised by handlers that want GRPC to send an application error
diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb
index 33b3cea..d5fc11d 100644
--- a/src/ruby/lib/grpc/generic/rpc_server.rb
+++ b/src/ruby/lib/grpc/generic/rpc_server.rb
@@ -196,11 +196,18 @@
     #
     # * server_args:
     # A server arguments hash to be passed down to the underlying core server
+    #
+    # * interceptors:
+    # Am array of GRPC::ServerInterceptor objects that will be used for
+    # intercepting server handlers to provide extra functionality.
+    # Interceptors are an EXPERIMENTAL API.
+    #
     def initialize(pool_size:DEFAULT_POOL_SIZE,
                    max_waiting_requests:DEFAULT_MAX_WAITING_REQUESTS,
                    poll_period:DEFAULT_POLL_PERIOD,
                    connect_md_proc:nil,
-                   server_args:{})
+                   server_args:{},
+                   interceptors:[])
       @connect_md_proc = RpcServer.setup_connect_md_proc(connect_md_proc)
       @max_waiting_requests = max_waiting_requests
       @poll_period = poll_period
@@ -212,6 +219,7 @@
       # :stopped. State transitions can only proceed in that order.
       @running_state = :not_started
       @server = Core::Server.new(server_args)
+      @interceptors = InterceptorRegistry.new(interceptors)
     end
 
     # stops a running server
@@ -374,7 +382,11 @@
             @pool.schedule(active_call) do |ac|
               c, mth = ac
               begin
-                rpc_descs[mth].run_server_method(c, rpc_handlers[mth])
+                rpc_descs[mth].run_server_method(
+                  c,
+                  rpc_handlers[mth],
+                  @interceptors.build_context
+                )
               rescue StandardError
                 c.send_status(GRPC::Core::StatusCodes::INTERNAL,
                               'Server handler failed')
@@ -382,7 +394,7 @@
             end
           end
         rescue Core::CallError, RuntimeError => e
-          # these might happen for various reasonse.  The correct behaviour of
+          # these might happen for various reasons.  The correct behavior of
           # the server is to log them and continue, if it's not shutting down.
           if running_state == :running
             GRPC.logger.warn("server call failed: #{e}")
diff --git a/src/ruby/lib/grpc/google_rpc_status_utils.rb b/src/ruby/lib/grpc/google_rpc_status_utils.rb
index fdadd6b..f253b08 100644
--- a/src/ruby/lib/grpc/google_rpc_status_utils.rb
+++ b/src/ruby/lib/grpc/google_rpc_status_utils.rb
@@ -19,10 +19,17 @@
 module GRPC
   # GoogleRpcStatusUtils provides utilities to convert between a
   # GRPC::Core::Status and a deserialized Google::Rpc::Status proto
+  # Returns nil if the grpc-status-details-bin trailer could not be
+  # converted to a GoogleRpcStatus due to the server not providing
+  # the necessary trailers.
+  # Raises an error if the server did provide the necessary trailers
+  # but they fail to deseriliaze into a GoogleRpcStatus protobuf.
   class GoogleRpcStatusUtils
     def self.extract_google_rpc_status(status)
       fail ArgumentError, 'bad type' unless status.is_a? Struct::Status
-      Google::Rpc::Status.decode(status.metadata['grpc-status-details-bin'])
+      grpc_status_details_bin_trailer = 'grpc-status-details-bin'
+      return nil if status.metadata[grpc_status_details_bin_trailer].nil?
+      Google::Rpc::Status.decode(status.metadata[grpc_status_details_bin_trailer])
     end
   end
 end
diff --git a/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb b/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb
index 6833701..ab50d9b 100644
--- a/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb
+++ b/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb
@@ -34,6 +34,7 @@
           self.service_name = 'grpc.testing.duplicate.EchoTestService'
 
           rpc :Echo, Grpc::Testing::EchoRequest, Grpc::Testing::EchoResponse
+          rpc :ResponseStream, Grpc::Testing::EchoRequest, stream(Grpc::Testing::EchoResponse)
         end
 
         Stub = Service.rpc_stub_class
diff --git a/src/ruby/spec/channel_connection_spec.rb b/src/ruby/spec/channel_connection_spec.rb
index c760566..ce3e3b1 100644
--- a/src/ruby/spec/channel_connection_spec.rb
+++ b/src/ruby/spec/channel_connection_spec.rb
@@ -11,45 +11,12 @@
 # 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.
-
-require 'grpc'
+require 'spec_helper'
 require 'timeout'
 
 include Timeout
 include GRPC::Core
 
-# A test message
-class EchoMsg
-  def self.marshal(_o)
-    ''
-  end
-
-  def self.unmarshal(_o)
-    EchoMsg.new
-  end
-end
-
-# A test service with an echo implementation.
-class EchoService
-  include GRPC::GenericService
-  rpc :an_rpc, EchoMsg, EchoMsg
-  attr_reader :received_md
-
-  def initialize(**kw)
-    @trailing_metadata = kw
-    @received_md = []
-  end
-
-  def an_rpc(req, call)
-    GRPC.logger.info('echo service received a request')
-    call.output_metadata.update(@trailing_metadata)
-    @received_md << call.metadata unless call.metadata.nil?
-    req
-  end
-end
-
-EchoStub = EchoService.rpc_stub_class
-
 def start_server(port = 0)
   @srv = GRPC::RpcServer.new(pool_size: 1)
   server_port = @srv.add_http2_port("localhost:#{port}", :this_port_is_insecure)
diff --git a/src/ruby/spec/generic/active_call_spec.rb b/src/ruby/spec/generic/active_call_spec.rb
index a00df92..120acc3 100644
--- a/src/ruby/spec/generic/active_call_spec.rb
+++ b/src/ruby/spec/generic/active_call_spec.rb
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-require 'grpc'
+require 'spec_helper'
 
 include GRPC::Core::StatusCodes
 
@@ -82,6 +82,16 @@
         end
       end
     end
+
+    describe '#interceptable' do
+      it 'exposes a fixed subset of the ActiveCall.methods' do
+        want = %w(deadline)
+        v = @client_call.interceptable
+        want.each do |w|
+          expect(v.methods.include?(w))
+        end
+      end
+    end
   end
 
   describe '#remote_send' do
@@ -609,9 +619,11 @@
         msgs
       end
 
+      int_ctx = GRPC::InterceptionContext.new
+
       @server_thread = Thread.new do
         @server_call.run_server_bidi(
-          fake_gen_each_reply_with_no_call_param)
+          fake_gen_each_reply_with_no_call_param, int_ctx)
         @server_call.send_status(@server_status)
       end
     end
@@ -624,10 +636,11 @@
         call_param.send_initial_metadata
         msgs
       end
+      int_ctx = GRPC::InterceptionContext.new
 
       @server_thread = Thread.new do
         @server_call.run_server_bidi(
-          fake_gen_each_reply_with_call_param)
+          fake_gen_each_reply_with_call_param, int_ctx)
         @server_call.send_status(@server_status)
       end
     end
diff --git a/src/ruby/spec/generic/client_interceptors_spec.rb b/src/ruby/spec/generic/client_interceptors_spec.rb
new file mode 100644
index 0000000..f292715
--- /dev/null
+++ b/src/ruby/spec/generic/client_interceptors_spec.rb
@@ -0,0 +1,153 @@
+# 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.
+require 'spec_helper'
+
+describe 'Client Interceptors' do
+  let(:interceptor) { TestClientInterceptor.new }
+  let(:interceptors_opts) { { interceptors: [interceptor] } }
+  let(:request) { EchoMsg.new }
+  let(:service) { EchoService }
+
+  before(:each) do
+    build_rpc_server
+  end
+
+  context 'when a client interceptor is added' do
+    context 'with a request/response call' do
+      it 'should be called', server: true do
+        expect(interceptor).to receive(:request_response)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+          expect_any_instance_of(GRPC::ActiveCall).to receive(:request_response)
+            .once.and_call_original
+          expect(stub.an_rpc(request)).to be_a(EchoMsg)
+        end
+      end
+
+      it 'can modify outgoing metadata', server: true do
+        expect(interceptor).to receive(:request_response)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+          expect_any_instance_of(GRPC::ActiveCall).to receive(:request_response)
+            .with(request, metadata: { 'foo' => 'bar_from_request_response' })
+            .once.and_call_original
+          expect(stub.an_rpc(request)).to be_a(EchoMsg)
+        end
+      end
+    end
+
+    context 'with a client streaming call' do
+      it 'should be called', server: true do
+        expect(interceptor).to receive(:client_streamer)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+          expect_any_instance_of(GRPC::ActiveCall).to receive(:client_streamer)
+            .once.and_call_original
+          requests = [EchoMsg.new, EchoMsg.new]
+          expect(stub.a_client_streaming_rpc(requests)).to be_a(EchoMsg)
+        end
+      end
+
+      it 'can modify outgoing metadata', server: true do
+        expect(interceptor).to receive(:client_streamer)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+          requests = [EchoMsg.new, EchoMsg.new]
+          expect_any_instance_of(GRPC::ActiveCall).to receive(:client_streamer)
+            .with(requests, metadata: { 'foo' => 'bar_from_client_streamer' })
+            .once.and_call_original
+          expect(stub.a_client_streaming_rpc(requests)).to be_a(EchoMsg)
+        end
+      end
+    end
+
+    context 'with a server streaming call' do
+      it 'should be called', server: true do
+        expect(interceptor).to receive(:server_streamer)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+          request = EchoMsg.new
+          expect_any_instance_of(GRPC::ActiveCall).to receive(:server_streamer)
+            .once.and_call_original
+          responses = stub.a_server_streaming_rpc(request)
+          responses.each do |r|
+            expect(r).to be_a(EchoMsg)
+          end
+        end
+      end
+
+      it 'can modify outgoing metadata', server: true do
+        expect(interceptor).to receive(:server_streamer)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+          request = EchoMsg.new
+          expect_any_instance_of(GRPC::ActiveCall).to receive(:server_streamer)
+            .with(request, metadata: { 'foo' => 'bar_from_server_streamer' })
+            .once.and_call_original
+          responses = stub.a_server_streaming_rpc(request)
+          responses.each do |r|
+            expect(r).to be_a(EchoMsg)
+          end
+        end
+      end
+    end
+
+    context 'with a bidi call' do
+      it 'should be called', server: true do
+        expect(interceptor).to receive(:bidi_streamer)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+          expect_any_instance_of(GRPC::ActiveCall).to receive(:bidi_streamer)
+            .once.and_call_original
+          requests = [EchoMsg.new, EchoMsg.new]
+          responses = stub.a_bidi_rpc(requests)
+          responses.each do |r|
+            expect(r).to be_a(EchoMsg)
+          end
+        end
+      end
+
+      it 'can modify outgoing metadata', server: true do
+        expect(interceptor).to receive(:bidi_streamer)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub, opts: interceptors_opts)
+          requests = [EchoMsg.new, EchoMsg.new]
+          expect_any_instance_of(GRPC::ActiveCall).to receive(:bidi_streamer)
+            .with(requests, metadata: { 'foo' => 'bar_from_bidi_streamer' })
+            .once.and_call_original
+          responses = stub.a_bidi_rpc(requests)
+          responses.each do |r|
+            expect(r).to be_a(EchoMsg)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/src/ruby/spec/generic/interceptor_registry_spec.rb b/src/ruby/spec/generic/interceptor_registry_spec.rb
new file mode 100644
index 0000000..f93f5ce
--- /dev/null
+++ b/src/ruby/spec/generic/interceptor_registry_spec.rb
@@ -0,0 +1,65 @@
+# 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.
+require 'spec_helper'
+
+describe GRPC::InterceptorRegistry do
+  let(:server) { RpcServer.new }
+  let(:interceptor) { TestServerInterceptor.new }
+  let(:interceptors) { [interceptor] }
+  let(:registry) { described_class.new(interceptors) }
+
+  describe 'initialization' do
+    subject { registry }
+
+    context 'with an interceptor extending GRPC::ServerInterceptor' do
+      it 'should add the interceptor to the registry' do
+        subject
+        is = registry.instance_variable_get('@interceptors')
+        expect(is.count).to eq 1
+        expect(is.first).to eq interceptor
+      end
+    end
+
+    context 'with multiple interceptors' do
+      let(:interceptor2) { TestServerInterceptor.new }
+      let(:interceptor3) { TestServerInterceptor.new }
+      let(:interceptors) { [interceptor, interceptor2, interceptor3] }
+
+      it 'should maintain order of insertion when iterated against' do
+        subject
+        is = registry.instance_variable_get('@interceptors')
+        expect(is.count).to eq 3
+        is.each_with_index do |i, idx|
+          case idx
+          when 0
+            expect(i).to eq interceptor
+          when 1
+            expect(i).to eq interceptor2
+          when 2
+            expect(i).to eq interceptor3
+          end
+        end
+      end
+    end
+
+    context 'with an interceptor not extending GRPC::ServerInterceptor' do
+      let(:interceptor) { Class }
+      let(:err) { GRPC::InterceptorRegistry::DescendantError }
+
+      it 'should raise an InvalidArgument exception' do
+        expect { subject }.to raise_error(err)
+      end
+    end
+  end
+end
diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb
index b887eaa..05059fb 100644
--- a/src/ruby/spec/generic/rpc_server_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_spec.rb
@@ -11,8 +11,7 @@
 # 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.
-
-require 'grpc'
+require 'spec_helper'
 
 def load_test_certs
   test_root = File.join(File.dirname(File.dirname(__FILE__)), 'testdata')
@@ -28,17 +27,6 @@
   end
 end
 
-# A test message
-class EchoMsg
-  def self.marshal(_o)
-    ''
-  end
-
-  def self.unmarshal(_o)
-    EchoMsg.new
-  end
-end
-
 # A test service with no methods.
 class EmptyService
   include GRPC::GenericService
@@ -50,27 +38,6 @@
   rpc :an_rpc, EchoMsg, EchoMsg
 end
 
-# A test service with an echo implementation.
-class EchoService
-  include GRPC::GenericService
-  rpc :an_rpc, EchoMsg, EchoMsg
-  attr_reader :received_md
-
-  def initialize(**kw)
-    @trailing_metadata = kw
-    @received_md = []
-  end
-
-  def an_rpc(req, call)
-    GRPC.logger.info('echo service received a request')
-    call.output_metadata.update(@trailing_metadata)
-    @received_md << call.metadata unless call.metadata.nil?
-    req
-  end
-end
-
-EchoStub = EchoService.rpc_stub_class
-
 # A test service with an implementation that fails with BadStatus
 class FailingService
   include GRPC::GenericService
diff --git a/src/ruby/spec/generic/server_interceptors_spec.rb b/src/ruby/spec/generic/server_interceptors_spec.rb
new file mode 100644
index 0000000..eb86686
--- /dev/null
+++ b/src/ruby/spec/generic/server_interceptors_spec.rb
@@ -0,0 +1,218 @@
+# 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.
+require 'spec_helper'
+
+describe 'Server Interceptors' do
+  let(:interceptor) { TestServerInterceptor.new }
+  let(:request) { EchoMsg.new }
+  let(:trailing_metadata) { {} }
+  let(:service) { EchoService.new(trailing_metadata) }
+  let(:interceptors) { [] }
+
+  before(:each) do
+    build_rpc_server(server_opts: { interceptors: interceptors })
+  end
+
+  context 'when a server interceptor is added' do
+    let(:interceptors) { [interceptor] }
+    let(:client_metadata) { { client_md: 'test' } }
+    let(:client_call_opts) { { metadata: client_metadata, return_op: true } }
+
+    context 'with a request/response call' do
+      let(:trailing_metadata) { { server_om: 'from_request_response' } }
+
+      it 'should be called', server: true do
+        expect(interceptor).to receive(:request_response)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub)
+          expect(stub.an_rpc(request)).to be_a(EchoMsg)
+        end
+      end
+
+      it 'can modify trailing metadata', server: true do
+        expect(interceptor).to receive(:request_response)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub)
+          expect_any_instance_of(GRPC::ActiveCall).to(
+            receive(:request_response).with(request, metadata: client_metadata)
+              .once.and_call_original
+          )
+          op = stub.an_rpc(request, client_call_opts)
+          msg = op.execute
+          expect(op.trailing_metadata).to eq(
+            'interc' => 'from_request_response',
+            'server_om' => 'from_request_response'
+          )
+          expect(msg).to be_a(EchoMsg)
+        end
+      end
+    end
+
+    context 'with a client streaming call' do
+      let(:trailing_metadata) { { server_om: 'from_client_streamer' } }
+      let(:requests) { [EchoMsg.new, EchoMsg.new] }
+
+      it 'should be called', server: true do
+        expect(interceptor).to receive(:client_streamer)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub)
+          expect(stub.a_client_streaming_rpc(requests)).to be_a(EchoMsg)
+        end
+      end
+
+      it 'can modify trailing metadata', server: true do
+        expect(interceptor).to receive(:client_streamer)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub)
+          expect_any_instance_of(GRPC::ActiveCall).to(
+            receive(:client_streamer).with(requests)
+              .once.and_call_original
+          )
+          op = stub.a_client_streaming_rpc(requests, client_call_opts)
+          msg = op.execute
+          expect(op.trailing_metadata).to eq(
+            'interc' => 'from_client_streamer',
+            'server_om' => 'from_client_streamer'
+          )
+          expect(msg).to be_a(EchoMsg)
+        end
+      end
+    end
+
+    context 'with a server streaming call' do
+      let(:trailing_metadata) { { server_om: 'from_server_streamer' } }
+      let(:request) { EchoMsg.new }
+
+      it 'should be called', server: true do
+        expect(interceptor).to receive(:server_streamer)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub)
+          responses = stub.a_server_streaming_rpc(request)
+          responses.each do |r|
+            expect(r).to be_a(EchoMsg)
+          end
+        end
+      end
+
+      it 'can modify trailing metadata', server: true do
+        expect(interceptor).to receive(:server_streamer)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub)
+          expect_any_instance_of(GRPC::ActiveCall).to(
+            receive(:server_streamer).with(request)
+              .once.and_call_original
+          )
+          op = stub.a_server_streaming_rpc(request, client_call_opts)
+          responses = op.execute
+          responses.each do |r|
+            expect(r).to be_a(EchoMsg)
+          end
+          expect(op.trailing_metadata).to eq(
+            'interc' => 'from_server_streamer',
+            'server_om' => 'from_server_streamer'
+          )
+        end
+      end
+    end
+
+    context 'with a bidi call' do
+      let(:trailing_metadata) { { server_om: 'from_bidi_streamer' } }
+      let(:requests) { [EchoMsg.new, EchoMsg.new] }
+
+      it 'should be called', server: true do
+        expect(interceptor).to receive(:bidi_streamer)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub)
+          responses = stub.a_bidi_rpc(requests)
+          responses.each do |r|
+            expect(r).to be_a(EchoMsg)
+          end
+        end
+      end
+
+      it 'can modify trailing metadata', server: true do
+        expect(interceptor).to receive(:bidi_streamer)
+          .once.and_call_original
+
+        run_services_on_server(@server, services: [service]) do
+          stub = build_insecure_stub(EchoStub)
+          expect_any_instance_of(GRPC::ActiveCall).to(
+            receive(:bidi_streamer).with(requests)
+              .once.and_call_original
+          )
+          op = stub.a_bidi_rpc(requests, client_call_opts)
+          responses = op.execute
+          responses.each do |r|
+            expect(r).to be_a(EchoMsg)
+          end
+          expect(op.trailing_metadata).to eq(
+            'interc' => 'from_bidi_streamer',
+            'server_om' => 'from_bidi_streamer'
+          )
+        end
+      end
+    end
+  end
+
+  context 'when multiple interceptors are added' do
+    let(:interceptor2) { TestServerInterceptor.new }
+    let(:interceptor3) { TestServerInterceptor.new }
+    let(:interceptors) do
+      [
+        interceptor,
+        interceptor2,
+        interceptor3
+      ]
+    end
+
+    it 'each should be called', server: true do
+      expect(interceptor).to receive(:request_response)
+        .once.and_call_original
+      expect(interceptor2).to receive(:request_response)
+        .once.and_call_original
+      expect(interceptor3).to receive(:request_response)
+        .once.and_call_original
+
+      run_services_on_server(@server, services: [service]) do
+        stub = build_insecure_stub(EchoStub)
+        expect(stub.an_rpc(request)).to be_a(EchoMsg)
+      end
+    end
+  end
+
+  context 'when an interceptor is not added' do
+    it 'should not be called', server: true do
+      expect(interceptor).to_not receive(:call)
+
+      run_services_on_server(@server, services: [service]) do
+        stub = build_insecure_stub(EchoStub)
+        expect(stub.an_rpc(request)).to be_a(EchoMsg)
+      end
+    end
+  end
+end
diff --git a/src/ruby/spec/google_rpc_status_utils_spec.rb b/src/ruby/spec/google_rpc_status_utils_spec.rb
index fe221c3..6f2a06b 100644
--- a/src/ruby/spec/google_rpc_status_utils_spec.rb
+++ b/src/ruby/spec/google_rpc_status_utils_spec.rb
@@ -31,12 +31,11 @@
     expect(exception.message.include?('bad type')).to be true
   end
 
-  it 'fails with some error if the header key is missing' do
+  it 'returns nil if the header key is missing' do
     status = Struct::Status.new(1, 'details', key: 'val')
     expect(status.metadata.nil?).to be false
-    expect do
-      GRPC::GoogleRpcStatusUtils.extract_google_rpc_status(status)
-    end.to raise_error(StandardError)
+    expect(GRPC::GoogleRpcStatusUtils.extract_google_rpc_status(
+             status)).to be(nil)
   end
 
   it 'fails with some error if the header key fails to deserialize' do
@@ -221,3 +220,73 @@
              status_from_exception)).to eq(rpc_status)
   end
 end
+
+# A test service that fails without explicitly setting the
+# grpc-status-details-bin trailer. Tests assumptions about value
+# of grpc-status-details-bin on the client side when the trailer wasn't
+# set explicitly.
+class NoStatusDetailsBinTestService
+  include GRPC::GenericService
+  rpc :an_rpc, EchoMsg, EchoMsg
+
+  def an_rpc(_, _)
+    fail GRPC::Unknown
+  end
+end
+
+NoStatusDetailsBinTestServiceStub = NoStatusDetailsBinTestService.rpc_stub_class
+
+describe 'when the endpoint doesnt send grpc-status-details-bin' do
+  def start_server
+    @srv = GRPC::RpcServer.new(pool_size: 1)
+    @server_port = @srv.add_http2_port('localhost:0',
+                                       :this_port_is_insecure)
+    @srv.handle(NoStatusDetailsBinTestService)
+    @server_thd = Thread.new { @srv.run }
+    @srv.wait_till_running
+  end
+
+  def stop_server
+    expect(@srv.stopped?).to be(false)
+    @srv.stop
+    @server_thd.join
+    expect(@srv.stopped?).to be(true)
+  end
+
+  before(:each) do
+    start_server
+  end
+
+  after(:each) do
+    stop_server
+  end
+
+  it 'should receive nil when we extract try to extract a google '\
+    'rpc status from a BadStatus exception that didnt have it' do
+    stub = NoStatusDetailsBinTestServiceStub.new("localhost:#{@server_port}",
+                                                 :this_channel_is_insecure)
+    begin
+      stub.an_rpc(EchoMsg.new)
+    rescue GRPC::Unknown => e
+      rpc_status = GRPC::GoogleRpcStatusUtils.extract_google_rpc_status(
+        e.to_status)
+    end
+    expect(rpc_status).to be(nil)
+  end
+
+  it 'should receive nil when we extract try to extract a google '\
+    'rpc status from an op views status object that didnt have it' do
+    stub = NoStatusDetailsBinTestServiceStub.new("localhost:#{@server_port}",
+                                                 :this_channel_is_insecure)
+    op = stub.an_rpc(EchoMsg.new, return_op: true)
+    begin
+      op.execute
+    rescue GRPC::Unknown => e
+      status_from_exception = e.to_status
+    end
+    expect(GRPC::GoogleRpcStatusUtils.extract_google_rpc_status(
+             status_from_exception)).to be(nil)
+    expect(GRPC::GoogleRpcStatusUtils.extract_google_rpc_status(
+             op.status)).to be nil
+  end
+end
diff --git a/src/ruby/spec/spec_helper.rb b/src/ruby/spec/spec_helper.rb
index 6e1eba1..8fe9e6e 100644
--- a/src/ruby/spec/spec_helper.rb
+++ b/src/ruby/spec/spec_helper.rb
@@ -32,6 +32,9 @@
 require 'logging'
 require 'rspec/logging_helper'
 
+require_relative 'support/services'
+require_relative 'support/helpers'
+
 # GRPC is the general RPC module
 #
 # Configure its logging for fine-grained log control during test runs
@@ -49,6 +52,7 @@
 RSpec.configure do |config|
   include RSpec::LoggingHelper
   config.capture_log_messages  # comment this out to see logs during test runs
+  include GRPC::Spec::Helpers
 end
 
 RSpec::Expectations.configuration.warn_about_potential_false_positives = false
diff --git a/src/ruby/spec/support/helpers.rb b/src/ruby/spec/support/helpers.rb
new file mode 100644
index 0000000..65fffff
--- /dev/null
+++ b/src/ruby/spec/support/helpers.rb
@@ -0,0 +1,73 @@
+# 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.
+
+# GRPC contains the General RPC module.
+module GRPC
+  ##
+  # GRPC RSpec base module
+  #
+  module Spec
+    ##
+    # A module that is used for providing generic helpers across the
+    # GRPC test suite
+    #
+    module Helpers
+      # Shortcut syntax for a GRPC RPC Server
+      RpcServer = GRPC::RpcServer
+
+      ##
+      # Build an RPC server used for testing
+      #
+      def build_rpc_server(server_opts: {},
+                           client_opts: {})
+        @server = RpcServer.new({ poll_period: 1 }.merge(server_opts))
+        @port = @server.add_http2_port('0.0.0.0:0', :this_port_is_insecure)
+        @host = "0.0.0.0:#{@port}"
+        @client_opts = client_opts
+        @server
+      end
+
+      ##
+      # Run services on an RPC server, yielding to allow testing within
+      #
+      # @param [RpcServer] server
+      # @param [Array<Class>] services
+      #
+      def run_services_on_server(server, services: [])
+        services.each do |s|
+          server.handle(s)
+        end
+        t = Thread.new { server.run }
+        server.wait_till_running
+
+        yield
+
+        server.stop
+        t.join
+      end
+
+      ##
+      # Build an insecure stub from a given stub class
+      #
+      # @param [Class] klass
+      # @param [String] host
+      #
+      def build_insecure_stub(klass, host: nil, opts: nil)
+        host ||= @host
+        opts ||= @client_opts
+        klass.new(host, :this_channel_is_insecure, **opts)
+      end
+    end
+  end
+end
diff --git a/src/ruby/spec/support/services.rb b/src/ruby/spec/support/services.rb
new file mode 100644
index 0000000..27cc8e6
--- /dev/null
+++ b/src/ruby/spec/support/services.rb
@@ -0,0 +1,147 @@
+# 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.
+
+# Test stubs for various scenarios
+require 'grpc'
+
+# A test message
+class EchoMsg
+  def self.marshal(_o)
+    ''
+  end
+
+  def self.unmarshal(_o)
+    EchoMsg.new
+  end
+end
+
+# A test service with an echo implementation.
+class EchoService
+  include GRPC::GenericService
+  rpc :an_rpc, EchoMsg, EchoMsg
+  rpc :a_client_streaming_rpc, stream(EchoMsg), EchoMsg
+  rpc :a_server_streaming_rpc, EchoMsg, stream(EchoMsg)
+  rpc :a_bidi_rpc, stream(EchoMsg), stream(EchoMsg)
+  attr_reader :received_md
+
+  def initialize(**kw)
+    @trailing_metadata = kw
+    @received_md = []
+  end
+
+  def an_rpc(req, call)
+    GRPC.logger.info('echo service received a request')
+    call.output_metadata.update(@trailing_metadata)
+    @received_md << call.metadata unless call.metadata.nil?
+    req
+  end
+
+  def a_client_streaming_rpc(call)
+    # iterate through requests so call can complete
+    call.output_metadata.update(@trailing_metadata)
+    call.each_remote_read.each { |r| p r }
+    EchoMsg.new
+  end
+
+  def a_server_streaming_rpc(_req, call)
+    call.output_metadata.update(@trailing_metadata)
+    [EchoMsg.new, EchoMsg.new]
+  end
+
+  def a_bidi_rpc(requests, call)
+    call.output_metadata.update(@trailing_metadata)
+    requests.each { |r| p r }
+    [EchoMsg.new, EchoMsg.new]
+  end
+end
+
+EchoStub = EchoService.rpc_stub_class
+
+# For testing server interceptors
+class TestServerInterceptor < GRPC::ServerInterceptor
+  def request_response(request:, call:, method:)
+    p "Received request/response call at method #{method}" \
+      " with request #{request} for call #{call}"
+    call.output_metadata[:interc] = 'from_request_response'
+    p "[GRPC::Ok] (#{method.owner.name}.#{method.name})"
+    yield
+  end
+
+  def client_streamer(call:, method:)
+    call.output_metadata[:interc] = 'from_client_streamer'
+    call.each_remote_read.each do |r|
+      p "In interceptor: #{r}"
+    end
+    p "Received client streamer call at method #{method} for call #{call}"
+    yield
+  end
+
+  def server_streamer(request:, call:, method:)
+    p "Received server streamer call at method #{method} with request" \
+      " #{request} for call #{call}"
+    call.output_metadata[:interc] = 'from_server_streamer'
+    yield
+  end
+
+  def bidi_streamer(requests:, call:, method:)
+    requests.each do |r|
+      p "Bidi request: #{r}"
+    end
+    p "Received bidi streamer call at method #{method} with requests" \
+      " #{requests} for call #{call}"
+    call.output_metadata[:interc] = 'from_bidi_streamer'
+    yield
+  end
+end
+
+# For testing client interceptors
+class TestClientInterceptor < GRPC::ClientInterceptor
+  def request_response(request:, call:, method:, metadata: {})
+    p "Intercepted request/response call at method #{method}" \
+      " with request #{request} for call #{call}" \
+      " and metadata: #{metadata}"
+    metadata['foo'] = 'bar_from_request_response'
+    yield
+  end
+
+  def client_streamer(requests:, call:, method:, metadata: {})
+    p "Received client streamer call at method #{method}" \
+      " with requests #{requests} for call #{call}" \
+      " and metadata: #{metadata}"
+    requests.each do |r|
+      p "In client interceptor: #{r}"
+    end
+    metadata['foo'] = 'bar_from_client_streamer'
+    yield
+  end
+
+  def server_streamer(request:, call:, method:, metadata: {})
+    p "Received server streamer call at method #{method}" \
+      " with request #{request} for call #{call}" \
+      " and metadata: #{metadata}"
+    metadata['foo'] = 'bar_from_server_streamer'
+    yield
+  end
+
+  def bidi_streamer(requests:, call:, method:, metadata: {})
+    p "Received bidi streamer call at method #{method}" \
+      "with requests #{requests} for call #{call}" \
+      " and metadata: #{metadata}"
+    requests.each do |r|
+      p "In client interceptor: #{r}"
+    end
+    metadata['foo'] = 'bar_from_bidi_streamer'
+    yield
+  end
+end
diff --git a/templates/test/cpp/naming/create_private_dns_zone.sh.template b/templates/test/cpp/naming/create_private_dns_zone.sh.template
new file mode 100644
index 0000000..14324b0
--- /dev/null
+++ b/templates/test/cpp/naming/create_private_dns_zone.sh.template
@@ -0,0 +1,4 @@
+%YAML 1.2
+--- |
+  <%namespace file="create_private_dns_zone_defs.include" import="*"/>\
+  ${create_private_dns_zone(resolver_gce_integration_tests_zone_id, resolver_tests_common_zone_name)}
diff --git a/templates/test/cpp/naming/create_private_dns_zone_defs.include b/templates/test/cpp/naming/create_private_dns_zone_defs.include
new file mode 100644
index 0000000..465dd63
--- /dev/null
+++ b/templates/test/cpp/naming/create_private_dns_zone_defs.include
@@ -0,0 +1,32 @@
+<%def name="create_private_dns_zone(resolver_gce_integration_tests_zone_id, resolver_tests_common_zone_name)">#!/bin/bash
+# Copyright 2015 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 file is auto-generated
+
+set -ex
+
+cd $(dirname $0)/../../..
+
+gcloud alpha dns managed-zones create \\
+
+  ${resolver_gce_integration_tests_zone_id} \\
+
+  --dns-name=${resolver_tests_common_zone_name} \\
+
+  --description="GCE-DNS-private-zone-for-GRPC-testing" \\
+
+  --visibility=private \\
+
+  --networks=default</%def>
diff --git a/templates/test/cpp/naming/private_dns_zone_init.sh.template b/templates/test/cpp/naming/private_dns_zone_init.sh.template
new file mode 100644
index 0000000..d5ffd04
--- /dev/null
+++ b/templates/test/cpp/naming/private_dns_zone_init.sh.template
@@ -0,0 +1,4 @@
+%YAML 1.2
+--- |
+  <%namespace file="private_dns_zone_init_defs.include" import="*"/>\
+  ${private_dns_zone_init(all_integration_test_records, resolver_gce_integration_tests_zone_id, resolver_tests_common_zone_name)}
diff --git a/templates/test/cpp/naming/private_dns_zone_init_defs.include b/templates/test/cpp/naming/private_dns_zone_init_defs.include
new file mode 100644
index 0000000..06bc8ad
--- /dev/null
+++ b/templates/test/cpp/naming/private_dns_zone_init_defs.include
@@ -0,0 +1,40 @@
+<%def name="private_dns_zone_init(records,resolver_gce_integration_tests_zone_id,resolver_tests_common_zone_name)">#!/bin/bash
+# Copyright 2015 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 file is auto-generated
+
+set -ex
+
+cd $(dirname $0)/../../..
+
+gcloud dns record-sets transaction start -z=${resolver_gce_integration_tests_zone_id}
+
+% for r in records:
+gcloud dns record-sets transaction add \\
+
+  -z=${resolver_gce_integration_tests_zone_id} \\
+
+  --name=${r['name']}.${resolver_tests_common_zone_name} \\
+
+  --type=${r['type']} \\
+
+  --ttl=${r['ttl']} \\
+
+  ${r['data']}
+
+% endfor
+gcloud dns record-sets transaction describe -z=${resolver_gce_integration_tests_zone_id}
+gcloud dns record-sets transaction execute -z=${resolver_gce_integration_tests_zone_id}
+gcloud dns record-sets list -z=${resolver_gce_integration_tests_zone_id}</%def>
diff --git a/templates/test/cpp/naming/resolver_gce_integration_tests_defs.include b/templates/test/cpp/naming/resolver_gce_integration_tests_defs.include
new file mode 100644
index 0000000..2413ec5
--- /dev/null
+++ b/templates/test/cpp/naming/resolver_gce_integration_tests_defs.include
@@ -0,0 +1,64 @@
+<%def name="resolver_gce_integration_tests(tests, records, resolver_tests_common_zone_name)">#!/bin/bash
+# Copyright 2015 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 file is auto-generated
+
+set -ex
+
+if [[ "$GRPC_DNS_RESOLVER" == "" ]]; then
+  export GRPC_DNS_RESOLVER=ares
+elif [[ "$GRPC_DNS_RESOLVER" != ares ]]; then
+  echo "Unexpected: GRPC_DNS_RESOLVER=$GRPC_DNS_RESOLVER. This test only works with c-ares resolver"
+  exit 1
+fi
+
+cd $(dirname $0)/../../..
+
+if [[ "$CONFIG" == "" ]]; then
+  export CONFIG=opt
+fi
+make resolver_component_test
+echo "Sanity check DNS records are resolveable with dig:"
+EXIT_CODE=0
+
+% for r in records:
+ONE_FAILED=0
+dig ${r['type']} ${r['name']}.${resolver_tests_common_zone_name} | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig ${r['type']} ${r['name']}.${resolver_tests_common_zone_name} FAILED"
+  exit 1
+fi
+
+% endfor
+echo "Sanity check PASSED. Run resolver tests:"
+
+% for test in tests:
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \\
+
+  --target_name='${test['target_name']}' \\
+
+  --expected_addrs='${test['expected_addrs']}' \\
+
+  --expected_chosen_service_config='${test['expected_chosen_service_config']}' \\
+
+  --expected_lb_policy='${test['expected_lb_policy']}' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: ${test['target_name']} FAILED"
+  EXIT_CODE=1
+fi
+
+% endfor
+exit $EXIT_CODE</%def>
diff --git a/templates/test/cpp/naming/resolver_gce_integration_tests_runner.sh.template b/templates/test/cpp/naming/resolver_gce_integration_tests_runner.sh.template
new file mode 100644
index 0000000..c728784
--- /dev/null
+++ b/templates/test/cpp/naming/resolver_gce_integration_tests_runner.sh.template
@@ -0,0 +1,4 @@
+%YAML 1.2
+--- |
+  <%namespace file="resolver_gce_integration_tests_defs.include" import="*"/>\
+  ${resolver_gce_integration_tests(resolver_gce_integration_test_cases, all_integration_test_records, resolver_tests_common_zone_name)}
diff --git a/templates/tools/dockerfile/clang_format.include b/templates/tools/dockerfile/clang_format.include
index 9a2b60b..81bd2be 100644
--- a/templates/tools/dockerfile/clang_format.include
+++ b/templates/tools/dockerfile/clang_format.include
@@ -1,5 +1,5 @@
 RUN apt-get update && apt-get -y install wget
-RUN echo deb http://llvm.org/apt/wily/ llvm-toolchain-wily-3.8 main >> /etc/apt/sources.list
-RUN echo deb-src http://llvm.org/apt/wily/ llvm-toolchain-wily-3.8 main >> /etc/apt/sources.list
-RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key| apt-key add -
+RUN echo "deb http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.8 main" >> /etc/apt/sources.list
+RUN echo "deb-src http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.8 main" >> /etc/apt/sources.list
+RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | apt-key add -
 RUN apt-get update && apt-get -y install clang-format-3.8
diff --git a/templates/tools/dockerfile/grpc_clang_format/Dockerfile.template b/templates/tools/dockerfile/grpc_clang_format/Dockerfile.template
index 69cd403..1ab667c 100644
--- a/templates/tools/dockerfile/grpc_clang_format/Dockerfile.template
+++ b/templates/tools/dockerfile/grpc_clang_format/Dockerfile.template
@@ -14,9 +14,10 @@
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
-  FROM ubuntu:15.10
+  FROM debian:jessie
   
   <%include file="../clang_format.include"/>
   ADD clang_format_all_the_things.sh /
   CMD ["echo 'Run with tools/distrib/clang_format_code.sh'"]
   
+  
\ No newline at end of file
diff --git a/templates/tools/dockerfile/python_deps.include b/templates/tools/dockerfile/python_deps.include
index 94b854a..bf1f57f 100644
--- a/templates/tools/dockerfile/python_deps.include
+++ b/templates/tools/dockerfile/python_deps.include
@@ -9,6 +9,6 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
diff --git a/templates/tools/dockerfile/test/sanity/Dockerfile.template b/templates/tools/dockerfile/test/sanity/Dockerfile.template
index 3f9ea32..bf34c0a 100644
--- a/templates/tools/dockerfile/test/sanity/Dockerfile.template
+++ b/templates/tools/dockerfile/test/sanity/Dockerfile.template
@@ -14,7 +14,7 @@
   # See the License for the specific language governing permissions and
   # limitations under the License.
   
-  FROM ubuntu:15.10
+  FROM debian:jessie
   
   <%include file="../../apt_get_basic.include"/>
   <%include file="../../gcp_api_libraries.include"/>
@@ -33,14 +33,23 @@
   
   #======================================
   # More sanity test dependencies (bazel)
-  RUN apt-get install -y openjdk-8-jdk
-  # Check out Bazel version 0.4.1 since this version allows running
-  # ./compile.sh without a local protoc dependency
-  # TODO(mattkwong): install dependencies to support latest Bazel version if newer
-  # version is needed
-  RUN git clone https://github.com/bazelbuild/bazel.git /bazel && ${"\\"}
-    cd /bazel && git checkout tags/0.4.1 && ./compile.sh
-  RUN ln -s /bazel/output/bazel /bin/
+  RUN echo "deb http://http.debian.net/debian jessie-backports main" >> /etc/apt/sources.list
+  RUN apt-get update
+  RUN apt-get install -y -t jessie-backports openjdk-8-jdk
+
+  #========================
+  # Bazel installation
+  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
+
+  # Pin Bazel to 0.4.4
+  # Installing Bazel via apt-get first is required before installing 0.4.4 to
+  # allow gRPC to build without errors. See https://github.com/grpc/grpc/issues/10553
+  RUN curl -fSsL -O https://github.com/bazelbuild/bazel/releases/download/0.4.4/bazel-0.4.4-installer-linux-x86_64.sh
+  RUN chmod +x ./bazel-0.4.4-installer-linux-x86_64.sh
+  RUN ./bazel-0.4.4-installer-linux-x86_64.sh
   
   <%include file="../../clang_format.include"/>
   <%include file="../../run_tests_addons.include"/>
diff --git a/test/core/backoff/BUILD b/test/core/backoff/BUILD
new file mode 100644
index 0000000..4ae7620
--- /dev/null
+++ b/test/core/backoff/BUILD
@@ -0,0 +1,36 @@
+# Copyright 2016 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.
+
+load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_cc_binary")
+
+licenses(["notice"])  # Apache v2
+
+package(
+    features = [
+        "-layering_check",
+        "-parse_headers",
+    ],
+)
+
+grpc_cc_test(
+    name = "backoff_test",
+    srcs = ["backoff_test.c"],
+    language = "C",
+    deps = [
+        "//:grpc",
+        "//test/core/util:grpc_test_util",
+        "//:gpr",
+        "//test/core/util:gpr_test_util",
+    ],
+)
diff --git a/test/core/backoff/backoff_test.c b/test/core/backoff/backoff_test.c
new file mode 100644
index 0000000..3848b2a
--- /dev/null
+++ b/test/core/backoff/backoff_test.c
@@ -0,0 +1,149 @@
+/*
+ *
+ * Copyright 2016 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.
+ *
+ */
+
+#include "src/core/lib/backoff/backoff.h"
+
+#include <grpc/support/log.h>
+
+#include "test/core/util/test_config.h"
+
+static void test_constant_backoff(void) {
+  grpc_backoff backoff;
+  grpc_backoff_init(&backoff, 200 /* initial timeout */, 1.0 /* multiplier */,
+                    0.0 /* jitter */, 100 /* min timeout */,
+                    1000 /* max timeout */);
+
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_millis next = grpc_backoff_begin(&exec_ctx, &backoff);
+  GPR_ASSERT(next - grpc_exec_ctx_now(&exec_ctx) == 200);
+  for (int i = 0; i < 10000; i++) {
+    next = grpc_backoff_step(&exec_ctx, &backoff);
+    GPR_ASSERT(next - grpc_exec_ctx_now(&exec_ctx) == 200);
+    exec_ctx.now = next;
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void test_min_connect(void) {
+  grpc_backoff backoff;
+  grpc_backoff_init(&backoff, 100 /* initial timeout */, 1.0 /* multiplier */,
+                    0.0 /* jitter */, 200 /* min timeout */,
+                    1000 /* max timeout */);
+
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_millis next = grpc_backoff_begin(&exec_ctx, &backoff);
+  GPR_ASSERT(next - grpc_exec_ctx_now(&exec_ctx) == 200);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void test_no_jitter_backoff(void) {
+  grpc_backoff backoff;
+  grpc_backoff_init(&backoff, 2 /* initial timeout */, 2.0 /* multiplier */,
+                    0.0 /* jitter */, 1 /* min timeout */,
+                    513 /* max timeout */);
+  // x_1 = 2
+  // x_n = 2**i + x_{i-1} ( = 2**(n+1) - 2 )
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  exec_ctx.now = 0;
+  exec_ctx.now_is_valid = true;
+  grpc_millis next = grpc_backoff_begin(&exec_ctx, &backoff);
+  GPR_ASSERT(next == 2);
+  exec_ctx.now = next;
+  next = grpc_backoff_step(&exec_ctx, &backoff);
+  GPR_ASSERT(next == 6);
+  exec_ctx.now = next;
+  next = grpc_backoff_step(&exec_ctx, &backoff);
+  GPR_ASSERT(next == 14);
+  exec_ctx.now = next;
+  next = grpc_backoff_step(&exec_ctx, &backoff);
+  GPR_ASSERT(next == 30);
+  exec_ctx.now = next;
+  next = grpc_backoff_step(&exec_ctx, &backoff);
+  GPR_ASSERT(next == 62);
+  exec_ctx.now = next;
+  next = grpc_backoff_step(&exec_ctx, &backoff);
+  GPR_ASSERT(next == 126);
+  exec_ctx.now = next;
+  next = grpc_backoff_step(&exec_ctx, &backoff);
+  GPR_ASSERT(next == 254);
+  exec_ctx.now = next;
+  next = grpc_backoff_step(&exec_ctx, &backoff);
+  GPR_ASSERT(next == 510);
+  exec_ctx.now = next;
+  next = grpc_backoff_step(&exec_ctx, &backoff);
+  GPR_ASSERT(next == 1022);
+  exec_ctx.now = next;
+  next = grpc_backoff_step(&exec_ctx, &backoff);
+  // Hit the maximum timeout. From this point onwards, retries will increase
+  // only by max timeout.
+  GPR_ASSERT(next == 1535);
+  exec_ctx.now = next;
+  next = grpc_backoff_step(&exec_ctx, &backoff);
+  GPR_ASSERT(next == 2048);
+  exec_ctx.now = next;
+  next = grpc_backoff_step(&exec_ctx, &backoff);
+  GPR_ASSERT(next == 2561);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void test_jitter_backoff(void) {
+  const int64_t initial_timeout = 500;
+  const double jitter = 0.1;
+  grpc_backoff backoff;
+  grpc_backoff_init(&backoff, initial_timeout, 1.0 /* multiplier */, jitter,
+                    100 /* min timeout */, 1000 /* max timeout */);
+
+  backoff.rng_state = 0;  // force consistent PRNG
+
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_millis next = grpc_backoff_begin(&exec_ctx, &backoff);
+  GPR_ASSERT(next - grpc_exec_ctx_now(&exec_ctx) == 500);
+
+  int64_t expected_next_lower_bound =
+      (int64_t)((double)initial_timeout * (1 - jitter));
+  int64_t expected_next_upper_bound =
+      (int64_t)((double)initial_timeout * (1 + jitter));
+
+  for (int i = 0; i < 10000; i++) {
+    next = grpc_backoff_step(&exec_ctx, &backoff);
+
+    // next-now must be within (jitter*100)% of the previous timeout.
+    const int64_t timeout_millis = next - grpc_exec_ctx_now(&exec_ctx);
+    GPR_ASSERT(timeout_millis >= expected_next_lower_bound);
+    GPR_ASSERT(timeout_millis <= expected_next_upper_bound);
+
+    expected_next_lower_bound =
+        (int64_t)((double)timeout_millis * (1 - jitter));
+    expected_next_upper_bound =
+        (int64_t)((double)timeout_millis * (1 + jitter));
+    exec_ctx.now = next;
+  }
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  gpr_time_init();
+
+  test_constant_backoff();
+  test_min_connect();
+  test_no_jitter_backoff();
+  test_jitter_backoff();
+
+  return 0;
+}
diff --git a/test/core/bad_client/bad_client.c b/test/core/bad_client/bad_client.c
index 888a7c8..b7b28a9 100644
--- a/test/core/bad_client/bad_client.c
+++ b/test/core/bad_client/bad_client.c
@@ -139,9 +139,12 @@
   grpc_endpoint_write(&exec_ctx, sfd.client, &outgoing, &done_write_closure);
   grpc_exec_ctx_finish(&exec_ctx);
 
-  /* Await completion */
-  GPR_ASSERT(
-      gpr_event_wait(&a.done_write, grpc_timeout_seconds_to_deadline(5)));
+  /* Await completion, unless the request is large and write may not finish
+   * before the peer shuts down. */
+  if (!(flags & GRPC_BAD_CLIENT_LARGE_REQUEST)) {
+    GPR_ASSERT(
+        gpr_event_wait(&a.done_write, grpc_timeout_seconds_to_deadline(5)));
+  }
 
   if (flags & GRPC_BAD_CLIENT_DISCONNECT) {
     grpc_endpoint_shutdown(
@@ -191,6 +194,8 @@
     grpc_exec_ctx_finish(&exec_ctx);
   }
 
+  GPR_ASSERT(
+      gpr_event_wait(&a.done_write, grpc_timeout_seconds_to_deadline(1)));
   shutdown_cq = grpc_completion_queue_create_for_pluck(NULL);
   grpc_server_shutdown_and_notify(a.server, shutdown_cq, NULL);
   GPR_ASSERT(grpc_completion_queue_pluck(
diff --git a/test/core/bad_client/bad_client.h b/test/core/bad_client/bad_client.h
index 22f1a3a..a5b01f7 100644
--- a/test/core/bad_client/bad_client.h
+++ b/test/core/bad_client/bad_client.h
@@ -37,6 +37,7 @@
     grpc_slice_buffer *incoming);
 
 #define GRPC_BAD_CLIENT_DISCONNECT 1
+#define GRPC_BAD_CLIENT_LARGE_REQUEST 2
 
 /* Test runner.
 
diff --git a/test/core/bad_client/tests/window_overflow.c b/test/core/bad_client/tests/window_overflow.c
index 9ced956..e4b5f97 100644
--- a/test/core/bad_client/tests/window_overflow.c
+++ b/test/core/bad_client/tests/window_overflow.c
@@ -90,7 +90,8 @@
       addbuf(message, sizeof(message));
     }
   }
-  grpc_run_bad_client_test(verifier, NULL, g_buffer, g_count, 0);
+  grpc_run_bad_client_test(verifier, NULL, g_buffer, g_count,
+                           GRPC_BAD_CLIENT_LARGE_REQUEST);
   gpr_free(g_buffer);
 
   return 0;
diff --git a/test/core/channel/channel_stack_test.c b/test/core/channel/channel_stack_test.c
index 7c3614b..a07ef89 100644
--- a/test/core/channel/channel_stack_test.c
+++ b/test/core/channel/channel_stack_test.c
@@ -125,7 +125,7 @@
       .context = NULL,
       .path = path,
       .start_time = gpr_now(GPR_CLOCK_MONOTONIC),
-      .deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC),
+      .deadline = GRPC_MILLIS_INF_FUTURE,
       .arena = NULL};
   grpc_error *error = grpc_call_stack_init(&exec_ctx, channel_stack, 1,
                                            free_call, call_stack, &args);
diff --git a/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c b/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c
index 364e180..4597285 100644
--- a/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c
+++ b/test/core/client_channel/resolvers/dns_resolver_connectivity_test.c
@@ -106,7 +106,7 @@
     deadline_seconds--;
 
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_timer_check(&exec_ctx, gpr_now(GPR_CLOCK_MONOTONIC), NULL);
+    grpc_timer_check(&exec_ctx, NULL);
     grpc_exec_ctx_finish(&exec_ctx);
   }
   return false;
diff --git a/test/core/compression/stream_compression_test.c b/test/core/compression/stream_compression_test.c
index e576507..afed6cd 100644
--- a/test/core/compression/stream_compression_test.c
+++ b/test/core/compression/stream_compression_test.c
@@ -59,10 +59,11 @@
   grpc_slice_buffer_init(&relay);
   grpc_slice_buffer_init(&sink);
   grpc_stream_compression_context *compress_ctx =
-      grpc_stream_compression_context_create(GRPC_STREAM_COMPRESSION_COMPRESS);
+      grpc_stream_compression_context_create(
+          GRPC_STREAM_COMPRESSION_GZIP_COMPRESS);
   grpc_stream_compression_context *decompress_ctx =
       grpc_stream_compression_context_create(
-          GRPC_STREAM_COMPRESSION_DECOMPRESS);
+          GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS);
   grpc_slice slice = grpc_slice_from_static_string(test_str);
   grpc_slice_buffer_add(&source, slice);
   GPR_ASSERT(grpc_stream_compress(compress_ctx, &source, &relay, NULL,
@@ -91,10 +92,11 @@
   grpc_slice_buffer_init(&relay);
   grpc_slice_buffer_init(&sink);
   grpc_stream_compression_context *compress_ctx =
-      grpc_stream_compression_context_create(GRPC_STREAM_COMPRESSION_COMPRESS);
+      grpc_stream_compression_context_create(
+          GRPC_STREAM_COMPRESSION_GZIP_COMPRESS);
   grpc_stream_compression_context *decompress_ctx =
       grpc_stream_compression_context_create(
-          GRPC_STREAM_COMPRESSION_DECOMPRESS);
+          GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS);
   grpc_slice slice = grpc_slice_from_static_string(test_str);
   grpc_slice_buffer_add(&source, slice);
   GPR_ASSERT(grpc_stream_compress(compress_ctx, &source, &relay, NULL,
@@ -139,10 +141,11 @@
   grpc_slice_buffer_init(&relay);
   grpc_slice_buffer_init(&sink);
   grpc_stream_compression_context *compress_ctx =
-      grpc_stream_compression_context_create(GRPC_STREAM_COMPRESSION_COMPRESS);
+      grpc_stream_compression_context_create(
+          GRPC_STREAM_COMPRESSION_GZIP_COMPRESS);
   grpc_stream_compression_context *decompress_ctx =
       grpc_stream_compression_context_create(
-          GRPC_STREAM_COMPRESSION_DECOMPRESS);
+          GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS);
   grpc_slice slice = grpc_slice_from_static_string(test_str);
   grpc_slice_buffer_add(&source, slice);
   GPR_ASSERT(grpc_stream_compress(compress_ctx, &source, &relay, NULL,
@@ -172,7 +175,8 @@
   grpc_slice_buffer_init(&relay);
   grpc_slice_buffer_init(&sink);
   grpc_stream_compression_context *compress_ctx =
-      grpc_stream_compression_context_create(GRPC_STREAM_COMPRESSION_COMPRESS);
+      grpc_stream_compression_context_create(
+          GRPC_STREAM_COMPRESSION_GZIP_COMPRESS);
   grpc_slice slice = grpc_slice_from_static_string(test_str);
   grpc_slice_buffer_add(&source, slice);
   GPR_ASSERT(grpc_stream_compress(compress_ctx, &source, &relay, NULL,
@@ -180,8 +184,8 @@
                                   GRPC_STREAM_COMPRESSION_FLUSH_FINISH));
   grpc_stream_compression_context_destroy(compress_ctx);
 
-  compress_ctx =
-      grpc_stream_compression_context_create(GRPC_STREAM_COMPRESSION_COMPRESS);
+  compress_ctx = grpc_stream_compression_context_create(
+      GRPC_STREAM_COMPRESSION_GZIP_COMPRESS);
   slice = grpc_slice_from_static_string(test_str2);
   grpc_slice_buffer_add(&source, slice);
   GPR_ASSERT(grpc_stream_compress(compress_ctx, &source, &relay, NULL,
@@ -205,7 +209,7 @@
 
   grpc_stream_compression_context *decompress_ctx =
       grpc_stream_compression_context_create(
-          GRPC_STREAM_COMPRESSION_DECOMPRESS);
+          GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS);
   bool end_of_context;
   size_t output_size;
   GPR_ASSERT(grpc_stream_decompress(decompress_ctx, &relay, &sink, &output_size,
@@ -219,7 +223,7 @@
 
   grpc_slice_buffer_init(&sink);
   decompress_ctx = grpc_stream_compression_context_create(
-      GRPC_STREAM_COMPRESSION_DECOMPRESS);
+      GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS);
   GPR_ASSERT(grpc_stream_decompress(decompress_ctx, &relay, &sink, &output_size,
                                     ~(size_t)0, &end_of_context));
   GPR_ASSERT(end_of_context == true);
@@ -240,7 +244,8 @@
   grpc_slice_buffer_init(&relay);
   grpc_slice_buffer_init(&sink);
   grpc_stream_compression_context *compress_ctx =
-      grpc_stream_compression_context_create(GRPC_STREAM_COMPRESSION_COMPRESS);
+      grpc_stream_compression_context_create(
+          GRPC_STREAM_COMPRESSION_GZIP_COMPRESS);
   grpc_slice slice = grpc_slice_from_static_string(test_str);
   grpc_slice_buffer_add(&source, slice);
   GPR_ASSERT(grpc_stream_compress(compress_ctx, &source, &relay, NULL,
@@ -249,7 +254,7 @@
 
   grpc_stream_compression_context *decompress_ctx =
       grpc_stream_compression_context_create(
-          GRPC_STREAM_COMPRESSION_DECOMPRESS);
+          GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS);
   bool end_of_context;
   size_t output_size;
   GPR_ASSERT(grpc_stream_decompress(decompress_ctx, &relay, &sink, &output_size,
diff --git a/test/core/end2end/fixtures/h2_ssl_cert.c b/test/core/end2end/fixtures/h2_ssl_cert.c
index 9b1ddad..f0a2ee5 100644
--- a/test/core/end2end/fixtures/h2_ssl_cert.c
+++ b/test/core/end2end/fixtures/h2_ssl_cert.c
@@ -319,7 +319,7 @@
   op = ops;
   op->op = GRPC_OP_SEND_INITIAL_METADATA;
   op->data.send_initial_metadata.count = 0;
-  op->flags = 0;
+  op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY;
   op->reserved = NULL;
   op++;
   error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
diff --git a/test/core/end2end/fixtures/http_proxy_fixture.c b/test/core/end2end/fixtures/http_proxy_fixture.c
index a4cfc77..d29401f 100644
--- a/test/core/end2end/fixtures/http_proxy_fixture.c
+++ b/test/core/end2end/fixtures/http_proxy_fixture.c
@@ -412,8 +412,8 @@
   GPR_ASSERT(resolved_addresses->naddrs >= 1);
   // Connect to requested address.
   // The connection callback inherits our reference to conn.
-  const gpr_timespec deadline = gpr_time_add(
-      gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(10, GPR_TIMESPAN));
+  const grpc_millis deadline =
+      grpc_exec_ctx_now(exec_ctx) + 10 * GPR_MS_PER_SEC;
   grpc_tcp_client_connect(exec_ctx, &conn->on_server_connect_done,
                           &conn->server_endpoint, conn->pollset_set, NULL,
                           &resolved_addresses->addrs[0], deadline);
@@ -469,14 +469,12 @@
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   do {
     gpr_ref(&proxy->users);
-    const gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
-    const gpr_timespec deadline =
-        gpr_time_add(now, gpr_time_from_seconds(1, GPR_TIMESPAN));
     grpc_pollset_worker* worker = NULL;
     gpr_mu_lock(proxy->mu);
     GRPC_LOG_IF_ERROR(
         "grpc_pollset_work",
-        grpc_pollset_work(&exec_ctx, proxy->pollset, &worker, now, deadline));
+        grpc_pollset_work(&exec_ctx, proxy->pollset, &worker,
+                          grpc_exec_ctx_now(&exec_ctx) + GPR_MS_PER_SEC));
     gpr_mu_unlock(proxy->mu);
     grpc_exec_ctx_flush(&exec_ctx);
   } while (!gpr_unref(&proxy->users));
diff --git a/test/core/end2end/fixtures/proxy.c b/test/core/end2end/fixtures/proxy.c
index 9ad8627..6a2d75d 100644
--- a/test/core/end2end/fixtures/proxy.c
+++ b/test/core/end2end/fixtures/proxy.c
@@ -227,6 +227,10 @@
                                   new_closure(on_p2s_sent_close, pc), NULL);
       GPR_ASSERT(err == GRPC_CALL_OK);
     }
+  } else {
+    if (pc->c2p_msg != NULL) {
+      grpc_byte_buffer_destroy(pc->c2p_msg);
+    }
   }
 
   unrefpc(pc, "on_c2p_recv_msg");
diff --git a/test/core/end2end/fuzzers/api_fuzzer.c b/test/core/end2end/fuzzers/api_fuzzer.c
index 1228c9f..0a787bb 100644
--- a/test/core/end2end/fuzzers/api_fuzzer.c
+++ b/test/core/end2end/fuzzers/api_fuzzer.c
@@ -60,7 +60,9 @@
 
 static gpr_timespec now_impl(gpr_clock_type clock_type) {
   GPR_ASSERT(clock_type != GPR_TIMESPAN);
-  return g_now;
+  gpr_timespec ts = g_now;
+  ts.clock_type = clock_type;
+  return ts;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -407,10 +409,8 @@
   r->addrs = addresses;
   r->lb_addrs = NULL;
   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));
+      exec_ctx, &r->timer, GPR_MS_PER_SEC + grpc_exec_ctx_now(exec_ctx),
+      GRPC_CLOSURE_CREATE(finish_resolve, r, grpc_schedule_on_exec_ctx));
 }
 
 grpc_ares_request *my_dns_lookup_ares(
@@ -424,10 +424,8 @@
   r->addrs = NULL;
   r->lb_addrs = lb_addrs;
   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));
+      exec_ctx, &r->timer, GPR_MS_PER_SEC + grpc_exec_ctx_now(exec_ctx),
+      GRPC_CLOSURE_CREATE(finish_resolve, r, grpc_schedule_on_exec_ctx));
   return NULL;
 }
 
@@ -487,10 +485,8 @@
   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)),
-      GRPC_CLOSURE_CREATE(do_connect, fc, grpc_schedule_on_exec_ctx),
-      gpr_now(GPR_CLOCK_MONOTONIC));
+      exec_ctx, &fc->timer, GPR_MS_PER_SEC + grpc_exec_ctx_now(exec_ctx),
+      GRPC_CLOSURE_CREATE(do_connect, fc, grpc_schedule_on_exec_ctx));
 }
 
 static void my_tcp_client_connect(grpc_exec_ctx *exec_ctx,
diff --git a/test/core/end2end/invalid_call_argument_test.c b/test/core/end2end/invalid_call_argument_test.c
index bf0d08a..e3fd5a8 100644
--- a/test/core/end2end/invalid_call_argument_test.c
+++ b/test/core/end2end/invalid_call_argument_test.c
@@ -92,7 +92,7 @@
     op = g_state.ops;
     op->op = GRPC_OP_SEND_INITIAL_METADATA;
     op->data.send_initial_metadata.count = 0;
-    op->flags = 0;
+    op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY;
     op->reserved = NULL;
     op++;
     GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(g_state.call, g_state.ops,
diff --git a/test/core/end2end/tests/bad_ping.c b/test/core/end2end/tests/bad_ping.c
index c97d11b..d442f12 100644
--- a/test/core/end2end/tests/bad_ping.c
+++ b/test/core/end2end/tests/bad_ping.c
@@ -155,14 +155,16 @@
   cq_verify(cqv);
 
   // Send too many pings to the server to trigger the punishment:
-  // The first ping is sent after data frames, it won't trigger a ping strike.
-  // Each of the following pings will trigger a ping strike, and we need at
-  // least (MAX_PING_STRIKES + 1) strikes to trigger the punishment. So
-  // (MAX_PING_STRIKES + 2) pings are needed here.
+  // Each ping will trigger a ping strike, and we need at least MAX_PING_STRIKES
+  // strikes to trigger the punishment. So (MAX_PING_STRIKES + 1) pings are
+  // needed here.
   int i;
-  for (i = 200; i < 202 + MAX_PING_STRIKES; i++) {
-    grpc_channel_ping(f.client, f.cq, tag(i), NULL);
-    CQ_EXPECT_COMPLETION(cqv, tag(i), 1);
+  for (i = 1; i <= MAX_PING_STRIKES + 1; i++) {
+    grpc_channel_ping(f.client, f.cq, tag(200 + i), NULL);
+    CQ_EXPECT_COMPLETION(cqv, tag(200 + i), 1);
+    if (i == MAX_PING_STRIKES + 1) {
+      CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
+    }
     cq_verify(cqv);
   }
 
@@ -190,7 +192,6 @@
   GPR_ASSERT(GRPC_CALL_OK == error);
 
   CQ_EXPECT_COMPLETION(cqv, tag(102), 1);
-  CQ_EXPECT_COMPLETION(cqv, tag(1), 1);
   cq_verify(cqv);
 
   grpc_server_shutdown_and_notify(f.server, f.cq, tag(0xdead));
diff --git a/test/core/end2end/tests/compressed_payload.c b/test/core/end2end/tests/compressed_payload.c
index 639af15..ba03773 100644
--- a/test/core/end2end/tests/compressed_payload.c
+++ b/test/core/end2end/tests/compressed_payload.c
@@ -151,6 +151,11 @@
   grpc_metadata_array_init(&request_metadata_recv);
   grpc_call_details_init(&call_details);
 
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_SEND_INITIAL_METADATA;
@@ -187,11 +192,8 @@
   error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  error =
-      grpc_server_request_call(f.server, &s, &call_details,
-                               &request_metadata_recv, f.cq, f.cq, tag(101));
-  GPR_ASSERT(GRPC_CALL_OK == error);
   CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
   cq_verify(cqv);
 
   op = ops;
@@ -220,7 +222,6 @@
   GPR_ASSERT(GRPC_CALL_OK == error);
 
   CQ_EXPECT_COMPLETION(cqv, tag(103), true);
-  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
   cq_verify(cqv);
 
   /* call was cancelled (closed) ... */
diff --git a/test/core/end2end/tests/simple_request.c b/test/core/end2end/tests/simple_request.c
index 82ab0a1..7ce7e1f 100644
--- a/test/core/end2end/tests/simple_request.c
+++ b/test/core/end2end/tests/simple_request.c
@@ -103,10 +103,10 @@
   grpc_slice details;
   int was_cancelled = 2;
   char *peer;
-  grpc_stats_data before;
-  grpc_stats_data after;
+  grpc_stats_data *before = gpr_malloc(sizeof(grpc_stats_data));
+  grpc_stats_data *after = gpr_malloc(sizeof(grpc_stats_data));
 
-  grpc_stats_collect(&before);
+  grpc_stats_collect(before);
 
   gpr_timespec deadline = five_seconds_from_now();
   c = grpc_channel_create_call(
@@ -214,9 +214,9 @@
 
   cq_verifier_destroy(cqv);
 
-  grpc_stats_collect(&after);
+  grpc_stats_collect(after);
 
-  char *stats = grpc_stats_data_as_json(&after);
+  char *stats = grpc_stats_data_as_json(after);
   gpr_log(GPR_DEBUG, "%s", stats);
   gpr_free(stats);
 
@@ -224,12 +224,14 @@
   if (config.feature_mask & FEATURE_MASK_SUPPORTS_REQUEST_PROXYING) {
     expected_calls *= 2;
   }
-  GPR_ASSERT(after.counters[GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED] -
-                 before.counters[GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED] ==
+  GPR_ASSERT(after->counters[GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED] -
+                 before->counters[GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED] ==
              expected_calls);
-  GPR_ASSERT(after.counters[GRPC_STATS_COUNTER_SERVER_CALLS_CREATED] -
-                 before.counters[GRPC_STATS_COUNTER_SERVER_CALLS_CREATED] ==
+  GPR_ASSERT(after->counters[GRPC_STATS_COUNTER_SERVER_CALLS_CREATED] -
+                 before->counters[GRPC_STATS_COUNTER_SERVER_CALLS_CREATED] ==
              expected_calls);
+  gpr_free(before);
+  gpr_free(after);
 }
 
 static void test_invoke_simple_request(grpc_end2end_test_config config) {
diff --git a/test/core/end2end/tests/stream_compression_compressed_payload.c b/test/core/end2end/tests/stream_compression_compressed_payload.c
index 5a69091..8b47741 100644
--- a/test/core/end2end/tests/stream_compression_compressed_payload.c
+++ b/test/core/end2end/tests/stream_compression_compressed_payload.c
@@ -151,6 +151,11 @@
   grpc_metadata_array_init(&request_metadata_recv);
   grpc_call_details_init(&call_details);
 
+  error =
+      grpc_server_request_call(f.server, &s, &call_details,
+                               &request_metadata_recv, f.cq, f.cq, tag(101));
+  GPR_ASSERT(GRPC_CALL_OK == error);
+
   memset(ops, 0, sizeof(ops));
   op = ops;
   op->op = GRPC_OP_SEND_INITIAL_METADATA;
@@ -187,11 +192,8 @@
   error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL);
   GPR_ASSERT(GRPC_CALL_OK == error);
 
-  error =
-      grpc_server_request_call(f.server, &s, &call_details,
-                               &request_metadata_recv, f.cq, f.cq, tag(101));
-  GPR_ASSERT(GRPC_CALL_OK == error);
   CQ_EXPECT_COMPLETION(cqv, tag(101), true);
+  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
   cq_verify(cqv);
 
   op = ops;
@@ -220,7 +222,6 @@
   GPR_ASSERT(GRPC_CALL_OK == error);
 
   CQ_EXPECT_COMPLETION(cqv, tag(103), true);
-  CQ_EXPECT_COMPLETION(cqv, tag(1), true);
   cq_verify(cqv);
 
   /* call was cancelled (closed) ... */
diff --git a/test/core/http/httpcli_test.c b/test/core/http/httpcli_test.c
index 8a53903..cc1c16d 100644
--- a/test/core/http/httpcli_test.c
+++ b/test/core/http/httpcli_test.c
@@ -35,8 +35,9 @@
 static gpr_mu *g_mu;
 static grpc_polling_entity g_pops;
 
-static gpr_timespec n_seconds_time(int seconds) {
-  return grpc_timeout_seconds_to_deadline(seconds);
+static grpc_millis n_seconds_time(int seconds) {
+  return grpc_timespec_to_millis_round_up(
+      grpc_timeout_seconds_to_deadline(seconds));
 }
 
 static void on_finish(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
@@ -86,8 +87,7 @@
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
         grpc_pollset_work(&exec_ctx, grpc_polling_entity_pollset(&g_pops),
-                          &worker, gpr_now(GPR_CLOCK_MONOTONIC),
-                          n_seconds_time(1))));
+                          &worker, n_seconds_time(1))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -128,8 +128,7 @@
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
         grpc_pollset_work(&exec_ctx, grpc_polling_entity_pollset(&g_pops),
-                          &worker, gpr_now(GPR_CLOCK_MONOTONIC),
-                          n_seconds_time(1))));
+                          &worker, n_seconds_time(1))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
diff --git a/test/core/http/httpscli_test.c b/test/core/http/httpscli_test.c
index c7455bd..f8a3cfd 100644
--- a/test/core/http/httpscli_test.c
+++ b/test/core/http/httpscli_test.c
@@ -35,8 +35,9 @@
 static gpr_mu *g_mu;
 static grpc_polling_entity g_pops;
 
-static gpr_timespec n_seconds_time(int seconds) {
-  return grpc_timeout_seconds_to_deadline(seconds);
+static grpc_millis n_seconds_time(int seconds) {
+  return grpc_timespec_to_millis_round_up(
+      grpc_timeout_seconds_to_deadline(seconds));
 }
 
 static void on_finish(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
@@ -87,8 +88,7 @@
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
         grpc_pollset_work(&exec_ctx, grpc_polling_entity_pollset(&g_pops),
-                          &worker, gpr_now(GPR_CLOCK_MONOTONIC),
-                          n_seconds_time(1))));
+                          &worker, n_seconds_time(1))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -130,8 +130,7 @@
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
         grpc_pollset_work(&exec_ctx, grpc_polling_entity_pollset(&g_pops),
-                          &worker, gpr_now(GPR_CLOCK_MONOTONIC),
-                          n_seconds_time(1))));
+                          &worker, n_seconds_time(1))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
diff --git a/test/core/iomgr/endpoint_tests.c b/test/core/iomgr/endpoint_tests.c
index f8570ed..61e901f 100644
--- a/test/core/iomgr/endpoint_tests.c
+++ b/test/core/iomgr/endpoint_tests.c
@@ -176,10 +176,11 @@
                                 size_t num_bytes, size_t write_size,
                                 size_t slice_size, bool shutdown) {
   struct read_and_write_test_state state;
-  gpr_timespec deadline = grpc_timeout_seconds_to_deadline(20);
   grpc_endpoint_test_fixture f =
       begin_test(config, "read_and_write_test", slice_size);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_millis deadline =
+      grpc_timespec_to_millis_round_up(grpc_timeout_seconds_to_deadline(20));
   gpr_log(GPR_DEBUG, "num_bytes=%" PRIuPTR " write_size=%" PRIuPTR
                      " slice_size=%" PRIuPTR " shutdown=%d",
           num_bytes, write_size, slice_size, shutdown);
@@ -235,11 +236,10 @@
   gpr_mu_lock(g_mu);
   while (!state.read_done || !state.write_done) {
     grpc_pollset_worker *worker = NULL;
-    GPR_ASSERT(gpr_time_cmp(gpr_now(GPR_CLOCK_MONOTONIC), deadline) < 0);
+    GPR_ASSERT(grpc_exec_ctx_now(&exec_ctx) < deadline);
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
-        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker, deadline)));
   }
   gpr_mu_unlock(g_mu);
   grpc_exec_ctx_flush(&exec_ctx);
@@ -265,14 +265,14 @@
                                 int want_fail_count) {
   grpc_exec_ctx_flush(exec_ctx);
   gpr_mu_lock(g_mu);
-  gpr_timespec deadline = grpc_timeout_seconds_to_deadline(10);
-  while (gpr_time_cmp(gpr_now(deadline.clock_type), deadline) < 0 &&
+  grpc_millis deadline =
+      grpc_timespec_to_millis_round_up(grpc_timeout_seconds_to_deadline(10));
+  while (grpc_exec_ctx_now(exec_ctx) < deadline &&
          *fail_count < want_fail_count) {
     grpc_pollset_worker *worker = NULL;
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
-        grpc_pollset_work(exec_ctx, g_pollset, &worker,
-                          gpr_now(deadline.clock_type), deadline)));
+        grpc_pollset_work(exec_ctx, g_pollset, &worker, deadline)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_flush(exec_ctx);
     gpr_mu_lock(g_mu);
diff --git a/test/core/iomgr/ev_epollsig_linux_test.c b/test/core/iomgr/ev_epollsig_linux_test.c
index cca07bf..37aadac 100644
--- a/test/core/iomgr/ev_epollsig_linux_test.c
+++ b/test/core/iomgr/ev_epollsig_linux_test.c
@@ -238,10 +238,8 @@
     grpc_pollset_worker *worker;
     gpr_mu_lock(shared->mu);
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
-        "pollset_work",
-        grpc_pollset_work(&exec_ctx, shared->pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC),
-                          gpr_inf_future(GPR_CLOCK_MONOTONIC))));
+        "pollset_work", grpc_pollset_work(&exec_ctx, shared->pollset, &worker,
+                                          GRPC_MILLIS_INF_FUTURE)));
     gpr_mu_unlock(shared->mu);
     grpc_exec_ctx_finish(&exec_ctx);
   }
diff --git a/test/core/iomgr/fd_posix_test.c b/test/core/iomgr/fd_posix_test.c
index 881277a..1c62f34 100644
--- a/test/core/iomgr/fd_posix_test.c
+++ b/test/core/iomgr/fd_posix_test.c
@@ -252,10 +252,8 @@
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     grpc_pollset_worker *worker = NULL;
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
-        "pollset_work",
-        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC),
-                          gpr_inf_future(GPR_CLOCK_MONOTONIC))));
+        "pollset_work", grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                                          GRPC_MILLIS_INF_FUTURE)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -371,10 +369,8 @@
     grpc_pollset_worker *worker = NULL;
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
-        "pollset_work",
-        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC),
-                          gpr_inf_future(GPR_CLOCK_MONOTONIC))));
+        "pollset_work", grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                                          GRPC_MILLIS_INF_FUTURE)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -477,10 +473,8 @@
   while (a.cb_that_ran == NULL) {
     grpc_pollset_worker *worker = NULL;
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
-        "pollset_work",
-        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC),
-                          gpr_inf_future(GPR_CLOCK_MONOTONIC))));
+        "pollset_work", grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                                          GRPC_MILLIS_INF_FUTURE)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -503,10 +497,8 @@
   while (b.cb_that_ran == NULL) {
     grpc_pollset_worker *worker = NULL;
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
-        "pollset_work",
-        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC),
-                          gpr_inf_future(GPR_CLOCK_MONOTONIC))));
+        "pollset_work", grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                                          GRPC_MILLIS_INF_FUTURE)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
diff --git a/test/core/iomgr/pollset_set_test.c b/test/core/iomgr/pollset_set_test.c
index 70efca8..3dd4bc8 100644
--- a/test/core/iomgr/pollset_set_test.c
+++ b/test/core/iomgr/pollset_set_test.c
@@ -24,7 +24,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
@@ -204,7 +203,7 @@
    */
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_pollset_worker *worker;
-  gpr_timespec deadline;
+  grpc_millis deadline;
 
   test_fd tfds[10];
   test_pollset pollsets[3];
@@ -257,10 +256,10 @@
     make_test_fds_readable(tfds, num_fds);
 
     gpr_mu_lock(pollsets[i].mu);
-    deadline = grpc_timeout_milliseconds_to_deadline(2);
+    deadline = grpc_timespec_to_millis_round_up(
+        grpc_timeout_milliseconds_to_deadline(2));
     GPR_ASSERT(GRPC_ERROR_NONE ==
-               grpc_pollset_work(&exec_ctx, pollsets[i].ps, &worker,
-                                 gpr_now(GPR_CLOCK_MONOTONIC), deadline));
+               grpc_pollset_work(&exec_ctx, pollsets[i].ps, &worker, deadline));
     gpr_mu_unlock(pollsets[i].mu);
 
     grpc_exec_ctx_flush(&exec_ctx);
@@ -309,7 +308,7 @@
    */
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_pollset_worker *worker;
-  gpr_timespec deadline;
+  grpc_millis deadline;
 
   test_fd tfds[3];
   test_pollset pollset;
@@ -339,10 +338,10 @@
   make_test_fds_readable(tfds, num_fds);
 
   gpr_mu_lock(pollset.mu);
-  deadline = grpc_timeout_milliseconds_to_deadline(2);
+  deadline = grpc_timespec_to_millis_round_up(
+      grpc_timeout_milliseconds_to_deadline(2));
   GPR_ASSERT(GRPC_ERROR_NONE ==
-             grpc_pollset_work(&exec_ctx, pollset.ps, &worker,
-                               gpr_now(GPR_CLOCK_MONOTONIC), deadline));
+             grpc_pollset_work(&exec_ctx, pollset.ps, &worker, deadline));
   gpr_mu_unlock(pollset.mu);
   grpc_exec_ctx_flush(&exec_ctx);
 
@@ -382,7 +381,7 @@
    */
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_pollset_worker *worker;
-  gpr_timespec deadline;
+  grpc_millis deadline;
 
   test_fd tfds[3];
   test_pollset pollsets[2];
@@ -408,10 +407,10 @@
   make_test_fds_readable(tfds, num_fds);
 
   gpr_mu_lock(pollsets[0].mu);
-  deadline = grpc_timeout_milliseconds_to_deadline(2);
+  deadline = grpc_timespec_to_millis_round_up(
+      grpc_timeout_milliseconds_to_deadline(2));
   GPR_ASSERT(GRPC_ERROR_NONE ==
-             grpc_pollset_work(&exec_ctx, pollsets[0].ps, &worker,
-                               gpr_now(GPR_CLOCK_MONOTONIC), deadline));
+             grpc_pollset_work(&exec_ctx, pollsets[0].ps, &worker, deadline));
   gpr_mu_unlock(pollsets[0].mu);
   grpc_exec_ctx_flush(&exec_ctx);
 
@@ -434,7 +433,8 @@
   const char *poll_strategy = grpc_get_poll_strategy_name();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_test_init(argc, argv);
-  grpc_init();
+  grpc_iomgr_init(&exec_ctx);
+  grpc_iomgr_start(&exec_ctx);
 
   if (poll_strategy != NULL &&
       (strcmp(poll_strategy, "epoll") == 0 ||
@@ -449,8 +449,8 @@
             poll_strategy);
   }
 
+  grpc_iomgr_shutdown(&exec_ctx);
   grpc_exec_ctx_finish(&exec_ctx);
-  grpc_shutdown();
   return 0;
 }
 #else /* defined(GRPC_LINUX_EPOLL) */
diff --git a/test/core/iomgr/resolve_address_posix_test.c b/test/core/iomgr/resolve_address_posix_test.c
index e4be99f..cb9d608 100644
--- a/test/core/iomgr/resolve_address_posix_test.c
+++ b/test/core/iomgr/resolve_address_posix_test.c
@@ -72,35 +72,33 @@
   gpr_free(args->pollset);
 }
 
-static gpr_timespec n_sec_deadline(int seconds) {
-  return gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
-                      gpr_time_from_seconds(seconds, GPR_TIMESPAN));
+static grpc_millis n_sec_deadline(int seconds) {
+  return grpc_timespec_to_millis_round_up(
+      grpc_timeout_seconds_to_deadline(seconds));
 }
 
 static void actually_poll(void *argsp) {
   args_struct *args = argsp;
-  gpr_timespec deadline = n_sec_deadline(10);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_millis deadline = n_sec_deadline(10);
   while (true) {
     bool done = gpr_atm_acq_load(&args->done_atm) != 0;
     if (done) {
       break;
     }
-    gpr_timespec time_left =
-        gpr_time_sub(deadline, gpr_now(GPR_CLOCK_REALTIME));
-    gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRId64 ".%09d", done,
-            time_left.tv_sec, time_left.tv_nsec);
-    GPR_ASSERT(gpr_time_cmp(time_left, gpr_time_0(GPR_TIMESPAN)) >= 0);
+    grpc_millis time_left = deadline - grpc_exec_ctx_now(&exec_ctx);
+    gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRIdPTR, done, time_left);
+    GPR_ASSERT(time_left >= 0);
     grpc_pollset_worker *worker = NULL;
-    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     gpr_mu_lock(args->mu);
-    GRPC_LOG_IF_ERROR(
-        "pollset_work",
-        grpc_pollset_work(&exec_ctx, args->pollset, &worker,
-                          gpr_now(GPR_CLOCK_REALTIME), n_sec_deadline(1)));
+    GRPC_LOG_IF_ERROR("pollset_work",
+                      grpc_pollset_work(&exec_ctx, args->pollset, &worker,
+                                        n_sec_deadline(1)));
     gpr_mu_unlock(args->mu);
-    grpc_exec_ctx_finish(&exec_ctx);
+    grpc_exec_ctx_flush(&exec_ctx);
   }
   gpr_event_set(&args->ev, (void *)1);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void poll_pollset_until_request_done(args_struct *args) {
diff --git a/test/core/iomgr/resolve_address_test.c b/test/core/iomgr/resolve_address_test.c
index 1110c04..178bbbb 100644
--- a/test/core/iomgr/resolve_address_test.c
+++ b/test/core/iomgr/resolve_address_test.c
@@ -68,34 +68,32 @@
   gpr_free(args->pollset);
 }
 
-static gpr_timespec n_sec_deadline(int seconds) {
-  return gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
-                      gpr_time_from_seconds(seconds, GPR_TIMESPAN));
+static grpc_millis n_sec_deadline(int seconds) {
+  return grpc_timespec_to_millis_round_up(
+      grpc_timeout_seconds_to_deadline(seconds));
 }
 
 static void poll_pollset_until_request_done(args_struct *args) {
-  gpr_timespec deadline = n_sec_deadline(10);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_millis deadline = n_sec_deadline(10);
   while (true) {
     bool done = gpr_atm_acq_load(&args->done_atm) != 0;
     if (done) {
       break;
     }
-    gpr_timespec time_left =
-        gpr_time_sub(deadline, gpr_now(GPR_CLOCK_REALTIME));
-    gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRId64 ".%09d", done,
-            time_left.tv_sec, time_left.tv_nsec);
-    GPR_ASSERT(gpr_time_cmp(time_left, gpr_time_0(GPR_TIMESPAN)) >= 0);
+    grpc_millis time_left = deadline - grpc_exec_ctx_now(&exec_ctx);
+    gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRIdPTR, done, time_left);
+    GPR_ASSERT(time_left >= 0);
     grpc_pollset_worker *worker = NULL;
-    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     gpr_mu_lock(args->mu);
-    GRPC_LOG_IF_ERROR(
-        "pollset_work",
-        grpc_pollset_work(&exec_ctx, args->pollset, &worker,
-                          gpr_now(GPR_CLOCK_REALTIME), n_sec_deadline(1)));
+    GRPC_LOG_IF_ERROR("pollset_work",
+                      grpc_pollset_work(&exec_ctx, args->pollset, &worker,
+                                        n_sec_deadline(1)));
     gpr_mu_unlock(args->mu);
-    grpc_exec_ctx_finish(&exec_ctx);
+    grpc_exec_ctx_flush(&exec_ctx);
   }
   gpr_event_set(&args->ev, (void *)1);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void must_succeed(grpc_exec_ctx *exec_ctx, void *argsp,
diff --git a/test/core/iomgr/tcp_client_posix_test.c b/test/core/iomgr/tcp_client_posix_test.c
index 1032da9..b8b76d1 100644
--- a/test/core/iomgr/tcp_client_posix_test.c
+++ b/test/core/iomgr/tcp_client_posix_test.c
@@ -46,8 +46,8 @@
 static int g_connections_complete = 0;
 static grpc_endpoint *g_connecting = NULL;
 
-static gpr_timespec test_deadline(void) {
-  return grpc_timeout_seconds_to_deadline(10);
+static grpc_millis test_deadline(void) {
+  return grpc_timespec_to_millis_round_up(grpc_timeout_seconds_to_deadline(10));
 }
 
 static void finish_connection() {
@@ -109,7 +109,7 @@
                          (socklen_t *)&resolved_addr.len) == 0);
   GRPC_CLOSURE_INIT(&done, must_succeed, NULL, grpc_schedule_on_exec_ctx);
   grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, g_pollset_set, NULL,
-                          &resolved_addr, gpr_inf_future(GPR_CLOCK_REALTIME));
+                          &resolved_addr, GRPC_MILLIS_INF_FUTURE);
 
   /* await the connection */
   do {
@@ -127,8 +127,8 @@
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
         grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC),
-                          grpc_timeout_seconds_to_deadline(5))));
+                          grpc_timespec_to_millis_round_up(
+                              grpc_timeout_seconds_to_deadline(5)))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_flush(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -159,25 +159,24 @@
   /* connect to a broken address */
   GRPC_CLOSURE_INIT(&done, must_fail, NULL, grpc_schedule_on_exec_ctx);
   grpc_tcp_client_connect(&exec_ctx, &done, &g_connecting, g_pollset_set, NULL,
-                          &resolved_addr, gpr_inf_future(GPR_CLOCK_REALTIME));
+                          &resolved_addr, GRPC_MILLIS_INF_FUTURE);
 
   gpr_mu_lock(g_mu);
 
   /* wait for the connection callback to finish */
   while (g_connections_complete == connections_complete_before) {
     grpc_pollset_worker *worker = NULL;
-    gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
-    gpr_timespec polling_deadline = test_deadline();
-    switch (grpc_timer_check(&exec_ctx, now, &polling_deadline)) {
+    grpc_millis polling_deadline = test_deadline();
+    switch (grpc_timer_check(&exec_ctx, &polling_deadline)) {
       case GRPC_TIMERS_FIRED:
         break;
       case GRPC_TIMERS_NOT_CHECKED:
-        polling_deadline = now;
+        polling_deadline = 0;
       /* fall through */
       case GRPC_TIMERS_CHECKED_AND_EMPTY:
         GPR_ASSERT(GRPC_LOG_IF_ERROR(
             "pollset_work", grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                                              now, polling_deadline)));
+                                              polling_deadline)));
         break;
     }
     gpr_mu_unlock(g_mu);
diff --git a/test/core/iomgr/tcp_posix_test.c b/test/core/iomgr/tcp_posix_test.c
index cfb3cf8..6501160 100644
--- a/test/core/iomgr/tcp_posix_test.c
+++ b/test/core/iomgr/tcp_posix_test.c
@@ -162,7 +162,8 @@
   grpc_endpoint *ep;
   struct read_socket_state state;
   size_t written_bytes;
-  gpr_timespec deadline = grpc_timeout_seconds_to_deadline(20);
+  grpc_millis deadline =
+      grpc_timespec_to_millis_round_up(grpc_timeout_seconds_to_deadline(20));
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
   gpr_log(GPR_INFO, "Read test of size %" PRIuPTR ", slice size %" PRIuPTR,
@@ -194,8 +195,7 @@
     grpc_pollset_worker *worker = NULL;
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
-        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker, deadline)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -215,7 +215,8 @@
   grpc_endpoint *ep;
   struct read_socket_state state;
   ssize_t written_bytes;
-  gpr_timespec deadline = grpc_timeout_seconds_to_deadline(20);
+  grpc_millis deadline =
+      grpc_timespec_to_millis_round_up(grpc_timeout_seconds_to_deadline(20));
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
   gpr_log(GPR_INFO, "Start large read test, slice size %" PRIuPTR, slice_size);
@@ -246,8 +247,7 @@
     grpc_pollset_worker *worker = NULL;
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
-        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker, deadline)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -319,8 +319,8 @@
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
         grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC),
-                          grpc_timeout_milliseconds_to_deadline(10))));
+                          grpc_timespec_to_millis_round_up(
+                              grpc_timeout_milliseconds_to_deadline(10)))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     do {
@@ -353,7 +353,8 @@
   uint8_t current_data = 0;
   grpc_slice_buffer outgoing;
   grpc_closure write_done_closure;
-  gpr_timespec deadline = grpc_timeout_seconds_to_deadline(20);
+  grpc_millis deadline =
+      grpc_timespec_to_millis_round_up(grpc_timeout_seconds_to_deadline(20));
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
   gpr_log(GPR_INFO,
@@ -390,8 +391,7 @@
     }
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
-        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker, deadline)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -419,7 +419,8 @@
   struct read_socket_state state;
   size_t written_bytes;
   int fd;
-  gpr_timespec deadline = grpc_timeout_seconds_to_deadline(20);
+  grpc_millis deadline =
+      grpc_timespec_to_millis_round_up(grpc_timeout_seconds_to_deadline(20));
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_closure fd_released_cb;
   int fd_released_done = 0;
@@ -457,8 +458,7 @@
     grpc_pollset_worker *worker = NULL;
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
-        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker, deadline)));
     gpr_log(GPR_DEBUG, "wakeup: read=%" PRIdPTR " target=%" PRIdPTR,
             state.read_bytes, state.target_read_bytes);
     gpr_mu_unlock(g_mu);
@@ -476,8 +476,7 @@
     grpc_pollset_worker *worker = NULL;
     GPR_ASSERT(GRPC_LOG_IF_ERROR(
         "pollset_work",
-        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker, deadline)));
     gpr_log(GPR_DEBUG, "wakeup: fd_released_done=%d", fd_released_done);
   }
   gpr_mu_unlock(g_mu);
diff --git a/test/core/iomgr/tcp_server_posix_test.c b/test/core/iomgr/tcp_server_posix_test.c
index 4d84608..782dfb4 100644
--- a/test/core/iomgr/tcp_server_posix_test.c
+++ b/test/core/iomgr/tcp_server_posix_test.c
@@ -230,7 +230,8 @@
 
 static grpc_error *tcp_connect(grpc_exec_ctx *exec_ctx, const test_addr *remote,
                                on_connect_result *result) {
-  gpr_timespec deadline = grpc_timeout_seconds_to_deadline(10);
+  grpc_millis deadline =
+      grpc_timespec_to_millis_round_up(grpc_timeout_seconds_to_deadline(10));
   int clifd;
   int nconnects_before;
   const struct sockaddr *remote_addr =
@@ -253,11 +254,10 @@
   }
   gpr_log(GPR_DEBUG, "wait");
   while (g_nconnects == nconnects_before &&
-         gpr_time_cmp(deadline, gpr_now(deadline.clock_type)) > 0) {
+         deadline > grpc_exec_ctx_now(exec_ctx)) {
     grpc_pollset_worker *worker = NULL;
     grpc_error *err;
-    if ((err = grpc_pollset_work(exec_ctx, g_pollset, &worker,
-                                 gpr_now(GPR_CLOCK_MONOTONIC), deadline)) !=
+    if ((err = grpc_pollset_work(exec_ctx, g_pollset, &worker, deadline)) !=
         GRPC_ERROR_NONE) {
       gpr_mu_unlock(g_mu);
       close(clifd);
diff --git a/test/core/iomgr/timer_list_test.c b/test/core/iomgr/timer_list_test.c
index 5f8b01f..c3d9f9d 100644
--- a/test/core/iomgr/timer_list_test.c
+++ b/test/core/iomgr/timer_list_test.c
@@ -41,51 +41,45 @@
 }
 
 static void add_test(void) {
-  gpr_timespec start = gpr_now(GPR_CLOCK_REALTIME);
   int i;
   grpc_timer timers[20];
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
   gpr_log(GPR_INFO, "add_test");
 
-  grpc_timer_list_init(start);
+  grpc_timer_list_init(&exec_ctx);
   grpc_timer_trace.value = 1;
   grpc_timer_check_trace.value = 1;
   memset(cb_called, 0, sizeof(cb_called));
 
+  grpc_millis start = grpc_exec_ctx_now(&exec_ctx);
+
   /* 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)),
-        GRPC_CLOSURE_CREATE(cb, (void *)(intptr_t)i, grpc_schedule_on_exec_ctx),
-        start);
+    grpc_timer_init(&exec_ctx, &timers[i], start + 10,
+                    GRPC_CLOSURE_CREATE(cb, (void *)(intptr_t)i,
+                                        grpc_schedule_on_exec_ctx));
   }
 
   /* 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)),
-        GRPC_CLOSURE_CREATE(cb, (void *)(intptr_t)i, grpc_schedule_on_exec_ctx),
-        start);
+    grpc_timer_init(&exec_ctx, &timers[i], start + 1010,
+                    GRPC_CLOSURE_CREATE(cb, (void *)(intptr_t)i,
+                                        grpc_schedule_on_exec_ctx));
   }
 
   /* collect timers.  Only the first batch should be ready. */
-  GPR_ASSERT(grpc_timer_check(
-                 &exec_ctx,
-                 gpr_time_add(start, gpr_time_from_millis(500, GPR_TIMESPAN)),
-                 NULL) == GRPC_TIMERS_FIRED);
+  exec_ctx.now = start + 500;
+  GPR_ASSERT(grpc_timer_check(&exec_ctx, NULL) == GRPC_TIMERS_FIRED);
   grpc_exec_ctx_finish(&exec_ctx);
   for (i = 0; i < 20; i++) {
     GPR_ASSERT(cb_called[i][1] == (i < 10));
     GPR_ASSERT(cb_called[i][0] == 0);
   }
 
-  GPR_ASSERT(grpc_timer_check(
-                 &exec_ctx,
-                 gpr_time_add(start, gpr_time_from_millis(600, GPR_TIMESPAN)),
-                 NULL) == GRPC_TIMERS_CHECKED_AND_EMPTY);
+  exec_ctx.now = start + 600;
+  GPR_ASSERT(grpc_timer_check(&exec_ctx, NULL) ==
+             GRPC_TIMERS_CHECKED_AND_EMPTY);
   grpc_exec_ctx_finish(&exec_ctx);
   for (i = 0; i < 30; i++) {
     GPR_ASSERT(cb_called[i][1] == (i < 10));
@@ -93,20 +87,17 @@
   }
 
   /* collect the rest of the timers */
-  GPR_ASSERT(grpc_timer_check(
-                 &exec_ctx,
-                 gpr_time_add(start, gpr_time_from_millis(1500, GPR_TIMESPAN)),
-                 NULL) == GRPC_TIMERS_FIRED);
+  exec_ctx.now = start + 1500;
+  GPR_ASSERT(grpc_timer_check(&exec_ctx, NULL) == GRPC_TIMERS_FIRED);
   grpc_exec_ctx_finish(&exec_ctx);
   for (i = 0; i < 30; i++) {
     GPR_ASSERT(cb_called[i][1] == (i < 20));
     GPR_ASSERT(cb_called[i][0] == 0);
   }
 
-  GPR_ASSERT(grpc_timer_check(
-                 &exec_ctx,
-                 gpr_time_add(start, gpr_time_from_millis(1600, GPR_TIMESPAN)),
-                 NULL) == GRPC_TIMERS_CHECKED_AND_EMPTY);
+  exec_ctx.now = start + 1600;
+  GPR_ASSERT(grpc_timer_check(&exec_ctx, NULL) ==
+             GRPC_TIMERS_CHECKED_AND_EMPTY);
   for (i = 0; i < 30; i++) {
     GPR_ASSERT(cb_called[i][1] == (i < 20));
     GPR_ASSERT(cb_called[i][0] == 0);
@@ -116,10 +107,6 @@
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
-static gpr_timespec tfm(int m) {
-  return gpr_time_from_millis(m, GPR_CLOCK_REALTIME);
-}
-
 /* Cleaning up a list with pending timers. */
 void destruction_test(void) {
   grpc_timer timers[5];
@@ -127,32 +114,30 @@
 
   gpr_log(GPR_INFO, "destruction_test");
 
-  grpc_timer_list_init(gpr_time_0(GPR_CLOCK_REALTIME));
+  exec_ctx.now_is_valid = true;
+  exec_ctx.now = 0;
+  grpc_timer_list_init(&exec_ctx);
   grpc_timer_trace.value = 1;
   grpc_timer_check_trace.value = 1;
   memset(cb_called, 0, sizeof(cb_called));
 
   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));
+      &exec_ctx, &timers[0], 100,
+      GRPC_CLOSURE_CREATE(cb, (void *)(intptr_t)0, grpc_schedule_on_exec_ctx));
   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));
+      &exec_ctx, &timers[1], 3,
+      GRPC_CLOSURE_CREATE(cb, (void *)(intptr_t)1, grpc_schedule_on_exec_ctx));
   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));
+      &exec_ctx, &timers[2], 100,
+      GRPC_CLOSURE_CREATE(cb, (void *)(intptr_t)2, grpc_schedule_on_exec_ctx));
   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));
+      &exec_ctx, &timers[3], 3,
+      GRPC_CLOSURE_CREATE(cb, (void *)(intptr_t)3, grpc_schedule_on_exec_ctx));
   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(grpc_timer_check(&exec_ctx, tfm(2), NULL) == GRPC_TIMERS_FIRED);
+      &exec_ctx, &timers[4], 1,
+      GRPC_CLOSURE_CREATE(cb, (void *)(intptr_t)4, grpc_schedule_on_exec_ctx));
+  exec_ctx.now = 2;
+  GPR_ASSERT(grpc_timer_check(&exec_ctx, NULL) == GRPC_TIMERS_FIRED);
   grpc_exec_ctx_finish(&exec_ctx);
   GPR_ASSERT(1 == cb_called[4][1]);
   grpc_timer_cancel(&exec_ctx, &timers[0]);
diff --git a/test/core/iomgr/udp_server_test.c b/test/core/iomgr/udp_server_test.c
index 1d051be..2e44d0a 100644
--- a/test/core/iomgr/udp_server_test.c
+++ b/test/core/iomgr/udp_server_test.c
@@ -226,7 +226,7 @@
   grpc_udp_server *s = grpc_udp_server_create(NULL);
   int i;
   int number_of_reads_before;
-  gpr_timespec deadline;
+  grpc_millis deadline;
   grpc_pollset *pollsets[1];
   LOG_TEST("test_receive");
   gpr_log(GPR_INFO, "clients=%d", number_of_clients);
@@ -252,7 +252,8 @@
   gpr_mu_lock(g_mu);
 
   for (i = 0; i < number_of_clients; i++) {
-    deadline = grpc_timeout_seconds_to_deadline(10);
+    deadline =
+        grpc_timespec_to_millis_round_up(grpc_timeout_seconds_to_deadline(10));
 
     number_of_reads_before = g_number_of_reads;
     /* Create a socket, send a packet to the UDP server. */
@@ -262,14 +263,13 @@
                        (socklen_t)resolved_addr.len) == 0);
     GPR_ASSERT(5 == write(clifd, "hello", 5));
     while (g_number_of_reads == number_of_reads_before &&
-           gpr_time_cmp(deadline, gpr_now(deadline.clock_type)) > 0) {
+           deadline > grpc_exec_ctx_now(&exec_ctx)) {
       grpc_pollset_worker *worker = NULL;
       GPR_ASSERT(GRPC_LOG_IF_ERROR(
           "pollset_work",
-          grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                            gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
+          grpc_pollset_work(&exec_ctx, g_pollset, &worker, deadline)));
       gpr_mu_unlock(g_mu);
-      grpc_exec_ctx_finish(&exec_ctx);
+      grpc_exec_ctx_flush(&exec_ctx);
       gpr_mu_lock(g_mu);
     }
     GPR_ASSERT(g_number_of_reads == number_of_reads_before + 1);
diff --git a/test/core/security/credentials_test.c b/test/core/security/credentials_test.c
index 441c431..34f3101 100644
--- a/test/core/security/credentials_test.c
+++ b/test/core/security/credentials_test.c
@@ -194,14 +194,13 @@
 static void test_oauth2_token_fetcher_creds_parsing_ok(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_mdelem token_md = GRPC_MDNULL;
-  gpr_timespec token_lifetime;
+  grpc_millis token_lifetime;
   grpc_httpcli_response response =
       http_response(200, valid_oauth2_json_response);
   GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
                  &exec_ctx, &response, &token_md, &token_lifetime) ==
              GRPC_CREDENTIALS_OK);
-  GPR_ASSERT(token_lifetime.tv_sec == 3599);
-  GPR_ASSERT(token_lifetime.tv_nsec == 0);
+  GPR_ASSERT(token_lifetime == 3599 * GPR_MS_PER_SEC);
   GPR_ASSERT(grpc_slice_str_cmp(GRPC_MDKEY(token_md), "authorization") == 0);
   GPR_ASSERT(grpc_slice_str_cmp(GRPC_MDVALUE(token_md),
                                 "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_") ==
@@ -214,7 +213,7 @@
 static void test_oauth2_token_fetcher_creds_parsing_bad_http_status(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_mdelem token_md = GRPC_MDNULL;
-  gpr_timespec token_lifetime;
+  grpc_millis token_lifetime;
   grpc_httpcli_response response =
       http_response(401, valid_oauth2_json_response);
   GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
@@ -227,7 +226,7 @@
 static void test_oauth2_token_fetcher_creds_parsing_empty_http_body(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_mdelem token_md = GRPC_MDNULL;
-  gpr_timespec token_lifetime;
+  grpc_millis token_lifetime;
   grpc_httpcli_response response = http_response(200, "");
   GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
                  &exec_ctx, &response, &token_md, &token_lifetime) ==
@@ -239,7 +238,7 @@
 static void test_oauth2_token_fetcher_creds_parsing_invalid_json(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_mdelem token_md = GRPC_MDNULL;
-  gpr_timespec token_lifetime;
+  grpc_millis token_lifetime;
   grpc_httpcli_response response =
       http_response(200,
                     "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
@@ -255,7 +254,7 @@
 static void test_oauth2_token_fetcher_creds_parsing_missing_token(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_mdelem token_md = GRPC_MDNULL;
-  gpr_timespec token_lifetime;
+  grpc_millis token_lifetime;
   grpc_httpcli_response response = http_response(200,
                                                  "{"
                                                  " \"expires_in\":3599, "
@@ -270,7 +269,7 @@
 static void test_oauth2_token_fetcher_creds_parsing_missing_token_type(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_mdelem token_md = GRPC_MDNULL;
-  gpr_timespec token_lifetime;
+  grpc_millis token_lifetime;
   grpc_httpcli_response response =
       http_response(200,
                     "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
@@ -287,7 +286,7 @@
     void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_mdelem token_md = GRPC_MDNULL;
-  gpr_timespec token_lifetime;
+  grpc_millis token_lifetime;
   grpc_httpcli_response response =
       http_response(200,
                     "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
@@ -555,7 +554,7 @@
 
 static int compute_engine_httpcli_get_success_override(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_closure *on_done,
+    grpc_millis deadline, grpc_closure *on_done,
     grpc_httpcli_response *response) {
   validate_compute_engine_http_request(request);
   *response = http_response(200, valid_oauth2_json_response);
@@ -565,7 +564,7 @@
 
 static int compute_engine_httpcli_get_failure_override(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_closure *on_done,
+    grpc_millis deadline, grpc_closure *on_done,
     grpc_httpcli_response *response) {
   validate_compute_engine_http_request(request);
   *response = http_response(403, "Not Authorized.");
@@ -575,7 +574,7 @@
 
 static int httpcli_post_should_not_be_called(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    const char *body_bytes, size_t body_size, gpr_timespec deadline,
+    const char *body_bytes, size_t body_size, grpc_millis deadline,
     grpc_closure *on_done, grpc_httpcli_response *response) {
   GPR_ASSERT("HTTP POST should not be called" == NULL);
   return 1;
@@ -583,7 +582,7 @@
 
 static int httpcli_get_should_not_be_called(grpc_exec_ctx *exec_ctx,
                                             const grpc_httpcli_request *request,
-                                            gpr_timespec deadline,
+                                            grpc_millis deadline,
                                             grpc_closure *on_done,
                                             grpc_httpcli_response *response) {
   GPR_ASSERT("HTTP GET should not be called" == NULL);
@@ -663,7 +662,7 @@
 
 static int refresh_token_httpcli_post_success(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    const char *body, size_t body_size, gpr_timespec deadline,
+    const char *body, size_t body_size, grpc_millis deadline,
     grpc_closure *on_done, grpc_httpcli_response *response) {
   validate_refresh_token_http_request(request, body, body_size);
   *response = http_response(200, valid_oauth2_json_response);
@@ -673,7 +672,7 @@
 
 static int refresh_token_httpcli_post_failure(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    const char *body, size_t body_size, gpr_timespec deadline,
+    const char *body, size_t body_size, grpc_millis deadline,
     grpc_closure *on_done, grpc_httpcli_response *response) {
   validate_refresh_token_http_request(request, body, body_size);
   *response = http_response(403, "Not Authorized.");
@@ -932,7 +931,7 @@
 
 static int default_creds_gce_detection_httpcli_get_success_override(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_closure *on_done,
+    grpc_millis deadline, grpc_closure *on_done,
     grpc_httpcli_response *response) {
   *response = http_response(200, "");
   grpc_http_header *headers = gpr_malloc(sizeof(*headers) * 1);
@@ -996,7 +995,7 @@
 
 static int default_creds_gce_detection_httpcli_get_failure_override(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_closure *on_done,
+    grpc_millis deadline, grpc_closure *on_done,
     grpc_httpcli_response *response) {
   /* No magic header. */
   GPR_ASSERT(strcmp(request->http.path, "/") == 0);
@@ -1036,39 +1035,46 @@
 
 static const expected_md plugin_md[] = {{"foo", "bar"}, {"hi", "there"}};
 
-static void plugin_get_metadata_success(void *state,
-                                        grpc_auth_metadata_context context,
-                                        grpc_credentials_plugin_metadata_cb cb,
-                                        void *user_data) {
-  size_t i;
-  grpc_metadata md[GPR_ARRAY_SIZE(plugin_md)];
-  plugin_state *s = (plugin_state *)state;
+static int plugin_get_metadata_success(
+    void *state, grpc_auth_metadata_context context,
+    grpc_credentials_plugin_metadata_cb cb, void *user_data,
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+    size_t *num_creds_md, grpc_status_code *status,
+    const char **error_details) {
   GPR_ASSERT(strcmp(context.service_url, test_service_url) == 0);
   GPR_ASSERT(strcmp(context.method_name, test_method) == 0);
   GPR_ASSERT(context.channel_auth_context == NULL);
   GPR_ASSERT(context.reserved == NULL);
+  GPR_ASSERT(GPR_ARRAY_SIZE(plugin_md) <
+             GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX);
+  plugin_state *s = (plugin_state *)state;
   *s = PLUGIN_GET_METADATA_CALLED_STATE;
-  for (i = 0; i < GPR_ARRAY_SIZE(plugin_md); i++) {
-    memset(&md[i], 0, sizeof(grpc_metadata));
-    md[i].key = grpc_slice_from_copied_string(plugin_md[i].key);
-    md[i].value = grpc_slice_from_copied_string(plugin_md[i].value);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(plugin_md); ++i) {
+    memset(&creds_md[i], 0, sizeof(grpc_metadata));
+    creds_md[i].key = grpc_slice_from_copied_string(plugin_md[i].key);
+    creds_md[i].value = grpc_slice_from_copied_string(plugin_md[i].value);
   }
-  cb(user_data, md, GPR_ARRAY_SIZE(md), GRPC_STATUS_OK, NULL);
+  *num_creds_md = GPR_ARRAY_SIZE(plugin_md);
+  return true;  // Synchronous return.
 }
 
 static const char *plugin_error_details = "Could not get metadata for plugin.";
 
-static void plugin_get_metadata_failure(void *state,
-                                        grpc_auth_metadata_context context,
-                                        grpc_credentials_plugin_metadata_cb cb,
-                                        void *user_data) {
-  plugin_state *s = (plugin_state *)state;
+static int plugin_get_metadata_failure(
+    void *state, grpc_auth_metadata_context context,
+    grpc_credentials_plugin_metadata_cb cb, void *user_data,
+    grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
+    size_t *num_creds_md, grpc_status_code *status,
+    const char **error_details) {
   GPR_ASSERT(strcmp(context.service_url, test_service_url) == 0);
   GPR_ASSERT(strcmp(context.method_name, test_method) == 0);
   GPR_ASSERT(context.channel_auth_context == NULL);
   GPR_ASSERT(context.reserved == NULL);
+  plugin_state *s = (plugin_state *)state;
   *s = PLUGIN_GET_METADATA_CALLED_STATE;
-  cb(user_data, NULL, 0, GRPC_STATUS_UNAUTHENTICATED, plugin_error_details);
+  *status = GRPC_STATUS_UNAUTHENTICATED;
+  *error_details = gpr_strdup(plugin_error_details);
+  return true;  // Synchronous return.
 }
 
 static void plugin_destroy(void *state) {
diff --git a/test/core/security/jwt_verifier_test.c b/test/core/security/jwt_verifier_test.c
index 9b17fb5..a4bfe01 100644
--- a/test/core/security/jwt_verifier_test.c
+++ b/test/core/security/jwt_verifier_test.c
@@ -324,7 +324,7 @@
 
 static int httpcli_post_should_not_be_called(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    const char *body_bytes, size_t body_size, gpr_timespec deadline,
+    const char *body_bytes, size_t body_size, grpc_millis deadline,
     grpc_closure *on_done, grpc_httpcli_response *response) {
   GPR_ASSERT("HTTP POST should not be called" == NULL);
   return 1;
@@ -332,7 +332,7 @@
 
 static int httpcli_get_google_keys_for_email(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_closure *on_done,
+    grpc_millis deadline, grpc_closure *on_done,
     grpc_httpcli_response *response) {
   *response = http_response(200, good_google_email_keys());
   GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
@@ -379,7 +379,7 @@
 
 static int httpcli_get_custom_keys_for_email(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_closure *on_done,
+    grpc_millis deadline, grpc_closure *on_done,
     grpc_httpcli_response *response) {
   *response = http_response(200, gpr_strdup(good_jwk_set));
   GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
@@ -413,7 +413,7 @@
 
 static int httpcli_get_jwk_set(grpc_exec_ctx *exec_ctx,
                                const grpc_httpcli_request *request,
-                               gpr_timespec deadline, grpc_closure *on_done,
+                               grpc_millis deadline, grpc_closure *on_done,
                                grpc_httpcli_response *response) {
   *response = http_response(200, gpr_strdup(good_jwk_set));
   GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
@@ -425,7 +425,7 @@
 
 static int httpcli_get_openid_config(grpc_exec_ctx *exec_ctx,
                                      const grpc_httpcli_request *request,
-                                     gpr_timespec deadline,
+                                     grpc_millis deadline,
                                      grpc_closure *on_done,
                                      grpc_httpcli_response *response) {
   *response = http_response(200, gpr_strdup(good_openid_config));
@@ -471,7 +471,7 @@
 
 static int httpcli_get_bad_json(grpc_exec_ctx *exec_ctx,
                                 const grpc_httpcli_request *request,
-                                gpr_timespec deadline, grpc_closure *on_done,
+                                grpc_millis deadline, grpc_closure *on_done,
                                 grpc_httpcli_response *response) {
   *response = http_response(200, gpr_strdup("{\"bad\": \"stuff\"}"));
   GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
@@ -581,7 +581,7 @@
 
 static int httpcli_get_should_not_be_called(grpc_exec_ctx *exec_ctx,
                                             const grpc_httpcli_request *request,
-                                            gpr_timespec deadline,
+                                            grpc_millis deadline,
                                             grpc_closure *on_done,
                                             grpc_httpcli_response *response) {
   GPR_ASSERT(0);
diff --git a/test/core/security/oauth2_utils.c b/test/core/security/oauth2_utils.c
index d240403..73d6c5b 100644
--- a/test/core/security/oauth2_utils.c
+++ b/test/core/security/oauth2_utils.c
@@ -104,8 +104,7 @@
             "pollset_work",
             grpc_pollset_work(&exec_ctx,
                               grpc_polling_entity_pollset(&request.pops),
-                              &worker, gpr_now(GPR_CLOCK_MONOTONIC),
-                              gpr_inf_future(GPR_CLOCK_MONOTONIC)))) {
+                              &worker, GRPC_MILLIS_INF_FUTURE))) {
       request.is_done = true;
     }
   }
diff --git a/test/core/security/print_google_default_creds_token.c b/test/core/security/print_google_default_creds_token.c
index 3144717..29c38df 100644
--- a/test/core/security/print_google_default_creds_token.c
+++ b/test/core/security/print_google_default_creds_token.c
@@ -110,8 +110,7 @@
             "pollset_work",
             grpc_pollset_work(&exec_ctx,
                               grpc_polling_entity_pollset(&sync.pops), &worker,
-                              gpr_now(GPR_CLOCK_MONOTONIC),
-                              gpr_inf_future(GPR_CLOCK_MONOTONIC))))
+                              GRPC_MILLIS_INF_FUTURE)))
       sync.is_done = true;
     gpr_mu_unlock(sync.mu);
     grpc_exec_ctx_flush(&exec_ctx);
diff --git a/test/core/security/ssl_server_fuzzer.c b/test/core/security/ssl_server_fuzzer.c
index 9858b11..f9b754b 100644
--- a/test/core/security/ssl_server_fuzzer.c
+++ b/test/core/security/ssl_server_fuzzer.c
@@ -84,8 +84,7 @@
   grpc_security_status status =
       grpc_server_credentials_create_security_connector(&exec_ctx, creds, &sc);
   GPR_ASSERT(status == GRPC_SECURITY_OK);
-  gpr_timespec deadline = gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
-                                       gpr_time_from_seconds(1, GPR_TIMESPAN));
+  grpc_millis deadline = GPR_MS_PER_SEC + grpc_exec_ctx_now(&exec_ctx);
 
   struct handshake_state state;
   state.done_callback_called = false;
diff --git a/test/core/security/verify_jwt.c b/test/core/security/verify_jwt.c
index 5faa635..cec6fb9 100644
--- a/test/core/security/verify_jwt.c
+++ b/test/core/security/verify_jwt.c
@@ -102,11 +102,9 @@
   gpr_mu_lock(sync.mu);
   while (!sync.is_done) {
     grpc_pollset_worker *worker = NULL;
-    if (!GRPC_LOG_IF_ERROR(
-            "pollset_work",
-            grpc_pollset_work(&exec_ctx, sync.pollset, &worker,
-                              gpr_now(GPR_CLOCK_MONOTONIC),
-                              gpr_inf_future(GPR_CLOCK_MONOTONIC))))
+    if (!GRPC_LOG_IF_ERROR("pollset_work",
+                           grpc_pollset_work(&exec_ctx, sync.pollset, &worker,
+                                             GRPC_MILLIS_INF_FUTURE)))
       sync.is_done = true;
     gpr_mu_unlock(sync.mu);
     grpc_exec_ctx_flush(&exec_ctx);
diff --git a/test/core/support/BUILD b/test/core/support/BUILD
index 096576e..407c3ef 100644
--- a/test/core/support/BUILD
+++ b/test/core/support/BUILD
@@ -39,16 +39,6 @@
 )
 
 grpc_cc_test(
-    name = "backoff_test",
-    srcs = ["backoff_test.c"],
-    language = "C",
-    deps = [
-        "//:gpr",
-        "//test/core/util:gpr_test_util",
-    ],
-)
-
-grpc_cc_test(
     name = "cmdline_test",
     srcs = ["cmdline_test.c"],
     language = "C",
diff --git a/test/core/support/backoff_test.c b/test/core/support/backoff_test.c
deleted file mode 100644
index 23e3005..0000000
--- a/test/core/support/backoff_test.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- *
- * Copyright 2016 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.
- *
- */
-
-#include "src/core/lib/support/backoff.h"
-
-#include <grpc/support/log.h>
-
-#include "test/core/util/test_config.h"
-
-static void test_constant_backoff(void) {
-  gpr_backoff backoff;
-  gpr_backoff_init(&backoff, 200 /* initial timeout */, 1.0 /* multiplier */,
-                   0.0 /* jitter */, 100 /* min timeout */,
-                   1000 /* max timeout */);
-
-  gpr_timespec now = gpr_time_0(GPR_TIMESPAN);
-  gpr_timespec next = gpr_backoff_begin(&backoff, now);
-  GPR_ASSERT(gpr_time_to_millis(gpr_time_sub(next, now)) == 200);
-  for (int i = 0; i < 10000; i++) {
-    next = gpr_backoff_step(&backoff, now);
-    GPR_ASSERT(gpr_time_to_millis(gpr_time_sub(next, now)) == 200);
-    now = next;
-  }
-}
-
-static void test_min_connect(void) {
-  gpr_backoff backoff;
-  gpr_backoff_init(&backoff, 100 /* initial timeout */, 1.0 /* multiplier */,
-                   0.0 /* jitter */, 200 /* min timeout */,
-                   1000 /* max timeout */);
-
-  gpr_timespec now = gpr_time_0(GPR_TIMESPAN);
-  gpr_timespec next = gpr_backoff_begin(&backoff, now);
-  GPR_ASSERT(gpr_time_to_millis(gpr_time_sub(next, now)) == 200);
-}
-
-static void test_no_jitter_backoff(void) {
-  gpr_backoff backoff;
-  gpr_backoff_init(&backoff, 2 /* initial timeout */, 2.0 /* multiplier */,
-                   0.0 /* jitter */, 1 /* min timeout */,
-                   513 /* max timeout */);
-  // x_1 = 2
-  // x_n = 2**i + x_{i-1} ( = 2**(n+1) - 2 )
-  gpr_timespec now = gpr_time_0(GPR_TIMESPAN);
-  gpr_timespec next = gpr_backoff_begin(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(2, GPR_TIMESPAN), next) == 0);
-  now = next;
-  next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(6, GPR_TIMESPAN), next) == 0);
-  now = next;
-  next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(14, GPR_TIMESPAN), next) == 0);
-  now = next;
-  next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(30, GPR_TIMESPAN), next) == 0);
-  now = next;
-  next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(62, GPR_TIMESPAN), next) == 0);
-  now = next;
-  next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(126, GPR_TIMESPAN), next) == 0);
-  now = next;
-  next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(254, GPR_TIMESPAN), next) == 0);
-  now = next;
-  next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(510, GPR_TIMESPAN), next) == 0);
-  now = next;
-  next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(1022, GPR_TIMESPAN), next) == 0);
-  now = next;
-  next = gpr_backoff_step(&backoff, now);
-  // Hit the maximum timeout. From this point onwards, retries will increase
-  // only by max timeout.
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(1535, GPR_TIMESPAN), next) == 0);
-  now = next;
-  next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(2048, GPR_TIMESPAN), next) == 0);
-  now = next;
-  next = gpr_backoff_step(&backoff, now);
-  GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(2561, GPR_TIMESPAN), next) == 0);
-}
-
-static void test_jitter_backoff(void) {
-  const int64_t initial_timeout = 500;
-  const double jitter = 0.1;
-  gpr_backoff backoff;
-  gpr_backoff_init(&backoff, initial_timeout, 1.0 /* multiplier */, jitter,
-                   100 /* min timeout */, 1000 /* max timeout */);
-
-  backoff.rng_state = 0;  // force consistent PRNG
-
-  gpr_timespec now = gpr_time_0(GPR_TIMESPAN);
-  gpr_timespec next = gpr_backoff_begin(&backoff, now);
-  GPR_ASSERT(gpr_time_to_millis(gpr_time_sub(next, now)) == 500);
-
-  int64_t expected_next_lower_bound =
-      (int64_t)((double)initial_timeout * (1 - jitter));
-  int64_t expected_next_upper_bound =
-      (int64_t)((double)initial_timeout * (1 + jitter));
-
-  for (int i = 0; i < 10000; i++) {
-    next = gpr_backoff_step(&backoff, now);
-
-    // next-now must be within (jitter*100)% of the previous timeout.
-    const int64_t timeout_millis = gpr_time_to_millis(gpr_time_sub(next, now));
-    GPR_ASSERT(timeout_millis >= expected_next_lower_bound);
-    GPR_ASSERT(timeout_millis <= expected_next_upper_bound);
-
-    expected_next_lower_bound =
-        (int64_t)((double)timeout_millis * (1 - jitter));
-    expected_next_upper_bound =
-        (int64_t)((double)timeout_millis * (1 + jitter));
-    now = next;
-  }
-}
-
-int main(int argc, char **argv) {
-  grpc_test_init(argc, argv);
-  gpr_time_init();
-
-  test_constant_backoff();
-  test_min_connect();
-  test_no_jitter_backoff();
-  test_jitter_backoff();
-
-  return 0;
-}
diff --git a/test/core/surface/concurrent_connectivity_test.c b/test/core/surface/concurrent_connectivity_test.c
index ec2cd86..3595885 100644
--- a/test/core/surface/concurrent_connectivity_test.c
+++ b/test/core/surface/concurrent_connectivity_test.c
@@ -135,14 +135,12 @@
 
   gpr_mu_lock(args->mu);
   while (gpr_atm_acq_load(&args->stop) == 0) {
-    gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
-    gpr_timespec deadline =
-        gpr_time_add(now, gpr_time_from_millis(100, GPR_TIMESPAN));
+    grpc_millis deadline = grpc_exec_ctx_now(&exec_ctx) + 100;
 
     grpc_pollset_worker *worker = NULL;
-    if (!GRPC_LOG_IF_ERROR("pollset_work",
-                           grpc_pollset_work(&exec_ctx, args->pollset, &worker,
-                                             now, deadline))) {
+    if (!GRPC_LOG_IF_ERROR(
+            "pollset_work",
+            grpc_pollset_work(&exec_ctx, args->pollset, &worker, deadline))) {
       gpr_atm_rel_store(&args->stop, 1);
     }
     gpr_mu_unlock(args->mu);
diff --git a/test/core/surface/public_headers_must_be_c89.c b/test/core/surface/public_headers_must_be_c89.c
index 0d7f68c..d36d116 100644
--- a/test/core/surface/public_headers_must_be_c89.c
+++ b/test/core/surface/public_headers_must_be_c89.c
@@ -24,6 +24,7 @@
 #include <grpc/grpc_security.h>
 #include <grpc/grpc_security_constants.h>
 #include <grpc/impl/codegen/atm.h>
+#include <grpc/impl/codegen/byte_buffer.h>
 #include <grpc/impl/codegen/byte_buffer_reader.h>
 #include <grpc/impl/codegen/compression_types.h>
 #include <grpc/impl/codegen/connectivity_state.h>
diff --git a/test/core/transport/status_conversion_test.c b/test/core/transport/status_conversion_test.c
index 8955896..de8fa44 100644
--- a/test/core/transport/status_conversion_test.c
+++ b/test/core/transport/status_conversion_test.c
@@ -22,8 +22,13 @@
 
 #define GRPC_STATUS_TO_HTTP2_ERROR(a, b) \
   GPR_ASSERT(grpc_status_to_http2_error(a) == (b))
-#define HTTP2_ERROR_TO_GRPC_STATUS(a, deadline, b) \
-  GPR_ASSERT(grpc_http2_error_to_grpc_status(a, deadline) == (b))
+#define HTTP2_ERROR_TO_GRPC_STATUS(a, deadline, b)                           \
+  do {                                                                       \
+    grpc_exec_ctx my_exec_ctx = GRPC_EXEC_CTX_INIT;                          \
+    GPR_ASSERT(grpc_http2_error_to_grpc_status(&my_exec_ctx, a, deadline) == \
+               (b));                                                         \
+    grpc_exec_ctx_finish(&my_exec_ctx);                                      \
+  } while (0)
 #define GRPC_STATUS_TO_HTTP2_STATUS(a, b) \
   GPR_ASSERT(grpc_status_to_http2_status(a) == (b))
 #define HTTP2_STATUS_TO_GRPC_STATUS(a, b) \
@@ -79,7 +84,7 @@
   GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_UNAVAILABLE, 200);
   GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_DATA_LOSS, 200);
 
-  const gpr_timespec before_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  const grpc_millis before_deadline = GRPC_MILLIS_INF_FUTURE;
   HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_NO_ERROR, before_deadline,
                              GRPC_STATUS_INTERNAL);
   HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_PROTOCOL_ERROR, before_deadline,
@@ -107,7 +112,7 @@
   HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_INADEQUATE_SECURITY, before_deadline,
                              GRPC_STATUS_PERMISSION_DENIED);
 
-  const gpr_timespec after_deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC);
+  const grpc_millis after_deadline = 0;
   HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_NO_ERROR, after_deadline,
                              GRPC_STATUS_INTERNAL);
   HTTP2_ERROR_TO_GRPC_STATUS(GRPC_HTTP2_PROTOCOL_ERROR, after_deadline,
diff --git a/test/core/transport/timeout_encoding_test.c b/test/core/transport/timeout_encoding_test.c
index 6388ffb..3010c6d 100644
--- a/test/core/transport/timeout_encoding_test.c
+++ b/test/core/transport/timeout_encoding_test.c
@@ -25,12 +25,13 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/useful.h>
+#include "src/core/lib/support/murmur_hash.h"
 #include "src/core/lib/support/string.h"
 #include "test/core/util/test_config.h"
 
 #define LOG_TEST(x) gpr_log(GPR_INFO, "%s", x)
 
-static void assert_encodes_as(gpr_timespec ts, const char *s) {
+static void assert_encodes_as(grpc_millis ts, const char *s) {
   char buffer[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE];
   grpc_http2_encode_timeout(ts, buffer);
   gpr_log(GPR_INFO, "check '%s' == '%s'", buffer, s);
@@ -39,47 +40,43 @@
 
 void test_encoding(void) {
   LOG_TEST("test_encoding");
-  assert_encodes_as(gpr_time_from_micros(-1, GPR_TIMESPAN), "1n");
-  assert_encodes_as(gpr_time_from_seconds(-10, GPR_TIMESPAN), "1n");
-  assert_encodes_as(gpr_time_from_nanos(10, GPR_TIMESPAN), "10n");
-  assert_encodes_as(gpr_time_from_nanos(999999999, GPR_TIMESPAN), "1S");
-  assert_encodes_as(gpr_time_from_micros(1, GPR_TIMESPAN), "1u");
-  assert_encodes_as(gpr_time_from_micros(10, GPR_TIMESPAN), "10u");
-  assert_encodes_as(gpr_time_from_micros(100, GPR_TIMESPAN), "100u");
-  assert_encodes_as(gpr_time_from_micros(890, GPR_TIMESPAN), "890u");
-  assert_encodes_as(gpr_time_from_micros(900, GPR_TIMESPAN), "900u");
-  assert_encodes_as(gpr_time_from_micros(901, GPR_TIMESPAN), "901u");
-  assert_encodes_as(gpr_time_from_millis(1, GPR_TIMESPAN), "1m");
-  assert_encodes_as(gpr_time_from_millis(2, GPR_TIMESPAN), "2m");
-  assert_encodes_as(gpr_time_from_micros(10001, GPR_TIMESPAN), "10100u");
-  assert_encodes_as(gpr_time_from_micros(999999, GPR_TIMESPAN), "1S");
-  assert_encodes_as(gpr_time_from_millis(1000, GPR_TIMESPAN), "1S");
-  assert_encodes_as(gpr_time_from_millis(2000, GPR_TIMESPAN), "2S");
-  assert_encodes_as(gpr_time_from_millis(2500, GPR_TIMESPAN), "2500m");
-  assert_encodes_as(gpr_time_from_millis(59900, GPR_TIMESPAN), "59900m");
-  assert_encodes_as(gpr_time_from_seconds(50, GPR_TIMESPAN), "50S");
-  assert_encodes_as(gpr_time_from_seconds(59, GPR_TIMESPAN), "59S");
-  assert_encodes_as(gpr_time_from_seconds(60, GPR_TIMESPAN), "1M");
-  assert_encodes_as(gpr_time_from_seconds(80, GPR_TIMESPAN), "80S");
-  assert_encodes_as(gpr_time_from_seconds(90, GPR_TIMESPAN), "90S");
-  assert_encodes_as(gpr_time_from_minutes(2, GPR_TIMESPAN), "2M");
-  assert_encodes_as(gpr_time_from_minutes(20, GPR_TIMESPAN), "20M");
-  assert_encodes_as(gpr_time_from_hours(1, GPR_TIMESPAN), "1H");
-  assert_encodes_as(gpr_time_from_hours(10, GPR_TIMESPAN), "10H");
-  assert_encodes_as(gpr_time_from_seconds(1000000000, GPR_TIMESPAN),
-                    "1000000000S");
+  assert_encodes_as(-1, "1n");
+  assert_encodes_as(-10, "1n");
+  assert_encodes_as(1, "1m");
+  assert_encodes_as(10, "10m");
+  assert_encodes_as(100, "100m");
+  assert_encodes_as(890, "890m");
+  assert_encodes_as(900, "900m");
+  assert_encodes_as(901, "901m");
+  assert_encodes_as(1000, "1S");
+  assert_encodes_as(2000, "2S");
+  assert_encodes_as(2500, "2500m");
+  assert_encodes_as(59900, "59900m");
+  assert_encodes_as(50000, "50S");
+  assert_encodes_as(59000, "59S");
+  assert_encodes_as(60000, "1M");
+  assert_encodes_as(80000, "80S");
+  assert_encodes_as(90000, "90S");
+  assert_encodes_as(120000, "2M");
+  assert_encodes_as(20 * 60 * GPR_MS_PER_SEC, "20M");
+  assert_encodes_as(60 * 60 * GPR_MS_PER_SEC, "1H");
+  assert_encodes_as(10 * 60 * 60 * GPR_MS_PER_SEC, "10H");
 }
 
-static void assert_decodes_as(const char *buffer, gpr_timespec expected) {
-  gpr_timespec got;
-  gpr_log(GPR_INFO, "check decoding '%s'", buffer);
+static void assert_decodes_as(const char *buffer, grpc_millis expected) {
+  grpc_millis got;
+  uint32_t hash = gpr_murmur_hash3(buffer, strlen(buffer), 0);
+  gpr_log(GPR_INFO, "check decoding '%s' (hash=0x%x)", buffer, hash);
   GPR_ASSERT(1 == grpc_http2_decode_timeout(
                       grpc_slice_from_static_string(buffer), &got));
-  GPR_ASSERT(0 == gpr_time_cmp(got, expected));
+  if (got != expected) {
+    gpr_log(GPR_ERROR, "got:'%" PRIdPTR "' != expected:'%" PRIdPTR "'", got,
+            expected);
+    abort();
+  }
 }
 
-void decode_suite(char ext,
-                  gpr_timespec (*answer)(int64_t x, gpr_clock_type clock)) {
+void decode_suite(char ext, grpc_millis (*answer)(int64_t x)) {
   long test_vals[] = {1,       12,       123,       1234,     12345,   123456,
                       1234567, 12345678, 123456789, 98765432, 9876543, 987654,
                       98765,   9876,     987,       98,       9};
@@ -87,41 +84,55 @@
   char *input;
   for (i = 0; i < GPR_ARRAY_SIZE(test_vals); i++) {
     gpr_asprintf(&input, "%ld%c", test_vals[i], ext);
-    assert_decodes_as(input, answer(test_vals[i], GPR_TIMESPAN));
+    assert_decodes_as(input, answer(test_vals[i]));
     gpr_free(input);
 
     gpr_asprintf(&input, "   %ld%c", test_vals[i], ext);
-    assert_decodes_as(input, answer(test_vals[i], GPR_TIMESPAN));
+    assert_decodes_as(input, answer(test_vals[i]));
     gpr_free(input);
 
     gpr_asprintf(&input, "%ld %c", test_vals[i], ext);
-    assert_decodes_as(input, answer(test_vals[i], GPR_TIMESPAN));
+    assert_decodes_as(input, answer(test_vals[i]));
     gpr_free(input);
 
     gpr_asprintf(&input, "%ld %c  ", test_vals[i], ext);
-    assert_decodes_as(input, answer(test_vals[i], GPR_TIMESPAN));
+    assert_decodes_as(input, answer(test_vals[i]));
     gpr_free(input);
   }
 }
 
+static grpc_millis millis_from_nanos(int64_t x) {
+  return x / GPR_NS_PER_MS + (x % GPR_NS_PER_MS != 0);
+}
+static grpc_millis millis_from_micros(int64_t x) {
+  return x / GPR_US_PER_MS + (x % GPR_US_PER_MS != 0);
+}
+static grpc_millis millis_from_millis(int64_t x) { return x; }
+static grpc_millis millis_from_seconds(int64_t x) { return x * GPR_MS_PER_SEC; }
+static grpc_millis millis_from_minutes(int64_t x) {
+  return x * 60 * GPR_MS_PER_SEC;
+}
+static grpc_millis millis_from_hours(int64_t x) {
+  return x * 3600 * GPR_MS_PER_SEC;
+}
+
 void test_decoding(void) {
   LOG_TEST("test_decoding");
-  decode_suite('n', gpr_time_from_nanos);
-  decode_suite('u', gpr_time_from_micros);
-  decode_suite('m', gpr_time_from_millis);
-  decode_suite('S', gpr_time_from_seconds);
-  decode_suite('M', gpr_time_from_minutes);
-  decode_suite('H', gpr_time_from_hours);
-  assert_decodes_as("1000000000S",
-                    gpr_time_from_seconds(1000 * 1000 * 1000, GPR_TIMESPAN));
-  assert_decodes_as("1000000000000000000000u", gpr_inf_future(GPR_TIMESPAN));
-  assert_decodes_as("1000000001S", gpr_inf_future(GPR_TIMESPAN));
-  assert_decodes_as("2000000001S", gpr_inf_future(GPR_TIMESPAN));
-  assert_decodes_as("9999999999S", gpr_inf_future(GPR_TIMESPAN));
+  decode_suite('n', millis_from_nanos);
+  decode_suite('u', millis_from_micros);
+  decode_suite('m', millis_from_millis);
+  decode_suite('S', millis_from_seconds);
+  decode_suite('M', millis_from_minutes);
+  decode_suite('H', millis_from_hours);
+  assert_decodes_as("1000000000S", millis_from_seconds(1000 * 1000 * 1000));
+  assert_decodes_as("1000000000000000000000u", GRPC_MILLIS_INF_FUTURE);
+  assert_decodes_as("1000000001S", GRPC_MILLIS_INF_FUTURE);
+  assert_decodes_as("2000000001S", GRPC_MILLIS_INF_FUTURE);
+  assert_decodes_as("9999999999S", GRPC_MILLIS_INF_FUTURE);
 }
 
 static void assert_decoding_fails(const char *s) {
-  gpr_timespec x;
+  grpc_millis x;
   GPR_ASSERT(0 ==
              grpc_http2_decode_timeout(grpc_slice_from_static_string(s), &x));
 }
diff --git a/test/core/tsi/transport_security_test_lib.c b/test/core/tsi/transport_security_test_lib.c
index 7d66e11..329b237 100644
--- a/test/core/tsi/transport_security_test_lib.c
+++ b/test/core/tsi/transport_security_test_lib.c
@@ -23,9 +23,26 @@
 #include <grpc/grpc.h>
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/thd.h>
 #include "src/core/lib/security/transport/tsi_error.h"
 #include "test/core/tsi/transport_security_test_lib.h"
 
+static void notification_signal(tsi_test_fixture *fixture) {
+  gpr_mu_lock(&fixture->mu);
+  fixture->notified = true;
+  gpr_cv_signal(&fixture->cv);
+  gpr_mu_unlock(&fixture->mu);
+}
+
+static void notification_wait(tsi_test_fixture *fixture) {
+  gpr_mu_lock(&fixture->mu);
+  while (!fixture->notified) {
+    gpr_cv_wait(&fixture->cv, &fixture->mu, gpr_inf_future(GPR_CLOCK_REALTIME));
+  }
+  fixture->notified = false;
+  gpr_mu_unlock(&fixture->mu);
+}
+
 typedef struct handshaker_args {
   tsi_test_fixture *fixture;
   unsigned char *handshake_buffer;
@@ -273,9 +290,11 @@
   /* Read more data if we need to. */
   if (result == TSI_INCOMPLETE_DATA) {
     GPR_ASSERT(bytes_to_send_size == 0);
+    notification_signal(fixture);
     return error;
   }
   if (result != TSI_OK) {
+    notification_signal(fixture);
     return grpc_set_tsi_error_result(
         GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshake failed"), result);
   }
@@ -295,6 +314,7 @@
   if (handshaker_result != NULL) {
     maybe_append_unused_bytes(args);
   }
+  notification_signal(fixture);
   return error;
 }
 
@@ -345,7 +365,11 @@
   if (result != TSI_ASYNC) {
     args->error = on_handshake_next_done(result, args, bytes_to_send,
                                          bytes_to_send_size, handshaker_result);
+    if (args->error != GRPC_ERROR_NONE) {
+      return;
+    }
   }
+  notification_wait(fixture);
 }
 
 void tsi_test_do_handshake(tsi_test_fixture *fixture) {
@@ -532,6 +556,9 @@
   fixture->bytes_read_from_server_channel = 0;
   fixture->test_unused_bytes = true;
   fixture->has_client_finished_first = false;
+  gpr_mu_init(&fixture->mu);
+  gpr_cv_init(&fixture->cv);
+  fixture->notified = false;
 }
 
 void tsi_test_fixture_destroy(tsi_test_fixture *fixture) {
@@ -546,5 +573,7 @@
   GPR_ASSERT(fixture->vtable != NULL);
   GPR_ASSERT(fixture->vtable->destruct != NULL);
   fixture->vtable->destruct(fixture);
+  gpr_mu_destroy(&fixture->mu);
+  gpr_cv_destroy(&fixture->cv);
   gpr_free(fixture);
 }
diff --git a/test/core/tsi/transport_security_test_lib.h b/test/core/tsi/transport_security_test_lib.h
index 8ae2024..ed8ff85 100644
--- a/test/core/tsi/transport_security_test_lib.h
+++ b/test/core/tsi/transport_security_test_lib.h
@@ -21,6 +21,10 @@
 
 #include "src/core/tsi/transport_security_interface.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #define TSI_TEST_TINY_HANDSHAKE_BUFFER_SIZE 32
 #define TSI_TEST_SMALL_HANDSHAKE_BUFFER_SIZE 128
 #define TSI_TEST_SMALL_READ_BUFFER_ALLOCATED_SIZE 41
@@ -56,10 +60,10 @@
   void (*setup_handshakers)(tsi_test_fixture *fixture);
   void (*check_handshaker_peers)(tsi_test_fixture *fixture);
   void (*destruct)(tsi_test_fixture *fixture);
-} tranport_security_test_vtable;
+} tsi_test_fixture_vtable;
 
 struct tsi_test_fixture {
-  const struct tsi_test_fixture_vtable *vtable;
+  const tsi_test_fixture_vtable *vtable;
   /* client/server TSI handshaker used to perform TSI handshakes, and will get
      instantiated during the call to setup_handshakers. */
   tsi_handshaker *client_handshaker;
@@ -95,6 +99,13 @@
      (https://github.com/grpc/grpc/issues/12164).
   */
   bool test_unused_bytes;
+  /* These objects will be used coordinate client/server handshakers with TSI
+     thread to perform TSI handshakes in an asynchronous manner (for GTS TSI
+     implementations).
+  */
+  gpr_cv cv;
+  gpr_mu mu;
+  bool notified;
 };
 
 struct tsi_test_frame_protector_config {
@@ -162,4 +173,8 @@
    the client and server switching its role. */
 void tsi_test_do_round_trip(tsi_test_fixture *fixture);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif  // GRPC_TEST_CORE_TSI_TRANSPORT_SECURITY_TEST_LIB_H_
diff --git a/test/core/util/port_server_client.c b/test/core/util/port_server_client.c
index ba4028db..7b94ac4 100644
--- a/test/core/util/port_server_client.c
+++ b/test/core/util/port_server_client.c
@@ -88,7 +88,7 @@
   grpc_resource_quota *resource_quota =
       grpc_resource_quota_create("port_server_client/free");
   grpc_httpcli_get(&exec_ctx, &context, &pr.pops, resource_quota, &req,
-                   grpc_timeout_seconds_to_deadline(30),
+                   grpc_exec_ctx_now(&exec_ctx) + 30 * GPR_MS_PER_SEC,
                    GRPC_CLOSURE_CREATE(freed_port_from_server, &pr,
                                        grpc_schedule_on_exec_ctx),
                    &rsp);
@@ -100,8 +100,8 @@
     if (!GRPC_LOG_IF_ERROR(
             "pollset_work",
             grpc_pollset_work(&exec_ctx, grpc_polling_entity_pollset(&pr.pops),
-                              &worker, gpr_now(GPR_CLOCK_MONOTONIC),
-                              grpc_timeout_seconds_to_deadline(1)))) {
+                              &worker,
+                              grpc_exec_ctx_now(&exec_ctx) + GPR_MS_PER_SEC))) {
       pr.done = 1;
     }
   }
@@ -173,7 +173,7 @@
     grpc_resource_quota *resource_quota =
         grpc_resource_quota_create("port_server_client/pick_retry");
     grpc_httpcli_get(exec_ctx, pr->ctx, &pr->pops, resource_quota, &req,
-                     grpc_timeout_seconds_to_deadline(10),
+                     grpc_exec_ctx_now(exec_ctx) + 30 * GPR_MS_PER_SEC,
                      GRPC_CLOSURE_CREATE(got_port_from_server, pr,
                                          grpc_schedule_on_exec_ctx),
                      &pr->response);
@@ -224,7 +224,7 @@
       grpc_resource_quota_create("port_server_client/pick");
   grpc_httpcli_get(
       &exec_ctx, &context, &pr.pops, resource_quota, &req,
-      grpc_timeout_seconds_to_deadline(30),
+      grpc_exec_ctx_now(&exec_ctx) + 30 * GPR_MS_PER_SEC,
       GRPC_CLOSURE_CREATE(got_port_from_server, &pr, grpc_schedule_on_exec_ctx),
       &pr.response);
   grpc_resource_quota_unref_internal(&exec_ctx, resource_quota);
@@ -235,8 +235,8 @@
     if (!GRPC_LOG_IF_ERROR(
             "pollset_work",
             grpc_pollset_work(&exec_ctx, grpc_polling_entity_pollset(&pr.pops),
-                              &worker, gpr_now(GPR_CLOCK_MONOTONIC),
-                              grpc_timeout_seconds_to_deadline(1)))) {
+                              &worker,
+                              grpc_exec_ctx_now(&exec_ctx) + GPR_MS_PER_SEC))) {
       pr.port = 0;
     }
   }
diff --git a/test/core/util/test_tcp_server.c b/test/core/util/test_tcp_server.c
index d3a1de8..611ecb3 100644
--- a/test/core/util/test_tcp_server.c
+++ b/test/core/util/test_tcp_server.c
@@ -31,6 +31,7 @@
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/iomgr/tcp_server.h"
 #include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
 
 static void on_server_destroyed(grpc_exec_ctx *exec_ctx, void *data,
                                 grpc_error *error) {
@@ -78,14 +79,13 @@
 
 void test_tcp_server_poll(test_tcp_server *server, int seconds) {
   grpc_pollset_worker *worker = NULL;
-  gpr_timespec deadline =
-      gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
-                   gpr_time_from_seconds(seconds, GPR_TIMESPAN));
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_millis deadline = grpc_timespec_to_millis_round_up(
+      grpc_timeout_seconds_to_deadline(seconds));
   gpr_mu_lock(server->mu);
-  GRPC_LOG_IF_ERROR("pollset_work",
-                    grpc_pollset_work(&exec_ctx, server->pollset, &worker,
-                                      gpr_now(GPR_CLOCK_MONOTONIC), deadline));
+  GRPC_LOG_IF_ERROR(
+      "pollset_work",
+      grpc_pollset_work(&exec_ctx, server->pollset, &worker, deadline));
   gpr_mu_unlock(server->mu);
   grpc_exec_ctx_finish(&exec_ctx);
 }
diff --git a/test/cpp/common/alarm_cpp_test.cc b/test/cpp/common/alarm_cpp_test.cc
index 212972d..7adc310 100644
--- a/test/cpp/common/alarm_cpp_test.cc
+++ b/test/cpp/common/alarm_cpp_test.cc
@@ -142,7 +142,7 @@
   void* output_tag;
   bool ok;
   const CompletionQueue::NextStatus status = cq.AsyncNext(
-      (void**)&output_tag, &ok, grpc_timeout_seconds_to_deadline(0));
+      (void**)&output_tag, &ok, grpc_timeout_seconds_to_deadline(1));
 
   EXPECT_EQ(status, CompletionQueue::GOT_EVENT);
   EXPECT_TRUE(ok);
@@ -158,7 +158,7 @@
   void* output_tag;
   bool ok;
   const CompletionQueue::NextStatus status = cq.AsyncNext(
-      (void**)&output_tag, &ok, grpc_timeout_seconds_to_deadline(0));
+      (void**)&output_tag, &ok, grpc_timeout_seconds_to_deadline(1));
 
   EXPECT_EQ(status, CompletionQueue::GOT_EVENT);
   EXPECT_TRUE(ok);
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index e54cd03..5dae5b0 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -1673,6 +1673,34 @@
                 kTestCredsPluginErrorMsg);
 }
 
+TEST_P(SecureEnd2endTest, CompositeCallCreds) {
+  ResetStub();
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+  const char kMetadataKey1[] = "call-creds-key1";
+  const char kMetadataKey2[] = "call-creds-key2";
+  const char kMetadataVal1[] = "call-creds-val1";
+  const char kMetadataVal2[] = "call-creds-val2";
+
+  context.set_credentials(CompositeCallCredentials(
+      MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin>(
+          new TestMetadataCredentialsPlugin(kMetadataKey1, kMetadataVal1, true,
+                                            true))),
+      MetadataCredentialsFromPlugin(std::unique_ptr<MetadataCredentialsPlugin>(
+          new TestMetadataCredentialsPlugin(kMetadataKey2, kMetadataVal2, true,
+                                            true)))));
+  request.set_message("Hello");
+  request.mutable_param()->set_echo_metadata(true);
+
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_TRUE(s.ok());
+  EXPECT_TRUE(MetadataContains(context.GetServerTrailingMetadata(),
+                               kMetadataKey1, kMetadataVal1));
+  EXPECT_TRUE(MetadataContains(context.GetServerTrailingMetadata(),
+                               kMetadataKey2, kMetadataVal2));
+}
+
 TEST_P(SecureEnd2endTest, ClientAuthContext) {
   ResetStub();
   EchoRequest request;
diff --git a/test/cpp/end2end/generic_end2end_test.cc b/test/cpp/end2end/generic_end2end_test.cc
index b9e6e18..9450182 100644
--- a/test/cpp/end2end/generic_end2end_test.cc
+++ b/test/cpp/end2end/generic_end2end_test.cc
@@ -145,7 +145,7 @@
 
       if (check_deadline) {
         EXPECT_TRUE(gpr_time_similar(deadline, srv_ctx.raw_deadline(),
-                                     gpr_time_from_millis(100, GPR_TIMESPAN)));
+                                     gpr_time_from_millis(1000, GPR_TIMESPAN)));
       }
 
       ByteBuffer recv_buffer;
@@ -196,6 +196,62 @@
   SendRpc(10);
 }
 
+TEST_F(GenericEnd2endTest, SequentialUnaryRpcs) {
+  ResetStub();
+  const int num_rpcs = 10;
+  const grpc::string kMethodName("/grpc.cpp.test.util.EchoTestService/Echo");
+  for (int i = 0; i < num_rpcs; i++) {
+    EchoRequest send_request;
+    EchoRequest recv_request;
+    EchoResponse send_response;
+    EchoResponse recv_response;
+    Status recv_status;
+
+    ClientContext cli_ctx;
+    GenericServerContext srv_ctx;
+    GenericServerAsyncReaderWriter stream(&srv_ctx);
+
+    // The string needs to be long enough to test heap-based slice.
+    send_request.set_message("Hello world. Hello world. Hello world.");
+
+    std::unique_ptr<ByteBuffer> cli_send_buffer =
+        SerializeToByteBuffer(&send_request);
+    std::unique_ptr<GenericClientAsyncResponseReader> call =
+        generic_stub_->PrepareUnaryCall(&cli_ctx, kMethodName,
+                                        *cli_send_buffer.get(), &cli_cq_);
+    call->StartCall();
+    ByteBuffer cli_recv_buffer;
+    call->Finish(&cli_recv_buffer, &recv_status, tag(1));
+
+    generic_service_.RequestCall(&srv_ctx, &stream, srv_cq_.get(),
+                                 srv_cq_.get(), tag(4));
+
+    verify_ok(srv_cq_.get(), 4, true);
+    EXPECT_EQ(server_host_, srv_ctx.host().substr(0, server_host_.length()));
+    EXPECT_EQ(kMethodName, srv_ctx.method());
+
+    ByteBuffer srv_recv_buffer;
+    stream.Read(&srv_recv_buffer, tag(5));
+    server_ok(5);
+    EXPECT_TRUE(ParseFromByteBuffer(&srv_recv_buffer, &recv_request));
+    EXPECT_EQ(send_request.message(), recv_request.message());
+
+    send_response.set_message(recv_request.message());
+    std::unique_ptr<ByteBuffer> srv_send_buffer =
+        SerializeToByteBuffer(&send_response);
+    stream.Write(*srv_send_buffer, tag(6));
+    server_ok(6);
+
+    stream.Finish(Status::OK, tag(7));
+    server_ok(7);
+
+    client_ok(1);
+    EXPECT_TRUE(ParseFromByteBuffer(&cli_recv_buffer, &recv_response));
+    EXPECT_EQ(send_response.message(), recv_response.message());
+    EXPECT_TRUE(recv_status.ok());
+  }
+}
+
 // One ping, one pong.
 TEST_F(GenericEnd2endTest, SimpleBidiStreaming) {
   ResetStub();
diff --git a/test/cpp/end2end/grpclb_end2end_test.cc b/test/cpp/end2end/grpclb_end2end_test.cc
index 570a3d1..f73a9c1 100644
--- a/test/cpp/end2end/grpclb_end2end_test.cc
+++ b/test/cpp/end2end/grpclb_end2end_test.cc
@@ -368,8 +368,9 @@
     grpc_fake_resolver_response_generator_unref(response_generator_);
   }
 
-  void ResetStub() {
+  void ResetStub(int fallback_timeout = 0) {
     ChannelArguments args;
+    args.SetGrpclbFallbackTimeout(fallback_timeout);
     args.SetPointer(GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR,
                     response_generator_);
     std::ostringstream uri;
@@ -398,11 +399,40 @@
     return true;
   }
 
-  void WaitForAllBackends() {
+  void SendRpcAndCount(int* num_total, int* num_ok, int* num_failure,
+                       int* num_drops) {
+    const Status status = SendRpc();
+    if (status.ok()) {
+      ++*num_ok;
+    } else {
+      if (status.error_message() == "Call dropped by load balancing policy") {
+        ++*num_drops;
+      } else {
+        ++*num_failure;
+      }
+    }
+    ++*num_total;
+  }
+
+  std::tuple<int, int, int> WaitForAllBackends(
+      int num_requests_multiple_of = 1) {
+    int num_ok = 0;
+    int num_failure = 0;
+    int num_drops = 0;
+    int num_total = 0;
     while (!SeenAllBackends()) {
-      CheckRpcSendOk();
+      SendRpcAndCount(&num_total, &num_ok, &num_failure, &num_drops);
+    }
+    while (num_total % num_requests_multiple_of != 0) {
+      SendRpcAndCount(&num_total, &num_ok, &num_failure, &num_drops);
     }
     ResetBackendCounters();
+    gpr_log(GPR_INFO,
+            "Performed %d warm up requests (a multiple of %d) against the "
+            "backends. %d succeeded, %d failed, %d dropped.",
+            num_total, num_requests_multiple_of, num_ok, num_failure,
+            num_drops);
+    return std::make_tuple(num_ok, num_failure, num_drops);
   }
 
   void WaitForBackend(size_t backend_idx) {
@@ -441,10 +471,10 @@
     grpc_exec_ctx_finish(&exec_ctx);
   }
 
-  const std::vector<int> GetBackendPorts() const {
+  const std::vector<int> GetBackendPorts(const size_t start_index = 0) const {
     std::vector<int> backend_ports;
-    for (const auto& bs : backend_servers_) {
-      backend_ports.push_back(bs.port_);
+    for (size_t i = start_index; i < backend_servers_.size(); ++i) {
+      backend_ports.push_back(backend_servers_[i].port_);
     }
     return backend_ports;
   }
@@ -556,10 +586,8 @@
       0);
   // Make sure that trying to connect works without a call.
   channel_->GetState(true /* try_to_connect */);
-
   // We need to wait for all backends to come online.
   WaitForAllBackends();
-
   // Send kNumRpcsPerAddress RPCs per server.
   CheckRpcSendOk(kNumRpcsPerAddress * num_backends_);
 
@@ -615,6 +643,177 @@
   EXPECT_EQ("grpclb", channel_->GetLoadBalancingPolicyName());
 }
 
+TEST_F(SingleBalancerTest, Fallback) {
+  const int kFallbackTimeoutMs = 200 * grpc_test_slowdown_factor();
+  const int kServerlistDelayMs = 500 * grpc_test_slowdown_factor();
+  const size_t kNumBackendInResolution = backends_.size() / 2;
+
+  ResetStub(kFallbackTimeoutMs);
+  std::vector<AddressData> addresses;
+  addresses.emplace_back(AddressData{balancer_servers_[0].port_, true, ""});
+  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+    addresses.emplace_back(AddressData{backend_servers_[i].port_, false, ""});
+  }
+  SetNextResolution(addresses);
+
+  // Send non-empty serverlist only after kServerlistDelayMs.
+  ScheduleResponseForBalancer(
+      0, BalancerServiceImpl::BuildResponseForBackends(
+             GetBackendPorts(kNumBackendInResolution /* start_index */), {}),
+      kServerlistDelayMs);
+
+  // Wait until all the fallback backends are reachable.
+  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+    WaitForBackend(i);
+  }
+
+  // The first request.
+  gpr_log(GPR_INFO, "========= BEFORE FIRST BATCH ==========");
+  CheckRpcSendOk(kNumBackendInResolution);
+  gpr_log(GPR_INFO, "========= DONE WITH FIRST BATCH ==========");
+
+  // Fallback is used: each backend returned by the resolver should have
+  // gotten one request.
+  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+    EXPECT_EQ(1U, backend_servers_[i].service_->request_count());
+  }
+  for (size_t i = kNumBackendInResolution; i < backends_.size(); ++i) {
+    EXPECT_EQ(0U, backend_servers_[i].service_->request_count());
+  }
+
+  // Wait until the serverlist reception has been processed and all backends
+  // in the serverlist are reachable.
+  for (size_t i = kNumBackendInResolution; i < backends_.size(); ++i) {
+    WaitForBackend(i);
+  }
+
+  // Send out the second request.
+  gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
+  CheckRpcSendOk(backends_.size() - kNumBackendInResolution);
+  gpr_log(GPR_INFO, "========= DONE WITH SECOND BATCH ==========");
+
+  // Serverlist is used: each backend returned by the balancer should
+  // have gotten one request.
+  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+    EXPECT_EQ(0U, backend_servers_[i].service_->request_count());
+  }
+  for (size_t i = kNumBackendInResolution; i < backends_.size(); ++i) {
+    EXPECT_EQ(1U, backend_servers_[i].service_->request_count());
+  }
+
+  balancers_[0]->NotifyDoneWithServerlists();
+  // The balancer got a single request.
+  EXPECT_EQ(1U, balancer_servers_[0].service_->request_count());
+  // and sent a single response.
+  EXPECT_EQ(1U, balancer_servers_[0].service_->response_count());
+}
+
+TEST_F(SingleBalancerTest, FallbackUpdate) {
+  const int kFallbackTimeoutMs = 200 * grpc_test_slowdown_factor();
+  const int kServerlistDelayMs = 500 * grpc_test_slowdown_factor();
+  const size_t kNumBackendInResolution = backends_.size() / 3;
+  const size_t kNumBackendInResolutionUpdate = backends_.size() / 3;
+
+  ResetStub(kFallbackTimeoutMs);
+  std::vector<AddressData> addresses;
+  addresses.emplace_back(AddressData{balancer_servers_[0].port_, true, ""});
+  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+    addresses.emplace_back(AddressData{backend_servers_[i].port_, false, ""});
+  }
+  SetNextResolution(addresses);
+
+  // Send non-empty serverlist only after kServerlistDelayMs.
+  ScheduleResponseForBalancer(
+      0, BalancerServiceImpl::BuildResponseForBackends(
+             GetBackendPorts(kNumBackendInResolution +
+                             kNumBackendInResolutionUpdate /* start_index */),
+             {}),
+      kServerlistDelayMs);
+
+  // Wait until all the fallback backends are reachable.
+  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+    WaitForBackend(i);
+  }
+
+  // The first request.
+  gpr_log(GPR_INFO, "========= BEFORE FIRST BATCH ==========");
+  CheckRpcSendOk(kNumBackendInResolution);
+  gpr_log(GPR_INFO, "========= DONE WITH FIRST BATCH ==========");
+
+  // Fallback is used: each backend returned by the resolver should have
+  // gotten one request.
+  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+    EXPECT_EQ(1U, backend_servers_[i].service_->request_count());
+  }
+  for (size_t i = kNumBackendInResolution; i < backends_.size(); ++i) {
+    EXPECT_EQ(0U, backend_servers_[i].service_->request_count());
+  }
+
+  addresses.clear();
+  addresses.emplace_back(AddressData{balancer_servers_[0].port_, true, ""});
+  for (size_t i = kNumBackendInResolution;
+       i < kNumBackendInResolution + kNumBackendInResolutionUpdate; ++i) {
+    addresses.emplace_back(AddressData{backend_servers_[i].port_, false, ""});
+  }
+  SetNextResolution(addresses);
+
+  // Wait until the resolution update has been processed and all the new
+  // fallback backends are reachable.
+  for (size_t i = kNumBackendInResolution;
+       i < kNumBackendInResolution + kNumBackendInResolutionUpdate; ++i) {
+    WaitForBackend(i);
+  }
+
+  // Send out the second request.
+  gpr_log(GPR_INFO, "========= BEFORE SECOND BATCH ==========");
+  CheckRpcSendOk(kNumBackendInResolutionUpdate);
+  gpr_log(GPR_INFO, "========= DONE WITH SECOND BATCH ==========");
+
+  // The resolution update is used: each backend in the resolution update should
+  // have gotten one request.
+  for (size_t i = 0; i < kNumBackendInResolution; ++i) {
+    EXPECT_EQ(0U, backend_servers_[i].service_->request_count());
+  }
+  for (size_t i = kNumBackendInResolution;
+       i < kNumBackendInResolution + kNumBackendInResolutionUpdate; ++i) {
+    EXPECT_EQ(1U, backend_servers_[i].service_->request_count());
+  }
+  for (size_t i = kNumBackendInResolution + kNumBackendInResolutionUpdate;
+       i < backends_.size(); ++i) {
+    EXPECT_EQ(0U, backend_servers_[i].service_->request_count());
+  }
+
+  // Wait until the serverlist reception has been processed and all backends
+  // in the serverlist are reachable.
+  for (size_t i = kNumBackendInResolution + kNumBackendInResolutionUpdate;
+       i < backends_.size(); ++i) {
+    WaitForBackend(i);
+  }
+
+  // Send out the third request.
+  gpr_log(GPR_INFO, "========= BEFORE THIRD BATCH ==========");
+  CheckRpcSendOk(backends_.size() - kNumBackendInResolution -
+                 kNumBackendInResolutionUpdate);
+  gpr_log(GPR_INFO, "========= DONE WITH THIRD BATCH ==========");
+
+  // Serverlist is used: each backend returned by the balancer should
+  // have gotten one request.
+  for (size_t i = 0;
+       i < kNumBackendInResolution + kNumBackendInResolutionUpdate; ++i) {
+    EXPECT_EQ(0U, backend_servers_[i].service_->request_count());
+  }
+  for (size_t i = kNumBackendInResolution + kNumBackendInResolutionUpdate;
+       i < backends_.size(); ++i) {
+    EXPECT_EQ(1U, backend_servers_[i].service_->request_count());
+  }
+
+  balancers_[0]->NotifyDoneWithServerlists();
+  // The balancer got a single request.
+  EXPECT_EQ(1U, balancer_servers_[0].service_->request_count());
+  // and sent a single response.
+  EXPECT_EQ(1U, balancer_servers_[0].service_->response_count());
+}
+
 TEST_F(SingleBalancerTest, BackendsRestart) {
   const size_t kNumRpcsPerAddress = 100;
   ScheduleResponseForBalancer(
@@ -863,13 +1062,22 @@
 
 TEST_F(SingleBalancerTest, Drop) {
   const size_t kNumRpcsPerAddress = 100;
+  const int num_of_drop_by_rate_limiting_addresses = 1;
+  const int num_of_drop_by_load_balancing_addresses = 2;
+  const int num_of_drop_addresses = num_of_drop_by_rate_limiting_addresses +
+                                    num_of_drop_by_load_balancing_addresses;
+  const int num_total_addresses = num_backends_ + num_of_drop_addresses;
   ScheduleResponseForBalancer(
       0, BalancerServiceImpl::BuildResponseForBackends(
-             GetBackendPorts(), {{"rate_limiting", 1}, {"load_balancing", 2}}),
+             GetBackendPorts(),
+             {{"rate_limiting", num_of_drop_by_rate_limiting_addresses},
+              {"load_balancing", num_of_drop_by_load_balancing_addresses}}),
       0);
+  // Wait until all backends are ready.
+  WaitForAllBackends();
   // Send kNumRpcsPerAddress RPCs for each server and drop address.
   size_t num_drops = 0;
-  for (size_t i = 0; i < kNumRpcsPerAddress * (num_backends_ + 3); ++i) {
+  for (size_t i = 0; i < kNumRpcsPerAddress * num_total_addresses; ++i) {
     EchoResponse response;
     const Status status = SendRpc(&response);
     if (!status.ok() &&
@@ -881,7 +1089,7 @@
       EXPECT_EQ(response.message(), kMessage_);
     }
   }
-  EXPECT_EQ(kNumRpcsPerAddress * 3, num_drops);
+  EXPECT_EQ(kNumRpcsPerAddress * num_of_drop_addresses, num_drops);
 
   // Each backend should have gotten 100 requests.
   for (size_t i = 0; i < backends_.size(); ++i) {
@@ -896,9 +1104,12 @@
 
 TEST_F(SingleBalancerTest, DropAllFirst) {
   // All registered addresses are marked as "drop".
+  const int num_of_drop_by_rate_limiting_addresses = 1;
+  const int num_of_drop_by_load_balancing_addresses = 1;
   ScheduleResponseForBalancer(
       0, BalancerServiceImpl::BuildResponseForBackends(
-             {}, {{"rate_limiting", 1}, {"load_balancing", 1}}),
+             {}, {{"rate_limiting", num_of_drop_by_rate_limiting_addresses},
+                  {"load_balancing", num_of_drop_by_load_balancing_addresses}}),
       0);
   const Status status = SendRpc();
   EXPECT_FALSE(status.ok());
@@ -909,9 +1120,12 @@
   ScheduleResponseForBalancer(
       0, BalancerServiceImpl::BuildResponseForBackends(GetBackendPorts(), {}),
       0);
+  const int num_of_drop_by_rate_limiting_addresses = 1;
+  const int num_of_drop_by_load_balancing_addresses = 1;
   ScheduleResponseForBalancer(
       0, BalancerServiceImpl::BuildResponseForBackends(
-             {}, {{"rate_limiting", 1}, {"load_balancing", 1}}),
+             {}, {{"rate_limiting", num_of_drop_by_rate_limiting_addresses},
+                  {"load_balancing", num_of_drop_by_load_balancing_addresses}}),
       1000);
 
   // First call succeeds.
@@ -936,6 +1150,11 @@
   ScheduleResponseForBalancer(
       0, BalancerServiceImpl::BuildResponseForBackends(GetBackendPorts(), {}),
       0);
+  // Wait until all backends are ready.
+  int num_ok = 0;
+  int num_failure = 0;
+  int num_drops = 0;
+  std::tie(num_ok, num_failure, num_drops) = WaitForAllBackends();
   // Send kNumRpcsPerAddress RPCs per server.
   CheckRpcSendOk(kNumRpcsPerAddress * num_backends_);
   // Each backend should have gotten 100 requests.
@@ -950,24 +1169,39 @@
   EXPECT_EQ(1U, balancer_servers_[0].service_->response_count());
 
   const ClientStats client_stats = WaitForLoadReports();
-  EXPECT_EQ(kNumRpcsPerAddress * num_backends_, client_stats.num_calls_started);
-  EXPECT_EQ(kNumRpcsPerAddress * num_backends_,
+  EXPECT_EQ(kNumRpcsPerAddress * num_backends_ + num_ok,
+            client_stats.num_calls_started);
+  EXPECT_EQ(kNumRpcsPerAddress * num_backends_ + num_ok,
             client_stats.num_calls_finished);
   EXPECT_EQ(0U, client_stats.num_calls_finished_with_client_failed_to_send);
-  EXPECT_EQ(kNumRpcsPerAddress * num_backends_,
+  EXPECT_EQ(kNumRpcsPerAddress * num_backends_ + (num_ok + num_drops),
             client_stats.num_calls_finished_known_received);
   EXPECT_THAT(client_stats.drop_token_counts, ::testing::ElementsAre());
 }
 
 TEST_F(SingleBalancerWithClientLoadReportingTest, Drop) {
   const size_t kNumRpcsPerAddress = 3;
+  const int num_of_drop_by_rate_limiting_addresses = 2;
+  const int num_of_drop_by_load_balancing_addresses = 1;
+  const int num_of_drop_addresses = num_of_drop_by_rate_limiting_addresses +
+                                    num_of_drop_by_load_balancing_addresses;
+  const int num_total_addresses = num_backends_ + num_of_drop_addresses;
   ScheduleResponseForBalancer(
       0, BalancerServiceImpl::BuildResponseForBackends(
-             GetBackendPorts(), {{"rate_limiting", 2}, {"load_balancing", 1}}),
+             GetBackendPorts(),
+             {{"rate_limiting", num_of_drop_by_rate_limiting_addresses},
+              {"load_balancing", num_of_drop_by_load_balancing_addresses}}),
       0);
-
+  // Wait until all backends are ready.
+  int num_warmup_ok = 0;
+  int num_warmup_failure = 0;
+  int num_warmup_drops = 0;
+  std::tie(num_warmup_ok, num_warmup_failure, num_warmup_drops) =
+      WaitForAllBackends(num_total_addresses /* num_requests_multiple_of */);
+  const int num_total_warmup_requests =
+      num_warmup_ok + num_warmup_failure + num_warmup_drops;
   size_t num_drops = 0;
-  for (size_t i = 0; i < kNumRpcsPerAddress * (num_backends_ + 3); ++i) {
+  for (size_t i = 0; i < kNumRpcsPerAddress * num_total_addresses; ++i) {
     EchoResponse response;
     const Status status = SendRpc(&response);
     if (!status.ok() &&
@@ -979,8 +1213,7 @@
       EXPECT_EQ(response.message(), kMessage_);
     }
   }
-  EXPECT_EQ(kNumRpcsPerAddress * 3, num_drops);
-
+  EXPECT_EQ(kNumRpcsPerAddress * num_of_drop_addresses, num_drops);
   // Each backend should have gotten 100 requests.
   for (size_t i = 0; i < backends_.size(); ++i) {
     EXPECT_EQ(kNumRpcsPerAddress,
@@ -993,17 +1226,28 @@
   EXPECT_EQ(1U, balancer_servers_[0].service_->response_count());
 
   const ClientStats client_stats = WaitForLoadReports();
-  EXPECT_EQ(kNumRpcsPerAddress * (num_backends_ + 3),
-            client_stats.num_calls_started);
-  EXPECT_EQ(kNumRpcsPerAddress * (num_backends_ + 3),
-            client_stats.num_calls_finished);
+  EXPECT_EQ(
+      kNumRpcsPerAddress * num_total_addresses + num_total_warmup_requests,
+      client_stats.num_calls_started);
+  EXPECT_EQ(
+      kNumRpcsPerAddress * num_total_addresses + num_total_warmup_requests,
+      client_stats.num_calls_finished);
   EXPECT_EQ(0U, client_stats.num_calls_finished_with_client_failed_to_send);
-  EXPECT_EQ(kNumRpcsPerAddress * num_backends_,
+  EXPECT_EQ(kNumRpcsPerAddress * num_backends_ + num_warmup_ok,
             client_stats.num_calls_finished_known_received);
-  EXPECT_THAT(client_stats.drop_token_counts,
-              ::testing::ElementsAre(
-                  ::testing::Pair("load_balancing", kNumRpcsPerAddress),
-                  ::testing::Pair("rate_limiting", kNumRpcsPerAddress * 2)));
+  // The number of warmup request is a multiple of the number of addresses.
+  // Therefore, all addresses in the scheduled balancer response are hit the
+  // same number of times.
+  const int num_times_drop_addresses_hit =
+      num_warmup_drops / num_of_drop_addresses;
+  EXPECT_THAT(
+      client_stats.drop_token_counts,
+      ::testing::ElementsAre(
+          ::testing::Pair("load_balancing",
+                          (kNumRpcsPerAddress + num_times_drop_addresses_hit)),
+          ::testing::Pair(
+              "rate_limiting",
+              (kNumRpcsPerAddress + num_times_drop_addresses_hit) * 2)));
 }
 
 }  // namespace
diff --git a/test/cpp/microbenchmarks/bm_call_create.cc b/test/cpp/microbenchmarks/bm_call_create.cc
index cadc9b2..cf9a42e 100644
--- a/test/cpp/microbenchmarks/bm_call_create.cc
+++ b/test/cpp/microbenchmarks/bm_call_create.cc
@@ -554,7 +554,7 @@
   grpc_exec_ctx_flush(&exec_ctx);
   grpc_call_stack *call_stack = static_cast<grpc_call_stack *>(
       gpr_zalloc(channel_stack->call_stack_size));
-  gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  grpc_millis deadline = GRPC_MILLIS_INF_FUTURE;
   gpr_timespec start_time = gpr_now(GPR_CLOCK_MONOTONIC);
   grpc_slice method = grpc_slice_from_static_string("/foo/bar");
   grpc_call_final_info final_info;
diff --git a/test/cpp/microbenchmarks/bm_chttp2_transport.cc b/test/cpp/microbenchmarks/bm_chttp2_transport.cc
index 070034f..6f9dee7 100644
--- a/test/cpp/microbenchmarks/bm_chttp2_transport.cc
+++ b/test/cpp/microbenchmarks/bm_chttp2_transport.cc
@@ -321,7 +321,7 @@
 
   grpc_metadata_batch b;
   grpc_metadata_batch_init(&b);
-  b.deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  b.deadline = GRPC_MILLIS_INF_FUTURE;
   std::vector<grpc_mdelem> elems = Metadata::GetElems(f.exec_ctx());
   std::vector<grpc_linked_mdelem> storage(elems.size());
   for (size_t i = 0; i < elems.size(); i++) {
@@ -410,7 +410,7 @@
 
   grpc_metadata_batch b;
   grpc_metadata_batch_init(&b);
-  b.deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  b.deadline = GRPC_MILLIS_INF_FUTURE;
   std::vector<grpc_mdelem> elems =
       RepresentativeClientInitialMetadata::GetElems(f.exec_ctx());
   std::vector<grpc_linked_mdelem> storage(elems.size());
@@ -542,7 +542,7 @@
   grpc_metadata_batch_init(&b);
   grpc_metadata_batch b_recv;
   grpc_metadata_batch_init(&b_recv);
-  b.deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  b.deadline = GRPC_MILLIS_INF_FUTURE;
   std::vector<grpc_mdelem> elems =
       RepresentativeClientInitialMetadata::GetElems(f.exec_ctx());
   std::vector<grpc_linked_mdelem> storage(elems.size());
diff --git a/test/cpp/microbenchmarks/bm_cq_multiple_threads.cc b/test/cpp/microbenchmarks/bm_cq_multiple_threads.cc
index 5c9405f..57a69ac 100644
--- a/test/cpp/microbenchmarks/bm_cq_multiple_threads.cc
+++ b/test/cpp/microbenchmarks/bm_cq_multiple_threads.cc
@@ -73,9 +73,9 @@
 /* Queues a completion tag if deadline is > 0.
  * Does nothing if deadline is 0 (i.e gpr_time_0(GPR_CLOCK_MONOTONIC)) */
 static grpc_error* pollset_work(grpc_exec_ctx* exec_ctx, grpc_pollset* ps,
-                                grpc_pollset_worker** worker, gpr_timespec now,
-                                gpr_timespec deadline) {
-  if (gpr_time_cmp(deadline, gpr_time_0(GPR_CLOCK_MONOTONIC)) == 0) {
+                                grpc_pollset_worker** worker,
+                                grpc_millis deadline) {
+  if (deadline == 0) {
     gpr_log(GPR_DEBUG, "no-op");
     return GRPC_ERROR_NONE;
   }
diff --git a/test/cpp/microbenchmarks/bm_error.cc b/test/cpp/microbenchmarks/bm_error.cc
index bd5f02e..56b80df 100644
--- a/test/cpp/microbenchmarks/bm_error.cc
+++ b/test/cpp/microbenchmarks/bm_error.cc
@@ -159,39 +159,39 @@
 // Fixtures for tests: generate different kinds of errors
 class ErrorNone {
  public:
-  gpr_timespec deadline() const { return deadline_; }
+  grpc_millis deadline() const { return deadline_; }
   grpc_error* error() const { return GRPC_ERROR_NONE; }
 
  private:
-  const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  const grpc_millis deadline_ = GRPC_MILLIS_INF_FUTURE;
 };
 
 class ErrorCancelled {
  public:
-  gpr_timespec deadline() const { return deadline_; }
+  grpc_millis deadline() const { return deadline_; }
   grpc_error* error() const { return GRPC_ERROR_CANCELLED; }
 
  private:
-  const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  const grpc_millis deadline_ = GRPC_MILLIS_INF_FUTURE;
 };
 
 class SimpleError {
  public:
-  gpr_timespec deadline() const { return deadline_; }
+  grpc_millis deadline() const { return deadline_; }
   grpc_error* error() const { return error_.get(); }
 
  private:
-  const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  const grpc_millis deadline_ = GRPC_MILLIS_INF_FUTURE;
   ErrorPtr error_{GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error")};
 };
 
 class ErrorWithGrpcStatus {
  public:
-  gpr_timespec deadline() const { return deadline_; }
+  grpc_millis deadline() const { return deadline_; }
   grpc_error* error() const { return error_.get(); }
 
  private:
-  const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  const grpc_millis deadline_ = GRPC_MILLIS_INF_FUTURE;
   ErrorPtr error_{grpc_error_set_int(
       GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"), GRPC_ERROR_INT_GRPC_STATUS,
       GRPC_STATUS_UNIMPLEMENTED)};
@@ -199,11 +199,11 @@
 
 class ErrorWithHttpError {
  public:
-  gpr_timespec deadline() const { return deadline_; }
+  grpc_millis deadline() const { return deadline_; }
   grpc_error* error() const { return error_.get(); }
 
  private:
-  const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  const grpc_millis deadline_ = GRPC_MILLIS_INF_FUTURE;
   ErrorPtr error_{grpc_error_set_int(
       GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"), GRPC_ERROR_INT_HTTP2_ERROR,
       GRPC_HTTP2_COMPRESSION_ERROR)};
@@ -211,11 +211,11 @@
 
 class ErrorWithNestedGrpcStatus {
  public:
-  gpr_timespec deadline() const { return deadline_; }
+  grpc_millis deadline() const { return deadline_; }
   grpc_error* error() const { return error_.get(); }
 
  private:
-  const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  const grpc_millis deadline_ = GRPC_MILLIS_INF_FUTURE;
   ErrorPtr nested_error_{grpc_error_set_int(
       GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error"), GRPC_ERROR_INT_GRPC_STATUS,
       GRPC_STATUS_UNIMPLEMENTED)};
@@ -248,12 +248,14 @@
 static void BM_ErrorGetStatus(benchmark::State& state) {
   TrackCounters track_counters;
   Fixture fixture;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   while (state.KeepRunning()) {
     grpc_status_code status;
     grpc_slice slice;
-    grpc_error_get_status(fixture.error(), fixture.deadline(), &status, &slice,
-                          NULL);
+    grpc_error_get_status(&exec_ctx, fixture.error(), fixture.deadline(),
+                          &status, &slice, NULL);
   }
+  grpc_exec_ctx_finish(&exec_ctx);
   track_counters.Finish(state);
 }
 
@@ -261,11 +263,13 @@
 static void BM_ErrorGetStatusCode(benchmark::State& state) {
   TrackCounters track_counters;
   Fixture fixture;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   while (state.KeepRunning()) {
     grpc_status_code status;
-    grpc_error_get_status(fixture.error(), fixture.deadline(), &status, NULL,
-                          NULL);
+    grpc_error_get_status(&exec_ctx, fixture.error(), fixture.deadline(),
+                          &status, NULL, NULL);
   }
+  grpc_exec_ctx_finish(&exec_ctx);
   track_counters.Finish(state);
 }
 
@@ -273,11 +277,13 @@
 static void BM_ErrorHttpError(benchmark::State& state) {
   TrackCounters track_counters;
   Fixture fixture;
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   while (state.KeepRunning()) {
     grpc_http2_error_code error;
-    grpc_error_get_status(fixture.error(), fixture.deadline(), NULL, NULL,
-                          &error);
+    grpc_error_get_status(&exec_ctx, fixture.error(), fixture.deadline(), NULL,
+                          NULL, &error);
   }
+  grpc_exec_ctx_finish(&exec_ctx);
   track_counters.Finish(state);
 }
 
diff --git a/test/cpp/microbenchmarks/bm_fullstack_trickle.cc b/test/cpp/microbenchmarks/bm_fullstack_trickle.cc
index 2656566..adb5e66 100644
--- a/test/cpp/microbenchmarks/bm_fullstack_trickle.cc
+++ b/test/cpp/microbenchmarks/bm_fullstack_trickle.cc
@@ -29,6 +29,7 @@
 extern "C" {
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
+#include "src/core/lib/iomgr/timer_manager.h"
 #include "test/core/util/trickle_endpoint.h"
 }
 
@@ -45,6 +46,22 @@
 namespace grpc {
 namespace testing {
 
+gpr_atm g_now_us = 0;
+
+static gpr_timespec fake_now(gpr_clock_type clock_type) {
+  gpr_timespec t;
+  gpr_atm now = gpr_atm_no_barrier_load(&g_now_us);
+  t.tv_sec = now / GPR_US_PER_SEC;
+  t.tv_nsec = (now % GPR_US_PER_SEC) * GPR_NS_PER_US;
+  t.clock_type = clock_type;
+  return t;
+}
+
+static void inc_time() {
+  gpr_atm_no_barrier_fetch_add(&g_now_us, 100);
+  grpc_timer_manager_tick();
+}
+
 static void* tag(intptr_t x) { return reinterpret_cast<void*>(x); }
 
 template <class A0>
@@ -158,6 +175,7 @@
 
   void Step(bool update_stats) {
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+    inc_time();
     size_t client_backlog =
         grpc_trickle_endpoint_trickle(&exec_ctx, endpoint_pair_.client);
     size_t server_backlog =
@@ -212,9 +230,8 @@
                           int64_t iteration) {
   while (true) {
     fixture->Log(iteration);
-    switch (fixture->cq()->AsyncNext(
-        t, ok, gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
-                            gpr_time_from_micros(100, GPR_TIMESPAN)))) {
+    switch (
+        fixture->cq()->AsyncNext(t, ok, gpr_inf_past(GPR_CLOCK_MONOTONIC))) {
       case CompletionQueue::TIMEOUT:
         fixture->Step(iteration != -1);
         break;
@@ -289,9 +306,15 @@
       inner_loop(false);
     }
     response_rw.Finish(Status::OK, tag(1));
-    need_tags = (1 << 0) | (1 << 1);
+    grpc::Status status;
+    request_rw->Finish(&status, tag(2));
+    need_tags = (1 << 0) | (1 << 1) | (1 << 2);
     while (need_tags) {
       TrickleCQNext(fixture.get(), &t, &ok, -1);
+      if (t == tag(0) && ok) {
+        request_rw->Read(&recv_response, tag(0));
+        continue;
+      }
       int i = (int)(intptr_t)t;
       GPR_ASSERT(need_tags & (1 << i));
       need_tags &= ~(1 << i);
@@ -419,8 +442,12 @@
 }
 }
 
+extern "C" gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type);
+
 int main(int argc, char** argv) {
   ::benchmark::Initialize(&argc, argv);
   ::grpc::testing::InitTest(&argc, &argv, false);
+  grpc_timer_manager_set_threading(false);
+  gpr_now_impl = ::grpc::testing::fake_now;
   ::benchmark::RunSpecifiedBenchmarks();
 }
diff --git a/test/cpp/microbenchmarks/bm_pollset.cc b/test/cpp/microbenchmarks/bm_pollset.cc
index 1fc1f2f..eab1e89 100644
--- a/test/cpp/microbenchmarks/bm_pollset.cc
+++ b/test/cpp/microbenchmarks/bm_pollset.cc
@@ -117,11 +117,9 @@
   gpr_mu* mu;
   grpc_pollset_init(ps, &mu);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  gpr_timespec now = gpr_time_0(GPR_CLOCK_MONOTONIC);
-  gpr_timespec deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC);
   gpr_mu_lock(mu);
   while (state.KeepRunning()) {
-    GRPC_ERROR_UNREF(grpc_pollset_work(&exec_ctx, ps, NULL, now, deadline));
+    GRPC_ERROR_UNREF(grpc_pollset_work(&exec_ctx, ps, NULL, 0));
   }
   grpc_closure shutdown_ps_closure;
   GRPC_CLOSURE_INIT(&shutdown_ps_closure, shutdown_ps, ps,
@@ -223,8 +221,6 @@
   gpr_mu* mu;
   grpc_pollset_init(ps, &mu);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  gpr_timespec now = gpr_time_0(GPR_CLOCK_MONOTONIC);
-  gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
   grpc_wakeup_fd wakeup_fd;
   GRPC_ERROR_UNREF(grpc_wakeup_fd_init(&wakeup_fd));
   grpc_fd* wakeup = grpc_fd_create(wakeup_fd.read_fd, "wakeup_read");
@@ -245,7 +241,8 @@
   grpc_fd_notify_on_read(&exec_ctx, wakeup, continue_closure);
   gpr_mu_lock(mu);
   while (!done) {
-    GRPC_ERROR_UNREF(grpc_pollset_work(&exec_ctx, ps, NULL, now, deadline));
+    GRPC_ERROR_UNREF(
+        grpc_pollset_work(&exec_ctx, ps, NULL, GRPC_MILLIS_INF_FUTURE));
   }
   grpc_fd_orphan(&exec_ctx, wakeup, NULL, NULL, false /* already_closed */,
                  "done");
diff --git a/test/cpp/microbenchmarks/fullstack_fixtures.h b/test/cpp/microbenchmarks/fullstack_fixtures.h
index ecd28c3..a7f8504 100644
--- a/test/cpp/microbenchmarks/fullstack_fixtures.h
+++ b/test/cpp/microbenchmarks/fullstack_fixtures.h
@@ -85,7 +85,7 @@
   }
 
   virtual ~FullstackFixture() {
-    server_->Shutdown();
+    server_->Shutdown(gpr_inf_past(GPR_CLOCK_MONOTONIC));
     cq_->Shutdown();
     void* tag;
     bool ok;
@@ -212,7 +212,7 @@
   }
 
   virtual ~EndpointPairFixture() {
-    server_->Shutdown();
+    server_->Shutdown(gpr_inf_past(GPR_CLOCK_MONOTONIC));
     cq_->Shutdown();
     void* tag;
     bool ok;
diff --git a/test/cpp/naming/README.md b/test/cpp/naming/README.md
new file mode 100644
index 0000000..e331846
--- /dev/null
+++ b/test/cpp/naming/README.md
@@ -0,0 +1,43 @@
+# Resolver Tests
+
+This directory has tests and infrastructure for unit tests and GCE
+integration tests of gRPC resolver functionality.
+
+There are two different tests here:
+
+## Resolver unit tests (resolver "component" tests)
+
+These tests run per-change, along with the rest of the grpc unit tests.
+They query a local testing DNS server.
+
+## GCE integration tests
+
+These tests use the same test binary and the same test records
+as the unit tests, but they run against GCE DNS (this is done by
+running the test on a GCE instance and not specifying an authority
+in uris). These tests run in a background job, which needs to be
+actively monitored.
+
+## Making changes to test records
+
+After making a change to `resolver_test_record_groups.yaml`:
+
+1. Increment the "version number" in the `resolver_tests_common_zone_name`
+   DNS zone (this is a yaml field at the top
+   of `resolver_test_record_groups.yaml`).
+
+2. Regenerate projects.
+
+3. From the repo root, run:
+
+```
+$ test/cpp/naming/create_dns_private_zone.sh
+$ test/cpp/naming/private_dns_zone_init.sh
+```
+
+Note that these commands must be ran in environment that
+has access to the grpc-testing GCE project.
+
+If everything runs smoothly, then once the change is merged,
+the GCE DNS integration testing job will transition to the
+new records and continue passing.
diff --git a/test/cpp/naming/create_private_dns_zone.sh b/test/cpp/naming/create_private_dns_zone.sh
new file mode 100755
index 0000000..3d7520b
--- /dev/null
+++ b/test/cpp/naming/create_private_dns_zone.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# Copyright 2015 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 file is auto-generated
+
+set -ex
+
+cd $(dirname $0)/../../..
+
+gcloud alpha dns managed-zones create \
+  resolver-tests-version-1-grpctestingexp-zone-id \
+  --dns-name=resolver-tests-version-1.grpctestingexp. \
+  --description="GCE-DNS-private-zone-for-GRPC-testing" \
+  --visibility=private \
+  --networks=default
diff --git a/test/cpp/naming/gen_build_yaml.py b/test/cpp/naming/gen_build_yaml.py
index 3a51fef..9171815 100755
--- a/test/cpp/naming/gen_build_yaml.py
+++ b/test/cpp/naming/gen_build_yaml.py
@@ -24,6 +24,12 @@
 
 _LOCAL_DNS_SERVER_ADDRESS = '127.0.0.1:15353'
 
+_TARGET_RECORDS_TO_SKIP_AGAINST_GCE = [
+  # TODO: enable this once able to upload the very large TXT record
+  # in this group to GCE DNS.
+  'ipv4-config-causing-fallback-to-tcp',
+]
+
 def _append_zone_name(name, zone_name):
   return '%s.%s' % (name, zone_name)
 
@@ -33,21 +39,107 @@
     out.append('%s,%s' % (addr['address'], str(addr['is_balancer'])))
   return ';'.join(out)
 
+def _data_for_type(r_type, r_data, common_zone_name):
+  if r_type in ['A', 'AAAA']:
+    return ' '.join(map(lambda x: '\"%s\"' % x, r_data))
+  if r_type == 'SRV':
+    assert len(r_data) == 1
+    target = r_data[0].split(' ')[3]
+    uploadable_target = '%s.%s' % (target, common_zone_name)
+    uploadable = r_data[0].split(' ')
+    uploadable[3] = uploadable_target
+    return '\"%s\"' % ' '.join(uploadable)
+  if r_type == 'TXT':
+    assert len(r_data) == 1
+    chunks = []
+    all_data = r_data[0]
+    cur = 0
+    # Split TXT records that span more than 255 characters (the single
+    # string length-limit in DNS) into multiple strings. Each string
+    # needs to be wrapped with double-quotes, and all inner double-quotes
+    # are escaped. The wrapping double-quotes and inner backslashes can be
+    # counted towards the 255 character length limit (as observed with gcloud),
+    # so make sure all strings fit within that limit.
+    while len(all_data[cur:]) > 0:
+      next_chunk = '\"'
+      while len(next_chunk) < 254 and len(all_data[cur:]) > 0:
+        if all_data[cur] == '\"':
+          if len(next_chunk) < 253:
+            next_chunk += '\\\"'
+          else:
+            break
+        else:
+          next_chunk += all_data[cur]
+        cur += 1
+      next_chunk += '\"'
+      if len(next_chunk) > 255:
+        raise Exception('Bug: next chunk is too long.')
+      chunks.append(next_chunk)
+    # Wrap the whole record in single quotes to make sure all strings
+    # are associated with the same TXT record (to make it one bash token for
+    # gcloud)
+    return '\'%s\'' % ' '.join(chunks)
+
+# Convert DNS records from their "within a test group" format
+# of the yaml file to an easier form for the templates to use.
+def _gcloud_uploadable_form(test_cases, common_zone_name):
+  out = []
+  for group in test_cases:
+    if group['record_to_resolve'] in _TARGET_RECORDS_TO_SKIP_AGAINST_GCE:
+      continue
+    for record_name in group['records'].keys():
+      r_ttl = None
+      all_r_data = {}
+      for r_data in group['records'][record_name]:
+        # enforce records have the same TTL only for simplicity
+        if r_ttl is None:
+          r_ttl = r_data['TTL']
+        assert r_ttl == r_data['TTL'], '%s and %s differ' % (r_ttl, r_data['TTL'])
+        r_type = r_data['type']
+        if all_r_data.get(r_type) is None:
+          all_r_data[r_type] = []
+        all_r_data[r_type].append(r_data['data'])
+      for r_type in all_r_data.keys():
+        for r in out:
+          assert r['name'] != record_name or r['type'] != r_type, 'attempt to add a duplicate record'
+        out.append({
+            'name': record_name,
+            'ttl': r_ttl,
+            'type': r_type,
+            'data': _data_for_type(r_type, all_r_data[r_type], common_zone_name)
+        })
+  return out
+
+def _gce_dns_zone_id(resolver_component_data):
+  dns_name = resolver_component_data['resolver_tests_common_zone_name']
+  return dns_name.replace('.', '-') + 'zone-id'
+
+def _resolver_test_cases(resolver_component_data, records_to_skip):
+  out = []
+  for test_case in resolver_component_data['resolver_component_tests']:
+    if test_case['record_to_resolve'] in records_to_skip:
+      continue
+    out.append({
+      'target_name': _append_zone_name(test_case['record_to_resolve'],
+                                       resolver_component_data['resolver_tests_common_zone_name']),
+      'expected_addrs': _build_expected_addrs_cmd_arg(test_case['expected_addrs']),
+      'expected_chosen_service_config': (test_case['expected_chosen_service_config'] or ''),
+      'expected_lb_policy': (test_case['expected_lb_policy'] or ''),
+    })
+  return out
+
 def main():
   resolver_component_data = ''
   with open('test/cpp/naming/resolver_test_record_groups.yaml') as f:
     resolver_component_data = yaml.load(f)
 
   json = {
-      'resolver_component_test_cases': [
-          {
-              'target_name': _append_zone_name(test_case['record_to_resolve'],
-                                                 resolver_component_data['resolver_component_tests_common_zone_name']),
-              'expected_addrs': _build_expected_addrs_cmd_arg(test_case['expected_addrs']),
-              'expected_chosen_service_config': (test_case['expected_chosen_service_config'] or ''),
-              'expected_lb_policy': (test_case['expected_lb_policy'] or ''),
-          } for test_case in resolver_component_data['resolver_component_tests']
-      ],
+      'resolver_tests_common_zone_name': resolver_component_data['resolver_tests_common_zone_name'],
+      'resolver_gce_integration_tests_zone_id': _gce_dns_zone_id(resolver_component_data),
+      'all_integration_test_records': _gcloud_uploadable_form(resolver_component_data['resolver_component_tests'],
+                                                              resolver_component_data['resolver_tests_common_zone_name']),
+      'resolver_gce_integration_test_cases': _resolver_test_cases(resolver_component_data, _TARGET_RECORDS_TO_SKIP_AGAINST_GCE),
+      'resolver_component_test_cases': _resolver_test_cases(resolver_component_data, []),
       'targets': [
           {
               'name': 'resolver_component_test' + unsecure_build_config_suffix,
diff --git a/test/cpp/naming/private_dns_zone_init.sh b/test/cpp/naming/private_dns_zone_init.sh
new file mode 100755
index 0000000..4eaf750
--- /dev/null
+++ b/test/cpp/naming/private_dns_zone_init.sh
@@ -0,0 +1,215 @@
+#!/bin/bash
+# Copyright 2015 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 file is auto-generated
+
+set -ex
+
+cd $(dirname $0)/../../..
+
+gcloud dns record-sets transaction start -z=resolver-tests-version-1-grpctestingexp-zone-id
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv4-single-target.resolver-tests-version-1.grpctestingexp. \
+  --type=SRV \
+  --ttl=2100 \
+  "0 0 1234 ipv4-single-target.resolver-tests-version-1.grpctestingexp."
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-single-target.resolver-tests-version-1.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.4"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv4-multi-target.resolver-tests-version-1.grpctestingexp. \
+  --type=SRV \
+  --ttl=2100 \
+  "0 0 1234 ipv4-multi-target.resolver-tests-version-1.grpctestingexp."
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-multi-target.resolver-tests-version-1.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.5" "1.2.3.6" "1.2.3.7"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv6-single-target.resolver-tests-version-1.grpctestingexp. \
+  --type=SRV \
+  --ttl=2100 \
+  "0 0 1234 ipv6-single-target.resolver-tests-version-1.grpctestingexp."
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv6-single-target.resolver-tests-version-1.grpctestingexp. \
+  --type=AAAA \
+  --ttl=2100 \
+  "2607:f8b0:400a:801::1001"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv6-multi-target.resolver-tests-version-1.grpctestingexp. \
+  --type=SRV \
+  --ttl=2100 \
+  "0 0 1234 ipv6-multi-target.resolver-tests-version-1.grpctestingexp."
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv6-multi-target.resolver-tests-version-1.grpctestingexp. \
+  --type=AAAA \
+  --ttl=2100 \
+  "2607:f8b0:400a:801::1002" "2607:f8b0:400a:801::1003" "2607:f8b0:400a:801::1004"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. \
+  --type=TXT \
+  --ttl=2100 \
+  '"grpc_config=[{\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"SimpleService\",\"waitForReady\":true}]}]}}]"'
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.4"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. \
+  --type=SRV \
+  --ttl=2100 \
+  "0 0 1234 ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp."
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.4"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. \
+  --type=TXT \
+  --ttl=2100 \
+  '"grpc_config=[{\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"NoSrvSimpleService\",\"waitForReady\":true}]}]}}]"'
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.4"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. \
+  --type=TXT \
+  --ttl=2100 \
+  '"grpc_config=[{\"clientLanguage\":[\"python\"],\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"PythonService\",\"waitForReady\":true}]}]}}]"'
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.4"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. \
+  --type=TXT \
+  --ttl=2100 \
+  '"grpc_config=[{\"percentage\":0,\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"CppService\",\"waitForReady\":true}]}]}}]"'
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.4"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. \
+  --type=TXT \
+  --ttl=2100 \
+  '"grpc_config=[{\"clientLanguage\":[\"go\"],\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"GoService\",\"waitForReady\":true}]}]}},{\"clientLanguage\":[\"c++\"],\"serviceConfig\":{" "\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"CppService\",\"waitForReady\":true}]}]}}]"'
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.4"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. \
+  --type=TXT \
+  --ttl=2100 \
+  '"grpc_config=[{\"percentage\":0,\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"NeverPickedService\",\"waitForReady\":true}]}]}},{\"percentage\":100,\"serviceConfig\":{\"loadBalanc" "ingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"method\":\"Foo\",\"service\":\"AlwaysPickedService\",\"waitForReady\":true}]}]}}]"'
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. \
+  --type=SRV \
+  --ttl=2100 \
+  "0 0 1234 balancer-for-ipv4-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp."
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=balancer-for-ipv4-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.4"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. \
+  --type=A \
+  --ttl=2100 \
+  "1.2.3.4"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=_grpclb._tcp.srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. \
+  --type=SRV \
+  --ttl=2100 \
+  "0 0 1234 balancer-for-ipv6-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp."
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=balancer-for-ipv6-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. \
+  --type=AAAA \
+  --ttl=2100 \
+  "2607:f8b0:400a:801::1002"
+
+gcloud dns record-sets transaction add \
+  -z=resolver-tests-version-1-grpctestingexp-zone-id \
+  --name=srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. \
+  --type=AAAA \
+  --ttl=2100 \
+  "2607:f8b0:400a:801::1002"
+
+gcloud dns record-sets transaction describe -z=resolver-tests-version-1-grpctestingexp-zone-id
+gcloud dns record-sets transaction execute -z=resolver-tests-version-1-grpctestingexp-zone-id
+gcloud dns record-sets list -z=resolver-tests-version-1-grpctestingexp-zone-id
diff --git a/test/cpp/naming/resolver_component_test.cc b/test/cpp/naming/resolver_component_test.cc
index 396f4ce..7d0371b 100644
--- a/test/cpp/naming/resolver_component_test.cc
+++ b/test/cpp/naming/resolver_component_test.cc
@@ -199,10 +199,10 @@
     grpc_pollset_worker *worker = NULL;
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     gpr_mu_lock(args->mu);
-    GRPC_LOG_IF_ERROR(
-        "pollset_work",
-        grpc_pollset_work(&exec_ctx, args->pollset, &worker,
-                          gpr_now(GPR_CLOCK_REALTIME), NSecondDeadline(1)));
+    GRPC_LOG_IF_ERROR("pollset_work",
+                      grpc_pollset_work(&exec_ctx, args->pollset, &worker,
+                                        grpc_timespec_to_millis_round_up(
+                                            NSecondDeadline(1))));
     gpr_mu_unlock(args->mu);
     grpc_exec_ctx_finish(&exec_ctx);
   }
@@ -267,7 +267,9 @@
   }
   EXPECT_THAT(args->expected_addrs, UnorderedElementsAreArray(found_lb_addrs));
   CheckServiceConfigResultLocked(channel_args, args);
-  CheckLBPolicyResultLocked(channel_args, args);
+  if (args->expected_service_config_string == "") {
+    CheckLBPolicyResultLocked(channel_args, args);
+  }
   gpr_atm_rel_store(&args->done_atm, 1);
   gpr_mu_lock(args->mu);
   GRPC_LOG_IF_ERROR("pollset_kick",
diff --git a/test/cpp/naming/resolver_component_tests_runner.sh b/test/cpp/naming/resolver_component_tests_runner.sh
index 83b03b6..407db5e 100755
--- a/test/cpp/naming/resolver_component_tests_runner.sh
+++ b/test/cpp/naming/resolver_component_tests_runner.sh
@@ -73,7 +73,7 @@
 # in the resolver.
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv4-single-target.resolver-tests.grpctestingexp.' \
+  --target_name='srv-ipv4-single-target.resolver-tests-version-1.grpctestingexp.' \
   --expected_addrs='1.2.3.4:1234,True' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -81,7 +81,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv4-multi-target.resolver-tests.grpctestingexp.' \
+  --target_name='srv-ipv4-multi-target.resolver-tests-version-1.grpctestingexp.' \
   --expected_addrs='1.2.3.5:1234,True;1.2.3.6:1234,True;1.2.3.7:1234,True' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -89,7 +89,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv6-single-target.resolver-tests.grpctestingexp.' \
+  --target_name='srv-ipv6-single-target.resolver-tests-version-1.grpctestingexp.' \
   --expected_addrs='[2607:f8b0:400a:801::1001]:1234,True' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -97,7 +97,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv6-multi-target.resolver-tests.grpctestingexp.' \
+  --target_name='srv-ipv6-multi-target.resolver-tests-version-1.grpctestingexp.' \
   --expected_addrs='[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1003]:1234,True;[2607:f8b0:400a:801::1004]:1234,True' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -105,7 +105,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv4-simple-service-config.resolver-tests.grpctestingexp.' \
+  --target_name='srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp.' \
   --expected_addrs='1.2.3.4:1234,True' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}' \
   --expected_lb_policy='round_robin' \
@@ -113,7 +113,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='ipv4-no-srv-simple-service-config.resolver-tests.grpctestingexp.' \
+  --target_name='ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService","waitForReady":true}]}]}' \
   --expected_lb_policy='round_robin' \
@@ -121,7 +121,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='ipv4-no-config-for-cpp.resolver-tests.grpctestingexp.' \
+  --target_name='ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -129,7 +129,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='ipv4-cpp-config-has-zero-percentage.resolver-tests.grpctestingexp.' \
+  --target_name='ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -137,7 +137,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='ipv4-second-language-is-cpp.resolver-tests.grpctestingexp.' \
+  --target_name='ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}' \
   --expected_lb_policy='round_robin' \
@@ -145,7 +145,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='ipv4-config-with-percentages.resolver-tests.grpctestingexp.' \
+  --target_name='ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp.' \
   --expected_addrs='1.2.3.4:443,False' \
   --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService","waitForReady":true}]}]}' \
   --expected_lb_policy='round_robin' \
@@ -153,7 +153,7 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv4-target-has-backend-and-balancer.resolver-tests.grpctestingexp.' \
+  --target_name='srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp.' \
   --expected_addrs='1.2.3.4:1234,True;1.2.3.4:443,False' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
@@ -161,13 +161,21 @@
 wait $! || EXIT_CODE=1
 
 $FLAGS_test_bin_path \
-  --target_name='srv-ipv6-target-has-backend-and-balancer.resolver-tests.grpctestingexp.' \
+  --target_name='srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp.' \
   --expected_addrs='[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1002]:443,False' \
   --expected_chosen_service_config='' \
   --expected_lb_policy='' \
   --local_dns_server_address=127.0.0.1:$FLAGS_test_dns_server_port &
 wait $! || EXIT_CODE=1
 
+$FLAGS_test_bin_path \
+  --target_name='ipv4-config-causing-fallback-to-tcp.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='1.2.3.4:443,False' \
+  --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooThree","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFour","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFive","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSix","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSeven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEight","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooNine","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTen","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEleven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]}]}' \
+  --expected_lb_policy='' \
+  --local_dns_server_address=127.0.0.1:$FLAGS_test_dns_server_port &
+wait $! || EXIT_CODE=1
+
 kill -SIGTERM $DNS_SERVER_PID || true
 wait
 exit $EXIT_CODE
diff --git a/test/cpp/naming/resolver_gce_integration_tests_runner.sh b/test/cpp/naming/resolver_gce_integration_tests_runner.sh
new file mode 100755
index 0000000..b20d18d
--- /dev/null
+++ b/test/cpp/naming/resolver_gce_integration_tests_runner.sh
@@ -0,0 +1,359 @@
+#!/bin/bash
+# Copyright 2015 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 file is auto-generated
+
+set -ex
+
+if [[ "$GRPC_DNS_RESOLVER" == "" ]]; then
+  export GRPC_DNS_RESOLVER=ares
+elif [[ "$GRPC_DNS_RESOLVER" != ares ]]; then
+  echo "Unexpected: GRPC_DNS_RESOLVER=$GRPC_DNS_RESOLVER. This test only works with c-ares resolver"
+  exit 1
+fi
+
+cd $(dirname $0)/../../..
+
+if [[ "$CONFIG" == "" ]]; then
+  export CONFIG=opt
+fi
+make resolver_component_test
+echo "Sanity check DNS records are resolveable with dig:"
+EXIT_CODE=0
+
+ONE_FAILED=0
+dig SRV _grpclb._tcp.srv-ipv4-single-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv4-single-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig A ipv4-single-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig A ipv4-single-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig SRV _grpclb._tcp.srv-ipv4-multi-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv4-multi-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig A ipv4-multi-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig A ipv4-multi-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig SRV _grpclb._tcp.srv-ipv6-single-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv6-single-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig AAAA ipv6-single-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig AAAA ipv6-single-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig SRV _grpclb._tcp.srv-ipv6-multi-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv6-multi-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig AAAA ipv6-multi-target.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig AAAA ipv6-multi-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig TXT srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig TXT srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig A ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig A ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig SRV _grpclb._tcp.srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig A ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig A ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig TXT ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig TXT ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig A ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig A ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig TXT ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig TXT ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig A ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig A ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig TXT ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig TXT ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig A ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig A ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig TXT ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig TXT ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig A ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig A ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig TXT ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig TXT ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig SRV _grpclb._tcp.srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig A balancer-for-ipv4-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig A balancer-for-ipv4-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig A srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig A srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig SRV _grpclb._tcp.srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig SRV _grpclb._tcp.srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig AAAA balancer-for-ipv6-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig AAAA balancer-for-ipv6-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+ONE_FAILED=0
+dig AAAA srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. | grep 'ANSWER SECTION' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Sanity check: dig AAAA srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  exit 1
+fi
+
+echo "Sanity check PASSED. Run resolver tests:"
+
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \
+  --target_name='srv-ipv4-single-target.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='1.2.3.4:1234,True' \
+  --expected_chosen_service_config='' \
+  --expected_lb_policy='' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: srv-ipv4-single-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  EXIT_CODE=1
+fi
+
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \
+  --target_name='srv-ipv4-multi-target.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='1.2.3.5:1234,True;1.2.3.6:1234,True;1.2.3.7:1234,True' \
+  --expected_chosen_service_config='' \
+  --expected_lb_policy='' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: srv-ipv4-multi-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  EXIT_CODE=1
+fi
+
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \
+  --target_name='srv-ipv6-single-target.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='[2607:f8b0:400a:801::1001]:1234,True' \
+  --expected_chosen_service_config='' \
+  --expected_lb_policy='' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: srv-ipv6-single-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  EXIT_CODE=1
+fi
+
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \
+  --target_name='srv-ipv6-multi-target.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1003]:1234,True;[2607:f8b0:400a:801::1004]:1234,True' \
+  --expected_chosen_service_config='' \
+  --expected_lb_policy='' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: srv-ipv6-multi-target.resolver-tests-version-1.grpctestingexp. FAILED"
+  EXIT_CODE=1
+fi
+
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \
+  --target_name='srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='1.2.3.4:1234,True' \
+  --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]}]}' \
+  --expected_lb_policy='round_robin' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: srv-ipv4-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  EXIT_CODE=1
+fi
+
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \
+  --target_name='ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='1.2.3.4:443,False' \
+  --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"NoSrvSimpleService","waitForReady":true}]}]}' \
+  --expected_lb_policy='round_robin' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: ipv4-no-srv-simple-service-config.resolver-tests-version-1.grpctestingexp. FAILED"
+  EXIT_CODE=1
+fi
+
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \
+  --target_name='ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='1.2.3.4:443,False' \
+  --expected_chosen_service_config='' \
+  --expected_lb_policy='' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: ipv4-no-config-for-cpp.resolver-tests-version-1.grpctestingexp. FAILED"
+  EXIT_CODE=1
+fi
+
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \
+  --target_name='ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='1.2.3.4:443,False' \
+  --expected_chosen_service_config='' \
+  --expected_lb_policy='' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: ipv4-cpp-config-has-zero-percentage.resolver-tests-version-1.grpctestingexp. FAILED"
+  EXIT_CODE=1
+fi
+
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \
+  --target_name='ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='1.2.3.4:443,False' \
+  --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"CppService","waitForReady":true}]}]}' \
+  --expected_lb_policy='round_robin' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: ipv4-second-language-is-cpp.resolver-tests-version-1.grpctestingexp. FAILED"
+  EXIT_CODE=1
+fi
+
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \
+  --target_name='ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='1.2.3.4:443,False' \
+  --expected_chosen_service_config='{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"AlwaysPickedService","waitForReady":true}]}]}' \
+  --expected_lb_policy='round_robin' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: ipv4-config-with-percentages.resolver-tests-version-1.grpctestingexp. FAILED"
+  EXIT_CODE=1
+fi
+
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \
+  --target_name='srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='1.2.3.4:1234,True;1.2.3.4:443,False' \
+  --expected_chosen_service_config='' \
+  --expected_lb_policy='' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: srv-ipv4-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  EXIT_CODE=1
+fi
+
+ONE_FAILED=0
+bins/$CONFIG/resolver_component_test \
+  --target_name='srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp.' \
+  --expected_addrs='[2607:f8b0:400a:801::1002]:1234,True;[2607:f8b0:400a:801::1002]:443,False' \
+  --expected_chosen_service_config='' \
+  --expected_lb_policy='' || ONE_FAILED=1
+if [[ "$ONE_FAILED" != 0 ]]; then
+  echo "Test based on target record: srv-ipv6-target-has-backend-and-balancer.resolver-tests-version-1.grpctestingexp. FAILED"
+  EXIT_CODE=1
+fi
+
+exit $EXIT_CODE
diff --git a/test/cpp/naming/resolver_test_record_groups.yaml b/test/cpp/naming/resolver_test_record_groups.yaml
index 67c611d..2b32043 100644
--- a/test/cpp/naming/resolver_test_record_groups.yaml
+++ b/test/cpp/naming/resolver_test_record_groups.yaml
@@ -1,4 +1,4 @@
-resolver_component_tests_common_zone_name: resolver-tests.grpctestingexp.
+resolver_tests_common_zone_name: resolver-tests-version-1.grpctestingexp.
 resolver_component_tests:
 - expected_addrs:
   - {address: '1.2.3.4:1234', is_balancer: true}
@@ -137,16 +137,10 @@
     - {TTL: '2100', data: '2607:f8b0:400a:801::1002', type: AAAA}
     srv-ipv6-target-has-backend-and-balancer:
     - {TTL: '2100', data: '2607:f8b0:400a:801::1002', type: AAAA}
-
-resolver_component_tests_TODO:
-- 'TODO: enable this large-txt-record test once working. (it is much longer than 512
-  bytes, likely to cause use of TCP even if max payload for UDP is changed somehow,
-  e.g. via notes in RFC 2671)'
 - expected_addrs:
   - {address: '1.2.3.4:443', is_balancer: false}
   expected_chosen_service_config: '{"loadBalancingPolicy":"round_robin","methodConfig":[{"name":[{"method":"Foo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwo","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooThree","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFour","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooFive","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSix","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooSeven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEight","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooNine","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTen","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooEleven","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]},{"name":[{"method":"FooTwelve","service":"SimpleService","waitForReady":true}]}]}'
   expected_lb_policy: null
-  record_to_resolve: srv-ipv6-target-has-backend-and-balancer
   record_to_resolve: ipv4-config-causing-fallback-to-tcp
   records:
     ipv4-config-causing-fallback-to-tcp:
diff --git a/test/cpp/naming/test_dns_server.py b/test/cpp/naming/test_dns_server.py
index 9d4b89c..9f42f65 100755
--- a/test/cpp/naming/test_dns_server.py
+++ b/test/cpp/naming/test_dns_server.py
@@ -66,7 +66,7 @@
 
   with open(args.records_config_path) as config:
     test_records_config = yaml.load(config)
-  common_zone_name = test_records_config['resolver_component_tests_common_zone_name']
+  common_zone_name = test_records_config['resolver_tests_common_zone_name']
   for group in test_records_config['resolver_component_tests']:
     for name in group['records'].keys():
       for record in group['records'][name]:
diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc
index 1a44380..f5807da 100644
--- a/test/cpp/qps/client_async.cc
+++ b/test/cpp/qps/client_async.cc
@@ -56,6 +56,7 @@
   }
 
   virtual void Start(CompletionQueue* cq, const ClientConfig& config) = 0;
+  virtual void TryCancel() = 0;
 };
 
 template <class RequestType, class ResponseType>
@@ -110,6 +111,7 @@
                                                 prepare_req_, callback_);
     clone->StartInternal(cq);
   }
+  void TryCancel() override { context_.TryCancel(); }
 
  private:
   grpc::ClientContext context_;
@@ -142,8 +144,6 @@
   }
 };
 
-typedef std::forward_list<ClientRpcContext*> context_list;
-
 template <class StubType, class RequestType>
 class AsyncClient : public ClientImpl<StubType, RequestType> {
   // Specify which protected members we are using since there is no
@@ -247,6 +247,7 @@
       // this thread isn't supposed to shut down
       std::lock_guard<std::mutex> l(shutdown_state_[thread_idx]->mutex);
       if (shutdown_state_[thread_idx]->shutdown) {
+        ctx->TryCancel();
         delete ctx;
         return true;
       }
@@ -388,6 +389,7 @@
         stub_, req_, next_issue_, prepare_req_, callback_);
     clone->StartInternal(cq, messages_per_stream_);
   }
+  void TryCancel() override { context_.TryCancel(); }
 
  private:
   grpc::ClientContext context_;
@@ -527,6 +529,7 @@
         stub_, req_, next_issue_, prepare_req_, callback_);
     clone->StartInternal(cq);
   }
+  void TryCancel() override { context_.TryCancel(); }
 
  private:
   grpc::ClientContext context_;
@@ -644,6 +647,7 @@
         stub_, req_, next_issue_, prepare_req_, callback_);
     clone->StartInternal(cq);
   }
+  void TryCancel() override { context_.TryCancel(); }
 
  private:
   grpc::ClientContext context_;
@@ -786,6 +790,7 @@
         stub_, req_, next_issue_, prepare_req_, callback_);
     clone->StartInternal(cq, messages_per_stream_);
   }
+  void TryCancel() override { context_.TryCancel(); }
 
  private:
   grpc::ClientContext context_;
diff --git a/test/cpp/util/byte_buffer_test.cc b/test/cpp/util/byte_buffer_test.cc
index cac01a7..8fb51bc 100644
--- a/test/cpp/util/byte_buffer_test.cc
+++ b/test/cpp/util/byte_buffer_test.cc
@@ -93,7 +93,7 @@
   std::vector<Slice> slices;
   slices.push_back(Slice(hello, Slice::STEAL_REF));
   slices.push_back(Slice(world, Slice::STEAL_REF));
-  grpc_byte_buffer* send_buffer = nullptr;
+  ByteBuffer send_buffer;
   bool owned = false;
   ByteBuffer buffer(&slices[0], 2);
   slices.clear();
@@ -101,8 +101,7 @@
       buffer, &send_buffer, &owned);
   EXPECT_TRUE(status.ok());
   EXPECT_TRUE(owned);
-  EXPECT_TRUE(send_buffer != nullptr);
-  grpc_byte_buffer_destroy(send_buffer);
+  EXPECT_TRUE(send_buffer.Valid());
 }
 
 }  // namespace
diff --git a/tools/codegen/core/gen_stats_data.py b/tools/codegen/core/gen_stats_data.py
index 10ad0cc..8359734 100755
--- a/tools/codegen/core/gen_stats_data.py
+++ b/tools/codegen/core/gen_stats_data.py
@@ -147,8 +147,7 @@
     shift_data = find_ideal_shift(code_bounds[first_nontrivial:], 256 * histogram.buckets)
   #print first_nontrivial, shift_data, bounds
   #if shift_data is not None: print [hex(x >> shift_data[0]) for x in code_bounds[first_nontrivial:]]
-  code = '\n/* Automatically generated by tools/codegen/core/gen_stats_data.py */\n'
-  code += 'value = GPR_CLAMP(value, 0, %d);\n' % histogram.max
+  code = 'value = GPR_CLAMP(value, 0, %d);\n' % histogram.max
   map_table = gen_map_table(code_bounds[first_nontrivial:], shift_data)
   if first_nontrivial is None:
     code += ('GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_%s, value);\n'
@@ -408,3 +407,4 @@
   for counter in inst_map['Counter']:
     columns.append(('%s_per_iteration' % counter.name, 'FLOAT'))
   print >>S, ',\n'.join('%s:%s' % x for x in columns)
+
diff --git a/tools/distrib/pull_requests_interval.sh b/tools/distrib/pull_requests_interval.sh
new file mode 100755
index 0000000..7a6c702
--- /dev/null
+++ b/tools/distrib/pull_requests_interval.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+# 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.
+
+if [ "x$1" = "x" ] ; then
+  echo "Usage: $0 <first ref> [second ref]"
+  exit 1
+else
+  first=$1
+fi
+
+if [ -n $2 ] ; then
+  second=HEAD
+fi
+
+if [ -e ~/github-credentials.vars ] ; then
+  . ~/github-credentials.vars
+fi
+
+if [ "x$github_client_id" = "x" ] || [ "x$github_client_secret" = "x" ] ; then
+  echo "Warning: you don't have github credentials set."
+  echo
+  echo "You may end up exceeding guest quota quickly."
+  echo "You can create an application for yourself,"
+  echo "and get its credentials. Go to"
+  echo
+  echo "  https://github.com/settings/developers"
+  echo
+  echo "and click 'Register a new application'."
+  echo
+  echo "From the application's information, copy/paste"
+  echo "its Client ID and Client Secret, into the file"
+  echo
+  echo "  ~/github-credentials.vars"
+  echo
+  echo "with the following format:"
+  echo
+  echo "github_client_id=0123456789abcdef0123"
+  echo "github_client_secret=0123456789abcdef0123456789abcdef"
+  echo
+  echo
+  addendum=""
+else
+  addendum="?client_id=$github_client_id&client_secret=$github_client_secret"
+fi
+
+unset notfirst
+echo "["
+git log --pretty=oneline $1..$2 |
+  grep '[^ ]\+ Merge pull request #[0-9]\{4,6\} ' |
+  cut -f 2 -d# |
+  cut -f 1 -d\  |
+  sort -u |
+  while read id ; do
+    if [ "x$notfirst" = "x" ] ; then
+      notfirst=true
+    else
+      echo ","
+    fi
+    echo -n "  {\"url\": \"https://github.com/grpc/grpc/pull/$id\","
+    out=`mktemp`
+    curl -s "https://api.github.com/repos/grpc/grpc/pulls/$id$addendum" > $out
+    echo -n " "`grep '"title"' $out`
+    echo -n " "`grep '"login"' $out | head -1`
+    echo -n "  \"pr\": $id }"
+    rm $out
+  done
+echo
+echo "]"
diff --git a/tools/distrib/pylint_code.sh b/tools/distrib/pylint_code.sh
index 3c9235b..7175f1e 100755
--- a/tools/distrib/pylint_code.sh
+++ b/tools/distrib/pylint_code.sh
@@ -29,7 +29,7 @@
 
 virtualenv $VIRTUALENV
 PYTHON=$(realpath $VIRTUALENV/bin/python)
-$PYTHON -m pip install --upgrade pip
+$PYTHON -m pip install --upgrade pip==9.0.1
 $PYTHON -m pip install pylint==1.6.5
 
 for dir in "${DIRS[@]}"; do
diff --git a/tools/distrib/python/docgen.py b/tools/distrib/python/docgen.py
index 6f6d43c..1822e51 100755
--- a/tools/distrib/python/docgen.py
+++ b/tools/distrib/python/docgen.py
@@ -60,7 +60,7 @@
 
 subprocess_arguments_list = [
     {'args': ['virtualenv', VIRTUALENV_DIR], 'env': environment},
-    {'args': [VIRTUALENV_PIP_PATH, 'install', '--upgrade', 'pip'],
+    {'args': [VIRTUALENV_PIP_PATH, 'install', '--upgrade', 'pip==9.0.1'],
      'env': environment},
     {'args': [VIRTUALENV_PIP_PATH, 'install', '-r', REQUIREMENTS_PATH],
      'env': environment},
diff --git a/tools/distrib/yapf_code.sh b/tools/distrib/yapf_code.sh
index dbb6b5c..e5beb70 100755
--- a/tools/distrib/yapf_code.sh
+++ b/tools/distrib/yapf_code.sh
@@ -33,7 +33,7 @@
 
 virtualenv $VIRTUALENV
 PYTHON=$(realpath "${VIRTUALENV}/bin/python")
-$PYTHON -m pip install --upgrade pip
+$PYTHON -m pip install --upgrade pip==9.0.1
 $PYTHON -m pip install --upgrade futures
 $PYTHON -m pip install yapf==0.16.0
 
diff --git a/tools/dockerfile/grpc_clang_format/Dockerfile b/tools/dockerfile/grpc_clang_format/Dockerfile
index 647cb52..dff07fe 100644
--- a/tools/dockerfile/grpc_clang_format/Dockerfile
+++ b/tools/dockerfile/grpc_clang_format/Dockerfile
@@ -12,12 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM ubuntu:15.10
+FROM debian:jessie
 
 RUN apt-get update && apt-get -y install wget
-RUN echo deb http://llvm.org/apt/wily/ llvm-toolchain-wily-3.8 main >> /etc/apt/sources.list
-RUN echo deb-src http://llvm.org/apt/wily/ llvm-toolchain-wily-3.8 main >> /etc/apt/sources.list
-RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key| apt-key add -
+RUN echo "deb http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.8 main" >> /etc/apt/sources.list
+RUN echo "deb-src http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.8 main" >> /etc/apt/sources.list
+RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | apt-key add -
 RUN apt-get update && apt-get -y install clang-format-3.8
 
 ADD clang_format_all_the_things.sh /
diff --git a/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile
index dbf5802..ea82476 100644
--- a/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile
@@ -60,7 +60,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile
index 4ccfbc4..56b8be8 100644
--- a/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile
@@ -59,7 +59,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile
index 7cfe98c..38d377c 100644
--- a/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_cxx/Dockerfile
@@ -60,7 +60,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_go/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_go/Dockerfile
index febe2fa..73c41a4 100644
--- a/tools/dockerfile/interoptest/grpc_interop_go/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_go/Dockerfile
@@ -28,7 +28,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile
index 3a516cb..7c083de 100644
--- a/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_go1.7/Dockerfile
@@ -28,7 +28,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile
index acb640a..61efc18 100644
--- a/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_go1.8/Dockerfile
@@ -28,7 +28,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_http2/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_http2/Dockerfile
index 354b7bf..278b09a 100644
--- a/tools/dockerfile/interoptest/grpc_interop_http2/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_http2/Dockerfile
@@ -28,7 +28,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_java/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_java/Dockerfile
index 92a542f..d566324 100644
--- a/tools/dockerfile/interoptest/grpc_interop_java/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_java/Dockerfile
@@ -43,7 +43,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_java_oracle8/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_java_oracle8/Dockerfile
index 92a542f..d566324 100644
--- a/tools/dockerfile/interoptest/grpc_interop_java_oracle8/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_java_oracle8/Dockerfile
@@ -43,7 +43,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_node/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_node/Dockerfile
index 4343d56..f4c3e41 100644
--- a/tools/dockerfile/interoptest/grpc_interop_node/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_node/Dockerfile
@@ -60,7 +60,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile
index 271c6e7..d165307 100644
--- a/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile
@@ -60,7 +60,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/interoptest/grpc_interop_ruby/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_ruby/Dockerfile
index 7bcada6..2217b10 100644
--- a/tools/dockerfile/interoptest/grpc_interop_ruby/Dockerfile
+++ b/tools/dockerfile/interoptest/grpc_interop_ruby/Dockerfile
@@ -60,7 +60,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/csharp_jessie_x64/Dockerfile b/tools/dockerfile/test/csharp_jessie_x64/Dockerfile
index 40d46fc..3e31e67 100644
--- a/tools/dockerfile/test/csharp_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/csharp_jessie_x64/Dockerfile
@@ -64,7 +64,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/cxx_alpine_x64/Dockerfile b/tools/dockerfile/test/cxx_alpine_x64/Dockerfile
index 1ae50c1..af5e7d6 100644
--- a/tools/dockerfile/test/cxx_alpine_x64/Dockerfile
+++ b/tools/dockerfile/test/cxx_alpine_x64/Dockerfile
@@ -36,7 +36,7 @@
   zip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
 
diff --git a/tools/dockerfile/test/cxx_jessie_x64/Dockerfile b/tools/dockerfile/test/cxx_jessie_x64/Dockerfile
index 888a37b..3492dd7 100644
--- a/tools/dockerfile/test/cxx_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/cxx_jessie_x64/Dockerfile
@@ -64,7 +64,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/cxx_jessie_x86/Dockerfile b/tools/dockerfile/test/cxx_jessie_x86/Dockerfile
index 319f1e1..f8cbf35 100644
--- a/tools/dockerfile/test/cxx_jessie_x86/Dockerfile
+++ b/tools/dockerfile/test/cxx_jessie_x86/Dockerfile
@@ -64,7 +64,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile b/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile
index 61f005d..6966d6b 100644
--- a/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile
+++ b/tools/dockerfile/test/cxx_ubuntu1404_x64/Dockerfile
@@ -64,7 +64,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile b/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile
index f35247e..016034a 100644
--- a/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile
+++ b/tools/dockerfile/test/cxx_ubuntu1604_x64/Dockerfile
@@ -64,7 +64,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/fuzzer/Dockerfile b/tools/dockerfile/test/fuzzer/Dockerfile
index ce1badf..50104ad 100644
--- a/tools/dockerfile/test/fuzzer/Dockerfile
+++ b/tools/dockerfile/test/fuzzer/Dockerfile
@@ -64,7 +64,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/multilang_jessie_x64/Dockerfile b/tools/dockerfile/test/multilang_jessie_x64/Dockerfile
index 59fe4d8..1a4b681 100644
--- a/tools/dockerfile/test/multilang_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/multilang_jessie_x64/Dockerfile
@@ -121,7 +121,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/node_jessie_x64/Dockerfile b/tools/dockerfile/test/node_jessie_x64/Dockerfile
index 103be84..4f18dba 100644
--- a/tools/dockerfile/test/node_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/node_jessie_x64/Dockerfile
@@ -75,7 +75,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/php7_jessie_x64/Dockerfile b/tools/dockerfile/test/php7_jessie_x64/Dockerfile
index f6d426b..1399502 100644
--- a/tools/dockerfile/test/php7_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/php7_jessie_x64/Dockerfile
@@ -75,7 +75,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/php_jessie_x64/Dockerfile b/tools/dockerfile/test/php_jessie_x64/Dockerfile
index ae82a8d..56dc604 100644
--- a/tools/dockerfile/test/php_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/php_jessie_x64/Dockerfile
@@ -64,7 +64,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/python_alpine_x64/Dockerfile b/tools/dockerfile/test/python_alpine_x64/Dockerfile
index 7bd11d7..7584ab8 100644
--- a/tools/dockerfile/test/python_alpine_x64/Dockerfile
+++ b/tools/dockerfile/test/python_alpine_x64/Dockerfile
@@ -36,7 +36,7 @@
   zip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0
 
diff --git a/tools/dockerfile/test/python_jessie_x64/Dockerfile b/tools/dockerfile/test/python_jessie_x64/Dockerfile
index d5d781c..8d89f50 100644
--- a/tools/dockerfile/test/python_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/python_jessie_x64/Dockerfile
@@ -64,7 +64,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/python_pyenv_x64/Dockerfile b/tools/dockerfile/test/python_pyenv_x64/Dockerfile
index 3b4ad12..f8cbdc5 100644
--- a/tools/dockerfile/test/python_pyenv_x64/Dockerfile
+++ b/tools/dockerfile/test/python_pyenv_x64/Dockerfile
@@ -64,7 +64,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/ruby_jessie_x64/Dockerfile b/tools/dockerfile/test/ruby_jessie_x64/Dockerfile
index 3d879bb..5d7f80b 100644
--- a/tools/dockerfile/test/ruby_jessie_x64/Dockerfile
+++ b/tools/dockerfile/test/ruby_jessie_x64/Dockerfile
@@ -64,7 +64,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
diff --git a/tools/dockerfile/test/sanity/Dockerfile b/tools/dockerfile/test/sanity/Dockerfile
index 44732a5..487ce15 100644
--- a/tools/dockerfile/test/sanity/Dockerfile
+++ b/tools/dockerfile/test/sanity/Dockerfile
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM ubuntu:15.10
+FROM debian:jessie
 
 # Install Git and basic packages.
 RUN apt-get update && apt-get install -y \
@@ -64,7 +64,7 @@
     python-pip
 
 # Install Python packages from PyPI
-RUN pip install pip --upgrade
+RUN pip install --upgrade pip==9.0.1
 RUN pip install virtualenv
 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.2.0 six==1.10.0 twisted==17.5.0
 
@@ -82,19 +82,28 @@
 
 #======================================
 # More sanity test dependencies (bazel)
-RUN apt-get install -y openjdk-8-jdk
-# Check out Bazel version 0.4.1 since this version allows running
-# ./compile.sh without a local protoc dependency
-# TODO(mattkwong): install dependencies to support latest Bazel version if newer
-# version is needed
-RUN git clone https://github.com/bazelbuild/bazel.git /bazel && \
-  cd /bazel && git checkout tags/0.4.1 && ./compile.sh
-RUN ln -s /bazel/output/bazel /bin/
+RUN echo "deb http://http.debian.net/debian jessie-backports main" >> /etc/apt/sources.list
+RUN apt-get update
+RUN apt-get install -y -t jessie-backports openjdk-8-jdk
+
+#========================
+# Bazel installation
+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
+
+# Pin Bazel to 0.4.4
+# Installing Bazel via apt-get first is required before installing 0.4.4 to
+# allow gRPC to build without errors. See https://github.com/grpc/grpc/issues/10553
+RUN curl -fSsL -O https://github.com/bazelbuild/bazel/releases/download/0.4.4/bazel-0.4.4-installer-linux-x86_64.sh
+RUN chmod +x ./bazel-0.4.4-installer-linux-x86_64.sh
+RUN ./bazel-0.4.4-installer-linux-x86_64.sh
 
 RUN apt-get update && apt-get -y install wget
-RUN echo deb http://llvm.org/apt/wily/ llvm-toolchain-wily-3.8 main >> /etc/apt/sources.list
-RUN echo deb-src http://llvm.org/apt/wily/ llvm-toolchain-wily-3.8 main >> /etc/apt/sources.list
-RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key| apt-key add -
+RUN echo "deb http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.8 main" >> /etc/apt/sources.list
+RUN echo "deb-src http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.8 main" >> /etc/apt/sources.list
+RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | apt-key add -
 RUN apt-get update && apt-get -y install clang-format-3.8
 
 # Prepare ccache
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index 62f1139..d81b7b4 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -811,6 +811,7 @@
 include/grpc++/impl/client_unary_call.h \
 include/grpc++/impl/codegen/async_stream.h \
 include/grpc++/impl/codegen/async_unary_call.h \
+include/grpc++/impl/codegen/byte_buffer.h \
 include/grpc++/impl/codegen/call.h \
 include/grpc++/impl/codegen/call_hook.h \
 include/grpc++/impl/codegen/channel_interface.h \
@@ -881,6 +882,7 @@
 include/grpc/impl/codegen/atm_gcc_atomic.h \
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
+include/grpc/impl/codegen/byte_buffer.h \
 include/grpc/impl/codegen/byte_buffer_reader.h \
 include/grpc/impl/codegen/compression_types.h \
 include/grpc/impl/codegen/connectivity_state.h \
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 4991941..d220365 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -811,6 +811,7 @@
 include/grpc++/impl/client_unary_call.h \
 include/grpc++/impl/codegen/async_stream.h \
 include/grpc++/impl/codegen/async_unary_call.h \
+include/grpc++/impl/codegen/byte_buffer.h \
 include/grpc++/impl/codegen/call.h \
 include/grpc++/impl/codegen/call_hook.h \
 include/grpc++/impl/codegen/channel_interface.h \
@@ -882,6 +883,7 @@
 include/grpc/impl/codegen/atm_gcc_atomic.h \
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
+include/grpc/impl/codegen/byte_buffer.h \
 include/grpc/impl/codegen/byte_buffer_reader.h \
 include/grpc/impl/codegen/compression_types.h \
 include/grpc/impl/codegen/connectivity_state.h \
@@ -931,6 +933,7 @@
 include/grpc/support/useful.h \
 include/grpc/support/workaround_list.h \
 src/core/ext/transport/inproc/inproc_transport.h \
+src/core/lib/backoff/backoff.h \
 src/core/lib/channel/channel_args.h \
 src/core/lib/channel/channel_stack.h \
 src/core/lib/channel/channel_stack_builder.h \
@@ -942,6 +945,8 @@
 src/core/lib/compression/algorithm_metadata.h \
 src/core/lib/compression/message_compress.h \
 src/core/lib/compression/stream_compression.h \
+src/core/lib/compression/stream_compression_gzip.h \
+src/core/lib/compression/stream_compression_identity.h \
 src/core/lib/debug/stats.h \
 src/core/lib/debug/stats_data.h \
 src/core/lib/debug/trace.h \
@@ -1024,7 +1029,6 @@
 src/core/lib/support/atomic.h \
 src/core/lib/support/atomic_with_atm.h \
 src/core/lib/support/atomic_with_std.h \
-src/core/lib/support/backoff.h \
 src/core/lib/support/block_annotate.h \
 src/core/lib/support/env.h \
 src/core/lib/support/memory.h \
diff --git a/tools/doxygen/Doxyfile.core b/tools/doxygen/Doxyfile.core
index 6327353..b8514fe 100644
--- a/tools/doxygen/Doxyfile.core
+++ b/tools/doxygen/Doxyfile.core
@@ -811,6 +811,7 @@
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
 include/grpc/impl/codegen/atm_windows.h \
+include/grpc/impl/codegen/byte_buffer.h \
 include/grpc/impl/codegen/byte_buffer_reader.h \
 include/grpc/impl/codegen/compression_types.h \
 include/grpc/impl/codegen/connectivity_state.h \
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index e352cb7..430566b 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -811,6 +811,7 @@
 include/grpc/impl/codegen/atm_gcc_sync.h \
 include/grpc/impl/codegen/atm_windows.h \
 include/grpc/impl/codegen/atm_windows.h \
+include/grpc/impl/codegen/byte_buffer.h \
 include/grpc/impl/codegen/byte_buffer_reader.h \
 include/grpc/impl/codegen/compression_types.h \
 include/grpc/impl/codegen/connectivity_state.h \
@@ -1055,6 +1056,8 @@
 src/core/ext/transport/inproc/inproc_transport.c \
 src/core/ext/transport/inproc/inproc_transport.h \
 src/core/lib/README.md \
+src/core/lib/backoff/backoff.c \
+src/core/lib/backoff/backoff.h \
 src/core/lib/channel/README.md \
 src/core/lib/channel/channel_args.c \
 src/core/lib/channel/channel_args.h \
@@ -1077,6 +1080,10 @@
 src/core/lib/compression/message_compress.h \
 src/core/lib/compression/stream_compression.c \
 src/core/lib/compression/stream_compression.h \
+src/core/lib/compression/stream_compression_gzip.c \
+src/core/lib/compression/stream_compression_gzip.h \
+src/core/lib/compression/stream_compression_identity.c \
+src/core/lib/compression/stream_compression_identity.h \
 src/core/lib/debug/stats.c \
 src/core/lib/debug/stats.h \
 src/core/lib/debug/stats_data.c \
@@ -1296,8 +1303,6 @@
 src/core/lib/support/atomic_with_atm.h \
 src/core/lib/support/atomic_with_std.h \
 src/core/lib/support/avl.c \
-src/core/lib/support/backoff.c \
-src/core/lib/support/backoff.h \
 src/core/lib/support/block_annotate.h \
 src/core/lib/support/cmdline.c \
 src/core/lib/support/cpu_iphone.c \
diff --git a/tools/gce/linux_performance_worker_init.sh b/tools/gce/linux_performance_worker_init.sh
index 88d8de7..8d900f1 100755
--- a/tools/gce/linux_performance_worker_init.sh
+++ b/tools/gce/linux_performance_worker_init.sh
@@ -72,6 +72,7 @@
 sudo apt-get install -y libgflags-dev libgtest-dev libc++-dev clang
 
 # Python dependencies
+sudo pip install --upgrade pip==9.0.1
 sudo pip install tabulate
 sudo pip install google-api-python-client
 sudo pip install virtualenv
diff --git a/tools/github/pr_latency.py b/tools/github/pr_latency.py
new file mode 100644
index 0000000..5d63583
--- /dev/null
+++ b/tools/github/pr_latency.py
@@ -0,0 +1,163 @@
+#!/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.
+
+"""Measure the time between PR creation and completion of all tests.
+
+You'll need a github API token to avoid being rate-limited. See
+https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
+
+This script goes over the most recent 100 pull requests. For PRs with a single
+commit, it uses the PR's creation as the initial time; othewise, it uses the
+date of the last commit. This is somewhat fragile, and imposed by the fact that
+GitHub reports a PR's updated timestamp for any event that modifies the PR (e.g.
+comments), not just the addition of new commits.
+
+In addition, it ignores latencies greater than five hours, as that's likely due
+to a manual re-run of tests.
+"""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import json
+import logging
+import pprint
+import urllib2
+
+from datetime import datetime, timedelta
+
+logging.basicConfig(format='%(asctime)s %(message)s')
+
+PRS = 'https://api.github.com/repos/grpc/grpc/pulls?state=open&per_page=100'
+COMMITS = 'https://api.github.com/repos/grpc/grpc/pulls/{pr_number}/commits'
+
+
+def gh(url):
+  request = urllib2.Request(url)
+  if TOKEN:
+    request.add_header('Authorization', 'token {}'.format(TOKEN))
+  response = urllib2.urlopen(request)
+  return response.read()
+
+
+def print_csv_header():
+  print('pr,base_time,test_time,latency_seconds,successes,failures,errors')
+
+
+def output(pr, base_time, test_time, diff_time, successes, failures, errors, mode='human'):
+  if mode == 'human':
+    print("PR #{} base time: {} UTC, Tests completed at: {} UTC. Latency: {}."
+          "\n\tSuccesses: {}, Failures: {}, Errors: {}".format(
+              pr, base_time, test_time, diff_time, successes, failures, errors))
+  elif mode == 'csv':
+    print(','.join([str(pr), str(base_time),
+                    str(test_time), str(int((test_time-base_time).total_seconds())),
+                    str(successes), str(failures), str(errors)]))
+
+
+def parse_timestamp(datetime_str):
+  return datetime.strptime(datetime_str, '%Y-%m-%dT%H:%M:%SZ')
+
+
+def to_posix_timestamp(dt):
+  return str((dt - datetime(1970, 1, 1)).total_seconds())
+
+
+def get_pr_data():
+  latest_prs = json.loads(gh(PRS))
+  res =  [{'number': pr['number'],
+           'created_at': parse_timestamp(pr['created_at']),
+           'updated_at': parse_timestamp(pr['updated_at']),
+           'statuses_url': pr['statuses_url']}
+          for pr in latest_prs]
+  return res
+
+
+def get_commits_data(pr_number):
+  commits = json.loads(gh(COMMITS.format(pr_number=pr_number)))
+  return {'num_commits': len(commits),
+          'most_recent_date': parse_timestamp(commits[-1]['commit']['author']['date'])}
+
+
+def get_status_data(statuses_url, system):
+  status_url = statuses_url.replace('statuses', 'status')
+  statuses = json.loads(gh(status_url + '?per_page=100'))
+  successes = 0
+  failures = 0
+  errors = 0
+  latest_datetime = None
+  if not statuses: return None
+  if system == 'kokoro': string_in_target_url = 'kokoro'
+  elif system == 'jenkins': string_in_target_url = 'grpc-testing'
+  for status in statuses['statuses']:
+    if not status['target_url'] or string_in_target_url not in status['target_url']: continue  # Ignore jenkins
+    if status['state'] == 'pending': return None
+    elif status['state'] == 'success': successes += 1
+    elif status['state'] == 'failure': failures += 1
+    elif status['state'] == 'error': errors += 1
+    if not latest_datetime:
+      latest_datetime = parse_timestamp(status['updated_at'])
+    else:
+      latest_datetime = max(latest_datetime, parse_timestamp(status['updated_at']))
+  # First status is the most recent one.
+  if any([successes, failures, errors]) and sum([successes, failures, errors]) > 15:
+    return {'latest_datetime': latest_datetime,
+            'successes': successes,
+            'failures': failures,
+            'errors': errors}
+  else: return None
+
+
+def build_args_parser():
+  import argparse
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--format', type=str, choices=['human', 'csv'],
+                      default='human',
+                      help='Output format: are you a human or a machine?')
+  parser.add_argument('--system', type=str, choices=['jenkins', 'kokoro'],
+                      required=True, help='Consider only the given CI system')
+  parser.add_argument('--token', type=str, default='',
+                      help='GitHub token to use its API with a higher rate limit')
+  return parser
+
+
+def main():
+  import sys
+  global TOKEN
+  args_parser = build_args_parser()
+  args = args_parser.parse_args()
+  TOKEN = args.token
+  if args.format == 'csv': print_csv_header()
+  for pr_data in get_pr_data():
+    commit_data = get_commits_data(pr_data['number'])
+    # PR with a single commit -> use the PRs creation time.
+    # else -> use the latest commit's date.
+    base_timestamp = pr_data['updated_at']
+    if commit_data['num_commits'] > 1:
+      base_timestamp = commit_data['most_recent_date']
+    else:
+      base_timestamp = pr_data['created_at']
+    last_status = get_status_data(pr_data['statuses_url'], args.system)
+    if last_status:
+      diff = last_status['latest_datetime'] - base_timestamp
+      if diff < timedelta(hours=5):
+        output(pr_data['number'], base_timestamp, last_status['latest_datetime'],
+               diff, last_status['successes'], last_status['failures'],
+               last_status['errors'], mode=args.format)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/internal_ci/linux/grpc_line_count.cfg b/tools/internal_ci/linux/grpc_line_count.cfg
new file mode 100644
index 0000000..5854d9c
--- /dev/null
+++ b/tools/internal_ci/linux/grpc_line_count.cfg
@@ -0,0 +1,19 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/linux/grpc_line_count.sh"
+timeout_mins: 30
diff --git a/tools/internal_ci/linux/grpc_line_count.sh b/tools/internal_ci/linux/grpc_line_count.sh
new file mode 100755
index 0000000..22e484b
--- /dev/null
+++ b/tools/internal_ci/linux/grpc_line_count.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+# 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 script counts the numbers of line in gRPC's repo and uploads to BQ
+set -ex
+
+# Enter the gRPC repo root
+cd $(dirname $0)/../../..
+
+git submodule update --init
+
+# Install cloc
+git clone -b v1.72 https://github.com/AlDanial/cloc/ ~/cloc
+PERL_MM_USE_DEFAULT=1 sudo perl -MCPAN -e 'install Regexp::Common; install Algorithm::Diff'
+sudo make install -C ~/cloc/Unix
+
+./tools/line_count/collect-now.sh
diff --git a/tools/internal_ci/linux/grpc_performance_profile_daily.cfg b/tools/internal_ci/linux/grpc_performance_profile_daily.cfg
index 9831869..7320a19 100644
--- a/tools/internal_ci/linux/grpc_performance_profile_daily.cfg
+++ b/tools/internal_ci/linux/grpc_performance_profile_daily.cfg
@@ -19,7 +19,6 @@
 timeout_mins: 1440
 action {
   define_artifacts {
-    regex: "**"
     regex: "github/grpc/reports/**"
   }
 }
diff --git a/tools/internal_ci/linux/grpc_performance_profile_daily.sh b/tools/internal_ci/linux/grpc_performance_profile_daily.sh
index 25523e2..34d41bc 100755
--- a/tools/internal_ci/linux/grpc_performance_profile_daily.sh
+++ b/tools/internal_ci/linux/grpc_performance_profile_daily.sh
@@ -22,6 +22,8 @@
 
 CPUS=`python -c 'import multiprocessing; print multiprocessing.cpu_count()'`
 
+./tools/run_tests/start_port_server.py || true
+
 make CONFIG=opt memory_profile_test memory_profile_client memory_profile_server -j $CPUS
 bins/opt/memory_profile_test
 bq load microbenchmarks.memory memory_usage.csv
diff --git a/tools/internal_ci/linux/grpc_performance_profile_master.cfg b/tools/internal_ci/linux/grpc_performance_profile_master.cfg
index e4cefbc..ccb8aab 100644
--- a/tools/internal_ci/linux/grpc_performance_profile_master.cfg
+++ b/tools/internal_ci/linux/grpc_performance_profile_master.cfg
@@ -19,7 +19,6 @@
 timeout_mins: 600
 action {
   define_artifacts {
-    regex: "**"
     regex: "github/grpc/reports/**"
   }
 }
diff --git a/tools/internal_ci/windows/pull_request/grpc_basictests_dbg.cfg b/tools/internal_ci/windows/pull_request/grpc_basictests_dbg.cfg
new file mode 100644
index 0000000..81a1e9d
--- /dev/null
+++ b/tools/internal_ci/windows/pull_request/grpc_basictests_dbg.cfg
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat"
+timeout_mins: 240
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.xml"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests windows dbg -j 1 --inner_jobs 8 --internal_ci --max_time=3600"
+}
diff --git a/tools/internal_ci/windows/pull_request/grpc_basictests_opt.cfg b/tools/internal_ci/windows/pull_request/grpc_basictests_opt.cfg
new file mode 100644
index 0000000..3bb6510
--- /dev/null
+++ b/tools/internal_ci/windows/pull_request/grpc_basictests_opt.cfg
@@ -0,0 +1,30 @@
+# 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.
+
+# Config file for the internal CI (in protobuf text format)
+
+# Location of the continuous shell script in repository.
+build_file: "grpc/tools/internal_ci/windows/grpc_run_tests_matrix.bat"
+timeout_mins: 240
+action {
+  define_artifacts {
+    regex: "**/*sponge_log.xml"
+    regex: "github/grpc/reports/**"
+  }
+}
+
+env_vars {
+  key: "RUN_TESTS_FLAGS"
+  value: "-f basictests windows opt -j 1 --inner_jobs 8 --internal_ci --max_time=3600"
+}
diff --git a/tools/interop_matrix/README.md b/tools/interop_matrix/README.md
index f92dc69..c2f3543 100644
--- a/tools/interop_matrix/README.md
+++ b/tools/interop_matrix/README.md
@@ -5,6 +5,21 @@
 The setup builds gRPC docker images for each language/runtime and upload it to Google Container Registry (GCR). These images, encapsulating gRPC stack
 from specific releases/tag, are used to test version compatiblity between gRPC release versions.
 
+## Step-by-step instructions for adding a new release to compatibility test
+We have continuous nightly test setup to test gRPC backward compatibility between old clients and latest server.  When a gRPC developer creates a new gRPC release, s/he is also responsible to add the just-released gRPC client to the nightly test.  The steps are:
+- Add (or update) an entry in ./client_matrix.py file to reference the github tag for the release.
+- Build new client docker image(s).  For example, for java release `v1.9.9`, do
+  - `tools/interop_matrix/create_matrix_images.py --git_checkout --release=v1.9.9 --language=java`
+- Verify that the new docker image was built successfully and uploaded to GCR.  For example,
+  - `gcloud beta container images list-tags gcr.io/grpc-testing/grpc_interop_java_oracle8`
+  - should show an image entry with tag `v1.9.9`.
+- Verify the just-created docker client image would pass backward compatibility test (it should).  For example,
+  - `gcloud docker -- pull gcr.io/grpc-testing/grpc_interop_java_oracle8:v1.9.9` followed by
+  - `docker_image=gcr.io/grpc-testing/grpc_interop_java_oracle8:v1.9.9 ./testcases/java__master`
+- git commit the change and merge it to upstream/master.
+- (Optional) clean up the tmp directory to where grpc source is cloned at `/export/hda3/tmp/grpc_matrix/`.
+For more details on each step, refer to sections below.
+
 ## Instructions for creating GCR images
 - Edit  `./client_matrix.py` to include desired gRPC release.
 - Run `tools/interop_matrix/create_matrix_images.py`.  Useful options:
@@ -45,3 +60,4 @@
 
 Note:
 - File path starting with `tools/` or `template/` are relative to the grpc repo root dir.  File path starting with `./` are relative to current directory (`tools/interop_matrix`).
+- Creating and referencing images in GCR require read and write permission to Google Container Registry path gcr.io/grpc-testing.
diff --git a/tools/jenkins/run_full_performance.sh b/tools/jenkins/run_full_performance.sh
index 0f101f6..a9661c7 100755
--- a/tools/jenkins/run_full_performance.sh
+++ b/tools/jenkins/run_full_performance.sh
@@ -21,7 +21,7 @@
 
 # run 8core client vs 8core server
 tools/run_tests/run_performance_tests.py \
-    -l c++ csharp node ruby java python go node_express \
+    -l c++ csharp node ruby java python go node_express php \
     --netperf \
     --category scalable \
     --bq_result_table performance_test.performance_experiment \
diff --git a/tools/jenkins/run_performance_profile_daily.sh b/tools/jenkins/run_performance_profile_daily.sh
index 04a2464..48d82a9 100755
--- a/tools/jenkins/run_performance_profile_daily.sh
+++ b/tools/jenkins/run_performance_profile_daily.sh
@@ -29,4 +29,6 @@
 
 BENCHMARKS_TO_RUN="bm_fullstack_unary_ping_pong bm_fullstack_streaming_ping_pong bm_fullstack_streaming_pump bm_closure bm_cq bm_call_create bm_error bm_chttp2_hpack bm_chttp2_transport bm_pollset bm_metadata"
 
+./tools/run_tests/start_port_server.py || true
+
 $PYTHON tools/run_tests/run_microbenchmark.py --collect summary perf latency -b $BENCHMARKS_TO_RUN
diff --git a/tools/profiling/microbenchmarks/bm_diff/bm_main.py b/tools/profiling/microbenchmarks/bm_diff/bm_main.py
index 5aa11ac..516d110 100755
--- a/tools/profiling/microbenchmarks/bm_diff/bm_main.py
+++ b/tools/profiling/microbenchmarks/bm_diff/bm_main.py
@@ -23,6 +23,7 @@
 
 import sys
 import os
+import random
 import argparse
 import multiprocessing
 import subprocess
@@ -32,6 +33,12 @@
     os.path.dirname(sys.argv[0]), '..', '..', 'run_tests', 'python_utils'))
 import comment_on_pr
 
+sys.path.append(
+  os.path.join(
+    os.path.dirname(sys.argv[0]), '..', '..', '..', 'run_tests',
+    'python_utils'))
+import jobset
+
 
 def _args():
   argp = argparse.ArgumentParser(
@@ -125,8 +132,13 @@
       subprocess.check_call(['git', 'checkout', where_am_i])
       subprocess.check_call(['git', 'submodule', 'update'])
 
-  bm_run.run('new', args.benchmarks, args.jobs, args.loops, args.regex, args.counters)
-  bm_run.run(old, args.benchmarks, args.jobs, args.loops, args.regex, args.counters)
+  jobs_list = []
+  jobs_list += bm_run.create_jobs('new', args.benchmarks, args.loops, args.regex, args.counters)
+  jobs_list += bm_run.create_jobs(old, args.benchmarks, args.loops, args.regex, args.counters)
+
+  # shuffle all jobs to eliminate noise from GCE CPU drift
+  random.shuffle(jobs_list, random.SystemRandom().random)
+  jobset.run(jobs_list, maxjobs=args.jobs)
 
   diff, note = bm_diff.diff(args.benchmarks, args.loops, args.regex, args.track, old,
                 'new', args.counters)
diff --git a/tools/profiling/microbenchmarks/bm_diff/bm_run.py b/tools/profiling/microbenchmarks/bm_diff/bm_run.py
index 206f7c5..81db5a2 100755
--- a/tools/profiling/microbenchmarks/bm_diff/bm_run.py
+++ b/tools/profiling/microbenchmarks/bm_diff/bm_run.py
@@ -95,11 +95,12 @@
         shortname='%s %s %s %s %d/%d' % (bm, line, cfg, name, idx + 1,
                          loops),
         verbose_success=True,
+        cpu_cost=2,
         timeout_seconds=60 * 60)) # one hour
   return jobs_list
 
 
-def run(name, benchmarks, jobs, loops, regex, counters):
+def create_jobs(name, benchmarks, loops, regex, counters):
   jobs_list = []
   for loop in range(0, loops):
     for bm in benchmarks:
@@ -108,9 +109,11 @@
         jobs_list += _collect_bm_data(bm, 'counters', name, regex, loop,
                         loops)
   random.shuffle(jobs_list, random.SystemRandom().random)
-  jobset.run(jobs_list, maxjobs=jobs)
+  return jobs_list
 
 
 if __name__ == '__main__':
   args = _args()
-  run(args.name, args.benchmarks, args.jobs, args.loops, args.regex, args.counters)
+  jobs_list = create_jobs(args.name, args.benchmarks, args.loops, 
+                          args.regex, args.counters)
+  jobset.run(jobs_list, maxjobs=args.jobs)
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index 8d42c3e..5e825e2 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -104,6 +104,23 @@
       "gpr", 
       "gpr_test_util", 
       "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "backoff_test", 
+    "src": [
+      "test/core/backoff/backoff_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
       "grpc_test_util", 
       "test_tcp_server"
     ], 
@@ -734,21 +751,6 @@
     "headers": [], 
     "is_filegroup": false, 
     "language": "c", 
-    "name": "gpr_backoff_test", 
-    "src": [
-      "test/core/support/backoff_test.c"
-    ], 
-    "third_party": false, 
-    "type": "target"
-  }, 
-  {
-    "deps": [
-      "gpr", 
-      "gpr_test_util"
-    ], 
-    "headers": [], 
-    "is_filegroup": false, 
-    "language": "c", 
     "name": "gpr_cmdline_test", 
     "src": [
       "test/core/support/cmdline_test.c"
@@ -7773,7 +7775,6 @@
       "src/core/lib/support/arena.c", 
       "src/core/lib/support/atm.c", 
       "src/core/lib/support/avl.c", 
-      "src/core/lib/support/backoff.c", 
       "src/core/lib/support/cmdline.c", 
       "src/core/lib/support/cpu_iphone.c", 
       "src/core/lib/support/cpu_linux.c", 
@@ -7854,7 +7855,6 @@
       "src/core/lib/support/atomic.h", 
       "src/core/lib/support/atomic_with_atm.h", 
       "src/core/lib/support/atomic_with_std.h", 
-      "src/core/lib/support/backoff.h", 
       "src/core/lib/support/block_annotate.h", 
       "src/core/lib/support/env.h", 
       "src/core/lib/support/memory.h", 
@@ -7903,7 +7903,6 @@
       "src/core/lib/support/atomic.h", 
       "src/core/lib/support/atomic_with_atm.h", 
       "src/core/lib/support/atomic_with_std.h", 
-      "src/core/lib/support/backoff.h", 
       "src/core/lib/support/block_annotate.h", 
       "src/core/lib/support/env.h", 
       "src/core/lib/support/memory.h", 
@@ -7997,6 +7996,7 @@
     "language": "c", 
     "name": "grpc_base", 
     "src": [
+      "src/core/lib/backoff/backoff.c", 
       "src/core/lib/channel/channel_args.c", 
       "src/core/lib/channel/channel_stack.c", 
       "src/core/lib/channel/channel_stack_builder.c", 
@@ -8007,6 +8007,8 @@
       "src/core/lib/compression/compression.c", 
       "src/core/lib/compression/message_compress.c", 
       "src/core/lib/compression/stream_compression.c", 
+      "src/core/lib/compression/stream_compression_gzip.c", 
+      "src/core/lib/compression/stream_compression_identity.c", 
       "src/core/lib/debug/stats.c", 
       "src/core/lib/debug/stats_data.c", 
       "src/core/lib/http/format_request.c", 
@@ -8148,6 +8150,7 @@
       "include/grpc/slice_buffer.h", 
       "include/grpc/status.h", 
       "include/grpc/support/workaround_list.h", 
+      "src/core/lib/backoff/backoff.h", 
       "src/core/lib/channel/channel_args.h", 
       "src/core/lib/channel/channel_stack.h", 
       "src/core/lib/channel/channel_stack_builder.h", 
@@ -8159,6 +8162,8 @@
       "src/core/lib/compression/algorithm_metadata.h", 
       "src/core/lib/compression/message_compress.h", 
       "src/core/lib/compression/stream_compression.h", 
+      "src/core/lib/compression/stream_compression_gzip.h", 
+      "src/core/lib/compression/stream_compression_identity.h", 
       "src/core/lib/debug/stats.h", 
       "src/core/lib/debug/stats_data.h", 
       "src/core/lib/http/format_request.h", 
@@ -8279,6 +8284,7 @@
       "include/grpc/slice_buffer.h", 
       "include/grpc/status.h", 
       "include/grpc/support/workaround_list.h", 
+      "src/core/lib/backoff/backoff.h", 
       "src/core/lib/channel/channel_args.h", 
       "src/core/lib/channel/channel_stack.h", 
       "src/core/lib/channel/channel_stack_builder.h", 
@@ -8290,6 +8296,8 @@
       "src/core/lib/compression/algorithm_metadata.h", 
       "src/core/lib/compression/message_compress.h", 
       "src/core/lib/compression/stream_compression.h", 
+      "src/core/lib/compression/stream_compression_gzip.h", 
+      "src/core/lib/compression/stream_compression_identity.h", 
       "src/core/lib/debug/stats.h", 
       "src/core/lib/debug/stats_data.h", 
       "src/core/lib/http/format_request.h", 
@@ -8475,6 +8483,7 @@
       "gpr_codegen"
     ], 
     "headers": [
+      "include/grpc/impl/codegen/byte_buffer.h", 
       "include/grpc/impl/codegen/byte_buffer_reader.h", 
       "include/grpc/impl/codegen/compression_types.h", 
       "include/grpc/impl/codegen/connectivity_state.h", 
@@ -8488,6 +8497,7 @@
     "language": "c", 
     "name": "grpc_codegen", 
     "src": [
+      "include/grpc/impl/codegen/byte_buffer.h", 
       "include/grpc/impl/codegen/byte_buffer_reader.h", 
       "include/grpc/impl/codegen/compression_types.h", 
       "include/grpc/impl/codegen/connectivity_state.h", 
@@ -9366,6 +9376,7 @@
     "headers": [
       "include/grpc++/impl/codegen/async_stream.h", 
       "include/grpc++/impl/codegen/async_unary_call.h", 
+      "include/grpc++/impl/codegen/byte_buffer.h", 
       "include/grpc++/impl/codegen/call.h", 
       "include/grpc++/impl/codegen/call_hook.h", 
       "include/grpc++/impl/codegen/channel_interface.h", 
@@ -9400,6 +9411,7 @@
     "src": [
       "include/grpc++/impl/codegen/async_stream.h", 
       "include/grpc++/impl/codegen/async_unary_call.h", 
+      "include/grpc++/impl/codegen/byte_buffer.h", 
       "include/grpc++/impl/codegen/call.h", 
       "include/grpc++/impl/codegen/call_hook.h", 
       "include/grpc++/impl/codegen/channel_interface.h", 
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 72dfbc0..ff1d47c 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -121,6 +121,28 @@
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
+    "name": "backoff_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix", 
+      "windows"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
     ], 
@@ -805,28 +827,6 @@
     "flaky": false, 
     "gtest": false, 
     "language": "c", 
-    "name": "gpr_backoff_test", 
-    "platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ]
-  }, 
-  {
-    "args": [], 
-    "ci_platforms": [
-      "linux", 
-      "mac", 
-      "posix", 
-      "windows"
-    ], 
-    "cpu_cost": 1.0, 
-    "exclude_configs": [], 
-    "exclude_iomgrs": [], 
-    "flaky": false, 
-    "gtest": false, 
-    "language": "c", 
     "name": "gpr_cmdline_test", 
     "platforms": [
       "linux", 
diff --git a/tools/run_tests/helper_scripts/build_python.sh b/tools/run_tests/helper_scripts/build_python.sh
index be65055..e362082 100755
--- a/tools/run_tests/helper_scripts/build_python.sh
+++ b/tools/run_tests/helper_scripts/build_python.sh
@@ -152,7 +152,7 @@
   cd $PWD
 }
 
-$VENV_PYTHON -m pip install --upgrade pip
+$VENV_PYTHON -m pip install --upgrade pip==9.0.1
 $VENV_PYTHON -m pip install setuptools
 $VENV_PYTHON -m pip install cython
 $VENV_PYTHON -m pip install six enum34 protobuf futures
diff --git a/tools/run_tests/helper_scripts/run_grpc-node.sh b/tools/run_tests/helper_scripts/run_grpc-node.sh
new file mode 100755
index 0000000..25f149f
--- /dev/null
+++ b/tools/run_tests/helper_scripts/run_grpc-node.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# Copyright 2015 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 script runs grpc/grpc-node tests with their grpc submodule updated
+# to this reference
+
+# cd to gRPC root directory
+cd $(dirname $0)/../../..
+
+CURRENT_COMMIT=$(git rev-parse --verify HEAD)
+
+rm -rf ./../grpc-node
+git clone --recursive https://github.com/grpc/grpc-node ./../grpc-node
+cd ./../grpc-node
+
+./test-grpc-submodule.sh $CURRENT_COMMIT
diff --git a/tools/run_tests/performance/massage_qps_stats.py b/tools/run_tests/performance/massage_qps_stats.py
index 3bdace1..ebbfe6c 100644
--- a/tools/run_tests/performance/massage_qps_stats.py
+++ b/tools/run_tests/performance/massage_qps_stats.py
@@ -75,6 +75,28 @@
     stats["core_http2_initiate_write_due_to_ping_response"] = massage_qps_stats_helpers.counter(core_stats, "http2_initiate_write_due_to_ping_response")
     stats["core_http2_initiate_write_due_to_force_rst_stream"] = massage_qps_stats_helpers.counter(core_stats, "http2_initiate_write_due_to_force_rst_stream")
     stats["core_http2_spurious_writes_begun"] = massage_qps_stats_helpers.counter(core_stats, "http2_spurious_writes_begun")
+    stats["core_hpack_recv_indexed"] = massage_qps_stats_helpers.counter(core_stats, "hpack_recv_indexed")
+    stats["core_hpack_recv_lithdr_incidx"] = massage_qps_stats_helpers.counter(core_stats, "hpack_recv_lithdr_incidx")
+    stats["core_hpack_recv_lithdr_incidx_v"] = massage_qps_stats_helpers.counter(core_stats, "hpack_recv_lithdr_incidx_v")
+    stats["core_hpack_recv_lithdr_notidx"] = massage_qps_stats_helpers.counter(core_stats, "hpack_recv_lithdr_notidx")
+    stats["core_hpack_recv_lithdr_notidx_v"] = massage_qps_stats_helpers.counter(core_stats, "hpack_recv_lithdr_notidx_v")
+    stats["core_hpack_recv_lithdr_nvridx"] = massage_qps_stats_helpers.counter(core_stats, "hpack_recv_lithdr_nvridx")
+    stats["core_hpack_recv_lithdr_nvridx_v"] = massage_qps_stats_helpers.counter(core_stats, "hpack_recv_lithdr_nvridx_v")
+    stats["core_hpack_recv_uncompressed"] = massage_qps_stats_helpers.counter(core_stats, "hpack_recv_uncompressed")
+    stats["core_hpack_recv_huffman"] = massage_qps_stats_helpers.counter(core_stats, "hpack_recv_huffman")
+    stats["core_hpack_recv_binary"] = massage_qps_stats_helpers.counter(core_stats, "hpack_recv_binary")
+    stats["core_hpack_recv_binary_base64"] = massage_qps_stats_helpers.counter(core_stats, "hpack_recv_binary_base64")
+    stats["core_hpack_send_indexed"] = massage_qps_stats_helpers.counter(core_stats, "hpack_send_indexed")
+    stats["core_hpack_send_lithdr_incidx"] = massage_qps_stats_helpers.counter(core_stats, "hpack_send_lithdr_incidx")
+    stats["core_hpack_send_lithdr_incidx_v"] = massage_qps_stats_helpers.counter(core_stats, "hpack_send_lithdr_incidx_v")
+    stats["core_hpack_send_lithdr_notidx"] = massage_qps_stats_helpers.counter(core_stats, "hpack_send_lithdr_notidx")
+    stats["core_hpack_send_lithdr_notidx_v"] = massage_qps_stats_helpers.counter(core_stats, "hpack_send_lithdr_notidx_v")
+    stats["core_hpack_send_lithdr_nvridx"] = massage_qps_stats_helpers.counter(core_stats, "hpack_send_lithdr_nvridx")
+    stats["core_hpack_send_lithdr_nvridx_v"] = massage_qps_stats_helpers.counter(core_stats, "hpack_send_lithdr_nvridx_v")
+    stats["core_hpack_send_uncompressed"] = massage_qps_stats_helpers.counter(core_stats, "hpack_send_uncompressed")
+    stats["core_hpack_send_huffman"] = massage_qps_stats_helpers.counter(core_stats, "hpack_send_huffman")
+    stats["core_hpack_send_binary"] = massage_qps_stats_helpers.counter(core_stats, "hpack_send_binary")
+    stats["core_hpack_send_binary_base64"] = massage_qps_stats_helpers.counter(core_stats, "hpack_send_binary_base64")
     stats["core_combiner_locks_initiated"] = massage_qps_stats_helpers.counter(core_stats, "combiner_locks_initiated")
     stats["core_combiner_locks_scheduled_items"] = massage_qps_stats_helpers.counter(core_stats, "combiner_locks_scheduled_items")
     stats["core_combiner_locks_scheduled_final_items"] = massage_qps_stats_helpers.counter(core_stats, "combiner_locks_scheduled_final_items")
@@ -85,8 +107,6 @@
     stats["core_executor_wakeup_initiated"] = massage_qps_stats_helpers.counter(core_stats, "executor_wakeup_initiated")
     stats["core_executor_queue_drained"] = massage_qps_stats_helpers.counter(core_stats, "executor_queue_drained")
     stats["core_executor_push_retries"] = massage_qps_stats_helpers.counter(core_stats, "executor_push_retries")
-    stats["core_executor_threads_created"] = massage_qps_stats_helpers.counter(core_stats, "executor_threads_created")
-    stats["core_executor_threads_used"] = massage_qps_stats_helpers.counter(core_stats, "executor_threads_used")
     stats["core_server_requested_calls"] = massage_qps_stats_helpers.counter(core_stats, "server_requested_calls")
     stats["core_server_slowpath_requests_queued"] = massage_qps_stats_helpers.counter(core_stats, "server_slowpath_requests_queued")
     h = massage_qps_stats_helpers.histogram(core_stats, "call_initial_size")
@@ -161,12 +181,6 @@
     stats["core_http2_send_flowctl_per_write_50p"] = massage_qps_stats_helpers.percentile(h.buckets, 50, h.boundaries)
     stats["core_http2_send_flowctl_per_write_95p"] = massage_qps_stats_helpers.percentile(h.buckets, 95, h.boundaries)
     stats["core_http2_send_flowctl_per_write_99p"] = massage_qps_stats_helpers.percentile(h.buckets, 99, h.boundaries)
-    h = massage_qps_stats_helpers.histogram(core_stats, "executor_closures_per_wakeup")
-    stats["core_executor_closures_per_wakeup"] = ",".join("%f" % x for x in h.buckets)
-    stats["core_executor_closures_per_wakeup_bkts"] = ",".join("%f" % x for x in h.boundaries)
-    stats["core_executor_closures_per_wakeup_50p"] = massage_qps_stats_helpers.percentile(h.buckets, 50, h.boundaries)
-    stats["core_executor_closures_per_wakeup_95p"] = massage_qps_stats_helpers.percentile(h.buckets, 95, h.boundaries)
-    stats["core_executor_closures_per_wakeup_99p"] = massage_qps_stats_helpers.percentile(h.buckets, 99, h.boundaries)
     h = massage_qps_stats_helpers.histogram(core_stats, "server_cqs_checked")
     stats["core_server_cqs_checked"] = ",".join("%f" % x for x in h.buckets)
     stats["core_server_cqs_checked_bkts"] = ",".join("%f" % x for x in h.boundaries)
diff --git a/tools/run_tests/performance/run_worker_php.sh b/tools/run_tests/performance/run_worker_php.sh
index 1da74cd..5d0c4fa 100755
--- a/tools/run_tests/performance/run_worker_php.sh
+++ b/tools/run_tests/performance/run_worker_php.sh
@@ -1,32 +1,17 @@
 #!/bin/bash
-# Copyright 2017, Google Inc.
-# All rights reserved.
+# Copyright 2017 gRPC authors.
 #
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
+# 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
 #
-#     * 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.
+#     http://www.apache.org/licenses/LICENSE-2.0
 #
-# 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.
+# 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.
 
 source ~/.rvm/scripts/rvm
 set -ex
diff --git a/tools/run_tests/performance/scenario_result_schema.json b/tools/run_tests/performance/scenario_result_schema.json
index 583bce1..169221d 100644
--- a/tools/run_tests/performance/scenario_result_schema.json
+++ b/tools/run_tests/performance/scenario_result_schema.json
@@ -387,6 +387,116 @@
       }, 
       {
         "mode": "NULLABLE", 
+        "name": "core_hpack_recv_indexed", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_lithdr_incidx", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_lithdr_incidx_v", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_lithdr_notidx", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_lithdr_notidx_v", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_lithdr_nvridx", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_lithdr_nvridx_v", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_uncompressed", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_huffman", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_binary", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_binary_base64", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_indexed", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_lithdr_incidx", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_lithdr_incidx_v", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_lithdr_notidx", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_lithdr_notidx_v", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_lithdr_nvridx", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_lithdr_nvridx_v", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_uncompressed", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_huffman", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_binary", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_binary_base64", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
         "name": "core_combiner_locks_initiated", 
         "type": "INTEGER"
       }, 
@@ -437,16 +547,6 @@
       }, 
       {
         "mode": "NULLABLE", 
-        "name": "core_executor_threads_created", 
-        "type": "INTEGER"
-      }, 
-      {
-        "mode": "NULLABLE", 
-        "name": "core_executor_threads_used", 
-        "type": "INTEGER"
-      }, 
-      {
-        "mode": "NULLABLE", 
         "name": "core_server_requested_calls", 
         "type": "INTEGER"
       }, 
@@ -757,31 +857,6 @@
       }, 
       {
         "mode": "NULLABLE", 
-        "name": "core_executor_closures_per_wakeup", 
-        "type": "STRING"
-      }, 
-      {
-        "mode": "NULLABLE", 
-        "name": "core_executor_closures_per_wakeup_bkts", 
-        "type": "STRING"
-      }, 
-      {
-        "mode": "NULLABLE", 
-        "name": "core_executor_closures_per_wakeup_50p", 
-        "type": "FLOAT"
-      }, 
-      {
-        "mode": "NULLABLE", 
-        "name": "core_executor_closures_per_wakeup_95p", 
-        "type": "FLOAT"
-      }, 
-      {
-        "mode": "NULLABLE", 
-        "name": "core_executor_closures_per_wakeup_99p", 
-        "type": "FLOAT"
-      }, 
-      {
-        "mode": "NULLABLE", 
         "name": "core_server_cqs_checked", 
         "type": "STRING"
       }, 
@@ -1109,6 +1184,116 @@
       }, 
       {
         "mode": "NULLABLE", 
+        "name": "core_hpack_recv_indexed", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_lithdr_incidx", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_lithdr_incidx_v", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_lithdr_notidx", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_lithdr_notidx_v", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_lithdr_nvridx", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_lithdr_nvridx_v", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_uncompressed", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_huffman", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_binary", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_recv_binary_base64", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_indexed", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_lithdr_incidx", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_lithdr_incidx_v", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_lithdr_notidx", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_lithdr_notidx_v", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_lithdr_nvridx", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_lithdr_nvridx_v", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_uncompressed", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_huffman", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_binary", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
+        "name": "core_hpack_send_binary_base64", 
+        "type": "INTEGER"
+      }, 
+      {
+        "mode": "NULLABLE", 
         "name": "core_combiner_locks_initiated", 
         "type": "INTEGER"
       }, 
@@ -1159,16 +1344,6 @@
       }, 
       {
         "mode": "NULLABLE", 
-        "name": "core_executor_threads_created", 
-        "type": "INTEGER"
-      }, 
-      {
-        "mode": "NULLABLE", 
-        "name": "core_executor_threads_used", 
-        "type": "INTEGER"
-      }, 
-      {
-        "mode": "NULLABLE", 
         "name": "core_server_requested_calls", 
         "type": "INTEGER"
       }, 
@@ -1479,31 +1654,6 @@
       }, 
       {
         "mode": "NULLABLE", 
-        "name": "core_executor_closures_per_wakeup", 
-        "type": "STRING"
-      }, 
-      {
-        "mode": "NULLABLE", 
-        "name": "core_executor_closures_per_wakeup_bkts", 
-        "type": "STRING"
-      }, 
-      {
-        "mode": "NULLABLE", 
-        "name": "core_executor_closures_per_wakeup_50p", 
-        "type": "FLOAT"
-      }, 
-      {
-        "mode": "NULLABLE", 
-        "name": "core_executor_closures_per_wakeup_95p", 
-        "type": "FLOAT"
-      }, 
-      {
-        "mode": "NULLABLE", 
-        "name": "core_executor_closures_per_wakeup_99p", 
-        "type": "FLOAT"
-      }, 
-      {
-        "mode": "NULLABLE", 
         "name": "core_server_cqs_checked", 
         "type": "STRING"
       }, 
diff --git a/tools/run_tests/python_utils/filter_pull_request_tests.py b/tools/run_tests/python_utils/filter_pull_request_tests.py
index f99143f..393aef8 100644
--- a/tools/run_tests/python_utils/filter_pull_request_tests.py
+++ b/tools/run_tests/python_utils/filter_pull_request_tests.py
@@ -47,7 +47,7 @@
 _CORE_TEST_SUITE = TestSuite(['c'])
 _CPP_TEST_SUITE = TestSuite(['c++'])
 _CSHARP_TEST_SUITE = TestSuite(['csharp'])
-_NODE_TEST_SUITE = TestSuite(['node'])
+_NODE_TEST_SUITE = TestSuite(['node', 'grpc-node'])
 _OBJC_TEST_SUITE = TestSuite(['objc'])
 _PHP_TEST_SUITE = TestSuite(['php', 'php7'])
 _PYTHON_TEST_SUITE = TestSuite(['python'])
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index b38108d..5750b90 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -444,6 +444,67 @@
     return self.make_target
 
 
+# This tests Node on grpc/grpc-node and will become the standard for Node testing
+class RemoteNodeLanguage(object):
+
+  def __init__(self):
+    self.platform = platform_string()
+
+  def configure(self, config, args):
+    self.config = config
+    self.args = args
+    # Note: electron ABI only depends on major and minor version, so that's all
+    # we should specify in the compiler argument
+    _check_compiler(self.args.compiler, ['default', 'node0.12',
+                                         'node4', 'node5', 'node6',
+                                         'node7', 'node8',
+                                         'electron1.3', 'electron1.6'])
+    if self.args.compiler == 'default':
+      self.runtime = 'node'
+      self.node_version = '8'
+    else:
+      if self.args.compiler.startswith('electron'):
+        self.runtime = 'electron'
+        self.node_version = self.args.compiler[8:]
+      else:
+        self.runtime = 'node'
+        # Take off the word "node"
+        self.node_version = self.args.compiler[4:]
+
+  # TODO: update with Windows/electron scripts when available for grpc/grpc-node
+  def test_specs(self):
+    if self.platform == 'windows':
+      return [self.config.job_spec(['tools\\run_tests\\helper_scripts\\run_node.bat'])]
+    else:
+      return [self.config.job_spec(['tools/run_tests/helper_scripts/run_grpc-node.sh'],
+                                   None,
+                                   environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
+
+  def pre_build_steps(self):
+    return []
+
+  def make_targets(self):
+    return []
+
+  def make_options(self):
+    return []
+
+  def build_steps(self):
+    return []
+
+  def post_tests_steps(self):
+    return []
+
+  def makefile_name(self):
+    return 'Makefile'
+
+  def dockerfile_dir(self):
+    return 'tools/dockerfile/test/node_jessie_%s' % _docker_arch_suffix(self.args.arch)
+
+  def __str__(self):
+    return 'grpc-node'
+
+
 class NodeLanguage(object):
 
   def __init__(self):
@@ -717,6 +778,9 @@
       return (pypy32_config,)
     elif args.compiler == 'python_alpine':
       return (python27_config,)
+    elif args.compiler == 'all_the_cpythons':
+      return (python27_config, python34_config, python35_config,
+              python36_config,)
     else:
       raise Exception('Compiler %s not supported.' % args.compiler)
 
@@ -1064,6 +1128,7 @@
 _LANGUAGES = {
     'c++': CLanguage('cxx', 'c++'),
     'c': CLanguage('c', 'c'),
+    'grpc-node': RemoteNodeLanguage(),
     'node': NodeLanguage(),
     'node_express': NodeExpressLanguage(),
     'php': PhpLanguage(),
@@ -1214,7 +1279,7 @@
                   choices=['default',
                            'gcc4.4', 'gcc4.6', 'gcc4.8', 'gcc4.9', 'gcc5.3', 'gcc_musl',
                            'clang3.4', 'clang3.5', 'clang3.6', 'clang3.7',
-                           'python2.7', 'python3.4', 'python3.5', 'python3.6', 'pypy', 'pypy3', 'python_alpine',
+                           'python2.7', 'python3.4', 'python3.5', 'python3.6', 'pypy', 'pypy3', 'python_alpine', 'all_the_cpythons',
                            'node0.12', 'node4', 'node5', 'node6', 'node7', 'node8',
                            'electron1.3', 'electron1.6',
                            'coreclr',
@@ -1479,8 +1544,11 @@
 
 
 def _has_epollexclusive():
+  binary = 'bins/%s/check_epollexclusive' % args.config
+  if not os.path.exists(binary):
+    return False
   try:
-    subprocess.check_call('bins/%s/check_epollexclusive' % args.config)
+    subprocess.check_call(binary)
     return True
   except subprocess.CalledProcessError, e:
     return False
diff --git a/tools/run_tests/run_tests_matrix.py b/tools/run_tests/run_tests_matrix.py
index 2417c5a..34d839e 100755
--- a/tools/run_tests/run_tests_matrix.py
+++ b/tools/run_tests/run_tests_matrix.py
@@ -169,7 +169,7 @@
                               inner_jobs=inner_jobs,
                               timeout_seconds=_CPP_RUNTESTS_TIMEOUT)
   
-  test_jobs += _generate_jobs(languages=['ruby', 'php'],
+  test_jobs += _generate_jobs(languages=['grpc-node', 'ruby', 'php'],
                               configs=['dbg', 'opt'],
                               platforms=['linux', 'macos'],
                               labels=['basictests', 'multilang'],
diff --git a/tools/run_tests/sanity/check_test_filtering.py b/tools/run_tests/sanity/check_test_filtering.py
index bea8125..3ebb938 100755
--- a/tools/run_tests/sanity/check_test_filtering.py
+++ b/tools/run_tests/sanity/check_test_filtering.py
@@ -25,7 +25,7 @@
 from run_tests_matrix import _create_test_jobs, _create_portability_test_jobs
 import python_utils.filter_pull_request_tests as filter_pull_request_tests
 
-_LIST_OF_LANGUAGE_LABELS = ['c', 'c++', 'csharp', 'node', 'objc', 'php', 'php7', 'python', 'ruby']
+_LIST_OF_LANGUAGE_LABELS = ['c', 'c++', 'csharp', 'grpc-node', 'node', 'objc', 'php', 'php7', 'python', 'ruby']
 _LIST_OF_PLATFORM_LABELS = ['linux', 'macos', 'windows']
 
 class TestFilteringTest(unittest.TestCase):