[igt-dev] [PATCH i-g-t] igt/drv_suspend: Suspend under memory pressure
Ewelina Musial
ewelina.musial at intel.com
Fri Jun 8 10:21:41 UTC 2018
On Thu, Jun 07, 2018 at 09:50:54PM +0100, Chris Wilson wrote:
> Recently we discovered that we have a race between swapping and
> suspend in our resume path (we might be trying to page in an object
> after disabling the block devices). Let's try to exercise that by
> exhausting all of system memory before suspend.
>
> v2: Explicitly share the large memory area on forking to avoid running
> out of memory inside the suspend helpers (for they fork!)
>
> References: https://bugs.freedesktop.org/show_bug.cgi?id=106640
> Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
> Cc: Tomi Sarvela <tomi.p.sarvela at intel.com>
> Reviewed-by: Antonio Argenziano <antonio.argenziano at intel.com>
> ---
> lib/igt_core.c | 34 ++++++++++++++++------------
> lib/igt_core.h | 1 +
> tests/drv_suspend.c | 54 +++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 75 insertions(+), 14 deletions(-)
>
> diff --git a/lib/igt_core.c b/lib/igt_core.c
> index 5092a3f03..06d8b0370 100644
> --- a/lib/igt_core.c
> +++ b/lib/igt_core.c
> @@ -1702,20 +1702,7 @@ void igt_child_done(pid_t pid)
> test_children[i] = test_children[i + 1];
> }
>
> -/**
> - * igt_waitchildren:
> - *
> - * Wait for all children forked with igt_fork.
> - *
> - * The magic here is that exit codes from children will be correctly propagated
> - * to the main thread, including the relevant exit code if a child thread failed.
> - * Of course if multiple children failed with different exit codes the resulting
> - * exit code will be non-deterministic.
> - *
> - * Note that igt_skip() will not be forwarded, feature tests need to be done
> - * before spawning threads with igt_fork().
> - */
> -void igt_waitchildren(void)
> +int __igt_waitchildren(void)
> {
> int err = 0;
> int count;
> @@ -1761,6 +1748,25 @@ void igt_waitchildren(void)
> }
>
> num_test_children = 0;
> + return err;
> +}
> +
> +/**
> + * igt_waitchildren:
> + *
> + * Wait for all children forked with igt_fork.
> + *
> + * The magic here is that exit codes from children will be correctly propagated
> + * to the main thread, including the relevant exit code if a child thread failed.
> + * Of course if multiple children failed with different exit codes the resulting
> + * exit code will be non-deterministic.
> + *
> + * Note that igt_skip() will not be forwarded, feature tests need to be done
> + * before spawning threads with igt_fork().
> + */
> +void igt_waitchildren(void)
> +{
> + int err = __igt_waitchildren();
> if (err)
> igt_fail(err);
> }
> diff --git a/lib/igt_core.h b/lib/igt_core.h
> index bf8cd66ca..a73b06493 100644
> --- a/lib/igt_core.h
> +++ b/lib/igt_core.h
> @@ -742,6 +742,7 @@ bool __igt_fork(void);
> for (int child = 0; child < (num_children); child++) \
> for (; __igt_fork(); exit(0))
> void igt_child_done(pid_t pid);
> +int __igt_waitchildren(void);
> void igt_waitchildren(void);
> void igt_waitchildren_timeout(int seconds, const char *reason);
>
> diff --git a/tests/drv_suspend.c b/tests/drv_suspend.c
> index 2e39f20ae..9a9ff2005 100644
> --- a/tests/drv_suspend.c
> +++ b/tests/drv_suspend.c
> @@ -160,6 +160,57 @@ test_sysfs_reader(bool hibernate)
> igt_stop_helper(&reader);
> }
>
> +static void
> +test_shrink(int fd, unsigned int mode)
> +{
> + uint64_t *can_mlock, pin;
> +
> + gem_quiescent_gpu(fd);
> + intel_purge_vm_caches(fd);
> +
> + pin = (intel_get_total_ram_mb() + 1) << 20;
> +
> + igt_debug("Total memory %'"PRIu64" B (%'"PRIu64" MiB)\n",
> + pin, pin >> 20);
> + can_mlock = mmap(NULL, pin, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
> + igt_require(can_mlock != MAP_FAILED);
> +
> + /* Lock all the system memory, forcing the driver into swap and OOM */
> + for (uint64_t inc = 64 << 20; inc >= 4 << 10; inc >>= 1) {
> + igt_debug("Testing+ %'"PRIu64" B (%'"PRIu64" MiB)\n",
> + *can_mlock, *can_mlock >> 20);
> +
> + igt_fork(child, 1) {
> + for (uint64_t bytes = *can_mlock;
> + bytes <= pin;
> + bytes += inc) {
> + if (mlock(can_mlock, bytes))
> + break;
> +
> + *can_mlock = bytes;
> + __sync_synchronize();
I am just curious, what __sync_synchronize() is doing in this case?
-Ewelina
> + }
> + }
> + __igt_waitchildren();
> + }
> +
> + intel_purge_vm_caches(fd);
> +
> + igt_require(*can_mlock > 64 << 20);
> + *can_mlock -= 64 << 20;
> +
> + igt_debug("Locking %'"PRIu64" B (%'"PRIu64" MiB)\n",
> + *can_mlock, *can_mlock >> 20);
> + igt_assert(!mlock(can_mlock, *can_mlock));
> + igt_info("Locked %'"PRIu64" B (%'"PRIu64" MiB)\n",
> + *can_mlock, *can_mlock >> 20);
> +
> + intel_purge_vm_caches(fd);
> + igt_system_suspend_autoresume(mode, SUSPEND_TEST_NONE);
> +
> + munmap(can_mlock, pin);
> +}
> +
> static void
> test_forcewake(int fd, bool hibernate)
> {
> @@ -199,6 +250,9 @@ igt_main
> igt_subtest("sysfs-reader")
> test_sysfs_reader(false);
>
> + igt_subtest("shrink")
> + test_shrink(fd, SUSPEND_STATE_MEM);
> +
> igt_subtest("forcewake")
> test_forcewake(fd, false);
>
> --
> 2.17.1
>
> _______________________________________________
> igt-dev mailing list
> igt-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/igt-dev
More information about the igt-dev
mailing list