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
...@@ -549,24 +549,6 @@ static inline void neigh_ha_snapshot(char *dst, const struct neighbour *n, ...@@ -549,24 +549,6 @@ static inline void neigh_ha_snapshot(char *dst, const struct neighbour *n,
} while (read_seqretry(&n->ha_lock, seq)); } 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, static inline void neigh_update_is_router(struct neighbour *neigh, u32 flags,
int *notify) int *notify)
{ {
......
...@@ -127,40 +127,62 @@ static void neigh_mark_dead(struct neighbour *n) ...@@ -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 on_gc_list, exempt_from_gc;
bool new_is_perm = new & NUD_PERMANENT;
n->nud_state = new; write_lock_bh(&n->tbl->lock);
write_lock(&n->lock);
/* remove from the gc list if new state is permanent; /* remove from the gc list if new state is permanent or if neighbor
* add to the gc list if new state is not permanent * is externally learned; otherwise entry should be on the gc list
*/ */
if (new_is_perm && on_gc_list) { exempt_from_gc = n->nud_state & NUD_PERMANENT ||
write_lock_bh(&n->tbl->lock); n->flags & NTF_EXT_LEARNED;
list_del_init(&n->gc_list); on_gc_list = !list_empty(&n->gc_list);
write_unlock_bh(&n->tbl->lock);
if (exempt_from_gc && on_gc_list) {
list_del_init(&n->gc_list);
atomic_dec(&n->tbl->gc_entries); 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 */ /* 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); list_add_tail(&n->gc_list, &n->tbl->gc_list);
write_unlock_bh(&n->tbl->lock);
atomic_inc(&n->tbl->gc_entries); atomic_inc(&n->tbl->gc_entries);
} }
write_unlock(&n->lock);
write_unlock_bh(&n->tbl->lock);
} }
static bool neigh_del(struct neighbour *n, __u8 state, __u8 flags, static bool neigh_update_ext_learned(struct neighbour *neigh, u32 flags,
struct neighbour __rcu **np, struct neigh_table *tbl) 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, struct neighbour __rcu **np,
struct neigh_table *tbl)
{ {
bool retval = false; bool retval = false;
write_lock(&n->lock); write_lock(&n->lock);
if (refcount_read(&n->refcnt) == 1 && !(n->nud_state & state) && if (refcount_read(&n->refcnt) == 1) {
!(n->flags & flags)) {
struct neighbour *neigh; struct neighbour *neigh;
neigh = rcu_dereference_protected(n->next, neigh = rcu_dereference_protected(n->next,
...@@ -192,7 +214,7 @@ bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl) ...@@ -192,7 +214,7 @@ bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl)
while ((n = rcu_dereference_protected(*np, while ((n = rcu_dereference_protected(*np,
lockdep_is_held(&tbl->lock)))) { lockdep_is_held(&tbl->lock)))) {
if (n == ndel) if (n == ndel)
return neigh_del(n, 0, 0, np, tbl); return neigh_del(n, np, tbl);
np = &n->next; np = &n->next;
} }
return false; return false;
...@@ -202,9 +224,7 @@ static int neigh_forced_gc(struct neigh_table *tbl) ...@@ -202,9 +224,7 @@ static int neigh_forced_gc(struct neigh_table *tbl)
{ {
int max_clean = atomic_read(&tbl->gc_entries) - tbl->gc_thresh2; int max_clean = atomic_read(&tbl->gc_entries) - tbl->gc_thresh2;
unsigned long tref = jiffies - 5 * HZ; unsigned long tref = jiffies - 5 * HZ;
u8 flags = NTF_EXT_LEARNED;
struct neighbour *n, *tmp; struct neighbour *n, *tmp;
u8 state = NUD_PERMANENT;
int shrunk = 0; int shrunk = 0;
NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs); NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
...@@ -216,7 +236,7 @@ static int neigh_forced_gc(struct neigh_table *tbl) ...@@ -216,7 +236,7 @@ static int neigh_forced_gc(struct neigh_table *tbl)
bool remove = false; bool remove = false;
write_lock(&n->lock); write_lock(&n->lock);
if (!(n->nud_state & state) && !(n->flags & flags) && if ((n->nud_state == NUD_FAILED) ||
time_after(tref, n->updated)) time_after(tref, n->updated))
remove = true; remove = true;
write_unlock(&n->lock); write_unlock(&n->lock);
...@@ -355,13 +375,13 @@ EXPORT_SYMBOL(neigh_ifdown); ...@@ -355,13 +375,13 @@ EXPORT_SYMBOL(neigh_ifdown);
static struct neighbour *neigh_alloc(struct neigh_table *tbl, static struct neighbour *neigh_alloc(struct neigh_table *tbl,
struct net_device *dev, struct net_device *dev,
bool permanent) bool exempt_from_gc)
{ {
struct neighbour *n = NULL; struct neighbour *n = NULL;
unsigned long now = jiffies; unsigned long now = jiffies;
int entries; int entries;
if (permanent) if (exempt_from_gc)
goto do_alloc; goto do_alloc;
entries = atomic_inc_return(&tbl->gc_entries) - 1; entries = atomic_inc_return(&tbl->gc_entries) - 1;
...@@ -403,7 +423,7 @@ out: ...@@ -403,7 +423,7 @@ out:
return n; return n;
out_entries: out_entries:
if (!permanent) if (!exempt_from_gc)
atomic_dec(&tbl->gc_entries); atomic_dec(&tbl->gc_entries);
goto out; goto out;
} }
...@@ -550,9 +570,9 @@ EXPORT_SYMBOL(neigh_lookup_nodev); ...@@ -550,9 +570,9 @@ EXPORT_SYMBOL(neigh_lookup_nodev);
static struct neighbour *___neigh_create(struct neigh_table *tbl, static struct neighbour *___neigh_create(struct neigh_table *tbl,
const void *pkey, const void *pkey,
struct net_device *dev, 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; u32 hash_val;
unsigned int key_len = tbl->key_len; unsigned int key_len = tbl->key_len;
int error; int error;
...@@ -618,7 +638,7 @@ static struct neighbour *___neigh_create(struct neigh_table *tbl, ...@@ -618,7 +638,7 @@ static struct neighbour *___neigh_create(struct neigh_table *tbl,
} }
n->dead = 0; n->dead = 0;
if (!permanent) if (!exempt_from_gc)
list_add_tail(&n->gc_list, &n->tbl->gc_list); list_add_tail(&n->gc_list, &n->tbl->gc_list);
if (want_ref) if (want_ref)
...@@ -1194,6 +1214,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, ...@@ -1194,6 +1214,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
u8 new, u32 flags, u32 nlmsg_pid, u8 new, u32 flags, u32 nlmsg_pid,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
bool ext_learn_change = false;
u8 old; u8 old;
int err; int err;
int notify = 0; int notify = 0;
...@@ -1214,13 +1235,13 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, ...@@ -1214,13 +1235,13 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
goto out; goto out;
} }
neigh_update_ext_learned(neigh, flags, &notify); ext_learn_change = neigh_update_ext_learned(neigh, flags, &notify);
if (!(new & NUD_VALID)) { if (!(new & NUD_VALID)) {
neigh_del_timer(neigh); neigh_del_timer(neigh);
if (old & NUD_CONNECTED) if (old & NUD_CONNECTED)
neigh_suspect(neigh); neigh_suspect(neigh);
neigh_change_state(neigh, new); neigh->nud_state = new;
err = 0; err = 0;
notify = old & NUD_VALID; notify = old & NUD_VALID;
if ((old & (NUD_INCOMPLETE | NUD_PROBE)) && if ((old & (NUD_INCOMPLETE | NUD_PROBE)) &&
...@@ -1299,7 +1320,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, ...@@ -1299,7 +1320,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
((new & NUD_REACHABLE) ? ((new & NUD_REACHABLE) ?
neigh->parms->reachable_time : neigh->parms->reachable_time :
0))); 0)));
neigh_change_state(neigh, new); neigh->nud_state = new;
notify = 1; notify = 1;
} }
...@@ -1360,6 +1381,9 @@ out: ...@@ -1360,6 +1381,9 @@ out:
neigh_update_is_router(neigh, flags, &notify); neigh_update_is_router(neigh, flags, &notify);
write_unlock_bh(&neigh->lock); write_unlock_bh(&neigh->lock);
if (((new ^ old) & NUD_PERMANENT) || ext_learn_change)
neigh_update_gc_list(neigh);
if (notify) if (notify)
neigh_update_notify(neigh, nlmsg_pid); neigh_update_notify(neigh, nlmsg_pid);
...@@ -1862,14 +1886,16 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -1862,14 +1886,16 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
neigh = neigh_lookup(tbl, dst, dev); neigh = neigh_lookup(tbl, dst, dev);
if (neigh == NULL) { if (neigh == NULL) {
bool exempt_from_gc;
if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
err = -ENOENT; err = -ENOENT;
goto out; goto out;
} }
neigh = ___neigh_create(tbl, dst, dev, exempt_from_gc = ndm->ndm_state & NUD_PERMANENT ||
ndm->ndm_state & NUD_PERMANENT, ndm->ndm_flags & NTF_EXT_LEARNED;
true); neigh = ___neigh_create(tbl, dst, dev, exempt_from_gc, true);
if (IS_ERR(neigh)) { if (IS_ERR(neigh)) {
err = PTR_ERR(neigh); err = PTR_ERR(neigh);
goto out; goto out;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment