Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 384aee46 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'neighbor-More-gc_list-changes'



David Ahern says:

====================
neighbor: More gc_list changes

More gc_list changes and cleanups.

The first 2 patches are bug fixes from the first gc_list change.
Specifically, fix the locking order to be consistent - table lock
followed by neighbor lock, and then entries in the FAILED state
should always be candidates for forced_gc without waiting for any
time span (return to the eviction logic prior to the separate gc_list).

Patch 3 removes 2 now unnecessary arguments to neigh_del.

Patch 4 moves a helper from a header file to core code in preparation
for Patch 5 which removes NTF_EXT_LEARNED entries from the gc_list.
These entries are already exempt from forced_gc; patch 5 removes them
from consideration and makes them on par with PERMANENT entries given
that they are also managed by userspace.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents aeb3fecd e997f8a2
Loading
Loading
Loading
Loading
+0 −18
Original line number Diff line number Diff line
@@ -549,24 +549,6 @@ static inline void neigh_ha_snapshot(char *dst, const struct neighbour *n,
	} while (read_seqretry(&n->ha_lock, seq));
}

static inline void neigh_update_ext_learned(struct neighbour *neigh, u32 flags,
					    int *notify)
{
	u8 ndm_flags = 0;

	if (!(flags & NEIGH_UPDATE_F_ADMIN))
		return;

	ndm_flags |= (flags & NEIGH_UPDATE_F_EXT_LEARNED) ? NTF_EXT_LEARNED : 0;
	if ((neigh->flags ^ ndm_flags) & NTF_EXT_LEARNED) {
		if (ndm_flags & NTF_EXT_LEARNED)
			neigh->flags |= NTF_EXT_LEARNED;
		else
			neigh->flags &= ~NTF_EXT_LEARNED;
		*notify = 1;
	}
}

static inline void neigh_update_is_router(struct neighbour *neigh, u32 flags,
					  int *notify)
{
+60 −34
Original line number Diff line number Diff line
@@ -127,40 +127,62 @@ static void neigh_mark_dead(struct neighbour *n)
	}
}

static void neigh_change_state(struct neighbour *n, u8 new)
static void neigh_update_gc_list(struct neighbour *n)
{
	bool on_gc_list = !list_empty(&n->gc_list);
	bool new_is_perm = new & NUD_PERMANENT;
	bool on_gc_list, exempt_from_gc;

	n->nud_state = new;
	write_lock_bh(&n->tbl->lock);
	write_lock(&n->lock);

	/* remove from the gc list if new state is permanent;
	 * add to the gc list if new state is not permanent
	/* remove from the gc list if new state is permanent or if neighbor
	 * is externally learned; otherwise entry should be on the gc list
	 */
	if (new_is_perm && on_gc_list) {
		write_lock_bh(&n->tbl->lock);
		list_del_init(&n->gc_list);
		write_unlock_bh(&n->tbl->lock);
	exempt_from_gc = n->nud_state & NUD_PERMANENT ||
			 n->flags & NTF_EXT_LEARNED;
	on_gc_list = !list_empty(&n->gc_list);

	if (exempt_from_gc && on_gc_list) {
		list_del_init(&n->gc_list);
		atomic_dec(&n->tbl->gc_entries);
	} else if (!new_is_perm && !on_gc_list) {
	} else if (!exempt_from_gc && !on_gc_list) {
		/* add entries to the tail; cleaning removes from the front */
		write_lock_bh(&n->tbl->lock);
		list_add_tail(&n->gc_list, &n->tbl->gc_list);
		atomic_inc(&n->tbl->gc_entries);
	}

	write_unlock(&n->lock);
	write_unlock_bh(&n->tbl->lock);
}

		atomic_inc(&n->tbl->gc_entries);
static bool neigh_update_ext_learned(struct neighbour *neigh, u32 flags,
				     int *notify)
{
	bool rc = false;
	u8 ndm_flags;

	if (!(flags & NEIGH_UPDATE_F_ADMIN))
		return rc;

	ndm_flags = (flags & NEIGH_UPDATE_F_EXT_LEARNED) ? NTF_EXT_LEARNED : 0;
	if ((neigh->flags ^ ndm_flags) & NTF_EXT_LEARNED) {
		if (ndm_flags & NTF_EXT_LEARNED)
			neigh->flags |= NTF_EXT_LEARNED;
		else
			neigh->flags &= ~NTF_EXT_LEARNED;
		rc = true;
		*notify = 1;
	}

	return rc;
}

static bool neigh_del(struct neighbour *n, __u8 state, __u8 flags,
		      struct neighbour __rcu **np, struct neigh_table *tbl)
static bool neigh_del(struct neighbour *n, 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) &&
	    !(n->flags & flags)) {
	if (refcount_read(&n->refcnt) == 1) {
		struct neighbour *neigh;

		neigh = rcu_dereference_protected(n->next,
@@ -192,7 +214,7 @@ bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl)
	while ((n = rcu_dereference_protected(*np,
					      lockdep_is_held(&tbl->lock)))) {
		if (n == ndel)
			return neigh_del(n, 0, 0, np, tbl);
			return neigh_del(n, np, tbl);
		np = &n->next;
	}
	return false;
@@ -202,9 +224,7 @@ static int neigh_forced_gc(struct neigh_table *tbl)
{
	int max_clean = atomic_read(&tbl->gc_entries) - tbl->gc_thresh2;
	unsigned long tref = jiffies - 5 * HZ;
	u8 flags = NTF_EXT_LEARNED;
	struct neighbour *n, *tmp;
	u8 state = NUD_PERMANENT;
	int shrunk = 0;

	NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
@@ -216,7 +236,7 @@ static int neigh_forced_gc(struct neigh_table *tbl)
			bool remove = false;

			write_lock(&n->lock);
			if (!(n->nud_state & state) && !(n->flags & flags) &&
			if ((n->nud_state == NUD_FAILED) ||
			    time_after(tref, n->updated))
				remove = true;
			write_unlock(&n->lock);
@@ -355,13 +375,13 @@ EXPORT_SYMBOL(neigh_ifdown);

static struct neighbour *neigh_alloc(struct neigh_table *tbl,
				     struct net_device *dev,
				     bool permanent)
				     bool exempt_from_gc)
{
	struct neighbour *n = NULL;
	unsigned long now = jiffies;
	int entries;

	if (permanent)
	if (exempt_from_gc)
		goto do_alloc;

	entries = atomic_inc_return(&tbl->gc_entries) - 1;
@@ -403,7 +423,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl,
	return n;

out_entries:
	if (!permanent)
	if (!exempt_from_gc)
		atomic_dec(&tbl->gc_entries);
	goto out;
}
@@ -550,9 +570,9 @@ EXPORT_SYMBOL(neigh_lookup_nodev);
static struct neighbour *___neigh_create(struct neigh_table *tbl,
					 const void *pkey,
					 struct net_device *dev,
					 bool permanent, bool want_ref)
					 bool exempt_from_gc, bool want_ref)
{
	struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev, permanent);
	struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev, exempt_from_gc);
	u32 hash_val;
	unsigned int key_len = tbl->key_len;
	int error;
@@ -618,7 +638,7 @@ static struct neighbour *___neigh_create(struct neigh_table *tbl,
	}

	n->dead = 0;
	if (!permanent)
	if (!exempt_from_gc)
		list_add_tail(&n->gc_list, &n->tbl->gc_list);

	if (want_ref)
@@ -1194,6 +1214,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
			  u8 new, u32 flags, u32 nlmsg_pid,
			  struct netlink_ext_ack *extack)
{
	bool ext_learn_change = false;
	u8 old;
	int err;
	int notify = 0;
@@ -1214,13 +1235,13 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
		goto out;
	}

	neigh_update_ext_learned(neigh, flags, &notify);
	ext_learn_change = neigh_update_ext_learned(neigh, flags, &notify);

	if (!(new & NUD_VALID)) {
		neigh_del_timer(neigh);
		if (old & NUD_CONNECTED)
			neigh_suspect(neigh);
		neigh_change_state(neigh, new);
		neigh->nud_state = new;
		err = 0;
		notify = old & NUD_VALID;
		if ((old & (NUD_INCOMPLETE | NUD_PROBE)) &&
@@ -1299,7 +1320,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
						((new & NUD_REACHABLE) ?
						 neigh->parms->reachable_time :
						 0)));
		neigh_change_state(neigh, new);
		neigh->nud_state = new;
		notify = 1;
	}

@@ -1360,6 +1381,9 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
		neigh_update_is_router(neigh, flags, &notify);
	write_unlock_bh(&neigh->lock);

	if (((new ^ old) & NUD_PERMANENT) || ext_learn_change)
		neigh_update_gc_list(neigh);

	if (notify)
		neigh_update_notify(neigh, nlmsg_pid);

@@ -1862,14 +1886,16 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,

	neigh = neigh_lookup(tbl, dst, dev);
	if (neigh == NULL) {
		bool exempt_from_gc;

		if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
			err = -ENOENT;
			goto out;
		}

		neigh = ___neigh_create(tbl, dst, dev,
					ndm->ndm_state & NUD_PERMANENT,
					true);
		exempt_from_gc = ndm->ndm_state & NUD_PERMANENT ||
				 ndm->ndm_flags & NTF_EXT_LEARNED;
		neigh = ___neigh_create(tbl, dst, dev, exempt_from_gc, true);
		if (IS_ERR(neigh)) {
			err = PTR_ERR(neigh);
			goto out;