Commit 4d429c5d authored by Jiri Pirko's avatar Jiri Pirko Committed by David S. Miller

switchdev: introduce possibility to defer obj_add/del

Similar to the attr usecase, the caller knows if he is holding RTNL and is
in atomic section. So let the called to decide the correct call variant.

This allows drivers to sleep inside their ops and wait for hw to get the
operation status. Then the status is propagated into switchdev core.
This avoids silent errors in drivers.
Signed-off-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 850d0cbc
...@@ -69,6 +69,7 @@ enum switchdev_obj_id { ...@@ -69,6 +69,7 @@ enum switchdev_obj_id {
struct switchdev_obj { struct switchdev_obj {
enum switchdev_obj_id id; enum switchdev_obj_id id;
u32 flags;
}; };
/* SWITCHDEV_OBJ_ID_PORT_VLAN */ /* SWITCHDEV_OBJ_ID_PORT_VLAN */
......
...@@ -362,20 +362,7 @@ static int __switchdev_port_obj_add(struct net_device *dev, ...@@ -362,20 +362,7 @@ static int __switchdev_port_obj_add(struct net_device *dev,
return err; return err;
} }
/** static int switchdev_port_obj_add_now(struct net_device *dev,
* switchdev_port_obj_add - Add port object
*
* @dev: port device
* @id: object ID
* @obj: object to add
*
* Use a 2-phase prepare-commit transaction model to ensure
* system is not left in a partially updated state due to
* failure from driver/device.
*
* rtnl_lock must be held.
*/
int switchdev_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj) const struct switchdev_obj *obj)
{ {
struct switchdev_trans trans; struct switchdev_trans trans;
...@@ -418,16 +405,51 @@ int switchdev_port_obj_add(struct net_device *dev, ...@@ -418,16 +405,51 @@ int switchdev_port_obj_add(struct net_device *dev,
return err; return err;
} }
EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
static void switchdev_port_obj_add_deferred(struct net_device *dev,
const void *data)
{
const struct switchdev_obj *obj = data;
int err;
err = switchdev_port_obj_add_now(dev, obj);
if (err && err != -EOPNOTSUPP)
netdev_err(dev, "failed (err=%d) to add object (id=%d)\n",
err, obj->id);
}
static int switchdev_port_obj_add_defer(struct net_device *dev,
const struct switchdev_obj *obj)
{
return switchdev_deferred_enqueue(dev, obj, sizeof(*obj),
switchdev_port_obj_add_deferred);
}
/** /**
* switchdev_port_obj_del - Delete port object * switchdev_port_obj_add - Add port object
* *
* @dev: port device * @dev: port device
* @id: object ID * @id: object ID
* @obj: object to delete * @obj: object to add
*
* Use a 2-phase prepare-commit transaction model to ensure
* system is not left in a partially updated state due to
* failure from driver/device.
*
* rtnl_lock must be held and must not be in atomic section,
* in case SWITCHDEV_F_DEFER flag is not set.
*/ */
int switchdev_port_obj_del(struct net_device *dev, int switchdev_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj)
{
if (obj->flags & SWITCHDEV_F_DEFER)
return switchdev_port_obj_add_defer(dev, obj);
ASSERT_RTNL();
return switchdev_port_obj_add_now(dev, obj);
}
EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
static int switchdev_port_obj_del_now(struct net_device *dev,
const struct switchdev_obj *obj) const struct switchdev_obj *obj)
{ {
const struct switchdev_ops *ops = dev->switchdev_ops; const struct switchdev_ops *ops = dev->switchdev_ops;
...@@ -444,13 +466,51 @@ int switchdev_port_obj_del(struct net_device *dev, ...@@ -444,13 +466,51 @@ int switchdev_port_obj_del(struct net_device *dev,
*/ */
netdev_for_each_lower_dev(dev, lower_dev, iter) { netdev_for_each_lower_dev(dev, lower_dev, iter) {
err = switchdev_port_obj_del(lower_dev, obj); err = switchdev_port_obj_del_now(lower_dev, obj);
if (err) if (err)
break; break;
} }
return err; return err;
} }
static void switchdev_port_obj_del_deferred(struct net_device *dev,
const void *data)
{
const struct switchdev_obj *obj = data;
int err;
err = switchdev_port_obj_del_now(dev, obj);
if (err && err != -EOPNOTSUPP)
netdev_err(dev, "failed (err=%d) to del object (id=%d)\n",
err, obj->id);
}
static int switchdev_port_obj_del_defer(struct net_device *dev,
const struct switchdev_obj *obj)
{
return switchdev_deferred_enqueue(dev, obj, sizeof(*obj),
switchdev_port_obj_del_deferred);
}
/**
* switchdev_port_obj_del - Delete port object
*
* @dev: port device
* @id: object ID
* @obj: object to delete
*
* rtnl_lock must be held and must not be in atomic section,
* in case SWITCHDEV_F_DEFER flag is not set.
*/
int switchdev_port_obj_del(struct net_device *dev,
const struct switchdev_obj *obj)
{
if (obj->flags & SWITCHDEV_F_DEFER)
return switchdev_port_obj_del_defer(dev, obj);
ASSERT_RTNL();
return switchdev_port_obj_del_now(dev, obj);
}
EXPORT_SYMBOL_GPL(switchdev_port_obj_del); EXPORT_SYMBOL_GPL(switchdev_port_obj_del);
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment