* [PATCH v4 3/7] gianfar: Add Multiple Queue Support
From: Sandeep Gopalpet @ 2009-11-02 17:03 UTC (permalink / raw)
To: netdev; +Cc: davem, Sandeep Gopalpet
From: Sandeep Gopalpet <Sandeep.Kumar@freescale.com>
This patch introduces multiple Tx and Rx queues.
The incoming packets can be classified into different queues
based on filer rules (out of scope of this patch). The number
of queues enabled will be based on a DTS entries fsl,num_tx_queues
and fsl,num_rx_queues.
Although we are enabling multiple queues, the interrupt coalescing
is on per device level (etsec-1.7 doesn't support multiple rxics
and txics).
Signed-off-by: Sandeep Gopalpet <Sandeep.Kumar@freescale.com>
---
drivers/net/gianfar.c | 695 +++++++++++++++++++++++++++--------------
drivers/net/gianfar.h | 96 ++++++-
drivers/net/gianfar_ethtool.c | 70 +++--
drivers/net/gianfar_sysfs.c | 50 ++--
4 files changed, 621 insertions(+), 290 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index fa0188e..aa258e8 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -143,6 +143,7 @@ void gfar_start(struct net_device *dev);
static void gfar_clear_exact_match(struct net_device *dev);
static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr);
static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+u16 gfar_select_queue(struct net_device *dev, struct sk_buff *skb);
MODULE_AUTHOR("Freescale Semiconductor, Inc");
MODULE_DESCRIPTION("Gianfar Ethernet Driver");
@@ -171,71 +172,89 @@ static int gfar_init_bds(struct net_device *ndev)
struct gfar_priv_rx_q *rx_queue = NULL;
struct txbd8 *txbdp;
struct rxbd8 *rxbdp;
- int i;
-
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
+ int i, j;
- /* Initialize some variables in our dev structure */
- tx_queue->num_txbdfree = tx_queue->tx_ring_size;
- tx_queue->dirty_tx = tx_queue->cur_tx = tx_queue->tx_bd_base;
- rx_queue->cur_rx = rx_queue->rx_bd_base;
- tx_queue->skb_curtx = tx_queue->skb_dirtytx = 0;
- rx_queue->skb_currx = 0;
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ tx_queue = priv->tx_queue[i];
+ /* Initialize some variables in our dev structure */
+ tx_queue->num_txbdfree = tx_queue->tx_ring_size;
+ tx_queue->dirty_tx = tx_queue->tx_bd_base;
+ tx_queue->cur_tx = tx_queue->tx_bd_base;
+ tx_queue->skb_curtx = 0;
+ tx_queue->skb_dirtytx = 0;
+
+ /* Initialize Transmit Descriptor Ring */
+ txbdp = tx_queue->tx_bd_base;
+ for (j = 0; j < tx_queue->tx_ring_size; j++) {
+ txbdp->lstatus = 0;
+ txbdp->bufPtr = 0;
+ txbdp++;
+ }
- /* Initialize Transmit Descriptor Ring */
- txbdp = tx_queue->tx_bd_base;
- for (i = 0; i < tx_queue->tx_ring_size; i++) {
- txbdp->lstatus = 0;
- txbdp->bufPtr = 0;
- txbdp++;
+ /* Set the last descriptor in the ring to indicate wrap */
+ txbdp--;
+ txbdp->status |= TXBD_WRAP;
}
- /* Set the last descriptor in the ring to indicate wrap */
- txbdp--;
- txbdp->status |= TXBD_WRAP;
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ rx_queue = priv->rx_queue[i];
+ rx_queue->cur_rx = rx_queue->rx_bd_base;
+ rx_queue->skb_currx = 0;
+ rxbdp = rx_queue->rx_bd_base;
- rxbdp = rx_queue->rx_bd_base;
- for (i = 0; i < rx_queue->rx_ring_size; i++) {
- struct sk_buff *skb = rx_queue->rx_skbuff[i];
+ for (j = 0; j < rx_queue->rx_ring_size; j++) {
+ struct sk_buff *skb = rx_queue->rx_skbuff[j];
- if (skb) {
- gfar_init_rxbdp(rx_queue, rxbdp, rxbdp->bufPtr);
- } else {
- skb = gfar_new_skb(ndev);
- if (!skb) {
- pr_err("%s: Can't allocate RX buffers\n",
- ndev->name);
- return -ENOMEM;
+ if (skb) {
+ gfar_init_rxbdp(rx_queue, rxbdp,
+ rxbdp->bufPtr);
+ } else {
+ skb = gfar_new_skb(ndev);
+ if (!skb) {
+ pr_err("%s: Can't allocate RX buffers\n",
+ ndev->name);
+ goto err_rxalloc_fail;
+ }
+ rx_queue->rx_skbuff[j] = skb;
+
+ gfar_new_rxbdp(rx_queue, rxbdp, skb);
}
- rx_queue->rx_skbuff[i] = skb;
- gfar_new_rxbdp(rx_queue, rxbdp, skb);
+ rxbdp++;
}
- rxbdp++;
}
return 0;
+
+err_rxalloc_fail:
+ free_skb_resources(priv);
+ return -ENOMEM;
}
static int gfar_alloc_skb_resources(struct net_device *ndev)
{
void *vaddr;
- int i;
+ dma_addr_t addr;
+ int i, j, k;
struct gfar_private *priv = netdev_priv(ndev);
struct device *dev = &priv->ofdev->dev;
struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
+ priv->total_tx_ring_size = 0;
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->total_tx_ring_size += priv->tx_queue[i]->tx_ring_size;
+
+ priv->total_rx_ring_size = 0;
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->total_rx_ring_size += priv->rx_queue[i]->rx_ring_size;
/* Allocate memory for the buffer descriptors */
vaddr = dma_alloc_coherent(dev,
- sizeof(*tx_queue->tx_bd_base) * tx_queue->tx_ring_size +
- sizeof(*rx_queue->rx_bd_base) * rx_queue->rx_ring_size,
- &tx_queue->tx_bd_dma_base, GFP_KERNEL);
+ sizeof(struct txbd8) * priv->total_tx_ring_size +
+ sizeof(struct rxbd8) * priv->total_rx_ring_size,
+ &addr, GFP_KERNEL);
if (!vaddr) {
if (netif_msg_ifup(priv))
pr_err("%s: Could not allocate buffer descriptors!\n",
@@ -243,38 +262,57 @@ static int gfar_alloc_skb_resources(struct net_device *ndev)
return -ENOMEM;
}
- tx_queue->tx_bd_base = vaddr;
- tx_queue->dev = ndev;
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ tx_queue = priv->tx_queue[i];
+ tx_queue->tx_bd_base = (struct txbd8 *) vaddr;
+ tx_queue->tx_bd_dma_base = addr;
+ tx_queue->dev = ndev;
+ /* enet DMA only understands physical addresses */
+ addr += sizeof(struct txbd8) *tx_queue->tx_ring_size;
+ vaddr += sizeof(struct txbd8) *tx_queue->tx_ring_size;
+ }
/* Start the rx descriptor ring where the tx ring leaves off */
- vaddr = vaddr + sizeof(*tx_queue->tx_bd_base) * tx_queue->tx_ring_size;
- rx_queue->rx_bd_base = vaddr;
- rx_queue->dev = ndev;
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ rx_queue = priv->rx_queue[i];
+ rx_queue->rx_bd_base = (struct rxbd8 *) vaddr;
+ rx_queue->rx_bd_dma_base = addr;
+ rx_queue->dev = ndev;
+ addr += sizeof (struct rxbd8) * rx_queue->rx_ring_size;
+ vaddr += sizeof (struct rxbd8) * rx_queue->rx_ring_size;
+ }
/* Setup the skbuff rings */
- tx_queue->tx_skbuff = kmalloc(sizeof(*tx_queue->tx_skbuff) *
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ tx_queue = priv->tx_queue[i];
+ tx_queue->tx_skbuff = kmalloc(sizeof(*tx_queue->tx_skbuff) *
tx_queue->tx_ring_size, GFP_KERNEL);
- if (!tx_queue->tx_skbuff) {
- if (netif_msg_ifup(priv))
- pr_err("%s: Could not allocate tx_skbuff\n",
- ndev->name);
- goto cleanup;
- }
+ if (!tx_queue->tx_skbuff) {
+ if (netif_msg_ifup(priv))
+ pr_err("%s: Could not allocate tx_skbuff\n",
+ ndev->name);
+ goto cleanup;
+ }
- for (i = 0; i < tx_queue->tx_ring_size; i++)
- tx_queue->tx_skbuff[i] = NULL;
+ for (k = 0; k < tx_queue->tx_ring_size; k++)
+ tx_queue->tx_skbuff[k] = NULL;
+ }
- rx_queue->rx_skbuff = kmalloc(sizeof(*rx_queue->rx_skbuff) *
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ rx_queue = priv->rx_queue[i];
+ rx_queue->rx_skbuff = kmalloc(sizeof(*rx_queue->rx_skbuff) *
rx_queue->rx_ring_size, GFP_KERNEL);
- if (!rx_queue->rx_skbuff) {
- if (netif_msg_ifup(priv))
- pr_err("%s: Could not allocate rx_skbuff\n",
- ndev->name);
- goto cleanup;
- }
- for (i = 0; i < rx_queue->rx_ring_size; i++)
- rx_queue->rx_skbuff[i] = NULL;
+ if (!rx_queue->rx_skbuff) {
+ if (netif_msg_ifup(priv))
+ pr_err("%s: Could not allocate rx_skbuff\n",
+ ndev->name);
+ goto cleanup;
+ }
+
+ for (j = 0; j < rx_queue->rx_ring_size; j++)
+ rx_queue->rx_skbuff[j] = NULL;
+ }
if (gfar_init_bds(ndev))
goto cleanup;
@@ -286,33 +324,47 @@ cleanup:
return -ENOMEM;
}
+static void gfar_init_tx_rx_base(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs = priv->gfargrp.regs;
+ u32 *baddr;
+ int i;
+
+ baddr = ®s->tbase0;
+ for(i = 0; i < priv->num_tx_queues; i++) {
+ gfar_write(baddr, priv->tx_queue[i]->tx_bd_dma_base);
+ baddr += 2;
+ }
+
+ baddr = ®s->rbase0;
+ for(i = 0; i < priv->num_rx_queues; i++) {
+ gfar_write(baddr, priv->rx_queue[i]->rx_bd_dma_base);
+ baddr += 2;
+ }
+}
+
static void gfar_init_mac(struct net_device *ndev)
{
struct gfar_private *priv = netdev_priv(ndev);
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
struct gfar __iomem *regs = priv->gfargrp.regs;
u32 rctrl = 0;
u32 tctrl = 0;
u32 attrs = 0;
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
-
- /* enet DMA only understands physical addresses */
- gfar_write(®s->tbase0, tx_queue->tx_bd_dma_base);
- gfar_write(®s->rbase0, tx_queue->tx_bd_dma_base +
- sizeof(*tx_queue->tx_bd_base) *
- tx_queue->tx_ring_size);
+ /* write the tx/rx base registers */
+ gfar_init_tx_rx_base(priv);
/* Configure the coalescing support */
gfar_write(®s->txic, 0);
- if (tx_queue->txcoalescing)
- gfar_write(®s->txic, tx_queue->txic);
+ if (priv->tx_queue[0]->txcoalescing)
+ gfar_write(®s->txic, priv->tx_queue[0]->txic);
gfar_write(®s->rxic, 0);
- if (rx_queue->rxcoalescing)
- gfar_write(®s->rxic, rx_queue->rxic);
+ if (priv->rx_queue[0]->rxcoalescing)
+ gfar_write(®s->rxic, priv->rx_queue[0]->rxic);
+
+ if (priv->rx_filer_enable)
+ rctrl |= RCTRL_FILREN;
if (priv->rx_csum_enable)
rctrl |= RCTRL_CHECKSUMMING;
@@ -341,6 +393,8 @@ static void gfar_init_mac(struct net_device *ndev)
if (ndev->features & NETIF_F_IP_CSUM)
tctrl |= TCTRL_INIT_CSUM;
+ tctrl |= TCTRL_TXSCHED_PRIO;
+
gfar_write(®s->tctrl, tctrl);
/* Set the extraction length and index */
@@ -374,6 +428,7 @@ static const struct net_device_ops gfar_netdev_ops = {
.ndo_set_multicast_list = gfar_set_multi,
.ndo_tx_timeout = gfar_timeout,
.ndo_do_ioctl = gfar_ioctl,
+ .ndo_select_queue = gfar_select_queue,
.ndo_vlan_rx_register = gfar_vlan_rx_register,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
@@ -382,36 +437,131 @@ static const struct net_device_ops gfar_netdev_ops = {
#endif
};
+void lock_rx_qs(struct gfar_private *priv)
+{
+ int i = 0x0;
+
+ for (i = 0; i < priv->num_rx_queues; i++)
+ spin_lock(&priv->rx_queue[i]->rxlock);
+}
+
+void lock_tx_qs(struct gfar_private *priv)
+{
+ int i = 0x0;
+
+ for (i = 0; i < priv->num_tx_queues; i++)
+ spin_lock(&priv->tx_queue[i]->txlock);
+}
+
+void unlock_rx_qs(struct gfar_private *priv)
+{
+ int i = 0x0;
+
+ for (i = 0; i < priv->num_rx_queues; i++)
+ spin_unlock(&priv->rx_queue[i]->rxlock);
+}
+
+void unlock_tx_qs(struct gfar_private *priv)
+{
+ int i = 0x0;
+
+ for (i = 0; i < priv->num_tx_queues; i++)
+ spin_unlock(&priv->tx_queue[i]->txlock);
+}
+
/* Returns 1 if incoming frames use an FCB */
static inline int gfar_uses_fcb(struct gfar_private *priv)
{
return priv->vlgrp || priv->rx_csum_enable;
}
-static int gfar_of_init(struct net_device *dev)
+u16 gfar_select_queue(struct net_device *dev, struct sk_buff *skb)
+{
+ return skb_get_queue_mapping(skb);
+}
+static void free_tx_pointers(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < priv->num_tx_queues; i++)
+ kfree(priv->tx_queue[i]);
+}
+
+static void free_rx_pointers(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < priv->num_rx_queues; i++)
+ kfree(priv->rx_queue[i]);
+}
+
+static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
{
const char *model;
const char *ctype;
const void *mac_addr;
u64 addr, size;
- int err = 0;
- struct gfar_private *priv = netdev_priv(dev);
- struct device_node *np = priv->node;
+ int err = 0, i;
+ struct net_device *dev = NULL;
+ struct gfar_private *priv = NULL;
+ struct device_node *np = ofdev->node;
const u32 *stash;
const u32 *stash_len;
const u32 *stash_idx;
+ unsigned int num_tx_qs, num_rx_qs;
+ u32 *tx_queues, *rx_queues;
if (!np || !of_device_is_available(np))
return -ENODEV;
+ /* parse the num of tx and rx queues */
+ tx_queues = (u32 *)of_get_property(np, "fsl,num_tx_queues", NULL);
+ num_tx_qs = tx_queues ? *tx_queues : 1;
+
+ if (num_tx_qs > MAX_TX_QS) {
+ printk(KERN_ERR "num_tx_qs(=%d) greater than MAX_TX_QS(=%d)\n",
+ num_tx_qs, MAX_TX_QS);
+ printk(KERN_ERR "Cannot do alloc_etherdev, aborting\n");
+ return -EINVAL;
+ }
+
+ rx_queues = (u32 *)of_get_property(np, "fsl,num_rx_queues", NULL);
+ num_rx_qs = rx_queues ? *rx_queues : 1;
+
+ if (num_rx_qs > MAX_RX_QS) {
+ printk(KERN_ERR "num_rx_qs(=%d) greater than MAX_RX_QS(=%d)\n",
+ num_tx_qs, MAX_TX_QS);
+ printk(KERN_ERR "Cannot do alloc_etherdev, aborting\n");
+ return -EINVAL;
+ }
+
+ *pdev = alloc_etherdev_mq(sizeof(*priv), num_tx_qs);
+ dev = *pdev;
+ if (NULL == dev)
+ return -ENOMEM;
+
+ priv = netdev_priv(dev);
+ priv->node = ofdev->node;
+ priv->ndev = dev;
+
+ dev->num_tx_queues = num_tx_qs;
+ dev->real_num_tx_queues = num_tx_qs;
+ priv->num_tx_queues = num_tx_qs;
+ priv->num_rx_queues = num_rx_qs;
+
/* get a pointer to the register memory */
addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
priv->gfargrp.regs = ioremap(addr, size);
- if (priv->gfargrp.regs == NULL)
- return -ENOMEM;
+ if (priv->gfargrp.regs == NULL) {
+ err = -ENOMEM;
+ goto err_out;
+ }
priv->gfargrp.priv = priv; /* back pointer from group to priv */
+ priv->gfargrp.rx_bit_map = DEFAULT_MAPPING;
+ priv->gfargrp.tx_bit_map = DEFAULT_MAPPING;
+
priv->gfargrp.interruptTransmit = irq_of_parse_and_map(np, 0);
model = of_get_property(np, "model", NULL);
@@ -430,6 +580,38 @@ static int gfar_of_init(struct net_device *dev)
}
}
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->tx_queue[i] = NULL;
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i] = NULL;
+
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ priv->tx_queue[i] = (struct gfar_priv_tx_q *)kmalloc(
+ sizeof (struct gfar_priv_tx_q), GFP_KERNEL);
+ if (!priv->tx_queue[i]) {
+ err = -ENOMEM;
+ goto tx_alloc_failed;
+ }
+ priv->tx_queue[i]->tx_skbuff = NULL;
+ priv->tx_queue[i]->qindex = i;
+ priv->tx_queue[i]->dev = dev;
+ spin_lock_init(&(priv->tx_queue[i]->txlock));
+ }
+
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i] = (struct gfar_priv_rx_q *)kmalloc(
+ sizeof (struct gfar_priv_rx_q), GFP_KERNEL);
+ if (!priv->rx_queue[i]) {
+ err = -ENOMEM;
+ goto rx_alloc_failed;
+ }
+ priv->rx_queue[i]->rx_skbuff = NULL;
+ priv->rx_queue[i]->qindex = i;
+ priv->rx_queue[i]->dev = dev;
+ spin_lock_init(&(priv->rx_queue[i]->rxlock));
+ }
+
+
stash = of_get_property(np, "bd-stash", NULL);
if (stash) {
@@ -490,8 +672,13 @@ static int gfar_of_init(struct net_device *dev)
return 0;
+rx_alloc_failed:
+ free_rx_pointers(priv);
+tx_alloc_failed:
+ free_tx_pointers(priv);
err_out:
iounmap(priv->gfargrp.regs);
+ free_netdev(dev);
return err;
}
@@ -509,6 +696,17 @@ static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
return phy_mii_ioctl(priv->phydev, if_mii(rq), cmd);
}
+static unsigned int reverse_bitmap(unsigned int bit_map, unsigned int max_qs)
+{
+ unsigned int new_bit_map = 0x0;
+ int mask = 0x1 << (max_qs - 1), i;
+ for (i = 0; i < max_qs; i++) {
+ if (bit_map & mask)
+ new_bit_map = new_bit_map + (1 << i);
+ mask = mask >> 0x1;
+ }
+ return new_bit_map;
+}
/* Set up the ethernet device structure, private data,
* and anything else we need before we start */
static int gfar_probe(struct of_device *ofdev,
@@ -518,14 +716,14 @@ static int gfar_probe(struct of_device *ofdev,
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
struct gfar __iomem *regs = NULL;
- int err = 0;
+ int err = 0, i;
int len_devname;
+ u32 rstat = 0, tstat = 0, rqueue = 0, tqueue = 0;
- /* Create an ethernet device instance */
- dev = alloc_etherdev(sizeof (*priv));
+ err = gfar_of_init(ofdev, &dev);
- if (NULL == dev)
- return -ENOMEM;
+ if (err)
+ return err;
priv = netdev_priv(dev);
priv->ndev = dev;
@@ -533,23 +731,6 @@ static int gfar_probe(struct of_device *ofdev,
priv->node = ofdev->node;
SET_NETDEV_DEV(dev, &ofdev->dev);
- err = gfar_of_init(dev);
-
- if (err)
- goto regs_fail;
-
- priv->tx_queue = (struct gfar_priv_tx_q *)kmalloc(
- sizeof (struct gfar_priv_tx_q), GFP_KERNEL);
- if (!priv->tx_queue)
- goto regs_fail;
-
- priv->rx_queue = (struct gfar_priv_rx_q *)kmalloc(
- sizeof (struct gfar_priv_rx_q), GFP_KERNEL);
- if (!priv->rx_queue)
- goto rx_queue_fail;
-
- spin_lock_init(&priv->tx_queue->txlock);
- spin_lock_init(&priv->rx_queue->rxlock);
spin_lock_init(&priv->gfargrp.grplock);
spin_lock_init(&priv->bflock);
INIT_WORK(&priv->reset_task, gfar_reset_task);
@@ -587,8 +768,8 @@ static int gfar_probe(struct of_device *ofdev,
dev->netdev_ops = &gfar_netdev_ops;
dev->ethtool_ops = &gfar_ethtool_ops;
- /* Register for napi ...NAPI is for each rx_queue */
- netif_napi_add(dev, &priv->rx_queue->napi, gfar_poll, GFAR_DEV_WEIGHT);
+ /* Register for napi ...We are registering NAPI for each grp */
+ netif_napi_add(dev, &priv->gfargrp.napi, gfar_poll, GFAR_DEV_WEIGHT);
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
priv->rx_csum_enable = 1;
@@ -644,17 +825,44 @@ static int gfar_probe(struct of_device *ofdev,
if (dev->features & NETIF_F_IP_CSUM)
dev->hard_header_len += GMAC_FCB_LEN;
+ /* Need to reverse the bit maps as bit_map's MSB is q0
+ * but, for_each_bit parses from right to left, which
+ * basically reverses the queue numbers */
+ priv->gfargrp.tx_bit_map = reverse_bitmap(priv->gfargrp.tx_bit_map, MAX_TX_QS);
+ priv->gfargrp.rx_bit_map = reverse_bitmap(priv->gfargrp.rx_bit_map, MAX_RX_QS);
+
+ /* Calculate RSTAT, TSTAT, RQUEUE and TQUEUE values */
+ for_each_bit(i, &priv->gfargrp.rx_bit_map, priv->num_rx_queues) {
+ priv->gfargrp.num_rx_queues++;
+ rstat = rstat | (RSTAT_CLEAR_RHALT >> i);
+ rqueue = rqueue | ((RQUEUE_EN0 | RQUEUE_EX0) >> i);
+ }
+ for_each_bit (i, &priv->gfargrp.tx_bit_map, priv->num_tx_queues) {
+ priv->gfargrp.num_tx_queues++;
+ tstat = tstat | (TSTAT_CLEAR_THALT >> i);
+ tqueue = tqueue | (TQUEUE_EN0 >> i);
+ }
+ priv->gfargrp.rstat = rstat;
+ priv->gfargrp.tstat = tstat;
+
+ gfar_write(®s->rqueue, rqueue);
+ gfar_write(®s->tqueue, tqueue);
+
priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
/* Initializing some of the rx/tx queue level parameters */
- priv->tx_queue->tx_ring_size = DEFAULT_TX_RING_SIZE;
- priv->tx_queue->num_txbdfree = DEFAULT_TX_RING_SIZE;
- priv->tx_queue->txcoalescing = DEFAULT_TX_COALESCE;
- priv->tx_queue->txic = DEFAULT_TXIC;
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ priv->tx_queue[i]->tx_ring_size = DEFAULT_TX_RING_SIZE;
+ priv->tx_queue[i]->num_txbdfree = DEFAULT_TX_RING_SIZE;
+ priv->tx_queue[i]->txcoalescing = DEFAULT_TX_COALESCE;
+ priv->tx_queue[i]->txic = DEFAULT_TXIC;
+ }
- priv->rx_queue->rx_ring_size = DEFAULT_RX_RING_SIZE;
- priv->rx_queue->rxcoalescing = DEFAULT_RX_COALESCE;
- priv->rx_queue->rxic = DEFAULT_RXIC;
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i]->rx_ring_size = DEFAULT_RX_RING_SIZE;
+ priv->rx_queue[i]->rxcoalescing = DEFAULT_RX_COALESCE;
+ priv->rx_queue[i]->rxic = DEFAULT_RXIC;
+ }
/* Enable most messages by default */
priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
@@ -699,17 +907,19 @@ static int gfar_probe(struct of_device *ofdev,
/* Even more device info helps when determining which kernel */
/* provided which set of benchmarks. */
printk(KERN_INFO "%s: Running with NAPI enabled\n", dev->name);
- printk(KERN_INFO "%s: %d/%d RX/TX BD ring size\n",
- dev->name, priv->rx_queue->rx_ring_size, priv->tx_queue->tx_ring_size);
+ for (i = 0; i < priv->num_rx_queues; i++)
+ printk(KERN_INFO "%s: :RX BD ring size for Q[%d]: %d\n",
+ dev->name, i, priv->rx_queue[i]->rx_ring_size);
+ for(i = 0; i < priv->num_tx_queues; i++)
+ printk(KERN_INFO "%s:TX BD ring size for Q[%d]: %d\n",
+ dev->name, i, priv->tx_queue[i]->tx_ring_size);
return 0;
register_fail:
iounmap(priv->gfargrp.regs);
- kfree(priv->rx_queue);
-rx_queue_fail:
- kfree(priv->tx_queue);
-regs_fail:
+ free_tx_pointers(priv);
+ free_rx_pointers(priv);
if (priv->phy_node)
of_node_put(priv->phy_node);
if (priv->tbi_node)
@@ -742,8 +952,6 @@ static int gfar_suspend(struct device *dev)
{
struct gfar_private *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
struct gfar __iomem *regs = NULL;
unsigned long flags;
u32 tempval;
@@ -752,13 +960,13 @@ static int gfar_suspend(struct device *dev)
(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
netif_device_detach(ndev);
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
regs = priv->gfargrp.regs;
if (netif_running(ndev)) {
- spin_lock_irqsave(&tx_queue->txlock, flags);
- spin_lock(&rx_queue->rxlock);
+
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
gfar_halt_nodisable(ndev);
@@ -772,10 +980,11 @@ static int gfar_suspend(struct device *dev)
gfar_write(®s->maccfg1, tempval);
- spin_unlock(&rx_queue->rxlock);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
- napi_disable(&rx_queue->napi);
+ napi_disable(&priv->gfargrp.napi);
if (magic_packet) {
/* Enable interrupt on Magic Packet */
@@ -797,8 +1006,6 @@ static int gfar_resume(struct device *dev)
{
struct gfar_private *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
struct gfar __iomem *regs = NULL;
unsigned long flags;
u32 tempval;
@@ -816,12 +1023,11 @@ static int gfar_resume(struct device *dev)
/* Disable Magic Packet mode, in case something
* else woke us up.
*/
- rx_queue = priv->rx_queue;
- tx_queue = priv->tx_queue;
regs = priv->gfargrp.regs;
- spin_lock_irqsave(&tx_queue->txlock, flags);
- spin_lock(&rx_queue->rxlock);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
tempval = gfar_read(®s->maccfg2);
tempval &= ~MACCFG2_MPEN;
@@ -829,12 +1035,13 @@ static int gfar_resume(struct device *dev)
gfar_start(ndev);
- spin_unlock(&rx_queue->rxlock);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
netif_device_attach(ndev);
- napi_enable(&rx_queue->napi);
+ napi_enable(&priv->gfargrp.napi);
return 0;
}
@@ -861,7 +1068,7 @@ static int gfar_restore(struct device *dev)
phy_start(priv->phydev);
netif_device_attach(ndev);
- napi_enable(&priv->napi);
+ napi_enable(&priv->gfargrp.napi);
return 0;
}
@@ -1115,23 +1322,21 @@ void gfar_halt(struct net_device *dev)
void stop_gfar(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
unsigned long flags;
phy_stop(priv->phydev);
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
/* Lock it down */
- spin_lock_irqsave(&tx_queue->txlock, flags);
- spin_lock(&rx_queue->rxlock);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
gfar_halt(dev);
- spin_unlock(&rx_queue->rxlock);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
/* Free the IRQs */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
@@ -1145,24 +1350,14 @@ void stop_gfar(struct net_device *dev)
free_skb_resources(priv);
}
-/* If there are any tx skbs or rx skbs still around, free them.
- * Then free tx_skbuff and rx_skbuff */
-static void free_skb_resources(struct gfar_private *priv)
+static void free_skb_tx_queue(struct gfar_priv_tx_q *tx_queue)
{
- struct device *dev = &priv->ofdev->dev;
- struct rxbd8 *rxbdp;
struct txbd8 *txbdp;
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar_private *priv = netdev_priv(tx_queue->dev);
int i, j;
- /* Go through all the buffer descriptors and free their data buffers */
- tx_queue = priv->tx_queue;
txbdp = tx_queue->tx_bd_base;
- if (!tx_queue->tx_skbuff)
- goto skip_tx_skbuff;
-
for (i = 0; i < tx_queue->tx_ring_size; i++) {
if (!tx_queue->tx_skbuff[i])
continue;
@@ -1170,7 +1365,8 @@ static void free_skb_resources(struct gfar_private *priv)
dma_unmap_single(&priv->ofdev->dev, txbdp->bufPtr,
txbdp->length, DMA_TO_DEVICE);
txbdp->lstatus = 0;
- for (j = 0; j < skb_shinfo(tx_queue->tx_skbuff[i])->nr_frags; j++) {
+ for (j = 0; j < skb_shinfo(tx_queue->tx_skbuff[i])->nr_frags;
+ j++) {
txbdp++;
dma_unmap_page(&priv->ofdev->dev, txbdp->bufPtr,
txbdp->length, DMA_TO_DEVICE);
@@ -1179,36 +1375,58 @@ static void free_skb_resources(struct gfar_private *priv)
dev_kfree_skb_any(tx_queue->tx_skbuff[i]);
tx_queue->tx_skbuff[i] = NULL;
}
-
kfree(tx_queue->tx_skbuff);
-skip_tx_skbuff:
+}
- rx_queue = priv->rx_queue;
- rxbdp = rx_queue->rx_bd_base;
+static void free_skb_rx_queue(struct gfar_priv_rx_q *rx_queue)
+{
+ struct rxbd8 *rxbdp;
+ struct gfar_private *priv = netdev_priv(rx_queue->dev);
+ int i;
- if (!rx_queue->rx_skbuff)
- goto skip_rx_skbuff;
+ rxbdp = rx_queue->rx_bd_base;
for (i = 0; i < rx_queue->rx_ring_size; i++) {
if (rx_queue->rx_skbuff[i]) {
- dma_unmap_single(&priv->ofdev->dev, rxbdp->bufPtr,
- priv->rx_buffer_size,
+ dma_unmap_single(&priv->ofdev->dev,
+ rxbdp->bufPtr, priv->rx_buffer_size,
DMA_FROM_DEVICE);
dev_kfree_skb_any(rx_queue->rx_skbuff[i]);
rx_queue->rx_skbuff[i] = NULL;
}
-
rxbdp->lstatus = 0;
rxbdp->bufPtr = 0;
rxbdp++;
}
-
kfree(rx_queue->rx_skbuff);
-skip_rx_skbuff:
+}
- dma_free_coherent(dev, sizeof(*txbdp) * tx_queue->tx_ring_size +
- sizeof(*rxbdp) * rx_queue->rx_ring_size,
- tx_queue->tx_bd_base, tx_queue->tx_bd_dma_base);
+/* If there are any tx skbs or rx skbs still around, free them.
+ * Then free tx_skbuff and rx_skbuff */
+static void free_skb_resources(struct gfar_private *priv)
+{
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ int i;
+
+ /* Go through all the buffer descriptors and free their data buffers */
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ tx_queue = priv->tx_queue[i];
+ if(!tx_queue->tx_skbuff)
+ free_skb_tx_queue(tx_queue);
+ }
+
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ rx_queue = priv->rx_queue[i];
+ if(!rx_queue->rx_skbuff)
+ free_skb_rx_queue(rx_queue);
+ }
+
+ dma_free_coherent(&priv->ofdev->dev,
+ sizeof(struct txbd8) * priv->total_tx_ring_size +
+ sizeof(struct rxbd8) * priv->total_rx_ring_size,
+ priv->tx_queue[0]->tx_bd_base,
+ priv->tx_queue[0]->tx_bd_dma_base);
}
void gfar_start(struct net_device *dev)
@@ -1233,8 +1451,8 @@ void gfar_start(struct net_device *dev)
gfar_write(®s->dmactrl, tempval);
/* Clear THLT/RHLT, so that the DMA starts polling now */
- gfar_write(®s->tstat, TSTAT_CLEAR_THALT);
- gfar_write(®s->rstat, RSTAT_CLEAR_RHALT);
+ gfar_write(®s->tstat, priv->gfargrp.tstat);
+ gfar_write(®s->rstat, priv->gfargrp.rstat);
/* Unmask the interrupts we look for */
gfar_write(®s->imask, IMASK_DEFAULT);
@@ -1329,7 +1547,7 @@ static int gfar_enet_open(struct net_device *dev)
struct gfar_private *priv = netdev_priv(dev);
int err;
- napi_enable(&priv->rx_queue->napi);
+ napi_enable(&priv->gfargrp.napi);
skb_queue_head_init(&priv->rx_recycle);
@@ -1341,17 +1559,17 @@ static int gfar_enet_open(struct net_device *dev)
err = init_phy(dev);
if (err) {
- napi_disable(&priv->rx_queue->napi);
+ napi_disable(&priv->gfargrp.napi);
return err;
}
err = startup_gfar(dev);
if (err) {
- napi_disable(&priv->rx_queue->napi);
+ napi_disable(&priv->gfargrp.napi);
return err;
}
- netif_start_queue(dev);
+ netif_tx_start_all_queues(dev);
device_set_wakeup_enable(&dev->dev, priv->wol_en);
@@ -1421,16 +1639,20 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar_priv_tx_q *tx_queue = NULL;
+ struct netdev_queue *txq;
struct gfar __iomem *regs = NULL;
struct txfcb *fcb = NULL;
struct txbd8 *txbdp, *txbdp_start, *base;
u32 lstatus;
- int i;
+ int i, rq = 0;
u32 bufaddr;
unsigned long flags;
unsigned int nr_frags, length;
- tx_queue = priv->tx_queue;
+
+ rq = skb->queue_mapping;
+ tx_queue = priv->tx_queue[rq];
+ txq = netdev_get_tx_queue(dev, rq);
base = tx_queue->tx_bd_base;
regs = priv->gfargrp.regs;
@@ -1458,7 +1680,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* check if there is space to queue this packet */
if ((nr_frags+1) > tx_queue->num_txbdfree) {
/* no space, stop the queue */
- netif_stop_queue(dev);
+ netif_tx_stop_queue(txq);
dev->stats.tx_fifo_errors++;
spin_unlock_irqrestore(&tx_queue->txlock, flags);
return NETDEV_TX_BUSY;
@@ -1550,13 +1772,13 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* If the next BD still needs to be cleaned up, then the bds
are full. We need to tell the kernel to stop sending us stuff. */
if (!tx_queue->num_txbdfree) {
- netif_stop_queue(dev);
+ netif_tx_stop_queue(txq);
dev->stats.tx_fifo_errors++;
}
/* Tell the DMA to go go go */
- gfar_write(®s->tstat, TSTAT_CLEAR_THALT);
+ gfar_write(®s->tstat, TSTAT_CLEAR_THALT >> tx_queue->qindex);
/* Unlock priv */
spin_unlock_irqrestore(&tx_queue->txlock, flags);
@@ -1569,7 +1791,7 @@ static int gfar_close(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- napi_disable(&priv->rx_queue->napi);
+ napi_disable(&priv->gfargrp.napi);
skb_queue_purge(&priv->rx_recycle);
cancel_work_sync(&priv->reset_task);
@@ -1579,7 +1801,7 @@ static int gfar_close(struct net_device *dev)
phy_disconnect(priv->phydev);
priv->phydev = NULL;
- netif_stop_queue(dev);
+ netif_tx_stop_all_queues(dev);
return 0;
}
@@ -1598,14 +1820,13 @@ static void gfar_vlan_rx_register(struct net_device *dev,
struct vlan_group *grp)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_rx_q *rx_queue = NULL;
struct gfar __iomem *regs = NULL;
unsigned long flags;
u32 tempval;
- rx_queue = priv->rx_queue;
regs = priv->gfargrp.regs;
- spin_lock_irqsave(&rx_queue->rxlock, flags);
+ local_irq_save(flags);
+ lock_rx_qs(priv);
priv->vlgrp = grp;
@@ -1639,7 +1860,8 @@ static void gfar_vlan_rx_register(struct net_device *dev,
gfar_change_mtu(dev, dev->mtu);
- spin_unlock_irqrestore(&rx_queue->rxlock, flags);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
}
static int gfar_change_mtu(struct net_device *dev, int new_mtu)
@@ -1711,10 +1933,10 @@ static void gfar_reset_task(struct work_struct *work)
struct net_device *dev = priv->ndev;
if (dev->flags & IFF_UP) {
- netif_stop_queue(dev);
+ netif_tx_stop_all_queues(dev);
stop_gfar(dev);
startup_gfar(dev);
- netif_start_queue(dev);
+ netif_tx_start_all_queues(dev);
}
netif_tx_schedule_all(dev);
@@ -1745,7 +1967,7 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
int howmany = 0;
u32 lstatus;
- rx_queue = priv->rx_queue;
+ rx_queue = priv->rx_queue[tx_queue->qindex];
bdp = tx_queue->dirty_tx;
skb_dirtytx = tx_queue->skb_dirtytx;
@@ -1798,8 +2020,8 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
}
/* If we freed a buffer, we can restart transmission, if necessary */
- if (netif_queue_stopped(dev) && tx_queue->num_txbdfree)
- netif_wake_queue(dev);
+ if (__netif_subqueue_stopped(dev, tx_queue->qindex) && tx_queue->num_txbdfree)
+ netif_wake_subqueue(dev, tx_queue->qindex);
/* Update dirty indicators */
tx_queue->skb_dirtytx = skb_dirtytx;
@@ -1812,19 +2034,12 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
static void gfar_schedule_cleanup(struct gfar_priv_grp *gfargrp)
{
- struct gfar_private *priv = gfargrp->priv;
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
unsigned long flags;
- rx_queue = priv->rx_queue;
- tx_queue = priv->tx_queue;
- spin_lock_irqsave(&tx_queue->txlock, flags);
- spin_lock(&rx_queue->rxlock);
-
- if (napi_schedule_prep(&rx_queue->napi)) {
+ spin_lock_irqsave(&gfargrp->grplock, flags);
+ if (napi_schedule_prep(&gfargrp->napi)) {
gfar_write(&gfargrp->regs->imask, IMASK_RTX_DISABLED);
- __napi_schedule(&rx_queue->napi);
+ __napi_schedule(&gfargrp->napi);
} else {
/*
* Clear IEVENT, so interrupts aren't called again
@@ -1832,9 +2047,8 @@ static void gfar_schedule_cleanup(struct gfar_priv_grp *gfargrp)
*/
gfar_write(&gfargrp->regs->ievent, IEVENT_RTX_MASK);
}
+ spin_unlock_irqrestore(&gfargrp->grplock, flags);
- spin_unlock(&rx_queue->rxlock);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
}
/* Interrupt Handler for Transmit complete */
@@ -1952,6 +2166,7 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
fcb = (struct rxfcb *)skb->data;
/* Remove the FCB from the skb */
+ skb_set_queue_mapping(skb, fcb->rq);
/* Remove the padded bytes, if there are any */
if (amount_pull)
skb_pull(skb, amount_pull);
@@ -2072,28 +2287,54 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
static int gfar_poll(struct napi_struct *napi, int budget)
{
- struct gfar_priv_rx_q *rx_queue = container_of(napi,
- struct gfar_priv_rx_q, napi);
- struct net_device *dev = rx_queue->dev;
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_grp *gfargrp = container_of(napi,
+ struct gfar_priv_grp, napi);
+ struct gfar_private *priv = gfargrp->priv;
struct gfar __iomem *regs = priv->gfargrp.regs;
struct gfar_priv_tx_q *tx_queue = NULL;
- int tx_cleaned = 0;
- int rx_cleaned = 0;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ int rx_cleaned = 0, budget_per_queue = 0, rx_cleaned_per_queue = 0;
+ int tx_cleaned = 0, i, left_over_budget = budget, serviced_queues = 0;
+ int num_queues = 0;
unsigned long flags;
+ num_queues = gfargrp->num_rx_queues;
+ budget_per_queue = budget/num_queues;
+
/* Clear IEVENT, so interrupts aren't called again
* because of the packets that have already arrived */
gfar_write(®s->ievent, IEVENT_RTX_MASK);
- tx_queue = priv->tx_queue;
- /* If we fail to get the lock, don't bother with the TX BDs */
- if (spin_trylock_irqsave(&tx_queue->txlock, flags)) {
- tx_cleaned = gfar_clean_tx_ring(tx_queue);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
- }
+ while (num_queues && left_over_budget) {
- rx_cleaned = gfar_clean_rx_ring(rx_queue, budget);
+ budget_per_queue = left_over_budget/num_queues;
+ left_over_budget = 0;
+
+ for_each_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) {
+ if (test_bit(i, &serviced_queues))
+ continue;
+ rx_queue = priv->rx_queue[i];
+ tx_queue = priv->tx_queue[rx_queue->qindex];
+
+ /* If we fail to get the lock,
+ * don't bother with the TX BDs */
+ if (spin_trylock_irqsave(&tx_queue->txlock, flags)) {
+ tx_cleaned += gfar_clean_tx_ring(tx_queue);
+ spin_unlock_irqrestore(&tx_queue->txlock,
+ flags);
+ }
+
+ rx_cleaned_per_queue = gfar_clean_rx_ring(rx_queue,
+ budget_per_queue);
+ rx_cleaned += rx_cleaned_per_queue;
+ if(rx_cleaned_per_queue < budget_per_queue) {
+ left_over_budget = left_over_budget +
+ (budget_per_queue - rx_cleaned_per_queue);
+ set_bit(i, &serviced_queues);
+ num_queues--;
+ }
+ }
+ }
if (tx_cleaned)
return budget;
@@ -2102,7 +2343,7 @@ static int gfar_poll(struct napi_struct *napi, int budget)
napi_complete(napi);
/* Clear the halt bit in RSTAT */
- gfar_write(®s->rstat, RSTAT_CLEAR_RHALT);
+ gfar_write(®s->rstat, gfargrp->rstat);
gfar_write(®s->imask, IMASK_DEFAULT);
@@ -2180,14 +2421,14 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id)
static void adjust_link(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar __iomem *regs = priv->gfargrp.regs;
unsigned long flags;
struct phy_device *phydev = priv->phydev;
int new_state = 0;
- tx_queue = priv->tx_queue;
- spin_lock_irqsave(&tx_queue->txlock, flags);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+
if (phydev->link) {
u32 tempval = gfar_read(®s->maccfg2);
u32 ecntrl = gfar_read(®s->ecntrl);
@@ -2252,8 +2493,8 @@ static void adjust_link(struct net_device *dev)
if (new_state && netif_msg_link(priv))
phy_print_status(phydev);
-
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
}
/* Update the hash table based on the current list of multicast
@@ -2457,7 +2698,7 @@ static irqreturn_t gfar_error(int irq, void *grp_id)
priv->extra_stats.tx_underrun++;
/* Reactivate the Tx Queues */
- gfar_write(®s->tstat, TSTAT_CLEAR_THALT);
+ gfar_write(®s->tstat, gfargrp->tstat);
}
if (netif_msg_tx_err(priv))
printk(KERN_DEBUG "%s: Transmit Error\n", dev->name);
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 79e8471..5ae769d 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -75,6 +75,10 @@
extern const char gfar_driver_name[];
extern const char gfar_driver_version[];
+/* MAXIMUM NUMBER OF QUEUES SUPPORTED */
+#define MAX_TX_QS 0x8
+#define MAX_RX_QS 0x8
+
/* These need to be powers of 2 for this driver */
#define DEFAULT_TX_RING_SIZE 256
#define DEFAULT_RX_RING_SIZE 256
@@ -172,12 +176,63 @@ extern const char gfar_driver_version[];
#define MINFLR_INIT_SETTINGS 0x00000040
+/* Tqueue control */
+#define TQUEUE_EN0 0x00008000
+#define TQUEUE_EN1 0x00004000
+#define TQUEUE_EN2 0x00002000
+#define TQUEUE_EN3 0x00001000
+#define TQUEUE_EN4 0x00000800
+#define TQUEUE_EN5 0x00000400
+#define TQUEUE_EN6 0x00000200
+#define TQUEUE_EN7 0x00000100
+#define TQUEUE_EN_ALL 0x0000FF00
+
+#define TR03WT_WT0_MASK 0xFF000000
+#define TR03WT_WT1_MASK 0x00FF0000
+#define TR03WT_WT2_MASK 0x0000FF00
+#define TR03WT_WT3_MASK 0x000000FF
+
+#define TR47WT_WT4_MASK 0xFF000000
+#define TR47WT_WT5_MASK 0x00FF0000
+#define TR47WT_WT6_MASK 0x0000FF00
+#define TR47WT_WT7_MASK 0x000000FF
+
+/* Rqueue control */
+#define RQUEUE_EX0 0x00800000
+#define RQUEUE_EX1 0x00400000
+#define RQUEUE_EX2 0x00200000
+#define RQUEUE_EX3 0x00100000
+#define RQUEUE_EX4 0x00080000
+#define RQUEUE_EX5 0x00040000
+#define RQUEUE_EX6 0x00020000
+#define RQUEUE_EX7 0x00010000
+#define RQUEUE_EX_ALL 0x00FF0000
+
+#define RQUEUE_EN0 0x00000080
+#define RQUEUE_EN1 0x00000040
+#define RQUEUE_EN2 0x00000020
+#define RQUEUE_EN3 0x00000010
+#define RQUEUE_EN4 0x00000008
+#define RQUEUE_EN5 0x00000004
+#define RQUEUE_EN6 0x00000002
+#define RQUEUE_EN7 0x00000001
+#define RQUEUE_EN_ALL 0x000000FF
+
/* Init to do tx snooping for buffers and descriptors */
#define DMACTRL_INIT_SETTINGS 0x000000c3
#define DMACTRL_GRS 0x00000010
#define DMACTRL_GTS 0x00000008
-#define TSTAT_CLEAR_THALT 0x80000000
+#define TSTAT_CLEAR_THALT_ALL 0xFF000000
+#define TSTAT_CLEAR_THALT 0x80000000
+#define TSTAT_CLEAR_THALT0 0x80000000
+#define TSTAT_CLEAR_THALT1 0x40000000
+#define TSTAT_CLEAR_THALT2 0x20000000
+#define TSTAT_CLEAR_THALT3 0x10000000
+#define TSTAT_CLEAR_THALT4 0x08000000
+#define TSTAT_CLEAR_THALT5 0x04000000
+#define TSTAT_CLEAR_THALT6 0x02000000
+#define TSTAT_CLEAR_THALT7 0x01000000
/* Interrupt coalescing macros */
#define IC_ICEN 0x80000000
@@ -228,6 +283,13 @@ extern const char gfar_driver_version[];
#define TCTRL_IPCSEN 0x00004000
#define TCTRL_TUCSEN 0x00002000
#define TCTRL_VLINS 0x00001000
+#define TCTRL_THDF 0x00000800
+#define TCTRL_RFCPAUSE 0x00000010
+#define TCTRL_TFCPAUSE 0x00000008
+#define TCTRL_TXSCHED_MASK 0x00000006
+#define TCTRL_TXSCHED_INIT 0x00000000
+#define TCTRL_TXSCHED_PRIO 0x00000002
+#define TCTRL_TXSCHED_WRRS 0x00000004
#define TCTRL_INIT_CSUM (TCTRL_TUCSEN | TCTRL_IPCSEN)
#define IEVENT_INIT_CLEAR 0xffffffff
@@ -700,6 +762,8 @@ struct gfar {
#define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200
#define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400
+#define DEFAULT_MAPPING 0xFF
+
/**
* struct gfar_priv_tx_q - per tx queue structure
* @txlock: per queue tx spin lock
@@ -743,7 +807,6 @@ struct gfar_priv_tx_q {
/**
* struct gfar_priv_rx_q - per rx queue structure
* @rxlock: per queue rx spin lock
- * @napi: the napi poll function
* @rx_skbuff: skb pointers
* @skb_currx: currently use skb pointer
* @rx_bd_base: First rx buffer descriptor
@@ -757,8 +820,8 @@ struct gfar_priv_tx_q {
struct gfar_priv_rx_q {
spinlock_t rxlock __attribute__ ((aligned (SMP_CACHE_BYTES)));
- struct napi_struct napi;
struct sk_buff ** rx_skbuff;
+ dma_addr_t rx_bd_dma_base;
struct rxbd8 *rx_bd_base;
struct rxbd8 *cur_rx;
struct net_device *dev;
@@ -772,6 +835,7 @@ struct gfar_priv_rx_q {
/**
* struct gfar_priv_grp - per group structure
+ * @napi: the napi poll function
* @priv: back pointer to the priv structure
* @regs: the ioremapped register space for this group
* @grp_id: group id for this group
@@ -785,8 +849,17 @@ struct gfar_priv_rx_q {
struct gfar_priv_grp {
spinlock_t grplock __attribute__ ((aligned (SMP_CACHE_BYTES)));
+ struct napi_struct napi;
struct gfar_private *priv;
struct gfar __iomem *regs;
+ unsigned int rx_bit_map;
+ unsigned int tx_bit_map;
+ unsigned int num_tx_queues;
+ unsigned int num_rx_queues;
+ unsigned int rstat;
+ unsigned int tstat;
+ unsigned int imask;
+ unsigned int ievent;
unsigned int interruptTransmit;
unsigned int interruptReceive;
unsigned int interruptError;
@@ -807,13 +880,21 @@ struct gfar_priv_grp {
*/
struct gfar_private {
+ /* Indicates how many tx, rx queues are enabled */
+ unsigned int num_tx_queues;
+ unsigned int num_rx_queues;
+
+ /* The total tx and rx ring size for the enabled queues */
+ unsigned int total_tx_ring_size;
+ unsigned int total_rx_ring_size;
+
struct device_node *node;
struct net_device *ndev;
struct of_device *ofdev;
struct gfar_priv_grp gfargrp;
- struct gfar_priv_tx_q *tx_queue;
- struct gfar_priv_rx_q *rx_queue;
+ struct gfar_priv_tx_q *tx_queue[MAX_TX_QS];
+ struct gfar_priv_rx_q *rx_queue[MAX_RX_QS];
/* RX per device parameters */
unsigned int rx_buffer_size;
@@ -844,6 +925,7 @@ struct gfar_private {
unsigned char rx_csum_enable:1,
extended_hash:1,
bd_stash_en:1,
+ rx_filer_enable:1,
wol_en:1; /* Wake-on-LAN enabled */
unsigned short padding;
@@ -874,6 +956,10 @@ static inline void gfar_write(volatile unsigned __iomem *addr, u32 val)
out_be32(addr, val);
}
+extern void lock_rx_qs(struct gfar_private *priv);
+extern void lock_tx_qs(struct gfar_private *priv);
+extern void unlock_rx_qs(struct gfar_private *priv);
+extern void unlock_tx_qs(struct gfar_private *priv);
extern irqreturn_t gfar_receive(int irq, void *dev_id);
extern int startup_gfar(struct net_device *dev);
extern void stop_gfar(struct net_device *dev);
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index c681b41..d3d2623 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -204,9 +204,11 @@ static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
if (NULL == phydev)
return -ENODEV;
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
+ tx_queue = priv->tx_queue[0];
+ rx_queue = priv->rx_queue[0];
+ /* etsec-1.7 and older versions have only one txic
+ * and rxic regs although they support multiple queues */
cmd->maxtxpkt = get_icft_value(tx_queue->txic);
cmd->maxrxpkt = get_icft_value(rx_queue->rxic);
@@ -298,8 +300,8 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
if (NULL == priv->phydev)
return -ENODEV;
- rx_queue = priv->rx_queue;
- tx_queue = priv->tx_queue;
+ rx_queue = priv->rx_queue[0];
+ tx_queue = priv->tx_queue[0];
rxtime = get_ictt_value(rx_queue->rxic);
rxcount = get_icft_value(rx_queue->rxic);
@@ -357,8 +359,8 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP;
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
+ tx_queue = priv->tx_queue[0];
+ rx_queue = priv->rx_queue[0];
/* Set up rx coalescing */
if ((cvals->rx_coalesce_usecs == 0) ||
@@ -429,8 +431,8 @@ static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rv
struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
+ tx_queue = priv->tx_queue[0];
+ rx_queue = priv->rx_queue[0];
rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
@@ -453,9 +455,7 @@ static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rv
static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
- int err = 0;
+ int err = 0, i = 0;
if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
return -EINVAL;
@@ -475,37 +475,41 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva
return -EINVAL;
}
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
if (dev->flags & IFF_UP) {
unsigned long flags;
/* Halt TX and RX, and process the frames which
* have already been received */
- spin_lock_irqsave(&tx_queue->txlock, flags);
- spin_lock(&rx_queue->rxlock);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
gfar_halt(dev);
- spin_unlock(&rx_queue->rxlock);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
- gfar_clean_rx_ring(rx_queue, rx_queue->rx_ring_size);
+ for (i = 0; i < priv->num_rx_queues; i++)
+ gfar_clean_rx_ring(priv->rx_queue[i],
+ priv->rx_queue[i]->rx_ring_size);
/* Now we take down the rings to rebuild them */
stop_gfar(dev);
}
/* Change the size */
- rx_queue->rx_ring_size = rvals->rx_pending;
- tx_queue->tx_ring_size = rvals->tx_pending;
- tx_queue->num_txbdfree = tx_queue->tx_ring_size;
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i]->rx_ring_size = rvals->rx_pending;
+ priv->tx_queue[i]->tx_ring_size = rvals->tx_pending;
+ priv->tx_queue[i]->num_txbdfree = priv->tx_queue[i]->tx_ring_size;
+ }
/* Rebuild the rings with the new size */
if (dev->flags & IFF_UP) {
err = startup_gfar(dev);
- netif_wake_queue(dev);
+ netif_tx_wake_all_queues(dev);
}
return err;
}
@@ -513,29 +517,29 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva
static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_priv_rx_q *rx_queue = NULL;
- struct gfar_priv_tx_q *tx_queue = NULL;
unsigned long flags;
- int err = 0;
+ int err = 0, i = 0;
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
return -EOPNOTSUPP;
- tx_queue = priv->tx_queue;
- rx_queue = priv->rx_queue;
if (dev->flags & IFF_UP) {
/* Halt TX and RX, and process the frames which
* have already been received */
- spin_lock_irqsave(&tx_queue->txlock, flags);
- spin_lock(&rx_queue->rxlock);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
gfar_halt(dev);
- spin_unlock(&rx_queue->rxlock);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_tx_qs(priv);
+ unlock_rx_qs(priv);
+ local_irq_save(flags);
- gfar_clean_rx_ring(rx_queue, rx_queue->rx_ring_size);
+ for (i = 0; i < priv->num_rx_queues; i++)
+ gfar_clean_rx_ring(priv->rx_queue[i],
+ priv->rx_queue[i]->rx_ring_size);
/* Now we take down the rings to rebuild them */
stop_gfar(dev);
@@ -547,7 +551,7 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
if (dev->flags & IFF_UP) {
err = startup_gfar(dev);
- netif_wake_queue(dev);
+ netif_tx_wake_all_queues(dev);
}
return err;
}
diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c
index adea11e..4b726f6 100644
--- a/drivers/net/gianfar_sysfs.c
+++ b/drivers/net/gianfar_sysfs.c
@@ -51,7 +51,6 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_rx_q *rx_queue = NULL;
int new_setting = 0;
u32 temp;
unsigned long flags;
@@ -59,7 +58,6 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BD_STASHING))
return count;
- rx_queue = priv->rx_queue;
/* Find out the new setting */
if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))
@@ -70,7 +68,9 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
else
return count;
- spin_lock_irqsave(&rx_queue->rxlock, flags);
+
+ local_irq_save(flags);
+ lock_rx_qs(priv);
/* Set the new stashing value */
priv->bd_stash_en = new_setting;
@@ -84,7 +84,8 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
gfar_write(®s->attr, temp);
- spin_unlock_irqrestore(&rx_queue->rxlock, flags);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -105,7 +106,6 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_rx_q *rx_queue = NULL;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -113,9 +113,9 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING))
return count;
- rx_queue = priv->rx_queue;
+ local_irq_save(flags);
+ lock_rx_qs(priv);
- spin_lock_irqsave(&rx_queue->rxlock, flags);
if (length > priv->rx_buffer_size)
goto out;
@@ -140,7 +140,8 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
gfar_write(®s->attr, temp);
out:
- spin_unlock_irqrestore(&rx_queue->rxlock, flags);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -164,7 +165,6 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_rx_q *rx_queue = NULL;
unsigned short index = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -172,9 +172,9 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING))
return count;
- rx_queue = priv->rx_queue;
+ local_irq_save(flags);
+ lock_rx_qs(priv);
- spin_lock_irqsave(&rx_queue->rxlock, flags);
if (index > priv->rx_stash_size)
goto out;
@@ -189,7 +189,8 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
gfar_write(®s->attreli, flags);
out:
- spin_unlock_irqrestore(&rx_queue->rxlock, flags);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -212,7 +213,6 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_tx_q *tx_queue = NULL;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -220,9 +220,8 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
if (length > GFAR_MAX_FIFO_THRESHOLD)
return count;
- tx_queue = priv->tx_queue;
-
- spin_lock_irqsave(&tx_queue->txlock, flags);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
priv->fifo_threshold = length;
@@ -231,7 +230,8 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
temp |= length;
gfar_write(®s->fifo_tx_thr, temp);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -253,7 +253,6 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_tx_q *tx_queue = NULL;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -261,8 +260,8 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
if (num > GFAR_MAX_FIFO_STARVE)
return count;
- tx_queue = priv->tx_queue;
- spin_lock_irqsave(&tx_queue->txlock, flags);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
priv->fifo_starve = num;
@@ -271,7 +270,8 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
temp |= num;
gfar_write(®s->fifo_tx_starve, temp);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -294,7 +294,6 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_tx_q *tx_queue = NULL;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -302,8 +301,8 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
if (num > GFAR_MAX_FIFO_STARVE_OFF)
return count;
- tx_queue = priv->tx_queue;
- spin_lock_irqsave(&tx_queue->txlock, flags);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
priv->fifo_starve_off = num;
@@ -312,7 +311,8 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
temp |= num;
gfar_write(®s->fifo_tx_starve_shutoff, temp);
- spin_unlock_irqrestore(&tx_queue->txlock, flags);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
return count;
}
--
1.5.4.6
^ permalink raw reply related
* [PATCH v4 5/7] gianfar: Add support etsec2.0 registers.
From: Sandeep Gopalpet @ 2009-11-02 17:03 UTC (permalink / raw)
To: netdev; +Cc: davem, Sandeep Gopalpet
From: Sandeep Gopalpet <Sandeep.Kumar@freescale.com>
This patch adds support for etsec2.0 regsiters
Signed-off-by: Sandeep Gopalpet <Sandeep.Kumar@freescale.com>
---
drivers/net/gianfar.h | 55 +++++++++++++++++++++++++++++++++++++++---------
1 files changed, 44 insertions(+), 11 deletions(-)
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 5ae769d..08518c2 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -566,25 +566,32 @@ struct gfar_stats {
struct gfar {
u32 tsec_id; /* 0x.000 - Controller ID register */
- u8 res1[12];
+ u32 tsec_id2; /* 0x.004 - Controller ID2 register */
+ u8 res1[8];
u32 ievent; /* 0x.010 - Interrupt Event Register */
u32 imask; /* 0x.014 - Interrupt Mask Register */
u32 edis; /* 0x.018 - Error Disabled Register */
- u8 res2[4];
+ u32 emapg; /* 0x.01c - Group Error mapping register */
u32 ecntrl; /* 0x.020 - Ethernet Control Register */
u32 minflr; /* 0x.024 - Minimum Frame Length Register */
u32 ptv; /* 0x.028 - Pause Time Value Register */
u32 dmactrl; /* 0x.02c - DMA Control Register */
u32 tbipa; /* 0x.030 - TBI PHY Address Register */
- u8 res3[88];
+ u8 res2[28];
+ u32 fifo_rx_pause; /* 0x.050 - FIFO receive pause start threshold
+ register */
+ u32 fifo_rx_pause_shutoff; /* x.054 - FIFO receive starve shutoff
+ register */
+ u32 fifo_rx_alarm; /* 0x.058 - FIFO receive alarm start threshold
+ register */
+ u32 fifo_rx_alarm_shutoff; /*0x.05c - FIFO receive alarm starve
+ shutoff register */
+ u8 res3[44];
u32 fifo_tx_thr; /* 0x.08c - FIFO transmit threshold register */
u8 res4[8];
u32 fifo_tx_starve; /* 0x.098 - FIFO transmit starve register */
u32 fifo_tx_starve_shutoff; /* 0x.09c - FIFO transmit starve shutoff register */
- u8 res5[4];
- u32 fifo_rx_pause; /* 0x.0a4 - FIFO receive pause threshold register */
- u32 fifo_rx_alarm; /* 0x.0a8 - FIFO receive alarm threshold register */
- u8 res6[84];
+ u8 res5[96];
u32 tctrl; /* 0x.100 - Transmit Control Register */
u32 tstat; /* 0x.104 - Transmit Status Register */
u32 dfvlan; /* 0x.108 - Default VLAN Control word */
@@ -635,7 +642,11 @@ struct gfar {
u8 res12[8];
u32 rxic; /* 0x.310 - Receive Interrupt Coalescing Configuration Register */
u32 rqueue; /* 0x.314 - Receive queue control register */
- u8 res13[24];
+ u32 rir0; /* 0x.318 - Ring mapping register 0 */
+ u32 rir1; /* 0x.31c - Ring mapping register 1 */
+ u32 rir2; /* 0x.320 - Ring mapping register 2 */
+ u32 rir3; /* 0x.324 - Ring mapping register 3 */
+ u8 res13[8];
u32 rbifx; /* 0x.330 - Receive bit field extract control register */
u32 rqfar; /* 0x.334 - Receive queue filing table address register */
u32 rqfcr; /* 0x.338 - Receive queue filing table control register */
@@ -684,7 +695,7 @@ struct gfar {
u32 maxfrm; /* 0x.510 - Maximum Frame Length Register */
u8 res18[12];
u8 gfar_mii_regs[24]; /* See gianfar_phy.h */
- u8 res19[4];
+ u32 ifctrl; /* 0x.538 - Interface control register */
u32 ifstat; /* 0x.53c - Interface Status Register */
u32 macstnaddr1; /* 0x.540 - Station Address Part 1 Register */
u32 macstnaddr2; /* 0x.544 - Station Address Part 2 Register */
@@ -745,8 +756,30 @@ struct gfar {
u8 res23c[248];
u32 attr; /* 0x.bf8 - Attributes Register */
u32 attreli; /* 0x.bfc - Attributes Extract Length and Extract Index Register */
- u8 res24[1024];
-
+ u8 res24[688];
+ u32 isrg0; /* 0x.eb0 - Interrupt steering group 0 register */
+ u32 isrg1; /* 0x.eb4 - Interrupt steering group 1 register */
+ u32 isrg2; /* 0x.eb8 - Interrupt steering group 2 register */
+ u32 isrg3; /* 0x.ebc - Interrupt steering group 3 register */
+ u8 res25[16];
+ u32 rxic0; /* 0x.ed0 - Ring 0 Rx interrupt coalescing */
+ u32 rxic1; /* 0x.ed4 - Ring 1 Rx interrupt coalescing */
+ u32 rxic2; /* 0x.ed8 - Ring 2 Rx interrupt coalescing */
+ u32 rxic3; /* 0x.edc - Ring 3 Rx interrupt coalescing */
+ u32 rxic4; /* 0x.ee0 - Ring 4 Rx interrupt coalescing */
+ u32 rxic5; /* 0x.ee4 - Ring 5 Rx interrupt coalescing */
+ u32 rxic6; /* 0x.ee8 - Ring 6 Rx interrupt coalescing */
+ u32 rxic7; /* 0x.eec - Ring 7 Rx interrupt coalescing */
+ u8 res26[32];
+ u32 txic0; /* 0x.f10 - Ring 0 Tx interrupt coalescing */
+ u32 txic1; /* 0x.f14 - Ring 1 Tx interrupt coalescing */
+ u32 txic2; /* 0x.f18 - Ring 2 Tx interrupt coalescing */
+ u32 txic3; /* 0x.f1c - Ring 3 Tx interrupt coalescing */
+ u32 txic4; /* 0x.f20 - Ring 4 Tx interrupt coalescing */
+ u32 txic5; /* 0x.f24 - Ring 5 Tx interrupt coalescing */
+ u32 txic6; /* 0x.f28 - Ring 6 Tx interrupt coalescing */
+ u32 txic7; /* 0x.f2c - Ring 7 Tx interrupt coalescing */
+ u8 res27[208];
};
/* Flags related to gianfar device features */
--
1.5.4.6
^ permalink raw reply related
* [PATCH v4 6/7] gianfar: Add Multiple group Support
From: Sandeep Gopalpet @ 2009-11-02 17:03 UTC (permalink / raw)
To: netdev; +Cc: davem, Sandeep Gopalpet
From: Sandeep Gopalpet <Sandeep.Kumar@freescale.com>
This patch introduces multiple group support for etsec2.0
devices.
Multiple group support is provided by mapping the set of enabled
queues to different groups and then programming the per group
regsiters imask, ievent, rstat, tstat.
The queues corresponding to a group are indicated by programming
isrg (interrupt steering) registers.
Signed-off-by: Sandeep Gopalpet <Sandeep.Kumar@freescale.com>
---
drivers/net/gianfar.c | 532 +++++++++++++++++++++++++++--------------
drivers/net/gianfar.h | 29 +++-
drivers/net/gianfar_ethtool.c | 58 +++--
drivers/net/gianfar_sysfs.c | 12 +-
4 files changed, 422 insertions(+), 209 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index aa258e8..dc9fba0 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -326,7 +326,7 @@ cleanup:
static void gfar_init_tx_rx_base(struct gfar_private *priv)
{
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 *baddr;
int i;
@@ -346,7 +346,7 @@ static void gfar_init_tx_rx_base(struct gfar_private *priv)
static void gfar_init_mac(struct net_device *ndev)
{
struct gfar_private *priv = netdev_priv(ndev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 rctrl = 0;
u32 tctrl = 0;
u32 attrs = 0;
@@ -355,13 +355,7 @@ static void gfar_init_mac(struct net_device *ndev)
gfar_init_tx_rx_base(priv);
/* Configure the coalescing support */
- gfar_write(®s->txic, 0);
- if (priv->tx_queue[0]->txcoalescing)
- gfar_write(®s->txic, priv->tx_queue[0]->txic);
-
- gfar_write(®s->rxic, 0);
- if (priv->rx_queue[0]->rxcoalescing)
- gfar_write(®s->rxic, priv->rx_queue[0]->rxic);
+ gfar_configure_coalescing(priv, 0xFF, 0xFF);
if (priv->rx_filer_enable)
rctrl |= RCTRL_FILREN;
@@ -495,16 +489,91 @@ static void free_rx_pointers(struct gfar_private *priv)
kfree(priv->rx_queue[i]);
}
+static void unmap_group_regs(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < MAXGROUPS; i++)
+ if (priv->gfargrp[i].regs)
+ iounmap(priv->gfargrp[i].regs);
+}
+
+static void disable_napi(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < priv->num_grps; i++)
+ napi_disable(&priv->gfargrp[i].napi);
+}
+
+static void enable_napi(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < priv->num_grps; i++)
+ napi_enable(&priv->gfargrp[i].napi);
+}
+
+static int gfar_parse_group(struct device_node *np,
+ struct gfar_private *priv, const char *model)
+{
+ u32 *queue_mask;
+ u64 addr, size;
+
+ addr = of_translate_address(np,
+ of_get_address(np, 0, &size, NULL));
+ priv->gfargrp[priv->num_grps].regs = ioremap(addr, size);
+
+ if (!priv->gfargrp[priv->num_grps].regs)
+ return -ENOMEM;
+
+ priv->gfargrp[priv->num_grps].interruptTransmit =
+ irq_of_parse_and_map(np, 0);
+
+ /* If we aren't the FEC we have multiple interrupts */
+ if (model && strcasecmp(model, "FEC")) {
+ priv->gfargrp[priv->num_grps].interruptReceive =
+ irq_of_parse_and_map(np, 1);
+ priv->gfargrp[priv->num_grps].interruptError =
+ irq_of_parse_and_map(np,2);
+ if (priv->gfargrp[priv->num_grps].interruptTransmit < 0 ||
+ priv->gfargrp[priv->num_grps].interruptReceive < 0 ||
+ priv->gfargrp[priv->num_grps].interruptError < 0) {
+ return -EINVAL;
+ }
+ }
+
+ priv->gfargrp[priv->num_grps].grp_id = priv->num_grps;
+ priv->gfargrp[priv->num_grps].priv = priv;
+ spin_lock_init(&priv->gfargrp[priv->num_grps].grplock);
+ if(priv->mode == MQ_MG_MODE) {
+ queue_mask = (u32 *)of_get_property(np,
+ "fsl,rx-bit-map", NULL);
+ priv->gfargrp[priv->num_grps].rx_bit_map =
+ queue_mask ? *queue_mask :(DEFAULT_MAPPING >> priv->num_grps);
+ queue_mask = (u32 *)of_get_property(np,
+ "fsl,tx-bit-map", NULL);
+ priv->gfargrp[priv->num_grps].tx_bit_map =
+ queue_mask ? *queue_mask : (DEFAULT_MAPPING >> priv->num_grps);
+ } else {
+ priv->gfargrp[priv->num_grps].rx_bit_map = 0xFF;
+ priv->gfargrp[priv->num_grps].tx_bit_map = 0xFF;
+ }
+ priv->num_grps++;
+
+ return 0;
+}
+
static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
{
const char *model;
const char *ctype;
const void *mac_addr;
- u64 addr, size;
int err = 0, i;
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
struct device_node *np = ofdev->node;
+ struct device_node *child = NULL;
const u32 *stash;
const u32 *stash_len;
const u32 *stash_idx;
@@ -548,36 +617,26 @@ static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
dev->real_num_tx_queues = num_tx_qs;
priv->num_tx_queues = num_tx_qs;
priv->num_rx_queues = num_rx_qs;
-
- /* get a pointer to the register memory */
- addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
- priv->gfargrp.regs = ioremap(addr, size);
-
- if (priv->gfargrp.regs == NULL) {
- err = -ENOMEM;
- goto err_out;
- }
-
- priv->gfargrp.priv = priv; /* back pointer from group to priv */
- priv->gfargrp.rx_bit_map = DEFAULT_MAPPING;
- priv->gfargrp.tx_bit_map = DEFAULT_MAPPING;
-
- priv->gfargrp.interruptTransmit = irq_of_parse_and_map(np, 0);
+ priv->num_grps = 0x0;
model = of_get_property(np, "model", NULL);
- /* If we aren't the FEC we have multiple interrupts */
- if (model && strcasecmp(model, "FEC")) {
- priv->gfargrp.interruptReceive = irq_of_parse_and_map(np, 1);
+ for (i = 0; i < MAXGROUPS; i++)
+ priv->gfargrp[i].regs = NULL;
- priv->gfargrp.interruptError = irq_of_parse_and_map(np, 2);
-
- if (priv->gfargrp.interruptTransmit < 0 ||
- priv->gfargrp.interruptReceive < 0 ||
- priv->gfargrp.interruptError < 0) {
- err = -EINVAL;
- goto err_out;
+ /* Parse and initialize group specific information */
+ if (of_device_is_compatible(np, "fsl,etsec2")) {
+ priv->mode = MQ_MG_MODE;
+ for_each_child_of_node(np, child) {
+ err = gfar_parse_group(child, priv, model);
+ if (err)
+ goto err_grp_init;
}
+ } else {
+ priv->mode = SQ_SG_MODE;
+ err = gfar_parse_group(np, priv, model);
+ if(err)
+ goto err_grp_init;
}
for (i = 0; i < priv->num_tx_queues; i++)
@@ -676,8 +735,8 @@ rx_alloc_failed:
free_rx_pointers(priv);
tx_alloc_failed:
free_tx_pointers(priv);
-err_out:
- iounmap(priv->gfargrp.regs);
+err_grp_init:
+ unmap_group_regs(priv);
free_netdev(dev);
return err;
}
@@ -716,9 +775,11 @@ static int gfar_probe(struct of_device *ofdev,
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
struct gfar __iomem *regs = NULL;
- int err = 0, i;
+ int err = 0, i, grp_idx = 0;
int len_devname;
u32 rstat = 0, tstat = 0, rqueue = 0, tqueue = 0;
+ u32 isrg = 0;
+ u32 *baddr;
err = gfar_of_init(ofdev, &dev);
@@ -731,12 +792,11 @@ static int gfar_probe(struct of_device *ofdev,
priv->node = ofdev->node;
SET_NETDEV_DEV(dev, &ofdev->dev);
- spin_lock_init(&priv->gfargrp.grplock);
spin_lock_init(&priv->bflock);
INIT_WORK(&priv->reset_task, gfar_reset_task);
dev_set_drvdata(&ofdev->dev, priv);
- regs = priv->gfargrp.regs;
+ regs = priv->gfargrp[0].regs;
/* Stop the DMA engine now, in case it was running before */
/* (The firmware could have used it, and left it running). */
@@ -769,7 +829,8 @@ static int gfar_probe(struct of_device *ofdev,
dev->ethtool_ops = &gfar_ethtool_ops;
/* Register for napi ...We are registering NAPI for each grp */
- netif_napi_add(dev, &priv->gfargrp.napi, gfar_poll, GFAR_DEV_WEIGHT);
+ for (i = 0; i < priv->num_grps; i++)
+ netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll, GFAR_DEV_WEIGHT);
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
priv->rx_csum_enable = 1;
@@ -825,25 +886,51 @@ static int gfar_probe(struct of_device *ofdev,
if (dev->features & NETIF_F_IP_CSUM)
dev->hard_header_len += GMAC_FCB_LEN;
+ /* Program the isrg regs only if number of grps > 1 */
+ if (priv->num_grps > 1) {
+ baddr = ®s->isrg0;
+ for (i = 0; i < priv->num_grps; i++) {
+ isrg |= (priv->gfargrp[i].rx_bit_map << ISRG_SHIFT_RX);
+ isrg |= (priv->gfargrp[i].tx_bit_map << ISRG_SHIFT_TX);
+ gfar_write(baddr, isrg);
+ baddr++;
+ isrg = 0x0;
+ }
+ }
+
/* Need to reverse the bit maps as bit_map's MSB is q0
* but, for_each_bit parses from right to left, which
* basically reverses the queue numbers */
- priv->gfargrp.tx_bit_map = reverse_bitmap(priv->gfargrp.tx_bit_map, MAX_TX_QS);
- priv->gfargrp.rx_bit_map = reverse_bitmap(priv->gfargrp.rx_bit_map, MAX_RX_QS);
-
- /* Calculate RSTAT, TSTAT, RQUEUE and TQUEUE values */
- for_each_bit(i, &priv->gfargrp.rx_bit_map, priv->num_rx_queues) {
- priv->gfargrp.num_rx_queues++;
- rstat = rstat | (RSTAT_CLEAR_RHALT >> i);
- rqueue = rqueue | ((RQUEUE_EN0 | RQUEUE_EX0) >> i);
- }
- for_each_bit (i, &priv->gfargrp.tx_bit_map, priv->num_tx_queues) {
- priv->gfargrp.num_tx_queues++;
- tstat = tstat | (TSTAT_CLEAR_THALT >> i);
- tqueue = tqueue | (TQUEUE_EN0 >> i);
+ for (i = 0; i< priv->num_grps; i++) {
+ priv->gfargrp[i].tx_bit_map = reverse_bitmap(
+ priv->gfargrp[i].tx_bit_map, MAX_TX_QS);
+ priv->gfargrp[i].rx_bit_map = reverse_bitmap(
+ priv->gfargrp[i].rx_bit_map, MAX_RX_QS);
+ }
+
+ /* Calculate RSTAT, TSTAT, RQUEUE and TQUEUE values,
+ * also assign queues to groups */
+ for (grp_idx = 0; grp_idx < priv->num_grps; grp_idx++) {
+ priv->gfargrp[grp_idx].num_rx_queues = 0x0;
+ for_each_bit(i, &priv->gfargrp[grp_idx].rx_bit_map,
+ priv->num_rx_queues) {
+ priv->gfargrp[grp_idx].num_rx_queues++;
+ priv->rx_queue[i]->grp = &priv->gfargrp[grp_idx];
+ rstat = rstat | (RSTAT_CLEAR_RHALT >> i);
+ rqueue = rqueue | ((RQUEUE_EN0 | RQUEUE_EX0) >> i);
+ }
+ priv->gfargrp[grp_idx].num_tx_queues = 0x0;
+ for_each_bit (i, &priv->gfargrp[grp_idx].tx_bit_map,
+ priv->num_tx_queues) {
+ priv->gfargrp[grp_idx].num_tx_queues++;
+ priv->tx_queue[i]->grp = &priv->gfargrp[grp_idx];
+ tstat = tstat | (TSTAT_CLEAR_THALT >> i);
+ tqueue = tqueue | (TQUEUE_EN0 >> i);
+ }
+ priv->gfargrp[grp_idx].rstat = rstat;
+ priv->gfargrp[grp_idx].tstat = tstat;
+ rstat = tstat =0;
}
- priv->gfargrp.rstat = rstat;
- priv->gfargrp.tstat = tstat;
gfar_write(®s->rqueue, rqueue);
gfar_write(®s->tqueue, tqueue);
@@ -883,20 +970,40 @@ static int gfar_probe(struct of_device *ofdev,
/* fill out IRQ number and name fields */
len_devname = strlen(dev->name);
- strncpy(&priv->gfargrp.int_name_tx[0], dev->name, len_devname);
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- strncpy(&priv->gfargrp.int_name_tx[len_devname],
- "_tx", sizeof("_tx") + 1);
-
- strncpy(&priv->gfargrp.int_name_rx[0], dev->name, len_devname);
- strncpy(&priv->gfargrp.int_name_rx[len_devname],
- "_rx", sizeof("_rx") + 1);
-
- strncpy(&priv->gfargrp.int_name_er[0], dev->name, len_devname);
- strncpy(&priv->gfargrp.int_name_er[len_devname],
- "_er", sizeof("_er") + 1);
- } else
- priv->gfargrp.int_name_tx[len_devname] = '\0';
+ for (i = 0; i < priv->num_grps; i++) {
+ strncpy(&priv->gfargrp[i].int_name_tx[0], dev->name,
+ len_devname);
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+ strncpy(&priv->gfargrp[i].int_name_tx[len_devname],
+ "_g", sizeof("_g"));
+ priv->gfargrp[i].int_name_tx[
+ strlen(priv->gfargrp[i].int_name_tx)] = i+48;
+ strncpy(&priv->gfargrp[i].int_name_tx[strlen(
+ priv->gfargrp[i].int_name_tx)],
+ "_tx", sizeof("_tx") + 1);
+
+ strncpy(&priv->gfargrp[i].int_name_rx[0], dev->name,
+ len_devname);
+ strncpy(&priv->gfargrp[i].int_name_rx[len_devname],
+ "_g", sizeof("_g"));
+ priv->gfargrp[i].int_name_rx[
+ strlen(priv->gfargrp[i].int_name_rx)] = i+48;
+ strncpy(&priv->gfargrp[i].int_name_rx[strlen(
+ priv->gfargrp[i].int_name_rx)],
+ "_rx", sizeof("_rx") + 1);
+
+ strncpy(&priv->gfargrp[i].int_name_er[0], dev->name,
+ len_devname);
+ strncpy(&priv->gfargrp[i].int_name_er[len_devname],
+ "_g", sizeof("_g"));
+ priv->gfargrp[i].int_name_er[strlen(
+ priv->gfargrp[i].int_name_er)] = i+48;
+ strncpy(&priv->gfargrp[i].int_name_er[strlen(\
+ priv->gfargrp[i].int_name_er)],
+ "_er", sizeof("_er") + 1);
+ } else
+ priv->gfargrp[i].int_name_tx[len_devname] = '\0';
+ }
/* Create all the sysfs files */
gfar_init_sysfs(dev);
@@ -917,7 +1024,7 @@ static int gfar_probe(struct of_device *ofdev,
return 0;
register_fail:
- iounmap(priv->gfargrp.regs);
+ unmap_group_regs(priv);
free_tx_pointers(priv);
free_rx_pointers(priv);
if (priv->phy_node)
@@ -940,7 +1047,7 @@ static int gfar_remove(struct of_device *ofdev)
dev_set_drvdata(&ofdev->dev, NULL);
unregister_netdev(priv->ndev);
- iounmap(priv->gfargrp.regs);
+ unmap_group_regs(priv);
free_netdev(priv->ndev);
return 0;
@@ -952,7 +1059,7 @@ static int gfar_suspend(struct device *dev)
{
struct gfar_private *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
- struct gfar __iomem *regs = NULL;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned long flags;
u32 tempval;
@@ -960,7 +1067,6 @@ static int gfar_suspend(struct device *dev)
(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
netif_device_detach(ndev);
- regs = priv->gfargrp.regs;
if (netif_running(ndev)) {
@@ -984,7 +1090,7 @@ static int gfar_suspend(struct device *dev)
unlock_tx_qs(priv);
local_irq_restore(flags);
- napi_disable(&priv->gfargrp.napi);
+ disable_napi(priv);
if (magic_packet) {
/* Enable interrupt on Magic Packet */
@@ -1006,7 +1112,7 @@ static int gfar_resume(struct device *dev)
{
struct gfar_private *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
- struct gfar __iomem *regs = NULL;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned long flags;
u32 tempval;
int magic_packet = priv->wol_en &&
@@ -1023,8 +1129,6 @@ static int gfar_resume(struct device *dev)
/* Disable Magic Packet mode, in case something
* else woke us up.
*/
- regs = priv->gfargrp.regs;
-
local_irq_save(flags);
lock_tx_qs(priv);
lock_rx_qs(priv);
@@ -1041,7 +1145,7 @@ static int gfar_resume(struct device *dev)
netif_device_attach(ndev);
- napi_enable(&priv->gfargrp.napi);
+ enable_napi(priv);
return 0;
}
@@ -1107,10 +1211,9 @@ static int gfar_legacy_resume(struct of_device *ofdev)
static phy_interface_t gfar_get_interface(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = NULL;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 ecntrl;
- regs = priv->gfargrp.regs;
ecntrl = gfar_read(®s->ecntrl);
if (ecntrl & ECNTRL_SGMII_MODE)
@@ -1234,14 +1337,18 @@ static void init_registers(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar __iomem *regs = NULL;
+ int i = 0;
- regs = priv->gfargrp.regs;
- /* Clear IEVENT */
- gfar_write(®s->ievent, IEVENT_INIT_CLEAR);
+ for (i = 0; i < priv->num_grps; i++) {
+ regs = priv->gfargrp[i].regs;
+ /* Clear IEVENT */
+ gfar_write(®s->ievent, IEVENT_INIT_CLEAR);
- /* Initialize IMASK */
- gfar_write(®s->imask, IMASK_INIT_CLEAR);
+ /* Initialize IMASK */
+ gfar_write(®s->imask, IMASK_INIT_CLEAR);
+ }
+ regs = priv->gfargrp[0].regs;
/* Init hash registers to zero */
gfar_write(®s->igaddr0, 0);
gfar_write(®s->igaddr1, 0);
@@ -1282,15 +1389,20 @@ static void init_registers(struct net_device *dev)
static void gfar_halt_nodisable(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = NULL;
u32 tempval;
+ int i = 0;
- /* Mask all interrupts */
- gfar_write(®s->imask, IMASK_INIT_CLEAR);
+ for (i = 0; i < priv->num_grps; i++) {
+ regs = priv->gfargrp[i].regs;
+ /* Mask all interrupts */
+ gfar_write(®s->imask, IMASK_INIT_CLEAR);
- /* Clear all interrupts */
- gfar_write(®s->ievent, IEVENT_INIT_CLEAR);
+ /* Clear all interrupts */
+ gfar_write(®s->ievent, IEVENT_INIT_CLEAR);
+ }
+ regs = priv->gfargrp[0].regs;
/* Stop the DMA, and wait for it to stop */
tempval = gfar_read(®s->dmactrl);
if ((tempval & (DMACTRL_GRS | DMACTRL_GTS))
@@ -1308,7 +1420,7 @@ static void gfar_halt_nodisable(struct net_device *dev)
void gfar_halt(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
gfar_halt_nodisable(dev);
@@ -1319,10 +1431,18 @@ void gfar_halt(struct net_device *dev)
gfar_write(®s->maccfg1, tempval);
}
+static void free_grp_irqs(struct gfar_priv_grp *grp)
+{
+ free_irq(grp->interruptError, grp);
+ free_irq(grp->interruptTransmit, grp);
+ free_irq(grp->interruptReceive, grp);
+}
+
void stop_gfar(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
unsigned long flags;
+ int i;
phy_stop(priv->phydev);
@@ -1340,11 +1460,12 @@ void stop_gfar(struct net_device *dev)
/* Free the IRQs */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- free_irq(priv->gfargrp.interruptError, &priv->gfargrp);
- free_irq(priv->gfargrp.interruptTransmit, &priv->gfargrp);
- free_irq(priv->gfargrp.interruptReceive, &priv->gfargrp);
+ for (i = 0; i < priv->num_grps; i++)
+ free_grp_irqs(&priv->gfargrp[i]);
} else {
- free_irq(priv->gfargrp.interruptTransmit, &priv->gfargrp);
+ for (i = 0; i < priv->num_grps; i++)
+ free_irq(priv->gfargrp[i].interruptTransmit,
+ &priv->gfargrp[i]);
}
free_skb_resources(priv);
@@ -1432,8 +1553,9 @@ static void free_skb_resources(struct gfar_private *priv)
void gfar_start(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
+ int i = 0;
/* Enable Rx and Tx in MACCFG1 */
tempval = gfar_read(®s->maccfg1);
@@ -1450,92 +1572,149 @@ void gfar_start(struct net_device *dev)
tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
gfar_write(®s->dmactrl, tempval);
- /* Clear THLT/RHLT, so that the DMA starts polling now */
- gfar_write(®s->tstat, priv->gfargrp.tstat);
- gfar_write(®s->rstat, priv->gfargrp.rstat);
-
- /* Unmask the interrupts we look for */
- gfar_write(®s->imask, IMASK_DEFAULT);
+ for (i = 0; i < priv->num_grps; i++) {
+ regs = priv->gfargrp[i].regs;
+ /* Clear THLT/RHLT, so that the DMA starts polling now */
+ gfar_write(®s->tstat, priv->gfargrp[i].tstat);
+ gfar_write(®s->rstat, priv->gfargrp[i].rstat);
+ /* Unmask the interrupts we look for */
+ gfar_write(®s->imask, IMASK_DEFAULT);
+ }
dev->trans_start = jiffies;
}
-/* Bring the controller up and running */
-int startup_gfar(struct net_device *ndev)
+void gfar_configure_coalescing(struct gfar_private *priv,
+ unsigned int tx_mask, unsigned int rx_mask)
{
- struct gfar_private *priv = netdev_priv(ndev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
- int err;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 *baddr;
+ int i = 0;
- gfar_write(®s->imask, IMASK_INIT_CLEAR);
+ /* Backward compatible case ---- even if we enable
+ * multiple queues, there's only single reg to program
+ */
+ gfar_write(®s->txic, 0);
+ if(likely(priv->tx_queue[0]->txcoalescing))
+ gfar_write(®s->txic, priv->tx_queue[0]->txic);
- err = gfar_alloc_skb_resources(ndev);
- if (err)
- return err;
+ gfar_write(®s->rxic, 0);
+ if(unlikely(priv->rx_queue[0]->rxcoalescing))
+ gfar_write(®s->rxic, priv->rx_queue[0]->rxic);
- gfar_init_mac(ndev);
+ if (priv->mode == MQ_MG_MODE) {
+ baddr = ®s->txic0;
+ for_each_bit (i, &tx_mask, priv->num_tx_queues) {
+ if (likely(priv->tx_queue[i]->txcoalescing)) {
+ gfar_write(baddr + i, 0);
+ gfar_write(baddr + i, priv->tx_queue[i]->txic);
+ }
+ }
+
+ baddr = ®s->rxic0;
+ for_each_bit (i, &rx_mask, priv->num_rx_queues) {
+ if (likely(priv->rx_queue[i]->rxcoalescing)) {
+ gfar_write(baddr + i, 0);
+ gfar_write(baddr + i, priv->rx_queue[i]->rxic);
+ }
+ }
+ }
+}
+
+static int register_grp_irqs(struct gfar_priv_grp *grp)
+{
+ struct gfar_private *priv = grp->priv;
+ struct net_device *dev = priv->ndev;
+ int err;
/* If the device has multiple interrupts, register for
* them. Otherwise, only register for the one */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
/* Install our interrupt handlers for Error,
* Transmit, and Receive */
- err = request_irq(priv->gfargrp.interruptError, gfar_error, 0,
- priv->gfargrp.int_name_er, &priv->gfargrp);
- if (err) {
+ if ((err = request_irq(grp->interruptError, gfar_error, 0,
+ grp->int_name_er,grp)) < 0) {
if (netif_msg_intr(priv))
- pr_err("%s: Can't get IRQ %d\n", ndev->name,
- priv->gfargrp.interruptError);
- goto err_irq_fail;
+ printk(KERN_ERR "%s: Can't get IRQ %d\n",
+ dev->name, grp->interruptError);
+
+ goto err_irq_fail;
}
- err = request_irq(priv->gfargrp.interruptTransmit,
- gfar_transmit, 0,
- priv->gfargrp.int_name_tx,
- &priv->gfargrp);
- if (err) {
+ if ((err = request_irq(grp->interruptTransmit, gfar_transmit,
+ 0, grp->int_name_tx, grp)) < 0) {
if (netif_msg_intr(priv))
- pr_err("%s: Can't get IRQ %d\n", ndev->name,
- priv->gfargrp.interruptTransmit);
+ printk(KERN_ERR "%s: Can't get IRQ %d\n",
+ dev->name, grp->interruptTransmit);
goto tx_irq_fail;
}
- err = request_irq(priv->gfargrp.interruptReceive,
- gfar_receive, 0,
- priv->gfargrp.int_name_rx,
- &priv->gfargrp);
- if (err) {
+ if ((err = request_irq(grp->interruptReceive, gfar_receive, 0,
+ grp->int_name_rx, grp)) < 0) {
if (netif_msg_intr(priv))
- pr_err("%s: Can't get IRQ %d (receive0)\n",
- ndev->name,
- priv->gfargrp.interruptReceive);
+ printk(KERN_ERR "%s: Can't get IRQ %d\n",
+ dev->name, grp->interruptReceive);
goto rx_irq_fail;
}
} else {
- err = request_irq(priv->gfargrp.interruptTransmit,
- gfar_interrupt, 0,
- priv->gfargrp.int_name_tx,
- &priv->gfargrp);
- if (err) {
+ if ((err = request_irq(grp->interruptTransmit, gfar_interrupt, 0,
+ grp->int_name_tx, grp)) < 0) {
if (netif_msg_intr(priv))
- pr_err("%s: Can't get IRQ %d\n", ndev->name,
- priv->gfargrp.interruptTransmit);
+ printk(KERN_ERR "%s: Can't get IRQ %d\n",
+ dev->name, grp->interruptTransmit);
goto err_irq_fail;
}
}
+ return 0;
+
+rx_irq_fail:
+ free_irq(grp->interruptTransmit, grp);
+tx_irq_fail:
+ free_irq(grp->interruptError, grp);
+err_irq_fail:
+ return err;
+
+}
+
+/* Bring the controller up and running */
+int startup_gfar(struct net_device *ndev)
+{
+ struct gfar_private *priv = netdev_priv(ndev);
+ struct gfar __iomem *regs = NULL;
+ int err, i, j;
+
+ for (i = 0; i < priv->num_grps; i++) {
+ regs= priv->gfargrp[i].regs;
+ gfar_write(®s->imask, IMASK_INIT_CLEAR);
+ }
+
+ regs= priv->gfargrp[0].regs;
+ err = gfar_alloc_skb_resources(ndev);
+ if (err)
+ return err;
+
+ gfar_init_mac(ndev);
+
+ for (i = 0; i < priv->num_grps; i++) {
+ err = register_grp_irqs(&priv->gfargrp[i]);
+ if (err) {
+ for (j = 0; j < i; j++)
+ free_grp_irqs(&priv->gfargrp[j]);
+ goto irq_fail;
+ }
+ }
+
/* Start the controller */
gfar_start(ndev);
phy_start(priv->phydev);
+ gfar_configure_coalescing(priv, 0xFF, 0xFF);
+
return 0;
-rx_irq_fail:
- free_irq(priv->gfargrp.interruptTransmit, &priv->gfargrp);
-tx_irq_fail:
- free_irq(priv->gfargrp.interruptError, &priv->gfargrp);
-err_irq_fail:
+irq_fail:
free_skb_resources(priv);
return err;
}
@@ -1547,7 +1726,7 @@ static int gfar_enet_open(struct net_device *dev)
struct gfar_private *priv = netdev_priv(dev);
int err;
- napi_enable(&priv->gfargrp.napi);
+ enable_napi(priv);
skb_queue_head_init(&priv->rx_recycle);
@@ -1559,13 +1738,13 @@ static int gfar_enet_open(struct net_device *dev)
err = init_phy(dev);
if (err) {
- napi_disable(&priv->gfargrp.napi);
+ disable_napi(priv);
return err;
}
err = startup_gfar(dev);
if (err) {
- napi_disable(&priv->gfargrp.napi);
+ disable_napi(priv);
return err;
}
@@ -1654,7 +1833,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_queue = priv->tx_queue[rq];
txq = netdev_get_tx_queue(dev, rq);
base = tx_queue->tx_bd_base;
- regs = priv->gfargrp.regs;
+ regs = tx_queue->grp->regs;
/* make space for additional header when fcb is needed */
if (((skb->ip_summed == CHECKSUM_PARTIAL) ||
@@ -1791,7 +1970,7 @@ static int gfar_close(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- napi_disable(&priv->gfargrp.napi);
+ disable_napi(priv);
skb_queue_purge(&priv->rx_recycle);
cancel_work_sync(&priv->reset_task);
@@ -1824,7 +2003,7 @@ static void gfar_vlan_rx_register(struct net_device *dev,
unsigned long flags;
u32 tempval;
- regs = priv->gfargrp.regs;
+ regs = priv->gfargrp[0].regs;
local_irq_save(flags);
lock_rx_qs(priv);
@@ -1868,7 +2047,7 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
{
int tempsize, tempval;
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
int oldsize = priv->rx_buffer_size;
int frame_size = new_mtu + ETH_HLEN;
@@ -2290,7 +2469,7 @@ static int gfar_poll(struct napi_struct *napi, int budget)
struct gfar_priv_grp *gfargrp = container_of(napi,
struct gfar_priv_grp, napi);
struct gfar_private *priv = gfargrp->priv;
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = gfargrp->regs;
struct gfar_priv_tx_q *tx_queue = NULL;
struct gfar_priv_rx_q *rx_queue = NULL;
int rx_cleaned = 0, budget_per_queue = 0, rx_cleaned_per_queue = 0;
@@ -2349,14 +2528,8 @@ static int gfar_poll(struct napi_struct *napi, int budget)
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
- if (likely(rx_queue->rxcoalescing)) {
- gfar_write(®s->rxic, 0);
- gfar_write(®s->rxic, rx_queue->rxic);
- }
- if (likely(tx_queue->txcoalescing)) {
- gfar_write(®s->txic, 0);
- gfar_write(®s->txic, tx_queue->txic);
- }
+ gfar_configure_coalescing(priv,
+ gfargrp->rx_bit_map, gfargrp->tx_bit_map);
}
return rx_cleaned;
@@ -2371,20 +2544,26 @@ static int gfar_poll(struct napi_struct *napi, int budget)
static void gfar_netpoll(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
+ int i = 0;
/* If the device has multiple interrupts, run tx/rx */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- disable_irq(priv->gfargrp.interruptTransmit);
- disable_irq(priv->gfargrp.interruptReceive);
- disable_irq(priv->gfargrp.interruptError);
- gfar_interrupt(priv->gfargrp.interruptTransmit, &priv->gfargrp);
- enable_irq(priv->gfargrp.interruptError);
- enable_irq(priv->gfargrp.interruptReceive);
- enable_irq(priv->gfargrp.interruptTransmit);
+ for (i = 0; i < priv->num_grps; i++) {
+ disable_irq(priv->gfargrp[i].interruptTransmit);
+ disable_irq(priv->gfargrp[i].interruptReceive);
+ disable_irq(priv->gfargrp[i].interruptError);
+ gfar_interrupt(priv->gfargrp[i].interruptTransmit,
+ &priv->gfargrp[i]);
+ enable_irq(priv->gfargrp[i].interruptError);
+ enable_irq(priv->gfargrp[i].interruptReceive);
+ enable_irq(priv->gfargrp[i].interruptTransmit);
+ }
} else {
- disable_irq(priv->gfargrp.interruptTransmit);
- gfar_interrupt(priv->gfargrp.interruptTransmit, &priv->gfargrp);
- enable_irq(priv->gfargrp.interruptTransmit);
+ for (i = 0; i < priv->num_grps; i++) {
+ disable_irq(priv->gfargrp[i].interruptTransmit);
+ gfar_interrupt(priv->gfargrp[i].interruptTransmit,
+ &priv->gfargrp[i]);
+ enable_irq(priv->gfargrp[i].interruptTransmit);
}
}
#endif
@@ -2421,7 +2600,7 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id)
static void adjust_link(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned long flags;
struct phy_device *phydev = priv->phydev;
int new_state = 0;
@@ -2505,7 +2684,7 @@ static void gfar_set_multi(struct net_device *dev)
{
struct dev_mc_list *mc_ptr;
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
if (dev->flags & IFF_PROMISC) {
@@ -2638,7 +2817,7 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr)
static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
int idx;
char tmpbuf[MAC_ADDR_LEN];
u32 tempval;
@@ -2742,6 +2921,9 @@ static struct of_device_id gfar_match[] =
.type = "network",
.compatible = "gianfar",
},
+ {
+ .compatible = "fsl,etsec2",
+ },
{},
};
MODULE_DEVICE_TABLE(of, gfar_match);
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 08518c2..a2c1f96 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -79,6 +79,9 @@ extern const char gfar_driver_version[];
#define MAX_TX_QS 0x8
#define MAX_RX_QS 0x8
+/* MAXIMUM NUMBER OF GROUPS SUPPORTED */
+#define MAXGROUPS 0x2
+
/* These need to be powers of 2 for this driver */
#define DEFAULT_TX_RING_SIZE 256
#define DEFAULT_RX_RING_SIZE 256
@@ -795,7 +798,24 @@ struct gfar {
#define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200
#define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400
+#if (MAXGROUPS == 2)
+#define DEFAULT_MAPPING 0xAA
+#else
#define DEFAULT_MAPPING 0xFF
+#endif
+
+#define ISRG_SHIFT_TX 0x10
+#define ISRG_SHIFT_RX 0x18
+
+/* The same driver can operate in two modes */
+/* SQ_SG_MODE: Single Queue Single Group Mode
+ * (Backward compatible mode)
+ * MQ_MG_MODE: Multi Queue Multi Group mode
+ */
+enum {
+ SQ_SG_MODE = 0,
+ MQ_MG_MODE
+};
/**
* struct gfar_priv_tx_q - per tx queue structure
@@ -825,6 +845,7 @@ struct gfar_priv_tx_q {
struct txbd8 *cur_tx;
struct txbd8 *dirty_tx;
struct net_device *dev;
+ struct gfar_priv_grp *grp;
u16 skb_curtx;
u16 skb_dirtytx;
u16 qindex;
@@ -858,6 +879,7 @@ struct gfar_priv_rx_q {
struct rxbd8 *rx_bd_base;
struct rxbd8 *cur_rx;
struct net_device *dev;
+ struct gfar_priv_grp *grp;
u16 skb_currx;
u16 qindex;
unsigned int rx_ring_size;
@@ -885,6 +907,7 @@ struct gfar_priv_grp {
struct napi_struct napi;
struct gfar_private *priv;
struct gfar __iomem *regs;
+ unsigned int grp_id;
unsigned int rx_bit_map;
unsigned int tx_bit_map;
unsigned int num_tx_queues;
@@ -916,6 +939,8 @@ struct gfar_private {
/* Indicates how many tx, rx queues are enabled */
unsigned int num_tx_queues;
unsigned int num_rx_queues;
+ unsigned int num_grps;
+ unsigned int mode;
/* The total tx and rx ring size for the enabled queues */
unsigned int total_tx_ring_size;
@@ -925,7 +950,7 @@ struct gfar_private {
struct net_device *ndev;
struct of_device *ofdev;
- struct gfar_priv_grp gfargrp;
+ struct gfar_priv_grp gfargrp[MAXGROUPS];
struct gfar_priv_tx_q *tx_queue[MAX_TX_QS];
struct gfar_priv_rx_q *rx_queue[MAX_RX_QS];
@@ -999,6 +1024,8 @@ extern void stop_gfar(struct net_device *dev);
extern void gfar_halt(struct net_device *dev);
extern void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev,
int enable, u32 regnum, u32 read);
+extern void gfar_configure_coalescing(struct gfar_private *priv,
+ unsigned int tx_mask, unsigned int rx_mask);
void gfar_init_sysfs(struct net_device *dev);
extern const struct ethtool_ops gfar_ethtool_ops;
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index d3d2623..562f6c2 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -137,7 +137,7 @@ static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
{
int i;
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u64 *extra = (u64 *) & priv->extra_stats;
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
@@ -226,7 +226,7 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, voi
{
int i;
struct gfar_private *priv = netdev_priv(dev);
- u32 __iomem *theregs = (u32 __iomem *) priv->gfargrp.regs;
+ u32 __iomem *theregs = (u32 __iomem *) priv->gfargrp[0].regs;
u32 *buf = (u32 *) regbuf;
for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++)
@@ -352,22 +352,23 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->gfargrp.regs;
- struct gfar_priv_tx_q *tx_queue = NULL;
- struct gfar_priv_rx_q *rx_queue = NULL;
+ int i = 0;
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP;
- tx_queue = priv->tx_queue[0];
- rx_queue = priv->rx_queue[0];
-
/* Set up rx coalescing */
+ /* As of now, we will enable/disable coalescing for all
+ * queues together in case of eTSEC2, this will be modified
+ * along with the ethtool interface */
if ((cvals->rx_coalesce_usecs == 0) ||
- (cvals->rx_max_coalesced_frames == 0))
- rx_queue->rxcoalescing = 0;
- else
- rx_queue->rxcoalescing = 1;
+ (cvals->rx_max_coalesced_frames == 0)) {
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i]->rxcoalescing = 0;
+ } else {
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i]->rxcoalescing = 1;
+ }
if (NULL == priv->phydev)
return -ENODEV;
@@ -385,15 +386,21 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
return -EINVAL;
}
- rx_queue->rxic = mk_ic_value(cvals->rx_max_coalesced_frames,
- gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs));
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i]->rxic = mk_ic_value(
+ cvals->rx_max_coalesced_frames,
+ gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs));
+ }
/* Set up tx coalescing */
if ((cvals->tx_coalesce_usecs == 0) ||
- (cvals->tx_max_coalesced_frames == 0))
- tx_queue->txcoalescing = 0;
- else
- tx_queue->txcoalescing = 1;
+ (cvals->tx_max_coalesced_frames == 0)) {
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->tx_queue[i]->txcoalescing = 0;
+ } else {
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->tx_queue[i]->txcoalescing = 1;
+ }
/* Check the bounds of the values */
if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
@@ -408,16 +415,13 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
return -EINVAL;
}
- tx_queue->txic = mk_ic_value(cvals->tx_max_coalesced_frames,
- gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
-
- gfar_write(®s->rxic, 0);
- if (rx_queue->rxcoalescing)
- gfar_write(®s->rxic, rx_queue->rxic);
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ priv->tx_queue[i]->txic = mk_ic_value(
+ cvals->tx_max_coalesced_frames,
+ gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
+ }
- gfar_write(®s->txic, 0);
- if (tx_queue->txcoalescing)
- gfar_write(®s->txic, tx_queue->txic);
+ gfar_configure_coalescing(priv, 0xFF, 0xFF);
return 0;
}
diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c
index 4b726f6..3724835 100644
--- a/drivers/net/gianfar_sysfs.c
+++ b/drivers/net/gianfar_sysfs.c
@@ -50,7 +50,7 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
int new_setting = 0;
u32 temp;
unsigned long flags;
@@ -105,7 +105,7 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -164,7 +164,7 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned short index = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -212,7 +212,7 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -252,7 +252,7 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -293,7 +293,7 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
- struct gfar __iomem *regs = priv->gfargrp.regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
--
1.5.4.6
^ permalink raw reply related
* [PATCH v4 7/7] gianfar: Basic Support for programming hash rules
From: Sandeep Gopalpet @ 2009-11-02 17:03 UTC (permalink / raw)
To: netdev; +Cc: davem, Sandeep Gopalpet
From: Sandeep Gopalpet <Sandeep.Kumar@freescale.com>
This patch provides basic hash rules programming via the ethtool
interface.
Signed-off-by: Sandeep Gopalpet <Sandeep.Kumar@freescale.com>
---
drivers/net/gianfar.c | 73 +++++++++++++
drivers/net/gianfar.h | 93 ++++++++++++++++
drivers/net/gianfar_ethtool.c | 236 +++++++++++++++++++++++++++++++++++++++++
include/linux/ethtool.h | 2 +
4 files changed, 404 insertions(+), 0 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index dc9fba0..086d40d 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -431,6 +431,9 @@ static const struct net_device_ops gfar_netdev_ops = {
#endif
};
+unsigned int ftp_rqfpr[MAX_FILER_IDX + 1];
+unsigned int ftp_rqfcr[MAX_FILER_IDX + 1];
+
void lock_rx_qs(struct gfar_private *priv)
{
int i = 0x0;
@@ -766,6 +769,73 @@ static unsigned int reverse_bitmap(unsigned int bit_map, unsigned int max_qs)
}
return new_bit_map;
}
+
+u32 cluster_entry_per_class(struct gfar_private *priv, u32 rqfar, u32 class)
+{
+ u32 rqfpr = FPR_FILER_MASK;
+ u32 rqfcr = 0x0;
+
+ rqfar--;
+ rqfcr = RQFCR_CLE | RQFCR_PID_MASK | RQFCR_CMP_EXACT;
+ ftp_rqfpr[rqfar] = rqfpr;
+ ftp_rqfcr[rqfar] = rqfcr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ rqfar--;
+ rqfcr = RQFCR_CMP_NOMATCH;
+ ftp_rqfpr[rqfar] = rqfpr;
+ ftp_rqfcr[rqfar] = rqfcr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ rqfar--;
+ rqfcr = RQFCR_CMP_EXACT | RQFCR_PID_PARSE | RQFCR_CLE | RQFCR_AND;
+ rqfpr = class;
+ ftp_rqfcr[rqfar] = rqfcr;
+ ftp_rqfpr[rqfar] = rqfpr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ rqfar--;
+ rqfcr = RQFCR_CMP_EXACT | RQFCR_PID_MASK | RQFCR_AND;
+ rqfpr = class;
+ ftp_rqfcr[rqfar] = rqfcr;
+ ftp_rqfpr[rqfar] = rqfpr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ return rqfar;
+}
+
+static void gfar_init_filer_table(struct gfar_private *priv)
+{
+ int i = 0x0;
+ u32 rqfar = MAX_FILER_IDX;
+ u32 rqfcr = 0x0;
+ u32 rqfpr = FPR_FILER_MASK;
+
+ /* Default rule */
+ rqfcr = RQFCR_CMP_MATCH;
+ ftp_rqfcr[rqfar] = rqfcr;
+ ftp_rqfpr[rqfar] = rqfpr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6 | RQFPR_UDP);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6 | RQFPR_TCP);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4 | RQFPR_UDP);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4 | RQFPR_TCP);
+
+ /* cur_filer_idx indicated the fisrt non-masked rule */
+ priv->cur_filer_idx = rqfar;
+
+ /* Rest are masked rules */
+ rqfcr = RQFCR_CMP_NOMATCH;
+ for (i = 0; i < rqfar; i++) {
+ ftp_rqfcr[i] = rqfcr;
+ ftp_rqfpr[i] = rqfpr;
+ gfar_write_filer(priv, i, rqfcr, rqfpr);
+ }
+}
+
/* Set up the ethernet device structure, private data,
* and anything else we need before we start */
static int gfar_probe(struct of_device *ofdev,
@@ -1005,6 +1075,9 @@ static int gfar_probe(struct of_device *ofdev,
priv->gfargrp[i].int_name_tx[len_devname] = '\0';
}
+ /* Initialize the filer table */
+ gfar_init_filer_table(priv);
+
/* Create all the sysfs files */
gfar_init_sysfs(dev);
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index a2c1f96..44b63da 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -381,6 +381,84 @@ extern const char gfar_driver_version[];
#define BD_LFLAG(flags) ((flags) << 16)
#define BD_LENGTH_MASK 0x0000ffff
+#define CLASS_CODE_UNRECOG 0x00
+#define CLASS_CODE_DUMMY1 0x01
+#define CLASS_CODE_ETHERTYPE1 0x02
+#define CLASS_CODE_ETHERTYPE2 0x03
+#define CLASS_CODE_USER_PROG1 0x04
+#define CLASS_CODE_USER_PROG2 0x05
+#define CLASS_CODE_USER_PROG3 0x06
+#define CLASS_CODE_USER_PROG4 0x07
+#define CLASS_CODE_TCP_IPV4 0x08
+#define CLASS_CODE_UDP_IPV4 0x09
+#define CLASS_CODE_AH_ESP_IPV4 0x0a
+#define CLASS_CODE_SCTP_IPV4 0x0b
+#define CLASS_CODE_TCP_IPV6 0x0c
+#define CLASS_CODE_UDP_IPV6 0x0d
+#define CLASS_CODE_AH_ESP_IPV6 0x0e
+#define CLASS_CODE_SCTP_IPV6 0x0f
+
+#define FPR_FILER_MASK 0xFFFFFFFF
+#define MAX_FILER_IDX 0xFF
+
+/* RQFCR register bits */
+#define RQFCR_GPI 0x80000000
+#define RQFCR_HASHTBL_Q 0x00000000
+#define RQFCR_HASHTBL_0 0x00020000
+#define RQFCR_HASHTBL_1 0x00040000
+#define RQFCR_HASHTBL_2 0x00060000
+#define RQFCR_HASHTBL_3 0x00080000
+#define RQFCR_HASH 0x00010000
+#define RQFCR_CLE 0x00000200
+#define RQFCR_RJE 0x00000100
+#define RQFCR_AND 0x00000080
+#define RQFCR_CMP_EXACT 0x00000000
+#define RQFCR_CMP_MATCH 0x00000020
+#define RQFCR_CMP_NOEXACT 0x00000040
+#define RQFCR_CMP_NOMATCH 0x00000060
+
+/* RQFCR PID values */
+#define RQFCR_PID_MASK 0x00000000
+#define RQFCR_PID_PARSE 0x00000001
+#define RQFCR_PID_ARB 0x00000002
+#define RQFCR_PID_DAH 0x00000003
+#define RQFCR_PID_DAL 0x00000004
+#define RQFCR_PID_SAH 0x00000005
+#define RQFCR_PID_SAL 0x00000006
+#define RQFCR_PID_ETY 0x00000007
+#define RQFCR_PID_VID 0x00000008
+#define RQFCR_PID_PRI 0x00000009
+#define RQFCR_PID_TOS 0x0000000A
+#define RQFCR_PID_L4P 0x0000000B
+#define RQFCR_PID_DIA 0x0000000C
+#define RQFCR_PID_SIA 0x0000000D
+#define RQFCR_PID_DPT 0x0000000E
+#define RQFCR_PID_SPT 0x0000000F
+
+/* RQFPR when PID is 0x0001 */
+#define RQFPR_HDR_GE_512 0x00200000
+#define RQFPR_LERR 0x00100000
+#define RQFPR_RAR 0x00080000
+#define RQFPR_RARQ 0x00040000
+#define RQFPR_AR 0x00020000
+#define RQFPR_ARQ 0x00010000
+#define RQFPR_EBC 0x00008000
+#define RQFPR_VLN 0x00004000
+#define RQFPR_CFI 0x00002000
+#define RQFPR_JUM 0x00001000
+#define RQFPR_IPF 0x00000800
+#define RQFPR_FIF 0x00000400
+#define RQFPR_IPV4 0x00000200
+#define RQFPR_IPV6 0x00000100
+#define RQFPR_ICC 0x00000080
+#define RQFPR_ICV 0x00000040
+#define RQFPR_TCP 0x00000020
+#define RQFPR_UDP 0x00000010
+#define RQFPR_TUC 0x00000008
+#define RQFPR_TUV 0x00000004
+#define RQFPR_PER 0x00000002
+#define RQFPR_EER 0x00000001
+
/* TxBD status field bits */
#define TXBD_READY 0x8000
#define TXBD_PADCRC 0x4000
@@ -959,6 +1037,8 @@ struct gfar_private {
unsigned int rx_stash_size;
unsigned int rx_stash_index;
+ u32 cur_filer_idx;
+
struct sk_buff_head rx_recycle;
struct vlan_group *vlgrp;
@@ -1002,6 +1082,9 @@ struct gfar_private {
struct gfar_extra_stats extra_stats;
};
+extern unsigned int ftp_rqfpr[MAX_FILER_IDX + 1];
+extern unsigned int ftp_rqfcr[MAX_FILER_IDX + 1];
+
static inline u32 gfar_read(volatile unsigned __iomem *addr)
{
u32 val;
@@ -1014,6 +1097,16 @@ static inline void gfar_write(volatile unsigned __iomem *addr, u32 val)
out_be32(addr, val);
}
+static inline void gfar_write_filer(struct gfar_private *priv,
+ unsigned int far, unsigned int fcr, unsigned int fpr)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+
+ gfar_write(®s->rqfar, far);
+ gfar_write(®s->rqfcr, fcr);
+ gfar_write(®s->rqfpr, fpr);
+}
+
extern void lock_rx_qs(struct gfar_private *priv);
extern void lock_tx_qs(struct gfar_private *priv);
extern void unlock_rx_qs(struct gfar_private *priv);
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index 562f6c2..1010367 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -645,6 +645,241 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
}
#endif
+static int gfar_ethflow_to_class(int flow_type, u64 *class)
+{
+ switch (flow_type) {
+ case TCP_V4_FLOW:
+ *class = CLASS_CODE_TCP_IPV4;
+ break;
+ case UDP_V4_FLOW:
+ *class = CLASS_CODE_UDP_IPV4;
+ break;
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ *class = CLASS_CODE_AH_ESP_IPV4;
+ break;
+ case SCTP_V4_FLOW:
+ *class = CLASS_CODE_SCTP_IPV4;
+ break;
+ case TCP_V6_FLOW:
+ *class = CLASS_CODE_TCP_IPV6;
+ break;
+ case UDP_V6_FLOW:
+ *class = CLASS_CODE_UDP_IPV6;
+ break;
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ *class = CLASS_CODE_AH_ESP_IPV6;
+ break;
+ case SCTP_V6_FLOW:
+ *class = CLASS_CODE_SCTP_IPV6;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow)
+{
+ u32 fcr = 0x0, fpr = FPR_FILER_MASK;
+
+ if (ethflow & RXH_L2DA) {
+ fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH |
+ RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+
+ fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH |
+ RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_VLAN) {
+ fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_IP_SRC) {
+ fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & (RXH_IP_DST)) {
+ fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L3_PROTO) {
+ fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L4_B_0_1) {
+ fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L4_B_2_3) {
+ fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+}
+
+static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u64 class)
+{
+ unsigned int last_rule_idx = priv->cur_filer_idx;
+ unsigned int cmp_rqfpr;
+ unsigned int local_rqfpr[MAX_FILER_IDX + 1];
+ unsigned int local_rqfcr[MAX_FILER_IDX + 1];
+ int i = 0x0, k = 0x0;
+ int j = MAX_FILER_IDX, l = 0x0;
+
+ switch (class) {
+ case TCP_V4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP;
+ break;
+ case UDP_V4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP;
+ break;
+ case TCP_V6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP;
+ break;
+ case UDP_V6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP;
+ break;
+ case IPV4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4;
+ case IPV6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6;
+ break;
+ default:
+ printk(KERN_ERR "Right now this class is not supported\n");
+ return 0;
+ }
+
+ for (i = 0; i < MAX_FILER_IDX + 1; i++) {
+ local_rqfpr[j] = ftp_rqfpr[i];
+ local_rqfcr[j] = ftp_rqfcr[i];
+ j--;
+ if ((ftp_rqfcr[i] == (RQFCR_PID_PARSE |
+ RQFCR_CLE |RQFCR_AND)) &&
+ (ftp_rqfpr[i] == cmp_rqfpr))
+ break;
+ }
+
+ if (i == MAX_FILER_IDX + 1) {
+ printk(KERN_ERR "No parse rule found, ");
+ printk(KERN_ERR "can't create hash rules\n");
+ return 0;
+ }
+
+ /* If a match was found, then it begins the starting of a cluster rule
+ * if it was already programmed, we need to overwrite these rules
+ */
+ for (l = i+1; l < MAX_FILER_IDX; l++) {
+ if ((ftp_rqfcr[l] & RQFCR_CLE) &&
+ !(ftp_rqfcr[l] & RQFCR_AND)) {
+ ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT |
+ RQFCR_HASHTBL_0 | RQFCR_PID_MASK;
+ ftp_rqfpr[l] = FPR_FILER_MASK;
+ gfar_write_filer(priv, l, ftp_rqfcr[l], ftp_rqfpr[l]);
+ break;
+ }
+
+ if (!(ftp_rqfcr[l] & RQFCR_CLE) && (ftp_rqfcr[l] & RQFCR_AND))
+ continue;
+ else {
+ local_rqfpr[j] = ftp_rqfpr[l];
+ local_rqfcr[j] = ftp_rqfcr[l];
+ j--;
+ }
+ }
+
+ priv->cur_filer_idx = l - 1;
+ last_rule_idx = l;
+
+ /* hash rules */
+ ethflow_to_filer_rules(priv, ethflow);
+
+ /* Write back the popped out rules again */
+ for (k = j+1; k < MAX_FILER_IDX; k++) {
+ ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k];
+ ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k];
+ gfar_write_filer(priv, priv->cur_filer_idx,
+ local_rqfcr[k], local_rqfpr[k]);
+ if (!priv->cur_filer_idx)
+ break;
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ return 1;
+}
+
+static int gfar_set_hash_opts(struct gfar_private *priv, struct ethtool_rxnfc *cmd)
+{
+ u64 class;
+
+ if (!gfar_ethflow_to_class(cmd->flow_type, &class))
+ return -EINVAL;
+
+ if (class < CLASS_CODE_USER_PROG1 ||
+ class > CLASS_CODE_SCTP_IPV6)
+ return -EINVAL;
+
+ /* write the filer rules here */
+ if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type))
+ return -1;
+
+ return 0;
+}
+
+static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ int ret = 0;
+
+ switch(cmd->cmd) {
+ case ETHTOOL_SRXFH:
+ ret = gfar_set_hash_opts(priv, cmd);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
const struct ethtool_ops gfar_ethtool_ops = {
.get_settings = gfar_gsettings,
.set_settings = gfar_ssettings,
@@ -670,4 +905,5 @@ const struct ethtool_ops gfar_ethtool_ops = {
.get_wol = gfar_get_wol,
.set_wol = gfar_set_wol,
#endif
+ .set_rxnfc = gfar_set_nfc,
};
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index eb1a48d..edd03b7 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -674,6 +674,8 @@ struct ethtool_ops {
#define AH_V6_FLOW 0x0b
#define ESP_V6_FLOW 0x0c
#define IP_USER_FLOW 0x0d
+#define IPV4_FLOW 0x10
+#define IPV6_FLOW 0x11
/* L3-L4 network traffic flow hash options */
#define RXH_L2DA (1 << 1)
--
1.5.4.6
^ permalink raw reply related
* Re: HTB accuracy on 10GbE
From: Patrick McHardy @ 2009-11-02 15:43 UTC (permalink / raw)
To: Ryousei Takano; +Cc: shemminger, Linux Netdev List, takano-ryousei
In-Reply-To: <b30d1c3b0911012322v5d553fd6va56575fcf5e3daf8@mail.gmail.com>
Ryousei Takano wrote:
> Hi Stephen and all,
>
> I have observed a HTB accuracy problem on the Linux kernel 2.6.30 and
> the Myri-10G 10 GbE NIC.
> HTB can control the transmission rate at Gigabit speed, however it can
> not work well at 10 Gigabit speed.
>
> I asked Stephen this problem at Japan Linux Symposium. He mentioned a
> HTB bug related to the timer granularity.
> I want to know what is happen, and what should be do for fixing it.
>
> Any comments and suggestions will be welcome.
>
> For more detail, please see the following page:
> http://code.google.com/p/pspacer/wiki/HTBon10GbE
This is not an easy problem to fix. Userspace, the kernel and the
netlink API use 32 bit for timing related values, which is too small
to use more than microsecond resolution. All of them need to be
converted to use bigger types, additionally some kind of compatibility
handling to deal with old iproute versions still using microsecond
resolution is required.
^ permalink raw reply
* Re: [net-next PATCH 3/4] qlge: Reduce debug print output.
From: Ron Mercer @ 2009-11-02 15:57 UTC (permalink / raw)
To: David Miller; +Cc: joe@perches.com, netdev@vger.kernel.org
In-Reply-To: <20091102.000354.55192142.davem@davemloft.net>
Dave and Joe,
Thanks for your feedback on the printk macro. I agree there is good
infrastructure for debug printing for network devices. I will drop this
patch for now. The main reason I did it this way was to remove compares from the data path without removing the messages.
I will respin the last patch.
Thanks,
Ron
On Mon, Nov 02, 2009 at 12:03:54AM -0800, David Miller wrote:
> From: Joe Perches <joe@perches.com>
> Date: Fri, 30 Oct 2009 15:44:46 -0700
>
> > On Fri, 2009-10-30 at 15:13 -0700, Ron Mercer wrote:
> >> Signed-off-by: Ron Mercer <ron.mercer@qlogic.com>
> > []
> >> diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h
> >> index b9f65e0..502c3af 100644
> >> --- a/drivers/net/qlge/qlge.h
> >> +++ b/drivers/net/qlge/qlge.h
> >> @@ -27,6 +27,18 @@
> >> dev_printk(KERN_##klevel, &((qdev)->pdev->dev), \
> >> "%s: " fmt, __func__, ##args); \
> >> } while (0)
> >> +#if 0
> >> +#define QPRINTK_DBG(qdev, nlevel, klevel, fmt, args...) \
> >> + do { \
> >> + if (!((qdev)->msg_enable & NETIF_MSG_##nlevel)) \
> >> + ; \
> >> + else \
> >> + dev_printk(KERN_##klevel, &((qdev)->pdev->dev), \
> >> + "%s: " fmt, __func__, ##args); \
> >> + } while (0)
> >> +#else
> >> +#define QPRINTK_DBG(qdev, nlevel, klevel, fmt, args...)
> >> +#endif
> >
> > This uses an inverted test and it doesn't verify the args to
> > dev_printk when not #defined.
> >
> > How about:
>
> I also don't like this kind of change for another reason.
>
> The message levels are pointless if you're going to adhere to them
> or not based upon some CPP define.
>
> Either do it, or don't. Like every other driver does.
>
> If for some reason the default is problematic, adjust the default that
> you pass to netif_msg_init() or, alternatively, adjust what level the
> debugging messages are assigned to.
>
> We have all of this wonderful, full, infrastructure for message
> levelling. And you can change the setting either at module load time
> or via ethtool. The default is also up to you as well.
>
> And you're going to stick a "#if 0" CPP control in there? :-/
>
> No way, I absolutely won't accept this kind of change, there is no
> need for it. There is more than enough dynamic flexibility, both at
> run-time via module option and ethtool message level selections, and
> at compile time via the default you can choose hoever you like.
^ permalink raw reply
* Re: Connection tracking and vlan
From: Adayadil Thomas @ 2009-11-02 16:14 UTC (permalink / raw)
To: Ben Greear
Cc: Eric W. Biederman, Eric Dumazet, Herbert Xu, netdev,
Patrick McHardy
In-Reply-To: <4AEB75DD.8050204@candelatech.com>
[-- Attachment #1: Type: text/plain, Size: 1473 bytes --]
If the vlan id is used for hash, it still may not avoid the problem completely,
i.e. in case of both connections hashing to the same bucket.
I was wondering about your opinion about adding an optional member to the tuple
structure, vid (for vlan id).
I have attached the patch for this change. I would be grateful for any comments
such as dependencies on the rest of the system.
Thanks much
On Fri, Oct 30, 2009 at 6:25 PM, Ben Greear <greearb@candelatech.com> wrote:
> On 10/30/2009 04:15 PM, Eric W. Biederman wrote:
>
>>> If ip_conntrack does not consider vlans, it is possible that all 5
>>> tuple are the same
>>> and thus affect the connection tracking.
>>>
>>> I hope I have described the scenario well. If not I can explain in a
>>> more detailed fashion.
>>
>> Unless you have multiple network namespaces linux assumes all packets are
>> in the same ip space. And 10.10.10.1 is the same machine no matter
>> which interface you talk to it on.
>
> It only takes a relatively small patch that lets conn-track hash on a
> skb->foo_mark, and allow that mark to be set on incoming packets
> based on netdevice or whatever, (before the conn-track lookup is
> done).
>
> This is logically somewhat similar to using multiple routing
> tables and has been working well for me for several years....
>
> Thanks,
> Ben
>
> --
> Ben Greear <greearb@candelatech.com>
> Candela Technologies Inc http://www.candelatech.com
>
>
[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 6804 bytes --]
--- linux-2.6.20.21/include/linux/netfilter_ipv4/ip_conntrack_core.h 2007-10-17 15:31:14.000000000 -0400
+++ linux-2.6.20.21.mod/include/linux/netfilter_ipv4/ip_conntrack_core.h 2009-11-02 09:53:44.000000000 -0500
@@ -24,7 +24,12 @@
const struct sk_buff *skb,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple,
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ const struct ip_conntrack_protocol *protocol,
+ unsigned short vid);
+#else
const struct ip_conntrack_protocol *protocol);
+#endif
extern int
ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse,
--- linux-2.6.20.21/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2007-10-17 15:31:14.000000000 -0400
+++ linux-2.6.20.21.mod/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2009-11-02 09:56:09.000000000 -0500
@@ -79,6 +79,10 @@
/* The direction (for tuplehash) */
u_int8_t dir;
} dst;
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ __be16 vid;
+#endif
+
};
/* This is optimized opposed to a memset of the whole structure. Everything we
@@ -87,6 +91,9 @@
do { \
(tuple)->src.u.all = 0; \
(tuple)->dst.u.all = 0; \
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ (tuple)->vid = 0; \
+#endif
} while (0)
#ifdef __KERNEL__
@@ -125,10 +132,22 @@
&& t1->dst.protonum == t2->dst.protonum;
}
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+static inline int ip_ct_tuple_vlan_equal(const struct ip_conntrack_tuple *t1,
+ const struct ip_conntrack_tuple *t2)
+{
+ return t1->vid == t2->vid;
+}
+#endif
+
static inline int ip_ct_tuple_equal(const struct ip_conntrack_tuple *t1,
const struct ip_conntrack_tuple *t2)
{
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ return ip_ct_tuple_src_equal(t1, t2) && ip_ct_tuple_dst_equal(t1, t2) && ip_ct_tuple_vlan_equal(t1, t2);
+#else
return ip_ct_tuple_src_equal(t1, t2) && ip_ct_tuple_dst_equal(t1, t2);
+#endif
}
static inline int ip_ct_tuple_mask_cmp(const struct ip_conntrack_tuple *t,
--- linux-2.6.20.21/net/ipv4/netfilter/ip_conntrack_core.c 2009-11-02 10:57:49.000000000 -0500
+++ linux-2.6.20.21.mod/net/ipv4/netfilter/ip_conntrack_core.c 2009-11-02 09:57:57.000000000 -0500
@@ -45,6 +45,7 @@
#include <linux/percpu.h>
#include <linux/moduleparam.h>
#include <linux/notifier.h>
+#include <linux/if_vlan.h>
/* ip_conntrack_lock protects the main hash table, protocol/helper/expected
registrations, conntrack timers*/
@@ -181,7 +182,8 @@
const struct sk_buff *skb,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple,
- const struct ip_conntrack_protocol *protocol)
+ const struct ip_conntrack_protocol *protocol,
+ unsigned short vid)
{
/* Never happen */
if (iph->frag_off & htons(IP_OFFSET)) {
@@ -194,6 +196,7 @@
tuple->dst.ip = iph->daddr;
tuple->dst.protonum = iph->protocol;
tuple->dst.dir = IP_CT_DIR_ORIGINAL;
+ tuple->vid = vid;
return protocol->pkt_to_tuple(skb, dataoff, tuple);
}
@@ -206,6 +209,7 @@
inverse->src.ip = orig->dst.ip;
inverse->dst.ip = orig->src.ip;
inverse->dst.protonum = orig->dst.protonum;
+ inverse->vid = orig->vid;
inverse->dst.dir = !orig->dst.dir;
return protocol->invert_tuple(inverse, orig);
@@ -779,11 +783,26 @@
struct ip_conntrack_tuple tuple;
struct ip_conntrack_tuple_hash *h;
struct ip_conntrack *ct;
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ struct vlan_ethhdr *vhdr;
+ unsigned short vlan_TCI, vid = 8192;
+
+ vhdr = vlan_eth_hdr(skb);
+ if (vhdr && vhdr->h_vlan_proto == __constant_htons(ETH_P_8021Q)){
+ vlan_TCI = ntohs(vhdr->h_vlan_TCI);
+ vid = (vlan_TCI & VLAN_VID_MASK);
+ }
+#endif
IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
if (!ip_ct_get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4,
+ &tuple,proto,vid))
+#else
+ if (!ip_ct_get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4,
&tuple,proto))
+#endif
return NULL;
/* look for tuple match */
--- linux-2.6.20.21/net/ipv4/netfilter/ip_conntrack_proto_icmp.c 2007-10-17 15:31:14.000000000 -0400
+++ linux-2.6.20.21.mod/net/ipv4/netfilter/ip_conntrack_proto_icmp.c 2009-11-02 09:58:52.000000000 -0500
@@ -20,6 +20,7 @@
#include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_conntrack_core.h>
#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+#include <linux/if_vlan.h>
unsigned int ip_ct_icmp_timeout __read_mostly = 30*HZ;
@@ -146,6 +147,16 @@
struct ip_conntrack_protocol *innerproto;
struct ip_conntrack_tuple_hash *h;
int dataoff;
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ struct vlan_ethhdr *vhdr;
+ unsigned short vlan_TCI, vid = 8192;
+
+ vhdr = vlan_eth_hdr(skb);
+ if (vhdr && vhdr->h_vlan_proto == __constant_htons(ETH_P_8021Q)){
+ vlan_TCI = ntohs(vhdr->h_vlan_TCI);
+ vid = (vlan_TCI & VLAN_VID_MASK);
+ }
+#endif
IP_NF_ASSERT(skb->nfct == NULL);
@@ -164,7 +175,11 @@
innerproto = ip_conntrack_proto_find_get(inside->ip.protocol);
dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp) + inside->ip.ihl*4;
/* Are they talking about one of our connections? */
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ if (!ip_ct_get_tuple(&inside->ip, skb, dataoff, &origtuple, innerproto, vid)) {
+#else
if (!ip_ct_get_tuple(&inside->ip, skb, dataoff, &origtuple, innerproto)) {
+#endif
DEBUGP("icmp_error: ! get_tuple p=%u", inside->ip.protocol);
ip_conntrack_proto_put(innerproto);
return -NF_ACCEPT;
--- linux-2.6.20.21/net/ipv4/netfilter/ip_nat_core.c 2009-11-02 10:57:49.000000000 -0500
+++ linux-2.6.20.21.mod/net/ipv4/netfilter/ip_nat_core.c 2009-11-02 10:01:08.000000000 -0500
@@ -23,6 +23,7 @@
#include <linux/icmp.h>
#include <linux/udp.h>
#include <linux/jhash.h>
+#include <linux/if_vlan.h>
#include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_conntrack_core.h>
@@ -821,6 +822,17 @@
unsigned long statusbit;
enum ip_nat_manip_type manip = HOOK2MANIP(hooknum);
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ struct vlan_ethhdr *vhdr;
+ unsigned short vlan_TCI, vid = 8192;
+
+ vhdr = vlan_eth_hdr(*pskb);
+ if (vhdr && vhdr->h_vlan_proto == __constant_htons(ETH_P_8021Q)){
+ vlan_TCI = ntohs(vhdr->h_vlan_TCI);
+ vid = (vlan_TCI & VLAN_VID_MASK);
+ }
+#endif
+
if (!skb_make_writable(pskb, hdrlen + sizeof(*inside)))
return 0;
@@ -850,10 +862,14 @@
DEBUGP("icmp_reply_translation: translating error %p manp %u dir %s\n",
*pskb, manip, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY");
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
if (!ip_ct_get_tuple(&inside->ip, *pskb, (*pskb)->nh.iph->ihl*4 +
sizeof(struct icmphdr) + inside->ip.ihl*4,
&inner,
+ __ip_conntrack_proto_find(inside->ip.protocol), vid))
+#else
__ip_conntrack_proto_find(inside->ip.protocol)))
+#endif
return 0;
/* Change inner back to look like incoming packet. We do the
^ permalink raw reply
* Re: [net-next-2.6 PATCH v4 3/3] TCPCT part 1c: initial SYN exchange with SYNACK data
From: William Allen Simpson @ 2009-11-02 16:17 UTC (permalink / raw)
To: Linux Kernel Network Developers; +Cc: Ilpo Järvinen, Eric Dumazet
In-Reply-To: <alpine.DEB.2.00.0911021448280.19761@wel-95.cs.helsinki.fi>
Ilpo Järvinen wrote:
> Are you talking about particular case?!? ...You can safely split into even
> more parts if there are cleanups which is essential. ...We'll not stop you
> from doing that nor be angry if do that.
>
Actually, my earliest posting split the original single patch, and Miller
*did* seem angry. I had to put it back together again -- and then he
only commented on one thing that *was* in my first post, causing me to
have to redo the entire thing a third time. So, I've been posting patches
in bigger groups than I originally write and test.
> That kind of response certainly won't help you any. ...First, you said you
> adapt the current style but for some reason immediately start to say why
> you would careless about that principle. ...Also, telling that you have
> lots of experience here and there will not get you there either ;-).
>
I meant I adapt to existing style (no matter how odd) in places where I'm
patching, so that *patches* are easier to review -- and write in a more
elegant style where I'm making a significant stand-alone addition.
I'd thought that constant-left style was pretty common around here, as grep
tells me there are hundreds upon hundreds of examples in arch, drivers, net,
and sound....
Seems like I'm not alone.
Eric Dumazet wrote:
# Cooking patches to linux is not only matter of good ideas and programming (and Dropping
# patches for the masses).
#
# Its also a matter of convincing _people_ that your additions will be maintainable
# when you leave kernel programming and let people like us correct bugs.
#
# For the moment, I am not convinced at all. I prefer to talk now.
#
OK, I'm talking. Thank you.
Linux already has a fair amount of my code in it, often hard to recognize
now after 15 years, so I'm pretty sure that my code has been found
maintainable in the past.
Anyway, I don't want to argue about it on an open mailing list. I'm more
interested in getting work done!
# Note: I did read your TCPCT 25 pages documentation and very am interested by this
# improvement, but its _also_ important to implement it in the normal way.
# (I wish this document could be public in a RFC form)
#
It will be, when we have running code, as I'm loath to publish until I'm
certain it *can* be implemented.
I've something like 40 RFCs published over the years.
^ permalink raw reply
* Re: Connection tracking and vlan
From: Adayadil Thomas @ 2009-11-02 16:30 UTC (permalink / raw)
To: Ben Greear
Cc: Eric W. Biederman, Eric Dumazet, Herbert Xu, netdev,
Patrick McHardy
In-Reply-To: <fb7befa20911020814q2c4bcd1bj7e2b5a4c17ba0f89@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1718 bytes --]
A small correction to the patch. Thanks for any comments you can
provide on this patch.
Thanks
On Mon, Nov 2, 2009 at 11:14 AM, Adayadil Thomas
<adayadil.thomas@gmail.com> wrote:
> If the vlan id is used for hash, it still may not avoid the problem completely,
> i.e. in case of both connections hashing to the same bucket.
>
> I was wondering about your opinion about adding an optional member to the tuple
> structure, vid (for vlan id).
>
> I have attached the patch for this change. I would be grateful for any comments
> such as dependencies on the rest of the system.
>
>
> Thanks much
>
>
>
> On Fri, Oct 30, 2009 at 6:25 PM, Ben Greear <greearb@candelatech.com> wrote:
>> On 10/30/2009 04:15 PM, Eric W. Biederman wrote:
>>
>>>> If ip_conntrack does not consider vlans, it is possible that all 5
>>>> tuple are the same
>>>> and thus affect the connection tracking.
>>>>
>>>> I hope I have described the scenario well. If not I can explain in a
>>>> more detailed fashion.
>>>
>>> Unless you have multiple network namespaces linux assumes all packets are
>>> in the same ip space. And 10.10.10.1 is the same machine no matter
>>> which interface you talk to it on.
>>
>> It only takes a relatively small patch that lets conn-track hash on a
>> skb->foo_mark, and allow that mark to be set on incoming packets
>> based on netdevice or whatever, (before the conn-track lookup is
>> done).
>>
>> This is logically somewhat similar to using multiple routing
>> tables and has been working well for me for several years....
>>
>> Thanks,
>> Ben
>>
>> --
>> Ben Greear <greearb@candelatech.com>
>> Candela Technologies Inc http://www.candelatech.com
>>
>>
>
[-- Attachment #2: patch1.txt --]
[-- Type: text/plain, Size: 7137 bytes --]
--- linux-2.6.20.21/include/linux/netfilter_ipv4/ip_conntrack_core.h 2007-10-17 15:31:14.000000000 -0400
+++ linux-2.6.20.21.mod/include/linux/netfilter_ipv4/ip_conntrack_core.h 2009-11-02 11:01:58.000000000 -0500
@@ -24,7 +24,12 @@
const struct sk_buff *skb,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple,
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ const struct ip_conntrack_protocol *protocol,
+ unsigned short vid);
+#else
const struct ip_conntrack_protocol *protocol);
+#endif
extern int
ip_ct_invert_tuple(struct ip_conntrack_tuple *inverse,
--- linux-2.6.20.21/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2007-10-17 15:31:14.000000000 -0400
+++ linux-2.6.20.21.mod/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2009-11-02 11:18:39.000000000 -0500
@@ -79,15 +79,28 @@
/* The direction (for tuplehash) */
u_int8_t dir;
} dst;
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ __be16 vid;
+#endif
+
};
/* This is optimized opposed to a memset of the whole structure. Everything we
* really care about is the source/destination unions */
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
#define IP_CT_TUPLE_U_BLANK(tuple) \
do { \
(tuple)->src.u.all = 0; \
(tuple)->dst.u.all = 0; \
+ (tuple)->vid = 0; \
+ } while (0)
+#else
+#define IP_CT_TUPLE_U_BLANK(tuple) \
+ do { \
+ (tuple)->src.u.all = 0; \
+ (tuple)->dst.u.all = 0; \
} while (0)
+#endif
#ifdef __KERNEL__
@@ -125,10 +138,22 @@
&& t1->dst.protonum == t2->dst.protonum;
}
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+static inline int ip_ct_tuple_vlan_equal(const struct ip_conntrack_tuple *t1,
+ const struct ip_conntrack_tuple *t2)
+{
+ return t1->vid == t2->vid;
+}
+#endif
+
static inline int ip_ct_tuple_equal(const struct ip_conntrack_tuple *t1,
const struct ip_conntrack_tuple *t2)
{
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ return ip_ct_tuple_src_equal(t1, t2) && ip_ct_tuple_dst_equal(t1, t2) && ip_ct_tuple_vlan_equal(t1, t2);
+#else
return ip_ct_tuple_src_equal(t1, t2) && ip_ct_tuple_dst_equal(t1, t2);
+#endif
}
static inline int ip_ct_tuple_mask_cmp(const struct ip_conntrack_tuple *t,
--- linux-2.6.20.21/net/ipv4/netfilter/ip_conntrack_core.c 2009-11-02 11:26:07.000000000 -0500
+++ linux-2.6.20.21.mod/net/ipv4/netfilter/ip_conntrack_core.c 2009-11-02 11:22:00.000000000 -0500
@@ -45,6 +45,7 @@
#include <linux/percpu.h>
#include <linux/moduleparam.h>
#include <linux/notifier.h>
+#include <linux/if_vlan.h>
/* ip_conntrack_lock protects the main hash table, protocol/helper/expected
registrations, conntrack timers*/
@@ -181,7 +182,12 @@
const struct sk_buff *skb,
unsigned int dataoff,
struct ip_conntrack_tuple *tuple,
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ const struct ip_conntrack_protocol *protocol,
+ unsigned short vid)
+#else
const struct ip_conntrack_protocol *protocol)
+#endif
{
/* Never happen */
if (iph->frag_off & htons(IP_OFFSET)) {
@@ -194,6 +200,9 @@
tuple->dst.ip = iph->daddr;
tuple->dst.protonum = iph->protocol;
tuple->dst.dir = IP_CT_DIR_ORIGINAL;
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ tuple->vid = vid;
+#endif
return protocol->pkt_to_tuple(skb, dataoff, tuple);
}
@@ -206,6 +215,9 @@
inverse->src.ip = orig->dst.ip;
inverse->dst.ip = orig->src.ip;
inverse->dst.protonum = orig->dst.protonum;
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ inverse->vid = orig->vid;
+#endif
inverse->dst.dir = !orig->dst.dir;
return protocol->invert_tuple(inverse, orig);
@@ -779,11 +791,26 @@
struct ip_conntrack_tuple tuple;
struct ip_conntrack_tuple_hash *h;
struct ip_conntrack *ct;
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ struct vlan_ethhdr *vhdr;
+ unsigned short vlan_TCI, vid = 8192;
+
+ vhdr = vlan_eth_hdr(skb);
+ if (vhdr && vhdr->h_vlan_proto == __constant_htons(ETH_P_8021Q)){
+ vlan_TCI = ntohs(vhdr->h_vlan_TCI);
+ vid = (vlan_TCI & VLAN_VID_MASK);
+ }
+#endif
IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
if (!ip_ct_get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4,
+ &tuple,proto,vid))
+#else
+ if (!ip_ct_get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4,
&tuple,proto))
+#endif
return NULL;
/* look for tuple match */
--- linux-2.6.20.21/net/ipv4/netfilter/ip_conntrack_proto_icmp.c 2007-10-17 15:31:14.000000000 -0400
+++ linux-2.6.20.21.mod/net/ipv4/netfilter/ip_conntrack_proto_icmp.c 2009-11-02 11:01:58.000000000 -0500
@@ -20,6 +20,7 @@
#include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_conntrack_core.h>
#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+#include <linux/if_vlan.h>
unsigned int ip_ct_icmp_timeout __read_mostly = 30*HZ;
@@ -146,6 +147,16 @@
struct ip_conntrack_protocol *innerproto;
struct ip_conntrack_tuple_hash *h;
int dataoff;
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ struct vlan_ethhdr *vhdr;
+ unsigned short vlan_TCI, vid = 8192;
+
+ vhdr = vlan_eth_hdr(skb);
+ if (vhdr && vhdr->h_vlan_proto == __constant_htons(ETH_P_8021Q)){
+ vlan_TCI = ntohs(vhdr->h_vlan_TCI);
+ vid = (vlan_TCI & VLAN_VID_MASK);
+ }
+#endif
IP_NF_ASSERT(skb->nfct == NULL);
@@ -164,7 +175,11 @@
innerproto = ip_conntrack_proto_find_get(inside->ip.protocol);
dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp) + inside->ip.ihl*4;
/* Are they talking about one of our connections? */
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ if (!ip_ct_get_tuple(&inside->ip, skb, dataoff, &origtuple, innerproto, vid)) {
+#else
if (!ip_ct_get_tuple(&inside->ip, skb, dataoff, &origtuple, innerproto)) {
+#endif
DEBUGP("icmp_error: ! get_tuple p=%u", inside->ip.protocol);
ip_conntrack_proto_put(innerproto);
return -NF_ACCEPT;
--- linux-2.6.20.21/net/ipv4/netfilter/ip_nat_core.c 2009-11-02 11:26:07.000000000 -0500
+++ linux-2.6.20.21.mod/net/ipv4/netfilter/ip_nat_core.c 2009-11-02 11:23:50.000000000 -0500
@@ -23,6 +23,7 @@
#include <linux/icmp.h>
#include <linux/udp.h>
#include <linux/jhash.h>
+#include <linux/if_vlan.h>
#include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_conntrack_core.h>
@@ -821,6 +822,17 @@
unsigned long statusbit;
enum ip_nat_manip_type manip = HOOK2MANIP(hooknum);
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ struct vlan_ethhdr *vhdr;
+ unsigned short vlan_TCI, vid = 8192;
+
+ vhdr = vlan_eth_hdr(*pskb);
+ if (vhdr && vhdr->h_vlan_proto == __constant_htons(ETH_P_8021Q)){
+ vlan_TCI = ntohs(vhdr->h_vlan_TCI);
+ vid = (vlan_TCI & VLAN_VID_MASK);
+ }
+#endif
+
if (!skb_make_writable(pskb, hdrlen + sizeof(*inside)))
return 0;
@@ -853,7 +865,11 @@
if (!ip_ct_get_tuple(&inside->ip, *pskb, (*pskb)->nh.iph->ihl*4 +
sizeof(struct icmphdr) + inside->ip.ihl*4,
&inner,
+#ifdef CONFIG_IP_NF_CONNTRACK_VLAN
+ __ip_conntrack_proto_find(inside->ip.protocol), vid))
+#else
__ip_conntrack_proto_find(inside->ip.protocol)))
+#endif
return 0;
/* Change inner back to look like incoming packet. We do the
^ permalink raw reply
* Re: Connection tracking and vlan
From: Patrick McHardy @ 2009-11-02 16:33 UTC (permalink / raw)
To: Adayadil Thomas
Cc: Ben Greear, Eric W. Biederman, Eric Dumazet, Herbert Xu,
Linux Netdev List, Netfilter Development Mailinglist
In-Reply-To: <fb7befa20911020814q2c4bcd1bj7e2b5a4c17ba0f89@mail.gmail.com>
Adayadil Thomas wrote:
> If the vlan id is used for hash, it still may not avoid the problem completely,
> i.e. in case of both connections hashing to the same bucket.
>
> I was wondering about your opinion about adding an optional member to the tuple
> structure, vid (for vlan id).
>
> I have attached the patch for this change. I would be grateful for any comments
> such as dependencies on the rest of the system.
Absolutely not, conntrack is not meant to deal with anything below
the network layer and I don't want to add any hacks for the bridge
netfilter "integration", which has already caused an endless amount
of problems. Additionally this is just one of many possible identifiers
people might want to use to distinguish similar entries and has a
number of practical issues, like breaking asymetric setups using
different VLANs for each direction.
I might be willing to consider a generically usable numerical
identifier to distinguish similar entries, something like
"conntrack zones". This could also help with the defragmentation
issue discussed earlier, the identifier would also be added to
the defragmentation identifier, for asymetric setups the interfaces
would be put in the same "zone".
But it would be preferrable if we could do this using network
namespaces somehow.
^ permalink raw reply
* Re: Connection tracking and vlan
From: Eric Dumazet @ 2009-11-02 16:41 UTC (permalink / raw)
To: Patrick McHardy
Cc: Adayadil Thomas, Ben Greear, Eric W. Biederman, Herbert Xu,
Linux Netdev List, Netfilter Development Mailinglist
In-Reply-To: <4AEF09E7.2010004@trash.net>
Patrick McHardy a écrit :
>
> But it would be preferrable if we could do this using network
> namespaces somehow.
eg eth0.100 and eth0.101 on namespace 1, eth0.200 and eth0.201 on namespace 2 ?
Can we do that with current kernel ? (different vlans on an unique physical device)
^ permalink raw reply
* Re: pull request: wireless-next-2.6 2009-10-28
From: John W. Linville @ 2009-11-02 16:46 UTC (permalink / raw)
To: Ingo Molnar
Cc: Jarek Poplawski, Johannes Berg, Bartlomiej Zolnierkiewicz,
Pekka Enberg, David Miller, linux-wireless, netdev, linux-kernel
In-Reply-To: <20091102091038.GA9044@elte.hu>
On Mon, Nov 02, 2009 at 10:10:38AM +0100, Ingo Molnar wrote:
>
> * John W. Linville <linville@tuxdriver.com> wrote:
<snip>
> > Ever since Bartlomiej decided to tear himself away from
> > drivers/staging, he has been nothing but negative -- petty, whining,
> > indignat, whatever. Just what has he done to merit any special
> > consideration here? Why should he have any sort of veto over rt2x00?
>
> I got curious, as my past experience with Bartlomiej is that he is a
> factual, reliable, knowledgable upstream driver developer interested in
> difficult pieces of code others are reluctant to touch, for whom it is
> rather atypical to get 'petty, whining, indignant'.
YMMV...
<snip>
> Bartlomiej's reply (<1255792104.3434.2.camel@johannes.local>) ignored
> the attack (gracefully) and replied to the technical portion only:
>
> | > Tell me you're kidding -- comparing 2k duplicated LOC with a driver
> | > that ships its own wifi stack?
> |
> | Why would I be?
> |
> | 1) The patch is submitted to kernel _proper_ not kernel staging so I
> | see no excuse for duplicating 2-4 KLOC and it should be fixed.
> |
> | 2) The fact that the some staging driver consists in 90% of crap
> | doesn't mean that it doesn't have some good design ideas.. (i.e.
> | abstracting chipset registers access in a discussed case)
>
> To which technical point Johannes elected not to reply. (Effectively
> conceding Bartlomiej's point as per lkml discussion rules.)
Really? "Last post wins" is the rule? I hope you are joking...
> I can understand David trusting your judgement and not wanting to get
> involved in the fine details, but having read the surrounding discussion
> i dont understand your interpretation of the events, and i dont
> understand on what basis you launched your very serious accusation, that
> he is being 'petty, whining, indignant'. Every reply from him in that
> thread is the exact opposite of that. Care to elaborate?
Despite your links, we seem to be reading different threads. I'm not
sure I can reach your conclusions even with the most generous reading
of Bartlomiej's posts and the most critical readings of _everyone_
else's.
It seems Bartlomiej has taken it personally that we don't like having
the Ralink vendor drivers in the staging tree and he has decided to be
an irritant in linux-wireless as some sort of revenge. As a bonus,
he has thrown-in some random ramblings attacking the inclusion of
Pulseaudio in Fedora and criticizing Dave's handling of the ide tree.
And worst of all he has chosen to make a big stink about whether or
not the rt2x00 family of drivers (which shares tons of code already,
BTW) is sharing enough code for his taste in drivers that are currently
under active development.
Now you have come along to defend him for whatever your reasons,
and you have chosen to act as if Bartlomiej has been ignored simply
because you don't like the responses he got. Further, you seem to
expect me to have given him some veto over a driver for which has
has contributed essentially nothing[1]. Why? Simply because he doesn't
agree with the driver maintainer's JUDGMENT CALL? Or mine? Or Dave's?
And for all his objections, he can't be bothered to offer a patch?
I can only guess as to why you are being deferential to Bartlomiej
and overlooking not only how he has treated the rest of us but also
that his technical complaint is minor. I can only ask you to consider
that maybe Bartlomiej is not so aggrieved as he claims.
John
[1] Even his bug reports and code reviews have clearly been from the
perspective of "you should just be using the staging driver".
--
John W. Linville Someday the world will need a hero, and you
linville@tuxdriver.com might be all we have. Be ready.
^ permalink raw reply
* Re: Connection tracking and vlan
From: Patrick McHardy @ 2009-11-02 16:48 UTC (permalink / raw)
To: Eric Dumazet
Cc: Adayadil Thomas, Ben Greear, Eric W. Biederman, Herbert Xu,
Linux Netdev List, Netfilter Development Mailinglist
In-Reply-To: <4AEF0BAA.7050800@gmail.com>
Eric Dumazet wrote:
> Patrick McHardy a écrit :
>> But it would be preferrable if we could do this using network
>> namespaces somehow.
>
> eg eth0.100 and eth0.101 on namespace 1, eth0.200 and eth0.201 on namespace 2 ?
>
> Can we do that with current kernel ? (different vlans on an unique physical device)
By default the underlying device needs to exist in the same namespace
in which the VLAN device is created. I believe it should be possible
to move the VLAN device to a different namespace after creating it,
but I'm not sure about that.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: RFC: netdev: allow ethtool physical id to drop rtnl_lock
From: Stephen Hemminger @ 2009-11-02 16:51 UTC (permalink / raw)
To: David Miller; +Cc: mchan, netdev
In-Reply-To: <20091102.001710.209808608.davem@davemloft.net>
On Mon, 02 Nov 2009 00:17:10 -0800 (PST)
David Miller <davem@davemloft.net> wrote:
> From: "Michael Chan" <mchan@broadcom.com>
> Date: Sat, 31 Oct 2009 09:44:22 -0700
>
> >
> > On Fri, 2009-10-30 at 10:42 -0700, Stephen Hemminger wrote:
> >> The ethtool operation to blink LED can take an indeterminately long time,
> >> blocking out other operations (via rtnl_lock). This patch is an attempt
> >> to work around the problem.
> >>
> >> It does need more discussion, because it will mean that drivers that formerly
> >> were protected from changes during blink aren't. For example, user could
> >> start device blinking, and then plug in cable causing change netlink event
> >> to change state or pull cable and have device come down.
> >
> > Yeah, the biggest concern is shutting down the device while it is still
> > blinking. During shutdown, some devices are brought to low power state
> > and the chip will no longer respond to I/Os to blink the LEDs. On some
> > systems, this can cause bus hang or NMI.
>
> Right, and for this reason we'll either need find some way to stop
> the LED blinking when the device is brought down.
>
> We can deal with this in a way such that we'll never need to bug
> the drivers again if we want to mess with the implementation again.
>
> Create a "netif_phys_id_loop_iter()" that, along with a netdev
> pointer, takes a "u32 data" which is whatever was passed in to
> ethtool_ops->id().
>
> The drivers then structure their loops like:
>
> while (1) {
> blink_it_baby();
> data = netif_phys_id_loop_iter(dev, data);
> if (!data)
> break;
> }
>
> Next, we take that:
>
> if (data == 0)
> data = UINT_MAX / 2;
>
> That every driver seems to do, and stick it in the ethtool op dispatch
> in net/core/ethtool.c so it doesn't need to be duplicated (and
> potentially forgotten) in every implementation.
>
> Finally, in netif_phys_id_loop_iter() we put something like:
>
> u32 netif_phys_id_loop_iter(struct netdev *dev, u32 data)
> {
> if (dev->reg_state == NETREG_UNREGISTERING)
> return 0;
> if (msleep_interruptible(500))
> return 0;
> return data - 2;
> }
>
> Then, unregister somehow blocks on the ->phys_id() hitting that
> NETREG_UNREGISTERING check and returning.
>
> Anyways, you get the idea.
For compatibility, I was thinking of adding a new ethtool hook that
moves the blinking loop into ethtool.
static int ethtool_phys_blink(struct net_device *dev, u32 secs)
{
while (secs > 0) {
dev->ethtool_ps->phys_led(dev, ETH_LED_ON);
...
dev->ethtool_ps->phys_led(dev, ETH_LED_OFF);
}
dev->ethtool_ops->phys_led(dev, ETH_LED_NORMAL);
}
static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
{
struct ethtool_value id;
if (copy_from_user(&id, useraddr, sizeof(id)))
return -EFAULT;
if (dev->ethtool_ops->phys_led)
return ethtool_phys_blink(dev, id.data);
if (dev->ethtool_ops->phys_id)
return dev->ethtool_ops->phys_id(dev, id.data);
else
return -EOPNOTSUPP;
}
--
^ permalink raw reply
* [PATCH] cnic: ensure ulp_type is not negative
From: Roel Kluin @ 2009-11-02 16:53 UTC (permalink / raw)
To: netdev, Andrew Morton, LKML, mchan
`ulp_type' is signed, make sure it is not negative
when we read the array element.
Signed-off-by: Roel Kluin <roel.kluin@gmail.com>
---
drivers/net/cnic.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c
index 3bf1b04..f384b0a 100644
--- a/drivers/net/cnic.c
+++ b/drivers/net/cnic.c
@@ -347,7 +347,7 @@ int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops)
{
struct cnic_dev *dev;
- if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+ if (ulp_type < 0 || ulp_type >= MAX_CNIC_ULP_TYPE) {
printk(KERN_ERR PFX "cnic_register_driver: Bad type %d\n",
ulp_type);
return -EINVAL;
@@ -393,7 +393,7 @@ int cnic_unregister_driver(int ulp_type)
struct cnic_ulp_ops *ulp_ops;
int i = 0;
- if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+ if (ulp_type < 0 || ulp_type >= MAX_CNIC_ULP_TYPE) {
printk(KERN_ERR PFX "cnic_unregister_driver: Bad type %d\n",
ulp_type);
return -EINVAL;
@@ -449,7 +449,7 @@ static int cnic_register_device(struct cnic_dev *dev, int ulp_type,
struct cnic_local *cp = dev->cnic_priv;
struct cnic_ulp_ops *ulp_ops;
- if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+ if (ulp_type < 0 || ulp_type >= MAX_CNIC_ULP_TYPE) {
printk(KERN_ERR PFX "cnic_register_device: Bad type %d\n",
ulp_type);
return -EINVAL;
@@ -490,7 +490,7 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
struct cnic_local *cp = dev->cnic_priv;
int i = 0;
- if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+ if (ulp_type < 0 || ulp_type >= MAX_CNIC_ULP_TYPE) {
printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n",
ulp_type);
return -EINVAL;
^ permalink raw reply related
* Re: [RFC] bridge: check address size
From: Stephen Hemminger @ 2009-11-02 16:54 UTC (permalink / raw)
To: David Miller; +Cc: nneul, netdev
In-Reply-To: <20091102.000756.222203879.davem@davemloft.net>
On Mon, 02 Nov 2009 00:07:56 -0800 (PST)
David Miller <davem@davemloft.net> wrote:
> From: Stephen Hemminger <shemminger@vyatta.com>
> Date: Thu, 29 Oct 2009 15:24:08 -0700
>
> > - if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER)
> > + /* Don't allow bridging non ethernet like devices */
> > + if (dev->flags & IFF_LOOPBACK
> > + || dev->type != ARPHRD_ETHER
> > + || dev->addr_len != ETH_ALEN)
>
> Please format this as:
>
> > + if (dev->flags & IFF_LOOPBACK ||
> > + dev->type != ARPHRD_ETHER ||
> > + dev->addr_len != ETH_ALEN)
>
> What you're doing in the patch follows the GNU coding standards, no
> the kernel ones. :-)
Sure, I never saw which side to put conditionals as part of coding standard document.
--
^ permalink raw reply
* Re: [net-next-2.6 PATCH v4 3/3] TCPCT part 1c: initial SYN exchange with SYNACK data
From: Joe Perches @ 2009-11-02 17:00 UTC (permalink / raw)
To: William Allen Simpson; +Cc: Eric Dumazet, Linux Kernel Network Developers
In-Reply-To: <4AEECFA8.1080306@gmail.com>
On Mon, 2009-11-02 at 07:25 -0500, William Allen Simpson wrote:
> For complex tests, this makes the code much more readable and easier to
> visually verify on code walk-through:
>
> + if (0 < tmp_opt.cookie_plus
> + && tmp_opt.saw_tstamp
> + && !tp->cookie_out_never
> + && (0 < sysctl_tcp_cookie_size
> + || (NULL != tp->cookie_values
> + && 0 < tp->cookie_values->cookie_desired))) {
>
> Consistent use of security style would have obviated a lot of foolish >= 0
> tests that seem to be constantly in need of fixing. It's a bad idea to
> depend on the compiler to catch non-executable code.
Linus wrote a long time back (5+ years):
The reason for "if (x == 8)" comes from the way we're taught to think.
Arguing against that _fact_ is just totally non-productive, and you have
to _force_ yourself to write it the other way around.
And that just means that you will do other mistakes. You'll spend your
time thinking about trying to express your conditionals in strange ways,
and then not think about the _real_ issue.
So let's make a few rules:
- write your logical expressions the way people EXPECT them to be
written. No silly rules that make no sense.
Ergo:
if (x == 8)
is the ONE AND ONLY SANE WAY.
- avoid using assignment inside logical expressions unless you have a
damn good reason to.
Ergo: write
error = myfunction(xxxx)
if (error) {
...
instead of writing
if (error = myfunction(xxxx))
....
which is just unreadable and stupid.
- Don't get hung about stupid rules.
Ergo: sometimes assignments in conditionals make sense, especially in
loops. Don't avoid them just because of some silly rule. But strive to
use an explicit equality test when you do so:
while ((a = function(b)) != 0)
...
is fine.
- The compiler warns about the mistakes that remain, if you follow these
rules.
- mistakes happen. Deal with it. Having tons of rules just makes them
more likely. Expect mistakes, and make sure they are fixed quickly
^ permalink raw reply
* Re: pull request: wireless-next-2.6 2009-10-28
From: John W. Linville @ 2009-11-02 17:01 UTC (permalink / raw)
To: Ingo Molnar
Cc: Johannes Berg, Jarek Poplawski, Bartlomiej Zolnierkiewicz,
Pekka Enberg, David Miller, linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20091102100702.GA16963-X9Un+BFzKDI@public.gmane.org>
On Mon, Nov 02, 2009 at 11:07:02AM +0100, Ingo Molnar wrote:
>
> * Johannes Berg <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org> wrote:
>
> > On Mon, 2009-11-02 at 10:10 +0100, Ingo Molnar wrote:
> >
> > > So i have read the thread you and Bartlomiej referenced:
> > >
> > > http://lkml.org/lkml/2009/10/17/81
> > >
> > > ... and my understanding of that discussion is very different from
> > > yours. Here is my annotated history of the beginnings of that
> > > discussion:
> >
> > [snip]
> >
> > You shouldn't ignore all previous interaction between Bart and us --
> > which wasn't pretty: http://thread.gmane.org/gmane.linux.kernel/901892
>
> I have seen that exchange too - here's the lkml.org link for those who
> like the lkml.org format:
>
> http://lkml.org/lkml/2009/10/13/186
>
> And i can see no supporting fact here either, for the (very serious)
> accusation launched by John Linville, that Bartlomiej is 'petty,
> whining, indignant'. In my reading he is the opposite of that, even in
> this second thread you point out.
Again, you seem to be very generous to Bartlomiej and very critical of
the rest of us in your readings.
> So, no matter how much you disagree about the code and its direction,
> please either back up your assertion with specific links to a pattern of
> misbehavior or apologize for the ad-hominen attacks against Bartlomiej.
His behavior is evident in the threads you cited. How you do not
see that is a mystery.
> > Of course we were biased when he came around with that petty code
> > duplication argument, since it seemed to support only his agenda of
> > working only with the staging drivers.
>
> Why do you think that disagreeing in the past gives you the right to get
> into ad-hominens? You should concentrate on the code and on the
> technical side, not on the person making the argument.
>
> Also, why do you characterise a code duplication argument as 'petty'?
Did you actually look at the thread? Or did you simply here "code
duplication" and run with it?
> Bloat and unnecessary technical forking is the #1 enemy of Linux.
> Integrating code and infrastructure is the #1 strength of Linux.
>
> Upstream subsystems/drivers running away with their private
> implementations has its clear costs:
...which has nothing to do with the case at hand. Please do not
perpetuate this myth. If anything, your argument applies far more
to the drivers in staging that Bartlomiej has focused his attention
upon until recently.
<snip>
> [ I dont know why drivers/staging/ is even an argument here - he argued
> about the technical qualities of a new upstream driver, not about a
> staging driver. Upstream drivers are to be held to higher standards,
> _especially_ now that we can isolate not-clean-enough-yet drivers into
> drivers/staging/, without hurting users. ]
If you had been keeping-up, you would realize that Bartlomiej is upset
about our criticisms of the Ralink drivers in staging. That would seem
to be the reason he has made himself such an irritant in linux-wireless
over the past few weeks.
John
--
John W. Linville Someday the world will need a hero, and you
linville-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org might be all we have. Be ready.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: Connection tracking and vlan
From: Eric W. Biederman @ 2009-11-02 17:02 UTC (permalink / raw)
To: Patrick McHardy
Cc: Eric Dumazet, Adayadil Thomas, Ben Greear, Herbert Xu,
Linux Netdev List, Netfilter Development Mailinglist
In-Reply-To: <4AEF0D69.7070205@trash.net>
Patrick McHardy <kaber@trash.net> writes:
> Eric Dumazet wrote:
>> Patrick McHardy a écrit :
>>> But it would be preferrable if we could do this using network
>>> namespaces somehow.
>>
>> eg eth0.100 and eth0.101 on namespace 1, eth0.200 and eth0.201 on namespace 2 ?
>>
>> Can we do that with current kernel ? (different vlans on an unique physical device)
>
> By default the underlying device needs to exist in the same namespace
> in which the VLAN device is created. I believe it should be possible
> to move the VLAN device to a different namespace after creating it,
> but I'm not sure about that.
There should be no problem moving the vlan device after it is created.
Eric
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [net-next-2.6 PATCH v4 3/3] TCPCT part 1c: initial SYN exchange with SYNACK data
From: Eric Dumazet @ 2009-11-02 17:04 UTC (permalink / raw)
To: William Allen Simpson; +Cc: Linux Kernel Network Developers, Ilpo Järvinen
In-Reply-To: <4AEF0603.7060502@gmail.com>
William Allen Simpson a écrit :
> It will be, when we have running code, as I'm loath to publish until I'm
> certain it *can* be implemented.
This is why RFC is better before coding. To avoid wasting time on experiments
that have a fatal flaw. Once included in an official kernel, we wont be able
to change some parameters very easily (think about 253 constant you use)
Or do you think only *you* can understand what's going on, and we should
just trust you ?
>
> I've something like 40 RFCs published over the years.
I believe I know who you are, Mr William Allen Simpson, you dont need to
repeat how much work you did in the past. This is a bit annoying.
Some patches need 6-12 months of polishing before inclusion, there is nothing
wrong about it. It only depends on your cooperation and patience.
And yes, even if a patch comes from Linus Torvald himself, I can talk if
it does not please me.
SYNFLOOD problem is more than 13 years old, I am quite surprised its becoming
so urgent we should accept your patches "as is".
May I suggest to switch to normal mode, ie you prepare a next round of
patches, you submit them, we review them, [repeat 0-N time(s)], we Ack them ?
^ permalink raw reply
* Re: [net-next-2.6 PATCH RFC] TCPCT part 1d: generate Responder Cookie
From: William Allen Simpson @ 2009-11-02 17:21 UTC (permalink / raw)
To: Eric Dumazet; +Cc: Linux Kernel Developers, Linux Kernel Network Developers
In-Reply-To: <4AEEDBBC.40800@gmail.com>
Eric Dumazet wrote:
> Large part of network code is run by softirq handler, and a softirq handler
> is not preemptable with another softirq (including itself).
>
Thank you. That's helpful to know, as some existing locks have a "bh".
I've never figured out the ip_local_deliver_finish() context.
Knowing that there can only be one instance of the tcp stack running at
any one time, and the cpu never changes even after being interrupted, will
make it much easier to code.
>> Perhaps a function header comment that mentions it?
>
> So we are going to add a header to thousand of functions repeating this prereq ?
>
That's my usual practice. (Dozens would be more accurate in this case.)
I've always found it helpful for those coming after me, and sure would have
found it helpful now myself.... Repetitious, but well worth it.
Especially at tcp_v4_rcv(), as that's called through a vector named
"handler", which was particularly hard to track down.
It has an innocuous header "From tcp_input.c", that doesn't seem to have
anything to do with current reality.... (It's really called from
ip_input.c via af_inet.c).
>> All I know is (from testing) that the tcp_minisockets.c caller is sometimes
>> called in a fashion that requires atomic allocation, and other times
>> does not!
>
> Maybe callers have different contexts (running from softirq handler or
> from process context). Atomic ops are expensive and we try to avoid them
> if/when possible.
>
>> See my "Subject: query: tcpdump versus atomic?" thread from Oct 14th.
>
> You probably add a bug in your kernel, leaving a function with unpaired lock/unlock
> of notallow_something/allow_something
>
(No, I've not yet added locks; obviously, I'm still asking about them.)
Unlikely, as it was easy to reproduce by changing one line, without *any* of
my code present. Usually works, but doesn't work with tcpdump running on
the interface:
struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, struct sk_buff *skb)
{
- struct sock *newsk = inet_csk_clone(sk, req, GFP_ATOMIC);
+ struct sock *newsk = inet_csk_clone(sk, req, GFP_KERNEL);
if (newsk != NULL) {
[ 2.876485] eth0: RealTek RTL8139 at 0x2000, 00:40:2b:6b:61:36, IRQ 17
[ 2.876490] eth0: Identified 8139 chip type 'RTL-8101'
[ 88.997594] device eth0 entered promiscuous mode
[ 114.827403] BUG: scheduling while atomic: swapper/0/0x10000100
[ 114.827462] Modules linked in: lp snd_intel8x0 snd_ac97_codec ac97_bus snd_pcm snd_timer snd ppdev iTCO_wdt iTCO_vendor_support psmouse soundcore parport_pc intel_agp parport agpgart pcspkr serio_raw shpchp snd_page_alloc 8139too aic7xxx 8139cp
scsi_transport_spi mii floppy
[ 114.827493]
[ 114.827497] Pid: 0, comm: swapper Not tainted (2.6.32-rc3 #4) Imperial
[ 114.827501] EIP: 0060:[<c0123295>] EFLAGS: 00000246 CPU: 0
[ 114.827512] EIP is at native_safe_halt+0x5/0x10
[ 114.827515] EAX: c0740000 EBX: 00000000 ECX: ffff4b6e EDX: 00000000
[ 114.827519] ESI: c07992c0 EDI: c0743000 EBP: c0741fa0 ESP: c0741fa0
[ 114.827522] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
[ 114.827525] CR0: 8005003b CR2: 09278fc4 CR3: 04b56000 CR4: 00000690
[ 114.827529] DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000
[ 114.827532] DR6: ffff0ff0 DR7: 00000400
[ 114.827535] Call Trace:
[ 114.827546] [<c01098b5>] default_idle+0x65/0x90
[ 114.827550] [<c0102062>] cpu_idle+0x52/0x90
[ 114.827558] [<c056cc23>] rest_init+0x53/0x60
[ 114.827565] [<c079c93d>] start_kernel+0x328/0x390
[ 114.827569] [<c079c3ce>] ? unknown_bootoption+0x0/0x1f6
[ 114.827574] [<c079c07e>] i386_start_kernel+0x7e/0xa8
[ 136.570632] device eth0 left promiscuous mode
> There are books about linux internals that you could read if you want some extra
> documentation. Dont ask me details, I never read them :)
>
Sorry, I've only read much of the Documentation directory (some parts
repeatedly), and Googled for more specific information. Pretty sparse!
Thank you again for your patient explanation.
^ permalink raw reply
* [PATCHv2] tun: export underlying socket
From: Michael S. Tsirkin @ 2009-11-02 17:20 UTC (permalink / raw)
To: David Miller, Eric Dumazet; +Cc: Paul Moore, netdev, herbert
In-Reply-To: <20090910125929.GA32593@redhat.com>
Tun device looks similar to a packet socket
in that both pass complete frames from/to userspace.
This patch fills in enough fields in the socket underlying tun driver
to support sendmsg/recvmsg operations, and message flags
MSG_TRUNC and MSG_DONTWAIT, and exports access to this socket
to modules. Regular read/write behaviour is unchanged.
This way, code using raw sockets to inject packets
into a physical device, can support injecting
packets into host network stack almost without modification.
First user of this interface will be vhost virtualization
accelerator.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
Changes from v1:
addressed comments from review
This patch is on top of net-next master.
vhost which is the first user of this interface is intended to be merged
through Rusty's virtio tree, so I think it's easiest to merge this patch
trough that tree, as well, as part of the vhost patchset.
Makes sense? Please comment.
Usage example in vhost is here:
http://git.kernel.org/?p=linux/kernel/git/mst/vhost.git;a=commitdiff;h=030c5e25539e8fb14ca9130621710b7a006f3c1c
An alternative approach would be to make sendmsg work on tun fd from
userspace as well as from kernel: a uniform way to work with a network
device and the host stack might be useful there, as well. Kernel users
could then do sockfd_lookup to get the socket. I decided against it for
now as it requires more code and touches net core.
Please comment.
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 4fdfa2a..18f8876 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -144,6 +144,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file)
err = 0;
tfile->tun = tun;
tun->tfile = tfile;
+ tun->socket.file = file;
dev_hold(tun->dev);
sock_hold(tun->socket.sk);
atomic_inc(&tfile->count);
@@ -158,6 +159,7 @@ static void __tun_detach(struct tun_struct *tun)
/* Detach from net device */
netif_tx_lock_bh(tun->dev);
tun->tfile = NULL;
+ tun->socket.file = NULL;
netif_tx_unlock_bh(tun->dev);
/* Drop read queue */
@@ -387,7 +389,8 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
/* Notify and wake up reader process */
if (tun->flags & TUN_FASYNC)
kill_fasync(&tun->fasync, SIGIO, POLL_IN);
- wake_up_interruptible(&tun->socket.wait);
+ wake_up_interruptible_poll(&tun->socket.wait, POLLIN |
+ POLLRDNORM | POLLRDBAND);
return NETDEV_TX_OK;
drop:
@@ -743,7 +746,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
len = min_t(int, skb->len, len);
skb_copy_datagram_const_iovec(skb, 0, iv, total, len);
- total += len;
+ total += skb->len;
tun->dev->stats.tx_packets++;
tun->dev->stats.tx_bytes += len;
@@ -751,34 +754,23 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
return total;
}
-static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
- unsigned long count, loff_t pos)
+static ssize_t tun_do_read(struct tun_struct *tun,
+ struct kiocb *iocb, const struct iovec *iv,
+ ssize_t len, int noblock)
{
- struct file *file = iocb->ki_filp;
- struct tun_file *tfile = file->private_data;
- struct tun_struct *tun = __tun_get(tfile);
DECLARE_WAITQUEUE(wait, current);
struct sk_buff *skb;
- ssize_t len, ret = 0;
-
- if (!tun)
- return -EBADFD;
+ ssize_t ret = 0;
DBG(KERN_INFO "%s: tun_chr_read\n", tun->dev->name);
- len = iov_length(iv, count);
- if (len < 0) {
- ret = -EINVAL;
- goto out;
- }
-
add_wait_queue(&tun->socket.wait, &wait);
while (len) {
current->state = TASK_INTERRUPTIBLE;
/* Read frames from the queue */
if (!(skb=skb_dequeue(&tun->socket.sk->sk_receive_queue))) {
- if (file->f_flags & O_NONBLOCK) {
+ if (noblock) {
ret = -EAGAIN;
break;
}
@@ -805,6 +797,27 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
current->state = TASK_RUNNING;
remove_wait_queue(&tun->socket.wait, &wait);
+ return ret;
+}
+
+static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
+ unsigned long count, loff_t pos)
+{
+ struct file *file = iocb->ki_filp;
+ struct tun_file *tfile = file->private_data;
+ struct tun_struct *tun = __tun_get(tfile);
+ ssize_t len, ret;
+
+ if (!tun)
+ return -EBADFD;
+ len = iov_length(iv, count);
+ if (len < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = tun_do_read(tun, iocb, iv, len, file->f_flags & O_NONBLOCK);
+ ret = min_t(ssize_t, ret, len);
out:
tun_put(tun);
return ret;
@@ -847,7 +860,8 @@ static void tun_sock_write_space(struct sock *sk)
return;
if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
- wake_up_interruptible_sync(sk->sk_sleep);
+ wake_up_interruptible_sync_poll(sk->sk_sleep, POLLOUT |
+ POLLWRNORM | POLLWRBAND);
tun = container_of(sk, struct tun_sock, sk)->tun;
kill_fasync(&tun->fasync, SIGIO, POLL_OUT);
@@ -858,6 +872,37 @@ static void tun_sock_destruct(struct sock *sk)
free_netdev(container_of(sk, struct tun_sock, sk)->tun->dev);
}
+static int tun_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len)
+{
+ struct tun_struct *tun = container_of(sock, struct tun_struct, socket);
+ return tun_get_user(tun, m->msg_iov, total_len,
+ m->msg_flags & MSG_DONTWAIT);
+}
+
+static int tun_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len,
+ int flags)
+{
+ struct tun_struct *tun = container_of(sock, struct tun_struct, socket);
+ int ret;
+ if (flags & ~(MSG_DONTWAIT|MSG_TRUNC))
+ return -EINVAL;
+ ret = tun_do_read(tun, iocb, m->msg_iov, total_len,
+ flags & MSG_DONTWAIT);
+ if (ret > total_len) {
+ m->msg_flags |= MSG_TRUNC;
+ ret = flags & MSG_TRUNC ? ret : total_len;
+ }
+ return ret;
+}
+
+/* Ops structure to mimic raw sockets with tun */
+static const struct proto_ops tun_socket_ops = {
+ .sendmsg = tun_sendmsg,
+ .recvmsg = tun_recvmsg,
+};
+
static struct proto tun_proto = {
.name = "tun",
.owner = THIS_MODULE,
@@ -986,6 +1031,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
goto err_free_dev;
init_waitqueue_head(&tun->socket.wait);
+ tun->socket.ops = &tun_socket_ops;
sock_init_data(&tun->socket, sk);
sk->sk_write_space = tun_sock_write_space;
sk->sk_sndbuf = INT_MAX;
@@ -1489,6 +1535,23 @@ static void tun_cleanup(void)
rtnl_link_unregister(&tun_link_ops);
}
+/* Get an underlying socket object from tun file. Returns error unless file is
+ * attached to a device. The returned object works like a packet socket, it
+ * can be used for sock_sendmsg/sock_recvmsg. The caller is responsible for
+ * holding a reference to the file for as long as the socket is in use. */
+struct socket *tun_get_socket(struct file *file)
+{
+ struct tun_struct *tun;
+ if (file->f_op != &tun_fops)
+ return ERR_PTR(-EINVAL);
+ tun = tun_get(file);
+ if (!tun)
+ return ERR_PTR(-EBADFD);
+ tun_put(tun);
+ return &tun->socket;
+}
+EXPORT_SYMBOL_GPL(tun_get_socket);
+
module_init(tun_init);
module_exit(tun_cleanup);
MODULE_DESCRIPTION(DRV_DESCRIPTION);
diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h
index 3f5fd52..404abe0 100644
--- a/include/linux/if_tun.h
+++ b/include/linux/if_tun.h
@@ -86,4 +86,18 @@ struct tun_filter {
__u8 addr[0][ETH_ALEN];
};
+#ifdef __KERNEL__
+#if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE)
+struct socket *tun_get_socket(struct file *);
+#else
+#include <linux/err.h>
+#include <linux/errno.h>
+struct file;
+struct socket;
+static inline struct socket *tun_get_socket(struct file *f)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif /* CONFIG_TUN */
+#endif /* __KERNEL__ */
#endif /* __IF_TUN_H */
^ permalink raw reply related
* Re: pull request: wireless-next-2.6 2009-10-28
From: Luis Correia @ 2009-11-02 17:32 UTC (permalink / raw)
To: John W. Linville
Cc: Ingo Molnar, Johannes Berg, Jarek Poplawski,
Bartlomiej Zolnierkiewicz, Pekka Enberg, David Miller,
linux-wireless, netdev, linux-kernel
In-Reply-To: <20091102170114.GE14046@tuxdriver.com>
[huge snip]
This is going to be my last reply to this thread (even if I get to be insulted).
I'm not a coder, I'm a tester and administrator for the rt2x00 project.
I'm not happy about the current state of the rt2x00 in-kernel drivers.
I tend to agree that more could be done.
Even though I'm not a coder, I could be hipotetically responsable for
the current situation. It is my job function to motivate people to
work on the rt2x00 in-kernel drivers, but apparently I've failed here.
It's not an atitude problem, as some have suggested, it's just life.
About Bartlomiej, there is only one more thing that I'll say:
I've searched on my GMail archives and the only patch Bart has
provided so far for the rt2x00 project is this:
[PATCH] MAINTAINERS: rt2x00 list is moderated
Which, while technically correct, adds nothing to the project.
So, I will personally continue to ignore Bart's comments, regards and
rants, until he provides patches for rt2x00 that actually make the
driver better.
Luis Correia
rt2x00 project admin,
(but not writing this email as an official project statement)
^ permalink raw reply
* Re: pull request: wireless-next-2.6 2009-10-28
From: Randy Dunlap @ 2009-11-02 17:36 UTC (permalink / raw)
To: Luis Correia
Cc: John W. Linville, Ingo Molnar, Johannes Berg, Jarek Poplawski,
Bartlomiej Zolnierkiewicz, Pekka Enberg, David Miller,
linux-wireless, netdev, linux-kernel
In-Reply-To: <efe7343f0911020932l21a570c8u9478568329d38e2c@mail.gmail.com>
Luis Correia wrote:
> [huge snip]
>
> This is going to be my last reply to this thread (even if I get to be insulted).
>
> I'm not a coder, I'm a tester and administrator for the rt2x00 project.
> I'm not happy about the current state of the rt2x00 in-kernel drivers.
> I tend to agree that more could be done.
>
> Even though I'm not a coder, I could be hipotetically responsable for
> the current situation. It is my job function to motivate people to
> work on the rt2x00 in-kernel drivers, but apparently I've failed here.
> It's not an atitude problem, as some have suggested, it's just life.
>
> About Bartlomiej, there is only one more thing that I'll say:
>
> I've searched on my GMail archives and the only patch Bart has
> provided so far for the rt2x00 project is this:
>
> [PATCH] MAINTAINERS: rt2x00 list is moderated
>
> Which, while technically correct, adds nothing to the project.
whatever. That patch still needs to be applied.
> So, I will personally continue to ignore Bart's comments, regards and
> rants, until he provides patches for rt2x00 that actually make the
> driver better.
>
> Luis Correia
> rt2x00 project admin,
> (but not writing this email as an official project statement)
--
~Randy
^ permalink raw reply
* Re: [net-next-2.6 PATCH RFC] TCPCT part 1d: generate Responder Cookie
From: Eric Dumazet @ 2009-11-02 17:42 UTC (permalink / raw)
To: William Allen Simpson
Cc: Linux Kernel Developers, Linux Kernel Network Developers
In-Reply-To: <4AEF1534.4090506@gmail.com>
William Allen Simpson a écrit :
> Eric Dumazet wrote:
>> Large part of network code is run by softirq handler, and a softirq
>> handler
>> is not preemptable with another softirq (including itself).
>>
> Thank you. That's helpful to know, as some existing locks have a "bh".
> I've never figured out the ip_local_deliver_finish() context.
>
> Knowing that there can only be one instance of the tcp stack running at
> any one time, and the cpu never changes even after being interrupted, will
> make it much easier to code.
Correction :
There is one instance of sofirq handler running per cpu.
So TCP (or UDP) stack can run simultaneously on several (and eventually all online) cpus.
This is why we still need some locks in various places.
>>
> (No, I've not yet added locks; obviously, I'm still asking about them.)
>
> Unlikely, as it was easy to reproduce by changing one line, without
> *any* of
> my code present. Usually works, but doesn't work with tcpdump running on
> the interface:
Yes, tcpdump has the nasty habit to consume a lot of ram, queuing a copy of
all network traffic on an af_packet socket. (or using a mmap buffer, it depends
on libpcap version you use)
>
> struct sock *tcp_create_openreq_child(struct sock *sk, struct
> request_sock *req, struct sk_buff *skb)
> {
> - struct sock *newsk = inet_csk_clone(sk, req, GFP_ATOMIC);
> + struct sock *newsk = inet_csk_clone(sk, req, GFP_KERNEL);
Here you want a GFP_KERNEL allocation, that is allowed to sleep if there is not
enough available memory. (It's allowed to sleep to let some processes to free
bit of ram, eventually)
But as the caller of tcp_create_openreq_child() runs from {soft}irq context,
its not allowed to sleep at all, even 10 usecs.
Therefore, linux kernel kindly warns you that's its illegal and deadlockable.
Dont change GFP_ATOMIC here, its really there for a valid reason.
And be prepared to get a NULL result from allocation.
If your machine has problems when running tcpdump, maybe it lacks some ram, maybe you could
tune tcpdump socket to not exhaust all LOWMEM.
I see your kernel is 32bits, so you have only 860 MB of kernel memory, called LOWMEM.
I believe last kernels might have some problems in OOM situations...
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox