mlxsw: spectrum_router: Implement LPM trees management
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_router.c
1 /*
2  * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
3  * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5  * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the names of the copyright holders nor the names of its
16  *    contributors may be used to endorse or promote products derived from
17  *    this software without specific prior written permission.
18  *
19  * Alternatively, this software may be distributed under the terms of the
20  * GNU General Public License ("GPL") version 2 as published by the Free
21  * Software Foundation.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35
36 #include <linux/kernel.h>
37 #include <linux/types.h>
38 #include <linux/rhashtable.h>
39 #include <linux/bitops.h>
40 #include <linux/in6.h>
41
42 #include "spectrum.h"
43 #include "core.h"
44 #include "reg.h"
45
46 #define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
47         for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
48
49 static bool
50 mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
51                          struct mlxsw_sp_prefix_usage *prefix_usage2)
52 {
53         return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
54 }
55
56 static void
57 mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
58                           unsigned char prefix_len)
59 {
60         set_bit(prefix_len, prefix_usage->b);
61 }
62
63 static void
64 mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
65                             unsigned char prefix_len)
66 {
67         clear_bit(prefix_len, prefix_usage->b);
68 }
69
70 struct mlxsw_sp_fib_key {
71         unsigned char addr[sizeof(struct in6_addr)];
72         unsigned char prefix_len;
73 };
74
75 struct mlxsw_sp_fib_entry {
76         struct rhash_head ht_node;
77         struct mlxsw_sp_fib_key key;
78 };
79
80 struct mlxsw_sp_fib {
81         struct rhashtable ht;
82         unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
83         struct mlxsw_sp_prefix_usage prefix_usage;
84 };
85
86 static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
87         .key_offset = offsetof(struct mlxsw_sp_fib_entry, key),
88         .head_offset = offsetof(struct mlxsw_sp_fib_entry, ht_node),
89         .key_len = sizeof(struct mlxsw_sp_fib_key),
90         .automatic_shrinking = true,
91 };
92
93 static int mlxsw_sp_fib_entry_insert(struct mlxsw_sp_fib *fib,
94                                      struct mlxsw_sp_fib_entry *fib_entry)
95 {
96         unsigned char prefix_len = fib_entry->key.prefix_len;
97         int err;
98
99         err = rhashtable_insert_fast(&fib->ht, &fib_entry->ht_node,
100                                      mlxsw_sp_fib_ht_params);
101         if (err)
102                 return err;
103         if (fib->prefix_ref_count[prefix_len]++ == 0)
104                 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
105         return 0;
106 }
107
108 static void mlxsw_sp_fib_entry_remove(struct mlxsw_sp_fib *fib,
109                                       struct mlxsw_sp_fib_entry *fib_entry)
110 {
111         unsigned char prefix_len = fib_entry->key.prefix_len;
112
113         if (--fib->prefix_ref_count[prefix_len] == 0)
114                 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
115         rhashtable_remove_fast(&fib->ht, &fib_entry->ht_node,
116                                mlxsw_sp_fib_ht_params);
117 }
118
119 static struct mlxsw_sp_fib_entry *
120 mlxsw_sp_fib_entry_create(struct mlxsw_sp_fib *fib, const void *addr,
121                           size_t addr_len, unsigned char prefix_len)
122 {
123         struct mlxsw_sp_fib_entry *fib_entry;
124
125         fib_entry = kzalloc(sizeof(*fib_entry), GFP_KERNEL);
126         if (!fib_entry)
127                 return NULL;
128         memcpy(fib_entry->key.addr, addr, addr_len);
129         fib_entry->key.prefix_len = prefix_len;
130         return fib_entry;
131 }
132
133 static void mlxsw_sp_fib_entry_destroy(struct mlxsw_sp_fib_entry *fib_entry)
134 {
135         kfree(fib_entry);
136 }
137
138 static struct mlxsw_sp_fib_entry *
139 mlxsw_sp_fib_entry_lookup(struct mlxsw_sp_fib *fib, const void *addr,
140                           size_t addr_len, unsigned char prefix_len)
141 {
142         struct mlxsw_sp_fib_key key = {{ 0 } };
143
144         memcpy(key.addr, addr, addr_len);
145         key.prefix_len = prefix_len;
146         return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
147 }
148
149 static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void)
150 {
151         struct mlxsw_sp_fib *fib;
152         int err;
153
154         fib = kzalloc(sizeof(*fib), GFP_KERNEL);
155         if (!fib)
156                 return ERR_PTR(-ENOMEM);
157         err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
158         if (err)
159                 goto err_rhashtable_init;
160         return fib;
161
162 err_rhashtable_init:
163         kfree(fib);
164         return ERR_PTR(err);
165 }
166
167 static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
168 {
169         rhashtable_destroy(&fib->ht);
170         kfree(fib);
171 }
172
173 static struct mlxsw_sp_lpm_tree *
174 mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp, bool one_reserved)
175 {
176         static struct mlxsw_sp_lpm_tree *lpm_tree;
177         int i;
178
179         for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
180                 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
181                 if (lpm_tree->ref_count == 0) {
182                         if (one_reserved)
183                                 one_reserved = false;
184                         else
185                                 return lpm_tree;
186                 }
187         }
188         return NULL;
189 }
190
191 static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
192                                    struct mlxsw_sp_lpm_tree *lpm_tree)
193 {
194         char ralta_pl[MLXSW_REG_RALTA_LEN];
195
196         mlxsw_reg_ralta_pack(ralta_pl, true, lpm_tree->proto, lpm_tree->id);
197         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
198 }
199
200 static int mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
201                                   struct mlxsw_sp_lpm_tree *lpm_tree)
202 {
203         char ralta_pl[MLXSW_REG_RALTA_LEN];
204
205         mlxsw_reg_ralta_pack(ralta_pl, false, lpm_tree->proto, lpm_tree->id);
206         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
207 }
208
209 static int
210 mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
211                                   struct mlxsw_sp_prefix_usage *prefix_usage,
212                                   struct mlxsw_sp_lpm_tree *lpm_tree)
213 {
214         char ralst_pl[MLXSW_REG_RALST_LEN];
215         u8 root_bin = 0;
216         u8 prefix;
217         u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
218
219         mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
220                 root_bin = prefix;
221
222         mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
223         mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
224                 if (prefix == 0)
225                         continue;
226                 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
227                                          MLXSW_REG_RALST_BIN_NO_CHILD);
228                 last_prefix = prefix;
229         }
230         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
231 }
232
233 static struct mlxsw_sp_lpm_tree *
234 mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
235                          struct mlxsw_sp_prefix_usage *prefix_usage,
236                          enum mlxsw_sp_l3proto proto, bool one_reserved)
237 {
238         struct mlxsw_sp_lpm_tree *lpm_tree;
239         int err;
240
241         lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp, one_reserved);
242         if (!lpm_tree)
243                 return ERR_PTR(-EBUSY);
244         lpm_tree->proto = proto;
245         err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
246         if (err)
247                 return ERR_PTR(err);
248
249         err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
250                                                 lpm_tree);
251         if (err)
252                 goto err_left_struct_set;
253         return lpm_tree;
254
255 err_left_struct_set:
256         mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
257         return ERR_PTR(err);
258 }
259
260 static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
261                                      struct mlxsw_sp_lpm_tree *lpm_tree)
262 {
263         return mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
264 }
265
266 static struct mlxsw_sp_lpm_tree *
267 mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
268                       struct mlxsw_sp_prefix_usage *prefix_usage,
269                       enum mlxsw_sp_l3proto proto, bool one_reserved)
270 {
271         struct mlxsw_sp_lpm_tree *lpm_tree;
272         int i;
273
274         for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
275                 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
276                 if (lpm_tree->proto == proto &&
277                     mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
278                                              prefix_usage))
279                         goto inc_ref_count;
280         }
281         lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage,
282                                             proto, one_reserved);
283         if (IS_ERR(lpm_tree))
284                 return lpm_tree;
285
286 inc_ref_count:
287         lpm_tree->ref_count++;
288         return lpm_tree;
289 }
290
291 static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
292                                  struct mlxsw_sp_lpm_tree *lpm_tree)
293 {
294         if (--lpm_tree->ref_count == 0)
295                 return mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
296         return 0;
297 }
298
299 static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
300 {
301         struct mlxsw_sp_lpm_tree *lpm_tree;
302         int i;
303
304         for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
305                 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
306                 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
307         }
308 }
309
310 static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
311 {
312         char rgcr_pl[MLXSW_REG_RGCR_LEN];
313
314         mlxsw_reg_rgcr_pack(rgcr_pl, true);
315         mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, MLXSW_SP_RIF_MAX);
316         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
317 }
318
319 static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
320 {
321         char rgcr_pl[MLXSW_REG_RGCR_LEN];
322
323         mlxsw_reg_rgcr_pack(rgcr_pl, false);
324         mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
325 }
326
327 int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
328 {
329         int err;
330
331         err = __mlxsw_sp_router_init(mlxsw_sp);
332         if (err)
333                 return err;
334         mlxsw_sp_lpm_init(mlxsw_sp);
335         return 0;
336 }
337
338 void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
339 {
340         __mlxsw_sp_router_fini(mlxsw_sp);
341 }