@@ -8,6 +8,7 @@
#include <linux/dma-resv.h>
#include <linux/highmem.h>
#include <linux/minmax.h>
+#include <linux/prandom.h>
#include <linux/sync_file.h>
#include <linux/uaccess.h>
@@ -3018,15 +3019,32 @@ static void retire_requests(struct intel_timeline *tl, struct i915_request *end)
}
#ifdef CONFIG_CGROUP_DRM
+static unsigned int
+__get_class(struct drm_i915_file_private *fpriv, const struct i915_request *rq)
+{
+ unsigned int class;
+
+ class = rq->context->engine->uabi_class;
+
+ if (WARN_ON_ONCE(class >= ARRAY_SIZE(fpriv->client->throttle)))
+ class = 0;
+
+ return class;
+}
+
static void copy_priority(struct i915_sched_attr *attr,
- const struct i915_execbuffer *eb)
+ const struct i915_execbuffer *eb,
+ const struct i915_request *rq)
{
+ struct drm_i915_file_private *file_priv = eb->file->driver_priv;
const int scale = DIV_ROUND_CLOSEST(DRM_CGROUP_PRIORITY_MAX,
I915_CONTEXT_MAX_USER_PRIORITY);
int prio;
*attr = eb->gem_context->sched;
prio = attr->priority * scale + eb->file->drm_cgroup_priority;
+ if (file_priv->client->throttle[__get_class(file_priv, rq)])
+ prio -= 1 + prandom_u32_max(-DRM_CGROUP_PRIORITY_MIN / 2);
prio = DIV_ROUND_UP(prio, scale);
attr->priority = clamp(prio,
I915_CONTEXT_MIN_USER_PRIORITY,
@@ -3056,7 +3074,7 @@ static int eb_request_add(struct i915_execbuffer *eb, struct i915_request *rq,
/* Check that the context wasn't destroyed before submission */
if (likely(!intel_context_is_closed(eb->context))) {
- copy_priority(&attr, eb);
+ copy_priority(&attr, eb, rq);
} else {
/* Serialise with context_close via the add_to_timeline */
i915_request_set_error_once(rq, -ENOENT);
@@ -1897,6 +1897,7 @@ static const struct drm_ioctl_desc i915_ioctls[] = {
static const struct drm_cgroup_ops i915_drm_cgroup_ops = {
.priority_levels = i915_drm_priority_levels,
.active_time_us = i915_drm_cgroup_get_active_time_us,
+ .signal_budget = i915_drm_cgroup_signal_budget,
};
#endif
@@ -4,6 +4,7 @@
*/
#include <linux/kernel.h>
+#include <linux/ktime.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -173,6 +174,98 @@ u64 i915_drm_cgroup_get_active_time_us(struct drm_file *file)
return busy;
}
+
+int i915_drm_cgroup_signal_budget(struct drm_file *file, u64 usage, u64 budget)
+{
+ struct drm_i915_file_private *fpriv = file->driver_priv;
+ u64 class_usage[I915_LAST_UABI_ENGINE_CLASS + 1];
+ u64 class_last[I915_LAST_UABI_ENGINE_CLASS + 1];
+ struct drm_i915_private *i915 = fpriv->dev_priv;
+ struct i915_drm_client *client = fpriv->client;
+ struct intel_engine_cs *engine;
+ bool over = usage > budget;
+ unsigned int i;
+ ktime_t unused;
+ int ret = 0;
+ u64 t;
+
+ if (!supports_stats(i915))
+ return -EINVAL;
+
+ if (usage == 0 && budget == 0)
+ return 0;
+
+printk("i915_drm_cgroup_signal_budget client-id=%u over=%u (%llu/%llu) <%u>\n",
+ client->id, over, usage, budget, client->over_budget);
+
+ if (over) {
+ client->over_budget++;
+ if (!client->over_budget)
+ client->over_budget = 2;
+ } else {
+ client->over_budget = 0;
+ memset(client->class_last, 0, sizeof(client->class_last));
+ memset(client->throttle, 0, sizeof(client->throttle));
+ return 0;
+ }
+
+ memset(class_usage, 0, sizeof(class_usage));
+ for_each_uabi_engine(engine, i915)
+ class_usage[engine->uabi_class] +=
+ ktime_to_ns(intel_engine_get_busy_time(engine, &unused));
+
+ memcpy(class_last, client->class_last, sizeof(class_last));
+ memcpy(client->class_last, class_usage, sizeof(class_last));
+
+ for (i = 0; i < ARRAY_SIZE(uabi_class_names); i++)
+ class_usage[i] -= class_last[i];
+
+ t = client->last;
+ client->last = ktime_get_raw_ns();
+ t = client->last - t;
+
+ if (client->over_budget == 1)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(uabi_class_names); i++) {
+ u64 client_class_usage[I915_LAST_UABI_ENGINE_CLASS + 1];
+ unsigned int capacity;
+
+ if (!i915->engine_uabi_class_count[i])
+ continue;
+
+ t = DIV_ROUND_UP_ULL(t, 1000);
+ class_usage[i] = DIV_ROUND_CLOSEST_ULL(class_usage[i], 1000);
+ usage = DIV_ROUND_CLOSEST_ULL(class_usage[i] * 100ULL,
+ t *
+ i915->engine_uabi_class_count[i]);
+ if (usage <= 95) {
+ /* class not oversubsribed */
+ if (client->throttle[i]) {
+ client->throttle[i] = false;
+printk(" UN-throttling class%u (phys=%lld%%)\n",
+ i, usage);
+ }
+ continue;
+ }
+
+ client_class_usage[i] =
+ get_class_active_ns(client, i, &capacity);
+
+ if (client_class_usage[i] && !client->throttle[i]) {
+ ret |= 1;
+ client->throttle[i] = true;
+ /*
+ * QQQ maybe apply "strength" of throttling based on
+ * usage/budget?
+ */
+printk(" THROTTLING class%u (phys=%lld%% client=%lluus)\n",
+ i, usage, client_class_usage[i] / 1000);
+ }
+ }
+
+ return ret;
+}
#endif
#ifdef CONFIG_PROC_FS
@@ -40,6 +40,13 @@ struct i915_drm_client {
* @past_runtime: Accumulation of pphwsp runtimes from closed contexts.
*/
atomic64_t past_runtime[I915_LAST_UABI_ENGINE_CLASS + 1];
+
+#ifdef CONFIG_CGROUP_DRM
+ bool throttle[I915_LAST_UABI_ENGINE_CLASS + 1];
+ unsigned int over_budget;
+ u64 last;
+ u64 class_last[I915_LAST_UABI_ENGINE_CLASS + 1];
+#endif
};
void i915_drm_clients_init(struct i915_drm_clients *clients,
@@ -70,5 +77,7 @@ void i915_drm_clients_fini(struct i915_drm_clients *clients);
unsigned int i915_drm_priority_levels(struct drm_file *file);
u64 i915_drm_cgroup_get_active_time_us(struct drm_file *file);
+int i915_drm_cgroup_signal_budget(struct drm_file *file,
+ u64 usage, u64 budget);
#endif /* !__I915_DRM_CLIENT_H__ */