1 /* 2 * SPDX-License-Identifier: MIT 3 * 4 * Copyright © 2019 Intel Corporation 5 */ 6 7 #include "intel_context.h" 8 #include "intel_engine_pm.h" 9 #include "intel_gpu_commands.h" 10 #include "intel_gt_requests.h" 11 #include "intel_ring.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, GEN6_GT_GFX_RC6); 24 if (HAS_RC6p(rc6_to_i915(rc6))) 25 result += intel_rc6_residency_ns(rc6, GEN6_GT_GFX_RC6p); 26 if (HAS_RC6pp(rc6_to_i915(rc6))) 27 result += intel_rc6_residency_ns(rc6, GEN6_GT_GFX_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 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 wakeref = intel_runtime_pm_get(gt->uncore->rpm); 55 56 /* Force RC6 off for starters */ 57 __intel_rc6_disable(rc6); 58 msleep(1); /* wakeup is not immediate, takes about 100us on icl */ 59 60 res[0] = rc6_residency(rc6); 61 62 dt = ktime_get(); 63 rc0_power = librapl_energy_uJ(); 64 msleep(250); 65 rc0_power = librapl_energy_uJ() - rc0_power; 66 dt = ktime_sub(ktime_get(), dt); 67 res[1] = rc6_residency(rc6); 68 if ((res[1] - res[0]) >> 10) { 69 pr_err("RC6 residency increased by %lldus while disabled for 250ms!\n", 70 (res[1] - res[0]) >> 10); 71 err = -EINVAL; 72 goto out_unlock; 73 } 74 75 rc0_power = div64_u64(NSEC_PER_SEC * rc0_power, ktime_to_ns(dt)); 76 if (!rc0_power) { 77 pr_err("No power measured while in RC0\n"); 78 err = -EINVAL; 79 goto out_unlock; 80 } 81 82 /* Manually enter RC6 */ 83 intel_rc6_park(rc6); 84 85 res[0] = rc6_residency(rc6); 86 intel_uncore_forcewake_flush(rc6_to_uncore(rc6), FORCEWAKE_ALL); 87 dt = ktime_get(); 88 rc6_power = librapl_energy_uJ(); 89 msleep(100); 90 rc6_power = librapl_energy_uJ() - rc6_power; 91 dt = ktime_sub(ktime_get(), dt); 92 res[1] = rc6_residency(rc6); 93 if (res[1] == res[0]) { 94 pr_err("Did not enter RC6! RC6_STATE=%08x, RC6_CONTROL=%08x, residency=%lld\n", 95 intel_uncore_read_fw(gt->uncore, GEN6_RC_STATE), 96 intel_uncore_read_fw(gt->uncore, GEN6_RC_CONTROL), 97 res[0]); 98 err = -EINVAL; 99 } 100 101 rc6_power = div64_u64(NSEC_PER_SEC * rc6_power, ktime_to_ns(dt)); 102 pr_info("GPU consumed %llduW in RC0 and %llduW in RC6\n", 103 rc0_power, rc6_power); 104 if (2 * rc6_power > rc0_power) { 105 pr_err("GPU leaked energy while in RC6!\n"); 106 err = -EINVAL; 107 goto out_unlock; 108 } 109 110 /* Restore what should have been the original state! */ 111 intel_rc6_unpark(rc6); 112 113 out_unlock: 114 intel_runtime_pm_put(gt->uncore->rpm, wakeref); 115 return err; 116 } 117 118 static const u32 *__live_rc6_ctx(struct intel_context *ce) 119 { 120 struct i915_request *rq; 121 const u32 *result; 122 u32 cmd; 123 u32 *cs; 124 125 rq = intel_context_create_request(ce); 126 if (IS_ERR(rq)) 127 return ERR_CAST(rq); 128 129 cs = intel_ring_begin(rq, 4); 130 if (IS_ERR(cs)) { 131 i915_request_add(rq); 132 return cs; 133 } 134 135 cmd = MI_STORE_REGISTER_MEM | MI_USE_GGTT; 136 if (INTEL_GEN(rq->engine->i915) >= 8) 137 cmd++; 138 139 *cs++ = cmd; 140 *cs++ = i915_mmio_reg_offset(GEN8_RC6_CTX_INFO); 141 *cs++ = ce->timeline->hwsp_offset + 8; 142 *cs++ = 0; 143 intel_ring_advance(rq, cs); 144 145 result = rq->hwsp_seqno + 2; 146 i915_request_add(rq); 147 148 return result; 149 } 150 151 static struct intel_engine_cs ** 152 randomised_engines(struct intel_gt *gt, 153 struct rnd_state *prng, 154 unsigned int *count) 155 { 156 struct intel_engine_cs *engine, **engines; 157 enum intel_engine_id id; 158 int n; 159 160 n = 0; 161 for_each_engine(engine, gt, id) 162 n++; 163 if (!n) 164 return NULL; 165 166 engines = kmalloc_array(n, sizeof(*engines), GFP_KERNEL); 167 if (!engines) 168 return NULL; 169 170 n = 0; 171 for_each_engine(engine, gt, id) 172 engines[n++] = engine; 173 174 i915_prandom_shuffle(engines, sizeof(*engines), n, prng); 175 176 *count = n; 177 return engines; 178 } 179 180 int live_rc6_ctx_wa(void *arg) 181 { 182 struct intel_gt *gt = arg; 183 struct intel_engine_cs **engines; 184 unsigned int n, count; 185 I915_RND_STATE(prng); 186 int err = 0; 187 188 /* A read of CTX_INFO upsets rc6. Poke the bear! */ 189 if (INTEL_GEN(gt->i915) < 8) 190 return 0; 191 192 engines = randomised_engines(gt, &prng, &count); 193 if (!engines) 194 return 0; 195 196 for (n = 0; n < count; n++) { 197 struct intel_engine_cs *engine = engines[n]; 198 int pass; 199 200 for (pass = 0; pass < 2; pass++) { 201 struct i915_gpu_error *error = >->i915->gpu_error; 202 struct intel_context *ce; 203 unsigned int resets = 204 i915_reset_engine_count(error, engine); 205 const u32 *res; 206 207 /* Use a sacrifical context */ 208 ce = intel_context_create(engine); 209 if (IS_ERR(ce)) { 210 err = PTR_ERR(ce); 211 goto out; 212 } 213 214 intel_engine_pm_get(engine); 215 res = __live_rc6_ctx(ce); 216 intel_engine_pm_put(engine); 217 intel_context_put(ce); 218 if (IS_ERR(res)) { 219 err = PTR_ERR(res); 220 goto out; 221 } 222 223 if (intel_gt_wait_for_idle(gt, HZ / 5) == -ETIME) { 224 intel_gt_set_wedged(gt); 225 err = -ETIME; 226 goto out; 227 } 228 229 intel_gt_pm_wait_for_idle(gt); 230 pr_debug("%s: CTX_INFO=%0x\n", 231 engine->name, READ_ONCE(*res)); 232 233 if (resets != 234 i915_reset_engine_count(error, engine)) { 235 pr_err("%s: GPU reset required\n", 236 engine->name); 237 add_taint_for_CI(gt->i915, TAINT_WARN); 238 err = -EIO; 239 goto out; 240 } 241 } 242 } 243 244 out: 245 kfree(engines); 246 return err; 247 } 248