[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