diff mbox

[3/4] drm/i915: userspace interface to the forcewake refcount

Message ID 1302284850-8274-4-git-send-email-ben@bwidawsk.net (mailing list archive)
State New, archived
Headers show

Commit Message

Ben Widawsky April 8, 2011, 5:47 p.m. UTC
In the simple case, all that needs to happen is grab the lock, possibly
wake the GPU and bump the refcount.

In the not simple case, we close our eyes and hope for the best (but we
only do it one at a time).

Signed-off-by: Ben Widawsky <ben@bwidawsk.net>
---
 drivers/gpu/drm/i915/i915_debugfs.c |   99 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/i915_drv.c     |   12 ++++-
 drivers/gpu/drm/i915/i915_drv.h     |    1 +
 3 files changed, 110 insertions(+), 2 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 6eee101..286896e 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -1193,6 +1193,19 @@  static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
 	return 0;
 }
 
+static int i915_gen6_forcewake_count_info(struct seq_file *m, void *data)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	seq_printf(m, "forcewake count = %d\n", dev_priv->forcewake_count);
+	seq_printf(m, "forcewake forced = %d\n",
+		   atomic_read(&dev_priv->forcewake_force));
+
+	return 0;
+}
+
 static int
 i915_wedged_open(struct inode *inode,
 		 struct file *filp)
@@ -1295,6 +1308,85 @@  static int i915_wedged_create(struct dentry *root, struct drm_minor *minor)
 	return drm_add_fake_info_node(minor, ent, &i915_wedged_fops);
 }
 
+static int i915_forcewake_open(struct inode *inode, struct file *file)
+{
+	struct drm_device *dev = inode->i_private;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	int ret;
+	bool locked;
+
+	if (!IS_GEN6(dev))
+		return 0;
+
+	ret = i915_mutex_lock_interruptible(dev);
+	locked = (ret == 0) ? true : false;
+
+	if (!locked) {
+		/*
+		 *  Only let one user violently forcewake, else it gets messy,
+		 *  that user must be responsible for cleanup as well. Store
+		 *  away current for later cleanup.
+		 */
+		if (atomic_cmpxchg(&dev_priv->forcewake_force, 0, 1) != 0)
+			return -EBUSY;
+		file->private_data = current;
+	}
+
+	/*  We either have the lock, or we've set forcewake */
+	gen6_gt_force_wake_get(dev_priv);
+
+	if (locked) {
+		atomic_set(&dev_priv->forcewake_force, 0);
+		mutex_unlock(&dev->struct_mutex);
+	}
+
+	return 0;
+}
+
+int i915_forcewake_release(struct inode *inode, struct file *file)
+{
+	struct drm_device *dev = inode->i_private;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	if (!IS_GEN6(dev))
+		return 0;
+
+	/* more ugliness as a result of force */
+	if (file->private_data == current) {
+		WARN_ON(!atomic_read(&dev_priv->forcewake_force));
+		WARN_ON(i915_mutex_lock_interruptible(dev));
+		gen6_gt_force_wake_put(dev_priv);
+		atomic_set(&dev_priv->forcewake_force, 0);
+	} else {
+		mutex_lock(&dev->struct_mutex);
+		gen6_gt_force_wake_put(dev_priv);
+		atomic_set(&dev_priv->forcewake_force, 0);
+		mutex_unlock(&dev->struct_mutex);
+	}
+	return 0;
+}
+
+static const struct file_operations i915_forcewake_fops = {
+	.owner = THIS_MODULE,
+	.open = i915_forcewake_open,
+	.release = i915_forcewake_release,
+};
+
+static int i915_forcewake_create(struct dentry *root, struct drm_minor *minor)
+{
+	struct drm_device *dev = minor->dev;
+	struct dentry *ent;
+
+	ent = debugfs_create_file("i915_forcewake_lock",
+				  S_IRWXU,
+				  root, dev,
+				  &i915_forcewake_fops);
+	if (IS_ERR(ent))
+		return PTR_ERR(ent);
+
+	return 0;
+}
+
 static struct drm_info_list i915_debugfs_list[] = {
 	{"i915_capabilities", i915_capabilities, 0},
 	{"i915_gem_objects", i915_gem_object_info, 0},
@@ -1331,6 +1423,7 @@  static struct drm_info_list i915_debugfs_list[] = {
 	{"i915_sr_status", i915_sr_status, 0},
 	{"i915_opregion", i915_opregion, 0},
 	{"i915_gem_framebuffer", i915_gem_framebuffer_info, 0},
+	{"i915_gen6_forcewake_count", i915_gen6_forcewake_count_info, 0},
 };
 #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
 
@@ -1342,6 +1435,10 @@  int i915_debugfs_init(struct drm_minor *minor)
 	if (ret)
 		return ret;
 
+	ret = i915_forcewake_create(minor->debugfs_root, minor);
+	if (ret)
+		return ret;
+
 	return drm_debugfs_create_files(i915_debugfs_list,
 					I915_DEBUGFS_ENTRIES,
 					minor->debugfs_root, minor);
@@ -1351,6 +1448,8 @@  void i915_debugfs_cleanup(struct drm_minor *minor)
 {
 	drm_debugfs_remove_files(i915_debugfs_list,
 				 I915_DEBUGFS_ENTRIES, minor);
+	drm_debugfs_remove_files((struct drm_info_list *) &i915_forcewake_fops,
+				 1, minor);
 	drm_debugfs_remove_files((struct drm_info_list *) &i915_wedged_fops,
 				 1, minor);
 }
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index ce59cd4..fcd5c9a 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -281,7 +281,14 @@  static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
 
 void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
 {
-	WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
+	/*
+	 * It takes a long time to wake, but the count is incremented
+	 * immediately. Not having the lock causes a race, but all bets are off
+	 * when using forced forcewake, which should only be touched through
+	 * root-only entry in debugfs.
+	 */
+	WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex) &&
+		!atomic_read(&dev_priv->forcewake_force));
 
 	if (dev_priv->forcewake_count++ == 0)
 		__gen6_gt_force_wake_get(dev_priv);
@@ -295,7 +302,8 @@  static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
 
 void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
 {
-	WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
+	WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex) &&
+		!atomic_read(&dev_priv->forcewake_force));
 
 	if (--dev_priv->forcewake_count == 0)
 		__gen6_gt_force_wake_put(dev_priv);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index f024927..f1c8a70 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -705,6 +705,7 @@  typedef struct drm_i915_private {
 	struct drm_property *broadcast_rgb_property;
 
 	int forcewake_count;
+	atomic_t forcewake_force;
 } drm_i915_private_t;
 
 struct drm_i915_gem_object {