From 5ba679e1863b5ec4b7b29e040a5cbb7eb66c2d37 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Thu, 16 May 2013 15:11:30 +0300 Subject: [PATCH] drm/i915: [HACK] vmalloced error state with prealloc Introduce 'i915_error_state2' debugfs node. It uses vmalloc for buffering the error state. This allows error state to be captured even when memory is too fragmented for seq_file (kmalloc). You can enhance your changes for succesful capture by forcing allocation. First read from 'i915_error_state2' will (pre)allocate the error state buffer. Signed-off-by: Mika Kuoppala --- drivers/gpu/drm/i915/i915_debugfs.c | 107 +++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.h | 3 + 2 files changed, 110 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a55630a..570b326 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -866,6 +866,99 @@ static const struct file_operations i915_error_state_fops = { .release = i915_error_state_release, }; + +static int i915_error_state2_open(struct inode *inode, struct file *file) +{ + struct drm_device *dev = inode->i_private; + drm_i915_private_t *dev_priv = dev->dev_private; + struct i915_error_state_file_priv *error_priv; + unsigned long flags; + + error_priv = kzalloc(sizeof(*error_priv), GFP_KERNEL); + if (!error_priv) + return -ENOMEM; + + error_priv->dev = dev; + + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error_priv->error = dev_priv->gpu_error.first_error; + if (error_priv->error) + kref_get(&error_priv->error->ref); + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); + + file->private_data = error_priv; + + return 0; +} + +static int i915_error_state2_release(struct inode *inode, struct file *file) +{ + struct i915_error_state_file_priv *error_priv = file->private_data; + + if (error_priv->error) + kref_put(&error_priv->error->ref, i915_error_state_free); + kfree(error_priv); + + return 0; +} + +#define MAX_ERROR_STATE_LEN (1024*1024*10) + +static ssize_t i915_estate2_read(struct file *file, char __user *userbuf, + size_t count, loff_t *position) +{ + int ret = 0; + struct i915_error_state_file_priv *error_priv = file->private_data; + struct drm_device *dev = error_priv->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + ssize_t ret_count = 0; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + if (!dev_priv->gpu_error.first_error_str) { + dev_priv->gpu_error.first_error_str = + vmalloc(MAX_ERROR_STATE_LEN); + } + + if (!dev_priv->gpu_error.first_error_str) { + ret = -ENOMEM; + goto out; + } + + if (!dev_priv->gpu_error.first_error_str_len && + dev_priv->gpu_error.first_error) { + struct seq_file fakeseq; + memset(&fakeseq, 0, sizeof(fakeseq)); + fakeseq.buf = dev_priv->gpu_error.first_error_str; + fakeseq.count = 0; + fakeseq.size = MAX_ERROR_STATE_LEN; + fakeseq.private = error_priv; + ret = i915_error_state(&fakeseq, NULL); + if (ret) + goto out; + + dev_priv->gpu_error.first_error_str_len = fakeseq.count; + } + + ret_count = simple_read_from_buffer(userbuf, count, position, + dev_priv->gpu_error.first_error_str, + dev_priv->gpu_error. + first_error_str_len); +out: + mutex_unlock(&dev->struct_mutex); + return ret ? ret : ret_count; +} + +static const struct file_operations i915_estate2_fops = { + .owner = THIS_MODULE, + .open = i915_error_state2_open, + .read = i915_estate2_read, + .llseek = default_llseek, + .release = i915_error_state2_release, +}; + static int i915_next_seqno_get(void *data, u64 *val) { @@ -2175,6 +2268,12 @@ int i915_debugfs_init(struct drm_minor *minor) if (ret) return ret; + ret = i915_debugfs_create(minor->debugfs_root, minor, + "i915_error_state2", + &i915_estate2_fops); + if (ret) + return ret; + return drm_debugfs_create_files(i915_debugfs_list, I915_DEBUGFS_ENTRIES, minor->debugfs_root, minor); @@ -2182,6 +2281,9 @@ int i915_debugfs_init(struct drm_minor *minor) void i915_debugfs_cleanup(struct drm_minor *minor) { + struct drm_device *dev = minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + drm_debugfs_remove_files(i915_debugfs_list, I915_DEBUGFS_ENTRIES, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_forcewake_fops, @@ -2202,6 +2304,11 @@ void i915_debugfs_cleanup(struct drm_minor *minor) 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_next_seqno_fops, 1, minor); + + if (dev_priv->gpu_error.first_error_str) { + vfree(dev_priv->gpu_error.first_error_str); + dev_priv->gpu_error.first_error_str = NULL; + } } #endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 2330058..a67c82f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -852,6 +852,9 @@ struct i915_gpu_error { spinlock_t lock; /* Protected by the above dev->gpu_error.lock. */ struct drm_i915_error_state *first_error; + char *first_error_str; + unsigned long first_error_str_len; + struct work_struct work; unsigned long last_reset; -- 1.7.9.5