@@ -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 \
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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__ */
@@ -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"
@@ -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_* */
@@ -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:
@@ -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);
@@ -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);
@@ -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"
@@ -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:
@@ -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;
@@ -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;
}
@@ -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);
}