From 4d2e88828f25eb587b78a430e18071a5bc8d18c2 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 31 Jul 2015 11:42:54 -0700 Subject: [PATCH 1/4] net: bcmgenet: Add netconsole support Implement a poll controller for netconsole which invokes both of our interrupt handlers for the different RX/TX queues. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- .../net/ethernet/broadcom/genet/bcmgenet.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index c6f2d396edf0..eb080ef8ee97 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -2388,6 +2388,23 @@ static irqreturn_t bcmgenet_wol_isr(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef CONFIG_NET_POLL_CONTROLLER +static void bcmgenet_poll_controller(struct net_device *dev) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + + /* Invoke the main RX/TX interrupt handler */ + disable_irq(priv->irq0); + bcmgenet_isr0(priv->irq0, priv); + enable_irq(priv->irq0); + + /* And the interrupt handler for RX/TX priority queues */ + disable_irq(priv->irq1); + bcmgenet_isr1(priv->irq1, priv); + enable_irq(priv->irq1); +} +#endif + static void bcmgenet_umac_reset(struct bcmgenet_priv *priv) { u32 reg; @@ -2939,6 +2956,9 @@ static const struct net_device_ops bcmgenet_netdev_ops = { .ndo_set_mac_address = bcmgenet_set_mac_addr, .ndo_do_ioctl = bcmgenet_ioctl, .ndo_set_features = bcmgenet_set_features, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = bcmgenet_poll_controller, +#endif }; /* Array of GENET hardware parameters/characteristics */ From 6cec4f5e00a34a1c7407af302470246dd4f8be28 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 31 Jul 2015 11:42:55 -0700 Subject: [PATCH 2/4] net: systemport: Add netconsole support Implement a poll controller for netconsole which invokes the RX interrupt handler to poll for incoming packets, and cleans up all TX queues by invoking the TX interrupt handler. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcmsysport.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 4566cdf0bc39..b9a5a97ed4dd 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -933,6 +933,21 @@ static irqreturn_t bcm_sysport_wol_isr(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef CONFIG_NET_POLL_CONTROLLER +static void bcm_sysport_poll_controller(struct net_device *dev) +{ + struct bcm_sysport_priv *priv = netdev_priv(dev); + + disable_irq(priv->irq0); + bcm_sysport_rx_isr(priv->irq0, priv); + enable_irq(priv->irq0); + + disable_irq(priv->irq1); + bcm_sysport_tx_isr(priv->irq1, priv); + enable_irq(priv->irq1); +} +#endif + static struct sk_buff *bcm_sysport_insert_tsb(struct sk_buff *skb, struct net_device *dev) { @@ -1723,6 +1738,9 @@ static const struct net_device_ops bcm_sysport_netdev_ops = { .ndo_set_features = bcm_sysport_set_features, .ndo_set_rx_mode = bcm_sysport_set_rx_mode, .ndo_set_mac_address = bcm_sysport_change_mac, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = bcm_sysport_poll_controller, +#endif }; #define REV_FMT "v%2x.%02x" From 4ed70ce9f01c998999e48642a768d9013bee2c4f Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 31 Jul 2015 11:42:56 -0700 Subject: [PATCH 3/4] net: dsa: Refactor transmit path to eliminate duplication All tagging protocols do the same thing: increment device statistics, make room for the tag to be inserted, create the tag, invoke the parent network device transmit function. In order to prepare for adding netpoll support, which requires the tag creation, but not using the parent network device transmit function, do some little refactoring which eliminates duplication between the 4 tagging protocols supported. We need to return a sk_buff pointer back to the caller because the tag specific transmit function may have to reallocate the original skb (e.g: tag_trailer.c) and this is the one we should be transmitting, not the original sk_buff we were passed. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa_priv.h | 4 ++-- net/dsa/slave.c | 27 +++++++++++++++++++-------- net/dsa/tag_brcm.c | 15 +++------------ net/dsa/tag_dsa.c | 12 +++--------- net/dsa/tag_edsa.c | 12 +++--------- net/dsa/tag_trailer.c | 12 +++--------- 6 files changed, 33 insertions(+), 49 deletions(-) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index d5f1f9b862ea..eeade901e67a 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -15,7 +15,7 @@ #include struct dsa_device_ops { - netdev_tx_t (*xmit)(struct sk_buff *skb, struct net_device *dev); + struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev); int (*rcv)(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev); }; @@ -26,7 +26,7 @@ struct dsa_slave_priv { * switch port. */ struct net_device *dev; - netdev_tx_t (*xmit)(struct sk_buff *skb, + struct sk_buff * (*xmit)(struct sk_buff *skb, struct net_device *dev); /* diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 0917123790ea..5fc87ee53905 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -421,21 +421,32 @@ static int dsa_slave_port_attr_get(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; - return p->xmit(skb, dev); -} + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; -static netdev_tx_t dsa_slave_notag_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct dsa_slave_priv *p = netdev_priv(dev); + /* Transmit function may have to reallocate the original SKB */ + nskb = p->xmit(skb, dev); + if (!nskb) + return NETDEV_TX_OK; - skb->dev = p->parent->dst->master_netdev; - dev_queue_xmit(skb); + /* 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 diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index 83d3572cdb20..e2aadb73111d 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -58,14 +58,11 @@ #define BRCM_EG_TC_MASK 0x7 #define BRCM_EG_PID_MASK 0x1f -static netdev_tx_t brcm_tag_xmit(struct sk_buff *skb, struct net_device *dev) +static struct sk_buff *brcm_tag_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); u8 *brcm_tag; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - if (skb_cow_head(skb, BRCM_TAG_LEN) < 0) goto out_free; @@ -87,17 +84,11 @@ static netdev_tx_t brcm_tag_xmit(struct sk_buff *skb, struct net_device *dev) brcm_tag[2] = BRCM_IG_DSTMAP2_MASK; brcm_tag[3] = (1 << p->port) & BRCM_IG_DSTMAP1_MASK; - /* Queue the SKB for transmission on the parent interface, but - * do not modify its EtherType - */ - skb->dev = p->parent->dst->master_netdev; - dev_queue_xmit(skb); - - return NETDEV_TX_OK; + return skb; out_free: kfree_skb(skb); - return NETDEV_TX_OK; + return NULL; } static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index 2dab27063273..aa780e4ac0bd 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -15,14 +15,11 @@ #define DSA_HLEN 4 -static netdev_tx_t dsa_xmit(struct sk_buff *skb, struct net_device *dev) +static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); u8 *dsa_header; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - /* * Convert the outermost 802.1q tag to a DSA tag for tagged * packets, or insert a DSA tag between the addresses and @@ -63,14 +60,11 @@ static netdev_tx_t dsa_xmit(struct sk_buff *skb, struct net_device *dev) dsa_header[3] = 0x00; } - skb->dev = p->parent->dst->master_netdev; - dev_queue_xmit(skb); - - return NETDEV_TX_OK; + return skb; out_free: kfree_skb(skb); - return NETDEV_TX_OK; + return NULL; } static int dsa_rcv(struct sk_buff *skb, struct net_device *dev, diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c index 9aeda596f7ec..2288c8098c42 100644 --- a/net/dsa/tag_edsa.c +++ b/net/dsa/tag_edsa.c @@ -16,14 +16,11 @@ #define DSA_HLEN 4 #define EDSA_HLEN 8 -static netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev) +static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); u8 *edsa_header; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - /* * Convert the outermost 802.1q tag to a DSA tag and prepend * a DSA ethertype field is the packet is tagged, or insert @@ -76,14 +73,11 @@ static netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev) edsa_header[7] = 0x00; } - skb->dev = p->parent->dst->master_netdev; - dev_queue_xmit(skb); - - return NETDEV_TX_OK; + return skb; out_free: kfree_skb(skb); - return NETDEV_TX_OK; + return NULL; } static int edsa_rcv(struct sk_buff *skb, struct net_device *dev, diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index e268f9db8893..d25efc93d8f1 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -13,16 +13,13 @@ #include #include "dsa_priv.h" -static netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev) +static struct sk_buff *trailer_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); struct sk_buff *nskb; int padlen; u8 *trailer; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - /* * We have to make sure that the trailer ends up as the very * last 4 bytes of the packet. This means that we have to pad @@ -36,7 +33,7 @@ static netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev) nskb = alloc_skb(NET_IP_ALIGN + skb->len + padlen + 4, GFP_ATOMIC); if (nskb == NULL) { kfree_skb(skb); - return NETDEV_TX_OK; + return NULL; } skb_reserve(nskb, NET_IP_ALIGN); @@ -57,10 +54,7 @@ static netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev) trailer[2] = 0x10; trailer[3] = 0x00; - nskb->dev = p->parent->dst->master_netdev; - dev_queue_xmit(nskb); - - return NETDEV_TX_OK; + return nskb; } static int trailer_rcv(struct sk_buff *skb, struct net_device *dev, From 04ff53f96a931751a70c2bb3926770900b5fbebe Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 31 Jul 2015 11:42:57 -0700 Subject: [PATCH 4/4] net: dsa: Add netconsole support Add support for using DSA slave network devices with netconsole, which requires us to allocate and free custom netpoll instances and invoke the parent network device poll controller callback. In order for netconsole to work, we need to construct the DSA tag, but not queue the skb for transmission on the master network device xmit function. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa_priv.h | 4 +++ net/dsa/slave.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index eeade901e67a..311796c809af 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -13,6 +13,7 @@ #include #include +#include struct dsa_device_ops { struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev); @@ -47,6 +48,9 @@ struct dsa_slave_priv { int old_duplex; struct net_device *bridge_dev; +#ifdef CONFIG_NET_POLL_CONTROLLER + struct netpoll *netpoll; +#endif }; /* dsa.c */ diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 5fc87ee53905..0010c690cc67 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "dsa_priv.h" /* slave mii_bus handling ***************************************************/ @@ -418,6 +419,18 @@ static int dsa_slave_port_attr_get(struct net_device *dev, return 0; } +static inline netdev_tx_t dsa_netpoll_send_skb(struct dsa_slave_priv *p, + struct sk_buff *skb) +{ +#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_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); @@ -431,6 +444,12 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *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 */ @@ -676,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, @@ -708,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 = {