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