| 1 | // SPDX-License-Identifier: MIT | 
|---|
| 2 | /* | 
|---|
| 3 | * Copyright © 2014-2016 Intel Corporation | 
|---|
| 4 | */ | 
|---|
| 5 |  | 
|---|
| 6 | #include <linux/jiffies.h> | 
|---|
| 7 |  | 
|---|
| 8 | #include <drm/drm_file.h> | 
|---|
| 9 |  | 
|---|
| 10 | #include "i915_drv.h" | 
|---|
| 11 | #include "i915_file_private.h" | 
|---|
| 12 | #include "i915_gem_context.h" | 
|---|
| 13 | #include "i915_gem_ioctls.h" | 
|---|
| 14 | #include "i915_gem_object.h" | 
|---|
| 15 |  | 
|---|
| 16 | /* | 
|---|
| 17 | * 20ms is a fairly arbitrary limit (greater than the average frame time) | 
|---|
| 18 | * chosen to prevent the CPU getting more than a frame ahead of the GPU | 
|---|
| 19 | * (when using lax throttling for the frontbuffer). We also use it to | 
|---|
| 20 | * offer free GPU waitboosts for severely congested workloads. | 
|---|
| 21 | */ | 
|---|
| 22 | #define DRM_I915_THROTTLE_JIFFIES msecs_to_jiffies(20) | 
|---|
| 23 |  | 
|---|
| 24 | /* | 
|---|
| 25 | * Throttle our rendering by waiting until the ring has completed our requests | 
|---|
| 26 | * emitted over 20 msec ago. | 
|---|
| 27 | * | 
|---|
| 28 | * Note that if we were to use the current jiffies each time around the loop, | 
|---|
| 29 | * we wouldn't escape the function with any frames outstanding if the time to | 
|---|
| 30 | * render a frame was over 20ms. | 
|---|
| 31 | * | 
|---|
| 32 | * This should get us reasonable parallelism between CPU and GPU but also | 
|---|
| 33 | * relatively low latency when blocking on a particular request to finish. | 
|---|
| 34 | */ | 
|---|
| 35 | int | 
|---|
| 36 | i915_gem_throttle_ioctl(struct drm_device *dev, void *data, | 
|---|
| 37 | struct drm_file *file) | 
|---|
| 38 | { | 
|---|
| 39 | const unsigned long recent_enough = jiffies - DRM_I915_THROTTLE_JIFFIES; | 
|---|
| 40 | struct drm_i915_file_private *file_priv = file->driver_priv; | 
|---|
| 41 | struct drm_i915_private *i915 = to_i915(dev); | 
|---|
| 42 | struct i915_gem_context *ctx; | 
|---|
| 43 | unsigned long idx; | 
|---|
| 44 | long ret; | 
|---|
| 45 |  | 
|---|
| 46 | /* ABI: return -EIO if already wedged */ | 
|---|
| 47 | ret = intel_gt_terminally_wedged(gt: to_gt(i915)); | 
|---|
| 48 | if (ret) | 
|---|
| 49 | return ret; | 
|---|
| 50 |  | 
|---|
| 51 | rcu_read_lock(); | 
|---|
| 52 | xa_for_each(&file_priv->context_xa, idx, ctx) { | 
|---|
| 53 | struct i915_gem_engines_iter it; | 
|---|
| 54 | struct intel_context *ce; | 
|---|
| 55 |  | 
|---|
| 56 | if (!kref_get_unless_zero(kref: &ctx->ref)) | 
|---|
| 57 | continue; | 
|---|
| 58 | rcu_read_unlock(); | 
|---|
| 59 |  | 
|---|
| 60 | for_each_gem_engine(ce, | 
|---|
| 61 | i915_gem_context_lock_engines(ctx), | 
|---|
| 62 | it) { | 
|---|
| 63 | struct i915_request *rq, *target = NULL; | 
|---|
| 64 |  | 
|---|
| 65 | if (!ce->timeline) | 
|---|
| 66 | continue; | 
|---|
| 67 |  | 
|---|
| 68 | mutex_lock(lock: &ce->timeline->mutex); | 
|---|
| 69 | list_for_each_entry_reverse(rq, | 
|---|
| 70 | &ce->timeline->requests, | 
|---|
| 71 | link) { | 
|---|
| 72 | if (i915_request_completed(rq)) | 
|---|
| 73 | break; | 
|---|
| 74 |  | 
|---|
| 75 | if (time_after(rq->emitted_jiffies, | 
|---|
| 76 | recent_enough)) | 
|---|
| 77 | continue; | 
|---|
| 78 |  | 
|---|
| 79 | target = i915_request_get(rq); | 
|---|
| 80 | break; | 
|---|
| 81 | } | 
|---|
| 82 | mutex_unlock(lock: &ce->timeline->mutex); | 
|---|
| 83 | if (!target) | 
|---|
| 84 | continue; | 
|---|
| 85 |  | 
|---|
| 86 | ret = i915_request_wait(rq: target, | 
|---|
| 87 | I915_WAIT_INTERRUPTIBLE, | 
|---|
| 88 | MAX_SCHEDULE_TIMEOUT); | 
|---|
| 89 | i915_request_put(rq: target); | 
|---|
| 90 | if (ret < 0) | 
|---|
| 91 | break; | 
|---|
| 92 | } | 
|---|
| 93 | i915_gem_context_unlock_engines(ctx); | 
|---|
| 94 | i915_gem_context_put(ctx); | 
|---|
| 95 |  | 
|---|
| 96 | rcu_read_lock(); | 
|---|
| 97 | } | 
|---|
| 98 | rcu_read_unlock(); | 
|---|
| 99 |  | 
|---|
| 100 | return ret < 0 ? ret : 0; | 
|---|
| 101 | } | 
|---|
| 102 |  | 
|---|