1 /* 2 * SPDX-License-Identifier: MIT 3 * 4 * Copyright © 2019 Intel Corporation 5 */ 6 7 #include "intel_runtime_pm.h" 8 #include "i915_gem.h" 9 10 static void rpm_get(struct intel_runtime_pm *rpm, struct intel_wakeref *wf) 11 { 12 wf->wakeref = intel_runtime_pm_get(rpm); 13 } 14 15 static void rpm_put(struct intel_runtime_pm *rpm, struct intel_wakeref *wf) 16 { 17 intel_wakeref_t wakeref = fetch_and_zero(&wf->wakeref); 18 19 intel_runtime_pm_put(rpm, wakeref); 20 GEM_BUG_ON(!wakeref); 21 } 22 23 int __intel_wakeref_get_first(struct intel_runtime_pm *rpm, 24 struct intel_wakeref *wf, 25 int (*fn)(struct intel_wakeref *wf)) 26 { 27 /* 28 * Treat get/put as different subclasses, as we may need to run 29 * the put callback from under the shrinker and do not want to 30 * cross-contanimate that callback with any extra work performed 31 * upon acquiring the wakeref. 32 */ 33 mutex_lock_nested(&wf->mutex, SINGLE_DEPTH_NESTING); 34 if (!atomic_read(&wf->count)) { 35 int err; 36 37 rpm_get(rpm, wf); 38 39 err = fn(wf); 40 if (unlikely(err)) { 41 rpm_put(rpm, wf); 42 mutex_unlock(&wf->mutex); 43 return err; 44 } 45 46 smp_mb__before_atomic(); /* release wf->count */ 47 } 48 atomic_inc(&wf->count); 49 mutex_unlock(&wf->mutex); 50 51 return 0; 52 } 53 54 int __intel_wakeref_put_last(struct intel_runtime_pm *rpm, 55 struct intel_wakeref *wf, 56 int (*fn)(struct intel_wakeref *wf)) 57 { 58 int err; 59 60 err = fn(wf); 61 if (likely(!err)) 62 rpm_put(rpm, wf); 63 else 64 atomic_inc(&wf->count); 65 mutex_unlock(&wf->mutex); 66 67 return err; 68 } 69 70 void __intel_wakeref_init(struct intel_wakeref *wf, struct lock_class_key *key) 71 { 72 __mutex_init(&wf->mutex, "wakeref", key); 73 atomic_set(&wf->count, 0); 74 wf->wakeref = 0; 75 } 76 77 static void wakeref_auto_timeout(struct timer_list *t) 78 { 79 struct intel_wakeref_auto *wf = from_timer(wf, t, timer); 80 intel_wakeref_t wakeref; 81 unsigned long flags; 82 83 if (!refcount_dec_and_lock_irqsave(&wf->count, &wf->lock, &flags)) 84 return; 85 86 wakeref = fetch_and_zero(&wf->wakeref); 87 spin_unlock_irqrestore(&wf->lock, flags); 88 89 intel_runtime_pm_put(wf->rpm, wakeref); 90 } 91 92 void intel_wakeref_auto_init(struct intel_wakeref_auto *wf, 93 struct intel_runtime_pm *rpm) 94 { 95 spin_lock_init(&wf->lock); 96 timer_setup(&wf->timer, wakeref_auto_timeout, 0); 97 refcount_set(&wf->count, 0); 98 wf->wakeref = 0; 99 wf->rpm = rpm; 100 } 101 102 void intel_wakeref_auto(struct intel_wakeref_auto *wf, unsigned long timeout) 103 { 104 unsigned long flags; 105 106 if (!timeout) { 107 if (del_timer_sync(&wf->timer)) 108 wakeref_auto_timeout(&wf->timer); 109 return; 110 } 111 112 /* Our mission is that we only extend an already active wakeref */ 113 assert_rpm_wakelock_held(wf->rpm); 114 115 if (!refcount_inc_not_zero(&wf->count)) { 116 spin_lock_irqsave(&wf->lock, flags); 117 if (!refcount_inc_not_zero(&wf->count)) { 118 GEM_BUG_ON(wf->wakeref); 119 wf->wakeref = intel_runtime_pm_get_if_in_use(wf->rpm); 120 refcount_set(&wf->count, 1); 121 } 122 spin_unlock_irqrestore(&wf->lock, flags); 123 } 124 125 /* 126 * If we extend a pending timer, we will only get a single timer 127 * callback and so need to cancel the local inc by running the 128 * elided callback to keep the wf->count balanced. 129 */ 130 if (mod_timer(&wf->timer, jiffies + timeout)) 131 wakeref_auto_timeout(&wf->timer); 132 } 133 134 void intel_wakeref_auto_fini(struct intel_wakeref_auto *wf) 135 { 136 intel_wakeref_auto(wf, 0); 137 GEM_BUG_ON(wf->wakeref); 138 } 139