aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2018-03-29 14:10:31 -0400
committerDavid S. Miller <davem@davemloft.net>2018-03-29 14:10:31 -0400
commitb349e0b5ec5d7be57ac243fb08ae8b994c928165 (patch)
tree51418fe5934da050f867b26bf0ee836e14cbc38a
parent6e2135ce54b72f8b2b20cef2a06ae6acb77a3431 (diff)
parent37923ed6b8cea94d7d76038e2f72c57a0b45daab (diff)
Merge branch 'net-Allow-FIB-notifiers-to-fail-add-and-replace'
David Ahern says: ==================== net: Allow FIB notifiers to fail add and replace I wanted to revisit how resource overload is handled for hardware offload of FIB entries and rules. At the moment, the in-kernel fib notifier can tell a driver about a route or rule add, replace, and delete, but the notifier can not affect the action. Specifically, in the case of mlxsw if a route or rule add is going to overflow the ASIC resources the only recourse is to abort hardware offload. Aborting offload is akin to taking down the switch as the path from data plane to the control plane simply can not support the traffic bandwidth of the front panel ports. Further, the current state of FIB notifiers is inconsistent with other resources where a driver can affect a user request - e.g., enslavement of a port into a bridge or a VRF. As a result of the work done over the past 3+ years, I believe we are at a point where we can bring consistency to the stack and offloads, and reliably allow the FIB notifiers to fail a request, pushing an error along with a suitable error message back to the user. Rather than aborting offload when the switch is out of resources, userspace is simply prevented from adding more routes and has a clear indication of why. This set does not resolve the corner case where rules or routes not supported by the device are installed prior to the driver getting loaded and registering for FIB notifications. In that case, hardware offload has not been established and it can refuse to offload anything, sending errors back to userspace via extack. Since conceptually the driver owns the netdevices associated with its asic, this corner case mainly applies to unsupported rules and any races during the bringup phase. Patch 1 fixes call_fib_notifiers to extract the errno from the encoded response from handlers. Patches 2-5 allow the call to call_fib_notifiers to fail the add or replace of a route or rule. Patch 6 adds a simple resource controller to netdevsim to illustrate how a FIB resource controller can limit the number of route entries. Changes since RFC - correct return code for call_fib_notifier - dropped patch 6 exporting devlink symbols - limited example resource controller to init_net only - updated Kconfig for netdevsim to use MAY_USE_DEVLINK - updated cover letter regarding startup case noted by Ido ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/Kconfig1
-rw-r--r--drivers/net/netdevsim/Makefile4
-rw-r--r--drivers/net/netdevsim/devlink.c294
-rw-r--r--drivers/net/netdevsim/fib.c263
-rw-r--r--drivers/net/netdevsim/netdev.c12
-rw-r--r--drivers/net/netdevsim/netdevsim.h43
-rw-r--r--net/core/fib_notifier.c10
-rw-r--r--net/core/fib_rules.c6
-rw-r--r--net/ipv4/fib_trie.c27
-rw-r--r--net/ipv6/ip6_fib.c16
10 files changed, 664 insertions, 12 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 08b85215c2be..891846655000 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -500,6 +500,7 @@ source "drivers/net/hyperv/Kconfig"
500config NETDEVSIM 500config NETDEVSIM
501 tristate "Simulated networking device" 501 tristate "Simulated networking device"
502 depends on DEBUG_FS 502 depends on DEBUG_FS
503 depends on MAY_USE_DEVLINK
503 help 504 help
504 This driver is a developer testing tool and software model that can 505 This driver is a developer testing tool and software model that can
505 be used to test various control path networking APIs, especially 506 be used to test various control path networking APIs, especially
diff --git a/drivers/net/netdevsim/Makefile b/drivers/net/netdevsim/Makefile
index 09388c06171d..449b2a1a1800 100644
--- a/drivers/net/netdevsim/Makefile
+++ b/drivers/net/netdevsim/Makefile
@@ -9,3 +9,7 @@ ifeq ($(CONFIG_BPF_SYSCALL),y)
9netdevsim-objs += \ 9netdevsim-objs += \
10 bpf.o 10 bpf.o
11endif 11endif
12
13ifneq ($(CONFIG_NET_DEVLINK),)
14netdevsim-objs += devlink.o fib.o
15endif
diff --git a/drivers/net/netdevsim/devlink.c b/drivers/net/netdevsim/devlink.c
new file mode 100644
index 000000000000..bbdcf064ba10
--- /dev/null
+++ b/drivers/net/netdevsim/devlink.c
@@ -0,0 +1,294 @@
1/*
2 * Copyright (c) 2018 Cumulus Networks. All rights reserved.
3 * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
4 *
5 * This software is licensed under the GNU General License Version 2,
6 * June 1991 as shown in the file COPYING in the top-level directory of this
7 * source tree.
8 *
9 * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
10 * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
11 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12 * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
13 * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
14 * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
15 */
16
17#include <linux/device.h>
18#include <net/devlink.h>
19#include <net/netns/generic.h>
20
21#include "netdevsim.h"
22
23static unsigned int nsim_devlink_id;
24
25/* place holder until devlink and namespaces is sorted out */
26static struct net *nsim_devlink_net(struct devlink *devlink)
27{
28 return &init_net;
29}
30
31/* IPv4
32 */
33static u64 nsim_ipv4_fib_resource_occ_get(struct devlink *devlink)
34{
35 struct net *net = nsim_devlink_net(devlink);
36
37 return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, false);
38}
39
40static struct devlink_resource_ops nsim_ipv4_fib_res_ops = {
41 .occ_get = nsim_ipv4_fib_resource_occ_get,
42};
43
44static u64 nsim_ipv4_fib_rules_res_occ_get(struct devlink *devlink)
45{
46 struct net *net = nsim_devlink_net(devlink);
47
48 return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, false);
49}
50
51static struct devlink_resource_ops nsim_ipv4_fib_rules_res_ops = {
52 .occ_get = nsim_ipv4_fib_rules_res_occ_get,
53};
54
55/* IPv6
56 */
57static u64 nsim_ipv6_fib_resource_occ_get(struct devlink *devlink)
58{
59 struct net *net = nsim_devlink_net(devlink);
60
61 return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, false);
62}
63
64static struct devlink_resource_ops nsim_ipv6_fib_res_ops = {
65 .occ_get = nsim_ipv6_fib_resource_occ_get,
66};
67
68static u64 nsim_ipv6_fib_rules_res_occ_get(struct devlink *devlink)
69{
70 struct net *net = nsim_devlink_net(devlink);
71
72 return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, false);
73}
74
75static struct devlink_resource_ops nsim_ipv6_fib_rules_res_ops = {
76 .occ_get = nsim_ipv6_fib_rules_res_occ_get,
77};
78
79static int devlink_resources_register(struct devlink *devlink)
80{
81 struct devlink_resource_size_params params = {
82 .size_max = (u64)-1,
83 .size_granularity = 1,
84 .unit = DEVLINK_RESOURCE_UNIT_ENTRY
85 };
86 struct net *net = nsim_devlink_net(devlink);
87 int err;
88 u64 n;
89
90 /* Resources for IPv4 */
91 err = devlink_resource_register(devlink, "IPv4", (u64)-1,
92 NSIM_RESOURCE_IPV4,
93 DEVLINK_RESOURCE_ID_PARENT_TOP,
94 &params, NULL);
95 if (err) {
96 pr_err("Failed to register IPv4 top resource\n");
97 goto out;
98 }
99
100 n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, true);
101 err = devlink_resource_register(devlink, "fib", n,
102 NSIM_RESOURCE_IPV4_FIB,
103 NSIM_RESOURCE_IPV4,
104 &params, &nsim_ipv4_fib_res_ops);
105 if (err) {
106 pr_err("Failed to register IPv4 FIB resource\n");
107 return err;
108 }
109
110 n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, true);
111 err = devlink_resource_register(devlink, "fib-rules", n,
112 NSIM_RESOURCE_IPV4_FIB_RULES,
113 NSIM_RESOURCE_IPV4,
114 &params, &nsim_ipv4_fib_rules_res_ops);
115 if (err) {
116 pr_err("Failed to register IPv4 FIB rules resource\n");
117 return err;
118 }
119
120 /* Resources for IPv6 */
121 err = devlink_resource_register(devlink, "IPv6", (u64)-1,
122 NSIM_RESOURCE_IPV6,
123 DEVLINK_RESOURCE_ID_PARENT_TOP,
124 &params, NULL);
125 if (err) {
126 pr_err("Failed to register IPv6 top resource\n");
127 goto out;
128 }
129
130 n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, true);
131 err = devlink_resource_register(devlink, "fib", n,
132 NSIM_RESOURCE_IPV6_FIB,
133 NSIM_RESOURCE_IPV6,
134 &params, &nsim_ipv6_fib_res_ops);
135 if (err) {
136 pr_err("Failed to register IPv6 FIB resource\n");
137 return err;
138 }
139
140 n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, true);
141 err = devlink_resource_register(devlink, "fib-rules", n,
142 NSIM_RESOURCE_IPV6_FIB_RULES,
143 NSIM_RESOURCE_IPV6,
144 &params, &nsim_ipv6_fib_rules_res_ops);
145 if (err) {
146 pr_err("Failed to register IPv6 FIB rules resource\n");
147 return err;
148 }
149out:
150 return err;
151}
152
153static int nsim_devlink_reload(struct devlink *devlink)
154{
155 enum nsim_resource_id res_ids[] = {
156 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
157 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
158 };
159 struct net *net = nsim_devlink_net(devlink);
160 int i;
161
162 for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
163 int err;
164 u64 val;
165
166 err = devlink_resource_size_get(devlink, res_ids[i], &val);
167 if (!err) {
168 err = nsim_fib_set_max(net, res_ids[i], val);
169 if (err)
170 return err;
171 }
172 }
173
174 return 0;
175}
176
177static void nsim_devlink_net_reset(struct net *net)
178{
179 enum nsim_resource_id res_ids[] = {
180 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
181 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
182 };
183 int i;
184
185 for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
186 if (nsim_fib_set_max(net, res_ids[i], (u64)-1)) {
187 pr_err("Failed to reset limit for resource %u\n",
188 res_ids[i]);
189 }
190 }
191}
192
193static const struct devlink_ops nsim_devlink_ops = {
194 .reload = nsim_devlink_reload,
195};
196
197/* once devlink / namespace issues are sorted out
198 * this needs to be net in which a devlink instance
199 * is to be created. e.g., dev_net(ns->netdev)
200 */
201static struct net *nsim_to_net(struct netdevsim *ns)
202{
203 return &init_net;
204}
205
206void nsim_devlink_teardown(struct netdevsim *ns)
207{
208 if (ns->devlink) {
209 struct net *net = nsim_to_net(ns);
210 bool *reg_devlink = net_generic(net, nsim_devlink_id);
211
212 devlink_unregister(ns->devlink);
213 devlink_free(ns->devlink);
214 ns->devlink = NULL;
215
216 nsim_devlink_net_reset(net);
217 *reg_devlink = true;
218 }
219}
220
221void nsim_devlink_setup(struct netdevsim *ns)
222{
223 struct net *net = nsim_to_net(ns);
224 bool *reg_devlink = net_generic(net, nsim_devlink_id);
225 struct devlink *devlink;
226 int err = -ENOMEM;
227
228 /* only one device per namespace controls devlink */
229 if (!*reg_devlink) {
230 ns->devlink = NULL;
231 return;
232 }
233
234 devlink = devlink_alloc(&nsim_devlink_ops, 0);
235 if (!devlink)
236 return;
237
238 err = devlink_register(devlink, &ns->dev);
239 if (err)
240 goto err_devlink_free;
241
242 err = devlink_resources_register(devlink);
243 if (err)
244 goto err_dl_unregister;
245
246 ns->devlink = devlink;
247
248 *reg_devlink = false;
249
250 return;
251
252err_dl_unregister:
253 devlink_unregister(devlink);
254err_devlink_free:
255 devlink_free(devlink);
256}
257
258/* Initialize per network namespace state */
259static int __net_init nsim_devlink_netns_init(struct net *net)
260{
261 bool *reg_devlink = net_generic(net, nsim_devlink_id);
262
263 *reg_devlink = true;
264
265 return 0;
266}
267
268static struct pernet_operations nsim_devlink_net_ops __net_initdata = {
269 .init = nsim_devlink_netns_init,
270 .id = &nsim_devlink_id,
271 .size = sizeof(bool),
272};
273
274void nsim_devlink_exit(void)
275{
276 unregister_pernet_subsys(&nsim_devlink_net_ops);
277 nsim_fib_exit();
278}
279
280int nsim_devlink_init(void)
281{
282 int err;
283
284 err = nsim_fib_init();
285 if (err)
286 goto err_out;
287
288 err = register_pernet_subsys(&nsim_devlink_net_ops);
289 if (err)
290 nsim_fib_exit();
291
292err_out:
293 return err;
294}
diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c
new file mode 100644
index 000000000000..0d105bafa261
--- /dev/null
+++ b/drivers/net/netdevsim/fib.c
@@ -0,0 +1,263 @@
1/*
2 * Copyright (c) 2018 Cumulus Networks. All rights reserved.
3 * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
4 *
5 * This software is licensed under the GNU General License Version 2,
6 * June 1991 as shown in the file COPYING in the top-level directory of this
7 * source tree.
8 *
9 * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
10 * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
11 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12 * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
13 * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
14 * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
15 */
16
17#include <net/fib_notifier.h>
18#include <net/ip_fib.h>
19#include <net/ip6_fib.h>
20#include <net/fib_rules.h>
21#include <net/netns/generic.h>
22
23#include "netdevsim.h"
24
25struct nsim_fib_entry {
26 u64 max;
27 u64 num;
28};
29
30struct nsim_per_fib_data {
31 struct nsim_fib_entry fib;
32 struct nsim_fib_entry rules;
33};
34
35struct nsim_fib_data {
36 struct nsim_per_fib_data ipv4;
37 struct nsim_per_fib_data ipv6;
38};
39
40static unsigned int nsim_fib_net_id;
41
42u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max)
43{
44 struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
45 struct nsim_fib_entry *entry;
46
47 switch (res_id) {
48 case NSIM_RESOURCE_IPV4_FIB:
49 entry = &fib_data->ipv4.fib;
50 break;
51 case NSIM_RESOURCE_IPV4_FIB_RULES:
52 entry = &fib_data->ipv4.rules;
53 break;
54 case NSIM_RESOURCE_IPV6_FIB:
55 entry = &fib_data->ipv6.fib;
56 break;
57 case NSIM_RESOURCE_IPV6_FIB_RULES:
58 entry = &fib_data->ipv6.rules;
59 break;
60 default:
61 return 0;
62 }
63
64 return max ? entry->max : entry->num;
65}
66
67int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val)
68{
69 struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
70 struct nsim_fib_entry *entry;
71 int err = 0;
72
73 switch (res_id) {
74 case NSIM_RESOURCE_IPV4_FIB:
75 entry = &fib_data->ipv4.fib;
76 break;
77 case NSIM_RESOURCE_IPV4_FIB_RULES:
78 entry = &fib_data->ipv4.rules;
79 break;
80 case NSIM_RESOURCE_IPV6_FIB:
81 entry = &fib_data->ipv6.fib;
82 break;
83 case NSIM_RESOURCE_IPV6_FIB_RULES:
84 entry = &fib_data->ipv6.rules;
85 break;
86 default:
87 return 0;
88 }
89
90 /* not allowing a new max to be less than curren occupancy
91 * --> no means of evicting entries
92 */
93 if (val < entry->num)
94 err = -EINVAL;
95 else
96 entry->max = val;
97
98 return err;
99}
100
101static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
102 struct netlink_ext_ack *extack)
103{
104 int err = 0;
105
106 if (add) {
107 if (entry->num < entry->max) {
108 entry->num++;
109 } else {
110 err = -ENOSPC;
111 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
112 }
113 } else {
114 entry->num--;
115 }
116
117 return err;
118}
119
120static int nsim_fib_rule_event(struct fib_notifier_info *info, bool add)
121{
122 struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
123 struct netlink_ext_ack *extack = info->extack;
124 int err = 0;
125
126 switch (info->family) {
127 case AF_INET:
128 err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
129 break;
130 case AF_INET6:
131 err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
132 break;
133 }
134
135 return err;
136}
137
138static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
139 struct netlink_ext_ack *extack)
140{
141 int err = 0;
142
143 if (add) {
144 if (entry->num < entry->max) {
145 entry->num++;
146 } else {
147 err = -ENOSPC;
148 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
149 }
150 } else {
151 entry->num--;
152 }
153
154 return err;
155}
156
157static int nsim_fib_event(struct fib_notifier_info *info, bool add)
158{
159 struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
160 struct netlink_ext_ack *extack = info->extack;
161 int err = 0;
162
163 switch (info->family) {
164 case AF_INET:
165 err = nsim_fib_account(&data->ipv4.fib, add, extack);
166 break;
167 case AF_INET6:
168 err = nsim_fib_account(&data->ipv6.fib, add, extack);
169 break;
170 }
171
172 return err;
173}
174
175static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
176 void *ptr)
177{
178 struct fib_notifier_info *info = ptr;
179 int err = 0;
180
181 switch (event) {
182 case FIB_EVENT_RULE_ADD: /* fall through */
183 case FIB_EVENT_RULE_DEL:
184 err = nsim_fib_rule_event(info, event == FIB_EVENT_RULE_ADD);
185 break;
186
187 case FIB_EVENT_ENTRY_ADD: /* fall through */
188 case FIB_EVENT_ENTRY_DEL:
189 err = nsim_fib_event(info, event == FIB_EVENT_ENTRY_ADD);
190 break;
191 }
192
193 return notifier_from_errno(err);
194}
195
196/* inconsistent dump, trying again */
197static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
198{
199 struct nsim_fib_data *data;
200 struct net *net;
201
202 rcu_read_lock();
203 for_each_net_rcu(net) {
204 data = net_generic(net, nsim_fib_net_id);
205
206 data->ipv4.fib.num = 0ULL;
207 data->ipv4.rules.num = 0ULL;
208
209 data->ipv6.fib.num = 0ULL;
210 data->ipv6.rules.num = 0ULL;
211 }
212 rcu_read_unlock();
213}
214
215static struct notifier_block nsim_fib_nb = {
216 .notifier_call = nsim_fib_event_nb,
217};
218
219/* Initialize per network namespace state */
220static int __net_init nsim_fib_netns_init(struct net *net)
221{
222 struct nsim_fib_data *data = net_generic(net, nsim_fib_net_id);
223
224 data->ipv4.fib.max = (u64)-1;
225 data->ipv4.rules.max = (u64)-1;
226
227 data->ipv6.fib.max = (u64)-1;
228 data->ipv6.rules.max = (u64)-1;
229
230 return 0;
231}
232
233static struct pernet_operations nsim_fib_net_ops __net_initdata = {
234 .init = nsim_fib_netns_init,
235 .id = &nsim_fib_net_id,
236 .size = sizeof(struct nsim_fib_data),
237};
238
239void nsim_fib_exit(void)
240{
241 unregister_pernet_subsys(&nsim_fib_net_ops);
242 unregister_fib_notifier(&nsim_fib_nb);
243}
244
245int nsim_fib_init(void)
246{
247 int err;
248
249 err = register_pernet_subsys(&nsim_fib_net_ops);
250 if (err < 0) {
251 pr_err("Failed to register pernet subsystem\n");
252 goto err_out;
253 }
254
255 err = register_fib_notifier(&nsim_fib_nb, nsim_fib_dump_inconsistent);
256 if (err < 0) {
257 pr_err("Failed to register fib notifier\n");
258 goto err_out;
259 }
260
261err_out:
262 return err;
263}
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index 3fd567928f3d..8b30ab3ea2c2 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -167,6 +167,8 @@ static int nsim_init(struct net_device *dev)
167 167
168 SET_NETDEV_DEV(dev, &ns->dev); 168 SET_NETDEV_DEV(dev, &ns->dev);
169 169
170 nsim_devlink_setup(ns);
171
170 return 0; 172 return 0;
171 173
172err_bpf_uninit: 174err_bpf_uninit:
@@ -180,6 +182,7 @@ static void nsim_uninit(struct net_device *dev)
180{ 182{
181 struct netdevsim *ns = netdev_priv(dev); 183 struct netdevsim *ns = netdev_priv(dev);
182 184
185 nsim_devlink_teardown(ns);
183 debugfs_remove_recursive(ns->ddir); 186 debugfs_remove_recursive(ns->ddir);
184 nsim_bpf_uninit(ns); 187 nsim_bpf_uninit(ns);
185} 188}
@@ -478,12 +481,18 @@ static int __init nsim_module_init(void)
478 if (err) 481 if (err)
479 goto err_debugfs_destroy; 482 goto err_debugfs_destroy;
480 483
481 err = rtnl_link_register(&nsim_link_ops); 484 err = nsim_devlink_init();
482 if (err) 485 if (err)
483 goto err_unreg_bus; 486 goto err_unreg_bus;
484 487
488 err = rtnl_link_register(&nsim_link_ops);
489 if (err)
490 goto err_dl_fini;
491
485 return 0; 492 return 0;
486 493
494err_dl_fini:
495 nsim_devlink_exit();
487err_unreg_bus: 496err_unreg_bus:
488 bus_unregister(&nsim_bus); 497 bus_unregister(&nsim_bus);
489err_debugfs_destroy: 498err_debugfs_destroy:
@@ -494,6 +503,7 @@ err_debugfs_destroy:
494static void __exit nsim_module_exit(void) 503static void __exit nsim_module_exit(void)
495{ 504{
496 rtnl_link_unregister(&nsim_link_ops); 505 rtnl_link_unregister(&nsim_link_ops);
506 nsim_devlink_exit();
497 bus_unregister(&nsim_bus); 507 bus_unregister(&nsim_bus);
498 debugfs_remove_recursive(nsim_ddir); 508 debugfs_remove_recursive(nsim_ddir);
499} 509}
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index ea081c10efb8..afb8cf90c0fd 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -64,6 +64,9 @@ struct netdevsim {
64 64
65 bool bpf_map_accept; 65 bool bpf_map_accept;
66 struct list_head bpf_bound_maps; 66 struct list_head bpf_bound_maps;
67#if IS_ENABLED(CONFIG_NET_DEVLINK)
68 struct devlink *devlink;
69#endif
67}; 70};
68 71
69extern struct dentry *nsim_ddir; 72extern struct dentry *nsim_ddir;
@@ -103,6 +106,46 @@ nsim_bpf_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
103} 106}
104#endif 107#endif
105 108
109#if IS_ENABLED(CONFIG_NET_DEVLINK)
110enum nsim_resource_id {
111 NSIM_RESOURCE_NONE, /* DEVLINK_RESOURCE_ID_PARENT_TOP */
112 NSIM_RESOURCE_IPV4,
113 NSIM_RESOURCE_IPV4_FIB,
114 NSIM_RESOURCE_IPV4_FIB_RULES,
115 NSIM_RESOURCE_IPV6,
116 NSIM_RESOURCE_IPV6_FIB,
117 NSIM_RESOURCE_IPV6_FIB_RULES,
118};
119
120void nsim_devlink_setup(struct netdevsim *ns);
121void nsim_devlink_teardown(struct netdevsim *ns);
122
123int nsim_devlink_init(void);
124void nsim_devlink_exit(void);
125
126int nsim_fib_init(void);
127void nsim_fib_exit(void);
128u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max);
129int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val);
130#else
131static inline void nsim_devlink_setup(struct netdevsim *ns)
132{
133}
134
135static inline void nsim_devlink_teardown(struct netdevsim *ns)
136{
137}
138
139static inline int nsim_devlink_init(void)
140{
141 return 0;
142}
143
144static inline void nsim_devlink_exit(void)
145{
146}
147#endif
148
106static inline struct netdevsim *to_nsim(struct device *ptr) 149static inline struct netdevsim *to_nsim(struct device *ptr)
107{ 150{
108 return container_of(ptr, struct netdevsim, dev); 151 return container_of(ptr, struct netdevsim, dev);
diff --git a/net/core/fib_notifier.c b/net/core/fib_notifier.c
index 614b985c92a4..13a40b831d6d 100644
--- a/net/core/fib_notifier.c
+++ b/net/core/fib_notifier.c
@@ -13,16 +13,22 @@ int call_fib_notifier(struct notifier_block *nb, struct net *net,
13 enum fib_event_type event_type, 13 enum fib_event_type event_type,
14 struct fib_notifier_info *info) 14 struct fib_notifier_info *info)
15{ 15{
16 int err;
17
16 info->net = net; 18 info->net = net;
17 return nb->notifier_call(nb, event_type, info); 19 err = nb->notifier_call(nb, event_type, info);
20 return notifier_to_errno(err);
18} 21}
19EXPORT_SYMBOL(call_fib_notifier); 22EXPORT_SYMBOL(call_fib_notifier);
20 23
21int call_fib_notifiers(struct net *net, enum fib_event_type event_type, 24int call_fib_notifiers(struct net *net, enum fib_event_type event_type,
22 struct fib_notifier_info *info) 25 struct fib_notifier_info *info)
23{ 26{
27 int err;
28
24 info->net = net; 29 info->net = net;
25 return atomic_notifier_call_chain(&fib_chain, event_type, info); 30 err = atomic_notifier_call_chain(&fib_chain, event_type, info);
31 return notifier_to_errno(err);
26} 32}
27EXPORT_SYMBOL(call_fib_notifiers); 33EXPORT_SYMBOL(call_fib_notifiers);
28 34
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index 9d87ce868402..33958f84c173 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -631,6 +631,11 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh,
631 if (err < 0) 631 if (err < 0)
632 goto errout_free; 632 goto errout_free;
633 633
634 err = call_fib_rule_notifiers(net, FIB_EVENT_RULE_ADD, rule, ops,
635 extack);
636 if (err < 0)
637 goto errout_free;
638
634 list_for_each_entry(r, &ops->rules_list, list) { 639 list_for_each_entry(r, &ops->rules_list, list) {
635 if (r->pref > rule->pref) 640 if (r->pref > rule->pref)
636 break; 641 break;
@@ -667,7 +672,6 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh,
667 if (rule->tun_id) 672 if (rule->tun_id)
668 ip_tunnel_need_metadata(); 673 ip_tunnel_need_metadata();
669 674
670 call_fib_rule_notifiers(net, FIB_EVENT_RULE_ADD, rule, ops, extack);
671 notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid); 675 notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid);
672 flush_route_cache(ops); 676 flush_route_cache(ops);
673 rules_ops_put(ops); 677 rules_ops_put(ops);
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index fac0b73e24d1..3dcffd3ce98c 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1065,6 +1065,9 @@ noleaf:
1065 return -ENOMEM; 1065 return -ENOMEM;
1066} 1066}
1067 1067
1068/* fib notifier for ADD is sent before calling fib_insert_alias with
1069 * the expectation that the only possible failure ENOMEM
1070 */
1068static int fib_insert_alias(struct trie *t, struct key_vector *tp, 1071static int fib_insert_alias(struct trie *t, struct key_vector *tp,
1069 struct key_vector *l, struct fib_alias *new, 1072 struct key_vector *l, struct fib_alias *new,
1070 struct fib_alias *fa, t_key key) 1073 struct fib_alias *fa, t_key key)
@@ -1216,8 +1219,13 @@ int fib_table_insert(struct net *net, struct fib_table *tb,
1216 new_fa->tb_id = tb->tb_id; 1219 new_fa->tb_id = tb->tb_id;
1217 new_fa->fa_default = -1; 1220 new_fa->fa_default = -1;
1218 1221
1219 call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, 1222 err = call_fib_entry_notifiers(net,
1220 key, plen, new_fa, extack); 1223 FIB_EVENT_ENTRY_REPLACE,
1224 key, plen, new_fa,
1225 extack);
1226 if (err)
1227 goto out_free_new_fa;
1228
1221 rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, 1229 rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen,
1222 tb->tb_id, &cfg->fc_nlinfo, nlflags); 1230 tb->tb_id, &cfg->fc_nlinfo, nlflags);
1223 1231
@@ -1263,21 +1271,32 @@ int fib_table_insert(struct net *net, struct fib_table *tb,
1263 new_fa->tb_id = tb->tb_id; 1271 new_fa->tb_id = tb->tb_id;
1264 new_fa->fa_default = -1; 1272 new_fa->fa_default = -1;
1265 1273
1274 err = call_fib_entry_notifiers(net, event, key, plen, new_fa, extack);
1275 if (err)
1276 goto out_free_new_fa;
1277
1266 /* Insert new entry to the list. */ 1278 /* Insert new entry to the list. */
1267 err = fib_insert_alias(t, tp, l, new_fa, fa, key); 1279 err = fib_insert_alias(t, tp, l, new_fa, fa, key);
1268 if (err) 1280 if (err)
1269 goto out_free_new_fa; 1281 goto out_fib_notif;
1270 1282
1271 if (!plen) 1283 if (!plen)
1272 tb->tb_num_default++; 1284 tb->tb_num_default++;
1273 1285
1274 rt_cache_flush(cfg->fc_nlinfo.nl_net); 1286 rt_cache_flush(cfg->fc_nlinfo.nl_net);
1275 call_fib_entry_notifiers(net, event, key, plen, new_fa, extack);
1276 rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id, 1287 rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id,
1277 &cfg->fc_nlinfo, nlflags); 1288 &cfg->fc_nlinfo, nlflags);
1278succeeded: 1289succeeded:
1279 return 0; 1290 return 0;
1280 1291
1292out_fib_notif:
1293 /* notifier was sent that entry would be added to trie, but
1294 * the add failed and need to recover. Only failure for
1295 * fib_insert_alias is ENOMEM.
1296 */
1297 NL_SET_ERR_MSG(extack, "Failed to insert route into trie");
1298 call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, key,
1299 plen, new_fa, NULL);
1281out_free_new_fa: 1300out_free_new_fa:
1282 kmem_cache_free(fn_alias_kmem, new_fa); 1301 kmem_cache_free(fn_alias_kmem, new_fa);
1283out: 1302out:
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 908b8e5b615a..deab2db6692e 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -1007,12 +1007,16 @@ add:
1007 if (err) 1007 if (err)
1008 return err; 1008 return err;
1009 1009
1010 err = call_fib6_entry_notifiers(info->nl_net,
1011 FIB_EVENT_ENTRY_ADD,
1012 rt, extack);
1013 if (err)
1014 return err;
1015
1010 rcu_assign_pointer(rt->rt6_next, iter); 1016 rcu_assign_pointer(rt->rt6_next, iter);
1011 atomic_inc(&rt->rt6i_ref); 1017 atomic_inc(&rt->rt6i_ref);
1012 rcu_assign_pointer(rt->rt6i_node, fn); 1018 rcu_assign_pointer(rt->rt6i_node, fn);
1013 rcu_assign_pointer(*ins, rt); 1019 rcu_assign_pointer(*ins, rt);
1014 call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_ADD,
1015 rt, extack);
1016 if (!info->skip_notify) 1020 if (!info->skip_notify)
1017 inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags); 1021 inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
1018 info->nl_net->ipv6.rt6_stats->fib_rt_entries++; 1022 info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
@@ -1036,12 +1040,16 @@ add:
1036 if (err) 1040 if (err)
1037 return err; 1041 return err;
1038 1042
1043 err = call_fib6_entry_notifiers(info->nl_net,
1044 FIB_EVENT_ENTRY_REPLACE,
1045 rt, extack);
1046 if (err)
1047 return err;
1048
1039 atomic_inc(&rt->rt6i_ref); 1049 atomic_inc(&rt->rt6i_ref);
1040 rcu_assign_pointer(rt->rt6i_node, fn); 1050 rcu_assign_pointer(rt->rt6i_node, fn);
1041 rt->rt6_next = iter->rt6_next; 1051 rt->rt6_next = iter->rt6_next;
1042 rcu_assign_pointer(*ins, rt); 1052 rcu_assign_pointer(*ins, rt);
1043 call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_REPLACE,
1044 rt, extack);
1045 if (!info->skip_notify) 1053 if (!info->skip_notify)
1046 inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE); 1054 inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE);
1047 if (!(fn->fn_flags & RTN_RTINFO)) { 1055 if (!(fn->fn_flags & RTN_RTINFO)) {