| 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(payload: rmft2_pl, v: true, offset: parman_item->index, |
| 38 | virtual_router: key->vrid, |
| 39 | irif_mask: MLXSW_REG_RMFT2_IRIF_MASK_IGNORE, irif: 0, |
| 40 | ntohl(key->group.addr4), |
| 41 | ntohl(key->group_mask.addr4), |
| 42 | ntohl(key->source.addr4), |
| 43 | ntohl(key->source_mask.addr4), |
| 44 | flexible_action_set: mlxsw_afa_block_first_set(block: afa_block)); |
| 45 | break; |
| 46 | case MLXSW_SP_L3_PROTO_IPV6: |
| 47 | mlxsw_reg_rmft2_ipv6_pack(payload: rmft2_pl, v: true, offset: parman_item->index, |
| 48 | virtual_router: key->vrid, |
| 49 | irif_mask: MLXSW_REG_RMFT2_IRIF_MASK_IGNORE, irif: 0, |
| 50 | dip6: key->group.addr6, |
| 51 | dip6_mask: key->group_mask.addr6, |
| 52 | sip6: key->source.addr6, |
| 53 | sip6_mask: key->source_mask.addr6, |
| 54 | flexible_action_set: mlxsw_afa_block_first_set(block: afa_block)); |
| 55 | } |
| 56 | |
| 57 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(rmft2), payload: 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(payload: rmft2_pl, v: false, offset: parman_item->index, |
| 70 | virtual_router: key->vrid, irif_mask: 0, irif: 0, dip4: 0, dip4_mask: 0, sip4: 0, sip4_mask: 0, NULL); |
| 71 | break; |
| 72 | case MLXSW_SP_L3_PROTO_IPV6: |
| 73 | mlxsw_reg_rmft2_ipv6_pack(payload: rmft2_pl, v: false, offset: parman_item->index, |
| 74 | virtual_router: key->vrid, irif_mask: 0, irif: 0, dip6: zero_addr, dip6_mask: zero_addr, |
| 75 | sip6: zero_addr, sip6_mask: zero_addr, NULL); |
| 76 | break; |
| 77 | } |
| 78 | |
| 79 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(rmft2), payload: 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, proto: key->proto); |
| 99 | err = parman_item_add(parman: tcam_region->parman, |
| 100 | prio: &tcam_region->parman_prios[prio], |
| 101 | item: &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, proto: key->proto); |
| 117 | parman_item_remove(parman: tcam_region->parman, |
| 118 | prio: route->parman_prio, item: &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, parman_item: &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, parman_item: &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, parman_item: &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(payload: rtar_pl, op: MLXSW_REG_RTAR_OP_ALLOCATE, |
| 182 | key_type: mr_tcam_region->rtar_key_type, |
| 183 | MLXSW_SP1_MR_TCAM_REGION_BASE_COUNT); |
| 184 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(rtar), payload: 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(payload: rtar_pl, op: MLXSW_REG_RTAR_OP_DEALLOCATE, |
| 194 | key_type: mr_tcam_region->rtar_key_type, region_size: 0); |
| 195 | mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(rtar), payload: 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(payload: rtar_pl, op: MLXSW_REG_RTAR_OP_RESIZE, |
| 210 | key_type: mr_tcam_region->rtar_key_type, region_size: new_count); |
| 211 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(rtar), payload: 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(payload: rrcr_pl, op: MLXSW_REG_RRCR_OP_MOVE, |
| 224 | offset: from_index, size: count, |
| 225 | table_id: mr_tcam_region->rtar_key_type, dest_offset: to_index); |
| 226 | mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(rrcr), payload: 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(ops: &mlxsw_sp1_mr_tcam_region_parman_ops, |
| 255 | priv: 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(parman: mr_tcam_region->parman, |
| 272 | prio: &mr_tcam_region->parman_prios[i], priority: 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(prio: &mr_tcam_region->parman_prios[i]); |
| 289 | kfree(objp: mr_tcam_region->parman_prios); |
| 290 | parman_destroy(parman: 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 | mr_tcam_region: ®ion[MLXSW_SP_L3_PROTO_IPV4], |
| 307 | rtar_key_type: 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 | mr_tcam_region: ®ion[MLXSW_SP_L3_PROTO_IPV6], |
| 314 | rtar_key_type: 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(mr_tcam_region: ®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(mr_tcam_region: ®ion[MLXSW_SP_L3_PROTO_IPV6]); |
| 331 | mlxsw_sp1_mr_tcam_region_fini(mr_tcam_region: ®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 | |