ref_tracker.c (7a113ff6355944283402fb617dc97122f68d5a41) ref_tracker.c (b6d7c0eb2dcbd238fa233a3a1737654e380e784a)
1// SPDX-License-Identifier: GPL-2.0-or-later
1// SPDX-License-Identifier: GPL-2.0-or-later
2
3#define pr_fmt(fmt) "ref_tracker: " fmt
4
2#include <linux/export.h>
5#include <linux/export.h>
6#include <linux/list_sort.h>
3#include <linux/ref_tracker.h>
4#include <linux/slab.h>
5#include <linux/stacktrace.h>
6#include <linux/stackdepot.h>
7
8#define REF_TRACKER_STACK_ENTRIES 16
7#include <linux/ref_tracker.h>
8#include <linux/slab.h>
9#include <linux/stacktrace.h>
10#include <linux/stackdepot.h>
11
12#define REF_TRACKER_STACK_ENTRIES 16
13#define STACK_BUF_SIZE 1024
9
10struct ref_tracker {
11 struct list_head head; /* anchor into dir->list or dir->quarantine */
12 bool dead;
13 depot_stack_handle_t alloc_stack_handle;
14 depot_stack_handle_t free_stack_handle;
15};
16
14
15struct ref_tracker {
16 struct list_head head; /* anchor into dir->list or dir->quarantine */
17 bool dead;
18 depot_stack_handle_t alloc_stack_handle;
19 depot_stack_handle_t free_stack_handle;
20};
21
17void ref_tracker_dir_print_locked(struct ref_tracker_dir *dir,
18 unsigned int display_limit)
22struct ref_tracker_dir_stats {
23 int total;
24 int count;
25 struct {
26 depot_stack_handle_t stack_handle;
27 unsigned int count;
28 } stacks[];
29};
30
31static struct ref_tracker_dir_stats *
32ref_tracker_get_stats(struct ref_tracker_dir *dir, unsigned int limit)
19{
33{
34 struct ref_tracker_dir_stats *stats;
20 struct ref_tracker *tracker;
35 struct ref_tracker *tracker;
21 unsigned int i = 0;
22
36
23 lockdep_assert_held(&dir->lock);
37 stats = kmalloc(struct_size(stats, stacks, limit),
38 GFP_NOWAIT | __GFP_NOWARN);
39 if (!stats)
40 return ERR_PTR(-ENOMEM);
41 stats->total = 0;
42 stats->count = 0;
24
25 list_for_each_entry(tracker, &dir->list, head) {
43
44 list_for_each_entry(tracker, &dir->list, head) {
26 if (i < display_limit) {
27 pr_err("leaked reference.\n");
28 if (tracker->alloc_stack_handle)
29 stack_depot_print(tracker->alloc_stack_handle);
30 i++;
31 } else {
32 break;
45 depot_stack_handle_t stack = tracker->alloc_stack_handle;
46 int i;
47
48 ++stats->total;
49 for (i = 0; i < stats->count; ++i)
50 if (stats->stacks[i].stack_handle == stack)
51 break;
52 if (i >= limit)
53 continue;
54 if (i >= stats->count) {
55 stats->stacks[i].stack_handle = stack;
56 stats->stacks[i].count = 0;
57 ++stats->count;
33 }
58 }
59 ++stats->stacks[i].count;
34 }
60 }
61
62 return stats;
35}
63}
64
65void ref_tracker_dir_print_locked(struct ref_tracker_dir *dir,
66 unsigned int display_limit)
67{
68 struct ref_tracker_dir_stats *stats;
69 unsigned int i = 0, skipped;
70 depot_stack_handle_t stack;
71 char *sbuf;
72
73 lockdep_assert_held(&dir->lock);
74
75 if (list_empty(&dir->list))
76 return;
77
78 stats = ref_tracker_get_stats(dir, display_limit);
79 if (IS_ERR(stats)) {
80 pr_err("%s@%pK: couldn't get stats, error %pe\n",
81 dir->name, dir, stats);
82 return;
83 }
84
85 sbuf = kmalloc(STACK_BUF_SIZE, GFP_NOWAIT | __GFP_NOWARN);
86
87 for (i = 0, skipped = stats->total; i < stats->count; ++i) {
88 stack = stats->stacks[i].stack_handle;
89 if (sbuf && !stack_depot_snprint(stack, sbuf, STACK_BUF_SIZE, 4))
90 sbuf[0] = 0;
91 pr_err("%s@%pK has %d/%d users at\n%s\n", dir->name, dir,
92 stats->stacks[i].count, stats->total, sbuf);
93 skipped -= stats->stacks[i].count;
94 }
95
96 if (skipped)
97 pr_err("%s@%pK skipped reports about %d/%d users.\n",
98 dir->name, dir, skipped, stats->total);
99
100 kfree(sbuf);
101
102 kfree(stats);
103}
36EXPORT_SYMBOL(ref_tracker_dir_print_locked);
37
38void ref_tracker_dir_print(struct ref_tracker_dir *dir,
39 unsigned int display_limit)
40{
41 unsigned long flags;
42
43 spin_lock_irqsave(&dir->lock, flags);

--- 123 unchanged lines hidden ---
104EXPORT_SYMBOL(ref_tracker_dir_print_locked);
105
106void ref_tracker_dir_print(struct ref_tracker_dir *dir,
107 unsigned int display_limit)
108{
109 unsigned long flags;
110
111 spin_lock_irqsave(&dir->lock, flags);

--- 123 unchanged lines hidden ---