From 861a612d7bad4812630c9be55e7b59d8fe2cfa18 Mon Sep 17 00:00:00 2001 From: Konstantin Belousov Date: Tue, 6 Apr 2021 06:38:59 +0300 Subject: mlx5en: register vxlan start/stop handlers Handlers maintain flow rules and inform hardware about non-standard VxLAN port in use. The database of the vxlan end points is maintained. Reviewed by: hselasky Sponsored by: Mellanox Technologies/NVidia Networking MFC after: 1 week --- sys/dev/mlx5/mlx5_en/en.h | 25 ++ sys/dev/mlx5/mlx5_en/mlx5_en_flow_table.c | 674 +++++++++++++++++++++++++++++- sys/dev/mlx5/mlx5_en/mlx5_en_main.c | 10 + 3 files changed, 701 insertions(+), 8 deletions(-) diff --git a/sys/dev/mlx5/mlx5_en/en.h b/sys/dev/mlx5/mlx5_en/en.h index 86f44dda168c..ab30c7e2ca90 100644 --- a/sys/dev/mlx5/mlx5_en/en.h +++ b/sys/dev/mlx5/mlx5_en/en.h @@ -944,6 +944,18 @@ struct mlx5e_vlan_db { bool filter_disabled; }; +struct mlx5e_vxlan_db_el { + u_int refcount; + u_int proto; + u_int port; + struct mlx5_flow_rule *vxlan_ft_rule; + TAILQ_ENTRY(mlx5e_vxlan_db_el) link; +}; + +struct mlx5e_vxlan_db { + TAILQ_HEAD(, mlx5e_vxlan_db_el) head; +}; + struct mlx5e_flow_table { int num_groups; struct mlx5_flow_table *t; @@ -953,7 +965,11 @@ struct mlx5e_flow_table { struct mlx5e_flow_tables { struct mlx5_flow_namespace *ns; struct mlx5e_flow_table vlan; + struct mlx5e_flow_table vxlan; + struct mlx5_flow_rule *vxlan_catchall_ft_rule; struct mlx5e_flow_table main; + struct mlx5e_flow_table main_vxlan; + struct mlx5_flow_rule *main_vxlan_rule[MLX5E_NUM_TT]; struct mlx5e_flow_table inner_rss; }; @@ -1005,10 +1021,12 @@ struct mlx5e_priv { u32 tisn[MLX5E_MAX_TX_NUM_TC]; u32 rqtn; u32 tirn[MLX5E_NUM_TT]; + u32 tirn_inner_vxlan[MLX5E_NUM_TT]; struct mlx5e_flow_tables fts; struct mlx5e_eth_addr_db eth_addr; struct mlx5e_vlan_db vlan; + struct mlx5e_vxlan_db vxlan; struct mlx5e_params params; struct mlx5e_params_ethtool params_ethtool; @@ -1035,6 +1053,8 @@ struct mlx5e_priv { struct ifmedia media; int media_status_last; int media_active_last; + eventhandler_tag vxlan_start; + eventhandler_tag vxlan_stop; struct callout watchdog; @@ -1124,6 +1144,11 @@ void mlx5e_disable_vlan_filter(struct mlx5e_priv *priv); int mlx5e_add_all_vlan_rules(struct mlx5e_priv *priv); void mlx5e_del_all_vlan_rules(struct mlx5e_priv *priv); +void mlx5e_vxlan_start(void *arg, struct ifnet *ifp, sa_family_t family, + u_int port); +void mlx5e_vxlan_stop(void *arg, struct ifnet *ifp, sa_family_t family, + u_int port); + static inline void mlx5e_tx_notify_hw(struct mlx5e_sq *sq, u32 *wqe) { diff --git a/sys/dev/mlx5/mlx5_en/mlx5_en_flow_table.c b/sys/dev/mlx5/mlx5_en/mlx5_en_flow_table.c index 20e81fe806a5..f4f9e494308e 100644 --- a/sys/dev/mlx5/mlx5_en/mlx5_en_flow_table.c +++ b/sys/dev/mlx5/mlx5_en/mlx5_en_flow_table.c @@ -450,6 +450,226 @@ add_eth_addr_rule_out: return (err); } +static void +mlx5e_del_main_vxlan_rules(struct mlx5e_priv *priv) +{ + struct mlx5_flow_rule **ra = priv->fts.main_vxlan_rule, **r; + + r = &ra[MLX5E_TT_IPV6_IPSEC_ESP]; + if (*r != NULL) { + mlx5_del_flow_rule(*r); + *r = NULL; + } + + r = &ra[MLX5E_TT_IPV4_IPSEC_ESP]; + if (*r != NULL) { + mlx5_del_flow_rule(*r); + *r = NULL; + } + + r = &ra[MLX5E_TT_IPV6_IPSEC_AH]; + if (*r != NULL) { + mlx5_del_flow_rule(*r); + *r = NULL; + } + + r = &ra[MLX5E_TT_IPV4_IPSEC_AH]; + if (*r != NULL) { + mlx5_del_flow_rule(*r); + *r = NULL; + } + + r = &ra[MLX5E_TT_IPV6_TCP]; + if (*r != NULL) { + mlx5_del_flow_rule(*r); + *r = NULL; + } + + r = &ra[MLX5E_TT_IPV4_TCP]; + if (*r != NULL) { + mlx5_del_flow_rule(*r); + *r = NULL; + } + + r = &ra[MLX5E_TT_IPV6_UDP]; + if (*r != NULL) { + mlx5_del_flow_rule(*r); + *r = NULL; + } + + r = &ra[MLX5E_TT_IPV4_UDP]; + if (*r != NULL) { + mlx5_del_flow_rule(*r); + *r = NULL; + } + + r = &ra[MLX5E_TT_IPV6]; + if (*r != NULL) { + mlx5_del_flow_rule(*r); + *r = NULL; + } + + r = &ra[MLX5E_TT_IPV4]; + if (*r != NULL) { + mlx5_del_flow_rule(*r); + *r = NULL; + } + + r = &ra[MLX5E_TT_ANY]; + if (*r != NULL) { + mlx5_del_flow_rule(*r); + *r = NULL; + } +} + +static int +mlx5e_add_main_vxlan_rules_sub(struct mlx5e_priv *priv, u32 *mc, u32 *mv) +{ + struct mlx5_flow_destination dest = {}; + u8 mc_enable = 0; + struct mlx5_flow_rule **rule_p; + struct mlx5_flow_table *ft = priv->fts.main_vxlan.t; + u32 *tirn = priv->tirn_inner_vxlan; + int err = 0; + + dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR; + + mc_enable = MLX5_MATCH_INNER_HEADERS; + MLX5_SET_TO_ONES(fte_match_param, mc, inner_headers.ethertype); + + rule_p = &priv->fts.main_vxlan_rule[MLX5E_TT_IPV4]; + dest.tir_num = tirn[MLX5E_TT_IPV4]; + MLX5_SET(fte_match_param, mv, inner_headers.ethertype, ETHERTYPE_IP); + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + + rule_p = &priv->fts.main_vxlan_rule[MLX5E_TT_IPV6]; + dest.tir_num = tirn[MLX5E_TT_IPV6]; + MLX5_SET(fte_match_param, mv, inner_headers.ethertype, ETHERTYPE_IPV6); + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + + MLX5_SET_TO_ONES(fte_match_param, mc, inner_headers.ip_protocol); + MLX5_SET(fte_match_param, mv, inner_headers.ip_protocol, IPPROTO_UDP); + + rule_p = &priv->fts.main_vxlan_rule[MLX5E_TT_IPV4_UDP]; + dest.tir_num = tirn[MLX5E_TT_IPV4_UDP]; + MLX5_SET(fte_match_param, mv, inner_headers.ethertype, ETHERTYPE_IP); + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + + rule_p = &priv->fts.main_vxlan_rule[MLX5E_TT_IPV6_UDP]; + dest.tir_num = tirn[MLX5E_TT_IPV6_UDP]; + MLX5_SET(fte_match_param, mv, inner_headers.ethertype, ETHERTYPE_IPV6); + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + + MLX5_SET(fte_match_param, mv, inner_headers.ip_protocol, IPPROTO_TCP); + + rule_p = &priv->fts.main_vxlan_rule[MLX5E_TT_IPV4_TCP]; + dest.tir_num = tirn[MLX5E_TT_IPV4_TCP]; + MLX5_SET(fte_match_param, mv, inner_headers.ethertype, ETHERTYPE_IP); + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + + rule_p = &priv->fts.main_vxlan_rule[MLX5E_TT_IPV6_TCP]; + dest.tir_num = tirn[MLX5E_TT_IPV6_TCP]; + MLX5_SET(fte_match_param, mv, inner_headers.ethertype, ETHERTYPE_IPV6); + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + + MLX5_SET(fte_match_param, mv, inner_headers.ip_protocol, IPPROTO_AH); + + rule_p = &priv->fts.main_vxlan_rule[MLX5E_TT_IPV4_IPSEC_AH]; + dest.tir_num = tirn[MLX5E_TT_IPV4_IPSEC_AH]; + MLX5_SET(fte_match_param, mv, inner_headers.ethertype, ETHERTYPE_IP); + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + + rule_p = &priv->fts.main_vxlan_rule[MLX5E_TT_IPV6_IPSEC_AH]; + dest.tir_num = tirn[MLX5E_TT_IPV6_IPSEC_AH]; + MLX5_SET(fte_match_param, mv, inner_headers.ethertype, ETHERTYPE_IPV6); + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + + MLX5_SET(fte_match_param, mv, inner_headers.ip_protocol, IPPROTO_ESP); + + rule_p = &priv->fts.main_vxlan_rule[MLX5E_TT_IPV4_IPSEC_ESP]; + dest.tir_num = tirn[MLX5E_TT_IPV4_IPSEC_ESP]; + MLX5_SET(fte_match_param, mv, inner_headers.ethertype, ETHERTYPE_IP); + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + + rule_p = &priv->fts.main_vxlan_rule[MLX5E_TT_IPV6_IPSEC_ESP]; + dest.tir_num = tirn[MLX5E_TT_IPV6_IPSEC_ESP]; + MLX5_SET(fte_match_param, mv, inner_headers.ethertype, + ETHERTYPE_IPV6); + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + + mc_enable = 0; + memset(mv, 0, MLX5_ST_SZ_BYTES(fte_match_param)); + memset(mc, 0, MLX5_ST_SZ_BYTES(fte_match_param)); + rule_p = &priv->fts.main_vxlan_rule[MLX5E_TT_ANY]; + dest.tir_num = tirn[MLX5E_TT_ANY]; + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + + return (0); + +err_del_ai: + err = PTR_ERR(*rule_p); + *rule_p = NULL; + mlx5e_del_main_vxlan_rules(priv); + + return (err); +} + +static int +mlx5e_add_main_vxlan_rules(struct mlx5e_priv *priv) +{ + u32 *match_criteria; + u32 *match_value; + int err = 0; + + match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + if (match_value == NULL || match_criteria == NULL) { + mlx5_en_err(priv->ifp, "alloc failed\n"); + err = -ENOMEM; + goto add_main_vxlan_rules_out; + } + err = mlx5e_add_main_vxlan_rules_sub(priv, match_criteria, match_value); + +add_main_vxlan_rules_out: + kvfree(match_criteria); + kvfree(match_value); + + return (err); +} + static int mlx5e_vport_context_update_vlans(struct mlx5e_priv *priv) { struct ifnet *ifp = priv->ifp; @@ -512,7 +732,7 @@ mlx5e_add_vlan_rule_sub(struct mlx5e_priv *priv, int err = 0; dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; - dest.ft = priv->fts.main.t; + dest.ft = priv->fts.vxlan.t; mc_enable = MLX5_MATCH_OUTER_HEADERS; @@ -1267,14 +1487,87 @@ mlx5e_create_main_groups(struct mlx5e_flow_table *ft) return (err); } -static int mlx5e_create_main_flow_table(struct mlx5e_priv *priv) +#define MLX5E_MAIN_VXLAN_GROUP0_SIZE BIT(3) +#define MLX5E_MAIN_VXLAN_GROUP1_SIZE BIT(3) +#define MLX5E_MAIN_VXLAN_GROUP2_SIZE BIT(0) +static int +mlx5e_create_main_vxlan_groups_sub(struct mlx5e_flow_table *ft, u32 *in, + int inlen) +{ + u8 *mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); + int err; + int ix = 0; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_INNER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, inner_headers.ethertype); + MLX5_SET_TO_ONES(fte_match_param, mc, inner_headers.ip_protocol); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_VXLAN_GROUP0_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destory_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_INNER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, inner_headers.ethertype); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_VXLAN_GROUP1_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destory_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_VXLAN_GROUP2_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destory_groups; + ft->num_groups++; + + return (0); + +err_destory_groups: + err = PTR_ERR(ft->g[ft->num_groups]); + ft->g[ft->num_groups] = NULL; + mlx5e_destroy_groups(ft); + + return (err); +} + +static int +mlx5e_create_main_vxlan_groups(struct mlx5e_flow_table *ft) +{ + u32 *in; + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + int err; + + in = mlx5_vzalloc(inlen); + if (!in) + return (-ENOMEM); + + err = mlx5e_create_main_vxlan_groups_sub(ft, in, inlen); + + kvfree(in); + return (err); +} + + +static int +mlx5e_create_main_flow_table(struct mlx5e_priv *priv, bool inner_vxlan) { - struct mlx5e_flow_table *ft = &priv->fts.main; + struct mlx5e_flow_table *ft = inner_vxlan ? &priv->fts.main_vxlan : + &priv->fts.main; int err; ft->num_groups = 0; - ft->t = mlx5_create_flow_table(priv->fts.ns, 0, "main", - MLX5E_MAIN_TABLE_SIZE); + ft->t = mlx5_create_flow_table(priv->fts.ns, 0, + inner_vxlan ? "vxlan_main" : "main", MLX5E_MAIN_TABLE_SIZE); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); @@ -1287,7 +1580,8 @@ static int mlx5e_create_main_flow_table(struct mlx5e_priv *priv) goto err_destroy_main_flow_table; } - err = mlx5e_create_main_groups(ft); + err = inner_vxlan ? mlx5e_create_main_vxlan_groups(ft) : + mlx5e_create_main_groups(ft); if (err) goto err_free_g; return (0); @@ -1307,6 +1601,11 @@ static void mlx5e_destroy_main_flow_table(struct mlx5e_priv *priv) mlx5e_destroy_flow_table(&priv->fts.main); } +static void mlx5e_destroy_main_vxlan_flow_table(struct mlx5e_priv *priv) +{ + mlx5e_destroy_flow_table(&priv->fts.main_vxlan); +} + #define MLX5E_NUM_VLAN_GROUPS 3 #define MLX5E_VLAN_GROUP0_SIZE BIT(12) #define MLX5E_VLAN_GROUP1_SIZE BIT(1) @@ -1428,6 +1727,333 @@ mlx5e_destroy_vlan_flow_table(struct mlx5e_priv *priv) mlx5e_destroy_flow_table(&priv->fts.vlan); } +static int +mlx5e_add_vxlan_rule_sub(struct mlx5e_priv *priv, u_int protocol, u_int port, + u32 *mc, u32 *mv, struct mlx5e_vxlan_db_el *el) +{ + struct mlx5_flow_table *ft = priv->fts.vxlan.t; + struct mlx5_flow_destination dest = {}; + u8 mc_enable; + struct mlx5_flow_rule **rule_p; + int err = 0; + + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = priv->fts.main_vxlan.t; + + mc_enable = MLX5_MATCH_OUTER_HEADERS; + rule_p = &el->vxlan_ft_rule; + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, protocol); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); + MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_UDP); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.udp_dport); + MLX5_SET(fte_match_param, mv, outer_headers.udp_dport, port); + + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + + if (IS_ERR(*rule_p)) { + err = PTR_ERR(*rule_p); + *rule_p = NULL; + mlx5_en_err(priv->ifp, "add rule failed\n"); + } + + return (err); +} + +static struct mlx5e_vxlan_db_el * +mlx5e_vxlan_find_db_el(struct mlx5e_priv *priv, u_int proto, u_int port) +{ + struct mlx5e_vxlan_db_el *el; + + TAILQ_FOREACH(el, &priv->vxlan.head, link) { + if (el->proto == proto && el->port == port) + return (el); + } + return (NULL); +} + +static struct mlx5e_vxlan_db_el * +mlx5e_vxlan_alloc_db_el(struct mlx5e_priv *priv, u_int proto, u_int port) +{ + struct mlx5e_vxlan_db_el *el; + + el = mlx5_vzalloc(sizeof(*el)); + el->refcount = 1; + el->proto = proto; + el->port = port; + el->vxlan_ft_rule = NULL; + return (el); +} + +static int +mlx5e_vxlan_family_to_proto(sa_family_t family, u_int *proto) +{ + switch (family) { + case AF_INET: + *proto = ETHERTYPE_IP; + return (0); + case AF_INET6: + *proto = ETHERTYPE_IPV6; + return (0); + default: + return (-EINVAL); + } +} + +static int +mlx5e_add_vxlan_rule(struct mlx5e_priv *priv, sa_family_t family, u_int port) +{ + struct mlx5e_vxlan_db_el *el; + u32 *match_criteria; + u32 *match_value; + u_int proto; + int err; + + err = mlx5e_vxlan_family_to_proto(family, &proto); + if (err != 0) + return (err); + + el = mlx5e_vxlan_find_db_el(priv, proto, port); + if (el != NULL) { + el->refcount++; + return (0); + } + el = mlx5e_vxlan_alloc_db_el(priv, proto, port); + + match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + if (match_value == NULL || match_criteria == NULL) { + mlx5_en_err(priv->ifp, "alloc failed\n"); + err = -ENOMEM; + goto add_vxlan_rule_out; + } + + err = mlx5e_add_vxlan_rule_sub(priv, proto, port, match_criteria, + match_value, el); + if (err == 0) { + TAILQ_INSERT_TAIL(&priv->vxlan.head, el, link); + } else { + kvfree(el); + } + +add_vxlan_rule_out: + kvfree(match_criteria); + kvfree(match_value); + + return (err); +} + +static int +mlx5e_add_vxlan_catchall_rule_sub(struct mlx5e_priv *priv, u32 *mc, u32 *mv) +{ + struct mlx5_flow_table *ft = priv->fts.vxlan.t; + struct mlx5_flow_destination dest = {}; + u8 mc_enable = 0; + struct mlx5_flow_rule **rule_p; + int err = 0; + + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = priv->fts.main.t; + + rule_p = &priv->fts.vxlan_catchall_ft_rule; + *rule_p = mlx5_add_flow_rule(ft, mc_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, MLX5_FS_ETH_FLOW_TAG, &dest); + + if (IS_ERR(*rule_p)) { + err = PTR_ERR(*rule_p); + *rule_p = NULL; + mlx5_en_err(priv->ifp, "add rule failed\n"); + } + + return (err); +} + + +static int +mlx5e_add_vxlan_catchall_rule(struct mlx5e_priv *priv) +{ + u32 *match_criteria; + u32 *match_value; + int err; + + match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + if (match_value == NULL || match_criteria == NULL) { + mlx5_en_err(priv->ifp, "alloc failed\n"); + err = -ENOMEM; + goto add_vxlan_rule_out; + } + + err = mlx5e_add_vxlan_catchall_rule_sub(priv, match_criteria, + match_value); + +add_vxlan_rule_out: + kvfree(match_criteria); + kvfree(match_value); + + return (err); +} + +static int +mlx5e_del_vxlan_rule(struct mlx5e_priv *priv, sa_family_t family, u_int port) +{ + struct mlx5e_vxlan_db_el *el; + u_int proto; + int err; + + err = mlx5e_vxlan_family_to_proto(family, &proto); + if (err != 0) + return (err); + + el = mlx5e_vxlan_find_db_el(priv, proto, port); + if (el == NULL) + return (0); + if (el->refcount > 1) { + el->refcount--; + return (0); + } + + mlx5_del_flow_rule(el->vxlan_ft_rule); + TAILQ_REMOVE(&priv->vxlan.head, el, link); + kvfree(el); + return (0); +} + +static void +mlx5e_del_vxlan_catchall_rule(struct mlx5e_priv *priv) +{ + mlx5_del_flow_rule(priv->fts.vxlan_catchall_ft_rule); +} + +void +mlx5e_vxlan_start(void *arg, struct ifnet *ifp __unused, sa_family_t family, + u_int port) +{ + struct mlx5e_priv *priv = arg; + int err; + + PRIV_LOCK(priv); + err = mlx5_vxlan_udp_port_add(priv->mdev, port); + if (err == 0 && test_bit(MLX5E_STATE_OPENED, &priv->state)) + mlx5e_add_vxlan_rule(priv, family, port); + PRIV_UNLOCK(priv); +} + +void +mlx5e_vxlan_stop(void *arg, struct ifnet *ifp __unused, sa_family_t family, + u_int port) +{ + struct mlx5e_priv *priv = arg; + + PRIV_LOCK(priv); + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) + mlx5e_del_vxlan_rule(priv, family, port); + (void)mlx5_vxlan_udp_port_delete(priv->mdev, port); + PRIV_UNLOCK(priv); +} + +#define MLX5E_VXLAN_GROUP0_SIZE BIT(3) /* XXXKIB */ +#define MLX5E_VXLAN_GROUP1_SIZE BIT(0) +#define MLX5E_NUM_VXLAN_GROUPS BIT(1) +#define MLX5E_VXLAN_TABLE_SIZE \ + (MLX5E_VXLAN_GROUP0_SIZE + MLX5E_VXLAN_GROUP1_SIZE) + +static int +mlx5e_create_vxlan_groups_sub(struct mlx5e_flow_table *ft, u32 *in, + int inlen) +{ + int err; + int ix = 0; + u8 *mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.udp_dport); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_VXLAN_GROUP0_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destory_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_VXLAN_GROUP1_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destory_groups; + ft->num_groups++; + + return (0); + +err_destory_groups: + err = PTR_ERR(ft->g[ft->num_groups]); + ft->g[ft->num_groups] = NULL; + mlx5e_destroy_groups(ft); + + return (err); +} + +static int +mlx5e_create_vxlan_groups(struct mlx5e_flow_table *ft) +{ + u32 *in; + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + int err; + + in = mlx5_vzalloc(inlen); + if (!in) + return (-ENOMEM); + + err = mlx5e_create_vxlan_groups_sub(ft, in, inlen); + + kvfree(in); + return (err); +} + +static int +mlx5e_create_vxlan_flow_table(struct mlx5e_priv *priv) +{ + struct mlx5e_flow_table *ft = &priv->fts.vxlan; + int err; + + ft->num_groups = 0; + ft->t = mlx5_create_flow_table(priv->fts.ns, 0, "vxlan", + MLX5E_VXLAN_TABLE_SIZE); + + if (IS_ERR(ft->t)) { + err = PTR_ERR(ft->t); + ft->t = NULL; + return (err); + } + ft->g = kcalloc(MLX5E_NUM_VXLAN_GROUPS, sizeof(*ft->g), GFP_KERNEL); + if (!ft->g) { + err = -ENOMEM; + goto err_destroy_vxlan_flow_table; + } + + err = mlx5e_create_vxlan_groups(ft); + if (err) + goto err_free_g; + + TAILQ_INIT(&priv->vxlan.head); + return (0); + +err_free_g: + kfree(ft->g); + +err_destroy_vxlan_flow_table: + mlx5_destroy_flow_table(ft->t); + ft->t = NULL; + + return (err); +} + #define MLX5E_NUM_INNER_RSS_GROUPS 3 #define MLX5E_INNER_RSS_GROUP0_SIZE BIT(3) #define MLX5E_INNER_RSS_GROUP1_SIZE BIT(1) @@ -1547,6 +2173,12 @@ static void mlx5e_destroy_inner_rss_flow_table(struct mlx5e_priv *priv) mlx5e_destroy_flow_table(&priv->fts.inner_rss); } +static void +mlx5e_destroy_vxlan_flow_table(struct mlx5e_priv *priv) +{ + mlx5e_destroy_flow_table(&priv->fts.vxlan); +} + int mlx5e_open_flow_table(struct mlx5e_priv *priv) { @@ -1559,18 +2191,40 @@ mlx5e_open_flow_table(struct mlx5e_priv *priv) if (err) return (err); - err = mlx5e_create_main_flow_table(priv); + err = mlx5e_create_vxlan_flow_table(priv); if (err) goto err_destroy_vlan_flow_table; - err = mlx5e_create_inner_rss_flow_table(priv); + err = mlx5e_create_main_flow_table(priv, false); + if (err) + goto err_destroy_vxlan_flow_table; + + err = mlx5e_create_main_flow_table(priv, true); if (err) goto err_destroy_main_flow_table; + err = mlx5e_create_inner_rss_flow_table(priv); + if (err) + goto err_destroy_main_vxlan_flow_table; + + err = mlx5e_add_vxlan_catchall_rule(priv); + if (err != 0) + goto err_destroy_inner_rss_flow_table; + + err = mlx5e_add_main_vxlan_rules(priv); + if (err != 0) + goto err_destroy_inner_rss_flow_table; + return (0); +err_destroy_inner_rss_flow_table: + mlx5e_destroy_inner_rss_flow_table(priv); +err_destroy_main_vxlan_flow_table: + mlx5e_destroy_main_vxlan_flow_table(priv); err_destroy_main_flow_table: mlx5e_destroy_main_flow_table(priv); +err_destroy_vxlan_flow_table: + mlx5e_destroy_vxlan_flow_table(priv); err_destroy_vlan_flow_table: mlx5e_destroy_vlan_flow_table(priv); @@ -1583,6 +2237,10 @@ mlx5e_close_flow_table(struct mlx5e_priv *priv) mlx5e_handle_ifp_addr(priv); mlx5e_destroy_inner_rss_flow_table(priv); + mlx5e_del_vxlan_catchall_rule(priv); + mlx5e_destroy_vxlan_flow_table(priv); + mlx5e_del_main_vxlan_rules(priv); mlx5e_destroy_main_flow_table(priv); + mlx5e_destroy_main_vxlan_flow_table(priv); mlx5e_destroy_vlan_flow_table(priv); } diff --git a/sys/dev/mlx5/mlx5_en/mlx5_en_main.c b/sys/dev/mlx5/mlx5_en/mlx5_en_main.c index 296252875888..1adf83cf57f9 100644 --- a/sys/dev/mlx5/mlx5_en/mlx5_en_main.c +++ b/sys/dev/mlx5/mlx5_en/mlx5_en_main.c @@ -4558,6 +4558,12 @@ mlx5e_create_ifp(struct mlx5_core_dev *mdev) priv->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig, mlx5e_vlan_rx_kill_vid, priv, EVENTHANDLER_PRI_FIRST); + /* Register for VxLAN events */ + priv->vxlan_start = EVENTHANDLER_REGISTER(vxlan_start, + mlx5e_vxlan_start, priv, EVENTHANDLER_PRI_ANY); + priv->vxlan_stop = EVENTHANDLER_REGISTER(vxlan_stop, + mlx5e_vxlan_stop, priv, EVENTHANDLER_PRI_ANY); + /* Link is down by default */ if_link_state_change(ifp, LINK_STATE_DOWN); @@ -4660,6 +4666,10 @@ mlx5e_destroy_ifp(struct mlx5_core_dev *mdev, void *vpriv) EVENTHANDLER_DEREGISTER(vlan_config, priv->vlan_attach); if (priv->vlan_detach != NULL) EVENTHANDLER_DEREGISTER(vlan_unconfig, priv->vlan_detach); + if (priv->vxlan_start != NULL) + EVENTHANDLER_DEREGISTER(vxlan_start, priv->vxlan_start); + if (priv->vxlan_stop != NULL) + EVENTHANDLER_DEREGISTER(vxlan_stop, priv->vxlan_stop); /* make sure device gets closed */ PRIV_LOCK(priv); -- cgit v1.2.3