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 --- |