diff options
Diffstat (limited to 'drivers/net/ethernet/marvell/prestera')
13 files changed, 867 insertions, 48 deletions
diff --git a/drivers/net/ethernet/marvell/prestera/prestera.h b/drivers/net/ethernet/marvell/prestera/prestera.h index 2fd9ef2fe5d6..6f754ae2a584 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera.h +++ b/drivers/net/ethernet/marvell/prestera/prestera.h @@ -281,8 +281,11 @@ struct prestera_router { struct prestera_switch *sw; struct list_head vr_list; struct list_head rif_entry_list; + struct rhashtable fib_ht; + struct rhashtable kern_fib_cache_ht; struct notifier_block inetaddr_nb; struct notifier_block inetaddr_valid_nb; + struct notifier_block fib_nb; }; struct prestera_rxtx_params { @@ -325,6 +328,8 @@ int prestera_port_cfg_mac_write(struct prestera_port *port, struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev); +void prestera_queue_work(struct work_struct *work); + int prestera_port_pvid_set(struct prestera_port *port, u16 vid); bool prestera_netdev_check(const struct net_device *dev); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_acl.c b/drivers/net/ethernet/marvell/prestera/prestera_acl.c index f0d9f592173b..47c899c08951 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_acl.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_acl.c @@ -22,6 +22,7 @@ struct prestera_acl { struct prestera_acl_ruleset_ht_key { struct prestera_flow_block *block; + u32 chain_index; }; struct prestera_acl_rule_entry { @@ -34,6 +35,10 @@ struct prestera_acl_rule_entry { u8 valid:1; } accept, drop, trap; struct { + struct prestera_acl_action_jump i; + u8 valid:1; + } jump; + struct { u32 id; struct prestera_counter_block *block; } counter; @@ -49,6 +54,7 @@ struct prestera_acl_ruleset { refcount_t refcount; void *keymask; u32 vtcam_id; + u32 index; u16 pcl_id; bool offload; }; @@ -83,20 +89,45 @@ static const struct rhashtable_params __prestera_acl_rule_entry_ht_params = { .automatic_shrinking = true, }; +int prestera_acl_chain_to_client(u32 chain_index, u32 *client) +{ + static const u32 client_map[] = { + PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0, + PRESTERA_HW_COUNTER_CLIENT_LOOKUP_1, + PRESTERA_HW_COUNTER_CLIENT_LOOKUP_2 + }; + + if (chain_index >= ARRAY_SIZE(client_map)) + return -EINVAL; + + *client = client_map[chain_index]; + return 0; +} + +static bool prestera_acl_chain_is_supported(u32 chain_index) +{ + return (chain_index & ~PRESTERA_ACL_CHAIN_MASK) == 0; +} + static struct prestera_acl_ruleset * prestera_acl_ruleset_create(struct prestera_acl *acl, - struct prestera_flow_block *block) + struct prestera_flow_block *block, + u32 chain_index) { struct prestera_acl_ruleset *ruleset; u32 uid = 0; int err; + if (!prestera_acl_chain_is_supported(chain_index)) + return ERR_PTR(-EINVAL); + ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL); if (!ruleset) return ERR_PTR(-ENOMEM); ruleset->acl = acl; ruleset->ht_key.block = block; + ruleset->ht_key.chain_index = chain_index; refcount_set(&ruleset->refcount, 1); err = rhashtable_init(&ruleset->rule_ht, &prestera_acl_rule_ht_params); @@ -108,7 +139,9 @@ prestera_acl_ruleset_create(struct prestera_acl *acl, goto err_ruleset_create; /* make pcl-id based on uid */ - ruleset->pcl_id = (u8)uid; + ruleset->pcl_id = PRESTERA_ACL_PCL_ID_MAKE((u8)uid, chain_index); + ruleset->index = uid; + err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node, prestera_acl_ruleset_ht_params); if (err) @@ -133,35 +166,64 @@ void prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset, int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset) { + struct prestera_acl_iface iface; u32 vtcam_id; int err; if (ruleset->offload) return -EEXIST; - err = prestera_acl_vtcam_id_get(ruleset->acl, 0, + err = prestera_acl_vtcam_id_get(ruleset->acl, + ruleset->ht_key.chain_index, ruleset->keymask, &vtcam_id); if (err) - return err; + goto err_vtcam_create; + + if (ruleset->ht_key.chain_index) { + /* for chain > 0, bind iface index to pcl-id to be able + * to jump from any other ruleset to this one using the index. + */ + iface.index = ruleset->index; + iface.type = PRESTERA_ACL_IFACE_TYPE_INDEX; + err = prestera_hw_vtcam_iface_bind(ruleset->acl->sw, &iface, + vtcam_id, ruleset->pcl_id); + if (err) + goto err_ruleset_bind; + } ruleset->vtcam_id = vtcam_id; ruleset->offload = true; return 0; + +err_ruleset_bind: + prestera_acl_vtcam_id_put(ruleset->acl, ruleset->vtcam_id); +err_vtcam_create: + return err; } static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset) { struct prestera_acl *acl = ruleset->acl; u8 uid = ruleset->pcl_id & PRESTERA_ACL_KEYMASK_PCL_ID_USER; + int err; rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node, prestera_acl_ruleset_ht_params); - if (ruleset->offload) + if (ruleset->offload) { + if (ruleset->ht_key.chain_index) { + struct prestera_acl_iface iface = { + .type = PRESTERA_ACL_IFACE_TYPE_INDEX, + .index = ruleset->index + }; + err = prestera_hw_vtcam_iface_unbind(acl->sw, &iface, + ruleset->vtcam_id); + WARN_ON(err); + } WARN_ON(prestera_acl_vtcam_id_put(acl, ruleset->vtcam_id)); + } idr_remove(&acl->uid, uid); - rhashtable_destroy(&ruleset->rule_ht); kfree(ruleset->keymask); kfree(ruleset); @@ -169,23 +231,26 @@ static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset) static struct prestera_acl_ruleset * __prestera_acl_ruleset_lookup(struct prestera_acl *acl, - struct prestera_flow_block *block) + struct prestera_flow_block *block, + u32 chain_index) { struct prestera_acl_ruleset_ht_key ht_key; memset(&ht_key, 0, sizeof(ht_key)); ht_key.block = block; + ht_key.chain_index = chain_index; return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key, prestera_acl_ruleset_ht_params); } struct prestera_acl_ruleset * prestera_acl_ruleset_lookup(struct prestera_acl *acl, - struct prestera_flow_block *block) + struct prestera_flow_block *block, + u32 chain_index) { struct prestera_acl_ruleset *ruleset; - ruleset = __prestera_acl_ruleset_lookup(acl, block); + ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index); if (!ruleset) return ERR_PTR(-ENOENT); @@ -195,17 +260,18 @@ prestera_acl_ruleset_lookup(struct prestera_acl *acl, struct prestera_acl_ruleset * prestera_acl_ruleset_get(struct prestera_acl *acl, - struct prestera_flow_block *block) + struct prestera_flow_block *block, + u32 chain_index) { struct prestera_acl_ruleset *ruleset; - ruleset = __prestera_acl_ruleset_lookup(acl, block); + ruleset = __prestera_acl_ruleset_lookup(acl, block, chain_index); if (ruleset) { refcount_inc(&ruleset->refcount); return ruleset; } - return prestera_acl_ruleset_create(acl, block); + return prestera_acl_ruleset_create(acl, block, chain_index); } void prestera_acl_ruleset_put(struct prestera_acl_ruleset *ruleset) @@ -293,6 +359,11 @@ prestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset, prestera_acl_rule_ht_params); } +u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset) +{ + return ruleset->index; +} + bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset) { return ruleset->offload; @@ -300,7 +371,7 @@ bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset) struct prestera_acl_rule * prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset, - unsigned long cookie) + unsigned long cookie, u32 chain_index) { struct prestera_acl_rule *rule; @@ -310,6 +381,7 @@ prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset, rule->ruleset = ruleset; rule->cookie = cookie; + rule->chain_index = chain_index; refcount_inc(&ruleset->refcount); @@ -324,6 +396,10 @@ void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule, void prestera_acl_rule_destroy(struct prestera_acl_rule *rule) { + if (rule->jump_ruleset) + /* release ruleset kept by jump action */ + prestera_acl_ruleset_put(rule->jump_ruleset); + prestera_acl_ruleset_put(rule->ruleset); kfree(rule); } @@ -347,7 +423,10 @@ int prestera_acl_rule_add(struct prestera_switch *sw, /* setup counter */ rule->re_arg.count.valid = true; - rule->re_arg.count.client = PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0; + err = prestera_acl_chain_to_client(ruleset->ht_key.chain_index, + &rule->re_arg.count.client); + if (err) + goto err_rule_add; rule->re = prestera_acl_rule_entry_find(sw->acl, &rule->re_key); err = WARN_ON(rule->re) ? -EEXIST : 0; @@ -360,8 +439,10 @@ int prestera_acl_rule_add(struct prestera_switch *sw, if (err) goto err_rule_add; - /* bind the block (all ports) to chain index 0 */ - if (!ruleset->rule_count) { + /* bind the block (all ports) to chain index 0, rest of + * the chains are bound to goto action + */ + if (!ruleset->ht_key.chain_index && !ruleset->rule_count) { err = prestera_acl_ruleset_block_bind(ruleset, block); if (err) goto err_acl_block_bind; @@ -395,7 +476,7 @@ void prestera_acl_rule_del(struct prestera_switch *sw, prestera_acl_rule_entry_destroy(sw->acl, rule->re); /* unbind block (all ports) */ - if (!ruleset->rule_count) + if (!ruleset->ht_key.chain_index && !ruleset->rule_count) prestera_acl_ruleset_block_unbind(ruleset, block); } @@ -459,6 +540,12 @@ static int __prestera_acl_rule_entry2hw_add(struct prestera_switch *sw, act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_TRAP; act_num++; } + /* jump */ + if (e->jump.valid) { + act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_JUMP; + act_hw[act_num].jump = e->jump.i; + act_num++; + } /* counter */ if (e->counter.block) { act_hw[act_num].id = PRESTERA_ACL_RULE_ACTION_COUNT; @@ -505,6 +592,9 @@ __prestera_acl_rule_entry_act_construct(struct prestera_switch *sw, e->drop.valid = arg->drop.valid; /* trap */ e->trap.valid = arg->trap.valid; + /* jump */ + e->jump.valid = arg->jump.valid; + e->jump.i = arg->jump.i; /* counter */ if (arg->count.valid) { int err; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_acl.h b/drivers/net/ethernet/marvell/prestera/prestera_acl.h index 40f6c1d961fa..6d2ad27682d1 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_acl.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_acl.h @@ -10,6 +10,14 @@ #define PRESTERA_ACL_KEYMASK_PCL_ID 0x3FF #define PRESTERA_ACL_KEYMASK_PCL_ID_USER \ (PRESTERA_ACL_KEYMASK_PCL_ID & 0x00FF) +#define PRESTERA_ACL_KEYMASK_PCL_ID_CHAIN \ + (PRESTERA_ACL_KEYMASK_PCL_ID & 0xFF00) +#define PRESTERA_ACL_CHAIN_MASK \ + (PRESTERA_ACL_KEYMASK_PCL_ID >> 8) + +#define PRESTERA_ACL_PCL_ID_MAKE(uid, chain_id) \ + (((uid) & PRESTERA_ACL_KEYMASK_PCL_ID_USER) | \ + (((chain_id) << 8) & PRESTERA_ACL_KEYMASK_PCL_ID_CHAIN)) #define rule_match_set_n(match_p, type, val_p, size) \ memcpy(&(match_p)[PRESTERA_ACL_RULE_MATCH_TYPE_##type], \ @@ -46,6 +54,7 @@ enum prestera_acl_rule_action { PRESTERA_ACL_RULE_ACTION_ACCEPT = 0, PRESTERA_ACL_RULE_ACTION_DROP = 1, PRESTERA_ACL_RULE_ACTION_TRAP = 2, + PRESTERA_ACL_RULE_ACTION_JUMP = 5, PRESTERA_ACL_RULE_ACTION_COUNT = 7, PRESTERA_ACL_RULE_ACTION_MAX @@ -61,6 +70,10 @@ struct prestera_acl_match { __be32 mask[__PRESTERA_ACL_RULE_MATCH_TYPE_MAX]; }; +struct prestera_acl_action_jump { + u32 index; +}; + struct prestera_acl_action_count { u32 id; }; @@ -74,6 +87,7 @@ struct prestera_acl_hw_action_info { enum prestera_acl_rule_action id; union { struct prestera_acl_action_count count; + struct prestera_acl_action_jump jump; }; }; @@ -88,6 +102,10 @@ struct prestera_acl_rule_entry_arg { u8 valid:1; } accept, drop, trap; struct { + struct prestera_acl_action_jump i; + u8 valid:1; + } jump; + struct { u8 valid:1; u32 client; } count; @@ -98,7 +116,9 @@ struct prestera_acl_rule { struct rhash_head ht_node; /* Member of acl HT */ struct list_head list; struct prestera_acl_ruleset *ruleset; + struct prestera_acl_ruleset *jump_ruleset; unsigned long cookie; + u32 chain_index; u32 priority; struct prestera_acl_rule_entry_key re_key; struct prestera_acl_rule_entry_arg re_arg; @@ -122,7 +142,7 @@ void prestera_acl_fini(struct prestera_switch *sw); struct prestera_acl_rule * prestera_acl_rule_create(struct prestera_acl_ruleset *ruleset, - unsigned long cookie); + unsigned long cookie, u32 chain_index); void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule, u32 priority); void prestera_acl_rule_destroy(struct prestera_acl_rule *rule); @@ -147,10 +167,12 @@ prestera_acl_rule_entry_create(struct prestera_acl *acl, struct prestera_acl_rule_entry_arg *arg); struct prestera_acl_ruleset * prestera_acl_ruleset_get(struct prestera_acl *acl, - struct prestera_flow_block *block); + struct prestera_flow_block *block, + u32 chain_index); struct prestera_acl_ruleset * prestera_acl_ruleset_lookup(struct prestera_acl *acl, - struct prestera_flow_block *block); + struct prestera_flow_block *block, + u32 chain_index); void prestera_acl_ruleset_keymask_set(struct prestera_acl_ruleset *ruleset, void *keymask); bool prestera_acl_ruleset_is_offload(struct prestera_acl_ruleset *ruleset); @@ -160,6 +182,7 @@ int prestera_acl_ruleset_bind(struct prestera_acl_ruleset *ruleset, struct prestera_port *port); int prestera_acl_ruleset_unbind(struct prestera_acl_ruleset *ruleset, struct prestera_port *port); +u32 prestera_acl_ruleset_index_get(const struct prestera_acl_ruleset *ruleset); void prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id); @@ -167,5 +190,6 @@ prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, void *keymask, u32 *vtcam_id); int prestera_acl_vtcam_id_put(struct prestera_acl *acl, u32 vtcam_id); +int prestera_acl_chain_to_client(u32 chain_index, u32 *client); #endif /* _PRESTERA_ACL_H_ */ diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flow.c b/drivers/net/ethernet/marvell/prestera/prestera_flow.c index d849f046ece7..05c3ad98eba9 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flow.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_flow.c @@ -29,9 +29,6 @@ static int prestera_flow_block_mall_cb(struct prestera_flow_block *block, static int prestera_flow_block_flower_cb(struct prestera_flow_block *block, struct flow_cls_offload *f) { - if (f->common.chain_index != 0) - return -EOPNOTSUPP; - switch (f->command) { case FLOW_CLS_REPLACE: return prestera_flower_replace(block, f); @@ -71,6 +68,7 @@ static void prestera_flow_block_destroy(void *cb_priv) prestera_flower_template_cleanup(block); + WARN_ON(!list_empty(&block->template_list)); WARN_ON(!list_empty(&block->binding_list)); kfree(block); @@ -86,6 +84,7 @@ prestera_flow_block_create(struct prestera_switch *sw, struct net *net) return NULL; INIT_LIST_HEAD(&block->binding_list); + INIT_LIST_HEAD(&block->template_list); block->net = net; block->sw = sw; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flow.h b/drivers/net/ethernet/marvell/prestera/prestera_flow.h index 1ea5b745bf72..6550278b166a 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flow.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_flow.h @@ -8,7 +8,6 @@ struct prestera_port; struct prestera_switch; -struct prestera_flower_template; struct prestera_flow_block_binding { struct list_head list; @@ -22,7 +21,7 @@ struct prestera_flow_block { struct net *net; struct prestera_acl_ruleset *ruleset_zero; struct flow_block_cb *block_cb; - struct prestera_flower_template *tmplt; + struct list_head template_list; unsigned int rule_count; }; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flower.c b/drivers/net/ethernet/marvell/prestera/prestera_flower.c index 19c1417fd05f..921959a980ee 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flower.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_flower.c @@ -8,26 +8,63 @@ struct prestera_flower_template { struct prestera_acl_ruleset *ruleset; + struct list_head list; + u32 chain_index; }; +static void +prestera_flower_template_free(struct prestera_flower_template *template) +{ + prestera_acl_ruleset_put(template->ruleset); + list_del(&template->list); + kfree(template); +} + void prestera_flower_template_cleanup(struct prestera_flow_block *block) { - if (block->tmplt) { - /* put the reference to the ruleset kept in create */ - prestera_acl_ruleset_put(block->tmplt->ruleset); - kfree(block->tmplt); - block->tmplt = NULL; - return; - } + struct prestera_flower_template *template, *tmp; + + /* put the reference to all rulesets kept in tmpl create */ + list_for_each_entry_safe(template, tmp, &block->template_list, list) + prestera_flower_template_free(template); +} + +static int +prestera_flower_parse_goto_action(struct prestera_flow_block *block, + struct prestera_acl_rule *rule, + u32 chain_index, + const struct flow_action_entry *act) +{ + struct prestera_acl_ruleset *ruleset; + + if (act->chain_index <= chain_index) + /* we can jump only forward */ + return -EINVAL; + + if (rule->re_arg.jump.valid) + return -EEXIST; + + ruleset = prestera_acl_ruleset_get(block->sw->acl, block, + act->chain_index); + if (IS_ERR(ruleset)) + return PTR_ERR(ruleset); + + rule->re_arg.jump.valid = 1; + rule->re_arg.jump.i.index = prestera_acl_ruleset_index_get(ruleset); + + rule->jump_ruleset = ruleset; + + return 0; } static int prestera_flower_parse_actions(struct prestera_flow_block *block, struct prestera_acl_rule *rule, struct flow_action *flow_action, + u32 chain_index, struct netlink_ext_ack *extack) { const struct flow_action_entry *act; - int i; + int err, i; /* whole struct (rule->re_arg) must be initialized with 0 */ if (!flow_action_has_entries(flow_action)) @@ -53,6 +90,13 @@ static int prestera_flower_parse_actions(struct prestera_flow_block *block, rule->re_arg.trap.valid = 1; break; + case FLOW_ACTION_GOTO: + err = prestera_flower_parse_goto_action(block, rule, + chain_index, + act); + if (err) + return err; + break; default: NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); pr_err("Unsupported action\n"); @@ -259,6 +303,7 @@ static int prestera_flower_parse(struct prestera_flow_block *block, } return prestera_flower_parse_actions(block, rule, &f->rule->action, + f->common.chain_index, f->common.extack); } @@ -270,12 +315,13 @@ int prestera_flower_replace(struct prestera_flow_block *block, struct prestera_acl_rule *rule; int err; - ruleset = prestera_acl_ruleset_get(acl, block); + ruleset = prestera_acl_ruleset_get(acl, block, f->common.chain_index); if (IS_ERR(ruleset)) return PTR_ERR(ruleset); /* increments the ruleset reference */ - rule = prestera_acl_rule_create(ruleset, f->cookie); + rule = prestera_acl_rule_create(ruleset, f->cookie, + f->common.chain_index); if (IS_ERR(rule)) { err = PTR_ERR(rule); goto err_rule_create; @@ -312,7 +358,8 @@ void prestera_flower_destroy(struct prestera_flow_block *block, struct prestera_acl_ruleset *ruleset; struct prestera_acl_rule *rule; - ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block); + ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block, + f->common.chain_index); if (IS_ERR(ruleset)) return; @@ -345,7 +392,8 @@ int prestera_flower_tmplt_create(struct prestera_flow_block *block, } prestera_acl_rule_keymask_pcl_id_set(&rule, 0); - ruleset = prestera_acl_ruleset_get(block->sw->acl, block); + ruleset = prestera_acl_ruleset_get(block->sw->acl, block, + f->common.chain_index); if (IS_ERR_OR_NULL(ruleset)) { err = -EINVAL; goto err_ruleset_get; @@ -364,7 +412,8 @@ int prestera_flower_tmplt_create(struct prestera_flow_block *block, /* keep the reference to the ruleset */ template->ruleset = ruleset; - block->tmplt = template; + template->chain_index = f->common.chain_index; + list_add_rcu(&template->list, &block->template_list); return 0; err_ruleset_get: @@ -377,7 +426,14 @@ err_malloc: void prestera_flower_tmplt_destroy(struct prestera_flow_block *block, struct flow_cls_offload *f) { - prestera_flower_template_cleanup(block); + struct prestera_flower_template *template, *tmp; + + list_for_each_entry_safe(template, tmp, &block->template_list, list) + if (template->chain_index == f->common.chain_index) { + /* put the reference to the ruleset kept in create */ + prestera_flower_template_free(template); + return; + } } int prestera_flower_stats(struct prestera_flow_block *block, @@ -390,7 +446,8 @@ int prestera_flower_stats(struct prestera_flow_block *block, u64 bytes; int err; - ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block); + ruleset = prestera_acl_ruleset_lookup(block->sw->acl, block, + f->common.chain_index); if (IS_ERR(ruleset)) return PTR_ERR(ruleset); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flower.h b/drivers/net/ethernet/marvell/prestera/prestera_flower.h index dc3aa4280e9f..495f151e6fa9 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flower.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_flower.h @@ -6,7 +6,6 @@ #include <net/pkt_cls.h> -struct prestera_switch; struct prestera_flow_block; int prestera_flower_replace(struct prestera_flow_block *block, diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.c b/drivers/net/ethernet/marvell/prestera/prestera_hw.c index e6bfadc874c5..c66cc929c820 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_hw.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.c @@ -55,6 +55,8 @@ enum prestera_cmd_type_t { PRESTERA_CMD_TYPE_ROUTER_RIF_CREATE = 0x600, PRESTERA_CMD_TYPE_ROUTER_RIF_DELETE = 0x601, + PRESTERA_CMD_TYPE_ROUTER_LPM_ADD = 0x610, + PRESTERA_CMD_TYPE_ROUTER_LPM_DELETE = 0x611, PRESTERA_CMD_TYPE_ROUTER_VR_CREATE = 0x630, PRESTERA_CMD_TYPE_ROUTER_VR_DELETE = 0x631, @@ -424,6 +426,9 @@ struct prestera_msg_acl_action { __le32 __reserved; union { struct { + __le32 index; + } jump; + struct { __le32 id; } count; __le32 reserved[6]; @@ -499,6 +504,15 @@ struct prestera_msg_iface { u8 __pad[3]; }; +struct prestera_msg_ip_addr { + union { + __be32 ipv4; + __be32 ipv6[4]; + } u; + u8 v; /* e.g. PRESTERA_IPV4 */ + u8 __pad[3]; +}; + struct prestera_msg_rif_req { struct prestera_msg_cmd cmd; struct prestera_msg_iface iif; @@ -515,6 +529,15 @@ struct prestera_msg_rif_resp { u8 __pad[2]; }; +struct prestera_msg_lpm_req { + struct prestera_msg_cmd cmd; + struct prestera_msg_ip_addr dst; + __le32 grp_id; + __le32 dst_len; + __le16 vr_id; + u8 __pad[2]; +}; + struct prestera_msg_vr_req { struct prestera_msg_cmd cmd; __le16 vr_id; @@ -598,9 +621,11 @@ static void prestera_hw_build_tests(void) BUILD_BUG_ON(sizeof(struct prestera_msg_counter_stats) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_rif_req) != 36); BUILD_BUG_ON(sizeof(struct prestera_msg_vr_req) != 8); + BUILD_BUG_ON(sizeof(struct prestera_msg_lpm_req) != 36); /* structure that are part of req/resp fw messages */ BUILD_BUG_ON(sizeof(struct prestera_msg_iface) != 16); + BUILD_BUG_ON(sizeof(struct prestera_msg_ip_addr) != 20); /* check responses */ BUILD_BUG_ON(sizeof(struct prestera_msg_common_resp) != 8); @@ -1164,6 +1189,9 @@ prestera_acl_rule_add_put_action(struct prestera_msg_acl_action *action, case PRESTERA_ACL_RULE_ACTION_TRAP: /* just rule action id, no specific data */ break; + case PRESTERA_ACL_RULE_ACTION_JUMP: + action->jump.index = __cpu_to_le32(info->jump.index); + break; case PRESTERA_ACL_RULE_ACTION_COUNT: action->count.id = __cpu_to_le32(info->count.id); break; @@ -1891,6 +1919,33 @@ int prestera_hw_vr_delete(struct prestera_switch *sw, u16 vr_id) sizeof(req)); } +int prestera_hw_lpm_add(struct prestera_switch *sw, u16 vr_id, + __be32 dst, u32 dst_len, u32 grp_id) +{ + struct prestera_msg_lpm_req req = { + .dst_len = __cpu_to_le32(dst_len), + .vr_id = __cpu_to_le16(vr_id), + .grp_id = __cpu_to_le32(grp_id), + .dst.u.ipv4 = dst + }; + + return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_LPM_ADD, &req.cmd, + sizeof(req)); +} + +int prestera_hw_lpm_del(struct prestera_switch *sw, u16 vr_id, + __be32 dst, u32 dst_len) +{ + struct prestera_msg_lpm_req req = { + .dst_len = __cpu_to_le32(dst_len), + .vr_id = __cpu_to_le16(vr_id), + .dst.u.ipv4 = dst + }; + + return prestera_cmd(sw, PRESTERA_CMD_TYPE_ROUTER_LPM_DELETE, &req.cmd, + sizeof(req)); +} + int prestera_hw_rxtx_init(struct prestera_switch *sw, struct prestera_rxtx_params *params) { diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.h b/drivers/net/ethernet/marvell/prestera/prestera_hw.h index 3ff12bae5909..fd896a8838bb 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_hw.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.h @@ -249,6 +249,12 @@ int prestera_hw_rif_delete(struct prestera_switch *sw, u16 rif_id, int prestera_hw_vr_create(struct prestera_switch *sw, u16 *vr_id); int prestera_hw_vr_delete(struct prestera_switch *sw, u16 vr_id); +/* LPM PI */ +int prestera_hw_lpm_add(struct prestera_switch *sw, u16 vr_id, + __be32 dst, u32 dst_len, u32 grp_id); +int prestera_hw_lpm_del(struct prestera_switch *sw, u16 vr_id, + __be32 dst, u32 dst_len); + /* Event handlers */ int prestera_hw_event_handler_register(struct prestera_switch *sw, enum prestera_event_type type, diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c index 73cd0a4b7291..1402c7889e78 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_main.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c @@ -28,6 +28,12 @@ #define PRESTERA_MAC_ADDR_NUM_MAX 255 static struct workqueue_struct *prestera_wq; +static struct workqueue_struct *prestera_owq; + +void prestera_queue_work(struct work_struct *work) +{ + queue_work(prestera_owq, work); +} int prestera_port_pvid_set(struct prestera_port *port, u16 vid) { @@ -1025,12 +1031,17 @@ static int __init prestera_module_init(void) if (!prestera_wq) return -ENOMEM; + prestera_owq = alloc_ordered_workqueue("prestera_ordered", 0); + if (!prestera_owq) + return -ENOMEM; + return 0; } static void __exit prestera_module_exit(void) { destroy_workqueue(prestera_wq); + destroy_workqueue(prestera_owq); } module_init(prestera_module_init); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c index 6ef4d32b8fdd..6c5618cf4f08 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_router.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_router.c @@ -5,10 +5,39 @@ #include <linux/types.h> #include <linux/inetdevice.h> #include <net/switchdev.h> +#include <linux/rhashtable.h> #include "prestera.h" #include "prestera_router_hw.h" +struct prestera_kern_fib_cache_key { + struct prestera_ip_addr addr; + u32 prefix_len; + u32 kern_tb_id; /* tb_id from kernel (not fixed) */ +}; + +/* Subscribing on neighbours in kernel */ +struct prestera_kern_fib_cache { + struct prestera_kern_fib_cache_key key; + struct { + struct prestera_fib_key fib_key; + enum prestera_fib_type fib_type; + } lpm_info; /* hold prepared lpm info */ + /* Indicate if route is not overlapped by another table */ + struct rhash_head ht_node; /* node of prestera_router */ + struct fib_info *fi; + u8 kern_tos; + u8 kern_type; + bool reachable; +}; + +static const struct rhashtable_params __prestera_kern_fib_cache_ht_params = { + .key_offset = offsetof(struct prestera_kern_fib_cache, key), + .head_offset = offsetof(struct prestera_kern_fib_cache, ht_node), + .key_len = sizeof(struct prestera_kern_fib_cache_key), + .automatic_shrinking = true, +}; + /* This util to be used, to convert kernel rules for default vr in hw_vr */ static u32 prestera_fix_tb_id(u32 tb_id) { @@ -20,6 +49,290 @@ static u32 prestera_fix_tb_id(u32 tb_id) return tb_id; } +static void +prestera_util_fen_info2fib_cache_key(struct fib_entry_notifier_info *fen_info, + struct prestera_kern_fib_cache_key *key) +{ + memset(key, 0, sizeof(*key)); + key->addr.u.ipv4 = cpu_to_be32(fen_info->dst); + key->prefix_len = fen_info->dst_len; + key->kern_tb_id = fen_info->tb_id; +} + +static struct prestera_kern_fib_cache * +prestera_kern_fib_cache_find(struct prestera_switch *sw, + struct prestera_kern_fib_cache_key *key) +{ + struct prestera_kern_fib_cache *fib_cache; + + fib_cache = + rhashtable_lookup_fast(&sw->router->kern_fib_cache_ht, key, + __prestera_kern_fib_cache_ht_params); + return fib_cache; +} + +static void +prestera_kern_fib_cache_destroy(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fib_cache) +{ + fib_info_put(fib_cache->fi); + rhashtable_remove_fast(&sw->router->kern_fib_cache_ht, + &fib_cache->ht_node, + __prestera_kern_fib_cache_ht_params); + kfree(fib_cache); +} + +/* Operations on fi (offload, etc) must be wrapped in utils. + * This function just create storage. + */ +static struct prestera_kern_fib_cache * +prestera_kern_fib_cache_create(struct prestera_switch *sw, + struct prestera_kern_fib_cache_key *key, + struct fib_info *fi, u8 tos, u8 type) +{ + struct prestera_kern_fib_cache *fib_cache; + int err; + + fib_cache = kzalloc(sizeof(*fib_cache), GFP_KERNEL); + if (!fib_cache) + goto err_kzalloc; + + memcpy(&fib_cache->key, key, sizeof(*key)); + fib_info_hold(fi); + fib_cache->fi = fi; + fib_cache->kern_tos = tos; + fib_cache->kern_type = type; + + err = rhashtable_insert_fast(&sw->router->kern_fib_cache_ht, + &fib_cache->ht_node, + __prestera_kern_fib_cache_ht_params); + if (err) + goto err_ht_insert; + + return fib_cache; + +err_ht_insert: + fib_info_put(fi); + kfree(fib_cache); +err_kzalloc: + return NULL; +} + +static void +__prestera_k_arb_fib_lpm_offload_set(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc, + bool fail, bool offload, bool trap) +{ + struct fib_rt_info fri; + + if (fc->key.addr.v != PRESTERA_IPV4) + return; + + fri.fi = fc->fi; + fri.tb_id = fc->key.kern_tb_id; + fri.dst = fc->key.addr.u.ipv4; + fri.dst_len = fc->key.prefix_len; + fri.tos = fc->kern_tos; + fri.type = fc->kern_type; + /* flags begin */ + fri.offload = offload; + fri.trap = trap; + fri.offload_failed = fail; + /* flags end */ + fib_alias_hw_flags_set(&init_net, &fri); +} + +static int +__prestera_pr_k_arb_fc_lpm_info_calc(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc) +{ + memset(&fc->lpm_info, 0, sizeof(fc->lpm_info)); + + switch (fc->fi->fib_type) { + case RTN_UNICAST: + fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_TRAP; + break; + /* Unsupported. Leave it for kernel: */ + case RTN_BROADCAST: + case RTN_MULTICAST: + /* Routes we must trap by design: */ + case RTN_LOCAL: + case RTN_UNREACHABLE: + case RTN_PROHIBIT: + fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_TRAP; + break; + case RTN_BLACKHOLE: + fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_DROP; + break; + default: + dev_err(sw->dev->dev, "Unsupported fib_type"); + return -EOPNOTSUPP; + } + + fc->lpm_info.fib_key.addr = fc->key.addr; + fc->lpm_info.fib_key.prefix_len = fc->key.prefix_len; + fc->lpm_info.fib_key.tb_id = prestera_fix_tb_id(fc->key.kern_tb_id); + + return 0; +} + +static int __prestera_k_arb_f_lpm_set(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc, + bool enabled) +{ + struct prestera_fib_node *fib_node; + + fib_node = prestera_fib_node_find(sw, &fc->lpm_info.fib_key); + if (fib_node) + prestera_fib_node_destroy(sw, fib_node); + + if (!enabled) + return 0; + + fib_node = prestera_fib_node_create(sw, &fc->lpm_info.fib_key, + fc->lpm_info.fib_type); + + if (!fib_node) { + dev_err(sw->dev->dev, "fib_node=NULL %pI4n/%d kern_tb_id = %d", + &fc->key.addr.u.ipv4, fc->key.prefix_len, + fc->key.kern_tb_id); + return -ENOENT; + } + + return 0; +} + +static int __prestera_k_arb_fc_apply(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc) +{ + int err; + + err = __prestera_pr_k_arb_fc_lpm_info_calc(sw, fc); + if (err) + return err; + + err = __prestera_k_arb_f_lpm_set(sw, fc, fc->reachable); + if (err) { + __prestera_k_arb_fib_lpm_offload_set(sw, fc, + true, false, false); + return err; + } + + switch (fc->lpm_info.fib_type) { + case PRESTERA_FIB_TYPE_TRAP: + __prestera_k_arb_fib_lpm_offload_set(sw, fc, false, + false, fc->reachable); + break; + case PRESTERA_FIB_TYPE_DROP: + __prestera_k_arb_fib_lpm_offload_set(sw, fc, false, true, + fc->reachable); + break; + case PRESTERA_FIB_TYPE_INVALID: + break; + } + + return 0; +} + +static struct prestera_kern_fib_cache * +__prestera_k_arb_util_fib_overlaps(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc) +{ + struct prestera_kern_fib_cache_key fc_key; + struct prestera_kern_fib_cache *rfc; + + /* TODO: parse kernel rules */ + rfc = NULL; + if (fc->key.kern_tb_id == RT_TABLE_LOCAL) { + memcpy(&fc_key, &fc->key, sizeof(fc_key)); + fc_key.kern_tb_id = RT_TABLE_MAIN; + rfc = prestera_kern_fib_cache_find(sw, &fc_key); + } + + return rfc; +} + +static struct prestera_kern_fib_cache * +__prestera_k_arb_util_fib_overlapped(struct prestera_switch *sw, + struct prestera_kern_fib_cache *fc) +{ + struct prestera_kern_fib_cache_key fc_key; + struct prestera_kern_fib_cache *rfc; + + /* TODO: parse kernel rules */ + rfc = NULL; + if (fc->key.kern_tb_id == RT_TABLE_MAIN) { + memcpy(&fc_key, &fc->key, sizeof(fc_key)); + fc_key.kern_tb_id = RT_TABLE_LOCAL; + rfc = prestera_kern_fib_cache_find(sw, &fc_key); + } + + return rfc; +} + +static int +prestera_k_arb_fib_evt(struct prestera_switch *sw, + bool replace, /* replace or del */ + struct fib_entry_notifier_info *fen_info) +{ + struct prestera_kern_fib_cache *tfib_cache, *bfib_cache; /* top/btm */ + struct prestera_kern_fib_cache_key fc_key; + struct prestera_kern_fib_cache *fib_cache; + int err; + + prestera_util_fen_info2fib_cache_key(fen_info, &fc_key); + fib_cache = prestera_kern_fib_cache_find(sw, &fc_key); + if (fib_cache) { + fib_cache->reachable = false; + err = __prestera_k_arb_fc_apply(sw, fib_cache); + if (err) + dev_err(sw->dev->dev, + "Applying destroyed fib_cache failed"); + + bfib_cache = __prestera_k_arb_util_fib_overlaps(sw, fib_cache); + tfib_cache = __prestera_k_arb_util_fib_overlapped(sw, fib_cache); + if (!tfib_cache && bfib_cache) { + bfib_cache->reachable = true; + err = __prestera_k_arb_fc_apply(sw, bfib_cache); + if (err) + dev_err(sw->dev->dev, + "Applying fib_cache btm failed"); + } + + prestera_kern_fib_cache_destroy(sw, fib_cache); + } + + if (replace) { + fib_cache = prestera_kern_fib_cache_create(sw, &fc_key, + fen_info->fi, + fen_info->tos, + fen_info->type); + if (!fib_cache) { + dev_err(sw->dev->dev, "fib_cache == NULL"); + return -ENOENT; + } + + bfib_cache = __prestera_k_arb_util_fib_overlaps(sw, fib_cache); + tfib_cache = __prestera_k_arb_util_fib_overlapped(sw, fib_cache); + if (!tfib_cache) + fib_cache->reachable = true; + + if (bfib_cache) { + bfib_cache->reachable = false; + err = __prestera_k_arb_fc_apply(sw, bfib_cache); + if (err) + dev_err(sw->dev->dev, + "Applying fib_cache btm failed"); + } + + err = __prestera_k_arb_fc_apply(sw, fib_cache); + if (err) + dev_err(sw->dev->dev, "Applying fib_cache failed"); + } + + return 0; +} + static int __prestera_inetaddr_port_event(struct net_device *port_dev, unsigned long event, struct netlink_ext_ack *extack) @@ -137,6 +450,89 @@ out: return notifier_from_errno(err); } +struct prestera_fib_event_work { + struct work_struct work; + struct prestera_switch *sw; + struct fib_entry_notifier_info fen_info; + unsigned long event; +}; + +static void __prestera_router_fib_event_work(struct work_struct *work) +{ + struct prestera_fib_event_work *fib_work = + container_of(work, struct prestera_fib_event_work, work); + struct prestera_switch *sw = fib_work->sw; + int err; + + rtnl_lock(); + + switch (fib_work->event) { + case FIB_EVENT_ENTRY_REPLACE: + err = prestera_k_arb_fib_evt(sw, true, &fib_work->fen_info); + if (err) + goto err_out; + + break; + case FIB_EVENT_ENTRY_DEL: + err = prestera_k_arb_fib_evt(sw, false, &fib_work->fen_info); + if (err) + goto err_out; + + break; + } + + goto out; + +err_out: + dev_err(sw->dev->dev, "Error when processing %pI4h/%d", + &fib_work->fen_info.dst, + fib_work->fen_info.dst_len); +out: + fib_info_put(fib_work->fen_info.fi); + rtnl_unlock(); + kfree(fib_work); +} + +/* Called with rcu_read_lock() */ +static int __prestera_router_fib_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct prestera_fib_event_work *fib_work; + struct fib_entry_notifier_info *fen_info; + struct fib_notifier_info *info = ptr; + struct prestera_router *router; + + if (info->family != AF_INET) + return NOTIFY_DONE; + + router = container_of(nb, struct prestera_router, fib_nb); + + switch (event) { + case FIB_EVENT_ENTRY_REPLACE: + case FIB_EVENT_ENTRY_DEL: + fen_info = container_of(info, struct fib_entry_notifier_info, + info); + if (!fen_info->fi) + return NOTIFY_DONE; + + fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); + if (WARN_ON(!fib_work)) + return NOTIFY_BAD; + + fib_info_hold(fen_info->fi); + fib_work->fen_info = *fen_info; + fib_work->event = event; + fib_work->sw = router->sw; + INIT_WORK(&fib_work->work, __prestera_router_fib_event_work); + prestera_queue_work(&fib_work->work); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_DONE; +} + int prestera_router_init(struct prestera_switch *sw) { struct prestera_router *router; @@ -153,6 +549,11 @@ int prestera_router_init(struct prestera_switch *sw) if (err) goto err_router_lib_init; + err = rhashtable_init(&router->kern_fib_cache_ht, + &__prestera_kern_fib_cache_ht_params); + if (err) + goto err_kern_fib_cache_ht_init; + router->inetaddr_valid_nb.notifier_call = __prestera_inetaddr_valid_cb; err = register_inetaddr_validator_notifier(&router->inetaddr_valid_nb); if (err) @@ -163,11 +564,21 @@ int prestera_router_init(struct prestera_switch *sw) if (err) goto err_register_inetaddr_notifier; + router->fib_nb.notifier_call = __prestera_router_fib_event; + err = register_fib_notifier(&init_net, &router->fib_nb, + /* TODO: flush fib entries */ NULL, NULL); + if (err) + goto err_register_fib_notifier; + return 0; +err_register_fib_notifier: + unregister_inetaddr_notifier(&router->inetaddr_nb); err_register_inetaddr_notifier: unregister_inetaddr_validator_notifier(&router->inetaddr_valid_nb); err_register_inetaddr_validator_notifier: + rhashtable_destroy(&router->kern_fib_cache_ht); +err_kern_fib_cache_ht_init: prestera_router_hw_fini(sw); err_router_lib_init: kfree(sw->router); @@ -178,6 +589,7 @@ void prestera_router_fini(struct prestera_switch *sw) { unregister_inetaddr_notifier(&sw->router->inetaddr_nb); unregister_inetaddr_validator_notifier(&sw->router->inetaddr_valid_nb); + rhashtable_destroy(&sw->router->kern_fib_cache_ht); prestera_router_hw_fini(sw); kfree(sw->router); sw->router = NULL; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router_hw.c b/drivers/net/ethernet/marvell/prestera/prestera_router_hw.c index e5592b69ad37..5b0cf3be9a9e 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_router_hw.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_router_hw.c @@ -9,23 +9,41 @@ #include "prestera_acl.h" /* +--+ - * +------->|vr| - * | +--+ - * | - * +-+-------+ - * |rif_entry| - * +---------+ - * Rif is + * +------->|vr|<-+ + * | +--+ | + * | | + * +-+-------+ +--+---+-+ + * |rif_entry| |fib_node| + * +---------+ +--------+ + * Rif is Fib - is exit point * used as * entry point * for vr in hw */ +#define PRESTERA_NHGR_UNUSED (0) +#define PRESTERA_NHGR_DROP (0xFFFFFFFF) + +static const struct rhashtable_params __prestera_fib_ht_params = { + .key_offset = offsetof(struct prestera_fib_node, key), + .head_offset = offsetof(struct prestera_fib_node, ht_node), + .key_len = sizeof(struct prestera_fib_key), + .automatic_shrinking = true, +}; + int prestera_router_hw_init(struct prestera_switch *sw) { + int err; + + err = rhashtable_init(&sw->router->fib_ht, + &__prestera_fib_ht_params); + if (err) + goto err_fib_ht_init; + INIT_LIST_HEAD(&sw->router->vr_list); INIT_LIST_HEAD(&sw->router->rif_entry_list); +err_fib_ht_init: return 0; } @@ -33,6 +51,7 @@ void prestera_router_hw_fini(struct prestera_switch *sw) { WARN_ON(!list_empty(&sw->router->vr_list)); WARN_ON(!list_empty(&sw->router->rif_entry_list)); + rhashtable_destroy(&sw->router->fib_ht); } static struct prestera_vr *__prestera_vr_find(struct prestera_switch *sw, @@ -212,3 +231,102 @@ err_key_copy: err_kzalloc: return NULL; } + +struct prestera_fib_node * +prestera_fib_node_find(struct prestera_switch *sw, struct prestera_fib_key *key) +{ + struct prestera_fib_node *fib_node; + + fib_node = rhashtable_lookup_fast(&sw->router->fib_ht, key, + __prestera_fib_ht_params); + return fib_node; +} + +static void __prestera_fib_node_destruct(struct prestera_switch *sw, + struct prestera_fib_node *fib_node) +{ + struct prestera_vr *vr; + + vr = fib_node->info.vr; + prestera_hw_lpm_del(sw, vr->hw_vr_id, fib_node->key.addr.u.ipv4, + fib_node->key.prefix_len); + switch (fib_node->info.type) { + case PRESTERA_FIB_TYPE_TRAP: + break; + case PRESTERA_FIB_TYPE_DROP: + break; + default: + pr_err("Unknown fib_node->info.type = %d", + fib_node->info.type); + } + + prestera_vr_put(sw, vr); +} + +void prestera_fib_node_destroy(struct prestera_switch *sw, + struct prestera_fib_node *fib_node) +{ + __prestera_fib_node_destruct(sw, fib_node); + rhashtable_remove_fast(&sw->router->fib_ht, &fib_node->ht_node, + __prestera_fib_ht_params); + kfree(fib_node); +} + +struct prestera_fib_node * +prestera_fib_node_create(struct prestera_switch *sw, + struct prestera_fib_key *key, + enum prestera_fib_type fib_type) +{ + struct prestera_fib_node *fib_node; + u32 grp_id; + struct prestera_vr *vr; + int err; + + fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL); + if (!fib_node) + goto err_kzalloc; + + memcpy(&fib_node->key, key, sizeof(*key)); + fib_node->info.type = fib_type; + + vr = prestera_vr_get(sw, key->tb_id, NULL); + if (IS_ERR(vr)) + goto err_vr_get; + + fib_node->info.vr = vr; + + switch (fib_type) { + case PRESTERA_FIB_TYPE_TRAP: + grp_id = PRESTERA_NHGR_UNUSED; + break; + case PRESTERA_FIB_TYPE_DROP: + grp_id = PRESTERA_NHGR_DROP; + break; + default: + pr_err("Unsupported fib_type %d", fib_type); + goto err_nh_grp_get; + } + + err = prestera_hw_lpm_add(sw, vr->hw_vr_id, key->addr.u.ipv4, + key->prefix_len, grp_id); + if (err) + goto err_lpm_add; + + err = rhashtable_insert_fast(&sw->router->fib_ht, &fib_node->ht_node, + __prestera_fib_ht_params); + if (err) + goto err_ht_insert; + + return fib_node; + +err_ht_insert: + prestera_hw_lpm_del(sw, vr->hw_vr_id, key->addr.u.ipv4, + key->prefix_len); +err_lpm_add: +err_nh_grp_get: + prestera_vr_put(sw, vr); +err_vr_get: + kfree(fib_node); +err_kzalloc: + return NULL; +} diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router_hw.h b/drivers/net/ethernet/marvell/prestera/prestera_router_hw.h index b6b028551868..67dbb49c8bd4 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_router_hw.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_router_hw.h @@ -22,6 +22,42 @@ struct prestera_rif_entry { struct list_head router_node; /* ht */ }; +struct prestera_ip_addr { + union { + __be32 ipv4; + struct in6_addr ipv6; + } u; + enum { + PRESTERA_IPV4 = 0, + PRESTERA_IPV6 + } v; +}; + +struct prestera_fib_key { + struct prestera_ip_addr addr; + u32 prefix_len; + u32 tb_id; +}; + +struct prestera_fib_info { + struct prestera_vr *vr; + struct list_head vr_node; + enum prestera_fib_type { + PRESTERA_FIB_TYPE_INVALID = 0, + /* It can be connected route + * and will be overlapped with neighbours + */ + PRESTERA_FIB_TYPE_TRAP, + PRESTERA_FIB_TYPE_DROP + } type; +}; + +struct prestera_fib_node { + struct rhash_head ht_node; /* node of prestera_vr */ + struct prestera_fib_key key; + struct prestera_fib_info info; /* action related info */ +}; + struct prestera_rif_entry * prestera_rif_entry_find(const struct prestera_switch *sw, const struct prestera_rif_entry_key *k); @@ -31,6 +67,14 @@ struct prestera_rif_entry * prestera_rif_entry_create(struct prestera_switch *sw, struct prestera_rif_entry_key *k, u32 tb_id, const unsigned char *addr); +struct prestera_fib_node *prestera_fib_node_find(struct prestera_switch *sw, + struct prestera_fib_key *key); +void prestera_fib_node_destroy(struct prestera_switch *sw, + struct prestera_fib_node *fib_node); +struct prestera_fib_node * +prestera_fib_node_create(struct prestera_switch *sw, + struct prestera_fib_key *key, + enum prestera_fib_type fib_type); int prestera_router_hw_init(struct prestera_switch *sw); void prestera_router_hw_fini(struct prestera_switch *sw); |