]> Git Repo - linux.git/blob - drivers/net/netdevsim/fib.c
Merge tag 'ieee802154-for-davem-2019-11-13' of git://git.kernel.org/pub/scm/linux...
[linux.git] / drivers / net / netdevsim / fib.c
1 /*
2  * Copyright (c) 2018 Cumulus Networks. All rights reserved.
3  * Copyright (c) 2018 David Ahern <[email protected]>
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/net_namespace.h>
22
23 #include "netdevsim.h"
24
25 struct nsim_fib_entry {
26         u64 max;
27         u64 num;
28 };
29
30 struct nsim_per_fib_data {
31         struct nsim_fib_entry fib;
32         struct nsim_fib_entry rules;
33 };
34
35 struct nsim_fib_data {
36         struct notifier_block fib_nb;
37         struct nsim_per_fib_data ipv4;
38         struct nsim_per_fib_data ipv6;
39 };
40
41 u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
42                      enum nsim_resource_id res_id, bool max)
43 {
44         struct nsim_fib_entry *entry;
45
46         switch (res_id) {
47         case NSIM_RESOURCE_IPV4_FIB:
48                 entry = &fib_data->ipv4.fib;
49                 break;
50         case NSIM_RESOURCE_IPV4_FIB_RULES:
51                 entry = &fib_data->ipv4.rules;
52                 break;
53         case NSIM_RESOURCE_IPV6_FIB:
54                 entry = &fib_data->ipv6.fib;
55                 break;
56         case NSIM_RESOURCE_IPV6_FIB_RULES:
57                 entry = &fib_data->ipv6.rules;
58                 break;
59         default:
60                 return 0;
61         }
62
63         return max ? entry->max : entry->num;
64 }
65
66 static void nsim_fib_set_max(struct nsim_fib_data *fib_data,
67                              enum nsim_resource_id res_id, u64 val)
68 {
69         struct nsim_fib_entry *entry;
70
71         switch (res_id) {
72         case NSIM_RESOURCE_IPV4_FIB:
73                 entry = &fib_data->ipv4.fib;
74                 break;
75         case NSIM_RESOURCE_IPV4_FIB_RULES:
76                 entry = &fib_data->ipv4.rules;
77                 break;
78         case NSIM_RESOURCE_IPV6_FIB:
79                 entry = &fib_data->ipv6.fib;
80                 break;
81         case NSIM_RESOURCE_IPV6_FIB_RULES:
82                 entry = &fib_data->ipv6.rules;
83                 break;
84         default:
85                 WARN_ON(1);
86                 return;
87         }
88         entry->max = val;
89 }
90
91 static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
92                                  struct netlink_ext_ack *extack)
93 {
94         int err = 0;
95
96         if (add) {
97                 if (entry->num < entry->max) {
98                         entry->num++;
99                 } else {
100                         err = -ENOSPC;
101                         NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
102                 }
103         } else {
104                 entry->num--;
105         }
106
107         return err;
108 }
109
110 static int nsim_fib_rule_event(struct nsim_fib_data *data,
111                                struct fib_notifier_info *info, bool add)
112 {
113         struct netlink_ext_ack *extack = info->extack;
114         int err = 0;
115
116         switch (info->family) {
117         case AF_INET:
118                 err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
119                 break;
120         case AF_INET6:
121                 err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
122                 break;
123         }
124
125         return err;
126 }
127
128 static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
129                             struct netlink_ext_ack *extack)
130 {
131         int err = 0;
132
133         if (add) {
134                 if (entry->num < entry->max) {
135                         entry->num++;
136                 } else {
137                         err = -ENOSPC;
138                         NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
139                 }
140         } else {
141                 entry->num--;
142         }
143
144         return err;
145 }
146
147 static int nsim_fib_event(struct nsim_fib_data *data,
148                           struct fib_notifier_info *info, bool add)
149 {
150         struct netlink_ext_ack *extack = info->extack;
151         int err = 0;
152
153         switch (info->family) {
154         case AF_INET:
155                 err = nsim_fib_account(&data->ipv4.fib, add, extack);
156                 break;
157         case AF_INET6:
158                 err = nsim_fib_account(&data->ipv6.fib, add, extack);
159                 break;
160         }
161
162         return err;
163 }
164
165 static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
166                              void *ptr)
167 {
168         struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
169                                                   fib_nb);
170         struct fib_notifier_info *info = ptr;
171         int err = 0;
172
173         switch (event) {
174         case FIB_EVENT_RULE_ADD: /* fall through */
175         case FIB_EVENT_RULE_DEL:
176                 err = nsim_fib_rule_event(data, info,
177                                           event == FIB_EVENT_RULE_ADD);
178                 break;
179
180         case FIB_EVENT_ENTRY_ADD:  /* fall through */
181         case FIB_EVENT_ENTRY_DEL:
182                 err = nsim_fib_event(data, info,
183                                      event == FIB_EVENT_ENTRY_ADD);
184                 break;
185         }
186
187         return notifier_from_errno(err);
188 }
189
190 /* inconsistent dump, trying again */
191 static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
192 {
193         struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
194                                                   fib_nb);
195
196         data->ipv4.fib.num = 0ULL;
197         data->ipv4.rules.num = 0ULL;
198         data->ipv6.fib.num = 0ULL;
199         data->ipv6.rules.num = 0ULL;
200 }
201
202 static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
203 {
204         struct nsim_fib_data *data = priv;
205
206         return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);
207 }
208
209 static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)
210 {
211         struct nsim_fib_data *data = priv;
212
213         return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);
214 }
215
216 static u64 nsim_fib_ipv6_resource_occ_get(void *priv)
217 {
218         struct nsim_fib_data *data = priv;
219
220         return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);
221 }
222
223 static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv)
224 {
225         struct nsim_fib_data *data = priv;
226
227         return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);
228 }
229
230 static void nsim_fib_set_max_all(struct nsim_fib_data *data,
231                                  struct devlink *devlink)
232 {
233         enum nsim_resource_id res_ids[] = {
234                 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
235                 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
236         };
237         int i;
238
239         for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
240                 int err;
241                 u64 val;
242
243                 err = devlink_resource_size_get(devlink, res_ids[i], &val);
244                 if (err)
245                         val = (u64) -1;
246                 nsim_fib_set_max(data, res_ids[i], val);
247         }
248 }
249
250 struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
251                                       struct netlink_ext_ack *extack)
252 {
253         struct nsim_fib_data *data;
254         int err;
255
256         data = kzalloc(sizeof(*data), GFP_KERNEL);
257         if (!data)
258                 return ERR_PTR(-ENOMEM);
259
260         nsim_fib_set_max_all(data, devlink);
261
262         data->fib_nb.notifier_call = nsim_fib_event_nb;
263         err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,
264                                     nsim_fib_dump_inconsistent, extack);
265         if (err) {
266                 pr_err("Failed to register fib notifier\n");
267                 goto err_out;
268         }
269
270         devlink_resource_occ_get_register(devlink,
271                                           NSIM_RESOURCE_IPV4_FIB,
272                                           nsim_fib_ipv4_resource_occ_get,
273                                           data);
274         devlink_resource_occ_get_register(devlink,
275                                           NSIM_RESOURCE_IPV4_FIB_RULES,
276                                           nsim_fib_ipv4_rules_res_occ_get,
277                                           data);
278         devlink_resource_occ_get_register(devlink,
279                                           NSIM_RESOURCE_IPV6_FIB,
280                                           nsim_fib_ipv6_resource_occ_get,
281                                           data);
282         devlink_resource_occ_get_register(devlink,
283                                           NSIM_RESOURCE_IPV6_FIB_RULES,
284                                           nsim_fib_ipv6_rules_res_occ_get,
285                                           data);
286         return data;
287
288 err_out:
289         kfree(data);
290         return ERR_PTR(err);
291 }
292
293 void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
294 {
295         devlink_resource_occ_get_unregister(devlink,
296                                             NSIM_RESOURCE_IPV6_FIB_RULES);
297         devlink_resource_occ_get_unregister(devlink,
298                                             NSIM_RESOURCE_IPV6_FIB);
299         devlink_resource_occ_get_unregister(devlink,
300                                             NSIM_RESOURCE_IPV4_FIB_RULES);
301         devlink_resource_occ_get_unregister(devlink,
302                                             NSIM_RESOURCE_IPV4_FIB);
303         unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
304         kfree(data);
305 }
This page took 0.048966 seconds and 4 git commands to generate.