[Intel-gfx] [RFC 1/8] drm/i915: Expose list of clients in sysfs

Tvrtko Ursulin
2020-01-10T13:30:56Z

From: Tvrtko Ursulin <tvrtko.ursulin@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.

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
drivers/gpu/drm/i915/Makefile | 3 +-
drivers/gpu/drm/i915/i915_drm_client.c | 150 +++++++++++++++++++++++++
drivers/gpu/drm/i915/i915_drm_client.h | 61 ++++++++++
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, 254 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 b8c5f8934dbd..3a677a20a709 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..02356f48d85b
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_drm_client.c
@@ -0,0 +1,150 @@
+/*
+ * 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"
+
+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, "%s%s%s",
+ client->closed ? "<" : "",
+ client->name,
+ client->closed ? ">" : "");
+}
+
+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, "%s%u%s",
+ client->closed ? "<" : "",
+ pid_nr(client->pid),
+ client->closed ? ">" : "");
+}
+
+static int
+__i915_drm_client_register(struct i915_drm_client *client,
+ struct task_struct *task)
+{
+ struct i915_drm_clients *clients = client->clients;
+ struct device_attribute *attr;
+ int ret = -ENOMEM;
+ char idstr[32];
+
+ if (!clients->root)
+ return 0; /* intel_fbdev_init registers a client before sysfs */
+
+ client->name = kstrdup(task->comm, GFP_KERNEL);
+ if (!client->name)
+ goto err_name;
+
+ snprintf(idstr, sizeof(idstr), "%u", client->id);
+ client->root = kobject_create_and_add(idstr, clients->root);
+ if (!client->root)
+ goto err_client;
+
+ attr = &client->attr.name;
+ sysfs_attr_init(&attr->attr);
+ attr->attr.name = "name";
+ attr->attr.mode = 0444;
+ attr->show = show_client_name;
+
+ ret = sysfs_create_file(client->root, (struct attribute *)attr);
+ if (ret)
+ goto err_attr;
+
+ attr = &client->attr.pid;
+ sysfs_attr_init(&attr->attr);
+ attr->attr.name = "pid";
+ attr->attr.mode = 0444;
+ attr->show = show_client_pid;
+
+ ret = sysfs_create_file(client->root, (struct attribute *)attr);
+ if (ret)
+ goto err_attr;
+
+ client->pid = get_task_pid(task, PIDTYPE_PID);
+
+ return 0;
+
+err_attr:
+ kobject_put(client->root);
+err_client:
+ kfree(client->name);
+err_name:
+ return ret;
+}
+
+static void __i915_drm_client_unregister(struct i915_drm_client *client)
+{
+ if (!client->name)
+ return; /* fbdev client or error during drm open */
+
+ kobject_put(fetch_and_zero(&client->root));
+ 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;
+ u32 next = 0;
+ 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, &next, 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(client->closed);
+ 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..8b261dc4a1f3
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_drm_client.h
@@ -0,0 +1,61 @@
+/*
+ * 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;
+ 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;
+};
+
+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 1025d783f494..b04c868ef9b5 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"
@@ -224,6 +225,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:
@@ -1286,6 +1289,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 94f993e4c12f..74007ce8f81f 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1213,12 +1213,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);
+
+ xa_init_flags(&i915->clients.xarray, XA_FLAGS_ALLOC);
}

void i915_gem_cleanup_early(struct drm_i915_private *dev_priv)
@@ -1227,6 +1229,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));
WARN_ON(dev_priv->mm.shrink_count);
+ WARN_ON(!xa_empty(&dev_priv->clients.xarray));
+ xa_destroy(&dev_priv->clients.xarray);
}

int i915_gem_freeze(struct drm_i915_private *dev_priv)
@@ -1280,6 +1284,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.
@@ -1293,17 +1299,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);
@@ -1313,8 +1327,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 ad2b1b833d7b..3ab50e29fddf 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -559,6 +559,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,
@@ -619,4 +624,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