diff options
author | David S. Miller <davem@davemloft.net> | 2011-01-27 17:58:42 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-01-27 17:59:31 -0500 |
commit | 065825402c058f4a123ddc53dbbe864cc5caaf64 (patch) | |
tree | 7d0d5122a5315c5850a0b01ed7349e9eebf794e2 /net | |
parent | 1397e171f143878dd16ad5f8c99f7b9440cc8911 (diff) |
net: Store ipv4/ipv6 COW'd metrics in inetpeer cache.
Please note that the IPSEC dst entry metrics keep using
the generic metrics COW'ing mechanism using kmalloc/kfree.
This gives the IPSEC routes an opportunity to use metrics
which are unique to their encapsulated paths.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/route.c | 18 | ||||
-rw-r--r-- | net/ipv6/route.c | 33 |
2 files changed, 42 insertions, 9 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 980030d4e4ae..68cee358d9a3 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
@@ -154,25 +154,30 @@ static void ipv4_dst_ifdown(struct dst_entry *dst, struct net_device *dev, | |||
154 | 154 | ||
155 | static u32 *ipv4_cow_metrics(struct dst_entry *dst, unsigned long old) | 155 | static u32 *ipv4_cow_metrics(struct dst_entry *dst, unsigned long old) |
156 | { | 156 | { |
157 | u32 *p = kmalloc(sizeof(u32) * RTAX_MAX, GFP_ATOMIC); | 157 | struct rtable *rt = (struct rtable *) dst; |
158 | struct inet_peer *peer; | ||
159 | u32 *p = NULL; | ||
160 | |||
161 | if (!rt->peer) | ||
162 | rt_bind_peer(rt, 1); | ||
158 | 163 | ||
159 | if (p) { | 164 | peer = rt->peer; |
165 | if (peer) { | ||
160 | u32 *old_p = __DST_METRICS_PTR(old); | 166 | u32 *old_p = __DST_METRICS_PTR(old); |
161 | unsigned long prev, new; | 167 | unsigned long prev, new; |
162 | 168 | ||
163 | memcpy(p, old_p, sizeof(u32) * RTAX_MAX); | 169 | p = peer->metrics; |
170 | if (inet_metrics_new(peer)) | ||
171 | memcpy(p, old_p, sizeof(u32) * RTAX_MAX); | ||
164 | 172 | ||
165 | new = (unsigned long) p; | 173 | new = (unsigned long) p; |
166 | prev = cmpxchg(&dst->_metrics, old, new); | 174 | prev = cmpxchg(&dst->_metrics, old, new); |
167 | 175 | ||
168 | if (prev != old) { | 176 | if (prev != old) { |
169 | kfree(p); | ||
170 | p = __DST_METRICS_PTR(prev); | 177 | p = __DST_METRICS_PTR(prev); |
171 | if (prev & DST_METRICS_READ_ONLY) | 178 | if (prev & DST_METRICS_READ_ONLY) |
172 | p = NULL; | 179 | p = NULL; |
173 | } else { | 180 | } else { |
174 | struct rtable *rt = (struct rtable *) dst; | ||
175 | |||
176 | if (rt->fi) { | 181 | if (rt->fi) { |
177 | fib_info_put(rt->fi); | 182 | fib_info_put(rt->fi); |
178 | rt->fi = NULL; | 183 | rt->fi = NULL; |
@@ -1753,7 +1758,6 @@ static void ipv4_dst_destroy(struct dst_entry *dst) | |||
1753 | struct rtable *rt = (struct rtable *) dst; | 1758 | struct rtable *rt = (struct rtable *) dst; |
1754 | struct inet_peer *peer = rt->peer; | 1759 | struct inet_peer *peer = rt->peer; |
1755 | 1760 | ||
1756 | dst_destroy_metrics_generic(dst); | ||
1757 | if (rt->fi) { | 1761 | if (rt->fi) { |
1758 | fib_info_put(rt->fi); | 1762 | fib_info_put(rt->fi); |
1759 | rt->fi = NULL; | 1763 | rt->fi = NULL; |
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 691798c169a5..72609f1c6158 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -97,6 +97,36 @@ static struct rt6_info *rt6_get_route_info(struct net *net, | |||
97 | struct in6_addr *gwaddr, int ifindex); | 97 | struct in6_addr *gwaddr, int ifindex); |
98 | #endif | 98 | #endif |
99 | 99 | ||
100 | static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) | ||
101 | { | ||
102 | struct rt6_info *rt = (struct rt6_info *) dst; | ||
103 | struct inet_peer *peer; | ||
104 | u32 *p = NULL; | ||
105 | |||
106 | if (!rt->rt6i_peer) | ||
107 | rt6_bind_peer(rt, 1); | ||
108 | |||
109 | peer = rt->rt6i_peer; | ||
110 | if (peer) { | ||
111 | u32 *old_p = __DST_METRICS_PTR(old); | ||
112 | unsigned long prev, new; | ||
113 | |||
114 | p = peer->metrics; | ||
115 | if (inet_metrics_new(peer)) | ||
116 | memcpy(p, old_p, sizeof(u32) * RTAX_MAX); | ||
117 | |||
118 | new = (unsigned long) p; | ||
119 | prev = cmpxchg(&dst->_metrics, old, new); | ||
120 | |||
121 | if (prev != old) { | ||
122 | p = __DST_METRICS_PTR(prev); | ||
123 | if (prev & DST_METRICS_READ_ONLY) | ||
124 | p = NULL; | ||
125 | } | ||
126 | } | ||
127 | return p; | ||
128 | } | ||
129 | |||
100 | static struct dst_ops ip6_dst_ops_template = { | 130 | static struct dst_ops ip6_dst_ops_template = { |
101 | .family = AF_INET6, | 131 | .family = AF_INET6, |
102 | .protocol = cpu_to_be16(ETH_P_IPV6), | 132 | .protocol = cpu_to_be16(ETH_P_IPV6), |
@@ -105,7 +135,7 @@ static struct dst_ops ip6_dst_ops_template = { | |||
105 | .check = ip6_dst_check, | 135 | .check = ip6_dst_check, |
106 | .default_advmss = ip6_default_advmss, | 136 | .default_advmss = ip6_default_advmss, |
107 | .default_mtu = ip6_default_mtu, | 137 | .default_mtu = ip6_default_mtu, |
108 | .cow_metrics = dst_cow_metrics_generic, | 138 | .cow_metrics = ipv6_cow_metrics, |
109 | .destroy = ip6_dst_destroy, | 139 | .destroy = ip6_dst_destroy, |
110 | .ifdown = ip6_dst_ifdown, | 140 | .ifdown = ip6_dst_ifdown, |
111 | .negative_advice = ip6_negative_advice, | 141 | .negative_advice = ip6_negative_advice, |
@@ -198,7 +228,6 @@ static void ip6_dst_destroy(struct dst_entry *dst) | |||
198 | rt->rt6i_idev = NULL; | 228 | rt->rt6i_idev = NULL; |
199 | in6_dev_put(idev); | 229 | in6_dev_put(idev); |
200 | } | 230 | } |
201 | dst_destroy_metrics_generic(dst); | ||
202 | if (peer) { | 231 | if (peer) { |
203 | rt->rt6i_peer = NULL; | 232 | rt->rt6i_peer = NULL; |
204 | inet_putpeer(peer); | 233 | inet_putpeer(peer); |