Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
authorDavid S. Miller <davem@davemloft.net>
Wed, 12 May 2010 07:05:35 +0000 (00:05 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 12 May 2010 07:05:35 +0000 (00:05 -0700)
Conflicts:
Documentation/feature-removal-schedule.txt
drivers/net/wireless/ath/ar9170/usb.c
drivers/scsi/iscsi_tcp.c
net/ipv4/ipmr.c

18 files changed:
1  2 
Documentation/feature-removal-schedule.txt
MAINTAINERS
drivers/net/arm/ep93xx_eth.c
drivers/net/fec.c
drivers/net/gianfar.c
drivers/net/phy/micrel.c
drivers/net/wireless/ath/ar9170/usb.c
include/linux/fs.h
include/net/sctp/sm.h
include/net/sctp/structs.h
net/core/dev.c
net/ipv4/ipmr.c
net/ipv4/udp.c
net/ipv6/af_inet6.c
net/ipv6/datagram.c
net/mac80211/mlme.c
net/sctp/sm_sideeffect.c
net/sctp/transport.c

@@@ -241,6 -241,16 +241,6 @@@ Who:      Thomas Gleixner <tglx@linutronix.d
  
  ---------------------------
  
 -What (Why):
 -      - xt_recent: the old ipt_recent proc dir
 -        (superseded by /proc/net/xt_recent)
 -
 -When: January 2009 or Linux 2.7.0, whichever comes first
 -Why:  Superseded by newer revisions or modules
 -Who:  Jan Engelhardt <jengelh@computergmbh.de>
 -
 ----------------------------
 -
  What: GPIO autorequest on gpio_direction_{input,output}() in gpiolib
  When: February 2010
  Why:  All callers should use explicit gpio_request()/gpio_free().
@@@ -533,24 -543,6 +533,24 @@@ Who:     Eric Miao <eric.y.miao@gmail.com
  
  ----------------------------
  
 +What: sysfs-class-rfkill state file
 +When: Feb 2014
 +Files:        net/rfkill/core.c
 +Why:  Documented as obsolete since Feb 2010. This file is limited to 3
 +      states while the rfkill drivers can have 4 states.
 +Who:  anybody or Florian Mickler <florian@mickler.org>
 +
 +----------------------------
 +
 +What:         sysfs-class-rfkill claim file
 +When: Feb 2012
 +Files:        net/rfkill/core.c
 +Why:  It is not possible to claim an rfkill driver since 2007. This is
 +      Documented as obsolete since Feb 2010.
 +Who:  anybody or Florian Mickler <florian@mickler.org>
 +
 +----------------------------
 +
  What: capifs
  When: February 2011
  Files:        drivers/isdn/capi/capifs.*
@@@ -599,30 -591,24 +599,53 @@@ Why:    Useful in 2003, implementation is 
  Who:  Len Brown <len.brown@intel.com>
  
  ----------------------------
 +
 +What: iwlwifi 50XX module parameters
 +When: 2.6.40
 +Why:  The "..50" modules parameters were used to configure 5000 series and
 +      up devices; different set of module parameters also available for 4965
 +      with same functionalities. Consolidate both set into single place
 +      in drivers/net/wireless/iwlwifi/iwl-agn.c
 +
 +Who:  Wey-Yi Guy <wey-yi.w.guy@intel.com>
 +
 +----------------------------
 +
 +What: iwl4965 alias support
 +When: 2.6.40
 +Why:  Internal alias support has been present in module-init-tools for some
 +      time, the MODULE_ALIAS("iwl4965") boilerplate aliases can be removed
 +      with no impact.
 +
 +Who:  Wey-Yi Guy <wey-yi.w.guy@intel.com>
 +
 +---------------------------
 +
 +What: xt_NOTRACK
 +Files:        net/netfilter/xt_NOTRACK.c
 +When: April 2011
 +Why:  Superseded by xt_CT
 +Who:  Netfilter developer team <netfilter-devel@vger.kernel.org>
++
++---------------------------
+ What: video4linux /dev/vtx teletext API support
+ When: 2.6.35
+ Files:        drivers/media/video/saa5246a.c drivers/media/video/saa5249.c
+       include/linux/videotext.h
+ Why:  The vtx device nodes have been superseded by vbi device nodes
+       for many years. No applications exist that use the vtx support.
+       Of the two i2c drivers that actually support this API the saa5249
+       has been impossible to use for a year now and no known hardware
+       that supports this device exists. The saa5246a is theoretically
+       supported by the old mxb boards, but it never actually worked.
+       In summary: there is no hardware that can use this API and there
+       are no applications actually implementing this API.
+       The vtx support still reserves minors 192-223 and we would really
+       like to reuse those for upcoming new functionality. In the unlikely
+       event that new hardware appears that wants to use the functionality
+       provided by the vtx API, then that functionality should be build
+       around the sliced VBI API instead.
+ Who:  Hans Verkuil <hverkuil@xs4all.nl>
diff --combined MAINTAINERS
@@@ -1501,10 -1501,9 +1501,10 @@@ M:    Andy Whitcroft <apw@canonical.com
  S:    Supported
  F:    scripts/checkpatch.pl
  
 -CISCO 10G ETHERNET DRIVER
 +CISCO VIC ETHERNET NIC DRIVER
  M:    Scott Feldman <scofeldm@cisco.com>
 -M:    Joe Eykholt <jeykholt@cisco.com>
 +M:    Vasanthy Kolluri <vkolluri@cisco.com>
 +M:    Roopa Prabhu <roprabhu@cisco.com>
  S:    Supported
  F:    drivers/net/enic/
  
@@@ -1961,7 -1960,7 +1961,7 @@@ F:      lib/kobj
  
  DRM DRIVERS
  M:    David Airlie <airlied@linux.ie>
- L:    dri-devel@lists.sourceforge.net
+ L:    dri-devel@lists.freedesktop.org
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/airlied/drm-2.6.git
  S:    Maintained
  F:    drivers/gpu/drm/
@@@ -3003,9 -3002,10 +3003,9 @@@ F:     net/ipv4/netfilter/ipt_MASQUERADE.
  IP1000A 10/100/1000 GIGABIT ETHERNET DRIVER
  M:    Francois Romieu <romieu@fr.zoreil.com>
  M:    Sorbica Shieh <sorbica@icplus.com.tw>
 -M:    Jesse Huang <jesse@icplus.com.tw>
  L:    netdev@vger.kernel.org
  S:    Maintained
 -F:    drivers/net/ipg.c
 +F:    drivers/net/ipg.*
  
  IPATH DRIVER
  M:    Ralph Campbell <infinipath@qlogic.com>
@@@ -3852,6 -3852,7 +3852,6 @@@ M:      Ramkrishna Vepa <ram.vepa@neterion.c
  M:    Rastapur Santosh <santosh.rastapur@neterion.com>
  M:    Sivakumar Subramani <sivakumar.subramani@neterion.com>
  M:    Sreenivasa Honnur <sreenivasa.honnur@neterion.com>
 -M:    Anil Murthy <anil.murthy@neterion.com>
  L:    netdev@vger.kernel.org
  W:    http://trac.neterion.com/cgi-bin/trac.cgi/wiki/Linux?Anonymous
  W:    http://trac.neterion.com/cgi-bin/trac.cgi/wiki/X3100Linux?Anonymous
@@@ -3956,7 -3957,6 +3956,7 @@@ F:      net/rfkill
  F:    net/wireless/
  F:    include/net/ieee80211*
  F:    include/linux/wireless.h
 +F:    include/linux/iw_handler.h
  F:    drivers/net/wireless/
  
  NETWORKING DRIVERS
@@@ -4482,17 -4482,17 +4482,17 @@@ S:   Maintaine
  F:    drivers/ata/sata_promise.*
  
  PS3 NETWORK SUPPORT
- M:    Geoff Levand <geoffrey.levand@am.sony.com>
+ M:    Geoff Levand <geoff@infradead.org>
  L:    netdev@vger.kernel.org
  L:    cbe-oss-dev@ozlabs.org
- S:    Supported
+ S:    Maintained
  F:    drivers/net/ps3_gelic_net.*
  
  PS3 PLATFORM SUPPORT
- M:    Geoff Levand <geoffrey.levand@am.sony.com>
+ M:    Geoff Levand <geoff@infradead.org>
  L:    linuxppc-dev@ozlabs.org
  L:    cbe-oss-dev@ozlabs.org
- S:    Supported
+ S:    Maintained
  F:    arch/powerpc/boot/ps3*
  F:    arch/powerpc/include/asm/lv1call.h
  F:    arch/powerpc/include/asm/ps3*.h
@@@ -4791,12 -4791,11 +4791,11 @@@ F:   drivers/s390/crypto
  
  S390 ZFCP DRIVER
  M:    Christof Schmitt <christof.schmitt@de.ibm.com>
- M:    Martin Peschke <mp3@de.ibm.com>
+ M:    Swen Schillig <swen@vnet.ibm.com>
  M:    linux390@de.ibm.com
  L:    linux-s390@vger.kernel.org
  W:    http://www.ibm.com/developerworks/linux/linux390/
  S:    Supported
- F:    Documentation/s390/zfcpdump.txt
  F:    drivers/s390/scsi/zfcp_*
  
  S390 IUCV NETWORK LAYER
@@@ -311,11 -311,6 +311,6 @@@ err
                processed++;
        }
  
-       if (processed) {
-               wrw(ep, REG_RXDENQ, processed);
-               wrw(ep, REG_RXSTSENQ, processed);
-       }
        return processed;
  }
  
@@@ -350,6 -345,11 +345,11 @@@ poll_some_more
                        goto poll_some_more;
        }
  
+       if (rx) {
+               wrw(ep, REG_RXDENQ, rx);
+               wrw(ep, REG_RXSTSENQ, rx);
+       }
        return rx;
  }
  
@@@ -374,6 -374,8 +374,6 @@@ static int ep93xx_xmit(struct sk_buff *
                                skb->len, DMA_TO_DEVICE);
        dev_kfree_skb(skb);
  
 -      dev->trans_start = jiffies;
 -
        spin_lock_irq(&ep->tx_pending_lock);
        ep->tx_pending++;
        if (ep->tx_pending == TX_QUEUE_ENTRIES)
diff --combined drivers/net/fec.c
@@@ -40,7 -40,6 +40,7 @@@
  #include <linux/irq.h>
  #include <linux/clk.h>
  #include <linux/platform_device.h>
 +#include <linux/phy.h>
  
  #include <asm/cacheflush.h>
  
@@@ -62,6 -61,7 +62,6 @@@
   * Define the fixed address of the FEC hardware.
   */
  #if defined(CONFIG_M5272)
 -#define HAVE_mii_link_interrupt
  
  static unsigned char  fec_mac_default[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  #endif
  #endif /* CONFIG_M5272 */
  
 -/* Forward declarations of some structures to support different PHYs */
 -
 -typedef struct {
 -      uint mii_data;
 -      void (*funct)(uint mii_reg, struct net_device *dev);
 -} phy_cmd_t;
 -
 -typedef struct {
 -      uint id;
 -      char *name;
 -
 -      const phy_cmd_t *config;
 -      const phy_cmd_t *startup;
 -      const phy_cmd_t *ack_int;
 -      const phy_cmd_t *shutdown;
 -} phy_info_t;
 -
  /* The number of Tx and Rx buffers.  These are allocated from the page
   * pool.  The code may assume these are power of two, so it it best
   * to keep them that size.
@@@ -172,21 -189,29 +172,21 @@@ struct fec_enet_private 
        uint    tx_full;
        /* hold while accessing the HW like ringbuffer for tx/rx but not MAC */
        spinlock_t hw_lock;
 -      /* hold while accessing the mii_list_t() elements */
 -      spinlock_t mii_lock;
 -
 -      uint    phy_id;
 -      uint    phy_id_done;
 -      uint    phy_status;
 -      uint    phy_speed;
 -      phy_info_t const        *phy;
 -      struct work_struct phy_task;
  
 -      uint    sequence_done;
 -      uint    mii_phy_task_queued;
 +      struct  platform_device *pdev;
  
 -      uint    phy_addr;
 +      int     opened;
  
 +      /* Phylib and MDIO interface */
 +      struct  mii_bus *mii_bus;
 +      struct  phy_device *phy_dev;
 +      int     mii_timeout;
 +      uint    phy_speed;
        int     index;
 -      int     opened;
        int     link;
        int     full_duplex;
  };
  
 -static void fec_enet_mii(struct net_device *dev);
  static irqreturn_t fec_enet_interrupt(int irq, void * dev_id);
  static void fec_enet_tx(struct net_device *dev);
  static void fec_enet_rx(struct net_device *dev);
@@@ -194,20 -219,67 +194,20 @@@ static int fec_enet_close(struct net_de
  static void fec_restart(struct net_device *dev, int duplex);
  static void fec_stop(struct net_device *dev);
  
 +/* FEC MII MMFR bits definition */
 +#define FEC_MMFR_ST           (1 << 30)
 +#define FEC_MMFR_OP_READ      (2 << 28)
 +#define FEC_MMFR_OP_WRITE     (1 << 28)
 +#define FEC_MMFR_PA(v)                ((v & 0x1f) << 23)
 +#define FEC_MMFR_RA(v)                ((v & 0x1f) << 18)
 +#define FEC_MMFR_TA           (2 << 16)
 +#define FEC_MMFR_DATA(v)      (v & 0xffff)
  
 -/* MII processing.  We keep this as simple as possible.  Requests are
 - * placed on the list (if there is room).  When the request is finished
 - * by the MII, an optional function may be called.
 - */
 -typedef struct mii_list {
 -      uint    mii_regval;
 -      void    (*mii_func)(uint val, struct net_device *dev);
 -      struct  mii_list *mii_next;
 -} mii_list_t;
 -
 -#define               NMII    20
 -static mii_list_t     mii_cmds[NMII];
 -static mii_list_t     *mii_free;
 -static mii_list_t     *mii_head;
 -static mii_list_t     *mii_tail;
 -
 -static int    mii_queue(struct net_device *dev, int request,
 -                              void (*func)(uint, struct net_device *));
 -
 -/* Make MII read/write commands for the FEC */
 -#define mk_mii_read(REG)      (0x60020000 | ((REG & 0x1f) << 18))
 -#define mk_mii_write(REG, VAL)        (0x50020000 | ((REG & 0x1f) << 18) | \
 -                                              (VAL & 0xffff))
 -#define mk_mii_end    0
 +#define FEC_MII_TIMEOUT               10000
  
  /* Transmitter timeout */
  #define TX_TIMEOUT (2 * HZ)
  
 -/* Register definitions for the PHY */
 -
 -#define MII_REG_CR          0  /* Control Register                         */
 -#define MII_REG_SR          1  /* Status Register                          */
 -#define MII_REG_PHYIR1      2  /* PHY Identification Register 1            */
 -#define MII_REG_PHYIR2      3  /* PHY Identification Register 2            */
 -#define MII_REG_ANAR        4  /* A-N Advertisement Register               */
 -#define MII_REG_ANLPAR      5  /* A-N Link Partner Ability Register        */
 -#define MII_REG_ANER        6  /* A-N Expansion Register                   */
 -#define MII_REG_ANNPTR      7  /* A-N Next Page Transmit Register          */
 -#define MII_REG_ANLPRNPR    8  /* A-N Link Partner Received Next Page Reg. */
 -
 -/* values for phy_status */
 -
 -#define PHY_CONF_ANE  0x0001  /* 1 auto-negotiation enabled */
 -#define PHY_CONF_LOOP 0x0002  /* 1 loopback mode enabled */
 -#define PHY_CONF_SPMASK       0x00f0  /* mask for speed */
 -#define PHY_CONF_10HDX        0x0010  /* 10 Mbit half duplex supported */
 -#define PHY_CONF_10FDX        0x0020  /* 10 Mbit full duplex supported */
 -#define PHY_CONF_100HDX       0x0040  /* 100 Mbit half duplex supported */
 -#define PHY_CONF_100FDX       0x0080  /* 100 Mbit full duplex supported */
 -
 -#define PHY_STAT_LINK 0x0100  /* 1 up - 0 down */
 -#define PHY_STAT_FAULT        0x0200  /* 1 remote fault */
 -#define PHY_STAT_ANC  0x0400  /* 1 auto-negotiation complete  */
 -#define PHY_STAT_SPMASK       0xf000  /* mask for speed */
 -#define PHY_STAT_10HDX        0x1000  /* 10 Mbit half duplex selected */
 -#define PHY_STAT_10FDX        0x2000  /* 10 Mbit full duplex selected */
 -#define PHY_STAT_100HDX       0x4000  /* 100 Mbit half duplex selected */
 -#define PHY_STAT_100FDX       0x8000  /* 100 Mbit full duplex selected */
 -
 -
  static int
  fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
  {
                        | BD_ENET_TX_LAST | BD_ENET_TX_TC);
        bdp->cbd_sc = status;
  
 -      dev->trans_start = jiffies;
 -
        /* Trigger transmission start */
        writel(0, fep->hwp + FEC_X_DES_ACTIVE);
  
@@@ -332,6 -406,12 +332,6 @@@ fec_enet_interrupt(int irq, void * dev_
                        ret = IRQ_HANDLED;
                        fec_enet_tx(dev);
                }
 -
 -              if (int_events & FEC_ENET_MII) {
 -                      ret = IRQ_HANDLED;
 -                      fec_enet_mii(dev);
 -              }
 -
        } while (int_events);
  
        return ret;
@@@ -527,311 -607,827 +527,311 @@@ rx_processing_done
        spin_unlock(&fep->hw_lock);
  }
  
 -/* called from interrupt context */
 -static void
 -fec_enet_mii(struct net_device *dev)
 -{
 -      struct  fec_enet_private *fep;
 -      mii_list_t      *mip;
 -
 -      fep = netdev_priv(dev);
 -      spin_lock(&fep->mii_lock);
 -
 -      if ((mip = mii_head) == NULL) {
 -              printk("MII and no head!\n");
 -              goto unlock;
 -      }
 -
 -      if (mip->mii_func != NULL)
 -              (*(mip->mii_func))(readl(fep->hwp + FEC_MII_DATA), dev);
 -
 -      mii_head = mip->mii_next;
 -      mip->mii_next = mii_free;
 -      mii_free = mip;
 -
 -      if ((mip = mii_head) != NULL)
 -              writel(mip->mii_regval, fep->hwp + FEC_MII_DATA);
 -
 -unlock:
 -      spin_unlock(&fep->mii_lock);
 -}
 -
 -static int
 -mii_queue_unlocked(struct net_device *dev, int regval,
 -              void (*func)(uint, struct net_device *))
 +/* ------------------------------------------------------------------------- */
 +#ifdef CONFIG_M5272
 +static void __inline__ fec_get_mac(struct net_device *dev)
  {
 -      struct fec_enet_private *fep;
 -      mii_list_t      *mip;
 -      int             retval;
 -
 -      /* Add PHY address to register command */
 -      fep = netdev_priv(dev);
 +      struct fec_enet_private *fep = netdev_priv(dev);
 +      unsigned char *iap, tmpaddr[ETH_ALEN];
  
 -      regval |= fep->phy_addr << 23;
 -      retval = 0;
 -
 -      if ((mip = mii_free) != NULL) {
 -              mii_free = mip->mii_next;
 -              mip->mii_regval = regval;
 -              mip->mii_func = func;
 -              mip->mii_next = NULL;
 -              if (mii_head) {
 -                      mii_tail->mii_next = mip;
 -                      mii_tail = mip;
 -              } else {
 -                      mii_head = mii_tail = mip;
 -                      writel(regval, fep->hwp + FEC_MII_DATA);
 -              }
 +      if (FEC_FLASHMAC) {
 +              /*
 +               * Get MAC address from FLASH.
 +               * If it is all 1's or 0's, use the default.
 +               */
 +              iap = (unsigned char *)FEC_FLASHMAC;
 +              if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
 +                  (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
 +                      iap = fec_mac_default;
 +              if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
 +                  (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
 +                      iap = fec_mac_default;
        } else {
 -              retval = 1;
 +              *((unsigned long *) &tmpaddr[0]) = readl(fep->hwp + FEC_ADDR_LOW);
 +              *((unsigned short *) &tmpaddr[4]) = (readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
 +              iap = &tmpaddr[0];
        }
  
 -      return retval;
 -}
 -
 -static int
 -mii_queue(struct net_device *dev, int regval,
 -              void (*func)(uint, struct net_device *))
 -{
 -      struct fec_enet_private *fep;
 -      unsigned long   flags;
 -      int             retval;
 -      fep = netdev_priv(dev);
 -      spin_lock_irqsave(&fep->mii_lock, flags);
 -      retval = mii_queue_unlocked(dev, regval, func);
 -      spin_unlock_irqrestore(&fep->mii_lock, flags);
 -      return retval;
 -}
 -
 -static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c)
 -{
 -      if(!c)
 -              return;
 +      memcpy(dev->dev_addr, iap, ETH_ALEN);
  
 -      for (; c->mii_data != mk_mii_end; c++)
 -              mii_queue(dev, c->mii_data, c->funct);
 +      /* Adjust MAC if using default MAC address */
 +      if (iap == fec_mac_default)
 +               dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
  }
 +#endif
  
 -static void mii_parse_sr(uint mii_reg, struct net_device *dev)
 -{
 -      struct fec_enet_private *fep = netdev_priv(dev);
 -      volatile uint *s = &(fep->phy_status);
 -      uint status;
 -
 -      status = *s & ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC);
 -
 -      if (mii_reg & 0x0004)
 -              status |= PHY_STAT_LINK;
 -      if (mii_reg & 0x0010)
 -              status |= PHY_STAT_FAULT;
 -      if (mii_reg & 0x0020)
 -              status |= PHY_STAT_ANC;
 -      *s = status;
 -}
 +/* ------------------------------------------------------------------------- */
  
 -static void mii_parse_cr(uint mii_reg, struct net_device *dev)
 +/*
 + * Phy section
 + */
 +static void fec_enet_adjust_link(struct net_device *dev)
  {
        struct fec_enet_private *fep = netdev_priv(dev);
 -      volatile uint *s = &(fep->phy_status);
 -      uint status;
 -
 -      status = *s & ~(PHY_CONF_ANE | PHY_CONF_LOOP);
 -
 -      if (mii_reg & 0x1000)
 -              status |= PHY_CONF_ANE;
 -      if (mii_reg & 0x4000)
 -              status |= PHY_CONF_LOOP;
 -      *s = status;
 -}
 +      struct phy_device *phy_dev = fep->phy_dev;
 +      unsigned long flags;
  
 -static void mii_parse_anar(uint mii_reg, struct net_device *dev)
 -{
 -      struct fec_enet_private *fep = netdev_priv(dev);
 -      volatile uint *s = &(fep->phy_status);
 -      uint status;
 -
 -      status = *s & ~(PHY_CONF_SPMASK);
 -
 -      if (mii_reg & 0x0020)
 -              status |= PHY_CONF_10HDX;
 -      if (mii_reg & 0x0040)
 -              status |= PHY_CONF_10FDX;
 -      if (mii_reg & 0x0080)
 -              status |= PHY_CONF_100HDX;
 -      if (mii_reg & 0x00100)
 -              status |= PHY_CONF_100FDX;
 -      *s = status;
 -}
 +      int status_change = 0;
  
 -/* ------------------------------------------------------------------------- */
 -/* The Level one LXT970 is used by many boards                                     */
 +      spin_lock_irqsave(&fep->hw_lock, flags);
  
 -#define MII_LXT970_MIRROR    16  /* Mirror register           */
 -#define MII_LXT970_IER       17  /* Interrupt Enable Register */
 -#define MII_LXT970_ISR       18  /* Interrupt Status Register */
 -#define MII_LXT970_CONFIG    19  /* Configuration Register    */
 -#define MII_LXT970_CSR       20  /* Chip Status Register      */
 +      /* Prevent a state halted on mii error */
 +      if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
 +              phy_dev->state = PHY_RESUMING;
 +              goto spin_unlock;
 +      }
  
 -static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev)
 -{
 -      struct fec_enet_private *fep = netdev_priv(dev);
 -      volatile uint *s = &(fep->phy_status);
 -      uint status;
 +      /* Duplex link change */
 +      if (phy_dev->link) {
 +              if (fep->full_duplex != phy_dev->duplex) {
 +                      fec_restart(dev, phy_dev->duplex);
 +                      status_change = 1;
 +              }
 +      }
  
 -      status = *s & ~(PHY_STAT_SPMASK);
 -      if (mii_reg & 0x0800) {
 -              if (mii_reg & 0x1000)
 -                      status |= PHY_STAT_100FDX;
 +      /* Link on or off change */
 +      if (phy_dev->link != fep->link) {
 +              fep->link = phy_dev->link;
 +              if (phy_dev->link)
 +                      fec_restart(dev, phy_dev->duplex);
                else
 -                      status |= PHY_STAT_100HDX;
 -      } else {
 -              if (mii_reg & 0x1000)
 -                      status |= PHY_STAT_10FDX;
 -              else
 -                      status |= PHY_STAT_10HDX;
 +                      fec_stop(dev);
 +              status_change = 1;
        }
 -      *s = status;
 -}
 -
 -static phy_cmd_t const phy_cmd_lxt970_config[] = {
 -              { mk_mii_read(MII_REG_CR), mii_parse_cr },
 -              { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_lxt970_startup[] = { /* enable interrupts */
 -              { mk_mii_write(MII_LXT970_IER, 0x0002), NULL },
 -              { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_lxt970_ack_int[] = {
 -              /* read SR and ISR to acknowledge */
 -              { mk_mii_read(MII_REG_SR), mii_parse_sr },
 -              { mk_mii_read(MII_LXT970_ISR), NULL },
 -
 -              /* find out the current status */
 -              { mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_lxt970_shutdown[] = { /* disable interrupts */
 -              { mk_mii_write(MII_LXT970_IER, 0x0000), NULL },
 -              { mk_mii_end, }
 -      };
 -static phy_info_t const phy_info_lxt970 = {
 -      .id = 0x07810000,
 -      .name = "LXT970",
 -      .config = phy_cmd_lxt970_config,
 -      .startup = phy_cmd_lxt970_startup,
 -      .ack_int = phy_cmd_lxt970_ack_int,
 -      .shutdown = phy_cmd_lxt970_shutdown
 -};
  
 -/* ------------------------------------------------------------------------- */
 -/* The Level one LXT971 is used on some of my custom boards                  */
 -
 -/* register definitions for the 971 */
 +spin_unlock:
 +      spin_unlock_irqrestore(&fep->hw_lock, flags);
  
 -#define MII_LXT971_PCR       16  /* Port Control Register     */
 -#define MII_LXT971_SR2       17  /* Status Register 2         */
 -#define MII_LXT971_IER       18  /* Interrupt Enable Register */
 -#define MII_LXT971_ISR       19  /* Interrupt Status Register */
 -#define MII_LXT971_LCR       20  /* LED Control Register      */
 -#define MII_LXT971_TCR       30  /* Transmit Control Register */
 +      if (status_change)
 +              phy_print_status(phy_dev);
 +}
  
  /*
 - * I had some nice ideas of running the MDIO faster...
 - * The 971 should support 8MHz and I tried it, but things acted really
 - * weird, so 2.5 MHz ought to be enough for anyone...
 + * NOTE: a MII transaction is during around 25 us, so polling it...
   */
 -
 -static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev)
 +static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
  {
 -      struct fec_enet_private *fep = netdev_priv(dev);
 -      volatile uint *s = &(fep->phy_status);
 -      uint status;
 +      struct fec_enet_private *fep = bus->priv;
 +      int timeout = FEC_MII_TIMEOUT;
  
 -      status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
 +      fep->mii_timeout = 0;
  
 -      if (mii_reg & 0x0400) {
 -              fep->link = 1;
 -              status |= PHY_STAT_LINK;
 -      } else {
 -              fep->link = 0;
 -      }
 -      if (mii_reg & 0x0080)
 -              status |= PHY_STAT_ANC;
 -      if (mii_reg & 0x4000) {
 -              if (mii_reg & 0x0200)
 -                      status |= PHY_STAT_100FDX;
 -              else
 -                      status |= PHY_STAT_100HDX;
 -      } else {
 -              if (mii_reg & 0x0200)
 -                      status |= PHY_STAT_10FDX;
 -              else
 -                      status |= PHY_STAT_10HDX;
 +      /* clear MII end of transfer bit*/
 +      writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
 +
 +      /* start a read op */
 +      writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
 +              FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
 +              FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
 +
 +      /* wait for end of transfer */
 +      while (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_MII)) {
 +              cpu_relax();
 +              if (timeout-- < 0) {
 +                      fep->mii_timeout = 1;
 +                      printk(KERN_ERR "FEC: MDIO read timeout\n");
 +                      return -ETIMEDOUT;
 +              }
        }
 -      if (mii_reg & 0x0008)
 -              status |= PHY_STAT_FAULT;
  
 -      *s = status;
 +      /* return value */
 +      return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
  }
  
 -static phy_cmd_t const phy_cmd_lxt971_config[] = {
 -              /* limit to 10MBit because my prototype board
 -               * doesn't work with 100. */
 -              { mk_mii_read(MII_REG_CR), mii_parse_cr },
 -              { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
 -              { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_lxt971_startup[] = {  /* enable interrupts */
 -              { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL },
 -              { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
 -              { mk_mii_write(MII_LXT971_LCR, 0xd422), NULL }, /* LED config */
 -              /* Somehow does the 971 tell me that the link is down
 -               * the first read after power-up.
 -               * read here to get a valid value in ack_int */
 -              { mk_mii_read(MII_REG_SR), mii_parse_sr },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_lxt971_ack_int[] = {
 -              /* acknowledge the int before reading status ! */
 -              { mk_mii_read(MII_LXT971_ISR), NULL },
 -              /* find out the current status */
 -              { mk_mii_read(MII_REG_SR), mii_parse_sr },
 -              { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_lxt971_shutdown[] = { /* disable interrupts */
 -              { mk_mii_write(MII_LXT971_IER, 0x0000), NULL },
 -              { mk_mii_end, }
 -      };
 -static phy_info_t const phy_info_lxt971 = {
 -      .id = 0x0001378e,
 -      .name = "LXT971",
 -      .config = phy_cmd_lxt971_config,
 -      .startup = phy_cmd_lxt971_startup,
 -      .ack_int = phy_cmd_lxt971_ack_int,
 -      .shutdown = phy_cmd_lxt971_shutdown
 -};
 -
 -/* ------------------------------------------------------------------------- */
 -/* The Quality Semiconductor QS6612 is used on the RPX CLLF                  */
 -
 -/* register definitions */
 -
 -#define MII_QS6612_MCR       17  /* Mode Control Register      */
 -#define MII_QS6612_FTR       27  /* Factory Test Register      */
 -#define MII_QS6612_MCO       28  /* Misc. Control Register     */
 -#define MII_QS6612_ISR       29  /* Interrupt Source Register  */
 -#define MII_QS6612_IMR       30  /* Interrupt Mask Register    */
 -#define MII_QS6612_PCR       31  /* 100BaseTx PHY Control Reg. */
 -
 -static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev)
 +static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
 +                         u16 value)
  {
 -      struct fec_enet_private *fep = netdev_priv(dev);
 -      volatile uint *s = &(fep->phy_status);
 -      uint status;
 -
 -      status = *s & ~(PHY_STAT_SPMASK);
 -
 -      switch((mii_reg >> 2) & 7) {
 -      case 1: status |= PHY_STAT_10HDX; break;
 -      case 2: status |= PHY_STAT_100HDX; break;
 -      case 5: status |= PHY_STAT_10FDX; break;
 -      case 6: status |= PHY_STAT_100FDX; break;
 -}
 -
 -      *s = status;
 -}
 +      struct fec_enet_private *fep = bus->priv;
 +      int timeout = FEC_MII_TIMEOUT;
  
 -static phy_cmd_t const phy_cmd_qs6612_config[] = {
 -              /* The PHY powers up isolated on the RPX,
 -               * so send a command to allow operation.
 -               */
 -              { mk_mii_write(MII_QS6612_PCR, 0x0dc0), NULL },
 -
 -              /* parse cr and anar to get some info */
 -              { mk_mii_read(MII_REG_CR), mii_parse_cr },
 -              { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_qs6612_startup[] = {  /* enable interrupts */
 -              { mk_mii_write(MII_QS6612_IMR, 0x003a), NULL },
 -              { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_qs6612_ack_int[] = {
 -              /* we need to read ISR, SR and ANER to acknowledge */
 -              { mk_mii_read(MII_QS6612_ISR), NULL },
 -              { mk_mii_read(MII_REG_SR), mii_parse_sr },
 -              { mk_mii_read(MII_REG_ANER), NULL },
 -
 -              /* read pcr to get info */
 -              { mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_qs6612_shutdown[] = { /* disable interrupts */
 -              { mk_mii_write(MII_QS6612_IMR, 0x0000), NULL },
 -              { mk_mii_end, }
 -      };
 -static phy_info_t const phy_info_qs6612 = {
 -      .id = 0x00181440,
 -      .name = "QS6612",
 -      .config = phy_cmd_qs6612_config,
 -      .startup = phy_cmd_qs6612_startup,
 -      .ack_int = phy_cmd_qs6612_ack_int,
 -      .shutdown = phy_cmd_qs6612_shutdown
 -};
 +      fep->mii_timeout = 0;
  
 -/* ------------------------------------------------------------------------- */
 -/* AMD AM79C874 phy                                                          */
 +      /* clear MII end of transfer bit*/
 +      writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
  
 -/* register definitions for the 874 */
 +      /* start a read op */
 +      writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
 +              FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
 +              FEC_MMFR_TA | FEC_MMFR_DATA(value),
 +              fep->hwp + FEC_MII_DATA);
 +
 +      /* wait for end of transfer */
 +      while (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_MII)) {
 +              cpu_relax();
 +              if (timeout-- < 0) {
 +                      fep->mii_timeout = 1;
 +                      printk(KERN_ERR "FEC: MDIO write timeout\n");
 +                      return -ETIMEDOUT;
 +              }
 +      }
  
 -#define MII_AM79C874_MFR       16  /* Miscellaneous Feature Register */
 -#define MII_AM79C874_ICSR      17  /* Interrupt/Status Register      */
 -#define MII_AM79C874_DR        18  /* Diagnostic Register            */
 -#define MII_AM79C874_PMLR      19  /* Power and Loopback Register    */
 -#define MII_AM79C874_MCR       21  /* ModeControl Register           */
 -#define MII_AM79C874_DC        23  /* Disconnect Counter             */
 -#define MII_AM79C874_REC       24  /* Recieve Error Counter          */
 +      return 0;
 +}
  
 -static void mii_parse_am79c874_dr(uint mii_reg, struct net_device *dev)
 +static int fec_enet_mdio_reset(struct mii_bus *bus)
  {
 -      struct fec_enet_private *fep = netdev_priv(dev);
 -      volatile uint *s = &(fep->phy_status);
 -      uint status;
 -
 -      status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_ANC);
 -
 -      if (mii_reg & 0x0080)
 -              status |= PHY_STAT_ANC;
 -      if (mii_reg & 0x0400)
 -              status |= ((mii_reg & 0x0800) ? PHY_STAT_100FDX : PHY_STAT_100HDX);
 -      else
 -              status |= ((mii_reg & 0x0800) ? PHY_STAT_10FDX : PHY_STAT_10HDX);
 -
 -      *s = status;
 +      return 0;
  }
  
 -static phy_cmd_t const phy_cmd_am79c874_config[] = {
 -              { mk_mii_read(MII_REG_CR), mii_parse_cr },
 -              { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
 -              { mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_am79c874_startup[] = {  /* enable interrupts */
 -              { mk_mii_write(MII_AM79C874_ICSR, 0xff00), NULL },
 -              { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
 -              { mk_mii_read(MII_REG_SR), mii_parse_sr },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_am79c874_ack_int[] = {
 -              /* find out the current status */
 -              { mk_mii_read(MII_REG_SR), mii_parse_sr },
 -              { mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr },
 -              /* we only need to read ISR to acknowledge */
 -              { mk_mii_read(MII_AM79C874_ICSR), NULL },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_am79c874_shutdown[] = { /* disable interrupts */
 -              { mk_mii_write(MII_AM79C874_ICSR, 0x0000), NULL },
 -              { mk_mii_end, }
 -      };
 -static phy_info_t const phy_info_am79c874 = {
 -      .id = 0x00022561,
 -      .name = "AM79C874",
 -      .config = phy_cmd_am79c874_config,
 -      .startup = phy_cmd_am79c874_startup,
 -      .ack_int = phy_cmd_am79c874_ack_int,
 -      .shutdown = phy_cmd_am79c874_shutdown
 -};
 -
 -
 -/* ------------------------------------------------------------------------- */
 -/* Kendin KS8721BL phy                                                       */
 -
 -/* register definitions for the 8721 */
 -
 -#define MII_KS8721BL_RXERCR   21
 -#define MII_KS8721BL_ICSR     27
 -#define       MII_KS8721BL_PHYCR      31
 -
 -static phy_cmd_t const phy_cmd_ks8721bl_config[] = {
 -              { mk_mii_read(MII_REG_CR), mii_parse_cr },
 -              { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_ks8721bl_startup[] = {  /* enable interrupts */
 -              { mk_mii_write(MII_KS8721BL_ICSR, 0xff00), NULL },
 -              { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
 -              { mk_mii_read(MII_REG_SR), mii_parse_sr },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_ks8721bl_ack_int[] = {
 -              /* find out the current status */
 -              { mk_mii_read(MII_REG_SR), mii_parse_sr },
 -              /* we only need to read ISR to acknowledge */
 -              { mk_mii_read(MII_KS8721BL_ICSR), NULL },
 -              { mk_mii_end, }
 -      };
 -static phy_cmd_t const phy_cmd_ks8721bl_shutdown[] = { /* disable interrupts */
 -              { mk_mii_write(MII_KS8721BL_ICSR, 0x0000), NULL },
 -              { mk_mii_end, }
 -      };
 -static phy_info_t const phy_info_ks8721bl = {
 -      .id = 0x00022161,
 -      .name = "KS8721BL",
 -      .config = phy_cmd_ks8721bl_config,
 -      .startup = phy_cmd_ks8721bl_startup,
 -      .ack_int = phy_cmd_ks8721bl_ack_int,
 -      .shutdown = phy_cmd_ks8721bl_shutdown
 -};
 -
 -/* ------------------------------------------------------------------------- */
 -/* register definitions for the DP83848 */
 -
 -#define MII_DP8384X_PHYSTST    16  /* PHY Status Register */
 -
 -static void mii_parse_dp8384x_sr2(uint mii_reg, struct net_device *dev)
 +static int fec_enet_mii_probe(struct net_device *dev)
  {
        struct fec_enet_private *fep = netdev_priv(dev);
 -      volatile uint *s = &(fep->phy_status);
 -
 -      *s &= ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
 -
 -      /* Link up */
 -      if (mii_reg & 0x0001) {
 -              fep->link = 1;
 -              *s |= PHY_STAT_LINK;
 -      } else
 -              fep->link = 0;
 -      /* Status of link */
 -      if (mii_reg & 0x0010)   /* Autonegotioation complete */
 -              *s |= PHY_STAT_ANC;
 -      if (mii_reg & 0x0002) {   /* 10MBps? */
 -              if (mii_reg & 0x0004)   /* Full Duplex? */
 -                      *s |= PHY_STAT_10FDX;
 -              else
 -                      *s |= PHY_STAT_10HDX;
 -      } else {                  /* 100 Mbps? */
 -              if (mii_reg & 0x0004)   /* Full Duplex? */
 -                      *s |= PHY_STAT_100FDX;
 -              else
 -                      *s |= PHY_STAT_100HDX;
 -      }
 -      if (mii_reg & 0x0008)
 -              *s |= PHY_STAT_FAULT;
 -}
 +      struct phy_device *phy_dev = NULL;
 +      int phy_addr;
  
 -static phy_info_t phy_info_dp83848= {
 -      0x020005c9,
 -      "DP83848",
 -
 -      (const phy_cmd_t []) {  /* config */
 -              { mk_mii_read(MII_REG_CR), mii_parse_cr },
 -              { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
 -              { mk_mii_read(MII_DP8384X_PHYSTST), mii_parse_dp8384x_sr2 },
 -              { mk_mii_end, }
 -      },
 -      (const phy_cmd_t []) {  /* startup - enable interrupts */
 -              { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
 -              { mk_mii_read(MII_REG_SR), mii_parse_sr },
 -              { mk_mii_end, }
 -      },
 -      (const phy_cmd_t []) { /* ack_int - never happens, no interrupt */
 -              { mk_mii_end, }
 -      },
 -      (const phy_cmd_t []) {  /* shutdown */
 -              { mk_mii_end, }
 -      },
 -};
 +      /* find the first phy */
 +      for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
 +              if (fep->mii_bus->phy_map[phy_addr]) {
 +                      phy_dev = fep->mii_bus->phy_map[phy_addr];
 +                      break;
 +              }
 +      }
  
 -static phy_info_t phy_info_lan8700 = {
 -      0x0007C0C,
 -      "LAN8700",
 -      (const phy_cmd_t []) { /* config */
 -              { mk_mii_read(MII_REG_CR), mii_parse_cr },
 -              { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
 -              { mk_mii_end, }
 -      },
 -      (const phy_cmd_t []) { /* startup */
 -              { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
 -              { mk_mii_read(MII_REG_SR), mii_parse_sr },
 -              { mk_mii_end, }
 -      },
 -      (const phy_cmd_t []) { /* act_int */
 -              { mk_mii_end, }
 -      },
 -      (const phy_cmd_t []) { /* shutdown */
 -              { mk_mii_end, }
 -      },
 -};
 -/* ------------------------------------------------------------------------- */
 +      if (!phy_dev) {
 +              printk(KERN_ERR "%s: no PHY found\n", dev->name);
 +              return -ENODEV;
 +      }
  
 -static phy_info_t const * const phy_info[] = {
 -      &phy_info_lxt970,
 -      &phy_info_lxt971,
 -      &phy_info_qs6612,
 -      &phy_info_am79c874,
 -      &phy_info_ks8721bl,
 -      &phy_info_dp83848,
 -      &phy_info_lan8700,
 -      NULL
 -};
 +      /* attach the mac to the phy */
 +      phy_dev = phy_connect(dev, dev_name(&phy_dev->dev),
 +                           &fec_enet_adjust_link, 0,
 +                           PHY_INTERFACE_MODE_MII);
 +      if (IS_ERR(phy_dev)) {
 +              printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
 +              return PTR_ERR(phy_dev);
 +      }
  
 -/* ------------------------------------------------------------------------- */
 -#ifdef HAVE_mii_link_interrupt
 -static irqreturn_t
 -mii_link_interrupt(int irq, void * dev_id);
 +      /* mask with MAC supported features */
 +      phy_dev->supported &= PHY_BASIC_FEATURES;
 +      phy_dev->advertising = phy_dev->supported;
  
 -/*
 - *    This is specific to the MII interrupt setup of the M5272EVB.
 - */
 -static void __inline__ fec_request_mii_intr(struct net_device *dev)
 -{
 -      if (request_irq(66, mii_link_interrupt, IRQF_DISABLED, "fec(MII)", dev) != 0)
 -              printk("FEC: Could not allocate fec(MII) IRQ(66)!\n");
 -}
 +      fep->phy_dev = phy_dev;
 +      fep->link = 0;
 +      fep->full_duplex = 0;
  
 -static void __inline__ fec_disable_phy_intr(struct net_device *dev)
 -{
 -      free_irq(66, dev);
 +      return 0;
  }
 -#endif
  
 -#ifdef CONFIG_M5272
 -static void __inline__ fec_get_mac(struct net_device *dev)
 +static int fec_enet_mii_init(struct platform_device *pdev)
  {
 +      struct net_device *dev = platform_get_drvdata(pdev);
        struct fec_enet_private *fep = netdev_priv(dev);
 -      unsigned char *iap, tmpaddr[ETH_ALEN];
 -
 -      if (FEC_FLASHMAC) {
 -              /*
 -               * Get MAC address from FLASH.
 -               * If it is all 1's or 0's, use the default.
 -               */
 -              iap = (unsigned char *)FEC_FLASHMAC;
 -              if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
 -                  (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
 -                      iap = fec_mac_default;
 -              if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
 -                  (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
 -                      iap = fec_mac_default;
 -      } else {
 -              *((unsigned long *) &tmpaddr[0]) = readl(fep->hwp + FEC_ADDR_LOW);
 -              *((unsigned short *) &tmpaddr[4]) = (readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
 -              iap = &tmpaddr[0];
 -      }
 +      int err = -ENXIO, i;
  
 -      memcpy(dev->dev_addr, iap, ETH_ALEN);
 -
 -      /* Adjust MAC if using default MAC address */
 -      if (iap == fec_mac_default)
 -               dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
 -}
 -#endif
 -
 -/* ------------------------------------------------------------------------- */
 +      fep->mii_timeout = 0;
  
 -static void mii_display_status(struct net_device *dev)
 -{
 -      struct fec_enet_private *fep = netdev_priv(dev);
 -      volatile uint *s = &(fep->phy_status);
 +      /*
 +       * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
 +       */
 +      fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000) << 1;
 +      writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
  
 -      if (!fep->link && !fep->old_link) {
 -              /* Link is still down - don't print anything */
 -              return;
 +      fep->mii_bus = mdiobus_alloc();
 +      if (fep->mii_bus == NULL) {
 +              err = -ENOMEM;
 +              goto err_out;
        }
  
 -      printk("%s: status: ", dev->name);
 -
 -      if (!fep->link) {
 -              printk("link down");
 -      } else {
 -              printk("link up");
 -
 -              switch(*s & PHY_STAT_SPMASK) {
 -              case PHY_STAT_100FDX: printk(", 100MBit Full Duplex"); break;
 -              case PHY_STAT_100HDX: printk(", 100MBit Half Duplex"); break;
 -              case PHY_STAT_10FDX: printk(", 10MBit Full Duplex"); break;
 -              case PHY_STAT_10HDX: printk(", 10MBit Half Duplex"); break;
 -              default:
 -                      printk(", Unknown speed/duplex");
 -              }
 -
 -              if (*s & PHY_STAT_ANC)
 -                      printk(", auto-negotiation complete");
 +      fep->mii_bus->name = "fec_enet_mii_bus";
 +      fep->mii_bus->read = fec_enet_mdio_read;
 +      fep->mii_bus->write = fec_enet_mdio_write;
 +      fep->mii_bus->reset = fec_enet_mdio_reset;
 +      snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
 +      fep->mii_bus->priv = fep;
 +      fep->mii_bus->parent = &pdev->dev;
 +
 +      fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
 +      if (!fep->mii_bus->irq) {
 +              err = -ENOMEM;
 +              goto err_out_free_mdiobus;
        }
  
 -      if (*s & PHY_STAT_FAULT)
 -              printk(", remote fault");
 -
 -      printk(".\n");
 -}
 +      for (i = 0; i < PHY_MAX_ADDR; i++)
 +              fep->mii_bus->irq[i] = PHY_POLL;
  
 -static void mii_display_config(struct work_struct *work)
 -{
 -      struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
 -      struct net_device *dev = fep->netdev;
 -      uint status = fep->phy_status;
 -
 -      /*
 -      ** When we get here, phy_task is already removed from
 -      ** the workqueue.  It is thus safe to allow to reuse it.
 -      */
 -      fep->mii_phy_task_queued = 0;
 -      printk("%s: config: auto-negotiation ", dev->name);
 -
 -      if (status & PHY_CONF_ANE)
 -              printk("on");
 -      else
 -              printk("off");
 +      platform_set_drvdata(dev, fep->mii_bus);
  
 -      if (status & PHY_CONF_100FDX)
 -              printk(", 100FDX");
 -      if (status & PHY_CONF_100HDX)
 -              printk(", 100HDX");
 -      if (status & PHY_CONF_10FDX)
 -              printk(", 10FDX");
 -      if (status & PHY_CONF_10HDX)
 -              printk(", 10HDX");
 -      if (!(status & PHY_CONF_SPMASK))
 -              printk(", No speed/duplex selected?");
 +      if (mdiobus_register(fep->mii_bus))
 +              goto err_out_free_mdio_irq;
  
 -      if (status & PHY_CONF_LOOP)
 -              printk(", loopback enabled");
 +      if (fec_enet_mii_probe(dev) != 0)
 +              goto err_out_unregister_bus;
  
 -      printk(".\n");
 +      return 0;
  
 -      fep->sequence_done = 1;
 +err_out_unregister_bus:
 +      mdiobus_unregister(fep->mii_bus);
 +err_out_free_mdio_irq:
 +      kfree(fep->mii_bus->irq);
 +err_out_free_mdiobus:
 +      mdiobus_free(fep->mii_bus);
 +err_out:
 +      return err;
  }
  
 -static void mii_relink(struct work_struct *work)
 +static void fec_enet_mii_remove(struct fec_enet_private *fep)
  {
 -      struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
 -      struct net_device *dev = fep->netdev;
 -      int duplex;
 -
 -      /*
 -      ** When we get here, phy_task is already removed from
 -      ** the workqueue.  It is thus safe to allow to reuse it.
 -      */
 -      fep->mii_phy_task_queued = 0;
 -      fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0;
 -      mii_display_status(dev);
 -      fep->old_link = fep->link;
 -
 -      if (fep->link) {
 -              duplex = 0;
 -              if (fep->phy_status
 -                  & (PHY_STAT_100FDX | PHY_STAT_10FDX))
 -                      duplex = 1;
 -              fec_restart(dev, duplex);
 -      } else
 -              fec_stop(dev);
 +      if (fep->phy_dev)
 +              phy_disconnect(fep->phy_dev);
 +      mdiobus_unregister(fep->mii_bus);
 +      kfree(fep->mii_bus->irq);
 +      mdiobus_free(fep->mii_bus);
  }
  
 -/* mii_queue_relink is called in interrupt context from mii_link_interrupt */
 -static void mii_queue_relink(uint mii_reg, struct net_device *dev)
 +static int fec_enet_get_settings(struct net_device *dev,
 +                                struct ethtool_cmd *cmd)
  {
        struct fec_enet_private *fep = netdev_priv(dev);
 +      struct phy_device *phydev = fep->phy_dev;
  
 -      /*
 -       * We cannot queue phy_task twice in the workqueue.  It
 -       * would cause an endless loop in the workqueue.
 -       * Fortunately, if the last mii_relink entry has not yet been
 -       * executed now, it will do the job for the current interrupt,
 -       * which is just what we want.
 -       */
 -      if (fep->mii_phy_task_queued)
 -              return;
 +      if (!phydev)
 +              return -ENODEV;
  
 -      fep->mii_phy_task_queued = 1;
 -      INIT_WORK(&fep->phy_task, mii_relink);
 -      schedule_work(&fep->phy_task);
 +      return phy_ethtool_gset(phydev, cmd);
  }
  
 -/* mii_queue_config is called in interrupt context from fec_enet_mii */
 -static void mii_queue_config(uint mii_reg, struct net_device *dev)
 +static int fec_enet_set_settings(struct net_device *dev,
 +                               struct ethtool_cmd *cmd)
  {
        struct fec_enet_private *fep = netdev_priv(dev);
 +      struct phy_device *phydev = fep->phy_dev;
  
 -      if (fep->mii_phy_task_queued)
 -              return;
 +      if (!phydev)
 +              return -ENODEV;
  
 -      fep->mii_phy_task_queued = 1;
 -      INIT_WORK(&fep->phy_task, mii_display_config);
 -      schedule_work(&fep->phy_task);
 +      return phy_ethtool_sset(phydev, cmd);
  }
  
 -phy_cmd_t const phy_cmd_relink[] = {
 -      { mk_mii_read(MII_REG_CR), mii_queue_relink },
 -      { mk_mii_end, }
 -      };
 -phy_cmd_t const phy_cmd_config[] = {
 -      { mk_mii_read(MII_REG_CR), mii_queue_config },
 -      { mk_mii_end, }
 -      };
 -
 -/* Read remainder of PHY ID. */
 -static void
 -mii_discover_phy3(uint mii_reg, struct net_device *dev)
 +static void fec_enet_get_drvinfo(struct net_device *dev,
 +                               struct ethtool_drvinfo *info)
  {
 -      struct fec_enet_private *fep;
 -      int i;
 -
 -      fep = netdev_priv(dev);
 -      fep->phy_id |= (mii_reg & 0xffff);
 -      printk("fec: PHY @ 0x%x, ID 0x%08x", fep->phy_addr, fep->phy_id);
 -
 -      for(i = 0; phy_info[i]; i++) {
 -              if(phy_info[i]->id == (fep->phy_id >> 4))
 -                      break;
 -      }
 -
 -      if (phy_info[i])
 -              printk(" -- %s\n", phy_info[i]->name);
 -      else
 -              printk(" -- unknown PHY!\n");
 +      struct fec_enet_private *fep = netdev_priv(dev);
  
 -      fep->phy = phy_info[i];
 -      fep->phy_id_done = 1;
 +      strcpy(info->driver, fep->pdev->dev.driver->name);
 +      strcpy(info->version, "Revision: 1.0");
 +      strcpy(info->bus_info, dev_name(&dev->dev));
  }
  
 -/* Scan all of the MII PHY addresses looking for someone to respond
 - * with a valid ID.  This usually happens quickly.
 - */
 -static void
 -mii_discover_phy(uint mii_reg, struct net_device *dev)
 -{
 -      struct fec_enet_private *fep;
 -      uint phytype;
 -
 -      fep = netdev_priv(dev);
 -
 -      if (fep->phy_addr < 32) {
 -              if ((phytype = (mii_reg & 0xffff)) != 0xffff && phytype != 0) {
 -
 -                      /* Got first part of ID, now get remainder */
 -                      fep->phy_id = phytype << 16;
 -                      mii_queue_unlocked(dev, mk_mii_read(MII_REG_PHYIR2),
 -                                                      mii_discover_phy3);
 -              } else {
 -                      fep->phy_addr++;
 -                      mii_queue_unlocked(dev, mk_mii_read(MII_REG_PHYIR1),
 -                                                      mii_discover_phy);
 -              }
 -      } else {
 -              printk("FEC: No PHY device found.\n");
 -              /* Disable external MII interface */
 -              writel(0, fep->hwp + FEC_MII_SPEED);
 -              fep->phy_speed = 0;
 -#ifdef HAVE_mii_link_interrupt
 -              fec_disable_phy_intr(dev);
 -#endif
 -      }
 -}
 +static struct ethtool_ops fec_enet_ethtool_ops = {
 +      .get_settings           = fec_enet_get_settings,
 +      .set_settings           = fec_enet_set_settings,
 +      .get_drvinfo            = fec_enet_get_drvinfo,
 +      .get_link               = ethtool_op_get_link,
 +};
  
 -/* This interrupt occurs when the PHY detects a link change */
 -#ifdef HAVE_mii_link_interrupt
 -static irqreturn_t
 -mii_link_interrupt(int irq, void * dev_id)
 +static int fec_enet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
  {
 -      struct  net_device *dev = dev_id;
        struct fec_enet_private *fep = netdev_priv(dev);
 +      struct phy_device *phydev = fep->phy_dev;
  
 -      mii_do_cmd(dev, fep->phy->ack_int);
 -      mii_do_cmd(dev, phy_cmd_relink);  /* restart and display status */
 +      if (!netif_running(dev))
 +              return -EINVAL;
  
 -      return IRQ_HANDLED;
 +      if (!phydev)
 +              return -ENODEV;
 +
 +      return phy_mii_ioctl(phydev, if_mii(rq), cmd);
  }
 -#endif
  
  static void fec_enet_free_buffers(struct net_device *dev)
  {
@@@ -913,8 -1509,35 +913,8 @@@ fec_enet_open(struct net_device *dev
        if (ret)
                return ret;
  
 -      fep->sequence_done = 0;
 -      fep->link = 0;
 -
 -      fec_restart(dev, 1);
 -
 -      if (fep->phy) {
 -              mii_do_cmd(dev, fep->phy->ack_int);
 -              mii_do_cmd(dev, fep->phy->config);
 -              mii_do_cmd(dev, phy_cmd_config);  /* display configuration */
 -
 -              /* Poll until the PHY tells us its configuration
 -               * (not link state).
 -               * Request is initiated by mii_do_cmd above, but answer
 -               * comes by interrupt.
 -               * This should take about 25 usec per register at 2.5 MHz,
 -               * and we read approximately 5 registers.
 -               */
 -              while(!fep->sequence_done)
 -                      schedule();
 -
 -              mii_do_cmd(dev, fep->phy->startup);
 -      }
 -
 -      /* Set the initial link state to true. A lot of hardware
 -       * based on this device does not implement a PHY interrupt,
 -       * so we are never notified of link change.
 -       */
 -      fep->link = 1;
 -
 +      /* schedule a link state check */
 +      phy_start(fep->phy_dev);
        netif_start_queue(dev);
        fep->opened = 1;
        return 0;
@@@ -927,7 -1550,6 +927,7 @@@ fec_enet_close(struct net_device *dev
  
        /* Don't know what to do yet. */
        fep->opened = 0;
 +      phy_stop(fep->phy_dev);
        netif_stop_queue(dev);
        fec_stop(dev);
  
  static void set_multicast_list(struct net_device *dev)
  {
        struct fec_enet_private *fep = netdev_priv(dev);
 -      struct dev_mc_list *dmi;
 +      struct netdev_hw_addr *ha;
        unsigned int i, bit, data, crc, tmp;
        unsigned char hash;
  
        writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
        writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
  
 -      netdev_for_each_mc_addr(dmi, dev) {
 +      netdev_for_each_mc_addr(ha, dev) {
                /* Only support group multicast for now */
 -              if (!(dmi->dmi_addr[0] & 1))
 +              if (!(ha->addr[0] & 1))
                        continue;
  
                /* calculate crc32 value of mac address */
                crc = 0xffffffff;
  
 -              for (i = 0; i < dmi->dmi_addrlen; i++) {
 -                      data = dmi->dmi_addr[i];
 +              for (i = 0; i < dev->addr_len; i++) {
 +                      data = ha->addr[i];
                        for (bit = 0; bit < 8; bit++, data >>= 1) {
                                crc = (crc >> 1) ^
                                (((crc ^ data) & 1) ? CRC32_POLY : 0);
@@@ -1031,7 -1653,7 +1031,7 @@@ fec_set_mac_address(struct net_device *
                (dev->dev_addr[1] << 16) | (dev->dev_addr[0] << 24),
                fep->hwp + FEC_ADDR_LOW);
        writel((dev->dev_addr[5] << 16) | (dev->dev_addr[4] << 24),
-               fep + FEC_ADDR_HIGH);
+               fep->hwp + FEC_ADDR_HIGH);
        return 0;
  }
  
@@@ -1044,7 -1666,6 +1044,7 @@@ static const struct net_device_ops fec_
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_tx_timeout         = fec_timeout,
        .ndo_set_mac_address    = fec_set_mac_address,
 +      .ndo_do_ioctl           = fec_enet_ioctl,
  };
  
   /*
@@@ -1068,6 -1689,7 +1068,6 @@@ static int fec_enet_init(struct net_dev
        }
  
        spin_lock_init(&fep->hw_lock);
 -      spin_lock_init(&fep->mii_lock);
  
        fep->index = index;
        fep->hwp = (void __iomem *)dev->base_addr;
        fep->rx_bd_base = cbd_base;
        fep->tx_bd_base = cbd_base + RX_RING_SIZE;
  
        /* The FEC Ethernet specific entries in the device structure */
        dev->watchdog_timeo = TX_TIMEOUT;
        dev->netdev_ops = &fec_netdev_ops;
 -
 -      for (i=0; i<NMII-1; i++)
 -              mii_cmds[i].mii_next = &mii_cmds[i+1];
 -      mii_free = mii_cmds;
 -
 -      /* Set MII speed to 2.5 MHz */
 -      fep->phy_speed = ((((clk_get_rate(fep->clk) / 2 + 4999999)
 -                                      / 2500000) / 2) & 0x3F) << 1;
 +      dev->ethtool_ops = &fec_enet_ethtool_ops;
  
        /* Initialize the receive buffer descriptors. */
        bdp = fep->rx_bd_base;
  
        fec_restart(dev, 0);
  
 -      /* Queue up command to detect the PHY and initialize the
 -       * remainder of the interface.
 -       */
 -      fep->phy_id_done = 0;
 -      fep->phy_addr = 0;
 -      mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);
 -
        return 0;
  }
  
@@@ -1196,7 -1835,8 +1196,7 @@@ fec_restart(struct net_device *dev, in
        writel(0, fep->hwp + FEC_R_DES_ACTIVE);
  
        /* Enable interrupts we wish to service */
 -      writel(FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII,
 -                      fep->hwp + FEC_IMASK);
 +      writel(FEC_ENET_TXF | FEC_ENET_RXF, fep->hwp + FEC_IMASK);
  }
  
  static void
@@@ -1219,6 -1859,7 +1219,6 @@@ fec_stop(struct net_device *dev
        /* Clear outstanding MII command interrupts. */
        writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
  
 -      writel(FEC_ENET_MII, fep->hwp + FEC_IMASK);
        writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
  }
  
@@@ -1250,7 -1891,6 +1250,7 @@@ fec_probe(struct platform_device *pdev
        memset(fep, 0, sizeof(*fep));
  
        ndev->base_addr = (unsigned long)ioremap(r->start, resource_size(r));
 +      fep->pdev = pdev;
  
        if (!ndev->base_addr) {
                ret = -ENOMEM;
        if (ret)
                goto failed_init;
  
 +      ret = fec_enet_mii_init(pdev);
 +      if (ret)
 +              goto failed_mii_init;
 +
        ret = register_netdev(ndev);
        if (ret)
                goto failed_register;
  
 +      printk(KERN_INFO "%s: Freescale FEC PHY driver [%s] "
 +              "(mii_bus:phy_addr=%s, irq=%d)\n", ndev->name,
 +              fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
 +              fep->phy_dev->irq);
 +
        return 0;
  
  failed_register:
 +      fec_enet_mii_remove(fep);
 +failed_mii_init:
  failed_init:
        clk_disable(fep->clk);
        clk_put(fep->clk);
@@@ -1330,7 -1959,6 +1330,7 @@@ fec_drv_remove(struct platform_device *
        platform_set_drvdata(pdev, NULL);
  
        fec_stop(ndev);
 +      fec_enet_mii_remove(fep);
        clk_disable(fep->clk);
        clk_put(fep->clk);
        iounmap((void __iomem *)ndev->base_addr);
diff --combined drivers/net/gianfar.c
@@@ -82,7 -82,6 +82,7 @@@
  #include <linux/tcp.h>
  #include <linux/udp.h>
  #include <linux/in.h>
 +#include <linux/net_tstamp.h>
  
  #include <asm/io.h>
  #include <asm/irq.h>
@@@ -378,13 -377,6 +378,13 @@@ static void gfar_init_mac(struct net_de
                rctrl |= RCTRL_PADDING(priv->padding);
        }
  
 +      /* Insert receive time stamps into padding alignment bytes */
 +      if (priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER) {
 +              rctrl &= ~RCTRL_PAL_MASK;
 +              rctrl |= RCTRL_PRSDEP_INIT | RCTRL_TS_ENABLE | RCTRL_PADDING(8);
 +              priv->padding = 8;
 +      }
 +
        /* keep vlan related bits if it's enabled */
        if (priv->vlgrp) {
                rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT;
@@@ -509,8 -501,7 +509,8 @@@ void unlock_tx_qs(struct gfar_private *
  /* Returns 1 if incoming frames use an FCB */
  static inline int gfar_uses_fcb(struct gfar_private *priv)
  {
 -      return priv->vlgrp || priv->rx_csum_enable;
 +      return priv->vlgrp || priv->rx_csum_enable ||
 +              (priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER);
  }
  
  static void free_tx_pointers(struct gfar_private *priv)
@@@ -747,8 -738,7 +747,8 @@@ static int gfar_of_init(struct of_devic
                        FSL_GIANFAR_DEV_HAS_CSUM |
                        FSL_GIANFAR_DEV_HAS_VLAN |
                        FSL_GIANFAR_DEV_HAS_MAGIC_PACKET |
 -                      FSL_GIANFAR_DEV_HAS_EXTENDED_HASH;
 +                      FSL_GIANFAR_DEV_HAS_EXTENDED_HASH |
 +                      FSL_GIANFAR_DEV_HAS_TIMER;
  
        ctype = of_get_property(np, "phy-connection-type", NULL);
  
@@@ -778,48 -768,6 +778,48 @@@ err_grp_init
        return err;
  }
  
 +static int gfar_hwtstamp_ioctl(struct net_device *netdev,
 +                      struct ifreq *ifr, int cmd)
 +{
 +      struct hwtstamp_config config;
 +      struct gfar_private *priv = netdev_priv(netdev);
 +
 +      if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
 +              return -EFAULT;
 +
 +      /* reserved for future extensions */
 +      if (config.flags)
 +              return -EINVAL;
 +
 +      switch (config.tx_type) {
 +      case HWTSTAMP_TX_OFF:
 +              priv->hwts_tx_en = 0;
 +              break;
 +      case HWTSTAMP_TX_ON:
 +              if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER))
 +                      return -ERANGE;
 +              priv->hwts_tx_en = 1;
 +              break;
 +      default:
 +              return -ERANGE;
 +      }
 +
 +      switch (config.rx_filter) {
 +      case HWTSTAMP_FILTER_NONE:
 +              priv->hwts_rx_en = 0;
 +              break;
 +      default:
 +              if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER))
 +                      return -ERANGE;
 +              priv->hwts_rx_en = 1;
 +              config.rx_filter = HWTSTAMP_FILTER_ALL;
 +              break;
 +      }
 +
 +      return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
 +              -EFAULT : 0;
 +}
 +
  /* Ioctl MII Interface */
  static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
  {
        if (!netif_running(dev))
                return -EINVAL;
  
 +      if (cmd == SIOCSHWTSTAMP)
 +              return gfar_hwtstamp_ioctl(dev, rq, cmd);
 +
        if (!priv->phydev)
                return -ENODEV;
  
@@@ -1033,8 -978,7 +1033,8 @@@ static int gfar_probe(struct of_device 
        else
                priv->padding = 0;
  
 -      if (dev->features & NETIF_F_IP_CSUM)
 +      if (dev->features & NETIF_F_IP_CSUM ||
 +                      priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)
                dev->hard_header_len += GMAC_FCB_LEN;
  
        /* Program the isrg regs only if number of grps > 1 */
@@@ -1705,6 -1649,7 +1705,7 @@@ static void free_skb_resources(struct g
                        sizeof(struct rxbd8) * priv->total_rx_ring_size,
                        priv->tx_queue[0]->tx_bd_base,
                        priv->tx_queue[0]->tx_bd_dma_base);
+       skb_queue_purge(&priv->rx_recycle);
  }
  
  void gfar_start(struct net_device *dev)
                gfar_write(&regs->imask, IMASK_DEFAULT);
        }
  
 -      dev->trans_start = jiffies;
 +      dev->trans_start = jiffies; /* prevent tx timeout */
  }
  
  void gfar_configure_coalescing(struct gfar_private *priv,
@@@ -1978,29 -1923,23 +1979,29 @@@ static int gfar_start_xmit(struct sk_bu
        struct netdev_queue *txq;
        struct gfar __iomem *regs = NULL;
        struct txfcb *fcb = NULL;
 -      struct txbd8 *txbdp, *txbdp_start, *base;
 +      struct txbd8 *txbdp, *txbdp_start, *base, *txbdp_tstamp = NULL;
        u32 lstatus;
 -      int i, rq = 0;
 +      int i, rq = 0, do_tstamp = 0;
        u32 bufaddr;
        unsigned long flags;
 -      unsigned int nr_frags, length;
 -
 +      unsigned int nr_frags, nr_txbds, length;
 +      union skb_shared_tx *shtx;
  
        rq = skb->queue_mapping;
        tx_queue = priv->tx_queue[rq];
        txq = netdev_get_tx_queue(dev, rq);
        base = tx_queue->tx_bd_base;
        regs = tx_queue->grp->regs;
 +      shtx = skb_tx(skb);
 +
 +      /* check if time stamp should be generated */
 +      if (unlikely(shtx->hardware && priv->hwts_tx_en))
 +              do_tstamp = 1;
  
        /* make space for additional header when fcb is needed */
        if (((skb->ip_summed == CHECKSUM_PARTIAL) ||
 -                      (priv->vlgrp && vlan_tx_tag_present(skb))) &&
 +                      (priv->vlgrp && vlan_tx_tag_present(skb)) ||
 +                      unlikely(do_tstamp)) &&
                        (skb_headroom(skb) < GMAC_FCB_LEN)) {
                struct sk_buff *skb_new;
  
        /* total number of fragments in the SKB */
        nr_frags = skb_shinfo(skb)->nr_frags;
  
 +      /* calculate the required number of TxBDs for this skb */
 +      if (unlikely(do_tstamp))
 +              nr_txbds = nr_frags + 2;
 +      else
 +              nr_txbds = nr_frags + 1;
 +
        /* check if there is space to queue this packet */
 -      if ((nr_frags+1) > tx_queue->num_txbdfree) {
 +      if (nr_txbds > tx_queue->num_txbdfree) {
                /* no space, stop the queue */
                netif_tx_stop_queue(txq);
                dev->stats.tx_fifo_errors++;
        txq->tx_packets ++;
  
        txbdp = txbdp_start = tx_queue->cur_tx;
 +      lstatus = txbdp->lstatus;
 +
 +      /* Time stamp insertion requires one additional TxBD */
 +      if (unlikely(do_tstamp))
 +              txbdp_tstamp = txbdp = next_txbd(txbdp, base,
 +                              tx_queue->tx_ring_size);
  
        if (nr_frags == 0) {
 -              lstatus = txbdp->lstatus | BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT);
 +              if (unlikely(do_tstamp))
 +                      txbdp_tstamp->lstatus |= BD_LFLAG(TXBD_LAST |
 +                                      TXBD_INTERRUPT);
 +              else
 +                      lstatus |= BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT);
        } else {
                /* Place the fragment addresses and lengths into the TxBDs */
                for (i = 0; i < nr_frags; i++) {
                gfar_tx_vlan(skb, fcb);
        }
  
 -      /* setup the TxBD length and buffer pointer for the first BD */
 +      /* Setup tx hardware time stamping if requested */
 +      if (unlikely(do_tstamp)) {
 +              shtx->in_progress = 1;
 +              if (fcb == NULL)
 +                      fcb = gfar_add_fcb(skb);
 +              fcb->ptp = 1;
 +              lstatus |= BD_LFLAG(TXBD_TOE);
 +      }
 +
        txbdp_start->bufPtr = dma_map_single(&priv->ofdev->dev, skb->data,
                        skb_headlen(skb), DMA_TO_DEVICE);
  
 -      lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | skb_headlen(skb);
 +      /*
 +       * If time stamping is requested one additional TxBD must be set up. The
 +       * first TxBD points to the FCB and must have a data length of
 +       * GMAC_FCB_LEN. The second TxBD points to the actual frame data with
 +       * the full frame length.
 +       */
 +      if (unlikely(do_tstamp)) {
 +              txbdp_tstamp->bufPtr = txbdp_start->bufPtr + GMAC_FCB_LEN;
 +              txbdp_tstamp->lstatus |= BD_LFLAG(TXBD_READY) |
 +                              (skb_headlen(skb) - GMAC_FCB_LEN);
 +              lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | GMAC_FCB_LEN;
 +      } else {
 +              lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | skb_headlen(skb);
 +      }
  
        /*
         * We can work in parallel with gfar_clean_tx_ring(), except
        tx_queue->cur_tx = next_txbd(txbdp, base, tx_queue->tx_ring_size);
  
        /* reduce TxBD free count */
 -      tx_queue->num_txbdfree -= (nr_frags + 1);
 -
 -      dev->trans_start = jiffies;
 +      tx_queue->num_txbdfree -= (nr_txbds);
  
        /* If the next BD still needs to be cleaned up, then the bds
           are full.  We need to tell the kernel to stop sending us stuff. */
@@@ -2185,7 -2089,6 +2186,6 @@@ static int gfar_close(struct net_devic
  
        disable_napi(priv);
  
-       skb_queue_purge(&priv->rx_recycle);
        cancel_work_sync(&priv->reset_task);
        stop_gfar(dev);
  
@@@ -2348,18 -2251,16 +2348,18 @@@ static int gfar_clean_tx_ring(struct gf
        struct net_device *dev = tx_queue->dev;
        struct gfar_private *priv = netdev_priv(dev);
        struct gfar_priv_rx_q *rx_queue = NULL;
 -      struct txbd8 *bdp;
 +      struct txbd8 *bdp, *next = NULL;
        struct txbd8 *lbdp = NULL;
        struct txbd8 *base = tx_queue->tx_bd_base;
        struct sk_buff *skb;
        int skb_dirtytx;
        int tx_ring_size = tx_queue->tx_ring_size;
 -      int frags = 0;
 +      int frags = 0, nr_txbds = 0;
        int i;
        int howmany = 0;
        u32 lstatus;
 +      size_t buflen;
 +      union skb_shared_tx *shtx;
  
        rx_queue = priv->rx_queue[tx_queue->qindex];
        bdp = tx_queue->dirty_tx;
                unsigned long flags;
  
                frags = skb_shinfo(skb)->nr_frags;
 -              lbdp = skip_txbd(bdp, frags, base, tx_ring_size);
 +
 +              /*
 +               * When time stamping, one additional TxBD must be freed.
 +               * Also, we need to dma_unmap_single() the TxPAL.
 +               */
 +              shtx = skb_tx(skb);
 +              if (unlikely(shtx->in_progress))
 +                      nr_txbds = frags + 2;
 +              else
 +                      nr_txbds = frags + 1;
 +
 +              lbdp = skip_txbd(bdp, nr_txbds - 1, base, tx_ring_size);
  
                lstatus = lbdp->lstatus;
  
                                (lstatus & BD_LENGTH_MASK))
                        break;
  
 -              dma_unmap_single(&priv->ofdev->dev,
 -                              bdp->bufPtr,
 -                              bdp->length,
 -                              DMA_TO_DEVICE);
 +              if (unlikely(shtx->in_progress)) {
 +                      next = next_txbd(bdp, base, tx_ring_size);
 +                      buflen = next->length + GMAC_FCB_LEN;
 +              } else
 +                      buflen = bdp->length;
 +
 +              dma_unmap_single(&priv->ofdev->dev, bdp->bufPtr,
 +                              buflen, DMA_TO_DEVICE);
 +
 +              if (unlikely(shtx->in_progress)) {
 +                      struct skb_shared_hwtstamps shhwtstamps;
 +                      u64 *ns = (u64*) (((u32)skb->data + 0x10) & ~0x7);
 +                      memset(&shhwtstamps, 0, sizeof(shhwtstamps));
 +                      shhwtstamps.hwtstamp = ns_to_ktime(*ns);
 +                      skb_tstamp_tx(skb, &shhwtstamps);
 +                      bdp->lstatus &= BD_LFLAG(TXBD_WRAP);
 +                      bdp = next;
 +              }
  
                bdp->lstatus &= BD_LFLAG(TXBD_WRAP);
                bdp = next_txbd(bdp, base, tx_ring_size);
  
                howmany++;
                spin_lock_irqsave(&tx_queue->txlock, flags);
 -              tx_queue->num_txbdfree += frags + 1;
 +              tx_queue->num_txbdfree += nr_txbds;
                spin_unlock_irqrestore(&tx_queue->txlock, flags);
        }
  
@@@ -2594,17 -2470,6 +2594,17 @@@ static int gfar_process_frame(struct ne
                skb_pull(skb, amount_pull);
        }
  
 +      /* Get receive timestamp from the skb */
 +      if (priv->hwts_rx_en) {
 +              struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
 +              u64 *ns = (u64 *) skb->data;
 +              memset(shhwtstamps, 0, sizeof(*shhwtstamps));
 +              shhwtstamps->hwtstamp = ns_to_ktime(*ns);
 +      }
 +
 +      if (priv->padding)
 +              skb_pull(skb, priv->padding);
 +
        if (priv->rx_csum_enable)
                gfar_rx_checksum(skb, fcb);
  
@@@ -2641,7 -2506,8 +2641,7 @@@ int gfar_clean_rx_ring(struct gfar_priv
        bdp = rx_queue->cur_rx;
        base = rx_queue->rx_bd_base;
  
 -      amount_pull = (gfar_uses_fcb(priv) ? GMAC_FCB_LEN : 0) +
 -              priv->padding;
 +      amount_pull = (gfar_uses_fcb(priv) ? GMAC_FCB_LEN : 0);
  
        while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) {
                struct sk_buff *newskb;
@@@ -2928,7 -2794,7 +2928,7 @@@ static void adjust_link(struct net_devi
   * whenever dev->flags is changed */
  static void gfar_set_multi(struct net_device *dev)
  {
 -      struct dev_mc_list *mc_ptr;
 +      struct netdev_hw_addr *ha;
        struct gfar_private *priv = netdev_priv(dev);
        struct gfar __iomem *regs = priv->gfargrp[0].regs;
        u32 tempval;
                        return;
  
                /* Parse the list, and set the appropriate bits */
 -              netdev_for_each_mc_addr(mc_ptr, dev) {
 +              netdev_for_each_mc_addr(ha, dev) {
                        if (idx < em_num) {
 -                              gfar_set_mac_for_addr(dev, idx,
 -                                              mc_ptr->dmi_addr);
 +                              gfar_set_mac_for_addr(dev, idx, ha->addr);
                                idx++;
                        } else
 -                              gfar_set_hash_for_addr(dev, mc_ptr->dmi_addr);
 +                              gfar_set_hash_for_addr(dev, ha->addr);
                }
        }
  
diff --combined drivers/net/phy/micrel.c
@@@ -32,6 -32,7 +32,7 @@@ static int kszphy_config_init(struct ph
  
  static struct phy_driver ks8001_driver = {
        .phy_id         = PHY_ID_KS8001,
+       .name           = "Micrel KS8001",
        .phy_id_mask    = 0x00fffff0,
        .features       = PHY_BASIC_FEATURES,
        .flags          = PHY_POLL,
@@@ -102,12 -103,3 +103,12 @@@ module_exit(ksphy_exit)
  MODULE_DESCRIPTION("Micrel PHY driver");
  MODULE_AUTHOR("David J. Choi");
  MODULE_LICENSE("GPL");
 +
 +static struct mdio_device_id micrel_tbl[] = {
 +      { PHY_ID_KSZ9021, 0x000fff10 },
 +      { PHY_ID_VSC8201, 0x00fffff0 },
 +      { PHY_ID_KS8001, 0x00fffff0 },
 +      { }
 +};
 +
 +MODULE_DEVICE_TABLE(mdio, micrel_tbl);
@@@ -42,7 -42,6 +42,7 @@@
  #include <linux/usb.h>
  #include <linux/firmware.h>
  #include <linux/etherdevice.h>
 +#include <linux/device.h>
  #include <net/mac80211.h>
  #include "ar9170.h"
  #include "cmd.h"
@@@ -68,28 -67,18 +68,28 @@@ static struct usb_device_id ar9170_usb_
        { USB_DEVICE(0x0cf3, 0x1001) },
        /* TP-Link TL-WN821N v2 */
        { USB_DEVICE(0x0cf3, 0x1002) },
 +      /* 3Com Dual Band 802.11n USB Adapter */
 +      { USB_DEVICE(0x0cf3, 0x1010) },
 +      /* H3C Dual Band 802.11n USB Adapter */
 +      { USB_DEVICE(0x0cf3, 0x1011) },
        /* Cace Airpcap NX */
        { USB_DEVICE(0xcace, 0x0300) },
        /* D-Link DWA 160 A1 */
        { USB_DEVICE(0x07d1, 0x3c10) },
        /* D-Link DWA 160 A2 */
        { USB_DEVICE(0x07d1, 0x3a09) },
 +      /* Netgear WNA1000 */
 +      { USB_DEVICE(0x0846, 0x9040) },
        /* Netgear WNDA3100 */
        { USB_DEVICE(0x0846, 0x9010) },
        /* Netgear WN111 v2 */
        { USB_DEVICE(0x0846, 0x9001) },
        /* Zydas ZD1221 */
        { USB_DEVICE(0x0ace, 0x1221) },
 +      /* Proxim ORiNOCO 802.11n USB */
 +      { USB_DEVICE(0x1435, 0x0804) },
 +      /* WNC Generic 11n USB Dongle */
 +      { USB_DEVICE(0x1435, 0x0326) },
        /* ZyXEL NWD271N */
        { USB_DEVICE(0x0586, 0x3417) },
        /* Z-Com UB81 BG */
@@@ -738,12 -727,16 +738,16 @@@ static void ar9170_usb_firmware_failed(
  {
        struct device *parent = aru->udev->dev.parent;
  
+       complete(&aru->firmware_loading_complete);
        /* unbind anything failed */
        if (parent)
 -              down(&parent->sem);
 +              device_lock(parent);
        device_release_driver(&aru->udev->dev);
        if (parent)
 -              up(&parent->sem);
 +              device_unlock(parent);
+       usb_put_dev(aru->udev);
  }
  
  static void ar9170_usb_firmware_finish(const struct firmware *fw, void *context)
        if (err)
                goto err_unrx;
  
+       complete(&aru->firmware_loading_complete);
+       usb_put_dev(aru->udev);
        return;
  
   err_unrx:
@@@ -869,6 -864,7 +875,7 @@@ static int ar9170_usb_probe(struct usb_
        init_usb_anchor(&aru->tx_pending);
        init_usb_anchor(&aru->tx_submitted);
        init_completion(&aru->cmd_wait);
+       init_completion(&aru->firmware_loading_complete);
        spin_lock_init(&aru->tx_urb_lock);
  
        aru->tx_pending_urbs = 0;
        if (err)
                goto err_freehw;
  
+       usb_get_dev(aru->udev);
        return request_firmware_nowait(THIS_MODULE, 1, "ar9170.fw",
                                       &aru->udev->dev, GFP_KERNEL, aru,
                                       ar9170_usb_firmware_step2);
@@@ -907,6 -904,9 +915,9 @@@ static void ar9170_usb_disconnect(struc
                return;
  
        aru->common.state = AR9170_IDLE;
+       wait_for_completion(&aru->firmware_loading_complete);
        ar9170_unregister(&aru->common);
        ar9170_usb_cancel_urbs(aru);
  
diff --combined include/linux/fs.h
@@@ -1280,12 -1280,10 +1280,12 @@@ static inline int lock_may_write(struc
  
  
  struct fasync_struct {
 -      int     magic;
 -      int     fa_fd;
 -      struct  fasync_struct   *fa_next; /* singly linked list */
 -      struct  file            *fa_file;
 +      spinlock_t              fa_lock;
 +      int                     magic;
 +      int                     fa_fd;
 +      struct fasync_struct    *fa_next; /* singly linked list */
 +      struct file             *fa_file;
 +      struct rcu_head         fa_rcu;
  };
  
  #define FASYNC_MAGIC 0x4601
  extern int fasync_helper(int, struct file *, int, struct fasync_struct **);
  /* can be called from interrupts */
  extern void kill_fasync(struct fasync_struct **, int, int);
 -/* only for net: no internal synchronization */
 -extern void __kill_fasync(struct fasync_struct *, int, int);
  
  extern int __f_setown(struct file *filp, struct pid *, enum pid_type, int force);
  extern int f_setown(struct file *filp, unsigned long arg, int force);
@@@ -2315,8 -2315,9 +2315,9 @@@ extern int vfs_fstatat(int , char __use
  extern int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
                    unsigned long arg);
  extern int __generic_block_fiemap(struct inode *inode,
-                                 struct fiemap_extent_info *fieinfo, u64 start,
-                                 u64 len, get_block_t *get_block);
+                                 struct fiemap_extent_info *fieinfo,
+                                 loff_t start, loff_t len,
+                                 get_block_t *get_block);
  extern int generic_block_fiemap(struct inode *inode,
                                struct fiemap_extent_info *fieinfo, u64 start,
                                u64 len, get_block_t *get_block);
diff --combined include/net/sctp/sm.h
@@@ -279,6 -279,7 +279,7 @@@ int sctp_do_sm(sctp_event_t event_type
  /* 2nd level prototypes */
  void sctp_generate_t3_rtx_event(unsigned long peer);
  void sctp_generate_heartbeat_event(unsigned long peer);
+ void sctp_generate_proto_unreach_event(unsigned long peer);
  
  void sctp_ootb_pkt_free(struct sctp_packet *);
  
@@@ -437,7 -438,7 +438,7 @@@ sctp_vtag_verify_either(const struct sc
         */
          if ((!sctp_test_T_bit(chunk) &&
               (ntohl(chunk->sctp_hdr->vtag) == asoc->c.my_vtag)) ||
 -          (sctp_test_T_bit(chunk) &&
 +          (sctp_test_T_bit(chunk) && asoc->c.peer_vtag &&
             (ntohl(chunk->sctp_hdr->vtag) == asoc->c.peer_vtag))) {
                  return 1;
        }
@@@ -643,15 -643,17 +643,15 @@@ struct sctp_pf 
  struct sctp_datamsg {
        /* Chunks waiting to be submitted to lower layer. */
        struct list_head chunks;
 -      /* Chunks that have been transmitted. */
 -      size_t msg_size;
        /* Reference counting. */
        atomic_t refcnt;
        /* When is this message no longer interesting to the peer? */
        unsigned long expires_at;
        /* Did the messenge fail to send? */
        int send_error;
 -      char send_failed;
 -      /* Control whether chunks from this message can be abandoned. */
 -      char can_abandon;
 +      u8 send_failed:1,
 +         can_abandon:1,   /* can chunks from this message can be abandoned. */
 +         can_delay;       /* should this message be Nagle delayed */
  };
  
  struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *,
@@@ -755,6 -757,7 +755,6 @@@ struct sctp_chunk 
  #define SCTP_NEED_FRTX 0x1
  #define SCTP_DONT_FRTX 0x2
        __u16   rtt_in_progress:1,      /* This chunk used for RTT calc? */
 -              resent:1,               /* Has this chunk ever been resent. */
                has_tsn:1,              /* Does this chunk have a TSN yet? */
                has_ssn:1,              /* Does this chunk have a SSN yet? */
                singleton:1,            /* Only chunk in the packet? */
@@@ -876,30 -879,7 +876,30 @@@ struct sctp_transport 
  
        /* Reference counting. */
        atomic_t refcnt;
 -      int      dead;
 +      int      dead:1,
 +              /* RTO-Pending : A flag used to track if one of the DATA
 +               *              chunks sent to this address is currently being
 +               *              used to compute a RTT. If this flag is 0,
 +               *              the next DATA chunk sent to this destination
 +               *              should be used to compute a RTT and this flag
 +               *              should be set. Every time the RTT
 +               *              calculation completes (i.e. the DATA chunk
 +               *              is SACK'd) clear this flag.
 +               */
 +               rto_pending:1,
 +
 +              /*
 +               * hb_sent : a flag that signals that we have a pending
 +               * heartbeat.
 +               */
 +              hb_sent:1,
 +
 +              /* Is the Path MTU update pending on this tranport */
 +              pmtu_pending:1,
 +
 +              /* Is this structure kfree()able? */
 +              malloced:1;
 +
  
        /* This is the peer's IP address and port. */
        union sctp_addr ipaddr;
        /* SRTT        : The current smoothed round trip time.  */
        __u32 srtt;
  
 -      /* RTO-Pending : A flag used to track if one of the DATA
 -       *              chunks sent to this address is currently being
 -       *              used to compute a RTT. If this flag is 0,
 -       *              the next DATA chunk sent to this destination
 -       *              should be used to compute a RTT and this flag
 -       *              should be set. Every time the RTT
 -       *              calculation completes (i.e. the DATA chunk
 -       *              is SACK'd) clear this flag.
 -       * hb_sent : a flag that signals that we have a pending heartbeat.
 -       */
 -      __u8 rto_pending;
 -      __u8 hb_sent;
 -
 -      /* Flag to track the current fast recovery state */
 -      __u8 fast_recovery;
 -
        /*
         * These are the congestion stats.
         */
  
        __u32 burst_limited;    /* Holds old cwnd when max.burst is applied */
  
 -      /* TSN marking the fast recovery exit point */
 -      __u32 fast_recovery_exit;
 -
        /* Destination */
        struct dst_entry *dst;
        /* Source address. */
         */
        __u16 pathmaxrxt;
  
 -      /* is the Path MTU update pending on this tranport */
 -      __u8 pmtu_pending;
 -
        /* PMTU       : The current known path MTU.  */
        __u32 pathmtu;
  
        /* Heartbeat timer is per destination. */
        struct timer_list hb_timer;
  
+       /* Timer to handle ICMP proto unreachable envets */
+       struct timer_list proto_unreach_timer;
        /* Since we're using per-destination retransmission timers
         * (see above), we're also using per-destination "transmitted"
         * queues.  This probably ought to be a private struct
        /* This is the list of transports that have chunks to send.  */
        struct list_head send_ready;
  
 -      int malloced; /* Is this structure kfree()able? */
 -
        /* State information saved for SFR_CACC algorithm. The key
         * idea in SFR_CACC is to maintain state at the sender on a
         * per-destination basis when a changeover happens.
@@@ -1062,7 -1069,7 +1065,7 @@@ void sctp_transport_route(struct sctp_t
                          struct sctp_sock *);
  void sctp_transport_pmtu(struct sctp_transport *);
  void sctp_transport_free(struct sctp_transport *);
 -void sctp_transport_reset_timers(struct sctp_transport *, int);
 +void sctp_transport_reset_timers(struct sctp_transport *);
  void sctp_transport_hold(struct sctp_transport *);
  void sctp_transport_put(struct sctp_transport *);
  void sctp_transport_update_rto(struct sctp_transport *, __u32);
@@@ -1716,12 -1723,6 +1719,12 @@@ struct sctp_association 
        /* Highest TSN that is acknowledged by incoming SACKs. */
        __u32 highest_sacked;
  
 +      /* TSN marking the fast recovery exit point */
 +      __u32 fast_recovery_exit;
 +
 +      /* Flag to track the current fast recovery state */
 +      __u8 fast_recovery;
 +
        /* The number of unacknowledged data chunks.  Reported through
         * the SCTP_STATUS sockopt.
         */
diff --combined net/core/dev.c
  #include <linux/jhash.h>
  #include <linux/random.h>
  #include <trace/events/napi.h>
 +#include <linux/pci.h>
  
  #include "net-sysfs.h"
  
@@@ -208,20 -207,6 +208,20 @@@ static inline struct hlist_head *dev_in
        return &net->dev_index_head[ifindex & (NETDEV_HASHENTRIES - 1)];
  }
  
 +static inline void rps_lock(struct softnet_data *sd)
 +{
 +#ifdef CONFIG_RPS
 +      spin_lock(&sd->input_pkt_queue.lock);
 +#endif
 +}
 +
 +static inline void rps_unlock(struct softnet_data *sd)
 +{
 +#ifdef CONFIG_RPS
 +      spin_unlock(&sd->input_pkt_queue.lock);
 +#endif
 +}
 +
  /* Device list insertion */
  static int list_netdevice(struct net_device *dev)
  {
@@@ -264,7 -249,7 +264,7 @@@ static RAW_NOTIFIER_HEAD(netdev_chain)
   *    queue in the local softnet handler.
   */
  
 -DEFINE_PER_CPU(struct softnet_data, softnet_data);
 +DEFINE_PER_CPU_ALIGNED(struct softnet_data, softnet_data);
  EXPORT_PER_CPU_SYMBOL(softnet_data);
  
  #ifdef CONFIG_LOCKDEP
@@@ -788,17 -773,14 +788,17 @@@ EXPORT_SYMBOL(__dev_getfirstbyhwtype)
  
  struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type)
  {
 -      struct net_device *dev;
 +      struct net_device *dev, *ret = NULL;
  
 -      rtnl_lock();
 -      dev = __dev_getfirstbyhwtype(net, type);
 -      if (dev)
 -              dev_hold(dev);
 -      rtnl_unlock();
 -      return dev;
 +      rcu_read_lock();
 +      for_each_netdev_rcu(net, dev)
 +              if (dev->type == type) {
 +                      dev_hold(dev);
 +                      ret = dev;
 +                      break;
 +              }
 +      rcu_read_unlock();
 +      return ret;
  }
  EXPORT_SYMBOL(dev_getfirstbyhwtype);
  
@@@ -1103,9 -1085,9 +1103,9 @@@ void netdev_state_change(struct net_dev
  }
  EXPORT_SYMBOL(netdev_state_change);
  
 -void netdev_bonding_change(struct net_device *dev, unsigned long event)
 +int netdev_bonding_change(struct net_device *dev, unsigned long event)
  {
 -      call_netdevice_notifiers(event, dev);
 +      return call_netdevice_notifiers(event, dev);
  }
  EXPORT_SYMBOL(netdev_bonding_change);
  
@@@ -1435,7 -1417,6 +1435,7 @@@ EXPORT_SYMBOL(unregister_netdevice_noti
  
  int call_netdevice_notifiers(unsigned long val, struct net_device *dev)
  {
 +      ASSERT_RTNL();
        return raw_notifier_call_chain(&netdev_chain, val, dev);
  }
  
@@@ -1470,7 -1451,7 +1470,7 @@@ static inline void net_timestamp(struc
   *
   * return values:
   *    NET_RX_SUCCESS  (no congestion)
-  *    NET_RX_DROP     (packet was dropped)
+  *    NET_RX_DROP     (packet was dropped, but freed)
   *
   * dev_forward_skb can be used for injecting an skb from the
   * start_xmit function of one device into the receive queue
@@@ -1484,12 -1465,11 +1484,11 @@@ int dev_forward_skb(struct net_device *
  {
        skb_orphan(skb);
  
-       if (!(dev->flags & IFF_UP))
-               return NET_RX_DROP;
-       if (skb->len > (dev->mtu + dev->hard_header_len))
+       if (!(dev->flags & IFF_UP) ||
+           (skb->len > (dev->mtu + dev->hard_header_len))) {
+               kfree_skb(skb);
                return NET_RX_DROP;
+       }
        skb_set_dev(skb, dev);
        skb->tstamp.tv64 = 0;
        skb->pkt_type = PACKET_HOST;
@@@ -1557,9 -1537,8 +1556,9 @@@ static inline void __netif_reschedule(s
  
        local_irq_save(flags);
        sd = &__get_cpu_var(softnet_data);
 -      q->next_sched = sd->output_queue;
 -      sd->output_queue = q;
 +      q->next_sched = NULL;
 +      *sd->output_queue_tailp = q;
 +      sd->output_queue_tailp = &q->next_sched;
        raise_softirq_irqoff(NET_TX_SOFTIRQ);
        local_irq_restore(flags);
  }
@@@ -1804,27 -1783,18 +1803,27 @@@ EXPORT_SYMBOL(netdev_rx_csum_fault)
   * 2. No high memory really exists on this machine.
   */
  
 -static inline int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
 +static int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
  {
  #ifdef CONFIG_HIGHMEM
        int i;
 +      if (!(dev->features & NETIF_F_HIGHDMA)) {
 +              for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
 +                      if (PageHighMem(skb_shinfo(skb)->frags[i].page))
 +                              return 1;
 +      }
  
 -      if (dev->features & NETIF_F_HIGHDMA)
 -              return 0;
 -
 -      for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
 -              if (PageHighMem(skb_shinfo(skb)->frags[i].page))
 -                      return 1;
 +      if (PCI_DMA_BUS_IS_PHYS) {
 +              struct device *pdev = dev->dev.parent;
  
 +              if (!pdev)
 +                      return 0;
 +              for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 +                      dma_addr_t addr = page_to_phys(skb_shinfo(skb)->frags[i].page);
 +                      if (!pdev->dma_mask || addr + PAGE_SIZE - 1 > *pdev->dma_mask)
 +                              return 1;
 +              }
 +      }
  #endif
        return 0;
  }
@@@ -1882,17 -1852,6 +1881,17 @@@ static int dev_gso_segment(struct sk_bu
        return 0;
  }
  
 +/*
 + * Try to orphan skb early, right before transmission by the device.
 + * We cannot orphan skb if tx timestamp is requested, since
 + * drivers need to call skb_tstamp_tx() to send the timestamp.
 + */
 +static inline void skb_orphan_try(struct sk_buff *skb)
 +{
 +      if (!skb_tx(skb)->flags)
 +              skb_orphan(skb);
 +}
 +
  int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
                        struct netdev_queue *txq)
  {
                if (!list_empty(&ptype_all))
                        dev_queue_xmit_nit(skb, dev);
  
 -              if (netif_needs_gso(dev, skb)) {
 -                      if (unlikely(dev_gso_segment(skb)))
 -                              goto out_kfree_skb;
 -                      if (skb->next)
 -                              goto gso;
 -              }
 -
                /*
                 * If device doesnt need skb->dst, release it right now while
                 * its hot in this cpu cache
                if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
                        skb_dst_drop(skb);
  
 +              skb_orphan_try(skb);
 +
 +              if (netif_needs_gso(dev, skb)) {
 +                      if (unlikely(dev_gso_segment(skb)))
 +                              goto out_kfree_skb;
 +                      if (skb->next)
 +                              goto gso;
 +              }
 +
                rc = ops->ndo_start_xmit(skb, dev);
                if (rc == NETDEV_TX_OK)
                        txq_trans_update(txq);
 -              /*
 -               * TODO: if skb_orphan() was called by
 -               * dev->hard_start_xmit() (for example, the unmodified
 -               * igb driver does that; bnx2 doesn't), then
 -               * skb_tx_software_timestamp() will be unable to send
 -               * back the time stamp.
 -               *
 -               * How can this be prevented? Always create another
 -               * reference to the socket before calling
 -               * dev->hard_start_xmit()? Prevent that skb_orphan()
 -               * does anything in dev->hard_start_xmit() by clearing
 -               * the skb destructor before the call and restoring it
 -               * afterwards, then doing the skb_orphan() ourselves?
 -               */
                return rc;
        }
  
@@@ -1960,7 -1931,7 +1959,7 @@@ out_kfree_skb
        return rc;
  }
  
 -static u32 skb_tx_hashrnd;
 +static u32 hashrnd __read_mostly;
  
  u16 skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb)
  {
        if (skb->sk && skb->sk->sk_hash)
                hash = skb->sk->sk_hash;
        else
 -              hash = skb->protocol;
 +              hash = (__force u16) skb->protocol;
  
 -      hash = jhash_1word(hash, skb_tx_hashrnd);
 +      hash = jhash_1word(hash, hashrnd);
  
        return (u16) (((u64) hash * dev->real_num_tx_queues) >> 32);
  }
@@@ -1988,9 -1959,10 +1987,9 @@@ static inline u16 dev_cap_txqueue(struc
  {
        if (unlikely(queue_index >= dev->real_num_tx_queues)) {
                if (net_ratelimit()) {
 -                      WARN(1, "%s selects TX queue %d, but "
 -                           "real number of TX queues is %d\n",
 -                           dev->name, queue_index,
 -                           dev->real_num_tx_queues);
 +                      pr_warning("%s selects TX queue %d, but "
 +                              "real number of TX queues is %d\n",
 +                              dev->name, queue_index, dev->real_num_tx_queues);
                }
                return 0;
        }
@@@ -2017,7 -1989,7 +2016,7 @@@ static struct netdev_queue *dev_pick_tx
                                queue_index = skb_tx_hash(dev, skb);
  
                        if (sk) {
 -                              struct dst_entry *dst = rcu_dereference_bh(sk->sk_dst_cache);
 +                              struct dst_entry *dst = rcu_dereference_check(sk->sk_dst_cache, 1);
  
                                if (dst && skb_dst(skb) == dst)
                                        sk_tx_queue_set(sk, queue_index);
@@@ -2205,243 -2177,8 +2204,243 @@@ int netdev_max_backlog __read_mostly = 
  int netdev_budget __read_mostly = 300;
  int weight_p __read_mostly = 64;            /* old backlog weight */
  
 -DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, };
 +/* Called with irq disabled */
 +static inline void ____napi_schedule(struct softnet_data *sd,
 +                                   struct napi_struct *napi)
 +{
 +      list_add_tail(&napi->poll_list, &sd->poll_list);
 +      __raise_softirq_irqoff(NET_RX_SOFTIRQ);
 +}
 +
 +#ifdef CONFIG_RPS
 +
 +/* One global table that all flow-based protocols share. */
 +struct rps_sock_flow_table *rps_sock_flow_table __read_mostly;
 +EXPORT_SYMBOL(rps_sock_flow_table);
 +
 +/*
 + * get_rps_cpu is called from netif_receive_skb and returns the target
 + * CPU from the RPS map of the receiving queue for a given skb.
 + * rcu_read_lock must be held on entry.
 + */
 +static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb,
 +                     struct rps_dev_flow **rflowp)
 +{
 +      struct ipv6hdr *ip6;
 +      struct iphdr *ip;
 +      struct netdev_rx_queue *rxqueue;
 +      struct rps_map *map;
 +      struct rps_dev_flow_table *flow_table;
 +      struct rps_sock_flow_table *sock_flow_table;
 +      int cpu = -1;
 +      u8 ip_proto;
 +      u16 tcpu;
 +      u32 addr1, addr2, ihl;
 +      union {
 +              u32 v32;
 +              u16 v16[2];
 +      } ports;
 +
 +      if (skb_rx_queue_recorded(skb)) {
 +              u16 index = skb_get_rx_queue(skb);
 +              if (unlikely(index >= dev->num_rx_queues)) {
 +                      if (net_ratelimit()) {
 +                              pr_warning("%s received packet on queue "
 +                                      "%u, but number of RX queues is %u\n",
 +                                      dev->name, index, dev->num_rx_queues);
 +                      }
 +                      goto done;
 +              }
 +              rxqueue = dev->_rx + index;
 +      } else
 +              rxqueue = dev->_rx;
 +
 +      if (!rxqueue->rps_map && !rxqueue->rps_flow_table)
 +              goto done;
 +
 +      if (skb->rxhash)
 +              goto got_hash; /* Skip hash computation on packet header */
 +
 +      switch (skb->protocol) {
 +      case __constant_htons(ETH_P_IP):
 +              if (!pskb_may_pull(skb, sizeof(*ip)))
 +                      goto done;
 +
 +              ip = (struct iphdr *) skb->data;
 +              ip_proto = ip->protocol;
 +              addr1 = (__force u32) ip->saddr;
 +              addr2 = (__force u32) ip->daddr;
 +              ihl = ip->ihl;
 +              break;
 +      case __constant_htons(ETH_P_IPV6):
 +              if (!pskb_may_pull(skb, sizeof(*ip6)))
 +                      goto done;
 +
 +              ip6 = (struct ipv6hdr *) skb->data;
 +              ip_proto = ip6->nexthdr;
 +              addr1 = (__force u32) ip6->saddr.s6_addr32[3];
 +              addr2 = (__force u32) ip6->daddr.s6_addr32[3];
 +              ihl = (40 >> 2);
 +              break;
 +      default:
 +              goto done;
 +      }
 +      switch (ip_proto) {
 +      case IPPROTO_TCP:
 +      case IPPROTO_UDP:
 +      case IPPROTO_DCCP:
 +      case IPPROTO_ESP:
 +      case IPPROTO_AH:
 +      case IPPROTO_SCTP:
 +      case IPPROTO_UDPLITE:
 +              if (pskb_may_pull(skb, (ihl * 4) + 4)) {
 +                      ports.v32 = * (__force u32 *) (skb->data + (ihl * 4));
 +                      if (ports.v16[1] < ports.v16[0])
 +                              swap(ports.v16[0], ports.v16[1]);
 +                      break;
 +              }
 +      default:
 +              ports.v32 = 0;
 +              break;
 +      }
 +
 +      /* get a consistent hash (same value on both flow directions) */
 +      if (addr2 < addr1)
 +              swap(addr1, addr2);
 +      skb->rxhash = jhash_3words(addr1, addr2, ports.v32, hashrnd);
 +      if (!skb->rxhash)
 +              skb->rxhash = 1;
 +
 +got_hash:
 +      flow_table = rcu_dereference(rxqueue->rps_flow_table);
 +      sock_flow_table = rcu_dereference(rps_sock_flow_table);
 +      if (flow_table && sock_flow_table) {
 +              u16 next_cpu;
 +              struct rps_dev_flow *rflow;
 +
 +              rflow = &flow_table->flows[skb->rxhash & flow_table->mask];
 +              tcpu = rflow->cpu;
 +
 +              next_cpu = sock_flow_table->ents[skb->rxhash &
 +                  sock_flow_table->mask];
 +
 +              /*
 +               * If the desired CPU (where last recvmsg was done) is
 +               * different from current CPU (one in the rx-queue flow
 +               * table entry), switch if one of the following holds:
 +               *   - Current CPU is unset (equal to RPS_NO_CPU).
 +               *   - Current CPU is offline.
 +               *   - The current CPU's queue tail has advanced beyond the
 +               *     last packet that was enqueued using this table entry.
 +               *     This guarantees that all previous packets for the flow
 +               *     have been dequeued, thus preserving in order delivery.
 +               */
 +              if (unlikely(tcpu != next_cpu) &&
 +                  (tcpu == RPS_NO_CPU || !cpu_online(tcpu) ||
 +                   ((int)(per_cpu(softnet_data, tcpu).input_queue_head -
 +                    rflow->last_qtail)) >= 0)) {
 +                      tcpu = rflow->cpu = next_cpu;
 +                      if (tcpu != RPS_NO_CPU)
 +                              rflow->last_qtail = per_cpu(softnet_data,
 +                                  tcpu).input_queue_head;
 +              }
 +              if (tcpu != RPS_NO_CPU && cpu_online(tcpu)) {
 +                      *rflowp = rflow;
 +                      cpu = tcpu;
 +                      goto done;
 +              }
 +      }
 +
 +      map = rcu_dereference(rxqueue->rps_map);
 +      if (map) {
 +              tcpu = map->cpus[((u64) skb->rxhash * map->len) >> 32];
 +
 +              if (cpu_online(tcpu)) {
 +                      cpu = tcpu;
 +                      goto done;
 +              }
 +      }
 +
 +done:
 +      return cpu;
 +}
 +
 +/* Called from hardirq (IPI) context */
 +static void rps_trigger_softirq(void *data)
 +{
 +      struct softnet_data *sd = data;
 +
 +      ____napi_schedule(sd, &sd->backlog);
 +      sd->received_rps++;
 +}
 +
 +#endif /* CONFIG_RPS */
 +
 +/*
 + * Check if this softnet_data structure is another cpu one
 + * If yes, queue it to our IPI list and return 1
 + * If no, return 0
 + */
 +static int rps_ipi_queued(struct softnet_data *sd)
 +{
 +#ifdef CONFIG_RPS
 +      struct softnet_data *mysd = &__get_cpu_var(softnet_data);
 +
 +      if (sd != mysd) {
 +              sd->rps_ipi_next = mysd->rps_ipi_list;
 +              mysd->rps_ipi_list = sd;
 +
 +              __raise_softirq_irqoff(NET_RX_SOFTIRQ);
 +              return 1;
 +      }
 +#endif /* CONFIG_RPS */
 +      return 0;
 +}
 +
 +/*
 + * enqueue_to_backlog is called to queue an skb to a per CPU backlog
 + * queue (may be a remote CPU queue).
 + */
 +static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
 +                            unsigned int *qtail)
 +{
 +      struct softnet_data *sd;
 +      unsigned long flags;
 +
 +      sd = &per_cpu(softnet_data, cpu);
 +
 +      local_irq_save(flags);
 +
 +      rps_lock(sd);
 +      if (skb_queue_len(&sd->input_pkt_queue) <= netdev_max_backlog) {
 +              if (skb_queue_len(&sd->input_pkt_queue)) {
 +enqueue:
 +                      __skb_queue_tail(&sd->input_pkt_queue, skb);
 +#ifdef CONFIG_RPS
 +                      *qtail = sd->input_queue_head +
 +                                      skb_queue_len(&sd->input_pkt_queue);
 +#endif
 +                      rps_unlock(sd);
 +                      local_irq_restore(flags);
 +                      return NET_RX_SUCCESS;
 +              }
 +
 +              /* Schedule NAPI for backlog device */
 +              if (napi_schedule_prep(&sd->backlog)) {
 +                      if (!rps_ipi_queued(sd))
 +                              ____napi_schedule(sd, &sd->backlog);
 +              }
 +              goto enqueue;
 +      }
 +
 +      sd->dropped++;
 +      rps_unlock(sd);
 +
 +      local_irq_restore(flags);
  
 +      kfree_skb(skb);
 +      return NET_RX_DROP;
 +}
  
  /**
   *    netif_rx        -       post buffer to the network code
  
  int netif_rx(struct sk_buff *skb)
  {
 -      struct softnet_data *queue;
 -      unsigned long flags;
 +      int ret;
  
        /* if netpoll wants it, pretend we never saw it */
        if (netpoll_rx(skb))
        if (!skb->tstamp.tv64)
                net_timestamp(skb);
  
 -      /*
 -       * The code is rearranged so that the path is the most
 -       * short when CPU is congested, but is still operating.
 -       */
 -      local_irq_save(flags);
 -      queue = &__get_cpu_var(softnet_data);
 +#ifdef CONFIG_RPS
 +      {
 +              struct rps_dev_flow voidflow, *rflow = &voidflow;
 +              int cpu;
  
 -      __get_cpu_var(netdev_rx_stat).total++;
 -      if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
 -              if (queue->input_pkt_queue.qlen) {
 -enqueue:
 -                      __skb_queue_tail(&queue->input_pkt_queue, skb);
 -                      local_irq_restore(flags);
 -                      return NET_RX_SUCCESS;
 -              }
 +              rcu_read_lock();
  
 -              napi_schedule(&queue->backlog);
 -              goto enqueue;
 -      }
 +              cpu = get_rps_cpu(skb->dev, skb, &rflow);
 +              if (cpu < 0)
 +                      cpu = smp_processor_id();
  
 -      __get_cpu_var(netdev_rx_stat).dropped++;
 -      local_irq_restore(flags);
 +              ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
  
 -      kfree_skb(skb);
 -      return NET_RX_DROP;
 +              rcu_read_unlock();
 +      }
 +#else
 +      {
 +              unsigned int qtail;
 +              ret = enqueue_to_backlog(skb, get_cpu(), &qtail);
 +              put_cpu();
 +      }
 +#endif
 +      return ret;
  }
  EXPORT_SYMBOL(netif_rx);
  
@@@ -2536,7 -2276,6 +2535,7 @@@ static void net_tx_action(struct softir
                local_irq_disable();
                head = sd->output_queue;
                sd->output_queue = NULL;
 +              sd->output_queue_tailp = &sd->output_queue;
                local_irq_enable();
  
                while (head) {
@@@ -2729,56 -2468,22 +2728,56 @@@ void netif_nit_deliver(struct sk_buff *
        rcu_read_unlock();
  }
  
 -/**
 - *    netif_receive_skb - process receive buffer from network
 - *    @skb: buffer to process
 - *
 - *    netif_receive_skb() is the main receive data processing function.
 - *    It always succeeds. The buffer may be dropped during processing
 - *    for congestion control or by the protocol layers.
 - *
 - *    This function may only be called from softirq context and interrupts
 - *    should be enabled.
 - *
 - *    Return values (usually ignored):
 - *    NET_RX_SUCCESS: no congestion
 - *    NET_RX_DROP: packet was dropped
 +static inline void skb_bond_set_mac_by_master(struct sk_buff *skb,
 +                                            struct net_device *master)
 +{
 +      if (skb->pkt_type == PACKET_HOST) {
 +              u16 *dest = (u16 *) eth_hdr(skb)->h_dest;
 +
 +              memcpy(dest, master->dev_addr, ETH_ALEN);
 +      }
 +}
 +
 +/* On bonding slaves other than the currently active slave, suppress
 + * duplicates except for 802.3ad ETH_P_SLOW, alb non-mcast/bcast, and
 + * ARP on active-backup slaves with arp_validate enabled.
   */
 -int netif_receive_skb(struct sk_buff *skb)
 +int __skb_bond_should_drop(struct sk_buff *skb, struct net_device *master)
 +{
 +      struct net_device *dev = skb->dev;
 +
 +      if (master->priv_flags & IFF_MASTER_ARPMON)
 +              dev->last_rx = jiffies;
 +
 +      if ((master->priv_flags & IFF_MASTER_ALB) && master->br_port) {
 +              /* Do address unmangle. The local destination address
 +               * will be always the one master has. Provides the right
 +               * functionality in a bridge.
 +               */
 +              skb_bond_set_mac_by_master(skb, master);
 +      }
 +
 +      if (dev->priv_flags & IFF_SLAVE_INACTIVE) {
 +              if ((dev->priv_flags & IFF_SLAVE_NEEDARP) &&
 +                  skb->protocol == __cpu_to_be16(ETH_P_ARP))
 +                      return 0;
 +
 +              if (master->priv_flags & IFF_MASTER_ALB) {
 +                      if (skb->pkt_type != PACKET_BROADCAST &&
 +                          skb->pkt_type != PACKET_MULTICAST)
 +                              return 0;
 +              }
 +              if (master->priv_flags & IFF_MASTER_8023AD &&
 +                  skb->protocol == __cpu_to_be16(ETH_P_SLOW))
 +                      return 0;
 +
 +              return 1;
 +      }
 +      return 0;
 +}
 +EXPORT_SYMBOL(__skb_bond_should_drop);
 +
 +static int __netif_receive_skb(struct sk_buff *skb)
  {
        struct packet_type *ptype, *pt_prev;
        struct net_device *orig_dev;
                        skb->dev = master;
        }
  
 -      __get_cpu_var(netdev_rx_stat).total++;
 +      __get_cpu_var(softnet_data).processed++;
  
        skb_reset_network_header(skb);
        skb_reset_transport_header(skb);
        rcu_read_unlock();
        return ret;
  }
 +
 +/**
 + *    netif_receive_skb - process receive buffer from network
 + *    @skb: buffer to process
 + *
 + *    netif_receive_skb() is the main receive data processing function.
 + *    It always succeeds. The buffer may be dropped during processing
 + *    for congestion control or by the protocol layers.
 + *
 + *    This function may only be called from softirq context and interrupts
 + *    should be enabled.
 + *
 + *    Return values (usually ignored):
 + *    NET_RX_SUCCESS: no congestion
 + *    NET_RX_DROP: packet was dropped
 + */
 +int netif_receive_skb(struct sk_buff *skb)
 +{
 +#ifdef CONFIG_RPS
 +      struct rps_dev_flow voidflow, *rflow = &voidflow;
 +      int cpu, ret;
 +
 +      rcu_read_lock();
 +
 +      cpu = get_rps_cpu(skb->dev, skb, &rflow);
 +
 +      if (cpu >= 0) {
 +              ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
 +              rcu_read_unlock();
 +      } else {
 +              rcu_read_unlock();
 +              ret = __netif_receive_skb(skb);
 +      }
 +
 +      return ret;
 +#else
 +      return __netif_receive_skb(skb);
 +#endif
 +}
  EXPORT_SYMBOL(netif_receive_skb);
  
 -/* Network device is going away, flush any packets still pending  */
 +/* Network device is going away, flush any packets still pending
 + * Called with irqs disabled.
 + */
  static void flush_backlog(void *arg)
  {
        struct net_device *dev = arg;
 -      struct softnet_data *queue = &__get_cpu_var(softnet_data);
 +      struct softnet_data *sd = &__get_cpu_var(softnet_data);
        struct sk_buff *skb, *tmp;
  
 -      skb_queue_walk_safe(&queue->input_pkt_queue, skb, tmp)
 +      rps_lock(sd);
 +      skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) {
 +              if (skb->dev == dev) {
 +                      __skb_unlink(skb, &sd->input_pkt_queue);
 +                      kfree_skb(skb);
 +                      input_queue_head_add(sd, 1);
 +              }
 +      }
 +      rps_unlock(sd);
 +
 +      skb_queue_walk_safe(&sd->process_queue, skb, tmp) {
                if (skb->dev == dev) {
 -                      __skb_unlink(skb, &queue->input_pkt_queue);
 +                      __skb_unlink(skb, &sd->process_queue);
                        kfree_skb(skb);
                }
 +      }
  }
  
  static int napi_gro_complete(struct sk_buff *skb)
@@@ -3257,85 -2910,27 +3256,85 @@@ gro_result_t napi_gro_frags(struct napi
  }
  EXPORT_SYMBOL(napi_gro_frags);
  
 +/*
 + * net_rps_action sends any pending IPI's for rps.
 + * Note: called with local irq disabled, but exits with local irq enabled.
 + */
 +static void net_rps_action_and_irq_enable(struct softnet_data *sd)
 +{
 +#ifdef CONFIG_RPS
 +      struct softnet_data *remsd = sd->rps_ipi_list;
 +
 +      if (remsd) {
 +              sd->rps_ipi_list = NULL;
 +
 +              local_irq_enable();
 +
 +              /* Send pending IPI's to kick RPS processing on remote cpus. */
 +              while (remsd) {
 +                      struct softnet_data *next = remsd->rps_ipi_next;
 +
 +                      if (cpu_online(remsd->cpu))
 +                              __smp_call_function_single(remsd->cpu,
 +                                                         &remsd->csd, 0);
 +                      remsd = next;
 +              }
 +      } else
 +#endif
 +              local_irq_enable();
 +}
 +
  static int process_backlog(struct napi_struct *napi, int quota)
  {
        int work = 0;
 -      struct softnet_data *queue = &__get_cpu_var(softnet_data);
 -      unsigned long start_time = jiffies;
 +      struct softnet_data *sd = container_of(napi, struct softnet_data, backlog);
  
 +#ifdef CONFIG_RPS
 +      /* Check if we have pending ipi, its better to send them now,
 +       * not waiting net_rx_action() end.
 +       */
 +      if (sd->rps_ipi_list) {
 +              local_irq_disable();
 +              net_rps_action_and_irq_enable(sd);
 +      }
 +#endif
        napi->weight = weight_p;
 -      do {
 +      local_irq_disable();
 +      while (work < quota) {
                struct sk_buff *skb;
 +              unsigned int qlen;
  
 -              local_irq_disable();
 -              skb = __skb_dequeue(&queue->input_pkt_queue);
 -              if (!skb) {
 -                      __napi_complete(napi);
 +              while ((skb = __skb_dequeue(&sd->process_queue))) {
                        local_irq_enable();
 -                      break;
 +                      __netif_receive_skb(skb);
 +                      if (++work >= quota)
 +                              return work;
 +                      local_irq_disable();
                }
 -              local_irq_enable();
 -
 -              netif_receive_skb(skb);
 -      } while (++work < quota && jiffies == start_time);
 +
 +              rps_lock(sd);
 +              qlen = skb_queue_len(&sd->input_pkt_queue);
 +              if (qlen) {
 +                      input_queue_head_add(sd, qlen);
 +                      skb_queue_splice_tail_init(&sd->input_pkt_queue,
 +                                                 &sd->process_queue);
 +              }
 +              if (qlen < quota - work) {
 +                      /*
 +                       * Inline a custom version of __napi_complete().
 +                       * only current cpu owns and manipulates this napi,
 +                       * and NAPI_STATE_SCHED is the only possible flag set on backlog.
 +                       * we can use a plain write instead of clear_bit(),
 +                       * and we dont need an smp_mb() memory barrier.
 +                       */
 +                      list_del(&napi->poll_list);
 +                      napi->state = 0;
 +
 +                      quota = work + qlen;
 +              }
 +              rps_unlock(sd);
 +      }
 +      local_irq_enable();
  
        return work;
  }
@@@ -3351,7 -2946,8 +3350,7 @@@ void __napi_schedule(struct napi_struc
        unsigned long flags;
  
        local_irq_save(flags);
 -      list_add_tail(&n->poll_list, &__get_cpu_var(softnet_data).poll_list);
 -      __raise_softirq_irqoff(NET_RX_SOFTIRQ);
 +      ____napi_schedule(&__get_cpu_var(softnet_data), n);
        local_irq_restore(flags);
  }
  EXPORT_SYMBOL(__napi_schedule);
@@@ -3422,16 -3018,17 +3421,16 @@@ void netif_napi_del(struct napi_struct 
  }
  EXPORT_SYMBOL(netif_napi_del);
  
 -
  static void net_rx_action(struct softirq_action *h)
  {
 -      struct list_head *list = &__get_cpu_var(softnet_data).poll_list;
 +      struct softnet_data *sd = &__get_cpu_var(softnet_data);
        unsigned long time_limit = jiffies + 2;
        int budget = netdev_budget;
        void *have;
  
        local_irq_disable();
  
 -      while (!list_empty(list)) {
 +      while (!list_empty(&sd->poll_list)) {
                struct napi_struct *n;
                int work, weight;
  
                 * entries to the tail of this list, and only ->poll()
                 * calls can remove this head entry from the list.
                 */
 -              n = list_first_entry(list, struct napi_struct, poll_list);
 +              n = list_first_entry(&sd->poll_list, struct napi_struct, poll_list);
  
                have = netpoll_poll_lock(n);
  
                                napi_complete(n);
                                local_irq_disable();
                        } else
 -                              list_move_tail(&n->poll_list, list);
 +                              list_move_tail(&n->poll_list, &sd->poll_list);
                }
  
                netpoll_poll_unlock(have);
        }
  out:
 -      local_irq_enable();
 +      net_rps_action_and_irq_enable(sd);
  
  #ifdef CONFIG_NET_DMA
        /*
        return;
  
  softnet_break:
 -      __get_cpu_var(netdev_rx_stat).time_squeeze++;
 +      sd->time_squeeze++;
        __raise_softirq_irqoff(NET_RX_SOFTIRQ);
        goto out;
  }
@@@ -3704,17 -3301,17 +3703,17 @@@ static int dev_seq_show(struct seq_fil
        return 0;
  }
  
 -static struct netif_rx_stats *softnet_get_online(loff_t *pos)
 +static struct softnet_data *softnet_get_online(loff_t *pos)
  {
 -      struct netif_rx_stats *rc = NULL;
 +      struct softnet_data *sd = NULL;
  
        while (*pos < nr_cpu_ids)
                if (cpu_online(*pos)) {
 -                      rc = &per_cpu(netdev_rx_stat, *pos);
 +                      sd = &per_cpu(softnet_data, *pos);
                        break;
                } else
                        ++*pos;
 -      return rc;
 +      return sd;
  }
  
  static void *softnet_seq_start(struct seq_file *seq, loff_t *pos)
@@@ -3734,12 -3331,12 +3733,12 @@@ static void softnet_seq_stop(struct seq
  
  static int softnet_seq_show(struct seq_file *seq, void *v)
  {
 -      struct netif_rx_stats *s = v;
 +      struct softnet_data *sd = v;
  
 -      seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
 -                 s->total, s->dropped, s->time_squeeze, 0,
 +      seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
 +                 sd->processed, sd->dropped, sd->time_squeeze, 0,
                   0, 0, 0, 0, /* was fastroute */
 -                 s->cpu_collision);
 +                 sd->cpu_collision, sd->received_rps);
        return 0;
  }
  
@@@ -3962,10 -3559,11 +3961,10 @@@ int netdev_set_master(struct net_devic
  
        slave->master = master;
  
 -      synchronize_net();
 -
 -      if (old)
 +      if (old) {
 +              synchronize_net();
                dev_put(old);
 -
 +      }
        if (master)
                slave->flags |= IFF_SLAVE;
        else
@@@ -4142,6 -3740,562 +4141,6 @@@ void dev_set_rx_mode(struct net_device 
        netif_addr_unlock_bh(dev);
  }
  
 -/* hw addresses list handling functions */
 -
 -static int __hw_addr_add(struct netdev_hw_addr_list *list, unsigned char *addr,
 -                       int addr_len, unsigned char addr_type)
 -{
 -      struct netdev_hw_addr *ha;
 -      int alloc_size;
 -
 -      if (addr_len > MAX_ADDR_LEN)
 -              return -EINVAL;
 -
 -      list_for_each_entry(ha, &list->list, list) {
 -              if (!memcmp(ha->addr, addr, addr_len) &&
 -                  ha->type == addr_type) {
 -                      ha->refcount++;
 -                      return 0;
 -              }
 -      }
 -
 -
 -      alloc_size = sizeof(*ha);
 -      if (alloc_size < L1_CACHE_BYTES)
 -              alloc_size = L1_CACHE_BYTES;
 -      ha = kmalloc(alloc_size, GFP_ATOMIC);
 -      if (!ha)
 -              return -ENOMEM;
 -      memcpy(ha->addr, addr, addr_len);
 -      ha->type = addr_type;
 -      ha->refcount = 1;
 -      ha->synced = false;
 -      list_add_tail_rcu(&ha->list, &list->list);
 -      list->count++;
 -      return 0;
 -}
 -
 -static void ha_rcu_free(struct rcu_head *head)
 -{
 -      struct netdev_hw_addr *ha;
 -
 -      ha = container_of(head, struct netdev_hw_addr, rcu_head);
 -      kfree(ha);
 -}
 -
 -static int __hw_addr_del(struct netdev_hw_addr_list *list, unsigned char *addr,
 -                       int addr_len, unsigned char addr_type)
 -{
 -      struct netdev_hw_addr *ha;
 -
 -      list_for_each_entry(ha, &list->list, list) {
 -              if (!memcmp(ha->addr, addr, addr_len) &&
 -                  (ha->type == addr_type || !addr_type)) {
 -                      if (--ha->refcount)
 -                              return 0;
 -                      list_del_rcu(&ha->list);
 -                      call_rcu(&ha->rcu_head, ha_rcu_free);
 -                      list->count--;
 -                      return 0;
 -              }
 -      }
 -      return -ENOENT;
 -}
 -
 -static int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list,
 -                                struct netdev_hw_addr_list *from_list,
 -                                int addr_len,
 -                                unsigned char addr_type)
 -{
 -      int err;
 -      struct netdev_hw_addr *ha, *ha2;
 -      unsigned char type;
 -
 -      list_for_each_entry(ha, &from_list->list, list) {
 -              type = addr_type ? addr_type : ha->type;
 -              err = __hw_addr_add(to_list, ha->addr, addr_len, type);
 -              if (err)
 -                      goto unroll;
 -      }
 -      return 0;
 -
 -unroll:
 -      list_for_each_entry(ha2, &from_list->list, list) {
 -              if (ha2 == ha)
 -                      break;
 -              type = addr_type ? addr_type : ha2->type;
 -              __hw_addr_del(to_list, ha2->addr, addr_len, type);
 -      }
 -      return err;
 -}
 -
 -static void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list,
 -                                 struct netdev_hw_addr_list *from_list,
 -                                 int addr_len,
 -                                 unsigned char addr_type)
 -{
 -      struct netdev_hw_addr *ha;
 -      unsigned char type;
 -
 -      list_for_each_entry(ha, &from_list->list, list) {
 -              type = addr_type ? addr_type : ha->type;
 -              __hw_addr_del(to_list, ha->addr, addr_len, addr_type);
 -      }
 -}
 -
 -static int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
 -                        struct netdev_hw_addr_list *from_list,
 -                        int addr_len)
 -{
 -      int err = 0;
 -      struct netdev_hw_addr *ha, *tmp;
 -
 -      list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
 -              if (!ha->synced) {
 -                      err = __hw_addr_add(to_list, ha->addr,
 -                                          addr_len, ha->type);
 -                      if (err)
 -                              break;
 -                      ha->synced = true;
 -                      ha->refcount++;
 -              } else if (ha->refcount == 1) {
 -                      __hw_addr_del(to_list, ha->addr, addr_len, ha->type);
 -                      __hw_addr_del(from_list, ha->addr, addr_len, ha->type);
 -              }
 -      }
 -      return err;
 -}
 -
 -static void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
 -                           struct netdev_hw_addr_list *from_list,
 -                           int addr_len)
 -{
 -      struct netdev_hw_addr *ha, *tmp;
 -
 -      list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
 -              if (ha->synced) {
 -                      __hw_addr_del(to_list, ha->addr,
 -                                    addr_len, ha->type);
 -                      ha->synced = false;
 -                      __hw_addr_del(from_list, ha->addr,
 -                                    addr_len, ha->type);
 -              }
 -      }
 -}
 -
 -static void __hw_addr_flush(struct netdev_hw_addr_list *list)
 -{
 -      struct netdev_hw_addr *ha, *tmp;
 -
 -      list_for_each_entry_safe(ha, tmp, &list->list, list) {
 -              list_del_rcu(&ha->list);
 -              call_rcu(&ha->rcu_head, ha_rcu_free);
 -      }
 -      list->count = 0;
 -}
 -
 -static void __hw_addr_init(struct netdev_hw_addr_list *list)
 -{
 -      INIT_LIST_HEAD(&list->list);
 -      list->count = 0;
 -}
 -
 -/* Device addresses handling functions */
 -
 -static void dev_addr_flush(struct net_device *dev)
 -{
 -      /* rtnl_mutex must be held here */
 -
 -      __hw_addr_flush(&dev->dev_addrs);
 -      dev->dev_addr = NULL;
 -}
 -
 -static int dev_addr_init(struct net_device *dev)
 -{
 -      unsigned char addr[MAX_ADDR_LEN];
 -      struct netdev_hw_addr *ha;
 -      int err;
 -
 -      /* rtnl_mutex must be held here */
 -
 -      __hw_addr_init(&dev->dev_addrs);
 -      memset(addr, 0, sizeof(addr));
 -      err = __hw_addr_add(&dev->dev_addrs, addr, sizeof(addr),
 -                          NETDEV_HW_ADDR_T_LAN);
 -      if (!err) {
 -              /*
 -               * Get the first (previously created) address from the list
 -               * and set dev_addr pointer to this location.
 -               */
 -              ha = list_first_entry(&dev->dev_addrs.list,
 -                                    struct netdev_hw_addr, list);
 -              dev->dev_addr = ha->addr;
 -      }
 -      return err;
 -}
 -
 -/**
 - *    dev_addr_add    - Add a device address
 - *    @dev: device
 - *    @addr: address to add
 - *    @addr_type: address type
 - *
 - *    Add a device address to the device or increase the reference count if
 - *    it already exists.
 - *
 - *    The caller must hold the rtnl_mutex.
 - */
 -int dev_addr_add(struct net_device *dev, unsigned char *addr,
 -               unsigned char addr_type)
 -{
 -      int err;
 -
 -      ASSERT_RTNL();
 -
 -      err = __hw_addr_add(&dev->dev_addrs, addr, dev->addr_len, addr_type);
 -      if (!err)
 -              call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
 -      return err;
 -}
 -EXPORT_SYMBOL(dev_addr_add);
 -
 -/**
 - *    dev_addr_del    - Release a device address.
 - *    @dev: device
 - *    @addr: address to delete
 - *    @addr_type: address type
 - *
 - *    Release reference to a device address and remove it from the device
 - *    if the reference count drops to zero.
 - *
 - *    The caller must hold the rtnl_mutex.
 - */
 -int dev_addr_del(struct net_device *dev, unsigned char *addr,
 -               unsigned char addr_type)
 -{
 -      int err;
 -      struct netdev_hw_addr *ha;
 -
 -      ASSERT_RTNL();
 -
 -      /*
 -       * We can not remove the first address from the list because
 -       * dev->dev_addr points to that.
 -       */
 -      ha = list_first_entry(&dev->dev_addrs.list,
 -                            struct netdev_hw_addr, list);
 -      if (ha->addr == dev->dev_addr && ha->refcount == 1)
 -              return -ENOENT;
 -
 -      err = __hw_addr_del(&dev->dev_addrs, addr, dev->addr_len,
 -                          addr_type);
 -      if (!err)
 -              call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
 -      return err;
 -}
 -EXPORT_SYMBOL(dev_addr_del);
 -
 -/**
 - *    dev_addr_add_multiple   - Add device addresses from another device
 - *    @to_dev: device to which addresses will be added
 - *    @from_dev: device from which addresses will be added
 - *    @addr_type: address type - 0 means type will be used from from_dev
 - *
 - *    Add device addresses of the one device to another.
 - **
 - *    The caller must hold the rtnl_mutex.
 - */
 -int dev_addr_add_multiple(struct net_device *to_dev,
 -                        struct net_device *from_dev,
 -                        unsigned char addr_type)
 -{
 -      int err;
 -
 -      ASSERT_RTNL();
 -
 -      if (from_dev->addr_len != to_dev->addr_len)
 -              return -EINVAL;
 -      err = __hw_addr_add_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs,
 -                                   to_dev->addr_len, addr_type);
 -      if (!err)
 -              call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev);
 -      return err;
 -}
 -EXPORT_SYMBOL(dev_addr_add_multiple);
 -
 -/**
 - *    dev_addr_del_multiple   - Delete device addresses by another device
 - *    @to_dev: device where the addresses will be deleted
 - *    @from_dev: device by which addresses the addresses will be deleted
 - *    @addr_type: address type - 0 means type will used from from_dev
 - *
 - *    Deletes addresses in to device by the list of addresses in from device.
 - *
 - *    The caller must hold the rtnl_mutex.
 - */
 -int dev_addr_del_multiple(struct net_device *to_dev,
 -                        struct net_device *from_dev,
 -                        unsigned char addr_type)
 -{
 -      ASSERT_RTNL();
 -
 -      if (from_dev->addr_len != to_dev->addr_len)
 -              return -EINVAL;
 -      __hw_addr_del_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs,
 -                             to_dev->addr_len, addr_type);
 -      call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev);
 -      return 0;
 -}
 -EXPORT_SYMBOL(dev_addr_del_multiple);
 -
 -/* multicast addresses handling functions */
 -
 -int __dev_addr_delete(struct dev_addr_list **list, int *count,
 -                    void *addr, int alen, int glbl)
 -{
 -      struct dev_addr_list *da;
 -
 -      for (; (da = *list) != NULL; list = &da->next) {
 -              if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
 -                  alen == da->da_addrlen) {
 -                      if (glbl) {
 -                              int old_glbl = da->da_gusers;
 -                              da->da_gusers = 0;
 -                              if (old_glbl == 0)
 -                                      break;
 -                      }
 -                      if (--da->da_users)
 -                              return 0;
 -
 -                      *list = da->next;
 -                      kfree(da);
 -                      (*count)--;
 -                      return 0;
 -              }
 -      }
 -      return -ENOENT;
 -}
 -
 -int __dev_addr_add(struct dev_addr_list **list, int *count,
 -                 void *addr, int alen, int glbl)
 -{
 -      struct dev_addr_list *da;
 -
 -      for (da = *list; da != NULL; da = da->next) {
 -              if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
 -                  da->da_addrlen == alen) {
 -                      if (glbl) {
 -                              int old_glbl = da->da_gusers;
 -                              da->da_gusers = 1;
 -                              if (old_glbl)
 -                                      return 0;
 -                      }
 -                      da->da_users++;
 -                      return 0;
 -              }
 -      }
 -
 -      da = kzalloc(sizeof(*da), GFP_ATOMIC);
 -      if (da == NULL)
 -              return -ENOMEM;
 -      memcpy(da->da_addr, addr, alen);
 -      da->da_addrlen = alen;
 -      da->da_users = 1;
 -      da->da_gusers = glbl ? 1 : 0;
 -      da->next = *list;
 -      *list = da;
 -      (*count)++;
 -      return 0;
 -}
 -
 -/**
 - *    dev_unicast_delete      - Release secondary unicast address.
 - *    @dev: device
 - *    @addr: address to delete
 - *
 - *    Release reference to a secondary unicast address and remove it
 - *    from the device if the reference count drops to zero.
 - *
 - *    The caller must hold the rtnl_mutex.
 - */
 -int dev_unicast_delete(struct net_device *dev, void *addr)
 -{
 -      int err;
 -
 -      ASSERT_RTNL();
 -
 -      netif_addr_lock_bh(dev);
 -      err = __hw_addr_del(&dev->uc, addr, dev->addr_len,
 -                          NETDEV_HW_ADDR_T_UNICAST);
 -      if (!err)
 -              __dev_set_rx_mode(dev);
 -      netif_addr_unlock_bh(dev);
 -      return err;
 -}
 -EXPORT_SYMBOL(dev_unicast_delete);
 -
 -/**
 - *    dev_unicast_add         - add a secondary unicast address
 - *    @dev: device
 - *    @addr: address to add
 - *
 - *    Add a secondary unicast address to the device or increase
 - *    the reference count if it already exists.
 - *
 - *    The caller must hold the rtnl_mutex.
 - */
 -int dev_unicast_add(struct net_device *dev, void *addr)
 -{
 -      int err;
 -
 -      ASSERT_RTNL();
 -
 -      netif_addr_lock_bh(dev);
 -      err = __hw_addr_add(&dev->uc, addr, dev->addr_len,
 -                          NETDEV_HW_ADDR_T_UNICAST);
 -      if (!err)
 -              __dev_set_rx_mode(dev);
 -      netif_addr_unlock_bh(dev);
 -      return err;
 -}
 -EXPORT_SYMBOL(dev_unicast_add);
 -
 -int __dev_addr_sync(struct dev_addr_list **to, int *to_count,
 -                  struct dev_addr_list **from, int *from_count)
 -{
 -      struct dev_addr_list *da, *next;
 -      int err = 0;
 -
 -      da = *from;
 -      while (da != NULL) {
 -              next = da->next;
 -              if (!da->da_synced) {
 -                      err = __dev_addr_add(to, to_count,
 -                                           da->da_addr, da->da_addrlen, 0);
 -                      if (err < 0)
 -                              break;
 -                      da->da_synced = 1;
 -                      da->da_users++;
 -              } else if (da->da_users == 1) {
 -                      __dev_addr_delete(to, to_count,
 -                                        da->da_addr, da->da_addrlen, 0);
 -                      __dev_addr_delete(from, from_count,
 -                                        da->da_addr, da->da_addrlen, 0);
 -              }
 -              da = next;
 -      }
 -      return err;
 -}
 -EXPORT_SYMBOL_GPL(__dev_addr_sync);
 -
 -void __dev_addr_unsync(struct dev_addr_list **to, int *to_count,
 -                     struct dev_addr_list **from, int *from_count)
 -{
 -      struct dev_addr_list *da, *next;
 -
 -      da = *from;
 -      while (da != NULL) {
 -              next = da->next;
 -              if (da->da_synced) {
 -                      __dev_addr_delete(to, to_count,
 -                                        da->da_addr, da->da_addrlen, 0);
 -                      da->da_synced = 0;
 -                      __dev_addr_delete(from, from_count,
 -                                        da->da_addr, da->da_addrlen, 0);
 -              }
 -              da = next;
 -      }
 -}
 -EXPORT_SYMBOL_GPL(__dev_addr_unsync);
 -
 -/**
 - *    dev_unicast_sync - Synchronize device's unicast list to another device
 - *    @to: destination device
 - *    @from: source device
 - *
 - *    Add newly added addresses to the destination device and release
 - *    addresses that have no users left. The source device must be
 - *    locked by netif_tx_lock_bh.
 - *
 - *    This function is intended to be called from the dev->set_rx_mode
 - *    function of layered software devices.
 - */
 -int dev_unicast_sync(struct net_device *to, struct net_device *from)
 -{
 -      int err = 0;
 -
 -      if (to->addr_len != from->addr_len)
 -              return -EINVAL;
 -
 -      netif_addr_lock_bh(to);
 -      err = __hw_addr_sync(&to->uc, &from->uc, to->addr_len);
 -      if (!err)
 -              __dev_set_rx_mode(to);
 -      netif_addr_unlock_bh(to);
 -      return err;
 -}
 -EXPORT_SYMBOL(dev_unicast_sync);
 -
 -/**
 - *    dev_unicast_unsync - Remove synchronized addresses from the destination device
 - *    @to: destination device
 - *    @from: source device
 - *
 - *    Remove all addresses that were added to the destination device by
 - *    dev_unicast_sync(). This function is intended to be called from the
 - *    dev->stop function of layered software devices.
 - */
 -void dev_unicast_unsync(struct net_device *to, struct net_device *from)
 -{
 -      if (to->addr_len != from->addr_len)
 -              return;
 -
 -      netif_addr_lock_bh(from);
 -      netif_addr_lock(to);
 -      __hw_addr_unsync(&to->uc, &from->uc, to->addr_len);
 -      __dev_set_rx_mode(to);
 -      netif_addr_unlock(to);
 -      netif_addr_unlock_bh(from);
 -}
 -EXPORT_SYMBOL(dev_unicast_unsync);
 -
 -static void dev_unicast_flush(struct net_device *dev)
 -{
 -      netif_addr_lock_bh(dev);
 -      __hw_addr_flush(&dev->uc);
 -      netif_addr_unlock_bh(dev);
 -}
 -
 -static void dev_unicast_init(struct net_device *dev)
 -{
 -      __hw_addr_init(&dev->uc);
 -}
 -
 -
 -static void __dev_addr_discard(struct dev_addr_list **list)
 -{
 -      struct dev_addr_list *tmp;
 -
 -      while (*list != NULL) {
 -              tmp = *list;
 -              *list = tmp->next;
 -              if (tmp->da_users > tmp->da_gusers)
 -                      printk("__dev_addr_discard: address leakage! "
 -                             "da_users=%d\n", tmp->da_users);
 -              kfree(tmp);
 -      }
 -}
 -
 -static void dev_addr_discard(struct net_device *dev)
 -{
 -      netif_addr_lock_bh(dev);
 -
 -      __dev_addr_discard(&dev->mc_list);
 -      netdev_mc_count(dev) = 0;
 -
 -      netif_addr_unlock_bh(dev);
 -}
 -
  /**
   *    dev_get_flags - get flags reported to userspace
   *    @dev: device
@@@ -4452,7 -4606,8 +4451,7 @@@ static int dev_ifsioc(struct net *net, 
                        return -EINVAL;
                if (!netif_device_present(dev))
                        return -ENODEV;
 -              return dev_mc_add(dev, ifr->ifr_hwaddr.sa_data,
 -                                dev->addr_len, 1);
 +              return dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data);
  
        case SIOCDELMULTI:
                if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) ||
                        return -EINVAL;
                if (!netif_device_present(dev))
                        return -ENODEV;
 -              return dev_mc_delete(dev, ifr->ifr_hwaddr.sa_data,
 -                                   dev->addr_len, 1);
 +              return dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data);
  
        case SIOCSIFTXQLEN:
                if (ifr->ifr_qlen < 0)
@@@ -4767,8 -4923,8 +4766,8 @@@ static void rollback_registered_many(st
                /*
                 *      Flush the unicast and multicast chains
                 */
 -              dev_unicast_flush(dev);
 -              dev_addr_discard(dev);
 +              dev_uc_flush(dev);
 +              dev_mc_flush(dev);
  
                if (dev->netdev_ops->ndo_uninit)
                        dev->netdev_ops->ndo_uninit(dev);
@@@ -4917,24 -5073,6 +4916,24 @@@ int register_netdevice(struct net_devic
  
        dev->iflink = -1;
  
 +#ifdef CONFIG_RPS
 +      if (!dev->num_rx_queues) {
 +              /*
 +               * Allocate a single RX queue if driver never called
 +               * alloc_netdev_mq
 +               */
 +
 +              dev->_rx = kzalloc(sizeof(struct netdev_rx_queue), GFP_KERNEL);
 +              if (!dev->_rx) {
 +                      ret = -ENOMEM;
 +                      goto out;
 +              }
 +
 +              dev->_rx->first = dev->_rx;
 +              atomic_set(&dev->_rx->count, 1);
 +              dev->num_rx_queues = 1;
 +      }
 +#endif
        /* Init, if this function is available */
        if (dev->netdev_ops->ndo_init) {
                ret = dev->netdev_ops->ndo_init(dev);
@@@ -5295,10 -5433,6 +5294,10 @@@ struct net_device *alloc_netdev_mq(int 
        struct net_device *dev;
        size_t alloc_size;
        struct net_device *p;
 +#ifdef CONFIG_RPS
 +      struct netdev_rx_queue *rx;
 +      int i;
 +#endif
  
        BUG_ON(strlen(name) >= sizeof(dev->name));
  
                goto free_p;
        }
  
 +#ifdef CONFIG_RPS
 +      rx = kcalloc(queue_count, sizeof(struct netdev_rx_queue), GFP_KERNEL);
 +      if (!rx) {
 +              printk(KERN_ERR "alloc_netdev: Unable to allocate "
 +                     "rx queues.\n");
 +              goto free_tx;
 +      }
 +
 +      atomic_set(&rx->count, queue_count);
 +
 +      /*
 +       * Set a pointer to first element in the array which holds the
 +       * reference count.
 +       */
 +      for (i = 0; i < queue_count; i++)
 +              rx[i].first = rx;
 +#endif
 +
        dev = PTR_ALIGN(p, NETDEV_ALIGN);
        dev->padded = (char *)dev - (char *)p;
  
        if (dev_addr_init(dev))
 -              goto free_tx;
 +              goto free_rx;
  
 -      dev_unicast_init(dev);
 +      dev_mc_init(dev);
 +      dev_uc_init(dev);
  
        dev_net_set(dev, &init_net);
  
        dev->num_tx_queues = queue_count;
        dev->real_num_tx_queues = queue_count;
  
 +#ifdef CONFIG_RPS
 +      dev->_rx = rx;
 +      dev->num_rx_queues = queue_count;
 +#endif
 +
        dev->gso_max_size = GSO_MAX_SIZE;
  
        netdev_init_queues(dev);
        strcpy(dev->name, name);
        return dev;
  
 +free_rx:
 +#ifdef CONFIG_RPS
 +      kfree(rx);
  free_tx:
 +#endif
        kfree(tx);
 -
  free_p:
        kfree(p);
        return NULL;
@@@ -5583,8 -5690,8 +5582,8 @@@ int dev_change_net_namespace(struct net
        /*
         *      Flush the unicast and multicast chains
         */
 -      dev_unicast_flush(dev);
 -      dev_addr_discard(dev);
 +      dev_uc_flush(dev);
 +      dev_mc_flush(dev);
  
        netdev_unregister_kobject(dev);
  
@@@ -5627,6 -5734,7 +5626,6 @@@ static int dev_cpu_callback(struct noti
                            void *ocpu)
  {
        struct sk_buff **list_skb;
 -      struct Qdisc **list_net;
        struct sk_buff *skb;
        unsigned int cpu, oldcpu = (unsigned long)ocpu;
        struct softnet_data *sd, *oldsd;
        *list_skb = oldsd->completion_queue;
        oldsd->completion_queue = NULL;
  
 -      /* Find end of our output_queue. */
 -      list_net = &sd->output_queue;
 -      while (*list_net)
 -              list_net = &(*list_net)->next_sched;
        /* Append output queue from offline CPU. */
 -      *list_net = oldsd->output_queue;
 -      oldsd->output_queue = NULL;
 +      if (oldsd->output_queue) {
 +              *sd->output_queue_tailp = oldsd->output_queue;
 +              sd->output_queue_tailp = oldsd->output_queue_tailp;
 +              oldsd->output_queue = NULL;
 +              oldsd->output_queue_tailp = &oldsd->output_queue;
 +      }
  
        raise_softirq_irqoff(NET_TX_SOFTIRQ);
        local_irq_enable();
  
        /* Process offline CPU's input_pkt_queue */
 -      while ((skb = __skb_dequeue(&oldsd->input_pkt_queue)))
 +      while ((skb = __skb_dequeue(&oldsd->input_pkt_queue))) {
 +              netif_rx(skb);
 +              input_queue_head_add(oldsd, 1);
 +      }
 +      while ((skb = __skb_dequeue(&oldsd->process_queue)))
                netif_rx(skb);
  
        return NOTIFY_OK;
@@@ -5880,26 -5984,17 +5879,26 @@@ static int __init net_dev_init(void
         */
  
        for_each_possible_cpu(i) {
 -              struct softnet_data *queue;
 +              struct softnet_data *sd = &per_cpu(softnet_data, i);
  
 -              queue = &per_cpu(softnet_data, i);
 -              skb_queue_head_init(&queue->input_pkt_queue);
 -              queue->completion_queue = NULL;
 -              INIT_LIST_HEAD(&queue->poll_list);
 +              memset(sd, 0, sizeof(*sd));
 +              skb_queue_head_init(&sd->input_pkt_queue);
 +              skb_queue_head_init(&sd->process_queue);
 +              sd->completion_queue = NULL;
 +              INIT_LIST_HEAD(&sd->poll_list);
 +              sd->output_queue = NULL;
 +              sd->output_queue_tailp = &sd->output_queue;
 +#ifdef CONFIG_RPS
 +              sd->csd.func = rps_trigger_softirq;
 +              sd->csd.info = sd;
 +              sd->csd.flags = 0;
 +              sd->cpu = i;
 +#endif
  
 -              queue->backlog.poll = process_backlog;
 -              queue->backlog.weight = weight_p;
 -              queue->backlog.gro_list = NULL;
 -              queue->backlog.gro_count = 0;
 +              sd->backlog.poll = process_backlog;
 +              sd->backlog.weight = weight_p;
 +              sd->backlog.gro_list = NULL;
 +              sd->backlog.gro_count = 0;
        }
  
        dev_boot_phase = 0;
@@@ -5934,7 -6029,7 +5933,7 @@@ subsys_initcall(net_dev_init)
  
  static int __init initialize_hashrnd(void)
  {
 -      get_random_bytes(&skb_tx_hashrnd, sizeof(skb_tx_hashrnd));
 +      get_random_bytes(&hashrnd, sizeof(hashrnd));
        return 0;
  }
  
diff --combined net/ipv4/ipmr.c
  #include <net/ipip.h>
  #include <net/checksum.h>
  #include <net/netlink.h>
 +#include <net/fib_rules.h>
  
  #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
  #define CONFIG_IP_PIMSM       1
  #endif
  
 +struct mr_table {
 +      struct list_head        list;
 +#ifdef CONFIG_NET_NS
 +      struct net              *net;
 +#endif
 +      u32                     id;
 +      struct sock             *mroute_sk;
 +      struct timer_list       ipmr_expire_timer;
 +      struct list_head        mfc_unres_queue;
 +      struct list_head        mfc_cache_array[MFC_LINES];
 +      struct vif_device       vif_table[MAXVIFS];
 +      int                     maxvif;
 +      atomic_t                cache_resolve_queue_len;
 +      int                     mroute_do_assert;
 +      int                     mroute_do_pim;
 +#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
 +      int                     mroute_reg_vif_num;
 +#endif
 +};
 +
 +struct ipmr_rule {
 +      struct fib_rule         common;
 +};
 +
 +struct ipmr_result {
 +      struct mr_table         *mrt;
 +};
 +
  /* Big lock, protecting vif table, mrt cache and mroute socket state.
     Note that the changes are semaphored via rtnl_lock.
   */
@@@ -107,7 -78,9 +107,7 @@@ static DEFINE_RWLOCK(mrt_lock)
   *    Multicast router control variables
   */
  
 -#define VIF_EXISTS(_net, _idx) ((_net)->ipv4.vif_table[_idx].dev != NULL)
 -
 -static struct mfc_cache *mfc_unres_queue;             /* Queue of unresolved entries */
 +#define VIF_EXISTS(_mrt, _idx) ((_mrt)->vif_table[_idx].dev != NULL)
  
  /* Special spinlock for queue of unresolved entries */
  static DEFINE_SPINLOCK(mfc_unres_lock);
  
  static struct kmem_cache *mrt_cachep __read_mostly;
  
 -static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local);
 -static int ipmr_cache_report(struct net *net,
 +static struct mr_table *ipmr_new_table(struct net *net, u32 id);
 +static int ip_mr_forward(struct net *net, struct mr_table *mrt,
 +                       struct sk_buff *skb, struct mfc_cache *cache,
 +                       int local);
 +static int ipmr_cache_report(struct mr_table *mrt,
                             struct sk_buff *pkt, vifi_t vifi, int assert);
 -static int ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm);
 +static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
 +                            struct mfc_cache *c, struct rtmsg *rtm);
 +static void ipmr_expire_process(unsigned long arg);
 +
 +#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
 +#define ipmr_for_each_table(mrt, net) \
 +      list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list)
 +
 +static struct mr_table *ipmr_get_table(struct net *net, u32 id)
 +{
 +      struct mr_table *mrt;
 +
 +      ipmr_for_each_table(mrt, net) {
 +              if (mrt->id == id)
 +                      return mrt;
 +      }
 +      return NULL;
 +}
 +
 +static int ipmr_fib_lookup(struct net *net, struct flowi *flp,
 +                         struct mr_table **mrt)
 +{
 +      struct ipmr_result res;
 +      struct fib_lookup_arg arg = { .result = &res, };
 +      int err;
 +
 +      err = fib_rules_lookup(net->ipv4.mr_rules_ops, flp, 0, &arg);
 +      if (err < 0)
 +              return err;
 +      *mrt = res.mrt;
 +      return 0;
 +}
 +
 +static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp,
 +                          int flags, struct fib_lookup_arg *arg)
 +{
 +      struct ipmr_result *res = arg->result;
 +      struct mr_table *mrt;
 +
 +      switch (rule->action) {
 +      case FR_ACT_TO_TBL:
 +              break;
 +      case FR_ACT_UNREACHABLE:
 +              return -ENETUNREACH;
 +      case FR_ACT_PROHIBIT:
 +              return -EACCES;
 +      case FR_ACT_BLACKHOLE:
 +      default:
 +              return -EINVAL;
 +      }
 +
 +      mrt = ipmr_get_table(rule->fr_net, rule->table);
 +      if (mrt == NULL)
 +              return -EAGAIN;
 +      res->mrt = mrt;
 +      return 0;
 +}
 +
 +static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
 +{
 +      return 1;
 +}
  
 -static struct timer_list ipmr_expire_timer;
 +static const struct nla_policy ipmr_rule_policy[FRA_MAX + 1] = {
 +      FRA_GENERIC_POLICY,
 +};
 +
 +static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 +                             struct fib_rule_hdr *frh, struct nlattr **tb)
 +{
 +      return 0;
 +}
 +
 +static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
 +                           struct nlattr **tb)
 +{
 +      return 1;
 +}
 +
 +static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
 +                        struct fib_rule_hdr *frh)
 +{
 +      frh->dst_len = 0;
 +      frh->src_len = 0;
 +      frh->tos     = 0;
 +      return 0;
 +}
 +
 +static const struct fib_rules_ops __net_initdata ipmr_rules_ops_template = {
 +      .family         = RTNL_FAMILY_IPMR,
 +      .rule_size      = sizeof(struct ipmr_rule),
 +      .addr_size      = sizeof(u32),
 +      .action         = ipmr_rule_action,
 +      .match          = ipmr_rule_match,
 +      .configure      = ipmr_rule_configure,
 +      .compare        = ipmr_rule_compare,
 +      .default_pref   = fib_default_rule_pref,
 +      .fill           = ipmr_rule_fill,
 +      .nlgroup        = RTNLGRP_IPV4_RULE,
 +      .policy         = ipmr_rule_policy,
 +      .owner          = THIS_MODULE,
 +};
 +
 +static int __net_init ipmr_rules_init(struct net *net)
 +{
 +      struct fib_rules_ops *ops;
 +      struct mr_table *mrt;
 +      int err;
 +
 +      ops = fib_rules_register(&ipmr_rules_ops_template, net);
 +      if (IS_ERR(ops))
 +              return PTR_ERR(ops);
 +
 +      INIT_LIST_HEAD(&net->ipv4.mr_tables);
 +
 +      mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
 +      if (mrt == NULL) {
 +              err = -ENOMEM;
 +              goto err1;
 +      }
 +
 +      err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0);
 +      if (err < 0)
 +              goto err2;
 +
 +      net->ipv4.mr_rules_ops = ops;
 +      return 0;
 +
 +err2:
 +      kfree(mrt);
 +err1:
 +      fib_rules_unregister(ops);
 +      return err;
 +}
 +
 +static void __net_exit ipmr_rules_exit(struct net *net)
 +{
 +      struct mr_table *mrt, *next;
 +
 +      list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list)
 +              kfree(mrt);
 +      fib_rules_unregister(net->ipv4.mr_rules_ops);
 +}
 +#else
 +#define ipmr_for_each_table(mrt, net) \
 +      for (mrt = net->ipv4.mrt; mrt; mrt = NULL)
 +
 +static struct mr_table *ipmr_get_table(struct net *net, u32 id)
 +{
 +      return net->ipv4.mrt;
 +}
 +
 +static int ipmr_fib_lookup(struct net *net, struct flowi *flp,
 +                         struct mr_table **mrt)
 +{
 +      *mrt = net->ipv4.mrt;
 +      return 0;
 +}
 +
 +static int __net_init ipmr_rules_init(struct net *net)
 +{
 +      net->ipv4.mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
 +      return net->ipv4.mrt ? 0 : -ENOMEM;
 +}
 +
 +static void __net_exit ipmr_rules_exit(struct net *net)
 +{
 +      kfree(net->ipv4.mrt);
 +}
 +#endif
 +
 +static struct mr_table *ipmr_new_table(struct net *net, u32 id)
 +{
 +      struct mr_table *mrt;
 +      unsigned int i;
 +
 +      mrt = ipmr_get_table(net, id);
 +      if (mrt != NULL)
 +              return mrt;
 +
 +      mrt = kzalloc(sizeof(*mrt), GFP_KERNEL);
 +      if (mrt == NULL)
 +              return NULL;
 +      write_pnet(&mrt->net, net);
 +      mrt->id = id;
 +
 +      /* Forwarding cache */
 +      for (i = 0; i < MFC_LINES; i++)
 +              INIT_LIST_HEAD(&mrt->mfc_cache_array[i]);
 +
 +      INIT_LIST_HEAD(&mrt->mfc_unres_queue);
 +
 +      setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process,
 +                  (unsigned long)mrt);
 +
 +#ifdef CONFIG_IP_PIMSM
 +      mrt->mroute_reg_vif_num = -1;
 +#endif
 +#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
 +      list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables);
 +#endif
 +      return mrt;
 +}
  
  /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */
  
@@@ -431,22 -201,12 +431,22 @@@ failure
  static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
  {
        struct net *net = dev_net(dev);
 +      struct mr_table *mrt;
 +      struct flowi fl = {
 +              .oif            = dev->ifindex,
 +              .iif            = skb->skb_iif,
 +              .mark           = skb->mark,
 +      };
 +      int err;
 +
 +      err = ipmr_fib_lookup(net, &fl, &mrt);
 +      if (err < 0)
 +              return err;
  
        read_lock(&mrt_lock);
        dev->stats.tx_bytes += skb->len;
        dev->stats.tx_packets++;
 -      ipmr_cache_report(net, skb, net->ipv4.mroute_reg_vif_num,
 -                        IGMPMSG_WHOLEPKT);
 +      ipmr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, IGMPMSG_WHOLEPKT);
        read_unlock(&mrt_lock);
        kfree_skb(skb);
        return NETDEV_TX_OK;
@@@ -466,18 -226,12 +466,18 @@@ static void reg_vif_setup(struct net_de
        dev->features           |= NETIF_F_NETNS_LOCAL;
  }
  
 -static struct net_device *ipmr_reg_vif(struct net *net)
 +static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
  {
        struct net_device *dev;
        struct in_device *in_dev;
 +      char name[IFNAMSIZ];
  
 -      dev = alloc_netdev(0, "pimreg", reg_vif_setup);
 +      if (mrt->id == RT_TABLE_DEFAULT)
 +              sprintf(name, "pimreg");
 +      else
 +              sprintf(name, "pimreg%u", mrt->id);
 +
 +      dev = alloc_netdev(0, name, reg_vif_setup);
  
        if (dev == NULL)
                return NULL;
@@@ -522,17 -276,17 +522,17 @@@ failure
   *    @notify: Set to 1, if the caller is a notifier_call
   */
  
 -static int vif_delete(struct net *net, int vifi, int notify,
 +static int vif_delete(struct mr_table *mrt, int vifi, int notify,
                      struct list_head *head)
  {
        struct vif_device *v;
        struct net_device *dev;
        struct in_device *in_dev;
  
 -      if (vifi < 0 || vifi >= net->ipv4.maxvif)
 +      if (vifi < 0 || vifi >= mrt->maxvif)
                return -EADDRNOTAVAIL;
  
 -      v = &net->ipv4.vif_table[vifi];
 +      v = &mrt->vif_table[vifi];
  
        write_lock_bh(&mrt_lock);
        dev = v->dev;
        }
  
  #ifdef CONFIG_IP_PIMSM
 -      if (vifi == net->ipv4.mroute_reg_vif_num)
 -              net->ipv4.mroute_reg_vif_num = -1;
 +      if (vifi == mrt->mroute_reg_vif_num)
 +              mrt->mroute_reg_vif_num = -1;
  #endif
  
 -      if (vifi+1 == net->ipv4.maxvif) {
 +      if (vifi+1 == mrt->maxvif) {
                int tmp;
                for (tmp=vifi-1; tmp>=0; tmp--) {
 -                      if (VIF_EXISTS(net, tmp))
 +                      if (VIF_EXISTS(mrt, tmp))
                                break;
                }
 -              net->ipv4.maxvif = tmp+1;
 +              mrt->maxvif = tmp+1;
        }
  
        write_unlock_bh(&mrt_lock);
  
  static inline void ipmr_cache_free(struct mfc_cache *c)
  {
 -      release_net(mfc_net(c));
        kmem_cache_free(mrt_cachep, c);
  }
  
     and reporting error to netlink readers.
   */
  
 -static void ipmr_destroy_unres(struct mfc_cache *c)
 +static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
  {
 +      struct net *net = read_pnet(&mrt->net);
        struct sk_buff *skb;
        struct nlmsgerr *e;
 -      struct net *net = mfc_net(c);
  
 -      atomic_dec(&net->ipv4.cache_resolve_queue_len);
 +      atomic_dec(&mrt->cache_resolve_queue_len);
  
        while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) {
                if (ip_hdr(skb)->version == 0) {
  }
  
  
 -/* Single timer process for all the unresolved queue. */
 +/* Timer process for the unresolved queue. */
  
 -static void ipmr_expire_process(unsigned long dummy)
 +static void ipmr_expire_process(unsigned long arg)
  {
 +      struct mr_table *mrt = (struct mr_table *)arg;
        unsigned long now;
        unsigned long expires;
 -      struct mfc_cache *c, **cp;
 +      struct mfc_cache *c, *next;
  
        if (!spin_trylock(&mfc_unres_lock)) {
 -              mod_timer(&ipmr_expire_timer, jiffies+HZ/10);
 +              mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10);
                return;
        }
  
 -      if (mfc_unres_queue == NULL)
 +      if (list_empty(&mrt->mfc_unres_queue))
                goto out;
  
        now = jiffies;
        expires = 10*HZ;
 -      cp = &mfc_unres_queue;
  
 -      while ((c=*cp) != NULL) {
 +      list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
                if (time_after(c->mfc_un.unres.expires, now)) {
                        unsigned long interval = c->mfc_un.unres.expires - now;
                        if (interval < expires)
                                expires = interval;
                        continue;
                }
  
 -              *cp = c->next;
 -
 -              ipmr_destroy_unres(c);
 +              list_del(&c->list);
 +              ipmr_destroy_unres(mrt, c);
        }
  
 -      if (mfc_unres_queue != NULL)
 -              mod_timer(&ipmr_expire_timer, jiffies + expires);
 +      if (!list_empty(&mrt->mfc_unres_queue))
 +              mod_timer(&mrt->ipmr_expire_timer, jiffies + expires);
  
  out:
        spin_unlock(&mfc_unres_lock);
  
  /* Fill oifs list. It is called under write locked mrt_lock. */
  
 -static void ipmr_update_thresholds(struct mfc_cache *cache, unsigned char *ttls)
 +static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache,
 +                                 unsigned char *ttls)
  {
        int vifi;
 -      struct net *net = mfc_net(cache);
  
        cache->mfc_un.res.minvif = MAXVIFS;
        cache->mfc_un.res.maxvif = 0;
        memset(cache->mfc_un.res.ttls, 255, MAXVIFS);
  
 -      for (vifi = 0; vifi < net->ipv4.maxvif; vifi++) {
 -              if (VIF_EXISTS(net, vifi) &&
 +      for (vifi = 0; vifi < mrt->maxvif; vifi++) {
 +              if (VIF_EXISTS(mrt, vifi) &&
                    ttls[vifi] && ttls[vifi] < 255) {
                        cache->mfc_un.res.ttls[vifi] = ttls[vifi];
                        if (cache->mfc_un.res.minvif > vifi)
        }
  }
  
 -static int vif_add(struct net *net, struct vifctl *vifc, int mrtsock)
 +static int vif_add(struct net *net, struct mr_table *mrt,
 +                 struct vifctl *vifc, int mrtsock)
  {
        int vifi = vifc->vifc_vifi;
 -      struct vif_device *v = &net->ipv4.vif_table[vifi];
 +      struct vif_device *v = &mrt->vif_table[vifi];
        struct net_device *dev;
        struct in_device *in_dev;
        int err;
  
        /* Is vif busy ? */
 -      if (VIF_EXISTS(net, vifi))
 +      if (VIF_EXISTS(mrt, vifi))
                return -EADDRINUSE;
  
        switch (vifc->vifc_flags) {
                 * Special Purpose VIF in PIM
                 * All the packets will be sent to the daemon
                 */
 -              if (net->ipv4.mroute_reg_vif_num >= 0)
 +              if (mrt->mroute_reg_vif_num >= 0)
                        return -EADDRINUSE;
 -              dev = ipmr_reg_vif(net);
 +              dev = ipmr_reg_vif(net, mrt);
                if (!dev)
                        return -ENOBUFS;
                err = dev_set_allmulti(dev, 1);
        v->dev = dev;
  #ifdef CONFIG_IP_PIMSM
        if (v->flags&VIFF_REGISTER)
 -              net->ipv4.mroute_reg_vif_num = vifi;
 +              mrt->mroute_reg_vif_num = vifi;
  #endif
 -      if (vifi+1 > net->ipv4.maxvif)
 -              net->ipv4.maxvif = vifi+1;
 +      if (vifi+1 > mrt->maxvif)
 +              mrt->maxvif = vifi+1;
        write_unlock_bh(&mrt_lock);
        return 0;
  }
  
 -static struct mfc_cache *ipmr_cache_find(struct net *net,
 +static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt,
                                         __be32 origin,
                                         __be32 mcastgrp)
  {
        int line = MFC_HASH(mcastgrp, origin);
        struct mfc_cache *c;
  
 -      for (c = net->ipv4.mfc_cache_array[line]; c; c = c->next) {
 -              if (c->mfc_origin==origin && c->mfc_mcastgrp==mcastgrp)
 -                      break;
 +      list_for_each_entry(c, &mrt->mfc_cache_array[line], list) {
 +              if (c->mfc_origin == origin && c->mfc_mcastgrp == mcastgrp)
 +                      return c;
        }
 -      return c;
 +      return NULL;
  }
  
  /*
   *    Allocate a multicast cache entry
   */
 -static struct mfc_cache *ipmr_cache_alloc(struct net *net)
 +static struct mfc_cache *ipmr_cache_alloc(void)
  {
        struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
        if (c == NULL)
                return NULL;
        c->mfc_un.res.minvif = MAXVIFS;
        return c;
  }
  
 -static struct mfc_cache *ipmr_cache_alloc_unres(struct net *net)
 +static struct mfc_cache *ipmr_cache_alloc_unres(void)
  {
        struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
        if (c == NULL)
                return NULL;
        skb_queue_head_init(&c->mfc_un.unres.unresolved);
        c->mfc_un.unres.expires = jiffies + 10*HZ;
 -      mfc_net_set(c, net);
        return c;
  }
  
   *    A cache entry has gone into a resolved state from queued
   */
  
 -static void ipmr_cache_resolve(struct mfc_cache *uc, struct mfc_cache *c)
 +static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
 +                             struct mfc_cache *uc, struct mfc_cache *c)
  {
        struct sk_buff *skb;
        struct nlmsgerr *e;
                if (ip_hdr(skb)->version == 0) {
                        struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
  
 -                      if (ipmr_fill_mroute(skb, c, NLMSG_DATA(nlh)) > 0) {
 +                      if (__ipmr_fill_mroute(mrt, skb, c, NLMSG_DATA(nlh)) > 0) {
                                nlh->nlmsg_len = (skb_tail_pointer(skb) -
                                                  (u8 *)nlh);
                        } else {
                                memset(&e->msg, 0, sizeof(e->msg));
                        }
  
 -                      rtnl_unicast(skb, mfc_net(c), NETLINK_CB(skb).pid);
 +                      rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
                } else
 -                      ip_mr_forward(skb, c, 0);
 +                      ip_mr_forward(net, mrt, skb, c, 0);
        }
  }
  
   *    Called under mrt_lock.
   */
  
 -static int ipmr_cache_report(struct net *net,
 +static int ipmr_cache_report(struct mr_table *mrt,
                             struct sk_buff *pkt, vifi_t vifi, int assert)
  {
        struct sk_buff *skb;
                memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr));
                msg->im_msgtype = IGMPMSG_WHOLEPKT;
                msg->im_mbz = 0;
 -              msg->im_vif = net->ipv4.mroute_reg_vif_num;
 +              msg->im_vif = mrt->mroute_reg_vif_num;
                ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
                ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
                                             sizeof(struct iphdr));
        skb->transport_header = skb->network_header;
        }
  
 -      if (net->ipv4.mroute_sk == NULL) {
 +      if (mrt->mroute_sk == NULL) {
                kfree_skb(skb);
                return -EINVAL;
        }
        /*
         *      Deliver to mrouted
         */
 -      ret = sock_queue_rcv_skb(net->ipv4.mroute_sk, skb);
 +      ret = sock_queue_rcv_skb(mrt->mroute_sk, skb);
        if (ret < 0) {
                if (net_ratelimit())
                        printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n");
   */
  
  static int
 -ipmr_cache_unresolved(struct net *net, vifi_t vifi, struct sk_buff *skb)
 +ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
  {
 +      bool found = false;
        int err;
        struct mfc_cache *c;
        const struct iphdr *iph = ip_hdr(skb);
  
        spin_lock_bh(&mfc_unres_lock);
 -      for (c=mfc_unres_queue; c; c=c->next) {
 -              if (net_eq(mfc_net(c), net) &&
 -                  c->mfc_mcastgrp == iph->daddr &&
 -                  c->mfc_origin == iph->saddr)
 +      list_for_each_entry(c, &mrt->mfc_unres_queue, list) {
 +              if (c->mfc_mcastgrp == iph->daddr &&
 +                  c->mfc_origin == iph->saddr) {
 +                      found = true;
                        break;
 +              }
        }
  
 -      if (c == NULL) {
 +      if (!found) {
                /*
                 *      Create a new entry if allowable
                 */
  
 -              if (atomic_read(&net->ipv4.cache_resolve_queue_len) >= 10 ||
 -                  (c = ipmr_cache_alloc_unres(net)) == NULL) {
 +              if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 ||
 +                  (c = ipmr_cache_alloc_unres()) == NULL) {
                        spin_unlock_bh(&mfc_unres_lock);
  
                        kfree_skb(skb);
                /*
                 *      Reflect first query at mrouted.
                 */
 -              err = ipmr_cache_report(net, skb, vifi, IGMPMSG_NOCACHE);
 +              err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE);
                if (err < 0) {
                        /* If the report failed throw the cache entry
                           out - Brad Parker
                        return err;
                }
  
 -              atomic_inc(&net->ipv4.cache_resolve_queue_len);
 -              c->next = mfc_unres_queue;
 -              mfc_unres_queue = c;
 +              atomic_inc(&mrt->cache_resolve_queue_len);
 +              list_add(&c->list, &mrt->mfc_unres_queue);
  
-               mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires);
 -              if (atomic_read(&net->ipv4.cache_resolve_queue_len) == 1)
 -                      mod_timer(&ipmr_expire_timer, c->mfc_un.unres.expires);
++              if (atomic_read(&mrt->cache_resolve_queue_len) == 1)
++                      mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires);
        }
  
        /*
   *    MFC cache manipulation by user space mroute daemon
   */
  
 -static int ipmr_mfc_delete(struct net *net, struct mfcctl *mfc)
 +static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc)
  {
        int line;
 -      struct mfc_cache *c, **cp;
 +      struct mfc_cache *c, *next;
  
        line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
  
 -      for (cp = &net->ipv4.mfc_cache_array[line];
 -           (c = *cp) != NULL; cp = &c->next) {
 +      list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) {
                if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
                    c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
                        write_lock_bh(&mrt_lock);
 -                      *cp = c->next;
 +                      list_del(&c->list);
                        write_unlock_bh(&mrt_lock);
  
                        ipmr_cache_free(c);
        return -ENOENT;
  }
  
 -static int ipmr_mfc_add(struct net *net, struct mfcctl *mfc, int mrtsock)
 +static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
 +                      struct mfcctl *mfc, int mrtsock)
  {
 +      bool found = false;
        int line;
 -      struct mfc_cache *uc, *c, **cp;
 +      struct mfc_cache *uc, *c;
  
        if (mfc->mfcc_parent >= MAXVIFS)
                return -ENFILE;
  
        line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
  
 -      for (cp = &net->ipv4.mfc_cache_array[line];
 -           (c = *cp) != NULL; cp = &c->next) {
 +      list_for_each_entry(c, &mrt->mfc_cache_array[line], list) {
                if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
 -                  c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr)
 +                  c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
 +                      found = true;
                        break;
 +              }
        }
  
 -      if (c != NULL) {
 +      if (found) {
                write_lock_bh(&mrt_lock);
                c->mfc_parent = mfc->mfcc_parent;
 -              ipmr_update_thresholds(c, mfc->mfcc_ttls);
 +              ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
                if (!mrtsock)
                        c->mfc_flags |= MFC_STATIC;
                write_unlock_bh(&mrt_lock);
        if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
                return -EINVAL;
  
 -      c = ipmr_cache_alloc(net);
 +      c = ipmr_cache_alloc();
        if (c == NULL)
                return -ENOMEM;
  
        c->mfc_origin = mfc->mfcc_origin.s_addr;
        c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr;
        c->mfc_parent = mfc->mfcc_parent;
 -      ipmr_update_thresholds(c, mfc->mfcc_ttls);
 +      ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls);
        if (!mrtsock)
                c->mfc_flags |= MFC_STATIC;
  
        write_lock_bh(&mrt_lock);
 -      c->next = net->ipv4.mfc_cache_array[line];
 -      net->ipv4.mfc_cache_array[line] = c;
 +      list_add(&c->list, &mrt->mfc_cache_array[line]);
        write_unlock_bh(&mrt_lock);
  
        /*
         *      Check to see if we resolved a queued list. If so we
         *      need to send on the frames and tidy up.
         */
 +      found = false;
        spin_lock_bh(&mfc_unres_lock);
 -      for (cp = &mfc_unres_queue; (uc=*cp) != NULL;
 -           cp = &uc->next) {
 -              if (net_eq(mfc_net(uc), net) &&
 -                  uc->mfc_origin == c->mfc_origin &&
 +      list_for_each_entry(uc, &mrt->mfc_unres_queue, list) {
 +              if (uc->mfc_origin == c->mfc_origin &&
                    uc->mfc_mcastgrp == c->mfc_mcastgrp) {
 -                      *cp = uc->next;
 -                      atomic_dec(&net->ipv4.cache_resolve_queue_len);
 +                      list_del(&uc->list);
 +                      atomic_dec(&mrt->cache_resolve_queue_len);
 +                      found = true;
                        break;
                }
        }
 -      if (mfc_unres_queue == NULL)
 -              del_timer(&ipmr_expire_timer);
 +      if (list_empty(&mrt->mfc_unres_queue))
 +              del_timer(&mrt->ipmr_expire_timer);
        spin_unlock_bh(&mfc_unres_lock);
  
 -      if (uc) {
 -              ipmr_cache_resolve(uc, c);
 +      if (found) {
 +              ipmr_cache_resolve(net, mrt, uc, c);
                ipmr_cache_free(uc);
        }
        return 0;
   *    Close the multicast socket, and clear the vif tables etc
   */
  
 -static void mroute_clean_tables(struct net *net)
 +static void mroute_clean_tables(struct mr_table *mrt)
  {
        int i;
        LIST_HEAD(list);
 +      struct mfc_cache *c, *next;
  
        /*
         *      Shut down all active vif entries
         */
 -      for (i = 0; i < net->ipv4.maxvif; i++) {
 -              if (!(net->ipv4.vif_table[i].flags&VIFF_STATIC))
 -                      vif_delete(net, i, 0, &list);
 +      for (i = 0; i < mrt->maxvif; i++) {
 +              if (!(mrt->vif_table[i].flags&VIFF_STATIC))
 +                      vif_delete(mrt, i, 0, &list);
        }
        unregister_netdevice_many(&list);
  
        /*
         *      Wipe the cache
         */
 -      for (i=0; i<MFC_LINES; i++) {
 -              struct mfc_cache *c, **cp;
 -
 -              cp = &net->ipv4.mfc_cache_array[i];
 -              while ((c = *cp) != NULL) {
 -                      if (c->mfc_flags&MFC_STATIC) {
 -                              cp = &c->next;
 +      for (i = 0; i < MFC_LINES; i++) {
 +              list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[i], list) {
 +                      if (c->mfc_flags&MFC_STATIC)
                                continue;
 -                      }
                        write_lock_bh(&mrt_lock);
 -                      *cp = c->next;
 +                      list_del(&c->list);
                        write_unlock_bh(&mrt_lock);
  
                        ipmr_cache_free(c);
                }
        }
  
 -      if (atomic_read(&net->ipv4.cache_resolve_queue_len) != 0) {
 -              struct mfc_cache *c, **cp;
 -
 +      if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
                spin_lock_bh(&mfc_unres_lock);
 -              cp = &mfc_unres_queue;
 -              while ((c = *cp) != NULL) {
 -                      if (!net_eq(mfc_net(c), net)) {
 -                              cp = &c->next;
 -                              continue;
 -                      }
 -                      *cp = c->next;
 -
 -                      ipmr_destroy_unres(c);
 +              list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
 +                      list_del(&c->list);
 +                      ipmr_destroy_unres(mrt, c);
                }
                spin_unlock_bh(&mfc_unres_lock);
        }
  static void mrtsock_destruct(struct sock *sk)
  {
        struct net *net = sock_net(sk);
 +      struct mr_table *mrt;
  
        rtnl_lock();
 -      if (sk == net->ipv4.mroute_sk) {
 -              IPV4_DEVCONF_ALL(net, MC_FORWARDING)--;
 +      ipmr_for_each_table(mrt, net) {
 +              if (sk == mrt->mroute_sk) {
 +                      IPV4_DEVCONF_ALL(net, MC_FORWARDING)--;
  
 -              write_lock_bh(&mrt_lock);
 -              net->ipv4.mroute_sk = NULL;
 -              write_unlock_bh(&mrt_lock);
 +                      write_lock_bh(&mrt_lock);
 +                      mrt->mroute_sk = NULL;
 +                      write_unlock_bh(&mrt_lock);
  
 -              mroute_clean_tables(net);
 +                      mroute_clean_tables(mrt);
 +              }
        }
        rtnl_unlock();
  }
@@@ -1192,14 -957,9 +1193,14 @@@ int ip_mroute_setsockopt(struct sock *s
        struct vifctl vif;
        struct mfcctl mfc;
        struct net *net = sock_net(sk);
 +      struct mr_table *mrt;
 +
 +      mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
 +      if (mrt == NULL)
 +              return -ENOENT;
  
        if (optname != MRT_INIT) {
 -              if (sk != net->ipv4.mroute_sk && !capable(CAP_NET_ADMIN))
 +              if (sk != mrt->mroute_sk && !capable(CAP_NET_ADMIN))
                        return -EACCES;
        }
  
                        return -ENOPROTOOPT;
  
                rtnl_lock();
 -              if (net->ipv4.mroute_sk) {
 +              if (mrt->mroute_sk) {
                        rtnl_unlock();
                        return -EADDRINUSE;
                }
                ret = ip_ra_control(sk, 1, mrtsock_destruct);
                if (ret == 0) {
                        write_lock_bh(&mrt_lock);
 -                      net->ipv4.mroute_sk = sk;
 +                      mrt->mroute_sk = sk;
                        write_unlock_bh(&mrt_lock);
  
                        IPV4_DEVCONF_ALL(net, MC_FORWARDING)++;
                rtnl_unlock();
                return ret;
        case MRT_DONE:
 -              if (sk != net->ipv4.mroute_sk)
 +              if (sk != mrt->mroute_sk)
                        return -EACCES;
                return ip_ra_control(sk, 0, NULL);
        case MRT_ADD_VIF:
                        return -ENFILE;
                rtnl_lock();
                if (optname == MRT_ADD_VIF) {
 -                      ret = vif_add(net, &vif, sk == net->ipv4.mroute_sk);
 +                      ret = vif_add(net, mrt, &vif, sk == mrt->mroute_sk);
                } else {
 -                      ret = vif_delete(net, vif.vifc_vifi, 0, NULL);
 +                      ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL);
                }
                rtnl_unlock();
                return ret;
                        return -EFAULT;
                rtnl_lock();
                if (optname == MRT_DEL_MFC)
 -                      ret = ipmr_mfc_delete(net, &mfc);
 +                      ret = ipmr_mfc_delete(mrt, &mfc);
                else
 -                      ret = ipmr_mfc_add(net, &mfc, sk == net->ipv4.mroute_sk);
 +                      ret = ipmr_mfc_add(net, mrt, &mfc, sk == mrt->mroute_sk);
                rtnl_unlock();
                return ret;
                /*
                int v;
                if (get_user(v,(int __user *)optval))
                        return -EFAULT;
 -              net->ipv4.mroute_do_assert = (v) ? 1 : 0;
 +              mrt->mroute_do_assert = (v) ? 1 : 0;
                return 0;
        }
  #ifdef CONFIG_IP_PIMSM
  
                rtnl_lock();
                ret = 0;
 -              if (v != net->ipv4.mroute_do_pim) {
 -                      net->ipv4.mroute_do_pim = v;
 -                      net->ipv4.mroute_do_assert = v;
 +              if (v != mrt->mroute_do_pim) {
 +                      mrt->mroute_do_pim = v;
 +                      mrt->mroute_do_assert = v;
                }
                rtnl_unlock();
                return ret;
        }
  #endif
 +#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
 +      case MRT_TABLE:
 +      {
 +              u32 v;
 +
 +              if (optlen != sizeof(u32))
 +                      return -EINVAL;
 +              if (get_user(v, (u32 __user *)optval))
 +                      return -EFAULT;
 +              if (sk == mrt->mroute_sk)
 +                      return -EBUSY;
 +
 +              rtnl_lock();
 +              ret = 0;
 +              if (!ipmr_new_table(net, v))
 +                      ret = -ENOMEM;
 +              raw_sk(sk)->ipmr_table = v;
 +              rtnl_unlock();
 +              return ret;
 +      }
 +#endif
        /*
         *      Spurious command, or MRT_VERSION which you cannot
         *      set.
@@@ -1334,11 -1073,6 +1335,11 @@@ int ip_mroute_getsockopt(struct sock *s
        int olr;
        int val;
        struct net *net = sock_net(sk);
 +      struct mr_table *mrt;
 +
 +      mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
 +      if (mrt == NULL)
 +              return -ENOENT;
  
        if (optname != MRT_VERSION &&
  #ifdef CONFIG_IP_PIMSM
                val = 0x0305;
  #ifdef CONFIG_IP_PIMSM
        else if (optname == MRT_PIM)
 -              val = net->ipv4.mroute_do_pim;
 +              val = mrt->mroute_do_pim;
  #endif
        else
 -              val = net->ipv4.mroute_do_assert;
 +              val = mrt->mroute_do_assert;
        if (copy_to_user(optval, &val, olr))
                return -EFAULT;
        return 0;
@@@ -1380,21 -1114,16 +1381,21 @@@ int ipmr_ioctl(struct sock *sk, int cmd
        struct vif_device *vif;
        struct mfc_cache *c;
        struct net *net = sock_net(sk);
 +      struct mr_table *mrt;
 +
 +      mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
 +      if (mrt == NULL)
 +              return -ENOENT;
  
        switch (cmd) {
        case SIOCGETVIFCNT:
                if (copy_from_user(&vr, arg, sizeof(vr)))
                        return -EFAULT;
 -              if (vr.vifi >= net->ipv4.maxvif)
 +              if (vr.vifi >= mrt->maxvif)
                        return -EINVAL;
                read_lock(&mrt_lock);
 -              vif = &net->ipv4.vif_table[vr.vifi];
 -              if (VIF_EXISTS(net, vr.vifi)) {
 +              vif = &mrt->vif_table[vr.vifi];
 +              if (VIF_EXISTS(mrt, vr.vifi)) {
                        vr.icount = vif->pkt_in;
                        vr.ocount = vif->pkt_out;
                        vr.ibytes = vif->bytes_in;
                        return -EFAULT;
  
                read_lock(&mrt_lock);
 -              c = ipmr_cache_find(net, sr.src.s_addr, sr.grp.s_addr);
 +              c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
                if (c) {
                        sr.pktcnt = c->mfc_un.res.pkt;
                        sr.bytecnt = c->mfc_un.res.bytes;
@@@ -1435,20 -1164,16 +1436,20 @@@ static int ipmr_device_event(struct not
  {
        struct net_device *dev = ptr;
        struct net *net = dev_net(dev);
 +      struct mr_table *mrt;
        struct vif_device *v;
        int ct;
        LIST_HEAD(list);
  
        if (event != NETDEV_UNREGISTER)
                return NOTIFY_DONE;
 -      v = &net->ipv4.vif_table[0];
 -      for (ct = 0; ct < net->ipv4.maxvif; ct++, v++) {
 -              if (v->dev == dev)
 -                      vif_delete(net, ct, 1, &list);
 +
 +      ipmr_for_each_table(mrt, net) {
 +              v = &mrt->vif_table[0];
 +              for (ct = 0; ct < mrt->maxvif; ct++, v++) {
 +                      if (v->dev == dev)
 +                              vif_delete(mrt, ct, 1, &list);
 +              }
        }
        unregister_netdevice_many(&list);
        return NOTIFY_DONE;
@@@ -1507,11 -1232,11 +1508,11 @@@ static inline int ipmr_forward_finish(s
   *    Processing handlers for ipmr_forward
   */
  
 -static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi)
 +static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
 +                          struct sk_buff *skb, struct mfc_cache *c, int vifi)
  {
 -      struct net *net = mfc_net(c);
        const struct iphdr *iph = ip_hdr(skb);
 -      struct vif_device *vif = &net->ipv4.vif_table[vifi];
 +      struct vif_device *vif = &mrt->vif_table[vifi];
        struct net_device *dev;
        struct rtable *rt;
        int    encap = 0;
                vif->bytes_out += skb->len;
                vif->dev->stats.tx_bytes += skb->len;
                vif->dev->stats.tx_packets++;
 -              ipmr_cache_report(net, skb, vifi, IGMPMSG_WHOLEPKT);
 +              ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT);
                goto out_free;
        }
  #endif
         * not mrouter) cannot join to more than one interface - it will
         * result in receiving multiple packets.
         */
 -      NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, dev,
 +      NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, dev,
                ipmr_forward_finish);
        return;
  
@@@ -1608,12 -1333,12 +1609,12 @@@ out_free
        return;
  }
  
 -static int ipmr_find_vif(struct net_device *dev)
 +static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev)
  {
 -      struct net *net = dev_net(dev);
        int ct;
 -      for (ct = net->ipv4.maxvif-1; ct >= 0; ct--) {
 -              if (net->ipv4.vif_table[ct].dev == dev)
 +
 +      for (ct = mrt->maxvif-1; ct >= 0; ct--) {
 +              if (mrt->vif_table[ct].dev == dev)
                        break;
        }
        return ct;
  
  /* "local" means that we should preserve one skb (for local delivery) */
  
 -static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local)
 +static int ip_mr_forward(struct net *net, struct mr_table *mrt,
 +                       struct sk_buff *skb, struct mfc_cache *cache,
 +                       int local)
  {
        int psend = -1;
        int vif, ct;
 -      struct net *net = mfc_net(cache);
  
        vif = cache->mfc_parent;
        cache->mfc_un.res.pkt++;
        /*
         * Wrong interface: drop packet and (maybe) send PIM assert.
         */
 -      if (net->ipv4.vif_table[vif].dev != skb->dev) {
 +      if (mrt->vif_table[vif].dev != skb->dev) {
                int true_vifi;
  
                if (skb_rtable(skb)->fl.iif == 0) {
                }
  
                cache->mfc_un.res.wrong_if++;
 -              true_vifi = ipmr_find_vif(skb->dev);
 +              true_vifi = ipmr_find_vif(mrt, skb->dev);
  
 -              if (true_vifi >= 0 && net->ipv4.mroute_do_assert &&
 +              if (true_vifi >= 0 && mrt->mroute_do_assert &&
                    /* pimsm uses asserts, when switching from RPT to SPT,
                       so that we cannot check that packet arrived on an oif.
                       It is bad, but otherwise we would need to move pretty
                       large chunk of pimd to kernel. Ough... --ANK
                     */
 -                  (net->ipv4.mroute_do_pim ||
 +                  (mrt->mroute_do_pim ||
                     cache->mfc_un.res.ttls[true_vifi] < 255) &&
                    time_after(jiffies,
                               cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
                        cache->mfc_un.res.last_assert = jiffies;
 -                      ipmr_cache_report(net, skb, true_vifi, IGMPMSG_WRONGVIF);
 +                      ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF);
                }
                goto dont_forward;
        }
  
 -      net->ipv4.vif_table[vif].pkt_in++;
 -      net->ipv4.vif_table[vif].bytes_in += skb->len;
 +      mrt->vif_table[vif].pkt_in++;
 +      mrt->vif_table[vif].bytes_in += skb->len;
  
        /*
         *      Forward the frame
                        if (psend != -1) {
                                struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
                                if (skb2)
 -                                      ipmr_queue_xmit(skb2, cache, psend);
 +                                      ipmr_queue_xmit(net, mrt, skb2, cache,
 +                                                      psend);
                        }
                        psend = ct;
                }
                if (local) {
                        struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
                        if (skb2)
 -                              ipmr_queue_xmit(skb2, cache, psend);
 +                              ipmr_queue_xmit(net, mrt, skb2, cache, psend);
                } else {
 -                      ipmr_queue_xmit(skb, cache, psend);
 +                      ipmr_queue_xmit(net, mrt, skb, cache, psend);
                        return 0;
                }
        }
@@@ -1716,8 -1439,6 +1717,8 @@@ int ip_mr_input(struct sk_buff *skb
        struct mfc_cache *cache;
        struct net *net = dev_net(skb->dev);
        int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL;
 +      struct mr_table *mrt;
 +      int err;
  
        /* Packet is looped back after forward, it should not be
           forwarded second time, but still can be delivered locally.
        if (IPCB(skb)->flags&IPSKB_FORWARDED)
                goto dont_forward;
  
 +      err = ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt);
 +      if (err < 0)
 +              return err;
 +
        if (!local) {
                    if (IPCB(skb)->opt.router_alert) {
                            if (ip_call_ra_chain(skb))
                               that we can forward NO IGMP messages.
                             */
                            read_lock(&mrt_lock);
 -                          if (net->ipv4.mroute_sk) {
 +                          if (mrt->mroute_sk) {
                                    nf_reset(skb);
 -                                  raw_rcv(net->ipv4.mroute_sk, skb);
 +                                  raw_rcv(mrt->mroute_sk, skb);
                                    read_unlock(&mrt_lock);
                                    return 0;
                            }
        }
  
        read_lock(&mrt_lock);
 -      cache = ipmr_cache_find(net, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
 +      cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
  
        /*
         *      No usable cache entry
                        skb = skb2;
                }
  
 -              vif = ipmr_find_vif(skb->dev);
 +              vif = ipmr_find_vif(mrt, skb->dev);
                if (vif >= 0) {
 -                      int err = ipmr_cache_unresolved(net, vif, skb);
 +                      int err2 = ipmr_cache_unresolved(mrt, vif, skb);
                        read_unlock(&mrt_lock);
  
 -                      return err;
 +                      return err2;
                }
                read_unlock(&mrt_lock);
                kfree_skb(skb);
                return -ENODEV;
        }
  
 -      ip_mr_forward(skb, cache, local);
 +      ip_mr_forward(net, mrt, skb, cache, local);
  
        read_unlock(&mrt_lock);
  
@@@ -1799,11 -1516,11 +1800,11 @@@ dont_forward
  }
  
  #ifdef CONFIG_IP_PIMSM
 -static int __pim_rcv(struct sk_buff *skb, unsigned int pimlen)
 +static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb,
 +                   unsigned int pimlen)
  {
        struct net_device *reg_dev = NULL;
        struct iphdr *encap;
 -      struct net *net = dev_net(skb->dev);
  
        encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
        /*
                return 1;
  
        read_lock(&mrt_lock);
 -      if (net->ipv4.mroute_reg_vif_num >= 0)
 -              reg_dev = net->ipv4.vif_table[net->ipv4.mroute_reg_vif_num].dev;
 +      if (mrt->mroute_reg_vif_num >= 0)
 +              reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev;
        if (reg_dev)
                dev_hold(reg_dev);
        read_unlock(&mrt_lock);
@@@ -1854,21 -1571,17 +1855,21 @@@ int pim_rcv_v1(struct sk_buff * skb
  {
        struct igmphdr *pim;
        struct net *net = dev_net(skb->dev);
 +      struct mr_table *mrt;
  
        if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
                goto drop;
  
        pim = igmp_hdr(skb);
  
 -      if (!net->ipv4.mroute_do_pim ||
 +      if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0)
 +              goto drop;
 +
 +      if (!mrt->mroute_do_pim ||
            pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
                goto drop;
  
 -      if (__pim_rcv(skb, sizeof(*pim))) {
 +      if (__pim_rcv(mrt, skb, sizeof(*pim))) {
  drop:
                kfree_skb(skb);
        }
  static int pim_rcv(struct sk_buff * skb)
  {
        struct pimreghdr *pim;
 +      struct net *net = dev_net(skb->dev);
 +      struct mr_table *mrt;
  
        if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
                goto drop;
             csum_fold(skb_checksum(skb, 0, skb->len, 0))))
                goto drop;
  
 -      if (__pim_rcv(skb, sizeof(*pim))) {
 +      if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0)
 +              goto drop;
 +
 +      if (__pim_rcv(mrt, skb, sizeof(*pim))) {
  drop:
                kfree_skb(skb);
        }
  }
  #endif
  
 -static int
 -ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm)
 +static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
 +                            struct mfc_cache *c, struct rtmsg *rtm)
  {
        int ct;
        struct rtnexthop *nhp;
 -      struct net *net = mfc_net(c);
        u8 *b = skb_tail_pointer(skb);
        struct rtattr *mp_head;
  
        if (c->mfc_parent > MAXVIFS)
                return -ENOENT;
  
 -      if (VIF_EXISTS(net, c->mfc_parent))
 -              RTA_PUT(skb, RTA_IIF, 4, &net->ipv4.vif_table[c->mfc_parent].dev->ifindex);
 +      if (VIF_EXISTS(mrt, c->mfc_parent))
 +              RTA_PUT(skb, RTA_IIF, 4, &mrt->vif_table[c->mfc_parent].dev->ifindex);
  
        mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0));
  
        for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
 -              if (VIF_EXISTS(net, ct) && c->mfc_un.res.ttls[ct] < 255) {
 +              if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) {
                        if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
                                goto rtattr_failure;
                        nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
                        nhp->rtnh_flags = 0;
                        nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
 -                      nhp->rtnh_ifindex = net->ipv4.vif_table[ct].dev->ifindex;
 +                      nhp->rtnh_ifindex = mrt->vif_table[ct].dev->ifindex;
                        nhp->rtnh_len = sizeof(*nhp);
                }
        }
@@@ -1946,16 -1655,11 +1947,16 @@@ int ipmr_get_route(struct net *net
                   struct sk_buff *skb, struct rtmsg *rtm, int nowait)
  {
        int err;
 +      struct mr_table *mrt;
        struct mfc_cache *cache;
        struct rtable *rt = skb_rtable(skb);
  
 +      mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
 +      if (mrt == NULL)
 +              return -ENOENT;
 +
        read_lock(&mrt_lock);
 -      cache = ipmr_cache_find(net, rt->rt_src, rt->rt_dst);
 +      cache = ipmr_cache_find(mrt, rt->rt_src, rt->rt_dst);
  
        if (cache == NULL) {
                struct sk_buff *skb2;
                }
  
                dev = skb->dev;
 -              if (dev == NULL || (vif = ipmr_find_vif(dev)) < 0) {
 +              if (dev == NULL || (vif = ipmr_find_vif(mrt, dev)) < 0) {
                        read_unlock(&mrt_lock);
                        return -ENODEV;
                }
                iph->saddr = rt->rt_src;
                iph->daddr = rt->rt_dst;
                iph->version = 0;
 -              err = ipmr_cache_unresolved(net, vif, skb2);
 +              err = ipmr_cache_unresolved(mrt, vif, skb2);
                read_unlock(&mrt_lock);
                return err;
        }
  
        if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY))
                cache->mfc_flags |= MFC_NOTIFY;
 -      err = ipmr_fill_mroute(skb, cache, rtm);
 +      err = __ipmr_fill_mroute(mrt, skb, cache, rtm);
        read_unlock(&mrt_lock);
        return err;
  }
  
 +static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
 +                          u32 pid, u32 seq, struct mfc_cache *c)
 +{
 +      struct nlmsghdr *nlh;
 +      struct rtmsg *rtm;
 +
 +      nlh = nlmsg_put(skb, pid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI);
 +      if (nlh == NULL)
 +              return -EMSGSIZE;
 +
 +      rtm = nlmsg_data(nlh);
 +      rtm->rtm_family   = RTNL_FAMILY_IPMR;
 +      rtm->rtm_dst_len  = 32;
 +      rtm->rtm_src_len  = 32;
 +      rtm->rtm_tos      = 0;
 +      rtm->rtm_table    = mrt->id;
 +      NLA_PUT_U32(skb, RTA_TABLE, mrt->id);
 +      rtm->rtm_type     = RTN_MULTICAST;
 +      rtm->rtm_scope    = RT_SCOPE_UNIVERSE;
 +      rtm->rtm_protocol = RTPROT_UNSPEC;
 +      rtm->rtm_flags    = 0;
 +
 +      NLA_PUT_BE32(skb, RTA_SRC, c->mfc_origin);
 +      NLA_PUT_BE32(skb, RTA_DST, c->mfc_mcastgrp);
 +
 +      if (__ipmr_fill_mroute(mrt, skb, c, rtm) < 0)
 +              goto nla_put_failure;
 +
 +      return nlmsg_end(skb, nlh);
 +
 +nla_put_failure:
 +      nlmsg_cancel(skb, nlh);
 +      return -EMSGSIZE;
 +}
 +
 +static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
 +{
 +      struct net *net = sock_net(skb->sk);
 +      struct mr_table *mrt;
 +      struct mfc_cache *mfc;
 +      unsigned int t = 0, s_t;
 +      unsigned int h = 0, s_h;
 +      unsigned int e = 0, s_e;
 +
 +      s_t = cb->args[0];
 +      s_h = cb->args[1];
 +      s_e = cb->args[2];
 +
 +      read_lock(&mrt_lock);
 +      ipmr_for_each_table(mrt, net) {
 +              if (t < s_t)
 +                      goto next_table;
 +              if (t > s_t)
 +                      s_h = 0;
 +              for (h = s_h; h < MFC_LINES; h++) {
 +                      list_for_each_entry(mfc, &mrt->mfc_cache_array[h], list) {
 +                              if (e < s_e)
 +                                      goto next_entry;
 +                              if (ipmr_fill_mroute(mrt, skb,
 +                                                   NETLINK_CB(cb->skb).pid,
 +                                                   cb->nlh->nlmsg_seq,
 +                                                   mfc) < 0)
 +                                      goto done;
 +next_entry:
 +                              e++;
 +                      }
 +                      e = s_e = 0;
 +              }
 +              s_h = 0;
 +next_table:
 +              t++;
 +      }
 +done:
 +      read_unlock(&mrt_lock);
 +
 +      cb->args[2] = e;
 +      cb->args[1] = h;
 +      cb->args[0] = t;
 +
 +      return skb->len;
 +}
 +
  #ifdef CONFIG_PROC_FS
  /*
   *    The /proc interfaces to multicast routing /proc/ip_mr_cache /proc/ip_mr_vif
   */
  struct ipmr_vif_iter {
        struct seq_net_private p;
 +      struct mr_table *mrt;
        int ct;
  };
  
@@@ -2094,13 -1715,11 +2095,13 @@@ static struct vif_device *ipmr_vif_seq_
                                           struct ipmr_vif_iter *iter,
                                           loff_t pos)
  {
 -      for (iter->ct = 0; iter->ct < net->ipv4.maxvif; ++iter->ct) {
 -              if (!VIF_EXISTS(net, iter->ct))
 +      struct mr_table *mrt = iter->mrt;
 +
 +      for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) {
 +              if (!VIF_EXISTS(mrt, iter->ct))
                        continue;
                if (pos-- == 0)
 -                      return &net->ipv4.vif_table[iter->ct];
 +                      return &mrt->vif_table[iter->ct];
        }
        return NULL;
  }
  static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos)
        __acquires(mrt_lock)
  {
 +      struct ipmr_vif_iter *iter = seq->private;
        struct net *net = seq_file_net(seq);
 +      struct mr_table *mrt;
 +
 +      mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
 +      if (mrt == NULL)
 +              return ERR_PTR(-ENOENT);
 +
 +      iter->mrt = mrt;
  
        read_lock(&mrt_lock);
        return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1)
@@@ -2127,16 -1738,15 +2128,16 @@@ static void *ipmr_vif_seq_next(struct s
  {
        struct ipmr_vif_iter *iter = seq->private;
        struct net *net = seq_file_net(seq);
 +      struct mr_table *mrt = iter->mrt;
  
        ++*pos;
        if (v == SEQ_START_TOKEN)
                return ipmr_vif_seq_idx(net, iter, 0);
  
 -      while (++iter->ct < net->ipv4.maxvif) {
 -              if (!VIF_EXISTS(net, iter->ct))
 +      while (++iter->ct < mrt->maxvif) {
 +              if (!VIF_EXISTS(mrt, iter->ct))
                        continue;
 -              return &net->ipv4.vif_table[iter->ct];
 +              return &mrt->vif_table[iter->ct];
        }
        return NULL;
  }
@@@ -2149,8 -1759,7 +2150,8 @@@ static void ipmr_vif_seq_stop(struct se
  
  static int ipmr_vif_seq_show(struct seq_file *seq, void *v)
  {
 -      struct net *net = seq_file_net(seq);
 +      struct ipmr_vif_iter *iter = seq->private;
 +      struct mr_table *mrt = iter->mrt;
  
        if (v == SEQ_START_TOKEN) {
                seq_puts(seq,
  
                seq_printf(seq,
                           "%2Zd %-10s %8ld %7ld  %8ld %7ld %05X %08X %08X\n",
 -                         vif - net->ipv4.vif_table,
 +                         vif - mrt->vif_table,
                           name, vif->bytes_in, vif->pkt_in,
                           vif->bytes_out, vif->pkt_out,
                           vif->flags, vif->local, vif->remote);
@@@ -2192,8 -1801,7 +2193,8 @@@ static const struct file_operations ipm
  
  struct ipmr_mfc_iter {
        struct seq_net_private p;
 -      struct mfc_cache **cache;
 +      struct mr_table *mrt;
 +      struct list_head *cache;
        int ct;
  };
  
  static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net,
                                          struct ipmr_mfc_iter *it, loff_t pos)
  {
 +      struct mr_table *mrt = it->mrt;
        struct mfc_cache *mfc;
  
 -      it->cache = net->ipv4.mfc_cache_array;
        read_lock(&mrt_lock);
 -      for (it->ct = 0; it->ct < MFC_LINES; it->ct++)
 -              for (mfc = net->ipv4.mfc_cache_array[it->ct];
 -                   mfc; mfc = mfc->next)
 +      for (it->ct = 0; it->ct < MFC_LINES; it->ct++) {
 +              it->cache = &mrt->mfc_cache_array[it->ct];
 +              list_for_each_entry(mfc, it->cache, list)
                        if (pos-- == 0)
                                return mfc;
 +      }
        read_unlock(&mrt_lock);
  
 -      it->cache = &mfc_unres_queue;
        spin_lock_bh(&mfc_unres_lock);
 -      for (mfc = mfc_unres_queue; mfc; mfc = mfc->next)
 -              if (net_eq(mfc_net(mfc), net) &&
 -                  pos-- == 0)
 +      it->cache = &mrt->mfc_unres_queue;
 +      list_for_each_entry(mfc, it->cache, list)
 +              if (pos-- == 0)
                        return mfc;
        spin_unlock_bh(&mfc_unres_lock);
  
@@@ -2229,13 -1837,7 +2230,13 @@@ static void *ipmr_mfc_seq_start(struct 
  {
        struct ipmr_mfc_iter *it = seq->private;
        struct net *net = seq_file_net(seq);
 +      struct mr_table *mrt;
  
 +      mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
 +      if (mrt == NULL)
 +              return ERR_PTR(-ENOENT);
 +
 +      it->mrt = mrt;
        it->cache = NULL;
        it->ct = 0;
        return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1)
@@@ -2247,36 -1849,37 +2248,36 @@@ static void *ipmr_mfc_seq_next(struct s
        struct mfc_cache *mfc = v;
        struct ipmr_mfc_iter *it = seq->private;
        struct net *net = seq_file_net(seq);
 +      struct mr_table *mrt = it->mrt;
  
        ++*pos;
  
        if (v == SEQ_START_TOKEN)
                return ipmr_mfc_seq_idx(net, seq->private, 0);
  
 -      if (mfc->next)
 -              return mfc->next;
 +      if (mfc->list.next != it->cache)
 +              return list_entry(mfc->list.next, struct mfc_cache, list);
  
 -      if (it->cache == &mfc_unres_queue)
 +      if (it->cache == &mrt->mfc_unres_queue)
                goto end_of_list;
  
 -      BUG_ON(it->cache != net->ipv4.mfc_cache_array);
 +      BUG_ON(it->cache != &mrt->mfc_cache_array[it->ct]);
  
        while (++it->ct < MFC_LINES) {
 -              mfc = net->ipv4.mfc_cache_array[it->ct];
 -              if (mfc)
 -                      return mfc;
 +              it->cache = &mrt->mfc_cache_array[it->ct];
 +              if (list_empty(it->cache))
 +                      continue;
 +              return list_first_entry(it->cache, struct mfc_cache, list);
        }
  
        /* exhausted cache_array, show unresolved */
        read_unlock(&mrt_lock);
 -      it->cache = &mfc_unres_queue;
 +      it->cache = &mrt->mfc_unres_queue;
        it->ct = 0;
  
        spin_lock_bh(&mfc_unres_lock);
 -      mfc = mfc_unres_queue;
 -      while (mfc && !net_eq(mfc_net(mfc), net))
 -              mfc = mfc->next;
 -      if (mfc)
 -              return mfc;
 +      if (!list_empty(it->cache))
 +              return list_first_entry(it->cache, struct mfc_cache, list);
  
   end_of_list:
        spin_unlock_bh(&mfc_unres_lock);
  static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v)
  {
        struct ipmr_mfc_iter *it = seq->private;
 -      struct net *net = seq_file_net(seq);
 +      struct mr_table *mrt = it->mrt;
  
 -      if (it->cache == &mfc_unres_queue)
 +      if (it->cache == &mrt->mfc_unres_queue)
                spin_unlock_bh(&mfc_unres_lock);
 -      else if (it->cache == net->ipv4.mfc_cache_array)
 +      else if (it->cache == &mrt->mfc_cache_array[it->ct])
                read_unlock(&mrt_lock);
  }
  
  static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
  {
        int n;
 -      struct net *net = seq_file_net(seq);
  
        if (v == SEQ_START_TOKEN) {
                seq_puts(seq,
        } else {
                const struct mfc_cache *mfc = v;
                const struct ipmr_mfc_iter *it = seq->private;
 +              const struct mr_table *mrt = it->mrt;
  
 -              seq_printf(seq, "%08lX %08lX %-3hd",
 -                         (unsigned long) mfc->mfc_mcastgrp,
 -                         (unsigned long) mfc->mfc_origin,
 +              seq_printf(seq, "%08X %08X %-3hd",
 +                         (__force u32) mfc->mfc_mcastgrp,
 +                         (__force u32) mfc->mfc_origin,
                           mfc->mfc_parent);
  
 -              if (it->cache != &mfc_unres_queue) {
 +              if (it->cache != &mrt->mfc_unres_queue) {
                        seq_printf(seq, " %8lu %8lu %8lu",
                                   mfc->mfc_un.res.pkt,
                                   mfc->mfc_un.res.bytes,
                                   mfc->mfc_un.res.wrong_if);
                        for (n = mfc->mfc_un.res.minvif;
                             n < mfc->mfc_un.res.maxvif; n++ ) {
 -                              if (VIF_EXISTS(net, n) &&
 +                              if (VIF_EXISTS(mrt, n) &&
                                    mfc->mfc_un.res.ttls[n] < 255)
                                        seq_printf(seq,
                                           " %2d:%-3d",
@@@ -2372,11 -1975,27 +2373,11 @@@ static const struct net_protocol pim_pr
   */
  static int __net_init ipmr_net_init(struct net *net)
  {
 -      int err = 0;
 +      int err;
  
 -      net->ipv4.vif_table = kcalloc(MAXVIFS, sizeof(struct vif_device),
 -                                    GFP_KERNEL);
 -      if (!net->ipv4.vif_table) {
 -              err = -ENOMEM;
 +      err = ipmr_rules_init(net);
 +      if (err < 0)
                goto fail;
 -      }
 -
 -      /* Forwarding cache */
 -      net->ipv4.mfc_cache_array = kcalloc(MFC_LINES,
 -                                          sizeof(struct mfc_cache *),
 -                                          GFP_KERNEL);
 -      if (!net->ipv4.mfc_cache_array) {
 -              err = -ENOMEM;
 -              goto fail_mfc_cache;
 -      }
 -
 -#ifdef CONFIG_IP_PIMSM
 -      net->ipv4.mroute_reg_vif_num = -1;
 -#endif
  
  #ifdef CONFIG_PROC_FS
        err = -ENOMEM;
  proc_cache_fail:
        proc_net_remove(net, "ip_mr_vif");
  proc_vif_fail:
 -      kfree(net->ipv4.mfc_cache_array);
 +      ipmr_rules_exit(net);
  #endif
 -fail_mfc_cache:
 -      kfree(net->ipv4.vif_table);
  fail:
        return err;
  }
@@@ -2403,7 -2024,8 +2404,7 @@@ static void __net_exit ipmr_net_exit(st
        proc_net_remove(net, "ip_mr_cache");
        proc_net_remove(net, "ip_mr_vif");
  #endif
 -      kfree(net->ipv4.mfc_cache_array);
 -      kfree(net->ipv4.vif_table);
 +      ipmr_rules_exit(net);
  }
  
  static struct pernet_operations ipmr_net_ops = {
@@@ -2426,6 -2048,7 +2427,6 @@@ int __init ip_mr_init(void
        if (err)
                goto reg_pernet_fail;
  
 -      setup_timer(&ipmr_expire_timer, ipmr_expire_process, 0);
        err = register_netdevice_notifier(&ip_mr_notifier);
        if (err)
                goto reg_notif_fail;
                goto add_proto_fail;
        }
  #endif
 +      rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, NULL, ipmr_rtm_dumproute);
        return 0;
  
  #ifdef CONFIG_IP_PIMSM_V2
@@@ -2444,6 -2066,7 +2445,6 @@@ add_proto_fail
        unregister_netdevice_notifier(&ip_mr_notifier);
  #endif
  reg_notif_fail:
 -      del_timer(&ipmr_expire_timer);
        unregister_pernet_subsys(&ipmr_net_ops);
  reg_pernet_fail:
        kmem_cache_destroy(mrt_cachep);
diff --combined net/ipv4/udp.c
@@@ -307,13 -307,13 +307,13 @@@ static int ipv4_rcv_saddr_equal(const s
  static unsigned int udp4_portaddr_hash(struct net *net, __be32 saddr,
                                       unsigned int port)
  {
 -      return jhash_1word(saddr, net_hash_mix(net)) ^ port;
 +      return jhash_1word((__force u32)saddr, net_hash_mix(net)) ^ port;
  }
  
  int udp_v4_get_port(struct sock *sk, unsigned short snum)
  {
        unsigned int hash2_nulladdr =
 -              udp4_portaddr_hash(sock_net(sk), INADDR_ANY, snum);
 +              udp4_portaddr_hash(sock_net(sk), htonl(INADDR_ANY), snum);
        unsigned int hash2_partial =
                udp4_portaddr_hash(sock_net(sk), inet_sk(sk)->inet_rcv_saddr, 0);
  
@@@ -466,14 -466,14 +466,14 @@@ static struct sock *__udp4_lib_lookup(s
                                          daddr, hnum, dif,
                                          hslot2, slot2);
                if (!result) {
 -                      hash2 = udp4_portaddr_hash(net, INADDR_ANY, hnum);
 +                      hash2 = udp4_portaddr_hash(net, htonl(INADDR_ANY), hnum);
                        slot2 = hash2 & udptable->mask;
                        hslot2 = &udptable->hash2[slot2];
                        if (hslot->count < hslot2->count)
                                goto begin;
  
                        result = udp4_lib_lookup2(net, saddr, sport,
 -                                                INADDR_ANY, hnum, dif,
 +                                                htonl(INADDR_ANY), hnum, dif,
                                                  hslot2, slot2);
                }
                rcu_read_unlock();
@@@ -1062,10 -1062,10 +1062,10 @@@ static unsigned int first_packet_length
        spin_unlock_bh(&rcvq->lock);
  
        if (!skb_queue_empty(&list_kill)) {
 -              lock_sock(sk);
 +              lock_sock_bh(sk);
                __skb_queue_purge(&list_kill);
                sk_mem_reclaim_partial(sk);
 -              release_sock(sk);
 +              unlock_sock_bh(sk);
        }
        return res;
  }
        return err;
  
  csum_copy_err:
 -      lock_sock(sk);
 +      lock_sock_bh(sk);
        if (!skb_kill_datagram(sk, skb, flags))
                UDP_INC_STATS_USER(sock_net(sk), UDP_MIB_INERRORS, is_udplite);
 -      release_sock(sk);
 +      unlock_sock_bh(sk);
  
        if (noblock)
                return -EAGAIN;
@@@ -1217,7 -1217,6 +1217,7 @@@ int udp_disconnect(struct sock *sk, in
        sk->sk_state = TCP_CLOSE;
        inet->inet_daddr = 0;
        inet->inet_dport = 0;
 +      sock_rps_save_rxhash(sk, 0);
        sk->sk_bound_dev_if = 0;
        if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
                inet_reset_saddr(sk);
@@@ -1259,12 -1258,8 +1259,12 @@@ EXPORT_SYMBOL(udp_lib_unhash)
  
  static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
  {
 -      int rc = sock_queue_rcv_skb(sk, skb);
 +      int rc;
 +
 +      if (inet_sk(sk)->inet_daddr)
 +              sock_rps_save_rxhash(sk, skb->rxhash);
  
 +      rc = ip_queue_rcv_skb(sk, skb);
        if (rc < 0) {
                int is_udplite = IS_UDPLITE(sk);
  
@@@ -1372,10 -1367,6 +1372,10 @@@ int udp_queue_rcv_skb(struct sock *sk, 
                        goto drop;
        }
  
 +
 +      if (sk_rcvqueues_full(sk, skb))
 +              goto drop;
 +
        rc = 0;
  
        bh_lock_sock(sk);
@@@ -1536,6 -1527,9 +1536,9 @@@ int __udp4_lib_rcv(struct sk_buff *skb
  
        uh   = udp_hdr(skb);
        ulen = ntohs(uh->len);
+       saddr = ip_hdr(skb)->saddr;
+       daddr = ip_hdr(skb)->daddr;
        if (ulen > skb->len)
                goto short_packet;
  
        if (udp4_csum_init(skb, uh, proto))
                goto csum_error;
  
-       saddr = ip_hdr(skb)->saddr;
-       daddr = ip_hdr(skb)->daddr;
        if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
                return __udp4_lib_mcast_deliver(net, skb, uh,
                                saddr, daddr, udptable);
@@@ -1624,9 -1615,9 +1624,9 @@@ int udp_rcv(struct sk_buff *skb
  
  void udp_destroy_sock(struct sock *sk)
  {
 -      lock_sock(sk);
 +      lock_sock_bh(sk);
        udp_flush_pending_frames(sk);
 -      release_sock(sk);
 +      unlock_sock_bh(sk);
  }
  
  /*
diff --combined net/ipv6/af_inet6.c
@@@ -200,7 -200,7 +200,7 @@@ lookup_protocol
  
        inet_sk(sk)->pinet6 = np = inet6_sk_generic(sk);
        np->hop_limit   = -1;
-       np->mcast_hops  = -1;
+       np->mcast_hops  = IPV6_DEFAULT_MCASTHOPS;
        np->mc_loop     = 1;
        np->pmtudisc    = IPV6_PMTUDISC_WANT;
        np->ipv6only    = net->ipv6.sysctl.bindv6only;
@@@ -416,9 -416,6 +416,9 @@@ void inet6_destroy_sock(struct sock *sk
  
        if ((skb = xchg(&np->pktoptions, NULL)) != NULL)
                kfree_skb(skb);
 +
 +      if ((skb = xchg(&np->rxpmtu, NULL)) != NULL)
 +              kfree_skb(skb);
  
        /* Free flowlabels */
        fl6_free_socklist(sk);
diff --combined net/ipv6/datagram.c
@@@ -222,6 -222,8 +222,8 @@@ void ipv6_icmp_error(struct sock *sk, s
        if (!skb)
                return;
  
+       skb->protocol = htons(ETH_P_IPV6);
        serr = SKB_EXT_ERR(skb);
        serr->ee.ee_errno = err;
        serr->ee.ee_origin = SO_EE_ORIGIN_ICMP6;
@@@ -255,6 -257,8 +257,8 @@@ void ipv6_local_error(struct sock *sk, 
        if (!skb)
                return;
  
+       skb->protocol = htons(ETH_P_IPV6);
        skb_put(skb, sizeof(struct ipv6hdr));
        skb_reset_network_header(skb);
        iph = ipv6_hdr(skb);
                kfree_skb(skb);
  }
  
 +void ipv6_local_rxpmtu(struct sock *sk, struct flowi *fl, u32 mtu)
 +{
 +      struct ipv6_pinfo *np = inet6_sk(sk);
 +      struct ipv6hdr *iph;
 +      struct sk_buff *skb;
 +      struct ip6_mtuinfo *mtu_info;
 +
 +      if (!np->rxopt.bits.rxpmtu)
 +              return;
 +
 +      skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC);
 +      if (!skb)
 +              return;
 +
 +      skb_put(skb, sizeof(struct ipv6hdr));
 +      skb_reset_network_header(skb);
 +      iph = ipv6_hdr(skb);
 +      ipv6_addr_copy(&iph->daddr, &fl->fl6_dst);
 +
 +      mtu_info = IP6CBMTU(skb);
 +      if (!mtu_info) {
 +              kfree_skb(skb);
 +              return;
 +      }
 +
 +      mtu_info->ip6m_mtu = mtu;
 +      mtu_info->ip6m_addr.sin6_family = AF_INET6;
 +      mtu_info->ip6m_addr.sin6_port = 0;
 +      mtu_info->ip6m_addr.sin6_flowinfo = 0;
 +      mtu_info->ip6m_addr.sin6_scope_id = fl->oif;
 +      ipv6_addr_copy(&mtu_info->ip6m_addr.sin6_addr, &ipv6_hdr(skb)->daddr);
 +
 +      __skb_pull(skb, skb_tail_pointer(skb) - skb->data);
 +      skb_reset_transport_header(skb);
 +
 +      skb = xchg(&np->rxpmtu, skb);
 +      kfree_skb(skb);
 +}
 +
  /*
   *    Handle MSG_ERRQUEUE
   */
@@@ -358,7 -323,7 +362,7 @@@ int ipv6_recv_error(struct sock *sk, st
                sin->sin6_flowinfo = 0;
                sin->sin6_port = serr->port;
                sin->sin6_scope_id = 0;
-               if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) {
+               if (skb->protocol == htons(ETH_P_IPV6)) {
                        ipv6_addr_copy(&sin->sin6_addr,
                                  (struct in6_addr *)(nh + serr->addr_offset));
                        if (np->sndflow)
                sin->sin6_family = AF_INET6;
                sin->sin6_flowinfo = 0;
                sin->sin6_scope_id = 0;
-               if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) {
+               if (skb->protocol == htons(ETH_P_IPV6)) {
                        ipv6_addr_copy(&sin->sin6_addr, &ipv6_hdr(skb)->saddr);
                        if (np->rxopt.all)
                                datagram_recv_ctl(sk, msg, skb);
@@@ -420,54 -385,6 +424,54 @@@ out
        return err;
  }
  
 +/*
 + *    Handle IPV6_RECVPATHMTU
 + */
 +int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len)
 +{
 +      struct ipv6_pinfo *np = inet6_sk(sk);
 +      struct sk_buff *skb;
 +      struct sockaddr_in6 *sin;
 +      struct ip6_mtuinfo mtu_info;
 +      int err;
 +      int copied;
 +
 +      err = -EAGAIN;
 +      skb = xchg(&np->rxpmtu, NULL);
 +      if (skb == NULL)
 +              goto out;
 +
 +      copied = skb->len;
 +      if (copied > len) {
 +              msg->msg_flags |= MSG_TRUNC;
 +              copied = len;
 +      }
 +      err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 +      if (err)
 +              goto out_free_skb;
 +
 +      sock_recv_timestamp(msg, sk, skb);
 +
 +      memcpy(&mtu_info, IP6CBMTU(skb), sizeof(mtu_info));
 +
 +      sin = (struct sockaddr_in6 *)msg->msg_name;
 +      if (sin) {
 +              sin->sin6_family = AF_INET6;
 +              sin->sin6_flowinfo = 0;
 +              sin->sin6_port = 0;
 +              sin->sin6_scope_id = mtu_info.ip6m_addr.sin6_scope_id;
 +              ipv6_addr_copy(&sin->sin6_addr, &mtu_info.ip6m_addr.sin6_addr);
 +      }
 +
 +      put_cmsg(msg, SOL_IPV6, IPV6_PATHMTU, sizeof(mtu_info), &mtu_info);
 +
 +      err = copied;
 +
 +out_free_skb:
 +      kfree_skb(skb);
 +out:
 +      return err;
 +}
  
  
  int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
  int datagram_send_ctl(struct net *net,
                      struct msghdr *msg, struct flowi *fl,
                      struct ipv6_txoptions *opt,
 -                    int *hlimit, int *tclass)
 +                    int *hlimit, int *tclass, int *dontfrag)
  {
        struct in6_pktinfo *src_info;
        struct cmsghdr *cmsg;
  
                        break;
                    }
 +
 +              case IPV6_DONTFRAG:
 +                  {
 +                      int df;
 +
 +                      err = -EINVAL;
 +                      if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
 +                              goto exit_f;
 +                      }
 +
 +                      df = *(int *)CMSG_DATA(cmsg);
 +                      if (df < 0 || df > 1)
 +                              goto exit_f;
 +
 +                      err = 0;
 +                      *dontfrag = df;
 +
 +                      break;
 +                  }
                default:
                        LIMIT_NETDEBUG(KERN_DEBUG "invalid cmsg type: %d\n",
                                       cmsg->cmsg_type);
diff --combined net/mac80211/mlme.c
   */
  #define IEEE80211_PROBE_WAIT          (HZ / 2)
  
 +/*
 + * Weight given to the latest Beacon frame when calculating average signal
 + * strength for Beacon frames received in the current BSS. This must be
 + * between 1 and 15.
 + */
 +#define IEEE80211_SIGNAL_AVE_WEIGHT   3
 +
  #define TMR_RUNNING_TIMER     0
  #define TMR_RUNNING_CHANSW    1
  
@@@ -137,14 -130,11 +137,14 @@@ static u32 ieee80211_enable_ht(struct i
        struct sta_info *sta;
        u32 changed = 0;
        u16 ht_opmode;
 -      bool enable_ht = true, ht_changed;
 +      bool enable_ht = true;
 +      enum nl80211_channel_type prev_chantype;
        enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
  
        sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
  
 +      prev_chantype = sdata->vif.bss_conf.channel_type;
 +
        /* HT is not supported */
        if (!sband->ht_cap.ht_supported)
                enable_ht = false;
                }
        }
  
 -      ht_changed = conf_is_ht(&local->hw.conf) != enable_ht ||
 -                   channel_type != local->hw.conf.channel_type;
 -
        if (local->tmp_channel)
                local->tmp_channel_type = channel_type;
 -      local->oper_channel_type = channel_type;
  
 -      if (ht_changed) {
 -                /* channel_type change automatically detected */
 -              ieee80211_hw_config(local, 0);
 +      if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
 +              /* can only fail due to HT40+/- mismatch */
 +              channel_type = NL80211_CHAN_HT20;
 +              WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type));
 +      }
 +
 +      /* channel_type change automatically detected */
 +      ieee80211_hw_config(local, 0);
  
 +      if (prev_chantype != channel_type) {
                rcu_read_lock();
                sta = sta_info_get(sdata, bssid);
                if (sta)
                        rate_control_rate_update(local, sband, sta,
                                                 IEEE80211_RC_HT_CHANGED,
 -                                               local->oper_channel_type);
 +                                               channel_type);
                rcu_read_unlock();
 -        }
 -
 -      /* disable HT */
 -      if (!enable_ht)
 -              return 0;
 +      }
  
        ht_opmode = le16_to_cpu(hti->operation_mode);
  
        /* if bss configuration changed store the new one */
 -      if (!sdata->ht_opmode_valid ||
 -          sdata->vif.bss_conf.ht_operation_mode != ht_opmode) {
 +      if (sdata->ht_opmode_valid != enable_ht ||
 +          sdata->vif.bss_conf.ht_operation_mode != ht_opmode ||
 +          prev_chantype != channel_type) {
                changed |= BSS_CHANGED_HT;
                sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
 -              sdata->ht_opmode_valid = true;
 +              sdata->ht_opmode_valid = enable_ht;
        }
  
        return changed;
  
  static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
                                           const u8 *bssid, u16 stype, u16 reason,
 -                                         void *cookie)
 +                                         void *cookie, bool send_frame)
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
                        cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
        if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
 -      ieee80211_tx_skb(sdata, skb);
 +
 +      if (send_frame)
 +              ieee80211_tx_skb(sdata, skb);
 +      else
 +              kfree_skb(skb);
  }
  
  void ieee80211_send_pspoll(struct ieee80211_local *local,
@@@ -480,7 -467,6 +480,7 @@@ void ieee80211_recalc_ps(struct ieee802
  {
        struct ieee80211_sub_if_data *sdata, *found = NULL;
        int count = 0;
 +      int timeout;
  
        if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) {
                local->ps_sdata = NULL;
                beaconint_us = ieee80211_tu_to_usec(
                                        found->vif.bss_conf.beacon_int);
  
 +              timeout = local->hw.conf.dynamic_ps_forced_timeout;
 +              if (timeout < 0) {
 +                      /*
 +                       * The 2 second value is there for compatibility until
 +                       * the PM_QOS_NETWORK_LATENCY is configured with real
 +                       * values.
 +                       */
 +                      if (latency == 2000000000)
 +                              timeout = 100;
 +                      else if (latency <= 50000)
 +                              timeout = 300;
 +                      else if (latency <= 100000)
 +                              timeout = 100;
 +                      else if (latency <= 500000)
 +                              timeout = 50;
 +                      else
 +                              timeout = 0;
 +              }
 +              local->hw.conf.dynamic_ps_timeout = timeout;
 +
                if (beaconint_us > latency) {
                        local->ps_sdata = NULL;
                } else {
@@@ -626,9 -592,6 +626,9 @@@ static void ieee80211_sta_wmm_params(st
        int count;
        u8 *pos, uapsd_queues = 0;
  
 +      if (!local->ops->conf_tx)
 +              return;
 +
        if (local->hw.queues < 4)
                return;
  
                       params.aifs, params.cw_min, params.cw_max, params.txop,
                       params.uapsd);
  #endif
 -              if (drv_conf_tx(local, queue, &params) && local->ops->conf_tx)
 +              if (drv_conf_tx(local, queue, &params))
                        printk(KERN_DEBUG "%s: failed to set TX queue "
                               "parameters for queue %d\n",
                               wiphy_name(local->hw.wiphy), queue);
        }
 +
 +      /* enable WMM or activate new settings */
 +      local->hw.conf.flags |= IEEE80211_CONF_QOS;
 +      drv_config(local, IEEE80211_CONF_CHANGE_QOS);
  }
  
  static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
@@@ -772,8 -731,6 +772,8 @@@ static void ieee80211_set_associated(st
        sdata->u.mgd.associated = cbss;
        memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN);
  
 +      sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
 +
        /* just to be sure */
        sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
                                IEEE80211_STA_BEACON_POLL);
        /* And the BSSID changed - we're associated now */
        bss_info_changed |= BSS_CHANGED_BSSID;
  
 +      /* Tell the driver to monitor connection quality (if supported) */
 +      if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) &&
 +          sdata->vif.bss_conf.cqm_rssi_thold)
 +              bss_info_changed |= BSS_CHANGED_CQM;
 +
        ieee80211_bss_info_change_notify(sdata, bss_info_changed);
  
        mutex_lock(&local->iflist_mtx);
        netif_carrier_on(sdata->dev);
  }
  
 -static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
 +static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 +                                 bool remove_sta)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
        ieee80211_set_wmm_default(sdata);
  
        /* channel(_type) changes are handled by ieee80211_hw_config */
 -      local->oper_channel_type = NL80211_CHAN_NO_HT;
 +      WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
  
        /* on the next assoc, re-program HT parameters */
        sdata->ht_opmode_valid = false;
  
        ieee80211_hw_config(local, config_changed);
  
 -      /* And the BSSID changed -- not very interesting here */
 -      changed |= BSS_CHANGED_BSSID;
 +      /* The BSSID (not really interesting) and HT changed */
 +      changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
        ieee80211_bss_info_change_notify(sdata, changed);
  
 -      sta_info_destroy_addr(sdata, bssid);
 +      if (remove_sta)
 +              sta_info_destroy_addr(sdata, bssid);
  }
  
  void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
        if (is_multicast_ether_addr(hdr->addr1))
                return;
  
 +      if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
 +              return;
 +
        mod_timer(&sdata->u.mgd.conn_mon_timer,
                  round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
  }
@@@ -987,72 -934,23 +987,72 @@@ static void ieee80211_mgd_probe_ap(stru
        mutex_unlock(&ifmgd->mtx);
  }
  
 -void ieee80211_beacon_loss_work(struct work_struct *work)
 +static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
 +{
 +      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      struct ieee80211_local *local = sdata->local;
 +      u8 bssid[ETH_ALEN];
 +
 +      mutex_lock(&ifmgd->mtx);
 +      if (!ifmgd->associated) {
 +              mutex_unlock(&ifmgd->mtx);
 +              return;
 +      }
 +
 +      memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
 +
 +      printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid);
 +
 +      ieee80211_set_disassoc(sdata, true);
 +      ieee80211_recalc_idle(local);
 +      mutex_unlock(&ifmgd->mtx);
 +      /*
 +       * must be outside lock due to cfg80211,
 +       * but that's not a problem.
 +       */
 +      ieee80211_send_deauth_disassoc(sdata, bssid,
 +                                     IEEE80211_STYPE_DEAUTH,
 +                                     WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
 +                                     NULL, true);
 +}
 +
 +void ieee80211_beacon_connection_loss_work(struct work_struct *work)
  {
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data,
 -                           u.mgd.beacon_loss_work);
 +                           u.mgd.beacon_connection_loss_work);
  
 -      ieee80211_mgd_probe_ap(sdata, true);
 +      if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
 +              __ieee80211_connection_loss(sdata);
 +      else
 +              ieee80211_mgd_probe_ap(sdata, true);
  }
  
  void ieee80211_beacon_loss(struct ieee80211_vif *vif)
  {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 +      struct ieee80211_hw *hw = &sdata->local->hw;
 +
 +      trace_api_beacon_loss(sdata);
  
 -      ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
 +      WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
 +      ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
  }
  EXPORT_SYMBOL(ieee80211_beacon_loss);
  
 +void ieee80211_connection_loss(struct ieee80211_vif *vif)
 +{
 +      struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 +      struct ieee80211_hw *hw = &sdata->local->hw;
 +
 +      trace_api_connection_loss(sdata);
 +
 +      WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR));
 +      ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
 +}
 +EXPORT_SYMBOL(ieee80211_connection_loss);
 +
 +
  static enum rx_mgmt_action __must_check
  ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
                         struct ieee80211_mgmt *mgmt, size_t len)
        printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
                        sdata->name, bssid, reason_code);
  
 -      ieee80211_set_disassoc(sdata);
 +      ieee80211_set_disassoc(sdata, true);
        ieee80211_recalc_idle(sdata->local);
  
        return RX_MGMT_CFG80211_DEAUTH;
@@@ -1103,7 -1001,7 +1103,7 @@@ ieee80211_rx_mgmt_disassoc(struct ieee8
        printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n",
                        sdata->name, mgmt->sa, reason_code);
  
 -      ieee80211_set_disassoc(sdata);
 +      ieee80211_set_disassoc(sdata, true);
        ieee80211_recalc_idle(sdata->local);
        return RX_MGMT_CFG80211_DISASSOC;
  }
@@@ -1356,17 -1254,12 +1356,17 @@@ static void ieee80211_rx_mgmt_probe_res
                mutex_lock(&sdata->local->iflist_mtx);
                ieee80211_recalc_ps(sdata->local, -1);
                mutex_unlock(&sdata->local->iflist_mtx);
 +
 +              if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
 +                      return;
 +
                /*
                 * We've received a probe response, but are not sure whether
                 * we have or will be receiving any beacons or data, so let's
                 * schedule the timers again, just in case.
                 */
                mod_beacon_timer(sdata);
 +
                mod_timer(&ifmgd->conn_mon_timer,
                          round_jiffies_up(jiffies +
                                           IEEE80211_CONNECTION_IDLE_TIME));
@@@ -1400,7 -1293,6 +1400,7 @@@ static void ieee80211_rx_mgmt_beacon(st
                                     struct ieee80211_rx_status *rx_status)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
        size_t baselen;
        struct ieee802_11_elems elems;
        struct ieee80211_local *local = sdata->local;
        if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)
                return;
  
 +      /* Track average RSSI from the Beacon frames of the current AP */
 +      ifmgd->last_beacon_signal = rx_status->signal;
 +      if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) {
 +              ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE;
 +              ifmgd->ave_beacon_signal = rx_status->signal;
 +              ifmgd->last_cqm_event_signal = 0;
 +      } else {
 +              ifmgd->ave_beacon_signal =
 +                      (IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 +
 +                       (16 - IEEE80211_SIGNAL_AVE_WEIGHT) *
 +                       ifmgd->ave_beacon_signal) / 16;
 +      }
 +      if (bss_conf->cqm_rssi_thold &&
 +          !(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) {
 +              int sig = ifmgd->ave_beacon_signal / 16;
 +              int last_event = ifmgd->last_cqm_event_signal;
 +              int thold = bss_conf->cqm_rssi_thold;
 +              int hyst = bss_conf->cqm_rssi_hyst;
 +              if (sig < thold &&
 +                  (last_event == 0 || sig < last_event - hyst)) {
 +                      ifmgd->last_cqm_event_signal = sig;
 +                      ieee80211_cqm_rssi_notify(
 +                              &sdata->vif,
 +                              NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
 +                              GFP_KERNEL);
 +              } else if (sig > thold &&
 +                         (last_event == 0 || sig > last_event + hyst)) {
 +                      ifmgd->last_cqm_event_signal = sig;
 +                      ieee80211_cqm_rssi_notify(
 +                              &sdata->vif,
 +                              NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
 +                              GFP_KERNEL);
 +              }
 +      }
 +
        if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
  #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
                if (net_ratelimit()) {
@@@ -1756,7 -1613,7 +1756,7 @@@ static void ieee80211_sta_work(struct w
                        printk(KERN_DEBUG "No probe response from AP %pM"
                                " after %dms, disconnecting.\n",
                                bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
 -                      ieee80211_set_disassoc(sdata);
 +                      ieee80211_set_disassoc(sdata, true);
                        ieee80211_recalc_idle(local);
                        mutex_unlock(&ifmgd->mtx);
                        /*
                        ieee80211_send_deauth_disassoc(sdata, bssid,
                                        IEEE80211_STYPE_DEAUTH,
                                        WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
 -                                      NULL);
 +                                      NULL, true);
                        mutex_lock(&ifmgd->mtx);
                }
        }
@@@ -1783,8 -1640,7 +1783,8 @@@ static void ieee80211_sta_bcn_mon_timer
        if (local->quiescing)
                return;
  
 -      ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
 +      ieee80211_queue_work(&sdata->local->hw,
 +                           &sdata->u.mgd.beacon_connection_loss_work);
  }
  
  static void ieee80211_sta_conn_mon_timer(unsigned long data)
@@@ -1836,7 -1692,7 +1836,7 @@@ void ieee80211_sta_quiesce(struct ieee8
         */
  
        cancel_work_sync(&ifmgd->work);
 -      cancel_work_sync(&ifmgd->beacon_loss_work);
 +      cancel_work_sync(&ifmgd->beacon_connection_loss_work);
        if (del_timer_sync(&ifmgd->timer))
                set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
  
@@@ -1870,8 -1726,7 +1870,8 @@@ void ieee80211_sta_setup_sdata(struct i
        INIT_WORK(&ifmgd->work, ieee80211_sta_work);
        INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
        INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
 -      INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work);
 +      INIT_WORK(&ifmgd->beacon_connection_loss_work,
 +                ieee80211_beacon_connection_loss_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
        setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@@@ -1950,9 -1805,6 +1950,9 @@@ int ieee80211_mgd_auth(struct ieee80211
        struct ieee80211_work *wk;
        u16 auth_alg;
  
 +      if (req->local_state_change)
 +              return 0; /* no need to update mac80211 state */
 +
        switch (req->auth_type) {
        case NL80211_AUTHTYPE_OPEN_SYSTEM:
                auth_alg = WLAN_AUTH_OPEN;
@@@ -2061,7 -1913,7 +2061,7 @@@ int ieee80211_mgd_assoc(struct ieee8021
                }
  
                /* Trying to reassociate - clear previous association state */
 -              ieee80211_set_disassoc(sdata);
 +              ieee80211_set_disassoc(sdata, true);
        }
        mutex_unlock(&ifmgd->mtx);
  
@@@ -2165,7 -2017,7 +2165,7 @@@ int ieee80211_mgd_deauth(struct ieee802
  
        if (ifmgd->associated == req->bss) {
                bssid = req->bss->bssid;
 -              ieee80211_set_disassoc(sdata);
 +              ieee80211_set_disassoc(sdata, true);
                mutex_unlock(&ifmgd->mtx);
        } else {
                bool not_auth_yet = false;
                                continue;
  
                        if (wk->type != IEEE80211_WORK_DIRECT_PROBE &&
-                           wk->type != IEEE80211_WORK_AUTH)
+                           wk->type != IEEE80211_WORK_AUTH &&
+                           wk->type != IEEE80211_WORK_ASSOC)
                                continue;
  
                        if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN))
        printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n",
               sdata->name, bssid, req->reason_code);
  
 -      ieee80211_send_deauth_disassoc(sdata, bssid,
 -                      IEEE80211_STYPE_DEAUTH, req->reason_code,
 -                      cookie);
 +      ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH,
 +                                     req->reason_code, cookie,
 +                                     !req->local_state_change);
  
        ieee80211_recalc_idle(sdata->local);
  
@@@ -2222,7 -2075,6 +2223,7 @@@ int ieee80211_mgd_disassoc(struct ieee8
                           void *cookie)
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      u8 bssid[ETH_ALEN];
  
        mutex_lock(&ifmgd->mtx);
  
        printk(KERN_DEBUG "%s: disassociating from %pM by local choice (reason=%d)\n",
               sdata->name, req->bss->bssid, req->reason_code);
  
 -      ieee80211_set_disassoc(sdata);
 +      memcpy(bssid, req->bss->bssid, ETH_ALEN);
 +      ieee80211_set_disassoc(sdata, false);
  
        mutex_unlock(&ifmgd->mtx);
  
        ieee80211_send_deauth_disassoc(sdata, req->bss->bssid,
                        IEEE80211_STYPE_DISASSOC, req->reason_code,
 -                      cookie);
 +                      cookie, !req->local_state_change);
 +      sta_info_destroy_addr(sdata, bssid);
  
        ieee80211_recalc_idle(sdata->local);
  
@@@ -2268,7 -2118,7 +2269,7 @@@ int ieee80211_mgd_action(struct ieee802
        if ((chan != local->tmp_channel ||
             channel_type != local->tmp_channel_type) &&
            (chan != local->oper_channel ||
 -           channel_type != local->oper_channel_type))
 +           channel_type != local->_oper_channel_type))
                return -EBUSY;
  
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + len);
        *cookie = (unsigned long) skb;
        return 0;
  }
 +
 +void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
 +                             enum nl80211_cqm_rssi_threshold_event rssi_event,
 +                             gfp_t gfp)
 +{
 +      struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 +
 +      trace_api_cqm_rssi_notify(sdata, rssi_event);
 +
 +      cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
 +}
 +EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
diff --combined net/sctp/sm_sideeffect.c
@@@ -397,6 -397,41 +397,41 @@@ out_unlock
        sctp_transport_put(transport);
  }
  
+ /* Handle the timeout of the ICMP protocol unreachable timer.  Trigger
+  * the correct state machine transition that will close the association.
+  */
+ void sctp_generate_proto_unreach_event(unsigned long data)
+ {
+       struct sctp_transport *transport = (struct sctp_transport *) data;
+       struct sctp_association *asoc = transport->asoc;
+       
+       sctp_bh_lock_sock(asoc->base.sk);
+       if (sock_owned_by_user(asoc->base.sk)) {
+               SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __func__);
+               /* Try again later.  */
+               if (!mod_timer(&transport->proto_unreach_timer,
+                               jiffies + (HZ/20)))
+                       sctp_association_hold(asoc);
+               goto out_unlock;
+       }
+       /* Is this structure just waiting around for us to actually
+        * get destroyed?
+        */
+       if (asoc->base.dead)
+               goto out_unlock;
+       sctp_do_sm(SCTP_EVENT_T_OTHER,
+                  SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH),
+                  asoc->state, asoc->ep, asoc, transport, GFP_ATOMIC);
+ out_unlock:
+       sctp_bh_unlock_sock(asoc->base.sk);
+       sctp_association_put(asoc);
+ }
  /* Inject a SACK Timeout event into the state machine.  */
  static void sctp_generate_sack_event(unsigned long data)
  {
@@@ -697,15 -732,11 +732,15 @@@ static void sctp_cmd_setup_t2(sctp_cmd_
  {
        struct sctp_transport *t;
  
 -      t = sctp_assoc_choose_alter_transport(asoc,
 +      if (chunk->transport)
 +              t = chunk->transport;
 +      else {
 +              t = sctp_assoc_choose_alter_transport(asoc,
                                              asoc->shutdown_last_sent_to);
 +              chunk->transport = t;
 +      }
        asoc->shutdown_last_sent_to = t;
        asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto;
 -      chunk->transport = t;
  }
  
  /* Helper function to change the state of an association. */
diff --combined net/sctp/transport.c
@@@ -64,6 -64,9 +64,6 @@@ static struct sctp_transport *sctp_tran
        /* Copy in the address.  */
        peer->ipaddr = *addr;
        peer->af_specific = sctp_get_af_specific(addr->sa.sa_family);
 -      peer->asoc = NULL;
 -
 -      peer->dst = NULL;
        memset(&peer->saddr, 0, sizeof(union sctp_addr));
  
        /* From 6.3.1 RTO Calculation:
         * parameter 'RTO.Initial'.
         */
        peer->rto = msecs_to_jiffies(sctp_rto_initial);
 -      peer->rtt = 0;
 -      peer->rttvar = 0;
 -      peer->srtt = 0;
 -      peer->rto_pending = 0;
 -      peer->hb_sent = 0;
 -      peer->fast_recovery = 0;
  
        peer->last_time_heard = jiffies;
        peer->last_time_ecne_reduced = jiffies;
  
        peer->param_flags = SPP_HB_DISABLE |
                            SPP_PMTUD_ENABLE |
                            SPP_SACKDELAY_ENABLE;
  
        /* Initialize the default path max_retrans.  */
        peer->pathmaxrxt  = sctp_max_retrans_path;
 -      peer->error_count = 0;
  
        INIT_LIST_HEAD(&peer->transmitted);
        INIT_LIST_HEAD(&peer->send_ready);
        INIT_LIST_HEAD(&peer->transports);
  
 -      peer->T3_rtx_timer.expires = 0;
 -      peer->hb_timer.expires = 0;
 -
        setup_timer(&peer->T3_rtx_timer, sctp_generate_t3_rtx_event,
                        (unsigned long)peer);
        setup_timer(&peer->hb_timer, sctp_generate_heartbeat_event,
                        (unsigned long)peer);
+       setup_timer(&peer->proto_unreach_timer,
+                   sctp_generate_proto_unreach_event, (unsigned long)peer);
  
        /* Initialize the 64-bit random nonce sent with heartbeat. */
        get_random_bytes(&peer->hb_nonce, sizeof(peer->hb_nonce));
  
        atomic_set(&peer->refcnt, 1);
 -      peer->dead = 0;
 -
 -      peer->malloced = 0;
 -
 -      /* Initialize the state information for SFR-CACC */
 -      peer->cacc.changeover_active = 0;
 -      peer->cacc.cycling_changeover = 0;
 -      peer->cacc.next_tsn_at_change = 0;
 -      peer->cacc.cacc_saw_newack = 0;
  
        return peer;
  }
@@@ -170,7 -197,7 +172,7 @@@ static void sctp_transport_destroy(stru
  /* Start T3_rtx timer if it is not already running and update the heartbeat
   * timer.  This routine is called every time a DATA chunk is sent.
   */
 -void sctp_transport_reset_timers(struct sctp_transport *transport, int force)
 +void sctp_transport_reset_timers(struct sctp_transport *transport)
  {
        /* RFC 2960 6.3.2 Retransmission Timer Rules
         *
         * address.
         */
  
 -      if (force || !timer_pending(&transport->T3_rtx_timer))
 +      if (!timer_pending(&transport->T3_rtx_timer))
                if (!mod_timer(&transport->T3_rtx_timer,
                               jiffies + transport->rto))
                        sctp_transport_hold(transport);
@@@ -378,16 -405,15 +380,16 @@@ void sctp_transport_update_rto(struct s
  void sctp_transport_raise_cwnd(struct sctp_transport *transport,
                               __u32 sack_ctsn, __u32 bytes_acked)
  {
 +      struct sctp_association *asoc = transport->asoc;
        __u32 cwnd, ssthresh, flight_size, pba, pmtu;
  
        cwnd = transport->cwnd;
        flight_size = transport->flight_size;
  
        /* See if we need to exit Fast Recovery first */
 -      if (transport->fast_recovery &&
 -          TSN_lte(transport->fast_recovery_exit, sack_ctsn))
 -              transport->fast_recovery = 0;
 +      if (asoc->fast_recovery &&
 +          TSN_lte(asoc->fast_recovery_exit, sack_ctsn))
 +              asoc->fast_recovery = 0;
  
        /* The appropriate cwnd increase algorithm is performed if, and only
         * if the cumulative TSN whould advanced and the congestion window is
                 *    2) the destination's path MTU.  This upper bound protects
                 *    against the ACK-Splitting attack outlined in [SAVAGE99].
                 */
 -              if (transport->fast_recovery)
 +              if (asoc->fast_recovery)
                        return;
  
                if (bytes_acked > pmtu)
  void sctp_transport_lower_cwnd(struct sctp_transport *transport,
                               sctp_lower_cwnd_t reason)
  {
 +      struct sctp_association *asoc = transport->asoc;
 +
        switch (reason) {
        case SCTP_LOWER_CWND_T3_RTX:
                /* RFC 2960 Section 7.2.3, sctpimpguide
                 *      partial_bytes_acked = 0
                 */
                transport->ssthresh = max(transport->cwnd/2,
 -                                        4*transport->asoc->pathmtu);
 -              transport->cwnd = transport->asoc->pathmtu;
 +                                        4*asoc->pathmtu);
 +              transport->cwnd = asoc->pathmtu;
  
 -              /* T3-rtx also clears fast recovery on the transport */
 -              transport->fast_recovery = 0;
 +              /* T3-rtx also clears fast recovery */
 +              asoc->fast_recovery = 0;
                break;
  
        case SCTP_LOWER_CWND_FAST_RTX:
                 *      cwnd = ssthresh
                 *      partial_bytes_acked = 0
                 */
 -              if (transport->fast_recovery)
 +              if (asoc->fast_recovery)
                        return;
  
                /* Mark Fast recovery */
 -              transport->fast_recovery = 1;
 -              transport->fast_recovery_exit = transport->asoc->next_tsn - 1;
 +              asoc->fast_recovery = 1;
 +              asoc->fast_recovery_exit = asoc->next_tsn - 1;
  
                transport->ssthresh = max(transport->cwnd/2,
 -                                        4*transport->asoc->pathmtu);
 +                                        4*asoc->pathmtu);
                transport->cwnd = transport->ssthresh;
                break;
  
                if (time_after(jiffies, transport->last_time_ecne_reduced +
                                        transport->rtt)) {
                        transport->ssthresh = max(transport->cwnd/2,
 -                                                4*transport->asoc->pathmtu);
 +                                                4*asoc->pathmtu);
                        transport->cwnd = transport->ssthresh;
                        transport->last_time_ecne_reduced = jiffies;
                }
                 * interval.
                 */
                transport->cwnd = max(transport->cwnd/2,
 -                                       4*transport->asoc->pathmtu);
 +                                       4*asoc->pathmtu);
                break;
        }
  
@@@ -628,6 -652,7 +630,6 @@@ void sctp_transport_reset(struct sctp_t
        t->error_count = 0;
        t->rto_pending = 0;
        t->hb_sent = 0;
 -      t->fast_recovery = 0;
  
        /* Initialize the state information for SFR-CACC */
        t->cacc.changeover_active = 0;