Most DRM drivers will want to handle the CGROUP_SETPARAM ioctl by looking up a
driver-specific per-cgroup data structure (or allocating a new one) and storing
the supplied parameter value into the data structure (possibly after doing some
checking and sanitization on the provided value).  Let's provide a helper
library for drivers that will take care of the details of storing per-cgroup
data structures in a hashtable and destroying those structures if/when the
cgroup itself is removed.

Cc: Tejun Heo <t...@kernel.org>
Cc: cgro...@vger.kernel.org
Signed-off-by: Matt Roper <matthew.d.ro...@intel.com>
---
 drivers/gpu/drm/Makefile            |   1 +
 drivers/gpu/drm/drm_cgroup_helper.c | 244 ++++++++++++++++++++++++++++++++++++
 include/drm/drm_cgroup_helper.h     | 153 ++++++++++++++++++++++
 include/drm/drm_device.h            |   5 +
 4 files changed, 403 insertions(+)
 create mode 100644 drivers/gpu/drm/drm_cgroup_helper.c
 create mode 100644 include/drm/drm_cgroup_helper.h

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 351f3817bc27..45cd58d66658 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -31,6 +31,7 @@ drm-$(CONFIG_AGP) += drm_agpsupport.o
 drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o
 drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
 drm-$(CONFIG_CGROUPS) += drm_cgroup.o
+drm-$(CONFIG_CGROUPS) += drm_cgroup_helper.o
 
 drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
                drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
diff --git a/drivers/gpu/drm/drm_cgroup_helper.c 
b/drivers/gpu/drm/drm_cgroup_helper.c
new file mode 100644
index 000000000000..b62843456f63
--- /dev/null
+++ b/drivers/gpu/drm/drm_cgroup_helper.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_cgroup_helper.h>
+
+/**
+ * DOC: cgroup helper library
+ *
+ * This helper library provides implementations for the DRM cgroup parameter
+ * entry points.  Most drivers will wish to store driver-specific data
+ * associated with individual cgroups; this helper will manage the storage
+ * and lookup of these data structures and will ensure that they are properly
+ * destroyed when the corresponding cgroup is destroyed.
+ *
+ * This helper library should be initialized by calling drm_cgrp_helper_init()
+ * and torn down (on driver unload) by calling drm_cgrp_helper_shutdown().
+ * Drivers wishing to make use of this helper library should subclass the
+ * &drm_cgroup_helper_data structure to store values for any driver-specific
+ * cgroup parameters and provide implementations of at least
+ * &drm_cgroup_helper.alloc_params, &drm_cgroup_helper.update_param, and
+ * &drm_cgroup_helper.read_param.
+ */
+
+/**
+ * drm_cgrp_helper_set_param - set parameter value for cgroup
+ * @dev: DRM device
+ * @cgrp: cgroup to set parameter for
+ * @param: parameter to set
+ * @val: value to assign to parameter
+ *
+ * Provides a default handler for the CGROUP_SETPARAM ioctl.  At this time
+ * parameters may only be set on cgroups in the v2 hierarchy.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
+ */
+int
+drm_cgrp_helper_set_param(struct drm_device *dev,
+                         struct cgroup *cgrp,
+                         uint64_t param,
+                         int64_t val)
+{
+       struct drm_cgroup_helper *helper = dev->cgroup_helper;
+       struct drm_cgroup_helper_data *data;
+       int ret;
+
+       if (WARN_ON(!helper))
+               return -EINVAL;
+
+       mutex_lock(&helper->hash_mutex);
+
+       /*
+        * Search for an existing parameter set for this cgroup and update
+        * it if found.
+        */
+       hash_for_each_possible(helper->param_hash, data, node, cgrp->id) {
+               if (data->cgroup == cgrp) {
+                       DRM_DEBUG("Updating existing data for cgroup %d\n",
+                                 cgrp->id);
+                       ret = helper->update_param(data, param, val);
+                       goto out;
+               }
+       }
+
+       /*
+        * Looks like this is the first time we've tried to set a parameter
+        * on this cgroup.  We need to allocate a new parameter storage
+        * structure.  Note that we'll still allocate the structure and
+        * associate it with the cgroup even if the setting of the specific
+        * parameter fails.
+        */
+       DRM_DEBUG("Allocating new data for cgroup %d\n", cgrp->id);
+       data = helper->alloc_params();
+       if (IS_ERR(data)) {
+               ret = PTR_ERR(data);
+               goto out;
+       }
+
+       data->dev = dev;
+       data->cgroup = cgrp;
+       hash_add(helper->param_hash, &data->node, cgrp->id);
+
+       ret = helper->update_param(data, param, val);
+
+out:
+       mutex_unlock(&helper->hash_mutex);
+       return ret;
+}
+EXPORT_SYMBOL(drm_cgrp_helper_set_param);
+
+/**
+ * drm_cgrp_helper_get_param - retrieve parameter value for cgroup
+ * @dev: DRM device
+ * @cgrp: cgroup to set parameter for
+ * @param: parameter to retrieve
+ * @val: parameter value returned by reference
+ *
+ * Helper function that drivers may call to lookup a parameter associated
+ * with a specific cgroup.
+ *
+ * RETURNS:
+ * If a parameter value is found for this cgroup, returns zero and sets
+ * 'val' to the value retrieved.  If no parameters have been explicitly
+ * set for this cgroup in the past, returns -EINVAL and does not update
+ * 'val.'  If other errors occur, a negative error code will be returned
+ * and 'val' will not be modified.
+ */
+int
+drm_cgrp_helper_get_param(struct drm_device *dev,
+                         struct cgroup *cgrp,
+                         uint64_t param,
+                         int64_t *val)
+{
+       struct drm_cgroup_helper *helper = dev->cgroup_helper;
+       struct drm_cgroup_helper_data *data;
+       int ret;
+
+       if (WARN_ON(!helper))
+               return -EINVAL;
+
+       mutex_lock(&helper->hash_mutex);
+
+       ret = -EINVAL;
+       hash_for_each_possible(helper->param_hash, data, node, cgrp->id) {
+               if (data->cgroup == cgrp) {
+                       ret = helper->read_param(data, param, val);
+                       break;
+               }
+       }
+
+       mutex_unlock(&helper->hash_mutex);
+       return ret;
+}
+EXPORT_SYMBOL(drm_cgrp_helper_get_param);
+
+/*
+ * Notifier callback for cgroup destruction.  Search for any driver-specific
+ * data associated with the cgroup and free it.
+ */
+static int
+cgrp_destroyed(struct notifier_block *nb,
+              unsigned long val,
+              void *ptr)
+{
+       struct cgroup *cgrp = ptr;
+       struct drm_cgroup_helper *helper = container_of(nb,
+                                                       struct 
drm_cgroup_helper,
+                                                       cgrp_notifier);
+       struct drm_cgroup_helper_data *data;
+       struct hlist_node *tmp;
+
+       mutex_lock(&helper->hash_mutex);
+
+       hash_for_each_possible_safe(helper->param_hash, data, tmp, node,
+                                   cgrp->id) {
+               if (data->cgroup == cgrp) {
+                       if (helper->remove_params)
+                               helper->remove_params(data);
+                       else
+                               kfree(data);
+
+                       DRM_DEBUG("Destroyed DRM parameters for cgroup %d\n",
+                                 cgrp->id);
+
+                       break;
+               }
+       }
+
+       mutex_unlock(&helper->hash_mutex);
+
+       return 0;
+}
+
+/**
+ * drm_cgrp_helper_init - initialize cgroup helper library
+ * @dev: DRM device
+ *
+ * Drivers that wish to make use of the cgroup helper library should
+ * call this function during driver load.
+ */
+void
+drm_cgrp_helper_init(struct drm_device *dev,
+                    struct drm_cgroup_helper *helper)
+{
+       dev->cgroup_helper = helper;
+       helper->dev = dev;
+
+       hash_init(helper->param_hash);
+       mutex_init(&helper->hash_mutex);
+
+       helper->cgrp_notifier.notifier_call = cgrp_destroyed;
+       blocking_notifier_chain_register(&cgroup_destroy_notifier_list,
+                                        &helper->cgrp_notifier);
+}
+EXPORT_SYMBOL(drm_cgrp_helper_init);
+
+/**
+ * drm_cgrp_helper_shutdown - tear down cgroup helper library
+ * @helper: cgroup helper structure
+ *
+ * Drivers making use of the cgroup helper library should call this function
+ * when unloaded.
+ */
+void
+drm_cgrp_helper_shutdown(struct drm_cgroup_helper *helper)
+{
+       struct drm_cgroup_helper_data *data;
+       struct hlist_node *tmp;
+       int i;
+
+       mutex_lock(&helper->hash_mutex);
+       hash_for_each_safe(helper->param_hash, i, tmp, data, node) {
+               hash_del(&data->node);
+               if (helper->remove_params)
+                       helper->remove_params(data);
+               else
+                       kfree(data);
+       }
+       mutex_unlock(&helper->hash_mutex);
+
+       blocking_notifier_chain_unregister(&cgroup_destroy_notifier_list,
+                                          &helper->cgrp_notifier);
+}
+EXPORT_SYMBOL(drm_cgrp_helper_shutdown);
diff --git a/include/drm/drm_cgroup_helper.h b/include/drm/drm_cgroup_helper.h
new file mode 100644
index 000000000000..435973fadd32
--- /dev/null
+++ b/include/drm/drm_cgroup_helper.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __DRM_CGROUP_HELPER_H__
+
+#include <linux/cgroup.h>
+#include <linux/hashtable.h>
+
+#ifdef CONFIG_CGROUPS
+
+/**
+ * struct drm_cgroup_helper_data - storage of cgroup-specific information
+ * @dev: DRM device
+ * @node: hashtable node
+ * @cgroup: individual cgroup that this structure instance is associated with
+ *
+ * Drivers should subclass this structure and add fields for all parameters
+ * that they wish to track on a per-cgroup basis.  The cgroup helper library
+ * will allocate a new instance of this structure the first time the
+ * CGROUP_SETPARAM ioctl is called for a cgroup and will destroy this structure
+ * if the corresponding cgroup is destroyed or if the DRM driver is unloaded.
+ */
+struct drm_cgroup_helper_data {
+       struct drm_device *dev;
+       struct hlist_node node;
+       struct cgroup *cgroup;
+};
+
+/**
+ * struct drm_cgroup_helper - main cgroup helper data structure
+ * @dev: DRM device
+ * @param_hash: hashtable used to store per-cgroup parameter data
+ * @hash_mutex: mutex to protect access to hash_mutex
+ * @cgrp_notifier: blocking notifier for cgroup destruction
+ */
+struct drm_cgroup_helper {
+       struct drm_device *dev;
+
+       DECLARE_HASHTABLE(param_hash, 5);
+       struct mutex hash_mutex;
+
+       struct notifier_block cgrp_notifier;
+
+       /**
+        * @alloc_params:
+        *
+        * Driver callback to allocate driver-specific parameter data
+        * associated with a single cgroup.  This callback will be called if
+        * CGROUP_SETPARAM is issued for a cgroup that does not already have
+        * driver-specific storage allocated.
+        *
+        * This callback is mandatory.
+        *
+        * RETURNS:
+        *
+        * The driver should allocate and return a driver-specific subclass
+        * of drm_cgroup_helper_data.  Returns -ENOMEM on allocation failure.
+        */
+       struct drm_cgroup_helper_data *(*alloc_params)(void);
+
+       /**
+        * @update_param:
+        *
+        * Driver callback to update a parameter's value in a specific
+        * cgroup's driver-side storage structure.
+        *
+        * This callback is mandatory.
+        *
+        * RETURNS:
+        *
+        * The driver should return 0 on success or a negative error code
+        * on failure.
+        */
+       int (*update_param)(struct drm_cgroup_helper_data *data,
+                           uint64_t param, int64_t val);
+
+       /**
+        * @read_param:
+        *
+        * Driver callback to read a parameter's value from a specific
+        * cgroup's driver-side storage structure.  If successful, the
+        * parameter val will be updated with the appropriate value.
+        *
+        * This callback is mandatory.
+        *
+        * RETURNS:
+        *
+        * The driver should return 0 on success or a negative error code
+        * on failure.
+        */
+       int (*read_param)(struct drm_cgroup_helper_data *data,
+                         uint64_t param, int64_t *val);
+
+       /**
+        * @remove_params:
+        *
+        * Driver callback to reap the driver-specific data structure
+        * after the corresponding cgroup has been removed.
+        *
+        * This callback is optional.  If not provided, the helper library
+        * will call kfree() on the driver-specific structure.
+        */
+       void (*remove_params)(struct drm_cgroup_helper_data *data);
+};
+
+
+void drm_cgrp_helper_init(struct drm_device *dev,
+                         struct drm_cgroup_helper *helper);
+void drm_cgrp_helper_shutdown(struct drm_cgroup_helper *helper);
+int drm_cgrp_helper_set_param(struct drm_device *dev,
+                             struct cgroup *cgrp,
+                             uint64_t param,
+                             int64_t val);
+int drm_cgrp_helper_get_param(struct drm_device *dev,
+                             struct cgroup *cgrp,
+                             uint64_t param,
+                             int64_t *val);
+
+#else /* CGROUPS */
+
+void drm_cgrp_helper_init(struct drm_device *dev) {}
+void drm_cgrp_helper_shutdown(struct drm_device *dev) {}
+int drm_cgrp_helper_set_param(struct drm_device *dev,
+                             struct cgroup *cgrp,
+                             uint64_t param,
+                             int64_t val) { return -EINVAL; }
+int drm_cgrp_helper_get_param(struct drm_device *dev,
+                             struct cgroup *cgrp,
+                             uint64_t param,
+                             int64_t *val) { return -EINVAL; }
+
+#endif
+
+#endif
diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h
index 6eee7bbee602..609c1528d681 100644
--- a/include/drm/drm_device.h
+++ b/include/drm/drm_device.h
@@ -201,6 +201,11 @@ struct drm_device {
         * @cgroup: Per-driver cgroup handlers.
         */
        struct drm_cgroup_funcs *cgroup;
+
+       /**
+        * @cgrp_helper: cgroup helper handlers and data
+        */
+       struct drm_cgroup_helper *cgroup_helper;
 #endif /* CONFIG_CGROUPS */
 };
 
-- 
2.14.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to