Merge branch 'regmap-5.4' into regmap-next
[linux-2.6-block.git] / drivers / net / netdevsim / fib.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 <net/fib_notifier.h>
18 #include <net/ip_fib.h>
19 #include <net/ip6_fib.h>
20 #include <net/fib_rules.h>
21
22 #include "netdevsim.h"
23
24 struct nsim_fib_entry {
25         u64 max;
26         u64 num;
27 };
28
29 struct nsim_per_fib_data {
30         struct nsim_fib_entry fib;
31         struct nsim_fib_entry rules;
32 };
33
34 struct nsim_fib_data {
35         struct notifier_block fib_nb;
36         struct nsim_per_fib_data ipv4;
37         struct nsim_per_fib_data ipv6;
38 };
39
40 u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
41                      enum nsim_resource_id res_id, bool max)
42 {
43         struct nsim_fib_entry *entry;
44
45         switch (res_id) {
46         case NSIM_RESOURCE_IPV4_FIB:
47                 entry = &fib_data->ipv4.fib;
48                 break;
49         case NSIM_RESOURCE_IPV4_FIB_RULES:
50                 entry = &fib_data->ipv4.rules;
51                 break;
52         case NSIM_RESOURCE_IPV6_FIB:
53                 entry = &fib_data->ipv6.fib;
54                 break;
55         case NSIM_RESOURCE_IPV6_FIB_RULES:
56                 entry = &fib_data->ipv6.rules;
57                 break;
58         default:
59                 return 0;
60         }
61
62         return max ? entry->max : entry->num;
63 }
64
65 int nsim_fib_set_max(struct nsim_fib_data *fib_data,
66                      enum nsim_resource_id res_id, u64 val,
67                      struct netlink_ext_ack *extack)
68 {
69         struct nsim_fib_entry *entry;
70         int err = 0;
71
72         switch (res_id) {
73         case NSIM_RESOURCE_IPV4_FIB:
74                 entry = &fib_data->ipv4.fib;
75                 break;
76         case NSIM_RESOURCE_IPV4_FIB_RULES:
77                 entry = &fib_data->ipv4.rules;
78                 break;
79         case NSIM_RESOURCE_IPV6_FIB:
80                 entry = &fib_data->ipv6.fib;
81                 break;
82         case NSIM_RESOURCE_IPV6_FIB_RULES:
83                 entry = &fib_data->ipv6.rules;
84                 break;
85         default:
86                 return 0;
87         }
88
89         /* not allowing a new max to be less than curren occupancy
90          * --> no means of evicting entries
91          */
92         if (val < entry->num) {
93                 NL_SET_ERR_MSG_MOD(extack, "New size is less than current occupancy");
94                 err = -EINVAL;
95         } else {
96                 entry->max = val;
97         }
98
99         return err;
100 }
101
102 static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
103                                  struct netlink_ext_ack *extack)
104 {
105         int err = 0;
106
107         if (add) {
108                 if (entry->num < entry->max) {
109                         entry->num++;
110                 } else {
111                         err = -ENOSPC;
112                         NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
113                 }
114         } else {
115                 entry->num--;
116         }
117
118         return err;
119 }
120
121 static int nsim_fib_rule_event(struct nsim_fib_data *data,
122                                struct fib_notifier_info *info, bool add)
123 {
124         struct netlink_ext_ack *extack = info->extack;
125         int err = 0;
126
127         switch (info->family) {
128         case AF_INET:
129                 err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
130                 break;
131         case AF_INET6:
132                 err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
133                 break;
134         }
135
136         return err;
137 }
138
139 static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
140                             struct netlink_ext_ack *extack)
141 {
142         int err = 0;
143
144         if (add) {
145                 if (entry->num < entry->max) {
146                         entry->num++;
147                 } else {
148                         err = -ENOSPC;
149                         NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
150                 }
151         } else {
152                 entry->num--;
153         }
154
155         return err;
156 }
157
158 static int nsim_fib_event(struct nsim_fib_data *data,
159                           struct fib_notifier_info *info, bool add)
160 {
161         struct netlink_ext_ack *extack = info->extack;
162         int err = 0;
163
164         switch (info->family) {
165         case AF_INET:
166                 err = nsim_fib_account(&data->ipv4.fib, add, extack);
167                 break;
168         case AF_INET6:
169                 err = nsim_fib_account(&data->ipv6.fib, add, extack);
170                 break;
171         }
172
173         return err;
174 }
175
176 static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
177                              void *ptr)
178 {
179         struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
180                                                   fib_nb);
181         struct fib_notifier_info *info = ptr;
182         int err = 0;
183
184         switch (event) {
185         case FIB_EVENT_RULE_ADD: /* fall through */
186         case FIB_EVENT_RULE_DEL:
187                 err = nsim_fib_rule_event(data, info,
188                                           event == FIB_EVENT_RULE_ADD);
189                 break;
190
191         case FIB_EVENT_ENTRY_ADD:  /* fall through */
192         case FIB_EVENT_ENTRY_DEL:
193                 err = nsim_fib_event(data, info,
194                                      event == FIB_EVENT_ENTRY_ADD);
195                 break;
196         }
197
198         return notifier_from_errno(err);
199 }
200
201 /* inconsistent dump, trying again */
202 static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
203 {
204         struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
205                                                   fib_nb);
206
207         data->ipv4.fib.num = 0ULL;
208         data->ipv4.rules.num = 0ULL;
209         data->ipv6.fib.num = 0ULL;
210         data->ipv6.rules.num = 0ULL;
211 }
212
213 struct nsim_fib_data *nsim_fib_create(void)
214 {
215         struct nsim_fib_data *data;
216         int err;
217
218         data = kzalloc(sizeof(*data), GFP_KERNEL);
219         if (!data)
220                 return ERR_PTR(-ENOMEM);
221
222         data->ipv4.fib.max = (u64)-1;
223         data->ipv4.rules.max = (u64)-1;
224
225         data->ipv6.fib.max = (u64)-1;
226         data->ipv6.rules.max = (u64)-1;
227
228         data->fib_nb.notifier_call = nsim_fib_event_nb;
229         err = register_fib_notifier(&data->fib_nb, nsim_fib_dump_inconsistent);
230         if (err) {
231                 pr_err("Failed to register fib notifier\n");
232                 goto err_out;
233         }
234
235         return data;
236
237 err_out:
238         kfree(data);
239         return ERR_PTR(err);
240 }
241
242 void nsim_fib_destroy(struct nsim_fib_data *data)
243 {
244         unregister_fib_notifier(&data->fib_nb);
245         kfree(data);
246 }