blob: 73fd85cfad85b5d39932157efb7333d00b7d2b2c [file] [log] [blame]
Ido Schimmel464dce12016-07-02 11:00:15 +02001/*
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>
Jiri Pirko5e9c16c2016-07-04 08:23:04 +020038#include <linux/rhashtable.h>
39#include <linux/bitops.h>
40#include <linux/in6.h>
Ido Schimmel464dce12016-07-02 11:00:15 +020041
42#include "spectrum.h"
43#include "core.h"
44#include "reg.h"
45
Jiri Pirko53342022016-07-04 08:23:08 +020046#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
47 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
48
49static bool
50mlxsw_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
Jiri Pirko5e9c16c2016-07-04 08:23:04 +020056static void
57mlxsw_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
63static void
64mlxsw_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
70struct mlxsw_sp_fib_key {
71 unsigned char addr[sizeof(struct in6_addr)];
72 unsigned char prefix_len;
73};
74
75struct mlxsw_sp_fib_entry {
76 struct rhash_head ht_node;
77 struct mlxsw_sp_fib_key key;
78};
79
80struct 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
86static 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
93static 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
108static 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
119static struct mlxsw_sp_fib_entry *
120mlxsw_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
133static void mlxsw_sp_fib_entry_destroy(struct mlxsw_sp_fib_entry *fib_entry)
134{
135 kfree(fib_entry);
136}
137
138static struct mlxsw_sp_fib_entry *
139mlxsw_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
149static 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
162err_rhashtable_init:
163 kfree(fib);
164 return ERR_PTR(err);
165}
166
167static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
168{
169 rhashtable_destroy(&fib->ht);
170 kfree(fib);
171}
172
Jiri Pirko53342022016-07-04 08:23:08 +0200173static struct mlxsw_sp_lpm_tree *
174mlxsw_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
191static 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
200static 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
209static int
210mlxsw_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
233static struct mlxsw_sp_lpm_tree *
234mlxsw_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
255err_left_struct_set:
256 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
257 return ERR_PTR(err);
258}
259
260static 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
266static struct mlxsw_sp_lpm_tree *
267mlxsw_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
286inc_ref_count:
287 lpm_tree->ref_count++;
288 return lpm_tree;
289}
290
291static 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
299static 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
Ido Schimmel464dce12016-07-02 11:00:15 +0200310static 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
319static 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
327int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
328{
Jiri Pirko53342022016-07-04 08:23:08 +0200329 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;
Ido Schimmel464dce12016-07-02 11:00:15 +0200336}
337
338void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
339{
340 __mlxsw_sp_router_fini(mlxsw_sp);
341}