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 bool added_by_user, bool offloaded) 107 { 108 struct switchdev_notifier_fdb_info info; 109 unsigned long notifier_type; 110 111 info.addr = mac; 112 info.vid = vid; 113 info.added_by_user = added_by_user; 114 info.offloaded = offloaded; 115 notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE; 116 call_switchdev_notifiers(notifier_type, dev, &info.info); 117 } 118 119 void 120 br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) 121 { 122 if (!fdb->dst) 123 return; 124 125 switch (type) { 126 case RTM_DELNEIGH: 127 br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr, 128 fdb->key.vlan_id, 129 fdb->dst->dev, 130 fdb->added_by_user, 131 fdb->offloaded); 132 break; 133 case RTM_NEWNEIGH: 134 br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr, 135 fdb->key.vlan_id, 136 fdb->dst->dev, 137 fdb->added_by_user, 138 fdb->offloaded); 139 break; 140 } 141 } 142 143 int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags) 144 { 145 struct switchdev_obj_port_vlan v = { 146 .obj.orig_dev = dev, 147 .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 148 .flags = flags, 149 .vid_begin = vid, 150 .vid_end = vid, 151 }; 152 153 return switchdev_port_obj_add(dev, &v.obj); 154 } 155 156 int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) 157 { 158 struct switchdev_obj_port_vlan v = { 159 .obj.orig_dev = dev, 160 .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 161 .vid_begin = vid, 162 .vid_end = vid, 163 }; 164 165 return switchdev_port_obj_del(dev, &v.obj); 166 } 167