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