public inbox for intel-gfx@lists.freedesktop.org
 help / color / mirror / Atom feed
* [RFC] Smattering of selftests
@ 2016-12-07 13:58 Chris Wilson
  2016-12-07 13:58 ` [PATCH 01/16] drm/i915: Provide a hook for selftests Chris Wilson
                   ` (20 more replies)
  0 siblings, 21 replies; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

More changes to GEM are on the cards, so before touching it again, let's
try and nail down how the internals are meant to work. The advantage
of mock testing is that we can write a universal test independent of the
hw (e.g. testing physical object creation) and we can inspect internal
state which should be able to spot subtle bugs easier than mashing the
uabi. The downside to mock testing is that it doubles the upfront cost
of every patch submission (if you change internal state, you're likely
going to upset a test) and adds maintainance burden tracking change to
external API (on the other hand it catches those silent changes that
would lead to broken code).

In addition to mock tests, I thought it would be useful to start writing
a few run-once tests against real hardware. These are not as interesting
as probing uabi (I don't envisage having kms_flip inside the kernel),
but we might like to exercise runtime suspend once upon startup, for
example. Again, we have access to internal state that may prove
impossible to capture even in debugfs.

Is this a totally insane and impractical proposal?
-Chris

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 01/16] drm/i915: Provide a hook for selftests
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-08 10:47   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 02/16] kselftests: Exercise hw-independent mock tests for i915.ko Chris Wilson
                   ` (19 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

Some pieces of code are independent of hardware but are very tricky to
exercise through the normal userspace ABI or via debugfs hooks. Being
able to create mock unit tests and execute them through CI is vital.
Start by adding a central point where we can execute unit tests and
a parameter to enable them. This is disabled by default as the
expectation is that these tests will occasionally explode.

To facilitate integration with igt, any parameter beginning with
i915.igt__ is interpreted as a subtest executable independently via
igt/drv_selftest.

Two classes of selftests are recognised: mock unit tests and integration
tests. Mock unit tests are run as soon as the module is loaded, before
the device is probed. At that point there is no driver instantiated and
all hw interactions must be "mocked". This is very useful for writing
universal tests to exercise code not typically run on a broad range of
architectures. Alternatively, you can hook into the late selftests and
run when the device has been instantiated - hw interactions are real.

v2: Add a macro for compiling conditional code for mock objects inside
real objects.
v3: Differentiate between mock unit tests and late integration test.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> #v1
---
 drivers/gpu/drm/i915/Kconfig.debug         |  15 +++
 drivers/gpu/drm/i915/Makefile              |   1 +
 drivers/gpu/drm/i915/i915_late_selftests.h |  11 ++
 drivers/gpu/drm/i915/i915_mock_selftests.h |  11 ++
 drivers/gpu/drm/i915/i915_params.c         |   8 ++
 drivers/gpu/drm/i915/i915_params.h         |   4 +
 drivers/gpu/drm/i915/i915_pci.c            |  19 +++-
 drivers/gpu/drm/i915/i915_selftest.c       | 173 +++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/i915_selftest.h       |  77 +++++++++++++
 9 files changed, 318 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/i915/i915_late_selftests.h
 create mode 100644 drivers/gpu/drm/i915/i915_mock_selftests.h
 create mode 100644 drivers/gpu/drm/i915/i915_selftest.c
 create mode 100644 drivers/gpu/drm/i915/i915_selftest.h

diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug
index 597648c7a645..76af8774cf70 100644
--- a/drivers/gpu/drm/i915/Kconfig.debug
+++ b/drivers/gpu/drm/i915/Kconfig.debug
@@ -25,6 +25,7 @@ config DRM_I915_DEBUG
         select DRM_VGEM # used by igt/prime_vgem (dmabuf interop checks)
         select DRM_DEBUG_MM if DRM=y
 	select DRM_I915_SW_FENCE_DEBUG_OBJECTS
+	select DRM_I915_SELFTEST
         default n
         help
           Choose this option to turn on extra driver debugging that may affect
@@ -58,3 +59,17 @@ config DRM_I915_SW_FENCE_DEBUG_OBJECTS
           Recommended for driver developers only.
 
           If in doubt, say "N".
+
+config DRM_I915_SELFTEST
+	bool "Enable selftests upon driver load"
+	depends on DRM_I915
+	default n
+	help
+	  Choose this option to allow the driver to perform selftests upon
+	  loading; also requires the i915.selftest=1 module parameter. To
+	  exit the module after running the selftests (i.e. to prevent normal
+	  module initialisation afterwards) use i915.selftest=-1.
+
+	  Recommended for driver developers only.
+
+	  If in doubt, say "N".
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 3c30916727fb..7c3b4f0c836c 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -114,6 +114,7 @@ i915-y += dvo_ch7017.o \
 
 # Post-mortem debug and GPU hang state capture
 i915-$(CONFIG_DRM_I915_CAPTURE_ERROR) += i915_gpu_error.o
+i915-$(CONFIG_DRM_I915_SELFTEST) += i915_selftest.o
 
 # virtual gpu code
 i915-y += i915_vgpu.o
diff --git a/drivers/gpu/drm/i915/i915_late_selftests.h b/drivers/gpu/drm/i915/i915_late_selftests.h
new file mode 100644
index 000000000000..e6645d08d964
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_late_selftests.h
@@ -0,0 +1,11 @@
+/* List each unit test as selftest(name, function)
+ *
+ * The name is used as both an enum and expanded as subtest__name to create
+ * a module parameter. It must be unique and legal for a C identifier.
+ *
+ * The function should be of type int function(void). It may be conditionally
+ * compiled using #if IS_ENABLED(DRM_I915_SELFTEST).
+ *
+ * Tests are executed in reverse order by igt/drv_selftest
+ */
+selftest(sanitycheck, i915_late_sanitycheck) /* keep last */
diff --git a/drivers/gpu/drm/i915/i915_mock_selftests.h b/drivers/gpu/drm/i915/i915_mock_selftests.h
new file mode 100644
index 000000000000..9bead7b496b0
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_mock_selftests.h
@@ -0,0 +1,11 @@
+/* List each unit test as selftest(name, function)
+ *
+ * The name is used as both an enum and expanded as subtest__name to create
+ * a module parameter. It must be unique and legal for a C identifier.
+ *
+ * The function should be of type int function(void). It may be conditionally
+ * compiled using #if IS_ENABLED(DRM_I915_SELFTEST).
+ *
+ * Tests are executed in reverse order by igt/drv_selftest
+ */
+selftest(sanitycheck, i915_mock_sanitycheck) /* keep last */
diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c
index 0e280fbd52f1..6d528972e0d4 100644
--- a/drivers/gpu/drm/i915/i915_params.c
+++ b/drivers/gpu/drm/i915/i915_params.c
@@ -243,3 +243,11 @@ MODULE_PARM_DESC(enable_dpcd_backlight,
 module_param_named(enable_gvt, i915.enable_gvt, bool, 0400);
 MODULE_PARM_DESC(enable_gvt,
 	"Enable support for Intel GVT-g graphics virtualization host support(default:false)");
+
+#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+module_param_named_unsafe(mock_selftests, i915.mock_selftests, int, 0400);
+MODULE_PARM_DESC(selftest, "Run selftests before loading (0:disabled [default], 1:run tests then load driver, -1:run tests then exit module)");
+
+module_param_named_unsafe(late_selftests, i915.late_selftests, int, 0400);
+MODULE_PARM_DESC(selftest, "Run selftests after driver initialisation (0:disabled [default], 1:run tests then continue, -1:run tests then exit module)");
+#endif
diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h
index 8e433de04679..76a1517bd244 100644
--- a/drivers/gpu/drm/i915/i915_params.h
+++ b/drivers/gpu/drm/i915/i915_params.h
@@ -27,7 +27,11 @@
 
 #include <linux/cache.h> /* for __read_mostly */
 
+#include "i915_selftest.h"
+
 struct i915_params {
+	I915_SELFTEST_DECLARE(int mock_selftests);
+	I915_SELFTEST_DECLARE(int late_selftests);
 	int modeset;
 	int panel_ignore_lid;
 	int semaphores;
diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
index f7ec6e944e09..efed080f4b1d 100644
--- a/drivers/gpu/drm/i915/i915_pci.c
+++ b/drivers/gpu/drm/i915/i915_pci.c
@@ -27,6 +27,7 @@
 #include <linux/vga_switcheroo.h>
 
 #include "i915_drv.h"
+#include "i915_selftest.h"
 
 #define GEN_DEFAULT_PIPEOFFSETS \
 	.pipe_offsets = { PIPE_A_OFFSET, PIPE_B_OFFSET, \
@@ -475,6 +476,7 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	struct intel_device_info *intel_info =
 		(struct intel_device_info *) ent->driver_data;
+	int err;
 
 	if (IS_ALPHA_SUPPORT(intel_info) && !i915.alpha_support) {
 		DRM_INFO("The driver support for your hardware in this kernel version is alpha quality\n"
@@ -498,7 +500,17 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	if (vga_switcheroo_client_probe_defer(pdev))
 		return -EPROBE_DEFER;
 
-	return i915_driver_load(pdev, ent);
+	err = i915_driver_load(pdev, ent);
+	if (err)
+		return err;
+
+	err = i915_late_selftests(pdev);
+	if (err) {
+		i915_driver_unload(pci_get_drvdata(pdev));
+		return err > 0 ? -ENOTTY : err;
+	}
+
+	return 0;
 }
 
 static void i915_pci_remove(struct pci_dev *pdev)
@@ -520,6 +532,11 @@ static struct pci_driver i915_pci_driver = {
 static int __init i915_init(void)
 {
 	bool use_kms = true;
+	int err;
+
+	err = i915_mock_selftests();
+	if (err)
+		return err > 0 ? 0 : err;
 
 	/*
 	 * Enable KMS by default, unless explicitly overriden by
diff --git a/drivers/gpu/drm/i915/i915_selftest.c b/drivers/gpu/drm/i915/i915_selftest.c
new file mode 100644
index 000000000000..8b1b3c76855c
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_selftest.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * 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 "i915_drv.h"
+#include "i915_selftest.h"
+
+int i915_mock_sanitycheck(void)
+{
+	pr_info("i915: %s() - ok!\n", __func__);
+	return 0;
+}
+
+int i915_late_sanitycheck(struct drm_i915_private *i915)
+{
+	pr_info("%s: %s() - ok!\n", i915->drm.driver->name, __func__);
+	return 0;
+}
+
+enum {
+#define selftest(name, func) mock_##name,
+#include "i915_mock_selftests.h"
+#undef selftest
+};
+enum {
+#define selftest(name, func) late_##name,
+#include "i915_late_selftests.h"
+#undef selftest
+};
+
+struct i915_selftest {
+	bool enabled;
+	const char *name;
+	union {
+		int (*mock)(void);
+		int (*late)(struct drm_i915_private *);
+	};
+};
+
+#define selftest(n, f) [mock_##n] = { .name = #n, .mock = f },
+static struct i915_selftest mock_selftests[] = {
+#include "i915_mock_selftests.h"
+};
+#undef selftest
+
+#define selftest(n, f) [late_##n] = { .name = #n, .late = f },
+static struct i915_selftest late_selftests[] = {
+#include "i915_late_selftests.h"
+};
+#undef selftest
+
+#define selftest(n, func) \
+module_param_named(igt__mock_##n, mock_selftests[mock_##n].enabled, bool, 0400);
+#include "i915_mock_selftests.h"
+#undef selftest
+
+#define selftest(n, func) \
+module_param_named(igt__late_##n, late_selftests[late_##n].enabled, bool, 0400);
+#include "i915_late_selftests.h"
+#undef selftest
+
+static void set_default_test_all(struct i915_selftest *st, unsigned long count)
+{
+	unsigned long i;
+
+	for (i = 0; i < count; i++)
+		if (st[i].enabled)
+			return;
+
+	for (i = 0; i < count; i++)
+		st[i].enabled = true;
+}
+
+static int run_selftests(struct i915_selftest *st,
+			 unsigned long count,
+			 void *data)
+{
+	int err;
+
+	set_default_test_all(st, count);
+
+	for (; count--; st++) {
+		if (!st->enabled)
+			continue;
+
+		pr_debug("i915: Running %s\n", st->name);
+		if (data)
+			err = st->late(data);
+		else
+			err = st->mock();
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+int i915_mock_selftests(void)
+{
+	int err;
+
+	if (!i915.mock_selftests)
+		return 0;
+
+	pr_info("i915: Performing mock selftests\n");
+	err = run_selftests(mock_selftests, ARRAY_SIZE(mock_selftests), NULL);
+	if (err)
+		return err;
+
+	if (i915.mock_selftests < 0)
+		return 1;
+
+	return 0;
+}
+
+int i915_late_selftests(struct pci_dev *pdev)
+{
+	int err;
+
+	if (!i915.late_selftests)
+		return 0;
+
+	pr_info("i915: Performing late selftests\n");
+	err = run_selftests(late_selftests,
+			    ARRAY_SIZE(late_selftests),
+			    pci_get_drvdata(pdev));
+	if (err)
+		return err;
+
+	if (i915.late_selftests < 0)
+		return 1;
+
+	return 0;
+}
+
+int __i915_subtests(const char *caller,
+		    const struct i915_subtest *st,
+		    int count,
+		    void *data)
+{
+	int err;
+
+	for (; count--; st++) {
+		pr_debug("i915: Running %s/%s\n", caller, st->name);
+		err = st->func(data);
+		if (err) {
+			pr_err("i915/%s: %s failed with error %d\n",
+			       caller, st->name, err);
+			return err;
+		}
+	}
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/i915/i915_selftest.h b/drivers/gpu/drm/i915/i915_selftest.h
new file mode 100644
index 000000000000..86876ba52d5e
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_selftest.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * 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 __I915_SELFTEST_H__
+#define __I915_SELFTEST_H__
+
+struct pci_dev;
+struct drm_i915_private;
+
+#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+int i915_mock_selftests(void);
+int i915_late_selftests(struct pci_dev *pdev);
+#else
+static inline int i915_mock_selftests(void) { return 0; }
+static inline int i915_late_selftests(struct pci_dev *pdev) { return 0; }
+#endif
+
+/* We extract the function declarations from i915_mock_selftests.h and
+ * i915_late_selftests.h Add your unit test declarations there!
+ *
+ * Mock unit tests are run very early upon module load, before the driver
+ * is probed. All hardware interactions, as well as other subsystems, must
+ * be "mocked".
+ *
+ * Late unit tests are run after the driver is loaded - all hardware
+ * interactions are real.
+ */
+#define selftest(name, func) int func(void);
+#include "i915_mock_selftests.h"
+#undef selftest
+#define selftest(name, func) int func(struct drm_i915_private *i915);
+#include "i915_late_selftests.h"
+#undef selftest
+
+struct i915_subtest {
+	int (*func)(void *data);
+	const char *name;
+};
+
+int __i915_subtests(const char *caller,
+		    const struct i915_subtest *st,
+		    int count,
+		    void *data);
+#define i915_subtests(T, data) \
+	__i915_subtests(__func__, T, ARRAY_SIZE(T), data)
+
+#define SUBTEST(x) { x, #x }
+
+#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+#define I915_SELFTEST_DECLARE(x) x
+#define I915_SELFTEST_ONLY(x) unlikely(x)
+#else
+#define I915_SELFTEST_DECLARE(x)
+#define I915_SELFTEST_ONLY(x) 0
+#endif
+
+#endif /* __I915_SELFTEST_H__ */
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 02/16] kselftests: Exercise hw-independent mock tests for i915.ko
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
  2016-12-07 13:58 ` [PATCH 01/16] drm/i915: Provide a hook for selftests Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-07 14:09   ` Chris Wilson
  2016-12-07 13:58 ` [PATCH 03/16] drm/i915: Add unit tests for the breadcrumb rbtree, insert/remove Chris Wilson
                   ` (18 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx; +Cc: Shuah Khan, linux-kselftest

Although being a GPU driver most functionality of i915.ko depends upon
real hardware, many of its internal interfaces can be "mocked" and so
tested independently of any hardware. Expanding the test coverage is not
only useful for i915.ko, but should provide some integration tests for
core infrastructure as well.

Loading i915.ko with mock_selftests=-1 will cause it to execute its mock
tests then fail with -ENOTTY. If the driver is already loaded and bound
to hardware, it requires a few more steps to unbind, and so the simple
preliminary modprobe -r will fail.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Shuah Khan <shuah@kernel.org>
Cc: linux-kselftest@vger.kernel.org
---
 tools/testing/selftests/drivers/gpu/i915.sh | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100755 tools/testing/selftests/drivers/gpu/i915.sh

diff --git a/tools/testing/selftests/drivers/gpu/i915.sh b/tools/testing/selftests/drivers/gpu/i915.sh
new file mode 100755
index 000000000000..d407f0fa1e3a
--- /dev/null
+++ b/tools/testing/selftests/drivers/gpu/i915.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+# Runs hardware independent tests for i915 (drivers/gpu/drm/i915)
+
+if ! /sbin/modprobe -q -r i915; then
+	echo "drivers/gpu/i915: [SKIP]"
+	exit 77
+fi
+
+if /sbin/modprobe -q i915 mock_selftests=-1; then
+	echo "drivers/gpu/i915: ok"
+else
+	echo "drivers/gpu/i915: [FAIL]"
+	exit 1
+fi
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 03/16] drm/i915: Add unit tests for the breadcrumb rbtree, insert/remove
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
  2016-12-07 13:58 ` [PATCH 01/16] drm/i915: Provide a hook for selftests Chris Wilson
  2016-12-07 13:58 ` [PATCH 02/16] kselftests: Exercise hw-independent mock tests for i915.ko Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-08 11:00   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 04/16] drm/i915: Add unit tests for the breadcrumb rbtree, completion Chris Wilson
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

First retroactive test, make sure that the waiters are in global seqno
order after random inserts and removals.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_mock_selftests.h |   1 +
 drivers/gpu/drm/i915/intel_breadcrumbs.c   | 205 +++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_ringbuffer.h    |   2 +
 3 files changed, 208 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_mock_selftests.h b/drivers/gpu/drm/i915/i915_mock_selftests.h
index 9bead7b496b0..1603fd35d190 100644
--- a/drivers/gpu/drm/i915/i915_mock_selftests.h
+++ b/drivers/gpu/drm/i915/i915_mock_selftests.h
@@ -8,4 +8,5 @@
  *
  * Tests are executed in reverse order by igt/drv_selftest
  */
+selftest(breadcrumbs, intel_breadcrumbs_selftest)
 selftest(sanitycheck, i915_mock_sanitycheck) /* keep last */
diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c
index 53ae7884babd..c768608974e1 100644
--- a/drivers/gpu/drm/i915/intel_breadcrumbs.c
+++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c
@@ -109,6 +109,18 @@ static void __intel_breadcrumbs_enable_irq(struct intel_breadcrumbs *b)
 	if (b->rpm_wakelock)
 		return;
 
+	if (I915_SELFTEST_ONLY(b->mock)) {
+		/* For our mock objects we want to avoid interaction
+		 * with the real hardware (which is not set up). So
+		 * we simply pretend we have enabled the powerwell
+		 * and the irq, and leave it up to the mock
+		 * implementation to call intel_engine_wakeup()
+		 * itself when it wants to simulate a user interrupt,
+		 */
+		b->rpm_wakelock = true;
+		return;
+	}
+
 	/* Since we are waiting on a request, the GPU should be busy
 	 * and should have its own rpm reference. For completeness,
 	 * record an rpm reference for ourselves to cover the
@@ -143,6 +155,11 @@ static void __intel_breadcrumbs_disable_irq(struct intel_breadcrumbs *b)
 	if (!b->rpm_wakelock)
 		return;
 
+	if (I915_SELFTEST_ONLY(b->mock)) {
+		b->rpm_wakelock = false;
+		return;
+	}
+
 	if (b->irq_enabled) {
 		irq_disable(engine);
 		b->irq_enabled = false;
@@ -661,3 +678,191 @@ unsigned int intel_breadcrumbs_busy(struct drm_i915_private *i915)
 
 	return mask;
 }
+
+#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+#include <linux/random.h>
+
+#include "i915_selftest.h"
+
+static struct intel_engine_cs *mock_engine(const char *name)
+{
+	struct intel_engine_cs *engine;
+	static int id;
+
+	engine = kzalloc(sizeof(*engine) + 4096, GFP_TEMPORARY);
+	if (!engine)
+		return NULL;
+
+	/* minimal engine setup for seqno */
+	engine->name = name;
+	engine->id = id++;
+	engine->status_page.page_addr = (void *)(engine + 1);
+
+	/* minimal breadcrumbs init */
+	spin_lock_init(&engine->breadcrumbs.lock);
+	engine->breadcrumbs.mock = true;
+
+	return engine;
+}
+
+static int *get_random_order(int count)
+{
+	int *order;
+	int n, r, tmp;
+
+	order = kmalloc_array(count, sizeof(*order), GFP_TEMPORARY);
+	if (!order)
+		return order;
+
+	for (n = 0; n < count; n++)
+		order[n] = n;
+
+	for (n = count - 1; n > 1; n--) {
+		r = get_random_int() % (n + 1);
+		if (r != n) {
+			tmp = order[n];
+			order[n] = order[r];
+			order[r] = tmp;
+		}
+	}
+
+	return order;
+}
+
+static int check_rbtree(struct intel_engine_cs *engine,
+			const unsigned long *bitmap,
+			const struct intel_wait *waiters,
+			const int count)
+{
+	struct intel_breadcrumbs *b = &engine->breadcrumbs;
+	struct rb_node *rb;
+	int n;
+
+	if (&b->first_wait->node != rb_first(&b->waiters)) {
+		pr_err("First waiter does not match first element of wait-tree\n");
+		return -EINVAL;
+	}
+
+	n = find_first_bit(bitmap, count);
+	for (rb = rb_first(&b->waiters); rb; rb = rb_next(rb)) {
+		struct intel_wait *w = container_of(rb, typeof(*w), node);
+		int idx = w - waiters;
+
+		if (!test_bit(idx, bitmap)) {
+			pr_err("waiter[%d, seqno=%d] removed but still in wait-tree\n",
+			       idx, w->seqno);
+			return -EINVAL;
+		}
+
+		if (n != idx) {
+			pr_err("waiter[%d, seqno=%d] does not match expected next element in tree [%d]\n",
+			       idx, w->seqno, n);
+			return -EINVAL;
+		}
+
+		n = find_next_bit(bitmap, count, n + 1);
+	}
+
+	return 0;
+}
+
+static int check_rbtree_empty(struct intel_engine_cs *engine)
+{
+	struct intel_breadcrumbs *b = &engine->breadcrumbs;
+
+	if (b->first_wait) {
+		pr_err("Empty breadcrumbs still has a waiter\n");
+		return -EINVAL;
+	}
+
+	if (!RB_EMPTY_ROOT(&b->waiters)) {
+		pr_err("Empty breadcrumbs, but wait-tree not empty\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int igt_random_insert_remove(void *ignore)
+{
+	struct intel_engine_cs *engine;
+	struct intel_wait *waiters;
+	const int count = 4096;
+	int *in_order, *out_order;
+	unsigned long *bitmap;
+	int err = -ENOMEM;
+	int n;
+
+	engine = mock_engine("mock");
+	if (!engine)
+		goto out;
+
+	waiters = drm_malloc_gfp(count, sizeof(*waiters), GFP_TEMPORARY);
+	if (!waiters)
+		goto out_engines;
+
+	bitmap = kcalloc(DIV_ROUND_UP(count, BITS_PER_LONG), sizeof(*bitmap),
+			 GFP_TEMPORARY);
+	if (!bitmap)
+		goto out_waiters;
+
+	in_order = get_random_order(count);
+	if (!in_order)
+		goto out_bitmap;
+
+	out_order = get_random_order(count);
+	if (!out_order)
+		goto out_order;
+
+	for (n = 0; n < count; n++)
+		intel_wait_init(&waiters[n], 0x1000 + n);
+
+	err = check_rbtree(engine, bitmap, waiters, count);
+	if (err)
+		goto err;
+
+	for (n = 0; n < count; n++) {
+		int i = in_order[n];
+
+		intel_engine_add_wait(engine, &waiters[i]);
+		__set_bit(i, bitmap);
+
+		err = check_rbtree(engine, bitmap, waiters, count);
+		if (err)
+			goto err;
+	}
+	for (n = 0; n < count; n++) {
+		int i = out_order[n];
+
+		intel_engine_remove_wait(engine, &waiters[i]);
+		__clear_bit(i, bitmap);
+
+		err = check_rbtree(engine, bitmap, waiters, count);
+		if (err)
+			goto err;
+	}
+
+	err = check_rbtree_empty(engine);
+err:
+	kfree(out_order);
+out_order:
+	kfree(in_order);
+out_bitmap:
+	kfree(bitmap);
+out_waiters:
+	drm_free_large(waiters);
+out_engines:
+	kfree(engine);
+out:
+	return err;
+}
+
+int intel_breadcrumbs_selftest(void)
+{
+	static const struct i915_subtest tests[] = {
+		SUBTEST(igt_random_insert_remove),
+	};
+
+	return i915_subtests(tests, NULL);
+}
+#endif
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 3f43adefd1c0..d8b066fd5dcf 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -5,6 +5,7 @@
 #include "i915_gem_batch_pool.h"
 #include "i915_gem_request.h"
 #include "i915_gem_timeline.h"
+#include "i915_selftest.h"
 
 #define I915_CMD_HASH_ORDER 9
 
@@ -244,6 +245,7 @@ struct intel_engine_cs {
 
 		bool irq_enabled : 1;
 		bool rpm_wakelock : 1;
+		I915_SELFTEST_DECLARE(bool mock : 1);
 	} breadcrumbs;
 
 	/*
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 04/16] drm/i915: Add unit tests for the breadcrumb rbtree, completion
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (2 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 03/16] drm/i915: Add unit tests for the breadcrumb rbtree, insert/remove Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-08 11:47   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 05/16] drm/i915: Add unit tests for the breadcrumb rbtree, wakeups Chris Wilson
                   ` (16 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

Second retroactive test, make sure that the waiters are removed from the
global wait-tree when their seqno completes.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/intel_breadcrumbs.c | 110 +++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)

diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c
index c768608974e1..fc950f7ff322 100644
--- a/drivers/gpu/drm/i915/intel_breadcrumbs.c
+++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c
@@ -705,6 +705,12 @@ static struct intel_engine_cs *mock_engine(const char *name)
 	return engine;
 }
 
+static void mock_seqno_advance(struct intel_engine_cs *engine, u32 seqno)
+{
+	intel_write_status_page(engine, I915_GEM_HWS_INDEX, seqno);
+	intel_engine_wakeup(engine);
+}
+
 static int *get_random_order(int count)
 {
 	int *order;
@@ -766,6 +772,27 @@ static int check_rbtree(struct intel_engine_cs *engine,
 	return 0;
 }
 
+static int check_completion(struct intel_engine_cs *engine,
+			    const unsigned long *bitmap,
+			    const struct intel_wait *waiters,
+			    const int count)
+{
+	int n;
+
+	for (n = 0; n < count; n++) {
+		if (intel_wait_complete(&waiters[n]) != !!test_bit(n, bitmap))
+			continue;
+
+		pr_err("waiter[%d, seqno=%d] is %s, but expected %s\n",
+		       n, waiters[n].seqno,
+		       intel_wait_complete(&waiters[n]) ? "complete" : "active",
+		       test_bit(n, bitmap) ? "active" : "complete");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int check_rbtree_empty(struct intel_engine_cs *engine)
 {
 	struct intel_breadcrumbs *b = &engine->breadcrumbs;
@@ -857,10 +884,93 @@ static int igt_random_insert_remove(void *ignore)
 	return err;
 }
 
+static int igt_insert_complete(void *ignore)
+{
+	struct intel_engine_cs *engine;
+	struct intel_wait *waiters;
+	const int count = 4096;
+	unsigned long *bitmap;
+	int err = -ENOMEM;
+	int n, m;
+
+	engine = mock_engine("mock");
+	if (!engine)
+		goto out;
+
+	waiters = drm_malloc_gfp(count, sizeof(*waiters), GFP_TEMPORARY);
+	if (!waiters)
+		goto out_engines;
+
+	bitmap = kcalloc(DIV_ROUND_UP(count, BITS_PER_LONG), sizeof(*bitmap),
+			 GFP_TEMPORARY);
+	if (!bitmap)
+		goto out_waiters;
+
+	for (n = 0; n < count; n++) {
+		intel_wait_init(&waiters[n], n + 0x1000);
+		intel_engine_add_wait(engine, &waiters[n]);
+		__set_bit(n, bitmap);
+	}
+	err = check_rbtree(engine, bitmap, waiters, count);
+	if (err)
+		goto err;
+
+	for (n = 0; n < count; n = m) {
+		int seqno = 2 * n;
+
+		GEM_BUG_ON(find_first_bit(bitmap, count) != n);
+
+		if (intel_wait_complete(&waiters[n])) {
+			pr_err("waiter[%d, seqno=%d] completed too early\n",
+			       n, waiters[n].seqno);
+			err = -EINVAL;
+			goto err;
+		}
+
+		/* complete the following waiters */
+		mock_seqno_advance(engine, seqno + 0x1000);
+		for (m = n; m <= seqno; m++) {
+			if (m == count)
+				break;
+
+			GEM_BUG_ON(!test_bit(m, bitmap));
+			__clear_bit(m, bitmap);
+		}
+
+		intel_engine_remove_wait(engine, &waiters[n]);
+		RB_CLEAR_NODE(&waiters[n].node);
+
+		err = check_rbtree(engine, bitmap, waiters, count);
+		if (err) {
+			pr_err("rbtree corrupt after seqno advance to %d\n",
+			       seqno + 0x1000);
+			goto err;
+		}
+
+		err = check_completion(engine, bitmap, waiters, count);
+		if (err) {
+			pr_err("completions after seqno advance to %d failed\n",
+			       seqno + 0x1000);
+			goto err;
+		}
+	}
+
+	err = check_rbtree_empty(engine);
+err:
+	kfree(bitmap);
+out_waiters:
+	drm_free_large(waiters);
+out_engines:
+	kfree(engine);
+out:
+	return err;
+}
+
 int intel_breadcrumbs_selftest(void)
 {
 	static const struct i915_subtest tests[] = {
 		SUBTEST(igt_random_insert_remove),
+		SUBTEST(igt_insert_complete),
 	};
 
 	return i915_subtests(tests, NULL);
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 05/16] drm/i915: Add unit tests for the breadcrumb rbtree, wakeups
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (3 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 04/16] drm/i915: Add unit tests for the breadcrumb rbtree, completion Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-08 17:38   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 06/16] drm/i915: Add a reminder that i915_vma_move_to_active() requires struct_mutex Chris Wilson
                   ` (15 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

Third retroactive test, make sure that the seqno waiters are woken.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/intel_breadcrumbs.c | 171 +++++++++++++++++++++++++++++++
 1 file changed, 171 insertions(+)

diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c
index fc950f7ff322..1374a54e41c9 100644
--- a/drivers/gpu/drm/i915/intel_breadcrumbs.c
+++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c
@@ -966,11 +966,182 @@ static int igt_insert_complete(void *ignore)
 	return err;
 }
 
+struct igt_wakeup {
+	struct task_struct *tsk;
+	atomic_t *ready, *set, *done;
+	struct intel_engine_cs *engine;
+	unsigned long flags;
+	wait_queue_head_t *wq;
+	u32 seqno;
+};
+
+static int wait_atomic(atomic_t *p)
+{
+	schedule();
+	return 0;
+}
+
+static int wait_atomic_timeout(atomic_t *p)
+{
+	return schedule_timeout(10 * HZ) ? 0 : -ETIMEDOUT;
+}
+
+static int igt_wakeup_thread(void *arg)
+{
+	struct igt_wakeup *w = arg;
+	struct intel_wait wait;
+
+	while (!kthread_should_stop()) {
+		DEFINE_WAIT(ready);
+
+		for (;;) {
+			prepare_to_wait(w->wq, &ready, TASK_INTERRUPTIBLE);
+			if (atomic_read(w->ready) == 0)
+				break;
+
+			schedule();
+		}
+		finish_wait(w->wq, &ready);
+		if (atomic_dec_and_test(w->set))
+			wake_up_atomic_t(w->set);
+
+		if (test_bit(0, &w->flags))
+			break;
+
+		intel_wait_init(&wait, w->seqno);
+		intel_engine_add_wait(w->engine, &wait);
+		for (;;) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			if (i915_seqno_passed(intel_engine_get_seqno(w->engine),
+					      w->seqno))
+				break;
+
+			schedule();
+		}
+		intel_engine_remove_wait(w->engine, &wait);
+		__set_current_state(TASK_RUNNING);
+
+		if (atomic_dec_and_test(w->done))
+			wake_up_atomic_t(w->done);
+	}
+
+	if (atomic_dec_and_test(w->done))
+		wake_up_atomic_t(w->done);
+	return 0;
+}
+
+static void igt_wake_all_sync(atomic_t *ready,
+			      atomic_t *set,
+			      atomic_t *done,
+			      wait_queue_head_t *wq,
+			      int count)
+{
+	atomic_set(set, count);
+	atomic_set(done, count);
+
+	atomic_set(ready, 0);
+	wake_up_all(wq);
+
+	wait_on_atomic_t(set, wait_atomic, TASK_UNINTERRUPTIBLE);
+	atomic_set(ready, count);
+}
+
+static int igt_wakeup(void *ignore)
+{
+	const int state = TASK_UNINTERRUPTIBLE;
+	struct intel_engine_cs *engine;
+	struct igt_wakeup *waiters;
+	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
+	const int count = 4096;
+	const u32 max_seqno = count / 4;
+	atomic_t ready, set, done;
+	int err = -ENOMEM;
+	int n, step;
+
+	engine = mock_engine("mock");
+	if (!engine)
+		goto out;
+
+	waiters = drm_malloc_gfp(count, sizeof(*waiters), GFP_TEMPORARY);
+	if (!waiters)
+		goto out_engines;
+
+	atomic_set(&ready, count);
+	for (n = 0; n < count; n++) {
+		waiters[n].wq = &wq;
+		waiters[n].ready = &ready;
+		waiters[n].set = &set;
+		waiters[n].done = &done;
+		waiters[n].engine = engine;
+		waiters[n].flags = 0;
+
+		waiters[n].tsk = kthread_run(igt_wakeup_thread, &waiters[n],
+					     "i915/igt:%d", n);
+		if (IS_ERR(waiters[n].tsk))
+			goto out_waiters;
+
+		get_task_struct(waiters[n].tsk);
+	}
+
+	for (step = 1; step <= max_seqno; step <<= 1) {
+		u32 seqno;
+
+		for (n = 0; n < count; n++)
+			waiters[n].seqno = 1 + get_random_int() % max_seqno;
+
+		mock_seqno_advance(engine, 0);
+		igt_wake_all_sync(&ready, &set, &done, &wq, count);
+
+		for (seqno = 1; seqno <= max_seqno + step; seqno += step) {
+			usleep_range(50, 500);
+			mock_seqno_advance(engine, seqno);
+		}
+		GEM_BUG_ON(intel_engine_get_seqno(engine) < 1 + max_seqno);
+
+		err = wait_on_atomic_t(&done, wait_atomic_timeout, state);
+		if (err) {
+			pr_err("Timed out waiting for %d remaining waiters\n",
+			       atomic_read(&done));
+			break;
+		}
+
+		err = check_rbtree_empty(engine);
+		if (err)
+			break;
+	}
+
+out_waiters:
+	for (n = 0; n < count; n++) {
+		if (IS_ERR(waiters[n].tsk))
+			break;
+
+		set_bit(0, &waiters[n].flags);
+	}
+
+	igt_wake_all_sync(&ready, &set, &done, &wq, n);
+	wait_on_atomic_t(&done, wait_atomic, state);
+
+	for (n = 0; n < count; n++) {
+		if (IS_ERR(waiters[n].tsk))
+			break;
+
+		kthread_stop(waiters[n].tsk);
+		put_task_struct(waiters[n].tsk);
+	}
+
+	drm_free_large(waiters);
+out_engines:
+	kfree(engine);
+out:
+	return err;
+}
+
 int intel_breadcrumbs_selftest(void)
 {
 	static const struct i915_subtest tests[] = {
 		SUBTEST(igt_random_insert_remove),
 		SUBTEST(igt_insert_complete),
+		SUBTEST(igt_wakeup),
 	};
 
 	return i915_subtests(tests, NULL);
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 06/16] drm/i915: Add a reminder that i915_vma_move_to_active() requires struct_mutex
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (4 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 05/16] drm/i915: Add unit tests for the breadcrumb rbtree, wakeups Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-08 17:40   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 07/16] drm/i915: Move intel_lrc_context_pin() to avoid the forward declaration Chris Wilson
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

i915_vma_move_to_active() requires the struct_mutex for serialisation
with retirement, so mark it up with lockdep_assert_held().

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_gem_execbuffer.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index d665a33229bd..c64438f8171c 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -1259,6 +1259,7 @@ void i915_vma_move_to_active(struct i915_vma *vma,
 	struct drm_i915_gem_object *obj = vma->obj;
 	const unsigned int idx = req->engine->id;
 
+	lockdep_assert_held(&req->i915->drm.struct_mutex);
 	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 
 	/* Add a reference if we're newly entering the active list.
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 07/16] drm/i915: Move intel_lrc_context_pin() to avoid the forward declaration
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (5 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 06/16] drm/i915: Add a reminder that i915_vma_move_to_active() requires struct_mutex Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-08 17:45   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 08/16] drm/i915: Unify active context tracking between legacy/execlists/guc Chris Wilson
                   ` (13 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

Just a simple move to avoid a forward declaration.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/intel_lrc.c | 132 +++++++++++++++++++--------------------
 1 file changed, 65 insertions(+), 67 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 8b412880e88c..5cabe4e9d22f 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -230,8 +230,6 @@ enum {
 
 static int execlists_context_deferred_alloc(struct i915_gem_context *ctx,
 					    struct intel_engine_cs *engine);
-static int intel_lr_context_pin(struct i915_gem_context *ctx,
-				struct intel_engine_cs *engine);
 static void execlists_init_reg_state(u32 *reg_state,
 				     struct i915_gem_context *ctx,
 				     struct intel_engine_cs *engine,
@@ -774,71 +772,6 @@ static void execlists_schedule(struct drm_i915_gem_request *request, int prio)
 	/* XXX Do we need to preempt to make room for us and our deps? */
 }
 
-int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request)
-{
-	struct intel_engine_cs *engine = request->engine;
-	struct intel_context *ce = &request->ctx->engine[engine->id];
-	int ret;
-
-	/* Flush enough space to reduce the likelihood of waiting after
-	 * we start building the request - in which case we will just
-	 * have to repeat work.
-	 */
-	request->reserved_space += EXECLISTS_REQUEST_SIZE;
-
-	if (!ce->state) {
-		ret = execlists_context_deferred_alloc(request->ctx, engine);
-		if (ret)
-			return ret;
-	}
-
-	request->ring = ce->ring;
-
-	ret = intel_lr_context_pin(request->ctx, engine);
-	if (ret)
-		return ret;
-
-	if (i915.enable_guc_submission) {
-		/*
-		 * Check that the GuC has space for the request before
-		 * going any further, as the i915_add_request() call
-		 * later on mustn't fail ...
-		 */
-		ret = i915_guc_wq_reserve(request);
-		if (ret)
-			goto err_unpin;
-	}
-
-	ret = intel_ring_begin(request, 0);
-	if (ret)
-		goto err_unreserve;
-
-	if (!ce->initialised) {
-		ret = engine->init_context(request);
-		if (ret)
-			goto err_unreserve;
-
-		ce->initialised = true;
-	}
-
-	/* Note that after this point, we have committed to using
-	 * this request as it is being used to both track the
-	 * state of engine initialisation and liveness of the
-	 * golden renderstate above. Think twice before you try
-	 * to cancel/unwind this request now.
-	 */
-
-	request->reserved_space -= EXECLISTS_REQUEST_SIZE;
-	return 0;
-
-err_unreserve:
-	if (i915.enable_guc_submission)
-		i915_guc_wq_unreserve(request);
-err_unpin:
-	intel_lr_context_unpin(request->ctx, engine);
-	return ret;
-}
-
 static int intel_lr_context_pin(struct i915_gem_context *ctx,
 				struct intel_engine_cs *engine)
 {
@@ -911,6 +844,71 @@ void intel_lr_context_unpin(struct i915_gem_context *ctx,
 	i915_gem_context_put(ctx);
 }
 
+int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request)
+{
+	struct intel_engine_cs *engine = request->engine;
+	struct intel_context *ce = &request->ctx->engine[engine->id];
+	int ret;
+
+	/* Flush enough space to reduce the likelihood of waiting after
+	 * we start building the request - in which case we will just
+	 * have to repeat work.
+	 */
+	request->reserved_space += EXECLISTS_REQUEST_SIZE;
+
+	if (!ce->state) {
+		ret = execlists_context_deferred_alloc(request->ctx, engine);
+		if (ret)
+			return ret;
+	}
+
+	request->ring = ce->ring;
+
+	ret = intel_lr_context_pin(request->ctx, engine);
+	if (ret)
+		return ret;
+
+	if (i915.enable_guc_submission) {
+		/*
+		 * Check that the GuC has space for the request before
+		 * going any further, as the i915_add_request() call
+		 * later on mustn't fail ...
+		 */
+		ret = i915_guc_wq_reserve(request);
+		if (ret)
+			goto err_unpin;
+	}
+
+	ret = intel_ring_begin(request, 0);
+	if (ret)
+		goto err_unreserve;
+
+	if (!ce->initialised) {
+		ret = engine->init_context(request);
+		if (ret)
+			goto err_unreserve;
+
+		ce->initialised = true;
+	}
+
+	/* Note that after this point, we have committed to using
+	 * this request as it is being used to both track the
+	 * state of engine initialisation and liveness of the
+	 * golden renderstate above. Think twice before you try
+	 * to cancel/unwind this request now.
+	 */
+
+	request->reserved_space -= EXECLISTS_REQUEST_SIZE;
+	return 0;
+
+err_unreserve:
+	if (i915.enable_guc_submission)
+		i915_guc_wq_unreserve(request);
+err_unpin:
+	intel_lr_context_unpin(request->ctx, engine);
+	return ret;
+}
+
 static int intel_logical_ring_workarounds_emit(struct drm_i915_gem_request *req)
 {
 	int ret, i;
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 08/16] drm/i915: Unify active context tracking between legacy/execlists/guc
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (6 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 07/16] drm/i915: Move intel_lrc_context_pin() to avoid the forward declaration Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-09 11:48   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 09/16] drm/i915: Simplify releasing context reference Chris Wilson
                   ` (12 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

The requests conversion introduced a nasty bug where we could generate a
new request in the middle of constructing a request. The new request
would be executed (and waited upon) before the current one, creating a
minor havoc in the seqno accounting. (Prior to deferred seqno
assignment, this would mean that the seqno would be out of order, and
the current request would be deemed complete even before it was
submitted.)

We also employed two different mechanisms to track the active context
until it was switched out. The legacy method allowed for waiting upon an
active context (it could forcibly evict any vma, including context's),
but the execlists method took a step backwards by pinning the vma for
the entire active lifespan of the context (the only way to evict was to
idle the entire GPU, not individual contexts). However, to circumvent
the tricky issue of locking (i.e. we cannot take struct_mutex at the
time of i915_gem_request_submit(), where we would want to move the
previous context onto the active tracker and unpin it), we take the
execlists approach and keep the contexts pinned until retirement.
The benefit of the execlists approach, more important for execlists than
legacy, was the reduction in work in pinning the context for each
request - as the context was kept pinned until idle, it could short
circuit the pinning for all active contexts.

We introduce new engine vfuncs to pin and unpin the context
respectively. The context is pinned at the start of the request, and
only unpinned when the following request is retired (this ensures that
the context is idle and coherent in main memory before we unpin it). We
move the engine->last_context tracking into the retirement itself
(rather than during request submission) in order to allow the submission
to be reordered or unwound without undue difficultly.

And finally an ulterior motive for unifying context handling was to
prepare for mock requests.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_drv.h            |   4 --
 drivers/gpu/drm/i915/i915_gem_context.c    | 102 +++--------------------------
 drivers/gpu/drm/i915/i915_gem_request.c    |  38 +++++++----
 drivers/gpu/drm/i915/i915_gem_request.h    |  11 ----
 drivers/gpu/drm/i915/i915_guc_submission.c |  11 ----
 drivers/gpu/drm/i915/i915_perf.c           |  18 ++---
 drivers/gpu/drm/i915/intel_engine_cs.c     |  21 +++++-
 drivers/gpu/drm/i915/intel_lrc.c           |  62 ++++++------------
 drivers/gpu/drm/i915/intel_lrc.h           |   5 +-
 drivers/gpu/drm/i915/intel_ringbuffer.c    |  57 +++++++++-------
 drivers/gpu/drm/i915/intel_ringbuffer.h    |   4 ++
 11 files changed, 122 insertions(+), 211 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 8daa4fb13b52..7c228622716a 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2250,7 +2250,6 @@ struct drm_i915_private {
 			struct i915_perf_stream *exclusive_stream;
 
 			u32 specific_ctx_id;
-			struct i915_vma *pinned_rcs_vma;
 
 			struct hrtimer poll_check_timer;
 			wait_queue_head_t poll_wq;
@@ -3290,9 +3289,6 @@ int i915_gem_context_open(struct drm_device *dev, struct drm_file *file);
 void i915_gem_context_close(struct drm_device *dev, struct drm_file *file);
 int i915_switch_context(struct drm_i915_gem_request *req);
 int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv);
-struct i915_vma *
-i915_gem_context_pin_legacy(struct i915_gem_context *ctx,
-			    unsigned int flags);
 void i915_gem_context_free(struct kref *ctx_ref);
 struct i915_gem_context *
 i915_gem_context_create_gvt(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index a57c22659a3c..95812c26767c 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -416,21 +416,6 @@ i915_gem_context_create_gvt(struct drm_device *dev)
 	return ctx;
 }
 
-static void i915_gem_context_unpin(struct i915_gem_context *ctx,
-				   struct intel_engine_cs *engine)
-{
-	if (i915.enable_execlists) {
-		intel_lr_context_unpin(ctx, engine);
-	} else {
-		struct intel_context *ce = &ctx->engine[engine->id];
-
-		if (ce->state)
-			i915_vma_unpin(ce->state);
-
-		i915_gem_context_put(ctx);
-	}
-}
-
 int i915_gem_context_init(struct drm_i915_private *dev_priv)
 {
 	struct i915_gem_context *ctx;
@@ -490,10 +475,11 @@ void i915_gem_context_lost(struct drm_i915_private *dev_priv)
 	lockdep_assert_held(&dev_priv->drm.struct_mutex);
 
 	for_each_engine(engine, dev_priv, id) {
-		if (engine->last_context) {
-			i915_gem_context_unpin(engine->last_context, engine);
-			engine->last_context = NULL;
-		}
+		if (!engine->last_context)
+			continue;
+
+		engine->context_unpin(engine, engine->last_context);
+		engine->last_context = NULL;
 	}
 
 	/* Force the GPU state to be restored on enabling */
@@ -761,57 +747,20 @@ needs_pd_load_post(struct i915_hw_ppgtt *ppgtt,
 	return false;
 }
 
-struct i915_vma *
-i915_gem_context_pin_legacy(struct i915_gem_context *ctx,
-			    unsigned int flags)
-{
-	struct i915_vma *vma = ctx->engine[RCS].state;
-	int ret;
-
-	/* Clear this page out of any CPU caches for coherent swap-in/out.
-	 * We only want to do this on the first bind so that we do not stall
-	 * on an active context (which by nature is already on the GPU).
-	 */
-	if (!(vma->flags & I915_VMA_GLOBAL_BIND)) {
-		ret = i915_gem_object_set_to_gtt_domain(vma->obj, false);
-		if (ret)
-			return ERR_PTR(ret);
-	}
-
-	ret = i915_vma_pin(vma, 0, ctx->ggtt_alignment, PIN_GLOBAL | flags);
-	if (ret)
-		return ERR_PTR(ret);
-
-	return vma;
-}
-
 static int do_rcs_switch(struct drm_i915_gem_request *req)
 {
 	struct i915_gem_context *to = req->ctx;
 	struct intel_engine_cs *engine = req->engine;
 	struct i915_hw_ppgtt *ppgtt = to->ppgtt ?: req->i915->mm.aliasing_ppgtt;
-	struct i915_vma *vma;
-	struct i915_gem_context *from;
+	struct i915_gem_context *from = engine->last_context;
 	u32 hw_flags;
 	int ret, i;
 
+	GEM_BUG_ON(engine->id != RCS);
+
 	if (skip_rcs_switch(ppgtt, engine, to))
 		return 0;
 
-	/* Trying to pin first makes error handling easier. */
-	vma = i915_gem_context_pin_legacy(to, 0);
-	if (IS_ERR(vma))
-		return PTR_ERR(vma);
-
-	/*
-	 * Pin can switch back to the default context if we end up calling into
-	 * evict_everything - as a last ditch gtt defrag effort that also
-	 * switches to the default context. Hence we need to reload from here.
-	 *
-	 * XXX: Doing so is painfully broken!
-	 */
-	from = engine->last_context;
-
 	if (needs_pd_load_pre(ppgtt, engine, to)) {
 		/* Older GENs and non render rings still want the load first,
 		 * "PP_DCLV followed by PP_DIR_BASE register through Load
@@ -820,7 +769,7 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
 		trace_switch_mm(engine, to);
 		ret = ppgtt->switch_mm(ppgtt, req);
 		if (ret)
-			goto err;
+			return ret;
 	}
 
 	if (!to->engine[RCS].initialised || i915_gem_context_is_default(to))
@@ -837,29 +786,8 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
 	if (to != from || (hw_flags & MI_FORCE_RESTORE)) {
 		ret = mi_set_context(req, hw_flags);
 		if (ret)
-			goto err;
-	}
-
-	/* The backing object for the context is done after switching to the
-	 * *next* context. Therefore we cannot retire the previous context until
-	 * the next context has already started running. In fact, the below code
-	 * is a bit suboptimal because the retiring can occur simply after the
-	 * MI_SET_CONTEXT instead of when the next seqno has completed.
-	 */
-	if (from != NULL) {
-		/* As long as MI_SET_CONTEXT is serializing, ie. it flushes the
-		 * whole damn pipeline, we don't need to explicitly mark the
-		 * object dirty. The only exception is that the context must be
-		 * correct in case the object gets swapped out. Ideally we'd be
-		 * able to defer doing this until we know the object would be
-		 * swapped, but there is no way to do that yet.
-		 */
-		i915_vma_move_to_active(from->engine[RCS].state, req, 0);
-		/* state is kept alive until the next request */
-		i915_vma_unpin(from->engine[RCS].state);
-		i915_gem_context_put(from);
+			return ret;
 	}
-	engine->last_context = i915_gem_context_get(to);
 
 	/* GEN8 does *not* require an explicit reload if the PDPs have been
 	 * setup, and we do not wish to move them.
@@ -900,10 +828,6 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
 	}
 
 	return 0;
-
-err:
-	i915_vma_unpin(vma);
-	return ret;
 }
 
 /**
@@ -943,12 +867,6 @@ int i915_switch_context(struct drm_i915_gem_request *req)
 			ppgtt->pd_dirty_rings &= ~intel_engine_flag(engine);
 		}
 
-		if (to != engine->last_context) {
-			if (engine->last_context)
-				i915_gem_context_put(engine->last_context);
-			engine->last_context = i915_gem_context_get(to);
-		}
-
 		return 0;
 	}
 
diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
index fcf22b0e2967..06e9a607d934 100644
--- a/drivers/gpu/drm/i915/i915_gem_request.c
+++ b/drivers/gpu/drm/i915/i915_gem_request.c
@@ -206,6 +206,7 @@ void i915_gem_retire_noop(struct i915_gem_active *active,
 
 static void i915_gem_request_retire(struct drm_i915_gem_request *request)
 {
+	struct intel_engine_cs *engine = request->engine;
 	struct i915_gem_active *active, *next;
 
 	lockdep_assert_held(&request->i915->drm.struct_mutex);
@@ -216,9 +217,9 @@ static void i915_gem_request_retire(struct drm_i915_gem_request *request)
 
 	trace_i915_gem_request_retire(request);
 
-	spin_lock_irq(&request->engine->timeline->lock);
+	spin_lock_irq(&engine->timeline->lock);
 	list_del_init(&request->link);
-	spin_unlock_irq(&request->engine->timeline->lock);
+	spin_unlock_irq(&engine->timeline->lock);
 
 	/* We know the GPU must have read the request to have
 	 * sent us the seqno + interrupt, so use the position
@@ -266,17 +267,20 @@ static void i915_gem_request_retire(struct drm_i915_gem_request *request)
 
 	i915_gem_request_remove_from_client(request);
 
-	if (request->previous_context) {
-		if (i915.enable_execlists)
-			intel_lr_context_unpin(request->previous_context,
-					       request->engine);
-	}
-
 	/* Retirement decays the ban score as it is a sign of ctx progress */
 	if (request->ctx->ban_score > 0)
 		request->ctx->ban_score--;
 
-	i915_gem_context_put(request->ctx);
+	/* The backing object for the context is done after switching to the
+	 * *next* context. Therefore we cannot retire the previous context until
+	 * the next context has already started running. However, since we
+	 * cannot take the required locks at i915_gem_request_submit() we
+	 * defer the unpinning of the active context to now, retirement of
+	 * the subsequent request.
+	 */
+	if (engine->last_context)
+		engine->context_unpin(engine, engine->last_context);
+	engine->last_context = request->ctx;
 
 	dma_fence_signal(&request->fence);
 
@@ -524,10 +528,18 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
 	if (ret)
 		return ERR_PTR(ret);
 
-	ret = reserve_global_seqno(dev_priv);
+	/* Pinning the contexts may generate requests in order to acquire
+	 * GGTT space, so do this first before we reserve a seqno for
+	 * ourselves.
+	 */
+	ret = engine->context_pin(engine, ctx);
 	if (ret)
 		return ERR_PTR(ret);
 
+	ret = reserve_global_seqno(dev_priv);
+	if (ret)
+		goto err_unpin;
+
 	/* Move the oldest request to the slab-cache (if not in use!) */
 	req = list_first_entry_or_null(&engine->timeline->requests,
 				       typeof(*req), link);
@@ -593,11 +605,10 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
 	INIT_LIST_HEAD(&req->active_list);
 	req->i915 = dev_priv;
 	req->engine = engine;
-	req->ctx = i915_gem_context_get(ctx);
+	req->ctx = ctx;
 
 	/* No zalloc, must clear what we need by hand */
 	req->global_seqno = 0;
-	req->previous_context = NULL;
 	req->file_priv = NULL;
 	req->batch = NULL;
 
@@ -633,10 +644,11 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
 	GEM_BUG_ON(!list_empty(&req->priotree.signalers_list));
 	GEM_BUG_ON(!list_empty(&req->priotree.waiters_list));
 
-	i915_gem_context_put(ctx);
 	kmem_cache_free(dev_priv->requests, req);
 err_unreserve:
 	dev_priv->gt.active_requests--;
+err_unpin:
+	engine->context_unpin(engine, ctx);
 	return ERR_PTR(ret);
 }
 
diff --git a/drivers/gpu/drm/i915/i915_gem_request.h b/drivers/gpu/drm/i915/i915_gem_request.h
index e2b077df2da0..8569b35a332a 100644
--- a/drivers/gpu/drm/i915/i915_gem_request.h
+++ b/drivers/gpu/drm/i915/i915_gem_request.h
@@ -170,17 +170,6 @@ struct drm_i915_gem_request {
 	/** Preallocate space in the ring for the emitting the request */
 	u32 reserved_space;
 
-	/**
-	 * Context related to the previous request.
-	 * As the contexts are accessed by the hardware until the switch is
-	 * completed to a new context, the hardware may still be writing
-	 * to the context object after the breadcrumb is visible. We must
-	 * not unpin/unbind/prune that object whilst still active and so
-	 * we keep the previous context pinned until the following (this)
-	 * request is retired.
-	 */
-	struct i915_gem_context *previous_context;
-
 	/** Batch buffer related to this request if any (used for
 	 * error state dump only).
 	 */
diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c
index 7fa4e74c1dd3..3f144216e188 100644
--- a/drivers/gpu/drm/i915/i915_guc_submission.c
+++ b/drivers/gpu/drm/i915/i915_guc_submission.c
@@ -533,17 +533,6 @@ static void __i915_guc_submit(struct drm_i915_gem_request *rq)
 
 static void i915_guc_submit(struct drm_i915_gem_request *rq)
 {
-	struct intel_engine_cs *engine = rq->engine;
-
-	/* We keep the previous context alive until we retire the following
-	 * request. This ensures that any the context object is still pinned
-	 * for any residual writes the HW makes into it on the context switch
-	 * into the next object following the breadcrumb. Otherwise, we may
-	 * retire the context too early.
-	 */
-	rq->previous_context = engine->last_context;
-	engine->last_context = rq->ctx;
-
 	i915_gem_request_submit(rq);
 	__i915_guc_submit(rq);
 }
diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
index 5669f0862458..cfe4152212b9 100644
--- a/drivers/gpu/drm/i915/i915_perf.c
+++ b/drivers/gpu/drm/i915/i915_perf.c
@@ -641,7 +641,7 @@ static int i915_oa_read(struct i915_perf_stream *stream,
 static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
 {
 	struct drm_i915_private *dev_priv = stream->dev_priv;
-	struct i915_vma *vma;
+	struct intel_engine_cs *engine = dev_priv->engine[RCS];
 	int ret;
 
 	ret = i915_mutex_lock_interruptible(&dev_priv->drm);
@@ -653,19 +653,16 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
 	 *
 	 * NB: implied RCS engine...
 	 */
-	vma = i915_gem_context_pin_legacy(stream->ctx, 0);
-	if (IS_ERR(vma)) {
-		ret = PTR_ERR(vma);
+	ret = engine->context_pin(engine, stream->ctx);
+	if (ret)
 		goto unlock;
-	}
-
-	dev_priv->perf.oa.pinned_rcs_vma = vma;
 
 	/* Explicitly track the ID (instead of calling i915_ggtt_offset()
 	 * on the fly) considering the difference with gen8+ and
 	 * execlists
 	 */
-	dev_priv->perf.oa.specific_ctx_id = i915_ggtt_offset(vma);
+	dev_priv->perf.oa.specific_ctx_id =
+		i915_ggtt_offset(stream->ctx->engine[engine->id].state);
 
 unlock:
 	mutex_unlock(&dev_priv->drm.struct_mutex);
@@ -676,13 +673,12 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
 static void oa_put_render_ctx_id(struct i915_perf_stream *stream)
 {
 	struct drm_i915_private *dev_priv = stream->dev_priv;
+	struct intel_engine_cs *engine = dev_priv->engine[RCS];
 
 	mutex_lock(&dev_priv->drm.struct_mutex);
 
-	i915_vma_unpin(dev_priv->perf.oa.pinned_rcs_vma);
-	dev_priv->perf.oa.pinned_rcs_vma = NULL;
-
 	dev_priv->perf.oa.specific_ctx_id = INVALID_CTX_ID;
+	engine->context_unpin(engine, stream->ctx);
 
 	mutex_unlock(&dev_priv->drm.struct_mutex);
 }
diff --git a/drivers/gpu/drm/i915/intel_engine_cs.c b/drivers/gpu/drm/i915/intel_engine_cs.c
index e8afe1185831..97bbbc3d6aa8 100644
--- a/drivers/gpu/drm/i915/intel_engine_cs.c
+++ b/drivers/gpu/drm/i915/intel_engine_cs.c
@@ -304,15 +304,30 @@ int intel_engine_init_common(struct intel_engine_cs *engine)
 {
 	int ret;
 
-	ret = intel_engine_init_breadcrumbs(engine);
+	/* We may need to do things with the shrinker which
+	 * require us to immediately switch back to the default
+	 * context. This can cause a problem as pinning the
+	 * default context also requires GTT space which may not
+	 * be available. To avoid this we always pin the default
+	 * context.
+	 */
+	ret = engine->context_pin(engine, engine->i915->kernel_context);
 	if (ret)
 		return ret;
 
+	ret = intel_engine_init_breadcrumbs(engine);
+	if (ret)
+		goto err_unpin;
+
 	ret = i915_gem_render_state_init(engine);
 	if (ret)
-		return ret;
+		goto err_unpin;
 
 	return 0;
+
+err_unpin:
+	engine->context_unpin(engine, engine->i915->kernel_context);
+	return ret;
 }
 
 /**
@@ -330,6 +345,8 @@ void intel_engine_cleanup_common(struct intel_engine_cs *engine)
 	intel_engine_fini_breadcrumbs(engine);
 	intel_engine_cleanup_cmd_parser(engine);
 	i915_gem_batch_pool_fini(&engine->batch_pool);
+
+	engine->context_unpin(engine, engine->i915->kernel_context);
 }
 
 u64 intel_engine_get_active_head(struct intel_engine_cs *engine)
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 5cabe4e9d22f..58cea1f9ad27 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -512,15 +512,6 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
 		RB_CLEAR_NODE(&cursor->priotree.node);
 		cursor->priotree.priority = INT_MAX;
 
-		/* We keep the previous context alive until we retire the
-		 * following request. This ensures that any the context object
-		 * is still pinned for any residual writes the HW makes into it
-		 * on the context switch into the next object following the
-		 * breadcrumb. Otherwise, we may retire the context too early.
-		 */
-		cursor->previous_context = engine->last_context;
-		engine->last_context = cursor->ctx;
-
 		__i915_gem_request_submit(cursor);
 		last = cursor;
 		submit = true;
@@ -772,8 +763,8 @@ static void execlists_schedule(struct drm_i915_gem_request *request, int prio)
 	/* XXX Do we need to preempt to make room for us and our deps? */
 }
 
-static int intel_lr_context_pin(struct i915_gem_context *ctx,
-				struct intel_engine_cs *engine)
+static int execlists_context_pin(struct intel_engine_cs *engine,
+				 struct i915_gem_context *ctx)
 {
 	struct intel_context *ce = &ctx->engine[engine->id];
 	void *vaddr;
@@ -784,6 +775,12 @@ static int intel_lr_context_pin(struct i915_gem_context *ctx,
 	if (ce->pin_count++)
 		return 0;
 
+	if (!ce->state) {
+		ret = execlists_context_deferred_alloc(ctx, engine);
+		if (ret)
+			goto err;
+	}
+
 	ret = i915_vma_pin(ce->state, 0, GEN8_LR_CONTEXT_ALIGN,
 			   PIN_OFFSET_BIAS | GUC_WOPCM_TOP | PIN_GLOBAL);
 	if (ret)
@@ -825,8 +822,8 @@ static int intel_lr_context_pin(struct i915_gem_context *ctx,
 	return ret;
 }
 
-void intel_lr_context_unpin(struct i915_gem_context *ctx,
-			    struct intel_engine_cs *engine)
+static void execlists_context_unpin(struct intel_engine_cs *engine,
+				    struct i915_gem_context *ctx)
 {
 	struct intel_context *ce = &ctx->engine[engine->id];
 
@@ -850,24 +847,17 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request
 	struct intel_context *ce = &request->ctx->engine[engine->id];
 	int ret;
 
+	GEM_BUG_ON(!ce->pin_count);
+
 	/* Flush enough space to reduce the likelihood of waiting after
 	 * we start building the request - in which case we will just
 	 * have to repeat work.
 	 */
 	request->reserved_space += EXECLISTS_REQUEST_SIZE;
 
-	if (!ce->state) {
-		ret = execlists_context_deferred_alloc(request->ctx, engine);
-		if (ret)
-			return ret;
-	}
-
+	GEM_BUG_ON(!ce->ring);
 	request->ring = ce->ring;
 
-	ret = intel_lr_context_pin(request->ctx, engine);
-	if (ret)
-		return ret;
-
 	if (i915.enable_guc_submission) {
 		/*
 		 * Check that the GuC has space for the request before
@@ -876,7 +866,7 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request
 		 */
 		ret = i915_guc_wq_reserve(request);
 		if (ret)
-			goto err_unpin;
+			goto err;
 	}
 
 	ret = intel_ring_begin(request, 0);
@@ -904,8 +894,7 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request
 err_unreserve:
 	if (i915.enable_guc_submission)
 		i915_guc_wq_unreserve(request);
-err_unpin:
-	intel_lr_context_unpin(request->ctx, engine);
+err:
 	return ret;
 }
 
@@ -1789,13 +1778,12 @@ void intel_logical_ring_cleanup(struct intel_engine_cs *engine)
 	if (engine->cleanup)
 		engine->cleanup(engine);
 
-	intel_engine_cleanup_common(engine);
-
 	if (engine->status_page.vma) {
 		i915_gem_object_unpin_map(engine->status_page.vma->obj);
 		engine->status_page.vma = NULL;
 	}
-	intel_lr_context_unpin(dev_priv->kernel_context, engine);
+
+	intel_engine_cleanup_common(engine);
 
 	lrc_destroy_wa_ctx_obj(engine);
 	engine->i915 = NULL;
@@ -1820,6 +1808,10 @@ logical_ring_default_vfuncs(struct intel_engine_cs *engine)
 	/* Default vfuncs which can be overriden by each engine. */
 	engine->init_hw = gen8_init_common_ring;
 	engine->reset_hw = reset_common_ring;
+
+	engine->context_pin = execlists_context_pin;
+	engine->context_unpin = execlists_context_unpin;
+
 	engine->emit_flush = gen8_emit_flush;
 	engine->emit_breadcrumb = gen8_emit_breadcrumb;
 	engine->emit_breadcrumb_sz = gen8_emit_breadcrumb_sz;
@@ -1902,18 +1894,6 @@ logical_ring_init(struct intel_engine_cs *engine)
 	if (ret)
 		goto error;
 
-	ret = execlists_context_deferred_alloc(dctx, engine);
-	if (ret)
-		goto error;
-
-	/* As this is the default context, always pin it */
-	ret = intel_lr_context_pin(dctx, engine);
-	if (ret) {
-		DRM_ERROR("Failed to pin context for %s: %d\n",
-			  engine->name, ret);
-		goto error;
-	}
-
 	/* And setup the hardware status page. */
 	ret = lrc_setup_hws(engine, dctx->engine[engine->id].state);
 	if (ret) {
diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h
index 7c6403243394..b5630331086a 100644
--- a/drivers/gpu/drm/i915/intel_lrc.h
+++ b/drivers/gpu/drm/i915/intel_lrc.h
@@ -79,13 +79,10 @@ int intel_engines_init(struct drm_i915_private *dev_priv);
 #define LRC_PPHWSP_PN	(LRC_GUCSHR_PN + 1)
 #define LRC_STATE_PN	(LRC_PPHWSP_PN + 1)
 
+struct drm_i915_private;
 struct i915_gem_context;
 
 uint32_t intel_lr_context_size(struct intel_engine_cs *engine);
-void intel_lr_context_unpin(struct i915_gem_context *ctx,
-			    struct intel_engine_cs *engine);
-
-struct drm_i915_private;
 
 void intel_lr_context_resume(struct drm_i915_private *dev_priv);
 uint64_t intel_lr_context_descriptor(struct i915_gem_context *ctx,
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 0b7d13b6e228..a57eb5dec991 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -1939,8 +1939,26 @@ intel_ring_free(struct intel_ring *ring)
 	kfree(ring);
 }
 
-static int intel_ring_context_pin(struct i915_gem_context *ctx,
-				  struct intel_engine_cs *engine)
+static int context_pin(struct i915_gem_context *ctx, unsigned int flags)
+{
+	struct i915_vma *vma = ctx->engine[RCS].state;
+	int ret;
+
+	/* Clear this page out of any CPU caches for coherent swap-in/out.
+	 * We only want to do this on the first bind so that we do not stall
+	 * on an active context (which by nature is already on the GPU).
+	 */
+	if (!(vma->flags & I915_VMA_GLOBAL_BIND)) {
+		ret = i915_gem_object_set_to_gtt_domain(vma->obj, false);
+		if (ret)
+			return ret;
+	}
+
+	return i915_vma_pin(vma, 0, ctx->ggtt_alignment, PIN_GLOBAL | flags);
+}
+
+static int intel_ring_context_pin(struct intel_engine_cs *engine,
+				  struct i915_gem_context *ctx)
 {
 	struct intel_context *ce = &ctx->engine[engine->id];
 	int ret;
@@ -1951,13 +1969,15 @@ static int intel_ring_context_pin(struct i915_gem_context *ctx,
 		return 0;
 
 	if (ce->state) {
-		struct i915_vma *vma;
+		unsigned int flags;
+
+		flags = 0;
+		if (ctx == ctx->i915->kernel_context)
+			flags = PIN_HIGH;
 
-		vma = i915_gem_context_pin_legacy(ctx, PIN_HIGH);
-		if (IS_ERR(vma)) {
-			ret = PTR_ERR(vma);
+		ret = context_pin(ctx, flags);
+		if (ret)
 			goto error;
-		}
 	}
 
 	/* The kernel context is only used as a placeholder for flushing the
@@ -1978,8 +1998,8 @@ static int intel_ring_context_pin(struct i915_gem_context *ctx,
 	return ret;
 }
 
-static void intel_ring_context_unpin(struct i915_gem_context *ctx,
-				     struct intel_engine_cs *engine)
+static void intel_ring_context_unpin(struct intel_engine_cs *engine,
+				     struct i915_gem_context *ctx)
 {
 	struct intel_context *ce = &ctx->engine[engine->id];
 
@@ -2008,17 +2028,6 @@ static int intel_init_ring_buffer(struct intel_engine_cs *engine)
 	if (ret)
 		goto error;
 
-	/* We may need to do things with the shrinker which
-	 * require us to immediately switch back to the default
-	 * context. This can cause a problem as pinning the
-	 * default context also requires GTT space which may not
-	 * be available. To avoid this we always pin the default
-	 * context.
-	 */
-	ret = intel_ring_context_pin(dev_priv->kernel_context, engine);
-	if (ret)
-		goto error;
-
 	ring = intel_engine_create_ring(engine, 32 * PAGE_SIZE);
 	if (IS_ERR(ring)) {
 		ret = PTR_ERR(ring);
@@ -2077,8 +2086,6 @@ void intel_engine_cleanup(struct intel_engine_cs *engine)
 
 	intel_engine_cleanup_common(engine);
 
-	intel_ring_context_unpin(dev_priv->kernel_context, engine);
-
 	engine->i915 = NULL;
 	dev_priv->engine[engine->id] = NULL;
 	kfree(engine);
@@ -2099,12 +2106,15 @@ int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request)
 {
 	int ret;
 
+	GEM_BUG_ON(!request->ctx->engine[request->engine->id].pin_count);
+
 	/* Flush enough space to reduce the likelihood of waiting after
 	 * we start building the request - in which case we will just
 	 * have to repeat work.
 	 */
 	request->reserved_space += LEGACY_REQUEST_SIZE;
 
+	GEM_BUG_ON(!request->engine->buffer);
 	request->ring = request->engine->buffer;
 
 	ret = intel_ring_begin(request, 0);
@@ -2584,6 +2594,9 @@ static void intel_ring_default_vfuncs(struct drm_i915_private *dev_priv,
 	engine->init_hw = init_ring_common;
 	engine->reset_hw = reset_ring_common;
 
+	engine->context_pin = intel_ring_context_pin;
+	engine->context_unpin = intel_ring_context_unpin;
+
 	engine->emit_breadcrumb = i9xx_emit_breadcrumb;
 	engine->emit_breadcrumb_sz = i9xx_emit_breadcrumb_sz;
 	if (i915.semaphores) {
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index d8b066fd5dcf..0b69a50ab833 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -268,6 +268,10 @@ struct intel_engine_cs {
 	void		(*reset_hw)(struct intel_engine_cs *engine,
 				    struct drm_i915_gem_request *req);
 
+	int		(*context_pin)(struct intel_engine_cs *engine,
+				       struct i915_gem_context *ctx);
+	void		(*context_unpin)(struct intel_engine_cs *engine,
+					 struct i915_gem_context *ctx);
 	int		(*init_context)(struct drm_i915_gem_request *req);
 
 	int		(*emit_flush)(struct drm_i915_gem_request *request,
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 09/16] drm/i915: Simplify releasing context reference
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (7 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 08/16] drm/i915: Unify active context tracking between legacy/execlists/guc Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-09 15:03   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 10/16] drm/i915: Mark the shadow gvt context as closed Chris Wilson
                   ` (11 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

A few users only take the struct_mutex in order to release a reference
to a context. We can expose a kref_put_mutex() wrapper in order to
simplify these users, and optimise taking of the mutex to the final
unref.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_drv.h  |  7 +++++++
 drivers/gpu/drm/i915/i915_perf.c | 16 ++++------------
 2 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 7c228622716a..3411e38e32af 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3320,6 +3320,13 @@ static inline void i915_gem_context_put(struct i915_gem_context *ctx)
 	kref_put(&ctx->ref, i915_gem_context_free);
 }
 
+static inline void i915_gem_context_put_unlocked(struct i915_gem_context *ctx)
+{
+	kref_put_mutex(&ctx->ref,
+		       i915_gem_context_free,
+		       &ctx->i915->drm.struct_mutex);
+}
+
 static inline struct intel_timeline *
 i915_gem_context_lookup_timeline(struct i915_gem_context *ctx,
 				 struct intel_engine_cs *engine)
diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
index cfe4152212b9..ee6271fe96de 100644
--- a/drivers/gpu/drm/i915/i915_perf.c
+++ b/drivers/gpu/drm/i915/i915_perf.c
@@ -1298,8 +1298,6 @@ static long i915_perf_ioctl(struct file *file,
 
 static void i915_perf_destroy_locked(struct i915_perf_stream *stream)
 {
-	struct drm_i915_private *dev_priv = stream->dev_priv;
-
 	if (stream->enabled)
 		i915_perf_disable_locked(stream);
 
@@ -1308,11 +1306,8 @@ static void i915_perf_destroy_locked(struct i915_perf_stream *stream)
 
 	list_del(&stream->link);
 
-	if (stream->ctx) {
-		mutex_lock(&dev_priv->drm.struct_mutex);
-		i915_gem_context_put(stream->ctx);
-		mutex_unlock(&dev_priv->drm.struct_mutex);
-	}
+	if (stream->ctx)
+		i915_gem_context_put_unlocked(stream->ctx);
 
 	kfree(stream);
 }
@@ -1446,11 +1441,8 @@ i915_perf_open_ioctl_locked(struct drm_i915_private *dev_priv,
 err_alloc:
 	kfree(stream);
 err_ctx:
-	if (specific_ctx) {
-		mutex_lock(&dev_priv->drm.struct_mutex);
-		i915_gem_context_put(specific_ctx);
-		mutex_unlock(&dev_priv->drm.struct_mutex);
-	}
+	if (specific_ctx)
+		i915_gem_context_put_unlocked(specific_ctx);
 err:
 	return ret;
 }
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 10/16] drm/i915: Mark the shadow gvt context as closed
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (8 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 09/16] drm/i915: Simplify releasing context reference Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-09 15:07   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 11/16] drm/i915/execlists: Request the kernel context be pinned high Chris Wilson
                   ` (10 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

As the shadow gvt is not user accessible and does not have an associated
vm, we can mark it as closed during its construction. This saves leaking
the internal knowledge of i915_gem_context into gvt/.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gvt/scheduler.c    | 10 +---------
 drivers/gpu/drm/i915/i915_gem_context.c |  1 +
 2 files changed, 2 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c
index 4db242250235..fd2b026f7ecd 100644
--- a/drivers/gpu/drm/i915/gvt/scheduler.c
+++ b/drivers/gpu/drm/i915/gvt/scheduler.c
@@ -549,18 +549,10 @@ int intel_gvt_init_workload_scheduler(struct intel_gvt *gvt)
 
 void intel_vgpu_clean_gvt_context(struct intel_vgpu *vgpu)
 {
-	struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
-
 	atomic_notifier_chain_unregister(&vgpu->shadow_ctx->status_notifier,
 			&vgpu->shadow_ctx_notifier_block);
 
-	mutex_lock(&dev_priv->drm.struct_mutex);
-
-	/* a little hacky to mark as ctx closed */
-	vgpu->shadow_ctx->closed = true;
-	i915_gem_context_put(vgpu->shadow_ctx);
-
-	mutex_unlock(&dev_priv->drm.struct_mutex);
+	i915_gem_context_put_unlocked(vgpu->shadow_ctx);
 }
 
 int intel_vgpu_init_gvt_context(struct intel_vgpu *vgpu)
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index 95812c26767c..042befd263fe 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -409,6 +409,7 @@ i915_gem_context_create_gvt(struct drm_device *dev)
 	if (IS_ERR(ctx))
 		goto out;
 
+	ctx->closed = true; /* not user accessible */
 	ctx->execlists_force_single_submission = true;
 	ctx->ring_size = 512 * PAGE_SIZE; /* Max ring buffer size */
 out:
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 11/16] drm/i915/execlists: Request the kernel context be pinned high
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (9 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 10/16] drm/i915: Mark the shadow gvt context as closed Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-09 15:08   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 12/16] drm/i915: Swap if(enable_execlists) in i915_gem_request_alloc for a vfunc Chris Wilson
                   ` (9 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

PIN_HIGH is an expensive operation (in comparison to allocating from the
hole stack) unsuitable for frequent use (such as switching between
contexts). However, the kernel context should be pinned just once for
the lifetime of the driver, and here it is appropriate to keep it out of
the mappable range (in order to maximise mappable space for users).

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/intel_lrc.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 58cea1f9ad27..22ded92d0346 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -767,6 +767,7 @@ static int execlists_context_pin(struct intel_engine_cs *engine,
 				 struct i915_gem_context *ctx)
 {
 	struct intel_context *ce = &ctx->engine[engine->id];
+	unsigned int flags;
 	void *vaddr;
 	int ret;
 
@@ -781,8 +782,11 @@ static int execlists_context_pin(struct intel_engine_cs *engine,
 			goto err;
 	}
 
-	ret = i915_vma_pin(ce->state, 0, GEN8_LR_CONTEXT_ALIGN,
-			   PIN_OFFSET_BIAS | GUC_WOPCM_TOP | PIN_GLOBAL);
+	flags = PIN_OFFSET_BIAS | GUC_WOPCM_TOP | PIN_GLOBAL;
+	if (ctx == ctx->i915->kernel_context)
+		flags |= PIN_HIGH;
+
+	ret = i915_vma_pin(ce->state, 0, GEN8_LR_CONTEXT_ALIGN, flags);
 	if (ret)
 		goto err;
 
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 12/16] drm/i915: Swap if(enable_execlists) in i915_gem_request_alloc for a vfunc
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (10 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 11/16] drm/i915/execlists: Request the kernel context be pinned high Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-09 15:16   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 13/16] drm/i915: Add selftests for i915_gem_request Chris Wilson
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

A fairly trivial move of a matching pair of routines (for preparing a
request for construction) onto an engine vfunc. The ulterior motive is
to be able to create a mock request implementation.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_gem_request.c | 5 +----
 drivers/gpu/drm/i915/intel_lrc.c        | 4 +++-
 drivers/gpu/drm/i915/intel_lrc.h        | 2 --
 drivers/gpu/drm/i915/intel_ringbuffer.c | 4 +++-
 drivers/gpu/drm/i915/intel_ringbuffer.h | 3 +--
 5 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
index 06e9a607d934..881bed5347fb 100644
--- a/drivers/gpu/drm/i915/i915_gem_request.c
+++ b/drivers/gpu/drm/i915/i915_gem_request.c
@@ -622,10 +622,7 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
 	req->reserved_space = MIN_SPACE_FOR_ADD_REQUEST;
 	GEM_BUG_ON(req->reserved_space < engine->emit_breadcrumb_sz);
 
-	if (i915.enable_execlists)
-		ret = intel_logical_ring_alloc_request_extras(req);
-	else
-		ret = intel_ring_alloc_request_extras(req);
+	ret = engine->request_alloc(req);
 	if (ret)
 		goto err_ctx;
 
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
index 22ded92d0346..3f536ef37968 100644
--- a/drivers/gpu/drm/i915/intel_lrc.c
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -845,7 +845,7 @@ static void execlists_context_unpin(struct intel_engine_cs *engine,
 	i915_gem_context_put(ctx);
 }
 
-int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request)
+static int execlists_request_alloc(struct drm_i915_gem_request *request)
 {
 	struct intel_engine_cs *engine = request->engine;
 	struct intel_context *ce = &request->ctx->engine[engine->id];
@@ -1816,6 +1816,8 @@ logical_ring_default_vfuncs(struct intel_engine_cs *engine)
 	engine->context_pin = execlists_context_pin;
 	engine->context_unpin = execlists_context_unpin;
 
+	engine->request_alloc = execlists_request_alloc;
+
 	engine->emit_flush = gen8_emit_flush;
 	engine->emit_breadcrumb = gen8_emit_breadcrumb;
 	engine->emit_breadcrumb_sz = gen8_emit_breadcrumb_sz;
diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h
index b5630331086a..01ba36ea125e 100644
--- a/drivers/gpu/drm/i915/intel_lrc.h
+++ b/drivers/gpu/drm/i915/intel_lrc.h
@@ -63,8 +63,6 @@ enum {
 };
 
 /* Logical Rings */
-int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request);
-int intel_logical_ring_reserve_space(struct drm_i915_gem_request *request);
 void intel_logical_ring_stop(struct intel_engine_cs *engine);
 void intel_logical_ring_cleanup(struct intel_engine_cs *engine);
 int logical_render_ring_init(struct intel_engine_cs *engine);
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index a57eb5dec991..a42b9bf45b42 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -2102,7 +2102,7 @@ void intel_legacy_submission_resume(struct drm_i915_private *dev_priv)
 	}
 }
 
-int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request)
+static int ring_request_alloc(struct drm_i915_gem_request *request)
 {
 	int ret;
 
@@ -2597,6 +2597,8 @@ static void intel_ring_default_vfuncs(struct drm_i915_private *dev_priv,
 	engine->context_pin = intel_ring_context_pin;
 	engine->context_unpin = intel_ring_context_unpin;
 
+	engine->request_alloc = ring_request_alloc;
+
 	engine->emit_breadcrumb = i9xx_emit_breadcrumb;
 	engine->emit_breadcrumb_sz = i9xx_emit_breadcrumb_sz;
 	if (i915.semaphores) {
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 0b69a50ab833..12f4dd1cbf9c 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -272,6 +272,7 @@ struct intel_engine_cs {
 				       struct i915_gem_context *ctx);
 	void		(*context_unpin)(struct intel_engine_cs *engine,
 					 struct i915_gem_context *ctx);
+	int		(*request_alloc)(struct drm_i915_gem_request *req);
 	int		(*init_context)(struct drm_i915_gem_request *req);
 
 	int		(*emit_flush)(struct drm_i915_gem_request *request,
@@ -476,8 +477,6 @@ void intel_engine_cleanup(struct intel_engine_cs *engine);
 
 void intel_legacy_submission_resume(struct drm_i915_private *dev_priv);
 
-int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request);
-
 int __must_check intel_ring_begin(struct drm_i915_gem_request *req, int n);
 int __must_check intel_ring_cacheline_align(struct drm_i915_gem_request *req);
 
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 13/16] drm/i915: Add selftests for i915_gem_request
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (11 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 12/16] drm/i915: Swap if(enable_execlists) in i915_gem_request_alloc for a vfunc Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-09 15:51   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 14/16] drm/i915: Add a simple request selftest for waiting Chris Wilson
                   ` (7 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

Simple starting point for adding seltests for i915_gem_request, first
mock a device (with engines and contexts) that allows us to construct
and execute a request, along with waiting for the request to complete.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_gem_request.c    | 402 +++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/i915_mock_selftests.h |   1 +
 2 files changed, 403 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
index 881bed5347fb..6553457adc77 100644
--- a/drivers/gpu/drm/i915/i915_gem_request.c
+++ b/drivers/gpu/drm/i915/i915_gem_request.c
@@ -1208,3 +1208,405 @@ void i915_gem_retire_requests(struct drm_i915_private *dev_priv)
 	for_each_engine(engine, dev_priv, id)
 		engine_retire_requests(engine);
 }
+
+#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+#include "i915_selftest.h"
+
+struct mock_engine {
+	struct intel_engine_cs base;
+
+	spinlock_t hw_lock;
+	struct list_head hw_queue;
+	struct timer_list hw_delay;
+};
+
+struct mock_request {
+	struct drm_i915_gem_request base;
+
+	struct list_head link;
+	unsigned long delay;
+};
+
+static void mock_seqno_advance(struct intel_engine_cs *engine, u32 seqno)
+{
+	intel_write_status_page(engine, I915_GEM_HWS_INDEX, seqno);
+	intel_engine_wakeup(engine);
+}
+
+static void hw_delay_complete(unsigned long data)
+{
+	struct mock_engine *engine = (typeof(engine))data;
+	struct mock_request *request;
+
+	spin_lock(&engine->hw_lock);
+	request = list_first_entry_or_null(&engine->hw_queue,
+					   typeof(*request),
+					   link);
+	if (request) {
+		list_del(&request->link);
+		mock_seqno_advance(&engine->base, request->base.global_seqno);
+	}
+
+	request = list_first_entry_or_null(&engine->hw_queue,
+					   typeof(*request),
+					   link);
+	if (request)
+		mod_timer(&engine->hw_delay, jiffies + request->delay);
+	spin_unlock(&engine->hw_lock);
+}
+
+static void mock_engine_flush(struct intel_engine_cs *engine)
+{
+	struct mock_engine *mock =
+		container_of(engine, typeof(*mock), base);
+	struct mock_request *request, *rn;
+
+	del_timer_sync(&mock->hw_delay);
+
+	spin_lock_irq(&mock->hw_lock);
+	list_for_each_entry_safe(request, rn, &mock->hw_queue, link) {
+		list_del_init(&request->link);
+		mock_seqno_advance(&mock->base, request->base.global_seqno);
+	}
+	spin_unlock_irq(&mock->hw_lock);
+}
+
+static int mock_context_pin(struct intel_engine_cs *engine,
+			    struct i915_gem_context *ctx)
+{
+	i915_gem_context_get(ctx);
+	return 0;
+}
+
+static void mock_context_unpin(struct intel_engine_cs *engine,
+			       struct i915_gem_context *ctx)
+{
+	i915_gem_context_put(ctx);
+}
+
+static int mock_request_alloc(struct drm_i915_gem_request *request)
+{
+	request->ring = request->engine->buffer;
+	return 0;
+}
+
+static int mock_emit_flush(struct drm_i915_gem_request *request,
+			   unsigned int flags)
+{
+	return 0;
+}
+
+static void mock_emit_breadcrumb(struct drm_i915_gem_request *request,
+				 u32 *flags)
+{
+}
+
+static void mock_submit_request(struct drm_i915_gem_request *request)
+{
+	struct mock_request *mock = container_of(request, typeof(*mock), base);
+	struct mock_engine *engine =
+		container_of(request->engine, typeof(*engine), base);
+
+	i915_gem_request_submit(request);
+
+	spin_lock_irq(&engine->hw_lock);
+	list_add_tail(&mock->link, &engine->hw_queue);
+	if (list_first_entry(&engine->hw_queue, typeof(*mock), link) == mock)
+		mod_timer(&engine->hw_delay, jiffies + mock->delay);
+	spin_unlock_irq(&engine->hw_lock);
+}
+
+static struct drm_i915_gem_request *
+mock_request(struct intel_engine_cs *engine,
+	     struct i915_gem_context *context,
+	     unsigned long delay)
+{
+	struct drm_i915_gem_request *request;
+	struct mock_request *mock;
+
+	request = i915_gem_request_alloc(engine, context);
+	if (!request)
+		return NULL;
+
+	mock = container_of(request, typeof(*mock), base);
+	INIT_LIST_HEAD(&mock->link);
+	mock->delay = delay;
+
+	return &mock->base;
+}
+
+static struct intel_ring *mock_ring(struct intel_engine_cs *engine)
+{
+	struct intel_ring *ring;
+
+	ring = kzalloc(sizeof(*ring) + 4096, GFP_KERNEL);
+	if (!ring)
+		return NULL;
+
+	ring->engine = engine;
+	ring->size = 4096;
+	ring->effective_size = ring->size;
+	ring->vaddr = (void *)(ring + 1);
+
+	INIT_LIST_HEAD(&ring->request_list);
+	ring->last_retired_head = -1;
+	intel_ring_update_space(ring);
+
+	return ring;
+}
+
+static struct intel_engine_cs *mock_engine(struct drm_i915_private *i915,
+					   const char *name)
+{
+	struct mock_engine *engine;
+	static int id;
+
+	engine = kzalloc(sizeof(*engine) + 4096, GFP_KERNEL);
+	if (!engine)
+		return NULL;
+
+	engine->base.buffer = mock_ring(&engine->base);
+	if (!engine->base.buffer) {
+		kfree(engine);
+		return NULL;
+	}
+
+	/* minimal engine setup for requests */
+	engine->base.i915 = i915;
+	engine->base.name = name;
+	engine->base.id = id++;
+	engine->base.status_page.page_addr = (void *)(engine + 1);
+
+	engine->base.context_pin = mock_context_pin;
+	engine->base.context_unpin = mock_context_unpin;
+	engine->base.request_alloc = mock_request_alloc;
+	engine->base.emit_flush = mock_emit_flush;
+	engine->base.emit_breadcrumb = mock_emit_breadcrumb;
+	engine->base.submit_request = mock_submit_request;
+
+	engine->base.timeline =
+		&i915->gt.global_timeline.engine[engine->base.id];
+
+	/* minimal breadcrumbs init */
+	spin_lock_init(&engine->base.breadcrumbs.lock);
+	engine->base.breadcrumbs.mock = true;
+
+	/* fake hw queue */
+	spin_lock_init(&engine->hw_lock);
+	setup_timer(&engine->hw_delay,
+		    hw_delay_complete,
+		    (unsigned long)engine);
+	INIT_LIST_HEAD(&engine->hw_queue);
+
+	return &engine->base;
+}
+
+static void mock_engine_free(struct intel_engine_cs *engine)
+{
+	if (engine->last_context)
+		engine->context_unpin(engine, engine->last_context);
+
+	kfree(engine->buffer);
+	kfree(engine);
+}
+
+static void mock_ppgtt_cleanup(struct i915_address_space *vm)
+{
+}
+
+static struct i915_hw_ppgtt *
+mock_ppgtt(struct drm_i915_private *i915,
+	   const char *name)
+{
+	struct i915_hw_ppgtt *ppgtt;
+
+	ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
+	if (!ppgtt)
+		return NULL;
+
+	kref_init(&ppgtt->ref);
+
+	INIT_LIST_HEAD(&ppgtt->base.active_list);
+	INIT_LIST_HEAD(&ppgtt->base.inactive_list);
+	INIT_LIST_HEAD(&ppgtt->base.unbound_list);
+
+	INIT_LIST_HEAD(&ppgtt->base.global_link);
+	drm_mm_init(&ppgtt->base.mm, 0, ~0);
+	i915_gem_timeline_init(i915, &ppgtt->base.timeline, name);
+
+	ppgtt->base.cleanup = mock_ppgtt_cleanup;
+
+	return ppgtt;
+}
+
+static struct i915_gem_context *
+mock_context(struct drm_i915_private *i915,
+	     const char *name)
+{
+	struct i915_gem_context *ctx;
+	int ret;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return NULL;
+
+	kref_init(&ctx->ref);
+	INIT_LIST_HEAD(&ctx->link);
+	ctx->name = name ? kstrdup(name, GFP_KERNEL) : NULL;
+	ctx->i915 = i915;
+
+	ret = ida_simple_get(&i915->context_hw_ida,
+			     0, MAX_CONTEXT_HW_ID, GFP_KERNEL);
+	if (ret < 0) {
+		kfree(ctx);
+		return NULL;
+	}
+	ctx->hw_id = ret;
+
+	if (name) {
+		ctx->ppgtt = mock_ppgtt(i915, name);
+		if (!ctx->ppgtt) {
+			kfree(ctx);
+			return NULL;
+		}
+	}
+
+	return ctx;
+}
+
+static void mock_retire_work_handler(struct work_struct *work)
+{
+}
+
+static void mock_idle_work_handler(struct work_struct *work)
+{
+}
+
+static struct drm_i915_private *mock_device(void)
+{
+	struct drm_i915_private *i915;
+
+	i915 = kzalloc(sizeof(*i915), GFP_KERNEL);
+	if (!i915)
+		return NULL;
+
+	mutex_init(&i915->drm.struct_mutex);
+
+	init_waitqueue_head(&i915->gpu_error.wait_queue);
+	init_waitqueue_head(&i915->gpu_error.reset_queue);
+
+	ida_init(&i915->context_hw_ida);
+
+	INIT_DELAYED_WORK(&i915->gt.retire_work, mock_retire_work_handler);
+	INIT_DELAYED_WORK(&i915->gt.idle_work, mock_idle_work_handler);
+
+	INIT_LIST_HEAD(&i915->gt.timelines);
+	i915->gt.awake = true;
+
+	mutex_lock(&i915->drm.struct_mutex);
+	i915_gem_timeline_init__global(i915);
+	i915_gem_timeline_init(i915, &i915->ggtt.base.timeline, "mock");
+	mutex_unlock(&i915->drm.struct_mutex);
+
+	i915->requests = KMEM_CACHE(mock_request,
+				    SLAB_HWCACHE_ALIGN |
+				    SLAB_RECLAIM_ACCOUNT |
+				    SLAB_DESTROY_BY_RCU);
+	if (!i915->requests)
+		goto err_device;
+
+	i915->dependencies = KMEM_CACHE(i915_dependency,
+					SLAB_HWCACHE_ALIGN |
+					SLAB_RECLAIM_ACCOUNT);
+	if (!i915->dependencies)
+		goto err_requests;
+
+	mkwrite_device_info(i915)->ring_mask = BIT(0);
+	i915->engine[RCS] = mock_engine(i915, "mock");
+	if (!i915->engine[RCS])
+		goto err_dependencies;
+
+	i915->kernel_context = mock_context(i915, NULL);
+	if (!i915->kernel_context)
+		goto err_engine;
+
+	return i915;
+
+err_engine:
+	mock_engine_free(i915->engine[RCS]);
+err_dependencies:
+	kmem_cache_destroy(i915->dependencies);
+err_requests:
+	kmem_cache_destroy(i915->requests);
+err_device:
+	kfree(i915);
+	return NULL;
+}
+
+static void mock_device_free(struct drm_i915_private *i915)
+{
+	struct intel_engine_cs *engine;
+	enum intel_engine_id id;
+
+	for_each_engine(engine, i915, id)
+		mock_engine_flush(engine);
+
+	mutex_lock(&i915->drm.struct_mutex);
+	i915_gem_retire_requests(i915);
+	mutex_unlock(&i915->drm.struct_mutex);
+
+	cancel_delayed_work_sync(&i915->gt.retire_work);
+	cancel_delayed_work_sync(&i915->gt.idle_work);
+
+	mutex_lock(&i915->drm.struct_mutex);
+	for_each_engine(engine, i915, id)
+		mock_engine_free(engine);
+
+	i915_gem_context_fini(i915);
+
+	i915_gem_timeline_fini(&i915->ggtt.base.timeline);
+	i915_gem_timeline_fini(&i915->gt.global_timeline);
+	mutex_unlock(&i915->drm.struct_mutex);
+
+	kmem_cache_destroy(i915->dependencies);
+	kmem_cache_destroy(i915->requests);
+
+	kfree(i915);
+}
+
+static int igt_add_request(void *ignore)
+{
+	struct drm_i915_private *i915;
+	struct drm_i915_gem_request *request;
+	int err = -ENOMEM;
+
+	i915 = mock_device();
+	if (!i915)
+		goto out;
+
+	mutex_lock(&i915->drm.struct_mutex);
+	request = mock_request(i915->engine[RCS],
+			       i915->kernel_context,
+			       HZ / 10);
+	if (!request)
+		goto out_unlock;
+
+	i915_add_request(request);
+
+	err = 0;
+out_unlock:
+	mutex_unlock(&i915->drm.struct_mutex);
+	mock_device_free(i915);
+out:
+	return err;
+}
+
+int i915_gem_request_selftest(void)
+{
+	static const struct i915_subtest tests[] = {
+		SUBTEST(igt_add_request),
+	};
+
+	return i915_subtests(tests, NULL);
+}
+#endif
diff --git a/drivers/gpu/drm/i915/i915_mock_selftests.h b/drivers/gpu/drm/i915/i915_mock_selftests.h
index 1603fd35d190..9ff379b18c20 100644
--- a/drivers/gpu/drm/i915/i915_mock_selftests.h
+++ b/drivers/gpu/drm/i915/i915_mock_selftests.h
@@ -8,5 +8,6 @@
  *
  * Tests are executed in reverse order by igt/drv_selftest
  */
+selftest(requests, i915_gem_request_selftest)
 selftest(breadcrumbs, intel_breadcrumbs_selftest)
 selftest(sanitycheck, i915_mock_sanitycheck) /* keep last */
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 14/16] drm/i915: Add a simple request selftest for waiting
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (12 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 13/16] drm/i915: Add selftests for i915_gem_request Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-09 15:59   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 15/16] drm/i915: Add a simple fence selftest to i915_gem_request Chris Wilson
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

A trivial kselftest to submit a request and wait upon it.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_gem_request.c | 48 +++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
index 6553457adc77..9ba17d3e35cb 100644
--- a/drivers/gpu/drm/i915/i915_gem_request.c
+++ b/drivers/gpu/drm/i915/i915_gem_request.c
@@ -1601,10 +1601,58 @@ static int igt_add_request(void *ignore)
 	return err;
 }
 
+static int igt_wait_request(void *ignore)
+{
+	struct drm_i915_private *i915;
+	struct drm_i915_gem_request *request;
+	int err = -ENOMEM;
+
+	i915 = mock_device();
+	if (!i915)
+		goto out;
+
+	mutex_lock(&i915->drm.struct_mutex);
+	request = mock_request(i915->engine[RCS],
+			       i915->kernel_context,
+			       HZ / 2);
+	if (!request)
+		goto out_unlock;
+
+	i915_add_request(request);
+
+	if (i915_gem_request_completed(request)) {
+		pr_err("request completed immediately!\n");
+		goto out_unlock;
+	}
+
+	if (i915_wait_request(request, I915_WAIT_LOCKED, HZ / 4) != -ETIME) {
+		pr_err("request wait succeeded (expected tiemout!)\n");
+		goto out_unlock;
+	}
+
+	if (i915_wait_request(request, I915_WAIT_LOCKED, HZ / 2) == -ETIME) {
+		pr_err("request wait timed out!\n");
+		goto out_unlock;
+	}
+
+	if (!i915_gem_request_completed(request)) {
+		pr_err("request not complete after waiting!\n");
+		goto out_unlock;
+	}
+
+	err = 0;
+out_unlock:
+	mutex_unlock(&i915->drm.struct_mutex);
+	mock_device_free(i915);
+out:
+	return err;
+}
+
 int i915_gem_request_selftest(void)
 {
 	static const struct i915_subtest tests[] = {
 		SUBTEST(igt_add_request),
+		SUBTEST(igt_wait_request),
 	};
 
 	return i915_subtests(tests, NULL);
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 15/16] drm/i915: Add a simple fence selftest to i915_gem_request
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (13 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 14/16] drm/i915: Add a simple request selftest for waiting Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-09 16:02   ` Tvrtko Ursulin
  2016-12-07 13:58 ` [PATCH 16/16] drm/i915: Add selftests for object allocation, phys Chris Wilson
                   ` (5 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

Do a quick selftest on in the interoperability of dma_fence_wait on a
i915_gem_request.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_gem_request.c | 51 +++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
index 9ba17d3e35cb..2bde3fc8e8bf 100644
--- a/drivers/gpu/drm/i915/i915_gem_request.c
+++ b/drivers/gpu/drm/i915/i915_gem_request.c
@@ -1648,11 +1648,62 @@ static int igt_wait_request(void *ignore)
 	return err;
 }
 
+static int igt_fence_wait(void *ignore)
+{
+	struct drm_i915_private *i915;
+	struct drm_i915_gem_request *request;
+	int err = -ENOMEM;
+
+	i915 = mock_device();
+	if (!i915)
+		goto out;
+
+	err = -EINVAL;
+	mutex_lock(&i915->drm.struct_mutex);
+	request = mock_request(i915->engine[RCS],
+			       i915->kernel_context,
+			       HZ);
+	if (!request) {
+		mutex_unlock(&i915->drm.struct_mutex);
+		goto out_device;
+	}
+
+	i915_add_request(request);
+	mutex_unlock(&i915->drm.struct_mutex);
+
+	if (dma_fence_is_signaled(&request->fence)) {
+		pr_err("fence signaled immediately!\n");
+		goto out_device;
+	}
+
+	if (dma_fence_wait_timeout(&request->fence, false, 1) != -ETIME) {
+		pr_err("fence wait success after submit (expected timeout)!\n");
+		goto out_device;
+	}
+
+	if (dma_fence_wait_timeout(&request->fence, false, 2 * HZ) <= 0) {
+		pr_err("fence wait timed out (expected success)!\n");
+		goto out_device;
+	}
+
+	if (!dma_fence_is_signaled(&request->fence)) {
+		pr_err("fence unsignaled after waiting!\n");
+		goto out_device;
+	}
+
+	err = 0;
+out_device:
+	mock_device_free(i915);
+out:
+	return err;
+}
+
 int i915_gem_request_selftest(void)
 {
 	static const struct i915_subtest tests[] = {
 		SUBTEST(igt_add_request),
 		SUBTEST(igt_wait_request),
+		SUBTEST(igt_fence_wait),
 	};
 
 	return i915_subtests(tests, NULL);
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 16/16] drm/i915: Add selftests for object allocation, phys
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (14 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 15/16] drm/i915: Add a simple fence selftest to i915_gem_request Chris Wilson
@ 2016-12-07 13:58 ` Chris Wilson
  2016-12-13 17:10   ` Tvrtko Ursulin
  2016-12-07 14:04 ` [RFC] Smattering of selftests Chris Wilson
                   ` (4 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 13:58 UTC (permalink / raw)
  To: intel-gfx

The phys object is a rarely used device (only very old machines require
a chunk of physically contiguous pages for a few hardware interactions).
As such, it is not exercised by CI and to combat that we want to add a
test that exercises the phys object on all platforms.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_gem.c            | 168 +++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/i915_mock_selftests.h |   1 +
 2 files changed, 169 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 9794dd655877..1ab5fdae2d47 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -4949,3 +4949,171 @@ i915_gem_object_get_dma_address(struct drm_i915_gem_object *obj,
 	sg = i915_gem_object_get_sg(obj, n, &offset);
 	return sg_dma_address(sg) + (offset << PAGE_SHIFT);
 }
+
+#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+#include <linux/pm_runtime.h>
+
+#include "i915_selftest.h"
+
+static struct drm_driver mock_driver = {
+	.name = "mock",
+	.driver_features = DRIVER_GEM,
+
+	.gem_close_object = i915_gem_close_object,
+	.gem_free_object_unlocked = i915_gem_free_object,
+};
+
+struct mock_object {
+	struct drm_i915_gem_object base;
+};
+
+static void release_dev(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+
+	kfree(pdev);
+}
+
+static struct drm_i915_private *mock_gem_device(void)
+{
+	struct drm_i915_private *i915;
+	struct pci_dev *pdev;
+	int err;
+
+	i915 = kzalloc(sizeof(*i915), GFP_TEMPORARY);
+	if (!i915)
+		return NULL;
+
+	pdev = kzalloc(sizeof(*pdev), GFP_TEMPORARY);
+	if (!pdev) {
+		kfree(i915);
+		return NULL;
+	}
+
+	device_initialize(&pdev->dev);
+	pdev->dev.release = release_dev;
+	dev_set_name(&pdev->dev, "mock");
+	dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+
+	pm_runtime_dont_use_autosuspend(&pdev->dev);
+	pm_runtime_get_sync(&pdev->dev);
+	pci_set_drvdata(pdev, i915);
+
+	err = drm_dev_init(&i915->drm, &mock_driver, &pdev->dev);
+	if (err) {
+		pr_err("Failed to initialise mock GEM device: err=%d\n", err);
+		put_device(&pdev->dev);
+		kfree(i915);
+		return NULL;
+	}
+	i915->drm.pdev = pdev;
+	i915->drm.dev_private = i915;
+
+	mkwrite_device_info(i915)->gen = -1;
+
+	spin_lock_init(&i915->mm.object_stat_lock);
+
+	INIT_WORK(&i915->mm.free_work, __i915_gem_free_work);
+	init_llist_head(&i915->mm.free_list);
+
+	i915->objects = KMEM_CACHE(mock_object, SLAB_HWCACHE_ALIGN);
+	if (!i915->objects)
+		goto err_device;
+
+	return i915;
+
+err_device:
+	kfree(i915);
+	return NULL;
+}
+
+static void mock_device_free(struct drm_i915_private *i915)
+{
+	struct pci_dev *pdev = i915->drm.pdev;
+
+	rcu_barrier();
+	while (flush_work(&i915->mm.free_work))
+		rcu_barrier();
+
+	drm_dev_unref(&i915->drm);
+	put_device(&pdev->dev);
+}
+
+static int igt_gem_object(void *ignore)
+{
+	struct drm_i915_gem_object *obj;
+	struct drm_i915_private *i915;
+	int err = -ENOMEM;
+
+	i915 = mock_gem_device();
+	if (!i915)
+		goto out;
+
+	obj = i915_gem_object_create(i915, 4096);
+	if (IS_ERR(obj)) {
+		err = PTR_ERR(obj);
+		pr_err("i915_gem_object_create failed, err=%d\n", err);
+		goto out_device;
+	}
+
+	err = 0;
+	i915_gem_object_put(obj);
+out_device:
+	mock_device_free(i915);
+out:
+	return err;
+}
+
+static int igt_phys_object(void *ignore)
+{
+	struct drm_i915_gem_object *obj;
+	struct drm_i915_private *i915;
+	int err = -ENOMEM;
+
+	i915 = mock_gem_device();
+	if (!i915)
+		goto out;
+
+	obj = i915_gem_object_create(i915, 4096);
+	if (IS_ERR(obj)) {
+		err = PTR_ERR(obj);
+		pr_err("i915_gem_object_create failed, err=%d\n", err);
+		goto out_device;
+	}
+
+	err = -EINVAL;
+	mutex_lock(&i915->drm.struct_mutex);
+	err = i915_gem_object_attach_phys(obj, PAGE_SIZE);
+	mutex_unlock(&i915->drm.struct_mutex);
+	if (err) {
+		pr_err("i915_gem_object_attach_phys failed, err=%d\n", err);
+		goto err;
+	}
+
+	if (obj->ops != &i915_gem_phys_ops) {
+		pr_err("i915_gem_object_attach_phys did not create a phys object\n");
+		goto err;
+	}
+
+	/* Make the object work during teardown */
+	obj->mm.dirty = true;
+
+	err = 0;
+err:
+	i915_gem_object_put(obj);
+out_device:
+	mock_device_free(i915);
+out:
+	return err;
+}
+
+int i915_gem_object_selftests(void)
+{
+	static const struct i915_subtest tests[] = {
+		SUBTEST(igt_gem_object),
+		SUBTEST(igt_phys_object),
+	};
+
+	return i915_subtests(tests, NULL);
+}
+#endif
diff --git a/drivers/gpu/drm/i915/i915_mock_selftests.h b/drivers/gpu/drm/i915/i915_mock_selftests.h
index 9ff379b18c20..34f32f777b34 100644
--- a/drivers/gpu/drm/i915/i915_mock_selftests.h
+++ b/drivers/gpu/drm/i915/i915_mock_selftests.h
@@ -8,6 +8,7 @@
  *
  * Tests are executed in reverse order by igt/drv_selftest
  */
+selftest(objects, i915_gem_object_selftests)
 selftest(requests, i915_gem_request_selftest)
 selftest(breadcrumbs, intel_breadcrumbs_selftest)
 selftest(sanitycheck, i915_mock_sanitycheck) /* keep last */
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [RFC] Smattering of selftests
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (15 preceding siblings ...)
  2016-12-07 13:58 ` [PATCH 16/16] drm/i915: Add selftests for object allocation, phys Chris Wilson
@ 2016-12-07 14:04 ` Chris Wilson
  2016-12-07 15:45 ` ✓ Fi.CI.BAT: success for series starting with [01/16] drm/i915: Provide a hook for selftests Patchwork
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 14:04 UTC (permalink / raw)
  To: intel-gfx

On Wed, Dec 07, 2016 at 01:58:17PM +0000, Chris Wilson wrote:
> More changes to GEM are on the cards, so before touching it again, let's
> try and nail down how the internals are meant to work. The advantage
> of mock testing is that we can write a universal test independent of the
> hw (e.g. testing physical object creation) and we can inspect internal
> state which should be able to spot subtle bugs easier than mashing the
> uabi. The downside to mock testing is that it doubles the upfront cost
> of every patch submission (if you change internal state, you're likely
> going to upset a test) and adds maintainance burden tracking change to
> external API (on the other hand it catches those silent changes that
> would lead to broken code).

I should also say it does not alleviate the need for uabi behaviour
testing as well.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 02/16] kselftests: Exercise hw-independent mock tests for i915.ko
  2016-12-07 13:58 ` [PATCH 02/16] kselftests: Exercise hw-independent mock tests for i915.ko Chris Wilson
@ 2016-12-07 14:09   ` Chris Wilson
  2016-12-08 15:50     ` Shuah Khan
  0 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 14:09 UTC (permalink / raw)
  To: intel-gfx; +Cc: Shuah Khan, linux-kselftest

On Wed, Dec 07, 2016 at 01:58:19PM +0000, Chris Wilson wrote:
> Although being a GPU driver most functionality of i915.ko depends upon
> real hardware, many of its internal interfaces can be "mocked" and so
> tested independently of any hardware. Expanding the test coverage is not
> only useful for i915.ko, but should provide some integration tests for
> core infrastructure as well.
> 
> Loading i915.ko with mock_selftests=-1 will cause it to execute its mock
> tests then fail with -ENOTTY. If the driver is already loaded and bound
> to hardware, it requires a few more steps to unbind, and so the simple
> preliminary modprobe -r will fail.

I changed the exit condition to return 0 after successfully completing
the mock tests (when passed mock_selftests=-1) so modprobe reports
success/fail clearly.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* ✓ Fi.CI.BAT: success for series starting with [01/16] drm/i915: Provide a hook for selftests
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (16 preceding siblings ...)
  2016-12-07 14:04 ` [RFC] Smattering of selftests Chris Wilson
@ 2016-12-07 15:45 ` Patchwork
  2016-12-07 18:52 ` [PATCH 1/2] drm/i915: Move uncore selfchecks to late selftest infrastructure Chris Wilson
                   ` (2 subsequent siblings)
  20 siblings, 0 replies; 62+ messages in thread
From: Patchwork @ 2016-12-07 15:45 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [01/16] drm/i915: Provide a hook for selftests
URL   : https://patchwork.freedesktop.org/series/16488/
State : success

== Summary ==

Series 16488v1 Series without cover letter
https://patchwork.freedesktop.org/api/1.0/series/16488/revisions/1/mbox/


fi-bdw-5557u     total:247  pass:219  dwarn:0   dfail:0   fail:0   skip:28 
fi-bsw-n3050     total:247  pass:195  dwarn:0   dfail:0   fail:0   skip:52 
fi-bxt-t5700     total:247  pass:206  dwarn:0   dfail:0   fail:0   skip:41 
fi-byt-j1900     total:247  pass:206  dwarn:0   dfail:0   fail:0   skip:41 
fi-byt-n2820     total:247  pass:202  dwarn:0   dfail:0   fail:0   skip:45 
fi-hsw-4770      total:247  pass:214  dwarn:0   dfail:0   fail:0   skip:33 
fi-hsw-4770r     total:247  pass:214  dwarn:0   dfail:0   fail:0   skip:33 
fi-ilk-650       total:247  pass:181  dwarn:0   dfail:0   fail:0   skip:66 
fi-ivb-3520m     total:247  pass:213  dwarn:0   dfail:0   fail:0   skip:34 
fi-ivb-3770      total:247  pass:212  dwarn:0   dfail:0   fail:0   skip:35 
fi-kbl-7500u     total:247  pass:212  dwarn:0   dfail:0   fail:0   skip:35 
fi-skl-6260u     total:247  pass:220  dwarn:0   dfail:0   fail:0   skip:27 
fi-skl-6700hq    total:247  pass:214  dwarn:0   dfail:0   fail:0   skip:33 
fi-skl-6700k     total:247  pass:210  dwarn:3   dfail:0   fail:0   skip:34 
fi-skl-6770hq    total:247  pass:220  dwarn:0   dfail:0   fail:0   skip:27 
fi-snb-2520m     total:247  pass:202  dwarn:0   dfail:0   fail:0   skip:45 
fi-snb-2600      total:247  pass:201  dwarn:0   dfail:0   fail:0   skip:46 

c5421da3288090b260e882bdfa5754a122ba6263 drm-tip: 2016y-12m-07d-13h-56m-01s UTC integration manifest
3326f57 drm/i915: Add selftests for object allocation, phys
b524565 drm/i915: Add a simple fence selftest to i915_gem_request
76ba51f drm/i915: Add a simple request selftest for waiting
5daaf73 drm/i915: Add selftests for i915_gem_request
1b56da3 drm/i915: Swap if(enable_execlists) in i915_gem_request_alloc for a vfunc
ee582d5 drm/i915/execlists: Request the kernel context be pinned high
e571ba1 drm/i915: Mark the shadow gvt context as closed
4473ea1 drm/i915: Simplify releasing context reference
0a3bad5 drm/i915: Unify active context tracking between legacy/execlists/guc
fe08f31 drm/i915: Move intel_lrc_context_pin() to avoid the forward declaration
1cf26bd drm/i915: Add a reminder that i915_vma_move_to_active() requires struct_mutex
9996660 drm/i915: Add unit tests for the breadcrumb rbtree, wakeups
52bd7cd drm/i915: Add unit tests for the breadcrumb rbtree, completion
e86a257 drm/i915: Add unit tests for the breadcrumb rbtree, insert/remove
58127e4 kselftests: Exercise hw-independent mock tests for i915.ko
0fd7803 drm/i915: Provide a hook for selftests

== Logs ==

For more details see: https://intel-gfx-ci.01.org/CI/Patchwork_3217/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 1/2] drm/i915: Move uncore selfchecks to late selftest infrastructure
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (17 preceding siblings ...)
  2016-12-07 15:45 ` ✓ Fi.CI.BAT: success for series starting with [01/16] drm/i915: Provide a hook for selftests Patchwork
@ 2016-12-07 18:52 ` Chris Wilson
  2016-12-07 18:52   ` [PATCH 2/2] drm/i915: Test all fw tables during mock selftests Chris Wilson
  2016-12-08 11:58   ` [PATCH 1/2] drm/i915: Move uncore selfchecks to late selftest infrastructure Tvrtko Ursulin
  2016-12-07 19:56 ` [RFC] Smattering of selftests Paulo Zanoni
  2016-12-07 20:52 ` ✓ Fi.CI.BAT: success for series starting with [01/16] drm/i915: Provide a hook for selftests (rev2) Patchwork
  20 siblings, 2 replies; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 18:52 UTC (permalink / raw)
  To: intel-gfx

Now that the kselftest infrastructure exists, put it to use and add to
it the existing consistency checks on the fw register lookup tables.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 drivers/gpu/drm/i915/i915_late_selftests.h |   1 +
 drivers/gpu/drm/i915/intel_uncore.c        | 116 +++++++++++++++++------------
 2 files changed, 69 insertions(+), 48 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_late_selftests.h b/drivers/gpu/drm/i915/i915_late_selftests.h
index e6645d08d964..289a651db2fd 100644
--- a/drivers/gpu/drm/i915/i915_late_selftests.h
+++ b/drivers/gpu/drm/i915/i915_late_selftests.h
@@ -8,4 +8,5 @@
  *
  * Tests are executed in reverse order by igt/drv_selftest
  */
+selftest(uncore, intel_uncore_late_selftests)
 selftest(sanitycheck, i915_late_sanitycheck) /* keep last */
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index c1ca4df38dea..bd8436b8f3a4 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -628,33 +628,6 @@ find_fw_domain(struct drm_i915_private *dev_priv, u32 offset)
 	return entry ? entry->domains : 0;
 }
 
-static void
-intel_fw_table_check(struct drm_i915_private *dev_priv)
-{
-	const struct intel_forcewake_range *ranges;
-	unsigned int num_ranges;
-	s32 prev;
-	unsigned int i;
-
-	if (!IS_ENABLED(CONFIG_DRM_I915_DEBUG))
-		return;
-
-	ranges = dev_priv->uncore.fw_domains_table;
-	if (!ranges)
-		return;
-
-	num_ranges = dev_priv->uncore.fw_domains_table_entries;
-
-	for (i = 0, prev = -1; i < num_ranges; i++, ranges++) {
-		WARN_ON_ONCE(IS_GEN9(dev_priv) &&
-			     (prev + 1) != (s32)ranges->start);
-		WARN_ON_ONCE(prev >= (s32)ranges->start);
-		prev = ranges->start;
-		WARN_ON_ONCE(prev >= (s32)ranges->end);
-		prev = ranges->end;
-	}
-}
-
 #define GEN_FW_RANGE(s, e, d) \
 	{ .start = (s), .end = (e), .domains = (d) }
 
@@ -693,23 +666,6 @@ static const i915_reg_t gen8_shadowed_regs[] = {
 	/* TODO: Other registers are not yet used */
 };
 
-static void intel_shadow_table_check(void)
-{
-	const i915_reg_t *reg = gen8_shadowed_regs;
-	s32 prev;
-	u32 offset;
-	unsigned int i;
-
-	if (!IS_ENABLED(CONFIG_DRM_I915_DEBUG))
-		return;
-
-	for (i = 0, prev = -1; i < ARRAY_SIZE(gen8_shadowed_regs); i++, reg++) {
-		offset = i915_mmio_reg_offset(*reg);
-		WARN_ON_ONCE(prev >= (s32)offset);
-		prev = offset;
-	}
-}
-
 static int mmio_reg_cmp(u32 key, const i915_reg_t *reg)
 {
 	u32 offset = i915_mmio_reg_offset(*reg);
@@ -1436,10 +1392,6 @@ void intel_uncore_init(struct drm_i915_private *dev_priv)
 		break;
 	}
 
-	intel_fw_table_check(dev_priv);
-	if (INTEL_GEN(dev_priv) >= 8)
-		intel_shadow_table_check();
-
 	if (intel_vgpu_active(dev_priv)) {
 		ASSIGN_WRITE_MMIO_VFUNCS(vgpu);
 		ASSIGN_READ_MMIO_VFUNCS(vgpu);
@@ -1962,3 +1914,71 @@ intel_uncore_forcewake_for_reg(struct drm_i915_private *dev_priv,
 
 	return fw_domains;
 }
+
+#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+#include "i915_selftest.h"
+
+static int intel_fw_table_check(struct drm_i915_private *i915)
+{
+	const struct intel_forcewake_range *ranges;
+	unsigned int num_ranges, i;
+	s32 prev;
+
+	ranges = i915->uncore.fw_domains_table;
+	if (!ranges)
+		return 0;
+
+	num_ranges = i915->uncore.fw_domains_table_entries;
+	for (i = 0, prev = -1; i < num_ranges; i++, ranges++) {
+		/* Check that the tabke is watertight */
+		if (WARN_ON(IS_GEN9(i915) && (prev + 1) != (s32)ranges->start))
+			return -EINVAL;
+
+		/* Check that the table never goes backwards */
+		if (WARN_ON(prev >= (s32)ranges->start))
+			return -EINVAL;
+
+		/* Check that the entry is valid */
+		if (WARN_ON(ranges->start >= (s32)ranges->end))
+			return -EINVAL;
+
+		prev = ranges->end;
+	}
+
+	return 0;
+}
+
+static int intel_shadow_table_check(void)
+{
+	const i915_reg_t *reg = gen8_shadowed_regs;
+	unsigned int i;
+	s32 prev;
+
+	for (i = 0, prev = -1; i < ARRAY_SIZE(gen8_shadowed_regs); i++, reg++) {
+		u32 offset = i915_mmio_reg_offset(*reg);
+		if (WARN_ON(prev >= (s32)offset))
+			return -EINVAL;
+
+		prev = offset;
+	}
+
+	return 0;
+}
+
+int intel_uncore_late_selftests(struct drm_i915_private *i915)
+{
+	int err;
+
+	err = intel_fw_table_check(i915);
+	if (err)
+		return err;
+
+	if (INTEL_GEN(i915) >= 8) {
+		err = intel_shadow_table_check();
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+#endif
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 2/2] drm/i915: Test all fw tables during mock selftests
  2016-12-07 18:52 ` [PATCH 1/2] drm/i915: Move uncore selfchecks to late selftest infrastructure Chris Wilson
@ 2016-12-07 18:52   ` Chris Wilson
  2016-12-08 12:14     ` Tvrtko Ursulin
                       ` (2 more replies)
  2016-12-08 11:58   ` [PATCH 1/2] drm/i915: Move uncore selfchecks to late selftest infrastructure Tvrtko Ursulin
  1 sibling, 3 replies; 62+ messages in thread
From: Chris Wilson @ 2016-12-07 18:52 UTC (permalink / raw)
  To: intel-gfx

In addition to just testing the fw table we load, during the initial
mock testing we can test that all tables are valid (so the testing is
not limited to just the platforms that load that particular table).

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 drivers/gpu/drm/i915/i915_mock_selftests.h |  1 +
 drivers/gpu/drm/i915/intel_uncore.c        | 43 +++++++++++++++++++++++-------
 2 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_mock_selftests.h b/drivers/gpu/drm/i915/i915_mock_selftests.h
index 34f32f777b34..d2c469eba1dc 100644
--- a/drivers/gpu/drm/i915/i915_mock_selftests.h
+++ b/drivers/gpu/drm/i915/i915_mock_selftests.h
@@ -11,4 +11,5 @@
 selftest(objects, i915_gem_object_selftests)
 selftest(requests, i915_gem_request_selftest)
 selftest(breadcrumbs, intel_breadcrumbs_selftest)
+selftest(uncore, intel_uncore_mock_selftests)
 selftest(sanitycheck, i915_mock_sanitycheck) /* keep last */
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index bd8436b8f3a4..8ad93acbb16f 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -1918,20 +1918,16 @@ intel_uncore_forcewake_for_reg(struct drm_i915_private *dev_priv,
 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
 #include "i915_selftest.h"
 
-static int intel_fw_table_check(struct drm_i915_private *i915)
+static int intel_fw_table_check(const struct intel_forcewake_range *ranges,
+				unsigned int num_ranges,
+				int gen)
 {
-	const struct intel_forcewake_range *ranges;
-	unsigned int num_ranges, i;
+	unsigned int i;
 	s32 prev;
 
-	ranges = i915->uncore.fw_domains_table;
-	if (!ranges)
-		return 0;
-
-	num_ranges = i915->uncore.fw_domains_table_entries;
 	for (i = 0, prev = -1; i < num_ranges; i++, ranges++) {
 		/* Check that the tabke is watertight */
-		if (WARN_ON(IS_GEN9(i915) && (prev + 1) != (s32)ranges->start))
+		if (WARN_ON(gen >= 9 && (prev + 1) != (s32)ranges->start))
 			return -EINVAL;
 
 		/* Check that the table never goes backwards */
@@ -1965,11 +1961,38 @@ static int intel_shadow_table_check(void)
 	return 0;
 }
 
+int intel_uncore_mock_selftests(void)
+{
+	struct {
+		const struct intel_forcewake_range *ranges;
+		unsigned int num_ranges;
+		int gen;
+	} fw[] = {
+		{ __vlv_fw_ranges, ARRAY_SIZE(__vlv_fw_ranges), 7 },
+		{ __chv_fw_ranges, ARRAY_SIZE(__chv_fw_ranges), 8 },
+		{ __gen9_fw_ranges, ARRAY_SIZE(__gen9_fw_ranges), 9 },
+	};
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(fw); i++) {
+		err = intel_fw_table_check(fw[i].ranges,
+					   fw[i].num_ranges,
+					   fw[i].gen);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
 int intel_uncore_late_selftests(struct drm_i915_private *i915)
 {
 	int err;
 
-	err = intel_fw_table_check(i915);
+	/* Confirm the table we load is still valid */
+	err = intel_fw_table_check(i915->uncore.fw_domains_table,
+				   i915->uncore.fw_domains_table_entries,
+				   INTEL_GEN(i915));
 	if (err)
 		return err;
 
-- 
2.11.0

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [RFC] Smattering of selftests
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (18 preceding siblings ...)
  2016-12-07 18:52 ` [PATCH 1/2] drm/i915: Move uncore selfchecks to late selftest infrastructure Chris Wilson
@ 2016-12-07 19:56 ` Paulo Zanoni
  2016-12-07 20:52 ` ✓ Fi.CI.BAT: success for series starting with [01/16] drm/i915: Provide a hook for selftests (rev2) Patchwork
  20 siblings, 0 replies; 62+ messages in thread
From: Paulo Zanoni @ 2016-12-07 19:56 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx

Em Qua, 2016-12-07 às 13:58 +0000, Chris Wilson escreveu:
> More changes to GEM are on the cards, so before touching it again,
> let's
> try and nail down how the internals are meant to work. The advantage
> of mock testing is that we can write a universal test independent of
> the
> hw (e.g. testing physical object creation) and we can inspect
> internal
> state which should be able to spot subtle bugs easier than mashing
> the
> uabi. The downside to mock testing is that it doubles the upfront
> cost
> of every patch submission (if you change internal state, you're
> likely
> going to upset a test) and adds maintainance burden tracking change
> to
> external API (on the other hand it catches those silent changes that
> would lead to broken code).
> 
> In addition to mock tests, I thought it would be useful to start
> writing
> a few run-once tests against real hardware. These are not as
> interesting
> as probing uabi (I don't envisage having kms_flip inside the kernel),
> but we might like to exercise runtime suspend once upon startup, for
> example. Again, we have access to internal state that may prove
> impossible to capture even in debugfs.
> 
> Is this a totally insane and impractical proposal?

In case my opinion matters to anyone, I really like the idea of kernel-
side tests. There's a limit to what we can do from user space, and the
Kernel even has self-tests for other modules, so we have a precedent
here. Also, in the past I did have some ideas for kernel-side test code
that I never implemented since we didn't have this infrastructure (I
forgot what were my ideas). I'd love to be able to contribute to this
when I have appropriate ideas.

Can we somehow make passing these tests a requirement for the CI system
too?

Now, I haven't looked at the specific tests you proposed, so I'm not in
the position to judge sanity in those specific tests. I'm also assuming
that DRM_I915_SELFTESTS=n will keep performance the same as before.

> -Chris
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* ✓ Fi.CI.BAT: success for series starting with [01/16] drm/i915: Provide a hook for selftests (rev2)
  2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
                   ` (19 preceding siblings ...)
  2016-12-07 19:56 ` [RFC] Smattering of selftests Paulo Zanoni
@ 2016-12-07 20:52 ` Patchwork
  20 siblings, 0 replies; 62+ messages in thread
From: Patchwork @ 2016-12-07 20:52 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [01/16] drm/i915: Provide a hook for selftests (rev2)
URL   : https://patchwork.freedesktop.org/series/16488/
State : success

== Summary ==

Series 16488v2 Series without cover letter
https://patchwork.freedesktop.org/api/1.0/series/16488/revisions/2/mbox/


fi-bdw-5557u     total:247  pass:219  dwarn:0   dfail:0   fail:0   skip:28 
fi-bsw-n3050     total:247  pass:195  dwarn:0   dfail:0   fail:0   skip:52 
fi-bxt-t5700     total:247  pass:206  dwarn:0   dfail:0   fail:0   skip:41 
fi-byt-j1900     total:247  pass:206  dwarn:0   dfail:0   fail:0   skip:41 
fi-hsw-4770      total:247  pass:214  dwarn:0   dfail:0   fail:0   skip:33 
fi-hsw-4770r     total:247  pass:214  dwarn:0   dfail:0   fail:0   skip:33 
fi-ilk-650       total:247  pass:181  dwarn:0   dfail:0   fail:0   skip:66 
fi-ivb-3520m     total:247  pass:213  dwarn:0   dfail:0   fail:0   skip:34 
fi-ivb-3770      total:247  pass:212  dwarn:0   dfail:0   fail:0   skip:35 
fi-kbl-7500u     total:247  pass:212  dwarn:0   dfail:0   fail:0   skip:35 
fi-skl-6260u     total:247  pass:220  dwarn:0   dfail:0   fail:0   skip:27 
fi-skl-6700hq    total:247  pass:214  dwarn:0   dfail:0   fail:0   skip:33 
fi-skl-6700k     total:247  pass:210  dwarn:3   dfail:0   fail:0   skip:34 
fi-skl-6770hq    total:247  pass:220  dwarn:0   dfail:0   fail:0   skip:27 
fi-snb-2520m     total:247  pass:202  dwarn:0   dfail:0   fail:0   skip:45 
fi-snb-2600      total:247  pass:201  dwarn:0   dfail:0   fail:0   skip:46 

e46c97f4cfb63328f5bbbc0fb85c3f53703792f2 drm-tip: 2016y-12m-07d-17h-47m-18s UTC integration manifest
6bb976b drm/i915: Provide a hook for selftests

== Logs ==

For more details see: https://intel-gfx-ci.01.org/CI/Patchwork_3225/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 01/16] drm/i915: Provide a hook for selftests
  2016-12-07 13:58 ` [PATCH 01/16] drm/i915: Provide a hook for selftests Chris Wilson
@ 2016-12-08 10:47   ` Tvrtko Ursulin
  2016-12-08 11:15     ` Chris Wilson
  0 siblings, 1 reply; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-08 10:47 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> Some pieces of code are independent of hardware but are very tricky to
> exercise through the normal userspace ABI or via debugfs hooks. Being
> able to create mock unit tests and execute them through CI is vital.
> Start by adding a central point where we can execute unit tests and
> a parameter to enable them. This is disabled by default as the
> expectation is that these tests will occasionally explode.
>
> To facilitate integration with igt, any parameter beginning with
> i915.igt__ is interpreted as a subtest executable independently via
> igt/drv_selftest.
>
> Two classes of selftests are recognised: mock unit tests and integration
> tests. Mock unit tests are run as soon as the module is loaded, before
> the device is probed. At that point there is no driver instantiated and
> all hw interactions must be "mocked". This is very useful for writing
> universal tests to exercise code not typically run on a broad range of
> architectures. Alternatively, you can hook into the late selftests and
> run when the device has been instantiated - hw interactions are real.
>
> v2: Add a macro for compiling conditional code for mock objects inside
> real objects.
> v3: Differentiate between mock unit tests and late integration test.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> #v1
> ---
>  drivers/gpu/drm/i915/Kconfig.debug         |  15 +++
>  drivers/gpu/drm/i915/Makefile              |   1 +
>  drivers/gpu/drm/i915/i915_late_selftests.h |  11 ++
>  drivers/gpu/drm/i915/i915_mock_selftests.h |  11 ++
>  drivers/gpu/drm/i915/i915_params.c         |   8 ++
>  drivers/gpu/drm/i915/i915_params.h         |   4 +
>  drivers/gpu/drm/i915/i915_pci.c            |  19 +++-
>  drivers/gpu/drm/i915/i915_selftest.c       | 173 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/i915/i915_selftest.h       |  77 +++++++++++++
>  9 files changed, 318 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/i915/i915_late_selftests.h
>  create mode 100644 drivers/gpu/drm/i915/i915_mock_selftests.h
>  create mode 100644 drivers/gpu/drm/i915/i915_selftest.c
>  create mode 100644 drivers/gpu/drm/i915/i915_selftest.h
>
> diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug
> index 597648c7a645..76af8774cf70 100644
> --- a/drivers/gpu/drm/i915/Kconfig.debug
> +++ b/drivers/gpu/drm/i915/Kconfig.debug
> @@ -25,6 +25,7 @@ config DRM_I915_DEBUG
>          select DRM_VGEM # used by igt/prime_vgem (dmabuf interop checks)
>          select DRM_DEBUG_MM if DRM=y
>  	select DRM_I915_SW_FENCE_DEBUG_OBJECTS
> +	select DRM_I915_SELFTEST
>          default n
>          help
>            Choose this option to turn on extra driver debugging that may affect
> @@ -58,3 +59,17 @@ config DRM_I915_SW_FENCE_DEBUG_OBJECTS
>            Recommended for driver developers only.
>
>            If in doubt, say "N".
> +
> +config DRM_I915_SELFTEST
> +	bool "Enable selftests upon driver load"
> +	depends on DRM_I915
> +	default n
> +	help
> +	  Choose this option to allow the driver to perform selftests upon
> +	  loading; also requires the i915.selftest=1 module parameter. To
> +	  exit the module after running the selftests (i.e. to prevent normal
> +	  module initialisation afterwards) use i915.selftest=-1.
> +
> +	  Recommended for driver developers only.
> +
> +	  If in doubt, say "N".
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index 3c30916727fb..7c3b4f0c836c 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -114,6 +114,7 @@ i915-y += dvo_ch7017.o \
>
>  # Post-mortem debug and GPU hang state capture
>  i915-$(CONFIG_DRM_I915_CAPTURE_ERROR) += i915_gpu_error.o
> +i915-$(CONFIG_DRM_I915_SELFTEST) += i915_selftest.o
>
>  # virtual gpu code
>  i915-y += i915_vgpu.o
> diff --git a/drivers/gpu/drm/i915/i915_late_selftests.h b/drivers/gpu/drm/i915/i915_late_selftests.h
> new file mode 100644
> index 000000000000..e6645d08d964
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/i915_late_selftests.h
> @@ -0,0 +1,11 @@
> +/* List each unit test as selftest(name, function)
> + *
> + * The name is used as both an enum and expanded as subtest__name to create
> + * a module parameter. It must be unique and legal for a C identifier.
> + *
> + * The function should be of type int function(void). It may be conditionally
> + * compiled using #if IS_ENABLED(DRM_I915_SELFTEST).
> + *
> + * Tests are executed in reverse order by igt/drv_selftest
> + */
> +selftest(sanitycheck, i915_late_sanitycheck) /* keep last */
> diff --git a/drivers/gpu/drm/i915/i915_mock_selftests.h b/drivers/gpu/drm/i915/i915_mock_selftests.h
> new file mode 100644
> index 000000000000..9bead7b496b0
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/i915_mock_selftests.h
> @@ -0,0 +1,11 @@
> +/* List each unit test as selftest(name, function)
> + *
> + * The name is used as both an enum and expanded as subtest__name to create
> + * a module parameter. It must be unique and legal for a C identifier.
> + *
> + * The function should be of type int function(void). It may be conditionally
> + * compiled using #if IS_ENABLED(DRM_I915_SELFTEST).
> + *
> + * Tests are executed in reverse order by igt/drv_selftest
> + */
> +selftest(sanitycheck, i915_mock_sanitycheck) /* keep last */
> diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c
> index 0e280fbd52f1..6d528972e0d4 100644
> --- a/drivers/gpu/drm/i915/i915_params.c
> +++ b/drivers/gpu/drm/i915/i915_params.c
> @@ -243,3 +243,11 @@ MODULE_PARM_DESC(enable_dpcd_backlight,
>  module_param_named(enable_gvt, i915.enable_gvt, bool, 0400);
>  MODULE_PARM_DESC(enable_gvt,
>  	"Enable support for Intel GVT-g graphics virtualization host support(default:false)");
> +
> +#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
> +module_param_named_unsafe(mock_selftests, i915.mock_selftests, int, 0400);
> +MODULE_PARM_DESC(selftest, "Run selftests before loading (0:disabled [default], 1:run tests then load driver, -1:run tests then exit module)");
> +
> +module_param_named_unsafe(late_selftests, i915.late_selftests, int, 0400);
> +MODULE_PARM_DESC(selftest, "Run selftests after driver initialisation (0:disabled [default], 1:run tests then continue, -1:run tests then exit module)");
> +#endif
> diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h
> index 8e433de04679..76a1517bd244 100644
> --- a/drivers/gpu/drm/i915/i915_params.h
> +++ b/drivers/gpu/drm/i915/i915_params.h
> @@ -27,7 +27,11 @@
>
>  #include <linux/cache.h> /* for __read_mostly */
>
> +#include "i915_selftest.h"
> +
>  struct i915_params {
> +	I915_SELFTEST_DECLARE(int mock_selftests);
> +	I915_SELFTEST_DECLARE(int late_selftests);
>  	int modeset;
>  	int panel_ignore_lid;
>  	int semaphores;
> diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
> index f7ec6e944e09..efed080f4b1d 100644
> --- a/drivers/gpu/drm/i915/i915_pci.c
> +++ b/drivers/gpu/drm/i915/i915_pci.c
> @@ -27,6 +27,7 @@
>  #include <linux/vga_switcheroo.h>
>
>  #include "i915_drv.h"
> +#include "i915_selftest.h"
>
>  #define GEN_DEFAULT_PIPEOFFSETS \
>  	.pipe_offsets = { PIPE_A_OFFSET, PIPE_B_OFFSET, \
> @@ -475,6 +476,7 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>  {
>  	struct intel_device_info *intel_info =
>  		(struct intel_device_info *) ent->driver_data;
> +	int err;
>
>  	if (IS_ALPHA_SUPPORT(intel_info) && !i915.alpha_support) {
>  		DRM_INFO("The driver support for your hardware in this kernel version is alpha quality\n"
> @@ -498,7 +500,17 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>  	if (vga_switcheroo_client_probe_defer(pdev))
>  		return -EPROBE_DEFER;
>
> -	return i915_driver_load(pdev, ent);
> +	err = i915_driver_load(pdev, ent);
> +	if (err)
> +		return err;
> +
> +	err = i915_late_selftests(pdev);
> +	if (err) {
> +		i915_driver_unload(pci_get_drvdata(pdev));
> +		return err > 0 ? -ENOTTY : err;

Here ...

> +	}
> +
> +	return 0;
>  }
>
>  static void i915_pci_remove(struct pci_dev *pdev)
> @@ -520,6 +532,11 @@ static struct pci_driver i915_pci_driver = {
>  static int __init i915_init(void)
>  {
>  	bool use_kms = true;
> +	int err;
> +
> +	err = i915_mock_selftests();
> +	if (err)
> +		return err > 0 ? 0 : err;

... and here, the return conversion is different but in the 
implementation is the same. It is probably wrong or at least confusing 
so it would be good to make it the same.

>
>  	/*
>  	 * Enable KMS by default, unless explicitly overriden by
> diff --git a/drivers/gpu/drm/i915/i915_selftest.c b/drivers/gpu/drm/i915/i915_selftest.c
> new file mode 100644
> index 000000000000..8b1b3c76855c
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/i915_selftest.c
> @@ -0,0 +1,173 @@
> +/*
> + * Copyright © 2016 Intel Corporation
> + *
> + * 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 "i915_drv.h"
> +#include "i915_selftest.h"
> +
> +int i915_mock_sanitycheck(void)
> +{
> +	pr_info("i915: %s() - ok!\n", __func__);
> +	return 0;
> +}
> +
> +int i915_late_sanitycheck(struct drm_i915_private *i915)
> +{
> +	pr_info("%s: %s() - ok!\n", i915->drm.driver->name, __func__);
> +	return 0;
> +}
> +
> +enum {
> +#define selftest(name, func) mock_##name,
> +#include "i915_mock_selftests.h"
> +#undef selftest
> +};
> +enum {
> +#define selftest(name, func) late_##name,
> +#include "i915_late_selftests.h"
> +#undef selftest
> +};
> +
> +struct i915_selftest {
> +	bool enabled;
> +	const char *name;
> +	union {
> +		int (*mock)(void);
> +		int (*late)(struct drm_i915_private *);
> +	};
> +};
> +
> +#define selftest(n, f) [mock_##n] = { .name = #n, .mock = f },
> +static struct i915_selftest mock_selftests[] = {
> +#include "i915_mock_selftests.h"
> +};
> +#undef selftest
> +
> +#define selftest(n, f) [late_##n] = { .name = #n, .late = f },
> +static struct i915_selftest late_selftests[] = {
> +#include "i915_late_selftests.h"
> +};
> +#undef selftest
> +
> +#define selftest(n, func) \
> +module_param_named(igt__mock_##n, mock_selftests[mock_##n].enabled, bool, 0400);
> +#include "i915_mock_selftests.h"
> +#undef selftest
> +
> +#define selftest(n, func) \
> +module_param_named(igt__late_##n, late_selftests[late_##n].enabled, bool, 0400);
> +#include "i915_late_selftests.h"
> +#undef selftest
> +
> +static void set_default_test_all(struct i915_selftest *st, unsigned long count)
> +{
> +	unsigned long i;
> +
> +	for (i = 0; i < count; i++)
> +		if (st[i].enabled)
> +			return;
> +
> +	for (i = 0; i < count; i++)
> +		st[i].enabled = true;
> +}
> +
> +static int run_selftests(struct i915_selftest *st,
> +			 unsigned long count,
> +			 void *data)
> +{
> +	int err;
> +
> +	set_default_test_all(st, count);
> +
> +	for (; count--; st++) {
> +		if (!st->enabled)
> +			continue;
> +
> +		pr_debug("i915: Running %s\n", st->name);
> +		if (data)
> +			err = st->late(data);
> +		else
> +			err = st->mock();
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +int i915_mock_selftests(void)
> +{
> +	int err;
> +
> +	if (!i915.mock_selftests)
> +		return 0;
> +
> +	pr_info("i915: Performing mock selftests\n");
> +	err = run_selftests(mock_selftests, ARRAY_SIZE(mock_selftests), NULL);

I think a WARN_ON to ensure the test return code is aligned with what 
the framework expects is needed here. (Since it is returned unmodified 
to the caller.) This was if a test fails with a positive error the 
driver would still load above.

> +	if (err)
> +		return err;
> +
> +	if (i915.mock_selftests < 0)
> +		return 1;
> +
> +	return 0;
> +}
> +
> +int i915_late_selftests(struct pci_dev *pdev)
> +{
> +	int err;
> +
> +	if (!i915.late_selftests)
> +		return 0;
> +
> +	pr_info("i915: Performing late selftests\n");
> +	err = run_selftests(late_selftests,
> +			    ARRAY_SIZE(late_selftests),
> +			    pci_get_drvdata(pdev));

This passes in drm_device pointer while the tests expect i915. Just a 
to_i915 might be needed?

> +	if (err)
> +		return err;
> +
> +	if (i915.late_selftests < 0)
> +		return 1;
> +
> +	return 0;
> +}
> +
> +int __i915_subtests(const char *caller,
> +		    const struct i915_subtest *st,
> +		    int count,
> +		    void *data)
> +{
> +	int err;
> +
> +	for (; count--; st++) {
> +		pr_debug("i915: Running %s/%s\n", caller, st->name);
> +		err = st->func(data);
> +		if (err) {
> +			pr_err("i915/%s: %s failed with error %d\n",
> +			       caller, st->name, err);
> +			return err;
> +		}
> +	}
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/i915/i915_selftest.h b/drivers/gpu/drm/i915/i915_selftest.h
> new file mode 100644
> index 000000000000..86876ba52d5e
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/i915_selftest.h
> @@ -0,0 +1,77 @@
> +/*
> + * Copyright © 2016 Intel Corporation
> + *
> + * 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 __I915_SELFTEST_H__
> +#define __I915_SELFTEST_H__
> +
> +struct pci_dev;
> +struct drm_i915_private;
> +
> +#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
> +int i915_mock_selftests(void);
> +int i915_late_selftests(struct pci_dev *pdev);
> +#else
> +static inline int i915_mock_selftests(void) { return 0; }
> +static inline int i915_late_selftests(struct pci_dev *pdev) { return 0; }
> +#endif
> +
> +/* We extract the function declarations from i915_mock_selftests.h and
> + * i915_late_selftests.h Add your unit test declarations there!
> + *
> + * Mock unit tests are run very early upon module load, before the driver
> + * is probed. All hardware interactions, as well as other subsystems, must
> + * be "mocked".
> + *
> + * Late unit tests are run after the driver is loaded - all hardware
> + * interactions are real.
> + */
> +#define selftest(name, func) int func(void);
> +#include "i915_mock_selftests.h"
> +#undef selftest
> +#define selftest(name, func) int func(struct drm_i915_private *i915);
> +#include "i915_late_selftests.h"
> +#undef selftest
> +
> +struct i915_subtest {
> +	int (*func)(void *data);
> +	const char *name;
> +};
> +
> +int __i915_subtests(const char *caller,
> +		    const struct i915_subtest *st,
> +		    int count,
> +		    void *data);
> +#define i915_subtests(T, data) \
> +	__i915_subtests(__func__, T, ARRAY_SIZE(T), data)
> +
> +#define SUBTEST(x) { x, #x }
> +
> +#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
> +#define I915_SELFTEST_DECLARE(x) x
> +#define I915_SELFTEST_ONLY(x) unlikely(x)
> +#else
> +#define I915_SELFTEST_DECLARE(x)
> +#define I915_SELFTEST_ONLY(x) 0
> +#endif
> +
> +#endif /* __I915_SELFTEST_H__ */
>

The rest looks ok.

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 03/16] drm/i915: Add unit tests for the breadcrumb rbtree, insert/remove
  2016-12-07 13:58 ` [PATCH 03/16] drm/i915: Add unit tests for the breadcrumb rbtree, insert/remove Chris Wilson
@ 2016-12-08 11:00   ` Tvrtko Ursulin
  0 siblings, 0 replies; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-08 11:00 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> First retroactive test, make sure that the waiters are in global seqno
> order after random inserts and removals.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/i915_mock_selftests.h |   1 +
>  drivers/gpu/drm/i915/intel_breadcrumbs.c   | 205 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/i915/intel_ringbuffer.h    |   2 +
>  3 files changed, 208 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/i915_mock_selftests.h b/drivers/gpu/drm/i915/i915_mock_selftests.h
> index 9bead7b496b0..1603fd35d190 100644
> --- a/drivers/gpu/drm/i915/i915_mock_selftests.h
> +++ b/drivers/gpu/drm/i915/i915_mock_selftests.h
> @@ -8,4 +8,5 @@
>   *
>   * Tests are executed in reverse order by igt/drv_selftest
>   */
> +selftest(breadcrumbs, intel_breadcrumbs_selftest)
>  selftest(sanitycheck, i915_mock_sanitycheck) /* keep last */
> diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c
> index 53ae7884babd..c768608974e1 100644
> --- a/drivers/gpu/drm/i915/intel_breadcrumbs.c
> +++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c
> @@ -109,6 +109,18 @@ static void __intel_breadcrumbs_enable_irq(struct intel_breadcrumbs *b)
>  	if (b->rpm_wakelock)
>  		return;
>
> +	if (I915_SELFTEST_ONLY(b->mock)) {
> +		/* For our mock objects we want to avoid interaction
> +		 * with the real hardware (which is not set up). So
> +		 * we simply pretend we have enabled the powerwell
> +		 * and the irq, and leave it up to the mock
> +		 * implementation to call intel_engine_wakeup()
> +		 * itself when it wants to simulate a user interrupt,

Another broken key? :) (,)

> +		 */
> +		b->rpm_wakelock = true;
> +		return;
> +	}
> +
>  	/* Since we are waiting on a request, the GPU should be busy
>  	 * and should have its own rpm reference. For completeness,
>  	 * record an rpm reference for ourselves to cover the
> @@ -143,6 +155,11 @@ static void __intel_breadcrumbs_disable_irq(struct intel_breadcrumbs *b)
>  	if (!b->rpm_wakelock)
>  		return;
>
> +	if (I915_SELFTEST_ONLY(b->mock)) {
> +		b->rpm_wakelock = false;
> +		return;
> +	}
> +
>  	if (b->irq_enabled) {
>  		irq_disable(engine);
>  		b->irq_enabled = false;
> @@ -661,3 +678,191 @@ unsigned int intel_breadcrumbs_busy(struct drm_i915_private *i915)
>
>  	return mask;
>  }
> +
> +#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
> +#include <linux/random.h>
> +
> +#include "i915_selftest.h"
> +
> +static struct intel_engine_cs *mock_engine(const char *name)
> +{
> +	struct intel_engine_cs *engine;
> +	static int id;
> +
> +	engine = kzalloc(sizeof(*engine) + 4096, GFP_TEMPORARY);

Nitpick but PAGE_SIZE would be better if you care the change it.

> +	if (!engine)
> +		return NULL;
> +
> +	/* minimal engine setup for seqno */
> +	engine->name = name;
> +	engine->id = id++;
> +	engine->status_page.page_addr = (void *)(engine + 1);
> +
> +	/* minimal breadcrumbs init */
> +	spin_lock_init(&engine->breadcrumbs.lock);
> +	engine->breadcrumbs.mock = true;
> +
> +	return engine;
> +}
> +
> +static int *get_random_order(int count)
> +{
> +	int *order;
> +	int n, r, tmp;
> +
> +	order = kmalloc_array(count, sizeof(*order), GFP_TEMPORARY);
> +	if (!order)
> +		return order;
> +
> +	for (n = 0; n < count; n++)
> +		order[n] = n;
> +
> +	for (n = count - 1; n > 1; n--) {
> +		r = get_random_int() % (n + 1);
> +		if (r != n) {
> +			tmp = order[n];
> +			order[n] = order[r];
> +			order[r] = tmp;
> +		}
> +	}
> +
> +	return order;
> +}
> +
> +static int check_rbtree(struct intel_engine_cs *engine,
> +			const unsigned long *bitmap,
> +			const struct intel_wait *waiters,
> +			const int count)
> +{
> +	struct intel_breadcrumbs *b = &engine->breadcrumbs;
> +	struct rb_node *rb;
> +	int n;
> +
> +	if (&b->first_wait->node != rb_first(&b->waiters)) {
> +		pr_err("First waiter does not match first element of wait-tree\n");
> +		return -EINVAL;
> +	}
> +
> +	n = find_first_bit(bitmap, count);
> +	for (rb = rb_first(&b->waiters); rb; rb = rb_next(rb)) {
> +		struct intel_wait *w = container_of(rb, typeof(*w), node);
> +		int idx = w - waiters;
> +
> +		if (!test_bit(idx, bitmap)) {
> +			pr_err("waiter[%d, seqno=%d] removed but still in wait-tree\n",
> +			       idx, w->seqno);
> +			return -EINVAL;
> +		}
> +
> +		if (n != idx) {
> +			pr_err("waiter[%d, seqno=%d] does not match expected next element in tree [%d]\n",
> +			       idx, w->seqno, n);
> +			return -EINVAL;
> +		}
> +
> +		n = find_next_bit(bitmap, count, n + 1);
> +	}
> +
> +	return 0;
> +}
> +
> +static int check_rbtree_empty(struct intel_engine_cs *engine)
> +{
> +	struct intel_breadcrumbs *b = &engine->breadcrumbs;
> +
> +	if (b->first_wait) {
> +		pr_err("Empty breadcrumbs still has a waiter\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!RB_EMPTY_ROOT(&b->waiters)) {
> +		pr_err("Empty breadcrumbs, but wait-tree not empty\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int igt_random_insert_remove(void *ignore)
> +{
> +	struct intel_engine_cs *engine;
> +	struct intel_wait *waiters;
> +	const int count = 4096;
> +	int *in_order, *out_order;
> +	unsigned long *bitmap;
> +	int err = -ENOMEM;
> +	int n;
> +
> +	engine = mock_engine("mock");
> +	if (!engine)
> +		goto out;
> +
> +	waiters = drm_malloc_gfp(count, sizeof(*waiters), GFP_TEMPORARY);
> +	if (!waiters)
> +		goto out_engines;
> +
> +	bitmap = kcalloc(DIV_ROUND_UP(count, BITS_PER_LONG), sizeof(*bitmap),
> +			 GFP_TEMPORARY);
> +	if (!bitmap)
> +		goto out_waiters;
> +
> +	in_order = get_random_order(count);
> +	if (!in_order)
> +		goto out_bitmap;
> +
> +	out_order = get_random_order(count);
> +	if (!out_order)
> +		goto out_order;
> +
> +	for (n = 0; n < count; n++)
> +		intel_wait_init(&waiters[n], 0x1000 + n);
> +
> +	err = check_rbtree(engine, bitmap, waiters, count);
> +	if (err)
> +		goto err;
> +
> +	for (n = 0; n < count; n++) {
> +		int i = in_order[n];
> +
> +		intel_engine_add_wait(engine, &waiters[i]);
> +		__set_bit(i, bitmap);
> +
> +		err = check_rbtree(engine, bitmap, waiters, count);
> +		if (err)
> +			goto err;
> +	}
> +	for (n = 0; n < count; n++) {
> +		int i = out_order[n];
> +
> +		intel_engine_remove_wait(engine, &waiters[i]);
> +		__clear_bit(i, bitmap);
> +
> +		err = check_rbtree(engine, bitmap, waiters, count);
> +		if (err)
> +			goto err;
> +	}
> +
> +	err = check_rbtree_empty(engine);
> +err:
> +	kfree(out_order);
> +out_order:
> +	kfree(in_order);
> +out_bitmap:
> +	kfree(bitmap);
> +out_waiters:
> +	drm_free_large(waiters);
> +out_engines:
> +	kfree(engine);
> +out:
> +	return err;
> +}
> +
> +int intel_breadcrumbs_selftest(void)
> +{
> +	static const struct i915_subtest tests[] = {
> +		SUBTEST(igt_random_insert_remove),
> +	};
> +
> +	return i915_subtests(tests, NULL);
> +}
> +#endif
> diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
> index 3f43adefd1c0..d8b066fd5dcf 100644
> --- a/drivers/gpu/drm/i915/intel_ringbuffer.h
> +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
> @@ -5,6 +5,7 @@
>  #include "i915_gem_batch_pool.h"
>  #include "i915_gem_request.h"
>  #include "i915_gem_timeline.h"
> +#include "i915_selftest.h"
>
>  #define I915_CMD_HASH_ORDER 9
>
> @@ -244,6 +245,7 @@ struct intel_engine_cs {
>
>  		bool irq_enabled : 1;
>  		bool rpm_wakelock : 1;
> +		I915_SELFTEST_DECLARE(bool mock : 1);
>  	} breadcrumbs;
>
>  	/*
>

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 01/16] drm/i915: Provide a hook for selftests
  2016-12-08 10:47   ` Tvrtko Ursulin
@ 2016-12-08 11:15     ` Chris Wilson
  2016-12-08 12:30       ` Tvrtko Ursulin
  0 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-08 11:15 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

On Thu, Dec 08, 2016 at 10:47:52AM +0000, Tvrtko Ursulin wrote:
> On 07/12/2016 13:58, Chris Wilson wrote:
> >-	return i915_driver_load(pdev, ent);
> >+	err = i915_driver_load(pdev, ent);
> >+	if (err)
> >+		return err;
> >+
> >+	err = i915_late_selftests(pdev);
> >+	if (err) {
> >+		i915_driver_unload(pci_get_drvdata(pdev));
> >+		return err > 0 ? -ENOTTY : err;
> 
> Here ...
> 
> >+	}
> >+
> >+	return 0;
> > }
> >
> > static void i915_pci_remove(struct pci_dev *pdev)
> >@@ -520,6 +532,11 @@ static struct pci_driver i915_pci_driver = {
> > static int __init i915_init(void)
> > {
> > 	bool use_kms = true;
> >+	int err;
> >+
> >+	err = i915_mock_selftests();
> >+	if (err)
> >+		return err > 0 ? 0 : err;
> 
> ... and here, the return conversion is different but in the
> implementation is the same. It is probably wrong or at least
> confusing so it would be good to make it the same.

The return convention is tricky because ->probe and module_init want
different things. To cancel the probe, we have to return an error value
(-ENOTTY). But for integration with kselftest we want the module load to
report success (kselftest effectively uses
	modprobe i915 mock_selftests=-1 || exit "FAIL"
). Our igt_kselftest has access to the error code from the syscall and
so can differentiate better than the kselftest.sh, but that shell script
is the lowest common demoninator
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 04/16] drm/i915: Add unit tests for the breadcrumb rbtree, completion
  2016-12-07 13:58 ` [PATCH 04/16] drm/i915: Add unit tests for the breadcrumb rbtree, completion Chris Wilson
@ 2016-12-08 11:47   ` Tvrtko Ursulin
  0 siblings, 0 replies; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-08 11:47 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> Second retroactive test, make sure that the waiters are removed from the
> global wait-tree when their seqno completes.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/intel_breadcrumbs.c | 110 +++++++++++++++++++++++++++++++
>  1 file changed, 110 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c
> index c768608974e1..fc950f7ff322 100644
> --- a/drivers/gpu/drm/i915/intel_breadcrumbs.c
> +++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c
> @@ -705,6 +705,12 @@ static struct intel_engine_cs *mock_engine(const char *name)
>  	return engine;
>  }
>
> +static void mock_seqno_advance(struct intel_engine_cs *engine, u32 seqno)
> +{
> +	intel_write_status_page(engine, I915_GEM_HWS_INDEX, seqno);
> +	intel_engine_wakeup(engine);
> +}
> +
>  static int *get_random_order(int count)
>  {
>  	int *order;
> @@ -766,6 +772,27 @@ static int check_rbtree(struct intel_engine_cs *engine,
>  	return 0;
>  }
>
> +static int check_completion(struct intel_engine_cs *engine,
> +			    const unsigned long *bitmap,
> +			    const struct intel_wait *waiters,
> +			    const int count)
> +{
> +	int n;
> +
> +	for (n = 0; n < count; n++) {
> +		if (intel_wait_complete(&waiters[n]) != !!test_bit(n, bitmap))
> +			continue;
> +
> +		pr_err("waiter[%d, seqno=%d] is %s, but expected %s\n",
> +		       n, waiters[n].seqno,
> +		       intel_wait_complete(&waiters[n]) ? "complete" : "active",
> +		       test_bit(n, bitmap) ? "active" : "complete");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  static int check_rbtree_empty(struct intel_engine_cs *engine)
>  {
>  	struct intel_breadcrumbs *b = &engine->breadcrumbs;
> @@ -857,10 +884,93 @@ static int igt_random_insert_remove(void *ignore)
>  	return err;
>  }
>
> +static int igt_insert_complete(void *ignore)
> +{
> +	struct intel_engine_cs *engine;
> +	struct intel_wait *waiters;
> +	const int count = 4096;
> +	unsigned long *bitmap;
> +	int err = -ENOMEM;
> +	int n, m;
> +
> +	engine = mock_engine("mock");
> +	if (!engine)
> +		goto out;
> +
> +	waiters = drm_malloc_gfp(count, sizeof(*waiters), GFP_TEMPORARY);
> +	if (!waiters)
> +		goto out_engines;
> +
> +	bitmap = kcalloc(DIV_ROUND_UP(count, BITS_PER_LONG), sizeof(*bitmap),
> +			 GFP_TEMPORARY);
> +	if (!bitmap)
> +		goto out_waiters;
> +
> +	for (n = 0; n < count; n++) {
> +		intel_wait_init(&waiters[n], n + 0x1000);

Maybe const int seqno_start = 0x1000 since there are multiple users.

> +		intel_engine_add_wait(engine, &waiters[n]);
> +		__set_bit(n, bitmap);
> +	}
> +	err = check_rbtree(engine, bitmap, waiters, count);
> +	if (err)
> +		goto err;
> +
> +	for (n = 0; n < count; n = m) {
> +		int seqno = 2 * n;
> +
> +		GEM_BUG_ON(find_first_bit(bitmap, count) != n);
> +
> +		if (intel_wait_complete(&waiters[n])) {
> +			pr_err("waiter[%d, seqno=%d] completed too early\n",
> +			       n, waiters[n].seqno);
> +			err = -EINVAL;
> +			goto err;
> +		}
> +
> +		/* complete the following waiters */
> +		mock_seqno_advance(engine, seqno + 0x1000);
> +		for (m = n; m <= seqno; m++) {
> +			if (m == count)
> +				break;
> +
> +			GEM_BUG_ON(!test_bit(m, bitmap));
> +			__clear_bit(m, bitmap);
> +		}
> +
> +		intel_engine_remove_wait(engine, &waiters[n]);
> +		RB_CLEAR_NODE(&waiters[n].node);
> +
> +		err = check_rbtree(engine, bitmap, waiters, count);
> +		if (err) {
> +			pr_err("rbtree corrupt after seqno advance to %d\n",
> +			       seqno + 0x1000);
> +			goto err;
> +		}
> +
> +		err = check_completion(engine, bitmap, waiters, count);
> +		if (err) {
> +			pr_err("completions after seqno advance to %d failed\n",
> +			       seqno + 0x1000);
> +			goto err;
> +		}
> +	}
> +
> +	err = check_rbtree_empty(engine);
> +err:
> +	kfree(bitmap);
> +out_waiters:
> +	drm_free_large(waiters);
> +out_engines:
> +	kfree(engine);
> +out:
> +	return err;
> +}
> +
>  int intel_breadcrumbs_selftest(void)
>  {
>  	static const struct i915_subtest tests[] = {
>  		SUBTEST(igt_random_insert_remove),
> +		SUBTEST(igt_insert_complete),
>  	};
>
>  	return i915_subtests(tests, NULL);
>

Looks OK.

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 1/2] drm/i915: Move uncore selfchecks to late selftest infrastructure
  2016-12-07 18:52 ` [PATCH 1/2] drm/i915: Move uncore selfchecks to late selftest infrastructure Chris Wilson
  2016-12-07 18:52   ` [PATCH 2/2] drm/i915: Test all fw tables during mock selftests Chris Wilson
@ 2016-12-08 11:58   ` Tvrtko Ursulin
  1 sibling, 0 replies; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-08 11:58 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 18:52, Chris Wilson wrote:
> Now that the kselftest infrastructure exists, put it to use and add to
> it the existing consistency checks on the fw register lookup tables.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> ---
>  drivers/gpu/drm/i915/i915_late_selftests.h |   1 +
>  drivers/gpu/drm/i915/intel_uncore.c        | 116 +++++++++++++++++------------
>  2 files changed, 69 insertions(+), 48 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_late_selftests.h b/drivers/gpu/drm/i915/i915_late_selftests.h
> index e6645d08d964..289a651db2fd 100644
> --- a/drivers/gpu/drm/i915/i915_late_selftests.h
> +++ b/drivers/gpu/drm/i915/i915_late_selftests.h
> @@ -8,4 +8,5 @@
>   *
>   * Tests are executed in reverse order by igt/drv_selftest
>   */
> +selftest(uncore, intel_uncore_late_selftests)
>  selftest(sanitycheck, i915_late_sanitycheck) /* keep last */
> diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
> index c1ca4df38dea..bd8436b8f3a4 100644
> --- a/drivers/gpu/drm/i915/intel_uncore.c
> +++ b/drivers/gpu/drm/i915/intel_uncore.c
> @@ -628,33 +628,6 @@ find_fw_domain(struct drm_i915_private *dev_priv, u32 offset)
>  	return entry ? entry->domains : 0;
>  }
>
> -static void
> -intel_fw_table_check(struct drm_i915_private *dev_priv)
> -{
> -	const struct intel_forcewake_range *ranges;
> -	unsigned int num_ranges;
> -	s32 prev;
> -	unsigned int i;
> -
> -	if (!IS_ENABLED(CONFIG_DRM_I915_DEBUG))
> -		return;
> -
> -	ranges = dev_priv->uncore.fw_domains_table;
> -	if (!ranges)
> -		return;
> -
> -	num_ranges = dev_priv->uncore.fw_domains_table_entries;
> -
> -	for (i = 0, prev = -1; i < num_ranges; i++, ranges++) {
> -		WARN_ON_ONCE(IS_GEN9(dev_priv) &&
> -			     (prev + 1) != (s32)ranges->start);
> -		WARN_ON_ONCE(prev >= (s32)ranges->start);
> -		prev = ranges->start;
> -		WARN_ON_ONCE(prev >= (s32)ranges->end);
> -		prev = ranges->end;
> -	}
> -}
> -
>  #define GEN_FW_RANGE(s, e, d) \
>  	{ .start = (s), .end = (e), .domains = (d) }
>
> @@ -693,23 +666,6 @@ static const i915_reg_t gen8_shadowed_regs[] = {
>  	/* TODO: Other registers are not yet used */
>  };
>
> -static void intel_shadow_table_check(void)
> -{
> -	const i915_reg_t *reg = gen8_shadowed_regs;
> -	s32 prev;
> -	u32 offset;
> -	unsigned int i;
> -
> -	if (!IS_ENABLED(CONFIG_DRM_I915_DEBUG))
> -		return;
> -
> -	for (i = 0, prev = -1; i < ARRAY_SIZE(gen8_shadowed_regs); i++, reg++) {
> -		offset = i915_mmio_reg_offset(*reg);
> -		WARN_ON_ONCE(prev >= (s32)offset);
> -		prev = offset;
> -	}
> -}
> -
>  static int mmio_reg_cmp(u32 key, const i915_reg_t *reg)
>  {
>  	u32 offset = i915_mmio_reg_offset(*reg);
> @@ -1436,10 +1392,6 @@ void intel_uncore_init(struct drm_i915_private *dev_priv)
>  		break;
>  	}
>
> -	intel_fw_table_check(dev_priv);
> -	if (INTEL_GEN(dev_priv) >= 8)
> -		intel_shadow_table_check();
> -
>  	if (intel_vgpu_active(dev_priv)) {
>  		ASSIGN_WRITE_MMIO_VFUNCS(vgpu);
>  		ASSIGN_READ_MMIO_VFUNCS(vgpu);
> @@ -1962,3 +1914,71 @@ intel_uncore_forcewake_for_reg(struct drm_i915_private *dev_priv,
>
>  	return fw_domains;
>  }
> +
> +#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
> +#include "i915_selftest.h"
> +
> +static int intel_fw_table_check(struct drm_i915_private *i915)
> +{
> +	const struct intel_forcewake_range *ranges;
> +	unsigned int num_ranges, i;
> +	s32 prev;
> +
> +	ranges = i915->uncore.fw_domains_table;
> +	if (!ranges)
> +		return 0;
> +
> +	num_ranges = i915->uncore.fw_domains_table_entries;
> +	for (i = 0, prev = -1; i < num_ranges; i++, ranges++) {
> +		/* Check that the tabke is watertight */

table

> +		if (WARN_ON(IS_GEN9(i915) && (prev + 1) != (s32)ranges->start))
> +			return -EINVAL;
> +
> +		/* Check that the table never goes backwards */
> +		if (WARN_ON(prev >= (s32)ranges->start))
> +			return -EINVAL;
> +
> +		/* Check that the entry is valid */
> +		if (WARN_ON(ranges->start >= (s32)ranges->end))

Comparison between signed and unsigned?

> +			return -EINVAL;
> +
> +		prev = ranges->end;
> +	}
> +
> +	return 0;
> +}
> +
> +static int intel_shadow_table_check(void)
> +{
> +	const i915_reg_t *reg = gen8_shadowed_regs;
> +	unsigned int i;
> +	s32 prev;
> +
> +	for (i = 0, prev = -1; i < ARRAY_SIZE(gen8_shadowed_regs); i++, reg++) {
> +		u32 offset = i915_mmio_reg_offset(*reg);
> +		if (WARN_ON(prev >= (s32)offset))
> +			return -EINVAL;
> +
> +		prev = offset;
> +	}
> +
> +	return 0;
> +}
> +
> +int intel_uncore_late_selftests(struct drm_i915_private *i915)
> +{
> +	int err;
> +
> +	err = intel_fw_table_check(i915);
> +	if (err)
> +		return err;
> +
> +	if (INTEL_GEN(i915) >= 8) {

Could just always check it, the fw table is identically unused on some 
platforms.

> +		err = intel_shadow_table_check();
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +#endif
>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 2/2] drm/i915: Test all fw tables during mock selftests
  2016-12-07 18:52   ` [PATCH 2/2] drm/i915: Test all fw tables during mock selftests Chris Wilson
@ 2016-12-08 12:14     ` Tvrtko Ursulin
  2016-12-08 12:28       ` Chris Wilson
  2016-12-08 13:11     ` Joonas Lahtinen
  2016-12-08 16:52     ` Tvrtko Ursulin
  2 siblings, 1 reply; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-08 12:14 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 18:52, Chris Wilson wrote:
> In addition to just testing the fw table we load, during the initial
> mock testing we can test that all tables are valid (so the testing is
> not limited to just the platforms that load that particular table).
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> ---
>  drivers/gpu/drm/i915/i915_mock_selftests.h |  1 +
>  drivers/gpu/drm/i915/intel_uncore.c        | 43 +++++++++++++++++++++++-------
>  2 files changed, 34 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_mock_selftests.h b/drivers/gpu/drm/i915/i915_mock_selftests.h
> index 34f32f777b34..d2c469eba1dc 100644
> --- a/drivers/gpu/drm/i915/i915_mock_selftests.h
> +++ b/drivers/gpu/drm/i915/i915_mock_selftests.h
> @@ -11,4 +11,5 @@
>  selftest(objects, i915_gem_object_selftests)
>  selftest(requests, i915_gem_request_selftest)
>  selftest(breadcrumbs, intel_breadcrumbs_selftest)
> +selftest(uncore, intel_uncore_mock_selftests)
>  selftest(sanitycheck, i915_mock_sanitycheck) /* keep last */
> diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
> index bd8436b8f3a4..8ad93acbb16f 100644
> --- a/drivers/gpu/drm/i915/intel_uncore.c
> +++ b/drivers/gpu/drm/i915/intel_uncore.c
> @@ -1918,20 +1918,16 @@ intel_uncore_forcewake_for_reg(struct drm_i915_private *dev_priv,
>  #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
>  #include "i915_selftest.h"
>
> -static int intel_fw_table_check(struct drm_i915_private *i915)
> +static int intel_fw_table_check(const struct intel_forcewake_range *ranges,
> +				unsigned int num_ranges,
> +				int gen)
>  {
> -	const struct intel_forcewake_range *ranges;
> -	unsigned int num_ranges, i;
> +	unsigned int i;
>  	s32 prev;
>
> -	ranges = i915->uncore.fw_domains_table;
> -	if (!ranges)
> -		return 0;
> -
> -	num_ranges = i915->uncore.fw_domains_table_entries;
>  	for (i = 0, prev = -1; i < num_ranges; i++, ranges++) {
>  		/* Check that the tabke is watertight */
> -		if (WARN_ON(IS_GEN9(i915) && (prev + 1) != (s32)ranges->start))
> +		if (WARN_ON(gen >= 9 && (prev + 1) != (s32)ranges->start))
>  			return -EINVAL;
>
>  		/* Check that the table never goes backwards */
> @@ -1965,11 +1961,38 @@ static int intel_shadow_table_check(void)
>  	return 0;
>  }
>
> +int intel_uncore_mock_selftests(void)
> +{
> +	struct {
> +		const struct intel_forcewake_range *ranges;
> +		unsigned int num_ranges;
> +		int gen;
> +	} fw[] = {
> +		{ __vlv_fw_ranges, ARRAY_SIZE(__vlv_fw_ranges), 7 },
> +		{ __chv_fw_ranges, ARRAY_SIZE(__chv_fw_ranges), 8 },
> +		{ __gen9_fw_ranges, ARRAY_SIZE(__gen9_fw_ranges), 9 },

Tables are not per gen so this is a bit confusing. I don't have a much 
better idea though. Apart from replacing "int gen" with "bool 
test_watertight"?

> +	};
> +	int err, i;
> +
> +	for (i = 0; i < ARRAY_SIZE(fw); i++) {
> +		err = intel_fw_table_check(fw[i].ranges,
> +					   fw[i].num_ranges,
> +					   fw[i].gen);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
>  int intel_uncore_late_selftests(struct drm_i915_private *i915)
>  {
>  	int err;
>
> -	err = intel_fw_table_check(i915);
> +	/* Confirm the table we load is still valid */
> +	err = intel_fw_table_check(i915->uncore.fw_domains_table,
> +				   i915->uncore.fw_domains_table_entries,
> +				   INTEL_GEN(i915));
>  	if (err)
>  		return err;
>
>

Either way,

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 2/2] drm/i915: Test all fw tables during mock selftests
  2016-12-08 12:14     ` Tvrtko Ursulin
@ 2016-12-08 12:28       ` Chris Wilson
  0 siblings, 0 replies; 62+ messages in thread
From: Chris Wilson @ 2016-12-08 12:28 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

On Thu, Dec 08, 2016 at 12:14:10PM +0000, Tvrtko Ursulin wrote:
> >-static int intel_fw_table_check(struct drm_i915_private *i915)
> >+static int intel_fw_table_check(const struct intel_forcewake_range *ranges,
> >+				unsigned int num_ranges,
> >+				int gen)
> > {
> >-	const struct intel_forcewake_range *ranges;
> >-	unsigned int num_ranges, i;
> >+	unsigned int i;
> > 	s32 prev;
> >
> >-	ranges = i915->uncore.fw_domains_table;
> >-	if (!ranges)
> >-		return 0;
> >-
> >-	num_ranges = i915->uncore.fw_domains_table_entries;
> > 	for (i = 0, prev = -1; i < num_ranges; i++, ranges++) {
> > 		/* Check that the tabke is watertight */
> >-		if (WARN_ON(IS_GEN9(i915) && (prev + 1) != (s32)ranges->start))
> >+		if (WARN_ON(gen >= 9 && (prev + 1) != (s32)ranges->start))
> > 			return -EINVAL;
> >
> > 		/* Check that the table never goes backwards */
> >@@ -1965,11 +1961,38 @@ static int intel_shadow_table_check(void)
> > 	return 0;
> > }
> >
> >+int intel_uncore_mock_selftests(void)
> >+{
> >+	struct {
> >+		const struct intel_forcewake_range *ranges;
> >+		unsigned int num_ranges;
> >+		int gen;
> >+	} fw[] = {
> >+		{ __vlv_fw_ranges, ARRAY_SIZE(__vlv_fw_ranges), 7 },
> >+		{ __chv_fw_ranges, ARRAY_SIZE(__chv_fw_ranges), 8 },
> >+		{ __gen9_fw_ranges, ARRAY_SIZE(__gen9_fw_ranges), 9 },
> 
> Tables are not per gen so this is a bit confusing. I don't have a
> much better idea though. Apart from replacing "int gen" with "bool
> test_watertight"?

bool is_watertight is a good suggestion.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 01/16] drm/i915: Provide a hook for selftests
  2016-12-08 11:15     ` Chris Wilson
@ 2016-12-08 12:30       ` Tvrtko Ursulin
  2016-12-08 12:40         ` Chris Wilson
  0 siblings, 1 reply; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-08 12:30 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 08/12/2016 11:15, Chris Wilson wrote:
> On Thu, Dec 08, 2016 at 10:47:52AM +0000, Tvrtko Ursulin wrote:
>> On 07/12/2016 13:58, Chris Wilson wrote:
>>> -	return i915_driver_load(pdev, ent);
>>> +	err = i915_driver_load(pdev, ent);
>>> +	if (err)
>>> +		return err;
>>> +
>>> +	err = i915_late_selftests(pdev);
>>> +	if (err) {
>>> +		i915_driver_unload(pci_get_drvdata(pdev));
>>> +		return err > 0 ? -ENOTTY : err;
>>
>> Here ...
>>
>>> +	}
>>> +
>>> +	return 0;
>>> }
>>>
>>> static void i915_pci_remove(struct pci_dev *pdev)
>>> @@ -520,6 +532,11 @@ static struct pci_driver i915_pci_driver = {
>>> static int __init i915_init(void)
>>> {
>>> 	bool use_kms = true;
>>> +	int err;
>>> +
>>> +	err = i915_mock_selftests();
>>> +	if (err)
>>> +		return err > 0 ? 0 : err;
>>
>> ... and here, the return conversion is different but in the
>> implementation is the same. It is probably wrong or at least
>> confusing so it would be good to make it the same.
>
> The return convention is tricky because ->probe and module_init want
> different things. To cancel the probe, we have to return an error value
> (-ENOTTY). But for integration with kselftest we want the module load to
> report success (kselftest effectively uses
> 	modprobe i915 mock_selftests=-1 || exit "FAIL"
> ). Our igt_kselftest has access to the error code from the syscall and
> so can differentiate better than the kselftest.sh, but that shell script
> is the lowest common demoninator

Ah ok.

But you agree on the point of validating that subtest do not return 
positive error codes in order not to interfere with the behaviour set by 
module parameters?

Regards,

Tvrtko



_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 01/16] drm/i915: Provide a hook for selftests
  2016-12-08 12:30       ` Tvrtko Ursulin
@ 2016-12-08 12:40         ` Chris Wilson
  0 siblings, 0 replies; 62+ messages in thread
From: Chris Wilson @ 2016-12-08 12:40 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

On Thu, Dec 08, 2016 at 12:30:23PM +0000, Tvrtko Ursulin wrote:
> But you agree on the point of validating that subtest do not return
> positive error codes in order not to interfere with the behaviour
> set by module parameters?

Yup, checking that the tests don't return our magic values is a good
idea.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 2/2] drm/i915: Test all fw tables during mock selftests
  2016-12-07 18:52   ` [PATCH 2/2] drm/i915: Test all fw tables during mock selftests Chris Wilson
  2016-12-08 12:14     ` Tvrtko Ursulin
@ 2016-12-08 13:11     ` Joonas Lahtinen
  2016-12-08 16:52     ` Tvrtko Ursulin
  2 siblings, 0 replies; 62+ messages in thread
From: Joonas Lahtinen @ 2016-12-08 13:11 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx

On ke, 2016-12-07 at 18:52 +0000, Chris Wilson wrote:
> In addition to just testing the fw table we load, during the initial
> mock testing we can test that all tables are valid (so the testing is
> not limited to just the platforms that load that particular table).
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

<SNIP>

> +static int intel_fw_table_check(const struct intel_forcewake_range *ranges,
> +				unsigned int num_ranges,
> +				int gen)
>  {
> -	const struct intel_forcewake_range *ranges;
> -	unsigned int num_ranges, i;
> +	unsigned int i;
>  	s32 prev;
>  
> -	ranges = i915->uncore.fw_domains_table;
> -	if (!ranges)
> -		return 0;
> -
> -	num_ranges = i915->uncore.fw_domains_table_entries;
>  	for (i = 0, prev = -1; i < num_ranges; i++, ranges++) {
>  		/* Check that the tabke is watertight */

s/tabke/table/

>  int intel_uncore_late_selftests(struct drm_i915_private *i915)
>  {
>  	int err;
>  
> -	err = intel_fw_table_check(i915);
> +	/* Confirm the table we load is still valid */
> +	err = intel_fw_table_check(i915->uncore.fw_domains_table,
> +				   i915->uncore.fw_domains_table_entries,
> +				   INTEL_GEN(i915));
>  	if (err)
>  		return err;

Maybe the mock test should skip .gen == INTEL_GEN(i915) to avoid
duplicate coverage on full run.
 
Regards, Joonas
-- 
Joonas Lahtinen
Open Source Technology Center
Intel Corporation
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 02/16] kselftests: Exercise hw-independent mock tests for i915.ko
  2016-12-07 14:09   ` Chris Wilson
@ 2016-12-08 15:50     ` Shuah Khan
  2016-12-08 16:01       ` Chris Wilson
  0 siblings, 1 reply; 62+ messages in thread
From: Shuah Khan @ 2016-12-08 15:50 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx, linux-kselftest; +Cc: Shuah Khan

On 12/07/2016 07:09 AM, Chris Wilson wrote:
> On Wed, Dec 07, 2016 at 01:58:19PM +0000, Chris Wilson wrote:
>> Although being a GPU driver most functionality of i915.ko depends upon
>> real hardware, many of its internal interfaces can be "mocked" and so
>> tested independently of any hardware. Expanding the test coverage is not
>> only useful for i915.ko, but should provide some integration tests for
>> core infrastructure as well.
>>
>> Loading i915.ko with mock_selftests=-1 will cause it to execute its mock
>> tests then fail with -ENOTTY. If the driver is already loaded and bound
>> to hardware, it requires a few more steps to unbind, and so the simple
>> preliminary modprobe -r will fail.
> 
> I changed the exit condition to return 0 after successfully completing
> the mock tests (when passed mock_selftests=-1) so modprobe reports
> success/fail clearly.
> -Chris
> 

Hi Chris,

Is this patch slated to go in through drm tree with the other patches
in the series. Just make sure you create .gitignore for any generated
files that should be ignored by git

thanks,
-- Shuah
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 02/16] kselftests: Exercise hw-independent mock tests for i915.ko
  2016-12-08 15:50     ` Shuah Khan
@ 2016-12-08 16:01       ` Chris Wilson
  2016-12-08 16:44         ` Shuah Khan
  0 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-08 16:01 UTC (permalink / raw)
  To: Shuah Khan; +Cc: intel-gfx, linux-kselftest

On Thu, Dec 08, 2016 at 08:50:17AM -0700, Shuah Khan wrote:
> On 12/07/2016 07:09 AM, Chris Wilson wrote:
> > On Wed, Dec 07, 2016 at 01:58:19PM +0000, Chris Wilson wrote:
> >> Although being a GPU driver most functionality of i915.ko depends upon
> >> real hardware, many of its internal interfaces can be "mocked" and so
> >> tested independently of any hardware. Expanding the test coverage is not
> >> only useful for i915.ko, but should provide some integration tests for
> >> core infrastructure as well.
> >>
> >> Loading i915.ko with mock_selftests=-1 will cause it to execute its mock
> >> tests then fail with -ENOTTY. If the driver is already loaded and bound
> >> to hardware, it requires a few more steps to unbind, and so the simple
> >> preliminary modprobe -r will fail.
> > 
> > I changed the exit condition to return 0 after successfully completing
> > the mock tests (when passed mock_selftests=-1) so modprobe reports
> > success/fail clearly.
> > -Chris
> > 
> 
> Hi Chris,
> 
> Is this patch slated to go in through drm tree with the other patches
> in the series. Just make sure you create .gitignore for any generated
> files that should be ignored by git

If you agree to having it turn up via the drm tree, then yes :)

Is there any guideline on test behaviour I can read? Is there a
particular directory structure you like? I have a few selftests planned
for i915.ko and core drm.ko, and maybe a few other gpus will join in
over time (certainly should write a few for drivers/gpu/drm/vgem/vgem.ko as
well), is selftests/drivers/gpu/(drm.sh, i915.sh, vgem.sh, etc)
suitable?
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 02/16] kselftests: Exercise hw-independent mock tests for i915.ko
  2016-12-08 16:01       ` Chris Wilson
@ 2016-12-08 16:44         ` Shuah Khan
  0 siblings, 0 replies; 62+ messages in thread
From: Shuah Khan @ 2016-12-08 16:44 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx, linux-kselftest, Shuah Khan, Shuah Khan

Hi Chris,

On 12/08/2016 09:01 AM, Chris Wilson wrote:
> On Thu, Dec 08, 2016 at 08:50:17AM -0700, Shuah Khan wrote:
>> On 12/07/2016 07:09 AM, Chris Wilson wrote:
>>> On Wed, Dec 07, 2016 at 01:58:19PM +0000, Chris Wilson wrote:
>>>> Although being a GPU driver most functionality of i915.ko depends upon
>>>> real hardware, many of its internal interfaces can be "mocked" and so
>>>> tested independently of any hardware. Expanding the test coverage is not
>>>> only useful for i915.ko, but should provide some integration tests for
>>>> core infrastructure as well.
>>>>
>>>> Loading i915.ko with mock_selftests=-1 will cause it to execute its mock
>>>> tests then fail with -ENOTTY. If the driver is already loaded and bound
>>>> to hardware, it requires a few more steps to unbind, and so the simple
>>>> preliminary modprobe -r will fail.
>>>
>>> I changed the exit condition to return 0 after successfully completing
>>> the mock tests (when passed mock_selftests=-1) so modprobe reports
>>> success/fail clearly.
>>> -Chris
>>>
>>
>> Hi Chris,
>>
>> Is this patch slated to go in through drm tree with the other patches
>> in the series. Just make sure you create .gitignore for any generated
>> files that should be ignored by git
> 
> If you agree to having it turn up via the drm tree, then yes :)

When there is a dependency, it makes sense for it to go through drm. Looks
like there are no dependencies in this script.

It is now applied to linux-kselftest next for 4.10-rc1

> 
> Is there any guideline on test behaviour I can read? Is there a
> particular directory structure you like? I have a few selftests planned
> for i915.ko and core drm.ko, and maybe a few other gpus will join in
> over time (certainly should write a few for drivers/gpu/drm/vgem/vgem.ko as
> well), is selftests/drivers/gpu/(drm.sh, i915.sh, vgem.sh, etc)
> suitable?

This test isn't hooked into kselftest run and install as is. I applied
it anyway since it doesn't impact anything.

If you want these tests run during kselftest run, then add a Makefile for
run and install. Good examples for your case are:

tools/testing/selftests/firmware
tools/testing/selftests/net

grep modprobe */* will show you other tests that depend on module being
loaded.

General guide is if a test can't be run because of module not being there
then SKIP without impacting the run with a clear message that test is skipped
because module isn't there is good.

Kselftest lacks driver test support at the moment and any efforts to add tests
is greatly appreciated. So please send patches.

thanks,
-- Shuah



_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 2/2] drm/i915: Test all fw tables during mock selftests
  2016-12-07 18:52   ` [PATCH 2/2] drm/i915: Test all fw tables during mock selftests Chris Wilson
  2016-12-08 12:14     ` Tvrtko Ursulin
  2016-12-08 13:11     ` Joonas Lahtinen
@ 2016-12-08 16:52     ` Tvrtko Ursulin
  2016-12-08 22:29       ` Chris Wilson
  2 siblings, 1 reply; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-08 16:52 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 18:52, Chris Wilson wrote:
> In addition to just testing the fw table we load, during the initial
> mock testing we can test that all tables are valid (so the testing is
> not limited to just the platforms that load that particular table).
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> ---
>  drivers/gpu/drm/i915/i915_mock_selftests.h |  1 +
>  drivers/gpu/drm/i915/intel_uncore.c        | 43 +++++++++++++++++++++++-------
>  2 files changed, 34 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_mock_selftests.h b/drivers/gpu/drm/i915/i915_mock_selftests.h
> index 34f32f777b34..d2c469eba1dc 100644
> --- a/drivers/gpu/drm/i915/i915_mock_selftests.h
> +++ b/drivers/gpu/drm/i915/i915_mock_selftests.h
> @@ -11,4 +11,5 @@
>  selftest(objects, i915_gem_object_selftests)
>  selftest(requests, i915_gem_request_selftest)
>  selftest(breadcrumbs, intel_breadcrumbs_selftest)
> +selftest(uncore, intel_uncore_mock_selftests)
>  selftest(sanitycheck, i915_mock_sanitycheck) /* keep last */
> diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
> index bd8436b8f3a4..8ad93acbb16f 100644
> --- a/drivers/gpu/drm/i915/intel_uncore.c
> +++ b/drivers/gpu/drm/i915/intel_uncore.c
> @@ -1918,20 +1918,16 @@ intel_uncore_forcewake_for_reg(struct drm_i915_private *dev_priv,
>  #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
>  #include "i915_selftest.h"
>
> -static int intel_fw_table_check(struct drm_i915_private *i915)
> +static int intel_fw_table_check(const struct intel_forcewake_range *ranges,
> +				unsigned int num_ranges,
> +				int gen)
>  {
> -	const struct intel_forcewake_range *ranges;
> -	unsigned int num_ranges, i;
> +	unsigned int i;
>  	s32 prev;
>
> -	ranges = i915->uncore.fw_domains_table;
> -	if (!ranges)
> -		return 0;
> -
> -	num_ranges = i915->uncore.fw_domains_table_entries;
>  	for (i = 0, prev = -1; i < num_ranges; i++, ranges++) {
>  		/* Check that the tabke is watertight */
> -		if (WARN_ON(IS_GEN9(i915) && (prev + 1) != (s32)ranges->start))
> +		if (WARN_ON(gen >= 9 && (prev + 1) != (s32)ranges->start))
>  			return -EINVAL;
>
>  		/* Check that the table never goes backwards */
> @@ -1965,11 +1961,38 @@ static int intel_shadow_table_check(void)
>  	return 0;
>  }
>
> +int intel_uncore_mock_selftests(void)
> +{
> +	struct {
> +		const struct intel_forcewake_range *ranges;
> +		unsigned int num_ranges;
> +		int gen;
> +	} fw[] = {
> +		{ __vlv_fw_ranges, ARRAY_SIZE(__vlv_fw_ranges), 7 },
> +		{ __chv_fw_ranges, ARRAY_SIZE(__chv_fw_ranges), 8 },
> +		{ __gen9_fw_ranges, ARRAY_SIZE(__gen9_fw_ranges), 9 },
> +	};
> +	int err, i;
> +
> +	for (i = 0; i < ARRAY_SIZE(fw); i++) {
> +		err = intel_fw_table_check(fw[i].ranges,
> +					   fw[i].num_ranges,
> +					   fw[i].gen);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
>  int intel_uncore_late_selftests(struct drm_i915_private *i915)
>  {
>  	int err;
>
> -	err = intel_fw_table_check(i915);
> +	/* Confirm the table we load is still valid */
> +	err = intel_fw_table_check(i915->uncore.fw_domains_table,
> +				   i915->uncore.fw_domains_table_entries,
> +				   INTEL_GEN(i915));
>  	if (err)
>  		return err;

Idea for another late test:

for (offset = 0; offset < 0x40000; offset++) {
	fw_domain = intel_uncore_forcewake_for_reg(dev_priv, { .reg = offset }, 
FW_REG_READ | FW_REG_WRITE);
	if (WARN_ON(fw_domain & ~dev_priv->uncore.fw_domains))
		return -EINVAL;
}

And then we could convert the existing related WARNs in the live code 
base to GEM_WARN_ONs.

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 05/16] drm/i915: Add unit tests for the breadcrumb rbtree, wakeups
  2016-12-07 13:58 ` [PATCH 05/16] drm/i915: Add unit tests for the breadcrumb rbtree, wakeups Chris Wilson
@ 2016-12-08 17:38   ` Tvrtko Ursulin
  2016-12-08 21:04     ` Chris Wilson
  0 siblings, 1 reply; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-08 17:38 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> Third retroactive test, make sure that the seqno waiters are woken.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/intel_breadcrumbs.c | 171 +++++++++++++++++++++++++++++++
>  1 file changed, 171 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c
> index fc950f7ff322..1374a54e41c9 100644
> --- a/drivers/gpu/drm/i915/intel_breadcrumbs.c
> +++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c
> @@ -966,11 +966,182 @@ static int igt_insert_complete(void *ignore)
>  	return err;
>  }
>
> +struct igt_wakeup {
> +	struct task_struct *tsk;
> +	atomic_t *ready, *set, *done;
> +	struct intel_engine_cs *engine;
> +	unsigned long flags;
> +	wait_queue_head_t *wq;
> +	u32 seqno;
> +};
> +
> +static int wait_atomic(atomic_t *p)
> +{
> +	schedule();
> +	return 0;
> +}
> +
> +static int wait_atomic_timeout(atomic_t *p)
> +{
> +	return schedule_timeout(10 * HZ) ? 0 : -ETIMEDOUT;
> +}
> +
> +static int igt_wakeup_thread(void *arg)
> +{
> +	struct igt_wakeup *w = arg;
> +	struct intel_wait wait;
> +
> +	while (!kthread_should_stop()) {
> +		DEFINE_WAIT(ready);
> +
> +		for (;;) {
> +			prepare_to_wait(w->wq, &ready, TASK_INTERRUPTIBLE);
> +			if (atomic_read(w->ready) == 0)
> +				break;
> +
> +			schedule();
> +		}
> +		finish_wait(w->wq, &ready);

Have to say this is the first time I've learnt about wake_up_atomic_t & 
co. You couldn't use wait_on_atomic_t instead of the loop above?

> +		if (atomic_dec_and_test(w->set))
> +			wake_up_atomic_t(w->set);
> +

Okay, all the threads have observed that all other threads have been 
started, yes?

> +		if (test_bit(0, &w->flags))
> +			break;

One thread failed to start = bailout.

Do you intend to use the flags for something more which precludes a more 
descriptive name for its single purpose here?

> +
> +		intel_wait_init(&wait, w->seqno);
> +		intel_engine_add_wait(w->engine, &wait);
> +		for (;;) {
> +			set_current_state(TASK_UNINTERRUPTIBLE);
> +			if (i915_seqno_passed(intel_engine_get_seqno(w->engine),
> +					      w->seqno))
> +				break;
> +
> +			schedule();
> +		}
> +		intel_engine_remove_wait(w->engine, &wait);
> +		__set_current_state(TASK_RUNNING);
> +
> +		if (atomic_dec_and_test(w->done))
> +			wake_up_atomic_t(w->done);
> +	}
> +
> +	if (atomic_dec_and_test(w->done))
> +		wake_up_atomic_t(w->done);

Must be a special reason done is decremented in the loop and outside the 
loop?

> +	return 0;
> +}
> +
> +static void igt_wake_all_sync(atomic_t *ready,
> +			      atomic_t *set,
> +			      atomic_t *done,
> +			      wait_queue_head_t *wq,
> +			      int count)
> +{
> +	atomic_set(set, count);
> +	atomic_set(done, count);
> +
> +	atomic_set(ready, 0);
> +	wake_up_all(wq);
> +
> +	wait_on_atomic_t(set, wait_atomic, TASK_UNINTERRUPTIBLE);
> +	atomic_set(ready, count);
> +}
> +
> +static int igt_wakeup(void *ignore)
> +{
> +	const int state = TASK_UNINTERRUPTIBLE;
> +	struct intel_engine_cs *engine;
> +	struct igt_wakeup *waiters;
> +	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
> +	const int count = 4096;
> +	const u32 max_seqno = count / 4;
> +	atomic_t ready, set, done;
> +	int err = -ENOMEM;
> +	int n, step;
> +
> +	engine = mock_engine("mock");
> +	if (!engine)
> +		goto out;
> +
> +	waiters = drm_malloc_gfp(count, sizeof(*waiters), GFP_TEMPORARY);
> +	if (!waiters)
> +		goto out_engines;
> +
> +	atomic_set(&ready, count);
> +	for (n = 0; n < count; n++) {
> +		waiters[n].wq = &wq;
> +		waiters[n].ready = &ready;
> +		waiters[n].set = &set;
> +		waiters[n].done = &done;
> +		waiters[n].engine = engine;
> +		waiters[n].flags = 0;
> +
> +		waiters[n].tsk = kthread_run(igt_wakeup_thread, &waiters[n],
> +					     "i915/igt:%d", n);
> +		if (IS_ERR(waiters[n].tsk))
> +			goto out_waiters;
> +
> +		get_task_struct(waiters[n].tsk);
> +	}
> +

It is time to start documenting the tests I think via nice comments at 
strategic places. Probably a short commentary on the test as a whole and 
then separately at interesting steps.

> +	for (step = 1; step <= max_seqno; step <<= 1) {
> +		u32 seqno;
> +
> +		for (n = 0; n < count; n++)
> +			waiters[n].seqno = 1 + get_random_int() % max_seqno;

So you have 4096 waiters but some are waiting on the same seqno, since 
there are at most 1024 unique seqnos. Took me a while to figure this one 
out.

> +
> +		mock_seqno_advance(engine, 0);
> +		igt_wake_all_sync(&ready, &set, &done, &wq, count);
> +
> +		for (seqno = 1; seqno <= max_seqno + step; seqno += step) {

First step wakes up all seqnos one by one, the other steps do it in 
chunks with a larger and larger skip. All the way to waking the whole 
bunch at once?

> +			usleep_range(50, 500);

Why sleep? It should work without it, no?

> +			mock_seqno_advance(engine, seqno);
> +		}
> +		GEM_BUG_ON(intel_engine_get_seqno(engine) < 1 + max_seqno);
> +
> +		err = wait_on_atomic_t(&done, wait_atomic_timeout, state);
> +		if (err) {
> +			pr_err("Timed out waiting for %d remaining waiters\n",
> +			       atomic_read(&done));
> +			break;
> +		}
> +
> +		err = check_rbtree_empty(engine);
> +		if (err)
> +			break;
> +	}
> +
> +out_waiters:
> +	for (n = 0; n < count; n++) {
> +		if (IS_ERR(waiters[n].tsk))
> +			break;
> +
> +		set_bit(0, &waiters[n].flags);
> +	}
> +
> +	igt_wake_all_sync(&ready, &set, &done, &wq, n);
> +	wait_on_atomic_t(&done, wait_atomic, state);

C-O-M-M-E-N-T-S! :D

> +
> +	for (n = 0; n < count; n++) {
> +		if (IS_ERR(waiters[n].tsk))
> +			break;
> +
> +		kthread_stop(waiters[n].tsk);
> +		put_task_struct(waiters[n].tsk);
> +	}
> +
> +	drm_free_large(waiters);
> +out_engines:
> +	kfree(engine);
> +out:
> +	return err;
> +}
> +
>  int intel_breadcrumbs_selftest(void)
>  {
>  	static const struct i915_subtest tests[] = {
>  		SUBTEST(igt_random_insert_remove),
>  		SUBTEST(igt_insert_complete),
> +		SUBTEST(igt_wakeup),
>  	};
>
>  	return i915_subtests(tests, NULL);
>

Phew, looks mostly OK. I think only one thing I am unsure of.

This was quite smart, please start adding comments when you come up with 
similar things. :)

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 06/16] drm/i915: Add a reminder that i915_vma_move_to_active() requires struct_mutex
  2016-12-07 13:58 ` [PATCH 06/16] drm/i915: Add a reminder that i915_vma_move_to_active() requires struct_mutex Chris Wilson
@ 2016-12-08 17:40   ` Tvrtko Ursulin
  0 siblings, 0 replies; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-08 17:40 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> i915_vma_move_to_active() requires the struct_mutex for serialisation
> with retirement, so mark it up with lockdep_assert_held().
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/i915_gem_execbuffer.c | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
> index d665a33229bd..c64438f8171c 100644
> --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
> @@ -1259,6 +1259,7 @@ void i915_vma_move_to_active(struct i915_vma *vma,
>  	struct drm_i915_gem_object *obj = vma->obj;
>  	const unsigned int idx = req->engine->id;
>
> +	lockdep_assert_held(&req->i915->drm.struct_mutex);
>  	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
>
>  	/* Add a reference if we're newly entering the active list.
>

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 07/16] drm/i915: Move intel_lrc_context_pin() to avoid the forward declaration
  2016-12-07 13:58 ` [PATCH 07/16] drm/i915: Move intel_lrc_context_pin() to avoid the forward declaration Chris Wilson
@ 2016-12-08 17:45   ` Tvrtko Ursulin
  2016-12-08 20:55     ` Chris Wilson
  0 siblings, 1 reply; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-08 17:45 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> Just a simple move to avoid a forward declaration.

Patch subject is wrong - you are moving 
intel_logical_ring_alloc_request_extras.

> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/intel_lrc.c | 132 +++++++++++++++++++--------------------
>  1 file changed, 65 insertions(+), 67 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
> index 8b412880e88c..5cabe4e9d22f 100644
> --- a/drivers/gpu/drm/i915/intel_lrc.c
> +++ b/drivers/gpu/drm/i915/intel_lrc.c
> @@ -230,8 +230,6 @@ enum {
>
>  static int execlists_context_deferred_alloc(struct i915_gem_context *ctx,
>  					    struct intel_engine_cs *engine);
> -static int intel_lr_context_pin(struct i915_gem_context *ctx,
> -				struct intel_engine_cs *engine);
>  static void execlists_init_reg_state(u32 *reg_state,
>  				     struct i915_gem_context *ctx,
>  				     struct intel_engine_cs *engine,
> @@ -774,71 +772,6 @@ static void execlists_schedule(struct drm_i915_gem_request *request, int prio)
>  	/* XXX Do we need to preempt to make room for us and our deps? */
>  }
>
> -int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request)
> -{
> -	struct intel_engine_cs *engine = request->engine;
> -	struct intel_context *ce = &request->ctx->engine[engine->id];
> -	int ret;
> -
> -	/* Flush enough space to reduce the likelihood of waiting after
> -	 * we start building the request - in which case we will just
> -	 * have to repeat work.
> -	 */
> -	request->reserved_space += EXECLISTS_REQUEST_SIZE;
> -
> -	if (!ce->state) {
> -		ret = execlists_context_deferred_alloc(request->ctx, engine);
> -		if (ret)
> -			return ret;
> -	}
> -
> -	request->ring = ce->ring;
> -
> -	ret = intel_lr_context_pin(request->ctx, engine);
> -	if (ret)
> -		return ret;
> -
> -	if (i915.enable_guc_submission) {
> -		/*
> -		 * Check that the GuC has space for the request before
> -		 * going any further, as the i915_add_request() call
> -		 * later on mustn't fail ...
> -		 */
> -		ret = i915_guc_wq_reserve(request);
> -		if (ret)
> -			goto err_unpin;
> -	}
> -
> -	ret = intel_ring_begin(request, 0);
> -	if (ret)
> -		goto err_unreserve;
> -
> -	if (!ce->initialised) {
> -		ret = engine->init_context(request);
> -		if (ret)
> -			goto err_unreserve;
> -
> -		ce->initialised = true;
> -	}
> -
> -	/* Note that after this point, we have committed to using
> -	 * this request as it is being used to both track the
> -	 * state of engine initialisation and liveness of the
> -	 * golden renderstate above. Think twice before you try
> -	 * to cancel/unwind this request now.
> -	 */
> -
> -	request->reserved_space -= EXECLISTS_REQUEST_SIZE;
> -	return 0;
> -
> -err_unreserve:
> -	if (i915.enable_guc_submission)
> -		i915_guc_wq_unreserve(request);
> -err_unpin:
> -	intel_lr_context_unpin(request->ctx, engine);
> -	return ret;
> -}
> -
>  static int intel_lr_context_pin(struct i915_gem_context *ctx,
>  				struct intel_engine_cs *engine)
>  {
> @@ -911,6 +844,71 @@ void intel_lr_context_unpin(struct i915_gem_context *ctx,
>  	i915_gem_context_put(ctx);
>  }
>
> +int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request)
> +{
> +	struct intel_engine_cs *engine = request->engine;
> +	struct intel_context *ce = &request->ctx->engine[engine->id];
> +	int ret;
> +
> +	/* Flush enough space to reduce the likelihood of waiting after
> +	 * we start building the request - in which case we will just
> +	 * have to repeat work.
> +	 */
> +	request->reserved_space += EXECLISTS_REQUEST_SIZE;
> +
> +	if (!ce->state) {
> +		ret = execlists_context_deferred_alloc(request->ctx, engine);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	request->ring = ce->ring;
> +
> +	ret = intel_lr_context_pin(request->ctx, engine);
> +	if (ret)
> +		return ret;
> +
> +	if (i915.enable_guc_submission) {
> +		/*
> +		 * Check that the GuC has space for the request before
> +		 * going any further, as the i915_add_request() call
> +		 * later on mustn't fail ...
> +		 */
> +		ret = i915_guc_wq_reserve(request);
> +		if (ret)
> +			goto err_unpin;
> +	}
> +
> +	ret = intel_ring_begin(request, 0);
> +	if (ret)
> +		goto err_unreserve;
> +
> +	if (!ce->initialised) {
> +		ret = engine->init_context(request);
> +		if (ret)
> +			goto err_unreserve;
> +
> +		ce->initialised = true;
> +	}
> +
> +	/* Note that after this point, we have committed to using
> +	 * this request as it is being used to both track the
> +	 * state of engine initialisation and liveness of the
> +	 * golden renderstate above. Think twice before you try
> +	 * to cancel/unwind this request now.
> +	 */
> +
> +	request->reserved_space -= EXECLISTS_REQUEST_SIZE;
> +	return 0;
> +
> +err_unreserve:
> +	if (i915.enable_guc_submission)
> +		i915_guc_wq_unreserve(request);
> +err_unpin:
> +	intel_lr_context_unpin(request->ctx, engine);
> +	return ret;
> +}
> +
>  static int intel_logical_ring_workarounds_emit(struct drm_i915_gem_request *req)
>  {
>  	int ret, i;
>

Poor man's split view shows proper use of cut and paste. With the 
subject fixed:

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 07/16] drm/i915: Move intel_lrc_context_pin() to avoid the forward declaration
  2016-12-08 17:45   ` Tvrtko Ursulin
@ 2016-12-08 20:55     ` Chris Wilson
  0 siblings, 0 replies; 62+ messages in thread
From: Chris Wilson @ 2016-12-08 20:55 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

On Thu, Dec 08, 2016 at 05:45:23PM +0000, Tvrtko Ursulin wrote:
> 
> On 07/12/2016 13:58, Chris Wilson wrote:
> >Just a simple move to avoid a forward declaration.
> 
> Patch subject is wrong - you are moving
> intel_logical_ring_alloc_request_extras.

I moved what I said, what diff shows is the opposite motion!
 
> >Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> >---
> > drivers/gpu/drm/i915/intel_lrc.c | 132 +++++++++++++++++++--------------------
> > 1 file changed, 65 insertions(+), 67 deletions(-)
> >
> >diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
> >index 8b412880e88c..5cabe4e9d22f 100644
> >--- a/drivers/gpu/drm/i915/intel_lrc.c
> >+++ b/drivers/gpu/drm/i915/intel_lrc.c
> >@@ -230,8 +230,6 @@ enum {
> >
> > static int execlists_context_deferred_alloc(struct i915_gem_context *ctx,
> > 					    struct intel_engine_cs *engine);
> >-static int intel_lr_context_pin(struct i915_gem_context *ctx,
> >-				struct intel_engine_cs *engine);
> > static void execlists_init_reg_state(u32 *reg_state,
> > 				     struct i915_gem_context *ctx,
> > 				     struct intel_engine_cs *engine,
> >@@ -774,71 +772,6 @@ static void execlists_schedule(struct drm_i915_gem_request *request, int prio)
> > 	/* XXX Do we need to preempt to make room for us and our deps? */
> > }
> >
> >-int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request)
> >-{
> >-	struct intel_engine_cs *engine = request->engine;
> >-	struct intel_context *ce = &request->ctx->engine[engine->id];
> >-	int ret;
> >-
> >-	/* Flush enough space to reduce the likelihood of waiting after
> >-	 * we start building the request - in which case we will just
> >-	 * have to repeat work.
> >-	 */
> >-	request->reserved_space += EXECLISTS_REQUEST_SIZE;
> >-
> >-	if (!ce->state) {
> >-		ret = execlists_context_deferred_alloc(request->ctx, engine);
> >-		if (ret)
> >-			return ret;
> >-	}
> >-
> >-	request->ring = ce->ring;
> >-
> >-	ret = intel_lr_context_pin(request->ctx, engine);
> >-	if (ret)
> >-		return ret;
> >-
> >-	if (i915.enable_guc_submission) {
> >-		/*
> >-		 * Check that the GuC has space for the request before
> >-		 * going any further, as the i915_add_request() call
> >-		 * later on mustn't fail ...
> >-		 */
> >-		ret = i915_guc_wq_reserve(request);
> >-		if (ret)
> >-			goto err_unpin;
> >-	}
> >-
> >-	ret = intel_ring_begin(request, 0);
> >-	if (ret)
> >-		goto err_unreserve;
> >-
> >-	if (!ce->initialised) {
> >-		ret = engine->init_context(request);
> >-		if (ret)
> >-			goto err_unreserve;
> >-
> >-		ce->initialised = true;
> >-	}
> >-
> >-	/* Note that after this point, we have committed to using
> >-	 * this request as it is being used to both track the
> >-	 * state of engine initialisation and liveness of the
> >-	 * golden renderstate above. Think twice before you try
> >-	 * to cancel/unwind this request now.
> >-	 */
> >-
> >-	request->reserved_space -= EXECLISTS_REQUEST_SIZE;
> >-	return 0;
> >-
> >-err_unreserve:
> >-	if (i915.enable_guc_submission)
> >-		i915_guc_wq_unreserve(request);
> >-err_unpin:
> >-	intel_lr_context_unpin(request->ctx, engine);
> >-	return ret;
> >-}
> >-
> > static int intel_lr_context_pin(struct i915_gem_context *ctx,
> > 				struct intel_engine_cs *engine)
> > {
> >@@ -911,6 +844,71 @@ void intel_lr_context_unpin(struct i915_gem_context *ctx,
> > 	i915_gem_context_put(ctx);
> > }
> >
> >+int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request)
> >+{
> >+	struct intel_engine_cs *engine = request->engine;
> >+	struct intel_context *ce = &request->ctx->engine[engine->id];
> >+	int ret;
> >+
> >+	/* Flush enough space to reduce the likelihood of waiting after
> >+	 * we start building the request - in which case we will just
> >+	 * have to repeat work.
> >+	 */
> >+	request->reserved_space += EXECLISTS_REQUEST_SIZE;
> >+
> >+	if (!ce->state) {
> >+		ret = execlists_context_deferred_alloc(request->ctx, engine);
> >+		if (ret)
> >+			return ret;
> >+	}
> >+
> >+	request->ring = ce->ring;
> >+
> >+	ret = intel_lr_context_pin(request->ctx, engine);
> >+	if (ret)
> >+		return ret;
> >+
> >+	if (i915.enable_guc_submission) {
> >+		/*
> >+		 * Check that the GuC has space for the request before
> >+		 * going any further, as the i915_add_request() call
> >+		 * later on mustn't fail ...
> >+		 */
> >+		ret = i915_guc_wq_reserve(request);
> >+		if (ret)
> >+			goto err_unpin;
> >+	}
> >+
> >+	ret = intel_ring_begin(request, 0);
> >+	if (ret)
> >+		goto err_unreserve;
> >+
> >+	if (!ce->initialised) {
> >+		ret = engine->init_context(request);
> >+		if (ret)
> >+			goto err_unreserve;
> >+
> >+		ce->initialised = true;
> >+	}
> >+
> >+	/* Note that after this point, we have committed to using
> >+	 * this request as it is being used to both track the
> >+	 * state of engine initialisation and liveness of the
> >+	 * golden renderstate above. Think twice before you try
> >+	 * to cancel/unwind this request now.
> >+	 */
> >+
> >+	request->reserved_space -= EXECLISTS_REQUEST_SIZE;
> >+	return 0;
> >+
> >+err_unreserve:
> >+	if (i915.enable_guc_submission)
> >+		i915_guc_wq_unreserve(request);
> >+err_unpin:
> >+	intel_lr_context_unpin(request->ctx, engine);
> >+	return ret;
> >+}
> >+
> > static int intel_logical_ring_workarounds_emit(struct drm_i915_gem_request *req)
> > {
> > 	int ret, i;
> >
> 
> Poor man's split view shows proper use of cut and paste. With the
> subject fixed:

Pfiffle! I standby the cut'n'paste as I performed it!

All motion is relative and whatnot.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 05/16] drm/i915: Add unit tests for the breadcrumb rbtree, wakeups
  2016-12-08 17:38   ` Tvrtko Ursulin
@ 2016-12-08 21:04     ` Chris Wilson
  2016-12-09  9:33       ` Tvrtko Ursulin
  0 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-08 21:04 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

On Thu, Dec 08, 2016 at 05:38:34PM +0000, Tvrtko Ursulin wrote:
> 
> On 07/12/2016 13:58, Chris Wilson wrote:
> >Third retroactive test, make sure that the seqno waiters are woken.
> >
> >Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> >---
> > drivers/gpu/drm/i915/intel_breadcrumbs.c | 171 +++++++++++++++++++++++++++++++
> > 1 file changed, 171 insertions(+)
> >
> >diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c
> >index fc950f7ff322..1374a54e41c9 100644
> >--- a/drivers/gpu/drm/i915/intel_breadcrumbs.c
> >+++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c
> >@@ -966,11 +966,182 @@ static int igt_insert_complete(void *ignore)
> > 	return err;
> > }
> >
> >+struct igt_wakeup {
> >+	struct task_struct *tsk;
> >+	atomic_t *ready, *set, *done;
> >+	struct intel_engine_cs *engine;
> >+	unsigned long flags;
> >+	wait_queue_head_t *wq;
> >+	u32 seqno;
> >+};
> >+
> >+static int wait_atomic(atomic_t *p)
> >+{
> >+	schedule();
> >+	return 0;
> >+}
> >+
> >+static int wait_atomic_timeout(atomic_t *p)
> >+{
> >+	return schedule_timeout(10 * HZ) ? 0 : -ETIMEDOUT;
> >+}
> >+
> >+static int igt_wakeup_thread(void *arg)
> >+{
> >+	struct igt_wakeup *w = arg;
> >+	struct intel_wait wait;
> >+
> >+	while (!kthread_should_stop()) {
> >+		DEFINE_WAIT(ready);
> >+
> >+		for (;;) {
> >+			prepare_to_wait(w->wq, &ready, TASK_INTERRUPTIBLE);
> >+			if (atomic_read(w->ready) == 0)
> >+				break;
> >+
> >+			schedule();
> >+		}
> >+		finish_wait(w->wq, &ready);
> 
> Have to say this is the first time I've learnt about
> wake_up_atomic_t & co. You couldn't use wait_on_atomic_t instead of
> the loop above?

No, there's only wake_up_atomic_t and not wake_up_all_atomic_t. That was
a few hours of staring before I released the test was failing because
only the first was being woken.
> 
> >+		if (atomic_dec_and_test(w->set))
> >+			wake_up_atomic_t(w->set);
> >+
 
> Okay, all the threads have observed that all other threads have been
> started, yes?

Yes. We can do the wake_up_atomic_t after each (because the other side
will just go back to sleep until w->set is zero) but since we have to do
the atomic operation we may as use the result to only do the wakeup
once.
 
> >+		if (test_bit(0, &w->flags))
> >+			break;
> 
> One thread failed to start = bailout.

That's for the synchronisation on exit. Required because the caller
frees the structs. If I make the allocation per-thread and free it in
the worker, we could have a simpler while (!kthread_should_stop()) I
think.
 
> Do you intend to use the flags for something more which precludes a
> more descriptive name for its single purpose here?

No, bit I typically call my bitmasks flags, unless there is a clear
reason for them. In this case it is because I couldn't use the
kthread->flags for my purposes.
 
> >+
> >+		intel_wait_init(&wait, w->seqno);
> >+		intel_engine_add_wait(w->engine, &wait);
> >+		for (;;) {
> >+			set_current_state(TASK_UNINTERRUPTIBLE);
> >+			if (i915_seqno_passed(intel_engine_get_seqno(w->engine),
> >+					      w->seqno))
> >+				break;
> >+
> >+			schedule();
> >+		}
> >+		intel_engine_remove_wait(w->engine, &wait);
> >+		__set_current_state(TASK_RUNNING);
> >+
> >+		if (atomic_dec_and_test(w->done))
> >+			wake_up_atomic_t(w->done);
> >+	}
> >+
> >+	if (atomic_dec_and_test(w->done))
> >+		wake_up_atomic_t(w->done);
> 
> Must be a special reason done is decremented in the loop and outside
> the loop?

Synchronisation with the kfree required the wakeup, but I couldn't find
a pleasant contortion to only do the wakeup(w->done) once. As above
could change the allocation to avoid the sync before kfree.
> 
> >+	return 0;
> >+}
> >+
> >+static void igt_wake_all_sync(atomic_t *ready,
> >+			      atomic_t *set,
> >+			      atomic_t *done,
> >+			      wait_queue_head_t *wq,
> >+			      int count)
> >+{
> >+	atomic_set(set, count);
> >+	atomic_set(done, count);
> >+
> >+	atomic_set(ready, 0);
> >+	wake_up_all(wq);
> >+
> >+	wait_on_atomic_t(set, wait_atomic, TASK_UNINTERRUPTIBLE);
> >+	atomic_set(ready, count);
> >+}
> >+
> >+static int igt_wakeup(void *ignore)
> >+{
> >+	const int state = TASK_UNINTERRUPTIBLE;
> >+	struct intel_engine_cs *engine;
> >+	struct igt_wakeup *waiters;
> >+	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
> >+	const int count = 4096;
> >+	const u32 max_seqno = count / 4;
> >+	atomic_t ready, set, done;
> >+	int err = -ENOMEM;
> >+	int n, step;
> >+
> >+	engine = mock_engine("mock");
> >+	if (!engine)
> >+		goto out;
> >+
> >+	waiters = drm_malloc_gfp(count, sizeof(*waiters), GFP_TEMPORARY);
> >+	if (!waiters)
> >+		goto out_engines;
> >+
> >+	atomic_set(&ready, count);
> >+	for (n = 0; n < count; n++) {
> >+		waiters[n].wq = &wq;
> >+		waiters[n].ready = &ready;
> >+		waiters[n].set = &set;
> >+		waiters[n].done = &done;
> >+		waiters[n].engine = engine;
> >+		waiters[n].flags = 0;
> >+
> >+		waiters[n].tsk = kthread_run(igt_wakeup_thread, &waiters[n],
> >+					     "i915/igt:%d", n);
> >+		if (IS_ERR(waiters[n].tsk))
> >+			goto out_waiters;
> >+
> >+		get_task_struct(waiters[n].tsk);
> >+	}
> >+
> 
> It is time to start documenting the tests I think via nice comments
> at strategic places. Probably a short commentary on the test as a
> whole and then separately at interesting steps.
> 
> >+	for (step = 1; step <= max_seqno; step <<= 1) {
> >+		u32 seqno;
> >+
> >+		for (n = 0; n < count; n++)
> >+			waiters[n].seqno = 1 + get_random_int() % max_seqno;
> 
> So you have 4096 waiters but some are waiting on the same seqno,
> since there are at most 1024 unique seqnos. Took me a while to
> figure this one out.

Yup.
 
> >+
> >+		mock_seqno_advance(engine, 0);
> >+		igt_wake_all_sync(&ready, &set, &done, &wq, count);
> >+
> >+		for (seqno = 1; seqno <= max_seqno + step; seqno += step) {
> 
> First step wakes up all seqnos one by one, the other steps do it in
> chunks with a larger and larger skip. All the way to waking the
> whole bunch at once?

Yes.
 
> >+			usleep_range(50, 500);
> 
> Why sleep? It should work without it, no?

Yes. But we want the wakeups to happen in batches, not the first waiter
seeing the entire sequence is complete and waiting up everyone.
 
> >+			mock_seqno_advance(engine, seqno);
> >+		}
> >+		GEM_BUG_ON(intel_engine_get_seqno(engine) < 1 + max_seqno);
> >+
> >+		err = wait_on_atomic_t(&done, wait_atomic_timeout, state);
> >+		if (err) {
> >+			pr_err("Timed out waiting for %d remaining waiters\n",
> >+			       atomic_read(&done));
> >+			break;
> >+		}
> >+
> >+		err = check_rbtree_empty(engine);
> >+		if (err)
> >+			break;
> >+	}
> >+
> >+out_waiters:
> >+	for (n = 0; n < count; n++) {
> >+		if (IS_ERR(waiters[n].tsk))
> >+			break;
> >+
> >+		set_bit(0, &waiters[n].flags);
> >+	}
> >+
> >+	igt_wake_all_sync(&ready, &set, &done, &wq, n);
> >+	wait_on_atomic_t(&done, wait_atomic, state);
> 
> C-O-M-M-E-N-T-S! :D
> 
> >+
> >+	for (n = 0; n < count; n++) {
> >+		if (IS_ERR(waiters[n].tsk))
> >+			break;
> >+
> >+		kthread_stop(waiters[n].tsk);
> >+		put_task_struct(waiters[n].tsk);
> >+	}
> >+
> >+	drm_free_large(waiters);
> >+out_engines:
> >+	kfree(engine);
> >+out:
> >+	return err;
> >+}
> >+
> > int intel_breadcrumbs_selftest(void)
> > {
> > 	static const struct i915_subtest tests[] = {
> > 		SUBTEST(igt_random_insert_remove),
> > 		SUBTEST(igt_insert_complete),
> >+		SUBTEST(igt_wakeup),
> > 	};
> >
> > 	return i915_subtests(tests, NULL);
> >
> 
> Phew, looks mostly OK. I think only one thing I am unsure of.
> 
> This was quite smart, please start adding comments when you come up
> with similar things. :)

Bah, you are mistaking throwing ideas/snippets at a wall and only picking
those that stick.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 2/2] drm/i915: Test all fw tables during mock selftests
  2016-12-08 16:52     ` Tvrtko Ursulin
@ 2016-12-08 22:29       ` Chris Wilson
  2016-12-09  9:41         ` Tvrtko Ursulin
  0 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-08 22:29 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

On Thu, Dec 08, 2016 at 04:52:24PM +0000, Tvrtko Ursulin wrote:
> Idea for another late test:
> 
> for (offset = 0; offset < 0x40000; offset++) {
> 	fw_domain = intel_uncore_forcewake_for_reg(dev_priv, { .reg =
> offset }, FW_REG_READ | FW_REG_WRITE);
> 	if (WARN_ON(fw_domain & ~dev_priv->uncore.fw_domains))
> 		return -EINVAL;
> }
> 
> And then we could convert the existing related WARNs in the live
> code base to GEM_WARN_ONs.

I liked it enough to type something up... However, isn't the argument
for skipping some fw_domains that the associated register banks are
invalid/absent on those platforms? i.e. we can't simply walk
	for (offset = 0; NEEDS_FORCE_WAKE(offset); offset++)
and expect every offset to correspond to a register (and so need a
covering fw_domain)?
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 05/16] drm/i915: Add unit tests for the breadcrumb rbtree, wakeups
  2016-12-08 21:04     ` Chris Wilson
@ 2016-12-09  9:33       ` Tvrtko Ursulin
  0 siblings, 0 replies; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-09  9:33 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 08/12/2016 21:04, Chris Wilson wrote:
> On Thu, Dec 08, 2016 at 05:38:34PM +0000, Tvrtko Ursulin wrote:
>>
>> On 07/12/2016 13:58, Chris Wilson wrote:
>>> Third retroactive test, make sure that the seqno waiters are woken.
>>>
>>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>>> ---
>>> drivers/gpu/drm/i915/intel_breadcrumbs.c | 171 +++++++++++++++++++++++++++++++
>>> 1 file changed, 171 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/i915/intel_breadcrumbs.c b/drivers/gpu/drm/i915/intel_breadcrumbs.c
>>> index fc950f7ff322..1374a54e41c9 100644
>>> --- a/drivers/gpu/drm/i915/intel_breadcrumbs.c
>>> +++ b/drivers/gpu/drm/i915/intel_breadcrumbs.c
>>> @@ -966,11 +966,182 @@ static int igt_insert_complete(void *ignore)
>>> 	return err;
>>> }
>>>
>>> +struct igt_wakeup {
>>> +	struct task_struct *tsk;
>>> +	atomic_t *ready, *set, *done;
>>> +	struct intel_engine_cs *engine;
>>> +	unsigned long flags;
>>> +	wait_queue_head_t *wq;
>>> +	u32 seqno;
>>> +};
>>> +
>>> +static int wait_atomic(atomic_t *p)
>>> +{
>>> +	schedule();
>>> +	return 0;
>>> +}
>>> +
>>> +static int wait_atomic_timeout(atomic_t *p)
>>> +{
>>> +	return schedule_timeout(10 * HZ) ? 0 : -ETIMEDOUT;
>>> +}
>>> +
>>> +static int igt_wakeup_thread(void *arg)
>>> +{
>>> +	struct igt_wakeup *w = arg;
>>> +	struct intel_wait wait;
>>> +
>>> +	while (!kthread_should_stop()) {
>>> +		DEFINE_WAIT(ready);
>>> +
>>> +		for (;;) {
>>> +			prepare_to_wait(w->wq, &ready, TASK_INTERRUPTIBLE);
>>> +			if (atomic_read(w->ready) == 0)
>>> +				break;
>>> +
>>> +			schedule();
>>> +		}
>>> +		finish_wait(w->wq, &ready);
>>
>> Have to say this is the first time I've learnt about
>> wake_up_atomic_t & co. You couldn't use wait_on_atomic_t instead of
>> the loop above?
>
> No, there's only wake_up_atomic_t and not wake_up_all_atomic_t. That was
> a few hours of staring before I released the test was failing because
> only the first was being woken.

wait_on_atomic_t, not wake up. Still a problem with that one? It is a 
bit magic how it works, did not get into details yet so possibly I am wrong.

>>
>>> +		if (atomic_dec_and_test(w->set))
>>> +			wake_up_atomic_t(w->set);
>>> +
>
>> Okay, all the threads have observed that all other threads have been
>> started, yes?
>
> Yes. We can do the wake_up_atomic_t after each (because the other side
> will just go back to sleep until w->set is zero) but since we have to do
> the atomic operation we may as use the result to only do the wakeup
> once.
>
>>> +		if (test_bit(0, &w->flags))
>>> +			break;
>>
>> One thread failed to start = bailout.
>
> That's for the synchronisation on exit. Required because the caller
> frees the structs. If I make the allocation per-thread and free it in
> the worker, we could have a simpler while (!kthread_should_stop()) I
> think.

Doesn't matter to me hugely as long as there are some high level 
comments on what is happening.

>
>> Do you intend to use the flags for something more which precludes a
>> more descriptive name for its single purpose here?
>
> No, bit I typically call my bitmasks flags, unless there is a clear
> reason for them. In this case it is because I couldn't use the
> kthread->flags for my purposes.

My point is it could have been "bool stop_thread" (or something) and 
then I wouldn't have to double check what bits are used and for what.

>
>>> +
>>> +		intel_wait_init(&wait, w->seqno);
>>> +		intel_engine_add_wait(w->engine, &wait);
>>> +		for (;;) {
>>> +			set_current_state(TASK_UNINTERRUPTIBLE);
>>> +			if (i915_seqno_passed(intel_engine_get_seqno(w->engine),
>>> +					      w->seqno))
>>> +				break;
>>> +
>>> +			schedule();
>>> +		}
>>> +		intel_engine_remove_wait(w->engine, &wait);
>>> +		__set_current_state(TASK_RUNNING);
>>> +
>>> +		if (atomic_dec_and_test(w->done))
>>> +			wake_up_atomic_t(w->done);
>>> +	}
>>> +
>>> +	if (atomic_dec_and_test(w->done))
>>> +		wake_up_atomic_t(w->done);
>>
>> Must be a special reason done is decremented in the loop and outside
>> the loop?
>
> Synchronisation with the kfree required the wakeup, but I couldn't find
> a pleasant contortion to only do the wakeup(w->done) once. As above
> could change the allocation to avoid the sync before kfree.

I don't see how this last decrement does not overflow. Assuming all have 
decremented in the loop above, then you exit to do it once more. Or 
maybe the value doesn't matter at this point since it will get 
overwritten in the next iteration, hm. It is confusing.

>>
>>> +	return 0;
>>> +}
>>> +
>>> +static void igt_wake_all_sync(atomic_t *ready,
>>> +			      atomic_t *set,
>>> +			      atomic_t *done,
>>> +			      wait_queue_head_t *wq,
>>> +			      int count)
>>> +{
>>> +	atomic_set(set, count);
>>> +	atomic_set(done, count);
>>> +
>>> +	atomic_set(ready, 0);
>>> +	wake_up_all(wq);
>>> +
>>> +	wait_on_atomic_t(set, wait_atomic, TASK_UNINTERRUPTIBLE);
>>> +	atomic_set(ready, count);
>>> +}
>>> +
>>> +static int igt_wakeup(void *ignore)
>>> +{
>>> +	const int state = TASK_UNINTERRUPTIBLE;
>>> +	struct intel_engine_cs *engine;
>>> +	struct igt_wakeup *waiters;
>>> +	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
>>> +	const int count = 4096;
>>> +	const u32 max_seqno = count / 4;
>>> +	atomic_t ready, set, done;
>>> +	int err = -ENOMEM;
>>> +	int n, step;
>>> +
>>> +	engine = mock_engine("mock");
>>> +	if (!engine)
>>> +		goto out;
>>> +
>>> +	waiters = drm_malloc_gfp(count, sizeof(*waiters), GFP_TEMPORARY);
>>> +	if (!waiters)
>>> +		goto out_engines;
>>> +
>>> +	atomic_set(&ready, count);
>>> +	for (n = 0; n < count; n++) {
>>> +		waiters[n].wq = &wq;
>>> +		waiters[n].ready = &ready;
>>> +		waiters[n].set = &set;
>>> +		waiters[n].done = &done;
>>> +		waiters[n].engine = engine;
>>> +		waiters[n].flags = 0;
>>> +
>>> +		waiters[n].tsk = kthread_run(igt_wakeup_thread, &waiters[n],
>>> +					     "i915/igt:%d", n);
>>> +		if (IS_ERR(waiters[n].tsk))
>>> +			goto out_waiters;
>>> +
>>> +		get_task_struct(waiters[n].tsk);
>>> +	}
>>> +
>>
>> It is time to start documenting the tests I think via nice comments
>> at strategic places. Probably a short commentary on the test as a
>> whole and then separately at interesting steps.
>>
>>> +	for (step = 1; step <= max_seqno; step <<= 1) {
>>> +		u32 seqno;
>>> +
>>> +		for (n = 0; n < count; n++)
>>> +			waiters[n].seqno = 1 + get_random_int() % max_seqno;
>>
>> So you have 4096 waiters but some are waiting on the same seqno,
>> since there are at most 1024 unique seqnos. Took me a while to
>> figure this one out.
>
> Yup.
>
>>> +
>>> +		mock_seqno_advance(engine, 0);
>>> +		igt_wake_all_sync(&ready, &set, &done, &wq, count);
>>> +
>>> +		for (seqno = 1; seqno <= max_seqno + step; seqno += step) {
>>
>> First step wakes up all seqnos one by one, the other steps do it in
>> chunks with a larger and larger skip. All the way to waking the
>> whole bunch at once?
>
> Yes.
>
>>> +			usleep_range(50, 500);
>>
>> Why sleep? It should work without it, no?
>
> Yes. But we want the wakeups to happen in batches, not the first waiter
> seeing the entire sequence is complete and waiting up everyone.

Hm yes, makes sense. Simulates the GPU execution speed could be a comment.

>
>>> +			mock_seqno_advance(engine, seqno);
>>> +		}
>>> +		GEM_BUG_ON(intel_engine_get_seqno(engine) < 1 + max_seqno);
>>> +
>>> +		err = wait_on_atomic_t(&done, wait_atomic_timeout, state);
>>> +		if (err) {
>>> +			pr_err("Timed out waiting for %d remaining waiters\n",
>>> +			       atomic_read(&done));
>>> +			break;
>>> +		}
>>> +
>>> +		err = check_rbtree_empty(engine);
>>> +		if (err)
>>> +			break;
>>> +	}
>>> +
>>> +out_waiters:
>>> +	for (n = 0; n < count; n++) {
>>> +		if (IS_ERR(waiters[n].tsk))
>>> +			break;
>>> +
>>> +		set_bit(0, &waiters[n].flags);
>>> +	}
>>> +
>>> +	igt_wake_all_sync(&ready, &set, &done, &wq, n);
>>> +	wait_on_atomic_t(&done, wait_atomic, state);
>>
>> C-O-M-M-E-N-T-S! :D
>>
>>> +
>>> +	for (n = 0; n < count; n++) {
>>> +		if (IS_ERR(waiters[n].tsk))
>>> +			break;
>>> +
>>> +		kthread_stop(waiters[n].tsk);
>>> +		put_task_struct(waiters[n].tsk);
>>> +	}
>>> +
>>> +	drm_free_large(waiters);
>>> +out_engines:
>>> +	kfree(engine);
>>> +out:
>>> +	return err;
>>> +}
>>> +
>>> int intel_breadcrumbs_selftest(void)
>>> {
>>> 	static const struct i915_subtest tests[] = {
>>> 		SUBTEST(igt_random_insert_remove),
>>> 		SUBTEST(igt_insert_complete),
>>> +		SUBTEST(igt_wakeup),
>>> 	};
>>>
>>> 	return i915_subtests(tests, NULL);
>>>
>>
>> Phew, looks mostly OK. I think only one thing I am unsure of.
>>
>> This was quite smart, please start adding comments when you come up
>> with similar things. :)
>
> Bah, you are mistaking throwing ideas/snippets at a wall and only picking
> those that stick.

Idea looks good to me!

The whole initiative actually is excellent.

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 2/2] drm/i915: Test all fw tables during mock selftests
  2016-12-08 22:29       ` Chris Wilson
@ 2016-12-09  9:41         ` Tvrtko Ursulin
  2016-12-09 10:18           ` Chris Wilson
  0 siblings, 1 reply; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-09  9:41 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 08/12/2016 22:29, Chris Wilson wrote:
> On Thu, Dec 08, 2016 at 04:52:24PM +0000, Tvrtko Ursulin wrote:
>> Idea for another late test:
>>
>> for (offset = 0; offset < 0x40000; offset++) {
>> 	fw_domain = intel_uncore_forcewake_for_reg(dev_priv, { .reg =
>> offset }, FW_REG_READ | FW_REG_WRITE);
>> 	if (WARN_ON(fw_domain & ~dev_priv->uncore.fw_domains))
>> 		return -EINVAL;
>> }
>>
>> And then we could convert the existing related WARNs in the live
>> code base to GEM_WARN_ONs.
>
> I liked it enough to type something up... However, isn't the argument
> for skipping some fw_domains that the associated register banks are
> invalid/absent on those platforms? i.e. we can't simply walk
> 	for (offset = 0; NEEDS_FORCE_WAKE(offset); offset++)
> and expect every offset to correspond to a register (and so need a
> covering fw_domain)?

I thought it won't be a problem because it would return 0 in those 
cases. So only those offsets that need a forcewake in a platform can 
trigger the fail.

Oh I see. We would need another layer before the condition checking, like:

fw_domains = intel_uncore_forcewake_for_reg(...)
if ((fw_domains & FORCEWAKE_MEDIA) && !HAS_ENGINE(VCS))
	fw_domains &= ~FORCEWAKE_MEDIA;
...
WARN_ON(fw_domains & ~...);

Would that work?

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 2/2] drm/i915: Test all fw tables during mock selftests
  2016-12-09  9:41         ` Tvrtko Ursulin
@ 2016-12-09 10:18           ` Chris Wilson
  2016-12-09 10:35             ` Tvrtko Ursulin
  0 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-09 10:18 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

On Fri, Dec 09, 2016 at 09:41:30AM +0000, Tvrtko Ursulin wrote:
> 
> On 08/12/2016 22:29, Chris Wilson wrote:
> >On Thu, Dec 08, 2016 at 04:52:24PM +0000, Tvrtko Ursulin wrote:
> >>Idea for another late test:
> >>
> >>for (offset = 0; offset < 0x40000; offset++) {
> >>	fw_domain = intel_uncore_forcewake_for_reg(dev_priv, { .reg =
> >>offset }, FW_REG_READ | FW_REG_WRITE);
> >>	if (WARN_ON(fw_domain & ~dev_priv->uncore.fw_domains))
> >>		return -EINVAL;
> >>}
> >>
> >>And then we could convert the existing related WARNs in the live
> >>code base to GEM_WARN_ONs.
> >
> >I liked it enough to type something up... However, isn't the argument
> >for skipping some fw_domains that the associated register banks are
> >invalid/absent on those platforms? i.e. we can't simply walk
> >	for (offset = 0; NEEDS_FORCE_WAKE(offset); offset++)
> >and expect every offset to correspond to a register (and so need a
> >covering fw_domain)?
> 
> I thought it won't be a problem because it would return 0 in those
> cases. So only those offsets that need a forcewake in a platform can
> trigger the fail.
> 
> Oh I see. We would need another layer before the condition checking, like:
> 
> fw_domains = intel_uncore_forcewake_for_reg(...)
> if ((fw_domains & FORCEWAKE_MEDIA) && !HAS_ENGINE(VCS))
> 	fw_domains &= ~FORCEWAKE_MEDIA;

That's almost like saying we should just
	return entry->domain & i915->uncore.fw_domains;
which we do anyway by the filtering in force_wake_auto.

Hmm. Could we use the mmio debug. Something like

	valid_reg = bitmap_create(0x40000)
	FORCEWAKE_ALL
	for_each_offset()
		read reg
		if (!mmio_debug)
			set_bit(valid_reg);

	for_each_bit()
		FORCEWAKE_DISABLE
		read reg using fw_domains
		if (mmio_debug)
			AWOOGA!
?

Hopefully read reg won't cause system hangs, and less liable to corrupt
state than write reg.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 2/2] drm/i915: Test all fw tables during mock selftests
  2016-12-09 10:18           ` Chris Wilson
@ 2016-12-09 10:35             ` Tvrtko Ursulin
  2016-12-09 10:51               ` Chris Wilson
  0 siblings, 1 reply; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-09 10:35 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 09/12/2016 10:18, Chris Wilson wrote:
> On Fri, Dec 09, 2016 at 09:41:30AM +0000, Tvrtko Ursulin wrote:
>>
>> On 08/12/2016 22:29, Chris Wilson wrote:
>>> On Thu, Dec 08, 2016 at 04:52:24PM +0000, Tvrtko Ursulin wrote:
>>>> Idea for another late test:
>>>>
>>>> for (offset = 0; offset < 0x40000; offset++) {
>>>> 	fw_domain = intel_uncore_forcewake_for_reg(dev_priv, { .reg =
>>>> offset }, FW_REG_READ | FW_REG_WRITE);
>>>> 	if (WARN_ON(fw_domain & ~dev_priv->uncore.fw_domains))
>>>> 		return -EINVAL;
>>>> }
>>>>
>>>> And then we could convert the existing related WARNs in the live
>>>> code base to GEM_WARN_ONs.
>>>
>>> I liked it enough to type something up... However, isn't the argument
>>> for skipping some fw_domains that the associated register banks are
>>> invalid/absent on those platforms? i.e. we can't simply walk
>>> 	for (offset = 0; NEEDS_FORCE_WAKE(offset); offset++)
>>> and expect every offset to correspond to a register (and so need a
>>> covering fw_domain)?
>>
>> I thought it won't be a problem because it would return 0 in those
>> cases. So only those offsets that need a forcewake in a platform can
>> trigger the fail.
>>
>> Oh I see. We would need another layer before the condition checking, like:
>>
>> fw_domains = intel_uncore_forcewake_for_reg(...)
>> if ((fw_domains & FORCEWAKE_MEDIA) && !HAS_ENGINE(VCS))
>> 	fw_domains &= ~FORCEWAKE_MEDIA;
>
> That's almost like saying we should just
> 	return entry->domain & i915->uncore.fw_domains;
> which we do anyway by the filtering in force_wake_auto.

Haha yes, so my proposed test is completely pointless. :)

All forcewake get functions mask with i915->uncore.fw_domains so there 
is no potential to access an uninitialized domain.

WARN_ON(fw_domain & ~dev_priv->uncore.fw_domains) elsewhere in the code 
have to be really removed for future proofing it seems to me.

> Hmm. Could we use the mmio debug. Something like
>
> 	valid_reg = bitmap_create(0x40000)
> 	FORCEWAKE_ALL
> 	for_each_offset()
> 		read reg
> 		if (!mmio_debug)
> 			set_bit(valid_reg);
>
> 	for_each_bit()
> 		FORCEWAKE_DISABLE
> 		read reg using fw_domains
> 		if (mmio_debug)
> 			AWOOGA!
> ?
>
> Hopefully read reg won't cause system hangs, and less liable to corrupt
> state than write reg.

I think we need to restate what are we actually trying to test. I at 
least am confused now. :)

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 2/2] drm/i915: Test all fw tables during mock selftests
  2016-12-09 10:35             ` Tvrtko Ursulin
@ 2016-12-09 10:51               ` Chris Wilson
  0 siblings, 0 replies; 62+ messages in thread
From: Chris Wilson @ 2016-12-09 10:51 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

On Fri, Dec 09, 2016 at 10:35:49AM +0000, Tvrtko Ursulin wrote:
> On 09/12/2016 10:18, Chris Wilson wrote:
> >Hmm. Could we use the mmio debug. Something like
> >
> >	valid_reg = bitmap_create(0x40000)
> >	FORCEWAKE_ALL
> >	for_each_offset()
> >		read reg
> >		if (!mmio_debug)
> >			set_bit(valid_reg);
> >
> >	for_each_bit()
> >		FORCEWAKE_DISABLE
> >		read reg using fw_domains
> >		if (mmio_debug)
> >			AWOOGA!
> >?
> >
> >Hopefully read reg won't cause system hangs, and less liable to corrupt
> >state than write reg.
> 
> I think we need to restate what are we actually trying to test. I at
> least am confused now. :)

Right, that tests that for all known registers covered by the fw domains
(which we force on, including the expected absent domains?), the
entry->domains are correct. It may not detect if we have a register that
we need that is outside of the set of initialised domains.

We still need to catch runtime misuse of mmio (i.e. callers who forget
to grab the fw domain). Anyway, I think it does provide some useful
validation of the fw getters, so I'll give it a spin and see what
happens.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 08/16] drm/i915: Unify active context tracking between legacy/execlists/guc
  2016-12-07 13:58 ` [PATCH 08/16] drm/i915: Unify active context tracking between legacy/execlists/guc Chris Wilson
@ 2016-12-09 11:48   ` Tvrtko Ursulin
  2016-12-09 12:17     ` Chris Wilson
  0 siblings, 1 reply; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-09 11:48 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> The requests conversion introduced a nasty bug where we could generate a
> new request in the middle of constructing a request. The new request
> would be executed (and waited upon) before the current one, creating a
> minor havoc in the seqno accounting. (Prior to deferred seqno
> assignment, this would mean that the seqno would be out of order, and
> the current request would be deemed complete even before it was
> submitted.)

How exactly it can generate and submit a request while creating a 
request? It would be good to describe it here.

> We also employed two different mechanisms to track the active context
> until it was switched out. The legacy method allowed for waiting upon an
> active context (it could forcibly evict any vma, including context's),
> but the execlists method took a step backwards by pinning the vma for
> the entire active lifespan of the context (the only way to evict was to
> idle the entire GPU, not individual contexts). However, to circumvent

Because of the pin in request creation?

> the tricky issue of locking (i.e. we cannot take struct_mutex at the
> time of i915_gem_request_submit(), where we would want to move the
> previous context onto the active tracker and unpin it), we take the
> execlists approach and keep the contexts pinned until retirement.
> The benefit of the execlists approach, more important for execlists than
> legacy, was the reduction in work in pinning the context for each
> request - as the context was kept pinned until idle, it could short
> circuit the pinning for all active contexts.
>
> We introduce new engine vfuncs to pin and unpin the context
> respectively. The context is pinned at the start of the request, and
> only unpinned when the following request is retired (this ensures that
> the context is idle and coherent in main memory before we unpin it). We
> move the engine->last_context tracking into the retirement itself
> (rather than during request submission) in order to allow the submission
> to be reordered or unwound without undue difficultly.
>
> And finally an ulterior motive for unifying context handling was to
> prepare for mock requests.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/i915_drv.h            |   4 --
>  drivers/gpu/drm/i915/i915_gem_context.c    | 102 +++--------------------------
>  drivers/gpu/drm/i915/i915_gem_request.c    |  38 +++++++----
>  drivers/gpu/drm/i915/i915_gem_request.h    |  11 ----
>  drivers/gpu/drm/i915/i915_guc_submission.c |  11 ----
>  drivers/gpu/drm/i915/i915_perf.c           |  18 ++---
>  drivers/gpu/drm/i915/intel_engine_cs.c     |  21 +++++-
>  drivers/gpu/drm/i915/intel_lrc.c           |  62 ++++++------------
>  drivers/gpu/drm/i915/intel_lrc.h           |   5 +-
>  drivers/gpu/drm/i915/intel_ringbuffer.c    |  57 +++++++++-------
>  drivers/gpu/drm/i915/intel_ringbuffer.h    |   4 ++
>  11 files changed, 122 insertions(+), 211 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 8daa4fb13b52..7c228622716a 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -2250,7 +2250,6 @@ struct drm_i915_private {
>  			struct i915_perf_stream *exclusive_stream;
>
>  			u32 specific_ctx_id;
> -			struct i915_vma *pinned_rcs_vma;
>
>  			struct hrtimer poll_check_timer;
>  			wait_queue_head_t poll_wq;
> @@ -3290,9 +3289,6 @@ int i915_gem_context_open(struct drm_device *dev, struct drm_file *file);
>  void i915_gem_context_close(struct drm_device *dev, struct drm_file *file);
>  int i915_switch_context(struct drm_i915_gem_request *req);
>  int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv);
> -struct i915_vma *
> -i915_gem_context_pin_legacy(struct i915_gem_context *ctx,
> -			    unsigned int flags);
>  void i915_gem_context_free(struct kref *ctx_ref);
>  struct i915_gem_context *
>  i915_gem_context_create_gvt(struct drm_device *dev);
> diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
> index a57c22659a3c..95812c26767c 100644
> --- a/drivers/gpu/drm/i915/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/i915_gem_context.c
> @@ -416,21 +416,6 @@ i915_gem_context_create_gvt(struct drm_device *dev)
>  	return ctx;
>  }
>
> -static void i915_gem_context_unpin(struct i915_gem_context *ctx,
> -				   struct intel_engine_cs *engine)
> -{
> -	if (i915.enable_execlists) {
> -		intel_lr_context_unpin(ctx, engine);
> -	} else {
> -		struct intel_context *ce = &ctx->engine[engine->id];
> -
> -		if (ce->state)
> -			i915_vma_unpin(ce->state);
> -
> -		i915_gem_context_put(ctx);
> -	}
> -}
> -
>  int i915_gem_context_init(struct drm_i915_private *dev_priv)
>  {
>  	struct i915_gem_context *ctx;
> @@ -490,10 +475,11 @@ void i915_gem_context_lost(struct drm_i915_private *dev_priv)
>  	lockdep_assert_held(&dev_priv->drm.struct_mutex);
>
>  	for_each_engine(engine, dev_priv, id) {
> -		if (engine->last_context) {
> -			i915_gem_context_unpin(engine->last_context, engine);
> -			engine->last_context = NULL;
> -		}
> +		if (!engine->last_context)
> +			continue;
> +
> +		engine->context_unpin(engine, engine->last_context);
> +		engine->last_context = NULL;
>  	}
>
>  	/* Force the GPU state to be restored on enabling */
> @@ -761,57 +747,20 @@ needs_pd_load_post(struct i915_hw_ppgtt *ppgtt,
>  	return false;
>  }
>
> -struct i915_vma *
> -i915_gem_context_pin_legacy(struct i915_gem_context *ctx,
> -			    unsigned int flags)
> -{
> -	struct i915_vma *vma = ctx->engine[RCS].state;
> -	int ret;
> -
> -	/* Clear this page out of any CPU caches for coherent swap-in/out.
> -	 * We only want to do this on the first bind so that we do not stall
> -	 * on an active context (which by nature is already on the GPU).
> -	 */
> -	if (!(vma->flags & I915_VMA_GLOBAL_BIND)) {
> -		ret = i915_gem_object_set_to_gtt_domain(vma->obj, false);
> -		if (ret)
> -			return ERR_PTR(ret);
> -	}
> -
> -	ret = i915_vma_pin(vma, 0, ctx->ggtt_alignment, PIN_GLOBAL | flags);
> -	if (ret)
> -		return ERR_PTR(ret);
> -
> -	return vma;
> -}
> -
>  static int do_rcs_switch(struct drm_i915_gem_request *req)
>  {
>  	struct i915_gem_context *to = req->ctx;
>  	struct intel_engine_cs *engine = req->engine;
>  	struct i915_hw_ppgtt *ppgtt = to->ppgtt ?: req->i915->mm.aliasing_ppgtt;
> -	struct i915_vma *vma;
> -	struct i915_gem_context *from;
> +	struct i915_gem_context *from = engine->last_context;
>  	u32 hw_flags;
>  	int ret, i;
>
> +	GEM_BUG_ON(engine->id != RCS);
> +
>  	if (skip_rcs_switch(ppgtt, engine, to))
>  		return 0;
>
> -	/* Trying to pin first makes error handling easier. */
> -	vma = i915_gem_context_pin_legacy(to, 0);
> -	if (IS_ERR(vma))
> -		return PTR_ERR(vma);
> -
> -	/*
> -	 * Pin can switch back to the default context if we end up calling into
> -	 * evict_everything - as a last ditch gtt defrag effort that also
> -	 * switches to the default context. Hence we need to reload from here.
> -	 *
> -	 * XXX: Doing so is painfully broken!
> -	 */
> -	from = engine->last_context;
> -
>  	if (needs_pd_load_pre(ppgtt, engine, to)) {
>  		/* Older GENs and non render rings still want the load first,
>  		 * "PP_DCLV followed by PP_DIR_BASE register through Load
> @@ -820,7 +769,7 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
>  		trace_switch_mm(engine, to);
>  		ret = ppgtt->switch_mm(ppgtt, req);
>  		if (ret)
> -			goto err;
> +			return ret;
>  	}
>
>  	if (!to->engine[RCS].initialised || i915_gem_context_is_default(to))
> @@ -837,29 +786,8 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
>  	if (to != from || (hw_flags & MI_FORCE_RESTORE)) {
>  		ret = mi_set_context(req, hw_flags);
>  		if (ret)
> -			goto err;
> -	}
> -
> -	/* The backing object for the context is done after switching to the
> -	 * *next* context. Therefore we cannot retire the previous context until
> -	 * the next context has already started running. In fact, the below code
> -	 * is a bit suboptimal because the retiring can occur simply after the
> -	 * MI_SET_CONTEXT instead of when the next seqno has completed.
> -	 */
> -	if (from != NULL) {
> -		/* As long as MI_SET_CONTEXT is serializing, ie. it flushes the
> -		 * whole damn pipeline, we don't need to explicitly mark the
> -		 * object dirty. The only exception is that the context must be
> -		 * correct in case the object gets swapped out. Ideally we'd be
> -		 * able to defer doing this until we know the object would be
> -		 * swapped, but there is no way to do that yet.
> -		 */
> -		i915_vma_move_to_active(from->engine[RCS].state, req, 0);
> -		/* state is kept alive until the next request */
> -		i915_vma_unpin(from->engine[RCS].state);
> -		i915_gem_context_put(from);
> +			return ret;
>  	}
> -	engine->last_context = i915_gem_context_get(to);
>
>  	/* GEN8 does *not* require an explicit reload if the PDPs have been
>  	 * setup, and we do not wish to move them.
> @@ -900,10 +828,6 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
>  	}
>
>  	return 0;
> -
> -err:
> -	i915_vma_unpin(vma);
> -	return ret;
>  }
>
>  /**
> @@ -943,12 +867,6 @@ int i915_switch_context(struct drm_i915_gem_request *req)
>  			ppgtt->pd_dirty_rings &= ~intel_engine_flag(engine);
>  		}
>
> -		if (to != engine->last_context) {
> -			if (engine->last_context)
> -				i915_gem_context_put(engine->last_context);
> -			engine->last_context = i915_gem_context_get(to);
> -		}
> -
>  		return 0;
>  	}
>
> diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
> index fcf22b0e2967..06e9a607d934 100644
> --- a/drivers/gpu/drm/i915/i915_gem_request.c
> +++ b/drivers/gpu/drm/i915/i915_gem_request.c
> @@ -206,6 +206,7 @@ void i915_gem_retire_noop(struct i915_gem_active *active,
>
>  static void i915_gem_request_retire(struct drm_i915_gem_request *request)
>  {
> +	struct intel_engine_cs *engine = request->engine;
>  	struct i915_gem_active *active, *next;
>
>  	lockdep_assert_held(&request->i915->drm.struct_mutex);
> @@ -216,9 +217,9 @@ static void i915_gem_request_retire(struct drm_i915_gem_request *request)
>
>  	trace_i915_gem_request_retire(request);
>
> -	spin_lock_irq(&request->engine->timeline->lock);
> +	spin_lock_irq(&engine->timeline->lock);
>  	list_del_init(&request->link);
> -	spin_unlock_irq(&request->engine->timeline->lock);
> +	spin_unlock_irq(&engine->timeline->lock);
>
>  	/* We know the GPU must have read the request to have
>  	 * sent us the seqno + interrupt, so use the position
> @@ -266,17 +267,20 @@ static void i915_gem_request_retire(struct drm_i915_gem_request *request)
>
>  	i915_gem_request_remove_from_client(request);
>
> -	if (request->previous_context) {
> -		if (i915.enable_execlists)
> -			intel_lr_context_unpin(request->previous_context,
> -					       request->engine);
> -	}
> -
>  	/* Retirement decays the ban score as it is a sign of ctx progress */
>  	if (request->ctx->ban_score > 0)
>  		request->ctx->ban_score--;
>
> -	i915_gem_context_put(request->ctx);
> +	/* The backing object for the context is done after switching to the
> +	 * *next* context. Therefore we cannot retire the previous context until
> +	 * the next context has already started running. However, since we
> +	 * cannot take the required locks at i915_gem_request_submit() we
> +	 * defer the unpinning of the active context to now, retirement of
> +	 * the subsequent request.
> +	 */
> +	if (engine->last_context)
> +		engine->context_unpin(engine, engine->last_context);
> +	engine->last_context = request->ctx;

Would it be worth renaming this member to last_retired_context for clarity?

>
>  	dma_fence_signal(&request->fence);
>
> @@ -524,10 +528,18 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
>  	if (ret)
>  		return ERR_PTR(ret);
>
> -	ret = reserve_global_seqno(dev_priv);
> +	/* Pinning the contexts may generate requests in order to acquire
> +	 * GGTT space, so do this first before we reserve a seqno for
> +	 * ourselves.
> +	 */
> +	ret = engine->context_pin(engine, ctx);
>  	if (ret)
>  		return ERR_PTR(ret);
>
> +	ret = reserve_global_seqno(dev_priv);
> +	if (ret)
> +		goto err_unpin;
> +
>  	/* Move the oldest request to the slab-cache (if not in use!) */
>  	req = list_first_entry_or_null(&engine->timeline->requests,
>  				       typeof(*req), link);
> @@ -593,11 +605,10 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
>  	INIT_LIST_HEAD(&req->active_list);
>  	req->i915 = dev_priv;
>  	req->engine = engine;
> -	req->ctx = i915_gem_context_get(ctx);
> +	req->ctx = ctx;
>
>  	/* No zalloc, must clear what we need by hand */
>  	req->global_seqno = 0;
> -	req->previous_context = NULL;
>  	req->file_priv = NULL;
>  	req->batch = NULL;
>
> @@ -633,10 +644,11 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
>  	GEM_BUG_ON(!list_empty(&req->priotree.signalers_list));
>  	GEM_BUG_ON(!list_empty(&req->priotree.waiters_list));
>
> -	i915_gem_context_put(ctx);
>  	kmem_cache_free(dev_priv->requests, req);
>  err_unreserve:
>  	dev_priv->gt.active_requests--;
> +err_unpin:
> +	engine->context_unpin(engine, ctx);
>  	return ERR_PTR(ret);
>  }
>
> diff --git a/drivers/gpu/drm/i915/i915_gem_request.h b/drivers/gpu/drm/i915/i915_gem_request.h
> index e2b077df2da0..8569b35a332a 100644
> --- a/drivers/gpu/drm/i915/i915_gem_request.h
> +++ b/drivers/gpu/drm/i915/i915_gem_request.h
> @@ -170,17 +170,6 @@ struct drm_i915_gem_request {
>  	/** Preallocate space in the ring for the emitting the request */
>  	u32 reserved_space;
>
> -	/**
> -	 * Context related to the previous request.
> -	 * As the contexts are accessed by the hardware until the switch is
> -	 * completed to a new context, the hardware may still be writing
> -	 * to the context object after the breadcrumb is visible. We must
> -	 * not unpin/unbind/prune that object whilst still active and so
> -	 * we keep the previous context pinned until the following (this)
> -	 * request is retired.
> -	 */
> -	struct i915_gem_context *previous_context;
> -
>  	/** Batch buffer related to this request if any (used for
>  	 * error state dump only).
>  	 */
> diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c
> index 7fa4e74c1dd3..3f144216e188 100644
> --- a/drivers/gpu/drm/i915/i915_guc_submission.c
> +++ b/drivers/gpu/drm/i915/i915_guc_submission.c
> @@ -533,17 +533,6 @@ static void __i915_guc_submit(struct drm_i915_gem_request *rq)
>
>  static void i915_guc_submit(struct drm_i915_gem_request *rq)
>  {
> -	struct intel_engine_cs *engine = rq->engine;
> -
> -	/* We keep the previous context alive until we retire the following
> -	 * request. This ensures that any the context object is still pinned
> -	 * for any residual writes the HW makes into it on the context switch
> -	 * into the next object following the breadcrumb. Otherwise, we may
> -	 * retire the context too early.
> -	 */
> -	rq->previous_context = engine->last_context;
> -	engine->last_context = rq->ctx;
> -
>  	i915_gem_request_submit(rq);
>  	__i915_guc_submit(rq);
>  }
> diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
> index 5669f0862458..cfe4152212b9 100644
> --- a/drivers/gpu/drm/i915/i915_perf.c
> +++ b/drivers/gpu/drm/i915/i915_perf.c
> @@ -641,7 +641,7 @@ static int i915_oa_read(struct i915_perf_stream *stream,
>  static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
>  {
>  	struct drm_i915_private *dev_priv = stream->dev_priv;
> -	struct i915_vma *vma;
> +	struct intel_engine_cs *engine = dev_priv->engine[RCS];
>  	int ret;
>
>  	ret = i915_mutex_lock_interruptible(&dev_priv->drm);
> @@ -653,19 +653,16 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
>  	 *
>  	 * NB: implied RCS engine...
>  	 */
> -	vma = i915_gem_context_pin_legacy(stream->ctx, 0);
> -	if (IS_ERR(vma)) {
> -		ret = PTR_ERR(vma);
> +	ret = engine->context_pin(engine, stream->ctx);
> +	if (ret)
>  		goto unlock;
> -	}
> -
> -	dev_priv->perf.oa.pinned_rcs_vma = vma;
>
>  	/* Explicitly track the ID (instead of calling i915_ggtt_offset()
>  	 * on the fly) considering the difference with gen8+ and
>  	 * execlists
>  	 */
> -	dev_priv->perf.oa.specific_ctx_id = i915_ggtt_offset(vma);
> +	dev_priv->perf.oa.specific_ctx_id =
> +		i915_ggtt_offset(stream->ctx->engine[engine->id].state);
>
>  unlock:
>  	mutex_unlock(&dev_priv->drm.struct_mutex);
> @@ -676,13 +673,12 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
>  static void oa_put_render_ctx_id(struct i915_perf_stream *stream)
>  {
>  	struct drm_i915_private *dev_priv = stream->dev_priv;
> +	struct intel_engine_cs *engine = dev_priv->engine[RCS];
>
>  	mutex_lock(&dev_priv->drm.struct_mutex);
>
> -	i915_vma_unpin(dev_priv->perf.oa.pinned_rcs_vma);
> -	dev_priv->perf.oa.pinned_rcs_vma = NULL;
> -
>  	dev_priv->perf.oa.specific_ctx_id = INVALID_CTX_ID;
> +	engine->context_unpin(engine, stream->ctx);
>
>  	mutex_unlock(&dev_priv->drm.struct_mutex);
>  }
> diff --git a/drivers/gpu/drm/i915/intel_engine_cs.c b/drivers/gpu/drm/i915/intel_engine_cs.c
> index e8afe1185831..97bbbc3d6aa8 100644
> --- a/drivers/gpu/drm/i915/intel_engine_cs.c
> +++ b/drivers/gpu/drm/i915/intel_engine_cs.c
> @@ -304,15 +304,30 @@ int intel_engine_init_common(struct intel_engine_cs *engine)
>  {
>  	int ret;
>
> -	ret = intel_engine_init_breadcrumbs(engine);
> +	/* We may need to do things with the shrinker which
> +	 * require us to immediately switch back to the default
> +	 * context. This can cause a problem as pinning the
> +	 * default context also requires GTT space which may not
> +	 * be available. To avoid this we always pin the default
> +	 * context.
> +	 */
> +	ret = engine->context_pin(engine, engine->i915->kernel_context);
>  	if (ret)
>  		return ret;
>
> +	ret = intel_engine_init_breadcrumbs(engine);
> +	if (ret)
> +		goto err_unpin;
> +
>  	ret = i915_gem_render_state_init(engine);
>  	if (ret)
> -		return ret;
> +		goto err_unpin;
>
>  	return 0;
> +
> +err_unpin:
> +	engine->context_unpin(engine, engine->i915->kernel_context);
> +	return ret;
>  }
>
>  /**
> @@ -330,6 +345,8 @@ void intel_engine_cleanup_common(struct intel_engine_cs *engine)
>  	intel_engine_fini_breadcrumbs(engine);
>  	intel_engine_cleanup_cmd_parser(engine);
>  	i915_gem_batch_pool_fini(&engine->batch_pool);
> +
> +	engine->context_unpin(engine, engine->i915->kernel_context);
>  }
>
>  u64 intel_engine_get_active_head(struct intel_engine_cs *engine)
> diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
> index 5cabe4e9d22f..58cea1f9ad27 100644
> --- a/drivers/gpu/drm/i915/intel_lrc.c
> +++ b/drivers/gpu/drm/i915/intel_lrc.c
> @@ -512,15 +512,6 @@ static void execlists_dequeue(struct intel_engine_cs *engine)
>  		RB_CLEAR_NODE(&cursor->priotree.node);
>  		cursor->priotree.priority = INT_MAX;
>
> -		/* We keep the previous context alive until we retire the
> -		 * following request. This ensures that any the context object
> -		 * is still pinned for any residual writes the HW makes into it
> -		 * on the context switch into the next object following the
> -		 * breadcrumb. Otherwise, we may retire the context too early.
> -		 */
> -		cursor->previous_context = engine->last_context;
> -		engine->last_context = cursor->ctx;
> -
>  		__i915_gem_request_submit(cursor);
>  		last = cursor;
>  		submit = true;
> @@ -772,8 +763,8 @@ static void execlists_schedule(struct drm_i915_gem_request *request, int prio)
>  	/* XXX Do we need to preempt to make room for us and our deps? */
>  }
>
> -static int intel_lr_context_pin(struct i915_gem_context *ctx,
> -				struct intel_engine_cs *engine)
> +static int execlists_context_pin(struct intel_engine_cs *engine,
> +				 struct i915_gem_context *ctx)
>  {
>  	struct intel_context *ce = &ctx->engine[engine->id];
>  	void *vaddr;
> @@ -784,6 +775,12 @@ static int intel_lr_context_pin(struct i915_gem_context *ctx,
>  	if (ce->pin_count++)
>  		return 0;
>
> +	if (!ce->state) {
> +		ret = execlists_context_deferred_alloc(ctx, engine);
> +		if (ret)
> +			goto err;
> +	}
> +
>  	ret = i915_vma_pin(ce->state, 0, GEN8_LR_CONTEXT_ALIGN,
>  			   PIN_OFFSET_BIAS | GUC_WOPCM_TOP | PIN_GLOBAL);
>  	if (ret)
> @@ -825,8 +822,8 @@ static int intel_lr_context_pin(struct i915_gem_context *ctx,
>  	return ret;
>  }
>
> -void intel_lr_context_unpin(struct i915_gem_context *ctx,
> -			    struct intel_engine_cs *engine)
> +static void execlists_context_unpin(struct intel_engine_cs *engine,
> +				    struct i915_gem_context *ctx)
>  {
>  	struct intel_context *ce = &ctx->engine[engine->id];
>
> @@ -850,24 +847,17 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request
>  	struct intel_context *ce = &request->ctx->engine[engine->id];
>  	int ret;
>
> +	GEM_BUG_ON(!ce->pin_count);
> +
>  	/* Flush enough space to reduce the likelihood of waiting after
>  	 * we start building the request - in which case we will just
>  	 * have to repeat work.
>  	 */
>  	request->reserved_space += EXECLISTS_REQUEST_SIZE;
>
> -	if (!ce->state) {
> -		ret = execlists_context_deferred_alloc(request->ctx, engine);
> -		if (ret)
> -			return ret;
> -	}
> -
> +	GEM_BUG_ON(!ce->ring);
>  	request->ring = ce->ring;
>
> -	ret = intel_lr_context_pin(request->ctx, engine);
> -	if (ret)
> -		return ret;
> -
>  	if (i915.enable_guc_submission) {
>  		/*
>  		 * Check that the GuC has space for the request before
> @@ -876,7 +866,7 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request
>  		 */
>  		ret = i915_guc_wq_reserve(request);
>  		if (ret)
> -			goto err_unpin;
> +			goto err;
>  	}
>
>  	ret = intel_ring_begin(request, 0);
> @@ -904,8 +894,7 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request
>  err_unreserve:
>  	if (i915.enable_guc_submission)
>  		i915_guc_wq_unreserve(request);
> -err_unpin:
> -	intel_lr_context_unpin(request->ctx, engine);
> +err:
>  	return ret;
>  }
>
> @@ -1789,13 +1778,12 @@ void intel_logical_ring_cleanup(struct intel_engine_cs *engine)
>  	if (engine->cleanup)
>  		engine->cleanup(engine);
>
> -	intel_engine_cleanup_common(engine);
> -
>  	if (engine->status_page.vma) {
>  		i915_gem_object_unpin_map(engine->status_page.vma->obj);
>  		engine->status_page.vma = NULL;
>  	}
> -	intel_lr_context_unpin(dev_priv->kernel_context, engine);
> +
> +	intel_engine_cleanup_common(engine);
>
>  	lrc_destroy_wa_ctx_obj(engine);
>  	engine->i915 = NULL;
> @@ -1820,6 +1808,10 @@ logical_ring_default_vfuncs(struct intel_engine_cs *engine)
>  	/* Default vfuncs which can be overriden by each engine. */
>  	engine->init_hw = gen8_init_common_ring;
>  	engine->reset_hw = reset_common_ring;
> +
> +	engine->context_pin = execlists_context_pin;
> +	engine->context_unpin = execlists_context_unpin;
> +
>  	engine->emit_flush = gen8_emit_flush;
>  	engine->emit_breadcrumb = gen8_emit_breadcrumb;
>  	engine->emit_breadcrumb_sz = gen8_emit_breadcrumb_sz;
> @@ -1902,18 +1894,6 @@ logical_ring_init(struct intel_engine_cs *engine)
>  	if (ret)
>  		goto error;
>
> -	ret = execlists_context_deferred_alloc(dctx, engine);
> -	if (ret)
> -		goto error;
> -
> -	/* As this is the default context, always pin it */
> -	ret = intel_lr_context_pin(dctx, engine);
> -	if (ret) {
> -		DRM_ERROR("Failed to pin context for %s: %d\n",
> -			  engine->name, ret);
> -		goto error;
> -	}
> -
>  	/* And setup the hardware status page. */
>  	ret = lrc_setup_hws(engine, dctx->engine[engine->id].state);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h
> index 7c6403243394..b5630331086a 100644
> --- a/drivers/gpu/drm/i915/intel_lrc.h
> +++ b/drivers/gpu/drm/i915/intel_lrc.h
> @@ -79,13 +79,10 @@ int intel_engines_init(struct drm_i915_private *dev_priv);
>  #define LRC_PPHWSP_PN	(LRC_GUCSHR_PN + 1)
>  #define LRC_STATE_PN	(LRC_PPHWSP_PN + 1)
>
> +struct drm_i915_private;
>  struct i915_gem_context;
>
>  uint32_t intel_lr_context_size(struct intel_engine_cs *engine);
> -void intel_lr_context_unpin(struct i915_gem_context *ctx,
> -			    struct intel_engine_cs *engine);
> -
> -struct drm_i915_private;
>
>  void intel_lr_context_resume(struct drm_i915_private *dev_priv);
>  uint64_t intel_lr_context_descriptor(struct i915_gem_context *ctx,
> diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
> index 0b7d13b6e228..a57eb5dec991 100644
> --- a/drivers/gpu/drm/i915/intel_ringbuffer.c
> +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
> @@ -1939,8 +1939,26 @@ intel_ring_free(struct intel_ring *ring)
>  	kfree(ring);
>  }
>
> -static int intel_ring_context_pin(struct i915_gem_context *ctx,
> -				  struct intel_engine_cs *engine)
> +static int context_pin(struct i915_gem_context *ctx, unsigned int flags)
> +{
> +	struct i915_vma *vma = ctx->engine[RCS].state;
> +	int ret;
> +
> +	/* Clear this page out of any CPU caches for coherent swap-in/out.
> +	 * We only want to do this on the first bind so that we do not stall
> +	 * on an active context (which by nature is already on the GPU).
> +	 */
> +	if (!(vma->flags & I915_VMA_GLOBAL_BIND)) {
> +		ret = i915_gem_object_set_to_gtt_domain(vma->obj, false);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return i915_vma_pin(vma, 0, ctx->ggtt_alignment, PIN_GLOBAL | flags);
> +}
> +
> +static int intel_ring_context_pin(struct intel_engine_cs *engine,
> +				  struct i915_gem_context *ctx)
>  {
>  	struct intel_context *ce = &ctx->engine[engine->id];
>  	int ret;
> @@ -1951,13 +1969,15 @@ static int intel_ring_context_pin(struct i915_gem_context *ctx,
>  		return 0;
>
>  	if (ce->state) {
> -		struct i915_vma *vma;
> +		unsigned int flags;
> +
> +		flags = 0;
> +		if (ctx == ctx->i915->kernel_context)
> +			flags = PIN_HIGH;
>
> -		vma = i915_gem_context_pin_legacy(ctx, PIN_HIGH);
> -		if (IS_ERR(vma)) {
> -			ret = PTR_ERR(vma);
> +		ret = context_pin(ctx, flags);
> +		if (ret)
>  			goto error;
> -		}
>  	}
>
>  	/* The kernel context is only used as a placeholder for flushing the
> @@ -1978,8 +1998,8 @@ static int intel_ring_context_pin(struct i915_gem_context *ctx,
>  	return ret;
>  }
>
> -static void intel_ring_context_unpin(struct i915_gem_context *ctx,
> -				     struct intel_engine_cs *engine)
> +static void intel_ring_context_unpin(struct intel_engine_cs *engine,
> +				     struct i915_gem_context *ctx)
>  {
>  	struct intel_context *ce = &ctx->engine[engine->id];
>
> @@ -2008,17 +2028,6 @@ static int intel_init_ring_buffer(struct intel_engine_cs *engine)
>  	if (ret)
>  		goto error;
>
> -	/* We may need to do things with the shrinker which
> -	 * require us to immediately switch back to the default
> -	 * context. This can cause a problem as pinning the
> -	 * default context also requires GTT space which may not
> -	 * be available. To avoid this we always pin the default
> -	 * context.
> -	 */
> -	ret = intel_ring_context_pin(dev_priv->kernel_context, engine);
> -	if (ret)
> -		goto error;
> -
>  	ring = intel_engine_create_ring(engine, 32 * PAGE_SIZE);
>  	if (IS_ERR(ring)) {
>  		ret = PTR_ERR(ring);
> @@ -2077,8 +2086,6 @@ void intel_engine_cleanup(struct intel_engine_cs *engine)
>
>  	intel_engine_cleanup_common(engine);
>
> -	intel_ring_context_unpin(dev_priv->kernel_context, engine);
> -
>  	engine->i915 = NULL;
>  	dev_priv->engine[engine->id] = NULL;
>  	kfree(engine);
> @@ -2099,12 +2106,15 @@ int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request)
>  {
>  	int ret;
>
> +	GEM_BUG_ON(!request->ctx->engine[request->engine->id].pin_count);
> +
>  	/* Flush enough space to reduce the likelihood of waiting after
>  	 * we start building the request - in which case we will just
>  	 * have to repeat work.
>  	 */
>  	request->reserved_space += LEGACY_REQUEST_SIZE;
>
> +	GEM_BUG_ON(!request->engine->buffer);
>  	request->ring = request->engine->buffer;
>
>  	ret = intel_ring_begin(request, 0);
> @@ -2584,6 +2594,9 @@ static void intel_ring_default_vfuncs(struct drm_i915_private *dev_priv,
>  	engine->init_hw = init_ring_common;
>  	engine->reset_hw = reset_ring_common;
>
> +	engine->context_pin = intel_ring_context_pin;
> +	engine->context_unpin = intel_ring_context_unpin;
> +
>  	engine->emit_breadcrumb = i9xx_emit_breadcrumb;
>  	engine->emit_breadcrumb_sz = i9xx_emit_breadcrumb_sz;
>  	if (i915.semaphores) {
> diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
> index d8b066fd5dcf..0b69a50ab833 100644
> --- a/drivers/gpu/drm/i915/intel_ringbuffer.h
> +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
> @@ -268,6 +268,10 @@ struct intel_engine_cs {
>  	void		(*reset_hw)(struct intel_engine_cs *engine,
>  				    struct drm_i915_gem_request *req);
>
> +	int		(*context_pin)(struct intel_engine_cs *engine,
> +				       struct i915_gem_context *ctx);
> +	void		(*context_unpin)(struct intel_engine_cs *engine,
> +					 struct i915_gem_context *ctx);
>  	int		(*init_context)(struct drm_i915_gem_request *req);
>
>  	int		(*emit_flush)(struct drm_i915_gem_request *request,
>

Well well well, this is nice! I really like the unification (and 
simplification), it only worries me a bit I did not spot any problems. 
:) But the code is so much easier to follow now and maintain. And for 
the newcomers especially. I know how much it took me to get to the grips 
with execlists, and the legacy was completely different.

I would consider the engine->last_context rename to be clear it is not 
the last submitted context which was the established use until this patch.

With that:

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 08/16] drm/i915: Unify active context tracking between legacy/execlists/guc
  2016-12-09 11:48   ` Tvrtko Ursulin
@ 2016-12-09 12:17     ` Chris Wilson
  0 siblings, 0 replies; 62+ messages in thread
From: Chris Wilson @ 2016-12-09 12:17 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

On Fri, Dec 09, 2016 at 11:48:15AM +0000, Tvrtko Ursulin wrote:
> 
> On 07/12/2016 13:58, Chris Wilson wrote:
> >The requests conversion introduced a nasty bug where we could generate a
> >new request in the middle of constructing a request. The new request
> >would be executed (and waited upon) before the current one, creating a
> >minor havoc in the seqno accounting. (Prior to deferred seqno
> >assignment, this would mean that the seqno would be out of order, and
> >the current request would be deemed complete even before it was
> >submitted.)
> 
> How exactly it can generate and submit a request while creating a
> request? It would be good to describe it here.

i915_switch_context can create a request in the middle of servicing the
request. It used to cause the seqno to advance upsetting the tracking
(causing a chance that this the actual request is retired early),
just fortunate that there are no earlier instructions to mess up the
ring->tail.

> >We also employed two different mechanisms to track the active context
> >until it was switched out. The legacy method allowed for waiting upon an
> >active context (it could forcibly evict any vma, including context's),
> >but the execlists method took a step backwards by pinning the vma for
> >the entire active lifespan of the context (the only way to evict was to
> >idle the entire GPU, not individual contexts). However, to circumvent
> 
> Because of the pin in request creation?

Because of the unpin in retirement.

Legacy, pin in create, unpin on the next request.
Execlists, pin in create, unpin on retirment.

To unbind a context in legacy, just requires selecting it for eviction.
To unbind a context in execlists, requires idling the GPU.

Now both require idling the GPU, which is more of an issue for the
shared GTT in legacy - but hopefully the contexts were the last victims
anyway.

Hmm. Probably wise to restore the retire at the start of eviction.
We removed it because we had faith in our inactive tracking, however we
know have stray pins to clear.
 
> >+	/* The backing object for the context is done after switching to the
> >+	 * *next* context. Therefore we cannot retire the previous context until
> >+	 * the next context has already started running. However, since we
> >+	 * cannot take the required locks at i915_gem_request_submit() we
> >+	 * defer the unpinning of the active context to now, retirement of
> >+	 * the subsequent request.
> >+	 */
> >+	if (engine->last_context)
> >+		engine->context_unpin(engine, engine->last_context);
> >+	engine->last_context = request->ctx;
> 
> Would it be worth renaming this member to last_retired_context for clarity?

Definitely makes it clearer.
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 09/16] drm/i915: Simplify releasing context reference
  2016-12-07 13:58 ` [PATCH 09/16] drm/i915: Simplify releasing context reference Chris Wilson
@ 2016-12-09 15:03   ` Tvrtko Ursulin
  0 siblings, 0 replies; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-09 15:03 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> A few users only take the struct_mutex in order to release a reference
> to a context. We can expose a kref_put_mutex() wrapper in order to
> simplify these users, and optimise taking of the mutex to the final
> unref.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/i915_drv.h  |  7 +++++++
>  drivers/gpu/drm/i915/i915_perf.c | 16 ++++------------
>  2 files changed, 11 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 7c228622716a..3411e38e32af 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -3320,6 +3320,13 @@ static inline void i915_gem_context_put(struct i915_gem_context *ctx)
>  	kref_put(&ctx->ref, i915_gem_context_free);
>  }
>
> +static inline void i915_gem_context_put_unlocked(struct i915_gem_context *ctx)
> +{
> +	kref_put_mutex(&ctx->ref,
> +		       i915_gem_context_free,
> +		       &ctx->i915->drm.struct_mutex);
> +}
> +
>  static inline struct intel_timeline *
>  i915_gem_context_lookup_timeline(struct i915_gem_context *ctx,
>  				 struct intel_engine_cs *engine)
> diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
> index cfe4152212b9..ee6271fe96de 100644
> --- a/drivers/gpu/drm/i915/i915_perf.c
> +++ b/drivers/gpu/drm/i915/i915_perf.c
> @@ -1298,8 +1298,6 @@ static long i915_perf_ioctl(struct file *file,
>
>  static void i915_perf_destroy_locked(struct i915_perf_stream *stream)
>  {
> -	struct drm_i915_private *dev_priv = stream->dev_priv;
> -
>  	if (stream->enabled)
>  		i915_perf_disable_locked(stream);
>
> @@ -1308,11 +1306,8 @@ static void i915_perf_destroy_locked(struct i915_perf_stream *stream)
>
>  	list_del(&stream->link);
>
> -	if (stream->ctx) {
> -		mutex_lock(&dev_priv->drm.struct_mutex);
> -		i915_gem_context_put(stream->ctx);
> -		mutex_unlock(&dev_priv->drm.struct_mutex);
> -	}
> +	if (stream->ctx)
> +		i915_gem_context_put_unlocked(stream->ctx);
>
>  	kfree(stream);
>  }
> @@ -1446,11 +1441,8 @@ i915_perf_open_ioctl_locked(struct drm_i915_private *dev_priv,
>  err_alloc:
>  	kfree(stream);
>  err_ctx:
> -	if (specific_ctx) {
> -		mutex_lock(&dev_priv->drm.struct_mutex);
> -		i915_gem_context_put(specific_ctx);
> -		mutex_unlock(&dev_priv->drm.struct_mutex);
> -	}
> +	if (specific_ctx)
> +		i915_gem_context_put_unlocked(specific_ctx);
>  err:
>  	return ret;
>  }
>

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 10/16] drm/i915: Mark the shadow gvt context as closed
  2016-12-07 13:58 ` [PATCH 10/16] drm/i915: Mark the shadow gvt context as closed Chris Wilson
@ 2016-12-09 15:07   ` Tvrtko Ursulin
  0 siblings, 0 replies; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-09 15:07 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> As the shadow gvt is not user accessible and does not have an associated
> vm, we can mark it as closed during its construction. This saves leaking
> the internal knowledge of i915_gem_context into gvt/.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/gvt/scheduler.c    | 10 +---------
>  drivers/gpu/drm/i915/i915_gem_context.c |  1 +
>  2 files changed, 2 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c
> index 4db242250235..fd2b026f7ecd 100644
> --- a/drivers/gpu/drm/i915/gvt/scheduler.c
> +++ b/drivers/gpu/drm/i915/gvt/scheduler.c
> @@ -549,18 +549,10 @@ int intel_gvt_init_workload_scheduler(struct intel_gvt *gvt)
>
>  void intel_vgpu_clean_gvt_context(struct intel_vgpu *vgpu)
>  {
> -	struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
> -
>  	atomic_notifier_chain_unregister(&vgpu->shadow_ctx->status_notifier,
>  			&vgpu->shadow_ctx_notifier_block);
>
> -	mutex_lock(&dev_priv->drm.struct_mutex);
> -
> -	/* a little hacky to mark as ctx closed */
> -	vgpu->shadow_ctx->closed = true;
> -	i915_gem_context_put(vgpu->shadow_ctx);
> -
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
> +	i915_gem_context_put_unlocked(vgpu->shadow_ctx);
>  }
>
>  int intel_vgpu_init_gvt_context(struct intel_vgpu *vgpu)
> diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
> index 95812c26767c..042befd263fe 100644
> --- a/drivers/gpu/drm/i915/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/i915_gem_context.c
> @@ -409,6 +409,7 @@ i915_gem_context_create_gvt(struct drm_device *dev)
>  	if (IS_ERR(ctx))
>  		goto out;
>
> +	ctx->closed = true; /* not user accessible */
>  	ctx->execlists_force_single_submission = true;
>  	ctx->ring_size = 512 * PAGE_SIZE; /* Max ring buffer size */
>  out:
>

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 11/16] drm/i915/execlists: Request the kernel context be pinned high
  2016-12-07 13:58 ` [PATCH 11/16] drm/i915/execlists: Request the kernel context be pinned high Chris Wilson
@ 2016-12-09 15:08   ` Tvrtko Ursulin
  0 siblings, 0 replies; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-09 15:08 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> PIN_HIGH is an expensive operation (in comparison to allocating from the
> hole stack) unsuitable for frequent use (such as switching between
> contexts). However, the kernel context should be pinned just once for
> the lifetime of the driver, and here it is appropriate to keep it out of
> the mappable range (in order to maximise mappable space for users).
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/intel_lrc.c | 8 ++++++--
>  1 file changed, 6 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
> index 58cea1f9ad27..22ded92d0346 100644
> --- a/drivers/gpu/drm/i915/intel_lrc.c
> +++ b/drivers/gpu/drm/i915/intel_lrc.c
> @@ -767,6 +767,7 @@ static int execlists_context_pin(struct intel_engine_cs *engine,
>  				 struct i915_gem_context *ctx)
>  {
>  	struct intel_context *ce = &ctx->engine[engine->id];
> +	unsigned int flags;
>  	void *vaddr;
>  	int ret;
>
> @@ -781,8 +782,11 @@ static int execlists_context_pin(struct intel_engine_cs *engine,
>  			goto err;
>  	}
>
> -	ret = i915_vma_pin(ce->state, 0, GEN8_LR_CONTEXT_ALIGN,
> -			   PIN_OFFSET_BIAS | GUC_WOPCM_TOP | PIN_GLOBAL);
> +	flags = PIN_OFFSET_BIAS | GUC_WOPCM_TOP | PIN_GLOBAL;
> +	if (ctx == ctx->i915->kernel_context)
> +		flags |= PIN_HIGH;
> +
> +	ret = i915_vma_pin(ce->state, 0, GEN8_LR_CONTEXT_ALIGN, flags);
>  	if (ret)
>  		goto err;
>
>

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 12/16] drm/i915: Swap if(enable_execlists) in i915_gem_request_alloc for a vfunc
  2016-12-07 13:58 ` [PATCH 12/16] drm/i915: Swap if(enable_execlists) in i915_gem_request_alloc for a vfunc Chris Wilson
@ 2016-12-09 15:16   ` Tvrtko Ursulin
  2016-12-09 15:25     ` Chris Wilson
  0 siblings, 1 reply; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-09 15:16 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> A fairly trivial move of a matching pair of routines (for preparing a
> request for construction) onto an engine vfunc. The ulterior motive is
> to be able to create a mock request implementation.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/i915_gem_request.c | 5 +----
>  drivers/gpu/drm/i915/intel_lrc.c        | 4 +++-
>  drivers/gpu/drm/i915/intel_lrc.h        | 2 --
>  drivers/gpu/drm/i915/intel_ringbuffer.c | 4 +++-
>  drivers/gpu/drm/i915/intel_ringbuffer.h | 3 +--
>  5 files changed, 8 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
> index 06e9a607d934..881bed5347fb 100644
> --- a/drivers/gpu/drm/i915/i915_gem_request.c
> +++ b/drivers/gpu/drm/i915/i915_gem_request.c
> @@ -622,10 +622,7 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
>  	req->reserved_space = MIN_SPACE_FOR_ADD_REQUEST;
>  	GEM_BUG_ON(req->reserved_space < engine->emit_breadcrumb_sz);
>
> -	if (i915.enable_execlists)
> -		ret = intel_logical_ring_alloc_request_extras(req);
> -	else
> -		ret = intel_ring_alloc_request_extras(req);
> +	ret = engine->request_alloc(req);
>  	if (ret)
>  		goto err_ctx;
>
> diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
> index 22ded92d0346..3f536ef37968 100644
> --- a/drivers/gpu/drm/i915/intel_lrc.c
> +++ b/drivers/gpu/drm/i915/intel_lrc.c
> @@ -845,7 +845,7 @@ static void execlists_context_unpin(struct intel_engine_cs *engine,
>  	i915_gem_context_put(ctx);
>  }
>
> -int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request)
> +static int execlists_request_alloc(struct drm_i915_gem_request *request)
>  {
>  	struct intel_engine_cs *engine = request->engine;
>  	struct intel_context *ce = &request->ctx->engine[engine->id];
> @@ -1816,6 +1816,8 @@ logical_ring_default_vfuncs(struct intel_engine_cs *engine)
>  	engine->context_pin = execlists_context_pin;
>  	engine->context_unpin = execlists_context_unpin;
>
> +	engine->request_alloc = execlists_request_alloc;
> +
>  	engine->emit_flush = gen8_emit_flush;
>  	engine->emit_breadcrumb = gen8_emit_breadcrumb;
>  	engine->emit_breadcrumb_sz = gen8_emit_breadcrumb_sz;
> diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h
> index b5630331086a..01ba36ea125e 100644
> --- a/drivers/gpu/drm/i915/intel_lrc.h
> +++ b/drivers/gpu/drm/i915/intel_lrc.h
> @@ -63,8 +63,6 @@ enum {
>  };
>
>  /* Logical Rings */
> -int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request);
> -int intel_logical_ring_reserve_space(struct drm_i915_gem_request *request);
>  void intel_logical_ring_stop(struct intel_engine_cs *engine);
>  void intel_logical_ring_cleanup(struct intel_engine_cs *engine);
>  int logical_render_ring_init(struct intel_engine_cs *engine);
> diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
> index a57eb5dec991..a42b9bf45b42 100644
> --- a/drivers/gpu/drm/i915/intel_ringbuffer.c
> +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
> @@ -2102,7 +2102,7 @@ void intel_legacy_submission_resume(struct drm_i915_private *dev_priv)
>  	}
>  }
>
> -int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request)
> +static int ring_request_alloc(struct drm_i915_gem_request *request)
>  {
>  	int ret;
>
> @@ -2597,6 +2597,8 @@ static void intel_ring_default_vfuncs(struct drm_i915_private *dev_priv,
>  	engine->context_pin = intel_ring_context_pin;
>  	engine->context_unpin = intel_ring_context_unpin;
>
> +	engine->request_alloc = ring_request_alloc;
> +
>  	engine->emit_breadcrumb = i9xx_emit_breadcrumb;
>  	engine->emit_breadcrumb_sz = i9xx_emit_breadcrumb_sz;
>  	if (i915.semaphores) {
> diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
> index 0b69a50ab833..12f4dd1cbf9c 100644
> --- a/drivers/gpu/drm/i915/intel_ringbuffer.h
> +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
> @@ -272,6 +272,7 @@ struct intel_engine_cs {
>  				       struct i915_gem_context *ctx);
>  	void		(*context_unpin)(struct intel_engine_cs *engine,
>  					 struct i915_gem_context *ctx);
> +	int		(*request_alloc)(struct drm_i915_gem_request *req);
>  	int		(*init_context)(struct drm_i915_gem_request *req);
>
>  	int		(*emit_flush)(struct drm_i915_gem_request *request,
> @@ -476,8 +477,6 @@ void intel_engine_cleanup(struct intel_engine_cs *engine);
>
>  void intel_legacy_submission_resume(struct drm_i915_private *dev_priv);
>
> -int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request);
> -
>  int __must_check intel_ring_begin(struct drm_i915_gem_request *req, int n);
>  int __must_check intel_ring_cacheline_align(struct drm_i915_gem_request *req);
>
>

In dev_priv->gt and not per engine would be better. I think we talked 
about this in some other context as well.

Blast, the same then applies to engine->context_(un)pin. :)

What do you think?

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 12/16] drm/i915: Swap if(enable_execlists) in i915_gem_request_alloc for a vfunc
  2016-12-09 15:16   ` Tvrtko Ursulin
@ 2016-12-09 15:25     ` Chris Wilson
  2016-12-09 15:53       ` Tvrtko Ursulin
  0 siblings, 1 reply; 62+ messages in thread
From: Chris Wilson @ 2016-12-09 15:25 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

On Fri, Dec 09, 2016 at 03:16:33PM +0000, Tvrtko Ursulin wrote:
> 
> On 07/12/2016 13:58, Chris Wilson wrote:
> >A fairly trivial move of a matching pair of routines (for preparing a
> >request for construction) onto an engine vfunc. The ulterior motive is
> >to be able to create a mock request implementation.
> >
> >Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> >---
> > drivers/gpu/drm/i915/i915_gem_request.c | 5 +----
> > drivers/gpu/drm/i915/intel_lrc.c        | 4 +++-
> > drivers/gpu/drm/i915/intel_lrc.h        | 2 --
> > drivers/gpu/drm/i915/intel_ringbuffer.c | 4 +++-
> > drivers/gpu/drm/i915/intel_ringbuffer.h | 3 +--
> > 5 files changed, 8 insertions(+), 10 deletions(-)
> >
> >diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
> >index 06e9a607d934..881bed5347fb 100644
> >--- a/drivers/gpu/drm/i915/i915_gem_request.c
> >+++ b/drivers/gpu/drm/i915/i915_gem_request.c
> >@@ -622,10 +622,7 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
> > 	req->reserved_space = MIN_SPACE_FOR_ADD_REQUEST;
> > 	GEM_BUG_ON(req->reserved_space < engine->emit_breadcrumb_sz);
> >
> >-	if (i915.enable_execlists)
> >-		ret = intel_logical_ring_alloc_request_extras(req);
> >-	else
> >-		ret = intel_ring_alloc_request_extras(req);
> >+	ret = engine->request_alloc(req);
> > 	if (ret)
> > 		goto err_ctx;
> >
> >diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
> >index 22ded92d0346..3f536ef37968 100644
> >--- a/drivers/gpu/drm/i915/intel_lrc.c
> >+++ b/drivers/gpu/drm/i915/intel_lrc.c
> >@@ -845,7 +845,7 @@ static void execlists_context_unpin(struct intel_engine_cs *engine,
> > 	i915_gem_context_put(ctx);
> > }
> >
> >-int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request)
> >+static int execlists_request_alloc(struct drm_i915_gem_request *request)
> > {
> > 	struct intel_engine_cs *engine = request->engine;
> > 	struct intel_context *ce = &request->ctx->engine[engine->id];
> >@@ -1816,6 +1816,8 @@ logical_ring_default_vfuncs(struct intel_engine_cs *engine)
> > 	engine->context_pin = execlists_context_pin;
> > 	engine->context_unpin = execlists_context_unpin;
> >
> >+	engine->request_alloc = execlists_request_alloc;
> >+
> > 	engine->emit_flush = gen8_emit_flush;
> > 	engine->emit_breadcrumb = gen8_emit_breadcrumb;
> > 	engine->emit_breadcrumb_sz = gen8_emit_breadcrumb_sz;
> >diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h
> >index b5630331086a..01ba36ea125e 100644
> >--- a/drivers/gpu/drm/i915/intel_lrc.h
> >+++ b/drivers/gpu/drm/i915/intel_lrc.h
> >@@ -63,8 +63,6 @@ enum {
> > };
> >
> > /* Logical Rings */
> >-int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request);
> >-int intel_logical_ring_reserve_space(struct drm_i915_gem_request *request);
> > void intel_logical_ring_stop(struct intel_engine_cs *engine);
> > void intel_logical_ring_cleanup(struct intel_engine_cs *engine);
> > int logical_render_ring_init(struct intel_engine_cs *engine);
> >diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
> >index a57eb5dec991..a42b9bf45b42 100644
> >--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
> >+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
> >@@ -2102,7 +2102,7 @@ void intel_legacy_submission_resume(struct drm_i915_private *dev_priv)
> > 	}
> > }
> >
> >-int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request)
> >+static int ring_request_alloc(struct drm_i915_gem_request *request)
> > {
> > 	int ret;
> >
> >@@ -2597,6 +2597,8 @@ static void intel_ring_default_vfuncs(struct drm_i915_private *dev_priv,
> > 	engine->context_pin = intel_ring_context_pin;
> > 	engine->context_unpin = intel_ring_context_unpin;
> >
> >+	engine->request_alloc = ring_request_alloc;
> >+
> > 	engine->emit_breadcrumb = i9xx_emit_breadcrumb;
> > 	engine->emit_breadcrumb_sz = i9xx_emit_breadcrumb_sz;
> > 	if (i915.semaphores) {
> >diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
> >index 0b69a50ab833..12f4dd1cbf9c 100644
> >--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
> >+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
> >@@ -272,6 +272,7 @@ struct intel_engine_cs {
> > 				       struct i915_gem_context *ctx);
> > 	void		(*context_unpin)(struct intel_engine_cs *engine,
> > 					 struct i915_gem_context *ctx);
> >+	int		(*request_alloc)(struct drm_i915_gem_request *req);
> > 	int		(*init_context)(struct drm_i915_gem_request *req);
> >
> > 	int		(*emit_flush)(struct drm_i915_gem_request *request,
> >@@ -476,8 +477,6 @@ void intel_engine_cleanup(struct intel_engine_cs *engine);
> >
> > void intel_legacy_submission_resume(struct drm_i915_private *dev_priv);
> >
> >-int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request);
> >-
> > int __must_check intel_ring_begin(struct drm_i915_gem_request *req, int n);
> > int __must_check intel_ring_cacheline_align(struct drm_i915_gem_request *req);
> >
> >
> 
> In dev_priv->gt and not per engine would be better. I think we
> talked about this in some other context as well.
>
> Blast, the same then applies to engine->context_(un)pin. :)
> 
I've toyed with the idea of splitting context_pin for RCS/xCS on legacy
since only RCS has a context, and before Ironlake we have no contexts at
all.

->request_alloc() could specialise itself per engine, in theory at
least, don't know if we ever would want to do so in practice.

> What do you think?

Quite happy to pencil the idea in for later under sorting out a new split
for GT vs engine [vs scheduler] ;)
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 13/16] drm/i915: Add selftests for i915_gem_request
  2016-12-07 13:58 ` [PATCH 13/16] drm/i915: Add selftests for i915_gem_request Chris Wilson
@ 2016-12-09 15:51   ` Tvrtko Ursulin
  0 siblings, 0 replies; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-09 15:51 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> Simple starting point for adding seltests for i915_gem_request, first
> mock a device (with engines and contexts) that allows us to construct
> and execute a request, along with waiting for the request to complete.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/i915_gem_request.c    | 402 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/i915/i915_mock_selftests.h |   1 +
>  2 files changed, 403 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
> index 881bed5347fb..6553457adc77 100644
> --- a/drivers/gpu/drm/i915/i915_gem_request.c
> +++ b/drivers/gpu/drm/i915/i915_gem_request.c
> @@ -1208,3 +1208,405 @@ void i915_gem_retire_requests(struct drm_i915_private *dev_priv)
>  	for_each_engine(engine, dev_priv, id)
>  		engine_retire_requests(engine);
>  }
> +
> +#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
> +#include "i915_selftest.h"
> +
> +struct mock_engine {
> +	struct intel_engine_cs base;
> +
> +	spinlock_t hw_lock;
> +	struct list_head hw_queue;
> +	struct timer_list hw_delay;
> +};
> +
> +struct mock_request {
> +	struct drm_i915_gem_request base;
> +
> +	struct list_head link;
> +	unsigned long delay;
> +};
> +
> +static void mock_seqno_advance(struct intel_engine_cs *engine, u32 seqno)
> +{
> +	intel_write_status_page(engine, I915_GEM_HWS_INDEX, seqno);
> +	intel_engine_wakeup(engine);
> +}
> +
> +static void hw_delay_complete(unsigned long data)
> +{
> +	struct mock_engine *engine = (typeof(engine))data;
> +	struct mock_request *request;
> +
> +	spin_lock(&engine->hw_lock);
> +	request = list_first_entry_or_null(&engine->hw_queue,
> +					   typeof(*request),
> +					   link);
> +	if (request) {
> +		list_del(&request->link);
> +		mock_seqno_advance(&engine->base, request->base.global_seqno);
> +	}
> +
> +	request = list_first_entry_or_null(&engine->hw_queue,
> +					   typeof(*request),
> +					   link);
> +	if (request)
> +		mod_timer(&engine->hw_delay, jiffies + request->delay);
> +	spin_unlock(&engine->hw_lock);
> +}
> +
> +static void mock_engine_flush(struct intel_engine_cs *engine)
> +{
> +	struct mock_engine *mock =
> +		container_of(engine, typeof(*mock), base);
> +	struct mock_request *request, *rn;
> +
> +	del_timer_sync(&mock->hw_delay);
> +
> +	spin_lock_irq(&mock->hw_lock);
> +	list_for_each_entry_safe(request, rn, &mock->hw_queue, link) {
> +		list_del_init(&request->link);
> +		mock_seqno_advance(&mock->base, request->base.global_seqno);
> +	}
> +	spin_unlock_irq(&mock->hw_lock);
> +}
> +
> +static int mock_context_pin(struct intel_engine_cs *engine,
> +			    struct i915_gem_context *ctx)
> +{
> +	i915_gem_context_get(ctx);
> +	return 0;
> +}
> +
> +static void mock_context_unpin(struct intel_engine_cs *engine,
> +			       struct i915_gem_context *ctx)
> +{
> +	i915_gem_context_put(ctx);
> +}
> +
> +static int mock_request_alloc(struct drm_i915_gem_request *request)
> +{
> +	request->ring = request->engine->buffer;
> +	return 0;
> +}
> +
> +static int mock_emit_flush(struct drm_i915_gem_request *request,
> +			   unsigned int flags)
> +{
> +	return 0;
> +}
> +
> +static void mock_emit_breadcrumb(struct drm_i915_gem_request *request,
> +				 u32 *flags)
> +{
> +}
> +
> +static void mock_submit_request(struct drm_i915_gem_request *request)
> +{
> +	struct mock_request *mock = container_of(request, typeof(*mock), base);
> +	struct mock_engine *engine =
> +		container_of(request->engine, typeof(*engine), base);
> +
> +	i915_gem_request_submit(request);
> +
> +	spin_lock_irq(&engine->hw_lock);
> +	list_add_tail(&mock->link, &engine->hw_queue);
> +	if (list_first_entry(&engine->hw_queue, typeof(*mock), link) == mock)
> +		mod_timer(&engine->hw_delay, jiffies + mock->delay);
> +	spin_unlock_irq(&engine->hw_lock);
> +}
> +
> +static struct drm_i915_gem_request *
> +mock_request(struct intel_engine_cs *engine,
> +	     struct i915_gem_context *context,
> +	     unsigned long delay)
> +{
> +	struct drm_i915_gem_request *request;
> +	struct mock_request *mock;
> +
> +	request = i915_gem_request_alloc(engine, context);
> +	if (!request)
> +		return NULL;
> +
> +	mock = container_of(request, typeof(*mock), base);
> +	INIT_LIST_HEAD(&mock->link);
> +	mock->delay = delay;

How about just:

mock = (struct mock_request *)i915_gem_request_alloc(...)

?

Both versions are equally confusing until the reader figures out the 
kmem cache size is correct. Doesn't matter really, just thinking out 
loud. :)

> +
> +	return &mock->base;
> +}
> +
> +static struct intel_ring *mock_ring(struct intel_engine_cs *engine)
> +{
> +	struct intel_ring *ring;
> +
> +	ring = kzalloc(sizeof(*ring) + 4096, GFP_KERNEL);
> +	if (!ring)
> +		return NULL;
> +
> +	ring->engine = engine;
> +	ring->size = 4096;
> +	ring->effective_size = ring->size;
> +	ring->vaddr = (void *)(ring + 1);
> +
> +	INIT_LIST_HEAD(&ring->request_list);
> +	ring->last_retired_head = -1;
> +	intel_ring_update_space(ring);
> +
> +	return ring;
> +}
> +
> +static struct intel_engine_cs *mock_engine(struct drm_i915_private *i915,
> +					   const char *name)
> +{
> +	struct mock_engine *engine;
> +	static int id;
> +
> +	engine = kzalloc(sizeof(*engine) + 4096, GFP_KERNEL);
> +	if (!engine)
> +		return NULL;
> +
> +	engine->base.buffer = mock_ring(&engine->base);
> +	if (!engine->base.buffer) {
> +		kfree(engine);
> +		return NULL;
> +	}
> +
> +	/* minimal engine setup for requests */
> +	engine->base.i915 = i915;
> +	engine->base.name = name;
> +	engine->base.id = id++;
> +	engine->base.status_page.page_addr = (void *)(engine + 1);
> +
> +	engine->base.context_pin = mock_context_pin;
> +	engine->base.context_unpin = mock_context_unpin;
> +	engine->base.request_alloc = mock_request_alloc;
> +	engine->base.emit_flush = mock_emit_flush;
> +	engine->base.emit_breadcrumb = mock_emit_breadcrumb;
> +	engine->base.submit_request = mock_submit_request;
> +
> +	engine->base.timeline =
> +		&i915->gt.global_timeline.engine[engine->base.id];
> +
> +	/* minimal breadcrumbs init */
> +	spin_lock_init(&engine->base.breadcrumbs.lock);
> +	engine->base.breadcrumbs.mock = true;
> +
> +	/* fake hw queue */
> +	spin_lock_init(&engine->hw_lock);
> +	setup_timer(&engine->hw_delay,
> +		    hw_delay_complete,
> +		    (unsigned long)engine);
> +	INIT_LIST_HEAD(&engine->hw_queue);
> +
> +	return &engine->base;
> +}
> +
> +static void mock_engine_free(struct intel_engine_cs *engine)
> +{
> +	if (engine->last_context)
> +		engine->context_unpin(engine, engine->last_context);
> +
> +	kfree(engine->buffer);
> +	kfree(engine);
> +}
> +
> +static void mock_ppgtt_cleanup(struct i915_address_space *vm)
> +{
> +}
> +
> +static struct i915_hw_ppgtt *
> +mock_ppgtt(struct drm_i915_private *i915,
> +	   const char *name)
> +{
> +	struct i915_hw_ppgtt *ppgtt;
> +
> +	ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
> +	if (!ppgtt)
> +		return NULL;
> +
> +	kref_init(&ppgtt->ref);
> +
> +	INIT_LIST_HEAD(&ppgtt->base.active_list);
> +	INIT_LIST_HEAD(&ppgtt->base.inactive_list);
> +	INIT_LIST_HEAD(&ppgtt->base.unbound_list);
> +
> +	INIT_LIST_HEAD(&ppgtt->base.global_link);
> +	drm_mm_init(&ppgtt->base.mm, 0, ~0);
> +	i915_gem_timeline_init(i915, &ppgtt->base.timeline, name);
> +
> +	ppgtt->base.cleanup = mock_ppgtt_cleanup;
> +
> +	return ppgtt;
> +}
> +
> +static struct i915_gem_context *
> +mock_context(struct drm_i915_private *i915,
> +	     const char *name)
> +{
> +	struct i915_gem_context *ctx;
> +	int ret;
> +
> +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx)
> +		return NULL;
> +
> +	kref_init(&ctx->ref);
> +	INIT_LIST_HEAD(&ctx->link);
> +	ctx->name = name ? kstrdup(name, GFP_KERNEL) : NULL;
> +	ctx->i915 = i915;
> +
> +	ret = ida_simple_get(&i915->context_hw_ida,
> +			     0, MAX_CONTEXT_HW_ID, GFP_KERNEL);
> +	if (ret < 0) {
> +		kfree(ctx);
> +		return NULL;
> +	}
> +	ctx->hw_id = ret;
> +
> +	if (name) {
> +		ctx->ppgtt = mock_ppgtt(i915, name);
> +		if (!ctx->ppgtt) {
> +			kfree(ctx);
> +			return NULL;
> +		}
> +	}
> +
> +	return ctx;
> +}
> +
> +static void mock_retire_work_handler(struct work_struct *work)
> +{
> +}
> +
> +static void mock_idle_work_handler(struct work_struct *work)
> +{
> +}
> +
> +static struct drm_i915_private *mock_device(void)
> +{
> +	struct drm_i915_private *i915;
> +
> +	i915 = kzalloc(sizeof(*i915), GFP_KERNEL);
> +	if (!i915)
> +		return NULL;
> +
> +	mutex_init(&i915->drm.struct_mutex);
> +
> +	init_waitqueue_head(&i915->gpu_error.wait_queue);
> +	init_waitqueue_head(&i915->gpu_error.reset_queue);
> +
> +	ida_init(&i915->context_hw_ida);
> +
> +	INIT_DELAYED_WORK(&i915->gt.retire_work, mock_retire_work_handler);
> +	INIT_DELAYED_WORK(&i915->gt.idle_work, mock_idle_work_handler);
> +
> +	INIT_LIST_HEAD(&i915->gt.timelines);
> +	i915->gt.awake = true;
> +
> +	mutex_lock(&i915->drm.struct_mutex);
> +	i915_gem_timeline_init__global(i915);
> +	i915_gem_timeline_init(i915, &i915->ggtt.base.timeline, "mock");
> +	mutex_unlock(&i915->drm.struct_mutex);
> +
> +	i915->requests = KMEM_CACHE(mock_request,
> +				    SLAB_HWCACHE_ALIGN |
> +				    SLAB_RECLAIM_ACCOUNT |
> +				    SLAB_DESTROY_BY_RCU);
> +	if (!i915->requests)
> +		goto err_device;
> +
> +	i915->dependencies = KMEM_CACHE(i915_dependency,
> +					SLAB_HWCACHE_ALIGN |
> +					SLAB_RECLAIM_ACCOUNT);
> +	if (!i915->dependencies)
> +		goto err_requests;
> +
> +	mkwrite_device_info(i915)->ring_mask = BIT(0);
> +	i915->engine[RCS] = mock_engine(i915, "mock");
> +	if (!i915->engine[RCS])
> +		goto err_dependencies;
> +
> +	i915->kernel_context = mock_context(i915, NULL);
> +	if (!i915->kernel_context)
> +		goto err_engine;
> +
> +	return i915;
> +
> +err_engine:
> +	mock_engine_free(i915->engine[RCS]);
> +err_dependencies:
> +	kmem_cache_destroy(i915->dependencies);
> +err_requests:
> +	kmem_cache_destroy(i915->requests);
> +err_device:
> +	kfree(i915);
> +	return NULL;
> +}
> +
> +static void mock_device_free(struct drm_i915_private *i915)
> +{
> +	struct intel_engine_cs *engine;
> +	enum intel_engine_id id;
> +
> +	for_each_engine(engine, i915, id)
> +		mock_engine_flush(engine);
> +
> +	mutex_lock(&i915->drm.struct_mutex);
> +	i915_gem_retire_requests(i915);
> +	mutex_unlock(&i915->drm.struct_mutex);
> +
> +	cancel_delayed_work_sync(&i915->gt.retire_work);
> +	cancel_delayed_work_sync(&i915->gt.idle_work);
> +
> +	mutex_lock(&i915->drm.struct_mutex);
> +	for_each_engine(engine, i915, id)
> +		mock_engine_free(engine);
> +
> +	i915_gem_context_fini(i915);
> +
> +	i915_gem_timeline_fini(&i915->ggtt.base.timeline);
> +	i915_gem_timeline_fini(&i915->gt.global_timeline);
> +	mutex_unlock(&i915->drm.struct_mutex);
> +
> +	kmem_cache_destroy(i915->dependencies);
> +	kmem_cache_destroy(i915->requests);
> +
> +	kfree(i915);
> +}
> +
> +static int igt_add_request(void *ignore)
> +{
> +	struct drm_i915_private *i915;
> +	struct drm_i915_gem_request *request;
> +	int err = -ENOMEM;
> +
> +	i915 = mock_device();
> +	if (!i915)
> +		goto out;
> +
> +	mutex_lock(&i915->drm.struct_mutex);
> +	request = mock_request(i915->engine[RCS],
> +			       i915->kernel_context,
> +			       HZ / 10);
> +	if (!request)
> +		goto out_unlock;
> +
> +	i915_add_request(request);
> +
> +	err = 0;
> +out_unlock:
> +	mutex_unlock(&i915->drm.struct_mutex);
> +	mock_device_free(i915);
> +out:
> +	return err;
> +}
> +
> +int i915_gem_request_selftest(void)
> +{
> +	static const struct i915_subtest tests[] = {
> +		SUBTEST(igt_add_request),
> +	};
> +
> +	return i915_subtests(tests, NULL);
> +}
> +#endif
> diff --git a/drivers/gpu/drm/i915/i915_mock_selftests.h b/drivers/gpu/drm/i915/i915_mock_selftests.h
> index 1603fd35d190..9ff379b18c20 100644
> --- a/drivers/gpu/drm/i915/i915_mock_selftests.h
> +++ b/drivers/gpu/drm/i915/i915_mock_selftests.h
> @@ -8,5 +8,6 @@
>   *
>   * Tests are executed in reverse order by igt/drv_selftest
>   */
> +selftest(requests, i915_gem_request_selftest)
>  selftest(breadcrumbs, intel_breadcrumbs_selftest)
>  selftest(sanitycheck, i915_mock_sanitycheck) /* keep last */
>

It looks tidy enough. I trust you'll run it through the full memory 
debugging arsenal just to double-check it doesn't leak anything. But it 
looks OK to me.

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 12/16] drm/i915: Swap if(enable_execlists) in i915_gem_request_alloc for a vfunc
  2016-12-09 15:25     ` Chris Wilson
@ 2016-12-09 15:53       ` Tvrtko Ursulin
  0 siblings, 0 replies; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-09 15:53 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 09/12/2016 15:25, Chris Wilson wrote:
> On Fri, Dec 09, 2016 at 03:16:33PM +0000, Tvrtko Ursulin wrote:
>>
>> On 07/12/2016 13:58, Chris Wilson wrote:
>>> A fairly trivial move of a matching pair of routines (for preparing a
>>> request for construction) onto an engine vfunc. The ulterior motive is
>>> to be able to create a mock request implementation.
>>>
>>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>>> ---
>>> drivers/gpu/drm/i915/i915_gem_request.c | 5 +----
>>> drivers/gpu/drm/i915/intel_lrc.c        | 4 +++-
>>> drivers/gpu/drm/i915/intel_lrc.h        | 2 --
>>> drivers/gpu/drm/i915/intel_ringbuffer.c | 4 +++-
>>> drivers/gpu/drm/i915/intel_ringbuffer.h | 3 +--
>>> 5 files changed, 8 insertions(+), 10 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
>>> index 06e9a607d934..881bed5347fb 100644
>>> --- a/drivers/gpu/drm/i915/i915_gem_request.c
>>> +++ b/drivers/gpu/drm/i915/i915_gem_request.c
>>> @@ -622,10 +622,7 @@ i915_gem_request_alloc(struct intel_engine_cs *engine,
>>> 	req->reserved_space = MIN_SPACE_FOR_ADD_REQUEST;
>>> 	GEM_BUG_ON(req->reserved_space < engine->emit_breadcrumb_sz);
>>>
>>> -	if (i915.enable_execlists)
>>> -		ret = intel_logical_ring_alloc_request_extras(req);
>>> -	else
>>> -		ret = intel_ring_alloc_request_extras(req);
>>> +	ret = engine->request_alloc(req);
>>> 	if (ret)
>>> 		goto err_ctx;
>>>
>>> diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
>>> index 22ded92d0346..3f536ef37968 100644
>>> --- a/drivers/gpu/drm/i915/intel_lrc.c
>>> +++ b/drivers/gpu/drm/i915/intel_lrc.c
>>> @@ -845,7 +845,7 @@ static void execlists_context_unpin(struct intel_engine_cs *engine,
>>> 	i915_gem_context_put(ctx);
>>> }
>>>
>>> -int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request)
>>> +static int execlists_request_alloc(struct drm_i915_gem_request *request)
>>> {
>>> 	struct intel_engine_cs *engine = request->engine;
>>> 	struct intel_context *ce = &request->ctx->engine[engine->id];
>>> @@ -1816,6 +1816,8 @@ logical_ring_default_vfuncs(struct intel_engine_cs *engine)
>>> 	engine->context_pin = execlists_context_pin;
>>> 	engine->context_unpin = execlists_context_unpin;
>>>
>>> +	engine->request_alloc = execlists_request_alloc;
>>> +
>>> 	engine->emit_flush = gen8_emit_flush;
>>> 	engine->emit_breadcrumb = gen8_emit_breadcrumb;
>>> 	engine->emit_breadcrumb_sz = gen8_emit_breadcrumb_sz;
>>> diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h
>>> index b5630331086a..01ba36ea125e 100644
>>> --- a/drivers/gpu/drm/i915/intel_lrc.h
>>> +++ b/drivers/gpu/drm/i915/intel_lrc.h
>>> @@ -63,8 +63,6 @@ enum {
>>> };
>>>
>>> /* Logical Rings */
>>> -int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request);
>>> -int intel_logical_ring_reserve_space(struct drm_i915_gem_request *request);
>>> void intel_logical_ring_stop(struct intel_engine_cs *engine);
>>> void intel_logical_ring_cleanup(struct intel_engine_cs *engine);
>>> int logical_render_ring_init(struct intel_engine_cs *engine);
>>> diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
>>> index a57eb5dec991..a42b9bf45b42 100644
>>> --- a/drivers/gpu/drm/i915/intel_ringbuffer.c
>>> +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
>>> @@ -2102,7 +2102,7 @@ void intel_legacy_submission_resume(struct drm_i915_private *dev_priv)
>>> 	}
>>> }
>>>
>>> -int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request)
>>> +static int ring_request_alloc(struct drm_i915_gem_request *request)
>>> {
>>> 	int ret;
>>>
>>> @@ -2597,6 +2597,8 @@ static void intel_ring_default_vfuncs(struct drm_i915_private *dev_priv,
>>> 	engine->context_pin = intel_ring_context_pin;
>>> 	engine->context_unpin = intel_ring_context_unpin;
>>>
>>> +	engine->request_alloc = ring_request_alloc;
>>> +
>>> 	engine->emit_breadcrumb = i9xx_emit_breadcrumb;
>>> 	engine->emit_breadcrumb_sz = i9xx_emit_breadcrumb_sz;
>>> 	if (i915.semaphores) {
>>> diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
>>> index 0b69a50ab833..12f4dd1cbf9c 100644
>>> --- a/drivers/gpu/drm/i915/intel_ringbuffer.h
>>> +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
>>> @@ -272,6 +272,7 @@ struct intel_engine_cs {
>>> 				       struct i915_gem_context *ctx);
>>> 	void		(*context_unpin)(struct intel_engine_cs *engine,
>>> 					 struct i915_gem_context *ctx);
>>> +	int		(*request_alloc)(struct drm_i915_gem_request *req);
>>> 	int		(*init_context)(struct drm_i915_gem_request *req);
>>>
>>> 	int		(*emit_flush)(struct drm_i915_gem_request *request,
>>> @@ -476,8 +477,6 @@ void intel_engine_cleanup(struct intel_engine_cs *engine);
>>>
>>> void intel_legacy_submission_resume(struct drm_i915_private *dev_priv);
>>>
>>> -int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request);
>>> -
>>> int __must_check intel_ring_begin(struct drm_i915_gem_request *req, int n);
>>> int __must_check intel_ring_cacheline_align(struct drm_i915_gem_request *req);
>>>
>>>
>>
>> In dev_priv->gt and not per engine would be better. I think we
>> talked about this in some other context as well.
>>
>> Blast, the same then applies to engine->context_(un)pin. :)
>>
> I've toyed with the idea of splitting context_pin for RCS/xCS on legacy
> since only RCS has a context, and before Ironlake we have no contexts at
> all.
>
> ->request_alloc() could specialise itself per engine, in theory at
> least, don't know if we ever would want to do so in practice.
>
>> What do you think?
>
> Quite happy to pencil the idea in for later under sorting out a new split
> for GT vs engine [vs scheduler] ;)

Yeah OK, fine by me.

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 14/16] drm/i915: Add a simple request selftest for waiting
  2016-12-07 13:58 ` [PATCH 14/16] drm/i915: Add a simple request selftest for waiting Chris Wilson
@ 2016-12-09 15:59   ` Tvrtko Ursulin
  0 siblings, 0 replies; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-09 15:59 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> A trivial kselftest to submit a request and wait upon it.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/i915_gem_request.c | 48 +++++++++++++++++++++++++++++++++
>  1 file changed, 48 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
> index 6553457adc77..9ba17d3e35cb 100644
> --- a/drivers/gpu/drm/i915/i915_gem_request.c
> +++ b/drivers/gpu/drm/i915/i915_gem_request.c
> @@ -1601,10 +1601,58 @@ static int igt_add_request(void *ignore)
>  	return err;
>  }
>
> +static int igt_wait_request(void *ignore)
> +{
> +	struct drm_i915_private *i915;
> +	struct drm_i915_gem_request *request;
> +	int err = -ENOMEM;
> +
> +	i915 = mock_device();
> +	if (!i915)
> +		goto out;
> +
> +	mutex_lock(&i915->drm.struct_mutex);
> +	request = mock_request(i915->engine[RCS],
> +			       i915->kernel_context,
> +			       HZ / 2);

Yawn, very slow. :) I suppose you have to put in some safety, but it 
feels too much. Is there any timer jitter by default? We could try 
doubling that and multiply by another two maybe?

> +	if (!request)
> +		goto out_unlock;
> +
> +	i915_add_request(request);
> +
> +	if (i915_gem_request_completed(request)) {
> +		pr_err("request completed immediately!\n");

err = -Esomethingappropriate maybe?

> +		goto out_unlock;
> +	}
> +
> +	if (i915_wait_request(request, I915_WAIT_LOCKED, HZ / 4) != -ETIME) {
> +		pr_err("request wait succeeded (expected tiemout!)\n");

Here also.

> +		goto out_unlock;
> +	}
> +
> +	if (i915_wait_request(request, I915_WAIT_LOCKED, HZ / 2) == -ETIME) {
> +		pr_err("request wait timed out!\n");

And here.

> +		goto out_unlock;
> +	}
> +
> +	if (!i915_gem_request_completed(request)) {
> +		pr_err("request not complete after waiting!\n");

...

> +		goto out_unlock;
> +	}
> +
> +	err = 0;
> +out_unlock:
> +	mutex_unlock(&i915->drm.struct_mutex);
> +	mock_device_free(i915);
> +out:
> +	return err;
> +}
> +
>  int i915_gem_request_selftest(void)
>  {
>  	static const struct i915_subtest tests[] = {
>  		SUBTEST(igt_add_request),
> +		SUBTEST(igt_wait_request),
>  	};
>
>  	return i915_subtests(tests, NULL);
>

No other complaints. :)

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 15/16] drm/i915: Add a simple fence selftest to i915_gem_request
  2016-12-07 13:58 ` [PATCH 15/16] drm/i915: Add a simple fence selftest to i915_gem_request Chris Wilson
@ 2016-12-09 16:02   ` Tvrtko Ursulin
  0 siblings, 0 replies; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-09 16:02 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> Do a quick selftest on in the interoperability of dma_fence_wait on a
> i915_gem_request.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/i915_gem_request.c | 51 +++++++++++++++++++++++++++++++++
>  1 file changed, 51 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
> index 9ba17d3e35cb..2bde3fc8e8bf 100644
> --- a/drivers/gpu/drm/i915/i915_gem_request.c
> +++ b/drivers/gpu/drm/i915/i915_gem_request.c
> @@ -1648,11 +1648,62 @@ static int igt_wait_request(void *ignore)
>  	return err;
>  }
>
> +static int igt_fence_wait(void *ignore)
> +{
> +	struct drm_i915_private *i915;
> +	struct drm_i915_gem_request *request;
> +	int err = -ENOMEM;
> +
> +	i915 = mock_device();
> +	if (!i915)
> +		goto out;
> +
> +	err = -EINVAL;
> +	mutex_lock(&i915->drm.struct_mutex);
> +	request = mock_request(i915->engine[RCS],
> +			       i915->kernel_context,
> +			       HZ);

Even slower than the previous one. :)

> +	if (!request) {
> +		mutex_unlock(&i915->drm.struct_mutex);
> +		goto out_device;
> +	}
> +
> +	i915_add_request(request);
> +	mutex_unlock(&i915->drm.struct_mutex);
> +
> +	if (dma_fence_is_signaled(&request->fence)) {
> +		pr_err("fence signaled immediately!\n");
> +		goto out_device;
> +	}
> +
> +	if (dma_fence_wait_timeout(&request->fence, false, 1) != -ETIME) {
> +		pr_err("fence wait success after submit (expected timeout)!\n");
> +		goto out_device;
> +	}
> +
> +	if (dma_fence_wait_timeout(&request->fence, false, 2 * HZ) <= 0) {
> +		pr_err("fence wait timed out (expected success)!\n");
> +		goto out_device;
> +	}
> +
> +	if (!dma_fence_is_signaled(&request->fence)) {
> +		pr_err("fence unsignaled after waiting!\n");
> +		goto out_device;
> +	}

And the error codes.

> +
> +	err = 0;
> +out_device:
> +	mock_device_free(i915);
> +out:
> +	return err;
> +}
> +
>  int i915_gem_request_selftest(void)
>  {
>  	static const struct i915_subtest tests[] = {
>  		SUBTEST(igt_add_request),
>  		SUBTEST(igt_wait_request),
> +		SUBTEST(igt_fence_wait),
>  	};
>
>  	return i915_subtests(tests, NULL);
>

See if you can speed it up, but that is optional.

Just some better failure codes from the test and then:

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko


_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 16/16] drm/i915: Add selftests for object allocation, phys
  2016-12-07 13:58 ` [PATCH 16/16] drm/i915: Add selftests for object allocation, phys Chris Wilson
@ 2016-12-13 17:10   ` Tvrtko Ursulin
  2016-12-17 13:55     ` Chris Wilson
  0 siblings, 1 reply; 62+ messages in thread
From: Tvrtko Ursulin @ 2016-12-13 17:10 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 07/12/2016 13:58, Chris Wilson wrote:
> The phys object is a rarely used device (only very old machines require
> a chunk of physically contiguous pages for a few hardware interactions).
> As such, it is not exercised by CI and to combat that we want to add a
> test that exercises the phys object on all platforms.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/i915_gem.c            | 168 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/i915/i915_mock_selftests.h |   1 +
>  2 files changed, 169 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 9794dd655877..1ab5fdae2d47 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -4949,3 +4949,171 @@ i915_gem_object_get_dma_address(struct drm_i915_gem_object *obj,
>  	sg = i915_gem_object_get_sg(obj, n, &offset);
>  	return sg_dma_address(sg) + (offset << PAGE_SHIFT);
>  }
> +
> +#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
> +#include <linux/pm_runtime.h>
> +
> +#include "i915_selftest.h"
> +
> +static struct drm_driver mock_driver = {
> +	.name = "mock",
> +	.driver_features = DRIVER_GEM,
> +
> +	.gem_close_object = i915_gem_close_object,
> +	.gem_free_object_unlocked = i915_gem_free_object,
> +};
> +
> +struct mock_object {
> +	struct drm_i915_gem_object base;
> +};
> +
> +static void release_dev(struct device *dev)
> +{
> +	struct pci_dev *pdev = to_pci_dev(dev);
> +
> +	kfree(pdev);
> +}
> +
> +static struct drm_i915_private *mock_gem_device(void)
> +{
> +	struct drm_i915_private *i915;
> +	struct pci_dev *pdev;
> +	int err;
> +
> +	i915 = kzalloc(sizeof(*i915), GFP_TEMPORARY);
> +	if (!i915)
> +		return NULL;
> +
> +	pdev = kzalloc(sizeof(*pdev), GFP_TEMPORARY);
> +	if (!pdev) {
> +		kfree(i915);
> +		return NULL;
> +	}
> +
> +	device_initialize(&pdev->dev);
> +	pdev->dev.release = release_dev;
> +	dev_set_name(&pdev->dev, "mock");
> +	dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
> +
> +	pm_runtime_dont_use_autosuspend(&pdev->dev);
> +	pm_runtime_get_sync(&pdev->dev);
> +	pci_set_drvdata(pdev, i915);
> +
> +	err = drm_dev_init(&i915->drm, &mock_driver, &pdev->dev);
> +	if (err) {
> +		pr_err("Failed to initialise mock GEM device: err=%d\n", err);
> +		put_device(&pdev->dev);
> +		kfree(i915);
> +		return NULL;
> +	}
> +	i915->drm.pdev = pdev;
> +	i915->drm.dev_private = i915;
> +
> +	mkwrite_device_info(i915)->gen = -1;
> +
> +	spin_lock_init(&i915->mm.object_stat_lock);
> +
> +	INIT_WORK(&i915->mm.free_work, __i915_gem_free_work);
> +	init_llist_head(&i915->mm.free_list);
> +
> +	i915->objects = KMEM_CACHE(mock_object, SLAB_HWCACHE_ALIGN);
> +	if (!i915->objects)
> +		goto err_device;
> +
> +	return i915;
> +
> +err_device:
> +	kfree(i915);
> +	return NULL;
> +}
> +
> +static void mock_device_free(struct drm_i915_private *i915)
> +{
> +	struct pci_dev *pdev = i915->drm.pdev;
> +
> +	rcu_barrier();
> +	while (flush_work(&i915->mm.free_work))
> +		rcu_barrier();
> +
> +	drm_dev_unref(&i915->drm);
> +	put_device(&pdev->dev);
> +}
> +
> +static int igt_gem_object(void *ignore)
> +{
> +	struct drm_i915_gem_object *obj;
> +	struct drm_i915_private *i915;
> +	int err = -ENOMEM;
> +
> +	i915 = mock_gem_device();
> +	if (!i915)
> +		goto out;
> +
> +	obj = i915_gem_object_create(i915, 4096);
> +	if (IS_ERR(obj)) {
> +		err = PTR_ERR(obj);
> +		pr_err("i915_gem_object_create failed, err=%d\n", err);
> +		goto out_device;
> +	}
> +
> +	err = 0;
> +	i915_gem_object_put(obj);
> +out_device:
> +	mock_device_free(i915);
> +out:
> +	return err;
> +}
> +
> +static int igt_phys_object(void *ignore)
> +{
> +	struct drm_i915_gem_object *obj;
> +	struct drm_i915_private *i915;
> +	int err = -ENOMEM;
> +
> +	i915 = mock_gem_device();
> +	if (!i915)
> +		goto out;
> +
> +	obj = i915_gem_object_create(i915, 4096);
> +	if (IS_ERR(obj)) {
> +		err = PTR_ERR(obj);
> +		pr_err("i915_gem_object_create failed, err=%d\n", err);
> +		goto out_device;
> +	}
> +
> +	err = -EINVAL;
> +	mutex_lock(&i915->drm.struct_mutex);
> +	err = i915_gem_object_attach_phys(obj, PAGE_SIZE);
> +	mutex_unlock(&i915->drm.struct_mutex);
> +	if (err) {
> +		pr_err("i915_gem_object_attach_phys failed, err=%d\n", err);
> +		goto err;
> +	}
> +
> +	if (obj->ops != &i915_gem_phys_ops) {
> +		pr_err("i915_gem_object_attach_phys did not create a phys object\n");
> +		goto err;
> +	}
> +
> +	/* Make the object work during teardown */

Not the clearest what does "work" mean? I see that the exercises the 
copy to shmem path, just saying comment could be better.

> +	obj->mm.dirty = true;
> +
> +	err = 0;
> +err:
> +	i915_gem_object_put(obj);
> +out_device:
> +	mock_device_free(i915);
> +out:
> +	return err;
> +}
> +
> +int i915_gem_object_selftests(void)
> +{
> +	static const struct i915_subtest tests[] = {
> +		SUBTEST(igt_gem_object),
> +		SUBTEST(igt_phys_object),
> +	};
> +
> +	return i915_subtests(tests, NULL);
> +}
> +#endif

Would it be worth shunting these "if 
IS_ENABLED(CONFIG_DRM_I915_SELFTEST)" blocks into separate files and 
include them from the parent?

Like in this case:

#include "i915_gem_selftests.c"

Or even:

#include "selftests/i915_gem.c"

If the include is at the end of the file it should always work. It will 
be more manageable as the tests grow I think.

> diff --git a/drivers/gpu/drm/i915/i915_mock_selftests.h b/drivers/gpu/drm/i915/i915_mock_selftests.h
> index 9ff379b18c20..34f32f777b34 100644
> --- a/drivers/gpu/drm/i915/i915_mock_selftests.h
> +++ b/drivers/gpu/drm/i915/i915_mock_selftests.h
> @@ -8,6 +8,7 @@
>   *
>   * Tests are executed in reverse order by igt/drv_selftest
>   */
> +selftest(objects, i915_gem_object_selftests)
>  selftest(requests, i915_gem_request_selftest)
>  selftest(breadcrumbs, intel_breadcrumbs_selftest)
>  selftest(sanitycheck, i915_mock_sanitycheck) /* keep last */
>

Looks clean enough and although I am not 100% familiar with some of the 
core APIs used - if it works it works.

Strong suggestion to consider the move to a separate file, but in essence:

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 16/16] drm/i915: Add selftests for object allocation, phys
  2016-12-13 17:10   ` Tvrtko Ursulin
@ 2016-12-17 13:55     ` Chris Wilson
  0 siblings, 0 replies; 62+ messages in thread
From: Chris Wilson @ 2016-12-17 13:55 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: intel-gfx

On Tue, Dec 13, 2016 at 05:10:46PM +0000, Tvrtko Ursulin wrote:
> >+int i915_gem_object_selftests(void)
> >+{
> >+	static const struct i915_subtest tests[] = {
> >+		SUBTEST(igt_gem_object),
> >+		SUBTEST(igt_phys_object),
> >+	};
> >+
> >+	return i915_subtests(tests, NULL);
> >+}
> >+#endif
> 
> Would it be worth shunting these "if
> IS_ENABLED(CONFIG_DRM_I915_SELFTEST)" blocks into separate files and
> include them from the parent?
> 
> Like in this case:
> 
> #include "i915_gem_selftests.c"
> 
> Or even:
> 
> #include "selftests/i915_gem.c"
> 
> If the include is at the end of the file it should always work. It
> will be more manageable as the tests grow I think.

I like the idea of moving the test code under selftests/. It does make
it more difficult, but not terribly so, to read both chunks (oh, the
pain of having to have two panes!). I still like having the include
under the ifdef guard to make it clear to the reader of the *primary*
code that what follows is auxiliary. You can judge for yourself
shortly...
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 62+ messages in thread

end of thread, other threads:[~2016-12-17 13:55 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-12-07 13:58 [RFC] Smattering of selftests Chris Wilson
2016-12-07 13:58 ` [PATCH 01/16] drm/i915: Provide a hook for selftests Chris Wilson
2016-12-08 10:47   ` Tvrtko Ursulin
2016-12-08 11:15     ` Chris Wilson
2016-12-08 12:30       ` Tvrtko Ursulin
2016-12-08 12:40         ` Chris Wilson
2016-12-07 13:58 ` [PATCH 02/16] kselftests: Exercise hw-independent mock tests for i915.ko Chris Wilson
2016-12-07 14:09   ` Chris Wilson
2016-12-08 15:50     ` Shuah Khan
2016-12-08 16:01       ` Chris Wilson
2016-12-08 16:44         ` Shuah Khan
2016-12-07 13:58 ` [PATCH 03/16] drm/i915: Add unit tests for the breadcrumb rbtree, insert/remove Chris Wilson
2016-12-08 11:00   ` Tvrtko Ursulin
2016-12-07 13:58 ` [PATCH 04/16] drm/i915: Add unit tests for the breadcrumb rbtree, completion Chris Wilson
2016-12-08 11:47   ` Tvrtko Ursulin
2016-12-07 13:58 ` [PATCH 05/16] drm/i915: Add unit tests for the breadcrumb rbtree, wakeups Chris Wilson
2016-12-08 17:38   ` Tvrtko Ursulin
2016-12-08 21:04     ` Chris Wilson
2016-12-09  9:33       ` Tvrtko Ursulin
2016-12-07 13:58 ` [PATCH 06/16] drm/i915: Add a reminder that i915_vma_move_to_active() requires struct_mutex Chris Wilson
2016-12-08 17:40   ` Tvrtko Ursulin
2016-12-07 13:58 ` [PATCH 07/16] drm/i915: Move intel_lrc_context_pin() to avoid the forward declaration Chris Wilson
2016-12-08 17:45   ` Tvrtko Ursulin
2016-12-08 20:55     ` Chris Wilson
2016-12-07 13:58 ` [PATCH 08/16] drm/i915: Unify active context tracking between legacy/execlists/guc Chris Wilson
2016-12-09 11:48   ` Tvrtko Ursulin
2016-12-09 12:17     ` Chris Wilson
2016-12-07 13:58 ` [PATCH 09/16] drm/i915: Simplify releasing context reference Chris Wilson
2016-12-09 15:03   ` Tvrtko Ursulin
2016-12-07 13:58 ` [PATCH 10/16] drm/i915: Mark the shadow gvt context as closed Chris Wilson
2016-12-09 15:07   ` Tvrtko Ursulin
2016-12-07 13:58 ` [PATCH 11/16] drm/i915/execlists: Request the kernel context be pinned high Chris Wilson
2016-12-09 15:08   ` Tvrtko Ursulin
2016-12-07 13:58 ` [PATCH 12/16] drm/i915: Swap if(enable_execlists) in i915_gem_request_alloc for a vfunc Chris Wilson
2016-12-09 15:16   ` Tvrtko Ursulin
2016-12-09 15:25     ` Chris Wilson
2016-12-09 15:53       ` Tvrtko Ursulin
2016-12-07 13:58 ` [PATCH 13/16] drm/i915: Add selftests for i915_gem_request Chris Wilson
2016-12-09 15:51   ` Tvrtko Ursulin
2016-12-07 13:58 ` [PATCH 14/16] drm/i915: Add a simple request selftest for waiting Chris Wilson
2016-12-09 15:59   ` Tvrtko Ursulin
2016-12-07 13:58 ` [PATCH 15/16] drm/i915: Add a simple fence selftest to i915_gem_request Chris Wilson
2016-12-09 16:02   ` Tvrtko Ursulin
2016-12-07 13:58 ` [PATCH 16/16] drm/i915: Add selftests for object allocation, phys Chris Wilson
2016-12-13 17:10   ` Tvrtko Ursulin
2016-12-17 13:55     ` Chris Wilson
2016-12-07 14:04 ` [RFC] Smattering of selftests Chris Wilson
2016-12-07 15:45 ` ✓ Fi.CI.BAT: success for series starting with [01/16] drm/i915: Provide a hook for selftests Patchwork
2016-12-07 18:52 ` [PATCH 1/2] drm/i915: Move uncore selfchecks to late selftest infrastructure Chris Wilson
2016-12-07 18:52   ` [PATCH 2/2] drm/i915: Test all fw tables during mock selftests Chris Wilson
2016-12-08 12:14     ` Tvrtko Ursulin
2016-12-08 12:28       ` Chris Wilson
2016-12-08 13:11     ` Joonas Lahtinen
2016-12-08 16:52     ` Tvrtko Ursulin
2016-12-08 22:29       ` Chris Wilson
2016-12-09  9:41         ` Tvrtko Ursulin
2016-12-09 10:18           ` Chris Wilson
2016-12-09 10:35             ` Tvrtko Ursulin
2016-12-09 10:51               ` Chris Wilson
2016-12-08 11:58   ` [PATCH 1/2] drm/i915: Move uncore selfchecks to late selftest infrastructure Tvrtko Ursulin
2016-12-07 19:56 ` [RFC] Smattering of selftests Paulo Zanoni
2016-12-07 20:52 ` ✓ Fi.CI.BAT: success for series starting with [01/16] drm/i915: Provide a hook for selftests (rev2) Patchwork

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox