1 /* 2 * SPDX-License-Identifier: MIT 3 * 4 * Copyright © 2019 Intel Corporation 5 */ 6 7 #include <linux/workqueue.h> 8 9 #include "i915_drv.h" /* for_each_engine() */ 10 #include "i915_request.h" 11 #include "intel_engine_heartbeat.h" 12 #include "intel_gt.h" 13 #include "intel_gt_pm.h" 14 #include "intel_gt_requests.h" 15 #include "intel_timeline.h" 16 17 static bool retire_requests(struct intel_timeline *tl) 18 { 19 struct i915_request *rq, *rn; 20 21 list_for_each_entry_safe(rq, rn, &tl->requests, link) 22 if (!i915_request_retire(rq)) 23 return false; 24 25 /* And check nothing new was submitted */ 26 return !i915_active_fence_isset(&tl->last_request); 27 } 28 29 static bool flush_submission(struct intel_gt *gt) 30 { 31 struct intel_engine_cs *engine; 32 enum intel_engine_id id; 33 bool active = false; 34 35 if (!intel_gt_pm_is_awake(gt)) 36 return false; 37 38 for_each_engine(engine, gt, id) { 39 intel_engine_flush_submission(engine); 40 active |= flush_work(&engine->retire_work); 41 active |= flush_work(&engine->wakeref.work); 42 } 43 44 return active; 45 } 46 47 static void engine_retire(struct work_struct *work) 48 { 49 struct intel_engine_cs *engine = 50 container_of(work, typeof(*engine), retire_work); 51 struct intel_timeline *tl = xchg(&engine->retire, NULL); 52 53 do { 54 struct intel_timeline *next = xchg(&tl->retire, NULL); 55 56 /* 57 * Our goal here is to retire _idle_ timelines as soon as 58 * possible (as they are idle, we do not expect userspace 59 * to be cleaning up anytime soon). 60 * 61 * If the timeline is currently locked, either it is being 62 * retired elsewhere or about to be! 63 */ 64 if (mutex_trylock(&tl->mutex)) { 65 retire_requests(tl); 66 mutex_unlock(&tl->mutex); 67 } 68 intel_timeline_put(tl); 69 70 GEM_BUG_ON(!next); 71 tl = ptr_mask_bits(next, 1); 72 } while (tl); 73 } 74 75 static bool add_retire(struct intel_engine_cs *engine, 76 struct intel_timeline *tl) 77 { 78 #define STUB ((struct intel_timeline *)1) 79 struct intel_timeline *first; 80 81 /* 82 * We open-code a llist here to include the additional tag [BIT(0)] 83 * so that we know when the timeline is already on a 84 * retirement queue: either this engine or another. 85 */ 86 87 if (cmpxchg(&tl->retire, NULL, STUB)) /* already queued */ 88 return false; 89 90 intel_timeline_get(tl); 91 first = READ_ONCE(engine->retire); 92 do 93 tl->retire = ptr_pack_bits(first, 1, 1); 94 while (!try_cmpxchg(&engine->retire, &first, tl)); 95 96 return !first; 97 } 98 99 void intel_engine_add_retire(struct intel_engine_cs *engine, 100 struct intel_timeline *tl) 101 { 102 /* We don't deal well with the engine disappearing beneath us */ 103 GEM_BUG_ON(intel_engine_is_virtual(engine)); 104 105 if (add_retire(engine, tl)) 106 schedule_work(&engine->retire_work); 107 } 108 109 void intel_engine_init_retire(struct intel_engine_cs *engine) 110 { 111 INIT_WORK(&engine->retire_work, engine_retire); 112 } 113 114 void intel_engine_fini_retire(struct intel_engine_cs *engine) 115 { 116 flush_work(&engine->retire_work); 117 GEM_BUG_ON(engine->retire); 118 } 119 120 long intel_gt_retire_requests_timeout(struct intel_gt *gt, long timeout) 121 { 122 struct intel_gt_timelines *timelines = >->timelines; 123 struct intel_timeline *tl, *tn; 124 unsigned long active_count = 0; 125 bool interruptible; 126 LIST_HEAD(free); 127 128 interruptible = true; 129 if (unlikely(timeout < 0)) 130 timeout = -timeout, interruptible = false; 131 132 flush_submission(gt); /* kick the ksoftirqd tasklets */ 133 spin_lock(&timelines->lock); 134 list_for_each_entry_safe(tl, tn, &timelines->active_list, link) { 135 if (!mutex_trylock(&tl->mutex)) { 136 active_count++; /* report busy to caller, try again? */ 137 continue; 138 } 139 140 intel_timeline_get(tl); 141 GEM_BUG_ON(!atomic_read(&tl->active_count)); 142 atomic_inc(&tl->active_count); /* pin the list element */ 143 spin_unlock(&timelines->lock); 144 145 if (timeout > 0) { 146 struct dma_fence *fence; 147 148 fence = i915_active_fence_get(&tl->last_request); 149 if (fence) { 150 mutex_unlock(&tl->mutex); 151 152 timeout = dma_fence_wait_timeout(fence, 153 interruptible, 154 timeout); 155 dma_fence_put(fence); 156 157 /* Retirement is best effort */ 158 if (!mutex_trylock(&tl->mutex)) { 159 active_count++; 160 goto out_active; 161 } 162 } 163 } 164 165 if (!retire_requests(tl) || flush_submission(gt)) 166 active_count++; 167 mutex_unlock(&tl->mutex); 168 169 out_active: spin_lock(&timelines->lock); 170 171 /* Resume list iteration after reacquiring spinlock */ 172 list_safe_reset_next(tl, tn, link); 173 if (atomic_dec_and_test(&tl->active_count)) 174 list_del(&tl->link); 175 176 177 /* Defer the final release to after the spinlock */ 178 if (refcount_dec_and_test(&tl->kref.refcount)) { 179 GEM_BUG_ON(atomic_read(&tl->active_count)); 180 list_add(&tl->link, &free); 181 } 182 } 183 spin_unlock(&timelines->lock); 184 185 list_for_each_entry_safe(tl, tn, &free, link) 186 __intel_timeline_free(&tl->kref); 187 188 return active_count ? timeout : 0; 189 } 190 191 int intel_gt_wait_for_idle(struct intel_gt *gt, long timeout) 192 { 193 /* If the device is asleep, we have no requests outstanding */ 194 if (!intel_gt_pm_is_awake(gt)) 195 return 0; 196 197 while ((timeout = intel_gt_retire_requests_timeout(gt, timeout)) > 0) { 198 cond_resched(); 199 if (signal_pending(current)) 200 return -EINTR; 201 } 202 203 return timeout; 204 } 205 206 static void retire_work_handler(struct work_struct *work) 207 { 208 struct intel_gt *gt = 209 container_of(work, typeof(*gt), requests.retire_work.work); 210 211 schedule_delayed_work(>->requests.retire_work, 212 round_jiffies_up_relative(HZ)); 213 intel_gt_retire_requests(gt); 214 } 215 216 void intel_gt_init_requests(struct intel_gt *gt) 217 { 218 INIT_DELAYED_WORK(>->requests.retire_work, retire_work_handler); 219 } 220 221 void intel_gt_park_requests(struct intel_gt *gt) 222 { 223 cancel_delayed_work(>->requests.retire_work); 224 } 225 226 void intel_gt_unpark_requests(struct intel_gt *gt) 227 { 228 schedule_delayed_work(>->requests.retire_work, 229 round_jiffies_up_relative(HZ)); 230 } 231 232 void intel_gt_fini_requests(struct intel_gt *gt) 233 { 234 /* Wait until the work is marked as finished before unloading! */ 235 cancel_delayed_work_sync(>->requests.retire_work); 236 } 237