diff mbox series

[RFC,04/42] drm/i915: introduce intel_memory_region

Message ID 20190214145740.14521-5-matthew.auld@intel.com (mailing list archive)
State New, archived
Headers show
Series Introduce memory region concept (including device local memory) | expand

Commit Message

Matthew Auld Feb. 14, 2019, 2:57 p.m. UTC
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

Comments

Chris Wilson Feb. 14, 2019, 3:16 p.m. UTC | #1
Quoting Matthew Auld (2019-02-14 14:57:02)
> +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);

min_size is a power of two.

n_pages = size >> ilog2(min_size);

would suffice. Keeping min_order would come in handy later.

> +
> +       if (sg_alloc_table(st, n_pages, GFP_KERNEL)) {
> +               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--)
> +                               goto err_free_blocks;
> +               } while (1);
> +
> +               n_pages -= 1 << order;

BIT(order) so you don't have sign extension fun. Need to fix
i915_gem_internal.c!

> +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);

create_object()

ops is acting as a factory here; and we are not operating on the object
itself.

> +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;

Hmm, 32b? Can resource_size_t be 64b on a 32b system?

It must be to accommodate PAE.

> +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)

A mix of 64b and 32b types.

if (size >> mem->mm.max_order + mem->mm.min_order)

> +               return ERR_PTR(-E2BIG);

GEM_BUG_ON(overflows_type(size, obj->base.size);

> +       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;

i915_gem_object_set_cache_coherency() ?
-Chris
Tvrtko Ursulin Feb. 26, 2019, 1 p.m. UTC | #2
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 */
>
Matthew Auld Feb. 26, 2019, 2:03 p.m. UTC | #3
On Thu, 14 Feb 2019 at 15:16, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>
> Quoting Matthew Auld (2019-02-14 14:57:02)
> > +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);
>
> min_size is a power of two.
>
> n_pages = size >> ilog2(min_size);
>
> would suffice. Keeping min_order would come in handy later.
>
> > +
> > +       if (sg_alloc_table(st, n_pages, GFP_KERNEL)) {
> > +               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--)
> > +                               goto err_free_blocks;
> > +               } while (1);
> > +
> > +               n_pages -= 1 << order;
>
> BIT(order) so you don't have sign extension fun. Need to fix
> i915_gem_internal.c!
>
> > +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);
>
> create_object()
>
> ops is acting as a factory here; and we are not operating on the object
> itself.
>
> > +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;
>
> Hmm, 32b? Can resource_size_t be 64b on a 32b system?
>
> It must be to accommodate PAE.
>
> > +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)
>
> A mix of 64b and 32b types.
>
> if (size >> mem->mm.max_order + mem->mm.min_order)
>
> > +               return ERR_PTR(-E2BIG);
>
> GEM_BUG_ON(overflows_type(size, obj->base.size);
>
> > +       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;
>
> i915_gem_object_set_cache_coherency() ?

We do that as part of object_create_region(), but maybe that's not so hot?

Also I just noticed that get_pages_buddy() returns -ENOSPC if we can't
satisfy the request, for consistency that should probably be -ENOMEM?

> -Chris
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Chris Wilson Feb. 26, 2019, 2:18 p.m. UTC | #4
Quoting Matthew Auld (2019-02-26 14:03:10)
> On Thu, 14 Feb 2019 at 15:16, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> >
> > Quoting Matthew Auld (2019-02-14 14:57:02)
> > > +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);
> >
> > min_size is a power of two.
> >
> > n_pages = size >> ilog2(min_size);
> >
> > would suffice. Keeping min_order would come in handy later.
> >
> > > +
> > > +       if (sg_alloc_table(st, n_pages, GFP_KERNEL)) {
> > > +               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--)
> > > +                               goto err_free_blocks;
> > > +               } while (1);
> > > +
> > > +               n_pages -= 1 << order;
> >
> > BIT(order) so you don't have sign extension fun. Need to fix
> > i915_gem_internal.c!
> >
> > > +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);
> >
> > create_object()
> >
> > ops is acting as a factory here; and we are not operating on the object
> > itself.
> >
> > > +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;
> >
> > Hmm, 32b? Can resource_size_t be 64b on a 32b system?
> >
> > It must be to accommodate PAE.
> >
> > > +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)
> >
> > A mix of 64b and 32b types.
> >
> > if (size >> mem->mm.max_order + mem->mm.min_order)
> >
> > > +               return ERR_PTR(-E2BIG);
> >
> > GEM_BUG_ON(overflows_type(size, obj->base.size);
> >
> > > +       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;
> >
> > i915_gem_object_set_cache_coherency() ?
> 
> We do that as part of object_create_region(), but maybe that's not so hot?

I think tidier done together; and maybe you'll feel inspired to clean up
it a bit more as well.

> Also I just noticed that get_pages_buddy() returns -ENOSPC if we can't
> satisfy the request, for consistency that should probably be -ENOMEM?

Yes. ENOSPC returned to userspace currently means "batch could not fit
into the GTT after all constraints applied", and since this errno can
propagate to execbuf, we should strive for uniqueness. In this case not
being able to find memory on the device... ENOMEM. Although -ENOXMEM
would be better to indicate we ran out of memory on the device. But that
doesn't exist, maybe -ENOBUFS?

If we restrict ourselves to errno-base.h (and I think we should try to);
ENOMEM or ENXIO.
-Chris
Matthew Auld Feb. 26, 2019, 2:20 p.m. UTC | #5
On Tue, 26 Feb 2019 at 13:00, Tvrtko Ursulin
<tvrtko.ursulin@linux.intel.com> wrote:
>
>
> 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.

Yeah, the type stuff here is a little odd, but we do make sure that
the number of pages doesn't exceed INT_MAX at object creation.

>
> > +             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?

It will either return -ENOMEM or -ENOSPC, although I'm now wondering
if returning -ENOSPC from get_pages() is a bad idea. We normally just
return -ENOMEM if we can't satisfy the request for the other
backends...

>
> > +                             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.

Yup, makes sense.

>
> 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 */
> >
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
diff mbox series

Patch

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)) {
+		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--)
+				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_page_sizes |= block_size;
+		st->nents++;
+
+		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 */