@@ -42,6 +42,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_mode_object.h>
#include <drm/drm_print.h>
+#include <drm/drm_cgroup.h>
#include "drm_crtc_internal.h"
#include "drm_internal.h"
@@ -893,6 +894,8 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_modeset_register_all(dev);
+ drmcg_register_dev(dev);
+
DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
driver->name, driver->major, driver->minor,
driver->patchlevel, driver->date,
@@ -928,6 +931,8 @@ EXPORT_SYMBOL(drm_dev_register);
*/
void drm_dev_unregister(struct drm_device *dev)
{
+ drmcg_unregister_dev(dev);
+
if (drm_core_check_feature(dev, DRIVER_LEGACY))
drm_lastclose(dev);
@@ -1030,6 +1035,7 @@ static const struct file_operations drm_stub_fops = {
static void drm_core_exit(void)
{
+ drmcg_unbind();
unregister_chrdev(DRM_MAJOR, "drm");
debugfs_remove(drm_debugfs_root);
drm_sysfs_destroy();
@@ -1056,6 +1062,8 @@ static int __init drm_core_init(void)
if (ret < 0)
goto error;
+ drmcg_bind(&drm_minor_acquire, &drm_dev_put);
+
drm_core_init_complete = true;
DRM_DEBUG("Initialized\n");
new file mode 100644
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: MIT
+ * Copyright 2019 Advanced Micro Devices, Inc.
+ */
+#ifndef __DRM_CGROUP_H__
+#define __DRM_CGROUP_H__
+
+#ifdef CONFIG_CGROUP_DRM
+
+void drmcg_bind(struct drm_minor (*(*acq_dm)(unsigned int minor_id)),
+ void (*put_ddev)(struct drm_device *dev));
+
+void drmcg_unbind(void);
+
+void drmcg_register_dev(struct drm_device *dev);
+
+void drmcg_unregister_dev(struct drm_device *dev);
+
+#else
+
+static inline void drmcg_bind(
+ struct drm_minor (*(*acq_dm)(unsigned int minor_id)),
+ void (*put_ddev)(struct drm_device *dev))
+{
+}
+
+static inline void drmcg_unbind(void)
+{
+}
+
+static inline void drmcg_register_dev(struct drm_device *dev)
+{
+}
+
+static inline void drmcg_unregister_dev(struct drm_device *dev)
+{
+}
+
+#endif /* CONFIG_CGROUP_DRM */
+#endif /* __DRM_CGROUP_H__ */
@@ -5,6 +5,10 @@
#define _CGROUP_DRM_H
#include <linux/cgroup.h>
+#include <drm/drm_file.h>
+
+/* limit defined per the way drm_minor_alloc operates */
+#define MAX_DRM_DEV (64 * DRM_MINOR_RENDER)
#ifdef CONFIG_CGROUP_DRM
@@ -1,11 +1,142 @@
// SPDX-License-Identifier: MIT
// Copyright 2019 Advanced Micro Devices, Inc.
+#include <linux/bitmap.h>
+#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/cgroup.h>
#include <linux/cgroup_drm.h>
+#include <drm/drm_file.h>
+#include <drm/drm_device.h>
+#include <drm/drm_cgroup.h>
static struct drmcg *root_drmcg __read_mostly;
+/* global mutex for drmcg across all devices */
+static DEFINE_MUTEX(drmcg_mutex);
+
+static DECLARE_BITMAP(known_devs, MAX_DRM_DEV);
+
+static struct drm_minor (*(*acquire_drm_minor)(unsigned int minor_id));
+
+static void (*put_drm_dev)(struct drm_device *dev);
+
+/**
+ * drmcg_bind - Bind DRM subsystem to cgroup subsystem
+ * @acq_dm: function pointer to the drm_minor_acquire function
+ * @put_ddev: function pointer to the drm_dev_put function
+ *
+ * This function binds some functions from the DRM subsystem and make
+ * them available to the drmcg subsystem.
+ *
+ * drmcg_unbind does the opposite of this function
+ */
+void drmcg_bind(struct drm_minor (*(*acq_dm)(unsigned int minor_id)),
+ void (*put_ddev)(struct drm_device *dev))
+{
+ mutex_lock(&drmcg_mutex);
+ acquire_drm_minor = acq_dm;
+ put_drm_dev = put_ddev;
+ mutex_unlock(&drmcg_mutex);
+}
+EXPORT_SYMBOL(drmcg_bind);
+
+/**
+ * drmcg_unbind - Unbind DRM subsystem from cgroup subsystem
+ *
+ * drmcg_bind does the opposite of this function
+ */
+void drmcg_unbind(void)
+{
+ mutex_lock(&drmcg_mutex);
+ acquire_drm_minor = NULL;
+ put_drm_dev = NULL;
+ mutex_unlock(&drmcg_mutex);
+}
+EXPORT_SYMBOL(drmcg_unbind);
+
+/**
+ * drmcg_register_dev - register a DRM device for usage in drm cgroup
+ * @dev: DRM device
+ *
+ * This function make a DRM device visible to the cgroup subsystem.
+ * Once the drmcg is aware of the device, drmcg can start tracking and
+ * control resource usage for said device.
+ *
+ * drmcg_unregister_dev reverse the operation of this function
+ */
+void drmcg_register_dev(struct drm_device *dev)
+{
+ if (WARN_ON(dev->primary->index >= MAX_DRM_DEV))
+ return;
+
+ mutex_lock(&drmcg_mutex);
+ set_bit(dev->primary->index, known_devs);
+ mutex_unlock(&drmcg_mutex);
+}
+EXPORT_SYMBOL(drmcg_register_dev);
+
+/**
+ * drmcg_unregister_dev - Iterate through all stored DRM minors
+ * @dev: DRM device
+ *
+ * Unregister @dev so that drmcg no longer control resource usage
+ * of @dev. The @dev was registered to drmcg using
+ * drmcg_register_dev function
+ */
+void drmcg_unregister_dev(struct drm_device *dev)
+{
+ if (WARN_ON(dev->primary->index >= MAX_DRM_DEV))
+ return;
+
+ mutex_lock(&drmcg_mutex);
+ clear_bit(dev->primary->index, known_devs);
+ mutex_unlock(&drmcg_mutex);
+}
+EXPORT_SYMBOL(drmcg_unregister_dev);
+
+/**
+ * drm_minor_for_each - Iterate through all stored DRM minors
+ * @fn: Function to be called for each pointer.
+ * @data: Data passed to callback function.
+ *
+ * The callback function will be called for each registered device, passing
+ * the minor, the @drm_minor entry and @data.
+ *
+ * If @fn returns anything other than %0, the iteration stops and that
+ * value is returned from this function.
+ */
+static int drm_minor_for_each(int (*fn)(int id, void *p, void *data),
+ void *data)
+{
+ int rc = 0;
+
+ mutex_lock(&drmcg_mutex);
+ if (acquire_drm_minor) {
+ unsigned int minor;
+ struct drm_minor *dm;
+
+ minor = find_next_bit(known_devs, MAX_DRM_DEV, 0);
+ while (minor < MAX_DRM_DEV) {
+ dm = acquire_drm_minor(minor);
+
+ if (IS_ERR(dm))
+ continue;
+
+ rc = fn(minor, (void *)dm, data);
+
+ put_drm_dev(dm->dev); /* release from acquire_drm_minor */
+
+ if (rc)
+ break;
+
+ minor = find_next_bit(known_devs, MAX_DRM_DEV, minor+1);
+ }
+ }
+ mutex_unlock(&drmcg_mutex);
+
+ return rc;
+}
+
static void drmcg_css_free(struct cgroup_subsys_state *css)
{
struct drmcg *drmcg = css_to_drmcg(css);