diff options
Diffstat (limited to 'net/core/neighbour.c')
| -rw-r--r-- | net/core/neighbour.c | 94 | 
1 files changed, 69 insertions, 25 deletions
| diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 58b0bcc125b5..e31fc11a8000 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -118,6 +118,50 @@ unsigned long neigh_rand_reach_time(unsigned long base)  EXPORT_SYMBOL(neigh_rand_reach_time); +static bool neigh_del(struct neighbour *n, __u8 state, +		      struct neighbour __rcu **np, struct neigh_table *tbl) +{ +	bool retval = false; + +	write_lock(&n->lock); +	if (refcount_read(&n->refcnt) == 1 && !(n->nud_state & state)) { +		struct neighbour *neigh; + +		neigh = rcu_dereference_protected(n->next, +						  lockdep_is_held(&tbl->lock)); +		rcu_assign_pointer(*np, neigh); +		n->dead = 1; +		retval = true; +	} +	write_unlock(&n->lock); +	if (retval) +		neigh_cleanup_and_release(n); +	return retval; +} + +bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl) +{ +	struct neigh_hash_table *nht; +	void *pkey = ndel->primary_key; +	u32 hash_val; +	struct neighbour *n; +	struct neighbour __rcu **np; + +	nht = rcu_dereference_protected(tbl->nht, +					lockdep_is_held(&tbl->lock)); +	hash_val = tbl->hash(pkey, ndel->dev, nht->hash_rnd); +	hash_val = hash_val >> (32 - nht->hash_shift); + +	np = &nht->hash_buckets[hash_val]; +	while ((n = rcu_dereference_protected(*np, +					      lockdep_is_held(&tbl->lock)))) { +		if (n == ndel) +			return neigh_del(n, 0, np, tbl); +		np = &n->next; +	} +	return false; +} +  static int neigh_forced_gc(struct neigh_table *tbl)  {  	int shrunk = 0; @@ -140,19 +184,10 @@ static int neigh_forced_gc(struct neigh_table *tbl)  			 * - nobody refers to it.  			 * - it is not permanent  			 */ -			write_lock(&n->lock); -			if (atomic_read(&n->refcnt) == 1 && -			    !(n->nud_state & NUD_PERMANENT)) { -				rcu_assign_pointer(*np, -					rcu_dereference_protected(n->next, -						  lockdep_is_held(&tbl->lock))); -				n->dead = 1; -				shrunk	= 1; -				write_unlock(&n->lock); -				neigh_cleanup_and_release(n); +			if (neigh_del(n, NUD_PERMANENT, np, tbl)) { +				shrunk = 1;  				continue;  			} -			write_unlock(&n->lock);  			np = &n->next;  		}  	} @@ -219,7 +254,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)  			neigh_del_timer(n);  			n->dead = 1; -			if (atomic_read(&n->refcnt) != 1) { +			if (refcount_read(&n->refcnt) != 1) {  				/* The most unpleasant situation.  				   We must destroy neighbour entry,  				   but someone still uses it. @@ -300,7 +335,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device  	NEIGH_CACHE_STAT_INC(tbl, allocs);  	n->tbl		  = tbl; -	atomic_set(&n->refcnt, 1); +	refcount_set(&n->refcnt, 1);  	n->dead		  = 1;  out:  	return n; @@ -409,7 +444,7 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,  	rcu_read_lock_bh();  	n = __neigh_lookup_noref(tbl, pkey, dev);  	if (n) { -		if (!atomic_inc_not_zero(&n->refcnt)) +		if (!refcount_inc_not_zero(&n->refcnt))  			n = NULL;  		NEIGH_CACHE_STAT_INC(tbl, hits);  	} @@ -438,7 +473,7 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,  	     n = rcu_dereference_bh(n->next)) {  		if (!memcmp(n->primary_key, pkey, key_len) &&  		    net_eq(dev_net(n->dev), net)) { -			if (!atomic_inc_not_zero(&n->refcnt)) +			if (!refcount_inc_not_zero(&n->refcnt))  				n = NULL;  			NEIGH_CACHE_STAT_INC(tbl, hits);  			break; @@ -674,7 +709,7 @@ static void neigh_parms_destroy(struct neigh_parms *parms);  static inline void neigh_parms_put(struct neigh_parms *parms)  { -	if (atomic_dec_and_test(&parms->refcnt)) +	if (refcount_dec_and_test(&parms->refcnt))  		neigh_parms_destroy(parms);  } @@ -786,7 +821,7 @@ static void neigh_periodic_work(struct work_struct *work)  			if (time_before(n->used, n->confirmed))  				n->used = n->confirmed; -			if (atomic_read(&n->refcnt) == 1 && +			if (refcount_read(&n->refcnt) == 1 &&  			    (state == NUD_FAILED ||  			     time_after(jiffies, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) {  				*np = n->next; @@ -1132,10 +1167,6 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,  		lladdr = neigh->ha;  	} -	if (new & NUD_CONNECTED) -		neigh->confirmed = jiffies; -	neigh->updated = jiffies; -  	/* If entry was valid and address is not changed,  	   do not change entry state, if new one is STALE.  	 */ @@ -1157,6 +1188,16 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,  		}  	} +	/* Update timestamps only once we know we will make a change to the +	 * neighbour entry. Otherwise we risk to move the locktime window with +	 * noop updates and ignore relevant ARP updates. +	 */ +	if (new != old || lladdr != neigh->ha) { +		if (new & NUD_CONNECTED) +			neigh->confirmed = jiffies; +		neigh->updated = jiffies; +	} +  	if (new != old) {  		neigh_del_timer(neigh);  		if (new & NUD_PROBE) @@ -1438,7 +1479,7 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,  	p = kmemdup(&tbl->parms, sizeof(*p), GFP_KERNEL);  	if (p) {  		p->tbl		  = tbl; -		atomic_set(&p->refcnt, 1); +		refcount_set(&p->refcnt, 1);  		p->reachable_time =  				neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));  		dev_hold(dev); @@ -1501,7 +1542,7 @@ void neigh_table_init(int index, struct neigh_table *tbl)  	INIT_LIST_HEAD(&tbl->parms_list);  	list_add(&tbl->parms.list, &tbl->parms_list);  	write_pnet(&tbl->parms.net, &init_net); -	atomic_set(&tbl->parms.refcnt, 1); +	refcount_set(&tbl->parms.refcnt, 1);  	tbl->parms.reachable_time =  			  neigh_rand_reach_time(NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME)); @@ -1643,7 +1684,10 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,  			   NEIGH_UPDATE_F_OVERRIDE |  			   NEIGH_UPDATE_F_ADMIN,  			   NETLINK_CB(skb).portid); +	write_lock_bh(&tbl->lock);  	neigh_release(neigh); +	neigh_remove_one(neigh, tbl); +	write_unlock_bh(&tbl->lock);  out:  	return err; @@ -1752,7 +1796,7 @@ static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms)  	if ((parms->dev &&  	     nla_put_u32(skb, NDTPA_IFINDEX, parms->dev->ifindex)) || -	    nla_put_u32(skb, NDTPA_REFCNT, atomic_read(&parms->refcnt)) || +	    nla_put_u32(skb, NDTPA_REFCNT, refcount_read(&parms->refcnt)) ||  	    nla_put_u32(skb, NDTPA_QUEUE_LENBYTES,  			NEIGH_VAR(parms, QUEUE_LEN_BYTES)) ||  	    /* approximative value for deprecated QUEUE_LEN (in packets) */ @@ -2190,7 +2234,7 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh,  	ci.ndm_used	 = jiffies_to_clock_t(now - neigh->used);  	ci.ndm_confirmed = jiffies_to_clock_t(now - neigh->confirmed);  	ci.ndm_updated	 = jiffies_to_clock_t(now - neigh->updated); -	ci.ndm_refcnt	 = atomic_read(&neigh->refcnt) - 1; +	ci.ndm_refcnt	 = refcount_read(&neigh->refcnt) - 1;  	read_unlock_bh(&neigh->lock);  	if (nla_put_u32(skb, NDA_PROBES, atomic_read(&neigh->probes)) || |