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