at v5.15-rc7 9.0 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2#include <linux/kernel.h> 3#include <linux/list.h> 4#include <linux/netdevice.h> 5#include <linux/rtnetlink.h> 6#include <linux/skbuff.h> 7#include <net/switchdev.h> 8 9#include "br_private.h" 10 11static struct static_key_false br_switchdev_tx_fwd_offload; 12 13static bool nbp_switchdev_can_offload_tx_fwd(const struct net_bridge_port *p, 14 const struct sk_buff *skb) 15{ 16 if (!static_branch_unlikely(&br_switchdev_tx_fwd_offload)) 17 return false; 18 19 return (p->flags & BR_TX_FWD_OFFLOAD) && 20 (p->hwdom != BR_INPUT_SKB_CB(skb)->src_hwdom); 21} 22 23bool br_switchdev_frame_uses_tx_fwd_offload(struct sk_buff *skb) 24{ 25 if (!static_branch_unlikely(&br_switchdev_tx_fwd_offload)) 26 return false; 27 28 return BR_INPUT_SKB_CB(skb)->tx_fwd_offload; 29} 30 31void br_switchdev_frame_set_offload_fwd_mark(struct sk_buff *skb) 32{ 33 skb->offload_fwd_mark = br_switchdev_frame_uses_tx_fwd_offload(skb); 34} 35 36/* Mark the frame for TX forwarding offload if this egress port supports it */ 37void nbp_switchdev_frame_mark_tx_fwd_offload(const struct net_bridge_port *p, 38 struct sk_buff *skb) 39{ 40 if (nbp_switchdev_can_offload_tx_fwd(p, skb)) 41 BR_INPUT_SKB_CB(skb)->tx_fwd_offload = true; 42} 43 44/* Lazily adds the hwdom of the egress bridge port to the bit mask of hwdoms 45 * that the skb has been already forwarded to, to avoid further cloning to 46 * other ports in the same hwdom by making nbp_switchdev_allowed_egress() 47 * return false. 48 */ 49void nbp_switchdev_frame_mark_tx_fwd_to_hwdom(const struct net_bridge_port *p, 50 struct sk_buff *skb) 51{ 52 if (nbp_switchdev_can_offload_tx_fwd(p, skb)) 53 set_bit(p->hwdom, &BR_INPUT_SKB_CB(skb)->fwd_hwdoms); 54} 55 56void nbp_switchdev_frame_mark(const struct net_bridge_port *p, 57 struct sk_buff *skb) 58{ 59 if (p->hwdom) 60 BR_INPUT_SKB_CB(skb)->src_hwdom = p->hwdom; 61} 62 63bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, 64 const struct sk_buff *skb) 65{ 66 struct br_input_skb_cb *cb = BR_INPUT_SKB_CB(skb); 67 68 return !test_bit(p->hwdom, &cb->fwd_hwdoms) && 69 (!skb->offload_fwd_mark || cb->src_hwdom != p->hwdom); 70} 71 72/* Flags that can be offloaded to hardware */ 73#define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \ 74 BR_MCAST_FLOOD | BR_BCAST_FLOOD) 75 76int br_switchdev_set_port_flag(struct net_bridge_port *p, 77 unsigned long flags, 78 unsigned long mask, 79 struct netlink_ext_ack *extack) 80{ 81 struct switchdev_attr attr = { 82 .orig_dev = p->dev, 83 }; 84 struct switchdev_notifier_port_attr_info info = { 85 .attr = &attr, 86 }; 87 int err; 88 89 mask &= BR_PORT_FLAGS_HW_OFFLOAD; 90 if (!mask) 91 return 0; 92 93 attr.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS; 94 attr.u.brport_flags.val = flags; 95 attr.u.brport_flags.mask = mask; 96 97 /* We run from atomic context here */ 98 err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev, 99 &info.info, extack); 100 err = notifier_to_errno(err); 101 if (err == -EOPNOTSUPP) 102 return 0; 103 104 if (err) { 105 if (extack && !extack->_msg) 106 NL_SET_ERR_MSG_MOD(extack, 107 "bridge flag offload is not supported"); 108 return -EOPNOTSUPP; 109 } 110 111 attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS; 112 attr.flags = SWITCHDEV_F_DEFER; 113 114 err = switchdev_port_attr_set(p->dev, &attr, extack); 115 if (err) { 116 if (extack && !extack->_msg) 117 NL_SET_ERR_MSG_MOD(extack, 118 "error setting offload flag on port"); 119 return err; 120 } 121 122 return 0; 123} 124 125void 126br_switchdev_fdb_notify(struct net_bridge *br, 127 const struct net_bridge_fdb_entry *fdb, int type) 128{ 129 const struct net_bridge_port *dst = READ_ONCE(fdb->dst); 130 struct switchdev_notifier_fdb_info info = { 131 .addr = fdb->key.addr.addr, 132 .vid = fdb->key.vlan_id, 133 .added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags), 134 .is_local = test_bit(BR_FDB_LOCAL, &fdb->flags), 135 .offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags), 136 }; 137 struct net_device *dev = (!dst || info.is_local) ? br->dev : dst->dev; 138 139 switch (type) { 140 case RTM_DELNEIGH: 141 call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE, 142 dev, &info.info, NULL); 143 break; 144 case RTM_NEWNEIGH: 145 call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE, 146 dev, &info.info, NULL); 147 break; 148 } 149} 150 151int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, 152 struct netlink_ext_ack *extack) 153{ 154 struct switchdev_obj_port_vlan v = { 155 .obj.orig_dev = dev, 156 .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 157 .flags = flags, 158 .vid = vid, 159 }; 160 161 return switchdev_port_obj_add(dev, &v.obj, extack); 162} 163 164int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) 165{ 166 struct switchdev_obj_port_vlan v = { 167 .obj.orig_dev = dev, 168 .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 169 .vid = vid, 170 }; 171 172 return switchdev_port_obj_del(dev, &v.obj); 173} 174 175static int nbp_switchdev_hwdom_set(struct net_bridge_port *joining) 176{ 177 struct net_bridge *br = joining->br; 178 struct net_bridge_port *p; 179 int hwdom; 180 181 /* joining is yet to be added to the port list. */ 182 list_for_each_entry(p, &br->port_list, list) { 183 if (netdev_phys_item_id_same(&joining->ppid, &p->ppid)) { 184 joining->hwdom = p->hwdom; 185 return 0; 186 } 187 } 188 189 hwdom = find_next_zero_bit(&br->busy_hwdoms, BR_HWDOM_MAX, 1); 190 if (hwdom >= BR_HWDOM_MAX) 191 return -EBUSY; 192 193 set_bit(hwdom, &br->busy_hwdoms); 194 joining->hwdom = hwdom; 195 return 0; 196} 197 198static void nbp_switchdev_hwdom_put(struct net_bridge_port *leaving) 199{ 200 struct net_bridge *br = leaving->br; 201 struct net_bridge_port *p; 202 203 /* leaving is no longer in the port list. */ 204 list_for_each_entry(p, &br->port_list, list) { 205 if (p->hwdom == leaving->hwdom) 206 return; 207 } 208 209 clear_bit(leaving->hwdom, &br->busy_hwdoms); 210} 211 212static int nbp_switchdev_add(struct net_bridge_port *p, 213 struct netdev_phys_item_id ppid, 214 bool tx_fwd_offload, 215 struct netlink_ext_ack *extack) 216{ 217 int err; 218 219 if (p->offload_count) { 220 /* Prevent unsupported configurations such as a bridge port 221 * which is a bonding interface, and the member ports are from 222 * different hardware switches. 223 */ 224 if (!netdev_phys_item_id_same(&p->ppid, &ppid)) { 225 NL_SET_ERR_MSG_MOD(extack, 226 "Same bridge port cannot be offloaded by two physical switches"); 227 return -EBUSY; 228 } 229 230 /* Tolerate drivers that call switchdev_bridge_port_offload() 231 * more than once for the same bridge port, such as when the 232 * bridge port is an offloaded bonding/team interface. 233 */ 234 p->offload_count++; 235 236 return 0; 237 } 238 239 p->ppid = ppid; 240 p->offload_count = 1; 241 242 err = nbp_switchdev_hwdom_set(p); 243 if (err) 244 return err; 245 246 if (tx_fwd_offload) { 247 p->flags |= BR_TX_FWD_OFFLOAD; 248 static_branch_inc(&br_switchdev_tx_fwd_offload); 249 } 250 251 return 0; 252} 253 254static void nbp_switchdev_del(struct net_bridge_port *p) 255{ 256 if (WARN_ON(!p->offload_count)) 257 return; 258 259 p->offload_count--; 260 261 if (p->offload_count) 262 return; 263 264 if (p->hwdom) 265 nbp_switchdev_hwdom_put(p); 266 267 if (p->flags & BR_TX_FWD_OFFLOAD) { 268 p->flags &= ~BR_TX_FWD_OFFLOAD; 269 static_branch_dec(&br_switchdev_tx_fwd_offload); 270 } 271} 272 273static int nbp_switchdev_sync_objs(struct net_bridge_port *p, const void *ctx, 274 struct notifier_block *atomic_nb, 275 struct notifier_block *blocking_nb, 276 struct netlink_ext_ack *extack) 277{ 278 struct net_device *br_dev = p->br->dev; 279 struct net_device *dev = p->dev; 280 int err; 281 282 err = br_vlan_replay(br_dev, dev, ctx, true, blocking_nb, extack); 283 if (err && err != -EOPNOTSUPP) 284 return err; 285 286 err = br_mdb_replay(br_dev, dev, ctx, true, blocking_nb, extack); 287 if (err && err != -EOPNOTSUPP) 288 return err; 289 290 err = br_fdb_replay(br_dev, ctx, true, atomic_nb); 291 if (err && err != -EOPNOTSUPP) 292 return err; 293 294 return 0; 295} 296 297static void nbp_switchdev_unsync_objs(struct net_bridge_port *p, 298 const void *ctx, 299 struct notifier_block *atomic_nb, 300 struct notifier_block *blocking_nb) 301{ 302 struct net_device *br_dev = p->br->dev; 303 struct net_device *dev = p->dev; 304 305 br_vlan_replay(br_dev, dev, ctx, false, blocking_nb, NULL); 306 307 br_mdb_replay(br_dev, dev, ctx, false, blocking_nb, NULL); 308 309 br_fdb_replay(br_dev, ctx, false, atomic_nb); 310} 311 312/* Let the bridge know that this port is offloaded, so that it can assign a 313 * switchdev hardware domain to it. 314 */ 315int br_switchdev_port_offload(struct net_bridge_port *p, 316 struct net_device *dev, const void *ctx, 317 struct notifier_block *atomic_nb, 318 struct notifier_block *blocking_nb, 319 bool tx_fwd_offload, 320 struct netlink_ext_ack *extack) 321{ 322 struct netdev_phys_item_id ppid; 323 int err; 324 325 err = dev_get_port_parent_id(dev, &ppid, false); 326 if (err) 327 return err; 328 329 err = nbp_switchdev_add(p, ppid, tx_fwd_offload, extack); 330 if (err) 331 return err; 332 333 err = nbp_switchdev_sync_objs(p, ctx, atomic_nb, blocking_nb, extack); 334 if (err) 335 goto out_switchdev_del; 336 337 return 0; 338 339out_switchdev_del: 340 nbp_switchdev_del(p); 341 342 return err; 343} 344 345void br_switchdev_port_unoffload(struct net_bridge_port *p, const void *ctx, 346 struct notifier_block *atomic_nb, 347 struct notifier_block *blocking_nb) 348{ 349 nbp_switchdev_unsync_objs(p, ctx, atomic_nb, blocking_nb); 350 351 nbp_switchdev_del(p); 352}