From eb846d46bca614f24c50f3fa89f94a6820e16589 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 31 May 2016 12:06:50 -0400 Subject: [PATCH] freedreno: add madvise support With a new enough drm/msm, we can let the kernel know about buffers that are in the bo cache, so the kernel can free them under memory pressure. Signed-off-by: Rob Clark --- freedreno/freedreno_bo_cache.c | 10 ++++++++++ freedreno/freedreno_device.c | 10 ++++++++++ freedreno/freedreno_priv.h | 9 +++++++++ freedreno/kgsl/kgsl_bo.c | 6 ++++++ freedreno/msm/msm_bo.c | 20 ++++++++++++++++++++ 5 files changed, 55 insertions(+) diff --git a/freedreno/freedreno_bo_cache.c b/freedreno/freedreno_bo_cache.c index 17199d27..58d171eb 100644 --- a/freedreno/freedreno_bo_cache.c +++ b/freedreno/freedreno_bo_cache.c @@ -165,10 +165,18 @@ fd_bo_cache_alloc(struct fd_bo_cache *cache, uint32_t *size, uint32_t flags) bucket = get_bucket(cache, *size); /* see if we can be green and recycle: */ +retry: if (bucket) { *size = bucket->size; bo = find_in_bucket(bucket, flags); if (bo) { + if (bo->funcs->madvise(bo, TRUE) <= 0) { + /* we've lost the backing pages, delete and try again: */ + pthread_mutex_lock(&table_lock); + bo_del(bo); + pthread_mutex_unlock(&table_lock); + goto retry; + } atomic_set(&bo->refcnt, 1); fd_device_ref(bo->dev); return bo; @@ -187,6 +195,8 @@ fd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo) if (bucket) { struct timespec time; + bo->funcs->madvise(bo, FALSE); + clock_gettime(CLOCK_MONOTONIC, &time); bo->free_time = time.tv_sec; diff --git a/freedreno/freedreno_device.c b/freedreno/freedreno_device.c index 15e41f0e..027414ec 100644 --- a/freedreno/freedreno_device.c +++ b/freedreno/freedreno_device.c @@ -56,7 +56,15 @@ struct fd_device * fd_device_new(int fd) if (!strcmp(version->name, "msm")) { DEBUG_MSG("msm DRM device"); + if (version->version_major != 1) { + ERROR_MSG("unsupported version: %u.%u.%u", version->version_major, + version->version_minor, version->version_patchlevel); + dev = NULL; + goto out; + } + dev = msm_device_new(fd); + dev->version = version->version_minor; #ifdef HAVE_FREEDRENO_KGSL } else if (!strcmp(version->name, "kgsl")) { DEBUG_MSG("kgsl DRM device"); @@ -66,6 +74,8 @@ struct fd_device * fd_device_new(int fd) ERROR_MSG("unknown device: %s", version->name); dev = NULL; } + +out: drmFreeVersion(version); if (!dev) diff --git a/freedreno/freedreno_priv.h b/freedreno/freedreno_priv.h index 4159e526..f3ddd77d 100644 --- a/freedreno/freedreno_priv.h +++ b/freedreno/freedreno_priv.h @@ -54,6 +54,13 @@ #include "freedreno_ringbuffer.h" #include "drm.h" +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif + struct fd_device_funcs { int (*bo_new_handle)(struct fd_device *dev, uint32_t size, uint32_t flags, uint32_t *handle); @@ -76,6 +83,7 @@ struct fd_bo_cache { struct fd_device { int fd; + int version; atomic_t refcnt; /* tables to keep track of bo's, to avoid "evil-twin" fd_bo objects: @@ -139,6 +147,7 @@ struct fd_bo_funcs { int (*offset)(struct fd_bo *bo, uint64_t *offset); int (*cpu_prep)(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op); void (*cpu_fini)(struct fd_bo *bo); + int (*madvise)(struct fd_bo *bo, int willneed); void (*destroy)(struct fd_bo *bo); }; diff --git a/freedreno/kgsl/kgsl_bo.c b/freedreno/kgsl/kgsl_bo.c index 2b45b5e2..ab3485e3 100644 --- a/freedreno/kgsl/kgsl_bo.c +++ b/freedreno/kgsl/kgsl_bo.c @@ -116,6 +116,11 @@ static void kgsl_bo_cpu_fini(struct fd_bo *bo) { } +static int kgsl_bo_madvise(struct fd_bo *bo, int willneed) +{ + return willneed; /* not supported by kgsl */ +} + static void kgsl_bo_destroy(struct fd_bo *bo) { struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo); @@ -127,6 +132,7 @@ static const struct fd_bo_funcs funcs = { .offset = kgsl_bo_offset, .cpu_prep = kgsl_bo_cpu_prep, .cpu_fini = kgsl_bo_cpu_fini, + .madvise = kgsl_bo_madvise, .destroy = kgsl_bo_destroy, }; diff --git a/freedreno/msm/msm_bo.c b/freedreno/msm/msm_bo.c index cd05a6cd..cfaec827 100644 --- a/freedreno/msm/msm_bo.c +++ b/freedreno/msm/msm_bo.c @@ -89,6 +89,25 @@ static void msm_bo_cpu_fini(struct fd_bo *bo) drmCommandWrite(bo->dev->fd, DRM_MSM_GEM_CPU_FINI, &req, sizeof(req)); } +static int msm_bo_madvise(struct fd_bo *bo, int willneed) +{ + struct drm_msm_gem_madvise req = { + .handle = bo->handle, + .madv = willneed ? MSM_MADV_WILLNEED : MSM_MADV_DONTNEED, + }; + int ret; + + /* older kernels do not support this: */ + if (bo->dev->version < 1) + return willneed; + + ret = drmCommandWriteRead(bo->dev->fd, DRM_MSM_GEM_MADVISE, &req, sizeof(req)); + if (ret) + return ret; + + return req.retained; +} + static void msm_bo_destroy(struct fd_bo *bo) { struct msm_bo *msm_bo = to_msm_bo(bo); @@ -100,6 +119,7 @@ static const struct fd_bo_funcs funcs = { .offset = msm_bo_offset, .cpu_prep = msm_bo_cpu_prep, .cpu_fini = msm_bo_cpu_fini, + .madvise = msm_bo_madvise, .destroy = msm_bo_destroy, };