summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/networking/ip-sysctl.txt10
-rw-r--r--arch/arm/net/bpf_jit_32.c3
-rw-r--r--arch/mips/net/bpf_jit.c3
-rw-r--r--arch/powerpc/net/bpf_jit_comp.c3
-rw-r--r--arch/s390/net/bpf_jit_comp.c2
-rw-r--r--arch/sparc/net/bpf_jit_comp.c3
-rw-r--r--arch/x86/net/bpf_jit_comp.c18
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-common.h11
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-dcb.c1
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c1
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-desc.c6
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-dev.c1
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-drv.c1
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c2
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-main.c1
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-mdio.c1
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-ptp.c1
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe.h2
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.c9
-rw-r--r--drivers/net/ethernet/cisco/enic/enic.h1
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_ethtool.c39
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_main.c50
-rw-r--r--drivers/net/ethernet/ti/cpsw.c22
-rw-r--r--drivers/net/phy/amd-xgbe-phy.c169
-rw-r--r--drivers/net/usb/r8152.c59
-rw-r--r--include/linux/ethtool.h4
-rw-r--r--include/linux/filter.h49
-rw-r--r--include/linux/igmp.h1
-rw-r--r--include/net/ip_fib.h2
-rw-r--r--include/net/ipv6.h1
-rw-r--r--include/uapi/linux/ethtool.h28
-rw-r--r--kernel/bpf/core.c80
-rw-r--r--kernel/seccomp.c7
-rw-r--r--lib/test_bpf.c2
-rw-r--r--net/core/dev_ioctl.c7
-rw-r--r--net/core/ethtool.c81
-rw-r--r--net/core/filter.c6
-rw-r--r--net/ipv4/fib_semantics.c8
-rw-r--r--net/ipv4/igmp.c31
-rw-r--r--net/ipv4/route.c6
-rw-r--r--net/ipv4/sysctl_net_ipv4.c10
-rw-r--r--net/ipv6/mcast.c25
-rw-r--r--net/ipv6/sysctl_net_ipv6.c10
-rw-r--r--net/l2tp/l2tp_core.c2
44 files changed, 571 insertions, 208 deletions
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 3cce8ea6b139..db2383cb1df9 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -844,6 +844,11 @@ igmp_max_memberships - INTEGER
conf/all/* is special, changes the settings for all interfaces
+igmp_qrv - INTEGER
+ Controls the IGMP query robustness variable (see RFC2236 8.1).
+ Default: 2 (as specified by RFC2236 8.1)
+ Minimum: 1 (as specified by RFC6636 4.5)
+
log_martians - BOOLEAN
Log packets with impossible addresses to kernel log.
log_martians for the interface will be enabled if at least one of
@@ -1152,6 +1157,11 @@ anycast_src_echo_reply - BOOLEAN
FALSE: disabled
Default: FALSE
+mld_qrv - INTEGER
+ Controls the MLD query robustness variable (see RFC3810 9.1).
+ Default: 2 (as specified by RFC3810 9.1)
+ Minimum: 1 (as specified by RFC6636 4.5)
+
IPv6 Fragmentation:
ip6frag_high_thresh - INTEGER
diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c
index a37b989a2f91..a76623bcf722 100644
--- a/arch/arm/net/bpf_jit_32.c
+++ b/arch/arm/net/bpf_jit_32.c
@@ -930,5 +930,6 @@ void bpf_jit_free(struct bpf_prog *fp)
{
if (fp->jited)
module_free(NULL, fp->bpf_func);
- kfree(fp);
+
+ bpf_prog_unlock_free(fp);
}
diff --git a/arch/mips/net/bpf_jit.c b/arch/mips/net/bpf_jit.c
index 05a56619ece2..cfa83cf2447d 100644
--- a/arch/mips/net/bpf_jit.c
+++ b/arch/mips/net/bpf_jit.c
@@ -1427,5 +1427,6 @@ void bpf_jit_free(struct bpf_prog *fp)
{
if (fp->jited)
module_free(NULL, fp->bpf_func);
- kfree(fp);
+
+ bpf_prog_unlock_free(fp);
}
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index 3afa6f4c1957..40c53ff59124 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -697,5 +697,6 @@ void bpf_jit_free(struct bpf_prog *fp)
{
if (fp->jited)
module_free(NULL, fp->bpf_func);
- kfree(fp);
+
+ bpf_prog_unlock_free(fp);
}
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index 61e45b7c04d7..f2833c5b218a 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -887,5 +887,5 @@ void bpf_jit_free(struct bpf_prog *fp)
module_free(NULL, header);
free_filter:
- kfree(fp);
+ bpf_prog_unlock_free(fp);
}
diff --git a/arch/sparc/net/bpf_jit_comp.c b/arch/sparc/net/bpf_jit_comp.c
index 1f76c22a6a75..f7a736b645e8 100644
--- a/arch/sparc/net/bpf_jit_comp.c
+++ b/arch/sparc/net/bpf_jit_comp.c
@@ -812,5 +812,6 @@ void bpf_jit_free(struct bpf_prog *fp)
{
if (fp->jited)
module_free(NULL, fp->bpf_func);
- kfree(fp);
+
+ bpf_prog_unlock_free(fp);
}
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index b08a98c59530..39ccfbb4a723 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -972,23 +972,17 @@ out:
kfree(addrs);
}
-static void bpf_jit_free_deferred(struct work_struct *work)
+void bpf_jit_free(struct bpf_prog *fp)
{
- struct bpf_prog *fp = container_of(work, struct bpf_prog, work);
unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
struct bpf_binary_header *header = (void *)addr;
+ if (!fp->jited)
+ goto free_filter;
+
set_memory_rw(addr, header->pages);
module_free(NULL, header);
- kfree(fp);
-}
-void bpf_jit_free(struct bpf_prog *fp)
-{
- if (fp->jited) {
- INIT_WORK(&fp->work, bpf_jit_free_deferred);
- schedule_work(&fp->work);
- } else {
- kfree(fp);
- }
+free_filter:
+ bpf_prog_unlock_free(fp);
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
index cc25a3a9e7cf..caade30820d5 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
@@ -271,7 +271,6 @@
#define DMA_PBL_X8_DISABLE 0x00
#define DMA_PBL_X8_ENABLE 0x01
-
/* MAC register offsets */
#define MAC_TCR 0x0000
#define MAC_RCR 0x0004
@@ -792,7 +791,6 @@
#define MTL_Q_DISABLED 0x00
#define MTL_Q_ENABLED 0x02
-
/* MTL traffic class register offsets
* Multiple traffic classes can be active. The first class has registers
* that begin at 0x1100. Each subsequent queue has registers that
@@ -815,7 +813,6 @@
#define MTL_TSA_SP 0x00
#define MTL_TSA_ETS 0x02
-
/* PCS MMD select register offset
* The MMD select register is used for accessing PCS registers
* when the underlying APB3 interface is using indirect addressing.
@@ -825,7 +822,6 @@
*/
#define PCS_MMD_SELECT 0xff
-
/* Descriptor/Packet entry bit positions and sizes */
#define RX_PACKET_ERRORS_CRC_INDEX 2
#define RX_PACKET_ERRORS_CRC_WIDTH 1
@@ -929,7 +925,6 @@
#define MDIO_AN_COMP_STAT 0x0030
#endif
-
/* Bit setting and getting macros
* The get macro will extract the current bit field value from within
* the variable
@@ -957,7 +952,6 @@ do { \
((0x1 << (_width)) - 1)) << (_index))); \
} while (0)
-
/* Bit setting and getting macros based on register fields
* The get macro uses the bit field definitions formed using the input
* names to extract the current bit field value from within the
@@ -986,7 +980,6 @@ do { \
_prefix##_##_field##_INDEX, \
_prefix##_##_field##_WIDTH, (_val))
-
/* Macros for reading or writing registers
* The ioread macros will get bit fields or full values using the
* register definitions formed using the input names
@@ -1014,7 +1007,6 @@ do { \
XGMAC_IOWRITE((_pdata), _reg, reg_val); \
} while (0)
-
/* Macros for reading or writing MTL queue or traffic class registers
* Similar to the standard read and write macros except that the
* base register value is calculated by the queue or traffic class number
@@ -1041,7 +1033,6 @@ do { \
XGMAC_MTL_IOWRITE((_pdata), (_n), _reg, reg_val); \
} while (0)
-
/* Macros for reading or writing DMA channel registers
* Similar to the standard read and write macros except that the
* base register value is obtained from the ring
@@ -1066,7 +1057,6 @@ do { \
XGMAC_DMA_IOWRITE((_channel), _reg, reg_val); \
} while (0)
-
/* Macros for building, reading or writing register values or bits
* within the register values of XPCS registers.
*/
@@ -1076,7 +1066,6 @@ do { \
#define XPCS_IOREAD(_pdata, _off) \
ioread32((_pdata)->xpcs_regs + (_off))
-
/* Macros for building, reading or writing register values or bits
* using MDIO. Different from above because of the use of standardized
* Linux include values. No shifting is performed with the bit
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dcb.c b/drivers/net/ethernet/amd/xgbe/xgbe-dcb.c
index 7d6a49b24321..8a50b01c2686 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-dcb.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-dcb.c
@@ -120,7 +120,6 @@
#include "xgbe.h"
#include "xgbe-common.h"
-
static int xgbe_dcb_ieee_getets(struct net_device *netdev,
struct ieee_ets *ets)
{
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
index 346592dca33c..978f9ec961ae 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
@@ -121,7 +121,6 @@
#include "xgbe.h"
#include "xgbe-common.h"
-
static ssize_t xgbe_common_read(char __user *buffer, size_t count,
loff_t *ppos, unsigned int value)
{
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-desc.c b/drivers/net/ethernet/amd/xgbe/xgbe-desc.c
index 1c5d62e8dab6..6fc5da01437d 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-desc.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-desc.c
@@ -117,7 +117,6 @@
#include "xgbe.h"
#include "xgbe-common.h"
-
static void xgbe_unmap_skb(struct xgbe_prv_data *, struct xgbe_ring_data *);
static void xgbe_free_ring(struct xgbe_prv_data *pdata,
@@ -524,11 +523,8 @@ static void xgbe_realloc_skb(struct xgbe_channel *channel)
/* Allocate skb & assign to each rdesc */
skb = dev_alloc_skb(pdata->rx_buf_size);
- if (skb == NULL) {
- netdev_alert(pdata->netdev,
- "failed to allocate skb\n");
+ if (skb == NULL)
break;
- }
skb_dma = dma_map_single(pdata->dev, skb->data,
pdata->rx_buf_size, DMA_FROM_DEVICE);
if (dma_mapping_error(pdata->dev, skb_dma)) {
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
index edaca4496264..3be98e505001 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
@@ -122,7 +122,6 @@
#include "xgbe.h"
#include "xgbe-common.h"
-
static unsigned int xgbe_usec_to_riwt(struct xgbe_prv_data *pdata,
unsigned int usec)
{
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index dc84f7193c2d..847da66d1548 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -126,7 +126,6 @@
#include "xgbe.h"
#include "xgbe-common.h"
-
static int xgbe_poll(struct napi_struct *, int);
static void xgbe_set_rx_mode(struct net_device *);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
index a076aca138a1..2289526b6f1c 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
@@ -121,7 +121,6 @@
#include "xgbe.h"
#include "xgbe-common.h"
-
struct xgbe_stats {
char stat_string[ETH_GSTRING_LEN];
int stat_size;
@@ -173,6 +172,7 @@ static const struct xgbe_stats xgbe_gstring_stats[] = {
XGMAC_MMC_STAT("rx_watchdog_errors", rxwatchdogerror),
XGMAC_MMC_STAT("rx_pause_frames", rxpauseframes),
};
+
#define XGBE_STATS_COUNT ARRAY_SIZE(xgbe_gstring_stats)
static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
index 8aa6a9353f7b..984161d4ffc9 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
@@ -128,7 +128,6 @@
#include "xgbe.h"
#include "xgbe-common.h"
-
MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(XGBE_DRV_VERSION);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
index 6d2221e023f4..363b210560f3 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
@@ -123,7 +123,6 @@
#include "xgbe.h"
#include "xgbe-common.h"
-
static int xgbe_mdio_read(struct mii_bus *mii, int prtad, int mmd_reg)
{
struct xgbe_prv_data *pdata = mii->priv;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c
index 37e64cfa5718..a1bf9d1cdae1 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c
@@ -122,7 +122,6 @@
#include "xgbe.h"
#include "xgbe-common.h"
-
static cycle_t xgbe_cc_read(const struct cyclecounter *cc)
{
struct xgbe_prv_data *pdata = container_of(cc,
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index 07bf70a82908..84fe34ce5571 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -128,7 +128,6 @@
#include <linux/net_tstamp.h>
#include <net/dcbnl.h>
-
#define XGBE_DRV_NAME "amd-xgbe"
#define XGBE_DRV_VERSION "1.0.0-a"
#define XGBE_DRV_DESC "AMD 10 Gigabit Ethernet Driver"
@@ -198,7 +197,6 @@
((_ring)->rdata + \
((_idx) & ((_ring)->rdesc_count - 1)))
-
/* Default coalescing parameters */
#define XGMAC_INIT_DMA_TX_USECS 50
#define XGMAC_INIT_DMA_TX_FRAMES 25
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index 8f91de169663..662cf2222873 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -1071,16 +1071,19 @@ static void bcm_sysport_adj_link(struct net_device *dev)
if (!phydev->pause)
cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE;
- if (changed) {
+ if (!changed)
+ return;
+
+ if (phydev->link) {
reg = umac_readl(priv, UMAC_CMD);
reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) |
CMD_HD_EN | CMD_RX_PAUSE_IGNORE |
CMD_TX_PAUSE_IGNORE);
reg |= cmd_bits;
umac_writel(priv, reg, UMAC_CMD);
-
- phy_print_status(priv->phydev);
}
+
+ phy_print_status(priv->phydev);
}
static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv,
diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h
index 962510f391df..5ba5ad071bb6 100644
--- a/drivers/net/ethernet/cisco/enic/enic.h
+++ b/drivers/net/ethernet/cisco/enic/enic.h
@@ -186,6 +186,7 @@ struct enic {
____cacheline_aligned struct vnic_cq cq[ENIC_CQ_MAX];
unsigned int cq_count;
struct enic_rfs_flw_tbl rfs_h;
+ u32 rx_copybreak;
};
static inline struct device *enic_get_dev(struct enic *enic)
diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c
index 523c9ceb04c0..85173d620758 100644
--- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c
+++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c
@@ -379,6 +379,43 @@ static int enic_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
return ret;
}
+static int enic_get_tunable(struct net_device *dev,
+ const struct ethtool_tunable *tuna, void *data)
+{
+ struct enic *enic = netdev_priv(dev);
+ int ret = 0;
+
+ switch (tuna->id) {
+ case ETHTOOL_RX_COPYBREAK:
+ *(u32 *)data = enic->rx_copybreak;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int enic_set_tunable(struct net_device *dev,
+ const struct ethtool_tunable *tuna,
+ const void *data)
+{
+ struct enic *enic = netdev_priv(dev);
+ int ret = 0;
+
+ switch (tuna->id) {
+ case ETHTOOL_RX_COPYBREAK:
+ enic->rx_copybreak = *(u32 *)data;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
static const struct ethtool_ops enic_ethtool_ops = {
.get_settings = enic_get_settings,
.get_drvinfo = enic_get_drvinfo,
@@ -391,6 +428,8 @@ static const struct ethtool_ops enic_ethtool_ops = {
.get_coalesce = enic_get_coalesce,
.set_coalesce = enic_set_coalesce,
.get_rxnfc = enic_get_rxnfc,
+ .get_tunable = enic_get_tunable,
+ .set_tunable = enic_set_tunable,
};
void enic_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index c8832bc1c5f7..929bfe70080a 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -66,6 +66,8 @@
#define PCI_DEVICE_ID_CISCO_VIC_ENET_DYN 0x0044 /* enet dynamic vnic */
#define PCI_DEVICE_ID_CISCO_VIC_ENET_VF 0x0071 /* enet SRIOV VF */
+#define RX_COPYBREAK_DEFAULT 256
+
/* Supported devices */
static const struct pci_device_id enic_id_table[] = {
{ PCI_VDEVICE(CISCO, PCI_DEVICE_ID_CISCO_VIC_ENET) },
@@ -924,6 +926,7 @@ static void enic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
pci_unmap_single(enic->pdev, buf->dma_addr,
buf->len, PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(buf->os_buf);
+ buf->os_buf = NULL;
}
static int enic_rq_alloc_buf(struct vnic_rq *rq)
@@ -934,7 +937,24 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq)
unsigned int len = netdev->mtu + VLAN_ETH_HLEN;
unsigned int os_buf_index = 0;
dma_addr_t dma_addr;
+ struct vnic_rq_buf *buf = rq->to_use;
+
+ if (buf->os_buf) {
+ buf = buf->next;
+ rq->to_use = buf;
+ rq->ring.desc_avail--;
+ if ((buf->index & VNIC_RQ_RETURN_RATE) == 0) {
+ /* Adding write memory barrier prevents compiler and/or
+ * CPU reordering, thus avoiding descriptor posting
+ * before descriptor is initialized. Otherwise, hardware
+ * can read stale descriptor fields.
+ */
+ wmb();
+ iowrite32(buf->index, &rq->ctrl->posted_index);
+ }
+ return 0;
+ }
skb = netdev_alloc_skb_ip_align(netdev, len);
if (!skb)
return -ENOMEM;
@@ -957,6 +977,25 @@ static void enic_intr_update_pkt_size(struct vnic_rx_bytes_counter *pkt_size,
pkt_size->small_pkt_bytes_cnt += pkt_len;
}
+static bool enic_rxcopybreak(struct net_device *netdev, struct sk_buff **skb,
+ struct vnic_rq_buf *buf, u16 len)
+{
+ struct enic *enic = netdev_priv(netdev);
+ struct sk_buff *new_skb;
+
+ if (len > enic->rx_copybreak)
+ return false;
+ new_skb = netdev_alloc_skb_ip_align(netdev, len);
+ if (!new_skb)
+ return false;
+ pci_dma_sync_single_for_cpu(enic->pdev, buf->dma_addr, len,
+ DMA_FROM_DEVICE);
+ memcpy(new_skb->data, (*skb)->data, len);
+ *skb = new_skb;
+
+ return true;
+}
+
static void enic_rq_indicate_buf(struct vnic_rq *rq,
struct cq_desc *cq_desc, struct vnic_rq_buf *buf,
int skipped, void *opaque)
@@ -978,9 +1017,6 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
return;
skb = buf->os_buf;
- prefetch(skb->data - NET_IP_ALIGN);
- pci_unmap_single(enic->pdev, buf->dma_addr,
- buf->len, PCI_DMA_FROMDEVICE);
cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc,
&type, &color, &q_number, &completed_index,
@@ -1011,6 +1047,13 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
/* Good receive
*/
+ if (!enic_rxcopybreak(netdev, &skb, buf, bytes_written)) {
+ buf->os_buf = NULL;
+ pci_unmap_single(enic->pdev, buf->dma_addr, buf->len,
+ PCI_DMA_FROMDEVICE);
+ }
+ prefetch(skb->data - NET_IP_ALIGN);
+
skb_put(skb, bytes_written);
skb->protocol = eth_type_trans(skb, netdev);
skb_record_rx_queue(skb, q_number);
@@ -2531,6 +2574,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_err(dev, "Cannot register net device, aborting\n");
goto err_out_dev_deinit;
}
+ enic->rx_copybreak = RX_COPYBREAK_DEFAULT;
return 0;
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 999fb72688d2..03b409988566 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -2232,18 +2232,24 @@ static int cpsw_probe(struct platform_device *pdev)
}
while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) {
- for (i = res->start; i <= res->end; i++) {
- if (devm_request_irq(&pdev->dev, i, cpsw_interrupt, 0,
- dev_name(&pdev->dev), priv)) {
- dev_err(priv->dev, "error attaching irq\n");
- goto clean_ale_ret;
- }
- priv->irqs_table[k] = i;
- priv->num_irqs = k + 1;
+ if (k >= ARRAY_SIZE(priv->irqs_table)) {
+ ret = -EINVAL;
+ goto clean_ale_ret;
}
+
+ ret = devm_request_irq(&pdev->dev, res->start, cpsw_interrupt,
+ 0, dev_name(&pdev->dev), priv);
+ if (ret < 0) {
+ dev_err(priv->dev, "error attaching irq (%d)\n", ret);
+ goto clean_ale_ret;
+ }
+
+ priv->irqs_table[k] = res->start;
k++;
}
+ priv->num_irqs = k;
+
ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
ndev->netdev_ops = &cpsw_netdev_ops;
diff --git a/drivers/net/phy/amd-xgbe-phy.c b/drivers/net/phy/amd-xgbe-phy.c
index f3230eef41fd..4e1f7f734d27 100644
--- a/drivers/net/phy/amd-xgbe-phy.c
+++ b/drivers/net/phy/amd-xgbe-phy.c
@@ -75,7 +75,6 @@
#include <linux/of_device.h>
#include <linux/uaccess.h>
-
MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION("1.0.0-a");
@@ -100,9 +99,11 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
#ifndef MDIO_PMA_10GBR_PMD_CTRL
#define MDIO_PMA_10GBR_PMD_CTRL 0x0096
#endif
+
#ifndef MDIO_PMA_10GBR_FEC_CTRL
#define MDIO_PMA_10GBR_FEC_CTRL 0x00ab
#endif
+
#ifndef MDIO_AN_XNP
#define MDIO_AN_XNP 0x0016
#endif
@@ -110,14 +111,23 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
#ifndef MDIO_AN_INTMASK
#define MDIO_AN_INTMASK 0x8001
#endif
+
#ifndef MDIO_AN_INT
#define MDIO_AN_INT 0x8002
#endif
+#ifndef MDIO_AN_KR_CTRL
+#define MDIO_AN_KR_CTRL 0x8003
+#endif
+
#ifndef MDIO_CTRL1_SPEED1G
#define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
#endif
+#ifndef MDIO_KR_CTRL_PDETECT
+#define MDIO_KR_CTRL_PDETECT 0x01
+#endif
+
/* SerDes integration register offsets */
#define SIR0_KR_RT_1 0x002c
#define SIR0_STATUS 0x0040
@@ -161,7 +171,6 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
#define SPEED_1000_TXAMP 0xf
#define SPEED_1000_WORD 0x1
-
/* SerDes RxTx register offsets */
#define RXTX_REG20 0x0050
#define RXTX_REG114 0x01c8
@@ -255,7 +264,6 @@ do { \
XSIR1_IOWRITE((_priv), _reg, reg_val); \
} while (0)
-
/* Macros for reading or writing SerDes RxTx registers
* The ioread macros will get bit fields or full values using the
* register definitions formed using the input names
@@ -283,7 +291,6 @@ do { \
XRXTX_IOWRITE((_priv), _reg, reg_val); \
} while (0)
-
enum amd_xgbe_phy_an {
AMD_XGBE_AN_READY = 0,
AMD_XGBE_AN_START,
@@ -331,7 +338,6 @@ struct amd_xgbe_phy_priv {
/* Maintain link status for re-starting auto-negotiation */
unsigned int link;
- enum amd_xgbe_phy_mode mode;
unsigned int speed_set;
/* Auto-negotiation state machine support */
@@ -342,6 +348,7 @@ struct amd_xgbe_phy_priv {
enum amd_xgbe_phy_rx kx_state;
struct work_struct an_work;
struct workqueue_struct *an_workqueue;
+ unsigned int parallel_detect;
};
static int amd_xgbe_an_enable_kr_training(struct phy_device *phydev)
@@ -468,8 +475,6 @@ static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev)
amd_xgbe_phy_serdes_complete_ratechange(phydev);
- priv->mode = AMD_XGBE_MODE_KR;
-
return 0;
}
@@ -518,8 +523,6 @@ static int amd_xgbe_phy_gmii_2500_mode(struct phy_device *phydev)
amd_xgbe_phy_serdes_complete_ratechange(phydev);
- priv->mode = AMD_XGBE_MODE_KX;
-
return 0;
}
@@ -568,18 +571,43 @@ static int amd_xgbe_phy_gmii_mode(struct phy_device *phydev)
amd_xgbe_phy_serdes_complete_ratechange(phydev);
- priv->mode = AMD_XGBE_MODE_KX;
+ return 0;
+}
+
+static int amd_xgbe_phy_cur_mode(struct phy_device *phydev,
+ enum amd_xgbe_phy_mode *mode)
+{
+ int ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+ if (ret < 0)
+ return ret;
+
+ if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR)
+ *mode = AMD_XGBE_MODE_KR;
+ else
+ *mode = AMD_XGBE_MODE_KX;
return 0;
}
+static bool amd_xgbe_phy_in_kr_mode(struct phy_device *phydev)
+{
+ enum amd_xgbe_phy_mode mode;
+
+ if (amd_xgbe_phy_cur_mode(phydev, &mode))
+ return false;
+
+ return (mode == AMD_XGBE_MODE_KR);
+}
+
static int amd_xgbe_phy_switch_mode(struct phy_device *phydev)
{
struct amd_xgbe_phy_priv *priv = phydev->priv;
int ret;
/* If we are in KR switch to KX, and vice-versa */
- if (priv->mode == AMD_XGBE_MODE_KR) {
+ if (amd_xgbe_phy_in_kr_mode(phydev)) {
if (priv->speed_set == AMD_XGBE_PHY_SPEEDSET_1000_10000)
ret = amd_xgbe_phy_gmii_mode(phydev);
else
@@ -591,27 +619,31 @@ static int amd_xgbe_phy_switch_mode(struct phy_device *phydev)
return ret;
}
-static enum amd_xgbe_phy_an amd_xgbe_an_switch_mode(struct phy_device *phydev)
+static int amd_xgbe_phy_set_mode(struct phy_device *phydev,
+ enum amd_xgbe_phy_mode mode)
{
+ enum amd_xgbe_phy_mode cur_mode;
int ret;
- ret = amd_xgbe_phy_switch_mode(phydev);
- if (ret < 0)
- return AMD_XGBE_AN_ERROR;
+ ret = amd_xgbe_phy_cur_mode(phydev, &cur_mode);
+ if (ret)
+ return ret;
- return AMD_XGBE_AN_START;
+ if (mode != cur_mode)
+ ret = amd_xgbe_phy_switch_mode(phydev);
+
+ return ret;
}
static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
enum amd_xgbe_phy_rx *state)
{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
int ad_reg, lp_reg, ret;
*state = AMD_XGBE_RX_COMPLETE;
- /* If we're in KX mode then we're done */
- if (priv->mode == AMD_XGBE_MODE_KX)
+ /* If we're not in KR mode then we're done */
+ if (!amd_xgbe_phy_in_kr_mode(phydev))
return AMD_XGBE_AN_EVENT;
/* Enable/Disable FEC */
@@ -669,7 +701,6 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev,
static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev,
enum amd_xgbe_phy_rx *state)
{
- struct amd_xgbe_phy_priv *priv = phydev->priv;
unsigned int link_support;
int ret, ad_reg, lp_reg;
@@ -679,9 +710,9 @@ static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev,
return AMD_XGBE_AN_ERROR;
/* Check for a supported mode, otherwise restart in a different one */
- link_support = (priv->mode == AMD_XGBE_MODE_KR) ? 0x80 : 0x20;
+ link_support = amd_xgbe_phy_in_kr_mode(phydev) ? 0x80 : 0x20;
if (!(ret & link_support))
- return amd_xgbe_an_switch_mode(phydev);
+ return AMD_XGBE_AN_INCOMPAT_LINK;
/* Check Extended Next Page support */
ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
@@ -722,7 +753,7 @@ static enum amd_xgbe_phy_an amd_xgbe_an_start(struct phy_device *phydev)
int ret;
/* Be sure we aren't looping trying to negotiate */
- if (priv->mode == AMD_XGBE_MODE_KR) {
+ if (amd_xgbe_phy_in_kr_mode(phydev)) {
if (priv->kr_state != AMD_XGBE_RX_READY)
return AMD_XGBE_AN_NO_LINK;
priv->kr_state = AMD_XGBE_RX_BPA;
@@ -785,6 +816,13 @@ static enum amd_xgbe_phy_an amd_xgbe_an_start(struct phy_device *phydev)
/* Enable and start auto-negotiation */
phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_KR_CTRL);
+ if (ret < 0)
+ return AMD_XGBE_AN_ERROR;
+
+ ret |= MDIO_KR_CTRL_PDETECT;
+ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_KR_CTRL, ret);
+
ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
if (ret < 0)
return AMD_XGBE_AN_ERROR;
@@ -825,8 +863,8 @@ static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev)
enum amd_xgbe_phy_rx *state;
int ret;
- state = (priv->mode == AMD_XGBE_MODE_KR) ? &priv->kr_state
- : &priv->kx_state;
+ state = amd_xgbe_phy_in_kr_mode(phydev) ? &priv->kr_state
+ : &priv->kx_state;
switch (*state) {
case AMD_XGBE_RX_BPA:
@@ -846,7 +884,13 @@ static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev)
static enum amd_xgbe_phy_an amd_xgbe_an_incompat_link(struct phy_device *phydev)
{
- return amd_xgbe_an_switch_mode(phydev);
+ int ret;
+
+ ret = amd_xgbe_phy_switch_mode(phydev);
+ if (ret)
+ return AMD_XGBE_AN_ERROR;
+
+ return AMD_XGBE_AN_START;
}
static void amd_xgbe_an_state_machine(struct work_struct *work)
@@ -859,6 +903,10 @@ static void amd_xgbe_an_state_machine(struct work_struct *work)
int sleep;
unsigned int an_supported = 0;
+ /* Start in KX mode */
+ if (amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX))
+ priv->an_state = AMD_XGBE_AN_ERROR;
+
while (1) {
mutex_lock(&priv->an_mutex);
@@ -866,8 +914,9 @@ static void amd_xgbe_an_state_machine(struct work_struct *work)
switch (priv->an_state) {
case AMD_XGBE_AN_START:
- priv->an_state = amd_xgbe_an_start(phydev);
an_supported = 0;
+ priv->parallel_detect = 0;
+ priv->an_state = amd_xgbe_an_start(phydev);
break;
case AMD_XGBE_AN_EVENT:
@@ -884,6 +933,7 @@ static void amd_xgbe_an_state_machine(struct work_struct *work)
break;
case AMD_XGBE_AN_COMPLETE:
+ priv->parallel_detect = an_supported ? 0 : 1;
netdev_info(phydev->attached_dev, "%s successful\n",
an_supported ? "Auto negotiation"
: "Parallel detection");
@@ -1018,7 +1068,6 @@ static int amd_xgbe_phy_config_aneg(struct phy_device *phydev)
{
struct amd_xgbe_phy_priv *priv = phydev->priv;
u32 mmd_mask = phydev->c45_ids.devices_in_package;
- int ret;
if (phydev->autoneg != AUTONEG_ENABLE)
return amd_xgbe_phy_setup_forced(phydev);
@@ -1027,11 +1076,6 @@ static int amd_xgbe_phy_config_aneg(struct phy_device *phydev)
if (!(mmd_mask & MDIO_DEVS_AN))
return -EINVAL;
- /* Get the current speed mode */
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
- if (ret < 0)
- return ret;
-
/* Start/Restart the auto-negotiation state machine */
mutex_lock(&priv->an_mutex);
priv->an_result = AMD_XGBE_AN_READY;
@@ -1121,18 +1165,14 @@ static int amd_xgbe_phy_read_status(struct phy_device *phydev)
{
struct amd_xgbe_phy_priv *priv = phydev->priv;
u32 mmd_mask = phydev->c45_ids.devices_in_package;
- int ret, mode, ad_ret, lp_ret;
+ int ret, ad_ret, lp_ret;
ret = amd_xgbe_phy_update_link(phydev);
if (ret)
return ret;
- mode = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
- if (mode < 0)
- return mode;
- mode &= MDIO_PCS_CTRL2_TYPE;
-
- if (phydev->autoneg == AUTONEG_ENABLE) {
+ if ((phydev->autoneg == AUTONEG_ENABLE) &&
+ !priv->parallel_detect) {
if (!(mmd_mask & MDIO_DEVS_AN))
return -EINVAL;
@@ -1163,40 +1203,39 @@ static int amd_xgbe_phy_read_status(struct phy_device *phydev)
ad_ret &= lp_ret;
if (ad_ret & 0x80) {
phydev->speed = SPEED_10000;
- if (mode != MDIO_PCS_CTRL2_10GBR) {
- ret = amd_xgbe_phy_xgmii_mode(phydev);
- if (ret < 0)
- return ret;
- }
+ ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KR);
+ if (ret)
+ return ret;
} else {
- int (*mode_fcn)(struct phy_device *);
-
- if (priv->speed_set ==
- AMD_XGBE_PHY_SPEEDSET_1000_10000) {
+ switch (priv->speed_set) {
+ case AMD_XGBE_PHY_SPEEDSET_1000_10000:
phydev->speed = SPEED_1000;
- mode_fcn = amd_xgbe_phy_gmii_mode;
- } else {
+ break;
+
+ case AMD_XGBE_PHY_SPEEDSET_2500_10000:
phydev->speed = SPEED_2500;
- mode_fcn = amd_xgbe_phy_gmii_2500_mode;
+ break;
}
- if (mode == MDIO_PCS_CTRL2_10GBR) {
- ret = mode_fcn(phydev);
- if (ret < 0)
- return ret;
- }
+ ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX);
+ if (ret)
+ return ret;
}
phydev->duplex = DUPLEX_FULL;
} else {
- if (mode == MDIO_PCS_CTRL2_10GBR) {
+ if (amd_xgbe_phy_in_kr_mode(phydev)) {
phydev->speed = SPEED_10000;
} else {
- if (priv->speed_set ==
- AMD_XGBE_PHY_SPEEDSET_1000_10000)
+ switch (priv->speed_set) {
+ case AMD_XGBE_PHY_SPEEDSET_1000_10000:
phydev->speed = SPEED_1000;
- else
+ break;
+
+ case AMD_XGBE_PHY_SPEEDSET_2500_10000:
phydev->speed = SPEED_2500;
+ break;
+ }
}
phydev->duplex = DUPLEX_FULL;
phydev->pause = 0;
@@ -1329,14 +1368,6 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
priv->link = 1;
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
- if (ret < 0)
- goto err_sir1;
- if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR)
- priv->mode = AMD_XGBE_MODE_KR;
- else
- priv->mode = AMD_XGBE_MODE_KX;
-
mutex_init(&priv->an_mutex);
INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine);
priv->an_workqueue = create_singlethread_workqueue(wq_name);
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 80b0179bff1f..f95e678cb69d 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -975,34 +975,6 @@ void write_mii_word(struct net_device *netdev, int phy_id, int reg, int val)
static int
r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags);
-static inline void set_ethernet_addr(struct r8152 *tp)
-{
- struct net_device *dev = tp->netdev;
- int ret;
- u8 node_id[8] = {0};
-
- if (tp->version == RTL_VER_01)
- ret = pla_ocp_read(tp, PLA_IDR, sizeof(node_id), node_id);
- else
- ret = pla_ocp_read(tp, PLA_BACKUP, sizeof(node_id), node_id);
-
- if (ret < 0) {
- netif_notice(tp, probe, dev, "inet addr fail\n");
- } else {
- if (tp->version != RTL_VER_01) {
- ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR,
- CRWECR_CONFIG);
- pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES,
- sizeof(node_id), node_id);
- ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR,
- CRWECR_NORAML);
- }
-
- memcpy(dev->dev_addr, node_id, dev->addr_len);
- memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
- }
-}
-
static int rtl8152_set_mac_address(struct net_device *netdev, void *p)
{
struct r8152 *tp = netdev_priv(netdev);
@@ -1020,6 +992,37 @@ static int rtl8152_set_mac_address(struct net_device *netdev, void *p)
return 0;
}
+static int set_ethernet_addr(struct r8152 *tp)
+{
+ struct net_device *dev = tp->netdev;
+ struct sockaddr sa;
+ int ret;
+
+ if (tp->version == RTL_VER_01)
+ ret = pla_ocp_read(tp, PLA_IDR, 8, sa.sa_data);
+ else
+ ret = pla_ocp_read(tp, PLA_BACKUP, 8, sa.sa_data);
+
+ if (ret < 0) {
+ netif_err(tp, probe, dev, "Get ether addr fail\n");
+ } else if (!is_valid_ether_addr(sa.sa_data)) {
+ netif_err(tp, probe, dev, "Invalid ether addr %pM\n",
+ sa.sa_data);
+ eth_hw_addr_random(dev);
+ ether_addr_copy(sa.sa_data, dev->dev_addr);
+ ret = rtl8152_set_mac_address(dev, &sa);
+ netif_info(tp, probe, dev, "Random ether addr %pM\n",
+ sa.sa_data);
+ } else {
+ if (tp->version == RTL_VER_01)
+ ether_addr_copy(dev->dev_addr, sa.sa_data);
+ else
+ ret = rtl8152_set_mac_address(dev, &sa);
+ }
+
+ return ret;
+}
+
static void read_bulk_callback(struct urb *urb)
{
struct net_device *netdev;
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index e658229fee39..c1a2d60dfb82 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -257,6 +257,10 @@ struct ethtool_ops {
struct ethtool_eeprom *, u8 *);
int (*get_eee)(struct net_device *, struct ethtool_eee *);
int (*set_eee)(struct net_device *, struct ethtool_eee *);
+ int (*get_tunable)(struct net_device *,
+ const struct ethtool_tunable *, void *);
+ int (*set_tunable)(struct net_device *,
+ const struct ethtool_tunable *, const void *);
};
diff --git a/include/linux/filter.h b/include/linux/filter.h
index a5227ab8ccb1..c78994593355 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -9,6 +9,11 @@
#include <linux/skbuff.h>
#include <linux/workqueue.h>
#include <uapi/linux/filter.h>
+#include <asm/cacheflush.h>
+
+struct sk_buff;
+struct sock;
+struct seccomp_data;
/* Internally used and optimized filter representation with extended
* instruction set based on top of classic BPF.
@@ -320,20 +325,23 @@ struct sock_fprog_kern {
struct sock_filter *filter;
};
-struct sk_buff;
-struct sock;
-struct seccomp_data;
+struct bpf_work_struct {
+ struct bpf_prog *prog;
+ struct work_struct work;
+};
struct bpf_prog {
+ u32 pages; /* Number of allocated pages */
u32 jited:1, /* Is our filter JIT'ed? */
len:31; /* Number of filter blocks */
struct sock_fprog_kern *orig_prog; /* Original BPF program */
+ struct bpf_work_struct *work; /* Deferred free work struct */
unsigned int (*bpf_func)(const struct sk_buff *skb,
const struct bpf_insn *filter);
+ /* Instructions for interpreter */
union {
struct sock_filter insns[0];
struct bpf_insn insnsi[0];
- struct work_struct work;
};
};
@@ -353,6 +361,26 @@ static inline unsigned int bpf_prog_size(unsigned int proglen)
#define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0]))
+#ifdef CONFIG_DEBUG_SET_MODULE_RONX
+static inline void bpf_prog_lock_ro(struct bpf_prog *fp)
+{
+ set_memory_ro((unsigned long)fp, fp->pages);
+}
+
+static inline void bpf_prog_unlock_ro(struct bpf_prog *fp)
+{
+ set_memory_rw((unsigned long)fp, fp->pages);
+}
+#else
+static inline void bpf_prog_lock_ro(struct bpf_prog *fp)
+{
+}
+
+static inline void bpf_prog_unlock_ro(struct bpf_prog *fp)
+{
+}
+#endif /* CONFIG_DEBUG_SET_MODULE_RONX */
+
int sk_filter(struct sock *sk, struct sk_buff *skb);
void bpf_prog_select_runtime(struct bpf_prog *fp);
@@ -361,6 +389,17 @@ void bpf_prog_free(struct bpf_prog *fp);
int bpf_convert_filter(struct sock_filter *prog, int len,
struct bpf_insn *new_prog, int *new_len);
+struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags);
+struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
+ gfp_t gfp_extra_flags);
+void __bpf_prog_free(struct bpf_prog *fp);
+
+static inline void bpf_prog_unlock_free(struct bpf_prog *fp)
+{
+ bpf_prog_unlock_ro(fp);
+ __bpf_prog_free(fp);
+}
+
int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog);
void bpf_prog_destroy(struct bpf_prog *fp);
@@ -450,7 +489,7 @@ static inline void bpf_jit_compile(struct bpf_prog *fp)
static inline void bpf_jit_free(struct bpf_prog *fp)
{
- kfree(fp);
+ bpf_prog_unlock_free(fp);
}
#endif /* CONFIG_BPF_JIT */
diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index f47550d75f85..2c677afeea47 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -39,6 +39,7 @@ static inline struct igmpv3_query *
extern int sysctl_igmp_max_memberships;
extern int sysctl_igmp_max_msf;
+extern int sysctl_igmp_qrv;
struct ip_sf_socklist {
unsigned int sl_max;
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 9922093f575e..f30fd554127e 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -87,7 +87,7 @@ struct fib_nh {
int nh_saddr_genid;
struct rtable __rcu * __percpu *nh_pcpu_rth_output;
struct rtable __rcu *nh_rth_input;
- struct fnhe_hash_bucket *nh_exceptions;
+ struct fnhe_hash_bucket __rcu *nh_exceptions;
};
/*
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index a2db816e8461..7e247e9b8765 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -121,6 +121,7 @@ struct frag_hdr {
/* sysctls */
extern int sysctl_mld_max_msf;
+extern int sysctl_mld_qrv;
#define _DEVINC(net, statname, modifier, idev, field) \
({ \
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index e3c7a719c76b..7a364f2f3d3f 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -209,6 +209,32 @@ struct ethtool_value {
__u32 data;
};
+enum tunable_id {
+ ETHTOOL_ID_UNSPEC,
+ ETHTOOL_RX_COPYBREAK,
+};
+
+enum tunable_type_id {
+ ETHTOOL_TUNABLE_UNSPEC,
+ ETHTOOL_TUNABLE_U8,
+ ETHTOOL_TUNABLE_U16,
+ ETHTOOL_TUNABLE_U32,
+ ETHTOOL_TUNABLE_U64,
+ ETHTOOL_TUNABLE_STRING,
+ ETHTOOL_TUNABLE_S8,
+ ETHTOOL_TUNABLE_S16,
+ ETHTOOL_TUNABLE_S32,
+ ETHTOOL_TUNABLE_S64,
+};
+
+struct ethtool_tunable {
+ __u32 cmd;
+ __u32 id;
+ __u32 type_id;
+ __u32 len;
+ void *data[0];
+};
+
/**
* struct ethtool_regs - hardware register dump
* @cmd: Command number = %ETHTOOL_GREGS
@@ -1152,6 +1178,8 @@ enum ethtool_sfeatures_retval_bits {
#define ETHTOOL_GRSSH 0x00000046 /* Get RX flow hash configuration */
#define ETHTOOL_SRSSH 0x00000047 /* Set RX flow hash configuration */
+#define ETHTOOL_GTUNABLE 0x00000048 /* Get tunable configuration */
+#define ETHTOOL_STUNABLE 0x00000049 /* Set tunable configuration */
/* compatibility with older code */
#define SPARC_ETH_GSET ETHTOOL_GSET
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 7f0dbcbb34af..b54bb2c2e494 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -22,6 +22,7 @@
*/
#include <linux/filter.h>
#include <linux/skbuff.h>
+#include <linux/vmalloc.h>
#include <asm/unaligned.h>
/* Registers */
@@ -63,6 +64,67 @@ void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, int k, uns
return NULL;
}
+struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags)
+{
+ gfp_t gfp_flags = GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO |
+ gfp_extra_flags;
+ struct bpf_work_struct *ws;
+ struct bpf_prog *fp;
+
+ size = round_up(size, PAGE_SIZE);
+ fp = __vmalloc(size, gfp_flags, PAGE_KERNEL);
+ if (fp == NULL)
+ return NULL;
+
+ ws = kmalloc(sizeof(*ws), GFP_KERNEL | gfp_extra_flags);
+ if (ws == NULL) {
+ vfree(fp);
+ return NULL;
+ }
+
+ fp->pages = size / PAGE_SIZE;
+ fp->work = ws;
+
+ return fp;
+}
+EXPORT_SYMBOL_GPL(bpf_prog_alloc);
+
+struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
+ gfp_t gfp_extra_flags)
+{
+ gfp_t gfp_flags = GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO |
+ gfp_extra_flags;
+ struct bpf_prog *fp;
+
+ BUG_ON(fp_old == NULL);
+
+ size = round_up(size, PAGE_SIZE);
+ if (size <= fp_old->pages * PAGE_SIZE)
+ return fp_old;
+
+ fp = __vmalloc(size, gfp_flags, PAGE_KERNEL);
+ if (fp != NULL) {
+ memcpy(fp, fp_old, fp_old->pages * PAGE_SIZE);
+ fp->pages = size / PAGE_SIZE;
+
+ /* We keep fp->work from fp_old around in the new
+ * reallocated structure.
+ */
+ fp_old->work = NULL;
+ __bpf_prog_free(fp_old);
+ }
+
+ return fp;
+}
+EXPORT_SYMBOL_GPL(bpf_prog_realloc);
+
+void __bpf_prog_free(struct bpf_prog *fp)
+{
+ kfree(fp->work);
+ vfree(fp);
+}
+EXPORT_SYMBOL_GPL(__bpf_prog_free);
+
/* Base function for offset calculation. Needs to go into .text section,
* therefore keeping it non-static as well; will also be used by JITs
* anyway later on, so do not let the compiler omit it.
@@ -523,12 +585,26 @@ void bpf_prog_select_runtime(struct bpf_prog *fp)
/* Probe if internal BPF can be JITed */
bpf_int_jit_compile(fp);
+ /* Lock whole bpf_prog as read-only */
+ bpf_prog_lock_ro(fp);
}
EXPORT_SYMBOL_GPL(bpf_prog_select_runtime);
-/* free internal BPF program */
+static void bpf_prog_free_deferred(struct work_struct *work)
+{
+ struct bpf_work_struct *ws;
+
+ ws = container_of(work, struct bpf_work_struct, work);
+ bpf_jit_free(ws->prog);
+}
+
+/* Free internal BPF program */
void bpf_prog_free(struct bpf_prog *fp)
{
- bpf_jit_free(fp);
+ struct bpf_work_struct *ws = fp->work;
+
+ INIT_WORK(&ws->work, bpf_prog_free_deferred);
+ ws->prog = fp;
+ schedule_work(&ws->work);
}
EXPORT_SYMBOL_GPL(bpf_prog_free);
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 44eb005c6695..84922befea84 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -395,16 +395,15 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
if (!filter)
goto free_prog;
- filter->prog = kzalloc(bpf_prog_size(new_len),
- GFP_KERNEL|__GFP_NOWARN);
+ filter->prog = bpf_prog_alloc(bpf_prog_size(new_len), __GFP_NOWARN);
if (!filter->prog)
goto free_filter;
ret = bpf_convert_filter(fp, fprog->len, filter->prog->insnsi, &new_len);
if (ret)
goto free_filter_prog;
- kfree(fp);
+ kfree(fp);
atomic_set(&filter->usage, 1);
filter->prog->len = new_len;
@@ -413,7 +412,7 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
return filter;
free_filter_prog:
- kfree(filter->prog);
+ __bpf_prog_free(filter->prog);
free_filter:
kfree(filter);
free_prog:
diff --git a/lib/test_bpf.c b/lib/test_bpf.c
index 8c66c6aace04..9a67456ba29a 100644
--- a/lib/test_bpf.c
+++ b/lib/test_bpf.c
@@ -1836,7 +1836,7 @@ static struct bpf_prog *generate_filter(int which, int *err)
break;
case INTERNAL:
- fp = kzalloc(bpf_prog_size(flen), GFP_KERNEL);
+ fp = bpf_prog_alloc(bpf_prog_size(flen), 0);
if (fp == NULL) {
pr_cont("UNEXPECTED_FAIL no memory left\n");
*err = -ENOMEM;
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index cf999e09bcd2..72e899a3efda 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -365,11 +365,8 @@ void dev_load(struct net *net, const char *name)
no_module = !dev;
if (no_module && capable(CAP_NET_ADMIN))
no_module = request_module("netdev-%s", name);
- if (no_module && capable(CAP_SYS_MODULE)) {
- if (!request_module("%s", name))
- pr_warn("Loading kernel module for a network device with CAP_SYS_MODULE (deprecated). Use CAP_NET_ADMIN and alias netdev-%s instead.\n",
- name);
- }
+ if (no_module && capable(CAP_SYS_MODULE))
+ request_module("%s", name);
}
EXPORT_SYMBOL(dev_load);
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 17cb912793fa..27e61b886520 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -1621,6 +1621,80 @@ static int ethtool_get_module_eeprom(struct net_device *dev,
modinfo.eeprom_len);
}
+static int ethtool_tunable_valid(const struct ethtool_tunable *tuna)
+{
+ switch (tuna->id) {
+ case ETHTOOL_RX_COPYBREAK:
+ if (tuna->len != sizeof(u32) ||
+ tuna->type_id != ETHTOOL_TUNABLE_U32)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ethtool_get_tunable(struct net_device *dev, void __user *useraddr)
+{
+ int ret;
+ struct ethtool_tunable tuna;
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ void *data;
+
+ if (!ops->get_tunable)
+ return -EOPNOTSUPP;
+ if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
+ return -EFAULT;
+ ret = ethtool_tunable_valid(&tuna);
+ if (ret)
+ return ret;
+ data = kmalloc(tuna.len, GFP_USER);
+ if (!data)
+ return -ENOMEM;
+ ret = ops->get_tunable(dev, &tuna, data);
+ if (ret)
+ goto out;
+ useraddr += sizeof(tuna);
+ ret = -EFAULT;
+ if (copy_to_user(useraddr, data, tuna.len))
+ goto out;
+ ret = 0;
+
+out:
+ kfree(data);
+ return ret;
+}
+
+static int ethtool_set_tunable(struct net_device *dev, void __user *useraddr)
+{
+ int ret;
+ struct ethtool_tunable tuna;
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ void *data;
+
+ if (!ops->set_tunable)
+ return -EOPNOTSUPP;
+ if (copy_from_user(&tuna, useraddr, sizeof(tuna)))
+ return -EFAULT;
+ ret = ethtool_tunable_valid(&tuna);
+ if (ret)
+ return ret;
+ data = kmalloc(tuna.len, GFP_USER);
+ if (!data)
+ return -ENOMEM;
+ useraddr += sizeof(tuna);
+ ret = -EFAULT;
+ if (copy_from_user(data, useraddr, tuna.len))
+ goto out;
+ ret = ops->set_tunable(dev, &tuna, data);
+
+out:
+ kfree(data);
+ return ret;
+}
+
/* The main entry point in this file. Called from net/core/dev_ioctl.c */
int dev_ethtool(struct net *net, struct ifreq *ifr)
@@ -1670,6 +1744,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_GCHANNELS:
case ETHTOOL_GET_TS_INFO:
case ETHTOOL_GEEE:
+ case ETHTOOL_GTUNABLE:
break;
default:
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
@@ -1857,6 +1932,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_GMODULEEEPROM:
rc = ethtool_get_module_eeprom(dev, useraddr);
break;
+ case ETHTOOL_GTUNABLE:
+ rc = ethtool_get_tunable(dev, useraddr);
+ break;
+ case ETHTOOL_STUNABLE:
+ rc = ethtool_set_tunable(dev, useraddr);
+ break;
default:
rc = -EOPNOTSUPP;
}
diff --git a/net/core/filter.c b/net/core/filter.c
index d814b8a89d0f..37f8eb06fdee 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -933,7 +933,7 @@ static struct bpf_prog *bpf_migrate_filter(struct bpf_prog *fp)
/* Expand fp for appending the new filter representation. */
old_fp = fp;
- fp = krealloc(old_fp, bpf_prog_size(new_len), GFP_KERNEL);
+ fp = bpf_prog_realloc(old_fp, bpf_prog_size(new_len), 0);
if (!fp) {
/* The old_fp is still around in case we couldn't
* allocate new memory, so uncharge on that one.
@@ -1013,7 +1013,7 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog)
if (fprog->filter == NULL)
return -EINVAL;
- fp = kmalloc(bpf_prog_size(fprog->len), GFP_KERNEL);
+ fp = bpf_prog_alloc(bpf_prog_size(fprog->len), 0);
if (!fp)
return -ENOMEM;
@@ -1069,7 +1069,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
if (fprog->filter == NULL)
return -EINVAL;
- prog = kmalloc(bpf_fsize, GFP_KERNEL);
+ prog = bpf_prog_alloc(bpf_fsize, 0);
if (!prog)
return -ENOMEM;
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index b10cd43a4722..5b6efb3d2308 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -157,9 +157,12 @@ static void rt_fibinfo_free(struct rtable __rcu **rtp)
static void free_nh_exceptions(struct fib_nh *nh)
{
- struct fnhe_hash_bucket *hash = nh->nh_exceptions;
+ struct fnhe_hash_bucket *hash;
int i;
+ hash = rcu_dereference_protected(nh->nh_exceptions, 1);
+ if (!hash)
+ return;
for (i = 0; i < FNHE_HASH_SIZE; i++) {
struct fib_nh_exception *fnhe;
@@ -205,8 +208,7 @@ static void free_fib_info_rcu(struct rcu_head *head)
change_nexthops(fi) {
if (nexthop_nh->nh_dev)
dev_put(nexthop_nh->nh_dev);
- if (nexthop_nh->nh_exceptions)
- free_nh_exceptions(nexthop_nh);
+ free_nh_exceptions(nexthop_nh);
rt_fibinfo_free_cpus(nexthop_nh->nh_pcpu_rth_output);
rt_fibinfo_free(&nexthop_nh->nh_rth_input);
} endfor_nexthops(fi);
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 890c4258804c..4146153d875d 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -117,7 +117,7 @@
#define IGMP_V2_Unsolicited_Report_Interval (10*HZ)
#define IGMP_V3_Unsolicited_Report_Interval (1*HZ)
#define IGMP_Query_Response_Interval (10*HZ)
-#define IGMP_Unsolicited_Report_Count 2
+#define IGMP_Query_Robustness_Variable 2
#define IGMP_Initial_Report_Delay (1)
@@ -756,8 +756,7 @@ static void igmp_ifc_event(struct in_device *in_dev)
{
if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev))
return;
- in_dev->mr_ifc_count = in_dev->mr_qrv ? in_dev->mr_qrv :
- IGMP_Unsolicited_Report_Count;
+ in_dev->mr_ifc_count = in_dev->mr_qrv ?: sysctl_igmp_qrv;
igmp_ifc_start_timer(in_dev, 1);
}
@@ -1086,8 +1085,7 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im)
pmc->interface = im->interface;
in_dev_hold(in_dev);
pmc->multiaddr = im->multiaddr;
- pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
- IGMP_Unsolicited_Report_Count;
+ pmc->crcount = in_dev->mr_qrv ?: sysctl_igmp_qrv;
pmc->sfmode = im->sfmode;
if (pmc->sfmode == MCAST_INCLUDE) {
struct ip_sf_list *psf;
@@ -1226,8 +1224,7 @@ static void igmp_group_added(struct ip_mc_list *im)
}
/* else, v3 */
- im->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
- IGMP_Unsolicited_Report_Count;
+ im->crcount = in_dev->mr_qrv ?: sysctl_igmp_qrv;
igmp_ifc_event(in_dev);
#endif
}
@@ -1322,7 +1319,7 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
spin_lock_init(&im->lock);
#ifdef CONFIG_IP_MULTICAST
setup_timer(&im->timer, igmp_timer_expire, (unsigned long)im);
- im->unsolicit_count = IGMP_Unsolicited_Report_Count;
+ im->unsolicit_count = sysctl_igmp_qrv;
#endif
im->next_rcu = in_dev->mc_list;
@@ -1460,7 +1457,7 @@ void ip_mc_init_dev(struct in_device *in_dev)
(unsigned long)in_dev);
setup_timer(&in_dev->mr_ifc_timer, igmp_ifc_timer_expire,
(unsigned long)in_dev);
- in_dev->mr_qrv = IGMP_Unsolicited_Report_Count;
+ in_dev->mr_qrv = sysctl_igmp_qrv;
#endif
spin_lock_init(&in_dev->mc_tomb_lock);
@@ -1474,6 +1471,9 @@ void ip_mc_up(struct in_device *in_dev)
ASSERT_RTNL();
+#ifdef CONFIG_IP_MULTICAST
+ in_dev->mr_qrv = sysctl_igmp_qrv;
+#endif
ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
for_each_pmc_rtnl(in_dev, pmc)
@@ -1540,7 +1540,9 @@ static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr)
*/
int sysctl_igmp_max_memberships __read_mostly = IP_MAX_MEMBERSHIPS;
int sysctl_igmp_max_msf __read_mostly = IP_MAX_MSF;
-
+#ifdef CONFIG_IP_MULTICAST
+int sysctl_igmp_qrv __read_mostly = IGMP_Query_Robustness_Variable;
+#endif
static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
__be32 *psfsrc)
@@ -1575,8 +1577,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
#ifdef CONFIG_IP_MULTICAST
if (psf->sf_oldin &&
!IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) {
- psf->sf_crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
- IGMP_Unsolicited_Report_Count;
+ psf->sf_crcount = in_dev->mr_qrv ?: sysctl_igmp_qrv;
psf->sf_next = pmc->tomb;
pmc->tomb = psf;
rv = 1;
@@ -1639,8 +1640,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
/* filter mode change */
pmc->sfmode = MCAST_INCLUDE;
#ifdef CONFIG_IP_MULTICAST
- pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
- IGMP_Unsolicited_Report_Count;
+ pmc->crcount = in_dev->mr_qrv ?: sysctl_igmp_qrv;
in_dev->mr_ifc_count = pmc->crcount;
for (psf = pmc->sources; psf; psf = psf->sf_next)
psf->sf_crcount = 0;
@@ -1818,8 +1818,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
#ifdef CONFIG_IP_MULTICAST
/* else no filters; keep old mode for reports */
- pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
- IGMP_Unsolicited_Report_Count;
+ pmc->crcount = in_dev->mr_qrv ?: sysctl_igmp_qrv;
in_dev->mr_ifc_count = pmc->crcount;
for (psf = pmc->sources; psf; psf = psf->sf_next)
psf->sf_crcount = 0;
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index eaa4b000c7b4..44b0cbdd76f1 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -628,12 +628,12 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
spin_lock_bh(&fnhe_lock);
- hash = nh->nh_exceptions;
+ hash = rcu_dereference(nh->nh_exceptions);
if (!hash) {
hash = kzalloc(FNHE_HASH_SIZE * sizeof(*hash), GFP_ATOMIC);
if (!hash)
goto out_unlock;
- nh->nh_exceptions = hash;
+ rcu_assign_pointer(nh->nh_exceptions, hash);
}
hash += hval;
@@ -1242,7 +1242,7 @@ static unsigned int ipv4_mtu(const struct dst_entry *dst)
static struct fib_nh_exception *find_exception(struct fib_nh *nh, __be32 daddr)
{
- struct fnhe_hash_bucket *hash = nh->nh_exceptions;
+ struct fnhe_hash_bucket *hash = rcu_dereference(nh->nh_exceptions);
struct fib_nh_exception *fnhe;
u32 hval;
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 79a007c52558..45d156dacd61 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -450,6 +450,16 @@ static struct ctl_table ipv4_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec
},
+#ifdef CONFIG_IP_MULTICAST
+ {
+ .procname = "igmp_qrv",
+ .data = &sysctl_igmp_qrv,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &one
+ },
+#endif
{
.procname = "inet_peer_threshold",
.data = &inet_peer_threshold,
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 70881795da96..64919425f1ab 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -121,6 +121,7 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
#define IPV6_MLD_MAX_MSF 64
int sysctl_mld_max_msf __read_mostly = IPV6_MLD_MAX_MSF;
+int sysctl_mld_qrv __read_mostly = MLD_QRV_DEFAULT;
/*
* socket join on multicast group
@@ -1191,15 +1192,16 @@ static void mld_update_qrv(struct inet6_dev *idev,
* and SHOULD NOT be one. Catch this here if we ever run
* into such a case in future.
*/
+ const int min_qrv = min(MLD_QRV_DEFAULT, sysctl_mld_qrv);
WARN_ON(idev->mc_qrv == 0);
if (mlh2->mld2q_qrv > 0)
idev->mc_qrv = mlh2->mld2q_qrv;
- if (unlikely(idev->mc_qrv < 2)) {
+ if (unlikely(idev->mc_qrv < min_qrv)) {
net_warn_ratelimited("IPv6: MLD: clamping QRV from %u to %u!\n",
- idev->mc_qrv, MLD_QRV_DEFAULT);
- idev->mc_qrv = MLD_QRV_DEFAULT;
+ idev->mc_qrv, min_qrv);
+ idev->mc_qrv = min_qrv;
}
}
@@ -2478,6 +2480,14 @@ void ipv6_mc_down(struct inet6_dev *idev)
mld_clear_delrec(idev);
}
+static void ipv6_mc_reset(struct inet6_dev *idev)
+{
+ idev->mc_qrv = sysctl_mld_qrv;
+ idev->mc_qi = MLD_QI_DEFAULT;
+ idev->mc_qri = MLD_QRI_DEFAULT;
+ idev->mc_v1_seen = 0;
+ idev->mc_maxdelay = unsolicited_report_interval(idev);
+}
/* Device going up */
@@ -2488,6 +2498,7 @@ void ipv6_mc_up(struct inet6_dev *idev)
/* Install multicast list, except for all-nodes (already installed) */
read_lock_bh(&idev->lock);
+ ipv6_mc_reset(idev);
for (i = idev->mc_list; i; i = i->next)
igmp6_group_added(i);
read_unlock_bh(&idev->lock);
@@ -2508,13 +2519,7 @@ void ipv6_mc_init_dev(struct inet6_dev *idev)
(unsigned long)idev);
setup_timer(&idev->mc_dad_timer, mld_dad_timer_expire,
(unsigned long)idev);
-
- idev->mc_qrv = MLD_QRV_DEFAULT;
- idev->mc_qi = MLD_QI_DEFAULT;
- idev->mc_qri = MLD_QRI_DEFAULT;
-
- idev->mc_maxdelay = unsolicited_report_interval(idev);
- idev->mc_v1_seen = 0;
+ ipv6_mc_reset(idev);
write_unlock_bh(&idev->lock);
}
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index 0c56c93619e0..c5c10fafcfe2 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -16,6 +16,8 @@
#include <net/addrconf.h>
#include <net/inet_frag.h>
+static int one = 1;
+
static struct ctl_table ipv6_table_template[] = {
{
.procname = "bindv6only",
@@ -63,6 +65,14 @@ static struct ctl_table ipv6_rotable[] = {
.mode = 0644,
.proc_handler = proc_dointvec
},
+ {
+ .procname = "mld_qrv",
+ .data = &sysctl_mld_qrv,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &one
+ },
{ }
};
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 797c0af71c06..2aa2b6c15f20 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -148,7 +148,7 @@ do { \
atomic_read(&_t->ref_count)); \
l2tp_tunnel_inc_refcount_1(_t); \
} while (0)
-#define l2tp_tunnel_dec_refcount(_t)
+#define l2tp_tunnel_dec_refcount(_t) \
do { \
pr_debug("l2tp_tunnel_dec_refcount: %s:%d %s: cnt=%d\n", \
__func__, __LINE__, (_t)->name, \