162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * SPDX-License-Identifier: MIT 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright © 2019 Intel Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/wait_bit.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "intel_runtime_pm.h" 1062306a36Sopenharmony_ci#include "intel_wakeref.h" 1162306a36Sopenharmony_ci#include "i915_drv.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic void rpm_get(struct intel_wakeref *wf) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci wf->wakeref = intel_runtime_pm_get(&wf->i915->runtime_pm); 1662306a36Sopenharmony_ci} 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic void rpm_put(struct intel_wakeref *wf) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci intel_wakeref_t wakeref = fetch_and_zero(&wf->wakeref); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci intel_runtime_pm_put(&wf->i915->runtime_pm, wakeref); 2362306a36Sopenharmony_ci INTEL_WAKEREF_BUG_ON(!wakeref); 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciint __intel_wakeref_get_first(struct intel_wakeref *wf) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci /* 2962306a36Sopenharmony_ci * Treat get/put as different subclasses, as we may need to run 3062306a36Sopenharmony_ci * the put callback from under the shrinker and do not want to 3162306a36Sopenharmony_ci * cross-contanimate that callback with any extra work performed 3262306a36Sopenharmony_ci * upon acquiring the wakeref. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci mutex_lock_nested(&wf->mutex, SINGLE_DEPTH_NESTING); 3562306a36Sopenharmony_ci if (!atomic_read(&wf->count)) { 3662306a36Sopenharmony_ci int err; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci rpm_get(wf); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci err = wf->ops->get(wf); 4162306a36Sopenharmony_ci if (unlikely(err)) { 4262306a36Sopenharmony_ci rpm_put(wf); 4362306a36Sopenharmony_ci mutex_unlock(&wf->mutex); 4462306a36Sopenharmony_ci return err; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci smp_mb__before_atomic(); /* release wf->count */ 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci atomic_inc(&wf->count); 5062306a36Sopenharmony_ci mutex_unlock(&wf->mutex); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci INTEL_WAKEREF_BUG_ON(atomic_read(&wf->count) <= 0); 5362306a36Sopenharmony_ci return 0; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void ____intel_wakeref_put_last(struct intel_wakeref *wf) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci INTEL_WAKEREF_BUG_ON(atomic_read(&wf->count) <= 0); 5962306a36Sopenharmony_ci if (unlikely(!atomic_dec_and_test(&wf->count))) 6062306a36Sopenharmony_ci goto unlock; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* ops->put() must reschedule its own release on error/deferral */ 6362306a36Sopenharmony_ci if (likely(!wf->ops->put(wf))) { 6462306a36Sopenharmony_ci rpm_put(wf); 6562306a36Sopenharmony_ci wake_up_var(&wf->wakeref); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ciunlock: 6962306a36Sopenharmony_ci mutex_unlock(&wf->mutex); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_civoid __intel_wakeref_put_last(struct intel_wakeref *wf, unsigned long flags) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci INTEL_WAKEREF_BUG_ON(delayed_work_pending(&wf->work)); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Assume we are not in process context and so cannot sleep. */ 7762306a36Sopenharmony_ci if (flags & INTEL_WAKEREF_PUT_ASYNC || !mutex_trylock(&wf->mutex)) { 7862306a36Sopenharmony_ci mod_delayed_work(wf->i915->unordered_wq, &wf->work, 7962306a36Sopenharmony_ci FIELD_GET(INTEL_WAKEREF_PUT_DELAY, flags)); 8062306a36Sopenharmony_ci return; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci ____intel_wakeref_put_last(wf); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void __intel_wakeref_put_work(struct work_struct *wrk) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct intel_wakeref *wf = container_of(wrk, typeof(*wf), work.work); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (atomic_add_unless(&wf->count, -1, 1)) 9162306a36Sopenharmony_ci return; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci mutex_lock(&wf->mutex); 9462306a36Sopenharmony_ci ____intel_wakeref_put_last(wf); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_civoid __intel_wakeref_init(struct intel_wakeref *wf, 9862306a36Sopenharmony_ci struct drm_i915_private *i915, 9962306a36Sopenharmony_ci const struct intel_wakeref_ops *ops, 10062306a36Sopenharmony_ci struct intel_wakeref_lockclass *key) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci wf->i915 = i915; 10362306a36Sopenharmony_ci wf->ops = ops; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci __mutex_init(&wf->mutex, "wakeref.mutex", &key->mutex); 10662306a36Sopenharmony_ci atomic_set(&wf->count, 0); 10762306a36Sopenharmony_ci wf->wakeref = 0; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci INIT_DELAYED_WORK(&wf->work, __intel_wakeref_put_work); 11062306a36Sopenharmony_ci lockdep_init_map(&wf->work.work.lockdep_map, 11162306a36Sopenharmony_ci "wakeref.work", &key->work, 0); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ciint intel_wakeref_wait_for_idle(struct intel_wakeref *wf) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci int err; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci might_sleep(); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci err = wait_var_event_killable(&wf->wakeref, 12162306a36Sopenharmony_ci !intel_wakeref_is_active(wf)); 12262306a36Sopenharmony_ci if (err) 12362306a36Sopenharmony_ci return err; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci intel_wakeref_unlock_wait(wf); 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void wakeref_auto_timeout(struct timer_list *t) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct intel_wakeref_auto *wf = from_timer(wf, t, timer); 13262306a36Sopenharmony_ci intel_wakeref_t wakeref; 13362306a36Sopenharmony_ci unsigned long flags; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (!refcount_dec_and_lock_irqsave(&wf->count, &wf->lock, &flags)) 13662306a36Sopenharmony_ci return; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci wakeref = fetch_and_zero(&wf->wakeref); 13962306a36Sopenharmony_ci spin_unlock_irqrestore(&wf->lock, flags); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci intel_runtime_pm_put(&wf->i915->runtime_pm, wakeref); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_civoid intel_wakeref_auto_init(struct intel_wakeref_auto *wf, 14562306a36Sopenharmony_ci struct drm_i915_private *i915) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci spin_lock_init(&wf->lock); 14862306a36Sopenharmony_ci timer_setup(&wf->timer, wakeref_auto_timeout, 0); 14962306a36Sopenharmony_ci refcount_set(&wf->count, 0); 15062306a36Sopenharmony_ci wf->wakeref = 0; 15162306a36Sopenharmony_ci wf->i915 = i915; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_civoid intel_wakeref_auto(struct intel_wakeref_auto *wf, unsigned long timeout) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci unsigned long flags; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!timeout) { 15962306a36Sopenharmony_ci if (del_timer_sync(&wf->timer)) 16062306a36Sopenharmony_ci wakeref_auto_timeout(&wf->timer); 16162306a36Sopenharmony_ci return; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* Our mission is that we only extend an already active wakeref */ 16562306a36Sopenharmony_ci assert_rpm_wakelock_held(&wf->i915->runtime_pm); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (!refcount_inc_not_zero(&wf->count)) { 16862306a36Sopenharmony_ci spin_lock_irqsave(&wf->lock, flags); 16962306a36Sopenharmony_ci if (!refcount_inc_not_zero(&wf->count)) { 17062306a36Sopenharmony_ci INTEL_WAKEREF_BUG_ON(wf->wakeref); 17162306a36Sopenharmony_ci wf->wakeref = 17262306a36Sopenharmony_ci intel_runtime_pm_get_if_in_use(&wf->i915->runtime_pm); 17362306a36Sopenharmony_ci refcount_set(&wf->count, 1); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci spin_unlock_irqrestore(&wf->lock, flags); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * If we extend a pending timer, we will only get a single timer 18062306a36Sopenharmony_ci * callback and so need to cancel the local inc by running the 18162306a36Sopenharmony_ci * elided callback to keep the wf->count balanced. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci if (mod_timer(&wf->timer, jiffies + timeout)) 18462306a36Sopenharmony_ci wakeref_auto_timeout(&wf->timer); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_civoid intel_wakeref_auto_fini(struct intel_wakeref_auto *wf) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci intel_wakeref_auto(wf, 0); 19062306a36Sopenharmony_ci INTEL_WAKEREF_BUG_ON(wf->wakeref); 19162306a36Sopenharmony_ci} 192