| From f90ab5ac8a2b3a2d7af46efc4bc812baa596381e Mon Sep 17 00:00:00 2001 |
| From: Tvrtko Ursulin <tvrtko.ursulin@intel.com> |
| Date: Sun, 24 Jan 2021 15:31:31 +0000 |
| Subject: [PATCH] FROMGIT: drm/i915: Update client name on context create |
| |
| Some clients have the DRM fd passed to them over a socket by the X server. |
| |
| Grab the real client and pid when they create their first context and |
| update the exposed data for more useful enumeration. |
| |
| To enable lockless access to client name and pid data from the following |
| patches, we also make these fields rcu protected. In this way asynchronous |
| code paths where both contexts which remain after the client exit, and |
| access to client name and pid as they are getting updated due context |
| creation running in parallel with name/pid queries. |
| |
| v2: |
| * Do not leak the pid reference and borrow context idr_lock. (Chris) |
| |
| v3: |
| * More avoiding leaks. (Chris) |
| |
| v4: |
| * Move update completely to drm client. (Chris) |
| * Do not lose previous client data on failure to re-register and simplify |
| update to only touch what it needs. |
| |
| v5: |
| * Reuse ext_data local. (Chris) |
| |
| 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> |
| Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> |
| Link: https://patchwork.freedesktop.org/patch/msgid/20210123153733.18139-3-chris@chris-wilson.co.uk |
| Link: https://patchwork.freedesktop.org/patch/msgid/20210124153136.19124-3-chris@chris-wilson.co.uk |
| (cherry picked from commit 12d52c78bc459feb7eda622f5013c620d6a20149 |
| https://anongit.freedesktop.org/git/drm-intel.git drm-intel-gt-next) |
| |
| BUG=b:179453336 |
| TEST=eDP comes up on brya when built with this commit https://crrev.com/i/3584703 |
| |
| Change-Id: I990badf6bebebc17ddd02cf213a065042bfd6dd4 |
| Signed-off-by: Azhar Shaikh <azhar.shaikh@intel.com> |
| --- |
| drivers/gpu/drm/i915/gem/i915_gem_context.c | 5 ++ |
| drivers/gpu/drm/i915/i915_drm_client.c | 93 +++++++++++++++++---- |
| drivers/gpu/drm/i915/i915_drm_client.h | 34 +++++++- |
| 3 files changed, 115 insertions(+), 17 deletions(-) |
| |
| diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c |
| --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c |
| +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c |
| @@ -77,6 +77,7 @@ |
| #include "gt/intel_gpu_commands.h" |
| #include "gt/intel_ring.h" |
| |
| +#include "i915_drm_client.h" |
| #include "i915_gem_context.h" |
| #include "i915_trace.h" |
| #include "i915_user_extensions.h" |
| @@ -1982,6 +1983,10 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, |
| return -EIO; |
| } |
| |
| + ret = i915_drm_client_update(ext_data.fpriv->client, current); |
| + if (ret) |
| + return ret; |
| + |
| ext_data.pc = proto_context_create(i915, args->flags); |
| if (IS_ERR(ext_data.pc)) |
| return PTR_ERR(ext_data.pc); |
| diff --git a/drivers/gpu/drm/i915/i915_drm_client.c b/drivers/gpu/drm/i915/i915_drm_client.c |
| --- a/drivers/gpu/drm/i915/i915_drm_client.c |
| +++ b/drivers/gpu/drm/i915/i915_drm_client.c |
| @@ -7,7 +7,10 @@ |
| #include <linux/slab.h> |
| #include <linux/types.h> |
| |
| +#include <drm/drm_print.h> |
| + |
| #include "i915_drm_client.h" |
| +#include "i915_drv.h" |
| #include "i915_gem.h" |
| #include "i915_utils.h" |
| |
| @@ -25,10 +28,15 @@ show_client_name(struct device *kdev, struct device_attribute *attr, char *buf) |
| { |
| struct i915_drm_client *client = |
| container_of(attr, typeof(*client), attr.name); |
| + int ret; |
| |
| - return sysfs_emit(buf, |
| - READ_ONCE(client->closed) ? "<%s>\n" : "%s\n", |
| - client->name); |
| + rcu_read_lock(); |
| + ret = sysfs_emit(buf, |
| + READ_ONCE(client->closed) ? "<%s>\n" : "%s\n", |
| + i915_drm_client_name(client)); |
| + rcu_read_unlock(); |
| + |
| + return ret; |
| } |
| |
| static ssize_t |
| @@ -36,10 +44,15 @@ show_client_pid(struct device *kdev, struct device_attribute *attr, char *buf) |
| { |
| struct i915_drm_client *client = |
| container_of(attr, typeof(*client), attr.pid); |
| + int ret; |
| + |
| + rcu_read_lock(); |
| + ret = sysfs_emit(buf, |
| + READ_ONCE(client->closed) ? "<%u>\n" : "%u\n", |
| + pid_nr(i915_drm_client_pid(client))); |
| + rcu_read_unlock(); |
| |
| - return sysfs_emit(buf, |
| - READ_ONCE(client->closed) ? "<%u>\n" : "%u\n", |
| - pid_nr(client->pid)); |
| + return ret; |
| } |
| |
| static int __client_register_sysfs(struct i915_drm_client *client) |
| @@ -91,20 +104,46 @@ static void __client_unregister_sysfs(struct i915_drm_client *client) |
| kobject_put(fetch_and_zero(&client->root)); |
| } |
| |
| +static struct i915_drm_client_name *get_name(struct i915_drm_client *client, |
| + struct task_struct *task) |
| +{ |
| + struct i915_drm_client_name *name; |
| + int len = strlen(task->comm); |
| + |
| + name = kmalloc(struct_size(name, name, len + 1), GFP_KERNEL); |
| + if (!name) |
| + return NULL; |
| + |
| + init_rcu_head(&name->rcu); |
| + name->client = client; |
| + name->pid = get_task_pid(task, PIDTYPE_PID); |
| + memcpy(name->name, task->comm, len + 1); |
| + |
| + return name; |
| +} |
| + |
| +static void free_name(struct rcu_head *rcu) |
| +{ |
| + struct i915_drm_client_name *name = |
| + container_of(rcu, typeof(*name), rcu); |
| + |
| + put_pid(name->pid); |
| + kfree(name); |
| +} |
| + |
| static int |
| __i915_drm_client_register(struct i915_drm_client *client, |
| struct task_struct *task) |
| { |
| struct i915_drm_clients *clients = client->clients; |
| - char *name; |
| + struct i915_drm_client_name *name; |
| int ret; |
| |
| - name = kstrdup(task->comm, GFP_KERNEL); |
| + name = get_name(client, task); |
| if (!name) |
| return -ENOMEM; |
| |
| - client->pid = get_task_pid(task, PIDTYPE_PID); |
| - client->name = name; |
| + RCU_INIT_POINTER(client->name, name); |
| |
| if (!clients->root) |
| return 0; /* intel_fbdev_init registers a client before sysfs */ |
| @@ -116,18 +155,22 @@ __i915_drm_client_register(struct i915_drm_client *client, |
| return 0; |
| |
| err_sysfs: |
| - put_pid(client->pid); |
| - kfree(client->name); |
| - |
| + RCU_INIT_POINTER(client->name, NULL); |
| + call_rcu(&name->rcu, free_name); |
| return ret; |
| } |
| |
| static void __i915_drm_client_unregister(struct i915_drm_client *client) |
| { |
| + struct i915_drm_client_name *name; |
| + |
| __client_unregister_sysfs(client); |
| |
| - put_pid(fetch_and_zero(&client->pid)); |
| - kfree(fetch_and_zero(&client->name)); |
| + mutex_lock(&client->update_lock); |
| + name = rcu_replace_pointer(client->name, NULL, true); |
| + mutex_unlock(&client->update_lock); |
| + |
| + call_rcu(&name->rcu, free_name); |
| } |
| |
| static void __rcu_i915_drm_client_free(struct work_struct *wrk) |
| @@ -152,6 +195,7 @@ i915_drm_client_add(struct i915_drm_clients *clients, struct task_struct *task) |
| return ERR_PTR(-ENOMEM); |
| |
| kref_init(&client->kref); |
| + mutex_init(&client->update_lock); |
| client->clients = clients; |
| INIT_RCU_WORK(&client->rcu, __rcu_i915_drm_client_free); |
| |
| @@ -189,6 +233,25 @@ void i915_drm_client_close(struct i915_drm_client *client) |
| i915_drm_client_put(client); |
| } |
| |
| +int |
| +i915_drm_client_update(struct i915_drm_client *client, |
| + struct task_struct *task) |
| +{ |
| + struct i915_drm_client_name *name; |
| + |
| + name = get_name(client, task); |
| + if (!name) |
| + return -ENOMEM; |
| + |
| + mutex_lock(&client->update_lock); |
| + if (name->pid != rcu_dereference_protected(client->name, true)->pid) |
| + name = rcu_replace_pointer(client->name, name, true); |
| + mutex_unlock(&client->update_lock); |
| + |
| + call_rcu(&name->rcu, free_name); |
| + return 0; |
| +} |
| + |
| void i915_drm_clients_fini(struct i915_drm_clients *clients) |
| { |
| while (!xa_empty(&clients->xarray)) { |
| diff --git a/drivers/gpu/drm/i915/i915_drm_client.h b/drivers/gpu/drm/i915/i915_drm_client.h |
| --- a/drivers/gpu/drm/i915/i915_drm_client.h |
| +++ b/drivers/gpu/drm/i915/i915_drm_client.h |
| @@ -9,6 +9,7 @@ |
| #include <linux/device.h> |
| #include <linux/kobject.h> |
| #include <linux/kref.h> |
| +#include <linux/mutex.h> |
| #include <linux/pid.h> |
| #include <linux/rcupdate.h> |
| #include <linux/sched.h> |
| @@ -25,14 +26,22 @@ struct i915_drm_clients { |
| struct kobject *root; |
| }; |
| |
| +struct i915_drm_client_name { |
| + struct rcu_head rcu; |
| + struct i915_drm_client *client; |
| + struct pid *pid; |
| + char name[]; |
| +}; |
| + |
| struct i915_drm_client { |
| struct kref kref; |
| |
| struct rcu_work rcu; |
| |
| + struct mutex update_lock; /* Serializes name and pid updates. */ |
| + |
| unsigned int id; |
| - struct pid *pid; |
| - char *name; |
| + struct i915_drm_client_name __rcu *name; |
| bool closed; |
| |
| struct i915_drm_clients *clients; |
| @@ -66,6 +75,27 @@ 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); |
| |
| +int i915_drm_client_update(struct i915_drm_client *client, |
| + struct task_struct *task); |
| + |
| +static inline const struct i915_drm_client_name * |
| +__i915_drm_client_name(const struct i915_drm_client *client) |
| +{ |
| + return rcu_dereference(client->name); |
| +} |
| + |
| +static inline const char * |
| +i915_drm_client_name(const struct i915_drm_client *client) |
| +{ |
| + return __i915_drm_client_name(client)->name; |
| +} |
| + |
| +static inline struct pid * |
| +i915_drm_client_pid(const struct i915_drm_client *client) |
| +{ |
| + return __i915_drm_client_name(client)->pid; |
| +} |
| + |
| void i915_drm_clients_fini(struct i915_drm_clients *clients); |
| |
| #endif /* !__I915_DRM_CLIENT_H__ */ |
| -- |
| 2.33.0.685.g46640cef36-goog |
| |