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 <robclark@freedesktop.org>
diff --git a/freedreno/freedreno_bo_cache.c b/freedreno/freedreno_bo_cache.c
index 17199d2..58d171e 100644
--- a/freedreno/freedreno_bo_cache.c
+++ b/freedreno/freedreno_bo_cache.c
@@ -165,10 +165,18 @@
 	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 @@
 	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 15e41f0..027414e 100644
--- a/freedreno/freedreno_device.c
+++ b/freedreno/freedreno_device.c
@@ -56,7 +56,15 @@
 
 	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 @@
 		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 4159e52..f3ddd77 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_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 @@
 	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 2b45b5e..ab3485e 100644
--- a/freedreno/kgsl/kgsl_bo.c
+++ b/freedreno/kgsl/kgsl_bo.c
@@ -116,6 +116,11 @@
 {
 }
 
+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 @@
 		.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 cd05a6c..cfaec82 100644
--- a/freedreno/msm/msm_bo.c
+++ b/freedreno/msm/msm_bo.c
@@ -89,6 +89,25 @@
 	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 @@
 		.offset = msm_bo_offset,
 		.cpu_prep = msm_bo_cpu_prep,
 		.cpu_fini = msm_bo_cpu_fini,
+		.madvise = msm_bo_madvise,
 		.destroy = msm_bo_destroy,
 };