OSDN Git Service

net/mlx5e: TC preparation refactoring for routing update event
authorVlad Buslov <vladbu@nvidia.com>
Mon, 25 Jan 2021 12:13:20 +0000 (14:13 +0200)
committerSaeed Mahameed <saeedm@nvidia.com>
Sat, 6 Feb 2021 04:53:38 +0000 (20:53 -0800)
Following patch in series implement routing update event which requires
ability to modify rule match_to_reg modify header actions dynamically
during rule lifetime. In order to accommodate such behavior, refactor and
extend TC infrastructure in following ways:

- Modify mod_hdr infrastructure to preserve its parse attribute for whole
rule lifetime, instead of deallocating it after rule creation.

- Extend match_to_reg infrastructure with new function
mlx5e_tc_match_to_reg_set_and_get_id() that returns mod_hdr action id that
can be used afterwards to update the action, and
mlx5e_tc_match_to_reg_mod_hdr_change() that can modify existing actions by
its id.

- Extend tun API with new functions mlx5e_tc_tun_update_header_ipv{4|6}()
that are used to updated existing encap entry tunnel header.

Signed-off-by: Vlad Buslov <vladbu@nvidia.com>
Signed-off-by: Dmytro Linkin <dlinkin@nvidia.com>
Reviewed-by: Roi Dayan <roid@nvidia.com>
Signed-off-by: Saeed Mahameed <saeedm@nvidia.com>
drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c
drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c
drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
drivers/net/ethernet/mellanox/mlx5/core/en_tc.h

index 3fb75dc..0b503eb 100644 (file)
@@ -1763,7 +1763,6 @@ __mlx5_tc_ct_flow_offload_clear(struct mlx5_tc_ct_priv *ct_priv,
                goto err_set_registers;
        }
 
-       dealloc_mod_hdr_actions(mod_acts);
        pre_ct_attr->modify_hdr = mod_hdr;
        pre_ct_attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
 
index 0ad22f5..f8075a6 100644 (file)
@@ -318,6 +318,105 @@ release_neigh:
        return err;
 }
 
+int mlx5e_tc_tun_update_header_ipv4(struct mlx5e_priv *priv,
+                                   struct net_device *mirred_dev,
+                                   struct mlx5e_encap_entry *e)
+{
+       int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size);
+       const struct ip_tunnel_key *tun_key = &e->tun_info->key;
+       TC_TUN_ROUTE_ATTR_INIT(attr);
+       int ipv4_encap_size;
+       char *encap_header;
+       struct iphdr *ip;
+       u8 nud_state;
+       int err;
+
+       /* add the IP fields */
+       attr.fl.fl4.flowi4_tos = tun_key->tos;
+       attr.fl.fl4.daddr = tun_key->u.ipv4.dst;
+       attr.fl.fl4.saddr = tun_key->u.ipv4.src;
+       attr.ttl = tun_key->ttl;
+
+       err = mlx5e_route_lookup_ipv4_get(priv, mirred_dev, &attr);
+       if (err)
+               return err;
+
+       ipv4_encap_size =
+               (is_vlan_dev(attr.route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) +
+               sizeof(struct iphdr) +
+               e->tunnel->calc_hlen(e);
+
+       if (max_encap_size < ipv4_encap_size) {
+               mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
+                              ipv4_encap_size, max_encap_size);
+               err = -EOPNOTSUPP;
+               goto release_neigh;
+       }
+
+       encap_header = kzalloc(ipv4_encap_size, GFP_KERNEL);
+       if (!encap_header) {
+               err = -ENOMEM;
+               goto release_neigh;
+       }
+
+       e->route_dev_ifindex = attr.route_dev->ifindex;
+
+       read_lock_bh(&attr.n->lock);
+       nud_state = attr.n->nud_state;
+       ether_addr_copy(e->h_dest, attr.n->ha);
+       WRITE_ONCE(e->nhe->neigh_dev, attr.n->dev);
+       read_unlock_bh(&attr.n->lock);
+
+       /* add ethernet header */
+       ip = (struct iphdr *)gen_eth_tnl_hdr(encap_header, attr.route_dev, e,
+                                            ETH_P_IP);
+
+       /* add ip header */
+       ip->tos = tun_key->tos;
+       ip->version = 0x4;
+       ip->ihl = 0x5;
+       ip->ttl = attr.ttl;
+       ip->daddr = attr.fl.fl4.daddr;
+       ip->saddr = attr.fl.fl4.saddr;
+
+       /* add tunneling protocol header */
+       err = mlx5e_gen_ip_tunnel_header((char *)ip + sizeof(struct iphdr),
+                                        &ip->protocol, e);
+       if (err)
+               goto free_encap;
+
+       e->encap_size = ipv4_encap_size;
+       kfree(e->encap_header);
+       e->encap_header = encap_header;
+
+       if (!(nud_state & NUD_VALID)) {
+               neigh_event_send(attr.n, NULL);
+               /* the encap entry will be made valid on neigh update event
+                * and not used before that.
+                */
+               goto release_neigh;
+       }
+       e->pkt_reformat = mlx5_packet_reformat_alloc(priv->mdev,
+                                                    e->reformat_type,
+                                                    ipv4_encap_size, encap_header,
+                                                    MLX5_FLOW_NAMESPACE_FDB);
+       if (IS_ERR(e->pkt_reformat)) {
+               err = PTR_ERR(e->pkt_reformat);
+               goto free_encap;
+       }
+
+       e->flags |= MLX5_ENCAP_ENTRY_VALID;
+       mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev));
+       mlx5e_route_lookup_ipv4_put(&attr);
+       return err;
+
+free_encap:
+       kfree(encap_header);
+release_neigh:
+       mlx5e_route_lookup_ipv4_put(&attr);
+       return err;
+}
+
 #if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
 static int mlx5e_route_lookup_ipv6_get(struct mlx5e_priv *priv,
                                       struct net_device *mirred_dev,
@@ -476,6 +575,105 @@ release_neigh:
        mlx5e_route_lookup_ipv6_put(&attr);
        return err;
 }
+
+int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv,
+                                   struct net_device *mirred_dev,
+                                   struct mlx5e_encap_entry *e)
+{
+       int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size);
+       const struct ip_tunnel_key *tun_key = &e->tun_info->key;
+       TC_TUN_ROUTE_ATTR_INIT(attr);
+       struct ipv6hdr *ip6h;
+       int ipv6_encap_size;
+       char *encap_header;
+       u8 nud_state;
+       int err;
+
+       attr.ttl = tun_key->ttl;
+
+       attr.fl.fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label);
+       attr.fl.fl6.daddr = tun_key->u.ipv6.dst;
+       attr.fl.fl6.saddr = tun_key->u.ipv6.src;
+
+       err = mlx5e_route_lookup_ipv6_get(priv, mirred_dev, &attr);
+       if (err)
+               return err;
+
+       ipv6_encap_size =
+               (is_vlan_dev(attr.route_dev) ? VLAN_ETH_HLEN : ETH_HLEN) +
+               sizeof(struct ipv6hdr) +
+               e->tunnel->calc_hlen(e);
+
+       if (max_encap_size < ipv6_encap_size) {
+               mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
+                              ipv6_encap_size, max_encap_size);
+               err = -EOPNOTSUPP;
+               goto release_neigh;
+       }
+
+       encap_header = kzalloc(ipv6_encap_size, GFP_KERNEL);
+       if (!encap_header) {
+               err = -ENOMEM;
+               goto release_neigh;
+       }
+
+       e->route_dev_ifindex = attr.route_dev->ifindex;
+
+       read_lock_bh(&attr.n->lock);
+       nud_state = attr.n->nud_state;
+       ether_addr_copy(e->h_dest, attr.n->ha);
+       WRITE_ONCE(e->nhe->neigh_dev, attr.n->dev);
+       read_unlock_bh(&attr.n->lock);
+
+       /* add ethernet header */
+       ip6h = (struct ipv6hdr *)gen_eth_tnl_hdr(encap_header, attr.route_dev, e,
+                                                ETH_P_IPV6);
+
+       /* add ip header */
+       ip6_flow_hdr(ip6h, tun_key->tos, 0);
+       /* the HW fills up ipv6 payload len */
+       ip6h->hop_limit   = attr.ttl;
+       ip6h->daddr       = attr.fl.fl6.daddr;
+       ip6h->saddr       = attr.fl.fl6.saddr;
+
+       /* add tunneling protocol header */
+       err = mlx5e_gen_ip_tunnel_header((char *)ip6h + sizeof(struct ipv6hdr),
+                                        &ip6h->nexthdr, e);
+       if (err)
+               goto free_encap;
+
+       e->encap_size = ipv6_encap_size;
+       kfree(e->encap_header);
+       e->encap_header = encap_header;
+
+       if (!(nud_state & NUD_VALID)) {
+               neigh_event_send(attr.n, NULL);
+               /* the encap entry will be made valid on neigh update event
+                * and not used before that.
+                */
+               goto release_neigh;
+       }
+
+       e->pkt_reformat = mlx5_packet_reformat_alloc(priv->mdev,
+                                                    e->reformat_type,
+                                                    ipv6_encap_size, encap_header,
+                                                    MLX5_FLOW_NAMESPACE_FDB);
+       if (IS_ERR(e->pkt_reformat)) {
+               err = PTR_ERR(e->pkt_reformat);
+               goto free_encap;
+       }
+
+       e->flags |= MLX5_ENCAP_ENTRY_VALID;
+       mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev));
+       mlx5e_route_lookup_ipv6_put(&attr);
+       return err;
+
+free_encap:
+       kfree(encap_header);
+release_neigh:
+       mlx5e_route_lookup_ipv6_put(&attr);
+       return err;
+}
 #endif
 
 int mlx5e_tc_tun_route_lookup(struct mlx5e_priv *priv,
index 9d6ee94..fa992e8 100644 (file)
@@ -59,16 +59,26 @@ int mlx5e_tc_tun_init_encap_attr(struct net_device *tunnel_dev,
 int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv,
                                    struct net_device *mirred_dev,
                                    struct mlx5e_encap_entry *e);
+int mlx5e_tc_tun_update_header_ipv4(struct mlx5e_priv *priv,
+                                   struct net_device *mirred_dev,
+                                   struct mlx5e_encap_entry *e);
 
 #if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
 int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
                                    struct net_device *mirred_dev,
                                    struct mlx5e_encap_entry *e);
+int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv,
+                                   struct net_device *mirred_dev,
+                                   struct mlx5e_encap_entry *e);
 #else
 static inline int
 mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
                                struct net_device *mirred_dev,
                                struct mlx5e_encap_entry *e) { return -EOPNOTSUPP; }
+int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv,
+                                   struct net_device *mirred_dev,
+                                   struct mlx5e_encap_entry *e)
+{ return -EOPNOTSUPP; }
 #endif
 int mlx5e_tc_tun_route_lookup(struct mlx5e_priv *priv,
                              struct mlx5_flow_spec *spec,
index 2cc31f9..e6150c7 100644 (file)
@@ -170,11 +170,11 @@ mlx5e_tc_match_to_reg_get_match(struct mlx5_flow_spec *spec,
 }
 
 int
-mlx5e_tc_match_to_reg_set(struct mlx5_core_dev *mdev,
-                         struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts,
-                         enum mlx5_flow_namespace_type ns,
-                         enum mlx5e_tc_attr_to_reg type,
-                         u32 data)
+mlx5e_tc_match_to_reg_set_and_get_id(struct mlx5_core_dev *mdev,
+                                    struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts,
+                                    enum mlx5_flow_namespace_type ns,
+                                    enum mlx5e_tc_attr_to_reg type,
+                                    u32 data)
 {
        int moffset = mlx5e_tc_attr_to_reg_mappings[type].moffset;
        int mfield = mlx5e_tc_attr_to_reg_mappings[type].mfield;
@@ -198,9 +198,10 @@ mlx5e_tc_match_to_reg_set(struct mlx5_core_dev *mdev,
        MLX5_SET(set_action_in, modact, offset, moffset * 8);
        MLX5_SET(set_action_in, modact, length, mlen * 8);
        MLX5_SET(set_action_in, modact, data, data);
+       err = mod_hdr_acts->num_actions;
        mod_hdr_acts->num_actions++;
 
-       return 0;
+       return err;
 }
 
 static struct mlx5_tc_ct_priv *
@@ -249,6 +250,41 @@ mlx5_tc_rule_delete(struct mlx5e_priv *priv,
        mlx5e_del_offloaded_nic_rule(priv, rule, attr);
 }
 
+int
+mlx5e_tc_match_to_reg_set(struct mlx5_core_dev *mdev,
+                         struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts,
+                         enum mlx5_flow_namespace_type ns,
+                         enum mlx5e_tc_attr_to_reg type,
+                         u32 data)
+{
+       int ret = mlx5e_tc_match_to_reg_set_and_get_id(mdev, mod_hdr_acts, ns, type, data);
+
+       return ret < 0 ? ret : 0;
+}
+
+void mlx5e_tc_match_to_reg_mod_hdr_change(struct mlx5_core_dev *mdev,
+                                         struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts,
+                                         enum mlx5e_tc_attr_to_reg type,
+                                         int act_id, u32 data)
+{
+       int moffset = mlx5e_tc_attr_to_reg_mappings[type].moffset;
+       int mfield = mlx5e_tc_attr_to_reg_mappings[type].mfield;
+       int mlen = mlx5e_tc_attr_to_reg_mappings[type].mlen;
+       char *modact;
+
+       modact = mod_hdr_acts->actions + (act_id * MLX5_MH_ACT_SZ);
+
+       /* Firmware has 5bit length field and 0 means 32bits */
+       if (mlen == 4)
+               mlen = 0;
+
+       MLX5_SET(set_action_in, modact, action_type, MLX5_ACTION_TYPE_SET);
+       MLX5_SET(set_action_in, modact, field, mfield);
+       MLX5_SET(set_action_in, modact, offset, moffset * 8);
+       MLX5_SET(set_action_in, modact, length, mlen * 8);
+       MLX5_SET(set_action_in, modact, data, data);
+}
+
 struct mlx5e_hairpin {
        struct mlx5_hairpin *pair;
 
@@ -1214,6 +1250,26 @@ int mlx5e_tc_query_route_vport(struct net_device *out_dev, struct net_device *ro
        return err;
 }
 
+int mlx5e_tc_add_flow_mod_hdr(struct mlx5e_priv *priv,
+                             struct mlx5e_tc_flow_parse_attr *parse_attr,
+                             struct mlx5e_tc_flow *flow)
+{
+       struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts = &parse_attr->mod_hdr_acts;
+       struct mlx5_modify_hdr *mod_hdr;
+
+       mod_hdr = mlx5_modify_header_alloc(priv->mdev,
+                                          get_flow_name_space(flow),
+                                          mod_hdr_acts->num_actions,
+                                          mod_hdr_acts->actions);
+       if (IS_ERR(mod_hdr))
+               return PTR_ERR(mod_hdr);
+
+       WARN_ON(flow->attr->modify_hdr);
+       flow->attr->modify_hdr = mod_hdr;
+
+       return 0;
+}
+
 static int
 mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
                      struct mlx5e_tc_flow *flow,
@@ -1293,7 +1349,6 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
        if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR &&
            !(attr->ct_attr.ct_action & TCA_CT_ACT_CLEAR)) {
                err = mlx5e_attach_mod_hdr(priv, flow, parse_attr);
-               dealloc_mod_hdr_actions(&parse_attr->mod_hdr_acts);
                if (err)
                        return err;
        }
@@ -1376,8 +1431,10 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv,
 
        mlx5_tc_ct_match_del(get_ct_priv(priv), &flow->attr->ct_attr);
 
-       if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
+       if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
+               dealloc_mod_hdr_actions(&attr->parse_attr->mod_hdr_acts);
                mlx5e_detach_mod_hdr(priv, flow);
+       }
 
        if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT)
                mlx5_fc_destroy(esw_attr->counter_dev, attr->counter);
index 5434bbb..9042e64 100644 (file)
@@ -214,6 +214,11 @@ int mlx5e_tc_match_to_reg_set(struct mlx5_core_dev *mdev,
                              enum mlx5e_tc_attr_to_reg type,
                              u32 data);
 
+void mlx5e_tc_match_to_reg_mod_hdr_change(struct mlx5_core_dev *mdev,
+                                         struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts,
+                                         enum mlx5e_tc_attr_to_reg type,
+                                         int act_id, u32 data);
+
 void mlx5e_tc_match_to_reg_match(struct mlx5_flow_spec *spec,
                                 enum mlx5e_tc_attr_to_reg type,
                                 u32 data,
@@ -224,6 +229,16 @@ void mlx5e_tc_match_to_reg_get_match(struct mlx5_flow_spec *spec,
                                     u32 *data,
                                     u32 *mask);
 
+int mlx5e_tc_match_to_reg_set_and_get_id(struct mlx5_core_dev *mdev,
+                                        struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts,
+                                        enum mlx5_flow_namespace_type ns,
+                                        enum mlx5e_tc_attr_to_reg type,
+                                        u32 data);
+
+int mlx5e_tc_add_flow_mod_hdr(struct mlx5e_priv *priv,
+                             struct mlx5e_tc_flow_parse_attr *parse_attr,
+                             struct mlx5e_tc_flow *flow);
+
 int alloc_mod_hdr_actions(struct mlx5_core_dev *mdev,
                          int namespace,
                          struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts);