GNU Linux-libre 4.19.264-gnu1
[releases.git] / drivers / net / netdevsim / devlink.c
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 */
26 static struct net *nsim_devlink_net(struct devlink *devlink)
27 {
28         return &init_net;
29 }
30
31 /* IPv4
32  */
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
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  */
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
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
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
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
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  */
199 static struct net *nsim_to_net(struct netdevsim *ns)
200 {
201         return &init_net;
202 }
203
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
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 */
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
275 void nsim_devlink_exit(void)
276 {
277         unregister_pernet_subsys(&nsim_devlink_net_ops);
278         nsim_fib_exit();
279 }
280
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 }