Add switchdev_port_bridge_getlink_deferred() which does a deferred
object dump operation, this is required for e.g: DSA switches which
typically have sleeping I/O operations which is incompatible with being
in atomic context obviously.

Signed-off-by: Florian Fainelli <f.faine...@gmail.com>
---
 include/net/switchdev.h   |  3 ++
 net/switchdev/switchdev.c | 79 ++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 67 insertions(+), 15 deletions(-)

diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index eba80c4fc56f..087761b0df49 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -189,6 +189,9 @@ int call_switchdev_notifiers(unsigned long val, struct 
net_device *dev,
 int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
                                  struct net_device *dev, u32 filter_mask,
                                  int nlflags);
+int switchdev_port_bridge_getlink_deferred(struct sk_buff *skb, u32 pid,
+                                          u32 seq, struct net_device *dev,
+                                          u32 filter_mask, int nlflags);
 int switchdev_port_bridge_setlink(struct net_device *dev,
                                  struct nlmsghdr *nlh, u16 flags);
 int switchdev_port_bridge_dellink(struct net_device *dev,
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index 4fa9972d72d2..ab614a9dd872 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -776,12 +776,14 @@ static int switchdev_port_vlan_dump_cb(struct 
switchdev_obj *obj)
        return err;
 }
 
-static int switchdev_port_vlan_fill(struct sk_buff *skb, struct net_device 
*dev,
-                                   u32 filter_mask)
+static int __switchdev_port_vlan_fill(struct sk_buff *skb,
+                                     struct net_device *dev,
+                                     u32 filter_mask, u32 obj_flags)
 {
        struct switchdev_vlan_dump dump = {
                .vlan.obj.orig_dev = dev,
                .vlan.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
+               .vlan.obj.flags = obj_flags,
                .skb = skb,
                .filter_mask = filter_mask,
        };
@@ -802,17 +804,27 @@ static int switchdev_port_vlan_fill(struct sk_buff *skb, 
struct net_device *dev,
        return err == -EOPNOTSUPP ? 0 : err;
 }
 
-/**
- *     switchdev_port_bridge_getlink - Get bridge port attributes
- *
- *     @dev: port device
- *
- *     Called for SELF on rtnl_bridge_getlink to get bridge port
- *     attributes.
- */
-int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
-                                 struct net_device *dev, u32 filter_mask,
-                                 int nlflags)
+static int switchdev_port_vlan_fill_deferred(struct sk_buff *skb,
+                                            struct net_device *dev,
+                                            u32 filter_mask)
+{
+       return __switchdev_port_vlan_fill(skb, dev, filter_mask,
+                                         SWITCHDEV_F_DEFER);
+}
+
+static int switchdev_port_vlan_fill(struct sk_buff *skb,
+                                   struct net_device *dev,
+                                   u32 filter_mask)
+{
+       return __switchdev_port_vlan_fill(skb, dev, filter_mask, 0);
+}
+
+static int __switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid,
+                                          u32 seq, struct net_device *dev,
+                                          u32 filter_mask, int nlflags,
+                                          int (*fill_cb)(struct sk_buff *skb,
+                                                         struct net_device *d,
+                                                         u32 filter_mask))
 {
        struct switchdev_attr attr = {
                .orig_dev = dev,
@@ -829,12 +841,49 @@ int switchdev_port_bridge_getlink(struct sk_buff *skb, 
u32 pid, u32 seq,
        if (err && err != -EOPNOTSUPP)
                return err;
 
-       return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode,
+       return ndo_dflt_bridge_getlink(skb, pid, seq, d, mode,
                                       attr.u.brport_flags, mask, nlflags,
-                                      filter_mask, switchdev_port_vlan_fill);
+                                      filter_mask, fill_cb);
+}
+
+/**
+ *     switchdev_port_bridge_getlink - Get bridge port attributes
+ *
+ *     @dev: port device
+ *
+ *     Called for SELF on rtnl_bridge_getlink to get bridge port
+ *     attributes.
+ */
+int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
+                                 struct net_device *dev, u32 filter_mask,
+                                 int nlflags)
+{
+       return __switchdev_port_bridge_getlink(skb, pid, seq, dev, filter_mask,
+                                              nlflags,
+                                              switchdev_port_vlan_fill);
 }
 EXPORT_SYMBOL_GPL(switchdev_port_bridge_getlink);
 
+/**
+ *     switchdev_port_bridge_getlink_deferred - Get bridge port attributes
+ *     (deferred variant)
+ *
+ *     @dev: port device
+ *
+ *     Called for SELF on rtnl_bridge_getlink to get bridge port
+ *     attributes.
+ */
+int switchdev_port_bridge_getlink_deferred(struct sk_buff *skb, u32 pid,
+                                          u32 seq, struct net_device *dev,
+                                          u32 filter_mask,
+                                          int nlflags)
+{
+       return __switchdev_port_bridge_getlink(skb, pid, seq, dev, filter_mask,
+                                              nlflags,
+                                              
switchdev_port_vlan_fill_deferred);
+}
+EXPORT_SYMBOL_GPL(switchdev_port_bridge_getlink_deferred);
+
 static int switchdev_port_br_setflag(struct net_device *dev,
                                     struct nlattr *nlattr,
                                     unsigned long brport_flag)
-- 
2.9.3

Reply via email to