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 11 static int br_switchdev_mark_get(struct net_bridge *br, struct net_device *dev) 12 { 13 struct net_bridge_port *p; 14 15 /* dev is yet to be added to the port list. */ 16 list_for_each_entry(p, &br->port_list, list) { 17 if (switchdev_port_same_parent_id(dev, p->dev)) 18 return p->offload_fwd_mark; 19 } 20 21 return ++br->offload_fwd_mark; 22 } 23 24 int nbp_switchdev_mark_set(struct net_bridge_port *p) 25 { 26 struct switchdev_attr attr = { 27 .orig_dev = p->dev, 28 .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID, 29 }; 30 int err; 31 32 ASSERT_RTNL(); 33 34 err = switchdev_port_attr_get(p->dev, &attr); 35 if (err) { 36 if (err == -EOPNOTSUPP) 37 return 0; 38 return err; 39 } 40 41 p->offload_fwd_mark = br_switchdev_mark_get(p->br, p->dev); 42 43 return 0; 44 } 45 46 void nbp_switchdev_frame_mark(const struct net_bridge_port *p, 47 struct sk_buff *skb) 48 { 49 if (skb->offload_fwd_mark && !WARN_ON_ONCE(!p->offload_fwd_mark)) 50 BR_INPUT_SKB_CB(skb)->offload_fwd_mark = p->offload_fwd_mark; 51 } 52 53 bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, 54 const struct sk_buff *skb) 55 { 56 return !skb->offload_fwd_mark || 57 BR_INPUT_SKB_CB(skb)->offload_fwd_mark != p->offload_fwd_mark; 58 } 59 60 /* Flags that can be offloaded to hardware */ 61 #define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \ 62 BR_MCAST_FLOOD | BR_BCAST_FLOOD) 63 64 int br_switchdev_set_port_flag(struct net_bridge_port *p, 65 unsigned long flags, 66 unsigned long mask) 67 { 68 struct switchdev_attr attr = { 69 .orig_dev = p->dev, 70 .id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT, 71 }; 72 int err; 73 74 if (mask & ~BR_PORT_FLAGS_HW_OFFLOAD) 75 return 0; 76 77 err = switchdev_port_attr_get(p->dev, &attr); 78 if (err == -EOPNOTSUPP) 79 return 0; 80 if (err) 81 return err; 82 83 /* Check if specific bridge flag attribute offload is supported */ 84 if (!(attr.u.brport_flags_support & mask)) { 85 br_warn(p->br, "bridge flag offload is not supported %u(%s)\n", 86 (unsigned int)p->port_no, p->dev->name); 87 return -EOPNOTSUPP; 88 } 89 90 attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS; 91 attr.flags = SWITCHDEV_F_DEFER; 92 attr.u.brport_flags = flags; 93 err = switchdev_port_attr_set(p->dev, &attr); 94 if (err) { 95 br_warn(p->br, "error setting offload flag on port %u(%s)\n", 96 (unsigned int)p->port_no, p->dev->name); 97 return err; 98 } 99 100 return 0; 101 } 102 103 static void 104 br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac, 105 u16 vid, struct net_device *dev) 106 { 107 struct switchdev_notifier_fdb_info info; 108 unsigned long notifier_type; 109 110 info.addr = mac; 111 info.vid = vid; 112 notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE; 113 call_switchdev_notifiers(notifier_type, dev, &info.info); 114 } 115 116 void 117 br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) 118 { 119 if (!fdb->added_by_user || !fdb->dst) 120 return; 121 122 switch (type) { 123 case RTM_DELNEIGH: 124 br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr, 125 fdb->key.vlan_id, 126 fdb->dst->dev); 127 break; 128 case RTM_NEWNEIGH: 129 br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr, 130 fdb->key.vlan_id, 131 fdb->dst->dev); 132 break; 133 } 134 } 135