Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
0002 /* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
0003 
0004 #include <linux/refcount.h>
0005 #include <linux/idr.h>
0006 
0007 #include "spectrum.h"
0008 #include "reg.h"
0009 
0010 struct mlxsw_sp_pgt {
0011     struct idr pgt_idr;
0012     u16 end_index; /* Exclusive. */
0013     struct mutex lock; /* Protects PGT. */
0014     bool smpe_index_valid;
0015 };
0016 
0017 struct mlxsw_sp_pgt_entry {
0018     struct list_head ports_list;
0019     u16 index;
0020     u16 smpe_index;
0021 };
0022 
0023 struct mlxsw_sp_pgt_entry_port {
0024     struct list_head list; /* Member of 'ports_list'. */
0025     u16 local_port;
0026 };
0027 
0028 int mlxsw_sp_pgt_mid_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_mid)
0029 {
0030     int index, err = 0;
0031 
0032     mutex_lock(&mlxsw_sp->pgt->lock);
0033     index = idr_alloc(&mlxsw_sp->pgt->pgt_idr, NULL, 0,
0034               mlxsw_sp->pgt->end_index, GFP_KERNEL);
0035 
0036     if (index < 0) {
0037         err = index;
0038         goto err_idr_alloc;
0039     }
0040 
0041     *p_mid = index;
0042     mutex_unlock(&mlxsw_sp->pgt->lock);
0043     return 0;
0044 
0045 err_idr_alloc:
0046     mutex_unlock(&mlxsw_sp->pgt->lock);
0047     return err;
0048 }
0049 
0050 void mlxsw_sp_pgt_mid_free(struct mlxsw_sp *mlxsw_sp, u16 mid_base)
0051 {
0052     mutex_lock(&mlxsw_sp->pgt->lock);
0053     WARN_ON(idr_remove(&mlxsw_sp->pgt->pgt_idr, mid_base));
0054     mutex_unlock(&mlxsw_sp->pgt->lock);
0055 }
0056 
0057 int
0058 mlxsw_sp_pgt_mid_alloc_range(struct mlxsw_sp *mlxsw_sp, u16 mid_base, u16 count)
0059 {
0060     unsigned int idr_cursor;
0061     int i, err;
0062 
0063     mutex_lock(&mlxsw_sp->pgt->lock);
0064 
0065     /* This function is supposed to be called several times as part of
0066      * driver init, in specific order. Verify that the mid_index is the
0067      * first free index in the idr, to be able to free the indexes in case
0068      * of error.
0069      */
0070     idr_cursor = idr_get_cursor(&mlxsw_sp->pgt->pgt_idr);
0071     if (WARN_ON(idr_cursor != mid_base)) {
0072         err = -EINVAL;
0073         goto err_idr_cursor;
0074     }
0075 
0076     for (i = 0; i < count; i++) {
0077         err = idr_alloc_cyclic(&mlxsw_sp->pgt->pgt_idr, NULL,
0078                        mid_base, mid_base + count, GFP_KERNEL);
0079         if (err < 0)
0080             goto err_idr_alloc_cyclic;
0081     }
0082 
0083     mutex_unlock(&mlxsw_sp->pgt->lock);
0084     return 0;
0085 
0086 err_idr_alloc_cyclic:
0087     for (i--; i >= 0; i--)
0088         idr_remove(&mlxsw_sp->pgt->pgt_idr, mid_base + i);
0089 err_idr_cursor:
0090     mutex_unlock(&mlxsw_sp->pgt->lock);
0091     return err;
0092 }
0093 
0094 void
0095 mlxsw_sp_pgt_mid_free_range(struct mlxsw_sp *mlxsw_sp, u16 mid_base, u16 count)
0096 {
0097     struct idr *pgt_idr = &mlxsw_sp->pgt->pgt_idr;
0098     int i;
0099 
0100     mutex_lock(&mlxsw_sp->pgt->lock);
0101 
0102     for (i = 0; i < count; i++)
0103         WARN_ON_ONCE(idr_remove(pgt_idr, mid_base + i));
0104 
0105     mutex_unlock(&mlxsw_sp->pgt->lock);
0106 }
0107 
0108 static struct mlxsw_sp_pgt_entry_port *
0109 mlxsw_sp_pgt_entry_port_lookup(struct mlxsw_sp_pgt_entry *pgt_entry,
0110                    u16 local_port)
0111 {
0112     struct mlxsw_sp_pgt_entry_port *pgt_entry_port;
0113 
0114     list_for_each_entry(pgt_entry_port, &pgt_entry->ports_list, list) {
0115         if (pgt_entry_port->local_port == local_port)
0116             return pgt_entry_port;
0117     }
0118 
0119     return NULL;
0120 }
0121 
0122 static struct mlxsw_sp_pgt_entry *
0123 mlxsw_sp_pgt_entry_create(struct mlxsw_sp_pgt *pgt, u16 mid, u16 smpe)
0124 {
0125     struct mlxsw_sp_pgt_entry *pgt_entry;
0126     void *ret;
0127     int err;
0128 
0129     pgt_entry = kzalloc(sizeof(*pgt_entry), GFP_KERNEL);
0130     if (!pgt_entry)
0131         return ERR_PTR(-ENOMEM);
0132 
0133     ret = idr_replace(&pgt->pgt_idr, pgt_entry, mid);
0134     if (IS_ERR(ret)) {
0135         err = PTR_ERR(ret);
0136         goto err_idr_replace;
0137     }
0138 
0139     INIT_LIST_HEAD(&pgt_entry->ports_list);
0140     pgt_entry->index = mid;
0141     pgt_entry->smpe_index = smpe;
0142     return pgt_entry;
0143 
0144 err_idr_replace:
0145     kfree(pgt_entry);
0146     return ERR_PTR(err);
0147 }
0148 
0149 static void mlxsw_sp_pgt_entry_destroy(struct mlxsw_sp_pgt *pgt,
0150                        struct mlxsw_sp_pgt_entry *pgt_entry)
0151 {
0152     WARN_ON(!list_empty(&pgt_entry->ports_list));
0153 
0154     pgt_entry = idr_replace(&pgt->pgt_idr, NULL, pgt_entry->index);
0155     if (WARN_ON(IS_ERR(pgt_entry)))
0156         return;
0157 
0158     kfree(pgt_entry);
0159 }
0160 
0161 static struct mlxsw_sp_pgt_entry *
0162 mlxsw_sp_pgt_entry_get(struct mlxsw_sp_pgt *pgt, u16 mid, u16 smpe)
0163 {
0164     struct mlxsw_sp_pgt_entry *pgt_entry;
0165 
0166     pgt_entry = idr_find(&pgt->pgt_idr, mid);
0167     if (pgt_entry)
0168         return pgt_entry;
0169 
0170     return mlxsw_sp_pgt_entry_create(pgt, mid, smpe);
0171 }
0172 
0173 static void mlxsw_sp_pgt_entry_put(struct mlxsw_sp_pgt *pgt, u16 mid)
0174 {
0175     struct mlxsw_sp_pgt_entry *pgt_entry;
0176 
0177     pgt_entry = idr_find(&pgt->pgt_idr, mid);
0178     if (WARN_ON(!pgt_entry))
0179         return;
0180 
0181     if (list_empty(&pgt_entry->ports_list))
0182         mlxsw_sp_pgt_entry_destroy(pgt, pgt_entry);
0183 }
0184 
0185 static void mlxsw_sp_pgt_smid2_port_set(char *smid2_pl, u16 local_port,
0186                     bool member)
0187 {
0188     mlxsw_reg_smid2_port_set(smid2_pl, local_port, member);
0189     mlxsw_reg_smid2_port_mask_set(smid2_pl, local_port, 1);
0190 }
0191 
0192 static int
0193 mlxsw_sp_pgt_entry_port_write(struct mlxsw_sp *mlxsw_sp,
0194                   const struct mlxsw_sp_pgt_entry *pgt_entry,
0195                   u16 local_port, bool member)
0196 {
0197     char *smid2_pl;
0198     int err;
0199 
0200     smid2_pl = kmalloc(MLXSW_REG_SMID2_LEN, GFP_KERNEL);
0201     if (!smid2_pl)
0202         return -ENOMEM;
0203 
0204     mlxsw_reg_smid2_pack(smid2_pl, pgt_entry->index, 0, 0,
0205                  mlxsw_sp->pgt->smpe_index_valid,
0206                  pgt_entry->smpe_index);
0207 
0208     mlxsw_sp_pgt_smid2_port_set(smid2_pl, local_port, member);
0209     err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid2), smid2_pl);
0210 
0211     kfree(smid2_pl);
0212 
0213     return err;
0214 }
0215 
0216 static struct mlxsw_sp_pgt_entry_port *
0217 mlxsw_sp_pgt_entry_port_create(struct mlxsw_sp *mlxsw_sp,
0218                    struct mlxsw_sp_pgt_entry *pgt_entry,
0219                    u16 local_port)
0220 {
0221     struct mlxsw_sp_pgt_entry_port *pgt_entry_port;
0222     int err;
0223 
0224     pgt_entry_port = kzalloc(sizeof(*pgt_entry_port), GFP_KERNEL);
0225     if (!pgt_entry_port)
0226         return ERR_PTR(-ENOMEM);
0227 
0228     err = mlxsw_sp_pgt_entry_port_write(mlxsw_sp, pgt_entry, local_port,
0229                         true);
0230     if (err)
0231         goto err_pgt_entry_port_write;
0232 
0233     pgt_entry_port->local_port = local_port;
0234     list_add(&pgt_entry_port->list, &pgt_entry->ports_list);
0235 
0236     return pgt_entry_port;
0237 
0238 err_pgt_entry_port_write:
0239     kfree(pgt_entry_port);
0240     return ERR_PTR(err);
0241 }
0242 
0243 static void
0244 mlxsw_sp_pgt_entry_port_destroy(struct mlxsw_sp *mlxsw_sp,
0245                 struct mlxsw_sp_pgt_entry *pgt_entry,
0246                 struct mlxsw_sp_pgt_entry_port *pgt_entry_port)
0247 
0248 {
0249     list_del(&pgt_entry_port->list);
0250     mlxsw_sp_pgt_entry_port_write(mlxsw_sp, pgt_entry,
0251                       pgt_entry_port->local_port, false);
0252     kfree(pgt_entry_port);
0253 }
0254 
0255 static int mlxsw_sp_pgt_entry_port_add(struct mlxsw_sp *mlxsw_sp, u16 mid,
0256                        u16 smpe, u16 local_port)
0257 {
0258     struct mlxsw_sp_pgt_entry_port *pgt_entry_port;
0259     struct mlxsw_sp_pgt_entry *pgt_entry;
0260     int err;
0261 
0262     mutex_lock(&mlxsw_sp->pgt->lock);
0263 
0264     pgt_entry = mlxsw_sp_pgt_entry_get(mlxsw_sp->pgt, mid, smpe);
0265     if (IS_ERR(pgt_entry)) {
0266         err = PTR_ERR(pgt_entry);
0267         goto err_pgt_entry_get;
0268     }
0269 
0270     pgt_entry_port = mlxsw_sp_pgt_entry_port_create(mlxsw_sp, pgt_entry,
0271                             local_port);
0272     if (IS_ERR(pgt_entry_port)) {
0273         err = PTR_ERR(pgt_entry_port);
0274         goto err_pgt_entry_port_get;
0275     }
0276 
0277     mutex_unlock(&mlxsw_sp->pgt->lock);
0278     return 0;
0279 
0280 err_pgt_entry_port_get:
0281     mlxsw_sp_pgt_entry_put(mlxsw_sp->pgt, mid);
0282 err_pgt_entry_get:
0283     mutex_unlock(&mlxsw_sp->pgt->lock);
0284     return err;
0285 }
0286 
0287 static void mlxsw_sp_pgt_entry_port_del(struct mlxsw_sp *mlxsw_sp,
0288                     u16 mid, u16 smpe, u16 local_port)
0289 {
0290     struct mlxsw_sp_pgt_entry_port *pgt_entry_port;
0291     struct mlxsw_sp_pgt_entry *pgt_entry;
0292 
0293     mutex_lock(&mlxsw_sp->pgt->lock);
0294 
0295     pgt_entry = idr_find(&mlxsw_sp->pgt->pgt_idr, mid);
0296     if (!pgt_entry)
0297         goto out;
0298 
0299     pgt_entry_port = mlxsw_sp_pgt_entry_port_lookup(pgt_entry, local_port);
0300     if (!pgt_entry_port)
0301         goto out;
0302 
0303     mlxsw_sp_pgt_entry_port_destroy(mlxsw_sp, pgt_entry, pgt_entry_port);
0304     mlxsw_sp_pgt_entry_put(mlxsw_sp->pgt, mid);
0305 
0306 out:
0307     mutex_unlock(&mlxsw_sp->pgt->lock);
0308 }
0309 
0310 int mlxsw_sp_pgt_entry_port_set(struct mlxsw_sp *mlxsw_sp, u16 mid,
0311                 u16 smpe, u16 local_port, bool member)
0312 {
0313     if (member)
0314         return mlxsw_sp_pgt_entry_port_add(mlxsw_sp, mid, smpe,
0315                            local_port);
0316 
0317     mlxsw_sp_pgt_entry_port_del(mlxsw_sp, mid, smpe, local_port);
0318     return 0;
0319 }
0320 
0321 int mlxsw_sp_pgt_init(struct mlxsw_sp *mlxsw_sp)
0322 {
0323     struct mlxsw_sp_pgt *pgt;
0324 
0325     if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, PGT_SIZE))
0326         return -EIO;
0327 
0328     pgt = kzalloc(sizeof(*mlxsw_sp->pgt), GFP_KERNEL);
0329     if (!pgt)
0330         return -ENOMEM;
0331 
0332     idr_init(&pgt->pgt_idr);
0333     pgt->end_index = MLXSW_CORE_RES_GET(mlxsw_sp->core, PGT_SIZE);
0334     mutex_init(&pgt->lock);
0335     pgt->smpe_index_valid = mlxsw_sp->pgt_smpe_index_valid;
0336     mlxsw_sp->pgt = pgt;
0337     return 0;
0338 }
0339 
0340 void mlxsw_sp_pgt_fini(struct mlxsw_sp *mlxsw_sp)
0341 {
0342     mutex_destroy(&mlxsw_sp->pgt->lock);
0343     WARN_ON(!idr_is_empty(&mlxsw_sp->pgt->pgt_idr));
0344     idr_destroy(&mlxsw_sp->pgt->pgt_idr);
0345     kfree(mlxsw_sp->pgt);
0346 }