switchdev: Add helpers to aid traversal through lower devices
authorPetr Machata <petrm@mellanox.com>
Thu, 22 Nov 2018 23:29:44 +0000 (23:29 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sat, 24 Nov 2018 02:02:24 +0000 (18:02 -0800)
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>
include/net/switchdev.h
net/switchdev/switchdev.c

index a2f3ebf39301adb18d92fda942cf7f7a8c75322f..6dc7de5761670ba79d831cbe1a2b640a1eaefc6d 100644 (file)
@@ -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
index e109bb97ce3fb6c712b6f822dc903c5f40fc5999..099434ec79963461279f73f8b929330e6a14399a 100644 (file)
@@ -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);