diff options
Diffstat (limited to 'net/switchdev/switchdev.c')
-rw-r--r-- | net/switchdev/switchdev.c | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index a3c359004902..3d4d99a70b80 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c | |||
@@ -187,6 +187,113 @@ int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr) | |||
187 | } | 187 | } |
188 | EXPORT_SYMBOL_GPL(switchdev_port_attr_set); | 188 | EXPORT_SYMBOL_GPL(switchdev_port_attr_set); |
189 | 189 | ||
190 | int __switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj) | ||
191 | { | ||
192 | const struct switchdev_ops *ops = dev->switchdev_ops; | ||
193 | struct net_device *lower_dev; | ||
194 | struct list_head *iter; | ||
195 | int err = -EOPNOTSUPP; | ||
196 | |||
197 | if (ops && ops->switchdev_port_obj_add) | ||
198 | return ops->switchdev_port_obj_add(dev, obj); | ||
199 | |||
200 | /* Switch device port(s) may be stacked under | ||
201 | * bond/team/vlan dev, so recurse down to add object on | ||
202 | * each port. | ||
203 | */ | ||
204 | |||
205 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | ||
206 | err = __switchdev_port_obj_add(lower_dev, obj); | ||
207 | if (err) | ||
208 | break; | ||
209 | } | ||
210 | |||
211 | return err; | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * switchdev_port_obj_add - Add port object | ||
216 | * | ||
217 | * @dev: port device | ||
218 | * @obj: object to add | ||
219 | * | ||
220 | * Use a 2-phase prepare-commit transaction model to ensure | ||
221 | * system is not left in a partially updated state due to | ||
222 | * failure from driver/device. | ||
223 | * | ||
224 | * rtnl_lock must be held. | ||
225 | */ | ||
226 | int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj) | ||
227 | { | ||
228 | int err; | ||
229 | |||
230 | ASSERT_RTNL(); | ||
231 | |||
232 | /* Phase I: prepare for obj add. Driver/device should fail | ||
233 | * here if there are going to be issues in the commit phase, | ||
234 | * such as lack of resources or support. The driver/device | ||
235 | * should reserve resources needed for the commit phase here, | ||
236 | * but should not commit the obj. | ||
237 | */ | ||
238 | |||
239 | obj->trans = SWITCHDEV_TRANS_PREPARE; | ||
240 | err = __switchdev_port_obj_add(dev, obj); | ||
241 | if (err) { | ||
242 | /* Prepare phase failed: abort the transaction. Any | ||
243 | * resources reserved in the prepare phase are | ||
244 | * released. | ||
245 | */ | ||
246 | |||
247 | obj->trans = SWITCHDEV_TRANS_ABORT; | ||
248 | __switchdev_port_obj_add(dev, obj); | ||
249 | |||
250 | return err; | ||
251 | } | ||
252 | |||
253 | /* Phase II: commit obj add. This cannot fail as a fault | ||
254 | * of driver/device. If it does, it's a bug in the driver/device | ||
255 | * because the driver said everythings was OK in phase I. | ||
256 | */ | ||
257 | |||
258 | obj->trans = SWITCHDEV_TRANS_COMMIT; | ||
259 | err = __switchdev_port_obj_add(dev, obj); | ||
260 | WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id); | ||
261 | |||
262 | return err; | ||
263 | } | ||
264 | EXPORT_SYMBOL_GPL(switchdev_port_obj_add); | ||
265 | |||
266 | /** | ||
267 | * switchdev_port_obj_del - Delete port object | ||
268 | * | ||
269 | * @dev: port device | ||
270 | * @obj: object to delete | ||
271 | */ | ||
272 | int switchdev_port_obj_del(struct net_device *dev, struct switchdev_obj *obj) | ||
273 | { | ||
274 | const struct switchdev_ops *ops = dev->switchdev_ops; | ||
275 | struct net_device *lower_dev; | ||
276 | struct list_head *iter; | ||
277 | int err = -EOPNOTSUPP; | ||
278 | |||
279 | if (ops && ops->switchdev_port_obj_del) | ||
280 | return ops->switchdev_port_obj_del(dev, obj); | ||
281 | |||
282 | /* Switch device port(s) may be stacked under | ||
283 | * bond/team/vlan dev, so recurse down to delete object on | ||
284 | * each port. | ||
285 | */ | ||
286 | |||
287 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | ||
288 | err = switchdev_port_obj_del(lower_dev, obj); | ||
289 | if (err) | ||
290 | break; | ||
291 | } | ||
292 | |||
293 | return err; | ||
294 | } | ||
295 | EXPORT_SYMBOL_GPL(switchdev_port_obj_del); | ||
296 | |||
190 | static DEFINE_MUTEX(switchdev_mutex); | 297 | static DEFINE_MUTEX(switchdev_mutex); |
191 | static RAW_NOTIFIER_HEAD(switchdev_notif_chain); | 298 | static RAW_NOTIFIER_HEAD(switchdev_notif_chain); |
192 | 299 | ||