drm/i915: Add a debug interface to forcibly evict and shrink our object caches
authorChris Wilson <chris@chris-wilson.co.uk>
Tue, 15 Jan 2013 12:39:35 +0000 (12:39 +0000)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 17 Jan 2013 21:07:57 +0000 (22:07 +0100)
As a means to investigate some bad system behaviour related to the
purging of the active, inactive and unbound lists, it is useful to be
able to manually control when those lists should be cleared.

v2: use _safe list iterators as we kick objects from the list as we
walk.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
[danvet: Add a small comment explaining why we don't need to check and
wait for gpu resets, acked by Chris on irc.]
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c

index f7d88e99ebf07d839389bc672491e58581bba114..f94418bd1ed2efa12ce4df99eb169904f5cd29a3 100644 (file)
@@ -1776,6 +1776,102 @@ static const struct file_operations i915_ring_stop_fops = {
        .llseek = default_llseek,
 };
 
+#define DROP_UNBOUND 0x1
+#define DROP_BOUND 0x2
+#define DROP_RETIRE 0x4
+#define DROP_ACTIVE 0x8
+#define DROP_ALL (DROP_UNBOUND | \
+                 DROP_BOUND | \
+                 DROP_RETIRE | \
+                 DROP_ACTIVE)
+static ssize_t
+i915_drop_caches_read(struct file *filp,
+                     char __user *ubuf,
+                     size_t max,
+                     loff_t *ppos)
+{
+       char buf[20];
+       int len;
+
+       len = snprintf(buf, sizeof(buf), "0x%08x\n", DROP_ALL);
+       if (len > sizeof(buf))
+               len = sizeof(buf);
+
+       return simple_read_from_buffer(ubuf, max, ppos, buf, len);
+}
+
+static ssize_t
+i915_drop_caches_write(struct file *filp,
+                      const char __user *ubuf,
+                      size_t cnt,
+                      loff_t *ppos)
+{
+       struct drm_device *dev = filp->private_data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_gem_object *obj, *next;
+       char buf[20];
+       int val = 0, ret;
+
+       if (cnt > 0) {
+               if (cnt > sizeof(buf) - 1)
+                       return -EINVAL;
+
+               if (copy_from_user(buf, ubuf, cnt))
+                       return -EFAULT;
+               buf[cnt] = 0;
+
+               val = simple_strtoul(buf, NULL, 0);
+       }
+
+       DRM_DEBUG_DRIVER("Dropping caches: 0x%08x\n", val);
+
+       /* No need to check and wait for gpu resets, only libdrm auto-restarts
+        * on ioctls on -EAGAIN. */
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       if (ret)
+               return ret;
+
+       if (val & DROP_ACTIVE) {
+               ret = i915_gpu_idle(dev);
+               if (ret)
+                       goto unlock;
+       }
+
+       if (val & (DROP_RETIRE | DROP_ACTIVE))
+               i915_gem_retire_requests(dev);
+
+       if (val & DROP_BOUND) {
+               list_for_each_entry_safe(obj, next, &dev_priv->mm.inactive_list, mm_list)
+                       if (obj->pin_count == 0) {
+                               ret = i915_gem_object_unbind(obj);
+                               if (ret)
+                                       goto unlock;
+                       }
+       }
+
+       if (val & DROP_UNBOUND) {
+               list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, gtt_list)
+                       if (obj->pages_pin_count == 0) {
+                               ret = i915_gem_object_put_pages(obj);
+                               if (ret)
+                                       goto unlock;
+                       }
+       }
+
+unlock:
+       mutex_unlock(&dev->struct_mutex);
+
+       return ret ?: cnt;
+}
+
+static const struct file_operations i915_drop_caches_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read = i915_drop_caches_read,
+       .write = i915_drop_caches_write,
+       .llseek = default_llseek,
+};
+
 static ssize_t
 i915_max_freq_read(struct file *filp,
                   char __user *ubuf,
@@ -2172,6 +2268,12 @@ int i915_debugfs_init(struct drm_minor *minor)
        if (ret)
                return ret;
 
+       ret = i915_debugfs_create(minor->debugfs_root, minor,
+                                 "i915_gem_drop_caches",
+                                 &i915_drop_caches_fops);
+       if (ret)
+               return ret;
+
        ret = i915_debugfs_create(minor->debugfs_root, minor,
                                  "i915_error_state",
                                  &i915_error_state_fops);
@@ -2203,6 +2305,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor)
                                 1, minor);
        drm_debugfs_remove_files((struct drm_info_list *) &i915_cache_sharing_fops,
                                 1, minor);
+       drm_debugfs_remove_files((struct drm_info_list *) &i915_drop_caches_fops,
+                                1, minor);
        drm_debugfs_remove_files((struct drm_info_list *) &i915_ring_stop_fops,
                                 1, minor);
        drm_debugfs_remove_files((struct drm_info_list *) &i915_error_state_fops,
index 35ecabc711edd6799ba89be90974e1275bf6eb6b..8f5b2816fc038722cc6f7d732781ed5f9cf44ddc 100644 (file)
@@ -1445,6 +1445,7 @@ int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj,
                                     bool nonblocking);
 void i915_gem_object_unpin(struct drm_i915_gem_object *obj);
 int __must_check i915_gem_object_unbind(struct drm_i915_gem_object *obj);
+int i915_gem_object_put_pages(struct drm_i915_gem_object *obj);
 void i915_gem_release_mmap(struct drm_i915_gem_object *obj);
 void i915_gem_lastclose(struct drm_device *dev);
 
index 313bdbaba3cdf861bf1e0621d5039ba34dd634af..9d33fb3f8d42b08caf0ff4524df2c9046fcfa21c 100644 (file)
@@ -1662,7 +1662,7 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
        kfree(obj->pages);
 }
 
-static int
+int
 i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
 {
        const struct drm_i915_gem_object_ops *ops = obj->ops;