@@ -5,6 +5,8 @@
enum {
OBJ_CNT_DEV_HOLD,
OBJ_CNT_DEV_PUT,
+ OBJ_CNT_DST_HOLD,
+ OBJ_CNT_DST_PUT,
OBJ_CNT_TYPE_MAX
};
@@ -227,6 +227,7 @@ static inline void dst_hold(struct dst_entry *dst)
* If your kernel compilation stops here, please check
* the placement of __refcnt in struct dst_entry
*/
+ obj_cnt_track_by_dev(dst, dst->dev, OBJ_CNT_DST_HOLD);
BUILD_BUG_ON(offsetof(struct dst_entry, __refcnt) & 63);
WARN_ON(atomic_inc_not_zero(&dst->__refcnt) == 0);
}
@@ -298,7 +299,12 @@ static inline void skb_dst_copy(struct sk_buff *nskb, const struct sk_buff *oskb
*/
static inline bool dst_hold_safe(struct dst_entry *dst)
{
- return atomic_inc_not_zero(&dst->__refcnt);
+ if (atomic_inc_not_zero(&dst->__refcnt)) {
+ obj_cnt_track_by_dev(dst, dst->dev, OBJ_CNT_DST_HOLD);
+ return true;
+ }
+
+ return false;
}
/**
@@ -2071,8 +2071,9 @@ sk_dst_get(struct sock *sk)
rcu_read_lock();
dst = rcu_dereference(sk->sk_dst_cache);
- if (dst && !atomic_inc_not_zero(&dst->__refcnt))
+ if (dst && !dst_hold_safe(dst))
dst = NULL;
+
rcu_read_unlock();
return dst;
}
@@ -16,7 +16,9 @@ static spinlock_t obj_cnt_lock;
static char *obj_cnt_str[OBJ_CNT_TYPE_MAX] = {
"dev_hold",
- "dev_put"
+ "dev_put",
+ "dst_hold",
+ "dst_put"
};
struct obj_cnt {
@@ -169,6 +169,7 @@ void dst_release(struct dst_entry *dst)
if (dst) {
int newrefcnt;
+ obj_cnt_track_by_dev(dst, dst->dev, OBJ_CNT_DST_PUT);
newrefcnt = atomic_dec_return(&dst->__refcnt);
if (WARN_ONCE(newrefcnt < 0, "dst_release underflow"))
net_warn_ratelimited("%s: dst:%p refcnt:%d\n",
@@ -184,6 +185,7 @@ void dst_release_immediate(struct dst_entry *dst)
if (dst) {
int newrefcnt;
+ obj_cnt_track_by_dev(dst, dst->dev, OBJ_CNT_DST_PUT);
newrefcnt = atomic_dec_return(&dst->__refcnt);
if (WARN_ONCE(newrefcnt < 0, "dst_release_immediate underflow"))
net_warn_ratelimited("%s: dst:%p refcnt:%d\n",
Two types are added into obj_cnt to count dst_hold* and dst_release*, and all it does is put obj_cnt_track_by_dev() into these two functions. Here is an example to track the refcnt of a dst whose dev is dummy0: # sysctl -w obj_cnt.control="clear" # clear the old result # sysctl -w obj_cnt.type=0xc # enable dst_hold/put track # sysctl -w obj_cnt.name=dummy0 # count dst_hold/put(dst) # sysctl -w obj_cnt.nr_entries=4 # save 4 frames' call trace # # ip link add dummy0 type dummy # ip link set dummy0 up # ip addr add 1.1.1.1/24 dev dummy0 # ping 1.1.1.2 -c 2 # ip link set dummy0 down # ip link del dummy0 # sysctl -w obj_cnt.control="scan" # print the new result # dmesg OBJ_CNT: obj_cnt_dump: obj: ffff9e45d7e8b780, type: dst_hold, cnt: 1,: rt_cache_route+0x45/0xc0 rt_set_nexthop.constprop.63+0x143/0x3c0 ip_route_output_key_hash_rcu+0x256/0x9a0 ip_route_output_key_hash+0x72/0xa0 OBJ_CNT: obj_cnt_dump: obj: ffff9e45cbef9100, type: dst_put, cnt: 1,: dst_release+0x2a/0x90 __dev_queue_xmit+0x72c/0xc90 ip6_finish_output2+0x2d2/0x660 ip6_output+0x6e/0x130 ... OBJ_CNT: obj_cnt_dump: obj: ffff9e45ca463d00, type: dst_put, cnt: 1,: dst_release+0x2a/0x90 __dev_queue_xmit+0x72c/0xc90 ip6_finish_output2+0x1e8/0x660 ip6_output+0x6e/0x130 OBJ_CNT: obj_cnt_dump: obj: ffff9e45d7e8b780, type: dst_hold, cnt: 2,: ip_route_output_key_hash_rcu+0x88e/0x9a0 ip_route_output_key_hash+0x72/0xa0 ip_route_output_flow+0x19/0x50 raw_sendmsg+0x32b/0xe40 ... Signed-off-by: Xin Long <lucien.xin@gmail.com> --- include/linux/obj_cnt.h | 2 ++ include/net/dst.h | 8 +++++++- include/net/sock.h | 3 ++- lib/obj_cnt.c | 4 +++- net/core/dst.c | 2 ++ 5 files changed, 16 insertions(+), 3 deletions(-)