Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'ref_tracker-add-ability-to-register-a-debugfs-file-for-a-ref_tracker_dir'

Jeff Layton says:

====================
ref_tracker: add ability to register a debugfs file for a ref_tracker_dir

For those just joining in, this series adds a new top-level
"ref_tracker" debugfs directory, and has each ref_tracker_dir register a
file in there as part of its initialization. It also adds the ability to
register a symlink with a more human-usable name that points to the
file, and does some general cleanup of how the ref_tracker object names
are handled.

v14: https://lore.kernel.org/20250610-reftrack-dbgfs-v14-0-efb532861428@kernel.org
v13: https://lore.kernel.org/20250603-reftrack-dbgfs-v13-0-7b2a425019d8@kernel.org
v12: https://lore.kernel.org/20250529-reftrack-dbgfs-v12-0-11b93c0c0b6e@kernel.org
v11: https://lore.kernel.org/20250528-reftrack-dbgfs-v11-0-94ae0b165841@kernel.org
v10: https://lore.kernel.org/20250527-reftrack-dbgfs-v10-0-dc55f7705691@kernel.org
v9: https://lore.kernel.org/20250509-reftrack-dbgfs-v9-0-8ab888a4524d@kernel.org
v8: https://lore.kernel.org/20250507-reftrack-dbgfs-v8-0-607717d3bb98@kernel.org
v7: https://lore.kernel.org/20250505-reftrack-dbgfs-v7-0-f78c5d97bcca@kernel.org
v6: https://lore.kernel.org/20250430-reftrack-dbgfs-v6-0-867c29aff03a@kernel.org
v5: https://lore.kernel.org/20250428-reftrack-dbgfs-v5-0-1cbbdf2038bd@kernel.org
v4: https://lore.kernel.org/20250418-reftrack-dbgfs-v4-0-5ca5c7899544@kernel.org
v3: https://lore.kernel.org/20250417-reftrack-dbgfs-v3-0-c3159428c8fb@kernel.org
v2: https://lore.kernel.org/20250415-reftrack-dbgfs-v2-0-b18c4abd122f@kernel.org
v1: https://lore.kernel.org/20250414-reftrack-dbgfs-v1-0-f03585832203@kernel.org
====================

Link: https://patch.msgid.link/20250618-reftrack-dbgfs-v15-0-24fc37ead144@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+352 -26
+1 -1
drivers/gpu/drm/display/drm_dp_tunnel.c
··· 1920 1920 } 1921 1921 1922 1922 #ifdef CONFIG_DRM_DISPLAY_DP_TUNNEL_STATE_DEBUG 1923 - ref_tracker_dir_init(&mgr->ref_tracker, 16, "dptun"); 1923 + ref_tracker_dir_init(&mgr->ref_tracker, 16, "drm_dptun"); 1924 1924 #endif 1925 1925 1926 1926 for (i = 0; i < max_group_count; i++) {
+3 -1
drivers/gpu/drm/i915/intel_runtime_pm.c
··· 59 59 60 60 static void init_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm) 61 61 { 62 - ref_tracker_dir_init(&rpm->debug, INTEL_REFTRACK_DEAD_COUNT, dev_name(rpm->kdev)); 62 + if (!rpm->debug.class) 63 + ref_tracker_dir_init(&rpm->debug, INTEL_REFTRACK_DEAD_COUNT, 64 + "intel_runtime_pm"); 63 65 } 64 66 65 67 static intel_wakeref_t
+2 -1
drivers/gpu/drm/i915/intel_wakeref.c
··· 114 114 "wakeref.work", &key->work, 0); 115 115 116 116 #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_WAKEREF) 117 - ref_tracker_dir_init(&wf->debug, INTEL_REFTRACK_DEAD_COUNT, name); 117 + if (!wf->debug.class) 118 + ref_tracker_dir_init(&wf->debug, INTEL_REFTRACK_DEAD_COUNT, "intel_wakeref"); 118 119 #endif 119 120 } 120 121
+46 -4
include/linux/ref_tracker.h
··· 6 6 #include <linux/spinlock.h> 7 7 #include <linux/stackdepot.h> 8 8 9 + #define __ostream_printf __printf(2, 3) 10 + 9 11 struct ref_tracker; 10 12 11 13 struct ref_tracker_dir { ··· 19 17 bool dead; 20 18 struct list_head list; /* List of active trackers */ 21 19 struct list_head quarantine; /* List of dead trackers */ 22 - char name[32]; 20 + const char *class; /* object classname */ 23 21 #endif 24 22 }; 25 23 26 24 #ifdef CONFIG_REF_TRACKER 27 25 26 + #ifdef CONFIG_DEBUG_FS 27 + 28 + void ref_tracker_dir_debugfs(struct ref_tracker_dir *dir); 29 + void ref_tracker_dir_symlink(struct ref_tracker_dir *dir, const char *fmt, ...); 30 + 31 + #else /* CONFIG_DEBUG_FS */ 32 + 33 + static inline void ref_tracker_dir_debugfs(struct ref_tracker_dir *dir) 34 + { 35 + } 36 + 37 + static inline __ostream_printf 38 + void ref_tracker_dir_symlink(struct ref_tracker_dir *dir, const char *fmt, ...) 39 + { 40 + } 41 + 42 + #endif /* CONFIG_DEBUG_FS */ 43 + 44 + /** 45 + * ref_tracker_dir_init - initialize a ref_tracker dir 46 + * @dir: ref_tracker_dir to be initialized 47 + * @quarantine_count: max number of entries to be tracked 48 + * @class: pointer to static string that describes object type 49 + * 50 + * Initialize a ref_tracker_dir. If debugfs is configured, then a file 51 + * will also be created for it under the top-level ref_tracker debugfs 52 + * directory. 53 + * 54 + * Note that @class must point to a static string. 55 + */ 28 56 static inline void ref_tracker_dir_init(struct ref_tracker_dir *dir, 29 57 unsigned int quarantine_count, 30 - const char *name) 58 + const char *class) 31 59 { 32 60 INIT_LIST_HEAD(&dir->list); 33 61 INIT_LIST_HEAD(&dir->quarantine); ··· 66 34 dir->dead = false; 67 35 refcount_set(&dir->untracked, 1); 68 36 refcount_set(&dir->no_tracker, 1); 69 - strscpy(dir->name, name, sizeof(dir->name)); 37 + dir->class = class; 38 + ref_tracker_dir_debugfs(dir); 70 39 stack_depot_init(); 71 40 } 72 41 ··· 91 58 92 59 static inline void ref_tracker_dir_init(struct ref_tracker_dir *dir, 93 60 unsigned int quarantine_count, 94 - const char *name) 61 + const char *class) 62 + { 63 + } 64 + 65 + static inline void ref_tracker_dir_debugfs(struct ref_tracker_dir *dir) 66 + { 67 + } 68 + 69 + static inline __ostream_printf 70 + void ref_tracker_dir_symlink(struct ref_tracker_dir *dir, const char *fmt, ...) 95 71 { 96 72 } 97 73
+268 -15
lib/ref_tracker.c
··· 8 8 #include <linux/slab.h> 9 9 #include <linux/stacktrace.h> 10 10 #include <linux/stackdepot.h> 11 + #include <linux/seq_file.h> 11 12 12 13 #define REF_TRACKER_STACK_ENTRIES 16 13 14 #define STACK_BUF_SIZE 1024 ··· 28 27 unsigned int count; 29 28 } stacks[]; 30 29 }; 30 + 31 + #ifdef CONFIG_DEBUG_FS 32 + #include <linux/xarray.h> 33 + 34 + /* 35 + * ref_tracker_dir_init() is usually called in allocation-safe contexts, but 36 + * the same is not true of ref_tracker_dir_exit() which can be called from 37 + * anywhere an object is freed. Removing debugfs dentries is a blocking 38 + * operation, so we defer that work to the debugfs_reap_worker. 39 + * 40 + * Each dentry is tracked in the appropriate xarray. When 41 + * ref_tracker_dir_exit() is called, its entries in the xarrays are marked and 42 + * the workqueue job is scheduled. The worker then runs and deletes any marked 43 + * dentries asynchronously. 44 + */ 45 + static struct xarray debugfs_dentries; 46 + static struct xarray debugfs_symlinks; 47 + static struct work_struct debugfs_reap_worker; 48 + 49 + #define REF_TRACKER_DIR_DEAD XA_MARK_0 50 + static inline void ref_tracker_debugfs_mark(struct ref_tracker_dir *dir) 51 + { 52 + unsigned long flags; 53 + 54 + xa_lock_irqsave(&debugfs_dentries, flags); 55 + __xa_set_mark(&debugfs_dentries, (unsigned long)dir, REF_TRACKER_DIR_DEAD); 56 + xa_unlock_irqrestore(&debugfs_dentries, flags); 57 + 58 + xa_lock_irqsave(&debugfs_symlinks, flags); 59 + __xa_set_mark(&debugfs_symlinks, (unsigned long)dir, REF_TRACKER_DIR_DEAD); 60 + xa_unlock_irqrestore(&debugfs_symlinks, flags); 61 + 62 + schedule_work(&debugfs_reap_worker); 63 + } 64 + #else 65 + static inline void ref_tracker_debugfs_mark(struct ref_tracker_dir *dir) 66 + { 67 + } 68 + #endif 31 69 32 70 static struct ref_tracker_dir_stats * 33 71 ref_tracker_get_stats(struct ref_tracker_dir *dir, unsigned int limit) ··· 103 63 } 104 64 105 65 struct ostream { 66 + void __ostream_printf (*func)(struct ostream *stream, char *fmt, ...); 67 + char *prefix; 106 68 char *buf; 69 + struct seq_file *seq; 107 70 int size, used; 108 71 }; 72 + 73 + static void __ostream_printf pr_ostream_log(struct ostream *stream, char *fmt, ...) 74 + { 75 + va_list args; 76 + 77 + va_start(args, fmt); 78 + vprintk(fmt, args); 79 + va_end(args); 80 + } 81 + 82 + static void __ostream_printf pr_ostream_buf(struct ostream *stream, char *fmt, ...) 83 + { 84 + int ret, len = stream->size - stream->used; 85 + va_list args; 86 + 87 + va_start(args, fmt); 88 + ret = vsnprintf(stream->buf + stream->used, len, fmt, args); 89 + va_end(args); 90 + if (ret > 0) 91 + stream->used += min(ret, len); 92 + } 109 93 110 94 #define pr_ostream(stream, fmt, args...) \ 111 95 ({ \ 112 96 struct ostream *_s = (stream); \ 113 97 \ 114 - if (!_s->buf) { \ 115 - pr_err(fmt, ##args); \ 116 - } else { \ 117 - int ret, len = _s->size - _s->used; \ 118 - ret = snprintf(_s->buf + _s->used, len, pr_fmt(fmt), ##args); \ 119 - _s->used += min(ret, len); \ 120 - } \ 98 + _s->func(_s, fmt, ##args); \ 121 99 }) 122 100 123 101 static void ··· 154 96 155 97 stats = ref_tracker_get_stats(dir, display_limit); 156 98 if (IS_ERR(stats)) { 157 - pr_ostream(s, "%s@%pK: couldn't get stats, error %pe\n", 158 - dir->name, dir, stats); 99 + pr_ostream(s, "%s%s@%p: couldn't get stats, error %pe\n", 100 + s->prefix, dir->class, dir, stats); 159 101 return; 160 102 } 161 103 ··· 165 107 stack = stats->stacks[i].stack_handle; 166 108 if (sbuf && !stack_depot_snprint(stack, sbuf, STACK_BUF_SIZE, 4)) 167 109 sbuf[0] = 0; 168 - pr_ostream(s, "%s@%pK has %d/%d users at\n%s\n", dir->name, dir, 169 - stats->stacks[i].count, stats->total, sbuf); 110 + pr_ostream(s, "%s%s@%p has %d/%d users at\n%s\n", s->prefix, 111 + dir->class, dir, stats->stacks[i].count, 112 + stats->total, sbuf); 170 113 skipped -= stats->stacks[i].count; 171 114 } 172 115 173 116 if (skipped) 174 - pr_ostream(s, "%s@%pK skipped reports about %d/%d users.\n", 175 - dir->name, dir, skipped, stats->total); 117 + pr_ostream(s, "%s%s@%p skipped reports about %d/%d users.\n", 118 + s->prefix, dir->class, dir, skipped, stats->total); 176 119 177 120 kfree(sbuf); 178 121 ··· 183 124 void ref_tracker_dir_print_locked(struct ref_tracker_dir *dir, 184 125 unsigned int display_limit) 185 126 { 186 - struct ostream os = {}; 127 + struct ostream os = { .func = pr_ostream_log, 128 + .prefix = "ref_tracker: " }; 187 129 188 130 __ref_tracker_dir_pr_ostream(dir, display_limit, &os); 189 131 } ··· 203 143 204 144 int ref_tracker_dir_snprint(struct ref_tracker_dir *dir, char *buf, size_t size) 205 145 { 206 - struct ostream os = { .buf = buf, .size = size }; 146 + struct ostream os = { .func = pr_ostream_buf, 147 + .prefix = "ref_tracker: ", 148 + .buf = buf, 149 + .size = size }; 207 150 unsigned long flags; 208 151 209 152 spin_lock_irqsave(&dir->lock, flags); ··· 224 161 bool leak = false; 225 162 226 163 dir->dead = true; 164 + /* 165 + * The xarray entries must be marked before the dir->lock is taken to 166 + * protect simultaneous debugfs readers. 167 + */ 168 + ref_tracker_debugfs_mark(dir); 227 169 spin_lock_irqsave(&dir->lock, flags); 228 170 list_for_each_entry_safe(tracker, n, &dir->quarantine, head) { 229 171 list_del(&tracker->head); ··· 341 273 return 0; 342 274 } 343 275 EXPORT_SYMBOL_GPL(ref_tracker_free); 276 + 277 + #ifdef CONFIG_DEBUG_FS 278 + #include <linux/debugfs.h> 279 + 280 + static struct dentry *ref_tracker_debug_dir = (struct dentry *)-ENOENT; 281 + 282 + static void __ostream_printf pr_ostream_seq(struct ostream *stream, char *fmt, ...) 283 + { 284 + va_list args; 285 + 286 + va_start(args, fmt); 287 + seq_vprintf(stream->seq, fmt, args); 288 + va_end(args); 289 + } 290 + 291 + static int ref_tracker_dir_seq_print(struct ref_tracker_dir *dir, struct seq_file *seq) 292 + { 293 + struct ostream os = { .func = pr_ostream_seq, 294 + .prefix = "", 295 + .seq = seq }; 296 + 297 + __ref_tracker_dir_pr_ostream(dir, 16, &os); 298 + 299 + return os.used; 300 + } 301 + 302 + static int ref_tracker_debugfs_show(struct seq_file *f, void *v) 303 + { 304 + struct ref_tracker_dir *dir = f->private; 305 + unsigned long index = (unsigned long)dir; 306 + unsigned long flags; 307 + int ret; 308 + 309 + /* 310 + * "dir" may not exist at this point if ref_tracker_dir_exit() has 311 + * already been called. Take care not to dereference it until its 312 + * legitimacy is established. 313 + * 314 + * The xa_lock is necessary to ensure that "dir" doesn't disappear 315 + * before its lock can be taken. If it's in the hash and not marked 316 + * dead, then it's safe to take dir->lock which prevents 317 + * ref_tracker_dir_exit() from completing. Once the dir->lock is 318 + * acquired, the xa_lock can be released. All of this must be IRQ-safe. 319 + */ 320 + xa_lock_irqsave(&debugfs_dentries, flags); 321 + if (!xa_load(&debugfs_dentries, index) || 322 + xa_get_mark(&debugfs_dentries, index, REF_TRACKER_DIR_DEAD)) { 323 + xa_unlock_irqrestore(&debugfs_dentries, flags); 324 + return -ENODATA; 325 + } 326 + 327 + spin_lock(&dir->lock); 328 + xa_unlock(&debugfs_dentries); 329 + ret = ref_tracker_dir_seq_print(dir, f); 330 + spin_unlock_irqrestore(&dir->lock, flags); 331 + return ret; 332 + } 333 + 334 + static int ref_tracker_debugfs_open(struct inode *inode, struct file *filp) 335 + { 336 + struct ref_tracker_dir *dir = inode->i_private; 337 + 338 + return single_open(filp, ref_tracker_debugfs_show, dir); 339 + } 340 + 341 + static const struct file_operations ref_tracker_debugfs_fops = { 342 + .owner = THIS_MODULE, 343 + .open = ref_tracker_debugfs_open, 344 + .read = seq_read, 345 + .llseek = seq_lseek, 346 + .release = single_release, 347 + }; 348 + 349 + /** 350 + * ref_tracker_dir_debugfs - create debugfs file for ref_tracker_dir 351 + * @dir: ref_tracker_dir to be associated with debugfs file 352 + * 353 + * In most cases, a debugfs file will be created automatically for every 354 + * ref_tracker_dir. If the object was created before debugfs is brought up 355 + * then that may fail. In those cases, it is safe to call this at a later 356 + * time to create the file. 357 + */ 358 + void ref_tracker_dir_debugfs(struct ref_tracker_dir *dir) 359 + { 360 + char name[NAME_MAX + 1]; 361 + struct dentry *dentry; 362 + int ret; 363 + 364 + /* No-op if already created */ 365 + dentry = xa_load(&debugfs_dentries, (unsigned long)dir); 366 + if (dentry && !xa_is_err(dentry)) 367 + return; 368 + 369 + ret = snprintf(name, sizeof(name), "%s@%px", dir->class, dir); 370 + name[sizeof(name) - 1] = '\0'; 371 + 372 + if (ret < sizeof(name)) { 373 + dentry = debugfs_create_file(name, S_IFREG | 0400, 374 + ref_tracker_debug_dir, dir, 375 + &ref_tracker_debugfs_fops); 376 + if (!IS_ERR(dentry)) { 377 + void *old; 378 + 379 + old = xa_store_irq(&debugfs_dentries, (unsigned long)dir, 380 + dentry, GFP_KERNEL); 381 + 382 + if (xa_is_err(old)) 383 + debugfs_remove(dentry); 384 + else 385 + WARN_ON_ONCE(old); 386 + } 387 + } 388 + } 389 + EXPORT_SYMBOL(ref_tracker_dir_debugfs); 390 + 391 + void __ostream_printf ref_tracker_dir_symlink(struct ref_tracker_dir *dir, const char *fmt, ...) 392 + { 393 + char name[NAME_MAX + 1]; 394 + struct dentry *symlink, *dentry; 395 + va_list args; 396 + int ret; 397 + 398 + symlink = xa_load(&debugfs_symlinks, (unsigned long)dir); 399 + dentry = xa_load(&debugfs_dentries, (unsigned long)dir); 400 + 401 + /* Already created?*/ 402 + if (symlink && !xa_is_err(symlink)) 403 + return; 404 + 405 + if (!dentry || xa_is_err(dentry)) 406 + return; 407 + 408 + va_start(args, fmt); 409 + ret = vsnprintf(name, sizeof(name), fmt, args); 410 + va_end(args); 411 + name[sizeof(name) - 1] = '\0'; 412 + 413 + if (ret < sizeof(name)) { 414 + symlink = debugfs_create_symlink(name, ref_tracker_debug_dir, 415 + dentry->d_name.name); 416 + if (!IS_ERR(symlink)) { 417 + void *old; 418 + 419 + old = xa_store_irq(&debugfs_symlinks, (unsigned long)dir, 420 + symlink, GFP_KERNEL); 421 + if (xa_is_err(old)) 422 + debugfs_remove(symlink); 423 + else 424 + WARN_ON_ONCE(old); 425 + } 426 + } 427 + } 428 + EXPORT_SYMBOL(ref_tracker_dir_symlink); 429 + 430 + static void debugfs_reap_work(struct work_struct *work) 431 + { 432 + struct dentry *dentry; 433 + unsigned long index; 434 + bool reaped; 435 + 436 + do { 437 + reaped = false; 438 + xa_for_each_marked(&debugfs_symlinks, index, dentry, REF_TRACKER_DIR_DEAD) { 439 + xa_erase_irq(&debugfs_symlinks, index); 440 + debugfs_remove(dentry); 441 + reaped = true; 442 + } 443 + xa_for_each_marked(&debugfs_dentries, index, dentry, REF_TRACKER_DIR_DEAD) { 444 + xa_erase_irq(&debugfs_dentries, index); 445 + debugfs_remove(dentry); 446 + reaped = true; 447 + } 448 + } while (reaped); 449 + } 450 + 451 + static int __init ref_tracker_debugfs_init(void) 452 + { 453 + INIT_WORK(&debugfs_reap_worker, debugfs_reap_work); 454 + xa_init_flags(&debugfs_dentries, XA_FLAGS_LOCK_IRQ); 455 + xa_init_flags(&debugfs_symlinks, XA_FLAGS_LOCK_IRQ); 456 + ref_tracker_debug_dir = debugfs_create_dir("ref_tracker", NULL); 457 + return 0; 458 + } 459 + late_initcall(ref_tracker_debugfs_init); 460 + #endif /* CONFIG_DEBUG_FS */
+1 -1
net/core/dev.c
··· 11756 11756 11757 11757 dev->priv_len = sizeof_priv; 11758 11758 11759 - ref_tracker_dir_init(&dev->refcnt_tracker, 128, name); 11759 + ref_tracker_dir_init(&dev->refcnt_tracker, 128, "netdev"); 11760 11760 #ifdef CONFIG_PCPU_DEV_REFCNT 11761 11761 dev->pcpu_refcnt = alloc_percpu(int); 11762 11762 if (!dev->pcpu_refcnt)
+31 -3
net/core/net_namespace.c
··· 403 403 { 404 404 refcount_set(&net->passive, 1); 405 405 refcount_set(&net->ns.count, 1); 406 - ref_tracker_dir_init(&net->refcnt_tracker, 128, "net refcnt"); 407 - ref_tracker_dir_init(&net->notrefcnt_tracker, 128, "net notrefcnt"); 406 + ref_tracker_dir_init(&net->refcnt_tracker, 128, "net_refcnt"); 407 + ref_tracker_dir_init(&net->notrefcnt_tracker, 128, "net_notrefcnt"); 408 408 409 409 get_random_bytes(&net->hash_mix, sizeof(u32)); 410 410 net->dev_base_seq = 1; ··· 791 791 } 792 792 EXPORT_SYMBOL_GPL(get_net_ns_by_pid); 793 793 794 + #ifdef CONFIG_NET_NS_REFCNT_TRACKER 795 + static void net_ns_net_debugfs(struct net *net) 796 + { 797 + ref_tracker_dir_symlink(&net->refcnt_tracker, "netns-%llx-%u-refcnt", 798 + net->net_cookie, net->ns.inum); 799 + ref_tracker_dir_symlink(&net->notrefcnt_tracker, "netns-%llx-%u-notrefcnt", 800 + net->net_cookie, net->ns.inum); 801 + } 802 + 803 + static int __init init_net_debugfs(void) 804 + { 805 + ref_tracker_dir_debugfs(&init_net.refcnt_tracker); 806 + ref_tracker_dir_debugfs(&init_net.notrefcnt_tracker); 807 + net_ns_net_debugfs(&init_net); 808 + return 0; 809 + } 810 + late_initcall(init_net_debugfs); 811 + #else 812 + static void net_ns_net_debugfs(struct net *net) 813 + { 814 + } 815 + #endif 816 + 794 817 static __net_init int net_ns_net_init(struct net *net) 795 818 { 819 + int ret; 820 + 796 821 #ifdef CONFIG_NET_NS 797 822 net->ns.ops = &netns_operations; 798 823 #endif 799 - return ns_alloc_inum(&net->ns); 824 + ret = ns_alloc_inum(&net->ns); 825 + if (!ret) 826 + net_ns_net_debugfs(net); 827 + return ret; 800 828 } 801 829 802 830 static __net_exit void net_ns_net_exit(struct net *net)