1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2019 Intel Corporation 4 */ 5 6 #include "intel_context.h" 7 #include "intel_engine_pm.h" 8 #include "intel_gpu_commands.h" 9 #include "intel_gt_requests.h" 10 #include "intel_ring.h" 11 #include "intel_rps.h" 12 #include "selftest_rc6.h" 13 14 #include "selftests/i915_random.h" 15 #include "selftests/librapl.h" 16 17 static u64 rc6_residency(struct intel_rc6 *rc6) 18 { 19 u64 result; 20 21 /* XXX VLV_GT_MEDIA_RC6? */ 22 23 result = intel_rc6_residency_ns(rc6, INTEL_RC6_RES_RC6); 24 if (HAS_RC6p(rc6_to_i915(rc6))) 25 result += intel_rc6_residency_ns(rc6, INTEL_RC6_RES_RC6p); 26 if (HAS_RC6pp(rc6_to_i915(rc6))) 27 result += intel_rc6_residency_ns(rc6, INTEL_RC6_RES_RC6pp); 28 29 return result; 30 } 31 32 int live_rc6_manual(void *arg) 33 { 34 struct intel_gt *gt = arg; 35 struct intel_rc6 *rc6 = >->rc6; 36 u64 rc0_power, rc6_power; 37 intel_wakeref_t wakeref; 38 bool has_power; 39 ktime_t dt; 40 u64 res[2]; 41 int err = 0; 42 u32 rc0_freq = 0; 43 u32 rc6_freq = 0; 44 struct intel_rps *rps = >->rps; 45 46 /* 47 * Our claim is that we can "encourage" the GPU to enter rc6 at will. 48 * Let's try it! 49 */ 50 51 if (!rc6->enabled) 52 return 0; 53 54 /* bsw/byt use a PCU and decouple RC6 from our manual control */ 55 if (IS_VALLEYVIEW(gt->i915) || IS_CHERRYVIEW(gt->i915)) 56 return 0; 57 58 has_power = librapl_supported(gt->i915); 59 wakeref = intel_runtime_pm_get(gt->uncore->rpm); 60 61 /* Force RC6 off for starters */ 62 __intel_rc6_disable(rc6); 63 msleep(1); /* wakeup is not immediate, takes about 100us on icl */ 64 65 res[0] = rc6_residency(rc6); 66 67 dt = ktime_get(); 68 rc0_power = librapl_energy_uJ(); 69 msleep(1000); 70 rc0_power = librapl_energy_uJ() - rc0_power; 71 dt = ktime_sub(ktime_get(), dt); 72 res[1] = rc6_residency(rc6); 73 rc0_freq = intel_rps_read_actual_frequency_fw(rps); 74 if ((res[1] - res[0]) >> 10) { 75 pr_err("RC6 residency increased by %lldus while disabled for 1000ms!\n", 76 (res[1] - res[0]) >> 10); 77 err = -EINVAL; 78 goto out_unlock; 79 } 80 81 if (has_power) { 82 rc0_power = div64_u64(NSEC_PER_SEC * rc0_power, 83 ktime_to_ns(dt)); 84 if (!rc0_power) { 85 if (rc0_freq) 86 pr_debug("No power measured while in RC0! GPU Freq: %u in RC0\n", 87 rc0_freq); 88 else 89 pr_err("No power and freq measured while in RC0\n"); 90 err = -EINVAL; 91 goto out_unlock; 92 } 93 } 94 95 /* Manually enter RC6 */ 96 intel_rc6_park(rc6); 97 98 res[0] = rc6_residency(rc6); 99 intel_uncore_forcewake_flush(rc6_to_uncore(rc6), FORCEWAKE_ALL); 100 dt = ktime_get(); 101 rc6_power = librapl_energy_uJ(); 102 msleep(1000); 103 rc6_freq = intel_rps_read_actual_frequency_fw(rps); 104 rc6_power = librapl_energy_uJ() - rc6_power; 105 dt = ktime_sub(ktime_get(), dt); 106 res[1] = rc6_residency(rc6); 107 if (res[1] == res[0]) { 108 pr_err("Did not enter RC6! RC6_STATE=%08x, RC6_CONTROL=%08x, residency=%lld\n", 109 intel_uncore_read_fw(gt->uncore, GEN6_RC_STATE), 110 intel_uncore_read_fw(gt->uncore, GEN6_RC_CONTROL), 111 res[0]); 112 err = -EINVAL; 113 } 114 115 if (has_power) { 116 rc6_power = div64_u64(NSEC_PER_SEC * rc6_power, 117 ktime_to_ns(dt)); 118 pr_info("GPU consumed %llduW in RC0 and %llduW in RC6\n", 119 rc0_power, rc6_power); 120 if (2 * rc6_power > rc0_power) { 121 pr_err("GPU leaked energy while in RC6! GPU Freq: %u in RC6 and %u in RC0\n", 122 rc6_freq, rc0_freq); 123 err = -EINVAL; 124 goto out_unlock; 125 } 126 } 127 128 /* Restore what should have been the original state! */ 129 intel_rc6_unpark(rc6); 130 131 out_unlock: 132 intel_runtime_pm_put(gt->uncore->rpm, wakeref); 133 return err; 134 } 135 136 static const u32 *__live_rc6_ctx(struct intel_context *ce) 137 { 138 struct i915_request *rq; 139 const u32 *result; 140 u32 cmd; 141 u32 *cs; 142 143 rq = intel_context_create_request(ce); 144 if (IS_ERR(rq)) 145 return ERR_CAST(rq); 146 147 cs = intel_ring_begin(rq, 4); 148 if (IS_ERR(cs)) { 149 i915_request_add(rq); 150 return cs; 151 } 152 153 cmd = MI_STORE_REGISTER_MEM | MI_USE_GGTT; 154 if (GRAPHICS_VER(rq->i915) >= 8) 155 cmd++; 156 157 *cs++ = cmd; 158 *cs++ = i915_mmio_reg_offset(GEN8_RC6_CTX_INFO); 159 *cs++ = ce->timeline->hwsp_offset + 8; 160 *cs++ = 0; 161 intel_ring_advance(rq, cs); 162 163 result = rq->hwsp_seqno + 2; 164 i915_request_add(rq); 165 166 return result; 167 } 168 169 static struct intel_engine_cs ** 170 randomised_engines(struct intel_gt *gt, 171 struct rnd_state *prng, 172 unsigned int *count) 173 { 174 struct intel_engine_cs *engine, **engines; 175 enum intel_engine_id id; 176 int n; 177 178 n = 0; 179 for_each_engine(engine, gt, id) 180 n++; 181 if (!n) 182 return NULL; 183 184 engines = kmalloc_array(n, sizeof(*engines), GFP_KERNEL); 185 if (!engines) 186 return NULL; 187 188 n = 0; 189 for_each_engine(engine, gt, id) 190 engines[n++] = engine; 191 192 i915_prandom_shuffle(engines, sizeof(*engines), n, prng); 193 194 *count = n; 195 return engines; 196 } 197 198 int live_rc6_ctx_wa(void *arg) 199 { 200 struct intel_gt *gt = arg; 201 struct intel_engine_cs **engines; 202 unsigned int n, count; 203 I915_RND_STATE(prng); 204 int err = 0; 205 206 /* A read of CTX_INFO upsets rc6. Poke the bear! */ 207 if (GRAPHICS_VER(gt->i915) < 8) 208 return 0; 209 210 engines = randomised_engines(gt, &prng, &count); 211 if (!engines) 212 return 0; 213 214 for (n = 0; n < count; n++) { 215 struct intel_engine_cs *engine = engines[n]; 216 int pass; 217 218 for (pass = 0; pass < 2; pass++) { 219 struct i915_gpu_error *error = >->i915->gpu_error; 220 struct intel_context *ce; 221 unsigned int resets = 222 i915_reset_engine_count(error, engine); 223 const u32 *res; 224 225 /* Use a sacrifical context */ 226 ce = intel_context_create(engine); 227 if (IS_ERR(ce)) { 228 err = PTR_ERR(ce); 229 goto out; 230 } 231 232 intel_engine_pm_get(engine); 233 res = __live_rc6_ctx(ce); 234 intel_engine_pm_put(engine); 235 intel_context_put(ce); 236 if (IS_ERR(res)) { 237 err = PTR_ERR(res); 238 goto out; 239 } 240 241 if (intel_gt_wait_for_idle(gt, HZ / 5) == -ETIME) { 242 intel_gt_set_wedged(gt); 243 err = -ETIME; 244 goto out; 245 } 246 247 intel_gt_pm_wait_for_idle(gt); 248 pr_debug("%s: CTX_INFO=%0x\n", 249 engine->name, READ_ONCE(*res)); 250 251 if (resets != 252 i915_reset_engine_count(error, engine)) { 253 pr_err("%s: GPU reset required\n", 254 engine->name); 255 add_taint_for_CI(gt->i915, TAINT_WARN); 256 err = -EIO; 257 goto out; 258 } 259 } 260 } 261 262 out: 263 kfree(engines); 264 return err; 265 } 266