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