1007f790cSJiri Pirko /* 2007f790cSJiri Pirko * net/switchdev/switchdev.c - Switch device API 3007f790cSJiri Pirko * Copyright (c) 2014 Jiri Pirko <[email protected]> 4f8f21471SScott Feldman * Copyright (c) 2014-2015 Scott Feldman <[email protected]> 5007f790cSJiri Pirko * 6007f790cSJiri Pirko * This program is free software; you can redistribute it and/or modify 7007f790cSJiri Pirko * it under the terms of the GNU General Public License as published by 8007f790cSJiri Pirko * the Free Software Foundation; either version 2 of the License, or 9007f790cSJiri Pirko * (at your option) any later version. 10007f790cSJiri Pirko */ 11007f790cSJiri Pirko 12007f790cSJiri Pirko #include <linux/kernel.h> 13007f790cSJiri Pirko #include <linux/types.h> 14007f790cSJiri Pirko #include <linux/init.h> 1503bf0c28SJiri Pirko #include <linux/mutex.h> 1603bf0c28SJiri Pirko #include <linux/notifier.h> 17007f790cSJiri Pirko #include <linux/netdevice.h> 185e8d9049SScott Feldman #include <net/ip_fib.h> 19007f790cSJiri Pirko #include <net/switchdev.h> 20007f790cSJiri Pirko 21007f790cSJiri Pirko /** 22ebb9a03aSJiri Pirko * switchdev_parent_id_get - Get ID of a switch 23007f790cSJiri Pirko * @dev: port device 24007f790cSJiri Pirko * @psid: switch ID 25007f790cSJiri Pirko * 26007f790cSJiri Pirko * Get ID of a switch this port is part of. 27007f790cSJiri Pirko */ 28ebb9a03aSJiri Pirko int switchdev_parent_id_get(struct net_device *dev, 29007f790cSJiri Pirko struct netdev_phys_item_id *psid) 30007f790cSJiri Pirko { 31*9d47c0a2SJiri Pirko const struct switchdev_ops *ops = dev->switchdev_ops; 32007f790cSJiri Pirko 33*9d47c0a2SJiri Pirko if (!ops || !ops->switchdev_parent_id_get) 34007f790cSJiri Pirko return -EOPNOTSUPP; 35*9d47c0a2SJiri Pirko return ops->switchdev_parent_id_get(dev, psid); 36007f790cSJiri Pirko } 37ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_parent_id_get); 3838dcf357SScott Feldman 3938dcf357SScott Feldman /** 40ebb9a03aSJiri Pirko * switchdev_port_stp_update - Notify switch device port of STP 4138dcf357SScott Feldman * state change 4238dcf357SScott Feldman * @dev: port device 4338dcf357SScott Feldman * @state: port STP state 4438dcf357SScott Feldman * 4538dcf357SScott Feldman * Notify switch device port of bridge port STP state change. 4638dcf357SScott Feldman */ 47ebb9a03aSJiri Pirko int switchdev_port_stp_update(struct net_device *dev, u8 state) 4838dcf357SScott Feldman { 49*9d47c0a2SJiri Pirko const struct switchdev_ops *ops = dev->switchdev_ops; 50558d51faSRoopa Prabhu struct net_device *lower_dev; 51558d51faSRoopa Prabhu struct list_head *iter; 52558d51faSRoopa Prabhu int err = -EOPNOTSUPP; 5338dcf357SScott Feldman 54*9d47c0a2SJiri Pirko if (ops && ops->switchdev_port_stp_update) 55*9d47c0a2SJiri Pirko return ops->switchdev_port_stp_update(dev, state); 56558d51faSRoopa Prabhu 57558d51faSRoopa Prabhu netdev_for_each_lower_dev(dev, lower_dev, iter) { 58ebb9a03aSJiri Pirko err = switchdev_port_stp_update(lower_dev, state); 59558d51faSRoopa Prabhu if (err && err != -EOPNOTSUPP) 60558d51faSRoopa Prabhu return err; 61558d51faSRoopa Prabhu } 62558d51faSRoopa Prabhu 63558d51faSRoopa Prabhu return err; 6438dcf357SScott Feldman } 65ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_stp_update); 6603bf0c28SJiri Pirko 67ebb9a03aSJiri Pirko static DEFINE_MUTEX(switchdev_mutex); 68ebb9a03aSJiri Pirko static RAW_NOTIFIER_HEAD(switchdev_notif_chain); 6903bf0c28SJiri Pirko 7003bf0c28SJiri Pirko /** 71ebb9a03aSJiri Pirko * register_switchdev_notifier - Register notifier 7203bf0c28SJiri Pirko * @nb: notifier_block 7303bf0c28SJiri Pirko * 7403bf0c28SJiri Pirko * Register switch device notifier. This should be used by code 7503bf0c28SJiri Pirko * which needs to monitor events happening in particular device. 7603bf0c28SJiri Pirko * Return values are same as for atomic_notifier_chain_register(). 7703bf0c28SJiri Pirko */ 78ebb9a03aSJiri Pirko int register_switchdev_notifier(struct notifier_block *nb) 7903bf0c28SJiri Pirko { 8003bf0c28SJiri Pirko int err; 8103bf0c28SJiri Pirko 82ebb9a03aSJiri Pirko mutex_lock(&switchdev_mutex); 83ebb9a03aSJiri Pirko err = raw_notifier_chain_register(&switchdev_notif_chain, nb); 84ebb9a03aSJiri Pirko mutex_unlock(&switchdev_mutex); 8503bf0c28SJiri Pirko return err; 8603bf0c28SJiri Pirko } 87ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(register_switchdev_notifier); 8803bf0c28SJiri Pirko 8903bf0c28SJiri Pirko /** 90ebb9a03aSJiri Pirko * unregister_switchdev_notifier - Unregister notifier 9103bf0c28SJiri Pirko * @nb: notifier_block 9203bf0c28SJiri Pirko * 9303bf0c28SJiri Pirko * Unregister switch device notifier. 9403bf0c28SJiri Pirko * Return values are same as for atomic_notifier_chain_unregister(). 9503bf0c28SJiri Pirko */ 96ebb9a03aSJiri Pirko int unregister_switchdev_notifier(struct notifier_block *nb) 9703bf0c28SJiri Pirko { 9803bf0c28SJiri Pirko int err; 9903bf0c28SJiri Pirko 100ebb9a03aSJiri Pirko mutex_lock(&switchdev_mutex); 101ebb9a03aSJiri Pirko err = raw_notifier_chain_unregister(&switchdev_notif_chain, nb); 102ebb9a03aSJiri Pirko mutex_unlock(&switchdev_mutex); 10303bf0c28SJiri Pirko return err; 10403bf0c28SJiri Pirko } 105ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(unregister_switchdev_notifier); 10603bf0c28SJiri Pirko 10703bf0c28SJiri Pirko /** 108ebb9a03aSJiri Pirko * call_switchdev_notifiers - Call notifiers 10903bf0c28SJiri Pirko * @val: value passed unmodified to notifier function 11003bf0c28SJiri Pirko * @dev: port device 11103bf0c28SJiri Pirko * @info: notifier information data 11203bf0c28SJiri Pirko * 11303bf0c28SJiri Pirko * Call all network notifier blocks. This should be called by driver 11403bf0c28SJiri Pirko * when it needs to propagate hardware event. 11503bf0c28SJiri Pirko * Return values are same as for atomic_notifier_call_chain(). 11603bf0c28SJiri Pirko */ 117ebb9a03aSJiri Pirko int call_switchdev_notifiers(unsigned long val, struct net_device *dev, 118ebb9a03aSJiri Pirko struct switchdev_notifier_info *info) 11903bf0c28SJiri Pirko { 12003bf0c28SJiri Pirko int err; 12103bf0c28SJiri Pirko 12203bf0c28SJiri Pirko info->dev = dev; 123ebb9a03aSJiri Pirko mutex_lock(&switchdev_mutex); 124ebb9a03aSJiri Pirko err = raw_notifier_call_chain(&switchdev_notif_chain, val, info); 125ebb9a03aSJiri Pirko mutex_unlock(&switchdev_mutex); 12603bf0c28SJiri Pirko return err; 12703bf0c28SJiri Pirko } 128ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(call_switchdev_notifiers); 1298a44dbb2SRoopa Prabhu 1308a44dbb2SRoopa Prabhu /** 131ebb9a03aSJiri Pirko * switchdev_port_bridge_setlink - Notify switch device port of bridge 1328a44dbb2SRoopa Prabhu * port attributes 1338a44dbb2SRoopa Prabhu * 1348a44dbb2SRoopa Prabhu * @dev: port device 1358a44dbb2SRoopa Prabhu * @nlh: netlink msg with bridge port attributes 1368a44dbb2SRoopa Prabhu * @flags: bridge setlink flags 1378a44dbb2SRoopa Prabhu * 1388a44dbb2SRoopa Prabhu * Notify switch device port of bridge port attributes 1398a44dbb2SRoopa Prabhu */ 140ebb9a03aSJiri Pirko int switchdev_port_bridge_setlink(struct net_device *dev, 1418a44dbb2SRoopa Prabhu struct nlmsghdr *nlh, u16 flags) 1428a44dbb2SRoopa Prabhu { 1438a44dbb2SRoopa Prabhu const struct net_device_ops *ops = dev->netdev_ops; 1448a44dbb2SRoopa Prabhu 1458a44dbb2SRoopa Prabhu if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) 1468a44dbb2SRoopa Prabhu return 0; 1478a44dbb2SRoopa Prabhu 1488a44dbb2SRoopa Prabhu if (!ops->ndo_bridge_setlink) 1498a44dbb2SRoopa Prabhu return -EOPNOTSUPP; 1508a44dbb2SRoopa Prabhu 1518a44dbb2SRoopa Prabhu return ops->ndo_bridge_setlink(dev, nlh, flags); 1528a44dbb2SRoopa Prabhu } 153ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink); 1548a44dbb2SRoopa Prabhu 1558a44dbb2SRoopa Prabhu /** 156ebb9a03aSJiri Pirko * switchdev_port_bridge_dellink - Notify switch device port of bridge 1578a44dbb2SRoopa Prabhu * port attribute delete 1588a44dbb2SRoopa Prabhu * 1598a44dbb2SRoopa Prabhu * @dev: port device 1608a44dbb2SRoopa Prabhu * @nlh: netlink msg with bridge port attributes 1618a44dbb2SRoopa Prabhu * @flags: bridge setlink flags 1628a44dbb2SRoopa Prabhu * 1638a44dbb2SRoopa Prabhu * Notify switch device port of bridge port attribute delete 1648a44dbb2SRoopa Prabhu */ 165ebb9a03aSJiri Pirko int switchdev_port_bridge_dellink(struct net_device *dev, 1668a44dbb2SRoopa Prabhu struct nlmsghdr *nlh, u16 flags) 1678a44dbb2SRoopa Prabhu { 1688a44dbb2SRoopa Prabhu const struct net_device_ops *ops = dev->netdev_ops; 1698a44dbb2SRoopa Prabhu 1708a44dbb2SRoopa Prabhu if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) 1718a44dbb2SRoopa Prabhu return 0; 1728a44dbb2SRoopa Prabhu 1738a44dbb2SRoopa Prabhu if (!ops->ndo_bridge_dellink) 1748a44dbb2SRoopa Prabhu return -EOPNOTSUPP; 1758a44dbb2SRoopa Prabhu 1768a44dbb2SRoopa Prabhu return ops->ndo_bridge_dellink(dev, nlh, flags); 1778a44dbb2SRoopa Prabhu } 178ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_bridge_dellink); 1798a44dbb2SRoopa Prabhu 1808a44dbb2SRoopa Prabhu /** 181ebb9a03aSJiri Pirko * ndo_dflt_switchdev_port_bridge_setlink - default ndo bridge setlink 1828a44dbb2SRoopa Prabhu * op for master devices 1838a44dbb2SRoopa Prabhu * 1848a44dbb2SRoopa Prabhu * @dev: port device 1858a44dbb2SRoopa Prabhu * @nlh: netlink msg with bridge port attributes 1868a44dbb2SRoopa Prabhu * @flags: bridge setlink flags 1878a44dbb2SRoopa Prabhu * 1888a44dbb2SRoopa Prabhu * Notify master device slaves of bridge port attributes 1898a44dbb2SRoopa Prabhu */ 190ebb9a03aSJiri Pirko int ndo_dflt_switchdev_port_bridge_setlink(struct net_device *dev, 1918a44dbb2SRoopa Prabhu struct nlmsghdr *nlh, u16 flags) 1928a44dbb2SRoopa Prabhu { 1938a44dbb2SRoopa Prabhu struct net_device *lower_dev; 1948a44dbb2SRoopa Prabhu struct list_head *iter; 1958a44dbb2SRoopa Prabhu int ret = 0, err = 0; 1968a44dbb2SRoopa Prabhu 1978a44dbb2SRoopa Prabhu if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) 1988a44dbb2SRoopa Prabhu return ret; 1998a44dbb2SRoopa Prabhu 2008a44dbb2SRoopa Prabhu netdev_for_each_lower_dev(dev, lower_dev, iter) { 201ebb9a03aSJiri Pirko err = switchdev_port_bridge_setlink(lower_dev, nlh, flags); 2028a44dbb2SRoopa Prabhu if (err && err != -EOPNOTSUPP) 2038a44dbb2SRoopa Prabhu ret = err; 2048a44dbb2SRoopa Prabhu } 2058a44dbb2SRoopa Prabhu 2068a44dbb2SRoopa Prabhu return ret; 2078a44dbb2SRoopa Prabhu } 208ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(ndo_dflt_switchdev_port_bridge_setlink); 2098a44dbb2SRoopa Prabhu 2108a44dbb2SRoopa Prabhu /** 211ebb9a03aSJiri Pirko * ndo_dflt_switchdev_port_bridge_dellink - default ndo bridge dellink 2128a44dbb2SRoopa Prabhu * op for master devices 2138a44dbb2SRoopa Prabhu * 2148a44dbb2SRoopa Prabhu * @dev: port device 2158a44dbb2SRoopa Prabhu * @nlh: netlink msg with bridge port attributes 2168a44dbb2SRoopa Prabhu * @flags: bridge dellink flags 2178a44dbb2SRoopa Prabhu * 2188a44dbb2SRoopa Prabhu * Notify master device slaves of bridge port attribute deletes 2198a44dbb2SRoopa Prabhu */ 220ebb9a03aSJiri Pirko int ndo_dflt_switchdev_port_bridge_dellink(struct net_device *dev, 2218a44dbb2SRoopa Prabhu struct nlmsghdr *nlh, u16 flags) 2228a44dbb2SRoopa Prabhu { 2238a44dbb2SRoopa Prabhu struct net_device *lower_dev; 2248a44dbb2SRoopa Prabhu struct list_head *iter; 2258a44dbb2SRoopa Prabhu int ret = 0, err = 0; 2268a44dbb2SRoopa Prabhu 2278a44dbb2SRoopa Prabhu if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) 2288a44dbb2SRoopa Prabhu return ret; 2298a44dbb2SRoopa Prabhu 2308a44dbb2SRoopa Prabhu netdev_for_each_lower_dev(dev, lower_dev, iter) { 231ebb9a03aSJiri Pirko err = switchdev_port_bridge_dellink(lower_dev, nlh, flags); 2328a44dbb2SRoopa Prabhu if (err && err != -EOPNOTSUPP) 2338a44dbb2SRoopa Prabhu ret = err; 2348a44dbb2SRoopa Prabhu } 2358a44dbb2SRoopa Prabhu 2368a44dbb2SRoopa Prabhu return ret; 2378a44dbb2SRoopa Prabhu } 238ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(ndo_dflt_switchdev_port_bridge_dellink); 2395e8d9049SScott Feldman 240ebb9a03aSJiri Pirko static struct net_device *switchdev_get_lowest_dev(struct net_device *dev) 241b5d6fbdeSScott Feldman { 242*9d47c0a2SJiri Pirko const struct switchdev_ops *ops = dev->switchdev_ops; 243b5d6fbdeSScott Feldman struct net_device *lower_dev; 244b5d6fbdeSScott Feldman struct net_device *port_dev; 245b5d6fbdeSScott Feldman struct list_head *iter; 246b5d6fbdeSScott Feldman 247b5d6fbdeSScott Feldman /* Recusively search down until we find a sw port dev. 248*9d47c0a2SJiri Pirko * (A sw port dev supports switchdev_parent_id_get). 249b5d6fbdeSScott Feldman */ 250b5d6fbdeSScott Feldman 251b5d6fbdeSScott Feldman if (dev->features & NETIF_F_HW_SWITCH_OFFLOAD && 252*9d47c0a2SJiri Pirko ops && ops->switchdev_parent_id_get) 253b5d6fbdeSScott Feldman return dev; 254b5d6fbdeSScott Feldman 255b5d6fbdeSScott Feldman netdev_for_each_lower_dev(dev, lower_dev, iter) { 256ebb9a03aSJiri Pirko port_dev = switchdev_get_lowest_dev(lower_dev); 257b5d6fbdeSScott Feldman if (port_dev) 258b5d6fbdeSScott Feldman return port_dev; 259b5d6fbdeSScott Feldman } 260b5d6fbdeSScott Feldman 261b5d6fbdeSScott Feldman return NULL; 262b5d6fbdeSScott Feldman } 263b5d6fbdeSScott Feldman 264ebb9a03aSJiri Pirko static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi) 265b5d6fbdeSScott Feldman { 266b5d6fbdeSScott Feldman struct netdev_phys_item_id psid; 267b5d6fbdeSScott Feldman struct netdev_phys_item_id prev_psid; 268b5d6fbdeSScott Feldman struct net_device *dev = NULL; 269b5d6fbdeSScott Feldman int nhsel; 270b5d6fbdeSScott Feldman 271b5d6fbdeSScott Feldman /* For this route, all nexthop devs must be on the same switch. */ 272b5d6fbdeSScott Feldman 273b5d6fbdeSScott Feldman for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) { 274b5d6fbdeSScott Feldman const struct fib_nh *nh = &fi->fib_nh[nhsel]; 275b5d6fbdeSScott Feldman 276b5d6fbdeSScott Feldman if (!nh->nh_dev) 277b5d6fbdeSScott Feldman return NULL; 278b5d6fbdeSScott Feldman 279ebb9a03aSJiri Pirko dev = switchdev_get_lowest_dev(nh->nh_dev); 280b5d6fbdeSScott Feldman if (!dev) 281b5d6fbdeSScott Feldman return NULL; 282b5d6fbdeSScott Feldman 283ebb9a03aSJiri Pirko if (switchdev_parent_id_get(dev, &psid)) 284b5d6fbdeSScott Feldman return NULL; 285b5d6fbdeSScott Feldman 286b5d6fbdeSScott Feldman if (nhsel > 0) { 287b5d6fbdeSScott Feldman if (prev_psid.id_len != psid.id_len) 288b5d6fbdeSScott Feldman return NULL; 289b5d6fbdeSScott Feldman if (memcmp(prev_psid.id, psid.id, psid.id_len)) 290b5d6fbdeSScott Feldman return NULL; 291b5d6fbdeSScott Feldman } 292b5d6fbdeSScott Feldman 293b5d6fbdeSScott Feldman prev_psid = psid; 294b5d6fbdeSScott Feldman } 295b5d6fbdeSScott Feldman 296b5d6fbdeSScott Feldman return dev; 297b5d6fbdeSScott Feldman } 298b5d6fbdeSScott Feldman 2995e8d9049SScott Feldman /** 300ebb9a03aSJiri Pirko * switchdev_fib_ipv4_add - Add IPv4 route entry to switch 3015e8d9049SScott Feldman * 3025e8d9049SScott Feldman * @dst: route's IPv4 destination address 3035e8d9049SScott Feldman * @dst_len: destination address length (prefix length) 3045e8d9049SScott Feldman * @fi: route FIB info structure 3055e8d9049SScott Feldman * @tos: route TOS 3065e8d9049SScott Feldman * @type: route type 307f8f21471SScott Feldman * @nlflags: netlink flags passed in (NLM_F_*) 3085e8d9049SScott Feldman * @tb_id: route table ID 3095e8d9049SScott Feldman * 3105e8d9049SScott Feldman * Add IPv4 route entry to switch device. 3115e8d9049SScott Feldman */ 312ebb9a03aSJiri Pirko int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, 313f8f21471SScott Feldman u8 tos, u8 type, u32 nlflags, u32 tb_id) 3145e8d9049SScott Feldman { 315b5d6fbdeSScott Feldman struct net_device *dev; 316*9d47c0a2SJiri Pirko const struct switchdev_ops *ops; 317b5d6fbdeSScott Feldman int err = 0; 318b5d6fbdeSScott Feldman 3198e05fd71SScott Feldman /* Don't offload route if using custom ip rules or if 3208e05fd71SScott Feldman * IPv4 FIB offloading has been disabled completely. 3218e05fd71SScott Feldman */ 3228e05fd71SScott Feldman 323e1315db1SScott Feldman #ifdef CONFIG_IP_MULTIPLE_TABLES 324e1315db1SScott Feldman if (fi->fib_net->ipv4.fib_has_custom_rules) 325e1315db1SScott Feldman return 0; 326e1315db1SScott Feldman #endif 327e1315db1SScott Feldman 328e1315db1SScott Feldman if (fi->fib_net->ipv4.fib_offload_disabled) 329104616e7SScott Feldman return 0; 330104616e7SScott Feldman 331ebb9a03aSJiri Pirko dev = switchdev_get_dev_by_nhs(fi); 332b5d6fbdeSScott Feldman if (!dev) 3335e8d9049SScott Feldman return 0; 334*9d47c0a2SJiri Pirko ops = dev->switchdev_ops; 335b5d6fbdeSScott Feldman 336*9d47c0a2SJiri Pirko if (ops->switchdev_fib_ipv4_add) { 337*9d47c0a2SJiri Pirko err = ops->switchdev_fib_ipv4_add(dev, htonl(dst), dst_len, 338f8f21471SScott Feldman fi, tos, type, nlflags, 339f8f21471SScott Feldman tb_id); 340b5d6fbdeSScott Feldman if (!err) 341b5d6fbdeSScott Feldman fi->fib_flags |= RTNH_F_EXTERNAL; 342b5d6fbdeSScott Feldman } 343b5d6fbdeSScott Feldman 344b5d6fbdeSScott Feldman return err; 3455e8d9049SScott Feldman } 346ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_add); 3475e8d9049SScott Feldman 3485e8d9049SScott Feldman /** 349ebb9a03aSJiri Pirko * switchdev_fib_ipv4_del - Delete IPv4 route entry from switch 3505e8d9049SScott Feldman * 3515e8d9049SScott Feldman * @dst: route's IPv4 destination address 3525e8d9049SScott Feldman * @dst_len: destination address length (prefix length) 3535e8d9049SScott Feldman * @fi: route FIB info structure 3545e8d9049SScott Feldman * @tos: route TOS 3555e8d9049SScott Feldman * @type: route type 3565e8d9049SScott Feldman * @tb_id: route table ID 3575e8d9049SScott Feldman * 3585e8d9049SScott Feldman * Delete IPv4 route entry from switch device. 3595e8d9049SScott Feldman */ 360ebb9a03aSJiri Pirko int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, 3615e8d9049SScott Feldman u8 tos, u8 type, u32 tb_id) 3625e8d9049SScott Feldman { 363b5d6fbdeSScott Feldman struct net_device *dev; 364*9d47c0a2SJiri Pirko const struct switchdev_ops *ops; 365b5d6fbdeSScott Feldman int err = 0; 366b5d6fbdeSScott Feldman 367b5d6fbdeSScott Feldman if (!(fi->fib_flags & RTNH_F_EXTERNAL)) 3685e8d9049SScott Feldman return 0; 369b5d6fbdeSScott Feldman 370ebb9a03aSJiri Pirko dev = switchdev_get_dev_by_nhs(fi); 371b5d6fbdeSScott Feldman if (!dev) 372b5d6fbdeSScott Feldman return 0; 373*9d47c0a2SJiri Pirko ops = dev->switchdev_ops; 374b5d6fbdeSScott Feldman 375*9d47c0a2SJiri Pirko if (ops->switchdev_fib_ipv4_del) { 376*9d47c0a2SJiri Pirko err = ops->switchdev_fib_ipv4_del(dev, htonl(dst), dst_len, 377b5d6fbdeSScott Feldman fi, tos, type, tb_id); 378b5d6fbdeSScott Feldman if (!err) 379b5d6fbdeSScott Feldman fi->fib_flags &= ~RTNH_F_EXTERNAL; 380b5d6fbdeSScott Feldman } 381b5d6fbdeSScott Feldman 382b5d6fbdeSScott Feldman return err; 3835e8d9049SScott Feldman } 384ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_del); 3858e05fd71SScott Feldman 3868e05fd71SScott Feldman /** 387ebb9a03aSJiri Pirko * switchdev_fib_ipv4_abort - Abort an IPv4 FIB operation 3888e05fd71SScott Feldman * 3898e05fd71SScott Feldman * @fi: route FIB info structure 3908e05fd71SScott Feldman */ 391ebb9a03aSJiri Pirko void switchdev_fib_ipv4_abort(struct fib_info *fi) 3928e05fd71SScott Feldman { 3938e05fd71SScott Feldman /* There was a problem installing this route to the offload 3948e05fd71SScott Feldman * device. For now, until we come up with more refined 3958e05fd71SScott Feldman * policy handling, abruptly end IPv4 fib offloading for 3968e05fd71SScott Feldman * for entire net by flushing offload device(s) of all 3978e05fd71SScott Feldman * IPv4 routes, and mark IPv4 fib offloading broken from 3988e05fd71SScott Feldman * this point forward. 3998e05fd71SScott Feldman */ 4008e05fd71SScott Feldman 4018e05fd71SScott Feldman fib_flush_external(fi->fib_net); 4028e05fd71SScott Feldman fi->fib_net->ipv4.fib_offload_disabled = true; 4038e05fd71SScott Feldman } 404ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_abort); 405