diff options
Diffstat (limited to 'net/core/dst.c')
| -rw-r--r-- | net/core/dst.c | 40 | 
1 files changed, 34 insertions, 6 deletions
diff --git a/net/core/dst.c b/net/core/dst.c index 6c41b1fac3d..b99c7c7ffce 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -168,7 +168,7 @@ void *dst_alloc(struct dst_ops *ops)  {  	struct dst_entry *dst; -	if (ops->gc && atomic_read(&ops->entries) > ops->gc_thresh) { +	if (ops->gc && dst_entries_get_fast(ops) > ops->gc_thresh) {  		if (ops->gc(ops))  			return NULL;  	} @@ -183,7 +183,7 @@ void *dst_alloc(struct dst_ops *ops)  #if RT_CACHE_DEBUG >= 2  	atomic_inc(&dst_total);  #endif -	atomic_inc(&ops->entries); +	dst_entries_add(ops, 1);  	return dst;  }  EXPORT_SYMBOL(dst_alloc); @@ -228,15 +228,15 @@ again:  	child = dst->child;  	dst->hh = NULL; -	if (hh && atomic_dec_and_test(&hh->hh_refcnt)) -		kfree(hh); +	if (hh) +		hh_cache_put(hh);  	if (neigh) {  		dst->neighbour = NULL;  		neigh_release(neigh);  	} -	atomic_dec(&dst->ops->entries); +	dst_entries_add(dst->ops, -1);  	if (dst->ops->destroy)  		dst->ops->destroy(dst); @@ -271,13 +271,40 @@ void dst_release(struct dst_entry *dst)  	if (dst) {  		int newrefcnt; -		smp_mb__before_atomic_dec();  		newrefcnt = atomic_dec_return(&dst->__refcnt);  		WARN_ON(newrefcnt < 0); +		if (unlikely(dst->flags & DST_NOCACHE) && !newrefcnt) { +			dst = dst_destroy(dst); +			if (dst) +				__dst_free(dst); +		}  	}  }  EXPORT_SYMBOL(dst_release); +/** + * skb_dst_set_noref - sets skb dst, without a reference + * @skb: buffer + * @dst: dst entry + * + * Sets skb dst, assuming a reference was not taken on dst + * skb_dst_drop() should not dst_release() this dst + */ +void skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst) +{ +	WARN_ON(!rcu_read_lock_held() && !rcu_read_lock_bh_held()); +	/* If dst not in cache, we must take a reference, because +	 * dst_release() will destroy dst as soon as its refcount becomes zero +	 */ +	if (unlikely(dst->flags & DST_NOCACHE)) { +		dst_hold(dst); +		skb_dst_set(skb, dst); +	} else { +		skb->_skb_refdst = (unsigned long)dst | SKB_DST_NOREF; +	} +} +EXPORT_SYMBOL(skb_dst_set_noref); +  /* 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 @@ -343,6 +370,7 @@ static int dst_dev_event(struct notifier_block *this, unsigned long event,  static struct notifier_block dst_dev_notifier = {  	.notifier_call	= dst_dev_event, +	.priority = -10, /* must be called after other network notifiers */  };  void __init dst_init(void)  | 
