Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'nexthop-add-support-for-nexthop-objects-offload'

Ido Schimmel says:

====================
nexthop: Add support for nexthop objects offload

This patch set adds support for nexthop objects offload with a dummy
implementation over netdevsim. mlxsw support will be added later.

The general idea is very similar to route offload in that notifications
are sent whenever nexthop objects are changed. A listener can veto the
change and the error will be communicated to user space with extack.

To keep listeners as simple as possible, they not only receive
notifications for the nexthop object that is changed, but also for all
the other objects affected by this change. For example, when a single
nexthop is replaced, a replace notification is sent for the single
nexthop, but also for all the nexthop groups this nexthop is member in.
This relieves listeners from the need to track such dependencies.

To simplify things further for listeners, the notification info does not
contain the raw nexthop data structures (e.g., 'struct nexthop'), but
less complex data structures into which the raw data structures are
parsed into.

Tested with a new selftest over netdevsim and with fib_nexthops.sh:

Tests passed: 164
Tests failed: 0

Patch set overview:

Patches #1-#4 introduce the aforementioned data structures and convert
existing listeners (i.e., the VXLAN driver) to use them.

Patches #5-#6 add a new RTNH_F_TRAP flag and the ability to set it and
RTNH_F_OFFLOAD on nexthops. This flag is used by netdevsim for testing
purposes and will also be used by mlxsw. These flags are consistent with
the existing RTM_F_OFFLOAD and RTM_F_TRAP flags.

Patches #7-#14 gradually add the new nexthop notifications.

Patches #15-#18 add a dummy implementation for nexthop offload over
netdevsim and a selftest to exercise both good and bad flows.

Changes since RFC [1]:

Patch #1: s/is_encap/has_encap/
Patch #3: Add a blank line in __nh_notifier_single_info_init()
Patch #5: Reword commit message
Patch #6: s/nexthop_hw_flags_set/nexthop_set_hw_flags/
Patch #7: Reword commit message
Patch #11: Allocate extack on the stack

Follow-up patch sets:

selftests: forwarding: Add nexthop objects tests
mlxsw: Preparations for nexthop objects support - part 1/2
mlxsw: Preparations for nexthop objects support - part 2/2
mlxsw: Add support for nexthop objects
mlxsw: Add support for blackhole nexthops
mlxsw: Update adjacency index more efficiently

[1] https://lore.kernel.org/netdev/20200908091037.2709823-1-idosch@idosch.org/
====================

Link: https://lore.kernel.org/r/20201104133040.1125369-1-idosch@idosch.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+995 -47
+2 -1
Documentation/networking/devlink/netdevsim.rst
··· 46 46 ========= 47 47 48 48 The ``netdevsim`` driver exposes resources to control the number of FIB 49 - entries and FIB rule entries that the driver will allow. 49 + entries, FIB rule entries and nexthops that the driver will allow. 50 50 51 51 .. code:: shell 52 52 ··· 54 54 $ devlink resource set netdevsim/netdevsim0 path /IPv4/fib-rules size 16 55 55 $ devlink resource set netdevsim/netdevsim0 path /IPv6/fib size 64 56 56 $ devlink resource set netdevsim/netdevsim0 path /IPv6/fib-rules size 16 57 + $ devlink resource set netdevsim/netdevsim0 path /nexthops size 16 57 58 $ devlink dev reload netdevsim/netdevsim0 58 59 59 60 Driver-specific Traps
+6
drivers/net/netdevsim/dev.c
··· 324 324 return err; 325 325 } 326 326 327 + /* Resources for nexthops */ 328 + err = devlink_resource_register(devlink, "nexthops", (u64)-1, 329 + NSIM_RESOURCE_NEXTHOPS, 330 + DEVLINK_RESOURCE_ID_PARENT_TOP, 331 + &params); 332 + 327 333 out: 328 334 return err; 329 335 }
+251 -14
drivers/net/netdevsim/fib.c
··· 25 25 #include <net/ip6_fib.h> 26 26 #include <net/fib_rules.h> 27 27 #include <net/net_namespace.h> 28 + #include <net/nexthop.h> 28 29 29 30 #include "netdevsim.h" 30 31 ··· 43 42 struct notifier_block fib_nb; 44 43 struct nsim_per_fib_data ipv4; 45 44 struct nsim_per_fib_data ipv6; 45 + struct nsim_fib_entry nexthops; 46 46 struct rhashtable fib_rt_ht; 47 47 struct list_head fib_rt_list; 48 48 spinlock_t fib_lock; /* Protects hashtable, list and accounting */ 49 + struct notifier_block nexthop_nb; 50 + struct rhashtable nexthop_ht; 49 51 struct devlink *devlink; 50 52 }; 51 53 ··· 90 86 .automatic_shrinking = true, 91 87 }; 92 88 89 + struct nsim_nexthop { 90 + struct rhash_head ht_node; 91 + u64 occ; 92 + u32 id; 93 + }; 94 + 95 + static const struct rhashtable_params nsim_nexthop_ht_params = { 96 + .key_offset = offsetof(struct nsim_nexthop, id), 97 + .head_offset = offsetof(struct nsim_nexthop, ht_node), 98 + .key_len = sizeof(u32), 99 + .automatic_shrinking = true, 100 + }; 101 + 93 102 u64 nsim_fib_get_val(struct nsim_fib_data *fib_data, 94 103 enum nsim_resource_id res_id, bool max) 95 104 { ··· 120 103 break; 121 104 case NSIM_RESOURCE_IPV6_FIB_RULES: 122 105 entry = &fib_data->ipv6.rules; 106 + break; 107 + case NSIM_RESOURCE_NEXTHOPS: 108 + entry = &fib_data->nexthops; 123 109 break; 124 110 default: 125 111 return 0; ··· 148 128 break; 149 129 case NSIM_RESOURCE_IPV6_FIB_RULES: 150 130 entry = &fib_data->ipv6.rules; 131 + break; 132 + case NSIM_RESOURCE_NEXTHOPS: 133 + entry = &fib_data->nexthops; 151 134 break; 152 135 default: 153 136 WARN_ON(1); ··· 411 388 int err = 0; 412 389 413 390 fen_info = container_of(info, struct fib_entry_notifier_info, info); 414 - 415 - if (fen_info->fi->nh) { 416 - NL_SET_ERR_MSG_MOD(info->extack, "IPv4 route with nexthop objects is not supported"); 417 - return 0; 418 - } 419 391 420 392 switch (event) { 421 393 case FIB_EVENT_ENTRY_REPLACE: ··· 722 704 723 705 fen6_info = container_of(info, struct fib6_entry_notifier_info, info); 724 706 725 - if (fen6_info->rt->nh) { 726 - NL_SET_ERR_MSG_MOD(info->extack, "IPv6 route with nexthop objects is not supported"); 727 - return 0; 728 - } 729 - 730 707 if (fen6_info->rt->fib6_src.plen) { 731 708 NL_SET_ERR_MSG_MOD(info->extack, "IPv6 source-specific route is not supported"); 732 709 return 0; ··· 851 838 data->ipv6.rules.num = 0ULL; 852 839 } 853 840 841 + static struct nsim_nexthop *nsim_nexthop_create(struct nsim_fib_data *data, 842 + struct nh_notifier_info *info) 843 + { 844 + struct nsim_nexthop *nexthop; 845 + u64 occ = 0; 846 + int i; 847 + 848 + nexthop = kzalloc(sizeof(*nexthop), GFP_KERNEL); 849 + if (!nexthop) 850 + return NULL; 851 + 852 + nexthop->id = info->id; 853 + 854 + /* Determine the number of nexthop entries the new nexthop will 855 + * occupy. 856 + */ 857 + 858 + if (!info->is_grp) { 859 + occ = 1; 860 + goto out; 861 + } 862 + 863 + for (i = 0; i < info->nh_grp->num_nh; i++) 864 + occ += info->nh_grp->nh_entries[i].weight; 865 + 866 + out: 867 + nexthop->occ = occ; 868 + return nexthop; 869 + } 870 + 871 + static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop) 872 + { 873 + kfree(nexthop); 874 + } 875 + 876 + static int nsim_nexthop_account(struct nsim_fib_data *data, u64 occ, 877 + bool add, struct netlink_ext_ack *extack) 878 + { 879 + int err = 0; 880 + 881 + if (add) { 882 + if (data->nexthops.num + occ <= data->nexthops.max) { 883 + data->nexthops.num += occ; 884 + } else { 885 + err = -ENOSPC; 886 + NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported nexthops"); 887 + } 888 + } else { 889 + if (WARN_ON(occ > data->nexthops.num)) 890 + return -EINVAL; 891 + data->nexthops.num -= occ; 892 + } 893 + 894 + return err; 895 + } 896 + 897 + static int nsim_nexthop_add(struct nsim_fib_data *data, 898 + struct nsim_nexthop *nexthop, 899 + struct netlink_ext_ack *extack) 900 + { 901 + struct net *net = devlink_net(data->devlink); 902 + int err; 903 + 904 + err = nsim_nexthop_account(data, nexthop->occ, true, extack); 905 + if (err) 906 + return err; 907 + 908 + err = rhashtable_insert_fast(&data->nexthop_ht, &nexthop->ht_node, 909 + nsim_nexthop_ht_params); 910 + if (err) { 911 + NL_SET_ERR_MSG_MOD(extack, "Failed to insert nexthop"); 912 + goto err_nexthop_dismiss; 913 + } 914 + 915 + nexthop_set_hw_flags(net, nexthop->id, false, true); 916 + 917 + return 0; 918 + 919 + err_nexthop_dismiss: 920 + nsim_nexthop_account(data, nexthop->occ, false, extack); 921 + return err; 922 + } 923 + 924 + static int nsim_nexthop_replace(struct nsim_fib_data *data, 925 + struct nsim_nexthop *nexthop, 926 + struct nsim_nexthop *nexthop_old, 927 + struct netlink_ext_ack *extack) 928 + { 929 + struct net *net = devlink_net(data->devlink); 930 + int err; 931 + 932 + err = nsim_nexthop_account(data, nexthop->occ, true, extack); 933 + if (err) 934 + return err; 935 + 936 + err = rhashtable_replace_fast(&data->nexthop_ht, 937 + &nexthop_old->ht_node, &nexthop->ht_node, 938 + nsim_nexthop_ht_params); 939 + if (err) { 940 + NL_SET_ERR_MSG_MOD(extack, "Failed to replace nexthop"); 941 + goto err_nexthop_dismiss; 942 + } 943 + 944 + nexthop_set_hw_flags(net, nexthop->id, false, true); 945 + nsim_nexthop_account(data, nexthop_old->occ, false, extack); 946 + nsim_nexthop_destroy(nexthop_old); 947 + 948 + return 0; 949 + 950 + err_nexthop_dismiss: 951 + nsim_nexthop_account(data, nexthop->occ, false, extack); 952 + return err; 953 + } 954 + 955 + static int nsim_nexthop_insert(struct nsim_fib_data *data, 956 + struct nh_notifier_info *info) 957 + { 958 + struct nsim_nexthop *nexthop, *nexthop_old; 959 + int err; 960 + 961 + nexthop = nsim_nexthop_create(data, info); 962 + if (!nexthop) 963 + return -ENOMEM; 964 + 965 + nexthop_old = rhashtable_lookup_fast(&data->nexthop_ht, &info->id, 966 + nsim_nexthop_ht_params); 967 + if (!nexthop_old) 968 + err = nsim_nexthop_add(data, nexthop, info->extack); 969 + else 970 + err = nsim_nexthop_replace(data, nexthop, nexthop_old, 971 + info->extack); 972 + 973 + if (err) 974 + nsim_nexthop_destroy(nexthop); 975 + 976 + return err; 977 + } 978 + 979 + static void nsim_nexthop_remove(struct nsim_fib_data *data, 980 + struct nh_notifier_info *info) 981 + { 982 + struct nsim_nexthop *nexthop; 983 + 984 + nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &info->id, 985 + nsim_nexthop_ht_params); 986 + if (!nexthop) 987 + return; 988 + 989 + rhashtable_remove_fast(&data->nexthop_ht, &nexthop->ht_node, 990 + nsim_nexthop_ht_params); 991 + nsim_nexthop_account(data, nexthop->occ, false, info->extack); 992 + nsim_nexthop_destroy(nexthop); 993 + } 994 + 995 + static int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event, 996 + void *ptr) 997 + { 998 + struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, 999 + nexthop_nb); 1000 + struct nh_notifier_info *info = ptr; 1001 + int err = 0; 1002 + 1003 + ASSERT_RTNL(); 1004 + 1005 + switch (event) { 1006 + case NEXTHOP_EVENT_REPLACE: 1007 + err = nsim_nexthop_insert(data, info); 1008 + break; 1009 + case NEXTHOP_EVENT_DEL: 1010 + nsim_nexthop_remove(data, info); 1011 + break; 1012 + default: 1013 + break; 1014 + } 1015 + 1016 + return notifier_from_errno(err); 1017 + } 1018 + 1019 + static void nsim_nexthop_free(void *ptr, void *arg) 1020 + { 1021 + struct nsim_nexthop *nexthop = ptr; 1022 + struct nsim_fib_data *data = arg; 1023 + struct net *net; 1024 + 1025 + net = devlink_net(data->devlink); 1026 + nexthop_set_hw_flags(net, nexthop->id, false, false); 1027 + nsim_nexthop_account(data, nexthop->occ, false, NULL); 1028 + nsim_nexthop_destroy(nexthop); 1029 + } 1030 + 854 1031 static u64 nsim_fib_ipv4_resource_occ_get(void *priv) 855 1032 { 856 1033 struct nsim_fib_data *data = priv; ··· 1069 866 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false); 1070 867 } 1071 868 869 + static u64 nsim_fib_nexthops_res_occ_get(void *priv) 870 + { 871 + struct nsim_fib_data *data = priv; 872 + 873 + return nsim_fib_get_val(data, NSIM_RESOURCE_NEXTHOPS, false); 874 + } 875 + 1072 876 static void nsim_fib_set_max_all(struct nsim_fib_data *data, 1073 877 struct devlink *devlink) 1074 878 { 1075 879 enum nsim_resource_id res_ids[] = { 1076 880 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES, 1077 - NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES 881 + NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES, 882 + NSIM_RESOURCE_NEXTHOPS, 1078 883 }; 1079 884 int i; 1080 885 ··· 1108 897 return ERR_PTR(-ENOMEM); 1109 898 data->devlink = devlink; 1110 899 900 + err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params); 901 + if (err) 902 + goto err_data_free; 903 + 1111 904 spin_lock_init(&data->fib_lock); 1112 905 INIT_LIST_HEAD(&data->fib_rt_list); 1113 906 err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params); 1114 907 if (err) 1115 - goto err_data_free; 908 + goto err_rhashtable_nexthop_destroy; 1116 909 1117 910 nsim_fib_set_max_all(data, devlink); 911 + 912 + data->nexthop_nb.notifier_call = nsim_nexthop_event_nb; 913 + err = register_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb, 914 + extack); 915 + if (err) { 916 + pr_err("Failed to register nexthop notifier\n"); 917 + goto err_rhashtable_fib_destroy; 918 + } 1118 919 1119 920 data->fib_nb.notifier_call = nsim_fib_event_nb; 1120 921 err = register_fib_notifier(devlink_net(devlink), &data->fib_nb, 1121 922 nsim_fib_dump_inconsistent, extack); 1122 923 if (err) { 1123 924 pr_err("Failed to register fib notifier\n"); 1124 - goto err_rhashtable_destroy; 925 + goto err_nexthop_nb_unregister; 1125 926 } 1126 927 1127 928 devlink_resource_occ_get_register(devlink, ··· 1152 929 NSIM_RESOURCE_IPV6_FIB_RULES, 1153 930 nsim_fib_ipv6_rules_res_occ_get, 1154 931 data); 932 + devlink_resource_occ_get_register(devlink, 933 + NSIM_RESOURCE_NEXTHOPS, 934 + nsim_fib_nexthops_res_occ_get, 935 + data); 1155 936 return data; 1156 937 1157 - err_rhashtable_destroy: 938 + err_nexthop_nb_unregister: 939 + unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb); 940 + err_rhashtable_fib_destroy: 1158 941 rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free, 942 + data); 943 + err_rhashtable_nexthop_destroy: 944 + rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free, 1159 945 data); 1160 946 err_data_free: 1161 947 kfree(data); ··· 1174 942 void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data) 1175 943 { 1176 944 devlink_resource_occ_get_unregister(devlink, 945 + NSIM_RESOURCE_NEXTHOPS); 946 + devlink_resource_occ_get_unregister(devlink, 1177 947 NSIM_RESOURCE_IPV6_FIB_RULES); 1178 948 devlink_resource_occ_get_unregister(devlink, 1179 949 NSIM_RESOURCE_IPV6_FIB); ··· 1184 950 devlink_resource_occ_get_unregister(devlink, 1185 951 NSIM_RESOURCE_IPV4_FIB); 1186 952 unregister_fib_notifier(devlink_net(devlink), &data->fib_nb); 953 + unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb); 1187 954 rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free, 955 + data); 956 + rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free, 1188 957 data); 1189 958 WARN_ON_ONCE(!list_empty(&data->fib_rt_list)); 1190 959 kfree(data);
+1
drivers/net/netdevsim/netdevsim.h
··· 158 158 NSIM_RESOURCE_IPV6, 159 159 NSIM_RESOURCE_IPV6_FIB, 160 160 NSIM_RESOURCE_IPV6_FIB_RULES, 161 + NSIM_RESOURCE_NEXTHOPS, 161 162 }; 162 163 163 164 struct nsim_dev_health {
+9 -3
drivers/net/vxlan.c
··· 4684 4684 static int vxlan_nexthop_event(struct notifier_block *nb, 4685 4685 unsigned long event, void *ptr) 4686 4686 { 4687 - struct nexthop *nh = ptr; 4687 + struct nh_notifier_info *info = ptr; 4688 + struct nexthop *nh; 4688 4689 4689 - if (!nh || event != NEXTHOP_EVENT_DEL) 4690 + if (event != NEXTHOP_EVENT_DEL) 4691 + return NOTIFY_DONE; 4692 + 4693 + nh = nexthop_find_by_id(info->net, info->id); 4694 + if (!nh) 4690 4695 return NOTIFY_DONE; 4691 4696 4692 4697 vxlan_fdb_nh_flush(nh); ··· 4711 4706 for (h = 0; h < PORT_HASH_SIZE; ++h) 4712 4707 INIT_HLIST_HEAD(&vn->sock_list[h]); 4713 4708 4714 - return register_nexthop_notifier(net, &vn->nexthop_notifier_block); 4709 + return register_nexthop_notifier(net, &vn->nexthop_notifier_block, 4710 + NULL); 4715 4711 } 4716 4712 4717 4713 static void vxlan_destroy_tunnels(struct net *net, struct list_head *head)
+40 -2
include/net/nexthop.h
··· 105 105 }; 106 106 107 107 enum nexthop_event_type { 108 - NEXTHOP_EVENT_DEL 108 + NEXTHOP_EVENT_DEL, 109 + NEXTHOP_EVENT_REPLACE, 109 110 }; 110 111 111 - int register_nexthop_notifier(struct net *net, struct notifier_block *nb); 112 + struct nh_notifier_single_info { 113 + struct net_device *dev; 114 + u8 gw_family; 115 + union { 116 + __be32 ipv4; 117 + struct in6_addr ipv6; 118 + }; 119 + u8 is_reject:1, 120 + is_fdb:1, 121 + has_encap:1; 122 + }; 123 + 124 + struct nh_notifier_grp_entry_info { 125 + u8 weight; 126 + u32 id; 127 + struct nh_notifier_single_info nh; 128 + }; 129 + 130 + struct nh_notifier_grp_info { 131 + u16 num_nh; 132 + bool is_fdb; 133 + struct nh_notifier_grp_entry_info nh_entries[]; 134 + }; 135 + 136 + struct nh_notifier_info { 137 + struct net *net; 138 + struct netlink_ext_ack *extack; 139 + u32 id; 140 + bool is_grp; 141 + union { 142 + struct nh_notifier_single_info *nh; 143 + struct nh_notifier_grp_info *nh_grp; 144 + }; 145 + }; 146 + 147 + int register_nexthop_notifier(struct net *net, struct notifier_block *nb, 148 + struct netlink_ext_ack *extack); 112 149 int unregister_nexthop_notifier(struct net *net, struct notifier_block *nb); 150 + void nexthop_set_hw_flags(struct net *net, u32 id, bool offload, bool trap); 113 151 114 152 /* caller is holding rcu or rtnl; no reference taken to nexthop */ 115 153 struct nexthop *nexthop_find_by_id(struct net *net, u32 id);
+4 -2
include/uapi/linux/rtnetlink.h
··· 396 396 #define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ 397 397 #define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ 398 398 #define RTNH_F_ONLINK 4 /* Gateway is forced on link */ 399 - #define RTNH_F_OFFLOAD 8 /* offloaded route */ 399 + #define RTNH_F_OFFLOAD 8 /* Nexthop is offloaded */ 400 400 #define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */ 401 401 #define RTNH_F_UNRESOLVED 32 /* The entry is unresolved (ipmr) */ 402 + #define RTNH_F_TRAP 64 /* Nexthop is trapping packets */ 402 403 403 - #define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD) 404 + #define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | \ 405 + RTNH_F_OFFLOAD | RTNH_F_TRAP) 404 406 405 407 /* Macros to handle hexthops */ 406 408
+2
net/ipv4/fib_semantics.c
··· 1644 1644 *flags |= (nhc->nhc_flags & RTNH_F_ONLINK); 1645 1645 if (nhc->nhc_flags & RTNH_F_OFFLOAD) 1646 1646 *flags |= RTNH_F_OFFLOAD; 1647 + if (nhc->nhc_flags & RTNH_F_TRAP) 1648 + *flags |= RTNH_F_TRAP; 1647 1649 1648 1650 if (!skip_oif && nhc->nhc_dev && 1649 1651 nla_put_u32(skb, RTA_OIF, nhc->nhc_dev->ifindex))
-9
net/ipv4/fib_trie.c
··· 2100 2100 rtmsg_fib(RTM_NEWROUTE, htonl(n->key), fa, 2101 2101 KEYLENGTH - fa->fa_slen, tb->tb_id, 2102 2102 info, NLM_F_REPLACE); 2103 - 2104 - /* call_fib_entry_notifiers will be removed when 2105 - * in-kernel notifier is implemented and supported 2106 - * for nexthop objects 2107 - */ 2108 - call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, 2109 - n->key, 2110 - KEYLENGTH - fa->fa_slen, fa, 2111 - NULL); 2112 2103 } 2113 2104 } 2114 2105 }
+244 -11
net/ipv4/nexthop.c
··· 36 36 [NHA_FDB] = { .type = NLA_FLAG }, 37 37 }; 38 38 39 + static bool nexthop_notifiers_is_empty(struct net *net) 40 + { 41 + return !net->nexthop.notifier_chain.head; 42 + } 43 + 44 + static void 45 + __nh_notifier_single_info_init(struct nh_notifier_single_info *nh_info, 46 + const struct nexthop *nh) 47 + { 48 + struct nh_info *nhi = rtnl_dereference(nh->nh_info); 49 + 50 + nh_info->dev = nhi->fib_nhc.nhc_dev; 51 + nh_info->gw_family = nhi->fib_nhc.nhc_gw_family; 52 + if (nh_info->gw_family == AF_INET) 53 + nh_info->ipv4 = nhi->fib_nhc.nhc_gw.ipv4; 54 + else if (nh_info->gw_family == AF_INET6) 55 + nh_info->ipv6 = nhi->fib_nhc.nhc_gw.ipv6; 56 + 57 + nh_info->is_reject = nhi->reject_nh; 58 + nh_info->is_fdb = nhi->fdb_nh; 59 + nh_info->has_encap = !!nhi->fib_nhc.nhc_lwtstate; 60 + } 61 + 62 + static int nh_notifier_single_info_init(struct nh_notifier_info *info, 63 + const struct nexthop *nh) 64 + { 65 + info->nh = kzalloc(sizeof(*info->nh), GFP_KERNEL); 66 + if (!info->nh) 67 + return -ENOMEM; 68 + 69 + __nh_notifier_single_info_init(info->nh, nh); 70 + 71 + return 0; 72 + } 73 + 74 + static void nh_notifier_single_info_fini(struct nh_notifier_info *info) 75 + { 76 + kfree(info->nh); 77 + } 78 + 79 + static int nh_notifier_grp_info_init(struct nh_notifier_info *info, 80 + const struct nexthop *nh) 81 + { 82 + struct nh_group *nhg = rtnl_dereference(nh->nh_grp); 83 + u16 num_nh = nhg->num_nh; 84 + int i; 85 + 86 + info->nh_grp = kzalloc(struct_size(info->nh_grp, nh_entries, num_nh), 87 + GFP_KERNEL); 88 + if (!info->nh_grp) 89 + return -ENOMEM; 90 + 91 + info->nh_grp->num_nh = num_nh; 92 + info->nh_grp->is_fdb = nhg->fdb_nh; 93 + 94 + for (i = 0; i < num_nh; i++) { 95 + struct nh_grp_entry *nhge = &nhg->nh_entries[i]; 96 + 97 + info->nh_grp->nh_entries[i].id = nhge->nh->id; 98 + info->nh_grp->nh_entries[i].weight = nhge->weight; 99 + __nh_notifier_single_info_init(&info->nh_grp->nh_entries[i].nh, 100 + nhge->nh); 101 + } 102 + 103 + return 0; 104 + } 105 + 106 + static void nh_notifier_grp_info_fini(struct nh_notifier_info *info) 107 + { 108 + kfree(info->nh_grp); 109 + } 110 + 111 + static int nh_notifier_info_init(struct nh_notifier_info *info, 112 + const struct nexthop *nh) 113 + { 114 + info->id = nh->id; 115 + info->is_grp = nh->is_group; 116 + 117 + if (info->is_grp) 118 + return nh_notifier_grp_info_init(info, nh); 119 + else 120 + return nh_notifier_single_info_init(info, nh); 121 + } 122 + 123 + static void nh_notifier_info_fini(struct nh_notifier_info *info) 124 + { 125 + if (info->is_grp) 126 + nh_notifier_grp_info_fini(info); 127 + else 128 + nh_notifier_single_info_fini(info); 129 + } 130 + 39 131 static int call_nexthop_notifiers(struct net *net, 40 132 enum nexthop_event_type event_type, 41 - struct nexthop *nh) 133 + struct nexthop *nh, 134 + struct netlink_ext_ack *extack) 42 135 { 136 + struct nh_notifier_info info = { 137 + .net = net, 138 + .extack = extack, 139 + }; 43 140 int err; 44 141 142 + ASSERT_RTNL(); 143 + 144 + if (nexthop_notifiers_is_empty(net)) 145 + return 0; 146 + 147 + err = nh_notifier_info_init(&info, nh); 148 + if (err) { 149 + NL_SET_ERR_MSG(extack, "Failed to initialize nexthop notifier info"); 150 + return err; 151 + } 152 + 45 153 err = blocking_notifier_call_chain(&net->nexthop.notifier_chain, 46 - event_type, nh); 154 + event_type, &info); 155 + nh_notifier_info_fini(&info); 156 + 157 + return notifier_to_errno(err); 158 + } 159 + 160 + static int call_nexthop_notifier(struct notifier_block *nb, struct net *net, 161 + enum nexthop_event_type event_type, 162 + struct nexthop *nh, 163 + struct netlink_ext_ack *extack) 164 + { 165 + struct nh_notifier_info info = { 166 + .net = net, 167 + .extack = extack, 168 + }; 169 + int err; 170 + 171 + err = nh_notifier_info_init(&info, nh); 172 + if (err) 173 + return err; 174 + 175 + err = nb->notifier_call(nb, event_type, &info); 176 + nh_notifier_info_fini(&info); 177 + 47 178 return notifier_to_errno(err); 48 179 } 49 180 ··· 913 782 { 914 783 struct nh_grp_entry *nhges, *new_nhges; 915 784 struct nexthop *nhp = nhge->nh_parent; 785 + struct netlink_ext_ack extack; 916 786 struct nexthop *nh = nhge->nh; 917 787 struct nh_group *nhg, *newg; 918 - int i, j; 788 + int i, j, err; 919 789 920 790 WARN_ON(!nh); 921 791 ··· 963 831 964 832 list_del(&nhge->nh_list); 965 833 nexthop_put(nhge->nh); 834 + 835 + err = call_nexthop_notifiers(net, NEXTHOP_EVENT_REPLACE, nhp, &extack); 836 + if (err) 837 + pr_err("%s\n", extack._msg); 966 838 967 839 if (nlinfo) 968 840 nexthop_notify(RTM_NEWNEXTHOP, nhp, nlinfo); ··· 1043 907 static void remove_nexthop(struct net *net, struct nexthop *nh, 1044 908 struct nl_info *nlinfo) 1045 909 { 1046 - call_nexthop_notifiers(net, NEXTHOP_EVENT_DEL, nh); 910 + call_nexthop_notifiers(net, NEXTHOP_EVENT_DEL, nh, NULL); 1047 911 1048 912 /* remove from the tree */ 1049 913 rb_erase(&nh->rb_node, &net->nexthop.rb_root); ··· 1076 940 struct netlink_ext_ack *extack) 1077 941 { 1078 942 struct nh_group *oldg, *newg; 1079 - int i; 943 + int i, err; 1080 944 1081 945 if (!new->is_group) { 1082 946 NL_SET_ERR_MSG(extack, "Can not replace a nexthop group with a nexthop."); 1083 947 return -EINVAL; 1084 948 } 949 + 950 + err = call_nexthop_notifiers(net, NEXTHOP_EVENT_REPLACE, new, extack); 951 + if (err) 952 + return err; 1085 953 1086 954 oldg = rtnl_dereference(old->nh_grp); 1087 955 newg = rtnl_dereference(new->nh_grp); ··· 1125 985 struct nexthop *new, 1126 986 struct netlink_ext_ack *extack) 1127 987 { 988 + u8 old_protocol, old_nh_flags; 1128 989 struct nh_info *oldi, *newi; 990 + struct nh_grp_entry *nhge; 991 + int err; 1129 992 1130 993 if (new->is_group) { 1131 994 NL_SET_ERR_MSG(extack, "Can not replace a nexthop with a nexthop group."); 1132 995 return -EINVAL; 1133 996 } 997 + 998 + err = call_nexthop_notifiers(net, NEXTHOP_EVENT_REPLACE, new, extack); 999 + if (err) 1000 + return err; 1001 + 1002 + /* Hardware flags were set on 'old' as 'new' is not in the red-black 1003 + * tree. Therefore, inherit the flags from 'old' to 'new'. 1004 + */ 1005 + new->nh_flags |= old->nh_flags & (RTNH_F_OFFLOAD | RTNH_F_TRAP); 1134 1006 1135 1007 oldi = rtnl_dereference(old->nh_info); 1136 1008 newi = rtnl_dereference(new->nh_info); ··· 1150 998 newi->nh_parent = old; 1151 999 oldi->nh_parent = new; 1152 1000 1001 + old_protocol = old->protocol; 1002 + old_nh_flags = old->nh_flags; 1003 + 1153 1004 old->protocol = new->protocol; 1154 1005 old->nh_flags = new->nh_flags; 1155 1006 1156 1007 rcu_assign_pointer(old->nh_info, newi); 1157 1008 rcu_assign_pointer(new->nh_info, oldi); 1158 1009 1010 + /* Send a replace notification for all the groups using the nexthop. */ 1011 + list_for_each_entry(nhge, &old->grp_list, nh_list) { 1012 + struct nexthop *nhp = nhge->nh_parent; 1013 + 1014 + err = call_nexthop_notifiers(net, NEXTHOP_EVENT_REPLACE, nhp, 1015 + extack); 1016 + if (err) 1017 + goto err_notify; 1018 + } 1019 + 1159 1020 /* When replacing an IPv4 nexthop with an IPv6 nexthop, potentially 1160 1021 * update IPv4 indication in all the groups using the nexthop. 1161 1022 */ 1162 1023 if (oldi->family == AF_INET && newi->family == AF_INET6) { 1163 - struct nh_grp_entry *nhge; 1164 - 1165 1024 list_for_each_entry(nhge, &old->grp_list, nh_list) { 1166 1025 struct nexthop *nhp = nhge->nh_parent; 1167 1026 struct nh_group *nhg; ··· 1183 1020 } 1184 1021 1185 1022 return 0; 1023 + 1024 + err_notify: 1025 + rcu_assign_pointer(new->nh_info, newi); 1026 + rcu_assign_pointer(old->nh_info, oldi); 1027 + old->nh_flags = old_nh_flags; 1028 + old->protocol = old_protocol; 1029 + oldi->nh_parent = old; 1030 + newi->nh_parent = new; 1031 + list_for_each_entry_continue_reverse(nhge, &old->grp_list, nh_list) { 1032 + struct nexthop *nhp = nhge->nh_parent; 1033 + 1034 + call_nexthop_notifiers(net, NEXTHOP_EVENT_REPLACE, nhp, extack); 1035 + } 1036 + call_nexthop_notifiers(net, NEXTHOP_EVENT_REPLACE, old, extack); 1037 + return err; 1186 1038 } 1187 1039 1188 1040 static void __nexthop_replace_notify(struct net *net, struct nexthop *nh, ··· 1346 1168 1347 1169 rb_link_node_rcu(&new_nh->rb_node, parent, pp); 1348 1170 rb_insert_color(&new_nh->rb_node, root); 1349 - rc = 0; 1171 + 1172 + rc = call_nexthop_notifiers(net, NEXTHOP_EVENT_REPLACE, new_nh, extack); 1173 + if (rc) 1174 + rb_erase(&new_nh->rb_node, &net->nexthop.rb_root); 1175 + 1350 1176 out: 1351 1177 if (!rc) { 1352 1178 nh_base_seq_inc(net); ··· 2139 1957 .notifier_call = nh_netdev_event, 2140 1958 }; 2141 1959 2142 - int register_nexthop_notifier(struct net *net, struct notifier_block *nb) 1960 + static int nexthops_dump(struct net *net, struct notifier_block *nb, 1961 + struct netlink_ext_ack *extack) 2143 1962 { 2144 - return blocking_notifier_chain_register(&net->nexthop.notifier_chain, 2145 - nb); 1963 + struct rb_root *root = &net->nexthop.rb_root; 1964 + struct rb_node *node; 1965 + int err = 0; 1966 + 1967 + for (node = rb_first(root); node; node = rb_next(node)) { 1968 + struct nexthop *nh; 1969 + 1970 + nh = rb_entry(node, struct nexthop, rb_node); 1971 + err = call_nexthop_notifier(nb, net, NEXTHOP_EVENT_REPLACE, nh, 1972 + extack); 1973 + if (err) 1974 + break; 1975 + } 1976 + 1977 + return err; 1978 + } 1979 + 1980 + int register_nexthop_notifier(struct net *net, struct notifier_block *nb, 1981 + struct netlink_ext_ack *extack) 1982 + { 1983 + int err; 1984 + 1985 + rtnl_lock(); 1986 + err = nexthops_dump(net, nb, extack); 1987 + if (err) 1988 + goto unlock; 1989 + err = blocking_notifier_chain_register(&net->nexthop.notifier_chain, 1990 + nb); 1991 + unlock: 1992 + rtnl_unlock(); 1993 + return err; 2146 1994 } 2147 1995 EXPORT_SYMBOL(register_nexthop_notifier); 2148 1996 ··· 2182 1970 nb); 2183 1971 } 2184 1972 EXPORT_SYMBOL(unregister_nexthop_notifier); 1973 + 1974 + void nexthop_set_hw_flags(struct net *net, u32 id, bool offload, bool trap) 1975 + { 1976 + struct nexthop *nexthop; 1977 + 1978 + rcu_read_lock(); 1979 + 1980 + nexthop = nexthop_find_by_id(net, id); 1981 + if (!nexthop) 1982 + goto out; 1983 + 1984 + nexthop->nh_flags &= ~(RTNH_F_OFFLOAD | RTNH_F_TRAP); 1985 + if (offload) 1986 + nexthop->nh_flags |= RTNH_F_OFFLOAD; 1987 + if (trap) 1988 + nexthop->nh_flags |= RTNH_F_TRAP; 1989 + 1990 + out: 1991 + rcu_read_unlock(); 1992 + } 1993 + EXPORT_SYMBOL(nexthop_set_hw_flags); 2185 1994 2186 1995 static void __net_exit nexthop_net_exit(struct net *net) 2187 1996 {
-5
net/ipv6/route.c
··· 6039 6039 struct sk_buff *skb; 6040 6040 int err = -ENOBUFS; 6041 6041 6042 - /* call_fib6_entry_notifiers will be removed when in-kernel notifier 6043 - * is implemented and supported for nexthop objects 6044 - */ 6045 - call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, rt, NULL); 6046 - 6047 6042 skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 6048 6043 if (!skb) 6049 6044 goto errout;
+436
tools/testing/selftests/drivers/net/netdevsim/nexthop.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # This test is for checking the nexthop offload API. It makes use of netdevsim 5 + # which registers a listener to the nexthop notification chain. 6 + 7 + lib_dir=$(dirname $0)/../../../net/forwarding 8 + 9 + ALL_TESTS=" 10 + nexthop_single_add_test 11 + nexthop_single_add_err_test 12 + nexthop_group_add_test 13 + nexthop_group_add_err_test 14 + nexthop_group_replace_test 15 + nexthop_group_replace_err_test 16 + nexthop_single_replace_test 17 + nexthop_single_replace_err_test 18 + nexthop_single_in_group_replace_test 19 + nexthop_single_in_group_replace_err_test 20 + nexthop_single_in_group_delete_test 21 + nexthop_single_in_group_delete_err_test 22 + nexthop_replay_test 23 + nexthop_replay_err_test 24 + " 25 + NETDEVSIM_PATH=/sys/bus/netdevsim/ 26 + DEV_ADDR=1337 27 + DEV=netdevsim${DEV_ADDR} 28 + DEVLINK_DEV=netdevsim/${DEV} 29 + SYSFS_NET_DIR=/sys/bus/netdevsim/devices/$DEV/net/ 30 + NUM_NETIFS=0 31 + source $lib_dir/lib.sh 32 + source $lib_dir/devlink_lib.sh 33 + 34 + nexthop_check() 35 + { 36 + local nharg="$1"; shift 37 + local expected="$1"; shift 38 + 39 + out=$($IP nexthop show ${nharg} | sed -e 's/ *$//') 40 + if [[ "$out" != "$expected" ]]; then 41 + return 1 42 + fi 43 + 44 + return 0 45 + } 46 + 47 + nexthop_resource_check() 48 + { 49 + local expected_occ=$1; shift 50 + 51 + occ=$($DEVLINK -jp resource show $DEVLINK_DEV \ 52 + | jq '.[][][] | select(.name=="nexthops") | .["occ"]') 53 + 54 + if [ $expected_occ -ne $occ ]; then 55 + return 1 56 + fi 57 + 58 + return 0 59 + } 60 + 61 + nexthop_resource_set() 62 + { 63 + local size=$1; shift 64 + 65 + $DEVLINK resource set $DEVLINK_DEV path nexthops size $size 66 + $DEVLINK dev reload $DEVLINK_DEV 67 + } 68 + 69 + nexthop_single_add_test() 70 + { 71 + RET=0 72 + 73 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 74 + nexthop_check "id 1" "id 1 via 192.0.2.2 dev dummy1 scope link trap" 75 + check_err $? "Unexpected nexthop entry" 76 + 77 + nexthop_resource_check 1 78 + check_err $? "Wrong nexthop occupancy" 79 + 80 + $IP nexthop del id 1 81 + nexthop_resource_check 0 82 + check_err $? "Wrong nexthop occupancy after delete" 83 + 84 + log_test "Single nexthop add and delete" 85 + } 86 + 87 + nexthop_single_add_err_test() 88 + { 89 + RET=0 90 + 91 + nexthop_resource_set 1 92 + 93 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 94 + 95 + $IP nexthop add id 2 via 192.0.2.3 dev dummy1 &> /dev/null 96 + check_fail $? "Nexthop addition succeeded when should fail" 97 + 98 + nexthop_resource_check 1 99 + check_err $? "Wrong nexthop occupancy" 100 + 101 + log_test "Single nexthop add failure" 102 + 103 + $IP nexthop flush &> /dev/null 104 + nexthop_resource_set 9999 105 + } 106 + 107 + nexthop_group_add_test() 108 + { 109 + RET=0 110 + 111 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 112 + $IP nexthop add id 2 via 192.0.2.3 dev dummy1 113 + 114 + $IP nexthop add id 10 group 1/2 115 + nexthop_check "id 10" "id 10 group 1/2 trap" 116 + check_err $? "Unexpected nexthop group entry" 117 + 118 + nexthop_resource_check 4 119 + check_err $? "Wrong nexthop occupancy" 120 + 121 + $IP nexthop del id 10 122 + nexthop_resource_check 2 123 + check_err $? "Wrong nexthop occupancy after delete" 124 + 125 + $IP nexthop add id 10 group 1,20/2,39 126 + nexthop_check "id 10" "id 10 group 1,20/2,39 trap" 127 + check_err $? "Unexpected weighted nexthop group entry" 128 + 129 + nexthop_resource_check 61 130 + check_err $? "Wrong weighted nexthop occupancy" 131 + 132 + $IP nexthop del id 10 133 + nexthop_resource_check 2 134 + check_err $? "Wrong nexthop occupancy after delete" 135 + 136 + log_test "Nexthop group add and delete" 137 + 138 + $IP nexthop flush &> /dev/null 139 + } 140 + 141 + nexthop_group_add_err_test() 142 + { 143 + RET=0 144 + 145 + nexthop_resource_set 2 146 + 147 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 148 + $IP nexthop add id 2 via 192.0.2.3 dev dummy1 149 + 150 + $IP nexthop add id 10 group 1/2 &> /dev/null 151 + check_fail $? "Nexthop group addition succeeded when should fail" 152 + 153 + nexthop_resource_check 2 154 + check_err $? "Wrong nexthop occupancy" 155 + 156 + log_test "Nexthop group add failure" 157 + 158 + $IP nexthop flush &> /dev/null 159 + nexthop_resource_set 9999 160 + } 161 + 162 + nexthop_group_replace_test() 163 + { 164 + RET=0 165 + 166 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 167 + $IP nexthop add id 2 via 192.0.2.3 dev dummy1 168 + $IP nexthop add id 3 via 192.0.2.4 dev dummy1 169 + $IP nexthop add id 10 group 1/2 170 + 171 + $IP nexthop replace id 10 group 1/2/3 172 + nexthop_check "id 10" "id 10 group 1/2/3 trap" 173 + check_err $? "Unexpected nexthop group entry" 174 + 175 + nexthop_resource_check 6 176 + check_err $? "Wrong nexthop occupancy" 177 + 178 + log_test "Nexthop group replace" 179 + 180 + $IP nexthop flush &> /dev/null 181 + } 182 + 183 + nexthop_group_replace_err_test() 184 + { 185 + RET=0 186 + 187 + nexthop_resource_set 5 188 + 189 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 190 + $IP nexthop add id 2 via 192.0.2.3 dev dummy1 191 + $IP nexthop add id 3 via 192.0.2.4 dev dummy1 192 + $IP nexthop add id 10 group 1/2 193 + 194 + $IP nexthop replace id 10 group 1/2/3 &> /dev/null 195 + check_fail $? "Nexthop group replacement succeeded when should fail" 196 + 197 + nexthop_check "id 10" "id 10 group 1/2 trap" 198 + check_err $? "Unexpected nexthop group entry after failure" 199 + 200 + nexthop_resource_check 5 201 + check_err $? "Wrong nexthop occupancy after failure" 202 + 203 + log_test "Nexthop group replace failure" 204 + 205 + $IP nexthop flush &> /dev/null 206 + nexthop_resource_set 9999 207 + } 208 + 209 + nexthop_single_replace_test() 210 + { 211 + RET=0 212 + 213 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 214 + 215 + $IP nexthop replace id 1 via 192.0.2.3 dev dummy1 216 + nexthop_check "id 1" "id 1 via 192.0.2.3 dev dummy1 scope link trap" 217 + check_err $? "Unexpected nexthop entry" 218 + 219 + nexthop_resource_check 1 220 + check_err $? "Wrong nexthop occupancy" 221 + 222 + log_test "Single nexthop replace" 223 + 224 + $IP nexthop flush &> /dev/null 225 + } 226 + 227 + nexthop_single_replace_err_test() 228 + { 229 + RET=0 230 + 231 + # This is supposed to cause the replace to fail because the new nexthop 232 + # is programmed before deleting the replaced one. 233 + nexthop_resource_set 1 234 + 235 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 236 + 237 + $IP nexthop replace id 1 via 192.0.2.3 dev dummy1 &> /dev/null 238 + check_fail $? "Nexthop replace succeeded when should fail" 239 + 240 + nexthop_check "id 1" "id 1 via 192.0.2.2 dev dummy1 scope link trap" 241 + check_err $? "Unexpected nexthop entry after failure" 242 + 243 + nexthop_resource_check 1 244 + check_err $? "Wrong nexthop occupancy after failure" 245 + 246 + log_test "Single nexthop replace failure" 247 + 248 + $IP nexthop flush &> /dev/null 249 + nexthop_resource_set 9999 250 + } 251 + 252 + nexthop_single_in_group_replace_test() 253 + { 254 + RET=0 255 + 256 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 257 + $IP nexthop add id 2 via 192.0.2.3 dev dummy1 258 + $IP nexthop add id 10 group 1/2 259 + 260 + $IP nexthop replace id 1 via 192.0.2.4 dev dummy1 261 + check_err $? "Failed to replace nexthop when should not" 262 + 263 + nexthop_check "id 10" "id 10 group 1/2 trap" 264 + check_err $? "Unexpected nexthop group entry" 265 + 266 + nexthop_resource_check 4 267 + check_err $? "Wrong nexthop occupancy" 268 + 269 + log_test "Single nexthop replace while in group" 270 + 271 + $IP nexthop flush &> /dev/null 272 + } 273 + 274 + nexthop_single_in_group_replace_err_test() 275 + { 276 + RET=0 277 + 278 + nexthop_resource_set 5 279 + 280 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 281 + $IP nexthop add id 2 via 192.0.2.3 dev dummy1 282 + $IP nexthop add id 10 group 1/2 283 + 284 + $IP nexthop replace id 1 via 192.0.2.4 dev dummy1 &> /dev/null 285 + check_fail $? "Nexthop replacement succeeded when should fail" 286 + 287 + nexthop_check "id 1" "id 1 via 192.0.2.2 dev dummy1 scope link trap" 288 + check_err $? "Unexpected nexthop entry after failure" 289 + 290 + nexthop_check "id 10" "id 10 group 1/2 trap" 291 + check_err $? "Unexpected nexthop group entry after failure" 292 + 293 + nexthop_resource_check 4 294 + check_err $? "Wrong nexthop occupancy" 295 + 296 + log_test "Single nexthop replace while in group failure" 297 + 298 + $IP nexthop flush &> /dev/null 299 + nexthop_resource_set 9999 300 + } 301 + 302 + nexthop_single_in_group_delete_test() 303 + { 304 + RET=0 305 + 306 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 307 + $IP nexthop add id 2 via 192.0.2.3 dev dummy1 308 + $IP nexthop add id 10 group 1/2 309 + 310 + $IP nexthop del id 1 311 + nexthop_check "id 10" "id 10 group 2 trap" 312 + check_err $? "Unexpected nexthop group entry" 313 + 314 + nexthop_resource_check 2 315 + check_err $? "Wrong nexthop occupancy" 316 + 317 + log_test "Single nexthop delete while in group" 318 + 319 + $IP nexthop flush &> /dev/null 320 + } 321 + 322 + nexthop_single_in_group_delete_err_test() 323 + { 324 + RET=0 325 + 326 + # First, nexthop 1 will be deleted, which will reduce the occupancy to 327 + # 5. Afterwards, a replace notification will be sent for nexthop group 328 + # 10 with only two nexthops. Since the new group is allocated before 329 + # the old is deleted, the replacement will fail as it will result in an 330 + # occupancy of 7. 331 + nexthop_resource_set 6 332 + 333 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 334 + $IP nexthop add id 2 via 192.0.2.3 dev dummy1 335 + $IP nexthop add id 3 via 192.0.2.4 dev dummy1 336 + $IP nexthop add id 10 group 1/2/3 337 + 338 + $IP nexthop del id 1 339 + 340 + nexthop_resource_check 5 341 + check_err $? "Wrong nexthop occupancy" 342 + 343 + log_test "Single nexthop delete while in group failure" 344 + 345 + $IP nexthop flush &> /dev/null 346 + nexthop_resource_set 9999 347 + } 348 + 349 + nexthop_replay_test() 350 + { 351 + RET=0 352 + 353 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 354 + $IP nexthop add id 2 via 192.0.2.3 dev dummy1 355 + $IP nexthop add id 10 group 1/2 356 + 357 + $DEVLINK dev reload $DEVLINK_DEV 358 + check_err $? "Failed to reload when should not" 359 + 360 + nexthop_check "id 1" "id 1 via 192.0.2.2 dev dummy1 scope link trap" 361 + check_err $? "Unexpected nexthop entry after reload" 362 + 363 + nexthop_check "id 2" "id 2 via 192.0.2.3 dev dummy1 scope link trap" 364 + check_err $? "Unexpected nexthop entry after reload" 365 + 366 + nexthop_check "id 10" "id 10 group 1/2 trap" 367 + check_err $? "Unexpected nexthop group entry after reload" 368 + 369 + nexthop_resource_check 4 370 + check_err $? "Wrong nexthop occupancy" 371 + 372 + log_test "Nexthop replay" 373 + 374 + $IP nexthop flush &> /dev/null 375 + } 376 + 377 + nexthop_replay_err_test() 378 + { 379 + RET=0 380 + 381 + $IP nexthop add id 1 via 192.0.2.2 dev dummy1 382 + $IP nexthop add id 2 via 192.0.2.3 dev dummy1 383 + $IP nexthop add id 10 group 1/2 384 + 385 + # Reduce size of nexthop resource so that reload will fail. 386 + $DEVLINK resource set $DEVLINK_DEV path nexthops size 3 387 + $DEVLINK dev reload $DEVLINK_DEV &> /dev/null 388 + check_fail $? "Reload succeeded when should fail" 389 + 390 + $DEVLINK resource set $DEVLINK_DEV path nexthops size 9999 391 + $DEVLINK dev reload $DEVLINK_DEV 392 + check_err $? "Failed to reload when should not" 393 + 394 + log_test "Nexthop replay failure" 395 + 396 + $IP nexthop flush &> /dev/null 397 + } 398 + 399 + setup_prepare() 400 + { 401 + local netdev 402 + 403 + modprobe netdevsim &> /dev/null 404 + 405 + echo "$DEV_ADDR 1" > ${NETDEVSIM_PATH}/new_device 406 + while [ ! -d $SYSFS_NET_DIR ] ; do :; done 407 + 408 + set -e 409 + 410 + ip netns add testns1 411 + devlink dev reload $DEVLINK_DEV netns testns1 412 + 413 + IP="ip -netns testns1" 414 + DEVLINK="devlink -N testns1" 415 + 416 + $IP link add name dummy1 up type dummy 417 + $IP address add 192.0.2.1/24 dev dummy1 418 + 419 + set +e 420 + } 421 + 422 + cleanup() 423 + { 424 + pre_cleanup 425 + ip netns del testns1 426 + echo "$DEV_ADDR" > ${NETDEVSIM_PATH}/del_device 427 + modprobe -r netdevsim &> /dev/null 428 + } 429 + 430 + trap cleanup EXIT 431 + 432 + setup_prepare 433 + 434 + tests_run 435 + 436 + exit $EXIT_STATUS