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 					&params);
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, &params);
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, &params);
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 					&params);
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, &params);
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, &params);
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