brintos

brintos / linux-shallow public Read only

0
0
Text · 7.8 KiB · 4ef81ba Raw
337 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* 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 10struct 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 17struct mlxsw_sp_pgt_entry {18	struct list_head ports_list;19	u16 index;20	u16 smpe_index;21};22 23struct mlxsw_sp_pgt_entry_port {24	struct list_head list; /* Member of 'ports_list'. */25	u16 local_port;26};27 28int 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 45err_idr_alloc:46	mutex_unlock(&mlxsw_sp->pgt->lock);47	return err;48}49 50void 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 57int 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 77err_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 84void85mlxsw_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 98static struct mlxsw_sp_pgt_entry_port *99mlxsw_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 112static struct mlxsw_sp_pgt_entry *113mlxsw_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 134err_idr_replace:135	kfree(pgt_entry);136	return ERR_PTR(err);137}138 139static 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 151static struct mlxsw_sp_pgt_entry *152mlxsw_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 163static 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 175static 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 182static int183mlxsw_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 206static struct mlxsw_sp_pgt_entry_port *207mlxsw_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 228err_pgt_entry_port_write:229	kfree(pgt_entry_port);230	return ERR_PTR(err);231}232 233static void234mlxsw_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 245static 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 270err_pgt_entry_port_get:271	mlxsw_sp_pgt_entry_put(mlxsw_sp->pgt, mid);272err_pgt_entry_get:273	mutex_unlock(&mlxsw_sp->pgt->lock);274	return err;275}276 277static 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 296out:297	mutex_unlock(&mlxsw_sp->pgt->lock);298}299 300int 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 311int 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 330void 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