[IPV4] fib_trie: rescan if key is lost during dump

Normally during a dump the key of the last dumped entry is used for
continuation, but since lock is dropped it might be lost. In that case
fallback to the old counter based N^2 behaviour.  This means the dump
will end up skipping some routes which matches what FIB_HASH does.

Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Stephen Hemminger 2008-01-31 16:45:47 -08:00 коммит произвёл David S. Miller
Родитель 9fe7c712fc
Коммит 71d67e666e
2 изменённых файлов: 33 добавлений и 18 удалений

Просмотреть файл

@ -219,7 +219,7 @@ struct netlink_callback
int (*dump)(struct sk_buff * skb, struct netlink_callback *cb);
int (*done)(struct netlink_callback *cb);
int family;
long args[5];
long args[6];
};
struct netlink_notify

Просмотреть файл

@ -1758,6 +1758,19 @@ static struct leaf *trie_nextleaf(struct leaf *l)
return leaf_walk_rcu(p, c);
}
static struct leaf *trie_leafindex(struct trie *t, int index)
{
struct leaf *l = trie_firstleaf(t);
while (index-- > 0) {
l = trie_nextleaf(l);
if (!l)
break;
}
return l;
}
/*
* Caller must hold RTNL.
*/
@ -1863,7 +1876,7 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah,
struct fib_alias *fa;
__be32 xkey = htonl(key);
s_i = cb->args[4];
s_i = cb->args[5];
i = 0;
/* rcu_read_lock is hold by caller */
@ -1884,12 +1897,12 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah,
plen,
fa->fa_tos,
fa->fa_info, NLM_F_MULTI) < 0) {
cb->args[4] = i;
cb->args[5] = i;
return -1;
}
i++;
}
cb->args[4] = i;
cb->args[5] = i;
return skb->len;
}
@ -1900,7 +1913,7 @@ static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb,
struct hlist_node *node;
int i, s_i;
s_i = cb->args[3];
s_i = cb->args[4];
i = 0;
/* rcu_read_lock is hold by caller */
@ -1911,19 +1924,19 @@ static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb,
}
if (i > s_i)
cb->args[4] = 0;
cb->args[5] = 0;
if (list_empty(&li->falh))
continue;
if (fn_trie_dump_fa(l->key, li->plen, &li->falh, tb, skb, cb) < 0) {
cb->args[3] = i;
cb->args[4] = i;
return -1;
}
i++;
}
cb->args[3] = i;
cb->args[4] = i;
return skb->len;
}
@ -1933,35 +1946,37 @@ static int fn_trie_dump(struct fib_table *tb, struct sk_buff *skb,
struct leaf *l;
struct trie *t = (struct trie *) tb->tb_data;
t_key key = cb->args[2];
int count = cb->args[3];
rcu_read_lock();
/* Dump starting at last key.
* Note: 0.0.0.0/0 (ie default) is first key.
*/
if (!key)
if (count == 0)
l = trie_firstleaf(t);
else {
/* Normally, continue from last key, but if that is missing
* fallback to using slow rescan
*/
l = fib_find_node(t, key);
if (!l) {
/* The table changed during the dump, rather than
* giving partial data, just make application retry.
*/
rcu_read_unlock();
return -EBUSY;
}
if (!l)
l = trie_leafindex(t, count);
}
while (l) {
cb->args[2] = l->key;
if (fn_trie_dump_leaf(l, tb, skb, cb) < 0) {
cb->args[3] = count;
rcu_read_unlock();
return -1;
}
++count;
l = trie_nextleaf(l);
memset(&cb->args[3], 0,
sizeof(cb->args) - 3*sizeof(cb->args[0]));
memset(&cb->args[4], 0,
sizeof(cb->args) - 4*sizeof(cb->args[0]));
}
cb->args[3] = count;
rcu_read_unlock();
return skb->len;