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