diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 7816cd5..94e7987 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -63,21 +63,6 @@ static char get_active_flag(struct drm_i915_gem_object *obj)
 	return i915_gem_object_is_active(obj) ? '*' : ' ';
 }
 
-static char get_pin_flag(struct drm_i915_gem_object *obj)
-{
-	return obj->pin_global ? 'p' : ' ';
-}
-
-static char get_tiling_flag(struct drm_i915_gem_object *obj)
-{
-	switch (i915_gem_object_get_tiling(obj)) {
-	default:
-	case I915_TILING_NONE: return ' ';
-	case I915_TILING_X: return 'X';
-	case I915_TILING_Y: return 'Y';
-	}
-}
-
 static char get_global_flag(struct drm_i915_gem_object *obj)
 {
 	return obj->userfault_count ? 'g' : ' ';
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index b760133..85a3dd9 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -337,6 +337,7 @@ struct drm_i915_file_private {
 	} rps_client;
 
 	unsigned int bsd_engine;
+	struct bin_attribute *obj_attr;
 
 /* Client can have a maximum of 3 contexts banned before
  * it is denied of creating new contexts. As one context
@@ -982,6 +983,8 @@ struct i915_gem_mm {
 	u32 object_count;
 
 	size_t phys_mem_total;
+	/* accounting all gem clients */
+	struct list_head gem_client_pids;
 };
 
 #define I915_IDLE_ENGINES_TIMEOUT (200) /* in ms */
@@ -2890,6 +2893,8 @@ struct drm_i915_gem_object *
 int i915_gem_open_object(struct drm_gem_object *gem, struct drm_file *file);
 void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file);
 void i915_gem_free_object(struct drm_gem_object *obj);
+char get_pin_flag(struct drm_i915_gem_object *obj);
+char get_tiling_flag(struct drm_i915_gem_object *obj);
 
 static inline void i915_gem_drain_freed_objects(struct drm_i915_private *i915)
 {
@@ -3358,6 +3363,12 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine,
 void i915_teardown_sysfs(struct drm_i915_private *dev_priv);
 int i915_get_drm_clients_info(struct drm_i915_error_state_buf *m,
 			      struct drm_device *dev);
+int i915_gem_get_obj_info(struct drm_i915_error_state_buf *m,
+			  struct drm_device *dev, struct pid *tgid);
+int i915_gem_create_sysfs_file_entry(struct drm_device *dev,
+				     struct bin_attribute *obj_attr);
+void i915_gem_remove_sysfs_file_entry(struct drm_device *dev,
+				      struct bin_attribute *obj_attr);
 
 /* intel_lpe_audio.c */
 int  intel_lpe_audio_init(struct drm_i915_private *dev_priv);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 6116e37..2cb6c98 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -91,6 +91,13 @@ struct drm_i915_obj_pid_info {
 	int open_handle_count;
 };
 
+struct drm_i915_gem_client_pid {
+	struct list_head head;
+	struct bin_attribute obj_attr;
+	pid_t tgid;
+	int open_obj_count;
+};
+
 struct get_obj_stats_buf {
 	struct pid_stat_entry *entry;
 	struct drm_i915_error_state_buf *m;
@@ -99,6 +106,21 @@ struct get_obj_stats_buf {
 #define err_printf(e, ...) i915_error_printf(e, __VA_ARGS__)
 #define err_puts(e, s) i915_error_puts(e, s)
 
+char get_pin_flag(struct drm_i915_gem_object *obj)
+{
+	return obj->pin_global ? 'p' : ' ';
+}
+
+char get_tiling_flag(struct drm_i915_gem_object *obj)
+{
+	switch (i915_gem_object_get_tiling(obj)) {
+	default:
+	case I915_TILING_NONE: return ' ';
+	case I915_TILING_X: return 'X';
+	case I915_TILING_Y: return 'Y';
+	}
+}
+
 /*
  * If this mmput call is the last one, it will tear down the mmaps of the
  * process and calls drm_gem_vm_close(), which leads deadlock on i915 mutex.
@@ -120,7 +142,8 @@ static void async_mmput(struct mm_struct *mm)
 static void i915_gem_flush_free_objects(struct drm_i915_private *i915);
 static int i915_gem_obj_insert_pid(struct drm_i915_gem_object *obj);
 static int i915_get_pid_cmdline(struct task_struct *task, char *buffer);
-static void i915_gem_obj_remove_all_pids(struct drm_i915_gem_object *obj);
+static void i915_gem_obj_remove_all_pids(struct drm_i915_private *i915,
+					 struct drm_i915_gem_object *obj);
 static void i915_gem_obj_remove_pid(struct drm_i915_gem_object *obj);
 
 static bool cpu_write_needs_clflush(struct drm_i915_gem_object *obj)
@@ -3646,11 +3669,85 @@ static void __sleep_rcu(struct rcu_head *rcu)
 	}
 }
 
+int i915_gem_client_add(struct drm_i915_private *i915)
+{
+	struct drm_i915_gem_client_pid *client_pid;
+	pid_t current_tgid = task_tgid_nr(current);
+	int ret, found = 0;
+
+	mutex_lock(&i915->drm.struct_mutex);
+	list_for_each_entry(client_pid, &i915->mm.gem_client_pids, head) {
+		if (client_pid->tgid == current_tgid) {
+			found = 1;
+			client_pid->open_obj_count++;
+		}
+	}
+	mutex_unlock(&i915->drm.struct_mutex);
+
+	if (!found) {
+		client_pid = kzalloc(sizeof(*client_pid), GFP_KERNEL);
+		if (client_pid == NULL) {
+			DRM_ERROR("alloc failed\n");
+			return -ENOMEM;
+		}
+		client_pid->tgid = current_tgid;
+		client_pid->open_obj_count = 1;
+		/* Create memtracker sysfs entry for this pid */
+		ret = i915_gem_create_sysfs_file_entry(&i915->drm,
+						 &client_pid->obj_attr);
+		if (ret) {
+			kfree(client_pid);
+			DRM_ERROR("Sysfs entry creation failed for pid %d",
+				  current_tgid);
+			return ret;
+		}
+
+		mutex_lock(&i915->drm.struct_mutex);
+		list_add_tail(&client_pid->head, &i915->mm.gem_client_pids);
+		mutex_unlock(&i915->drm.struct_mutex);
+
+	}
+	return 0;
+}
+
+void i915_gem_client_remove(struct drm_i915_private *i915,
+			    pid_t tgid, bool locked)
+{
+	struct drm_i915_gem_client_pid *client_pid, *next_pid, *tbr_pid = NULL;
+
+	if (!locked)
+		mutex_lock(&i915->drm.struct_mutex);
+
+	list_for_each_entry_safe(client_pid, next_pid,
+				 &i915->mm.gem_client_pids, head) {
+		if ((client_pid->tgid == tgid) &&
+		    (--client_pid->open_obj_count == 0)) {
+			list_del(&client_pid->head);
+			tbr_pid = client_pid;
+		}
+	}
+
+	if (!locked)
+		mutex_unlock(&i915->drm.struct_mutex);
+
+	if (tbr_pid) {
+		i915_gem_remove_sysfs_file_entry(&i915->drm,
+						 &tbr_pid->obj_attr);
+		kfree(tbr_pid);
+	}
+}
+
 int i915_gem_open_object(struct drm_gem_object *gem, struct drm_file *file)
 {
 	struct drm_i915_gem_object *obj = to_intel_bo(gem);
+	struct drm_i915_private *i915 = to_i915(gem->dev);
+	int ret = 0;
 
-	return i915_gem_obj_insert_pid(obj);
+	ret = i915_gem_obj_insert_pid(obj);
+	if (ret)
+		return ret;
+
+	return i915_gem_client_add(i915);
 }
 
 void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file)
@@ -3659,6 +3756,7 @@ void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file)
 	struct drm_i915_gem_object *obj = to_intel_bo(gem);
 	struct drm_i915_file_private *fpriv = file->driver_priv;
 	struct i915_lut_handle *lut, *ln;
+	pid_t current_tgid = task_tgid_nr(current);
 
 	mutex_lock(&i915->drm.struct_mutex);
 
@@ -3686,9 +3784,10 @@ void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file)
 		kmem_cache_free(i915->luts, lut);
 		__i915_gem_object_release_unless_active(obj);
 	}
-
+	i915_gem_client_remove(i915, current_tgid, true);
 	mutex_unlock(&i915->drm.struct_mutex);
 
+
 	i915_gem_obj_remove_pid(obj);
 
 }
@@ -4869,7 +4968,7 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
 		GEM_BUG_ON(atomic_read(&obj->frontbuffer_bits));
 		GEM_BUG_ON(!list_empty(&obj->lut_list));
 
-		i915_gem_obj_remove_all_pids(obj);
+		i915_gem_obj_remove_all_pids(i915, obj);
 
 		if (obj->ops->release)
 			obj->ops->release(obj);
@@ -5616,6 +5715,7 @@ static void i915_gem_init__mm(struct drm_i915_private *i915)
 	INIT_LIST_HEAD(&i915->mm.bound_list);
 	INIT_LIST_HEAD(&i915->mm.fence_list);
 	INIT_LIST_HEAD(&i915->mm.userfault_list);
+	INIT_LIST_HEAD(&i915->mm.gem_client_pids);
 
 	INIT_WORK(&i915->mm.free_work, __i915_gem_free_work);
 }
@@ -5788,7 +5888,6 @@ int i915_gem_open(struct drm_i915_private *i915, struct drm_file *file)
 	int ret;
 
 	DRM_DEBUG("\n");
-
 	file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
 	if (!file_priv)
 		return -ENOMEM;
@@ -6235,14 +6334,21 @@ static void i915_gem_obj_remove_pid(struct drm_i915_gem_object *obj)
 				current_tgid, obj);
 }
 
-static void i915_gem_obj_remove_all_pids(struct drm_i915_gem_object *obj)
+static void i915_gem_obj_remove_all_pids(struct drm_i915_private *i915,
+					 struct drm_i915_gem_object *obj)
 {
 	struct drm_i915_obj_pid_info *pid_entry, *pid_next;
 
+	mutex_lock(&i915->drm.struct_mutex);
 	list_for_each_entry_safe(pid_entry, pid_next, &obj->pid_info, head) {
+		/* Remove object from that pid's client list */
+		while (pid_entry->open_handle_count--)
+			i915_gem_client_remove(i915, pid_entry->tgid, true);
+
 		list_del(&pid_entry->head);
 		kfree(pid_entry);
 	}
+	mutex_unlock(&i915->drm.struct_mutex);
 }
 
 static int i915_obj_find_insert_in_hash(struct drm_i915_gem_object *obj,
@@ -6310,6 +6416,102 @@ static int i915_obj_shared_count(struct drm_i915_gem_object *obj,
 }
 
 static int
+i915_describe_obj(struct get_obj_stats_buf *obj_stat_buf,
+		  struct drm_i915_gem_object *obj)
+{
+	struct i915_vma *vma;
+	struct drm_i915_obj_pid_info *pid_info_entry;
+	struct drm_i915_error_state_buf *m = obj_stat_buf->m;
+	struct pid_stat_entry *pid_entry = obj_stat_buf->entry;
+	struct per_file_obj_mem_info *stats = &pid_entry->stats;
+	int obj_shared_count = 0;
+	bool discard = false;
+	u64 nr_bytes = 0;
+
+	obj_shared_count = i915_obj_shared_count(obj, pid_entry, &discard);
+	if (obj_shared_count < 0)
+		return obj_shared_count;
+
+	if (!obj->stolen)
+		nr_bytes = i915_obj_get_shmem_pages_alloced(obj)*PAGE_SIZE;
+
+	if (!discard && !obj->stolen &&
+			(obj->mm.madv != __I915_MADV_PURGED) &&
+			(nr_bytes != 0)) {
+		if (obj_shared_count > 1)
+			stats->phys_space_shared_proportion +=
+				div64_u64(nr_bytes, obj_shared_count);
+		else
+			stats->phys_space_allocated_priv += nr_bytes;
+	}
+
+	i915_error_printf(m,
+		"%p: %7zdK  %10zdK     %c      %c     %s      %s      %s     %s     %s",
+		   &obj->base,
+		   obj->base.size / 1024,
+		   (size_t)div64_u64(nr_bytes, 1024),
+		   get_pin_flag(obj),
+		   get_tiling_flag(obj),
+		   obj->mm.dirty ? "Y" : "N",
+		   (obj_shared_count > 1) ? "Y" : "N",
+		   (obj->userptr.mm != 0) ? "Y" : "N",
+		   obj->stolen ? "Y" : "N",
+		   (obj->pin_global || obj->userptr.mm) ? "Y" : "N");
+
+	if (obj->mm.madv == __I915_MADV_PURGED)
+		err_puts(m, "   purged    ");
+	else if (obj->mm.madv == I915_MADV_DONTNEED)
+		err_puts(m, "   purgeable ");
+	else if (obj->has_backing_pages != 0)
+		err_puts(m, "   allocated ");
+	else
+		err_puts(m, "             ");
+
+	list_for_each_entry(vma, &obj->vma_list, obj_link) {
+		if (!i915_is_ggtt(vma->vm))
+			err_puts(m, " PP    ");
+		else
+			err_puts(m, " G     ");
+		i915_error_printf(m, "  %08llx ", vma->node.start);
+	}
+	if (list_empty(&obj->vma_list))
+		err_puts(m, "                  ");
+
+	list_for_each_entry(pid_info_entry, &obj->pid_info, head)
+		i915_error_printf(m, " (%d: %d)",
+			   pid_info_entry->tgid,
+			   pid_info_entry->open_handle_count);
+
+	err_puts(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 get_obj_stats_buf *obj_stat_buf = data;
+	struct drm_i915_obj_pid_info *pid_info;
+	pid_t tgid = pid_nr(obj_stat_buf->entry->tgid);
+	int ret;
+
+	list_for_each_entry(pid_info, &obj->pid_info, head) {
+
+		if (tgid != pid_info->tgid)
+			continue;
+
+		ret = i915_describe_obj(obj_stat_buf, obj);
+		if (ret)
+			return ret;
+	}
+	return ret;
+}
+
+static int
 i915_drm_gem_obj_per_process_summary(struct drm_i915_gem_object *obj,
 				     struct pid_stat_entry *pid_entry)
 {
@@ -6560,6 +6762,78 @@ static int i915_gem_object_pid_order(int id, void *ptr, void *data)
 	return 0;
 }
 
+#define NUM_SPACES 100
+#define INITIAL_SPACES_STR(x) #x
+#define SPACES_STR(x) INITIAL_SPACES_STR(x)
+
+static int
+__i915_gem_get_obj_info(struct drm_i915_error_state_buf *m,
+			struct drm_device *dev, struct pid *tgid)
+{
+	struct drm_file *file;
+	int bytes_copy, ret = 0;
+	struct pid_stat_entry pid_entry;
+	struct name_entry *entry, *next;
+
+	pid_entry.stats.phys_space_shared_proportion = 0;
+	pid_entry.stats.phys_space_allocated_priv = 0;
+	pid_entry.tgid = tgid;
+	pid_entry.pid_num = pid_nr(tgid);
+	ret = drm_ht_create(&pid_entry.namelist, DRM_DEBUG_MAGIC_HASH_ORDER);
+	if (ret)
+		return ret;
+
+	INIT_LIST_HEAD(&pid_entry.namefree);
+
+	/*
+	 * Fill up initial few bytes with spaces, to insert summary data later
+	 * on
+	 */
+	i915_error_printf(m, "%"SPACES_STR(NUM_SPACES)"s\n", " ");
+
+	err_puts(m,
+		"\n Obj Identifier   Obj-Size Resident-Size Pin Tiling Dirty Shared Vmap Stolen Mappable  AllocState Global/PP  GttOffset (PID: handle count)\n");
+
+	list_for_each_entry(file, &dev->filelist, lhead) {
+		struct get_obj_stats_buf obj_stat_buf;
+
+		obj_stat_buf.entry = &pid_entry;
+		obj_stat_buf.m = m;
+
+		spin_lock(&file->table_lock);
+		ret = idr_for_each(&file->object_idr,
+				&i915_drm_gem_obj_info, &obj_stat_buf);
+		spin_unlock(&file->table_lock);
+		if (ret)
+			break;
+	}
+
+	/* Reset the bytes counter to buffer beginning */
+	bytes_copy = m->bytes;
+	m->bytes = 0;
+
+	i915_error_printf(m, "\n  PID    GfxMem\n");
+	i915_error_printf(m, "%5d %8zdK ", pid_nr(tgid),
+			(pid_entry.stats.phys_space_shared_proportion +
+			 pid_entry.stats.phys_space_allocated_priv)/1024);
+
+	/* Reinstate the previous saved value of bytes counter */
+	m->bytes = bytes_copy;
+	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);
+
+	if (ret)
+		return ret;
+	if (m->bytes == 0 && m->err)
+		return m->err;
+	return 0;
+}
+
 int i915_get_drm_clients_info(struct drm_i915_error_state_buf *m,
 			      struct drm_device *dev)
 {
@@ -6584,3 +6858,19 @@ int i915_get_drm_clients_info(struct drm_i915_error_state_buf *m,
 
 	return ret;
 }
+
+int i915_gem_get_obj_info(struct drm_i915_error_state_buf *m,
+			struct drm_device *dev, struct pid *tgid)
+{
+	int ret = 0;
+
+	ret = i915_mutex_lock_interruptible(dev);
+	if (ret)
+		return ret;
+
+	ret = __i915_gem_get_obj_info(m, dev, tgid);
+
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 3e8fc79..7638459 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -859,6 +859,20 @@ int i915_error_state_buf_init(struct drm_i915_error_state_buf *ebuf,
 	return 0;
 }
 
+int i915_obj_state_buf_init(struct drm_i915_error_state_buf *ebuf,
+				size_t count)
+{
+	memset(ebuf, 0, sizeof(*ebuf));
+
+	ebuf->buf = kmalloc(count, GFP_KERNEL);
+
+	if (ebuf->buf == NULL)
+		return -ENOMEM;
+
+	ebuf->size = count;
+	return 0;
+}
+
 static void i915_error_object_free(struct drm_i915_error_object *obj)
 {
 	int page;
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.h b/drivers/gpu/drm/i915/i915_gpu_error.h
index dac2a35..0db5829 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.h
+++ b/drivers/gpu/drm/i915/i915_gpu_error.h
@@ -307,6 +307,8 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *estr,
 int i915_error_state_buf_init(struct drm_i915_error_state_buf *eb,
 			      struct drm_i915_private *i915,
 			      size_t count, loff_t pos);
+int i915_obj_state_buf_init(struct drm_i915_error_state_buf *eb,
+			    size_t count);
 
 void i915_error_puts(struct drm_i915_error_state_buf *e,
 		     const char *str);
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c
index d35c789..7f7d74a 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -582,6 +582,190 @@ static ssize_t i915_gem_clients_state_read(struct file *filp,
 
 	return ret ?: ret_count;
 }
+#define GEM_OBJ_STAT_BUF_SIZE (4*1024) /* 4KB */
+#define GEM_OBJ_STAT_BUF_SIZE_MAX (1024*1024) /* 1MB */
+
+struct i915_gem_file_attr_priv {
+	char tgid_str[16];
+	struct pid *tgid;
+	struct drm_i915_error_state_buf buf;
+};
+
+static ssize_t i915_gem_read_objects(struct file *filp,
+				struct kobject *memtrack_kobj,
+				struct bin_attribute *attr,
+				char *buf, loff_t off, size_t count)
+{
+	struct kobject *kobj = memtrack_kobj->parent;
+	struct device *kdev = container_of(kobj, struct device, kobj);
+	struct drm_minor *minor = dev_get_drvdata(kdev);
+	struct drm_device *dev = minor->dev;
+	struct i915_gem_file_attr_priv *attr_priv;
+	struct pid *tgid;
+	ssize_t ret_count = 0;
+	long bytes_available;
+	int ret = 0, buf_size = GEM_OBJ_STAT_BUF_SIZE;
+
+	/*
+	 * FIXME:There may arise a scenario where syfs file entry is being
+	 * removed, and may race against sysfs read. Sysfs file remove function
+	 * would have taken the drm_global_mutex and would wait for read to
+	 * finish, which is again waiting to acquire drm_global_mutex, leading
+	 * to deadlock. To avoid this, use mutex_trylock here with a timeout.
+	 *
+	 *unsigned long timeout = msecs_to_jiffies(500) + 1;
+	 *
+	 *while (!mutex_trylock(&drm_global_mutex) && --timeout)
+	 *	schedule_timeout_killable(1);
+	 *if (timeout == 0) {
+	 *	DRM_DEBUG_DRIVER("Unable to acquire drm global mutex.\n");
+	 *	return -EBUSY;
+	 *}
+	 */
+
+	if (!attr || !attr->private) {
+		ret = -EINVAL;
+		DRM_ERROR("attr | attr->private pointer is NULL\n");
+		goto out;
+	}
+
+	attr_priv = attr->private;
+	tgid = attr_priv->tgid;
+
+	if (off && !attr_priv->buf.buf) {
+		ret = -EINVAL;
+		DRM_ERROR(
+			"Buf not allocated during read with non-zero offset\n");
+		goto out;
+	}
+
+	if (off == 0) {
+retry:
+		if (!attr_priv->buf.buf) {
+			ret = i915_obj_state_buf_init(&attr_priv->buf,
+				buf_size);
+			if (ret) {
+				DRM_ERROR(
+					"obj state buf init failed. buf_size=%d\n",
+					buf_size);
+				goto out;
+			}
+		} else {
+			/* Reset the buf parameters before filling data */
+			attr_priv->buf.pos = 0;
+			attr_priv->buf.bytes = 0;
+		}
+
+		/* Read the gfx device stats */
+		ret = i915_gem_get_obj_info(&attr_priv->buf, dev, tgid);
+		if (ret)
+			goto out;
+
+		ret = i915_error_ok(&attr_priv->buf);
+		if (ret) {
+			ret = 0;
+			goto copy_data;
+		}
+		if (buf_size >= GEM_OBJ_STAT_BUF_SIZE_MAX) {
+			DRM_DEBUG_DRIVER("obj stat buf size limit reached\n");
+			ret = -ENOMEM;
+			goto out;
+		} else {
+			/* Try to reallocate buf of larger size */
+			i915_error_state_buf_release(&attr_priv->buf);
+			buf_size *= 2;
+
+			ret = i915_obj_state_buf_init(&attr_priv->buf,
+						buf_size);
+			if (ret) {
+				DRM_ERROR(
+					"obj stat buf init failed. buf_size=%d\n",
+					buf_size);
+				goto out;
+			}
+			goto retry;
+		}
+	}
+copy_data:
+
+	bytes_available = (long)attr_priv->buf.bytes - (long)off;
+
+	if (bytes_available > 0) {
+		ret_count = count < bytes_available ? count : bytes_available;
+		memcpy(buf, attr_priv->buf.buf + off, ret_count);
+	} else
+		ret_count = 0;
+
+out:
+	/* mutex_unlock(&drm_global_mutex); */
+
+	return ret ?: ret_count;
+}
+
+int i915_gem_create_sysfs_file_entry(struct drm_device *dev,
+				     struct bin_attribute *obj_attr)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct i915_gem_file_attr_priv *attr_priv;
+	struct pid *tgid = get_pid(find_vpid(task_tgid_nr(current)));
+	int ret;
+
+	if (!i915_modparams.memtrack_debug)
+		return 0;
+
+	attr_priv = kzalloc(sizeof(*attr_priv), GFP_KERNEL);
+	if (!attr_priv) {
+		DRM_ERROR("Alloc failed. Out of memory\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	snprintf(attr_priv->tgid_str, 16, "%d", task_tgid_nr(current));
+	sysfs_bin_attr_init(obj_attr);
+	obj_attr->attr.name = attr_priv->tgid_str;
+	obj_attr->attr.mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+	obj_attr->size = 0;
+	obj_attr->read = i915_gem_read_objects;
+
+	attr_priv->tgid = tgid;
+	obj_attr->private = attr_priv;
+
+	ret = sysfs_create_bin_file(&dev_priv->memtrack_kobj,
+				    obj_attr);
+	if (ret) {
+		DRM_ERROR(
+			"sysfs tgid file setup failed. tgid=%d, ret:%d\n",
+			pid_nr(tgid), ret);
+
+		goto out_attr_priv;
+	}
+	return 0;
+
+out_attr_priv:
+	kfree(attr_priv);
+out:
+	return ret;
+}
+
+void i915_gem_remove_sysfs_file_entry(struct drm_device *dev,
+				      struct bin_attribute *obj_attr)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct i915_gem_file_attr_priv *attr_priv;
+
+	if (!i915_modparams.memtrack_debug)
+		return;
+
+	if (WARN_ON(obj_attr == NULL))
+		return;
+
+	attr_priv = obj_attr->private;
+
+	sysfs_remove_bin_file(&dev_priv->memtrack_kobj, obj_attr);
+
+	i915_error_state_buf_release(&attr_priv->buf);
+	kfree(obj_attr->private);
+}
 
 static const struct bin_attribute error_state_attr = {
 	.attr.name = "error",
