neighbor: Remove externally learned entries from gc_list
Externally learned entries are similar to PERMANENT entries in the sense they are managed by userspace and can not be garbage collected. As such remove them from the gc_list, remove the flags check from neigh_forced_gc and skip threshold checks in neigh_alloc. As with PERMANENT entries, this allows unlimited number of NTF_EXT_LEARNED entries. Signed-off-by: David Ahern <dsahern@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
526f1b587c
commit
e997f8a20a
|
@ -129,21 +129,22 @@ static void neigh_mark_dead(struct neighbour *n)
|
||||||
|
|
||||||
static void neigh_update_gc_list(struct neighbour *n)
|
static void neigh_update_gc_list(struct neighbour *n)
|
||||||
{
|
{
|
||||||
bool on_gc_list, new_is_perm;
|
bool on_gc_list, exempt_from_gc;
|
||||||
|
|
||||||
write_lock_bh(&n->tbl->lock);
|
write_lock_bh(&n->tbl->lock);
|
||||||
write_lock(&n->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
|
||||||
*/
|
*/
|
||||||
new_is_perm = n->nud_state & NUD_PERMANENT;
|
exempt_from_gc = n->nud_state & NUD_PERMANENT ||
|
||||||
|
n->flags & NTF_EXT_LEARNED;
|
||||||
on_gc_list = !list_empty(&n->gc_list);
|
on_gc_list = !list_empty(&n->gc_list);
|
||||||
|
|
||||||
if (new_is_perm && on_gc_list) {
|
if (exempt_from_gc && on_gc_list) {
|
||||||
list_del_init(&n->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 */
|
||||||
list_add_tail(&n->gc_list, &n->tbl->gc_list);
|
list_add_tail(&n->gc_list, &n->tbl->gc_list);
|
||||||
atomic_inc(&n->tbl->gc_entries);
|
atomic_inc(&n->tbl->gc_entries);
|
||||||
|
@ -153,13 +154,14 @@ static void neigh_update_gc_list(struct neighbour *n)
|
||||||
write_unlock_bh(&n->tbl->lock);
|
write_unlock_bh(&n->tbl->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void neigh_update_ext_learned(struct neighbour *neigh, u32 flags,
|
static bool neigh_update_ext_learned(struct neighbour *neigh, u32 flags,
|
||||||
int *notify)
|
int *notify)
|
||||||
{
|
{
|
||||||
|
bool rc = false;
|
||||||
u8 ndm_flags;
|
u8 ndm_flags;
|
||||||
|
|
||||||
if (!(flags & NEIGH_UPDATE_F_ADMIN))
|
if (!(flags & NEIGH_UPDATE_F_ADMIN))
|
||||||
return;
|
return rc;
|
||||||
|
|
||||||
ndm_flags = (flags & NEIGH_UPDATE_F_EXT_LEARNED) ? NTF_EXT_LEARNED : 0;
|
ndm_flags = (flags & NEIGH_UPDATE_F_EXT_LEARNED) ? NTF_EXT_LEARNED : 0;
|
||||||
if ((neigh->flags ^ ndm_flags) & NTF_EXT_LEARNED) {
|
if ((neigh->flags ^ ndm_flags) & NTF_EXT_LEARNED) {
|
||||||
|
@ -167,8 +169,11 @@ static void neigh_update_ext_learned(struct neighbour *neigh, u32 flags,
|
||||||
neigh->flags |= NTF_EXT_LEARNED;
|
neigh->flags |= NTF_EXT_LEARNED;
|
||||||
else
|
else
|
||||||
neigh->flags &= ~NTF_EXT_LEARNED;
|
neigh->flags &= ~NTF_EXT_LEARNED;
|
||||||
|
rc = true;
|
||||||
*notify = 1;
|
*notify = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool neigh_del(struct neighbour *n, struct neighbour __rcu **np,
|
static bool neigh_del(struct neighbour *n, struct neighbour __rcu **np,
|
||||||
|
@ -219,7 +224,6 @@ 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;
|
||||||
int shrunk = 0;
|
int shrunk = 0;
|
||||||
|
|
||||||
|
@ -233,7 +237,7 @@ static int neigh_forced_gc(struct neigh_table *tbl)
|
||||||
|
|
||||||
write_lock(&n->lock);
|
write_lock(&n->lock);
|
||||||
if ((n->nud_state == NUD_FAILED) ||
|
if ((n->nud_state == NUD_FAILED) ||
|
||||||
(!(n->flags & flags) && time_after(tref, n->updated)))
|
time_after(tref, n->updated))
|
||||||
remove = true;
|
remove = true;
|
||||||
write_unlock(&n->lock);
|
write_unlock(&n->lock);
|
||||||
|
|
||||||
|
@ -371,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;
|
||||||
|
@ -419,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;
|
||||||
}
|
}
|
||||||
|
@ -566,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;
|
||||||
|
@ -634,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)
|
||||||
|
@ -1210,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;
|
||||||
|
@ -1230,7 +1235,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
neigh_update_ext_learned(neigh, flags, ¬ify);
|
ext_learn_change = neigh_update_ext_learned(neigh, flags, ¬ify);
|
||||||
|
|
||||||
if (!(new & NUD_VALID)) {
|
if (!(new & NUD_VALID)) {
|
||||||
neigh_del_timer(neigh);
|
neigh_del_timer(neigh);
|
||||||
|
@ -1376,7 +1381,7 @@ out:
|
||||||
neigh_update_is_router(neigh, flags, ¬ify);
|
neigh_update_is_router(neigh, flags, ¬ify);
|
||||||
write_unlock_bh(&neigh->lock);
|
write_unlock_bh(&neigh->lock);
|
||||||
|
|
||||||
if ((new ^ old) & NUD_PERMANENT)
|
if (((new ^ old) & NUD_PERMANENT) || ext_learn_change)
|
||||||
neigh_update_gc_list(neigh);
|
neigh_update_gc_list(neigh);
|
||||||
|
|
||||||
if (notify)
|
if (notify)
|
||||||
|
@ -1881,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;
|
||||||
|
|
Loading…
Reference in New Issue