1 // SPDX-License-Identifier: GPL-2.0-or-later 2 #include <linux/export.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 9 10 struct 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 17 void ref_tracker_dir_exit(struct ref_tracker_dir *dir) 18 { 19 struct ref_tracker *tracker, *n; 20 unsigned long flags; 21 bool leak = false; 22 23 dir->dead = true; 24 spin_lock_irqsave(&dir->lock, flags); 25 list_for_each_entry_safe(tracker, n, &dir->quarantine, head) { 26 list_del(&tracker->head); 27 kfree(tracker); 28 dir->quarantine_avail++; 29 } 30 list_for_each_entry_safe(tracker, n, &dir->list, head) { 31 pr_err("leaked reference.\n"); 32 if (tracker->alloc_stack_handle) 33 stack_depot_print(tracker->alloc_stack_handle); 34 leak = true; 35 list_del(&tracker->head); 36 kfree(tracker); 37 } 38 spin_unlock_irqrestore(&dir->lock, flags); 39 WARN_ON_ONCE(leak); 40 WARN_ON_ONCE(refcount_read(&dir->untracked) != 1); 41 } 42 EXPORT_SYMBOL(ref_tracker_dir_exit); 43 44 void ref_tracker_dir_print(struct ref_tracker_dir *dir, 45 unsigned int display_limit) 46 { 47 struct ref_tracker *tracker; 48 unsigned long flags; 49 unsigned int i = 0; 50 51 spin_lock_irqsave(&dir->lock, flags); 52 list_for_each_entry(tracker, &dir->list, head) { 53 if (i < display_limit) { 54 pr_err("leaked reference.\n"); 55 if (tracker->alloc_stack_handle) 56 stack_depot_print(tracker->alloc_stack_handle); 57 i++; 58 } else { 59 break; 60 } 61 } 62 spin_unlock_irqrestore(&dir->lock, flags); 63 } 64 EXPORT_SYMBOL(ref_tracker_dir_print); 65 66 int ref_tracker_alloc(struct ref_tracker_dir *dir, 67 struct ref_tracker **trackerp, 68 gfp_t gfp) 69 { 70 unsigned long entries[REF_TRACKER_STACK_ENTRIES]; 71 struct ref_tracker *tracker; 72 unsigned int nr_entries; 73 gfp_t gfp_mask = gfp; 74 unsigned long flags; 75 76 WARN_ON_ONCE(dir->dead); 77 78 if (gfp & __GFP_DIRECT_RECLAIM) 79 gfp_mask |= __GFP_NOFAIL; 80 *trackerp = tracker = kzalloc(sizeof(*tracker), gfp_mask); 81 if (unlikely(!tracker)) { 82 pr_err_once("memory allocation failure, unreliable refcount tracker.\n"); 83 refcount_inc(&dir->untracked); 84 return -ENOMEM; 85 } 86 nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 1); 87 nr_entries = filter_irq_stacks(entries, nr_entries); 88 tracker->alloc_stack_handle = stack_depot_save(entries, nr_entries, gfp); 89 90 spin_lock_irqsave(&dir->lock, flags); 91 list_add(&tracker->head, &dir->list); 92 spin_unlock_irqrestore(&dir->lock, flags); 93 return 0; 94 } 95 EXPORT_SYMBOL_GPL(ref_tracker_alloc); 96 97 int ref_tracker_free(struct ref_tracker_dir *dir, 98 struct ref_tracker **trackerp) 99 { 100 unsigned long entries[REF_TRACKER_STACK_ENTRIES]; 101 struct ref_tracker *tracker = *trackerp; 102 depot_stack_handle_t stack_handle; 103 unsigned int nr_entries; 104 unsigned long flags; 105 106 WARN_ON_ONCE(dir->dead); 107 108 if (!tracker) { 109 refcount_dec(&dir->untracked); 110 return -EEXIST; 111 } 112 nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 1); 113 nr_entries = filter_irq_stacks(entries, nr_entries); 114 stack_handle = stack_depot_save(entries, nr_entries, GFP_ATOMIC); 115 116 spin_lock_irqsave(&dir->lock, flags); 117 if (tracker->dead) { 118 pr_err("reference already released.\n"); 119 if (tracker->alloc_stack_handle) { 120 pr_err("allocated in:\n"); 121 stack_depot_print(tracker->alloc_stack_handle); 122 } 123 if (tracker->free_stack_handle) { 124 pr_err("freed in:\n"); 125 stack_depot_print(tracker->free_stack_handle); 126 } 127 spin_unlock_irqrestore(&dir->lock, flags); 128 WARN_ON_ONCE(1); 129 return -EINVAL; 130 } 131 tracker->dead = true; 132 133 tracker->free_stack_handle = stack_handle; 134 135 list_move_tail(&tracker->head, &dir->quarantine); 136 if (!dir->quarantine_avail) { 137 tracker = list_first_entry(&dir->quarantine, struct ref_tracker, head); 138 list_del(&tracker->head); 139 } else { 140 dir->quarantine_avail--; 141 tracker = NULL; 142 } 143 spin_unlock_irqrestore(&dir->lock, flags); 144 145 kfree(tracker); 146 return 0; 147 } 148 EXPORT_SYMBOL_GPL(ref_tracker_free); 149