diff options
author | Stephen Hemminger <shemminger@vyatta.com> | 2008-01-31 19:45:47 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-31 22:28:23 -0500 |
commit | 71d67e666e73e3b7e9ef124745ee2e454ac04be8 (patch) | |
tree | e163b578c98f3c3ac69cb88ca6bf936d6ab4d698 | |
parent | 9fe7c712fc955565c32e2f899d4ffeceaf028398 (diff) |
[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>
-rw-r--r-- | include/linux/netlink.h | 2 | ||||
-rw-r--r-- | net/ipv4/fib_trie.c | 49 |
2 files changed, 33 insertions, 18 deletions
diff --git a/include/linux/netlink.h b/include/linux/netlink.h index bd13b6f4a98e..fb0713b6ffaf 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h | |||
@@ -219,7 +219,7 @@ struct netlink_callback | |||
219 | int (*dump)(struct sk_buff * skb, struct netlink_callback *cb); | 219 | int (*dump)(struct sk_buff * skb, struct netlink_callback *cb); |
220 | int (*done)(struct netlink_callback *cb); | 220 | int (*done)(struct netlink_callback *cb); |
221 | int family; | 221 | int family; |
222 | long args[5]; | 222 | long args[6]; |
223 | }; | 223 | }; |
224 | 224 | ||
225 | struct netlink_notify | 225 | struct netlink_notify |
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index cbccafde8238..35851c96bdfb 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c | |||
@@ -1758,6 +1758,19 @@ static struct leaf *trie_nextleaf(struct leaf *l) | |||
1758 | return leaf_walk_rcu(p, c); | 1758 | return leaf_walk_rcu(p, c); |
1759 | } | 1759 | } |
1760 | 1760 | ||
1761 | static struct leaf *trie_leafindex(struct trie *t, int index) | ||
1762 | { | ||
1763 | struct leaf *l = trie_firstleaf(t); | ||
1764 | |||
1765 | while (index-- > 0) { | ||
1766 | l = trie_nextleaf(l); | ||
1767 | if (!l) | ||
1768 | break; | ||
1769 | } | ||
1770 | return l; | ||
1771 | } | ||
1772 | |||
1773 | |||
1761 | /* | 1774 | /* |
1762 | * Caller must hold RTNL. | 1775 | * Caller must hold RTNL. |
1763 | */ | 1776 | */ |
@@ -1863,7 +1876,7 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, | |||
1863 | struct fib_alias *fa; | 1876 | struct fib_alias *fa; |
1864 | __be32 xkey = htonl(key); | 1877 | __be32 xkey = htonl(key); |
1865 | 1878 | ||
1866 | s_i = cb->args[4]; | 1879 | s_i = cb->args[5]; |
1867 | i = 0; | 1880 | i = 0; |
1868 | 1881 | ||
1869 | /* rcu_read_lock is hold by caller */ | 1882 | /* 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, | |||
1884 | plen, | 1897 | plen, |
1885 | fa->fa_tos, | 1898 | fa->fa_tos, |
1886 | fa->fa_info, NLM_F_MULTI) < 0) { | 1899 | fa->fa_info, NLM_F_MULTI) < 0) { |
1887 | cb->args[4] = i; | 1900 | cb->args[5] = i; |
1888 | return -1; | 1901 | return -1; |
1889 | } | 1902 | } |
1890 | i++; | 1903 | i++; |
1891 | } | 1904 | } |
1892 | cb->args[4] = i; | 1905 | cb->args[5] = i; |
1893 | return skb->len; | 1906 | return skb->len; |
1894 | } | 1907 | } |
1895 | 1908 | ||
@@ -1900,7 +1913,7 @@ static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb, | |||
1900 | struct hlist_node *node; | 1913 | struct hlist_node *node; |
1901 | int i, s_i; | 1914 | int i, s_i; |
1902 | 1915 | ||
1903 | s_i = cb->args[3]; | 1916 | s_i = cb->args[4]; |
1904 | i = 0; | 1917 | i = 0; |
1905 | 1918 | ||
1906 | /* rcu_read_lock is hold by caller */ | 1919 | /* rcu_read_lock is hold by caller */ |
@@ -1911,19 +1924,19 @@ static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb, | |||
1911 | } | 1924 | } |
1912 | 1925 | ||
1913 | if (i > s_i) | 1926 | if (i > s_i) |
1914 | cb->args[4] = 0; | 1927 | cb->args[5] = 0; |
1915 | 1928 | ||
1916 | if (list_empty(&li->falh)) | 1929 | if (list_empty(&li->falh)) |
1917 | continue; | 1930 | continue; |
1918 | 1931 | ||
1919 | if (fn_trie_dump_fa(l->key, li->plen, &li->falh, tb, skb, cb) < 0) { | 1932 | if (fn_trie_dump_fa(l->key, li->plen, &li->falh, tb, skb, cb) < 0) { |
1920 | cb->args[3] = i; | 1933 | cb->args[4] = i; |
1921 | return -1; | 1934 | return -1; |
1922 | } | 1935 | } |
1923 | i++; | 1936 | i++; |
1924 | } | 1937 | } |
1925 | 1938 | ||
1926 | cb->args[3] = i; | 1939 | cb->args[4] = i; |
1927 | return skb->len; | 1940 | return skb->len; |
1928 | } | 1941 | } |
1929 | 1942 | ||
@@ -1933,35 +1946,37 @@ static int fn_trie_dump(struct fib_table *tb, struct sk_buff *skb, | |||
1933 | struct leaf *l; | 1946 | struct leaf *l; |
1934 | struct trie *t = (struct trie *) tb->tb_data; | 1947 | struct trie *t = (struct trie *) tb->tb_data; |
1935 | t_key key = cb->args[2]; | 1948 | t_key key = cb->args[2]; |
1949 | int count = cb->args[3]; | ||
1936 | 1950 | ||
1937 | rcu_read_lock(); | 1951 | rcu_read_lock(); |
1938 | /* Dump starting at last key. | 1952 | /* Dump starting at last key. |
1939 | * Note: 0.0.0.0/0 (ie default) is first key. | 1953 | * Note: 0.0.0.0/0 (ie default) is first key. |
1940 | */ | 1954 | */ |
1941 | if (!key) | 1955 | if (count == 0) |
1942 | l = trie_firstleaf(t); | 1956 | l = trie_firstleaf(t); |
1943 | else { | 1957 | else { |
1958 | /* Normally, continue from last key, but if that is missing | ||
1959 | * fallback to using slow rescan | ||
1960 | */ | ||
1944 | l = fib_find_node(t, key); | 1961 | l = fib_find_node(t, key); |
1945 | if (!l) { | 1962 | if (!l) |
1946 | /* The table changed during the dump, rather than | 1963 | l = trie_leafindex(t, count); |
1947 | * giving partial data, just make application retry. | ||
1948 | */ | ||
1949 | rcu_read_unlock(); | ||
1950 | return -EBUSY; | ||
1951 | } | ||
1952 | } | 1964 | } |
1953 | 1965 | ||
1954 | while (l) { | 1966 | while (l) { |
1955 | cb->args[2] = l->key; | 1967 | cb->args[2] = l->key; |
1956 | if (fn_trie_dump_leaf(l, tb, skb, cb) < 0) { | 1968 | if (fn_trie_dump_leaf(l, tb, skb, cb) < 0) { |
1969 | cb->args[3] = count; | ||
1957 | rcu_read_unlock(); | 1970 | rcu_read_unlock(); |
1958 | return -1; | 1971 | return -1; |
1959 | } | 1972 | } |
1960 | 1973 | ||
1974 | ++count; | ||
1961 | l = trie_nextleaf(l); | 1975 | l = trie_nextleaf(l); |
1962 | memset(&cb->args[3], 0, | 1976 | memset(&cb->args[4], 0, |
1963 | sizeof(cb->args) - 3*sizeof(cb->args[0])); | 1977 | sizeof(cb->args) - 4*sizeof(cb->args[0])); |
1964 | } | 1978 | } |
1979 | cb->args[3] = count; | ||
1965 | rcu_read_unlock(); | 1980 | rcu_read_unlock(); |
1966 | 1981 | ||
1967 | return skb->len; | 1982 | return skb->len; |