diff options
Diffstat (limited to 'net/ipv4/fib_trie.c')
-rw-r--r-- | net/ipv4/fib_trie.c | 104 |
1 files changed, 67 insertions, 37 deletions
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index f2f47033f31f..35851c96bdfb 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c | |||
@@ -1205,20 +1205,45 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg) | |||
1205 | * and we need to allocate a new one of those as well. | 1205 | * and we need to allocate a new one of those as well. |
1206 | */ | 1206 | */ |
1207 | 1207 | ||
1208 | if (fa && fa->fa_info->fib_priority == fi->fib_priority) { | 1208 | if (fa && fa->fa_tos == tos && |
1209 | struct fib_alias *fa_orig; | 1209 | fa->fa_info->fib_priority == fi->fib_priority) { |
1210 | struct fib_alias *fa_first, *fa_match; | ||
1210 | 1211 | ||
1211 | err = -EEXIST; | 1212 | err = -EEXIST; |
1212 | if (cfg->fc_nlflags & NLM_F_EXCL) | 1213 | if (cfg->fc_nlflags & NLM_F_EXCL) |
1213 | goto out; | 1214 | goto out; |
1214 | 1215 | ||
1216 | /* We have 2 goals: | ||
1217 | * 1. Find exact match for type, scope, fib_info to avoid | ||
1218 | * duplicate routes | ||
1219 | * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it | ||
1220 | */ | ||
1221 | fa_match = NULL; | ||
1222 | fa_first = fa; | ||
1223 | fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list); | ||
1224 | list_for_each_entry_continue(fa, fa_head, fa_list) { | ||
1225 | if (fa->fa_tos != tos) | ||
1226 | break; | ||
1227 | if (fa->fa_info->fib_priority != fi->fib_priority) | ||
1228 | break; | ||
1229 | if (fa->fa_type == cfg->fc_type && | ||
1230 | fa->fa_scope == cfg->fc_scope && | ||
1231 | fa->fa_info == fi) { | ||
1232 | fa_match = fa; | ||
1233 | break; | ||
1234 | } | ||
1235 | } | ||
1236 | |||
1215 | if (cfg->fc_nlflags & NLM_F_REPLACE) { | 1237 | if (cfg->fc_nlflags & NLM_F_REPLACE) { |
1216 | struct fib_info *fi_drop; | 1238 | struct fib_info *fi_drop; |
1217 | u8 state; | 1239 | u8 state; |
1218 | 1240 | ||
1219 | if (fi->fib_treeref > 1) | 1241 | fa = fa_first; |
1242 | if (fa_match) { | ||
1243 | if (fa == fa_match) | ||
1244 | err = 0; | ||
1220 | goto out; | 1245 | goto out; |
1221 | 1246 | } | |
1222 | err = -ENOBUFS; | 1247 | err = -ENOBUFS; |
1223 | new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); | 1248 | new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL); |
1224 | if (new_fa == NULL) | 1249 | if (new_fa == NULL) |
@@ -1230,7 +1255,7 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg) | |||
1230 | new_fa->fa_type = cfg->fc_type; | 1255 | new_fa->fa_type = cfg->fc_type; |
1231 | new_fa->fa_scope = cfg->fc_scope; | 1256 | new_fa->fa_scope = cfg->fc_scope; |
1232 | state = fa->fa_state; | 1257 | state = fa->fa_state; |
1233 | new_fa->fa_state &= ~FA_S_ACCESSED; | 1258 | new_fa->fa_state = state & ~FA_S_ACCESSED; |
1234 | 1259 | ||
1235 | list_replace_rcu(&fa->fa_list, &new_fa->fa_list); | 1260 | list_replace_rcu(&fa->fa_list, &new_fa->fa_list); |
1236 | alias_free_mem_rcu(fa); | 1261 | alias_free_mem_rcu(fa); |
@@ -1247,20 +1272,11 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg) | |||
1247 | * uses the same scope, type, and nexthop | 1272 | * uses the same scope, type, and nexthop |
1248 | * information. | 1273 | * information. |
1249 | */ | 1274 | */ |
1250 | fa_orig = fa; | 1275 | if (fa_match) |
1251 | list_for_each_entry(fa, fa_orig->fa_list.prev, fa_list) { | 1276 | goto out; |
1252 | if (fa->fa_tos != tos) | ||
1253 | break; | ||
1254 | if (fa->fa_info->fib_priority != fi->fib_priority) | ||
1255 | break; | ||
1256 | if (fa->fa_type == cfg->fc_type && | ||
1257 | fa->fa_scope == cfg->fc_scope && | ||
1258 | fa->fa_info == fi) | ||
1259 | goto out; | ||
1260 | } | ||
1261 | 1277 | ||
1262 | if (!(cfg->fc_nlflags & NLM_F_APPEND)) | 1278 | if (!(cfg->fc_nlflags & NLM_F_APPEND)) |
1263 | fa = fa_orig; | 1279 | fa = fa_first; |
1264 | } | 1280 | } |
1265 | err = -ENOENT; | 1281 | err = -ENOENT; |
1266 | if (!(cfg->fc_nlflags & NLM_F_CREATE)) | 1282 | if (!(cfg->fc_nlflags & NLM_F_CREATE)) |
@@ -1600,9 +1616,8 @@ static int fn_trie_delete(struct fib_table *tb, struct fib_config *cfg) | |||
1600 | pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t); | 1616 | pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t); |
1601 | 1617 | ||
1602 | fa_to_delete = NULL; | 1618 | fa_to_delete = NULL; |
1603 | fa_head = fa->fa_list.prev; | 1619 | fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list); |
1604 | 1620 | list_for_each_entry_continue(fa, fa_head, fa_list) { | |
1605 | list_for_each_entry(fa, fa_head, fa_list) { | ||
1606 | struct fib_info *fi = fa->fa_info; | 1621 | struct fib_info *fi = fa->fa_info; |
1607 | 1622 | ||
1608 | if (fa->fa_tos != tos) | 1623 | if (fa->fa_tos != tos) |
@@ -1743,6 +1758,19 @@ static struct leaf *trie_nextleaf(struct leaf *l) | |||
1743 | return leaf_walk_rcu(p, c); | 1758 | return leaf_walk_rcu(p, c); |
1744 | } | 1759 | } |
1745 | 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 | |||
1746 | /* | 1774 | /* |
1747 | * Caller must hold RTNL. | 1775 | * Caller must hold RTNL. |
1748 | */ | 1776 | */ |
@@ -1848,7 +1876,7 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, | |||
1848 | struct fib_alias *fa; | 1876 | struct fib_alias *fa; |
1849 | __be32 xkey = htonl(key); | 1877 | __be32 xkey = htonl(key); |
1850 | 1878 | ||
1851 | s_i = cb->args[4]; | 1879 | s_i = cb->args[5]; |
1852 | i = 0; | 1880 | i = 0; |
1853 | 1881 | ||
1854 | /* rcu_read_lock is hold by caller */ | 1882 | /* rcu_read_lock is hold by caller */ |
@@ -1869,12 +1897,12 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, | |||
1869 | plen, | 1897 | plen, |
1870 | fa->fa_tos, | 1898 | fa->fa_tos, |
1871 | fa->fa_info, NLM_F_MULTI) < 0) { | 1899 | fa->fa_info, NLM_F_MULTI) < 0) { |
1872 | cb->args[4] = i; | 1900 | cb->args[5] = i; |
1873 | return -1; | 1901 | return -1; |
1874 | } | 1902 | } |
1875 | i++; | 1903 | i++; |
1876 | } | 1904 | } |
1877 | cb->args[4] = i; | 1905 | cb->args[5] = i; |
1878 | return skb->len; | 1906 | return skb->len; |
1879 | } | 1907 | } |
1880 | 1908 | ||
@@ -1885,7 +1913,7 @@ static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb, | |||
1885 | struct hlist_node *node; | 1913 | struct hlist_node *node; |
1886 | int i, s_i; | 1914 | int i, s_i; |
1887 | 1915 | ||
1888 | s_i = cb->args[3]; | 1916 | s_i = cb->args[4]; |
1889 | i = 0; | 1917 | i = 0; |
1890 | 1918 | ||
1891 | /* rcu_read_lock is hold by caller */ | 1919 | /* rcu_read_lock is hold by caller */ |
@@ -1896,19 +1924,19 @@ static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb, | |||
1896 | } | 1924 | } |
1897 | 1925 | ||
1898 | if (i > s_i) | 1926 | if (i > s_i) |
1899 | cb->args[4] = 0; | 1927 | cb->args[5] = 0; |
1900 | 1928 | ||
1901 | if (list_empty(&li->falh)) | 1929 | if (list_empty(&li->falh)) |
1902 | continue; | 1930 | continue; |
1903 | 1931 | ||
1904 | 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) { |
1905 | cb->args[3] = i; | 1933 | cb->args[4] = i; |
1906 | return -1; | 1934 | return -1; |
1907 | } | 1935 | } |
1908 | i++; | 1936 | i++; |
1909 | } | 1937 | } |
1910 | 1938 | ||
1911 | cb->args[3] = i; | 1939 | cb->args[4] = i; |
1912 | return skb->len; | 1940 | return skb->len; |
1913 | } | 1941 | } |
1914 | 1942 | ||
@@ -1918,35 +1946,37 @@ static int fn_trie_dump(struct fib_table *tb, struct sk_buff *skb, | |||
1918 | struct leaf *l; | 1946 | struct leaf *l; |
1919 | struct trie *t = (struct trie *) tb->tb_data; | 1947 | struct trie *t = (struct trie *) tb->tb_data; |
1920 | t_key key = cb->args[2]; | 1948 | t_key key = cb->args[2]; |
1949 | int count = cb->args[3]; | ||
1921 | 1950 | ||
1922 | rcu_read_lock(); | 1951 | rcu_read_lock(); |
1923 | /* Dump starting at last key. | 1952 | /* Dump starting at last key. |
1924 | * Note: 0.0.0.0/0 (ie default) is first key. | 1953 | * Note: 0.0.0.0/0 (ie default) is first key. |
1925 | */ | 1954 | */ |
1926 | if (!key) | 1955 | if (count == 0) |
1927 | l = trie_firstleaf(t); | 1956 | l = trie_firstleaf(t); |
1928 | else { | 1957 | else { |
1958 | /* Normally, continue from last key, but if that is missing | ||
1959 | * fallback to using slow rescan | ||
1960 | */ | ||
1929 | l = fib_find_node(t, key); | 1961 | l = fib_find_node(t, key); |
1930 | if (!l) { | 1962 | if (!l) |
1931 | /* The table changed during the dump, rather than | 1963 | l = trie_leafindex(t, count); |
1932 | * giving partial data, just make application retry. | ||
1933 | */ | ||
1934 | rcu_read_unlock(); | ||
1935 | return -EBUSY; | ||
1936 | } | ||
1937 | } | 1964 | } |
1938 | 1965 | ||
1939 | while (l) { | 1966 | while (l) { |
1940 | cb->args[2] = l->key; | 1967 | cb->args[2] = l->key; |
1941 | 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; | ||
1942 | rcu_read_unlock(); | 1970 | rcu_read_unlock(); |
1943 | return -1; | 1971 | return -1; |
1944 | } | 1972 | } |
1945 | 1973 | ||
1974 | ++count; | ||
1946 | l = trie_nextleaf(l); | 1975 | l = trie_nextleaf(l); |
1947 | memset(&cb->args[3], 0, | 1976 | memset(&cb->args[4], 0, |
1948 | sizeof(cb->args) - 3*sizeof(cb->args[0])); | 1977 | sizeof(cb->args) - 4*sizeof(cb->args[0])); |
1949 | } | 1978 | } |
1979 | cb->args[3] = count; | ||
1950 | rcu_read_unlock(); | 1980 | rcu_read_unlock(); |
1951 | 1981 | ||
1952 | return skb->len; | 1982 | return skb->len; |