Skip to content

Commit

Permalink
drm/i915: Expose list of clients in sysfs
Browse files Browse the repository at this point in the history
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.

v11:
 * Call clients init directly from driver init. (Chris)

v12:
 * Do not fail client add on id wrap. (Maciej)

v13 (Lucas): Rebase on upstream

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Aravind Iddamsetty <aravind.iddamsetty@intel.com> # v11
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
  • Loading branch information
tursulin authored and intel-lab-lkp committed Jan 25, 2021
1 parent 3062794 commit 68ec040
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 5 deletions.
5 changes: 3 additions & 2 deletions drivers/gpu/drm/i915/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ subdir-ccflags-y += -I$(srctree)/$(src)
# Please keep these build lists sorted!

# core driver code
i915-y += i915_drv.o \
i915_config.o \
i915-y += i915_config.o \
i915_drm_client.o \
i915_drv.o \
i915_irq.o \
i915_getparam.o \
i915_mitigations.o \
Expand Down
200 changes: 200 additions & 0 deletions drivers/gpu/drm/i915/i915_drm_client.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// 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,
struct drm_i915_private *i915)
{
clients->i915 = i915;

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 sysfs_emit(buf,
READ_ONCE(client->closed) ? "<%s>\n" : "%s\n",
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 sysfs_emit(buf,
READ_ONCE(client->closed) ? "<%u>\n" : "%u\n",
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 *dev,
struct device_attribute *attr,
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, &attr->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));
}

static void __rcu_i915_drm_client_free(struct work_struct *wrk)
{
struct i915_drm_client *client =
container_of(wrk, typeof(*client), rcu.work);

__i915_drm_client_unregister(client);

xa_erase(&client->clients->xarray, client->id);
kfree(client);
}

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;
INIT_RCU_WORK(&client->rcu, __rcu_i915_drm_client_free);

ret = xa_alloc_cyclic(&clients->xarray, &client->id, client,
xa_limit_32b, &clients->next_id, GFP_KERNEL);
if (ret < 0)
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);

queue_rcu_work(system_wq, &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);
}

void i915_drm_clients_fini(struct i915_drm_clients *clients)
{
while (!xa_empty(&clients->xarray)) {
rcu_barrier();
flush_workqueue(system_wq);
}

xa_destroy(&clients->xarray);
}
71 changes: 71 additions & 0 deletions drivers/gpu/drm/i915/i915_drm_client.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* 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 drm_i915_private;

struct i915_drm_clients {
struct drm_i915_private *i915;

struct xarray xarray;
u32 next_id;

struct kobject *root;
};

struct i915_drm_client {
struct kref kref;

struct rcu_work 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,
struct drm_i915_private *i915);

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);

void i915_drm_clients_fini(struct i915_drm_clients *clients);

#endif /* !__I915_DRM_CLIENT_H__ */
6 changes: 6 additions & 0 deletions drivers/gpu/drm/i915/i915_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
#include "gt/intel_rc6.h"

#include "i915_debugfs.h"
#include "i915_drm_client.h"
#include "i915_drv.h"
#include "i915_ioc32.h"
#include "i915_irq.h"
Expand Down Expand Up @@ -339,6 +340,8 @@ static int i915_driver_early_probe(struct drm_i915_private *dev_priv)

intel_gt_init_early(&dev_priv->gt, dev_priv);

i915_drm_clients_init(&dev_priv->clients, dev_priv);

i915_gem_init_early(dev_priv);

/* This must be called before any calls to HAS_PCH_* */
Expand All @@ -359,6 +362,7 @@ static int i915_driver_early_probe(struct drm_i915_private *dev_priv)

err_gem:
i915_gem_cleanup_early(dev_priv);
i915_drm_clients_fini(&dev_priv->clients);
intel_gt_driver_late_release(&dev_priv->gt);
vlv_suspend_cleanup(dev_priv);
err_workqueues:
Expand All @@ -376,6 +380,7 @@ static void i915_driver_late_release(struct drm_i915_private *dev_priv)
intel_irq_fini(dev_priv);
intel_power_domains_cleanup(dev_priv);
i915_gem_cleanup_early(dev_priv);
i915_drm_clients_fini(&dev_priv->clients);
intel_gt_driver_late_release(&dev_priv->gt);
vlv_suspend_cleanup(dev_priv);
i915_workqueues_cleanup(dev_priv);
Expand Down Expand Up @@ -1013,6 +1018,7 @@ static void i915_driver_postclose(struct drm_device *dev, struct drm_file *file)
struct drm_i915_file_private *file_priv = file->driver_priv;

i915_gem_context_close(file);
i915_drm_client_close(file_priv->client);

kfree_rcu(file_priv, rcu);

Expand Down
5 changes: 5 additions & 0 deletions drivers/gpu/drm/i915/i915_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
#include "intel_wakeref.h"
#include "intel_wopcm.h"

#include "i915_drm_client.h"
#include "i915_gem.h"
#include "i915_gem_gtt.h"
#include "i915_gpu_error.h"
Expand Down Expand Up @@ -219,6 +220,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:
Expand Down Expand Up @@ -1203,6 +1206,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;

Expand Down
21 changes: 18 additions & 3 deletions drivers/gpu/drm/i915/i915_gem.c
Original file line number Diff line number Diff line change
Expand Up @@ -1189,25 +1189,40 @@ int i915_gem_freeze_late(struct drm_i915_private *i915)
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;

file_priv->bsd_engine = -1;
file_priv->hang_timestamp = jiffies;

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;
}

Expand Down
Loading

0 comments on commit 68ec040

Please sign in to comment.