| File: | dev/pci/drm/i915/gt/intel_engine_heartbeat.c |
| Warning: | line 141, column 5 Access to field 'emitted_jiffies' results in a dereference of a null pointer (loaded from variable 'rq') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // SPDX-License-Identifier: MIT | |||
| 2 | /* | |||
| 3 | * Copyright © 2019 Intel Corporation | |||
| 4 | */ | |||
| 5 | ||||
| 6 | #include "i915_drv.h" | |||
| 7 | #include "i915_request.h" | |||
| 8 | ||||
| 9 | #include "intel_context.h" | |||
| 10 | #include "intel_engine_heartbeat.h" | |||
| 11 | #include "intel_engine_pm.h" | |||
| 12 | #include "intel_engine.h" | |||
| 13 | #include "intel_gt.h" | |||
| 14 | #include "intel_reset.h" | |||
| 15 | ||||
| 16 | /* | |||
| 17 | * While the engine is active, we send a periodic pulse along the engine | |||
| 18 | * to check on its health and to flush any idle-barriers. If that request | |||
| 19 | * is stuck, and we fail to preempt it, we declare the engine hung and | |||
| 20 | * issue a reset -- in the hope that restores progress. | |||
| 21 | */ | |||
| 22 | ||||
| 23 | static bool_Bool next_heartbeat(struct intel_engine_cs *engine) | |||
| 24 | { | |||
| 25 | long delay; | |||
| 26 | ||||
| 27 | delay = READ_ONCE(engine->props.heartbeat_interval_ms)({ typeof(engine->props.heartbeat_interval_ms) __tmp = *(volatile typeof(engine->props.heartbeat_interval_ms) *)&(engine ->props.heartbeat_interval_ms); membar_datadep_consumer(); __tmp; }); | |||
| 28 | if (!delay) | |||
| 29 | return false0; | |||
| 30 | ||||
| 31 | delay = msecs_to_jiffies_timeout(delay); | |||
| 32 | if (delay >= HZhz) | |||
| 33 | delay = round_jiffies_up_relative(delay); | |||
| 34 | mod_delayed_work(system_highpri_wq, &engine->heartbeat.work, delay + 1); | |||
| 35 | ||||
| 36 | return true1; | |||
| 37 | } | |||
| 38 | ||||
| 39 | static struct i915_request * | |||
| 40 | heartbeat_create(struct intel_context *ce, gfp_t gfp) | |||
| 41 | { | |||
| 42 | struct i915_request *rq; | |||
| 43 | ||||
| 44 | intel_context_enter(ce); | |||
| 45 | rq = __i915_request_create(ce, gfp); | |||
| 46 | intel_context_exit(ce); | |||
| 47 | ||||
| 48 | return rq; | |||
| 49 | } | |||
| 50 | ||||
| 51 | static void idle_pulse(struct intel_engine_cs *engine, struct i915_request *rq) | |||
| 52 | { | |||
| 53 | engine->wakeref_serial = READ_ONCE(engine->serial)({ typeof(engine->serial) __tmp = *(volatile typeof(engine ->serial) *)&(engine->serial); membar_datadep_consumer (); __tmp; }) + 1; | |||
| 54 | i915_request_add_active_barriers(rq); | |||
| 55 | if (!engine->heartbeat.systole && intel_engine_has_heartbeat(engine)) | |||
| 56 | engine->heartbeat.systole = i915_request_get(rq); | |||
| 57 | } | |||
| 58 | ||||
| 59 | static void heartbeat_commit(struct i915_request *rq, | |||
| 60 | const struct i915_sched_attr *attr) | |||
| 61 | { | |||
| 62 | idle_pulse(rq->engine, rq); | |||
| 63 | ||||
| 64 | __i915_request_commit(rq); | |||
| 65 | __i915_request_queue(rq, attr); | |||
| 66 | } | |||
| 67 | ||||
| 68 | static void show_heartbeat(const struct i915_request *rq, | |||
| 69 | struct intel_engine_cs *engine) | |||
| 70 | { | |||
| 71 | struct drm_printer p = drm_debug_printer("heartbeat"); | |||
| 72 | ||||
| 73 | if (!rq) { | |||
| 74 | intel_engine_dump(engine, &p, | |||
| 75 | "%s heartbeat not ticking\n", | |||
| 76 | engine->name); | |||
| 77 | } else { | |||
| 78 | intel_engine_dump(engine, &p, | |||
| 79 | "%s heartbeat {seqno:%llx:%lld, prio:%d} not ticking\n", | |||
| 80 | engine->name, | |||
| 81 | rq->fence.context, | |||
| 82 | rq->fence.seqno, | |||
| 83 | rq->sched.attr.priority); | |||
| 84 | } | |||
| 85 | } | |||
| 86 | ||||
| 87 | static void | |||
| 88 | reset_engine(struct intel_engine_cs *engine, struct i915_request *rq) | |||
| 89 | { | |||
| 90 | if (IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)0) | |||
| 91 | show_heartbeat(rq, engine); | |||
| 92 | ||||
| 93 | if (intel_engine_uses_guc(engine)) | |||
| 94 | /* | |||
| 95 | * GuC itself is toast or GuC's hang detection | |||
| 96 | * is disabled. Either way, need to find the | |||
| 97 | * hang culprit manually. | |||
| 98 | */ | |||
| 99 | intel_guc_find_hung_context(engine); | |||
| 100 | ||||
| 101 | intel_gt_handle_error(engine->gt, engine->mask, | |||
| 102 | I915_ERROR_CAPTURE(1UL << (0)), | |||
| 103 | "stopped heartbeat on %s", | |||
| 104 | engine->name); | |||
| 105 | } | |||
| 106 | ||||
| 107 | static void heartbeat(struct work_struct *wrk) | |||
| 108 | { | |||
| 109 | struct i915_sched_attr attr = { .priority = I915_PRIORITY_MIN }; | |||
| 110 | struct intel_engine_cs *engine = | |||
| 111 | container_of(wrk, typeof(*engine), heartbeat.work.work)({ const __typeof( ((typeof(*engine) *)0)->heartbeat.work. work ) *__mptr = (wrk); (typeof(*engine) *)( (char *)__mptr - __builtin_offsetof(typeof(*engine), heartbeat.work.work) );} ); | |||
| 112 | struct intel_context *ce = engine->kernel_context; | |||
| 113 | struct i915_request *rq; | |||
| 114 | unsigned long serial; | |||
| 115 | ||||
| 116 | /* Just in case everything has gone horribly wrong, give it a kick */ | |||
| 117 | intel_engine_flush_submission(engine); | |||
| ||||
| 118 | ||||
| 119 | rq = engine->heartbeat.systole; | |||
| 120 | if (rq && i915_request_completed(rq)) { | |||
| 121 | i915_request_put(rq); | |||
| 122 | engine->heartbeat.systole = NULL((void *)0); | |||
| 123 | } | |||
| 124 | ||||
| 125 | if (!intel_engine_pm_get_if_awake(engine)) | |||
| 126 | return; | |||
| 127 | ||||
| 128 | if (intel_gt_is_wedged(engine->gt)) | |||
| 129 | goto out; | |||
| 130 | ||||
| 131 | if (i915_sched_engine_disabled(engine->sched_engine)) { | |||
| 132 | reset_engine(engine, engine->heartbeat.systole); | |||
| 133 | goto out; | |||
| 134 | } | |||
| 135 | ||||
| 136 | if (engine->heartbeat.systole) { | |||
| 137 | long delay = READ_ONCE(engine->props.heartbeat_interval_ms)({ typeof(engine->props.heartbeat_interval_ms) __tmp = *(volatile typeof(engine->props.heartbeat_interval_ms) *)&(engine ->props.heartbeat_interval_ms); membar_datadep_consumer(); __tmp; }); | |||
| 138 | ||||
| 139 | /* Safeguard against too-fast worker invocations */ | |||
| 140 | if (!time_after(jiffies, | |||
| 141 | rq->emitted_jiffies + msecs_to_jiffies(delay)(((uint64_t)(delay)) * hz / 1000))) | |||
| ||||
| 142 | goto out; | |||
| 143 | ||||
| 144 | if (!i915_sw_fence_signaled(&rq->submit)) { | |||
| 145 | /* | |||
| 146 | * Not yet submitted, system is stalled. | |||
| 147 | * | |||
| 148 | * This more often happens for ring submission, | |||
| 149 | * where all contexts are funnelled into a common | |||
| 150 | * ringbuffer. If one context is blocked on an | |||
| 151 | * external fence, not only is it not submitted, | |||
| 152 | * but all other contexts, including the kernel | |||
| 153 | * context are stuck waiting for the signal. | |||
| 154 | */ | |||
| 155 | } else if (engine->sched_engine->schedule && | |||
| 156 | rq->sched.attr.priority < I915_PRIORITY_BARRIER(0x7fffffff - 1)) { | |||
| 157 | /* | |||
| 158 | * Gradually raise the priority of the heartbeat to | |||
| 159 | * give high priority work [which presumably desires | |||
| 160 | * low latency and no jitter] the chance to naturally | |||
| 161 | * complete before being preempted. | |||
| 162 | */ | |||
| 163 | attr.priority = 0; | |||
| 164 | if (rq->sched.attr.priority >= attr.priority) | |||
| 165 | attr.priority = I915_PRIORITY_HEARTBEAT; | |||
| 166 | if (rq->sched.attr.priority >= attr.priority) | |||
| 167 | attr.priority = I915_PRIORITY_BARRIER(0x7fffffff - 1); | |||
| 168 | ||||
| 169 | local_bh_disable(); | |||
| 170 | engine->sched_engine->schedule(rq, &attr); | |||
| 171 | local_bh_enable(); | |||
| 172 | } else { | |||
| 173 | reset_engine(engine, rq); | |||
| 174 | } | |||
| 175 | ||||
| 176 | rq->emitted_jiffies = jiffies; | |||
| 177 | goto out; | |||
| 178 | } | |||
| 179 | ||||
| 180 | serial = READ_ONCE(engine->serial)({ typeof(engine->serial) __tmp = *(volatile typeof(engine ->serial) *)&(engine->serial); membar_datadep_consumer (); __tmp; }); | |||
| 181 | if (engine->wakeref_serial == serial) | |||
| 182 | goto out; | |||
| 183 | ||||
| 184 | if (!mutex_trylock(&ce->timeline->mutex)(rw_enter(&ce->timeline->mutex, 0x0001UL | 0x0040UL ) == 0)) { | |||
| 185 | /* Unable to lock the kernel timeline, is the engine stuck? */ | |||
| 186 | if (xchg(&engine->heartbeat.blocked, serial)__sync_lock_test_and_set(&engine->heartbeat.blocked, serial ) == serial) | |||
| 187 | intel_gt_handle_error(engine->gt, engine->mask, | |||
| 188 | I915_ERROR_CAPTURE(1UL << (0)), | |||
| 189 | "no heartbeat on %s", | |||
| 190 | engine->name); | |||
| 191 | goto out; | |||
| 192 | } | |||
| 193 | ||||
| 194 | rq = heartbeat_create(ce, GFP_NOWAIT0x0002 | __GFP_NOWARN0); | |||
| 195 | if (IS_ERR(rq)) | |||
| 196 | goto unlock; | |||
| 197 | ||||
| 198 | heartbeat_commit(rq, &attr); | |||
| 199 | ||||
| 200 | unlock: | |||
| 201 | mutex_unlock(&ce->timeline->mutex)rw_exit_write(&ce->timeline->mutex); | |||
| 202 | out: | |||
| 203 | if (!engine->i915->params.enable_hangcheck || !next_heartbeat(engine)) | |||
| 204 | i915_request_put(fetch_and_zero(&engine->heartbeat.systole)({ typeof(*&engine->heartbeat.systole) __T = *(&engine ->heartbeat.systole); *(&engine->heartbeat.systole) = (typeof(*&engine->heartbeat.systole))0; __T; })); | |||
| 205 | intel_engine_pm_put(engine); | |||
| 206 | } | |||
| 207 | ||||
| 208 | void intel_engine_unpark_heartbeat(struct intel_engine_cs *engine) | |||
| 209 | { | |||
| 210 | if (!CONFIG_DRM_I915_HEARTBEAT_INTERVAL2500) | |||
| 211 | return; | |||
| 212 | ||||
| 213 | next_heartbeat(engine); | |||
| 214 | } | |||
| 215 | ||||
| 216 | void intel_engine_park_heartbeat(struct intel_engine_cs *engine) | |||
| 217 | { | |||
| 218 | if (cancel_delayed_work(&engine->heartbeat.work)) | |||
| 219 | i915_request_put(fetch_and_zero(&engine->heartbeat.systole)({ typeof(*&engine->heartbeat.systole) __T = *(&engine ->heartbeat.systole); *(&engine->heartbeat.systole) = (typeof(*&engine->heartbeat.systole))0; __T; })); | |||
| 220 | } | |||
| 221 | ||||
| 222 | void intel_gt_unpark_heartbeats(struct intel_gt *gt) | |||
| 223 | { | |||
| 224 | struct intel_engine_cs *engine; | |||
| 225 | enum intel_engine_id id; | |||
| 226 | ||||
| 227 | for_each_engine(engine, gt, id)for ((id) = 0; (id) < I915_NUM_ENGINES; (id)++) if (!((engine ) = (gt)->engine[(id)])) {} else | |||
| 228 | if (intel_engine_pm_is_awake(engine)) | |||
| 229 | intel_engine_unpark_heartbeat(engine); | |||
| 230 | } | |||
| 231 | ||||
| 232 | void intel_gt_park_heartbeats(struct intel_gt *gt) | |||
| 233 | { | |||
| 234 | struct intel_engine_cs *engine; | |||
| 235 | enum intel_engine_id id; | |||
| 236 | ||||
| 237 | for_each_engine(engine, gt, id)for ((id) = 0; (id) < I915_NUM_ENGINES; (id)++) if (!((engine ) = (gt)->engine[(id)])) {} else | |||
| 238 | intel_engine_park_heartbeat(engine); | |||
| 239 | } | |||
| 240 | ||||
| 241 | void intel_engine_init_heartbeat(struct intel_engine_cs *engine) | |||
| 242 | { | |||
| 243 | INIT_DELAYED_WORK(&engine->heartbeat.work, heartbeat); | |||
| 244 | } | |||
| 245 | ||||
| 246 | static int __intel_engine_pulse(struct intel_engine_cs *engine) | |||
| 247 | { | |||
| 248 | struct i915_sched_attr attr = { .priority = I915_PRIORITY_BARRIER(0x7fffffff - 1) }; | |||
| 249 | struct intel_context *ce = engine->kernel_context; | |||
| 250 | struct i915_request *rq; | |||
| 251 | ||||
| 252 | lockdep_assert_held(&ce->timeline->mutex)do { (void)(&ce->timeline->mutex); } while(0); | |||
| 253 | GEM_BUG_ON(!intel_engine_has_preemption(engine))((void)0); | |||
| 254 | GEM_BUG_ON(!intel_engine_pm_is_awake(engine))((void)0); | |||
| 255 | ||||
| 256 | rq = heartbeat_create(ce, GFP_NOWAIT0x0002 | __GFP_NOWARN0); | |||
| 257 | if (IS_ERR(rq)) | |||
| 258 | return PTR_ERR(rq); | |||
| 259 | ||||
| 260 | __set_bit(I915_FENCE_FLAG_SENTINEL, &rq->fence.flags); | |||
| 261 | ||||
| 262 | heartbeat_commit(rq, &attr); | |||
| 263 | GEM_BUG_ON(rq->sched.attr.priority < I915_PRIORITY_BARRIER)((void)0); | |||
| 264 | ||||
| 265 | return 0; | |||
| 266 | } | |||
| 267 | ||||
| 268 | static unsigned long set_heartbeat(struct intel_engine_cs *engine, | |||
| 269 | unsigned long delay) | |||
| 270 | { | |||
| 271 | unsigned long old; | |||
| 272 | ||||
| 273 | old = xchg(&engine->props.heartbeat_interval_ms, delay)__sync_lock_test_and_set(&engine->props.heartbeat_interval_ms , delay); | |||
| 274 | if (delay) | |||
| 275 | intel_engine_unpark_heartbeat(engine); | |||
| 276 | else | |||
| 277 | intel_engine_park_heartbeat(engine); | |||
| 278 | ||||
| 279 | return old; | |||
| 280 | } | |||
| 281 | ||||
| 282 | int intel_engine_set_heartbeat(struct intel_engine_cs *engine, | |||
| 283 | unsigned long delay) | |||
| 284 | { | |||
| 285 | struct intel_context *ce = engine->kernel_context; | |||
| 286 | int err = 0; | |||
| 287 | ||||
| 288 | if (!delay && !intel_engine_has_preempt_reset(engine)) | |||
| 289 | return -ENODEV19; | |||
| 290 | ||||
| 291 | intel_engine_pm_get(engine); | |||
| 292 | ||||
| 293 | err = mutex_lock_interruptible(&ce->timeline->mutex); | |||
| 294 | if (err) | |||
| 295 | goto out_rpm; | |||
| 296 | ||||
| 297 | if (delay != engine->props.heartbeat_interval_ms) { | |||
| 298 | unsigned long saved = set_heartbeat(engine, delay); | |||
| 299 | ||||
| 300 | /* recheck current execution */ | |||
| 301 | if (intel_engine_has_preemption(engine)) { | |||
| 302 | err = __intel_engine_pulse(engine); | |||
| 303 | if (err) | |||
| 304 | set_heartbeat(engine, saved); | |||
| 305 | } | |||
| 306 | } | |||
| 307 | ||||
| 308 | mutex_unlock(&ce->timeline->mutex)rw_exit_write(&ce->timeline->mutex); | |||
| 309 | ||||
| 310 | out_rpm: | |||
| 311 | intel_engine_pm_put(engine); | |||
| 312 | return err; | |||
| 313 | } | |||
| 314 | ||||
| 315 | int intel_engine_pulse(struct intel_engine_cs *engine) | |||
| 316 | { | |||
| 317 | struct intel_context *ce = engine->kernel_context; | |||
| 318 | int err; | |||
| 319 | ||||
| 320 | if (!intel_engine_has_preemption(engine)) | |||
| 321 | return -ENODEV19; | |||
| 322 | ||||
| 323 | if (!intel_engine_pm_get_if_awake(engine)) | |||
| 324 | return 0; | |||
| 325 | ||||
| 326 | err = -EINTR4; | |||
| 327 | if (!mutex_lock_interruptible(&ce->timeline->mutex)) { | |||
| 328 | err = __intel_engine_pulse(engine); | |||
| 329 | mutex_unlock(&ce->timeline->mutex)rw_exit_write(&ce->timeline->mutex); | |||
| 330 | } | |||
| 331 | ||||
| 332 | intel_engine_flush_submission(engine); | |||
| 333 | intel_engine_pm_put(engine); | |||
| 334 | return err; | |||
| 335 | } | |||
| 336 | ||||
| 337 | int intel_engine_flush_barriers(struct intel_engine_cs *engine) | |||
| 338 | { | |||
| 339 | struct i915_sched_attr attr = { .priority = I915_PRIORITY_MIN }; | |||
| 340 | struct intel_context *ce = engine->kernel_context; | |||
| 341 | struct i915_request *rq; | |||
| 342 | int err; | |||
| 343 | ||||
| 344 | if (llist_empty(&engine->barrier_tasks)) | |||
| 345 | return 0; | |||
| 346 | ||||
| 347 | if (!intel_engine_pm_get_if_awake(engine)) | |||
| 348 | return 0; | |||
| 349 | ||||
| 350 | if (mutex_lock_interruptible(&ce->timeline->mutex)) { | |||
| 351 | err = -EINTR4; | |||
| 352 | goto out_rpm; | |||
| 353 | } | |||
| 354 | ||||
| 355 | rq = heartbeat_create(ce, GFP_KERNEL(0x0001 | 0x0004)); | |||
| 356 | if (IS_ERR(rq)) { | |||
| 357 | err = PTR_ERR(rq); | |||
| 358 | goto out_unlock; | |||
| 359 | } | |||
| 360 | ||||
| 361 | heartbeat_commit(rq, &attr); | |||
| 362 | ||||
| 363 | err = 0; | |||
| 364 | out_unlock: | |||
| 365 | mutex_unlock(&ce->timeline->mutex)rw_exit_write(&ce->timeline->mutex); | |||
| 366 | out_rpm: | |||
| 367 | intel_engine_pm_put(engine); | |||
| 368 | return err; | |||
| 369 | } | |||
| 370 | ||||
| 371 | #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)0 | |||
| 372 | #include "selftest_engine_heartbeat.c" | |||
| 373 | #endif |
| 1 | /* SPDX-License-Identifier: MIT */ |
| 2 | #ifndef _INTEL_RINGBUFFER_H_ |
| 3 | #define _INTEL_RINGBUFFER_H_ |
| 4 | |
| 5 | #include <asm/cacheflush.h> |
| 6 | #include <drm/drm_util.h> |
| 7 | #include <drm/drm_cache.h> |
| 8 | |
| 9 | #include <linux/hashtable.h> |
| 10 | #include <linux/irq_work.h> |
| 11 | #include <linux/random.h> |
| 12 | #include <linux/seqlock.h> |
| 13 | |
| 14 | #include "i915_pmu.h" |
| 15 | #include "i915_request.h" |
| 16 | #include "i915_selftest.h" |
| 17 | #include "intel_engine_types.h" |
| 18 | #include "intel_gt_types.h" |
| 19 | #include "intel_timeline.h" |
| 20 | #include "intel_workarounds.h" |
| 21 | |
| 22 | struct drm_printer; |
| 23 | struct intel_context; |
| 24 | struct intel_gt; |
| 25 | struct lock_class_key; |
| 26 | |
| 27 | /* Early gen2 devices have a cacheline of just 32 bytes, using 64 is overkill, |
| 28 | * but keeps the logic simple. Indeed, the whole purpose of this macro is just |
| 29 | * to give some inclination as to some of the magic values used in the various |
| 30 | * workarounds! |
| 31 | */ |
| 32 | #define CACHELINE_BYTES64 64 |
| 33 | #define CACHELINE_DWORDS(64 / sizeof(u32)) (CACHELINE_BYTES64 / sizeof(u32)) |
| 34 | |
| 35 | #define ENGINE_TRACE(e, fmt, ...)do { const struct intel_engine_cs *e__ __attribute__((__unused__ )) = (e); do { } while (0); } while (0) do { \ |
| 36 | const struct intel_engine_cs *e__ __maybe_unused__attribute__((__unused__)) = (e); \ |
| 37 | GEM_TRACE("%s %s: " fmt, \do { } while (0) |
| 38 | dev_name(e__->i915->drm.dev), e__->name, \do { } while (0) |
| 39 | ##__VA_ARGS__)do { } while (0); \ |
| 40 | } while (0) |
| 41 | |
| 42 | /* |
| 43 | * The register defines to be used with the following macros need to accept a |
| 44 | * base param, e.g: |
| 45 | * |
| 46 | * REG_FOO(base) _MMIO((base) + <relative offset>) |
| 47 | * ENGINE_READ(engine, REG_FOO); |
| 48 | * |
| 49 | * register arrays are to be defined and accessed as follows: |
| 50 | * |
| 51 | * REG_BAR(base, i) _MMIO((base) + <relative offset> + (i) * <shift>) |
| 52 | * ENGINE_READ_IDX(engine, REG_BAR, i) |
| 53 | */ |
| 54 | |
| 55 | #define __ENGINE_REG_OP(op__, engine__, ...)intel_uncore_op__((engine__)->uncore, ...) \ |
| 56 | intel_uncore_##op__((engine__)->uncore, __VA_ARGS__) |
| 57 | |
| 58 | #define __ENGINE_READ_OP(op__, engine__, reg__)intel_uncore_op__(((engine__))->uncore, reg__((engine__)-> mmio_base)) \ |
| 59 | __ENGINE_REG_OP(op__, (engine__), reg__((engine__)->mmio_base))intel_uncore_op__(((engine__))->uncore, reg__((engine__)-> mmio_base)) |
| 60 | |
| 61 | #define ENGINE_READ16(...)__ENGINE_READ_OP __ENGINE_READ_OP(read16, __VA_ARGS__) |
| 62 | #define ENGINE_READ(...)__ENGINE_READ_OP __ENGINE_READ_OP(read, __VA_ARGS__) |
| 63 | #define ENGINE_READ_FW(...)__ENGINE_READ_OP __ENGINE_READ_OP(read_fw, __VA_ARGS__) |
| 64 | #define ENGINE_POSTING_READ(...)__ENGINE_READ_OP __ENGINE_READ_OP(posting_read_fw, __VA_ARGS__) |
| 65 | #define ENGINE_POSTING_READ16(...)__ENGINE_READ_OP __ENGINE_READ_OP(posting_read16, __VA_ARGS__) |
| 66 | |
| 67 | #define ENGINE_READ64(engine__, lower_reg__, upper_reg__)intel_uncore_read64_2x32(((engine__))->uncore, lower_reg__ ((engine__)->mmio_base), upper_reg__((engine__)->mmio_base )) \ |
| 68 | __ENGINE_REG_OP(read64_2x32, (engine__), \intel_uncore_read64_2x32(((engine__))->uncore, lower_reg__ ((engine__)->mmio_base), upper_reg__((engine__)->mmio_base )) |
| 69 | lower_reg__((engine__)->mmio_base), \intel_uncore_read64_2x32(((engine__))->uncore, lower_reg__ ((engine__)->mmio_base), upper_reg__((engine__)->mmio_base )) |
| 70 | upper_reg__((engine__)->mmio_base))intel_uncore_read64_2x32(((engine__))->uncore, lower_reg__ ((engine__)->mmio_base), upper_reg__((engine__)->mmio_base )) |
| 71 | |
| 72 | #define ENGINE_READ_IDX(engine__, reg__, idx__)intel_uncore_read(((engine__))->uncore, reg__((engine__)-> mmio_base, (idx__))) \ |
| 73 | __ENGINE_REG_OP(read, (engine__), reg__((engine__)->mmio_base, (idx__)))intel_uncore_read(((engine__))->uncore, reg__((engine__)-> mmio_base, (idx__))) |
| 74 | |
| 75 | #define __ENGINE_WRITE_OP(op__, engine__, reg__, val__)intel_uncore_op__(((engine__))->uncore, reg__((engine__)-> mmio_base), (val__)) \ |
| 76 | __ENGINE_REG_OP(op__, (engine__), reg__((engine__)->mmio_base), (val__))intel_uncore_op__(((engine__))->uncore, reg__((engine__)-> mmio_base), (val__)) |
| 77 | |
| 78 | #define ENGINE_WRITE16(...)__ENGINE_WRITE_OP __ENGINE_WRITE_OP(write16, __VA_ARGS__) |
| 79 | #define ENGINE_WRITE(...)__ENGINE_WRITE_OP __ENGINE_WRITE_OP(write, __VA_ARGS__) |
| 80 | #define ENGINE_WRITE_FW(...)__ENGINE_WRITE_OP __ENGINE_WRITE_OP(write_fw, __VA_ARGS__) |
| 81 | |
| 82 | #define GEN6_RING_FAULT_REG_READ(engine__)intel_uncore_read((engine__)->uncore, RING_FAULT_REG(engine__ )) \ |
| 83 | intel_uncore_read((engine__)->uncore, RING_FAULT_REG(engine__)) |
| 84 | |
| 85 | #define GEN6_RING_FAULT_REG_POSTING_READ(engine__)((void)intel_uncore_read_notrace((engine__)->uncore, RING_FAULT_REG (engine__))) \ |
| 86 | intel_uncore_posting_read((engine__)->uncore, RING_FAULT_REG(engine__))((void)intel_uncore_read_notrace((engine__)->uncore, RING_FAULT_REG (engine__))) |
| 87 | |
| 88 | #define GEN6_RING_FAULT_REG_RMW(engine__, clear__, set__)({ u32 __val; __val = intel_uncore_read((engine__)->uncore , RING_FAULT_REG(engine__)); __val &= ~(clear__); __val |= (set__); intel_uncore_write((engine__)->uncore, RING_FAULT_REG (engine__), __val); }) \ |
| 89 | ({ \ |
| 90 | u32 __val; \ |
| 91 | \ |
| 92 | __val = intel_uncore_read((engine__)->uncore, \ |
| 93 | RING_FAULT_REG(engine__)); \ |
| 94 | __val &= ~(clear__); \ |
| 95 | __val |= (set__); \ |
| 96 | intel_uncore_write((engine__)->uncore, RING_FAULT_REG(engine__), \ |
| 97 | __val); \ |
| 98 | }) |
| 99 | |
| 100 | /* seqno size is actually only a uint32, but since we plan to use MI_FLUSH_DW to |
| 101 | * do the writes, and that must have qw aligned offsets, simply pretend it's 8b. |
| 102 | */ |
| 103 | |
| 104 | static inline unsigned int |
| 105 | execlists_num_ports(const struct intel_engine_execlists * const execlists) |
| 106 | { |
| 107 | return execlists->port_mask + 1; |
| 108 | } |
| 109 | |
| 110 | static inline struct i915_request * |
| 111 | execlists_active(const struct intel_engine_execlists *execlists) |
| 112 | { |
| 113 | struct i915_request * const *cur, * const *old, *active; |
| 114 | |
| 115 | cur = READ_ONCE(execlists->active)({ typeof(execlists->active) __tmp = *(volatile typeof(execlists ->active) *)&(execlists->active); membar_datadep_consumer (); __tmp; }); |
| 116 | smp_rmb()do { __asm volatile("" ::: "memory"); } while (0); /* pairs with overwrite protection in process_csb() */ |
| 117 | do { |
| 118 | old = cur; |
| 119 | |
| 120 | active = READ_ONCE(*cur)({ typeof(*cur) __tmp = *(volatile typeof(*cur) *)&(*cur) ; membar_datadep_consumer(); __tmp; }); |
| 121 | cur = READ_ONCE(execlists->active)({ typeof(execlists->active) __tmp = *(volatile typeof(execlists ->active) *)&(execlists->active); membar_datadep_consumer (); __tmp; }); |
| 122 | |
| 123 | smp_rmb()do { __asm volatile("" ::: "memory"); } while (0); /* and complete the seqlock retry */ |
| 124 | } while (unlikely(cur != old)__builtin_expect(!!(cur != old), 0)); |
| 125 | |
| 126 | return active; |
| 127 | } |
| 128 | |
| 129 | struct i915_request * |
| 130 | execlists_unwind_incomplete_requests(struct intel_engine_execlists *execlists); |
| 131 | |
| 132 | static inline u32 |
| 133 | intel_read_status_page(const struct intel_engine_cs *engine, int reg) |
| 134 | { |
| 135 | /* Ensure that the compiler doesn't optimize away the load. */ |
| 136 | return READ_ONCE(engine->status_page.addr[reg])({ typeof(engine->status_page.addr[reg]) __tmp = *(volatile typeof(engine->status_page.addr[reg]) *)&(engine-> status_page.addr[reg]); membar_datadep_consumer(); __tmp; }); |
| 137 | } |
| 138 | |
| 139 | static inline void |
| 140 | intel_write_status_page(struct intel_engine_cs *engine, int reg, u32 value) |
| 141 | { |
| 142 | /* Writing into the status page should be done sparingly. Since |
| 143 | * we do when we are uncertain of the device state, we take a bit |
| 144 | * of extra paranoia to try and ensure that the HWS takes the value |
| 145 | * we give and that it doesn't end up trapped inside the CPU! |
| 146 | */ |
| 147 | drm_clflush_virt_range(&engine->status_page.addr[reg], sizeof(value)); |
| 148 | WRITE_ONCE(engine->status_page.addr[reg], value)({ typeof(engine->status_page.addr[reg]) __tmp = (value); * (volatile typeof(engine->status_page.addr[reg]) *)&(engine ->status_page.addr[reg]) = __tmp; __tmp; }); |
| 149 | drm_clflush_virt_range(&engine->status_page.addr[reg], sizeof(value)); |
| 150 | } |
| 151 | |
| 152 | /* |
| 153 | * Reads a dword out of the status page, which is written to from the command |
| 154 | * queue by automatic updates, MI_REPORT_HEAD, MI_STORE_DATA_INDEX, or |
| 155 | * MI_STORE_DATA_IMM. |
| 156 | * |
| 157 | * The following dwords have a reserved meaning: |
| 158 | * 0x00: ISR copy, updated when an ISR bit not set in the HWSTAM changes. |
| 159 | * 0x04: ring 0 head pointer |
| 160 | * 0x05: ring 1 head pointer (915-class) |
| 161 | * 0x06: ring 2 head pointer (915-class) |
| 162 | * 0x10-0x1b: Context status DWords (GM45) |
| 163 | * 0x1f: Last written status offset. (GM45) |
| 164 | * 0x20-0x2f: Reserved (Gen6+) |
| 165 | * |
| 166 | * The area from dword 0x30 to 0x3ff is available for driver usage. |
| 167 | */ |
| 168 | #define I915_GEM_HWS_PREEMPT0x32 0x32 |
| 169 | #define I915_GEM_HWS_PREEMPT_ADDR(0x32 * sizeof(u32)) (I915_GEM_HWS_PREEMPT0x32 * sizeof(u32)) |
| 170 | #define I915_GEM_HWS_SEQNO0x40 0x40 |
| 171 | #define I915_GEM_HWS_SEQNO_ADDR(0x40 * sizeof(u32)) (I915_GEM_HWS_SEQNO0x40 * sizeof(u32)) |
| 172 | #define I915_GEM_HWS_MIGRATE(0x42 * sizeof(u32)) (0x42 * sizeof(u32)) |
| 173 | #define I915_GEM_HWS_PXP0x60 0x60 |
| 174 | #define I915_GEM_HWS_PXP_ADDR(0x60 * sizeof(u32)) (I915_GEM_HWS_PXP0x60 * sizeof(u32)) |
| 175 | #define I915_GEM_HWS_SCRATCH0x80 0x80 |
| 176 | |
| 177 | #define I915_HWS_CSB_BUF0_INDEX0x10 0x10 |
| 178 | #define I915_HWS_CSB_WRITE_INDEX0x1f 0x1f |
| 179 | #define ICL_HWS_CSB_WRITE_INDEX0x2f 0x2f |
| 180 | #define INTEL_HWS_CSB_WRITE_INDEX(__i915)(((&(__i915)->__runtime)->graphics.ip.ver) >= 11 ? 0x2f : 0x1f) \ |
| 181 | (GRAPHICS_VER(__i915)((&(__i915)->__runtime)->graphics.ip.ver) >= 11 ? ICL_HWS_CSB_WRITE_INDEX0x2f : I915_HWS_CSB_WRITE_INDEX0x1f) |
| 182 | |
| 183 | void intel_engine_stop(struct intel_engine_cs *engine); |
| 184 | void intel_engine_cleanup(struct intel_engine_cs *engine); |
| 185 | |
| 186 | int intel_engines_init_mmio(struct intel_gt *gt); |
| 187 | int intel_engines_init(struct intel_gt *gt); |
| 188 | |
| 189 | void intel_engine_free_request_pool(struct intel_engine_cs *engine); |
| 190 | |
| 191 | void intel_engines_release(struct intel_gt *gt); |
| 192 | void intel_engines_free(struct intel_gt *gt); |
| 193 | |
| 194 | int intel_engine_init_common(struct intel_engine_cs *engine); |
| 195 | void intel_engine_cleanup_common(struct intel_engine_cs *engine); |
| 196 | |
| 197 | int intel_engine_resume(struct intel_engine_cs *engine); |
| 198 | |
| 199 | int intel_ring_submission_setup(struct intel_engine_cs *engine); |
| 200 | |
| 201 | int intel_engine_stop_cs(struct intel_engine_cs *engine); |
| 202 | void intel_engine_cancel_stop_cs(struct intel_engine_cs *engine); |
| 203 | |
| 204 | void intel_engine_wait_for_pending_mi_fw(struct intel_engine_cs *engine); |
| 205 | |
| 206 | void intel_engine_set_hwsp_writemask(struct intel_engine_cs *engine, u32 mask); |
| 207 | |
| 208 | u64 intel_engine_get_active_head(const struct intel_engine_cs *engine); |
| 209 | u64 intel_engine_get_last_batch_head(const struct intel_engine_cs *engine); |
| 210 | |
| 211 | void intel_engine_get_instdone(const struct intel_engine_cs *engine, |
| 212 | struct intel_instdone *instdone); |
| 213 | |
| 214 | void intel_engine_init_execlists(struct intel_engine_cs *engine); |
| 215 | |
| 216 | bool_Bool intel_engine_irq_enable(struct intel_engine_cs *engine); |
| 217 | void intel_engine_irq_disable(struct intel_engine_cs *engine); |
| 218 | |
| 219 | static inline void __intel_engine_reset(struct intel_engine_cs *engine, |
| 220 | bool_Bool stalled) |
| 221 | { |
| 222 | if (engine->reset.rewind) |
| 223 | engine->reset.rewind(engine, stalled); |
| 224 | engine->serial++; /* contexts lost */ |
| 225 | } |
| 226 | |
| 227 | bool_Bool intel_engines_are_idle(struct intel_gt *gt); |
| 228 | bool_Bool intel_engine_is_idle(struct intel_engine_cs *engine); |
| 229 | |
| 230 | void __intel_engine_flush_submission(struct intel_engine_cs *engine, bool_Bool sync); |
| 231 | static inline void intel_engine_flush_submission(struct intel_engine_cs *engine) |
| 232 | { |
| 233 | __intel_engine_flush_submission(engine, true1); |
| 234 | } |
| 235 | |
| 236 | void intel_engines_reset_default_submission(struct intel_gt *gt); |
| 237 | |
| 238 | bool_Bool intel_engine_can_store_dword(struct intel_engine_cs *engine); |
| 239 | |
| 240 | __printf(3, 4)__attribute__((__format__(__kprintf__,3,4))) |
| 241 | void intel_engine_dump(struct intel_engine_cs *engine, |
| 242 | struct drm_printer *m, |
| 243 | const char *header, ...); |
| 244 | void intel_engine_dump_active_requests(struct list_head *requests, |
| 245 | struct i915_request *hung_rq, |
| 246 | struct drm_printer *m); |
| 247 | |
| 248 | ktime_t intel_engine_get_busy_time(struct intel_engine_cs *engine, |
| 249 | ktime_t *now); |
| 250 | |
| 251 | void intel_engine_get_hung_entity(struct intel_engine_cs *engine, |
| 252 | struct intel_context **ce, struct i915_request **rq); |
| 253 | |
| 254 | u32 intel_engine_context_size(struct intel_gt *gt, u8 class); |
| 255 | struct intel_context * |
| 256 | intel_engine_create_pinned_context(struct intel_engine_cs *engine, |
| 257 | struct i915_address_space *vm, |
| 258 | unsigned int ring_size, |
| 259 | unsigned int hwsp, |
| 260 | struct lock_class_key *key, |
| 261 | const char *name); |
| 262 | |
| 263 | void intel_engine_destroy_pinned_context(struct intel_context *ce); |
| 264 | |
| 265 | void xehp_enable_ccs_engines(struct intel_engine_cs *engine); |
| 266 | |
| 267 | #define ENGINE_PHYSICAL0 0 |
| 268 | #define ENGINE_MOCK1 1 |
| 269 | #define ENGINE_VIRTUAL2 2 |
| 270 | |
| 271 | static inline bool_Bool intel_engine_uses_guc(const struct intel_engine_cs *engine) |
| 272 | { |
| 273 | return engine->gt->submission_method >= INTEL_SUBMISSION_GUC; |
| 274 | } |
| 275 | |
| 276 | static inline bool_Bool |
| 277 | intel_engine_has_preempt_reset(const struct intel_engine_cs *engine) |
| 278 | { |
| 279 | if (!CONFIG_DRM_I915_PREEMPT_TIMEOUT640) |
| 280 | return false0; |
| 281 | |
| 282 | return intel_engine_has_preemption(engine); |
| 283 | } |
| 284 | |
| 285 | #define FORCE_VIRTUAL(1UL << (0)) BIT(0)(1UL << (0)) |
| 286 | struct intel_context * |
| 287 | intel_engine_create_virtual(struct intel_engine_cs **siblings, |
| 288 | unsigned int count, unsigned long flags); |
| 289 | |
| 290 | static inline struct intel_context * |
| 291 | intel_engine_create_parallel(struct intel_engine_cs **engines, |
| 292 | unsigned int num_engines, |
| 293 | unsigned int width) |
| 294 | { |
| 295 | GEM_BUG_ON(!engines[0]->cops->create_parallel)((void)0); |
| 296 | return engines[0]->cops->create_parallel(engines, num_engines, width); |
| 297 | } |
| 298 | |
| 299 | static inline bool_Bool |
| 300 | intel_virtual_engine_has_heartbeat(const struct intel_engine_cs *engine) |
| 301 | { |
| 302 | /* |
| 303 | * For non-GuC submission we expect the back-end to look at the |
| 304 | * heartbeat status of the actual physical engine that the work |
| 305 | * has been (or is being) scheduled on, so we should only reach |
| 306 | * here with GuC submission enabled. |
| 307 | */ |
| 308 | GEM_BUG_ON(!intel_engine_uses_guc(engine))((void)0); |
| 309 | |
| 310 | return intel_guc_virtual_engine_has_heartbeat(engine); |
| 311 | } |
| 312 | |
| 313 | static inline bool_Bool |
| 314 | intel_engine_has_heartbeat(const struct intel_engine_cs *engine) |
| 315 | { |
| 316 | if (!CONFIG_DRM_I915_HEARTBEAT_INTERVAL2500) |
| 317 | return false0; |
| 318 | |
| 319 | if (intel_engine_is_virtual(engine)) |
| 320 | return intel_virtual_engine_has_heartbeat(engine); |
| 321 | else |
| 322 | return READ_ONCE(engine->props.heartbeat_interval_ms)({ typeof(engine->props.heartbeat_interval_ms) __tmp = *(volatile typeof(engine->props.heartbeat_interval_ms) *)&(engine ->props.heartbeat_interval_ms); membar_datadep_consumer(); __tmp; }); |
| 323 | } |
| 324 | |
| 325 | static inline struct intel_engine_cs * |
| 326 | intel_engine_get_sibling(struct intel_engine_cs *engine, unsigned int sibling) |
| 327 | { |
| 328 | GEM_BUG_ON(!intel_engine_is_virtual(engine))((void)0); |
| 329 | return engine->cops->get_sibling(engine, sibling); |
| 330 | } |
| 331 | |
| 332 | static inline void |
| 333 | intel_engine_set_hung_context(struct intel_engine_cs *engine, |
| 334 | struct intel_context *ce) |
| 335 | { |
| 336 | engine->hung_ce = ce; |
| 337 | } |
| 338 | |
| 339 | static inline void |
| 340 | intel_engine_clear_hung_context(struct intel_engine_cs *engine) |
| 341 | { |
| 342 | intel_engine_set_hung_context(engine, NULL((void *)0)); |
| 343 | } |
| 344 | |
| 345 | static inline struct intel_context * |
| 346 | intel_engine_get_hung_context(struct intel_engine_cs *engine) |
| 347 | { |
| 348 | return engine->hung_ce; |
| 349 | } |
| 350 | |
| 351 | u64 intel_clamp_heartbeat_interval_ms(struct intel_engine_cs *engine, u64 value); |
| 352 | u64 intel_clamp_max_busywait_duration_ns(struct intel_engine_cs *engine, u64 value); |
| 353 | u64 intel_clamp_preempt_timeout_ms(struct intel_engine_cs *engine, u64 value); |
| 354 | u64 intel_clamp_stop_timeout_ms(struct intel_engine_cs *engine, u64 value); |
| 355 | u64 intel_clamp_timeslice_duration_ms(struct intel_engine_cs *engine, u64 value); |
| 356 | |
| 357 | #endif /* _INTEL_RINGBUFFER_H_ */ |
| 1 | /* SPDX-License-Identifier: MIT */ |
| 2 | /* |
| 3 | * Copyright © 2019 Intel Corporation |
| 4 | */ |
| 5 | |
| 6 | #ifndef INTEL_ENGINE_PM_H |
| 7 | #define INTEL_ENGINE_PM_H |
| 8 | |
| 9 | #include "i915_drv.h" |
| 10 | #include "i915_request.h" |
| 11 | #include "intel_engine_types.h" |
| 12 | #include "intel_wakeref.h" |
| 13 | #include "intel_gt_pm.h" |
| 14 | |
| 15 | static inline bool_Bool |
| 16 | intel_engine_pm_is_awake(const struct intel_engine_cs *engine) |
| 17 | { |
| 18 | return intel_wakeref_is_active(&engine->wakeref); |
| 19 | } |
| 20 | |
| 21 | static inline void __intel_engine_pm_get(struct intel_engine_cs *engine) |
| 22 | { |
| 23 | __intel_wakeref_get(&engine->wakeref); |
| 24 | } |
| 25 | |
| 26 | static inline void intel_engine_pm_get(struct intel_engine_cs *engine) |
| 27 | { |
| 28 | intel_wakeref_get(&engine->wakeref); |
| 29 | } |
| 30 | |
| 31 | static inline bool_Bool intel_engine_pm_get_if_awake(struct intel_engine_cs *engine) |
| 32 | { |
| 33 | return intel_wakeref_get_if_active(&engine->wakeref); |
| 34 | } |
| 35 | |
| 36 | static inline void intel_engine_pm_might_get(struct intel_engine_cs *engine) |
| 37 | { |
| 38 | if (!intel_engine_is_virtual(engine)) { |
| 39 | intel_wakeref_might_get(&engine->wakeref); |
| 40 | } else { |
| 41 | struct intel_gt *gt = engine->gt; |
| 42 | struct intel_engine_cs *tengine; |
| 43 | intel_engine_mask_t tmp, mask = engine->mask; |
| 44 | |
| 45 | for_each_engine_masked(tengine, gt, mask, tmp)for ((tmp) = (mask) & (gt)->info.engine_mask; (tmp) ? ( (tengine) = (gt)->engine[({ int __idx = ffs(tmp) - 1; tmp &= ~(1UL << (__idx)); __idx; })]), 1 : 0;) |
| 46 | intel_wakeref_might_get(&tengine->wakeref); |
| 47 | } |
| 48 | intel_gt_pm_might_get(engine->gt); |
| 49 | } |
| 50 | |
| 51 | static inline void intel_engine_pm_put(struct intel_engine_cs *engine) |
| 52 | { |
| 53 | intel_wakeref_put(&engine->wakeref); |
| 54 | } |
| 55 | |
| 56 | static inline void intel_engine_pm_put_async(struct intel_engine_cs *engine) |
| 57 | { |
| 58 | intel_wakeref_put_async(&engine->wakeref); |
| 59 | } |
| 60 | |
| 61 | static inline void intel_engine_pm_put_delay(struct intel_engine_cs *engine, |
| 62 | unsigned long delay) |
| 63 | { |
| 64 | intel_wakeref_put_delay(&engine->wakeref, delay); |
| 65 | } |
| 66 | |
| 67 | static inline void intel_engine_pm_flush(struct intel_engine_cs *engine) |
| 68 | { |
| 69 | intel_wakeref_unlock_wait(&engine->wakeref); |
| 70 | } |
| 71 | |
| 72 | static inline void intel_engine_pm_might_put(struct intel_engine_cs *engine) |
| 73 | { |
| 74 | if (!intel_engine_is_virtual(engine)) { |
| 75 | intel_wakeref_might_put(&engine->wakeref); |
| 76 | } else { |
| 77 | struct intel_gt *gt = engine->gt; |
| 78 | struct intel_engine_cs *tengine; |
| 79 | intel_engine_mask_t tmp, mask = engine->mask; |
| 80 | |
| 81 | for_each_engine_masked(tengine, gt, mask, tmp)for ((tmp) = (mask) & (gt)->info.engine_mask; (tmp) ? ( (tengine) = (gt)->engine[({ int __idx = ffs(tmp) - 1; tmp &= ~(1UL << (__idx)); __idx; })]), 1 : 0;) |
| 82 | intel_wakeref_might_put(&tengine->wakeref); |
| 83 | } |
| 84 | intel_gt_pm_might_put(engine->gt); |
| 85 | } |
| 86 | |
| 87 | static inline struct i915_request * |
| 88 | intel_engine_create_kernel_request(struct intel_engine_cs *engine) |
| 89 | { |
| 90 | struct i915_request *rq; |
| 91 | |
| 92 | /* |
| 93 | * The engine->kernel_context is special as it is used inside |
| 94 | * the engine-pm barrier (see __engine_park()), circumventing |
| 95 | * the usual mutexes and relying on the engine-pm barrier |
| 96 | * instead. So whenever we use the engine->kernel_context |
| 97 | * outside of the barrier, we must manually handle the |
| 98 | * engine wakeref to serialise with the use inside. |
| 99 | */ |
| 100 | intel_engine_pm_get(engine); |
| 101 | rq = i915_request_create(engine->kernel_context); |
| 102 | intel_engine_pm_put(engine); |
| 103 | |
| 104 | return rq; |
| 105 | } |
| 106 | |
| 107 | void intel_engine_init__pm(struct intel_engine_cs *engine); |
| 108 | |
| 109 | void intel_engine_reset_pinned_contexts(struct intel_engine_cs *engine); |
| 110 | |
| 111 | #endif /* INTEL_ENGINE_PM_H */ |
| 1 | /* |
| 2 | * SPDX-License-Identifier: MIT |
| 3 | * |
| 4 | * Copyright © 2019 Intel Corporation |
| 5 | */ |
| 6 | |
| 7 | #ifndef INTEL_WAKEREF_H |
| 8 | #define INTEL_WAKEREF_H |
| 9 | |
| 10 | #include <linux/atomic.h> |
| 11 | #include <linux/bitfield.h> |
| 12 | #include <linux/bits.h> |
| 13 | #include <linux/lockdep.h> |
| 14 | #include <linux/mutex.h> |
| 15 | #include <linux/refcount.h> |
| 16 | #include <linux/stackdepot.h> |
| 17 | #include <linux/timer.h> |
| 18 | #include <linux/workqueue.h> |
| 19 | |
| 20 | #if IS_ENABLED(CONFIG_DRM_I915_DEBUG)0 |
| 21 | #define INTEL_WAKEREF_BUG_ON(expr)((void)0) BUG_ON(expr)((!(expr)) ? (void)0 : __assert("diagnostic ", "/usr/src/sys/dev/pci/drm/i915/intel_wakeref.h" , 21, "!(expr)")) |
| 22 | #else |
| 23 | #define INTEL_WAKEREF_BUG_ON(expr)((void)0) BUILD_BUG_ON_INVALID(expr)((void)0) |
| 24 | #endif |
| 25 | |
| 26 | struct intel_runtime_pm; |
| 27 | struct intel_wakeref; |
| 28 | |
| 29 | typedef depot_stack_handle_t intel_wakeref_t; |
| 30 | |
| 31 | struct intel_wakeref_ops { |
| 32 | int (*get)(struct intel_wakeref *wf); |
| 33 | int (*put)(struct intel_wakeref *wf); |
| 34 | }; |
| 35 | |
| 36 | struct intel_wakeref { |
| 37 | atomic_t count; |
| 38 | struct rwlock mutex; |
| 39 | |
| 40 | intel_wakeref_t wakeref; |
| 41 | |
| 42 | struct intel_runtime_pm *rpm; |
| 43 | const struct intel_wakeref_ops *ops; |
| 44 | |
| 45 | struct delayed_work work; |
| 46 | }; |
| 47 | |
| 48 | struct intel_wakeref_lockclass { |
| 49 | struct lock_class_key mutex; |
| 50 | struct lock_class_key work; |
| 51 | }; |
| 52 | |
| 53 | void __intel_wakeref_init(struct intel_wakeref *wf, |
| 54 | struct intel_runtime_pm *rpm, |
| 55 | const struct intel_wakeref_ops *ops, |
| 56 | struct intel_wakeref_lockclass *key); |
| 57 | #define intel_wakeref_init(wf, rpm, ops)do { static struct intel_wakeref_lockclass __key; __intel_wakeref_init ((wf), (rpm), (ops), &__key); } while (0) do { \ |
| 58 | static struct intel_wakeref_lockclass __key; \ |
| 59 | \ |
| 60 | __intel_wakeref_init((wf), (rpm), (ops), &__key); \ |
| 61 | } while (0) |
| 62 | |
| 63 | int __intel_wakeref_get_first(struct intel_wakeref *wf); |
| 64 | void __intel_wakeref_put_last(struct intel_wakeref *wf, unsigned long flags); |
| 65 | |
| 66 | /** |
| 67 | * intel_wakeref_get: Acquire the wakeref |
| 68 | * @wf: the wakeref |
| 69 | * |
| 70 | * Acquire a hold on the wakeref. The first user to do so, will acquire |
| 71 | * the runtime pm wakeref and then call the @fn underneath the wakeref |
| 72 | * mutex. |
| 73 | * |
| 74 | * Note that @fn is allowed to fail, in which case the runtime-pm wakeref |
| 75 | * will be released and the acquisition unwound, and an error reported. |
| 76 | * |
| 77 | * Returns: 0 if the wakeref was acquired successfully, or a negative error |
| 78 | * code otherwise. |
| 79 | */ |
| 80 | static inline int |
| 81 | intel_wakeref_get(struct intel_wakeref *wf) |
| 82 | { |
| 83 | might_sleep()assertwaitok(); |
| 84 | if (unlikely(!atomic_inc_not_zero(&wf->count))__builtin_expect(!!(!atomic_add_unless((&wf->count), 1 , 0)), 0)) |
| 85 | return __intel_wakeref_get_first(wf); |
| 86 | |
| 87 | return 0; |
| 88 | } |
| 89 | |
| 90 | /** |
| 91 | * __intel_wakeref_get: Acquire the wakeref, again |
| 92 | * @wf: the wakeref |
| 93 | * |
| 94 | * Increment the wakeref counter, only valid if it is already held by |
| 95 | * the caller. |
| 96 | * |
| 97 | * See intel_wakeref_get(). |
| 98 | */ |
| 99 | static inline void |
| 100 | __intel_wakeref_get(struct intel_wakeref *wf) |
| 101 | { |
| 102 | INTEL_WAKEREF_BUG_ON(atomic_read(&wf->count) <= 0)((void)0); |
| 103 | atomic_inc(&wf->count)__sync_fetch_and_add(&wf->count, 1); |
| 104 | } |
| 105 | |
| 106 | /** |
| 107 | * intel_wakeref_get_if_in_use: Acquire the wakeref |
| 108 | * @wf: the wakeref |
| 109 | * |
| 110 | * Acquire a hold on the wakeref, but only if the wakeref is already |
| 111 | * active. |
| 112 | * |
| 113 | * Returns: true if the wakeref was acquired, false otherwise. |
| 114 | */ |
| 115 | static inline bool_Bool |
| 116 | intel_wakeref_get_if_active(struct intel_wakeref *wf) |
| 117 | { |
| 118 | return atomic_inc_not_zero(&wf->count)atomic_add_unless((&wf->count), 1, 0); |
| 119 | } |
| 120 | |
| 121 | enum { |
| 122 | INTEL_WAKEREF_PUT_ASYNC_BIT = 0, |
| 123 | __INTEL_WAKEREF_PUT_LAST_BIT__ |
| 124 | }; |
| 125 | |
| 126 | static inline void |
| 127 | intel_wakeref_might_get(struct intel_wakeref *wf) |
| 128 | { |
| 129 | might_lock(&wf->mutex); |
| 130 | } |
| 131 | |
| 132 | /** |
| 133 | * intel_wakeref_put_flags: Release the wakeref |
| 134 | * @wf: the wakeref |
| 135 | * @flags: control flags |
| 136 | * |
| 137 | * Release our hold on the wakeref. When there are no more users, |
| 138 | * the runtime pm wakeref will be released after the @fn callback is called |
| 139 | * underneath the wakeref mutex. |
| 140 | * |
| 141 | * Note that @fn is allowed to fail, in which case the runtime-pm wakeref |
| 142 | * is retained and an error reported. |
| 143 | * |
| 144 | * Returns: 0 if the wakeref was released successfully, or a negative error |
| 145 | * code otherwise. |
| 146 | */ |
| 147 | static inline void |
| 148 | __intel_wakeref_put(struct intel_wakeref *wf, unsigned long flags) |
| 149 | #define INTEL_WAKEREF_PUT_ASYNC(1UL << (INTEL_WAKEREF_PUT_ASYNC_BIT)) BIT(INTEL_WAKEREF_PUT_ASYNC_BIT)(1UL << (INTEL_WAKEREF_PUT_ASYNC_BIT)) |
| 150 | #define INTEL_WAKEREF_PUT_DELAY(((~0UL) >> (64 - (64 - 1) - 1)) & ((~0UL) << (__INTEL_WAKEREF_PUT_LAST_BIT__))) \ |
| 151 | GENMASK(BITS_PER_LONG - 1, __INTEL_WAKEREF_PUT_LAST_BIT__)(((~0UL) >> (64 - (64 - 1) - 1)) & ((~0UL) << (__INTEL_WAKEREF_PUT_LAST_BIT__))) |
| 152 | { |
| 153 | INTEL_WAKEREF_BUG_ON(atomic_read(&wf->count) <= 0)((void)0); |
| 154 | if (unlikely(!atomic_add_unless(&wf->count, -1, 1))__builtin_expect(!!(!atomic_add_unless(&wf->count, -1, 1)), 0)) |
| 155 | __intel_wakeref_put_last(wf, flags); |
| 156 | } |
| 157 | |
| 158 | static inline void |
| 159 | intel_wakeref_put(struct intel_wakeref *wf) |
| 160 | { |
| 161 | might_sleep()assertwaitok(); |
| 162 | __intel_wakeref_put(wf, 0); |
| 163 | } |
| 164 | |
| 165 | static inline void |
| 166 | intel_wakeref_put_async(struct intel_wakeref *wf) |
| 167 | { |
| 168 | __intel_wakeref_put(wf, INTEL_WAKEREF_PUT_ASYNC(1UL << (INTEL_WAKEREF_PUT_ASYNC_BIT))); |
| 169 | } |
| 170 | |
| 171 | static inline void |
| 172 | intel_wakeref_put_delay(struct intel_wakeref *wf, unsigned long delay) |
| 173 | { |
| 174 | __intel_wakeref_put(wf, |
| 175 | INTEL_WAKEREF_PUT_ASYNC(1UL << (INTEL_WAKEREF_PUT_ASYNC_BIT)) | |
| 176 | FIELD_PREP(INTEL_WAKEREF_PUT_DELAY, delay)(((typeof((((~0UL) >> (64 - (64 - 1) - 1)) & ((~0UL ) << (__INTEL_WAKEREF_PUT_LAST_BIT__)))))(delay) << (__builtin_ffsll((((~0UL) >> (64 - (64 - 1) - 1)) & ((~0UL) << (__INTEL_WAKEREF_PUT_LAST_BIT__)))) - 1)) & ((((~0UL) >> (64 - (64 - 1) - 1)) & ((~0UL) << (__INTEL_WAKEREF_PUT_LAST_BIT__)))))); |
| 177 | } |
| 178 | |
| 179 | static inline void |
| 180 | intel_wakeref_might_put(struct intel_wakeref *wf) |
| 181 | { |
| 182 | might_lock(&wf->mutex); |
| 183 | } |
| 184 | |
| 185 | /** |
| 186 | * intel_wakeref_lock: Lock the wakeref (mutex) |
| 187 | * @wf: the wakeref |
| 188 | * |
| 189 | * Locks the wakeref to prevent it being acquired or released. New users |
| 190 | * can still adjust the counter, but the wakeref itself (and callback) |
| 191 | * cannot be acquired or released. |
| 192 | */ |
| 193 | static inline void |
| 194 | intel_wakeref_lock(struct intel_wakeref *wf) |
| 195 | __acquires(wf->mutex) |
| 196 | { |
| 197 | mutex_lock(&wf->mutex)rw_enter_write(&wf->mutex); |
| 198 | } |
| 199 | |
| 200 | /** |
| 201 | * intel_wakeref_unlock: Unlock the wakeref |
| 202 | * @wf: the wakeref |
| 203 | * |
| 204 | * Releases a previously acquired intel_wakeref_lock(). |
| 205 | */ |
| 206 | static inline void |
| 207 | intel_wakeref_unlock(struct intel_wakeref *wf) |
| 208 | __releases(wf->mutex) |
| 209 | { |
| 210 | mutex_unlock(&wf->mutex)rw_exit_write(&wf->mutex); |
| 211 | } |
| 212 | |
| 213 | /** |
| 214 | * intel_wakeref_unlock_wait: Wait until the active callback is complete |
| 215 | * @wf: the wakeref |
| 216 | * |
| 217 | * Waits for the active callback (under the @wf->mutex or another CPU) is |
| 218 | * complete. |
| 219 | */ |
| 220 | static inline void |
| 221 | intel_wakeref_unlock_wait(struct intel_wakeref *wf) |
| 222 | { |
| 223 | mutex_lock(&wf->mutex)rw_enter_write(&wf->mutex); |
| 224 | mutex_unlock(&wf->mutex)rw_exit_write(&wf->mutex); |
| 225 | flush_delayed_work(&wf->work); |
| 226 | } |
| 227 | |
| 228 | /** |
| 229 | * intel_wakeref_is_active: Query whether the wakeref is currently held |
| 230 | * @wf: the wakeref |
| 231 | * |
| 232 | * Returns: true if the wakeref is currently held. |
| 233 | */ |
| 234 | static inline bool_Bool |
| 235 | intel_wakeref_is_active(const struct intel_wakeref *wf) |
| 236 | { |
| 237 | return READ_ONCE(wf->wakeref)({ typeof(wf->wakeref) __tmp = *(volatile typeof(wf->wakeref ) *)&(wf->wakeref); membar_datadep_consumer(); __tmp; } ); |
| 238 | } |
| 239 | |
| 240 | /** |
| 241 | * __intel_wakeref_defer_park: Defer the current park callback |
| 242 | * @wf: the wakeref |
| 243 | */ |
| 244 | static inline void |
| 245 | __intel_wakeref_defer_park(struct intel_wakeref *wf) |
| 246 | { |
| 247 | lockdep_assert_held(&wf->mutex)do { (void)(&wf->mutex); } while(0); |
| 248 | INTEL_WAKEREF_BUG_ON(atomic_read(&wf->count))((void)0); |
| 249 | atomic_set_release(&wf->count, 1)({ typeof(*((&wf->count))) __tmp = (((1))); *(volatile typeof(*((&wf->count))) *)&(*((&wf->count) )) = __tmp; __tmp; }); |
| 250 | } |
| 251 | |
| 252 | /** |
| 253 | * intel_wakeref_wait_for_idle: Wait until the wakeref is idle |
| 254 | * @wf: the wakeref |
| 255 | * |
| 256 | * Wait for the earlier asynchronous release of the wakeref. Note |
| 257 | * this will wait for any third party as well, so make sure you only wait |
| 258 | * when you have control over the wakeref and trust no one else is acquiring |
| 259 | * it. |
| 260 | * |
| 261 | * Return: 0 on success, error code if killed. |
| 262 | */ |
| 263 | int intel_wakeref_wait_for_idle(struct intel_wakeref *wf); |
| 264 | |
| 265 | struct intel_wakeref_auto { |
| 266 | struct intel_runtime_pm *rpm; |
| 267 | struct timeout timer; |
| 268 | intel_wakeref_t wakeref; |
| 269 | spinlock_t lock; |
| 270 | refcount_t count; |
| 271 | }; |
| 272 | |
| 273 | /** |
| 274 | * intel_wakeref_auto: Delay the runtime-pm autosuspend |
| 275 | * @wf: the wakeref |
| 276 | * @timeout: relative timeout in jiffies |
| 277 | * |
| 278 | * The runtime-pm core uses a suspend delay after the last wakeref |
| 279 | * is released before triggering runtime suspend of the device. That |
| 280 | * delay is configurable via sysfs with little regard to the device |
| 281 | * characteristics. Instead, we want to tune the autosuspend based on our |
| 282 | * HW knowledge. intel_wakeref_auto() delays the sleep by the supplied |
| 283 | * timeout. |
| 284 | * |
| 285 | * Pass @timeout = 0 to cancel a previous autosuspend by executing the |
| 286 | * suspend immediately. |
| 287 | */ |
| 288 | void intel_wakeref_auto(struct intel_wakeref_auto *wf, unsigned long timeout); |
| 289 | |
| 290 | void intel_wakeref_auto_init(struct intel_wakeref_auto *wf, |
| 291 | struct intel_runtime_pm *rpm); |
| 292 | void intel_wakeref_auto_fini(struct intel_wakeref_auto *wf); |
| 293 | |
| 294 | #endif /* INTEL_WAKEREF_H */ |