From: Eric Dumazet <hidden> Date: 2021-12-03 02:47:10
From: Eric Dumazet <redacted>
Two first patches add a generic infrastructure, that will be used
to get tracking of refcount increments/decrements.
The general idea is to be able to precisely pair each decrement with
a corresponding prior increment. Both share a cookie, basically
a pointer to private data storing stack traces.
The third place adds dev_hold_track() and dev_put_track() helpers
(CONFIG_NET_DEV_REFCNT_TRACKER)
Then a series of 20 patches converts some dev_hold()/dev_put()
pairs to new hepers : dev_hold_track() and dev_put_track().
Hopefully this will be used by developpers and syzbot to
root cause bugs that cause netdevice dismantles freezes.
With CONFIG_PCPU_DEV_REFCNT=n option, we were able to detect
some class of bugs, but too late (when too many dev_put()
were happening).
v2: added four additional patches,
added netdev_tracker_alloc() and netdev_tracker_free()
addressed build error (kernel bots),
use GFP_ATOMIC in test_ref_tracker_timer_func()
Eric Dumazet (23):
lib: add reference counting tracking infrastructure
lib: add tests for reference tracker
net: add dev_hold_track() and dev_put_track() helpers
net: add net device refcount tracker to struct netdev_rx_queue
net: add net device refcount tracker to struct netdev_queue
net: add net device refcount tracker to ethtool_phys_id()
net: add net device refcount tracker to dev_ifsioc()
drop_monitor: add net device refcount tracker
net: dst: add net device refcount tracking to dst_entry
ipv6: add net device refcount tracker to rt6_probe_deferred()
sit: add net device refcount tracking to ip_tunnel
ipv6: add net device refcount tracker to struct ip6_tnl
net: add net device refcount tracker to struct neighbour
net: add net device refcount tracker to struct pneigh_entry
net: add net device refcount tracker to struct neigh_parms
net: add net device refcount tracker to struct netdev_adjacent
ipv6: add net device refcount tracker to struct inet6_dev
ipv4: add net device refcount tracker to struct in_device
net/sched: add net device refcount tracker to struct Qdisc
net: linkwatch: add net device refcount tracker
net: failover: add net device refcount tracker
ipmr, ip6mr: add net device refcount tracker to struct vif_device
netpoll: add net device refcount tracker to struct netpoll
drivers/net/netconsole.c | 2 +-
include/linux/inetdevice.h | 2 +
include/linux/mroute_base.h | 1 +
include/linux/netdevice.h | 66 +++++++++++++++++
include/linux/netpoll.h | 1 +
include/linux/ref_tracker.h | 73 +++++++++++++++++++
include/net/devlink.h | 3 +
include/net/dst.h | 1 +
include/net/failover.h | 1 +
include/net/if_inet6.h | 1 +
include/net/ip6_tunnel.h | 1 +
include/net/ip_tunnels.h | 3 +
include/net/neighbour.h | 3 +
include/net/sch_generic.h | 2 +-
lib/Kconfig | 5 ++
lib/Kconfig.debug | 10 +++
lib/Makefile | 4 +-
lib/ref_tracker.c | 140 ++++++++++++++++++++++++++++++++++++
lib/test_ref_tracker.c | 115 +++++++++++++++++++++++++++++
net/Kconfig | 8 +++
net/core/dev.c | 10 ++-
net/core/dev_ioctl.c | 5 +-
net/core/drop_monitor.c | 6 +-
net/core/dst.c | 8 +--
net/core/failover.c | 4 +-
net/core/link_watch.c | 4 +-
net/core/neighbour.c | 18 ++---
net/core/net-sysfs.c | 8 +--
net/core/netpoll.c | 4 +-
net/ethtool/ioctl.c | 5 +-
net/ipv4/devinet.c | 4 +-
net/ipv4/ipmr.c | 3 +-
net/ipv4/route.c | 7 +-
net/ipv6/addrconf.c | 4 +-
net/ipv6/addrconf_core.c | 2 +-
net/ipv6/ip6_gre.c | 8 +--
net/ipv6/ip6_tunnel.c | 4 +-
net/ipv6/ip6_vti.c | 4 +-
net/ipv6/ip6mr.c | 3 +-
net/ipv6/route.c | 10 +--
net/ipv6/sit.c | 4 +-
net/sched/sch_generic.c | 4 +-
42 files changed, 509 insertions(+), 62 deletions(-)
create mode 100644 include/linux/ref_tracker.h
create mode 100644 lib/ref_tracker.c
create mode 100644 lib/test_ref_tracker.c
--
2.34.1.400.ga245620fadb-goog
From: Eric Dumazet <hidden> Date: 2021-12-03 02:47:14
From: Eric Dumazet <redacted>
It can be hard to track where references are taken and released.
In networking, we have annoying issues at device or netns dismantles,
and we had various proposals to ease root causing them.
This patch adds new infrastructure pairing refcount increases
and decreases. This will self document code, because programmers
will have to associate increments/decrements.
This is controled by CONFIG_REF_TRACKER which can be selected
by users of this feature.
This adds both cpu and memory costs, and thus should probably be
used with care.
Signed-off-by: Eric Dumazet <redacted>
Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
---
include/linux/ref_tracker.h | 73 +++++++++++++++++++
lib/Kconfig | 5 ++
lib/Makefile | 2 +
lib/ref_tracker.c | 140 ++++++++++++++++++++++++++++++++++++
4 files changed, 220 insertions(+)
create mode 100644 include/linux/ref_tracker.h
create mode 100644 lib/ref_tracker.c
@@ -0,0 +1,73 @@+// SPDX-License-Identifier: GPL-2.0-or-later+#ifndef _LINUX_REF_TRACKER_H+#define _LINUX_REF_TRACKER_H+#include<linux/refcount.h>+#include<linux/types.h>+#include<linux/spinlock.h>++structref_tracker;++structref_tracker_dir{+#ifdef CONFIG_REF_TRACKER+spinlock_tlock;+unsignedintquarantine_avail;+refcount_tuntracked;+structlist_headlist;/* List of active trackers */+structlist_headquarantine;/* List of dead trackers */+#endif+};++#ifdef CONFIG_REF_TRACKER+staticinlinevoidref_tracker_dir_init(structref_tracker_dir*dir,+unsignedintquarantine_count)+{+INIT_LIST_HEAD(&dir->list);+INIT_LIST_HEAD(&dir->quarantine);+spin_lock_init(&dir->lock);+dir->quarantine_avail=quarantine_count;+refcount_set(&dir->untracked,1);+}++voidref_tracker_dir_exit(structref_tracker_dir*dir);++voidref_tracker_dir_print(structref_tracker_dir*dir,+unsignedintdisplay_limit);++intref_tracker_alloc(structref_tracker_dir*dir,+structref_tracker**trackerp,gfp_tgfp);++intref_tracker_free(structref_tracker_dir*dir,+structref_tracker**trackerp);++#else /* CONFIG_REF_TRACKER */++staticinlinevoidref_tracker_dir_init(structref_tracker_dir*dir,+unsignedintquarantine_count)+{+}++staticinlinevoidref_tracker_dir_exit(structref_tracker_dir*dir)+{+}++staticinlinevoidref_tracker_dir_print(structref_tracker_dir*dir,+unsignedintdisplay_limit)+{+}++staticinlineintref_tracker_alloc(structref_tracker_dir*dir,+structref_tracker**trackerp,+gfp_tgfp)+{+return0;+}++staticinlineintref_tracker_free(structref_tracker_dir*dir,+structref_tracker**trackerp)+{+return0;+}++#endif++#endif /* _LINUX_REF_TRACKER_H */
@@ -2106,6 +2106,16 @@ config BACKTRACE_SELF_TESTSayNifyouareunsure.+configTEST_REF_TRACKER+tristate"Self test for reference tracker"+depends onDEBUG_KERNEL+selectREF_TRACKER+help+Thisoptionprovidesakernelmoduleperformingtests+usingreferencetrackerinfrastructure.++SayNifyouareunsure.+configRBTREE_TESTtristate"Red-Black tree test"depends onDEBUG_KERNEL
@@ -101,7 +101,7 @@ obj-$(CONFIG_TEST_LOCKUP) += test_lockup.oobj-$(CONFIG_TEST_HMM)+=test_hmm.oobj-$(CONFIG_TEST_FREE_PAGES)+=test_free_pages.oobj-$(CONFIG_KPROBES_SANITY_TEST)+=test_kprobes.o-+obj-$(CONFIG_TEST_REF_TRACKER)+=test_ref_tracker.o## CFLAGS for compiling floating point code inside the kernel. x86/Makefile turns# off the generation of FPU/SSE* instructions for kernel proper but FPU_FLAGS
@@ -0,0 +1,115 @@+// SPDX-License-Identifier: GPL-2.0-only+/*+*Referrencetrackerselftest.+*+*Copyright(c)2021EricDumazet<edumazet@google.com>+*/+#include<linux/init.h>+#include<linux/module.h>+#include<linux/delay.h>+#include<linux/ref_tracker.h>+#include<linux/slab.h>+#include<linux/timer.h>++staticstructref_tracker_dirref_dir;+staticstructref_tracker*tracker[20];++#define TRT_ALLOC(X) static noinline void \+alloctest_ref_tracker_alloc##X(structref_tracker_dir*dir,\+structref_tracker**trackerp)\+{\+ref_tracker_alloc(dir,trackerp,GFP_KERNEL);\+}++TRT_ALLOC(1)+TRT_ALLOC(2)+TRT_ALLOC(3)+TRT_ALLOC(4)+TRT_ALLOC(5)+TRT_ALLOC(6)+TRT_ALLOC(7)+TRT_ALLOC(8)+TRT_ALLOC(9)+TRT_ALLOC(10)+TRT_ALLOC(11)+TRT_ALLOC(12)+TRT_ALLOC(13)+TRT_ALLOC(14)+TRT_ALLOC(15)+TRT_ALLOC(16)+TRT_ALLOC(17)+TRT_ALLOC(18)+TRT_ALLOC(19)++#undef TRT_ALLOC++staticnoinlinevoid+alloctest_ref_tracker_free(structref_tracker_dir*dir,+structref_tracker**trackerp)+{+ref_tracker_free(dir,trackerp);+}+++staticstructtimer_listtest_ref_tracker_timer;+staticatomic_ttest_ref_timer_done=ATOMIC_INIT(0);++staticvoidtest_ref_tracker_timer_func(structtimer_list*t)+{+ref_tracker_alloc(&ref_dir,&tracker[0],GFP_ATOMIC);+atomic_set(&test_ref_timer_done,1);+}++staticint__inittest_ref_tracker_init(void)+{+inti;++ref_tracker_dir_init(&ref_dir,100);++timer_setup(&test_ref_tracker_timer,test_ref_tracker_timer_func,0);+mod_timer(&test_ref_tracker_timer,jiffies+1);++alloctest_ref_tracker_alloc1(&ref_dir,&tracker[1]);+alloctest_ref_tracker_alloc2(&ref_dir,&tracker[2]);+alloctest_ref_tracker_alloc3(&ref_dir,&tracker[3]);+alloctest_ref_tracker_alloc4(&ref_dir,&tracker[4]);+alloctest_ref_tracker_alloc5(&ref_dir,&tracker[5]);+alloctest_ref_tracker_alloc6(&ref_dir,&tracker[6]);+alloctest_ref_tracker_alloc7(&ref_dir,&tracker[7]);+alloctest_ref_tracker_alloc8(&ref_dir,&tracker[8]);+alloctest_ref_tracker_alloc9(&ref_dir,&tracker[9]);+alloctest_ref_tracker_alloc10(&ref_dir,&tracker[10]);+alloctest_ref_tracker_alloc11(&ref_dir,&tracker[11]);+alloctest_ref_tracker_alloc12(&ref_dir,&tracker[12]);+alloctest_ref_tracker_alloc13(&ref_dir,&tracker[13]);+alloctest_ref_tracker_alloc14(&ref_dir,&tracker[14]);+alloctest_ref_tracker_alloc15(&ref_dir,&tracker[15]);+alloctest_ref_tracker_alloc16(&ref_dir,&tracker[16]);+alloctest_ref_tracker_alloc17(&ref_dir,&tracker[17]);+alloctest_ref_tracker_alloc18(&ref_dir,&tracker[18]);+alloctest_ref_tracker_alloc19(&ref_dir,&tracker[19]);++/* free all trackers but first 0 and 1. */+for(i=2;i<ARRAY_SIZE(tracker);i++)+alloctest_ref_tracker_free(&ref_dir,&tracker[i]);++/* Attempt to free an already freed tracker. */+alloctest_ref_tracker_free(&ref_dir,&tracker[2]);++while(!atomic_read(&test_ref_timer_done))+msleep(1);++/* This should warn about tracker[0] & tracker[1] being not freed. */+ref_tracker_dir_exit(&ref_dir);++return0;+}++staticvoid__exittest_ref_tracker_exit(void)+{+}++module_init(test_ref_tracker_init);+module_exit(test_ref_tracker_exit);++MODULE_LICENSE("GPL v2");
From: Eric Dumazet <hidden> Date: 2021-12-03 02:47:24
From: Eric Dumazet <redacted>
They should replace dev_hold() and dev_put().
To use these helpers, each data structure owning a refcount
should also use a "netdevice_tracker" to pair the hold and put.
Whenever a leak happens, we will get precise stack traces
of the point dev_hold_track() happened, at device dismantle phase.
Signed-off-by: Eric Dumazet <redacted>
---
include/linux/netdevice.h | 44 +++++++++++++++++++++++++++++++++++++++
net/Kconfig | 8 +++++++
net/core/dev.c | 3 +++
3 files changed, 55 insertions(+)
@@ -1044,7 +1044,7 @@ static int rx_queue_add_kobject(struct net_device *dev, int index)/* Kobject_put later will trigger rx_queue_release call which*decreasesdevrefcount:Takethatreferencehere*/-dev_hold(queue->dev);+dev_hold_track(queue->dev,&queue->dev_tracker,GFP_KERNEL);kobj->kset=dev->queues_kset;error=kobject_init_and_add(kobj,&rx_queue_ktype,NULL,
@@ -1647,7 +1647,7 @@ static int netdev_queue_add_kobject(struct net_device *dev, int index)/* Kobject_put later will trigger netdev_queue_release call*whichdecreasesdevrefcount:Takethatreferencehere*/-dev_hold(queue->dev);+dev_hold_track(queue->dev,&queue->dev_tracker,GFP_KERNEL);kobj->kset=dev->queues_kset;error=kobject_init_and_add(kobj,&netdev_queue_ktype,NULL,
From: Eric Dumazet <hidden> Date: 2021-12-03 02:47:35
From: Eric Dumazet <redacted>
This helper might hold a netdev reference for a long time,
lets add reference tracking.
Signed-off-by: Eric Dumazet <redacted>
---
net/ethtool/ioctl.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
From: Eric Dumazet <hidden> Date: 2021-12-03 02:47:53
From: Eric Dumazet <redacted>
Note that other ip_tunnel users do not seem to hold a reference
on tunnel->dev. Probably needs some investigations.
Signed-off-by: Eric Dumazet <redacted>
---
include/net/ip_tunnels.h | 3 +++
net/ipv6/sit.c | 4 ++--
2 files changed, 5 insertions(+), 2 deletions(-)
@@ -104,7 +104,10 @@ struct metadata_dst;structip_tunnel{structip_tunnel__rcu*next;structhlist_nodehash_node;+structnet_device*dev;+netdevice_trackerdev_tracker;+structnet*net;/* netns for packet i/o */unsignedlongerr_time;/* Time when the last ICMP error
@@ -6537,6 +6537,7 @@ static __latent_entropy void net_rx_action(struct softirq_action *h)structnetdev_adjacent{structnet_device*dev;+netdevice_trackerdev_tracker;/* upper master flag, there can only be one master device per list */boolmaster;
@@ -7301,7 +7302,7 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev,adj->ref_nr=1;adj->private=private;adj->ignore=false;-dev_hold(adj_dev);+dev_hold_track(adj_dev,&adj->dev_tracker,GFP_KERNEL);pr_debug("Insert adjacency: dev %s adj_dev %s adj->ref_nr %d; dev_hold on %s\n",dev->name,adj_dev->name,adj->ref_nr,adj_dev->name);
@@ -7330,8 +7331,8 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev,if(netdev_adjacent_is_neigh_list(dev,adj_dev,dev_list))netdev_adjacent_sysfs_del(dev,adj_dev->name,dev_list);free_adj:+dev_put_track(adj_dev,&adj->dev_tracker);kfree(adj);-dev_put(adj_dev);returnret;}
@@ -7372,7 +7373,7 @@ static void __netdev_adjacent_dev_remove(struct net_device *dev,list_del_rcu(&adj->list);pr_debug("adjacency: dev_put for %s, because link removed from %s to %s\n",adj_dev->name,dev->name,adj_dev->name);-dev_put(adj_dev);+dev_put_track(adj_dev,&adj->dev_tracker);kfree_rcu(adj,rcu);}
From: Eric Dumazet <hidden> Date: 2021-12-03 02:48:22
From: Eric Dumazet <redacted>
Add a netdevice_tracker inside struct net_device, to track
the self reference when a device is in lweventlist.
Signed-off-by: Eric Dumazet <redacted>
---
include/linux/netdevice.h | 1 +
net/core/link_watch.c | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
@@ -696,7 +696,7 @@ static int vif_delete(struct mr_table *mrt, int vifi, int notify,if(v->flags&(VIFF_TUNNEL|VIFF_REGISTER)&&!notify)unregister_netdevice_queue(dev,head);-dev_put(dev);+dev_put_track(dev,&v->dev_tracker);return0;}
@@ -896,6 +896,7 @@ static int vif_add(struct net *net, struct mr_table *mrt,/* And finish update writing critical data */write_lock_bh(&mrt_lock);v->dev=dev;+netdev_tracker_alloc(dev,&v->dev_tracker,GFP_ATOMIC);if(v->flags&VIFF_REGISTER)mrt->mroute_reg_vif_num=vifi;if(vifi+1>mrt->maxvif)
@@ -746,7 +746,7 @@ static int mif6_delete(struct mr_table *mrt, int vifi, int notify,if((v->flags&MIFF_REGISTER)&&!notify)unregister_netdevice_queue(dev,head);-dev_put(dev);+dev_put_track(dev,&v->dev_tracker);return0;}
@@ -919,6 +919,7 @@ static int mif6_add(struct net *net, struct mr_table *mrt,/* And finish update writing critical data */write_lock_bh(&mrt_lock);v->dev=dev;+netdev_tracker_alloc(dev,&v->dev_tracker,GFP_ATOMIC);#ifdef CONFIG_IPV6_PIMSM_V2if(v->flags&MIFF_REGISTER)mrt->mroute_reg_vif_num=vifi;
From: Jakub Kicinski <kuba@kernel.org> Date: 2021-12-04 00:47:51
On Thu, 2 Dec 2021 18:46:17 -0800 Eric Dumazet wrote:
Two first patches add a generic infrastructure, that will be used
to get tracking of refcount increments/decrements.
The general idea is to be able to precisely pair each decrement with
a corresponding prior increment. Both share a cookie, basically
a pointer to private data storing stack traces.
The third place adds dev_hold_track() and dev_put_track() helpers
(CONFIG_NET_DEV_REFCNT_TRACKER)
Then a series of 20 patches converts some dev_hold()/dev_put()
pairs to new hepers : dev_hold_track() and dev_put_track().
Hopefully this will be used by developpers and syzbot to
root cause bugs that cause netdevice dismantles freezes.
With CONFIG_PCPU_DEV_REFCNT=n option, we were able to detect
some class of bugs, but too late (when too many dev_put()
were happening).
Hi Eric, there's a handful of kdoc warnings added here:
include/linux/netdevice.h:2278: warning: Function parameter or member 'refcnt_tracker' not described in 'net_device'
include/net/devlink.h:679: warning: Function parameter or member 'dev_tracker' not described in 'devlink_trap_metadata'
include/linux/netdevice.h:2283: warning: Function parameter or member 'refcnt_tracker' not described in 'net_device'
include/linux/mroute_base.h:40: warning: Function parameter or member 'dev_tracker' not described in 'vif_device'
Would you mind following up? likely not worth re-spinning just for that.
From: Eric Dumazet <hidden> Date: 2021-12-04 01:00:27
On Fri, Dec 3, 2021 at 4:47 PM Jakub Kicinski [off-list ref] wrote:
On Thu, 2 Dec 2021 18:46:17 -0800 Eric Dumazet wrote:
quoted
Two first patches add a generic infrastructure, that will be used
to get tracking of refcount increments/decrements.
The general idea is to be able to precisely pair each decrement with
a corresponding prior increment. Both share a cookie, basically
a pointer to private data storing stack traces.
The third place adds dev_hold_track() and dev_put_track() helpers
(CONFIG_NET_DEV_REFCNT_TRACKER)
Then a series of 20 patches converts some dev_hold()/dev_put()
pairs to new hepers : dev_hold_track() and dev_put_track().
Hopefully this will be used by developpers and syzbot to
root cause bugs that cause netdevice dismantles freezes.
With CONFIG_PCPU_DEV_REFCNT=n option, we were able to detect
some class of bugs, but too late (when too many dev_put()
were happening).
Hi Eric, there's a handful of kdoc warnings added here:
include/linux/netdevice.h:2278: warning: Function parameter or member 'refcnt_tracker' not described in 'net_device'
include/net/devlink.h:679: warning: Function parameter or member 'dev_tracker' not described in 'devlink_trap_metadata'
include/linux/netdevice.h:2283: warning: Function parameter or member 'refcnt_tracker' not described in 'net_device'
include/linux/mroute_base.h:40: warning: Function parameter or member 'dev_tracker' not described in 'vif_device'
Would you mind following up? likely not worth re-spinning just for that.
Sure thing, I will insert a patch to fix this in the next round.
Thanks !
lib/ref_tracker.c:80:22: error: implicit declaration of function 'stack_trace_save'; did you mean 'stack_depot_save'? [-Werror=implicit-function-declaration]
I tried following these instructions, but this failed on my laptop.
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
All errors (new ones prefixed by >>):
lib/ref_tracker.c: In function 'ref_tracker_alloc':
quoted
quoted
lib/ref_tracker.c:80:22: error: implicit declaration of function 'stack_trace_save'; did you mean 'stack_depot_save'? [-Werror=implicit-function-declaration]
I tried following these instructions, but this failed on my laptop.
quoted
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
All errors (new ones prefixed by >>):
lib/ref_tracker.c: In function 'ref_tracker_alloc':
quoted
quoted
lib/ref_tracker.c:80:22: error: implicit declaration of function 'stack_trace_save'; did you mean 'stack_depot_save'? [-Werror=implicit-function-declaration]
lib/ref_tracker.c:81:22: error: implicit declaration of function 'filter_irq_stacks' [-Werror=implicit-function-declaration]
81 | nr_entries = filter_irq_stacks(entries, nr_entries);
| ^~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
Kconfig warnings: (for reference only)
WARNING: unmet direct dependencies detected for REF_TRACKER
Depends on STACKTRACE_SUPPORT
Selected by
- TEST_REF_TRACKER && RUNTIME_TESTING_MENU && DEBUG_KERNEL
This seems to be a bug unrelated to this patch series.
Ah... maybe I got the Kconfig thing wrong.
This is hard to believe we will have to duplicate " depends on
STACKTRACE_SUPPORT" for all trackers
will intend to have (I have a series for netns refcount tracking)
@@ -255,6 +255,7 @@ config PCPU_DEV_REFCNTconfigNET_DEV_REFCNT_TRACKERbool"Enable tracking in dev_put_track() and dev_hold_track()"+depends onSTACKTRACE_SUPPORTselectREF_TRACKERdefaultnhelp
I thought that having the dependency centralized in REF_TRACKER would
be enough really ?
config REF_TRACKER
bool
depends on STACKTRACE_SUPPORT
select STACKDEPOT