switchdev: Add helpers to aid traversal through lower devices
After the transition from switchdev operations to notifier chain (which will take place in following patches), the onus is on the driver to find its own devices below possible layer of LAG or other uppers. The logic to do so is fairly repetitive: each driver is looking for its own devices among the lowers of the notified device. For those that it finds, it calls a handler. To indicate that the event was handled, struct switchdev_notifier_port_obj_info.handled is set. The differences lie only in what constitutes an "own" device and what handler to call. Therefore abstract this logic into two helpers, switchdev_handle_port_obj_add() and switchdev_handle_port_obj_del(). If a driver only supports physical ports under a bridge device, it will simply avoid this layer of indirection. One area where this helper diverges from the current switchdev behavior is the case of mixed lowers, some of which are switchdev ports and some of which are not. Previously, such scenario would fail with -EOPNOTSUPP. The helper could do that for lowers for which the passed-in predicate doesn't hold. That would however break the case that switchdev ports from several different drivers are stashed under one master, a scenario that switchdev currently happily supports. Therefore tolerate any and all unknown netdevices, whether they are backed by a switchdev driver or not. Signed-off-by: Petr Machata <petrm@mellanox.com> Reviewed-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
a39b888863
Коммит
f30f0601eb
|
@ -210,6 +210,18 @@ void switchdev_port_fwd_mark_set(struct net_device *dev,
|
|||
bool switchdev_port_same_parent_id(struct net_device *a,
|
||||
struct net_device *b);
|
||||
|
||||
int switchdev_handle_port_obj_add(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*add_cb)(struct net_device *dev,
|
||||
const struct switchdev_obj *obj,
|
||||
struct switchdev_trans *trans));
|
||||
int switchdev_handle_port_obj_del(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*del_cb)(struct net_device *dev,
|
||||
const struct switchdev_obj *obj));
|
||||
|
||||
#define SWITCHDEV_SET_OPS(netdev, ops) ((netdev)->switchdev_ops = (ops))
|
||||
#else
|
||||
|
||||
|
@ -284,6 +296,27 @@ static inline bool switchdev_port_same_parent_id(struct net_device *a,
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline int
|
||||
switchdev_handle_port_obj_add(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*add_cb)(struct net_device *dev,
|
||||
const struct switchdev_obj *obj,
|
||||
struct switchdev_trans *trans))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
switchdev_handle_port_obj_del(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*del_cb)(struct net_device *dev,
|
||||
const struct switchdev_obj *obj))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SWITCHDEV_SET_OPS(netdev, ops) do {} while (0)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -621,3 +621,103 @@ bool switchdev_port_same_parent_id(struct net_device *a,
|
|||
return netdev_phys_item_id_same(&a_attr.u.ppid, &b_attr.u.ppid);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id);
|
||||
|
||||
static int __switchdev_handle_port_obj_add(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*add_cb)(struct net_device *dev,
|
||||
const struct switchdev_obj *obj,
|
||||
struct switchdev_trans *trans))
|
||||
{
|
||||
struct net_device *lower_dev;
|
||||
struct list_head *iter;
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
if (check_cb(dev)) {
|
||||
/* This flag is only checked if the return value is success. */
|
||||
port_obj_info->handled = true;
|
||||
return add_cb(dev, port_obj_info->obj, port_obj_info->trans);
|
||||
}
|
||||
|
||||
/* Switch ports might be stacked under e.g. a LAG. Ignore the
|
||||
* unsupported devices, another driver might be able to handle them. But
|
||||
* propagate to the callers any hard errors.
|
||||
*
|
||||
* If the driver does its own bookkeeping of stacked ports, it's not
|
||||
* necessary to go through this helper.
|
||||
*/
|
||||
netdev_for_each_lower_dev(dev, lower_dev, iter) {
|
||||
err = __switchdev_handle_port_obj_add(lower_dev, port_obj_info,
|
||||
check_cb, add_cb);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int switchdev_handle_port_obj_add(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*add_cb)(struct net_device *dev,
|
||||
const struct switchdev_obj *obj,
|
||||
struct switchdev_trans *trans))
|
||||
{
|
||||
int err;
|
||||
|
||||
err = __switchdev_handle_port_obj_add(dev, port_obj_info, check_cb,
|
||||
add_cb);
|
||||
if (err == -EOPNOTSUPP)
|
||||
err = 0;
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_add);
|
||||
|
||||
static int __switchdev_handle_port_obj_del(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*del_cb)(struct net_device *dev,
|
||||
const struct switchdev_obj *obj))
|
||||
{
|
||||
struct net_device *lower_dev;
|
||||
struct list_head *iter;
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
if (check_cb(dev)) {
|
||||
/* This flag is only checked if the return value is success. */
|
||||
port_obj_info->handled = true;
|
||||
return del_cb(dev, port_obj_info->obj);
|
||||
}
|
||||
|
||||
/* Switch ports might be stacked under e.g. a LAG. Ignore the
|
||||
* unsupported devices, another driver might be able to handle them. But
|
||||
* propagate to the callers any hard errors.
|
||||
*
|
||||
* If the driver does its own bookkeeping of stacked ports, it's not
|
||||
* necessary to go through this helper.
|
||||
*/
|
||||
netdev_for_each_lower_dev(dev, lower_dev, iter) {
|
||||
err = __switchdev_handle_port_obj_del(lower_dev, port_obj_info,
|
||||
check_cb, del_cb);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int switchdev_handle_port_obj_del(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
int (*del_cb)(struct net_device *dev,
|
||||
const struct switchdev_obj *obj))
|
||||
{
|
||||
int err;
|
||||
|
||||
err = __switchdev_handle_port_obj_del(dev, port_obj_info, check_cb,
|
||||
del_cb);
|
||||
if (err == -EOPNOTSUPP)
|
||||
err = 0;
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(switchdev_handle_port_obj_del);
|
||||
|
|
Загрузка…
Ссылка в новой задаче