@@ -2398,6 +2398,64 @@ HugeTLB Interface Files
hugetlb pages of <hugepagesize> in this cgroup. Only active in
use hugetlb pages are included. The per-node values are in bytes.
+DRM
+---
+
+The DRM controller allows configuring static hierarchical scheduling priority.
+
+DRM static priority control
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Static priority control exposes a hierarchical control interface for the
+scheduling priority support present in many DRM device drivers.
+
+Hierarchical meaning that the child group priorities are relative to their
+parent. As an example:
+
+ A=-1000
+ /\
+ / \
+ / \
+ B=0 C=100
+
+This results in the effective priority of a group B of -1000 and C of -900. In
+other words the fact C is configured for elevated priority is relative to its
+parent being a low priority and hence is only elevated in the context of its
+siblings.
+
+The scope of individual DRM scheduling priority may be per device or per device
+driver, or a combination of both, depending on the implementation. The
+controller does not ensure any priority ordering across multiple DRM drivers nor
+does it impose any further policy and leaves desired configuration to the system
+administrator.
+
+Individual DRM drivers are required to transparently convert the cgroup priority
+into values suitable for their capabilities.
+
+No guarantees on effectiveness or granularity are provided by the controller,
+apart the available range being chosen to be an integer and hence allowing a
+generic concept of normal (zero), lower (negative values) and higher (positive
+values) priority.
+
+DRM static priority interface files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ drm.priority_levels
+ One of:
+ 1) And integer representing the minimum number of discrete priority
+ levels for the whole group.
+ 2) '0'- indicating one or more DRM clients in the group has no support
+ for static priority control.
+ 3) 'n/a' - when there are no DRM clients in the configured group.
+
+ drm.priority
+ A read-write integer between -10000 and 10000 (inclusive) representing
+ an abstract static priority level.
+
+ drm.effective_priority
+ Read only integer showing the current effective priority level for the
+ group. Effective meaning taking into account the chain of inherited
+
Misc
----
@@ -6,4 +6,8 @@
#ifndef _CGROUP_DRM_H
#define _CGROUP_DRM_H
+#define DRM_CGROUP_PRIORITY_MIN (-10000)
+#define DRM_CGROUP_PRIORITY_DEF (0)
+#define DRM_CGROUP_PRIORITY_MAX (10000)
+
#endif /* _CGROUP_DRM_H */
@@ -6,24 +6,117 @@
#include <linux/slab.h>
#include <linux/cgroup.h>
#include <linux/cgroup_drm.h>
+#include <linux/minmax.h>
+#include <linux/mutex.h>
#include <linux/sched.h>
struct drm_cgroup_state {
struct cgroup_subsys_state css;
+
+ int priority;
+ int effective_priority;
};
+static DEFINE_MUTEX(drmcg_mutex);
+
static inline struct drm_cgroup_state *
css_to_drmcs(struct cgroup_subsys_state *css)
{
return container_of(css, struct drm_cgroup_state, css);
}
+static int drmcs_show_priority_levels(struct seq_file *sf, void *v)
+{
+ seq_printf(sf, "%u\n", 0);
+
+ return 0;
+}
+
+static s64
+drmcs_read_effective_priority(struct cgroup_subsys_state *css,
+ struct cftype *cft)
+{
+ struct drm_cgroup_state *drmcs = css_to_drmcs(css);
+
+ return drmcs->effective_priority;
+}
+
+static s64
+drmcs_read_priority(struct cgroup_subsys_state *css, struct cftype *cft)
+{
+ struct drm_cgroup_state *drmcs = css_to_drmcs(css);
+
+ return drmcs->priority;
+}
+
+static void update_priority(struct drm_cgroup_state *drmcs, int priority)
+{
+ struct cgroup_subsys_state *node;
+
+ lockdep_assert_held(&drmcg_mutex);
+
+ if (priority == drmcs->priority)
+ return;
+
+ drmcs->priority = priority;
+
+ rcu_read_lock();
+ css_for_each_descendant_pre(node, &drmcs->css) {
+ struct drm_cgroup_state *dnode = css_to_drmcs(node);
+ int pprio;
+
+ if (!node->parent)
+ pprio = DRM_CGROUP_PRIORITY_DEF;
+ else
+ pprio = css_to_drmcs(node->parent)->effective_priority;
+
+ dnode->effective_priority =
+ clamp(pprio + dnode->priority,
+ DRM_CGROUP_PRIORITY_MIN,
+ DRM_CGROUP_PRIORITY_MAX);
+ }
+ rcu_read_unlock();
+}
+
+static int
+drmcs_write_priority(struct cgroup_subsys_state *css, struct cftype *cftype,
+ s64 priority)
+{
+ struct drm_cgroup_state *drmcs = css_to_drmcs(css);
+ int ret;
+
+ if (priority < (s64)DRM_CGROUP_PRIORITY_MIN ||
+ priority > (s64)DRM_CGROUP_PRIORITY_MAX)
+ return -ERANGE;
+
+ ret = mutex_lock_interruptible(&drmcg_mutex);
+ if (ret)
+ return ret;
+ update_priority(drmcs, (int)priority);
+ mutex_unlock(&drmcg_mutex);
+
+ return 0;
+}
+
+static int drmcs_online(struct cgroup_subsys_state *css)
+{
+ struct drm_cgroup_state *drmcs = css_to_drmcs(css);
+
+ mutex_lock(&drmcg_mutex);
+ update_priority(drmcs, DRM_CGROUP_PRIORITY_DEF);
+ mutex_unlock(&drmcg_mutex);
+
+ return 0;
+}
+
static void drmcs_free(struct cgroup_subsys_state *css)
{
kfree(css_to_drmcs(css));
}
static struct drm_cgroup_state root_drmcs = {
+ .priority = DRM_CGROUP_PRIORITY_DEF,
+ .effective_priority = DRM_CGROUP_PRIORITY_DEF,
};
static struct cgroup_subsys_state *
@@ -42,12 +135,29 @@ drmcs_alloc(struct cgroup_subsys_state *parent_css)
}
struct cftype files[] = {
+ {
+ .name = "priority_levels",
+ .flags = CFTYPE_NOT_ON_ROOT,
+ .seq_show = drmcs_show_priority_levels,
+ },
+ {
+ .name = "priority",
+ .flags = CFTYPE_NOT_ON_ROOT,
+ .read_s64 = drmcs_read_priority,
+ .write_s64 = drmcs_write_priority,
+ },
+ {
+ .name = "effective_priority",
+ .flags = CFTYPE_NOT_ON_ROOT,
+ .read_s64 = drmcs_read_effective_priority,
+ },
{ } /* Zero entry terminates. */
};
struct cgroup_subsys drm_cgrp_subsys = {
.css_alloc = drmcs_alloc,
.css_free = drmcs_free,
+ .css_online = drmcs_online,
.early_init = false,
.legacy_cftypes = files,
.dfl_cftypes = files,