[PATCH 1/8] drm/i915: Explicitly track DRM clients
Daniel Vetter
daniel at ffwll.ch
Mon Jul 12 16:12:41 UTC 2021
On Mon, Jul 12, 2021 at 04:51:42PM +0100, Tvrtko Ursulin wrote:
>
> On 12/07/2021 15:42, Daniel Vetter wrote:
> > On Mon, Jul 12, 2021 at 01:17:12PM +0100, Tvrtko Ursulin wrote:
> > > From: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
> > >
> > > Tracking DRM clients more explicitly will allow later patches to
> > > accumulate past and current GPU usage in a centralised place and also
> > > consolidate access to owning task pid/name.
> > >
> > > Unique client id is also assigned for the purpose of distinguishing/
> > > consolidating between multiple file descriptors owned by the same process.
> > >
> > > 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.
> > >
> > > v14:
> > > * Dropped sysfs bits.
> > >
> > > Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
> > > Reviewed-by: Chris Wilson <chris at chris-wilson.co.uk> # v11
> > > Reviewed-by: Aravind Iddamsetty <aravind.iddamsetty at intel.com> # v11
> > > Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
> >
> > On the implementation: I'm not clear why this is a separate object. All
> > that seems to achieve is make the lifetim fun we have in here even more
> > annoying, for not real gain?
> >
> > What's the reasons for this separate i915_drm_client struct? The commit
> > message talks about de-duping these within the same process, but with
> > fdinfo I'm not seeing the relevance of this anymore.
>
> AFAIR I started with the new fields directly in file_priv (note file_priv
> then needed to be freed via RCU due sysfs access to it!), but then the idea
> there was to consolidate new members into a separate struct.
Yeah separate struct makes sense for this stuff, just to
encapsulate/document things a bit. It's the entire scaffolding around it
that I don't think makes sense anymore with the design switch to fdinfo.
> Plan was (and still is in internal) that the concept for DRM client will
> gain more users/usefulness and would benefit from encapsulation from the
> start.
>
> For instance at patch 3 in the series it does consolidate i915 users of
> ctx->pid to go via ctx->client (process name as well). Those are async entry
> points (compared to file_priv lifetime) from error capture and debugfs. Hm
> no, debugfs is there no more, only error capture remains.
>
> As you say since the change of direction to use fdinfo, the asynchronous
> entry path into those members from sysfs is gone. Hence if they were moved
> back to file_priv, and assuming ctx->pid/name changes to be undone, then
> file_priv could remain being immediately freed on file close. Or perhaps we
> lose easy pid/name update for files passed over sockets. I'd have to think
> about that a bit deeper.
>
> But essentially I think ctx->client is a cleaner design that ctx->pid and
> given error capture and debugfs can be async to file_priv lifetime that's a
> benefit for me.
>From a quick check it's just for debug printing when a ctx hung/got
banned, and for that we do want the pid - users won't have an
understanding of a drm_client. I think pid is actually what we want there.
> With regards to de-duping multiple fdinfo entries via client->id - that is
> actually the opposite from what you suggest. Whereas with the sysfs approach
> we had one entry per client, with fdinfo we have duplicates. So client->id
> is essential for userspace to correctly account per client statistics.
Uh why? Like if you use fdinfo and have a bunch of duplicate drm_file,
then your parsing tool can aggregate them all together under the same pid.
No need we do that in the kernel.
If the problem is that we can't tell apart a dup'ed fd from multiple
open() calls, then I think that should be solved by dropping the hash of
the drm_file pointer into the fdinfo.
> > Also, with the fdinfo approach, why do we still need to even track the
> > pid? That can be all figured out from proc now, with much cleaner
> > semantics.
>
> Not sure what you mean here. As explained above pid is tracked as
> replacement for current ctx->pid tracking. So for uses inside i915.
Why do we need to track that for that info file?
> Also note that even from userspace semantics of getting the pid from procfs
> is not necessarily clean, since actually three different file descriptors
> need to be open to get the whole set of data.
>
> /proc/<pid>/fdinfo/<fd> - obviously
> /proc/<pid>/fd/<fd> - to filter out non drm fds avoiding text parsing above
> file
> /proc/<pid>/stat - to get the pid
Well yes userspace needs to keep track of a few things. top works like
that too.
> Since in the intel_gpu_top conversion I do keep the /proc/<pid> open and do
> the rest via openat, I am hoping at least that's safe againt pid swaps. For
> the file descriptor cross check it is more questionable but worst case the
> drm fd check via stat could be dropped and replaced with more fdinfo text
> parsing.
>
> Having the pid and process name in fdinfo hence makes this easier and
> potentially safer. Given how we already store ctx->pid I think that should
> be fine.
Uh no. We need to understand either the procfs rules, or what we need to
understand them. Re-rolling our own interpretation of pid within something
that already has the pid seems very broken.
I do expect openat to work.
> FWIW I still think the whole approach is bad since number of operations
> required scales with number of non drm processes (even threads) on the
> system.
top works.
-Daniel
> Regards,
>
> Tvrtko
>
> > > ---
> > > drivers/gpu/drm/i915/Makefile | 5 +-
> > > drivers/gpu/drm/i915/i915_drm_client.c | 113 +++++++++++++++++++++++++
> > > drivers/gpu/drm/i915/i915_drm_client.h | 61 +++++++++++++
> > > drivers/gpu/drm/i915/i915_drv.c | 6 ++
> > > drivers/gpu/drm/i915/i915_drv.h | 5 ++
> > > drivers/gpu/drm/i915/i915_gem.c | 21 ++++-
> > > 6 files changed, 206 insertions(+), 5 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 10b3bb6207ba..784f99ca11fc 100644
> > > --- a/drivers/gpu/drm/i915/Makefile
> > > +++ b/drivers/gpu/drm/i915/Makefile
> > > @@ -33,8 +33,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 \
> > > 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..83080d9836b0
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/i915/i915_drm_client.c
> > > @@ -0,0 +1,113 @@
> > > +// 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 int
> > > +__i915_drm_client_register(struct i915_drm_client *client,
> > > + struct task_struct *task)
> > > +{
> > > + char *name;
> > > +
> > > + name = kstrdup(task->comm, GFP_KERNEL);
> > > + if (!name)
> > > + return -ENOMEM;
> > > +
> > > + client->pid = get_task_pid(task, PIDTYPE_PID);
> > > + client->name = name;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static void __i915_drm_client_unregister(struct i915_drm_client *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);
> > > +
> > > + xa_erase(&client->clients->xarray, client->id);
> > > +
> > > + __i915_drm_client_unregister(client);
> > > +
> > > + 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);
> > > +}
> > > 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..396f1e336b3f
> > > --- /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/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 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;
> > > +};
> > > +
> > > +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__ */
> > > diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
> > > index 30d8cd8c69b1..8247dcc7586e 100644
> > > --- a/drivers/gpu/drm/i915/i915_drv.c
> > > +++ b/drivers/gpu/drm/i915/i915_drv.c
> > > @@ -68,6 +68,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"
> > > @@ -343,6 +344,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_* */
> > > @@ -362,6 +365,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);
> > > intel_region_ttm_device_fini(dev_priv);
> > > err_ttm:
> > > @@ -381,6 +385,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);
> > > intel_region_ttm_device_fini(dev_priv);
> > > vlv_suspend_cleanup(dev_priv);
> > > @@ -996,6 +1001,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);
> > > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> > > index c4747f4407ef..338d384c31eb 100644
> > > --- a/drivers/gpu/drm/i915/i915_drv.h
> > > +++ b/drivers/gpu/drm/i915/i915_drv.h
> > > @@ -96,6 +96,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"
> > > @@ -284,6 +285,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:
> > > @@ -1218,6 +1221,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 590efc8b0265..ea6c88a99ed2 100644
> > > --- a/drivers/gpu/drm/i915/i915_gem.c
> > > +++ b/drivers/gpu/drm/i915/i915_gem.c
> > > @@ -1179,25 +1179,40 @@ void i915_gem_cleanup_early(struct drm_i915_private *dev_priv)
> > > 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;
> > > }
> > > --
> > > 2.30.2
> > >
> >
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
More information about the dri-devel
mailing list