diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index a58fed9..7ea3250 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1985,6 +1985,7 @@ void i915_driver_postclose(struct drm_device *dev, struct drm_file *file)
 {
 	struct drm_i915_file_private *file_priv = file->driver_priv;
 
+	kfree(file_priv->process_name);
 	if (file_priv && file_priv->bsd_ring)
 		file_priv->bsd_ring = NULL;
 	kfree(file_priv);
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 1d6d9ac..9bee20e 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1628,6 +1628,8 @@ static struct drm_driver driver = {
 	.debugfs_init = i915_debugfs_init,
 	.debugfs_cleanup = i915_debugfs_cleanup,
 #endif
+	.gem_open_object = i915_gem_open_object,
+	.gem_close_object = i915_gem_close_object,
 	.gem_free_object = i915_gem_free_object,
 	.gem_vm_ops = &i915_gem_vm_ops,
 
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 36f3da6..43ba7c4 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1765,6 +1765,11 @@ struct drm_i915_gem_object_ops {
 #define INTEL_FRONTBUFFER_ALL_MASK(pipe) \
 	(0xf << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))
 
+struct drm_i915_obj_virt_addr {
+	struct list_head head;
+	unsigned long user_virt_addr;
+};
+
 struct drm_i915_gem_object {
 	struct drm_gem_object base;
 
@@ -1890,6 +1895,13 @@ struct drm_i915_gem_object {
 			struct work_struct *work;
 		} userptr;
 	};
+
+#define MAX_OPEN_HANDLE 20
+	struct {
+		struct list_head virt_addr_head;
+		pid_t pid;
+		int open_handle_count;
+	} pid_array[MAX_OPEN_HANDLE];
 };
 #define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base)
 
@@ -1940,6 +1952,8 @@ struct drm_i915_gem_request {
 struct drm_i915_file_private {
 	struct drm_i915_private *dev_priv;
 	struct drm_file *file;
+	char *process_name;
+	struct pid *tgid;
 
 	struct {
 		spinlock_t lock;
@@ -2370,6 +2384,10 @@ void i915_init_vm(struct drm_i915_private *dev_priv,
 		  struct i915_address_space *vm);
 void i915_gem_free_object(struct drm_gem_object *obj);
 void i915_gem_vma_destroy(struct i915_vma *vma);
+int i915_gem_open_object(struct drm_gem_object *gem_obj,
+			struct drm_file *file_priv);
+int i915_gem_close_object(struct drm_gem_object *gem_obj,
+			struct drm_file *file_priv);
 
 #define PIN_MAPPABLE 0x1
 #define PIN_NONBLOCK 0x2
@@ -2420,6 +2438,8 @@ int i915_gem_dumb_create(struct drm_file *file_priv,
 			 struct drm_mode_create_dumb *args);
 int i915_gem_mmap_gtt(struct drm_file *file_priv, struct drm_device *dev,
 		      uint32_t handle, uint64_t *offset);
+int i915_gem_obj_shmem_pages_alloced(struct drm_i915_gem_object *obj);
+
 /**
  * Returns true if seq1 is later than seq2.
  */
@@ -2686,6 +2706,10 @@ int i915_verify_lists(struct drm_device *dev);
 #else
 #define i915_verify_lists(dev) 0
 #endif
+int i915_get_drm_clients_info(struct drm_i915_error_state_buf *m,
+				struct drm_device *dev);
+int i915_gem_get_all_obj_info(struct drm_i915_error_state_buf *m,
+				struct drm_device *dev);
 
 /* i915_debugfs.c */
 int i915_debugfs_init(struct drm_minor *minor);
@@ -2699,6 +2723,8 @@ static inline void intel_display_crc_init(struct drm_device *dev) {}
 /* i915_gpu_error.c */
 __printf(2, 3)
 void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...);
+void i915_error_puts(struct drm_i915_error_state_buf *e,
+			    const char *str);
 int i915_error_state_to_str(struct drm_i915_error_state_buf *estr,
 			    const struct i915_error_state_file_priv *error);
 int i915_error_state_buf_init(struct drm_i915_error_state_buf *eb,
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 6c68570..3c36486 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1461,6 +1461,45 @@ unlock:
 	return ret;
 }
 
+static void
+i915_gem_obj_insert_virt_addr(struct drm_i915_gem_object *obj,
+				unsigned long addr,
+				bool is_map_gtt)
+{
+	pid_t current_pid = task_tgid_nr(current);
+	int i, found = 0;
+
+	if (is_map_gtt)
+		addr |= 1;
+
+	for (i = 0; i < MAX_OPEN_HANDLE; i++) {
+		if (obj->pid_array[i].pid == current_pid) {
+			struct drm_i915_obj_virt_addr *entry, *new_entry;
+
+			list_for_each_entry(entry,
+					    &obj->pid_array[i].virt_addr_head,
+					    head) {
+				if (entry->user_virt_addr == addr) {
+					found = 1;
+					break;
+				}
+			}
+			if (found)
+				break;
+			new_entry = kzalloc
+				(sizeof(struct drm_i915_obj_virt_addr),
+				GFP_KERNEL);
+			new_entry->user_virt_addr = addr;
+			list_add_tail(&new_entry->head,
+				&obj->pid_array[i].virt_addr_head);
+			break;
+		}
+	}
+	if (i == MAX_OPEN_HANDLE)
+		DRM_DEBUG("Couldn't find matching pid %d for obj 0x%x\n",
+			current_pid, (u32) obj);
+}
+
 /**
  * Maps the contents of an object, returning the address it is mapped
  * into.
@@ -1495,6 +1534,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
 	if (IS_ERR((void *)addr))
 		return addr;
 
+	i915_gem_obj_insert_virt_addr(to_intel_bo(obj), addr, false);
 	args->addr_ptr = (uint64_t) addr;
 
 	return 0;
@@ -1585,6 +1625,8 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 		}
 
 		obj->fault_mappable = true;
+		i915_gem_obj_insert_virt_addr(obj,
+			(unsigned long)vma->vm_start, true);
 	} else
 		ret = vm_insert_pfn(vma,
 				    (unsigned long)vmf->virtual_address,
@@ -1830,6 +1872,24 @@ i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj)
 	return obj->madv == I915_MADV_DONTNEED;
 }
 
+int i915_gem_obj_shmem_pages_alloced(struct drm_i915_gem_object *obj)
+{
+	int ret;
+
+	if (obj->base.filp) {
+		struct inode *inode = file_inode(obj->base.filp);
+		struct shmem_inode_info *info = SHMEM_I(inode);
+
+		if (!inode)
+			return 0;
+		spin_lock(&info->lock);
+		ret = inode->i_mapping->nrpages;
+		spin_unlock(&info->lock);
+		return ret;
+	}
+	return 0;
+}
+
 /* Immediately discard the backing storage */
 static void
 i915_gem_object_truncate(struct drm_i915_gem_object *obj)
@@ -4447,6 +4507,79 @@ static bool discard_backing_storage(struct drm_i915_gem_object *obj)
 	return atomic_long_read(&obj->base.filp->f_count) == 1;
 }
 
+int
+i915_gem_open_object(struct drm_gem_object *gem_obj,
+			struct drm_file *file_priv)
+{
+	struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
+	pid_t current_pid = task_tgid_nr(current);
+	int i, ret, free = -1;
+
+	ret = i915_mutex_lock_interruptible(gem_obj->dev);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < MAX_OPEN_HANDLE; i++) {
+		if (obj->pid_array[i].pid == current_pid) {
+			obj->pid_array[i].open_handle_count++;
+			break;
+		} else if (obj->pid_array[i].pid == 0)
+			free = i;
+	}
+
+	if (i == MAX_OPEN_HANDLE) {
+		if (free != -1) {
+			WARN_ON(obj->pid_array[free].open_handle_count);
+			obj->pid_array[free].open_handle_count = 1;
+			obj->pid_array[free].pid = current_pid;
+			INIT_LIST_HEAD(&obj->pid_array[free].virt_addr_head);
+		} else
+			DRM_DEBUG("Max open handle count limit: obj 0x%x\n",
+					(u32) obj);
+	}
+
+	mutex_unlock(&gem_obj->dev->struct_mutex);
+	return 0;
+}
+
+int
+i915_gem_close_object(struct drm_gem_object *gem_obj,
+			struct drm_file *file_priv)
+{
+	struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
+	pid_t current_pid = task_tgid_nr(current);
+	int i, ret;
+
+	ret = i915_mutex_lock_interruptible(gem_obj->dev);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < MAX_OPEN_HANDLE; i++) {
+		if (obj->pid_array[i].pid == current_pid) {
+			obj->pid_array[i].open_handle_count--;
+			if (obj->pid_array[i].open_handle_count == 0) {
+				struct drm_i915_obj_virt_addr *entry, *next;
+
+				list_for_each_entry_safe(entry, next,
+					&obj->pid_array[i].virt_addr_head,
+					head) {
+					list_del(&entry->head);
+					kfree(entry);
+				}
+				obj->pid_array[i].pid = 0;
+			}
+			break;
+		}
+	}
+	if (i == MAX_OPEN_HANDLE)
+		DRM_DEBUG("Couldn't find matching pid %d for obj 0x%x\n",
+				current_pid, (u32) obj);
+
+	mutex_unlock(&gem_obj->dev->struct_mutex);
+	return 0;
+}
+
+
 void i915_gem_free_object(struct drm_gem_object *gem_obj)
 {
 	struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
@@ -5072,13 +5205,37 @@ i915_gem_file_idle_work_handler(struct work_struct *work)
 	atomic_set(&file_priv->rps_wait_boost, false);
 }
 
+static int i915_gem_get_pid_cmdline(struct task_struct *task, char *buffer)
+{
+	int res = 0;
+	unsigned int len;
+	struct mm_struct *mm = get_task_mm(task);
+
+	if (!mm)
+		goto out;
+	if (!mm->arg_end)
+		goto out_mm;
+
+	len = mm->arg_end - mm->arg_start;
+
+	if (len > PAGE_SIZE)
+		len = PAGE_SIZE;
+
+	res = access_process_vm(task, mm->arg_start, buffer, len, 0);
+
+	if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE)
+		buffer[res-1] = '\0';
+out_mm:
+	mmput(mm);
+out:
+	return res;
+}
+
 int i915_gem_open(struct drm_device *dev, struct drm_file *file)
 {
 	struct drm_i915_file_private *file_priv;
 	int ret;
 
-	DRM_DEBUG_DRIVER("\n");
-
 	file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
 	if (!file_priv)
 		return -ENOMEM;
@@ -5086,6 +5243,14 @@ int i915_gem_open(struct drm_device *dev, struct drm_file *file)
 	file->driver_priv = file_priv;
 	file_priv->dev_priv = dev->dev_private;
 	file_priv->file = file;
+	file_priv->tgid = find_vpid(task_tgid_nr(current));
+	file_priv->process_name =  kzalloc(PAGE_SIZE, GFP_ATOMIC);
+	if (!file_priv->process_name) {
+		kfree(file_priv);
+		return -ENOMEM;
+	}
+
+	ret = i915_gem_get_pid_cmdline(current, file_priv->process_name);
 
 	spin_lock_init(&file_priv->mm.lock);
 	INIT_LIST_HEAD(&file_priv->mm.request_list);
diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c
index f462d1b..7a42891 100644
--- a/drivers/gpu/drm/i915/i915_gem_debug.c
+++ b/drivers/gpu/drm/i915/i915_gem_debug.c
@@ -25,6 +25,7 @@
  *
  */
 
+#include <linux/pid.h>
 #include <drm/drmP.h>
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
@@ -116,3 +117,544 @@ i915_verify_lists(struct drm_device *dev)
 	return warned = err;
 }
 #endif /* WATCH_LIST */
+
+struct per_file_obj_mem_info {
+	int num_obj;
+	int num_obj_shared;
+	int num_obj_private;
+	int num_obj_gtt_bound;
+	int num_obj_purged;
+	int num_obj_purgeable;
+	int num_obj_allocated;
+	int num_obj_fault_mappable;
+	int num_obj_stolen;
+	size_t gtt_space_allocated_shared;
+	size_t gtt_space_allocated_priv;
+	size_t phys_space_allocated_shared;
+	size_t phys_space_allocated_priv;
+	size_t phys_space_purgeable;
+	size_t phys_space_shared_proportion;
+	size_t fault_mappable_size;
+	size_t stolen_space_allocated;
+	char *process_name;
+};
+
+struct name_entry {
+	struct list_head head;
+	struct drm_hash_item hash_item;
+};
+
+struct pid_stat_entry {
+	struct list_head head;
+	struct list_head namefree;
+	struct drm_open_hash namelist;
+	struct per_file_obj_mem_info stats;
+	struct pid *pid;
+	int pid_num;
+};
+
+
+#define err_printf(e, ...) i915_error_printf(e, __VA_ARGS__)
+#define err_puts(e, s) i915_error_puts(e, s)
+
+static const char *get_pin_flag(struct drm_i915_gem_object *obj)
+{
+	if (obj->user_pin_count > 0)
+		return "P";
+	else if (i915_gem_obj_is_pinned(obj))
+		return "p";
+	return " ";
+}
+
+static const char *get_tiling_flag(struct drm_i915_gem_object *obj)
+{
+	switch (obj->tiling_mode) {
+	default:
+	case I915_TILING_NONE: return " ";
+	case I915_TILING_X: return "X";
+	case I915_TILING_Y: return "Y";
+	}
+}
+
+static int i915_obj_virt_addr_is_valid(struct drm_gem_object *obj,
+				struct pid *pid, unsigned long addr)
+{
+	struct task_struct *task;
+	struct mm_struct *mm;
+	struct vm_area_struct *vma;
+	int locked, ret = 0;
+
+	task = get_pid_task(pid, PIDTYPE_PID);
+	if (task == NULL) {
+		DRM_DEBUG("null task for pid=%d\n", pid_nr(pid));
+		return -EINVAL;
+	}
+
+	mm = get_task_mm(task);
+	if (mm == NULL) {
+		DRM_DEBUG("null mm for pid=%d\n", pid_nr(pid));
+		return -EINVAL;
+	}
+
+	locked = down_read_trylock(&mm->mmap_sem);
+
+	vma = find_vma(mm, addr);
+	if (vma) {
+		if (addr & 1) { /* mmap_gtt case */
+			if (vma->vm_pgoff*PAGE_SIZE == (unsigned long)
+				drm_vma_node_offset_addr(&obj->vma_node))
+				ret = 0;
+			else
+				ret = -EINVAL;
+		} else { /* mmap case */
+			if (vma->vm_file == obj->filp)
+				ret = 0;
+			else
+				ret = -EINVAL;
+		}
+	} else
+		ret = -EINVAL;
+
+	if (locked)
+		up_read(&mm->mmap_sem);
+
+	mmput(mm);
+	return ret;
+}
+
+static void i915_obj_pidarray_validate(struct drm_gem_object *gem_obj)
+{
+	struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
+	struct drm_device *dev = gem_obj->dev;
+	struct drm_i915_obj_virt_addr *entry, *next;
+	struct drm_file *file;
+	struct drm_i915_file_private *file_priv;
+	struct pid *tgid;
+	int pid_num, i, present;
+
+	/* Run a sanity check on pid_array. All entries in pid_array should
+	 * be subset of the the drm filelist pid entries.
+	 */
+	for (i = 0; i < MAX_OPEN_HANDLE; i++) {
+		if (obj->pid_array[i].pid == 0)
+			continue;
+
+		present = 0;
+		list_for_each_entry(file, &dev->filelist, lhead) {
+			file_priv = file->driver_priv;
+			tgid = file_priv->tgid;
+			pid_num = pid_nr(tgid);
+
+			if (pid_num == obj->pid_array[i].pid) {
+				present = 1;
+				break;
+			}
+		}
+		if (present == 0) {
+			DRM_DEBUG("stale_pid=%d\n", obj->pid_array[i].pid);
+			list_for_each_entry_safe(entry, next,
+					&obj->pid_array[i].virt_addr_head,
+					head) {
+				list_del(&entry->head);
+				kfree(entry);
+			}
+
+			obj->pid_array[i].open_handle_count = 0;
+			obj->pid_array[i].pid = 0;
+		} else {
+			/* Validate the virtual address list */
+			struct task_struct *task =
+				get_pid_task(tgid, PIDTYPE_PID);
+			if (task == NULL)
+				continue;
+
+			list_for_each_entry_safe(entry, next,
+					&obj->pid_array[i].virt_addr_head,
+					head) {
+				if (i915_obj_virt_addr_is_valid(gem_obj, tgid,
+				entry->user_virt_addr)) {
+					DRM_DEBUG("stale_addr=%ld\n",
+					entry->user_virt_addr);
+					list_del(&entry->head);
+					kfree(entry);
+				}
+			}
+		}
+	}
+}
+
+static int
+i915_describe_obj(struct drm_i915_error_state_buf *m,
+		struct drm_i915_gem_object *obj)
+{
+	int i;
+	struct i915_vma *vma;
+	struct drm_i915_obj_virt_addr *entry;
+
+	err_printf(m,
+		"%p: %7zdK  %s    %s     %s      %s     %s      %s       %s     ",
+		   &obj->base,
+		   obj->base.size / 1024,
+		   get_pin_flag(obj),
+		   get_tiling_flag(obj),
+		   obj->dirty ? "Y" : "N",
+		   obj->base.name ? "Y" : "N",
+		   (obj->userptr.mm != 0) ? "Y" : "N",
+		   obj->stolen ? "Y" : "N",
+		   (obj->pin_mappable || obj->fault_mappable) ? "Y" : "N");
+
+	if (obj->madv == __I915_MADV_PURGED)
+		err_printf(m, " purged    ");
+	else if (obj->madv == I915_MADV_DONTNEED)
+		err_printf(m, " purgeable   ");
+	else if (i915_gem_obj_shmem_pages_alloced(obj) != 0)
+		err_printf(m, " allocated   ");
+
+
+	list_for_each_entry(vma, &obj->vma_list, vma_link) {
+		if (!i915_is_ggtt(vma->vm))
+			err_puts(m, " PP    ");
+		else
+			err_puts(m, " G     ");
+		err_printf(m, "  %08lx ", vma->node.start);
+	}
+
+	for (i = 0; i < MAX_OPEN_HANDLE; i++) {
+		if (obj->pid_array[i].pid != 0) {
+			err_printf(m, " (%d: %d:",
+			obj->pid_array[i].pid,
+			obj->pid_array[i].open_handle_count);
+			list_for_each_entry(entry,
+				&obj->pid_array[i].virt_addr_head, head) {
+				if (entry->user_virt_addr & 1)
+					err_printf(m, " %p",
+					(void *)(entry->user_virt_addr & ~1));
+				else
+					err_printf(m, " %p*",
+					(void *)entry->user_virt_addr);
+			}
+			err_printf(m, ") ");
+		}
+	}
+
+	err_printf(m, "\n");
+
+	if (m->bytes == 0 && m->err)
+		return m->err;
+
+	return 0;
+}
+
+static int
+i915_drm_gem_obj_info(int id, void *ptr, void *data)
+{
+	struct drm_i915_gem_object *obj = ptr;
+	struct drm_i915_error_state_buf *m = data;
+	int ret;
+
+	i915_obj_pidarray_validate(&obj->base);
+	ret = i915_describe_obj(m, obj);
+
+	return ret;
+}
+
+static int
+i915_drm_gem_object_per_file_summary(int id, void *ptr, void *data)
+{
+	struct pid_stat_entry *pid_entry = data;
+	struct drm_i915_gem_object *obj = ptr;
+	struct per_file_obj_mem_info *stats = &pid_entry->stats;
+	struct drm_hash_item *hash_item;
+	int i, obj_shared_count = 0;
+
+	i915_obj_pidarray_validate(&obj->base);
+
+	stats->num_obj++;
+
+	if (obj->base.name) {
+
+		if (drm_ht_find_item(&pid_entry->namelist,
+				(unsigned long)obj->base.name, &hash_item)) {
+			struct name_entry *entry =
+				kzalloc(sizeof(struct name_entry), GFP_KERNEL);
+			if (entry == NULL) {
+				DRM_ERROR("alloc failed\n");
+				return -ENOMEM;
+			}
+			entry->hash_item.key = obj->base.name;
+			drm_ht_insert_item(&pid_entry->namelist,
+					&entry->hash_item);
+			list_add_tail(&entry->head, &pid_entry->namefree);
+		} else {
+			DRM_DEBUG("Duplicate obj with name %d for process %s\n",
+				obj->base.name, stats->process_name);
+			return 0;
+		}
+		for (i = 0; i < MAX_OPEN_HANDLE; i++) {
+			if (obj->pid_array[i].pid != 0)
+				obj_shared_count++;
+		}
+		if (WARN_ON(obj_shared_count == 0))
+			return 1;
+
+		DRM_DEBUG("Obj: %p, shared count =%d\n",
+			&obj->base, obj_shared_count);
+
+		if (obj_shared_count > 1)
+			stats->num_obj_shared++;
+		else
+			stats->num_obj_private++;
+	} else {
+		obj_shared_count = 1;
+		stats->num_obj_private++;
+	}
+
+	if (i915_gem_obj_bound_any(obj)) {
+		stats->num_obj_gtt_bound++;
+		if (obj_shared_count > 1)
+			stats->gtt_space_allocated_shared += obj->base.size;
+		else
+			stats->gtt_space_allocated_priv += obj->base.size;
+	}
+
+	if (obj->stolen) {
+		stats->num_obj_stolen++;
+		stats->stolen_space_allocated += obj->base.size;
+	} else if (obj->madv == __I915_MADV_PURGED) {
+		stats->num_obj_purged++;
+	} else if (obj->madv == I915_MADV_DONTNEED) {
+		stats->num_obj_purgeable++;
+		stats->num_obj_allocated++;
+		if (i915_gem_obj_shmem_pages_alloced(obj) != 0) {
+			stats->phys_space_purgeable += obj->base.size;
+			if (obj_shared_count > 1) {
+				stats->phys_space_allocated_shared +=
+					obj->base.size;
+				stats->phys_space_shared_proportion +=
+					obj->base.size/obj_shared_count;
+			} else
+				stats->phys_space_allocated_priv +=
+					obj->base.size;
+		} else
+			WARN_ON(1);
+	} else if (i915_gem_obj_shmem_pages_alloced(obj) != 0) {
+		stats->num_obj_allocated++;
+			if (obj_shared_count > 1) {
+				stats->phys_space_allocated_shared +=
+					obj->base.size;
+				stats->phys_space_shared_proportion +=
+					obj->base.size/obj_shared_count;
+			}
+		else
+			stats->phys_space_allocated_priv += obj->base.size;
+	}
+	if (obj->fault_mappable) {
+		stats->num_obj_fault_mappable++;
+		stats->fault_mappable_size += obj->base.size;
+	}
+	return 0;
+}
+
+int i915_get_drm_clients_info(struct drm_i915_error_state_buf *m,
+			struct drm_device *dev)
+{
+	struct drm_file *file;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	struct name_entry *entry, *next;
+	struct pid_stat_entry *pid_entry, *temp_entry;
+	struct pid_stat_entry *new_pid_entry, *new_temp_entry;
+	struct list_head per_pid_stats, sorted_pid_stats;
+	int ret = 0, total_shared_prop_space = 0, total_priv_space = 0;
+
+	INIT_LIST_HEAD(&per_pid_stats);
+	INIT_LIST_HEAD(&sorted_pid_stats);
+
+	err_printf(m,
+		"\n\n  pid   Total  Shared  Priv   Purgeable  Alloced  SharedPHYsize   SharedPHYprop    PrivPHYsize   PurgeablePHYsize   process\n");
+
+	/* Protect the access to global drm resources such as filelist. Protect
+	 * against their removal under our noses, while in use.
+	 */
+	mutex_lock(&drm_global_mutex);
+	ret = i915_mutex_lock_interruptible(dev);
+	if (ret) {
+		mutex_unlock(&drm_global_mutex);
+		return ret;
+	}
+
+	list_for_each_entry(file, &dev->filelist, lhead) {
+		struct pid *tgid;
+		struct drm_i915_file_private *file_priv = file->driver_priv;
+		int pid_num, found = 0;
+
+		tgid = file_priv->tgid;
+		pid_num = pid_nr(tgid);
+
+		list_for_each_entry(pid_entry, &per_pid_stats, head) {
+			if (pid_entry->pid_num == pid_num) {
+				found = 1;
+				break;
+			}
+		}
+
+		if (!found) {
+			struct pid_stat_entry *new_entry =
+				kzalloc(sizeof(struct pid_stat_entry),
+					GFP_KERNEL);
+			if (new_entry == NULL) {
+				DRM_ERROR("alloc failed\n");
+				ret = -ENOMEM;
+				goto out_unlock;
+			}
+			new_entry->pid = tgid;
+			new_entry->pid_num = pid_num;
+			list_add_tail(&new_entry->head, &per_pid_stats);
+			drm_ht_create(&new_entry->namelist,
+				DRM_MAGIC_HASH_ORDER);
+			INIT_LIST_HEAD(&new_entry->namefree);
+			new_entry->stats.process_name = file_priv->process_name;
+			pid_entry = new_entry;
+		}
+
+		ret = idr_for_each(&file->object_idr,
+			&i915_drm_gem_object_per_file_summary, pid_entry);
+		if (ret)
+			break;
+	}
+
+	list_for_each_entry_safe(pid_entry, temp_entry, &per_pid_stats, head) {
+		if (list_empty(&sorted_pid_stats)) {
+			list_del(&pid_entry->head);
+			list_add_tail(&pid_entry->head, &sorted_pid_stats);
+			continue;
+		}
+
+		list_for_each_entry_safe(new_pid_entry, new_temp_entry,
+			&sorted_pid_stats, head) {
+			int prev_space =
+				pid_entry->stats.phys_space_shared_proportion +
+				pid_entry->stats.phys_space_allocated_priv;
+			int new_space =
+				new_pid_entry->
+				stats.phys_space_shared_proportion +
+				new_pid_entry->stats.phys_space_allocated_priv;
+			if (prev_space > new_space) {
+				list_del(&pid_entry->head);
+				list_add_tail(&pid_entry->head,
+					&new_pid_entry->head);
+				break;
+			}
+			if (list_is_last(&new_pid_entry->head,
+				&sorted_pid_stats)) {
+				list_del(&pid_entry->head);
+				list_add_tail(&pid_entry->head,
+						&sorted_pid_stats);
+			}
+		}
+	}
+
+	list_for_each_entry_safe(pid_entry, temp_entry,
+				&sorted_pid_stats, head) {
+		struct task_struct *task = get_pid_task(pid_entry->pid,
+							PIDTYPE_PID);
+		err_printf(m,
+			"%5d %6d %6d %6d %9d %8d %14zdK %14zdK %14zdK  %14zdK     %s",
+			   pid_entry->pid_num,
+			   pid_entry->stats.num_obj,
+			   pid_entry->stats.num_obj_shared,
+			   pid_entry->stats.num_obj_private,
+			   pid_entry->stats.num_obj_purgeable,
+			   pid_entry->stats.num_obj_allocated,
+			   pid_entry->stats.phys_space_allocated_shared/1024,
+			   pid_entry->stats.phys_space_shared_proportion/1024,
+			   pid_entry->stats.phys_space_allocated_priv/1024,
+			   pid_entry->stats.phys_space_purgeable/1024,
+			   pid_entry->stats.process_name);
+
+		if (task == NULL)
+			err_printf(m, "*\n");
+		else
+			err_printf(m, "\n");
+
+		total_shared_prop_space +=
+			pid_entry->stats.phys_space_shared_proportion/1024;
+		total_priv_space +=
+			pid_entry->stats.phys_space_allocated_priv/1024;
+		list_del(&pid_entry->head);
+
+		list_for_each_entry_safe(entry, next,
+					&pid_entry->namefree, head) {
+			list_del(&entry->head);
+			drm_ht_remove_item(&pid_entry->namelist,
+					&entry->hash_item);
+			kfree(entry);
+		}
+		drm_ht_remove(&pid_entry->namelist);
+		kfree(pid_entry);
+	}
+
+	err_printf(m,
+		"\t\t\t\t\t\t\t\t--------------\t-------------\t--------\n");
+	err_printf(m,
+		"\t\t\t\t\t\t\t\t%13zdK\t%12zdK\tTotal\n",
+			total_shared_prop_space, total_priv_space);
+
+out_unlock:
+	mutex_unlock(&dev->struct_mutex);
+	mutex_unlock(&drm_global_mutex);
+
+	if (ret)
+		return ret;
+	if (m->bytes == 0 && m->err)
+		return m->err;
+
+	return 0;
+}
+
+int i915_gem_get_all_obj_info(struct drm_i915_error_state_buf *m,
+			struct drm_device *dev)
+{
+	struct drm_file *file;
+	int pid_num, ret = 0;
+
+	/* Protect the access to global drm resources such as filelist. Protect
+	 * against their removal under our noses, while in use.
+	 */
+	mutex_lock(&drm_global_mutex);
+	ret = i915_mutex_lock_interruptible(dev);
+	if (ret) {
+		mutex_unlock(&drm_global_mutex);
+		return ret;
+	}
+
+	list_for_each_entry(file, &dev->filelist, lhead) {
+		struct pid *tgid;
+		struct drm_i915_file_private *file_priv = file->driver_priv;
+
+		tgid = file_priv->tgid;
+		pid_num = pid_nr(tgid);
+
+		err_printf(m, "\n\n  PID  process\n");
+
+		err_printf(m, "%5d  %s\n",
+			   pid_num, file_priv->process_name);
+
+		err_printf(m,
+			"\n Obj Identifier       Size Pin Tiling Dirty Shared Vmap Stolen Mappable  AllocState Global/PP  GttOffset (PID: handle count: user virt addrs)\n");
+		ret = idr_for_each(&file->object_idr,
+				&i915_drm_gem_obj_info, m);
+		if (ret)
+			break;
+	}
+	mutex_unlock(&dev->struct_mutex);
+	mutex_unlock(&drm_global_mutex);
+
+	if (ret)
+		return ret;
+	if (m->bytes == 0 && m->err)
+		return m->err;
+
+	return 0;
+}
+
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 2c87a79..089c7df 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -161,7 +161,7 @@ static void i915_error_vprintf(struct drm_i915_error_state_buf *e,
 	__i915_error_advance(e, len);
 }
 
-static void i915_error_puts(struct drm_i915_error_state_buf *e,
+void i915_error_puts(struct drm_i915_error_state_buf *e,
 			    const char *str)
 {
 	unsigned len;
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c
index 503847f..b204c92 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -582,6 +582,64 @@ static ssize_t error_state_write(struct file *file, struct kobject *kobj,
 	return count;
 }
 
+static ssize_t i915_gem_clients_state_read(struct file *filp,
+				struct kobject *kobj,
+				struct bin_attribute *attr,
+				char *buf, loff_t off, size_t count)
+{
+	struct device *kdev = container_of(kobj, struct device, kobj);
+	struct drm_minor *minor = dev_to_drm_minor(kdev);
+	struct drm_device *dev = minor->dev;
+	struct drm_i915_error_state_buf error_str;
+	ssize_t ret_count = 0;
+	int ret;
+
+	ret = i915_error_state_buf_init(&error_str, to_i915(dev), count, off);
+	if (ret)
+		return ret;
+
+	ret = i915_get_drm_clients_info(&error_str, dev);
+	if (ret)
+		goto out;
+
+	ret_count = count < error_str.bytes ? count : error_str.bytes;
+
+	memcpy(buf, error_str.buf, ret_count);
+out:
+	i915_error_state_buf_release(&error_str);
+
+	return ret ?: ret_count;
+}
+
+static ssize_t i915_gem_objects_state_read(struct file *filp,
+				struct kobject *kobj,
+				struct bin_attribute *attr,
+				char *buf, loff_t off, size_t count)
+{
+	struct device *kdev = container_of(kobj, struct device, kobj);
+	struct drm_minor *minor = dev_to_drm_minor(kdev);
+	struct drm_device *dev = minor->dev;
+	struct drm_i915_error_state_buf error_str;
+	ssize_t ret_count = 0;
+	int ret;
+
+	ret = i915_error_state_buf_init(&error_str, to_i915(dev), count, off);
+	if (ret)
+		return ret;
+
+	ret = i915_gem_get_all_obj_info(&error_str, dev);
+	if (ret)
+		goto out;
+
+	ret_count = count < error_str.bytes ? count : error_str.bytes;
+
+	memcpy(buf, error_str.buf, ret_count);
+out:
+	i915_error_state_buf_release(&error_str);
+
+	return ret ?: ret_count;
+}
+
 static struct bin_attribute error_state_attr = {
 	.attr.name = "error",
 	.attr.mode = S_IRUSR | S_IWUSR,
@@ -590,6 +648,20 @@ static struct bin_attribute error_state_attr = {
 	.write = error_state_write,
 };
 
+static struct bin_attribute i915_gem_client_state_attr = {
+	.attr.name = "i915_gem_meminfo",
+	.attr.mode = S_IRUSR | S_IWUSR,
+	.size = 0,
+	.read = i915_gem_clients_state_read,
+};
+
+static struct bin_attribute i915_gem_objects_state_attr = {
+	.attr.name = "i915_gem_objinfo",
+	.attr.mode = S_IRUSR | S_IWUSR,
+	.size = 0,
+	.read = i915_gem_objects_state_read,
+};
+
 void i915_setup_sysfs(struct drm_device *dev)
 {
 	int ret;
@@ -627,6 +699,17 @@ void i915_setup_sysfs(struct drm_device *dev)
 				    &error_state_attr);
 	if (ret)
 		DRM_ERROR("error_state sysfs setup failed\n");
+
+	ret = sysfs_create_bin_file(&dev->primary->kdev->kobj,
+				    &i915_gem_client_state_attr);
+	if (ret)
+		DRM_ERROR("i915_gem_client_state sysfs setup failed\n");
+
+	ret = sysfs_create_bin_file(&dev->primary->kdev->kobj,
+				    &i915_gem_objects_state_attr);
+	if (ret)
+		DRM_ERROR("i915_gem_objects_state sysfs setup failed\n");
+
 }
 
 void i915_teardown_sysfs(struct drm_device *dev)
