From patchwork Thu Mar 22 14:14:56 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Praveen Paneri X-Patchwork-Id: 10301589 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id D16F7600F6 for ; Thu, 22 Mar 2018 14:01:04 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C0A2528590 for ; Thu, 22 Mar 2018 14:01:04 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id BF27F285B7; Thu, 22 Mar 2018 14:01:04 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id DF5D0285A0 for ; Thu, 22 Mar 2018 14:01:02 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 8A6696EC28; Thu, 22 Mar 2018 14:00:56 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga03.intel.com (mga03.intel.com [134.134.136.65]) by gabe.freedesktop.org (Postfix) with ESMTPS id 5228B6EC27 for ; Thu, 22 Mar 2018 14:00:55 +0000 (UTC) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by orsmga103.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 22 Mar 2018 07:00:55 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.48,345,1517904000"; d="scan'208";a="30323532" Received: from intel-desktop.iind.intel.com ([10.223.26.163]) by fmsmga002.fm.intel.com with ESMTP; 22 Mar 2018 07:00:53 -0700 From: Praveen Paneri To: intel-gfx@lists.freedesktop.org Date: Thu, 22 Mar 2018 19:44:56 +0530 Message-Id: <1521728098-3669-4-git-send-email-praveen.paneri@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1521728098-3669-1-git-send-email-praveen.paneri@intel.com> References: <1521728098-3669-1-git-send-email-praveen.paneri@intel.com> Subject: [Intel-gfx] [RFC 3/5] drm/i915: Sysfs interface to get detailed GFX buffer info per process X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: daniel.vetter@ffwll.ch, nidhi1.gupta@intel.com MIME-Version: 1.0 Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Virus-Scanned: ClamAV using ClamSMTP From: Sourab Gupta Sometimes, in order to debug the mem usage scenarios, the summarized view of GFX memory consumption per process is not sufficient, as it doesn't provide buffer level details. Therefore, there's a need for an interface, which can provide such detailed information. This patch provides a sysfs interface to retrieve such detailed information about GFX buffers used per process. Each process has a sysfs file associated in the 'gfx_memtrack' directory, named by the process' tgid. Reading the file will provide list of all GFX buffers open by the process, along with their attributes such as size, pinneed, tiling, shared, allocstate, gtt offset, etc. v2: -Use div64_u64 for 64 bit divisions (Sagar) Signed-off-by: Sourab Gupta Signed-off-by: Akash Goel Signed-off-by: Nidhi Gupta Signed-off-by: Praveen Paneri --- drivers/gpu/drm/i915/i915_debugfs.c | 15 -- drivers/gpu/drm/i915/i915_drv.h | 11 ++ drivers/gpu/drm/i915/i915_gem.c | 302 +++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/i915_gpu_error.c | 14 ++ drivers/gpu/drm/i915/i915_gpu_error.h | 2 + drivers/gpu/drm/i915/i915_sysfs.c | 184 +++++++++++++++++++++ 6 files changed, 507 insertions(+), 21 deletions(-) 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",