diff options
Diffstat (limited to 'net/switchdev/switchdev.c')
-rw-r--r-- | net/switchdev/switchdev.c | 108 |
1 files changed, 43 insertions, 65 deletions
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 23b4e5b347dc..007b8f40df06 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c | |||
@@ -250,75 +250,12 @@ done: | |||
250 | return err; | 250 | return err; |
251 | } | 251 | } |
252 | 252 | ||
253 | struct switchdev_attr_set_work { | 253 | static int switchdev_port_attr_set_now(struct net_device *dev, |
254 | struct work_struct work; | 254 | const struct switchdev_attr *attr) |
255 | struct net_device *dev; | ||
256 | struct switchdev_attr attr; | ||
257 | }; | ||
258 | |||
259 | static void switchdev_port_attr_set_work(struct work_struct *work) | ||
260 | { | ||
261 | struct switchdev_attr_set_work *asw = | ||
262 | container_of(work, struct switchdev_attr_set_work, work); | ||
263 | int err; | ||
264 | |||
265 | rtnl_lock(); | ||
266 | err = switchdev_port_attr_set(asw->dev, &asw->attr); | ||
267 | if (err && err != -EOPNOTSUPP) | ||
268 | netdev_err(asw->dev, "failed (err=%d) to set attribute (id=%d)\n", | ||
269 | err, asw->attr.id); | ||
270 | rtnl_unlock(); | ||
271 | |||
272 | dev_put(asw->dev); | ||
273 | kfree(work); | ||
274 | } | ||
275 | |||
276 | static int switchdev_port_attr_set_defer(struct net_device *dev, | ||
277 | const struct switchdev_attr *attr) | ||
278 | { | ||
279 | struct switchdev_attr_set_work *asw; | ||
280 | |||
281 | asw = kmalloc(sizeof(*asw), GFP_ATOMIC); | ||
282 | if (!asw) | ||
283 | return -ENOMEM; | ||
284 | |||
285 | INIT_WORK(&asw->work, switchdev_port_attr_set_work); | ||
286 | |||
287 | dev_hold(dev); | ||
288 | asw->dev = dev; | ||
289 | memcpy(&asw->attr, attr, sizeof(asw->attr)); | ||
290 | |||
291 | schedule_work(&asw->work); | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | /** | ||
297 | * switchdev_port_attr_set - Set port attribute | ||
298 | * | ||
299 | * @dev: port device | ||
300 | * @attr: attribute to set | ||
301 | * | ||
302 | * Use a 2-phase prepare-commit transaction model to ensure | ||
303 | * system is not left in a partially updated state due to | ||
304 | * failure from driver/device. | ||
305 | */ | ||
306 | int switchdev_port_attr_set(struct net_device *dev, | ||
307 | const struct switchdev_attr *attr) | ||
308 | { | 255 | { |
309 | struct switchdev_trans trans; | 256 | struct switchdev_trans trans; |
310 | int err; | 257 | int err; |
311 | 258 | ||
312 | if (!rtnl_is_locked()) { | ||
313 | /* Running prepare-commit transaction across stacked | ||
314 | * devices requires nothing moves, so if rtnl_lock is | ||
315 | * not held, schedule a worker thread to hold rtnl_lock | ||
316 | * while setting attr. | ||
317 | */ | ||
318 | |||
319 | return switchdev_port_attr_set_defer(dev, attr); | ||
320 | } | ||
321 | |||
322 | switchdev_trans_init(&trans); | 259 | switchdev_trans_init(&trans); |
323 | 260 | ||
324 | /* Phase I: prepare for attr set. Driver/device should fail | 261 | /* Phase I: prepare for attr set. Driver/device should fail |
@@ -355,6 +292,47 @@ int switchdev_port_attr_set(struct net_device *dev, | |||
355 | 292 | ||
356 | return err; | 293 | return err; |
357 | } | 294 | } |
295 | |||
296 | static void switchdev_port_attr_set_deferred(struct net_device *dev, | ||
297 | const void *data) | ||
298 | { | ||
299 | const struct switchdev_attr *attr = data; | ||
300 | int err; | ||
301 | |||
302 | err = switchdev_port_attr_set_now(dev, attr); | ||
303 | if (err && err != -EOPNOTSUPP) | ||
304 | netdev_err(dev, "failed (err=%d) to set attribute (id=%d)\n", | ||
305 | err, attr->id); | ||
306 | } | ||
307 | |||
308 | static int switchdev_port_attr_set_defer(struct net_device *dev, | ||
309 | const struct switchdev_attr *attr) | ||
310 | { | ||
311 | return switchdev_deferred_enqueue(dev, attr, sizeof(*attr), | ||
312 | switchdev_port_attr_set_deferred); | ||
313 | } | ||
314 | |||
315 | /** | ||
316 | * switchdev_port_attr_set - Set port attribute | ||
317 | * | ||
318 | * @dev: port device | ||
319 | * @attr: attribute to set | ||
320 | * | ||
321 | * Use a 2-phase prepare-commit transaction model to ensure | ||
322 | * system is not left in a partially updated state due to | ||
323 | * failure from driver/device. | ||
324 | * | ||
325 | * rtnl_lock must be held and must not be in atomic section, | ||
326 | * in case SWITCHDEV_F_DEFER flag is not set. | ||
327 | */ | ||
328 | int switchdev_port_attr_set(struct net_device *dev, | ||
329 | const struct switchdev_attr *attr) | ||
330 | { | ||
331 | if (attr->flags & SWITCHDEV_F_DEFER) | ||
332 | return switchdev_port_attr_set_defer(dev, attr); | ||
333 | ASSERT_RTNL(); | ||
334 | return switchdev_port_attr_set_now(dev, attr); | ||
335 | } | ||
358 | EXPORT_SYMBOL_GPL(switchdev_port_attr_set); | 336 | EXPORT_SYMBOL_GPL(switchdev_port_attr_set); |
359 | 337 | ||
360 | static int __switchdev_port_obj_add(struct net_device *dev, | 338 | static int __switchdev_port_obj_add(struct net_device *dev, |