Re: [Intel-gfx] [PATCH 5/5] drm/i915: l3 parity sysfs interface

2012-05-25 Thread Jesse Barnes
On Fri, 27 Apr 2012 17:40:21 -0700
Ben Widawsky b...@bwidawsk.net wrote:

 Dumb binary interfaces which allow root-only updates of the cache
 remapping registers. As mentioned in a previous patch, software using
 this interface needs to know about HW limits, and other programming
 considerations as the kernel interface does no checking for these things
 on the root-only interface.
 
 v1: Drop extra posting reads (Chris)
 Return negative values in the sysfs interfaces on errors (Chris)
 
 Signed-off-by: Ben Widawsky b...@bwidawsk.net
 ---
  drivers/gpu/drm/i915/i915_sysfs.c |  128 
 -
  1 file changed, 126 insertions(+), 2 deletions(-)
 
 diff --git a/drivers/gpu/drm/i915/i915_sysfs.c 
 b/drivers/gpu/drm/i915/i915_sysfs.c
 index 79f8344..ed77cbf 100644
 --- a/drivers/gpu/drm/i915/i915_sysfs.c
 +++ b/drivers/gpu/drm/i915/i915_sysfs.c
 @@ -29,6 +29,7 @@
  #include linux/module.h
  #include linux/stat.h
  #include linux/sysfs.h
 +#include intel_drv.h
  #include i915_drv.h
  
  static u32 calc_residency(struct drm_device *dev, const u32 reg)
 @@ -92,20 +93,143 @@ static struct attribute_group rc6_attr_group = {
   .attrs =  rc6_attrs
  };
  
 +static int l3_access_valid(struct drm_device *dev, loff_t offset)
 +{
 + if (!IS_IVYBRIDGE(dev))
 + return -EPERM;

-ENODEV?

 +
 + if (offset % 4 != 0)
 + return -EPERM;

-EINVAL?

 +
 + if (offset = GEN7_L3LOG_SIZE)
 + return -ENXIO;

-E2BIG or -ERANGE?

And maybe some debug messages here so people can debug their daemons in
case some of the return values are identical.

 +
 + return 0;
 +}
 +
 +static ssize_t
 +i915_l3_read(struct file *filp, struct kobject *kobj,
 +  struct bin_attribute *attr, char *buf,
 +  loff_t offset, size_t count)
 +{
 + struct device *dev = container_of(kobj, struct device, kobj);
 + struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
 + struct drm_device *drm_dev = dminor-dev;
 + struct drm_i915_private *dev_priv = drm_dev-dev_private;
 + uint32_t misccpctl;
 + int i, ret;
 +
 + ret = l3_access_valid(drm_dev, offset);
 + if (ret)
 + return ret;
 +
 + ret = i915_mutex_lock_interruptible(drm_dev);
 + if (ret)
 + return ret;
 +
 + misccpctl = I915_READ(GEN7_MISCCPCTL);
 + I915_WRITE(GEN7_MISCCPCTL, misccpctl  ~GEN7_DOP_CLOCK_GATE_ENABLE);
 +
 + for (i = offset; count = 4  i  GEN7_L3LOG_SIZE; i += 4, count -= 4)
 + *((uint32_t *)(buf[i])) = I915_READ(GEN7_L3LOG_BASE + i);
 +
 + I915_WRITE(GEN7_MISCCPCTL, misccpctl);
 +
 + mutex_unlock(drm_dev-struct_mutex);
 +
 + return i - offset;
 +}
 +
 +static ssize_t
 +i915_l3_write(struct file *filp, struct kobject *kobj,
 +   struct bin_attribute *attr, char *buf,
 +   loff_t offset, size_t count)
 +{
 + struct device *dev = container_of(kobj, struct device, kobj);
 + struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
 + struct drm_device *drm_dev = dminor-dev;
 + struct drm_i915_private *dev_priv = drm_dev-dev_private;
 + u32 *temp = NULL;
 + int i, ret;
 +
 + ret = l3_access_valid(drm_dev, offset);
 + if (ret)
 + return ret;
 +
 + ret = i915_mutex_lock_interruptible(drm_dev);
 + if (ret)
 + return ret;
 +
 + if (!dev_priv-mm.l3_remap_info) {
 + temp = kzalloc(GEN7_L3LOG_SIZE, GFP_KERNEL);
 + if (!temp) {
 + mutex_unlock(drm_dev-struct_mutex);
 + return -ENOMEM;
 + }
 + }

Grumble, lazy allocation.  But if we did this at driver load time we'd
also need a valid bit so we knew whether to try to load it or not...

 +
 + ret = i915_gpu_idle(drm_dev, true);
 + if (ret) {
 + kfree(temp);
 + mutex_unlock(drm_dev-struct_mutex);
 + return ret;
 + }
 +
 + /* TODO: Ideally we really want a GPU reset here to make sure errors
 +  * aren't propagated Since I cannot find a stable way to reset the GPU
 +  * at this point it is left as a TODO.
 + */

Do we want a reset?  Or just to restart the app that consumed the
error?  If the latter, then the current uevent ought to be enough,
potentially with a configurable SIGBUS or something.

 +
 + if (dev_priv-mm.l3_remap_info)
 + temp = dev_priv-mm.l3_remap_info;
 +
 + dev_priv-mm.l3_remap_info = temp;

I'd combine these assignments with the kzalloc block above just to keep
things together.

 +
 + for (i = offset; count = 4  i  GEN7_L3LOG_SIZE; i += 4, count -= 4) 
 {
 + u32 remap = I915_READ(GEN7_L3LOG_BASE + i);
 + if (remap  remap != *temp)
 + DRM_ERROR(0x%x was already programmed to %x\n,
 +   GEN7_L3LOG_BASE + i, remap);
 + *temp++ = *(uint32_t *)(buf[i]);
 + }

Is this an error?  Or just a 

Re: [Intel-gfx] [PATCH 5/5] drm/i915: l3 parity sysfs interface

2012-05-25 Thread Ben Widawsky
On Fri, 25 May 2012 10:51:19 -0700
Jesse Barnes jbar...@virtuousgeek.org wrote:

 On Fri, 27 Apr 2012 17:40:21 -0700
 Ben Widawsky b...@bwidawsk.net wrote:
 
  Dumb binary interfaces which allow root-only updates of the cache
  remapping registers. As mentioned in a previous patch, software using
  this interface needs to know about HW limits, and other programming
  considerations as the kernel interface does no checking for these things
  on the root-only interface.
  
  v1: Drop extra posting reads (Chris)
  Return negative values in the sysfs interfaces on errors (Chris)
  
  Signed-off-by: Ben Widawsky b...@bwidawsk.net
  ---
   drivers/gpu/drm/i915/i915_sysfs.c |  128 
  -
   1 file changed, 126 insertions(+), 2 deletions(-)
  
  diff --git a/drivers/gpu/drm/i915/i915_sysfs.c 
  b/drivers/gpu/drm/i915/i915_sysfs.c
  index 79f8344..ed77cbf 100644
  --- a/drivers/gpu/drm/i915/i915_sysfs.c
  +++ b/drivers/gpu/drm/i915/i915_sysfs.c
  @@ -29,6 +29,7 @@
   #include linux/module.h
   #include linux/stat.h
   #include linux/sysfs.h
  +#include intel_drv.h
   #include i915_drv.h
   
   static u32 calc_residency(struct drm_device *dev, const u32 reg)
  @@ -92,20 +93,143 @@ static struct attribute_group rc6_attr_group = {
  .attrs =  rc6_attrs
   };
   
  +static int l3_access_valid(struct drm_device *dev, loff_t offset)
  +{
  +   if (!IS_IVYBRIDGE(dev))
  +   return -EPERM;
 
 -ENODEV?
 
  +
  +   if (offset % 4 != 0)
  +   return -EPERM;
 
 -EINVAL?
 
  +
  +   if (offset = GEN7_L3LOG_SIZE)
  +   return -ENXIO;
 
 -E2BIG or -ERANGE?
 
 And maybe some debug messages here so people can debug their daemons in
 case some of the return values are identical.
 

Good point, I've gone with EPERM, EINVAL, and ENXIO - so no more
duplication and added the debug message for good measure.

  +
  +   return 0;
  +}
  +
  +static ssize_t
  +i915_l3_read(struct file *filp, struct kobject *kobj,
  +struct bin_attribute *attr, char *buf,
  +loff_t offset, size_t count)
  +{
  +   struct device *dev = container_of(kobj, struct device, kobj);
  +   struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
  +   struct drm_device *drm_dev = dminor-dev;
  +   struct drm_i915_private *dev_priv = drm_dev-dev_private;
  +   uint32_t misccpctl;
  +   int i, ret;
  +
  +   ret = l3_access_valid(drm_dev, offset);
  +   if (ret)
  +   return ret;
  +
  +   ret = i915_mutex_lock_interruptible(drm_dev);
  +   if (ret)
  +   return ret;
  +
  +   misccpctl = I915_READ(GEN7_MISCCPCTL);
  +   I915_WRITE(GEN7_MISCCPCTL, misccpctl  ~GEN7_DOP_CLOCK_GATE_ENABLE);
  +
  +   for (i = offset; count = 4  i  GEN7_L3LOG_SIZE; i += 4, count -= 4)
  +   *((uint32_t *)(buf[i])) = I915_READ(GEN7_L3LOG_BASE + i);
  +
  +   I915_WRITE(GEN7_MISCCPCTL, misccpctl);
  +
  +   mutex_unlock(drm_dev-struct_mutex);
  +
  +   return i - offset;
  +}
  +
  +static ssize_t
  +i915_l3_write(struct file *filp, struct kobject *kobj,
  + struct bin_attribute *attr, char *buf,
  + loff_t offset, size_t count)
  +{
  +   struct device *dev = container_of(kobj, struct device, kobj);
  +   struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
  +   struct drm_device *drm_dev = dminor-dev;
  +   struct drm_i915_private *dev_priv = drm_dev-dev_private;
  +   u32 *temp = NULL;
  +   int i, ret;
  +
  +   ret = l3_access_valid(drm_dev, offset);
  +   if (ret)
  +   return ret;
  +
  +   ret = i915_mutex_lock_interruptible(drm_dev);
  +   if (ret)
  +   return ret;
  +
  +   if (!dev_priv-mm.l3_remap_info) {
  +   temp = kzalloc(GEN7_L3LOG_SIZE, GFP_KERNEL);
  +   if (!temp) {
  +   mutex_unlock(drm_dev-struct_mutex);
  +   return -ENOMEM;
  +   }
  +   }
 
 Grumble, lazy allocation.  But if we did this at driver load time we'd
 also need a valid bit so we knew whether to try to load it or not...
 

Just curious what the grumble is about? It is a sysfs entry after all.

  +
  +   ret = i915_gpu_idle(drm_dev, true);
  +   if (ret) {
  +   kfree(temp);
  +   mutex_unlock(drm_dev-struct_mutex);
  +   return ret;
  +   }
  +
  +   /* TODO: Ideally we really want a GPU reset here to make sure errors
  +* aren't propagated Since I cannot find a stable way to reset the GPU
  +* at this point it is left as a TODO.
  +   */
 
 Do we want a reset?  Or just to restart the app that consumed the
 error?  If the latter, then the current uevent ought to be enough,
 potentially with a configurable SIGBUS or something.
 

I've thought about this a great deal. I think as long as every app
referencing the buffer is killed, and we do a full pipeline flush after
the remapping, we should be able to avoid a reset. I like the idea of
SIGBUS, but it's a bit of work to track which buffer is actually bad,
and with buffer re-use, potentially harder to find the 

Re: [Intel-gfx] [PATCH 5/5] drm/i915: l3 parity sysfs interface

2012-05-01 Thread Daniel Vetter
On Fri, Apr 27, 2012 at 05:40:21PM -0700, Ben Widawsky wrote:
 Dumb binary interfaces which allow root-only updates of the cache
 remapping registers. As mentioned in a previous patch, software using
 this interface needs to know about HW limits, and other programming
 considerations as the kernel interface does no checking for these things
 on the root-only interface.
 
 v1: Drop extra posting reads (Chris)
 Return negative values in the sysfs interfaces on errors (Chris)
 
 Signed-off-by: Ben Widawsky b...@bwidawsk.net
 ---
  drivers/gpu/drm/i915/i915_sysfs.c |  128 
 -
  1 file changed, 126 insertions(+), 2 deletions(-)
 
 diff --git a/drivers/gpu/drm/i915/i915_sysfs.c 
 b/drivers/gpu/drm/i915/i915_sysfs.c
 index 79f8344..ed77cbf 100644
 --- a/drivers/gpu/drm/i915/i915_sysfs.c
 +++ b/drivers/gpu/drm/i915/i915_sysfs.c
 @@ -29,6 +29,7 @@
  #include linux/module.h
  #include linux/stat.h
  #include linux/sysfs.h
 +#include intel_drv.h
  #include i915_drv.h
  
  static u32 calc_residency(struct drm_device *dev, const u32 reg)
 @@ -92,20 +93,143 @@ static struct attribute_group rc6_attr_group = {
   .attrs =  rc6_attrs
  };
  
 +static int l3_access_valid(struct drm_device *dev, loff_t offset)
 +{
 + if (!IS_IVYBRIDGE(dev))
 + return -EPERM;
 +
 + if (offset % 4 != 0)
 + return -EPERM;
 +
 + if (offset = GEN7_L3LOG_SIZE)
 + return -ENXIO;
 +
 + return 0;
 +}
 +
 +static ssize_t
 +i915_l3_read(struct file *filp, struct kobject *kobj,
 +  struct bin_attribute *attr, char *buf,
 +  loff_t offset, size_t count)
 +{
 + struct device *dev = container_of(kobj, struct device, kobj);
 + struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
 + struct drm_device *drm_dev = dminor-dev;
 + struct drm_i915_private *dev_priv = drm_dev-dev_private;
 + uint32_t misccpctl;
 + int i, ret;
 +
 + ret = l3_access_valid(drm_dev, offset);
 + if (ret)
 + return ret;
 +
 + ret = i915_mutex_lock_interruptible(drm_dev);
 + if (ret)
 + return ret;
 +
 + misccpctl = I915_READ(GEN7_MISCCPCTL);
 + I915_WRITE(GEN7_MISCCPCTL, misccpctl  ~GEN7_DOP_CLOCK_GATE_ENABLE);
 +
 + for (i = offset; count = 4  i  GEN7_L3LOG_SIZE; i += 4, count -= 4)
 + *((uint32_t *)(buf[i])) = I915_READ(GEN7_L3LOG_BASE + i);
 +
 + I915_WRITE(GEN7_MISCCPCTL, misccpctl);
 +
 + mutex_unlock(drm_dev-struct_mutex);
 +
 + return i - offset;
 +}
 +
 +static ssize_t
 +i915_l3_write(struct file *filp, struct kobject *kobj,
 +   struct bin_attribute *attr, char *buf,
 +   loff_t offset, size_t count)
 +{
 + struct device *dev = container_of(kobj, struct device, kobj);
 + struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
 + struct drm_device *drm_dev = dminor-dev;
 + struct drm_i915_private *dev_priv = drm_dev-dev_private;
 + u32 *temp = NULL;
 + int i, ret;
 +
 + ret = l3_access_valid(drm_dev, offset);
 + if (ret)
 + return ret;
 +
 + ret = i915_mutex_lock_interruptible(drm_dev);
 + if (ret)
 + return ret;
 +
 + if (!dev_priv-mm.l3_remap_info) {
 + temp = kzalloc(GEN7_L3LOG_SIZE, GFP_KERNEL);
 + if (!temp) {
 + mutex_unlock(drm_dev-struct_mutex);
 + return -ENOMEM;
 + }
 + }
 +
 + ret = i915_gpu_idle(drm_dev, true);
 + if (ret) {
 + kfree(temp);
 + mutex_unlock(drm_dev-struct_mutex);
 + return ret;
 + }
 +
 + /* TODO: Ideally we really want a GPU reset here to make sure errors
 +  * aren't propagated Since I cannot find a stable way to reset the GPU
 +  * at this point it is left as a TODO.
 + */
 +
 + if (dev_priv-mm.l3_remap_info)
 + temp = dev_priv-mm.l3_remap_info;
 +
 + dev_priv-mm.l3_remap_info = temp;
 +
 + for (i = offset; count = 4  i  GEN7_L3LOG_SIZE; i += 4, count -= 4) 
 {
 + u32 remap = I915_READ(GEN7_L3LOG_BASE + i);
 + if (remap  remap != *temp)
 + DRM_ERROR(0x%x was already programmed to %x\n,
 +   GEN7_L3LOG_BASE + i, remap);
 + *temp++ = *(uint32_t *)(buf[i]);
 + }
 +
 + i915_gem_l3_remap(drm_dev);
 +
 + mutex_unlock(drm_dev-struct_mutex);
 +
 + return offset - i;
 +}
 +
 +static struct bin_attribute dpf_attrs = {
 + .attr = {.name = l3_parity, .mode = (S_IRUSR | S_IWUSR)},
 + .size = GEN7_L3LOG_SIZE,
 + .read = i915_l3_read,
 + .write = i915_l3_write,
 + .mmap = NULL
 +};
 +
  void i915_setup_sysfs(struct drm_device *dev)
  {
   int ret;
  
 - /* ILK doesn't have any residency information */
 + /* ILK and below don't yet have relevant sysfs files */
   if (INTEL_INFO(dev)-gen  6)
   return;
  
   ret = 

Re: [Intel-gfx] [PATCH 5/5] drm/i915: l3 parity sysfs interface

2012-05-01 Thread Ben Widawsky
On Tue, 1 May 2012 20:24:44 +0200
Daniel Vetter dan...@ffwll.ch wrote:

 On Fri, Apr 27, 2012 at 05:40:21PM -0700, Ben Widawsky wrote:
  Dumb binary interfaces which allow root-only updates of the cache
  remapping registers. As mentioned in a previous patch, software using
  this interface needs to know about HW limits, and other programming
  considerations as the kernel interface does no checking for these things
  on the root-only interface.
  
  v1: Drop extra posting reads (Chris)
  Return negative values in the sysfs interfaces on errors (Chris)
  
  Signed-off-by: Ben Widawsky b...@bwidawsk.net
  ---
   drivers/gpu/drm/i915/i915_sysfs.c |  128 
  -
   1 file changed, 126 insertions(+), 2 deletions(-)
  
  diff --git a/drivers/gpu/drm/i915/i915_sysfs.c 
  b/drivers/gpu/drm/i915/i915_sysfs.c
  index 79f8344..ed77cbf 100644
  --- a/drivers/gpu/drm/i915/i915_sysfs.c
  +++ b/drivers/gpu/drm/i915/i915_sysfs.c
  @@ -29,6 +29,7 @@
   #include linux/module.h
   #include linux/stat.h
   #include linux/sysfs.h
  +#include intel_drv.h
   #include i915_drv.h
   
   static u32 calc_residency(struct drm_device *dev, const u32 reg)
  @@ -92,20 +93,143 @@ static struct attribute_group rc6_attr_group = {
  .attrs =  rc6_attrs
   };
   
  +static int l3_access_valid(struct drm_device *dev, loff_t offset)
  +{
  +   if (!IS_IVYBRIDGE(dev))
  +   return -EPERM;
  +
  +   if (offset % 4 != 0)
  +   return -EPERM;
  +
  +   if (offset = GEN7_L3LOG_SIZE)
  +   return -ENXIO;
  +
  +   return 0;
  +}
  +
  +static ssize_t
  +i915_l3_read(struct file *filp, struct kobject *kobj,
  +struct bin_attribute *attr, char *buf,
  +loff_t offset, size_t count)
  +{
  +   struct device *dev = container_of(kobj, struct device, kobj);
  +   struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
  +   struct drm_device *drm_dev = dminor-dev;
  +   struct drm_i915_private *dev_priv = drm_dev-dev_private;
  +   uint32_t misccpctl;
  +   int i, ret;
  +
  +   ret = l3_access_valid(drm_dev, offset);
  +   if (ret)
  +   return ret;
  +
  +   ret = i915_mutex_lock_interruptible(drm_dev);
  +   if (ret)
  +   return ret;
  +
  +   misccpctl = I915_READ(GEN7_MISCCPCTL);
  +   I915_WRITE(GEN7_MISCCPCTL, misccpctl  ~GEN7_DOP_CLOCK_GATE_ENABLE);
  +
  +   for (i = offset; count = 4  i  GEN7_L3LOG_SIZE; i += 4, count -= 4)
  +   *((uint32_t *)(buf[i])) = I915_READ(GEN7_L3LOG_BASE + i);
  +
  +   I915_WRITE(GEN7_MISCCPCTL, misccpctl);
  +
  +   mutex_unlock(drm_dev-struct_mutex);
  +
  +   return i - offset;
  +}
  +
  +static ssize_t
  +i915_l3_write(struct file *filp, struct kobject *kobj,
  + struct bin_attribute *attr, char *buf,
  + loff_t offset, size_t count)
  +{
  +   struct device *dev = container_of(kobj, struct device, kobj);
  +   struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
  +   struct drm_device *drm_dev = dminor-dev;
  +   struct drm_i915_private *dev_priv = drm_dev-dev_private;
  +   u32 *temp = NULL;
  +   int i, ret;
  +
  +   ret = l3_access_valid(drm_dev, offset);
  +   if (ret)
  +   return ret;
  +
  +   ret = i915_mutex_lock_interruptible(drm_dev);
  +   if (ret)
  +   return ret;
  +
  +   if (!dev_priv-mm.l3_remap_info) {
  +   temp = kzalloc(GEN7_L3LOG_SIZE, GFP_KERNEL);
  +   if (!temp) {
  +   mutex_unlock(drm_dev-struct_mutex);
  +   return -ENOMEM;
  +   }
  +   }
  +
  +   ret = i915_gpu_idle(drm_dev, true);
  +   if (ret) {
  +   kfree(temp);
  +   mutex_unlock(drm_dev-struct_mutex);
  +   return ret;
  +   }
  +
  +   /* TODO: Ideally we really want a GPU reset here to make sure errors
  +* aren't propagated Since I cannot find a stable way to reset the GPU
  +* at this point it is left as a TODO.
  +   */
  +
  +   if (dev_priv-mm.l3_remap_info)
  +   temp = dev_priv-mm.l3_remap_info;
  +
  +   dev_priv-mm.l3_remap_info = temp;
  +
  +   for (i = offset; count = 4  i  GEN7_L3LOG_SIZE; i += 4, count -= 4) 
  {
  +   u32 remap = I915_READ(GEN7_L3LOG_BASE + i);
  +   if (remap  remap != *temp)
  +   DRM_ERROR(0x%x was already programmed to %x\n,
  + GEN7_L3LOG_BASE + i, remap);
  +   *temp++ = *(uint32_t *)(buf[i]);
  +   }
  +
  +   i915_gem_l3_remap(drm_dev);
  +
  +   mutex_unlock(drm_dev-struct_mutex);
  +
  +   return offset - i;
  +}
  +
  +static struct bin_attribute dpf_attrs = {
  +   .attr = {.name = l3_parity, .mode = (S_IRUSR | S_IWUSR)},
  +   .size = GEN7_L3LOG_SIZE,
  +   .read = i915_l3_read,
  +   .write = i915_l3_write,
  +   .mmap = NULL
  +};
  +
   void i915_setup_sysfs(struct drm_device *dev)
   {
  int ret;
   
  -   /* ILK doesn't have any residency information */
  +   /* ILK and below don't yet have relevant sysfs files */
  if (INTEL_INFO(dev)-gen 

[Intel-gfx] [PATCH 5/5] drm/i915: l3 parity sysfs interface

2012-04-27 Thread Ben Widawsky
Dumb binary interfaces which allow root-only updates of the cache
remapping registers. As mentioned in a previous patch, software using
this interface needs to know about HW limits, and other programming
considerations as the kernel interface does no checking for these things
on the root-only interface.

v1: Drop extra posting reads (Chris)
Return negative values in the sysfs interfaces on errors (Chris)

Signed-off-by: Ben Widawsky b...@bwidawsk.net
---
 drivers/gpu/drm/i915/i915_sysfs.c |  128 -
 1 file changed, 126 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_sysfs.c 
b/drivers/gpu/drm/i915/i915_sysfs.c
index 79f8344..ed77cbf 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -29,6 +29,7 @@
 #include linux/module.h
 #include linux/stat.h
 #include linux/sysfs.h
+#include intel_drv.h
 #include i915_drv.h
 
 static u32 calc_residency(struct drm_device *dev, const u32 reg)
@@ -92,20 +93,143 @@ static struct attribute_group rc6_attr_group = {
.attrs =  rc6_attrs
 };
 
+static int l3_access_valid(struct drm_device *dev, loff_t offset)
+{
+   if (!IS_IVYBRIDGE(dev))
+   return -EPERM;
+
+   if (offset % 4 != 0)
+   return -EPERM;
+
+   if (offset = GEN7_L3LOG_SIZE)
+   return -ENXIO;
+
+   return 0;
+}
+
+static ssize_t
+i915_l3_read(struct file *filp, struct kobject *kobj,
+struct bin_attribute *attr, char *buf,
+loff_t offset, size_t count)
+{
+   struct device *dev = container_of(kobj, struct device, kobj);
+   struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
+   struct drm_device *drm_dev = dminor-dev;
+   struct drm_i915_private *dev_priv = drm_dev-dev_private;
+   uint32_t misccpctl;
+   int i, ret;
+
+   ret = l3_access_valid(drm_dev, offset);
+   if (ret)
+   return ret;
+
+   ret = i915_mutex_lock_interruptible(drm_dev);
+   if (ret)
+   return ret;
+
+   misccpctl = I915_READ(GEN7_MISCCPCTL);
+   I915_WRITE(GEN7_MISCCPCTL, misccpctl  ~GEN7_DOP_CLOCK_GATE_ENABLE);
+
+   for (i = offset; count = 4  i  GEN7_L3LOG_SIZE; i += 4, count -= 4)
+   *((uint32_t *)(buf[i])) = I915_READ(GEN7_L3LOG_BASE + i);
+
+   I915_WRITE(GEN7_MISCCPCTL, misccpctl);
+
+   mutex_unlock(drm_dev-struct_mutex);
+
+   return i - offset;
+}
+
+static ssize_t
+i915_l3_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t offset, size_t count)
+{
+   struct device *dev = container_of(kobj, struct device, kobj);
+   struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
+   struct drm_device *drm_dev = dminor-dev;
+   struct drm_i915_private *dev_priv = drm_dev-dev_private;
+   u32 *temp = NULL;
+   int i, ret;
+
+   ret = l3_access_valid(drm_dev, offset);
+   if (ret)
+   return ret;
+
+   ret = i915_mutex_lock_interruptible(drm_dev);
+   if (ret)
+   return ret;
+
+   if (!dev_priv-mm.l3_remap_info) {
+   temp = kzalloc(GEN7_L3LOG_SIZE, GFP_KERNEL);
+   if (!temp) {
+   mutex_unlock(drm_dev-struct_mutex);
+   return -ENOMEM;
+   }
+   }
+
+   ret = i915_gpu_idle(drm_dev, true);
+   if (ret) {
+   kfree(temp);
+   mutex_unlock(drm_dev-struct_mutex);
+   return ret;
+   }
+
+   /* TODO: Ideally we really want a GPU reset here to make sure errors
+* aren't propagated Since I cannot find a stable way to reset the GPU
+* at this point it is left as a TODO.
+   */
+
+   if (dev_priv-mm.l3_remap_info)
+   temp = dev_priv-mm.l3_remap_info;
+
+   dev_priv-mm.l3_remap_info = temp;
+
+   for (i = offset; count = 4  i  GEN7_L3LOG_SIZE; i += 4, count -= 4) 
{
+   u32 remap = I915_READ(GEN7_L3LOG_BASE + i);
+   if (remap  remap != *temp)
+   DRM_ERROR(0x%x was already programmed to %x\n,
+ GEN7_L3LOG_BASE + i, remap);
+   *temp++ = *(uint32_t *)(buf[i]);
+   }
+
+   i915_gem_l3_remap(drm_dev);
+
+   mutex_unlock(drm_dev-struct_mutex);
+
+   return offset - i;
+}
+
+static struct bin_attribute dpf_attrs = {
+   .attr = {.name = l3_parity, .mode = (S_IRUSR | S_IWUSR)},
+   .size = GEN7_L3LOG_SIZE,
+   .read = i915_l3_read,
+   .write = i915_l3_write,
+   .mmap = NULL
+};
+
 void i915_setup_sysfs(struct drm_device *dev)
 {
int ret;
 
-   /* ILK doesn't have any residency information */
+   /* ILK and below don't yet have relevant sysfs files */
if (INTEL_INFO(dev)-gen  6)
return;
 
ret = sysfs_merge_group(dev-primary-kdev.kobj, rc6_attr_group);
if