Re: [igt-dev] [Intel-gfx] [PATCH i-g-t v3] tests/gem_shrink: Exercise OOM and other routes to shrinking in reasonable time

Chris Wilson
2019-01-08T10:57:37Z

Quoting Tvrtko Ursulin (2019-01-07 18:22:44)
> From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
>
> A set of subtests which exercises different paths to our shrinker code
> (including the OOM killer) in predictable and reasonable time budget.
>
> v2:
> * Fix blacklist regexp. (Petri Latvala)
>
> v3:
> * Skip userptr test if not userptr support.
>
> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> ---
> lib/igt_core.c | 19 ++
> lib/igt_core.h | 1 +
> tests/i915/gem_shrink.c | 401 ++++++++++++++++++++++++++
> tests/intel-ci/blacklist.txt | 2 +-
> tests/intel-ci/fast-feedback.testlist | 3 +
> 5 files changed, 425 insertions(+), 1 deletion(-)
>
> diff --git a/lib/igt_core.c b/lib/igt_core.c
> index 50d6008f6c82..351da0b4e020 100644
> --- a/lib/igt_core.c
> +++ b/lib/igt_core.c
> @@ -1685,6 +1685,25 @@ void igt_stop_helper(struct igt_helper_process *proc)
> assert(helper_was_alive(proc, status));
> }
>
> +/**
> + * igt_try_stop_helper:
> + * @proc: #igt_helper_process structure
> + *
> + * Terminates a helper process if it is still running and returns true, or false
> + * if the process wasn't running.
> + */
> +bool igt_try_stop_helper(struct igt_helper_process *proc)
> +{
> + int status;
> +
> + /* failure here means the pid is already dead and so waiting is safe */
> + kill(proc->pid, proc->use_SIGKILL ? SIGKILL : SIGTERM);
> +
> + status = igt_wait_helper(proc);
> +
> + return helper_was_alive(proc, status);
> +}
> +
> static void children_exit_handler(int sig)
> {
> int status;
> diff --git a/lib/igt_core.h b/lib/igt_core.h
> index 6f8c3852a686..ed5ceebf1205 100644
> --- a/lib/igt_core.h
> +++ b/lib/igt_core.h
> @@ -795,6 +795,7 @@ bool __igt_fork_helper(struct igt_helper_process *proc);
> for (; __igt_fork_helper(proc); exit(0))
> int igt_wait_helper(struct igt_helper_process *proc);
> void igt_stop_helper(struct igt_helper_process *proc);
> +bool igt_try_stop_helper(struct igt_helper_process *proc);
>
> /* exit handler code */
>
> diff --git a/tests/i915/gem_shrink.c b/tests/i915/gem_shrink.c
> index c8e05814ee70..3d48cbebc2f9 100644
> --- a/tests/i915/gem_shrink.c
> +++ b/tests/i915/gem_shrink.c
> @@ -26,6 +26,10 @@
> *
> * Exercise the shrinker by overallocating GEM objects
> */
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/wait.h>
> +#include <fcntl.h>
>
> #include "igt.h"
> #include "igt_gt.h"
> @@ -366,6 +370,378 @@ static void reclaim(unsigned engine, int timeout)
> close(fd);
> }
>
> +static unsigned long get_meminfo(const char *info, const char *tag)
> +{
> + const char *str;
> + unsigned long val;
> +
> + str = strstr(info, tag);
> + if (str && sscanf(str + strlen(tag), " %lu", &val) == 1)
> + return val >> 10;
> +
> + igt_warn("Unrecognised /proc/meminfo field: '%s'\n", tag);
> + return 0;
> +}
> +
> +static unsigned long get_avail_ram_mb(void)
> +{
> + int fd;
> + int ret;
> + char buf[4096];
> + unsigned long ram;
> +
> + fd = open("/proc/meminfo", O_RDONLY);
> + igt_assert_fd(fd);
> +
> + ret = read(fd, buf, sizeof(buf));
> + igt_assert(ret >= 0);
> +
> + close(fd);
> +
> + ram = get_meminfo(buf, "MemAvailable:");
> + ram += get_meminfo(buf, "Buffers:");
> + ram += get_meminfo(buf, "Cached:");
> + ram += get_meminfo(buf, "SwapCached:");
> +
> + return ram;
> +}
> +
> +struct test {
> +#define TEST_BO (1)
> +#define TEST_USERPTR (2)
> + unsigned int flags;
> + int fd;
> +};
> +
> +static uint32_t __get_pages(int fd, unsigned long alloc)
> +{
> + uint32_t handle = gem_create(fd, alloc);
> +
> + gem_set_domain(fd, handle, I915_GEM_DOMAIN_GTT, 0);

Only needs DOMAIN_CPU for us to call get_pages() and then avoids
excessive clflushing on !llc. (mempressure path, so I'm not sure if you
want to argue about exercising the clflush paths against mempressure.
I'd hope gem_shrink already covers that elsewhere...)

> + gem_madvise(fd, handle, I915_MADV_DONTNEED);
> +
> + return handle;
> +}
> +/*
> + * Use a specific way of using up memory until we are below a certain threshold.
> + */
> +static void *mempressure(void *arg)
> +{
> + const unsigned int free_threshold_mb = 256;
> + struct test_obj *list = NULL;
> + struct test *test = arg;
> + const unsigned int sz_mb = 2;
> + const unsigned int sz = sz_mb << 20;
> + unsigned int n = 0, max = 0;
> + unsigned int blocks;
> +
> + for (;;) {
> + unsigned long ram_mb = get_avail_ram_mb();

...

> + /*
> + * Allocate memory blocks and record the current working set
> + * size.
> + */
> + if (test->flags & TEST_BO) {
> + list[n].handle = __get_pages(test->fd, sz);

Under discussion is that in using gem_create() our bo are accounted by
Cached: in /proc/meminfo. That is we never _appear_ to decrease
avail_ram_mb when for TEST_BO. One hack for drm-tip (since we now mark
shmemfs objects as unevictable) would be to use
ram -= get_meminfo(buf, "Unevictable:");
-Chris