X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fcore%2Fdst.c;h=470c05bc4cb2bd513f1ba37002a81f46266c8905;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=571ef1f2c4678f122f82dceffc2914f07a4080f9;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/net/core/dst.c b/net/core/dst.c index 571ef1f2c..470c05bc4 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -32,19 +32,19 @@ static struct dst_entry *dst_garbage_list; #if RT_CACHE_DEBUG >= 2 static atomic_t dst_total = ATOMIC_INIT(0); #endif -static spinlock_t dst_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(dst_lock); static unsigned long dst_gc_timer_expires; static unsigned long dst_gc_timer_inc = DST_GC_MAX; static void dst_run_gc(unsigned long); static void ___dst_free(struct dst_entry * dst); -static struct timer_list dst_gc_timer = - TIMER_INITIALIZER(dst_run_gc, DST_GC_MIN, 0); +static DEFINE_TIMER(dst_gc_timer, dst_run_gc, DST_GC_MIN, 0); static void dst_run_gc(unsigned long dummy) { int delayed = 0; + int work_performed; struct dst_entry * dst, **dstp; if (!spin_trylock(&dst_lock)) { @@ -52,9 +52,9 @@ static void dst_run_gc(unsigned long dummy) return; } - del_timer(&dst_gc_timer); dstp = &dst_garbage_list; + work_performed = 0; while ((dst = *dstp) != NULL) { if (atomic_read(&dst->__refcnt)) { dstp = &dst->next; @@ -62,6 +62,7 @@ static void dst_run_gc(unsigned long dummy) continue; } *dstp = dst->next; + work_performed = 1; dst = dst_destroy(dst); if (dst) { @@ -86,9 +87,14 @@ static void dst_run_gc(unsigned long dummy) dst_gc_timer_inc = DST_GC_MAX; goto out; } - if ((dst_gc_timer_expires += dst_gc_timer_inc) > DST_GC_MAX) - dst_gc_timer_expires = DST_GC_MAX; - dst_gc_timer_inc += DST_GC_INC; + if (!work_performed) { + if ((dst_gc_timer_expires += dst_gc_timer_inc) > DST_GC_MAX) + dst_gc_timer_expires = DST_GC_MAX; + dst_gc_timer_inc += DST_GC_INC; + } else { + dst_gc_timer_inc = DST_GC_INC; + dst_gc_timer_expires = DST_GC_MIN; + } dst_gc_timer.expires = jiffies + dst_gc_timer_expires; #if RT_CACHE_DEBUG >= 2 printk("dst_total: %d/%d %ld\n", @@ -106,9 +112,9 @@ static int dst_discard_in(struct sk_buff *skb) return 0; } -static int dst_discard_out(struct sk_buff **pskb) +static int dst_discard_out(struct sk_buff *skb) { - kfree_skb(*pskb); + kfree_skb(skb); return 0; } @@ -169,6 +175,8 @@ struct dst_entry *dst_destroy(struct dst_entry * dst) struct neighbour *neigh; struct hh_cache *hh; + smp_rmb(); + again: neigh = dst->neighbour; hh = dst->hh; @@ -196,13 +204,15 @@ again: dst = child; if (dst) { + int nohash = dst->flags & DST_NOHASH; + if (atomic_dec_and_test(&dst->__refcnt)) { /* We were real parent of this dst, so kill child. */ - if (dst->flags&DST_NOHASH) + if (nohash) goto again; } else { /* Child is still referenced, return it for freeing. */ - if (dst->flags&DST_NOHASH) + if (nohash) return dst; /* Child is still in his hash table */ } @@ -210,6 +220,38 @@ again: return NULL; } +/* Dirty hack. We did it in 2.2 (in __dst_free), + * we have _very_ good reasons not to repeat + * this mistake in 2.3, but we have no choice + * now. _It_ _is_ _explicit_ _deliberate_ + * _race_ _condition_. + * + * Commented and originally written by Alexey. + */ +static inline void dst_ifdown(struct dst_entry *dst, struct net_device *dev, + int unregister) +{ + if (dst->ops->ifdown) + dst->ops->ifdown(dst, dev, unregister); + + if (dev != dst->dev) + return; + + if (!unregister) { + dst->input = dst_discard_in; + dst->output = dst_discard_out; + } else { + dst->dev = &loopback_dev; + dev_hold(&loopback_dev); + dev_put(dev); + if (dst->neighbour && dst->neighbour->dev == dev) { + dst->neighbour->dev = &loopback_dev; + dev_put(dev); + dev_hold(&loopback_dev); + } + } +} + static int dst_dev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *dev = ptr; @@ -220,31 +262,7 @@ static int dst_dev_event(struct notifier_block *this, unsigned long event, void case NETDEV_DOWN: spin_lock_bh(&dst_lock); for (dst = dst_garbage_list; dst; dst = dst->next) { - if (dst->dev == dev) { - /* Dirty hack. We did it in 2.2 (in __dst_free), - we have _very_ good reasons not to repeat - this mistake in 2.3, but we have no choice - now. _It_ _is_ _explicit_ _deliberate_ - _race_ _condition_. - */ - if (event!=NETDEV_DOWN && - dst->output == dst_discard_out) { - dst->dev = &loopback_dev; - dev_hold(&loopback_dev); - dev_put(dev); - dst->output = dst_discard_out; - if (dst->neighbour && dst->neighbour->dev == dev) { - dst->neighbour->dev = &loopback_dev; - dev_put(dev); - dev_hold(&loopback_dev); - } - } else { - dst->input = dst_discard_in; - dst->output = dst_discard_out; - } - if (dst->ops->ifdown) - dst->ops->ifdown(dst, event != NETDEV_DOWN); - } + dst_ifdown(dst, dev, event != NETDEV_DOWN); } spin_unlock_bh(&dst_lock); break; @@ -252,7 +270,7 @@ static int dst_dev_event(struct notifier_block *this, unsigned long event, void return NOTIFY_DONE; } -struct notifier_block dst_dev_notifier = { +static struct notifier_block dst_dev_notifier = { .notifier_call = dst_dev_event, };