1007f790cSJiri Pirko /* 2007f790cSJiri Pirko * net/switchdev/switchdev.c - Switch device API 3007f790cSJiri Pirko * Copyright (c) 2014 Jiri Pirko <[email protected]> 4007f790cSJiri Pirko * 5007f790cSJiri Pirko * This program is free software; you can redistribute it and/or modify 6007f790cSJiri Pirko * it under the terms of the GNU General Public License as published by 7007f790cSJiri Pirko * the Free Software Foundation; either version 2 of the License, or 8007f790cSJiri Pirko * (at your option) any later version. 9007f790cSJiri Pirko */ 10007f790cSJiri Pirko 11007f790cSJiri Pirko #include <linux/kernel.h> 12007f790cSJiri Pirko #include <linux/types.h> 13007f790cSJiri Pirko #include <linux/init.h> 1403bf0c28SJiri Pirko #include <linux/mutex.h> 1503bf0c28SJiri Pirko #include <linux/notifier.h> 16007f790cSJiri Pirko #include <linux/netdevice.h> 175e8d9049SScott Feldman #include <net/ip_fib.h> 18007f790cSJiri Pirko #include <net/switchdev.h> 19007f790cSJiri Pirko 20007f790cSJiri Pirko /** 21007f790cSJiri Pirko * netdev_switch_parent_id_get - Get ID of a switch 22007f790cSJiri Pirko * @dev: port device 23007f790cSJiri Pirko * @psid: switch ID 24007f790cSJiri Pirko * 25007f790cSJiri Pirko * Get ID of a switch this port is part of. 26007f790cSJiri Pirko */ 27007f790cSJiri Pirko int netdev_switch_parent_id_get(struct net_device *dev, 28007f790cSJiri Pirko struct netdev_phys_item_id *psid) 29007f790cSJiri Pirko { 30007f790cSJiri Pirko const struct net_device_ops *ops = dev->netdev_ops; 31007f790cSJiri Pirko 32007f790cSJiri Pirko if (!ops->ndo_switch_parent_id_get) 33007f790cSJiri Pirko return -EOPNOTSUPP; 34007f790cSJiri Pirko return ops->ndo_switch_parent_id_get(dev, psid); 35007f790cSJiri Pirko } 36007f790cSJiri Pirko EXPORT_SYMBOL(netdev_switch_parent_id_get); 3738dcf357SScott Feldman 3838dcf357SScott Feldman /** 3938dcf357SScott Feldman * netdev_switch_port_stp_update - Notify switch device port of STP 4038dcf357SScott Feldman * state change 4138dcf357SScott Feldman * @dev: port device 4238dcf357SScott Feldman * @state: port STP state 4338dcf357SScott Feldman * 4438dcf357SScott Feldman * Notify switch device port of bridge port STP state change. 4538dcf357SScott Feldman */ 4638dcf357SScott Feldman int netdev_switch_port_stp_update(struct net_device *dev, u8 state) 4738dcf357SScott Feldman { 4838dcf357SScott Feldman const struct net_device_ops *ops = dev->netdev_ops; 4938dcf357SScott Feldman 5038dcf357SScott Feldman if (!ops->ndo_switch_port_stp_update) 5138dcf357SScott Feldman return -EOPNOTSUPP; 5238dcf357SScott Feldman WARN_ON(!ops->ndo_switch_parent_id_get); 5338dcf357SScott Feldman return ops->ndo_switch_port_stp_update(dev, state); 5438dcf357SScott Feldman } 5538dcf357SScott Feldman EXPORT_SYMBOL(netdev_switch_port_stp_update); 5603bf0c28SJiri Pirko 5703bf0c28SJiri Pirko static DEFINE_MUTEX(netdev_switch_mutex); 5803bf0c28SJiri Pirko static RAW_NOTIFIER_HEAD(netdev_switch_notif_chain); 5903bf0c28SJiri Pirko 6003bf0c28SJiri Pirko /** 6103bf0c28SJiri Pirko * register_netdev_switch_notifier - Register nofifier 6203bf0c28SJiri Pirko * @nb: notifier_block 6303bf0c28SJiri Pirko * 6403bf0c28SJiri Pirko * Register switch device notifier. This should be used by code 6503bf0c28SJiri Pirko * which needs to monitor events happening in particular device. 6603bf0c28SJiri Pirko * Return values are same as for atomic_notifier_chain_register(). 6703bf0c28SJiri Pirko */ 6803bf0c28SJiri Pirko int register_netdev_switch_notifier(struct notifier_block *nb) 6903bf0c28SJiri Pirko { 7003bf0c28SJiri Pirko int err; 7103bf0c28SJiri Pirko 7203bf0c28SJiri Pirko mutex_lock(&netdev_switch_mutex); 7303bf0c28SJiri Pirko err = raw_notifier_chain_register(&netdev_switch_notif_chain, nb); 7403bf0c28SJiri Pirko mutex_unlock(&netdev_switch_mutex); 7503bf0c28SJiri Pirko return err; 7603bf0c28SJiri Pirko } 7703bf0c28SJiri Pirko EXPORT_SYMBOL(register_netdev_switch_notifier); 7803bf0c28SJiri Pirko 7903bf0c28SJiri Pirko /** 8003bf0c28SJiri Pirko * unregister_netdev_switch_notifier - Unregister nofifier 8103bf0c28SJiri Pirko * @nb: notifier_block 8203bf0c28SJiri Pirko * 8303bf0c28SJiri Pirko * Unregister switch device notifier. 8403bf0c28SJiri Pirko * Return values are same as for atomic_notifier_chain_unregister(). 8503bf0c28SJiri Pirko */ 8603bf0c28SJiri Pirko int unregister_netdev_switch_notifier(struct notifier_block *nb) 8703bf0c28SJiri Pirko { 8803bf0c28SJiri Pirko int err; 8903bf0c28SJiri Pirko 9003bf0c28SJiri Pirko mutex_lock(&netdev_switch_mutex); 9103bf0c28SJiri Pirko err = raw_notifier_chain_unregister(&netdev_switch_notif_chain, nb); 9203bf0c28SJiri Pirko mutex_unlock(&netdev_switch_mutex); 9303bf0c28SJiri Pirko return err; 9403bf0c28SJiri Pirko } 9503bf0c28SJiri Pirko EXPORT_SYMBOL(unregister_netdev_switch_notifier); 9603bf0c28SJiri Pirko 9703bf0c28SJiri Pirko /** 9803bf0c28SJiri Pirko * call_netdev_switch_notifiers - Call nofifiers 9903bf0c28SJiri Pirko * @val: value passed unmodified to notifier function 10003bf0c28SJiri Pirko * @dev: port device 10103bf0c28SJiri Pirko * @info: notifier information data 10203bf0c28SJiri Pirko * 10303bf0c28SJiri Pirko * Call all network notifier blocks. This should be called by driver 10403bf0c28SJiri Pirko * when it needs to propagate hardware event. 10503bf0c28SJiri Pirko * Return values are same as for atomic_notifier_call_chain(). 10603bf0c28SJiri Pirko */ 10703bf0c28SJiri Pirko int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev, 10803bf0c28SJiri Pirko struct netdev_switch_notifier_info *info) 10903bf0c28SJiri Pirko { 11003bf0c28SJiri Pirko int err; 11103bf0c28SJiri Pirko 11203bf0c28SJiri Pirko info->dev = dev; 11303bf0c28SJiri Pirko mutex_lock(&netdev_switch_mutex); 11403bf0c28SJiri Pirko err = raw_notifier_call_chain(&netdev_switch_notif_chain, val, info); 11503bf0c28SJiri Pirko mutex_unlock(&netdev_switch_mutex); 11603bf0c28SJiri Pirko return err; 11703bf0c28SJiri Pirko } 11803bf0c28SJiri Pirko EXPORT_SYMBOL(call_netdev_switch_notifiers); 1198a44dbb2SRoopa Prabhu 1208a44dbb2SRoopa Prabhu /** 1218a44dbb2SRoopa Prabhu * netdev_switch_port_bridge_setlink - Notify switch device port of bridge 1228a44dbb2SRoopa Prabhu * port attributes 1238a44dbb2SRoopa Prabhu * 1248a44dbb2SRoopa Prabhu * @dev: port device 1258a44dbb2SRoopa Prabhu * @nlh: netlink msg with bridge port attributes 1268a44dbb2SRoopa Prabhu * @flags: bridge setlink flags 1278a44dbb2SRoopa Prabhu * 1288a44dbb2SRoopa Prabhu * Notify switch device port of bridge port attributes 1298a44dbb2SRoopa Prabhu */ 1308a44dbb2SRoopa Prabhu int netdev_switch_port_bridge_setlink(struct net_device *dev, 1318a44dbb2SRoopa Prabhu struct nlmsghdr *nlh, u16 flags) 1328a44dbb2SRoopa Prabhu { 1338a44dbb2SRoopa Prabhu const struct net_device_ops *ops = dev->netdev_ops; 1348a44dbb2SRoopa Prabhu 1358a44dbb2SRoopa Prabhu if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) 1368a44dbb2SRoopa Prabhu return 0; 1378a44dbb2SRoopa Prabhu 1388a44dbb2SRoopa Prabhu if (!ops->ndo_bridge_setlink) 1398a44dbb2SRoopa Prabhu return -EOPNOTSUPP; 1408a44dbb2SRoopa Prabhu 1418a44dbb2SRoopa Prabhu return ops->ndo_bridge_setlink(dev, nlh, flags); 1428a44dbb2SRoopa Prabhu } 1438a44dbb2SRoopa Prabhu EXPORT_SYMBOL(netdev_switch_port_bridge_setlink); 1448a44dbb2SRoopa Prabhu 1458a44dbb2SRoopa Prabhu /** 1468a44dbb2SRoopa Prabhu * netdev_switch_port_bridge_dellink - Notify switch device port of bridge 1478a44dbb2SRoopa Prabhu * port attribute delete 1488a44dbb2SRoopa Prabhu * 1498a44dbb2SRoopa Prabhu * @dev: port device 1508a44dbb2SRoopa Prabhu * @nlh: netlink msg with bridge port attributes 1518a44dbb2SRoopa Prabhu * @flags: bridge setlink flags 1528a44dbb2SRoopa Prabhu * 1538a44dbb2SRoopa Prabhu * Notify switch device port of bridge port attribute delete 1548a44dbb2SRoopa Prabhu */ 1558a44dbb2SRoopa Prabhu int netdev_switch_port_bridge_dellink(struct net_device *dev, 1568a44dbb2SRoopa Prabhu struct nlmsghdr *nlh, u16 flags) 1578a44dbb2SRoopa Prabhu { 1588a44dbb2SRoopa Prabhu const struct net_device_ops *ops = dev->netdev_ops; 1598a44dbb2SRoopa Prabhu 1608a44dbb2SRoopa Prabhu if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) 1618a44dbb2SRoopa Prabhu return 0; 1628a44dbb2SRoopa Prabhu 1638a44dbb2SRoopa Prabhu if (!ops->ndo_bridge_dellink) 1648a44dbb2SRoopa Prabhu return -EOPNOTSUPP; 1658a44dbb2SRoopa Prabhu 1668a44dbb2SRoopa Prabhu return ops->ndo_bridge_dellink(dev, nlh, flags); 1678a44dbb2SRoopa Prabhu } 1688a44dbb2SRoopa Prabhu EXPORT_SYMBOL(netdev_switch_port_bridge_dellink); 1698a44dbb2SRoopa Prabhu 1708a44dbb2SRoopa Prabhu /** 1718a44dbb2SRoopa Prabhu * ndo_dflt_netdev_switch_port_bridge_setlink - default ndo bridge setlink 1728a44dbb2SRoopa Prabhu * op for master devices 1738a44dbb2SRoopa Prabhu * 1748a44dbb2SRoopa Prabhu * @dev: port device 1758a44dbb2SRoopa Prabhu * @nlh: netlink msg with bridge port attributes 1768a44dbb2SRoopa Prabhu * @flags: bridge setlink flags 1778a44dbb2SRoopa Prabhu * 1788a44dbb2SRoopa Prabhu * Notify master device slaves of bridge port attributes 1798a44dbb2SRoopa Prabhu */ 1808a44dbb2SRoopa Prabhu int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev, 1818a44dbb2SRoopa Prabhu struct nlmsghdr *nlh, u16 flags) 1828a44dbb2SRoopa Prabhu { 1838a44dbb2SRoopa Prabhu struct net_device *lower_dev; 1848a44dbb2SRoopa Prabhu struct list_head *iter; 1858a44dbb2SRoopa Prabhu int ret = 0, err = 0; 1868a44dbb2SRoopa Prabhu 1878a44dbb2SRoopa Prabhu if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) 1888a44dbb2SRoopa Prabhu return ret; 1898a44dbb2SRoopa Prabhu 1908a44dbb2SRoopa Prabhu netdev_for_each_lower_dev(dev, lower_dev, iter) { 1918a44dbb2SRoopa Prabhu err = netdev_switch_port_bridge_setlink(lower_dev, nlh, flags); 1928a44dbb2SRoopa Prabhu if (err && err != -EOPNOTSUPP) 1938a44dbb2SRoopa Prabhu ret = err; 1948a44dbb2SRoopa Prabhu } 1958a44dbb2SRoopa Prabhu 1968a44dbb2SRoopa Prabhu return ret; 1978a44dbb2SRoopa Prabhu } 1988a44dbb2SRoopa Prabhu EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_setlink); 1998a44dbb2SRoopa Prabhu 2008a44dbb2SRoopa Prabhu /** 2018a44dbb2SRoopa Prabhu * ndo_dflt_netdev_switch_port_bridge_dellink - default ndo bridge dellink 2028a44dbb2SRoopa Prabhu * op for master devices 2038a44dbb2SRoopa Prabhu * 2048a44dbb2SRoopa Prabhu * @dev: port device 2058a44dbb2SRoopa Prabhu * @nlh: netlink msg with bridge port attributes 2068a44dbb2SRoopa Prabhu * @flags: bridge dellink flags 2078a44dbb2SRoopa Prabhu * 2088a44dbb2SRoopa Prabhu * Notify master device slaves of bridge port attribute deletes 2098a44dbb2SRoopa Prabhu */ 2108a44dbb2SRoopa Prabhu int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev, 2118a44dbb2SRoopa Prabhu struct nlmsghdr *nlh, u16 flags) 2128a44dbb2SRoopa Prabhu { 2138a44dbb2SRoopa Prabhu struct net_device *lower_dev; 2148a44dbb2SRoopa Prabhu struct list_head *iter; 2158a44dbb2SRoopa Prabhu int ret = 0, err = 0; 2168a44dbb2SRoopa Prabhu 2178a44dbb2SRoopa Prabhu if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) 2188a44dbb2SRoopa Prabhu return ret; 2198a44dbb2SRoopa Prabhu 2208a44dbb2SRoopa Prabhu netdev_for_each_lower_dev(dev, lower_dev, iter) { 2218a44dbb2SRoopa Prabhu err = netdev_switch_port_bridge_dellink(lower_dev, nlh, flags); 2228a44dbb2SRoopa Prabhu if (err && err != -EOPNOTSUPP) 2238a44dbb2SRoopa Prabhu ret = err; 2248a44dbb2SRoopa Prabhu } 2258a44dbb2SRoopa Prabhu 2268a44dbb2SRoopa Prabhu return ret; 2278a44dbb2SRoopa Prabhu } 2288a44dbb2SRoopa Prabhu EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_dellink); 2295e8d9049SScott Feldman 230b5d6fbdeSScott Feldman static struct net_device *netdev_switch_get_lowest_dev(struct net_device *dev) 231b5d6fbdeSScott Feldman { 232b5d6fbdeSScott Feldman const struct net_device_ops *ops = dev->netdev_ops; 233b5d6fbdeSScott Feldman struct net_device *lower_dev; 234b5d6fbdeSScott Feldman struct net_device *port_dev; 235b5d6fbdeSScott Feldman struct list_head *iter; 236b5d6fbdeSScott Feldman 237b5d6fbdeSScott Feldman /* Recusively search down until we find a sw port dev. 238b5d6fbdeSScott Feldman * (A sw port dev supports ndo_switch_parent_id_get). 239b5d6fbdeSScott Feldman */ 240b5d6fbdeSScott Feldman 241b5d6fbdeSScott Feldman if (dev->features & NETIF_F_HW_SWITCH_OFFLOAD && 242b5d6fbdeSScott Feldman ops->ndo_switch_parent_id_get) 243b5d6fbdeSScott Feldman return dev; 244b5d6fbdeSScott Feldman 245b5d6fbdeSScott Feldman netdev_for_each_lower_dev(dev, lower_dev, iter) { 246b5d6fbdeSScott Feldman port_dev = netdev_switch_get_lowest_dev(lower_dev); 247b5d6fbdeSScott Feldman if (port_dev) 248b5d6fbdeSScott Feldman return port_dev; 249b5d6fbdeSScott Feldman } 250b5d6fbdeSScott Feldman 251b5d6fbdeSScott Feldman return NULL; 252b5d6fbdeSScott Feldman } 253b5d6fbdeSScott Feldman 254b5d6fbdeSScott Feldman static struct net_device *netdev_switch_get_dev_by_nhs(struct fib_info *fi) 255b5d6fbdeSScott Feldman { 256b5d6fbdeSScott Feldman struct netdev_phys_item_id psid; 257b5d6fbdeSScott Feldman struct netdev_phys_item_id prev_psid; 258b5d6fbdeSScott Feldman struct net_device *dev = NULL; 259b5d6fbdeSScott Feldman int nhsel; 260b5d6fbdeSScott Feldman 261b5d6fbdeSScott Feldman /* For this route, all nexthop devs must be on the same switch. */ 262b5d6fbdeSScott Feldman 263b5d6fbdeSScott Feldman for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) { 264b5d6fbdeSScott Feldman const struct fib_nh *nh = &fi->fib_nh[nhsel]; 265b5d6fbdeSScott Feldman 266b5d6fbdeSScott Feldman if (!nh->nh_dev) 267b5d6fbdeSScott Feldman return NULL; 268b5d6fbdeSScott Feldman 269b5d6fbdeSScott Feldman dev = netdev_switch_get_lowest_dev(nh->nh_dev); 270b5d6fbdeSScott Feldman if (!dev) 271b5d6fbdeSScott Feldman return NULL; 272b5d6fbdeSScott Feldman 273b5d6fbdeSScott Feldman if (netdev_switch_parent_id_get(dev, &psid)) 274b5d6fbdeSScott Feldman return NULL; 275b5d6fbdeSScott Feldman 276b5d6fbdeSScott Feldman if (nhsel > 0) { 277b5d6fbdeSScott Feldman if (prev_psid.id_len != psid.id_len) 278b5d6fbdeSScott Feldman return NULL; 279b5d6fbdeSScott Feldman if (memcmp(prev_psid.id, psid.id, psid.id_len)) 280b5d6fbdeSScott Feldman return NULL; 281b5d6fbdeSScott Feldman } 282b5d6fbdeSScott Feldman 283b5d6fbdeSScott Feldman prev_psid = psid; 284b5d6fbdeSScott Feldman } 285b5d6fbdeSScott Feldman 286b5d6fbdeSScott Feldman return dev; 287b5d6fbdeSScott Feldman } 288b5d6fbdeSScott Feldman 2895e8d9049SScott Feldman /** 2905e8d9049SScott Feldman * netdev_switch_fib_ipv4_add - Add IPv4 route entry to switch 2915e8d9049SScott Feldman * 2925e8d9049SScott Feldman * @dst: route's IPv4 destination address 2935e8d9049SScott Feldman * @dst_len: destination address length (prefix length) 2945e8d9049SScott Feldman * @fi: route FIB info structure 2955e8d9049SScott Feldman * @tos: route TOS 2965e8d9049SScott Feldman * @type: route type 2975e8d9049SScott Feldman * @tb_id: route table ID 2985e8d9049SScott Feldman * 2995e8d9049SScott Feldman * Add IPv4 route entry to switch device. 3005e8d9049SScott Feldman */ 3015e8d9049SScott Feldman int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, 3025e8d9049SScott Feldman u8 tos, u8 type, u32 tb_id) 3035e8d9049SScott Feldman { 304b5d6fbdeSScott Feldman struct net_device *dev; 305b5d6fbdeSScott Feldman const struct net_device_ops *ops; 306b5d6fbdeSScott Feldman int err = 0; 307b5d6fbdeSScott Feldman 308*8e05fd71SScott Feldman /* Don't offload route if using custom ip rules or if 309*8e05fd71SScott Feldman * IPv4 FIB offloading has been disabled completely. 310*8e05fd71SScott Feldman */ 311*8e05fd71SScott Feldman 312*8e05fd71SScott Feldman if (fi->fib_net->ipv4.fib_has_custom_rules | 313*8e05fd71SScott Feldman fi->fib_net->ipv4.fib_offload_disabled) 314104616e7SScott Feldman return 0; 315104616e7SScott Feldman 316b5d6fbdeSScott Feldman dev = netdev_switch_get_dev_by_nhs(fi); 317b5d6fbdeSScott Feldman if (!dev) 3185e8d9049SScott Feldman return 0; 319b5d6fbdeSScott Feldman ops = dev->netdev_ops; 320b5d6fbdeSScott Feldman 321b5d6fbdeSScott Feldman if (ops->ndo_switch_fib_ipv4_add) { 322b5d6fbdeSScott Feldman err = ops->ndo_switch_fib_ipv4_add(dev, htonl(dst), dst_len, 323b5d6fbdeSScott Feldman fi, tos, type, tb_id); 324b5d6fbdeSScott Feldman if (!err) 325b5d6fbdeSScott Feldman fi->fib_flags |= RTNH_F_EXTERNAL; 326b5d6fbdeSScott Feldman } 327b5d6fbdeSScott Feldman 328b5d6fbdeSScott Feldman return err; 3295e8d9049SScott Feldman } 3305e8d9049SScott Feldman EXPORT_SYMBOL(netdev_switch_fib_ipv4_add); 3315e8d9049SScott Feldman 3325e8d9049SScott Feldman /** 3335e8d9049SScott Feldman * netdev_switch_fib_ipv4_del - Delete IPv4 route entry from switch 3345e8d9049SScott Feldman * 3355e8d9049SScott Feldman * @dst: route's IPv4 destination address 3365e8d9049SScott Feldman * @dst_len: destination address length (prefix length) 3375e8d9049SScott Feldman * @fi: route FIB info structure 3385e8d9049SScott Feldman * @tos: route TOS 3395e8d9049SScott Feldman * @type: route type 3405e8d9049SScott Feldman * @tb_id: route table ID 3415e8d9049SScott Feldman * 3425e8d9049SScott Feldman * Delete IPv4 route entry from switch device. 3435e8d9049SScott Feldman */ 3445e8d9049SScott Feldman int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, 3455e8d9049SScott Feldman u8 tos, u8 type, u32 tb_id) 3465e8d9049SScott Feldman { 347b5d6fbdeSScott Feldman struct net_device *dev; 348b5d6fbdeSScott Feldman const struct net_device_ops *ops; 349b5d6fbdeSScott Feldman int err = 0; 350b5d6fbdeSScott Feldman 351b5d6fbdeSScott Feldman if (!(fi->fib_flags & RTNH_F_EXTERNAL)) 3525e8d9049SScott Feldman return 0; 353b5d6fbdeSScott Feldman 354b5d6fbdeSScott Feldman dev = netdev_switch_get_dev_by_nhs(fi); 355b5d6fbdeSScott Feldman if (!dev) 356b5d6fbdeSScott Feldman return 0; 357b5d6fbdeSScott Feldman ops = dev->netdev_ops; 358b5d6fbdeSScott Feldman 359b5d6fbdeSScott Feldman if (ops->ndo_switch_fib_ipv4_del) { 360b5d6fbdeSScott Feldman err = ops->ndo_switch_fib_ipv4_del(dev, htonl(dst), dst_len, 361b5d6fbdeSScott Feldman fi, tos, type, tb_id); 362b5d6fbdeSScott Feldman if (!err) 363b5d6fbdeSScott Feldman fi->fib_flags &= ~RTNH_F_EXTERNAL; 364b5d6fbdeSScott Feldman } 365b5d6fbdeSScott Feldman 366b5d6fbdeSScott Feldman return err; 3675e8d9049SScott Feldman } 3685e8d9049SScott Feldman EXPORT_SYMBOL(netdev_switch_fib_ipv4_del); 369*8e05fd71SScott Feldman 370*8e05fd71SScott Feldman /** 371*8e05fd71SScott Feldman * netdev_switch_fib_ipv4_abort - Abort an IPv4 FIB operation 372*8e05fd71SScott Feldman * 373*8e05fd71SScott Feldman * @fi: route FIB info structure 374*8e05fd71SScott Feldman */ 375*8e05fd71SScott Feldman void netdev_switch_fib_ipv4_abort(struct fib_info *fi) 376*8e05fd71SScott Feldman { 377*8e05fd71SScott Feldman /* There was a problem installing this route to the offload 378*8e05fd71SScott Feldman * device. For now, until we come up with more refined 379*8e05fd71SScott Feldman * policy handling, abruptly end IPv4 fib offloading for 380*8e05fd71SScott Feldman * for entire net by flushing offload device(s) of all 381*8e05fd71SScott Feldman * IPv4 routes, and mark IPv4 fib offloading broken from 382*8e05fd71SScott Feldman * this point forward. 383*8e05fd71SScott Feldman */ 384*8e05fd71SScott Feldman 385*8e05fd71SScott Feldman fib_flush_external(fi->fib_net); 386*8e05fd71SScott Feldman fi->fib_net->ipv4.fib_offload_disabled = true; 387*8e05fd71SScott Feldman } 388*8e05fd71SScott Feldman EXPORT_SYMBOL(netdev_switch_fib_ipv4_abort); 389