All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATH i-g-t 0/2] Per context dynamic (sub)slice power-gating
@ 2018-09-05 14:25 ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-05 14:25 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Some tests for the corresponding i915 series.

I dropped the benchmark for now but plan to bring it back later.

Lionel Landwerlin (2):
  headers: bump
  tests: add slice power programming test

 include/drm-uapi/amdgpu_drm.h  |   23 +
 include/drm-uapi/drm.h         |    7 +
 include/drm-uapi/drm_mode.h    |   22 +-
 include/drm-uapi/etnaviv_drm.h |    6 +
 include/drm-uapi/exynos_drm.h  |  240 ++++++++
 include/drm-uapi/i915_drm.h    |   43 ++
 include/drm-uapi/msm_drm.h     |    2 +
 include/drm-uapi/tegra_drm.h   |  492 ++++++++++++++-
 include/drm-uapi/vc4_drm.h     |   13 +-
 include/drm-uapi/virtgpu_drm.h |    1 +
 tests/Makefile.am              |    1 +
 tests/Makefile.sources         |    1 +
 tests/gem_ctx_param.c          |    4 +-
 tests/gem_ctx_sseu.c           | 1040 ++++++++++++++++++++++++++++++++
 tests/meson.build              |    7 +
 15 files changed, 1885 insertions(+), 17 deletions(-)
 create mode 100644 tests/gem_ctx_sseu.c

-- 
2.17.1

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

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

* [igt-dev] [PATH i-g-t 0/2] Per context dynamic (sub)slice power-gating
@ 2018-09-05 14:25 ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-05 14:25 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Some tests for the corresponding i915 series.

I dropped the benchmark for now but plan to bring it back later.

Lionel Landwerlin (2):
  headers: bump
  tests: add slice power programming test

 include/drm-uapi/amdgpu_drm.h  |   23 +
 include/drm-uapi/drm.h         |    7 +
 include/drm-uapi/drm_mode.h    |   22 +-
 include/drm-uapi/etnaviv_drm.h |    6 +
 include/drm-uapi/exynos_drm.h  |  240 ++++++++
 include/drm-uapi/i915_drm.h    |   43 ++
 include/drm-uapi/msm_drm.h     |    2 +
 include/drm-uapi/tegra_drm.h   |  492 ++++++++++++++-
 include/drm-uapi/vc4_drm.h     |   13 +-
 include/drm-uapi/virtgpu_drm.h |    1 +
 tests/Makefile.am              |    1 +
 tests/Makefile.sources         |    1 +
 tests/gem_ctx_param.c          |    4 +-
 tests/gem_ctx_sseu.c           | 1040 ++++++++++++++++++++++++++++++++
 tests/meson.build              |    7 +
 15 files changed, 1885 insertions(+), 17 deletions(-)
 create mode 100644 tests/gem_ctx_sseu.c

-- 
2.17.1

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [PATH i-g-t 1/2] headers: bump
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
@ 2018-09-05 14:25   ` Tvrtko Ursulin
  -1 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-05 14:25 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

---
 include/drm-uapi/amdgpu_drm.h  |  23 ++
 include/drm-uapi/drm.h         |   7 +
 include/drm-uapi/drm_mode.h    |  22 +-
 include/drm-uapi/etnaviv_drm.h |   6 +
 include/drm-uapi/exynos_drm.h  | 240 ++++++++++++++++
 include/drm-uapi/i915_drm.h    |  43 +++
 include/drm-uapi/msm_drm.h     |   2 +
 include/drm-uapi/tegra_drm.h   | 492 ++++++++++++++++++++++++++++++++-
 include/drm-uapi/vc4_drm.h     |  13 +-
 include/drm-uapi/virtgpu_drm.h |   1 +
 10 files changed, 833 insertions(+), 16 deletions(-)

diff --git a/include/drm-uapi/amdgpu_drm.h b/include/drm-uapi/amdgpu_drm.h
index 1816bd8200d1..78b4dd89fcb4 100644
--- a/include/drm-uapi/amdgpu_drm.h
+++ b/include/drm-uapi/amdgpu_drm.h
@@ -78,6 +78,12 @@ extern "C" {
 #define AMDGPU_GEM_DOMAIN_GDS		0x8
 #define AMDGPU_GEM_DOMAIN_GWS		0x10
 #define AMDGPU_GEM_DOMAIN_OA		0x20
+#define AMDGPU_GEM_DOMAIN_MASK		(AMDGPU_GEM_DOMAIN_CPU | \
+					 AMDGPU_GEM_DOMAIN_GTT | \
+					 AMDGPU_GEM_DOMAIN_VRAM | \
+					 AMDGPU_GEM_DOMAIN_GDS | \
+					 AMDGPU_GEM_DOMAIN_GWS | \
+					 AMDGPU_GEM_DOMAIN_OA)
 
 /* Flag that CPU access will be required for the case of VRAM domain */
 #define AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED	(1 << 0)
@@ -95,6 +101,10 @@ extern "C" {
 #define AMDGPU_GEM_CREATE_VM_ALWAYS_VALID	(1 << 6)
 /* Flag that BO sharing will be explicitly synchronized */
 #define AMDGPU_GEM_CREATE_EXPLICIT_SYNC		(1 << 7)
+/* Flag that indicates allocating MQD gart on GFX9, where the mtype
+ * for the second page onward should be set to NC.
+ */
+#define AMDGPU_GEM_CREATE_MQD_GFX9		(1 << 8)
 
 struct drm_amdgpu_gem_create_in  {
 	/** the requested memory size */
@@ -520,6 +530,10 @@ union drm_amdgpu_cs {
 /* Preempt flag, IB should set Pre_enb bit if PREEMPT flag detected */
 #define AMDGPU_IB_FLAG_PREEMPT (1<<2)
 
+/* The IB fence should do the L2 writeback but not invalidate any shader
+ * caches (L2/vL1/sL1/I$). */
+#define AMDGPU_IB_FLAG_TC_WB_NOT_INVALIDATE (1 << 3)
+
 struct drm_amdgpu_cs_chunk_ib {
 	__u32 _pad;
 	/** AMDGPU_IB_FLAG_* */
@@ -618,6 +632,14 @@ struct drm_amdgpu_cs_chunk_data {
 	#define AMDGPU_INFO_FW_SOS		0x0c
 	/* Subquery id: Query PSP ASD firmware version */
 	#define AMDGPU_INFO_FW_ASD		0x0d
+	/* Subquery id: Query VCN firmware version */
+	#define AMDGPU_INFO_FW_VCN		0x0e
+	/* Subquery id: Query GFX RLC SRLC firmware version */
+	#define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_CNTL 0x0f
+	/* Subquery id: Query GFX RLC SRLG firmware version */
+	#define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_GPM_MEM 0x10
+	/* Subquery id: Query GFX RLC SRLS firmware version */
+	#define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_SRM_MEM 0x11
 /* number of bytes moved for TTM migration */
 #define AMDGPU_INFO_NUM_BYTES_MOVED		0x0f
 /* the used VRAM size */
@@ -806,6 +828,7 @@ struct drm_amdgpu_info_firmware {
 #define AMDGPU_VRAM_TYPE_GDDR5 5
 #define AMDGPU_VRAM_TYPE_HBM   6
 #define AMDGPU_VRAM_TYPE_DDR3  7
+#define AMDGPU_VRAM_TYPE_DDR4  8
 
 struct drm_amdgpu_info_device {
 	/** PCI Device ID */
diff --git a/include/drm-uapi/drm.h b/include/drm-uapi/drm.h
index f0bd91de0cf9..778a97fcfe63 100644
--- a/include/drm-uapi/drm.h
+++ b/include/drm-uapi/drm.h
@@ -674,6 +674,13 @@ struct drm_get_cap {
  */
 #define DRM_CLIENT_CAP_ATOMIC	3
 
+/**
+ * DRM_CLIENT_CAP_ASPECT_RATIO
+ *
+ * If set to 1, the DRM core will provide aspect ratio information in modes.
+ */
+#define DRM_CLIENT_CAP_ASPECT_RATIO    4
+
 /** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */
 struct drm_set_client_cap {
 	__u64 capability;
diff --git a/include/drm-uapi/drm_mode.h b/include/drm-uapi/drm_mode.h
index 2c575794fb52..971c016b368c 100644
--- a/include/drm-uapi/drm_mode.h
+++ b/include/drm-uapi/drm_mode.h
@@ -93,6 +93,15 @@ extern "C" {
 #define DRM_MODE_PICTURE_ASPECT_NONE		0
 #define DRM_MODE_PICTURE_ASPECT_4_3		1
 #define DRM_MODE_PICTURE_ASPECT_16_9		2
+#define DRM_MODE_PICTURE_ASPECT_64_27		3
+#define DRM_MODE_PICTURE_ASPECT_256_135		4
+
+/* Content type options */
+#define DRM_MODE_CONTENT_TYPE_NO_DATA		0
+#define DRM_MODE_CONTENT_TYPE_GRAPHICS		1
+#define DRM_MODE_CONTENT_TYPE_PHOTO		2
+#define DRM_MODE_CONTENT_TYPE_CINEMA		3
+#define DRM_MODE_CONTENT_TYPE_GAME		4
 
 /* Aspect ratio flag bitmask (4 bits 22:19) */
 #define DRM_MODE_FLAG_PIC_AR_MASK		(0x0F<<19)
@@ -102,6 +111,10 @@ extern "C" {
 			(DRM_MODE_PICTURE_ASPECT_4_3<<19)
 #define  DRM_MODE_FLAG_PIC_AR_16_9 \
 			(DRM_MODE_PICTURE_ASPECT_16_9<<19)
+#define  DRM_MODE_FLAG_PIC_AR_64_27 \
+			(DRM_MODE_PICTURE_ASPECT_64_27<<19)
+#define  DRM_MODE_FLAG_PIC_AR_256_135 \
+			(DRM_MODE_PICTURE_ASPECT_256_135<<19)
 
 #define  DRM_MODE_FLAG_ALL	(DRM_MODE_FLAG_PHSYNC |		\
 				 DRM_MODE_FLAG_NHSYNC |		\
@@ -363,7 +376,7 @@ struct drm_mode_get_connector {
 	__u32 pad;
 };
 
-#define DRM_MODE_PROP_PENDING	(1<<0)
+#define DRM_MODE_PROP_PENDING	(1<<0) /* deprecated, do not use */
 #define DRM_MODE_PROP_RANGE	(1<<1)
 #define DRM_MODE_PROP_IMMUTABLE	(1<<2)
 #define DRM_MODE_PROP_ENUM	(1<<3) /* enumerated type with text strings */
@@ -598,8 +611,11 @@ struct drm_mode_crtc_lut {
 };
 
 struct drm_color_ctm {
-	/* Conversion matrix in S31.32 format. */
-	__s64 matrix[9];
+	/*
+	 * Conversion matrix in S31.32 sign-magnitude
+	 * (not two's complement!) format.
+	 */
+	__u64 matrix[9];
 };
 
 struct drm_color_lut {
diff --git a/include/drm-uapi/etnaviv_drm.h b/include/drm-uapi/etnaviv_drm.h
index e9b997a0ef27..0d5c49dc478c 100644
--- a/include/drm-uapi/etnaviv_drm.h
+++ b/include/drm-uapi/etnaviv_drm.h
@@ -55,6 +55,12 @@ struct drm_etnaviv_timespec {
 #define ETNAVIV_PARAM_GPU_FEATURES_4                0x07
 #define ETNAVIV_PARAM_GPU_FEATURES_5                0x08
 #define ETNAVIV_PARAM_GPU_FEATURES_6                0x09
+#define ETNAVIV_PARAM_GPU_FEATURES_7                0x0a
+#define ETNAVIV_PARAM_GPU_FEATURES_8                0x0b
+#define ETNAVIV_PARAM_GPU_FEATURES_9                0x0c
+#define ETNAVIV_PARAM_GPU_FEATURES_10               0x0d
+#define ETNAVIV_PARAM_GPU_FEATURES_11               0x0e
+#define ETNAVIV_PARAM_GPU_FEATURES_12               0x0f
 
 #define ETNAVIV_PARAM_GPU_STREAM_COUNT              0x10
 #define ETNAVIV_PARAM_GPU_REGISTER_MAX              0x11
diff --git a/include/drm-uapi/exynos_drm.h b/include/drm-uapi/exynos_drm.h
index a00116b5cc5c..7414cfd76419 100644
--- a/include/drm-uapi/exynos_drm.h
+++ b/include/drm-uapi/exynos_drm.h
@@ -135,6 +135,219 @@ struct drm_exynos_g2d_exec {
 	__u64					async;
 };
 
+/* Exynos DRM IPP v2 API */
+
+/**
+ * Enumerate available IPP hardware modules.
+ *
+ * @count_ipps: size of ipp_id array / number of ipp modules (set by driver)
+ * @reserved: padding
+ * @ipp_id_ptr: pointer to ipp_id array or NULL
+ */
+struct drm_exynos_ioctl_ipp_get_res {
+	__u32 count_ipps;
+	__u32 reserved;
+	__u64 ipp_id_ptr;
+};
+
+enum drm_exynos_ipp_format_type {
+	DRM_EXYNOS_IPP_FORMAT_SOURCE		= 0x01,
+	DRM_EXYNOS_IPP_FORMAT_DESTINATION	= 0x02,
+};
+
+struct drm_exynos_ipp_format {
+	__u32 fourcc;
+	__u32 type;
+	__u64 modifier;
+};
+
+enum drm_exynos_ipp_capability {
+	DRM_EXYNOS_IPP_CAP_CROP		= 0x01,
+	DRM_EXYNOS_IPP_CAP_ROTATE	= 0x02,
+	DRM_EXYNOS_IPP_CAP_SCALE	= 0x04,
+	DRM_EXYNOS_IPP_CAP_CONVERT	= 0x08,
+};
+
+/**
+ * Get IPP hardware capabilities and supported image formats.
+ *
+ * @ipp_id: id of IPP module to query
+ * @capabilities: bitmask of drm_exynos_ipp_capability (set by driver)
+ * @reserved: padding
+ * @formats_count: size of formats array (in entries) / number of filled
+ *		   formats (set by driver)
+ * @formats_ptr: pointer to formats array or NULL
+ */
+struct drm_exynos_ioctl_ipp_get_caps {
+	__u32 ipp_id;
+	__u32 capabilities;
+	__u32 reserved;
+	__u32 formats_count;
+	__u64 formats_ptr;
+};
+
+enum drm_exynos_ipp_limit_type {
+	/* size (horizontal/vertial) limits, in pixels (min, max, alignment) */
+	DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE		= 0x0001,
+	/* scale ratio (horizonta/vertial), 16.16 fixed point (min, max) */
+	DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE		= 0x0002,
+
+	/* image buffer area */
+	DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER	= 0x0001 << 16,
+	/* src/dst rectangle area */
+	DRM_EXYNOS_IPP_LIMIT_SIZE_AREA		= 0x0002 << 16,
+	/* src/dst rectangle area when rotation enabled */
+	DRM_EXYNOS_IPP_LIMIT_SIZE_ROTATED	= 0x0003 << 16,
+
+	DRM_EXYNOS_IPP_LIMIT_TYPE_MASK		= 0x000f,
+	DRM_EXYNOS_IPP_LIMIT_SIZE_MASK		= 0x000f << 16,
+};
+
+struct drm_exynos_ipp_limit_val {
+	__u32 min;
+	__u32 max;
+	__u32 align;
+	__u32 reserved;
+};
+
+/**
+ * IPP module limitation.
+ *
+ * @type: limit type (see drm_exynos_ipp_limit_type enum)
+ * @reserved: padding
+ * @h: horizontal limits
+ * @v: vertical limits
+ */
+struct drm_exynos_ipp_limit {
+	__u32 type;
+	__u32 reserved;
+	struct drm_exynos_ipp_limit_val h;
+	struct drm_exynos_ipp_limit_val v;
+};
+
+/**
+ * Get IPP limits for given image format.
+ *
+ * @ipp_id: id of IPP module to query
+ * @fourcc: image format code (see DRM_FORMAT_* in drm_fourcc.h)
+ * @modifier: image format modifier (see DRM_FORMAT_MOD_* in drm_fourcc.h)
+ * @type: source/destination identifier (drm_exynos_ipp_format_flag enum)
+ * @limits_count: size of limits array (in entries) / number of filled entries
+ *		 (set by driver)
+ * @limits_ptr: pointer to limits array or NULL
+ */
+struct drm_exynos_ioctl_ipp_get_limits {
+	__u32 ipp_id;
+	__u32 fourcc;
+	__u64 modifier;
+	__u32 type;
+	__u32 limits_count;
+	__u64 limits_ptr;
+};
+
+enum drm_exynos_ipp_task_id {
+	/* buffer described by struct drm_exynos_ipp_task_buffer */
+	DRM_EXYNOS_IPP_TASK_BUFFER		= 0x0001,
+	/* rectangle described by struct drm_exynos_ipp_task_rect */
+	DRM_EXYNOS_IPP_TASK_RECTANGLE		= 0x0002,
+	/* transformation described by struct drm_exynos_ipp_task_transform */
+	DRM_EXYNOS_IPP_TASK_TRANSFORM		= 0x0003,
+	/* alpha configuration described by struct drm_exynos_ipp_task_alpha */
+	DRM_EXYNOS_IPP_TASK_ALPHA		= 0x0004,
+
+	/* source image data (for buffer and rectangle chunks) */
+	DRM_EXYNOS_IPP_TASK_TYPE_SOURCE		= 0x0001 << 16,
+	/* destination image data (for buffer and rectangle chunks) */
+	DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION	= 0x0002 << 16,
+};
+
+/**
+ * Memory buffer with image data.
+ *
+ * @id: must be DRM_EXYNOS_IPP_TASK_BUFFER
+ * other parameters are same as for AddFB2 generic DRM ioctl
+ */
+struct drm_exynos_ipp_task_buffer {
+	__u32	id;
+	__u32	fourcc;
+	__u32	width, height;
+	__u32	gem_id[4];
+	__u32	offset[4];
+	__u32	pitch[4];
+	__u64	modifier;
+};
+
+/**
+ * Rectangle for processing.
+ *
+ * @id: must be DRM_EXYNOS_IPP_TASK_RECTANGLE
+ * @reserved: padding
+ * @x,@y: left corner in pixels
+ * @w,@h: width/height in pixels
+ */
+struct drm_exynos_ipp_task_rect {
+	__u32	id;
+	__u32	reserved;
+	__u32	x;
+	__u32	y;
+	__u32	w;
+	__u32	h;
+};
+
+/**
+ * Image tranformation description.
+ *
+ * @id: must be DRM_EXYNOS_IPP_TASK_TRANSFORM
+ * @rotation: DRM_MODE_ROTATE_* and DRM_MODE_REFLECT_* values
+ */
+struct drm_exynos_ipp_task_transform {
+	__u32	id;
+	__u32	rotation;
+};
+
+/**
+ * Image global alpha configuration for formats without alpha values.
+ *
+ * @id: must be DRM_EXYNOS_IPP_TASK_ALPHA
+ * @value: global alpha value (0-255)
+ */
+struct drm_exynos_ipp_task_alpha {
+	__u32	id;
+	__u32	value;
+};
+
+enum drm_exynos_ipp_flag {
+	/* generate DRM event after processing */
+	DRM_EXYNOS_IPP_FLAG_EVENT	= 0x01,
+	/* dry run, only check task parameters */
+	DRM_EXYNOS_IPP_FLAG_TEST_ONLY	= 0x02,
+	/* non-blocking processing */
+	DRM_EXYNOS_IPP_FLAG_NONBLOCK	= 0x04,
+};
+
+#define DRM_EXYNOS_IPP_FLAGS (DRM_EXYNOS_IPP_FLAG_EVENT |\
+		DRM_EXYNOS_IPP_FLAG_TEST_ONLY | DRM_EXYNOS_IPP_FLAG_NONBLOCK)
+
+/**
+ * Perform image processing described by array of drm_exynos_ipp_task_*
+ * structures (parameters array).
+ *
+ * @ipp_id: id of IPP module to run the task
+ * @flags: bitmask of drm_exynos_ipp_flag values
+ * @reserved: padding
+ * @params_size: size of parameters array (in bytes)
+ * @params_ptr: pointer to parameters array or NULL
+ * @user_data: (optional) data for drm event
+ */
+struct drm_exynos_ioctl_ipp_commit {
+	__u32 ipp_id;
+	__u32 flags;
+	__u32 reserved;
+	__u32 params_size;
+	__u64 params_ptr;
+	__u64 user_data;
+};
+
 #define DRM_EXYNOS_GEM_CREATE		0x00
 #define DRM_EXYNOS_GEM_MAP		0x01
 /* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */
@@ -147,6 +360,11 @@ struct drm_exynos_g2d_exec {
 #define DRM_EXYNOS_G2D_EXEC		0x22
 
 /* Reserved 0x30 ~ 0x33 for obsolete Exynos IPP ioctls */
+/* IPP - Image Post Processing */
+#define DRM_EXYNOS_IPP_GET_RESOURCES	0x40
+#define DRM_EXYNOS_IPP_GET_CAPS		0x41
+#define DRM_EXYNOS_IPP_GET_LIMITS	0x42
+#define DRM_EXYNOS_IPP_COMMIT		0x43
 
 #define DRM_IOCTL_EXYNOS_GEM_CREATE		DRM_IOWR(DRM_COMMAND_BASE + \
 		DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
@@ -165,8 +383,20 @@ struct drm_exynos_g2d_exec {
 #define DRM_IOCTL_EXYNOS_G2D_EXEC		DRM_IOWR(DRM_COMMAND_BASE + \
 		DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec)
 
+#define DRM_IOCTL_EXYNOS_IPP_GET_RESOURCES	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_IPP_GET_RESOURCES, \
+		struct drm_exynos_ioctl_ipp_get_res)
+#define DRM_IOCTL_EXYNOS_IPP_GET_CAPS		DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_IPP_GET_CAPS, struct drm_exynos_ioctl_ipp_get_caps)
+#define DRM_IOCTL_EXYNOS_IPP_GET_LIMITS		DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_IPP_GET_LIMITS, \
+		struct drm_exynos_ioctl_ipp_get_limits)
+#define DRM_IOCTL_EXYNOS_IPP_COMMIT		DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_IPP_COMMIT, struct drm_exynos_ioctl_ipp_commit)
+
 /* EXYNOS specific events */
 #define DRM_EXYNOS_G2D_EVENT		0x80000000
+#define DRM_EXYNOS_IPP_EVENT		0x80000002
 
 struct drm_exynos_g2d_event {
 	struct drm_event	base;
@@ -177,6 +407,16 @@ struct drm_exynos_g2d_event {
 	__u32			reserved;
 };
 
+struct drm_exynos_ipp_event {
+	struct drm_event	base;
+	__u64			user_data;
+	__u32			tv_sec;
+	__u32			tv_usec;
+	__u32			ipp_id;
+	__u32			sequence;
+	__u64			reserved;
+};
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/include/drm-uapi/i915_drm.h b/include/drm-uapi/i915_drm.h
index 16e452aa12d4..ab80759a2b9b 100644
--- a/include/drm-uapi/i915_drm.h
+++ b/include/drm-uapi/i915_drm.h
@@ -1456,9 +1456,52 @@ struct drm_i915_gem_context_param {
 #define   I915_CONTEXT_MAX_USER_PRIORITY	1023 /* inclusive */
 #define   I915_CONTEXT_DEFAULT_PRIORITY		0
 #define   I915_CONTEXT_MIN_USER_PRIORITY	-1023 /* inclusive */
+	/*
+	 * When using the following param, value should be a pointer to
+	 * drm_i915_gem_context_param_sseu.
+	 */
+#define I915_CONTEXT_PARAM_SSEU		0x7
 	__u64 value;
 };
 
+struct drm_i915_gem_context_param_sseu {
+	/*
+	 * Engine class & instance to be configured or queried.
+	 */
+	__u16 class;
+	__u16 instance;
+
+	/*
+	 * Unused for now. Must be cleared to zero.
+	 */
+	__u32 rsvd1;
+
+	/*
+	 * Mask of slices to enable for the context. Valid values are a subset
+	 * of the bitmask value returned for I915_PARAM_SLICE_MASK.
+	 */
+	__u64 slice_mask;
+
+	/*
+	 * Mask of subslices to enable for the context. Valid values are a
+	 * subset of the bitmask value return by I915_PARAM_SUBSLICE_MASK.
+	 */
+	__u64 subslice_mask;
+
+	/*
+	 * Minimum/Maximum number of EUs to enable per subslice for the
+	 * context. min_eus_per_subslice must be inferior or equal to
+	 * max_eus_per_subslice.
+	 */
+	__u16 min_eus_per_subslice;
+	__u16 max_eus_per_subslice;
+
+	/*
+	 * Unused for now. Must be cleared to zero.
+	 */
+	__u32 rsvd2;
+};
+
 enum drm_i915_oa_format {
 	I915_OA_FORMAT_A13 = 1,	    /* HSW only */
 	I915_OA_FORMAT_A29,	    /* HSW only */
diff --git a/include/drm-uapi/msm_drm.h b/include/drm-uapi/msm_drm.h
index bbbaffad772d..c06d0a5bdd80 100644
--- a/include/drm-uapi/msm_drm.h
+++ b/include/drm-uapi/msm_drm.h
@@ -201,10 +201,12 @@ struct drm_msm_gem_submit_bo {
 #define MSM_SUBMIT_NO_IMPLICIT   0x80000000 /* disable implicit sync */
 #define MSM_SUBMIT_FENCE_FD_IN   0x40000000 /* enable input fence_fd */
 #define MSM_SUBMIT_FENCE_FD_OUT  0x20000000 /* enable output fence_fd */
+#define MSM_SUBMIT_SUDO          0x10000000 /* run submitted cmds from RB */
 #define MSM_SUBMIT_FLAGS                ( \
 		MSM_SUBMIT_NO_IMPLICIT   | \
 		MSM_SUBMIT_FENCE_FD_IN   | \
 		MSM_SUBMIT_FENCE_FD_OUT  | \
+		MSM_SUBMIT_SUDO          | \
 		0)
 
 /* Each cmdstream submit consists of a table of buffers involved, and
diff --git a/include/drm-uapi/tegra_drm.h b/include/drm-uapi/tegra_drm.h
index 12f9bf848db1..6c07919c04e9 100644
--- a/include/drm-uapi/tegra_drm.h
+++ b/include/drm-uapi/tegra_drm.h
@@ -32,143 +32,615 @@ extern "C" {
 #define DRM_TEGRA_GEM_CREATE_TILED     (1 << 0)
 #define DRM_TEGRA_GEM_CREATE_BOTTOM_UP (1 << 1)
 
+/**
+ * struct drm_tegra_gem_create - parameters for the GEM object creation IOCTL
+ */
 struct drm_tegra_gem_create {
+	/**
+	 * @size:
+	 *
+	 * The size, in bytes, of the buffer object to be created.
+	 */
 	__u64 size;
+
+	/**
+	 * @flags:
+	 *
+	 * A bitmask of flags that influence the creation of GEM objects:
+	 *
+	 * DRM_TEGRA_GEM_CREATE_TILED
+	 *   Use the 16x16 tiling format for this buffer.
+	 *
+	 * DRM_TEGRA_GEM_CREATE_BOTTOM_UP
+	 *   The buffer has a bottom-up layout.
+	 */
 	__u32 flags;
+
+	/**
+	 * @handle:
+	 *
+	 * The handle of the created GEM object. Set by the kernel upon
+	 * successful completion of the IOCTL.
+	 */
 	__u32 handle;
 };
 
+/**
+ * struct drm_tegra_gem_mmap - parameters for the GEM mmap IOCTL
+ */
 struct drm_tegra_gem_mmap {
+	/**
+	 * @handle:
+	 *
+	 * Handle of the GEM object to obtain an mmap offset for.
+	 */
 	__u32 handle;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
+
+	/**
+	 * @offset:
+	 *
+	 * The mmap offset for the given GEM object. Set by the kernel upon
+	 * successful completion of the IOCTL.
+	 */
 	__u64 offset;
 };
 
+/**
+ * struct drm_tegra_syncpt_read - parameters for the read syncpoint IOCTL
+ */
 struct drm_tegra_syncpt_read {
+	/**
+	 * @id:
+	 *
+	 * ID of the syncpoint to read the current value from.
+	 */
 	__u32 id;
+
+	/**
+	 * @value:
+	 *
+	 * The current syncpoint value. Set by the kernel upon successful
+	 * completion of the IOCTL.
+	 */
 	__u32 value;
 };
 
+/**
+ * struct drm_tegra_syncpt_incr - parameters for the increment syncpoint IOCTL
+ */
 struct drm_tegra_syncpt_incr {
+	/**
+	 * @id:
+	 *
+	 * ID of the syncpoint to increment.
+	 */
 	__u32 id;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
 };
 
+/**
+ * struct drm_tegra_syncpt_wait - parameters for the wait syncpoint IOCTL
+ */
 struct drm_tegra_syncpt_wait {
+	/**
+	 * @id:
+	 *
+	 * ID of the syncpoint to wait on.
+	 */
 	__u32 id;
+
+	/**
+	 * @thresh:
+	 *
+	 * Threshold value for which to wait.
+	 */
 	__u32 thresh;
+
+	/**
+	 * @timeout:
+	 *
+	 * Timeout, in milliseconds, to wait.
+	 */
 	__u32 timeout;
+
+	/**
+	 * @value:
+	 *
+	 * The new syncpoint value after the wait. Set by the kernel upon
+	 * successful completion of the IOCTL.
+	 */
 	__u32 value;
 };
 
 #define DRM_TEGRA_NO_TIMEOUT	(0xffffffff)
 
+/**
+ * struct drm_tegra_open_channel - parameters for the open channel IOCTL
+ */
 struct drm_tegra_open_channel {
+	/**
+	 * @client:
+	 *
+	 * The client ID for this channel.
+	 */
 	__u32 client;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
+
+	/**
+	 * @context:
+	 *
+	 * The application context of this channel. Set by the kernel upon
+	 * successful completion of the IOCTL. This context needs to be passed
+	 * to the DRM_TEGRA_CHANNEL_CLOSE or the DRM_TEGRA_SUBMIT IOCTLs.
+	 */
 	__u64 context;
 };
 
+/**
+ * struct drm_tegra_close_channel - parameters for the close channel IOCTL
+ */
 struct drm_tegra_close_channel {
+	/**
+	 * @context:
+	 *
+	 * The application context of this channel. This is obtained from the
+	 * DRM_TEGRA_OPEN_CHANNEL IOCTL.
+	 */
 	__u64 context;
 };
 
+/**
+ * struct drm_tegra_get_syncpt - parameters for the get syncpoint IOCTL
+ */
 struct drm_tegra_get_syncpt {
+	/**
+	 * @context:
+	 *
+	 * The application context identifying the channel for which to obtain
+	 * the syncpoint ID.
+	 */
 	__u64 context;
+
+	/**
+	 * @index:
+	 *
+	 * Index of the client syncpoint for which to obtain the ID.
+	 */
 	__u32 index;
+
+	/**
+	 * @id:
+	 *
+	 * The ID of the given syncpoint. Set by the kernel upon successful
+	 * completion of the IOCTL.
+	 */
 	__u32 id;
 };
 
+/**
+ * struct drm_tegra_get_syncpt_base - parameters for the get wait base IOCTL
+ */
 struct drm_tegra_get_syncpt_base {
+	/**
+	 * @context:
+	 *
+	 * The application context identifying for which channel to obtain the
+	 * wait base.
+	 */
 	__u64 context;
+
+	/**
+	 * @syncpt:
+	 *
+	 * ID of the syncpoint for which to obtain the wait base.
+	 */
 	__u32 syncpt;
+
+	/**
+	 * @id:
+	 *
+	 * The ID of the wait base corresponding to the client syncpoint. Set
+	 * by the kernel upon successful completion of the IOCTL.
+	 */
 	__u32 id;
 };
 
+/**
+ * struct drm_tegra_syncpt - syncpoint increment operation
+ */
 struct drm_tegra_syncpt {
+	/**
+	 * @id:
+	 *
+	 * ID of the syncpoint to operate on.
+	 */
 	__u32 id;
+
+	/**
+	 * @incrs:
+	 *
+	 * Number of increments to perform for the syncpoint.
+	 */
 	__u32 incrs;
 };
 
+/**
+ * struct drm_tegra_cmdbuf - structure describing a command buffer
+ */
 struct drm_tegra_cmdbuf {
+	/**
+	 * @handle:
+	 *
+	 * Handle to a GEM object containing the command buffer.
+	 */
 	__u32 handle;
+
+	/**
+	 * @offset:
+	 *
+	 * Offset, in bytes, into the GEM object identified by @handle at
+	 * which the command buffer starts.
+	 */
 	__u32 offset;
+
+	/**
+	 * @words:
+	 *
+	 * Number of 32-bit words in this command buffer.
+	 */
 	__u32 words;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
 };
 
+/**
+ * struct drm_tegra_reloc - GEM object relocation structure
+ */
 struct drm_tegra_reloc {
 	struct {
+		/**
+		 * @cmdbuf.handle:
+		 *
+		 * Handle to the GEM object containing the command buffer for
+		 * which to perform this GEM object relocation.
+		 */
 		__u32 handle;
+
+		/**
+		 * @cmdbuf.offset:
+		 *
+		 * Offset, in bytes, into the command buffer at which to
+		 * insert the relocated address.
+		 */
 		__u32 offset;
 	} cmdbuf;
 	struct {
+		/**
+		 * @target.handle:
+		 *
+		 * Handle to the GEM object to be relocated.
+		 */
 		__u32 handle;
+
+		/**
+		 * @target.offset:
+		 *
+		 * Offset, in bytes, into the target GEM object at which the
+		 * relocated data starts.
+		 */
 		__u32 offset;
 	} target;
+
+	/**
+	 * @shift:
+	 *
+	 * The number of bits by which to shift relocated addresses.
+	 */
 	__u32 shift;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
 };
 
+/**
+ * struct drm_tegra_waitchk - wait check structure
+ */
 struct drm_tegra_waitchk {
+	/**
+	 * @handle:
+	 *
+	 * Handle to the GEM object containing a command stream on which to
+	 * perform the wait check.
+	 */
 	__u32 handle;
+
+	/**
+	 * @offset:
+	 *
+	 * Offset, in bytes, of the location in the command stream to perform
+	 * the wait check on.
+	 */
 	__u32 offset;
+
+	/**
+	 * @syncpt:
+	 *
+	 * ID of the syncpoint to wait check.
+	 */
 	__u32 syncpt;
+
+	/**
+	 * @thresh:
+	 *
+	 * Threshold value for which to check.
+	 */
 	__u32 thresh;
 };
 
+/**
+ * struct drm_tegra_submit - job submission structure
+ */
 struct drm_tegra_submit {
+	/**
+	 * @context:
+	 *
+	 * The application context identifying the channel to use for the
+	 * execution of this job.
+	 */
 	__u64 context;
+
+	/**
+	 * @num_syncpts:
+	 *
+	 * The number of syncpoints operated on by this job. This defines the
+	 * length of the array pointed to by @syncpts.
+	 */
 	__u32 num_syncpts;
+
+	/**
+	 * @num_cmdbufs:
+	 *
+	 * The number of command buffers to execute as part of this job. This
+	 * defines the length of the array pointed to by @cmdbufs.
+	 */
 	__u32 num_cmdbufs;
+
+	/**
+	 * @num_relocs:
+	 *
+	 * The number of relocations to perform before executing this job.
+	 * This defines the length of the array pointed to by @relocs.
+	 */
 	__u32 num_relocs;
+
+	/**
+	 * @num_waitchks:
+	 *
+	 * The number of wait checks to perform as part of this job. This
+	 * defines the length of the array pointed to by @waitchks.
+	 */
 	__u32 num_waitchks;
+
+	/**
+	 * @waitchk_mask:
+	 *
+	 * Bitmask of valid wait checks.
+	 */
 	__u32 waitchk_mask;
+
+	/**
+	 * @timeout:
+	 *
+	 * Timeout, in milliseconds, before this job is cancelled.
+	 */
 	__u32 timeout;
+
+	/**
+	 * @syncpts:
+	 *
+	 * A pointer to an array of &struct drm_tegra_syncpt structures that
+	 * specify the syncpoint operations performed as part of this job.
+	 * The number of elements in the array must be equal to the value
+	 * given by @num_syncpts.
+	 */
 	__u64 syncpts;
+
+	/**
+	 * @cmdbufs:
+	 *
+	 * A pointer to an array of &struct drm_tegra_cmdbuf structures that
+	 * define the command buffers to execute as part of this job. The
+	 * number of elements in the array must be equal to the value given
+	 * by @num_syncpts.
+	 */
 	__u64 cmdbufs;
+
+	/**
+	 * @relocs:
+	 *
+	 * A pointer to an array of &struct drm_tegra_reloc structures that
+	 * specify the relocations that need to be performed before executing
+	 * this job. The number of elements in the array must be equal to the
+	 * value given by @num_relocs.
+	 */
 	__u64 relocs;
+
+	/**
+	 * @waitchks:
+	 *
+	 * A pointer to an array of &struct drm_tegra_waitchk structures that
+	 * specify the wait checks to be performed while executing this job.
+	 * The number of elements in the array must be equal to the value
+	 * given by @num_waitchks.
+	 */
 	__u64 waitchks;
-	__u32 fence;		/* Return value */
 
-	__u32 reserved[5];	/* future expansion */
+	/**
+	 * @fence:
+	 *
+	 * The threshold of the syncpoint associated with this job after it
+	 * has been completed. Set by the kernel upon successful completion of
+	 * the IOCTL. This can be used with the DRM_TEGRA_SYNCPT_WAIT IOCTL to
+	 * wait for this job to be finished.
+	 */
+	__u32 fence;
+
+	/**
+	 * @reserved:
+	 *
+	 * This field is reserved for future use. Must be 0.
+	 */
+	__u32 reserved[5];
 };
 
 #define DRM_TEGRA_GEM_TILING_MODE_PITCH 0
 #define DRM_TEGRA_GEM_TILING_MODE_TILED 1
 #define DRM_TEGRA_GEM_TILING_MODE_BLOCK 2
 
+/**
+ * struct drm_tegra_gem_set_tiling - parameters for the set tiling IOCTL
+ */
 struct drm_tegra_gem_set_tiling {
-	/* input */
+	/**
+	 * @handle:
+	 *
+	 * Handle to the GEM object for which to set the tiling parameters.
+	 */
 	__u32 handle;
+
+	/**
+	 * @mode:
+	 *
+	 * The tiling mode to set. Must be one of:
+	 *
+	 * DRM_TEGRA_GEM_TILING_MODE_PITCH
+	 *   pitch linear format
+	 *
+	 * DRM_TEGRA_GEM_TILING_MODE_TILED
+	 *   16x16 tiling format
+	 *
+	 * DRM_TEGRA_GEM_TILING_MODE_BLOCK
+	 *   16Bx2 tiling format
+	 */
 	__u32 mode;
+
+	/**
+	 * @value:
+	 *
+	 * The value to set for the tiling mode parameter.
+	 */
 	__u32 value;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
 };
 
+/**
+ * struct drm_tegra_gem_get_tiling - parameters for the get tiling IOCTL
+ */
 struct drm_tegra_gem_get_tiling {
-	/* input */
+	/**
+	 * @handle:
+	 *
+	 * Handle to the GEM object for which to query the tiling parameters.
+	 */
 	__u32 handle;
-	/* output */
+
+	/**
+	 * @mode:
+	 *
+	 * The tiling mode currently associated with the GEM object. Set by
+	 * the kernel upon successful completion of the IOCTL.
+	 */
 	__u32 mode;
+
+	/**
+	 * @value:
+	 *
+	 * The tiling mode parameter currently associated with the GEM object.
+	 * Set by the kernel upon successful completion of the IOCTL.
+	 */
 	__u32 value;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
 };
 
 #define DRM_TEGRA_GEM_BOTTOM_UP		(1 << 0)
 #define DRM_TEGRA_GEM_FLAGS		(DRM_TEGRA_GEM_BOTTOM_UP)
 
+/**
+ * struct drm_tegra_gem_set_flags - parameters for the set flags IOCTL
+ */
 struct drm_tegra_gem_set_flags {
-	/* input */
+	/**
+	 * @handle:
+	 *
+	 * Handle to the GEM object for which to set the flags.
+	 */
 	__u32 handle;
-	/* output */
+
+	/**
+	 * @flags:
+	 *
+	 * The flags to set for the GEM object.
+	 */
 	__u32 flags;
 };
 
+/**
+ * struct drm_tegra_gem_get_flags - parameters for the get flags IOCTL
+ */
 struct drm_tegra_gem_get_flags {
-	/* input */
+	/**
+	 * @handle:
+	 *
+	 * Handle to the GEM object for which to query the flags.
+	 */
 	__u32 handle;
-	/* output */
+
+	/**
+	 * @flags:
+	 *
+	 * The flags currently associated with the GEM object. Set by the
+	 * kernel upon successful completion of the IOCTL.
+	 */
 	__u32 flags;
 };
 
@@ -193,7 +665,7 @@ struct drm_tegra_gem_get_flags {
 #define DRM_IOCTL_TEGRA_SYNCPT_INCR DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_INCR, struct drm_tegra_syncpt_incr)
 #define DRM_IOCTL_TEGRA_SYNCPT_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_WAIT, struct drm_tegra_syncpt_wait)
 #define DRM_IOCTL_TEGRA_OPEN_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_OPEN_CHANNEL, struct drm_tegra_open_channel)
-#define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_open_channel)
+#define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_close_channel)
 #define DRM_IOCTL_TEGRA_GET_SYNCPT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT, struct drm_tegra_get_syncpt)
 #define DRM_IOCTL_TEGRA_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SUBMIT, struct drm_tegra_submit)
 #define DRM_IOCTL_TEGRA_GET_SYNCPT_BASE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT_BASE, struct drm_tegra_get_syncpt_base)
diff --git a/include/drm-uapi/vc4_drm.h b/include/drm-uapi/vc4_drm.h
index 4117117b4204..31f50de39acb 100644
--- a/include/drm-uapi/vc4_drm.h
+++ b/include/drm-uapi/vc4_drm.h
@@ -183,10 +183,17 @@ struct drm_vc4_submit_cl {
 	/* ID of the perfmon to attach to this job. 0 means no perfmon. */
 	__u32 perfmonid;
 
-	/* Unused field to align this struct on 64 bits. Must be set to 0.
-	 * If one ever needs to add an u32 field to this struct, this field
-	 * can be used.
+	/* Syncobj handle to wait on. If set, processing of this render job
+	 * will not start until the syncobj is signaled. 0 means ignore.
 	 */
+	__u32 in_sync;
+
+	/* Syncobj handle to export fence to. If set, the fence in the syncobj
+	 * will be replaced with a fence that signals upon completion of this
+	 * render job. 0 means ignore.
+	 */
+	__u32 out_sync;
+
 	__u32 pad2;
 };
 
diff --git a/include/drm-uapi/virtgpu_drm.h b/include/drm-uapi/virtgpu_drm.h
index 91a31ffed828..9a781f0611df 100644
--- a/include/drm-uapi/virtgpu_drm.h
+++ b/include/drm-uapi/virtgpu_drm.h
@@ -63,6 +63,7 @@ struct drm_virtgpu_execbuffer {
 };
 
 #define VIRTGPU_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */
+#define VIRTGPU_PARAM_CAPSET_QUERY_FIX 2 /* do we have the capset fix */
 
 struct drm_virtgpu_getparam {
 	__u64 param;
-- 
2.17.1

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

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

* [igt-dev] [PATH i-g-t 1/2] headers: bump
@ 2018-09-05 14:25   ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-05 14:25 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

---
 include/drm-uapi/amdgpu_drm.h  |  23 ++
 include/drm-uapi/drm.h         |   7 +
 include/drm-uapi/drm_mode.h    |  22 +-
 include/drm-uapi/etnaviv_drm.h |   6 +
 include/drm-uapi/exynos_drm.h  | 240 ++++++++++++++++
 include/drm-uapi/i915_drm.h    |  43 +++
 include/drm-uapi/msm_drm.h     |   2 +
 include/drm-uapi/tegra_drm.h   | 492 ++++++++++++++++++++++++++++++++-
 include/drm-uapi/vc4_drm.h     |  13 +-
 include/drm-uapi/virtgpu_drm.h |   1 +
 10 files changed, 833 insertions(+), 16 deletions(-)

diff --git a/include/drm-uapi/amdgpu_drm.h b/include/drm-uapi/amdgpu_drm.h
index 1816bd8200d1..78b4dd89fcb4 100644
--- a/include/drm-uapi/amdgpu_drm.h
+++ b/include/drm-uapi/amdgpu_drm.h
@@ -78,6 +78,12 @@ extern "C" {
 #define AMDGPU_GEM_DOMAIN_GDS		0x8
 #define AMDGPU_GEM_DOMAIN_GWS		0x10
 #define AMDGPU_GEM_DOMAIN_OA		0x20
+#define AMDGPU_GEM_DOMAIN_MASK		(AMDGPU_GEM_DOMAIN_CPU | \
+					 AMDGPU_GEM_DOMAIN_GTT | \
+					 AMDGPU_GEM_DOMAIN_VRAM | \
+					 AMDGPU_GEM_DOMAIN_GDS | \
+					 AMDGPU_GEM_DOMAIN_GWS | \
+					 AMDGPU_GEM_DOMAIN_OA)
 
 /* Flag that CPU access will be required for the case of VRAM domain */
 #define AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED	(1 << 0)
@@ -95,6 +101,10 @@ extern "C" {
 #define AMDGPU_GEM_CREATE_VM_ALWAYS_VALID	(1 << 6)
 /* Flag that BO sharing will be explicitly synchronized */
 #define AMDGPU_GEM_CREATE_EXPLICIT_SYNC		(1 << 7)
+/* Flag that indicates allocating MQD gart on GFX9, where the mtype
+ * for the second page onward should be set to NC.
+ */
+#define AMDGPU_GEM_CREATE_MQD_GFX9		(1 << 8)
 
 struct drm_amdgpu_gem_create_in  {
 	/** the requested memory size */
@@ -520,6 +530,10 @@ union drm_amdgpu_cs {
 /* Preempt flag, IB should set Pre_enb bit if PREEMPT flag detected */
 #define AMDGPU_IB_FLAG_PREEMPT (1<<2)
 
+/* The IB fence should do the L2 writeback but not invalidate any shader
+ * caches (L2/vL1/sL1/I$). */
+#define AMDGPU_IB_FLAG_TC_WB_NOT_INVALIDATE (1 << 3)
+
 struct drm_amdgpu_cs_chunk_ib {
 	__u32 _pad;
 	/** AMDGPU_IB_FLAG_* */
@@ -618,6 +632,14 @@ struct drm_amdgpu_cs_chunk_data {
 	#define AMDGPU_INFO_FW_SOS		0x0c
 	/* Subquery id: Query PSP ASD firmware version */
 	#define AMDGPU_INFO_FW_ASD		0x0d
+	/* Subquery id: Query VCN firmware version */
+	#define AMDGPU_INFO_FW_VCN		0x0e
+	/* Subquery id: Query GFX RLC SRLC firmware version */
+	#define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_CNTL 0x0f
+	/* Subquery id: Query GFX RLC SRLG firmware version */
+	#define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_GPM_MEM 0x10
+	/* Subquery id: Query GFX RLC SRLS firmware version */
+	#define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_SRM_MEM 0x11
 /* number of bytes moved for TTM migration */
 #define AMDGPU_INFO_NUM_BYTES_MOVED		0x0f
 /* the used VRAM size */
@@ -806,6 +828,7 @@ struct drm_amdgpu_info_firmware {
 #define AMDGPU_VRAM_TYPE_GDDR5 5
 #define AMDGPU_VRAM_TYPE_HBM   6
 #define AMDGPU_VRAM_TYPE_DDR3  7
+#define AMDGPU_VRAM_TYPE_DDR4  8
 
 struct drm_amdgpu_info_device {
 	/** PCI Device ID */
diff --git a/include/drm-uapi/drm.h b/include/drm-uapi/drm.h
index f0bd91de0cf9..778a97fcfe63 100644
--- a/include/drm-uapi/drm.h
+++ b/include/drm-uapi/drm.h
@@ -674,6 +674,13 @@ struct drm_get_cap {
  */
 #define DRM_CLIENT_CAP_ATOMIC	3
 
+/**
+ * DRM_CLIENT_CAP_ASPECT_RATIO
+ *
+ * If set to 1, the DRM core will provide aspect ratio information in modes.
+ */
+#define DRM_CLIENT_CAP_ASPECT_RATIO    4
+
 /** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */
 struct drm_set_client_cap {
 	__u64 capability;
diff --git a/include/drm-uapi/drm_mode.h b/include/drm-uapi/drm_mode.h
index 2c575794fb52..971c016b368c 100644
--- a/include/drm-uapi/drm_mode.h
+++ b/include/drm-uapi/drm_mode.h
@@ -93,6 +93,15 @@ extern "C" {
 #define DRM_MODE_PICTURE_ASPECT_NONE		0
 #define DRM_MODE_PICTURE_ASPECT_4_3		1
 #define DRM_MODE_PICTURE_ASPECT_16_9		2
+#define DRM_MODE_PICTURE_ASPECT_64_27		3
+#define DRM_MODE_PICTURE_ASPECT_256_135		4
+
+/* Content type options */
+#define DRM_MODE_CONTENT_TYPE_NO_DATA		0
+#define DRM_MODE_CONTENT_TYPE_GRAPHICS		1
+#define DRM_MODE_CONTENT_TYPE_PHOTO		2
+#define DRM_MODE_CONTENT_TYPE_CINEMA		3
+#define DRM_MODE_CONTENT_TYPE_GAME		4
 
 /* Aspect ratio flag bitmask (4 bits 22:19) */
 #define DRM_MODE_FLAG_PIC_AR_MASK		(0x0F<<19)
@@ -102,6 +111,10 @@ extern "C" {
 			(DRM_MODE_PICTURE_ASPECT_4_3<<19)
 #define  DRM_MODE_FLAG_PIC_AR_16_9 \
 			(DRM_MODE_PICTURE_ASPECT_16_9<<19)
+#define  DRM_MODE_FLAG_PIC_AR_64_27 \
+			(DRM_MODE_PICTURE_ASPECT_64_27<<19)
+#define  DRM_MODE_FLAG_PIC_AR_256_135 \
+			(DRM_MODE_PICTURE_ASPECT_256_135<<19)
 
 #define  DRM_MODE_FLAG_ALL	(DRM_MODE_FLAG_PHSYNC |		\
 				 DRM_MODE_FLAG_NHSYNC |		\
@@ -363,7 +376,7 @@ struct drm_mode_get_connector {
 	__u32 pad;
 };
 
-#define DRM_MODE_PROP_PENDING	(1<<0)
+#define DRM_MODE_PROP_PENDING	(1<<0) /* deprecated, do not use */
 #define DRM_MODE_PROP_RANGE	(1<<1)
 #define DRM_MODE_PROP_IMMUTABLE	(1<<2)
 #define DRM_MODE_PROP_ENUM	(1<<3) /* enumerated type with text strings */
@@ -598,8 +611,11 @@ struct drm_mode_crtc_lut {
 };
 
 struct drm_color_ctm {
-	/* Conversion matrix in S31.32 format. */
-	__s64 matrix[9];
+	/*
+	 * Conversion matrix in S31.32 sign-magnitude
+	 * (not two's complement!) format.
+	 */
+	__u64 matrix[9];
 };
 
 struct drm_color_lut {
diff --git a/include/drm-uapi/etnaviv_drm.h b/include/drm-uapi/etnaviv_drm.h
index e9b997a0ef27..0d5c49dc478c 100644
--- a/include/drm-uapi/etnaviv_drm.h
+++ b/include/drm-uapi/etnaviv_drm.h
@@ -55,6 +55,12 @@ struct drm_etnaviv_timespec {
 #define ETNAVIV_PARAM_GPU_FEATURES_4                0x07
 #define ETNAVIV_PARAM_GPU_FEATURES_5                0x08
 #define ETNAVIV_PARAM_GPU_FEATURES_6                0x09
+#define ETNAVIV_PARAM_GPU_FEATURES_7                0x0a
+#define ETNAVIV_PARAM_GPU_FEATURES_8                0x0b
+#define ETNAVIV_PARAM_GPU_FEATURES_9                0x0c
+#define ETNAVIV_PARAM_GPU_FEATURES_10               0x0d
+#define ETNAVIV_PARAM_GPU_FEATURES_11               0x0e
+#define ETNAVIV_PARAM_GPU_FEATURES_12               0x0f
 
 #define ETNAVIV_PARAM_GPU_STREAM_COUNT              0x10
 #define ETNAVIV_PARAM_GPU_REGISTER_MAX              0x11
diff --git a/include/drm-uapi/exynos_drm.h b/include/drm-uapi/exynos_drm.h
index a00116b5cc5c..7414cfd76419 100644
--- a/include/drm-uapi/exynos_drm.h
+++ b/include/drm-uapi/exynos_drm.h
@@ -135,6 +135,219 @@ struct drm_exynos_g2d_exec {
 	__u64					async;
 };
 
+/* Exynos DRM IPP v2 API */
+
+/**
+ * Enumerate available IPP hardware modules.
+ *
+ * @count_ipps: size of ipp_id array / number of ipp modules (set by driver)
+ * @reserved: padding
+ * @ipp_id_ptr: pointer to ipp_id array or NULL
+ */
+struct drm_exynos_ioctl_ipp_get_res {
+	__u32 count_ipps;
+	__u32 reserved;
+	__u64 ipp_id_ptr;
+};
+
+enum drm_exynos_ipp_format_type {
+	DRM_EXYNOS_IPP_FORMAT_SOURCE		= 0x01,
+	DRM_EXYNOS_IPP_FORMAT_DESTINATION	= 0x02,
+};
+
+struct drm_exynos_ipp_format {
+	__u32 fourcc;
+	__u32 type;
+	__u64 modifier;
+};
+
+enum drm_exynos_ipp_capability {
+	DRM_EXYNOS_IPP_CAP_CROP		= 0x01,
+	DRM_EXYNOS_IPP_CAP_ROTATE	= 0x02,
+	DRM_EXYNOS_IPP_CAP_SCALE	= 0x04,
+	DRM_EXYNOS_IPP_CAP_CONVERT	= 0x08,
+};
+
+/**
+ * Get IPP hardware capabilities and supported image formats.
+ *
+ * @ipp_id: id of IPP module to query
+ * @capabilities: bitmask of drm_exynos_ipp_capability (set by driver)
+ * @reserved: padding
+ * @formats_count: size of formats array (in entries) / number of filled
+ *		   formats (set by driver)
+ * @formats_ptr: pointer to formats array or NULL
+ */
+struct drm_exynos_ioctl_ipp_get_caps {
+	__u32 ipp_id;
+	__u32 capabilities;
+	__u32 reserved;
+	__u32 formats_count;
+	__u64 formats_ptr;
+};
+
+enum drm_exynos_ipp_limit_type {
+	/* size (horizontal/vertial) limits, in pixels (min, max, alignment) */
+	DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE		= 0x0001,
+	/* scale ratio (horizonta/vertial), 16.16 fixed point (min, max) */
+	DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE		= 0x0002,
+
+	/* image buffer area */
+	DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER	= 0x0001 << 16,
+	/* src/dst rectangle area */
+	DRM_EXYNOS_IPP_LIMIT_SIZE_AREA		= 0x0002 << 16,
+	/* src/dst rectangle area when rotation enabled */
+	DRM_EXYNOS_IPP_LIMIT_SIZE_ROTATED	= 0x0003 << 16,
+
+	DRM_EXYNOS_IPP_LIMIT_TYPE_MASK		= 0x000f,
+	DRM_EXYNOS_IPP_LIMIT_SIZE_MASK		= 0x000f << 16,
+};
+
+struct drm_exynos_ipp_limit_val {
+	__u32 min;
+	__u32 max;
+	__u32 align;
+	__u32 reserved;
+};
+
+/**
+ * IPP module limitation.
+ *
+ * @type: limit type (see drm_exynos_ipp_limit_type enum)
+ * @reserved: padding
+ * @h: horizontal limits
+ * @v: vertical limits
+ */
+struct drm_exynos_ipp_limit {
+	__u32 type;
+	__u32 reserved;
+	struct drm_exynos_ipp_limit_val h;
+	struct drm_exynos_ipp_limit_val v;
+};
+
+/**
+ * Get IPP limits for given image format.
+ *
+ * @ipp_id: id of IPP module to query
+ * @fourcc: image format code (see DRM_FORMAT_* in drm_fourcc.h)
+ * @modifier: image format modifier (see DRM_FORMAT_MOD_* in drm_fourcc.h)
+ * @type: source/destination identifier (drm_exynos_ipp_format_flag enum)
+ * @limits_count: size of limits array (in entries) / number of filled entries
+ *		 (set by driver)
+ * @limits_ptr: pointer to limits array or NULL
+ */
+struct drm_exynos_ioctl_ipp_get_limits {
+	__u32 ipp_id;
+	__u32 fourcc;
+	__u64 modifier;
+	__u32 type;
+	__u32 limits_count;
+	__u64 limits_ptr;
+};
+
+enum drm_exynos_ipp_task_id {
+	/* buffer described by struct drm_exynos_ipp_task_buffer */
+	DRM_EXYNOS_IPP_TASK_BUFFER		= 0x0001,
+	/* rectangle described by struct drm_exynos_ipp_task_rect */
+	DRM_EXYNOS_IPP_TASK_RECTANGLE		= 0x0002,
+	/* transformation described by struct drm_exynos_ipp_task_transform */
+	DRM_EXYNOS_IPP_TASK_TRANSFORM		= 0x0003,
+	/* alpha configuration described by struct drm_exynos_ipp_task_alpha */
+	DRM_EXYNOS_IPP_TASK_ALPHA		= 0x0004,
+
+	/* source image data (for buffer and rectangle chunks) */
+	DRM_EXYNOS_IPP_TASK_TYPE_SOURCE		= 0x0001 << 16,
+	/* destination image data (for buffer and rectangle chunks) */
+	DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION	= 0x0002 << 16,
+};
+
+/**
+ * Memory buffer with image data.
+ *
+ * @id: must be DRM_EXYNOS_IPP_TASK_BUFFER
+ * other parameters are same as for AddFB2 generic DRM ioctl
+ */
+struct drm_exynos_ipp_task_buffer {
+	__u32	id;
+	__u32	fourcc;
+	__u32	width, height;
+	__u32	gem_id[4];
+	__u32	offset[4];
+	__u32	pitch[4];
+	__u64	modifier;
+};
+
+/**
+ * Rectangle for processing.
+ *
+ * @id: must be DRM_EXYNOS_IPP_TASK_RECTANGLE
+ * @reserved: padding
+ * @x,@y: left corner in pixels
+ * @w,@h: width/height in pixels
+ */
+struct drm_exynos_ipp_task_rect {
+	__u32	id;
+	__u32	reserved;
+	__u32	x;
+	__u32	y;
+	__u32	w;
+	__u32	h;
+};
+
+/**
+ * Image tranformation description.
+ *
+ * @id: must be DRM_EXYNOS_IPP_TASK_TRANSFORM
+ * @rotation: DRM_MODE_ROTATE_* and DRM_MODE_REFLECT_* values
+ */
+struct drm_exynos_ipp_task_transform {
+	__u32	id;
+	__u32	rotation;
+};
+
+/**
+ * Image global alpha configuration for formats without alpha values.
+ *
+ * @id: must be DRM_EXYNOS_IPP_TASK_ALPHA
+ * @value: global alpha value (0-255)
+ */
+struct drm_exynos_ipp_task_alpha {
+	__u32	id;
+	__u32	value;
+};
+
+enum drm_exynos_ipp_flag {
+	/* generate DRM event after processing */
+	DRM_EXYNOS_IPP_FLAG_EVENT	= 0x01,
+	/* dry run, only check task parameters */
+	DRM_EXYNOS_IPP_FLAG_TEST_ONLY	= 0x02,
+	/* non-blocking processing */
+	DRM_EXYNOS_IPP_FLAG_NONBLOCK	= 0x04,
+};
+
+#define DRM_EXYNOS_IPP_FLAGS (DRM_EXYNOS_IPP_FLAG_EVENT |\
+		DRM_EXYNOS_IPP_FLAG_TEST_ONLY | DRM_EXYNOS_IPP_FLAG_NONBLOCK)
+
+/**
+ * Perform image processing described by array of drm_exynos_ipp_task_*
+ * structures (parameters array).
+ *
+ * @ipp_id: id of IPP module to run the task
+ * @flags: bitmask of drm_exynos_ipp_flag values
+ * @reserved: padding
+ * @params_size: size of parameters array (in bytes)
+ * @params_ptr: pointer to parameters array or NULL
+ * @user_data: (optional) data for drm event
+ */
+struct drm_exynos_ioctl_ipp_commit {
+	__u32 ipp_id;
+	__u32 flags;
+	__u32 reserved;
+	__u32 params_size;
+	__u64 params_ptr;
+	__u64 user_data;
+};
+
 #define DRM_EXYNOS_GEM_CREATE		0x00
 #define DRM_EXYNOS_GEM_MAP		0x01
 /* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */
@@ -147,6 +360,11 @@ struct drm_exynos_g2d_exec {
 #define DRM_EXYNOS_G2D_EXEC		0x22
 
 /* Reserved 0x30 ~ 0x33 for obsolete Exynos IPP ioctls */
+/* IPP - Image Post Processing */
+#define DRM_EXYNOS_IPP_GET_RESOURCES	0x40
+#define DRM_EXYNOS_IPP_GET_CAPS		0x41
+#define DRM_EXYNOS_IPP_GET_LIMITS	0x42
+#define DRM_EXYNOS_IPP_COMMIT		0x43
 
 #define DRM_IOCTL_EXYNOS_GEM_CREATE		DRM_IOWR(DRM_COMMAND_BASE + \
 		DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
@@ -165,8 +383,20 @@ struct drm_exynos_g2d_exec {
 #define DRM_IOCTL_EXYNOS_G2D_EXEC		DRM_IOWR(DRM_COMMAND_BASE + \
 		DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec)
 
+#define DRM_IOCTL_EXYNOS_IPP_GET_RESOURCES	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_IPP_GET_RESOURCES, \
+		struct drm_exynos_ioctl_ipp_get_res)
+#define DRM_IOCTL_EXYNOS_IPP_GET_CAPS		DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_IPP_GET_CAPS, struct drm_exynos_ioctl_ipp_get_caps)
+#define DRM_IOCTL_EXYNOS_IPP_GET_LIMITS		DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_IPP_GET_LIMITS, \
+		struct drm_exynos_ioctl_ipp_get_limits)
+#define DRM_IOCTL_EXYNOS_IPP_COMMIT		DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_IPP_COMMIT, struct drm_exynos_ioctl_ipp_commit)
+
 /* EXYNOS specific events */
 #define DRM_EXYNOS_G2D_EVENT		0x80000000
+#define DRM_EXYNOS_IPP_EVENT		0x80000002
 
 struct drm_exynos_g2d_event {
 	struct drm_event	base;
@@ -177,6 +407,16 @@ struct drm_exynos_g2d_event {
 	__u32			reserved;
 };
 
+struct drm_exynos_ipp_event {
+	struct drm_event	base;
+	__u64			user_data;
+	__u32			tv_sec;
+	__u32			tv_usec;
+	__u32			ipp_id;
+	__u32			sequence;
+	__u64			reserved;
+};
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/include/drm-uapi/i915_drm.h b/include/drm-uapi/i915_drm.h
index 16e452aa12d4..ab80759a2b9b 100644
--- a/include/drm-uapi/i915_drm.h
+++ b/include/drm-uapi/i915_drm.h
@@ -1456,9 +1456,52 @@ struct drm_i915_gem_context_param {
 #define   I915_CONTEXT_MAX_USER_PRIORITY	1023 /* inclusive */
 #define   I915_CONTEXT_DEFAULT_PRIORITY		0
 #define   I915_CONTEXT_MIN_USER_PRIORITY	-1023 /* inclusive */
+	/*
+	 * When using the following param, value should be a pointer to
+	 * drm_i915_gem_context_param_sseu.
+	 */
+#define I915_CONTEXT_PARAM_SSEU		0x7
 	__u64 value;
 };
 
+struct drm_i915_gem_context_param_sseu {
+	/*
+	 * Engine class & instance to be configured or queried.
+	 */
+	__u16 class;
+	__u16 instance;
+
+	/*
+	 * Unused for now. Must be cleared to zero.
+	 */
+	__u32 rsvd1;
+
+	/*
+	 * Mask of slices to enable for the context. Valid values are a subset
+	 * of the bitmask value returned for I915_PARAM_SLICE_MASK.
+	 */
+	__u64 slice_mask;
+
+	/*
+	 * Mask of subslices to enable for the context. Valid values are a
+	 * subset of the bitmask value return by I915_PARAM_SUBSLICE_MASK.
+	 */
+	__u64 subslice_mask;
+
+	/*
+	 * Minimum/Maximum number of EUs to enable per subslice for the
+	 * context. min_eus_per_subslice must be inferior or equal to
+	 * max_eus_per_subslice.
+	 */
+	__u16 min_eus_per_subslice;
+	__u16 max_eus_per_subslice;
+
+	/*
+	 * Unused for now. Must be cleared to zero.
+	 */
+	__u32 rsvd2;
+};
+
 enum drm_i915_oa_format {
 	I915_OA_FORMAT_A13 = 1,	    /* HSW only */
 	I915_OA_FORMAT_A29,	    /* HSW only */
diff --git a/include/drm-uapi/msm_drm.h b/include/drm-uapi/msm_drm.h
index bbbaffad772d..c06d0a5bdd80 100644
--- a/include/drm-uapi/msm_drm.h
+++ b/include/drm-uapi/msm_drm.h
@@ -201,10 +201,12 @@ struct drm_msm_gem_submit_bo {
 #define MSM_SUBMIT_NO_IMPLICIT   0x80000000 /* disable implicit sync */
 #define MSM_SUBMIT_FENCE_FD_IN   0x40000000 /* enable input fence_fd */
 #define MSM_SUBMIT_FENCE_FD_OUT  0x20000000 /* enable output fence_fd */
+#define MSM_SUBMIT_SUDO          0x10000000 /* run submitted cmds from RB */
 #define MSM_SUBMIT_FLAGS                ( \
 		MSM_SUBMIT_NO_IMPLICIT   | \
 		MSM_SUBMIT_FENCE_FD_IN   | \
 		MSM_SUBMIT_FENCE_FD_OUT  | \
+		MSM_SUBMIT_SUDO          | \
 		0)
 
 /* Each cmdstream submit consists of a table of buffers involved, and
diff --git a/include/drm-uapi/tegra_drm.h b/include/drm-uapi/tegra_drm.h
index 12f9bf848db1..6c07919c04e9 100644
--- a/include/drm-uapi/tegra_drm.h
+++ b/include/drm-uapi/tegra_drm.h
@@ -32,143 +32,615 @@ extern "C" {
 #define DRM_TEGRA_GEM_CREATE_TILED     (1 << 0)
 #define DRM_TEGRA_GEM_CREATE_BOTTOM_UP (1 << 1)
 
+/**
+ * struct drm_tegra_gem_create - parameters for the GEM object creation IOCTL
+ */
 struct drm_tegra_gem_create {
+	/**
+	 * @size:
+	 *
+	 * The size, in bytes, of the buffer object to be created.
+	 */
 	__u64 size;
+
+	/**
+	 * @flags:
+	 *
+	 * A bitmask of flags that influence the creation of GEM objects:
+	 *
+	 * DRM_TEGRA_GEM_CREATE_TILED
+	 *   Use the 16x16 tiling format for this buffer.
+	 *
+	 * DRM_TEGRA_GEM_CREATE_BOTTOM_UP
+	 *   The buffer has a bottom-up layout.
+	 */
 	__u32 flags;
+
+	/**
+	 * @handle:
+	 *
+	 * The handle of the created GEM object. Set by the kernel upon
+	 * successful completion of the IOCTL.
+	 */
 	__u32 handle;
 };
 
+/**
+ * struct drm_tegra_gem_mmap - parameters for the GEM mmap IOCTL
+ */
 struct drm_tegra_gem_mmap {
+	/**
+	 * @handle:
+	 *
+	 * Handle of the GEM object to obtain an mmap offset for.
+	 */
 	__u32 handle;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
+
+	/**
+	 * @offset:
+	 *
+	 * The mmap offset for the given GEM object. Set by the kernel upon
+	 * successful completion of the IOCTL.
+	 */
 	__u64 offset;
 };
 
+/**
+ * struct drm_tegra_syncpt_read - parameters for the read syncpoint IOCTL
+ */
 struct drm_tegra_syncpt_read {
+	/**
+	 * @id:
+	 *
+	 * ID of the syncpoint to read the current value from.
+	 */
 	__u32 id;
+
+	/**
+	 * @value:
+	 *
+	 * The current syncpoint value. Set by the kernel upon successful
+	 * completion of the IOCTL.
+	 */
 	__u32 value;
 };
 
+/**
+ * struct drm_tegra_syncpt_incr - parameters for the increment syncpoint IOCTL
+ */
 struct drm_tegra_syncpt_incr {
+	/**
+	 * @id:
+	 *
+	 * ID of the syncpoint to increment.
+	 */
 	__u32 id;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
 };
 
+/**
+ * struct drm_tegra_syncpt_wait - parameters for the wait syncpoint IOCTL
+ */
 struct drm_tegra_syncpt_wait {
+	/**
+	 * @id:
+	 *
+	 * ID of the syncpoint to wait on.
+	 */
 	__u32 id;
+
+	/**
+	 * @thresh:
+	 *
+	 * Threshold value for which to wait.
+	 */
 	__u32 thresh;
+
+	/**
+	 * @timeout:
+	 *
+	 * Timeout, in milliseconds, to wait.
+	 */
 	__u32 timeout;
+
+	/**
+	 * @value:
+	 *
+	 * The new syncpoint value after the wait. Set by the kernel upon
+	 * successful completion of the IOCTL.
+	 */
 	__u32 value;
 };
 
 #define DRM_TEGRA_NO_TIMEOUT	(0xffffffff)
 
+/**
+ * struct drm_tegra_open_channel - parameters for the open channel IOCTL
+ */
 struct drm_tegra_open_channel {
+	/**
+	 * @client:
+	 *
+	 * The client ID for this channel.
+	 */
 	__u32 client;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
+
+	/**
+	 * @context:
+	 *
+	 * The application context of this channel. Set by the kernel upon
+	 * successful completion of the IOCTL. This context needs to be passed
+	 * to the DRM_TEGRA_CHANNEL_CLOSE or the DRM_TEGRA_SUBMIT IOCTLs.
+	 */
 	__u64 context;
 };
 
+/**
+ * struct drm_tegra_close_channel - parameters for the close channel IOCTL
+ */
 struct drm_tegra_close_channel {
+	/**
+	 * @context:
+	 *
+	 * The application context of this channel. This is obtained from the
+	 * DRM_TEGRA_OPEN_CHANNEL IOCTL.
+	 */
 	__u64 context;
 };
 
+/**
+ * struct drm_tegra_get_syncpt - parameters for the get syncpoint IOCTL
+ */
 struct drm_tegra_get_syncpt {
+	/**
+	 * @context:
+	 *
+	 * The application context identifying the channel for which to obtain
+	 * the syncpoint ID.
+	 */
 	__u64 context;
+
+	/**
+	 * @index:
+	 *
+	 * Index of the client syncpoint for which to obtain the ID.
+	 */
 	__u32 index;
+
+	/**
+	 * @id:
+	 *
+	 * The ID of the given syncpoint. Set by the kernel upon successful
+	 * completion of the IOCTL.
+	 */
 	__u32 id;
 };
 
+/**
+ * struct drm_tegra_get_syncpt_base - parameters for the get wait base IOCTL
+ */
 struct drm_tegra_get_syncpt_base {
+	/**
+	 * @context:
+	 *
+	 * The application context identifying for which channel to obtain the
+	 * wait base.
+	 */
 	__u64 context;
+
+	/**
+	 * @syncpt:
+	 *
+	 * ID of the syncpoint for which to obtain the wait base.
+	 */
 	__u32 syncpt;
+
+	/**
+	 * @id:
+	 *
+	 * The ID of the wait base corresponding to the client syncpoint. Set
+	 * by the kernel upon successful completion of the IOCTL.
+	 */
 	__u32 id;
 };
 
+/**
+ * struct drm_tegra_syncpt - syncpoint increment operation
+ */
 struct drm_tegra_syncpt {
+	/**
+	 * @id:
+	 *
+	 * ID of the syncpoint to operate on.
+	 */
 	__u32 id;
+
+	/**
+	 * @incrs:
+	 *
+	 * Number of increments to perform for the syncpoint.
+	 */
 	__u32 incrs;
 };
 
+/**
+ * struct drm_tegra_cmdbuf - structure describing a command buffer
+ */
 struct drm_tegra_cmdbuf {
+	/**
+	 * @handle:
+	 *
+	 * Handle to a GEM object containing the command buffer.
+	 */
 	__u32 handle;
+
+	/**
+	 * @offset:
+	 *
+	 * Offset, in bytes, into the GEM object identified by @handle at
+	 * which the command buffer starts.
+	 */
 	__u32 offset;
+
+	/**
+	 * @words:
+	 *
+	 * Number of 32-bit words in this command buffer.
+	 */
 	__u32 words;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
 };
 
+/**
+ * struct drm_tegra_reloc - GEM object relocation structure
+ */
 struct drm_tegra_reloc {
 	struct {
+		/**
+		 * @cmdbuf.handle:
+		 *
+		 * Handle to the GEM object containing the command buffer for
+		 * which to perform this GEM object relocation.
+		 */
 		__u32 handle;
+
+		/**
+		 * @cmdbuf.offset:
+		 *
+		 * Offset, in bytes, into the command buffer at which to
+		 * insert the relocated address.
+		 */
 		__u32 offset;
 	} cmdbuf;
 	struct {
+		/**
+		 * @target.handle:
+		 *
+		 * Handle to the GEM object to be relocated.
+		 */
 		__u32 handle;
+
+		/**
+		 * @target.offset:
+		 *
+		 * Offset, in bytes, into the target GEM object at which the
+		 * relocated data starts.
+		 */
 		__u32 offset;
 	} target;
+
+	/**
+	 * @shift:
+	 *
+	 * The number of bits by which to shift relocated addresses.
+	 */
 	__u32 shift;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
 };
 
+/**
+ * struct drm_tegra_waitchk - wait check structure
+ */
 struct drm_tegra_waitchk {
+	/**
+	 * @handle:
+	 *
+	 * Handle to the GEM object containing a command stream on which to
+	 * perform the wait check.
+	 */
 	__u32 handle;
+
+	/**
+	 * @offset:
+	 *
+	 * Offset, in bytes, of the location in the command stream to perform
+	 * the wait check on.
+	 */
 	__u32 offset;
+
+	/**
+	 * @syncpt:
+	 *
+	 * ID of the syncpoint to wait check.
+	 */
 	__u32 syncpt;
+
+	/**
+	 * @thresh:
+	 *
+	 * Threshold value for which to check.
+	 */
 	__u32 thresh;
 };
 
+/**
+ * struct drm_tegra_submit - job submission structure
+ */
 struct drm_tegra_submit {
+	/**
+	 * @context:
+	 *
+	 * The application context identifying the channel to use for the
+	 * execution of this job.
+	 */
 	__u64 context;
+
+	/**
+	 * @num_syncpts:
+	 *
+	 * The number of syncpoints operated on by this job. This defines the
+	 * length of the array pointed to by @syncpts.
+	 */
 	__u32 num_syncpts;
+
+	/**
+	 * @num_cmdbufs:
+	 *
+	 * The number of command buffers to execute as part of this job. This
+	 * defines the length of the array pointed to by @cmdbufs.
+	 */
 	__u32 num_cmdbufs;
+
+	/**
+	 * @num_relocs:
+	 *
+	 * The number of relocations to perform before executing this job.
+	 * This defines the length of the array pointed to by @relocs.
+	 */
 	__u32 num_relocs;
+
+	/**
+	 * @num_waitchks:
+	 *
+	 * The number of wait checks to perform as part of this job. This
+	 * defines the length of the array pointed to by @waitchks.
+	 */
 	__u32 num_waitchks;
+
+	/**
+	 * @waitchk_mask:
+	 *
+	 * Bitmask of valid wait checks.
+	 */
 	__u32 waitchk_mask;
+
+	/**
+	 * @timeout:
+	 *
+	 * Timeout, in milliseconds, before this job is cancelled.
+	 */
 	__u32 timeout;
+
+	/**
+	 * @syncpts:
+	 *
+	 * A pointer to an array of &struct drm_tegra_syncpt structures that
+	 * specify the syncpoint operations performed as part of this job.
+	 * The number of elements in the array must be equal to the value
+	 * given by @num_syncpts.
+	 */
 	__u64 syncpts;
+
+	/**
+	 * @cmdbufs:
+	 *
+	 * A pointer to an array of &struct drm_tegra_cmdbuf structures that
+	 * define the command buffers to execute as part of this job. The
+	 * number of elements in the array must be equal to the value given
+	 * by @num_syncpts.
+	 */
 	__u64 cmdbufs;
+
+	/**
+	 * @relocs:
+	 *
+	 * A pointer to an array of &struct drm_tegra_reloc structures that
+	 * specify the relocations that need to be performed before executing
+	 * this job. The number of elements in the array must be equal to the
+	 * value given by @num_relocs.
+	 */
 	__u64 relocs;
+
+	/**
+	 * @waitchks:
+	 *
+	 * A pointer to an array of &struct drm_tegra_waitchk structures that
+	 * specify the wait checks to be performed while executing this job.
+	 * The number of elements in the array must be equal to the value
+	 * given by @num_waitchks.
+	 */
 	__u64 waitchks;
-	__u32 fence;		/* Return value */
 
-	__u32 reserved[5];	/* future expansion */
+	/**
+	 * @fence:
+	 *
+	 * The threshold of the syncpoint associated with this job after it
+	 * has been completed. Set by the kernel upon successful completion of
+	 * the IOCTL. This can be used with the DRM_TEGRA_SYNCPT_WAIT IOCTL to
+	 * wait for this job to be finished.
+	 */
+	__u32 fence;
+
+	/**
+	 * @reserved:
+	 *
+	 * This field is reserved for future use. Must be 0.
+	 */
+	__u32 reserved[5];
 };
 
 #define DRM_TEGRA_GEM_TILING_MODE_PITCH 0
 #define DRM_TEGRA_GEM_TILING_MODE_TILED 1
 #define DRM_TEGRA_GEM_TILING_MODE_BLOCK 2
 
+/**
+ * struct drm_tegra_gem_set_tiling - parameters for the set tiling IOCTL
+ */
 struct drm_tegra_gem_set_tiling {
-	/* input */
+	/**
+	 * @handle:
+	 *
+	 * Handle to the GEM object for which to set the tiling parameters.
+	 */
 	__u32 handle;
+
+	/**
+	 * @mode:
+	 *
+	 * The tiling mode to set. Must be one of:
+	 *
+	 * DRM_TEGRA_GEM_TILING_MODE_PITCH
+	 *   pitch linear format
+	 *
+	 * DRM_TEGRA_GEM_TILING_MODE_TILED
+	 *   16x16 tiling format
+	 *
+	 * DRM_TEGRA_GEM_TILING_MODE_BLOCK
+	 *   16Bx2 tiling format
+	 */
 	__u32 mode;
+
+	/**
+	 * @value:
+	 *
+	 * The value to set for the tiling mode parameter.
+	 */
 	__u32 value;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
 };
 
+/**
+ * struct drm_tegra_gem_get_tiling - parameters for the get tiling IOCTL
+ */
 struct drm_tegra_gem_get_tiling {
-	/* input */
+	/**
+	 * @handle:
+	 *
+	 * Handle to the GEM object for which to query the tiling parameters.
+	 */
 	__u32 handle;
-	/* output */
+
+	/**
+	 * @mode:
+	 *
+	 * The tiling mode currently associated with the GEM object. Set by
+	 * the kernel upon successful completion of the IOCTL.
+	 */
 	__u32 mode;
+
+	/**
+	 * @value:
+	 *
+	 * The tiling mode parameter currently associated with the GEM object.
+	 * Set by the kernel upon successful completion of the IOCTL.
+	 */
 	__u32 value;
+
+	/**
+	 * @pad:
+	 *
+	 * Structure padding that may be used in the future. Must be 0.
+	 */
 	__u32 pad;
 };
 
 #define DRM_TEGRA_GEM_BOTTOM_UP		(1 << 0)
 #define DRM_TEGRA_GEM_FLAGS		(DRM_TEGRA_GEM_BOTTOM_UP)
 
+/**
+ * struct drm_tegra_gem_set_flags - parameters for the set flags IOCTL
+ */
 struct drm_tegra_gem_set_flags {
-	/* input */
+	/**
+	 * @handle:
+	 *
+	 * Handle to the GEM object for which to set the flags.
+	 */
 	__u32 handle;
-	/* output */
+
+	/**
+	 * @flags:
+	 *
+	 * The flags to set for the GEM object.
+	 */
 	__u32 flags;
 };
 
+/**
+ * struct drm_tegra_gem_get_flags - parameters for the get flags IOCTL
+ */
 struct drm_tegra_gem_get_flags {
-	/* input */
+	/**
+	 * @handle:
+	 *
+	 * Handle to the GEM object for which to query the flags.
+	 */
 	__u32 handle;
-	/* output */
+
+	/**
+	 * @flags:
+	 *
+	 * The flags currently associated with the GEM object. Set by the
+	 * kernel upon successful completion of the IOCTL.
+	 */
 	__u32 flags;
 };
 
@@ -193,7 +665,7 @@ struct drm_tegra_gem_get_flags {
 #define DRM_IOCTL_TEGRA_SYNCPT_INCR DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_INCR, struct drm_tegra_syncpt_incr)
 #define DRM_IOCTL_TEGRA_SYNCPT_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_WAIT, struct drm_tegra_syncpt_wait)
 #define DRM_IOCTL_TEGRA_OPEN_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_OPEN_CHANNEL, struct drm_tegra_open_channel)
-#define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_open_channel)
+#define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_close_channel)
 #define DRM_IOCTL_TEGRA_GET_SYNCPT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT, struct drm_tegra_get_syncpt)
 #define DRM_IOCTL_TEGRA_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SUBMIT, struct drm_tegra_submit)
 #define DRM_IOCTL_TEGRA_GET_SYNCPT_BASE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT_BASE, struct drm_tegra_get_syncpt_base)
diff --git a/include/drm-uapi/vc4_drm.h b/include/drm-uapi/vc4_drm.h
index 4117117b4204..31f50de39acb 100644
--- a/include/drm-uapi/vc4_drm.h
+++ b/include/drm-uapi/vc4_drm.h
@@ -183,10 +183,17 @@ struct drm_vc4_submit_cl {
 	/* ID of the perfmon to attach to this job. 0 means no perfmon. */
 	__u32 perfmonid;
 
-	/* Unused field to align this struct on 64 bits. Must be set to 0.
-	 * If one ever needs to add an u32 field to this struct, this field
-	 * can be used.
+	/* Syncobj handle to wait on. If set, processing of this render job
+	 * will not start until the syncobj is signaled. 0 means ignore.
 	 */
+	__u32 in_sync;
+
+	/* Syncobj handle to export fence to. If set, the fence in the syncobj
+	 * will be replaced with a fence that signals upon completion of this
+	 * render job. 0 means ignore.
+	 */
+	__u32 out_sync;
+
 	__u32 pad2;
 };
 
diff --git a/include/drm-uapi/virtgpu_drm.h b/include/drm-uapi/virtgpu_drm.h
index 91a31ffed828..9a781f0611df 100644
--- a/include/drm-uapi/virtgpu_drm.h
+++ b/include/drm-uapi/virtgpu_drm.h
@@ -63,6 +63,7 @@ struct drm_virtgpu_execbuffer {
 };
 
 #define VIRTGPU_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */
+#define VIRTGPU_PARAM_CAPSET_QUERY_FIX 2 /* do we have the capset fix */
 
 struct drm_virtgpu_getparam {
 	__u64 param;
-- 
2.17.1

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [PATH i-g-t 2/2] tests: add slice power programming test
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
@ 2018-09-05 14:25   ` Tvrtko Ursulin
  -1 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-05 14:25 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

Verifies that the kernel programs slices correctly based by reading
the value of PWR_CLK_STATE register or MI_SET_PREDICATE on platforms
before Cannonlake.

v2: Add subslice tests (Lionel)
    Use MI_SET_PREDICATE for further verification when available (Lionel)

v3: Rename to gem_ctx_rpcs (Lionel)

v4: Update kernel API (Lionel)
    Add 0 value test (Lionel)
    Exercise invalid values (Lionel)

v5: Add perf tests (Lionel)

v6: Add new sysfs entry tests (Lionel)

v7: Test rsvd fields
    Update for kernel series changes

v8: Drop test_no_sseu_support() test (Kelvin)
    Drop drm_intel_*() apis (Chris)

v9: by Chris:
    Drop all do_ioctl/do_ioctl_err()
    Use gem_context_[gs]et_param()
    Use gem_read() instead of mapping memory
    by Lionel:
    Test dynamic sseu on/off more

Tvrtko Ursulin:

v10:
 * Various style tweaks and refactorings.
 * New test coverage.

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tests/Makefile.am      |    1 +
 tests/Makefile.sources |    1 +
 tests/gem_ctx_param.c  |    4 +-
 tests/gem_ctx_sseu.c   | 1040 ++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |    7 +
 5 files changed, 1052 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_sseu.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index ee5a7c5e83b8..6b67bd2cc17a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -107,6 +107,7 @@ gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
+gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
 gem_fence_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index c84933f1d971..f8f2c8d67d72 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -56,6 +56,7 @@ TESTS_progs = \
 	gem_ctx_exec \
 	gem_ctx_isolation \
 	gem_ctx_param \
+	gem_ctx_sseu \
 	gem_ctx_switch \
 	gem_ctx_thrash \
 	gem_double_irq_loop \
diff --git a/tests/gem_ctx_param.c b/tests/gem_ctx_param.c
index c46fd709b0d7..af1afeaa2f2f 100644
--- a/tests/gem_ctx_param.c
+++ b/tests/gem_ctx_param.c
@@ -294,11 +294,13 @@ igt_main
 			set_priority(fd);
 	}
 
+	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
+
 	/* NOTE: This testcase intentionally tests for the next free parameter
 	 * to catch ABI extensions. Don't "fix" this testcase without adding all
 	 * the tests for the new param first.
 	 */
-	arg.param = I915_CONTEXT_PARAM_PRIORITY + 1;
+	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
 
 	igt_subtest("invalid-param-get") {
 		arg.ctx_id = ctx;
diff --git a/tests/gem_ctx_sseu.c b/tests/gem_ctx_sseu.c
new file mode 100644
index 000000000000..1f816818e3de
--- /dev/null
+++ b/tests/gem_ctx_sseu.c
@@ -0,0 +1,1040 @@
+/*
+ * Copyright © 2017-2018 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.
+ *
+ * Authors:
+ *    Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/wait.h>
+
+#include "igt_dummyload.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+#include "ioctl_wrappers.h"
+
+IGT_TEST_DESCRIPTION("Test context render powergating programming.");
+
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+
+#define MI_SET_PREDICATE      (0x1 << 23)
+#define  MI_SET_PREDICATE_NOOP_NEVER         (0)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_SET   (2)
+#define  MI_SET_PREDICATE_NOOP_RESULT_CLEAR  (3)
+#define  MI_SET_PREDICATE_NOOP_RESULT_SET    (4)
+#define  MI_SET_PREDICATE_1_SLICES           (5)
+#define  MI_SET_PREDICATE_2_SLICES           (6)
+#define  MI_SET_PREDICATE_3_SLICES           (7)
+
+#define GEN8_R_PWR_CLK_STATE		0x20C8
+#define   GEN8_RPCS_ENABLE		(1 << 31)
+#define   GEN8_RPCS_S_CNT_ENABLE	(1 << 18)
+#define   GEN8_RPCS_S_CNT_SHIFT		15
+#define   GEN8_RPCS_S_CNT_MASK		(0x7 << GEN8_RPCS_S_CNT_SHIFT)
+#define   GEN11_RPCS_S_CNT_SHIFT	12
+#define   GEN11_RPCS_S_CNT_MASK		(0x3f << GEN11_RPCS_S_CNT_SHIFT)
+#define   GEN8_RPCS_SS_CNT_ENABLE	(1 << 11)
+#define   GEN8_RPCS_SS_CNT_SHIFT	8
+#define   GEN8_RPCS_SS_CNT_MASK		(0x7 << GEN8_RPCS_SS_CNT_SHIFT)
+#define   GEN8_RPCS_EU_MAX_SHIFT	4
+#define   GEN8_RPCS_EU_MAX_MASK		(0xf << GEN8_RPCS_EU_MAX_SHIFT)
+#define   GEN8_RPCS_EU_MIN_SHIFT	0
+#define   GEN8_RPCS_EU_MIN_MASK		(0xf << GEN8_RPCS_EU_MIN_SHIFT)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+static unsigned int __intel_gen__, __intel_devid__;
+static uint64_t __slice_mask__, __subslice_mask__;
+static unsigned int __slice_count__, __subslice_count__;
+
+static uint64_t mask_minus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if ((1ULL << i) & mask)
+			return mask & ~(1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_plus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if (((1ULL << i) & mask) == 0)
+			return mask | (1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_minus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_minus_one(mask);
+
+	return mask;
+}
+
+static uint64_t mask_plus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_plus_one(mask);
+
+	return mask;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+		struct drm_i915_gem_relocation_entry *reloc,
+		uint32_t gem_handle, uint32_t delta, /* in bytes */
+		uint32_t offset, /* in dwords */
+		uint32_t read_domains, uint32_t write_domains)
+{
+	reloc->target_handle = gem_handle;
+	reloc->delta = delta;
+	reloc->offset = offset * sizeof(uint32_t);
+	reloc->presumed_offset = 0;
+	reloc->read_domains = read_domains;
+	reloc->write_domain = write_domains;
+
+	*batch++ = delta;
+	*batch++ = 0;
+
+	return batch;
+}
+
+
+static uint32_t
+read_rpcs_reg(int fd, uint32_t ctx, uint32_t expected_slices, igt_spin_t *spin)
+{
+	struct drm_i915_gem_execbuffer2 execbuf = { };
+	struct drm_i915_gem_relocation_entry relocs[2];
+	struct drm_i915_gem_exec_object2 obj[2];
+	uint32_t *batch, *b, data[2];
+	unsigned int n_relocs = 0;
+	uint32_t rpcs;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[1].handle = gem_create(fd, 4096);
+
+	batch = b =
+	      gem_mmap__cpu(fd, obj[1].handle, 0, 4096, PROT_READ | PROT_WRITE);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) |
+		       (MI_SET_PREDICATE_1_SLICES + expected_slices - 1);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = RCS_TIMESTAMP;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    0, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = GEN8_R_PWR_CLK_STATE;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    4, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) | MI_SET_PREDICATE_NOOP_NEVER;
+
+	*b++ = MI_BATCH_BUFFER_END;
+
+	gem_munmap(batch, 4096);
+
+	obj[1].relocation_count = n_relocs;
+	obj[1].relocs_ptr = to_user_pointer(relocs);
+
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+	i915_execbuffer2_set_context_id(execbuf, ctx);
+
+	gem_execbuf(fd, &execbuf);
+
+	if (spin)
+		igt_spin_batch_end(spin);
+
+	gem_read(fd, obj[0].handle, 0, data, sizeof(data));
+
+	rpcs = data[1];
+
+	igt_debug("rcs_timestamp=0x%x rpcs=0x%x\n", data[0], data[1]);
+
+	gem_close(fd, obj[0].handle);
+	gem_close(fd, obj[1].handle);
+
+	return rpcs;
+}
+
+typedef uint32_t (*read_slice_count_f)(int fd, uint32_t ctx, uint32_t expected,
+				       igt_spin_t *spin);
+
+static read_slice_count_f __read_slice_count;
+
+static uint32_t
+read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count)
+{
+	return __read_slice_count(fd, ctx, expected_slice_count, NULL);
+}
+
+static uint32_t
+gen8_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		      igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN8_RPCS_S_CNT_MASK) >> GEN8_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+gen11_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		       igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN11_RPCS_S_CNT_MASK) >> GEN11_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+read_subslice_count(int fd, uint32_t ctx)
+{
+	return (read_rpcs_reg(fd, ctx, 0, NULL) & GEN8_RPCS_SS_CNT_MASK) >>
+	       GEN8_RPCS_SS_CNT_SHIFT;
+}
+
+static bool
+kernel_has_per_context_sseu_support(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .value = to_user_pointer(&sseu) };
+
+	return __gem_context_get_param(fd, &arg) == 0;
+}
+
+static void
+context_get_sseu_masks(int fd, uint32_t ctx,
+		       uint64_t *slice_mask,
+		       uint64_t *subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+
+	if (slice_mask)
+		*slice_mask = sseu.slice_mask;
+
+	if (subslice_mask)
+		*subslice_mask = sseu.subslice_mask;
+}
+
+static void
+context_set_slice_mask(int fd, uint32_t ctx, uint64_t slice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.slice_mask = slice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+static void
+context_set_subslice_mask(int fd, uint32_t ctx, uint64_t subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.subslice_mask = subslice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+/*
+ * Verify that we can program the slice count.
+ */
+static void
+test_slice_pg(int fd, uint32_t pg_slice_count)
+{
+	uint64_t pg_slice_mask = mask_minus(__slice_mask__, pg_slice_count);
+	unsigned int slice_count = __slice_count__ - pg_slice_count;
+	uint64_t slice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(slice_count, __builtin_popcount(pg_slice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_slice_mask(fd, ctx, pg_slice_mask);
+	context_get_sseu_masks(fd, ctx, &slice_mask, NULL);
+	igt_assert_eq(pg_slice_mask, slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx, __slice_count__), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), slice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+/*
+ * Verify that we can program the subslice count.
+ */
+static void
+test_subslice_pg(int fd, int pg_subslice_count)
+{
+	uint64_t pg_subslice_mask =
+		mask_minus(__subslice_mask__, pg_subslice_count);
+	unsigned int subslice_count = __subslice_count__ - pg_subslice_count;
+	uint64_t subslice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(subslice_count, __builtin_popcount(pg_subslice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_subslice_mask(fd, ctx, pg_subslice_mask);
+	context_get_sseu_masks(fd, ctx, NULL, &subslice_mask);
+	igt_assert_eq(pg_subslice_mask, subslice_mask);
+
+	igt_assert_eq(read_subslice_count(fd, ctx), subslice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_engine(int fd, unsigned int class, unsigned int instance)
+{
+	int pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+
+	if (pmu >= 0)
+		close(pmu);
+
+	return pmu >= 0;
+}
+
+static bool
+engine_supports_sseu(int fd, unsigned int class, unsigned int instance)
+{
+	return __intel_gen__ >= 8 &&
+	       class == I915_ENGINE_CLASS_RENDER && instance == 0 ?
+	       true : false;
+}
+
+/*
+ * Verify that invalid engines are rejected and valid ones are accepted.
+ */
+static void test_engines(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	unsigned int class, instance;
+	int last_with_engines;
+
+	/* get_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+	sseu.class = I915_ENGINE_CLASS_RENDER;
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_get_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert_eq(ret, 0);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	/*
+	 * Get some proper values before trying to reprogram them onto
+	 * an invalid engine.
+	 */
+	sseu.class = 0;
+	sseu.instance = 0;
+	gem_context_get_param(fd, &arg);
+
+	/* set_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_set_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				if (engine_supports_sseu(fd, class, instance))
+					igt_assert_eq(ret, 0);
+				else
+					igt_assert_eq(ret, -ENODEV);
+
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid arguments are rejected.
+ */
+static void
+test_invalid_args(int fd)
+{
+	struct drm_i915_gem_context_param_sseu default_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		};
+	unsigned int sz;
+
+	/* get param */
+
+	/* Invalid size. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	/* Query size. */
+	arg.size = 0;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	sz = arg.size;
+
+	/* Bad pointer. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = to_user_pointer(&sseu);
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&default_sseu);
+	gem_context_get_param(fd, &arg);
+
+	/* set param */
+
+	/* Invalid sizes. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	arg.size = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	arg.size = sz;
+
+	/* Bad pointer. */
+	arg.value = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = to_user_pointer(&sseu);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid SSEU values are rejected.
+ */
+static void
+test_invalid_sseu(int fd)
+{
+	struct drm_i915_gem_context_param_sseu default_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		};
+	unsigned int i;
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&default_sseu);
+	gem_context_get_param(fd, &arg);
+
+	arg.value = to_user_pointer(&sseu);
+
+	/* Try all slice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __slice_count__); i++) {
+		sseu.slice_mask = mask_plus(__slice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 slices. */
+	sseu.slice_mask = 0;
+	igt_assert_eq(-EINVAL, __gem_context_set_param(fd, &arg));
+
+	/* Try all subslice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __subslice_count__); i++) {
+		sseu.subslice_mask = mask_plus(__subslice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 subslices. */
+	sseu.subslice_mask = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try number of EUs superior to the max available. */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try to program 0 max EUs. */
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Min > max */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = sseu.max_eus_per_subslice;
+	sseu.max_eus_per_subslice = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	if (__intel_gen__ != 11)
+		goto out;
+
+	/* Subset of subslices but slice mask greater than one. */
+	if (__slice_count__ > 1) {
+		sseu = default_sseu;
+		sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Odd subslices above four. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* More than half subslices with one slice. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 - 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+out:
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/* Verify that the kernel returns a correct error value on Gen < 8. */
+static void
+init_contexts(int fd, uint32_t *ctx, unsigned int num,
+	      uint64_t mask0, uint64_t mask1)
+{
+	unsigned int i;
+
+	igt_assert_eq(num, 2);
+
+	for (i = 0; i < num; i++)
+		ctx[i] = gem_context_create(fd);
+
+	context_set_slice_mask(fd, ctx[0], mask0);
+	context_set_slice_mask(fd, ctx[1], mask1);
+}
+
+/*
+ * Verify that powergating settings are put on hold while i915/perf is
+ * active.
+ */
+static void
+test_perf_oa(int fd)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, 1, /* test metric */
+		DRM_I915_PERF_PROP_OA_FORMAT, I915_OA_FORMAT_A32u40_A4u32_B8_C8,
+		DRM_I915_PERF_PROP_OA_EXPONENT, 20,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+		I915_PERF_FLAG_FD_NONBLOCK,
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int slice_count = __slice_count__ - 1;
+	uint32_t ctx[2];
+	unsigned int i;
+	int perf_fd;
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	/*
+	 * Now open i915/perf and verify that all contexts have been
+	 * reconfigured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1], slice_count),
+				      0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__), 0);
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+
+	/*
+	 * Open i915/perf first and verify that all contexts created
+	 * afterward are configured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Check the device's default values, despite setting
+	 * otherwise.
+	 */
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1],
+						       slice_count), 0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	struct igt_spin_factory opts = {
+		.ctx = ctx,
+		.engine = flags,
+	};
+
+	if (gem_can_store_dword(fd, flags))
+		opts.flags |= IGT_SPIN_POLL_RUN;
+
+	return __igt_spin_batch_factory(fd, &opts);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!READ_ONCE(*spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static uint32_t
+read_slice_count_busy(int fd, uint32_t context, uint32_t expected,
+		      igt_spin_t *spin)
+{
+	return __read_slice_count(fd, context, expected, spin);
+}
+
+#define TEST_IDLE (1)
+#define TEST_BUSY (2)
+
+/*
+ * Test context re-configuration with either idle or busy contexts.
+ */
+static void
+test_dynamic(int fd, unsigned int flags)
+{
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int pg_slice_count = __slice_count__ - 1;
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	igt_spin_t *spin = NULL;
+
+	gem_context_get_param(fd, &arg);
+
+	/* First set the default mask */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      __slice_count__);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* Then set a powergated configuration */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      pg_slice_count);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* Put the device's default back again */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      __slice_count__);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* One last powergated config for the road... */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      pg_slice_count);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+static void test_unsupported_device(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -ENODEV);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+static bool
+platform_has_per_context_sseu_support(void)
+{
+	return __intel_gen__ >= 8;
+}
+
+igt_main
+{
+	unsigned int max_slices = 3, max_subslices = 3;
+	unsigned int i;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		__intel_devid__ = intel_get_drm_devid(fd);
+		__intel_gen__ = intel_gen(__intel_devid__);
+
+		igt_require(kernel_has_per_context_sseu_support(fd));
+
+		if (__intel_gen__ >= 11)
+			__read_slice_count = gen11_read_slice_count;
+		else
+			__read_slice_count = gen8_read_slice_count;
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			igt_require(!platform_has_per_context_sseu_support());
+		}
+
+		igt_subtest("unsupported-device")
+			test_unsupported_device(fd);
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			drm_i915_getparam_t gp;
+
+			igt_require(platform_has_per_context_sseu_support());
+
+			gp.param = I915_PARAM_SLICE_MASK;
+			gp.value = (int *) &__slice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__slice_count__ = __builtin_popcount(__slice_mask__);
+
+			gp.param = I915_PARAM_SUBSLICE_MASK;
+			gp.value = (int *) &__subslice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__subslice_count__ =
+				__builtin_popcount(__subslice_mask__);
+		}
+
+		igt_subtest("invalid-args")
+			test_invalid_args(fd);
+
+		igt_subtest("invalid-sseu")
+			test_invalid_sseu(fd);
+
+		igt_subtest("engines")
+			test_engines(fd);
+
+		for (i = 1; i < max_slices; i++) {
+			igt_subtest_f("slice-pg-%i", i) {
+				igt_require(__slice_count__ > i);
+
+				test_slice_pg(fd, i);
+			}
+		}
+
+		for (i = 1; i < max_subslices; i++) {
+			igt_subtest_f("subslice-pg-%i", i) {
+				igt_require(__subslice_count__ >= 2);
+
+				/*
+				 * Only available on some Atom platforms and
+				 * Gen10+.
+				 */
+				igt_require(IS_BROXTON(__intel_devid__) ||
+					    IS_GEMINILAKE(__intel_devid__) ||
+					    __intel_gen__ >= 10);
+
+				test_subslice_pg(fd, i);
+			}
+		}
+
+		igt_subtest("sseu-perf-oa") {
+			igt_require(__slice_count__ > 1);
+
+			test_perf_oa(fd);
+		}
+
+		igt_subtest("dynamic") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, 0);
+		}
+
+		igt_subtest("dynamic-busy") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, TEST_BUSY);
+		}
+
+		igt_subtest("dynamic-idle") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, TEST_IDLE);
+		}
+	}
+
+	igt_fixture {
+		close(fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 17deb945ec95..9319fa4e19b4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -249,6 +249,13 @@ foreach prog : test_progs
 		   install : true)
 endforeach
 
+test_executables += executable('gem_ctx_sseu', 'gem_ctx_sseu.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_ctx_sseu'
+
 test_executables += executable('gem_eio', 'gem_eio.c',
 	   dependencies : test_deps + [ realtime ],
 	   install_dir : libexecdir,
-- 
2.17.1

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

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

* [igt-dev] [PATH i-g-t 2/2] tests: add slice power programming test
@ 2018-09-05 14:25   ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-05 14:25 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

Verifies that the kernel programs slices correctly based by reading
the value of PWR_CLK_STATE register or MI_SET_PREDICATE on platforms
before Cannonlake.

v2: Add subslice tests (Lionel)
    Use MI_SET_PREDICATE for further verification when available (Lionel)

v3: Rename to gem_ctx_rpcs (Lionel)

v4: Update kernel API (Lionel)
    Add 0 value test (Lionel)
    Exercise invalid values (Lionel)

v5: Add perf tests (Lionel)

v6: Add new sysfs entry tests (Lionel)

v7: Test rsvd fields
    Update for kernel series changes

v8: Drop test_no_sseu_support() test (Kelvin)
    Drop drm_intel_*() apis (Chris)

v9: by Chris:
    Drop all do_ioctl/do_ioctl_err()
    Use gem_context_[gs]et_param()
    Use gem_read() instead of mapping memory
    by Lionel:
    Test dynamic sseu on/off more

Tvrtko Ursulin:

v10:
 * Various style tweaks and refactorings.
 * New test coverage.

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tests/Makefile.am      |    1 +
 tests/Makefile.sources |    1 +
 tests/gem_ctx_param.c  |    4 +-
 tests/gem_ctx_sseu.c   | 1040 ++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |    7 +
 5 files changed, 1052 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_sseu.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index ee5a7c5e83b8..6b67bd2cc17a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -107,6 +107,7 @@ gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
+gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
 gem_fence_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index c84933f1d971..f8f2c8d67d72 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -56,6 +56,7 @@ TESTS_progs = \
 	gem_ctx_exec \
 	gem_ctx_isolation \
 	gem_ctx_param \
+	gem_ctx_sseu \
 	gem_ctx_switch \
 	gem_ctx_thrash \
 	gem_double_irq_loop \
diff --git a/tests/gem_ctx_param.c b/tests/gem_ctx_param.c
index c46fd709b0d7..af1afeaa2f2f 100644
--- a/tests/gem_ctx_param.c
+++ b/tests/gem_ctx_param.c
@@ -294,11 +294,13 @@ igt_main
 			set_priority(fd);
 	}
 
+	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
+
 	/* NOTE: This testcase intentionally tests for the next free parameter
 	 * to catch ABI extensions. Don't "fix" this testcase without adding all
 	 * the tests for the new param first.
 	 */
-	arg.param = I915_CONTEXT_PARAM_PRIORITY + 1;
+	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
 
 	igt_subtest("invalid-param-get") {
 		arg.ctx_id = ctx;
diff --git a/tests/gem_ctx_sseu.c b/tests/gem_ctx_sseu.c
new file mode 100644
index 000000000000..1f816818e3de
--- /dev/null
+++ b/tests/gem_ctx_sseu.c
@@ -0,0 +1,1040 @@
+/*
+ * Copyright © 2017-2018 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.
+ *
+ * Authors:
+ *    Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/wait.h>
+
+#include "igt_dummyload.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+#include "ioctl_wrappers.h"
+
+IGT_TEST_DESCRIPTION("Test context render powergating programming.");
+
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+
+#define MI_SET_PREDICATE      (0x1 << 23)
+#define  MI_SET_PREDICATE_NOOP_NEVER         (0)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_SET   (2)
+#define  MI_SET_PREDICATE_NOOP_RESULT_CLEAR  (3)
+#define  MI_SET_PREDICATE_NOOP_RESULT_SET    (4)
+#define  MI_SET_PREDICATE_1_SLICES           (5)
+#define  MI_SET_PREDICATE_2_SLICES           (6)
+#define  MI_SET_PREDICATE_3_SLICES           (7)
+
+#define GEN8_R_PWR_CLK_STATE		0x20C8
+#define   GEN8_RPCS_ENABLE		(1 << 31)
+#define   GEN8_RPCS_S_CNT_ENABLE	(1 << 18)
+#define   GEN8_RPCS_S_CNT_SHIFT		15
+#define   GEN8_RPCS_S_CNT_MASK		(0x7 << GEN8_RPCS_S_CNT_SHIFT)
+#define   GEN11_RPCS_S_CNT_SHIFT	12
+#define   GEN11_RPCS_S_CNT_MASK		(0x3f << GEN11_RPCS_S_CNT_SHIFT)
+#define   GEN8_RPCS_SS_CNT_ENABLE	(1 << 11)
+#define   GEN8_RPCS_SS_CNT_SHIFT	8
+#define   GEN8_RPCS_SS_CNT_MASK		(0x7 << GEN8_RPCS_SS_CNT_SHIFT)
+#define   GEN8_RPCS_EU_MAX_SHIFT	4
+#define   GEN8_RPCS_EU_MAX_MASK		(0xf << GEN8_RPCS_EU_MAX_SHIFT)
+#define   GEN8_RPCS_EU_MIN_SHIFT	0
+#define   GEN8_RPCS_EU_MIN_MASK		(0xf << GEN8_RPCS_EU_MIN_SHIFT)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+static unsigned int __intel_gen__, __intel_devid__;
+static uint64_t __slice_mask__, __subslice_mask__;
+static unsigned int __slice_count__, __subslice_count__;
+
+static uint64_t mask_minus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if ((1ULL << i) & mask)
+			return mask & ~(1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_plus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if (((1ULL << i) & mask) == 0)
+			return mask | (1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_minus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_minus_one(mask);
+
+	return mask;
+}
+
+static uint64_t mask_plus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_plus_one(mask);
+
+	return mask;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+		struct drm_i915_gem_relocation_entry *reloc,
+		uint32_t gem_handle, uint32_t delta, /* in bytes */
+		uint32_t offset, /* in dwords */
+		uint32_t read_domains, uint32_t write_domains)
+{
+	reloc->target_handle = gem_handle;
+	reloc->delta = delta;
+	reloc->offset = offset * sizeof(uint32_t);
+	reloc->presumed_offset = 0;
+	reloc->read_domains = read_domains;
+	reloc->write_domain = write_domains;
+
+	*batch++ = delta;
+	*batch++ = 0;
+
+	return batch;
+}
+
+
+static uint32_t
+read_rpcs_reg(int fd, uint32_t ctx, uint32_t expected_slices, igt_spin_t *spin)
+{
+	struct drm_i915_gem_execbuffer2 execbuf = { };
+	struct drm_i915_gem_relocation_entry relocs[2];
+	struct drm_i915_gem_exec_object2 obj[2];
+	uint32_t *batch, *b, data[2];
+	unsigned int n_relocs = 0;
+	uint32_t rpcs;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[1].handle = gem_create(fd, 4096);
+
+	batch = b =
+	      gem_mmap__cpu(fd, obj[1].handle, 0, 4096, PROT_READ | PROT_WRITE);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) |
+		       (MI_SET_PREDICATE_1_SLICES + expected_slices - 1);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = RCS_TIMESTAMP;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    0, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = GEN8_R_PWR_CLK_STATE;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    4, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) | MI_SET_PREDICATE_NOOP_NEVER;
+
+	*b++ = MI_BATCH_BUFFER_END;
+
+	gem_munmap(batch, 4096);
+
+	obj[1].relocation_count = n_relocs;
+	obj[1].relocs_ptr = to_user_pointer(relocs);
+
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+	i915_execbuffer2_set_context_id(execbuf, ctx);
+
+	gem_execbuf(fd, &execbuf);
+
+	if (spin)
+		igt_spin_batch_end(spin);
+
+	gem_read(fd, obj[0].handle, 0, data, sizeof(data));
+
+	rpcs = data[1];
+
+	igt_debug("rcs_timestamp=0x%x rpcs=0x%x\n", data[0], data[1]);
+
+	gem_close(fd, obj[0].handle);
+	gem_close(fd, obj[1].handle);
+
+	return rpcs;
+}
+
+typedef uint32_t (*read_slice_count_f)(int fd, uint32_t ctx, uint32_t expected,
+				       igt_spin_t *spin);
+
+static read_slice_count_f __read_slice_count;
+
+static uint32_t
+read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count)
+{
+	return __read_slice_count(fd, ctx, expected_slice_count, NULL);
+}
+
+static uint32_t
+gen8_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		      igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN8_RPCS_S_CNT_MASK) >> GEN8_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+gen11_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		       igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN11_RPCS_S_CNT_MASK) >> GEN11_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+read_subslice_count(int fd, uint32_t ctx)
+{
+	return (read_rpcs_reg(fd, ctx, 0, NULL) & GEN8_RPCS_SS_CNT_MASK) >>
+	       GEN8_RPCS_SS_CNT_SHIFT;
+}
+
+static bool
+kernel_has_per_context_sseu_support(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .value = to_user_pointer(&sseu) };
+
+	return __gem_context_get_param(fd, &arg) == 0;
+}
+
+static void
+context_get_sseu_masks(int fd, uint32_t ctx,
+		       uint64_t *slice_mask,
+		       uint64_t *subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+
+	if (slice_mask)
+		*slice_mask = sseu.slice_mask;
+
+	if (subslice_mask)
+		*subslice_mask = sseu.subslice_mask;
+}
+
+static void
+context_set_slice_mask(int fd, uint32_t ctx, uint64_t slice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.slice_mask = slice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+static void
+context_set_subslice_mask(int fd, uint32_t ctx, uint64_t subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.subslice_mask = subslice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+/*
+ * Verify that we can program the slice count.
+ */
+static void
+test_slice_pg(int fd, uint32_t pg_slice_count)
+{
+	uint64_t pg_slice_mask = mask_minus(__slice_mask__, pg_slice_count);
+	unsigned int slice_count = __slice_count__ - pg_slice_count;
+	uint64_t slice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(slice_count, __builtin_popcount(pg_slice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_slice_mask(fd, ctx, pg_slice_mask);
+	context_get_sseu_masks(fd, ctx, &slice_mask, NULL);
+	igt_assert_eq(pg_slice_mask, slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx, __slice_count__), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), slice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+/*
+ * Verify that we can program the subslice count.
+ */
+static void
+test_subslice_pg(int fd, int pg_subslice_count)
+{
+	uint64_t pg_subslice_mask =
+		mask_minus(__subslice_mask__, pg_subslice_count);
+	unsigned int subslice_count = __subslice_count__ - pg_subslice_count;
+	uint64_t subslice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(subslice_count, __builtin_popcount(pg_subslice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_subslice_mask(fd, ctx, pg_subslice_mask);
+	context_get_sseu_masks(fd, ctx, NULL, &subslice_mask);
+	igt_assert_eq(pg_subslice_mask, subslice_mask);
+
+	igt_assert_eq(read_subslice_count(fd, ctx), subslice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_engine(int fd, unsigned int class, unsigned int instance)
+{
+	int pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+
+	if (pmu >= 0)
+		close(pmu);
+
+	return pmu >= 0;
+}
+
+static bool
+engine_supports_sseu(int fd, unsigned int class, unsigned int instance)
+{
+	return __intel_gen__ >= 8 &&
+	       class == I915_ENGINE_CLASS_RENDER && instance == 0 ?
+	       true : false;
+}
+
+/*
+ * Verify that invalid engines are rejected and valid ones are accepted.
+ */
+static void test_engines(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	unsigned int class, instance;
+	int last_with_engines;
+
+	/* get_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+	sseu.class = I915_ENGINE_CLASS_RENDER;
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_get_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert_eq(ret, 0);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	/*
+	 * Get some proper values before trying to reprogram them onto
+	 * an invalid engine.
+	 */
+	sseu.class = 0;
+	sseu.instance = 0;
+	gem_context_get_param(fd, &arg);
+
+	/* set_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_set_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				if (engine_supports_sseu(fd, class, instance))
+					igt_assert_eq(ret, 0);
+				else
+					igt_assert_eq(ret, -ENODEV);
+
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid arguments are rejected.
+ */
+static void
+test_invalid_args(int fd)
+{
+	struct drm_i915_gem_context_param_sseu default_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		};
+	unsigned int sz;
+
+	/* get param */
+
+	/* Invalid size. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	/* Query size. */
+	arg.size = 0;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	sz = arg.size;
+
+	/* Bad pointer. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = to_user_pointer(&sseu);
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&default_sseu);
+	gem_context_get_param(fd, &arg);
+
+	/* set param */
+
+	/* Invalid sizes. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	arg.size = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	arg.size = sz;
+
+	/* Bad pointer. */
+	arg.value = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = to_user_pointer(&sseu);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid SSEU values are rejected.
+ */
+static void
+test_invalid_sseu(int fd)
+{
+	struct drm_i915_gem_context_param_sseu default_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		};
+	unsigned int i;
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&default_sseu);
+	gem_context_get_param(fd, &arg);
+
+	arg.value = to_user_pointer(&sseu);
+
+	/* Try all slice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __slice_count__); i++) {
+		sseu.slice_mask = mask_plus(__slice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 slices. */
+	sseu.slice_mask = 0;
+	igt_assert_eq(-EINVAL, __gem_context_set_param(fd, &arg));
+
+	/* Try all subslice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __subslice_count__); i++) {
+		sseu.subslice_mask = mask_plus(__subslice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 subslices. */
+	sseu.subslice_mask = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try number of EUs superior to the max available. */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try to program 0 max EUs. */
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Min > max */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = sseu.max_eus_per_subslice;
+	sseu.max_eus_per_subslice = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	if (__intel_gen__ != 11)
+		goto out;
+
+	/* Subset of subslices but slice mask greater than one. */
+	if (__slice_count__ > 1) {
+		sseu = default_sseu;
+		sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Odd subslices above four. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* More than half subslices with one slice. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 - 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+out:
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/* Verify that the kernel returns a correct error value on Gen < 8. */
+static void
+init_contexts(int fd, uint32_t *ctx, unsigned int num,
+	      uint64_t mask0, uint64_t mask1)
+{
+	unsigned int i;
+
+	igt_assert_eq(num, 2);
+
+	for (i = 0; i < num; i++)
+		ctx[i] = gem_context_create(fd);
+
+	context_set_slice_mask(fd, ctx[0], mask0);
+	context_set_slice_mask(fd, ctx[1], mask1);
+}
+
+/*
+ * Verify that powergating settings are put on hold while i915/perf is
+ * active.
+ */
+static void
+test_perf_oa(int fd)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, 1, /* test metric */
+		DRM_I915_PERF_PROP_OA_FORMAT, I915_OA_FORMAT_A32u40_A4u32_B8_C8,
+		DRM_I915_PERF_PROP_OA_EXPONENT, 20,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+		I915_PERF_FLAG_FD_NONBLOCK,
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int slice_count = __slice_count__ - 1;
+	uint32_t ctx[2];
+	unsigned int i;
+	int perf_fd;
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	/*
+	 * Now open i915/perf and verify that all contexts have been
+	 * reconfigured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1], slice_count),
+				      0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__), 0);
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+
+	/*
+	 * Open i915/perf first and verify that all contexts created
+	 * afterward are configured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Check the device's default values, despite setting
+	 * otherwise.
+	 */
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1],
+						       slice_count), 0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	struct igt_spin_factory opts = {
+		.ctx = ctx,
+		.engine = flags,
+	};
+
+	if (gem_can_store_dword(fd, flags))
+		opts.flags |= IGT_SPIN_POLL_RUN;
+
+	return __igt_spin_batch_factory(fd, &opts);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!READ_ONCE(*spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static uint32_t
+read_slice_count_busy(int fd, uint32_t context, uint32_t expected,
+		      igt_spin_t *spin)
+{
+	return __read_slice_count(fd, context, expected, spin);
+}
+
+#define TEST_IDLE (1)
+#define TEST_BUSY (2)
+
+/*
+ * Test context re-configuration with either idle or busy contexts.
+ */
+static void
+test_dynamic(int fd, unsigned int flags)
+{
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int pg_slice_count = __slice_count__ - 1;
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	igt_spin_t *spin = NULL;
+
+	gem_context_get_param(fd, &arg);
+
+	/* First set the default mask */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      __slice_count__);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* Then set a powergated configuration */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      pg_slice_count);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* Put the device's default back again */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      __slice_count__);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* One last powergated config for the road... */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      pg_slice_count);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+static void test_unsupported_device(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -ENODEV);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+static bool
+platform_has_per_context_sseu_support(void)
+{
+	return __intel_gen__ >= 8;
+}
+
+igt_main
+{
+	unsigned int max_slices = 3, max_subslices = 3;
+	unsigned int i;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		__intel_devid__ = intel_get_drm_devid(fd);
+		__intel_gen__ = intel_gen(__intel_devid__);
+
+		igt_require(kernel_has_per_context_sseu_support(fd));
+
+		if (__intel_gen__ >= 11)
+			__read_slice_count = gen11_read_slice_count;
+		else
+			__read_slice_count = gen8_read_slice_count;
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			igt_require(!platform_has_per_context_sseu_support());
+		}
+
+		igt_subtest("unsupported-device")
+			test_unsupported_device(fd);
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			drm_i915_getparam_t gp;
+
+			igt_require(platform_has_per_context_sseu_support());
+
+			gp.param = I915_PARAM_SLICE_MASK;
+			gp.value = (int *) &__slice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__slice_count__ = __builtin_popcount(__slice_mask__);
+
+			gp.param = I915_PARAM_SUBSLICE_MASK;
+			gp.value = (int *) &__subslice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__subslice_count__ =
+				__builtin_popcount(__subslice_mask__);
+		}
+
+		igt_subtest("invalid-args")
+			test_invalid_args(fd);
+
+		igt_subtest("invalid-sseu")
+			test_invalid_sseu(fd);
+
+		igt_subtest("engines")
+			test_engines(fd);
+
+		for (i = 1; i < max_slices; i++) {
+			igt_subtest_f("slice-pg-%i", i) {
+				igt_require(__slice_count__ > i);
+
+				test_slice_pg(fd, i);
+			}
+		}
+
+		for (i = 1; i < max_subslices; i++) {
+			igt_subtest_f("subslice-pg-%i", i) {
+				igt_require(__subslice_count__ >= 2);
+
+				/*
+				 * Only available on some Atom platforms and
+				 * Gen10+.
+				 */
+				igt_require(IS_BROXTON(__intel_devid__) ||
+					    IS_GEMINILAKE(__intel_devid__) ||
+					    __intel_gen__ >= 10);
+
+				test_subslice_pg(fd, i);
+			}
+		}
+
+		igt_subtest("sseu-perf-oa") {
+			igt_require(__slice_count__ > 1);
+
+			test_perf_oa(fd);
+		}
+
+		igt_subtest("dynamic") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, 0);
+		}
+
+		igt_subtest("dynamic-busy") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, TEST_BUSY);
+		}
+
+		igt_subtest("dynamic-idle") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, TEST_IDLE);
+		}
+	}
+
+	igt_fixture {
+		close(fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 17deb945ec95..9319fa4e19b4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -249,6 +249,13 @@ foreach prog : test_progs
 		   install : true)
 endforeach
 
+test_executables += executable('gem_ctx_sseu', 'gem_ctx_sseu.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_ctx_sseu'
+
 test_executables += executable('gem_eio', 'gem_eio.c',
 	   dependencies : test_deps + [ realtime ],
 	   install_dir : libexecdir,
-- 
2.17.1

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [igt-dev] ✓ Fi.CI.BAT: success for Per context dynamic (sub)slice power-gating
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
                   ` (2 preceding siblings ...)
  (?)
@ 2018-09-05 16:46 ` Patchwork
  -1 siblings, 0 replies; 55+ messages in thread
From: Patchwork @ 2018-09-05 16:46 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: igt-dev

== Series Details ==

Series: Per context dynamic (sub)slice power-gating
URL   : https://patchwork.freedesktop.org/series/49190/
State : success

== Summary ==

= CI Bug Log - changes from CI_DRM_4772 -> IGTPW_1793 =

== Summary - SUCCESS ==

  No regressions found.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/49190/revisions/1/mbox/

== Known issues ==

  Here are the changes found in IGTPW_1793 that come from known issues:

  === IGT changes ===

    ==== Possible fixes ====

    igt@gem_exec_suspend@basic-s4-devices:
      fi-kbl-7500u:       DMESG-WARN (fdo#107139, fdo#105128) -> PASS

    igt@kms_pipe_crc_basic@read-crc-pipe-b-frame-sequence:
      fi-ilk-650:         DMESG-WARN (fdo#106387) -> PASS

    igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
      fi-byt-clapper:     FAIL (fdo#107362, fdo#103191) -> PASS
      fi-blb-e6850:       INCOMPLETE (fdo#107718) -> PASS

    
  fdo#103191 https://bugs.freedesktop.org/show_bug.cgi?id=103191
  fdo#105128 https://bugs.freedesktop.org/show_bug.cgi?id=105128
  fdo#106387 https://bugs.freedesktop.org/show_bug.cgi?id=106387
  fdo#107139 https://bugs.freedesktop.org/show_bug.cgi?id=107139
  fdo#107362 https://bugs.freedesktop.org/show_bug.cgi?id=107362
  fdo#107718 https://bugs.freedesktop.org/show_bug.cgi?id=107718


== Participating hosts (54 -> 49) ==

  Missing    (5): fi-ctg-p8600 fi-ilk-m540 fi-byt-squawks fi-bsw-cyan fi-hsw-4200u 


== Build changes ==

    * IGT: IGT_4629 -> IGTPW_1793

  CI_DRM_4772: 1351ee8f3aacdb8f4a71cd17a7035556065c59a9 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_1793: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1793/
  IGT_4629: c3b6d69aa3dd2d1a6c1f2e787670a0aef78f2ea5 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools



== Testlist changes ==

+igt@gem_ctx_sseu@dynamic
+igt@gem_ctx_sseu@dynamic-busy
+igt@gem_ctx_sseu@dynamic-idle
+igt@gem_ctx_sseu@engines
+igt@gem_ctx_sseu@invalid-args
+igt@gem_ctx_sseu@invalid-sseu
+igt@gem_ctx_sseu@slice-pg-1
+igt@gem_ctx_sseu@slice-pg-2
+igt@gem_ctx_sseu@sseu-perf-oa
+igt@gem_ctx_sseu@subslice-pg-1
+igt@gem_ctx_sseu@subslice-pg-2
+igt@gem_ctx_sseu@unsupported-device

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1793/issues.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [igt-dev] ✓ Fi.CI.IGT: success for Per context dynamic (sub)slice power-gating
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
                   ` (3 preceding siblings ...)
  (?)
@ 2018-09-05 22:44 ` Patchwork
  -1 siblings, 0 replies; 55+ messages in thread
From: Patchwork @ 2018-09-05 22:44 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: igt-dev

== Series Details ==

Series: Per context dynamic (sub)slice power-gating
URL   : https://patchwork.freedesktop.org/series/49190/
State : success

== Summary ==

= CI Bug Log - changes from IGT_4629_full -> IGTPW_1793_full =

== Summary - WARNING ==

  Minor unknown changes coming with IGTPW_1793_full need to be verified
  manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_1793_full, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/49190/revisions/1/mbox/

== Possible new issues ==

  Here are the unknown changes that may have been introduced in IGTPW_1793_full:

  === IGT changes ===

    ==== Warnings ====

    igt@pm_rc6_residency@rc6-accuracy:
      shard-kbl:          SKIP -> PASS

    
== Known issues ==

  Here are the changes found in IGTPW_1793_full that come from known issues:

  === IGT changes ===

    ==== Issues hit ====

    igt@gem_exec_await@wide-contexts:
      shard-glk:          PASS -> FAIL (fdo#105900)

    igt@gem_exec_flush@basic-uc-rw-default:
      shard-snb:          PASS -> INCOMPLETE (fdo#105411)

    igt@kms_available_modes_crc@available_mode_test_crc:
      shard-snb:          PASS -> FAIL (fdo#106641)

    igt@kms_frontbuffer_tracking@fbc-rgb565-draw-mmap-cpu:
      shard-glk:          PASS -> FAIL (fdo#103167)

    igt@kms_plane_multiple@atomic-pipe-a-tiling-x:
      shard-snb:          PASS -> FAIL (fdo#103166)

    igt@kms_setmode@basic:
      shard-apl:          PASS -> FAIL (fdo#99912)

    
    ==== Possible fixes ====

    igt@gem_ppgtt@blt-vs-render-ctx0:
      shard-kbl:          INCOMPLETE (fdo#106023, fdo#103665) -> PASS

    igt@kms_cursor_legacy@2x-long-nonblocking-modeset-vs-cursor-atomic:
      shard-glk:          FAIL (fdo#105454, fdo#106509) -> PASS

    igt@kms_flip@2x-flip-vs-expired-vblank-interruptible:
      shard-glk:          FAIL (fdo#105363) -> PASS

    igt@kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-plflip-blt:
      shard-glk:          FAIL (fdo#103167) -> PASS

    igt@kms_plane@plane-panning-bottom-right-suspend-pipe-a-planes:
      shard-apl:          FAIL (fdo#103375) -> PASS

    igt@kms_setmode@basic:
      shard-kbl:          FAIL (fdo#99912) -> PASS

    igt@perf_pmu@rc6-runtime-pm:
      shard-kbl:          FAIL (fdo#105010) -> PASS

    igt@prime_busy@before-bsd1:
      shard-snb:          INCOMPLETE (fdo#105411) -> SKIP

    
  fdo#103166 https://bugs.freedesktop.org/show_bug.cgi?id=103166
  fdo#103167 https://bugs.freedesktop.org/show_bug.cgi?id=103167
  fdo#103375 https://bugs.freedesktop.org/show_bug.cgi?id=103375
  fdo#103665 https://bugs.freedesktop.org/show_bug.cgi?id=103665
  fdo#105010 https://bugs.freedesktop.org/show_bug.cgi?id=105010
  fdo#105363 https://bugs.freedesktop.org/show_bug.cgi?id=105363
  fdo#105411 https://bugs.freedesktop.org/show_bug.cgi?id=105411
  fdo#105454 https://bugs.freedesktop.org/show_bug.cgi?id=105454
  fdo#105900 https://bugs.freedesktop.org/show_bug.cgi?id=105900
  fdo#106023 https://bugs.freedesktop.org/show_bug.cgi?id=106023
  fdo#106509 https://bugs.freedesktop.org/show_bug.cgi?id=106509
  fdo#106641 https://bugs.freedesktop.org/show_bug.cgi?id=106641
  fdo#99912 https://bugs.freedesktop.org/show_bug.cgi?id=99912


== Participating hosts (5 -> 5) ==

  No changes in participating hosts


== Build changes ==

    * IGT: IGT_4629 -> IGTPW_1793
    * Linux: CI_DRM_4770 -> CI_DRM_4772

  CI_DRM_4770: 0c3535cf60140d017a5df73d84d06e8b1a5b5d3b @ git://anongit.freedesktop.org/gfx-ci/linux
  CI_DRM_4772: 1351ee8f3aacdb8f4a71cd17a7035556065c59a9 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_1793: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1793/
  IGT_4629: c3b6d69aa3dd2d1a6c1f2e787670a0aef78f2ea5 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1793/shards.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* Re: [igt-dev] [PATH i-g-t 2/2] tests: add slice power programming test
  2018-09-05 14:25   ` [igt-dev] " Tvrtko Ursulin
@ 2018-09-05 22:57     ` Chris Wilson
  -1 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-05 22:57 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-05 15:25:44)
> +static bool
> +kernel_has_per_context_sseu_support(int fd)
> +{
> +       struct drm_i915_gem_context_param_sseu sseu = { };
> +       struct drm_i915_gem_context_param arg =
> +               { .param = I915_CONTEXT_PARAM_SSEU,
> +                 .value = to_user_pointer(&sseu) };
> +
> +       return __gem_context_get_param(fd, &arg) == 0;

This is meant to just work with

	struct drm_i915_gem_context_param arg = {
		.param = I915_CONTEXT_PARAM_SSEU
	};
	return __gem_context_get_param(fd, &arg) == 0;

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

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

* Re: [igt-dev] [PATH i-g-t 2/2] tests: add slice power programming test
@ 2018-09-05 22:57     ` Chris Wilson
  0 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-05 22:57 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

Quoting Tvrtko Ursulin (2018-09-05 15:25:44)
> +static bool
> +kernel_has_per_context_sseu_support(int fd)
> +{
> +       struct drm_i915_gem_context_param_sseu sseu = { };
> +       struct drm_i915_gem_context_param arg =
> +               { .param = I915_CONTEXT_PARAM_SSEU,
> +                 .value = to_user_pointer(&sseu) };
> +
> +       return __gem_context_get_param(fd, &arg) == 0;

This is meant to just work with

	struct drm_i915_gem_context_param arg = {
		.param = I915_CONTEXT_PARAM_SSEU
	};
	return __gem_context_get_param(fd, &arg) == 0;

-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* Re: [igt-dev] [PATH i-g-t 2/2] tests: add slice power programming test
  2018-09-05 14:25   ` [igt-dev] " Tvrtko Ursulin
@ 2018-09-06  7:00     ` Chris Wilson
  -1 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-06  7:00 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-05 15:25:44)
> From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
> 
> Verifies that the kernel programs slices correctly based by reading
> the value of PWR_CLK_STATE register or MI_SET_PREDICATE on platforms
> before Cannonlake.
> 
> v2: Add subslice tests (Lionel)
>     Use MI_SET_PREDICATE for further verification when available (Lionel)
> 
> v3: Rename to gem_ctx_rpcs (Lionel)
> 
> v4: Update kernel API (Lionel)
>     Add 0 value test (Lionel)
>     Exercise invalid values (Lionel)
> 
> v5: Add perf tests (Lionel)
> 
> v6: Add new sysfs entry tests (Lionel)
> 
> v7: Test rsvd fields
>     Update for kernel series changes
> 
> v8: Drop test_no_sseu_support() test (Kelvin)
>     Drop drm_intel_*() apis (Chris)
> 
> v9: by Chris:
>     Drop all do_ioctl/do_ioctl_err()
>     Use gem_context_[gs]et_param()
>     Use gem_read() instead of mapping memory
>     by Lionel:
>     Test dynamic sseu on/off more
> 
> Tvrtko Ursulin:
> 
> v10:
>  * Various style tweaks and refactorings.
>  * New test coverage.

I didn't notice any testing of:
 - param->size
 - feeding garbage into param->value user pointer (always cleared before
   use, perhaps just poison instead), along with abusive pointers.
   E.g., how does the code fare if we pass in an unfaulted GGTT mmap?

We so need a cook book (even automated) for pointer abuse.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [igt-dev] [PATH i-g-t 2/2] tests: add slice power programming test
@ 2018-09-06  7:00     ` Chris Wilson
  0 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-06  7:00 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

Quoting Tvrtko Ursulin (2018-09-05 15:25:44)
> From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
> 
> Verifies that the kernel programs slices correctly based by reading
> the value of PWR_CLK_STATE register or MI_SET_PREDICATE on platforms
> before Cannonlake.
> 
> v2: Add subslice tests (Lionel)
>     Use MI_SET_PREDICATE for further verification when available (Lionel)
> 
> v3: Rename to gem_ctx_rpcs (Lionel)
> 
> v4: Update kernel API (Lionel)
>     Add 0 value test (Lionel)
>     Exercise invalid values (Lionel)
> 
> v5: Add perf tests (Lionel)
> 
> v6: Add new sysfs entry tests (Lionel)
> 
> v7: Test rsvd fields
>     Update for kernel series changes
> 
> v8: Drop test_no_sseu_support() test (Kelvin)
>     Drop drm_intel_*() apis (Chris)
> 
> v9: by Chris:
>     Drop all do_ioctl/do_ioctl_err()
>     Use gem_context_[gs]et_param()
>     Use gem_read() instead of mapping memory
>     by Lionel:
>     Test dynamic sseu on/off more
> 
> Tvrtko Ursulin:
> 
> v10:
>  * Various style tweaks and refactorings.
>  * New test coverage.

I didn't notice any testing of:
 - param->size
 - feeding garbage into param->value user pointer (always cleared before
   use, perhaps just poison instead), along with abusive pointers.
   E.g., how does the code fare if we pass in an unfaulted GGTT mmap?

We so need a cook book (even automated) for pointer abuse.
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* Re: [igt-dev] [PATH i-g-t 2/2] tests: add slice power programming test
  2018-09-06  7:00     ` Chris Wilson
@ 2018-09-06  9:31       ` Tvrtko Ursulin
  -1 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-06  9:31 UTC (permalink / raw)
  To: Chris Wilson, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx


On 06/09/2018 08:00, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2018-09-05 15:25:44)
>> From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
>>
>> Verifies that the kernel programs slices correctly based by reading
>> the value of PWR_CLK_STATE register or MI_SET_PREDICATE on platforms
>> before Cannonlake.
>>
>> v2: Add subslice tests (Lionel)
>>      Use MI_SET_PREDICATE for further verification when available (Lionel)
>>
>> v3: Rename to gem_ctx_rpcs (Lionel)
>>
>> v4: Update kernel API (Lionel)
>>      Add 0 value test (Lionel)
>>      Exercise invalid values (Lionel)
>>
>> v5: Add perf tests (Lionel)
>>
>> v6: Add new sysfs entry tests (Lionel)
>>
>> v7: Test rsvd fields
>>      Update for kernel series changes
>>
>> v8: Drop test_no_sseu_support() test (Kelvin)
>>      Drop drm_intel_*() apis (Chris)
>>
>> v9: by Chris:
>>      Drop all do_ioctl/do_ioctl_err()
>>      Use gem_context_[gs]et_param()
>>      Use gem_read() instead of mapping memory
>>      by Lionel:
>>      Test dynamic sseu on/off more
>>
>> Tvrtko Ursulin:
>>
>> v10:
>>   * Various style tweaks and refactorings.
>>   * New test coverage.
> 
> I didn't notice any testing of:
>   - param->size

It exists in test_invalid_args.

>   - feeding garbage into param->value user pointer (always cleared before
>     use, perhaps just poison instead), along with abusive pointers.

Also in test_invalid_args - but only the null pointer. I can add an 
unmapped or read-only one.

>     E.g., how does the code fare if we pass in an unfaulted GGTT mmap?

Would not fare well. :I It would be best to be able to reject them but 
how? We'll hit the same problem in future other patches so to support 
this, I think we need to refactor

Regards,

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

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

* Re: [igt-dev] [Intel-gfx] [PATH i-g-t 2/2] tests: add slice power programming test
@ 2018-09-06  9:31       ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-06  9:31 UTC (permalink / raw)
  To: Chris Wilson, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx


On 06/09/2018 08:00, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2018-09-05 15:25:44)
>> From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
>>
>> Verifies that the kernel programs slices correctly based by reading
>> the value of PWR_CLK_STATE register or MI_SET_PREDICATE on platforms
>> before Cannonlake.
>>
>> v2: Add subslice tests (Lionel)
>>      Use MI_SET_PREDICATE for further verification when available (Lionel)
>>
>> v3: Rename to gem_ctx_rpcs (Lionel)
>>
>> v4: Update kernel API (Lionel)
>>      Add 0 value test (Lionel)
>>      Exercise invalid values (Lionel)
>>
>> v5: Add perf tests (Lionel)
>>
>> v6: Add new sysfs entry tests (Lionel)
>>
>> v7: Test rsvd fields
>>      Update for kernel series changes
>>
>> v8: Drop test_no_sseu_support() test (Kelvin)
>>      Drop drm_intel_*() apis (Chris)
>>
>> v9: by Chris:
>>      Drop all do_ioctl/do_ioctl_err()
>>      Use gem_context_[gs]et_param()
>>      Use gem_read() instead of mapping memory
>>      by Lionel:
>>      Test dynamic sseu on/off more
>>
>> Tvrtko Ursulin:
>>
>> v10:
>>   * Various style tweaks and refactorings.
>>   * New test coverage.
> 
> I didn't notice any testing of:
>   - param->size

It exists in test_invalid_args.

>   - feeding garbage into param->value user pointer (always cleared before
>     use, perhaps just poison instead), along with abusive pointers.

Also in test_invalid_args - but only the null pointer. I can add an 
unmapped or read-only one.

>     E.g., how does the code fare if we pass in an unfaulted GGTT mmap?

Would not fare well. :I It would be best to be able to reject them but 
how? We'll hit the same problem in future other patches so to support 
this, I think we need to refactor

Regards,

Tvrtko
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* Re: [igt-dev] [PATH i-g-t 2/2] tests: add slice power programming test
  2018-09-06  9:31       ` [igt-dev] [Intel-gfx] " Tvrtko Ursulin
@ 2018-09-06  9:50         ` Chris Wilson
  -1 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-06  9:50 UTC (permalink / raw)
  To: Tvrtko Ursulin, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-06 10:31:21)
> 
> On 06/09/2018 08:00, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2018-09-05 15:25:44)
> >> From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
> >>
> >> Verifies that the kernel programs slices correctly based by reading
> >> the value of PWR_CLK_STATE register or MI_SET_PREDICATE on platforms
> >> before Cannonlake.
> >>
> >> v2: Add subslice tests (Lionel)
> >>      Use MI_SET_PREDICATE for further verification when available (Lionel)
> >>
> >> v3: Rename to gem_ctx_rpcs (Lionel)
> >>
> >> v4: Update kernel API (Lionel)
> >>      Add 0 value test (Lionel)
> >>      Exercise invalid values (Lionel)
> >>
> >> v5: Add perf tests (Lionel)
> >>
> >> v6: Add new sysfs entry tests (Lionel)
> >>
> >> v7: Test rsvd fields
> >>      Update for kernel series changes
> >>
> >> v8: Drop test_no_sseu_support() test (Kelvin)
> >>      Drop drm_intel_*() apis (Chris)
> >>
> >> v9: by Chris:
> >>      Drop all do_ioctl/do_ioctl_err()
> >>      Use gem_context_[gs]et_param()
> >>      Use gem_read() instead of mapping memory
> >>      by Lionel:
> >>      Test dynamic sseu on/off more
> >>
> >> Tvrtko Ursulin:
> >>
> >> v10:
> >>   * Various style tweaks and refactorings.
> >>   * New test coverage.
> > 
> > I didn't notice any testing of:
> >   - param->size
> 
> It exists in test_invalid_args.
> 
> >   - feeding garbage into param->value user pointer (always cleared before
> >     use, perhaps just poison instead), along with abusive pointers.
> 
> Also in test_invalid_args - but only the null pointer. I can add an 
> unmapped or read-only one.

I think passing a pointer that starts valid and crosses into an unmapped
page is essential. That makes sure we are using copy_from_user
correctly. Another trivial one is param->value = -param->size + 1.

As for the garbage, I wasn't sure (because I'm fully cogniscent of the
sseu responses) if we would not interpret 0 as a valid response. If you
are happy that we can differentiate an output 0 after passing in 0,
that's all fine (as opposed to us forgetting to fill a value along some
path).

> >     E.g., how does the code fare if we pass in an unfaulted GGTT mmap?
> 
> Would not fare well. :I It would be best to be able to reject them but 
> how? We'll hit the same problem in future other patches so to support 
> this, I think we need to refactor

Do you want my patch to drop struct_mutex entirely here :) So then you
have to add it where you need it, which is after the copy_from_user() so
recursive faults are not a problem.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [igt-dev] [Intel-gfx] [PATH i-g-t 2/2] tests: add slice power programming test
@ 2018-09-06  9:50         ` Chris Wilson
  0 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-06  9:50 UTC (permalink / raw)
  To: Tvrtko Ursulin, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-06 10:31:21)
> 
> On 06/09/2018 08:00, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2018-09-05 15:25:44)
> >> From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
> >>
> >> Verifies that the kernel programs slices correctly based by reading
> >> the value of PWR_CLK_STATE register or MI_SET_PREDICATE on platforms
> >> before Cannonlake.
> >>
> >> v2: Add subslice tests (Lionel)
> >>      Use MI_SET_PREDICATE for further verification when available (Lionel)
> >>
> >> v3: Rename to gem_ctx_rpcs (Lionel)
> >>
> >> v4: Update kernel API (Lionel)
> >>      Add 0 value test (Lionel)
> >>      Exercise invalid values (Lionel)
> >>
> >> v5: Add perf tests (Lionel)
> >>
> >> v6: Add new sysfs entry tests (Lionel)
> >>
> >> v7: Test rsvd fields
> >>      Update for kernel series changes
> >>
> >> v8: Drop test_no_sseu_support() test (Kelvin)
> >>      Drop drm_intel_*() apis (Chris)
> >>
> >> v9: by Chris:
> >>      Drop all do_ioctl/do_ioctl_err()
> >>      Use gem_context_[gs]et_param()
> >>      Use gem_read() instead of mapping memory
> >>      by Lionel:
> >>      Test dynamic sseu on/off more
> >>
> >> Tvrtko Ursulin:
> >>
> >> v10:
> >>   * Various style tweaks and refactorings.
> >>   * New test coverage.
> > 
> > I didn't notice any testing of:
> >   - param->size
> 
> It exists in test_invalid_args.
> 
> >   - feeding garbage into param->value user pointer (always cleared before
> >     use, perhaps just poison instead), along with abusive pointers.
> 
> Also in test_invalid_args - but only the null pointer. I can add an 
> unmapped or read-only one.

I think passing a pointer that starts valid and crosses into an unmapped
page is essential. That makes sure we are using copy_from_user
correctly. Another trivial one is param->value = -param->size + 1.

As for the garbage, I wasn't sure (because I'm fully cogniscent of the
sseu responses) if we would not interpret 0 as a valid response. If you
are happy that we can differentiate an output 0 after passing in 0,
that's all fine (as opposed to us forgetting to fill a value along some
path).

> >     E.g., how does the code fare if we pass in an unfaulted GGTT mmap?
> 
> Would not fare well. :I It would be best to be able to reject them but 
> how? We'll hit the same problem in future other patches so to support 
> this, I think we need to refactor

Do you want my patch to drop struct_mutex entirely here :) So then you
have to add it where you need it, which is after the copy_from_user() so
recursive faults are not a problem.
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [PATH i-g-t v11 2/2] tests: add slice power programming test
  2018-09-06  9:50         ` [igt-dev] [Intel-gfx] " Chris Wilson
@ 2018-09-11 11:34           ` Tvrtko Ursulin
  -1 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-11 11:34 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

Verifies that the kernel programs slices correctly based by reading
the value of PWR_CLK_STATE register or MI_SET_PREDICATE on platforms
before Cannonlake.

v2: Add subslice tests (Lionel)
    Use MI_SET_PREDICATE for further verification when available (Lionel)

v3: Rename to gem_ctx_rpcs (Lionel)

v4: Update kernel API (Lionel)
    Add 0 value test (Lionel)
    Exercise invalid values (Lionel)

v5: Add perf tests (Lionel)

v6: Add new sysfs entry tests (Lionel)

v7: Test rsvd fields
    Update for kernel series changes

v8: Drop test_no_sseu_support() test (Kelvin)
    Drop drm_intel_*() apis (Chris)

v9: by Chris:
    Drop all do_ioctl/do_ioctl_err()
    Use gem_context_[gs]et_param()
    Use gem_read() instead of mapping memory
    by Lionel:
    Test dynamic sseu on/off more

Tvrtko Ursulin:

v10:
 * Various style tweaks and refactorings.
 * New test coverage.

v11:
 * Change platform support to just Gen11.
 * Simplify availability test. (Chris Wilson)
 * More invalid pointer tests. (Chris Wilson)

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tests/Makefile.am      |    1 +
 tests/Makefile.sources |    1 +
 tests/gem_ctx_param.c  |    4 +-
 tests/gem_ctx_sseu.c   | 1087 ++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |    7 +
 5 files changed, 1099 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_sseu.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index ee5a7c5e83b8..6b67bd2cc17a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -107,6 +107,7 @@ gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
+gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
 gem_fence_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index c84933f1d971..f8f2c8d67d72 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -56,6 +56,7 @@ TESTS_progs = \
 	gem_ctx_exec \
 	gem_ctx_isolation \
 	gem_ctx_param \
+	gem_ctx_sseu \
 	gem_ctx_switch \
 	gem_ctx_thrash \
 	gem_double_irq_loop \
diff --git a/tests/gem_ctx_param.c b/tests/gem_ctx_param.c
index c46fd709b0d7..af1afeaa2f2f 100644
--- a/tests/gem_ctx_param.c
+++ b/tests/gem_ctx_param.c
@@ -294,11 +294,13 @@ igt_main
 			set_priority(fd);
 	}
 
+	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
+
 	/* NOTE: This testcase intentionally tests for the next free parameter
 	 * to catch ABI extensions. Don't "fix" this testcase without adding all
 	 * the tests for the new param first.
 	 */
-	arg.param = I915_CONTEXT_PARAM_PRIORITY + 1;
+	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
 
 	igt_subtest("invalid-param-get") {
 		arg.ctx_id = ctx;
diff --git a/tests/gem_ctx_sseu.c b/tests/gem_ctx_sseu.c
new file mode 100644
index 000000000000..6caa9d9f90d4
--- /dev/null
+++ b/tests/gem_ctx_sseu.c
@@ -0,0 +1,1087 @@
+/*
+ * Copyright © 2017-2018 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.
+ *
+ * Authors:
+ *    Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include "igt_dummyload.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+#include "ioctl_wrappers.h"
+
+IGT_TEST_DESCRIPTION("Test context render powergating programming.");
+
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+
+#define MI_SET_PREDICATE      (0x1 << 23)
+#define  MI_SET_PREDICATE_NOOP_NEVER         (0)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_SET   (2)
+#define  MI_SET_PREDICATE_NOOP_RESULT_CLEAR  (3)
+#define  MI_SET_PREDICATE_NOOP_RESULT_SET    (4)
+#define  MI_SET_PREDICATE_1_SLICES           (5)
+#define  MI_SET_PREDICATE_2_SLICES           (6)
+#define  MI_SET_PREDICATE_3_SLICES           (7)
+
+#define GEN8_R_PWR_CLK_STATE		0x20C8
+#define   GEN8_RPCS_ENABLE		(1 << 31)
+#define   GEN8_RPCS_S_CNT_ENABLE	(1 << 18)
+#define   GEN8_RPCS_S_CNT_SHIFT		15
+#define   GEN8_RPCS_S_CNT_MASK		(0x7 << GEN8_RPCS_S_CNT_SHIFT)
+#define   GEN11_RPCS_S_CNT_SHIFT	12
+#define   GEN11_RPCS_S_CNT_MASK		(0x3f << GEN11_RPCS_S_CNT_SHIFT)
+#define   GEN8_RPCS_SS_CNT_ENABLE	(1 << 11)
+#define   GEN8_RPCS_SS_CNT_SHIFT	8
+#define   GEN8_RPCS_SS_CNT_MASK		(0x7 << GEN8_RPCS_SS_CNT_SHIFT)
+#define   GEN8_RPCS_EU_MAX_SHIFT	4
+#define   GEN8_RPCS_EU_MAX_MASK		(0xf << GEN8_RPCS_EU_MAX_SHIFT)
+#define   GEN8_RPCS_EU_MIN_SHIFT	0
+#define   GEN8_RPCS_EU_MIN_MASK		(0xf << GEN8_RPCS_EU_MIN_SHIFT)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+static unsigned int __intel_gen__, __intel_devid__;
+static uint64_t __slice_mask__, __subslice_mask__;
+static unsigned int __slice_count__, __subslice_count__;
+
+static uint64_t mask_minus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if ((1ULL << i) & mask)
+			return mask & ~(1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_plus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if (((1ULL << i) & mask) == 0)
+			return mask | (1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_minus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_minus_one(mask);
+
+	return mask;
+}
+
+static uint64_t mask_plus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_plus_one(mask);
+
+	return mask;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+		struct drm_i915_gem_relocation_entry *reloc,
+		uint32_t gem_handle, uint32_t delta, /* in bytes */
+		uint32_t offset, /* in dwords */
+		uint32_t read_domains, uint32_t write_domains)
+{
+	reloc->target_handle = gem_handle;
+	reloc->delta = delta;
+	reloc->offset = offset * sizeof(uint32_t);
+	reloc->presumed_offset = 0;
+	reloc->read_domains = read_domains;
+	reloc->write_domain = write_domains;
+
+	*batch++ = delta;
+	*batch++ = 0;
+
+	return batch;
+}
+
+
+static uint32_t
+read_rpcs_reg(int fd, uint32_t ctx, uint32_t expected_slices, igt_spin_t *spin)
+{
+	struct drm_i915_gem_execbuffer2 execbuf = { };
+	struct drm_i915_gem_relocation_entry relocs[2];
+	struct drm_i915_gem_exec_object2 obj[2];
+	uint32_t *batch, *b, data[2];
+	unsigned int n_relocs = 0;
+	uint32_t rpcs;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[1].handle = gem_create(fd, 4096);
+
+	batch = b =
+	      gem_mmap__cpu(fd, obj[1].handle, 0, 4096, PROT_READ | PROT_WRITE);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) |
+		       (MI_SET_PREDICATE_1_SLICES + expected_slices - 1);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = RCS_TIMESTAMP;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    0, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = GEN8_R_PWR_CLK_STATE;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    4, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) | MI_SET_PREDICATE_NOOP_NEVER;
+
+	*b++ = MI_BATCH_BUFFER_END;
+
+	gem_munmap(batch, 4096);
+
+	obj[1].relocation_count = n_relocs;
+	obj[1].relocs_ptr = to_user_pointer(relocs);
+
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+	i915_execbuffer2_set_context_id(execbuf, ctx);
+
+	gem_execbuf(fd, &execbuf);
+
+	if (spin)
+		igt_spin_batch_end(spin);
+
+	gem_read(fd, obj[0].handle, 0, data, sizeof(data));
+
+	rpcs = data[1];
+
+	igt_debug("rcs_timestamp=0x%x rpcs=0x%x\n", data[0], data[1]);
+
+	gem_close(fd, obj[0].handle);
+	gem_close(fd, obj[1].handle);
+
+	return rpcs;
+}
+
+typedef uint32_t (*read_slice_count_f)(int fd, uint32_t ctx, uint32_t expected,
+				       igt_spin_t *spin);
+
+static read_slice_count_f __read_slice_count;
+
+static uint32_t
+read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count)
+{
+	return __read_slice_count(fd, ctx, expected_slice_count, NULL);
+}
+
+static uint32_t
+gen8_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		      igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN8_RPCS_S_CNT_MASK) >> GEN8_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+gen11_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		       igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN11_RPCS_S_CNT_MASK) >> GEN11_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+read_subslice_count(int fd, uint32_t ctx)
+{
+	return (read_rpcs_reg(fd, ctx, 0, NULL) & GEN8_RPCS_SS_CNT_MASK) >>
+	       GEN8_RPCS_SS_CNT_SHIFT;
+}
+
+static bool
+kernel_has_per_context_sseu_support(int fd)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU };
+
+	return __gem_context_get_param(fd, &arg) == 0;
+}
+
+static void
+context_get_sseu_masks(int fd, uint32_t ctx,
+		       uint64_t *slice_mask,
+		       uint64_t *subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+
+	if (slice_mask)
+		*slice_mask = sseu.slice_mask;
+
+	if (subslice_mask)
+		*subslice_mask = sseu.subslice_mask;
+}
+
+static void
+context_set_slice_mask(int fd, uint32_t ctx, uint64_t slice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.slice_mask = slice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+static void
+context_set_subslice_mask(int fd, uint32_t ctx, uint64_t subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.subslice_mask = subslice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+/*
+ * Verify that we can program the slice count.
+ */
+static void
+test_slice_pg(int fd, uint32_t pg_slice_count)
+{
+	uint64_t pg_slice_mask = mask_minus(__slice_mask__, pg_slice_count);
+	unsigned int slice_count = __slice_count__ - pg_slice_count;
+	uint64_t slice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(slice_count, __builtin_popcount(pg_slice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_slice_mask(fd, ctx, pg_slice_mask);
+	context_get_sseu_masks(fd, ctx, &slice_mask, NULL);
+	igt_assert_eq(pg_slice_mask, slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx, __slice_count__), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), slice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+/*
+ * Verify that we can program the subslice count.
+ */
+static void
+test_subslice_pg(int fd, int pg_subslice_count)
+{
+	uint64_t pg_subslice_mask =
+		mask_minus(__subslice_mask__, pg_subslice_count);
+	unsigned int subslice_count = __subslice_count__ - pg_subslice_count;
+	uint64_t subslice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(subslice_count, __builtin_popcount(pg_subslice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_subslice_mask(fd, ctx, pg_subslice_mask);
+	context_get_sseu_masks(fd, ctx, NULL, &subslice_mask);
+	igt_assert_eq(pg_subslice_mask, subslice_mask);
+
+	igt_assert_eq(read_subslice_count(fd, ctx), subslice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_engine(int fd, unsigned int class, unsigned int instance)
+{
+	int pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+
+	if (pmu >= 0)
+		close(pmu);
+
+	return pmu >= 0;
+}
+
+static bool
+engine_supports_sseu(int fd, unsigned int class, unsigned int instance)
+{
+	return __intel_gen__ >= 8 &&
+	       class == I915_ENGINE_CLASS_RENDER && instance == 0 ?
+	       true : false;
+}
+
+/*
+ * Verify that invalid engines are rejected and valid ones are accepted.
+ */
+static void test_engines(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	unsigned int class, instance;
+	int last_with_engines;
+
+	/* get_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+	sseu.class = I915_ENGINE_CLASS_RENDER;
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_get_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert_eq(ret, 0);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	/*
+	 * Get some proper values before trying to reprogram them onto
+	 * an invalid engine.
+	 */
+	sseu.class = 0;
+	sseu.instance = 0;
+	gem_context_get_param(fd, &arg);
+
+	/* set_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_set_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				if (engine_supports_sseu(fd, class, instance))
+					igt_assert_eq(ret, 0);
+				else
+					igt_assert_eq(ret, -ENODEV);
+
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid arguments are rejected.
+ */
+static void
+test_invalid_args(int fd)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		};
+	unsigned int sz;
+	void *page[2];
+
+	/* get param */
+
+	/* Invalid size. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	/* Query size. */
+	arg.size = 0;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	sz = arg.size;
+
+	/* Bad pointers. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* set param */
+
+	/* Invalid sizes. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	arg.size = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	arg.size = sz;
+
+	/* Bad pointers. */
+	arg.value = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Unmapepd. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Read-only. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	igt_assert(mprotect(page[0], 4096, PROT_READ) == 0);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(page[0]) -
+		    sizeof(struct drm_i915_gem_context_param_sseu) - 4;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	/* Straddle into read-only area. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	igt_assert(mprotect(page[1], 4096, PROT_READ) == 0);
+	arg.value = to_user_pointer(page[0]) -
+		    sizeof(struct drm_i915_gem_context_param_sseu) - 4;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+	munmap(page[1], 4096);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid SSEU values are rejected.
+ */
+static void
+test_invalid_sseu(int fd)
+{
+	struct drm_i915_gem_context_param_sseu default_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		};
+	unsigned int i;
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&default_sseu);
+	gem_context_get_param(fd, &arg);
+
+	arg.value = to_user_pointer(&sseu);
+
+	/* Try all slice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __slice_count__); i++) {
+		sseu.slice_mask = mask_plus(__slice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 slices. */
+	sseu.slice_mask = 0;
+	igt_assert_eq(-EINVAL, __gem_context_set_param(fd, &arg));
+
+	/* Try all subslice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __subslice_count__); i++) {
+		sseu.subslice_mask = mask_plus(__subslice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 subslices. */
+	sseu.subslice_mask = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try number of EUs superior to the max available. */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try to program 0 max EUs. */
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Min > max */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = sseu.max_eus_per_subslice;
+	sseu.max_eus_per_subslice = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	if (__intel_gen__ != 11)
+		goto out;
+
+	/* Subset of subslices but slice mask greater than one. */
+	if (__slice_count__ > 1) {
+		sseu = default_sseu;
+		sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Odd subslices above four. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* More than half subslices with one slice. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 - 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+out:
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/* Verify that the kernel returns a correct error value on Gen < 8. */
+static void
+init_contexts(int fd, uint32_t *ctx, unsigned int num,
+	      uint64_t mask0, uint64_t mask1)
+{
+	unsigned int i;
+
+	igt_assert_eq(num, 2);
+
+	for (i = 0; i < num; i++)
+		ctx[i] = gem_context_create(fd);
+
+	context_set_slice_mask(fd, ctx[0], mask0);
+	context_set_slice_mask(fd, ctx[1], mask1);
+}
+
+/*
+ * Verify that powergating settings are put on hold while i915/perf is
+ * active.
+ */
+static void
+test_perf_oa(int fd)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, 1, /* test metric */
+		DRM_I915_PERF_PROP_OA_FORMAT, I915_OA_FORMAT_A32u40_A4u32_B8_C8,
+		DRM_I915_PERF_PROP_OA_EXPONENT, 20,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+		I915_PERF_FLAG_FD_NONBLOCK,
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int slice_count = __slice_count__ - 1;
+	uint32_t ctx[2];
+	unsigned int i;
+	int perf_fd;
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	/*
+	 * Now open i915/perf and verify that all contexts have been
+	 * reconfigured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1], slice_count),
+				      0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__), 0);
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+
+	/*
+	 * Open i915/perf first and verify that all contexts created
+	 * afterward are configured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Check the device's default values, despite setting
+	 * otherwise.
+	 */
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1],
+						       slice_count), 0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	struct igt_spin_factory opts = {
+		.ctx = ctx,
+		.engine = flags,
+	};
+
+	if (gem_can_store_dword(fd, flags))
+		opts.flags |= IGT_SPIN_POLL_RUN;
+
+	return __igt_spin_batch_factory(fd, &opts);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!READ_ONCE(*spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static uint32_t
+read_slice_count_busy(int fd, uint32_t context, uint32_t expected,
+		      igt_spin_t *spin)
+{
+	return __read_slice_count(fd, context, expected, spin);
+}
+
+#define TEST_IDLE (1)
+#define TEST_BUSY (2)
+
+/*
+ * Test context re-configuration with either idle or busy contexts.
+ */
+static void
+test_dynamic(int fd, unsigned int flags)
+{
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int pg_slice_count = __slice_count__ - 1;
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	igt_spin_t *spin = NULL;
+
+	gem_context_get_param(fd, &arg);
+
+	/* First set the default mask */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      __slice_count__);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* Then set a powergated configuration */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      pg_slice_count);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* Put the device's default back again */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      __slice_count__);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* One last powergated config for the road... */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      pg_slice_count);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+static void test_unsupported_device(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -ENODEV);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+static bool
+platform_has_per_context_sseu_support(void)
+{
+	return __intel_gen__ == 11;
+}
+
+igt_main
+{
+	unsigned int max_slices = 3, max_subslices = 3;
+	unsigned int i;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		__intel_devid__ = intel_get_drm_devid(fd);
+		__intel_gen__ = intel_gen(__intel_devid__);
+
+		igt_require(kernel_has_per_context_sseu_support(fd));
+
+		if (__intel_gen__ >= 11)
+			__read_slice_count = gen11_read_slice_count;
+		else
+			__read_slice_count = gen8_read_slice_count;
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			igt_require(!platform_has_per_context_sseu_support());
+		}
+
+		igt_subtest("unsupported-device")
+			test_unsupported_device(fd);
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			drm_i915_getparam_t gp;
+
+			igt_require(platform_has_per_context_sseu_support());
+
+			gp.param = I915_PARAM_SLICE_MASK;
+			gp.value = (int *) &__slice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__slice_count__ = __builtin_popcount(__slice_mask__);
+
+			gp.param = I915_PARAM_SUBSLICE_MASK;
+			gp.value = (int *) &__subslice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__subslice_count__ =
+				__builtin_popcount(__subslice_mask__);
+		}
+
+		igt_subtest("invalid-args")
+			test_invalid_args(fd);
+
+		igt_subtest("invalid-sseu")
+			test_invalid_sseu(fd);
+
+		igt_subtest("engines")
+			test_engines(fd);
+
+		for (i = 1; i < max_slices; i++) {
+			igt_subtest_f("slice-pg-%i", i) {
+				igt_require(__slice_count__ > i);
+
+				test_slice_pg(fd, i);
+			}
+		}
+
+		for (i = 1; i < max_subslices; i++) {
+			igt_subtest_f("subslice-pg-%i", i) {
+				igt_require(__subslice_count__ >= 2);
+
+				/*
+				 * Only available on some Atom platforms and
+				 * Gen10+.
+				 */
+				igt_require(IS_BROXTON(__intel_devid__) ||
+					    IS_GEMINILAKE(__intel_devid__) ||
+					    __intel_gen__ >= 10);
+
+				test_subslice_pg(fd, i);
+			}
+		}
+
+		igt_subtest("sseu-perf-oa") {
+			igt_require(__slice_count__ > 1);
+
+			test_perf_oa(fd);
+		}
+
+		igt_subtest("dynamic") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, 0);
+		}
+
+		igt_subtest("dynamic-busy") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, TEST_BUSY);
+		}
+
+		igt_subtest("dynamic-idle") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, TEST_IDLE);
+		}
+	}
+
+	igt_fixture {
+		close(fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 17deb945ec95..9319fa4e19b4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -249,6 +249,13 @@ foreach prog : test_progs
 		   install : true)
 endforeach
 
+test_executables += executable('gem_ctx_sseu', 'gem_ctx_sseu.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_ctx_sseu'
+
 test_executables += executable('gem_eio', 'gem_eio.c',
 	   dependencies : test_deps + [ realtime ],
 	   install_dir : libexecdir,
-- 
2.17.1

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

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

* [igt-dev] [PATH i-g-t v11 2/2] tests: add slice power programming test
@ 2018-09-11 11:34           ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-11 11:34 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

Verifies that the kernel programs slices correctly based by reading
the value of PWR_CLK_STATE register or MI_SET_PREDICATE on platforms
before Cannonlake.

v2: Add subslice tests (Lionel)
    Use MI_SET_PREDICATE for further verification when available (Lionel)

v3: Rename to gem_ctx_rpcs (Lionel)

v4: Update kernel API (Lionel)
    Add 0 value test (Lionel)
    Exercise invalid values (Lionel)

v5: Add perf tests (Lionel)

v6: Add new sysfs entry tests (Lionel)

v7: Test rsvd fields
    Update for kernel series changes

v8: Drop test_no_sseu_support() test (Kelvin)
    Drop drm_intel_*() apis (Chris)

v9: by Chris:
    Drop all do_ioctl/do_ioctl_err()
    Use gem_context_[gs]et_param()
    Use gem_read() instead of mapping memory
    by Lionel:
    Test dynamic sseu on/off more

Tvrtko Ursulin:

v10:
 * Various style tweaks and refactorings.
 * New test coverage.

v11:
 * Change platform support to just Gen11.
 * Simplify availability test. (Chris Wilson)
 * More invalid pointer tests. (Chris Wilson)

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tests/Makefile.am      |    1 +
 tests/Makefile.sources |    1 +
 tests/gem_ctx_param.c  |    4 +-
 tests/gem_ctx_sseu.c   | 1087 ++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |    7 +
 5 files changed, 1099 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_sseu.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index ee5a7c5e83b8..6b67bd2cc17a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -107,6 +107,7 @@ gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
+gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
 gem_fence_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index c84933f1d971..f8f2c8d67d72 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -56,6 +56,7 @@ TESTS_progs = \
 	gem_ctx_exec \
 	gem_ctx_isolation \
 	gem_ctx_param \
+	gem_ctx_sseu \
 	gem_ctx_switch \
 	gem_ctx_thrash \
 	gem_double_irq_loop \
diff --git a/tests/gem_ctx_param.c b/tests/gem_ctx_param.c
index c46fd709b0d7..af1afeaa2f2f 100644
--- a/tests/gem_ctx_param.c
+++ b/tests/gem_ctx_param.c
@@ -294,11 +294,13 @@ igt_main
 			set_priority(fd);
 	}
 
+	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
+
 	/* NOTE: This testcase intentionally tests for the next free parameter
 	 * to catch ABI extensions. Don't "fix" this testcase without adding all
 	 * the tests for the new param first.
 	 */
-	arg.param = I915_CONTEXT_PARAM_PRIORITY + 1;
+	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
 
 	igt_subtest("invalid-param-get") {
 		arg.ctx_id = ctx;
diff --git a/tests/gem_ctx_sseu.c b/tests/gem_ctx_sseu.c
new file mode 100644
index 000000000000..6caa9d9f90d4
--- /dev/null
+++ b/tests/gem_ctx_sseu.c
@@ -0,0 +1,1087 @@
+/*
+ * Copyright © 2017-2018 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.
+ *
+ * Authors:
+ *    Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include "igt_dummyload.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+#include "ioctl_wrappers.h"
+
+IGT_TEST_DESCRIPTION("Test context render powergating programming.");
+
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+
+#define MI_SET_PREDICATE      (0x1 << 23)
+#define  MI_SET_PREDICATE_NOOP_NEVER         (0)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_SET   (2)
+#define  MI_SET_PREDICATE_NOOP_RESULT_CLEAR  (3)
+#define  MI_SET_PREDICATE_NOOP_RESULT_SET    (4)
+#define  MI_SET_PREDICATE_1_SLICES           (5)
+#define  MI_SET_PREDICATE_2_SLICES           (6)
+#define  MI_SET_PREDICATE_3_SLICES           (7)
+
+#define GEN8_R_PWR_CLK_STATE		0x20C8
+#define   GEN8_RPCS_ENABLE		(1 << 31)
+#define   GEN8_RPCS_S_CNT_ENABLE	(1 << 18)
+#define   GEN8_RPCS_S_CNT_SHIFT		15
+#define   GEN8_RPCS_S_CNT_MASK		(0x7 << GEN8_RPCS_S_CNT_SHIFT)
+#define   GEN11_RPCS_S_CNT_SHIFT	12
+#define   GEN11_RPCS_S_CNT_MASK		(0x3f << GEN11_RPCS_S_CNT_SHIFT)
+#define   GEN8_RPCS_SS_CNT_ENABLE	(1 << 11)
+#define   GEN8_RPCS_SS_CNT_SHIFT	8
+#define   GEN8_RPCS_SS_CNT_MASK		(0x7 << GEN8_RPCS_SS_CNT_SHIFT)
+#define   GEN8_RPCS_EU_MAX_SHIFT	4
+#define   GEN8_RPCS_EU_MAX_MASK		(0xf << GEN8_RPCS_EU_MAX_SHIFT)
+#define   GEN8_RPCS_EU_MIN_SHIFT	0
+#define   GEN8_RPCS_EU_MIN_MASK		(0xf << GEN8_RPCS_EU_MIN_SHIFT)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+static unsigned int __intel_gen__, __intel_devid__;
+static uint64_t __slice_mask__, __subslice_mask__;
+static unsigned int __slice_count__, __subslice_count__;
+
+static uint64_t mask_minus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if ((1ULL << i) & mask)
+			return mask & ~(1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_plus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if (((1ULL << i) & mask) == 0)
+			return mask | (1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_minus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_minus_one(mask);
+
+	return mask;
+}
+
+static uint64_t mask_plus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_plus_one(mask);
+
+	return mask;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+		struct drm_i915_gem_relocation_entry *reloc,
+		uint32_t gem_handle, uint32_t delta, /* in bytes */
+		uint32_t offset, /* in dwords */
+		uint32_t read_domains, uint32_t write_domains)
+{
+	reloc->target_handle = gem_handle;
+	reloc->delta = delta;
+	reloc->offset = offset * sizeof(uint32_t);
+	reloc->presumed_offset = 0;
+	reloc->read_domains = read_domains;
+	reloc->write_domain = write_domains;
+
+	*batch++ = delta;
+	*batch++ = 0;
+
+	return batch;
+}
+
+
+static uint32_t
+read_rpcs_reg(int fd, uint32_t ctx, uint32_t expected_slices, igt_spin_t *spin)
+{
+	struct drm_i915_gem_execbuffer2 execbuf = { };
+	struct drm_i915_gem_relocation_entry relocs[2];
+	struct drm_i915_gem_exec_object2 obj[2];
+	uint32_t *batch, *b, data[2];
+	unsigned int n_relocs = 0;
+	uint32_t rpcs;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[1].handle = gem_create(fd, 4096);
+
+	batch = b =
+	      gem_mmap__cpu(fd, obj[1].handle, 0, 4096, PROT_READ | PROT_WRITE);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) |
+		       (MI_SET_PREDICATE_1_SLICES + expected_slices - 1);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = RCS_TIMESTAMP;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    0, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = GEN8_R_PWR_CLK_STATE;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    4, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) | MI_SET_PREDICATE_NOOP_NEVER;
+
+	*b++ = MI_BATCH_BUFFER_END;
+
+	gem_munmap(batch, 4096);
+
+	obj[1].relocation_count = n_relocs;
+	obj[1].relocs_ptr = to_user_pointer(relocs);
+
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+	i915_execbuffer2_set_context_id(execbuf, ctx);
+
+	gem_execbuf(fd, &execbuf);
+
+	if (spin)
+		igt_spin_batch_end(spin);
+
+	gem_read(fd, obj[0].handle, 0, data, sizeof(data));
+
+	rpcs = data[1];
+
+	igt_debug("rcs_timestamp=0x%x rpcs=0x%x\n", data[0], data[1]);
+
+	gem_close(fd, obj[0].handle);
+	gem_close(fd, obj[1].handle);
+
+	return rpcs;
+}
+
+typedef uint32_t (*read_slice_count_f)(int fd, uint32_t ctx, uint32_t expected,
+				       igt_spin_t *spin);
+
+static read_slice_count_f __read_slice_count;
+
+static uint32_t
+read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count)
+{
+	return __read_slice_count(fd, ctx, expected_slice_count, NULL);
+}
+
+static uint32_t
+gen8_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		      igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN8_RPCS_S_CNT_MASK) >> GEN8_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+gen11_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		       igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN11_RPCS_S_CNT_MASK) >> GEN11_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+read_subslice_count(int fd, uint32_t ctx)
+{
+	return (read_rpcs_reg(fd, ctx, 0, NULL) & GEN8_RPCS_SS_CNT_MASK) >>
+	       GEN8_RPCS_SS_CNT_SHIFT;
+}
+
+static bool
+kernel_has_per_context_sseu_support(int fd)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU };
+
+	return __gem_context_get_param(fd, &arg) == 0;
+}
+
+static void
+context_get_sseu_masks(int fd, uint32_t ctx,
+		       uint64_t *slice_mask,
+		       uint64_t *subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+
+	if (slice_mask)
+		*slice_mask = sseu.slice_mask;
+
+	if (subslice_mask)
+		*subslice_mask = sseu.subslice_mask;
+}
+
+static void
+context_set_slice_mask(int fd, uint32_t ctx, uint64_t slice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.slice_mask = slice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+static void
+context_set_subslice_mask(int fd, uint32_t ctx, uint64_t subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.subslice_mask = subslice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+/*
+ * Verify that we can program the slice count.
+ */
+static void
+test_slice_pg(int fd, uint32_t pg_slice_count)
+{
+	uint64_t pg_slice_mask = mask_minus(__slice_mask__, pg_slice_count);
+	unsigned int slice_count = __slice_count__ - pg_slice_count;
+	uint64_t slice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(slice_count, __builtin_popcount(pg_slice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_slice_mask(fd, ctx, pg_slice_mask);
+	context_get_sseu_masks(fd, ctx, &slice_mask, NULL);
+	igt_assert_eq(pg_slice_mask, slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx, __slice_count__), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), slice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+/*
+ * Verify that we can program the subslice count.
+ */
+static void
+test_subslice_pg(int fd, int pg_subslice_count)
+{
+	uint64_t pg_subslice_mask =
+		mask_minus(__subslice_mask__, pg_subslice_count);
+	unsigned int subslice_count = __subslice_count__ - pg_subslice_count;
+	uint64_t subslice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(subslice_count, __builtin_popcount(pg_subslice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_subslice_mask(fd, ctx, pg_subslice_mask);
+	context_get_sseu_masks(fd, ctx, NULL, &subslice_mask);
+	igt_assert_eq(pg_subslice_mask, subslice_mask);
+
+	igt_assert_eq(read_subslice_count(fd, ctx), subslice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_engine(int fd, unsigned int class, unsigned int instance)
+{
+	int pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+
+	if (pmu >= 0)
+		close(pmu);
+
+	return pmu >= 0;
+}
+
+static bool
+engine_supports_sseu(int fd, unsigned int class, unsigned int instance)
+{
+	return __intel_gen__ >= 8 &&
+	       class == I915_ENGINE_CLASS_RENDER && instance == 0 ?
+	       true : false;
+}
+
+/*
+ * Verify that invalid engines are rejected and valid ones are accepted.
+ */
+static void test_engines(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	unsigned int class, instance;
+	int last_with_engines;
+
+	/* get_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+	sseu.class = I915_ENGINE_CLASS_RENDER;
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_get_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert_eq(ret, 0);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	/*
+	 * Get some proper values before trying to reprogram them onto
+	 * an invalid engine.
+	 */
+	sseu.class = 0;
+	sseu.instance = 0;
+	gem_context_get_param(fd, &arg);
+
+	/* set_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_set_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				if (engine_supports_sseu(fd, class, instance))
+					igt_assert_eq(ret, 0);
+				else
+					igt_assert_eq(ret, -ENODEV);
+
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid arguments are rejected.
+ */
+static void
+test_invalid_args(int fd)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		};
+	unsigned int sz;
+	void *page[2];
+
+	/* get param */
+
+	/* Invalid size. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	/* Query size. */
+	arg.size = 0;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	sz = arg.size;
+
+	/* Bad pointers. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* set param */
+
+	/* Invalid sizes. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	arg.size = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	arg.size = sz;
+
+	/* Bad pointers. */
+	arg.value = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Unmapepd. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Read-only. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	igt_assert(mprotect(page[0], 4096, PROT_READ) == 0);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(page[0]) -
+		    sizeof(struct drm_i915_gem_context_param_sseu) - 4;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	/* Straddle into read-only area. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	igt_assert(mprotect(page[1], 4096, PROT_READ) == 0);
+	arg.value = to_user_pointer(page[0]) -
+		    sizeof(struct drm_i915_gem_context_param_sseu) - 4;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+	munmap(page[1], 4096);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid SSEU values are rejected.
+ */
+static void
+test_invalid_sseu(int fd)
+{
+	struct drm_i915_gem_context_param_sseu default_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		};
+	unsigned int i;
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&default_sseu);
+	gem_context_get_param(fd, &arg);
+
+	arg.value = to_user_pointer(&sseu);
+
+	/* Try all slice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __slice_count__); i++) {
+		sseu.slice_mask = mask_plus(__slice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 slices. */
+	sseu.slice_mask = 0;
+	igt_assert_eq(-EINVAL, __gem_context_set_param(fd, &arg));
+
+	/* Try all subslice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __subslice_count__); i++) {
+		sseu.subslice_mask = mask_plus(__subslice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 subslices. */
+	sseu.subslice_mask = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try number of EUs superior to the max available. */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try to program 0 max EUs. */
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Min > max */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = sseu.max_eus_per_subslice;
+	sseu.max_eus_per_subslice = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	if (__intel_gen__ != 11)
+		goto out;
+
+	/* Subset of subslices but slice mask greater than one. */
+	if (__slice_count__ > 1) {
+		sseu = default_sseu;
+		sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Odd subslices above four. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* More than half subslices with one slice. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 - 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+out:
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/* Verify that the kernel returns a correct error value on Gen < 8. */
+static void
+init_contexts(int fd, uint32_t *ctx, unsigned int num,
+	      uint64_t mask0, uint64_t mask1)
+{
+	unsigned int i;
+
+	igt_assert_eq(num, 2);
+
+	for (i = 0; i < num; i++)
+		ctx[i] = gem_context_create(fd);
+
+	context_set_slice_mask(fd, ctx[0], mask0);
+	context_set_slice_mask(fd, ctx[1], mask1);
+}
+
+/*
+ * Verify that powergating settings are put on hold while i915/perf is
+ * active.
+ */
+static void
+test_perf_oa(int fd)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, 1, /* test metric */
+		DRM_I915_PERF_PROP_OA_FORMAT, I915_OA_FORMAT_A32u40_A4u32_B8_C8,
+		DRM_I915_PERF_PROP_OA_EXPONENT, 20,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+		I915_PERF_FLAG_FD_NONBLOCK,
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int slice_count = __slice_count__ - 1;
+	uint32_t ctx[2];
+	unsigned int i;
+	int perf_fd;
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	/*
+	 * Now open i915/perf and verify that all contexts have been
+	 * reconfigured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1], slice_count),
+				      0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__), 0);
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+
+	/*
+	 * Open i915/perf first and verify that all contexts created
+	 * afterward are configured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Check the device's default values, despite setting
+	 * otherwise.
+	 */
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1],
+						       slice_count), 0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	struct igt_spin_factory opts = {
+		.ctx = ctx,
+		.engine = flags,
+	};
+
+	if (gem_can_store_dword(fd, flags))
+		opts.flags |= IGT_SPIN_POLL_RUN;
+
+	return __igt_spin_batch_factory(fd, &opts);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!READ_ONCE(*spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static uint32_t
+read_slice_count_busy(int fd, uint32_t context, uint32_t expected,
+		      igt_spin_t *spin)
+{
+	return __read_slice_count(fd, context, expected, spin);
+}
+
+#define TEST_IDLE (1)
+#define TEST_BUSY (2)
+
+/*
+ * Test context re-configuration with either idle or busy contexts.
+ */
+static void
+test_dynamic(int fd, unsigned int flags)
+{
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int pg_slice_count = __slice_count__ - 1;
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	igt_spin_t *spin = NULL;
+
+	gem_context_get_param(fd, &arg);
+
+	/* First set the default mask */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      __slice_count__);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* Then set a powergated configuration */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      pg_slice_count);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* Put the device's default back again */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      __slice_count__);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* One last powergated config for the road... */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      pg_slice_count);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+static void test_unsupported_device(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -ENODEV);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+static bool
+platform_has_per_context_sseu_support(void)
+{
+	return __intel_gen__ == 11;
+}
+
+igt_main
+{
+	unsigned int max_slices = 3, max_subslices = 3;
+	unsigned int i;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		__intel_devid__ = intel_get_drm_devid(fd);
+		__intel_gen__ = intel_gen(__intel_devid__);
+
+		igt_require(kernel_has_per_context_sseu_support(fd));
+
+		if (__intel_gen__ >= 11)
+			__read_slice_count = gen11_read_slice_count;
+		else
+			__read_slice_count = gen8_read_slice_count;
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			igt_require(!platform_has_per_context_sseu_support());
+		}
+
+		igt_subtest("unsupported-device")
+			test_unsupported_device(fd);
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			drm_i915_getparam_t gp;
+
+			igt_require(platform_has_per_context_sseu_support());
+
+			gp.param = I915_PARAM_SLICE_MASK;
+			gp.value = (int *) &__slice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__slice_count__ = __builtin_popcount(__slice_mask__);
+
+			gp.param = I915_PARAM_SUBSLICE_MASK;
+			gp.value = (int *) &__subslice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__subslice_count__ =
+				__builtin_popcount(__subslice_mask__);
+		}
+
+		igt_subtest("invalid-args")
+			test_invalid_args(fd);
+
+		igt_subtest("invalid-sseu")
+			test_invalid_sseu(fd);
+
+		igt_subtest("engines")
+			test_engines(fd);
+
+		for (i = 1; i < max_slices; i++) {
+			igt_subtest_f("slice-pg-%i", i) {
+				igt_require(__slice_count__ > i);
+
+				test_slice_pg(fd, i);
+			}
+		}
+
+		for (i = 1; i < max_subslices; i++) {
+			igt_subtest_f("subslice-pg-%i", i) {
+				igt_require(__subslice_count__ >= 2);
+
+				/*
+				 * Only available on some Atom platforms and
+				 * Gen10+.
+				 */
+				igt_require(IS_BROXTON(__intel_devid__) ||
+					    IS_GEMINILAKE(__intel_devid__) ||
+					    __intel_gen__ >= 10);
+
+				test_subslice_pg(fd, i);
+			}
+		}
+
+		igt_subtest("sseu-perf-oa") {
+			igt_require(__slice_count__ > 1);
+
+			test_perf_oa(fd);
+		}
+
+		igt_subtest("dynamic") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, 0);
+		}
+
+		igt_subtest("dynamic-busy") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, TEST_BUSY);
+		}
+
+		igt_subtest("dynamic-idle") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, TEST_IDLE);
+		}
+	}
+
+	igt_fixture {
+		close(fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 17deb945ec95..9319fa4e19b4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -249,6 +249,13 @@ foreach prog : test_progs
 		   install : true)
 endforeach
 
+test_executables += executable('gem_ctx_sseu', 'gem_ctx_sseu.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_ctx_sseu'
+
 test_executables += executable('gem_eio', 'gem_eio.c',
 	   dependencies : test_deps + [ realtime ],
 	   install_dir : libexecdir,
-- 
2.17.1

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* Re: [igt-dev] [PATH i-g-t v11 2/2] tests: add slice power programming test
  2018-09-11 11:34           ` [igt-dev] " Tvrtko Ursulin
@ 2018-09-11 11:45             ` Chris Wilson
  -1 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-11 11:45 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-11 12:34:17)
> +igt_main
> +{
> +       unsigned int max_slices = 3, max_subslices = 3;
> +       unsigned int i;
> +       int fd;
> +
> +       igt_fixture {
> +               fd = drm_open_driver(DRIVER_INTEL);
> +               igt_require_gem(fd);
> +
> +               __intel_devid__ = intel_get_drm_devid(fd);
> +               __intel_gen__ = intel_gen(__intel_devid__);
> +
> +               igt_require(kernel_has_per_context_sseu_support(fd));
> +       }
> +
> +       igt_subtest_group {
> +               igt_fixture {
> +                       igt_require(!platform_has_per_context_sseu_support());

Why does the kernel say that the platform has context sseu support?
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [igt-dev] [PATH i-g-t v11 2/2] tests: add slice power programming test
@ 2018-09-11 11:45             ` Chris Wilson
  0 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-11 11:45 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

Quoting Tvrtko Ursulin (2018-09-11 12:34:17)
> +igt_main
> +{
> +       unsigned int max_slices = 3, max_subslices = 3;
> +       unsigned int i;
> +       int fd;
> +
> +       igt_fixture {
> +               fd = drm_open_driver(DRIVER_INTEL);
> +               igt_require_gem(fd);
> +
> +               __intel_devid__ = intel_get_drm_devid(fd);
> +               __intel_gen__ = intel_gen(__intel_devid__);
> +
> +               igt_require(kernel_has_per_context_sseu_support(fd));
> +       }
> +
> +       igt_subtest_group {
> +               igt_fixture {
> +                       igt_require(!platform_has_per_context_sseu_support());

Why does the kernel say that the platform has context sseu support?
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* Re: [igt-dev] [PATH i-g-t v11 2/2] tests: add slice power programming test
  2018-09-11 11:45             ` Chris Wilson
@ 2018-09-11 12:00               ` Tvrtko Ursulin
  -1 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-11 12:00 UTC (permalink / raw)
  To: Chris Wilson, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx


On 11/09/2018 12:45, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2018-09-11 12:34:17)
>> +igt_main
>> +{
>> +       unsigned int max_slices = 3, max_subslices = 3;
>> +       unsigned int i;
>> +       int fd;
>> +
>> +       igt_fixture {
>> +               fd = drm_open_driver(DRIVER_INTEL);
>> +               igt_require_gem(fd);
>> +
>> +               __intel_devid__ = intel_get_drm_devid(fd);
>> +               __intel_gen__ = intel_gen(__intel_devid__);
>> +
>> +               igt_require(kernel_has_per_context_sseu_support(fd));
>> +       }
>> +
>> +       igt_subtest_group {
>> +               igt_fixture {
>> +                       igt_require(!platform_has_per_context_sseu_support());
> 
> Why does the kernel say that the platform has context sseu support?

Note sure what are you aiming at. Here I have a subgroup of tests (well 
one) which test that the kernel and IGT expectation of which platforms 
should support the feature match.

Regards,

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

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

* Re: [Intel-gfx] [igt-dev] [PATH i-g-t v11 2/2] tests: add slice power programming test
@ 2018-09-11 12:00               ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-11 12:00 UTC (permalink / raw)
  To: Chris Wilson, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx


On 11/09/2018 12:45, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2018-09-11 12:34:17)
>> +igt_main
>> +{
>> +       unsigned int max_slices = 3, max_subslices = 3;
>> +       unsigned int i;
>> +       int fd;
>> +
>> +       igt_fixture {
>> +               fd = drm_open_driver(DRIVER_INTEL);
>> +               igt_require_gem(fd);
>> +
>> +               __intel_devid__ = intel_get_drm_devid(fd);
>> +               __intel_gen__ = intel_gen(__intel_devid__);
>> +
>> +               igt_require(kernel_has_per_context_sseu_support(fd));
>> +       }
>> +
>> +       igt_subtest_group {
>> +               igt_fixture {
>> +                       igt_require(!platform_has_per_context_sseu_support());
> 
> Why does the kernel say that the platform has context sseu support?

Note sure what are you aiming at. Here I have a subgroup of tests (well 
one) which test that the kernel and IGT expectation of which platforms 
should support the feature match.

Regards,

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

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

* [PATH i-g-t v12 2/2] tests: add slice power programming test
  2018-09-11 12:00               ` [Intel-gfx] " Tvrtko Ursulin
@ 2018-09-11 14:42                 ` Tvrtko Ursulin
  -1 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-11 14:42 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

Verifies that the kernel programs slices correctly based by reading
the value of PWR_CLK_STATE register or MI_SET_PREDICATE on platforms
before Cannonlake.

v2: Add subslice tests (Lionel)
    Use MI_SET_PREDICATE for further verification when available (Lionel)

v3: Rename to gem_ctx_rpcs (Lionel)

v4: Update kernel API (Lionel)
    Add 0 value test (Lionel)
    Exercise invalid values (Lionel)

v5: Add perf tests (Lionel)

v6: Add new sysfs entry tests (Lionel)

v7: Test rsvd fields
    Update for kernel series changes

v8: Drop test_no_sseu_support() test (Kelvin)
    Drop drm_intel_*() apis (Chris)

v9: by Chris:
    Drop all do_ioctl/do_ioctl_err()
    Use gem_context_[gs]et_param()
    Use gem_read() instead of mapping memory
    by Lionel:
    Test dynamic sseu on/off more

Tvrtko Ursulin:

v10:
 * Various style tweaks and refactorings.
 * New test coverage.

v11:
 * Change platform support to just Gen11.
 * Simplify availability test. (Chris Wilson)
 * More invalid pointer tests. (Chris Wilson)

v12:
 * Fix MAP_FIXED use (doh!).
 * Fix get/set copy&paste errors.
 * Drop supported platform test. (Chris Wilson)
 * Add mmap__gtt test. (Chris Wilson)

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tests/Makefile.am      |    1 +
 tests/Makefile.sources |    1 +
 tests/gem_ctx_param.c  |    4 +-
 tests/gem_ctx_sseu.c   | 1128 ++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |    7 +
 5 files changed, 1140 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_sseu.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index ee5a7c5e83b8..6b67bd2cc17a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -107,6 +107,7 @@ gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
+gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
 gem_fence_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index c84933f1d971..f8f2c8d67d72 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -56,6 +56,7 @@ TESTS_progs = \
 	gem_ctx_exec \
 	gem_ctx_isolation \
 	gem_ctx_param \
+	gem_ctx_sseu \
 	gem_ctx_switch \
 	gem_ctx_thrash \
 	gem_double_irq_loop \
diff --git a/tests/gem_ctx_param.c b/tests/gem_ctx_param.c
index c46fd709b0d7..af1afeaa2f2f 100644
--- a/tests/gem_ctx_param.c
+++ b/tests/gem_ctx_param.c
@@ -294,11 +294,13 @@ igt_main
 			set_priority(fd);
 	}
 
+	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
+
 	/* NOTE: This testcase intentionally tests for the next free parameter
 	 * to catch ABI extensions. Don't "fix" this testcase without adding all
 	 * the tests for the new param first.
 	 */
-	arg.param = I915_CONTEXT_PARAM_PRIORITY + 1;
+	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
 
 	igt_subtest("invalid-param-get") {
 		arg.ctx_id = ctx;
diff --git a/tests/gem_ctx_sseu.c b/tests/gem_ctx_sseu.c
new file mode 100644
index 000000000000..b38b1e527767
--- /dev/null
+++ b/tests/gem_ctx_sseu.c
@@ -0,0 +1,1128 @@
+/*
+ * Copyright © 2017-2018 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.
+ *
+ * Authors:
+ *    Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include "igt_dummyload.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+#include "ioctl_wrappers.h"
+
+IGT_TEST_DESCRIPTION("Test context render powergating programming.");
+
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+
+#define MI_SET_PREDICATE      (0x1 << 23)
+#define  MI_SET_PREDICATE_NOOP_NEVER         (0)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_SET   (2)
+#define  MI_SET_PREDICATE_NOOP_RESULT_CLEAR  (3)
+#define  MI_SET_PREDICATE_NOOP_RESULT_SET    (4)
+#define  MI_SET_PREDICATE_1_SLICES           (5)
+#define  MI_SET_PREDICATE_2_SLICES           (6)
+#define  MI_SET_PREDICATE_3_SLICES           (7)
+
+#define GEN8_R_PWR_CLK_STATE		0x20C8
+#define   GEN8_RPCS_ENABLE		(1 << 31)
+#define   GEN8_RPCS_S_CNT_ENABLE	(1 << 18)
+#define   GEN8_RPCS_S_CNT_SHIFT		15
+#define   GEN8_RPCS_S_CNT_MASK		(0x7 << GEN8_RPCS_S_CNT_SHIFT)
+#define   GEN11_RPCS_S_CNT_SHIFT	12
+#define   GEN11_RPCS_S_CNT_MASK		(0x3f << GEN11_RPCS_S_CNT_SHIFT)
+#define   GEN8_RPCS_SS_CNT_ENABLE	(1 << 11)
+#define   GEN8_RPCS_SS_CNT_SHIFT	8
+#define   GEN8_RPCS_SS_CNT_MASK		(0x7 << GEN8_RPCS_SS_CNT_SHIFT)
+#define   GEN8_RPCS_EU_MAX_SHIFT	4
+#define   GEN8_RPCS_EU_MAX_MASK		(0xf << GEN8_RPCS_EU_MAX_SHIFT)
+#define   GEN8_RPCS_EU_MIN_SHIFT	0
+#define   GEN8_RPCS_EU_MIN_MASK		(0xf << GEN8_RPCS_EU_MIN_SHIFT)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+static unsigned int __intel_gen__, __intel_devid__;
+static uint64_t __slice_mask__, __subslice_mask__;
+static unsigned int __slice_count__, __subslice_count__;
+
+static uint64_t mask_minus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if ((1ULL << i) & mask)
+			return mask & ~(1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_plus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if (((1ULL << i) & mask) == 0)
+			return mask | (1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_minus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_minus_one(mask);
+
+	return mask;
+}
+
+static uint64_t mask_plus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_plus_one(mask);
+
+	return mask;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+		struct drm_i915_gem_relocation_entry *reloc,
+		uint32_t gem_handle, uint32_t delta, /* in bytes */
+		uint32_t offset, /* in dwords */
+		uint32_t read_domains, uint32_t write_domains)
+{
+	reloc->target_handle = gem_handle;
+	reloc->delta = delta;
+	reloc->offset = offset * sizeof(uint32_t);
+	reloc->presumed_offset = 0;
+	reloc->read_domains = read_domains;
+	reloc->write_domain = write_domains;
+
+	*batch++ = delta;
+	*batch++ = 0;
+
+	return batch;
+}
+
+
+static uint32_t
+read_rpcs_reg(int fd, uint32_t ctx, uint32_t expected_slices, igt_spin_t *spin)
+{
+	struct drm_i915_gem_execbuffer2 execbuf = { };
+	struct drm_i915_gem_relocation_entry relocs[2];
+	struct drm_i915_gem_exec_object2 obj[2];
+	uint32_t *batch, *b, data[2];
+	unsigned int n_relocs = 0;
+	uint32_t rpcs;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[1].handle = gem_create(fd, 4096);
+
+	batch = b =
+	      gem_mmap__cpu(fd, obj[1].handle, 0, 4096, PROT_READ | PROT_WRITE);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) |
+		       (MI_SET_PREDICATE_1_SLICES + expected_slices - 1);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = RCS_TIMESTAMP;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    0, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = GEN8_R_PWR_CLK_STATE;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    4, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) | MI_SET_PREDICATE_NOOP_NEVER;
+
+	*b++ = MI_BATCH_BUFFER_END;
+
+	gem_munmap(batch, 4096);
+
+	obj[1].relocation_count = n_relocs;
+	obj[1].relocs_ptr = to_user_pointer(relocs);
+
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+	i915_execbuffer2_set_context_id(execbuf, ctx);
+
+	gem_execbuf(fd, &execbuf);
+
+	if (spin)
+		igt_spin_batch_end(spin);
+
+	gem_read(fd, obj[0].handle, 0, data, sizeof(data));
+
+	rpcs = data[1];
+
+	igt_debug("rcs_timestamp=0x%x rpcs=0x%x\n", data[0], data[1]);
+
+	gem_close(fd, obj[0].handle);
+	gem_close(fd, obj[1].handle);
+
+	return rpcs;
+}
+
+typedef uint32_t (*read_slice_count_f)(int fd, uint32_t ctx, uint32_t expected,
+				       igt_spin_t *spin);
+
+static read_slice_count_f __read_slice_count;
+
+static uint32_t
+read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count)
+{
+	return __read_slice_count(fd, ctx, expected_slice_count, NULL);
+}
+
+static uint32_t
+gen8_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		      igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN8_RPCS_S_CNT_MASK) >> GEN8_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+gen11_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		       igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN11_RPCS_S_CNT_MASK) >> GEN11_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+read_subslice_count(int fd, uint32_t ctx)
+{
+	return (read_rpcs_reg(fd, ctx, 0, NULL) & GEN8_RPCS_SS_CNT_MASK) >>
+	       GEN8_RPCS_SS_CNT_SHIFT;
+}
+
+static bool
+kernel_has_per_context_sseu_support(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{
+		  .param = I915_CONTEXT_PARAM_SSEU,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu),
+		};
+	int ret;
+
+	if (__gem_context_get_param(fd, &arg))
+		return false;
+
+	arg.value = to_user_pointer(&sseu);
+
+	ret = __gem_context_set_param(fd, &arg);
+
+	igt_assert(ret == 0 || ret == -ENODEV || ret == -EINVAL);
+
+	return ret == 0;
+}
+
+static void
+context_get_sseu_masks(int fd, uint32_t ctx,
+		       uint64_t *slice_mask,
+		       uint64_t *subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+
+	if (slice_mask)
+		*slice_mask = sseu.slice_mask;
+
+	if (subslice_mask)
+		*subslice_mask = sseu.subslice_mask;
+}
+
+static void
+context_set_slice_mask(int fd, uint32_t ctx, uint64_t slice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.slice_mask = slice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+static void
+context_set_subslice_mask(int fd, uint32_t ctx, uint64_t subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.subslice_mask = subslice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+/*
+ * Verify that we can program the slice count.
+ */
+static void
+test_slice_pg(int fd, uint32_t pg_slice_count)
+{
+	uint64_t pg_slice_mask = mask_minus(__slice_mask__, pg_slice_count);
+	unsigned int slice_count = __slice_count__ - pg_slice_count;
+	uint64_t slice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(slice_count, __builtin_popcount(pg_slice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_slice_mask(fd, ctx, pg_slice_mask);
+	context_get_sseu_masks(fd, ctx, &slice_mask, NULL);
+	igt_assert_eq(pg_slice_mask, slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx, __slice_count__), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), slice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+/*
+ * Verify that we can program the subslice count.
+ */
+static void
+test_subslice_pg(int fd, int pg_subslice_count)
+{
+	uint64_t pg_subslice_mask =
+		mask_minus(__subslice_mask__, pg_subslice_count);
+	unsigned int subslice_count = __subslice_count__ - pg_subslice_count;
+	uint64_t subslice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(subslice_count, __builtin_popcount(pg_subslice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_subslice_mask(fd, ctx, pg_subslice_mask);
+	context_get_sseu_masks(fd, ctx, NULL, &subslice_mask);
+	igt_assert_eq(pg_subslice_mask, subslice_mask);
+
+	igt_assert_eq(read_subslice_count(fd, ctx), subslice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_engine(int fd, unsigned int class, unsigned int instance)
+{
+	int pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+
+	if (pmu >= 0)
+		close(pmu);
+
+	return pmu >= 0;
+}
+
+static bool
+engine_supports_sseu(int fd, unsigned int class, unsigned int instance)
+{
+	return __intel_gen__ >= 8 &&
+	       class == I915_ENGINE_CLASS_RENDER && instance == 0 ?
+	       true : false;
+}
+
+/*
+ * Verify that invalid engines are rejected and valid ones are accepted.
+ */
+static void test_engines(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	unsigned int class, instance;
+	int last_with_engines;
+
+	/* get_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+	sseu.class = I915_ENGINE_CLASS_RENDER;
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_get_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert_eq(ret, 0);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	/*
+	 * Get some proper values before trying to reprogram them onto
+	 * an invalid engine.
+	 */
+	sseu.class = 0;
+	sseu.instance = 0;
+	gem_context_get_param(fd, &arg);
+
+	/* set_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_set_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				if (engine_supports_sseu(fd, class, instance))
+					igt_assert_eq(ret, 0);
+				else
+					igt_assert_eq(ret, -ENODEV);
+
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid arguments are rejected.
+ */
+static void
+test_invalid_args(int fd)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		};
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	unsigned char *page[2];
+	unsigned char *addr;
+	unsigned int sz;
+
+	/* get param */
+
+	/* Invalid size. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	/* Query size. */
+	arg.size = 0;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	sz = arg.size;
+
+	/* Bad pointers. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(page[1]) -
+		    sizeof(struct drm_i915_gem_context_param_sseu) + 4;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	/* Straddle into read-only area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	igt_assert(mprotect(page[1], 4096, PROT_READ) == 0);
+	arg.value = to_user_pointer(page[1] - sizeof(sseu) + 4);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+	munmap(page[1], 4096);
+
+	/* set param */
+
+	/* Invalid sizes. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	arg.size = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	arg.size = sz;
+
+	/* Bad pointers. */
+	arg.value = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Get valid SSEU. */
+	arg.value = to_user_pointer(&sseu);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memcpy(page[0], &sseu, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	addr = page[1] - sizeof(sseu) + 4;
+	memcpy(addr, &sseu, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(addr);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that ggtt mapped area can be used as the sseu pointer.
+ */
+static void
+test_ggtt_args(int fd)
+{
+	struct drm_i915_gem_context_param_sseu *sseu;
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(*sseu),
+		};
+	uint32_t bo;
+
+	bo = gem_create(fd, 4096);
+	arg.value = to_user_pointer(gem_mmap__gtt(fd, bo, 4096,
+						  PROT_READ | PROT_WRITE));
+
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), 0);
+
+	munmap((void *)arg.value, 4096);
+	gem_close(fd, bo);
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid SSEU values are rejected.
+ */
+static void
+test_invalid_sseu(int fd)
+{
+	struct drm_i915_gem_context_param_sseu default_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		};
+	unsigned int i;
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&default_sseu);
+	gem_context_get_param(fd, &arg);
+
+	arg.value = to_user_pointer(&sseu);
+
+	/* Try all slice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __slice_count__); i++) {
+		sseu.slice_mask = mask_plus(__slice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 slices. */
+	sseu.slice_mask = 0;
+	igt_assert_eq(-EINVAL, __gem_context_set_param(fd, &arg));
+
+	/* Try all subslice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __subslice_count__); i++) {
+		sseu.subslice_mask = mask_plus(__subslice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 subslices. */
+	sseu.subslice_mask = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try number of EUs superior to the max available. */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try to program 0 max EUs. */
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Min > max */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = sseu.max_eus_per_subslice;
+	sseu.max_eus_per_subslice = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	if (__intel_gen__ != 11)
+		goto out;
+
+	/* Subset of subslices but slice mask greater than one. */
+	if (__slice_count__ > 1) {
+		sseu = default_sseu;
+		sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Odd subslices above four. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* More than half subslices with one slice. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 - 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+out:
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/* Verify that the kernel returns a correct error value on Gen < 8. */
+static void
+init_contexts(int fd, uint32_t *ctx, unsigned int num,
+	      uint64_t mask0, uint64_t mask1)
+{
+	unsigned int i;
+
+	igt_assert_eq(num, 2);
+
+	for (i = 0; i < num; i++)
+		ctx[i] = gem_context_create(fd);
+
+	context_set_slice_mask(fd, ctx[0], mask0);
+	context_set_slice_mask(fd, ctx[1], mask1);
+}
+
+/*
+ * Verify that powergating settings are put on hold while i915/perf is
+ * active.
+ */
+static void
+test_perf_oa(int fd)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, 1, /* test metric */
+		DRM_I915_PERF_PROP_OA_FORMAT, I915_OA_FORMAT_A32u40_A4u32_B8_C8,
+		DRM_I915_PERF_PROP_OA_EXPONENT, 20,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+		I915_PERF_FLAG_FD_NONBLOCK,
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int slice_count = __slice_count__ - 1;
+	uint32_t ctx[2];
+	unsigned int i;
+	int perf_fd;
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	/*
+	 * Now open i915/perf and verify that all contexts have been
+	 * reconfigured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1], slice_count),
+				      0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__), 0);
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+
+	/*
+	 * Open i915/perf first and verify that all contexts created
+	 * afterward are configured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Check the device's default values, despite setting
+	 * otherwise.
+	 */
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1],
+						       slice_count), 0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	struct igt_spin_factory opts = {
+		.ctx = ctx,
+		.engine = flags,
+	};
+
+	if (gem_can_store_dword(fd, flags))
+		opts.flags |= IGT_SPIN_POLL_RUN;
+
+	return __igt_spin_batch_factory(fd, &opts);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!READ_ONCE(*spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static uint32_t
+read_slice_count_busy(int fd, uint32_t context, uint32_t expected,
+		      igt_spin_t *spin)
+{
+	return __read_slice_count(fd, context, expected, spin);
+}
+
+#define TEST_IDLE (1)
+#define TEST_BUSY (2)
+
+/*
+ * Test context re-configuration with either idle or busy contexts.
+ */
+static void
+test_dynamic(int fd, unsigned int flags)
+{
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int pg_slice_count = __slice_count__ - 1;
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	igt_spin_t *spin = NULL;
+
+	gem_context_get_param(fd, &arg);
+
+	/* First set the default mask */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      __slice_count__);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* Then set a powergated configuration */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      pg_slice_count);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* Put the device's default back again */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      __slice_count__);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* One last powergated config for the road... */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      pg_slice_count);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+igt_main
+{
+	unsigned int max_slices = 3, max_subslices = 3;
+	unsigned int i;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		__intel_devid__ = intel_get_drm_devid(fd);
+		__intel_gen__ = intel_gen(__intel_devid__);
+
+		igt_require(kernel_has_per_context_sseu_support(fd));
+
+		if (__intel_gen__ >= 11)
+			__read_slice_count = gen11_read_slice_count;
+		else
+			__read_slice_count = gen8_read_slice_count;
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			drm_i915_getparam_t gp;
+
+			gp.param = I915_PARAM_SLICE_MASK;
+			gp.value = (int *) &__slice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__slice_count__ = __builtin_popcount(__slice_mask__);
+
+			gp.param = I915_PARAM_SUBSLICE_MASK;
+			gp.value = (int *) &__subslice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__subslice_count__ =
+				__builtin_popcount(__subslice_mask__);
+		}
+
+		igt_subtest("invalid-args")
+			test_invalid_args(fd);
+
+		igt_subtest("invalid-sseu")
+			test_invalid_sseu(fd);
+
+		igt_subtest("ggtt-args")
+			test_ggtt_args(fd);
+
+		igt_subtest("engines")
+			test_engines(fd);
+
+		for (i = 1; i < max_slices; i++) {
+			igt_subtest_f("slice-pg-%i", i) {
+				igt_require(__slice_count__ > i);
+
+				test_slice_pg(fd, i);
+			}
+		}
+
+		for (i = 1; i < max_subslices; i++) {
+			igt_subtest_f("subslice-pg-%i", i) {
+				igt_require(__subslice_count__ >= 2);
+
+				/*
+				 * Only available on some Atom platforms and
+				 * Gen10+.
+				 */
+				igt_require(IS_BROXTON(__intel_devid__) ||
+					    IS_GEMINILAKE(__intel_devid__) ||
+					    __intel_gen__ >= 10);
+
+				test_subslice_pg(fd, i);
+			}
+		}
+
+		igt_subtest("sseu-perf-oa") {
+			igt_require(__slice_count__ > 1);
+
+			test_perf_oa(fd);
+		}
+
+		igt_subtest("dynamic") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, 0);
+		}
+
+		igt_subtest("dynamic-busy") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, TEST_BUSY);
+		}
+
+		igt_subtest("dynamic-idle") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, TEST_IDLE);
+		}
+	}
+
+	igt_fixture {
+		close(fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 17deb945ec95..9319fa4e19b4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -249,6 +249,13 @@ foreach prog : test_progs
 		   install : true)
 endforeach
 
+test_executables += executable('gem_ctx_sseu', 'gem_ctx_sseu.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_ctx_sseu'
+
 test_executables += executable('gem_eio', 'gem_eio.c',
 	   dependencies : test_deps + [ realtime ],
 	   install_dir : libexecdir,
-- 
2.17.1

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

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

* [igt-dev] [PATH i-g-t v12 2/2] tests: add slice power programming test
@ 2018-09-11 14:42                 ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-11 14:42 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

Verifies that the kernel programs slices correctly based by reading
the value of PWR_CLK_STATE register or MI_SET_PREDICATE on platforms
before Cannonlake.

v2: Add subslice tests (Lionel)
    Use MI_SET_PREDICATE for further verification when available (Lionel)

v3: Rename to gem_ctx_rpcs (Lionel)

v4: Update kernel API (Lionel)
    Add 0 value test (Lionel)
    Exercise invalid values (Lionel)

v5: Add perf tests (Lionel)

v6: Add new sysfs entry tests (Lionel)

v7: Test rsvd fields
    Update for kernel series changes

v8: Drop test_no_sseu_support() test (Kelvin)
    Drop drm_intel_*() apis (Chris)

v9: by Chris:
    Drop all do_ioctl/do_ioctl_err()
    Use gem_context_[gs]et_param()
    Use gem_read() instead of mapping memory
    by Lionel:
    Test dynamic sseu on/off more

Tvrtko Ursulin:

v10:
 * Various style tweaks and refactorings.
 * New test coverage.

v11:
 * Change platform support to just Gen11.
 * Simplify availability test. (Chris Wilson)
 * More invalid pointer tests. (Chris Wilson)

v12:
 * Fix MAP_FIXED use (doh!).
 * Fix get/set copy&paste errors.
 * Drop supported platform test. (Chris Wilson)
 * Add mmap__gtt test. (Chris Wilson)

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tests/Makefile.am      |    1 +
 tests/Makefile.sources |    1 +
 tests/gem_ctx_param.c  |    4 +-
 tests/gem_ctx_sseu.c   | 1128 ++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |    7 +
 5 files changed, 1140 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_sseu.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index ee5a7c5e83b8..6b67bd2cc17a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -107,6 +107,7 @@ gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
+gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
 gem_fence_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index c84933f1d971..f8f2c8d67d72 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -56,6 +56,7 @@ TESTS_progs = \
 	gem_ctx_exec \
 	gem_ctx_isolation \
 	gem_ctx_param \
+	gem_ctx_sseu \
 	gem_ctx_switch \
 	gem_ctx_thrash \
 	gem_double_irq_loop \
diff --git a/tests/gem_ctx_param.c b/tests/gem_ctx_param.c
index c46fd709b0d7..af1afeaa2f2f 100644
--- a/tests/gem_ctx_param.c
+++ b/tests/gem_ctx_param.c
@@ -294,11 +294,13 @@ igt_main
 			set_priority(fd);
 	}
 
+	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
+
 	/* NOTE: This testcase intentionally tests for the next free parameter
 	 * to catch ABI extensions. Don't "fix" this testcase without adding all
 	 * the tests for the new param first.
 	 */
-	arg.param = I915_CONTEXT_PARAM_PRIORITY + 1;
+	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
 
 	igt_subtest("invalid-param-get") {
 		arg.ctx_id = ctx;
diff --git a/tests/gem_ctx_sseu.c b/tests/gem_ctx_sseu.c
new file mode 100644
index 000000000000..b38b1e527767
--- /dev/null
+++ b/tests/gem_ctx_sseu.c
@@ -0,0 +1,1128 @@
+/*
+ * Copyright © 2017-2018 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.
+ *
+ * Authors:
+ *    Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include "igt_dummyload.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+#include "ioctl_wrappers.h"
+
+IGT_TEST_DESCRIPTION("Test context render powergating programming.");
+
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+
+#define MI_SET_PREDICATE      (0x1 << 23)
+#define  MI_SET_PREDICATE_NOOP_NEVER         (0)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_SET   (2)
+#define  MI_SET_PREDICATE_NOOP_RESULT_CLEAR  (3)
+#define  MI_SET_PREDICATE_NOOP_RESULT_SET    (4)
+#define  MI_SET_PREDICATE_1_SLICES           (5)
+#define  MI_SET_PREDICATE_2_SLICES           (6)
+#define  MI_SET_PREDICATE_3_SLICES           (7)
+
+#define GEN8_R_PWR_CLK_STATE		0x20C8
+#define   GEN8_RPCS_ENABLE		(1 << 31)
+#define   GEN8_RPCS_S_CNT_ENABLE	(1 << 18)
+#define   GEN8_RPCS_S_CNT_SHIFT		15
+#define   GEN8_RPCS_S_CNT_MASK		(0x7 << GEN8_RPCS_S_CNT_SHIFT)
+#define   GEN11_RPCS_S_CNT_SHIFT	12
+#define   GEN11_RPCS_S_CNT_MASK		(0x3f << GEN11_RPCS_S_CNT_SHIFT)
+#define   GEN8_RPCS_SS_CNT_ENABLE	(1 << 11)
+#define   GEN8_RPCS_SS_CNT_SHIFT	8
+#define   GEN8_RPCS_SS_CNT_MASK		(0x7 << GEN8_RPCS_SS_CNT_SHIFT)
+#define   GEN8_RPCS_EU_MAX_SHIFT	4
+#define   GEN8_RPCS_EU_MAX_MASK		(0xf << GEN8_RPCS_EU_MAX_SHIFT)
+#define   GEN8_RPCS_EU_MIN_SHIFT	0
+#define   GEN8_RPCS_EU_MIN_MASK		(0xf << GEN8_RPCS_EU_MIN_SHIFT)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+static unsigned int __intel_gen__, __intel_devid__;
+static uint64_t __slice_mask__, __subslice_mask__;
+static unsigned int __slice_count__, __subslice_count__;
+
+static uint64_t mask_minus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if ((1ULL << i) & mask)
+			return mask & ~(1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_plus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if (((1ULL << i) & mask) == 0)
+			return mask | (1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_minus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_minus_one(mask);
+
+	return mask;
+}
+
+static uint64_t mask_plus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_plus_one(mask);
+
+	return mask;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+		struct drm_i915_gem_relocation_entry *reloc,
+		uint32_t gem_handle, uint32_t delta, /* in bytes */
+		uint32_t offset, /* in dwords */
+		uint32_t read_domains, uint32_t write_domains)
+{
+	reloc->target_handle = gem_handle;
+	reloc->delta = delta;
+	reloc->offset = offset * sizeof(uint32_t);
+	reloc->presumed_offset = 0;
+	reloc->read_domains = read_domains;
+	reloc->write_domain = write_domains;
+
+	*batch++ = delta;
+	*batch++ = 0;
+
+	return batch;
+}
+
+
+static uint32_t
+read_rpcs_reg(int fd, uint32_t ctx, uint32_t expected_slices, igt_spin_t *spin)
+{
+	struct drm_i915_gem_execbuffer2 execbuf = { };
+	struct drm_i915_gem_relocation_entry relocs[2];
+	struct drm_i915_gem_exec_object2 obj[2];
+	uint32_t *batch, *b, data[2];
+	unsigned int n_relocs = 0;
+	uint32_t rpcs;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[1].handle = gem_create(fd, 4096);
+
+	batch = b =
+	      gem_mmap__cpu(fd, obj[1].handle, 0, 4096, PROT_READ | PROT_WRITE);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) |
+		       (MI_SET_PREDICATE_1_SLICES + expected_slices - 1);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = RCS_TIMESTAMP;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    0, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = GEN8_R_PWR_CLK_STATE;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    4, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) | MI_SET_PREDICATE_NOOP_NEVER;
+
+	*b++ = MI_BATCH_BUFFER_END;
+
+	gem_munmap(batch, 4096);
+
+	obj[1].relocation_count = n_relocs;
+	obj[1].relocs_ptr = to_user_pointer(relocs);
+
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+	i915_execbuffer2_set_context_id(execbuf, ctx);
+
+	gem_execbuf(fd, &execbuf);
+
+	if (spin)
+		igt_spin_batch_end(spin);
+
+	gem_read(fd, obj[0].handle, 0, data, sizeof(data));
+
+	rpcs = data[1];
+
+	igt_debug("rcs_timestamp=0x%x rpcs=0x%x\n", data[0], data[1]);
+
+	gem_close(fd, obj[0].handle);
+	gem_close(fd, obj[1].handle);
+
+	return rpcs;
+}
+
+typedef uint32_t (*read_slice_count_f)(int fd, uint32_t ctx, uint32_t expected,
+				       igt_spin_t *spin);
+
+static read_slice_count_f __read_slice_count;
+
+static uint32_t
+read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count)
+{
+	return __read_slice_count(fd, ctx, expected_slice_count, NULL);
+}
+
+static uint32_t
+gen8_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		      igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN8_RPCS_S_CNT_MASK) >> GEN8_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+gen11_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		       igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN11_RPCS_S_CNT_MASK) >> GEN11_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+read_subslice_count(int fd, uint32_t ctx)
+{
+	return (read_rpcs_reg(fd, ctx, 0, NULL) & GEN8_RPCS_SS_CNT_MASK) >>
+	       GEN8_RPCS_SS_CNT_SHIFT;
+}
+
+static bool
+kernel_has_per_context_sseu_support(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{
+		  .param = I915_CONTEXT_PARAM_SSEU,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu),
+		};
+	int ret;
+
+	if (__gem_context_get_param(fd, &arg))
+		return false;
+
+	arg.value = to_user_pointer(&sseu);
+
+	ret = __gem_context_set_param(fd, &arg);
+
+	igt_assert(ret == 0 || ret == -ENODEV || ret == -EINVAL);
+
+	return ret == 0;
+}
+
+static void
+context_get_sseu_masks(int fd, uint32_t ctx,
+		       uint64_t *slice_mask,
+		       uint64_t *subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+
+	if (slice_mask)
+		*slice_mask = sseu.slice_mask;
+
+	if (subslice_mask)
+		*subslice_mask = sseu.subslice_mask;
+}
+
+static void
+context_set_slice_mask(int fd, uint32_t ctx, uint64_t slice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.slice_mask = slice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+static void
+context_set_subslice_mask(int fd, uint32_t ctx, uint64_t subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.subslice_mask = subslice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+/*
+ * Verify that we can program the slice count.
+ */
+static void
+test_slice_pg(int fd, uint32_t pg_slice_count)
+{
+	uint64_t pg_slice_mask = mask_minus(__slice_mask__, pg_slice_count);
+	unsigned int slice_count = __slice_count__ - pg_slice_count;
+	uint64_t slice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(slice_count, __builtin_popcount(pg_slice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_slice_mask(fd, ctx, pg_slice_mask);
+	context_get_sseu_masks(fd, ctx, &slice_mask, NULL);
+	igt_assert_eq(pg_slice_mask, slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx, __slice_count__), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), slice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+/*
+ * Verify that we can program the subslice count.
+ */
+static void
+test_subslice_pg(int fd, int pg_subslice_count)
+{
+	uint64_t pg_subslice_mask =
+		mask_minus(__subslice_mask__, pg_subslice_count);
+	unsigned int subslice_count = __subslice_count__ - pg_subslice_count;
+	uint64_t subslice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(subslice_count, __builtin_popcount(pg_subslice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_subslice_mask(fd, ctx, pg_subslice_mask);
+	context_get_sseu_masks(fd, ctx, NULL, &subslice_mask);
+	igt_assert_eq(pg_subslice_mask, subslice_mask);
+
+	igt_assert_eq(read_subslice_count(fd, ctx), subslice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_engine(int fd, unsigned int class, unsigned int instance)
+{
+	int pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+
+	if (pmu >= 0)
+		close(pmu);
+
+	return pmu >= 0;
+}
+
+static bool
+engine_supports_sseu(int fd, unsigned int class, unsigned int instance)
+{
+	return __intel_gen__ >= 8 &&
+	       class == I915_ENGINE_CLASS_RENDER && instance == 0 ?
+	       true : false;
+}
+
+/*
+ * Verify that invalid engines are rejected and valid ones are accepted.
+ */
+static void test_engines(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	unsigned int class, instance;
+	int last_with_engines;
+
+	/* get_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+	sseu.class = I915_ENGINE_CLASS_RENDER;
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_get_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert_eq(ret, 0);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	/*
+	 * Get some proper values before trying to reprogram them onto
+	 * an invalid engine.
+	 */
+	sseu.class = 0;
+	sseu.instance = 0;
+	gem_context_get_param(fd, &arg);
+
+	/* set_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_set_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				if (engine_supports_sseu(fd, class, instance))
+					igt_assert_eq(ret, 0);
+				else
+					igt_assert_eq(ret, -ENODEV);
+
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid arguments are rejected.
+ */
+static void
+test_invalid_args(int fd)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		};
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	unsigned char *page[2];
+	unsigned char *addr;
+	unsigned int sz;
+
+	/* get param */
+
+	/* Invalid size. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	/* Query size. */
+	arg.size = 0;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	sz = arg.size;
+
+	/* Bad pointers. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(page[1]) -
+		    sizeof(struct drm_i915_gem_context_param_sseu) + 4;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	/* Straddle into read-only area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	igt_assert(mprotect(page[1], 4096, PROT_READ) == 0);
+	arg.value = to_user_pointer(page[1] - sizeof(sseu) + 4);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+	munmap(page[1], 4096);
+
+	/* set param */
+
+	/* Invalid sizes. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	arg.size = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	arg.size = sz;
+
+	/* Bad pointers. */
+	arg.value = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Get valid SSEU. */
+	arg.value = to_user_pointer(&sseu);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memcpy(page[0], &sseu, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	addr = page[1] - sizeof(sseu) + 4;
+	memcpy(addr, &sseu, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(addr);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that ggtt mapped area can be used as the sseu pointer.
+ */
+static void
+test_ggtt_args(int fd)
+{
+	struct drm_i915_gem_context_param_sseu *sseu;
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(*sseu),
+		};
+	uint32_t bo;
+
+	bo = gem_create(fd, 4096);
+	arg.value = to_user_pointer(gem_mmap__gtt(fd, bo, 4096,
+						  PROT_READ | PROT_WRITE));
+
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), 0);
+
+	munmap((void *)arg.value, 4096);
+	gem_close(fd, bo);
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid SSEU values are rejected.
+ */
+static void
+test_invalid_sseu(int fd)
+{
+	struct drm_i915_gem_context_param_sseu default_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		};
+	unsigned int i;
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&default_sseu);
+	gem_context_get_param(fd, &arg);
+
+	arg.value = to_user_pointer(&sseu);
+
+	/* Try all slice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __slice_count__); i++) {
+		sseu.slice_mask = mask_plus(__slice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 slices. */
+	sseu.slice_mask = 0;
+	igt_assert_eq(-EINVAL, __gem_context_set_param(fd, &arg));
+
+	/* Try all subslice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __subslice_count__); i++) {
+		sseu.subslice_mask = mask_plus(__subslice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 subslices. */
+	sseu.subslice_mask = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try number of EUs superior to the max available. */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try to program 0 max EUs. */
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Min > max */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = sseu.max_eus_per_subslice;
+	sseu.max_eus_per_subslice = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	if (__intel_gen__ != 11)
+		goto out;
+
+	/* Subset of subslices but slice mask greater than one. */
+	if (__slice_count__ > 1) {
+		sseu = default_sseu;
+		sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Odd subslices above four. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* More than half subslices with one slice. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 - 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+out:
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/* Verify that the kernel returns a correct error value on Gen < 8. */
+static void
+init_contexts(int fd, uint32_t *ctx, unsigned int num,
+	      uint64_t mask0, uint64_t mask1)
+{
+	unsigned int i;
+
+	igt_assert_eq(num, 2);
+
+	for (i = 0; i < num; i++)
+		ctx[i] = gem_context_create(fd);
+
+	context_set_slice_mask(fd, ctx[0], mask0);
+	context_set_slice_mask(fd, ctx[1], mask1);
+}
+
+/*
+ * Verify that powergating settings are put on hold while i915/perf is
+ * active.
+ */
+static void
+test_perf_oa(int fd)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, 1, /* test metric */
+		DRM_I915_PERF_PROP_OA_FORMAT, I915_OA_FORMAT_A32u40_A4u32_B8_C8,
+		DRM_I915_PERF_PROP_OA_EXPONENT, 20,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+		I915_PERF_FLAG_FD_NONBLOCK,
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int slice_count = __slice_count__ - 1;
+	uint32_t ctx[2];
+	unsigned int i;
+	int perf_fd;
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	/*
+	 * Now open i915/perf and verify that all contexts have been
+	 * reconfigured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1], slice_count),
+				      0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__), 0);
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+
+	/*
+	 * Open i915/perf first and verify that all contexts created
+	 * afterward are configured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Check the device's default values, despite setting
+	 * otherwise.
+	 */
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1],
+						       slice_count), 0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	struct igt_spin_factory opts = {
+		.ctx = ctx,
+		.engine = flags,
+	};
+
+	if (gem_can_store_dword(fd, flags))
+		opts.flags |= IGT_SPIN_POLL_RUN;
+
+	return __igt_spin_batch_factory(fd, &opts);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!READ_ONCE(*spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static uint32_t
+read_slice_count_busy(int fd, uint32_t context, uint32_t expected,
+		      igt_spin_t *spin)
+{
+	return __read_slice_count(fd, context, expected, spin);
+}
+
+#define TEST_IDLE (1)
+#define TEST_BUSY (2)
+
+/*
+ * Test context re-configuration with either idle or busy contexts.
+ */
+static void
+test_dynamic(int fd, unsigned int flags)
+{
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int pg_slice_count = __slice_count__ - 1;
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	igt_spin_t *spin = NULL;
+
+	gem_context_get_param(fd, &arg);
+
+	/* First set the default mask */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      __slice_count__);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* Then set a powergated configuration */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      pg_slice_count);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* Put the device's default back again */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      __slice_count__);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	/* One last powergated config for the road... */
+	if (flags & TEST_BUSY)
+		spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
+
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+
+	igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
+		      pg_slice_count);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin) {
+		igt_spin_batch_free(fd, spin);
+		spin = NULL;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+igt_main
+{
+	unsigned int max_slices = 3, max_subslices = 3;
+	unsigned int i;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		__intel_devid__ = intel_get_drm_devid(fd);
+		__intel_gen__ = intel_gen(__intel_devid__);
+
+		igt_require(kernel_has_per_context_sseu_support(fd));
+
+		if (__intel_gen__ >= 11)
+			__read_slice_count = gen11_read_slice_count;
+		else
+			__read_slice_count = gen8_read_slice_count;
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			drm_i915_getparam_t gp;
+
+			gp.param = I915_PARAM_SLICE_MASK;
+			gp.value = (int *) &__slice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__slice_count__ = __builtin_popcount(__slice_mask__);
+
+			gp.param = I915_PARAM_SUBSLICE_MASK;
+			gp.value = (int *) &__subslice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__subslice_count__ =
+				__builtin_popcount(__subslice_mask__);
+		}
+
+		igt_subtest("invalid-args")
+			test_invalid_args(fd);
+
+		igt_subtest("invalid-sseu")
+			test_invalid_sseu(fd);
+
+		igt_subtest("ggtt-args")
+			test_ggtt_args(fd);
+
+		igt_subtest("engines")
+			test_engines(fd);
+
+		for (i = 1; i < max_slices; i++) {
+			igt_subtest_f("slice-pg-%i", i) {
+				igt_require(__slice_count__ > i);
+
+				test_slice_pg(fd, i);
+			}
+		}
+
+		for (i = 1; i < max_subslices; i++) {
+			igt_subtest_f("subslice-pg-%i", i) {
+				igt_require(__subslice_count__ >= 2);
+
+				/*
+				 * Only available on some Atom platforms and
+				 * Gen10+.
+				 */
+				igt_require(IS_BROXTON(__intel_devid__) ||
+					    IS_GEMINILAKE(__intel_devid__) ||
+					    __intel_gen__ >= 10);
+
+				test_subslice_pg(fd, i);
+			}
+		}
+
+		igt_subtest("sseu-perf-oa") {
+			igt_require(__slice_count__ > 1);
+
+			test_perf_oa(fd);
+		}
+
+		igt_subtest("dynamic") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, 0);
+		}
+
+		igt_subtest("dynamic-busy") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, TEST_BUSY);
+		}
+
+		igt_subtest("dynamic-idle") {
+			igt_require(__slice_count__ > 1);
+
+			test_dynamic(fd, TEST_IDLE);
+		}
+	}
+
+	igt_fixture {
+		close(fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 17deb945ec95..9319fa4e19b4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -249,6 +249,13 @@ foreach prog : test_progs
 		   install : true)
 endforeach
 
+test_executables += executable('gem_ctx_sseu', 'gem_ctx_sseu.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_ctx_sseu'
+
 test_executables += executable('gem_eio', 'gem_eio.c',
 	   dependencies : test_deps + [ realtime ],
 	   install_dir : libexecdir,
-- 
2.17.1

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [igt-dev] ✓ Fi.CI.BAT: success for Per context dynamic (sub)slice power-gating (rev3)
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
                   ` (4 preceding siblings ...)
  (?)
@ 2018-09-11 18:13 ` Patchwork
  -1 siblings, 0 replies; 55+ messages in thread
From: Patchwork @ 2018-09-11 18:13 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: igt-dev

== Series Details ==

Series: Per context dynamic (sub)slice power-gating (rev3)
URL   : https://patchwork.freedesktop.org/series/49190/
State : success

== Summary ==

= CI Bug Log - changes from CI_DRM_4798 -> IGTPW_1823 =

== Summary - SUCCESS ==

  No regressions found.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/49190/revisions/3/mbox/

== Known issues ==

  Here are the changes found in IGTPW_1823 that come from known issues:

  === IGT changes ===

    ==== Issues hit ====

    igt@drv_selftest@live_coherency:
      fi-gdg-551:         PASS -> DMESG-FAIL (fdo#107164)

    igt@kms_psr@primary_page_flip:
      fi-icl-u:           PASS -> FAIL (fdo#107336)

    
    ==== Possible fixes ====

    igt@kms_frontbuffer_tracking@basic:
      fi-byt-clapper:     FAIL (fdo#103167) -> PASS

    igt@kms_pipe_crc_basic@hang-read-crc-pipe-a:
      fi-byt-clapper:     FAIL (fdo#107362, fdo#103191) -> PASS

    igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
      fi-blb-e6850:       INCOMPLETE (fdo#107718) -> PASS

    igt@prime_vgem@basic-fence-flip:
      fi-ilk-650:         FAIL (fdo#104008) -> PASS

    
  fdo#103167 https://bugs.freedesktop.org/show_bug.cgi?id=103167
  fdo#103191 https://bugs.freedesktop.org/show_bug.cgi?id=103191
  fdo#104008 https://bugs.freedesktop.org/show_bug.cgi?id=104008
  fdo#107164 https://bugs.freedesktop.org/show_bug.cgi?id=107164
  fdo#107336 https://bugs.freedesktop.org/show_bug.cgi?id=107336
  fdo#107362 https://bugs.freedesktop.org/show_bug.cgi?id=107362
  fdo#107718 https://bugs.freedesktop.org/show_bug.cgi?id=107718


== Participating hosts (48 -> 44) ==

  Additional (1): fi-hsw-4770r 
  Missing    (5): fi-ctg-p8600 fi-ilk-m540 fi-byt-squawks fi-bsw-cyan fi-hsw-4200u 


== Build changes ==

    * IGT: IGT_4637 -> IGTPW_1823

  CI_DRM_4798: b35a9812b9bbb5b562fd5b4faf7bf06fc80f59ee @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_1823: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1823/
  IGT_4637: 57e3d826dee154cb8664667db7660d854a707fc6 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools



== Testlist changes ==

+igt@gem_ctx_sseu@dynamic
+igt@gem_ctx_sseu@dynamic-busy
+igt@gem_ctx_sseu@dynamic-idle
+igt@gem_ctx_sseu@engines
+igt@gem_ctx_sseu@ggtt-args
+igt@gem_ctx_sseu@invalid-args
+igt@gem_ctx_sseu@invalid-sseu
+igt@gem_ctx_sseu@slice-pg-1
+igt@gem_ctx_sseu@slice-pg-2
+igt@gem_ctx_sseu@sseu-perf-oa
+igt@gem_ctx_sseu@subslice-pg-1
+igt@gem_ctx_sseu@subslice-pg-2

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1823/issues.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [igt-dev] ✓ Fi.CI.IGT: success for Per context dynamic (sub)slice power-gating (rev3)
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
                   ` (5 preceding siblings ...)
  (?)
@ 2018-09-11 23:31 ` Patchwork
  -1 siblings, 0 replies; 55+ messages in thread
From: Patchwork @ 2018-09-11 23:31 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: igt-dev

== Series Details ==

Series: Per context dynamic (sub)slice power-gating (rev3)
URL   : https://patchwork.freedesktop.org/series/49190/
State : success

== Summary ==

= CI Bug Log - changes from IGT_4637_full -> IGTPW_1823_full =

== Summary - WARNING ==

  Minor unknown changes coming with IGTPW_1823_full need to be verified
  manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_1823_full, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/49190/revisions/3/mbox/

== Possible new issues ==

  Here are the unknown changes that may have been introduced in IGTPW_1823_full:

  === IGT changes ===

    ==== Warnings ====

    igt@gem_tiled_swapping@non-threaded:
      shard-apl:          PASS -> SKIP

    igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-indfb-pgflip-blt:
      shard-hsw:          PASS -> SKIP +1

    igt@tools_test@tools_test:
      shard-apl:          SKIP -> PASS
      shard-glk:          SKIP -> PASS
      shard-snb:          SKIP -> PASS
      shard-hsw:          SKIP -> PASS +1
      shard-kbl:          SKIP -> PASS

    
== Known issues ==

  Here are the changes found in IGTPW_1823_full that come from known issues:

  === IGT changes ===

    ==== Issues hit ====

    igt@kms_atomic_transition@plane-all-modeset-transition-fencing:
      shard-glk:          PASS -> INCOMPLETE (fdo#103359, k.org#198133) +1

    igt@kms_available_modes_crc@available_mode_test_crc:
      shard-snb:          PASS -> FAIL (fdo#106641)

    igt@kms_ccs@pipe-b-missing-ccs-buffer:
      shard-kbl:          PASS -> DMESG-WARN (fdo#105602, fdo#103558) +19

    igt@kms_cursor_crc@cursor-256x256-suspend:
      shard-kbl:          PASS -> INCOMPLETE (fdo#103665)

    igt@kms_flip@2x-flip-vs-absolute-wf_vblank-interruptible:
      shard-snb:          SKIP -> INCOMPLETE (fdo#105411)

    igt@kms_setmode@basic:
      shard-apl:          PASS -> FAIL (fdo#99912)

    igt@kms_sysfs_edid_timing:
      shard-hsw:          PASS -> WARN (fdo#100047)
      shard-glk:          PASS -> WARN (fdo#100047)

    igt@testdisplay:
      shard-glk:          PASS -> INCOMPLETE (fdo#103359, k.org#198133, fdo#107093)

    
    ==== Possible fixes ====

    igt@gem_ctx_isolation@bcs0-s3:
      shard-kbl:          INCOMPLETE (fdo#103665) -> PASS

    igt@kms_cursor_legacy@2x-long-cursor-vs-flip-atomic:
      shard-hsw:          FAIL (fdo#105767) -> PASS

    igt@kms_flip@dpms-vs-vblank-race-interruptible:
      shard-glk:          FAIL (fdo#103060) -> PASS

    igt@kms_frontbuffer_tracking@fbc-2p-primscrn-pri-indfb-draw-mmap-wc:
      shard-glk:          FAIL (fdo#103167) -> PASS +1

    igt@kms_plane_multiple@atomic-pipe-a-tiling-x:
      shard-snb:          FAIL (fdo#103166) -> PASS

    igt@kms_rotation_crc@primary-rotation-180:
      shard-snb:          FAIL (fdo#103925) -> PASS

    igt@perf@blocking:
      shard-hsw:          FAIL (fdo#107900) -> PASS

    
  fdo#100047 https://bugs.freedesktop.org/show_bug.cgi?id=100047
  fdo#103060 https://bugs.freedesktop.org/show_bug.cgi?id=103060
  fdo#103166 https://bugs.freedesktop.org/show_bug.cgi?id=103166
  fdo#103167 https://bugs.freedesktop.org/show_bug.cgi?id=103167
  fdo#103359 https://bugs.freedesktop.org/show_bug.cgi?id=103359
  fdo#103558 https://bugs.freedesktop.org/show_bug.cgi?id=103558
  fdo#103665 https://bugs.freedesktop.org/show_bug.cgi?id=103665
  fdo#103925 https://bugs.freedesktop.org/show_bug.cgi?id=103925
  fdo#105411 https://bugs.freedesktop.org/show_bug.cgi?id=105411
  fdo#105602 https://bugs.freedesktop.org/show_bug.cgi?id=105602
  fdo#105767 https://bugs.freedesktop.org/show_bug.cgi?id=105767
  fdo#106641 https://bugs.freedesktop.org/show_bug.cgi?id=106641
  fdo#107093 https://bugs.freedesktop.org/show_bug.cgi?id=107093
  fdo#107900 https://bugs.freedesktop.org/show_bug.cgi?id=107900
  fdo#99912 https://bugs.freedesktop.org/show_bug.cgi?id=99912
  k.org#198133 https://bugzilla.kernel.org/show_bug.cgi?id=198133


== Participating hosts (5 -> 5) ==

  No changes in participating hosts


== Build changes ==

    * IGT: IGT_4637 -> IGTPW_1823
    * Linux: CI_DRM_4791 -> CI_DRM_4798

  CI_DRM_4791: 07cf212bc704357ee60aba52ec40bab538222040 @ git://anongit.freedesktop.org/gfx-ci/linux
  CI_DRM_4798: b35a9812b9bbb5b562fd5b4faf7bf06fc80f59ee @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_1823: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1823/
  IGT_4637: 57e3d826dee154cb8664667db7660d854a707fc6 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1823/shards.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* Re: [PATH i-g-t v12 2/2] tests: add slice power programming test
  2018-09-11 14:42                 ` [igt-dev] " Tvrtko Ursulin
@ 2018-09-12 11:53                   ` Chris Wilson
  -1 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-12 11:53 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-11 15:42:10)
> +       last_with_engines = -1;
> +       for (class = 0; class < ~0; class++) {
> +               for (instance = 0; instance < ~0; instance++) {
> +                       int ret;
> +
> +                       sseu.class = class;
> +                       sseu.instance = instance;
> +
> +                       ret = __gem_context_set_param(fd, &arg);
> +
> +                       if (has_engine(fd, class, instance)) {
> +                               if (engine_supports_sseu(fd, class, instance))

Meh, <rant>. I just don't like having hardcoded db on this side of the
ABI. The ABI imo is to ask the kernel if the device/engine is supported,
and should not allow for assumptions.

> +static void
> +test_dynamic(int fd, unsigned int flags)
> +{
> +       uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
> +       unsigned int pg_slice_count = __slice_count__ - 1;
> +       struct drm_i915_gem_context_param_sseu sseu = { };
> +       struct drm_i915_gem_context_param arg =
> +               { .param = I915_CONTEXT_PARAM_SSEU,
> +                 .ctx_id = gem_context_create(fd),
> +                 .size = sizeof(sseu),
> +                 .value = to_user_pointer(&sseu) };
> +       igt_spin_t *spin = NULL;
> +
> +       gem_context_get_param(fd, &arg);
> +
> +       /* First set the default mask */
> +       if (flags & TEST_BUSY)
> +               spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
> +
> +       sseu.slice_mask = __slice_mask__;
> +       gem_context_set_param(fd, &arg);

I would also suggest a reset test here. Both reset when idle, and by
hangcheck/forced-reset of the spinner & active context.

And across suspend.

> +       igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
> +                     __slice_count__);
> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);

In the read_slice I would suggest having a
igt_assert(gem_bo_busy(spin->handle));
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [igt-dev] [Intel-gfx] [PATH i-g-t v12 2/2] tests: add slice power programming test
@ 2018-09-12 11:53                   ` Chris Wilson
  0 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-12 11:53 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-11 15:42:10)
> +       last_with_engines = -1;
> +       for (class = 0; class < ~0; class++) {
> +               for (instance = 0; instance < ~0; instance++) {
> +                       int ret;
> +
> +                       sseu.class = class;
> +                       sseu.instance = instance;
> +
> +                       ret = __gem_context_set_param(fd, &arg);
> +
> +                       if (has_engine(fd, class, instance)) {
> +                               if (engine_supports_sseu(fd, class, instance))

Meh, <rant>. I just don't like having hardcoded db on this side of the
ABI. The ABI imo is to ask the kernel if the device/engine is supported,
and should not allow for assumptions.

> +static void
> +test_dynamic(int fd, unsigned int flags)
> +{
> +       uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
> +       unsigned int pg_slice_count = __slice_count__ - 1;
> +       struct drm_i915_gem_context_param_sseu sseu = { };
> +       struct drm_i915_gem_context_param arg =
> +               { .param = I915_CONTEXT_PARAM_SSEU,
> +                 .ctx_id = gem_context_create(fd),
> +                 .size = sizeof(sseu),
> +                 .value = to_user_pointer(&sseu) };
> +       igt_spin_t *spin = NULL;
> +
> +       gem_context_get_param(fd, &arg);
> +
> +       /* First set the default mask */
> +       if (flags & TEST_BUSY)
> +               spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
> +
> +       sseu.slice_mask = __slice_mask__;
> +       gem_context_set_param(fd, &arg);

I would also suggest a reset test here. Both reset when idle, and by
hangcheck/forced-reset of the spinner & active context.

And across suspend.

> +       igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
> +                     __slice_count__);
> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);

In the read_slice I would suggest having a
igt_assert(gem_bo_busy(spin->handle));
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* Re: [PATH i-g-t v12 2/2] tests: add slice power programming test
  2018-09-12 11:53                   ` [igt-dev] [Intel-gfx] " Chris Wilson
@ 2018-09-13 10:38                     ` Tvrtko Ursulin
  -1 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-13 10:38 UTC (permalink / raw)
  To: Chris Wilson, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx


On 12/09/2018 12:53, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2018-09-11 15:42:10)
>> +       last_with_engines = -1;
>> +       for (class = 0; class < ~0; class++) {
>> +               for (instance = 0; instance < ~0; instance++) {
>> +                       int ret;
>> +
>> +                       sseu.class = class;
>> +                       sseu.instance = instance;
>> +
>> +                       ret = __gem_context_set_param(fd, &arg);
>> +
>> +                       if (has_engine(fd, class, instance)) {
>> +                               if (engine_supports_sseu(fd, class, instance))
> 
> Meh, <rant>. I just don't like having hardcoded db on this side of the
> ABI. The ABI imo is to ask the kernel if the device/engine is supported,
> and should not allow for assumptions.

Done.

>> +static void
>> +test_dynamic(int fd, unsigned int flags)
>> +{
>> +       uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
>> +       unsigned int pg_slice_count = __slice_count__ - 1;
>> +       struct drm_i915_gem_context_param_sseu sseu = { };
>> +       struct drm_i915_gem_context_param arg =
>> +               { .param = I915_CONTEXT_PARAM_SSEU,
>> +                 .ctx_id = gem_context_create(fd),
>> +                 .size = sizeof(sseu),
>> +                 .value = to_user_pointer(&sseu) };
>> +       igt_spin_t *spin = NULL;
>> +
>> +       gem_context_get_param(fd, &arg);
>> +
>> +       /* First set the default mask */
>> +       if (flags & TEST_BUSY)
>> +               spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
>> +
>> +       sseu.slice_mask = __slice_mask__;
>> +       gem_context_set_param(fd, &arg);
> 
> I would also suggest a reset test here. Both reset when idle, and by
> hangcheck/forced-reset of the spinner & active context.
> 
> And across suspend.

Reset & suspsend after set param but before execbuf? Or after execbuf 
and then re-read rpcs?

>> +       igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
>> +                     __slice_count__);
>> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
> 
> In the read_slice I would suggest having a
> igt_assert(gem_bo_busy(spin->handle));

Done.

Regards,

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

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

* Re: [igt-dev] [Intel-gfx] [PATH i-g-t v12 2/2] tests: add slice power programming test
@ 2018-09-13 10:38                     ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-13 10:38 UTC (permalink / raw)
  To: Chris Wilson, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx


On 12/09/2018 12:53, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2018-09-11 15:42:10)
>> +       last_with_engines = -1;
>> +       for (class = 0; class < ~0; class++) {
>> +               for (instance = 0; instance < ~0; instance++) {
>> +                       int ret;
>> +
>> +                       sseu.class = class;
>> +                       sseu.instance = instance;
>> +
>> +                       ret = __gem_context_set_param(fd, &arg);
>> +
>> +                       if (has_engine(fd, class, instance)) {
>> +                               if (engine_supports_sseu(fd, class, instance))
> 
> Meh, <rant>. I just don't like having hardcoded db on this side of the
> ABI. The ABI imo is to ask the kernel if the device/engine is supported,
> and should not allow for assumptions.

Done.

>> +static void
>> +test_dynamic(int fd, unsigned int flags)
>> +{
>> +       uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
>> +       unsigned int pg_slice_count = __slice_count__ - 1;
>> +       struct drm_i915_gem_context_param_sseu sseu = { };
>> +       struct drm_i915_gem_context_param arg =
>> +               { .param = I915_CONTEXT_PARAM_SSEU,
>> +                 .ctx_id = gem_context_create(fd),
>> +                 .size = sizeof(sseu),
>> +                 .value = to_user_pointer(&sseu) };
>> +       igt_spin_t *spin = NULL;
>> +
>> +       gem_context_get_param(fd, &arg);
>> +
>> +       /* First set the default mask */
>> +       if (flags & TEST_BUSY)
>> +               spin = __spin_sync(fd, arg.ctx_id, I915_EXEC_RENDER);
>> +
>> +       sseu.slice_mask = __slice_mask__;
>> +       gem_context_set_param(fd, &arg);
> 
> I would also suggest a reset test here. Both reset when idle, and by
> hangcheck/forced-reset of the spinner & active context.
> 
> And across suspend.

Reset & suspsend after set param but before execbuf? Or after execbuf 
and then re-read rpcs?

>> +       igt_assert_eq(read_slice_count_busy(fd, arg.ctx_id, 0, spin),
>> +                     __slice_count__);
>> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
> 
> In the read_slice I would suggest having a
> igt_assert(gem_bo_busy(spin->handle));

Done.

Regards,

Tvrtko
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* Re: [PATH i-g-t v12 2/2] tests: add slice power programming test
  2018-09-13 10:38                     ` [igt-dev] [Intel-gfx] " Tvrtko Ursulin
@ 2018-09-13 10:48                       ` Chris Wilson
  -1 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-13 10:48 UTC (permalink / raw)
  To: Tvrtko Ursulin, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-13 11:38:47)
> 
> On 12/09/2018 12:53, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2018-09-11 15:42:10)
> > I would also suggest a reset test here. Both reset when idle, and by
> > hangcheck/forced-reset of the spinner & active context.
> > 
> > And across suspend.
> 
> Reset & suspsend after set param but before execbuf? Or after execbuf 
> and then re-read rpcs?

My concern is making sure that after the reset/suspend the adjusted sseu
is still in effect.

For reset, I think the hardest case is if the spinning batch (causing us
to use the MI_STORE_DATA_IMM path) itself hangs. An idle/reset is only
borderline interesting. For that it's just the question of whether
resetting the gpu breaks everything -- but we reset the gpu frequently
enough (on load, on resume) that there's no getting away from it :)

Argh, there's another dilemma here... The wedged driver. In that case,
it should still work as although we may lose the request to set the sseu
in flight, if we ever use the gpu again, we will repin the context and
so reset the sseu.

For suspend, I can see an argument for both idle/suspend and
active/suspend to check that the settings are preserved across the
suspend. In the first case, the path we take will apply them afterwards,
in the latter case, we will apply the settings again on resume. So maybe
there isn't much point to the second case. However, it does all presume
that we do remember to repin the context (so probably worth exercising).
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [igt-dev] [Intel-gfx] [PATH i-g-t v12 2/2] tests: add slice power programming test
@ 2018-09-13 10:48                       ` Chris Wilson
  0 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-13 10:48 UTC (permalink / raw)
  To: Tvrtko Ursulin, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-13 11:38:47)
> 
> On 12/09/2018 12:53, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2018-09-11 15:42:10)
> > I would also suggest a reset test here. Both reset when idle, and by
> > hangcheck/forced-reset of the spinner & active context.
> > 
> > And across suspend.
> 
> Reset & suspsend after set param but before execbuf? Or after execbuf 
> and then re-read rpcs?

My concern is making sure that after the reset/suspend the adjusted sseu
is still in effect.

For reset, I think the hardest case is if the spinning batch (causing us
to use the MI_STORE_DATA_IMM path) itself hangs. An idle/reset is only
borderline interesting. For that it's just the question of whether
resetting the gpu breaks everything -- but we reset the gpu frequently
enough (on load, on resume) that there's no getting away from it :)

Argh, there's another dilemma here... The wedged driver. In that case,
it should still work as although we may lose the request to set the sseu
in flight, if we ever use the gpu again, we will repin the context and
so reset the sseu.

For suspend, I can see an argument for both idle/suspend and
active/suspend to check that the settings are preserved across the
suspend. In the first case, the path we take will apply them afterwards,
in the latter case, we will apply the settings again on resume. So maybe
there isn't much point to the second case. However, it does all presume
that we do remember to repin the context (so probably worth exercising).
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [PATCH i-g-t v13 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
  2018-09-11 14:42                 ` [igt-dev] " Tvrtko Ursulin
@ 2018-09-14 16:04                   ` Tvrtko Ursulin
  -1 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-14 16:04 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

Verify that the per-context dynamic SSEU uAPI works as expected.

To achieve that, in the absence of a better mechamism, we read the value
of PWR_CLK_STATE register, or use MI_SET_PREDICATE on platforms before
Cannonlake.

This register is written to by the GPU on context restore so this way
we verify i915 is correctly updating the context image in all
circumstances.

v2: Add subslice tests (Lionel)
    Use MI_SET_PREDICATE for further verification when available (Lionel)

v3: Rename to gem_ctx_rpcs (Lionel)

v4: Update kernel API (Lionel)
    Add 0 value test (Lionel)
    Exercise invalid values (Lionel)

v5: Add perf tests (Lionel)

v6: Add new sysfs entry tests (Lionel)

v7: Test rsvd fields
    Update for kernel series changes

v8: Drop test_no_sseu_support() test (Kelvin)
    Drop drm_intel_*() apis (Chris)

v9: by Chris:
    Drop all do_ioctl/do_ioctl_err()
    Use gem_context_[gs]et_param()
    Use gem_read() instead of mapping memory
    by Lionel:
    Test dynamic sseu on/off more

Tvrtko Ursulin:

v10:
 * Various style tweaks and refactorings.
 * New test coverage.

v11:
 * Change platform support to just Gen11.
 * Simplify availability test. (Chris Wilson)
 * More invalid pointer tests. (Chris Wilson)

v12:
 * Fix MAP_FIXED use (doh!).
 * Fix get/set copy&paste errors.
 * Drop supported platform test. (Chris Wilson)
 * Add mmap__gtt test. (Chris Wilson)

v13:
 * Commit message tweaks.
 * Added reset/hang/suspend tests. (Chris Wilson)
 * Assert spinner is busy. (Chris Wilson)
 * Remove some more ABI assumptions. (Chris Wilson)

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tests/Makefile.am      |    1 +
 tests/Makefile.sources |    1 +
 tests/gem_ctx_param.c  |    4 +-
 tests/gem_ctx_sseu.c   | 1128 ++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |    7 +
 5 files changed, 1140 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_sseu.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index ee5a7c5e83b8..6b67bd2cc17a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -107,6 +107,7 @@ gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
+gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
 gem_fence_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index c84933f1d971..f8f2c8d67d72 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -56,6 +56,7 @@ TESTS_progs = \
 	gem_ctx_exec \
 	gem_ctx_isolation \
 	gem_ctx_param \
+	gem_ctx_sseu \
 	gem_ctx_switch \
 	gem_ctx_thrash \
 	gem_double_irq_loop \
diff --git a/tests/gem_ctx_param.c b/tests/gem_ctx_param.c
index c46fd709b0d7..af1afeaa2f2f 100644
--- a/tests/gem_ctx_param.c
+++ b/tests/gem_ctx_param.c
@@ -294,11 +294,13 @@ igt_main
 			set_priority(fd);
 	}
 
+	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
+
 	/* NOTE: This testcase intentionally tests for the next free parameter
 	 * to catch ABI extensions. Don't "fix" this testcase without adding all
 	 * the tests for the new param first.
 	 */
-	arg.param = I915_CONTEXT_PARAM_PRIORITY + 1;
+	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
 
 	igt_subtest("invalid-param-get") {
 		arg.ctx_id = ctx;
diff --git a/tests/gem_ctx_sseu.c b/tests/gem_ctx_sseu.c
new file mode 100644
index 000000000000..4669c40f34fb
--- /dev/null
+++ b/tests/gem_ctx_sseu.c
@@ -0,0 +1,1128 @@
+/*
+ * Copyright © 2017-2018 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.
+ *
+ * Authors:
+ *    Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include "igt_dummyload.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+#include "ioctl_wrappers.h"
+
+IGT_TEST_DESCRIPTION("Test context render powergating programming.");
+
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+
+#define MI_SET_PREDICATE      (0x1 << 23)
+#define  MI_SET_PREDICATE_NOOP_NEVER         (0)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_SET   (2)
+#define  MI_SET_PREDICATE_NOOP_RESULT_CLEAR  (3)
+#define  MI_SET_PREDICATE_NOOP_RESULT_SET    (4)
+#define  MI_SET_PREDICATE_1_SLICES           (5)
+#define  MI_SET_PREDICATE_2_SLICES           (6)
+#define  MI_SET_PREDICATE_3_SLICES           (7)
+
+#define GEN8_R_PWR_CLK_STATE		0x20C8
+#define   GEN8_RPCS_ENABLE		(1 << 31)
+#define   GEN8_RPCS_S_CNT_ENABLE	(1 << 18)
+#define   GEN8_RPCS_S_CNT_SHIFT		15
+#define   GEN8_RPCS_S_CNT_MASK		(0x7 << GEN8_RPCS_S_CNT_SHIFT)
+#define   GEN11_RPCS_S_CNT_SHIFT	12
+#define   GEN11_RPCS_S_CNT_MASK		(0x3f << GEN11_RPCS_S_CNT_SHIFT)
+#define   GEN8_RPCS_SS_CNT_ENABLE	(1 << 11)
+#define   GEN8_RPCS_SS_CNT_SHIFT	8
+#define   GEN8_RPCS_SS_CNT_MASK		(0x7 << GEN8_RPCS_SS_CNT_SHIFT)
+#define   GEN8_RPCS_EU_MAX_SHIFT	4
+#define   GEN8_RPCS_EU_MAX_MASK		(0xf << GEN8_RPCS_EU_MAX_SHIFT)
+#define   GEN8_RPCS_EU_MIN_SHIFT	0
+#define   GEN8_RPCS_EU_MIN_MASK		(0xf << GEN8_RPCS_EU_MIN_SHIFT)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+static unsigned int __intel_gen__, __intel_devid__;
+static uint64_t __slice_mask__, __subslice_mask__;
+static unsigned int __slice_count__, __subslice_count__;
+
+static uint64_t mask_minus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if ((1ULL << i) & mask)
+			return mask & ~(1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_plus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if (((1ULL << i) & mask) == 0)
+			return mask | (1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_minus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_minus_one(mask);
+
+	return mask;
+}
+
+static uint64_t mask_plus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_plus_one(mask);
+
+	return mask;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+		struct drm_i915_gem_relocation_entry *reloc,
+		uint32_t gem_handle, uint32_t delta, /* in bytes */
+		uint32_t offset, /* in dwords */
+		uint32_t read_domains, uint32_t write_domains)
+{
+	reloc->target_handle = gem_handle;
+	reloc->delta = delta;
+	reloc->offset = offset * sizeof(uint32_t);
+	reloc->presumed_offset = 0;
+	reloc->read_domains = read_domains;
+	reloc->write_domain = write_domains;
+
+	*batch++ = delta;
+	*batch++ = 0;
+
+	return batch;
+}
+
+
+static uint32_t
+read_rpcs_reg(int fd, uint32_t ctx, uint32_t expected_slices, igt_spin_t *spin)
+{
+	struct drm_i915_gem_execbuffer2 execbuf = { };
+	struct drm_i915_gem_relocation_entry relocs[2];
+	struct drm_i915_gem_exec_object2 obj[2];
+	uint32_t *batch, *b, data[2];
+	unsigned int n_relocs = 0;
+	uint32_t rpcs;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[1].handle = gem_create(fd, 4096);
+
+	batch = b =
+	      gem_mmap__cpu(fd, obj[1].handle, 0, 4096, PROT_READ | PROT_WRITE);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) |
+		       (MI_SET_PREDICATE_1_SLICES + expected_slices - 1);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = RCS_TIMESTAMP;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    0, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = GEN8_R_PWR_CLK_STATE;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    4, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) | MI_SET_PREDICATE_NOOP_NEVER;
+
+	*b++ = MI_BATCH_BUFFER_END;
+
+	gem_munmap(batch, 4096);
+
+	obj[1].relocation_count = n_relocs;
+	obj[1].relocs_ptr = to_user_pointer(relocs);
+
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+	i915_execbuffer2_set_context_id(execbuf, ctx);
+
+	gem_execbuf(fd, &execbuf);
+
+	if (spin) {
+		igt_assert(gem_bo_busy(fd, spin->handle));
+		igt_spin_batch_end(spin);
+	}
+
+	gem_read(fd, obj[0].handle, 0, data, sizeof(data));
+
+	rpcs = data[1];
+
+	igt_debug("rcs_timestamp=0x%x rpcs=0x%x\n", data[0], data[1]);
+
+	gem_close(fd, obj[0].handle);
+	gem_close(fd, obj[1].handle);
+
+	return rpcs;
+}
+
+typedef uint32_t (*read_slice_count_f)(int fd, uint32_t ctx, uint32_t expected,
+				       igt_spin_t *spin);
+
+static read_slice_count_f __read_slice_count;
+
+static uint32_t
+read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count)
+{
+	return __read_slice_count(fd, ctx, expected_slice_count, NULL);
+}
+
+static uint32_t
+gen8_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		      igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN8_RPCS_S_CNT_MASK) >> GEN8_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+gen11_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		       igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN11_RPCS_S_CNT_MASK) >> GEN11_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+read_subslice_count(int fd, uint32_t ctx)
+{
+	return (read_rpcs_reg(fd, ctx, 0, NULL) & GEN8_RPCS_SS_CNT_MASK) >>
+	       GEN8_RPCS_SS_CNT_SHIFT;
+}
+
+static bool
+kernel_has_per_context_sseu_support(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{
+		  .param = I915_CONTEXT_PARAM_SSEU,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu),
+		};
+	int ret;
+
+	if (__gem_context_get_param(fd, &arg))
+		return false;
+
+	arg.value = to_user_pointer(&sseu);
+
+	ret = __gem_context_set_param(fd, &arg);
+
+	igt_assert(ret == 0 || ret == -ENODEV || ret == -EINVAL);
+
+	return ret == 0;
+}
+
+static void
+context_get_sseu_masks(int fd, uint32_t ctx,
+		       uint64_t *slice_mask,
+		       uint64_t *subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+
+	if (slice_mask)
+		*slice_mask = sseu.slice_mask;
+
+	if (subslice_mask)
+		*subslice_mask = sseu.subslice_mask;
+}
+
+static void
+context_set_slice_mask(int fd, uint32_t ctx, uint64_t slice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.slice_mask = slice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+static void
+context_set_subslice_mask(int fd, uint32_t ctx, uint64_t subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.subslice_mask = subslice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+/*
+ * Verify that we can program the slice count.
+ */
+static void
+test_slice_pg(int fd, uint32_t pg_slice_count)
+{
+	uint64_t pg_slice_mask = mask_minus(__slice_mask__, pg_slice_count);
+	unsigned int slice_count = __slice_count__ - pg_slice_count;
+	uint64_t slice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(slice_count, __builtin_popcount(pg_slice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_slice_mask(fd, ctx, pg_slice_mask);
+	context_get_sseu_masks(fd, ctx, &slice_mask, NULL);
+	igt_assert_eq(pg_slice_mask, slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx, __slice_count__), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), slice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+/*
+ * Verify that we can program the subslice count.
+ */
+static void
+test_subslice_pg(int fd, int pg_subslice_count)
+{
+	uint64_t pg_subslice_mask =
+		mask_minus(__subslice_mask__, pg_subslice_count);
+	unsigned int subslice_count = __subslice_count__ - pg_subslice_count;
+	uint64_t subslice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(subslice_count, __builtin_popcount(pg_subslice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_subslice_mask(fd, ctx, pg_subslice_mask);
+	context_get_sseu_masks(fd, ctx, NULL, &subslice_mask);
+	igt_assert_eq(pg_subslice_mask, subslice_mask);
+
+	igt_assert_eq(read_subslice_count(fd, ctx), subslice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_engine(int fd, unsigned int class, unsigned int instance)
+{
+	int pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+
+	if (pmu >= 0)
+		close(pmu);
+
+	return pmu >= 0;
+}
+
+/*
+ * Verify that invalid engines are rejected and valid ones are accepted.
+ */
+static void test_engines(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	unsigned int class, instance;
+	int last_with_engines;
+
+	/* get_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+	sseu.class = I915_ENGINE_CLASS_RENDER;
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_get_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert_eq(ret, 0);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	/*
+	 * Get some proper values before trying to reprogram them onto
+	 * an invalid engine.
+	 */
+	sseu.class = 0;
+	sseu.instance = 0;
+	gem_context_get_param(fd, &arg);
+
+	/* set_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_set_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert(ret == 0 || ret == -ENODEV);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid arguments are rejected.
+ */
+static void
+test_invalid_args(int fd)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		};
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	unsigned char *page[2];
+	unsigned char *addr;
+	unsigned int sz;
+
+	/* get param */
+
+	/* Invalid size. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	/* Query size. */
+	arg.size = 0;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	sz = arg.size;
+
+	/* Bad pointers. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(page[1]) -
+		    sizeof(struct drm_i915_gem_context_param_sseu) + 4;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	/* Straddle into read-only area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	igt_assert(mprotect(page[1], 4096, PROT_READ) == 0);
+	arg.value = to_user_pointer(page[1] - sizeof(sseu) + 4);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+	munmap(page[1], 4096);
+
+	/* set param */
+
+	/* Invalid sizes. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	arg.size = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	arg.size = sz;
+
+	/* Bad pointers. */
+	arg.value = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Get valid SSEU. */
+	arg.value = to_user_pointer(&sseu);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memcpy(page[0], &sseu, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	addr = page[1] - sizeof(sseu) + 4;
+	memcpy(addr, &sseu, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(addr);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that ggtt mapped area can be used as the sseu pointer.
+ */
+static void
+test_ggtt_args(int fd)
+{
+	struct drm_i915_gem_context_param_sseu *sseu;
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(*sseu),
+		};
+	uint32_t bo;
+
+	bo = gem_create(fd, 4096);
+	arg.value = to_user_pointer(gem_mmap__gtt(fd, bo, 4096,
+						  PROT_READ | PROT_WRITE));
+
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), 0);
+
+	munmap((void *)arg.value, 4096);
+	gem_close(fd, bo);
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid SSEU values are rejected.
+ */
+static void
+test_invalid_sseu(int fd)
+{
+	struct drm_i915_gem_context_param_sseu default_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		};
+	unsigned int i;
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&default_sseu);
+	gem_context_get_param(fd, &arg);
+
+	arg.value = to_user_pointer(&sseu);
+
+	/* Try all slice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __slice_count__); i++) {
+		sseu.slice_mask = mask_plus(__slice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 slices. */
+	sseu.slice_mask = 0;
+	igt_assert_eq(-EINVAL, __gem_context_set_param(fd, &arg));
+
+	/* Try all subslice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __subslice_count__); i++) {
+		sseu.subslice_mask = mask_plus(__subslice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 subslices. */
+	sseu.subslice_mask = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try number of EUs superior to the max available. */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try to program 0 max EUs. */
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Min > max */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = sseu.max_eus_per_subslice;
+	sseu.max_eus_per_subslice = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	if (__intel_gen__ != 11)
+		goto out;
+
+	/* Subset of subslices but slice mask greater than one. */
+	if (__slice_count__ > 1) {
+		sseu = default_sseu;
+		sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Odd subslices above four. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* More than half subslices with one slice. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 - 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+out:
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/* Verify that the kernel returns a correct error value on Gen < 8. */
+static void
+init_contexts(int fd, uint32_t *ctx, unsigned int num,
+	      uint64_t mask0, uint64_t mask1)
+{
+	unsigned int i;
+
+	igt_assert_eq(num, 2);
+
+	for (i = 0; i < num; i++)
+		ctx[i] = gem_context_create(fd);
+
+	context_set_slice_mask(fd, ctx[0], mask0);
+	context_set_slice_mask(fd, ctx[1], mask1);
+}
+
+/*
+ * Verify that powergating settings are put on hold while i915/perf is
+ * active.
+ */
+static void
+test_perf_oa(int fd)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, 1, /* test metric */
+		DRM_I915_PERF_PROP_OA_FORMAT, I915_OA_FORMAT_A32u40_A4u32_B8_C8,
+		DRM_I915_PERF_PROP_OA_EXPONENT, 20,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+		I915_PERF_FLAG_FD_NONBLOCK,
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int slice_count = __slice_count__ - 1;
+	uint32_t ctx[2];
+	unsigned int i;
+	int perf_fd;
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	/*
+	 * Now open i915/perf and verify that all contexts have been
+	 * reconfigured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1], slice_count),
+				      0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__), 0);
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+
+	/*
+	 * Open i915/perf first and verify that all contexts created
+	 * afterward are configured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Check the device's default values, despite setting
+	 * otherwise.
+	 */
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1],
+						       slice_count), 0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	struct igt_spin_factory opts = {
+		.ctx = ctx,
+		.engine = flags,
+	};
+
+	if (gem_can_store_dword(fd, flags))
+		opts.flags |= IGT_SPIN_POLL_RUN;
+
+	return __igt_spin_batch_factory(fd, &opts);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!READ_ONCE(*spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static uint32_t
+read_slice_count_busy(int fd, uint32_t context, uint32_t expected,
+		      igt_spin_t *spin)
+{
+	return __read_slice_count(fd, context, expected, spin);
+}
+
+#define TEST_IDLE	(1)
+#define TEST_BUSY	(2)
+#define TEST_RESET	(4)
+#define TEST_HANG	(8)
+#define TEST_SUSPEND	(16)
+
+static igt_spin_t *
+__pre_set(int fd, unsigned flags, uint32_t ctx)
+{
+	if (flags & TEST_BUSY)
+		return __spin_sync(fd, ctx, I915_EXEC_RENDER);
+
+	return NULL;
+}
+
+static igt_spin_t *
+__post_set(int fd, unsigned int flags, uint32_t ctx, igt_spin_t *spin,
+	   unsigned int expected)
+{
+	bool busy_reset = (flags & TEST_BUSY) &&
+			  (flags & (TEST_RESET | TEST_HANG));
+
+	if (flags & TEST_RESET)
+		igt_force_gpu_reset(fd);
+	else if (flags & TEST_HANG)
+		gem_sync(fd, spin->handle);
+
+	if (spin && !busy_reset) {
+		igt_assert_eq(read_slice_count_busy(fd, ctx, 0, spin),
+			      expected);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
+	}
+
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin)
+		igt_spin_batch_free(fd, spin);
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	if (flags & TEST_SUSPEND) {
+		igt_set_autoresume_delay(5);
+		igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
+					      SUSPEND_TEST_NONE);
+	}
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	return NULL;
+}
+
+/*
+ * Test context re-configuration with either idle or busy contexts.
+ */
+static void
+test_dynamic(int fd, unsigned int flags)
+{
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int pg_slice_count = __slice_count__ - 1;
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	igt_spin_t *spin = NULL;
+
+	igt_require(__slice_count__ > 1);
+
+	gem_context_get_param(fd, &arg);
+
+	/* First set the default mask */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, __slice_count__);
+
+	/* Then set a powergated configuration */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, pg_slice_count);
+
+	/* Put the device's default back again */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, __slice_count__);
+
+	/* One last powergated config for the road... */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, pg_slice_count);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+igt_main
+{
+	unsigned int max_slices = 3, max_subslices = 3;
+	unsigned int i;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		__intel_devid__ = intel_get_drm_devid(fd);
+		__intel_gen__ = intel_gen(__intel_devid__);
+
+		igt_require(kernel_has_per_context_sseu_support(fd));
+
+		if (__intel_gen__ >= 11)
+			__read_slice_count = gen11_read_slice_count;
+		else
+			__read_slice_count = gen8_read_slice_count;
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			drm_i915_getparam_t gp;
+
+			gp.param = I915_PARAM_SLICE_MASK;
+			gp.value = (int *) &__slice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__slice_count__ = __builtin_popcount(__slice_mask__);
+
+			gp.param = I915_PARAM_SUBSLICE_MASK;
+			gp.value = (int *) &__subslice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__subslice_count__ =
+				__builtin_popcount(__subslice_mask__);
+		}
+
+		igt_subtest("invalid-args")
+			test_invalid_args(fd);
+
+		igt_subtest("invalid-sseu")
+			test_invalid_sseu(fd);
+
+		igt_subtest("ggtt-args")
+			test_ggtt_args(fd);
+
+		igt_subtest("engines")
+			test_engines(fd);
+
+		for (i = 1; i < max_slices; i++) {
+			igt_subtest_f("slice-pg-%i", i) {
+				igt_require(__slice_count__ > i);
+
+				test_slice_pg(fd, i);
+			}
+		}
+
+		for (i = 1; i < max_subslices; i++) {
+			igt_subtest_f("subslice-pg-%i", i) {
+				igt_require(__subslice_count__ >= 2);
+
+				/*
+				 * Only available on some Atom platforms and
+				 * Gen10+.
+				 */
+				igt_require(IS_BROXTON(__intel_devid__) ||
+					    IS_GEMINILAKE(__intel_devid__) ||
+					    __intel_gen__ >= 10);
+
+				test_subslice_pg(fd, i);
+			}
+		}
+
+		igt_subtest("perf-oa") {
+			igt_require(__slice_count__ > 1);
+
+			test_perf_oa(fd);
+		}
+
+		igt_subtest("dynamic")
+			test_dynamic(fd, 0);
+
+		igt_subtest("dynamic-busy")
+			test_dynamic(fd, TEST_BUSY);
+
+		igt_subtest("dynamic-reset")
+			test_dynamic(fd, TEST_RESET);
+
+		igt_subtest("dynamic-busy-reset")
+			test_dynamic(fd, TEST_BUSY | TEST_RESET);
+
+		igt_subtest("dynamic-busy-hang")
+			test_dynamic(fd, TEST_BUSY | TEST_HANG);
+
+		igt_subtest("dynamic-idle")
+			test_dynamic(fd, TEST_IDLE);
+
+		igt_subtest("dynamic-suspend")
+			test_dynamic(fd, TEST_SUSPEND);
+
+		igt_subtest("dynamic-idle-suspend")
+			test_dynamic(fd, TEST_IDLE | TEST_SUSPEND);
+	}
+
+	igt_fixture {
+		close(fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 17deb945ec95..9319fa4e19b4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -249,6 +249,13 @@ foreach prog : test_progs
 		   install : true)
 endforeach
 
+test_executables += executable('gem_ctx_sseu', 'gem_ctx_sseu.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_ctx_sseu'
+
 test_executables += executable('gem_eio', 'gem_eio.c',
 	   dependencies : test_deps + [ realtime ],
 	   install_dir : libexecdir,
-- 
2.17.1

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

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

* [Intel-gfx] [PATCH i-g-t v13 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
@ 2018-09-14 16:04                   ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-14 16:04 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

Verify that the per-context dynamic SSEU uAPI works as expected.

To achieve that, in the absence of a better mechamism, we read the value
of PWR_CLK_STATE register, or use MI_SET_PREDICATE on platforms before
Cannonlake.

This register is written to by the GPU on context restore so this way
we verify i915 is correctly updating the context image in all
circumstances.

v2: Add subslice tests (Lionel)
    Use MI_SET_PREDICATE for further verification when available (Lionel)

v3: Rename to gem_ctx_rpcs (Lionel)

v4: Update kernel API (Lionel)
    Add 0 value test (Lionel)
    Exercise invalid values (Lionel)

v5: Add perf tests (Lionel)

v6: Add new sysfs entry tests (Lionel)

v7: Test rsvd fields
    Update for kernel series changes

v8: Drop test_no_sseu_support() test (Kelvin)
    Drop drm_intel_*() apis (Chris)

v9: by Chris:
    Drop all do_ioctl/do_ioctl_err()
    Use gem_context_[gs]et_param()
    Use gem_read() instead of mapping memory
    by Lionel:
    Test dynamic sseu on/off more

Tvrtko Ursulin:

v10:
 * Various style tweaks and refactorings.
 * New test coverage.

v11:
 * Change platform support to just Gen11.
 * Simplify availability test. (Chris Wilson)
 * More invalid pointer tests. (Chris Wilson)

v12:
 * Fix MAP_FIXED use (doh!).
 * Fix get/set copy&paste errors.
 * Drop supported platform test. (Chris Wilson)
 * Add mmap__gtt test. (Chris Wilson)

v13:
 * Commit message tweaks.
 * Added reset/hang/suspend tests. (Chris Wilson)
 * Assert spinner is busy. (Chris Wilson)
 * Remove some more ABI assumptions. (Chris Wilson)

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tests/Makefile.am      |    1 +
 tests/Makefile.sources |    1 +
 tests/gem_ctx_param.c  |    4 +-
 tests/gem_ctx_sseu.c   | 1128 ++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |    7 +
 5 files changed, 1140 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_sseu.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index ee5a7c5e83b8..6b67bd2cc17a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -107,6 +107,7 @@ gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
+gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
 gem_fence_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index c84933f1d971..f8f2c8d67d72 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -56,6 +56,7 @@ TESTS_progs = \
 	gem_ctx_exec \
 	gem_ctx_isolation \
 	gem_ctx_param \
+	gem_ctx_sseu \
 	gem_ctx_switch \
 	gem_ctx_thrash \
 	gem_double_irq_loop \
diff --git a/tests/gem_ctx_param.c b/tests/gem_ctx_param.c
index c46fd709b0d7..af1afeaa2f2f 100644
--- a/tests/gem_ctx_param.c
+++ b/tests/gem_ctx_param.c
@@ -294,11 +294,13 @@ igt_main
 			set_priority(fd);
 	}
 
+	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
+
 	/* NOTE: This testcase intentionally tests for the next free parameter
 	 * to catch ABI extensions. Don't "fix" this testcase without adding all
 	 * the tests for the new param first.
 	 */
-	arg.param = I915_CONTEXT_PARAM_PRIORITY + 1;
+	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
 
 	igt_subtest("invalid-param-get") {
 		arg.ctx_id = ctx;
diff --git a/tests/gem_ctx_sseu.c b/tests/gem_ctx_sseu.c
new file mode 100644
index 000000000000..4669c40f34fb
--- /dev/null
+++ b/tests/gem_ctx_sseu.c
@@ -0,0 +1,1128 @@
+/*
+ * Copyright © 2017-2018 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.
+ *
+ * Authors:
+ *    Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include "igt_dummyload.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+#include "ioctl_wrappers.h"
+
+IGT_TEST_DESCRIPTION("Test context render powergating programming.");
+
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+
+#define MI_SET_PREDICATE      (0x1 << 23)
+#define  MI_SET_PREDICATE_NOOP_NEVER         (0)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_SET   (2)
+#define  MI_SET_PREDICATE_NOOP_RESULT_CLEAR  (3)
+#define  MI_SET_PREDICATE_NOOP_RESULT_SET    (4)
+#define  MI_SET_PREDICATE_1_SLICES           (5)
+#define  MI_SET_PREDICATE_2_SLICES           (6)
+#define  MI_SET_PREDICATE_3_SLICES           (7)
+
+#define GEN8_R_PWR_CLK_STATE		0x20C8
+#define   GEN8_RPCS_ENABLE		(1 << 31)
+#define   GEN8_RPCS_S_CNT_ENABLE	(1 << 18)
+#define   GEN8_RPCS_S_CNT_SHIFT		15
+#define   GEN8_RPCS_S_CNT_MASK		(0x7 << GEN8_RPCS_S_CNT_SHIFT)
+#define   GEN11_RPCS_S_CNT_SHIFT	12
+#define   GEN11_RPCS_S_CNT_MASK		(0x3f << GEN11_RPCS_S_CNT_SHIFT)
+#define   GEN8_RPCS_SS_CNT_ENABLE	(1 << 11)
+#define   GEN8_RPCS_SS_CNT_SHIFT	8
+#define   GEN8_RPCS_SS_CNT_MASK		(0x7 << GEN8_RPCS_SS_CNT_SHIFT)
+#define   GEN8_RPCS_EU_MAX_SHIFT	4
+#define   GEN8_RPCS_EU_MAX_MASK		(0xf << GEN8_RPCS_EU_MAX_SHIFT)
+#define   GEN8_RPCS_EU_MIN_SHIFT	0
+#define   GEN8_RPCS_EU_MIN_MASK		(0xf << GEN8_RPCS_EU_MIN_SHIFT)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+static unsigned int __intel_gen__, __intel_devid__;
+static uint64_t __slice_mask__, __subslice_mask__;
+static unsigned int __slice_count__, __subslice_count__;
+
+static uint64_t mask_minus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if ((1ULL << i) & mask)
+			return mask & ~(1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_plus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if (((1ULL << i) & mask) == 0)
+			return mask | (1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_minus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_minus_one(mask);
+
+	return mask;
+}
+
+static uint64_t mask_plus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_plus_one(mask);
+
+	return mask;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+		struct drm_i915_gem_relocation_entry *reloc,
+		uint32_t gem_handle, uint32_t delta, /* in bytes */
+		uint32_t offset, /* in dwords */
+		uint32_t read_domains, uint32_t write_domains)
+{
+	reloc->target_handle = gem_handle;
+	reloc->delta = delta;
+	reloc->offset = offset * sizeof(uint32_t);
+	reloc->presumed_offset = 0;
+	reloc->read_domains = read_domains;
+	reloc->write_domain = write_domains;
+
+	*batch++ = delta;
+	*batch++ = 0;
+
+	return batch;
+}
+
+
+static uint32_t
+read_rpcs_reg(int fd, uint32_t ctx, uint32_t expected_slices, igt_spin_t *spin)
+{
+	struct drm_i915_gem_execbuffer2 execbuf = { };
+	struct drm_i915_gem_relocation_entry relocs[2];
+	struct drm_i915_gem_exec_object2 obj[2];
+	uint32_t *batch, *b, data[2];
+	unsigned int n_relocs = 0;
+	uint32_t rpcs;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[1].handle = gem_create(fd, 4096);
+
+	batch = b =
+	      gem_mmap__cpu(fd, obj[1].handle, 0, 4096, PROT_READ | PROT_WRITE);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) |
+		       (MI_SET_PREDICATE_1_SLICES + expected_slices - 1);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = RCS_TIMESTAMP;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    0, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = GEN8_R_PWR_CLK_STATE;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    4, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) | MI_SET_PREDICATE_NOOP_NEVER;
+
+	*b++ = MI_BATCH_BUFFER_END;
+
+	gem_munmap(batch, 4096);
+
+	obj[1].relocation_count = n_relocs;
+	obj[1].relocs_ptr = to_user_pointer(relocs);
+
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+	i915_execbuffer2_set_context_id(execbuf, ctx);
+
+	gem_execbuf(fd, &execbuf);
+
+	if (spin) {
+		igt_assert(gem_bo_busy(fd, spin->handle));
+		igt_spin_batch_end(spin);
+	}
+
+	gem_read(fd, obj[0].handle, 0, data, sizeof(data));
+
+	rpcs = data[1];
+
+	igt_debug("rcs_timestamp=0x%x rpcs=0x%x\n", data[0], data[1]);
+
+	gem_close(fd, obj[0].handle);
+	gem_close(fd, obj[1].handle);
+
+	return rpcs;
+}
+
+typedef uint32_t (*read_slice_count_f)(int fd, uint32_t ctx, uint32_t expected,
+				       igt_spin_t *spin);
+
+static read_slice_count_f __read_slice_count;
+
+static uint32_t
+read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count)
+{
+	return __read_slice_count(fd, ctx, expected_slice_count, NULL);
+}
+
+static uint32_t
+gen8_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		      igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN8_RPCS_S_CNT_MASK) >> GEN8_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+gen11_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		       igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN11_RPCS_S_CNT_MASK) >> GEN11_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+read_subslice_count(int fd, uint32_t ctx)
+{
+	return (read_rpcs_reg(fd, ctx, 0, NULL) & GEN8_RPCS_SS_CNT_MASK) >>
+	       GEN8_RPCS_SS_CNT_SHIFT;
+}
+
+static bool
+kernel_has_per_context_sseu_support(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{
+		  .param = I915_CONTEXT_PARAM_SSEU,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu),
+		};
+	int ret;
+
+	if (__gem_context_get_param(fd, &arg))
+		return false;
+
+	arg.value = to_user_pointer(&sseu);
+
+	ret = __gem_context_set_param(fd, &arg);
+
+	igt_assert(ret == 0 || ret == -ENODEV || ret == -EINVAL);
+
+	return ret == 0;
+}
+
+static void
+context_get_sseu_masks(int fd, uint32_t ctx,
+		       uint64_t *slice_mask,
+		       uint64_t *subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+
+	if (slice_mask)
+		*slice_mask = sseu.slice_mask;
+
+	if (subslice_mask)
+		*subslice_mask = sseu.subslice_mask;
+}
+
+static void
+context_set_slice_mask(int fd, uint32_t ctx, uint64_t slice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.slice_mask = slice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+static void
+context_set_subslice_mask(int fd, uint32_t ctx, uint64_t subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.subslice_mask = subslice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+/*
+ * Verify that we can program the slice count.
+ */
+static void
+test_slice_pg(int fd, uint32_t pg_slice_count)
+{
+	uint64_t pg_slice_mask = mask_minus(__slice_mask__, pg_slice_count);
+	unsigned int slice_count = __slice_count__ - pg_slice_count;
+	uint64_t slice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(slice_count, __builtin_popcount(pg_slice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_slice_mask(fd, ctx, pg_slice_mask);
+	context_get_sseu_masks(fd, ctx, &slice_mask, NULL);
+	igt_assert_eq(pg_slice_mask, slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx, __slice_count__), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), slice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+/*
+ * Verify that we can program the subslice count.
+ */
+static void
+test_subslice_pg(int fd, int pg_subslice_count)
+{
+	uint64_t pg_subslice_mask =
+		mask_minus(__subslice_mask__, pg_subslice_count);
+	unsigned int subslice_count = __subslice_count__ - pg_subslice_count;
+	uint64_t subslice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(subslice_count, __builtin_popcount(pg_subslice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_subslice_mask(fd, ctx, pg_subslice_mask);
+	context_get_sseu_masks(fd, ctx, NULL, &subslice_mask);
+	igt_assert_eq(pg_subslice_mask, subslice_mask);
+
+	igt_assert_eq(read_subslice_count(fd, ctx), subslice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_engine(int fd, unsigned int class, unsigned int instance)
+{
+	int pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+
+	if (pmu >= 0)
+		close(pmu);
+
+	return pmu >= 0;
+}
+
+/*
+ * Verify that invalid engines are rejected and valid ones are accepted.
+ */
+static void test_engines(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	unsigned int class, instance;
+	int last_with_engines;
+
+	/* get_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+	sseu.class = I915_ENGINE_CLASS_RENDER;
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_get_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert_eq(ret, 0);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	/*
+	 * Get some proper values before trying to reprogram them onto
+	 * an invalid engine.
+	 */
+	sseu.class = 0;
+	sseu.instance = 0;
+	gem_context_get_param(fd, &arg);
+
+	/* set_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_set_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert(ret == 0 || ret == -ENODEV);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid arguments are rejected.
+ */
+static void
+test_invalid_args(int fd)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		};
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	unsigned char *page[2];
+	unsigned char *addr;
+	unsigned int sz;
+
+	/* get param */
+
+	/* Invalid size. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	/* Query size. */
+	arg.size = 0;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	sz = arg.size;
+
+	/* Bad pointers. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(page[1]) -
+		    sizeof(struct drm_i915_gem_context_param_sseu) + 4;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	/* Straddle into read-only area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	igt_assert(mprotect(page[1], 4096, PROT_READ) == 0);
+	arg.value = to_user_pointer(page[1] - sizeof(sseu) + 4);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+	munmap(page[1], 4096);
+
+	/* set param */
+
+	/* Invalid sizes. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	arg.size = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	arg.size = sz;
+
+	/* Bad pointers. */
+	arg.value = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Get valid SSEU. */
+	arg.value = to_user_pointer(&sseu);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memcpy(page[0], &sseu, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	addr = page[1] - sizeof(sseu) + 4;
+	memcpy(addr, &sseu, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(addr);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that ggtt mapped area can be used as the sseu pointer.
+ */
+static void
+test_ggtt_args(int fd)
+{
+	struct drm_i915_gem_context_param_sseu *sseu;
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(*sseu),
+		};
+	uint32_t bo;
+
+	bo = gem_create(fd, 4096);
+	arg.value = to_user_pointer(gem_mmap__gtt(fd, bo, 4096,
+						  PROT_READ | PROT_WRITE));
+
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), 0);
+
+	munmap((void *)arg.value, 4096);
+	gem_close(fd, bo);
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid SSEU values are rejected.
+ */
+static void
+test_invalid_sseu(int fd)
+{
+	struct drm_i915_gem_context_param_sseu default_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		};
+	unsigned int i;
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&default_sseu);
+	gem_context_get_param(fd, &arg);
+
+	arg.value = to_user_pointer(&sseu);
+
+	/* Try all slice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __slice_count__); i++) {
+		sseu.slice_mask = mask_plus(__slice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 slices. */
+	sseu.slice_mask = 0;
+	igt_assert_eq(-EINVAL, __gem_context_set_param(fd, &arg));
+
+	/* Try all subslice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __subslice_count__); i++) {
+		sseu.subslice_mask = mask_plus(__subslice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 subslices. */
+	sseu.subslice_mask = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try number of EUs superior to the max available. */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try to program 0 max EUs. */
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Min > max */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = sseu.max_eus_per_subslice;
+	sseu.max_eus_per_subslice = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	if (__intel_gen__ != 11)
+		goto out;
+
+	/* Subset of subslices but slice mask greater than one. */
+	if (__slice_count__ > 1) {
+		sseu = default_sseu;
+		sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Odd subslices above four. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* More than half subslices with one slice. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 - 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+out:
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/* Verify that the kernel returns a correct error value on Gen < 8. */
+static void
+init_contexts(int fd, uint32_t *ctx, unsigned int num,
+	      uint64_t mask0, uint64_t mask1)
+{
+	unsigned int i;
+
+	igt_assert_eq(num, 2);
+
+	for (i = 0; i < num; i++)
+		ctx[i] = gem_context_create(fd);
+
+	context_set_slice_mask(fd, ctx[0], mask0);
+	context_set_slice_mask(fd, ctx[1], mask1);
+}
+
+/*
+ * Verify that powergating settings are put on hold while i915/perf is
+ * active.
+ */
+static void
+test_perf_oa(int fd)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, 1, /* test metric */
+		DRM_I915_PERF_PROP_OA_FORMAT, I915_OA_FORMAT_A32u40_A4u32_B8_C8,
+		DRM_I915_PERF_PROP_OA_EXPONENT, 20,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+		I915_PERF_FLAG_FD_NONBLOCK,
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int slice_count = __slice_count__ - 1;
+	uint32_t ctx[2];
+	unsigned int i;
+	int perf_fd;
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	/*
+	 * Now open i915/perf and verify that all contexts have been
+	 * reconfigured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1], slice_count),
+				      0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__), 0);
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+
+	/*
+	 * Open i915/perf first and verify that all contexts created
+	 * afterward are configured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Check the device's default values, despite setting
+	 * otherwise.
+	 */
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1],
+						       slice_count), 0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	struct igt_spin_factory opts = {
+		.ctx = ctx,
+		.engine = flags,
+	};
+
+	if (gem_can_store_dword(fd, flags))
+		opts.flags |= IGT_SPIN_POLL_RUN;
+
+	return __igt_spin_batch_factory(fd, &opts);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!READ_ONCE(*spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static uint32_t
+read_slice_count_busy(int fd, uint32_t context, uint32_t expected,
+		      igt_spin_t *spin)
+{
+	return __read_slice_count(fd, context, expected, spin);
+}
+
+#define TEST_IDLE	(1)
+#define TEST_BUSY	(2)
+#define TEST_RESET	(4)
+#define TEST_HANG	(8)
+#define TEST_SUSPEND	(16)
+
+static igt_spin_t *
+__pre_set(int fd, unsigned flags, uint32_t ctx)
+{
+	if (flags & TEST_BUSY)
+		return __spin_sync(fd, ctx, I915_EXEC_RENDER);
+
+	return NULL;
+}
+
+static igt_spin_t *
+__post_set(int fd, unsigned int flags, uint32_t ctx, igt_spin_t *spin,
+	   unsigned int expected)
+{
+	bool busy_reset = (flags & TEST_BUSY) &&
+			  (flags & (TEST_RESET | TEST_HANG));
+
+	if (flags & TEST_RESET)
+		igt_force_gpu_reset(fd);
+	else if (flags & TEST_HANG)
+		gem_sync(fd, spin->handle);
+
+	if (spin && !busy_reset) {
+		igt_assert_eq(read_slice_count_busy(fd, ctx, 0, spin),
+			      expected);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
+	}
+
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin)
+		igt_spin_batch_free(fd, spin);
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	if (flags & TEST_SUSPEND) {
+		igt_set_autoresume_delay(5);
+		igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
+					      SUSPEND_TEST_NONE);
+	}
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	return NULL;
+}
+
+/*
+ * Test context re-configuration with either idle or busy contexts.
+ */
+static void
+test_dynamic(int fd, unsigned int flags)
+{
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int pg_slice_count = __slice_count__ - 1;
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	igt_spin_t *spin = NULL;
+
+	igt_require(__slice_count__ > 1);
+
+	gem_context_get_param(fd, &arg);
+
+	/* First set the default mask */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, __slice_count__);
+
+	/* Then set a powergated configuration */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, pg_slice_count);
+
+	/* Put the device's default back again */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, __slice_count__);
+
+	/* One last powergated config for the road... */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, pg_slice_count);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+igt_main
+{
+	unsigned int max_slices = 3, max_subslices = 3;
+	unsigned int i;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		__intel_devid__ = intel_get_drm_devid(fd);
+		__intel_gen__ = intel_gen(__intel_devid__);
+
+		igt_require(kernel_has_per_context_sseu_support(fd));
+
+		if (__intel_gen__ >= 11)
+			__read_slice_count = gen11_read_slice_count;
+		else
+			__read_slice_count = gen8_read_slice_count;
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			drm_i915_getparam_t gp;
+
+			gp.param = I915_PARAM_SLICE_MASK;
+			gp.value = (int *) &__slice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__slice_count__ = __builtin_popcount(__slice_mask__);
+
+			gp.param = I915_PARAM_SUBSLICE_MASK;
+			gp.value = (int *) &__subslice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__subslice_count__ =
+				__builtin_popcount(__subslice_mask__);
+		}
+
+		igt_subtest("invalid-args")
+			test_invalid_args(fd);
+
+		igt_subtest("invalid-sseu")
+			test_invalid_sseu(fd);
+
+		igt_subtest("ggtt-args")
+			test_ggtt_args(fd);
+
+		igt_subtest("engines")
+			test_engines(fd);
+
+		for (i = 1; i < max_slices; i++) {
+			igt_subtest_f("slice-pg-%i", i) {
+				igt_require(__slice_count__ > i);
+
+				test_slice_pg(fd, i);
+			}
+		}
+
+		for (i = 1; i < max_subslices; i++) {
+			igt_subtest_f("subslice-pg-%i", i) {
+				igt_require(__subslice_count__ >= 2);
+
+				/*
+				 * Only available on some Atom platforms and
+				 * Gen10+.
+				 */
+				igt_require(IS_BROXTON(__intel_devid__) ||
+					    IS_GEMINILAKE(__intel_devid__) ||
+					    __intel_gen__ >= 10);
+
+				test_subslice_pg(fd, i);
+			}
+		}
+
+		igt_subtest("perf-oa") {
+			igt_require(__slice_count__ > 1);
+
+			test_perf_oa(fd);
+		}
+
+		igt_subtest("dynamic")
+			test_dynamic(fd, 0);
+
+		igt_subtest("dynamic-busy")
+			test_dynamic(fd, TEST_BUSY);
+
+		igt_subtest("dynamic-reset")
+			test_dynamic(fd, TEST_RESET);
+
+		igt_subtest("dynamic-busy-reset")
+			test_dynamic(fd, TEST_BUSY | TEST_RESET);
+
+		igt_subtest("dynamic-busy-hang")
+			test_dynamic(fd, TEST_BUSY | TEST_HANG);
+
+		igt_subtest("dynamic-idle")
+			test_dynamic(fd, TEST_IDLE);
+
+		igt_subtest("dynamic-suspend")
+			test_dynamic(fd, TEST_SUSPEND);
+
+		igt_subtest("dynamic-idle-suspend")
+			test_dynamic(fd, TEST_IDLE | TEST_SUSPEND);
+	}
+
+	igt_fixture {
+		close(fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 17deb945ec95..9319fa4e19b4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -249,6 +249,13 @@ foreach prog : test_progs
 		   install : true)
 endforeach
 
+test_executables += executable('gem_ctx_sseu', 'gem_ctx_sseu.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_ctx_sseu'
+
 test_executables += executable('gem_eio', 'gem_eio.c',
 	   dependencies : test_deps + [ realtime ],
 	   install_dir : libexecdir,
-- 
2.17.1

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

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

* Re: [igt-dev] [PATCH i-g-t v13 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
  2018-09-14 16:04                   ` [Intel-gfx] " Tvrtko Ursulin
@ 2018-09-14 16:07                     ` Chris Wilson
  -1 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-14 16:07 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-14 17:04:38)
> +static uint32_t *
> +fill_relocation(uint32_t *batch,
> +               struct drm_i915_gem_relocation_entry *reloc,
> +               uint32_t gem_handle, uint32_t delta, /* in bytes */
> +               uint32_t offset, /* in dwords */
> +               uint32_t read_domains, uint32_t write_domains)

Fwiw, all the kernels with ctx-sseu support also have softpinning...
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [igt-dev] [PATCH i-g-t v13 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
@ 2018-09-14 16:07                     ` Chris Wilson
  0 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-14 16:07 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

Quoting Tvrtko Ursulin (2018-09-14 17:04:38)
> +static uint32_t *
> +fill_relocation(uint32_t *batch,
> +               struct drm_i915_gem_relocation_entry *reloc,
> +               uint32_t gem_handle, uint32_t delta, /* in bytes */
> +               uint32_t offset, /* in dwords */
> +               uint32_t read_domains, uint32_t write_domains)

Fwiw, all the kernels with ctx-sseu support also have softpinning...
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* Re: [igt-dev] [PATCH i-g-t v13 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
  2018-09-14 16:04                   ` [Intel-gfx] " Tvrtko Ursulin
@ 2018-09-14 16:17                     ` Chris Wilson
  -1 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-14 16:17 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-14 17:04:38)
> +static igt_spin_t *
> +__post_set(int fd, unsigned int flags, uint32_t ctx, igt_spin_t *spin,
> +          unsigned int expected)
> +{
> +       bool busy_reset = (flags & TEST_BUSY) &&
> +                         (flags & (TEST_RESET | TEST_HANG));
> +
> +       if (flags & TEST_RESET)
> +               igt_force_gpu_reset(fd);
> +       else if (flags & TEST_HANG)
> +               gem_sync(fd, spin->handle);

Hmm, in both cases this is before we submit the read/post-sseu batch.

I was thinking more for the hang you would simply opt to not terminate
the spinner.

Being utterly pedantic, we may want to check per-engine resets and
device level.

> +       if (spin && !busy_reset) {
> +               igt_assert_eq(read_slice_count_busy(fd, ctx, 0, spin),
> +                             expected);
> +       } else {
> +               igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
> +       }
> +
> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
> +
> +       if (spin)
> +               igt_spin_batch_free(fd, spin);
> +
> +       if (flags & TEST_IDLE)
> +               igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
> +
> +       if (flags & TEST_SUSPEND) {
> +               igt_set_autoresume_delay(5);

Brave. Opting for a faster wakeup than default...

> +               igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
> +                                             SUSPEND_TEST_NONE);
> +       }
> +
> +       igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
> +
> +       return NULL;
> +}

Overall, nothing else strikes me as being absent.

Oh, one more thing; do we are what state we leave the system in on
module unload? So long as the GPU reset clears it we should be ok.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [igt-dev] [PATCH i-g-t v13 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
@ 2018-09-14 16:17                     ` Chris Wilson
  0 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-14 16:17 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

Quoting Tvrtko Ursulin (2018-09-14 17:04:38)
> +static igt_spin_t *
> +__post_set(int fd, unsigned int flags, uint32_t ctx, igt_spin_t *spin,
> +          unsigned int expected)
> +{
> +       bool busy_reset = (flags & TEST_BUSY) &&
> +                         (flags & (TEST_RESET | TEST_HANG));
> +
> +       if (flags & TEST_RESET)
> +               igt_force_gpu_reset(fd);
> +       else if (flags & TEST_HANG)
> +               gem_sync(fd, spin->handle);

Hmm, in both cases this is before we submit the read/post-sseu batch.

I was thinking more for the hang you would simply opt to not terminate
the spinner.

Being utterly pedantic, we may want to check per-engine resets and
device level.

> +       if (spin && !busy_reset) {
> +               igt_assert_eq(read_slice_count_busy(fd, ctx, 0, spin),
> +                             expected);
> +       } else {
> +               igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
> +       }
> +
> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
> +
> +       if (spin)
> +               igt_spin_batch_free(fd, spin);
> +
> +       if (flags & TEST_IDLE)
> +               igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
> +
> +       if (flags & TEST_SUSPEND) {
> +               igt_set_autoresume_delay(5);

Brave. Opting for a faster wakeup than default...

> +               igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
> +                                             SUSPEND_TEST_NONE);
> +       }
> +
> +       igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
> +
> +       return NULL;
> +}

Overall, nothing else strikes me as being absent.

Oh, one more thing; do we are what state we leave the system in on
module unload? So long as the GPU reset clears it we should be ok.
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [igt-dev] ✓ Fi.CI.BAT: success for Per context dynamic (sub)slice power-gating (rev4)
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
                   ` (6 preceding siblings ...)
  (?)
@ 2018-09-14 17:22 ` Patchwork
  -1 siblings, 0 replies; 55+ messages in thread
From: Patchwork @ 2018-09-14 17:22 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: igt-dev

== Series Details ==

Series: Per context dynamic (sub)slice power-gating (rev4)
URL   : https://patchwork.freedesktop.org/series/49190/
State : success

== Summary ==

= CI Bug Log - changes from CI_DRM_4827 -> IGTPW_1845 =

== Summary - SUCCESS ==

  No regressions found.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/49190/revisions/4/mbox/

== Known issues ==

  Here are the changes found in IGTPW_1845 that come from known issues:

  === IGT changes ===

    ==== Issues hit ====

    igt@kms_psr@primary_page_flip:
      fi-whl-u:           PASS -> FAIL (fdo#107336)

    igt@prime_vgem@basic-fence-flip:
      fi-glk-j4005:       PASS -> FAIL (fdo#103182)

    
    ==== Possible fixes ====

    igt@gem_exec_suspend@basic-s3:
      fi-blb-e6850:       INCOMPLETE (fdo#107718) -> PASS

    igt@kms_pipe_crc_basic@read-crc-pipe-a-frame-sequence:
      fi-byt-clapper:     FAIL (fdo#103191, fdo#107362) -> PASS

    igt@kms_psr@primary_page_flip:
      fi-kbl-r:           FAIL (fdo#107336) -> PASS

    
  fdo#103182 https://bugs.freedesktop.org/show_bug.cgi?id=103182
  fdo#103191 https://bugs.freedesktop.org/show_bug.cgi?id=103191
  fdo#107336 https://bugs.freedesktop.org/show_bug.cgi?id=107336
  fdo#107362 https://bugs.freedesktop.org/show_bug.cgi?id=107362
  fdo#107718 https://bugs.freedesktop.org/show_bug.cgi?id=107718


== Participating hosts (48 -> 43) ==

  Missing    (5): fi-ilk-m540 fi-byt-squawks fi-bsw-cyan fi-snb-2520m fi-hsw-4200u 


== Build changes ==

    * IGT: IGT_4642 -> IGTPW_1845

  CI_DRM_4827: 8b1968f143e8bf65acf0ea6f7ce9120259043521 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_1845: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1845/
  IGT_4642: 7b3ea4efb9713cd67e17e33202fa9d0681a284d1 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools



== Testlist changes ==

+igt@gem_ctx_sseu@dynamic
+igt@gem_ctx_sseu@dynamic-busy
+igt@gem_ctx_sseu@dynamic-busy-hang
+igt@gem_ctx_sseu@dynamic-busy-reset
+igt@gem_ctx_sseu@dynamic-idle
+igt@gem_ctx_sseu@dynamic-idle-suspend
+igt@gem_ctx_sseu@dynamic-reset
+igt@gem_ctx_sseu@dynamic-suspend
+igt@gem_ctx_sseu@engines
+igt@gem_ctx_sseu@ggtt-args
+igt@gem_ctx_sseu@invalid-args
+igt@gem_ctx_sseu@invalid-sseu
+igt@gem_ctx_sseu@perf-oa
+igt@gem_ctx_sseu@slice-pg-1
+igt@gem_ctx_sseu@slice-pg-2
+igt@gem_ctx_sseu@subslice-pg-1
+igt@gem_ctx_sseu@subslice-pg-2

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1845/issues.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [igt-dev] ✗ Fi.CI.IGT: failure for Per context dynamic (sub)slice power-gating (rev4)
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
                   ` (7 preceding siblings ...)
  (?)
@ 2018-09-14 22:22 ` Patchwork
  -1 siblings, 0 replies; 55+ messages in thread
From: Patchwork @ 2018-09-14 22:22 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: igt-dev

== Series Details ==

Series: Per context dynamic (sub)slice power-gating (rev4)
URL   : https://patchwork.freedesktop.org/series/49190/
State : failure

== Summary ==

= CI Bug Log - changes from IGT_4642_full -> IGTPW_1845_full =

== Summary - FAILURE ==

  Serious unknown changes coming with IGTPW_1845_full absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_1845_full, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/49190/revisions/4/mbox/

== Possible new issues ==

  Here are the unknown changes that may have been introduced in IGTPW_1845_full:

  === IGT changes ===

    ==== Possible regressions ====

    igt@kms_busy@extended-modeset-hang-newfb-render-c:
      shard-glk:          PASS -> DMESG-WARN +11

    igt@kms_busy@extended-modeset-hang-newfb-with-reset-render-b:
      shard-kbl:          PASS -> DMESG-WARN +7
      shard-snb:          NOTRUN -> DMESG-WARN

    igt@kms_busy@extended-modeset-hang-newfb-with-reset-render-c:
      shard-apl:          PASS -> DMESG-WARN +11

    igt@kms_busy@extended-pageflip-modeset-hang-oldfb-render-b:
      shard-hsw:          PASS -> DMESG-WARN +8
      shard-snb:          PASS -> DMESG-WARN +4

    
    ==== Warnings ====

    igt@pm_rc6_residency@rc6-accuracy:
      shard-snb:          PASS -> SKIP

    
== Known issues ==

  Here are the changes found in IGTPW_1845_full that come from known issues:

  === IGT changes ===

    ==== Issues hit ====

    igt@gem_exec_big:
      shard-hsw:          PASS -> TIMEOUT (fdo#107937)

    igt@kms_ccs@pipe-b-crc-primary-rotation-180:
      shard-glk:          PASS -> DMESG-FAIL (fdo#106538)

    igt@kms_cursor_legacy@2x-nonblocking-modeset-vs-cursor-atomic:
      shard-glk:          PASS -> FAIL (fdo#105454, fdo#106509)

    igt@kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-blt:
      shard-glk:          PASS -> FAIL (fdo#103167)

    igt@kms_rotation_crc@sprite-rotation-180:
      shard-snb:          PASS -> FAIL (fdo#103925)

    igt@kms_setmode@basic:
      shard-kbl:          PASS -> FAIL (fdo#99912)

    igt@kms_vblank@pipe-a-ts-continuation-suspend:
      shard-kbl:          PASS -> INCOMPLETE (fdo#103665)

    igt@kms_vblank@pipe-c-wait-forked:
      shard-kbl:          PASS -> DMESG-WARN (fdo#105602, fdo#103558) +23

    igt@perf@create-destroy-userspace-config:
      shard-glk:          PASS -> DMESG-WARN (fdo#105763, fdo#106538) +1

    
    ==== Possible fixes ====

    igt@gem_ctx_isolation@rcs0-s3:
      shard-apl:          INCOMPLETE (fdo#103927) -> PASS

    igt@gem_exec_schedule@preempt-hang-bsd1:
      shard-snb:          INCOMPLETE (fdo#105411) -> SKIP

    igt@kms_available_modes_crc@available_mode_test_crc:
      shard-snb:          FAIL (fdo#106641) -> PASS

    igt@kms_flip@2x-blocking-absolute-wf_vblank:
      shard-glk:          INCOMPLETE (k.org#198133, fdo#103359) -> PASS

    igt@kms_frontbuffer_tracking@fbc-2p-primscrn-cur-indfb-draw-mmap-cpu:
      shard-glk:          FAIL (fdo#103167) -> PASS +2

    igt@kms_plane_multiple@atomic-pipe-a-tiling-x:
      shard-snb:          FAIL (fdo#103166) -> PASS

    igt@kms_vblank@pipe-b-wait-forked-busy-hang:
      shard-glk:          DMESG-WARN (fdo#105763, fdo#106538) -> PASS +1

    
    ==== Warnings ====

    igt@kms_available_modes_crc@available_mode_test_crc:
      shard-kbl:          FAIL (fdo#106641) -> DMESG-WARN (fdo#105602, fdo#103558)

    
  fdo#103166 https://bugs.freedesktop.org/show_bug.cgi?id=103166
  fdo#103167 https://bugs.freedesktop.org/show_bug.cgi?id=103167
  fdo#103359 https://bugs.freedesktop.org/show_bug.cgi?id=103359
  fdo#103558 https://bugs.freedesktop.org/show_bug.cgi?id=103558
  fdo#103665 https://bugs.freedesktop.org/show_bug.cgi?id=103665
  fdo#103925 https://bugs.freedesktop.org/show_bug.cgi?id=103925
  fdo#103927 https://bugs.freedesktop.org/show_bug.cgi?id=103927
  fdo#105411 https://bugs.freedesktop.org/show_bug.cgi?id=105411
  fdo#105454 https://bugs.freedesktop.org/show_bug.cgi?id=105454
  fdo#105602 https://bugs.freedesktop.org/show_bug.cgi?id=105602
  fdo#105763 https://bugs.freedesktop.org/show_bug.cgi?id=105763
  fdo#106509 https://bugs.freedesktop.org/show_bug.cgi?id=106509
  fdo#106538 https://bugs.freedesktop.org/show_bug.cgi?id=106538
  fdo#106641 https://bugs.freedesktop.org/show_bug.cgi?id=106641
  fdo#107937 https://bugs.freedesktop.org/show_bug.cgi?id=107937
  fdo#99912 https://bugs.freedesktop.org/show_bug.cgi?id=99912
  k.org#198133 https://bugzilla.kernel.org/show_bug.cgi?id=198133


== Participating hosts (5 -> 5) ==

  No changes in participating hosts


== Build changes ==

    * IGT: IGT_4642 -> IGTPW_1845
    * Linux: CI_DRM_4825 -> CI_DRM_4827

  CI_DRM_4825: b42528aaa961c0d469f381b4a5c3830fe46aedfa @ git://anongit.freedesktop.org/gfx-ci/linux
  CI_DRM_4827: 8b1968f143e8bf65acf0ea6f7ce9120259043521 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_1845: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1845/
  IGT_4642: 7b3ea4efb9713cd67e17e33202fa9d0681a284d1 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1845/shards.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* Re: [igt-dev] [PATCH i-g-t v13 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
  2018-09-14 16:17                     ` Chris Wilson
@ 2018-09-17  9:33                       ` Tvrtko Ursulin
  -1 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-17  9:33 UTC (permalink / raw)
  To: Chris Wilson, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx


On 14/09/2018 17:17, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2018-09-14 17:04:38)
>> +static igt_spin_t *
>> +__post_set(int fd, unsigned int flags, uint32_t ctx, igt_spin_t *spin,
>> +          unsigned int expected)
>> +{
>> +       bool busy_reset = (flags & TEST_BUSY) &&
>> +                         (flags & (TEST_RESET | TEST_HANG));
>> +
>> +       if (flags & TEST_RESET)
>> +               igt_force_gpu_reset(fd);
>> +       else if (flags & TEST_HANG)
>> +               gem_sync(fd, spin->handle);
> 
> Hmm, in both cases this is before we submit the read/post-sseu batch.
> 
> I was thinking more for the hang you would simply opt to not terminate
> the spinner.

Okay I can do that as well. Hang will then be processed while we try to 
pread the rpcs eb result so I think it works.

> 
> Being utterly pedantic, we may want to check per-engine resets and
> device level.

Isn't the per-engine default, so the hang test flavour would trigger it? 
And to explicitly ask for whole device we have some debugfs api for it?

>> +       if (spin && !busy_reset) {
>> +               igt_assert_eq(read_slice_count_busy(fd, ctx, 0, spin),
>> +                             expected);
>> +       } else {
>> +               igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
>> +       }
>> +
>> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
>> +
>> +       if (spin)
>> +               igt_spin_batch_free(fd, spin);
>> +
>> +       if (flags & TEST_IDLE)
>> +               igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
>> +
>> +       if (flags & TEST_SUSPEND) {
>> +               igt_set_autoresume_delay(5);
> 
> Brave. Opting for a faster wakeup than default...

I think I started with copy paste and then lowered it when testing the 
test. Is the right thing to omit specifying it?

>> +               igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
>> +                                             SUSPEND_TEST_NONE);
>> +       }
>> +
>> +       igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
>> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
>> +
>> +       return NULL;
>> +}
> 
> Overall, nothing else strikes me as being absent.
> 
> Oh, one more thing; do we are what state we leave the system in on
> module unload? So long as the GPU reset clears it we should be ok.

I don't think it applies when device is off. And on load running the 
kernel context restores the default full enablement.

Regards,

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

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

* Re: [Intel-gfx] [igt-dev] [PATCH i-g-t v13 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
@ 2018-09-17  9:33                       ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-17  9:33 UTC (permalink / raw)
  To: Chris Wilson, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx


On 14/09/2018 17:17, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2018-09-14 17:04:38)
>> +static igt_spin_t *
>> +__post_set(int fd, unsigned int flags, uint32_t ctx, igt_spin_t *spin,
>> +          unsigned int expected)
>> +{
>> +       bool busy_reset = (flags & TEST_BUSY) &&
>> +                         (flags & (TEST_RESET | TEST_HANG));
>> +
>> +       if (flags & TEST_RESET)
>> +               igt_force_gpu_reset(fd);
>> +       else if (flags & TEST_HANG)
>> +               gem_sync(fd, spin->handle);
> 
> Hmm, in both cases this is before we submit the read/post-sseu batch.
> 
> I was thinking more for the hang you would simply opt to not terminate
> the spinner.

Okay I can do that as well. Hang will then be processed while we try to 
pread the rpcs eb result so I think it works.

> 
> Being utterly pedantic, we may want to check per-engine resets and
> device level.

Isn't the per-engine default, so the hang test flavour would trigger it? 
And to explicitly ask for whole device we have some debugfs api for it?

>> +       if (spin && !busy_reset) {
>> +               igt_assert_eq(read_slice_count_busy(fd, ctx, 0, spin),
>> +                             expected);
>> +       } else {
>> +               igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
>> +       }
>> +
>> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
>> +
>> +       if (spin)
>> +               igt_spin_batch_free(fd, spin);
>> +
>> +       if (flags & TEST_IDLE)
>> +               igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
>> +
>> +       if (flags & TEST_SUSPEND) {
>> +               igt_set_autoresume_delay(5);
> 
> Brave. Opting for a faster wakeup than default...

I think I started with copy paste and then lowered it when testing the 
test. Is the right thing to omit specifying it?

>> +               igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
>> +                                             SUSPEND_TEST_NONE);
>> +       }
>> +
>> +       igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
>> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
>> +
>> +       return NULL;
>> +}
> 
> Overall, nothing else strikes me as being absent.
> 
> Oh, one more thing; do we are what state we leave the system in on
> module unload? So long as the GPU reset clears it we should be ok.

I don't think it applies when device is off. And on load running the 
kernel context restores the default full enablement.

Regards,

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

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

* Re: [igt-dev] [PATCH i-g-t v13 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
  2018-09-17  9:33                       ` [Intel-gfx] " Tvrtko Ursulin
@ 2018-09-17 10:38                         ` Chris Wilson
  -1 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-17 10:38 UTC (permalink / raw)
  To: Tvrtko Ursulin, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-17 10:33:47)
> 
> On 14/09/2018 17:17, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2018-09-14 17:04:38)
> >> +static igt_spin_t *
> >> +__post_set(int fd, unsigned int flags, uint32_t ctx, igt_spin_t *spin,
> >> +          unsigned int expected)
> >> +{
> >> +       bool busy_reset = (flags & TEST_BUSY) &&
> >> +                         (flags & (TEST_RESET | TEST_HANG));
> >> +
> >> +       if (flags & TEST_RESET)
> >> +               igt_force_gpu_reset(fd);
> >> +       else if (flags & TEST_HANG)
> >> +               gem_sync(fd, spin->handle);
> > 
> > Hmm, in both cases this is before we submit the read/post-sseu batch.
> > 
> > I was thinking more for the hang you would simply opt to not terminate
> > the spinner.
> 
> Okay I can do that as well. Hang will then be processed while we try to 
> pread the rpcs eb result so I think it works.
> 
> > 
> > Being utterly pedantic, we may want to check per-engine resets and
> > device level.
> 
> Isn't the per-engine default, so the hang test flavour would trigger it? 
> And to explicitly ask for whole device we have some debugfs api for it?

i915.reset=2 (default if supported) per-engine
i915.reset=1 global
i915.reset=0 none, will wedge on hang.

Alas no temporary override via debugfs yet. Be my guest :)

> >> +       if (spin && !busy_reset) {
> >> +               igt_assert_eq(read_slice_count_busy(fd, ctx, 0, spin),
> >> +                             expected);
> >> +       } else {
> >> +               igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
> >> +       }
> >> +
> >> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
> >> +
> >> +       if (spin)
> >> +               igt_spin_batch_free(fd, spin);
> >> +
> >> +       if (flags & TEST_IDLE)
> >> +               igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
> >> +
> >> +       if (flags & TEST_SUSPEND) {
> >> +               igt_set_autoresume_delay(5);
> > 
> > Brave. Opting for a faster wakeup than default...
> 
> I think I started with copy paste and then lowered it when testing the 
> test. Is the right thing to omit specifying it?

Yes. The default (15s) should be the conservative minimal required for
all machines to be sure we don't miss the wakeup (or it trigger at the
wrong time and cause -EBUSY). There are a few tests where we know
(e.g. we're waiting on hangcheck) that 15s is not enough.

We might suggest to Tomi to do some trial and error and see if we can
stably reduce it across the board.
 
> >> +               igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
> >> +                                             SUSPEND_TEST_NONE);
> >> +       }
> >> +
> >> +       igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
> >> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
> >> +
> >> +       return NULL;
> >> +}
> > 
> > Overall, nothing else strikes me as being absent.
> > 
> > Oh, one more thing; do we are what state we leave the system in on
> > module unload? So long as the GPU reset clears it we should be ok.
> 
> I don't think it applies when device is off. And on load running the 
> kernel context restores the default full enablement.

That's not strictly true... On module load the kernel context will be
marked with RESTORE_INHIBIT as it is created before the default_state
is. It's the next context to be used that will have the full setup.

I think the reset is good enough as the defaults should be sane (or we
have a w/a to set up sane register state), and once we start using the
device proper all is specified. Needless worry on my part.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [Intel-gfx] [igt-dev] [PATCH i-g-t v13 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
@ 2018-09-17 10:38                         ` Chris Wilson
  0 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-17 10:38 UTC (permalink / raw)
  To: Tvrtko Ursulin, Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-17 10:33:47)
> 
> On 14/09/2018 17:17, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2018-09-14 17:04:38)
> >> +static igt_spin_t *
> >> +__post_set(int fd, unsigned int flags, uint32_t ctx, igt_spin_t *spin,
> >> +          unsigned int expected)
> >> +{
> >> +       bool busy_reset = (flags & TEST_BUSY) &&
> >> +                         (flags & (TEST_RESET | TEST_HANG));
> >> +
> >> +       if (flags & TEST_RESET)
> >> +               igt_force_gpu_reset(fd);
> >> +       else if (flags & TEST_HANG)
> >> +               gem_sync(fd, spin->handle);
> > 
> > Hmm, in both cases this is before we submit the read/post-sseu batch.
> > 
> > I was thinking more for the hang you would simply opt to not terminate
> > the spinner.
> 
> Okay I can do that as well. Hang will then be processed while we try to 
> pread the rpcs eb result so I think it works.
> 
> > 
> > Being utterly pedantic, we may want to check per-engine resets and
> > device level.
> 
> Isn't the per-engine default, so the hang test flavour would trigger it? 
> And to explicitly ask for whole device we have some debugfs api for it?

i915.reset=2 (default if supported) per-engine
i915.reset=1 global
i915.reset=0 none, will wedge on hang.

Alas no temporary override via debugfs yet. Be my guest :)

> >> +       if (spin && !busy_reset) {
> >> +               igt_assert_eq(read_slice_count_busy(fd, ctx, 0, spin),
> >> +                             expected);
> >> +       } else {
> >> +               igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
> >> +       }
> >> +
> >> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
> >> +
> >> +       if (spin)
> >> +               igt_spin_batch_free(fd, spin);
> >> +
> >> +       if (flags & TEST_IDLE)
> >> +               igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
> >> +
> >> +       if (flags & TEST_SUSPEND) {
> >> +               igt_set_autoresume_delay(5);
> > 
> > Brave. Opting for a faster wakeup than default...
> 
> I think I started with copy paste and then lowered it when testing the 
> test. Is the right thing to omit specifying it?

Yes. The default (15s) should be the conservative minimal required for
all machines to be sure we don't miss the wakeup (or it trigger at the
wrong time and cause -EBUSY). There are a few tests where we know
(e.g. we're waiting on hangcheck) that 15s is not enough.

We might suggest to Tomi to do some trial and error and see if we can
stably reduce it across the board.
 
> >> +               igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
> >> +                                             SUSPEND_TEST_NONE);
> >> +       }
> >> +
> >> +       igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
> >> +       igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
> >> +
> >> +       return NULL;
> >> +}
> > 
> > Overall, nothing else strikes me as being absent.
> > 
> > Oh, one more thing; do we are what state we leave the system in on
> > module unload? So long as the GPU reset clears it we should be ok.
> 
> I don't think it applies when device is off. And on load running the 
> kernel context restores the default full enablement.

That's not strictly true... On module load the kernel context will be
marked with RESTORE_INHIBIT as it is created before the default_state
is. It's the next context to be used that will have the full setup.

I think the reset is good enough as the defaults should be sane (or we
have a w/a to set up sane register state), and once we start using the
device proper all is specified. Needless worry on my part.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* [PATCH i-g-t v14 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
  2018-09-14 16:17                     ` Chris Wilson
@ 2018-09-17 11:28                       ` Tvrtko Ursulin
  -1 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-17 11:28 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

Verify that the per-context dynamic SSEU uAPI works as expected.

To achieve that, in the absence of a better mechamism, we read the value
of PWR_CLK_STATE register, or use MI_SET_PREDICATE on platforms before
Cannonlake.

This register is written to by the GPU on context restore so this way
we verify i915 is correctly updating the context image in all
circumstances.

v2: Add subslice tests (Lionel)
    Use MI_SET_PREDICATE for further verification when available (Lionel)

v3: Rename to gem_ctx_rpcs (Lionel)

v4: Update kernel API (Lionel)
    Add 0 value test (Lionel)
    Exercise invalid values (Lionel)

v5: Add perf tests (Lionel)

v6: Add new sysfs entry tests (Lionel)

v7: Test rsvd fields
    Update for kernel series changes

v8: Drop test_no_sseu_support() test (Kelvin)
    Drop drm_intel_*() apis (Chris)

v9: by Chris:
    Drop all do_ioctl/do_ioctl_err()
    Use gem_context_[gs]et_param()
    Use gem_read() instead of mapping memory
    by Lionel:
    Test dynamic sseu on/off more

Tvrtko Ursulin:

v10:
 * Various style tweaks and refactorings.
 * New test coverage.

v11:
 * Change platform support to just Gen11.
 * Simplify availability test. (Chris Wilson)
 * More invalid pointer tests. (Chris Wilson)

v12:
 * Fix MAP_FIXED use (doh!).
 * Fix get/set copy&paste errors.
 * Drop supported platform test. (Chris Wilson)
 * Add mmap__gtt test. (Chris Wilson)

v13:
 * Commit message tweaks.
 * Added reset/hang/suspend tests. (Chris Wilson)
 * Assert spinner is busy. (Chris Wilson)
 * Remove some more ABI assumptions. (Chris Wilson)

v14:
 * Use default resume time. (Chris Wilson)
 * Trigger hang after rpcs read batch has been submitted. (Chris Wilson)

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tests/Makefile.am      |    1 +
 tests/Makefile.sources |    1 +
 tests/gem_ctx_param.c  |    4 +-
 tests/gem_ctx_sseu.c   | 1120 ++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |    7 +
 5 files changed, 1132 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_sseu.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index ee5a7c5e83b8..6b67bd2cc17a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -107,6 +107,7 @@ gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
+gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
 gem_fence_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index c84933f1d971..f8f2c8d67d72 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -56,6 +56,7 @@ TESTS_progs = \
 	gem_ctx_exec \
 	gem_ctx_isolation \
 	gem_ctx_param \
+	gem_ctx_sseu \
 	gem_ctx_switch \
 	gem_ctx_thrash \
 	gem_double_irq_loop \
diff --git a/tests/gem_ctx_param.c b/tests/gem_ctx_param.c
index c46fd709b0d7..af1afeaa2f2f 100644
--- a/tests/gem_ctx_param.c
+++ b/tests/gem_ctx_param.c
@@ -294,11 +294,13 @@ igt_main
 			set_priority(fd);
 	}
 
+	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
+
 	/* NOTE: This testcase intentionally tests for the next free parameter
 	 * to catch ABI extensions. Don't "fix" this testcase without adding all
 	 * the tests for the new param first.
 	 */
-	arg.param = I915_CONTEXT_PARAM_PRIORITY + 1;
+	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
 
 	igt_subtest("invalid-param-get") {
 		arg.ctx_id = ctx;
diff --git a/tests/gem_ctx_sseu.c b/tests/gem_ctx_sseu.c
new file mode 100644
index 000000000000..9dde3e457b71
--- /dev/null
+++ b/tests/gem_ctx_sseu.c
@@ -0,0 +1,1120 @@
+/*
+ * Copyright © 2017-2018 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.
+ *
+ * Authors:
+ *    Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include "igt_dummyload.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+#include "ioctl_wrappers.h"
+
+IGT_TEST_DESCRIPTION("Test context render powergating programming.");
+
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+
+#define MI_SET_PREDICATE      (0x1 << 23)
+#define  MI_SET_PREDICATE_NOOP_NEVER         (0)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_SET   (2)
+#define  MI_SET_PREDICATE_NOOP_RESULT_CLEAR  (3)
+#define  MI_SET_PREDICATE_NOOP_RESULT_SET    (4)
+#define  MI_SET_PREDICATE_1_SLICES           (5)
+#define  MI_SET_PREDICATE_2_SLICES           (6)
+#define  MI_SET_PREDICATE_3_SLICES           (7)
+
+#define GEN8_R_PWR_CLK_STATE		0x20C8
+#define   GEN8_RPCS_ENABLE		(1 << 31)
+#define   GEN8_RPCS_S_CNT_ENABLE	(1 << 18)
+#define   GEN8_RPCS_S_CNT_SHIFT		15
+#define   GEN8_RPCS_S_CNT_MASK		(0x7 << GEN8_RPCS_S_CNT_SHIFT)
+#define   GEN11_RPCS_S_CNT_SHIFT	12
+#define   GEN11_RPCS_S_CNT_MASK		(0x3f << GEN11_RPCS_S_CNT_SHIFT)
+#define   GEN8_RPCS_SS_CNT_ENABLE	(1 << 11)
+#define   GEN8_RPCS_SS_CNT_SHIFT	8
+#define   GEN8_RPCS_SS_CNT_MASK		(0x7 << GEN8_RPCS_SS_CNT_SHIFT)
+#define   GEN8_RPCS_EU_MAX_SHIFT	4
+#define   GEN8_RPCS_EU_MAX_MASK		(0xf << GEN8_RPCS_EU_MAX_SHIFT)
+#define   GEN8_RPCS_EU_MIN_SHIFT	0
+#define   GEN8_RPCS_EU_MIN_MASK		(0xf << GEN8_RPCS_EU_MIN_SHIFT)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+static unsigned int __intel_gen__, __intel_devid__;
+static uint64_t __slice_mask__, __subslice_mask__;
+static unsigned int __slice_count__, __subslice_count__;
+
+static uint64_t mask_minus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if ((1ULL << i) & mask)
+			return mask & ~(1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_plus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if (((1ULL << i) & mask) == 0)
+			return mask | (1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_minus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_minus_one(mask);
+
+	return mask;
+}
+
+static uint64_t mask_plus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_plus_one(mask);
+
+	return mask;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+		struct drm_i915_gem_relocation_entry *reloc,
+		uint32_t gem_handle, uint32_t delta, /* in bytes */
+		uint32_t offset, /* in dwords */
+		uint32_t read_domains, uint32_t write_domains)
+{
+	reloc->target_handle = gem_handle;
+	reloc->delta = delta;
+	reloc->offset = offset * sizeof(uint32_t);
+	reloc->presumed_offset = 0;
+	reloc->read_domains = read_domains;
+	reloc->write_domain = write_domains;
+
+	*batch++ = delta;
+	*batch++ = 0;
+
+	return batch;
+}
+
+
+static uint32_t
+read_rpcs_reg(int fd, uint32_t ctx, uint32_t expected_slices, igt_spin_t *spin)
+{
+	struct drm_i915_gem_execbuffer2 execbuf = { };
+	struct drm_i915_gem_relocation_entry relocs[2];
+	struct drm_i915_gem_exec_object2 obj[2];
+	uint32_t *batch, *b, data[2];
+	unsigned int n_relocs = 0;
+	uint32_t rpcs;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[1].handle = gem_create(fd, 4096);
+
+	batch = b =
+	      gem_mmap__cpu(fd, obj[1].handle, 0, 4096, PROT_READ | PROT_WRITE);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) |
+		       (MI_SET_PREDICATE_1_SLICES + expected_slices - 1);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = RCS_TIMESTAMP;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    0, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = GEN8_R_PWR_CLK_STATE;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    4, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) | MI_SET_PREDICATE_NOOP_NEVER;
+
+	*b++ = MI_BATCH_BUFFER_END;
+
+	gem_munmap(batch, 4096);
+
+	obj[1].relocation_count = n_relocs;
+	obj[1].relocs_ptr = to_user_pointer(relocs);
+
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+	i915_execbuffer2_set_context_id(execbuf, ctx);
+
+	gem_execbuf(fd, &execbuf);
+
+	if (spin) {
+		igt_assert(gem_bo_busy(fd, spin->handle));
+		igt_spin_batch_end(spin);
+	}
+
+	gem_read(fd, obj[0].handle, 0, data, sizeof(data));
+
+	rpcs = data[1];
+
+	igt_debug("rcs_timestamp=0x%x rpcs=0x%x\n", data[0], data[1]);
+
+	gem_close(fd, obj[0].handle);
+	gem_close(fd, obj[1].handle);
+
+	return rpcs;
+}
+
+typedef uint32_t (*read_slice_count_f)(int fd, uint32_t ctx, uint32_t expected,
+				       igt_spin_t *spin);
+
+static read_slice_count_f __read_slice_count;
+
+static uint32_t
+read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count)
+{
+	return __read_slice_count(fd, ctx, expected_slice_count, NULL);
+}
+
+static uint32_t
+gen8_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		      igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN8_RPCS_S_CNT_MASK) >> GEN8_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+gen11_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		       igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN11_RPCS_S_CNT_MASK) >> GEN11_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+read_subslice_count(int fd, uint32_t ctx)
+{
+	return (read_rpcs_reg(fd, ctx, 0, NULL) & GEN8_RPCS_SS_CNT_MASK) >>
+	       GEN8_RPCS_SS_CNT_SHIFT;
+}
+
+static bool
+kernel_has_per_context_sseu_support(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{
+		  .param = I915_CONTEXT_PARAM_SSEU,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu),
+		};
+	int ret;
+
+	if (__gem_context_get_param(fd, &arg))
+		return false;
+
+	arg.value = to_user_pointer(&sseu);
+
+	ret = __gem_context_set_param(fd, &arg);
+
+	igt_assert(ret == 0 || ret == -ENODEV || ret == -EINVAL);
+
+	return ret == 0;
+}
+
+static void
+context_get_sseu_masks(int fd, uint32_t ctx,
+		       uint64_t *slice_mask,
+		       uint64_t *subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+
+	if (slice_mask)
+		*slice_mask = sseu.slice_mask;
+
+	if (subslice_mask)
+		*subslice_mask = sseu.subslice_mask;
+}
+
+static void
+context_set_slice_mask(int fd, uint32_t ctx, uint64_t slice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.slice_mask = slice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+static void
+context_set_subslice_mask(int fd, uint32_t ctx, uint64_t subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.subslice_mask = subslice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+/*
+ * Verify that we can program the slice count.
+ */
+static void
+test_slice_pg(int fd, uint32_t pg_slice_count)
+{
+	uint64_t pg_slice_mask = mask_minus(__slice_mask__, pg_slice_count);
+	unsigned int slice_count = __slice_count__ - pg_slice_count;
+	uint64_t slice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(slice_count, __builtin_popcount(pg_slice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_slice_mask(fd, ctx, pg_slice_mask);
+	context_get_sseu_masks(fd, ctx, &slice_mask, NULL);
+	igt_assert_eq(pg_slice_mask, slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx, __slice_count__), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), slice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+/*
+ * Verify that we can program the subslice count.
+ */
+static void
+test_subslice_pg(int fd, int pg_subslice_count)
+{
+	uint64_t pg_subslice_mask =
+		mask_minus(__subslice_mask__, pg_subslice_count);
+	unsigned int subslice_count = __subslice_count__ - pg_subslice_count;
+	uint64_t subslice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(subslice_count, __builtin_popcount(pg_subslice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_subslice_mask(fd, ctx, pg_subslice_mask);
+	context_get_sseu_masks(fd, ctx, NULL, &subslice_mask);
+	igt_assert_eq(pg_subslice_mask, subslice_mask);
+
+	igt_assert_eq(read_subslice_count(fd, ctx), subslice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_engine(int fd, unsigned int class, unsigned int instance)
+{
+	int pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+
+	if (pmu >= 0)
+		close(pmu);
+
+	return pmu >= 0;
+}
+
+/*
+ * Verify that invalid engines are rejected and valid ones are accepted.
+ */
+static void test_engines(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	unsigned int class, instance;
+	int last_with_engines;
+
+	/* get_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+	sseu.class = I915_ENGINE_CLASS_RENDER;
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_get_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert_eq(ret, 0);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	/*
+	 * Get some proper values before trying to reprogram them onto
+	 * an invalid engine.
+	 */
+	sseu.class = 0;
+	sseu.instance = 0;
+	gem_context_get_param(fd, &arg);
+
+	/* set_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_set_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert(ret == 0 || ret == -ENODEV);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid arguments are rejected.
+ */
+static void
+test_invalid_args(int fd)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		};
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	unsigned char *page[2];
+	unsigned char *addr;
+	unsigned int sz;
+
+	/* get param */
+
+	/* Invalid size. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	/* Query size. */
+	arg.size = 0;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	sz = arg.size;
+
+	/* Bad pointers. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(page[1]) -
+		    sizeof(struct drm_i915_gem_context_param_sseu) + 4;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	/* Straddle into read-only area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	igt_assert(mprotect(page[1], 4096, PROT_READ) == 0);
+	arg.value = to_user_pointer(page[1] - sizeof(sseu) + 4);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+	munmap(page[1], 4096);
+
+	/* set param */
+
+	/* Invalid sizes. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	arg.size = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	arg.size = sz;
+
+	/* Bad pointers. */
+	arg.value = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Get valid SSEU. */
+	arg.value = to_user_pointer(&sseu);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memcpy(page[0], &sseu, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	addr = page[1] - sizeof(sseu) + 4;
+	memcpy(addr, &sseu, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(addr);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that ggtt mapped area can be used as the sseu pointer.
+ */
+static void
+test_ggtt_args(int fd)
+{
+	struct drm_i915_gem_context_param_sseu *sseu;
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(*sseu),
+		};
+	uint32_t bo;
+
+	bo = gem_create(fd, 4096);
+	arg.value = to_user_pointer(gem_mmap__gtt(fd, bo, 4096,
+						  PROT_READ | PROT_WRITE));
+
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), 0);
+
+	munmap((void *)arg.value, 4096);
+	gem_close(fd, bo);
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid SSEU values are rejected.
+ */
+static void
+test_invalid_sseu(int fd)
+{
+	struct drm_i915_gem_context_param_sseu default_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		};
+	unsigned int i;
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&default_sseu);
+	gem_context_get_param(fd, &arg);
+
+	arg.value = to_user_pointer(&sseu);
+
+	/* Try all slice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __slice_count__); i++) {
+		sseu.slice_mask = mask_plus(__slice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 slices. */
+	sseu.slice_mask = 0;
+	igt_assert_eq(-EINVAL, __gem_context_set_param(fd, &arg));
+
+	/* Try all subslice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __subslice_count__); i++) {
+		sseu.subslice_mask = mask_plus(__subslice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 subslices. */
+	sseu.subslice_mask = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try number of EUs superior to the max available. */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try to program 0 max EUs. */
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Min > max */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = sseu.max_eus_per_subslice;
+	sseu.max_eus_per_subslice = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	if (__intel_gen__ != 11)
+		goto out;
+
+	/* Subset of subslices but slice mask greater than one. */
+	if (__slice_count__ > 1) {
+		sseu = default_sseu;
+		sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Odd subslices above four. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* More than half subslices with one slice. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 - 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+out:
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/* Verify that the kernel returns a correct error value on Gen < 8. */
+static void
+init_contexts(int fd, uint32_t *ctx, unsigned int num,
+	      uint64_t mask0, uint64_t mask1)
+{
+	unsigned int i;
+
+	igt_assert_eq(num, 2);
+
+	for (i = 0; i < num; i++)
+		ctx[i] = gem_context_create(fd);
+
+	context_set_slice_mask(fd, ctx[0], mask0);
+	context_set_slice_mask(fd, ctx[1], mask1);
+}
+
+/*
+ * Verify that powergating settings are put on hold while i915/perf is
+ * active.
+ */
+static void
+test_perf_oa(int fd)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, 1, /* test metric */
+		DRM_I915_PERF_PROP_OA_FORMAT, I915_OA_FORMAT_A32u40_A4u32_B8_C8,
+		DRM_I915_PERF_PROP_OA_EXPONENT, 20,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+		I915_PERF_FLAG_FD_NONBLOCK,
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int slice_count = __slice_count__ - 1;
+	uint32_t ctx[2];
+	unsigned int i;
+	int perf_fd;
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	/*
+	 * Now open i915/perf and verify that all contexts have been
+	 * reconfigured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1], slice_count),
+				      0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__), 0);
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+
+	/*
+	 * Open i915/perf first and verify that all contexts created
+	 * afterward are configured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Check the device's default values, despite setting
+	 * otherwise.
+	 */
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1],
+						       slice_count), 0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	struct igt_spin_factory opts = {
+		.ctx = ctx,
+		.engine = flags,
+	};
+
+	if (gem_can_store_dword(fd, flags))
+		opts.flags |= IGT_SPIN_POLL_RUN;
+
+	return __igt_spin_batch_factory(fd, &opts);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!READ_ONCE(*spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static uint32_t
+read_slice_count_busy(int fd, uint32_t context, uint32_t expected,
+		      igt_spin_t *spin)
+{
+	return __read_slice_count(fd, context, expected, spin);
+}
+
+#define TEST_IDLE	(1)
+#define TEST_BUSY	(2)
+#define TEST_RESET	(4)
+#define TEST_HANG	(8)
+#define TEST_SUSPEND	(16)
+
+static igt_spin_t *
+__pre_set(int fd, unsigned flags, uint32_t ctx)
+{
+	if (flags & TEST_BUSY)
+		return __spin_sync(fd, ctx, I915_EXEC_RENDER);
+
+	return NULL;
+}
+
+static igt_spin_t *
+__post_set(int fd, unsigned int flags, uint32_t ctx, igt_spin_t *spin,
+	   unsigned int expected)
+{
+	if (flags & TEST_RESET)
+		igt_force_gpu_reset(fd);
+
+	if ((flags & TEST_BUSY) && !(flags & (TEST_RESET | TEST_HANG)))
+		igt_assert_eq(read_slice_count_busy(fd, ctx, 0, spin),
+			      expected);
+	else
+		igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
+
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin)
+		igt_spin_batch_free(fd, spin);
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	if (flags & TEST_SUSPEND)
+		igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
+					      SUSPEND_TEST_NONE);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	return NULL;
+}
+
+/*
+ * Test context re-configuration with either idle or busy contexts.
+ */
+static void
+test_dynamic(int fd, unsigned int flags)
+{
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int pg_slice_count = __slice_count__ - 1;
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	igt_spin_t *spin = NULL;
+
+	igt_require(__slice_count__ > 1);
+
+	gem_context_get_param(fd, &arg);
+
+	/* First set the default mask */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, __slice_count__);
+
+	/* Then set a powergated configuration */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, pg_slice_count);
+
+	/* Put the device's default back again */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, __slice_count__);
+
+	/* One last powergated config for the road... */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, pg_slice_count);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+igt_main
+{
+	unsigned int max_slices = 3, max_subslices = 3;
+	unsigned int i;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		__intel_devid__ = intel_get_drm_devid(fd);
+		__intel_gen__ = intel_gen(__intel_devid__);
+
+		igt_require(kernel_has_per_context_sseu_support(fd));
+
+		if (__intel_gen__ >= 11)
+			__read_slice_count = gen11_read_slice_count;
+		else
+			__read_slice_count = gen8_read_slice_count;
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			drm_i915_getparam_t gp;
+
+			gp.param = I915_PARAM_SLICE_MASK;
+			gp.value = (int *) &__slice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__slice_count__ = __builtin_popcount(__slice_mask__);
+
+			gp.param = I915_PARAM_SUBSLICE_MASK;
+			gp.value = (int *) &__subslice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__subslice_count__ =
+				__builtin_popcount(__subslice_mask__);
+		}
+
+		igt_subtest("invalid-args")
+			test_invalid_args(fd);
+
+		igt_subtest("invalid-sseu")
+			test_invalid_sseu(fd);
+
+		igt_subtest("ggtt-args")
+			test_ggtt_args(fd);
+
+		igt_subtest("engines")
+			test_engines(fd);
+
+		for (i = 1; i < max_slices; i++) {
+			igt_subtest_f("slice-pg-%i", i) {
+				igt_require(__slice_count__ > i);
+
+				test_slice_pg(fd, i);
+			}
+		}
+
+		for (i = 1; i < max_subslices; i++) {
+			igt_subtest_f("subslice-pg-%i", i) {
+				igt_require(__subslice_count__ >= 2);
+
+				/*
+				 * Only available on some Atom platforms and
+				 * Gen10+.
+				 */
+				igt_require(IS_BROXTON(__intel_devid__) ||
+					    IS_GEMINILAKE(__intel_devid__) ||
+					    __intel_gen__ >= 10);
+
+				test_subslice_pg(fd, i);
+			}
+		}
+
+		igt_subtest("perf-oa") {
+			igt_require(__slice_count__ > 1);
+
+			test_perf_oa(fd);
+		}
+
+		igt_subtest("dynamic")
+			test_dynamic(fd, 0);
+
+		igt_subtest("dynamic-busy")
+			test_dynamic(fd, TEST_BUSY);
+
+		igt_subtest("dynamic-reset")
+			test_dynamic(fd, TEST_RESET);
+
+		igt_subtest("dynamic-busy-reset")
+			test_dynamic(fd, TEST_BUSY | TEST_RESET);
+
+		igt_subtest("dynamic-busy-hang")
+			test_dynamic(fd, TEST_BUSY | TEST_HANG);
+
+		igt_subtest("dynamic-idle")
+			test_dynamic(fd, TEST_IDLE);
+
+		igt_subtest("dynamic-suspend")
+			test_dynamic(fd, TEST_SUSPEND);
+
+		igt_subtest("dynamic-idle-suspend")
+			test_dynamic(fd, TEST_IDLE | TEST_SUSPEND);
+	}
+
+	igt_fixture {
+		close(fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 17deb945ec95..9319fa4e19b4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -249,6 +249,13 @@ foreach prog : test_progs
 		   install : true)
 endforeach
 
+test_executables += executable('gem_ctx_sseu', 'gem_ctx_sseu.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_ctx_sseu'
+
 test_executables += executable('gem_eio', 'gem_eio.c',
 	   dependencies : test_deps + [ realtime ],
 	   install_dir : libexecdir,
-- 
2.17.1

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

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

* [Intel-gfx] [PATCH i-g-t v14 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
@ 2018-09-17 11:28                       ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-17 11:28 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

Verify that the per-context dynamic SSEU uAPI works as expected.

To achieve that, in the absence of a better mechamism, we read the value
of PWR_CLK_STATE register, or use MI_SET_PREDICATE on platforms before
Cannonlake.

This register is written to by the GPU on context restore so this way
we verify i915 is correctly updating the context image in all
circumstances.

v2: Add subslice tests (Lionel)
    Use MI_SET_PREDICATE for further verification when available (Lionel)

v3: Rename to gem_ctx_rpcs (Lionel)

v4: Update kernel API (Lionel)
    Add 0 value test (Lionel)
    Exercise invalid values (Lionel)

v5: Add perf tests (Lionel)

v6: Add new sysfs entry tests (Lionel)

v7: Test rsvd fields
    Update for kernel series changes

v8: Drop test_no_sseu_support() test (Kelvin)
    Drop drm_intel_*() apis (Chris)

v9: by Chris:
    Drop all do_ioctl/do_ioctl_err()
    Use gem_context_[gs]et_param()
    Use gem_read() instead of mapping memory
    by Lionel:
    Test dynamic sseu on/off more

Tvrtko Ursulin:

v10:
 * Various style tweaks and refactorings.
 * New test coverage.

v11:
 * Change platform support to just Gen11.
 * Simplify availability test. (Chris Wilson)
 * More invalid pointer tests. (Chris Wilson)

v12:
 * Fix MAP_FIXED use (doh!).
 * Fix get/set copy&paste errors.
 * Drop supported platform test. (Chris Wilson)
 * Add mmap__gtt test. (Chris Wilson)

v13:
 * Commit message tweaks.
 * Added reset/hang/suspend tests. (Chris Wilson)
 * Assert spinner is busy. (Chris Wilson)
 * Remove some more ABI assumptions. (Chris Wilson)

v14:
 * Use default resume time. (Chris Wilson)
 * Trigger hang after rpcs read batch has been submitted. (Chris Wilson)

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 tests/Makefile.am      |    1 +
 tests/Makefile.sources |    1 +
 tests/gem_ctx_param.c  |    4 +-
 tests/gem_ctx_sseu.c   | 1120 ++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |    7 +
 5 files changed, 1132 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_sseu.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index ee5a7c5e83b8..6b67bd2cc17a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -107,6 +107,7 @@ gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
+gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
 gem_fence_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index c84933f1d971..f8f2c8d67d72 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -56,6 +56,7 @@ TESTS_progs = \
 	gem_ctx_exec \
 	gem_ctx_isolation \
 	gem_ctx_param \
+	gem_ctx_sseu \
 	gem_ctx_switch \
 	gem_ctx_thrash \
 	gem_double_irq_loop \
diff --git a/tests/gem_ctx_param.c b/tests/gem_ctx_param.c
index c46fd709b0d7..af1afeaa2f2f 100644
--- a/tests/gem_ctx_param.c
+++ b/tests/gem_ctx_param.c
@@ -294,11 +294,13 @@ igt_main
 			set_priority(fd);
 	}
 
+	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
+
 	/* NOTE: This testcase intentionally tests for the next free parameter
 	 * to catch ABI extensions. Don't "fix" this testcase without adding all
 	 * the tests for the new param first.
 	 */
-	arg.param = I915_CONTEXT_PARAM_PRIORITY + 1;
+	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
 
 	igt_subtest("invalid-param-get") {
 		arg.ctx_id = ctx;
diff --git a/tests/gem_ctx_sseu.c b/tests/gem_ctx_sseu.c
new file mode 100644
index 000000000000..9dde3e457b71
--- /dev/null
+++ b/tests/gem_ctx_sseu.c
@@ -0,0 +1,1120 @@
+/*
+ * Copyright © 2017-2018 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.
+ *
+ * Authors:
+ *    Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include "igt_dummyload.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+#include "ioctl_wrappers.h"
+
+IGT_TEST_DESCRIPTION("Test context render powergating programming.");
+
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+
+#define MI_SET_PREDICATE      (0x1 << 23)
+#define  MI_SET_PREDICATE_NOOP_NEVER         (0)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_SET   (2)
+#define  MI_SET_PREDICATE_NOOP_RESULT_CLEAR  (3)
+#define  MI_SET_PREDICATE_NOOP_RESULT_SET    (4)
+#define  MI_SET_PREDICATE_1_SLICES           (5)
+#define  MI_SET_PREDICATE_2_SLICES           (6)
+#define  MI_SET_PREDICATE_3_SLICES           (7)
+
+#define GEN8_R_PWR_CLK_STATE		0x20C8
+#define   GEN8_RPCS_ENABLE		(1 << 31)
+#define   GEN8_RPCS_S_CNT_ENABLE	(1 << 18)
+#define   GEN8_RPCS_S_CNT_SHIFT		15
+#define   GEN8_RPCS_S_CNT_MASK		(0x7 << GEN8_RPCS_S_CNT_SHIFT)
+#define   GEN11_RPCS_S_CNT_SHIFT	12
+#define   GEN11_RPCS_S_CNT_MASK		(0x3f << GEN11_RPCS_S_CNT_SHIFT)
+#define   GEN8_RPCS_SS_CNT_ENABLE	(1 << 11)
+#define   GEN8_RPCS_SS_CNT_SHIFT	8
+#define   GEN8_RPCS_SS_CNT_MASK		(0x7 << GEN8_RPCS_SS_CNT_SHIFT)
+#define   GEN8_RPCS_EU_MAX_SHIFT	4
+#define   GEN8_RPCS_EU_MAX_MASK		(0xf << GEN8_RPCS_EU_MAX_SHIFT)
+#define   GEN8_RPCS_EU_MIN_SHIFT	0
+#define   GEN8_RPCS_EU_MIN_MASK		(0xf << GEN8_RPCS_EU_MIN_SHIFT)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+static unsigned int __intel_gen__, __intel_devid__;
+static uint64_t __slice_mask__, __subslice_mask__;
+static unsigned int __slice_count__, __subslice_count__;
+
+static uint64_t mask_minus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if ((1ULL << i) & mask)
+			return mask & ~(1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_plus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if (((1ULL << i) & mask) == 0)
+			return mask | (1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_minus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_minus_one(mask);
+
+	return mask;
+}
+
+static uint64_t mask_plus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_plus_one(mask);
+
+	return mask;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+		struct drm_i915_gem_relocation_entry *reloc,
+		uint32_t gem_handle, uint32_t delta, /* in bytes */
+		uint32_t offset, /* in dwords */
+		uint32_t read_domains, uint32_t write_domains)
+{
+	reloc->target_handle = gem_handle;
+	reloc->delta = delta;
+	reloc->offset = offset * sizeof(uint32_t);
+	reloc->presumed_offset = 0;
+	reloc->read_domains = read_domains;
+	reloc->write_domain = write_domains;
+
+	*batch++ = delta;
+	*batch++ = 0;
+
+	return batch;
+}
+
+
+static uint32_t
+read_rpcs_reg(int fd, uint32_t ctx, uint32_t expected_slices, igt_spin_t *spin)
+{
+	struct drm_i915_gem_execbuffer2 execbuf = { };
+	struct drm_i915_gem_relocation_entry relocs[2];
+	struct drm_i915_gem_exec_object2 obj[2];
+	uint32_t *batch, *b, data[2];
+	unsigned int n_relocs = 0;
+	uint32_t rpcs;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[1].handle = gem_create(fd, 4096);
+
+	batch = b =
+	      gem_mmap__cpu(fd, obj[1].handle, 0, 4096, PROT_READ | PROT_WRITE);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) |
+		       (MI_SET_PREDICATE_1_SLICES + expected_slices - 1);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = RCS_TIMESTAMP;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    0, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = GEN8_R_PWR_CLK_STATE;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    4, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) | MI_SET_PREDICATE_NOOP_NEVER;
+
+	*b++ = MI_BATCH_BUFFER_END;
+
+	gem_munmap(batch, 4096);
+
+	obj[1].relocation_count = n_relocs;
+	obj[1].relocs_ptr = to_user_pointer(relocs);
+
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+	i915_execbuffer2_set_context_id(execbuf, ctx);
+
+	gem_execbuf(fd, &execbuf);
+
+	if (spin) {
+		igt_assert(gem_bo_busy(fd, spin->handle));
+		igt_spin_batch_end(spin);
+	}
+
+	gem_read(fd, obj[0].handle, 0, data, sizeof(data));
+
+	rpcs = data[1];
+
+	igt_debug("rcs_timestamp=0x%x rpcs=0x%x\n", data[0], data[1]);
+
+	gem_close(fd, obj[0].handle);
+	gem_close(fd, obj[1].handle);
+
+	return rpcs;
+}
+
+typedef uint32_t (*read_slice_count_f)(int fd, uint32_t ctx, uint32_t expected,
+				       igt_spin_t *spin);
+
+static read_slice_count_f __read_slice_count;
+
+static uint32_t
+read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count)
+{
+	return __read_slice_count(fd, ctx, expected_slice_count, NULL);
+}
+
+static uint32_t
+gen8_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		      igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN8_RPCS_S_CNT_MASK) >> GEN8_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+gen11_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		       igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN11_RPCS_S_CNT_MASK) >> GEN11_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+read_subslice_count(int fd, uint32_t ctx)
+{
+	return (read_rpcs_reg(fd, ctx, 0, NULL) & GEN8_RPCS_SS_CNT_MASK) >>
+	       GEN8_RPCS_SS_CNT_SHIFT;
+}
+
+static bool
+kernel_has_per_context_sseu_support(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{
+		  .param = I915_CONTEXT_PARAM_SSEU,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu),
+		};
+	int ret;
+
+	if (__gem_context_get_param(fd, &arg))
+		return false;
+
+	arg.value = to_user_pointer(&sseu);
+
+	ret = __gem_context_set_param(fd, &arg);
+
+	igt_assert(ret == 0 || ret == -ENODEV || ret == -EINVAL);
+
+	return ret == 0;
+}
+
+static void
+context_get_sseu_masks(int fd, uint32_t ctx,
+		       uint64_t *slice_mask,
+		       uint64_t *subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+
+	if (slice_mask)
+		*slice_mask = sseu.slice_mask;
+
+	if (subslice_mask)
+		*subslice_mask = sseu.subslice_mask;
+}
+
+static void
+context_set_slice_mask(int fd, uint32_t ctx, uint64_t slice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.slice_mask = slice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+static void
+context_set_subslice_mask(int fd, uint32_t ctx, uint64_t subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.subslice_mask = subslice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+/*
+ * Verify that we can program the slice count.
+ */
+static void
+test_slice_pg(int fd, uint32_t pg_slice_count)
+{
+	uint64_t pg_slice_mask = mask_minus(__slice_mask__, pg_slice_count);
+	unsigned int slice_count = __slice_count__ - pg_slice_count;
+	uint64_t slice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(slice_count, __builtin_popcount(pg_slice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_slice_mask(fd, ctx, pg_slice_mask);
+	context_get_sseu_masks(fd, ctx, &slice_mask, NULL);
+	igt_assert_eq(pg_slice_mask, slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx, __slice_count__), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), slice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+/*
+ * Verify that we can program the subslice count.
+ */
+static void
+test_subslice_pg(int fd, int pg_subslice_count)
+{
+	uint64_t pg_subslice_mask =
+		mask_minus(__subslice_mask__, pg_subslice_count);
+	unsigned int subslice_count = __subslice_count__ - pg_subslice_count;
+	uint64_t subslice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(subslice_count, __builtin_popcount(pg_subslice_mask));
+
+	ctx = gem_context_create(fd);
+	context_set_subslice_mask(fd, ctx, pg_subslice_mask);
+	context_get_sseu_masks(fd, ctx, NULL, &subslice_mask);
+	igt_assert_eq(pg_subslice_mask, subslice_mask);
+
+	igt_assert_eq(read_subslice_count(fd, ctx), subslice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_engine(int fd, unsigned int class, unsigned int instance)
+{
+	int pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+
+	if (pmu >= 0)
+		close(pmu);
+
+	return pmu >= 0;
+}
+
+/*
+ * Verify that invalid engines are rejected and valid ones are accepted.
+ */
+static void test_engines(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	unsigned int class, instance;
+	int last_with_engines;
+
+	/* get_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+	sseu.class = I915_ENGINE_CLASS_RENDER;
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_get_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert_eq(ret, 0);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	/*
+	 * Get some proper values before trying to reprogram them onto
+	 * an invalid engine.
+	 */
+	sseu.class = 0;
+	sseu.instance = 0;
+	gem_context_get_param(fd, &arg);
+
+	/* set_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_set_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert(ret == 0 || ret == -ENODEV);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid arguments are rejected.
+ */
+static void
+test_invalid_args(int fd)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		};
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	unsigned char *page[2];
+	unsigned char *addr;
+	unsigned int sz;
+
+	/* get param */
+
+	/* Invalid size. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	/* Query size. */
+	arg.size = 0;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	sz = arg.size;
+
+	/* Bad pointers. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(page[1]) -
+		    sizeof(struct drm_i915_gem_context_param_sseu) + 4;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	/* Straddle into read-only area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	igt_assert(mprotect(page[1], 4096, PROT_READ) == 0);
+	arg.value = to_user_pointer(page[1] - sizeof(sseu) + 4);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+	munmap(page[1], 4096);
+
+	/* set param */
+
+	/* Invalid sizes. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	arg.size = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	arg.size = sz;
+
+	/* Bad pointers. */
+	arg.value = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Get valid SSEU. */
+	arg.value = to_user_pointer(&sseu);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memcpy(page[0], &sseu, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	addr = page[1] - sizeof(sseu) + 4;
+	memcpy(addr, &sseu, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(addr);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that ggtt mapped area can be used as the sseu pointer.
+ */
+static void
+test_ggtt_args(int fd)
+{
+	struct drm_i915_gem_context_param_sseu *sseu;
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(*sseu),
+		};
+	uint32_t bo;
+
+	bo = gem_create(fd, 4096);
+	arg.value = to_user_pointer(gem_mmap__gtt(fd, bo, 4096,
+						  PROT_READ | PROT_WRITE));
+
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), 0);
+
+	munmap((void *)arg.value, 4096);
+	gem_close(fd, bo);
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid SSEU values are rejected.
+ */
+static void
+test_invalid_sseu(int fd)
+{
+	struct drm_i915_gem_context_param_sseu default_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		};
+	unsigned int i;
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&default_sseu);
+	gem_context_get_param(fd, &arg);
+
+	arg.value = to_user_pointer(&sseu);
+
+	/* Try all slice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __slice_count__); i++) {
+		sseu.slice_mask = mask_plus(__slice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 slices. */
+	sseu.slice_mask = 0;
+	igt_assert_eq(-EINVAL, __gem_context_set_param(fd, &arg));
+
+	/* Try all subslice masks known to be invalid. */
+	sseu = default_sseu;
+	for (i = 1; i <= (8 - __subslice_count__); i++) {
+		sseu.subslice_mask = mask_plus(__subslice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 subslices. */
+	sseu.subslice_mask = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try number of EUs superior to the max available. */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = default_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try to program 0 max EUs. */
+	sseu = default_sseu;
+	sseu.max_eus_per_subslice = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Min > max */
+	sseu = default_sseu;
+	sseu.min_eus_per_subslice = sseu.max_eus_per_subslice;
+	sseu.max_eus_per_subslice = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	if (__intel_gen__ != 11)
+		goto out;
+
+	/* Subset of subslices but slice mask greater than one. */
+	if (__slice_count__ > 1) {
+		sseu = default_sseu;
+		sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Odd subslices above four. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* More than half subslices with one slice. */
+	sseu = default_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 - 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+out:
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/* Verify that the kernel returns a correct error value on Gen < 8. */
+static void
+init_contexts(int fd, uint32_t *ctx, unsigned int num,
+	      uint64_t mask0, uint64_t mask1)
+{
+	unsigned int i;
+
+	igt_assert_eq(num, 2);
+
+	for (i = 0; i < num; i++)
+		ctx[i] = gem_context_create(fd);
+
+	context_set_slice_mask(fd, ctx[0], mask0);
+	context_set_slice_mask(fd, ctx[1], mask1);
+}
+
+/*
+ * Verify that powergating settings are put on hold while i915/perf is
+ * active.
+ */
+static void
+test_perf_oa(int fd)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, 1, /* test metric */
+		DRM_I915_PERF_PROP_OA_FORMAT, I915_OA_FORMAT_A32u40_A4u32_B8_C8,
+		DRM_I915_PERF_PROP_OA_EXPONENT, 20,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+		I915_PERF_FLAG_FD_NONBLOCK,
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int slice_count = __slice_count__ - 1;
+	uint32_t ctx[2];
+	unsigned int i;
+	int perf_fd;
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	/*
+	 * Now open i915/perf and verify that all contexts have been
+	 * reconfigured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1], slice_count),
+				      0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__), 0);
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+
+	/*
+	 * Open i915/perf first and verify that all contexts created
+	 * afterward are configured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx), __slice_mask__, pg_slice_mask);
+
+	/*
+	 * Check the device's default values, despite setting
+	 * otherwise.
+	 */
+	if (__intel_gen__ != 11) {
+		if (__intel_gen__ < 10)
+			igt_assert_eq(read_slice_count(fd, ctx[1],
+						       slice_count), 0);
+
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), __slice_count__);
+	} else {
+		igt_assert_eq(read_slice_count(fd, ctx[0], 0), 1);
+		igt_assert_eq(read_slice_count(fd, ctx[1], 0), 1);
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	struct igt_spin_factory opts = {
+		.ctx = ctx,
+		.engine = flags,
+	};
+
+	if (gem_can_store_dword(fd, flags))
+		opts.flags |= IGT_SPIN_POLL_RUN;
+
+	return __igt_spin_batch_factory(fd, &opts);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!READ_ONCE(*spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static uint32_t
+read_slice_count_busy(int fd, uint32_t context, uint32_t expected,
+		      igt_spin_t *spin)
+{
+	return __read_slice_count(fd, context, expected, spin);
+}
+
+#define TEST_IDLE	(1)
+#define TEST_BUSY	(2)
+#define TEST_RESET	(4)
+#define TEST_HANG	(8)
+#define TEST_SUSPEND	(16)
+
+static igt_spin_t *
+__pre_set(int fd, unsigned flags, uint32_t ctx)
+{
+	if (flags & TEST_BUSY)
+		return __spin_sync(fd, ctx, I915_EXEC_RENDER);
+
+	return NULL;
+}
+
+static igt_spin_t *
+__post_set(int fd, unsigned int flags, uint32_t ctx, igt_spin_t *spin,
+	   unsigned int expected)
+{
+	if (flags & TEST_RESET)
+		igt_force_gpu_reset(fd);
+
+	if ((flags & TEST_BUSY) && !(flags & (TEST_RESET | TEST_HANG)))
+		igt_assert_eq(read_slice_count_busy(fd, ctx, 0, spin),
+			      expected);
+	else
+		igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
+
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin)
+		igt_spin_batch_free(fd, spin);
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	if (flags & TEST_SUSPEND)
+		igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
+					      SUSPEND_TEST_NONE);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	return NULL;
+}
+
+/*
+ * Test context re-configuration with either idle or busy contexts.
+ */
+static void
+test_dynamic(int fd, unsigned int flags)
+{
+	uint64_t pg_slice_mask = mask_minus_one(__slice_mask__);
+	unsigned int pg_slice_count = __slice_count__ - 1;
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	igt_spin_t *spin = NULL;
+
+	igt_require(__slice_count__ > 1);
+
+	gem_context_get_param(fd, &arg);
+
+	/* First set the default mask */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, __slice_count__);
+
+	/* Then set a powergated configuration */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, pg_slice_count);
+
+	/* Put the device's default back again */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = __slice_mask__;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, __slice_count__);
+
+	/* One last powergated config for the road... */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	sseu.slice_mask = pg_slice_mask;
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin, pg_slice_count);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+igt_main
+{
+	unsigned int max_slices = 3, max_subslices = 3;
+	unsigned int i;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		__intel_devid__ = intel_get_drm_devid(fd);
+		__intel_gen__ = intel_gen(__intel_devid__);
+
+		igt_require(kernel_has_per_context_sseu_support(fd));
+
+		if (__intel_gen__ >= 11)
+			__read_slice_count = gen11_read_slice_count;
+		else
+			__read_slice_count = gen8_read_slice_count;
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			drm_i915_getparam_t gp;
+
+			gp.param = I915_PARAM_SLICE_MASK;
+			gp.value = (int *) &__slice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__slice_count__ = __builtin_popcount(__slice_mask__);
+
+			gp.param = I915_PARAM_SUBSLICE_MASK;
+			gp.value = (int *) &__subslice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__subslice_count__ =
+				__builtin_popcount(__subslice_mask__);
+		}
+
+		igt_subtest("invalid-args")
+			test_invalid_args(fd);
+
+		igt_subtest("invalid-sseu")
+			test_invalid_sseu(fd);
+
+		igt_subtest("ggtt-args")
+			test_ggtt_args(fd);
+
+		igt_subtest("engines")
+			test_engines(fd);
+
+		for (i = 1; i < max_slices; i++) {
+			igt_subtest_f("slice-pg-%i", i) {
+				igt_require(__slice_count__ > i);
+
+				test_slice_pg(fd, i);
+			}
+		}
+
+		for (i = 1; i < max_subslices; i++) {
+			igt_subtest_f("subslice-pg-%i", i) {
+				igt_require(__subslice_count__ >= 2);
+
+				/*
+				 * Only available on some Atom platforms and
+				 * Gen10+.
+				 */
+				igt_require(IS_BROXTON(__intel_devid__) ||
+					    IS_GEMINILAKE(__intel_devid__) ||
+					    __intel_gen__ >= 10);
+
+				test_subslice_pg(fd, i);
+			}
+		}
+
+		igt_subtest("perf-oa") {
+			igt_require(__slice_count__ > 1);
+
+			test_perf_oa(fd);
+		}
+
+		igt_subtest("dynamic")
+			test_dynamic(fd, 0);
+
+		igt_subtest("dynamic-busy")
+			test_dynamic(fd, TEST_BUSY);
+
+		igt_subtest("dynamic-reset")
+			test_dynamic(fd, TEST_RESET);
+
+		igt_subtest("dynamic-busy-reset")
+			test_dynamic(fd, TEST_BUSY | TEST_RESET);
+
+		igt_subtest("dynamic-busy-hang")
+			test_dynamic(fd, TEST_BUSY | TEST_HANG);
+
+		igt_subtest("dynamic-idle")
+			test_dynamic(fd, TEST_IDLE);
+
+		igt_subtest("dynamic-suspend")
+			test_dynamic(fd, TEST_SUSPEND);
+
+		igt_subtest("dynamic-idle-suspend")
+			test_dynamic(fd, TEST_IDLE | TEST_SUSPEND);
+	}
+
+	igt_fixture {
+		close(fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 17deb945ec95..9319fa4e19b4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -249,6 +249,13 @@ foreach prog : test_progs
 		   install : true)
 endforeach
 
+test_executables += executable('gem_ctx_sseu', 'gem_ctx_sseu.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_ctx_sseu'
+
 test_executables += executable('gem_eio', 'gem_eio.c',
 	   dependencies : test_deps + [ realtime ],
 	   install_dir : libexecdir,
-- 
2.17.1

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

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

* Re: [igt-dev] [PATCH i-g-t v14 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
  2018-09-17 11:28                       ` [Intel-gfx] " Tvrtko Ursulin
@ 2018-09-17 12:04                         ` Chris Wilson
  -1 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-17 12:04 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx

Quoting Tvrtko Ursulin (2018-09-17 12:28:12)
> From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
> 
> Verify that the per-context dynamic SSEU uAPI works as expected.
> 
> To achieve that, in the absence of a better mechamism, we read the value
> of PWR_CLK_STATE register, or use MI_SET_PREDICATE on platforms before
> Cannonlake.
> 
> This register is written to by the GPU on context restore so this way
> we verify i915 is correctly updating the context image in all
> circumstances.
> 
> v2: Add subslice tests (Lionel)
>     Use MI_SET_PREDICATE for further verification when available (Lionel)
> 
> v3: Rename to gem_ctx_rpcs (Lionel)
> 
> v4: Update kernel API (Lionel)
>     Add 0 value test (Lionel)
>     Exercise invalid values (Lionel)
> 
> v5: Add perf tests (Lionel)
> 
> v6: Add new sysfs entry tests (Lionel)
> 
> v7: Test rsvd fields
>     Update for kernel series changes
> 
> v8: Drop test_no_sseu_support() test (Kelvin)
>     Drop drm_intel_*() apis (Chris)
> 
> v9: by Chris:
>     Drop all do_ioctl/do_ioctl_err()
>     Use gem_context_[gs]et_param()
>     Use gem_read() instead of mapping memory
>     by Lionel:
>     Test dynamic sseu on/off more
> 
> Tvrtko Ursulin:
> 
> v10:
>  * Various style tweaks and refactorings.
>  * New test coverage.
> 
> v11:
>  * Change platform support to just Gen11.
>  * Simplify availability test. (Chris Wilson)
>  * More invalid pointer tests. (Chris Wilson)
> 
> v12:
>  * Fix MAP_FIXED use (doh!).
>  * Fix get/set copy&paste errors.
>  * Drop supported platform test. (Chris Wilson)
>  * Add mmap__gtt test. (Chris Wilson)
> 
> v13:
>  * Commit message tweaks.
>  * Added reset/hang/suspend tests. (Chris Wilson)
>  * Assert spinner is busy. (Chris Wilson)
>  * Remove some more ABI assumptions. (Chris Wilson)
> 
> v14:
>  * Use default resume time. (Chris Wilson)
>  * Trigger hang after rpcs read batch has been submitted. (Chris Wilson)
> 
> Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

I can't think of any other mechanism that could interfere with ctx-sseu
that isn't being tested,
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [igt-dev] [PATCH i-g-t v14 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
@ 2018-09-17 12:04                         ` Chris Wilson
  0 siblings, 0 replies; 55+ messages in thread
From: Chris Wilson @ 2018-09-17 12:04 UTC (permalink / raw)
  To: Tvrtko Ursulin, igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

Quoting Tvrtko Ursulin (2018-09-17 12:28:12)
> From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
> 
> Verify that the per-context dynamic SSEU uAPI works as expected.
> 
> To achieve that, in the absence of a better mechamism, we read the value
> of PWR_CLK_STATE register, or use MI_SET_PREDICATE on platforms before
> Cannonlake.
> 
> This register is written to by the GPU on context restore so this way
> we verify i915 is correctly updating the context image in all
> circumstances.
> 
> v2: Add subslice tests (Lionel)
>     Use MI_SET_PREDICATE for further verification when available (Lionel)
> 
> v3: Rename to gem_ctx_rpcs (Lionel)
> 
> v4: Update kernel API (Lionel)
>     Add 0 value test (Lionel)
>     Exercise invalid values (Lionel)
> 
> v5: Add perf tests (Lionel)
> 
> v6: Add new sysfs entry tests (Lionel)
> 
> v7: Test rsvd fields
>     Update for kernel series changes
> 
> v8: Drop test_no_sseu_support() test (Kelvin)
>     Drop drm_intel_*() apis (Chris)
> 
> v9: by Chris:
>     Drop all do_ioctl/do_ioctl_err()
>     Use gem_context_[gs]et_param()
>     Use gem_read() instead of mapping memory
>     by Lionel:
>     Test dynamic sseu on/off more
> 
> Tvrtko Ursulin:
> 
> v10:
>  * Various style tweaks and refactorings.
>  * New test coverage.
> 
> v11:
>  * Change platform support to just Gen11.
>  * Simplify availability test. (Chris Wilson)
>  * More invalid pointer tests. (Chris Wilson)
> 
> v12:
>  * Fix MAP_FIXED use (doh!).
>  * Fix get/set copy&paste errors.
>  * Drop supported platform test. (Chris Wilson)
>  * Add mmap__gtt test. (Chris Wilson)
> 
> v13:
>  * Commit message tweaks.
>  * Added reset/hang/suspend tests. (Chris Wilson)
>  * Assert spinner is busy. (Chris Wilson)
>  * Remove some more ABI assumptions. (Chris Wilson)
> 
> v14:
>  * Use default resume time. (Chris Wilson)
>  * Trigger hang after rpcs read batch has been submitted. (Chris Wilson)
> 
> Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

I can't think of any other mechanism that could interfere with ctx-sseu
that isn't being tested,
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [igt-dev] ✗ Fi.CI.BAT: failure for Per context dynamic (sub)slice power-gating (rev5)
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
                   ` (8 preceding siblings ...)
  (?)
@ 2018-09-17 12:21 ` Patchwork
  -1 siblings, 0 replies; 55+ messages in thread
From: Patchwork @ 2018-09-17 12:21 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: igt-dev

== Series Details ==

Series: Per context dynamic (sub)slice power-gating (rev5)
URL   : https://patchwork.freedesktop.org/series/49190/
State : failure

== Summary ==

= CI Bug Log - changes from CI_DRM_4833 -> IGTPW_1848 =

== Summary - FAILURE ==

  Serious unknown changes coming with IGTPW_1848 absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_1848, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/49190/revisions/5/mbox/

== Possible new issues ==

  Here are the unknown changes that may have been introduced in IGTPW_1848:

  === IGT changes ===

    ==== Possible regressions ====

    igt@drv_selftest@live_contexts:
      fi-bsw-n3050:       PASS -> DMESG-WARN

    
== Known issues ==

  Here are the changes found in IGTPW_1848 that come from known issues:

  === IGT changes ===

    ==== Issues hit ====

    igt@gem_exec_suspend@basic-s3:
      fi-skl-caroline:    NOTRUN -> INCOMPLETE (fdo#107556, fdo#104108)

    igt@gem_exec_suspend@basic-s4-devices:
      fi-blb-e6850:       PASS -> INCOMPLETE (fdo#107718)

    igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
      fi-cfl-8109u:       PASS -> INCOMPLETE (fdo#106070)

    igt@kms_pipe_crc_basic@suspend-read-crc-pipe-c:
      fi-bsw-n3050:       PASS -> DMESG-WARN (fdo#106207) +6

    igt@kms_psr@primary_mmap_gtt:
      {fi-cnl-u}:         NOTRUN -> FAIL (fdo#107383) +3

    
    ==== Possible fixes ====

    igt@drv_selftest@live_hangcheck:
      fi-glk-j4005:       INCOMPLETE (fdo#103359, k.org#198133) -> PASS

    igt@kms_pipe_crc_basic@suspend-read-crc-pipe-a:
      fi-icl-u:           INCOMPLETE (fdo#107713) -> PASS

    igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
      fi-snb-2520m:       DMESG-FAIL (fdo#103713) -> PASS

    igt@kms_setmode@basic-clone-single-crtc:
      fi-snb-2520m:       DMESG-WARN (fdo#103713) -> PASS

    
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  fdo#103359 https://bugs.freedesktop.org/show_bug.cgi?id=103359
  fdo#103713 https://bugs.freedesktop.org/show_bug.cgi?id=103713
  fdo#104108 https://bugs.freedesktop.org/show_bug.cgi?id=104108
  fdo#106070 https://bugs.freedesktop.org/show_bug.cgi?id=106070
  fdo#106207 https://bugs.freedesktop.org/show_bug.cgi?id=106207
  fdo#107383 https://bugs.freedesktop.org/show_bug.cgi?id=107383
  fdo#107556 https://bugs.freedesktop.org/show_bug.cgi?id=107556
  fdo#107713 https://bugs.freedesktop.org/show_bug.cgi?id=107713
  fdo#107718 https://bugs.freedesktop.org/show_bug.cgi?id=107718
  k.org#198133 https://bugzilla.kernel.org/show_bug.cgi?id=198133


== Participating hosts (46 -> 44) ==

  Additional (2): fi-cnl-u fi-skl-caroline 
  Missing    (4): fi-ilk-m540 fi-byt-squawks fi-bsw-cyan fi-hsw-4200u 


== Build changes ==

    * IGT: IGT_4644 -> IGTPW_1848

  CI_DRM_4833: 75bb460b367a614d10b0fba220143bee42657d7e @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_1848: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1848/
  IGT_4644: 0b59bb3231ab481959528c5c7b3a98762772e1b0 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools



== Testlist changes ==

+igt@gem_ctx_sseu@dynamic
+igt@gem_ctx_sseu@dynamic-busy
+igt@gem_ctx_sseu@dynamic-busy-hang
+igt@gem_ctx_sseu@dynamic-busy-reset
+igt@gem_ctx_sseu@dynamic-idle
+igt@gem_ctx_sseu@dynamic-idle-suspend
+igt@gem_ctx_sseu@dynamic-reset
+igt@gem_ctx_sseu@dynamic-suspend
+igt@gem_ctx_sseu@engines
+igt@gem_ctx_sseu@ggtt-args
+igt@gem_ctx_sseu@invalid-args
+igt@gem_ctx_sseu@invalid-sseu
+igt@gem_ctx_sseu@perf-oa
+igt@gem_ctx_sseu@slice-pg-1
+igt@gem_ctx_sseu@slice-pg-2
+igt@gem_ctx_sseu@subslice-pg-1
+igt@gem_ctx_sseu@subslice-pg-2

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1848/issues.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [igt-dev] ✓ Fi.CI.BAT: success for Per context dynamic (sub)slice power-gating (rev5)
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
                   ` (9 preceding siblings ...)
  (?)
@ 2018-09-17 18:21 ` Patchwork
  -1 siblings, 0 replies; 55+ messages in thread
From: Patchwork @ 2018-09-17 18:21 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: igt-dev

== Series Details ==

Series: Per context dynamic (sub)slice power-gating (rev5)
URL   : https://patchwork.freedesktop.org/series/49190/
State : success

== Summary ==

= CI Bug Log - changes from CI_DRM_4833 -> IGTPW_1851 =

== Summary - WARNING ==

  Minor unknown changes coming with IGTPW_1851 need to be verified
  manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_1851, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/49190/revisions/5/mbox/

== Possible new issues ==

  Here are the unknown changes that may have been introduced in IGTPW_1851:

  === IGT changes ===

    ==== Warnings ====

    igt@pm_rpm@module-reload:
      fi-hsw-4770r:       PASS -> SKIP

    
== Known issues ==

  Here are the changes found in IGTPW_1851 that come from known issues:

  === IGT changes ===

    ==== Issues hit ====

    igt@drv_module_reload@basic-reload-inject:
      fi-hsw-4770r:       PASS -> DMESG-WARN (fdo#107924, fdo#107425)

    igt@drv_selftest@mock_hugepages:
      fi-bwr-2160:        PASS -> DMESG-FAIL (fdo#107930)

    igt@gem_exec_suspend@basic-s3:
      fi-skl-caroline:    NOTRUN -> INCOMPLETE (fdo#104108, fdo#107556)

    igt@kms_frontbuffer_tracking@basic:
      fi-byt-clapper:     PASS -> FAIL (fdo#103167)

    igt@kms_pipe_crc_basic@read-crc-pipe-b-frame-sequence:
      fi-byt-clapper:     PASS -> FAIL (fdo#107362, fdo#103191)

    igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
      fi-blb-e6850:       PASS -> INCOMPLETE (fdo#107718)

    igt@kms_psr@primary_mmap_gtt:
      {fi-cnl-u}:         NOTRUN -> FAIL (fdo#107383) +3

    
    ==== Possible fixes ====

    igt@drv_selftest@live_hangcheck:
      fi-glk-j4005:       INCOMPLETE (k.org#198133, fdo#103359) -> PASS

    igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
      fi-snb-2520m:       DMESG-FAIL (fdo#103713) -> PASS

    igt@kms_psr@primary_page_flip:
      fi-kbl-r:           FAIL (fdo#107336) -> PASS

    igt@kms_setmode@basic-clone-single-crtc:
      fi-snb-2520m:       DMESG-WARN (fdo#103713) -> PASS

    
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  fdo#103167 https://bugs.freedesktop.org/show_bug.cgi?id=103167
  fdo#103191 https://bugs.freedesktop.org/show_bug.cgi?id=103191
  fdo#103359 https://bugs.freedesktop.org/show_bug.cgi?id=103359
  fdo#103713 https://bugs.freedesktop.org/show_bug.cgi?id=103713
  fdo#104108 https://bugs.freedesktop.org/show_bug.cgi?id=104108
  fdo#107336 https://bugs.freedesktop.org/show_bug.cgi?id=107336
  fdo#107362 https://bugs.freedesktop.org/show_bug.cgi?id=107362
  fdo#107383 https://bugs.freedesktop.org/show_bug.cgi?id=107383
  fdo#107425 https://bugs.freedesktop.org/show_bug.cgi?id=107425
  fdo#107556 https://bugs.freedesktop.org/show_bug.cgi?id=107556
  fdo#107718 https://bugs.freedesktop.org/show_bug.cgi?id=107718
  fdo#107924 https://bugs.freedesktop.org/show_bug.cgi?id=107924
  fdo#107930 https://bugs.freedesktop.org/show_bug.cgi?id=107930
  k.org#198133 https://bugzilla.kernel.org/show_bug.cgi?id=198133


== Participating hosts (46 -> 42) ==

  Additional (2): fi-cnl-u fi-skl-caroline 
  Missing    (6): fi-ilk-m540 fi-hsw-4200u fi-byt-squawks fi-bsw-cyan fi-bsw-kefka fi-icl-u 


== Build changes ==

    * IGT: IGT_4644 -> IGTPW_1851

  CI_DRM_4833: 75bb460b367a614d10b0fba220143bee42657d7e @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_1851: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1851/
  IGT_4644: 0b59bb3231ab481959528c5c7b3a98762772e1b0 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools



== Testlist changes ==

+igt@gem_ctx_sseu@dynamic
+igt@gem_ctx_sseu@dynamic-busy
+igt@gem_ctx_sseu@dynamic-busy-hang
+igt@gem_ctx_sseu@dynamic-busy-reset
+igt@gem_ctx_sseu@dynamic-idle
+igt@gem_ctx_sseu@dynamic-idle-suspend
+igt@gem_ctx_sseu@dynamic-reset
+igt@gem_ctx_sseu@dynamic-suspend
+igt@gem_ctx_sseu@engines
+igt@gem_ctx_sseu@ggtt-args
+igt@gem_ctx_sseu@invalid-args
+igt@gem_ctx_sseu@invalid-sseu
+igt@gem_ctx_sseu@perf-oa
+igt@gem_ctx_sseu@slice-pg-1
+igt@gem_ctx_sseu@slice-pg-2
+igt@gem_ctx_sseu@subslice-pg-1
+igt@gem_ctx_sseu@subslice-pg-2

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1851/issues.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [igt-dev] ✓ Fi.CI.IGT: success for Per context dynamic (sub)slice power-gating (rev5)
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
                   ` (10 preceding siblings ...)
  (?)
@ 2018-09-17 20:43 ` Patchwork
  -1 siblings, 0 replies; 55+ messages in thread
From: Patchwork @ 2018-09-17 20:43 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: igt-dev

== Series Details ==

Series: Per context dynamic (sub)slice power-gating (rev5)
URL   : https://patchwork.freedesktop.org/series/49190/
State : success

== Summary ==

= CI Bug Log - changes from IGT_4644_full -> IGTPW_1851_full =

== Summary - WARNING ==

  Minor unknown changes coming with IGTPW_1851_full need to be verified
  manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_1851_full, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/49190/revisions/5/mbox/

== Possible new issues ==

  Here are the unknown changes that may have been introduced in IGTPW_1851_full:

  === IGT changes ===

    ==== Warnings ====

    igt@perf_pmu@rc6:
      shard-kbl:          SKIP -> PASS

    igt@pm_rc6_residency@rc6-accuracy:
      shard-kbl:          PASS -> SKIP

    
== Known issues ==

  Here are the changes found in IGTPW_1851_full that come from known issues:

  === IGT changes ===

    ==== Issues hit ====

    igt@kms_cursor_legacy@2x-long-cursor-vs-flip-legacy:
      shard-hsw:          PASS -> FAIL (fdo#105767)

    igt@kms_cursor_legacy@2x-long-flip-vs-cursor-legacy:
      shard-glk:          PASS -> FAIL (fdo#104873)

    igt@kms_frontbuffer_tracking@fbc-1p-offscren-pri-shrfb-draw-blt:
      shard-glk:          PASS -> FAIL (fdo#103167)

    igt@kms_pipe_crc_basic@suspend-read-crc-pipe-c:
      shard-kbl:          PASS -> INCOMPLETE (fdo#103665) +1

    
    ==== Possible fixes ====

    igt@gem_exec_big:
      shard-hsw:          TIMEOUT (fdo#107937) -> PASS

    igt@gem_ppgtt@blt-vs-render-ctxn:
      shard-kbl:          INCOMPLETE (fdo#106023, fdo#103665) -> PASS

    igt@kms_flip@flip-vs-expired-vblank-interruptible:
      shard-glk:          FAIL (fdo#105363) -> PASS

    igt@kms_frontbuffer_tracking@fbc-stridechange:
      shard-glk:          FAIL (fdo#103167) -> PASS

    igt@kms_rotation_crc@sprite-rotation-180:
      shard-snb:          FAIL (fdo#103925) -> PASS

    
  fdo#103167 https://bugs.freedesktop.org/show_bug.cgi?id=103167
  fdo#103665 https://bugs.freedesktop.org/show_bug.cgi?id=103665
  fdo#103925 https://bugs.freedesktop.org/show_bug.cgi?id=103925
  fdo#104873 https://bugs.freedesktop.org/show_bug.cgi?id=104873
  fdo#105363 https://bugs.freedesktop.org/show_bug.cgi?id=105363
  fdo#105767 https://bugs.freedesktop.org/show_bug.cgi?id=105767
  fdo#106023 https://bugs.freedesktop.org/show_bug.cgi?id=106023
  fdo#107937 https://bugs.freedesktop.org/show_bug.cgi?id=107937


== Participating hosts (5 -> 5) ==

  No changes in participating hosts


== Build changes ==

    * IGT: IGT_4644 -> IGTPW_1851
    * Linux: CI_DRM_4827 -> CI_DRM_4833

  CI_DRM_4827: 8b1968f143e8bf65acf0ea6f7ce9120259043521 @ git://anongit.freedesktop.org/gfx-ci/linux
  CI_DRM_4833: 75bb460b367a614d10b0fba220143bee42657d7e @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_1851: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1851/
  IGT_4644: 0b59bb3231ab481959528c5c7b3a98762772e1b0 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1851/shards.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [PATCH i-g-t v15 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
  2018-09-17 11:28                       ` [Intel-gfx] " Tvrtko Ursulin
@ 2018-09-18 13:41                         ` Tvrtko Ursulin
  -1 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-18 13:41 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

Verify that the per-context dynamic SSEU uAPI works as expected.

To achieve that, in the absence of a better mechamism, we read the value
of PWR_CLK_STATE register, or use MI_SET_PREDICATE on platforms before
Cannonlake.

This register is written to by the GPU on context restore so this way
we verify i915 is correctly updating the context image in all
circumstances.

v2: Add subslice tests (Lionel)
    Use MI_SET_PREDICATE for further verification when available (Lionel)

v3: Rename to gem_ctx_rpcs (Lionel)

v4: Update kernel API (Lionel)
    Add 0 value test (Lionel)
    Exercise invalid values (Lionel)

v5: Add perf tests (Lionel)

v6: Add new sysfs entry tests (Lionel)

v7: Test rsvd fields
    Update for kernel series changes

v8: Drop test_no_sseu_support() test (Kelvin)
    Drop drm_intel_*() apis (Chris)

v9: by Chris:
    Drop all do_ioctl/do_ioctl_err()
    Use gem_context_[gs]et_param()
    Use gem_read() instead of mapping memory
    by Lionel:
    Test dynamic sseu on/off more

Tvrtko Ursulin:

v10:
 * Various style tweaks and refactorings.
 * New test coverage.

v11:
 * Change platform support to just Gen11.
 * Simplify availability test. (Chris Wilson)
 * More invalid pointer tests. (Chris Wilson)

v12:
 * Fix MAP_FIXED use (doh!).
 * Fix get/set copy&paste errors.
 * Drop supported platform test. (Chris Wilson)
 * Add mmap__gtt test. (Chris Wilson)

v13:
 * Commit message tweaks.
 * Added reset/hang/suspend tests. (Chris Wilson)
 * Assert spinner is busy. (Chris Wilson)
 * Remove some more ABI assumptions. (Chris Wilson)

v14:
 * Use default resume time. (Chris Wilson)
 * Trigger hang after rpcs read batch has been submitted. (Chris Wilson)

v15:
 * Adjust for uAPI restrictions.

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> # v14
---
 tests/Makefile.am      |    1 +
 tests/Makefile.sources |    1 +
 tests/gem_ctx_param.c  |    4 +-
 tests/gem_ctx_sseu.c   | 1190 ++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |    7 +
 5 files changed, 1202 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_sseu.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index ee5a7c5e83b8..6b67bd2cc17a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -107,6 +107,7 @@ gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
+gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
 gem_fence_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 269336ad3150..6765143bf344 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -55,6 +55,7 @@ TESTS_progs = \
 	gem_ctx_exec \
 	gem_ctx_isolation \
 	gem_ctx_param \
+	gem_ctx_sseu \
 	gem_ctx_switch \
 	gem_ctx_thrash \
 	gem_double_irq_loop \
diff --git a/tests/gem_ctx_param.c b/tests/gem_ctx_param.c
index c46fd709b0d7..af1afeaa2f2f 100644
--- a/tests/gem_ctx_param.c
+++ b/tests/gem_ctx_param.c
@@ -294,11 +294,13 @@ igt_main
 			set_priority(fd);
 	}
 
+	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
+
 	/* NOTE: This testcase intentionally tests for the next free parameter
 	 * to catch ABI extensions. Don't "fix" this testcase without adding all
 	 * the tests for the new param first.
 	 */
-	arg.param = I915_CONTEXT_PARAM_PRIORITY + 1;
+	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
 
 	igt_subtest("invalid-param-get") {
 		arg.ctx_id = ctx;
diff --git a/tests/gem_ctx_sseu.c b/tests/gem_ctx_sseu.c
new file mode 100644
index 000000000000..889f70643392
--- /dev/null
+++ b/tests/gem_ctx_sseu.c
@@ -0,0 +1,1190 @@
+/*
+ * Copyright © 2017-2018 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.
+ *
+ * Authors:
+ *    Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include "igt_dummyload.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+#include "ioctl_wrappers.h"
+
+IGT_TEST_DESCRIPTION("Test context render powergating programming.");
+
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+
+#define MI_SET_PREDICATE      (0x1 << 23)
+#define  MI_SET_PREDICATE_NOOP_NEVER         (0)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_SET   (2)
+#define  MI_SET_PREDICATE_NOOP_RESULT_CLEAR  (3)
+#define  MI_SET_PREDICATE_NOOP_RESULT_SET    (4)
+#define  MI_SET_PREDICATE_1_SLICES           (5)
+#define  MI_SET_PREDICATE_2_SLICES           (6)
+#define  MI_SET_PREDICATE_3_SLICES           (7)
+
+#define GEN8_R_PWR_CLK_STATE		0x20C8
+#define   GEN8_RPCS_ENABLE		(1 << 31)
+#define   GEN8_RPCS_S_CNT_ENABLE	(1 << 18)
+#define   GEN8_RPCS_S_CNT_SHIFT		15
+#define   GEN8_RPCS_S_CNT_MASK		(0x7 << GEN8_RPCS_S_CNT_SHIFT)
+#define   GEN11_RPCS_S_CNT_SHIFT	12
+#define   GEN11_RPCS_S_CNT_MASK		(0x3f << GEN11_RPCS_S_CNT_SHIFT)
+#define   GEN8_RPCS_SS_CNT_ENABLE	(1 << 11)
+#define   GEN8_RPCS_SS_CNT_SHIFT	8
+#define   GEN8_RPCS_SS_CNT_MASK		(0x7 << GEN8_RPCS_SS_CNT_SHIFT)
+#define   GEN8_RPCS_EU_MAX_SHIFT	4
+#define   GEN8_RPCS_EU_MAX_MASK		(0xf << GEN8_RPCS_EU_MAX_SHIFT)
+#define   GEN8_RPCS_EU_MIN_SHIFT	0
+#define   GEN8_RPCS_EU_MIN_MASK		(0xf << GEN8_RPCS_EU_MIN_SHIFT)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+static unsigned int __intel_gen__, __intel_devid__;
+static uint64_t __slice_mask__, __subslice_mask__;
+static unsigned int __slice_count__, __subslice_count__;
+
+static uint64_t mask_minus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if ((1ULL << i) & mask)
+			return mask & ~(1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_plus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if (((1ULL << i) & mask) == 0)
+			return mask | (1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_minus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_minus_one(mask);
+
+	return mask;
+}
+
+static uint64_t mask_plus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_plus_one(mask);
+
+	return mask;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+		struct drm_i915_gem_relocation_entry *reloc,
+		uint32_t gem_handle, uint32_t delta, /* in bytes */
+		uint32_t offset, /* in dwords */
+		uint32_t read_domains, uint32_t write_domains)
+{
+	reloc->target_handle = gem_handle;
+	reloc->delta = delta;
+	reloc->offset = offset * sizeof(uint32_t);
+	reloc->presumed_offset = 0;
+	reloc->read_domains = read_domains;
+	reloc->write_domain = write_domains;
+
+	*batch++ = delta;
+	*batch++ = 0;
+
+	return batch;
+}
+
+
+static uint32_t
+read_rpcs_reg(int fd, uint32_t ctx, uint32_t expected_slices, igt_spin_t *spin)
+{
+	struct drm_i915_gem_execbuffer2 execbuf = { };
+	struct drm_i915_gem_relocation_entry relocs[2];
+	struct drm_i915_gem_exec_object2 obj[2];
+	uint32_t *batch, *b, data[2];
+	unsigned int n_relocs = 0;
+	uint32_t rpcs;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[1].handle = gem_create(fd, 4096);
+
+	batch = b =
+	      gem_mmap__cpu(fd, obj[1].handle, 0, 4096, PROT_READ | PROT_WRITE);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) |
+		       (MI_SET_PREDICATE_1_SLICES + expected_slices - 1);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = RCS_TIMESTAMP;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    0, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = GEN8_R_PWR_CLK_STATE;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    4, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) | MI_SET_PREDICATE_NOOP_NEVER;
+
+	*b++ = MI_BATCH_BUFFER_END;
+
+	gem_munmap(batch, 4096);
+
+	obj[1].relocation_count = n_relocs;
+	obj[1].relocs_ptr = to_user_pointer(relocs);
+
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+	i915_execbuffer2_set_context_id(execbuf, ctx);
+
+	gem_execbuf(fd, &execbuf);
+
+	if (spin) {
+		igt_assert(gem_bo_busy(fd, spin->handle));
+		igt_spin_batch_end(spin);
+	}
+
+	gem_read(fd, obj[0].handle, 0, data, sizeof(data));
+
+	rpcs = data[1];
+
+	igt_debug("rcs_timestamp=0x%x rpcs=0x%x\n", data[0], data[1]);
+
+	gem_close(fd, obj[0].handle);
+	gem_close(fd, obj[1].handle);
+
+	return rpcs;
+}
+
+typedef uint32_t (*read_slice_count_f)(int fd, uint32_t ctx, uint32_t expected,
+				       igt_spin_t *spin);
+
+static read_slice_count_f __read_slice_count;
+
+static uint32_t
+read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count)
+{
+	return __read_slice_count(fd, ctx, expected_slice_count, NULL);
+}
+
+static uint32_t
+gen8_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		      igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN8_RPCS_S_CNT_MASK) >> GEN8_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+gen11_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		       igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN11_RPCS_S_CNT_MASK) >> GEN11_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+read_subslice_count(int fd, uint32_t ctx)
+{
+	return (read_rpcs_reg(fd, ctx, 0, NULL) & GEN8_RPCS_SS_CNT_MASK) >>
+	       GEN8_RPCS_SS_CNT_SHIFT;
+}
+
+static bool
+kernel_has_per_context_sseu_support(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{
+		  .param = I915_CONTEXT_PARAM_SSEU,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu),
+		};
+	int ret;
+
+	if (__gem_context_get_param(fd, &arg))
+		return false;
+
+	arg.value = to_user_pointer(&sseu);
+
+	ret = __gem_context_set_param(fd, &arg);
+
+	igt_assert(ret == 0 || ret == -ENODEV || ret == -EINVAL);
+
+	return ret == 0;
+}
+
+static void
+context_get_sseu_masks(int fd, uint32_t ctx,
+		       uint64_t *slice_mask,
+		       uint64_t *subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+
+	if (slice_mask)
+		*slice_mask = sseu.slice_mask;
+
+	if (subslice_mask)
+		*subslice_mask = sseu.subslice_mask;
+}
+
+static void
+context_set_slice_mask(int fd, uint32_t ctx, uint64_t slice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.slice_mask = slice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+static void
+context_set_subslice_mask(int fd, uint32_t ctx, uint64_t subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.subslice_mask = subslice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+/*
+ * Verify that we can program the slice count.
+ */
+static void
+test_slice_pg(int fd, uint32_t pg_slice_count)
+{
+	uint64_t pg_slice_mask = mask_minus(__slice_mask__, pg_slice_count);
+	unsigned int slice_count = __slice_count__ - pg_slice_count;
+	uint64_t slice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(slice_count, __builtin_popcount(pg_slice_mask));
+
+	igt_skip_on(__intel_gen__ == 11 &&
+		    (slice_count != 1 && slice_count != __slice_count__));
+
+	ctx = gem_context_create(fd);
+	context_set_slice_mask(fd, ctx, pg_slice_mask);
+	context_get_sseu_masks(fd, ctx, &slice_mask, NULL);
+	igt_assert_eq(pg_slice_mask, slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx, __slice_count__), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), slice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+/*
+ * Verify that we can program the subslice count.
+ */
+static void
+test_subslice_pg(int fd, int pg_subslice_count)
+{
+	uint64_t pg_subslice_mask =
+		mask_minus(__subslice_mask__, pg_subslice_count);
+	unsigned int subslice_count = __subslice_count__ - pg_subslice_count;
+	uint64_t subslice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(subslice_count, __builtin_popcount(pg_subslice_mask));
+
+	igt_skip_on(__intel_gen__ == 11 &&
+		    (subslice_count != __subslice_count__ &&
+		     subslice_count != (__subslice_count__ / 2)));
+
+	ctx = gem_context_create(fd);
+	context_set_subslice_mask(fd, ctx, pg_subslice_mask);
+	context_get_sseu_masks(fd, ctx, NULL, &subslice_mask);
+	igt_assert_eq(pg_subslice_mask, subslice_mask);
+
+	igt_assert_eq(read_subslice_count(fd, ctx), subslice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_engine(int fd, unsigned int class, unsigned int instance)
+{
+	int pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+
+	if (pmu >= 0)
+		close(pmu);
+
+	return pmu >= 0;
+}
+
+/*
+ * Verify that invalid engines are rejected and valid ones are accepted.
+ */
+static void test_engines(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	unsigned int class, instance;
+	int last_with_engines;
+
+	/* get_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+	sseu.class = I915_ENGINE_CLASS_RENDER;
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_get_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert_eq(ret, 0);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	/*
+	 * Get some proper values before trying to reprogram them onto
+	 * an invalid engine.
+	 */
+	sseu.class = 0;
+	sseu.instance = 0;
+	gem_context_get_param(fd, &arg);
+
+	/* set_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_set_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert(ret == 0 || ret == -ENODEV);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid arguments are rejected.
+ */
+static void
+test_invalid_args(int fd)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		};
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	unsigned char *page[2];
+	unsigned char *addr;
+	unsigned int sz;
+
+	/* get param */
+
+	/* Invalid size. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	/* Query size. */
+	arg.size = 0;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	sz = arg.size;
+
+	/* Bad pointers. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(page[1]) -
+		    sizeof(struct drm_i915_gem_context_param_sseu) + 4;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	/* Straddle into read-only area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	igt_assert(mprotect(page[1], 4096, PROT_READ) == 0);
+	arg.value = to_user_pointer(page[1] - sizeof(sseu) + 4);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+	munmap(page[1], 4096);
+
+	/* set param */
+
+	/* Invalid sizes. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	arg.size = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	arg.size = sz;
+
+	/* Bad pointers. */
+	arg.value = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Get valid SSEU. */
+	arg.value = to_user_pointer(&sseu);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memcpy(page[0], &sseu, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	addr = page[1] - sizeof(sseu) + 4;
+	memcpy(addr, &sseu, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(addr);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that ggtt mapped area can be used as the sseu pointer.
+ */
+static void
+test_ggtt_args(int fd)
+{
+	struct drm_i915_gem_context_param_sseu *sseu;
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(*sseu),
+		};
+	uint32_t bo;
+
+	bo = gem_create(fd, 4096);
+	arg.value = to_user_pointer(gem_mmap__gtt(fd, bo, 4096,
+						  PROT_READ | PROT_WRITE));
+
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), 0);
+
+	munmap((void *)arg.value, 4096);
+	gem_close(fd, bo);
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid SSEU values are rejected.
+ */
+static void
+test_invalid_sseu(int fd)
+{
+	struct drm_i915_gem_context_param_sseu device_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		};
+	unsigned int i;
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&device_sseu);
+	gem_context_get_param(fd, &arg);
+
+	arg.value = to_user_pointer(&sseu);
+
+	/* Try all slice masks known to be invalid. */
+	sseu = device_sseu;
+	for (i = 1; i <= (8 - __slice_count__); i++) {
+		sseu.slice_mask = mask_plus(__slice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 slices. */
+	sseu.slice_mask = 0;
+	igt_assert_eq(-EINVAL, __gem_context_set_param(fd, &arg));
+
+	/* Try all subslice masks known to be invalid. */
+	sseu = device_sseu;
+	for (i = 1; i <= (8 - __subslice_count__); i++) {
+		sseu.subslice_mask = mask_plus(__subslice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 subslices. */
+	sseu.subslice_mask = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try number of EUs superior to the max available. */
+	sseu = device_sseu;
+	sseu.min_eus_per_subslice = device_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu = device_sseu;
+	sseu.max_eus_per_subslice = device_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try to program 0 max EUs. */
+	sseu = device_sseu;
+	sseu.max_eus_per_subslice = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Min > max */
+	sseu = device_sseu;
+	sseu.min_eus_per_subslice = sseu.max_eus_per_subslice;
+	sseu.max_eus_per_subslice = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	if (__intel_gen__ != 11)
+		goto out;
+
+	/* Subset of subslices but slice mask greater than one. */
+	if (__slice_count__ > 1) {
+		sseu = device_sseu;
+		sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Odd subslices above four. */
+	sseu = device_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* More than half subslices with one slice. */
+	sseu = device_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 - 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* VME */
+
+	/* Slice count between one and max. */
+	if (__slice_count__ > 2) {
+		sseu = device_sseu;
+		sseu.slice_mask = mask_minus_one(sseu.slice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Less than half subslices with one slice. */
+	sseu = device_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 + 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+out:
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/* Verify that the kernel returns a correct error value on Gen < 8. */
+static void
+init_contexts(int fd, uint32_t *ctx, unsigned int num,
+	      uint64_t mask0, uint64_t mask1)
+{
+	unsigned int i;
+
+	igt_assert_eq(num, 2);
+
+	for (i = 0; i < num; i++)
+		ctx[i] = gem_context_create(fd);
+
+	context_set_slice_mask(fd, ctx[0], mask0);
+	context_set_slice_mask(fd, ctx[1], mask1);
+}
+
+static void
+def_sseu(struct drm_i915_gem_context_param_sseu *sseu,
+	 struct drm_i915_gem_context_param_sseu *def)
+{
+	memcpy(sseu, def, sizeof(*sseu));
+}
+
+static void
+pg_sseu(struct drm_i915_gem_context_param_sseu *sseu,
+	 struct drm_i915_gem_context_param_sseu *def)
+{
+	unsigned int ss;
+
+	memcpy(sseu, def, sizeof(*sseu));
+
+	ss = __builtin_popcount(def->subslice_mask);
+
+	/* Gen11 VME friendly configuration. */
+	sseu->slice_mask = 1;
+	sseu->subslice_mask = ~(~0 << (ss / 2));
+}
+
+static void
+oa_sseu(struct drm_i915_gem_context_param_sseu *sseu,
+	 struct drm_i915_gem_context_param_sseu *def)
+{
+	if (__intel_gen__ == 11)
+		pg_sseu(sseu, def);
+	else
+		def_sseu(sseu, def);
+}
+
+static void
+get_device_sseu(int fd, struct drm_i915_gem_context_param_sseu *sseu)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .size = sizeof(*sseu),
+		  .value = to_user_pointer(sseu) };
+
+	memset(sseu, 0, sizeof(*sseu));
+	gem_context_get_param(fd, &arg);
+}
+
+/*
+ * Verify that powergating settings are put on hold while i915/perf is
+ * active.
+ */
+static void
+test_perf_oa(int fd)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, 1, /* test metric */
+		DRM_I915_PERF_PROP_OA_FORMAT, I915_OA_FORMAT_A32u40_A4u32_B8_C8,
+		DRM_I915_PERF_PROP_OA_EXPONENT, 20,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+		I915_PERF_FLAG_FD_NONBLOCK,
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	struct drm_i915_gem_context_param_sseu device_sseu, _pg_sseu, _oa_sseu;
+	unsigned int pg_slice_count, oa_slice_count;
+	uint32_t ctx[2];
+	unsigned int i;
+	int perf_fd;
+
+	get_device_sseu(fd, &device_sseu);
+	pg_sseu(&_pg_sseu, &device_sseu);
+	oa_sseu(&_oa_sseu, &device_sseu);
+	pg_slice_count = __builtin_popcount(_pg_sseu.slice_mask);
+	oa_slice_count = __builtin_popcount(_oa_sseu.slice_mask);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx),
+		      device_sseu.slice_mask, _pg_sseu.slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), pg_slice_count);
+
+	/*
+	 * Now open i915/perf and verify that all contexts have been
+	 * reconfigured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], pg_slice_count), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), oa_slice_count);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), oa_slice_count);
+	if (__intel_gen__ == 11) {
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__), 0);
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), pg_slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+
+	/*
+	 * Open i915/perf first and verify that all contexts created
+	 * afterward are configured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx),
+		      device_sseu.slice_mask, _pg_sseu.slice_mask);
+
+	/*
+	 * Check the device's default values, despite setting
+	 * otherwise.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], pg_slice_count), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), oa_slice_count);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), oa_slice_count);
+	if (__intel_gen__ == 11) {
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), pg_slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	struct igt_spin_factory opts = {
+		.ctx = ctx,
+		.engine = flags,
+	};
+
+	if (gem_can_store_dword(fd, flags))
+		opts.flags |= IGT_SPIN_POLL_RUN;
+
+	return __igt_spin_batch_factory(fd, &opts);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!READ_ONCE(*spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static uint32_t
+read_slice_count_busy(int fd, uint32_t context, uint32_t expected,
+		      igt_spin_t *spin)
+{
+	return __read_slice_count(fd, context, expected, spin);
+}
+
+#define TEST_IDLE	(1)
+#define TEST_BUSY	(2)
+#define TEST_RESET	(4)
+#define TEST_HANG	(8)
+#define TEST_SUSPEND	(16)
+
+static igt_spin_t *
+__pre_set(int fd, unsigned flags, uint32_t ctx)
+{
+	if (flags & TEST_BUSY)
+		return __spin_sync(fd, ctx, I915_EXEC_RENDER);
+
+	return NULL;
+}
+
+static igt_spin_t *
+__post_set(int fd, unsigned int flags, uint32_t ctx, igt_spin_t *spin,
+	   unsigned int expected)
+{
+	if (flags & TEST_RESET)
+		igt_force_gpu_reset(fd);
+
+	if ((flags & TEST_BUSY) && !(flags & (TEST_RESET | TEST_HANG)))
+		igt_assert_eq(read_slice_count_busy(fd, ctx, 0, spin),
+			      expected);
+	else
+		igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
+
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin)
+		igt_spin_batch_free(fd, spin);
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	if (flags & TEST_SUSPEND)
+		igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
+					      SUSPEND_TEST_NONE);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	return NULL;
+}
+
+/*
+ * Test context re-configuration with either idle or busy contexts.
+ */
+static void
+test_dynamic(int fd, unsigned int flags)
+{
+	struct drm_i915_gem_context_param_sseu device_sseu;
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	igt_spin_t *spin = NULL;
+
+	igt_require(__slice_count__ > 1);
+
+	get_device_sseu(fd, &device_sseu);
+	def_sseu(&sseu, &device_sseu);
+
+	/* First set the default mask */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin,
+			  __builtin_popcount(sseu.slice_mask));
+
+	/* Then set a powergated configuration */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	pg_sseu(&sseu, &device_sseu);
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin,
+			  __builtin_popcount(sseu.slice_mask));
+
+	/* Put the device's default back again */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	def_sseu(&sseu, &device_sseu);
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin,
+			  __builtin_popcount(sseu.slice_mask));
+
+	/* One last powergated config for the road... */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	pg_sseu(&sseu, &device_sseu);
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin,
+			  __builtin_popcount(sseu.slice_mask));
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+igt_main
+{
+	unsigned int max_slices = 3, max_subslices = 3;
+	unsigned int i;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		__intel_devid__ = intel_get_drm_devid(fd);
+		__intel_gen__ = intel_gen(__intel_devid__);
+
+		igt_require(kernel_has_per_context_sseu_support(fd));
+
+		if (__intel_gen__ >= 11)
+			__read_slice_count = gen11_read_slice_count;
+		else
+			__read_slice_count = gen8_read_slice_count;
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			drm_i915_getparam_t gp;
+
+			gp.param = I915_PARAM_SLICE_MASK;
+			gp.value = (int *) &__slice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__slice_count__ = __builtin_popcount(__slice_mask__);
+
+			gp.param = I915_PARAM_SUBSLICE_MASK;
+			gp.value = (int *) &__subslice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__subslice_count__ =
+				__builtin_popcount(__subslice_mask__);
+		}
+
+		igt_subtest("invalid-args")
+			test_invalid_args(fd);
+
+		igt_subtest("invalid-sseu")
+			test_invalid_sseu(fd);
+
+		igt_subtest("ggtt-args")
+			test_ggtt_args(fd);
+
+		igt_subtest("engines")
+			test_engines(fd);
+
+		for (i = 1; i < max_slices; i++) {
+			igt_subtest_f("slice-pg-%i", i) {
+				igt_require(__slice_count__ > i);
+
+				test_slice_pg(fd, i);
+			}
+		}
+
+		for (i = 1; i < max_subslices; i++) {
+			igt_subtest_f("subslice-pg-%i", i) {
+				igt_require(__subslice_count__ >= 2);
+
+				/*
+				 * Only available on some Atom platforms and
+				 * Gen10+.
+				 */
+				igt_require(IS_BROXTON(__intel_devid__) ||
+					    IS_GEMINILAKE(__intel_devid__) ||
+					    __intel_gen__ >= 10);
+
+				test_subslice_pg(fd, i);
+			}
+		}
+
+		igt_subtest("perf-oa") {
+			igt_require(__slice_count__ > 1);
+
+			test_perf_oa(fd);
+		}
+
+		igt_subtest("dynamic")
+			test_dynamic(fd, 0);
+
+		igt_subtest("dynamic-busy")
+			test_dynamic(fd, TEST_BUSY);
+
+		igt_subtest("dynamic-reset")
+			test_dynamic(fd, TEST_RESET);
+
+		igt_subtest("dynamic-busy-reset")
+			test_dynamic(fd, TEST_BUSY | TEST_RESET);
+
+		igt_subtest("dynamic-busy-hang")
+			test_dynamic(fd, TEST_BUSY | TEST_HANG);
+
+		igt_subtest("dynamic-idle")
+			test_dynamic(fd, TEST_IDLE);
+
+		igt_subtest("dynamic-suspend")
+			test_dynamic(fd, TEST_SUSPEND);
+
+		igt_subtest("dynamic-idle-suspend")
+			test_dynamic(fd, TEST_IDLE | TEST_SUSPEND);
+	}
+
+	igt_fixture {
+		close(fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index d22d59e0837d..f70c859f1a62 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -248,6 +248,13 @@ foreach prog : test_progs
 		   install : true)
 endforeach
 
+test_executables += executable('gem_ctx_sseu', 'gem_ctx_sseu.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_ctx_sseu'
+
 test_executables += executable('gem_eio', 'gem_eio.c',
 	   dependencies : test_deps + [ realtime ],
 	   install_dir : libexecdir,
-- 
2.17.1

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

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

* [igt-dev] [PATCH i-g-t v15 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests
@ 2018-09-18 13:41                         ` Tvrtko Ursulin
  0 siblings, 0 replies; 55+ messages in thread
From: Tvrtko Ursulin @ 2018-09-18 13:41 UTC (permalink / raw)
  To: igt-dev; +Cc: Intel-gfx, Tvrtko Ursulin

From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>

Verify that the per-context dynamic SSEU uAPI works as expected.

To achieve that, in the absence of a better mechamism, we read the value
of PWR_CLK_STATE register, or use MI_SET_PREDICATE on platforms before
Cannonlake.

This register is written to by the GPU on context restore so this way
we verify i915 is correctly updating the context image in all
circumstances.

v2: Add subslice tests (Lionel)
    Use MI_SET_PREDICATE for further verification when available (Lionel)

v3: Rename to gem_ctx_rpcs (Lionel)

v4: Update kernel API (Lionel)
    Add 0 value test (Lionel)
    Exercise invalid values (Lionel)

v5: Add perf tests (Lionel)

v6: Add new sysfs entry tests (Lionel)

v7: Test rsvd fields
    Update for kernel series changes

v8: Drop test_no_sseu_support() test (Kelvin)
    Drop drm_intel_*() apis (Chris)

v9: by Chris:
    Drop all do_ioctl/do_ioctl_err()
    Use gem_context_[gs]et_param()
    Use gem_read() instead of mapping memory
    by Lionel:
    Test dynamic sseu on/off more

Tvrtko Ursulin:

v10:
 * Various style tweaks and refactorings.
 * New test coverage.

v11:
 * Change platform support to just Gen11.
 * Simplify availability test. (Chris Wilson)
 * More invalid pointer tests. (Chris Wilson)

v12:
 * Fix MAP_FIXED use (doh!).
 * Fix get/set copy&paste errors.
 * Drop supported platform test. (Chris Wilson)
 * Add mmap__gtt test. (Chris Wilson)

v13:
 * Commit message tweaks.
 * Added reset/hang/suspend tests. (Chris Wilson)
 * Assert spinner is busy. (Chris Wilson)
 * Remove some more ABI assumptions. (Chris Wilson)

v14:
 * Use default resume time. (Chris Wilson)
 * Trigger hang after rpcs read batch has been submitted. (Chris Wilson)

v15:
 * Adjust for uAPI restrictions.

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> # v14
---
 tests/Makefile.am      |    1 +
 tests/Makefile.sources |    1 +
 tests/gem_ctx_param.c  |    4 +-
 tests/gem_ctx_sseu.c   | 1190 ++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |    7 +
 5 files changed, 1202 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_sseu.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index ee5a7c5e83b8..6b67bd2cc17a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -107,6 +107,7 @@ gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
+gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
 gem_fence_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 269336ad3150..6765143bf344 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -55,6 +55,7 @@ TESTS_progs = \
 	gem_ctx_exec \
 	gem_ctx_isolation \
 	gem_ctx_param \
+	gem_ctx_sseu \
 	gem_ctx_switch \
 	gem_ctx_thrash \
 	gem_double_irq_loop \
diff --git a/tests/gem_ctx_param.c b/tests/gem_ctx_param.c
index c46fd709b0d7..af1afeaa2f2f 100644
--- a/tests/gem_ctx_param.c
+++ b/tests/gem_ctx_param.c
@@ -294,11 +294,13 @@ igt_main
 			set_priority(fd);
 	}
 
+	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
+
 	/* NOTE: This testcase intentionally tests for the next free parameter
 	 * to catch ABI extensions. Don't "fix" this testcase without adding all
 	 * the tests for the new param first.
 	 */
-	arg.param = I915_CONTEXT_PARAM_PRIORITY + 1;
+	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
 
 	igt_subtest("invalid-param-get") {
 		arg.ctx_id = ctx;
diff --git a/tests/gem_ctx_sseu.c b/tests/gem_ctx_sseu.c
new file mode 100644
index 000000000000..889f70643392
--- /dev/null
+++ b/tests/gem_ctx_sseu.c
@@ -0,0 +1,1190 @@
+/*
+ * Copyright © 2017-2018 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.
+ *
+ * Authors:
+ *    Lionel Landwerlin <lionel.g.landwerlin@intel.com>
+ *
+ */
+
+#include "igt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include "igt_dummyload.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+#include "ioctl_wrappers.h"
+
+IGT_TEST_DESCRIPTION("Test context render powergating programming.");
+
+#define MI_STORE_REGISTER_MEM (0x24 << 23)
+
+#define MI_SET_PREDICATE      (0x1 << 23)
+#define  MI_SET_PREDICATE_NOOP_NEVER         (0)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_CLEAR (1)
+#define  MI_SET_PREDICATE_NOOP_RESULT2_SET   (2)
+#define  MI_SET_PREDICATE_NOOP_RESULT_CLEAR  (3)
+#define  MI_SET_PREDICATE_NOOP_RESULT_SET    (4)
+#define  MI_SET_PREDICATE_1_SLICES           (5)
+#define  MI_SET_PREDICATE_2_SLICES           (6)
+#define  MI_SET_PREDICATE_3_SLICES           (7)
+
+#define GEN8_R_PWR_CLK_STATE		0x20C8
+#define   GEN8_RPCS_ENABLE		(1 << 31)
+#define   GEN8_RPCS_S_CNT_ENABLE	(1 << 18)
+#define   GEN8_RPCS_S_CNT_SHIFT		15
+#define   GEN8_RPCS_S_CNT_MASK		(0x7 << GEN8_RPCS_S_CNT_SHIFT)
+#define   GEN11_RPCS_S_CNT_SHIFT	12
+#define   GEN11_RPCS_S_CNT_MASK		(0x3f << GEN11_RPCS_S_CNT_SHIFT)
+#define   GEN8_RPCS_SS_CNT_ENABLE	(1 << 11)
+#define   GEN8_RPCS_SS_CNT_SHIFT	8
+#define   GEN8_RPCS_SS_CNT_MASK		(0x7 << GEN8_RPCS_SS_CNT_SHIFT)
+#define   GEN8_RPCS_EU_MAX_SHIFT	4
+#define   GEN8_RPCS_EU_MAX_MASK		(0xf << GEN8_RPCS_EU_MAX_SHIFT)
+#define   GEN8_RPCS_EU_MIN_SHIFT	0
+#define   GEN8_RPCS_EU_MIN_MASK		(0xf << GEN8_RPCS_EU_MIN_SHIFT)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+static unsigned int __intel_gen__, __intel_devid__;
+static uint64_t __slice_mask__, __subslice_mask__;
+static unsigned int __slice_count__, __subslice_count__;
+
+static uint64_t mask_minus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if ((1ULL << i) & mask)
+			return mask & ~(1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_plus_one(uint64_t mask)
+{
+	unsigned int i;
+
+	for (i = 0; i < (sizeof(mask) * 8 - 1); i++) {
+		if (((1ULL << i) & mask) == 0)
+			return mask | (1ULL << i);
+	}
+
+	igt_assert(0);
+	return 0;
+}
+
+static uint64_t mask_minus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_minus_one(mask);
+
+	return mask;
+}
+
+static uint64_t mask_plus(uint64_t mask, int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++)
+		mask = mask_plus_one(mask);
+
+	return mask;
+}
+
+static uint32_t *
+fill_relocation(uint32_t *batch,
+		struct drm_i915_gem_relocation_entry *reloc,
+		uint32_t gem_handle, uint32_t delta, /* in bytes */
+		uint32_t offset, /* in dwords */
+		uint32_t read_domains, uint32_t write_domains)
+{
+	reloc->target_handle = gem_handle;
+	reloc->delta = delta;
+	reloc->offset = offset * sizeof(uint32_t);
+	reloc->presumed_offset = 0;
+	reloc->read_domains = read_domains;
+	reloc->write_domain = write_domains;
+
+	*batch++ = delta;
+	*batch++ = 0;
+
+	return batch;
+}
+
+
+static uint32_t
+read_rpcs_reg(int fd, uint32_t ctx, uint32_t expected_slices, igt_spin_t *spin)
+{
+	struct drm_i915_gem_execbuffer2 execbuf = { };
+	struct drm_i915_gem_relocation_entry relocs[2];
+	struct drm_i915_gem_exec_object2 obj[2];
+	uint32_t *batch, *b, data[2];
+	unsigned int n_relocs = 0;
+	uint32_t rpcs;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[1].handle = gem_create(fd, 4096);
+
+	batch = b =
+	      gem_mmap__cpu(fd, obj[1].handle, 0, 4096, PROT_READ | PROT_WRITE);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) |
+		       (MI_SET_PREDICATE_1_SLICES + expected_slices - 1);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = RCS_TIMESTAMP;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    0, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	*b++ = MI_STORE_REGISTER_MEM | (4 - 2);
+	*b++ = GEN8_R_PWR_CLK_STATE;
+	b = fill_relocation(b, &relocs[n_relocs++], obj[0].handle,
+			    4, b - batch,
+			    I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER);
+
+	if (expected_slices != 0 && __intel_gen__ < 10)
+		*b++ = MI_SET_PREDICATE | (1 - 1) | MI_SET_PREDICATE_NOOP_NEVER;
+
+	*b++ = MI_BATCH_BUFFER_END;
+
+	gem_munmap(batch, 4096);
+
+	obj[1].relocation_count = n_relocs;
+	obj[1].relocs_ptr = to_user_pointer(relocs);
+
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+	i915_execbuffer2_set_context_id(execbuf, ctx);
+
+	gem_execbuf(fd, &execbuf);
+
+	if (spin) {
+		igt_assert(gem_bo_busy(fd, spin->handle));
+		igt_spin_batch_end(spin);
+	}
+
+	gem_read(fd, obj[0].handle, 0, data, sizeof(data));
+
+	rpcs = data[1];
+
+	igt_debug("rcs_timestamp=0x%x rpcs=0x%x\n", data[0], data[1]);
+
+	gem_close(fd, obj[0].handle);
+	gem_close(fd, obj[1].handle);
+
+	return rpcs;
+}
+
+typedef uint32_t (*read_slice_count_f)(int fd, uint32_t ctx, uint32_t expected,
+				       igt_spin_t *spin);
+
+static read_slice_count_f __read_slice_count;
+
+static uint32_t
+read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count)
+{
+	return __read_slice_count(fd, ctx, expected_slice_count, NULL);
+}
+
+static uint32_t
+gen8_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		      igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN8_RPCS_S_CNT_MASK) >> GEN8_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+gen11_read_slice_count(int fd, uint32_t ctx, uint32_t expected_slice_count,
+		       igt_spin_t *spin)
+{
+	return (read_rpcs_reg(fd, ctx, expected_slice_count, spin) &
+		GEN11_RPCS_S_CNT_MASK) >> GEN11_RPCS_S_CNT_SHIFT;
+}
+
+static uint32_t
+read_subslice_count(int fd, uint32_t ctx)
+{
+	return (read_rpcs_reg(fd, ctx, 0, NULL) & GEN8_RPCS_SS_CNT_MASK) >>
+	       GEN8_RPCS_SS_CNT_SHIFT;
+}
+
+static bool
+kernel_has_per_context_sseu_support(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{
+		  .param = I915_CONTEXT_PARAM_SSEU,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu),
+		};
+	int ret;
+
+	if (__gem_context_get_param(fd, &arg))
+		return false;
+
+	arg.value = to_user_pointer(&sseu);
+
+	ret = __gem_context_set_param(fd, &arg);
+
+	igt_assert(ret == 0 || ret == -ENODEV || ret == -EINVAL);
+
+	return ret == 0;
+}
+
+static void
+context_get_sseu_masks(int fd, uint32_t ctx,
+		       uint64_t *slice_mask,
+		       uint64_t *subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+
+	if (slice_mask)
+		*slice_mask = sseu.slice_mask;
+
+	if (subslice_mask)
+		*subslice_mask = sseu.subslice_mask;
+}
+
+static void
+context_set_slice_mask(int fd, uint32_t ctx, uint64_t slice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.slice_mask = slice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+static void
+context_set_subslice_mask(int fd, uint32_t ctx, uint64_t subslice_mask)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = ctx,
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+
+	gem_context_get_param(fd, &arg);
+	sseu.subslice_mask = subslice_mask;
+	gem_context_set_param(fd, &arg);
+}
+
+/*
+ * Verify that we can program the slice count.
+ */
+static void
+test_slice_pg(int fd, uint32_t pg_slice_count)
+{
+	uint64_t pg_slice_mask = mask_minus(__slice_mask__, pg_slice_count);
+	unsigned int slice_count = __slice_count__ - pg_slice_count;
+	uint64_t slice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(slice_count, __builtin_popcount(pg_slice_mask));
+
+	igt_skip_on(__intel_gen__ == 11 &&
+		    (slice_count != 1 && slice_count != __slice_count__));
+
+	ctx = gem_context_create(fd);
+	context_set_slice_mask(fd, ctx, pg_slice_mask);
+	context_get_sseu_masks(fd, ctx, &slice_mask, NULL);
+	igt_assert_eq(pg_slice_mask, slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx, __slice_count__), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), slice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+/*
+ * Verify that we can program the subslice count.
+ */
+static void
+test_subslice_pg(int fd, int pg_subslice_count)
+{
+	uint64_t pg_subslice_mask =
+		mask_minus(__subslice_mask__, pg_subslice_count);
+	unsigned int subslice_count = __subslice_count__ - pg_subslice_count;
+	uint64_t subslice_mask;
+	uint32_t ctx;
+
+	igt_assert_eq(subslice_count, __builtin_popcount(pg_subslice_mask));
+
+	igt_skip_on(__intel_gen__ == 11 &&
+		    (subslice_count != __subslice_count__ &&
+		     subslice_count != (__subslice_count__ / 2)));
+
+	ctx = gem_context_create(fd);
+	context_set_subslice_mask(fd, ctx, pg_subslice_mask);
+	context_get_sseu_masks(fd, ctx, NULL, &subslice_mask);
+	igt_assert_eq(pg_subslice_mask, subslice_mask);
+
+	igt_assert_eq(read_subslice_count(fd, ctx), subslice_count);
+
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_engine(int fd, unsigned int class, unsigned int instance)
+{
+	int pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+
+	if (pmu >= 0)
+		close(pmu);
+
+	return pmu >= 0;
+}
+
+/*
+ * Verify that invalid engines are rejected and valid ones are accepted.
+ */
+static void test_engines(int fd)
+{
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	unsigned int class, instance;
+	int last_with_engines;
+
+	/* get_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+	sseu.class = I915_ENGINE_CLASS_RENDER;
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_get_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert_eq(ret, 0);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	/*
+	 * Get some proper values before trying to reprogram them onto
+	 * an invalid engine.
+	 */
+	sseu.class = 0;
+	sseu.instance = 0;
+	gem_context_get_param(fd, &arg);
+
+	/* set_param */
+
+	sseu.instance = -1; /* Assumed invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.class = I915_ENGINE_CLASS_INVALID; /* Both invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu.instance = 0; /* Class invalid. */
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	last_with_engines = -1;
+	for (class = 0; class < ~0; class++) {
+		for (instance = 0; instance < ~0; instance++) {
+			int ret;
+
+			sseu.class = class;
+			sseu.instance = instance;
+
+			ret = __gem_context_set_param(fd, &arg);
+
+			if (has_engine(fd, class, instance)) {
+				igt_assert(ret == 0 || ret == -ENODEV);
+				last_with_engines = class;
+			} else {
+				igt_assert_eq(ret, -EINVAL);
+				if (instance > 8) /* Skip over some instance holes. */
+					break;
+			}
+		}
+
+		if (class - last_with_engines > 8) /* Skip over some class holes. */
+			break;
+	}
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid arguments are rejected.
+ */
+static void
+test_invalid_args(int fd)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		};
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	unsigned char *page[2];
+	unsigned char *addr;
+	unsigned int sz;
+
+	/* get param */
+
+	/* Invalid size. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
+
+	/* Query size. */
+	arg.size = 0;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	sz = arg.size;
+
+	/* Bad pointers. */
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(page[1]) -
+		    sizeof(struct drm_i915_gem_context_param_sseu) + 4;
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	/* Straddle into read-only area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memset(page[0], 0, sizeof(sseu));
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	memset(page[1], 0, sizeof(sseu));
+	igt_assert(mprotect(page[1], 4096, PROT_READ) == 0);
+	arg.value = to_user_pointer(page[1] - sizeof(sseu) + 4);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+	munmap(page[1], 4096);
+
+	/* set param */
+
+	/* Invalid sizes. */
+	arg.size = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	arg.size = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	arg.size = sz;
+
+	/* Bad pointers. */
+	arg.value = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = -1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	arg.value = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Get valid SSEU. */
+	arg.value = to_user_pointer(&sseu);
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+
+	/* Unmapped. */
+	page[0] = mmap(0, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	memcpy(page[0], &sseu, sizeof(sseu));
+	munmap(page[0], 4096);
+	arg.value = to_user_pointer(page[0]);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+
+	/* Straddle into unmapped area. */
+	page[0] = mmap(0, 8192, PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	munmap(page[0], 8192);
+	page[0] = mmap(page[0], 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[0] != MAP_FAILED);
+	page[1] = mmap((void *)((unsigned long)page[0] + 4096), 4096,
+		       PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+	igt_assert(page[1] != MAP_FAILED);
+	addr = page[1] - sizeof(sseu) + 4;
+	memcpy(addr, &sseu, sizeof(sseu));
+	munmap(page[1], 4096);
+	arg.value = to_user_pointer(addr);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EFAULT);
+	munmap(page[0], 4096);
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that ggtt mapped area can be used as the sseu pointer.
+ */
+static void
+test_ggtt_args(int fd)
+{
+	struct drm_i915_gem_context_param_sseu *sseu;
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(*sseu),
+		};
+	uint32_t bo;
+
+	bo = gem_create(fd, 4096);
+	arg.value = to_user_pointer(gem_mmap__gtt(fd, bo, 4096,
+						  PROT_READ | PROT_WRITE));
+
+	igt_assert_eq(__gem_context_get_param(fd, &arg), 0);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), 0);
+
+	munmap((void *)arg.value, 4096);
+	gem_close(fd, bo);
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/*
+ * Verify that invalid SSEU values are rejected.
+ */
+static void
+test_invalid_sseu(int fd)
+{
+	struct drm_i915_gem_context_param_sseu device_sseu = { };
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		};
+	unsigned int i;
+
+	/* Fetch the device defaults. */
+	arg.value = to_user_pointer(&device_sseu);
+	gem_context_get_param(fd, &arg);
+
+	arg.value = to_user_pointer(&sseu);
+
+	/* Try all slice masks known to be invalid. */
+	sseu = device_sseu;
+	for (i = 1; i <= (8 - __slice_count__); i++) {
+		sseu.slice_mask = mask_plus(__slice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 slices. */
+	sseu.slice_mask = 0;
+	igt_assert_eq(-EINVAL, __gem_context_set_param(fd, &arg));
+
+	/* Try all subslice masks known to be invalid. */
+	sseu = device_sseu;
+	for (i = 1; i <= (8 - __subslice_count__); i++) {
+		sseu.subslice_mask = mask_plus(__subslice_mask__, i);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* 0 subslices. */
+	sseu.subslice_mask = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try number of EUs superior to the max available. */
+	sseu = device_sseu;
+	sseu.min_eus_per_subslice = device_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	sseu = device_sseu;
+	sseu.max_eus_per_subslice = device_sseu.max_eus_per_subslice + 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Try to program 0 max EUs. */
+	sseu = device_sseu;
+	sseu.max_eus_per_subslice = 0;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* Min > max */
+	sseu = device_sseu;
+	sseu.min_eus_per_subslice = sseu.max_eus_per_subslice;
+	sseu.max_eus_per_subslice = 1;
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	if (__intel_gen__ != 11)
+		goto out;
+
+	/* Subset of subslices but slice mask greater than one. */
+	if (__slice_count__ > 1) {
+		sseu = device_sseu;
+		sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Odd subslices above four. */
+	sseu = device_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus_one(sseu.subslice_mask);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* More than half subslices with one slice. */
+	sseu = device_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 - 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+	/* VME */
+
+	/* Slice count between one and max. */
+	if (__slice_count__ > 2) {
+		sseu = device_sseu;
+		sseu.slice_mask = mask_minus_one(sseu.slice_mask);
+		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+	}
+
+	/* Less than half subslices with one slice. */
+	sseu = device_sseu;
+	sseu.slice_mask = 0x1;
+	sseu.subslice_mask = mask_minus(sseu.subslice_mask,
+					__subslice_count__ / 2 + 1);
+	igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
+
+out:
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+/* Verify that the kernel returns a correct error value on Gen < 8. */
+static void
+init_contexts(int fd, uint32_t *ctx, unsigned int num,
+	      uint64_t mask0, uint64_t mask1)
+{
+	unsigned int i;
+
+	igt_assert_eq(num, 2);
+
+	for (i = 0; i < num; i++)
+		ctx[i] = gem_context_create(fd);
+
+	context_set_slice_mask(fd, ctx[0], mask0);
+	context_set_slice_mask(fd, ctx[1], mask1);
+}
+
+static void
+def_sseu(struct drm_i915_gem_context_param_sseu *sseu,
+	 struct drm_i915_gem_context_param_sseu *def)
+{
+	memcpy(sseu, def, sizeof(*sseu));
+}
+
+static void
+pg_sseu(struct drm_i915_gem_context_param_sseu *sseu,
+	 struct drm_i915_gem_context_param_sseu *def)
+{
+	unsigned int ss;
+
+	memcpy(sseu, def, sizeof(*sseu));
+
+	ss = __builtin_popcount(def->subslice_mask);
+
+	/* Gen11 VME friendly configuration. */
+	sseu->slice_mask = 1;
+	sseu->subslice_mask = ~(~0 << (ss / 2));
+}
+
+static void
+oa_sseu(struct drm_i915_gem_context_param_sseu *sseu,
+	 struct drm_i915_gem_context_param_sseu *def)
+{
+	if (__intel_gen__ == 11)
+		pg_sseu(sseu, def);
+	else
+		def_sseu(sseu, def);
+}
+
+static void
+get_device_sseu(int fd, struct drm_i915_gem_context_param_sseu *sseu)
+{
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .size = sizeof(*sseu),
+		  .value = to_user_pointer(sseu) };
+
+	memset(sseu, 0, sizeof(*sseu));
+	gem_context_get_param(fd, &arg);
+}
+
+/*
+ * Verify that powergating settings are put on hold while i915/perf is
+ * active.
+ */
+static void
+test_perf_oa(int fd)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, 1, /* test metric */
+		DRM_I915_PERF_PROP_OA_FORMAT, I915_OA_FORMAT_A32u40_A4u32_B8_C8,
+		DRM_I915_PERF_PROP_OA_EXPONENT, 20,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+		I915_PERF_FLAG_FD_NONBLOCK,
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	struct drm_i915_gem_context_param_sseu device_sseu, _pg_sseu, _oa_sseu;
+	unsigned int pg_slice_count, oa_slice_count;
+	uint32_t ctx[2];
+	unsigned int i;
+	int perf_fd;
+
+	get_device_sseu(fd, &device_sseu);
+	pg_sseu(&_pg_sseu, &device_sseu);
+	oa_sseu(&_oa_sseu, &device_sseu);
+	pg_slice_count = __builtin_popcount(_pg_sseu.slice_mask);
+	oa_slice_count = __builtin_popcount(_oa_sseu.slice_mask);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx),
+		      device_sseu.slice_mask, _pg_sseu.slice_mask);
+
+	/*
+	 * Test false positives with predicates (only available on
+	 * before Gen10).
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), pg_slice_count);
+
+	/*
+	 * Now open i915/perf and verify that all contexts have been
+	 * reconfigured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], pg_slice_count), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), oa_slice_count);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), oa_slice_count);
+	if (__intel_gen__ == 11) {
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__), 0);
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), pg_slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+
+	/*
+	 * Open i915/perf first and verify that all contexts created
+	 * afterward are configured to the device's default.
+	 */
+	perf_fd = igt_ioctl(fd, DRM_IOCTL_I915_PERF_OPEN, &param);
+	igt_assert_fd(perf_fd);
+
+	init_contexts(fd, ctx, ARRAY_SIZE(ctx),
+		      device_sseu.slice_mask, _pg_sseu.slice_mask);
+
+	/*
+	 * Check the device's default values, despite setting
+	 * otherwise.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], pg_slice_count), 0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), oa_slice_count);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), oa_slice_count);
+	if (__intel_gen__ == 11) {
+		igt_assert_eq(read_subslice_count(fd, ctx[0]),
+			      __subslice_count__ / 2);
+		igt_assert_eq(read_subslice_count(fd, ctx[1]),
+			      __subslice_count__ / 2);
+	}
+
+	close(perf_fd);
+
+	/*
+	 * After closing the perf stream, configurations should be
+	 * back to the programmed values.
+	 */
+	if (__intel_gen__ < 10)
+		igt_assert_eq(read_slice_count(fd, ctx[1], __slice_count__),
+			      0);
+
+	igt_assert_eq(read_slice_count(fd, ctx[0], 0), __slice_count__);
+	igt_assert_eq(read_slice_count(fd, ctx[1], 0), pg_slice_count);
+
+	for (i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(fd, ctx[i]);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	struct igt_spin_factory opts = {
+		.ctx = ctx,
+		.engine = flags,
+	};
+
+	if (gem_can_store_dword(fd, flags))
+		opts.flags |= IGT_SPIN_POLL_RUN;
+
+	return __igt_spin_batch_factory(fd, &opts);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!READ_ONCE(*spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static uint32_t
+read_slice_count_busy(int fd, uint32_t context, uint32_t expected,
+		      igt_spin_t *spin)
+{
+	return __read_slice_count(fd, context, expected, spin);
+}
+
+#define TEST_IDLE	(1)
+#define TEST_BUSY	(2)
+#define TEST_RESET	(4)
+#define TEST_HANG	(8)
+#define TEST_SUSPEND	(16)
+
+static igt_spin_t *
+__pre_set(int fd, unsigned flags, uint32_t ctx)
+{
+	if (flags & TEST_BUSY)
+		return __spin_sync(fd, ctx, I915_EXEC_RENDER);
+
+	return NULL;
+}
+
+static igt_spin_t *
+__post_set(int fd, unsigned int flags, uint32_t ctx, igt_spin_t *spin,
+	   unsigned int expected)
+{
+	if (flags & TEST_RESET)
+		igt_force_gpu_reset(fd);
+
+	if ((flags & TEST_BUSY) && !(flags & (TEST_RESET | TEST_HANG)))
+		igt_assert_eq(read_slice_count_busy(fd, ctx, 0, spin),
+			      expected);
+	else
+		igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
+
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	if (spin)
+		igt_spin_batch_free(fd, spin);
+
+	if (flags & TEST_IDLE)
+		igt_drop_caches_set(fd, DROP_RETIRE | DROP_IDLE | DROP_ACTIVE);
+
+	if (flags & TEST_SUSPEND)
+		igt_system_suspend_autoresume(SUSPEND_STATE_MEM,
+					      SUSPEND_TEST_NONE);
+
+	igt_assert_eq(read_slice_count(fd, ctx, 0), expected);
+	igt_assert_eq(read_slice_count(fd, 0, 0), __slice_count__);
+
+	return NULL;
+}
+
+/*
+ * Test context re-configuration with either idle or busy contexts.
+ */
+static void
+test_dynamic(int fd, unsigned int flags)
+{
+	struct drm_i915_gem_context_param_sseu device_sseu;
+	struct drm_i915_gem_context_param_sseu sseu = { };
+	struct drm_i915_gem_context_param arg =
+		{ .param = I915_CONTEXT_PARAM_SSEU,
+		  .ctx_id = gem_context_create(fd),
+		  .size = sizeof(sseu),
+		  .value = to_user_pointer(&sseu) };
+	igt_spin_t *spin = NULL;
+
+	igt_require(__slice_count__ > 1);
+
+	get_device_sseu(fd, &device_sseu);
+	def_sseu(&sseu, &device_sseu);
+
+	/* First set the default mask */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin,
+			  __builtin_popcount(sseu.slice_mask));
+
+	/* Then set a powergated configuration */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	pg_sseu(&sseu, &device_sseu);
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin,
+			  __builtin_popcount(sseu.slice_mask));
+
+	/* Put the device's default back again */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	def_sseu(&sseu, &device_sseu);
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin,
+			  __builtin_popcount(sseu.slice_mask));
+
+	/* One last powergated config for the road... */
+	spin = __pre_set(fd, flags, arg.ctx_id);
+	pg_sseu(&sseu, &device_sseu);
+	gem_context_set_param(fd, &arg);
+	spin = __post_set(fd, flags, arg.ctx_id, spin,
+			  __builtin_popcount(sseu.slice_mask));
+
+	gem_context_destroy(fd, arg.ctx_id);
+}
+
+igt_main
+{
+	unsigned int max_slices = 3, max_subslices = 3;
+	unsigned int i;
+	int fd;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		__intel_devid__ = intel_get_drm_devid(fd);
+		__intel_gen__ = intel_gen(__intel_devid__);
+
+		igt_require(kernel_has_per_context_sseu_support(fd));
+
+		if (__intel_gen__ >= 11)
+			__read_slice_count = gen11_read_slice_count;
+		else
+			__read_slice_count = gen8_read_slice_count;
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			drm_i915_getparam_t gp;
+
+			gp.param = I915_PARAM_SLICE_MASK;
+			gp.value = (int *) &__slice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__slice_count__ = __builtin_popcount(__slice_mask__);
+
+			gp.param = I915_PARAM_SUBSLICE_MASK;
+			gp.value = (int *) &__subslice_mask__;
+			do_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
+			__subslice_count__ =
+				__builtin_popcount(__subslice_mask__);
+		}
+
+		igt_subtest("invalid-args")
+			test_invalid_args(fd);
+
+		igt_subtest("invalid-sseu")
+			test_invalid_sseu(fd);
+
+		igt_subtest("ggtt-args")
+			test_ggtt_args(fd);
+
+		igt_subtest("engines")
+			test_engines(fd);
+
+		for (i = 1; i < max_slices; i++) {
+			igt_subtest_f("slice-pg-%i", i) {
+				igt_require(__slice_count__ > i);
+
+				test_slice_pg(fd, i);
+			}
+		}
+
+		for (i = 1; i < max_subslices; i++) {
+			igt_subtest_f("subslice-pg-%i", i) {
+				igt_require(__subslice_count__ >= 2);
+
+				/*
+				 * Only available on some Atom platforms and
+				 * Gen10+.
+				 */
+				igt_require(IS_BROXTON(__intel_devid__) ||
+					    IS_GEMINILAKE(__intel_devid__) ||
+					    __intel_gen__ >= 10);
+
+				test_subslice_pg(fd, i);
+			}
+		}
+
+		igt_subtest("perf-oa") {
+			igt_require(__slice_count__ > 1);
+
+			test_perf_oa(fd);
+		}
+
+		igt_subtest("dynamic")
+			test_dynamic(fd, 0);
+
+		igt_subtest("dynamic-busy")
+			test_dynamic(fd, TEST_BUSY);
+
+		igt_subtest("dynamic-reset")
+			test_dynamic(fd, TEST_RESET);
+
+		igt_subtest("dynamic-busy-reset")
+			test_dynamic(fd, TEST_BUSY | TEST_RESET);
+
+		igt_subtest("dynamic-busy-hang")
+			test_dynamic(fd, TEST_BUSY | TEST_HANG);
+
+		igt_subtest("dynamic-idle")
+			test_dynamic(fd, TEST_IDLE);
+
+		igt_subtest("dynamic-suspend")
+			test_dynamic(fd, TEST_SUSPEND);
+
+		igt_subtest("dynamic-idle-suspend")
+			test_dynamic(fd, TEST_IDLE | TEST_SUSPEND);
+	}
+
+	igt_fixture {
+		close(fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index d22d59e0837d..f70c859f1a62 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -248,6 +248,13 @@ foreach prog : test_progs
 		   install : true)
 endforeach
 
+test_executables += executable('gem_ctx_sseu', 'gem_ctx_sseu.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_ctx_sseu'
+
 test_executables += executable('gem_eio', 'gem_eio.c',
 	   dependencies : test_deps + [ realtime ],
 	   install_dir : libexecdir,
-- 
2.17.1

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [igt-dev] ✓ Fi.CI.BAT: success for Per context dynamic (sub)slice power-gating (rev6)
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
                   ` (11 preceding siblings ...)
  (?)
@ 2018-09-18 14:02 ` Patchwork
  -1 siblings, 0 replies; 55+ messages in thread
From: Patchwork @ 2018-09-18 14:02 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: igt-dev

== Series Details ==

Series: Per context dynamic (sub)slice power-gating (rev6)
URL   : https://patchwork.freedesktop.org/series/49190/
State : success

== Summary ==

= CI Bug Log - changes from CI_DRM_4836 -> IGTPW_1854 =

== Summary - SUCCESS ==

  No regressions found.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/49190/revisions/6/mbox/

== Known issues ==

  Here are the changes found in IGTPW_1854 that come from known issues:

  === IGT changes ===

    ==== Issues hit ====

    igt@drv_selftest@mock_hugepages:
      fi-bwr-2160:        PASS -> DMESG-FAIL (fdo#107930)

    igt@kms_flip@basic-flip-vs-modeset:
      fi-hsw-4770r:       PASS -> DMESG-WARN (fdo#105602)

    igt@kms_frontbuffer_tracking@basic:
      fi-byt-clapper:     NOTRUN -> FAIL (fdo#103167)

    igt@kms_psr@primary_page_flip:
      fi-kbl-r:           PASS -> FAIL (fdo#107336)

    
    ==== Possible fixes ====

    igt@drv_getparams_basic@basic-subslice-total:
      fi-snb-2520m:       DMESG-WARN (fdo#103713) -> PASS +9

    igt@drv_module_reload@basic-reload:
      fi-blb-e6850:       INCOMPLETE (fdo#107718) -> PASS

    igt@gem_mmap_gtt@basic-read-write:
      fi-glk-dsi:         INCOMPLETE (k.org#198133, fdo#103359) -> PASS

    igt@prime_vgem@basic-fence-flip:
      fi-ilk-650:         FAIL (fdo#104008) -> PASS

    
  fdo#103167 https://bugs.freedesktop.org/show_bug.cgi?id=103167
  fdo#103359 https://bugs.freedesktop.org/show_bug.cgi?id=103359
  fdo#103713 https://bugs.freedesktop.org/show_bug.cgi?id=103713
  fdo#104008 https://bugs.freedesktop.org/show_bug.cgi?id=104008
  fdo#105602 https://bugs.freedesktop.org/show_bug.cgi?id=105602
  fdo#107336 https://bugs.freedesktop.org/show_bug.cgi?id=107336
  fdo#107718 https://bugs.freedesktop.org/show_bug.cgi?id=107718
  fdo#107930 https://bugs.freedesktop.org/show_bug.cgi?id=107930
  k.org#198133 https://bugzilla.kernel.org/show_bug.cgi?id=198133


== Participating hosts (47 -> 46) ==

  Additional (3): fi-bsw-kefka fi-byt-clapper fi-elk-e7500 
  Missing    (4): fi-ctg-p8600 fi-ilk-m540 fi-byt-squawks fi-bsw-cyan 


== Build changes ==

    * IGT: IGT_4645 -> IGTPW_1854

  CI_DRM_4836: b2b0444aa439ade1ed809a91a19d382fbb5e7700 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_1854: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1854/
  IGT_4645: 03b90a39ed12a568c9da752466ea708d6348e110 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools



== Testlist changes ==

+igt@gem_ctx_sseu@dynamic
+igt@gem_ctx_sseu@dynamic-busy
+igt@gem_ctx_sseu@dynamic-busy-hang
+igt@gem_ctx_sseu@dynamic-busy-reset
+igt@gem_ctx_sseu@dynamic-idle
+igt@gem_ctx_sseu@dynamic-idle-suspend
+igt@gem_ctx_sseu@dynamic-reset
+igt@gem_ctx_sseu@dynamic-suspend
+igt@gem_ctx_sseu@engines
+igt@gem_ctx_sseu@ggtt-args
+igt@gem_ctx_sseu@invalid-args
+igt@gem_ctx_sseu@invalid-sseu
+igt@gem_ctx_sseu@perf-oa
+igt@gem_ctx_sseu@slice-pg-1
+igt@gem_ctx_sseu@slice-pg-2
+igt@gem_ctx_sseu@subslice-pg-1
+igt@gem_ctx_sseu@subslice-pg-2

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1854/issues.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [igt-dev] ✓ Fi.CI.IGT: success for Per context dynamic (sub)slice power-gating (rev6)
  2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
                   ` (12 preceding siblings ...)
  (?)
@ 2018-09-18 15:18 ` Patchwork
  -1 siblings, 0 replies; 55+ messages in thread
From: Patchwork @ 2018-09-18 15:18 UTC (permalink / raw)
  To: Tvrtko Ursulin; +Cc: igt-dev

== Series Details ==

Series: Per context dynamic (sub)slice power-gating (rev6)
URL   : https://patchwork.freedesktop.org/series/49190/
State : success

== Summary ==

= CI Bug Log - changes from IGT_4645_full -> IGTPW_1854_full =

== Summary - SUCCESS ==

  No regressions found.

  External URL: https://patchwork.freedesktop.org/api/1.0/series/49190/revisions/6/mbox/

== Known issues ==

  Here are the changes found in IGTPW_1854_full that come from known issues:

  === IGT changes ===

    ==== Issues hit ====

    igt@gem_ctx_isolation@vecs0-s3:
      shard-kbl:          PASS -> INCOMPLETE (fdo#103665)

    igt@kms_busy@extended-pageflip-modeset-hang-oldfb-render-c:
      shard-glk:          PASS -> DMESG-WARN (fdo#107956)

    igt@kms_cursor_legacy@cursorb-vs-flipb-toggle:
      shard-glk:          PASS -> DMESG-WARN (fdo#105763, fdo#106538)

    igt@kms_draw_crc@draw-method-rgb565-mmap-wc-ytiled:
      shard-glk:          PASS -> FAIL (fdo#103184)

    igt@pm_rpm@fences:
      shard-kbl:          PASS -> DMESG-WARN (fdo#103558, fdo#105602) +2

    igt@syncobj_wait@invalid-reset-one-illegal-handle:
      shard-snb:          PASS -> INCOMPLETE (fdo#105411)

    
    ==== Possible fixes ====

    igt@drv_suspend@shrink:
      shard-snb:          INCOMPLETE (fdo#106886, fdo#105411) -> PASS

    igt@kms_busy@extended-pageflip-modeset-hang-oldfb-render-b:
      shard-hsw:          DMESG-WARN (fdo#107956) -> PASS

    igt@kms_cursor_legacy@cursor-vs-flip-toggle:
      shard-hsw:          FAIL (fdo#103355) -> PASS

    igt@kms_draw_crc@draw-method-xrgb8888-mmap-cpu-ytiled:
      shard-glk:          FAIL (fdo#107791) -> PASS

    igt@kms_flip@2x-flip-vs-expired-vblank-interruptible:
      shard-glk:          FAIL (fdo#105363) -> PASS

    igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
      shard-kbl:          INCOMPLETE (fdo#103665) -> PASS

    igt@kms_rotation_crc@primary-rotation-180:
      shard-snb:          FAIL (fdo#103925) -> PASS

    igt@kms_setmode@basic:
      shard-apl:          FAIL (fdo#99912) -> PASS

    
  fdo#103184 https://bugs.freedesktop.org/show_bug.cgi?id=103184
  fdo#103355 https://bugs.freedesktop.org/show_bug.cgi?id=103355
  fdo#103558 https://bugs.freedesktop.org/show_bug.cgi?id=103558
  fdo#103665 https://bugs.freedesktop.org/show_bug.cgi?id=103665
  fdo#103925 https://bugs.freedesktop.org/show_bug.cgi?id=103925
  fdo#105363 https://bugs.freedesktop.org/show_bug.cgi?id=105363
  fdo#105411 https://bugs.freedesktop.org/show_bug.cgi?id=105411
  fdo#105602 https://bugs.freedesktop.org/show_bug.cgi?id=105602
  fdo#105763 https://bugs.freedesktop.org/show_bug.cgi?id=105763
  fdo#106538 https://bugs.freedesktop.org/show_bug.cgi?id=106538
  fdo#106886 https://bugs.freedesktop.org/show_bug.cgi?id=106886
  fdo#107791 https://bugs.freedesktop.org/show_bug.cgi?id=107791
  fdo#107956 https://bugs.freedesktop.org/show_bug.cgi?id=107956
  fdo#99912 https://bugs.freedesktop.org/show_bug.cgi?id=99912


== Participating hosts (5 -> 5) ==

  No changes in participating hosts


== Build changes ==

    * IGT: IGT_4645 -> IGTPW_1854
    * Linux: CI_DRM_4833 -> CI_DRM_4836

  CI_DRM_4833: 75bb460b367a614d10b0fba220143bee42657d7e @ git://anongit.freedesktop.org/gfx-ci/linux
  CI_DRM_4836: b2b0444aa439ade1ed809a91a19d382fbb5e7700 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_1854: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1854/
  IGT_4645: 03b90a39ed12a568c9da752466ea708d6348e110 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1854/shards.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

end of thread, other threads:[~2018-09-18 15:18 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-05 14:25 [PATH i-g-t 0/2] Per context dynamic (sub)slice power-gating Tvrtko Ursulin
2018-09-05 14:25 ` [igt-dev] " Tvrtko Ursulin
2018-09-05 14:25 ` [PATH i-g-t 1/2] headers: bump Tvrtko Ursulin
2018-09-05 14:25   ` [igt-dev] " Tvrtko Ursulin
2018-09-05 14:25 ` [PATH i-g-t 2/2] tests: add slice power programming test Tvrtko Ursulin
2018-09-05 14:25   ` [igt-dev] " Tvrtko Ursulin
2018-09-05 22:57   ` Chris Wilson
2018-09-05 22:57     ` Chris Wilson
2018-09-06  7:00   ` Chris Wilson
2018-09-06  7:00     ` Chris Wilson
2018-09-06  9:31     ` Tvrtko Ursulin
2018-09-06  9:31       ` [igt-dev] [Intel-gfx] " Tvrtko Ursulin
2018-09-06  9:50       ` [igt-dev] " Chris Wilson
2018-09-06  9:50         ` [igt-dev] [Intel-gfx] " Chris Wilson
2018-09-11 11:34         ` [PATH i-g-t v11 " Tvrtko Ursulin
2018-09-11 11:34           ` [igt-dev] " Tvrtko Ursulin
2018-09-11 11:45           ` Chris Wilson
2018-09-11 11:45             ` Chris Wilson
2018-09-11 12:00             ` Tvrtko Ursulin
2018-09-11 12:00               ` [Intel-gfx] " Tvrtko Ursulin
2018-09-11 14:42               ` [PATH i-g-t v12 " Tvrtko Ursulin
2018-09-11 14:42                 ` [igt-dev] " Tvrtko Ursulin
2018-09-12 11:53                 ` Chris Wilson
2018-09-12 11:53                   ` [igt-dev] [Intel-gfx] " Chris Wilson
2018-09-13 10:38                   ` Tvrtko Ursulin
2018-09-13 10:38                     ` [igt-dev] [Intel-gfx] " Tvrtko Ursulin
2018-09-13 10:48                     ` Chris Wilson
2018-09-13 10:48                       ` [igt-dev] [Intel-gfx] " Chris Wilson
2018-09-14 16:04                 ` [PATCH i-g-t v13 2/2] tests/gem_ctx_sseu: Dynamic (sub)slice programming tests Tvrtko Ursulin
2018-09-14 16:04                   ` [Intel-gfx] " Tvrtko Ursulin
2018-09-14 16:07                   ` [igt-dev] " Chris Wilson
2018-09-14 16:07                     ` Chris Wilson
2018-09-14 16:17                   ` Chris Wilson
2018-09-14 16:17                     ` Chris Wilson
2018-09-17  9:33                     ` Tvrtko Ursulin
2018-09-17  9:33                       ` [Intel-gfx] " Tvrtko Ursulin
2018-09-17 10:38                       ` Chris Wilson
2018-09-17 10:38                         ` [Intel-gfx] " Chris Wilson
2018-09-17 11:28                     ` [PATCH i-g-t v14 " Tvrtko Ursulin
2018-09-17 11:28                       ` [Intel-gfx] " Tvrtko Ursulin
2018-09-17 12:04                       ` [igt-dev] " Chris Wilson
2018-09-17 12:04                         ` Chris Wilson
2018-09-18 13:41                       ` [PATCH i-g-t v15 " Tvrtko Ursulin
2018-09-18 13:41                         ` [igt-dev] " Tvrtko Ursulin
2018-09-05 16:46 ` [igt-dev] ✓ Fi.CI.BAT: success for Per context dynamic (sub)slice power-gating Patchwork
2018-09-05 22:44 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork
2018-09-11 18:13 ` [igt-dev] ✓ Fi.CI.BAT: success for Per context dynamic (sub)slice power-gating (rev3) Patchwork
2018-09-11 23:31 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork
2018-09-14 17:22 ` [igt-dev] ✓ Fi.CI.BAT: success for Per context dynamic (sub)slice power-gating (rev4) Patchwork
2018-09-14 22:22 ` [igt-dev] ✗ Fi.CI.IGT: failure " Patchwork
2018-09-17 12:21 ` [igt-dev] ✗ Fi.CI.BAT: failure for Per context dynamic (sub)slice power-gating (rev5) Patchwork
2018-09-17 18:21 ` [igt-dev] ✓ Fi.CI.BAT: success " Patchwork
2018-09-17 20:43 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork
2018-09-18 14:02 ` [igt-dev] ✓ Fi.CI.BAT: success for Per context dynamic (sub)slice power-gating (rev6) Patchwork
2018-09-18 15:18 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.