GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_pgt.c
1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
3
4 #include <linux/refcount.h>
5 #include <linux/idr.h>
6
7 #include "spectrum.h"
8 #include "reg.h"
9
10 struct mlxsw_sp_pgt {
11         struct idr pgt_idr;
12         u16 end_index; /* Exclusive. */
13         struct mutex lock; /* Protects PGT. */
14         bool smpe_index_valid;
15 };
16
17 struct mlxsw_sp_pgt_entry {
18         struct list_head ports_list;
19         u16 index;
20         u16 smpe_index;
21 };
22
23 struct mlxsw_sp_pgt_entry_port {
24         struct list_head list; /* Member of 'ports_list'. */
25         u16 local_port;
26 };
27
28 int mlxsw_sp_pgt_mid_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_mid)
29 {
30         int index, err = 0;
31
32         mutex_lock(&mlxsw_sp->pgt->lock);
33         index = idr_alloc(&mlxsw_sp->pgt->pgt_idr, NULL, 0,
34                           mlxsw_sp->pgt->end_index, GFP_KERNEL);
35
36         if (index < 0) {
37                 err = index;
38                 goto err_idr_alloc;
39         }
40
41         *p_mid = index;
42         mutex_unlock(&mlxsw_sp->pgt->lock);
43         return 0;
44
45 err_idr_alloc:
46         mutex_unlock(&mlxsw_sp->pgt->lock);
47         return err;
48 }
49
50 void mlxsw_sp_pgt_mid_free(struct mlxsw_sp *mlxsw_sp, u16 mid_base)
51 {
52         mutex_lock(&mlxsw_sp->pgt->lock);
53         WARN_ON(idr_remove(&mlxsw_sp->pgt->pgt_idr, mid_base));
54         mutex_unlock(&mlxsw_sp->pgt->lock);
55 }
56
57 int mlxsw_sp_pgt_mid_alloc_range(struct mlxsw_sp *mlxsw_sp, u16 *p_mid_base,
58                                  u16 count)
59 {
60         unsigned int mid_base;
61         int i, err;
62
63         mutex_lock(&mlxsw_sp->pgt->lock);
64
65         mid_base = idr_get_cursor(&mlxsw_sp->pgt->pgt_idr);
66         for (i = 0; i < count; i++) {
67                 err = idr_alloc_cyclic(&mlxsw_sp->pgt->pgt_idr, NULL,
68                                        mid_base, mid_base + count, GFP_KERNEL);
69                 if (err < 0)
70                         goto err_idr_alloc_cyclic;
71         }
72
73         mutex_unlock(&mlxsw_sp->pgt->lock);
74         *p_mid_base = mid_base;
75         return 0;
76
77 err_idr_alloc_cyclic:
78         for (i--; i >= 0; i--)
79                 idr_remove(&mlxsw_sp->pgt->pgt_idr, mid_base + i);
80         mutex_unlock(&mlxsw_sp->pgt->lock);
81         return err;
82 }
83
84 void
85 mlxsw_sp_pgt_mid_free_range(struct mlxsw_sp *mlxsw_sp, u16 mid_base, u16 count)
86 {
87         struct idr *pgt_idr = &mlxsw_sp->pgt->pgt_idr;
88         int i;
89
90         mutex_lock(&mlxsw_sp->pgt->lock);
91
92         for (i = 0; i < count; i++)
93                 WARN_ON_ONCE(idr_remove(pgt_idr, mid_base + i));
94
95         mutex_unlock(&mlxsw_sp->pgt->lock);
96 }
97
98 static struct mlxsw_sp_pgt_entry_port *
99 mlxsw_sp_pgt_entry_port_lookup(struct mlxsw_sp_pgt_entry *pgt_entry,
100                                u16 local_port)
101 {
102         struct mlxsw_sp_pgt_entry_port *pgt_entry_port;
103
104         list_for_each_entry(pgt_entry_port, &pgt_entry->ports_list, list) {
105                 if (pgt_entry_port->local_port == local_port)
106                         return pgt_entry_port;
107         }
108
109         return NULL;
110 }
111
112 static struct mlxsw_sp_pgt_entry *
113 mlxsw_sp_pgt_entry_create(struct mlxsw_sp_pgt *pgt, u16 mid, u16 smpe)
114 {
115         struct mlxsw_sp_pgt_entry *pgt_entry;
116         void *ret;
117         int err;
118
119         pgt_entry = kzalloc(sizeof(*pgt_entry), GFP_KERNEL);
120         if (!pgt_entry)
121                 return ERR_PTR(-ENOMEM);
122
123         ret = idr_replace(&pgt->pgt_idr, pgt_entry, mid);
124         if (IS_ERR(ret)) {
125                 err = PTR_ERR(ret);
126                 goto err_idr_replace;
127         }
128
129         INIT_LIST_HEAD(&pgt_entry->ports_list);
130         pgt_entry->index = mid;
131         pgt_entry->smpe_index = smpe;
132         return pgt_entry;
133
134 err_idr_replace:
135         kfree(pgt_entry);
136         return ERR_PTR(err);
137 }
138
139 static void mlxsw_sp_pgt_entry_destroy(struct mlxsw_sp_pgt *pgt,
140                                        struct mlxsw_sp_pgt_entry *pgt_entry)
141 {
142         WARN_ON(!list_empty(&pgt_entry->ports_list));
143
144         pgt_entry = idr_replace(&pgt->pgt_idr, NULL, pgt_entry->index);
145         if (WARN_ON(IS_ERR(pgt_entry)))
146                 return;
147
148         kfree(pgt_entry);
149 }
150
151 static struct mlxsw_sp_pgt_entry *
152 mlxsw_sp_pgt_entry_get(struct mlxsw_sp_pgt *pgt, u16 mid, u16 smpe)
153 {
154         struct mlxsw_sp_pgt_entry *pgt_entry;
155
156         pgt_entry = idr_find(&pgt->pgt_idr, mid);
157         if (pgt_entry)
158                 return pgt_entry;
159
160         return mlxsw_sp_pgt_entry_create(pgt, mid, smpe);
161 }
162
163 static void mlxsw_sp_pgt_entry_put(struct mlxsw_sp_pgt *pgt, u16 mid)
164 {
165         struct mlxsw_sp_pgt_entry *pgt_entry;
166
167         pgt_entry = idr_find(&pgt->pgt_idr, mid);
168         if (WARN_ON(!pgt_entry))
169                 return;
170
171         if (list_empty(&pgt_entry->ports_list))
172                 mlxsw_sp_pgt_entry_destroy(pgt, pgt_entry);
173 }
174
175 static void mlxsw_sp_pgt_smid2_port_set(char *smid2_pl, u16 local_port,
176                                         bool member)
177 {
178         mlxsw_reg_smid2_port_set(smid2_pl, local_port, member);
179         mlxsw_reg_smid2_port_mask_set(smid2_pl, local_port, 1);
180 }
181
182 static int
183 mlxsw_sp_pgt_entry_port_write(struct mlxsw_sp *mlxsw_sp,
184                               const struct mlxsw_sp_pgt_entry *pgt_entry,
185                               u16 local_port, bool member)
186 {
187         char *smid2_pl;
188         int err;
189
190         smid2_pl = kmalloc(MLXSW_REG_SMID2_LEN, GFP_KERNEL);
191         if (!smid2_pl)
192                 return -ENOMEM;
193
194         mlxsw_reg_smid2_pack(smid2_pl, pgt_entry->index, 0, 0,
195                              mlxsw_sp->pgt->smpe_index_valid,
196                              pgt_entry->smpe_index);
197
198         mlxsw_sp_pgt_smid2_port_set(smid2_pl, local_port, member);
199         err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid2), smid2_pl);
200
201         kfree(smid2_pl);
202
203         return err;
204 }
205
206 static struct mlxsw_sp_pgt_entry_port *
207 mlxsw_sp_pgt_entry_port_create(struct mlxsw_sp *mlxsw_sp,
208                                struct mlxsw_sp_pgt_entry *pgt_entry,
209                                u16 local_port)
210 {
211         struct mlxsw_sp_pgt_entry_port *pgt_entry_port;
212         int err;
213
214         pgt_entry_port = kzalloc(sizeof(*pgt_entry_port), GFP_KERNEL);
215         if (!pgt_entry_port)
216                 return ERR_PTR(-ENOMEM);
217
218         err = mlxsw_sp_pgt_entry_port_write(mlxsw_sp, pgt_entry, local_port,
219                                             true);
220         if (err)
221                 goto err_pgt_entry_port_write;
222
223         pgt_entry_port->local_port = local_port;
224         list_add(&pgt_entry_port->list, &pgt_entry->ports_list);
225
226         return pgt_entry_port;
227
228 err_pgt_entry_port_write:
229         kfree(pgt_entry_port);
230         return ERR_PTR(err);
231 }
232
233 static void
234 mlxsw_sp_pgt_entry_port_destroy(struct mlxsw_sp *mlxsw_sp,
235                                 struct mlxsw_sp_pgt_entry *pgt_entry,
236                                 struct mlxsw_sp_pgt_entry_port *pgt_entry_port)
237
238 {
239         list_del(&pgt_entry_port->list);
240         mlxsw_sp_pgt_entry_port_write(mlxsw_sp, pgt_entry,
241                                       pgt_entry_port->local_port, false);
242         kfree(pgt_entry_port);
243 }
244
245 static int mlxsw_sp_pgt_entry_port_add(struct mlxsw_sp *mlxsw_sp, u16 mid,
246                                        u16 smpe, u16 local_port)
247 {
248         struct mlxsw_sp_pgt_entry_port *pgt_entry_port;
249         struct mlxsw_sp_pgt_entry *pgt_entry;
250         int err;
251
252         mutex_lock(&mlxsw_sp->pgt->lock);
253
254         pgt_entry = mlxsw_sp_pgt_entry_get(mlxsw_sp->pgt, mid, smpe);
255         if (IS_ERR(pgt_entry)) {
256                 err = PTR_ERR(pgt_entry);
257                 goto err_pgt_entry_get;
258         }
259
260         pgt_entry_port = mlxsw_sp_pgt_entry_port_create(mlxsw_sp, pgt_entry,
261                                                         local_port);
262         if (IS_ERR(pgt_entry_port)) {
263                 err = PTR_ERR(pgt_entry_port);
264                 goto err_pgt_entry_port_get;
265         }
266
267         mutex_unlock(&mlxsw_sp->pgt->lock);
268         return 0;
269
270 err_pgt_entry_port_get:
271         mlxsw_sp_pgt_entry_put(mlxsw_sp->pgt, mid);
272 err_pgt_entry_get:
273         mutex_unlock(&mlxsw_sp->pgt->lock);
274         return err;
275 }
276
277 static void mlxsw_sp_pgt_entry_port_del(struct mlxsw_sp *mlxsw_sp,
278                                         u16 mid, u16 smpe, u16 local_port)
279 {
280         struct mlxsw_sp_pgt_entry_port *pgt_entry_port;
281         struct mlxsw_sp_pgt_entry *pgt_entry;
282
283         mutex_lock(&mlxsw_sp->pgt->lock);
284
285         pgt_entry = idr_find(&mlxsw_sp->pgt->pgt_idr, mid);
286         if (!pgt_entry)
287                 goto out;
288
289         pgt_entry_port = mlxsw_sp_pgt_entry_port_lookup(pgt_entry, local_port);
290         if (!pgt_entry_port)
291                 goto out;
292
293         mlxsw_sp_pgt_entry_port_destroy(mlxsw_sp, pgt_entry, pgt_entry_port);
294         mlxsw_sp_pgt_entry_put(mlxsw_sp->pgt, mid);
295
296 out:
297         mutex_unlock(&mlxsw_sp->pgt->lock);
298 }
299
300 int mlxsw_sp_pgt_entry_port_set(struct mlxsw_sp *mlxsw_sp, u16 mid,
301                                 u16 smpe, u16 local_port, bool member)
302 {
303         if (member)
304                 return mlxsw_sp_pgt_entry_port_add(mlxsw_sp, mid, smpe,
305                                                    local_port);
306
307         mlxsw_sp_pgt_entry_port_del(mlxsw_sp, mid, smpe, local_port);
308         return 0;
309 }
310
311 int mlxsw_sp_pgt_init(struct mlxsw_sp *mlxsw_sp)
312 {
313         struct mlxsw_sp_pgt *pgt;
314
315         if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, PGT_SIZE))
316                 return -EIO;
317
318         pgt = kzalloc(sizeof(*mlxsw_sp->pgt), GFP_KERNEL);
319         if (!pgt)
320                 return -ENOMEM;
321
322         idr_init(&pgt->pgt_idr);
323         pgt->end_index = MLXSW_CORE_RES_GET(mlxsw_sp->core, PGT_SIZE);
324         mutex_init(&pgt->lock);
325         pgt->smpe_index_valid = mlxsw_sp->pgt_smpe_index_valid;
326         mlxsw_sp->pgt = pgt;
327         return 0;
328 }
329
330 void mlxsw_sp_pgt_fini(struct mlxsw_sp *mlxsw_sp)
331 {
332         mutex_destroy(&mlxsw_sp->pgt->lock);
333         WARN_ON(!idr_is_empty(&mlxsw_sp->pgt->pgt_idr));
334         idr_destroy(&mlxsw_sp->pgt->pgt_idr);
335         kfree(mlxsw_sp->pgt);
336 }