Skip to content

Commit b49e894

Browse files
committed
drm/i915: Replace custom intel runtime_pm tracker with ref_tracker library
Beside reusing existing code, the main advantage of ref_tracker is tracking per instance of wakeref. It allows also to catch double put. On the other side we lose information about the first acquire and the last release, but the advantages outweigh it. Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com> Reviewed-by: Andi Shyti <andi.shyti@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20231030-ref_tracker_i915-v1-1-006fe6b96421@intel.com
1 parent dfed6b5 commit b49e894

7 files changed

Lines changed: 86 additions & 217 deletions

File tree

drivers/gpu/drm/i915/Kconfig.debug

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ config DRM_I915_DEBUG
2424
select DEBUG_FS
2525
select PREEMPT_COUNT
2626
select I2C_CHARDEV
27+
select REF_TRACKER
2728
select STACKDEPOT
29+
select STACKTRACE
2830
select DRM_DP_AUX_CHARDEV
2931
select X86_MSR # used by igt/pm_rpm
3032
select DRM_VGEM # used by igt/prime_vgem (dmabuf interop checks)
@@ -231,7 +233,9 @@ config DRM_I915_DEBUG_RUNTIME_PM
231233
bool "Enable extra state checking for runtime PM"
232234
depends on DRM_I915
233235
default n
236+
select REF_TRACKER
234237
select STACKDEPOT
238+
select STACKTRACE
235239
help
236240
Choose this option to turn on extra state checking for the
237241
runtime PM functionality. This may introduce overhead during

drivers/gpu/drm/i915/display/intel_display_power.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ print_async_put_domains_state(struct i915_power_domains *power_domains)
405405
struct drm_i915_private,
406406
display.power.domains);
407407

408-
drm_dbg(&i915->drm, "async_put_wakeref %u\n",
408+
drm_dbg(&i915->drm, "async_put_wakeref %lu\n",
409409
power_domains->async_put_wakeref);
410410

411411
print_power_domains(power_domains, "async_put_domains[0]",

drivers/gpu/drm/i915/i915_driver.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1037,7 +1037,7 @@ void i915_driver_shutdown(struct drm_i915_private *i915)
10371037
intel_power_domains_driver_remove(i915);
10381038
enable_rpm_wakeref_asserts(&i915->runtime_pm);
10391039

1040-
intel_runtime_pm_driver_release(&i915->runtime_pm);
1040+
intel_runtime_pm_driver_last_release(&i915->runtime_pm);
10411041
}
10421042

10431043
static bool suspend_to_idle(struct drm_i915_private *dev_priv)

drivers/gpu/drm/i915/intel_runtime_pm.c

Lines changed: 17 additions & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -52,243 +52,52 @@
5252

5353
#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM)
5454

55-
#include <linux/sort.h>
56-
57-
#define STACKDEPTH 8
58-
59-
static noinline depot_stack_handle_t __save_depot_stack(void)
60-
{
61-
unsigned long entries[STACKDEPTH];
62-
unsigned int n;
63-
64-
n = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
65-
return stack_depot_save(entries, n, GFP_NOWAIT | __GFP_NOWARN);
66-
}
67-
6855
static void init_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm)
6956
{
70-
spin_lock_init(&rpm->debug.lock);
71-
stack_depot_init();
57+
ref_tracker_dir_init(&rpm->debug, INTEL_REFTRACK_DEAD_COUNT, dev_name(rpm->kdev));
7258
}
7359

74-
static noinline depot_stack_handle_t
60+
static intel_wakeref_t
7561
track_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm)
7662
{
77-
depot_stack_handle_t stack, *stacks;
78-
unsigned long flags;
79-
80-
if (rpm->no_wakeref_tracking)
81-
return -1;
82-
83-
stack = __save_depot_stack();
84-
if (!stack)
63+
if (!rpm->available || rpm->no_wakeref_tracking)
8564
return -1;
8665

87-
spin_lock_irqsave(&rpm->debug.lock, flags);
88-
89-
if (!rpm->debug.count)
90-
rpm->debug.last_acquire = stack;
91-
92-
stacks = krealloc(rpm->debug.owners,
93-
(rpm->debug.count + 1) * sizeof(*stacks),
94-
GFP_NOWAIT | __GFP_NOWARN);
95-
if (stacks) {
96-
stacks[rpm->debug.count++] = stack;
97-
rpm->debug.owners = stacks;
98-
} else {
99-
stack = -1;
100-
}
101-
102-
spin_unlock_irqrestore(&rpm->debug.lock, flags);
103-
104-
return stack;
66+
return intel_ref_tracker_alloc(&rpm->debug);
10567
}
10668

10769
static void untrack_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm,
108-
depot_stack_handle_t stack)
70+
intel_wakeref_t wakeref)
10971
{
110-
struct drm_i915_private *i915 = container_of(rpm,
111-
struct drm_i915_private,
112-
runtime_pm);
113-
unsigned long flags, n;
114-
bool found = false;
115-
116-
if (unlikely(stack == -1))
72+
if (!rpm->available || rpm->no_wakeref_tracking)
11773
return;
11874

119-
spin_lock_irqsave(&rpm->debug.lock, flags);
120-
for (n = rpm->debug.count; n--; ) {
121-
if (rpm->debug.owners[n] == stack) {
122-
memmove(rpm->debug.owners + n,
123-
rpm->debug.owners + n + 1,
124-
(--rpm->debug.count - n) * sizeof(stack));
125-
found = true;
126-
break;
127-
}
128-
}
129-
spin_unlock_irqrestore(&rpm->debug.lock, flags);
130-
131-
if (drm_WARN(&i915->drm, !found,
132-
"Unmatched wakeref (tracking %lu), count %u\n",
133-
rpm->debug.count, atomic_read(&rpm->wakeref_count))) {
134-
char *buf;
135-
136-
buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN);
137-
if (!buf)
138-
return;
139-
140-
stack_depot_snprint(stack, buf, PAGE_SIZE, 2);
141-
DRM_DEBUG_DRIVER("wakeref %x from\n%s", stack, buf);
142-
143-
stack = READ_ONCE(rpm->debug.last_release);
144-
if (stack) {
145-
stack_depot_snprint(stack, buf, PAGE_SIZE, 2);
146-
DRM_DEBUG_DRIVER("wakeref last released at\n%s", buf);
147-
}
148-
149-
kfree(buf);
150-
}
75+
intel_ref_tracker_free(&rpm->debug, wakeref);
15176
}
15277

153-
static int cmphandle(const void *_a, const void *_b)
78+
static void untrack_all_intel_runtime_pm_wakerefs(struct intel_runtime_pm *rpm)
15479
{
155-
const depot_stack_handle_t * const a = _a, * const b = _b;
156-
157-
if (*a < *b)
158-
return -1;
159-
else if (*a > *b)
160-
return 1;
161-
else
162-
return 0;
163-
}
164-
165-
static void
166-
__print_intel_runtime_pm_wakeref(struct drm_printer *p,
167-
const struct intel_runtime_pm_debug *dbg)
168-
{
169-
unsigned long i;
170-
char *buf;
171-
172-
buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN);
173-
if (!buf)
174-
return;
175-
176-
if (dbg->last_acquire) {
177-
stack_depot_snprint(dbg->last_acquire, buf, PAGE_SIZE, 2);
178-
drm_printf(p, "Wakeref last acquired:\n%s", buf);
179-
}
180-
181-
if (dbg->last_release) {
182-
stack_depot_snprint(dbg->last_release, buf, PAGE_SIZE, 2);
183-
drm_printf(p, "Wakeref last released:\n%s", buf);
184-
}
185-
186-
drm_printf(p, "Wakeref count: %lu\n", dbg->count);
187-
188-
sort(dbg->owners, dbg->count, sizeof(*dbg->owners), cmphandle, NULL);
189-
190-
for (i = 0; i < dbg->count; i++) {
191-
depot_stack_handle_t stack = dbg->owners[i];
192-
unsigned long rep;
193-
194-
rep = 1;
195-
while (i + 1 < dbg->count && dbg->owners[i + 1] == stack)
196-
rep++, i++;
197-
stack_depot_snprint(stack, buf, PAGE_SIZE, 2);
198-
drm_printf(p, "Wakeref x%lu taken at:\n%s", rep, buf);
199-
}
200-
201-
kfree(buf);
202-
}
203-
204-
static noinline void
205-
__untrack_all_wakerefs(struct intel_runtime_pm_debug *debug,
206-
struct intel_runtime_pm_debug *saved)
207-
{
208-
*saved = *debug;
209-
210-
debug->owners = NULL;
211-
debug->count = 0;
212-
debug->last_release = __save_depot_stack();
213-
}
214-
215-
static void
216-
dump_and_free_wakeref_tracking(struct intel_runtime_pm_debug *debug)
217-
{
218-
if (debug->count) {
219-
struct drm_printer p = drm_debug_printer("i915");
220-
221-
__print_intel_runtime_pm_wakeref(&p, debug);
222-
}
223-
224-
kfree(debug->owners);
80+
ref_tracker_dir_exit(&rpm->debug);
22581
}
22682

22783
static noinline void
22884
__intel_wakeref_dec_and_check_tracking(struct intel_runtime_pm *rpm)
22985
{
230-
struct intel_runtime_pm_debug dbg = {};
23186
unsigned long flags;
23287

23388
if (!atomic_dec_and_lock_irqsave(&rpm->wakeref_count,
23489
&rpm->debug.lock,
23590
flags))
23691
return;
23792

238-
__untrack_all_wakerefs(&rpm->debug, &dbg);
93+
ref_tracker_dir_print_locked(&rpm->debug, INTEL_REFTRACK_PRINT_LIMIT);
23994
spin_unlock_irqrestore(&rpm->debug.lock, flags);
240-
241-
dump_and_free_wakeref_tracking(&dbg);
242-
}
243-
244-
static noinline void
245-
untrack_all_intel_runtime_pm_wakerefs(struct intel_runtime_pm *rpm)
246-
{
247-
struct intel_runtime_pm_debug dbg = {};
248-
unsigned long flags;
249-
250-
spin_lock_irqsave(&rpm->debug.lock, flags);
251-
__untrack_all_wakerefs(&rpm->debug, &dbg);
252-
spin_unlock_irqrestore(&rpm->debug.lock, flags);
253-
254-
dump_and_free_wakeref_tracking(&dbg);
25595
}
25696

25797
void print_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm,
25898
struct drm_printer *p)
25999
{
260-
struct intel_runtime_pm_debug dbg = {};
261-
262-
do {
263-
unsigned long alloc = dbg.count;
264-
depot_stack_handle_t *s;
265-
266-
spin_lock_irq(&rpm->debug.lock);
267-
dbg.count = rpm->debug.count;
268-
if (dbg.count <= alloc) {
269-
memcpy(dbg.owners,
270-
rpm->debug.owners,
271-
dbg.count * sizeof(*s));
272-
}
273-
dbg.last_acquire = rpm->debug.last_acquire;
274-
dbg.last_release = rpm->debug.last_release;
275-
spin_unlock_irq(&rpm->debug.lock);
276-
if (dbg.count <= alloc)
277-
break;
278-
279-
s = krealloc(dbg.owners,
280-
dbg.count * sizeof(*s),
281-
GFP_NOWAIT | __GFP_NOWARN);
282-
if (!s)
283-
goto out;
284-
285-
dbg.owners = s;
286-
} while (1);
287-
288-
__print_intel_runtime_pm_wakeref(p, &dbg);
289-
290-
out:
291-
kfree(dbg.owners);
100+
intel_ref_tracker_show(&rpm->debug, p);
292101
}
293102

294103
#else
@@ -297,14 +106,14 @@ static void init_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm)
297106
{
298107
}
299108

300-
static depot_stack_handle_t
109+
static intel_wakeref_t
301110
track_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm)
302111
{
303112
return -1;
304113
}
305114

306115
static void untrack_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm,
307-
intel_wakeref_t wref)
116+
intel_wakeref_t wakeref)
308117
{
309118
}
310119

@@ -639,7 +448,11 @@ void intel_runtime_pm_driver_release(struct intel_runtime_pm *rpm)
639448
"i915 raw-wakerefs=%d wakelocks=%d on cleanup\n",
640449
intel_rpm_raw_wakeref_count(count),
641450
intel_rpm_wakelock_count(count));
451+
}
642452

453+
void intel_runtime_pm_driver_last_release(struct intel_runtime_pm *rpm)
454+
{
455+
intel_runtime_pm_driver_release(rpm);
643456
untrack_all_intel_runtime_pm_wakerefs(rpm);
644457
}
645458

drivers/gpu/drm/i915/intel_runtime_pm.h

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,7 @@ struct intel_runtime_pm {
7777
* paired rpm_put) we can remove corresponding pairs of and keep
7878
* the array trimmed to active wakerefs.
7979
*/
80-
struct intel_runtime_pm_debug {
81-
spinlock_t lock;
82-
83-
depot_stack_handle_t last_acquire;
84-
depot_stack_handle_t last_release;
85-
86-
depot_stack_handle_t *owners;
87-
unsigned long count;
88-
} debug;
80+
struct ref_tracker_dir debug;
8981
#endif
9082
};
9183

@@ -189,6 +181,7 @@ void intel_runtime_pm_init_early(struct intel_runtime_pm *rpm);
189181
void intel_runtime_pm_enable(struct intel_runtime_pm *rpm);
190182
void intel_runtime_pm_disable(struct intel_runtime_pm *rpm);
191183
void intel_runtime_pm_driver_release(struct intel_runtime_pm *rpm);
184+
void intel_runtime_pm_driver_last_release(struct intel_runtime_pm *rpm);
192185

193186
intel_wakeref_t intel_runtime_pm_get(struct intel_runtime_pm *rpm);
194187
intel_wakeref_t intel_runtime_pm_get_if_in_use(struct intel_runtime_pm *rpm);

drivers/gpu/drm/i915/intel_wakeref.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,31 @@ void intel_wakeref_auto_fini(struct intel_wakeref_auto *wf)
191191
intel_wakeref_auto(wf, 0);
192192
INTEL_WAKEREF_BUG_ON(wf->wakeref);
193193
}
194+
195+
void intel_ref_tracker_show(struct ref_tracker_dir *dir,
196+
struct drm_printer *p)
197+
{
198+
const size_t buf_size = PAGE_SIZE;
199+
char *buf, *sb, *se;
200+
size_t count;
201+
202+
buf = kmalloc(buf_size, GFP_NOWAIT);
203+
if (!buf)
204+
return;
205+
206+
count = ref_tracker_dir_snprint(dir, buf, buf_size);
207+
if (!count)
208+
goto free;
209+
/* printk does not like big buffers, so we split it */
210+
for (sb = buf; *sb; sb = se + 1) {
211+
se = strchrnul(sb, '\n');
212+
drm_printf(p, "%.*s", (int)(se - sb + 1), sb);
213+
if (!*se)
214+
break;
215+
}
216+
if (count >= buf_size)
217+
drm_printf(p, "\n...dropped %zd extra bytes of leak report.\n",
218+
count + 1 - buf_size);
219+
free:
220+
kfree(buf);
221+
}

0 commit comments

Comments
 (0)