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
23 static unsigned int nsim_devlink_id;
24
25 /* place holder until devlink and namespaces is sorted out */
nsim_devlink_net(struct devlink * devlink)26 static struct net *nsim_devlink_net(struct devlink *devlink)
27 {
28 return &init_net;
29 }
30
31 /* IPv4
32 */
nsim_ipv4_fib_resource_occ_get(void * priv)33 static u64 nsim_ipv4_fib_resource_occ_get(void *priv)
34 {
35 struct net *net = priv;
36
37 return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, false);
38 }
39
nsim_ipv4_fib_rules_res_occ_get(void * priv)40 static u64 nsim_ipv4_fib_rules_res_occ_get(void *priv)
41 {
42 struct net *net = priv;
43
44 return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, false);
45 }
46
47 /* IPv6
48 */
nsim_ipv6_fib_resource_occ_get(void * priv)49 static u64 nsim_ipv6_fib_resource_occ_get(void *priv)
50 {
51 struct net *net = priv;
52
53 return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, false);
54 }
55
nsim_ipv6_fib_rules_res_occ_get(void * priv)56 static u64 nsim_ipv6_fib_rules_res_occ_get(void *priv)
57 {
58 struct net *net = priv;
59
60 return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, false);
61 }
62
devlink_resources_register(struct devlink * devlink)63 static int devlink_resources_register(struct devlink *devlink)
64 {
65 struct devlink_resource_size_params params = {
66 .size_max = (u64)-1,
67 .size_granularity = 1,
68 .unit = DEVLINK_RESOURCE_UNIT_ENTRY
69 };
70 struct net *net = nsim_devlink_net(devlink);
71 int err;
72 u64 n;
73
74 /* Resources for IPv4 */
75 err = devlink_resource_register(devlink, "IPv4", (u64)-1,
76 NSIM_RESOURCE_IPV4,
77 DEVLINK_RESOURCE_ID_PARENT_TOP,
78 ¶ms);
79 if (err) {
80 pr_err("Failed to register IPv4 top resource\n");
81 goto out;
82 }
83
84 n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, true);
85 err = devlink_resource_register(devlink, "fib", n,
86 NSIM_RESOURCE_IPV4_FIB,
87 NSIM_RESOURCE_IPV4, ¶ms);
88 if (err) {
89 pr_err("Failed to register IPv4 FIB resource\n");
90 return err;
91 }
92
93 n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, true);
94 err = devlink_resource_register(devlink, "fib-rules", n,
95 NSIM_RESOURCE_IPV4_FIB_RULES,
96 NSIM_RESOURCE_IPV4, ¶ms);
97 if (err) {
98 pr_err("Failed to register IPv4 FIB rules resource\n");
99 return err;
100 }
101
102 /* Resources for IPv6 */
103 err = devlink_resource_register(devlink, "IPv6", (u64)-1,
104 NSIM_RESOURCE_IPV6,
105 DEVLINK_RESOURCE_ID_PARENT_TOP,
106 ¶ms);
107 if (err) {
108 pr_err("Failed to register IPv6 top resource\n");
109 goto out;
110 }
111
112 n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, true);
113 err = devlink_resource_register(devlink, "fib", n,
114 NSIM_RESOURCE_IPV6_FIB,
115 NSIM_RESOURCE_IPV6, ¶ms);
116 if (err) {
117 pr_err("Failed to register IPv6 FIB resource\n");
118 return err;
119 }
120
121 n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, true);
122 err = devlink_resource_register(devlink, "fib-rules", n,
123 NSIM_RESOURCE_IPV6_FIB_RULES,
124 NSIM_RESOURCE_IPV6, ¶ms);
125 if (err) {
126 pr_err("Failed to register IPv6 FIB rules resource\n");
127 return err;
128 }
129
130 devlink_resource_occ_get_register(devlink,
131 NSIM_RESOURCE_IPV4_FIB,
132 nsim_ipv4_fib_resource_occ_get,
133 net);
134 devlink_resource_occ_get_register(devlink,
135 NSIM_RESOURCE_IPV4_FIB_RULES,
136 nsim_ipv4_fib_rules_res_occ_get,
137 net);
138 devlink_resource_occ_get_register(devlink,
139 NSIM_RESOURCE_IPV6_FIB,
140 nsim_ipv6_fib_resource_occ_get,
141 net);
142 devlink_resource_occ_get_register(devlink,
143 NSIM_RESOURCE_IPV6_FIB_RULES,
144 nsim_ipv6_fib_rules_res_occ_get,
145 net);
146 out:
147 return err;
148 }
149
nsim_devlink_reload(struct devlink * devlink,struct netlink_ext_ack * extack)150 static int nsim_devlink_reload(struct devlink *devlink,
151 struct netlink_ext_ack *extack)
152 {
153 enum nsim_resource_id res_ids[] = {
154 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
155 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
156 };
157 struct net *net = nsim_devlink_net(devlink);
158 int i;
159
160 for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
161 int err;
162 u64 val;
163
164 err = devlink_resource_size_get(devlink, res_ids[i], &val);
165 if (!err) {
166 err = nsim_fib_set_max(net, res_ids[i], val, extack);
167 if (err)
168 return err;
169 }
170 }
171
172 return 0;
173 }
174
nsim_devlink_net_reset(struct net * net)175 static void nsim_devlink_net_reset(struct net *net)
176 {
177 enum nsim_resource_id res_ids[] = {
178 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
179 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
180 };
181 int i;
182
183 for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
184 if (nsim_fib_set_max(net, res_ids[i], (u64)-1, NULL)) {
185 pr_err("Failed to reset limit for resource %u\n",
186 res_ids[i]);
187 }
188 }
189 }
190
191 static const struct devlink_ops nsim_devlink_ops = {
192 .reload = nsim_devlink_reload,
193 };
194
195 /* once devlink / namespace issues are sorted out
196 * this needs to be net in which a devlink instance
197 * is to be created. e.g., dev_net(ns->netdev)
198 */
nsim_to_net(struct netdevsim * ns)199 static struct net *nsim_to_net(struct netdevsim *ns)
200 {
201 return &init_net;
202 }
203
nsim_devlink_teardown(struct netdevsim * ns)204 void nsim_devlink_teardown(struct netdevsim *ns)
205 {
206 if (ns->devlink) {
207 struct net *net = nsim_to_net(ns);
208 bool *reg_devlink = net_generic(net, nsim_devlink_id);
209
210 devlink_resources_unregister(ns->devlink, NULL);
211 devlink_unregister(ns->devlink);
212 devlink_free(ns->devlink);
213 ns->devlink = NULL;
214
215 nsim_devlink_net_reset(net);
216 *reg_devlink = true;
217 }
218 }
219
nsim_devlink_setup(struct netdevsim * ns)220 int nsim_devlink_setup(struct netdevsim *ns)
221 {
222 struct net *net = nsim_to_net(ns);
223 bool *reg_devlink = net_generic(net, nsim_devlink_id);
224 struct devlink *devlink;
225 int err;
226
227 /* only one device per namespace controls devlink */
228 if (!*reg_devlink) {
229 ns->devlink = NULL;
230 return 0;
231 }
232
233 devlink = devlink_alloc(&nsim_devlink_ops, 0);
234 if (!devlink)
235 return -ENOMEM;
236
237 err = devlink_register(devlink, &ns->dev);
238 if (err)
239 goto err_devlink_free;
240
241 err = devlink_resources_register(devlink);
242 if (err)
243 goto err_dl_unregister;
244
245 ns->devlink = devlink;
246
247 *reg_devlink = false;
248
249 return 0;
250
251 err_dl_unregister:
252 devlink_unregister(devlink);
253 err_devlink_free:
254 devlink_free(devlink);
255
256 return err;
257 }
258
259 /* Initialize per network namespace state */
nsim_devlink_netns_init(struct net * net)260 static int __net_init nsim_devlink_netns_init(struct net *net)
261 {
262 bool *reg_devlink = net_generic(net, nsim_devlink_id);
263
264 *reg_devlink = true;
265
266 return 0;
267 }
268
269 static struct pernet_operations nsim_devlink_net_ops = {
270 .init = nsim_devlink_netns_init,
271 .id = &nsim_devlink_id,
272 .size = sizeof(bool),
273 };
274
nsim_devlink_exit(void)275 void nsim_devlink_exit(void)
276 {
277 unregister_pernet_subsys(&nsim_devlink_net_ops);
278 nsim_fib_exit();
279 }
280
nsim_devlink_init(void)281 int nsim_devlink_init(void)
282 {
283 int err;
284
285 err = nsim_fib_init();
286 if (err)
287 goto err_out;
288
289 err = register_pernet_subsys(&nsim_devlink_net_ops);
290 if (err)
291 nsim_fib_exit();
292
293 err_out:
294 return err;
295 }
296