diff mbox series

[RFC,v3,07/11] drm, cgroup: Add TTM buffer allocation stats

Message ID 20190626150522.11618-8-Kenny.Ho@amd.com (mailing list archive)
State New, archived
Headers show
Series new cgroup controller for gpu/drm subsystem | expand

Commit Message

Ho, Kenny June 26, 2019, 3:05 p.m. UTC
The drm resource being measured is the TTM (Translation Table Manager)
buffers.  TTM manages different types of memory that a GPU might access.
These memory types include dedicated Video RAM (VRAM) and host/system
memory accessible through IOMMU (GART/GTT).  TTM is currently used by
multiple drm drivers (amd, ast, bochs, cirrus, hisilicon, maga200,
nouveau, qxl, virtio, vmwgfx.)

drm.memory.stats
        A read-only nested-keyed file which exists on all cgroups.
        Each entry is keyed by the drm device's major:minor.  The
        following nested keys are defined.

          ======         =============================================
          system         Host/system memory
          tt             Host memory used by the drm device (GTT/GART)
          vram           Video RAM used by the drm device
          priv           Other drm device, vendor specific memory
          ======         =============================================

        Reading returns the following::

        226:0 system=0 tt=0 vram=0 priv=0
        226:1 system=0 tt=9035776 vram=17768448 priv=16809984
        226:2 system=0 tt=9035776 vram=17768448 priv=16809984

drm.memory.evict.stats
        A read-only flat-keyed file which exists on all cgroups.  Each
        entry is keyed by the drm device's major:minor.

        Total number of evictions.

Change-Id: Ice2c4cc845051229549bebeb6aa2d7d6153bdf6a
Signed-off-by: Kenny Ho <Kenny.Ho@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c |   3 +-
 drivers/gpu/drm/ttm/ttm_bo.c            |  30 +++++++
 drivers/gpu/drm/ttm/ttm_bo_util.c       |   4 +
 include/drm/drm_cgroup.h                |  19 ++++
 include/drm/ttm/ttm_bo_api.h            |   2 +
 include/drm/ttm/ttm_bo_driver.h         |   8 ++
 include/linux/cgroup_drm.h              |   4 +
 kernel/cgroup/drm.c                     | 113 ++++++++++++++++++++++++
 8 files changed, 182 insertions(+), 1 deletion(-)

Comments

Daniel Vetter June 26, 2019, 4:12 p.m. UTC | #1
On Wed, Jun 26, 2019 at 11:05:18AM -0400, Kenny Ho wrote:
> The drm resource being measured is the TTM (Translation Table Manager)
> buffers.  TTM manages different types of memory that a GPU might access.
> These memory types include dedicated Video RAM (VRAM) and host/system
> memory accessible through IOMMU (GART/GTT).  TTM is currently used by
> multiple drm drivers (amd, ast, bochs, cirrus, hisilicon, maga200,
> nouveau, qxl, virtio, vmwgfx.)
> 
> drm.memory.stats
>         A read-only nested-keyed file which exists on all cgroups.
>         Each entry is keyed by the drm device's major:minor.  The
>         following nested keys are defined.
> 
>           ======         =============================================
>           system         Host/system memory

Shouldn't that be covered by gem bo stats already? Also, system memory is
definitely something a lot of non-ttm drivers want to be able to track, so
that needs to be separate from ttm.

>           tt             Host memory used by the drm device (GTT/GART)
>           vram           Video RAM used by the drm device
>           priv           Other drm device, vendor specific memory

So what's "priv". In general I think we need some way to register the
different kinds of memory, e.g. stuff not in your list:

- multiple kinds of vram (like numa-style gpus)
- cma (for all those non-ttm drivers that's a big one, it's like system
  memory but also totally different)
- any carveouts and stuff
>           ======         =============================================
> 
>         Reading returns the following::
> 
>         226:0 system=0 tt=0 vram=0 priv=0
>         226:1 system=0 tt=9035776 vram=17768448 priv=16809984
>         226:2 system=0 tt=9035776 vram=17768448 priv=16809984
> 
> drm.memory.evict.stats
>         A read-only flat-keyed file which exists on all cgroups.  Each
>         entry is keyed by the drm device's major:minor.
> 
>         Total number of evictions.
> 
> Change-Id: Ice2c4cc845051229549bebeb6aa2d7d6153bdf6a
> Signed-off-by: Kenny Ho <Kenny.Ho@amd.com>

I think with all the ttm refactoring going on I think we need to de-ttm
the interface functions here a bit. With Gerd Hoffmans series you can just
use a gem_bo pointer here, so what's left to do is have some extracted
structure for tracking memory types. I think Brian Welty has some ideas
for this, even in patch form. Would be good to keep him on cc at least for
the next version. We'd need to explicitly hand in the ttm_mem_reg (or
whatever the specific thing is going to be).

-Daniel
> ---
>  drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c |   3 +-
>  drivers/gpu/drm/ttm/ttm_bo.c            |  30 +++++++
>  drivers/gpu/drm/ttm/ttm_bo_util.c       |   4 +
>  include/drm/drm_cgroup.h                |  19 ++++
>  include/drm/ttm/ttm_bo_api.h            |   2 +
>  include/drm/ttm/ttm_bo_driver.h         |   8 ++
>  include/linux/cgroup_drm.h              |   4 +
>  kernel/cgroup/drm.c                     | 113 ++++++++++++++++++++++++
>  8 files changed, 182 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
> index e9ecc3953673..a8dfc78ed45f 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
> @@ -1678,8 +1678,9 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
>  	mutex_init(&adev->mman.gtt_window_lock);
>  
>  	/* No others user of address space so set it to 0 */
> -	r = ttm_bo_device_init(&adev->mman.bdev,
> +	r = ttm_bo_device_init_tmp(&adev->mman.bdev,
>  			       &amdgpu_bo_driver,
> +			       adev->ddev,
>  			       adev->ddev->anon_inode->i_mapping,
>  			       adev->need_dma32);
>  	if (r) {
> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
> index 2845fceb2fbd..e9f70547f0ad 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo.c
> @@ -34,6 +34,7 @@
>  #include <drm/ttm/ttm_module.h>
>  #include <drm/ttm/ttm_bo_driver.h>
>  #include <drm/ttm/ttm_placement.h>
> +#include <drm/drm_cgroup.h>
>  #include <linux/jiffies.h>
>  #include <linux/slab.h>
>  #include <linux/sched.h>
> @@ -42,6 +43,7 @@
>  #include <linux/module.h>
>  #include <linux/atomic.h>
>  #include <linux/reservation.h>
> +#include <linux/cgroup_drm.h>
>  
>  static void ttm_bo_global_kobj_release(struct kobject *kobj);
>  
> @@ -151,6 +153,10 @@ static void ttm_bo_release_list(struct kref *list_kref)
>  	struct ttm_bo_device *bdev = bo->bdev;
>  	size_t acc_size = bo->acc_size;
>  
> +	if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all
> +		drmcgrp_unchg_mem(bo);
> +	put_drmcgrp(bo->drmcgrp);
> +
>  	BUG_ON(kref_read(&bo->list_kref));
>  	BUG_ON(kref_read(&bo->kref));
>  	BUG_ON(atomic_read(&bo->cpu_writers));
> @@ -353,6 +359,8 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
>  		if (bo->mem.mem_type == TTM_PL_SYSTEM) {
>  			if (bdev->driver->move_notify)
>  				bdev->driver->move_notify(bo, evict, mem);
> +			if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all
> +				drmcgrp_mem_track_move(bo, evict, mem);
>  			bo->mem = *mem;
>  			mem->mm_node = NULL;
>  			goto moved;
> @@ -361,6 +369,8 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
>  
>  	if (bdev->driver->move_notify)
>  		bdev->driver->move_notify(bo, evict, mem);
> +	if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all
> +		drmcgrp_mem_track_move(bo, evict, mem);
>  
>  	if (!(old_man->flags & TTM_MEMTYPE_FLAG_FIXED) &&
>  	    !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED))
> @@ -374,6 +384,8 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
>  		if (bdev->driver->move_notify) {
>  			swap(*mem, bo->mem);
>  			bdev->driver->move_notify(bo, false, mem);
> +			if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all
> +				drmcgrp_mem_track_move(bo, evict, mem);
>  			swap(*mem, bo->mem);
>  		}
>  
> @@ -1275,6 +1287,10 @@ int ttm_bo_init_reserved(struct ttm_bo_device *bdev,
>  		WARN_ON(!locked);
>  	}
>  
> +	bo->drmcgrp = get_drmcgrp(current);
> +	if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all
> +		drmcgrp_chg_mem(bo);
> +
>  	if (likely(!ret))
>  		ret = ttm_bo_validate(bo, placement, ctx);
>  
> @@ -1666,6 +1682,20 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
>  }
>  EXPORT_SYMBOL(ttm_bo_device_init);
>  
> +/* TODO merge with official function when implementation finalized*/
> +int ttm_bo_device_init_tmp(struct ttm_bo_device *bdev,
> +		struct ttm_bo_driver *driver,
> +		struct drm_device *ddev,
> +		struct address_space *mapping,
> +		bool need_dma32)
> +{
> +	int ret = ttm_bo_device_init(bdev, driver, mapping, need_dma32);
> +
> +	bdev->ddev = ddev;
> +	return ret;
> +}
> +EXPORT_SYMBOL(ttm_bo_device_init_tmp);
> +
>  /*
>   * buffer object vm functions.
>   */
> diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
> index 895d77d799e4..4ed7847c21f4 100644
> --- a/drivers/gpu/drm/ttm/ttm_bo_util.c
> +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
> @@ -32,6 +32,7 @@
>  #include <drm/ttm/ttm_bo_driver.h>
>  #include <drm/ttm/ttm_placement.h>
>  #include <drm/drm_vma_manager.h>
> +#include <drm/drm_cgroup.h>
>  #include <linux/io.h>
>  #include <linux/highmem.h>
>  #include <linux/wait.h>
> @@ -522,6 +523,9 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
>  	ret = reservation_object_trylock(fbo->base.resv);
>  	WARN_ON(!ret);
>  
> +	if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all
> +		drmcgrp_chg_mem(bo);
> +
>  	*new_obj = &fbo->base;
>  	return 0;
>  }
> diff --git a/include/drm/drm_cgroup.h b/include/drm/drm_cgroup.h
> index 8711b7c5f7bf..48ab5450cf17 100644
> --- a/include/drm/drm_cgroup.h
> +++ b/include/drm/drm_cgroup.h
> @@ -5,6 +5,7 @@
>  #define __DRM_CGROUP_H__
>  
>  #include <linux/cgroup_drm.h>
> +#include <drm/ttm/ttm_bo_api.h>
>  
>  #ifdef CONFIG_CGROUP_DRM
>  
> @@ -18,6 +19,11 @@ void drmcgrp_unchg_bo_alloc(struct drmcgrp *drmcgrp, struct drm_device *dev,
>  		size_t size);
>  bool drmcgrp_bo_can_allocate(struct task_struct *task, struct drm_device *dev,
>  		size_t size);
> +void drmcgrp_chg_mem(struct ttm_buffer_object *tbo);
> +void drmcgrp_unchg_mem(struct ttm_buffer_object *tbo);
> +void drmcgrp_mem_track_move(struct ttm_buffer_object *old_bo, bool evict,
> +		struct ttm_mem_reg *new_mem);
> +
>  #else
>  static inline int drmcgrp_register_device(struct drm_device *device)
>  {
> @@ -50,5 +56,18 @@ static inline bool drmcgrp_bo_can_allocate(struct task_struct *task,
>  {
>  	return true;
>  }
> +
> +static inline void drmcgrp_chg_mem(struct ttm_buffer_object *tbo)
> +{
> +}
> +
> +static inline void drmcgrp_unchg_mem(struct ttm_buffer_object *tbo)
> +{
> +}
> +
> +static inline void drmcgrp_mem_track_move(struct ttm_buffer_object *old_bo,
> +		bool evict, struct ttm_mem_reg *new_mem)
> +{
> +}
>  #endif /* CONFIG_CGROUP_DRM */
>  #endif /* __DRM_CGROUP_H__ */
> diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
> index 49d9cdfc58f2..ae1bb6daec81 100644
> --- a/include/drm/ttm/ttm_bo_api.h
> +++ b/include/drm/ttm/ttm_bo_api.h
> @@ -128,6 +128,7 @@ struct ttm_tt;
>   * struct ttm_buffer_object
>   *
>   * @bdev: Pointer to the buffer object device structure.
> + * @drmcgrp: DRM cgroup this object belongs to.
>   * @type: The bo type.
>   * @destroy: Destruction function. If NULL, kfree is used.
>   * @num_pages: Actual number of pages.
> @@ -174,6 +175,7 @@ struct ttm_buffer_object {
>  	 */
>  
>  	struct ttm_bo_device *bdev;
> +	struct drmcgrp *drmcgrp;
>  	enum ttm_bo_type type;
>  	void (*destroy) (struct ttm_buffer_object *);
>  	unsigned long num_pages;
> diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
> index c008346c2401..4cbcb41e5aa9 100644
> --- a/include/drm/ttm/ttm_bo_driver.h
> +++ b/include/drm/ttm/ttm_bo_driver.h
> @@ -30,6 +30,7 @@
>  #ifndef _TTM_BO_DRIVER_H_
>  #define _TTM_BO_DRIVER_H_
>  
> +#include <drm/drm_device.h>
>  #include <drm/drm_mm.h>
>  #include <drm/drm_vma_manager.h>
>  #include <linux/workqueue.h>
> @@ -442,6 +443,7 @@ extern struct ttm_bo_global {
>   * @driver: Pointer to a struct ttm_bo_driver struct setup by the driver.
>   * @man: An array of mem_type_managers.
>   * @vma_manager: Address space manager
> + * @ddev: Pointer to struct drm_device that this ttm_bo_device belongs to
>   * lru_lock: Spinlock that protects the buffer+device lru lists and
>   * ddestroy lists.
>   * @dev_mapping: A pointer to the struct address_space representing the
> @@ -460,6 +462,7 @@ struct ttm_bo_device {
>  	struct ttm_bo_global *glob;
>  	struct ttm_bo_driver *driver;
>  	struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES];
> +	struct drm_device *ddev;
>  
>  	/*
>  	 * Protected by internal locks.
> @@ -598,6 +601,11 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
>  		       struct address_space *mapping,
>  		       bool need_dma32);
>  
> +int ttm_bo_device_init_tmp(struct ttm_bo_device *bdev,
> +		       struct ttm_bo_driver *driver,
> +		       struct drm_device *ddev,
> +		       struct address_space *mapping,
> +		       bool need_dma32);
>  /**
>   * ttm_bo_unmap_virtual
>   *
> diff --git a/include/linux/cgroup_drm.h b/include/linux/cgroup_drm.h
> index e4400b21ab8e..141bea06f74c 100644
> --- a/include/linux/cgroup_drm.h
> +++ b/include/linux/cgroup_drm.h
> @@ -9,6 +9,7 @@
>  #include <linux/mutex.h>
>  #include <linux/cgroup.h>
>  #include <drm/drm_file.h>
> +#include <drm/ttm/ttm_placement.h>
>  
>  /* limit defined per the way drm_minor_alloc operates */
>  #define MAX_DRM_DEV (64 * DRM_MINOR_RENDER)
> @@ -22,6 +23,9 @@ struct drmcgrp_device_resource {
>  	size_t			bo_limits_peak_allocated;
>  
>  	s64			bo_stats_count_allocated;
> +
> +	s64			mem_stats[TTM_PL_PRIV+1];
> +	s64			mem_stats_evict;
>  };
>  
>  struct drmcgrp {
> diff --git a/kernel/cgroup/drm.c b/kernel/cgroup/drm.c
> index 9144f93b851f..5aee42a628c1 100644
> --- a/kernel/cgroup/drm.c
> +++ b/kernel/cgroup/drm.c
> @@ -8,6 +8,8 @@
>  #include <linux/mutex.h>
>  #include <linux/cgroup_drm.h>
>  #include <linux/kernel.h>
> +#include <drm/ttm/ttm_bo_api.h>
> +#include <drm/ttm/ttm_bo_driver.h>
>  #include <drm/drm_device.h>
>  #include <drm/drm_ioctl.h>
>  #include <drm/drm_cgroup.h>
> @@ -34,6 +36,8 @@ enum drmcgrp_res_type {
>  	DRMCGRP_TYPE_BO_TOTAL,
>  	DRMCGRP_TYPE_BO_PEAK,
>  	DRMCGRP_TYPE_BO_COUNT,
> +	DRMCGRP_TYPE_MEM,
> +	DRMCGRP_TYPE_MEM_EVICT,
>  };
>  
>  enum drmcgrp_file_type {
> @@ -42,6 +46,13 @@ enum drmcgrp_file_type {
>  	DRMCGRP_FTYPE_DEFAULT,
>  };
>  
> +static char const *ttm_placement_names[] = {
> +	[TTM_PL_SYSTEM] = "system",
> +	[TTM_PL_TT]     = "tt",
> +	[TTM_PL_VRAM]   = "vram",
> +	[TTM_PL_PRIV]   = "priv",
> +};
> +
>  /* indexed by drm_minor for access speed */
>  static struct drmcgrp_device	*known_drmcgrp_devs[MAX_DRM_DEV];
>  
> @@ -134,6 +145,7 @@ drmcgrp_css_alloc(struct cgroup_subsys_state *parent_css)
>  static inline void drmcgrp_print_stats(struct drmcgrp_device_resource *ddr,
>  		struct seq_file *sf, enum drmcgrp_res_type type)
>  {
> +	int i;
>  	if (ddr == NULL) {
>  		seq_puts(sf, "\n");
>  		return;
> @@ -149,6 +161,16 @@ static inline void drmcgrp_print_stats(struct drmcgrp_device_resource *ddr,
>  	case DRMCGRP_TYPE_BO_COUNT:
>  		seq_printf(sf, "%lld\n", ddr->bo_stats_count_allocated);
>  		break;
> +	case DRMCGRP_TYPE_MEM:
> +		for (i = 0; i <= TTM_PL_PRIV; i++) {
> +			seq_printf(sf, "%s=%lld ", ttm_placement_names[i],
> +					ddr->mem_stats[i]);
> +		}
> +		seq_puts(sf, "\n");
> +		break;
> +	case DRMCGRP_TYPE_MEM_EVICT:
> +		seq_printf(sf, "%lld\n", ddr->mem_stats_evict);
> +		break;
>  	default:
>  		seq_puts(sf, "\n");
>  		break;
> @@ -406,6 +428,18 @@ struct cftype files[] = {
>  		.private = DRMCG_CTF_PRIV(DRMCGRP_TYPE_BO_COUNT,
>  						DRMCGRP_FTYPE_STATS),
>  	},
> +	{
> +		.name = "memory.stats",
> +		.seq_show = drmcgrp_bo_show,
> +		.private = DRMCG_CTF_PRIV(DRMCGRP_TYPE_MEM,
> +						DRMCGRP_FTYPE_STATS),
> +	},
> +	{
> +		.name = "memory.evict.stats",
> +		.seq_show = drmcgrp_bo_show,
> +		.private = DRMCG_CTF_PRIV(DRMCGRP_TYPE_MEM_EVICT,
> +						DRMCGRP_FTYPE_STATS),
> +	},
>  	{ }	/* terminate */
>  };
>  
> @@ -555,3 +589,82 @@ void drmcgrp_unchg_bo_alloc(struct drmcgrp *drmcgrp, struct drm_device *dev,
>  	mutex_unlock(&known_drmcgrp_devs[devIdx]->mutex);
>  }
>  EXPORT_SYMBOL(drmcgrp_unchg_bo_alloc);
> +
> +void drmcgrp_chg_mem(struct ttm_buffer_object *tbo)
> +{
> +	struct drm_device *dev = tbo->bdev->ddev;
> +	struct drmcgrp *drmcgrp = tbo->drmcgrp;
> +	int devIdx = dev->primary->index;
> +	s64 size = (s64)(tbo->mem.size);
> +	int mem_type = tbo->mem.mem_type;
> +	struct drmcgrp_device_resource *ddr;
> +
> +	if (drmcgrp == NULL || known_drmcgrp_devs[devIdx] == NULL)
> +		return;
> +
> +	mem_type = mem_type > TTM_PL_PRIV ? TTM_PL_PRIV : mem_type;
> +
> +	mutex_lock(&known_drmcgrp_devs[devIdx]->mutex);
> +	for ( ; drmcgrp != NULL; drmcgrp = parent_drmcgrp(drmcgrp)) {
> +		ddr = drmcgrp->dev_resources[devIdx];
> +		ddr->mem_stats[mem_type] += size;
> +	}
> +	mutex_unlock(&known_drmcgrp_devs[devIdx]->mutex);
> +}
> +EXPORT_SYMBOL(drmcgrp_chg_mem);
> +
> +void drmcgrp_unchg_mem(struct ttm_buffer_object *tbo)
> +{
> +	struct drm_device *dev = tbo->bdev->ddev;
> +	struct drmcgrp *drmcgrp = tbo->drmcgrp;
> +	int devIdx = dev->primary->index;
> +	s64 size = (s64)(tbo->mem.size);
> +	int mem_type = tbo->mem.mem_type;
> +	struct drmcgrp_device_resource *ddr;
> +
> +	if (drmcgrp == NULL || known_drmcgrp_devs[devIdx] == NULL)
> +		return;
> +
> +	mem_type = mem_type > TTM_PL_PRIV ? TTM_PL_PRIV : mem_type;
> +
> +	mutex_lock(&known_drmcgrp_devs[devIdx]->mutex);
> +	for ( ; drmcgrp != NULL; drmcgrp = parent_drmcgrp(drmcgrp)) {
> +		ddr = drmcgrp->dev_resources[devIdx];
> +		ddr->mem_stats[mem_type] -= size;
> +	}
> +	mutex_unlock(&known_drmcgrp_devs[devIdx]->mutex);
> +}
> +EXPORT_SYMBOL(drmcgrp_unchg_mem);
> +
> +void drmcgrp_mem_track_move(struct ttm_buffer_object *old_bo, bool evict,
> +		struct ttm_mem_reg *new_mem)
> +{
> +	struct drm_device *dev = old_bo->bdev->ddev;
> +	struct drmcgrp *drmcgrp = old_bo->drmcgrp;
> +	s64 move_in_bytes = (s64)(old_bo->mem.size);
> +	int devIdx = dev->primary->index;
> +	int old_mem_type = old_bo->mem.mem_type;
> +	int new_mem_type = new_mem->mem_type;
> +	struct drmcgrp_device_resource *ddr;
> +	struct drmcgrp_device *known_dev;
> +
> +	known_dev = known_drmcgrp_devs[devIdx];
> +
> +	if (drmcgrp == NULL || known_dev == NULL)
> +		return;
> +
> +	old_mem_type = old_mem_type > TTM_PL_PRIV ? TTM_PL_PRIV : old_mem_type;
> +	new_mem_type = new_mem_type > TTM_PL_PRIV ? TTM_PL_PRIV : new_mem_type;
> +
> +	mutex_lock(&known_dev->mutex);
> +	for ( ; drmcgrp != NULL; drmcgrp = parent_drmcgrp(drmcgrp)) {
> +		ddr = drmcgrp->dev_resources[devIdx];
> +		ddr->mem_stats[old_mem_type] -= move_in_bytes;
> +		ddr->mem_stats[new_mem_type] += move_in_bytes;
> +
> +		if (evict)
> +			ddr->mem_stats_evict++;
> +	}
> +	mutex_unlock(&known_dev->mutex);
> +}
> +EXPORT_SYMBOL(drmcgrp_mem_track_move);
> -- 
> 2.21.0
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Kenny Ho June 27, 2019, 4:06 a.m. UTC | #2
On Wed, Jun 26, 2019 at 12:12 PM Daniel Vetter <daniel@ffwll.ch> wrote:
>
> On Wed, Jun 26, 2019 at 11:05:18AM -0400, Kenny Ho wrote:
> > drm.memory.stats
> >         A read-only nested-keyed file which exists on all cgroups.
> >         Each entry is keyed by the drm device's major:minor.  The
> >         following nested keys are defined.
> >
> >           ======         =============================================
> >           system         Host/system memory
>
> Shouldn't that be covered by gem bo stats already? Also, system memory is
> definitely something a lot of non-ttm drivers want to be able to track, so
> that needs to be separate from ttm.
The gem bo stats covers all of these type.  I am treat the gem stats
as more of the front end and a hard limit and this set of stats as the
backing store which can be of various type.  How does non-ttm drivers
identify various memory types?

> >           tt             Host memory used by the drm device (GTT/GART)
> >           vram           Video RAM used by the drm device
> >           priv           Other drm device, vendor specific memory
>
> So what's "priv". In general I think we need some way to register the
> different kinds of memory, e.g. stuff not in your list:
>
> - multiple kinds of vram (like numa-style gpus)
> - cma (for all those non-ttm drivers that's a big one, it's like system
>   memory but also totally different)
> - any carveouts and stuff
privs are vendor specific, which is why I have truncated it.  For
example, AMD has AMDGPU_PL_GDS, GWS, OA
https://elixir.bootlin.com/linux/v5.2-rc6/source/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h#L30

Since we are using keyed file type, we should be able to support
vendor specific memory type but I am not sure if this is acceptable to
cgroup upstream.  This is why I stick to the 3 memory type that is
common across all ttm drivers.

> I think with all the ttm refactoring going on I think we need to de-ttm
> the interface functions here a bit. With Gerd Hoffmans series you can just
> use a gem_bo pointer here, so what's left to do is have some extracted
> structure for tracking memory types. I think Brian Welty has some ideas
> for this, even in patch form. Would be good to keep him on cc at least for
> the next version. We'd need to explicitly hand in the ttm_mem_reg (or
> whatever the specific thing is going to be).

I assume Gerd Hoffman's series you are referring to is this one?
https://www.spinics.net/lists/dri-devel/msg215056.html

I can certainly keep an eye out for Gerd's refactoring while
refactoring other parts of this RFC.

I have added Brian and Gerd to the thread for awareness.

Regards,
Kenny
Daniel Vetter June 27, 2019, 6:01 a.m. UTC | #3
On Thu, Jun 27, 2019 at 12:06:13AM -0400, Kenny Ho wrote:
> On Wed, Jun 26, 2019 at 12:12 PM Daniel Vetter <daniel@ffwll.ch> wrote:
> >
> > On Wed, Jun 26, 2019 at 11:05:18AM -0400, Kenny Ho wrote:
> > > drm.memory.stats
> > >         A read-only nested-keyed file which exists on all cgroups.
> > >         Each entry is keyed by the drm device's major:minor.  The
> > >         following nested keys are defined.
> > >
> > >           ======         =============================================
> > >           system         Host/system memory
> >
> > Shouldn't that be covered by gem bo stats already? Also, system memory is
> > definitely something a lot of non-ttm drivers want to be able to track, so
> > that needs to be separate from ttm.
> The gem bo stats covers all of these type.  I am treat the gem stats
> as more of the front end and a hard limit and this set of stats as the
> backing store which can be of various type.  How does non-ttm drivers
> identify various memory types?

Not explicitly, they generally just have one. I think i915 currently has
two, system and carveout (with vram getting added).

> > >           tt             Host memory used by the drm device (GTT/GART)
> > >           vram           Video RAM used by the drm device
> > >           priv           Other drm device, vendor specific memory
> >
> > So what's "priv". In general I think we need some way to register the
> > different kinds of memory, e.g. stuff not in your list:
> >
> > - multiple kinds of vram (like numa-style gpus)
> > - cma (for all those non-ttm drivers that's a big one, it's like system
> >   memory but also totally different)
> > - any carveouts and stuff
> privs are vendor specific, which is why I have truncated it.  For
> example, AMD has AMDGPU_PL_GDS, GWS, OA
> https://elixir.bootlin.com/linux/v5.2-rc6/source/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h#L30
> 
> Since we are using keyed file type, we should be able to support
> vendor specific memory type but I am not sure if this is acceptable to
> cgroup upstream.  This is why I stick to the 3 memory type that is
> common across all ttm drivers.

I think we'll need custom memory pools, not just priv, and I guess some
naming scheme for them. I think just exposing them as amd-gws, amd-oa,
amd-gds would make sense.

Another thing I wonder about is multi-gpu cards, with multiple gpus and
each their own vram and other device-specific resources. For those we'd
have node0.vram and node1.vram too (on top of maybe an overall vram node,
not sure).

> > I think with all the ttm refactoring going on I think we need to de-ttm
> > the interface functions here a bit. With Gerd Hoffmans series you can just
> > use a gem_bo pointer here, so what's left to do is have some extracted
> > structure for tracking memory types. I think Brian Welty has some ideas
> > for this, even in patch form. Would be good to keep him on cc at least for
> > the next version. We'd need to explicitly hand in the ttm_mem_reg (or
> > whatever the specific thing is going to be).
> 
> I assume Gerd Hoffman's series you are referring to is this one?
> https://www.spinics.net/lists/dri-devel/msg215056.html

There's a newer one, much more complete, but yes that's the work.

> I can certainly keep an eye out for Gerd's refactoring while
> refactoring other parts of this RFC.
> 
> I have added Brian and Gerd to the thread for awareness.

btw just realized that maybe building the interfaces on top of ttm_mem_reg
is maybe not the best. That's what you're using right now, but in a way
that's just the ttm internal detail of how the backing storage is
allocated. I think the structure we need to abstract away is
ttm_mem_type_manager, without any of the actual management details.

btw reminds me: I guess it would be good to have a per-type .total
read-only exposed, so that userspace has an idea of how much there is?
ttm is trying to be agnostic to the allocator that's used to manage a
memory type/resource, so doesn't even know that. But I think something we
need to expose to admins, otherwise they can't meaningfully set limits.
-Daniel
Kenny Ho June 27, 2019, 8:17 p.m. UTC | #4
On Thu, Jun 27, 2019 at 2:01 AM Daniel Vetter <daniel@ffwll.ch> wrote:
>
> btw reminds me: I guess it would be good to have a per-type .total
> read-only exposed, so that userspace has an idea of how much there is?
> ttm is trying to be agnostic to the allocator that's used to manage a
> memory type/resource, so doesn't even know that. But I think something we
> need to expose to admins, otherwise they can't meaningfully set limits.

I don't think I understand this bit, do you mean total across multiple
GPU of the same mem type?  Or do you mean the total available per GPU
(or something else?)

Regards,
Kenny
Daniel Vetter June 27, 2019, 9:33 p.m. UTC | #5
On Thu, Jun 27, 2019 at 04:17:09PM -0400, Kenny Ho wrote:
> On Thu, Jun 27, 2019 at 2:01 AM Daniel Vetter <daniel@ffwll.ch> wrote:
> > btw reminds me: I guess it would be good to have a per-type .total
> > read-only exposed, so that userspace has an idea of how much there is?
> > ttm is trying to be agnostic to the allocator that's used to manage a
> > memory type/resource, so doesn't even know that. But I think something we
> > need to expose to admins, otherwise they can't meaningfully set limits.
> 
> I don't think I understand this bit, do you mean total across multiple
> GPU of the same mem type?  Or do you mean the total available per GPU
> (or something else?)

Total for a given type on a given cpu. E.g. maybe you want to give 50% of
your vram to one cgroup, and the other 50% to the other cgroup. For that
you need to know how much vram you have. And expecting people to lspci and
then look at wikipedia for how much vram that chip should have (or
something like that) isn't great. Hence 0.vram.total, 0.tt.total, and so
on (also for all the other gpu minors ofc).  For system memory we probably
don't want to provide a total, since that's already a value that's easy to
obtain from various sources.
-Daniel
Welty, Brian June 28, 2019, 1:16 a.m. UTC | #6
On 6/26/2019 11:01 PM, Daniel Vetter wrote:
> On Thu, Jun 27, 2019 at 12:06:13AM -0400, Kenny Ho wrote:
>> On Wed, Jun 26, 2019 at 12:12 PM Daniel Vetter <daniel@ffwll.ch> wrote:
>>>
>>> I think with all the ttm refactoring going on I think we need to de-ttm
>>> the interface functions here a bit. With Gerd Hoffmans series you can just
>>> use a gem_bo pointer here, so what's left to do is have some extracted
>>> structure for tracking memory types. I think Brian Welty has some ideas
>>> for this, even in patch form. Would be good to keep him on cc at least for
>>> the next version. We'd need to explicitly hand in the ttm_mem_reg (or
>>> whatever the specific thing is going to be).
>>
>> I assume Gerd Hoffman's series you are referring to is this one?
>> https://www.spinics.net/lists/dri-devel/msg215056.html
> 
> There's a newer one, much more complete, but yes that's the work.
> 
>> I can certainly keep an eye out for Gerd's refactoring while
>> refactoring other parts of this RFC.
>>
>> I have added Brian and Gerd to the thread for awareness.
> 
> btw just realized that maybe building the interfaces on top of ttm_mem_reg
> is maybe not the best. That's what you're using right now, but in a way
> that's just the ttm internal detail of how the backing storage is
> allocated. I think the structure we need to abstract away is
> ttm_mem_type_manager, without any of the actual management details.
> 

Any de-ttm refactoring should probably not spam all the cgroups folks.
So I removed cgroups list.

As Daniel mentioned, some of us are looking at possible refactoring of TTM
for reuse in i915 driver.
Here is a brief summary of some ideas to be considered:

 1) refactor part of ttm_mem_type_manager into a new drm_mem_type_region.
    Really, should then move the array from ttm_bo_device.man[] into drm_device.

    Relevant to drm_cgroup, you could then perhaps access these stats through
    drm_device and don't need the mem_stats array in drmcgrp_device_resource.

  1a)  doing this right means replacing TTM_PL_XXX memory types with new DRM
     defines.  But could keep the TTM ones as redefinition of (new) DRM ones.
     Probably those private ones (TTM_PL_PRIV) make this difficult.

  All of the above could be eventually leveraged by the vram support being
  implemented now in i915 driver.
  
  2) refactor ttm_mem_reg + ttm_bus_placement into something generic for
     any GEM object,  maybe call it drm_gem_object_placement.
     ttm_mem_reg could remain as a wrapper for TTM drivers.
     This hasn't been broadly discussed with intel-gfx folks, so not sure
     this fits well into i915 or not.

     Relevant to drm_cgroup, maybe this function:
	drmcgrp_mem_track_move(struct ttm_buffer_object *old_bo, bool evict,
		struct ttm_mem_reg *new_mem)
     could potentially become:
        drmcgrp_mem_track_move(struct drm_gem_object *old_bo, bool evict,
		struct drm_gem_object_placement *new_place)

     Though from ttm_mem_reg, you look to only be using mem_type and size.
     I think Daniel is noting that ttm_mem_reg wasn't truly needed here, so
     you could just pass in the mem_type and size instead.

Would appreciate any feedback (positive or negative) on above....
Perhaps this should move to a new thread?   I could send out basic RFC
patches for (1) if helpful but as it touches all the TTM drivers, nice to
hear some feedback first.
Anyway, this doesn't necessarily need to block forward progress on drm_cgroup,
as refactoring into common base structures could happen incrementally.

Thanks,
-Brian
Daniel Vetter June 28, 2019, 6:53 a.m. UTC | #7
On Fri, Jun 28, 2019 at 3:16 AM Welty, Brian <brian.welty@intel.com> wrote:
> On 6/26/2019 11:01 PM, Daniel Vetter wrote:
> > On Thu, Jun 27, 2019 at 12:06:13AM -0400, Kenny Ho wrote:
> >> On Wed, Jun 26, 2019 at 12:12 PM Daniel Vetter <daniel@ffwll.ch> wrote:
> >>>
> >>> I think with all the ttm refactoring going on I think we need to de-ttm
> >>> the interface functions here a bit. With Gerd Hoffmans series you can just
> >>> use a gem_bo pointer here, so what's left to do is have some extracted
> >>> structure for tracking memory types. I think Brian Welty has some ideas
> >>> for this, even in patch form. Would be good to keep him on cc at least for
> >>> the next version. We'd need to explicitly hand in the ttm_mem_reg (or
> >>> whatever the specific thing is going to be).
> >>
> >> I assume Gerd Hoffman's series you are referring to is this one?
> >> https://www.spinics.net/lists/dri-devel/msg215056.html
> >
> > There's a newer one, much more complete, but yes that's the work.
> >
> >> I can certainly keep an eye out for Gerd's refactoring while
> >> refactoring other parts of this RFC.
> >>
> >> I have added Brian and Gerd to the thread for awareness.
> >
> > btw just realized that maybe building the interfaces on top of ttm_mem_reg
> > is maybe not the best. That's what you're using right now, but in a way
> > that's just the ttm internal detail of how the backing storage is
> > allocated. I think the structure we need to abstract away is
> > ttm_mem_type_manager, without any of the actual management details.
> >
>
> Any de-ttm refactoring should probably not spam all the cgroups folks.
> So I removed cgroups list.
>
> As Daniel mentioned, some of us are looking at possible refactoring of TTM
> for reuse in i915 driver.
> Here is a brief summary of some ideas to be considered:
>
>  1) refactor part of ttm_mem_type_manager into a new drm_mem_type_region.
>     Really, should then move the array from ttm_bo_device.man[] into drm_device.
>
>     Relevant to drm_cgroup, you could then perhaps access these stats through
>     drm_device and don't need the mem_stats array in drmcgrp_device_resource.
>
>   1a)  doing this right means replacing TTM_PL_XXX memory types with new DRM
>      defines.  But could keep the TTM ones as redefinition of (new) DRM ones.
>      Probably those private ones (TTM_PL_PRIV) make this difficult.
>
>   All of the above could be eventually leveraged by the vram support being
>   implemented now in i915 driver.
>
>   2) refactor ttm_mem_reg + ttm_bus_placement into something generic for
>      any GEM object,  maybe call it drm_gem_object_placement.
>      ttm_mem_reg could remain as a wrapper for TTM drivers.
>      This hasn't been broadly discussed with intel-gfx folks, so not sure
>      this fits well into i915 or not.
>
>      Relevant to drm_cgroup, maybe this function:
>         drmcgrp_mem_track_move(struct ttm_buffer_object *old_bo, bool evict,
>                 struct ttm_mem_reg *new_mem)
>      could potentially become:
>         drmcgrp_mem_track_move(struct drm_gem_object *old_bo, bool evict,
>                 struct drm_gem_object_placement *new_place)
>
>      Though from ttm_mem_reg, you look to only be using mem_type and size.
>      I think Daniel is noting that ttm_mem_reg wasn't truly needed here, so
>      you could just pass in the mem_type and size instead.

Yeah I think the relevant part of your refactoring is creating a more
abstract memory type/resource thing (not the individual allocations
from it which ttm calls regions and I get confused about that every
time). I think that abstraction should also have a field for the total
(which I think cgroups also needs, both as read-only information and
starting value). ttm would that put somewhere into the
ttm_mem_type_manager, i915 would put it somewhere else, cma based
drivers could perhaps expose the cma heap like that (if it's exclusive
to the gpu at least).

> Would appreciate any feedback (positive or negative) on above....
> Perhaps this should move to a new thread?   I could send out basic RFC
> patches for (1) if helpful but as it touches all the TTM drivers, nice to
> hear some feedback first.
> Anyway, this doesn't necessarily need to block forward progress on drm_cgroup,
> as refactoring into common base structures could happen incrementally.

Yeah I think new dri-devel thread with a totally unpolished rfc as
draft plus this as the intro would be good. That way we can ground
this a bit better in actual code.
-Daniel
diff mbox series

Patch

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index e9ecc3953673..a8dfc78ed45f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -1678,8 +1678,9 @@  int amdgpu_ttm_init(struct amdgpu_device *adev)
 	mutex_init(&adev->mman.gtt_window_lock);
 
 	/* No others user of address space so set it to 0 */
-	r = ttm_bo_device_init(&adev->mman.bdev,
+	r = ttm_bo_device_init_tmp(&adev->mman.bdev,
 			       &amdgpu_bo_driver,
+			       adev->ddev,
 			       adev->ddev->anon_inode->i_mapping,
 			       adev->need_dma32);
 	if (r) {
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 2845fceb2fbd..e9f70547f0ad 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -34,6 +34,7 @@ 
 #include <drm/ttm/ttm_module.h>
 #include <drm/ttm/ttm_bo_driver.h>
 #include <drm/ttm/ttm_placement.h>
+#include <drm/drm_cgroup.h>
 #include <linux/jiffies.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
@@ -42,6 +43,7 @@ 
 #include <linux/module.h>
 #include <linux/atomic.h>
 #include <linux/reservation.h>
+#include <linux/cgroup_drm.h>
 
 static void ttm_bo_global_kobj_release(struct kobject *kobj);
 
@@ -151,6 +153,10 @@  static void ttm_bo_release_list(struct kref *list_kref)
 	struct ttm_bo_device *bdev = bo->bdev;
 	size_t acc_size = bo->acc_size;
 
+	if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all
+		drmcgrp_unchg_mem(bo);
+	put_drmcgrp(bo->drmcgrp);
+
 	BUG_ON(kref_read(&bo->list_kref));
 	BUG_ON(kref_read(&bo->kref));
 	BUG_ON(atomic_read(&bo->cpu_writers));
@@ -353,6 +359,8 @@  static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
 		if (bo->mem.mem_type == TTM_PL_SYSTEM) {
 			if (bdev->driver->move_notify)
 				bdev->driver->move_notify(bo, evict, mem);
+			if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all
+				drmcgrp_mem_track_move(bo, evict, mem);
 			bo->mem = *mem;
 			mem->mm_node = NULL;
 			goto moved;
@@ -361,6 +369,8 @@  static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
 
 	if (bdev->driver->move_notify)
 		bdev->driver->move_notify(bo, evict, mem);
+	if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all
+		drmcgrp_mem_track_move(bo, evict, mem);
 
 	if (!(old_man->flags & TTM_MEMTYPE_FLAG_FIXED) &&
 	    !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED))
@@ -374,6 +384,8 @@  static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
 		if (bdev->driver->move_notify) {
 			swap(*mem, bo->mem);
 			bdev->driver->move_notify(bo, false, mem);
+			if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all
+				drmcgrp_mem_track_move(bo, evict, mem);
 			swap(*mem, bo->mem);
 		}
 
@@ -1275,6 +1287,10 @@  int ttm_bo_init_reserved(struct ttm_bo_device *bdev,
 		WARN_ON(!locked);
 	}
 
+	bo->drmcgrp = get_drmcgrp(current);
+	if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all
+		drmcgrp_chg_mem(bo);
+
 	if (likely(!ret))
 		ret = ttm_bo_validate(bo, placement, ctx);
 
@@ -1666,6 +1682,20 @@  int ttm_bo_device_init(struct ttm_bo_device *bdev,
 }
 EXPORT_SYMBOL(ttm_bo_device_init);
 
+/* TODO merge with official function when implementation finalized*/
+int ttm_bo_device_init_tmp(struct ttm_bo_device *bdev,
+		struct ttm_bo_driver *driver,
+		struct drm_device *ddev,
+		struct address_space *mapping,
+		bool need_dma32)
+{
+	int ret = ttm_bo_device_init(bdev, driver, mapping, need_dma32);
+
+	bdev->ddev = ddev;
+	return ret;
+}
+EXPORT_SYMBOL(ttm_bo_device_init_tmp);
+
 /*
  * buffer object vm functions.
  */
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 895d77d799e4..4ed7847c21f4 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -32,6 +32,7 @@ 
 #include <drm/ttm/ttm_bo_driver.h>
 #include <drm/ttm/ttm_placement.h>
 #include <drm/drm_vma_manager.h>
+#include <drm/drm_cgroup.h>
 #include <linux/io.h>
 #include <linux/highmem.h>
 #include <linux/wait.h>
@@ -522,6 +523,9 @@  static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
 	ret = reservation_object_trylock(fbo->base.resv);
 	WARN_ON(!ret);
 
+	if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all
+		drmcgrp_chg_mem(bo);
+
 	*new_obj = &fbo->base;
 	return 0;
 }
diff --git a/include/drm/drm_cgroup.h b/include/drm/drm_cgroup.h
index 8711b7c5f7bf..48ab5450cf17 100644
--- a/include/drm/drm_cgroup.h
+++ b/include/drm/drm_cgroup.h
@@ -5,6 +5,7 @@ 
 #define __DRM_CGROUP_H__
 
 #include <linux/cgroup_drm.h>
+#include <drm/ttm/ttm_bo_api.h>
 
 #ifdef CONFIG_CGROUP_DRM
 
@@ -18,6 +19,11 @@  void drmcgrp_unchg_bo_alloc(struct drmcgrp *drmcgrp, struct drm_device *dev,
 		size_t size);
 bool drmcgrp_bo_can_allocate(struct task_struct *task, struct drm_device *dev,
 		size_t size);
+void drmcgrp_chg_mem(struct ttm_buffer_object *tbo);
+void drmcgrp_unchg_mem(struct ttm_buffer_object *tbo);
+void drmcgrp_mem_track_move(struct ttm_buffer_object *old_bo, bool evict,
+		struct ttm_mem_reg *new_mem);
+
 #else
 static inline int drmcgrp_register_device(struct drm_device *device)
 {
@@ -50,5 +56,18 @@  static inline bool drmcgrp_bo_can_allocate(struct task_struct *task,
 {
 	return true;
 }
+
+static inline void drmcgrp_chg_mem(struct ttm_buffer_object *tbo)
+{
+}
+
+static inline void drmcgrp_unchg_mem(struct ttm_buffer_object *tbo)
+{
+}
+
+static inline void drmcgrp_mem_track_move(struct ttm_buffer_object *old_bo,
+		bool evict, struct ttm_mem_reg *new_mem)
+{
+}
 #endif /* CONFIG_CGROUP_DRM */
 #endif /* __DRM_CGROUP_H__ */
diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
index 49d9cdfc58f2..ae1bb6daec81 100644
--- a/include/drm/ttm/ttm_bo_api.h
+++ b/include/drm/ttm/ttm_bo_api.h
@@ -128,6 +128,7 @@  struct ttm_tt;
  * struct ttm_buffer_object
  *
  * @bdev: Pointer to the buffer object device structure.
+ * @drmcgrp: DRM cgroup this object belongs to.
  * @type: The bo type.
  * @destroy: Destruction function. If NULL, kfree is used.
  * @num_pages: Actual number of pages.
@@ -174,6 +175,7 @@  struct ttm_buffer_object {
 	 */
 
 	struct ttm_bo_device *bdev;
+	struct drmcgrp *drmcgrp;
 	enum ttm_bo_type type;
 	void (*destroy) (struct ttm_buffer_object *);
 	unsigned long num_pages;
diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
index c008346c2401..4cbcb41e5aa9 100644
--- a/include/drm/ttm/ttm_bo_driver.h
+++ b/include/drm/ttm/ttm_bo_driver.h
@@ -30,6 +30,7 @@ 
 #ifndef _TTM_BO_DRIVER_H_
 #define _TTM_BO_DRIVER_H_
 
+#include <drm/drm_device.h>
 #include <drm/drm_mm.h>
 #include <drm/drm_vma_manager.h>
 #include <linux/workqueue.h>
@@ -442,6 +443,7 @@  extern struct ttm_bo_global {
  * @driver: Pointer to a struct ttm_bo_driver struct setup by the driver.
  * @man: An array of mem_type_managers.
  * @vma_manager: Address space manager
+ * @ddev: Pointer to struct drm_device that this ttm_bo_device belongs to
  * lru_lock: Spinlock that protects the buffer+device lru lists and
  * ddestroy lists.
  * @dev_mapping: A pointer to the struct address_space representing the
@@ -460,6 +462,7 @@  struct ttm_bo_device {
 	struct ttm_bo_global *glob;
 	struct ttm_bo_driver *driver;
 	struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES];
+	struct drm_device *ddev;
 
 	/*
 	 * Protected by internal locks.
@@ -598,6 +601,11 @@  int ttm_bo_device_init(struct ttm_bo_device *bdev,
 		       struct address_space *mapping,
 		       bool need_dma32);
 
+int ttm_bo_device_init_tmp(struct ttm_bo_device *bdev,
+		       struct ttm_bo_driver *driver,
+		       struct drm_device *ddev,
+		       struct address_space *mapping,
+		       bool need_dma32);
 /**
  * ttm_bo_unmap_virtual
  *
diff --git a/include/linux/cgroup_drm.h b/include/linux/cgroup_drm.h
index e4400b21ab8e..141bea06f74c 100644
--- a/include/linux/cgroup_drm.h
+++ b/include/linux/cgroup_drm.h
@@ -9,6 +9,7 @@ 
 #include <linux/mutex.h>
 #include <linux/cgroup.h>
 #include <drm/drm_file.h>
+#include <drm/ttm/ttm_placement.h>
 
 /* limit defined per the way drm_minor_alloc operates */
 #define MAX_DRM_DEV (64 * DRM_MINOR_RENDER)
@@ -22,6 +23,9 @@  struct drmcgrp_device_resource {
 	size_t			bo_limits_peak_allocated;
 
 	s64			bo_stats_count_allocated;
+
+	s64			mem_stats[TTM_PL_PRIV+1];
+	s64			mem_stats_evict;
 };
 
 struct drmcgrp {
diff --git a/kernel/cgroup/drm.c b/kernel/cgroup/drm.c
index 9144f93b851f..5aee42a628c1 100644
--- a/kernel/cgroup/drm.c
+++ b/kernel/cgroup/drm.c
@@ -8,6 +8,8 @@ 
 #include <linux/mutex.h>
 #include <linux/cgroup_drm.h>
 #include <linux/kernel.h>
+#include <drm/ttm/ttm_bo_api.h>
+#include <drm/ttm/ttm_bo_driver.h>
 #include <drm/drm_device.h>
 #include <drm/drm_ioctl.h>
 #include <drm/drm_cgroup.h>
@@ -34,6 +36,8 @@  enum drmcgrp_res_type {
 	DRMCGRP_TYPE_BO_TOTAL,
 	DRMCGRP_TYPE_BO_PEAK,
 	DRMCGRP_TYPE_BO_COUNT,
+	DRMCGRP_TYPE_MEM,
+	DRMCGRP_TYPE_MEM_EVICT,
 };
 
 enum drmcgrp_file_type {
@@ -42,6 +46,13 @@  enum drmcgrp_file_type {
 	DRMCGRP_FTYPE_DEFAULT,
 };
 
+static char const *ttm_placement_names[] = {
+	[TTM_PL_SYSTEM] = "system",
+	[TTM_PL_TT]     = "tt",
+	[TTM_PL_VRAM]   = "vram",
+	[TTM_PL_PRIV]   = "priv",
+};
+
 /* indexed by drm_minor for access speed */
 static struct drmcgrp_device	*known_drmcgrp_devs[MAX_DRM_DEV];
 
@@ -134,6 +145,7 @@  drmcgrp_css_alloc(struct cgroup_subsys_state *parent_css)
 static inline void drmcgrp_print_stats(struct drmcgrp_device_resource *ddr,
 		struct seq_file *sf, enum drmcgrp_res_type type)
 {
+	int i;
 	if (ddr == NULL) {
 		seq_puts(sf, "\n");
 		return;
@@ -149,6 +161,16 @@  static inline void drmcgrp_print_stats(struct drmcgrp_device_resource *ddr,
 	case DRMCGRP_TYPE_BO_COUNT:
 		seq_printf(sf, "%lld\n", ddr->bo_stats_count_allocated);
 		break;
+	case DRMCGRP_TYPE_MEM:
+		for (i = 0; i <= TTM_PL_PRIV; i++) {
+			seq_printf(sf, "%s=%lld ", ttm_placement_names[i],
+					ddr->mem_stats[i]);
+		}
+		seq_puts(sf, "\n");
+		break;
+	case DRMCGRP_TYPE_MEM_EVICT:
+		seq_printf(sf, "%lld\n", ddr->mem_stats_evict);
+		break;
 	default:
 		seq_puts(sf, "\n");
 		break;
@@ -406,6 +428,18 @@  struct cftype files[] = {
 		.private = DRMCG_CTF_PRIV(DRMCGRP_TYPE_BO_COUNT,
 						DRMCGRP_FTYPE_STATS),
 	},
+	{
+		.name = "memory.stats",
+		.seq_show = drmcgrp_bo_show,
+		.private = DRMCG_CTF_PRIV(DRMCGRP_TYPE_MEM,
+						DRMCGRP_FTYPE_STATS),
+	},
+	{
+		.name = "memory.evict.stats",
+		.seq_show = drmcgrp_bo_show,
+		.private = DRMCG_CTF_PRIV(DRMCGRP_TYPE_MEM_EVICT,
+						DRMCGRP_FTYPE_STATS),
+	},
 	{ }	/* terminate */
 };
 
@@ -555,3 +589,82 @@  void drmcgrp_unchg_bo_alloc(struct drmcgrp *drmcgrp, struct drm_device *dev,
 	mutex_unlock(&known_drmcgrp_devs[devIdx]->mutex);
 }
 EXPORT_SYMBOL(drmcgrp_unchg_bo_alloc);
+
+void drmcgrp_chg_mem(struct ttm_buffer_object *tbo)
+{
+	struct drm_device *dev = tbo->bdev->ddev;
+	struct drmcgrp *drmcgrp = tbo->drmcgrp;
+	int devIdx = dev->primary->index;
+	s64 size = (s64)(tbo->mem.size);
+	int mem_type = tbo->mem.mem_type;
+	struct drmcgrp_device_resource *ddr;
+
+	if (drmcgrp == NULL || known_drmcgrp_devs[devIdx] == NULL)
+		return;
+
+	mem_type = mem_type > TTM_PL_PRIV ? TTM_PL_PRIV : mem_type;
+
+	mutex_lock(&known_drmcgrp_devs[devIdx]->mutex);
+	for ( ; drmcgrp != NULL; drmcgrp = parent_drmcgrp(drmcgrp)) {
+		ddr = drmcgrp->dev_resources[devIdx];
+		ddr->mem_stats[mem_type] += size;
+	}
+	mutex_unlock(&known_drmcgrp_devs[devIdx]->mutex);
+}
+EXPORT_SYMBOL(drmcgrp_chg_mem);
+
+void drmcgrp_unchg_mem(struct ttm_buffer_object *tbo)
+{
+	struct drm_device *dev = tbo->bdev->ddev;
+	struct drmcgrp *drmcgrp = tbo->drmcgrp;
+	int devIdx = dev->primary->index;
+	s64 size = (s64)(tbo->mem.size);
+	int mem_type = tbo->mem.mem_type;
+	struct drmcgrp_device_resource *ddr;
+
+	if (drmcgrp == NULL || known_drmcgrp_devs[devIdx] == NULL)
+		return;
+
+	mem_type = mem_type > TTM_PL_PRIV ? TTM_PL_PRIV : mem_type;
+
+	mutex_lock(&known_drmcgrp_devs[devIdx]->mutex);
+	for ( ; drmcgrp != NULL; drmcgrp = parent_drmcgrp(drmcgrp)) {
+		ddr = drmcgrp->dev_resources[devIdx];
+		ddr->mem_stats[mem_type] -= size;
+	}
+	mutex_unlock(&known_drmcgrp_devs[devIdx]->mutex);
+}
+EXPORT_SYMBOL(drmcgrp_unchg_mem);
+
+void drmcgrp_mem_track_move(struct ttm_buffer_object *old_bo, bool evict,
+		struct ttm_mem_reg *new_mem)
+{
+	struct drm_device *dev = old_bo->bdev->ddev;
+	struct drmcgrp *drmcgrp = old_bo->drmcgrp;
+	s64 move_in_bytes = (s64)(old_bo->mem.size);
+	int devIdx = dev->primary->index;
+	int old_mem_type = old_bo->mem.mem_type;
+	int new_mem_type = new_mem->mem_type;
+	struct drmcgrp_device_resource *ddr;
+	struct drmcgrp_device *known_dev;
+
+	known_dev = known_drmcgrp_devs[devIdx];
+
+	if (drmcgrp == NULL || known_dev == NULL)
+		return;
+
+	old_mem_type = old_mem_type > TTM_PL_PRIV ? TTM_PL_PRIV : old_mem_type;
+	new_mem_type = new_mem_type > TTM_PL_PRIV ? TTM_PL_PRIV : new_mem_type;
+
+	mutex_lock(&known_dev->mutex);
+	for ( ; drmcgrp != NULL; drmcgrp = parent_drmcgrp(drmcgrp)) {
+		ddr = drmcgrp->dev_resources[devIdx];
+		ddr->mem_stats[old_mem_type] -= move_in_bytes;
+		ddr->mem_stats[new_mem_type] += move_in_bytes;
+
+		if (evict)
+			ddr->mem_stats_evict++;
+	}
+	mutex_unlock(&known_dev->mutex);
+}
+EXPORT_SYMBOL(drmcgrp_mem_track_move);