diff options
| author | Or Gerlitz <ogerlitz@mellanox.com> | 2017-10-17 06:33:43 -0400 |
|---|---|---|
| committer | Saeed Mahameed <saeedm@mellanox.com> | 2017-10-26 03:47:27 -0400 |
| commit | 3c37745ec614ff048d5dce38f976804b05d307ee (patch) | |
| tree | 60df9abe2901def1827f7fc166b88f4ccb98b7ae | |
| parent | 4ca637a20a524cd8ddbca696f12bfa92111c96e3 (diff) | |
net/mlx5e: Properly deal with encap flows add/del under neigh update
Currently, the encap action offload is handled in the actions parse
function and not in mlx5e_tc_add_fdb_flow() where we deal with all
the other aspects of offloading actions (vlan, modify header) and
the rule itself.
When the neigh update code (mlx5e_tc_encap_flows_add()) recreates the
encap entry and offloads the related flows, we wrongly call again into
mlx5e_tc_add_fdb_flow(), this for itself would cause us to handle
again the offloading of vlans and header re-write which puts things
in non consistent state and step on freed memory (e.g the modify
header parse buffer which is already freed).
Since on error, mlx5e_tc_add_fdb_flow() detaches and may release the
encap entry, it causes a corruption at the neigh update code which goes
over the list of flows associated with this encap entry, or double free
when the tc flow is later deleted by user-space.
When neigh update (mlx5e_tc_encap_flows_del()) unoffloads the flows related
to an encap entry which is now invalid, we do a partial repeat of the eswitch
flow removal code which is wrong too.
To fix things up we do the following:
(1) handle the encap action offload in the eswitch flow add function
mlx5e_tc_add_fdb_flow() as done for the other actions and the rule itself.
(2) modify the neigh update code (mlx5e_tc_encap_flows_add/del) to only
deal with the encap entry and rules delete/add and not with any of
the other offloaded actions.
Fixes: 232c001398ae ('net/mlx5e: Add support to neighbour update flow')
Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com>
Reviewed-by: Paul Blakey <paulb@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 89 |
1 files changed, 54 insertions, 35 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 1aa2028ed995..9ba1f72060aa 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | |||
| @@ -78,9 +78,11 @@ struct mlx5e_tc_flow { | |||
| 78 | }; | 78 | }; |
| 79 | 79 | ||
| 80 | struct mlx5e_tc_flow_parse_attr { | 80 | struct mlx5e_tc_flow_parse_attr { |
| 81 | struct ip_tunnel_info tun_info; | ||
| 81 | struct mlx5_flow_spec spec; | 82 | struct mlx5_flow_spec spec; |
| 82 | int num_mod_hdr_actions; | 83 | int num_mod_hdr_actions; |
| 83 | void *mod_hdr_actions; | 84 | void *mod_hdr_actions; |
| 85 | int mirred_ifindex; | ||
| 84 | }; | 86 | }; |
| 85 | 87 | ||
| 86 | enum { | 88 | enum { |
| @@ -322,6 +324,12 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv, | |||
| 322 | static void mlx5e_detach_encap(struct mlx5e_priv *priv, | 324 | static void mlx5e_detach_encap(struct mlx5e_priv *priv, |
| 323 | struct mlx5e_tc_flow *flow); | 325 | struct mlx5e_tc_flow *flow); |
| 324 | 326 | ||
| 327 | static int mlx5e_attach_encap(struct mlx5e_priv *priv, | ||
| 328 | struct ip_tunnel_info *tun_info, | ||
| 329 | struct net_device *mirred_dev, | ||
| 330 | struct net_device **encap_dev, | ||
| 331 | struct mlx5e_tc_flow *flow); | ||
| 332 | |||
| 325 | static struct mlx5_flow_handle * | 333 | static struct mlx5_flow_handle * |
| 326 | mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, | 334 | mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, |
| 327 | struct mlx5e_tc_flow_parse_attr *parse_attr, | 335 | struct mlx5e_tc_flow_parse_attr *parse_attr, |
| @@ -329,9 +337,27 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, | |||
| 329 | { | 337 | { |
| 330 | struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; | 338 | struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; |
| 331 | struct mlx5_esw_flow_attr *attr = flow->esw_attr; | 339 | struct mlx5_esw_flow_attr *attr = flow->esw_attr; |
| 332 | struct mlx5_flow_handle *rule; | 340 | struct net_device *out_dev, *encap_dev = NULL; |
| 341 | struct mlx5_flow_handle *rule = NULL; | ||
| 342 | struct mlx5e_rep_priv *rpriv; | ||
| 343 | struct mlx5e_priv *out_priv; | ||
| 333 | int err; | 344 | int err; |
| 334 | 345 | ||
| 346 | if (attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) { | ||
| 347 | out_dev = __dev_get_by_index(dev_net(priv->netdev), | ||
| 348 | attr->parse_attr->mirred_ifindex); | ||
| 349 | err = mlx5e_attach_encap(priv, &parse_attr->tun_info, | ||
| 350 | out_dev, &encap_dev, flow); | ||
| 351 | if (err) { | ||
| 352 | rule = ERR_PTR(err); | ||
| 353 | if (err != -EAGAIN) | ||
| 354 | goto err_attach_encap; | ||
| 355 | } | ||
| 356 | out_priv = netdev_priv(encap_dev); | ||
| 357 | rpriv = out_priv->ppriv; | ||
| 358 | attr->out_rep = rpriv->rep; | ||
| 359 | } | ||
| 360 | |||
| 335 | err = mlx5_eswitch_add_vlan_action(esw, attr); | 361 | err = mlx5_eswitch_add_vlan_action(esw, attr); |
| 336 | if (err) { | 362 | if (err) { |
| 337 | rule = ERR_PTR(err); | 363 | rule = ERR_PTR(err); |
| @@ -347,10 +373,14 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, | |||
| 347 | } | 373 | } |
| 348 | } | 374 | } |
| 349 | 375 | ||
| 350 | rule = mlx5_eswitch_add_offloaded_rule(esw, &parse_attr->spec, attr); | 376 | /* we get here if (1) there's no error (rule being null) or when |
| 351 | if (IS_ERR(rule)) | 377 | * (2) there's an encap action and we're on -EAGAIN (no valid neigh) |
| 352 | goto err_add_rule; | 378 | */ |
| 353 | 379 | if (rule != ERR_PTR(-EAGAIN)) { | |
| 380 | rule = mlx5_eswitch_add_offloaded_rule(esw, &parse_attr->spec, attr); | ||
| 381 | if (IS_ERR(rule)) | ||
| 382 | goto err_add_rule; | ||
| 383 | } | ||
| 354 | return rule; | 384 | return rule; |
| 355 | 385 | ||
| 356 | err_add_rule: | 386 | err_add_rule: |
| @@ -361,6 +391,7 @@ err_mod_hdr: | |||
| 361 | err_add_vlan: | 391 | err_add_vlan: |
| 362 | if (attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) | 392 | if (attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) |
| 363 | mlx5e_detach_encap(priv, flow); | 393 | mlx5e_detach_encap(priv, flow); |
| 394 | err_attach_encap: | ||
| 364 | return rule; | 395 | return rule; |
| 365 | } | 396 | } |
| 366 | 397 | ||
| @@ -389,6 +420,8 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, | |||
| 389 | void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv, | 420 | void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv, |
| 390 | struct mlx5e_encap_entry *e) | 421 | struct mlx5e_encap_entry *e) |
| 391 | { | 422 | { |
| 423 | struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; | ||
| 424 | struct mlx5_esw_flow_attr *esw_attr; | ||
| 392 | struct mlx5e_tc_flow *flow; | 425 | struct mlx5e_tc_flow *flow; |
| 393 | int err; | 426 | int err; |
| 394 | 427 | ||
| @@ -404,10 +437,9 @@ void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv, | |||
| 404 | mlx5e_rep_queue_neigh_stats_work(priv); | 437 | mlx5e_rep_queue_neigh_stats_work(priv); |
| 405 | 438 | ||
| 406 | list_for_each_entry(flow, &e->flows, encap) { | 439 | list_for_each_entry(flow, &e->flows, encap) { |
| 407 | flow->esw_attr->encap_id = e->encap_id; | 440 | esw_attr = flow->esw_attr; |
| 408 | flow->rule = mlx5e_tc_add_fdb_flow(priv, | 441 | esw_attr->encap_id = e->encap_id; |
| 409 | flow->esw_attr->parse_attr, | 442 | flow->rule = mlx5_eswitch_add_offloaded_rule(esw, &esw_attr->parse_attr->spec, esw_attr); |
| 410 | flow); | ||
| 411 | if (IS_ERR(flow->rule)) { | 443 | if (IS_ERR(flow->rule)) { |
| 412 | err = PTR_ERR(flow->rule); | 444 | err = PTR_ERR(flow->rule); |
| 413 | mlx5_core_warn(priv->mdev, "Failed to update cached encapsulation flow, %d\n", | 445 | mlx5_core_warn(priv->mdev, "Failed to update cached encapsulation flow, %d\n", |
| @@ -421,15 +453,13 @@ void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv, | |||
| 421 | void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv, | 453 | void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv, |
| 422 | struct mlx5e_encap_entry *e) | 454 | struct mlx5e_encap_entry *e) |
| 423 | { | 455 | { |
| 456 | struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; | ||
| 424 | struct mlx5e_tc_flow *flow; | 457 | struct mlx5e_tc_flow *flow; |
| 425 | struct mlx5_fc *counter; | ||
| 426 | 458 | ||
| 427 | list_for_each_entry(flow, &e->flows, encap) { | 459 | list_for_each_entry(flow, &e->flows, encap) { |
| 428 | if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) { | 460 | if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) { |
| 429 | flow->flags &= ~MLX5E_TC_FLOW_OFFLOADED; | 461 | flow->flags &= ~MLX5E_TC_FLOW_OFFLOADED; |
| 430 | counter = mlx5_flow_rule_counter(flow->rule); | 462 | mlx5_eswitch_del_offloaded_rule(esw, flow->rule, flow->esw_attr); |
| 431 | mlx5_del_flow_rules(flow->rule); | ||
| 432 | mlx5_fc_destroy(priv->mdev, counter); | ||
| 433 | } | 463 | } |
| 434 | } | 464 | } |
| 435 | 465 | ||
| @@ -1942,7 +1972,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, | |||
| 1942 | 1972 | ||
| 1943 | if (is_tcf_mirred_egress_redirect(a)) { | 1973 | if (is_tcf_mirred_egress_redirect(a)) { |
| 1944 | int ifindex = tcf_mirred_ifindex(a); | 1974 | int ifindex = tcf_mirred_ifindex(a); |
| 1945 | struct net_device *out_dev, *encap_dev = NULL; | 1975 | struct net_device *out_dev; |
| 1946 | struct mlx5e_priv *out_priv; | 1976 | struct mlx5e_priv *out_priv; |
| 1947 | 1977 | ||
| 1948 | out_dev = __dev_get_by_index(dev_net(priv->netdev), ifindex); | 1978 | out_dev = __dev_get_by_index(dev_net(priv->netdev), ifindex); |
| @@ -1955,17 +1985,13 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, | |||
| 1955 | rpriv = out_priv->ppriv; | 1985 | rpriv = out_priv->ppriv; |
| 1956 | attr->out_rep = rpriv->rep; | 1986 | attr->out_rep = rpriv->rep; |
| 1957 | } else if (encap) { | 1987 | } else if (encap) { |
| 1958 | err = mlx5e_attach_encap(priv, info, | 1988 | parse_attr->mirred_ifindex = ifindex; |
| 1959 | out_dev, &encap_dev, flow); | 1989 | parse_attr->tun_info = *info; |
| 1960 | if (err && err != -EAGAIN) | 1990 | attr->parse_attr = parse_attr; |
| 1961 | return err; | ||
| 1962 | attr->action |= MLX5_FLOW_CONTEXT_ACTION_ENCAP | | 1991 | attr->action |= MLX5_FLOW_CONTEXT_ACTION_ENCAP | |
| 1963 | MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | | 1992 | MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | |
| 1964 | MLX5_FLOW_CONTEXT_ACTION_COUNT; | 1993 | MLX5_FLOW_CONTEXT_ACTION_COUNT; |
| 1965 | out_priv = netdev_priv(encap_dev); | 1994 | /* attr->out_rep is resolved when we handle encap */ |
| 1966 | rpriv = out_priv->ppriv; | ||
| 1967 | attr->out_rep = rpriv->rep; | ||
| 1968 | attr->parse_attr = parse_attr; | ||
| 1969 | } else { | 1995 | } else { |
| 1970 | pr_err("devices %s %s not on same switch HW, can't offload forwarding\n", | 1996 | pr_err("devices %s %s not on same switch HW, can't offload forwarding\n", |
| 1971 | priv->netdev->name, out_dev->name); | 1997 | priv->netdev->name, out_dev->name); |
| @@ -2047,7 +2073,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, | |||
| 2047 | if (flow->flags & MLX5E_TC_FLOW_ESWITCH) { | 2073 | if (flow->flags & MLX5E_TC_FLOW_ESWITCH) { |
| 2048 | err = parse_tc_fdb_actions(priv, f->exts, parse_attr, flow); | 2074 | err = parse_tc_fdb_actions(priv, f->exts, parse_attr, flow); |
| 2049 | if (err < 0) | 2075 | if (err < 0) |
| 2050 | goto err_handle_encap_flow; | 2076 | goto err_free; |
| 2051 | flow->rule = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow); | 2077 | flow->rule = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow); |
| 2052 | } else { | 2078 | } else { |
| 2053 | err = parse_tc_nic_actions(priv, f->exts, parse_attr, flow); | 2079 | err = parse_tc_nic_actions(priv, f->exts, parse_attr, flow); |
| @@ -2058,10 +2084,13 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, | |||
| 2058 | 2084 | ||
| 2059 | if (IS_ERR(flow->rule)) { | 2085 | if (IS_ERR(flow->rule)) { |
| 2060 | err = PTR_ERR(flow->rule); | 2086 | err = PTR_ERR(flow->rule); |
| 2061 | goto err_free; | 2087 | if (err != -EAGAIN) |
| 2088 | goto err_free; | ||
| 2062 | } | 2089 | } |
| 2063 | 2090 | ||
| 2064 | flow->flags |= MLX5E_TC_FLOW_OFFLOADED; | 2091 | if (err != -EAGAIN) |
| 2092 | flow->flags |= MLX5E_TC_FLOW_OFFLOADED; | ||
| 2093 | |||
| 2065 | err = rhashtable_insert_fast(&tc->ht, &flow->node, | 2094 | err = rhashtable_insert_fast(&tc->ht, &flow->node, |
| 2066 | tc->ht_params); | 2095 | tc->ht_params); |
| 2067 | if (err) | 2096 | if (err) |
| @@ -2075,16 +2104,6 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, | |||
| 2075 | err_del_rule: | 2104 | err_del_rule: |
| 2076 | mlx5e_tc_del_flow(priv, flow); | 2105 | mlx5e_tc_del_flow(priv, flow); |
| 2077 | 2106 | ||
| 2078 | err_handle_encap_flow: | ||
| 2079 | if (err == -EAGAIN) { | ||
| 2080 | err = rhashtable_insert_fast(&tc->ht, &flow->node, | ||
| 2081 | tc->ht_params); | ||
| 2082 | if (err) | ||
| 2083 | mlx5e_tc_del_flow(priv, flow); | ||
| 2084 | else | ||
| 2085 | return 0; | ||
| 2086 | } | ||
| 2087 | |||
| 2088 | err_free: | 2107 | err_free: |
| 2089 | kvfree(parse_attr); | 2108 | kvfree(parse_attr); |
| 2090 | kfree(flow); | 2109 | kfree(flow); |
