[PATCH 1/5] drm/i915: Sysfs interface to get GFX shmem usage stats per process

Nidhi Gupta nidhi1.gupta at intel.com
Tue Feb 6 12:21:21 UTC 2018


From: Sourab Gupta <sourab.gupta at intel.com>

There's a need for an interface which provide information about GFX
memory usage per process, at any instantaneous point of time.
Such information is useful to analyze GFX memory usage of the processes,
on the lines of other system memory usage tools (e.g. procrank).
This is especially useful for the GFX intensive processes, since the
tools such as procrank may not be able to provide a precise view of
GFX memory consumption, with its specific intricacies (such as
differentiation between private, shared and purgeable buffers).

The patch provides a sysfs interface to list GFX memory usage for
each process which has drm fd open. The interface also provides
GFX buffers/memory usage information categorized into various buckets
according to buffer type such as purgeable, private, shared.

Signed-off-by: Sourab Gupta <sourab.gupta at intel.com>
Signed-off-by: Akash Goel <akash.goel at intel.com>
Signed-off-by: Nidhi Gupta <nidhi1.gupta at intel.com>
---
 drivers/gpu/drm/i915/i915_drv.c        |   2 +-
 drivers/gpu/drm/i915/i915_drv.h        |  14 +
 drivers/gpu/drm/i915/i915_gem.c        | 585 ++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/i915/i915_gem_object.h |   1 +
 drivers/gpu/drm/i915/i915_gpu_error.c  |   7 +-
 drivers/gpu/drm/i915/i915_params.c     |   3 +
 drivers/gpu/drm/i915/i915_params.h     |   1 +
 drivers/gpu/drm/i915/i915_sysfs.c      |  80 +++++
 8 files changed, 690 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index e9f1daf..ce47707 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -2857,7 +2857,7 @@ static struct drm_driver driver = {
 	.open = i915_driver_open,
 	.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,
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 4e158aa..845a170 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -318,6 +318,8 @@ struct i915_mmu_object;
 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;
@@ -1915,6 +1917,7 @@ struct drm_i915_private {
 
 	bool preserve_bios_swizzle;
 
+	struct kobject memtrack_kobj;
 	/* overlay */
 	struct intel_overlay *overlay;
 
@@ -3124,6 +3127,8 @@ i915_gem_object_create_from_data(struct drm_i915_private *dev_priv,
 				 const void *data, size_t size);
 void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file);
 void i915_gem_free_object(struct drm_gem_object *obj);
+int i915_gem_open_object(struct drm_gem_object *gem_obj,
+			struct drm_file *file_priv);
 
 static inline void i915_gem_drain_freed_objects(struct drm_i915_private *i915)
 {
@@ -3561,12 +3566,21 @@ static inline int i915_debugfs_connector_add(struct drm_connector *connector)
 { return 0; }
 static inline void intel_display_crc_init(struct drm_i915_private *dev_priv) {}
 #endif
+int i915_get_pid_cmdline(struct task_struct *task, char *buffer);
+int i915_gem_obj_insert_pid(struct drm_i915_gem_object *obj);
+void i915_gem_obj_remove_pid(struct drm_i915_gem_object *obj);
+void i915_gem_obj_remove_all_pids(struct drm_i915_gem_object *obj);
+int i915_get_drm_clients_info(struct drm_i915_error_state_buf *m,
+				struct drm_device *dev);
 
 /* i915_gpu_error.c */
 #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
 
 __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);
+bool i915_error_ok(struct drm_i915_error_state_buf *e);
 int i915_error_state_to_str(struct drm_i915_error_state_buf *estr,
 			    const struct i915_gpu_state *gpu);
 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 63308ec..57a6d88 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -45,6 +45,61 @@
 #include <linux/swap.h>
 #include <linux/pci.h>
 #include <linux/dma-buf.h>
+#include <linux/sched/mm.h>
+#include <drm/drmP.h>
+#include <linux/async.h>
+#include <linux/pid.h>
+#include<linux/firmware.h>
+#include "i915_gem_object.h"
+#define DRM_DEBUG_MAGIC_HASH_ORDER 4
+/**< Size of key hash table. Must be power of 2. */
+
+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;
+};
+
 
 static void i915_gem_flush_free_objects(struct drm_i915_private *i915);
 
@@ -3536,6 +3591,8 @@ 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)
@@ -4550,6 +4607,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);
 
@@ -4727,6 +4785,7 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
 		GEM_BUG_ON(obj->userfault_count);
 		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);
@@ -4818,6 +4877,24 @@ static void __i915_gem_free_object_rcu(struct rcu_head *head)
 		queue_work(i915->wq, &i915->mm.free_work);
 }
 
+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);
+
+	return i915_gem_obj_insert_pid(obj);
+}
+
+/*void
+*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);
+*
+*	i915_gem_obj_remove_pid(obj);
+*}
+*/
 void i915_gem_free_object(struct drm_gem_object *gem_obj)
 {
 	struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
@@ -5603,6 +5680,9 @@ void i915_gem_release(struct drm_device *dev, struct drm_file *file)
 	struct drm_i915_file_private *file_priv = file->driver_priv;
 	struct drm_i915_gem_request *request;
 
+	put_pid(file_priv->tgid);
+	kfree(file_priv->process_name);
+
 	/* Clean up our request list when the client is going away, so that
 	 * later retire_requests won't dereference our soon-to-be-gone
 	 * file_priv.
@@ -5628,6 +5708,20 @@ int i915_gem_open(struct drm_i915_private *i915, struct drm_file *file)
 	file_priv->dev_priv = i915;
 	file_priv->file = file;
 
+	rcu_read_lock();
+	file_priv->tgid = get_pid(find_vpid(task_tgid_nr(current)));
+	rcu_read_unlock();
+
+	file_priv->process_name =  kzalloc(PAGE_SIZE, GFP_ATOMIC);
+	if (!file_priv->process_name) {
+		ret = -ENOMEM;
+		goto out_free_file;
+	}
+
+	ret = i915_get_pid_cmdline(current, file_priv->process_name);
+	if (ret)
+		goto out_free_name;
+
 	spin_lock_init(&file_priv->mm.lock);
 	INIT_LIST_HEAD(&file_priv->mm.request_list);
 
@@ -5635,7 +5729,15 @@ int i915_gem_open(struct drm_i915_private *i915, struct drm_file *file)
 
 	ret = i915_gem_context_open(i915, file);
 	if (ret)
-		kfree(file_priv);
+		goto out_free_name;
+
+	return 0;
+
+out_free_name:
+	kfree(file_priv->process_name);
+out_free_file:
+	put_pid(file_priv->tgid);
+	kfree(file_priv);
 
 	return ret;
 }
@@ -5953,3 +6055,484 @@ 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
+
+#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);
+}
+
+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;
+}
+
+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;
+}
+
+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);
+}
+
+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 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_pid_info *pid_entry, *pid_next;
+	struct drm_file *file;
+	struct drm_i915_file_private *file_priv;
+	struct pid *tgid;
+	int pid_num, present;
+
+	/*
+	 * Run a sanity check on pid_array. All entries in pid_array should
+	 * be subset of the the drm filelist pid entries.
+	 */
+	list_for_each_entry_safe(pid_entry, pid_next, &obj->pid_info, head) {
+		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 == pid_entry->tgid) {
+				present = 1;
+				break;
+			}
+		}
+			if (present == 0) {
+			DRM_DEBUG("stale_tgid=%d\n", pid_entry->tgid);
+			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_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;
+	int obj_shared_count = 0;
+	bool discard = false;
+
+	i915_obj_pidarray_validate(&obj->base);
+
+	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 +=
+					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_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) {
+		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(*new_entry), GFP_KERNEL);
+			if (new_entry == NULL) {
+				DRM_ERROR("alloc failed\n");
+				ret = -ENOMEM;
+				break;
+			}
+			new_entry->tgid = tgid;
+			new_entry->pid_num = 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);
+			new_entry->stats.process_name = file_priv->process_name;
+			pid_entry = new_entry;
+		}
+
+		spin_lock(&file->table_lock);
+		ret = idr_for_each(&file->object_idr,
+			&i915_drm_gem_object_per_file_summary, pid_entry);
+		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);
+		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;
+}
diff --git a/drivers/gpu/drm/i915/i915_gem_object.h b/drivers/gpu/drm/i915/i915_gem_object.h
index 05e89e1..ceb0bf7 100644
--- a/drivers/gpu/drm/i915/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/i915_gem_object.h
@@ -264,6 +264,7 @@ struct drm_i915_gem_object {
 
 		void *gvt_info;
 	};
+	struct list_head pid_info;
 
 	/** for phys allocated objects */
 	struct drm_dma_handle *phys_handle;
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index a81351d..fa83569 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -89,6 +89,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)
 {
@@ -160,7 +165,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_params.c b/drivers/gpu/drm/i915/i915_params.c
index 08108ce..04db7d2 100644
--- a/drivers/gpu/drm/i915/i915_params.c
+++ b/drivers/gpu/drm/i915/i915_params.c
@@ -172,6 +172,9 @@ i915_param_named_unsafe(inject_load_failure, uint, 0400,
 	"Force an error after a number of failure check points (0:disabled (default), N:force failure at the Nth failure check point)");
 #endif
 
+i915_param_named(memtrack_debug, bool, 0600,
+	"use memtrack capability (default:true)");
+
 i915_param_named(enable_dpcd_backlight, bool, 0600,
 	"Enable support for DPCD backlight control (default:false)");
 
diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h
index 430f5f9..1660170 100644
--- a/drivers/gpu/drm/i915/i915_params.h
+++ b/drivers/gpu/drm/i915/i915_params.h
@@ -49,6 +49,7 @@ struct drm_printer;
 	param(int, invert_brightness, 0) \
 	param(int, enable_guc, 0) \
 	param(int, guc_log_level, 0) \
+	param(bool, memtrack_debug, true) \
 	param(char *, guc_firmware_path, NULL) \
 	param(char *, huc_firmware_path, NULL) \
 	param(int, mmio_debug, 0) \
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c
index c74a20b..ba914c0 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -31,6 +31,8 @@
 #include <linux/sysfs.h>
 #include "intel_drv.h"
 #include "i915_drv.h"
+#include "../drm_internal.h"
+#include <linux/firmware.h>
 
 static inline struct drm_i915_private *kdev_minor_to_i915(struct device *kdev)
 {
@@ -546,6 +548,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",
@@ -554,11 +586,51 @@ static const struct bin_attribute error_state_attr = {
 	.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)
@@ -635,4 +707,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);
+	}
 }
-- 
2.7.4



More information about the Intel-gfx-trybot mailing list