FILE
mpesw.c
File preview with syntax highlighting.
linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c
1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ 3 4#include <linux/netdevice.h> 5#include <net/nexthop.h> 6#include "lag/lag.h" 7#include "eswitch.h" 8#include "esw/acl/ofld.h" 9#include "lib/events.h" 10 11static void mlx5_mpesw_metadata_cleanup(struct mlx5_lag *ldev) 12{ 13 struct mlx5_core_dev *dev; 14 struct mlx5_eswitch *esw; 15 u32 pf_metadata; 16 int i; 17 18 mlx5_ldev_for_each(i, 0, ldev) { 19 dev = ldev->pf[i].dev; 20 esw = dev->priv.eswitch; 21 pf_metadata = ldev->lag_mpesw.pf_metadata[i]; 22 if (!pf_metadata) 23 continue; 24 mlx5_esw_acl_ingress_vport_metadata_update(esw, MLX5_VPORT_UPLINK, 0); 25 mlx5_notifier_call_chain(dev->priv.events, MLX5_DEV_EVENT_MULTIPORT_ESW, 26 (void *)0); 27 mlx5_esw_match_metadata_free(esw, pf_metadata); 28 ldev->lag_mpesw.pf_metadata[i] = 0; 29 } 30} 31 32static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev) 33{ 34 struct mlx5_core_dev *dev; 35 struct mlx5_eswitch *esw; 36 u32 pf_metadata; 37 int i, err; 38 39 mlx5_ldev_for_each(i, 0, ldev) { 40 dev = ldev->pf[i].dev; 41 esw = dev->priv.eswitch; 42 pf_metadata = mlx5_esw_match_metadata_alloc(esw); 43 if (!pf_metadata) { 44 err = -ENOSPC; 45 goto err_metadata; 46 } 47 48 ldev->lag_mpesw.pf_metadata[i] = pf_metadata; 49 err = mlx5_esw_acl_ingress_vport_metadata_update(esw, MLX5_VPORT_UPLINK, 50 pf_metadata); 51 if (err) 52 goto err_metadata; 53 } 54 55 mlx5_ldev_for_each(i, 0, ldev) { 56 dev = ldev->pf[i].dev; 57 mlx5_notifier_call_chain(dev->priv.events, MLX5_DEV_EVENT_MULTIPORT_ESW, 58 (void *)0); 59 } 60 61 return 0; 62 63err_metadata: 64 mlx5_mpesw_metadata_cleanup(ldev); 65 return err; 66} 67 68static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev) 69{ 70 struct mlx5_core_dev *dev0; 71 int err; 72 int idx; 73 int i; 74 75 if (ldev->mode == MLX5_LAG_MODE_MPESW) 76 return 0; 77 78 if (ldev->mode != MLX5_LAG_MODE_NONE) 79 return -EINVAL; 80 81 idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); 82 if (idx < 0) 83 return -EINVAL; 84 85 dev0 = ldev->pf[idx].dev; 86 if (mlx5_eswitch_mode(dev0) != MLX5_ESWITCH_OFFLOADS || 87 !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table) || 88 !MLX5_CAP_GEN(dev0, create_lag_when_not_master_up) || 89 !mlx5_lag_check_prereq(ldev) || 90 !mlx5_lag_shared_fdb_supported(ldev)) 91 return -EOPNOTSUPP; 92 93 err = mlx5_mpesw_metadata_set(ldev); 94 if (err) 95 return err; 96 97 mlx5_lag_remove_devices(ldev); 98 99 err = mlx5_activate_lag(ldev, NULL, MLX5_LAG_MODE_MPESW, true); 100 if (err) { 101 mlx5_core_warn(dev0, "Failed to create LAG in MPESW mode (%d)\n", err); 102 goto err_add_devices; 103 } 104 105 dev0->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; 106 mlx5_rescan_drivers_locked(dev0); 107 mlx5_ldev_for_each(i, 0, ldev) { 108 err = mlx5_eswitch_reload_ib_reps(ldev->pf[i].dev->priv.eswitch); 109 if (err) 110 goto err_rescan_drivers; 111 } 112 113 return 0; 114 115err_rescan_drivers: 116 dev0->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV; 117 mlx5_rescan_drivers_locked(dev0); 118 mlx5_deactivate_lag(ldev); 119err_add_devices: 120 mlx5_lag_add_devices(ldev); 121 mlx5_ldev_for_each(i, 0, ldev) 122 mlx5_eswitch_reload_ib_reps(ldev->pf[i].dev->priv.eswitch); 123 mlx5_mpesw_metadata_cleanup(ldev); 124 return err; 125} 126 127void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev) 128{ 129 if (ldev->mode == MLX5_LAG_MODE_MPESW) { 130 mlx5_mpesw_metadata_cleanup(ldev); 131 mlx5_disable_lag(ldev); 132 } 133} 134 135static void mlx5_mpesw_work(struct work_struct *work) 136{ 137 struct mlx5_mpesw_work_st *mpesww = container_of(work, struct mlx5_mpesw_work_st, work); 138 struct mlx5_devcom_comp_dev *devcom; 139 struct mlx5_lag *ldev = mpesww->lag; 140 141 devcom = mlx5_lag_get_devcom_comp(ldev); 142 if (!devcom) 143 return; 144 145 mlx5_devcom_comp_lock(devcom); 146 mutex_lock(&ldev->lock); 147 if (ldev->mode_changes_in_progress) { 148 mpesww->result = -EAGAIN; 149 goto unlock; 150 } 151 152 if (mpesww->op == MLX5_MPESW_OP_ENABLE) 153 mpesww->result = mlx5_lag_enable_mpesw(ldev); 154 else if (mpesww->op == MLX5_MPESW_OP_DISABLE) 155 mlx5_lag_disable_mpesw(ldev); 156unlock: 157 mutex_unlock(&ldev->lock); 158 mlx5_devcom_comp_unlock(devcom); 159 complete(&mpesww->comp); 160} 161 162static int mlx5_lag_mpesw_queue_work(struct mlx5_core_dev *dev, 163 enum mpesw_op op) 164{ 165 struct mlx5_lag *ldev = mlx5_lag_dev(dev); 166 struct mlx5_mpesw_work_st *work; 167 int err = 0; 168 169 if (!ldev) 170 return 0; 171 172 work = kzalloc(sizeof(*work), GFP_KERNEL); 173 if (!work) 174 return -ENOMEM; 175 176 INIT_WORK(&work->work, mlx5_mpesw_work); 177 init_completion(&work->comp); 178 work->op = op; 179 work->lag = ldev; 180 181 if (!queue_work(ldev->wq, &work->work)) { 182 mlx5_core_warn(dev, "failed to queue mpesw work\n"); 183 err = -EINVAL; 184 goto out; 185 } 186 wait_for_completion(&work->comp); 187 err = work->result; 188out: 189 kfree(work); 190 return err; 191} 192 193void mlx5_lag_mpesw_disable(struct mlx5_core_dev *dev) 194{ 195 mlx5_lag_mpesw_queue_work(dev, MLX5_MPESW_OP_DISABLE); 196} 197 198int mlx5_lag_mpesw_enable(struct mlx5_core_dev *dev) 199{ 200 return mlx5_lag_mpesw_queue_work(dev, MLX5_MPESW_OP_ENABLE); 201} 202 203int mlx5_lag_mpesw_do_mirred(struct mlx5_core_dev *mdev, 204 struct net_device *out_dev, 205 struct netlink_ext_ack *extack) 206{ 207 struct mlx5_lag *ldev = mlx5_lag_dev(mdev); 208 209 if (!netif_is_bond_master(out_dev) || !ldev) 210 return 0; 211 212 if (ldev->mode != MLX5_LAG_MODE_MPESW) 213 return 0; 214 215 NL_SET_ERR_MSG_MOD(extack, "can't forward to bond in mpesw mode"); 216 return -EOPNOTSUPP; 217} 218 219bool mlx5_lag_is_mpesw(struct mlx5_core_dev *dev) 220{ 221 struct mlx5_lag *ldev = mlx5_lag_dev(dev); 222 223 return ldev && ldev->mode == MLX5_LAG_MODE_MPESW; 224} 225EXPORT_SYMBOL(mlx5_lag_is_mpesw);
~/
Read-only file preview.