net/mlx5e: Attach/detach XDP program safely
authorMaxim Mikityanskiy <maximmi@mellanox.com>
Wed, 26 Jun 2019 14:35:23 +0000 (17:35 +0300)
committerDaniel Borkmann <daniel@iogearbox.net>
Thu, 27 Jun 2019 20:53:26 +0000 (22:53 +0200)
When an XDP program is set, a full reopen of all channels happens in two
cases:

1. When there was no program set, and a new one is being set.

2. When there was a program set, but it's being unset.

The full reopen is necessary, because the channel parameters may change
if XDP is enabled or disabled. However, it's performed in an unsafe way:
if the new channels fail to open, the old ones are already closed, and
the interface goes down. Use the safe way to switch channels instead.
The same way is already used for other configuration changes.

Signed-off-by: Maxim Mikityanskiy <maximmi@mellanox.com>
Signed-off-by: Tariq Toukan <tariqt@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
drivers/net/ethernet/mellanox/mlx5/core/en_main.c

index 5e40db8f92e615266fdeff923190472ec244189f..1b427d7fab42a8c8c4f169dc18a276182ad49aa3 100644 (file)
@@ -4190,8 +4190,6 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
        /* no need for full reset when exchanging programs */
        reset = (!priv->channels.params.xdp_prog || !prog);
 
-       if (was_opened && reset)
-               mlx5e_close_locked(netdev);
        if (was_opened && !reset) {
                /* num_channels is invariant here, so we can take the
                 * batched reference right upfront.
@@ -4203,20 +4201,31 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
                }
        }
 
-       /* exchange programs, extra prog reference we got from caller
-        * as long as we don't fail from this point onwards.
-        */
-       old_prog = xchg(&priv->channels.params.xdp_prog, prog);
+       if (was_opened && reset) {
+               struct mlx5e_channels new_channels = {};
+
+               new_channels.params = priv->channels.params;
+               new_channels.params.xdp_prog = prog;
+               mlx5e_set_rq_type(priv->mdev, &new_channels.params);
+               old_prog = priv->channels.params.xdp_prog;
+
+               err = mlx5e_safe_switch_channels(priv, &new_channels, NULL);
+               if (err)
+                       goto unlock;
+       } else {
+               /* exchange programs, extra prog reference we got from caller
+                * as long as we don't fail from this point onwards.
+                */
+               old_prog = xchg(&priv->channels.params.xdp_prog, prog);
+       }
+
        if (old_prog)
                bpf_prog_put(old_prog);
 
-       if (reset) /* change RQ type according to priv->xdp_prog */
+       if (!was_opened && reset) /* change RQ type according to priv->xdp_prog */
                mlx5e_set_rq_type(priv->mdev, &priv->channels.params);
 
-       if (was_opened && reset)
-               err = mlx5e_open_locked(netdev);
-
-       if (!test_bit(MLX5E_STATE_OPENED, &priv->state) || reset)
+       if (!was_opened || reset)
                goto unlock;
 
        /* exchanging programs w/o reset, we update ref counts on behalf