All of lore.kernel.org
 help / color / mirror / Atom feed
From: Petri Latvala <petri.latvala@intel.com>
To: Chris Wilson <chris@chris-wilson.co.uk>
Cc: igt-dev@lists.freedesktop.org, intel-gfx@lists.freedesktop.org
Subject: Re: [igt-dev] [PATCH i-g-t] tests/i915/gem_mmap_offset: Add new API test for gem_mmap_offset
Date: Thu, 28 Nov 2019 15:38:06 +0200	[thread overview]
Message-ID: <20191128133806.GU25209@platvala-desk.ger.corp.intel.com> (raw)
In-Reply-To: <20191128125502.3886404-1-chris@chris-wilson.co.uk>

On Thu, Nov 28, 2019 at 12:55:02PM +0000, Chris Wilson wrote:
> From: Lukasz Kalamarz <lukasz.kalamarz@intel.com>
> 
> Few simple tests which tries to create / mmap buffer objects
> using GEM_MMAP_OFFSET uAPI.
> 
> v2: change from WC -> WB (according to Chris review comment)
> v3: add mmap-offset-close-race test
> 
> Signed-off-by: Lukasz Kalamarz <lukasz.kalamarz@intel.com>
> Signed-off-by: Zbigniew Kempczyński <zbigniew.kempczynski@intel.com>
> Cc: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Vanshidhar Konda <vanshidhar.r.konda@intel.com>
> ---
> Required (look to gem_mmap_(gtt,wc,cpu) for ideas):
>  size - check we can handle objects beyond a reasonable limit (note the
> kernel fails at beyond RAM)
>  forked - interactions with pagefault + new mmap_offsets across
>           threads/processes
>  suspend - check speed is consistent before/after suspend
>  coherency - combinatorial checker between all pointer types, prw and gpu
> ---
>  tests/Makefile.sources       |   3 +
>  tests/i915/gem_mmap_offset.c | 416 +++++++++++++++++++++++++++++++++++
>  tests/meson.build            |   1 +
>  3 files changed, 420 insertions(+)
>  create mode 100644 tests/i915/gem_mmap_offset.c
> 
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> index a211851cf..ddbc69bc8 100644
> --- a/tests/Makefile.sources
> +++ b/tests/Makefile.sources
> @@ -325,6 +325,9 @@ gem_mmap_SOURCES = i915/gem_mmap.c
>  TESTS_progs += gem_mmap_gtt
>  gem_mmap_gtt_SOURCES = i915/gem_mmap_gtt.c
>  
> +TESTS_progs += gem_mmap_offset
> +gem_mmap_offset_SOURCES = i915/gem_mmap_offset.c
> +
>  TESTS_progs += gem_mmap_offset_exhaustion
>  gem_mmap_offset_exhaustion_SOURCES = i915/gem_mmap_offset_exhaustion.c
>  
> diff --git a/tests/i915/gem_mmap_offset.c b/tests/i915/gem_mmap_offset.c
> new file mode 100644
> index 000000000..e75c3fd17
> --- /dev/null
> +++ b/tests/i915/gem_mmap_offset.c
> @@ -0,0 +1,416 @@
> +/*
> + * Copyright © 2019 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#include <errno.h>
> +#include <pthread.h>
> +#include <stdatomic.h>
> +#include <sys/stat.h>
> +#include <sys/ioctl.h>
> +#include "drm.h"
> +
> +#include "igt.h"
> +#include "igt_x86.h"
> +
> +IGT_TEST_DESCRIPTION("Basic MMAP_OFFSET IOCTL tests for mem regions\n");
> +
> +static const struct mmap_offset {
> +	const char *name;
> +	unsigned int type;
> +	unsigned int domain;
> +} mmap_offset_types[] = {
> +	{ "gtt", I915_MMAP_OFFSET_GTT, I915_GEM_DOMAIN_GTT },
> +	{ "wb", I915_MMAP_OFFSET_WB, I915_GEM_DOMAIN_CPU },
> +	{ "wc", I915_MMAP_OFFSET_WC, I915_GEM_DOMAIN_WC },
> +	{ "uc", I915_MMAP_OFFSET_UC, I915_GEM_DOMAIN_WC },
> +	{},
> +};
> +
> +#define for_each_mmap_offset_type(__t) \
> +	for (const struct mmap_offset *__t = mmap_offset_types; \
> +	     (__t)->name; \
> +	     (__t)++)
> +
> +static int mmap_offset_ioctl(int i915, struct drm_i915_gem_mmap_offset *arg)
> +{
> +	int err = 0;
> +
> +	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_MMAP_OFFSET, arg)) {
> +		err = -errno;
> +		igt_assume(err);
> +	}
> +
> +	errno = 0;
> +	return err;
> +}
> +
> +static void bad_object(int i915)
> +{
> +	uint32_t real_handle;
> +	uint32_t handles[20];
> +	int i = 0;
> +
> +	real_handle = gem_create(i915, 4096);
> +
> +	handles[i++] = 0xdeadbeef;
> +	for (int bit = 0; bit < 16; bit++)
> +		handles[i++] = real_handle | (1 << (bit + 16));
> +	handles[i] = real_handle + 1;
> +
> +	for (; i >= 0; i--) {
> +		struct drm_i915_gem_mmap_offset arg = {
> +			.handle = handles[i],
> +			.flags = I915_MMAP_OFFSET_WB,
> +		};
> +
> +		igt_debug("Trying MMAP IOCTL with handle %x\n",
> +			  handles[i]);
> +		igt_assert_eq(mmap_offset_ioctl(i915, &arg),
> +			      -ENOENT);
> +	}
> +
> +	gem_close(i915, real_handle);
> +}
> +
> +static void bad_flags(int i915)
> +{
> +	struct drm_i915_gem_mmap_offset arg = {
> +		.handle = gem_create(i915, 4096),
> +		.flags = -1ull,
> +	};
> +
> +	igt_assert_eq(mmap_offset_ioctl(i915, &arg), -EINVAL);
> +	gem_close(i915, arg.handle);
> +}
> +
> +static void basic_uaf(int i915)
> +{
> +	const uint32_t obj_size = 4096;
> +
> +	for_each_mmap_offset_type(t) {
> +		uint32_t handle = gem_create(i915, obj_size);
> +		uint8_t *expected, *buf, *addr;
> +
> +		addr = __gem_mmap_offset(i915, handle, 0, obj_size,
> +					 PROT_READ | PROT_WRITE,
> +					 t->type);
> +		if (!addr) {
> +			gem_close(i915, handle);
> +			continue;
> +		}
> +
> +		expected = calloc(obj_size, sizeof(*expected));
> +		gem_set_domain(i915, handle, t->domain, 0);
> +		igt_assert_f(memcmp(addr, expected, obj_size) == 0,
> +			     "mmap(%s) not clear on gem_create()\n",
> +			     t->name);
> +		free(expected);
> +
> +		buf = calloc(obj_size, sizeof(*buf));
> +		memset(buf + 1024, 0x01, 1024);
> +		gem_write(i915, handle, 0, buf, obj_size);
> +		gem_set_domain(i915, handle, t->domain, 0);
> +		igt_assert_f(memcmp(buf, addr, obj_size) == 0,
> +			     "mmap(%s) not coherent with gem_write()\n",
> +			     t->name);
> +
> +		gem_set_domain(i915, handle, t->domain, t->domain);
> +		memset(addr + 2048, 0xff, 1024);
> +		gem_read(i915, handle, 0, buf, obj_size);
> +		gem_set_domain(i915, handle, t->domain, 0);
> +		igt_assert_f(memcmp(buf, addr, obj_size) == 0,
> +			     "mmap(%s) not coherent with gem_read()\n",
> +			     t->name);
> +
> +		gem_close(i915, handle);
> +		igt_assert_f(memcmp(buf, addr, obj_size) == 0,
> +			     "mmap(%s) not resident after gem_close()\n",
> +			     t->name);
> +		free(buf);
> +
> +		igt_debug("Testing unmapping\n");
> +		munmap(addr, obj_size);
> +	}
> +}
> +
> +static void isolation(int i915)
> +{
> +	for_each_mmap_offset_type(t) {
> +		struct drm_i915_gem_mmap_offset mmap_arg = {
> +			.flags = t->type
> +		};
> +		int A = gem_reopen_driver(i915);
> +		int B = gem_reopen_driver(i915);
> +		uint64_t offset_a, offset_b;
> +		uint32_t a, b;
> +		void *ptr;
> +
> +		a = gem_create(A, 4096);
> +		b = gem_open(B, gem_flink(A, a));
> +
> +		mmap_arg.handle = a;
> +		igt_assert_eq(mmap_offset_ioctl(i915, &mmap_arg), 0);
> +		offset_a = mmap_arg.offset;
> +
> +		mmap_arg.handle = b;
> +		igt_assert_eq(mmap_offset_ioctl(i915, &mmap_arg), 0);
> +		offset_b = mmap_arg.offset;
> +
> +		igt_info("A[%s]: {fd:%d, handle:%d, offset:%"PRIx64"}\n",
> +			 t->name, A, a, offset_a);
> +		igt_info("B[%s]: {fd:%d, handle:%d, offset:%"PRIx64"}\n",
> +			 t->name, B, b, offset_b);
> +
> +		close(B);
> +
> +		ptr = mmap64(0, 4096, PROT_READ, MAP_SHARED, A, offset_a);
> +		igt_assert(ptr != MAP_FAILED);
> +		munmap(ptr, 4096);
> +
> +		close(A);
> +
> +		ptr = mmap64(0, 4096, PROT_READ, MAP_SHARED, A, offset_a);
> +		igt_assert(ptr == MAP_FAILED);
> +	}
> +}
> +
> +static void pf_nonblock(int i915)
> +{
> +	igt_spin_t *spin = igt_spin_new(i915);
> +
> +	for_each_mmap_offset_type(t) {
> +		uint32_t *ptr;
> +
> +		ptr = __gem_mmap_offset(i915, spin->handle, 0, 4096,
> +					PROT_READ | PROT_WRITE,
> +					t->type);
> +		if (!ptr)
> +			continue;
> +
> +		igt_set_timeout(1, t->name);
> +		/* no set-domain as we want to verify the pagefault is async */
> +		ptr[256] = 0;
> +		igt_reset_timeout();
> +
> +		munmap(ptr, 4096);
> +	}
> +
> +	igt_spin_free(i915, spin);
> +}
> +
> +static void close_race(int i915, int timeout)
> +{
> +	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
> +	_Atomic uint32_t *handles;
> +	size_t len = ALIGN((ncpus + 1) * sizeof(uint32_t), 4096);
> +
> +	handles = mmap64(0, len, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
> +	igt_assert(handles != MAP_FAILED);
> +
> +	igt_fork(child, ncpus + 1) {
> +		do {
> +			struct drm_i915_gem_mmap_offset mmap_arg = {};
> +			const int i = 1 + random() % ncpus;
> +			uint32_t old;
> +
> +			mmap_arg.handle = gem_create(i915, 4096);
> +			mmap_arg.flags = I915_MMAP_OFFSET_WB;
> +			old = atomic_exchange(&handles[i], mmap_arg.handle);

This will require adding libatomic to this executable's dependencies
with meson. Look to handling of gem_create.c for an example.


-- 
Petri Latvala




> +			ioctl(i915, DRM_IOCTL_GEM_CLOSE, &old);
> +
> +			if (ioctl(i915,
> +				  DRM_IOCTL_I915_GEM_MMAP_OFFSET,
> +				  &mmap_arg) != -1) {
> +				void *ptr;
> +
> +				ptr = mmap64(0, 4096,
> +					     PROT_WRITE, MAP_SHARED, i915,
> +					     mmap_arg.offset);
> +				if (ptr != MAP_FAILED) {
> +					*(volatile uint32_t *)ptr = 0;
> +					munmap(ptr, 4096);
> +				}
> +			}
> +
> +		} while (!READ_ONCE(handles[0]));
> +	}
> +
> +	sleep(timeout);
> +	handles[0] = 1;
> +	igt_waitchildren();
> +
> +	for (int i = 1; i <= ncpus; i++)
> +		ioctl(i915, DRM_IOCTL_GEM_CLOSE, handles[i]);
> +	munmap(handles, len);
> +}
> +
> +static uint64_t atomic_compare_swap_u64(_Atomic(uint64_t) *ptr,
> +					uint64_t oldval, uint64_t newval)
> +{
> +	atomic_compare_exchange_strong(ptr, &oldval, newval);
> +	return oldval;
> +}
> +
> +static uint64_t get_npages(_Atomic(uint64_t) *global, uint64_t npages)
> +{
> +	uint64_t try, old, max;
> +
> +	max = *global;
> +	do {
> +		old = max;
> +		try = 1 + npages % (max / 2);
> +		max -= try;
> +	} while ((max = atomic_compare_swap_u64(global, old, max)) != old);
> +
> +	return try;
> +}
> +
> +struct thread_clear {
> +	_Atomic(uint64_t) max;
> +	int timeout;
> +	int i915;
> +};
> +
> +static int create_ioctl(int i915, struct drm_i915_gem_create *create)
> +{
> +	int err = 0;
> +
> +	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_CREATE, create)) {
> +		err = -errno;
> +		igt_assume(err != 0);
> +	}
> +
> +	errno = 0;
> +	return err;
> +}
> +
> +static void *thread_clear(void *data)
> +{
> +	struct thread_clear *arg = data;
> +	const struct mmap_offset *t;
> +	unsigned long checked = 0;
> +	int i915 = arg->i915;
> +
> +	t = mmap_offset_types;
> +	igt_until_timeout(arg->timeout) {
> +		struct drm_i915_gem_create create = {};
> +		uint64_t npages;
> +		void *ptr;
> +
> +		npages = random();
> +		npages <<= 32;
> +		npages |= random();
> +		npages = get_npages(&arg->max, npages);
> +		create.size = npages << 12;
> +
> +		create_ioctl(i915, &create);
> +		ptr = __gem_mmap_offset(i915, create.handle, 0, create.size,
> +					PROT_READ | PROT_WRITE,
> +					t->type);
> +		/* No set-domains as we are being as naughty as possible */
> +		for (uint64_t page = 0; ptr && page < npages; page++) {
> +			uint64_t x[8] = {
> +				page * 4096 +
> +					sizeof(x) * ((page % (4096 - sizeof(x)) / sizeof(x)))
> +			};
> +
> +			if (page & 1)
> +				igt_memcpy_from_wc(x, ptr + x[0], sizeof(x));
> +			else
> +				memcpy(x, ptr + x[0], sizeof(x));
> +
> +			for (int i = 0; i < ARRAY_SIZE(x); i++)
> +				igt_assert_eq_u64(x[i], 0);
> +		}
> +		if (ptr)
> +			munmap(ptr, create.size);
> +		gem_close(i915, create.handle);
> +		checked += npages;
> +
> +		atomic_fetch_add(&arg->max, npages);
> +
> +		if (!(++t)->name)
> +			t = mmap_offset_types;
> +	}
> +
> +	return (void *)(uintptr_t)checked;
> +}
> +
> +static void always_clear(int i915, int timeout)
> +{
> +	struct thread_clear arg = {
> +		.i915 = i915,
> +		.timeout = timeout,
> +		.max = intel_get_avail_ram_mb() << (20 - 12), /* in pages */
> +	};
> +	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
> +	unsigned long checked;
> +	pthread_t thread[ncpus];
> +	void *result;
> +
> +	for (int i = 0; i < ncpus; i++)
> +		pthread_create(&thread[i], NULL, thread_clear, &arg);
> +
> +	checked = 0;
> +	for (int i = 0; i < ncpus; i++) {
> +		pthread_join(thread[i], &result);
> +		checked += (uintptr_t)result;
> +	}
> +	igt_info("Checked %'lu page allocations\n", checked);
> +}
> +
> +
> +igt_main
> +{
> +	int i915;
> +
> +	igt_fixture {
> +		i915 = drm_open_driver(DRIVER_INTEL);
> +		gem_require_mmap_offset(i915);
> +	}
> +
> +	igt_describe("Verify mapping to invalid gem objects won't be created");
> +	igt_subtest_f("bad-object")
> +		bad_object(i915);
> +	igt_subtest_f("bad-flags")
> +		bad_flags(i915);
> +
> +	igt_describe("Check buffer object mapping persists after gem_close");
> +	igt_subtest_f("basic-uaf")
> +		basic_uaf(i915);
> +
> +	igt_subtest_f("isolation")
> +		isolation(i915);
> +	igt_subtest_f("pf-nonblock")
> +		pf_nonblock(i915);
> +
> +	igt_describe("Check race between close and mmap offset between threads");
> +	igt_subtest_f("close-race")
> +		close_race(i915, 20);
> +
> +	igt_subtest_f("clear")
> +		always_clear(i915, 20);
> +
> +	igt_fixture {
> +		close(i915);
> +	}
> +}
> diff --git a/tests/meson.build b/tests/meson.build
> index 18788d44b..05dea1f4e 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -174,6 +174,7 @@ i915_progs = [
>  	'gem_media_vme',
>  	'gem_mmap',
>  	'gem_mmap_gtt',
> +	'gem_mmap_offset',
>  	'gem_mmap_offset_exhaustion',
>  	'gem_mmap_wc',
>  	'gem_partial_pwrite_pread',
> -- 
> 2.24.0
> 
> _______________________________________________
> igt-dev mailing list
> igt-dev@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/igt-dev
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

WARNING: multiple messages have this Message-ID (diff)
From: Petri Latvala <petri.latvala@intel.com>
To: Chris Wilson <chris@chris-wilson.co.uk>
Cc: igt-dev@lists.freedesktop.org, intel-gfx@lists.freedesktop.org
Subject: Re: [Intel-gfx] [igt-dev] [PATCH i-g-t] tests/i915/gem_mmap_offset: Add new API test for gem_mmap_offset
Date: Thu, 28 Nov 2019 15:38:06 +0200	[thread overview]
Message-ID: <20191128133806.GU25209@platvala-desk.ger.corp.intel.com> (raw)
Message-ID: <20191128133806.zysVnRBVCdlq0qZ9zw7iTSgd2-LejT1T8qXSvIZvjXI@z> (raw)
In-Reply-To: <20191128125502.3886404-1-chris@chris-wilson.co.uk>

On Thu, Nov 28, 2019 at 12:55:02PM +0000, Chris Wilson wrote:
> From: Lukasz Kalamarz <lukasz.kalamarz@intel.com>
> 
> Few simple tests which tries to create / mmap buffer objects
> using GEM_MMAP_OFFSET uAPI.
> 
> v2: change from WC -> WB (according to Chris review comment)
> v3: add mmap-offset-close-race test
> 
> Signed-off-by: Lukasz Kalamarz <lukasz.kalamarz@intel.com>
> Signed-off-by: Zbigniew Kempczyński <zbigniew.kempczynski@intel.com>
> Cc: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Vanshidhar Konda <vanshidhar.r.konda@intel.com>
> ---
> Required (look to gem_mmap_(gtt,wc,cpu) for ideas):
>  size - check we can handle objects beyond a reasonable limit (note the
> kernel fails at beyond RAM)
>  forked - interactions with pagefault + new mmap_offsets across
>           threads/processes
>  suspend - check speed is consistent before/after suspend
>  coherency - combinatorial checker between all pointer types, prw and gpu
> ---
>  tests/Makefile.sources       |   3 +
>  tests/i915/gem_mmap_offset.c | 416 +++++++++++++++++++++++++++++++++++
>  tests/meson.build            |   1 +
>  3 files changed, 420 insertions(+)
>  create mode 100644 tests/i915/gem_mmap_offset.c
> 
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> index a211851cf..ddbc69bc8 100644
> --- a/tests/Makefile.sources
> +++ b/tests/Makefile.sources
> @@ -325,6 +325,9 @@ gem_mmap_SOURCES = i915/gem_mmap.c
>  TESTS_progs += gem_mmap_gtt
>  gem_mmap_gtt_SOURCES = i915/gem_mmap_gtt.c
>  
> +TESTS_progs += gem_mmap_offset
> +gem_mmap_offset_SOURCES = i915/gem_mmap_offset.c
> +
>  TESTS_progs += gem_mmap_offset_exhaustion
>  gem_mmap_offset_exhaustion_SOURCES = i915/gem_mmap_offset_exhaustion.c
>  
> diff --git a/tests/i915/gem_mmap_offset.c b/tests/i915/gem_mmap_offset.c
> new file mode 100644
> index 000000000..e75c3fd17
> --- /dev/null
> +++ b/tests/i915/gem_mmap_offset.c
> @@ -0,0 +1,416 @@
> +/*
> + * Copyright © 2019 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#include <errno.h>
> +#include <pthread.h>
> +#include <stdatomic.h>
> +#include <sys/stat.h>
> +#include <sys/ioctl.h>
> +#include "drm.h"
> +
> +#include "igt.h"
> +#include "igt_x86.h"
> +
> +IGT_TEST_DESCRIPTION("Basic MMAP_OFFSET IOCTL tests for mem regions\n");
> +
> +static const struct mmap_offset {
> +	const char *name;
> +	unsigned int type;
> +	unsigned int domain;
> +} mmap_offset_types[] = {
> +	{ "gtt", I915_MMAP_OFFSET_GTT, I915_GEM_DOMAIN_GTT },
> +	{ "wb", I915_MMAP_OFFSET_WB, I915_GEM_DOMAIN_CPU },
> +	{ "wc", I915_MMAP_OFFSET_WC, I915_GEM_DOMAIN_WC },
> +	{ "uc", I915_MMAP_OFFSET_UC, I915_GEM_DOMAIN_WC },
> +	{},
> +};
> +
> +#define for_each_mmap_offset_type(__t) \
> +	for (const struct mmap_offset *__t = mmap_offset_types; \
> +	     (__t)->name; \
> +	     (__t)++)
> +
> +static int mmap_offset_ioctl(int i915, struct drm_i915_gem_mmap_offset *arg)
> +{
> +	int err = 0;
> +
> +	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_MMAP_OFFSET, arg)) {
> +		err = -errno;
> +		igt_assume(err);
> +	}
> +
> +	errno = 0;
> +	return err;
> +}
> +
> +static void bad_object(int i915)
> +{
> +	uint32_t real_handle;
> +	uint32_t handles[20];
> +	int i = 0;
> +
> +	real_handle = gem_create(i915, 4096);
> +
> +	handles[i++] = 0xdeadbeef;
> +	for (int bit = 0; bit < 16; bit++)
> +		handles[i++] = real_handle | (1 << (bit + 16));
> +	handles[i] = real_handle + 1;
> +
> +	for (; i >= 0; i--) {
> +		struct drm_i915_gem_mmap_offset arg = {
> +			.handle = handles[i],
> +			.flags = I915_MMAP_OFFSET_WB,
> +		};
> +
> +		igt_debug("Trying MMAP IOCTL with handle %x\n",
> +			  handles[i]);
> +		igt_assert_eq(mmap_offset_ioctl(i915, &arg),
> +			      -ENOENT);
> +	}
> +
> +	gem_close(i915, real_handle);
> +}
> +
> +static void bad_flags(int i915)
> +{
> +	struct drm_i915_gem_mmap_offset arg = {
> +		.handle = gem_create(i915, 4096),
> +		.flags = -1ull,
> +	};
> +
> +	igt_assert_eq(mmap_offset_ioctl(i915, &arg), -EINVAL);
> +	gem_close(i915, arg.handle);
> +}
> +
> +static void basic_uaf(int i915)
> +{
> +	const uint32_t obj_size = 4096;
> +
> +	for_each_mmap_offset_type(t) {
> +		uint32_t handle = gem_create(i915, obj_size);
> +		uint8_t *expected, *buf, *addr;
> +
> +		addr = __gem_mmap_offset(i915, handle, 0, obj_size,
> +					 PROT_READ | PROT_WRITE,
> +					 t->type);
> +		if (!addr) {
> +			gem_close(i915, handle);
> +			continue;
> +		}
> +
> +		expected = calloc(obj_size, sizeof(*expected));
> +		gem_set_domain(i915, handle, t->domain, 0);
> +		igt_assert_f(memcmp(addr, expected, obj_size) == 0,
> +			     "mmap(%s) not clear on gem_create()\n",
> +			     t->name);
> +		free(expected);
> +
> +		buf = calloc(obj_size, sizeof(*buf));
> +		memset(buf + 1024, 0x01, 1024);
> +		gem_write(i915, handle, 0, buf, obj_size);
> +		gem_set_domain(i915, handle, t->domain, 0);
> +		igt_assert_f(memcmp(buf, addr, obj_size) == 0,
> +			     "mmap(%s) not coherent with gem_write()\n",
> +			     t->name);
> +
> +		gem_set_domain(i915, handle, t->domain, t->domain);
> +		memset(addr + 2048, 0xff, 1024);
> +		gem_read(i915, handle, 0, buf, obj_size);
> +		gem_set_domain(i915, handle, t->domain, 0);
> +		igt_assert_f(memcmp(buf, addr, obj_size) == 0,
> +			     "mmap(%s) not coherent with gem_read()\n",
> +			     t->name);
> +
> +		gem_close(i915, handle);
> +		igt_assert_f(memcmp(buf, addr, obj_size) == 0,
> +			     "mmap(%s) not resident after gem_close()\n",
> +			     t->name);
> +		free(buf);
> +
> +		igt_debug("Testing unmapping\n");
> +		munmap(addr, obj_size);
> +	}
> +}
> +
> +static void isolation(int i915)
> +{
> +	for_each_mmap_offset_type(t) {
> +		struct drm_i915_gem_mmap_offset mmap_arg = {
> +			.flags = t->type
> +		};
> +		int A = gem_reopen_driver(i915);
> +		int B = gem_reopen_driver(i915);
> +		uint64_t offset_a, offset_b;
> +		uint32_t a, b;
> +		void *ptr;
> +
> +		a = gem_create(A, 4096);
> +		b = gem_open(B, gem_flink(A, a));
> +
> +		mmap_arg.handle = a;
> +		igt_assert_eq(mmap_offset_ioctl(i915, &mmap_arg), 0);
> +		offset_a = mmap_arg.offset;
> +
> +		mmap_arg.handle = b;
> +		igt_assert_eq(mmap_offset_ioctl(i915, &mmap_arg), 0);
> +		offset_b = mmap_arg.offset;
> +
> +		igt_info("A[%s]: {fd:%d, handle:%d, offset:%"PRIx64"}\n",
> +			 t->name, A, a, offset_a);
> +		igt_info("B[%s]: {fd:%d, handle:%d, offset:%"PRIx64"}\n",
> +			 t->name, B, b, offset_b);
> +
> +		close(B);
> +
> +		ptr = mmap64(0, 4096, PROT_READ, MAP_SHARED, A, offset_a);
> +		igt_assert(ptr != MAP_FAILED);
> +		munmap(ptr, 4096);
> +
> +		close(A);
> +
> +		ptr = mmap64(0, 4096, PROT_READ, MAP_SHARED, A, offset_a);
> +		igt_assert(ptr == MAP_FAILED);
> +	}
> +}
> +
> +static void pf_nonblock(int i915)
> +{
> +	igt_spin_t *spin = igt_spin_new(i915);
> +
> +	for_each_mmap_offset_type(t) {
> +		uint32_t *ptr;
> +
> +		ptr = __gem_mmap_offset(i915, spin->handle, 0, 4096,
> +					PROT_READ | PROT_WRITE,
> +					t->type);
> +		if (!ptr)
> +			continue;
> +
> +		igt_set_timeout(1, t->name);
> +		/* no set-domain as we want to verify the pagefault is async */
> +		ptr[256] = 0;
> +		igt_reset_timeout();
> +
> +		munmap(ptr, 4096);
> +	}
> +
> +	igt_spin_free(i915, spin);
> +}
> +
> +static void close_race(int i915, int timeout)
> +{
> +	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
> +	_Atomic uint32_t *handles;
> +	size_t len = ALIGN((ncpus + 1) * sizeof(uint32_t), 4096);
> +
> +	handles = mmap64(0, len, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
> +	igt_assert(handles != MAP_FAILED);
> +
> +	igt_fork(child, ncpus + 1) {
> +		do {
> +			struct drm_i915_gem_mmap_offset mmap_arg = {};
> +			const int i = 1 + random() % ncpus;
> +			uint32_t old;
> +
> +			mmap_arg.handle = gem_create(i915, 4096);
> +			mmap_arg.flags = I915_MMAP_OFFSET_WB;
> +			old = atomic_exchange(&handles[i], mmap_arg.handle);

This will require adding libatomic to this executable's dependencies
with meson. Look to handling of gem_create.c for an example.


-- 
Petri Latvala




> +			ioctl(i915, DRM_IOCTL_GEM_CLOSE, &old);
> +
> +			if (ioctl(i915,
> +				  DRM_IOCTL_I915_GEM_MMAP_OFFSET,
> +				  &mmap_arg) != -1) {
> +				void *ptr;
> +
> +				ptr = mmap64(0, 4096,
> +					     PROT_WRITE, MAP_SHARED, i915,
> +					     mmap_arg.offset);
> +				if (ptr != MAP_FAILED) {
> +					*(volatile uint32_t *)ptr = 0;
> +					munmap(ptr, 4096);
> +				}
> +			}
> +
> +		} while (!READ_ONCE(handles[0]));
> +	}
> +
> +	sleep(timeout);
> +	handles[0] = 1;
> +	igt_waitchildren();
> +
> +	for (int i = 1; i <= ncpus; i++)
> +		ioctl(i915, DRM_IOCTL_GEM_CLOSE, handles[i]);
> +	munmap(handles, len);
> +}
> +
> +static uint64_t atomic_compare_swap_u64(_Atomic(uint64_t) *ptr,
> +					uint64_t oldval, uint64_t newval)
> +{
> +	atomic_compare_exchange_strong(ptr, &oldval, newval);
> +	return oldval;
> +}
> +
> +static uint64_t get_npages(_Atomic(uint64_t) *global, uint64_t npages)
> +{
> +	uint64_t try, old, max;
> +
> +	max = *global;
> +	do {
> +		old = max;
> +		try = 1 + npages % (max / 2);
> +		max -= try;
> +	} while ((max = atomic_compare_swap_u64(global, old, max)) != old);
> +
> +	return try;
> +}
> +
> +struct thread_clear {
> +	_Atomic(uint64_t) max;
> +	int timeout;
> +	int i915;
> +};
> +
> +static int create_ioctl(int i915, struct drm_i915_gem_create *create)
> +{
> +	int err = 0;
> +
> +	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_CREATE, create)) {
> +		err = -errno;
> +		igt_assume(err != 0);
> +	}
> +
> +	errno = 0;
> +	return err;
> +}
> +
> +static void *thread_clear(void *data)
> +{
> +	struct thread_clear *arg = data;
> +	const struct mmap_offset *t;
> +	unsigned long checked = 0;
> +	int i915 = arg->i915;
> +
> +	t = mmap_offset_types;
> +	igt_until_timeout(arg->timeout) {
> +		struct drm_i915_gem_create create = {};
> +		uint64_t npages;
> +		void *ptr;
> +
> +		npages = random();
> +		npages <<= 32;
> +		npages |= random();
> +		npages = get_npages(&arg->max, npages);
> +		create.size = npages << 12;
> +
> +		create_ioctl(i915, &create);
> +		ptr = __gem_mmap_offset(i915, create.handle, 0, create.size,
> +					PROT_READ | PROT_WRITE,
> +					t->type);
> +		/* No set-domains as we are being as naughty as possible */
> +		for (uint64_t page = 0; ptr && page < npages; page++) {
> +			uint64_t x[8] = {
> +				page * 4096 +
> +					sizeof(x) * ((page % (4096 - sizeof(x)) / sizeof(x)))
> +			};
> +
> +			if (page & 1)
> +				igt_memcpy_from_wc(x, ptr + x[0], sizeof(x));
> +			else
> +				memcpy(x, ptr + x[0], sizeof(x));
> +
> +			for (int i = 0; i < ARRAY_SIZE(x); i++)
> +				igt_assert_eq_u64(x[i], 0);
> +		}
> +		if (ptr)
> +			munmap(ptr, create.size);
> +		gem_close(i915, create.handle);
> +		checked += npages;
> +
> +		atomic_fetch_add(&arg->max, npages);
> +
> +		if (!(++t)->name)
> +			t = mmap_offset_types;
> +	}
> +
> +	return (void *)(uintptr_t)checked;
> +}
> +
> +static void always_clear(int i915, int timeout)
> +{
> +	struct thread_clear arg = {
> +		.i915 = i915,
> +		.timeout = timeout,
> +		.max = intel_get_avail_ram_mb() << (20 - 12), /* in pages */
> +	};
> +	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
> +	unsigned long checked;
> +	pthread_t thread[ncpus];
> +	void *result;
> +
> +	for (int i = 0; i < ncpus; i++)
> +		pthread_create(&thread[i], NULL, thread_clear, &arg);
> +
> +	checked = 0;
> +	for (int i = 0; i < ncpus; i++) {
> +		pthread_join(thread[i], &result);
> +		checked += (uintptr_t)result;
> +	}
> +	igt_info("Checked %'lu page allocations\n", checked);
> +}
> +
> +
> +igt_main
> +{
> +	int i915;
> +
> +	igt_fixture {
> +		i915 = drm_open_driver(DRIVER_INTEL);
> +		gem_require_mmap_offset(i915);
> +	}
> +
> +	igt_describe("Verify mapping to invalid gem objects won't be created");
> +	igt_subtest_f("bad-object")
> +		bad_object(i915);
> +	igt_subtest_f("bad-flags")
> +		bad_flags(i915);
> +
> +	igt_describe("Check buffer object mapping persists after gem_close");
> +	igt_subtest_f("basic-uaf")
> +		basic_uaf(i915);
> +
> +	igt_subtest_f("isolation")
> +		isolation(i915);
> +	igt_subtest_f("pf-nonblock")
> +		pf_nonblock(i915);
> +
> +	igt_describe("Check race between close and mmap offset between threads");
> +	igt_subtest_f("close-race")
> +		close_race(i915, 20);
> +
> +	igt_subtest_f("clear")
> +		always_clear(i915, 20);
> +
> +	igt_fixture {
> +		close(i915);
> +	}
> +}
> diff --git a/tests/meson.build b/tests/meson.build
> index 18788d44b..05dea1f4e 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -174,6 +174,7 @@ i915_progs = [
>  	'gem_media_vme',
>  	'gem_mmap',
>  	'gem_mmap_gtt',
> +	'gem_mmap_offset',
>  	'gem_mmap_offset_exhaustion',
>  	'gem_mmap_wc',
>  	'gem_partial_pwrite_pread',
> -- 
> 2.24.0
> 
> _______________________________________________
> igt-dev mailing list
> igt-dev@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/igt-dev
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

WARNING: multiple messages have this Message-ID (diff)
From: Petri Latvala <petri.latvala@intel.com>
To: Chris Wilson <chris@chris-wilson.co.uk>
Cc: igt-dev@lists.freedesktop.org, intel-gfx@lists.freedesktop.org
Subject: Re: [igt-dev] [PATCH i-g-t] tests/i915/gem_mmap_offset: Add new API test for gem_mmap_offset
Date: Thu, 28 Nov 2019 15:38:06 +0200	[thread overview]
Message-ID: <20191128133806.GU25209@platvala-desk.ger.corp.intel.com> (raw)
In-Reply-To: <20191128125502.3886404-1-chris@chris-wilson.co.uk>

On Thu, Nov 28, 2019 at 12:55:02PM +0000, Chris Wilson wrote:
> From: Lukasz Kalamarz <lukasz.kalamarz@intel.com>
> 
> Few simple tests which tries to create / mmap buffer objects
> using GEM_MMAP_OFFSET uAPI.
> 
> v2: change from WC -> WB (according to Chris review comment)
> v3: add mmap-offset-close-race test
> 
> Signed-off-by: Lukasz Kalamarz <lukasz.kalamarz@intel.com>
> Signed-off-by: Zbigniew Kempczyński <zbigniew.kempczynski@intel.com>
> Cc: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Vanshidhar Konda <vanshidhar.r.konda@intel.com>
> ---
> Required (look to gem_mmap_(gtt,wc,cpu) for ideas):
>  size - check we can handle objects beyond a reasonable limit (note the
> kernel fails at beyond RAM)
>  forked - interactions with pagefault + new mmap_offsets across
>           threads/processes
>  suspend - check speed is consistent before/after suspend
>  coherency - combinatorial checker between all pointer types, prw and gpu
> ---
>  tests/Makefile.sources       |   3 +
>  tests/i915/gem_mmap_offset.c | 416 +++++++++++++++++++++++++++++++++++
>  tests/meson.build            |   1 +
>  3 files changed, 420 insertions(+)
>  create mode 100644 tests/i915/gem_mmap_offset.c
> 
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> index a211851cf..ddbc69bc8 100644
> --- a/tests/Makefile.sources
> +++ b/tests/Makefile.sources
> @@ -325,6 +325,9 @@ gem_mmap_SOURCES = i915/gem_mmap.c
>  TESTS_progs += gem_mmap_gtt
>  gem_mmap_gtt_SOURCES = i915/gem_mmap_gtt.c
>  
> +TESTS_progs += gem_mmap_offset
> +gem_mmap_offset_SOURCES = i915/gem_mmap_offset.c
> +
>  TESTS_progs += gem_mmap_offset_exhaustion
>  gem_mmap_offset_exhaustion_SOURCES = i915/gem_mmap_offset_exhaustion.c
>  
> diff --git a/tests/i915/gem_mmap_offset.c b/tests/i915/gem_mmap_offset.c
> new file mode 100644
> index 000000000..e75c3fd17
> --- /dev/null
> +++ b/tests/i915/gem_mmap_offset.c
> @@ -0,0 +1,416 @@
> +/*
> + * Copyright © 2019 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#include <errno.h>
> +#include <pthread.h>
> +#include <stdatomic.h>
> +#include <sys/stat.h>
> +#include <sys/ioctl.h>
> +#include "drm.h"
> +
> +#include "igt.h"
> +#include "igt_x86.h"
> +
> +IGT_TEST_DESCRIPTION("Basic MMAP_OFFSET IOCTL tests for mem regions\n");
> +
> +static const struct mmap_offset {
> +	const char *name;
> +	unsigned int type;
> +	unsigned int domain;
> +} mmap_offset_types[] = {
> +	{ "gtt", I915_MMAP_OFFSET_GTT, I915_GEM_DOMAIN_GTT },
> +	{ "wb", I915_MMAP_OFFSET_WB, I915_GEM_DOMAIN_CPU },
> +	{ "wc", I915_MMAP_OFFSET_WC, I915_GEM_DOMAIN_WC },
> +	{ "uc", I915_MMAP_OFFSET_UC, I915_GEM_DOMAIN_WC },
> +	{},
> +};
> +
> +#define for_each_mmap_offset_type(__t) \
> +	for (const struct mmap_offset *__t = mmap_offset_types; \
> +	     (__t)->name; \
> +	     (__t)++)
> +
> +static int mmap_offset_ioctl(int i915, struct drm_i915_gem_mmap_offset *arg)
> +{
> +	int err = 0;
> +
> +	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_MMAP_OFFSET, arg)) {
> +		err = -errno;
> +		igt_assume(err);
> +	}
> +
> +	errno = 0;
> +	return err;
> +}
> +
> +static void bad_object(int i915)
> +{
> +	uint32_t real_handle;
> +	uint32_t handles[20];
> +	int i = 0;
> +
> +	real_handle = gem_create(i915, 4096);
> +
> +	handles[i++] = 0xdeadbeef;
> +	for (int bit = 0; bit < 16; bit++)
> +		handles[i++] = real_handle | (1 << (bit + 16));
> +	handles[i] = real_handle + 1;
> +
> +	for (; i >= 0; i--) {
> +		struct drm_i915_gem_mmap_offset arg = {
> +			.handle = handles[i],
> +			.flags = I915_MMAP_OFFSET_WB,
> +		};
> +
> +		igt_debug("Trying MMAP IOCTL with handle %x\n",
> +			  handles[i]);
> +		igt_assert_eq(mmap_offset_ioctl(i915, &arg),
> +			      -ENOENT);
> +	}
> +
> +	gem_close(i915, real_handle);
> +}
> +
> +static void bad_flags(int i915)
> +{
> +	struct drm_i915_gem_mmap_offset arg = {
> +		.handle = gem_create(i915, 4096),
> +		.flags = -1ull,
> +	};
> +
> +	igt_assert_eq(mmap_offset_ioctl(i915, &arg), -EINVAL);
> +	gem_close(i915, arg.handle);
> +}
> +
> +static void basic_uaf(int i915)
> +{
> +	const uint32_t obj_size = 4096;
> +
> +	for_each_mmap_offset_type(t) {
> +		uint32_t handle = gem_create(i915, obj_size);
> +		uint8_t *expected, *buf, *addr;
> +
> +		addr = __gem_mmap_offset(i915, handle, 0, obj_size,
> +					 PROT_READ | PROT_WRITE,
> +					 t->type);
> +		if (!addr) {
> +			gem_close(i915, handle);
> +			continue;
> +		}
> +
> +		expected = calloc(obj_size, sizeof(*expected));
> +		gem_set_domain(i915, handle, t->domain, 0);
> +		igt_assert_f(memcmp(addr, expected, obj_size) == 0,
> +			     "mmap(%s) not clear on gem_create()\n",
> +			     t->name);
> +		free(expected);
> +
> +		buf = calloc(obj_size, sizeof(*buf));
> +		memset(buf + 1024, 0x01, 1024);
> +		gem_write(i915, handle, 0, buf, obj_size);
> +		gem_set_domain(i915, handle, t->domain, 0);
> +		igt_assert_f(memcmp(buf, addr, obj_size) == 0,
> +			     "mmap(%s) not coherent with gem_write()\n",
> +			     t->name);
> +
> +		gem_set_domain(i915, handle, t->domain, t->domain);
> +		memset(addr + 2048, 0xff, 1024);
> +		gem_read(i915, handle, 0, buf, obj_size);
> +		gem_set_domain(i915, handle, t->domain, 0);
> +		igt_assert_f(memcmp(buf, addr, obj_size) == 0,
> +			     "mmap(%s) not coherent with gem_read()\n",
> +			     t->name);
> +
> +		gem_close(i915, handle);
> +		igt_assert_f(memcmp(buf, addr, obj_size) == 0,
> +			     "mmap(%s) not resident after gem_close()\n",
> +			     t->name);
> +		free(buf);
> +
> +		igt_debug("Testing unmapping\n");
> +		munmap(addr, obj_size);
> +	}
> +}
> +
> +static void isolation(int i915)
> +{
> +	for_each_mmap_offset_type(t) {
> +		struct drm_i915_gem_mmap_offset mmap_arg = {
> +			.flags = t->type
> +		};
> +		int A = gem_reopen_driver(i915);
> +		int B = gem_reopen_driver(i915);
> +		uint64_t offset_a, offset_b;
> +		uint32_t a, b;
> +		void *ptr;
> +
> +		a = gem_create(A, 4096);
> +		b = gem_open(B, gem_flink(A, a));
> +
> +		mmap_arg.handle = a;
> +		igt_assert_eq(mmap_offset_ioctl(i915, &mmap_arg), 0);
> +		offset_a = mmap_arg.offset;
> +
> +		mmap_arg.handle = b;
> +		igt_assert_eq(mmap_offset_ioctl(i915, &mmap_arg), 0);
> +		offset_b = mmap_arg.offset;
> +
> +		igt_info("A[%s]: {fd:%d, handle:%d, offset:%"PRIx64"}\n",
> +			 t->name, A, a, offset_a);
> +		igt_info("B[%s]: {fd:%d, handle:%d, offset:%"PRIx64"}\n",
> +			 t->name, B, b, offset_b);
> +
> +		close(B);
> +
> +		ptr = mmap64(0, 4096, PROT_READ, MAP_SHARED, A, offset_a);
> +		igt_assert(ptr != MAP_FAILED);
> +		munmap(ptr, 4096);
> +
> +		close(A);
> +
> +		ptr = mmap64(0, 4096, PROT_READ, MAP_SHARED, A, offset_a);
> +		igt_assert(ptr == MAP_FAILED);
> +	}
> +}
> +
> +static void pf_nonblock(int i915)
> +{
> +	igt_spin_t *spin = igt_spin_new(i915);
> +
> +	for_each_mmap_offset_type(t) {
> +		uint32_t *ptr;
> +
> +		ptr = __gem_mmap_offset(i915, spin->handle, 0, 4096,
> +					PROT_READ | PROT_WRITE,
> +					t->type);
> +		if (!ptr)
> +			continue;
> +
> +		igt_set_timeout(1, t->name);
> +		/* no set-domain as we want to verify the pagefault is async */
> +		ptr[256] = 0;
> +		igt_reset_timeout();
> +
> +		munmap(ptr, 4096);
> +	}
> +
> +	igt_spin_free(i915, spin);
> +}
> +
> +static void close_race(int i915, int timeout)
> +{
> +	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
> +	_Atomic uint32_t *handles;
> +	size_t len = ALIGN((ncpus + 1) * sizeof(uint32_t), 4096);
> +
> +	handles = mmap64(0, len, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
> +	igt_assert(handles != MAP_FAILED);
> +
> +	igt_fork(child, ncpus + 1) {
> +		do {
> +			struct drm_i915_gem_mmap_offset mmap_arg = {};
> +			const int i = 1 + random() % ncpus;
> +			uint32_t old;
> +
> +			mmap_arg.handle = gem_create(i915, 4096);
> +			mmap_arg.flags = I915_MMAP_OFFSET_WB;
> +			old = atomic_exchange(&handles[i], mmap_arg.handle);

This will require adding libatomic to this executable's dependencies
with meson. Look to handling of gem_create.c for an example.


-- 
Petri Latvala




> +			ioctl(i915, DRM_IOCTL_GEM_CLOSE, &old);
> +
> +			if (ioctl(i915,
> +				  DRM_IOCTL_I915_GEM_MMAP_OFFSET,
> +				  &mmap_arg) != -1) {
> +				void *ptr;
> +
> +				ptr = mmap64(0, 4096,
> +					     PROT_WRITE, MAP_SHARED, i915,
> +					     mmap_arg.offset);
> +				if (ptr != MAP_FAILED) {
> +					*(volatile uint32_t *)ptr = 0;
> +					munmap(ptr, 4096);
> +				}
> +			}
> +
> +		} while (!READ_ONCE(handles[0]));
> +	}
> +
> +	sleep(timeout);
> +	handles[0] = 1;
> +	igt_waitchildren();
> +
> +	for (int i = 1; i <= ncpus; i++)
> +		ioctl(i915, DRM_IOCTL_GEM_CLOSE, handles[i]);
> +	munmap(handles, len);
> +}
> +
> +static uint64_t atomic_compare_swap_u64(_Atomic(uint64_t) *ptr,
> +					uint64_t oldval, uint64_t newval)
> +{
> +	atomic_compare_exchange_strong(ptr, &oldval, newval);
> +	return oldval;
> +}
> +
> +static uint64_t get_npages(_Atomic(uint64_t) *global, uint64_t npages)
> +{
> +	uint64_t try, old, max;
> +
> +	max = *global;
> +	do {
> +		old = max;
> +		try = 1 + npages % (max / 2);
> +		max -= try;
> +	} while ((max = atomic_compare_swap_u64(global, old, max)) != old);
> +
> +	return try;
> +}
> +
> +struct thread_clear {
> +	_Atomic(uint64_t) max;
> +	int timeout;
> +	int i915;
> +};
> +
> +static int create_ioctl(int i915, struct drm_i915_gem_create *create)
> +{
> +	int err = 0;
> +
> +	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_CREATE, create)) {
> +		err = -errno;
> +		igt_assume(err != 0);
> +	}
> +
> +	errno = 0;
> +	return err;
> +}
> +
> +static void *thread_clear(void *data)
> +{
> +	struct thread_clear *arg = data;
> +	const struct mmap_offset *t;
> +	unsigned long checked = 0;
> +	int i915 = arg->i915;
> +
> +	t = mmap_offset_types;
> +	igt_until_timeout(arg->timeout) {
> +		struct drm_i915_gem_create create = {};
> +		uint64_t npages;
> +		void *ptr;
> +
> +		npages = random();
> +		npages <<= 32;
> +		npages |= random();
> +		npages = get_npages(&arg->max, npages);
> +		create.size = npages << 12;
> +
> +		create_ioctl(i915, &create);
> +		ptr = __gem_mmap_offset(i915, create.handle, 0, create.size,
> +					PROT_READ | PROT_WRITE,
> +					t->type);
> +		/* No set-domains as we are being as naughty as possible */
> +		for (uint64_t page = 0; ptr && page < npages; page++) {
> +			uint64_t x[8] = {
> +				page * 4096 +
> +					sizeof(x) * ((page % (4096 - sizeof(x)) / sizeof(x)))
> +			};
> +
> +			if (page & 1)
> +				igt_memcpy_from_wc(x, ptr + x[0], sizeof(x));
> +			else
> +				memcpy(x, ptr + x[0], sizeof(x));
> +
> +			for (int i = 0; i < ARRAY_SIZE(x); i++)
> +				igt_assert_eq_u64(x[i], 0);
> +		}
> +		if (ptr)
> +			munmap(ptr, create.size);
> +		gem_close(i915, create.handle);
> +		checked += npages;
> +
> +		atomic_fetch_add(&arg->max, npages);
> +
> +		if (!(++t)->name)
> +			t = mmap_offset_types;
> +	}
> +
> +	return (void *)(uintptr_t)checked;
> +}
> +
> +static void always_clear(int i915, int timeout)
> +{
> +	struct thread_clear arg = {
> +		.i915 = i915,
> +		.timeout = timeout,
> +		.max = intel_get_avail_ram_mb() << (20 - 12), /* in pages */
> +	};
> +	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
> +	unsigned long checked;
> +	pthread_t thread[ncpus];
> +	void *result;
> +
> +	for (int i = 0; i < ncpus; i++)
> +		pthread_create(&thread[i], NULL, thread_clear, &arg);
> +
> +	checked = 0;
> +	for (int i = 0; i < ncpus; i++) {
> +		pthread_join(thread[i], &result);
> +		checked += (uintptr_t)result;
> +	}
> +	igt_info("Checked %'lu page allocations\n", checked);
> +}
> +
> +
> +igt_main
> +{
> +	int i915;
> +
> +	igt_fixture {
> +		i915 = drm_open_driver(DRIVER_INTEL);
> +		gem_require_mmap_offset(i915);
> +	}
> +
> +	igt_describe("Verify mapping to invalid gem objects won't be created");
> +	igt_subtest_f("bad-object")
> +		bad_object(i915);
> +	igt_subtest_f("bad-flags")
> +		bad_flags(i915);
> +
> +	igt_describe("Check buffer object mapping persists after gem_close");
> +	igt_subtest_f("basic-uaf")
> +		basic_uaf(i915);
> +
> +	igt_subtest_f("isolation")
> +		isolation(i915);
> +	igt_subtest_f("pf-nonblock")
> +		pf_nonblock(i915);
> +
> +	igt_describe("Check race between close and mmap offset between threads");
> +	igt_subtest_f("close-race")
> +		close_race(i915, 20);
> +
> +	igt_subtest_f("clear")
> +		always_clear(i915, 20);
> +
> +	igt_fixture {
> +		close(i915);
> +	}
> +}
> diff --git a/tests/meson.build b/tests/meson.build
> index 18788d44b..05dea1f4e 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -174,6 +174,7 @@ i915_progs = [
>  	'gem_media_vme',
>  	'gem_mmap',
>  	'gem_mmap_gtt',
> +	'gem_mmap_offset',
>  	'gem_mmap_offset_exhaustion',
>  	'gem_mmap_wc',
>  	'gem_partial_pwrite_pread',
> -- 
> 2.24.0
> 
> _______________________________________________
> igt-dev mailing list
> igt-dev@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/igt-dev
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

  parent reply	other threads:[~2019-11-28 13:38 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-11-26 17:25 [igt-dev] [PATCH i-g-t v4 0/3] Add GEM_MMAP_OFFSET support in IGT Zbigniew Kempczyński
2019-11-26 17:25 ` [igt-dev] [PATCH i-g-t v4 1/3] include/drm-uapi/i915_drm: Add defs for mmap_offset Zbigniew Kempczyński
2019-11-26 17:25 ` [igt-dev] [PATCH i-g-t v4 2/3] lib/i915/gem_mman: add mmap_offset support Zbigniew Kempczyński
2019-11-28 14:38   ` Chris Wilson
2019-11-28 14:43   ` Chris Wilson
2019-11-26 17:25 ` [igt-dev] [PATCH i-g-t v4 3/3] tests/i915/gem_mmap_offset: Add new API test for gem_mmap_offset Zbigniew Kempczyński
2019-11-28 12:55   ` [PATCH i-g-t] " Chris Wilson
2019-11-28 12:55     ` [igt-dev] " Chris Wilson
2019-11-28 12:55     ` [Intel-gfx] " Chris Wilson
2019-11-28 12:59     ` Chris Wilson
2019-11-28 12:59       ` [igt-dev] " Chris Wilson
2019-11-28 12:59       ` [Intel-gfx] " Chris Wilson
2019-11-28 13:38     ` Petri Latvala [this message]
2019-11-28 13:38       ` [igt-dev] " Petri Latvala
2019-11-28 13:38       ` [Intel-gfx] " Petri Latvala
2019-11-28 13:05   ` Chris Wilson
2019-11-28 13:05     ` [igt-dev] " Chris Wilson
2019-11-28 13:05     ` [Intel-gfx] " Chris Wilson
2019-11-28 13:10   ` Chris Wilson
2019-11-28 13:10     ` [igt-dev] " Chris Wilson
2019-11-28 13:10     ` [Intel-gfx] " Chris Wilson
2019-11-26 18:24 ` [igt-dev] ✓ Fi.CI.BAT: success for Add GEM_MMAP_OFFSET support in IGT. (rev4) Patchwork
2019-11-26 18:26   ` Chris Wilson
2019-11-27  5:22 ` [igt-dev] ✗ Fi.CI.IGT: failure " Patchwork
2019-11-28 13:05 ` [igt-dev] ✗ GitLab.Pipeline: warning for Add GEM_MMAP_OFFSET support in IGT. (rev5) Patchwork
2019-11-28 13:20 ` [igt-dev] ✓ Fi.CI.BAT: success " Patchwork
2019-11-28 13:43 ` [igt-dev] ✓ Fi.CI.BAT: success for Add GEM_MMAP_OFFSET support in IGT. (rev7) Patchwork
2019-11-29 19:26 ` [igt-dev] ✗ Fi.CI.IGT: failure for Add GEM_MMAP_OFFSET support in IGT. (rev5) Patchwork
2019-11-29 19:35 ` [igt-dev] ✗ Fi.CI.IGT: failure for Add GEM_MMAP_OFFSET support in IGT. (rev7) Patchwork

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191128133806.GU25209@platvala-desk.ger.corp.intel.com \
    --to=petri.latvala@intel.com \
    --cc=chris@chris-wilson.co.uk \
    --cc=igt-dev@lists.freedesktop.org \
    --cc=intel-gfx@lists.freedesktop.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.