@@ -2847,6 +2847,7 @@ static int intel_runtime_resume(struct device *kdev)
.lastclose = i915_driver_lastclose,
.postclose = i915_driver_postclose,
+ .gem_open_object = i915_gem_open_object,
.gem_close_object = i915_gem_close_object,
.gem_free_object_unlocked = i915_gem_free_object,
.gem_vm_ops = &i915_gem_vm_ops,
@@ -1660,6 +1660,7 @@ struct drm_i915_private {
bool preserve_bios_swizzle;
+ struct kobject memtrack_kobj;
/* overlay */
struct intel_overlay *overlay;
@@ -2884,6 +2885,7 @@ struct drm_i915_gem_object *
struct drm_i915_gem_object *
i915_gem_object_create_from_data(struct drm_i915_private *dev_priv,
const void *data, size_t size);
+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);
@@ -3352,6 +3354,8 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine,
/* i915_sysfs.c */
void i915_setup_sysfs(struct drm_i915_private *dev_priv);
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);
/* intel_lpe_audio.c */
int intel_lpe_audio_init(struct drm_i915_private *dev_priv);
@@ -45,8 +45,83 @@
#include <linux/swap.h>
#include <linux/pci.h>
#include <linux/dma-buf.h>
+#include <linux/sched/mm.h>
+
+/**< Size of key hash table. Must be power of 2. */
+#define DRM_DEBUG_MAGIC_HASH_ORDER 4
+
+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 *tgid;
+ int pid_num;
+};
+
+struct drm_i915_obj_pid_info {
+ struct list_head head;
+ pid_t tgid;
+ int open_handle_count;
+};
+
+struct get_obj_stats_buf {
+ struct pid_stat_entry *entry;
+ struct drm_i915_error_state_buf *m;
+};
+
+#define err_printf(e, ...) i915_error_printf(e, __VA_ARGS__)
+#define err_puts(e, s) i915_error_puts(e, s)
+
+/*
+ * 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.
+ * Instead, asynchronously schedule mmput function here, to avoid recursive
+ * calls to acquire i915_mutex.
+ */
+static void async_mmput_func(void *data, async_cookie_t cookie)
+{
+ struct mm_struct *mm = data;
+
+ mmput(mm);
+}
+
+static void async_mmput(struct mm_struct *mm)
+{
+ async_schedule(async_mmput_func, 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_pid(struct drm_i915_gem_object *obj);
static bool cpu_write_needs_clflush(struct drm_i915_gem_object *obj)
{
@@ -3553,6 +3628,13 @@ static void __sleep_rcu(struct rcu_head *rcu)
}
}
+int i915_gem_open_object(struct drm_gem_object *gem, struct drm_file *file)
+{
+ struct drm_i915_gem_object *obj = to_intel_bo(gem);
+
+ return i915_gem_obj_insert_pid(obj);
+}
+
void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file)
{
struct drm_i915_private *i915 = to_i915(gem->dev);
@@ -3588,6 +3670,9 @@ void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file)
}
mutex_unlock(&i915->drm.struct_mutex);
+
+ i915_gem_obj_remove_pid(obj);
+
}
static unsigned long to_wait_timeout(s64 timeout_ns)
@@ -4582,6 +4667,7 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
init_request_active(&obj->frontbuffer_write, frontbuffer_retire);
obj->mm.madv = I915_MADV_WILLNEED;
+ INIT_LIST_HEAD(&obj->pid_info);
INIT_RADIX_TREE(&obj->mm.get_page.radix, GFP_KERNEL | __GFP_NOWARN);
mutex_init(&obj->mm.get_page.lock);
@@ -4760,6 +4846,8 @@ 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);
+
if (obj->ops->release)
obj->ops->release(obj);
@@ -6004,3 +6092,463 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
#include "selftests/i915_gem_object.c"
#include "selftests/i915_gem_coherency.c"
#endif
+
+static int i915_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) {
+ async_mmput(mm);
+ return res;
+ }
+
+ if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE)
+ buffer[res-1] = '\0';
+out_mm:
+ async_mmput(mm);
+out:
+ return 0;
+}
+
+static unsigned long
+i915_obj_get_shmem_pages_alloced(struct drm_i915_gem_object *obj)
+{
+ unsigned long 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;
+}
+
+static int i915_gem_obj_insert_pid(struct drm_i915_gem_object *obj)
+{
+ int found = 0;
+ struct drm_i915_obj_pid_info *entry;
+ pid_t current_tgid = task_tgid_nr(current);
+
+ if (!i915_modparams.memtrack_debug)
+ return 0;
+
+ mutex_lock(&obj->base.dev->struct_mutex);
+
+ list_for_each_entry(entry, &obj->pid_info, head) {
+ if (entry->tgid == current_tgid) {
+ entry->open_handle_count++;
+ found = 1;
+ break;
+ }
+ }
+ if (found == 0) {
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (entry == NULL) {
+ DRM_ERROR("alloc failed\n");
+ mutex_unlock(&obj->base.dev->struct_mutex);
+ return -ENOMEM;
+ }
+ entry->tgid = current_tgid;
+ entry->open_handle_count = 1;
+ list_add_tail(&entry->head, &obj->pid_info);
+ }
+
+ mutex_unlock(&obj->base.dev->struct_mutex);
+ return 0;
+}
+
+static void i915_gem_obj_remove_pid(struct drm_i915_gem_object *obj)
+{
+ pid_t current_tgid = task_tgid_nr(current);
+ struct drm_i915_obj_pid_info *pid_entry, *pid_next;
+ int found = 0;
+
+ if (!i915_modparams.memtrack_debug)
+ return;
+
+ mutex_lock(&obj->base.dev->struct_mutex);
+
+ list_for_each_entry_safe(pid_entry, pid_next, &obj->pid_info, head) {
+ if (pid_entry->tgid == current_tgid) {
+ pid_entry->open_handle_count--;
+ found = 1;
+ if (pid_entry->open_handle_count == 0) {
+ list_del(&pid_entry->head);
+ kfree(pid_entry);
+ }
+ break;
+ }
+ }
+ mutex_unlock(&obj->base.dev->struct_mutex);
+
+ if (found == 0)
+ DRM_DEBUG("Couldn't find matching tgid %d for obj %p\n",
+ current_tgid, obj);
+}
+
+static void i915_gem_obj_remove_all_pids(struct drm_i915_gem_object *obj)
+{
+ struct drm_i915_obj_pid_info *pid_entry, *pid_next;
+
+ list_for_each_entry_safe(pid_entry, pid_next, &obj->pid_info, head) {
+ list_del(&pid_entry->head);
+ kfree(pid_entry);
+ }
+}
+
+static int i915_obj_find_insert_in_hash(struct drm_i915_gem_object *obj,
+ struct pid_stat_entry *pid_entry,
+ bool *found)
+{
+ struct drm_hash_item *hash_item;
+ int ret;
+
+ ret = drm_ht_find_item(&pid_entry->namelist,
+ (unsigned long)&obj->base, &hash_item);
+ /* Not found, insert in hash */
+ if (ret) {
+ struct name_entry *entry =
+ kzalloc(sizeof(*entry), GFP_NOWAIT);
+ if (entry == NULL) {
+ DRM_ERROR("alloc failed\n");
+ return -ENOMEM;
+ }
+ entry->hash_item.key = (unsigned long)&obj->base;
+ drm_ht_insert_item(&pid_entry->namelist,
+ &entry->hash_item);
+ list_add_tail(&entry->head, &pid_entry->namefree);
+ *found = false;
+ } else
+ *found = true;
+
+ return 0;
+}
+
+static int i915_obj_shared_count(struct drm_i915_gem_object *obj,
+ struct pid_stat_entry *pid_entry,
+ bool *discard)
+{
+ struct drm_i915_obj_pid_info *pid_info_entry;
+ int ret, obj_shared_count = 0;
+
+ /*
+ * The object can be shared among different processes by either flink
+ * or dma-buf mechanism, leading to shared count more than 1. For the
+ * objects not shared , return the shared count as 1.
+ * In case of shared dma-buf objects, there's a possibility that these
+ * may be external to i915. Detect this condition through
+ * 'import_attach' field.
+ */
+ if (!obj->base.name && !obj->base.dma_buf)
+ return 1;
+ else if (obj->base.import_attach) {
+ /* not our GEM obj */
+ *discard = true;
+ return 0;
+ }
+
+ ret = i915_obj_find_insert_in_hash(obj, pid_entry, discard);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(pid_info_entry, &obj->pid_info, head)
+ obj_shared_count++;
+
+ if (WARN_ON(obj_shared_count == 0))
+ return -EINVAL;
+
+ return obj_shared_count;
+}
+
+static int
+i915_drm_gem_obj_per_process_summary(struct drm_i915_gem_object *obj,
+ struct pid_stat_entry *pid_entry)
+{
+ struct per_file_obj_mem_info *stats = &pid_entry->stats;
+ int obj_shared_count = 0;
+ bool discard = false;
+
+ stats->num_obj++;
+
+ obj_shared_count = i915_obj_shared_count(obj, pid_entry, &discard);
+ if (obj_shared_count < 0)
+ return obj_shared_count;
+
+ if (discard)
+ return 0;
+
+ if (obj_shared_count > 1)
+ stats->num_obj_shared++;
+ else
+ stats->num_obj_private++;
+
+ if (obj->bind_count) {
+ 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->mm.madv == __I915_MADV_PURGED) {
+ stats->num_obj_purged++;
+ } else {
+ u64 nr_bytes =
+ i915_obj_get_shmem_pages_alloced(obj)*PAGE_SIZE;
+
+ if (obj->mm.madv == I915_MADV_DONTNEED) {
+ stats->num_obj_purgeable++;
+ if (nr_bytes != 0)
+ stats->phys_space_purgeable += nr_bytes;
+ }
+
+ if (nr_bytes != 0) {
+ stats->num_obj_allocated++;
+ if (obj_shared_count > 1) {
+ stats->phys_space_allocated_shared += nr_bytes;
+ stats->phys_space_shared_proportion +=
+ div64_u64(nr_bytes, obj_shared_count);
+ } else
+ stats->phys_space_allocated_priv += nr_bytes;
+ }
+ }
+
+ if (!list_empty(&obj->userfault_link)) {
+ stats->num_obj_fault_mappable++;
+ stats->fault_mappable_size += obj->base.size;
+ }
+
+ return 0;
+}
+
+static int i915_gem_object_pid_order(int id, void *ptr, void *data)
+{
+ struct drm_i915_gem_object *obj = ptr;
+ struct list_head *per_pid_stats = data;
+ struct pid_stat_entry *pid_entry;
+ struct drm_i915_obj_pid_info *pid_info;
+ int ret = 0;
+
+ list_for_each_entry(pid_info, &obj->pid_info, head) {
+ int pid_num = pid_info->tgid;
+ int found = 0;
+
+ list_for_each_entry(pid_entry, per_pid_stats, head) {
+ if (pid_entry->pid_num == pid_num) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ char *process_name;
+ struct task_struct *task;
+ struct pid_stat_entry *new_entry =
+ kzalloc(sizeof(*new_entry), GFP_KERNEL);
+ if (new_entry == NULL) {
+ DRM_ERROR("alloc failed\n");
+ ret = -ENOMEM;
+ break;
+ }
+
+ new_entry->pid_num = pid_num;
+ new_entry->tgid = find_get_pid(pid_num);
+
+ ret = drm_ht_create(&new_entry->namelist,
+ DRM_DEBUG_MAGIC_HASH_ORDER);
+ if (ret) {
+ kfree(new_entry);
+ break;
+ }
+
+ list_add_tail(&new_entry->head, per_pid_stats);
+ INIT_LIST_HEAD(&new_entry->namefree);
+
+ process_name = kzalloc(PAGE_SIZE, GFP_ATOMIC);
+ if (!process_name) {
+ kfree(new_entry);
+ ret = -ENOMEM;
+ break;
+ }
+
+ task = get_pid_task(new_entry->tgid, PIDTYPE_PID);
+ ret = i915_get_pid_cmdline(task, process_name);
+ if (ret) {
+ kfree(new_entry);
+ kfree(process_name);
+ break;
+ }
+ new_entry->stats.process_name = process_name;
+ pid_entry = new_entry;
+ }
+ /* Add this entry into per process accounting now */
+ i915_drm_gem_obj_per_process_summary(obj, pid_entry);
+
+ }
+
+ return ret;
+}
+
+static int
+__i915_get_drm_clients_info(struct drm_i915_error_state_buf *m,
+ struct drm_device *dev)
+{
+ struct drm_file *file;
+
+ 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;
+ size_t total_shared_prop_space = 0, total_priv_space = 0;
+
+ INIT_LIST_HEAD(&per_pid_stats);
+ INIT_LIST_HEAD(&sorted_pid_stats);
+
+ err_puts(m,
+ "\n\n pid Total Shared Priv Purgeable Alloced SharedPHYsize SharedPHYprop PrivPHYsize PurgeablePHYsize process\n");
+
+ list_for_each_entry(file, &dev->filelist, lhead) {
+ spin_lock(&file->table_lock);
+ ret = idr_for_each(&file->object_idr,
+ &i915_gem_object_pid_order,
+ &per_pid_stats);
+ spin_unlock(&file->table_lock);
+ 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->tgid,
+ 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_puts(m, "*\n");
+ else
+ err_puts(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->stats.process_name);
+ kfree(pid_entry);
+ if (task)
+ put_task_struct(task);
+ }
+
+ err_puts(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);
+
+ 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)
+{
+ int ret = 0;
+
+ /*
+ * Protect the access to global drm resources such as filelist. Protect
+ * against their removal under our noses, while in use.
+ * XXX: drm_global_mutex is undefined currently
+ */
+ /* mutex_lock(&drm_global_mutex); */
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret) {
+ /* mutex_unlock(&drm_global_mutex); */
+ return ret;
+ }
+
+ ret = __i915_get_drm_clients_info(m, dev);
+
+ mutex_unlock(&dev->struct_mutex);
+ /* mutex_unlock(&drm_global_mutex); */
+
+ return ret;
+}
@@ -279,6 +279,7 @@ struct drm_i915_gem_object {
void *gvt_info;
};
+ struct list_head pid_info;
/** for phys allocated objects */
struct drm_dma_handle *phys_handle;
@@ -90,6 +90,11 @@ static bool __i915_error_ok(struct drm_i915_error_state_buf *e)
return true;
}
+bool i915_error_ok(struct drm_i915_error_state_buf *e)
+{
+ return __i915_error_ok(e);
+}
+
static bool __i915_error_seek(struct drm_i915_error_state_buf *e,
unsigned len)
{
@@ -161,8 +166,8 @@ 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,
- const char *str)
+void i915_error_puts(struct drm_i915_error_state_buf *e,
+ const char *str)
{
unsigned len;
@@ -308,6 +308,10 @@ int i915_error_state_buf_init(struct drm_i915_error_state_buf *eb,
struct drm_i915_private *i915,
size_t count, loff_t pos);
+void i915_error_puts(struct drm_i915_error_state_buf *e,
+ const char *str);
+bool i915_error_ok(struct drm_i915_error_state_buf *e);
+
static inline void
i915_error_state_buf_release(struct drm_i915_error_state_buf *eb)
{
@@ -178,6 +178,8 @@ struct i915_params i915_modparams __read_mostly = {
i915_param_named(enable_gvt, bool, 0400,
"Enable support for Intel GVT-g graphics virtualization host support(default:false)");
+i915_param_named(memtrack_debug, bool, 0600, "use memtrack capability (default:true)");
+
static __always_inline void _print_param(struct drm_printer *p,
const char *name,
const char *type,
@@ -69,7 +69,8 @@
param(bool, nuclear_pageflip, false) \
param(bool, enable_dp_mst, true) \
param(bool, enable_dpcd_backlight, false) \
- param(bool, enable_gvt, false)
+ param(bool, enable_gvt, false) \
+ param(bool, memtrack_debug, true)
#define MEMBER(T, member, ...) T member;
struct i915_params {
@@ -552,6 +552,36 @@ 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 *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 drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_error_state_buf error_str;
+ ssize_t ret_count = 0;
+ int ret;
+
+ ret = i915_error_state_buf_init(&error_str, dev_priv, 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 const struct bin_attribute error_state_attr = {
.attr.name = "error",
@@ -560,11 +590,51 @@ static ssize_t error_state_write(struct file *file, struct kobject *kobj,
.read = error_state_read,
.write = error_state_write,
};
+static struct bin_attribute i915_gem_client_state_attr = {
+ .attr.name = "i915_gem_meminfo",
+ .attr.mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH,
+ .size = 0,
+ .read = i915_gem_clients_state_read,
+};
+
+static struct attribute *memtrack_kobj_attrs[] = {NULL};
+
+static struct kobj_type memtrack_kobj_type = {
+ .release = NULL,
+ .sysfs_ops = NULL,
+ .default_attrs = memtrack_kobj_attrs,
+};
static void i915_setup_error_capture(struct device *kdev)
{
+ int ret;
+
if (sysfs_create_bin_file(&kdev->kobj, &error_state_attr))
DRM_ERROR("error_state sysfs setup failed\n");
+
+ if (i915_modparams.memtrack_debug) {
+ struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
+
+ /*
+ * Create the gfx_memtrack directory for memtrack sysfs files
+ */
+ ret = kobject_init_and_add(
+ &dev_priv->memtrack_kobj, &memtrack_kobj_type,
+ &kdev->kobj, "gfx_memtrack");
+ if (unlikely(ret != 0)) {
+ DRM_ERROR(
+ "i915 sysfs setup memtrack directory failed\n"
+ );
+ kobject_put(&dev_priv->memtrack_kobj);
+ } else {
+ ret = sysfs_create_bin_file(&dev_priv->memtrack_kobj,
+ &i915_gem_client_state_attr);
+ if (ret)
+ DRM_ERROR(
+ "i915_gem_client_state sysfs setup failed\n"
+ );
+ }
+ }
}
static void i915_teardown_error_capture(struct device *kdev)
@@ -641,4 +711,12 @@ void i915_teardown_sysfs(struct drm_i915_private *dev_priv)
sysfs_unmerge_group(&kdev->kobj, &rc6_attr_group);
sysfs_unmerge_group(&kdev->kobj, &rc6p_attr_group);
#endif
+ if (i915_modparams.memtrack_debug) {
+ struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
+
+ sysfs_remove_bin_file(&dev_priv->memtrack_kobj,
+ &i915_gem_client_state_attr);
+ kobject_del(&dev_priv->memtrack_kobj);
+ kobject_put(&dev_priv->memtrack_kobj);
+ }
}