Introduce an xmit and work queue to allow switches to send frames
on their own, which can be useful to implement control frames via
Ethernet for instance.

Signed-off-by: Vivien Didelot <vivien.dide...@gmail.com>
---
 include/net/dsa.h  |  5 +++++
 net/dsa/dsa2.c     |  6 ++++++
 net/dsa/dsa_priv.h |  2 ++
 net/dsa/switch.c   | 15 +++++++++++++++
 4 files changed, 28 insertions(+)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 027bb67ebaf7..7b10a067b06d 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -275,6 +275,9 @@ struct dsa_switch {
         */
        bool                    vlan_filtering;
 
+       struct work_struct      xmit_work;
+       struct sk_buff_head     xmit_queue;
+
        unsigned long           *bitmap;
        unsigned long           _bitmap;
 
@@ -283,6 +286,8 @@ struct dsa_switch {
        struct dsa_port ports[];
 };
 
+void dsa_switch_xmit(struct dsa_switch *ds, struct sk_buff *skb);
+
 static inline const struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p)
 {
        return &ds->ports[p];
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 3b5f434cad3f..fb7318b20e0a 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -375,6 +375,9 @@ static int dsa_switch_setup(struct dsa_switch *ds)
        if (err)
                return err;
 
+       skb_queue_head_init(&ds->xmit_queue);
+       INIT_WORK(&ds->xmit_work, dsa_switch_xmit_work);
+
        err = ds->ops->setup(ds);
        if (err < 0)
                return err;
@@ -399,6 +402,9 @@ static void dsa_switch_teardown(struct dsa_switch *ds)
        if (ds->slave_mii_bus && ds->ops->phy_read)
                mdiobus_unregister(ds->slave_mii_bus);
 
+       cancel_work_sync(&ds->xmit_work);
+       skb_queue_purge(&ds->xmit_queue);
+
        dsa_switch_unregister_notifier(ds);
 
        if (ds->devlink) {
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 2a8ee4c6adc5..0ccb0eab6295 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -201,4 +201,6 @@ dsa_slave_to_master(const struct net_device *dev)
 /* switch.c */
 int dsa_switch_register_notifier(struct dsa_switch *ds);
 void dsa_switch_unregister_notifier(struct dsa_switch *ds);
+void dsa_switch_xmit_work(struct work_struct *work);
+
 #endif
diff --git a/net/dsa/switch.c b/net/dsa/switch.c
index 7d8cd9bc0ecc..b39d246f0d55 100644
--- a/net/dsa/switch.c
+++ b/net/dsa/switch.c
@@ -371,3 +371,18 @@ void dsa_switch_unregister_notifier(struct dsa_switch *ds)
        if (err)
                dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
 }
+
+void dsa_switch_xmit(struct dsa_switch *ds, struct sk_buff *skb)
+{
+       skb_queue_tail(&ds->xmit_queue, skb);
+       schedule_work(&ds->xmit_work);
+}
+
+void dsa_switch_xmit_work(struct work_struct *work)
+{
+       struct dsa_switch *ds = container_of(work, struct dsa_switch, 
xmit_work);
+       struct sk_buff *skb;
+
+       while ((skb = skb_dequeue(&ds->xmit_queue)) != NULL)
+               dev_queue_xmit(skb);
+}
-- 
2.21.0

Reply via email to