* [PATCH 2/2 net-next-2.6] au1000-eth: convert to platform_driver model
From: Florian Fainelli @ 2009-11-08 14:42 UTC (permalink / raw)
To: linux-mips, Ralf Baechle; +Cc: netdev, David Miller
This patch converts the au1000-eth driver to become a full
platform-driver as it ought to be. We now pass PHY-speficic
configurations through platform_data but for compatibility
the driver still assumes the default settings (search for PHY1 on
MAC0) when no platform_data is passed. Tested on my MTX-1 board.
Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c
index ce6f1ac..acc0c07 100644
--- a/drivers/net/au1000_eth.c
+++ b/drivers/net/au1000_eth.c
@@ -55,6 +55,7 @@
#include <linux/delay.h>
#include <linux/crc32.h>
#include <linux/phy.h>
+#include <linux/platform_device.h>
#include <asm/cpu.h>
#include <asm/mipsregs.h>
@@ -63,6 +64,7 @@
#include <asm/processor.h>
#include <au1000.h>
+#include <au1xxx_eth.h>
#include <prom.h>
#include "au1000_eth.h"
@@ -112,15 +114,15 @@ struct au1000_private *au_macs[NUM_ETH_INTERFACES];
*
* PHY detection algorithm
*
- * If AU1XXX_PHY_STATIC_CONFIG is undefined, the PHY setup is
+ * If phy_static_config is undefined, the PHY setup is
* autodetected:
*
* mii_probe() first searches the current MAC's MII bus for a PHY,
- * selecting the first (or last, if AU1XXX_PHY_SEARCH_HIGHEST_ADDR is
+ * selecting the first (or last, if phy_search_highest_addr is
* defined) PHY address not already claimed by another netdev.
*
* If nothing was found that way when searching for the 2nd ethernet
- * controller's PHY and AU1XXX_PHY1_SEARCH_ON_MAC0 is defined, then
+ * controller's PHY and phy1_search_mac0 is defined, then
* the first MII bus is searched as well for an unclaimed PHY; this is
* needed in case of a dual-PHY accessible only through the MAC0's MII
* bus.
@@ -129,9 +131,7 @@ struct au1000_private *au_macs[NUM_ETH_INTERFACES];
* controller is not registered to the network subsystem.
*/
-/* autodetection defaults */
-#undef AU1XXX_PHY_SEARCH_HIGHEST_ADDR
-#define AU1XXX_PHY1_SEARCH_ON_MAC0
+/* autodetection defaults: phy1_search_mac0 */
/* static PHY setup
*
@@ -148,29 +148,6 @@ struct au1000_private *au_macs[NUM_ETH_INTERFACES];
* specific irq-map
*/
-#if defined(CONFIG_MIPS_BOSPORUS)
-/*
- * Micrel/Kendin 5 port switch attached to MAC0,
- * MAC0 is associated with PHY address 5 (== WAN port)
- * MAC1 is not associated with any PHY, since it's connected directly
- * to the switch.
- * no interrupts are used
- */
-# define AU1XXX_PHY_STATIC_CONFIG
-
-# define AU1XXX_PHY0_ADDR 5
-# define AU1XXX_PHY0_BUSID 0
-# undef AU1XXX_PHY0_IRQ
-
-# undef AU1XXX_PHY1_ADDR
-# undef AU1XXX_PHY1_BUSID
-# undef AU1XXX_PHY1_IRQ
-#endif
-
-#if defined(AU1XXX_PHY0_BUSID) && (AU1XXX_PHY0_BUSID > 0)
-# error MAC0-associated PHY attached 2nd MACs MII bus not supported yet
-#endif
-
static void enable_mac(struct net_device *dev, int force_reset)
{
unsigned long flags;
@@ -390,67 +367,54 @@ static int mii_probe (struct net_device *dev)
struct au1000_private *const aup = netdev_priv(dev);
struct phy_device *phydev = NULL;
-#if defined(AU1XXX_PHY_STATIC_CONFIG)
- BUG_ON(aup->mac_id < 0 || aup->mac_id > 1);
+ if (aup->phy_static_config) {
+ BUG_ON(aup->mac_id < 0 || aup->mac_id > 1);
- if(aup->mac_id == 0) { /* get PHY0 */
-# if defined(AU1XXX_PHY0_ADDR)
- phydev = au_macs[AU1XXX_PHY0_BUSID]->mii_bus->phy_map[AU1XXX_PHY0_ADDR];
-# else
- printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n",
- dev->name);
- return 0;
-# endif /* defined(AU1XXX_PHY0_ADDR) */
- } else if (aup->mac_id == 1) { /* get PHY1 */
-# if defined(AU1XXX_PHY1_ADDR)
- phydev = au_macs[AU1XXX_PHY1_BUSID]->mii_bus->phy_map[AU1XXX_PHY1_ADDR];
-# else
- printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n",
- dev->name);
+ if (aup->phy_addr)
+ phydev = aup->mii_bus->phy_map[aup->phy_addr];
+ else
+ printk (KERN_INFO DRV_NAME ":%s: using PHY-less setup\n",
+ dev->name);
return 0;
-# endif /* defined(AU1XXX_PHY1_ADDR) */
- }
-
-#else /* defined(AU1XXX_PHY_STATIC_CONFIG) */
- int phy_addr;
-
- /* find the first (lowest address) PHY on the current MAC's MII bus */
- for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)
- if (aup->mii_bus->phy_map[phy_addr]) {
- phydev = aup->mii_bus->phy_map[phy_addr];
-# if !defined(AU1XXX_PHY_SEARCH_HIGHEST_ADDR)
- break; /* break out with first one found */
-# endif
- }
-
-# if defined(AU1XXX_PHY1_SEARCH_ON_MAC0)
- /* try harder to find a PHY */
- if (!phydev && (aup->mac_id == 1)) {
- /* no PHY found, maybe we have a dual PHY? */
- printk (KERN_INFO DRV_NAME ": no PHY found on MAC1, "
- "let's see if it's attached to MAC0...\n");
-
- BUG_ON(!au_macs[0]);
-
- /* find the first (lowest address) non-attached PHY on
- * the MAC0 MII bus */
- for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
- struct phy_device *const tmp_phydev =
- au_macs[0]->mii_bus->phy_map[phy_addr];
-
- if (!tmp_phydev)
- continue; /* no PHY here... */
-
- if (tmp_phydev->attached_dev)
- continue; /* already claimed by MAC0 */
+ } else {
+ int phy_addr;
+
+ /* find the first (lowest address) PHY on the current MAC's MII bus */
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)
+ if (aup->mii_bus->phy_map[phy_addr]) {
+ phydev = aup->mii_bus->phy_map[phy_addr];
+ if (!aup->phy_search_highest_addr)
+ break; /* break out with first one found */
+ }
- phydev = tmp_phydev;
- break; /* found it */
+ if (aup->phy1_search_mac0) {
+ /* try harder to find a PHY */
+ if (!phydev && (aup->mac_id == 1)) {
+ /* no PHY found, maybe we have a dual PHY? */
+ printk (KERN_INFO DRV_NAME ": no PHY found on MAC1, "
+ "let's see if it's attached to MAC0...\n");
+
+ /* find the first (lowest address) non-attached PHY on
+ * the MAC0 MII bus */
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+ if (aup->mac_id == 1)
+ break;
+ struct phy_device *const tmp_phydev =
+ aup->mii_bus->phy_map[phy_addr];
+
+ if (!tmp_phydev)
+ continue; /* no PHY here... */
+
+ if (tmp_phydev->attached_dev)
+ continue; /* already claimed by MAC0 */
+
+ phydev = tmp_phydev;
+ break; /* found it */
+ }
+ }
}
}
-# endif /* defined(AU1XXX_PHY1_SEARCH_OTHER_BUS) */
-#endif /* defined(AU1XXX_PHY_STATIC_CONFIG) */
if (!phydev) {
printk (KERN_ERR DRV_NAME ":%s: no PHY found\n", dev->name);
return -1;
@@ -578,31 +542,6 @@ setup_hw_rings(struct au1000_private *aup, u32 rx_base, u32 tx_base)
}
}
-static struct {
- u32 base_addr;
- u32 macen_addr;
- int irq;
- struct net_device *dev;
-} iflist[2] = {
-#ifdef CONFIG_SOC_AU1000
- {AU1000_ETH0_BASE, AU1000_MAC0_ENABLE, AU1000_MAC0_DMA_INT},
- {AU1000_ETH1_BASE, AU1000_MAC1_ENABLE, AU1000_MAC1_DMA_INT}
-#endif
-#ifdef CONFIG_SOC_AU1100
- {AU1100_ETH0_BASE, AU1100_MAC0_ENABLE, AU1100_MAC0_DMA_INT}
-#endif
-#ifdef CONFIG_SOC_AU1500
- {AU1500_ETH0_BASE, AU1500_MAC0_ENABLE, AU1500_MAC0_DMA_INT},
- {AU1500_ETH1_BASE, AU1500_MAC1_ENABLE, AU1500_MAC1_DMA_INT}
-#endif
-#ifdef CONFIG_SOC_AU1550
- {AU1550_ETH0_BASE, AU1550_MAC0_ENABLE, AU1550_MAC0_DMA_INT},
- {AU1550_ETH1_BASE, AU1550_MAC1_ENABLE, AU1550_MAC1_DMA_INT}
-#endif
-};
-
-static int num_ifs;
-
/*
* ethtool operations
*/
@@ -1058,46 +997,59 @@ static const struct net_device_ops au1000_netdev_ops = {
.ndo_change_mtu = eth_change_mtu,
};
-static struct net_device * au1000_probe(int port_num)
+static int __devinit au1000_probe(struct platform_device *pdev)
{
static unsigned version_printed = 0;
struct au1000_private *aup = NULL;
+ struct au1000_eth_platform_data *pd;
struct net_device *dev = NULL;
db_dest_t *pDB, *pDBfree;
- char ethaddr[6];
- int irq, i, err;
- u32 base, macen;
-
- if (port_num >= NUM_ETH_INTERFACES)
- return NULL;
+ int irq, i, err = 0;
+ struct resource *base, *macen;
+ DECLARE_MAC_BUF(ethaddr);
+
+ base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!base) {
+ printk(KERN_ERR DRV_NAME ": failed to retrieve base register\n");
+ err = -ENODEV;
+ goto out;
+ }
- base = CPHYSADDR(iflist[port_num].base_addr );
- macen = CPHYSADDR(iflist[port_num].macen_addr);
- irq = iflist[port_num].irq;
+ macen = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!macen) {
+ printk(KERN_ERR DRV_NAME ": failed to retrieve MAC Enable register\n");
+ err = -ENODEV;
+ goto out;
+ }
- if (!request_mem_region( base, MAC_IOSIZE, "Au1x00 ENET") ||
- !request_mem_region(macen, 4, "Au1x00 ENET"))
- return NULL;
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ printk(KERN_ERR DRV_NAME ": failed to retrieve IRQ\n");
+ err = -ENODEV;
+ goto out;
+ }
- if (version_printed++ == 0)
- printk("%s version %s %s\n", DRV_NAME, DRV_VERSION, DRV_AUTHOR);
+ if (!request_mem_region(base->start, resource_size(base), pdev->name)) {
+ printk(KERN_ERR DRV_NAME ": failed to request memory region for base registers\n");
+ err = -ENXIO;
+ goto out;
+ }
+
+ if (!request_mem_region(macen->start, resource_size(macen), pdev->name)) {
+ printk(KERN_ERR DRV_NAME ": failed to request memory region for MAC enable register\n");
+ err = -ENXIO;
+ goto err_request;
+ }
dev = alloc_etherdev(sizeof(struct au1000_private));
if (!dev) {
printk(KERN_ERR "%s: alloc_etherdev failed\n", DRV_NAME);
- return NULL;
- }
-
- if ((err = register_netdev(dev)) != 0) {
- printk(KERN_ERR "%s: Cannot register net device, error %d\n",
- DRV_NAME, err);
- free_netdev(dev);
- return NULL;
+ err = -ENOMEM;
+ goto err_alloc;
}
- printk("%s: Au1xx0 Ethernet found at 0x%x, irq %d\n",
- dev->name, base, irq);
-
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ platform_set_drvdata(pdev, dev);
aup = netdev_priv(dev);
spin_lock_init(&aup->lock);
@@ -1108,21 +1060,29 @@ static struct net_device * au1000_probe(int port_num)
(NUM_TX_BUFFS + NUM_RX_BUFFS),
&aup->dma_addr, 0);
if (!aup->vaddr) {
- free_netdev(dev);
- release_mem_region( base, MAC_IOSIZE);
- release_mem_region(macen, 4);
- return NULL;
+ printk(KERN_ERR DRV_NAME ": failed to allocate data buffers\n");
+ err = -ENOMEM;
+ goto err_vaddr;
}
/* aup->mac is the base address of the MAC's registers */
- aup->mac = (volatile mac_reg_t *)iflist[port_num].base_addr;
+ aup->mac = (volatile mac_reg_t *)ioremap_nocache(base->start, resource_size(base));
+ if (!aup->mac) {
+ printk(KERN_ERR DRV_NAME ": failed to ioremap MAC registers\n");
+ err = -ENXIO;
+ goto err_remap1;
+ }
- /* Setup some variables for quick register address access */
- aup->enable = (volatile u32 *)iflist[port_num].macen_addr;
- aup->mac_id = port_num;
- au_macs[port_num] = aup;
+ /* Setup some variables for quick register address access */
+ aup->enable = (volatile u32 *)ioremap_nocache(macen->start, resource_size(macen));
+ if (!aup->enable) {
+ printk(KERN_ERR DRV_NAME ": failed to ioremap MAC enable register\n");
+ err = -ENXIO;
+ goto err_remap2;
+ }
+ aup->mac_id = pdev->id;
- if (port_num == 0) {
+ if (pdev->id == 0) {
if (prom_get_ethernet_addr(ethaddr) == 0)
memcpy(au1000_mac_addr, ethaddr, sizeof(au1000_mac_addr));
else {
@@ -1132,7 +1092,7 @@ static struct net_device * au1000_probe(int port_num)
}
setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR);
- } else if (port_num == 1)
+ } else if (pdev->id == 1)
setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR);
/*
@@ -1140,14 +1100,37 @@ static struct net_device * au1000_probe(int port_num)
* to match those that are printed on their stickers
*/
memcpy(dev->dev_addr, au1000_mac_addr, sizeof(au1000_mac_addr));
- dev->dev_addr[5] += port_num;
+ dev->dev_addr[5] += pdev->id;
*aup->enable = 0;
aup->mac_enabled = 0;
+ pd = pdev->dev.platform_data;
+ if (!pd) {
+ printk(KERN_INFO DRV_NAME ": no platform_data passed, PHY search on MAC0\n");
+ aup->phy1_search_mac0 = 1;
+ } else {
+ aup->phy_static_config = pd->phy_static_config;
+ aup->phy_search_highest_addr = pd->phy_search_highest_addr;
+ aup->phy1_search_mac0 = pd->phy1_search_mac0;
+ aup->phy_addr = pd->phy_addr;
+ aup->phy_busid = pd->phy_busid;
+ aup->phy_irq = pd->phy_irq;
+ }
+
+ if (aup->phy_busid && aup->phy_busid > 0) {
+ printk(KERN_ERR DRV_NAME ": MAC0-associated PHY attached 2nd MACs MII"
+ "bus not supported yet\n");
+ err = -ENODEV;
+ goto err_mdiobus_alloc;
+ }
+
aup->mii_bus = mdiobus_alloc();
- if (aup->mii_bus == NULL)
- goto err_out;
+ if (aup->mii_bus == NULL) {
+ printk(KERN_ERR DRV_NAME ": failed to allocate mdiobus structure\n");
+ err = -ENOMEM;
+ goto err_mdiobus_alloc;
+ }
aup->mii_bus->priv = dev;
aup->mii_bus->read = au1000_mdiobus_read;
@@ -1161,23 +1144,19 @@ static struct net_device * au1000_probe(int port_num)
for(i = 0; i < PHY_MAX_ADDR; ++i)
aup->mii_bus->irq[i] = PHY_POLL;
-
/* if known, set corresponding PHY IRQs */
-#if defined(AU1XXX_PHY_STATIC_CONFIG)
-# if defined(AU1XXX_PHY0_IRQ)
- if (AU1XXX_PHY0_BUSID == aup->mac_id)
- aup->mii_bus->irq[AU1XXX_PHY0_ADDR] = AU1XXX_PHY0_IRQ;
-# endif
-# if defined(AU1XXX_PHY1_IRQ)
- if (AU1XXX_PHY1_BUSID == aup->mac_id)
- aup->mii_bus->irq[AU1XXX_PHY1_ADDR] = AU1XXX_PHY1_IRQ;
-# endif
-#endif
- mdiobus_register(aup->mii_bus);
+ if (aup->phy_static_config)
+ if (aup->phy_irq && aup->phy_busid == aup->mac_id)
+ aup->mii_bus->irq[aup->phy_addr] = aup->phy_irq;
+
+ err = mdiobus_register(aup->mii_bus);
+ if (err) {
+ printk(KERN_ERR DRV_NAME " failed to register MDIO bus\n");
+ goto err_mdiobus_reg;
+ }
- if (mii_probe(dev) != 0) {
+ if (mii_probe(dev) != 0)
goto err_out;
- }
pDBfree = NULL;
/* setup the data buffer descriptors and attach a buffer to each one */
@@ -1209,7 +1188,7 @@ static struct net_device * au1000_probe(int port_num)
aup->tx_db_inuse[i] = pDB;
}
- dev->base_addr = base;
+ dev->base_addr = base->start;
dev->irq = irq;
dev->netdev_ops = &au1000_netdev_ops;
SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops);
@@ -1221,14 +1200,24 @@ static struct net_device * au1000_probe(int port_num)
*/
reset_mac(dev);
- return dev;
+ err = register_netdev(dev);
+ if (err) {
+ printk(KERN_ERR DRV_NAME "%s: Cannot register net device, aborting.\n",
+ dev->name);
+ goto err_out;
+ }
+
+ printk("%s: Au1xx0 Ethernet found at 0x%x, irq %d\n",
+ dev->name, base->start, irq);
+ if (version_printed++ == 0)
+ printk("%s version %s %s\n", DRV_NAME, DRV_VERSION, DRV_AUTHOR);
+
+ return 0;
err_out:
- if (aup->mii_bus != NULL) {
+ if (aup->mii_bus != NULL)
mdiobus_unregister(aup->mii_bus);
- mdiobus_free(aup->mii_bus);
- }
-
+
/* here we should have a valid dev plus aup-> register addresses
* so we can reset the mac properly.*/
reset_mac(dev);
@@ -1241,67 +1230,84 @@ err_out:
if (aup->tx_db_inuse[i])
ReleaseDB(aup, aup->tx_db_inuse[i]);
}
+err_mdiobus_reg:
+ mdiobus_free(aup->mii_bus);
+err_mdiobus_alloc:
+ iounmap(aup->enable);
+err_remap2:
+ iounmap(aup->mac);
+err_remap1:
dma_free_noncoherent(NULL, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
(void *)aup->vaddr, aup->dma_addr);
- unregister_netdev(dev);
+err_vaddr:
free_netdev(dev);
- release_mem_region( base, MAC_IOSIZE);
- release_mem_region(macen, 4);
- return NULL;
+err_alloc:
+ release_mem_region(macen->start, resource_size(macen));
+err_request:
+ release_mem_region(base->start, resource_size(base));
+out:
+ return err;
}
-/*
- * Setup the base address and interrupt of the Au1xxx ethernet macs
- * based on cpu type and whether the interface is enabled in sys_pinfunc
- * register. The last interface is enabled if SYS_PF_NI2 (bit 4) is 0.
- */
-static int __init au1000_init_module(void)
+static int __devexit au1000_remove(struct platform_device *pdev)
{
- int ni = (int)((au_readl(SYS_PINFUNC) & (u32)(SYS_PF_NI2)) >> 4);
- struct net_device *dev;
- int i, found_one = 0;
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct au1000_private *aup = netdev_priv(dev);
+ int i;
+ struct resource *base, *macen;
- num_ifs = NUM_ETH_INTERFACES - ni;
+ platform_set_drvdata(pdev, NULL);
+
+ unregister_netdev(dev);
+ mdiobus_unregister(aup->mii_bus);
+ mdiobus_free(aup->mii_bus);
+
+ for (i = 0; i < NUM_RX_DMA; i++)
+ if (aup->rx_db_inuse[i])
+ ReleaseDB(aup, aup->rx_db_inuse[i]);
+
+ for (i = 0; i < NUM_TX_DMA; i++)
+ if (aup->tx_db_inuse[i])
+ ReleaseDB(aup, aup->tx_db_inuse[i]);
+
+ dma_free_noncoherent(NULL, MAX_BUF_SIZE *
+ (NUM_TX_BUFFS + NUM_RX_BUFFS),
+ (void *)aup->vaddr, aup->dma_addr);
+
+ iounmap(aup->mac);
+ iounmap(aup->enable);
+
+ base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(base->start, resource_size(base));
+
+ macen = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ release_mem_region(macen->start, resource_size(macen));
+
+ free_netdev(dev);
- for(i = 0; i < num_ifs; i++) {
- dev = au1000_probe(i);
- iflist[i].dev = dev;
- if (dev)
- found_one++;
- }
- if (!found_one)
- return -ENODEV;
return 0;
}
-static void __exit au1000_cleanup_module(void)
+static struct platform_driver au1000_eth_driver = {
+ .probe = au1000_probe,
+ .remove = __devexit_p(au1000_remove),
+ .driver = {
+ .name = "au1000-eth",
+ .owner = THIS_MODULE,
+ },
+};
+MODULE_ALIAS("platform:au1000-eth");
+
+
+static int __init au1000_init_module(void)
{
- int i, j;
- struct net_device *dev;
- struct au1000_private *aup;
-
- for (i = 0; i < num_ifs; i++) {
- dev = iflist[i].dev;
- if (dev) {
- aup = netdev_priv(dev);
- unregister_netdev(dev);
- mdiobus_unregister(aup->mii_bus);
- mdiobus_free(aup->mii_bus);
- for (j = 0; j < NUM_RX_DMA; j++)
- if (aup->rx_db_inuse[j])
- ReleaseDB(aup, aup->rx_db_inuse[j]);
- for (j = 0; j < NUM_TX_DMA; j++)
- if (aup->tx_db_inuse[j])
- ReleaseDB(aup, aup->tx_db_inuse[j]);
- dma_free_noncoherent(NULL, MAX_BUF_SIZE *
- (NUM_TX_BUFFS + NUM_RX_BUFFS),
- (void *)aup->vaddr, aup->dma_addr);
- release_mem_region(dev->base_addr, MAC_IOSIZE);
- release_mem_region(CPHYSADDR(iflist[i].macen_addr), 4);
- free_netdev(dev);
- }
- }
+ return platform_driver_register(&au1000_eth_driver);
+}
+
+static void __exit au1000_exit_module(void)
+{
+ platform_driver_unregister(&au1000_eth_driver);
}
module_init(au1000_init_module);
-module_exit(au1000_cleanup_module);
+module_exit(au1000_exit_module);
diff --git a/drivers/net/au1000_eth.h b/drivers/net/au1000_eth.h
index 824ecd5..f9d29a2 100644
--- a/drivers/net/au1000_eth.h
+++ b/drivers/net/au1000_eth.h
@@ -108,6 +108,15 @@ struct au1000_private {
struct phy_device *phy_dev;
struct mii_bus *mii_bus;
+ /* PHY configuration */
+ int phy_static_config;
+ int phy_search_highest_addr;
+ int phy1_search_mac0;
+
+ int phy_addr;
+ int phy_busid;
+ int phy_irq;
+
/* These variables are just for quick access to certain regs addresses. */
volatile mac_reg_t *mac; /* mac registers */
volatile u32 *enable; /* address of MAC Enable Register */
^ permalink raw reply related
* [PATCH] net: netlink_getname, packet_getname -- use DECLARE_SOCKADDR guard
From: Cyrill Gorcunov @ 2009-11-08 15:51 UTC (permalink / raw)
To: LNML; +Cc: David Miller, Eric Dumazet
Use guard DECLARE_SOCKADDR in a few more places which allow
us to catch if the structure copied back is too big.
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
---
Please review, comments are welcome!
net/netlink/af_netlink.c | 2 +-
net/packet/af_packet.c | 2 +-
net/unix/af_unix.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
Index: linux-2.6.git/net/netlink/af_netlink.c
=====================================================================
--- linux-2.6.git.orig/net/netlink/af_netlink.c
+++ linux-2.6.git/net/netlink/af_netlink.c
@@ -707,7 +707,7 @@ static int netlink_getname(struct socket
{
struct sock *sk = sock->sk;
struct netlink_sock *nlk = nlk_sk(sk);
- struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr;
+ DECLARE_SOCKADDR(struct sockaddr_nl *, nladdr, addr);
nladdr->nl_family = AF_NETLINK;
nladdr->nl_pad = 0;
Index: linux-2.6.git/net/packet/af_packet.c
=====================================================================
--- linux-2.6.git.orig/net/packet/af_packet.c
+++ linux-2.6.git/net/packet/af_packet.c
@@ -1532,7 +1532,7 @@ static int packet_getname(struct socket
struct net_device *dev;
struct sock *sk = sock->sk;
struct packet_sock *po = pkt_sk(sk);
- struct sockaddr_ll *sll = (struct sockaddr_ll *)uaddr;
+ DECLARE_SOCKADDR(struct sockaddr_ll *, sll, uaddr);
if (peer)
return -EOPNOTSUPP;
Index: linux-2.6.git/net/unix/af_unix.c
=====================================================================
--- linux-2.6.git.orig/net/unix/af_unix.c
+++ linux-2.6.git/net/unix/af_unix.c
@@ -1258,7 +1258,7 @@ static int unix_getname(struct socket *s
{
struct sock *sk = sock->sk;
struct unix_sock *u;
- struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
+ DECLARE_SOCKADDR(struct sockaddr_un *, sunaddr, uaddr);
int err = 0;
if (peer) {
^ permalink raw reply
* [PATCH]Use DMA_BIT_MASK(44) instead of deprecated DMA_44BIT_MASK
From: Marin Mitov @ 2009-11-08 15:59 UTC (permalink / raw)
To: David S. Miller; +Cc: linux-kernel, netdev
Hi all,
Use DMA_BIT_MASK(44) instead of deprecated DMA_44BIT_MASK
Signed-off-by: Marin Mitov <mitov@issp.bas.bg>
=========================================================
--- a/drivers/net/niu.c 2009-11-08 17:09:40.000000000 +0200
+++ b/drivers/net/niu.c 2009-11-08 17:10:31.000000000 +0200
@@ -45,10 +45,6 @@
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
-#ifndef DMA_44BIT_MASK
-#define DMA_44BIT_MASK 0x00000fffffffffffULL
-#endif
-
#ifndef readq
static u64 readq(void __iomem *reg)
{
@@ -9915,7 +9911,7 @@
PCI_EXP_DEVCTL_RELAX_EN);
pci_write_config_word(pdev, pos + PCI_EXP_DEVCTL, val16);
- dma_mask = DMA_44BIT_MASK;
+ dma_mask = DMA_BIT_MASK(44);
err = pci_set_dma_mask(pdev, dma_mask);
if (!err) {
dev->features |= NETIF_F_HIGHDMA;
^ permalink raw reply
* Re: [dm-devel] [PATCH v4 00/12] introduce skip_spaces(), reducing code size plus some clean-ups
From: James Bottomley @ 2009-11-08 16:05 UTC (permalink / raw)
To: device-mapper development
Cc: Pavel Roskin, Stefan Haberland, Jan Kara, linux-cachefs,
Mike Snitzer, Neil Brown, Frederic Weisbecker, Jens Axboe,
Heiko Carstens, James E . J . Bottomley, ibm-acpi-devel, Chr,
Julia Lawall, H . Peter Anvin, Daire Byrne, Alasdair G Kergon,
Greg Banks, Stefan Weinhuber, Eric Sandeen, Adam Belay,
netfilter-devel, Helge Deller, x86, James Morris, Takashi Iwai,
Ingo Molnar, Alan Cox
In-Reply-To: <cover.1257602781.git.andre.goddard@gmail.com>
On Sat, 2009-11-07 at 13:16 -0200, André Goddard Rosa wrote:
> This patch reduces lib.a code size by 173 bytes on my Core 2 with gcc 4.4.1
> even considering that it exports a newly defined function skip_spaces()
> to drivers:
> text data bss dec hex filename
> 64867 840 592 66299 102fb (TOTALS-lib.a-before)
> 64954 584 588 66126 1024e (TOTALS-lib.a-after)
> and implements some code tidy up.
>
> Besides reducing lib.a size, it converts many in-tree drivers to use the
> newly defined function, which makes another small reduction on kernel size
> overall when those drivers are used.
Before we embark on something as massive as this, could we take a step
back. I agree that if I were coming up with the strstip() interface
today I probably wouldn't have given it two overloaded uses.
However, I think the function, in spite of this minor issue, is very
usable. I still don't understand why people thought adding a
__must_check, which is what damaged one of the overloaded uses, is a
good idea.
Assuming there's a good answer to the above:
> + * skip_spaces - Removes leading whitespace from @s.
> + * @s: The string to be stripped.
> + *
> + * Returns a pointer to the first non-whitespace character in @s.
> + */
> +const char *skip_spaces(const char *str)
I don't think const return is a good idea because most functions will be
manipulating the string and using pointers that won't be const, so this
will generate a ton of 'initialization discards qualifiers from pointer
target type' ... so that leads to the question of whether this patch
series was actually compiled ...
James
^ permalink raw reply
* Re: [PATCH v4 10/12] string: factorize skip_spaces and export it to be generally available
From: Alan Cox @ 2009-11-08 16:50 UTC (permalink / raw)
To: André Goddard Rosa
Cc: Andreas Dilger, Mike Snitzer, Takashi Iwai, Kysela,
Stefan Weinhuber, Eric Sandeen, James E . J . Bottomley,
linux-cachefs, WANG Cong, Len Brown, Trond Myklebust,
Rusty Russell, netfilter, Al Viro, Thomas Gleixner, Engelhardt,
Bjorn Helgaas, Martin K . Petersen, linux-kernel, Stoyan Gaydarov,
Kyle McMartin, netfilter-devel, Joe Perches, Andrew Morton
In-Reply-To: <c7d3b02b5e28eaa54a5360d57dfd177c44320187.1257602781.git.andre.goddard@gmail.com>
On Sat, 7 Nov 2009 13:16:18 -0200
André Goddard Rosa <andre.goddard@gmail.com> wrote:
> On the following sentence:
> while (*s && isspace(*s))
> s++;
Looks fine but for one thing: it's actually shorter inline than moved
into /lib so at the very least it should be a header inline not a
function call.
Second minor comment. Although it never made it into the final ANSI C,
the proposed name (and the one used in a lot of other non Linux code for
this) is stpblk().
Alan
^ permalink raw reply
* Re: [dm-devel] [PATCH v4 00/12] introduce skip_spaces(), reducing code size plus some clean-ups
From: André Goddard Rosa @ 2009-11-08 16:52 UTC (permalink / raw)
To: James Bottomley
Cc: Pavel Roskin, Stefan Haberland, Jan Kara, linux-cachefs,
Mike Snitzer, Neil Brown, Frederic Weisbecker, Jens Axboe,
Heiko Carstens, James E . J . Bottomley, ibm-acpi-devel,
device-mapper development, Julia Lawall, H . Peter Anvin,
Daire Byrne, Alan Cox, Greg Banks, Stefan Weinhuber, Eric Sandeen,
Adam Belay, netfilter-devel, Helge Deller, x86, James Morris,
Takashi Iwai, Ing
In-Reply-To: <1257696303.4184.8.camel@mulgrave.site>
Hi, James!
On Sun, Nov 8, 2009 at 2:05 PM, James Bottomley
<James.Bottomley@hansenpartnership.com> wrote:
> On Sat, 2009-11-07 at 13:16 -0200, André Goddard Rosa wrote:
>> This patch reduces lib.a code size by 173 bytes on my Core 2 with gcc 4.4.1
>> even considering that it exports a newly defined function skip_spaces()
>> to drivers:
>> text data bss dec hex filename
>> 64867 840 592 66299 102fb (TOTALS-lib.a-before)
>> 64954 584 588 66126 1024e (TOTALS-lib.a-after)
>> and implements some code tidy up.
>>
>> Besides reducing lib.a size, it converts many in-tree drivers to use the
>> newly defined function, which makes another small reduction on kernel size
>> overall when those drivers are used.
>
> Before we embark on something as massive as this, could we take a step
> back. I agree that if I were coming up with the strstip() interface
> today I probably wouldn't have given it two overloaded uses.
>
> However, I think the function, in spite of this minor issue, is very
> usable. I still don't understand why people thought adding a
> __must_check, which is what damaged one of the overloaded uses, is a
> good idea.
Differently of "static void strip(char *str)"@scripts/kconfig/conf.c ,
this function
does not moves the characters to the beginning of the string, so that if that
string is going to be reused it should refer to the newly returned string start.
I've changed it to remove the const and return a "char *".
Do you think __must_check is not needed as well?
Thanks,
André
^ permalink raw reply
* Re: [PATCH v4 12/12] tree-wide: convert open calls to remove spaces to skip_spaces() lib function
From: Theodore Tso @ 2009-11-08 18:47 UTC (permalink / raw)
To: André Goddard Rosa
Cc: Pavel Roskin, Stefan Haberland, Jan Kara, linux-cachefs,
Mike Snitzer, Neil Brown, Frederic Weisbecker, Jens Axboe,
Heiko Carstens, James E . J . Bottomley, ibm-acpi-devel, dm-devel,
Julia Lawall, H . Peter Anvin, Daire Byrne, Alasdair G Kergon,
Greg Banks, Stefan Weinhuber, Eric Sandeen, Adam Belay,
Helge Deller, x86, James Morris, Takashi Iwai, Ingo Molnar,
Alan Cox
In-Reply-To: <7d5883637aa976b54e944998f635d47a41618a75.1257602781.git.andre.goddard@gmail.com>
On Sat, Nov 07, 2009 at 01:16:20PM -0200, André Goddard Rosa wrote:
> Makes use of skip_spaces() defined in lib/string.c for removing leading
> spaces from strings all over the tree.
>
> Also, while at it, if we see (*str && isspace(*str)), we can be sure to
> remove the first condition (*str) as the second one (isspace(*str)) also
> evaluates to 0 whenever *str == 0, making it redundant. In other words,
> "a char equals zero is never a space".
There are a number of places that have the pattern of skipping
whitespace, calling simpler_strtoul(), and then skipping whitespace
afterwards. And thinkpad_acpi.c and fs/ext4/super.c both have an
indentical function, parse_strotul(), which basically does this plus
doing actual error checking (a number of callers of simple_strtoul
aren't checking to see if the user passed in a valid number or not,
boo.)
I would suggest that we should lift parse_strtoul() into lib/, both to
save a bit of code, as well as encouraging people to do proper input
validation, while we are doing this tree-wide cleanup.
- Ted
^ permalink raw reply
* Re: [PATCHv7 3/3] vhost_net: a kernel-level virtio server
From: Paul E. McKenney @ 2009-11-08 19:36 UTC (permalink / raw)
To: Rusty Russell
Cc: Michael S. Tsirkin, Gregory Haskins, Eric Dumazet, netdev,
virtualization, kvm, linux-kernel, mingo, linux-mm, akpm, hpa,
s.hetze
In-Reply-To: <200911081439.59770.rusty@rustcorp.com.au>
On Sun, Nov 08, 2009 at 02:39:59PM +1030, Rusty Russell wrote:
> On Sat, 7 Nov 2009 03:00:07 am Paul E. McKenney wrote:
> > On Fri, Nov 06, 2009 at 03:31:20PM +1030, Rusty Russell wrote:
> > > But it's still nasty to use half an API. If it were a few places I would
> > > have open-coded it with a comment, or wrapped it. As it is, I don't think
> > > that would be a win.
> >
> > So would it help to have a rcu_read_lock_workqueue() and
> > rcu_read_unlock_workqueue() that checked nesting and whether they were
> > actually running in the context of a workqueue item? Or did you have
> > something else in mind? Or am I misjudging the level of sarcasm in
> > your reply? ;-)
>
> You read correctly. If we get a second user, creating an API makes sense.
Makes sense to me as well. Which does provide some time to come up with
a primitive designed to answer the question "Am I currently executing in
the context of a workqueue item?". ;-)
Thanx, Paul
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* [PATCH 0/8 net-next-2.6] udp: optimisations
From: Eric Dumazet @ 2009-11-08 20:16 UTC (permalink / raw)
To: David S. Miller
Cc: Linux Netdev List, Lucian Adrian Grijincu, Octavian Purdila
This patch series address UDP scalability problems, we failed to solve in 2007
(commit 6aaf47fa48d3c44 INET : IPV4 UDP lookups converted to a 2 pass algo)
we had to revert a bit later.
One of the problem of UDP is its use of a single hash table, with
a key based on local port value only. When many IP addresses are used,
it is possible to have a chain with very large number N of sockets,
lookup time being N/2 in average.
Size of hash table has no effect on this, since all sockets are
chained in one particular slot.
It seems Lucian Adrian Grijincu & Octavian Purdila from IXIACOM have
real workloads hitting hard this problem and posted a preliminary
patch/RFC, using a second hash, but based on (local address).
I took part of Lucian ideas and my previous patches, and cooked
a clean upgrade path.
With following patches, we might handle 1.000.000+ udp sockets
in linux without major slowdown, and no penalty for common cases.
Thanks
[PATCH 1/8] udp: add a counter into udp_hslot
Adds a counter in udp_hslot to keep an accurate count
of sockets present in chain.
This will permit to upcoming UDP lookup algo to chose
the shortest chain when secondary hash is added.
[PATCH 2/8] udp: split sk_hash into two u16 hashes
nion sk_hash with two u16 hashes for udp (no extra memory taken)
One 16 bits hash on (local port) value (the previous udp 'hash')
One 16 bits hash on (local address, local port) values, initialized
but not yet used. This second hash is using jenkin hash for better
distribution.
Because the 'port' is xored later, a partial hash is performed
on local address + net_hash_mix(net)
[PATCH 3/8] udp: secondary hash on (local port, local address)
Extends udp_table to contain a secondary hash table.
socket anchor for this second hash is free, because UDP
doesnt use skc_bind_node : We define an union to hold
both skc_bind_node & a new hlist_nulls_node udp_portaddr_node
udp_lib_get_port() inserts sockets into second hash chain
(additional cost of one atomic op)
udp_lib_unhash() deletes socket from second hash chain
(additional cost of one atomic op)
Note : No special lockdep annotation is needed, because
lock for the secondary hash chain is always get after
lock for primary hash chain.
[PATCH 4/8] ipv4: udp: optimize unicast RX path
We first locate the (local port) hash chain head
If few sockets are in this chain, we proceed with previous lookup algo.
If too many sockets are listed, we take a look at the secondary
(port, address) hash chain.
We choose the shortest chain and proceed with a RCU lookup on the elected chain.
But, if we chose (port, address) chain, and fail to find a socket on given address,
we must try another lookup on (port, INADDR_ANY) chain to find sockets not bound
to a particular IP.
-> No extra cost for typical setups, where the first lookup will probabbly
be performed.
RCU lookups everywhere, we dont acquire spinlock.
[PATCH 5/8] ipv6: udp: optimize unicast RX path
Same algo than patch 4, but for ipv6
[PATCH 6/8] ipv4: udp: Optimise multicast reception
UDP multicast rx path is a bit complex and can hold a spinlock
for a long time.
Using a small (32 or 64 entries) stack of socket pointers can help
to perform expensive operations (skb_clone(), udp_queue_rcv_skb())
outside of the lock, in most cases.
It's also a base for a future RCU conversion of multicast recption.
[PATCH 7/8] ipv6: udp: Optimise multicast reception
Same optimisation, but for ipv6
[PATCH 8/8] udp: multicast RX should increment SNMP/sk_drops counter in allocation failures
When skb_clone() fails, we should increment sk_drops and SNMP counters.
This fix is not urgent and better done after previous patches.
-------------------------------------------------------------------------------------
Furthers patches could be :
udp: bind() optimisations
udp: multicast uses of secondary hash
udp: multicast path uses RCU
^ permalink raw reply
* [PATCH 1/8] udp: add a counter into udp_hslot
From: Eric Dumazet @ 2009-11-08 20:17 UTC (permalink / raw)
To: David S. Miller
Cc: Linux Netdev List, Lucian Adrian Grijincu, Octavian Purdila
Adds a counter in udp_hslot to keep an accurate count
of sockets present in chain.
This will permit to upcoming UDP lookup algo to chose
the shortest chain when secondary hash is added.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
include/net/udp.h | 8 ++++++++
net/ipv4/udp.c | 3 +++
2 files changed, 11 insertions(+)
diff --git a/include/net/udp.h b/include/net/udp.h
index 22aa2e7..9167281 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -50,8 +50,16 @@ struct udp_skb_cb {
};
#define UDP_SKB_CB(__skb) ((struct udp_skb_cb *)((__skb)->cb))
+/**
+ * struct udp_hslot - UDP hash slot
+ *
+ * @head: head of list of sockets
+ * @count: number of sockets in 'head' list
+ * @lock: spinlock protecting changes to head/count
+ */
struct udp_hslot {
struct hlist_nulls_head head;
+ int count;
spinlock_t lock;
} __attribute__((aligned(2 * sizeof(long))));
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index d5e75e9..ffc8376 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -218,6 +218,7 @@ found:
sk->sk_hash = snum;
if (sk_unhashed(sk)) {
sk_nulls_add_node_rcu(sk, &hslot->head);
+ hslot->count++;
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
}
error = 0;
@@ -1053,6 +1054,7 @@ void udp_lib_unhash(struct sock *sk)
spin_lock_bh(&hslot->lock);
if (sk_nulls_del_node_init_rcu(sk)) {
+ hslot->count--;
inet_sk(sk)->inet_num = 0;
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
}
@@ -1862,6 +1864,7 @@ void __init udp_table_init(struct udp_table *table, const char *name)
}
for (i = 0; i <= table->mask; i++) {
INIT_HLIST_NULLS_HEAD(&table->hash[i].head, i);
+ table->hash[i].count = 0;
spin_lock_init(&table->hash[i].lock);
}
}
^ permalink raw reply related
* [PATCH 2/8] udp: split sk_hash into two u16 hashes
From: Eric Dumazet @ 2009-11-08 20:17 UTC (permalink / raw)
To: David S. Miller
Cc: Linux Netdev List, Lucian Adrian Grijincu, Octavian Purdila
Union sk_hash with two u16 hashes for udp (no extra memory taken)
One 16 bits hash on (local port) value (the previous udp 'hash')
One 16 bits hash on (local address, local port) values, initialized
but not yet used. This second hash is using jenkin hash for better
distribution.
Because the 'port' is xored later, a partial hash is performed
on local address + net_hash_mix(net)
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
include/linux/udp.h | 2 ++
include/net/sock.h | 6 +++++-
net/ipv4/udp.c | 25 +++++++++++++++++++------
net/ipv6/udp.c | 27 +++++++++++++++++++++++++--
4 files changed, 51 insertions(+), 9 deletions(-)
diff --git a/include/linux/udp.h b/include/linux/udp.h
index 832361e..5b4b527 100644
--- a/include/linux/udp.h
+++ b/include/linux/udp.h
@@ -55,6 +55,8 @@ static inline int udp_hashfn(struct net *net, unsigned num, unsigned mask)
struct udp_sock {
/* inet_sock has to be the first member */
struct inet_sock inet;
+#define udp_port_hash inet.sk.__sk_common.skc_u16hashes[0]
+#define udp_portaddr_hash inet.sk.__sk_common.skc_u16hashes[1]
int pending; /* Any pending frames ? */
unsigned int corkflag; /* Cork is required */
__u16 encap_type; /* Is this an Encapsulation socket? */
diff --git a/include/net/sock.h b/include/net/sock.h
index 55de3bd..827366b 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -109,6 +109,7 @@ struct net;
* @skc_refcnt: reference count
* @skc_tx_queue_mapping: tx queue number for this connection
* @skc_hash: hash value used with various protocol lookup tables
+ * @skc_u16hashes: two u16 hash values used by UDP lookup tables
* @skc_family: network address family
* @skc_state: Connection state
* @skc_reuse: %SO_REUSEADDR setting
@@ -131,7 +132,10 @@ struct sock_common {
atomic_t skc_refcnt;
int skc_tx_queue_mapping;
- unsigned int skc_hash;
+ union {
+ unsigned int skc_hash;
+ __u16 skc_u16hashes[2];
+ };
unsigned short skc_family;
volatile unsigned char skc_state;
unsigned char skc_reuse;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index ffc8376..af72de1 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -138,13 +138,14 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num,
sk_nulls_for_each(sk2, node, &hslot->head)
if (net_eq(sock_net(sk2), net) &&
sk2 != sk &&
- (bitmap || sk2->sk_hash == num) &&
+ (bitmap || udp_sk(sk2)->udp_port_hash == num) &&
(!sk2->sk_reuse || !sk->sk_reuse) &&
(!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if
|| sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
(*saddr_comp)(sk, sk2)) {
if (bitmap)
- __set_bit(sk2->sk_hash >> log, bitmap);
+ __set_bit(udp_sk(sk2)->udp_port_hash >> log,
+ bitmap);
else
return 1;
}
@@ -215,7 +216,8 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum,
}
found:
inet_sk(sk)->inet_num = snum;
- sk->sk_hash = snum;
+ udp_sk(sk)->udp_port_hash = snum;
+ udp_sk(sk)->udp_portaddr_hash ^= snum;
if (sk_unhashed(sk)) {
sk_nulls_add_node_rcu(sk, &hslot->head);
hslot->count++;
@@ -238,8 +240,19 @@ static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2)
inet1->inet_rcv_saddr == inet2->inet_rcv_saddr));
}
+static unsigned int udp4_portaddr_hash(struct net *net, __be32 saddr,
+ unsigned int port)
+{
+ return jhash_1word(saddr, net_hash_mix(net)) ^ port;
+}
+
int udp_v4_get_port(struct sock *sk, unsigned short snum)
{
+ /* precompute partial secondary hash */
+ udp_sk(sk)->udp_portaddr_hash =
+ udp4_portaddr_hash(sock_net(sk),
+ inet_sk(sk)->inet_rcv_saddr,
+ 0);
return udp_lib_get_port(sk, snum, ipv4_rcv_saddr_equal);
}
@@ -249,7 +262,7 @@ static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr,
{
int score = -1;
- if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum &&
+ if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum &&
!ipv6_only_sock(sk)) {
struct inet_sock *inet = inet_sk(sk);
@@ -360,7 +373,7 @@ static inline struct sock *udp_v4_mcast_next(struct net *net, struct sock *sk,
struct inet_sock *inet = inet_sk(s);
if (!net_eq(sock_net(s), net) ||
- s->sk_hash != hnum ||
+ udp_sk(s)->udp_port_hash != hnum ||
(inet->inet_daddr && inet->inet_daddr != rmt_addr) ||
(inet->inet_dport != rmt_port && inet->inet_dport) ||
(inet->inet_rcv_saddr &&
@@ -1050,7 +1063,7 @@ void udp_lib_unhash(struct sock *sk)
if (sk_hashed(sk)) {
struct udp_table *udptable = sk->sk_prot->h.udp_table;
struct udp_hslot *hslot = udp_hashslot(udptable, sock_net(sk),
- sk->sk_hash);
+ udp_sk(sk)->udp_port_hash);
spin_lock_bh(&hslot->lock);
if (sk_nulls_del_node_init_rcu(sk)) {
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 5bc7cdb..1e5fadd 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -81,8 +81,30 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
return 0;
}
+static unsigned int udp6_portaddr_hash(struct net *net,
+ const struct in6_addr *addr6,
+ unsigned int port)
+{
+ unsigned int hash, mix = net_hash_mix(net);
+
+ if (ipv6_addr_any(addr6))
+ hash = jhash_1word(0, mix);
+ else if (ipv6_addr_type(addr6) == IPV6_ADDR_MAPPED)
+ hash = jhash_1word(addr6->s6_addr32[3], mix);
+ else
+ hash = jhash2(addr6->s6_addr32, 4, mix);
+
+ return hash ^ port;
+}
+
+
int udp_v6_get_port(struct sock *sk, unsigned short snum)
{
+ /* precompute partial secondary hash */
+ udp_sk(sk)->udp_portaddr_hash =
+ udp6_portaddr_hash(sock_net(sk),
+ &inet6_sk(sk)->rcv_saddr,
+ 0);
return udp_lib_get_port(sk, snum, ipv6_rcv_saddr_equal);
}
@@ -94,7 +116,7 @@ static inline int compute_score(struct sock *sk, struct net *net,
{
int score = -1;
- if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum &&
+ if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum &&
sk->sk_family == PF_INET6) {
struct ipv6_pinfo *np = inet6_sk(sk);
struct inet_sock *inet = inet_sk(sk);
@@ -415,7 +437,8 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk,
if (!net_eq(sock_net(s), net))
continue;
- if (s->sk_hash == num && s->sk_family == PF_INET6) {
+ if (udp_sk(s)->udp_port_hash == num &&
+ s->sk_family == PF_INET6) {
struct ipv6_pinfo *np = inet6_sk(s);
if (inet->inet_dport) {
if (inet->inet_dport != rmt_port)
^ permalink raw reply related
* [PATCH 3/8] udp: secondary hash on (local port, local address)
From: Eric Dumazet @ 2009-11-08 20:17 UTC (permalink / raw)
To: David S. Miller
Cc: Linux Netdev List, Lucian Adrian Grijincu, Octavian Purdila
Extends udp_table to contain a secondary hash table.
socket anchor for this second hash is free, because UDP
doesnt use skc_bind_node : We define an union to hold
both skc_bind_node & a new hlist_nulls_node udp_portaddr_node
udp_lib_get_port() inserts sockets into second hash chain
(additional cost of one atomic op)
udp_lib_unhash() deletes socket from second hash chain
(additional cost of one atomic op)
Note : No spinlock lockdep annotation is needed, because
lock for the secondary hash chain is always get after
lock for primary hash chain.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
include/linux/udp.h | 1 +
include/net/sock.h | 8 ++++++--
include/net/udp.h | 22 ++++++++++++++++++++--
net/ipv4/udp.c | 31 ++++++++++++++++++++++++++-----
4 files changed, 53 insertions(+), 9 deletions(-)
diff --git a/include/linux/udp.h b/include/linux/udp.h
index 5b4b527..59f0ddf 100644
--- a/include/linux/udp.h
+++ b/include/linux/udp.h
@@ -57,6 +57,7 @@ struct udp_sock {
struct inet_sock inet;
#define udp_port_hash inet.sk.__sk_common.skc_u16hashes[0]
#define udp_portaddr_hash inet.sk.__sk_common.skc_u16hashes[1]
+#define udp_portaddr_node inet.sk.__sk_common.skc_portaddr_node
int pending; /* Any pending frames ? */
unsigned int corkflag; /* Cork is required */
__u16 encap_type; /* Is this an Encapsulation socket? */
diff --git a/include/net/sock.h b/include/net/sock.h
index 827366b..3f1a480 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -105,7 +105,7 @@ struct net;
/**
* struct sock_common - minimal network layer representation of sockets
* @skc_node: main hash linkage for various protocol lookup tables
- * @skc_nulls_node: main hash linkage for UDP/UDP-Lite protocol
+ * @skc_nulls_node: main hash linkage for TCP/UDP/UDP-Lite protocol
* @skc_refcnt: reference count
* @skc_tx_queue_mapping: tx queue number for this connection
* @skc_hash: hash value used with various protocol lookup tables
@@ -115,6 +115,7 @@ struct net;
* @skc_reuse: %SO_REUSEADDR setting
* @skc_bound_dev_if: bound device index if != 0
* @skc_bind_node: bind hash linkage for various protocol lookup tables
+ * @skc_portaddr_node: second hash linkage for UDP/UDP-Lite protocol
* @skc_prot: protocol handlers inside a network family
* @skc_net: reference to the network namespace of this socket
*
@@ -140,7 +141,10 @@ struct sock_common {
volatile unsigned char skc_state;
unsigned char skc_reuse;
int skc_bound_dev_if;
- struct hlist_node skc_bind_node;
+ union {
+ struct hlist_node skc_bind_node;
+ struct hlist_nulls_node skc_portaddr_node;
+ };
struct proto *skc_prot;
#ifdef CONFIG_NET_NS
struct net *skc_net;
diff --git a/include/net/udp.h b/include/net/udp.h
index 9167281..af41850 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -63,10 +63,19 @@ struct udp_hslot {
spinlock_t lock;
} __attribute__((aligned(2 * sizeof(long))));
+/**
+ * struct udp_table - UDP table
+ *
+ * @hash: hash table, sockets are hashed on (local port)
+ * @hash2: hash table, sockets are hashed on (local port, local address)
+ * @mask: number of slots in hash tables, minus 1
+ * @log: log2(number of slots in hash table)
+ */
struct udp_table {
struct udp_hslot *hash;
- unsigned int mask;
- unsigned int log;
+ struct udp_hslot *hash2;
+ unsigned int mask;
+ unsigned int log;
};
extern struct udp_table udp_table;
extern void udp_table_init(struct udp_table *, const char *);
@@ -75,6 +84,15 @@ static inline struct udp_hslot *udp_hashslot(struct udp_table *table,
{
return &table->hash[udp_hashfn(net, num, table->mask)];
}
+/*
+ * For secondary hash, net_hash_mix() is performed before calling
+ * udp_hashslot2(), this explains difference with udp_hashslot()
+ */
+static inline struct udp_hslot *udp_hashslot2(struct udp_table *table,
+ unsigned int hash)
+{
+ return &table->hash2[hash & table->mask];
+}
/* Note: this must match 'valbool' in sock_setsockopt */
#define UDP_CSUM_NOXMIT 1
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index af72de1..5f04216 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -163,7 +163,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum,
int (*saddr_comp)(const struct sock *sk1,
const struct sock *sk2))
{
- struct udp_hslot *hslot;
+ struct udp_hslot *hslot, *hslot2;
struct udp_table *udptable = sk->sk_prot->h.udp_table;
int error = 1;
struct net *net = sock_net(sk);
@@ -222,6 +222,13 @@ found:
sk_nulls_add_node_rcu(sk, &hslot->head);
hslot->count++;
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+
+ hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);
+ spin_lock(&hslot2->lock);
+ hlist_nulls_add_head_rcu(&udp_sk(sk)->udp_portaddr_node,
+ &hslot2->head);
+ hslot2->count++;
+ spin_unlock(&hslot2->lock);
}
error = 0;
fail_unlock:
@@ -1062,14 +1069,22 @@ void udp_lib_unhash(struct sock *sk)
{
if (sk_hashed(sk)) {
struct udp_table *udptable = sk->sk_prot->h.udp_table;
- struct udp_hslot *hslot = udp_hashslot(udptable, sock_net(sk),
- udp_sk(sk)->udp_port_hash);
+ struct udp_hslot *hslot, *hslot2;
+
+ hslot = udp_hashslot(udptable, sock_net(sk),
+ udp_sk(sk)->udp_port_hash);
+ hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);
spin_lock_bh(&hslot->lock);
if (sk_nulls_del_node_init_rcu(sk)) {
hslot->count--;
inet_sk(sk)->inet_num = 0;
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+
+ spin_lock(&hslot2->lock);
+ hlist_nulls_del_init_rcu(&udp_sk(sk)->udp_portaddr_node);
+ hslot2->count--;
+ spin_unlock(&hslot2->lock);
}
spin_unlock_bh(&hslot->lock);
}
@@ -1857,7 +1872,7 @@ void __init udp_table_init(struct udp_table *table, const char *name)
if (!CONFIG_BASE_SMALL)
table->hash = alloc_large_system_hash(name,
- sizeof(struct udp_hslot),
+ 2 * sizeof(struct udp_hslot),
uhash_entries,
21, /* one slot per 2 MB */
0,
@@ -1869,17 +1884,23 @@ void __init udp_table_init(struct udp_table *table, const char *name)
*/
if (CONFIG_BASE_SMALL || table->mask < UDP_HTABLE_SIZE_MIN - 1) {
table->hash = kmalloc(UDP_HTABLE_SIZE_MIN *
- sizeof(struct udp_hslot), GFP_KERNEL);
+ 2 * sizeof(struct udp_hslot), GFP_KERNEL);
if (!table->hash)
panic(name);
table->log = ilog2(UDP_HTABLE_SIZE_MIN);
table->mask = UDP_HTABLE_SIZE_MIN - 1;
}
+ table->hash2 = table->hash + (table->mask + 1);
for (i = 0; i <= table->mask; i++) {
INIT_HLIST_NULLS_HEAD(&table->hash[i].head, i);
table->hash[i].count = 0;
spin_lock_init(&table->hash[i].lock);
}
+ for (i = 0; i <= table->mask; i++) {
+ INIT_HLIST_NULLS_HEAD(&table->hash2[i].head, i);
+ table->hash2[i].count = 0;
+ spin_lock_init(&table->hash2[i].lock);
+ }
}
void __init udp_init(void)
^ permalink raw reply related
* [PATCH 4/8] ipv4: udp: optimize unicast RX path
From: Eric Dumazet @ 2009-11-08 20:18 UTC (permalink / raw)
To: David S. Miller
Cc: Linux Netdev List, Lucian Adrian Grijincu, Octavian Purdila
We first locate the (local port) hash chain head
If few sockets are in this chain, we proceed with previous lookup algo.
If too many sockets are listed, we take a look at the secondary
(port, address) hash chain we added in previous patch.
We choose the shortest chain and proceed with a RCU lookup on the elected chain.
But, if we chose (port, address) chain, and fail to find a socket on given address,
we must try another lookup on (port, INADDR_ANY) chain to find socket not bound
to a particular IP.
-> No extra cost for typical setups, where the first lookup will probabbly
be performed.
RCU lookups everywhere, we dont acquire spinlock.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
net/ipv4/udp.c | 115 +++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 112 insertions(+), 3 deletions(-)
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 5f04216..dd7f3d2 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -298,6 +298,91 @@ static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr,
return score;
}
+/*
+ * In this second variant, we check (daddr, dport) matches (inet_rcv_sadd, inet_num)
+ */
+#define SCORE2_MAX (1 + 2 + 2 + 2)
+static inline int compute_score2(struct sock *sk, struct net *net,
+ __be32 saddr, __be16 sport,
+ __be32 daddr, unsigned int hnum, int dif)
+{
+ int score = -1;
+
+ if (net_eq(sock_net(sk), net) && !ipv6_only_sock(sk)) {
+ struct inet_sock *inet = inet_sk(sk);
+
+ if (inet->inet_rcv_saddr != daddr)
+ return -1;
+ if (inet->inet_num != hnum)
+ return -1;
+
+ score = (sk->sk_family == PF_INET ? 1 : 0);
+ if (inet->inet_daddr) {
+ if (inet->inet_daddr != saddr)
+ return -1;
+ score += 2;
+ }
+ if (inet->inet_dport) {
+ if (inet->inet_dport != sport)
+ return -1;
+ score += 2;
+ }
+ if (sk->sk_bound_dev_if) {
+ if (sk->sk_bound_dev_if != dif)
+ return -1;
+ score += 2;
+ }
+ }
+ return score;
+}
+
+#define udp_portaddr_for_each_entry_rcu(__sk, node, list) \
+ hlist_nulls_for_each_entry_rcu(__sk, node, list, __sk_common.skc_portaddr_node)
+
+/* called with read_rcu_lock() */
+static struct sock *udp4_lib_lookup2(struct net *net,
+ __be32 saddr, __be16 sport,
+ __be32 daddr, unsigned int hnum, int dif,
+ struct udp_hslot *hslot2, unsigned int slot2)
+{
+ struct sock *sk, *result;
+ struct hlist_nulls_node *node;
+ int score, badness;
+
+begin:
+ result = NULL;
+ badness = -1;
+ udp_portaddr_for_each_entry_rcu(sk, node, &hslot2->head) {
+ score = compute_score2(sk, net, saddr, sport,
+ daddr, hnum, dif);
+ if (score > badness) {
+ result = sk;
+ badness = score;
+ if (score == SCORE2_MAX)
+ goto exact_match;
+ }
+ }
+ /*
+ * if the nulls value we got at the end of this lookup is
+ * not the expected one, we must restart lookup.
+ * We probably met an item that was moved to another chain.
+ */
+ if (get_nulls_value(node) != slot2)
+ goto begin;
+
+ if (result) {
+exact_match:
+ if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt)))
+ result = NULL;
+ else if (unlikely(compute_score2(result, net, saddr, sport,
+ daddr, hnum, dif) < badness)) {
+ sock_put(result);
+ goto begin;
+ }
+ }
+ return result;
+}
+
/* UDP is nearly always wildcards out the wazoo, it makes no sense to try
* harder than this. -DaveM
*/
@@ -308,11 +393,35 @@ static struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
struct sock *sk, *result;
struct hlist_nulls_node *node;
unsigned short hnum = ntohs(dport);
- unsigned int hash = udp_hashfn(net, hnum, udptable->mask);
- struct udp_hslot *hslot = &udptable->hash[hash];
+ unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask);
+ struct udp_hslot *hslot2, *hslot = &udptable->hash[slot];
int score, badness;
rcu_read_lock();
+ if (hslot->count > 10) {
+ hash2 = udp4_portaddr_hash(net, daddr, hnum);
+ slot2 = hash2 & udptable->mask;
+ hslot2 = &udptable->hash2[slot2];
+ if (hslot->count < hslot2->count)
+ goto begin;
+
+ result = udp4_lib_lookup2(net, saddr, sport,
+ daddr, hnum, dif,
+ hslot2, slot2);
+ if (!result) {
+ hash2 = udp4_portaddr_hash(net, INADDR_ANY, hnum);
+ slot2 = hash2 & udptable->mask;
+ hslot2 = &udptable->hash2[slot2];
+ if (hslot->count < hslot2->count)
+ goto begin;
+
+ result = udp4_lib_lookup2(net, INADDR_ANY, sport,
+ daddr, hnum, dif,
+ hslot2, slot2);
+ }
+ rcu_read_unlock();
+ return result;
+ }
begin:
result = NULL;
badness = -1;
@@ -329,7 +438,7 @@ begin:
* not the expected one, we must restart lookup.
* We probably met an item that was moved to another chain.
*/
- if (get_nulls_value(node) != hash)
+ if (get_nulls_value(node) != slot)
goto begin;
if (result) {
^ permalink raw reply related
* [PATCH 5/8] ipv6: udp: optimize unicast RX path
From: Eric Dumazet @ 2009-11-08 20:18 UTC (permalink / raw)
To: David S. Miller
Cc: Linux Netdev List, Lucian Adrian Grijincu, Octavian Purdila
We first locate the (local port) hash chain head
If few sockets are in this chain, we proceed with previous lookup algo.
If too many sockets are listed, we take a look at the secondary
(port, address) hash chain.
We choose the shortest chain and proceed with a RCU lookup on the elected chain.
But, if we chose (port, address) chain, and fail to find a socket on given address,
we must try another lookup on (port, in6addr_any) chain to find sockets not bound
to a particular IP.
-> No extra cost for typical setups, where the first lookup will probabbly
be performed.
RCU lookups everywhere, we dont acquire spinlock.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
net/ipv6/udp.c | 112 +++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 109 insertions(+), 3 deletions(-)
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 1e5fadd..c5b0451 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -146,6 +146,88 @@ static inline int compute_score(struct sock *sk, struct net *net,
return score;
}
+#define SCORE2_MAX (1 + 1 + 1)
+static inline int compute_score2(struct sock *sk, struct net *net,
+ const struct in6_addr *saddr, __be16 sport,
+ const struct in6_addr *daddr, unsigned short hnum,
+ int dif)
+{
+ int score = -1;
+
+ if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum &&
+ sk->sk_family == PF_INET6) {
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
+
+ if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
+ return -1;
+ score = 0;
+ if (inet->inet_dport) {
+ if (inet->inet_dport != sport)
+ return -1;
+ score++;
+ }
+ if (!ipv6_addr_any(&np->daddr)) {
+ if (!ipv6_addr_equal(&np->daddr, saddr))
+ return -1;
+ score++;
+ }
+ if (sk->sk_bound_dev_if) {
+ if (sk->sk_bound_dev_if != dif)
+ return -1;
+ score++;
+ }
+ }
+ return score;
+}
+
+#define udp_portaddr_for_each_entry_rcu(__sk, node, list) \
+ hlist_nulls_for_each_entry_rcu(__sk, node, list, __sk_common.skc_portaddr_node)
+
+/* called with read_rcu_lock() */
+static struct sock *udp6_lib_lookup2(struct net *net,
+ const struct in6_addr *saddr, __be16 sport,
+ const struct in6_addr *daddr, unsigned int hnum, int dif,
+ struct udp_hslot *hslot2, unsigned int slot2)
+{
+ struct sock *sk, *result;
+ struct hlist_nulls_node *node;
+ int score, badness;
+
+begin:
+ result = NULL;
+ badness = -1;
+ udp_portaddr_for_each_entry_rcu(sk, node, &hslot2->head) {
+ score = compute_score2(sk, net, saddr, sport,
+ daddr, hnum, dif);
+ if (score > badness) {
+ result = sk;
+ badness = score;
+ if (score == SCORE2_MAX)
+ goto exact_match;
+ }
+ }
+ /*
+ * if the nulls value we got at the end of this lookup is
+ * not the expected one, we must restart lookup.
+ * We probably met an item that was moved to another chain.
+ */
+ if (get_nulls_value(node) != slot2)
+ goto begin;
+
+ if (result) {
+exact_match:
+ if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt)))
+ result = NULL;
+ else if (unlikely(compute_score2(result, net, saddr, sport,
+ daddr, hnum, dif) < badness)) {
+ sock_put(result);
+ goto begin;
+ }
+ }
+ return result;
+}
+
static struct sock *__udp6_lib_lookup(struct net *net,
struct in6_addr *saddr, __be16 sport,
struct in6_addr *daddr, __be16 dport,
@@ -154,11 +236,35 @@ static struct sock *__udp6_lib_lookup(struct net *net,
struct sock *sk, *result;
struct hlist_nulls_node *node;
unsigned short hnum = ntohs(dport);
- unsigned int hash = udp_hashfn(net, hnum, udptable->mask);
- struct udp_hslot *hslot = &udptable->hash[hash];
+ unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask);
+ struct udp_hslot *hslot2, *hslot = &udptable->hash[slot];
int score, badness;
rcu_read_lock();
+ if (hslot->count > 10) {
+ hash2 = udp6_portaddr_hash(net, daddr, hnum);
+ slot2 = hash2 & udptable->mask;
+ hslot2 = &udptable->hash2[slot2];
+ if (hslot->count < hslot2->count)
+ goto begin;
+
+ result = udp6_lib_lookup2(net, saddr, sport,
+ daddr, hnum, dif,
+ hslot2, slot2);
+ if (!result) {
+ hash2 = udp6_portaddr_hash(net, &in6addr_any, hnum);
+ slot2 = hash2 & udptable->mask;
+ hslot2 = &udptable->hash2[slot2];
+ if (hslot->count < hslot2->count)
+ goto begin;
+
+ result = udp6_lib_lookup2(net, &in6addr_any, sport,
+ daddr, hnum, dif,
+ hslot2, slot2);
+ }
+ rcu_read_unlock();
+ return result;
+ }
begin:
result = NULL;
badness = -1;
@@ -174,7 +280,7 @@ begin:
* not the expected one, we must restart lookup.
* We probably met an item that was moved to another chain.
*/
- if (get_nulls_value(node) != hash)
+ if (get_nulls_value(node) != slot)
goto begin;
if (result) {
^ permalink raw reply related
* [PATCH 6/8] ipv4: udp: Optimise multicast reception
From: Eric Dumazet @ 2009-11-08 20:18 UTC (permalink / raw)
To: David S. Miller
Cc: Linux Netdev List, Lucian Adrian Grijincu, Octavian Purdila
UDP multicast rx path is a bit complex and can hold a spinlock
for a long time.
Using a small (32 or 64 entries) stack of socket pointers can help
to perform expensive operations (skb_clone(), udp_queue_rcv_skb())
outside of the lock, in most cases.
It's also a base for a future RCU conversion of multicast recption.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: Lucian Adrian Grijincu <lgrijincu@ixiacom.com>
---
net/ipv4/udp.c | 76 ++++++++++++++++++++++++++++++-----------------
1 files changed, 50 insertions(+), 26 deletions(-)
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index dd7f3d2..9d9072c 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1329,49 +1329,73 @@ drop:
return -1;
}
+
+static void flush_stack(struct sock **stack, unsigned int count,
+ struct sk_buff *skb, unsigned int final)
+{
+ unsigned int i;
+ struct sk_buff *skb1 = NULL;
+
+ for (i = 0; i < count; i++) {
+ if (likely(skb1 == NULL))
+ skb1 = (i == final) ? skb : skb_clone(skb, GFP_ATOMIC);
+
+ if (skb1 && udp_queue_rcv_skb(stack[i], skb1) <= 0)
+ skb1 = NULL;
+ }
+ if (unlikely(skb1))
+ kfree_skb(skb1);
+}
+
/*
* Multicasts and broadcasts go to each listener.
*
- * Note: called only from the BH handler context,
- * so we don't need to lock the hashes.
+ * Note: called only from the BH handler context.
*/
static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
struct udphdr *uh,
__be32 saddr, __be32 daddr,
struct udp_table *udptable)
{
- struct sock *sk;
+ struct sock *sk, *stack[256 / sizeof(struct sock *)];
struct udp_hslot *hslot = udp_hashslot(udptable, net, ntohs(uh->dest));
int dif;
+ unsigned int i, count = 0;
spin_lock(&hslot->lock);
sk = sk_nulls_head(&hslot->head);
dif = skb->dev->ifindex;
sk = udp_v4_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif);
- if (sk) {
- struct sock *sknext = NULL;
-
- do {
- struct sk_buff *skb1 = skb;
-
- sknext = udp_v4_mcast_next(net, sk_nulls_next(sk), uh->dest,
- daddr, uh->source, saddr,
- dif);
- if (sknext)
- skb1 = skb_clone(skb, GFP_ATOMIC);
-
- if (skb1) {
- int ret = udp_queue_rcv_skb(sk, skb1);
- if (ret > 0)
- /* we should probably re-process instead
- * of dropping packets here. */
- kfree_skb(skb1);
- }
- sk = sknext;
- } while (sknext);
- } else
- consume_skb(skb);
+ while (sk) {
+ stack[count++] = sk;
+ sk = udp_v4_mcast_next(net, sk_nulls_next(sk), uh->dest,
+ daddr, uh->source, saddr, dif);
+ if (unlikely(count == ARRAY_SIZE(stack))) {
+ if (!sk)
+ break;
+ flush_stack(stack, count, skb, ~0);
+ count = 0;
+ }
+ }
+ /*
+ * before releasing chain lock, we must take a reference on sockets
+ */
+ for (i = 0; i < count; i++)
+ sock_hold(stack[i]);
+
spin_unlock(&hslot->lock);
+
+ /*
+ * do the slow work with no lock held
+ */
+ if (count) {
+ flush_stack(stack, count, skb, count - 1);
+
+ for (i = 0; i < count; i++)
+ sock_put(stack[i]);
+ } else {
+ kfree_skb(skb);
+ }
return 0;
}
^ permalink raw reply related
* [PATCH 7/8] ipv6: udp: Optimise multicast reception
From: Eric Dumazet @ 2009-11-08 20:18 UTC (permalink / raw)
To: David S. Miller
Cc: Linux Netdev List, Lucian Adrian Grijincu, Octavian Purdila
IPV6 UDP multicast rx path is a bit complex and can hold a spinlock
for a long time.
Using a small (32 or 64 entries) stack of socket pointers can help
to perform expensive operations (skb_clone(), udp_queue_rcv_skb())
outside of the lock, in most cases.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
net/ipv6/udp.c | 71 +++++++++++++++++++++++++++++++----------------
1 files changed, 47 insertions(+), 24 deletions(-)
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index c5b0451..173e531 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -569,6 +569,27 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk,
return NULL;
}
+static void flush_stack(struct sock **stack, unsigned int count,
+ struct sk_buff *skb, unsigned int final)
+{
+ unsigned int i;
+ struct sock *sk;
+ struct sk_buff *skb1;
+
+ for (i = 0; i < count; i++) {
+ skb1 = (i == final) ? skb : skb_clone(skb, GFP_ATOMIC);
+
+ if (skb1) {
+ sk = stack[i];
+ bh_lock_sock(sk);
+ if (!sock_owned_by_user(sk))
+ udpv6_queue_rcv_skb(sk, skb1);
+ else
+ sk_add_backlog(sk, skb1);
+ bh_unlock_sock(sk);
+ }
+ }
+}
/*
* Note: called only from the BH handler context,
* so we don't need to lock the hashes.
@@ -577,41 +598,43 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
struct in6_addr *saddr, struct in6_addr *daddr,
struct udp_table *udptable)
{
- struct sock *sk, *sk2;
+ struct sock *sk, *stack[256 / sizeof(struct sock *)];
const struct udphdr *uh = udp_hdr(skb);
struct udp_hslot *hslot = udp_hashslot(udptable, net, ntohs(uh->dest));
int dif;
+ unsigned int i, count = 0;
spin_lock(&hslot->lock);
sk = sk_nulls_head(&hslot->head);
dif = inet6_iif(skb);
sk = udp_v6_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif);
- if (!sk) {
- kfree_skb(skb);
- goto out;
- }
-
- sk2 = sk;
- while ((sk2 = udp_v6_mcast_next(net, sk_nulls_next(sk2), uh->dest, daddr,
- uh->source, saddr, dif))) {
- struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC);
- if (buff) {
- bh_lock_sock(sk2);
- if (!sock_owned_by_user(sk2))
- udpv6_queue_rcv_skb(sk2, buff);
- else
- sk_add_backlog(sk2, buff);
- bh_unlock_sock(sk2);
+ while (sk) {
+ stack[count++] = sk;
+ sk = udp_v6_mcast_next(net, sk_nulls_next(sk), uh->dest, daddr,
+ uh->source, saddr, dif);
+ if (unlikely(count == ARRAY_SIZE(stack))) {
+ if (!sk)
+ break;
+ flush_stack(stack, count, skb, ~0);
+ count = 0;
}
}
- bh_lock_sock(sk);
- if (!sock_owned_by_user(sk))
- udpv6_queue_rcv_skb(sk, skb);
- else
- sk_add_backlog(sk, skb);
- bh_unlock_sock(sk);
-out:
+ /*
+ * before releasing the lock, we must take reference on sockets
+ */
+ for (i = 0; i < count; i++)
+ sock_hold(stack[i]);
+
spin_unlock(&hslot->lock);
+
+ if (count) {
+ flush_stack(stack, count, skb, count - 1);
+
+ for (i = 0; i < count; i++)
+ sock_put(stack[i]);
+ } else {
+ kfree_skb(skb);
+ }
return 0;
}
^ permalink raw reply related
* [PATCH 8/8] udp: multicast RX should increment SNMP/sk_drops counter in allocation failures
From: Eric Dumazet @ 2009-11-08 20:20 UTC (permalink / raw)
To: David S. Miller
Cc: Linux Netdev List, Lucian Adrian Grijincu, Octavian Purdila
When skb_clone() fails, we should increment sk_drops and SNMP counters.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
net/ipv4/udp.c | 12 +++++++++++-
net/ipv6/udp.c | 8 +++++++-
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 9d9072c..d73e917 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1335,12 +1335,22 @@ static void flush_stack(struct sock **stack, unsigned int count,
{
unsigned int i;
struct sk_buff *skb1 = NULL;
+ struct sock *sk;
for (i = 0; i < count; i++) {
+ sk = stack[i];
if (likely(skb1 == NULL))
skb1 = (i == final) ? skb : skb_clone(skb, GFP_ATOMIC);
- if (skb1 && udp_queue_rcv_skb(stack[i], skb1) <= 0)
+ if (!skb1) {
+ atomic_inc(&sk->sk_drops);
+ UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_RCVBUFERRORS,
+ IS_UDPLITE(sk));
+ UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_INERRORS,
+ IS_UDPLITE(sk));
+ }
+
+ if (skb1 && udp_queue_rcv_skb(sk, skb1) <= 0)
skb1 = NULL;
}
if (unlikely(skb1))
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 173e531..84cc4a8 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -579,14 +579,20 @@ static void flush_stack(struct sock **stack, unsigned int count,
for (i = 0; i < count; i++) {
skb1 = (i == final) ? skb : skb_clone(skb, GFP_ATOMIC);
+ sk = stack[i];
if (skb1) {
- sk = stack[i];
bh_lock_sock(sk);
if (!sock_owned_by_user(sk))
udpv6_queue_rcv_skb(sk, skb1);
else
sk_add_backlog(sk, skb1);
bh_unlock_sock(sk);
+ } else {
+ atomic_inc(&sk->sk_drops);
+ UDP6_INC_STATS_BH(sock_net(sk),
+ UDP_MIB_RCVBUFERRORS, IS_UDPLITE(sk));
+ UDP6_INC_STATS_BH(sock_net(sk),
+ UDP_MIB_INERRORS, IS_UDPLITE(sk));
}
}
}
^ permalink raw reply related
* Re: [PATCH v4 12/12] tree-wide: convert open calls to remove spaces to skip_spaces() lib function
From: Julia Lawall @ 2009-11-08 20:23 UTC (permalink / raw)
To: Theodore Tso
Cc: Pavel Roskin, Stefan Haberland, Jan Kara, linux-cachefs,
Mike Snitzer, Neil Brown, Frederic Weisbecker, Jens Axboe,
Heiko Carstens, James E . J . Bottomley, ibm-acpi-devel, dm-devel,
H . Peter Anvin, Daire Byrne, Alasdair G Kergon, Greg Banks,
Stefan Weinhuber, Eric Sandeen, Adam Belay, Helge Deller, x86,
James Morris, Takashi Iwai, André Goddard Rosa, Alan Cox
In-Reply-To: <20091108184722.GA1647@mit.edu>
> > Also, while at it, if we see (*str && isspace(*str)), we can be sure to
> > remove the first condition (*str) as the second one (isspace(*str)) also
> > evaluates to 0 whenever *str == 0, making it redundant. In other words,
> > "a char equals zero is never a space".
I tried the following semantic patch (http://coccinelle.lip6.fr), and got
the results below.
@@
expression str;
@@
( // ignore skip_spaces cases
while (*str && isspace(*str)) { \(str++;\|++str;\) }
|
- *str &&
isspace(*str)
)
I haven't checked the results in any way, however.
julia
diff -u -p a/drivers/leds/led-class.c b/drivers/leds/led-class.c
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -50,7 +50,7 @@ static ssize_t led_brightness_store(stru
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;
- if (*after && isspace(*after))
+ if (isspace(*after))
count++;
if (count == size) {
diff -u -p a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c
--- a/drivers/leds/ledtrig-timer.c
+++ b/drivers/leds/ledtrig-timer.c
@@ -83,7 +83,7 @@ static ssize_t led_delay_on_store(struct
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;
- if (*after && isspace(*after))
+ if (isspace(*after))
count++;
if (count == size) {
@@ -127,7 +127,7 @@ static ssize_t led_delay_off_store(struc
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;
- if (*after && isspace(*after))
+ if (isspace(*after))
count++;
if (count == size) {
diff -u -p a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c
--- a/drivers/video/backlight/lcd.c
+++ b/drivers/video/backlight/lcd.c
@@ -101,7 +101,7 @@ static ssize_t lcd_store_power(struct de
int power = simple_strtoul(buf, &endp, 0);
size_t size = endp - buf;
- if (*endp && isspace(*endp))
+ if (isspace(*endp))
size++;
if (size != count)
return -EINVAL;
@@ -140,7 +140,7 @@ static ssize_t lcd_store_contrast(struct
int contrast = simple_strtoul(buf, &endp, 0);
size_t size = endp - buf;
- if (*endp && isspace(*endp))
+ if (isspace(*endp))
size++;
if (size != count)
return -EINVAL;
diff -u -p a/drivers/video/display/display-sysfs.c b/drivers/video/display/display-sysfs.c
--- a/drivers/video/display/display-sysfs.c
+++ b/drivers/video/display/display-sysfs.c
@@ -67,7 +67,7 @@ static ssize_t display_store_contrast(st
contrast = simple_strtoul(buf, &endp, 0);
size = endp - buf;
- if (*endp && isspace(*endp))
+ if (isspace(*endp))
size++;
if (size != count)
diff -u -p a/drivers/video/output.c b/drivers/video/output.c
--- a/drivers/video/output.c
+++ b/drivers/video/output.c
@@ -50,7 +50,7 @@ static ssize_t video_output_store_state(
int request_state = simple_strtoul(buf,&endp,0);
size_t size = endp - buf;
- if (*endp && isspace(*endp))
+ if (isspace(*endp))
size++;
if (size != count)
return -EINVAL;
^ permalink raw reply
* Re: [RFC, PATCH 0/7] net, compat_ioctl: move handlers to net/socket.c
From: Arnd Bergmann @ 2009-11-08 21:31 UTC (permalink / raw)
To: David Miller; +Cc: linux-kernel, hch, netdev, David Woodhouse
In-Reply-To: <20091106.204753.223665411.davem@davemloft.net>
On Saturday 07 November 2009, David Miller wrote:
> From: Arnd Bergmann <arnd@arndb.de>
> Date: Fri, 6 Nov 2009 19:09:02 +0100
>
> > This cleans up some of the socket ioctl handling by moving it
> > from fs/compat_ioctl.c to net/socket.c. The code is still untested,
> > so this is an RFC for now. If you're happy with it, I'll do some
> > testing to see if everything still works.
> >
> > This series is a prerequisite for cleaning up the rest of
> > compat_ioctl.c, saving some 30kb of kernel memory in the end.
> >
> > The first four patches are probably worthwhile independently,
> > because they fix some bugs in compat_ioctl handling.
> > There is some obvious conflict with the ATM patch I sent
> > independently today. That one should probably be worked out
> > first.
>
> This looks great, all applied. Please make the fixups recommended
> to you in the feedback as followon patches.
Thanks! Two patches follow.
Any opinion on how to proceed with the ATM stuff? From my
point of view, I'm fine with having moved it out of fs/compat_ioctl.c,
but it's still a bit silly to have two half-complete implementations.
> Also, I added the following cure after your patch series:
>
> net: compat: No need to define IFHWADDRLEN and IFNAMSIZ twice.
>
> It's defined colloqually in linux/if.h and linux/compat.h
> includes that.
ok.
Arnd <><
^ permalink raw reply
* [PATCH 1/2] net, compat_ioctl: fix SIOCGMII ioctls
From: Arnd Bergmann @ 2009-11-08 21:34 UTC (permalink / raw)
To: David Miller; +Cc: linux-kernel, hch, netdev
In-Reply-To: <200911082231.48060.arnd@arndb.de>
SIOCGMIIPHY and SIOCGMIIREG return data through ifreq,
so it needs to be converted on the way out as well.
SIOCGIFPFLAGS is unused, but has the same problem in theory.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
net/socket.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/net/socket.c b/net/socket.c
index 688de5d..28a0263 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2587,7 +2587,10 @@ static int dev_ifsioc(struct net *net, struct socket *sock,
case SIOCGIFBRDADDR:
case SIOCGIFDSTADDR:
case SIOCGIFNETMASK:
+ case SIOCGIFPFLAGS:
case SIOCGIFTXQLEN:
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
if (copy_to_user(uifr32, &ifr, sizeof(*uifr32)))
return -EFAULT;
break;
--
1.6.3.3
^ permalink raw reply related
* [PATCH 2/2] net/compat_ioctl: support SIOCWANDEV
From: Arnd Bergmann @ 2009-11-08 21:39 UTC (permalink / raw)
To: David Miller, Krzysztof Halasa, Daniel Walker; +Cc: linux-kernel, hch, netdev
In-Reply-To: <200911082231.48060.arnd@arndb.de>
This adds compat_ioctl support for SIOCWANDEV, which has
always been missing.
The definition of struct compat_ifreq was missing an
ifru_settings fields that is needed to support SIOCWANDEV,
so add that and clean up the whitespace damage in the
struct definition.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Krzysztof Halasa <khc@pm.waw.pl>
Cc: Daniel Walker <dwalker@fifo99.com>
---
Krzysztof, can you verify that this is really needed and that it
does the right thing?
Daniel, I didn't want to add another patch just for the broken
whitespace I copied, but since we needed another fix in this area...
---
include/linux/compat.h | 41 ++++++++++++++++++++++++-----------------
net/socket.c | 23 +++++++++++++++++++++++
2 files changed, 47 insertions(+), 17 deletions(-)
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 224c7a8..4dff55a 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -165,25 +165,32 @@ struct compat_ifmap {
unsigned char port;
};
+struct compat_if_settings
+{
+ unsigned int type; /* Type of physical device or protocol */
+ unsigned int size; /* Size of the data allocated by the caller */
+ compat_uptr_t ifs_ifsu; /* union of pointers */
+};
+
struct compat_ifreq {
- union {
- char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
- } ifr_ifrn;
- union {
- struct sockaddr ifru_addr;
- struct sockaddr ifru_dstaddr;
- struct sockaddr ifru_broadaddr;
- struct sockaddr ifru_netmask;
- struct sockaddr ifru_hwaddr;
- short ifru_flags;
- compat_int_t ifru_ivalue;
- compat_int_t ifru_mtu;
- struct compat_ifmap ifru_map;
- char ifru_slave[IFNAMSIZ]; /* Just fits the size */
+ union {
+ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ } ifr_ifrn;
+ union {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_dstaddr;
+ struct sockaddr ifru_broadaddr;
+ struct sockaddr ifru_netmask;
+ struct sockaddr ifru_hwaddr;
+ short ifru_flags;
+ compat_int_t ifru_ivalue;
+ compat_int_t ifru_mtu;
+ struct compat_ifmap ifru_map;
+ char ifru_slave[IFNAMSIZ]; /* Just fits the size */
char ifru_newname[IFNAMSIZ];
- compat_caddr_t ifru_data;
- /* XXXX? ifru_settings should be here */
- } ifr_ifru;
+ compat_caddr_t ifru_data;
+ struct compat_if_settings ifru_settings;
+ } ifr_ifru;
};
struct compat_ifconf {
diff --git a/net/socket.c b/net/socket.c
index 28a0263..17c98a5 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2468,6 +2468,27 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32)
return dev_ioctl(net, SIOCETHTOOL, ifr);
}
+static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32)
+{
+ void __user *uptr;
+ compat_uptr_t uptr32;
+ struct ifreq __user *uifr;
+
+ uifr = compat_alloc_user_space(sizeof (*uifr));
+ if (copy_in_user(uifr, uifr32, sizeof(struct compat_ifreq)))
+ return -EFAULT;
+
+ if (get_user(uptr32, &uifr32->ifr_settings.ifs_ifsu))
+ return -EFAULT;
+
+ uptr = compat_ptr(uptr32);
+
+ if (put_user(uptr, &uifr->ifr_settings.ifs_ifsu.raw_hdlc))
+ return -EFAULT;
+
+ return dev_ioctl(net, SIOCWANDEV, uifr);
+}
+
static int bond_ioctl(struct net *net, unsigned int cmd,
struct compat_ifreq __user *ifr32)
{
@@ -2900,6 +2921,8 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
return dev_ifconf(net, argp);
case SIOCETHTOOL:
return ethtool_ioctl(net, argp);
+ case SIOCWANDEV:
+ return compat_siocwandev(net, argp);
case SIOCBONDENSLAVE:
case SIOCBONDRELEASE:
case SIOCBONDSETHWADDR:
--
1.6.3.3
^ permalink raw reply related
* tulip : kernel BUG in tulip_up/tulip_resume
From: Philippe De Muyter @ 2009-11-08 22:33 UTC (permalink / raw)
To: grundler, kyle, netdev, linux-kernel
In-Reply-To: <20090129210701.GA8092@frolo.macqel>
Hello,
I have just installed 2.6.31 (from opensuse 11.2) one a tulip-equipped
computer and I get the following error message from the kernel :
[ 2495.526390] ------------[ cut here ]------------
[ 2495.526390] kernel BUG at /usr/src/packages/BUILD/kernel-default-2.6.31.5/linux-2.6.31/include/linux/netdevice.h:439!
[ 2495.526390] invalid opcode: 0000 [#1] SMP
[ 2495.526390] last sysfs file: /sys/devices/pci0000:00/0000:00:07.1/host0/target0:0:0/0:0:0:0/block/sda/uevent
[ 2495.526390] Modules linked in: ohci_hcd raw1394 ohci1394 ieee1394 acpi_cpufreq speedstep_lib processor thermal_sys hwmon edd ipv6 af_packet fuse loop dm_mod rtc_cmos rtc_core rtc_lib apm pcspkr sg tulip uhci_hcd ehci_hcd reiserfs ata_piix ahci libata
[ 2495.526390]
[ 2495.526390] Pid: 339, comm: kapmd Not tainted (2.6.31.5-0.1-default #1)
[ 2495.526390] EIP: 0060:[<c3d6045d>] EFLAGS: 00010246 CPU: 0
[ 2495.526390] EIP is at tulip_up+0xa2d/0xa80 [tulip]
[ 2495.526390] EAX: 00000000 EBX: c1cd7000 ECX: c022af50 EDX: 0001ec00
[ 2495.526390] ESI: c1cd7340 EDI: 00000000 EBP: c20bde44 ESP: c20bddfc
[ 2495.526390] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
[ 2495.526390] Process kapmd (pid: 339, ti=c20bc000 task=c25a32c0 task.ti=c20bc000)
[ 2495.526390] Stack:
[ 2495.526390] 0000000b c20bde44 c02acf76 2caa9a94 c2481800 c20bde38 c043ca4a c20bde27
[ 2495.526390] <0> 069ee44b c20ec120 c087c020 0001ec00 c1cd7000 c3d5c720 2caa9a94 c1cd7000
[ 2495.526390] <0> c2480000 00000000 c20bde68 c3d60555 00000080 c1cd7000 c1cd7000 2caa9a94
[ 2495.526390] Call Trace:
[ 2495.526390] [<c3d60555>] tulip_resume+0xa5/0xd0 [tulip]
[ 2495.526390] [<c043dd65>] pci_legacy_resume+0x35/0x60
[ 2495.526390] [<c043df2f>] pci_pm_resume+0x7f/0xb0
[ 2495.526390] [<c04d82f2>] pm_op+0xd2/0x180
[ 2495.526390] [<c04d91ee>] device_resume+0x5e/0x1a0
[ 2495.526390] [<c04d93dd>] dpm_resume+0xad/0x140
[ 2495.526390] [<c04d948b>] dpm_resume_end+0x1b/0x40
[ 2495.526390] [<c3db1978>] check_events+0x148/0x240 [apm]
[ 2495.526390] [<c3db23a2>] apm_mainloop+0x82/0x130 [apm]
[ 2495.526390] [<c3db28fe>] apm+0x10e/0x3d0 [apm]
[ 2495.526390] [<c026bef4>] kthread+0x84/0x90
[ 2495.526390] [<c0204db7>] kernel_thread_helper+0x7/0x10
[ 2495.526390] Code: 45 e4 e8 37 ce 6c fc 8b 4d e8 89 5c 24 10 89 7c 24 0c 89 4c 24 04 89 44 24 08 c7 04 24 4c 4e d6 c3 e8 86 f7 89 fc e9 f4 f8 ff ff <0f> 0b eb fe 0f be 96 16 09 00 00 b9 01 00 00 00 8b 45 e8 e8 fb
[ 2495.526390] EIP: [<c3d6045d>] tulip_up+0xa2d/0xa80 [tulip] SS:ESP 0068:c20bddfc
[ 2495.534162] ---[ end trace 609ed25c95a75fa1 ]---
This comes from a BUG_ON in napi_enable in netdevice.h.
napi_enable itself is called by tulip_up as such :
#ifdef CONFIG_TULIP_NAPI
napi_enable(&tp->napi);
#endif
At first reading, a matching napi_disable is called in tulip_down.
Does someone know what could be wrong and have a fix or should I look myself ?
Thanks in advance
Philippe
^ permalink raw reply
* Re: query: net-next section mismatch(es)
From: David Miller @ 2009-11-09 0:35 UTC (permalink / raw)
To: william.allen.simpson; +Cc: netdev
In-Reply-To: <4AF6D1F5.80307@gmail.com>
From: William Allen Simpson <william.allen.simpson@gmail.com>
Date: Sun, 08 Nov 2009 09:13:09 -0500
> Yesterday morning (and for a month), I was getting the usual:
>
> WARNING: modpost: Found 1 section mismatch(es).
> To see full details build your kernel with:
> 'make CONFIG_DEBUG_SECTION_MISMATCH=y'
>
> This morning, it changed:
>
> WARNING: modpost: Found 4 section mismatch(es).
> To see full details build your kernel with:
> 'make CONFIG_DEBUG_SECTION_MISMATCH=y'
>
> I remember compiling it once with that option, and not finding
> anything
> wrong in my code, but I'm wondering how it took a great leap?
Well, type 'make CONFIG_DEBUG_SECTION_MISMATCH=y' as the message
says, in order to find out.
^ permalink raw reply
* Re: query: net-next section mismatch(es)
From: William Allen Simpson @ 2009-11-09 2:01 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <20091108.163527.185100796.davem@davemloft.net>
David Miller wrote:
> From: William Allen Simpson <william.allen.simpson@gmail.com>
>> I remember compiling it once with that option, and not finding
>> anything
>> wrong in my code, but I'm wondering how it took a great leap?
>
> Well, type 'make CONFIG_DEBUG_SECTION_MISMATCH=y' as the message
> says, in order to find out.
>
Hardly worth the time, as 'make modules' doesn't compile today:
/home/administer/net-next-2.6/drivers/staging/android/logger.c: In function ‘logger_read’:
/home/administer/net-next-2.6/drivers/staging/android/logger.c:165: error: ‘TASK_INTERRUPTIBLE’ undeclared (first use in this function)
/home/administer/net-next-2.6/drivers/staging/android/logger.c:165: error: (Each undeclared identifier is reported only once
/home/administer/net-next-2.6/drivers/staging/android/logger.c:165: error: for each function it appears in.)
/home/administer/net-next-2.6/drivers/staging/android/logger.c:178: error: implicit declaration of function ‘signal_pending’
/home/administer/net-next-2.6/drivers/staging/android/logger.c:183: error: implicit declaration of function ‘schedule’
/home/administer/net-next-2.6/drivers/staging/android/logger.c: In function ‘logger_aio_write’:
/home/administer/net-next-2.6/drivers/staging/android/logger.c:325: error: dereferencing pointer to incomplete type
/home/administer/net-next-2.6/drivers/staging/android/logger.c:333: error: dereferencing pointer to incomplete type
/home/administer/net-next-2.6/drivers/staging/android/logger.c:334: error: dereferencing pointer to incomplete type
/home/administer/net-next-2.6/drivers/staging/android/logger.c:337: error: dereferencing pointer to incomplete type
/home/administer/net-next-2.6/drivers/staging/android/logger.c:360: error: dereferencing pointer to incomplete type
/home/administer/net-next-2.6/drivers/staging/android/logger.c:363: error: dereferencing pointer to incomplete type
/home/administer/net-next-2.6/drivers/staging/android/logger.c:370: error: increment of pointer to unknown structure
/home/administer/net-next-2.6/drivers/staging/android/logger.c:370: error: arithmetic on pointer to an incomplete type
/home/administer/net-next-2.6/drivers/staging/android/logger.c:377: error: ‘TASK_INTERRUPTIBLE’ undeclared (first use in this function)
make[4]: *** [drivers/staging/android/logger.o] Error 1
make[3]: *** [drivers/staging/android] Error 2
make[2]: *** [drivers/staging] Error 2
make[2]: *** Waiting for unfinished jobs....
But I did it with 'make vmlinux' anyway, still makes no sense to me:
/home/administer/net-next-2.6/arch/x86/include/asm/string_32.h:74: warning: array subscript is above array bounds
WARNING: drivers/acpi/processor.o(.text+0xa88): Section mismatch in reference from the function acpi_processor_add() to the function .cpuinit.text:acpi_processor_power_init()
The function acpi_processor_add() references
the function __cpuinit acpi_processor_power_init().
This is often because acpi_processor_add lacks a __cpuinit
annotation or the annotation of acpi_processor_power_init is wrong.
WARNING: drivers/acpi/built-in.o(.text+0x21f24): Section mismatch in reference from the function acpi_processor_add() to the function .cpuinit.text:acpi_processor_power_init()
The function acpi_processor_add() references
the function __cpuinit acpi_processor_power_init().
This is often because acpi_processor_add lacks a __cpuinit
annotation or the annotation of acpi_processor_power_init is wrong.
/home/administer/net-next-2.6/include/linux/mca-legacy.h:12:2: warning: #warning "MCA legacy - please move your driver to the new sysfs api"
WARNING: drivers/net/phy/built-in.o(.devexit.text+0x13): Section mismatch in reference from the function mdio_gpio_bus_destroy() to the function .devinit.text:mdio_gpio_bus_deinit()
The function __devexit mdio_gpio_bus_destroy() references
a function __devinit mdio_gpio_bus_deinit().
This is often seen when error handling in the exit function
uses functionality in the init path.
The fix is often to remove the __devinit annotation of
mdio_gpio_bus_deinit() so it may be used outside an init section.
WARNING: drivers/net/built-in.o(.devexit.text+0x13): Section mismatch in reference from the function mdio_gpio_bus_destroy() to the function .devinit.text:mdio_gpio_bus_deinit()
The function __devexit mdio_gpio_bus_destroy() references
a function __devinit mdio_gpio_bus_deinit().
This is often seen when error handling in the exit function
uses functionality in the init path.
The fix is often to remove the __devinit annotation of
mdio_gpio_bus_deinit() so it may be used outside an init section.
/home/administer/net-next-2.6/arch/x86/include/asm/string_32.h:74: warning: array subscript is above array bounds
WARNING: drivers/built-in.o(.text+0x48274): Section mismatch in reference from the function acpi_processor_add() to the function .cpuinit.text:acpi_processor_power_init()
The function acpi_processor_add() references
the function __cpuinit acpi_processor_power_init().
This is often because acpi_processor_add lacks a __cpuinit
annotation or the annotation of acpi_processor_power_init is wrong.
WARNING: drivers/built-in.o(.devexit.text+0x38c): Section mismatch in reference from the function mdio_gpio_bus_destroy() to the function .devinit.text:mdio_gpio_bus_deinit()
The function __devexit mdio_gpio_bus_destroy() references
a function __devinit mdio_gpio_bus_deinit().
This is often seen when error handling in the exit function
uses functionality in the init path.
The fix is often to remove the __devinit annotation of
mdio_gpio_bus_deinit() so it may be used outside an init section.
WARNING: vmlinux.o(.text+0x27c824): Section mismatch in reference from the function acpi_processor_add() to the function .cpuinit.text:acpi_processor_power_init()
The function acpi_processor_add() references
the function __cpuinit acpi_processor_power_init().
This is often because acpi_processor_add lacks a __cpuinit
annotation or the annotation of acpi_processor_power_init is wrong.
WARNING: vmlinux.o(.text+0x2c8469): Section mismatch in reference from the function twl4030_sih_setup() to the function .init.text:set_irq_noprobe()
The function twl4030_sih_setup() references
the function __init set_irq_noprobe().
This is often because twl4030_sih_setup lacks a __init
annotation or the annotation of set_irq_noprobe is wrong.
WARNING: vmlinux.o(.text+0x2c8581): Section mismatch in reference from the function twl_init_irq() to the function .init.text:set_irq_noprobe()
The function twl_init_irq() references
the function __init set_irq_noprobe().
This is often because twl_init_irq lacks a __init
annotation or the annotation of set_irq_noprobe is wrong.
WARNING: vmlinux.o(.devexit.text+0x3e1): Section mismatch in reference from the function mdio_gpio_bus_destroy() to the function .devinit.text:mdio_gpio_bus_deinit()
The function __devexit mdio_gpio_bus_destroy() references
a function __devinit mdio_gpio_bus_deinit().
This is often seen when error handling in the exit function
uses functionality in the init path.
The fix is often to remove the __devinit annotation of
mdio_gpio_bus_deinit() so it may be used outside an init section.
^ permalink raw reply
* linux-next: manual merge of the net tree with the net-current tree
From: Stephen Rothwell @ 2009-11-09 2:03 UTC (permalink / raw)
To: David Miller, netdev
Cc: linux-next, linux-kernel, Wolfgang Grandegger, Sebastian Haas
Hi all,
Today's linux-next merge of the net tree got a conflict in
drivers/net/can/usb/ems_usb.c between commit
2b2072e902848a63168570f500a5726744b3873a ("ems_usb: Fix byte order issues
on big endian machines") from the net-current tree and commit
7b6856a0296a8f187bb88ba31fa83a08abba7966 ("can: provide library functions
for skb allocation") from the net tree.
Just context changes. I fixed it up (see below) and can carry the fix
for a while.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
diff --cc drivers/net/can/usb/ems_usb.c
index abdbd9c,3685f3e..0000000
--- a/drivers/net/can/usb/ems_usb.c
+++ b/drivers/net/can/usb/ems_usb.c
@@@ -315,11 -315,7 +315,7 @@@ static void ems_usb_rx_can_msg(struct e
if (skb == NULL)
return;
- skb->protocol = htons(ETH_P_CAN);
-
- cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
-
- cf->can_id = msg->msg.can_msg.id;
+ cf->can_id = le32_to_cpu(msg->msg.can_msg.id);
cf->can_dlc = min_t(u8, msg->msg.can_msg.length, 8);
if (msg->type == CPC_MSG_TYPE_EXT_CAN_FRAME
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox