xref: /linux-6.15/net/switchdev/switchdev.c (revision 3094333d)
1007f790cSJiri Pirko /*
2007f790cSJiri Pirko  * net/switchdev/switchdev.c - Switch device API
3007f790cSJiri Pirko  * Copyright (c) 2014 Jiri Pirko <[email protected]>
4f8f21471SScott Feldman  * Copyright (c) 2014-2015 Scott Feldman <[email protected]>
5007f790cSJiri Pirko  *
6007f790cSJiri Pirko  * This program is free software; you can redistribute it and/or modify
7007f790cSJiri Pirko  * it under the terms of the GNU General Public License as published by
8007f790cSJiri Pirko  * the Free Software Foundation; either version 2 of the License, or
9007f790cSJiri Pirko  * (at your option) any later version.
10007f790cSJiri Pirko  */
11007f790cSJiri Pirko 
12007f790cSJiri Pirko #include <linux/kernel.h>
13007f790cSJiri Pirko #include <linux/types.h>
14007f790cSJiri Pirko #include <linux/init.h>
1503bf0c28SJiri Pirko #include <linux/mutex.h>
1603bf0c28SJiri Pirko #include <linux/notifier.h>
17007f790cSJiri Pirko #include <linux/netdevice.h>
185e8d9049SScott Feldman #include <net/ip_fib.h>
19007f790cSJiri Pirko #include <net/switchdev.h>
20007f790cSJiri Pirko 
21007f790cSJiri Pirko /**
22ebb9a03aSJiri Pirko  *	switchdev_parent_id_get - Get ID of a switch
23007f790cSJiri Pirko  *	@dev: port device
24007f790cSJiri Pirko  *	@psid: switch ID
25007f790cSJiri Pirko  *
26007f790cSJiri Pirko  *	Get ID of a switch this port is part of.
27007f790cSJiri Pirko  */
28ebb9a03aSJiri Pirko int switchdev_parent_id_get(struct net_device *dev,
29007f790cSJiri Pirko 			    struct netdev_phys_item_id *psid)
30007f790cSJiri Pirko {
319d47c0a2SJiri Pirko 	const struct switchdev_ops *ops = dev->switchdev_ops;
32007f790cSJiri Pirko 
339d47c0a2SJiri Pirko 	if (!ops || !ops->switchdev_parent_id_get)
34007f790cSJiri Pirko 		return -EOPNOTSUPP;
359d47c0a2SJiri Pirko 	return ops->switchdev_parent_id_get(dev, psid);
36007f790cSJiri Pirko }
37ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_parent_id_get);
3838dcf357SScott Feldman 
3938dcf357SScott Feldman /**
40*3094333dSScott Feldman  *	switchdev_port_attr_get - Get port attribute
41*3094333dSScott Feldman  *
42*3094333dSScott Feldman  *	@dev: port device
43*3094333dSScott Feldman  *	@attr: attribute to get
44*3094333dSScott Feldman  */
45*3094333dSScott Feldman int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr)
46*3094333dSScott Feldman {
47*3094333dSScott Feldman 	const struct switchdev_ops *ops = dev->switchdev_ops;
48*3094333dSScott Feldman 	struct net_device *lower_dev;
49*3094333dSScott Feldman 	struct list_head *iter;
50*3094333dSScott Feldman 	struct switchdev_attr first = {
51*3094333dSScott Feldman 		.id = SWITCHDEV_ATTR_UNDEFINED
52*3094333dSScott Feldman 	};
53*3094333dSScott Feldman 	int err = -EOPNOTSUPP;
54*3094333dSScott Feldman 
55*3094333dSScott Feldman 	if (ops && ops->switchdev_port_attr_get)
56*3094333dSScott Feldman 		return ops->switchdev_port_attr_get(dev, attr);
57*3094333dSScott Feldman 
58*3094333dSScott Feldman 	if (attr->flags & SWITCHDEV_F_NO_RECURSE)
59*3094333dSScott Feldman 		return err;
60*3094333dSScott Feldman 
61*3094333dSScott Feldman 	/* Switch device port(s) may be stacked under
62*3094333dSScott Feldman 	 * bond/team/vlan dev, so recurse down to get attr on
63*3094333dSScott Feldman 	 * each port.  Return -ENODATA if attr values don't
64*3094333dSScott Feldman 	 * compare across ports.
65*3094333dSScott Feldman 	 */
66*3094333dSScott Feldman 
67*3094333dSScott Feldman 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
68*3094333dSScott Feldman 		err = switchdev_port_attr_get(lower_dev, attr);
69*3094333dSScott Feldman 		if (err)
70*3094333dSScott Feldman 			break;
71*3094333dSScott Feldman 		if (first.id == SWITCHDEV_ATTR_UNDEFINED)
72*3094333dSScott Feldman 			first = *attr;
73*3094333dSScott Feldman 		else if (memcmp(&first, attr, sizeof(*attr)))
74*3094333dSScott Feldman 			return -ENODATA;
75*3094333dSScott Feldman 	}
76*3094333dSScott Feldman 
77*3094333dSScott Feldman 	return err;
78*3094333dSScott Feldman }
79*3094333dSScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_attr_get);
80*3094333dSScott Feldman 
81*3094333dSScott Feldman static int __switchdev_port_attr_set(struct net_device *dev,
82*3094333dSScott Feldman 				     struct switchdev_attr *attr)
83*3094333dSScott Feldman {
84*3094333dSScott Feldman 	const struct switchdev_ops *ops = dev->switchdev_ops;
85*3094333dSScott Feldman 	struct net_device *lower_dev;
86*3094333dSScott Feldman 	struct list_head *iter;
87*3094333dSScott Feldman 	int err = -EOPNOTSUPP;
88*3094333dSScott Feldman 
89*3094333dSScott Feldman 	if (ops && ops->switchdev_port_attr_set)
90*3094333dSScott Feldman 		return ops->switchdev_port_attr_set(dev, attr);
91*3094333dSScott Feldman 
92*3094333dSScott Feldman 	if (attr->flags & SWITCHDEV_F_NO_RECURSE)
93*3094333dSScott Feldman 		return err;
94*3094333dSScott Feldman 
95*3094333dSScott Feldman 	/* Switch device port(s) may be stacked under
96*3094333dSScott Feldman 	 * bond/team/vlan dev, so recurse down to set attr on
97*3094333dSScott Feldman 	 * each port.
98*3094333dSScott Feldman 	 */
99*3094333dSScott Feldman 
100*3094333dSScott Feldman 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
101*3094333dSScott Feldman 		err = __switchdev_port_attr_set(lower_dev, attr);
102*3094333dSScott Feldman 		if (err)
103*3094333dSScott Feldman 			break;
104*3094333dSScott Feldman 	}
105*3094333dSScott Feldman 
106*3094333dSScott Feldman 	return err;
107*3094333dSScott Feldman }
108*3094333dSScott Feldman 
109*3094333dSScott Feldman struct switchdev_attr_set_work {
110*3094333dSScott Feldman 	struct work_struct work;
111*3094333dSScott Feldman 	struct net_device *dev;
112*3094333dSScott Feldman 	struct switchdev_attr attr;
113*3094333dSScott Feldman };
114*3094333dSScott Feldman 
115*3094333dSScott Feldman static void switchdev_port_attr_set_work(struct work_struct *work)
116*3094333dSScott Feldman {
117*3094333dSScott Feldman 	struct switchdev_attr_set_work *asw =
118*3094333dSScott Feldman 		container_of(work, struct switchdev_attr_set_work, work);
119*3094333dSScott Feldman 	int err;
120*3094333dSScott Feldman 
121*3094333dSScott Feldman 	rtnl_lock();
122*3094333dSScott Feldman 	err = switchdev_port_attr_set(asw->dev, &asw->attr);
123*3094333dSScott Feldman 	BUG_ON(err);
124*3094333dSScott Feldman 	rtnl_unlock();
125*3094333dSScott Feldman 
126*3094333dSScott Feldman 	dev_put(asw->dev);
127*3094333dSScott Feldman 	kfree(work);
128*3094333dSScott Feldman }
129*3094333dSScott Feldman 
130*3094333dSScott Feldman static int switchdev_port_attr_set_defer(struct net_device *dev,
131*3094333dSScott Feldman 					 struct switchdev_attr *attr)
132*3094333dSScott Feldman {
133*3094333dSScott Feldman 	struct switchdev_attr_set_work *asw;
134*3094333dSScott Feldman 
135*3094333dSScott Feldman 	asw = kmalloc(sizeof(*asw), GFP_ATOMIC);
136*3094333dSScott Feldman 	if (!asw)
137*3094333dSScott Feldman 		return -ENOMEM;
138*3094333dSScott Feldman 
139*3094333dSScott Feldman 	INIT_WORK(&asw->work, switchdev_port_attr_set_work);
140*3094333dSScott Feldman 
141*3094333dSScott Feldman 	dev_hold(dev);
142*3094333dSScott Feldman 	asw->dev = dev;
143*3094333dSScott Feldman 	memcpy(&asw->attr, attr, sizeof(asw->attr));
144*3094333dSScott Feldman 
145*3094333dSScott Feldman 	schedule_work(&asw->work);
146*3094333dSScott Feldman 
147*3094333dSScott Feldman 	return 0;
148*3094333dSScott Feldman }
149*3094333dSScott Feldman 
150*3094333dSScott Feldman /**
151*3094333dSScott Feldman  *	switchdev_port_attr_set - Set port attribute
152*3094333dSScott Feldman  *
153*3094333dSScott Feldman  *	@dev: port device
154*3094333dSScott Feldman  *	@attr: attribute to set
155*3094333dSScott Feldman  *
156*3094333dSScott Feldman  *	Use a 2-phase prepare-commit transaction model to ensure
157*3094333dSScott Feldman  *	system is not left in a partially updated state due to
158*3094333dSScott Feldman  *	failure from driver/device.
159*3094333dSScott Feldman  */
160*3094333dSScott Feldman int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr)
161*3094333dSScott Feldman {
162*3094333dSScott Feldman 	int err;
163*3094333dSScott Feldman 
164*3094333dSScott Feldman 	if (!rtnl_is_locked()) {
165*3094333dSScott Feldman 		/* Running prepare-commit transaction across stacked
166*3094333dSScott Feldman 		 * devices requires nothing moves, so if rtnl_lock is
167*3094333dSScott Feldman 		 * not held, schedule a worker thread to hold rtnl_lock
168*3094333dSScott Feldman 		 * while setting attr.
169*3094333dSScott Feldman 		 */
170*3094333dSScott Feldman 
171*3094333dSScott Feldman 		return switchdev_port_attr_set_defer(dev, attr);
172*3094333dSScott Feldman 	}
173*3094333dSScott Feldman 
174*3094333dSScott Feldman 	/* Phase I: prepare for attr set. Driver/device should fail
175*3094333dSScott Feldman 	 * here if there are going to be issues in the commit phase,
176*3094333dSScott Feldman 	 * such as lack of resources or support.  The driver/device
177*3094333dSScott Feldman 	 * should reserve resources needed for the commit phase here,
178*3094333dSScott Feldman 	 * but should not commit the attr.
179*3094333dSScott Feldman 	 */
180*3094333dSScott Feldman 
181*3094333dSScott Feldman 	attr->trans = SWITCHDEV_TRANS_PREPARE;
182*3094333dSScott Feldman 	err = __switchdev_port_attr_set(dev, attr);
183*3094333dSScott Feldman 	if (err) {
184*3094333dSScott Feldman 		/* Prepare phase failed: abort the transaction.  Any
185*3094333dSScott Feldman 		 * resources reserved in the prepare phase are
186*3094333dSScott Feldman 		 * released.
187*3094333dSScott Feldman 		 */
188*3094333dSScott Feldman 
189*3094333dSScott Feldman 		attr->trans = SWITCHDEV_TRANS_ABORT;
190*3094333dSScott Feldman 		__switchdev_port_attr_set(dev, attr);
191*3094333dSScott Feldman 
192*3094333dSScott Feldman 		return err;
193*3094333dSScott Feldman 	}
194*3094333dSScott Feldman 
195*3094333dSScott Feldman 	/* Phase II: commit attr set.  This cannot fail as a fault
196*3094333dSScott Feldman 	 * of driver/device.  If it does, it's a bug in the driver/device
197*3094333dSScott Feldman 	 * because the driver said everythings was OK in phase I.
198*3094333dSScott Feldman 	 */
199*3094333dSScott Feldman 
200*3094333dSScott Feldman 	attr->trans = SWITCHDEV_TRANS_COMMIT;
201*3094333dSScott Feldman 	err = __switchdev_port_attr_set(dev, attr);
202*3094333dSScott Feldman 	BUG_ON(err);
203*3094333dSScott Feldman 
204*3094333dSScott Feldman 	return err;
205*3094333dSScott Feldman }
206*3094333dSScott Feldman EXPORT_SYMBOL_GPL(switchdev_port_attr_set);
207*3094333dSScott Feldman 
208*3094333dSScott Feldman /**
209ebb9a03aSJiri Pirko  *	switchdev_port_stp_update - Notify switch device port of STP
21038dcf357SScott Feldman  *					state change
21138dcf357SScott Feldman  *	@dev: port device
21238dcf357SScott Feldman  *	@state: port STP state
21338dcf357SScott Feldman  *
21438dcf357SScott Feldman  *	Notify switch device port of bridge port STP state change.
21538dcf357SScott Feldman  */
216ebb9a03aSJiri Pirko int switchdev_port_stp_update(struct net_device *dev, u8 state)
21738dcf357SScott Feldman {
2189d47c0a2SJiri Pirko 	const struct switchdev_ops *ops = dev->switchdev_ops;
219558d51faSRoopa Prabhu 	struct net_device *lower_dev;
220558d51faSRoopa Prabhu 	struct list_head *iter;
221558d51faSRoopa Prabhu 	int err = -EOPNOTSUPP;
22238dcf357SScott Feldman 
2239d47c0a2SJiri Pirko 	if (ops && ops->switchdev_port_stp_update)
2249d47c0a2SJiri Pirko 		return ops->switchdev_port_stp_update(dev, state);
225558d51faSRoopa Prabhu 
226558d51faSRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
227ebb9a03aSJiri Pirko 		err = switchdev_port_stp_update(lower_dev, state);
228558d51faSRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
229558d51faSRoopa Prabhu 			return err;
230558d51faSRoopa Prabhu 	}
231558d51faSRoopa Prabhu 
232558d51faSRoopa Prabhu 	return err;
23338dcf357SScott Feldman }
234ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_stp_update);
23503bf0c28SJiri Pirko 
236ebb9a03aSJiri Pirko static DEFINE_MUTEX(switchdev_mutex);
237ebb9a03aSJiri Pirko static RAW_NOTIFIER_HEAD(switchdev_notif_chain);
23803bf0c28SJiri Pirko 
23903bf0c28SJiri Pirko /**
240ebb9a03aSJiri Pirko  *	register_switchdev_notifier - Register notifier
24103bf0c28SJiri Pirko  *	@nb: notifier_block
24203bf0c28SJiri Pirko  *
24303bf0c28SJiri Pirko  *	Register switch device notifier. This should be used by code
24403bf0c28SJiri Pirko  *	which needs to monitor events happening in particular device.
24503bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_chain_register().
24603bf0c28SJiri Pirko  */
247ebb9a03aSJiri Pirko int register_switchdev_notifier(struct notifier_block *nb)
24803bf0c28SJiri Pirko {
24903bf0c28SJiri Pirko 	int err;
25003bf0c28SJiri Pirko 
251ebb9a03aSJiri Pirko 	mutex_lock(&switchdev_mutex);
252ebb9a03aSJiri Pirko 	err = raw_notifier_chain_register(&switchdev_notif_chain, nb);
253ebb9a03aSJiri Pirko 	mutex_unlock(&switchdev_mutex);
25403bf0c28SJiri Pirko 	return err;
25503bf0c28SJiri Pirko }
256ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(register_switchdev_notifier);
25703bf0c28SJiri Pirko 
25803bf0c28SJiri Pirko /**
259ebb9a03aSJiri Pirko  *	unregister_switchdev_notifier - Unregister notifier
26003bf0c28SJiri Pirko  *	@nb: notifier_block
26103bf0c28SJiri Pirko  *
26203bf0c28SJiri Pirko  *	Unregister switch device notifier.
26303bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_chain_unregister().
26403bf0c28SJiri Pirko  */
265ebb9a03aSJiri Pirko int unregister_switchdev_notifier(struct notifier_block *nb)
26603bf0c28SJiri Pirko {
26703bf0c28SJiri Pirko 	int err;
26803bf0c28SJiri Pirko 
269ebb9a03aSJiri Pirko 	mutex_lock(&switchdev_mutex);
270ebb9a03aSJiri Pirko 	err = raw_notifier_chain_unregister(&switchdev_notif_chain, nb);
271ebb9a03aSJiri Pirko 	mutex_unlock(&switchdev_mutex);
27203bf0c28SJiri Pirko 	return err;
27303bf0c28SJiri Pirko }
274ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(unregister_switchdev_notifier);
27503bf0c28SJiri Pirko 
27603bf0c28SJiri Pirko /**
277ebb9a03aSJiri Pirko  *	call_switchdev_notifiers - Call notifiers
27803bf0c28SJiri Pirko  *	@val: value passed unmodified to notifier function
27903bf0c28SJiri Pirko  *	@dev: port device
28003bf0c28SJiri Pirko  *	@info: notifier information data
28103bf0c28SJiri Pirko  *
28203bf0c28SJiri Pirko  *	Call all network notifier blocks. This should be called by driver
28303bf0c28SJiri Pirko  *	when it needs to propagate hardware event.
28403bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_call_chain().
28503bf0c28SJiri Pirko  */
286ebb9a03aSJiri Pirko int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
287ebb9a03aSJiri Pirko 			     struct switchdev_notifier_info *info)
28803bf0c28SJiri Pirko {
28903bf0c28SJiri Pirko 	int err;
29003bf0c28SJiri Pirko 
29103bf0c28SJiri Pirko 	info->dev = dev;
292ebb9a03aSJiri Pirko 	mutex_lock(&switchdev_mutex);
293ebb9a03aSJiri Pirko 	err = raw_notifier_call_chain(&switchdev_notif_chain, val, info);
294ebb9a03aSJiri Pirko 	mutex_unlock(&switchdev_mutex);
29503bf0c28SJiri Pirko 	return err;
29603bf0c28SJiri Pirko }
297ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
2988a44dbb2SRoopa Prabhu 
2998a44dbb2SRoopa Prabhu /**
300ebb9a03aSJiri Pirko  *	switchdev_port_bridge_setlink - Notify switch device port of bridge
3018a44dbb2SRoopa Prabhu  *	port attributes
3028a44dbb2SRoopa Prabhu  *
3038a44dbb2SRoopa Prabhu  *	@dev: port device
3048a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
3058a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
3068a44dbb2SRoopa Prabhu  *
3078a44dbb2SRoopa Prabhu  *	Notify switch device port of bridge port attributes
3088a44dbb2SRoopa Prabhu  */
309ebb9a03aSJiri Pirko int switchdev_port_bridge_setlink(struct net_device *dev,
3108a44dbb2SRoopa Prabhu 				  struct nlmsghdr *nlh, u16 flags)
3118a44dbb2SRoopa Prabhu {
3128a44dbb2SRoopa Prabhu 	const struct net_device_ops *ops = dev->netdev_ops;
3138a44dbb2SRoopa Prabhu 
3148a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
3158a44dbb2SRoopa Prabhu 		return 0;
3168a44dbb2SRoopa Prabhu 
3178a44dbb2SRoopa Prabhu 	if (!ops->ndo_bridge_setlink)
3188a44dbb2SRoopa Prabhu 		return -EOPNOTSUPP;
3198a44dbb2SRoopa Prabhu 
3208a44dbb2SRoopa Prabhu 	return ops->ndo_bridge_setlink(dev, nlh, flags);
3218a44dbb2SRoopa Prabhu }
322ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink);
3238a44dbb2SRoopa Prabhu 
3248a44dbb2SRoopa Prabhu /**
325ebb9a03aSJiri Pirko  *	switchdev_port_bridge_dellink - Notify switch device port of bridge
3268a44dbb2SRoopa Prabhu  *	port attribute delete
3278a44dbb2SRoopa Prabhu  *
3288a44dbb2SRoopa Prabhu  *	@dev: port device
3298a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
3308a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
3318a44dbb2SRoopa Prabhu  *
3328a44dbb2SRoopa Prabhu  *	Notify switch device port of bridge port attribute delete
3338a44dbb2SRoopa Prabhu  */
334ebb9a03aSJiri Pirko int switchdev_port_bridge_dellink(struct net_device *dev,
3358a44dbb2SRoopa Prabhu 				  struct nlmsghdr *nlh, u16 flags)
3368a44dbb2SRoopa Prabhu {
3378a44dbb2SRoopa Prabhu 	const struct net_device_ops *ops = dev->netdev_ops;
3388a44dbb2SRoopa Prabhu 
3398a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
3408a44dbb2SRoopa Prabhu 		return 0;
3418a44dbb2SRoopa Prabhu 
3428a44dbb2SRoopa Prabhu 	if (!ops->ndo_bridge_dellink)
3438a44dbb2SRoopa Prabhu 		return -EOPNOTSUPP;
3448a44dbb2SRoopa Prabhu 
3458a44dbb2SRoopa Prabhu 	return ops->ndo_bridge_dellink(dev, nlh, flags);
3468a44dbb2SRoopa Prabhu }
347ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_port_bridge_dellink);
3488a44dbb2SRoopa Prabhu 
3498a44dbb2SRoopa Prabhu /**
350ebb9a03aSJiri Pirko  *	ndo_dflt_switchdev_port_bridge_setlink - default ndo bridge setlink
3518a44dbb2SRoopa Prabhu  *						 op for master devices
3528a44dbb2SRoopa Prabhu  *
3538a44dbb2SRoopa Prabhu  *	@dev: port device
3548a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
3558a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
3568a44dbb2SRoopa Prabhu  *
3578a44dbb2SRoopa Prabhu  *	Notify master device slaves of bridge port attributes
3588a44dbb2SRoopa Prabhu  */
359ebb9a03aSJiri Pirko int ndo_dflt_switchdev_port_bridge_setlink(struct net_device *dev,
3608a44dbb2SRoopa Prabhu 					   struct nlmsghdr *nlh, u16 flags)
3618a44dbb2SRoopa Prabhu {
3628a44dbb2SRoopa Prabhu 	struct net_device *lower_dev;
3638a44dbb2SRoopa Prabhu 	struct list_head *iter;
3648a44dbb2SRoopa Prabhu 	int ret = 0, err = 0;
3658a44dbb2SRoopa Prabhu 
3668a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
3678a44dbb2SRoopa Prabhu 		return ret;
3688a44dbb2SRoopa Prabhu 
3698a44dbb2SRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
370ebb9a03aSJiri Pirko 		err = switchdev_port_bridge_setlink(lower_dev, nlh, flags);
3718a44dbb2SRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
3728a44dbb2SRoopa Prabhu 			ret = err;
3738a44dbb2SRoopa Prabhu 	}
3748a44dbb2SRoopa Prabhu 
3758a44dbb2SRoopa Prabhu 	return ret;
3768a44dbb2SRoopa Prabhu }
377ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(ndo_dflt_switchdev_port_bridge_setlink);
3788a44dbb2SRoopa Prabhu 
3798a44dbb2SRoopa Prabhu /**
380ebb9a03aSJiri Pirko  *	ndo_dflt_switchdev_port_bridge_dellink - default ndo bridge dellink
3818a44dbb2SRoopa Prabhu  *						 op for master devices
3828a44dbb2SRoopa Prabhu  *
3838a44dbb2SRoopa Prabhu  *	@dev: port device
3848a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
3858a44dbb2SRoopa Prabhu  *	@flags: bridge dellink flags
3868a44dbb2SRoopa Prabhu  *
3878a44dbb2SRoopa Prabhu  *	Notify master device slaves of bridge port attribute deletes
3888a44dbb2SRoopa Prabhu  */
389ebb9a03aSJiri Pirko int ndo_dflt_switchdev_port_bridge_dellink(struct net_device *dev,
3908a44dbb2SRoopa Prabhu 					   struct nlmsghdr *nlh, u16 flags)
3918a44dbb2SRoopa Prabhu {
3928a44dbb2SRoopa Prabhu 	struct net_device *lower_dev;
3938a44dbb2SRoopa Prabhu 	struct list_head *iter;
3948a44dbb2SRoopa Prabhu 	int ret = 0, err = 0;
3958a44dbb2SRoopa Prabhu 
3968a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
3978a44dbb2SRoopa Prabhu 		return ret;
3988a44dbb2SRoopa Prabhu 
3998a44dbb2SRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
400ebb9a03aSJiri Pirko 		err = switchdev_port_bridge_dellink(lower_dev, nlh, flags);
4018a44dbb2SRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
4028a44dbb2SRoopa Prabhu 			ret = err;
4038a44dbb2SRoopa Prabhu 	}
4048a44dbb2SRoopa Prabhu 
4058a44dbb2SRoopa Prabhu 	return ret;
4068a44dbb2SRoopa Prabhu }
407ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(ndo_dflt_switchdev_port_bridge_dellink);
4085e8d9049SScott Feldman 
409ebb9a03aSJiri Pirko static struct net_device *switchdev_get_lowest_dev(struct net_device *dev)
410b5d6fbdeSScott Feldman {
4119d47c0a2SJiri Pirko 	const struct switchdev_ops *ops = dev->switchdev_ops;
412b5d6fbdeSScott Feldman 	struct net_device *lower_dev;
413b5d6fbdeSScott Feldman 	struct net_device *port_dev;
414b5d6fbdeSScott Feldman 	struct list_head *iter;
415b5d6fbdeSScott Feldman 
416b5d6fbdeSScott Feldman 	/* Recusively search down until we find a sw port dev.
4179d47c0a2SJiri Pirko 	 * (A sw port dev supports switchdev_parent_id_get).
418b5d6fbdeSScott Feldman 	 */
419b5d6fbdeSScott Feldman 
420b5d6fbdeSScott Feldman 	if (dev->features & NETIF_F_HW_SWITCH_OFFLOAD &&
4219d47c0a2SJiri Pirko 	    ops && ops->switchdev_parent_id_get)
422b5d6fbdeSScott Feldman 		return dev;
423b5d6fbdeSScott Feldman 
424b5d6fbdeSScott Feldman 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
425ebb9a03aSJiri Pirko 		port_dev = switchdev_get_lowest_dev(lower_dev);
426b5d6fbdeSScott Feldman 		if (port_dev)
427b5d6fbdeSScott Feldman 			return port_dev;
428b5d6fbdeSScott Feldman 	}
429b5d6fbdeSScott Feldman 
430b5d6fbdeSScott Feldman 	return NULL;
431b5d6fbdeSScott Feldman }
432b5d6fbdeSScott Feldman 
433ebb9a03aSJiri Pirko static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi)
434b5d6fbdeSScott Feldman {
435b5d6fbdeSScott Feldman 	struct netdev_phys_item_id psid;
436b5d6fbdeSScott Feldman 	struct netdev_phys_item_id prev_psid;
437b5d6fbdeSScott Feldman 	struct net_device *dev = NULL;
438b5d6fbdeSScott Feldman 	int nhsel;
439b5d6fbdeSScott Feldman 
440b5d6fbdeSScott Feldman 	/* For this route, all nexthop devs must be on the same switch. */
441b5d6fbdeSScott Feldman 
442b5d6fbdeSScott Feldman 	for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
443b5d6fbdeSScott Feldman 		const struct fib_nh *nh = &fi->fib_nh[nhsel];
444b5d6fbdeSScott Feldman 
445b5d6fbdeSScott Feldman 		if (!nh->nh_dev)
446b5d6fbdeSScott Feldman 			return NULL;
447b5d6fbdeSScott Feldman 
448ebb9a03aSJiri Pirko 		dev = switchdev_get_lowest_dev(nh->nh_dev);
449b5d6fbdeSScott Feldman 		if (!dev)
450b5d6fbdeSScott Feldman 			return NULL;
451b5d6fbdeSScott Feldman 
452ebb9a03aSJiri Pirko 		if (switchdev_parent_id_get(dev, &psid))
453b5d6fbdeSScott Feldman 			return NULL;
454b5d6fbdeSScott Feldman 
455b5d6fbdeSScott Feldman 		if (nhsel > 0) {
456b5d6fbdeSScott Feldman 			if (prev_psid.id_len != psid.id_len)
457b5d6fbdeSScott Feldman 				return NULL;
458b5d6fbdeSScott Feldman 			if (memcmp(prev_psid.id, psid.id, psid.id_len))
459b5d6fbdeSScott Feldman 				return NULL;
460b5d6fbdeSScott Feldman 		}
461b5d6fbdeSScott Feldman 
462b5d6fbdeSScott Feldman 		prev_psid = psid;
463b5d6fbdeSScott Feldman 	}
464b5d6fbdeSScott Feldman 
465b5d6fbdeSScott Feldman 	return dev;
466b5d6fbdeSScott Feldman }
467b5d6fbdeSScott Feldman 
4685e8d9049SScott Feldman /**
469ebb9a03aSJiri Pirko  *	switchdev_fib_ipv4_add - Add IPv4 route entry to switch
4705e8d9049SScott Feldman  *
4715e8d9049SScott Feldman  *	@dst: route's IPv4 destination address
4725e8d9049SScott Feldman  *	@dst_len: destination address length (prefix length)
4735e8d9049SScott Feldman  *	@fi: route FIB info structure
4745e8d9049SScott Feldman  *	@tos: route TOS
4755e8d9049SScott Feldman  *	@type: route type
476f8f21471SScott Feldman  *	@nlflags: netlink flags passed in (NLM_F_*)
4775e8d9049SScott Feldman  *	@tb_id: route table ID
4785e8d9049SScott Feldman  *
4795e8d9049SScott Feldman  *	Add IPv4 route entry to switch device.
4805e8d9049SScott Feldman  */
481ebb9a03aSJiri Pirko int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
482f8f21471SScott Feldman 			   u8 tos, u8 type, u32 nlflags, u32 tb_id)
4835e8d9049SScott Feldman {
484b5d6fbdeSScott Feldman 	struct net_device *dev;
4859d47c0a2SJiri Pirko 	const struct switchdev_ops *ops;
486b5d6fbdeSScott Feldman 	int err = 0;
487b5d6fbdeSScott Feldman 
4888e05fd71SScott Feldman 	/* Don't offload route if using custom ip rules or if
4898e05fd71SScott Feldman 	 * IPv4 FIB offloading has been disabled completely.
4908e05fd71SScott Feldman 	 */
4918e05fd71SScott Feldman 
492e1315db1SScott Feldman #ifdef CONFIG_IP_MULTIPLE_TABLES
493e1315db1SScott Feldman 	if (fi->fib_net->ipv4.fib_has_custom_rules)
494e1315db1SScott Feldman 		return 0;
495e1315db1SScott Feldman #endif
496e1315db1SScott Feldman 
497e1315db1SScott Feldman 	if (fi->fib_net->ipv4.fib_offload_disabled)
498104616e7SScott Feldman 		return 0;
499104616e7SScott Feldman 
500ebb9a03aSJiri Pirko 	dev = switchdev_get_dev_by_nhs(fi);
501b5d6fbdeSScott Feldman 	if (!dev)
5025e8d9049SScott Feldman 		return 0;
5039d47c0a2SJiri Pirko 	ops = dev->switchdev_ops;
504b5d6fbdeSScott Feldman 
5059d47c0a2SJiri Pirko 	if (ops->switchdev_fib_ipv4_add) {
5069d47c0a2SJiri Pirko 		err = ops->switchdev_fib_ipv4_add(dev, htonl(dst), dst_len,
507f8f21471SScott Feldman 						  fi, tos, type, nlflags,
508f8f21471SScott Feldman 						  tb_id);
509b5d6fbdeSScott Feldman 		if (!err)
510b5d6fbdeSScott Feldman 			fi->fib_flags |= RTNH_F_EXTERNAL;
511b5d6fbdeSScott Feldman 	}
512b5d6fbdeSScott Feldman 
513b5d6fbdeSScott Feldman 	return err;
5145e8d9049SScott Feldman }
515ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_add);
5165e8d9049SScott Feldman 
5175e8d9049SScott Feldman /**
518ebb9a03aSJiri Pirko  *	switchdev_fib_ipv4_del - Delete IPv4 route entry from switch
5195e8d9049SScott Feldman  *
5205e8d9049SScott Feldman  *	@dst: route's IPv4 destination address
5215e8d9049SScott Feldman  *	@dst_len: destination address length (prefix length)
5225e8d9049SScott Feldman  *	@fi: route FIB info structure
5235e8d9049SScott Feldman  *	@tos: route TOS
5245e8d9049SScott Feldman  *	@type: route type
5255e8d9049SScott Feldman  *	@tb_id: route table ID
5265e8d9049SScott Feldman  *
5275e8d9049SScott Feldman  *	Delete IPv4 route entry from switch device.
5285e8d9049SScott Feldman  */
529ebb9a03aSJiri Pirko int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
5305e8d9049SScott Feldman 			   u8 tos, u8 type, u32 tb_id)
5315e8d9049SScott Feldman {
532b5d6fbdeSScott Feldman 	struct net_device *dev;
5339d47c0a2SJiri Pirko 	const struct switchdev_ops *ops;
534b5d6fbdeSScott Feldman 	int err = 0;
535b5d6fbdeSScott Feldman 
536b5d6fbdeSScott Feldman 	if (!(fi->fib_flags & RTNH_F_EXTERNAL))
5375e8d9049SScott Feldman 		return 0;
538b5d6fbdeSScott Feldman 
539ebb9a03aSJiri Pirko 	dev = switchdev_get_dev_by_nhs(fi);
540b5d6fbdeSScott Feldman 	if (!dev)
541b5d6fbdeSScott Feldman 		return 0;
5429d47c0a2SJiri Pirko 	ops = dev->switchdev_ops;
543b5d6fbdeSScott Feldman 
5449d47c0a2SJiri Pirko 	if (ops->switchdev_fib_ipv4_del) {
5459d47c0a2SJiri Pirko 		err = ops->switchdev_fib_ipv4_del(dev, htonl(dst), dst_len,
546b5d6fbdeSScott Feldman 						  fi, tos, type, tb_id);
547b5d6fbdeSScott Feldman 		if (!err)
548b5d6fbdeSScott Feldman 			fi->fib_flags &= ~RTNH_F_EXTERNAL;
549b5d6fbdeSScott Feldman 	}
550b5d6fbdeSScott Feldman 
551b5d6fbdeSScott Feldman 	return err;
5525e8d9049SScott Feldman }
553ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_del);
5548e05fd71SScott Feldman 
5558e05fd71SScott Feldman /**
556ebb9a03aSJiri Pirko  *	switchdev_fib_ipv4_abort - Abort an IPv4 FIB operation
5578e05fd71SScott Feldman  *
5588e05fd71SScott Feldman  *	@fi: route FIB info structure
5598e05fd71SScott Feldman  */
560ebb9a03aSJiri Pirko void switchdev_fib_ipv4_abort(struct fib_info *fi)
5618e05fd71SScott Feldman {
5628e05fd71SScott Feldman 	/* There was a problem installing this route to the offload
5638e05fd71SScott Feldman 	 * device.  For now, until we come up with more refined
5648e05fd71SScott Feldman 	 * policy handling, abruptly end IPv4 fib offloading for
5658e05fd71SScott Feldman 	 * for entire net by flushing offload device(s) of all
5668e05fd71SScott Feldman 	 * IPv4 routes, and mark IPv4 fib offloading broken from
5678e05fd71SScott Feldman 	 * this point forward.
5688e05fd71SScott Feldman 	 */
5698e05fd71SScott Feldman 
5708e05fd71SScott Feldman 	fib_flush_external(fi->fib_net);
5718e05fd71SScott Feldman 	fi->fib_net->ipv4.fib_offload_disabled = true;
5728e05fd71SScott Feldman }
573ebb9a03aSJiri Pirko EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_abort);
574