diff --git a/Makefile.am b/Makefile.am
index 24561a58b9d6c2dd401007425d0ea2d8b56374ca..358dd7bb0c173b523250583911cc227524107ce4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -94,6 +94,8 @@ libweston_@LIBWESTON_MAJOR@_la_SOURCES =			\
 	libweston/timeline-object.h			\
 	libweston/linux-dmabuf.c			\
 	libweston/linux-dmabuf.h			\
+	libweston/linux-explicit-synchronization.c	\
+	libweston/linux-explicit-synchronization.h	\
 	libweston/pixel-formats.c			\
 	libweston/pixel-formats.h			\
 	libweston/weston-debug.c			\
@@ -177,7 +179,9 @@ nodist_libweston_@LIBWESTON_MAJOR@_la_SOURCES =				\
 	protocol/input-timestamps-unstable-v1-protocol.c		\
 	protocol/input-timestamps-unstable-v1-server-protocol.h		\
 	protocol/weston-touch-calibration-protocol.c			\
-	protocol/weston-touch-calibration-server-protocol.h
+	protocol/weston-touch-calibration-server-protocol.h		\
+	protocol/linux-explicit-synchronization-unstable-v1-protocol.c	\
+	protocol/linux-explicit-synchronization-unstable-v1-server-protocol.h
 
 BUILT_SOURCES += $(nodist_libweston_@LIBWESTON_MAJOR@_la_SOURCES)
 
@@ -968,7 +972,9 @@ BUILT_SOURCES +=					\
 	protocol/input-timestamps-unstable-v1-protocol.c		\
 	protocol/input-timestamps-unstable-v1-client-protocol.h		\
 	protocol/xdg-output-unstable-v1-protocol.c			\
-	protocol/xdg-output-unstable-v1-client-protocol.h
+	protocol/xdg-output-unstable-v1-client-protocol.h		\
+	protocol/linux-explicit-synchronization-unstable-v1-protocol.c	\
+	protocol/linux-explicit-synchronization-unstable-v1-client-protocol.h
 
 westondatadir = $(datadir)/weston
 dist_westondata_DATA =				\
@@ -1308,7 +1314,8 @@ weston_tests =					\
 	subsurface.weston			\
 	subsurface-shot.weston			\
 	devices.weston				\
-	touch.weston
+	touch.weston				\
+	linux-explicit-synchronization.weston
 
 AM_TESTS_ENVIRONMENT = \
 	abs_builddir='$(abs_builddir)'; export abs_builddir; \
@@ -1502,6 +1509,14 @@ touch_weston_SOURCES = tests/touch-test.c
 touch_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS)
 touch_weston_LDADD = libtest-client.la
 
+linux_explicit_synchronization_weston_SOURCES = \
+	tests/linux-explicit-synchronization-test.c
+nodist_linux_explicit_synchronization_weston_SOURCES = \
+	protocol/linux-explicit-synchronization-unstable-v1-protocol.c	\
+	protocol/linux-explicit-synchronization-unstable-v1-client-protocol.h
+linux_explicit_synchronization_weston_CFLAGS = $(AM_CFLAGS) $(TEST_CLIENT_CFLAGS)
+linux_explicit_synchronization_weston_LDADD = libtest-client.la
+
 if ENABLE_XWAYLAND_TEST
 weston_tests +=	xwayland-test.weston
 xwayland_test_weston_SOURCES = tests/xwayland-test.c
diff --git a/configure.ac b/configure.ac
index 00066eaecfb4d3f2082e3eb9dd701dbb7def2f64..cb8b0a5b300fa6e2a73216c31e82524a4bba84cd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -239,7 +239,10 @@ PKG_CHECK_MODULES(LIBEVDEV, [libevdev])
 PKG_CHECK_MODULES(LIBINPUT_BACKEND, [libinput >= 0.8.0])
 PKG_CHECK_MODULES(COMPOSITOR, [$COMPOSITOR_MODULES])
 
-PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.14],
+# XXX: For minor version 2 of zwp_linux_explicit_synchronization_v1, we
+# actually need a development version after 1.17, but there is no way to
+# express such a requirement at the moment.
+PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.17],
 		  [ac_wayland_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`])
 AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $ac_wayland_protocols_pkgdatadir)
 
diff --git a/libweston/compositor-headless.c b/libweston/compositor-headless.c
index 61a5bd93e3aba8223beedea8cf1b15a8dd5ed951..5a0e46c8d9b5ed825e82f86e04d77b7392827164 100644
--- a/libweston/compositor-headless.c
+++ b/libweston/compositor-headless.c
@@ -36,6 +36,7 @@
 #include "compositor.h"
 #include "compositor-headless.h"
 #include "shared/helpers.h"
+#include "linux-explicit-synchronization.h"
 #include "pixman-renderer.h"
 #include "presentation-time-server-protocol.h"
 #include "windowed-output-api.h"
@@ -339,6 +340,11 @@ headless_backend_create(struct weston_compositor *compositor,
 	if (!b->use_pixman && noop_renderer_init(compositor) < 0)
 		goto err_input;
 
+	/* Support zwp_linux_explicit_synchronization_unstable_v1 to enable
+	 * testing. */
+	if (linux_explicit_synchronization_setup(compositor) < 0)
+		goto err_input;
+
 	ret = weston_plugin_api_register(compositor, WESTON_WINDOWED_OUTPUT_API_NAME,
 					 &api, sizeof(api));
 
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 3ef472e3b81df2f7079f0c4ff4a4e50e7adffcaa..14033cbd821c8c7ed4485642ab80a9a9acc8bc65 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -2035,6 +2035,11 @@ destroy_surface(struct wl_resource *resource)
 	if (surface->viewport_resource)
 		wl_resource_set_user_data(surface->viewport_resource, NULL);
 
+	if (surface->synchronization_resource) {
+		wl_resource_set_user_data(surface->synchronization_resource,
+					  NULL);
+	}
+
 	weston_surface_destroy(surface);
 }
 
diff --git a/libweston/compositor.h b/libweston/compositor.h
index b4b75197729ffcf002f24c6c9c9cbdcbe366d917..3f2ad01e173e257dad8d185ce2c158ebf39de11a 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -1486,6 +1486,9 @@ struct weston_surface {
 
 	/* An list of per seat pointer constraints. */
 	struct wl_list pointer_constraints;
+
+	/* zwp_surface_synchronization_v1 resource for this surface */
+	struct wl_resource *synchronization_resource;
 };
 
 struct weston_subsurface {
diff --git a/libweston/linux-explicit-synchronization.c b/libweston/linux-explicit-synchronization.c
new file mode 100644
index 0000000000000000000000000000000000000000..c42b8aa358f188159852089391cdaf87d8fdc803
--- /dev/null
+++ b/libweston/linux-explicit-synchronization.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright © 2018 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <inttypes.h>
+
+#include "compositor.h"
+#include "linux-explicit-synchronization.h"
+#include "linux-explicit-synchronization-unstable-v1-server-protocol.h"
+
+static void
+destroy_linux_surface_synchronization(struct wl_resource *resource)
+{
+	struct weston_surface *surface =
+		wl_resource_get_user_data(resource);
+
+	if (surface)
+		surface->synchronization_resource = NULL;
+}
+
+static void
+linux_surface_synchronization_destroy(struct wl_client *client,
+				      struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static void
+linux_surface_synchronization_set_acquire_fence(struct wl_client *client,
+						struct wl_resource *resource,
+						int32_t fd)
+{
+	wl_client_post_no_memory(client);
+}
+
+static void
+linux_surface_synchronization_get_release(struct wl_client *client,
+					  struct wl_resource *resource,
+					  uint32_t id)
+{
+	wl_client_post_no_memory(client);
+}
+
+const struct zwp_linux_surface_synchronization_v1_interface
+linux_surface_synchronization_implementation = {
+	linux_surface_synchronization_destroy,
+	linux_surface_synchronization_set_acquire_fence,
+	linux_surface_synchronization_get_release,
+};
+
+static void
+linux_explicit_synchronization_destroy(struct wl_client *client,
+				       struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static void
+linux_explicit_synchronization_get_synchronization(struct wl_client *client,
+						   struct wl_resource *resource,
+						   uint32_t id,
+						   struct wl_resource *surface_resource)
+{
+	struct weston_surface *surface =
+		wl_resource_get_user_data(surface_resource);
+
+	if (surface->synchronization_resource) {
+		wl_resource_post_error(
+			resource,
+			ZWP_LINUX_EXPLICIT_SYNCHRONIZATION_V1_ERROR_SYNCHRONIZATION_EXISTS,
+			"wl_surface@%"PRIu32" already has a synchronization object",
+			wl_resource_get_id(surface_resource));
+		return;
+	}
+
+	surface->synchronization_resource =
+		wl_resource_create(client,
+				   &zwp_linux_surface_synchronization_v1_interface,
+				   wl_resource_get_version(resource), id);
+	if (!surface->synchronization_resource) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	wl_resource_set_implementation(surface->synchronization_resource,
+				       &linux_surface_synchronization_implementation,
+				       surface,
+				       destroy_linux_surface_synchronization);
+}
+
+static const struct zwp_linux_explicit_synchronization_v1_interface
+linux_explicit_synchronization_implementation = {
+	linux_explicit_synchronization_destroy,
+	linux_explicit_synchronization_get_synchronization
+};
+
+static void
+bind_linux_explicit_synchronization(struct wl_client *client,
+				    void *data, uint32_t version,
+				    uint32_t id)
+{
+	struct weston_compositor *compositor = data;
+	struct wl_resource *resource;
+
+	resource = wl_resource_create(client,
+			&zwp_linux_explicit_synchronization_v1_interface,
+			version, id);
+	if (resource == NULL) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	wl_resource_set_implementation(resource,
+	       &linux_explicit_synchronization_implementation,
+	       compositor, NULL);
+}
+
+/** Advertise linux_explicit_synchronization support
+ *
+ * Calling this initializes the zwp_linux_explicit_synchronization_v1
+ * protocol support, so that the interface will be advertised to clients.
+ * Essentially it creates a global. Do not call this function multiple times
+ * in the compositor's lifetime. There is no way to deinit explicitly, globals
+ * will be reaped when the wl_display gets destroyed.
+ *
+ * \param compositor The compositor to init for.
+ * \return Zero on success, -1 on failure.
+ */
+WL_EXPORT int
+linux_explicit_synchronization_setup(struct weston_compositor *compositor)
+{
+	/* TODO: Update to minor version 2 when the next version of
+	 * wayland-protocols that contains it is released. */
+	if (!wl_global_create(compositor->wl_display,
+			      &zwp_linux_explicit_synchronization_v1_interface,
+			      1, compositor,
+			      bind_linux_explicit_synchronization))
+		return -1;
+
+	return 0;
+}
diff --git a/libweston/linux-explicit-synchronization.h b/libweston/linux-explicit-synchronization.h
new file mode 100644
index 0000000000000000000000000000000000000000..96edbbb63351cbaf86840b3d71f0082f6c5f0b92
--- /dev/null
+++ b/libweston/linux-explicit-synchronization.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2018 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef WESTON_LINUX_EXPLICIT_SYNCHRONIZATION_H
+#define WESTON_LINUX_EXPLICIT_SYNCHRONIZATION_H
+
+struct weston_compositor;
+
+int
+linux_explicit_synchronization_setup(struct weston_compositor *compositor);
+
+#endif /* WESTON_LINUX_EXPLICIT_SYNCHRONIZATION */
diff --git a/libweston/meson.build b/libweston/meson.build
index 0e43a3fb94ed2679f6d40a1f0b726592984e117e..64efdd95a54e62b767807973c56bf34d0bc41e40 100644
--- a/libweston/meson.build
+++ b/libweston/meson.build
@@ -16,6 +16,7 @@ srcs_libweston = [
 	'data-device.c',
 	'input.c',
 	'linux-dmabuf.c',
+	'linux-explicit-synchronization.c',
 	'log.c',
 	'noop-renderer.c',
 	'pixel-formats.c',
@@ -29,6 +30,8 @@ srcs_libweston = [
 	'../shared/matrix.c',
 	linux_dmabuf_unstable_v1_protocol_c,
 	linux_dmabuf_unstable_v1_server_protocol_h,
+	linux_explicit_synchronization_unstable_v1_protocol_c,
+	linux_explicit_synchronization_unstable_v1_server_protocol_h,
 	input_method_unstable_v1_protocol_c,
 	input_method_unstable_v1_server_protocol_h,
 	input_timestamps_unstable_v1_protocol_c,
diff --git a/protocol/meson.build b/protocol/meson.build
index a947eeab8b81a5736d7c7564190bcdca12cc7926..b3ea5b214988e1bedf3f3133a8e94c8ddc0949c9 100644
--- a/protocol/meson.build
+++ b/protocol/meson.build
@@ -1,7 +1,10 @@
 dep_scanner = dependency('wayland-scanner', native: true)
 prog_scanner = find_program(dep_scanner.get_pkgconfig_variable('wayland_scanner'))
 
-dep_wp = dependency('wayland-protocols', version: '>= 1.14')
+# XXX: For minor version 2 of zwp_linux_explicit_synchronization_v1, we
+# actually need a development version after 1.17, but there is no way to
+# express such a requirement at the moment.
+dep_wp = dependency('wayland-protocols', version: '>= 1.17')
 dir_wp_base = dep_wp.get_pkgconfig_variable('pkgdatadir')
 
 install_data(
@@ -18,6 +21,7 @@ generated_protocols = [
 	[ 'ivi-hmi-controller', 'internal' ],
 	[ 'fullscreen-shell', 'v1' ],
 	[ 'linux-dmabuf', 'v1' ],
+	[ 'linux-explicit-synchronization', 'v1' ],
 	[ 'presentation-time', 'stable' ],
 	[ 'pointer-constraints', 'v1' ],
 	[ 'relative-pointer', 'v1' ],
diff --git a/tests/linux-explicit-synchronization-test.c b/tests/linux-explicit-synchronization-test.c
new file mode 100644
index 0000000000000000000000000000000000000000..62a54e0d4290575404675a878a97cdd77869c137
--- /dev/null
+++ b/tests/linux-explicit-synchronization-test.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright © 2018 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "linux-explicit-synchronization-unstable-v1-client-protocol.h"
+#include "weston-test-client-helper.h"
+#include "wayland-server-protocol.h"
+
+static struct zwp_linux_explicit_synchronization_v1 *
+get_linux_explicit_synchronization(struct client *client)
+{
+	struct global *g;
+	struct global *global_sync = NULL;
+	struct zwp_linux_explicit_synchronization_v1 *sync = NULL;
+
+	wl_list_for_each(g, &client->global_list, link) {
+		if (strcmp(g->interface,
+			   zwp_linux_explicit_synchronization_v1_interface.name))
+			continue;
+
+		if (global_sync)
+			assert(!"Multiple linux explicit sync objects");
+
+		global_sync = g;
+	}
+
+	assert(global_sync);
+	assert(global_sync->version == 1);
+
+	sync = wl_registry_bind(
+			client->wl_registry, global_sync->name,
+			&zwp_linux_explicit_synchronization_v1_interface, 1);
+	assert(sync);
+
+	return sync;
+}
+
+static struct client *
+create_test_client(void)
+{
+	struct client *cl = create_client_and_test_surface(0, 0, 100, 100);
+	assert(cl);
+	return cl;
+}
+
+TEST(second_surface_synchronization_on_surface_raises_error)
+{
+	struct client *client = create_test_client();
+	struct zwp_linux_explicit_synchronization_v1 *sync =
+		get_linux_explicit_synchronization(client);
+	struct zwp_linux_surface_synchronization_v1 *surface_sync1;
+	struct zwp_linux_surface_synchronization_v1 *surface_sync2;
+
+	surface_sync1 =
+		zwp_linux_explicit_synchronization_v1_get_synchronization(
+			sync, client->surface->wl_surface);
+	client_roundtrip(client);
+
+	/* Second surface_synchronization creation should fail */
+	surface_sync2 =
+		zwp_linux_explicit_synchronization_v1_get_synchronization(
+			sync, client->surface->wl_surface);
+	expect_protocol_error(
+		client,
+		&zwp_linux_explicit_synchronization_v1_interface,
+		ZWP_LINUX_EXPLICIT_SYNCHRONIZATION_V1_ERROR_SYNCHRONIZATION_EXISTS);
+
+	zwp_linux_surface_synchronization_v1_destroy(surface_sync2);
+	zwp_linux_surface_synchronization_v1_destroy(surface_sync1);
+	zwp_linux_explicit_synchronization_v1_destroy(sync);
+}
diff --git a/tests/meson.build b/tests/meson.build
index ebd3872aad3a062fc7d7d17d97fec06d221343df..7baee5122c0afcf7e2d5fd2411ff647ded061cd8 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -127,6 +127,13 @@ tests_weston = [
 			input_timestamps_unstable_v1_protocol_c,
 		]
 	],
+	[
+		'linux-explicit-synchronization',
+		[
+			linux_explicit_synchronization_unstable_v1_client_protocol_h,
+			linux_explicit_synchronization_unstable_v1_protocol_c,
+		]
+	],
 	['internal-screenshot'],
 	[
 		'presentation',