aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>2006-03-20 20:00:26 -0500
committerDavid S. Miller <davem@davemloft.net>2006-03-20 20:00:26 -0500
commit554cfb7ee5d4f2d0edb280e66e4a2db1906a8300 (patch)
tree586bca2143a23157ac2619b1c211144662ddf8f3
parent519fbd8715ce6413ec8e122bf02f09b0e49d3ca5 (diff)
[IPV6]: ROUTE: Eliminate lock for default route pointer.
And prepare for more advanced router selection. Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/ip6_route.h2
-rw-r--r--net/ipv6/ip6_fib.c1
-rw-r--r--net/ipv6/route.c197
3 files changed, 69 insertions, 131 deletions
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 1f2e428ca364..01acca06d6dd 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -91,8 +91,6 @@ extern struct rt6_info * rt6_add_dflt_router(struct in6_addr *gwaddr,
91 91
92extern void rt6_purge_dflt_routers(void); 92extern void rt6_purge_dflt_routers(void);
93 93
94extern void rt6_reset_dflt_pointer(struct rt6_info *rt);
95
96extern void rt6_redirect(struct in6_addr *dest, 94extern void rt6_redirect(struct in6_addr *dest,
97 struct in6_addr *saddr, 95 struct in6_addr *saddr,
98 struct neighbour *neigh, 96 struct neighbour *neigh,
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 1bf6d9a769e6..2cb6149349bf 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -1105,7 +1105,6 @@ static int fib6_age(struct rt6_info *rt, void *arg)
1105 if (rt->rt6i_flags&RTF_EXPIRES && rt->rt6i_expires) { 1105 if (rt->rt6i_flags&RTF_EXPIRES && rt->rt6i_expires) {
1106 if (time_after(now, rt->rt6i_expires)) { 1106 if (time_after(now, rt->rt6i_expires)) {
1107 RT6_TRACE("expiring %p\n", rt); 1107 RT6_TRACE("expiring %p\n", rt);
1108 rt6_reset_dflt_pointer(rt);
1109 return -1; 1108 return -1;
1110 } 1109 }
1111 gc_args.more++; 1110 gc_args.more++;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 6a4019a4ca89..f71e2365b43d 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -74,6 +74,9 @@
74 74
75#define CLONE_OFFLINK_ROUTE 0 75#define CLONE_OFFLINK_ROUTE 0
76 76
77#define RT6_SELECT_F_IFACE 0x1
78#define RT6_SELECT_F_REACHABLE 0x2
79
77static int ip6_rt_max_size = 4096; 80static int ip6_rt_max_size = 4096;
78static int ip6_rt_gc_min_interval = HZ / 2; 81static int ip6_rt_gc_min_interval = HZ / 2;
79static int ip6_rt_gc_timeout = 60*HZ; 82static int ip6_rt_gc_timeout = 60*HZ;
@@ -216,148 +219,89 @@ static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt,
216} 219}
217 220
218/* 221/*
219 * pointer to the last default router chosen. BH is disabled locally. 222 * Default Router Selection (RFC 2461 6.3.6)
220 */ 223 */
221static struct rt6_info *rt6_dflt_pointer; 224static int inline rt6_check_dev(struct rt6_info *rt, int oif)
222static DEFINE_SPINLOCK(rt6_dflt_lock); 225{
226 struct net_device *dev = rt->rt6i_dev;
227 if (!oif || dev->ifindex == oif)
228 return 2;
229 if ((dev->flags & IFF_LOOPBACK) &&
230 rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif)
231 return 1;
232 return 0;
233}
223 234
224void rt6_reset_dflt_pointer(struct rt6_info *rt) 235static int inline rt6_check_neigh(struct rt6_info *rt)
225{ 236{
226 spin_lock_bh(&rt6_dflt_lock); 237 struct neighbour *neigh = rt->rt6i_nexthop;
227 if (rt == NULL || rt == rt6_dflt_pointer) { 238 int m = 0;
228 RT6_TRACE("reset default router: %p->NULL\n", rt6_dflt_pointer); 239 if (neigh) {
229 rt6_dflt_pointer = NULL; 240 read_lock_bh(&neigh->lock);
241 if (neigh->nud_state & NUD_VALID)
242 m = 1;
243 read_unlock_bh(&neigh->lock);
230 } 244 }
231 spin_unlock_bh(&rt6_dflt_lock); 245 return m;
232} 246}
233 247
234/* Default Router Selection (RFC 2461 6.3.6) */ 248static int rt6_score_route(struct rt6_info *rt, int oif,
235static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, int oif) 249 int strict)
236{ 250{
237 struct rt6_info *match = NULL; 251 int m = rt6_check_dev(rt, oif);
238 struct rt6_info *sprt; 252 if (!m && (strict & RT6_SELECT_F_IFACE))
239 int mpri = 0; 253 return -1;
254 if (rt6_check_neigh(rt))
255 m |= 4;
256 else if (strict & RT6_SELECT_F_REACHABLE)
257 return -1;
258 return m;
259}
240 260
241 for (sprt = rt; sprt; sprt = sprt->u.next) { 261static struct rt6_info *rt6_select(struct rt6_info **head, int oif,
242 struct neighbour *neigh; 262 int strict)
243 int m = 0; 263{
264 struct rt6_info *match = NULL, *last = NULL;
265 struct rt6_info *rt, *rt0 = *head;
266 u32 metric;
267 int mpri = -1;
244 268
245 if (!oif || 269 RT6_TRACE("%s(head=%p(*head=%p), oif=%d)\n",
246 (sprt->rt6i_dev && 270 __FUNCTION__, head, head ? *head : NULL, oif);
247 sprt->rt6i_dev->ifindex == oif))
248 m += 8;
249 271
250 if (rt6_check_expired(sprt)) 272 for (rt = rt0, metric = rt0->rt6i_metric;
273 rt && rt->rt6i_metric == metric;
274 rt = rt->u.next) {
275 int m;
276
277 if (rt6_check_expired(rt))
251 continue; 278 continue;
252 279
253 if (sprt == rt6_dflt_pointer) 280 last = rt;
254 m += 4; 281
255 282 m = rt6_score_route(rt, oif, strict);
256 if ((neigh = sprt->rt6i_nexthop) != NULL) { 283 if (m < 0)
257 read_lock_bh(&neigh->lock);
258 switch (neigh->nud_state) {
259 case NUD_REACHABLE:
260 m += 3;
261 break;
262
263 case NUD_STALE:
264 case NUD_DELAY:
265 case NUD_PROBE:
266 m += 2;
267 break;
268
269 case NUD_NOARP:
270 case NUD_PERMANENT:
271 m += 1;
272 break;
273
274 case NUD_INCOMPLETE:
275 default:
276 read_unlock_bh(&neigh->lock);
277 continue;
278 }
279 read_unlock_bh(&neigh->lock);
280 } else {
281 continue; 284 continue;
282 }
283 285
284 if (m > mpri || m >= 12) { 286 if (m > mpri) {
285 match = sprt; 287 match = rt;
286 mpri = m; 288 mpri = m;
287 if (m >= 12) {
288 /* we choose the last default router if it
289 * is in (probably) reachable state.
290 * If route changed, we should do pmtu
291 * discovery. --yoshfuji
292 */
293 break;
294 }
295 } 289 }
296 } 290 }
297 291
298 spin_lock(&rt6_dflt_lock); 292 if (!match &&
299 if (!match) { 293 (strict & RT6_SELECT_F_REACHABLE) &&
300 /* 294 last && last != rt0) {
301 * No default routers are known to be reachable. 295 /* no entries matched; do round-robin */
302 * SHOULD round robin 296 *head = rt0->u.next;
303 */ 297 rt0->u.next = last->u.next;
304 if (rt6_dflt_pointer) { 298 last->u.next = rt0;
305 for (sprt = rt6_dflt_pointer->u.next;
306 sprt; sprt = sprt->u.next) {
307 if (sprt->u.dst.obsolete <= 0 &&
308 sprt->u.dst.error == 0 &&
309 !rt6_check_expired(sprt)) {
310 match = sprt;
311 break;
312 }
313 }
314 for (sprt = rt;
315 !match && sprt;
316 sprt = sprt->u.next) {
317 if (sprt->u.dst.obsolete <= 0 &&
318 sprt->u.dst.error == 0 &&
319 !rt6_check_expired(sprt)) {
320 match = sprt;
321 break;
322 }
323 if (sprt == rt6_dflt_pointer)
324 break;
325 }
326 }
327 }
328
329 if (match) {
330 if (rt6_dflt_pointer != match)
331 RT6_TRACE("changed default router: %p->%p\n",
332 rt6_dflt_pointer, match);
333 rt6_dflt_pointer = match;
334 } 299 }
335 spin_unlock(&rt6_dflt_lock);
336 300
337 if (!match) { 301 RT6_TRACE("%s() => %p, score=%d\n",
338 /* 302 __FUNCTION__, match, mpri);
339 * Last Resort: if no default routers found,
340 * use addrconf default route.
341 * We don't record this route.
342 */
343 for (sprt = ip6_routing_table.leaf;
344 sprt; sprt = sprt->u.next) {
345 if (!rt6_check_expired(sprt) &&
346 (sprt->rt6i_flags & RTF_DEFAULT) &&
347 (!oif ||
348 (sprt->rt6i_dev &&
349 sprt->rt6i_dev->ifindex == oif))) {
350 match = sprt;
351 break;
352 }
353 }
354 if (!match) {
355 /* no default route. give up. */
356 match = &ip6_null_entry;
357 }
358 }
359 303
360 return match; 304 return (match ? match : &ip6_null_entry);
361} 305}
362 306
363struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr, 307struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr,
@@ -542,7 +486,7 @@ struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl)
542 int attempts = 3; 486 int attempts = 3;
543 int err; 487 int err;
544 488
545 strict = ipv6_addr_type(&fl->fl6_dst) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL); 489 strict = ipv6_addr_type(&fl->fl6_dst) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL) ? RT6_SELECT_F_IFACE : 0;
546 490
547relookup: 491relookup:
548 read_lock_bh(&rt6_lock); 492 read_lock_bh(&rt6_lock);
@@ -558,8 +502,9 @@ restart:
558 goto out; 502 goto out;
559 } 503 }
560 if (rt->rt6i_flags & RTF_DEFAULT) { 504 if (rt->rt6i_flags & RTF_DEFAULT) {
561 if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF) 505 rt = rt6_select(&fn->leaf, fl->oif, strict | RT6_SELECT_F_REACHABLE);
562 rt = rt6_best_dflt(rt, fl->oif); 506 if (rt == &ip6_null_entry)
507 rt = rt6_select(&fn->leaf, fl->oif, strict);
563 } else { 508 } else {
564 rt = rt6_device_match(rt, fl->oif, strict); 509 rt = rt6_device_match(rt, fl->oif, strict);
565 BACKTRACK(); 510 BACKTRACK();
@@ -1025,8 +970,6 @@ int ip6_del_rt(struct rt6_info *rt, struct nlmsghdr *nlh, void *_rtattr, struct
1025 970
1026 write_lock_bh(&rt6_lock); 971 write_lock_bh(&rt6_lock);
1027 972
1028 rt6_reset_dflt_pointer(NULL);
1029
1030 err = fib6_del(rt, nlh, _rtattr, req); 973 err = fib6_del(rt, nlh, _rtattr, req);
1031 dst_release(&rt->u.dst); 974 dst_release(&rt->u.dst);
1032 975
@@ -1341,8 +1284,6 @@ restart:
1341 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { 1284 if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
1342 dst_hold(&rt->u.dst); 1285 dst_hold(&rt->u.dst);
1343 1286
1344 rt6_reset_dflt_pointer(NULL);
1345
1346 read_unlock_bh(&rt6_lock); 1287 read_unlock_bh(&rt6_lock);
1347 1288
1348 ip6_del_rt(rt, NULL, NULL, NULL); 1289 ip6_del_rt(rt, NULL, NULL, NULL);