Commit 71d67e66 authored by Stephen Hemminger's avatar Stephen Hemminger Committed by David S. Miller
Browse files

[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: default avatarStephen Hemminger <shemminger@vyatta.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9fe7c712
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+32 −17
Original line number Diff line number Diff line
@@ -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 {
		l = fib_find_node(t, key);
		if (!l) {
			/* The table changed during the dump, rather than
			 * giving partial data, just make application retry.
		/* Normally, continue from last key, but if that is missing
		 * fallback to using slow rescan
		 */
			rcu_read_unlock();
			return -EBUSY;
		}
		l = fib_find_node(t, key);
		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;