Linux Audio

Check our new training course

Embedded Linux training

Mar 10-20, 2025, special US time zones
Register
Loading...
Note: File does not exist in v4.6.
  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_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
 16static 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
 31int live_rc6_manual(void *arg)
 32{
 33	struct intel_gt *gt = arg;
 34	struct intel_rc6 *rc6 = &gt->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
112out_unlock:
113	intel_runtime_pm_put(gt->uncore->rpm, wakeref);
114	return err;
115}
116
117static 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
150static struct intel_engine_cs **
151randomised_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
179int 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 = &gt->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
243out:
244	kfree(engines);
245	return err;
246}