From: Tvrtko Ursulin <tvrtko.ursu...@intel.com>

Expose a list of clients with open file handles in sysfs.

This will be a basis for a top-like utility showing per-client and per-
engine GPU load.

Currently we only expose each client's pid and name under opaque numbered
directories in /sys/class/drm/card0/clients/.

For instance:

/sys/class/drm/card0/clients/3/name: Xorg
/sys/class/drm/card0/clients/3/pid: 5664

v2:
 Chris Wilson:
 * Enclose new members into dedicated structs.
 * Protect against failed sysfs registration.

v3:
 * sysfs_attr_init.

v4:
 * Fix for internal clients.

v5:
 * Use cyclic ida for client id. (Chris)
 * Do not leak pid reference. (Chris)
 * Tidy code with some locals.

v6:
 * Use xa_alloc_cyclic to simplify locking. (Chris)
 * No need to unregister individial sysfs files. (Chris)
 * Rebase on top of fpriv kref.
 * Track client closed status and reflect in sysfs.

v7:
 * Make drm_client more standalone concept.

v8:
 * Simplify sysfs show. (Chris)
 * Always track name and pid.

v9:
 * Fix cyclic id assignment.

v10:
 * No need for a mutex around xa_alloc_cyclic.
 * Refactor sysfs into own function.
 * Unregister sysfs before freeing pid and name.
 * Move clients setup into own function.

Signed-off-by: Tvrtko Ursulin <tvrtko.ursu...@intel.com>
---
 drivers/gpu/drm/i915/Makefile          |   3 +-
 drivers/gpu/drm/i915/i915_drm_client.c | 181 +++++++++++++++++++++++++
 drivers/gpu/drm/i915/i915_drm_client.h |  65 +++++++++
 drivers/gpu/drm/i915/i915_drv.h        |   5 +
 drivers/gpu/drm/i915/i915_gem.c        |  35 ++++-
 drivers/gpu/drm/i915/i915_sysfs.c      |   8 ++
 6 files changed, 289 insertions(+), 8 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/i915_drm_client.c
 create mode 100644 drivers/gpu/drm/i915/i915_drm_client.h

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 9f887a86e555..c6fc0f258ce3 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -36,7 +36,8 @@ subdir-ccflags-y += -I$(srctree)/$(src)
 # Please keep these build lists sorted!
 
 # core driver code
-i915-y += i915_drv.o \
+i915-y += i915_drm_client.o \
+         i915_drv.o \
          i915_irq.o \
          i915_getparam.o \
          i915_params.o \
diff --git a/drivers/gpu/drm/i915/i915_drm_client.c 
b/drivers/gpu/drm/i915/i915_drm_client.c
new file mode 100644
index 000000000000..0775c2273899
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_drm_client.c
@@ -0,0 +1,181 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2020 Intel Corporation
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "i915_drm_client.h"
+#include "i915_gem.h"
+#include "i915_utils.h"
+
+
+void i915_drm_clients_init(struct i915_drm_clients *clients)
+{
+       clients->next_id = 0;
+       xa_init_flags(&clients->xarray, XA_FLAGS_ALLOC);
+}
+
+static ssize_t
+show_client_name(struct device *kdev, struct device_attribute *attr, char *buf)
+{
+       struct i915_drm_client *client =
+               container_of(attr, typeof(*client), attr.name);
+
+       return snprintf(buf, PAGE_SIZE,
+                       READ_ONCE(client->closed) ? "<%s>" : "%s",
+                       client->name);
+}
+
+static ssize_t
+show_client_pid(struct device *kdev, struct device_attribute *attr, char *buf)
+{
+       struct i915_drm_client *client =
+               container_of(attr, typeof(*client), attr.pid);
+
+       return snprintf(buf, PAGE_SIZE,
+                       READ_ONCE(client->closed) ? "<%u>" : "%u",
+                       pid_nr(client->pid));
+}
+
+static int
+__client_register_sysfs(struct i915_drm_client *client)
+{
+       const struct {
+               const char *name;
+               struct device_attribute *attr;
+               ssize_t (*show)(struct device *,
+                               struct device_attribute *,
+                               char *buf);
+       } files[] = {
+               { "name", &client->attr.name, show_client_name },
+               { "pid", &client->attr.pid, show_client_pid },
+       };
+       unsigned int i;
+       char buf[16];
+       int ret;
+
+       ret = scnprintf(buf, sizeof(buf), "%u", client->id);
+       if (ret == sizeof(buf))
+               return -EINVAL;
+
+       client->root = kobject_create_and_add(buf, client->clients->root);
+       if (!client->root)
+               return -ENOMEM;
+
+       for (i = 0; i < ARRAY_SIZE(files); i++) {
+               struct device_attribute *attr = files[i].attr;
+
+               sysfs_attr_init(&attr->attr);
+
+               attr->attr.name = files[i].name;
+               attr->attr.mode = 0444;
+               attr->show = files[i].show;
+
+               ret = sysfs_create_file(client->root, (struct attribute *)attr);
+               if (ret)
+                       break;
+       }
+
+       if (ret)
+               kobject_put(client->root);
+
+       return ret;
+}
+
+static void __client_unregister_sysfs(struct i915_drm_client *client)
+{
+       kobject_put(fetch_and_zero(&client->root));
+}
+
+static int
+__i915_drm_client_register(struct i915_drm_client *client,
+                          struct task_struct *task)
+{
+       struct i915_drm_clients *clients = client->clients;
+       char *name;
+       int ret;
+
+       name = kstrdup(task->comm, GFP_KERNEL);
+       if (!name)
+               return -ENOMEM;
+
+       client->pid = get_task_pid(task, PIDTYPE_PID);
+       client->name = name;
+
+       if (!clients->root)
+               return 0; /* intel_fbdev_init registers a client before sysfs */
+
+       ret = __client_register_sysfs(client);
+       if (ret)
+               goto err_sysfs;
+
+       return 0;
+
+err_sysfs:
+       put_pid(client->pid);
+       kfree(client->name);
+
+       return ret;
+}
+
+static void
+__i915_drm_client_unregister(struct i915_drm_client *client)
+{
+       __client_unregister_sysfs(client);
+
+       put_pid(fetch_and_zero(&client->pid));
+       kfree(fetch_and_zero(&client->name));
+}
+
+struct i915_drm_client *
+i915_drm_client_add(struct i915_drm_clients *clients, struct task_struct *task)
+{
+       struct i915_drm_client *client;
+       int ret;
+
+       client = kzalloc(sizeof(*client), GFP_KERNEL);
+       if (!client)
+               return ERR_PTR(-ENOMEM);
+
+       kref_init(&client->kref);
+       client->clients = clients;
+
+       ret = xa_alloc_cyclic(&clients->xarray, &client->id, client,
+                             xa_limit_32b, &clients->next_id, GFP_KERNEL);
+       if (ret)
+               goto err_id;
+
+       ret = __i915_drm_client_register(client, task);
+       if (ret)
+               goto err_register;
+
+       return client;
+
+err_register:
+       xa_erase(&clients->xarray, client->id);
+err_id:
+       kfree(client);
+
+       return ERR_PTR(ret);
+}
+
+void __i915_drm_client_free(struct kref *kref)
+{
+       struct i915_drm_client *client =
+               container_of(kref, typeof(*client), kref);
+
+       __i915_drm_client_unregister(client);
+       xa_erase(&client->clients->xarray, client->id);
+       kfree_rcu(client, rcu);
+}
+
+void i915_drm_client_close(struct i915_drm_client *client)
+{
+       GEM_BUG_ON(READ_ONCE(client->closed));
+       WRITE_ONCE(client->closed, true);
+       i915_drm_client_put(client);
+}
diff --git a/drivers/gpu/drm/i915/i915_drm_client.h 
b/drivers/gpu/drm/i915/i915_drm_client.h
new file mode 100644
index 000000000000..5009a2ae4b65
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_drm_client.h
@@ -0,0 +1,65 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2020 Intel Corporation
+ */
+
+#ifndef __I915_DRM_CLIENT_H__
+#define __I915_DRM_CLIENT_H__
+
+#include <linux/device.h>
+#include <linux/kobject.h>
+#include <linux/kref.h>
+#include <linux/pid.h>
+#include <linux/rcupdate.h>
+#include <linux/sched.h>
+#include <linux/xarray.h>
+
+struct i915_drm_clients {
+       struct xarray xarray;
+       u32 next_id;
+
+       struct kobject *root;
+};
+
+struct i915_drm_client {
+       struct kref kref;
+
+       struct rcu_head rcu;
+
+       unsigned int id;
+       struct pid *pid;
+       char *name;
+       bool closed;
+
+       struct i915_drm_clients *clients;
+
+       struct kobject *root;
+       struct {
+               struct device_attribute pid;
+               struct device_attribute name;
+       } attr;
+};
+
+void i915_drm_clients_init(struct i915_drm_clients *clients);
+
+static inline struct i915_drm_client *
+i915_drm_client_get(struct i915_drm_client *client)
+{
+       kref_get(&client->kref);
+       return client;
+}
+
+void __i915_drm_client_free(struct kref *kref);
+
+static inline void i915_drm_client_put(struct i915_drm_client *client)
+{
+       kref_put(&client->kref, __i915_drm_client_free);
+}
+
+void i915_drm_client_close(struct i915_drm_client *client);
+
+struct i915_drm_client *i915_drm_client_add(struct i915_drm_clients *clients,
+                                           struct task_struct *task);
+
+#endif /* !__I915_DRM_CLIENT_H__ */
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 19195bde4921..b2617d2f5b29 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -91,6 +91,7 @@
 #include "intel_wakeref.h"
 #include "intel_wopcm.h"
 
+#include "i915_drm_client.h"
 #include "i915_gem.h"
 #include "i915_gem_fence_reg.h"
 #include "i915_gem_gtt.h"
@@ -227,6 +228,8 @@ struct drm_i915_file_private {
        /** ban_score: Accumulated score of all ctx bans and fast hangs. */
        atomic_t ban_score;
        unsigned long hang_timestamp;
+
+       struct i915_drm_client *client;
 };
 
 /* Interface history:
@@ -1200,6 +1203,8 @@ struct drm_i915_private {
 
        struct i915_pmu pmu;
 
+       struct i915_drm_clients clients;
+
        struct i915_hdcp_comp_master *hdcp_master;
        bool hdcp_comp_added;
 
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index ca5420012a22..7def0930684b 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1218,12 +1218,14 @@ static void i915_gem_init__mm(struct drm_i915_private 
*i915)
        i915_gem_init__objects(i915);
 }
 
-void i915_gem_init_early(struct drm_i915_private *dev_priv)
+void i915_gem_init_early(struct drm_i915_private *i915)
 {
-       i915_gem_init__mm(dev_priv);
-       i915_gem_init__contexts(dev_priv);
+       i915_gem_init__mm(i915);
+       i915_gem_init__contexts(i915);
 
-       spin_lock_init(&dev_priv->fb_tracking.lock);
+       spin_lock_init(&i915->fb_tracking.lock);
+
+       i915_drm_clients_init(&i915->clients);
 }
 
 void i915_gem_cleanup_early(struct drm_i915_private *dev_priv)
@@ -1232,6 +1234,8 @@ void i915_gem_cleanup_early(struct drm_i915_private 
*dev_priv)
        GEM_BUG_ON(!llist_empty(&dev_priv->mm.free_list));
        GEM_BUG_ON(atomic_read(&dev_priv->mm.free_count));
        drm_WARN_ON(&dev_priv->drm, dev_priv->mm.shrink_count);
+       drm_WARN_ON(&dev_priv->drm, !xa_empty(&dev_priv->clients.xarray));
+       xa_destroy(&dev_priv->clients.xarray);
 }
 
 int i915_gem_freeze(struct drm_i915_private *dev_priv)
@@ -1286,6 +1290,8 @@ void i915_gem_release(struct drm_device *dev, struct 
drm_file *file)
        struct drm_i915_file_private *file_priv = file->driver_priv;
        struct i915_request *request;
 
+       i915_drm_client_close(file_priv->client);
+
        /* 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.
@@ -1299,17 +1305,25 @@ void i915_gem_release(struct drm_device *dev, struct 
drm_file *file)
 int i915_gem_open(struct drm_i915_private *i915, struct drm_file *file)
 {
        struct drm_i915_file_private *file_priv;
-       int ret;
+       struct i915_drm_client *client;
+       int ret = -ENOMEM;
 
        DRM_DEBUG("\n");
 
        file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
        if (!file_priv)
-               return -ENOMEM;
+               goto err_alloc;
+
+       client = i915_drm_client_add(&i915->clients, current);
+       if (IS_ERR(client)) {
+               ret = PTR_ERR(client);
+               goto err_client;
+       }
 
        file->driver_priv = file_priv;
        file_priv->dev_priv = i915;
        file_priv->file = file;
+       file_priv->client = client;
 
        spin_lock_init(&file_priv->mm.lock);
        INIT_LIST_HEAD(&file_priv->mm.request_list);
@@ -1319,8 +1333,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 err_context;
+
+       return 0;
 
+err_context:
+       i915_drm_client_close(client);
+err_client:
+       kfree(file_priv);
+err_alloc:
        return ret;
 }
 
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c 
b/drivers/gpu/drm/i915/i915_sysfs.c
index 45d32ef42787..b7d4a6d2dd5c 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -560,6 +560,11 @@ void i915_setup_sysfs(struct drm_i915_private *dev_priv)
        struct device *kdev = dev_priv->drm.primary->kdev;
        int ret;
 
+       dev_priv->clients.root =
+               kobject_create_and_add("clients", &kdev->kobj);
+       if (!dev_priv->clients.root)
+               DRM_ERROR("Per-client sysfs setup failed\n");
+
 #ifdef CONFIG_PM
        if (HAS_RC6(dev_priv)) {
                ret = sysfs_merge_group(&kdev->kobj,
@@ -627,4 +632,7 @@ 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 (dev_priv->clients.root)
+               kobject_put(dev_priv->clients.root);
 }
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to