1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun #include <linux/kernel.h>
3*4882a593Smuzhiyun #include <linux/list.h>
4*4882a593Smuzhiyun #include <linux/netdevice.h>
5*4882a593Smuzhiyun #include <linux/rtnetlink.h>
6*4882a593Smuzhiyun #include <linux/skbuff.h>
7*4882a593Smuzhiyun #include <net/switchdev.h>
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include "br_private.h"
10*4882a593Smuzhiyun
br_switchdev_mark_get(struct net_bridge * br,struct net_device * dev)11*4882a593Smuzhiyun static int br_switchdev_mark_get(struct net_bridge *br, struct net_device *dev)
12*4882a593Smuzhiyun {
13*4882a593Smuzhiyun struct net_bridge_port *p;
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun /* dev is yet to be added to the port list. */
16*4882a593Smuzhiyun list_for_each_entry(p, &br->port_list, list) {
17*4882a593Smuzhiyun if (netdev_port_same_parent_id(dev, p->dev))
18*4882a593Smuzhiyun return p->offload_fwd_mark;
19*4882a593Smuzhiyun }
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun return ++br->offload_fwd_mark;
22*4882a593Smuzhiyun }
23*4882a593Smuzhiyun
nbp_switchdev_mark_set(struct net_bridge_port * p)24*4882a593Smuzhiyun int nbp_switchdev_mark_set(struct net_bridge_port *p)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun struct netdev_phys_item_id ppid = { };
27*4882a593Smuzhiyun int err;
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun ASSERT_RTNL();
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun err = dev_get_port_parent_id(p->dev, &ppid, true);
32*4882a593Smuzhiyun if (err) {
33*4882a593Smuzhiyun if (err == -EOPNOTSUPP)
34*4882a593Smuzhiyun return 0;
35*4882a593Smuzhiyun return err;
36*4882a593Smuzhiyun }
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun p->offload_fwd_mark = br_switchdev_mark_get(p->br, p->dev);
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun return 0;
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun
nbp_switchdev_frame_mark(const struct net_bridge_port * p,struct sk_buff * skb)43*4882a593Smuzhiyun void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
44*4882a593Smuzhiyun struct sk_buff *skb)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun if (skb->offload_fwd_mark && !WARN_ON_ONCE(!p->offload_fwd_mark))
47*4882a593Smuzhiyun BR_INPUT_SKB_CB(skb)->offload_fwd_mark = p->offload_fwd_mark;
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun
nbp_switchdev_allowed_egress(const struct net_bridge_port * p,const struct sk_buff * skb)50*4882a593Smuzhiyun bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
51*4882a593Smuzhiyun const struct sk_buff *skb)
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun return !skb->offload_fwd_mark ||
54*4882a593Smuzhiyun BR_INPUT_SKB_CB(skb)->offload_fwd_mark != p->offload_fwd_mark;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun /* Flags that can be offloaded to hardware */
58*4882a593Smuzhiyun #define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \
59*4882a593Smuzhiyun BR_MCAST_FLOOD | BR_BCAST_FLOOD)
60*4882a593Smuzhiyun
br_switchdev_set_port_flag(struct net_bridge_port * p,unsigned long flags,unsigned long mask)61*4882a593Smuzhiyun int br_switchdev_set_port_flag(struct net_bridge_port *p,
62*4882a593Smuzhiyun unsigned long flags,
63*4882a593Smuzhiyun unsigned long mask)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun struct switchdev_attr attr = {
66*4882a593Smuzhiyun .orig_dev = p->dev,
67*4882a593Smuzhiyun .id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS,
68*4882a593Smuzhiyun .u.brport_flags = mask,
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun struct switchdev_notifier_port_attr_info info = {
71*4882a593Smuzhiyun .attr = &attr,
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun int err;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun if (mask & ~BR_PORT_FLAGS_HW_OFFLOAD)
76*4882a593Smuzhiyun return 0;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun /* We run from atomic context here */
79*4882a593Smuzhiyun err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
80*4882a593Smuzhiyun &info.info, NULL);
81*4882a593Smuzhiyun err = notifier_to_errno(err);
82*4882a593Smuzhiyun if (err == -EOPNOTSUPP)
83*4882a593Smuzhiyun return 0;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun if (err) {
86*4882a593Smuzhiyun br_warn(p->br, "bridge flag offload is not supported %u(%s)\n",
87*4882a593Smuzhiyun (unsigned int)p->port_no, p->dev->name);
88*4882a593Smuzhiyun return -EOPNOTSUPP;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS;
92*4882a593Smuzhiyun attr.flags = SWITCHDEV_F_DEFER;
93*4882a593Smuzhiyun attr.u.brport_flags = flags;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun err = switchdev_port_attr_set(p->dev, &attr);
96*4882a593Smuzhiyun if (err) {
97*4882a593Smuzhiyun br_warn(p->br, "error setting offload flag on port %u(%s)\n",
98*4882a593Smuzhiyun (unsigned int)p->port_no, p->dev->name);
99*4882a593Smuzhiyun return err;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun return 0;
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun static void
br_switchdev_fdb_call_notifiers(bool adding,const unsigned char * mac,u16 vid,struct net_device * dev,bool added_by_user,bool offloaded)106*4882a593Smuzhiyun br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac,
107*4882a593Smuzhiyun u16 vid, struct net_device *dev,
108*4882a593Smuzhiyun bool added_by_user, bool offloaded)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun struct switchdev_notifier_fdb_info info;
111*4882a593Smuzhiyun unsigned long notifier_type;
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun info.addr = mac;
114*4882a593Smuzhiyun info.vid = vid;
115*4882a593Smuzhiyun info.added_by_user = added_by_user;
116*4882a593Smuzhiyun info.offloaded = offloaded;
117*4882a593Smuzhiyun notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE;
118*4882a593Smuzhiyun call_switchdev_notifiers(notifier_type, dev, &info.info, NULL);
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun void
br_switchdev_fdb_notify(const struct net_bridge_fdb_entry * fdb,int type)122*4882a593Smuzhiyun br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun if (!fdb->dst)
125*4882a593Smuzhiyun return;
126*4882a593Smuzhiyun if (test_bit(BR_FDB_LOCAL, &fdb->flags))
127*4882a593Smuzhiyun return;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun switch (type) {
130*4882a593Smuzhiyun case RTM_DELNEIGH:
131*4882a593Smuzhiyun br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr,
132*4882a593Smuzhiyun fdb->key.vlan_id,
133*4882a593Smuzhiyun fdb->dst->dev,
134*4882a593Smuzhiyun test_bit(BR_FDB_ADDED_BY_USER,
135*4882a593Smuzhiyun &fdb->flags),
136*4882a593Smuzhiyun test_bit(BR_FDB_OFFLOADED,
137*4882a593Smuzhiyun &fdb->flags));
138*4882a593Smuzhiyun break;
139*4882a593Smuzhiyun case RTM_NEWNEIGH:
140*4882a593Smuzhiyun br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr,
141*4882a593Smuzhiyun fdb->key.vlan_id,
142*4882a593Smuzhiyun fdb->dst->dev,
143*4882a593Smuzhiyun test_bit(BR_FDB_ADDED_BY_USER,
144*4882a593Smuzhiyun &fdb->flags),
145*4882a593Smuzhiyun test_bit(BR_FDB_OFFLOADED,
146*4882a593Smuzhiyun &fdb->flags));
147*4882a593Smuzhiyun break;
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
br_switchdev_port_vlan_add(struct net_device * dev,u16 vid,u16 flags,struct netlink_ext_ack * extack)151*4882a593Smuzhiyun int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
152*4882a593Smuzhiyun struct netlink_ext_ack *extack)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun struct switchdev_obj_port_vlan v = {
155*4882a593Smuzhiyun .obj.orig_dev = dev,
156*4882a593Smuzhiyun .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
157*4882a593Smuzhiyun .flags = flags,
158*4882a593Smuzhiyun .vid_begin = vid,
159*4882a593Smuzhiyun .vid_end = vid,
160*4882a593Smuzhiyun };
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun return switchdev_port_obj_add(dev, &v.obj, extack);
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
br_switchdev_port_vlan_del(struct net_device * dev,u16 vid)165*4882a593Smuzhiyun int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun struct switchdev_obj_port_vlan v = {
168*4882a593Smuzhiyun .obj.orig_dev = dev,
169*4882a593Smuzhiyun .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
170*4882a593Smuzhiyun .vid_begin = vid,
171*4882a593Smuzhiyun .vid_end = vid,
172*4882a593Smuzhiyun };
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun return switchdev_port_obj_del(dev, &v.obj);
175*4882a593Smuzhiyun }
176