prestera_router_hw.c (7456B)
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2/* Copyright (c) 2019-2021 Marvell International Ltd. All rights reserved */ 3 4#include <linux/rhashtable.h> 5 6#include "prestera.h" 7#include "prestera_hw.h" 8#include "prestera_router_hw.h" 9#include "prestera_acl.h" 10 11/* +--+ 12 * +------->|vr|<-+ 13 * | +--+ | 14 * | | 15 * +-+-------+ +--+---+-+ 16 * |rif_entry| |fib_node| 17 * +---------+ +--------+ 18 * Rif is Fib - is exit point 19 * used as 20 * entry point 21 * for vr in hw 22 */ 23 24#define PRESTERA_NHGR_UNUSED (0) 25#define PRESTERA_NHGR_DROP (0xFFFFFFFF) 26 27static const struct rhashtable_params __prestera_fib_ht_params = { 28 .key_offset = offsetof(struct prestera_fib_node, key), 29 .head_offset = offsetof(struct prestera_fib_node, ht_node), 30 .key_len = sizeof(struct prestera_fib_key), 31 .automatic_shrinking = true, 32}; 33 34int prestera_router_hw_init(struct prestera_switch *sw) 35{ 36 int err; 37 38 err = rhashtable_init(&sw->router->fib_ht, 39 &__prestera_fib_ht_params); 40 if (err) 41 goto err_fib_ht_init; 42 43 INIT_LIST_HEAD(&sw->router->vr_list); 44 INIT_LIST_HEAD(&sw->router->rif_entry_list); 45 46err_fib_ht_init: 47 return 0; 48} 49 50void prestera_router_hw_fini(struct prestera_switch *sw) 51{ 52 WARN_ON(!list_empty(&sw->router->vr_list)); 53 WARN_ON(!list_empty(&sw->router->rif_entry_list)); 54 rhashtable_destroy(&sw->router->fib_ht); 55} 56 57static struct prestera_vr *__prestera_vr_find(struct prestera_switch *sw, 58 u32 tb_id) 59{ 60 struct prestera_vr *vr; 61 62 list_for_each_entry(vr, &sw->router->vr_list, router_node) { 63 if (vr->tb_id == tb_id) 64 return vr; 65 } 66 67 return NULL; 68} 69 70static struct prestera_vr *__prestera_vr_create(struct prestera_switch *sw, 71 u32 tb_id, 72 struct netlink_ext_ack *extack) 73{ 74 struct prestera_vr *vr; 75 int err; 76 77 vr = kzalloc(sizeof(*vr), GFP_KERNEL); 78 if (!vr) { 79 err = -ENOMEM; 80 goto err_alloc_vr; 81 } 82 83 vr->tb_id = tb_id; 84 85 err = prestera_hw_vr_create(sw, &vr->hw_vr_id); 86 if (err) 87 goto err_hw_create; 88 89 list_add(&vr->router_node, &sw->router->vr_list); 90 91 return vr; 92 93err_hw_create: 94 kfree(vr); 95err_alloc_vr: 96 return ERR_PTR(err); 97} 98 99static void __prestera_vr_destroy(struct prestera_switch *sw, 100 struct prestera_vr *vr) 101{ 102 list_del(&vr->router_node); 103 prestera_hw_vr_delete(sw, vr->hw_vr_id); 104 kfree(vr); 105} 106 107static struct prestera_vr *prestera_vr_get(struct prestera_switch *sw, u32 tb_id, 108 struct netlink_ext_ack *extack) 109{ 110 struct prestera_vr *vr; 111 112 vr = __prestera_vr_find(sw, tb_id); 113 if (vr) { 114 refcount_inc(&vr->refcount); 115 } else { 116 vr = __prestera_vr_create(sw, tb_id, extack); 117 if (IS_ERR(vr)) 118 return ERR_CAST(vr); 119 120 refcount_set(&vr->refcount, 1); 121 } 122 123 return vr; 124} 125 126static void prestera_vr_put(struct prestera_switch *sw, struct prestera_vr *vr) 127{ 128 if (refcount_dec_and_test(&vr->refcount)) 129 __prestera_vr_destroy(sw, vr); 130} 131 132/* iface is overhead struct. vr_id also can be removed. */ 133static int 134__prestera_rif_entry_key_copy(const struct prestera_rif_entry_key *in, 135 struct prestera_rif_entry_key *out) 136{ 137 memset(out, 0, sizeof(*out)); 138 139 switch (in->iface.type) { 140 case PRESTERA_IF_PORT_E: 141 out->iface.dev_port.hw_dev_num = in->iface.dev_port.hw_dev_num; 142 out->iface.dev_port.port_num = in->iface.dev_port.port_num; 143 break; 144 case PRESTERA_IF_LAG_E: 145 out->iface.lag_id = in->iface.lag_id; 146 break; 147 case PRESTERA_IF_VID_E: 148 out->iface.vlan_id = in->iface.vlan_id; 149 break; 150 default: 151 WARN(1, "Unsupported iface type"); 152 return -EINVAL; 153 } 154 155 out->iface.type = in->iface.type; 156 return 0; 157} 158 159struct prestera_rif_entry * 160prestera_rif_entry_find(const struct prestera_switch *sw, 161 const struct prestera_rif_entry_key *k) 162{ 163 struct prestera_rif_entry *rif_entry; 164 struct prestera_rif_entry_key lk; /* lookup key */ 165 166 if (__prestera_rif_entry_key_copy(k, &lk)) 167 return NULL; 168 169 list_for_each_entry(rif_entry, &sw->router->rif_entry_list, 170 router_node) { 171 if (!memcmp(k, &rif_entry->key, sizeof(*k))) 172 return rif_entry; 173 } 174 175 return NULL; 176} 177 178void prestera_rif_entry_destroy(struct prestera_switch *sw, 179 struct prestera_rif_entry *e) 180{ 181 struct prestera_iface iface; 182 183 list_del(&e->router_node); 184 185 memcpy(&iface, &e->key.iface, sizeof(iface)); 186 iface.vr_id = e->vr->hw_vr_id; 187 prestera_hw_rif_delete(sw, e->hw_id, &iface); 188 189 prestera_vr_put(sw, e->vr); 190 kfree(e); 191} 192 193struct prestera_rif_entry * 194prestera_rif_entry_create(struct prestera_switch *sw, 195 struct prestera_rif_entry_key *k, 196 u32 tb_id, const unsigned char *addr) 197{ 198 int err; 199 struct prestera_rif_entry *e; 200 struct prestera_iface iface; 201 202 e = kzalloc(sizeof(*e), GFP_KERNEL); 203 if (!e) 204 goto err_kzalloc; 205 206 if (__prestera_rif_entry_key_copy(k, &e->key)) 207 goto err_key_copy; 208 209 e->vr = prestera_vr_get(sw, tb_id, NULL); 210 if (IS_ERR(e->vr)) 211 goto err_vr_get; 212 213 memcpy(&e->addr, addr, sizeof(e->addr)); 214 215 /* HW */ 216 memcpy(&iface, &e->key.iface, sizeof(iface)); 217 iface.vr_id = e->vr->hw_vr_id; 218 err = prestera_hw_rif_create(sw, &iface, e->addr, &e->hw_id); 219 if (err) 220 goto err_hw_create; 221 222 list_add(&e->router_node, &sw->router->rif_entry_list); 223 224 return e; 225 226err_hw_create: 227 prestera_vr_put(sw, e->vr); 228err_vr_get: 229err_key_copy: 230 kfree(e); 231err_kzalloc: 232 return NULL; 233} 234 235struct prestera_fib_node * 236prestera_fib_node_find(struct prestera_switch *sw, struct prestera_fib_key *key) 237{ 238 struct prestera_fib_node *fib_node; 239 240 fib_node = rhashtable_lookup_fast(&sw->router->fib_ht, key, 241 __prestera_fib_ht_params); 242 return fib_node; 243} 244 245static void __prestera_fib_node_destruct(struct prestera_switch *sw, 246 struct prestera_fib_node *fib_node) 247{ 248 struct prestera_vr *vr; 249 250 vr = fib_node->info.vr; 251 prestera_hw_lpm_del(sw, vr->hw_vr_id, fib_node->key.addr.u.ipv4, 252 fib_node->key.prefix_len); 253 switch (fib_node->info.type) { 254 case PRESTERA_FIB_TYPE_TRAP: 255 break; 256 case PRESTERA_FIB_TYPE_DROP: 257 break; 258 default: 259 pr_err("Unknown fib_node->info.type = %d", 260 fib_node->info.type); 261 } 262 263 prestera_vr_put(sw, vr); 264} 265 266void prestera_fib_node_destroy(struct prestera_switch *sw, 267 struct prestera_fib_node *fib_node) 268{ 269 __prestera_fib_node_destruct(sw, fib_node); 270 rhashtable_remove_fast(&sw->router->fib_ht, &fib_node->ht_node, 271 __prestera_fib_ht_params); 272 kfree(fib_node); 273} 274 275struct prestera_fib_node * 276prestera_fib_node_create(struct prestera_switch *sw, 277 struct prestera_fib_key *key, 278 enum prestera_fib_type fib_type) 279{ 280 struct prestera_fib_node *fib_node; 281 u32 grp_id; 282 struct prestera_vr *vr; 283 int err; 284 285 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL); 286 if (!fib_node) 287 goto err_kzalloc; 288 289 memcpy(&fib_node->key, key, sizeof(*key)); 290 fib_node->info.type = fib_type; 291 292 vr = prestera_vr_get(sw, key->tb_id, NULL); 293 if (IS_ERR(vr)) 294 goto err_vr_get; 295 296 fib_node->info.vr = vr; 297 298 switch (fib_type) { 299 case PRESTERA_FIB_TYPE_TRAP: 300 grp_id = PRESTERA_NHGR_UNUSED; 301 break; 302 case PRESTERA_FIB_TYPE_DROP: 303 grp_id = PRESTERA_NHGR_DROP; 304 break; 305 default: 306 pr_err("Unsupported fib_type %d", fib_type); 307 goto err_nh_grp_get; 308 } 309 310 err = prestera_hw_lpm_add(sw, vr->hw_vr_id, key->addr.u.ipv4, 311 key->prefix_len, grp_id); 312 if (err) 313 goto err_lpm_add; 314 315 err = rhashtable_insert_fast(&sw->router->fib_ht, &fib_node->ht_node, 316 __prestera_fib_ht_params); 317 if (err) 318 goto err_ht_insert; 319 320 return fib_node; 321 322err_ht_insert: 323 prestera_hw_lpm_del(sw, vr->hw_vr_id, key->addr.u.ipv4, 324 key->prefix_len); 325err_lpm_add: 326err_nh_grp_get: 327 prestera_vr_put(sw, vr); 328err_vr_get: 329 kfree(fib_node); 330err_kzalloc: 331 return NULL; 332}