]>
Commit | Line | Data |
---|---|---|
5e077f5d GKH |
1 | From foo@baz Thu Jun 29 19:45:34 CEST 2017 |
2 | From: Wei Wang <weiwan@google.com> | |
3 | Date: Fri, 16 Jun 2017 10:46:37 -0700 | |
4 | Subject: decnet: always not take dst->__refcnt when inserting dst into hash table | |
5 | ||
6 | From: Wei Wang <weiwan@google.com> | |
7 | ||
8 | ||
9 | [ Upstream commit 76371d2e3ad1f84426a30ebcd8c3b9b98f4c724f ] | |
10 | ||
11 | In the existing dn_route.c code, dn_route_output_slow() takes | |
12 | dst->__refcnt before calling dn_insert_route() while dn_route_input_slow() | |
13 | does not take dst->__refcnt before calling dn_insert_route(). | |
14 | This makes the whole routing code very buggy. | |
15 | In dn_dst_check_expire(), dnrt_free() is called when rt expires. This | |
16 | makes the routes inserted by dn_route_output_slow() not able to be | |
17 | freed as the refcnt is not released. | |
18 | In dn_dst_gc(), dnrt_drop() is called to release rt which could | |
19 | potentially cause the dst->__refcnt to be dropped to -1. | |
20 | In dn_run_flush(), dst_free() is called to release all the dst. Again, | |
21 | it makes the dst inserted by dn_route_output_slow() not able to be | |
22 | released and also, it does not wait on the rcu and could potentially | |
23 | cause crash in the path where other users still refer to this dst. | |
24 | ||
25 | This patch makes sure both input and output path do not take | |
26 | dst->__refcnt before calling dn_insert_route() and also makes sure | |
27 | dnrt_free()/dst_free() is called when removing dst from the hash table. | |
28 | The only difference between those 2 calls is that dnrt_free() waits on | |
29 | the rcu while dst_free() does not. | |
30 | ||
31 | Signed-off-by: Wei Wang <weiwan@google.com> | |
32 | Acked-by: Martin KaFai Lau <kafai@fb.com> | |
33 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
34 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
35 | --- | |
36 | net/decnet/dn_route.c | 14 ++++---------- | |
37 | 1 file changed, 4 insertions(+), 10 deletions(-) | |
38 | ||
39 | --- a/net/decnet/dn_route.c | |
40 | +++ b/net/decnet/dn_route.c | |
41 | @@ -189,12 +189,6 @@ static inline void dnrt_free(struct dn_r | |
42 | call_rcu_bh(&rt->dst.rcu_head, dst_rcu_free); | |
43 | } | |
44 | ||
45 | -static inline void dnrt_drop(struct dn_route *rt) | |
46 | -{ | |
47 | - dst_release(&rt->dst); | |
48 | - call_rcu_bh(&rt->dst.rcu_head, dst_rcu_free); | |
49 | -} | |
50 | - | |
51 | static void dn_dst_check_expire(unsigned long dummy) | |
52 | { | |
53 | int i; | |
54 | @@ -249,7 +243,7 @@ static int dn_dst_gc(struct dst_ops *ops | |
55 | } | |
56 | *rtp = rt->dst.dn_next; | |
57 | rt->dst.dn_next = NULL; | |
58 | - dnrt_drop(rt); | |
59 | + dnrt_free(rt); | |
60 | break; | |
61 | } | |
62 | spin_unlock_bh(&dn_rt_hash_table[i].lock); | |
63 | @@ -351,7 +345,7 @@ static int dn_insert_route(struct dn_rou | |
64 | dst_use(&rth->dst, now); | |
65 | spin_unlock_bh(&dn_rt_hash_table[hash].lock); | |
66 | ||
67 | - dnrt_drop(rt); | |
68 | + dst_free(&rt->dst); | |
69 | *rp = rth; | |
70 | return 0; | |
71 | } | |
72 | @@ -381,7 +375,7 @@ static void dn_run_flush(unsigned long d | |
73 | for(; rt; rt = next) { | |
74 | next = rcu_dereference_raw(rt->dst.dn_next); | |
75 | RCU_INIT_POINTER(rt->dst.dn_next, NULL); | |
76 | - dst_free((struct dst_entry *)rt); | |
77 | + dnrt_free(rt); | |
78 | } | |
79 | ||
80 | nothing_to_declare: | |
81 | @@ -1195,7 +1189,7 @@ make_route: | |
82 | if (dev_out->flags & IFF_LOOPBACK) | |
83 | flags |= RTCF_LOCAL; | |
84 | ||
85 | - rt = dst_alloc(&dn_dst_ops, dev_out, 1, DST_OBSOLETE_NONE, DST_HOST); | |
86 | + rt = dst_alloc(&dn_dst_ops, dev_out, 0, DST_OBSOLETE_NONE, DST_HOST); | |
87 | if (rt == NULL) | |
88 | goto e_nobufs; | |
89 |