Message ID | 1426022258-22481-3-git-send-email-jeff.mcgee@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 10 March 2015 at 21:17, <jeff.mcgee@intel.com> wrote: > From: Jeff McGee <jeff.mcgee@intel.com> > > New test pm_sseu is intended for any subtest related to the > slice/subslice/EU power gating feature. The sole initial subtest, > 'full-enable', confirms that the slice/subslice/EU state is at > full enablement when the render engine is active. Starting with > Gen9 SKL, the render power gating feature can leave SSEU in a > partially enabled state upon resumption of render work unless > explicit action is taken. Please add a short description to the test using the IGT_TEST_DESCRIPTION macro, so that it is included in the documentation and help output. > > Signed-off-by: Jeff McGee <jeff.mcgee@intel.com> > --- > tests/.gitignore | 1 + > tests/Makefile.sources | 1 + > tests/pm_sseu.c | 373 +++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 375 insertions(+) > create mode 100644 tests/pm_sseu.c > > diff --git a/tests/.gitignore b/tests/.gitignore > index 7b4dd94..23094ce 100644 > --- a/tests/.gitignore > +++ b/tests/.gitignore > @@ -144,6 +144,7 @@ pm_psr > pm_rc6_residency > pm_rpm > pm_rps > +pm_sseu > prime_nv_api > prime_nv_pcopy > prime_nv_test > diff --git a/tests/Makefile.sources b/tests/Makefile.sources > index 51e8376..74106c0 100644 > --- a/tests/Makefile.sources > +++ b/tests/Makefile.sources > @@ -82,6 +82,7 @@ TESTS_progs_M = \ > pm_rpm \ > pm_rps \ > pm_rc6_residency \ > + pm_sseu \ > prime_self_import \ > template \ > $(NULL) > diff --git a/tests/pm_sseu.c b/tests/pm_sseu.c > new file mode 100644 > index 0000000..45aeef3 > --- /dev/null > +++ b/tests/pm_sseu.c > @@ -0,0 +1,373 @@ > +/* > + * Copyright © 2015 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: > + * Jeff McGee <jeff.mcgee@intel.com> > + */ > + > +#include <fcntl.h> > +#include <unistd.h> > +#include <string.h> > +#include <errno.h> > +#include <time.h> > +#include "drmtest.h" > +#include "i915_drm.h" > +#include "intel_io.h" > +#include "intel_bufmgr.h" > +#include "intel_batchbuffer.h" > +#include "intel_chipset.h" > +#include "ioctl_wrappers.h" > +#include "igt_debugfs.h" > +#include "media_spin.h" > + > +static double > +to_dt(const struct timespec *start, const struct timespec *end) > +{ > + double dt; > + > + dt = (end->tv_sec - start->tv_sec) * 1e3; > + dt += (end->tv_nsec - start->tv_nsec) * 1e-6; > + > + return dt; > +} > + > +struct status { > + struct { > + int slice_total; > + int subslice_total; > + int subslice_per; > + int eu_total; > + int eu_per; > + bool has_slice_pg; > + bool has_subslice_pg; > + bool has_eu_pg; > + } info; > + struct { > + int slice_total; > + int subslice_total; > + int subslice_per; > + int eu_total; > + int eu_per; > + } hw; > +}; > + > +#define DBG_STATUS_BUF_SIZE 4096 > + > +struct { > + int init; > + int status_fd; > + char status_buf[DBG_STATUS_BUF_SIZE]; > +} dbg; > + > +static void > +dbg_get_status_section(const char *title, char **first, char **last) > +{ > + char *pos; > + > + *first = strstr(dbg.status_buf, title); > + igt_assert(*first != NULL); > + > + pos = *first; > + do { > + pos = strchr(pos, '\n'); > + igt_assert(pos != NULL); > + pos++; > + } while (*pos == ' '); /* lines in the section begin with a space */ > + *last = pos - 1; > +} > + > +static int > +dbg_get_int(const char *first, const char *last, const char *name) > +{ > + char *pos; > + > + pos = strstr(first, name); > + igt_assert(pos != NULL); > + pos = strstr(pos, ":"); > + igt_assert(pos != NULL); > + pos += 2; > + igt_assert(pos < last); > + > + return strtol(pos, &pos, 10); > +} > + > +static bool > +dbg_get_bool(const char *first, const char *last, const char *name) > +{ > + char *pos; > + > + pos = strstr(first, name); > + igt_assert(pos != NULL); > + pos = strstr(pos, ":"); > + igt_assert(pos != NULL); > + pos += 2; > + igt_assert(pos < last); > + > + if (*pos == 'y') > + return true; > + if (*pos == 'n') > + return false; > + > + igt_assert(false); Perhaps use igt_assert_f() to add a more detailed error message? > + return false; > +} > + > +static void > +dbg_get_status(struct status *stat) > +{ > + char *first, *last; > + int nread; > + > + lseek(dbg.status_fd, 0, SEEK_SET); > + nread = read(dbg.status_fd, dbg.status_buf, DBG_STATUS_BUF_SIZE); > + igt_assert(nread < DBG_STATUS_BUF_SIZE); igt_assert_lt() would produce a better error message here. Using igt.cocci will suggest other similar changes elsewhere too. > + dbg.status_buf[nread] = '\0'; > + > + memset(stat, 0, sizeof(*stat)); > + > + dbg_get_status_section("SSEU Device Info", &first, &last); > + stat->info.slice_total = > + dbg_get_int(first, last, "Available Slice Total:"); > + stat->info.subslice_total = > + dbg_get_int(first, last, "Available Subslice Total:"); > + stat->info.subslice_per = > + dbg_get_int(first, last, "Available Subslice Per Slice:"); > + stat->info.eu_total = > + dbg_get_int(first, last, "Available EU Total:"); > + stat->info.eu_per = > + dbg_get_int(first, last, "Available EU Per Subslice:"); > + stat->info.has_slice_pg = > + dbg_get_bool(first, last, "Has Slice Power Gating:"); > + stat->info.has_subslice_pg = > + dbg_get_bool(first, last, "Has Subslice Power Gating:"); > + stat->info.has_eu_pg = > + dbg_get_bool(first, last, "Has EU Power Gating:"); > + > + dbg_get_status_section("SSEU Device Status", &first, &last); > + stat->hw.slice_total = > + dbg_get_int(first, last, "Enabled Slice Total:"); > + stat->hw.subslice_total = > + dbg_get_int(first, last, "Enabled Subslice Total:"); > + stat->hw.subslice_per = > + dbg_get_int(first, last, "Enabled Subslice Per Slice:"); > + stat->hw.eu_total = > + dbg_get_int(first, last, "Enabled EU Total:"); > + stat->hw.eu_per = > + dbg_get_int(first, last, "Enabled EU Per Subslice:"); > +} > + > +static void > +dbg_init(void) > +{ > + dbg.status_fd = igt_debugfs_open("i915_sseu_status", O_RDONLY); > + igt_assert(dbg.status_fd != -1); > + dbg.init = 1; > +} > + > +static void > +dbg_deinit(void) > +{ > + switch (dbg.init) > + { > + case 1: > + close(dbg.status_fd); > + } > +} > + > +struct { > + int init; > + int drm_fd; > + int devid; > + int gen; > + int has_ppgtt; > + drm_intel_bufmgr *bufmgr; > + struct intel_batchbuffer *batch; > + igt_media_spinfunc_t spinfunc; > + struct igt_buf buf; > + uint32_t spins_per_msec; > +} gem; > + > +static void > +gem_check_spin(uint32_t spins) > +{ > + uint32_t *data; > + > + data = (uint32_t*)gem.buf.bo->virtual; > + igt_assert(*data == spins); > +} > + > +static uint32_t > +gem_get_target_spins(double dt) > +{ > + struct timespec tstart, tdone; > + double prev_dt, cur_dt; > + uint32_t spins; > + int i, ret; > + > + /* Double increments until we bound the target time */ > + prev_dt = 0.0; > + for (i = 0; i < 32; i++) { > + spins = 1 << i; > + clock_gettime(CLOCK_MONOTONIC, &tstart); > + > + gem.spinfunc(gem.batch, &gem.buf, spins); > + ret = drm_intel_bo_map(gem.buf.bo, 0); > + igt_assert (ret == 0); > + clock_gettime(CLOCK_MONOTONIC, &tdone); > + > + gem_check_spin(spins); > + drm_intel_bo_unmap(gem.buf.bo); > + > + cur_dt = to_dt(&tstart, &tdone); > + if (cur_dt > dt) > + break; > + prev_dt = cur_dt; > + } > + igt_assert(i != 32); > + > + /* Linearly interpolate between i and i-1 to get target increments */ > + spins = 1 << (i-1); /* lower bound spins */ > + spins += spins * (dt - prev_dt)/(cur_dt - prev_dt); /* target spins */ > + > + return spins; > +} > + > +static void > +gem_init(void) > +{ > + gem.drm_fd = drm_open_any(); > + gem.init = 1; > + > + gem.devid = intel_get_drm_devid(gem.drm_fd); > + gem.gen = intel_gen(gem.devid); > + gem.has_ppgtt = gem_uses_aliasing_ppgtt(gem.drm_fd); > + > + gem.bufmgr = drm_intel_bufmgr_gem_init(gem.drm_fd, 4096); > + igt_assert(gem.bufmgr); > + gem.init = 2; > + > + drm_intel_bufmgr_gem_enable_reuse(gem.bufmgr); > + > + gem.batch = intel_batchbuffer_alloc(gem.bufmgr, gem.devid); > + igt_assert(gem.batch); > + gem.init = 3; > + > + gem.spinfunc = igt_get_media_spinfunc(gem.devid); > + igt_assert(gem.spinfunc); > + > + gem.buf.stride = sizeof(uint32_t); > + gem.buf.tiling = I915_TILING_NONE; > + gem.buf.size = gem.buf.stride; > + gem.buf.bo = drm_intel_bo_alloc(gem.bufmgr, "", gem.buf.size, 4096); > + igt_assert(gem.buf.bo); > + gem.init = 4; > + > + gem.spins_per_msec = gem_get_target_spins(100) / 100; > +} > + > +static void > +gem_deinit(void) > +{ > + switch (gem.init) > + { > + case 4: > + drm_intel_bo_unmap(gem.buf.bo); > + drm_intel_bo_unreference(gem.buf.bo); > + case 3: > + intel_batchbuffer_free(gem.batch); > + case 2: > + drm_intel_bufmgr_destroy(gem.bufmgr); > + case 1: > + close(gem.drm_fd); > + } > +} > + > +static void > +check_full_enable(struct status *stat) > +{ > + igt_assert(stat->hw.slice_total == stat->info.slice_total); > + igt_assert(stat->hw.subslice_total == stat->info.subslice_total); > + igt_assert(stat->hw.subslice_per == stat->info.subslice_per); > + > + /* > + * EU are powered in pairs, but it is possible for one EU in the pair > + * to be non-functional due to fusing. The determination of enabled > + * EU does not account for this and can therefore actually exceed the > + * available count. Allow for this small discrepancy in our > + * comparison. > + */ > + igt_assert(stat->hw.eu_total >= stat->info.eu_total); > + igt_assert(stat->hw.eu_per >= stat->info.eu_per); > +} > + > +static void > +full_enable(void) > +{ > + struct status stat; > + const int spin_msec = 10; > + int ret, spins; > + > + /* Simulation doesn't currently model slice/subslice/EU power gating. */ > + igt_skip_on_simulation(); > + > + /* > + * Gen9 SKL is the first case in which render power gating can leave > + * slice/subslice/EU in a partially enabled state upon resumption of > + * render work. So start checking that this is prevented as of Gen9. > + */ > + igt_require(gem.gen >= 9); > + > + spins = spin_msec * gem.spins_per_msec; > + > + gem.spinfunc(gem.batch, &gem.buf, spins); > + > + usleep(2000); /* 2ms wait to make sure batch is running */ > + dbg_get_status(&stat); > + > + ret = drm_intel_bo_map(gem.buf.bo, 0); > + igt_assert (ret == 0); > + > + gem_check_spin(spins); > + drm_intel_bo_unmap(gem.buf.bo); > + > + check_full_enable(&stat); > +} > + > +static void > +exit_handler(int sig) > +{ > + gem_deinit(); > + dbg_deinit(); > +} > + > +igt_main > +{ > + igt_fixture { > + igt_install_exit_handler(exit_handler); > + > + dbg_init(); > + gem_init(); > + } > + > + igt_subtest("full-enable") > + full_enable(); > +} > -- > 2.3.0 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/intel-gfx
On Thu, Mar 12, 2015 at 12:09:50PM +0000, Thomas Wood wrote: > On 10 March 2015 at 21:17, <jeff.mcgee@intel.com> wrote: > > From: Jeff McGee <jeff.mcgee@intel.com> > > > > New test pm_sseu is intended for any subtest related to the > > slice/subslice/EU power gating feature. The sole initial subtest, > > 'full-enable', confirms that the slice/subslice/EU state is at > > full enablement when the render engine is active. Starting with > > Gen9 SKL, the render power gating feature can leave SSEU in a > > partially enabled state upon resumption of render work unless > > explicit action is taken. > > Please add a short description to the test using the > IGT_TEST_DESCRIPTION macro, so that it is included in the > documentation and help output. > Hi Thomas. I have posted v2 patches to address this and your other comments. Can you please have a second look? Thanks -Jeff
diff --git a/tests/.gitignore b/tests/.gitignore index 7b4dd94..23094ce 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -144,6 +144,7 @@ pm_psr pm_rc6_residency pm_rpm pm_rps +pm_sseu prime_nv_api prime_nv_pcopy prime_nv_test diff --git a/tests/Makefile.sources b/tests/Makefile.sources index 51e8376..74106c0 100644 --- a/tests/Makefile.sources +++ b/tests/Makefile.sources @@ -82,6 +82,7 @@ TESTS_progs_M = \ pm_rpm \ pm_rps \ pm_rc6_residency \ + pm_sseu \ prime_self_import \ template \ $(NULL) diff --git a/tests/pm_sseu.c b/tests/pm_sseu.c new file mode 100644 index 0000000..45aeef3 --- /dev/null +++ b/tests/pm_sseu.c @@ -0,0 +1,373 @@ +/* + * Copyright © 2015 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: + * Jeff McGee <jeff.mcgee@intel.com> + */ + +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include "drmtest.h" +#include "i915_drm.h" +#include "intel_io.h" +#include "intel_bufmgr.h" +#include "intel_batchbuffer.h" +#include "intel_chipset.h" +#include "ioctl_wrappers.h" +#include "igt_debugfs.h" +#include "media_spin.h" + +static double +to_dt(const struct timespec *start, const struct timespec *end) +{ + double dt; + + dt = (end->tv_sec - start->tv_sec) * 1e3; + dt += (end->tv_nsec - start->tv_nsec) * 1e-6; + + return dt; +} + +struct status { + struct { + int slice_total; + int subslice_total; + int subslice_per; + int eu_total; + int eu_per; + bool has_slice_pg; + bool has_subslice_pg; + bool has_eu_pg; + } info; + struct { + int slice_total; + int subslice_total; + int subslice_per; + int eu_total; + int eu_per; + } hw; +}; + +#define DBG_STATUS_BUF_SIZE 4096 + +struct { + int init; + int status_fd; + char status_buf[DBG_STATUS_BUF_SIZE]; +} dbg; + +static void +dbg_get_status_section(const char *title, char **first, char **last) +{ + char *pos; + + *first = strstr(dbg.status_buf, title); + igt_assert(*first != NULL); + + pos = *first; + do { + pos = strchr(pos, '\n'); + igt_assert(pos != NULL); + pos++; + } while (*pos == ' '); /* lines in the section begin with a space */ + *last = pos - 1; +} + +static int +dbg_get_int(const char *first, const char *last, const char *name) +{ + char *pos; + + pos = strstr(first, name); + igt_assert(pos != NULL); + pos = strstr(pos, ":"); + igt_assert(pos != NULL); + pos += 2; + igt_assert(pos < last); + + return strtol(pos, &pos, 10); +} + +static bool +dbg_get_bool(const char *first, const char *last, const char *name) +{ + char *pos; + + pos = strstr(first, name); + igt_assert(pos != NULL); + pos = strstr(pos, ":"); + igt_assert(pos != NULL); + pos += 2; + igt_assert(pos < last); + + if (*pos == 'y') + return true; + if (*pos == 'n') + return false; + + igt_assert(false); + return false; +} + +static void +dbg_get_status(struct status *stat) +{ + char *first, *last; + int nread; + + lseek(dbg.status_fd, 0, SEEK_SET); + nread = read(dbg.status_fd, dbg.status_buf, DBG_STATUS_BUF_SIZE); + igt_assert(nread < DBG_STATUS_BUF_SIZE); + dbg.status_buf[nread] = '\0'; + + memset(stat, 0, sizeof(*stat)); + + dbg_get_status_section("SSEU Device Info", &first, &last); + stat->info.slice_total = + dbg_get_int(first, last, "Available Slice Total:"); + stat->info.subslice_total = + dbg_get_int(first, last, "Available Subslice Total:"); + stat->info.subslice_per = + dbg_get_int(first, last, "Available Subslice Per Slice:"); + stat->info.eu_total = + dbg_get_int(first, last, "Available EU Total:"); + stat->info.eu_per = + dbg_get_int(first, last, "Available EU Per Subslice:"); + stat->info.has_slice_pg = + dbg_get_bool(first, last, "Has Slice Power Gating:"); + stat->info.has_subslice_pg = + dbg_get_bool(first, last, "Has Subslice Power Gating:"); + stat->info.has_eu_pg = + dbg_get_bool(first, last, "Has EU Power Gating:"); + + dbg_get_status_section("SSEU Device Status", &first, &last); + stat->hw.slice_total = + dbg_get_int(first, last, "Enabled Slice Total:"); + stat->hw.subslice_total = + dbg_get_int(first, last, "Enabled Subslice Total:"); + stat->hw.subslice_per = + dbg_get_int(first, last, "Enabled Subslice Per Slice:"); + stat->hw.eu_total = + dbg_get_int(first, last, "Enabled EU Total:"); + stat->hw.eu_per = + dbg_get_int(first, last, "Enabled EU Per Subslice:"); +} + +static void +dbg_init(void) +{ + dbg.status_fd = igt_debugfs_open("i915_sseu_status", O_RDONLY); + igt_assert(dbg.status_fd != -1); + dbg.init = 1; +} + +static void +dbg_deinit(void) +{ + switch (dbg.init) + { + case 1: + close(dbg.status_fd); + } +} + +struct { + int init; + int drm_fd; + int devid; + int gen; + int has_ppgtt; + drm_intel_bufmgr *bufmgr; + struct intel_batchbuffer *batch; + igt_media_spinfunc_t spinfunc; + struct igt_buf buf; + uint32_t spins_per_msec; +} gem; + +static void +gem_check_spin(uint32_t spins) +{ + uint32_t *data; + + data = (uint32_t*)gem.buf.bo->virtual; + igt_assert(*data == spins); +} + +static uint32_t +gem_get_target_spins(double dt) +{ + struct timespec tstart, tdone; + double prev_dt, cur_dt; + uint32_t spins; + int i, ret; + + /* Double increments until we bound the target time */ + prev_dt = 0.0; + for (i = 0; i < 32; i++) { + spins = 1 << i; + clock_gettime(CLOCK_MONOTONIC, &tstart); + + gem.spinfunc(gem.batch, &gem.buf, spins); + ret = drm_intel_bo_map(gem.buf.bo, 0); + igt_assert (ret == 0); + clock_gettime(CLOCK_MONOTONIC, &tdone); + + gem_check_spin(spins); + drm_intel_bo_unmap(gem.buf.bo); + + cur_dt = to_dt(&tstart, &tdone); + if (cur_dt > dt) + break; + prev_dt = cur_dt; + } + igt_assert(i != 32); + + /* Linearly interpolate between i and i-1 to get target increments */ + spins = 1 << (i-1); /* lower bound spins */ + spins += spins * (dt - prev_dt)/(cur_dt - prev_dt); /* target spins */ + + return spins; +} + +static void +gem_init(void) +{ + gem.drm_fd = drm_open_any(); + gem.init = 1; + + gem.devid = intel_get_drm_devid(gem.drm_fd); + gem.gen = intel_gen(gem.devid); + gem.has_ppgtt = gem_uses_aliasing_ppgtt(gem.drm_fd); + + gem.bufmgr = drm_intel_bufmgr_gem_init(gem.drm_fd, 4096); + igt_assert(gem.bufmgr); + gem.init = 2; + + drm_intel_bufmgr_gem_enable_reuse(gem.bufmgr); + + gem.batch = intel_batchbuffer_alloc(gem.bufmgr, gem.devid); + igt_assert(gem.batch); + gem.init = 3; + + gem.spinfunc = igt_get_media_spinfunc(gem.devid); + igt_assert(gem.spinfunc); + + gem.buf.stride = sizeof(uint32_t); + gem.buf.tiling = I915_TILING_NONE; + gem.buf.size = gem.buf.stride; + gem.buf.bo = drm_intel_bo_alloc(gem.bufmgr, "", gem.buf.size, 4096); + igt_assert(gem.buf.bo); + gem.init = 4; + + gem.spins_per_msec = gem_get_target_spins(100) / 100; +} + +static void +gem_deinit(void) +{ + switch (gem.init) + { + case 4: + drm_intel_bo_unmap(gem.buf.bo); + drm_intel_bo_unreference(gem.buf.bo); + case 3: + intel_batchbuffer_free(gem.batch); + case 2: + drm_intel_bufmgr_destroy(gem.bufmgr); + case 1: + close(gem.drm_fd); + } +} + +static void +check_full_enable(struct status *stat) +{ + igt_assert(stat->hw.slice_total == stat->info.slice_total); + igt_assert(stat->hw.subslice_total == stat->info.subslice_total); + igt_assert(stat->hw.subslice_per == stat->info.subslice_per); + + /* + * EU are powered in pairs, but it is possible for one EU in the pair + * to be non-functional due to fusing. The determination of enabled + * EU does not account for this and can therefore actually exceed the + * available count. Allow for this small discrepancy in our + * comparison. + */ + igt_assert(stat->hw.eu_total >= stat->info.eu_total); + igt_assert(stat->hw.eu_per >= stat->info.eu_per); +} + +static void +full_enable(void) +{ + struct status stat; + const int spin_msec = 10; + int ret, spins; + + /* Simulation doesn't currently model slice/subslice/EU power gating. */ + igt_skip_on_simulation(); + + /* + * Gen9 SKL is the first case in which render power gating can leave + * slice/subslice/EU in a partially enabled state upon resumption of + * render work. So start checking that this is prevented as of Gen9. + */ + igt_require(gem.gen >= 9); + + spins = spin_msec * gem.spins_per_msec; + + gem.spinfunc(gem.batch, &gem.buf, spins); + + usleep(2000); /* 2ms wait to make sure batch is running */ + dbg_get_status(&stat); + + ret = drm_intel_bo_map(gem.buf.bo, 0); + igt_assert (ret == 0); + + gem_check_spin(spins); + drm_intel_bo_unmap(gem.buf.bo); + + check_full_enable(&stat); +} + +static void +exit_handler(int sig) +{ + gem_deinit(); + dbg_deinit(); +} + +igt_main +{ + igt_fixture { + igt_install_exit_handler(exit_handler); + + dbg_init(); + gem_init(); + } + + igt_subtest("full-enable") + full_enable(); +}