[PATCH v3 01/19] drm: Add |struct drm_gem_vram_object| and helpers

Sam Ravnborg sam at ravnborg.org
Mon Apr 29 19:58:55 UTC 2019


Hi Thomas.

Some minor things and some bikeshedding too.

One general^Wbikeshedding thing - unint32_t is used in many places.
And then s64 in one place.
Seems like two concepts are mixed.
Maybe be consistent and use u32, s32 everywhere?

I did not read the code carefully enough to understand it.
I cannot give a r-b or a-b - as I feel I need to understand it to do
so.

	Sam

On Mon, Apr 29, 2019 at 04:43:23PM +0200, Thomas Zimmermann wrote:
> The type |struct drm_gem_vram_object| implements a GEM object for simple
> framebuffer devices with dedicated video memory. The BO is either located
> in VRAM or system memory.
> 
> The implementation has been created from the respective code in ast,
> bochs and mgag200. These drivers copy their implementation from each
> other; except for the names of several data types. The helpers are
> currently build with TTM, but this is considered an implementation
> detail and may change in future updates.
> 
> v2:
> 	* rename to |struct drm_gem_vram_object|
> 	* move drm_is_gem_ttm() to a later patch in the series
> 	* add drm_gem_vram_kmap_at()
> 	* return is_iomem from kmap functions
> 	* redefine TTM placement flags for public interface
> 	* documentation fixes
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de>
> ---
>  Documentation/gpu/drm-mm.rst             |  12 +
>  drivers/gpu/drm/Kconfig                  |  13 +
>  drivers/gpu/drm/Makefile                 |   4 +
>  drivers/gpu/drm/drm_gem_vram_helper.c    | 410 +++++++++++++++++++++++
>  drivers/gpu/drm/drm_vram_helper_common.c |   6 +
>  include/drm/drm_gem_vram_helper.h        |  92 +++++
>  6 files changed, 537 insertions(+)
>  create mode 100644 drivers/gpu/drm/drm_gem_vram_helper.c
>  create mode 100644 drivers/gpu/drm/drm_vram_helper_common.c
>  create mode 100644 include/drm/drm_gem_vram_helper.h
> 
> diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst
> index 54a696d961a7..d5327ed608d7 100644
> --- a/Documentation/gpu/drm-mm.rst
> +++ b/Documentation/gpu/drm-mm.rst
> @@ -380,6 +380,18 @@ GEM CMA Helper Functions Reference
>  .. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
>     :export:
>  
> +GEM VRAM Helper Functions Reference
> +----------------------------------
> +
> +.. kernel-doc:: drivers/gpu/drm/drm_gem_vram_helper.c
> +   :doc: overview
> +
> +.. kernel-doc:: include/drm/drm_gem_vram_helper.h
> +   :internal:
> +
> +.. kernel-doc:: drivers/gpu/drm/drm_gem_vram_helper.c
> +   :export:
> +
>  VMA Offset Manager
>  ==================
>  
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 2267e84d5cb4..c0d49a6c09d2 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -160,6 +160,12 @@ config DRM_TTM
>  	  GPU memory types. Will be enabled automatically if a device driver
>  	  uses it.
>  
> +config DRM_VRAM_HELPER
> +	tristate
> +	depends on DRM && DRM_TTM
> +	help
> +	  Helpers for VRAM memory management
> +
>  config DRM_GEM_CMA_HELPER
>  	bool
>  	depends on DRM
> @@ -179,6 +185,13 @@ config DRM_GEM_SHMEM_HELPER
>  	help
>  	  Choose this if you need the GEM shmem helper functions
>  
> +config DRM_GEM_VRAM_HELPER
> +	bool
> +	depends on DRM
> +	select DRM_VRAM_HELPER
> +	help
> +	  Choose this if you need the GEM VRAM helper functions
> +
I cannot remember how select will deal with symbols whos has
a  "depends on".
But if I recall correct then the "depends on" will be ignored
or in best case trigger a warning.
In other words - when we have symbols we select they should not
have a depends on.

What can be done is something like:

symbol foo
	bool

symbol bar
	depends on foo


symbol foobar
	bool "This is what you need - select me"
	select foo

So when one chooses "foobar" then we will select "foo" and this will
satisfy bar.

But maybe this rambling is irrelevant - maybe check what we do with
other selectable symbols in DRM.


> +/**
> + * DOC: overview
> + *
> + * This library provides a GEM object that is backed by VRAM. It
> + * can be used for simple framebuffer devices with dedicated memory.
> + */
It is likely only me, but...
I could use a short explanation what is GEM and maybe also VRAM.

VRAM as video RAM, but maybe there is more constraints?
(When I first looked at DRM I wondered what you used Virtual RAM for.
But thats a long time ago so it counts only as a funny story.

> + * Buffer-objects helpers
> + */
> +
> +static void drm_gem_vram_cleanup(struct drm_gem_vram_object *gbo)
> +{
> +	/* We got here via ttm_bo_put(), which means that the
> +	 * TTM buffer object in 'bo' has already been cleaned
> +	 * up; only release the GEM object. */
> +	drm_gem_object_release(&gbo->gem);
> +}
> +
> +static void drm_gem_vram_destroy(struct drm_gem_vram_object *gbo)
> +{
> +	drm_gem_vram_cleanup(gbo);
> +	kfree(gbo);
> +}
> +
> +static void ttm_buffer_object_destroy(struct ttm_buffer_object *bo)
> +{
> +	struct drm_gem_vram_object *gbo = drm_gem_vram_of_bo(bo);
> +	drm_gem_vram_destroy(gbo);
> +}
> +
> +static void drm_gem_vram_placement(struct drm_gem_vram_object *gbo, int pl_flag)
> +{
> +	unsigned int i;
> +	unsigned int c = 0;
> +
> +	gbo->placement.placement = gbo->placements;
> +	gbo->placement.busy_placement = gbo->placements;
> +
> +	if (pl_flag & TTM_PL_FLAG_VRAM)
> +		gbo->placements[c++].flags = TTM_PL_FLAG_WC |
> +					     TTM_PL_FLAG_UNCACHED |
> +					     TTM_PL_FLAG_VRAM;
> +
> +	if (pl_flag & TTM_PL_FLAG_SYSTEM)
> +		gbo->placements[c++].flags = TTM_PL_MASK_CACHING |
> +					     TTM_PL_FLAG_SYSTEM;
> +
> +	if (!c)
> +		gbo->placements[c++].flags = TTM_PL_MASK_CACHING |
> +					     TTM_PL_FLAG_SYSTEM;
> +
> +	gbo->placement.num_placement = c;
> +	gbo->placement.num_busy_placement = c;
> +
> +	for (i = 0; i < c; ++i) {
> +		gbo->placements[i].fpfn = 0;
> +		gbo->placements[i].lpfn = 0;
> +	}
> +}
> +
> +static int drm_gem_vram_init(struct drm_device *dev,
> +			     struct ttm_bo_device *bdev,
> +			     struct drm_gem_vram_object *gbo,
> +			     unsigned long size, uint32_t pg_align,
> +			     bool interruptible)
> +{
> +	int ret;
> +	size_t acc_size;
> +
> +	ret = drm_gem_object_init(dev, &gbo->gem, size);
> +	if (ret)
> +		return ret;
> +
> +	acc_size = ttm_bo_dma_acc_size(bdev, size, sizeof(*gbo));
> +
> +	gbo->bo.bdev = bdev;
> +	drm_gem_vram_placement(gbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
> +
> +	ret = ttm_bo_init(bdev, &gbo->bo, size, ttm_bo_type_device,
> +			  &gbo->placement, pg_align, interruptible, acc_size,
> +			  NULL, NULL, ttm_buffer_object_destroy);
> +	if (ret)
> +		goto err_drm_gem_object_release;
> +
> +	return 0;
> +
> +err_drm_gem_object_release:
> +	drm_gem_object_release(&gbo->gem);
> +	return ret;
> +}
> +
> +/**
> + * drm_gem_vram_create() - Creates a VRAM-backed GEM object
> + * @dev:		the DRM device
> + * @bdev:		the TTM BO device backing the object
> + * @size:		the buffer size in bytes
> + * @pg_align:		the buffer's alignment in multiples of the page size
> + * @interruptible:	sleep interruptible if waiting for memory
> + *
> + * Returns:
> + * A new instance of &struct drm_gem_vram_object on success, or
> + * an ERR_PTR()-encoded error code otherwise.
> + */
> +struct drm_gem_vram_object* drm_gem_vram_create(struct drm_device *dev,
> +						struct ttm_bo_device *bdev,
> +						unsigned long size,
> +						uint32_t pg_align,
> +						bool interruptible)
> +{
> +	struct drm_gem_vram_object *gbo;
> +	int ret;
> +
> +	gbo = kzalloc(sizeof(*gbo), GFP_KERNEL);
> +	if (!gbo)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ret = drm_gem_vram_init(dev, bdev, gbo, size, pg_align, interruptible);
> +	if (ret < 0)
> +		goto err_kfree;
> +
> +	return gbo;
> +
> +err_kfree:
> +	kfree(gbo);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL(drm_gem_vram_create);
> +
> +/**
> + * drm_gem_vram_put() - Releases a reference to a VRAM-backed GEM object
> + * @gbo:	the GEM VRAM object
> + *
> + * See ttm_bo_put() for more information.
> + */
> +void drm_gem_vram_put(struct drm_gem_vram_object *gbo)
> +{
> +	ttm_bo_put(&gbo->bo);
> +}
> +EXPORT_SYMBOL(drm_gem_vram_put);
> +
> +/**
> + * drm_gem_vram_reserve() - Reserves a VRAM-backed GEM object
> + * @gbo:	the GEM VRAM object
> + * @no_wait:	don't wait for buffer object to become available
> + *
> + * See ttm_bo_reserve() for more information.
> + *
> + * Returns:
> + * 0 on success, or
> + * a negative error code otherwise
> + */
> +int drm_gem_vram_reserve(struct drm_gem_vram_object *gbo, bool no_wait)
> +{
> +	return ttm_bo_reserve(&gbo->bo, true, no_wait, NULL);
> +}
> +EXPORT_SYMBOL(drm_gem_vram_reserve);
> +
> +/**
> + * drm_gem_vram_unreserve() - \
> +	Release a reservation acquired by drm_gem_vram_reserve()
> + * @gbo:	the GEM VRAM object
> + *
> + * See ttm_bo_unreserve() for more information.
> + */
> +void drm_gem_vram_unreserve(struct drm_gem_vram_object *gbo)
> +{
> +	ttm_bo_unreserve(&gbo->bo);
> +}
> +EXPORT_SYMBOL(drm_gem_vram_unreserve);
> +
> +/**
> + * drm_gem_vram_mmap_offset() - Returns a GEM VRAM object's mmap offset
> + * @gbo:	the GEM VRAM object
> + *
> + * See drm_vma_node_offset_addr() for more information.
> + *
> + * Returns:
> + * The buffer object's offset for userspace mappings on success, or
> + * 0 if no offset is allocated.
> + */
> +u64 drm_gem_vram_mmap_offset(struct drm_gem_vram_object *gbo)
> +{
> +	return drm_vma_node_offset_addr(&gbo->bo.vma_node);
> +}
> +EXPORT_SYMBOL(drm_gem_vram_mmap_offset);
> +
> +/**
> + * drm_gem_vram_offset() - \
> +	Returns a GEM VRAM object's offset in video memory
> + * @gbo:	the GEM VRAM object
> + *
> + * This function returns the buffer object's offset in the device's video
> + * memory. The buffer object has to be pinned to %TTM_PL_VRAM.
> + *
> + * Returns:
> + * The buffer object's offset in video memory on success, or
> + * a negative error code otherwise.
> + */
> +s64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo)
> +{
> +	if (!gbo->pin_count)
> +		return (s64)-ENODEV;
> +	return gbo->bo.offset;
> +}
> +EXPORT_SYMBOL(drm_gem_vram_offset);
> +
> +/**
> + * drm_gem_vram_pin() - Pins a GEM VRAM object in a region.
> + * @gbo:	the GEM VRAM object
> + * @pl_flag:	a bitmask of possible memory regions
> + *
> + * Pinning a buffer object ensures that it is not evicted from
> + * a memory region. A pinned buffer object has to be unpinned before
> + * it can be pinned to another region.
> + *
> + * Returns:
> + * 0 on success, or
> + * a negative error code otherwise.
> + */
> +int drm_gem_vram_pin(struct drm_gem_vram_object *gbo, u32 pl_flag)
> +{
> +	int i, ret;
> +	struct ttm_operation_ctx ctx = { false, false };
> +
> +	if (gbo->pin_count) {
> +		++gbo->pin_count;
> +		return 0;
> +	}
> +
> +	drm_gem_vram_placement(gbo, pl_flag);
> +	for (i = 0; i < gbo->placement.num_placement; ++i)
> +		gbo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
> +
> +	ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx);
> +	if (ret < 0)
> +		return ret;
> +
> +	gbo->pin_count = 1;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_gem_vram_pin);
> +
> +/**
> + * drm_gem_vram_unpin() - Unpins a GEM VRAM object
> + * @gbo:	the GEM VRAM object
> + *
> + * Returns:
> + * 0 on success, or
> + * a negative error code otherwise.
> + */
> +int drm_gem_vram_unpin(struct drm_gem_vram_object *gbo)
> +{
> +	int i, ret;
> +	struct ttm_operation_ctx ctx = { false, false };
> +
> +	if (!gbo->pin_count)
> +		return 0;
> +
> +	--gbo->pin_count;
> +	if (gbo->pin_count)
> +		return 0;
> +
> +	for (i = 0; i < gbo->placement.num_placement ; ++i)
> +		gbo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT;
> +
> +	ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_gem_vram_unpin);
> +
> +/**
> + * drm_gem_vram_push_to_system() - \
> +	Unpins a GEM VRAM object and moves it to system memory
> + * @gbo:	the GEM VRAM object
> + *
> + * This operation only works if the caller holds the final pin on the
> + * buffer object.
> + *
> + * Returns:
> + * 0 on success, or
> + * a negative error code otherwise.
> + */
> +int drm_gem_vram_push_to_system(struct drm_gem_vram_object *gbo)
> +{
> +	int i, ret;
> +	struct ttm_operation_ctx ctx = { false, false };
> +
> +	if (!gbo->pin_count)
> +		return 0;
> +
> +	--gbo->pin_count;
> +	if (gbo->pin_count)
> +		return 0;
> +
> +	if (gbo->kmap.virtual)
> +		ttm_bo_kunmap(&gbo->kmap);
> +
> +	drm_gem_vram_placement(gbo, TTM_PL_FLAG_SYSTEM);
> +	for (i = 0; i < gbo->placement.num_placement ; ++i)
> +		gbo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
> +
> +	ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_gem_vram_push_to_system);
> +
> +/**
> + * drm_gem_vram_kmap_at() - Maps a GEM VRAM object into kernel address space
> + * @gbo:	the GEM VRAM object
> + * @map:	establish a mapping if necessary
> + * @is_iomem:	returns true if the mapped memory is I/O memory, or false \
> +	otherwise; can be NULL
> + * @kmap:	the mapping's kmap object
> + *
> + * This function maps the buffer object into the kernel's address space
> + * or returns the current mapping. If the parameter map is false, the
> + * function only queries the current mapping, but does not establish a
> + * new one.
> + *
> + * Returns:
> + * The buffers virtual address if mapped, or
> + * NULL if not mapped, or
> + * an ERR_PTR()-encoded error code otherwise.
> + */
> +void* drm_gem_vram_kmap_at(struct drm_gem_vram_object *gbo, bool map,
> +			   bool *is_iomem, struct ttm_bo_kmap_obj *kmap)
> +{
> +	int ret;
> +
> +	if (kmap->virtual || !map)
> +		goto out;
> +
> +	ret = ttm_bo_kmap(&gbo->bo, 0, gbo->bo.num_pages, kmap);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +out:
> +	if (!is_iomem) {
> +		return kmap->virtual;
> +	}
> +	if (!kmap->virtual) {
> +		*is_iomem = false;
> +		return NULL;
> +	}
> +	return ttm_kmap_obj_virtual(kmap, is_iomem);
> +}
> +EXPORT_SYMBOL(drm_gem_vram_kmap_at);
> +
> +/**
> + * drm_gem_vram_kmap() - Maps a GEM VRAM object into kernel address space
> + * @gbo:	the GEM VRAM object
> + * @map:	establish a mapping if necessary
> + * @is_iomem:	returns true if the mapped memory is I/O memory, or false \
> +	otherwise; can be NULL
> + *
> + * This function maps the buffer object into the kernel's address space
> + * or returns the current mapping. If the parameter map is false, the
> + * function only queries the current mapping, but does not establish a
> + * new one.
> + *
> + * Returns:
> + * The buffers virtual address if mapped, or
> + * NULL if not mapped, or
> + * an ERR_PTR()-encoded error code otherwise.
> + */
> +void* drm_gem_vram_kmap(struct drm_gem_vram_object *gbo, bool map,
> +			bool *is_iomem)
> +{
> +	return drm_gem_vram_kmap_at(gbo, map, is_iomem, &gbo->kmap);
> +}
> +EXPORT_SYMBOL(drm_gem_vram_kmap);
> +
> +/**
> + * drm_gem_vram_kunmap_at() - Unmaps a GEM VRAM object
> + * @gbo:	the GEM VRAM object
> + * @kmap:	the mapping's kmap object
> + */
> +void drm_gem_vram_kunmap_at(struct drm_gem_vram_object *gbo,
> +			    struct ttm_bo_kmap_obj *kmap)
> +{
> +	if (!kmap->virtual)
> +		return;
> +
> +	ttm_bo_kunmap(kmap);
> +	kmap->virtual = NULL;
> +}
> +EXPORT_SYMBOL(drm_gem_vram_kunmap_at);
> +
> +/**
> + * drm_gem_vram_kunmap() - Unmaps a GEM VRAM object
> + * @gbo:	the GEM VRAM object
> + */
> +void drm_gem_vram_kunmap(struct drm_gem_vram_object *gbo)
> +{
> +	drm_gem_vram_kunmap_at(gbo, &gbo->kmap);
> +}
> +EXPORT_SYMBOL(drm_gem_vram_kunmap);
> diff --git a/drivers/gpu/drm/drm_vram_helper_common.c b/drivers/gpu/drm/drm_vram_helper_common.c
> new file mode 100644
> index 000000000000..76b6569c9aad
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_vram_helper_common.c
> @@ -0,0 +1,6 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#include <linux/module.h>
> +
> +MODULE_DESCRIPTION("DRM VRAM memory-management helpers");
> +MODULE_LICENSE("GPL");
> diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h
> new file mode 100644
> index 000000000000..167616f552e5
> --- /dev/null
> +++ b/include/drm/drm_gem_vram_helper.h
> @@ -0,0 +1,92 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#ifndef DRM_GEM_VRAM_HELPER_H
> +#define DRM_GEM_VRAM_HELPER_H
> +
> +#include <drm/drm_gem.h>
> +#include <drm/ttm/ttm_bo_api.h>
> +#include <drm/ttm/ttm_placement.h>
> +#include <linux/kernel.h> /* for container_of() */
> +
> +struct filp;
> +
> +#define DRM_GEM_VRAM_PL_FLAG_VRAM	TTM_PL_FLAG_VRAM
> +#define DRM_GEM_VRAM_PL_FLAG_SYSTEM	TTM_PL_FLAG_SYSTEM
> +
> +/*
> + * Buffer-object helpers
> + */
> +
> +/**
> + * struct drm_gem_vram_object - GEM object backed by VRAM
> + * @gem:	GEM object
> + * @bo:		TTM buffer object
> + * @kmap:	Mapping information for @bo
> + * @placement:	TTM placement information. Supported placements are \
> +	%TTM_PL_VRAM and %TTM_PL_SYSTEM
> + * @placements:	TTM placement information.
> + * @pin_count:	Pin counter
> + *
> + * The type struct drm_gem_vram_object represents a GEM object that is
> + * backed by VRAM. It can be used for simple frambuffer devices with
> + * dedicated memory. The buffer object can be evicted to system memory if
> + * video memory becomes scarce.
> + */
> +struct drm_gem_vram_object {
> +        struct drm_gem_object gem;
> +        struct ttm_buffer_object bo;
> +        struct ttm_bo_kmap_obj kmap;
> +
> +	/* Supported placements are %TTM_PL_VRAM and %TTM_PL_SYSTEM */
> +        struct ttm_placement placement;
> +        struct ttm_place placements[3];
> +
> +        int pin_count;
> +};
Use tabs for indent - not spaces.
Ask checkpatch if anything similar needs to be adjusted.


> +
> +/**
> + * Returns the container of type &struct drm_gem_vram_object
> + * for field bo.
> + * @bo:		the VRAM buffer object
> + * Returns:	The containing GEM VRAM object
> + */
> +static inline struct drm_gem_vram_object* drm_gem_vram_of_bo(
> +	struct ttm_buffer_object *bo)
> +{
> +	return container_of(bo, struct drm_gem_vram_object, bo);
> +}
Indent funny. USe same indent as used in other parts of file for
function arguments.

> +
> +/**
> + * Returns the container of type &struct drm_gem_vram_object
> + * for field gem.
> + * @gem:	the GEM object
> + * Returns:	The containing GEM VRAM object
> + */
> +static inline struct drm_gem_vram_object* drm_gem_vram_of_gem(
> +	struct drm_gem_object *gem)
> +{
> +	return container_of(gem, struct drm_gem_vram_object, gem);
> +}
ditto

> +
> +struct drm_gem_vram_object* drm_gem_vram_create(struct drm_device *dev,
> +						struct ttm_bo_device* bdev,
> +						unsigned long size,
> +						uint32_t pg_align,
> +						bool interruptible);

Here is is "normal"



More information about the dri-devel mailing list