0001
0002
0003
0004 #include <linux/rhashtable.h>
0005
0006 #include "prestera.h"
0007 #include "prestera_hw.h"
0008 #include "prestera_router_hw.h"
0009 #include "prestera_acl.h"
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024 #define PRESTERA_NHGR_UNUSED (0)
0025 #define PRESTERA_NHGR_DROP (0xFFFFFFFF)
0026
0027 static const struct rhashtable_params __prestera_fib_ht_params = {
0028 .key_offset = offsetof(struct prestera_fib_node, key),
0029 .head_offset = offsetof(struct prestera_fib_node, ht_node),
0030 .key_len = sizeof(struct prestera_fib_key),
0031 .automatic_shrinking = true,
0032 };
0033
0034 int prestera_router_hw_init(struct prestera_switch *sw)
0035 {
0036 int err;
0037
0038 err = rhashtable_init(&sw->router->fib_ht,
0039 &__prestera_fib_ht_params);
0040 if (err)
0041 goto err_fib_ht_init;
0042
0043 INIT_LIST_HEAD(&sw->router->vr_list);
0044 INIT_LIST_HEAD(&sw->router->rif_entry_list);
0045
0046 err_fib_ht_init:
0047 return 0;
0048 }
0049
0050 void prestera_router_hw_fini(struct prestera_switch *sw)
0051 {
0052 WARN_ON(!list_empty(&sw->router->vr_list));
0053 WARN_ON(!list_empty(&sw->router->rif_entry_list));
0054 rhashtable_destroy(&sw->router->fib_ht);
0055 }
0056
0057 static struct prestera_vr *__prestera_vr_find(struct prestera_switch *sw,
0058 u32 tb_id)
0059 {
0060 struct prestera_vr *vr;
0061
0062 list_for_each_entry(vr, &sw->router->vr_list, router_node) {
0063 if (vr->tb_id == tb_id)
0064 return vr;
0065 }
0066
0067 return NULL;
0068 }
0069
0070 static struct prestera_vr *__prestera_vr_create(struct prestera_switch *sw,
0071 u32 tb_id,
0072 struct netlink_ext_ack *extack)
0073 {
0074 struct prestera_vr *vr;
0075 int err;
0076
0077 vr = kzalloc(sizeof(*vr), GFP_KERNEL);
0078 if (!vr) {
0079 err = -ENOMEM;
0080 goto err_alloc_vr;
0081 }
0082
0083 vr->tb_id = tb_id;
0084
0085 err = prestera_hw_vr_create(sw, &vr->hw_vr_id);
0086 if (err)
0087 goto err_hw_create;
0088
0089 list_add(&vr->router_node, &sw->router->vr_list);
0090
0091 return vr;
0092
0093 err_hw_create:
0094 kfree(vr);
0095 err_alloc_vr:
0096 return ERR_PTR(err);
0097 }
0098
0099 static void __prestera_vr_destroy(struct prestera_switch *sw,
0100 struct prestera_vr *vr)
0101 {
0102 list_del(&vr->router_node);
0103 prestera_hw_vr_delete(sw, vr->hw_vr_id);
0104 kfree(vr);
0105 }
0106
0107 static struct prestera_vr *prestera_vr_get(struct prestera_switch *sw, u32 tb_id,
0108 struct netlink_ext_ack *extack)
0109 {
0110 struct prestera_vr *vr;
0111
0112 vr = __prestera_vr_find(sw, tb_id);
0113 if (vr) {
0114 refcount_inc(&vr->refcount);
0115 } else {
0116 vr = __prestera_vr_create(sw, tb_id, extack);
0117 if (IS_ERR(vr))
0118 return ERR_CAST(vr);
0119
0120 refcount_set(&vr->refcount, 1);
0121 }
0122
0123 return vr;
0124 }
0125
0126 static void prestera_vr_put(struct prestera_switch *sw, struct prestera_vr *vr)
0127 {
0128 if (refcount_dec_and_test(&vr->refcount))
0129 __prestera_vr_destroy(sw, vr);
0130 }
0131
0132
0133 static int
0134 __prestera_rif_entry_key_copy(const struct prestera_rif_entry_key *in,
0135 struct prestera_rif_entry_key *out)
0136 {
0137 memset(out, 0, sizeof(*out));
0138
0139 switch (in->iface.type) {
0140 case PRESTERA_IF_PORT_E:
0141 out->iface.dev_port.hw_dev_num = in->iface.dev_port.hw_dev_num;
0142 out->iface.dev_port.port_num = in->iface.dev_port.port_num;
0143 break;
0144 case PRESTERA_IF_LAG_E:
0145 out->iface.lag_id = in->iface.lag_id;
0146 break;
0147 case PRESTERA_IF_VID_E:
0148 out->iface.vlan_id = in->iface.vlan_id;
0149 break;
0150 default:
0151 WARN(1, "Unsupported iface type");
0152 return -EINVAL;
0153 }
0154
0155 out->iface.type = in->iface.type;
0156 return 0;
0157 }
0158
0159 struct prestera_rif_entry *
0160 prestera_rif_entry_find(const struct prestera_switch *sw,
0161 const struct prestera_rif_entry_key *k)
0162 {
0163 struct prestera_rif_entry *rif_entry;
0164 struct prestera_rif_entry_key lk;
0165
0166 if (__prestera_rif_entry_key_copy(k, &lk))
0167 return NULL;
0168
0169 list_for_each_entry(rif_entry, &sw->router->rif_entry_list,
0170 router_node) {
0171 if (!memcmp(k, &rif_entry->key, sizeof(*k)))
0172 return rif_entry;
0173 }
0174
0175 return NULL;
0176 }
0177
0178 void prestera_rif_entry_destroy(struct prestera_switch *sw,
0179 struct prestera_rif_entry *e)
0180 {
0181 struct prestera_iface iface;
0182
0183 list_del(&e->router_node);
0184
0185 memcpy(&iface, &e->key.iface, sizeof(iface));
0186 iface.vr_id = e->vr->hw_vr_id;
0187 prestera_hw_rif_delete(sw, e->hw_id, &iface);
0188
0189 prestera_vr_put(sw, e->vr);
0190 kfree(e);
0191 }
0192
0193 struct prestera_rif_entry *
0194 prestera_rif_entry_create(struct prestera_switch *sw,
0195 struct prestera_rif_entry_key *k,
0196 u32 tb_id, const unsigned char *addr)
0197 {
0198 int err;
0199 struct prestera_rif_entry *e;
0200 struct prestera_iface iface;
0201
0202 e = kzalloc(sizeof(*e), GFP_KERNEL);
0203 if (!e)
0204 goto err_kzalloc;
0205
0206 if (__prestera_rif_entry_key_copy(k, &e->key))
0207 goto err_key_copy;
0208
0209 e->vr = prestera_vr_get(sw, tb_id, NULL);
0210 if (IS_ERR(e->vr))
0211 goto err_vr_get;
0212
0213 memcpy(&e->addr, addr, sizeof(e->addr));
0214
0215
0216 memcpy(&iface, &e->key.iface, sizeof(iface));
0217 iface.vr_id = e->vr->hw_vr_id;
0218 err = prestera_hw_rif_create(sw, &iface, e->addr, &e->hw_id);
0219 if (err)
0220 goto err_hw_create;
0221
0222 list_add(&e->router_node, &sw->router->rif_entry_list);
0223
0224 return e;
0225
0226 err_hw_create:
0227 prestera_vr_put(sw, e->vr);
0228 err_vr_get:
0229 err_key_copy:
0230 kfree(e);
0231 err_kzalloc:
0232 return NULL;
0233 }
0234
0235 struct prestera_fib_node *
0236 prestera_fib_node_find(struct prestera_switch *sw, struct prestera_fib_key *key)
0237 {
0238 struct prestera_fib_node *fib_node;
0239
0240 fib_node = rhashtable_lookup_fast(&sw->router->fib_ht, key,
0241 __prestera_fib_ht_params);
0242 return fib_node;
0243 }
0244
0245 static void __prestera_fib_node_destruct(struct prestera_switch *sw,
0246 struct prestera_fib_node *fib_node)
0247 {
0248 struct prestera_vr *vr;
0249
0250 vr = fib_node->info.vr;
0251 prestera_hw_lpm_del(sw, vr->hw_vr_id, fib_node->key.addr.u.ipv4,
0252 fib_node->key.prefix_len);
0253 switch (fib_node->info.type) {
0254 case PRESTERA_FIB_TYPE_TRAP:
0255 break;
0256 case PRESTERA_FIB_TYPE_DROP:
0257 break;
0258 default:
0259 pr_err("Unknown fib_node->info.type = %d",
0260 fib_node->info.type);
0261 }
0262
0263 prestera_vr_put(sw, vr);
0264 }
0265
0266 void prestera_fib_node_destroy(struct prestera_switch *sw,
0267 struct prestera_fib_node *fib_node)
0268 {
0269 __prestera_fib_node_destruct(sw, fib_node);
0270 rhashtable_remove_fast(&sw->router->fib_ht, &fib_node->ht_node,
0271 __prestera_fib_ht_params);
0272 kfree(fib_node);
0273 }
0274
0275 struct prestera_fib_node *
0276 prestera_fib_node_create(struct prestera_switch *sw,
0277 struct prestera_fib_key *key,
0278 enum prestera_fib_type fib_type)
0279 {
0280 struct prestera_fib_node *fib_node;
0281 u32 grp_id;
0282 struct prestera_vr *vr;
0283 int err;
0284
0285 fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
0286 if (!fib_node)
0287 goto err_kzalloc;
0288
0289 memcpy(&fib_node->key, key, sizeof(*key));
0290 fib_node->info.type = fib_type;
0291
0292 vr = prestera_vr_get(sw, key->tb_id, NULL);
0293 if (IS_ERR(vr))
0294 goto err_vr_get;
0295
0296 fib_node->info.vr = vr;
0297
0298 switch (fib_type) {
0299 case PRESTERA_FIB_TYPE_TRAP:
0300 grp_id = PRESTERA_NHGR_UNUSED;
0301 break;
0302 case PRESTERA_FIB_TYPE_DROP:
0303 grp_id = PRESTERA_NHGR_DROP;
0304 break;
0305 default:
0306 pr_err("Unsupported fib_type %d", fib_type);
0307 goto err_nh_grp_get;
0308 }
0309
0310 err = prestera_hw_lpm_add(sw, vr->hw_vr_id, key->addr.u.ipv4,
0311 key->prefix_len, grp_id);
0312 if (err)
0313 goto err_lpm_add;
0314
0315 err = rhashtable_insert_fast(&sw->router->fib_ht, &fib_node->ht_node,
0316 __prestera_fib_ht_params);
0317 if (err)
0318 goto err_ht_insert;
0319
0320 return fib_node;
0321
0322 err_ht_insert:
0323 prestera_hw_lpm_del(sw, vr->hw_vr_id, key->addr.u.ipv4,
0324 key->prefix_len);
0325 err_lpm_add:
0326 err_nh_grp_get:
0327 prestera_vr_put(sw, vr);
0328 err_vr_get:
0329 kfree(fib_node);
0330 err_kzalloc:
0331 return NULL;
0332 }