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  
mlxsw_sp_pgt_mid_alloc(struct mlxsw_sp * mlxsw_sp,u16 * p_mid)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  
mlxsw_sp_pgt_mid_free(struct mlxsw_sp * mlxsw_sp,u16 mid_base)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  
mlxsw_sp_pgt_mid_alloc_range(struct mlxsw_sp * mlxsw_sp,u16 * p_mid_base,u16 count)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
mlxsw_sp_pgt_mid_free_range(struct mlxsw_sp * mlxsw_sp,u16 mid_base,u16 count)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 *
mlxsw_sp_pgt_entry_port_lookup(struct mlxsw_sp_pgt_entry * pgt_entry,u16 local_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 *
mlxsw_sp_pgt_entry_create(struct mlxsw_sp_pgt * pgt,u16 mid,u16 smpe)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  
mlxsw_sp_pgt_entry_destroy(struct mlxsw_sp_pgt * pgt,struct mlxsw_sp_pgt_entry * pgt_entry)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 *
mlxsw_sp_pgt_entry_get(struct mlxsw_sp_pgt * pgt,u16 mid,u16 smpe)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  
mlxsw_sp_pgt_entry_put(struct mlxsw_sp_pgt * pgt,u16 mid)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  
mlxsw_sp_pgt_smid2_port_set(char * smid2_pl,u16 local_port,bool member)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
mlxsw_sp_pgt_entry_port_write(struct mlxsw_sp * mlxsw_sp,const struct mlxsw_sp_pgt_entry * pgt_entry,u16 local_port,bool member)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 *
mlxsw_sp_pgt_entry_port_create(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_pgt_entry * pgt_entry,u16 local_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
mlxsw_sp_pgt_entry_port_destroy(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_pgt_entry * pgt_entry,struct mlxsw_sp_pgt_entry_port * pgt_entry_port)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  
mlxsw_sp_pgt_entry_port_add(struct mlxsw_sp * mlxsw_sp,u16 mid,u16 smpe,u16 local_port)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  
mlxsw_sp_pgt_entry_port_del(struct mlxsw_sp * mlxsw_sp,u16 mid,u16 smpe,u16 local_port)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  
mlxsw_sp_pgt_entry_port_set(struct mlxsw_sp * mlxsw_sp,u16 mid,u16 smpe,u16 local_port,bool member)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  
mlxsw_sp_pgt_init(struct mlxsw_sp * mlxsw_sp)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  
mlxsw_sp_pgt_fini(struct mlxsw_sp * mlxsw_sp)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  }
337