diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2005-09-09 16:35:42 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2005-09-09 16:35:42 -0400 |
commit | cb7b593c2c808b32a1ea188599713c434b95f849 (patch) | |
tree | e2fe1723327671120272bedafee37fe7ab36ebd6 /net | |
parent | 8259f1625789ad03e6451ecef3e690af52e8e802 (diff) |
[IPV4] fib_trie: fix proc interface
Create one iterator for walking over FIB trie, and use it
for all the /proc functions. Add a /proc/net/route
output for backwards compatibility with old applications.
Make initialization of fib_trie same as fib_hash so no #ifdef
is needed in af_inet.c
Fixes: http://bugzilla.kernel.org/show_bug.cgi?id=5209
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/af_inet.c | 13 | ||||
-rw-r--r-- | net/ipv4/fib_trie.c | 804 |
2 files changed, 385 insertions, 432 deletions
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index bf147f8db399..a9d84f93442c 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c | |||
@@ -1248,11 +1248,6 @@ module_init(inet_init); | |||
1248 | /* ------------------------------------------------------------------------ */ | 1248 | /* ------------------------------------------------------------------------ */ |
1249 | 1249 | ||
1250 | #ifdef CONFIG_PROC_FS | 1250 | #ifdef CONFIG_PROC_FS |
1251 | #ifdef CONFIG_IP_FIB_TRIE | ||
1252 | extern int fib_stat_proc_init(void); | ||
1253 | extern void fib_stat_proc_exit(void); | ||
1254 | #endif | ||
1255 | |||
1256 | static int __init ipv4_proc_init(void) | 1251 | static int __init ipv4_proc_init(void) |
1257 | { | 1252 | { |
1258 | int rc = 0; | 1253 | int rc = 0; |
@@ -1265,19 +1260,11 @@ static int __init ipv4_proc_init(void) | |||
1265 | goto out_udp; | 1260 | goto out_udp; |
1266 | if (fib_proc_init()) | 1261 | if (fib_proc_init()) |
1267 | goto out_fib; | 1262 | goto out_fib; |
1268 | #ifdef CONFIG_IP_FIB_TRIE | ||
1269 | if (fib_stat_proc_init()) | ||
1270 | goto out_fib_stat; | ||
1271 | #endif | ||
1272 | if (ip_misc_proc_init()) | 1263 | if (ip_misc_proc_init()) |
1273 | goto out_misc; | 1264 | goto out_misc; |
1274 | out: | 1265 | out: |
1275 | return rc; | 1266 | return rc; |
1276 | out_misc: | 1267 | out_misc: |
1277 | #ifdef CONFIG_IP_FIB_TRIE | ||
1278 | fib_stat_proc_exit(); | ||
1279 | out_fib_stat: | ||
1280 | #endif | ||
1281 | fib_proc_exit(); | 1268 | fib_proc_exit(); |
1282 | out_fib: | 1269 | out_fib: |
1283 | udp4_proc_exit(); | 1270 | udp4_proc_exit(); |
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index b2dea4e5da77..1b63b4824164 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c | |||
@@ -43,7 +43,7 @@ | |||
43 | * 2 of the License, or (at your option) any later version. | 43 | * 2 of the License, or (at your option) any later version. |
44 | */ | 44 | */ |
45 | 45 | ||
46 | #define VERSION "0.402" | 46 | #define VERSION "0.403" |
47 | 47 | ||
48 | #include <linux/config.h> | 48 | #include <linux/config.h> |
49 | #include <asm/uaccess.h> | 49 | #include <asm/uaccess.h> |
@@ -164,7 +164,6 @@ static struct node *resize(struct trie *t, struct tnode *tn); | |||
164 | static struct tnode *inflate(struct trie *t, struct tnode *tn); | 164 | static struct tnode *inflate(struct trie *t, struct tnode *tn); |
165 | static struct tnode *halve(struct trie *t, struct tnode *tn); | 165 | static struct tnode *halve(struct trie *t, struct tnode *tn); |
166 | static void tnode_free(struct tnode *tn); | 166 | static void tnode_free(struct tnode *tn); |
167 | static void trie_dump_seq(struct seq_file *seq, struct trie *t); | ||
168 | 167 | ||
169 | static kmem_cache_t *fn_alias_kmem __read_mostly; | 168 | static kmem_cache_t *fn_alias_kmem __read_mostly; |
170 | static struct trie *trie_local = NULL, *trie_main = NULL; | 169 | static struct trie *trie_local = NULL, *trie_main = NULL; |
@@ -1971,558 +1970,525 @@ struct fib_table * __init fib_hash_init(int id) | |||
1971 | return tb; | 1970 | return tb; |
1972 | } | 1971 | } |
1973 | 1972 | ||
1974 | /* Trie dump functions */ | 1973 | #ifdef CONFIG_PROC_FS |
1974 | /* Depth first Trie walk iterator */ | ||
1975 | struct fib_trie_iter { | ||
1976 | struct tnode *tnode; | ||
1977 | struct trie *trie; | ||
1978 | unsigned index; | ||
1979 | unsigned depth; | ||
1980 | }; | ||
1975 | 1981 | ||
1976 | static void putspace_seq(struct seq_file *seq, int n) | 1982 | static struct node *fib_trie_get_next(struct fib_trie_iter *iter) |
1977 | { | 1983 | { |
1978 | while (n--) | 1984 | struct tnode *tn = iter->tnode; |
1979 | seq_printf(seq, " "); | 1985 | unsigned cindex = iter->index; |
1980 | } | 1986 | struct tnode *p; |
1981 | 1987 | ||
1982 | static void printbin_seq(struct seq_file *seq, unsigned int v, int bits) | 1988 | pr_debug("get_next iter={node=%p index=%d depth=%d}\n", |
1983 | { | 1989 | iter->tnode, iter->index, iter->depth); |
1984 | while (bits--) | 1990 | rescan: |
1985 | seq_printf(seq, "%s", (v & (1<<bits))?"1":"0"); | 1991 | while (cindex < (1<<tn->bits)) { |
1986 | } | 1992 | struct node *n = tnode_get_child(tn, cindex); |
1987 | 1993 | ||
1988 | static void printnode_seq(struct seq_file *seq, int indent, struct node *n, | 1994 | if (n) { |
1989 | int pend, int cindex, int bits) | 1995 | if (IS_LEAF(n)) { |
1990 | { | 1996 | iter->tnode = tn; |
1991 | putspace_seq(seq, indent); | 1997 | iter->index = cindex + 1; |
1992 | if (IS_LEAF(n)) | 1998 | } else { |
1993 | seq_printf(seq, "|"); | 1999 | /* push down one level */ |
1994 | else | 2000 | iter->tnode = (struct tnode *) n; |
1995 | seq_printf(seq, "+"); | 2001 | iter->index = 0; |
1996 | if (bits) { | 2002 | ++iter->depth; |
1997 | seq_printf(seq, "%d/", cindex); | 2003 | } |
1998 | printbin_seq(seq, cindex, bits); | 2004 | return n; |
1999 | seq_printf(seq, ": "); | 2005 | } |
2000 | } else | ||
2001 | seq_printf(seq, "<root>: "); | ||
2002 | seq_printf(seq, "%s:%p ", IS_LEAF(n)?"Leaf":"Internal node", n); | ||
2003 | 2006 | ||
2004 | if (IS_LEAF(n)) { | 2007 | ++cindex; |
2005 | struct leaf *l = (struct leaf *)n; | 2008 | } |
2006 | struct fib_alias *fa; | ||
2007 | int i; | ||
2008 | 2009 | ||
2009 | seq_printf(seq, "key=%d.%d.%d.%d\n", | 2010 | /* Current node exhausted, pop back up */ |
2010 | n->key >> 24, (n->key >> 16) % 256, (n->key >> 8) % 256, n->key % 256); | 2011 | p = NODE_PARENT(tn); |
2011 | 2012 | if (p) { | |
2012 | for (i = 32; i >= 0; i--) | 2013 | cindex = tkey_extract_bits(tn->key, p->pos, p->bits)+1; |
2013 | if (find_leaf_info(&l->list, i)) { | 2014 | tn = p; |
2014 | struct list_head *fa_head = get_fa_head(l, i); | 2015 | --iter->depth; |
2015 | 2016 | goto rescan; | |
2016 | if (!fa_head) | ||
2017 | continue; | ||
2018 | |||
2019 | if (list_empty(fa_head)) | ||
2020 | continue; | ||
2021 | |||
2022 | putspace_seq(seq, indent+2); | ||
2023 | seq_printf(seq, "{/%d...dumping}\n", i); | ||
2024 | |||
2025 | list_for_each_entry_rcu(fa, fa_head, fa_list) { | ||
2026 | putspace_seq(seq, indent+2); | ||
2027 | if (fa->fa_info == NULL) { | ||
2028 | seq_printf(seq, "Error fa_info=NULL\n"); | ||
2029 | continue; | ||
2030 | } | ||
2031 | if (fa->fa_info->fib_nh == NULL) { | ||
2032 | seq_printf(seq, "Error _fib_nh=NULL\n"); | ||
2033 | continue; | ||
2034 | } | ||
2035 | |||
2036 | seq_printf(seq, "{type=%d scope=%d TOS=%d}\n", | ||
2037 | fa->fa_type, | ||
2038 | fa->fa_scope, | ||
2039 | fa->fa_tos); | ||
2040 | } | ||
2041 | } | ||
2042 | } else { | ||
2043 | struct tnode *tn = (struct tnode *)n; | ||
2044 | int plen = ((struct tnode *)n)->pos; | ||
2045 | t_key prf = MASK_PFX(n->key, plen); | ||
2046 | |||
2047 | seq_printf(seq, "key=%d.%d.%d.%d/%d\n", | ||
2048 | prf >> 24, (prf >> 16) % 256, (prf >> 8) % 256, prf % 256, plen); | ||
2049 | |||
2050 | putspace_seq(seq, indent); seq_printf(seq, "| "); | ||
2051 | seq_printf(seq, "{key prefix=%08x/", tn->key & TKEY_GET_MASK(0, tn->pos)); | ||
2052 | printbin_seq(seq, tkey_extract_bits(tn->key, 0, tn->pos), tn->pos); | ||
2053 | seq_printf(seq, "}\n"); | ||
2054 | putspace_seq(seq, indent); seq_printf(seq, "| "); | ||
2055 | seq_printf(seq, "{pos=%d", tn->pos); | ||
2056 | seq_printf(seq, " (skip=%d bits)", tn->pos - pend); | ||
2057 | seq_printf(seq, " bits=%d (%u children)}\n", tn->bits, (1 << tn->bits)); | ||
2058 | putspace_seq(seq, indent); seq_printf(seq, "| "); | ||
2059 | seq_printf(seq, "{empty=%d full=%d}\n", tn->empty_children, tn->full_children); | ||
2060 | } | 2017 | } |
2018 | |||
2019 | /* got root? */ | ||
2020 | return NULL; | ||
2061 | } | 2021 | } |
2062 | 2022 | ||
2063 | static void trie_dump_seq(struct seq_file *seq, struct trie *t) | 2023 | static struct node *fib_trie_get_first(struct fib_trie_iter *iter, |
2024 | struct trie *t) | ||
2064 | { | 2025 | { |
2065 | struct node *n; | 2026 | struct node *n = rcu_dereference(t->trie); |
2066 | int cindex = 0; | ||
2067 | int indent = 1; | ||
2068 | int pend = 0; | ||
2069 | int depth = 0; | ||
2070 | struct tnode *tn; | ||
2071 | |||
2072 | rcu_read_lock(); | ||
2073 | n = rcu_dereference(t->trie); | ||
2074 | seq_printf(seq, "------ trie_dump of t=%p ------\n", t); | ||
2075 | 2027 | ||
2076 | if (!n) { | 2028 | if (n && IS_TNODE(n)) { |
2077 | seq_printf(seq, "------ trie is empty\n"); | 2029 | iter->tnode = (struct tnode *) n; |
2078 | 2030 | iter->trie = t; | |
2079 | rcu_read_unlock(); | 2031 | iter->index = 0; |
2080 | return; | 2032 | iter->depth = 0; |
2033 | return n; | ||
2081 | } | 2034 | } |
2035 | return NULL; | ||
2036 | } | ||
2082 | 2037 | ||
2083 | printnode_seq(seq, indent, n, pend, cindex, 0); | 2038 | static void trie_collect_stats(struct trie *t, struct trie_stat *s) |
2084 | 2039 | { | |
2085 | if (!IS_TNODE(n)) { | 2040 | struct node *n; |
2086 | rcu_read_unlock(); | 2041 | struct fib_trie_iter iter; |
2087 | return; | ||
2088 | } | ||
2089 | |||
2090 | tn = (struct tnode *)n; | ||
2091 | pend = tn->pos+tn->bits; | ||
2092 | putspace_seq(seq, indent); seq_printf(seq, "\\--\n"); | ||
2093 | indent += 3; | ||
2094 | depth++; | ||
2095 | |||
2096 | while (tn && cindex < (1 << tn->bits)) { | ||
2097 | struct node *child = rcu_dereference(tn->child[cindex]); | ||
2098 | if (!child) | ||
2099 | cindex++; | ||
2100 | else { | ||
2101 | /* Got a child */ | ||
2102 | printnode_seq(seq, indent, child, pend, | ||
2103 | cindex, tn->bits); | ||
2104 | |||
2105 | if (IS_LEAF(child)) | ||
2106 | cindex++; | ||
2107 | |||
2108 | else { | ||
2109 | /* | ||
2110 | * New tnode. Decend one level | ||
2111 | */ | ||
2112 | |||
2113 | depth++; | ||
2114 | n = child; | ||
2115 | tn = (struct tnode *)n; | ||
2116 | pend = tn->pos+tn->bits; | ||
2117 | putspace_seq(seq, indent); | ||
2118 | seq_printf(seq, "\\--\n"); | ||
2119 | indent += 3; | ||
2120 | cindex = 0; | ||
2121 | } | ||
2122 | } | ||
2123 | |||
2124 | /* | ||
2125 | * Test if we are done | ||
2126 | */ | ||
2127 | |||
2128 | while (cindex >= (1 << tn->bits)) { | ||
2129 | /* | ||
2130 | * Move upwards and test for root | ||
2131 | * pop off all traversed nodes | ||
2132 | */ | ||
2133 | 2042 | ||
2134 | if (NODE_PARENT(tn) == NULL) { | 2043 | memset(s, 0, sizeof(*s)); |
2135 | tn = NULL; | ||
2136 | break; | ||
2137 | } | ||
2138 | 2044 | ||
2139 | cindex = tkey_extract_bits(tn->key, NODE_PARENT(tn)->pos, NODE_PARENT(tn)->bits); | 2045 | rcu_read_lock(); |
2140 | cindex++; | 2046 | for (n = fib_trie_get_first(&iter, t); n; |
2141 | tn = NODE_PARENT(tn); | 2047 | n = fib_trie_get_next(&iter)) { |
2142 | pend = tn->pos + tn->bits; | 2048 | if (IS_LEAF(n)) { |
2143 | indent -= 3; | 2049 | s->leaves++; |
2144 | depth--; | 2050 | s->totdepth += iter.depth; |
2051 | if (iter.depth > s->maxdepth) | ||
2052 | s->maxdepth = iter.depth; | ||
2053 | } else { | ||
2054 | const struct tnode *tn = (const struct tnode *) n; | ||
2055 | int i; | ||
2056 | |||
2057 | s->tnodes++; | ||
2058 | s->nodesizes[tn->bits]++; | ||
2059 | for (i = 0; i < (1<<tn->bits); i++) | ||
2060 | if (!tn->child[i]) | ||
2061 | s->nullpointers++; | ||
2145 | } | 2062 | } |
2146 | } | 2063 | } |
2147 | rcu_read_unlock(); | 2064 | rcu_read_unlock(); |
2148 | } | 2065 | } |
2149 | 2066 | ||
2150 | static struct trie_stat *trie_stat_new(void) | 2067 | /* |
2068 | * This outputs /proc/net/fib_triestats | ||
2069 | */ | ||
2070 | static void trie_show_stats(struct seq_file *seq, struct trie_stat *stat) | ||
2151 | { | 2071 | { |
2152 | struct trie_stat *s; | 2072 | unsigned i, max, pointers, bytes, avdepth; |
2153 | int i; | ||
2154 | 2073 | ||
2155 | s = kmalloc(sizeof(struct trie_stat), GFP_KERNEL); | 2074 | if (stat->leaves) |
2156 | if (!s) | 2075 | avdepth = stat->totdepth*100 / stat->leaves; |
2157 | return NULL; | 2076 | else |
2077 | avdepth = 0; | ||
2158 | 2078 | ||
2159 | s->totdepth = 0; | 2079 | seq_printf(seq, "\tAver depth: %d.%02d\n", avdepth / 100, avdepth % 100 ); |
2160 | s->maxdepth = 0; | 2080 | seq_printf(seq, "\tMax depth: %u\n", stat->maxdepth); |
2161 | s->tnodes = 0; | ||
2162 | s->leaves = 0; | ||
2163 | s->nullpointers = 0; | ||
2164 | 2081 | ||
2165 | for (i = 0; i < MAX_CHILDS; i++) | 2082 | seq_printf(seq, "\tLeaves: %u\n", stat->leaves); |
2166 | s->nodesizes[i] = 0; | ||
2167 | 2083 | ||
2168 | return s; | 2084 | bytes = sizeof(struct leaf) * stat->leaves; |
2169 | } | 2085 | seq_printf(seq, "\tInternal nodes: %d\n\t", stat->tnodes); |
2086 | bytes += sizeof(struct tnode) * stat->tnodes; | ||
2170 | 2087 | ||
2171 | static struct trie_stat *trie_collect_stats(struct trie *t) | 2088 | max = MAX_CHILDS-1; |
2172 | { | 2089 | while (max >= 0 && stat->nodesizes[max] == 0) |
2173 | struct node *n; | 2090 | max--; |
2174 | struct trie_stat *s = trie_stat_new(); | ||
2175 | int cindex = 0; | ||
2176 | int pend = 0; | ||
2177 | int depth = 0; | ||
2178 | 2091 | ||
2179 | if (!s) | 2092 | pointers = 0; |
2180 | return NULL; | 2093 | for (i = 1; i <= max; i++) |
2094 | if (stat->nodesizes[i] != 0) { | ||
2095 | seq_printf(seq, " %d: %d", i, stat->nodesizes[i]); | ||
2096 | pointers += (1<<i) * stat->nodesizes[i]; | ||
2097 | } | ||
2098 | seq_putc(seq, '\n'); | ||
2099 | seq_printf(seq, "\tPointers: %d\n", pointers); | ||
2181 | 2100 | ||
2182 | rcu_read_lock(); | 2101 | bytes += sizeof(struct node *) * pointers; |
2183 | n = rcu_dereference(t->trie); | 2102 | seq_printf(seq, "Null ptrs: %d\n", stat->nullpointers); |
2103 | seq_printf(seq, "Total size: %d kB\n", (bytes + 1023) / 1024); | ||
2184 | 2104 | ||
2185 | if (!n) | 2105 | #ifdef CONFIG_IP_FIB_TRIE_STATS |
2186 | return s; | 2106 | seq_printf(seq, "Counters:\n---------\n"); |
2107 | seq_printf(seq,"gets = %d\n", t->stats.gets); | ||
2108 | seq_printf(seq,"backtracks = %d\n", t->stats.backtrack); | ||
2109 | seq_printf(seq,"semantic match passed = %d\n", t->stats.semantic_match_passed); | ||
2110 | seq_printf(seq,"semantic match miss = %d\n", t->stats.semantic_match_miss); | ||
2111 | seq_printf(seq,"null node hit= %d\n", t->stats.null_node_hit); | ||
2112 | seq_printf(seq,"skipped node resize = %d\n", t->stats.resize_node_skipped); | ||
2113 | #ifdef CLEAR_STATS | ||
2114 | memset(&(t->stats), 0, sizeof(t->stats)); | ||
2115 | #endif | ||
2116 | #endif /* CONFIG_IP_FIB_TRIE_STATS */ | ||
2117 | } | ||
2187 | 2118 | ||
2188 | if (IS_TNODE(n)) { | 2119 | static int fib_triestat_seq_show(struct seq_file *seq, void *v) |
2189 | struct tnode *tn = (struct tnode *)n; | 2120 | { |
2190 | pend = tn->pos+tn->bits; | 2121 | struct trie_stat *stat; |
2191 | s->nodesizes[tn->bits]++; | ||
2192 | depth++; | ||
2193 | |||
2194 | while (tn && cindex < (1 << tn->bits)) { | ||
2195 | struct node *ch = rcu_dereference(tn->child[cindex]); | ||
2196 | if (ch) { | ||
2197 | |||
2198 | /* Got a child */ | ||
2199 | |||
2200 | if (IS_LEAF(tn->child[cindex])) { | ||
2201 | cindex++; | ||
2202 | |||
2203 | /* stats */ | ||
2204 | if (depth > s->maxdepth) | ||
2205 | s->maxdepth = depth; | ||
2206 | s->totdepth += depth; | ||
2207 | s->leaves++; | ||
2208 | } else { | ||
2209 | /* | ||
2210 | * New tnode. Decend one level | ||
2211 | */ | ||
2212 | |||
2213 | s->tnodes++; | ||
2214 | s->nodesizes[tn->bits]++; | ||
2215 | depth++; | ||
2216 | |||
2217 | n = ch; | ||
2218 | tn = (struct tnode *)n; | ||
2219 | pend = tn->pos+tn->bits; | ||
2220 | |||
2221 | cindex = 0; | ||
2222 | } | ||
2223 | } else { | ||
2224 | cindex++; | ||
2225 | s->nullpointers++; | ||
2226 | } | ||
2227 | 2122 | ||
2228 | /* | 2123 | stat = kmalloc(sizeof(*stat), GFP_KERNEL); |
2229 | * Test if we are done | 2124 | if (!stat) |
2230 | */ | 2125 | return -ENOMEM; |
2231 | 2126 | ||
2232 | while (cindex >= (1 << tn->bits)) { | 2127 | seq_printf(seq, "Basic info: size of leaf: %Zd bytes, size of tnode: %Zd bytes.\n", |
2233 | /* | 2128 | sizeof(struct leaf), sizeof(struct tnode)); |
2234 | * Move upwards and test for root | ||
2235 | * pop off all traversed nodes | ||
2236 | */ | ||
2237 | 2129 | ||
2238 | if (NODE_PARENT(tn) == NULL) { | 2130 | if (trie_local) { |
2239 | tn = NULL; | 2131 | seq_printf(seq, "Local:\n"); |
2240 | n = NULL; | 2132 | trie_collect_stats(trie_local, stat); |
2241 | break; | 2133 | trie_show_stats(seq, stat); |
2242 | } | 2134 | } |
2243 | 2135 | ||
2244 | cindex = tkey_extract_bits(tn->key, NODE_PARENT(tn)->pos, NODE_PARENT(tn)->bits); | 2136 | if (trie_main) { |
2245 | tn = NODE_PARENT(tn); | 2137 | seq_printf(seq, "Main:\n"); |
2246 | cindex++; | 2138 | trie_collect_stats(trie_main, stat); |
2247 | n = (struct node *)tn; | 2139 | trie_show_stats(seq, stat); |
2248 | pend = tn->pos+tn->bits; | ||
2249 | depth--; | ||
2250 | } | ||
2251 | } | ||
2252 | } | 2140 | } |
2141 | kfree(stat); | ||
2253 | 2142 | ||
2254 | rcu_read_unlock(); | 2143 | return 0; |
2255 | return s; | ||
2256 | } | 2144 | } |
2257 | 2145 | ||
2258 | #ifdef CONFIG_PROC_FS | 2146 | static int fib_triestat_seq_open(struct inode *inode, struct file *file) |
2259 | |||
2260 | static struct fib_alias *fib_triestat_get_first(struct seq_file *seq) | ||
2261 | { | 2147 | { |
2262 | return NULL; | 2148 | return single_open(file, fib_triestat_seq_show, NULL); |
2263 | } | 2149 | } |
2264 | 2150 | ||
2265 | static struct fib_alias *fib_triestat_get_next(struct seq_file *seq) | 2151 | static struct file_operations fib_triestat_fops = { |
2152 | .owner = THIS_MODULE, | ||
2153 | .open = fib_triestat_seq_open, | ||
2154 | .read = seq_read, | ||
2155 | .llseek = seq_lseek, | ||
2156 | .release = single_release, | ||
2157 | }; | ||
2158 | |||
2159 | static struct node *fib_trie_get_idx(struct fib_trie_iter *iter, | ||
2160 | loff_t pos) | ||
2266 | { | 2161 | { |
2162 | loff_t idx = 0; | ||
2163 | struct node *n; | ||
2164 | |||
2165 | for (n = fib_trie_get_first(iter, trie_local); | ||
2166 | n; ++idx, n = fib_trie_get_next(iter)) { | ||
2167 | if (pos == idx) | ||
2168 | return n; | ||
2169 | } | ||
2170 | |||
2171 | for (n = fib_trie_get_first(iter, trie_main); | ||
2172 | n; ++idx, n = fib_trie_get_next(iter)) { | ||
2173 | if (pos == idx) | ||
2174 | return n; | ||
2175 | } | ||
2267 | return NULL; | 2176 | return NULL; |
2268 | } | 2177 | } |
2269 | 2178 | ||
2270 | static void *fib_triestat_seq_start(struct seq_file *seq, loff_t *pos) | 2179 | static void *fib_trie_seq_start(struct seq_file *seq, loff_t *pos) |
2271 | { | 2180 | { |
2272 | if (!ip_fib_main_table) | 2181 | rcu_read_lock(); |
2273 | return NULL; | 2182 | if (*pos == 0) |
2274 | |||
2275 | if (*pos) | ||
2276 | return fib_triestat_get_next(seq); | ||
2277 | else | ||
2278 | return SEQ_START_TOKEN; | 2183 | return SEQ_START_TOKEN; |
2184 | return fib_trie_get_idx(seq->private, *pos - 1); | ||
2279 | } | 2185 | } |
2280 | 2186 | ||
2281 | static void *fib_triestat_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 2187 | static void *fib_trie_seq_next(struct seq_file *seq, void *v, loff_t *pos) |
2282 | { | 2188 | { |
2189 | struct fib_trie_iter *iter = seq->private; | ||
2190 | void *l = v; | ||
2191 | |||
2283 | ++*pos; | 2192 | ++*pos; |
2284 | if (v == SEQ_START_TOKEN) | 2193 | if (v == SEQ_START_TOKEN) |
2285 | return fib_triestat_get_first(seq); | 2194 | return fib_trie_get_idx(iter, 0); |
2286 | else | ||
2287 | return fib_triestat_get_next(seq); | ||
2288 | } | ||
2289 | 2195 | ||
2290 | static void fib_triestat_seq_stop(struct seq_file *seq, void *v) | 2196 | v = fib_trie_get_next(iter); |
2291 | { | 2197 | BUG_ON(v == l); |
2198 | if (v) | ||
2199 | return v; | ||
2292 | 2200 | ||
2293 | } | 2201 | /* continue scan in next trie */ |
2202 | if (iter->trie == trie_local) | ||
2203 | return fib_trie_get_first(iter, trie_main); | ||
2294 | 2204 | ||
2295 | /* | 2205 | return NULL; |
2296 | * This outputs /proc/net/fib_triestats | 2206 | } |
2297 | * | ||
2298 | * It always works in backward compatibility mode. | ||
2299 | * The format of the file is not supposed to be changed. | ||
2300 | */ | ||
2301 | 2207 | ||
2302 | static void collect_and_show(struct trie *t, struct seq_file *seq) | 2208 | static void fib_trie_seq_stop(struct seq_file *seq, void *v) |
2303 | { | 2209 | { |
2304 | int bytes = 0; /* How many bytes are used, a ref is 4 bytes */ | 2210 | rcu_read_unlock(); |
2305 | int i, max, pointers; | 2211 | } |
2306 | struct trie_stat *stat; | ||
2307 | int avdepth; | ||
2308 | |||
2309 | stat = trie_collect_stats(t); | ||
2310 | |||
2311 | bytes = 0; | ||
2312 | seq_printf(seq, "trie=%p\n", t); | ||
2313 | |||
2314 | if (stat) { | ||
2315 | if (stat->leaves) | ||
2316 | avdepth = stat->totdepth*100 / stat->leaves; | ||
2317 | else | ||
2318 | avdepth = 0; | ||
2319 | seq_printf(seq, "Aver depth: %d.%02d\n", avdepth / 100, avdepth % 100); | ||
2320 | seq_printf(seq, "Max depth: %4d\n", stat->maxdepth); | ||
2321 | 2212 | ||
2322 | seq_printf(seq, "Leaves: %d\n", stat->leaves); | 2213 | static void seq_indent(struct seq_file *seq, int n) |
2323 | bytes += sizeof(struct leaf) * stat->leaves; | 2214 | { |
2324 | seq_printf(seq, "Internal nodes: %d\n", stat->tnodes); | 2215 | while (n-- > 0) seq_puts(seq, " "); |
2325 | bytes += sizeof(struct tnode) * stat->tnodes; | 2216 | } |
2326 | 2217 | ||
2327 | max = MAX_CHILDS-1; | 2218 | static inline const char *rtn_scope(enum rt_scope_t s) |
2219 | { | ||
2220 | static char buf[32]; | ||
2328 | 2221 | ||
2329 | while (max >= 0 && stat->nodesizes[max] == 0) | 2222 | switch(s) { |
2330 | max--; | 2223 | case RT_SCOPE_UNIVERSE: return "universe"; |
2331 | pointers = 0; | 2224 | case RT_SCOPE_SITE: return "site"; |
2225 | case RT_SCOPE_LINK: return "link"; | ||
2226 | case RT_SCOPE_HOST: return "host"; | ||
2227 | case RT_SCOPE_NOWHERE: return "nowhere"; | ||
2228 | default: | ||
2229 | snprintf(buf, sizeof(buf), "scope=%d", s); | ||
2230 | return buf; | ||
2231 | } | ||
2232 | } | ||
2332 | 2233 | ||
2333 | for (i = 1; i <= max; i++) | 2234 | static const char *rtn_type_names[__RTN_MAX] = { |
2334 | if (stat->nodesizes[i] != 0) { | 2235 | [RTN_UNSPEC] = "UNSPEC", |
2335 | seq_printf(seq, " %d: %d", i, stat->nodesizes[i]); | 2236 | [RTN_UNICAST] = "UNICAST", |
2336 | pointers += (1<<i) * stat->nodesizes[i]; | 2237 | [RTN_LOCAL] = "LOCAL", |
2337 | } | 2238 | [RTN_BROADCAST] = "BROADCAST", |
2338 | seq_printf(seq, "\n"); | 2239 | [RTN_ANYCAST] = "ANYCAST", |
2339 | seq_printf(seq, "Pointers: %d\n", pointers); | 2240 | [RTN_MULTICAST] = "MULTICAST", |
2340 | bytes += sizeof(struct node *) * pointers; | 2241 | [RTN_BLACKHOLE] = "BLACKHOLE", |
2341 | seq_printf(seq, "Null ptrs: %d\n", stat->nullpointers); | 2242 | [RTN_UNREACHABLE] = "UNREACHABLE", |
2342 | seq_printf(seq, "Total size: %d kB\n", bytes / 1024); | 2243 | [RTN_PROHIBIT] = "PROHIBIT", |
2244 | [RTN_THROW] = "THROW", | ||
2245 | [RTN_NAT] = "NAT", | ||
2246 | [RTN_XRESOLVE] = "XRESOLVE", | ||
2247 | }; | ||
2343 | 2248 | ||
2344 | kfree(stat); | 2249 | static inline const char *rtn_type(unsigned t) |
2345 | } | 2250 | { |
2251 | static char buf[32]; | ||
2346 | 2252 | ||
2347 | #ifdef CONFIG_IP_FIB_TRIE_STATS | 2253 | if (t < __RTN_MAX && rtn_type_names[t]) |
2348 | seq_printf(seq, "Counters:\n---------\n"); | 2254 | return rtn_type_names[t]; |
2349 | seq_printf(seq,"gets = %d\n", t->stats.gets); | 2255 | snprintf(buf, sizeof(buf), "type %d", t); |
2350 | seq_printf(seq,"backtracks = %d\n", t->stats.backtrack); | 2256 | return buf; |
2351 | seq_printf(seq,"semantic match passed = %d\n", t->stats.semantic_match_passed); | ||
2352 | seq_printf(seq,"semantic match miss = %d\n", t->stats.semantic_match_miss); | ||
2353 | seq_printf(seq,"null node hit= %d\n", t->stats.null_node_hit); | ||
2354 | seq_printf(seq,"skipped node resize = %d\n", t->stats.resize_node_skipped); | ||
2355 | #ifdef CLEAR_STATS | ||
2356 | memset(&(t->stats), 0, sizeof(t->stats)); | ||
2357 | #endif | ||
2358 | #endif /* CONFIG_IP_FIB_TRIE_STATS */ | ||
2359 | } | 2257 | } |
2360 | 2258 | ||
2361 | static int fib_triestat_seq_show(struct seq_file *seq, void *v) | 2259 | /* Pretty print the trie */ |
2260 | static int fib_trie_seq_show(struct seq_file *seq, void *v) | ||
2362 | { | 2261 | { |
2363 | char bf[128]; | 2262 | const struct fib_trie_iter *iter = seq->private; |
2263 | struct node *n = v; | ||
2364 | 2264 | ||
2365 | if (v == SEQ_START_TOKEN) { | 2265 | if (v == SEQ_START_TOKEN) |
2366 | seq_printf(seq, "Basic info: size of leaf: %Zd bytes, size of tnode: %Zd bytes.\n", | 2266 | return 0; |
2367 | sizeof(struct leaf), sizeof(struct tnode)); | ||
2368 | if (trie_local) | ||
2369 | collect_and_show(trie_local, seq); | ||
2370 | 2267 | ||
2371 | if (trie_main) | 2268 | if (IS_TNODE(n)) { |
2372 | collect_and_show(trie_main, seq); | 2269 | struct tnode *tn = (struct tnode *) n; |
2373 | } else { | 2270 | t_key prf = ntohl(MASK_PFX(tn->key, tn->pos)); |
2374 | snprintf(bf, sizeof(bf), "*\t%08X\t%08X", 200, 400); | ||
2375 | 2271 | ||
2376 | seq_printf(seq, "%-127s\n", bf); | 2272 | if (!NODE_PARENT(n)) { |
2273 | if (iter->trie == trie_local) | ||
2274 | seq_puts(seq, "<local>:\n"); | ||
2275 | else | ||
2276 | seq_puts(seq, "<main>:\n"); | ||
2277 | } else { | ||
2278 | seq_indent(seq, iter->depth-1); | ||
2279 | seq_printf(seq, " +-- %d.%d.%d.%d/%d\n", | ||
2280 | NIPQUAD(prf), tn->pos); | ||
2281 | } | ||
2282 | } else { | ||
2283 | struct leaf *l = (struct leaf *) n; | ||
2284 | int i; | ||
2285 | u32 val = ntohl(l->key); | ||
2286 | |||
2287 | seq_indent(seq, iter->depth); | ||
2288 | seq_printf(seq, " |-- %d.%d.%d.%d\n", NIPQUAD(val)); | ||
2289 | for (i = 32; i >= 0; i--) { | ||
2290 | struct leaf_info *li = find_leaf_info(&l->list, i); | ||
2291 | if (li) { | ||
2292 | struct fib_alias *fa; | ||
2293 | list_for_each_entry_rcu(fa, &li->falh, fa_list) { | ||
2294 | seq_indent(seq, iter->depth+1); | ||
2295 | seq_printf(seq, " /%d %s %s", i, | ||
2296 | rtn_scope(fa->fa_scope), | ||
2297 | rtn_type(fa->fa_type)); | ||
2298 | if (fa->fa_tos) | ||
2299 | seq_printf(seq, "tos =%d\n", | ||
2300 | fa->fa_tos); | ||
2301 | seq_putc(seq, '\n'); | ||
2302 | } | ||
2303 | } | ||
2304 | } | ||
2377 | } | 2305 | } |
2306 | |||
2378 | return 0; | 2307 | return 0; |
2379 | } | 2308 | } |
2380 | 2309 | ||
2381 | static struct seq_operations fib_triestat_seq_ops = { | 2310 | static struct seq_operations fib_trie_seq_ops = { |
2382 | .start = fib_triestat_seq_start, | 2311 | .start = fib_trie_seq_start, |
2383 | .next = fib_triestat_seq_next, | 2312 | .next = fib_trie_seq_next, |
2384 | .stop = fib_triestat_seq_stop, | 2313 | .stop = fib_trie_seq_stop, |
2385 | .show = fib_triestat_seq_show, | 2314 | .show = fib_trie_seq_show, |
2386 | }; | 2315 | }; |
2387 | 2316 | ||
2388 | static int fib_triestat_seq_open(struct inode *inode, struct file *file) | 2317 | static int fib_trie_seq_open(struct inode *inode, struct file *file) |
2389 | { | 2318 | { |
2390 | struct seq_file *seq; | 2319 | struct seq_file *seq; |
2391 | int rc = -ENOMEM; | 2320 | int rc = -ENOMEM; |
2321 | struct fib_trie_iter *s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
2392 | 2322 | ||
2393 | rc = seq_open(file, &fib_triestat_seq_ops); | 2323 | if (!s) |
2324 | goto out; | ||
2325 | |||
2326 | rc = seq_open(file, &fib_trie_seq_ops); | ||
2394 | if (rc) | 2327 | if (rc) |
2395 | goto out_kfree; | 2328 | goto out_kfree; |
2396 | 2329 | ||
2397 | seq = file->private_data; | 2330 | seq = file->private_data; |
2331 | seq->private = s; | ||
2332 | memset(s, 0, sizeof(*s)); | ||
2398 | out: | 2333 | out: |
2399 | return rc; | 2334 | return rc; |
2400 | out_kfree: | 2335 | out_kfree: |
2336 | kfree(s); | ||
2401 | goto out; | 2337 | goto out; |
2402 | } | 2338 | } |
2403 | 2339 | ||
2404 | static struct file_operations fib_triestat_seq_fops = { | 2340 | static struct file_operations fib_trie_fops = { |
2405 | .owner = THIS_MODULE, | 2341 | .owner = THIS_MODULE, |
2406 | .open = fib_triestat_seq_open, | 2342 | .open = fib_trie_seq_open, |
2407 | .read = seq_read, | 2343 | .read = seq_read, |
2408 | .llseek = seq_lseek, | 2344 | .llseek = seq_lseek, |
2409 | .release = seq_release_private, | 2345 | .release = seq_release_private, |
2410 | }; | 2346 | }; |
2411 | 2347 | ||
2412 | int __init fib_stat_proc_init(void) | 2348 | static unsigned fib_flag_trans(int type, u32 mask, const struct fib_info *fi) |
2413 | { | ||
2414 | if (!proc_net_fops_create("fib_triestat", S_IRUGO, &fib_triestat_seq_fops)) | ||
2415 | return -ENOMEM; | ||
2416 | return 0; | ||
2417 | } | ||
2418 | |||
2419 | void __init fib_stat_proc_exit(void) | ||
2420 | { | 2349 | { |
2421 | proc_net_remove("fib_triestat"); | 2350 | static unsigned type2flags[RTN_MAX + 1] = { |
2422 | } | 2351 | [7] = RTF_REJECT, [8] = RTF_REJECT, |
2352 | }; | ||
2353 | unsigned flags = type2flags[type]; | ||
2423 | 2354 | ||
2424 | static struct fib_alias *fib_trie_get_first(struct seq_file *seq) | 2355 | if (fi && fi->fib_nh->nh_gw) |
2425 | { | 2356 | flags |= RTF_GATEWAY; |
2426 | return NULL; | 2357 | if (mask == 0xFFFFFFFF) |
2358 | flags |= RTF_HOST; | ||
2359 | flags |= RTF_UP; | ||
2360 | return flags; | ||
2427 | } | 2361 | } |
2428 | 2362 | ||
2429 | static struct fib_alias *fib_trie_get_next(struct seq_file *seq) | 2363 | /* |
2364 | * This outputs /proc/net/route. | ||
2365 | * The format of the file is not supposed to be changed | ||
2366 | * and needs to be same as fib_hash output to avoid breaking | ||
2367 | * legacy utilities | ||
2368 | */ | ||
2369 | static int fib_route_seq_show(struct seq_file *seq, void *v) | ||
2430 | { | 2370 | { |
2431 | return NULL; | 2371 | struct leaf *l = v; |
2432 | } | 2372 | int i; |
2373 | char bf[128]; | ||
2433 | 2374 | ||
2434 | static void *fib_trie_seq_start(struct seq_file *seq, loff_t *pos) | 2375 | if (v == SEQ_START_TOKEN) { |
2435 | { | 2376 | seq_printf(seq, "%-127s\n", "Iface\tDestination\tGateway " |
2436 | if (!ip_fib_main_table) | 2377 | "\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU" |
2437 | return NULL; | 2378 | "\tWindow\tIRTT"); |
2379 | return 0; | ||
2380 | } | ||
2438 | 2381 | ||
2439 | if (*pos) | 2382 | if (IS_TNODE(l)) |
2440 | return fib_trie_get_next(seq); | 2383 | return 0; |
2441 | else | ||
2442 | return SEQ_START_TOKEN; | ||
2443 | } | ||
2444 | 2384 | ||
2445 | static void *fib_trie_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 2385 | for (i=32; i>=0; i--) { |
2446 | { | 2386 | struct leaf_info *li = find_leaf_info(&l->list, i); |
2447 | ++*pos; | 2387 | struct fib_alias *fa; |
2448 | if (v == SEQ_START_TOKEN) | 2388 | u32 mask, prefix; |
2449 | return fib_trie_get_first(seq); | ||
2450 | else | ||
2451 | return fib_trie_get_next(seq); | ||
2452 | 2389 | ||
2453 | } | 2390 | if (!li) |
2391 | continue; | ||
2454 | 2392 | ||
2455 | static void fib_trie_seq_stop(struct seq_file *seq, void *v) | 2393 | mask = inet_make_mask(li->plen); |
2456 | { | 2394 | prefix = htonl(l->key); |
2457 | } | ||
2458 | 2395 | ||
2459 | /* | 2396 | list_for_each_entry_rcu(fa, &li->falh, fa_list) { |
2460 | * This outputs /proc/net/fib_trie. | 2397 | const struct fib_info *fi = rcu_dereference(fa->fa_info); |
2461 | * | 2398 | unsigned flags = fib_flag_trans(fa->fa_type, mask, fi); |
2462 | * It always works in backward compatibility mode. | ||
2463 | * The format of the file is not supposed to be changed. | ||
2464 | */ | ||
2465 | 2399 | ||
2466 | static int fib_trie_seq_show(struct seq_file *seq, void *v) | 2400 | if (fa->fa_type == RTN_BROADCAST |
2467 | { | 2401 | || fa->fa_type == RTN_MULTICAST) |
2468 | char bf[128]; | 2402 | continue; |
2469 | 2403 | ||
2470 | if (v == SEQ_START_TOKEN) { | 2404 | if (fi) |
2471 | if (trie_local) | 2405 | snprintf(bf, sizeof(bf), |
2472 | trie_dump_seq(seq, trie_local); | 2406 | "%s\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u", |
2407 | fi->fib_dev ? fi->fib_dev->name : "*", | ||
2408 | prefix, | ||
2409 | fi->fib_nh->nh_gw, flags, 0, 0, | ||
2410 | fi->fib_priority, | ||
2411 | mask, | ||
2412 | (fi->fib_advmss ? fi->fib_advmss + 40 : 0), | ||
2413 | fi->fib_window, | ||
2414 | fi->fib_rtt >> 3); | ||
2415 | else | ||
2416 | snprintf(bf, sizeof(bf), | ||
2417 | "*\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u", | ||
2418 | prefix, 0, flags, 0, 0, 0, | ||
2419 | mask, 0, 0, 0); | ||
2473 | 2420 | ||
2474 | if (trie_main) | 2421 | seq_printf(seq, "%-127s\n", bf); |
2475 | trie_dump_seq(seq, trie_main); | 2422 | } |
2476 | } else { | ||
2477 | snprintf(bf, sizeof(bf), | ||
2478 | "*\t%08X\t%08X", 200, 400); | ||
2479 | seq_printf(seq, "%-127s\n", bf); | ||
2480 | } | 2423 | } |
2481 | 2424 | ||
2482 | return 0; | 2425 | return 0; |
2483 | } | 2426 | } |
2484 | 2427 | ||
2485 | static struct seq_operations fib_trie_seq_ops = { | 2428 | static struct seq_operations fib_route_seq_ops = { |
2486 | .start = fib_trie_seq_start, | 2429 | .start = fib_trie_seq_start, |
2487 | .next = fib_trie_seq_next, | 2430 | .next = fib_trie_seq_next, |
2488 | .stop = fib_trie_seq_stop, | 2431 | .stop = fib_trie_seq_stop, |
2489 | .show = fib_trie_seq_show, | 2432 | .show = fib_route_seq_show, |
2490 | }; | 2433 | }; |
2491 | 2434 | ||
2492 | static int fib_trie_seq_open(struct inode *inode, struct file *file) | 2435 | static int fib_route_seq_open(struct inode *inode, struct file *file) |
2493 | { | 2436 | { |
2494 | struct seq_file *seq; | 2437 | struct seq_file *seq; |
2495 | int rc = -ENOMEM; | 2438 | int rc = -ENOMEM; |
2439 | struct fib_trie_iter *s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
2496 | 2440 | ||
2497 | rc = seq_open(file, &fib_trie_seq_ops); | 2441 | if (!s) |
2442 | goto out; | ||
2443 | |||
2444 | rc = seq_open(file, &fib_route_seq_ops); | ||
2498 | if (rc) | 2445 | if (rc) |
2499 | goto out_kfree; | 2446 | goto out_kfree; |
2500 | 2447 | ||
2501 | seq = file->private_data; | 2448 | seq = file->private_data; |
2449 | seq->private = s; | ||
2450 | memset(s, 0, sizeof(*s)); | ||
2502 | out: | 2451 | out: |
2503 | return rc; | 2452 | return rc; |
2504 | out_kfree: | 2453 | out_kfree: |
2454 | kfree(s); | ||
2505 | goto out; | 2455 | goto out; |
2506 | } | 2456 | } |
2507 | 2457 | ||
2508 | static struct file_operations fib_trie_seq_fops = { | 2458 | static struct file_operations fib_route_fops = { |
2509 | .owner = THIS_MODULE, | 2459 | .owner = THIS_MODULE, |
2510 | .open = fib_trie_seq_open, | 2460 | .open = fib_route_seq_open, |
2511 | .read = seq_read, | 2461 | .read = seq_read, |
2512 | .llseek = seq_lseek, | 2462 | .llseek = seq_lseek, |
2513 | .release= seq_release_private, | 2463 | .release = seq_release_private, |
2514 | }; | 2464 | }; |
2515 | 2465 | ||
2516 | int __init fib_proc_init(void) | 2466 | int __init fib_proc_init(void) |
2517 | { | 2467 | { |
2518 | if (!proc_net_fops_create("fib_trie", S_IRUGO, &fib_trie_seq_fops)) | 2468 | if (!proc_net_fops_create("fib_trie", S_IRUGO, &fib_trie_fops)) |
2519 | return -ENOMEM; | 2469 | goto out1; |
2470 | |||
2471 | if (!proc_net_fops_create("fib_triestat", S_IRUGO, &fib_triestat_fops)) | ||
2472 | goto out2; | ||
2473 | |||
2474 | if (!proc_net_fops_create("route", S_IRUGO, &fib_route_fops)) | ||
2475 | goto out3; | ||
2476 | |||
2520 | return 0; | 2477 | return 0; |
2478 | |||
2479 | out3: | ||
2480 | proc_net_remove("fib_triestat"); | ||
2481 | out2: | ||
2482 | proc_net_remove("fib_trie"); | ||
2483 | out1: | ||
2484 | return -ENOMEM; | ||
2521 | } | 2485 | } |
2522 | 2486 | ||
2523 | void __init fib_proc_exit(void) | 2487 | void __init fib_proc_exit(void) |
2524 | { | 2488 | { |
2525 | proc_net_remove("fib_trie"); | 2489 | proc_net_remove("fib_trie"); |
2490 | proc_net_remove("fib_triestat"); | ||
2491 | proc_net_remove("route"); | ||
2526 | } | 2492 | } |
2527 | 2493 | ||
2528 | #endif /* CONFIG_PROC_FS */ | 2494 | #endif /* CONFIG_PROC_FS */ |