diff mbox

[13/18] drm/i915: Introduce i915_gem_object_create_stolen()

Message ID 1350666204-8101-13-git-send-email-chris@chris-wilson.co.uk (mailing list archive)
State Accepted
Delegated to: Ben Widawsky
Headers show

Commit Message

Chris Wilson Oct. 19, 2012, 5:03 p.m. UTC
Allow for the creation of GEM objects backed by stolen memory. As these
are not backed by ordinary pages, we create a fake dma mapping and store
the address in the scatterlist rather than obj->pages.

v2: Mark _i915_gem_object_create_stolen() as static, as noticed by Jesse
Barnes.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
---
 drivers/gpu/drm/i915/i915_drv.h        |    3 +
 drivers/gpu/drm/i915/i915_gem.c        |    1 +
 drivers/gpu/drm/i915/i915_gem_stolen.c |  122 ++++++++++++++++++++++++++++++++
 3 files changed, 126 insertions(+)

Comments

Ben Widawsky Nov. 5, 2012, 4:32 p.m. UTC | #1
On Fri, 19 Oct 2012 18:03:19 +0100
Chris Wilson <chris@chris-wilson.co.uk> wrote:

> Allow for the creation of GEM objects backed by stolen memory. As these
> are not backed by ordinary pages, we create a fake dma mapping and store
> the address in the scatterlist rather than obj->pages.
> 
> v2: Mark _i915_gem_object_create_stolen() as static, as noticed by Jesse
> Barnes.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>

Deferring on an r-b for now until I understand the point of most of this
patch.

Some comments below however.

> ---
>  drivers/gpu/drm/i915/i915_drv.h        |    3 +
>  drivers/gpu/drm/i915/i915_gem.c        |    1 +
>  drivers/gpu/drm/i915/i915_gem_stolen.c |  122 ++++++++++++++++++++++++++++++++
>  3 files changed, 126 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 6b893c7..5a0e0cd 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -1526,6 +1526,9 @@ int i915_gem_init_stolen(struct drm_device *dev);
>  int i915_gem_stolen_setup_compression(struct drm_device *dev);
>  void i915_gem_stolen_cleanup_compression(struct drm_device *dev);
>  void i915_gem_cleanup_stolen(struct drm_device *dev);
> +struct drm_i915_gem_object *
> +i915_gem_object_create_stolen(struct drm_device *dev, u32 size);
> +void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj);
>  
>  /* i915_gem_tiling.c */
>  void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index d75c5c3..0349899 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -3857,6 +3857,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
>  	obj->pages_pin_count = 0;
>  	i915_gem_object_put_pages(obj);
>  	i915_gem_object_free_mmap_offset(obj);
> +	i915_gem_object_release_stolen(obj);
>  
>  	BUG_ON(obj->pages);
>  
> diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
> index 68ef22a..86c4af4 100644
> --- a/drivers/gpu/drm/i915/i915_gem_stolen.c
> +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
> @@ -204,3 +204,125 @@ int i915_gem_init_stolen(struct drm_device *dev)
>  
>  	return 0;
>  }
> +
> +static struct sg_table *
> +i915_pages_create_for_stolen(struct drm_device *dev,
> +			     u32 offset, u32 size)
> +{
> +	struct drm_i915_private *dev_priv = dev->dev_private;
> +	struct sg_table *st;
> +	struct scatterlist *sg;
> +

BUG_ON(offset + size <= dev_priv->mm.gtt->stolen_size);

> +	/* We hide that we have no struct page backing our stolen object
> +	 * by wrapping the contiguous physical allocation with a fake
> +	 * dma mapping in a single scatterlist.
> +	 */
> +
> +	st = kmalloc(sizeof(*st), GFP_KERNEL);
> +	if (st == NULL)
> +		return NULL;
> +
> +	if (!sg_alloc_table(st, 1, GFP_KERNEL)) {
> +		kfree(st);
> +		return NULL;
> +	}
> +
> +	sg = st->sgl;
> +	sg->offset = offset;
> +	sg->length = size;
> +
> +	sg_dma_address(sg) = dev_priv->mm.stolen_base + offset;
> +	sg_dma_len(sg) = size;
> +

Do we want to make stolen_base a dma_addr_t (or at least typecast it)?

> +	return st;
> +}
> +
> +static int i915_gem_object_get_pages_stolen(struct drm_i915_gem_object *obj)
> +{
> +	BUG();
> +	return -EINVAL;
> +}
> +

__noreturn, or maybe just make .get_pages = NULL, and do the check in
the upper layer get_pages?

> +static void i915_gem_object_put_pages_stolen(struct drm_i915_gem_object *obj)
> +{
> +	/* Should only be called during free */
> +	sg_free_table(obj->pages);
> +	kfree(obj->pages);
> +}
> +
> +static const struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = {
> +	.get_pages = i915_gem_object_get_pages_stolen,
> +	.put_pages = i915_gem_object_put_pages_stolen,
> +};
> +
> +static struct drm_i915_gem_object *
> +_i915_gem_object_create_stolen(struct drm_device *dev,
> +			       struct drm_mm_node *stolen)
> +{
> +	struct drm_i915_gem_object *obj;
> +
> +	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
> +	if (obj == NULL)
> +		return NULL;
> +
> +	if (drm_gem_private_object_init(dev, &obj->base, stolen->size))
> +		goto cleanup;
> +
> +	i915_gem_object_init(obj, &i915_gem_object_stolen_ops);
> +
> +	obj->pages = i915_pages_create_for_stolen(dev,
> +						  stolen->start, stolen->size);
> +	if (obj->pages == NULL)
> +		goto cleanup;
> +
> +	obj->has_dma_mapping = true;
> +	obj->pages_pin_count = 1;
> +	obj->stolen = stolen;
> +
> +	obj->base.write_domain = I915_GEM_DOMAIN_GTT;
> +	obj->base.read_domains = I915_GEM_DOMAIN_GTT;
> +	obj->cache_level = I915_CACHE_NONE;
> +
> +	return obj;
> +
> +cleanup:
> +	kfree(obj);
> +	return NULL;
> +}
> +
> +struct drm_i915_gem_object *
> +i915_gem_object_create_stolen(struct drm_device *dev, u32 size)
> +{
> +	struct drm_i915_private *dev_priv = dev->dev_private;
> +	struct drm_i915_gem_object *obj;
> +	struct drm_mm_node *stolen;
> +
> +	if (dev_priv->mm.stolen_base == 0)
> +		return 0;
> +
> +	DRM_DEBUG_KMS("creating stolen object: size=%x\n", size);
> +	if (size == 0)
> +		return NULL;
> +
> +	stolen = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0);
> +	if (stolen)
> +		stolen = drm_mm_get_block(stolen, size, 4096);
> +	if (stolen == NULL)
> +		return NULL;

Could probably do slightly better here with ERR_PTR(-ENOMEM) but since
we don't do that elsewhere, I guess it doesn't matter.

> +
> +	obj = _i915_gem_object_create_stolen(dev, stolen);
> +	if (obj)
> +		return obj;
> +
> +	drm_mm_put_block(stolen);
> +	return NULL;

Similarly here maybe use PTR_ERR(obj) and return something meaningful in
_i915_gem_object_create_stolen.

Since just about everything is more or less, ENOMEM, maybe it doesn't
matter.
> +}
> +
> +void
> +i915_gem_object_release_stolen(struct drm_i915_gem_object *obj)
> +{
> +	if (obj->stolen) {
> +		drm_mm_put_block(obj->stolen);
> +		obj->stolen = NULL;
> +	}
> +}
Chris Wilson Nov. 5, 2012, 4:59 p.m. UTC | #2
On Mon, 5 Nov 2012 16:32:26 +0000, Ben Widawsky <ben@bwidawsk.net> wrote:
> On Fri, 19 Oct 2012 18:03:19 +0100
> Chris Wilson <chris@chris-wilson.co.uk> wrote:
> 
> > Allow for the creation of GEM objects backed by stolen memory. As these
> > are not backed by ordinary pages, we create a fake dma mapping and store
> > the address in the scatterlist rather than obj->pages.
> > 
> > v2: Mark _i915_gem_object_create_stolen() as static, as noticed by Jesse
> > Barnes.
> > 
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
> 
> Deferring on an r-b for now until I understand the point of most of this
> patch.

The stolen support is a precursor for fastboot, where we need to wrap
the allocations made by the BIOS from the stolen memory and reuse that
for our own framebuffers.
 
> > +	struct scatterlist *sg;
> > +
> 
> BUG_ON(offset + size <= dev_priv->mm.gtt->stolen_size);

Done with a minor amendment.
 
> > +	/* We hide that we have no struct page backing our stolen object
> > +	 * by wrapping the contiguous physical allocation with a fake
> > +	 * dma mapping in a single scatterlist.
> > +	 */
> > +
> > +	st = kmalloc(sizeof(*st), GFP_KERNEL);
> > +	if (st == NULL)
> > +		return NULL;
> > +
> > +	if (!sg_alloc_table(st, 1, GFP_KERNEL)) {
Fixed.

> > +		kfree(st);
> > +		return NULL;
> > +	}
> > +
> > +	sg = st->sgl;
> > +	sg->offset = offset;
> > +	sg->length = size;
> > +
> > +	sg_dma_address(sg) = dev_priv->mm.stolen_base + offset;
> > +	sg_dma_len(sg) = size;
> > +
> 
> Do we want to make stolen_base a dma_addr_t (or at least typecast it)?

Interesting enough, the current FBC registers are limited to only using
32bit addresses, so stolen_base atm is not technically a dma_addr_t.
Maybe I'm picking hairs. :)
 
> > +	return st;
> > +}
> > +
> > +static int i915_gem_object_get_pages_stolen(struct drm_i915_gem_object *obj)
> > +{
> > +	BUG();
> > +	return -EINVAL;
> > +}
> > +
> 
> __noreturn, or maybe just make .get_pages = NULL, and do the check in
> the upper layer get_pages?

I refer you to http://lwn.net/Articles/336262/ where the argument is put
forth that default no-op functions are preferrable in most cases to
interpretting special NULL vfuncs. We have adopted this elsewhere in
i915.ko to good effect.

> > +	stolen = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0);
> > +	if (stolen)
> > +		stolen = drm_mm_get_block(stolen, size, 4096);
> > +	if (stolen == NULL)
> > +		return NULL;
> 
> Could probably do slightly better here with ERR_PTR(-ENOMEM) but since
> we don't do that elsewhere, I guess it doesn't matter.

I was tempted - it would have just looked odd as being the only create
routine to do so. :)
-Chris
Ben Widawsky Nov. 5, 2012, 5:34 p.m. UTC | #3
On Mon, 05 Nov 2012 16:59:44 +0000
Chris Wilson <chris@chris-wilson.co.uk> wrote:

> On Mon, 5 Nov 2012 16:32:26 +0000, Ben Widawsky <ben@bwidawsk.net> wrote:
> > On Fri, 19 Oct 2012 18:03:19 +0100
> > Chris Wilson <chris@chris-wilson.co.uk> wrote:
> > 
> > > Allow for the creation of GEM objects backed by stolen memory. As these
> > > are not backed by ordinary pages, we create a fake dma mapping and store
> > > the address in the scatterlist rather than obj->pages.
> > > 
> > > v2: Mark _i915_gem_object_create_stolen() as static, as noticed by Jesse
> > > Barnes.
> > > 
> > > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > > Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
> > 
> > Deferring on an r-b for now until I understand the point of most of this
> > patch.
> 
> The stolen support is a precursor for fastboot, where we need to wrap
> the allocations made by the BIOS from the stolen memory and reuse that
> for our own framebuffers.
>  

For some reason I thought this should be simpler than it is, but Jesse
has successfully convinced me otherwise.
Reviewed-by: Ben Widawsky <ben@bwidawsk.net>

> > > +	struct scatterlist *sg;
> > > +
> > 
> > BUG_ON(offset + size <= dev_priv->mm.gtt->stolen_size);
> 
> Done with a minor amendment.
>  
> > > +	/* We hide that we have no struct page backing our stolen object
> > > +	 * by wrapping the contiguous physical allocation with a fake
> > > +	 * dma mapping in a single scatterlist.
> > > +	 */
> > > +
> > > +	st = kmalloc(sizeof(*st), GFP_KERNEL);
> > > +	if (st == NULL)
> > > +		return NULL;
> > > +
> > > +	if (!sg_alloc_table(st, 1, GFP_KERNEL)) {
> Fixed.
> 
> > > +		kfree(st);
> > > +		return NULL;
> > > +	}
> > > +
> > > +	sg = st->sgl;
> > > +	sg->offset = offset;
> > > +	sg->length = size;
> > > +
> > > +	sg_dma_address(sg) = dev_priv->mm.stolen_base + offset;
> > > +	sg_dma_len(sg) = size;
> > > +
> > 
> > Do we want to make stolen_base a dma_addr_t (or at least typecast it)?
> 
> Interesting enough, the current FBC registers are limited to only using
> 32bit addresses, so stolen_base atm is not technically a dma_addr_t.
> Maybe I'm picking hairs. :)
>  
> > > +	return st;
> > > +}
> > > +
> > > +static int i915_gem_object_get_pages_stolen(struct drm_i915_gem_object *obj)
> > > +{
> > > +	BUG();
> > > +	return -EINVAL;
> > > +}
> > > +
> > 
> > __noreturn, or maybe just make .get_pages = NULL, and do the check in
> > the upper layer get_pages?
> 
> I refer you to http://lwn.net/Articles/336262/ where the argument is put
> forth that default no-op functions are preferrable in most cases to
> interpretting special NULL vfuncs. We have adopted this elsewhere in
> i915.ko to good effect.
> 
> > > +	stolen = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0);
> > > +	if (stolen)
> > > +		stolen = drm_mm_get_block(stolen, size, 4096);
> > > +	if (stolen == NULL)
> > > +		return NULL;
> > 
> > Could probably do slightly better here with ERR_PTR(-ENOMEM) but since
> > we don't do that elsewhere, I guess it doesn't matter.
> 
> I was tempted - it would have just looked odd as being the only create
> routine to do so. :)
> -Chris
>
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 6b893c7..5a0e0cd 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1526,6 +1526,9 @@  int i915_gem_init_stolen(struct drm_device *dev);
 int i915_gem_stolen_setup_compression(struct drm_device *dev);
 void i915_gem_stolen_cleanup_compression(struct drm_device *dev);
 void i915_gem_cleanup_stolen(struct drm_device *dev);
+struct drm_i915_gem_object *
+i915_gem_object_create_stolen(struct drm_device *dev, u32 size);
+void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj);
 
 /* i915_gem_tiling.c */
 void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index d75c5c3..0349899 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -3857,6 +3857,7 @@  void i915_gem_free_object(struct drm_gem_object *gem_obj)
 	obj->pages_pin_count = 0;
 	i915_gem_object_put_pages(obj);
 	i915_gem_object_free_mmap_offset(obj);
+	i915_gem_object_release_stolen(obj);
 
 	BUG_ON(obj->pages);
 
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index 68ef22a..86c4af4 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -204,3 +204,125 @@  int i915_gem_init_stolen(struct drm_device *dev)
 
 	return 0;
 }
+
+static struct sg_table *
+i915_pages_create_for_stolen(struct drm_device *dev,
+			     u32 offset, u32 size)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct sg_table *st;
+	struct scatterlist *sg;
+
+	/* We hide that we have no struct page backing our stolen object
+	 * by wrapping the contiguous physical allocation with a fake
+	 * dma mapping in a single scatterlist.
+	 */
+
+	st = kmalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL)
+		return NULL;
+
+	if (!sg_alloc_table(st, 1, GFP_KERNEL)) {
+		kfree(st);
+		return NULL;
+	}
+
+	sg = st->sgl;
+	sg->offset = offset;
+	sg->length = size;
+
+	sg_dma_address(sg) = dev_priv->mm.stolen_base + offset;
+	sg_dma_len(sg) = size;
+
+	return st;
+}
+
+static int i915_gem_object_get_pages_stolen(struct drm_i915_gem_object *obj)
+{
+	BUG();
+	return -EINVAL;
+}
+
+static void i915_gem_object_put_pages_stolen(struct drm_i915_gem_object *obj)
+{
+	/* Should only be called during free */
+	sg_free_table(obj->pages);
+	kfree(obj->pages);
+}
+
+static const struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = {
+	.get_pages = i915_gem_object_get_pages_stolen,
+	.put_pages = i915_gem_object_put_pages_stolen,
+};
+
+static struct drm_i915_gem_object *
+_i915_gem_object_create_stolen(struct drm_device *dev,
+			       struct drm_mm_node *stolen)
+{
+	struct drm_i915_gem_object *obj;
+
+	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+	if (obj == NULL)
+		return NULL;
+
+	if (drm_gem_private_object_init(dev, &obj->base, stolen->size))
+		goto cleanup;
+
+	i915_gem_object_init(obj, &i915_gem_object_stolen_ops);
+
+	obj->pages = i915_pages_create_for_stolen(dev,
+						  stolen->start, stolen->size);
+	if (obj->pages == NULL)
+		goto cleanup;
+
+	obj->has_dma_mapping = true;
+	obj->pages_pin_count = 1;
+	obj->stolen = stolen;
+
+	obj->base.write_domain = I915_GEM_DOMAIN_GTT;
+	obj->base.read_domains = I915_GEM_DOMAIN_GTT;
+	obj->cache_level = I915_CACHE_NONE;
+
+	return obj;
+
+cleanup:
+	kfree(obj);
+	return NULL;
+}
+
+struct drm_i915_gem_object *
+i915_gem_object_create_stolen(struct drm_device *dev, u32 size)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_i915_gem_object *obj;
+	struct drm_mm_node *stolen;
+
+	if (dev_priv->mm.stolen_base == 0)
+		return 0;
+
+	DRM_DEBUG_KMS("creating stolen object: size=%x\n", size);
+	if (size == 0)
+		return NULL;
+
+	stolen = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0);
+	if (stolen)
+		stolen = drm_mm_get_block(stolen, size, 4096);
+	if (stolen == NULL)
+		return NULL;
+
+	obj = _i915_gem_object_create_stolen(dev, stolen);
+	if (obj)
+		return obj;
+
+	drm_mm_put_block(stolen);
+	return NULL;
+}
+
+void
+i915_gem_object_release_stolen(struct drm_i915_gem_object *obj)
+{
+	if (obj->stolen) {
+		drm_mm_put_block(obj->stolen);
+		obj->stolen = NULL;
+	}
+}