[Intel-gfx] [PATCH 09/10] drm/i915: Use madvise-style ioctl to mark purgeable pages.

Chris Wilson chris at chris-wilson.co.uk
Sat Jun 6 10:46:05 CEST 2009


Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_dma.c |    1 +
 drivers/gpu/drm/i915/i915_drv.c |    3 +
 drivers/gpu/drm/i915/i915_drv.h |   10 ++
 drivers/gpu/drm/i915/i915_gem.c |  235 ++++++++++++++++++++++++++++++++++-----
 include/drm/i915_drm.h          |   16 +++
 5 files changed, 236 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 68e882c..7045afb 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1365,6 +1365,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
 	DRM_IOCTL_DEF(DRM_I915_GEM_GET_TILING, i915_gem_get_tiling, 0),
 	DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, 0),
 	DRM_IOCTL_DEF(DRM_I915_GET_PIPE_FROM_CRTC_ID, intel_get_pipe_from_crtc_id, 0),
+	DRM_IOCTL_DEF(DRM_I915_GEM_MADVISE, i915_gem_madvise_ioctl, 0),
 };
 
 int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 98560e1..38cca98 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -226,6 +226,8 @@ static int __init i915_init(void)
 {
 	driver.num_ioctls = i915_max_ioctl;
 
+	i915_gem_shrinker_init();
+
 	/*
 	 * If CONFIG_DRM_I915_KMS is set, default to KMS unless
 	 * explicitly disabled with the module pararmeter.
@@ -252,6 +254,7 @@ static int __init i915_init(void)
 
 static void __exit i915_exit(void)
 {
+	i915_gem_shrinker_exit();
 	drm_exit(&driver);
 }
 
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 26183d2..b026a5a 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -487,6 +487,11 @@ struct drm_i915_gem_object {
 	 * in an execbuffer object list.
 	 */
 	int in_execbuffer;
+
+	/**
+	 * Advice: are the backing pages purgeable?
+	 */
+	int madv;
 };
 
 /**
@@ -663,6 +668,11 @@ int i915_gem_object_get_pages(struct drm_gem_object *obj);
 void i915_gem_object_put_pages(struct drm_gem_object *obj);
 void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv);
 
+int i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv);
+void i915_gem_shrinker_init(void);
+void i915_gem_shrinker_exit(void);
+
 /* i915_gem_tiling.c */
 void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
 void i915_gem_object_do_bit_17_swizzle(struct drm_gem_object *obj);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 0116e3c..db702e0 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -48,11 +48,14 @@ static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
 					   unsigned alignment);
 static int i915_gem_object_get_fence_reg(struct drm_gem_object *obj, bool write);
 static void i915_gem_clear_fence_reg(struct drm_gem_object *obj);
-static int i915_gem_evict_something(struct drm_device *dev);
+static int i915_gem_evict_something(struct drm_device *dev, int min_size);
 static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
 				struct drm_i915_gem_pwrite *args,
 				struct drm_file *file_priv);
 
+static struct list_head shrink_list;
+static spinlock_t shrink_list_lock;
+
 int i915_gem_do_init(struct drm_device *dev, unsigned long start,
 		     unsigned long end)
 {
@@ -1459,9 +1462,13 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
 	struct drm_i915_gem_object *obj_priv = obj->driver_private;
 
 	i915_verify_inactive(dev, __FILE__, __LINE__);
-	if (obj_priv->pin_count != 0)
+	if (obj_priv->pin_count != 0) {
 		list_del_init(&obj_priv->list);
-	else
+	} else if (obj_priv->madv == I915_MADV_DONTNEED) {
+		spin_lock(&shrink_list_lock);
+		list_move_tail(&obj_priv->list, &shrink_list);
+		spin_unlock(&shrink_list_lock);
+	} else
 		list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
 
 	obj_priv->last_rendering_seqno = 0;
@@ -1915,14 +1922,17 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
 	}
 
 	/* Remove ourselves from the LRU list if present. */
-	if (!list_empty(&obj_priv->list))
+	if (!list_empty(&obj_priv->list)) {
+		spin_lock(&shrink_list_lock);
 		list_del_init(&obj_priv->list);
+		spin_unlock(&shrink_list_lock);
+	}
 
 	return 0;
 }
 
 static int
-i915_gem_evict_something(struct drm_device *dev)
+i915_gem_evict_something(struct drm_device *dev, int min_size)
 {
 	drm_i915_private_t *dev_priv = dev->dev_private;
 	struct drm_gem_object *obj;
@@ -1930,23 +1940,34 @@ i915_gem_evict_something(struct drm_device *dev)
 	int ret = 0;
 
 	for (;;) {
+		/* If there's a discarded buffer available, use it */
+		spin_lock(&shrink_list_lock);
+		list_for_each_entry(obj_priv, &shrink_list, list) {
+			obj = obj_priv->obj;
+			if (obj->dev == dev && obj->size >= min_size) {
+				spin_unlock(&shrink_list_lock);
+
+				return i915_gem_object_unbind(obj);
+			}
+		}
+		spin_unlock(&shrink_list_lock);
+
 		/* If there's an inactive buffer available now, grab it
 		 * and be done.
 		 */
-		if (!list_empty(&dev_priv->mm.inactive_list)) {
-			obj_priv = list_first_entry(&dev_priv->mm.inactive_list,
-						    struct drm_i915_gem_object,
-						    list);
+		list_for_each_entry(obj_priv,
+				    &dev_priv->mm.inactive_list, list) {
 			obj = obj_priv->obj;
-			BUG_ON(obj_priv->pin_count != 0);
+			if (obj->size >= min_size) {
+				BUG_ON(obj_priv->pin_count != 0);
 #if WATCH_LRU
-			DRM_INFO("%s: evicting %p\n", __func__, obj);
+				DRM_INFO("%s: evicting %p\n", __func__, obj);
 #endif
-			BUG_ON(obj_priv->active);
+				BUG_ON(obj_priv->active);
 
-			/* Wait on the rendering and unbind the buffer. */
-			ret = i915_gem_object_unbind(obj);
-			break;
+				/* Wait on the rendering and unbind the buffer. */
+				return i915_gem_object_unbind(obj);
+			}
 		}
 
 		/* If we didn't get anything, but the ring is still processing
@@ -2010,10 +2031,42 @@ i915_gem_evict_something(struct drm_device *dev)
 static int
 i915_gem_evict_everything(struct drm_device *dev)
 {
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	struct drm_i915_gem_object *obj_priv, *next;
+	struct drm_gem_object *obj;
 	int ret;
+	bool lists_empty = true;
+
+	spin_lock(&shrink_list_lock);
+	list_for_each_entry_safe(obj_priv, next, &shrink_list, list) {
+		obj = obj_priv->obj;
+		if (obj->dev == dev) {
+			spin_unlock(&shrink_list_lock);
+
+			ret = i915_gem_object_unbind(obj);
+			if (ret)
+				return ret;
+
+			lists_empty = false;
+
+			spin_lock(&shrink_list_lock);
+		}
+	}
+	spin_unlock(&shrink_list_lock);
+
+	if (lists_empty) {
+		spin_lock(&dev_priv->mm.active_list_lock);
+		lists_empty = (list_empty(&dev_priv->mm.inactive_list) &&
+			       list_empty(&dev_priv->mm.flushing_list) &&
+			       list_empty(&dev_priv->mm.active_list));
+		spin_unlock(&dev_priv->mm.active_list_lock);
+
+		if (lists_empty)
+			return -ENOSPC;
+	}
 
 	for (;;) {
-		ret = i915_gem_evict_something(dev);
+		ret = i915_gem_evict_something(dev, 0);
 		if (ret != 0)
 			break;
 	}
@@ -2376,6 +2429,12 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
 
 	if (dev_priv->mm.suspended)
 		return -EBUSY;
+
+	if (obj_priv->madv == I915_MADV_DONTNEED) {
+		DRM_ERROR("Attempting to bind a purgeable object\n");
+		return -EINVAL;
+	}
+
 	if (alignment == 0)
 		alignment = i915_gem_get_gtt_alignment(obj);
 	if (alignment & (i915_gem_get_gtt_alignment(obj) - 1)) {
@@ -2395,26 +2454,22 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
 		}
 	}
 	if (obj_priv->gtt_space == NULL) {
-		bool lists_empty;
-
 		/* If the gtt is empty and we're still having trouble
 		 * fitting our object in, we're out of memory.
 		 */
 #if WATCH_LRU
 		DRM_INFO("%s: GTT full, evicting something\n", __func__);
 #endif
-		spin_lock(&dev_priv->mm.active_list_lock);
-		lists_empty = (list_empty(&dev_priv->mm.inactive_list) &&
-			       list_empty(&dev_priv->mm.flushing_list) &&
-			       list_empty(&dev_priv->mm.active_list));
-		spin_unlock(&dev_priv->mm.active_list_lock);
-		if (lists_empty) {
-			DRM_ERROR("GTT full, but LRU list empty\n");
-			return -ENOSPC;
-		}
-
-		ret = i915_gem_evict_something(dev);
+		ret = i915_gem_evict_something(dev, obj->size);
 		if (ret != 0) {
+			if (ret == -ENOSPC) {
+				ret = i915_gem_evict_everything(dev);
+				if (ret)
+					return ret;
+
+				goto search_free;
+			}
+
 			if (ret != -ERESTARTSYS)
 				DRM_ERROR("Failed to evict a buffer %d\n", ret);
 			return ret;
@@ -3655,6 +3710,13 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
 	}
 	obj_priv = obj->driver_private;
 
+	if (obj_priv->madv == I915_MADV_DONTNEED) {
+		DRM_ERROR("Attempting to pin a I915_MADV_DONTNEED buffer\n");
+		drm_gem_object_unreference(obj);
+		mutex_unlock(&dev->struct_mutex);
+		return -EINVAL;
+	}
+
 	if (obj_priv->pin_filp != NULL && obj_priv->pin_filp != file_priv) {
 		DRM_ERROR("Already pinned in i915_gem_pin_ioctl(): %d\n",
 			  args->handle);
@@ -3791,6 +3853,8 @@ int i915_gem_init_object(struct drm_gem_object *obj)
 	obj_priv->fence_reg = I915_FENCE_REG_NONE;
 	INIT_LIST_HEAD(&obj_priv->list);
 
+	obj_priv->madv = I915_MADV_WILLNEED;
+
 	return 0;
 }
 
@@ -4441,3 +4505,116 @@ void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv)
 		list_del_init(i915_file_priv->mm.request_list.next);
 	mutex_unlock(&dev->struct_mutex);
 }
+
+int
+i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
+		       struct drm_file *file_priv)
+{
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	struct drm_i915_gem_madvise *args = data;
+	struct drm_gem_object *obj;
+	struct drm_i915_gem_object *obj_priv;
+
+	switch (args->madv) {
+	case I915_MADV_DONTNEED:
+	case I915_MADV_WILLNEED:
+	    break;
+	default:
+	    return -EINVAL;
+	}
+
+	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+	if (obj == NULL) {
+		DRM_ERROR("Bad handle in i915_gem_madvise_ioctl(): %d\n",
+			  args->handle);
+		return -EBADF;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+	obj_priv = obj->driver_private;
+
+	if (obj_priv->pin_count) {
+		drm_gem_object_unreference(obj);
+		mutex_unlock(&dev->struct_mutex);
+
+		DRM_ERROR("Attempted i915_gem_madvise_ioctl() on a pinned object\n");
+		return -EINVAL;
+	}
+
+	if (obj_priv->madv != args->madv) {
+		if (! obj_priv->active && obj_priv->gtt_space != NULL) {
+			spin_lock(&shrink_list_lock);
+			switch (args->madv) {
+			case I915_MADV_DONTNEED:
+				list_move_tail(&obj_priv->list,
+					       &shrink_list);
+				break;
+
+			case I915_MADV_WILLNEED:
+				list_move_tail(&obj_priv->list,
+					       &dev_priv->mm.inactive_list);
+				break;
+			}
+			spin_unlock(&shrink_list_lock);
+		}
+
+		obj_priv->madv = args->madv;
+	}
+	args->retained = obj_priv->gtt_space != NULL;
+
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	return 0;
+}
+
+static int
+i915_gem_shrink(int nr_to_scan, gfp_t gfp_mask)
+{
+	struct drm_i915_gem_object *obj_priv, *next;
+	int cnt = 0;
+
+	if (gfp_mask & GFP_ATOMIC)
+		return -1;
+
+	spin_lock(&shrink_list_lock);
+	list_for_each_entry_safe(obj_priv, next, &shrink_list, list) {
+		if (nr_to_scan > 0) {
+			struct drm_gem_object *obj = obj_priv->obj;
+			struct drm_device *dev = obj->dev;
+
+			spin_unlock(&shrink_list_lock);
+
+			if (mutex_trylock(&dev->struct_mutex)) {
+				i915_gem_object_unbind(obj);
+				nr_to_scan -= obj->size / 4096;
+				mutex_unlock(&dev->struct_mutex);
+			}
+
+			spin_lock(&shrink_list_lock);
+		} else
+			cnt += obj_priv->obj->size / 4096;
+	}
+	spin_unlock(&shrink_list_lock);
+
+	return (cnt / 100) * sysctl_vfs_cache_pressure;
+}
+
+static struct shrinker shrinker = {
+	.shrink = i915_gem_shrink,
+	.seeks = DEFAULT_SEEKS,
+};
+
+__init void
+i915_gem_shrinker_init(void)
+{
+    spin_lock_init(&shrink_list_lock);
+    INIT_LIST_HEAD(&shrink_list);
+    register_shrinker(&shrinker);
+}
+
+__exit void
+i915_gem_shrinker_exit(void)
+{
+    unregister_shrinker(&shrinker);
+}
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
index 8e1e925..d239fdc 100644
--- a/include/drm/i915_drm.h
+++ b/include/drm/i915_drm.h
@@ -185,6 +185,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_I915_GEM_GET_APERTURE 0x23
 #define DRM_I915_GEM_MMAP_GTT	0x24
 #define DRM_I915_GET_PIPE_FROM_CRTC_ID	0x25
+#define DRM_I915_GEM_MADVISE	0x26
 
 #define DRM_IOCTL_I915_INIT		DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
 #define DRM_IOCTL_I915_FLUSH		DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -221,6 +222,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_GEM_GET_TILING	DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling)
 #define DRM_IOCTL_I915_GEM_GET_APERTURE	DRM_IOR  (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture)
 #define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_intel_get_pipe_from_crtc_id)
+#define DRM_IOCTL_I915_GEM_MADVISE	DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise)
 
 /* Allow drivers to submit batchbuffers directly to hardware, relying
  * on the security mechanisms provided by hardware.
@@ -667,4 +669,18 @@ struct drm_i915_get_pipe_from_crtc_id {
 	__u32 pipe;
 };
 
+#define I915_MADV_WILLNEED	0
+#define I915_MADV_DONTNEED	1
+
+struct drm_i915_gem_madvise {
+	/** Handle of the buffer to change the backing store advice. */
+	__u32 handle;
+
+	/** Advice. */
+	__u32 madv;
+
+	/** Whether or not the backing store still exists */
+	__u32 retained;
+};
+
 #endif				/* _I915_DRM_H_ */
-- 
1.6.3.1




More information about the Intel-gfx mailing list