Snap for 7550930 from 6b06e7807dcb0c6226397cb8f453caa087775373 to mainline-captiveportallogin-release

Change-Id: I1ed725ef6eb94191f9349692baa97c019e7f50ff
diff --git a/Android.bp b/Android.bp
index 477efe2..cb0fda9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,3 +1,37 @@
+package {
+    default_applicable_licenses: ["external_igt-gpu-tools_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "external_igt-gpu-tools_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-GPL",
+        "SPDX-license-identifier-GPL-2.0",
+        "SPDX-license-identifier-ISC",
+        "SPDX-license-identifier-LGPL",
+        "SPDX-license-identifier-MIT",
+    ],
+    license_text: [
+        "COPYING",
+    ],
+}
+
 cc_defaults {
     name: "igt-gpu-tools-defaults",
     cflags: [
@@ -12,6 +46,15 @@
     ],
     static_libs: ["libelf", "libkmod", "libion", "liblog"],
     shared_libs: ["libdrm"],
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64"
+        },
+    },
 }
 
 cc_library_static {
@@ -58,28 +101,58 @@
     name: "gem_blt",
     defaults: ["igt-gpu-tools-test-defaults"],
     srcs: ["benchmarks/gem_blt.c"],
+    gtest: false,
 }
 
 cc_test {
     name: "kms_flip",
     defaults: ["igt-gpu-tools-test-defaults"],
     srcs: ["tests/kms_flip.c"],
+    gtest: false,
 }
 
 cc_test {
     name: "kms_atomic",
     defaults: ["igt-gpu-tools-test-defaults"],
     srcs: ["tests/kms_atomic.c"],
+    gtest: false,
 }
 
 cc_test {
     name: "ion_fb",
     defaults: ["igt-gpu-tools-test-defaults"],
     srcs: ["tests/ion_fb.c"],
+    gtest: false,
 }
 
 cc_test {
     name: "kms_vblank",
     defaults: ["igt-gpu-tools-test-defaults"],
     srcs: ["tests/kms_vblank.c"],
+    gtest: false,
+}
+
+python_test_host {
+    name: "igt_gpu_tools",
+    main: "scripts/test_igt_gpu_tools.py",
+    srcs: [ "scripts/test_igt_gpu_tools.py", ],
+    data: [ "scripts/test_igt_gpu_tools.xml" ],
+    test_config: "scripts/test_igt_gpu_tools.xml",
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+    test_options: {
+        unit_test: false,
+    },
+}
+
+cc_test {
+    name: "kms_throughput",
+    defaults: ["igt-gpu-tools-test-defaults"],
+    srcs: ["benchmarks/kms_throughput.c"],
 }
diff --git a/METADATA b/METADATA
index 518c48c..1b2cb52 100644
--- a/METADATA
+++ b/METADATA
@@ -6,7 +6,14 @@
     value: "https://gitlab.freedesktop.org/drm/igt-gpu-tools"
   }
   version: "357dbe1869d88a2f08bcee4eebceff4ee9014424"
-  license_type: NOTICE
+  # would be NOTICE save for:
+  #   include/drm-uapi/armada_drm.h
+  #   include/drm-uapi/etnaviv_drm.h
+  #   include/drm-uapi/exynos_drm.h
+  #   include/drm-uapi/i810_drm.h
+  #   include/drm-uapi/omap_drm.h
+  #   include/drm-uapi/sync_file.h
+  license_type: RESTRICTED
   last_upgrade_date {
     year: 2019
     month: 8
diff --git a/NOTICE b/NOTICE
deleted file mode 120000
index 7a694c9..0000000
--- a/NOTICE
+++ /dev/null
@@ -1 +0,0 @@
-LICENSE
\ No newline at end of file
diff --git a/benchmarks/kms_throughput.c b/benchmarks/kms_throughput.c
new file mode 100644
index 0000000..6923045
--- /dev/null
+++ b/benchmarks/kms_throughput.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright © 2019 Google LLC
+ *
+ * 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.
+ */
+
+/** @file kms_throughput.c
+ */
+
+#include <drm.h>
+#include <sys/time.h>
+#include <xf86drm.h>
+#include "drmtest.h"
+#include "igt.h"
+#include "ion.h"
+
+static void make_display(igt_display_t *display)
+{
+	display->drm_fd = drm_open_driver_master(DRIVER_ANY);
+	igt_display_require(display, display->drm_fd);
+	igt_require(display->is_atomic);
+	igt_display_require_output(display);
+}
+
+static bool get_output(igt_display_t *display,
+		       enum pipe *pipe, igt_output_t **output)
+{
+	igt_info("Display %p has %d pipes\n", display, display->n_pipes);
+	for (int i = 0; i < display->n_pipes; ++i)
+	{
+		igt_info("Pipe %d (crtc %u) has %d planes\n",
+			 i, display->pipes[i].crtc_id,
+			 display->pipes[i].n_planes);
+	}
+
+	for_each_pipe_with_valid_output(display, *pipe, *output)
+	{
+		/* we are happy with the first one */
+		return true;
+	}
+
+	return false;
+}
+
+static void get_pipe(igt_display_t *display,
+		     igt_pipe_t **p, igt_output_t **output)
+{
+	enum pipe pipe_idx = PIPE_NONE;
+	*output = NULL;
+	igt_require(get_output(display, &pipe_idx, output));
+	igt_info("Using output id %u, name %s\n",
+		 (*output)->id, (*output)->name);
+
+	/* I'd love to call this 'pipe' but pipe(2) is in the way */
+	*p = &display->pipes[pipe_idx];
+	igt_require(*p);
+
+	igt_info("Chosen pipe (crtc %u) has %d planes\n",
+		 (*p)->crtc_id, (*p)->n_planes);
+}
+
+static void prepare(igt_display_t *display, igt_pipe_t *p, igt_output_t *output)
+{
+	igt_display_reset(display);
+	igt_output_set_pipe(output, p->pipe);
+}
+
+static igt_plane_t *plane_for_index(igt_pipe_t *p, size_t index)
+{
+	return &p->planes[index];
+}
+
+struct histogram
+{
+	struct timeval last_commit;
+	size_t num_buckets;
+	size_t *buckets;
+};
+
+static void histogram_init(struct histogram *h)
+{
+	gettimeofday(&h->last_commit, NULL);
+	h->num_buckets = 100;
+	h->buckets = calloc(h->num_buckets, sizeof(*h->buckets));
+}
+
+static void histogram_update(struct histogram *h)
+{
+	struct timeval this_commit;
+	gettimeofday(&this_commit, NULL);
+
+	struct timeval diff;
+	timersub(&this_commit, &h->last_commit, &diff);
+	const size_t ms = (diff.tv_sec * 1000) + (diff.tv_usec / 1000);
+	size_t bucket = ms;
+	if (bucket >= h->num_buckets)
+	{
+		// the last bucket is a catch-all
+		bucket = h->num_buckets - 1;
+	}
+	h->buckets[bucket]++;
+
+	memcpy(&h->last_commit, &this_commit, sizeof(this_commit));
+}
+
+static void histogram_print(struct histogram *h)
+{
+	igt_info("Histogram buckets with 1 or more entries:\n");
+
+	for (size_t i = 0; i < h->num_buckets; ++i)
+	{
+		size_t value = h->buckets[i];
+
+		if (value)
+		{
+			if (i == h->num_buckets - 1)
+			{
+				igt_info("%zu+ ms: %zu\n", i, value);
+			}
+			else
+			{
+				igt_info("%zu ms: %zu\n", i, value);
+			}
+		}
+	}
+}
+
+static void histogram_cleanup(struct histogram *h)
+{
+	free(h->buckets);
+}
+
+static const size_t max_num_fbs = 32;
+
+struct tuning
+{
+	drmModeModeInfoPtr mode;
+	size_t num_iterations;
+	size_t num_fb_sets;
+	size_t num_fbs;
+	struct fbgeom {
+		size_t width;
+		size_t height;
+	} fb_geom[max_num_fbs];
+};
+
+static void info_timestamp(const char *text)
+{
+	struct timeval ts;
+	gettimeofday(&ts, NULL);
+	igt_debug("%ld: %s\n", ts.tv_usec, text);
+}
+
+static void flip_overlays(igt_pipe_t *p, struct igt_fb **fb_sets,
+			  const struct tuning *tuning,
+			  size_t iter)
+{
+	size_t fb_set = iter % tuning->num_fb_sets;
+	struct igt_fb *fbs = fb_sets[fb_set];
+
+	for (size_t i = 0; i < tuning->num_fbs; ++i)
+	{
+		igt_plane_t *plane = plane_for_index(p, i);
+		igt_plane_set_prop_value(plane, IGT_PLANE_ZPOS, i);
+		igt_plane_set_fb(plane, &fbs[i]);
+	}
+
+	igt_pipe_obj_set_prop_value(p, IGT_CRTC_ACTIVE, 1);
+
+	info_timestamp("start commit");
+	igt_display_commit2(p->display, COMMIT_ATOMIC);
+	info_timestamp("end commit");
+}
+
+static void repeat_flip(igt_pipe_t *p, struct igt_fb **fb_sets,
+			const struct tuning *tuning)
+{
+	struct histogram h;
+	histogram_init(&h);
+
+	for (size_t iter = 0; iter < tuning->num_iterations; ++iter)
+	{
+		igt_debug("Iteration %zu\n", iter);
+		flip_overlays(p, fb_sets, tuning, iter);
+		histogram_update(&h);
+	}
+
+	igt_debug("About to clear fbs\n");
+
+	for (size_t i = 0; i < tuning->num_fbs; ++i)
+	{
+		igt_plane_t *plane = plane_for_index(p, i);
+		igt_plane_set_fb(plane, NULL);
+	}
+
+	igt_debug("About to flip with no fbs\n");
+
+	igt_display_commit2(p->display, COMMIT_ATOMIC);
+	igt_wait_for_vblank(p->display->drm_fd, p->pipe);
+
+	igt_debug("About to deactivate the crtc\n");
+
+	igt_pipe_obj_set_prop_value(p, IGT_CRTC_ACTIVE, 0);
+	igt_display_commit2(p->display, COMMIT_ATOMIC);
+
+	histogram_print(&h);
+	histogram_cleanup(&h);
+}
+
+static void create_dumb_fb(igt_display_t *display,
+			   size_t width, size_t height,
+			   struct igt_fb *fb)
+{
+	igt_create_fb(display->drm_fd,
+		      width, height,
+		      DRM_FORMAT_ARGB8888, LOCAL_DRM_FORMAT_MOD_NONE, fb);
+}
+
+
+static int get_num_planes(igt_display_t *display)
+{
+	const int drm_fd = display->drm_fd;
+	int ret;
+
+	drmModePlaneRes *const plane_resources =
+		drmModeGetPlaneResources(drm_fd);
+
+	ret = plane_resources->count_planes;
+
+	drmModeFreePlaneResources(plane_resources);
+
+	return ret;
+}
+
+static int get_max_zpos(igt_display_t *display, igt_pipe_t *p)
+{
+	igt_plane_t *primary = plane_for_index(p, 0);
+
+	drmModePropertyPtr zpos_prop = NULL;
+
+	if (kmstest_get_property(display->drm_fd,
+				 primary->drm_plane->plane_id,
+				 DRM_MODE_OBJECT_PLANE,
+				 "zpos", NULL, NULL,
+				 &zpos_prop) &&
+	    zpos_prop &&
+	    zpos_prop->flags & DRM_MODE_PROP_RANGE)
+	{
+		return zpos_prop->values[1];
+	}
+	else
+	{
+		return -1;
+	}
+}
+
+size_t get_num_fbs(igt_display_t *display, igt_pipe_t *p)
+{
+	const char *NUM_FBS = getenv("NUM_FBS");
+
+	if (NUM_FBS)
+	{
+		return (size_t)atoi(NUM_FBS);
+	}
+	else
+	{
+		const int num_planes = get_num_planes(display);
+		const int max_zpos = get_max_zpos(display, p);
+
+		if (max_zpos >= 0 && max_zpos + 1 < num_planes)
+		{
+			return (size_t)max_zpos + 1;
+		}
+		else
+		{
+			return (size_t)num_planes;
+		}
+	}
+}
+
+size_t calculate_complexity(const drmModeModeInfoPtr mode)
+{
+	return (size_t)mode->hdisplay * (size_t)mode->vdisplay * (size_t)mode->vrefresh;
+}
+
+drmModeModeInfoPtr get_peak_mode(igt_output_t *output)
+{
+	drmModeConnector *const connector = output->config.connector;
+	if (!connector || connector->count_modes == 0)
+	{
+		return NULL;
+	}
+
+	drmModeModeInfoPtr peak_mode = &connector->modes[0];
+	size_t peak_complexity = calculate_complexity(peak_mode);
+
+	for (drmModeModeInfoPtr mode = &connector->modes[0];
+	     mode < &connector->modes[connector->count_modes]; ++mode)
+	{
+		const size_t complexity = calculate_complexity(mode);
+		igt_debug("Mode %zu is %hux%hu@%u\n",
+			  (size_t)(mode - connector->modes),
+			  mode->hdisplay, mode->vdisplay, mode->vrefresh);
+		if (complexity > peak_complexity)
+		{
+			peak_mode = mode;
+			peak_complexity = complexity;
+		}
+	}
+
+	return peak_mode;
+}
+
+void get_tuning(struct tuning *tuning,
+		igt_display_t *display, igt_pipe_t *p,
+		igt_output_t *output)
+{
+	tuning->mode = get_peak_mode(output);
+	igt_require(tuning->mode);
+
+	tuning->num_iterations = 1000;
+	tuning->num_fb_sets = 2;
+
+	tuning->num_fbs = get_num_fbs(display, p);
+	igt_require(tuning->num_fbs <= max_num_fbs);
+
+	drmModeModeInfo *mode = igt_output_get_mode(output);
+	const char *FB_WIDTH = getenv("FB_WIDTH");
+	const char *FB_HEIGHT = getenv("FB_HEIGHT");
+
+	const size_t requested_fb_width = FB_WIDTH ?
+		(size_t)atoi(FB_WIDTH) :
+		mode->hdisplay;
+
+	const size_t requested_fb_height = FB_HEIGHT ?
+		(size_t)atoi(FB_HEIGHT) :
+		mode->vdisplay;
+
+	igt_display_commit2(p->display, COMMIT_ATOMIC);
+
+	struct igt_fb fb;
+	create_dumb_fb(p->display, requested_fb_width, requested_fb_height, &fb);
+
+	for (size_t i = 0; i < tuning->num_fbs; ++i)
+	{
+		igt_plane_t *const plane = plane_for_index(p, i);
+		igt_plane_set_prop_value(plane, IGT_PLANE_ZPOS, i);
+		igt_plane_set_fb(plane, &fb);
+
+		int ret = igt_display_try_commit_atomic(p->display,
+							DRM_MODE_ATOMIC_TEST_ONLY,
+							NULL);
+
+		if (ret)
+		{
+			tuning->fb_geom[i].width = mode->hdisplay;
+			tuning->fb_geom[i].height = mode->vdisplay;
+		}
+		else
+		{
+			tuning->fb_geom[i].width = requested_fb_width;
+			tuning->fb_geom[i].height = requested_fb_height;
+		}
+
+		igt_info("Plane %zu is %zux%zu\n", i,
+			 tuning->fb_geom[i].width,
+			 tuning->fb_geom[i].height);
+
+		igt_plane_set_fb(plane, NULL);
+	}
+
+	igt_remove_fb(p->display->drm_fd, &fb);
+}
+
+igt_main
+{
+	igt_display_t display = {};
+	make_display(&display);
+
+	igt_pipe_t *p = NULL;
+	igt_output_t *output = NULL;
+	get_pipe(&display, &p, &output);
+
+	do_or_die(drmSetClientCap(
+		display.drm_fd,
+		DRM_CLIENT_CAP_ATOMIC,
+		1));
+
+	do_or_die(drmSetClientCap(
+		display.drm_fd,
+		DRM_CLIENT_CAP_UNIVERSAL_PLANES,
+		1));
+
+	igt_pipe_refresh(&display, p->pipe, true);
+
+	prepare(&display, p, output);
+
+	struct tuning tuning;
+	get_tuning(&tuning, &display, p, output);
+
+	drmModeModeInfoPtr orig_mode = igt_output_get_mode(output);
+	if (orig_mode != tuning.mode)
+	{
+		igt_output_override_mode(output, tuning.mode);
+		igt_display_commit2(&display, COMMIT_ATOMIC);
+	}
+
+	igt_info("Chosen mode:\n");
+	kmstest_dump_mode(tuning.mode);
+
+	{
+		struct igt_fb **fb_sets =
+				malloc(sizeof(struct igt_fb*[tuning.num_fb_sets]));
+
+		for (size_t i = 0; i < tuning.num_fb_sets; ++i)
+		{
+			fb_sets[i] = malloc(sizeof(struct igt_fb[tuning.num_fbs]));
+			struct igt_fb *fbs = fb_sets[i];
+			for (size_t j = 0; j < tuning.num_fbs; ++j)
+			{
+				create_dumb_fb(&display,
+					       tuning.fb_geom[j].width,
+					       tuning.fb_geom[j].height,
+					       &fbs[j]);
+			};
+		}
+
+
+		repeat_flip(p, fb_sets, &tuning);
+
+		for (size_t i = 0; i < tuning.num_fb_sets; ++i)
+		{
+			struct igt_fb *fbs = fb_sets[i];
+			for (size_t j = 0; j < tuning.num_fbs; ++j)
+			{
+				igt_remove_fb(display.drm_fd, &fbs[j]);
+			};
+			free(fbs);
+		}
+		free(fb_sets);
+	}
+
+	if (orig_mode != tuning.mode)
+	{
+		igt_output_override_mode(output, orig_mode);
+		igt_display_commit2(&display, COMMIT_ATOMIC);
+	}
+
+	igt_info("Success\n");
+}
diff --git a/scripts/test_igt_gpu_tools.py b/scripts/test_igt_gpu_tools.py
new file mode 100644
index 0000000..4597836
--- /dev/null
+++ b/scripts/test_igt_gpu_tools.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2020 The Android Open Source Project
+
+# 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.
+
+import os
+import unittest
+from xml.etree import ElementTree
+import subprocess
+import sys
+
+VERBOSE = True
+TEST_CONFIG = os.path.join(os.path.dirname(__file__), "test_igt_gpu_tools.xml")
+
+""" Extracts tests from AndroidTest.xml compatible file
+    param: xmlfile str containing path to AndroidTest.xml compatible xml file
+"""
+def extract_tests_from_xml(xmlfile):
+    tree = ElementTree.parse(xmlfile)
+    root = tree.getroot()
+    return [(o.attrib['key'], o.attrib['value'])
+            for o in root.findall("./target_preparer/option")
+            if o.attrib['name'] == "push-file"]
+
+def run_command(command):
+    serial_number = os.environ.get("ANDROID_SERIAL", "")
+    if not serial_number:
+        raise "$ANDROID_SERIAL is empty, device must be specified"
+    full_command = ["adb", "-s", serial_number, "shell"] + command
+    if VERBOSE:
+        print("+" + " ".join(full_command))
+    ret = subprocess.run(full_command, capture_output=True, universal_newlines=True)
+    if VERBOSE:
+        print(ret.stdout)
+    return ret.stdout
+
+class IGTGpuToolsBinary():
+    """Harness object for a specific IGT GPU test binary. """
+
+    """ param: ondevice_test_path The IGT binary path on device """
+    def __init__(self, ondevice_test_path):
+        self._path = ondevice_test_path
+        subtests = run_command([self._path, "--list-subtests"])
+        self._subtests = list(filter(lambda x: x != "", subtests.split("\n")))
+
+    """lists subtests detected in tests.
+       return: list of strings indicating subtests found in binary.
+    """
+    def subtests(self):
+        return self._subtests;
+
+    """Runs a subtest.
+
+        param: subtest_name One of the subtests listed by self.subtest()
+        return: a subprocess.CompletedProcess
+    """
+    def run_subtest(self, subtest_name):
+        if subtest_name not in self._subtests:
+            error
+        return run_command([self._path, "--run-subtest", subtest_name])
+
+class IGTGpuToolsTest(unittest.TestCase):
+    """Tests for DRM/KMS using Intel Graphics Tests suite"""
+
+    """ param: subtest A valid subtest for the binary
+        param: the IGT binary to run as a unit test.
+    """
+    def __init__(self, subtest, binary):
+        self._subtest_name = subtest
+        self._test_binary = binary
+        setattr(self, self._subtest_name, self.runTest)
+        super(IGTGpuToolsTest, self).__init__(methodName=self._subtest_name)
+
+    """ Runs the test set in the constructor """
+    def runTest(self):
+        output = self._test_binary.run_subtest(self._subtest_name)
+        for line in output.split("\n"):
+            if "Subtest" not in line:
+                continue;
+            if "SKIP" in line:
+                self.skipTest("{} - skipped".format(self._subtest_name))
+                break
+            if "SUCCESS" in line:
+                break
+            if "FAIL" in line:
+                self.fail(output)
+                break
+            self.fail("could not parse test output; test runner failure")
+
+ANDROID_RUNNER_REQUIRED_VERBOSITY = 2
+
+def main():
+    """main entrypoint for test runner"""
+
+    runner = unittest.TextTestRunner(stream=sys.stderr, verbosity=ANDROID_RUNNER_REQUIRED_VERBOSITY)
+    suite = unittest.TestSuite()
+    for test, test_path in extract_tests_from_xml(TEST_CONFIG):
+        print("Found IGT-GPU-tools test {}".format(test))
+        subsuite = unittest.TestSuite()
+        binary = IGTGpuToolsBinary(test_path)
+        for subtest in binary.subtests():
+            print("\tFound subtest {}".format(subtest))
+            subsuite.addTest(IGTGpuToolsTest(subtest, binary))
+        suite.addTest(subsuite)
+    runner.run(suite)
+
+if __name__=="__main__":
+    main()
diff --git a/scripts/test_igt_gpu_tools.xml b/scripts/test_igt_gpu_tools.xml
new file mode 100644
index 0000000..63a5feb
--- /dev/null
+++ b/scripts/test_igt_gpu_tools.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     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.
+-->
+<configuration description="Config to run IGT GPU tests">
+    <option name="logcat-on-failure" value="false" />
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="stop" />
+        <option name="run-command" value="stop $(grep -rn composer /vendor/etc/init | grep service | cut -d ' ' -f 2)" />
+        <option name="teardown-command" value="stop $(grep -rn composer /vendor/etc/init | grep service | cut -d ' ' -f 2)" />
+        <option name="teardown-command" value="start" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true"/>
+        <option name="append-bitness" value="true"/>
+        <option name="push-file"
+            key="kms_atomic" value="/data/local/tmp/kms_atomic"/>
+        <option name="push-file"
+            key="kms_flip" value="/data/local/tmp/kms_flip"/>
+        <option name="push-file"
+            key="kms_vblank" value="/data/local/tmp/kms_vblank"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
+        <option name="par-file-name" value="igt_gpu_tools" />
+        <option name="inject-android-serial" value="true" />
+        <option name="test-timeout" value="30m" />
+    </test>
+</configuration>
diff --git a/tests/kms_flip.c b/tests/kms_flip.c
index 28ecc21..574d038 100755
--- a/tests/kms_flip.c
+++ b/tests/kms_flip.c
@@ -1153,7 +1153,7 @@
 			 * be interrupted with -EINTR, handle this by restarting
 			 * until we poll timeout or success.
 			 */
-			poll_ret = poll(&(struct pollfd){drm_fd, POLLIN}, 1, -1);
+			poll_ret = poll(&(struct pollfd){drm_fd, POLLIN}, 1, 1000);
 
 			if (poll_ret == 1)
 				break;