@@ -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' : ' ';
@@ -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);
@@ -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;
+}
@@ -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;
@@ -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);
@@ -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",