]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - queue-4.14/xfrm-reuse-uncached_list-to-track-xdsts.patch
patches for 4.9
[thirdparty/kernel/stable-queue.git] / queue-4.14 / xfrm-reuse-uncached_list-to-track-xdsts.patch
1 From 66a256e4423a44db3bd0433babc7ad6beaa5853d Mon Sep 17 00:00:00 2001
2 From: Xin Long <lucien.xin@gmail.com>
3 Date: Wed, 14 Feb 2018 19:06:02 +0800
4 Subject: xfrm: reuse uncached_list to track xdsts
5
6 [ Upstream commit 510c321b557121861601f9d259aadd65aa274f35 ]
7
8 In early time, when freeing a xdst, it would be inserted into
9 dst_garbage.list first. Then if it's refcnt was still held
10 somewhere, later it would be put into dst_busy_list in
11 dst_gc_task().
12
13 When one dev was being unregistered, the dev of these dsts in
14 dst_busy_list would be set with loopback_dev and put this dev.
15 So that this dev's removal wouldn't get blocked, and avoid the
16 kmsg warning:
17
18 kernel:unregister_netdevice: waiting for veth0 to become \
19 free. Usage count = 2
20
21 However after Commit 52df157f17e5 ("xfrm: take refcnt of dst
22 when creating struct xfrm_dst bundle"), the xdst will not be
23 freed with dst gc, and this warning happens.
24
25 To fix it, we need to find these xdsts that are still held by
26 others when removing the dev, and free xdst's dev and set it
27 with loopback_dev.
28
29 But unfortunately after flow_cache for xfrm was deleted, no
30 list tracks them anymore. So we need to save these xdsts
31 somewhere to release the xdst's dev later.
32
33 To make this easier, this patch is to reuse uncached_list to
34 track xdsts, so that the dev refcnt can be released in the
35 event NETDEV_UNREGISTER process of fib_netdev_notifier.
36
37 Thanks to Florian, we could move forward this fix quickly.
38
39 Fixes: 52df157f17e5 ("xfrm: take refcnt of dst when creating struct xfrm_dst bundle")
40 Reported-by: Jianlin Shi <jishi@redhat.com>
41 Reported-by: Hangbin Liu <liuhangbin@gmail.com>
42 Tested-by: Eyal Birger <eyal.birger@gmail.com>
43 Signed-off-by: Xin Long <lucien.xin@gmail.com>
44 Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
45 Signed-off-by: Sasha Levin (Microsoft) <sashal@kernel.org>
46 ---
47 include/net/ip6_route.h | 3 +++
48 include/net/route.h | 3 +++
49 net/ipv4/route.c | 21 +++++++++++++--------
50 net/ipv4/xfrm4_policy.c | 4 +++-
51 net/ipv6/route.c | 4 ++--
52 net/ipv6/xfrm6_policy.c | 5 +++++
53 6 files changed, 29 insertions(+), 11 deletions(-)
54
55 diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
56 index bee528135cf1..00eb9162dbf1 100644
57 --- a/include/net/ip6_route.h
58 +++ b/include/net/ip6_route.h
59 @@ -165,6 +165,9 @@ void rt6_mtu_change(struct net_device *dev, unsigned int mtu);
60 void rt6_remove_prefsrc(struct inet6_ifaddr *ifp);
61 void rt6_clean_tohost(struct net *net, struct in6_addr *gateway);
62
63 +void rt6_uncached_list_add(struct rt6_info *rt);
64 +void rt6_uncached_list_del(struct rt6_info *rt);
65 +
66 static inline const struct rt6_info *skb_rt6_info(const struct sk_buff *skb)
67 {
68 const struct dst_entry *dst = skb_dst(skb);
69 diff --git a/include/net/route.h b/include/net/route.h
70 index 6077a0fb3044..1ab8b6f82812 100644
71 --- a/include/net/route.h
72 +++ b/include/net/route.h
73 @@ -228,6 +228,9 @@ struct in_ifaddr;
74 void fib_add_ifaddr(struct in_ifaddr *);
75 void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *);
76
77 +void rt_add_uncached_list(struct rtable *rt);
78 +void rt_del_uncached_list(struct rtable *rt);
79 +
80 static inline void ip_rt_put(struct rtable *rt)
81 {
82 /* dst_release() accepts a NULL parameter.
83 diff --git a/net/ipv4/route.c b/net/ipv4/route.c
84 index a1bf87711bfa..9fc2dbce424f 100644
85 --- a/net/ipv4/route.c
86 +++ b/net/ipv4/route.c
87 @@ -1440,7 +1440,7 @@ struct uncached_list {
88
89 static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt_uncached_list);
90
91 -static void rt_add_uncached_list(struct rtable *rt)
92 +void rt_add_uncached_list(struct rtable *rt)
93 {
94 struct uncached_list *ul = raw_cpu_ptr(&rt_uncached_list);
95
96 @@ -1451,14 +1451,8 @@ static void rt_add_uncached_list(struct rtable *rt)
97 spin_unlock_bh(&ul->lock);
98 }
99
100 -static void ipv4_dst_destroy(struct dst_entry *dst)
101 +void rt_del_uncached_list(struct rtable *rt)
102 {
103 - struct dst_metrics *p = (struct dst_metrics *)DST_METRICS_PTR(dst);
104 - struct rtable *rt = (struct rtable *) dst;
105 -
106 - if (p != &dst_default_metrics && refcount_dec_and_test(&p->refcnt))
107 - kfree(p);
108 -
109 if (!list_empty(&rt->rt_uncached)) {
110 struct uncached_list *ul = rt->rt_uncached_list;
111
112 @@ -1468,6 +1462,17 @@ static void ipv4_dst_destroy(struct dst_entry *dst)
113 }
114 }
115
116 +static void ipv4_dst_destroy(struct dst_entry *dst)
117 +{
118 + struct dst_metrics *p = (struct dst_metrics *)DST_METRICS_PTR(dst);
119 + struct rtable *rt = (struct rtable *)dst;
120 +
121 + if (p != &dst_default_metrics && refcount_dec_and_test(&p->refcnt))
122 + kfree(p);
123 +
124 + rt_del_uncached_list(rt);
125 +}
126 +
127 void rt_flush_dev(struct net_device *dev)
128 {
129 struct net *net = dev_net(dev);
130 diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
131 index 4b586e7d5637..fbebda67ac1b 100644
132 --- a/net/ipv4/xfrm4_policy.c
133 +++ b/net/ipv4/xfrm4_policy.c
134 @@ -103,6 +103,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
135 xdst->u.rt.rt_mtu_locked = rt->rt_mtu_locked;
136 xdst->u.rt.rt_table_id = rt->rt_table_id;
137 INIT_LIST_HEAD(&xdst->u.rt.rt_uncached);
138 + rt_add_uncached_list(&xdst->u.rt);
139
140 return 0;
141 }
142 @@ -242,7 +243,8 @@ static void xfrm4_dst_destroy(struct dst_entry *dst)
143 struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
144
145 dst_destroy_metrics_generic(dst);
146 -
147 + if (xdst->u.rt.rt_uncached_list)
148 + rt_del_uncached_list(&xdst->u.rt);
149 xfrm_dst_destroy(xdst);
150 }
151
152 diff --git a/net/ipv6/route.c b/net/ipv6/route.c
153 index 00f8fe8cebd5..620553401d75 100644
154 --- a/net/ipv6/route.c
155 +++ b/net/ipv6/route.c
156 @@ -124,7 +124,7 @@ struct uncached_list {
157
158 static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list);
159
160 -static void rt6_uncached_list_add(struct rt6_info *rt)
161 +void rt6_uncached_list_add(struct rt6_info *rt)
162 {
163 struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list);
164
165 @@ -135,7 +135,7 @@ static void rt6_uncached_list_add(struct rt6_info *rt)
166 spin_unlock_bh(&ul->lock);
167 }
168
169 -static void rt6_uncached_list_del(struct rt6_info *rt)
170 +void rt6_uncached_list_del(struct rt6_info *rt)
171 {
172 if (!list_empty(&rt->rt6i_uncached)) {
173 struct uncached_list *ul = rt->rt6i_uncached_list;
174 diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
175 index d6b012295b45..d99929113230 100644
176 --- a/net/ipv6/xfrm6_policy.c
177 +++ b/net/ipv6/xfrm6_policy.c
178 @@ -113,6 +113,9 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
179 xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway;
180 xdst->u.rt6.rt6i_dst = rt->rt6i_dst;
181 xdst->u.rt6.rt6i_src = rt->rt6i_src;
182 + INIT_LIST_HEAD(&xdst->u.rt6.rt6i_uncached);
183 + rt6_uncached_list_add(&xdst->u.rt6);
184 + atomic_inc(&dev_net(dev)->ipv6.rt6_stats->fib_rt_uncache);
185
186 return 0;
187 }
188 @@ -243,6 +246,8 @@ static void xfrm6_dst_destroy(struct dst_entry *dst)
189 if (likely(xdst->u.rt6.rt6i_idev))
190 in6_dev_put(xdst->u.rt6.rt6i_idev);
191 dst_destroy_metrics_generic(dst);
192 + if (xdst->u.rt6.rt6i_uncached_list)
193 + rt6_uncached_list_del(&xdst->u.rt6);
194 xfrm_dst_destroy(xdst);
195 }
196
197 --
198 2.19.1
199