Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v3]  net: add Fast Ethernet driver for PXA168.
From: Sachin Sanap @ 2010-08-16 10:07 UTC (permalink / raw)
  To: Philip Rakity
  Cc: Lennert Buytenhek, netdev@vger.kernel.org, Ashish Karkare,
	Prabhanjan Sarnaik, eric.y.miao@gmail.com, Mark Brown
In-Reply-To: <16B97483-C552-479E-876E-019A4F217F87@marvell.com>

On Sat, 2010-08-14 at 13:47 -0700, Philip Rakity wrote:
> 
> I was wondering why you need to take a tx interrupt done per packet.  The only normal reason for the interrupt is to restart the higher layer when the tx queue is blocked (FULL) due to flow control or sending traffic too fast.  The original code tried to minimize tx interrupts.
Restarting the higher layer is just one of the thing that we do, we also
kfree_skb() after the interrupt and by not generating any interrupt we
are delaying it indefinitely which is wrong.
> 
> The other point was the you should set the DMA ownership bit as the LAST operation after deciding if you need to set the TX interrupt bit in the descriptor and not before making the decision.
> 
I did not get your point. We are currently setting the DMA ownership at
last only.

> Philip
> 
> 
> On Aug 14, 2010, at 12:22 AM, Sachin Sanap wrote:
> 
> > From df92c2959eb0e50ea63c90822fb9e1edef673ea6 Mon Sep 17 00:00:00 2001
> > From: Sachin Sanap <ssanap@marvell.com>
> > Date: Sat, 14 Aug 2010 17:11:05 +0530
> > Subject: [PATCH] net: add Fast Ethernet driver for PXA168.
> >
> > Signed-off-by: Sachin Sanap <ssanap@marvell.com>
> > ---
> > V3:
> > * TX_DONE interrupt is now generated for every packet.
> > * MTU can now be configured upto 9500 bytes.
> > * TX/RX ring sizes are now platform configurable.
> > * Remove the CPU hogging and yield the CPU while SMI operations take time.
> > * Cleanup the hash table for old MAC address entries in set_rx_mode function.
> >
> > drivers/net/Kconfig        |   10 +
> > drivers/net/Makefile       |    1 +
> > drivers/net/pxa168_eth.c   | 1666 ++++++++++++++++++++++++++++++++++++++++++++
> > include/linux/pxa168_eth.h |   30 +
> > 4 files changed, 1707 insertions(+), 0 deletions(-)
> > create mode 100644 drivers/net/pxa168_eth.c
> > create mode 100644 include/linux/pxa168_eth.h
> >
> > diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> > index ce2fcdd..78cd7e8 100644
> > --- a/drivers/net/Kconfig
> > +++ b/drivers/net/Kconfig
> > @@ -927,6 +927,16 @@ config SMC91X
> >          The module will be called smc91x.  If you want to compile it as a
> >          module, say M here and read <file:Documentation/kbuild/modules.txt>.
> >
> > +config PXA168_ETH
> > +       tristate "Marvell pxa168 ethernet support"
> > +       depends on CPU_PXA168
> > +       select PHYLIB
> > +       help
> > +         This driver supports the pxa168 Ethernet ports.
> > +
> > +         To compile this driver as a module, choose M here. The module
> > +         will be called pxa168_eth.
> > +
> > config NET_NETX
> >        tristate "NetX Ethernet support"
> >        select MII
> > diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> > index 0a0512a..a42d437 100644
> > --- a/drivers/net/Makefile
> > +++ b/drivers/net/Makefile
> > @@ -244,6 +244,7 @@ obj-$(CONFIG_MYRI10GE) += myri10ge/
> > obj-$(CONFIG_SMC91X) += smc91x.o
> > obj-$(CONFIG_SMC911X) += smc911x.o
> > obj-$(CONFIG_SMSC911X) += smsc911x.o
> > +obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
> > obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
> > obj-$(CONFIG_DM9000) += dm9000.o
> > obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
> > diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
> > new file mode 100644
> > index 0000000..ecc64d7
> > --- /dev/null
> > +++ b/drivers/net/pxa168_eth.c
> > @@ -0,0 +1,1666 @@
> > +/*
> > + * PXA168 ethernet driver.
> > + * Most of the code is derived from mv643xx ethernet driver.
> > + *
> > + * Copyright (C) 2010 Marvell International Ltd.
> > + *             Sachin Sanap <ssanap@marvell.com>
> > + *             Philip Rakity <prakity@marvell.com>
> > + *             Mark Brown <markb@marvell.com>
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License
> > + * as published by the Free Software Foundation; either version 2
> > + * of the License, or (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, write to the Free Software
> > + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
> > + */
> > +
> > +#include <linux/init.h>
> > +#include <linux/dma-mapping.h>
> > +#include <linux/in.h>
> > +#include <linux/ip.h>
> > +#include <linux/tcp.h>
> > +#include <linux/udp.h>
> > +#include <linux/etherdevice.h>
> > +#include <linux/bitops.h>
> > +#include <linux/delay.h>
> > +#include <linux/ethtool.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/module.h>
> > +#include <linux/kernel.h>
> > +#include <linux/workqueue.h>
> > +#include <linux/clk.h>
> > +#include <linux/phy.h>
> > +#include <linux/io.h>
> > +#include <linux/types.h>
> > +#include <asm/pgtable.h>
> > +#include <asm/system.h>
> > +#include <linux/delay.h>
> > +#include <linux/dma-mapping.h>
> > +#include <asm/cacheflush.h>
> > +#include <linux/pxa168_eth.h>
> > +
> > +#define DRIVER_NAME    "pxa168-eth"
> > +#define DRIVER_VERSION "0.3"
> > +
> > +/*
> > + * Registers
> > + */
> > +
> > +#define PHY_ADDRESS            0x0000
> > +#define SMI                    0x0010
> > +#define PORT_CONFIG            0x0400
> > +#define PORT_CONFIG_EXT                0x0408
> > +#define PORT_COMMAND           0x0410
> > +#define PORT_STATUS            0x0418
> > +#define HTPR                   0x0428
> > +#define SDMA_CONFIG            0x0440
> > +#define SDMA_CMD               0x0448
> > +#define INT_CAUSE              0x0450
> > +#define INT_W_CLEAR            0x0454
> > +#define INT_MASK               0x0458
> > +#define ETH_F_RX_DESC_0                0x0480
> > +#define ETH_C_RX_DESC_0                0x04A0
> > +#define ETH_C_TX_DESC_1                0x04E4
> > +
> > +/* smi register */
> > +#define SMI_BUSY               (1 << 28)       /* 0 - Write, 1 - Read  */
> > +#define SMI_R_VALID            (1 << 27)       /* 0 - Write, 1 - Read  */
> > +#define SMI_OP_W               (0 << 26)       /* Write operation      */
> > +#define SMI_OP_R               (1 << 26)       /* Read operation */
> > +
> > +#define PHY_WAIT_ITERATIONS    10
> > +
> > +#define PXA168_ETH_PHY_ADDR_DEFAULT    0
> > +/* RX & TX descriptor command */
> > +#define BUF_OWNED_BY_DMA       (1 << 31)
> > +
> > +/* RX descriptor status */
> > +#define RX_EN_INT              (1 << 23)
> > +#define RX_FIRST_DESC          (1 << 17)
> > +#define RX_LAST_DESC           (1 << 16)
> > +#define RX_ERROR               (1 << 15)
> > +
> > +/* TX descriptor command */
> > +#define TX_EN_INT              (1 << 23)
> > +#define TX_GEN_CRC             (1 << 22)
> > +#define TX_ZERO_PADDING                (1 << 18)
> > +#define TX_FIRST_DESC          (1 << 17)
> > +#define TX_LAST_DESC           (1 << 16)
> > +#define TX_ERROR               (1 << 15)
> > +
> > +/* SDMA_CMD */
> > +#define SDMA_CMD_AT            (1 << 31)
> > +#define SDMA_CMD_TXDL          (1 << 24)
> > +#define SDMA_CMD_TXDH          (1 << 23)
> > +#define SDMA_CMD_AR            (1 << 15)
> > +#define SDMA_CMD_ERD           (1 << 7)
> > +
> > +/* Bit definitions of the Port Config Reg */
> > +#define PCR_HS                 (1 << 12)
> > +#define PCR_EN                 (1 << 7)
> > +#define PCR_PM                 (1 << 0)
> > +
> > +/* Bit definitions of the Port Config Extend Reg */
> > +#define PCXR_2BSM              (1 << 28)
> > +#define PCXR_DSCP_EN           (1 << 21)
> > +#define PCXR_MFL_1518          (0 << 14)
> > +#define PCXR_MFL_1536          (1 << 14)
> > +#define PCXR_MFL_2048          (2 << 14)
> > +#define PCXR_MFL_64K           (3 << 14)
> > +#define PCXR_FLP               (1 << 11)
> > +#define PCXR_PRIO_TX_OFF       3
> > +#define PCXR_TX_HIGH_PRI       (7 << PCXR_PRIO_TX_OFF)
> > +
> > +/* Bit definitions of the SDMA Config Reg */
> > +#define SDCR_BSZ_OFF           12
> > +#define SDCR_BSZ8              (3 << SDCR_BSZ_OFF)
> > +#define SDCR_BSZ4              (2 << SDCR_BSZ_OFF)
> > +#define SDCR_BSZ2              (1 << SDCR_BSZ_OFF)
> > +#define SDCR_BSZ1              (0 << SDCR_BSZ_OFF)
> > +#define SDCR_BLMR              (1 << 6)
> > +#define SDCR_BLMT              (1 << 7)
> > +#define SDCR_RIFB              (1 << 9)
> > +#define SDCR_RC_OFF            2
> > +#define SDCR_RC_MAX_RETRANS    (0xf << SDCR_RC_OFF)
> > +
> > +/*
> > + * Bit definitions of the Interrupt Cause Reg
> > + * and Interrupt MASK Reg is the same
> > + */
> > +#define ICR_RXBUF              (1 << 0)
> > +#define ICR_TXBUF_H            (1 << 2)
> > +#define ICR_TXBUF_L            (1 << 3)
> > +#define ICR_TXEND_H            (1 << 6)
> > +#define ICR_TXEND_L            (1 << 7)
> > +#define ICR_RXERR              (1 << 8)
> > +#define ICR_TXERR_H            (1 << 10)
> > +#define ICR_TXERR_L            (1 << 11)
> > +#define ICR_TX_UDR             (1 << 13)
> > +#define ICR_MII_CH             (1 << 28)
> > +
> > +#define ALL_INTS (ICR_TXBUF_H  | ICR_TXBUF_L  | ICR_TX_UDR |\
> > +                               ICR_TXERR_H  | ICR_TXERR_L |\
> > +                               ICR_TXEND_H  | ICR_TXEND_L |\
> > +                               ICR_RXBUF | ICR_RXERR  | ICR_MII_CH)
> > +
> > +#define ETH_HW_IP_ALIGN                2       /* hw aligns IP header */
> > +
> > +#define NUM_RX_DESCS           64
> > +#define NUM_TX_DESCS           64
> > +
> > +#define HASH_ADD               0
> > +#define HASH_DELETE            1
> > +#define HASH_ADDR_TABLE_SIZE   0x4000  /* 16K (1/2K address - PCR_HS == 1) */
> > +#define HOP_NUMBER             12
> > +
> > +/* Bit definitions for Port status */
> > +#define PORT_SPEED_100         (1 << 0)
> > +#define FULL_DUPLEX            (1 << 1)
> > +#define FLOW_CONTROL_ENABLED   (1 << 2)
> > +#define LINK_UP                        (1 << 3)
> > +
> > +/* Bit definitions for work to be done */
> > +#define WORK_LINK              (1 << 0)
> > +#define WORK_TX_DONE           (1 << 1)
> > +
> > +/*
> > + * Misc definitions.
> > + */
> > +#define SKB_DMA_REALIGN                ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES)
> > +
> > +struct rx_desc {
> > +       u32 cmd_sts;            /* Descriptor command status            */
> > +       u16 byte_cnt;           /* Descriptor buffer byte count         */
> > +       u16 buf_size;           /* Buffer size                          */
> > +       u32 buf_ptr;            /* Descriptor buffer pointer            */
> > +       u32 next_desc_ptr;      /* Next descriptor pointer              */
> > +};
> > +
> > +struct tx_desc {
> > +       u32 cmd_sts;            /* Command/status field                 */
> > +       u16 reserved;
> > +       u16 byte_cnt;           /* buffer byte count                    */
> > +       u32 buf_ptr;            /* pointer to buffer for this descriptor */
> > +       u32 next_desc_ptr;      /* Pointer to next descriptor           */
> > +};
> > +
> > +struct pxa168_eth_private {
> > +       int port_num;           /* User Ethernet port number    */
> > +
> > +       int rx_resource_err;    /* Rx ring resource error flag */
> > +
> > +       /* Next available and first returning Rx resource */
> > +       int rx_curr_desc_q, rx_used_desc_q;
> > +
> > +       /* Next available and first returning Tx resource */
> > +       int tx_curr_desc_q, tx_used_desc_q;
> > +
> > +       struct rx_desc *p_rx_desc_area;
> > +       dma_addr_t rx_desc_dma;
> > +       int rx_desc_area_size;
> > +       struct sk_buff **rx_skb;
> > +
> > +       struct tx_desc *p_tx_desc_area;
> > +       dma_addr_t tx_desc_dma;
> > +       int tx_desc_area_size;
> > +       struct sk_buff **tx_skb;
> > +
> > +       struct work_struct tx_timeout_task;
> > +
> > +       struct net_device *dev;
> > +       struct napi_struct napi;
> > +       u8 work_todo;
> > +       int skb_size;
> > +
> > +       struct net_device_stats stats;
> > +       /* Size of Tx Ring per queue */
> > +       int tx_ring_size;
> > +       /* Number of tx descriptors in use */
> > +       int tx_desc_count;
> > +       /* Size of Rx Ring per queue */
> > +       int rx_ring_size;
> > +       /* Number of rx descriptors in use */
> > +       int rx_desc_count;
> > +
> > +       /*
> > +        * Used in case RX Ring is empty, which can occur when
> > +        * system does not have resources (skb's)
> > +        */
> > +       struct timer_list timeout;
> > +       struct mii_bus *smi_bus;
> > +       struct phy_device *phy;
> > +
> > +       /* clock */
> > +       struct clk *clk;
> > +       struct pxa168_eth_platform_data *pd;
> > +       /*
> > +        * Ethernet controller base address.
> > +        */
> > +       void __iomem *base;
> > +
> > +       /* Pointer to the hardware address filter table */
> > +       void *htpr;
> > +       dma_addr_t htpr_dma;
> > +};
> > +
> > +struct addr_table_entry {
> > +       __le32 lo;
> > +       __le32 hi;
> > +};
> > +
> > +/* Bit fields of a Hash Table Entry */
> > +enum hash_table_entry {
> > +       HASH_ENTRY_VALID = 1,
> > +       SKIP = 2,
> > +       HASH_ENTRY_RECEIVE_DISCARD = 4,
> > +       HASH_ENTRY_RECEIVE_DISCARD_BIT = 2
> > +};
> > +
> > +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
> > +static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd *cmd);
> > +static int pxa168_init_hw(struct pxa168_eth_private *pep);
> > +static void eth_port_reset(struct net_device *dev);
> > +static void eth_port_start(struct net_device *dev);
> > +static int pxa168_eth_open(struct net_device *dev);
> > +static int pxa168_eth_stop(struct net_device *dev);
> > +static int ethernet_phy_setup(struct net_device *dev);
> > +
> > +static inline u32 rdl(struct pxa168_eth_private *pep, int offset)
> > +{
> > +       return readl(pep->base + offset);
> > +}
> > +
> > +static inline void wrl(struct pxa168_eth_private *pep, int offset, u32 data)
> > +{
> > +       writel(data, pep->base + offset);
> > +}
> > +
> > +static void abort_dma(struct pxa168_eth_private *pep)
> > +{
> > +       int delay;
> > +       int max_retries = 40;
> > +
> > +       do {
> > +               wrl(pep, SDMA_CMD, SDMA_CMD_AR | SDMA_CMD_AT);
> > +               udelay(100);
> > +
> > +               delay = 10;
> > +               while ((rdl(pep, SDMA_CMD) & (SDMA_CMD_AR | SDMA_CMD_AT))
> > +                      && delay-- > 0) {
> > +                       udelay(10);
> > +               }
> > +       } while (max_retries-- > 0 && delay <= 0);
> > +
> > +       if (max_retries <= 0)
> > +               printk(KERN_ERR "%s : DMA Stuck\n", __func__);
> > +}
> > +
> > +static int ethernet_phy_get(struct pxa168_eth_private *pep)
> > +{
> > +       unsigned int reg_data;
> > +
> > +       reg_data = rdl(pep, PHY_ADDRESS);
> > +
> > +       return (reg_data >> (5 * pep->port_num)) & 0x1f;
> > +}
> > +
> > +static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int phy_addr)
> > +{
> > +       u32 reg_data;
> > +       int addr_shift = 5 * pep->port_num;
> > +
> > +       reg_data = rdl(pep, PHY_ADDRESS);
> > +       reg_data &= ~(0x1f << addr_shift);
> > +       reg_data |= (phy_addr & 0x1f) << addr_shift;
> > +       wrl(pep, PHY_ADDRESS, reg_data);
> > +}
> > +
> > +static void ethernet_phy_reset(struct pxa168_eth_private *pep)
> > +{
> > +       int data;
> > +
> > +       data = phy_read(pep->phy, MII_BMCR);
> > +       if (data < 0)
> > +               return;
> > +
> > +       data |= BMCR_RESET;
> > +       if (phy_write(pep->phy, MII_BMCR, data) < 0)
> > +               return;
> > +
> > +       do {
> > +               data = phy_read(pep->phy, MII_BMCR);
> > +       } while (data >= 0 && data & BMCR_RESET);
> > +}
> > +
> > +static void rxq_refill(struct net_device *dev)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       struct sk_buff *skb;
> > +       struct rx_desc *p_used_rx_desc;
> > +       int used_rx_desc;
> > +
> > +       while (pep->rx_desc_count < pep->rx_ring_size) {
> > +               int size;
> > +
> > +               skb = dev_alloc_skb(pep->skb_size);
> > +               if (!skb)
> > +                       break;
> > +               if (SKB_DMA_REALIGN)
> > +                       skb_reserve(skb, SKB_DMA_REALIGN);
> > +               pep->rx_desc_count++;
> > +               /* Get 'used' Rx descriptor */
> > +               used_rx_desc = pep->rx_used_desc_q;
> > +               p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc];
> > +               size = skb->end - skb->data;
> > +               p_used_rx_desc->buf_ptr = dma_map_single(NULL,
> > +                                                        skb->data,
> > +                                                        size,
> > +                                                        DMA_FROM_DEVICE);
> > +               p_used_rx_desc->buf_size = size;
> > +               pep->rx_skb[used_rx_desc] = skb;
> > +
> > +               /* Return the descriptor to DMA ownership */
> > +               wmb();
> > +               p_used_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
> > +               wmb();
> > +
> > +               /* Move the used descriptor pointer to the next descriptor */
> > +               pep->rx_used_desc_q = (used_rx_desc + 1) % pep->rx_ring_size;
> > +
> > +               /* Any Rx return cancels the Rx resource error status */
> > +               pep->rx_resource_err = 0;
> > +
> > +               skb_reserve(skb, ETH_HW_IP_ALIGN);
> > +       }
> > +
> > +       /*
> > +        * If RX ring is empty of SKB, set a timer to try allocating
> > +        * again at a later time.
> > +        */
> > +       if (pep->rx_desc_count == 0) {
> > +               pep->timeout.expires = jiffies + (HZ / 10);
> > +               add_timer(&pep->timeout);
> > +       }
> > +}
> > +
> > +static inline void rxq_refill_timer_wrapper(unsigned long data)
> > +{
> > +       struct pxa168_eth_private *pep = (void *)data;
> > +       napi_schedule(&pep->napi);
> > +}
> > +
> > +static inline u8 flip_8_bits(u8 x)
> > +{
> > +       return (((x) & 0x01) << 3) | (((x) & 0x02) << 1)
> > +           | (((x) & 0x04) >> 1) | (((x) & 0x08) >> 3)
> > +           | (((x) & 0x10) << 3) | (((x) & 0x20) << 1)
> > +           | (((x) & 0x40) >> 1) | (((x) & 0x80) >> 3);
> > +}
> > +
> > +static void nibble_swap_every_byte(unsigned char *mac_addr)
> > +{
> > +       int i;
> > +       for (i = 0; i < ETH_ALEN; i++) {
> > +               mac_addr[i] = ((mac_addr[i] & 0x0f) << 4) |
> > +                               ((mac_addr[i] & 0xf0) >> 4);
> > +       }
> > +}
> > +
> > +static void inverse_every_nibble(unsigned char *mac_addr)
> > +{
> > +       int i;
> > +       for (i = 0; i < ETH_ALEN; i++)
> > +               mac_addr[i] = flip_8_bits(mac_addr[i]);
> > +}
> > +
> > +/*
> > + * ----------------------------------------------------------------------------
> > + * This function will calculate the hash function of the address.
> > + * Inputs
> > + * mac_addr_orig    - MAC address.
> > + * Outputs
> > + * return the calculated entry.
> > + */
> > +static u32 hash_function(unsigned char *mac_addr_orig)
> > +{
> > +       u32 hash_result;
> > +       u32 addr0;
> > +       u32 addr1;
> > +       u32 addr2;
> > +       u32 addr3;
> > +       unsigned char mac_addr[ETH_ALEN];
> > +
> > +       /* Make a copy of MAC address since we are going to performe bit
> > +        * operations on it
> > +        */
> > +       memcpy(mac_addr, mac_addr_orig, ETH_ALEN);
> > +
> > +       nibble_swap_every_byte(mac_addr);
> > +       inverse_every_nibble(mac_addr);
> > +
> > +       addr0 = (mac_addr[5] >> 2) & 0x3f;
> > +       addr1 = (mac_addr[5] & 0x03) | (((mac_addr[4] & 0x7f)) << 2);
> > +       addr2 = ((mac_addr[4] & 0x80) >> 7) | mac_addr[3] << 1;
> > +       addr3 = (mac_addr[2] & 0xff) | ((mac_addr[1] & 1) << 8);
> > +
> > +       hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
> > +       hash_result = hash_result & 0x07ff;
> > +       return hash_result;
> > +}
> > +
> > +/*
> > + * ----------------------------------------------------------------------------
> > + * This function will add/del an entry to the address table.
> > + * Inputs
> > + * pep - ETHERNET .
> > + * mac_addr - MAC address.
> > + * skip - if 1, skip this address.Used in case of deleting an entry which is a
> > + *       part of chain in the hash table.We cant just delete the entry since
> > + *       that will break the chain.We need to defragment the tables time to
> > + *       time.
> > + * rd   - 0 Discard packet upon match.
> > + *     - 1 Receive packet upon match.
> > + * Outputs
> > + * address table entry is added/deleted.
> > + * 0 if success.
> > + * -ENOSPC if table full
> > + */
> > +static int add_del_hash_entry(struct pxa168_eth_private *pep,
> > +                             unsigned char *mac_addr,
> > +                             u32 rd, u32 skip, int del)
> > +{
> > +       struct addr_table_entry *entry, *start;
> > +       u32 new_high;
> > +       u32 new_low;
> > +       u32 i;
> > +
> > +       new_low = (((mac_addr[1] >> 4) & 0xf) << 15)
> > +           | (((mac_addr[1] >> 0) & 0xf) << 11)
> > +           | (((mac_addr[0] >> 4) & 0xf) << 7)
> > +           | (((mac_addr[0] >> 0) & 0xf) << 3)
> > +           | (((mac_addr[3] >> 4) & 0x1) << 31)
> > +           | (((mac_addr[3] >> 0) & 0xf) << 27)
> > +           | (((mac_addr[2] >> 4) & 0xf) << 23)
> > +           | (((mac_addr[2] >> 0) & 0xf) << 19)
> > +           | (skip << SKIP) | (rd << HASH_ENTRY_RECEIVE_DISCARD_BIT)
> > +           | HASH_ENTRY_VALID;
> > +
> > +       new_high = (((mac_addr[5] >> 4) & 0xf) << 15)
> > +           | (((mac_addr[5] >> 0) & 0xf) << 11)
> > +           | (((mac_addr[4] >> 4) & 0xf) << 7)
> > +           | (((mac_addr[4] >> 0) & 0xf) << 3)
> > +           | (((mac_addr[3] >> 5) & 0x7) << 0);
> > +
> > +       /*
> > +        * Pick the appropriate table, start scanning for free/reusable
> > +        * entries at the index obtained by hashing the specified MAC address
> > +        */
> > +       start = (struct addr_table_entry *)(pep->htpr);
> > +       entry = start + hash_function(mac_addr);
> > +       for (i = 0; i < HOP_NUMBER; i++) {
> > +               if (!(le32_to_cpu(entry->lo) & HASH_ENTRY_VALID)) {
> > +                       break;
> > +               } else {
> > +                       /* if same address put in same position */
> > +                       if (((le32_to_cpu(entry->lo) & 0xfffffff8) ==
> > +                               (new_low & 0xfffffff8)) &&
> > +                               (le32_to_cpu(entry->hi) == new_high)) {
> > +                               break;
> > +                       }
> > +               }
> > +               if (entry == start + 0x7ff)
> > +                       entry = start;
> > +               else
> > +                       entry++;
> > +       }
> > +
> > +       if (((le32_to_cpu(entry->lo) & 0xfffffff8) != (new_low & 0xfffffff8)) &&
> > +           (le32_to_cpu(entry->hi) != new_high) && del)
> > +               return 0;
> > +
> > +       if (i == HOP_NUMBER) {
> > +               if (!del) {
> > +                       printk(KERN_INFO "%s: table section is full, need to "
> > +                                       "move to 16kB implementation?\n",
> > +                                        __FILE__);
> > +                       return -ENOSPC;
> > +               } else
> > +                       return 0;
> > +       }
> > +
> > +       /*
> > +        * Update the selected entry
> > +        */
> > +       if (del) {
> > +               entry->hi = 0;
> > +               entry->lo = 0;
> > +       } else {
> > +               entry->hi = cpu_to_le32(new_high);
> > +               entry->lo = cpu_to_le32(new_low);
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +/*
> > + * ----------------------------------------------------------------------------
> > + *  Create an addressTable entry from MAC address info
> > + *  found in the specifed net_device struct
> > + *
> > + *  Input : pointer to ethernet interface network device structure
> > + *  Output : N/A
> > + */
> > +static void update_hash_table_mac_address(struct pxa168_eth_private *pep,
> > +                                         unsigned char *oaddr,
> > +                                         unsigned char *addr)
> > +{
> > +       /* Delete old entry */
> > +       if (oaddr)
> > +               add_del_hash_entry(pep, oaddr, 1, 0, HASH_DELETE);
> > +       /* Add new entry */
> > +       add_del_hash_entry(pep, addr, 1, 0, HASH_ADD);
> > +}
> > +
> > +static int init_hash_table(struct pxa168_eth_private *pep)
> > +{
> > +       /*
> > +        * Hardware expects CPU to build a hash table based on a predefined
> > +        * hash function and populate it based on hardware address. The
> > +        * location of the hash table is identified by 32-bit pointer stored
> > +        * in HTPR internal register. Two possible sizes exists for the hash
> > +        * table 8kB (256kB of DRAM required (4 x 64 kB banks)) and 1/2kB
> > +        * (16kB of DRAM required (4 x 4 kB banks)).We currently only support
> > +        * 1/2kB.
> > +        */
> > +       /* TODO: Add support for 8kB hash table and alternative hash
> > +        * function.Driver can dynamically switch to them if the 1/2kB hash
> > +        * table is full.
> > +        */
> > +       if (pep->htpr == NULL) {
> > +               pep->htpr = dma_alloc_coherent(pep->dev->dev.parent,
> > +                                             HASH_ADDR_TABLE_SIZE,
> > +                                             &pep->htpr_dma, GFP_KERNEL);
> > +               if (pep->htpr == NULL)
> > +                       return -ENOMEM;
> > +       }
> > +       memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
> > +       wrl(pep, HTPR, pep->htpr_dma);
> > +       return 0;
> > +}
> > +
> > +static void pxa168_eth_set_rx_mode(struct net_device *dev)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       struct netdev_hw_addr *ha;
> > +       u32 val;
> > +
> > +       val = rdl(pep, PORT_CONFIG);
> > +       if (dev->flags & IFF_PROMISC)
> > +               val |= PCR_PM;
> > +       else
> > +               val &= ~PCR_PM;
> > +       wrl(pep, PORT_CONFIG, val);
> > +
> > +       /*
> > +        * Remove the old list of MAC address and add dev->addr
> > +        * and multicast address.
> > +        */
> > +       memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
> > +       update_hash_table_mac_address(pep, NULL, dev->dev_addr);
> > +
> > +       netdev_for_each_mc_addr(ha, dev)
> > +               update_hash_table_mac_address(pep, NULL, ha->addr);
> > +}
> > +
> > +static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr)
> > +{
> > +       struct sockaddr *sa = addr;
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       unsigned char oldMac[ETH_ALEN];
> > +
> > +       if (!is_valid_ether_addr(sa->sa_data))
> > +               return -EINVAL;
> > +       memcpy(oldMac, dev->dev_addr, ETH_ALEN);
> > +       memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
> > +       netif_addr_lock_bh(dev);
> > +       update_hash_table_mac_address(pep, oldMac, dev->dev_addr);
> > +       netif_addr_unlock_bh(dev);
> > +       return 0;
> > +}
> > +
> > +static void eth_port_start(struct net_device *dev)
> > +{
> > +       unsigned int val = 0;
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       int tx_curr_desc, rx_curr_desc;
> > +
> > +       /* Perform PHY reset, if there is a PHY. */
> > +       if (pep->phy != NULL) {
> > +               struct ethtool_cmd cmd;
> > +
> > +               pxa168_get_settings(pep->dev, &cmd);
> > +               ethernet_phy_reset(pep);
> > +               pxa168_set_settings(pep->dev, &cmd);
> > +       }
> > +
> > +       /* Assignment of Tx CTRP of given queue */
> > +       tx_curr_desc = pep->tx_curr_desc_q;
> > +       wrl(pep, ETH_C_TX_DESC_1,
> > +           (u32) ((struct tx_desc *)pep->tx_desc_dma + tx_curr_desc));
> > +
> > +       /* Assignment of Rx CRDP of given queue */
> > +       rx_curr_desc = pep->rx_curr_desc_q;
> > +       wrl(pep, ETH_C_RX_DESC_0,
> > +           (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
> > +
> > +       wrl(pep, ETH_F_RX_DESC_0,
> > +           (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
> > +
> > +       /* Clear all interrupts */
> > +       wrl(pep, INT_CAUSE, 0);
> > +
> > +       /* Enable all interrupts for receive, transmit and error. */
> > +       wrl(pep, INT_MASK, ALL_INTS);
> > +
> > +       val = rdl(pep, PORT_CONFIG);
> > +       val |= PCR_EN;
> > +       wrl(pep, PORT_CONFIG, val);
> > +
> > +       /* Start RX DMA engine */
> > +       val = rdl(pep, SDMA_CMD);
> > +       val |= SDMA_CMD_ERD;
> > +       wrl(pep, SDMA_CMD, val);
> > +}
> > +
> > +static void eth_port_reset(struct net_device *dev)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       unsigned int val = 0;
> > +
> > +       /* Stop all interrupts for receive, transmit and error. */
> > +       wrl(pep, INT_MASK, 0);
> > +
> > +       /* Clear all interrupts */
> > +       wrl(pep, INT_CAUSE, 0);
> > +
> > +       /* Stop RX DMA */
> > +       val = rdl(pep, SDMA_CMD);
> > +       val &= ~SDMA_CMD_ERD;   /* abort dma command */
> > +
> > +       /* Abort any transmit and receive operations and put DMA
> > +        * in idle state.
> > +        */
> > +       abort_dma(pep);
> > +
> > +       /* Disable port */
> > +       val = rdl(pep, PORT_CONFIG);
> > +       val &= ~PCR_EN;
> > +       wrl(pep, PORT_CONFIG, val);
> > +}
> > +
> > +/*
> > + * txq_reclaim - Free the tx desc data for completed descriptors
> > + * If force is non-zero, frees uncompleted descriptors as well
> > + */
> > +static int txq_reclaim(struct net_device *dev, int force)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       struct tx_desc *desc;
> > +       u32 cmd_sts;
> > +       struct sk_buff *skb;
> > +       int tx_index;
> > +       dma_addr_t addr;
> > +       int count;
> > +       int released = 0;
> > +
> > +       netif_tx_lock(dev);
> > +
> > +       pep->work_todo &= ~WORK_TX_DONE;
> > +       while (pep->tx_desc_count > 0) {
> > +               tx_index = pep->tx_used_desc_q;
> > +               desc = &pep->p_tx_desc_area[tx_index];
> > +               cmd_sts = desc->cmd_sts;
> > +               if (!force && (cmd_sts & BUF_OWNED_BY_DMA)) {
> > +                       if (released > 0) {
> > +                               goto txq_reclaim_end;
> > +                       } else {
> > +                               released = -1;
> > +                               goto txq_reclaim_end;
> > +                       }
> > +               }
> > +               pep->tx_used_desc_q = (tx_index + 1) % pep->tx_ring_size;
> > +               pep->tx_desc_count--;
> > +               addr = desc->buf_ptr;
> > +               count = desc->byte_cnt;
> > +               skb = pep->tx_skb[tx_index];
> > +               if (skb)
> > +                       pep->tx_skb[tx_index] = NULL;
> > +
> > +               if (cmd_sts & TX_ERROR) {
> > +                       if (net_ratelimit())
> > +                               printk(KERN_ERR "%s: Error in TX\n", dev->name);
> > +                       dev->stats.tx_errors++;
> > +               }
> > +               dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
> > +               if (skb)
> > +                       dev_kfree_skb_irq(skb);
> > +               released++;
> > +       }
> > +txq_reclaim_end:
> > +       netif_tx_unlock(dev);
> > +       return released;
> > +}
> > +
> > +static void pxa168_eth_tx_timeout(struct net_device *dev)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +
> > +       printk(KERN_INFO "%s: TX timeout  desc_count %d\n",
> > +              dev->name, pep->tx_desc_count);
> > +
> > +       schedule_work(&pep->tx_timeout_task);
> > +}
> > +
> > +static void pxa168_eth_tx_timeout_task(struct work_struct *work)
> > +{
> > +       struct pxa168_eth_private *pep = container_of(work,
> > +                                                struct pxa168_eth_private,
> > +                                                tx_timeout_task);
> > +       struct net_device *dev = pep->dev;
> > +       pxa168_eth_stop(dev);
> > +       pxa168_eth_open(dev);
> > +}
> > +
> > +static int rxq_process(struct net_device *dev, int budget)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       struct net_device_stats *stats = &dev->stats;
> > +       unsigned int received_packets = 0;
> > +       struct sk_buff *skb;
> > +
> > +       while (budget-- > 0) {
> > +               int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
> > +               struct rx_desc *rx_desc;
> > +               unsigned int cmd_sts;
> > +
> > +               /* Do not process Rx ring in case of Rx ring resource error */
> > +               if (pep->rx_resource_err)
> > +                       break;
> > +               rx_curr_desc = pep->rx_curr_desc_q;
> > +               rx_used_desc = pep->rx_used_desc_q;
> > +               rx_desc = &pep->p_rx_desc_area[rx_curr_desc];
> > +               cmd_sts = rx_desc->cmd_sts;
> > +               rmb();
> > +               if (cmd_sts & (BUF_OWNED_BY_DMA))
> > +                       break;
> > +               skb = pep->rx_skb[rx_curr_desc];
> > +               pep->rx_skb[rx_curr_desc] = NULL;
> > +
> > +               rx_next_curr_desc = (rx_curr_desc + 1) % pep->rx_ring_size;
> > +               pep->rx_curr_desc_q = rx_next_curr_desc;
> > +
> > +               /* Rx descriptors exhausted. */
> > +               /* Set the Rx ring resource error flag */
> > +               if (rx_next_curr_desc == rx_used_desc)
> > +                       pep->rx_resource_err = 1;
> > +               pep->rx_desc_count--;
> > +               dma_unmap_single(NULL, rx_desc->buf_ptr,
> > +                                rx_desc->buf_size,
> > +                                DMA_FROM_DEVICE);
> > +               received_packets++;
> > +               /*
> > +                * Update statistics.
> > +                * Note byte count includes 4 byte CRC count
> > +                */
> > +               stats->rx_packets++;
> > +               stats->rx_bytes += rx_desc->byte_cnt;
> > +               /*
> > +                * In case received a packet without first / last bits on OR
> > +                * the error summary bit is on, the packets needs to be droped.
> > +                */
> > +               if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
> > +                    (RX_FIRST_DESC | RX_LAST_DESC))
> > +                   || (cmd_sts & RX_ERROR)) {
> > +
> > +                       stats->rx_dropped++;
> > +                       if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
> > +                           (RX_FIRST_DESC | RX_LAST_DESC)) {
> > +                               if (net_ratelimit())
> > +                                       printk(KERN_ERR
> > +                                              "%s: Rx pkt on multiple desc\n",
> > +                                              dev->name);
> > +                       }
> > +                       if (cmd_sts & RX_ERROR)
> > +                               stats->rx_errors++;
> > +                       dev_kfree_skb_irq(skb);
> > +               } else {
> > +                       /*
> > +                        * The -4 is for the CRC in the trailer of the
> > +                        * received packet
> > +                        */
> > +                       skb_put(skb, rx_desc->byte_cnt - 4);
> > +                       skb->protocol = eth_type_trans(skb, dev);
> > +                       netif_receive_skb(skb);
> > +               }
> > +               dev->last_rx = jiffies;
> > +       }
> > +       /* Fill RX ring with skb's */
> > +       rxq_refill(dev);
> > +       return received_packets;
> > +}
> > +
> > +static int pxa168_eth_collect_events(struct pxa168_eth_private *pep,
> > +                                    struct net_device *dev)
> > +{
> > +       u32 icr;
> > +       int ret = 0;
> > +
> > +       icr = rdl(pep, INT_CAUSE);
> > +       if (icr == 0)
> > +               return IRQ_NONE;
> > +
> > +       wrl(pep, INT_CAUSE, ~icr);
> > +       if (icr & (ICR_TXBUF_H | ICR_TXBUF_L)) {
> > +               pep->work_todo |= WORK_TX_DONE;
> > +               ret = 1;
> > +       }
> > +       if (icr & ICR_RXBUF)
> > +               ret = 1;
> > +       if (icr & ICR_MII_CH) {
> > +               pep->work_todo |= WORK_LINK;
> > +               ret = 1;
> > +       }
> > +       return ret;
> > +}
> > +
> > +static void handle_link_event(struct pxa168_eth_private *pep)
> > +{
> > +       struct net_device *dev = pep->dev;
> > +       u32 port_status;
> > +       int speed;
> > +       int duplex;
> > +       int fc;
> > +
> > +       port_status = rdl(pep, PORT_STATUS);
> > +       if (!(port_status & LINK_UP)) {
> > +               if (netif_carrier_ok(dev)) {
> > +                       printk(KERN_INFO "%s: link down\n", dev->name);
> > +                       netif_carrier_off(dev);
> > +                       txq_reclaim(dev, 1);
> > +               }
> > +               return;
> > +       }
> > +       if (port_status & PORT_SPEED_100)
> > +               speed = 100;
> > +       else
> > +               speed = 10;
> > +
> > +       duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
> > +       fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
> > +       printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
> > +              "flow control %sabled\n", dev->name,
> > +              speed, duplex ? "full" : "half", fc ? "en" : "dis");
> > +       if (!netif_carrier_ok(dev))
> > +               netif_carrier_on(dev);
> > +}
> > +
> > +static irqreturn_t pxa168_eth_int_handler(int irq, void *dev_id)
> > +{
> > +       struct net_device *dev = (struct net_device *)dev_id;
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +
> > +       if (unlikely(!pxa168_eth_collect_events(pep, dev)))
> > +               return IRQ_NONE;
> > +       /* Disable interrupts */
> > +       wrl(pep, INT_MASK, 0);
> > +       napi_schedule(&pep->napi);
> > +       return IRQ_HANDLED;
> > +}
> > +
> > +static void pxa168_eth_recalc_skb_size(struct pxa168_eth_private *pep)
> > +{
> > +       int skb_size;
> > +
> > +       /*
> > +        * Reserve 2+14 bytes for an ethernet header (the hardware
> > +        * automatically prepends 2 bytes of dummy data to each
> > +        * received packet), 16 bytes for up to four VLAN tags, and
> > +        * 4 bytes for the trailing FCS -- 36 bytes total.
> > +        */
> > +       skb_size = pep->dev->mtu + 36;
> > +
> > +       /*
> > +        * Make sure that the skb size is a multiple of 8 bytes, as
> > +        * the lower three bits of the receive descriptor's buffer
> > +        * size field are ignored by the hardware.
> > +        */
> > +       pep->skb_size = (skb_size + 7) & ~7;
> > +
> > +       /*
> > +        * If NET_SKB_PAD is smaller than a cache line,
> > +        * netdev_alloc_skb() will cause skb->data to be misaligned
> > +        * to a cache line boundary.  If this is the case, include
> > +        * some extra space to allow re-aligning the data area.
> > +        */
> > +       pep->skb_size += SKB_DMA_REALIGN;
> > +
> > +}
> > +
> > +static int set_port_config_ext(struct pxa168_eth_private *pep)
> > +{
> > +       int skb_size;
> > +
> > +       pxa168_eth_recalc_skb_size(pep);
> > +       if  (pep->skb_size <= 1518)
> > +               skb_size = PCXR_MFL_1518;
> > +       else if (pep->skb_size <= 1536)
> > +               skb_size = PCXR_MFL_1536;
> > +       else if (pep->skb_size <= 2048)
> > +               skb_size = PCXR_MFL_2048;
> > +       else
> > +               skb_size = PCXR_MFL_64K;
> > +
> > +       /* Extended Port Configuration */
> > +       wrl(pep,
> > +           PORT_CONFIG_EXT, PCXR_2BSM | /* Two byte prefix aligns IP hdr */
> > +           PCXR_DSCP_EN |               /* Enable DSCP in IP */
> > +           skb_size | PCXR_FLP |        /* do not force link pass */
> > +           PCXR_TX_HIGH_PRI);           /* Transmit - high priority queue */
> > +
> > +       return 0;
> > +}
> > +
> > +static int pxa168_init_hw(struct pxa168_eth_private *pep)
> > +{
> > +       int err = 0;
> > +
> > +       /* Disable interrupts */
> > +       wrl(pep, INT_MASK, 0);
> > +       wrl(pep, INT_CAUSE, 0);
> > +       /* Write to ICR to clear interrupts. */
> > +       wrl(pep, INT_W_CLEAR, 0);
> > +       /* Abort any transmit and receive operations and put DMA
> > +        * in idle state.
> > +        */
> > +       abort_dma(pep);
> > +       /* Initialize address hash table */
> > +       err = init_hash_table(pep);
> > +       if (err)
> > +               return err;
> > +       /* SDMA configuration */
> > +       wrl(pep, SDMA_CONFIG, SDCR_BSZ8 |       /* Burst size = 32 bytes */
> > +           SDCR_RIFB |                         /* Rx interrupt on frame */
> > +           SDCR_BLMT |                         /* Little endian transmit */
> > +           SDCR_BLMR |                         /* Little endian receive */
> > +           SDCR_RC_MAX_RETRANS);               /* Max retransmit count */
> > +       /* Port Configuration */
> > +       wrl(pep, PORT_CONFIG, PCR_HS);          /* Hash size is 1/2kb */
> > +       set_port_config_ext(pep);
> > +
> > +       return err;
> > +}
> > +
> > +static int rxq_init(struct net_device *dev)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       struct rx_desc *p_rx_desc;
> > +       int size = 0, i = 0;
> > +       int rx_desc_num = pep->rx_ring_size;
> > +
> > +       /* Allocate RX skb rings */
> > +       pep->rx_skb = kmalloc(sizeof(*pep->rx_skb) * pep->rx_ring_size,
> > +                            GFP_KERNEL);
> > +       if (!pep->rx_skb) {
> > +               printk(KERN_ERR "%s: Cannot alloc RX skb ring\n", dev->name);
> > +               return -ENOMEM;
> > +       }
> > +       /* Allocate RX ring */
> > +       pep->rx_desc_count = 0;
> > +       size = pep->rx_ring_size * sizeof(struct rx_desc);
> > +       pep->rx_desc_area_size = size;
> > +       pep->p_rx_desc_area = dma_alloc_coherent(pep->dev->dev.parent, size,
> > +                                               &pep->rx_desc_dma, GFP_KERNEL);
> > +       if (!pep->p_rx_desc_area) {
> > +               printk(KERN_ERR "%s: Cannot alloc RX ring (size %d bytes)\n",
> > +                      dev->name, size);
> > +               goto out;
> > +       }
> > +       memset((void *)pep->p_rx_desc_area, 0, size);
> > +       /* initialize the next_desc_ptr links in the Rx descriptors ring */
> > +       p_rx_desc = (struct rx_desc *)pep->p_rx_desc_area;
> > +       for (i = 0; i < rx_desc_num; i++) {
> > +               p_rx_desc[i].next_desc_ptr = pep->rx_desc_dma +
> > +                   ((i + 1) % rx_desc_num) * sizeof(struct rx_desc);
> > +       }
> > +       /* Save Rx desc pointer to driver struct. */
> > +       pep->rx_curr_desc_q = 0;
> > +       pep->rx_used_desc_q = 0;
> > +       pep->rx_desc_area_size = rx_desc_num * sizeof(struct rx_desc);
> > +       return 0;
> > +out:
> > +       kfree(pep->rx_skb);
> > +       return -ENOMEM;
> > +}
> > +
> > +static void rxq_deinit(struct net_device *dev)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       int curr;
> > +
> > +       /* Free preallocated skb's on RX rings */
> > +       for (curr = 0; pep->rx_desc_count && curr < pep->rx_ring_size; curr++) {
> > +               if (pep->rx_skb[curr]) {
> > +                       dev_kfree_skb(pep->rx_skb[curr]);
> > +                       pep->rx_desc_count--;
> > +               }
> > +       }
> > +       if (pep->rx_desc_count)
> > +               printk(KERN_ERR
> > +                      "Error in freeing Rx Ring. %d skb's still\n",
> > +                      pep->rx_desc_count);
> > +       /* Free RX ring */
> > +       if (pep->p_rx_desc_area)
> > +               dma_free_coherent(pep->dev->dev.parent, pep->rx_desc_area_size,
> > +                                 pep->p_rx_desc_area, pep->rx_desc_dma);
> > +       kfree(pep->rx_skb);
> > +}
> > +
> > +static int txq_init(struct net_device *dev)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       struct tx_desc *p_tx_desc;
> > +       int size = 0, i = 0;
> > +       int tx_desc_num = pep->tx_ring_size;
> > +
> > +       pep->tx_skb = kmalloc(sizeof(*pep->tx_skb) * pep->tx_ring_size,
> > +                            GFP_KERNEL);
> > +       if (!pep->tx_skb) {
> > +               printk(KERN_ERR "%s: Cannot alloc TX skb ring\n", dev->name);
> > +               return -ENOMEM;
> > +       }
> > +       /* Allocate TX ring */
> > +       pep->tx_desc_count = 0;
> > +       size = pep->tx_ring_size * sizeof(struct tx_desc);
> > +       pep->tx_desc_area_size = size;
> > +       pep->p_tx_desc_area = dma_alloc_coherent(pep->dev->dev.parent, size,
> > +                                               &pep->tx_desc_dma, GFP_KERNEL);
> > +       if (!pep->p_tx_desc_area) {
> > +               printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d bytes)\n",
> > +                      dev->name, size);
> > +               goto out;
> > +       }
> > +       memset((void *)pep->p_tx_desc_area, 0, pep->tx_desc_area_size);
> > +       /* Initialize the next_desc_ptr links in the Tx descriptors ring */
> > +       p_tx_desc = (struct tx_desc *)pep->p_tx_desc_area;
> > +       for (i = 0; i < tx_desc_num; i++) {
> > +               p_tx_desc[i].next_desc_ptr = pep->tx_desc_dma +
> > +                   ((i + 1) % tx_desc_num) * sizeof(struct tx_desc);
> > +       }
> > +       pep->tx_curr_desc_q = 0;
> > +       pep->tx_used_desc_q = 0;
> > +       pep->tx_desc_area_size = tx_desc_num * sizeof(struct tx_desc);
> > +       return 0;
> > +out:
> > +       kfree(pep->tx_skb);
> > +       return -ENOMEM;
> > +}
> > +
> > +static void txq_deinit(struct net_device *dev)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +
> > +       /* Free outstanding skb's on TX ring */
> > +       txq_reclaim(dev, 1);
> > +       BUG_ON(pep->tx_used_desc_q != pep->tx_curr_desc_q);
> > +       /* Free TX ring */
> > +       if (pep->p_tx_desc_area)
> > +               dma_free_coherent(pep->dev->dev.parent, pep->tx_desc_area_size,
> > +                                 pep->p_tx_desc_area, pep->tx_desc_dma);
> > +       kfree(pep->tx_skb);
> > +}
> > +
> > +static int pxa168_eth_open(struct net_device *dev)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       int err;
> > +
> > +       err = request_irq(dev->irq, pxa168_eth_int_handler,
> > +                         IRQF_DISABLED, dev->name, dev);
> > +       if (err) {
> > +               dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n");
> > +               return -EAGAIN;
> > +       }
> > +       pep->rx_resource_err = 0;
> > +       err = rxq_init(dev);
> > +       if (err != 0)
> > +               goto out_free_irq;
> > +       err = txq_init(dev);
> > +       if (err != 0)
> > +               goto out_free_rx_skb;
> > +       pep->rx_used_desc_q = 0;
> > +       pep->rx_curr_desc_q = 0;
> > +
> > +       /* Fill RX ring with skb's */
> > +       rxq_refill(dev);
> > +       pep->rx_used_desc_q = 0;
> > +       pep->rx_curr_desc_q = 0;
> > +       netif_carrier_off(dev);
> > +       eth_port_start(dev);
> > +       napi_enable(&pep->napi);
> > +       return 0;
> > +out_free_rx_skb:
> > +       rxq_deinit(dev);
> > +out_free_irq:
> > +       free_irq(dev->irq, dev);
> > +       return err;
> > +}
> > +
> > +static int pxa168_eth_stop(struct net_device *dev)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       eth_port_reset(dev);
> > +
> > +       /* Disable interrupts */
> > +       wrl(pep, INT_MASK, 0);
> > +       wrl(pep, INT_CAUSE, 0);
> > +       /* Write to ICR to clear interrupts. */
> > +       wrl(pep, INT_W_CLEAR, 0);
> > +       napi_disable(&pep->napi);
> > +       del_timer_sync(&pep->timeout);
> > +       netif_carrier_off(dev);
> > +       free_irq(dev->irq, dev);
> > +       rxq_deinit(dev);
> > +       txq_deinit(dev);
> > +
> > +       return 0;
> > +}
> > +
> > +static int pxa168_eth_change_mtu(struct net_device *dev, int mtu)
> > +{
> > +       int retval;
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +
> > +       if ((mtu > 9500) || (mtu < 68))
> > +               return -EINVAL;
> > +
> > +       dev->mtu = mtu;
> > +       retval = set_port_config_ext(pep);
> > +
> > +       if (!netif_running(dev))
> > +               return 0;
> > +
> > +       /*
> > +        * Stop and then re-open the interface. This will allocate RX
> > +        * skbs of the new MTU.
> > +        * There is a possible danger that the open will not succeed,
> > +        * due to memory being full.
> > +        */
> > +       pxa168_eth_stop(dev);
> > +       if (pxa168_eth_open(dev)) {
> > +               dev_printk(KERN_ERR, &dev->dev,
> > +                          "fatal error on re-opening device after "
> > +                          "MTU change\n");
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int eth_alloc_tx_desc_index(struct pxa168_eth_private *pep)
> > +{
> > +       int tx_desc_curr;
> > +
> > +       tx_desc_curr = pep->tx_curr_desc_q;
> > +       pep->tx_curr_desc_q = (tx_desc_curr + 1) % pep->tx_ring_size;
> > +       BUG_ON(pep->tx_curr_desc_q == pep->tx_used_desc_q);
> > +       pep->tx_desc_count++;
> > +
> > +       return tx_desc_curr;
> > +}
> > +
> > +static int pxa168_rx_poll(struct napi_struct *napi, int budget)
> > +{
> > +       struct pxa168_eth_private *pep =
> > +           container_of(napi, struct pxa168_eth_private, napi);
> > +       struct net_device *dev = pep->dev;
> > +       int work_done = 0;
> > +
> > +       if (unlikely(pep->work_todo & WORK_LINK)) {
> > +               pep->work_todo &= ~(WORK_LINK);
> > +               handle_link_event(pep);
> > +       }
> > +       /*
> > +        * We call txq_reclaim every time since in NAPI interupts are disabled
> > +        * and due to this we miss the TX_DONE interrupt,which is not updated in
> > +        * interrupt status register.
> > +        */
> > +       txq_reclaim(dev, 0);
> > +       if (netif_queue_stopped(dev)
> > +           && pep->tx_ring_size - pep->tx_desc_count > 1) {
> > +               netif_wake_queue(dev);
> > +       }
> > +       work_done = rxq_process(dev, budget);
> > +       if (work_done < budget) {
> > +               napi_complete(napi);
> > +               wrl(pep, INT_MASK, ALL_INTS);
> > +       }
> > +
> > +       return work_done;
> > +}
> > +
> > +static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       struct net_device_stats *stats = &dev->stats;
> > +       struct tx_desc *desc;
> > +       int tx_index;
> > +       int length;
> > +
> > +       tx_index = eth_alloc_tx_desc_index(pep);
> > +       desc = &pep->p_tx_desc_area[tx_index];
> > +       length = skb->len;
> > +       pep->tx_skb[tx_index] = skb;
> > +       desc->byte_cnt = length;
> > +       desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
> > +       wmb();
> > +       desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC |
> > +                       TX_ZERO_PADDING | TX_LAST_DESC | TX_EN_INT;
> > +       wmb();
> > +       wrl(pep, SDMA_CMD, SDMA_CMD_TXDH | SDMA_CMD_ERD);
> > +
> > +       stats->tx_bytes += skb->len;
> > +       stats->tx_packets++;
> > +       dev->trans_start = jiffies;
> > +       if (pep->tx_ring_size - pep->tx_desc_count <= 1) {
> > +               /* We handled the current skb, but now we are out of space.*/
> > +               netif_stop_queue(dev);
> > +       }
> > +
> > +       return NETDEV_TX_OK;
> > +}
> > +
> > +static int smi_wait_ready(struct pxa168_eth_private *pep)
> > +{
> > +       int i = 0;
> > +
> > +       /* wait for the SMI register to become available */
> > +       for (i = 0; rdl(pep, SMI) & SMI_BUSY; i++) {
> > +               if (i == PHY_WAIT_ITERATIONS)
> > +                       return -ETIMEDOUT;
> > +               msleep(10);
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int pxa168_smi_read(struct mii_bus *bus, int phy_addr, int regnum)
> > +{
> > +       struct pxa168_eth_private *pep = bus->priv;
> > +       int i = 0;
> > +       int val;
> > +
> > +       if (smi_wait_ready(pep)) {
> > +               printk(KERN_WARNING "pxa168_eth: SMI bus busy timeout\n");
> > +               return -ETIMEDOUT;
> > +       }
> > +       wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) | SMI_OP_R);
> > +       /* now wait for the data to be valid */
> > +       for (i = 0; !((val = rdl(pep, SMI)) & SMI_R_VALID); i++) {
> > +               if (i == PHY_WAIT_ITERATIONS) {
> > +                       printk(KERN_WARNING
> > +                               "pxa168_eth: SMI bus read not valid\n");
> > +                       return -ENODEV;
> > +               }
> > +               msleep(10);
> > +       }
> > +
> > +       return val & 0xffff;
> > +}
> > +
> > +static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
> > +                           u16 value)
> > +{
> > +       struct pxa168_eth_private *pep = bus->priv;
> > +
> > +       if (smi_wait_ready(pep)) {
> > +               printk(KERN_WARNING "pxa168_eth: SMI bus busy timeout\n");
> > +               return -ETIMEDOUT;
> > +       }
> > +
> > +       wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) |
> > +           SMI_OP_W | (value & 0xffff));
> > +
> > +       if (smi_wait_ready(pep)) {
> > +               printk(KERN_ERR "pxa168_eth: SMI bus busy timeout\n");
> > +               return -ETIMEDOUT;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr,
> > +                              int cmd)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       if (pep->phy != NULL)
> > +               return phy_mii_ioctl(pep->phy, if_mii(ifr), cmd);
> > +
> > +       return -EOPNOTSUPP;
> > +}
> > +
> > +static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int phy_addr)
> > +{
> > +       struct mii_bus *bus = pep->smi_bus;
> > +       struct phy_device *phydev;
> > +       int start;
> > +       int num;
> > +       int i;
> > +
> > +       if (phy_addr == PXA168_ETH_PHY_ADDR_DEFAULT) {
> > +               /* Scan entire range */
> > +               start = ethernet_phy_get(pep);
> > +               num = 32;
> > +       } else {
> > +               /* Use phy addr specific to platform */
> > +               start = phy_addr & 0x1f;
> > +               num = 1;
> > +       }
> > +       phydev = NULL;
> > +       for (i = 0; i < num; i++) {
> > +               int addr = (start + i) & 0x1f;
> > +               if (bus->phy_map[addr] == NULL)
> > +                       mdiobus_scan(bus, addr);
> > +
> > +               if (phydev == NULL) {
> > +                       phydev = bus->phy_map[addr];
> > +                       if (phydev != NULL)
> > +                               ethernet_phy_set_addr(pep, addr);
> > +               }
> > +       }
> > +
> > +       return phydev;
> > +}
> > +
> > +static void phy_init(struct pxa168_eth_private *pep, int speed, int duplex)
> > +{
> > +       struct phy_device *phy = pep->phy;
> > +       ethernet_phy_reset(pep);
> > +
> > +       phy_attach(pep->dev, dev_name(&phy->dev), 0, PHY_INTERFACE_MODE_MII);
> > +
> > +       if (speed == 0) {
> > +               phy->autoneg = AUTONEG_ENABLE;
> > +               phy->speed = 0;
> > +               phy->duplex = 0;
> > +               phy->supported &= PHY_BASIC_FEATURES;
> > +               phy->advertising = phy->supported | ADVERTISED_Autoneg;
> > +       } else {
> > +               phy->autoneg = AUTONEG_DISABLE;
> > +               phy->advertising = 0;
> > +               phy->speed = speed;
> > +               phy->duplex = duplex;
> > +       }
> > +       phy_start_aneg(phy);
> > +}
> > +
> > +static int ethernet_phy_setup(struct net_device *dev)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +
> > +       if (pep->pd != NULL) {
> > +               if (pep->pd->init)
> > +                       pep->pd->init();
> > +       }
> > +       pep->phy = phy_scan(pep, pep->pd->phy_addr & 0x1f);
> > +       if (pep->phy != NULL)
> > +               phy_init(pep, pep->pd->speed, pep->pd->duplex);
> > +       update_hash_table_mac_address(pep, NULL, dev->dev_addr);
> > +
> > +       return 0;
> > +}
> > +
> > +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +       int err;
> > +
> > +       err = phy_read_status(pep->phy);
> > +       if (err == 0)
> > +               err = phy_ethtool_gset(pep->phy, cmd);
> > +
> > +       return err;
> > +}
> > +
> > +static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> > +{
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +
> > +       return phy_ethtool_sset(pep->phy, cmd);
> > +}
> > +
> > +static void pxa168_get_drvinfo(struct net_device *dev,
> > +                              struct ethtool_drvinfo *info)
> > +{
> > +       strncpy(info->driver, DRIVER_NAME, 32);
> > +       strncpy(info->version, DRIVER_VERSION, 32);
> > +       strncpy(info->fw_version, "N/A", 32);
> > +       strncpy(info->bus_info, "N/A", 32);
> > +}
> > +
> > +static u32 pxa168_get_link(struct net_device *dev)
> > +{
> > +       return !!netif_carrier_ok(dev);
> > +}
> > +
> > +static const struct ethtool_ops pxa168_ethtool_ops = {
> > +       .get_settings = pxa168_get_settings,
> > +       .set_settings = pxa168_set_settings,
> > +       .get_drvinfo = pxa168_get_drvinfo,
> > +       .get_link = pxa168_get_link,
> > +};
> > +
> > +static const struct net_device_ops pxa168_eth_netdev_ops = {
> > +       .ndo_open = pxa168_eth_open,
> > +       .ndo_stop = pxa168_eth_stop,
> > +       .ndo_start_xmit = pxa168_eth_start_xmit,
> > +       .ndo_set_rx_mode = pxa168_eth_set_rx_mode,
> > +       .ndo_set_mac_address = pxa168_eth_set_mac_address,
> > +       .ndo_validate_addr = eth_validate_addr,
> > +       .ndo_do_ioctl = pxa168_eth_do_ioctl,
> > +       .ndo_change_mtu = pxa168_eth_change_mtu,
> > +       .ndo_tx_timeout = pxa168_eth_tx_timeout,
> > +};
> > +
> > +static int pxa168_eth_probe(struct platform_device *pdev)
> > +{
> > +       struct pxa168_eth_private *pep = NULL;
> > +       struct net_device *dev = NULL;
> > +       struct resource *res;
> > +       struct clk *clk;
> > +       int err;
> > +
> > +       printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n");
> > +
> > +       clk = clk_get(&pdev->dev, "MFUCLK");
> > +       if (IS_ERR(clk)) {
> > +               printk(KERN_ERR "%s: Fast Ethernet failed to get clock\n",
> > +                       DRIVER_NAME);
> > +               return -ENODEV;
> > +       }
> > +       clk_enable(clk);
> > +
> > +       dev = alloc_etherdev(sizeof(struct pxa168_eth_private));
> > +       if (!dev) {
> > +               err = -ENOMEM;
> > +               goto out;
> > +       }
> > +
> > +       platform_set_drvdata(pdev, dev);
> > +       pep = netdev_priv(dev);
> > +       pep->dev = dev;
> > +       pep->clk = clk;
> > +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       if (res == NULL) {
> > +               err = -ENODEV;
> > +               goto out;
> > +       }
> > +       pep->base = ioremap(res->start, res->end - res->start + 1);
> > +       if (pep->base == NULL) {
> > +               err = -ENOMEM;
> > +               goto out;
> > +       }
> > +       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> > +       BUG_ON(!res);
> > +       dev->irq = res->start;
> > +       dev->netdev_ops = &pxa168_eth_netdev_ops;
> > +       dev->watchdog_timeo = 2 * HZ;
> > +       dev->base_addr = 0;
> > +       SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops);
> > +
> > +       INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
> > +
> > +       printk(KERN_INFO "%s:Using random mac address\n", DRIVER_NAME);
> > +       random_ether_addr(dev->dev_addr);
> > +
> > +       pep->pd = pdev->dev.platform_data;
> > +       pep->rx_ring_size = NUM_RX_DESCS;
> > +       if (pep->pd->rx_queue_size)
> > +               pep->rx_ring_size = pep->pd->rx_queue_size;
> > +
> > +       pep->tx_ring_size = NUM_TX_DESCS;
> > +       if (pep->pd->tx_queue_size)
> > +               pep->tx_ring_size = pep->pd->tx_queue_size;
> > +
> > +       pep->port_num = pep->pd->port_number;
> > +       /* Hardware supports only 3 ports */
> > +       BUG_ON(pep->port_num > 2);
> > +       netif_napi_add(dev, &pep->napi, pxa168_rx_poll, pep->rx_ring_size);
> > +
> > +       memset(&pep->timeout, 0, sizeof(struct timer_list));
> > +       init_timer(&pep->timeout);
> > +       pep->timeout.function = rxq_refill_timer_wrapper;
> > +       pep->timeout.data = (unsigned long)pep;
> > +
> > +       pep->smi_bus = mdiobus_alloc();
> > +       if (pep->smi_bus == NULL) {
> > +               err = -ENOMEM;
> > +               goto out;
> > +       }
> > +       pep->smi_bus->priv = pep;
> > +       pep->smi_bus->name = "pxa168_eth smi";
> > +       pep->smi_bus->read = pxa168_smi_read;
> > +       pep->smi_bus->write = pxa168_smi_write;
> > +       snprintf(pep->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id);
> > +       pep->smi_bus->parent = &pdev->dev;
> > +       pep->smi_bus->phy_mask = 0xffffffff;
> > +       if (mdiobus_register(pep->smi_bus) < 0) {
> > +               err = -ENOMEM;
> > +               goto out;
> > +       }
> > +       pxa168_init_hw(pep);
> > +       err = ethernet_phy_setup(dev);
> > +       if (err)
> > +               goto out;
> > +       SET_NETDEV_DEV(dev, &pdev->dev);
> > +       err = register_netdev(dev);
> > +       if (err)
> > +               goto out;
> > +       return 0;
> > +out:
> > +       if (pep->clk) {
> > +               clk_disable(pep->clk);
> > +               clk_put(pep->clk);
> > +               pep->clk = NULL;
> > +       }
> > +       if (pep->base) {
> > +               iounmap(pep->base);
> > +               pep->base = NULL;
> > +       }
> > +       if (dev)
> > +               free_netdev(dev);
> > +       return err;
> > +}
> > +
> > +static int pxa168_eth_remove(struct platform_device *pdev)
> > +{
> > +       struct net_device *dev = platform_get_drvdata(pdev);
> > +       struct pxa168_eth_private *pep = netdev_priv(dev);
> > +
> > +       if (pep->htpr) {
> > +               dma_free_coherent(pep->dev->dev.parent, HASH_ADDR_TABLE_SIZE,
> > +                                 pep->htpr, pep->htpr_dma);
> > +               pep->htpr = NULL;
> > +       }
> > +       if (pep->clk) {
> > +               clk_disable(pep->clk);
> > +               clk_put(pep->clk);
> > +               pep->clk = NULL;
> > +       }
> > +       if (pep->phy != NULL)
> > +               phy_detach(pep->phy);
> > +
> > +       iounmap(pep->base);
> > +       pep->base = NULL;
> > +       unregister_netdev(dev);
> > +       flush_scheduled_work();
> > +       free_netdev(dev);
> > +       platform_set_drvdata(pdev, NULL);
> > +       return 0;
> > +}
> > +
> > +static void pxa168_eth_shutdown(struct platform_device *pdev)
> > +{
> > +       struct net_device *dev = platform_get_drvdata(pdev);
> > +       eth_port_reset(dev);
> > +}
> > +
> > +#ifdef CONFIG_PM
> > +static int pxa168_eth_resume(struct platform_device *pdev)
> > +{
> > +       return -ENOSYS;
> > +}
> > +
> > +static int pxa168_eth_suspend(struct platform_device *pdev, pm_message_t state)
> > +{
> > +       return -ENOSYS;
> > +}
> > +
> > +#else
> > +#define pxa168_eth_resume NULL
> > +#define pxa168_eth_suspend NULL
> > +#endif
> > +
> > +static struct platform_driver pxa168_eth_driver = {
> > +       .probe = pxa168_eth_probe,
> > +       .remove = pxa168_eth_remove,
> > +       .shutdown = pxa168_eth_shutdown,
> > +       .resume = pxa168_eth_resume,
> > +       .suspend = pxa168_eth_suspend,
> > +       .driver = {
> > +                  .name = DRIVER_NAME,
> > +                  },
> > +};
> > +
> > +static int __init pxa168_init_module(void)
> > +{
> > +       return platform_driver_register(&pxa168_eth_driver);
> > +}
> > +
> > +static void __exit pxa168_cleanup_module(void)
> > +{
> > +       platform_driver_unregister(&pxa168_eth_driver);
> > +}
> > +
> > +module_init(pxa168_init_module);
> > +module_exit(pxa168_cleanup_module);
> > +
> > +MODULE_LICENSE("GPL");
> > +MODULE_DESCRIPTION("Ethernet driver for Marvell PXA168");
> > +MODULE_ALIAS("platform:pxa168_eth");
> > diff --git a/include/linux/pxa168_eth.h b/include/linux/pxa168_eth.h
> > new file mode 100644
> > index 0000000..18d75e7
> > --- /dev/null
> > +++ b/include/linux/pxa168_eth.h
> > @@ -0,0 +1,30 @@
> > +/*
> > + *pxa168 ethernet platform device data definition file.
> > + */
> > +#ifndef __LINUX_PXA168_ETH_H
> > +#define __LINUX_PXA168_ETH_H
> > +
> > +struct pxa168_eth_platform_data {
> > +       int     port_number;
> > +       int     phy_addr;
> > +
> > +       /*
> > +        * If speed is 0, then speed and duplex are autonegotiated.
> > +        */
> > +       int     speed;          /* 0, SPEED_10, SPEED_100 */
> > +       int     duplex;         /* DUPLEX_HALF or DUPLEX_FULL */
> > +
> > +       /*
> > +        * Override default RX/TX queue sizes if nonzero.
> > +        */
> > +       int     rx_queue_size;
> > +       int     tx_queue_size;
> > +
> > +       /*
> > +        * init callback is used for board specific initialization
> > +        * e.g on Aspenite its used to initialize the PHY transceiver.
> > +        */
> > +       int (*init)(void);
> > +};
> > +
> > +#endif /* __LINUX_PXA168_ETH_H */
> > --
> > 1.5.3.3
> >
> >
> >
> 



^ permalink raw reply

* Re: [GIT] Networking
From: Jarek Poplawski @ 2010-08-16  9:53 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David Miller, torvalds, akpm, netdev, linux-kernel,
	Patrick McHardy
In-Reply-To: <1281883637.2942.42.camel@edumazet-laptop>

Eric Dumazet wrote:
> Le dimanche 15 aou^t 2010 a` 12:55 +0200, Eric Dumazet a écrit :
...
> [PATCH] netfilter: {ip,ip6,arp}_tables: avoid lockdep false positive
> 
> After commit 24b36f019 (netfilter: {ip,ip6,arp}_tables: dont block
> bottom half more than necessary), lockdep can raise a warning
> because we attempt to lock a spinlock with BH enabled, while
> the same lock is usually locked by another cpu in a softirq context.

Btw, could you remind us how get_counters() are serialized (I guess
you can't have them on 2 cpus at the same time)?

Thanks,
Jarek P.

^ permalink raw reply

* Re: GTSM and TCP accept problem
From: Eric Dumazet @ 2010-08-16  9:43 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: Herbert Xu, netdev
In-Reply-To: <20100814214634.24a87715@s6510>

Le samedi 14 août 2010 à 21:46 -0400, Stephen Hemminger a écrit :
> The implementation of Generalized TTL Security has a problem
> on the TCP accept side. Since there can be different number of
> hops for each peer, the listener ends up doing
> 
>    socket
>    bind
>    set TTL to 255
>    listen
>    while (nfd = accept(peer)) {
>      info = lookup(peer)
>      set MINTTL to 255 - info->ttl_hops
> 
> 
> The problem is that a rogue peer can still do three way
> handshake causing the accept to succeed. But the rogue will
> leave a stuck connection that will then have to timeout.
> 
> The only ways I have come up to deal with this are:
>    * have short timeout on initial data (recycle faster)
>    * push peer;MINTTL table down into kernel (per socket)
>    * have BGP do this through iptables which is a non-starter
>      for a general application that needs to run on BSD, and
>      would mess up existing firewall rules.
> 
> Ideas welcome..
> --

Another idea would be to store the TTL of the SYN packet (or third
packet) and let application read it after accept(), allowing it to
reject the connection if it doesnt match expected TTL.




^ permalink raw reply

* Re: tproxy related crash in inet_hashtables
From: Stephen Buck @ 2010-08-16  8:26 UTC (permalink / raw)
  To: David Miller
  Cc: eric.dumazet@gmail.com, netdev@vger.kernel.org,
	netfilter-devel@vger.kernel.org, kaber@trash.net,
	hidden@sch.bme.hu
In-Reply-To: <20100814.221611.15248639.davem@davemloft.net>

On 15/08/10 15:16, David Miller wrote:
> From: Stephen Buck<stephen.buck@exinda.com>
> Date: Sat, 14 Aug 2010 14:35:21 +1000
>
>    
>> +		}
>> +		if (!node)
>> +			tb = inet_bind_bucket_create(table->bind_bucket_cachep,
>> +						     sock_net(sk), head, port);
>> +	}
>>   	sk_add_bind_node(child,&tb->owners);
>>      
> inet_bind_bucket_create() can fail due to memory allocation failure,
> you're therefore going to have to find a way to handle that.
>
> And in doing so you're going to find out that there is no easy
> way out of this code path in such an error, this code was
> absolutely not designed to be able to handle cases like this.
>    
You're right. I didn't cover the failure case. And no, it doesn't look easy.
I'll have another look at the problem when I get a chance.

Thanks for pointing it out.

^ permalink raw reply

* [PATCH] netlink: fix compat recvmsg
From: Johannes Berg @ 2010-08-16  7:20 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David Miller, netdev, Kalle Valo
In-Reply-To: <1281940172.2942.81.camel@edumazet-laptop>

From: Johannes Berg <johannes.berg@intel.com>

Since
commit 1dacc76d0014a034b8aca14237c127d7c19d7726
Author: Johannes Berg <johannes@sipsolutions.net>
Date:   Wed Jul 1 11:26:02 2009 +0000

    net/compat/wext: send different messages to compat tasks

we had a race condition when setting and then
restoring frag_list. Eric attempted to fix it,
but the fix created even worse problems.

However, the original motivation I had when I
added the code that turned out to be racy is
no longer clear to me, since we only copy up
to skb->len to userspace, which doesn't include
the frag_list length. As a result, not doing
any frag_list clearing and restoring avoids
the race condition, while not introducing any
other problems.

Additionally, while preparing this patch I found
that since none of the remaining netlink code is
really aware of the frag_list, we need to use the
original skb's information for packet information
and credentials. This fixes, for example, the
group information received by compat tasks.

Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: stable@kernel.org [2.6.31+, for 2.6.35 revert 1235f504aa]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 net/netlink/af_netlink.c |   46 ++++++++++++++++------------------------------
 1 file changed, 16 insertions(+), 30 deletions(-)

--- wireless-testing.orig/net/netlink/af_netlink.c	2010-08-16 08:33:42.000000000 +0200
+++ wireless-testing/net/netlink/af_netlink.c	2010-08-16 09:13:17.000000000 +0200
@@ -1406,7 +1406,7 @@ static int netlink_recvmsg(struct kiocb
 	struct netlink_sock *nlk = nlk_sk(sk);
 	int noblock = flags&MSG_DONTWAIT;
 	size_t copied;
-	struct sk_buff *skb, *frag __maybe_unused = NULL;
+	struct sk_buff *skb, *data_skb;
 	int err;
 
 	if (flags&MSG_OOB)
@@ -1418,45 +1418,35 @@ static int netlink_recvmsg(struct kiocb
 	if (skb == NULL)
 		goto out;
 
+	data_skb = skb;
+
 #ifdef CONFIG_COMPAT_NETLINK_MESSAGES
 	if (unlikely(skb_shinfo(skb)->frag_list)) {
-		bool need_compat = !!(flags & MSG_CMSG_COMPAT);
-
 		/*
-		 * If this skb has a frag_list, then here that means that
-		 * we will have to use the frag_list skb for compat tasks
-		 * and the regular skb for non-compat tasks.
+		 * If this skb has a frag_list, then here that means that we
+		 * will have to use the frag_list skb's data for compat tasks
+		 * and the regular skb's data for normal (non-compat) tasks.
 		 *
-		 * The skb might (and likely will) be cloned, so we can't
-		 * just reset frag_list and go on with things -- we need to
-		 * keep that. For the compat case that's easy -- simply get
-		 * a reference to the compat skb and free the regular one
-		 * including the frag. For the non-compat case, we need to
-		 * avoid sending the frag to the user -- so assign NULL but
-		 * restore it below before freeing the skb.
+		 * If we need to send the compat skb, assign it to the
+		 * 'data_skb' variable so that it will be used below for data
+		 * copying. We keep 'skb' for everything else, including
+		 * freeing both later.
 		 */
-		if (need_compat) {
-			struct sk_buff *compskb = skb_shinfo(skb)->frag_list;
-			skb_get(compskb);
-			kfree_skb(skb);
-			skb = compskb;
-		} else {
-			frag = skb_shinfo(skb)->frag_list;
-			skb_shinfo(skb)->frag_list = NULL;
-		}
+		if (flags & MSG_CMSG_COMPAT)
+			data_skb = skb_shinfo(skb)->frag_list;
 	}
 #endif
 
 	msg->msg_namelen = 0;
 
-	copied = skb->len;
+	copied = data_skb->len;
 	if (len < copied) {
 		msg->msg_flags |= MSG_TRUNC;
 		copied = len;
 	}
 
-	skb_reset_transport_header(skb);
-	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+	skb_reset_transport_header(data_skb);
+	err = skb_copy_datagram_iovec(data_skb, 0, msg->msg_iov, copied);
 
 	if (msg->msg_name) {
 		struct sockaddr_nl *addr = (struct sockaddr_nl *)msg->msg_name;
@@ -1476,11 +1466,7 @@ static int netlink_recvmsg(struct kiocb
 	}
 	siocb->scm->creds = *NETLINK_CREDS(skb);
 	if (flags & MSG_TRUNC)
-		copied = skb->len;
-
-#ifdef CONFIG_COMPAT_NETLINK_MESSAGES
-	skb_shinfo(skb)->frag_list = frag;
-#endif
+		copied = data_skb->len;
 
 	skb_free_datagram(sk, skb);
 



^ permalink raw reply

* Re: [Power Efficiency Proposal] Low Power on Media Disconnect
From: LionSky @ 2010-08-16  7:00 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: netdev, jie.yang
In-Reply-To: <1281938880.2942.68.camel@edumazet-laptop>

Thanks Eric,
Yes, you are right. It is better to handle the ethernet device power
switch from a user policy daemon.
This is just a proposal about Low Power on Media Disconnect which
means when media, such as ethernet cable, is disconnected, the related
device should be placed in a low power state, such as D3hot.
Maybe we needs a smart implementation.

Thanks
-Lionsky

2010/8/16 Eric Dumazet <eric.dumazet@gmail.com>:
> Le lundi 16 août 2010 à 13:35 +0800, LionSky a écrit :
>> It is a very typical scenario that users of laptop/Netbook experience
>> Internet over wireless instead of ethernet.
>> An obvious hint about that is user unplug-in the ethernet cable which
>> implies that he does not want to use the ethernet. So for power
>> efficiency, it is best to place ethernet adapter into a low power
>> state, such as C3hot, when ethernet cable is disconnected.
>> Battery life is very important for Laptop/Netbook. Currently, the
>> power consumption of active idle on a typical Netbook is about 5-6
>> Watt. If you placed the etherent adapter in C3hot state, about
>> 0.5~1.0Watt power is saved which depneds on your H/W. This can prolong
>> battery life about 10%~20% which is a quite big power saving.
>> Now, Linux kernel supports runtime power management. It is not a hard
>> work to enable this feature on each ethernet adapter driver.
>>
>> Here I attached a simple and prototype implementation which is
>> verified on a Netbook (ASUS E1005PE with Atheros AR8132 PCI-E Fast
>> Ethernet Controller).
>> 1. When ethernet cable is disconnected, driver places ethernet adapter
>> into D3hot low power state. Just as CPU go to deep C-State.
>> 2. When ethernet cable is connected again, driver places ethernet back
>> in D0 power state.
>>
>> To further improve the power behavior of Linux on laptop/Netbook, a
>> proposal is that all ethernet drivers must support Low Power on Media
>
>  ... should support ...
>
>> Disconnect.
>>
>> Is it right? Welcome any comments.
>>
>
> I cannot say if this patch is right on the power side.
> (Would it be better to handle the ethernet device power switch from a
> user policy daemon, instead of forcing the policy from the driver ?)
> I dont know the implications of Low Power on Media disconnect.
> At least, let ethtool be able to switch on or off this automatic
> behavior...
>
> But your patch introduces a shared (static actually) variable, atl1c_d3.
> Are you sure a single state is enough to handle several ethernet ports ?
>
>
>
>

^ permalink raw reply

* Re: PROBLEM: raw sockets rewriting IP ID in rare cases.
From: Jiri Olsa @ 2010-08-16  6:50 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David Miller, Michael Kerrisk, Morgon.J.Kanter, netdev,
	Stephen Hemminger
In-Reply-To: <1281939985.2942.79.camel@edumazet-laptop>

On Mon, Aug 16, 2010 at 08:26:25AM +0200, Eric Dumazet wrote:
> Le dimanche 15 août 2010 à 22:47 +0200, Eric Dumazet a écrit :
> 
> > Jiri Olsa added IP_NODEFRAG option some weeks ago (commit 7b2ff18e),
> > we probably could implement IP_NOIDENT option for RAW sockets ?
> > 
> > 
> 
> Hmm, it seems we forgot to send to Michael some updates for
> documentation.
> 
> http://www.kernel.org/doc/man-pages/online/pages/man7/ip.7.html
> 
> misses IP_FREEBIND, IP_IPSEC_POLICY, IP_XFRM_POLICY, IP_PASSSEC
> IP_TRANSPARENT, IP_ORIGDSTADDR, IP_MINTTL and IP_NODEFRAG bits
> 
> 
> 

hi,
I've sent out the IP_NODEFRAG man bits together with the change
http://kerneltrap.org/mailarchive/linux-netdev/2010/6/26/6280033

jirka

^ permalink raw reply

* Re: [PATCH 2/2] platform: Facilitate the creation of pseudo-platform buses
From: Grant Likely @ 2010-08-16  6:43 UTC (permalink / raw)
  To: Moffett, Kyle D, Patrick Pannuto
  Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
	magnus.damm@gmail.com, gregkh@suse.de, Kevin Hilman, Paul Mundt,
	Magnus Damm, Rafael J. Wysocki, Eric Miao, Dmitry Torokhov,
	netdev@vger.kernel.org, Kyle D Moffett
In-Reply-To: <3F978429-F916-42E5-8B36-6AC02DAC8CA2@boeing.com>

"Moffett, Kyle D" <Kyle.D.Moffett@boeing.com> wrote:

>On Aug 10, 2010, at 19:49, Patrick Pannuto wrote:
>> As SOCs become more popular, the desire to quickly define a simple,
>> but functional, bus type with only a few unique properties becomes
>> desirable. As they become more complicated, the ability to nest these
>> simple busses and otherwise orchestrate them to match the actual
>> topology also becomes desirable.
>>
>> EXAMPLE USAGE
>>
>> /arch/ARCH/MY_ARCH/my_bus.c:
>>
>> 	#include <linux/device.h>
>> 	#include <linux/platform_device.h>
>>
>> 	struct bus_type SOC_bus_type = {
>> 		.name = "SOC-bus-type",
>> 	};
>> 	EXPORT_SYMBOL_GPL(SOC_bus_type);
>>
>> 	struct platform_device SOC_bus1 = {
>> 		.name	 = "SOC-bus1",
>> 		.id		= -1,
>> 		.dev.bus = &SOC_bus_type;
>> 	};
>> 	EXPORT_SYMBOL_GPL(SOC_bus1);
>>
>> 	struct platform_device SOC_bus2 = {
>> 		.name	 = "SOC-bus2",
>> 		.id		= -2,
>> 		.dev.bus = &SOC_bus_type;
>> 	};
>> 	EXPORT_SYMBOL_GPL(SOC_bus2);
>>
>> 	static int __init SOC_bus_init(void)
>> 	{
>> 		int error;
>>
>> 		error = pseudo_platform_bus_register(&SOC_bus_type);
>> 		if (error)
>> 			return error;
>>
>> 		error = platform_device_register(&SOC_bus1);
>> 		if (error)
>> 			goto fail_bus1;
>>
>> 		error = platform_device_register(&SOC_bus2);
>> 		if (error)
>> 			goto fail_bus2;
>>
>> 		return error;
>>
>> 		/* platform_device_unregister(&SOC_bus2); */
>> fail_bus2:
>> 		platform_device_unregister(&SOC_bus1);
>> fail_bus1:
>> 		pseudo_platform_bus_unregister(&SOC_bus_type);
>>
>> 		return error;
>> 	}
>>
>> /drivers/my_driver.c:
>> 	static struct platform_driver my_driver = {
>> 		.driver = {
>> 			.name	= "my-driver",
>> 			.owner	= THIS_MODULE,
>> 			.bus	= &SOC_bus_type,
>> 		},
>> 	};
>>
>> /somewhere/my_device.c:
>> 	static struct platform_device my_device = {
>> 		.name		= "my-device",
>> 		.id		= -1,
>> 		.dev.bus	= &my_bus_type,
>> 		.dev.parent	= &SOC_bus1.dev,
>> 	};
>>
>> This will build a device tree that mirrors the actual system:
>>
>> /sys/bus
>> |-- SOC-bus-type
>> |   |-- devices
>> |   |   |-- SOC_bus1 -> ../../../devices/SOC_bus1
>> |   |   |-- SOC_bus2 -> ../../../devices/SOC_bus2
>> |   |   |-- my-device -> ../../../devices/SOC_bus1/my-device
>> |   |-- drivers
>> |   |   |-- my-driver
>>
>> /sys/devices
>> |-- SOC_bus1
>> |   |-- my-device
>> |-- SOC_bus2
>>
>> Driver can drive any device on the SOC, which is logical, without
>> actually being registered on multiple /bus_types/, even though the
>> devices may be on different /physical buses/ (which are actually
>> just devices).
>
>Hmm...
>
>To me this seems like a really painful implementation of what the OpenFirmware-esque "Flattened Device Tree" does on many embedded systems.

Patrick and Kevin have been trying to solve a different problem.  The
FDT is really good at describing the topology and interconnections of
the system, but it doesn't solve the problem of how the different bus
behaviour is implemented in the kernel.  They need a method of
registering devices that have subtly different behaviour from the
plain-vanilla platform bus.  Whether or not the device data originates
from the FDT is irrelevant, and the problem remains the same.

I'm not convinced (yet) that this is the right approach, and I'd like
to see a few sample drivers converted to the new approach.  Creating
new bus_types that "inherit" from the platform_bus is actually not a
bad idea, and it is an elegant way to change the behaviour (for
example, how power management is implemented) for devices connected in
a different way.

A problem with the approach that Kevin pointed out is that drivers
that need to work on both the platform_bus_type and the new
soc_bus_type must explicitly register themselves on both bus types.
There is no mechanism to allow drivers from one bus type to also be
made available to another bus type. Certainly it would be possible to
invent a mechanism, but the more I think about it, them more I think
it will be a bad idea.  The runtime-PM use-case that kicked this
discussion off makes the assumption that a driver will behave
identically when attached to either the platform_bus_type or the
soc_bus_type.  However, I think that in the general case that
assumption will prove to be false.  I strongly suspect that the new
bus type will turn out to be not as similar to the platform_bus_type
as originally assumed and that there will still be bus-type-specific
impact on device drivers (but I digress; this paragraph is more
directed to Patrick and Kevin, and doesn't address your comments).

More below...

>For example, to build an equivalent device tree using an OpenFirmware FDT file, I'd just use this:
>
>/dts-v1/;
>/ {
>	#address-cells = <1>;
>	#size-cells = <1>;
>	[...snip...]
>
>	soc-bus-1@fc000000 {
>		/* of_platform driver matches against this: */
>		compatible = "my-company-name,soc-bus-type";
>
>		/* Define base address and size of the bus */
>		reg = <0xfc000000 0x01000000>;
>		#address-cells = <1>;
>		#size-cells = <1>;
>
>		/*
>		 * Define logical memory mapping relative to the bus addr:
>		 * First field is the relative base address for children,
>		 * second field is the address in the parent's memory map,
>		 * third field is the size of the range.
>		 */
>		ranges = <0x0 0xfc000000 0x01000000>;
>
>		/* Now for sub-devices */
>		my-device@0x10000 {
>			compatible = "my-company-name,my-driver";
>			reg = <0x10000 0x100>; /* Address and size */
>		};
>	};
>
>	soc-bus-2@fd000000 {
>		/* of_platform driver matches against this: */
>		compatible = "my-company-name,soc-bus-type";
>
>		/* Define base address and size of the bus */
>		reg = <0xfd000000 0x01000000>;
>		#address-cells = <1>;
>		#size-cells = <1>;
>
>		/*
>		 * Define logical memory mapping relative to the bus addr:
>		 * First field is the relative base address for children,
>		 * second field is the address in the parent's memory map,
>		 * third field is the size of the range.
>		 */
>		ranges = <0x0 0xfd000000 0x01000000>;
>	};
>};
>
>If you don't need to actually do anything special at the bus level, you can just:
>	static struct of_device_id soc_bus_ids[] = {
>		{ .compatible = "soc-bus-type", },
>		{},
>	};
>	of_platform_bus_probe(NULL, &soc_bus_ids, NULL);
>
>Any of_platform driver that matches something on one of those busses is automatically probed.  Alternatively, if you need special bus behavior:
>
>	static struct of_device_id soc_bus_ids[] = {
>		{ .compatible = "soc-bus-type", },
>		{},
>	};
>	static struct of_platform_driver soc_bus_type = {
>		.name = "soc-bus-type",
>		.match_table = &soc_bus_ids,
>		.owner = THIS_MODULE,
>		.probe = mybus_probe,
>		.remove = mybus_remove,
>		.suspend = mybus_suspend,
>		.resume = mybus_resume,
>		.shutdown = mybus_shutdown,
>	};
>
>Then your .probe function actually registers a new OF bus.

Be careful!  This is actually a confusing example.  Linux already has
a concept of a bus_type, and it is not the same as what is shown in
this example.  Part of the problem is that Patrick's explanation leads
the reader to conflate two separate concepts; device topology and
bus_types (but I believe that Patrick understands the difference
between the two).  Topology is the tree of struct devices in the LInux
device model (specified by the .parent pointer).  It is represented by
the /sys/devices/* directory tree.

bus_type groups devices that use the same bus infrastructure, but it
says nothing about topology.  bus_types are represented by the
/sys/bus directory.  For example, a device on the i2c_bus_type must be
an i2c_device, is accessed with the i2c infrastructure, and can be
bound to an i2c_driver.  There can be multiple physical i2c busses in
a system, but all i2c_devices are grouped together in
/sys/bus/i2c/devices (as symlinks to the real location in
/sys/devices).

Using the name "soc-bus-type" in this example makes it easy to confuse
this of_platform_driver with an actual bus_type.

BTW, you'll probably be interested to know that as of about a week
ago, Linus pulled my tree which replaces of_platform_bus_type with the
regular platform_bus_type.  There no longer is any differentiation
between OF and non-OF devices.  Any device may have a pointer to an OF
device tree node regardless of bus_type.  Existing of_platform_drivers
do still work, but a shim is used to register them onto the platform
driver.  Next step is to convert existing of_platform_drivers (like
your example above) into normal platform_drivers.

>The best part is... all devices registered as "of_platform" devices can be used to support many entirely different board models from the exact same kernel.
>
>Fully commented and with actual physical addresses in, my FDT example is comparable to your sample code.  Furthermore, all of the error handling is automatically done, a bunch of device drivers are already ported over, and all the kinks regarding interrupts, etc are already taken care of.  I highly recommend taking a look to see if you can use the very nice existing OF bus code to solve your problem instead of writing yet another half-hard-coded platform bus type.
>
>Cheers,
>Kyle Moffett
>

^ permalink raw reply

* Re: [PATCH net-next-2.6] netlink: netlink_recvmsg() fix
From: Johannes Berg @ 2010-08-16  6:31 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David Miller, netdev
In-Reply-To: <1281940172.2942.81.camel@edumazet-laptop>

On Mon, 2010-08-16 at 08:29 +0200, Eric Dumazet wrote:
> Le lundi 16 août 2010 à 08:22 +0200, Johannes Berg a écrit :
> > On Mon, 2010-08-16 at 08:10 +0200, Eric Dumazet wrote:
> > 
> > > We'll find another way to address the problem.
> > 
> > For my understanding: The problem is that the save/restore of the
> > frag_list skb can race so a second reader will not see the frag_list skb
> > because it gets there while it's NULL. This happens with MSG_PEEK, or
> > with clones of the SKB since skb_shared_info is invariant across clones.
> > 
> > Correct?
> > 
> 
> Yes
> 
> I believe we should have a mutual exclusion for the critical section.
> 
> (setting pointer to NULL, ...., setting pointer back to its orig value)

Yeah, that'd work, but I'm wondering now why I did this at all. The code
already restricts the data copying to skb->len, which will not include
the frag_list, so it should be OK to just leave it intact? I'll go and
look at that in more detail and try it.

johannes


^ permalink raw reply

* Re: [PATCH net-next-2.6] netlink: netlink_recvmsg() fix
From: Eric Dumazet @ 2010-08-16  6:29 UTC (permalink / raw)
  To: Johannes Berg; +Cc: David Miller, netdev
In-Reply-To: <1281939753.3683.5.camel@jlt3.sipsolutions.net>

Le lundi 16 août 2010 à 08:22 +0200, Johannes Berg a écrit :
> On Mon, 2010-08-16 at 08:10 +0200, Eric Dumazet wrote:
> 
> > We'll find another way to address the problem.
> 
> For my understanding: The problem is that the save/restore of the
> frag_list skb can race so a second reader will not see the frag_list skb
> because it gets there while it's NULL. This happens with MSG_PEEK, or
> with clones of the SKB since skb_shared_info is invariant across clones.
> 
> Correct?
> 

Yes

I believe we should have a mutual exclusion for the critical section.

(setting pointer to NULL, ...., setting pointer back to its orig value)




^ permalink raw reply

* Re: PROBLEM: raw sockets rewriting IP ID in rare cases.
From: Eric Dumazet @ 2010-08-16  6:26 UTC (permalink / raw)
  To: David Miller, Michael Kerrisk
  Cc: Morgon.J.Kanter, netdev, Jiri Olsa, Stephen Hemminger
In-Reply-To: <1281905220.2942.57.camel@edumazet-laptop>

Le dimanche 15 août 2010 à 22:47 +0200, Eric Dumazet a écrit :

> Jiri Olsa added IP_NODEFRAG option some weeks ago (commit 7b2ff18e),
> we probably could implement IP_NOIDENT option for RAW sockets ?
> 
> 

Hmm, it seems we forgot to send to Michael some updates for
documentation.

http://www.kernel.org/doc/man-pages/online/pages/man7/ip.7.html

misses IP_FREEBIND, IP_IPSEC_POLICY, IP_XFRM_POLICY, IP_PASSSEC
IP_TRANSPARENT, IP_ORIGDSTADDR, IP_MINTTL and IP_NODEFRAG bits




^ permalink raw reply

* Re: [PATCH net-next-2.6] netlink: netlink_recvmsg() fix
From: Johannes Berg @ 2010-08-16  6:22 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David Miller, netdev
In-Reply-To: <1281939002.2942.70.camel@edumazet-laptop>

On Mon, 2010-08-16 at 08:10 +0200, Eric Dumazet wrote:

> We'll find another way to address the problem.

For my understanding: The problem is that the save/restore of the
frag_list skb can race so a second reader will not see the frag_list skb
because it gets there while it's NULL. This happens with MSG_PEEK, or
with clones of the SKB since skb_shared_info is invariant across clones.

Correct?

johannes


^ permalink raw reply

* Re: [PATCH net-next-2.6] netlink: netlink_recvmsg() fix
From: David Miller @ 2010-08-16  6:21 UTC (permalink / raw)
  To: eric.dumazet; +Cc: johannes, netdev
In-Reply-To: <1281939002.2942.70.camel@edumazet-laptop>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Mon, 16 Aug 2010 08:10:02 +0200

> Le lundi 16 août 2010 à 07:25 +0200, Johannes Berg a écrit :
> 
>> I haven't gotten around to it, but Kalle had been running into the same
>> issue and said reverting it fixed it:
>> 
>> http://article.gmane.org/gmane.linux.kernel.wireless.general/54492
>> 
> 
> Thanks Johannes
> 
> Please David revert 1235f504.
> 
> We'll find another way to address the problem.

I will, thanks guys.

^ permalink raw reply

* Re: [PATCH net-next-2.6] netlink: netlink_recvmsg() fix
From: Eric Dumazet @ 2010-08-16  6:10 UTC (permalink / raw)
  To: Johannes Berg; +Cc: David Miller, netdev
In-Reply-To: <1281936351.3683.1.camel@jlt3.sipsolutions.net>

Le lundi 16 août 2010 à 07:25 +0200, Johannes Berg a écrit :

> I haven't gotten around to it, but Kalle had been running into the same
> issue and said reverting it fixed it:
> 
> http://article.gmane.org/gmane.linux.kernel.wireless.general/54492
> 

Thanks Johannes

Please David revert 1235f504.

We'll find another way to address the problem.




^ permalink raw reply

* Re: [Power Efficiency Proposal] Low Power on Media Disconnect
From: Eric Dumazet @ 2010-08-16  6:08 UTC (permalink / raw)
  To: LionSky; +Cc: netdev, jie.yang
In-Reply-To: <AANLkTin3DNEPDA+vpY-WuSa8ia-sgEA8_sr9qOLpDHph@mail.gmail.com>

Le lundi 16 août 2010 à 13:35 +0800, LionSky a écrit :
> It is a very typical scenario that users of laptop/Netbook experience
> Internet over wireless instead of ethernet.
> An obvious hint about that is user unplug-in the ethernet cable which
> implies that he does not want to use the ethernet. So for power
> efficiency, it is best to place ethernet adapter into a low power
> state, such as C3hot, when ethernet cable is disconnected.
> Battery life is very important for Laptop/Netbook. Currently, the
> power consumption of active idle on a typical Netbook is about 5-6
> Watt. If you placed the etherent adapter in C3hot state, about
> 0.5~1.0Watt power is saved which depneds on your H/W. This can prolong
> battery life about 10%~20% which is a quite big power saving.
> Now, Linux kernel supports runtime power management. It is not a hard
> work to enable this feature on each ethernet adapter driver.
> 
> Here I attached a simple and prototype implementation which is
> verified on a Netbook (ASUS E1005PE with Atheros AR8132 PCI-E Fast
> Ethernet Controller).
> 1. When ethernet cable is disconnected, driver places ethernet adapter
> into D3hot low power state. Just as CPU go to deep C-State.
> 2. When ethernet cable is connected again, driver places ethernet back
> in D0 power state.
> 
> To further improve the power behavior of Linux on laptop/Netbook, a
> proposal is that all ethernet drivers must support Low Power on Media

 ... should support ...

> Disconnect.
> 
> Is it right? Welcome any comments.
> 

I cannot say if this patch is right on the power side.
(Would it be better to handle the ethernet device power switch from a
user policy daemon, instead of forcing the policy from the driver ?)
I dont know the implications of Low Power on Media disconnect.
At least, let ethtool be able to switch on or off this automatic
behavior...

But your patch introduces a shared (static actually) variable, atl1c_d3.
Are you sure a single state is enough to handle several ethernet ports ?




^ permalink raw reply

* [Power Efficiency Proposal] Low Power on Media Disconnect
From: LionSky @ 2010-08-16  5:35 UTC (permalink / raw)
  To: netdev; +Cc: jie.yang, weiliang lin

[-- Attachment #1: Type: text/plain, Size: 1412 bytes --]

It is a very typical scenario that users of laptop/Netbook experience
Internet over wireless instead of ethernet.
An obvious hint about that is user unplug-in the ethernet cable which
implies that he does not want to use the ethernet. So for power
efficiency, it is best to place ethernet adapter into a low power
state, such as C3hot, when ethernet cable is disconnected.
Battery life is very important for Laptop/Netbook. Currently, the
power consumption of active idle on a typical Netbook is about 5-6
Watt. If you placed the etherent adapter in C3hot state, about
0.5~1.0Watt power is saved which depneds on your H/W. This can prolong
battery life about 10%~20% which is a quite big power saving.
Now, Linux kernel supports runtime power management. It is not a hard
work to enable this feature on each ethernet adapter driver.

Here I attached a simple and prototype implementation which is
verified on a Netbook (ASUS E1005PE with Atheros AR8132 PCI-E Fast
Ethernet Controller).
1. When ethernet cable is disconnected, driver places ethernet adapter
into D3hot low power state. Just as CPU go to deep C-State.
2. When ethernet cable is connected again, driver places ethernet back
in D0 power state.

To further improve the power behavior of Linux on laptop/Netbook, a
proposal is that all ethernet drivers must support Low Power on Media
Disconnect.

Is it right? Welcome any comments.


Thanks
-Lionsky

[-- Attachment #2: patch.atl1c.rt.txt --]
[-- Type: text/plain, Size: 3854 bytes --]

--- atl1cold/atl1c_main.c	2010-08-04 16:21:59.863328895 +0800
+++ atl1cnew/atl1c_main.c	2010-08-16 11:06:42.217285358 +0800
@@ -20,6 +20,7 @@
  */
 
 #include "atl1c.h"
+#include <linux/pm_runtime.h>
 
 #define ATL1C_DRV_VERSION "1.0.0.2-NAPI"
 char atl1c_driver_name[] = "atl1c";
@@ -66,6 +67,8 @@
 static void atl1c_setup_mac_ctrl(struct atl1c_adapter *adapter);
 static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que,
 		   int *work_done, int work_to_do);
+static int atl1c_runtime_suspend(struct device *device);
+static int atl1c_runtime_resume(struct device *device);
 
 static const u16 atl1c_pay_load_size[] = {
 	128, 256, 512, 1024, 2048, 4096,
@@ -98,6 +101,9 @@
 static const u32 atl1c_default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE |
 	NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP;
 
+/*a variable indicates current H/W power state*/
+static u16 atl1c_d3  = 1;
+
 /*
  * atl1c_init_pcie - init PCIE module
  */
@@ -240,8 +246,13 @@
 			atl1c_set_aspm(hw, false);
 		}
 		netif_carrier_off(netdev);
+
+		pm_runtime_put_noidle(&pdev->dev);
+		pm_schedule_suspend(&pdev->dev,0);
 	} else {
 		/* Link Up */
+		pm_request_resume(&pdev->dev);
+
 		hw->hibernate = false;
 		spin_lock_irqsave(&adapter->mdio_lock, flags);
 		err = atl1c_get_speed_and_duplex(hw, &speed, &duplex);
@@ -1576,6 +1587,21 @@
 }
 
 /*
+ * checkPoweState - If current power state is D3hot, resume it to D0 
+ * @pdev: pointer to a pci network interface device structure
+ */
+static inline int checkPowerState(struct pci_dev * pdev)
+{
+	if (unlikely(atl1c_d3)) {
+		pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, 0);
+		pdev->dev.power.runtime_status = 0;
+		atl1c_d3 = 0;
+		return 1;
+	}
+	return 0;
+}
+
+/*
  * atl1c_intr - Interrupt Handler
  * @irq: interrupt number
  * @data: pointer to a network interface device structure
@@ -1592,6 +1618,8 @@
 	u32 status;
 	u32 reg_data;
 
+	checkPowerState(pdev);
+
 	do {
 		AT_READ_REG(hw, REG_ISR, &reg_data);
 		status = reg_data & hw->intr_mask;
@@ -2703,6 +2731,12 @@
 	if (netif_msg_probe(adapter))
 		dev_info(&pdev->dev, "version %s\n", ATL1C_DRV_VERSION);
 	cards_found++;
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	
+	pm_runtime_idle(&pdev->dev);
+
 	return 0;
 
 err_reset:
@@ -2832,6 +2866,57 @@
 	.resume = atl1c_io_resume,
 };
 
+/* Adding runtime power management support*/
+#ifdef CONFIG_PM
+
+//callback function definition
+static int atl1c_runtime_suspend(struct device *device)
+{
+	struct pci_dev *pdev = to_pci_dev(device);
+
+	pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, 3);
+	atl1c_d3 = 1;
+	dev_err(&pdev->dev, "[debug] runtime_suspend\n");
+
+	return 0; 
+}
+
+static int atl1c_runtime_resume(struct device *device)
+{
+	struct pci_dev *pdev = to_pci_dev(device);
+
+	pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, 0);
+	atl1c_d3 = 0;
+	dev_err(&pdev->dev, "[debug] runtime_resume\n");
+
+	return 0; 
+}
+
+static int atl1c_runtime_idle(struct device *device)
+{
+	struct pci_dev *pdev = to_pci_dev(device);
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct atl1c_adapter *adapter = netdev_priv(dev);	
+
+	atl1c_check_link_status(adapter);
+
+	return 0; 
+}
+
+static const struct dev_pm_ops atl1c_pm_ops = {
+	.runtime_suspend = atl1c_runtime_suspend,
+	.runtime_resume = atl1c_runtime_resume,
+	.runtime_idle = atl1c_runtime_idle,
+};
+
+#define ATL1C_PM_OPS (&atl1c_pm_ops)
+
+#else /* !CONFIG_PM */
+
+#define ATL1C_PM_OPS	NULL
+
+#endif /* !CONFIG_PM */
+
 static struct pci_driver atl1c_driver = {
 	.name     = atl1c_driver_name,
 	.id_table = atl1c_pci_tbl,
@@ -2841,7 +2926,8 @@
 	.suspend  = atl1c_suspend,
 	.resume   = atl1c_resume,
 	.shutdown = atl1c_shutdown,
-	.err_handler = &atl1c_err_handler
+	.err_handler = &atl1c_err_handler,
+	.driver.pm = ATL1C_PM_OPS,
 };
 
 /*

^ permalink raw reply

* Re: [PATCH net-next-2.6] netlink: netlink_recvmsg() fix
From: Johannes Berg @ 2010-08-16  5:25 UTC (permalink / raw)
  To: David Miller; +Cc: eric.dumazet, netdev
In-Reply-To: <20100814.223722.116371746.davem@davemloft.net>

On Sat, 2010-08-14 at 22:37 -0700, David Miller wrote:
> From: Johannes Berg <johannes@sipsolutions.net>
> Date: Fri, 13 Aug 2010 17:13:53 +0200
> 
> > On Fri, 2010-08-13 at 16:48 +0200, Eric Dumazet wrote:
> > 
> >> I assume reverting 1235f504aaba removes these errors ?
> > 
> > I haven't tried yet, but it only happened very recently and I didn't
> > find any other candidate -- the error always points to
> > wireless_send_event too.
> 
> Please test with the commit reverted and let us know if it helps.
> 
> The current situation is worse than what we were trying to fix
> in that commit, so if a revert fixes your problem then as Eric
> said we should do that first.

I haven't gotten around to it, but Kalle had been running into the same
issue and said reverting it fixed it:

http://article.gmane.org/gmane.linux.kernel.wireless.general/54492

johannes


^ permalink raw reply

* dccp test-tree [Patch 1/1] ccid-3: use per-route min-RTO and/or TCP default
From: Gerrit Renker @ 2010-08-16  5:19 UTC (permalink / raw)
  To: Leandro, Ivo Calado, dccp; +Cc: netdev

This patch relates to the similar one submitted last week for CCID-2.

It replaces the hardcoded (Kconfig) value of the minimum RTO value with
a reconfigurable, per-route value which falls back to the TCP minimum
RTO value.

Leando, Ivo, can you please have a look and indicate whether you are ok
with this? If yes I would like to also update the CCID-4 tree to make it
consistent with the test tree - for this reason the test tree has not yet
been updated.

>>>>>>>>>>>>>>>>>>>>>>> Patch <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
dccp ccid-3: use per-route RTO or TCP RTO as fallback

This makes the TCP (per-route) RTO value also available to CCID-3, hence it
is now possible to influence this value at runtime rather than, as before,
via Kconfig.

The same effect as the default Kconfig option of 100ms is now achieved by

> ip route replace to unicast 192.168.0.0/24 rto_min 100j dev eth0
 
(assuming HZ=1000).

Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk>
---
 net/dccp/ccids/Kconfig |   31 -------------------------------
 net/dccp/ccids/ccid3.c |    9 ++++-----
 2 files changed, 4 insertions(+), 36 deletions(-)

--- a/net/dccp/ccids/Kconfig
+++ b/net/dccp/ccids/Kconfig
@@ -47,37 +47,6 @@ config IP_DCCP_CCID3_DEBUG
 
 	  If in doubt, say N.
 
-config IP_DCCP_CCID3_RTO
-	  int "Use higher bound for nofeedback timer"
-	  default 100
-	  depends on IP_DCCP_CCID3 && EXPERIMENTAL
-	  ---help---
-	    Use higher lower bound for nofeedback timer expiration.
-
-	    The TFRC nofeedback timer normally expires after the maximum of 4
-	    RTTs and twice the current send interval (RFC 3448, 4.3). On LANs
-	    with a small RTT this can mean a high processing load and reduced
-	    performance, since then the nofeedback timer is triggered very
-	    frequently.
-
-	    This option enables to set a higher lower bound for the nofeedback
-	    value. Values in units of milliseconds can be set here.
-
-	    A value of 0 disables this feature by enforcing the value specified
-	    in RFC 3448. The following values have been suggested as bounds for
-	    experimental use:
-		* 16-20ms to match the typical multimedia inter-frame interval
-		* 100ms as a reasonable compromise [default]
-		* 1000ms corresponds to the lower TCP RTO bound (RFC 2988, 2.4)
-
-	    The default of 100ms is a compromise between a large value for
-	    efficient DCCP implementations, and a small value to avoid disrupting
-	    the network in times of congestion.
-
-	    The purpose of the nofeedback timer is to slow DCCP down when there
-	    is serious network congestion: experimenting with larger values should
-	    therefore not be performed on WANs.
-
 config IP_DCCP_TFRC_LIB
 	def_bool y if IP_DCCP_CCID3
 
--- a/net/dccp/ccids/ccid3.c
+++ b/net/dccp/ccids/ccid3.c
@@ -461,12 +461,11 @@ done_computing_x:
 
 	/*
 	 * Update timeout interval for the nofeedback timer.
-	 * We use a configuration option to increase the lower bound.
-	 * This can help avoid triggering the nofeedback timer too
-	 * often ('spinning') on LANs with small RTTs.
+	 * We use the current (per-route) value of the TCP RTO as lower bound
+	 * to slow down rate-halving on networks with small RTTs (loopback,
+	 * running DCCP in a virtual machine, Gbit ethernet, etc).
 	 */
-	hc->tx_t_rto = max_t(u32, 4 * hc->tx_rtt, (CONFIG_IP_DCCP_CCID3_RTO *
-						       (USEC_PER_SEC / 1000)));
+	hc->tx_t_rto = max(4 * hc->tx_rtt, USEC_PER_SEC/HZ * tcp_rto_min(sk));
 	/*
 	 * Schedule no feedback timer to expire in
 	 * max(t_RTO, 2 * s/X)  =  max(t_RTO, 2 * t_ipi)

^ permalink raw reply

* Re: PROBLEM: raw sockets rewriting IP ID in rare cases.
From: Morgon.J.Kanter @ 2010-08-16  0:43 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: netdev
In-Reply-To: <1281905220.2942.57.camel@edumazet-laptop>

Quoting Eric Dumazet <eric.dumazet@gmail.com>:
> Le dimanche 15 août 2010 à 13:13 -0700, David Miller a écrit :
>> From: Morgon.J.Kanter@Dartmouth.edu
>> Date: Sun, 15 Aug 2010 13:57:41 -0400
>>
>> > Is there a way to turn this behavior off, then, with raw sockets? Zero
>> > is the desired ID value.
>>
>> No, there isn't.
>
> Just a note about IP_DF (Dont Fragment) bit.
>
> If set, ID stay 0.
>
> Not sure it can help Morgon.
>
> Jiri Olsa added IP_NODEFRAG option some weeks ago (commit 7b2ff18e),
> we probably could implement IP_NOIDENT option for RAW sockets ?

Thanks for the information. I was thinking of adding it in as a socket  
option as well, similar to that one you pointed out -- but seeing that  
makes me wonder, perhaps an "as-is" option would be more appropriate,  
instead of a different option for each field you want to leave as-is?

Thanks,
-- Morgon

^ permalink raw reply

* Re: Using virtio as a physical (wire-level) transport
From: Rusty Russell @ 2010-08-16  0:19 UTC (permalink / raw)
  To: virtualization
  Cc: Alexander Graf, Ira W. Snyder, netdev@vger.kernel.org, Zang Roy,
	Michael S. Tsirkin
In-Reply-To: <4C336074-FC8C-4BDF-B945-5295133CDB38@suse.de>

On Sat, 14 Aug 2010 09:04:19 pm Alexander Graf wrote:
> 
> Am 06.08.2010 um 11:34 schrieb "Ira W. Snyder" <iws@ovro.caltech.edu>:
> > This problem is not limited to my new use of virtio. Virtio is
> > completely useless in a relatively common virtualization scenario:
> > x86 host with qemu-ppc guest. Or any other big endian guest system.
> 
> This one actually works because we know that we're building for a BE guest.
> But I agree that it's a mess and clearly a very incorrect design decision.

Yes, since you need to know the guest's endian to virtualize it, the
correct interpretation of the virtio ring seemed the least problem.  Perhaps
I went overboard in simplification here, but it seemed pure legacy.

If we did a virtio2, as has been suggested, it would be possible to address
this.  You could of course do a hack where you detect the ring endianness
the first time they use it (based on avail.flags, avail.index and the
descriptor it would be quite reliable in practice).

Cheers,
Rusty.

^ permalink raw reply

* Re: PROBLEM: raw sockets rewriting IP ID in rare cases.
From: Eric Dumazet @ 2010-08-15 20:47 UTC (permalink / raw)
  To: David Miller; +Cc: Morgon.J.Kanter, netdev
In-Reply-To: <20100815.131359.191392588.davem@davemloft.net>

Le dimanche 15 août 2010 à 13:13 -0700, David Miller a écrit :
> From: Morgon.J.Kanter@Dartmouth.edu
> Date: Sun, 15 Aug 2010 13:57:41 -0400
> 
> > Is there a way to turn this behavior off, then, with raw sockets? Zero
> > is the desired ID value.
> 
> No, there isn't.

Just a note about IP_DF (Dont Fragment) bit.

If set, ID stay 0.

Not sure it can help Morgon.

Jiri Olsa added IP_NODEFRAG option some weeks ago (commit 7b2ff18e),
we probably could implement IP_NOIDENT option for RAW sockets ?




^ permalink raw reply

* Re: PROBLEM: raw sockets rewriting IP ID in rare cases.
From: David Miller @ 2010-08-15 20:13 UTC (permalink / raw)
  To: Morgon.J.Kanter; +Cc: netdev
In-Reply-To: <20100815135741.1iud2jvgw0wwg4kw@webmail.dartmouth.edu>

From: Morgon.J.Kanter@Dartmouth.edu
Date: Sun, 15 Aug 2010 13:57:41 -0400

> Is there a way to turn this behavior off, then, with raw sockets? Zero
> is the desired ID value.

No, there isn't.

^ permalink raw reply

* Re: via-rhine interrupts
From: Jarek Poplawski @ 2010-08-15 20:04 UTC (permalink / raw)
  To: =?UTF-8?B?SmFrdWIgUnXFvmnEjWth?=; +Cc: netdev
In-Reply-To: <AANLkTi=xfBSn=FS9=eR=NZa9QwrHAQwGc7Kh1-bUn3aj@mail.gmail.com>

Jakub Ružička wrote, On 29.07.2010 13:03:

> Hello,
Hi,

> the via-rhine driver powered cards generate a really big number of
> interrupts, almost one per packet (11429 interrupts for 8210 incoming
> and 2475 outgoing packets per second on full 100 Mbps load). This is
> observed on multiple different machines (embbed and desktop) and
> kernels (2.6.25 with and without NAPI, 2.6.30, 2.6.32 and 2.6.33). Do
> you have any idea why isn't the polling used or what can I try to find
> out what's wrong?
> 
> I have tested sending to/from the machines with nc and scp, measured
> interrups and load with atop. Few of these measurements on an embbed
> device (where the interrupt handling is a problem) are attached.

I've just tested it using a simplistic patch below, which skips
some napi receiving by doing it only every second jiffie (on even
ones), and I've got around 30% less interrupts from via-rhine,
which seems to suggest napi works OK, but there is too low
traffic (or too fast soft interrupt handling) to affect hard
interrupts. (Btw, probably CONFIG_HZ can matter here a bit too.
I tested with 1000.)

Cheers,
Jarek P.

--- (patch only for testing)

diff -Nurp a/net/core/dev.c b/net/core/dev.c
--- a/net/core/dev.c	2010-08-15 20:29:58.000000000 +0200
+++ b/net/core/dev.c	2010-08-15 21:15:04.000000000 +0200
@@ -3495,6 +3495,9 @@ static void net_rx_action(struct softirq
 		if (unlikely(budget <= 0 || time_after(jiffies, time_limit)))
 			goto softnet_break;
 
+		if (jiffies & 1)
+			goto softnet_break;
+
 		local_irq_enable();
 
 		/* Even though interrupts have been re-enabled, this


^ permalink raw reply

* [PATCH] netfilter: fix userspace header warning
From: Sam Ravnborg @ 2010-08-15 20:03 UTC (permalink / raw)
  To: Patrick McHardy, netdev, netfilter

"make headers_check" issued the following warning:

  CHECK   include/linux/netfilter (64 files)
usr/include/linux/netfilter/xt_ipvs.h:19: found __[us]{8,16,32,64} type without #include <linux/types.h>

Fix this by as suggested including linux/types.h.

Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
---
diff --git a/include/linux/netfilter/xt_ipvs.h b/include/linux/netfilter/xt_ipvs.h
index 1167aeb..eff34ac 100644
--- a/include/linux/netfilter/xt_ipvs.h
+++ b/include/linux/netfilter/xt_ipvs.h
@@ -1,6 +1,8 @@
 #ifndef _XT_IPVS_H
 #define _XT_IPVS_H
 
+#include <linux/types.h>
+
 enum {
 	XT_IPVS_IPVS_PROPERTY =	1 << 0, /* all other options imply this one */
 	XT_IPVS_PROTO =		1 << 1,

^ permalink raw reply related

* [PATCH] tcp: allow effective reduction of TCP's rcv-buffer via setsockopt
From: Hagen Paul Pfeifer @ 2010-08-15 19:36 UTC (permalink / raw)
  To: netdev
  Cc: Hagen Paul Pfeifer, David S. Miller, Patrick McHardy,
	Eric Dumazet, Ilpo Järvinen

Via setsockopt it is possible to reduce the socket RX buffer
(SO_RCVBUF). TCP method to select the initial window and window scaling
option in tcp_select_initial_window() currently misbehaves and do not
consider a reduced RX socket buffer via setsockopt.

Even though the server's RX buffer is reduced via setsockopt() to 256
byte (Initial Window 384 byte => 256 * 2 - (256 * 2 / 4)) the window
scale option is still 7:

192.168.1.38.40676 > 78.47.222.210.5001: Flags [S], seq 2577214362, win 5840, options [mss 1460,sackOK,TS val 338417 ecr 0,nop,wscale 0], length 0
78.47.222.210.5001 > 192.168.1.38.40676: Flags [S.], seq 1570631029, ack 2577214363, win 384, options [mss 1452,sackOK,TS val 2435248895 ecr 338417,nop,wscale 7], length 0
192.168.1.38.40676 > 78.47.222.210.5001: Flags [.], ack 1, win 5840, options [nop,nop,TS val 338421 ecr 2435248895], length 0

Within tcp_select_initial_window() the original space argument - a
representation of the rx buffer size - is expanded during
tcp_select_initial_window(). Only sysctl_tcp_rmem[2], sysctl_rmem_max
and window_clamp are considered to calculate the initial window.

This patch adjust the window_clamp argument if the user explicitly
reduce the receive buffer.

Signed-off-by: Hagen Paul Pfeifer <hagen@jauu.net>
Cc: David S. Miller <davem@davemloft.net>
Cc: Patrick McHardy <kaber@trash.net>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
---
 net/ipv4/tcp_output.c |   11 +++++++++++
 1 files changed, 11 insertions(+), 0 deletions(-)

diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index de3bd84..c605312 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2429,6 +2429,12 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
 		__u8 rcv_wscale;
 		/* Set this up on the first call only */
 		req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW);
+
+		/* limit the window selection if the user enforce a smaller rx buffer */
+		if (sk->sk_userlocks & SOCK_RCVBUF_LOCK &&
+				(req->window_clamp > tcp_full_space(sk) || req->window_clamp == 0))
+			req->window_clamp = tcp_full_space(sk);
+
 		/* tcp_full_space because it is guaranteed to be the first packet */
 		tcp_select_initial_window(tcp_full_space(sk),
 			mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0),
@@ -2555,6 +2561,11 @@ static void tcp_connect_init(struct sock *sk)
 
 	tcp_initialize_rcv_mss(sk);
 
+	/* limit the window selection if the user enforce a smaller rx buffer */
+	if (sk->sk_userlocks & SOCK_RCVBUF_LOCK &&
+			(tp->window_clamp > tcp_full_space(sk) || tp->window_clamp == 0))
+		tp->window_clamp = tcp_full_space(sk);
+
 	tcp_select_initial_window(tcp_full_space(sk),
 				  tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0),
 				  &tp->rcv_wnd,
-- 
1.6.6.196.g1f735.dirty


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox