u8 tos, u8 type, u32 tb_id);
 int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
                               u8 tos, u8 type, u32 tb_id);
+void netdev_switch_fib_ipv4_abort(struct fib_info *fi);
 
 #else
 
        return 0;
 }
 
+void netdev_switch_fib_ipv4_abort(struct fib_info *fi)
+{
+}
+
 #endif
 
 #endif /* _LINUX_SWITCHDEV_H_ */
 
 #include <net/tcp.h>
 #include <net/sock.h>
 #include <net/ip_fib.h>
+#include <net/switchdev.h>
 #include "fib_lookup.h"
 
 #define MAX_STAT_DEPTH 32
                        new_fa->fa_state = state & ~FA_S_ACCESSED;
                        new_fa->fa_slen = fa->fa_slen;
 
+                       err = netdev_switch_fib_ipv4_add(key, plen, fi,
+                                                        new_fa->fa_tos,
+                                                        cfg->fc_type,
+                                                        tb->tb_id);
+                       if (err) {
+                               netdev_switch_fib_ipv4_abort(fi);
+                               kmem_cache_free(fn_alias_kmem, new_fa);
+                               goto out;
+                       }
+
                        hlist_replace_rcu(&fa->fa_list, &new_fa->fa_list);
+
                        alias_free_mem_rcu(fa);
 
                        fib_release_info(fi_drop);
        new_fa->fa_state = 0;
        new_fa->fa_slen = slen;
 
+       /* (Optionally) offload fib entry to switch hardware. */
+       err = netdev_switch_fib_ipv4_add(key, plen, fi, tos,
+                                        cfg->fc_type, tb->tb_id);
+       if (err) {
+               netdev_switch_fib_ipv4_abort(fi);
+               goto out_free_new_fa;
+       }
+
        /* Insert new entry to the list. */
        err = fib_insert_alias(t, tp, l, new_fa, fa, key);
        if (err)
-               goto out_free_new_fa;
+               goto out_sw_fib_del;
 
        if (!plen)
                tb->tb_num_default++;
 succeeded:
        return 0;
 
+out_sw_fib_del:
+       netdev_switch_fib_ipv4_del(key, plen, fi, tos, cfg->fc_type, tb->tb_id);
 out_free_new_fa:
        kmem_cache_free(fn_alias_kmem, new_fa);
 out:
        if (!fa_to_delete)
                return -ESRCH;
 
+       netdev_switch_fib_ipv4_del(key, plen, fa_to_delete->fa_info, tos,
+                                  cfg->fc_type, tb->tb_id);
+
        rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id,
                  &cfg->fc_nlinfo, 0);
 
                struct fib_info *fi = fa->fa_info;
 
                if (fi && (fi->fib_flags & RTNH_F_DEAD)) {
+                       netdev_switch_fib_ipv4_del(n->key,
+                                                  KEYLENGTH - fa->fa_slen,
+                                                  fi, fa->fa_tos,
+                                                  fa->fa_type, tb->tb_id);
                        hlist_del_rcu(&fa->fa_list);
                        fib_release_info(fa->fa_info);
                        alias_free_mem_rcu(fa);
 
        const struct net_device_ops *ops;
        int err = 0;
 
-       /* Don't offload route if using custom ip rules */
-       if (fi->fib_net->ipv4.fib_has_custom_rules)
+       /* Don't offload route if using custom ip rules or if
+        * IPv4 FIB offloading has been disabled completely.
+        */
+
+       if (fi->fib_net->ipv4.fib_has_custom_rules |
+           fi->fib_net->ipv4.fib_offload_disabled)
                return 0;
 
        dev = netdev_switch_get_dev_by_nhs(fi);
        return err;
 }
 EXPORT_SYMBOL(netdev_switch_fib_ipv4_del);
+
+/**
+ *     netdev_switch_fib_ipv4_abort - Abort an IPv4 FIB operation
+ *
+ *     @fi: route FIB info structure
+ */
+void netdev_switch_fib_ipv4_abort(struct fib_info *fi)
+{
+       /* There was a problem installing this route to the offload
+        * device.  For now, until we come up with more refined
+        * policy handling, abruptly end IPv4 fib offloading for
+        * for entire net by flushing offload device(s) of all
+        * IPv4 routes, and mark IPv4 fib offloading broken from
+        * this point forward.
+        */
+
+       fib_flush_external(fi->fib_net);
+       fi->fib_net->ipv4.fib_offload_disabled = true;
+}
+EXPORT_SYMBOL(netdev_switch_fib_ipv4_abort);