diff options
author | Alexander Duyck <alexander.h.duyck@redhat.com> | 2014-12-31 13:56:24 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-12-31 18:25:54 -0500 |
commit | 345e9b54268ae065520a7252c182d22ef4591718 (patch) | |
tree | ad4e6c4138028f1ad00d6a2aeeb7fff90f094801 | |
parent | 98293e8d2f51976d5f790400859a517c0f7cb598 (diff) |
fib_trie: Push rcu_read_lock/unlock to callers
This change is to start cleaning up some of the rcu_read_lock/unlock
handling. I realized while reviewing the code there are several spots that
I don't believe are being handled correctly or are masking warnings by
locally calling rcu_read_lock/unlock instead of calling them at the correct
level.
A common example is a call to fib_get_table followed by fib_table_lookup.
The rcu_read_lock/unlock ought to wrap both but there are several spots where
they were not wrapped.
Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/ip_fib.h | 50 | ||||
-rw-r--r-- | net/ipv4/fib_frontend.c | 27 | ||||
-rw-r--r-- | net/ipv4/fib_rules.c | 22 | ||||
-rw-r--r-- | net/ipv4/fib_trie.c | 137 |
4 files changed, 114 insertions, 122 deletions
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 09a819ee2151..5bd120e4bc0a 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h | |||
@@ -222,16 +222,19 @@ static inline struct fib_table *fib_new_table(struct net *net, u32 id) | |||
222 | static inline int fib_lookup(struct net *net, const struct flowi4 *flp, | 222 | static inline int fib_lookup(struct net *net, const struct flowi4 *flp, |
223 | struct fib_result *res) | 223 | struct fib_result *res) |
224 | { | 224 | { |
225 | struct fib_table *table; | 225 | int err = -ENETUNREACH; |
226 | |||
227 | rcu_read_lock(); | ||
228 | |||
229 | if (!fib_table_lookup(fib_get_table(net, RT_TABLE_LOCAL), flp, res, | ||
230 | FIB_LOOKUP_NOREF) || | ||
231 | !fib_table_lookup(fib_get_table(net, RT_TABLE_MAIN), flp, res, | ||
232 | FIB_LOOKUP_NOREF)) | ||
233 | err = 0; | ||
226 | 234 | ||
227 | table = fib_get_table(net, RT_TABLE_LOCAL); | 235 | rcu_read_unlock(); |
228 | if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF)) | ||
229 | return 0; | ||
230 | 236 | ||
231 | table = fib_get_table(net, RT_TABLE_MAIN); | 237 | return err; |
232 | if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF)) | ||
233 | return 0; | ||
234 | return -ENETUNREACH; | ||
235 | } | 238 | } |
236 | 239 | ||
237 | #else /* CONFIG_IP_MULTIPLE_TABLES */ | 240 | #else /* CONFIG_IP_MULTIPLE_TABLES */ |
@@ -247,20 +250,25 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp, | |||
247 | struct fib_result *res) | 250 | struct fib_result *res) |
248 | { | 251 | { |
249 | if (!net->ipv4.fib_has_custom_rules) { | 252 | if (!net->ipv4.fib_has_custom_rules) { |
253 | int err = -ENETUNREACH; | ||
254 | |||
255 | rcu_read_lock(); | ||
256 | |||
250 | res->tclassid = 0; | 257 | res->tclassid = 0; |
251 | if (net->ipv4.fib_local && | 258 | if ((net->ipv4.fib_local && |
252 | !fib_table_lookup(net->ipv4.fib_local, flp, res, | 259 | !fib_table_lookup(net->ipv4.fib_local, flp, res, |
253 | FIB_LOOKUP_NOREF)) | 260 | FIB_LOOKUP_NOREF)) || |
254 | return 0; | 261 | (net->ipv4.fib_main && |
255 | if (net->ipv4.fib_main && | 262 | !fib_table_lookup(net->ipv4.fib_main, flp, res, |
256 | !fib_table_lookup(net->ipv4.fib_main, flp, res, | 263 | FIB_LOOKUP_NOREF)) || |
257 | FIB_LOOKUP_NOREF)) | 264 | (net->ipv4.fib_default && |
258 | return 0; | 265 | !fib_table_lookup(net->ipv4.fib_default, flp, res, |
259 | if (net->ipv4.fib_default && | 266 | FIB_LOOKUP_NOREF))) |
260 | !fib_table_lookup(net->ipv4.fib_default, flp, res, | 267 | err = 0; |
261 | FIB_LOOKUP_NOREF)) | 268 | |
262 | return 0; | 269 | rcu_read_unlock(); |
263 | return -ENETUNREACH; | 270 | |
271 | return err; | ||
264 | } | 272 | } |
265 | return __fib_lookup(net, flp, res); | 273 | return __fib_lookup(net, flp, res); |
266 | } | 274 | } |
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 66890209208f..57be71dd6a9e 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c | |||
@@ -109,6 +109,7 @@ struct fib_table *fib_new_table(struct net *net, u32 id) | |||
109 | return tb; | 109 | return tb; |
110 | } | 110 | } |
111 | 111 | ||
112 | /* caller must hold either rtnl or rcu read lock */ | ||
112 | struct fib_table *fib_get_table(struct net *net, u32 id) | 113 | struct fib_table *fib_get_table(struct net *net, u32 id) |
113 | { | 114 | { |
114 | struct fib_table *tb; | 115 | struct fib_table *tb; |
@@ -119,15 +120,11 @@ struct fib_table *fib_get_table(struct net *net, u32 id) | |||
119 | id = RT_TABLE_MAIN; | 120 | id = RT_TABLE_MAIN; |
120 | h = id & (FIB_TABLE_HASHSZ - 1); | 121 | h = id & (FIB_TABLE_HASHSZ - 1); |
121 | 122 | ||
122 | rcu_read_lock(); | ||
123 | head = &net->ipv4.fib_table_hash[h]; | 123 | head = &net->ipv4.fib_table_hash[h]; |
124 | hlist_for_each_entry_rcu(tb, head, tb_hlist) { | 124 | hlist_for_each_entry_rcu(tb, head, tb_hlist) { |
125 | if (tb->tb_id == id) { | 125 | if (tb->tb_id == id) |
126 | rcu_read_unlock(); | ||
127 | return tb; | 126 | return tb; |
128 | } | ||
129 | } | 127 | } |
130 | rcu_read_unlock(); | ||
131 | return NULL; | 128 | return NULL; |
132 | } | 129 | } |
133 | #endif /* CONFIG_IP_MULTIPLE_TABLES */ | 130 | #endif /* CONFIG_IP_MULTIPLE_TABLES */ |
@@ -167,16 +164,18 @@ static inline unsigned int __inet_dev_addr_type(struct net *net, | |||
167 | if (ipv4_is_multicast(addr)) | 164 | if (ipv4_is_multicast(addr)) |
168 | return RTN_MULTICAST; | 165 | return RTN_MULTICAST; |
169 | 166 | ||
167 | rcu_read_lock(); | ||
168 | |||
170 | local_table = fib_get_table(net, RT_TABLE_LOCAL); | 169 | local_table = fib_get_table(net, RT_TABLE_LOCAL); |
171 | if (local_table) { | 170 | if (local_table) { |
172 | ret = RTN_UNICAST; | 171 | ret = RTN_UNICAST; |
173 | rcu_read_lock(); | ||
174 | if (!fib_table_lookup(local_table, &fl4, &res, FIB_LOOKUP_NOREF)) { | 172 | if (!fib_table_lookup(local_table, &fl4, &res, FIB_LOOKUP_NOREF)) { |
175 | if (!dev || dev == res.fi->fib_dev) | 173 | if (!dev || dev == res.fi->fib_dev) |
176 | ret = res.type; | 174 | ret = res.type; |
177 | } | 175 | } |
178 | rcu_read_unlock(); | ||
179 | } | 176 | } |
177 | |||
178 | rcu_read_unlock(); | ||
180 | return ret; | 179 | return ret; |
181 | } | 180 | } |
182 | 181 | ||
@@ -919,7 +918,7 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim) | |||
919 | #undef BRD1_OK | 918 | #undef BRD1_OK |
920 | } | 919 | } |
921 | 920 | ||
922 | static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb) | 921 | static void nl_fib_lookup(struct net *net, struct fib_result_nl *frn) |
923 | { | 922 | { |
924 | 923 | ||
925 | struct fib_result res; | 924 | struct fib_result res; |
@@ -929,6 +928,11 @@ static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb) | |||
929 | .flowi4_tos = frn->fl_tos, | 928 | .flowi4_tos = frn->fl_tos, |
930 | .flowi4_scope = frn->fl_scope, | 929 | .flowi4_scope = frn->fl_scope, |
931 | }; | 930 | }; |
931 | struct fib_table *tb; | ||
932 | |||
933 | rcu_read_lock(); | ||
934 | |||
935 | tb = fib_get_table(net, frn->tb_id_in); | ||
932 | 936 | ||
933 | frn->err = -ENOENT; | 937 | frn->err = -ENOENT; |
934 | if (tb) { | 938 | if (tb) { |
@@ -945,6 +949,8 @@ static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb) | |||
945 | } | 949 | } |
946 | local_bh_enable(); | 950 | local_bh_enable(); |
947 | } | 951 | } |
952 | |||
953 | rcu_read_unlock(); | ||
948 | } | 954 | } |
949 | 955 | ||
950 | static void nl_fib_input(struct sk_buff *skb) | 956 | static void nl_fib_input(struct sk_buff *skb) |
@@ -952,7 +958,6 @@ static void nl_fib_input(struct sk_buff *skb) | |||
952 | struct net *net; | 958 | struct net *net; |
953 | struct fib_result_nl *frn; | 959 | struct fib_result_nl *frn; |
954 | struct nlmsghdr *nlh; | 960 | struct nlmsghdr *nlh; |
955 | struct fib_table *tb; | ||
956 | u32 portid; | 961 | u32 portid; |
957 | 962 | ||
958 | net = sock_net(skb->sk); | 963 | net = sock_net(skb->sk); |
@@ -967,9 +972,7 @@ static void nl_fib_input(struct sk_buff *skb) | |||
967 | nlh = nlmsg_hdr(skb); | 972 | nlh = nlmsg_hdr(skb); |
968 | 973 | ||
969 | frn = (struct fib_result_nl *) nlmsg_data(nlh); | 974 | frn = (struct fib_result_nl *) nlmsg_data(nlh); |
970 | tb = fib_get_table(net, frn->tb_id_in); | 975 | nl_fib_lookup(net, frn); |
971 | |||
972 | nl_fib_lookup(frn, tb); | ||
973 | 976 | ||
974 | portid = NETLINK_CB(skb).portid; /* netlink portid */ | 977 | portid = NETLINK_CB(skb).portid; /* netlink portid */ |
975 | NETLINK_CB(skb).portid = 0; /* from kernel */ | 978 | NETLINK_CB(skb).portid = 0; /* from kernel */ |
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 8f7bd56955b0..d3db718be51d 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c | |||
@@ -81,27 +81,25 @@ static int fib4_rule_action(struct fib_rule *rule, struct flowi *flp, | |||
81 | break; | 81 | break; |
82 | 82 | ||
83 | case FR_ACT_UNREACHABLE: | 83 | case FR_ACT_UNREACHABLE: |
84 | err = -ENETUNREACH; | 84 | return -ENETUNREACH; |
85 | goto errout; | ||
86 | 85 | ||
87 | case FR_ACT_PROHIBIT: | 86 | case FR_ACT_PROHIBIT: |
88 | err = -EACCES; | 87 | return -EACCES; |
89 | goto errout; | ||
90 | 88 | ||
91 | case FR_ACT_BLACKHOLE: | 89 | case FR_ACT_BLACKHOLE: |
92 | default: | 90 | default: |
93 | err = -EINVAL; | 91 | return -EINVAL; |
94 | goto errout; | ||
95 | } | 92 | } |
96 | 93 | ||
94 | rcu_read_lock(); | ||
95 | |||
97 | tbl = fib_get_table(rule->fr_net, rule->table); | 96 | tbl = fib_get_table(rule->fr_net, rule->table); |
98 | if (!tbl) | 97 | if (tbl) |
99 | goto errout; | 98 | err = fib_table_lookup(tbl, &flp->u.ip4, |
99 | (struct fib_result *)arg->result, | ||
100 | arg->flags); | ||
100 | 101 | ||
101 | err = fib_table_lookup(tbl, &flp->u.ip4, (struct fib_result *) arg->result, arg->flags); | 102 | rcu_read_unlock(); |
102 | if (err > 0) | ||
103 | err = -EAGAIN; | ||
104 | errout: | ||
105 | return err; | 103 | return err; |
106 | } | 104 | } |
107 | 105 | ||
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 28a3065470bc..987b06d1effe 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c | |||
@@ -1181,72 +1181,6 @@ err: | |||
1181 | return err; | 1181 | return err; |
1182 | } | 1182 | } |
1183 | 1183 | ||
1184 | /* should be called with rcu_read_lock */ | ||
1185 | static int check_leaf(struct fib_table *tb, struct trie *t, struct tnode *l, | ||
1186 | t_key key, const struct flowi4 *flp, | ||
1187 | struct fib_result *res, int fib_flags) | ||
1188 | { | ||
1189 | struct leaf_info *li; | ||
1190 | struct hlist_head *hhead = &l->list; | ||
1191 | |||
1192 | hlist_for_each_entry_rcu(li, hhead, hlist) { | ||
1193 | struct fib_alias *fa; | ||
1194 | |||
1195 | if (l->key != (key & li->mask_plen)) | ||
1196 | continue; | ||
1197 | |||
1198 | list_for_each_entry_rcu(fa, &li->falh, fa_list) { | ||
1199 | struct fib_info *fi = fa->fa_info; | ||
1200 | int nhsel, err; | ||
1201 | |||
1202 | if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos) | ||
1203 | continue; | ||
1204 | if (fi->fib_dead) | ||
1205 | continue; | ||
1206 | if (fa->fa_info->fib_scope < flp->flowi4_scope) | ||
1207 | continue; | ||
1208 | fib_alias_accessed(fa); | ||
1209 | err = fib_props[fa->fa_type].error; | ||
1210 | if (unlikely(err < 0)) { | ||
1211 | #ifdef CONFIG_IP_FIB_TRIE_STATS | ||
1212 | this_cpu_inc(t->stats->semantic_match_passed); | ||
1213 | #endif | ||
1214 | return err; | ||
1215 | } | ||
1216 | if (fi->fib_flags & RTNH_F_DEAD) | ||
1217 | continue; | ||
1218 | for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) { | ||
1219 | const struct fib_nh *nh = &fi->fib_nh[nhsel]; | ||
1220 | |||
1221 | if (nh->nh_flags & RTNH_F_DEAD) | ||
1222 | continue; | ||
1223 | if (flp->flowi4_oif && flp->flowi4_oif != nh->nh_oif) | ||
1224 | continue; | ||
1225 | |||
1226 | #ifdef CONFIG_IP_FIB_TRIE_STATS | ||
1227 | this_cpu_inc(t->stats->semantic_match_passed); | ||
1228 | #endif | ||
1229 | res->prefixlen = li->plen; | ||
1230 | res->nh_sel = nhsel; | ||
1231 | res->type = fa->fa_type; | ||
1232 | res->scope = fi->fib_scope; | ||
1233 | res->fi = fi; | ||
1234 | res->table = tb; | ||
1235 | res->fa_head = &li->falh; | ||
1236 | if (!(fib_flags & FIB_LOOKUP_NOREF)) | ||
1237 | atomic_inc(&fi->fib_clntref); | ||
1238 | return 0; | ||
1239 | } | ||
1240 | } | ||
1241 | |||
1242 | #ifdef CONFIG_IP_FIB_TRIE_STATS | ||
1243 | this_cpu_inc(t->stats->semantic_match_miss); | ||
1244 | #endif | ||
1245 | } | ||
1246 | |||
1247 | return 1; | ||
1248 | } | ||
1249 | |||
1250 | static inline t_key prefix_mismatch(t_key key, struct tnode *n) | 1184 | static inline t_key prefix_mismatch(t_key key, struct tnode *n) |
1251 | { | 1185 | { |
1252 | t_key prefix = n->key; | 1186 | t_key prefix = n->key; |
@@ -1254,6 +1188,7 @@ static inline t_key prefix_mismatch(t_key key, struct tnode *n) | |||
1254 | return (key ^ prefix) & (prefix | -prefix); | 1188 | return (key ^ prefix) & (prefix | -prefix); |
1255 | } | 1189 | } |
1256 | 1190 | ||
1191 | /* should be called with rcu_read_lock */ | ||
1257 | int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, | 1192 | int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, |
1258 | struct fib_result *res, int fib_flags) | 1193 | struct fib_result *res, int fib_flags) |
1259 | { | 1194 | { |
@@ -1263,14 +1198,12 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, | |||
1263 | #endif | 1198 | #endif |
1264 | const t_key key = ntohl(flp->daddr); | 1199 | const t_key key = ntohl(flp->daddr); |
1265 | struct tnode *n, *pn; | 1200 | struct tnode *n, *pn; |
1201 | struct leaf_info *li; | ||
1266 | t_key cindex; | 1202 | t_key cindex; |
1267 | int ret = 1; | ||
1268 | |||
1269 | rcu_read_lock(); | ||
1270 | 1203 | ||
1271 | n = rcu_dereference(t->trie); | 1204 | n = rcu_dereference(t->trie); |
1272 | if (!n) | 1205 | if (!n) |
1273 | goto failed; | 1206 | return -EAGAIN; |
1274 | 1207 | ||
1275 | #ifdef CONFIG_IP_FIB_TRIE_STATS | 1208 | #ifdef CONFIG_IP_FIB_TRIE_STATS |
1276 | this_cpu_inc(stats->gets); | 1209 | this_cpu_inc(stats->gets); |
@@ -1350,7 +1283,7 @@ backtrace: | |||
1350 | 1283 | ||
1351 | pn = node_parent_rcu(pn); | 1284 | pn = node_parent_rcu(pn); |
1352 | if (unlikely(!pn)) | 1285 | if (unlikely(!pn)) |
1353 | goto failed; | 1286 | return -EAGAIN; |
1354 | #ifdef CONFIG_IP_FIB_TRIE_STATS | 1287 | #ifdef CONFIG_IP_FIB_TRIE_STATS |
1355 | this_cpu_inc(stats->backtrack); | 1288 | this_cpu_inc(stats->backtrack); |
1356 | #endif | 1289 | #endif |
@@ -1368,12 +1301,62 @@ backtrace: | |||
1368 | 1301 | ||
1369 | found: | 1302 | found: |
1370 | /* Step 3: Process the leaf, if that fails fall back to backtracing */ | 1303 | /* Step 3: Process the leaf, if that fails fall back to backtracing */ |
1371 | ret = check_leaf(tb, t, n, key, flp, res, fib_flags); | 1304 | hlist_for_each_entry_rcu(li, &n->list, hlist) { |
1372 | if (unlikely(ret > 0)) | 1305 | struct fib_alias *fa; |
1373 | goto backtrace; | 1306 | |
1374 | failed: | 1307 | if ((key ^ n->key) & li->mask_plen) |
1375 | rcu_read_unlock(); | 1308 | continue; |
1376 | return ret; | 1309 | |
1310 | list_for_each_entry_rcu(fa, &li->falh, fa_list) { | ||
1311 | struct fib_info *fi = fa->fa_info; | ||
1312 | int nhsel, err; | ||
1313 | |||
1314 | if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos) | ||
1315 | continue; | ||
1316 | if (fi->fib_dead) | ||
1317 | continue; | ||
1318 | if (fa->fa_info->fib_scope < flp->flowi4_scope) | ||
1319 | continue; | ||
1320 | fib_alias_accessed(fa); | ||
1321 | err = fib_props[fa->fa_type].error; | ||
1322 | if (unlikely(err < 0)) { | ||
1323 | #ifdef CONFIG_IP_FIB_TRIE_STATS | ||
1324 | this_cpu_inc(stats->semantic_match_passed); | ||
1325 | #endif | ||
1326 | return err; | ||
1327 | } | ||
1328 | if (fi->fib_flags & RTNH_F_DEAD) | ||
1329 | continue; | ||
1330 | for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) { | ||
1331 | const struct fib_nh *nh = &fi->fib_nh[nhsel]; | ||
1332 | |||
1333 | if (nh->nh_flags & RTNH_F_DEAD) | ||
1334 | continue; | ||
1335 | if (flp->flowi4_oif && flp->flowi4_oif != nh->nh_oif) | ||
1336 | continue; | ||
1337 | |||
1338 | if (!(fib_flags & FIB_LOOKUP_NOREF)) | ||
1339 | atomic_inc(&fi->fib_clntref); | ||
1340 | |||
1341 | res->prefixlen = li->plen; | ||
1342 | res->nh_sel = nhsel; | ||
1343 | res->type = fa->fa_type; | ||
1344 | res->scope = fi->fib_scope; | ||
1345 | res->fi = fi; | ||
1346 | res->table = tb; | ||
1347 | res->fa_head = &li->falh; | ||
1348 | #ifdef CONFIG_IP_FIB_TRIE_STATS | ||
1349 | this_cpu_inc(stats->semantic_match_passed); | ||
1350 | #endif | ||
1351 | return err; | ||
1352 | } | ||
1353 | } | ||
1354 | |||
1355 | #ifdef CONFIG_IP_FIB_TRIE_STATS | ||
1356 | this_cpu_inc(stats->semantic_match_miss); | ||
1357 | #endif | ||
1358 | } | ||
1359 | goto backtrace; | ||
1377 | } | 1360 | } |
1378 | EXPORT_SYMBOL_GPL(fib_table_lookup); | 1361 | EXPORT_SYMBOL_GPL(fib_table_lookup); |
1379 | 1362 | ||