1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2 /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ 3 4 #include <linux/kernel.h> 5 #include <linux/parman.h> 6 7 #include "reg.h" 8 #include "spectrum.h" 9 #include "core_acl_flex_actions.h" 10 #include "spectrum_mr.h" 11 12 struct mlxsw_sp1_mr_tcam_region { 13 struct mlxsw_sp *mlxsw_sp; 14 enum mlxsw_reg_rtar_key_type rtar_key_type; 15 struct parman *parman; 16 struct parman_prio *parman_prios; 17 }; 18 19 struct mlxsw_sp1_mr_tcam { 20 struct mlxsw_sp1_mr_tcam_region tcam_regions[MLXSW_SP_L3_PROTO_MAX]; 21 }; 22 23 struct mlxsw_sp1_mr_tcam_route { 24 struct parman_item parman_item; 25 struct parman_prio *parman_prio; 26 }; 27 28 static int mlxsw_sp1_mr_tcam_route_replace(struct mlxsw_sp *mlxsw_sp, 29 struct parman_item *parman_item, 30 struct mlxsw_sp_mr_route_key *key, 31 struct mlxsw_afa_block *afa_block) 32 { 33 char rmft2_pl[MLXSW_REG_RMFT2_LEN]; 34 35 switch (key->proto) { 36 case MLXSW_SP_L3_PROTO_IPV4: 37 mlxsw_reg_rmft2_ipv4_pack(rmft2_pl, true, parman_item->index, 38 key->vrid, 39 MLXSW_REG_RMFT2_IRIF_MASK_IGNORE, 0, 40 ntohl(key->group.addr4), 41 ntohl(key->group_mask.addr4), 42 ntohl(key->source.addr4), 43 ntohl(key->source_mask.addr4), 44 mlxsw_afa_block_first_set(afa_block)); 45 break; 46 case MLXSW_SP_L3_PROTO_IPV6: 47 mlxsw_reg_rmft2_ipv6_pack(rmft2_pl, true, parman_item->index, 48 key->vrid, 49 MLXSW_REG_RMFT2_IRIF_MASK_IGNORE, 0, 50 key->group.addr6, 51 key->group_mask.addr6, 52 key->source.addr6, 53 key->source_mask.addr6, 54 mlxsw_afa_block_first_set(afa_block)); 55 } 56 57 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rmft2), rmft2_pl); 58 } 59 60 static int mlxsw_sp1_mr_tcam_route_remove(struct mlxsw_sp *mlxsw_sp, 61 struct parman_item *parman_item, 62 struct mlxsw_sp_mr_route_key *key) 63 { 64 struct in6_addr zero_addr = IN6ADDR_ANY_INIT; 65 char rmft2_pl[MLXSW_REG_RMFT2_LEN]; 66 67 switch (key->proto) { 68 case MLXSW_SP_L3_PROTO_IPV4: 69 mlxsw_reg_rmft2_ipv4_pack(rmft2_pl, false, parman_item->index, 70 key->vrid, 0, 0, 0, 0, 0, 0, NULL); 71 break; 72 case MLXSW_SP_L3_PROTO_IPV6: 73 mlxsw_reg_rmft2_ipv6_pack(rmft2_pl, false, parman_item->index, 74 key->vrid, 0, 0, zero_addr, zero_addr, 75 zero_addr, zero_addr, NULL); 76 break; 77 } 78 79 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rmft2), rmft2_pl); 80 } 81 82 static struct mlxsw_sp1_mr_tcam_region * 83 mlxsw_sp1_mr_tcam_protocol_region(struct mlxsw_sp1_mr_tcam *mr_tcam, 84 enum mlxsw_sp_l3proto proto) 85 { 86 return &mr_tcam->tcam_regions[proto]; 87 } 88 89 static int 90 mlxsw_sp1_mr_tcam_route_parman_item_add(struct mlxsw_sp1_mr_tcam *mr_tcam, 91 struct mlxsw_sp1_mr_tcam_route *route, 92 struct mlxsw_sp_mr_route_key *key, 93 enum mlxsw_sp_mr_route_prio prio) 94 { 95 struct mlxsw_sp1_mr_tcam_region *tcam_region; 96 int err; 97 98 tcam_region = mlxsw_sp1_mr_tcam_protocol_region(mr_tcam, key->proto); 99 err = parman_item_add(tcam_region->parman, 100 &tcam_region->parman_prios[prio], 101 &route->parman_item); 102 if (err) 103 return err; 104 105 route->parman_prio = &tcam_region->parman_prios[prio]; 106 return 0; 107 } 108 109 static void 110 mlxsw_sp1_mr_tcam_route_parman_item_remove(struct mlxsw_sp1_mr_tcam *mr_tcam, 111 struct mlxsw_sp1_mr_tcam_route *route, 112 struct mlxsw_sp_mr_route_key *key) 113 { 114 struct mlxsw_sp1_mr_tcam_region *tcam_region; 115 116 tcam_region = mlxsw_sp1_mr_tcam_protocol_region(mr_tcam, key->proto); 117 parman_item_remove(tcam_region->parman, 118 route->parman_prio, &route->parman_item); 119 } 120 121 static int 122 mlxsw_sp1_mr_tcam_route_create(struct mlxsw_sp *mlxsw_sp, void *priv, 123 void *route_priv, 124 struct mlxsw_sp_mr_route_key *key, 125 struct mlxsw_afa_block *afa_block, 126 enum mlxsw_sp_mr_route_prio prio) 127 { 128 struct mlxsw_sp1_mr_tcam_route *route = route_priv; 129 struct mlxsw_sp1_mr_tcam *mr_tcam = priv; 130 int err; 131 132 err = mlxsw_sp1_mr_tcam_route_parman_item_add(mr_tcam, route, 133 key, prio); 134 if (err) 135 return err; 136 137 err = mlxsw_sp1_mr_tcam_route_replace(mlxsw_sp, &route->parman_item, 138 key, afa_block); 139 if (err) 140 goto err_route_replace; 141 return 0; 142 143 err_route_replace: 144 mlxsw_sp1_mr_tcam_route_parman_item_remove(mr_tcam, route, key); 145 return err; 146 } 147 148 static void 149 mlxsw_sp1_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp, void *priv, 150 void *route_priv, 151 struct mlxsw_sp_mr_route_key *key) 152 { 153 struct mlxsw_sp1_mr_tcam_route *route = route_priv; 154 struct mlxsw_sp1_mr_tcam *mr_tcam = priv; 155 156 mlxsw_sp1_mr_tcam_route_remove(mlxsw_sp, &route->parman_item, key); 157 mlxsw_sp1_mr_tcam_route_parman_item_remove(mr_tcam, route, key); 158 } 159 160 static int 161 mlxsw_sp1_mr_tcam_route_update(struct mlxsw_sp *mlxsw_sp, 162 void *route_priv, 163 struct mlxsw_sp_mr_route_key *key, 164 struct mlxsw_afa_block *afa_block) 165 { 166 struct mlxsw_sp1_mr_tcam_route *route = route_priv; 167 168 return mlxsw_sp1_mr_tcam_route_replace(mlxsw_sp, &route->parman_item, 169 key, afa_block); 170 } 171 172 #define MLXSW_SP1_MR_TCAM_REGION_BASE_COUNT 16 173 #define MLXSW_SP1_MR_TCAM_REGION_RESIZE_STEP 16 174 175 static int 176 mlxsw_sp1_mr_tcam_region_alloc(struct mlxsw_sp1_mr_tcam_region *mr_tcam_region) 177 { 178 struct mlxsw_sp *mlxsw_sp = mr_tcam_region->mlxsw_sp; 179 char rtar_pl[MLXSW_REG_RTAR_LEN]; 180 181 mlxsw_reg_rtar_pack(rtar_pl, MLXSW_REG_RTAR_OP_ALLOCATE, 182 mr_tcam_region->rtar_key_type, 183 MLXSW_SP1_MR_TCAM_REGION_BASE_COUNT); 184 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rtar), rtar_pl); 185 } 186 187 static void 188 mlxsw_sp1_mr_tcam_region_free(struct mlxsw_sp1_mr_tcam_region *mr_tcam_region) 189 { 190 struct mlxsw_sp *mlxsw_sp = mr_tcam_region->mlxsw_sp; 191 char rtar_pl[MLXSW_REG_RTAR_LEN]; 192 193 mlxsw_reg_rtar_pack(rtar_pl, MLXSW_REG_RTAR_OP_DEALLOCATE, 194 mr_tcam_region->rtar_key_type, 0); 195 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rtar), rtar_pl); 196 } 197 198 static int mlxsw_sp1_mr_tcam_region_parman_resize(void *priv, 199 unsigned long new_count) 200 { 201 struct mlxsw_sp1_mr_tcam_region *mr_tcam_region = priv; 202 struct mlxsw_sp *mlxsw_sp = mr_tcam_region->mlxsw_sp; 203 char rtar_pl[MLXSW_REG_RTAR_LEN]; 204 u64 max_tcam_rules; 205 206 max_tcam_rules = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_TCAM_RULES); 207 if (new_count > max_tcam_rules) 208 return -EINVAL; 209 mlxsw_reg_rtar_pack(rtar_pl, MLXSW_REG_RTAR_OP_RESIZE, 210 mr_tcam_region->rtar_key_type, new_count); 211 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rtar), rtar_pl); 212 } 213 214 static void mlxsw_sp1_mr_tcam_region_parman_move(void *priv, 215 unsigned long from_index, 216 unsigned long to_index, 217 unsigned long count) 218 { 219 struct mlxsw_sp1_mr_tcam_region *mr_tcam_region = priv; 220 struct mlxsw_sp *mlxsw_sp = mr_tcam_region->mlxsw_sp; 221 char rrcr_pl[MLXSW_REG_RRCR_LEN]; 222 223 mlxsw_reg_rrcr_pack(rrcr_pl, MLXSW_REG_RRCR_OP_MOVE, 224 from_index, count, 225 mr_tcam_region->rtar_key_type, to_index); 226 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rrcr), rrcr_pl); 227 } 228 229 static const struct parman_ops mlxsw_sp1_mr_tcam_region_parman_ops = { 230 .base_count = MLXSW_SP1_MR_TCAM_REGION_BASE_COUNT, 231 .resize_step = MLXSW_SP1_MR_TCAM_REGION_RESIZE_STEP, 232 .resize = mlxsw_sp1_mr_tcam_region_parman_resize, 233 .move = mlxsw_sp1_mr_tcam_region_parman_move, 234 .algo = PARMAN_ALGO_TYPE_LSORT, 235 }; 236 237 static int 238 mlxsw_sp1_mr_tcam_region_init(struct mlxsw_sp *mlxsw_sp, 239 struct mlxsw_sp1_mr_tcam_region *mr_tcam_region, 240 enum mlxsw_reg_rtar_key_type rtar_key_type) 241 { 242 struct parman_prio *parman_prios; 243 struct parman *parman; 244 int err; 245 int i; 246 247 mr_tcam_region->rtar_key_type = rtar_key_type; 248 mr_tcam_region->mlxsw_sp = mlxsw_sp; 249 250 err = mlxsw_sp1_mr_tcam_region_alloc(mr_tcam_region); 251 if (err) 252 return err; 253 254 parman = parman_create(&mlxsw_sp1_mr_tcam_region_parman_ops, 255 mr_tcam_region); 256 if (!parman) { 257 err = -ENOMEM; 258 goto err_parman_create; 259 } 260 mr_tcam_region->parman = parman; 261 262 parman_prios = kmalloc_array(MLXSW_SP_MR_ROUTE_PRIO_MAX + 1, 263 sizeof(*parman_prios), GFP_KERNEL); 264 if (!parman_prios) { 265 err = -ENOMEM; 266 goto err_parman_prios_alloc; 267 } 268 mr_tcam_region->parman_prios = parman_prios; 269 270 for (i = 0; i < MLXSW_SP_MR_ROUTE_PRIO_MAX + 1; i++) 271 parman_prio_init(mr_tcam_region->parman, 272 &mr_tcam_region->parman_prios[i], i); 273 return 0; 274 275 err_parman_prios_alloc: 276 parman_destroy(parman); 277 err_parman_create: 278 mlxsw_sp1_mr_tcam_region_free(mr_tcam_region); 279 return err; 280 } 281 282 static void 283 mlxsw_sp1_mr_tcam_region_fini(struct mlxsw_sp1_mr_tcam_region *mr_tcam_region) 284 { 285 int i; 286 287 for (i = 0; i < MLXSW_SP_MR_ROUTE_PRIO_MAX + 1; i++) 288 parman_prio_fini(&mr_tcam_region->parman_prios[i]); 289 kfree(mr_tcam_region->parman_prios); 290 parman_destroy(mr_tcam_region->parman); 291 mlxsw_sp1_mr_tcam_region_free(mr_tcam_region); 292 } 293 294 static int mlxsw_sp1_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv) 295 { 296 struct mlxsw_sp1_mr_tcam *mr_tcam = priv; 297 struct mlxsw_sp1_mr_tcam_region *region = &mr_tcam->tcam_regions[0]; 298 u32 rtar_key; 299 int err; 300 301 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_TCAM_RULES)) 302 return -EIO; 303 304 rtar_key = MLXSW_REG_RTAR_KEY_TYPE_IPV4_MULTICAST; 305 err = mlxsw_sp1_mr_tcam_region_init(mlxsw_sp, 306 ®ion[MLXSW_SP_L3_PROTO_IPV4], 307 rtar_key); 308 if (err) 309 return err; 310 311 rtar_key = MLXSW_REG_RTAR_KEY_TYPE_IPV6_MULTICAST; 312 err = mlxsw_sp1_mr_tcam_region_init(mlxsw_sp, 313 ®ion[MLXSW_SP_L3_PROTO_IPV6], 314 rtar_key); 315 if (err) 316 goto err_ipv6_region_init; 317 318 return 0; 319 320 err_ipv6_region_init: 321 mlxsw_sp1_mr_tcam_region_fini(®ion[MLXSW_SP_L3_PROTO_IPV4]); 322 return err; 323 } 324 325 static void mlxsw_sp1_mr_tcam_fini(void *priv) 326 { 327 struct mlxsw_sp1_mr_tcam *mr_tcam = priv; 328 struct mlxsw_sp1_mr_tcam_region *region = &mr_tcam->tcam_regions[0]; 329 330 mlxsw_sp1_mr_tcam_region_fini(®ion[MLXSW_SP_L3_PROTO_IPV6]); 331 mlxsw_sp1_mr_tcam_region_fini(®ion[MLXSW_SP_L3_PROTO_IPV4]); 332 } 333 334 const struct mlxsw_sp_mr_tcam_ops mlxsw_sp1_mr_tcam_ops = { 335 .priv_size = sizeof(struct mlxsw_sp1_mr_tcam), 336 .init = mlxsw_sp1_mr_tcam_init, 337 .fini = mlxsw_sp1_mr_tcam_fini, 338 .route_priv_size = sizeof(struct mlxsw_sp1_mr_tcam_route), 339 .route_create = mlxsw_sp1_mr_tcam_route_create, 340 .route_destroy = mlxsw_sp1_mr_tcam_route_destroy, 341 .route_update = mlxsw_sp1_mr_tcam_route_update, 342 }; 343