Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/bwh/sfc-next-2.6
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 8668114..762a510 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -71,6 +71,9 @@
/* Apple MacBookAir3,1, MacBookAir3,2 */
{ USB_DEVICE(0x05ac, 0x821b) },
+ /* Apple MacBookPro8,2 */
+ { USB_DEVICE(0x05ac, 0x821a) },
+
/* AVM BlueFRITZ! USB v2.0 */
{ USB_DEVICE(0x057c, 0x3800) },
@@ -690,7 +693,8 @@
break;
case HCI_ACLDATA_PKT:
- if (!data->bulk_tx_ep || hdev->conn_hash.acl_num < 1)
+ if (!data->bulk_tx_ep || (hdev->conn_hash.acl_num < 1 &&
+ hdev->conn_hash.le_num < 1))
return -ENODEV;
urb = usb_alloc_urb(0, GFP_ATOMIC);
diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c
index 4ae7505..9e1610f 100644
--- a/drivers/isdn/mISDN/layer2.c
+++ b/drivers/isdn/mISDN/layer2.c
@@ -1640,7 +1640,7 @@
}
static void
-l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg)
+l2_st14_persistent_da(struct FsmInst *fi, int event, void *arg)
{
struct layer2 *l2 = fi->userdata;
struct sk_buff *skb = arg;
@@ -1654,7 +1654,7 @@
}
static void
-l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg)
+l2_st5_persistent_da(struct FsmInst *fi, int event, void *arg)
{
struct layer2 *l2 = fi->userdata;
struct sk_buff *skb = arg;
@@ -1671,7 +1671,7 @@
}
static void
-l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg)
+l2_st6_persistent_da(struct FsmInst *fi, int event, void *arg)
{
struct layer2 *l2 = fi->userdata;
struct sk_buff *skb = arg;
@@ -1685,7 +1685,7 @@
}
static void
-l2_persistant_da(struct FsmInst *fi, int event, void *arg)
+l2_persistent_da(struct FsmInst *fi, int event, void *arg)
{
struct layer2 *l2 = fi->userdata;
struct sk_buff *skb = arg;
@@ -1829,14 +1829,14 @@
{ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error},
{ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest},
{ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest},
- {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da},
+ {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistent_da},
{ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove},
{ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove},
- {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da},
- {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da},
- {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da},
- {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da},
- {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da},
+ {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistent_da},
+ {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistent_da},
+ {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistent_da},
+ {ST_L2_7, EV_L1_DEACTIVATE, l2_persistent_da},
+ {ST_L2_8, EV_L1_DEACTIVATE, l2_persistent_da},
};
static int
diff --git a/drivers/net/bonding/bond_alb.h b/drivers/net/bonding/bond_alb.h
index 118c28a..4b3e358 100644
--- a/drivers/net/bonding/bond_alb.h
+++ b/drivers/net/bonding/bond_alb.h
@@ -74,7 +74,7 @@
* packets to a Client that the Hash function
* gave this entry index.
*/
- u32 tx_bytes; /* Each Client acumulates the BytesTx that
+ u32 tx_bytes; /* Each Client accumulates the BytesTx that
* were tranmitted to it, and after each
* CallBack the LoadHistory is devided
* by the balance interval
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 16d6fe9..b51e021 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1407,7 +1407,7 @@
int i;
features &= ~(NETIF_F_ALL_CSUM | BOND_VLAN_FEATURES);
- features |= NETIF_F_GSO_MASK | NETIF_F_NO_CSUM;
+ features |= NETIF_F_GSO_MASK | NETIF_F_NO_CSUM | NETIF_F_NOCACHE_COPY;
if (!bond->first_slave)
goto done;
diff --git a/drivers/net/jme.c b/drivers/net/jme.c
index 994c809..be4773f 100644
--- a/drivers/net/jme.c
+++ b/drivers/net/jme.c
@@ -2230,17 +2230,9 @@
jme_restart_rx_engine(jme);
}
- if (new_mtu > 1900) {
- netdev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
- NETIF_F_TSO | NETIF_F_TSO6);
- } else {
- if (test_bit(JME_FLAG_TXCSUM, &jme->flags))
- netdev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
- if (test_bit(JME_FLAG_TSO, &jme->flags))
- netdev->features |= NETIF_F_TSO | NETIF_F_TSO6;
- }
-
netdev->mtu = new_mtu;
+ netdev_update_features(netdev);
+
jme_reset_link(jme);
return 0;
@@ -2640,19 +2632,20 @@
}
static u32
-jme_get_rx_csum(struct net_device *netdev)
+jme_fix_features(struct net_device *netdev, u32 features)
{
- struct jme_adapter *jme = netdev_priv(netdev);
- return jme->reg_rxmcs & RXMCS_CHECKSUM;
+ if (netdev->mtu > 1900)
+ features &= ~(NETIF_F_ALL_TSO | NETIF_F_ALL_CSUM);
+ return features;
}
static int
-jme_set_rx_csum(struct net_device *netdev, u32 on)
+jme_set_features(struct net_device *netdev, u32 features)
{
struct jme_adapter *jme = netdev_priv(netdev);
spin_lock_bh(&jme->rxmcs_lock);
- if (on)
+ if (features & NETIF_F_RXCSUM)
jme->reg_rxmcs |= RXMCS_CHECKSUM;
else
jme->reg_rxmcs &= ~RXMCS_CHECKSUM;
@@ -2663,42 +2656,6 @@
}
static int
-jme_set_tx_csum(struct net_device *netdev, u32 on)
-{
- struct jme_adapter *jme = netdev_priv(netdev);
-
- if (on) {
- set_bit(JME_FLAG_TXCSUM, &jme->flags);
- if (netdev->mtu <= 1900)
- netdev->features |=
- NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
- } else {
- clear_bit(JME_FLAG_TXCSUM, &jme->flags);
- netdev->features &=
- ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
- }
-
- return 0;
-}
-
-static int
-jme_set_tso(struct net_device *netdev, u32 on)
-{
- struct jme_adapter *jme = netdev_priv(netdev);
-
- if (on) {
- set_bit(JME_FLAG_TSO, &jme->flags);
- if (netdev->mtu <= 1900)
- netdev->features |= NETIF_F_TSO | NETIF_F_TSO6;
- } else {
- clear_bit(JME_FLAG_TSO, &jme->flags);
- netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
- }
-
- return 0;
-}
-
-static int
jme_nway_reset(struct net_device *netdev)
{
struct jme_adapter *jme = netdev_priv(netdev);
@@ -2839,11 +2796,6 @@
.get_link = jme_get_link,
.get_msglevel = jme_get_msglevel,
.set_msglevel = jme_set_msglevel,
- .get_rx_csum = jme_get_rx_csum,
- .set_rx_csum = jme_set_rx_csum,
- .set_tx_csum = jme_set_tx_csum,
- .set_tso = jme_set_tso,
- .set_sg = ethtool_op_set_sg,
.nway_reset = jme_nway_reset,
.get_eeprom_len = jme_get_eeprom_len,
.get_eeprom = jme_get_eeprom,
@@ -2903,6 +2855,8 @@
.ndo_change_mtu = jme_change_mtu,
.ndo_tx_timeout = jme_tx_timeout,
.ndo_vlan_rx_register = jme_vlan_rx_register,
+ .ndo_fix_features = jme_fix_features,
+ .ndo_set_features = jme_set_features,
};
static int __devinit
@@ -2957,6 +2911,12 @@
netdev->netdev_ops = &jme_netdev_ops;
netdev->ethtool_ops = &jme_ethtool_ops;
netdev->watchdog_timeo = TX_TIMEOUT;
+ netdev->hw_features = NETIF_F_IP_CSUM |
+ NETIF_F_IPV6_CSUM |
+ NETIF_F_SG |
+ NETIF_F_TSO |
+ NETIF_F_TSO6 |
+ NETIF_F_RXCSUM;
netdev->features = NETIF_F_IP_CSUM |
NETIF_F_IPV6_CSUM |
NETIF_F_SG |
@@ -3040,8 +3000,9 @@
jme->reg_txpfc = 0;
jme->reg_pmcs = PMCS_MFEN;
jme->reg_gpreg1 = GPREG1_DEFAULT;
- set_bit(JME_FLAG_TXCSUM, &jme->flags);
- set_bit(JME_FLAG_TSO, &jme->flags);
+
+ if (jme->reg_rxmcs & RXMCS_CHECKSUM)
+ netdev->features |= NETIF_F_RXCSUM;
/*
* Get Max Read Req Size from PCI Config Space
diff --git a/drivers/net/jme.h b/drivers/net/jme.h
index 8bf3045..e9aaeca 100644
--- a/drivers/net/jme.h
+++ b/drivers/net/jme.h
@@ -468,8 +468,6 @@
enum jme_flags_bits {
JME_FLAG_MSI = 1,
JME_FLAG_SSET = 2,
- JME_FLAG_TXCSUM = 3,
- JME_FLAG_TSO = 4,
JME_FLAG_POLL = 5,
JME_FLAG_SHUTDOWN = 6,
};
diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c
index 506cfd0..1ad1f60 100644
--- a/drivers/net/mlx4/eq.c
+++ b/drivers/net/mlx4/eq.c
@@ -603,7 +603,9 @@
}
for (i = 0; i < dev->caps.num_comp_vectors; ++i) {
- err = mlx4_create_eq(dev, dev->caps.num_cqs + MLX4_NUM_SPARE_EQE,
+ err = mlx4_create_eq(dev, dev->caps.num_cqs -
+ dev->caps.reserved_cqs +
+ MLX4_NUM_SPARE_EQE,
(dev->flags & MLX4_FLAG_MSI_X) ? i : 0,
&priv->eq_table.eq[i]);
if (err) {
diff --git a/drivers/net/mlx4/mcg.c b/drivers/net/mlx4/mcg.c
index e71372a..c6d336a 100644
--- a/drivers/net/mlx4/mcg.c
+++ b/drivers/net/mlx4/mcg.c
@@ -111,7 +111,7 @@
u32 members_count;
struct mlx4_steer_index *new_entry;
struct mlx4_promisc_qp *pqp;
- struct mlx4_promisc_qp *dqp;
+ struct mlx4_promisc_qp *dqp = NULL;
u32 prot;
int err;
u8 pf_num;
@@ -184,7 +184,7 @@
out_alloc:
if (dqp) {
list_del(&dqp->list);
- kfree(&dqp);
+ kfree(dqp);
}
list_del(&new_entry->list);
kfree(new_entry);
@@ -469,7 +469,6 @@
/*remove from list of promisc qps */
list_del(&pqp->list);
- kfree(pqp);
/* set the default entry not to include the removed one */
mailbox = mlx4_alloc_cmd_mailbox(dev);
@@ -528,6 +527,8 @@
out_list:
if (back_to_list)
list_add_tail(&pqp->list, &s_steer->promisc_qps[steer]);
+ else
+ kfree(pqp);
out_mutex:
mutex_unlock(&priv->mcg_table.mutex);
return err;
diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c
index a4f2bd5..36045f3 100644
--- a/drivers/net/starfire.c
+++ b/drivers/net/starfire.c
@@ -144,11 +144,7 @@
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT (2 * HZ)
-/*
- * This SUCKS.
- * We need a much better method to determine if dma_addr_t is 64-bit.
- */
-#if (defined(__i386__) && defined(CONFIG_HIGHMEM64G)) || defined(__x86_64__) || defined (__ia64__) || defined(__alpha__) || (defined(CONFIG_MIPS) && ((defined(CONFIG_HIGHMEM) && defined(CONFIG_64BIT_PHYS_ADDR)) || defined(CONFIG_64BIT))) || (defined(__powerpc64__) || defined(CONFIG_PHYS_64BIT))
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
/* 64-bit dma_addr_t */
#define ADDR_64BITS /* This chip uses 64 bit addresses. */
#define netdrv_addr_t __le64
diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c
index c1a3448..a935426 100644
--- a/drivers/net/sungem.c
+++ b/drivers/net/sungem.c
@@ -3146,7 +3146,8 @@
gp->phy_mii.def ? gp->phy_mii.def->name : "no");
/* GEM can do it all... */
- dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_LLTX;
+ dev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM;
+ dev->features |= dev->hw_features | NETIF_F_RXCSUM | NETIF_F_LLTX;
if (pci_using_dac)
dev->features |= NETIF_F_HIGHDMA;
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index eb4f59f..80e907d 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -2788,7 +2788,8 @@
dev->ethtool_ops = &hme_ethtool_ops;
/* Happy Meal can do it all... */
- dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
+ dev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM;
+ dev->features |= dev->hw_features | NETIF_F_RXCSUM;
dev->irq = op->archdata.irqs[0];
@@ -3113,7 +3114,8 @@
dev->dma = 0;
/* Happy Meal can do it all... */
- dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
+ dev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM;
+ dev->features |= dev->hw_features | NETIF_F_RXCSUM;
#if defined(CONFIG_SBUS) && defined(CONFIG_PCI)
/* Hook up PCI register/descriptor accessors. */
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index b7e03a6..f944c6b 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -97,14 +97,12 @@
* them in the NIC onboard memory.
*/
#define TG3_RX_STD_RING_SIZE(tp) \
- ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || \
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719) ? \
- RX_STD_MAX_SIZE_5717 : 512)
+ ((tp->tg3_flags3 & TG3_FLG3_LRG_PROD_RING_CAP) ? \
+ TG3_RX_STD_MAX_SIZE_5717 : TG3_RX_STD_MAX_SIZE_5700)
#define TG3_DEF_RX_RING_PENDING 200
#define TG3_RX_JMB_RING_SIZE(tp) \
- ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 || \
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719) ? \
- 1024 : 256)
+ ((tp->tg3_flags3 & TG3_FLG3_LRG_PROD_RING_CAP) ? \
+ TG3_RX_JMB_MAX_SIZE_5717 : TG3_RX_JMB_MAX_SIZE_5700)
#define TG3_DEF_RX_JUMBO_RING_PENDING 100
#define TG3_RSS_INDIR_TBL_SIZE 128
@@ -266,6 +264,7 @@
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57791)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57795)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5719)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5720)},
{PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)},
{PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX)},
{PCI_DEVICE(PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000)},
@@ -1044,8 +1043,7 @@
u32 reg;
struct phy_device *phydev;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719) {
+ if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS) {
u32 is_serdes;
tp->phy_addr = PCI_FUNC(tp->pdev->devfn) + 1;
@@ -1623,8 +1621,7 @@
u32 reg;
if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS) ||
- ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719) &&
+ ((tp->tg3_flags3 & TG3_FLG3_5717_PLUS) &&
(tp->phy_flags & TG3_PHYFLG_MII_SERDES)))
return;
@@ -2047,8 +2044,7 @@
}
}
- if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719) &&
+ if ((tp->tg3_flags3 & TG3_FLG3_5717_PLUS) &&
(tp->phy_flags & TG3_PHYFLG_MII_SERDES))
return 0;
@@ -2130,7 +2126,8 @@
if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) &&
tp->pdev_peer != tp->pdev) {
struct net_device *dev_peer;
@@ -4394,6 +4391,7 @@
static int tg3_setup_phy(struct tg3 *tp, int force_reset)
{
+ u32 val;
int err;
if (tp->phy_flags & TG3_PHYFLG_PHY_SERDES)
@@ -4404,7 +4402,7 @@
err = tg3_setup_copper_phy(tp, force_reset);
if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5784_AX) {
- u32 val, scale;
+ u32 scale;
val = tr32(TG3_CPMU_CLCK_STAT) & CPMU_CLCK_STAT_MAC_CLCK_MASK;
if (val == CPMU_CLCK_STAT_MAC_CLCK_62_5)
@@ -4419,17 +4417,20 @@
tw32(GRC_MISC_CFG, val);
}
+ val = (2 << TX_LENGTHS_IPG_CRS_SHIFT) |
+ (6 << TX_LENGTHS_IPG_SHIFT);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
+ val |= tr32(MAC_TX_LENGTHS) &
+ (TX_LENGTHS_JMB_FRM_LEN_MSK |
+ TX_LENGTHS_CNT_DWN_VAL_MSK);
+
if (tp->link_config.active_speed == SPEED_1000 &&
tp->link_config.active_duplex == DUPLEX_HALF)
- tw32(MAC_TX_LENGTHS,
- ((2 << TX_LENGTHS_IPG_CRS_SHIFT) |
- (6 << TX_LENGTHS_IPG_SHIFT) |
- (0xff << TX_LENGTHS_SLOT_TIME_SHIFT)));
+ tw32(MAC_TX_LENGTHS, val |
+ (0xff << TX_LENGTHS_SLOT_TIME_SHIFT));
else
- tw32(MAC_TX_LENGTHS,
- ((2 << TX_LENGTHS_IPG_CRS_SHIFT) |
- (6 << TX_LENGTHS_IPG_SHIFT) |
- (32 << TX_LENGTHS_SLOT_TIME_SHIFT)));
+ tw32(MAC_TX_LENGTHS, val |
+ (32 << TX_LENGTHS_SLOT_TIME_SHIFT));
if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) {
if (netif_carrier_ok(tp->dev)) {
@@ -4441,7 +4442,7 @@
}
if (tp->tg3_flags & TG3_FLAG_ASPM_WORKAROUND) {
- u32 val = tr32(PCIE_PWR_MGMT_THRESH);
+ val = tr32(PCIE_PWR_MGMT_THRESH);
if (!netif_carrier_ok(tp->dev))
val = (val & ~PCIE_PWR_MGMT_L1_THRESH_MSK) |
tp->pwrmgmt_thresh;
@@ -7097,7 +7098,7 @@
if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) {
/* Force PCIe 1.0a mode */
if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785 &&
- !(tp->tg3_flags3 & TG3_FLG3_5717_PLUS) &&
+ !(tp->tg3_flags3 & TG3_FLG3_57765_PLUS) &&
tr32(TG3_PCIE_PHY_TSTCTL) ==
(TG3_PCIE_PHY_TSTCTL_PCIE10 | TG3_PCIE_PHY_TSTCTL_PSCRAM))
tw32(TG3_PCIE_PHY_TSTCTL, TG3_PCIE_PHY_TSTCTL_PSCRAM);
@@ -7250,12 +7251,17 @@
if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) &&
tp->pci_chip_rev_id != CHIPREV_ID_5750_A0 &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785 &&
- !(tp->tg3_flags3 & TG3_FLG3_5717_PLUS)) {
+ !(tp->tg3_flags3 & TG3_FLG3_57765_PLUS)) {
val = tr32(0x7c00);
tw32(0x7c00, val | (1 << 25));
}
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) {
+ val = tr32(TG3_CPMU_CLCK_ORIDE);
+ tw32(TG3_CPMU_CLCK_ORIDE, val & ~CPMU_CLCK_ORIDE_MAC_ORIDE_EN);
+ }
+
/* Reprobe ASF enable state. */
tp->tg3_flags &= ~TG3_FLAG_ENABLE_ASF;
tp->tg3_flags2 &= ~TG3_FLG2_ASF_NEW_HANDSHAKE;
@@ -7673,8 +7679,7 @@
/* Disable all transmit rings but the first. */
if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS))
limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 16;
- else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
+ else if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS)
limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 4;
else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 2;
@@ -7688,8 +7693,7 @@
/* Disable all receive return rings but the first. */
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
+ if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS)
limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 17;
else if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS))
limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 16;
@@ -7960,7 +7964,7 @@
if (err)
return err;
- if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS) {
+ if (tp->tg3_flags3 & TG3_FLG3_57765_PLUS) {
val = tr32(TG3PCI_DMA_RW_CTRL) &
~DMA_RWCTRL_DIS_CACHE_ALIGNMENT;
if (tp->pci_chip_rev_id == CHIPREV_ID_57765_A0)
@@ -8091,8 +8095,7 @@
((u64) tpr->rx_std_mapping >> 32));
tw32(RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW,
((u64) tpr->rx_std_mapping & 0xffffffff));
- if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5719)
+ if (!(tp->tg3_flags3 & TG3_FLG3_5717_PLUS))
tw32(RCVDBDI_STD_BD + TG3_BDINFO_NIC_ADDR,
NIC_SRAM_RX_BUFFER_DESC);
@@ -8115,9 +8118,10 @@
((u64) tpr->rx_jmb_mapping >> 32));
tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW,
((u64) tpr->rx_jmb_mapping & 0xffffffff));
+ val = TG3_RX_JMB_RING_SIZE(tp) <<
+ BDINFO_FLAGS_MAXLEN_SHIFT;
tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS,
- (RX_JUMBO_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT) |
- BDINFO_FLAGS_USE_EXT_RECV);
+ val | BDINFO_FLAGS_USE_EXT_RECV);
if (!(tp->tg3_flags3 & TG3_FLG3_USE_JUMBO_BDFLAG) ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_NIC_ADDR,
@@ -8127,17 +8131,17 @@
BDINFO_FLAGS_DISABLED);
}
- if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS) {
+ if (tp->tg3_flags3 & TG3_FLG3_57765_PLUS) {
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
- val = RX_STD_MAX_SIZE_5705;
+ val = TG3_RX_STD_MAX_SIZE_5700;
else
- val = RX_STD_MAX_SIZE_5717;
+ val = TG3_RX_STD_MAX_SIZE_5717;
val <<= BDINFO_FLAGS_MAXLEN_SHIFT;
val |= (TG3_RX_STD_DMA_SZ << 2);
} else
val = TG3_RX_STD_DMA_SZ << BDINFO_FLAGS_MAXLEN_SHIFT;
} else
- val = RX_STD_MAX_SIZE_5705 << BDINFO_FLAGS_MAXLEN_SHIFT;
+ val = TG3_RX_STD_MAX_SIZE_5700 << BDINFO_FLAGS_MAXLEN_SHIFT;
tw32(RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS, val);
@@ -8148,7 +8152,7 @@
tp->rx_jumbo_pending : 0;
tw32_rx_mbox(TG3_RX_JMB_PROD_IDX_REG, tpr->rx_jmb_prod_idx);
- if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS) {
+ if (tp->tg3_flags3 & TG3_FLG3_57765_PLUS) {
tw32(STD_REPLENISH_LWM, 32);
tw32(JMB_REPLENISH_LWM, 16);
}
@@ -8165,10 +8169,16 @@
/* The slot time is changed by tg3_setup_phy if we
* run at gigabit with half duplex.
*/
- tw32(MAC_TX_LENGTHS,
- (2 << TX_LENGTHS_IPG_CRS_SHIFT) |
- (6 << TX_LENGTHS_IPG_SHIFT) |
- (32 << TX_LENGTHS_SLOT_TIME_SHIFT));
+ val = (2 << TX_LENGTHS_IPG_CRS_SHIFT) |
+ (6 << TX_LENGTHS_IPG_SHIFT) |
+ (32 << TX_LENGTHS_SLOT_TIME_SHIFT);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
+ val |= tr32(MAC_TX_LENGTHS) &
+ (TX_LENGTHS_JMB_FRM_LEN_MSK |
+ TX_LENGTHS_CNT_DWN_VAL_MSK);
+
+ tw32(MAC_TX_LENGTHS, val);
/* Receive rules. */
tw32(MAC_RCV_RULE_CFG, RCV_RULE_CFG_DEFAULT_CLASS);
@@ -8215,13 +8225,17 @@
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
rdmac_mode |= RDMAC_MODE_IPV6_LSO_EN;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
+ rdmac_mode |= tr32(RDMAC_MODE) & RDMAC_MODE_H2BNC_VLAN_DET;
+
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 ||
- (tp->tg3_flags3 & TG3_FLG3_5717_PLUS)) {
+ (tp->tg3_flags3 & TG3_FLG3_57765_PLUS)) {
val = tr32(TG3_RDMA_RSRVCTRL_REG);
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719) {
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) {
val &= ~(TG3_RDMA_RSRVCTRL_TXMRGN_MASK |
TG3_RDMA_RSRVCTRL_FIFO_LWM_MASK |
TG3_RDMA_RSRVCTRL_FIFO_HWM_MASK);
@@ -8233,7 +8247,8 @@
val | TG3_RDMA_RSRVCTRL_FIFO_OFLW_FIX);
}
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719) {
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) {
val = tr32(TG3_LSO_RD_DMA_CRPTEN_CTRL);
tw32(TG3_LSO_RD_DMA_CRPTEN_CTRL, val |
TG3_LSO_RD_DMA_CRPTEN_CTRL_BLEN_BD_4K |
@@ -8421,8 +8436,7 @@
tw32(SNDBDC_MODE, SNDBDC_MODE_ENABLE | SNDBDC_MODE_ATTN_ENABLE);
tw32(RCVBDI_MODE, RCVBDI_MODE_ENABLE | RCVBDI_MODE_RCB_ATTN_ENAB);
val = RCVDBDI_MODE_ENABLE | RCVDBDI_MODE_INV_RING_SZ;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
+ if (tp->tg3_flags3 & TG3_FLG3_LRG_PROD_RING_CAP)
val |= RCVDBDI_MODE_LRG_RING_SZ;
tw32(RCVDBDI_MODE, val);
tw32(SNDDATAI_MODE, SNDDATAI_MODE_ENABLE);
@@ -8447,9 +8461,17 @@
}
tp->tx_mode = TX_MODE_ENABLE;
+
if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
tp->tx_mode |= TX_MODE_MBUF_LOCKUP_FIX;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) {
+ val = TX_MODE_JMB_FRM_LEN | TX_MODE_CNT_DN_MODE;
+ tp->tx_mode &= ~val;
+ tp->tx_mode |= tr32(MAC_TX_MODE) & val;
+ }
+
tw32_f(MAC_TX_MODE, tp->tx_mode);
udelay(100);
@@ -8868,7 +8890,7 @@
* Turn off MSI one shot mode. Otherwise this test has no
* observable way to know whether the interrupt was delivered.
*/
- if ((tp->tg3_flags3 & TG3_FLG3_5717_PLUS) &&
+ if ((tp->tg3_flags3 & TG3_FLG3_57765_PLUS) &&
(tp->tg3_flags2 & TG3_FLG2_USING_MSI)) {
val = tr32(MSGINT_MODE) | MSGINT_MODE_ONE_SHOT_DISABLE;
tw32(MSGINT_MODE, val);
@@ -8911,7 +8933,7 @@
if (intr_ok) {
/* Reenable MSI one shot mode. */
- if ((tp->tg3_flags3 & TG3_FLG3_5717_PLUS) &&
+ if ((tp->tg3_flags3 & TG3_FLG3_57765_PLUS) &&
(tp->tg3_flags2 & TG3_FLG2_USING_MSI)) {
val = tr32(MSGINT_MODE) & ~MSGINT_MODE_ONE_SHOT_DISABLE;
tw32(MSGINT_MODE, val);
@@ -9058,7 +9080,9 @@
if (tp->irq_cnt > 1) {
tp->tg3_flags3 |= TG3_FLG3_ENABLE_RSS;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719) {
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) {
tp->tg3_flags3 |= TG3_FLG3_ENABLE_TSS;
netif_set_real_num_tx_queues(tp->dev, tp->irq_cnt - 1);
}
@@ -9214,7 +9238,7 @@
goto err_out2;
}
- if (!(tp->tg3_flags3 & TG3_FLG3_5717_PLUS) &&
+ if (!(tp->tg3_flags3 & TG3_FLG3_57765_PLUS) &&
(tp->tg3_flags2 & TG3_FLG2_USING_MSI)) {
u32 val = tr32(PCIE_TRANSACTION_CFG);
@@ -10850,8 +10874,7 @@
int err = 0;
int i;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
+ if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS)
mem_tbl = mem_tbl_5717;
else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
mem_tbl = mem_tbl_57765;
@@ -11843,6 +11866,8 @@
switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) {
case FLASH_5717VENDOR_ATMEL_MDB021D:
+ /* Detect size with tg3_nvram_get_size() */
+ break;
case FLASH_5717VENDOR_ATMEL_ADB021B:
case FLASH_5717VENDOR_ATMEL_ADB021D:
tp->nvram_size = TG3_NVRAM_SIZE_256KB;
@@ -11868,8 +11893,10 @@
switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) {
case FLASH_5717VENDOR_ST_M_M25PE20:
- case FLASH_5717VENDOR_ST_A_M25PE20:
case FLASH_5717VENDOR_ST_M_M45PE20:
+ /* Detect size with tg3_nvram_get_size() */
+ break;
+ case FLASH_5717VENDOR_ST_A_M25PE20:
case FLASH_5717VENDOR_ST_A_M45PE20:
tp->nvram_size = TG3_NVRAM_SIZE_256KB;
break;
@@ -11888,6 +11915,118 @@
tp->tg3_flags3 |= TG3_FLG3_NO_NVRAM_ADDR_TRANS;
}
+static void __devinit tg3_get_5720_nvram_info(struct tg3 *tp)
+{
+ u32 nvcfg1, nvmpinstrp;
+
+ nvcfg1 = tr32(NVRAM_CFG1);
+ nvmpinstrp = nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK;
+
+ switch (nvmpinstrp) {
+ case FLASH_5720_EEPROM_HD:
+ case FLASH_5720_EEPROM_LD:
+ tp->nvram_jedecnum = JEDEC_ATMEL;
+ tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED;
+
+ nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS;
+ tw32(NVRAM_CFG1, nvcfg1);
+ if (nvmpinstrp == FLASH_5720_EEPROM_HD)
+ tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE;
+ else
+ tp->nvram_pagesize = ATMEL_AT24C02_CHIP_SIZE;
+ return;
+ case FLASH_5720VENDOR_M_ATMEL_DB011D:
+ case FLASH_5720VENDOR_A_ATMEL_DB011B:
+ case FLASH_5720VENDOR_A_ATMEL_DB011D:
+ case FLASH_5720VENDOR_M_ATMEL_DB021D:
+ case FLASH_5720VENDOR_A_ATMEL_DB021B:
+ case FLASH_5720VENDOR_A_ATMEL_DB021D:
+ case FLASH_5720VENDOR_M_ATMEL_DB041D:
+ case FLASH_5720VENDOR_A_ATMEL_DB041B:
+ case FLASH_5720VENDOR_A_ATMEL_DB041D:
+ case FLASH_5720VENDOR_M_ATMEL_DB081D:
+ case FLASH_5720VENDOR_A_ATMEL_DB081D:
+ case FLASH_5720VENDOR_ATMEL_45USPT:
+ tp->nvram_jedecnum = JEDEC_ATMEL;
+ tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED;
+ tp->tg3_flags2 |= TG3_FLG2_FLASH;
+
+ switch (nvmpinstrp) {
+ case FLASH_5720VENDOR_M_ATMEL_DB021D:
+ case FLASH_5720VENDOR_A_ATMEL_DB021B:
+ case FLASH_5720VENDOR_A_ATMEL_DB021D:
+ tp->nvram_size = TG3_NVRAM_SIZE_256KB;
+ break;
+ case FLASH_5720VENDOR_M_ATMEL_DB041D:
+ case FLASH_5720VENDOR_A_ATMEL_DB041B:
+ case FLASH_5720VENDOR_A_ATMEL_DB041D:
+ tp->nvram_size = TG3_NVRAM_SIZE_512KB;
+ break;
+ case FLASH_5720VENDOR_M_ATMEL_DB081D:
+ case FLASH_5720VENDOR_A_ATMEL_DB081D:
+ tp->nvram_size = TG3_NVRAM_SIZE_1MB;
+ break;
+ default:
+ tp->nvram_size = TG3_NVRAM_SIZE_128KB;
+ break;
+ }
+ break;
+ case FLASH_5720VENDOR_M_ST_M25PE10:
+ case FLASH_5720VENDOR_M_ST_M45PE10:
+ case FLASH_5720VENDOR_A_ST_M25PE10:
+ case FLASH_5720VENDOR_A_ST_M45PE10:
+ case FLASH_5720VENDOR_M_ST_M25PE20:
+ case FLASH_5720VENDOR_M_ST_M45PE20:
+ case FLASH_5720VENDOR_A_ST_M25PE20:
+ case FLASH_5720VENDOR_A_ST_M45PE20:
+ case FLASH_5720VENDOR_M_ST_M25PE40:
+ case FLASH_5720VENDOR_M_ST_M45PE40:
+ case FLASH_5720VENDOR_A_ST_M25PE40:
+ case FLASH_5720VENDOR_A_ST_M45PE40:
+ case FLASH_5720VENDOR_M_ST_M25PE80:
+ case FLASH_5720VENDOR_M_ST_M45PE80:
+ case FLASH_5720VENDOR_A_ST_M25PE80:
+ case FLASH_5720VENDOR_A_ST_M45PE80:
+ case FLASH_5720VENDOR_ST_25USPT:
+ case FLASH_5720VENDOR_ST_45USPT:
+ tp->nvram_jedecnum = JEDEC_ST;
+ tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED;
+ tp->tg3_flags2 |= TG3_FLG2_FLASH;
+
+ switch (nvmpinstrp) {
+ case FLASH_5720VENDOR_M_ST_M25PE20:
+ case FLASH_5720VENDOR_M_ST_M45PE20:
+ case FLASH_5720VENDOR_A_ST_M25PE20:
+ case FLASH_5720VENDOR_A_ST_M45PE20:
+ tp->nvram_size = TG3_NVRAM_SIZE_256KB;
+ break;
+ case FLASH_5720VENDOR_M_ST_M25PE40:
+ case FLASH_5720VENDOR_M_ST_M45PE40:
+ case FLASH_5720VENDOR_A_ST_M25PE40:
+ case FLASH_5720VENDOR_A_ST_M45PE40:
+ tp->nvram_size = TG3_NVRAM_SIZE_512KB;
+ break;
+ case FLASH_5720VENDOR_M_ST_M25PE80:
+ case FLASH_5720VENDOR_M_ST_M45PE80:
+ case FLASH_5720VENDOR_A_ST_M25PE80:
+ case FLASH_5720VENDOR_A_ST_M45PE80:
+ tp->nvram_size = TG3_NVRAM_SIZE_1MB;
+ break;
+ default:
+ tp->nvram_size = TG3_NVRAM_SIZE_128KB;
+ break;
+ }
+ break;
+ default:
+ tp->tg3_flags3 |= TG3_FLG3_NO_NVRAM;
+ return;
+ }
+
+ tg3_nvram_get_pagesize(tp, nvcfg1);
+ if (tp->nvram_pagesize != 264 && tp->nvram_pagesize != 528)
+ tp->tg3_flags3 |= TG3_FLG3_NO_NVRAM_ADDR_TRANS;
+}
+
/* Chips other than 5700/5701 use the NVRAM for fetching info. */
static void __devinit tg3_nvram_init(struct tg3 *tp)
{
@@ -11935,6 +12074,8 @@
else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
tg3_get_5717_nvram_info(tp);
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
+ tg3_get_5720_nvram_info(tp);
else
tg3_get_nvram_info(tp);
@@ -12472,7 +12613,7 @@
if (cfg2 & (1 << 18))
tp->phy_flags |= TG3_PHYFLG_SERDES_PREEMPHASIS;
- if (((tp->tg3_flags3 & TG3_FLG3_5717_PLUS) ||
+ if (((tp->tg3_flags3 & TG3_FLG3_57765_PLUS) ||
((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 &&
GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX))) &&
(cfg2 & NIC_SRAM_DATA_CFG_2_APD_EN))
@@ -12480,7 +12621,7 @@
if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785 &&
- !(tp->tg3_flags3 & TG3_FLG3_5717_PLUS)) {
+ !(tp->tg3_flags3 & TG3_FLG3_57765_PLUS)) {
u32 cfg3;
tg3_read_mem(tp, NIC_SRAM_DATA_CFG_3, &cfg3);
@@ -13125,14 +13266,13 @@
static inline u32 tg3_rx_ret_ring_size(struct tg3 *tp)
{
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
- return 4096;
+ if (tp->tg3_flags3 & TG3_FLG3_LRG_PROD_RING_CAP)
+ return TG3_RX_RET_MAX_SIZE_5717;
else if ((tp->tg3_flags & TG3_FLAG_JUMBO_CAPABLE) &&
!(tp->tg3_flags2 & TG3_FLG2_5780_CLASS))
- return 1024;
+ return TG3_RX_RET_MAX_SIZE_5700;
else
- return 512;
+ return TG3_RX_RET_MAX_SIZE_5705;
}
static DEFINE_PCI_DEVICE_TABLE(tg3_write_reorder_chipsets) = {
@@ -13177,7 +13317,8 @@
if (tp->pdev->device == TG3PCI_DEVICE_TIGON3_5717 ||
tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718 ||
- tp->pdev->device == TG3PCI_DEVICE_TIGON3_5719)
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_5719 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_5720)
pci_read_config_dword(tp->pdev,
TG3PCI_GEN2_PRODID_ASICREV,
&prod_id_asic_rev);
@@ -13332,14 +13473,19 @@
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717)
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
tp->pdev_peer = tg3_find_peer(tp);
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
tp->tg3_flags3 |= TG3_FLG3_5717_PLUS;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765 ||
+ (tp->tg3_flags3 & TG3_FLG3_5717_PLUS))
+ tp->tg3_flags3 |= TG3_FLG3_57765_PLUS;
+
/* Intentionally exclude ASIC_REV_5906 */
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
@@ -13347,7 +13493,7 @@
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 ||
- (tp->tg3_flags3 & TG3_FLG3_5717_PLUS))
+ (tp->tg3_flags3 & TG3_FLG3_57765_PLUS))
tp->tg3_flags3 |= TG3_FLG3_5755_PLUS;
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 ||
@@ -13379,7 +13525,7 @@
/* Determine TSO capabilities */
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
; /* Do nothing. HW bug. */
- else if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS)
+ else if (tp->tg3_flags3 & TG3_FLG3_57765_PLUS)
tp->tg3_flags2 |= TG3_FLG2_HW_TSO_3;
else if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
@@ -13415,7 +13561,7 @@
tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI;
}
- if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS) {
+ if (tp->tg3_flags3 & TG3_FLG3_57765_PLUS) {
tp->tg3_flags |= TG3_FLAG_SUPPORT_MSIX;
tp->irq_max = TG3_IRQ_MAX_VECS;
}
@@ -13430,7 +13576,10 @@
tp->tg3_flags3 |= TG3_FLG3_40BIT_DMA_LIMIT_BUG;
}
- if ((tp->tg3_flags3 & TG3_FLG3_5717_PLUS) &&
+ if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS)
+ tp->tg3_flags3 |= TG3_FLG3_LRG_PROD_RING_CAP;
+
+ if ((tp->tg3_flags3 & TG3_FLG3_57765_PLUS) &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5719)
tp->tg3_flags3 |= TG3_FLG3_USE_JUMBO_BDFLAG;
@@ -13449,7 +13598,8 @@
tp->tg3_flags2 |= TG3_FLG2_PCI_EXPRESS;
tp->pcie_readrq = 4096;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
tp->pcie_readrq = 2048;
pcie_set_readrq(tp->pdev, tp->pcie_readrq);
@@ -13636,7 +13786,7 @@
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 ||
- (tp->tg3_flags3 & TG3_FLG3_5717_PLUS))
+ (tp->tg3_flags3 & TG3_FLG3_57765_PLUS))
tp->tg3_flags |= TG3_FLAG_CPMU_PRESENT;
/* Set up tp->grc_local_ctrl before calling tg_power_up().
@@ -13715,7 +13865,7 @@
!(tp->phy_flags & TG3_PHYFLG_IS_FET) &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785 &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57780 &&
- !(tp->tg3_flags3 & TG3_FLG3_5717_PLUS)) {
+ !(tp->tg3_flags3 & TG3_FLG3_57765_PLUS)) {
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
@@ -13756,7 +13906,15 @@
/* Initialize data/descriptor byte/word swapping. */
val = tr32(GRC_MODE);
- val &= GRC_MODE_HOST_STACKUP;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720)
+ val &= (GRC_MODE_BYTE_SWAP_B2HRX_DATA |
+ GRC_MODE_WORD_SWAP_B2HRX_DATA |
+ GRC_MODE_B2HRX_ENABLE |
+ GRC_MODE_HTX2B_ENABLE |
+ GRC_MODE_HOST_STACKUP);
+ else
+ val &= GRC_MODE_HOST_STACKUP;
+
tw32(GRC_MODE, val | tp->grc_mode);
tg3_switch_clocks(tp);
@@ -13961,8 +14119,7 @@
tw32_f(NVRAM_CMD, NVRAM_CMD_RESET);
else
tg3_nvram_unlock(tp);
- } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719) {
+ } else if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS) {
if (PCI_FUNC(tp->pdev->devfn) & 1)
mac_offset = 0xcc;
if (PCI_FUNC(tp->pdev->devfn) > 1)
@@ -14051,7 +14208,7 @@
#endif
#endif
- if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS) {
+ if (tp->tg3_flags3 & TG3_FLG3_57765_PLUS) {
val = goal ? 0 : DMA_RWCTRL_DIS_CACHE_ALIGNMENT;
goto out;
}
@@ -14268,7 +14425,7 @@
tp->dma_rwctrl = tg3_calc_dma_bndry(tp, tp->dma_rwctrl);
- if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS)
+ if (tp->tg3_flags3 & TG3_FLG3_57765_PLUS)
goto out;
if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) {
@@ -14443,7 +14600,7 @@
static void __devinit tg3_init_bufmgr_config(struct tg3 *tp)
{
- if (tp->tg3_flags3 & TG3_FLG3_5717_PLUS) {
+ if (tp->tg3_flags3 & TG3_FLG3_57765_PLUS) {
tp->bufmgr_config.mbuf_read_dma_low_water =
DEFAULT_MB_RDMA_LOW_WATER_5705;
tp->bufmgr_config.mbuf_mac_rx_low_water =
@@ -14521,6 +14678,7 @@
case TG3_PHY_ID_BCM5718S: return "5718S";
case TG3_PHY_ID_BCM57765: return "57765";
case TG3_PHY_ID_BCM5719C: return "5719C";
+ case TG3_PHY_ID_BCM5720C: return "5720C";
case TG3_PHY_ID_BCM8002: return "8002/serdes";
case 0: return "serdes";
default: return "unknown";
@@ -14759,8 +14917,7 @@
}
if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5719)
+ !(tp->tg3_flags3 & TG3_FLG3_5717_PLUS))
dev->netdev_ops = &tg3_netdev_ops;
else
dev->netdev_ops = &tg3_netdev_ops_dma_bug;
diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h
index 73884b6..e7880d5 100644
--- a/drivers/net/tg3.h
+++ b/drivers/net/tg3.h
@@ -25,9 +25,13 @@
#define TG3_RX_INTERNAL_RING_SZ_5906 32
-#define RX_STD_MAX_SIZE_5705 512
-#define RX_STD_MAX_SIZE_5717 2048
-#define RX_JUMBO_MAX_SIZE 0xdeadbeef /* XXX */
+#define TG3_RX_STD_MAX_SIZE_5700 512
+#define TG3_RX_STD_MAX_SIZE_5717 2048
+#define TG3_RX_JMB_MAX_SIZE_5700 256
+#define TG3_RX_JMB_MAX_SIZE_5717 1024
+#define TG3_RX_RET_MAX_SIZE_5700 1024
+#define TG3_RX_RET_MAX_SIZE_5705 512
+#define TG3_RX_RET_MAX_SIZE_5717 4096
/* First 256 bytes are a mirror of PCI config space. */
#define TG3PCI_VENDOR 0x00000000
@@ -54,6 +58,7 @@
#define TG3PCI_DEVICE_TIGON3_57791 0x16b2
#define TG3PCI_DEVICE_TIGON3_57795 0x16b6
#define TG3PCI_DEVICE_TIGON3_5719 0x1657
+#define TG3PCI_DEVICE_TIGON3_5720 0x165f
/* 0x04 --> 0x2c unused */
#define TG3PCI_SUBVENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM
#define TG3PCI_SUBDEVICE_ID_BROADCOM_95700A6 0x1644
@@ -163,6 +168,7 @@
#define ASIC_REV_5717 0x5717
#define ASIC_REV_57765 0x57785
#define ASIC_REV_5719 0x5719
+#define ASIC_REV_5720 0x5720
#define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8)
#define CHIPREV_5700_AX 0x70
#define CHIPREV_5700_BX 0x71
@@ -473,6 +479,8 @@
#define TX_MODE_BIG_BCKOFF_ENABLE 0x00000020
#define TX_MODE_LONG_PAUSE_ENABLE 0x00000040
#define TX_MODE_MBUF_LOCKUP_FIX 0x00000100
+#define TX_MODE_JMB_FRM_LEN 0x00400000
+#define TX_MODE_CNT_DN_MODE 0x00800000
#define MAC_TX_STATUS 0x00000460
#define TX_STATUS_XOFFED 0x00000001
#define TX_STATUS_SENT_XOFF 0x00000002
@@ -487,6 +495,8 @@
#define TX_LENGTHS_IPG_SHIFT 8
#define TX_LENGTHS_IPG_CRS_MASK 0x00003000
#define TX_LENGTHS_IPG_CRS_SHIFT 12
+#define TX_LENGTHS_JMB_FRM_LEN_MSK 0x00ff0000
+#define TX_LENGTHS_CNT_DWN_VAL_MSK 0xff000000
#define MAC_RX_MODE 0x00000468
#define RX_MODE_RESET 0x00000001
#define RX_MODE_ENABLE 0x00000002
@@ -1079,6 +1089,9 @@
#define CPMU_HST_ACC_MACCLK_6_25 0x00130000
/* 0x3620 --> 0x3630 unused */
+#define TG3_CPMU_CLCK_ORIDE 0x00003624
+#define CPMU_CLCK_ORIDE_MAC_ORIDE_EN 0x80000000
+
#define TG3_CPMU_CLCK_STAT 0x00003630
#define CPMU_CLCK_STAT_MAC_CLCK_MASK 0x001f0000
#define CPMU_CLCK_STAT_MAC_CLCK_62_5 0x00000000
@@ -1321,6 +1334,7 @@
#define RDMAC_MODE_MULT_DMA_RD_DIS 0x01000000
#define RDMAC_MODE_IPV4_LSO_EN 0x08000000
#define RDMAC_MODE_IPV6_LSO_EN 0x10000000
+#define RDMAC_MODE_H2BNC_VLAN_DET 0x20000000
#define RDMAC_STATUS 0x00004804
#define RDMAC_STATUS_TGTABORT 0x00000004
#define RDMAC_STATUS_MSTABORT 0x00000008
@@ -1613,6 +1627,8 @@
#define GRC_MODE_WSWAP_NONFRM_DATA 0x00000004
#define GRC_MODE_BSWAP_DATA 0x00000010
#define GRC_MODE_WSWAP_DATA 0x00000020
+#define GRC_MODE_BYTE_SWAP_B2HRX_DATA 0x00000040
+#define GRC_MODE_WORD_SWAP_B2HRX_DATA 0x00000080
#define GRC_MODE_SPLITHDR 0x00000100
#define GRC_MODE_NOFRM_CRACKING 0x00000200
#define GRC_MODE_INCL_CRC 0x00000400
@@ -1620,8 +1636,10 @@
#define GRC_MODE_NOIRQ_ON_SENDS 0x00002000
#define GRC_MODE_NOIRQ_ON_RCV 0x00004000
#define GRC_MODE_FORCE_PCI32BIT 0x00008000
+#define GRC_MODE_B2HRX_ENABLE 0x00008000
#define GRC_MODE_HOST_STACKUP 0x00010000
#define GRC_MODE_HOST_SENDBDS 0x00020000
+#define GRC_MODE_HTX2B_ENABLE 0x00040000
#define GRC_MODE_NO_TX_PHDR_CSUM 0x00100000
#define GRC_MODE_NVRAM_WR_ENABLE 0x00200000
#define GRC_MODE_PCIE_TL_SEL 0x00000000
@@ -1818,6 +1836,38 @@
#define FLASH_5717VENDOR_ATMEL_45USPT 0x03400000
#define FLASH_5717VENDOR_ST_25USPT 0x03400002
#define FLASH_5717VENDOR_ST_45USPT 0x03400001
+#define FLASH_5720_EEPROM_HD 0x00000001
+#define FLASH_5720_EEPROM_LD 0x00000003
+#define FLASH_5720VENDOR_M_ATMEL_DB011D 0x01000000
+#define FLASH_5720VENDOR_M_ATMEL_DB021D 0x01000002
+#define FLASH_5720VENDOR_M_ATMEL_DB041D 0x01000001
+#define FLASH_5720VENDOR_M_ATMEL_DB081D 0x01000003
+#define FLASH_5720VENDOR_M_ST_M25PE10 0x02000000
+#define FLASH_5720VENDOR_M_ST_M25PE20 0x02000002
+#define FLASH_5720VENDOR_M_ST_M25PE40 0x02000001
+#define FLASH_5720VENDOR_M_ST_M25PE80 0x02000003
+#define FLASH_5720VENDOR_M_ST_M45PE10 0x03000000
+#define FLASH_5720VENDOR_M_ST_M45PE20 0x03000002
+#define FLASH_5720VENDOR_M_ST_M45PE40 0x03000001
+#define FLASH_5720VENDOR_M_ST_M45PE80 0x03000003
+#define FLASH_5720VENDOR_A_ATMEL_DB011B 0x01800000
+#define FLASH_5720VENDOR_A_ATMEL_DB021B 0x01800002
+#define FLASH_5720VENDOR_A_ATMEL_DB041B 0x01800001
+#define FLASH_5720VENDOR_A_ATMEL_DB011D 0x01c00000
+#define FLASH_5720VENDOR_A_ATMEL_DB021D 0x01c00002
+#define FLASH_5720VENDOR_A_ATMEL_DB041D 0x01c00001
+#define FLASH_5720VENDOR_A_ATMEL_DB081D 0x01c00003
+#define FLASH_5720VENDOR_A_ST_M25PE10 0x02800000
+#define FLASH_5720VENDOR_A_ST_M25PE20 0x02800002
+#define FLASH_5720VENDOR_A_ST_M25PE40 0x02800001
+#define FLASH_5720VENDOR_A_ST_M25PE80 0x02800003
+#define FLASH_5720VENDOR_A_ST_M45PE10 0x02c00000
+#define FLASH_5720VENDOR_A_ST_M45PE20 0x02c00002
+#define FLASH_5720VENDOR_A_ST_M45PE40 0x02c00001
+#define FLASH_5720VENDOR_A_ST_M45PE80 0x02c00003
+#define FLASH_5720VENDOR_ATMEL_45USPT 0x03c00000
+#define FLASH_5720VENDOR_ST_25USPT 0x03c00002
+#define FLASH_5720VENDOR_ST_45USPT 0x03c00001
#define NVRAM_CFG1_5752PAGE_SIZE_MASK 0x70000000
#define FLASH_5752PAGE_SIZE_256 0x00000000
#define FLASH_5752PAGE_SIZE_512 0x10000000
@@ -2897,6 +2947,7 @@
#define TG3_FLG3_5701_DMA_BUG 0x00000008
#define TG3_FLG3_USE_PHYLIB 0x00000010
#define TG3_FLG3_MDIOBUS_INITED 0x00000020
+#define TG3_FLG3_LRG_PROD_RING_CAP 0x00000080
#define TG3_FLG3_RGMII_INBAND_DISABLE 0x00000100
#define TG3_FLG3_RGMII_EXT_IBND_RX_EN 0x00000200
#define TG3_FLG3_RGMII_EXT_IBND_TX_EN 0x00000400
@@ -2910,8 +2961,9 @@
#define TG3_FLG3_SHORT_DMA_BUG 0x00200000
#define TG3_FLG3_USE_JUMBO_BDFLAG 0x00400000
#define TG3_FLG3_L1PLLPD_EN 0x00800000
-#define TG3_FLG3_5717_PLUS 0x01000000
+#define TG3_FLG3_57765_PLUS 0x01000000
#define TG3_FLG3_APE_HAS_NCSI 0x02000000
+#define TG3_FLG3_5717_PLUS 0x04000000
struct timer_list timer;
u16 timer_counter;
@@ -2983,6 +3035,7 @@
#define TG3_PHY_ID_BCM5718S 0xbc050ff0
#define TG3_PHY_ID_BCM57765 0x5c0d8a40
#define TG3_PHY_ID_BCM5719C 0x5c0d8a20
+#define TG3_PHY_ID_BCM5720C 0x5c0d8b60
#define TG3_PHY_ID_BCM5906 0xdc00ac40
#define TG3_PHY_ID_BCM8002 0x60010140
#define TG3_PHY_ID_INVALID 0xffffffff
@@ -3049,6 +3102,7 @@
int nvram_lock_cnt;
u32 nvram_size;
+#define TG3_NVRAM_SIZE_2KB 0x00000800
#define TG3_NVRAM_SIZE_64KB 0x00010000
#define TG3_NVRAM_SIZE_128KB 0x00020000
#define TG3_NVRAM_SIZE_256KB 0x00040000
@@ -3064,6 +3118,9 @@
#define JEDEC_SAIFUN 0x4f
#define JEDEC_SST 0xbf
+#define ATMEL_AT24C02_CHIP_SIZE TG3_NVRAM_SIZE_2KB
+#define ATMEL_AT24C02_PAGE_SIZE (8)
+
#define ATMEL_AT24C64_CHIP_SIZE TG3_NVRAM_SIZE_64KB
#define ATMEL_AT24C64_PAGE_SIZE (32)
diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c
index 5f3b976..8f12854 100644
--- a/drivers/net/usb/cdc_eem.c
+++ b/drivers/net/usb/cdc_eem.c
@@ -340,7 +340,7 @@
static const struct driver_info eem_info = {
.description = "CDC EEM Device",
- .flags = FLAG_ETHER,
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT,
.bind = eem_bind,
.rx_fixup = eem_rx_fixup,
.tx_fixup = eem_tx_fixup,
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 51c259b..341f705 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -452,7 +452,7 @@
static const struct driver_info cdc_info = {
.description = "CDC Ethernet Device",
- .flags = FLAG_ETHER,
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT,
// .check_connect = cdc_check_connect,
.bind = usbnet_cdc_bind,
.unbind = usbnet_cdc_unbind,
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 7113168..967371f 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -1237,7 +1237,7 @@
static const struct driver_info cdc_ncm_info = {
.description = "CDC NCM",
- .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET,
+ .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET,
.bind = cdc_ncm_bind,
.unbind = cdc_ncm_unbind,
.check_connect = cdc_ncm_check_connect,
diff --git a/drivers/net/usb/cdc_subset.c b/drivers/net/usb/cdc_subset.c
index ca39ace..fc5f13d 100644
--- a/drivers/net/usb/cdc_subset.c
+++ b/drivers/net/usb/cdc_subset.c
@@ -89,6 +89,7 @@
static const struct driver_info ali_m5632_info = {
.description = "ALi M5632",
+ .flags = FLAG_POINTTOPOINT,
};
#endif
@@ -110,6 +111,7 @@
static const struct driver_info an2720_info = {
.description = "AnchorChips/Cypress 2720",
+ .flags = FLAG_POINTTOPOINT,
// no reset available!
// no check_connect available!
@@ -132,6 +134,7 @@
static const struct driver_info belkin_info = {
.description = "Belkin, eTEK, or compatible",
+ .flags = FLAG_POINTTOPOINT,
};
#endif /* CONFIG_USB_BELKIN */
@@ -157,6 +160,7 @@
static const struct driver_info epson2888_info = {
.description = "Epson USB Device",
.check_connect = always_connected,
+ .flags = FLAG_POINTTOPOINT,
.in = 4, .out = 3,
};
@@ -173,6 +177,7 @@
#define HAVE_HARDWARE
static const struct driver_info kc2190_info = {
.description = "KC Technology KC-190",
+ .flags = FLAG_POINTTOPOINT,
};
#endif /* CONFIG_USB_KC2190 */
@@ -200,16 +205,19 @@
static const struct driver_info linuxdev_info = {
.description = "Linux Device",
.check_connect = always_connected,
+ .flags = FLAG_POINTTOPOINT,
};
static const struct driver_info yopy_info = {
.description = "Yopy",
.check_connect = always_connected,
+ .flags = FLAG_POINTTOPOINT,
};
static const struct driver_info blob_info = {
.description = "Boot Loader OBject",
.check_connect = always_connected,
+ .flags = FLAG_POINTTOPOINT,
};
#endif /* CONFIG_USB_ARMLINUX */
diff --git a/drivers/net/usb/gl620a.c b/drivers/net/usb/gl620a.c
index dcd57c3..c4cfd1d 100644
--- a/drivers/net/usb/gl620a.c
+++ b/drivers/net/usb/gl620a.c
@@ -193,7 +193,7 @@
static const struct driver_info genelink_info = {
.description = "Genesys GeneLink",
- .flags = FLAG_FRAMING_GL | FLAG_NO_SETINT,
+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_GL | FLAG_NO_SETINT,
.bind = genelink_bind,
.rx_fixup = genelink_rx_fixup,
.tx_fixup = genelink_tx_fixup,
diff --git a/drivers/net/usb/net1080.c b/drivers/net/usb/net1080.c
index ba72a72..01db460 100644
--- a/drivers/net/usb/net1080.c
+++ b/drivers/net/usb/net1080.c
@@ -560,7 +560,7 @@
static const struct driver_info net1080_info = {
.description = "NetChip TurboCONNECT",
- .flags = FLAG_FRAMING_NC,
+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_NC,
.bind = net1080_bind,
.reset = net1080_reset,
.check_connect = net1080_check_connect,
diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c
index 08ad269..823c537 100644
--- a/drivers/net/usb/plusb.c
+++ b/drivers/net/usb/plusb.c
@@ -96,7 +96,7 @@
static const struct driver_info prolific_info = {
.description = "Prolific PL-2301/PL-2302",
- .flags = FLAG_NO_SETINT,
+ .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT,
/* some PL-2302 versions seem to fail usb_set_interface() */
.reset = pl_reset,
};
diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index dd8a4ad..5994a25 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -573,7 +573,7 @@
static const struct driver_info rndis_info = {
.description = "RNDIS device",
- .flags = FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT,
.bind = rndis_bind,
.unbind = rndis_unbind,
.status = rndis_status,
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c
index 753ee6e..860a20c 100644
--- a/drivers/net/usb/smsc75xx.c
+++ b/drivers/net/usb/smsc75xx.c
@@ -65,7 +65,6 @@
struct usbnet *dev;
u32 rfe_ctl;
u32 multicast_hash_table[DP_SEL_VHF_HASH_LEN];
- bool use_rx_csum;
struct mutex dataport_mutex;
spinlock_t rfe_ctl_lock;
struct work_struct set_multicast;
@@ -548,28 +547,6 @@
"unexpected interrupt, intdata=0x%08X", intdata);
}
-/* Enable or disable Rx checksum offload engine */
-static int smsc75xx_set_rx_csum_offload(struct usbnet *dev)
-{
- struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&pdata->rfe_ctl_lock, flags);
-
- if (pdata->use_rx_csum)
- pdata->rfe_ctl |= RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM;
- else
- pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM);
-
- spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags);
-
- ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
- check_warn_return(ret, "Error writing RFE_CTL");
-
- return 0;
-}
-
static int smsc75xx_ethtool_get_eeprom_len(struct net_device *net)
{
return MAX_EEPROM_SIZE;
@@ -599,34 +576,6 @@
return smsc75xx_write_eeprom(dev, ee->offset, ee->len, data);
}
-static u32 smsc75xx_ethtool_get_rx_csum(struct net_device *netdev)
-{
- struct usbnet *dev = netdev_priv(netdev);
- struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
-
- return pdata->use_rx_csum;
-}
-
-static int smsc75xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val)
-{
- struct usbnet *dev = netdev_priv(netdev);
- struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
-
- pdata->use_rx_csum = !!val;
-
- return smsc75xx_set_rx_csum_offload(dev);
-}
-
-static int smsc75xx_ethtool_set_tso(struct net_device *netdev, u32 data)
-{
- if (data)
- netdev->features |= NETIF_F_TSO | NETIF_F_TSO6;
- else
- netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
-
- return 0;
-}
-
static const struct ethtool_ops smsc75xx_ethtool_ops = {
.get_link = usbnet_get_link,
.nway_reset = usbnet_nway_reset,
@@ -638,12 +587,6 @@
.get_eeprom_len = smsc75xx_ethtool_get_eeprom_len,
.get_eeprom = smsc75xx_ethtool_get_eeprom,
.set_eeprom = smsc75xx_ethtool_set_eeprom,
- .get_tx_csum = ethtool_op_get_tx_csum,
- .set_tx_csum = ethtool_op_set_tx_hw_csum,
- .get_rx_csum = smsc75xx_ethtool_get_rx_csum,
- .set_rx_csum = smsc75xx_ethtool_set_rx_csum,
- .get_tso = ethtool_op_get_tso,
- .set_tso = smsc75xx_ethtool_set_tso,
};
static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
@@ -782,6 +725,30 @@
return usbnet_change_mtu(netdev, new_mtu);
}
+/* Enable or disable Rx checksum offload engine */
+static int smsc75xx_set_features(struct net_device *netdev, u32 features)
+{
+ struct usbnet *dev = netdev_priv(netdev);
+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pdata->rfe_ctl_lock, flags);
+
+ if (features & NETIF_F_RXCSUM)
+ pdata->rfe_ctl |= RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM;
+ else
+ pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM);
+
+ spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags);
+ /* it's racing here! */
+
+ ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
+ check_warn_return(ret, "Error writing RFE_CTL");
+
+ return 0;
+}
+
static int smsc75xx_reset(struct usbnet *dev)
{
struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
@@ -960,11 +927,7 @@
netif_dbg(dev, ifup, dev->net, "RFE_CTL set to 0x%08x", pdata->rfe_ctl);
/* Enable or disable checksum offload engines */
- ethtool_op_set_tx_hw_csum(dev->net, DEFAULT_TX_CSUM_ENABLE);
- ret = smsc75xx_set_rx_csum_offload(dev);
- check_warn_return(ret, "Failed to set rx csum offload: %d", ret);
-
- smsc75xx_ethtool_set_tso(dev->net, DEFAULT_TSO_ENABLE);
+ smsc75xx_set_features(dev->net, dev->net->features);
smsc75xx_set_multicast(dev->net);
@@ -1037,6 +1000,7 @@
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = smsc75xx_ioctl,
.ndo_set_multicast_list = smsc75xx_set_multicast,
+ .ndo_set_features = smsc75xx_set_features,
};
static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf)
@@ -1065,10 +1029,17 @@
INIT_WORK(&pdata->set_multicast, smsc75xx_deferred_multicast_write);
- pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE;
+ if (DEFAULT_TX_CSUM_ENABLE) {
+ dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
+ if (DEFAULT_TSO_ENABLE)
+ dev->net->features |= NETIF_F_SG |
+ NETIF_F_TSO | NETIF_F_TSO6;
+ }
+ if (DEFAULT_RX_CSUM_ENABLE)
+ dev->net->features |= NETIF_F_RXCSUM;
- /* We have to advertise SG otherwise TSO cannot be enabled */
- dev->net->features |= NETIF_F_SG;
+ dev->net->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+ NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_RXCSUM;
/* Init all registers */
ret = smsc75xx_reset(dev);
@@ -1091,10 +1062,11 @@
}
}
-static void smsc75xx_rx_csum_offload(struct sk_buff *skb, u32 rx_cmd_a,
- u32 rx_cmd_b)
+static void smsc75xx_rx_csum_offload(struct usbnet *dev, struct sk_buff *skb,
+ u32 rx_cmd_a, u32 rx_cmd_b)
{
- if (unlikely(rx_cmd_a & RX_CMD_A_LCSM)) {
+ if (!(dev->net->features & NETIF_F_RXCSUM) ||
+ unlikely(rx_cmd_a & RX_CMD_A_LCSM)) {
skb->ip_summed = CHECKSUM_NONE;
} else {
skb->csum = ntohs((u16)(rx_cmd_b >> RX_CMD_B_CSUM_SHIFT));
@@ -1104,8 +1076,6 @@
static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
- struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
-
while (skb->len > 0) {
u32 rx_cmd_a, rx_cmd_b, align_count, size;
struct sk_buff *ax_skb;
@@ -1145,11 +1115,8 @@
/* last frame in this batch */
if (skb->len == size) {
- if (pdata->use_rx_csum)
- smsc75xx_rx_csum_offload(skb, rx_cmd_a,
- rx_cmd_b);
- else
- skb->ip_summed = CHECKSUM_NONE;
+ smsc75xx_rx_csum_offload(dev, skb, rx_cmd_a,
+ rx_cmd_b);
skb_trim(skb, skb->len - 4); /* remove fcs */
skb->truesize = size + sizeof(struct sk_buff);
@@ -1167,11 +1134,8 @@
ax_skb->data = packet;
skb_set_tail_pointer(ax_skb, size);
- if (pdata->use_rx_csum)
- smsc75xx_rx_csum_offload(ax_skb, rx_cmd_a,
- rx_cmd_b);
- else
- ax_skb->ip_summed = CHECKSUM_NONE;
+ smsc75xx_rx_csum_offload(dev, ax_skb, rx_cmd_a,
+ rx_cmd_b);
skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */
ax_skb->truesize = size + sizeof(struct sk_buff);
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 727874d..708f208 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -52,8 +52,6 @@
u32 hash_hi;
u32 hash_lo;
spinlock_t mac_cr_lock;
- bool use_tx_csum;
- bool use_rx_csum;
};
struct usb_context {
@@ -517,22 +515,24 @@
}
/* Enable or disable Tx & Rx checksum offload engines */
-static int smsc95xx_set_csums(struct usbnet *dev)
+static int smsc95xx_set_features(struct net_device *netdev, u32 features)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct usbnet *dev = netdev_priv(netdev);
u32 read_buf;
- int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
+ int ret;
+
+ ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
if (ret < 0) {
netdev_warn(dev->net, "Failed to read COE_CR: %d\n", ret);
return ret;
}
- if (pdata->use_tx_csum)
+ if (features & NETIF_F_HW_CSUM)
read_buf |= Tx_COE_EN_;
else
read_buf &= ~Tx_COE_EN_;
- if (pdata->use_rx_csum)
+ if (features & NETIF_F_RXCSUM)
read_buf |= Rx_COE_EN_;
else
read_buf &= ~Rx_COE_EN_;
@@ -576,43 +576,6 @@
return smsc95xx_write_eeprom(dev, ee->offset, ee->len, data);
}
-static u32 smsc95xx_ethtool_get_rx_csum(struct net_device *netdev)
-{
- struct usbnet *dev = netdev_priv(netdev);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
-
- return pdata->use_rx_csum;
-}
-
-static int smsc95xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val)
-{
- struct usbnet *dev = netdev_priv(netdev);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
-
- pdata->use_rx_csum = !!val;
-
- return smsc95xx_set_csums(dev);
-}
-
-static u32 smsc95xx_ethtool_get_tx_csum(struct net_device *netdev)
-{
- struct usbnet *dev = netdev_priv(netdev);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
-
- return pdata->use_tx_csum;
-}
-
-static int smsc95xx_ethtool_set_tx_csum(struct net_device *netdev, u32 val)
-{
- struct usbnet *dev = netdev_priv(netdev);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
-
- pdata->use_tx_csum = !!val;
-
- ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum);
- return smsc95xx_set_csums(dev);
-}
-
static const struct ethtool_ops smsc95xx_ethtool_ops = {
.get_link = usbnet_get_link,
.nway_reset = usbnet_nway_reset,
@@ -624,10 +587,6 @@
.get_eeprom_len = smsc95xx_ethtool_get_eeprom_len,
.get_eeprom = smsc95xx_ethtool_get_eeprom,
.set_eeprom = smsc95xx_ethtool_set_eeprom,
- .get_tx_csum = smsc95xx_ethtool_get_tx_csum,
- .set_tx_csum = smsc95xx_ethtool_set_tx_csum,
- .get_rx_csum = smsc95xx_ethtool_get_rx_csum,
- .set_rx_csum = smsc95xx_ethtool_set_rx_csum,
};
static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
@@ -755,7 +714,6 @@
static int smsc95xx_reset(struct usbnet *dev)
{
struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
- struct net_device *netdev = dev->net;
u32 read_buf, write_buf, burst_cap;
int ret = 0, timeout;
@@ -975,12 +933,7 @@
}
/* Enable or disable checksum offload engines */
- ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum);
- ret = smsc95xx_set_csums(dev);
- if (ret < 0) {
- netdev_warn(dev->net, "Failed to set csum offload: %d\n", ret);
- return ret;
- }
+ smsc95xx_set_features(dev->net, dev->net->features);
smsc95xx_set_multicast(dev->net);
@@ -1019,6 +972,7 @@
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = smsc95xx_ioctl,
.ndo_set_multicast_list = smsc95xx_set_multicast,
+ .ndo_set_features = smsc95xx_set_features,
};
static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
@@ -1045,8 +999,12 @@
spin_lock_init(&pdata->mac_cr_lock);
- pdata->use_tx_csum = DEFAULT_TX_CSUM_ENABLE;
- pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE;
+ if (DEFAULT_TX_CSUM_ENABLE)
+ dev->net->features |= NETIF_F_HW_CSUM;
+ if (DEFAULT_RX_CSUM_ENABLE)
+ dev->net->features |= NETIF_F_RXCSUM;
+
+ dev->net->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
smsc95xx_init_mac_address(dev);
@@ -1056,7 +1014,7 @@
dev->net->netdev_ops = &smsc95xx_netdev_ops;
dev->net->ethtool_ops = &smsc95xx_ethtool_ops;
dev->net->flags |= IFF_MULTICAST;
- dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD;
+ dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM;
return 0;
}
@@ -1080,8 +1038,6 @@
static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
-
while (skb->len > 0) {
u32 header, align_count;
struct sk_buff *ax_skb;
@@ -1123,7 +1079,7 @@
/* last frame in this batch */
if (skb->len == size) {
- if (pdata->use_rx_csum)
+ if (dev->net->features & NETIF_F_RXCSUM)
smsc95xx_rx_csum_offload(skb);
skb_trim(skb, skb->len - 4); /* remove fcs */
skb->truesize = size + sizeof(struct sk_buff);
@@ -1141,7 +1097,7 @@
ax_skb->data = packet;
skb_set_tail_pointer(ax_skb, size);
- if (pdata->use_rx_csum)
+ if (dev->net->features & NETIF_F_RXCSUM)
smsc95xx_rx_csum_offload(ax_skb);
skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */
ax_skb->truesize = size + sizeof(struct sk_buff);
@@ -1174,8 +1130,7 @@
static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
struct sk_buff *skb, gfp_t flags)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
- bool csum = pdata->use_tx_csum && (skb->ip_summed == CHECKSUM_PARTIAL);
+ bool csum = skb->ip_summed == CHECKSUM_PARTIAL;
int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD;
u32 tx_cmd_a, tx_cmd_b;
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index cf58b76..069c1cf 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1380,7 +1380,8 @@
// else "eth%d" when there's reasonable doubt. userspace
// can rename the link if it knows better.
if ((dev->driver_info->flags & FLAG_ETHER) != 0 &&
- (net->dev_addr [0] & 0x02) == 0)
+ ((dev->driver_info->flags & FLAG_POINTTOPOINT) == 0 ||
+ (net->dev_addr [0] & 0x02) == 0))
strcpy (net->name, "eth%d");
/* WLAN devices should always be named "wlan%d" */
if ((dev->driver_info->flags & FLAG_WLAN) != 0)
diff --git a/drivers/net/usb/zaurus.c b/drivers/net/usb/zaurus.c
index 3eb0b16..241756e 100644
--- a/drivers/net/usb/zaurus.c
+++ b/drivers/net/usb/zaurus.c
@@ -102,7 +102,7 @@
static const struct driver_info zaurus_sl5x00_info = {
.description = "Sharp Zaurus SL-5x00",
- .flags = FLAG_FRAMING_Z,
+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z,
.check_connect = always_connected,
.bind = zaurus_bind,
.unbind = usbnet_cdc_unbind,
@@ -112,7 +112,7 @@
static const struct driver_info zaurus_pxa_info = {
.description = "Sharp Zaurus, PXA-2xx based",
- .flags = FLAG_FRAMING_Z,
+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z,
.check_connect = always_connected,
.bind = zaurus_bind,
.unbind = usbnet_cdc_unbind,
@@ -122,7 +122,7 @@
static const struct driver_info olympus_mxl_info = {
.description = "Olympus R1000",
- .flags = FLAG_FRAMING_Z,
+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z,
.check_connect = always_connected,
.bind = zaurus_bind,
.unbind = usbnet_cdc_unbind,
@@ -258,7 +258,7 @@
static const struct driver_info bogus_mdlm_info = {
.description = "pseudo-MDLM (BLAN) device",
- .flags = FLAG_FRAMING_Z,
+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z,
.check_connect = always_connected,
.tx_fixup = zaurus_tx_fixup,
.bind = blan_mdlm_bind,
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 2de9b90..6542288 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -36,7 +36,6 @@
struct veth_priv {
struct net_device *peer;
struct veth_net_stats __percpu *stats;
- unsigned ip_summed;
};
/*
@@ -99,47 +98,10 @@
data[0] = priv->peer->ifindex;
}
-static u32 veth_get_rx_csum(struct net_device *dev)
-{
- struct veth_priv *priv;
-
- priv = netdev_priv(dev);
- return priv->ip_summed == CHECKSUM_UNNECESSARY;
-}
-
-static int veth_set_rx_csum(struct net_device *dev, u32 data)
-{
- struct veth_priv *priv;
-
- priv = netdev_priv(dev);
- priv->ip_summed = data ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
- return 0;
-}
-
-static u32 veth_get_tx_csum(struct net_device *dev)
-{
- return (dev->features & NETIF_F_NO_CSUM) != 0;
-}
-
-static int veth_set_tx_csum(struct net_device *dev, u32 data)
-{
- if (data)
- dev->features |= NETIF_F_NO_CSUM;
- else
- dev->features &= ~NETIF_F_NO_CSUM;
- return 0;
-}
-
static const struct ethtool_ops veth_ethtool_ops = {
.get_settings = veth_get_settings,
.get_drvinfo = veth_get_drvinfo,
.get_link = ethtool_op_get_link,
- .get_rx_csum = veth_get_rx_csum,
- .set_rx_csum = veth_set_rx_csum,
- .get_tx_csum = veth_get_tx_csum,
- .set_tx_csum = veth_set_tx_csum,
- .get_sg = ethtool_op_get_sg,
- .set_sg = ethtool_op_set_sg,
.get_strings = veth_get_strings,
.get_sset_count = veth_get_sset_count,
.get_ethtool_stats = veth_get_ethtool_stats,
@@ -168,8 +130,9 @@
/* don't change ip_summed == CHECKSUM_PARTIAL, as that
will cause bad checksum on forwarded packets */
- if (skb->ip_summed == CHECKSUM_NONE)
- skb->ip_summed = rcv_priv->ip_summed;
+ if (skb->ip_summed == CHECKSUM_NONE &&
+ rcv->features & NETIF_F_RXCSUM)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
length = skb->len;
if (dev_forward_skb(rcv, skb) != NET_RX_SUCCESS)
@@ -304,6 +267,8 @@
dev->ethtool_ops = &veth_ethtool_ops;
dev->features |= NETIF_F_LLTX;
dev->destructor = veth_dev_free;
+
+ dev->hw_features = NETIF_F_NO_CSUM | NETIF_F_SG | NETIF_F_RXCSUM;
}
/*
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 82dba5a..0cb0b06 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -710,17 +710,6 @@
return 0;
}
-static int virtnet_set_tx_csum(struct net_device *dev, u32 data)
-{
- struct virtnet_info *vi = netdev_priv(dev);
- struct virtio_device *vdev = vi->vdev;
-
- if (data && !virtio_has_feature(vdev, VIRTIO_NET_F_CSUM))
- return -ENOSYS;
-
- return ethtool_op_set_tx_hw_csum(dev, data);
-}
-
static void virtnet_set_rx_mode(struct net_device *dev)
{
struct virtnet_info *vi = netdev_priv(dev);
@@ -822,10 +811,6 @@
}
static const struct ethtool_ops virtnet_ethtool_ops = {
- .set_tx_csum = virtnet_set_tx_csum,
- .set_sg = ethtool_op_set_sg,
- .set_tso = ethtool_op_set_tso,
- .set_ufo = ethtool_op_set_ufo,
.get_link = ethtool_op_get_link,
};
@@ -912,22 +897,29 @@
SET_NETDEV_DEV(dev, &vdev->dev);
/* Do we support "hardware" checksums? */
- if (csum && virtio_has_feature(vdev, VIRTIO_NET_F_CSUM)) {
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_CSUM)) {
/* This opens up the world of extra features. */
- dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST;
- if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) {
- dev->features |= NETIF_F_TSO | NETIF_F_UFO
+ dev->hw_features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST;
+ if (csum)
+ dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST;
+
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) {
+ dev->hw_features |= NETIF_F_TSO | NETIF_F_UFO
| NETIF_F_TSO_ECN | NETIF_F_TSO6;
}
/* Individual feature bits: what can host handle? */
- if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO4))
- dev->features |= NETIF_F_TSO;
- if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO6))
- dev->features |= NETIF_F_TSO6;
- if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN))
- dev->features |= NETIF_F_TSO_ECN;
- if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UFO))
- dev->features |= NETIF_F_UFO;
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO4))
+ dev->hw_features |= NETIF_F_TSO;
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO6))
+ dev->hw_features |= NETIF_F_TSO6;
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN))
+ dev->hw_features |= NETIF_F_TSO_ECN;
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UFO))
+ dev->hw_features |= NETIF_F_UFO;
+
+ if (gso)
+ dev->features |= dev->hw_features & (NETIF_F_ALL_TSO|NETIF_F_UFO);
+ /* (!csum && gso) case will be fixed by register_netdev() */
}
/* Configuration may specify what MAC to use. Otherwise random. */
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 5248257..dddb85d 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1048,6 +1048,8 @@
"Starting driver with initial channel: %d MHz\n",
curchan->center_freq);
+ ath9k_ps_wakeup(sc);
+
mutex_lock(&sc->mutex);
/* setup initial channel */
@@ -1143,6 +1145,8 @@
mutex_unlock:
mutex_unlock(&sc->mutex);
+ ath9k_ps_restore(sc);
+
return r;
}
diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
index c6a5fae..9cad061 100644
--- a/drivers/net/wireless/ath/carl9170/carl9170.h
+++ b/drivers/net/wireless/ath/carl9170/carl9170.h
@@ -443,6 +443,7 @@
u8 ampdu_len;
u8 ampdu_ack_len;
bool clear;
+ bool req;
};
struct carl9170_sta_info {
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
index ede3d7e..89fe60a 100644
--- a/drivers/net/wireless/ath/carl9170/main.c
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -1355,6 +1355,7 @@
tid_info = rcu_dereference(sta_info->agg[tid]);
sta_info->stats[tid].clear = true;
+ sta_info->stats[tid].req = false;
if (tid_info) {
bitmap_zero(tid_info->bitmap, CARL9170_BAW_SIZE);
diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c
index 0ef70b6..cb70ed7 100644
--- a/drivers/net/wireless/ath/carl9170/tx.c
+++ b/drivers/net/wireless/ath/carl9170/tx.c
@@ -383,6 +383,7 @@
if (sta_info->stats[tid].clear) {
sta_info->stats[tid].clear = false;
+ sta_info->stats[tid].req = false;
sta_info->stats[tid].ampdu_len = 0;
sta_info->stats[tid].ampdu_ack_len = 0;
}
@@ -391,10 +392,16 @@
if (txinfo->status.rates[0].count == 1)
sta_info->stats[tid].ampdu_ack_len++;
+ if (!(txinfo->flags & IEEE80211_TX_STAT_ACK))
+ sta_info->stats[tid].req = true;
+
if (super->f.mac_control & cpu_to_le16(AR9170_TX_MAC_IMM_BA)) {
super->s.rix = sta_info->stats[tid].ampdu_len;
super->s.cnt = sta_info->stats[tid].ampdu_ack_len;
txinfo->flags |= IEEE80211_TX_STAT_AMPDU;
+ if (sta_info->stats[tid].req)
+ txinfo->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
+
sta_info->stats[tid].clear = true;
}
spin_unlock_bh(&tid_info->lock);
diff --git a/drivers/net/wireless/iwlegacy/iwl-core.c b/drivers/net/wireless/iwlegacy/iwl-core.c
index d418b64..a209a0e 100644
--- a/drivers/net/wireless/iwlegacy/iwl-core.c
+++ b/drivers/net/wireless/iwlegacy/iwl-core.c
@@ -1805,6 +1805,15 @@
mutex_lock(&priv->mutex);
+ if (!ctx->vif || !iwl_legacy_is_ready_rf(priv)) {
+ /*
+ * Huh? But wait ... this can maybe happen when
+ * we're in the middle of a firmware restart!
+ */
+ err = -EBUSY;
+ goto out;
+ }
+
interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes;
if (!(interface_modes & BIT(newtype))) {
@@ -1832,6 +1841,7 @@
/* success */
iwl_legacy_teardown_interface(priv, vif, true);
vif->type = newtype;
+ vif->p2p = newp2p;
err = iwl_legacy_setup_interface(priv, ctx);
WARN_ON(err);
/*
diff --git a/drivers/net/wireless/iwlegacy/iwl3945-base.c b/drivers/net/wireless/iwlegacy/iwl3945-base.c
index ab87e1b..28eb3d8 100644
--- a/drivers/net/wireless/iwlegacy/iwl3945-base.c
+++ b/drivers/net/wireless/iwlegacy/iwl3945-base.c
@@ -93,6 +93,7 @@
struct iwl_mod_params iwl3945_mod_params = {
.sw_crypto = 1,
.restart_fw = 1,
+ .disable_hw_scan = 1,
/* the rest are 0 by default */
};
@@ -3960,8 +3961,7 @@
* "the hard way", rather than using device's scan.
*/
if (iwl3945_mod_params.disable_hw_scan) {
- dev_printk(KERN_DEBUG, &(pdev->dev),
- "sw scan support is deprecated\n");
+ IWL_DEBUG_INFO(priv, "Disabling hw_scan\n");
iwl3945_hw_ops.hw_scan = NULL;
}
@@ -4280,8 +4280,7 @@
"using software crypto (default 1 [software])");
module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan,
int, S_IRUGO);
-MODULE_PARM_DESC(disable_hw_scan,
- "disable hardware scanning (default 0) (deprecated)");
+MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 1)");
#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG
module_param_named(debug, iwlegacy_debug_level, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "debug output mask");
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 4e36865..37509d0 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -730,8 +730,12 @@
{ USB_DEVICE(0x050d, 0x8053), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x050d, 0x805c), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x050d, 0x815c), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x050d, 0x825b), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x050d, 0x935a), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x050d, 0x935b), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Buffalo */
{ USB_DEVICE(0x0411, 0x00e8), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0411, 0x016f), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Conceptronic */
{ USB_DEVICE(0x14b2, 0x3c06), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x14b2, 0x3c07), USB_DEVICE_DATA(&rt2800usb_ops) },
@@ -818,6 +822,7 @@
/* Pegatron */
{ USB_DEVICE(0x1d4d, 0x000c), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x1d4d, 0x000e), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x1d4d, 0x0011), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Philips */
{ USB_DEVICE(0x0471, 0x200f), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Planex */
@@ -899,6 +904,8 @@
{ USB_DEVICE(0x148f, 0x3572), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Sitecom */
{ USB_DEVICE(0x0df6, 0x0041), USB_DEVICE_DATA(&rt2800usb_ops) },
+ /* Toshiba */
+ { USB_DEVICE(0x0930, 0x0a07), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Zinwell */
{ USB_DEVICE(0x5a57, 0x0284), USB_DEVICE_DATA(&rt2800usb_ops) },
#endif
@@ -961,7 +968,6 @@
{ USB_DEVICE(0x05a6, 0x0101), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x1d4d, 0x0002), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x1d4d, 0x0010), USB_DEVICE_DATA(&rt2800usb_ops) },
- { USB_DEVICE(0x1d4d, 0x0011), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Planex */
{ USB_DEVICE(0x2019, 0x5201), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x2019, 0xab24), USB_DEVICE_DATA(&rt2800usb_ops) },
@@ -975,8 +981,6 @@
/* Sweex */
{ USB_DEVICE(0x177f, 0x0153), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x177f, 0x0313), USB_DEVICE_DATA(&rt2800usb_ops) },
- /* Toshiba */
- { USB_DEVICE(0x0930, 0x0a07), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Zyxel */
{ USB_DEVICE(0x0586, 0x341a), USB_DEVICE_DATA(&rt2800usb_ops) },
#endif
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index c06f5a0..db9a763 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -1140,6 +1140,42 @@
gnttab_free_grant_references(np->gref_rx_head);
}
+static u32 xennet_fix_features(struct net_device *dev, u32 features)
+{
+ struct netfront_info *np = netdev_priv(dev);
+ int val;
+
+ if (features & NETIF_F_SG) {
+ if (xenbus_scanf(XBT_NIL, np->xbdev->otherend, "feature-sg",
+ "%d", &val) < 0)
+ val = 0;
+
+ if (!val)
+ features &= ~NETIF_F_SG;
+ }
+
+ if (features & NETIF_F_TSO) {
+ if (xenbus_scanf(XBT_NIL, np->xbdev->otherend,
+ "feature-gso-tcpv4", "%d", &val) < 0)
+ val = 0;
+
+ if (!val)
+ features &= ~NETIF_F_TSO;
+ }
+
+ return features;
+}
+
+static int xennet_set_features(struct net_device *dev, u32 features)
+{
+ if (!(features & NETIF_F_SG) && dev->mtu > ETH_DATA_LEN) {
+ netdev_info(dev, "Reducing MTU because no SG offload");
+ dev->mtu = ETH_DATA_LEN;
+ }
+
+ return 0;
+}
+
static const struct net_device_ops xennet_netdev_ops = {
.ndo_open = xennet_open,
.ndo_uninit = xennet_uninit,
@@ -1148,6 +1184,8 @@
.ndo_change_mtu = xennet_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
+ .ndo_fix_features = xennet_fix_features,
+ .ndo_set_features = xennet_set_features,
};
static struct net_device * __devinit xennet_create_dev(struct xenbus_device *dev)
@@ -1209,7 +1247,17 @@
netdev->netdev_ops = &xennet_netdev_ops;
netif_napi_add(netdev, &np->napi, xennet_poll, 64);
- netdev->features = NETIF_F_IP_CSUM;
+ netdev->features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
+ NETIF_F_GSO_ROBUST;
+ netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO;
+
+ /*
+ * Assume that all hw features are available for now. This set
+ * will be adjusted by the call to netdev_update_features() in
+ * xennet_connect() which is the earliest point where we can
+ * negotiate with the backend regarding supported features.
+ */
+ netdev->features |= netdev->hw_features;
SET_ETHTOOL_OPS(netdev, &xennet_ethtool_ops);
SET_NETDEV_DEV(netdev, &dev->dev);
@@ -1509,54 +1557,6 @@
return err;
}
-static int xennet_set_sg(struct net_device *dev, u32 data)
-{
- if (data) {
- struct netfront_info *np = netdev_priv(dev);
- int val;
-
- if (xenbus_scanf(XBT_NIL, np->xbdev->otherend, "feature-sg",
- "%d", &val) < 0)
- val = 0;
- if (!val)
- return -ENOSYS;
- } else if (dev->mtu > ETH_DATA_LEN)
- dev->mtu = ETH_DATA_LEN;
-
- return ethtool_op_set_sg(dev, data);
-}
-
-static int xennet_set_tso(struct net_device *dev, u32 data)
-{
- if (data) {
- struct netfront_info *np = netdev_priv(dev);
- int val;
-
- if (xenbus_scanf(XBT_NIL, np->xbdev->otherend,
- "feature-gso-tcpv4", "%d", &val) < 0)
- val = 0;
- if (!val)
- return -ENOSYS;
- }
-
- return ethtool_op_set_tso(dev, data);
-}
-
-static void xennet_set_features(struct net_device *dev)
-{
- /* Turn off all GSO bits except ROBUST. */
- dev->features &= ~NETIF_F_GSO_MASK;
- dev->features |= NETIF_F_GSO_ROBUST;
- xennet_set_sg(dev, 0);
-
- /* We need checksum offload to enable scatter/gather and TSO. */
- if (!(dev->features & NETIF_F_IP_CSUM))
- return;
-
- if (!xennet_set_sg(dev, 1))
- xennet_set_tso(dev, 1);
-}
-
static int xennet_connect(struct net_device *dev)
{
struct netfront_info *np = netdev_priv(dev);
@@ -1581,7 +1581,7 @@
if (err)
return err;
- xennet_set_features(dev);
+ netdev_update_features(dev);
spin_lock_bh(&np->rx_lock);
spin_lock_irq(&np->tx_lock);
@@ -1709,9 +1709,6 @@
static const struct ethtool_ops xennet_ethtool_ops =
{
- .set_tx_csum = ethtool_op_set_tx_csum,
- .set_sg = xennet_set_sg,
- .set_tso = xennet_set_tso,
.get_link = ethtool_op_get_link,
.get_sset_count = xennet_get_sset_count,
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 5eeb2cd..09d2624 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1066,6 +1066,7 @@
#define NETIF_F_NTUPLE (1 << 27) /* N-tuple filters supported */
#define NETIF_F_RXHASH (1 << 28) /* Receive hashing offload */
#define NETIF_F_RXCSUM (1 << 29) /* Receive checksumming offload */
+#define NETIF_F_NOCACHE_COPY (1 << 30) /* Use no-cache copyfromuser */
/* Segmentation offload features */
#define NETIF_F_GSO_SHIFT 16
@@ -1081,7 +1082,7 @@
/* = all defined minus driver/device-class-related */
#define NETIF_F_NEVER_CHANGE (NETIF_F_HIGHDMA | NETIF_F_VLAN_CHALLENGED | \
NETIF_F_LLTX | NETIF_F_NETNS_LOCAL)
-#define NETIF_F_ETHTOOL_BITS (0x3f3fffff & ~NETIF_F_NEVER_CHANGE)
+#define NETIF_F_ETHTOOL_BITS (0x7f3fffff & ~NETIF_F_NEVER_CHANGE)
/* List of features with software fallbacks. */
#define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | \
@@ -2550,6 +2551,7 @@
}
u32 netdev_increment_features(u32 all, u32 one, u32 mask);
u32 netdev_fix_features(struct net_device *dev, u32 features);
+int __netdev_update_features(struct net_device *dev);
void netdev_update_features(struct net_device *dev);
void netif_stacked_transfer_operstate(const struct net_device *rootdev,
@@ -2598,8 +2600,8 @@
static inline u32 dev_ethtool_get_rx_csum(struct net_device *dev)
{
- if (dev->hw_features & NETIF_F_RXCSUM)
- return !!(dev->features & NETIF_F_RXCSUM);
+ if (dev->features & NETIF_F_RXCSUM)
+ return 1;
if (!dev->ethtool_ops || !dev->ethtool_ops->get_rx_csum)
return 0;
return dev->ethtool_ops->get_rx_csum(dev);
diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index b1032a3..8062e0a 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -588,4 +588,19 @@
#define SFB_MAX_PROB 0xFFFF
+/* QFQ */
+enum {
+ TCA_QFQ_UNSPEC,
+ TCA_QFQ_WEIGHT,
+ TCA_QFQ_LMAX,
+ __TCA_QFQ_MAX
+};
+
+#define TCA_QFQ_MAX (__TCA_QFQ_MAX - 1)
+
+struct tc_qfq_stats {
+ __u32 weight;
+ __u32 lmax;
+};
+
#endif
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 201f222..3c7329b 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -97,6 +97,8 @@
#define FLAG_LINK_INTR 0x0800 /* updates link (carrier) status */
+#define FLAG_POINTTOPOINT 0x1000 /* possibly use "usb%d" names */
+
/*
* Indicates to usbnet, that USB driver accumulates multiple IP packets.
* Affects statistic (counters) and short packet handling.
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index ec6acf2..2c0d309 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -84,6 +84,8 @@
HCI_SERVICE_CACHE,
HCI_LINK_KEYS,
HCI_DEBUG_KEYS,
+
+ HCI_RESET,
};
/* HCI ioctl defines */
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index cc9185c..0f6e60a 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -422,7 +422,7 @@
__u32 adaptation_ind;
__u8 auth_random[sizeof(sctp_paramhdr_t) + SCTP_AUTH_RANDOM_LENGTH];
- __u8 auth_hmacs[SCTP_AUTH_NUM_HMACS + 2];
+ __u8 auth_hmacs[SCTP_AUTH_NUM_HMACS * sizeof(__u16) + 2];
__u8 auth_chunks[sizeof(sctp_paramhdr_t) + SCTP_AUTH_MAX_CHUNKS];
/* This is a shim for my peer's INIT packet, followed by
diff --git a/include/net/sock.h b/include/net/sock.h
index da0534d..43bd515 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -52,6 +52,7 @@
#include <linux/mm.h>
#include <linux/security.h>
#include <linux/slab.h>
+#include <linux/uaccess.h>
#include <linux/filter.h>
#include <linux/rculist_nulls.h>
@@ -1389,6 +1390,58 @@
sk->sk_route_caps &= ~flags;
}
+static inline int skb_do_copy_data_nocache(struct sock *sk, struct sk_buff *skb,
+ char __user *from, char *to,
+ int copy)
+{
+ if (skb->ip_summed == CHECKSUM_NONE) {
+ int err = 0;
+ __wsum csum = csum_and_copy_from_user(from, to, copy, 0, &err);
+ if (err)
+ return err;
+ skb->csum = csum_block_add(skb->csum, csum, skb->len);
+ } else if (sk->sk_route_caps & NETIF_F_NOCACHE_COPY) {
+ if (!access_ok(VERIFY_READ, from, copy) ||
+ __copy_from_user_nocache(to, from, copy))
+ return -EFAULT;
+ } else if (copy_from_user(to, from, copy))
+ return -EFAULT;
+
+ return 0;
+}
+
+static inline int skb_add_data_nocache(struct sock *sk, struct sk_buff *skb,
+ char __user *from, int copy)
+{
+ int err;
+
+ err = skb_do_copy_data_nocache(sk, skb, from, skb_put(skb, copy), copy);
+ if (err)
+ __skb_trim(skb, skb->len);
+
+ return err;
+}
+
+static inline int skb_copy_to_page_nocache(struct sock *sk, char __user *from,
+ struct sk_buff *skb,
+ struct page *page,
+ int off, int copy)
+{
+ int err;
+
+ err = skb_do_copy_data_nocache(sk, skb, from,
+ page_address(page) + off, copy);
+ if (err)
+ return err;
+
+ skb->len += copy;
+ skb->data_len += copy;
+ skb->truesize += copy;
+ sk->sk_wmem_queued += copy;
+ sk_mem_charge(sk, copy);
+ return 0;
+}
+
static inline int skb_copy_to_page(struct sock *sk, char __user *from,
struct sk_buff *skb, struct page *page,
int off, int copy)
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index 7850412..e47600b4 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -327,10 +327,6 @@
static void vlan_transfer_features(struct net_device *dev,
struct net_device *vlandev)
{
- u32 old_features = vlandev->features;
-
- vlandev->features &= ~dev->vlan_features;
- vlandev->features |= dev->features & dev->vlan_features;
vlandev->gso_max_size = dev->gso_max_size;
if (dev->features & NETIF_F_HW_VLAN_TX)
@@ -341,8 +337,8 @@
#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid;
#endif
- if (old_features != vlandev->features)
- netdev_features_change(vlandev);
+
+ netdev_update_features(vlandev);
}
static void __vlan_device_event(struct net_device *dev, unsigned long event)
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index e34ea9e..b84a46b 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -704,8 +704,8 @@
(1<<__LINK_STATE_DORMANT))) |
(1<<__LINK_STATE_PRESENT);
- dev->features |= real_dev->features & real_dev->vlan_features;
- dev->features |= NETIF_F_LLTX;
+ dev->hw_features = real_dev->vlan_features & NETIF_F_ALL_TX_OFFLOADS;
+ dev->features |= real_dev->vlan_features | NETIF_F_LLTX;
dev->gso_max_size = real_dev->gso_max_size;
/* ipv6 shared card related stuff */
@@ -759,6 +759,17 @@
}
}
+static u32 vlan_dev_fix_features(struct net_device *dev, u32 features)
+{
+ struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
+
+ features &= (real_dev->features | NETIF_F_LLTX);
+ if (dev_ethtool_get_rx_csum(real_dev))
+ features |= NETIF_F_RXCSUM;
+
+ return features;
+}
+
static int vlan_ethtool_get_settings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
@@ -774,18 +785,6 @@
strcpy(info->fw_version, "N/A");
}
-static u32 vlan_ethtool_get_rx_csum(struct net_device *dev)
-{
- const struct vlan_dev_info *vlan = vlan_dev_info(dev);
- return dev_ethtool_get_rx_csum(vlan->real_dev);
-}
-
-static u32 vlan_ethtool_get_flags(struct net_device *dev)
-{
- const struct vlan_dev_info *vlan = vlan_dev_info(dev);
- return dev_ethtool_get_flags(vlan->real_dev);
-}
-
static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
{
@@ -823,32 +822,10 @@
return stats;
}
-static int vlan_ethtool_set_tso(struct net_device *dev, u32 data)
-{
- if (data) {
- struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
-
- /* Underlying device must support TSO for VLAN-tagged packets
- * and must have TSO enabled now.
- */
- if (!(real_dev->vlan_features & NETIF_F_TSO))
- return -EOPNOTSUPP;
- if (!(real_dev->features & NETIF_F_TSO))
- return -EINVAL;
- dev->features |= NETIF_F_TSO;
- } else {
- dev->features &= ~NETIF_F_TSO;
- }
- return 0;
-}
-
static const struct ethtool_ops vlan_ethtool_ops = {
.get_settings = vlan_ethtool_get_settings,
.get_drvinfo = vlan_ethtool_get_drvinfo,
.get_link = ethtool_op_get_link,
- .get_rx_csum = vlan_ethtool_get_rx_csum,
- .get_flags = vlan_ethtool_get_flags,
- .set_tso = vlan_ethtool_set_tso,
};
static const struct net_device_ops vlan_netdev_ops = {
@@ -874,6 +851,7 @@
.ndo_fcoe_get_wwn = vlan_dev_fcoe_get_wwn,
.ndo_fcoe_ddp_target = vlan_dev_fcoe_ddp_target,
#endif
+ .ndo_fix_features = vlan_dev_fix_features,
};
void vlan_setup(struct net_device *dev)
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index 206e771..956a530 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -1051,16 +1051,17 @@
{
struct sock *sk = sock->sk;
- sock_hold(sk);
- lock_sock(sk);
if (sk) {
+ sock_hold(sk);
+ lock_sock(sk);
+
sock_orphan(sk);
sock->sk = NULL;
atalk_destroy_socket(sk);
- }
- release_sock(sk);
- sock_put(sk);
+ release_sock(sk);
+ sock_put(sk);
+ }
return 0;
}
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index b372fb8..2216620 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -186,6 +186,7 @@
BT_DBG("%s %ld", hdev->name, opt);
/* Reset device */
+ set_bit(HCI_RESET, &hdev->flags);
hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
}
@@ -213,8 +214,10 @@
/* Mandatory initialization */
/* Reset */
- if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks))
+ if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) {
+ set_bit(HCI_RESET, &hdev->flags);
hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
+ }
/* Read Local Supported Features */
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
@@ -584,6 +587,9 @@
hci_req_cancel(hdev, ENODEV);
hci_req_lock(hdev);
+ /* Stop timer, it might be running */
+ del_timer_sync(&hdev->cmd_timer);
+
if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
hci_req_unlock(hdev);
return 0;
@@ -623,7 +629,6 @@
/* Drop last sent command */
if (hdev->sent_cmd) {
- del_timer_sync(&hdev->cmd_timer);
kfree_skb(hdev->sent_cmd);
hdev->sent_cmd = NULL;
}
@@ -1074,6 +1079,7 @@
BT_ERR("%s command tx timeout", hdev->name);
atomic_set(&hdev->cmd_cnt, 1);
+ clear_bit(HCI_RESET, &hdev->flags);
tasklet_schedule(&hdev->cmd_task);
}
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 3fbfa50..cebe7588 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -183,6 +183,8 @@
BT_DBG("%s status 0x%x", hdev->name, status);
+ clear_bit(HCI_RESET, &hdev->flags);
+
hci_req_complete(hdev, HCI_OP_RESET, status);
}
@@ -1847,7 +1849,7 @@
if (ev->opcode != HCI_OP_NOP)
del_timer(&hdev->cmd_timer);
- if (ev->ncmd) {
+ if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
atomic_set(&hdev->cmd_cnt, 1);
if (!skb_queue_empty(&hdev->cmd_q))
tasklet_schedule(&hdev->cmd_task);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index c9f9cec..ca27f3a 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1116,7 +1116,9 @@
bt_cb(skb)->tx_seq = pi->next_tx_seq;
pi->next_tx_seq = (pi->next_tx_seq + 1) % 64;
- pi->unacked_frames++;
+ if (bt_cb(skb)->retries == 1)
+ pi->unacked_frames++;
+
pi->frames_sent++;
if (skb_queue_is_last(TX_QUEUE(sk), skb))
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index fc85e7a..f77308e 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -923,8 +923,9 @@
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
- } else
- l2cap_chan_del(sk, reason);
+ }
+
+ l2cap_chan_del(sk, reason);
break;
case BT_CONNECT:
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 0054c74..4476d8e 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1230,6 +1230,8 @@
if (!hdev)
return cmd_status(sk, index, mgmt_op, ENODEV);
+ hci_dev_lock_bh(hdev);
+
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, mgmt_op, ENETDOWN);
goto failed;
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 84bbb82..f20c4fd 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -104,3 +104,4 @@
module_exit(br_deinit)
MODULE_LICENSE("GPL");
MODULE_VERSION(BR_VERSION);
+MODULE_ALIAS_RTNL_LINK("bridge");
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 21e5901..45cfd54 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -74,6 +74,17 @@
return NETDEV_TX_OK;
}
+static int br_dev_init(struct net_device *dev)
+{
+ struct net_bridge *br = netdev_priv(dev);
+
+ br->stats = alloc_percpu(struct br_cpu_netstats);
+ if (!br->stats)
+ return -ENOMEM;
+
+ return 0;
+}
+
static int br_dev_open(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
@@ -334,6 +345,7 @@
static const struct net_device_ops br_netdev_ops = {
.ndo_open = br_dev_open,
.ndo_stop = br_dev_stop,
+ .ndo_init = br_dev_init,
.ndo_start_xmit = br_dev_xmit,
.ndo_get_stats64 = br_get_stats64,
.ndo_set_mac_address = br_set_mac_address,
@@ -357,18 +369,47 @@
free_netdev(dev);
}
+static struct device_type br_type = {
+ .name = "bridge",
+};
+
void br_dev_setup(struct net_device *dev)
{
+ struct net_bridge *br = netdev_priv(dev);
+
random_ether_addr(dev->dev_addr);
ether_setup(dev);
dev->netdev_ops = &br_netdev_ops;
dev->destructor = br_dev_free;
SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
+ SET_NETDEV_DEVTYPE(dev, &br_type);
dev->tx_queue_len = 0;
dev->priv_flags = IFF_EBRIDGE;
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
NETIF_F_GSO_MASK | NETIF_F_NO_CSUM | NETIF_F_LLTX |
NETIF_F_NETNS_LOCAL | NETIF_F_GSO | NETIF_F_HW_VLAN_TX;
+
+ br->dev = dev;
+ spin_lock_init(&br->lock);
+ INIT_LIST_HEAD(&br->port_list);
+ spin_lock_init(&br->hash_lock);
+
+ br->bridge_id.prio[0] = 0x80;
+ br->bridge_id.prio[1] = 0x00;
+
+ memcpy(br->group_addr, br_group_address, ETH_ALEN);
+
+ br->feature_mask = dev->features;
+ br->stp_enabled = BR_NO_STP;
+ br->designated_root = br->bridge_id;
+ br->bridge_max_age = br->max_age = 20 * HZ;
+ br->bridge_hello_time = br->hello_time = 2 * HZ;
+ br->bridge_forward_delay = br->forward_delay = 15 * HZ;
+ br->ageing_time = 300 * HZ;
+
+ br_netfilter_rtable_init(br);
+ br_stp_timer_init(br);
+ br_multicast_init(br);
}
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 88485cc..498f47c 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -28,6 +28,7 @@
static struct kmem_cache *br_fdb_cache __read_mostly;
static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
const unsigned char *addr);
+static void fdb_notify(const struct net_bridge_fdb_entry *, int);
static u32 fdb_salt __read_mostly;
@@ -62,7 +63,7 @@
const struct net_bridge_fdb_entry *fdb)
{
return !fdb->is_static &&
- time_before_eq(fdb->ageing_timer + hold_time(br), jiffies);
+ time_before_eq(fdb->updated + hold_time(br), jiffies);
}
static inline int br_mac_hash(const unsigned char *mac)
@@ -81,6 +82,7 @@
static inline void fdb_delete(struct net_bridge_fdb_entry *f)
{
+ fdb_notify(f, RTM_DELNEIGH);
hlist_del_rcu(&f->hlist);
call_rcu(&f->rcu, fdb_rcu_free);
}
@@ -140,7 +142,7 @@
unsigned long this_timer;
if (f->is_static)
continue;
- this_timer = f->ageing_timer + delay;
+ this_timer = f->updated + delay;
if (time_before_eq(this_timer, jiffies))
fdb_delete(f);
else if (time_before(this_timer, next_timer))
@@ -293,7 +295,7 @@
fe->is_local = f->is_local;
if (!f->is_static)
- fe->ageing_timer_value = jiffies_to_clock_t(jiffies - f->ageing_timer);
+ fe->ageing_timer_value = jiffies_to_clock_t(jiffies - f->updated);
++fe;
++num;
}
@@ -305,8 +307,21 @@
return num;
}
-static inline struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head,
- const unsigned char *addr)
+static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head,
+ const unsigned char *addr)
+{
+ struct hlist_node *h;
+ struct net_bridge_fdb_entry *fdb;
+
+ hlist_for_each_entry(fdb, h, head, hlist) {
+ if (!compare_ether_addr(fdb->addr.addr, addr))
+ return fdb;
+ }
+ return NULL;
+}
+
+static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head,
+ const unsigned char *addr)
{
struct hlist_node *h;
struct net_bridge_fdb_entry *fdb;
@@ -320,8 +335,7 @@
static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
struct net_bridge_port *source,
- const unsigned char *addr,
- int is_local)
+ const unsigned char *addr)
{
struct net_bridge_fdb_entry *fdb;
@@ -329,11 +343,11 @@
if (fdb) {
memcpy(fdb->addr.addr, addr, ETH_ALEN);
fdb->dst = source;
- fdb->is_local = is_local;
- fdb->is_static = is_local;
- fdb->ageing_timer = jiffies;
-
+ fdb->is_local = 0;
+ fdb->is_static = 0;
+ fdb->updated = fdb->used = jiffies;
hlist_add_head_rcu(&fdb->hlist, head);
+ fdb_notify(fdb, RTM_NEWNEIGH);
}
return fdb;
}
@@ -360,12 +374,15 @@
fdb_delete(fdb);
}
- if (!fdb_create(head, source, addr, 1))
+ fdb = fdb_create(head, source, addr);
+ if (!fdb)
return -ENOMEM;
+ fdb->is_local = fdb->is_static = 1;
return 0;
}
+/* Add entry for local address of interface */
int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
const unsigned char *addr)
{
@@ -392,7 +409,7 @@
source->state == BR_STATE_FORWARDING))
return;
- fdb = fdb_find(head, addr);
+ fdb = fdb_find_rcu(head, addr);
if (likely(fdb)) {
/* attempt to update an entry for a local interface */
if (unlikely(fdb->is_local)) {
@@ -403,15 +420,277 @@
} else {
/* fastpath: update of existing entry */
fdb->dst = source;
- fdb->ageing_timer = jiffies;
+ fdb->updated = jiffies;
}
} else {
spin_lock(&br->hash_lock);
- if (!fdb_find(head, addr))
- fdb_create(head, source, addr, 0);
+ if (likely(!fdb_find(head, addr)))
+ fdb_create(head, source, addr);
+
/* else we lose race and someone else inserts
* it first, don't bother updating
*/
spin_unlock(&br->hash_lock);
}
}
+
+static int fdb_to_nud(const struct net_bridge_fdb_entry *fdb)
+{
+ if (fdb->is_local)
+ return NUD_PERMANENT;
+ else if (fdb->is_static)
+ return NUD_NOARP;
+ else if (has_expired(fdb->dst->br, fdb))
+ return NUD_STALE;
+ else
+ return NUD_REACHABLE;
+}
+
+static int fdb_fill_info(struct sk_buff *skb,
+ const struct net_bridge_fdb_entry *fdb,
+ u32 pid, u32 seq, int type, unsigned int flags)
+{
+ unsigned long now = jiffies;
+ struct nda_cacheinfo ci;
+ struct nlmsghdr *nlh;
+ struct ndmsg *ndm;
+
+ nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+
+ ndm = nlmsg_data(nlh);
+ ndm->ndm_family = AF_BRIDGE;
+ ndm->ndm_pad1 = 0;
+ ndm->ndm_pad2 = 0;
+ ndm->ndm_flags = 0;
+ ndm->ndm_type = 0;
+ ndm->ndm_ifindex = fdb->dst->dev->ifindex;
+ ndm->ndm_state = fdb_to_nud(fdb);
+
+ NLA_PUT(skb, NDA_LLADDR, ETH_ALEN, &fdb->addr);
+
+ ci.ndm_used = jiffies_to_clock_t(now - fdb->used);
+ ci.ndm_confirmed = 0;
+ ci.ndm_updated = jiffies_to_clock_t(now - fdb->updated);
+ ci.ndm_refcnt = 0;
+ NLA_PUT(skb, NDA_CACHEINFO, sizeof(ci), &ci);
+
+ return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+}
+
+static inline size_t fdb_nlmsg_size(void)
+{
+ return NLMSG_ALIGN(sizeof(struct ndmsg))
+ + nla_total_size(ETH_ALEN) /* NDA_LLADDR */
+ + nla_total_size(sizeof(struct nda_cacheinfo));
+}
+
+static void fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
+{
+ struct net *net = dev_net(fdb->dst->dev);
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC);
+ if (skb == NULL)
+ goto errout;
+
+ err = fdb_fill_info(skb, fdb, 0, 0, type, 0);
+ if (err < 0) {
+ /* -EMSGSIZE implies BUG in fdb_nlmsg_size() */
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(skb);
+ goto errout;
+ }
+ rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
+ return;
+errout:
+ if (err < 0)
+ rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
+}
+
+/* Dump information about entries, in response to GETNEIGH */
+int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct net_device *dev;
+ int idx = 0;
+
+ rcu_read_lock();
+ for_each_netdev_rcu(net, dev) {
+ struct net_bridge *br = netdev_priv(dev);
+ int i;
+
+ if (!(dev->priv_flags & IFF_EBRIDGE))
+ continue;
+
+ for (i = 0; i < BR_HASH_SIZE; i++) {
+ struct hlist_node *h;
+ struct net_bridge_fdb_entry *f;
+
+ hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) {
+ if (idx < cb->args[0])
+ goto skip;
+
+ if (fdb_fill_info(skb, f,
+ NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq,
+ RTM_NEWNEIGH,
+ NLM_F_MULTI) < 0)
+ break;
+skip:
+ ++idx;
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ cb->args[0] = idx;
+
+ return skb->len;
+}
+
+/* Create new static fdb entry */
+static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
+ __u16 state)
+{
+ struct net_bridge *br = source->br;
+ struct hlist_head *head = &br->hash[br_mac_hash(addr)];
+ struct net_bridge_fdb_entry *fdb;
+
+ fdb = fdb_find(head, addr);
+ if (fdb)
+ return -EEXIST;
+
+ fdb = fdb_create(head, source, addr);
+ if (!fdb)
+ return -ENOMEM;
+
+ if (state & NUD_PERMANENT)
+ fdb->is_local = fdb->is_static = 1;
+ else if (state & NUD_NOARP)
+ fdb->is_static = 1;
+ return 0;
+}
+
+/* Add new permanent fdb entry with RTM_NEWNEIGH */
+int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct net *net = sock_net(skb->sk);
+ struct ndmsg *ndm;
+ struct nlattr *tb[NDA_MAX+1];
+ struct net_device *dev;
+ struct net_bridge_port *p;
+ const __u8 *addr;
+ int err;
+
+ ASSERT_RTNL();
+ err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
+ if (err < 0)
+ return err;
+
+ ndm = nlmsg_data(nlh);
+ if (ndm->ndm_ifindex == 0) {
+ pr_info("bridge: RTM_NEWNEIGH with invalid ifindex\n");
+ return -EINVAL;
+ }
+
+ dev = __dev_get_by_index(net, ndm->ndm_ifindex);
+ if (dev == NULL) {
+ pr_info("bridge: RTM_NEWNEIGH with unknown ifindex\n");
+ return -ENODEV;
+ }
+
+ if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) {
+ pr_info("bridge: RTM_NEWNEIGH with invalid address\n");
+ return -EINVAL;
+ }
+
+ addr = nla_data(tb[NDA_LLADDR]);
+ if (!is_valid_ether_addr(addr)) {
+ pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n");
+ return -EINVAL;
+ }
+
+ p = br_port_get_rtnl(dev);
+ if (p == NULL) {
+ pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+ spin_lock_bh(&p->br->hash_lock);
+ err = fdb_add_entry(p, addr, ndm->ndm_state);
+ spin_unlock_bh(&p->br->hash_lock);
+
+ return err;
+}
+
+static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr)
+{
+ struct net_bridge *br = p->br;
+ struct hlist_head *head = &br->hash[br_mac_hash(addr)];
+ struct net_bridge_fdb_entry *fdb;
+
+ fdb = fdb_find(head, addr);
+ if (!fdb)
+ return -ENOENT;
+
+ fdb_delete(fdb);
+ return 0;
+}
+
+/* Remove neighbor entry with RTM_DELNEIGH */
+int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct net *net = sock_net(skb->sk);
+ struct ndmsg *ndm;
+ struct net_bridge_port *p;
+ struct nlattr *llattr;
+ const __u8 *addr;
+ struct net_device *dev;
+ int err;
+
+ ASSERT_RTNL();
+ if (nlmsg_len(nlh) < sizeof(*ndm))
+ return -EINVAL;
+
+ ndm = nlmsg_data(nlh);
+ if (ndm->ndm_ifindex == 0) {
+ pr_info("bridge: RTM_DELNEIGH with invalid ifindex\n");
+ return -EINVAL;
+ }
+
+ dev = __dev_get_by_index(net, ndm->ndm_ifindex);
+ if (dev == NULL) {
+ pr_info("bridge: RTM_DELNEIGH with unknown ifindex\n");
+ return -ENODEV;
+ }
+
+ llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR);
+ if (llattr == NULL || nla_len(llattr) != ETH_ALEN) {
+ pr_info("bridge: RTM_DELNEIGH with invalid address\n");
+ return -EINVAL;
+ }
+
+ addr = nla_data(llattr);
+
+ p = br_port_get_rtnl(dev);
+ if (p == NULL) {
+ pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+ spin_lock_bh(&p->br->hash_lock);
+ err = fdb_delete_by_addr(p, addr);
+ spin_unlock_bh(&p->br->hash_lock);
+
+ return err;
+}
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 718b603..7f5379c 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -175,56 +175,6 @@
unregister_netdevice_queue(br->dev, head);
}
-static struct net_device *new_bridge_dev(struct net *net, const char *name)
-{
- struct net_bridge *br;
- struct net_device *dev;
-
- dev = alloc_netdev(sizeof(struct net_bridge), name,
- br_dev_setup);
-
- if (!dev)
- return NULL;
- dev_net_set(dev, net);
-
- br = netdev_priv(dev);
- br->dev = dev;
-
- br->stats = alloc_percpu(struct br_cpu_netstats);
- if (!br->stats) {
- free_netdev(dev);
- return NULL;
- }
-
- spin_lock_init(&br->lock);
- INIT_LIST_HEAD(&br->port_list);
- spin_lock_init(&br->hash_lock);
-
- br->bridge_id.prio[0] = 0x80;
- br->bridge_id.prio[1] = 0x00;
-
- memcpy(br->group_addr, br_group_address, ETH_ALEN);
-
- br->feature_mask = dev->features;
- br->stp_enabled = BR_NO_STP;
- br->designated_root = br->bridge_id;
- br->root_path_cost = 0;
- br->root_port = 0;
- br->bridge_max_age = br->max_age = 20 * HZ;
- br->bridge_hello_time = br->hello_time = 2 * HZ;
- br->bridge_forward_delay = br->forward_delay = 15 * HZ;
- br->topology_change = 0;
- br->topology_change_detected = 0;
- br->ageing_time = 300 * HZ;
-
- br_netfilter_rtable_init(br);
-
- br_stp_timer_init(br);
- br_multicast_init(br);
-
- return dev;
-}
-
/* find an available port number */
static int find_portno(struct net_bridge *br)
{
@@ -277,42 +227,19 @@
return p;
}
-static struct device_type br_type = {
- .name = "bridge",
-};
-
int br_add_bridge(struct net *net, const char *name)
{
struct net_device *dev;
- int ret;
- dev = new_bridge_dev(net, name);
+ dev = alloc_netdev(sizeof(struct net_bridge), name,
+ br_dev_setup);
+
if (!dev)
return -ENOMEM;
- rtnl_lock();
- if (strchr(dev->name, '%')) {
- ret = dev_alloc_name(dev, dev->name);
- if (ret < 0)
- goto out_free;
- }
+ dev_net_set(dev, net);
- SET_NETDEV_DEVTYPE(dev, &br_type);
-
- ret = register_netdevice(dev);
- if (ret)
- goto out_free;
-
- ret = br_sysfs_addbr(dev);
- if (ret)
- unregister_netdevice(dev);
- out:
- rtnl_unlock();
- return ret;
-
-out_free:
- free_netdev(dev);
- goto out;
+ return register_netdev(dev);
}
int br_del_bridge(struct net *net, const char *name)
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index e216079..785932d 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -98,9 +98,10 @@
}
if (skb) {
- if (dst)
+ if (dst) {
+ dst->used = jiffies;
br_forward(dst->dst, skb, skb2);
- else
+ } else
br_flood_forward(br, skb, skb2);
}
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index cb43312..0459890 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -181,40 +181,19 @@
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- spin_lock_bh(&br->lock);
- br->bridge_forward_delay = clock_t_to_jiffies(args[1]);
- if (br_is_root_bridge(br))
- br->forward_delay = br->bridge_forward_delay;
- spin_unlock_bh(&br->lock);
- return 0;
+ return br_set_forward_delay(br, args[1]);
case BRCTL_SET_BRIDGE_HELLO_TIME:
- {
- unsigned long t = clock_t_to_jiffies(args[1]);
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- if (t < HZ)
- return -EINVAL;
-
- spin_lock_bh(&br->lock);
- br->bridge_hello_time = t;
- if (br_is_root_bridge(br))
- br->hello_time = br->bridge_hello_time;
- spin_unlock_bh(&br->lock);
- return 0;
- }
+ return br_set_hello_time(br, args[1]);
case BRCTL_SET_BRIDGE_MAX_AGE:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- spin_lock_bh(&br->lock);
- br->bridge_max_age = clock_t_to_jiffies(args[1]);
- if (br_is_root_bridge(br))
- br->max_age = br->bridge_max_age;
- spin_unlock_bh(&br->lock);
- return 0;
+ return br_set_max_age(br, args[1]);
case BRCTL_SET_AGEING_TIME:
if (!capable(CAP_NET_ADMIN))
@@ -275,19 +254,16 @@
case BRCTL_SET_PORT_PRIORITY:
{
struct net_bridge_port *p;
- int ret = 0;
+ int ret;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- if (args[2] >= (1<<(16-BR_PORT_BITS)))
- return -ERANGE;
-
spin_lock_bh(&br->lock);
if ((p = br_get_port(br, args[1])) == NULL)
ret = -EINVAL;
else
- br_stp_set_port_priority(p, args[2]);
+ ret = br_stp_set_port_priority(p, args[2]);
spin_unlock_bh(&br->lock);
return ret;
}
@@ -295,15 +271,17 @@
case BRCTL_SET_PATH_COST:
{
struct net_bridge_port *p;
- int ret = 0;
+ int ret;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
+ spin_lock_bh(&br->lock);
if ((p = br_get_port(br, args[1])) == NULL)
ret = -EINVAL;
else
- br_stp_set_path_cost(p, args[2]);
+ ret = br_stp_set_path_cost(p, args[2]);
+ spin_unlock_bh(&br->lock);
return ret;
}
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index f8bf4c7..134a2ff 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -12,9 +12,11 @@
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/etherdevice.h>
#include <net/rtnetlink.h>
#include <net/net_namespace.h>
#include <net/sock.h>
+
#include "br_private.h"
static inline size_t br_nlmsg_size(void)
@@ -188,20 +190,61 @@
return 0;
}
-
-int __init br_netlink_init(void)
+static int br_validate(struct nlattr *tb[], struct nlattr *data[])
{
- if (__rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo))
- return -ENOBUFS;
-
- /* Only the first call to __rtnl_register can fail */
- __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL);
+ if (tb[IFLA_ADDRESS]) {
+ if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
+ return -EINVAL;
+ if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
+ return -EADDRNOTAVAIL;
+ }
return 0;
}
-void __exit br_netlink_fini(void)
+static struct rtnl_link_ops br_link_ops __read_mostly = {
+ .kind = "bridge",
+ .priv_size = sizeof(struct net_bridge),
+ .setup = br_dev_setup,
+ .validate = br_validate,
+};
+
+int __init br_netlink_init(void)
{
+ int err;
+
+ err = rtnl_link_register(&br_link_ops);
+ if (err < 0)
+ goto err1;
+
+ err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo);
+ if (err)
+ goto err2;
+ err = __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL);
+ if (err)
+ goto err3;
+ err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, br_fdb_add, NULL);
+ if (err)
+ goto err3;
+ err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, br_fdb_delete, NULL);
+ if (err)
+ goto err3;
+ err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, br_fdb_dump);
+ if (err)
+ goto err3;
+
+ return 0;
+
+err3:
rtnl_unregister_all(PF_BRIDGE);
+err2:
+ rtnl_link_unregister(&br_link_ops);
+err1:
+ return err;
}
+void __exit br_netlink_fini(void)
+{
+ rtnl_link_unregister(&br_link_ops);
+ rtnl_unregister_all(PF_BRIDGE);
+}
diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c
index 7d337c9..7a03bb9 100644
--- a/net/bridge/br_notify.c
+++ b/net/bridge/br_notify.c
@@ -36,6 +36,12 @@
struct net_bridge *br;
int err;
+ /* register of bridge completed, add sysfs entries */
+ if ((dev->priv_flags && IFF_EBRIDGE) && event == NETDEV_REGISTER) {
+ br_sysfs_addbr(dev);
+ return NOTIFY_DONE;
+ }
+
/* not a port of a bridge */
p = br_port_get_rtnl(dev);
if (!p)
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 387013d..e2a4034 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -64,7 +64,8 @@
struct net_bridge_port *dst;
struct rcu_head rcu;
- unsigned long ageing_timer;
+ unsigned long updated;
+ unsigned long used;
mac_addr addr;
unsigned char is_local;
unsigned char is_static;
@@ -353,6 +354,9 @@
extern void br_fdb_update(struct net_bridge *br,
struct net_bridge_port *source,
const unsigned char *addr);
+extern int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb);
+extern int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+extern int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
/* br_forward.c */
extern void br_deliver(const struct net_bridge_port *to,
@@ -491,6 +495,11 @@
extern void br_init_port(struct net_bridge_port *p);
extern void br_become_designated_port(struct net_bridge_port *p);
+extern int br_set_forward_delay(struct net_bridge *br, unsigned long x);
+extern int br_set_hello_time(struct net_bridge *br, unsigned long x);
+extern int br_set_max_age(struct net_bridge *br, unsigned long x);
+
+
/* br_stp_if.c */
extern void br_stp_enable_bridge(struct net_bridge *br);
extern void br_stp_disable_bridge(struct net_bridge *br);
@@ -501,10 +510,10 @@
extern void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *a);
extern void br_stp_set_bridge_priority(struct net_bridge *br,
u16 newprio);
-extern void br_stp_set_port_priority(struct net_bridge_port *p,
- u8 newprio);
-extern void br_stp_set_path_cost(struct net_bridge_port *p,
- u32 path_cost);
+extern int br_stp_set_port_priority(struct net_bridge_port *p,
+ unsigned long newprio);
+extern int br_stp_set_path_cost(struct net_bridge_port *p,
+ unsigned long path_cost);
extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id);
/* br_stp_bpdu.c */
diff --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h
index 8b650f7..642ef47 100644
--- a/net/bridge/br_private_stp.h
+++ b/net/bridge/br_private_stp.h
@@ -16,6 +16,19 @@
#define BPDU_TYPE_CONFIG 0
#define BPDU_TYPE_TCN 0x80
+/* IEEE 802.1D-1998 timer values */
+#define BR_MIN_HELLO_TIME (1*HZ)
+#define BR_MAX_HELLO_TIME (10*HZ)
+
+#define BR_MIN_FORWARD_DELAY (2*HZ)
+#define BR_MAX_FORWARD_DELAY (30*HZ)
+
+#define BR_MIN_MAX_AGE (6*HZ)
+#define BR_MAX_MAX_AGE (40*HZ)
+
+#define BR_MIN_PATH_COST 1
+#define BR_MAX_PATH_COST 65535
+
struct br_config_bpdu
{
unsigned topology_change:1;
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 7370d14..bb4383e 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -484,3 +484,51 @@
br_topology_change_acknowledge(p);
}
}
+
+/* Change bridge STP parameter */
+int br_set_hello_time(struct net_bridge *br, unsigned long val)
+{
+ unsigned long t = clock_t_to_jiffies(val);
+
+ if (t < BR_MIN_HELLO_TIME || t > BR_MAX_HELLO_TIME)
+ return -ERANGE;
+
+ spin_lock_bh(&br->lock);
+ br->bridge_hello_time = t;
+ if (br_is_root_bridge(br))
+ br->hello_time = br->bridge_hello_time;
+ spin_unlock_bh(&br->lock);
+ return 0;
+}
+
+int br_set_max_age(struct net_bridge *br, unsigned long val)
+{
+ unsigned long t = clock_t_to_jiffies(val);
+
+ if (t < BR_MIN_MAX_AGE || t > BR_MAX_MAX_AGE)
+ return -ERANGE;
+
+ spin_lock_bh(&br->lock);
+ br->bridge_max_age = t;
+ if (br_is_root_bridge(br))
+ br->max_age = br->bridge_max_age;
+ spin_unlock_bh(&br->lock);
+ return 0;
+
+}
+
+int br_set_forward_delay(struct net_bridge *br, unsigned long val)
+{
+ unsigned long t = clock_t_to_jiffies(val);
+
+ if (br->stp_enabled != BR_NO_STP &&
+ (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY))
+ return -ERANGE;
+
+ spin_lock_bh(&br->lock);
+ br->bridge_forward_delay = t;
+ if (br_is_root_bridge(br))
+ br->forward_delay = br->bridge_forward_delay;
+ spin_unlock_bh(&br->lock);
+ return 0;
+}
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index 9b61d09..6f615b8 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -20,7 +20,7 @@
/* Port id is composed of priority and port number.
- * NB: least significant bits of priority are dropped to
+ * NB: some bits of priority are dropped to
* make room for more ports.
*/
static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
@@ -29,6 +29,8 @@
| (port_no & ((1<<BR_PORT_BITS)-1));
}
+#define BR_MAX_PORT_PRIORITY ((u16)~0 >> BR_PORT_BITS)
+
/* called under bridge lock */
void br_init_port(struct net_bridge_port *p)
{
@@ -255,10 +257,14 @@
}
/* called under bridge lock */
-void br_stp_set_port_priority(struct net_bridge_port *p, u8 newprio)
+int br_stp_set_port_priority(struct net_bridge_port *p, unsigned long newprio)
{
- port_id new_port_id = br_make_port_id(newprio, p->port_no);
+ port_id new_port_id;
+ if (newprio > BR_MAX_PORT_PRIORITY)
+ return -ERANGE;
+
+ new_port_id = br_make_port_id(newprio, p->port_no);
if (br_is_designated_port(p))
p->designated_port = new_port_id;
@@ -269,14 +275,21 @@
br_become_designated_port(p);
br_port_state_selection(p->br);
}
+
+ return 0;
}
/* called under bridge lock */
-void br_stp_set_path_cost(struct net_bridge_port *p, u32 path_cost)
+int br_stp_set_path_cost(struct net_bridge_port *p, unsigned long path_cost)
{
+ if (path_cost < BR_MIN_PATH_COST ||
+ path_cost > BR_MAX_PATH_COST)
+ return -ERANGE;
+
p->path_cost = path_cost;
br_configuration_update(p->br);
br_port_state_selection(p->br);
+ return 0;
}
ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id)
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 5c1e555..68b893e 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -43,9 +43,7 @@
if (endp == buf)
return -EINVAL;
- spin_lock_bh(&br->lock);
err = (*set)(br, val);
- spin_unlock_bh(&br->lock);
return err ? err : len;
}
@@ -57,20 +55,11 @@
return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));
}
-static int set_forward_delay(struct net_bridge *br, unsigned long val)
-{
- unsigned long delay = clock_t_to_jiffies(val);
- br->forward_delay = delay;
- if (br_is_root_bridge(br))
- br->bridge_forward_delay = delay;
- return 0;
-}
-
static ssize_t store_forward_delay(struct device *d,
struct device_attribute *attr,
const char *buf, size_t len)
{
- return store_bridge_parm(d, buf, len, set_forward_delay);
+ return store_bridge_parm(d, buf, len, br_set_forward_delay);
}
static DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
show_forward_delay, store_forward_delay);
@@ -82,24 +71,11 @@
jiffies_to_clock_t(to_bridge(d)->hello_time));
}
-static int set_hello_time(struct net_bridge *br, unsigned long val)
-{
- unsigned long t = clock_t_to_jiffies(val);
-
- if (t < HZ)
- return -EINVAL;
-
- br->hello_time = t;
- if (br_is_root_bridge(br))
- br->bridge_hello_time = t;
- return 0;
-}
-
static ssize_t store_hello_time(struct device *d,
struct device_attribute *attr, const char *buf,
size_t len)
{
- return store_bridge_parm(d, buf, len, set_hello_time);
+ return store_bridge_parm(d, buf, len, br_set_hello_time);
}
static DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
store_hello_time);
@@ -111,19 +87,10 @@
jiffies_to_clock_t(to_bridge(d)->max_age));
}
-static int set_max_age(struct net_bridge *br, unsigned long val)
-{
- unsigned long t = clock_t_to_jiffies(val);
- br->max_age = t;
- if (br_is_root_bridge(br))
- br->bridge_max_age = t;
- return 0;
-}
-
static ssize_t store_max_age(struct device *d, struct device_attribute *attr,
const char *buf, size_t len)
{
- return store_bridge_parm(d, buf, len, set_max_age);
+ return store_bridge_parm(d, buf, len, br_set_max_age);
}
static DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index fd5799c..6229b62 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -23,7 +23,7 @@
struct brport_attribute {
struct attribute attr;
ssize_t (*show)(struct net_bridge_port *, char *);
- ssize_t (*store)(struct net_bridge_port *, unsigned long);
+ int (*store)(struct net_bridge_port *, unsigned long);
};
#define BRPORT_ATTR(_name,_mode,_show,_store) \
@@ -38,27 +38,17 @@
{
return sprintf(buf, "%d\n", p->path_cost);
}
-static ssize_t store_path_cost(struct net_bridge_port *p, unsigned long v)
-{
- br_stp_set_path_cost(p, v);
- return 0;
-}
+
static BRPORT_ATTR(path_cost, S_IRUGO | S_IWUSR,
- show_path_cost, store_path_cost);
+ show_path_cost, br_stp_set_path_cost);
static ssize_t show_priority(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "%d\n", p->priority);
}
-static ssize_t store_priority(struct net_bridge_port *p, unsigned long v)
-{
- if (v >= (1<<(16-BR_PORT_BITS)))
- return -ERANGE;
- br_stp_set_port_priority(p, v);
- return 0;
-}
+
static BRPORT_ATTR(priority, S_IRUGO | S_IWUSR,
- show_priority, store_priority);
+ show_priority, br_stp_set_port_priority);
static ssize_t show_designated_root(struct net_bridge_port *p, char *buf)
{
@@ -136,7 +126,7 @@
}
static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL);
-static ssize_t store_flush(struct net_bridge_port *p, unsigned long v)
+static int store_flush(struct net_bridge_port *p, unsigned long v)
{
br_fdb_delete_by_port(p->br, p, 0); // Don't delete local entry
return 0;
@@ -148,7 +138,7 @@
int hairpin_mode = (p->flags & BR_HAIRPIN_MODE) ? 1 : 0;
return sprintf(buf, "%d\n", hairpin_mode);
}
-static ssize_t store_hairpin_mode(struct net_bridge_port *p, unsigned long v)
+static int store_hairpin_mode(struct net_bridge_port *p, unsigned long v)
{
if (v)
p->flags |= BR_HAIRPIN_MODE;
@@ -165,7 +155,7 @@
return sprintf(buf, "%d\n", p->multicast_router);
}
-static ssize_t store_multicast_router(struct net_bridge_port *p,
+static int store_multicast_router(struct net_bridge_port *p,
unsigned long v)
{
return br_multicast_set_port_router(p, v);
diff --git a/net/core/dev.c b/net/core/dev.c
index 3da9fb0..5d0b4f6 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5236,7 +5236,7 @@
}
EXPORT_SYMBOL(netdev_fix_features);
-void netdev_update_features(struct net_device *dev)
+int __netdev_update_features(struct net_device *dev)
{
u32 features;
int err = 0;
@@ -5250,7 +5250,7 @@
features = netdev_fix_features(dev, features);
if (dev->features == features)
- return;
+ return 0;
netdev_info(dev, "Features changed: 0x%08x -> 0x%08x\n",
dev->features, features);
@@ -5258,12 +5258,23 @@
if (dev->netdev_ops->ndo_set_features)
err = dev->netdev_ops->ndo_set_features(dev, features);
- if (!err)
- dev->features = features;
- else if (err < 0)
+ if (unlikely(err < 0)) {
netdev_err(dev,
"set_features() failed (%d); wanted 0x%08x, left 0x%08x\n",
err, features, dev->features);
+ return -1;
+ }
+
+ if (!err)
+ dev->features = features;
+
+ return 1;
+}
+
+void netdev_update_features(struct net_device *dev)
+{
+ if (__netdev_update_features(dev))
+ netdev_features_change(dev);
}
EXPORT_SYMBOL(netdev_update_features);
@@ -5414,6 +5425,14 @@
dev->features &= ~NETIF_F_GSO;
}
+ /* Turn on no cache copy if HW is doing checksum */
+ dev->hw_features |= NETIF_F_NOCACHE_COPY;
+ if ((dev->features & NETIF_F_ALL_CSUM) &&
+ !(dev->features & NETIF_F_NO_CSUM)) {
+ dev->wanted_features |= NETIF_F_NOCACHE_COPY;
+ dev->features |= NETIF_F_NOCACHE_COPY;
+ }
+
/* Enable GRO and NETIF_F_HIGHDMA for vlans by default,
* vlan_dev_init() will do the dev->features check, so these features
* are enabled only if supported by underlying device.
@@ -5430,7 +5449,7 @@
goto err_uninit;
dev->reg_state = NETREG_REGISTERED;
- netdev_update_features(dev);
+ __netdev_update_features(dev);
/*
* Default initial state at registry is that the
@@ -6171,6 +6190,10 @@
}
}
+ /* If device can't no cache copy, don't do for all */
+ if (!(one & NETIF_F_NOCACHE_COPY))
+ all &= ~NETIF_F_NOCACHE_COPY;
+
one |= NETIF_F_ALL_CSUM;
one |= all & NETIF_F_ONE_FOR_ALL;
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 1c95f0f..1b7fa98 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -319,7 +319,7 @@
dev->wanted_features &= ~features[0].valid;
dev->wanted_features |= features[0].valid & features[0].requested;
- netdev_update_features(dev);
+ __netdev_update_features(dev);
if ((dev->wanted_features ^ dev->features) & features[0].valid)
ret |= ETHTOOL_F_WISH;
@@ -361,7 +361,7 @@
/* NETIF_F_NTUPLE */ "rx-ntuple-filter",
/* NETIF_F_RXHASH */ "rx-hashing",
/* NETIF_F_RXCSUM */ "rx-checksum",
- "",
+ /* NETIF_F_NOCACHE_COPY */ "tx-nocache-copy"
"",
};
@@ -501,7 +501,7 @@
else
dev->wanted_features &= ~mask;
- netdev_update_features(dev);
+ __netdev_update_features(dev);
return 0;
}
@@ -553,7 +553,7 @@
dev->wanted_features =
(dev->wanted_features & ~changed) | data;
- netdev_update_features(dev);
+ __netdev_update_features(dev);
return 0;
}
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index b22d450..054a59d 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -999,7 +999,8 @@
/* We have some space in skb head. Superb! */
if (copy > skb_tailroom(skb))
copy = skb_tailroom(skb);
- if ((err = skb_add_data(skb, from, copy)) != 0)
+ err = skb_add_data_nocache(sk, skb, from, copy);
+ if (err)
goto do_fault;
} else {
int merge = 0;
@@ -1042,8 +1043,8 @@
/* Time to copy data. We are close to
* the end! */
- err = skb_copy_to_page(sk, from, skb, page,
- off, copy);
+ err = skb_copy_to_page_nocache(sk, from, skb,
+ page, off, copy);
if (err) {
/* If this page was new, give it to the
* socket so it does not get leaked.
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index dfa5beb..8b0d016 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1003,7 +1003,8 @@
int nlen;
u8 flags;
- BUG_ON(len > skb->len);
+ if (WARN_ON(len > skb->len))
+ return -EINVAL;
nsize = skb_headlen(skb) - len;
if (nsize < 0)
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 8c02469..af3c564 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -342,7 +342,7 @@
if (IS_ERR(key->u.ccmp.tfm)) {
err = PTR_ERR(key->u.ccmp.tfm);
kfree(key);
- key = ERR_PTR(err);
+ return ERR_PTR(err);
}
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
@@ -360,7 +360,7 @@
if (IS_ERR(key->u.aes_cmac.tfm)) {
err = PTR_ERR(key->u.aes_cmac.tfm);
kfree(key);
- key = ERR_PTR(err);
+ return ERR_PTR(err);
}
break;
}
@@ -400,11 +400,12 @@
{
struct ieee80211_key *old_key;
int idx, ret;
- bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
+ bool pairwise;
BUG_ON(!sdata);
BUG_ON(!key);
+ pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
idx = key->conf.keyidx;
key->local = sdata->local;
key->sdata = sdata;
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 8212a8b..dbdebeda 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -659,18 +659,14 @@
struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
struct ieee80211_local *local = hw_to_local(mp->hw);
u16 sta_cap = sta->ht_cap.cap;
+ int n_supported = 0;
int ack_dur;
int stbc;
int i;
/* fall back to the old minstrel for legacy stations */
- if (!sta->ht_cap.ht_supported) {
- msp->is_ht = false;
- memset(&msp->legacy, 0, sizeof(msp->legacy));
- msp->legacy.r = msp->ratelist;
- msp->legacy.sample_table = msp->sample_table;
- return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy);
- }
+ if (!sta->ht_cap.ht_supported)
+ goto use_legacy;
BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) !=
MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS);
@@ -725,7 +721,22 @@
mi->groups[i].supported =
mcs->rx_mask[minstrel_mcs_groups[i].streams - 1];
+
+ if (mi->groups[i].supported)
+ n_supported++;
}
+
+ if (!n_supported)
+ goto use_legacy;
+
+ return;
+
+use_legacy:
+ msp->is_ht = false;
+ memset(&msp->legacy, 0, sizeof(msp->legacy));
+ msp->legacy.r = msp->ratelist;
+ msp->legacy.sample_table = msp->sample_table;
+ return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy);
}
static void
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 5c1930ba..aa5cc37 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -612,7 +612,8 @@
skipped++;
continue;
}
- if (!time_after(jiffies, tid_agg_rx->reorder_time[j] +
+ if (skipped &&
+ !time_after(jiffies, tid_agg_rx->reorder_time[j] +
HT_RX_REORDER_BUF_TIMEOUT))
goto set_release_timer;
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index a7a5583d..aeaa211 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -239,6 +239,17 @@
To compile this code as a module, choose M here: the
module will be called sch_choke.
+config NET_SCH_QFQ
+ tristate "Quick Fair Queueing scheduler (QFQ)"
+ help
+ Say Y here if you want to use the Quick Fair Queueing Scheduler (QFQ)
+ packet scheduling algorithm.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sch_qfq.
+
+ If unsure, say N.
+
config NET_SCH_INGRESS
tristate "Ingress Qdisc"
depends on NET_CLS_ACT
diff --git a/net/sched/Makefile b/net/sched/Makefile
index 2e77b8d..dc5889c 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -35,6 +35,7 @@
obj-$(CONFIG_NET_SCH_DRR) += sch_drr.o
obj-$(CONFIG_NET_SCH_MQPRIO) += sch_mqprio.o
obj-$(CONFIG_NET_SCH_CHOKE) += sch_choke.o
+obj-$(CONFIG_NET_SCH_QFQ) += sch_qfq.o
obj-$(CONFIG_NET_CLS_U32) += cls_u32.o
obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
new file mode 100644
index 0000000..1033434
--- /dev/null
+++ b/net/sched/sch_qfq.c
@@ -0,0 +1,1137 @@
+/*
+ * net/sched/sch_qfq.c Quick Fair Queueing Scheduler.
+ *
+ * Copyright (c) 2009 Fabio Checconi, Luigi Rizzo, and Paolo Valente.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/pkt_sched.h>
+#include <net/sch_generic.h>
+#include <net/pkt_sched.h>
+#include <net/pkt_cls.h>
+
+
+/* Quick Fair Queueing
+ ===================
+
+ Sources:
+
+ Fabio Checconi, Luigi Rizzo, and Paolo Valente: "QFQ: Efficient
+ Packet Scheduling with Tight Bandwidth Distribution Guarantees."
+
+ See also:
+ http://retis.sssup.it/~fabio/linux/qfq/
+ */
+
+/*
+
+ Virtual time computations.
+
+ S, F and V are all computed in fixed point arithmetic with
+ FRAC_BITS decimal bits.
+
+ QFQ_MAX_INDEX is the maximum index allowed for a group. We need
+ one bit per index.
+ QFQ_MAX_WSHIFT is the maximum power of two supported as a weight.
+
+ The layout of the bits is as below:
+
+ [ MTU_SHIFT ][ FRAC_BITS ]
+ [ MAX_INDEX ][ MIN_SLOT_SHIFT ]
+ ^.__grp->index = 0
+ *.__grp->slot_shift
+
+ where MIN_SLOT_SHIFT is derived by difference from the others.
+
+ The max group index corresponds to Lmax/w_min, where
+ Lmax=1<<MTU_SHIFT, w_min = 1 .
+ From this, and knowing how many groups (MAX_INDEX) we want,
+ we can derive the shift corresponding to each group.
+
+ Because we often need to compute
+ F = S + len/w_i and V = V + len/wsum
+ instead of storing w_i store the value
+ inv_w = (1<<FRAC_BITS)/w_i
+ so we can do F = S + len * inv_w * wsum.
+ We use W_TOT in the formulas so we can easily move between
+ static and adaptive weight sum.
+
+ The per-scheduler-instance data contain all the data structures
+ for the scheduler: bitmaps and bucket lists.
+
+ */
+
+/*
+ * Maximum number of consecutive slots occupied by backlogged classes
+ * inside a group.
+ */
+#define QFQ_MAX_SLOTS 32
+
+/*
+ * Shifts used for class<->group mapping. We allow class weights that are
+ * in the range [1, 2^MAX_WSHIFT], and we try to map each class i to the
+ * group with the smallest index that can support the L_i / r_i configured
+ * for the class.
+ *
+ * grp->index is the index of the group; and grp->slot_shift
+ * is the shift for the corresponding (scaled) sigma_i.
+ */
+#define QFQ_MAX_INDEX 19
+#define QFQ_MAX_WSHIFT 16
+
+#define QFQ_MAX_WEIGHT (1<<QFQ_MAX_WSHIFT)
+#define QFQ_MAX_WSUM (2*QFQ_MAX_WEIGHT)
+
+#define FRAC_BITS 30 /* fixed point arithmetic */
+#define ONE_FP (1UL << FRAC_BITS)
+#define IWSUM (ONE_FP/QFQ_MAX_WSUM)
+
+#define QFQ_MTU_SHIFT 11
+#define QFQ_MIN_SLOT_SHIFT (FRAC_BITS + QFQ_MTU_SHIFT - QFQ_MAX_INDEX)
+
+/*
+ * Possible group states. These values are used as indexes for the bitmaps
+ * array of struct qfq_queue.
+ */
+enum qfq_state { ER, IR, EB, IB, QFQ_MAX_STATE };
+
+struct qfq_group;
+
+struct qfq_class {
+ struct Qdisc_class_common common;
+
+ unsigned int refcnt;
+ unsigned int filter_cnt;
+
+ struct gnet_stats_basic_packed bstats;
+ struct gnet_stats_queue qstats;
+ struct gnet_stats_rate_est rate_est;
+ struct Qdisc *qdisc;
+
+ struct hlist_node next; /* Link for the slot list. */
+ u64 S, F; /* flow timestamps (exact) */
+
+ /* group we belong to. In principle we would need the index,
+ * which is log_2(lmax/weight), but we never reference it
+ * directly, only the group.
+ */
+ struct qfq_group *grp;
+
+ /* these are copied from the flowset. */
+ u32 inv_w; /* ONE_FP/weight */
+ u32 lmax; /* Max packet size for this flow. */
+};
+
+struct qfq_group {
+ u64 S, F; /* group timestamps (approx). */
+ unsigned int slot_shift; /* Slot shift. */
+ unsigned int index; /* Group index. */
+ unsigned int front; /* Index of the front slot. */
+ unsigned long full_slots; /* non-empty slots */
+
+ /* Array of RR lists of active classes. */
+ struct hlist_head slots[QFQ_MAX_SLOTS];
+};
+
+struct qfq_sched {
+ struct tcf_proto *filter_list;
+ struct Qdisc_class_hash clhash;
+
+ u64 V; /* Precise virtual time. */
+ u32 wsum; /* weight sum */
+
+ unsigned long bitmaps[QFQ_MAX_STATE]; /* Group bitmaps. */
+ struct qfq_group groups[QFQ_MAX_INDEX + 1]; /* The groups. */
+};
+
+static struct qfq_class *qfq_find_class(struct Qdisc *sch, u32 classid)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct Qdisc_class_common *clc;
+
+ clc = qdisc_class_find(&q->clhash, classid);
+ if (clc == NULL)
+ return NULL;
+ return container_of(clc, struct qfq_class, common);
+}
+
+static void qfq_purge_queue(struct qfq_class *cl)
+{
+ unsigned int len = cl->qdisc->q.qlen;
+
+ qdisc_reset(cl->qdisc);
+ qdisc_tree_decrease_qlen(cl->qdisc, len);
+}
+
+static const struct nla_policy qfq_policy[TCA_QFQ_MAX + 1] = {
+ [TCA_QFQ_WEIGHT] = { .type = NLA_U32 },
+ [TCA_QFQ_LMAX] = { .type = NLA_U32 },
+};
+
+/*
+ * Calculate a flow index, given its weight and maximum packet length.
+ * index = log_2(maxlen/weight) but we need to apply the scaling.
+ * This is used only once at flow creation.
+ */
+static int qfq_calc_index(u32 inv_w, unsigned int maxlen)
+{
+ u64 slot_size = (u64)maxlen * inv_w;
+ unsigned long size_map;
+ int index = 0;
+
+ size_map = slot_size >> QFQ_MIN_SLOT_SHIFT;
+ if (!size_map)
+ goto out;
+
+ index = __fls(size_map) + 1; /* basically a log_2 */
+ index -= !(slot_size - (1ULL << (index + QFQ_MIN_SLOT_SHIFT - 1)));
+
+ if (index < 0)
+ index = 0;
+out:
+ pr_debug("qfq calc_index: W = %lu, L = %u, I = %d\n",
+ (unsigned long) ONE_FP/inv_w, maxlen, index);
+
+ return index;
+}
+
+static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
+ struct nlattr **tca, unsigned long *arg)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct qfq_class *cl = (struct qfq_class *)*arg;
+ struct nlattr *tb[TCA_QFQ_MAX + 1];
+ u32 weight, lmax, inv_w;
+ int i, err;
+
+ if (tca[TCA_OPTIONS] == NULL) {
+ pr_notice("qfq: no options\n");
+ return -EINVAL;
+ }
+
+ err = nla_parse_nested(tb, TCA_QFQ_MAX, tca[TCA_OPTIONS], qfq_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[TCA_QFQ_WEIGHT]) {
+ weight = nla_get_u32(tb[TCA_QFQ_WEIGHT]);
+ if (!weight || weight > (1UL << QFQ_MAX_WSHIFT)) {
+ pr_notice("qfq: invalid weight %u\n", weight);
+ return -EINVAL;
+ }
+ } else
+ weight = 1;
+
+ inv_w = ONE_FP / weight;
+ weight = ONE_FP / inv_w;
+ if (q->wsum + weight > QFQ_MAX_WSUM) {
+ pr_notice("qfq: total weight out of range (%u + %u)\n",
+ weight, q->wsum);
+ return -EINVAL;
+ }
+
+ if (tb[TCA_QFQ_LMAX]) {
+ lmax = nla_get_u32(tb[TCA_QFQ_LMAX]);
+ if (!lmax || lmax > (1UL << QFQ_MTU_SHIFT)) {
+ pr_notice("qfq: invalid max length %u\n", lmax);
+ return -EINVAL;
+ }
+ } else
+ lmax = 1UL << QFQ_MTU_SHIFT;
+
+ if (cl != NULL) {
+ if (tca[TCA_RATE]) {
+ err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
+ qdisc_root_sleeping_lock(sch),
+ tca[TCA_RATE]);
+ if (err)
+ return err;
+ }
+
+ sch_tree_lock(sch);
+ if (tb[TCA_QFQ_WEIGHT]) {
+ q->wsum = weight - ONE_FP / cl->inv_w;
+ cl->inv_w = inv_w;
+ }
+ sch_tree_unlock(sch);
+
+ return 0;
+ }
+
+ cl = kzalloc(sizeof(struct qfq_class), GFP_KERNEL);
+ if (cl == NULL)
+ return -ENOBUFS;
+
+ cl->refcnt = 1;
+ cl->common.classid = classid;
+ cl->lmax = lmax;
+ cl->inv_w = inv_w;
+ i = qfq_calc_index(cl->inv_w, cl->lmax);
+
+ cl->grp = &q->groups[i];
+ q->wsum += weight;
+
+ cl->qdisc = qdisc_create_dflt(sch->dev_queue,
+ &pfifo_qdisc_ops, classid);
+ if (cl->qdisc == NULL)
+ cl->qdisc = &noop_qdisc;
+
+ if (tca[TCA_RATE]) {
+ err = gen_new_estimator(&cl->bstats, &cl->rate_est,
+ qdisc_root_sleeping_lock(sch),
+ tca[TCA_RATE]);
+ if (err) {
+ qdisc_destroy(cl->qdisc);
+ kfree(cl);
+ return err;
+ }
+ }
+
+ sch_tree_lock(sch);
+ qdisc_class_hash_insert(&q->clhash, &cl->common);
+ sch_tree_unlock(sch);
+
+ qdisc_class_hash_grow(sch, &q->clhash);
+
+ *arg = (unsigned long)cl;
+ return 0;
+}
+
+static void qfq_destroy_class(struct Qdisc *sch, struct qfq_class *cl)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+
+ if (cl->inv_w) {
+ q->wsum -= ONE_FP / cl->inv_w;
+ cl->inv_w = 0;
+ }
+
+ gen_kill_estimator(&cl->bstats, &cl->rate_est);
+ qdisc_destroy(cl->qdisc);
+ kfree(cl);
+}
+
+static int qfq_delete_class(struct Qdisc *sch, unsigned long arg)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct qfq_class *cl = (struct qfq_class *)arg;
+
+ if (cl->filter_cnt > 0)
+ return -EBUSY;
+
+ sch_tree_lock(sch);
+
+ qfq_purge_queue(cl);
+ qdisc_class_hash_remove(&q->clhash, &cl->common);
+
+ BUG_ON(--cl->refcnt == 0);
+ /*
+ * This shouldn't happen: we "hold" one cops->get() when called
+ * from tc_ctl_tclass; the destroy method is done from cops->put().
+ */
+
+ sch_tree_unlock(sch);
+ return 0;
+}
+
+static unsigned long qfq_get_class(struct Qdisc *sch, u32 classid)
+{
+ struct qfq_class *cl = qfq_find_class(sch, classid);
+
+ if (cl != NULL)
+ cl->refcnt++;
+
+ return (unsigned long)cl;
+}
+
+static void qfq_put_class(struct Qdisc *sch, unsigned long arg)
+{
+ struct qfq_class *cl = (struct qfq_class *)arg;
+
+ if (--cl->refcnt == 0)
+ qfq_destroy_class(sch, cl);
+}
+
+static struct tcf_proto **qfq_tcf_chain(struct Qdisc *sch, unsigned long cl)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+
+ if (cl)
+ return NULL;
+
+ return &q->filter_list;
+}
+
+static unsigned long qfq_bind_tcf(struct Qdisc *sch, unsigned long parent,
+ u32 classid)
+{
+ struct qfq_class *cl = qfq_find_class(sch, classid);
+
+ if (cl != NULL)
+ cl->filter_cnt++;
+
+ return (unsigned long)cl;
+}
+
+static void qfq_unbind_tcf(struct Qdisc *sch, unsigned long arg)
+{
+ struct qfq_class *cl = (struct qfq_class *)arg;
+
+ cl->filter_cnt--;
+}
+
+static int qfq_graft_class(struct Qdisc *sch, unsigned long arg,
+ struct Qdisc *new, struct Qdisc **old)
+{
+ struct qfq_class *cl = (struct qfq_class *)arg;
+
+ if (new == NULL) {
+ new = qdisc_create_dflt(sch->dev_queue,
+ &pfifo_qdisc_ops, cl->common.classid);
+ if (new == NULL)
+ new = &noop_qdisc;
+ }
+
+ sch_tree_lock(sch);
+ qfq_purge_queue(cl);
+ *old = cl->qdisc;
+ cl->qdisc = new;
+ sch_tree_unlock(sch);
+ return 0;
+}
+
+static struct Qdisc *qfq_class_leaf(struct Qdisc *sch, unsigned long arg)
+{
+ struct qfq_class *cl = (struct qfq_class *)arg;
+
+ return cl->qdisc;
+}
+
+static int qfq_dump_class(struct Qdisc *sch, unsigned long arg,
+ struct sk_buff *skb, struct tcmsg *tcm)
+{
+ struct qfq_class *cl = (struct qfq_class *)arg;
+ struct nlattr *nest;
+
+ tcm->tcm_parent = TC_H_ROOT;
+ tcm->tcm_handle = cl->common.classid;
+ tcm->tcm_info = cl->qdisc->handle;
+
+ nest = nla_nest_start(skb, TCA_OPTIONS);
+ if (nest == NULL)
+ goto nla_put_failure;
+ NLA_PUT_U32(skb, TCA_QFQ_WEIGHT, ONE_FP/cl->inv_w);
+ NLA_PUT_U32(skb, TCA_QFQ_LMAX, cl->lmax);
+ return nla_nest_end(skb, nest);
+
+nla_put_failure:
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+}
+
+static int qfq_dump_class_stats(struct Qdisc *sch, unsigned long arg,
+ struct gnet_dump *d)
+{
+ struct qfq_class *cl = (struct qfq_class *)arg;
+ struct tc_qfq_stats xstats;
+
+ memset(&xstats, 0, sizeof(xstats));
+ cl->qdisc->qstats.qlen = cl->qdisc->q.qlen;
+
+ xstats.weight = ONE_FP/cl->inv_w;
+ xstats.lmax = cl->lmax;
+
+ if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
+ gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 ||
+ gnet_stats_copy_queue(d, &cl->qdisc->qstats) < 0)
+ return -1;
+
+ return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
+}
+
+static void qfq_walk(struct Qdisc *sch, struct qdisc_walker *arg)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct qfq_class *cl;
+ struct hlist_node *n;
+ unsigned int i;
+
+ if (arg->stop)
+ return;
+
+ for (i = 0; i < q->clhash.hashsize; i++) {
+ hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode) {
+ if (arg->count < arg->skip) {
+ arg->count++;
+ continue;
+ }
+ if (arg->fn(sch, (unsigned long)cl, arg) < 0) {
+ arg->stop = 1;
+ return;
+ }
+ arg->count++;
+ }
+ }
+}
+
+static struct qfq_class *qfq_classify(struct sk_buff *skb, struct Qdisc *sch,
+ int *qerr)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct qfq_class *cl;
+ struct tcf_result res;
+ int result;
+
+ if (TC_H_MAJ(skb->priority ^ sch->handle) == 0) {
+ pr_debug("qfq_classify: found %d\n", skb->priority);
+ cl = qfq_find_class(sch, skb->priority);
+ if (cl != NULL)
+ return cl;
+ }
+
+ *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
+ result = tc_classify(skb, q->filter_list, &res);
+ if (result >= 0) {
+#ifdef CONFIG_NET_CLS_ACT
+ switch (result) {
+ case TC_ACT_QUEUED:
+ case TC_ACT_STOLEN:
+ *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
+ case TC_ACT_SHOT:
+ return NULL;
+ }
+#endif
+ cl = (struct qfq_class *)res.class;
+ if (cl == NULL)
+ cl = qfq_find_class(sch, res.classid);
+ return cl;
+ }
+
+ return NULL;
+}
+
+/* Generic comparison function, handling wraparound. */
+static inline int qfq_gt(u64 a, u64 b)
+{
+ return (s64)(a - b) > 0;
+}
+
+/* Round a precise timestamp to its slotted value. */
+static inline u64 qfq_round_down(u64 ts, unsigned int shift)
+{
+ return ts & ~((1ULL << shift) - 1);
+}
+
+/* return the pointer to the group with lowest index in the bitmap */
+static inline struct qfq_group *qfq_ffs(struct qfq_sched *q,
+ unsigned long bitmap)
+{
+ int index = __ffs(bitmap);
+ return &q->groups[index];
+}
+/* Calculate a mask to mimic what would be ffs_from(). */
+static inline unsigned long mask_from(unsigned long bitmap, int from)
+{
+ return bitmap & ~((1UL << from) - 1);
+}
+
+/*
+ * The state computation relies on ER=0, IR=1, EB=2, IB=3
+ * First compute eligibility comparing grp->S, q->V,
+ * then check if someone is blocking us and possibly add EB
+ */
+static int qfq_calc_state(struct qfq_sched *q, const struct qfq_group *grp)
+{
+ /* if S > V we are not eligible */
+ unsigned int state = qfq_gt(grp->S, q->V);
+ unsigned long mask = mask_from(q->bitmaps[ER], grp->index);
+ struct qfq_group *next;
+
+ if (mask) {
+ next = qfq_ffs(q, mask);
+ if (qfq_gt(grp->F, next->F))
+ state |= EB;
+ }
+
+ return state;
+}
+
+
+/*
+ * In principle
+ * q->bitmaps[dst] |= q->bitmaps[src] & mask;
+ * q->bitmaps[src] &= ~mask;
+ * but we should make sure that src != dst
+ */
+static inline void qfq_move_groups(struct qfq_sched *q, unsigned long mask,
+ int src, int dst)
+{
+ q->bitmaps[dst] |= q->bitmaps[src] & mask;
+ q->bitmaps[src] &= ~mask;
+}
+
+static void qfq_unblock_groups(struct qfq_sched *q, int index, u64 old_F)
+{
+ unsigned long mask = mask_from(q->bitmaps[ER], index + 1);
+ struct qfq_group *next;
+
+ if (mask) {
+ next = qfq_ffs(q, mask);
+ if (!qfq_gt(next->F, old_F))
+ return;
+ }
+
+ mask = (1UL << index) - 1;
+ qfq_move_groups(q, mask, EB, ER);
+ qfq_move_groups(q, mask, IB, IR);
+}
+
+/*
+ * perhaps
+ *
+ old_V ^= q->V;
+ old_V >>= QFQ_MIN_SLOT_SHIFT;
+ if (old_V) {
+ ...
+ }
+ *
+ */
+static void qfq_make_eligible(struct qfq_sched *q, u64 old_V)
+{
+ unsigned long vslot = q->V >> QFQ_MIN_SLOT_SHIFT;
+ unsigned long old_vslot = old_V >> QFQ_MIN_SLOT_SHIFT;
+
+ if (vslot != old_vslot) {
+ unsigned long mask = (1UL << fls(vslot ^ old_vslot)) - 1;
+ qfq_move_groups(q, mask, IR, ER);
+ qfq_move_groups(q, mask, IB, EB);
+ }
+}
+
+
+/*
+ * XXX we should make sure that slot becomes less than 32.
+ * This is guaranteed by the input values.
+ * roundedS is always cl->S rounded on grp->slot_shift bits.
+ */
+static void qfq_slot_insert(struct qfq_group *grp, struct qfq_class *cl,
+ u64 roundedS)
+{
+ u64 slot = (roundedS - grp->S) >> grp->slot_shift;
+ unsigned int i = (grp->front + slot) % QFQ_MAX_SLOTS;
+
+ hlist_add_head(&cl->next, &grp->slots[i]);
+ __set_bit(slot, &grp->full_slots);
+}
+
+/* Maybe introduce hlist_first_entry?? */
+static struct qfq_class *qfq_slot_head(struct qfq_group *grp)
+{
+ return hlist_entry(grp->slots[grp->front].first,
+ struct qfq_class, next);
+}
+
+/*
+ * remove the entry from the slot
+ */
+static void qfq_front_slot_remove(struct qfq_group *grp)
+{
+ struct qfq_class *cl = qfq_slot_head(grp);
+
+ BUG_ON(!cl);
+ hlist_del(&cl->next);
+ if (hlist_empty(&grp->slots[grp->front]))
+ __clear_bit(0, &grp->full_slots);
+}
+
+/*
+ * Returns the first full queue in a group. As a side effect,
+ * adjust the bucket list so the first non-empty bucket is at
+ * position 0 in full_slots.
+ */
+static struct qfq_class *qfq_slot_scan(struct qfq_group *grp)
+{
+ unsigned int i;
+
+ pr_debug("qfq slot_scan: grp %u full %#lx\n",
+ grp->index, grp->full_slots);
+
+ if (grp->full_slots == 0)
+ return NULL;
+
+ i = __ffs(grp->full_slots); /* zero based */
+ if (i > 0) {
+ grp->front = (grp->front + i) % QFQ_MAX_SLOTS;
+ grp->full_slots >>= i;
+ }
+
+ return qfq_slot_head(grp);
+}
+
+/*
+ * adjust the bucket list. When the start time of a group decreases,
+ * we move the index down (modulo QFQ_MAX_SLOTS) so we don't need to
+ * move the objects. The mask of occupied slots must be shifted
+ * because we use ffs() to find the first non-empty slot.
+ * This covers decreases in the group's start time, but what about
+ * increases of the start time ?
+ * Here too we should make sure that i is less than 32
+ */
+static void qfq_slot_rotate(struct qfq_group *grp, u64 roundedS)
+{
+ unsigned int i = (grp->S - roundedS) >> grp->slot_shift;
+
+ grp->full_slots <<= i;
+ grp->front = (grp->front - i) % QFQ_MAX_SLOTS;
+}
+
+static void qfq_update_eligible(struct qfq_sched *q, u64 old_V)
+{
+ struct qfq_group *grp;
+ unsigned long ineligible;
+
+ ineligible = q->bitmaps[IR] | q->bitmaps[IB];
+ if (ineligible) {
+ if (!q->bitmaps[ER]) {
+ grp = qfq_ffs(q, ineligible);
+ if (qfq_gt(grp->S, q->V))
+ q->V = grp->S;
+ }
+ qfq_make_eligible(q, old_V);
+ }
+}
+
+/* What is length of next packet in queue (0 if queue is empty) */
+static unsigned int qdisc_peek_len(struct Qdisc *sch)
+{
+ struct sk_buff *skb;
+
+ skb = sch->ops->peek(sch);
+ return skb ? qdisc_pkt_len(skb) : 0;
+}
+
+/*
+ * Updates the class, returns true if also the group needs to be updated.
+ */
+static bool qfq_update_class(struct qfq_group *grp, struct qfq_class *cl)
+{
+ unsigned int len = qdisc_peek_len(cl->qdisc);
+
+ cl->S = cl->F;
+ if (!len)
+ qfq_front_slot_remove(grp); /* queue is empty */
+ else {
+ u64 roundedS;
+
+ cl->F = cl->S + (u64)len * cl->inv_w;
+ roundedS = qfq_round_down(cl->S, grp->slot_shift);
+ if (roundedS == grp->S)
+ return false;
+
+ qfq_front_slot_remove(grp);
+ qfq_slot_insert(grp, cl, roundedS);
+ }
+
+ return true;
+}
+
+static struct sk_buff *qfq_dequeue(struct Qdisc *sch)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct qfq_group *grp;
+ struct qfq_class *cl;
+ struct sk_buff *skb;
+ unsigned int len;
+ u64 old_V;
+
+ if (!q->bitmaps[ER])
+ return NULL;
+
+ grp = qfq_ffs(q, q->bitmaps[ER]);
+
+ cl = qfq_slot_head(grp);
+ skb = qdisc_dequeue_peeked(cl->qdisc);
+ if (!skb) {
+ WARN_ONCE(1, "qfq_dequeue: non-workconserving leaf\n");
+ return NULL;
+ }
+
+ sch->q.qlen--;
+ qdisc_bstats_update(sch, skb);
+
+ old_V = q->V;
+ len = qdisc_pkt_len(skb);
+ q->V += (u64)len * IWSUM;
+ pr_debug("qfq dequeue: len %u F %lld now %lld\n",
+ len, (unsigned long long) cl->F, (unsigned long long) q->V);
+
+ if (qfq_update_class(grp, cl)) {
+ u64 old_F = grp->F;
+
+ cl = qfq_slot_scan(grp);
+ if (!cl)
+ __clear_bit(grp->index, &q->bitmaps[ER]);
+ else {
+ u64 roundedS = qfq_round_down(cl->S, grp->slot_shift);
+ unsigned int s;
+
+ if (grp->S == roundedS)
+ goto skip_unblock;
+ grp->S = roundedS;
+ grp->F = roundedS + (2ULL << grp->slot_shift);
+ __clear_bit(grp->index, &q->bitmaps[ER]);
+ s = qfq_calc_state(q, grp);
+ __set_bit(grp->index, &q->bitmaps[s]);
+ }
+
+ qfq_unblock_groups(q, grp->index, old_F);
+ }
+
+skip_unblock:
+ qfq_update_eligible(q, old_V);
+
+ return skb;
+}
+
+/*
+ * Assign a reasonable start time for a new flow k in group i.
+ * Admissible values for \hat(F) are multiples of \sigma_i
+ * no greater than V+\sigma_i . Larger values mean that
+ * we had a wraparound so we consider the timestamp to be stale.
+ *
+ * If F is not stale and F >= V then we set S = F.
+ * Otherwise we should assign S = V, but this may violate
+ * the ordering in ER. So, if we have groups in ER, set S to
+ * the F_j of the first group j which would be blocking us.
+ * We are guaranteed not to move S backward because
+ * otherwise our group i would still be blocked.
+ */
+static void qfq_update_start(struct qfq_sched *q, struct qfq_class *cl)
+{
+ unsigned long mask;
+ uint32_t limit, roundedF;
+ int slot_shift = cl->grp->slot_shift;
+
+ roundedF = qfq_round_down(cl->F, slot_shift);
+ limit = qfq_round_down(q->V, slot_shift) + (1UL << slot_shift);
+
+ if (!qfq_gt(cl->F, q->V) || qfq_gt(roundedF, limit)) {
+ /* timestamp was stale */
+ mask = mask_from(q->bitmaps[ER], cl->grp->index);
+ if (mask) {
+ struct qfq_group *next = qfq_ffs(q, mask);
+ if (qfq_gt(roundedF, next->F)) {
+ cl->S = next->F;
+ return;
+ }
+ }
+ cl->S = q->V;
+ } else /* timestamp is not stale */
+ cl->S = cl->F;
+}
+
+static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct qfq_group *grp;
+ struct qfq_class *cl;
+ int err;
+ u64 roundedS;
+ int s;
+
+ cl = qfq_classify(skb, sch, &err);
+ if (cl == NULL) {
+ if (err & __NET_XMIT_BYPASS)
+ sch->qstats.drops++;
+ kfree_skb(skb);
+ return err;
+ }
+ pr_debug("qfq_enqueue: cl = %x\n", cl->common.classid);
+
+ err = qdisc_enqueue(skb, cl->qdisc);
+ if (unlikely(err != NET_XMIT_SUCCESS)) {
+ pr_debug("qfq_enqueue: enqueue failed %d\n", err);
+ if (net_xmit_drop_count(err)) {
+ cl->qstats.drops++;
+ sch->qstats.drops++;
+ }
+ return err;
+ }
+
+ bstats_update(&cl->bstats, skb);
+ ++sch->q.qlen;
+
+ /* If the new skb is not the head of queue, then done here. */
+ if (cl->qdisc->q.qlen != 1)
+ return err;
+
+ /* If reach this point, queue q was idle */
+ grp = cl->grp;
+ qfq_update_start(q, cl);
+
+ /* compute new finish time and rounded start. */
+ cl->F = cl->S + (u64)qdisc_pkt_len(skb) * cl->inv_w;
+ roundedS = qfq_round_down(cl->S, grp->slot_shift);
+
+ /*
+ * insert cl in the correct bucket.
+ * If cl->S >= grp->S we don't need to adjust the
+ * bucket list and simply go to the insertion phase.
+ * Otherwise grp->S is decreasing, we must make room
+ * in the bucket list, and also recompute the group state.
+ * Finally, if there were no flows in this group and nobody
+ * was in ER make sure to adjust V.
+ */
+ if (grp->full_slots) {
+ if (!qfq_gt(grp->S, cl->S))
+ goto skip_update;
+
+ /* create a slot for this cl->S */
+ qfq_slot_rotate(grp, roundedS);
+ /* group was surely ineligible, remove */
+ __clear_bit(grp->index, &q->bitmaps[IR]);
+ __clear_bit(grp->index, &q->bitmaps[IB]);
+ } else if (!q->bitmaps[ER] && qfq_gt(roundedS, q->V))
+ q->V = roundedS;
+
+ grp->S = roundedS;
+ grp->F = roundedS + (2ULL << grp->slot_shift);
+ s = qfq_calc_state(q, grp);
+ __set_bit(grp->index, &q->bitmaps[s]);
+
+ pr_debug("qfq enqueue: new state %d %#lx S %lld F %lld V %lld\n",
+ s, q->bitmaps[s],
+ (unsigned long long) cl->S,
+ (unsigned long long) cl->F,
+ (unsigned long long) q->V);
+
+skip_update:
+ qfq_slot_insert(grp, cl, roundedS);
+
+ return err;
+}
+
+
+static void qfq_slot_remove(struct qfq_sched *q, struct qfq_group *grp,
+ struct qfq_class *cl)
+{
+ unsigned int i, offset;
+ u64 roundedS;
+
+ roundedS = qfq_round_down(cl->S, grp->slot_shift);
+ offset = (roundedS - grp->S) >> grp->slot_shift;
+ i = (grp->front + offset) % QFQ_MAX_SLOTS;
+
+ hlist_del(&cl->next);
+ if (hlist_empty(&grp->slots[i]))
+ __clear_bit(offset, &grp->full_slots);
+}
+
+/*
+ * called to forcibly destroy a queue.
+ * If the queue is not in the front bucket, or if it has
+ * other queues in the front bucket, we can simply remove
+ * the queue with no other side effects.
+ * Otherwise we must propagate the event up.
+ */
+static void qfq_deactivate_class(struct qfq_sched *q, struct qfq_class *cl)
+{
+ struct qfq_group *grp = cl->grp;
+ unsigned long mask;
+ u64 roundedS;
+ int s;
+
+ cl->F = cl->S;
+ qfq_slot_remove(q, grp, cl);
+
+ if (!grp->full_slots) {
+ __clear_bit(grp->index, &q->bitmaps[IR]);
+ __clear_bit(grp->index, &q->bitmaps[EB]);
+ __clear_bit(grp->index, &q->bitmaps[IB]);
+
+ if (test_bit(grp->index, &q->bitmaps[ER]) &&
+ !(q->bitmaps[ER] & ~((1UL << grp->index) - 1))) {
+ mask = q->bitmaps[ER] & ((1UL << grp->index) - 1);
+ if (mask)
+ mask = ~((1UL << __fls(mask)) - 1);
+ else
+ mask = ~0UL;
+ qfq_move_groups(q, mask, EB, ER);
+ qfq_move_groups(q, mask, IB, IR);
+ }
+ __clear_bit(grp->index, &q->bitmaps[ER]);
+ } else if (hlist_empty(&grp->slots[grp->front])) {
+ cl = qfq_slot_scan(grp);
+ roundedS = qfq_round_down(cl->S, grp->slot_shift);
+ if (grp->S != roundedS) {
+ __clear_bit(grp->index, &q->bitmaps[ER]);
+ __clear_bit(grp->index, &q->bitmaps[IR]);
+ __clear_bit(grp->index, &q->bitmaps[EB]);
+ __clear_bit(grp->index, &q->bitmaps[IB]);
+ grp->S = roundedS;
+ grp->F = roundedS + (2ULL << grp->slot_shift);
+ s = qfq_calc_state(q, grp);
+ __set_bit(grp->index, &q->bitmaps[s]);
+ }
+ }
+
+ qfq_update_eligible(q, q->V);
+}
+
+static void qfq_qlen_notify(struct Qdisc *sch, unsigned long arg)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct qfq_class *cl = (struct qfq_class *)arg;
+
+ if (cl->qdisc->q.qlen == 0)
+ qfq_deactivate_class(q, cl);
+}
+
+static unsigned int qfq_drop(struct Qdisc *sch)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct qfq_group *grp;
+ unsigned int i, j, len;
+
+ for (i = 0; i <= QFQ_MAX_INDEX; i++) {
+ grp = &q->groups[i];
+ for (j = 0; j < QFQ_MAX_SLOTS; j++) {
+ struct qfq_class *cl;
+ struct hlist_node *n;
+
+ hlist_for_each_entry(cl, n, &grp->slots[j], next) {
+
+ if (!cl->qdisc->ops->drop)
+ continue;
+
+ len = cl->qdisc->ops->drop(cl->qdisc);
+ if (len > 0) {
+ sch->q.qlen--;
+ if (!cl->qdisc->q.qlen)
+ qfq_deactivate_class(q, cl);
+
+ return len;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int qfq_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct qfq_group *grp;
+ int i, j, err;
+
+ err = qdisc_class_hash_init(&q->clhash);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i <= QFQ_MAX_INDEX; i++) {
+ grp = &q->groups[i];
+ grp->index = i;
+ grp->slot_shift = QFQ_MTU_SHIFT + FRAC_BITS
+ - (QFQ_MAX_INDEX - i);
+ for (j = 0; j < QFQ_MAX_SLOTS; j++)
+ INIT_HLIST_HEAD(&grp->slots[j]);
+ }
+
+ return 0;
+}
+
+static void qfq_reset_qdisc(struct Qdisc *sch)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct qfq_group *grp;
+ struct qfq_class *cl;
+ struct hlist_node *n, *tmp;
+ unsigned int i, j;
+
+ for (i = 0; i <= QFQ_MAX_INDEX; i++) {
+ grp = &q->groups[i];
+ for (j = 0; j < QFQ_MAX_SLOTS; j++) {
+ hlist_for_each_entry_safe(cl, n, tmp,
+ &grp->slots[j], next) {
+ qfq_deactivate_class(q, cl);
+ }
+ }
+ }
+
+ for (i = 0; i < q->clhash.hashsize; i++) {
+ hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode)
+ qdisc_reset(cl->qdisc);
+ }
+ sch->q.qlen = 0;
+}
+
+static void qfq_destroy_qdisc(struct Qdisc *sch)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct qfq_class *cl;
+ struct hlist_node *n, *next;
+ unsigned int i;
+
+ tcf_destroy_chain(&q->filter_list);
+
+ for (i = 0; i < q->clhash.hashsize; i++) {
+ hlist_for_each_entry_safe(cl, n, next, &q->clhash.hash[i],
+ common.hnode) {
+ qfq_destroy_class(sch, cl);
+ }
+ }
+ qdisc_class_hash_destroy(&q->clhash);
+}
+
+static const struct Qdisc_class_ops qfq_class_ops = {
+ .change = qfq_change_class,
+ .delete = qfq_delete_class,
+ .get = qfq_get_class,
+ .put = qfq_put_class,
+ .tcf_chain = qfq_tcf_chain,
+ .bind_tcf = qfq_bind_tcf,
+ .unbind_tcf = qfq_unbind_tcf,
+ .graft = qfq_graft_class,
+ .leaf = qfq_class_leaf,
+ .qlen_notify = qfq_qlen_notify,
+ .dump = qfq_dump_class,
+ .dump_stats = qfq_dump_class_stats,
+ .walk = qfq_walk,
+};
+
+static struct Qdisc_ops qfq_qdisc_ops __read_mostly = {
+ .cl_ops = &qfq_class_ops,
+ .id = "qfq",
+ .priv_size = sizeof(struct qfq_sched),
+ .enqueue = qfq_enqueue,
+ .dequeue = qfq_dequeue,
+ .peek = qdisc_peek_dequeued,
+ .drop = qfq_drop,
+ .init = qfq_init_qdisc,
+ .reset = qfq_reset_qdisc,
+ .destroy = qfq_destroy_qdisc,
+ .owner = THIS_MODULE,
+};
+
+static int __init qfq_init(void)
+{
+ return register_qdisc(&qfq_qdisc_ops);
+}
+
+static void __exit qfq_exit(void)
+{
+ unregister_qdisc(&qfq_qdisc_ops);
+}
+
+module_init(qfq_init);
+module_exit(qfq_exit);
+MODULE_LICENSE("GPL");
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index de98665..b3434cc 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3106,10 +3106,10 @@
/* create an ASCONF_ACK chunk.
* Based on the definitions of parameters, we know that the size of
- * ASCONF_ACK parameters are less than or equal to the twice of ASCONF
+ * ASCONF_ACK parameters are less than or equal to the fourfold of ASCONF
* parameters.
*/
- asconf_ack = sctp_make_asconf_ack(asoc, serial, chunk_len * 2);
+ asconf_ack = sctp_make_asconf_ack(asoc, serial, chunk_len * 4);
if (!asconf_ack)
goto done;
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index ea427f4..fbf6f33 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -124,6 +124,15 @@
}
/* must hold dev->bss_lock! */
+static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
+ struct cfg80211_internal_bss *bss)
+{
+ list_del_init(&bss->list);
+ rb_erase(&bss->rbn, &dev->bss_tree);
+ kref_put(&bss->ref, bss_release);
+}
+
+/* must hold dev->bss_lock! */
void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
{
struct cfg80211_internal_bss *bss, *tmp;
@@ -134,9 +143,7 @@
continue;
if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
continue;
- list_del(&bss->list);
- rb_erase(&bss->rbn, &dev->bss_tree);
- kref_put(&bss->ref, bss_release);
+ __cfg80211_unlink_bss(dev, bss);
expired = true;
}
@@ -585,16 +592,23 @@
struct cfg80211_internal_bss *res;
size_t ielen = len - offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
- size_t privsz = wiphy->bss_priv_size;
+ size_t privsz;
+
+ if (WARN_ON(!mgmt))
+ return NULL;
+
+ if (WARN_ON(!wiphy))
+ return NULL;
if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
(signal < 0 || signal > 100)))
return NULL;
- if (WARN_ON(!mgmt || !wiphy ||
- len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
+ if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
return NULL;
+ privsz = wiphy->bss_priv_size;
+
res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
if (!res)
return NULL;
@@ -662,11 +676,8 @@
spin_lock_bh(&dev->bss_lock);
if (!list_empty(&bss->list)) {
- list_del_init(&bss->list);
+ __cfg80211_unlink_bss(dev, bss);
dev->bss_generation++;
- rb_erase(&bss->rbn, &dev->bss_tree);
-
- kref_put(&bss->ref, bss_release);
}
spin_unlock_bh(&dev->bss_lock);
}