aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorScott Feldman <sfeldma@gmail.com>2015-03-06 00:21:19 -0500
committerDavid S. Miller <davem@davemloft.net>2015-03-06 00:24:58 -0500
commit8e05fd7166c6123334b7a739a697d677747aa462 (patch)
tree92a66ecee7e1ff94f0314cd32e4fd65e2fd12741
parent448b128a14501543748514a4f9adedd3c0da2e85 (diff)
fib: hook IPv4 fib for hardware offload
Call into the switchdev driver any time an IPv4 fib entry is added/modified/deleted from the kernel's FIB. The switchdev driver may or may not install the route to the offload device. In the case where the driver tries to install the route and something goes wrong (device's routing table is full, etc), then all of the offloaded routes will be flushed from the device, route forwarding falls back to the kernel, and no more routes are offloading. We can refine this logic later. For now, use the simplist model of offloading routes up to the point of failure, and then on failure, undo everything and mark IPv4 offloading disabled. Signed-off-by: Scott Feldman <sfeldma@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/switchdev.h5
-rw-r--r--net/ipv4/fib_trie.c31
-rw-r--r--net/switchdev/switchdev.c28
3 files changed, 61 insertions, 3 deletions
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 8d2ac663325a..dc0a5cc7c2c5 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -55,6 +55,7 @@ int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
55 u8 tos, u8 type, u32 tb_id); 55 u8 tos, u8 type, u32 tb_id);
56int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, 56int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
57 u8 tos, u8 type, u32 tb_id); 57 u8 tos, u8 type, u32 tb_id);
58void netdev_switch_fib_ipv4_abort(struct fib_info *fi);
58 59
59#else 60#else
60 61
@@ -128,6 +129,10 @@ static inline int netdev_switch_fib_ipv4_del(u32 dst, int dst_len,
128 return 0; 129 return 0;
129} 130}
130 131
132void netdev_switch_fib_ipv4_abort(struct fib_info *fi)
133{
134}
135
131#endif 136#endif
132 137
133#endif /* _LINUX_SWITCHDEV_H_ */ 138#endif /* _LINUX_SWITCHDEV_H_ */
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 2de43956c9d0..6544f1a0cfa1 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -79,6 +79,7 @@
79#include <net/tcp.h> 79#include <net/tcp.h>
80#include <net/sock.h> 80#include <net/sock.h>
81#include <net/ip_fib.h> 81#include <net/ip_fib.h>
82#include <net/switchdev.h>
82#include "fib_lookup.h" 83#include "fib_lookup.h"
83 84
84#define MAX_STAT_DEPTH 32 85#define MAX_STAT_DEPTH 32
@@ -1135,7 +1136,18 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
1135 new_fa->fa_state = state & ~FA_S_ACCESSED; 1136 new_fa->fa_state = state & ~FA_S_ACCESSED;
1136 new_fa->fa_slen = fa->fa_slen; 1137 new_fa->fa_slen = fa->fa_slen;
1137 1138
1139 err = netdev_switch_fib_ipv4_add(key, plen, fi,
1140 new_fa->fa_tos,
1141 cfg->fc_type,
1142 tb->tb_id);
1143 if (err) {
1144 netdev_switch_fib_ipv4_abort(fi);
1145 kmem_cache_free(fn_alias_kmem, new_fa);
1146 goto out;
1147 }
1148
1138 hlist_replace_rcu(&fa->fa_list, &new_fa->fa_list); 1149 hlist_replace_rcu(&fa->fa_list, &new_fa->fa_list);
1150
1139 alias_free_mem_rcu(fa); 1151 alias_free_mem_rcu(fa);
1140 1152
1141 fib_release_info(fi_drop); 1153 fib_release_info(fi_drop);
@@ -1171,10 +1183,18 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
1171 new_fa->fa_state = 0; 1183 new_fa->fa_state = 0;
1172 new_fa->fa_slen = slen; 1184 new_fa->fa_slen = slen;
1173 1185
1186 /* (Optionally) offload fib entry to switch hardware. */
1187 err = netdev_switch_fib_ipv4_add(key, plen, fi, tos,
1188 cfg->fc_type, tb->tb_id);
1189 if (err) {
1190 netdev_switch_fib_ipv4_abort(fi);
1191 goto out_free_new_fa;
1192 }
1193
1174 /* Insert new entry to the list. */ 1194 /* Insert new entry to the list. */
1175 err = fib_insert_alias(t, tp, l, new_fa, fa, key); 1195 err = fib_insert_alias(t, tp, l, new_fa, fa, key);
1176 if (err) 1196 if (err)
1177 goto out_free_new_fa; 1197 goto out_sw_fib_del;
1178 1198
1179 if (!plen) 1199 if (!plen)
1180 tb->tb_num_default++; 1200 tb->tb_num_default++;
@@ -1185,6 +1205,8 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
1185succeeded: 1205succeeded:
1186 return 0; 1206 return 0;
1187 1207
1208out_sw_fib_del:
1209 netdev_switch_fib_ipv4_del(key, plen, fi, tos, cfg->fc_type, tb->tb_id);
1188out_free_new_fa: 1210out_free_new_fa:
1189 kmem_cache_free(fn_alias_kmem, new_fa); 1211 kmem_cache_free(fn_alias_kmem, new_fa);
1190out: 1212out:
@@ -1456,6 +1478,9 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
1456 if (!fa_to_delete) 1478 if (!fa_to_delete)
1457 return -ESRCH; 1479 return -ESRCH;
1458 1480
1481 netdev_switch_fib_ipv4_del(key, plen, fa_to_delete->fa_info, tos,
1482 cfg->fc_type, tb->tb_id);
1483
1459 rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id, 1484 rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id,
1460 &cfg->fc_nlinfo, 0); 1485 &cfg->fc_nlinfo, 0);
1461 1486
@@ -1650,6 +1675,10 @@ backtrace:
1650 struct fib_info *fi = fa->fa_info; 1675 struct fib_info *fi = fa->fa_info;
1651 1676
1652 if (fi && (fi->fib_flags & RTNH_F_DEAD)) { 1677 if (fi && (fi->fib_flags & RTNH_F_DEAD)) {
1678 netdev_switch_fib_ipv4_del(n->key,
1679 KEYLENGTH - fa->fa_slen,
1680 fi, fa->fa_tos,
1681 fa->fa_type, tb->tb_id);
1653 hlist_del_rcu(&fa->fa_list); 1682 hlist_del_rcu(&fa->fa_list);
1654 fib_release_info(fa->fa_info); 1683 fib_release_info(fa->fa_info);
1655 alias_free_mem_rcu(fa); 1684 alias_free_mem_rcu(fa);
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index 99907d829419..f4fd575aa2a3 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -305,8 +305,12 @@ int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
305 const struct net_device_ops *ops; 305 const struct net_device_ops *ops;
306 int err = 0; 306 int err = 0;
307 307
308 /* Don't offload route if using custom ip rules */ 308 /* Don't offload route if using custom ip rules or if
309 if (fi->fib_net->ipv4.fib_has_custom_rules) 309 * IPv4 FIB offloading has been disabled completely.
310 */
311
312 if (fi->fib_net->ipv4.fib_has_custom_rules |
313 fi->fib_net->ipv4.fib_offload_disabled)
310 return 0; 314 return 0;
311 315
312 dev = netdev_switch_get_dev_by_nhs(fi); 316 dev = netdev_switch_get_dev_by_nhs(fi);
@@ -362,3 +366,23 @@ int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
362 return err; 366 return err;
363} 367}
364EXPORT_SYMBOL(netdev_switch_fib_ipv4_del); 368EXPORT_SYMBOL(netdev_switch_fib_ipv4_del);
369
370/**
371 * netdev_switch_fib_ipv4_abort - Abort an IPv4 FIB operation
372 *
373 * @fi: route FIB info structure
374 */
375void netdev_switch_fib_ipv4_abort(struct fib_info *fi)
376{
377 /* There was a problem installing this route to the offload
378 * device. For now, until we come up with more refined
379 * policy handling, abruptly end IPv4 fib offloading for
380 * for entire net by flushing offload device(s) of all
381 * IPv4 routes, and mark IPv4 fib offloading broken from
382 * this point forward.
383 */
384
385 fib_flush_external(fi->fib_net);
386 fi->fib_net->ipv4.fib_offload_disabled = true;
387}
388EXPORT_SYMBOL(netdev_switch_fib_ipv4_abort);