Re: [RFC PATCH 04/42] drm/i915: introduce intel_memory_region

Tvrtko Ursulin
2019-02-26T13:00:31Z

Hi,

Just some quick comments, not a full review. Possibly a repeat of some
same comments from way back, not sure.

On 14/02/2019 14:57, Matthew Auld wrote:
> Support memory regions, as defined by a given (start, end), and allow
> creating GEM objects which are backed by said region.
>
> Signed-off-by: Matthew Auld <matthew.auld@intel.com>
> Signed-off-by: Abdiel Janulgue <abdiel.janulgue@linux.intel.com>
> Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
> ---
> drivers/gpu/drm/i915/Makefile | 1 +
> drivers/gpu/drm/i915/i915_drv.h | 1 +
> drivers/gpu/drm/i915/i915_gem.c | 1 +
> drivers/gpu/drm/i915/i915_gem_object.h | 9 +
> drivers/gpu/drm/i915/intel_memory_region.c | 232 ++++++++++++++++++
> drivers/gpu/drm/i915/intel_memory_region.h | 126 ++++++++++
> drivers/gpu/drm/i915/selftests/huge_pages.c | 81 ++++++
> .../drm/i915/selftests/i915_mock_selftests.h | 1 +
> .../drm/i915/selftests/intel_memory_region.c | 128 ++++++++++
> .../gpu/drm/i915/selftests/mock_gem_device.c | 1 +
> drivers/gpu/drm/i915/selftests/mock_region.c | 71 ++++++
> drivers/gpu/drm/i915/selftests/mock_region.h | 35 +++
> 12 files changed, 687 insertions(+)
> create mode 100644 drivers/gpu/drm/i915/intel_memory_region.c
> create mode 100644 drivers/gpu/drm/i915/intel_memory_region.h
> create mode 100644 drivers/gpu/drm/i915/selftests/intel_memory_region.c
> create mode 100644 drivers/gpu/drm/i915/selftests/mock_region.c
> create mode 100644 drivers/gpu/drm/i915/selftests/mock_region.h
>
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index e5ce813d1936..96be264fa382 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -88,6 +88,7 @@ i915-y += \
> intel_engine_cs.o \
> intel_hangcheck.o \
> intel_lrc.o \
> + intel_memory_region.o \
> intel_mocs.o \
> intel_ringbuffer.o \
> intel_uncore.o \
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 17fe942eaafa..0bea7d889284 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -72,6 +72,7 @@
> #include "intel_wopcm.h"
> #include "intel_workarounds.h"
> #include "intel_uc.h"
> +#include "intel_memory_region.h"
>
> #include "i915_gem.h"
> #include "i915_gem_context.h"
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index b421bc7a2e26..92768ab294a4 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -5706,4 +5706,5 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
> #include "selftests/i915_gem_object.c"
> #include "selftests/i915_gem_coherency.c"
> #include "selftests/i915_gem.c"
> +#include "selftests/intel_memory_region.c"
> #endif
> diff --git a/drivers/gpu/drm/i915/i915_gem_object.h b/drivers/gpu/drm/i915/i915_gem_object.h
> index fab040331cdb..ac52f61e8ad1 100644
> --- a/drivers/gpu/drm/i915/i915_gem_object.h
> +++ b/drivers/gpu/drm/i915/i915_gem_object.h
> @@ -87,6 +87,15 @@ struct drm_i915_gem_object {
>
> const struct drm_i915_gem_object_ops *ops;
>
> + /**
> + * Memory region for this object.
> + */
> + struct intel_memory_region *memory_region;
> + /**
> + * List of memory region blocks allocated for this object.
> + */
> + struct list_head blocks;
> +
> struct {
> /**
> * @vma.lock: protect the list/tree of vmas
> diff --git a/drivers/gpu/drm/i915/intel_memory_region.c b/drivers/gpu/drm/i915/intel_memory_region.c
> new file mode 100644
> index 000000000000..405d6d51194f
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_memory_region.c
> @@ -0,0 +1,232 @@
> +/*
> + * Copyright © 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.
> + *
> + */
> +
> +#include "intel_memory_region.h"
> +#include "i915_drv.h"
> +
> +static void
> +memory_region_free_pages(struct drm_i915_gem_object *obj,
> + struct sg_table *pages)
> +{
> +
> + struct i915_gem_buddy_block *block, *on;
> +
> + lockdep_assert_held(&obj->memory_region->mm_lock);
> +
> + list_for_each_entry_safe(block, on, &obj->blocks, link) {
> + list_del_init(&block->link);
> + i915_gem_buddy_free(&obj->memory_region->mm, block);
> + }
> +
> + sg_free_table(pages);
> + kfree(pages);
> +}
> +
> +void
> +i915_memory_region_put_pages_buddy(struct drm_i915_gem_object *obj,
> + struct sg_table *pages)
> +{
> + mutex_lock(&obj->memory_region->mm_lock);
> + memory_region_free_pages(obj, pages);
> + mutex_unlock(&obj->memory_region->mm_lock);
> +
> + obj->mm.dirty = false;
> +}
> +
> +int
> +i915_memory_region_get_pages_buddy(struct drm_i915_gem_object *obj)
> +{
> + struct intel_memory_region *mem = obj->memory_region;
> + resource_size_t size = obj->base.size;
> + struct sg_table *st;
> + struct scatterlist *sg;
> + unsigned int sg_page_sizes;
> + unsigned long n_pages;
> +
> + GEM_BUG_ON(!IS_ALIGNED(size, mem->mm.min_size));
> + GEM_BUG_ON(!list_empty(&obj->blocks));
> +
> + st = kmalloc(sizeof(*st), GFP_KERNEL);
> + if (!st)
> + return -ENOMEM;
> +
> + n_pages = div64_u64(size, mem->mm.min_size);
> +
> + if (sg_alloc_table(st, n_pages, GFP_KERNEL)) {

sg_alloc_table takes unsigned int for nents so if you need to do this
calculation in u64 you should probably add some checks against overflow.
Although it would probably be simpler and more robust to have it as
unsigned int and just check before hand the allocation request is not
exceeding the sg_table API capabilities.

> + kfree(st);
> + return -ENOMEM;
> + }
> +
> + sg = st->sgl;
> + st->nents = 0;
> + sg_page_sizes = 0;
> +
> + mutex_lock(&mem->mm_lock);
> +
> + do {
> + struct i915_gem_buddy_block *block;
> + unsigned int order;
> + u64 block_size;
> + u64 offset;
> +
> + order = fls(n_pages) - 1;
> + GEM_BUG_ON(order > mem->mm.max_order);
> +
> + do {
> + block = i915_gem_buddy_alloc(&mem->mm, order);
> + if (!IS_ERR(block))
> + break;
> +
> + /* XXX: some kind of eviction pass, local to the device */
> + if (!order--)

Is it interesting to propagate the more specific error code returned by
i915_gem_buddy_alloc?

> + goto err_free_blocks;
> + } while (1);
> +
> + n_pages -= 1 << order;
> +
> + INIT_LIST_HEAD(&block->link);
> + list_add(&block->link, &obj->blocks);
> +
> + block_size = i915_gem_buddy_block_size(&mem->mm, block);
> + offset = i915_gem_buddy_block_offset(block);
> +
> + sg_dma_address(sg) = mem->region.start + offset;
> + sg_dma_len(sg) = block_size;
> +
> + sg->length = block_size;

sg->dma_len and sg->length are unsigned int so again I think block_size
can follow with some limit checking earlier on.

> + sg_page_sizes |= block_size;
> + st->nents++;

If address of this block is consecutive block to the previous one you
can, considering the overflow of sg->dma_len and sg->length fields,
coalesce the two by just bumping the lengths and not incrementing the
st->nents++. That would make the i915_sg_trim below more effective. To
be fair I have no idea how likely are you to get consecutive blocks with
the buddy allocator so your decision.

Regards,

Tvrtko

> +
> + if (!n_pages) {
> + sg_mark_end(sg);
> + break;
> + }
> +
> + sg = __sg_next(sg);
> + } while (1);
> +
> + mutex_unlock(&mem->mm_lock);
> +
> + i915_sg_trim(st);
> +
> + __i915_gem_object_set_pages(obj, st, sg_page_sizes);
> +
> + return 0;
> +
> +err_free_blocks:
> + memory_region_free_pages(obj, st);
> + mutex_unlock(&mem->mm_lock);
> + return -ENOSPC;
> +}
> +
> +int i915_memory_region_init_buddy(struct intel_memory_region *mem)
> +{
> + return i915_gem_buddy_init(&mem->mm, resource_size(&mem->region),
> + mem->min_page_size);
> +}
> +
> +void i915_memory_region_release_buddy(struct intel_memory_region *mem)
> +{
> + i915_gem_buddy_fini(&mem->mm);
> +}
> +
> +struct drm_i915_gem_object *
> +i915_gem_object_create_region(struct intel_memory_region *mem,
> + resource_size_t size,
> + unsigned int flags)
> +{
> + struct drm_i915_gem_object *obj;
> +
> + if (!mem)
> + return ERR_PTR(-ENODEV);
> +
> + size = round_up(size, mem->min_page_size);
> +
> + GEM_BUG_ON(!size);
> + GEM_BUG_ON(!IS_ALIGNED(size, I915_GTT_MIN_ALIGNMENT));
> +
> + if (size >> PAGE_SHIFT > INT_MAX)
> + return ERR_PTR(-E2BIG);
> +
> + if (overflows_type(size, obj->base.size))
> + return ERR_PTR(-E2BIG);
> +
> + obj = mem->ops->object_create(mem, size, flags);
> + if (IS_ERR(obj))
> + return obj;
> +
> + INIT_LIST_HEAD(&obj->blocks);
> + obj->memory_region = mem;
> +
> + i915_gem_object_set_cache_coherency(obj, obj->cache_level);
> +
> + return obj;
> +}
> +
> +struct intel_memory_region *
> +intel_memory_region_create(struct drm_i915_private *i915,
> + resource_size_t start,
> + resource_size_t size,
> + resource_size_t min_page_size,
> + resource_size_t io_start,
> + const struct intel_memory_region_ops *ops)
> +{
> + struct intel_memory_region *mem;
> + int err;
> +
> + mem = kzalloc(sizeof(*mem), GFP_KERNEL);
> + if (!mem)
> + return ERR_PTR(-ENOMEM);
> +
> + mem->i915 = i915;
> + mem->region = (struct resource)DEFINE_RES_MEM(start, size);
> + mem->io_start = io_start;
> + mem->min_page_size = min_page_size;
> + mem->ops = ops;
> +
> + mutex_init(&mem->mm_lock);
> +
> + if (ops->init) {
> + err = ops->init(mem);
> + if (err) {
> + kfree(mem);
> + mem = ERR_PTR(err);
> + }
> + }
> +
> + return mem;
> +}
> +
> +void
> +intel_memory_region_destroy(struct intel_memory_region *mem)
> +{
> + if (mem->ops->release)
> + mem->ops->release(mem);
> +
> + kfree(mem);
> +}
> +
> +#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
> +#include "selftests/mock_region.c"
> +#endif
> diff --git a/drivers/gpu/drm/i915/intel_memory_region.h b/drivers/gpu/drm/i915/intel_memory_region.h
> new file mode 100644
> index 000000000000..6d8a954ca75e
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_memory_region.h
> @@ -0,0 +1,126 @@
> +/*
> + * Copyright © 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.
> + *
> + */
> +
> +#ifndef __INTEL_MEMORY_REGION_H__
> +#define __INTEL_MEMORY_REGION_H__
> +
> +#include <linux/ioport.h>
> +#include <linux/mutex.h>
> +#include <linux/io-mapping.h>
> +
> +#include "i915_gem_buddy.h"
> +
> +struct drm_i915_private;
> +struct drm_i915_gem_object;
> +struct intel_memory_region;
> +struct sg_table;
> +
> +/**
> + * Base memory type
> + */
> +enum intel_memory_type {
> + INTEL_SMEM = 0,
> + INTEL_LMEM,
> + INTEL_STOLEN,
> +};
> +
> +enum intel_region_id {
> + INTEL_MEMORY_SMEM = 0,
> + INTEL_MEMORY_LMEM,
> + INTEL_MEMORY_STOLEN,
> + INTEL_MEMORY_UKNOWN, /* Should be last */
> +};
> +
> +#define REGION_SMEM BIT(INTEL_MEMORY_SMEM)
> +#define REGION_LMEM BIT(INTEL_MEMORY_LMEM)
> +#define REGION_STOLEN BIT(INTEL_MEMORY_STOLEN)
> +
> +#define INTEL_MEMORY_TYPE_SHIFT 16
> +
> +#define MEMORY_TYPE_FROM_REGION(r) (ilog2(r >> INTEL_MEMORY_TYPE_SHIFT))
> +#define MEMORY_INSTANCE_FROM_REGION(r) (ilog2(r & 0xffff))
> +
> +/**
> + * Memory regions encoded as type | instance
> + */
> +static const u32 intel_region_map[] = {
> + [INTEL_MEMORY_SMEM] = BIT(INTEL_SMEM + INTEL_MEMORY_TYPE_SHIFT) | BIT(0),
> + [INTEL_MEMORY_LMEM] = BIT(INTEL_LMEM + INTEL_MEMORY_TYPE_SHIFT) | BIT(0),
> + [INTEL_MEMORY_STOLEN] = BIT(INTEL_STOLEN + INTEL_MEMORY_TYPE_SHIFT) | BIT(0),
> +};
> +
> +struct intel_memory_region_ops {
> + unsigned int flags;
> +
> + int (*init)(struct intel_memory_region *);
> + void (*release)(struct intel_memory_region *);
> +
> + struct drm_i915_gem_object *
> + (*object_create)(struct intel_memory_region *,
> + resource_size_t,
> + unsigned int);
> +};
> +
> +struct intel_memory_region {
> + struct drm_i915_private *i915;
> +
> + const struct intel_memory_region_ops *ops;
> +
> + struct io_mapping iomap;
> + struct resource region;
> +
> + struct i915_gem_buddy_mm mm;
> + struct mutex mm_lock;
> +
> + resource_size_t io_start;
> + resource_size_t min_page_size;
> +
> + unsigned int type;
> + unsigned int instance;
> + unsigned int id;
> +};
> +
> +int i915_memory_region_init_buddy(struct intel_memory_region *mem);
> +void i915_memory_region_release_buddy(struct intel_memory_region *mem);
> +
> +int i915_memory_region_get_pages_buddy(struct drm_i915_gem_object *obj);
> +void i915_memory_region_put_pages_buddy(struct drm_i915_gem_object *obj,
> + struct sg_table *pages);
> +
> +struct intel_memory_region *
> +intel_memory_region_create(struct drm_i915_private *i915,
> + resource_size_t start,
> + resource_size_t size,
> + resource_size_t min_page_size,
> + resource_size_t io_start,
> + const struct intel_memory_region_ops *ops);
> +void
> +intel_memory_region_destroy(struct intel_memory_region *mem);
> +
> +struct drm_i915_gem_object *
> +i915_gem_object_create_region(struct intel_memory_region *mem,
> + resource_size_t size,
> + unsigned int flags);
> +
> +#endif
> diff --git a/drivers/gpu/drm/i915/selftests/huge_pages.c b/drivers/gpu/drm/i915/selftests/huge_pages.c
> index b6d84939592b..b0c8b4955f14 100644
> --- a/drivers/gpu/drm/i915/selftests/huge_pages.c
> +++ b/drivers/gpu/drm/i915/selftests/huge_pages.c
> @@ -458,6 +458,86 @@ static int igt_mock_exhaust_device_supported_pages(void *arg)
> return err;
> }
>
> +
> +static int igt_mock_memory_region_huge_pages(void *arg)
> +{
> + struct i915_hw_ppgtt *ppgtt = arg;
> + struct drm_i915_private *i915 = ppgtt->vm.i915;
> + unsigned long supported = INTEL_INFO(i915)->page_sizes;
> + struct intel_memory_region *mem;
> + struct drm_i915_gem_object *obj;
> + struct i915_vma *vma;
> + int bit;
> + int err = 0;
> +
> + mem = mock_region_create(i915, 0, SZ_2G,
> + I915_GTT_PAGE_SIZE_4K, 0);
> + if (IS_ERR(mem)) {
> + pr_err("failed to create memory region\n");
> + return PTR_ERR(mem);
> + }
> +
> + for_each_set_bit(bit, &supported, ilog2(I915_GTT_MAX_PAGE_SIZE) + 1) {
> + unsigned int page_size = BIT(bit);
> + resource_size_t phys;
> +
> + obj = i915_gem_object_create_region(mem, page_size, 0);
> + if (IS_ERR(obj)) {
> + err = PTR_ERR(obj);
> + goto out_destroy_device;
> + }
> +
> + pr_info("memory region start(%pa)\n",
> + &obj->memory_region->region.start);
> + pr_info("creating object, size=%x\n", page_size);
> +
> + vma = i915_vma_instance(obj, &ppgtt->vm, NULL);
> + if (IS_ERR(vma)) {
> + err = PTR_ERR(vma);
> + goto out_put;
> + }
> +
> + err = i915_vma_pin(vma, 0, 0, PIN_USER);
> + if (err)
> + goto out_close;
> +
> + phys = i915_gem_object_get_dma_address(obj, 0);
> + if (!IS_ALIGNED(phys, page_size)) {
> + pr_err("memory region misaligned(%pa)\n", &phys);
> + err = -EINVAL;
> + goto out_close;
> + }
> +
> + if (vma->page_sizes.gtt != page_size) {
> + pr_err("page_sizes.gtt=%u, expected=%u\n",
> + vma->page_sizes.gtt, page_size);
> + err = -EINVAL;
> + goto out_unpin;
> + }
> +
> + i915_vma_unpin(vma);
> + i915_vma_close(vma);
> +
> + i915_gem_object_put(obj);
> + }
> +
> + goto out_destroy_device;
> +
> +out_unpin:
> + i915_vma_unpin(vma);
> +out_close:
> + i915_vma_close(vma);
> +out_put:
> + i915_gem_object_put(obj);
> +out_destroy_device:
> + mutex_unlock(&i915->drm.struct_mutex);
> + i915_gem_drain_freed_objects(i915);
> + mutex_lock(&i915->drm.struct_mutex);
> + intel_memory_region_destroy(mem);
> +
> + return err;
> +}
> +
> static int igt_mock_ppgtt_misaligned_dma(void *arg)
> {
> struct i915_hw_ppgtt *ppgtt = arg;
> @@ -1697,6 +1777,7 @@ int i915_gem_huge_page_mock_selftests(void)
> {
> static const struct i915_subtest tests[] = {
> SUBTEST(igt_mock_exhaust_device_supported_pages),
> + SUBTEST(igt_mock_memory_region_huge_pages),
> SUBTEST(igt_mock_ppgtt_misaligned_dma),
> SUBTEST(igt_mock_ppgtt_huge_fill),
> SUBTEST(igt_mock_ppgtt_64K),
> diff --git a/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h b/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h
> index 984e07ed65e5..3e34ee2255db 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h
> +++ b/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h
> @@ -25,3 +25,4 @@ selftest(gtt, i915_gem_gtt_mock_selftests)
> selftest(hugepages, i915_gem_huge_page_mock_selftests)
> selftest(contexts, i915_gem_context_mock_selftests)
> selftest(buddy, i915_gem_buddy_mock_selftests)
> +selftest(memory_region, intel_memory_region_mock_selftests)
> diff --git a/drivers/gpu/drm/i915/selftests/intel_memory_region.c b/drivers/gpu/drm/i915/selftests/intel_memory_region.c
> new file mode 100644
> index 000000000000..2b8d28216d87
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/selftests/intel_memory_region.c
> @@ -0,0 +1,128 @@
> +/*
> + * Copyright © 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.
> + *
> + */
> +
> +#include "../i915_selftest.h"
> +
> +#include "mock_gem_device.h"
> +#include "mock_context.h"
> +#include "mock_drm.h"
> +
> +static void close_objects(struct list_head *objects)
> +{
> + struct drm_i915_gem_object *obj, *on;
> +
> + list_for_each_entry_safe(obj, on, objects, st_link) {
> + if (i915_gem_object_has_pinned_pages(obj))
> + i915_gem_object_unpin_pages(obj);
> + /* No polluting the memory region between tests */
> + __i915_gem_object_put_pages(obj, I915_MM_NORMAL);
> + i915_gem_object_put(obj);
> + list_del(&obj->st_link);
> + }
> +}
> +
> +static int igt_mock_fill(void *arg)
> +{
> + struct intel_memory_region *mem = arg;
> + resource_size_t total = resource_size(&mem->region);
> + resource_size_t page_size;
> + resource_size_t rem;
> + unsigned long max_pages;
> + unsigned long page_num;
> + LIST_HEAD(objects);
> + int err = 0;
> +
> + page_size = mem->mm.min_size;
> + max_pages = total / page_size;
> + rem = total;
> +
> + for_each_prime_number_from(page_num, 1, max_pages) {
> + resource_size_t size = page_num * page_size;
> + struct drm_i915_gem_object *obj;
> +
> + obj = i915_gem_object_create_region(mem, size, 0);
> + if (IS_ERR(obj)) {
> + err = PTR_ERR(obj);
> + break;
> + }
> +
> + err = i915_gem_object_pin_pages(obj);
> + if (err) {
> + i915_gem_object_put(obj);
> + break;
> + }
> +
> + list_add(&obj->st_link, &objects);
> + rem -= size;
> + }
> +
> + if (err == -ENOMEM)
> + err = 0;
> + if (err == -ENOSPC) {
> + if (page_num * page_size <= rem) {
> + pr_err("igt_mock_fill failed, space still left in region\n");
> + err = -EINVAL;
> + } else {
> + err = 0;
> + }
> + }
> +
> + close_objects(&objects);
> +
> + return err;
> +}
> +
> +int intel_memory_region_mock_selftests(void)
> +{
> + static const struct i915_subtest tests[] = {
> + SUBTEST(igt_mock_fill),
> + };
> + struct intel_memory_region *mem;
> + struct drm_i915_private *i915;
> + int err;
> +
> + i915 = mock_gem_device();
> + if (!i915)
> + return -ENOMEM;
> +
> + mem = mock_region_create(i915, 0, SZ_2G,
> + I915_GTT_PAGE_SIZE_4K, 0);
> + if (IS_ERR(mem)) {
> + pr_err("failed to create memory region\n");
> + err = PTR_ERR(mem);
> + goto out_unref;
> + }
> +
> + mutex_lock(&i915->drm.struct_mutex);
> + err = i915_subtests(tests, mem);
> + mutex_unlock(&i915->drm.struct_mutex);
> +
> + i915_gem_drain_freed_objects(i915);
> + intel_memory_region_destroy(mem);
> +
> +out_unref:
> + drm_dev_put(&i915->drm);
> +
> + return err;
> +}
> diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
> index 876f4e6dadac..f8901cd12180 100644
> --- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
> +++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
> @@ -32,6 +32,7 @@
> #include "mock_gem_object.h"
> #include "mock_gtt.h"
> #include "mock_uncore.h"
> +#include "mock_region.h"
>
> void mock_device_flush(struct drm_i915_private *i915)
> {
> diff --git a/drivers/gpu/drm/i915/selftests/mock_region.c b/drivers/gpu/drm/i915/selftests/mock_region.c
> new file mode 100644
> index 000000000000..2c83711f780d
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/selftests/mock_region.c
> @@ -0,0 +1,71 @@
> +/*
> + * 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 "mock_region.h"
> +
> +static const struct drm_i915_gem_object_ops mock_region_obj_ops = {
> + .get_pages = i915_memory_region_get_pages_buddy,
> + .put_pages = i915_memory_region_put_pages_buddy,
> +};
> +
> +static struct drm_i915_gem_object *
> +mock_object_create(struct intel_memory_region *mem,
> + resource_size_t size,
> + unsigned int flags)
> +{
> + struct drm_i915_private *i915 = mem->i915;
> + struct drm_i915_gem_object *obj;
> +
> + if (size > BIT(mem->mm.max_order) * mem->mm.min_size)
> + return ERR_PTR(-E2BIG);
> +
> + obj = i915_gem_object_alloc(i915);
> + if (!obj)
> + return ERR_PTR(-ENOMEM);
> +
> + drm_gem_private_object_init(&i915->drm, &obj->base, size);
> + i915_gem_object_init(obj, &mock_region_obj_ops);
> +
> + obj->read_domains = I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT;
> + obj->cache_level = HAS_LLC(i915) ? I915_CACHE_LLC : I915_CACHE_NONE;
> +
> + return obj;
> +}
> +
> +static const struct intel_memory_region_ops mock_region_ops = {
> + .init = i915_memory_region_init_buddy,
> + .release = i915_memory_region_release_buddy,
> + .object_create = mock_object_create,
> +};
> +
> +struct intel_memory_region *
> +mock_region_create(struct drm_i915_private *i915,
> + resource_size_t start,
> + resource_size_t size,
> + resource_size_t min_page_size,
> + resource_size_t io_start)
> +{
> + return intel_memory_region_create(i915, start, size, min_page_size,
> + io_start, &mock_region_ops);
> +}
> diff --git a/drivers/gpu/drm/i915/selftests/mock_region.h b/drivers/gpu/drm/i915/selftests/mock_region.h
> new file mode 100644
> index 000000000000..47718313fa58
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/selftests/mock_region.h
> @@ -0,0 +1,35 @@
> +/*
> + * 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.
> + *
> + */
> +
> +#ifndef __MOCK_REGION_H
> +#define __MOCK_REGION_H
> +
> +struct intel_memory_region *
> +mock_region_create(struct drm_i915_private *i915,
> + resource_size_t start,
> + resource_size_t size,
> + resource_size_t min_page_size,
> + resource_size_t io_start);
> +
> +#endif /* !__MOCK_REGION_H */
>