|
@@ -18,6 +18,7 @@
|
|
|
#include <net/rtnetlink.h>
|
|
|
#include <net/switchdev.h>
|
|
|
#include <linux/if_bridge.h>
|
|
|
+#include <linux/netpoll.h>
|
|
|
#include "dsa_priv.h"
|
|
|
|
|
|
/* slave mii_bus handling ***************************************************/
|
|
@@ -418,24 +419,53 @@ static int dsa_slave_port_attr_get(struct net_device *dev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
+static inline netdev_tx_t dsa_netpoll_send_skb(struct dsa_slave_priv *p,
|
|
|
+ struct sk_buff *skb)
|
|
|
{
|
|
|
- struct dsa_slave_priv *p = netdev_priv(dev);
|
|
|
-
|
|
|
- return p->xmit(skb, dev);
|
|
|
+#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
+ if (p->netpoll)
|
|
|
+ netpoll_send_skb(p->netpoll, skb);
|
|
|
+#else
|
|
|
+ BUG();
|
|
|
+#endif
|
|
|
+ return NETDEV_TX_OK;
|
|
|
}
|
|
|
|
|
|
-static netdev_tx_t dsa_slave_notag_xmit(struct sk_buff *skb,
|
|
|
- struct net_device *dev)
|
|
|
+static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
{
|
|
|
struct dsa_slave_priv *p = netdev_priv(dev);
|
|
|
+ struct sk_buff *nskb;
|
|
|
|
|
|
- skb->dev = p->parent->dst->master_netdev;
|
|
|
- dev_queue_xmit(skb);
|
|
|
+ dev->stats.tx_packets++;
|
|
|
+ dev->stats.tx_bytes += skb->len;
|
|
|
+
|
|
|
+ /* Transmit function may have to reallocate the original SKB */
|
|
|
+ nskb = p->xmit(skb, dev);
|
|
|
+ if (!nskb)
|
|
|
+ return NETDEV_TX_OK;
|
|
|
+
|
|
|
+ /* SKB for netpoll still need to be mangled with the protocol-specific
|
|
|
+ * tag to be successfully transmitted
|
|
|
+ */
|
|
|
+ if (unlikely(netpoll_tx_running(dev)))
|
|
|
+ return dsa_netpoll_send_skb(p, nskb);
|
|
|
+
|
|
|
+ /* Queue the SKB for transmission on the parent interface, but
|
|
|
+ * do not modify its EtherType
|
|
|
+ */
|
|
|
+ nskb->dev = p->parent->dst->master_netdev;
|
|
|
+ dev_queue_xmit(nskb);
|
|
|
|
|
|
return NETDEV_TX_OK;
|
|
|
}
|
|
|
|
|
|
+static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb,
|
|
|
+ struct net_device *dev)
|
|
|
+{
|
|
|
+ /* Just return the original SKB */
|
|
|
+ return skb;
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
/* ethtool operations *******************************************************/
|
|
|
static int
|
|
@@ -665,6 +695,49 @@ static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
+static int dsa_slave_netpoll_setup(struct net_device *dev,
|
|
|
+ struct netpoll_info *ni)
|
|
|
+{
|
|
|
+ struct dsa_slave_priv *p = netdev_priv(dev);
|
|
|
+ struct dsa_switch *ds = p->parent;
|
|
|
+ struct net_device *master = ds->dst->master_netdev;
|
|
|
+ struct netpoll *netpoll;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
|
|
|
+ if (!netpoll)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ err = __netpoll_setup(netpoll, master);
|
|
|
+ if (err) {
|
|
|
+ kfree(netpoll);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ p->netpoll = netpoll;
|
|
|
+out:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static void dsa_slave_netpoll_cleanup(struct net_device *dev)
|
|
|
+{
|
|
|
+ struct dsa_slave_priv *p = netdev_priv(dev);
|
|
|
+ struct netpoll *netpoll = p->netpoll;
|
|
|
+
|
|
|
+ if (!netpoll)
|
|
|
+ return;
|
|
|
+
|
|
|
+ p->netpoll = NULL;
|
|
|
+
|
|
|
+ __netpoll_free_async(netpoll);
|
|
|
+}
|
|
|
+
|
|
|
+static void dsa_slave_poll_controller(struct net_device *dev)
|
|
|
+{
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
static const struct ethtool_ops dsa_slave_ethtool_ops = {
|
|
|
.get_settings = dsa_slave_get_settings,
|
|
|
.set_settings = dsa_slave_set_settings,
|
|
@@ -697,6 +770,11 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
|
|
|
.ndo_fdb_dump = dsa_slave_fdb_dump,
|
|
|
.ndo_do_ioctl = dsa_slave_ioctl,
|
|
|
.ndo_get_iflink = dsa_slave_get_iflink,
|
|
|
+#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
+ .ndo_netpoll_setup = dsa_slave_netpoll_setup,
|
|
|
+ .ndo_netpoll_cleanup = dsa_slave_netpoll_cleanup,
|
|
|
+ .ndo_poll_controller = dsa_slave_poll_controller,
|
|
|
+#endif
|
|
|
};
|
|
|
|
|
|
static const struct switchdev_ops dsa_slave_switchdev_ops = {
|