0001
0002
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;
0013 struct mutex lock;
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;
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
0066
0067
0068
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 }