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@xxxxxxx> --- 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 | 6 ++ kernel/cgroup/drm.c | 108 ++++++++++++++++++++++++ 8 files changed, 179 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index cfcbbdc39656..463e015e8694 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -1720,8 +1720,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 58c403eda04e..a0e9ce46baf3 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 + drmcg_unchg_mem(bo); + drmcg_put(bo->drmcg); + BUG_ON(kref_read(&bo->list_kref)); BUG_ON(kref_read(&bo->kref)); BUG_ON(atomic_read(&bo->cpu_writers)); @@ -360,6 +366,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 + drmcg_mem_track_move(bo, evict, mem); bo->mem = *mem; mem->mm_node = NULL; goto moved; @@ -368,6 +376,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 + drmcg_mem_track_move(bo, evict, mem); if (!(old_man->flags & TTM_MEMTYPE_FLAG_FIXED) && !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) @@ -381,6 +391,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 + drmcg_mem_track_move(bo, evict, mem); swap(*mem, bo->mem); } @@ -1355,6 +1367,10 @@ int ttm_bo_init_reserved(struct ttm_bo_device *bdev, WARN_ON(!locked); } + bo->drmcg = drmcg_get(current); + if (bo->bdev->ddev != NULL) // TODO: remove after ddev initiazlied for all + drmcg_chg_mem(bo); + if (likely(!ret)) ret = ttm_bo_validate(bo, placement, ctx); @@ -1747,6 +1763,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..15acd2c0720e 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 + drmcg_chg_mem(bo); + *new_obj = &fbo->base; return 0; } diff --git a/include/drm/drm_cgroup.h b/include/drm/drm_cgroup.h index d61b90beded5..7d63f73a5375 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> /** * Per DRM device properties for DRM cgroup controller for the purpose @@ -25,6 +26,11 @@ bool drmcg_try_chg_bo_alloc(struct drmcg *drmcg, struct drm_device *dev, size_t size); void drmcg_unchg_bo_alloc(struct drmcg *drmcg, struct drm_device *dev, size_t size); +void drmcg_chg_mem(struct ttm_buffer_object *tbo); +void drmcg_unchg_mem(struct ttm_buffer_object *tbo); +void drmcg_mem_track_move(struct ttm_buffer_object *old_bo, bool evict, + struct ttm_mem_reg *new_mem); + #else static inline void drmcg_device_update(struct drm_device *device) { @@ -43,5 +49,18 @@ static inline void drmcg_unchg_bo_alloc(struct drmcg *drmcg, struct drm_device *dev, size_t size) { } + +static inline void drmcg_chg_mem(struct ttm_buffer_object *tbo) +{ +} + +static inline void drmcg_unchg_mem(struct ttm_buffer_object *tbo) +{ +} + +static inline void drmcg_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..839936ab358c 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. + * @drmcg: 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 drmcg *drmcg; 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 c9b8ba492f24..e1a805d65b83 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 87a2566c9fdd..4c2794c9333d 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) @@ -17,6 +18,8 @@ enum drmcg_res_type { DRMCG_TYPE_BO_TOTAL, DRMCG_TYPE_BO_PEAK, DRMCG_TYPE_BO_COUNT, + DRMCG_TYPE_MEM, + DRMCG_TYPE_MEM_EVICT, __DRMCG_TYPE_LAST, }; @@ -32,6 +35,9 @@ struct drmcg_device_resource { s64 bo_limits_peak_allocated; s64 bo_stats_count_allocated; + + s64 mem_stats[TTM_PL_PRIV+1]; + s64 mem_stats_evict; }; /** diff --git a/kernel/cgroup/drm.c b/kernel/cgroup/drm.c index 2f54bff291e5..4960a8d1e8f4 100644 --- a/kernel/cgroup/drm.c +++ b/kernel/cgroup/drm.c @@ -10,6 +10,8 @@ #include <linux/kernel.h> #include <drm/drm_file.h> #include <drm/drm_drv.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> @@ -31,6 +33,13 @@ enum drmcg_file_type { DRMCG_FTYPE_DEFAULT, }; +static char const *ttm_placement_names[] = { + [TTM_PL_SYSTEM] = "system", + [TTM_PL_TT] = "tt", + [TTM_PL_VRAM] = "vram", + [TTM_PL_PRIV] = "priv", +}; + static struct drmcg *root_drmcg __read_mostly; static int drmcg_css_free_fn(int id, void *ptr, void *data) @@ -127,6 +136,7 @@ drmcg_css_alloc(struct cgroup_subsys_state *parent_css) static void drmcg_print_stats(struct drmcg_device_resource *ddr, struct seq_file *sf, enum drmcg_res_type type) { + int i; if (ddr == NULL) { seq_puts(sf, "\n"); return; @@ -142,6 +152,16 @@ static void drmcg_print_stats(struct drmcg_device_resource *ddr, case DRMCG_TYPE_BO_COUNT: seq_printf(sf, "%lld\n", ddr->bo_stats_count_allocated); break; + case DRMCG_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 DRMCG_TYPE_MEM_EVICT: + seq_printf(sf, "%lld\n", ddr->mem_stats_evict); + break; default: seq_puts(sf, "\n"); break; @@ -411,6 +431,18 @@ struct cftype files[] = { .private = DRMCG_CTF_PRIV(DRMCG_TYPE_BO_COUNT, DRMCG_FTYPE_STATS), }, + { + .name = "memory.stats", + .seq_show = drmcg_seq_show, + .private = DRMCG_CTF_PRIV(DRMCG_TYPE_MEM, + DRMCG_FTYPE_STATS), + }, + { + .name = "memory.evict.stats", + .seq_show = drmcg_seq_show, + .private = DRMCG_CTF_PRIV(DRMCG_TYPE_MEM_EVICT, + DRMCG_FTYPE_STATS), + }, { } /* terminate */ }; @@ -566,3 +598,79 @@ void drmcg_unchg_bo_alloc(struct drmcg *drmcg, struct drm_device *dev, mutex_unlock(&dev->drmcg_mutex); } EXPORT_SYMBOL(drmcg_unchg_bo_alloc); + +void drmcg_chg_mem(struct ttm_buffer_object *tbo) +{ + struct drm_device *dev = tbo->bdev->ddev; + struct drmcg *drmcg = tbo->drmcg; + int devIdx = dev->primary->index; + s64 size = (s64)(tbo->mem.size); + int mem_type = tbo->mem.mem_type; + struct drmcg_device_resource *ddr; + + if (drmcg == NULL) + return; + + mem_type = mem_type > TTM_PL_PRIV ? TTM_PL_PRIV : mem_type; + + mutex_lock(&dev->drmcg_mutex); + for ( ; drmcg != NULL; drmcg = drmcg_parent(drmcg)) { + ddr = drmcg->dev_resources[devIdx]; + ddr->mem_stats[mem_type] += size; + } + mutex_unlock(&dev->drmcg_mutex); +} +EXPORT_SYMBOL(drmcg_chg_mem); + +void drmcg_unchg_mem(struct ttm_buffer_object *tbo) +{ + struct drm_device *dev = tbo->bdev->ddev; + struct drmcg *drmcg = tbo->drmcg; + int devIdx = dev->primary->index; + s64 size = (s64)(tbo->mem.size); + int mem_type = tbo->mem.mem_type; + struct drmcg_device_resource *ddr; + + if (drmcg == NULL) + return; + + mem_type = mem_type > TTM_PL_PRIV ? TTM_PL_PRIV : mem_type; + + mutex_lock(&dev->drmcg_mutex); + for ( ; drmcg != NULL; drmcg = drmcg_parent(drmcg)) { + ddr = drmcg->dev_resources[devIdx]; + ddr->mem_stats[mem_type] -= size; + } + mutex_unlock(&dev->drmcg_mutex); +} +EXPORT_SYMBOL(drmcg_unchg_mem); + +void drmcg_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 drmcg *drmcg = old_bo->drmcg; + 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 drmcg_device_resource *ddr; + + if (drmcg == 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(&dev->drmcg_mutex); + for ( ; drmcg != NULL; drmcg = drmcg_parent(drmcg)) { + ddr = drmcg->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(&dev->drmcg_mutex); +} +EXPORT_SYMBOL(drmcg_mem_track_move); -- 2.22.0