diff options
Diffstat (limited to 'net/ipv6/tunnel6.c')
-rw-r--r-- | net/ipv6/tunnel6.c | 37 |
1 files changed, 24 insertions, 13 deletions
diff --git a/net/ipv6/tunnel6.c b/net/ipv6/tunnel6.c index fc3c86a47452..4f3cec12aa85 100644 --- a/net/ipv6/tunnel6.c +++ b/net/ipv6/tunnel6.c | |||
@@ -30,28 +30,31 @@ | |||
30 | #include <net/protocol.h> | 30 | #include <net/protocol.h> |
31 | #include <net/xfrm.h> | 31 | #include <net/xfrm.h> |
32 | 32 | ||
33 | static struct xfrm6_tunnel *tunnel6_handlers; | 33 | static struct xfrm6_tunnel __rcu *tunnel6_handlers __read_mostly; |
34 | static struct xfrm6_tunnel *tunnel46_handlers; | 34 | static struct xfrm6_tunnel __rcu *tunnel46_handlers __read_mostly; |
35 | static DEFINE_MUTEX(tunnel6_mutex); | 35 | static DEFINE_MUTEX(tunnel6_mutex); |
36 | 36 | ||
37 | int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family) | 37 | int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family) |
38 | { | 38 | { |
39 | struct xfrm6_tunnel **pprev; | 39 | struct xfrm6_tunnel __rcu **pprev; |
40 | struct xfrm6_tunnel *t; | ||
40 | int ret = -EEXIST; | 41 | int ret = -EEXIST; |
41 | int priority = handler->priority; | 42 | int priority = handler->priority; |
42 | 43 | ||
43 | mutex_lock(&tunnel6_mutex); | 44 | mutex_lock(&tunnel6_mutex); |
44 | 45 | ||
45 | for (pprev = (family == AF_INET6) ? &tunnel6_handlers : &tunnel46_handlers; | 46 | for (pprev = (family == AF_INET6) ? &tunnel6_handlers : &tunnel46_handlers; |
46 | *pprev; pprev = &(*pprev)->next) { | 47 | (t = rcu_dereference_protected(*pprev, |
47 | if ((*pprev)->priority > priority) | 48 | lockdep_is_held(&tunnel6_mutex))) != NULL; |
49 | pprev = &t->next) { | ||
50 | if (t->priority > priority) | ||
48 | break; | 51 | break; |
49 | if ((*pprev)->priority == priority) | 52 | if (t->priority == priority) |
50 | goto err; | 53 | goto err; |
51 | } | 54 | } |
52 | 55 | ||
53 | handler->next = *pprev; | 56 | handler->next = *pprev; |
54 | *pprev = handler; | 57 | rcu_assign_pointer(*pprev, handler); |
55 | 58 | ||
56 | ret = 0; | 59 | ret = 0; |
57 | 60 | ||
@@ -65,14 +68,17 @@ EXPORT_SYMBOL(xfrm6_tunnel_register); | |||
65 | 68 | ||
66 | int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family) | 69 | int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family) |
67 | { | 70 | { |
68 | struct xfrm6_tunnel **pprev; | 71 | struct xfrm6_tunnel __rcu **pprev; |
72 | struct xfrm6_tunnel *t; | ||
69 | int ret = -ENOENT; | 73 | int ret = -ENOENT; |
70 | 74 | ||
71 | mutex_lock(&tunnel6_mutex); | 75 | mutex_lock(&tunnel6_mutex); |
72 | 76 | ||
73 | for (pprev = (family == AF_INET6) ? &tunnel6_handlers : &tunnel46_handlers; | 77 | for (pprev = (family == AF_INET6) ? &tunnel6_handlers : &tunnel46_handlers; |
74 | *pprev; pprev = &(*pprev)->next) { | 78 | (t = rcu_dereference_protected(*pprev, |
75 | if (*pprev == handler) { | 79 | lockdep_is_held(&tunnel6_mutex))) != NULL; |
80 | pprev = &t->next) { | ||
81 | if (t == handler) { | ||
76 | *pprev = handler->next; | 82 | *pprev = handler->next; |
77 | ret = 0; | 83 | ret = 0; |
78 | break; | 84 | break; |
@@ -88,6 +94,11 @@ int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family) | |||
88 | 94 | ||
89 | EXPORT_SYMBOL(xfrm6_tunnel_deregister); | 95 | EXPORT_SYMBOL(xfrm6_tunnel_deregister); |
90 | 96 | ||
97 | #define for_each_tunnel_rcu(head, handler) \ | ||
98 | for (handler = rcu_dereference(head); \ | ||
99 | handler != NULL; \ | ||
100 | handler = rcu_dereference(handler->next)) \ | ||
101 | |||
91 | static int tunnel6_rcv(struct sk_buff *skb) | 102 | static int tunnel6_rcv(struct sk_buff *skb) |
92 | { | 103 | { |
93 | struct xfrm6_tunnel *handler; | 104 | struct xfrm6_tunnel *handler; |
@@ -95,7 +106,7 @@ static int tunnel6_rcv(struct sk_buff *skb) | |||
95 | if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) | 106 | if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) |
96 | goto drop; | 107 | goto drop; |
97 | 108 | ||
98 | for (handler = tunnel6_handlers; handler; handler = handler->next) | 109 | for_each_tunnel_rcu(tunnel6_handlers, handler) |
99 | if (!handler->handler(skb)) | 110 | if (!handler->handler(skb)) |
100 | return 0; | 111 | return 0; |
101 | 112 | ||
@@ -113,7 +124,7 @@ static int tunnel46_rcv(struct sk_buff *skb) | |||
113 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) | 124 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) |
114 | goto drop; | 125 | goto drop; |
115 | 126 | ||
116 | for (handler = tunnel46_handlers; handler; handler = handler->next) | 127 | for_each_tunnel_rcu(tunnel46_handlers, handler) |
117 | if (!handler->handler(skb)) | 128 | if (!handler->handler(skb)) |
118 | return 0; | 129 | return 0; |
119 | 130 | ||
@@ -129,7 +140,7 @@ static void tunnel6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
129 | { | 140 | { |
130 | struct xfrm6_tunnel *handler; | 141 | struct xfrm6_tunnel *handler; |
131 | 142 | ||
132 | for (handler = tunnel6_handlers; handler; handler = handler->next) | 143 | for_each_tunnel_rcu(tunnel6_handlers, handler) |
133 | if (!handler->err_handler(skb, opt, type, code, offset, info)) | 144 | if (!handler->err_handler(skb, opt, type, code, offset, info)) |
134 | break; | 145 | break; |
135 | } | 146 | } |