mirror of
https://github.com/Fishwaldo/build.git
synced 2025-03-26 16:51:48 +00:00
1382 lines
46 KiB
Diff
1382 lines
46 KiB
Diff
diff --git a/Makefile b/Makefile
|
|
index 7c6f28e7a2f6..fba9b09a1330 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -1,6 +1,6 @@
|
|
VERSION = 4
|
|
PATCHLEVEL = 4
|
|
-SUBLEVEL = 31
|
|
+SUBLEVEL = 32
|
|
EXTRAVERSION =
|
|
NAME = Blurry Fish Butt
|
|
|
|
diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c
|
|
index bbe56871245c..4298aeb1e20f 100644
|
|
--- a/arch/mips/kvm/emulate.c
|
|
+++ b/arch/mips/kvm/emulate.c
|
|
@@ -822,7 +822,7 @@ static void kvm_mips_invalidate_guest_tlb(struct kvm_vcpu *vcpu,
|
|
bool user;
|
|
|
|
/* No need to flush for entries which are already invalid */
|
|
- if (!((tlb->tlb_lo[0] | tlb->tlb_lo[1]) & ENTRYLO_V))
|
|
+ if (!((tlb->tlb_lo0 | tlb->tlb_lo1) & MIPS3_PG_V))
|
|
return;
|
|
/* User address space doesn't need flushing for KSeg2/3 changes */
|
|
user = tlb->tlb_hi < KVM_GUEST_KSEG0;
|
|
diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_dp.c b/drivers/gpu/drm/amd/amdgpu/atombios_dp.c
|
|
index 21aacc1f45c1..7f85c2c1d681 100644
|
|
--- a/drivers/gpu/drm/amd/amdgpu/atombios_dp.c
|
|
+++ b/drivers/gpu/drm/amd/amdgpu/atombios_dp.c
|
|
@@ -265,15 +265,27 @@ static int amdgpu_atombios_dp_get_dp_link_config(struct drm_connector *connector
|
|
unsigned max_lane_num = drm_dp_max_lane_count(dpcd);
|
|
unsigned lane_num, i, max_pix_clock;
|
|
|
|
- for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
|
|
- for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
|
|
- max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
|
|
+ if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) ==
|
|
+ ENCODER_OBJECT_ID_NUTMEG) {
|
|
+ for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
|
|
+ max_pix_clock = (lane_num * 270000 * 8) / bpp;
|
|
if (max_pix_clock >= pix_clock) {
|
|
*dp_lanes = lane_num;
|
|
- *dp_rate = link_rates[i];
|
|
+ *dp_rate = 270000;
|
|
return 0;
|
|
}
|
|
}
|
|
+ } else {
|
|
+ for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
|
|
+ for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
|
|
+ max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
|
|
+ if (max_pix_clock >= pix_clock) {
|
|
+ *dp_lanes = lane_num;
|
|
+ *dp_rate = link_rates[i];
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
return -EINVAL;
|
|
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
|
|
index 44ee72e04df9..b5760851195c 100644
|
|
--- a/drivers/gpu/drm/radeon/atombios_dp.c
|
|
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
|
|
@@ -315,15 +315,27 @@ int radeon_dp_get_dp_link_config(struct drm_connector *connector,
|
|
unsigned max_lane_num = drm_dp_max_lane_count(dpcd);
|
|
unsigned lane_num, i, max_pix_clock;
|
|
|
|
- for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
|
|
- for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
|
|
- max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
|
|
+ if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) ==
|
|
+ ENCODER_OBJECT_ID_NUTMEG) {
|
|
+ for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
|
|
+ max_pix_clock = (lane_num * 270000 * 8) / bpp;
|
|
if (max_pix_clock >= pix_clock) {
|
|
*dp_lanes = lane_num;
|
|
- *dp_rate = link_rates[i];
|
|
+ *dp_rate = 270000;
|
|
return 0;
|
|
}
|
|
}
|
|
+ } else {
|
|
+ for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
|
|
+ for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
|
|
+ max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
|
|
+ if (max_pix_clock >= pix_clock) {
|
|
+ *dp_lanes = lane_num;
|
|
+ *dp_rate = link_rates[i];
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
return -EINVAL;
|
|
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
|
|
index ca5ac5d6f4e6..49056c33be74 100644
|
|
--- a/drivers/net/ethernet/broadcom/tg3.c
|
|
+++ b/drivers/net/ethernet/broadcom/tg3.c
|
|
@@ -18142,14 +18142,14 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev,
|
|
|
|
rtnl_lock();
|
|
|
|
- /* We needn't recover from permanent error */
|
|
- if (state == pci_channel_io_frozen)
|
|
- tp->pcierr_recovery = true;
|
|
-
|
|
/* We probably don't have netdev yet */
|
|
if (!netdev || !netif_running(netdev))
|
|
goto done;
|
|
|
|
+ /* We needn't recover from permanent error */
|
|
+ if (state == pci_channel_io_frozen)
|
|
+ tp->pcierr_recovery = true;
|
|
+
|
|
tg3_phy_stop(tp);
|
|
|
|
tg3_netif_stop(tp);
|
|
@@ -18246,7 +18246,7 @@ static void tg3_io_resume(struct pci_dev *pdev)
|
|
|
|
rtnl_lock();
|
|
|
|
- if (!netif_running(netdev))
|
|
+ if (!netdev || !netif_running(netdev))
|
|
goto done;
|
|
|
|
tg3_full_lock(tp, 0);
|
|
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
|
|
index f6147ffc7fbc..ab716042bdd2 100644
|
|
--- a/drivers/net/ethernet/freescale/fec_main.c
|
|
+++ b/drivers/net/ethernet/freescale/fec_main.c
|
|
@@ -944,11 +944,11 @@ fec_restart(struct net_device *ndev)
|
|
* enet-mac reset will reset mac address registers too,
|
|
* so need to reconfigure it.
|
|
*/
|
|
- if (fep->quirks & FEC_QUIRK_ENET_MAC) {
|
|
- memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
|
|
- writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
|
|
- writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
|
|
- }
|
|
+ memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
|
|
+ writel((__force u32)cpu_to_be32(temp_mac[0]),
|
|
+ fep->hwp + FEC_ADDR_LOW);
|
|
+ writel((__force u32)cpu_to_be32(temp_mac[1]),
|
|
+ fep->hwp + FEC_ADDR_HIGH);
|
|
|
|
/* Clear any outstanding interrupt. */
|
|
writel(0xffffffff, fep->hwp + FEC_IEVENT);
|
|
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
|
|
index 69e31e2a68fc..4827c6987ac3 100644
|
|
--- a/drivers/net/geneve.c
|
|
+++ b/drivers/net/geneve.c
|
|
@@ -440,7 +440,7 @@ static struct sk_buff **geneve_gro_receive(struct sk_buff **head,
|
|
|
|
skb_gro_pull(skb, gh_len);
|
|
skb_gro_postpull_rcsum(skb, gh, gh_len);
|
|
- pp = ptype->callbacks.gro_receive(head, skb);
|
|
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
|
|
|
|
out_unlock:
|
|
rcu_read_unlock();
|
|
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
|
|
index 003780901628..6fa8e165878e 100644
|
|
--- a/drivers/net/vxlan.c
|
|
+++ b/drivers/net/vxlan.c
|
|
@@ -593,7 +593,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head,
|
|
}
|
|
}
|
|
|
|
- pp = eth_gro_receive(head, skb);
|
|
+ pp = call_gro_receive(eth_gro_receive, head, skb);
|
|
|
|
out:
|
|
skb_gro_remcsum_cleanup(skb, &grc);
|
|
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
|
|
index ed01c0172e4a..07dd81586c52 100644
|
|
--- a/drivers/of/of_reserved_mem.c
|
|
+++ b/drivers/of/of_reserved_mem.c
|
|
@@ -127,8 +127,12 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
|
|
}
|
|
|
|
/* Need adjust the alignment to satisfy the CMA requirement */
|
|
- if (IS_ENABLED(CONFIG_CMA) && of_flat_dt_is_compatible(node, "shared-dma-pool"))
|
|
- align = max(align, (phys_addr_t)PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order));
|
|
+ if (IS_ENABLED(CONFIG_CMA) && of_flat_dt_is_compatible(node, "shared-dma-pool")) {
|
|
+ unsigned long order =
|
|
+ max_t(unsigned long, MAX_ORDER - 1, pageblock_order);
|
|
+
|
|
+ align = max(align, (phys_addr_t)PAGE_SIZE << order);
|
|
+ }
|
|
|
|
prop = of_get_flat_dt_prop(node, "alloc-ranges", &len);
|
|
if (prop) {
|
|
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
|
|
index ef4ff03242ea..aaf7da07a358 100644
|
|
--- a/drivers/scsi/megaraid/megaraid_sas.h
|
|
+++ b/drivers/scsi/megaraid/megaraid_sas.h
|
|
@@ -1923,7 +1923,7 @@ struct megasas_instance_template {
|
|
};
|
|
|
|
#define MEGASAS_IS_LOGICAL(scp) \
|
|
- (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1
|
|
+ ((scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1)
|
|
|
|
#define MEGASAS_DEV_INDEX(scp) \
|
|
(((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \
|
|
diff --git a/include/linux/mroute.h b/include/linux/mroute.h
|
|
index 79aaa9fc1a15..d5277fc3ce2e 100644
|
|
--- a/include/linux/mroute.h
|
|
+++ b/include/linux/mroute.h
|
|
@@ -103,5 +103,5 @@ struct mfc_cache {
|
|
struct rtmsg;
|
|
extern int ipmr_get_route(struct net *net, struct sk_buff *skb,
|
|
__be32 saddr, __be32 daddr,
|
|
- struct rtmsg *rtm, int nowait);
|
|
+ struct rtmsg *rtm, int nowait, u32 portid);
|
|
#endif
|
|
diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h
|
|
index 66982e764051..f831155dc7d1 100644
|
|
--- a/include/linux/mroute6.h
|
|
+++ b/include/linux/mroute6.h
|
|
@@ -115,7 +115,7 @@ struct mfc6_cache {
|
|
|
|
struct rtmsg;
|
|
extern int ip6mr_get_route(struct net *net, struct sk_buff *skb,
|
|
- struct rtmsg *rtm, int nowait);
|
|
+ struct rtmsg *rtm, int nowait, u32 portid);
|
|
|
|
#ifdef CONFIG_IPV6_MROUTE
|
|
extern struct sock *mroute6_socket(struct net *net, struct sk_buff *skb);
|
|
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
|
|
index 12b4d54a8ffa..9d6025703f73 100644
|
|
--- a/include/linux/netdevice.h
|
|
+++ b/include/linux/netdevice.h
|
|
@@ -2003,7 +2003,10 @@ struct napi_gro_cb {
|
|
/* Used in foo-over-udp, set in udp[46]_gro_receive */
|
|
u8 is_ipv6:1;
|
|
|
|
- /* 7 bit hole */
|
|
+ /* Number of gro_receive callbacks this packet already went through */
|
|
+ u8 recursion_counter:4;
|
|
+
|
|
+ /* 3 bit hole */
|
|
|
|
/* used to support CHECKSUM_COMPLETE for tunneling protocols */
|
|
__wsum csum;
|
|
@@ -2014,6 +2017,25 @@ struct napi_gro_cb {
|
|
|
|
#define NAPI_GRO_CB(skb) ((struct napi_gro_cb *)(skb)->cb)
|
|
|
|
+#define GRO_RECURSION_LIMIT 15
|
|
+static inline int gro_recursion_inc_test(struct sk_buff *skb)
|
|
+{
|
|
+ return ++NAPI_GRO_CB(skb)->recursion_counter == GRO_RECURSION_LIMIT;
|
|
+}
|
|
+
|
|
+typedef struct sk_buff **(*gro_receive_t)(struct sk_buff **, struct sk_buff *);
|
|
+static inline struct sk_buff **call_gro_receive(gro_receive_t cb,
|
|
+ struct sk_buff **head,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+ if (unlikely(gro_recursion_inc_test(skb))) {
|
|
+ NAPI_GRO_CB(skb)->flush |= 1;
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return cb(head, skb);
|
|
+}
|
|
+
|
|
struct packet_type {
|
|
__be16 type; /* This is really htons(ether_type). */
|
|
struct net_device *dev; /* NULL is wildcarded here */
|
|
@@ -2059,6 +2081,22 @@ struct udp_offload {
|
|
struct udp_offload_callbacks callbacks;
|
|
};
|
|
|
|
+typedef struct sk_buff **(*gro_receive_udp_t)(struct sk_buff **,
|
|
+ struct sk_buff *,
|
|
+ struct udp_offload *);
|
|
+static inline struct sk_buff **call_gro_receive_udp(gro_receive_udp_t cb,
|
|
+ struct sk_buff **head,
|
|
+ struct sk_buff *skb,
|
|
+ struct udp_offload *uoff)
|
|
+{
|
|
+ if (unlikely(gro_recursion_inc_test(skb))) {
|
|
+ NAPI_GRO_CB(skb)->flush |= 1;
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return cb(head, skb, uoff);
|
|
+}
|
|
+
|
|
/* often modified stats are per cpu, other are shared (netdev->stats) */
|
|
struct pcpu_sw_netstats {
|
|
u64 rx_packets;
|
|
diff --git a/include/net/ip.h b/include/net/ip.h
|
|
index 1a98f1ca1638..b450d8653b30 100644
|
|
--- a/include/net/ip.h
|
|
+++ b/include/net/ip.h
|
|
@@ -553,7 +553,7 @@ int ip_options_rcv_srr(struct sk_buff *skb);
|
|
*/
|
|
|
|
void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb);
|
|
-void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, int offset);
|
|
+void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, int tlen, int offset);
|
|
int ip_cmsg_send(struct net *net, struct msghdr *msg,
|
|
struct ipcm_cookie *ipc, bool allow_ipv6);
|
|
int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
|
|
@@ -575,7 +575,7 @@ void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport,
|
|
|
|
static inline void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
|
|
{
|
|
- ip_cmsg_recv_offset(msg, skb, 0);
|
|
+ ip_cmsg_recv_offset(msg, skb, 0, 0);
|
|
}
|
|
|
|
bool icmp_global_allow(void);
|
|
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
|
|
index 86df0835f6b5..e5bba897d206 100644
|
|
--- a/include/net/sch_generic.h
|
|
+++ b/include/net/sch_generic.h
|
|
@@ -408,6 +408,15 @@ bool tcf_destroy(struct tcf_proto *tp, bool force);
|
|
void tcf_destroy_chain(struct tcf_proto __rcu **fl);
|
|
int skb_do_redirect(struct sk_buff *);
|
|
|
|
+static inline bool skb_at_tc_ingress(const struct sk_buff *skb)
|
|
+{
|
|
+#ifdef CONFIG_NET_CLS_ACT
|
|
+ return G_TC_AT(skb->tc_verd) & AT_INGRESS;
|
|
+#else
|
|
+ return false;
|
|
+#endif
|
|
+}
|
|
+
|
|
/* Reset all TX qdiscs greater then index of a device. */
|
|
static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i)
|
|
{
|
|
diff --git a/include/net/sock.h b/include/net/sock.h
|
|
index 14d3c0734007..3d5ff7436f41 100644
|
|
--- a/include/net/sock.h
|
|
+++ b/include/net/sock.h
|
|
@@ -1425,6 +1425,16 @@ static inline void sk_mem_uncharge(struct sock *sk, int size)
|
|
if (!sk_has_account(sk))
|
|
return;
|
|
sk->sk_forward_alloc += size;
|
|
+
|
|
+ /* Avoid a possible overflow.
|
|
+ * TCP send queues can make this happen, if sk_mem_reclaim()
|
|
+ * is not called and more than 2 GBytes are released at once.
|
|
+ *
|
|
+ * If we reach 2 MBytes, reclaim 1 MBytes right now, there is
|
|
+ * no need to hold that much forward allocation anyway.
|
|
+ */
|
|
+ if (unlikely(sk->sk_forward_alloc >= 1 << 21))
|
|
+ __sk_mem_reclaim(sk, 1 << 20);
|
|
}
|
|
|
|
static inline void sk_wmem_free_skb(struct sock *sk, struct sk_buff *skb)
|
|
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
|
|
index 123a5af4e8bb..fa3b34365560 100644
|
|
--- a/include/uapi/linux/rtnetlink.h
|
|
+++ b/include/uapi/linux/rtnetlink.h
|
|
@@ -343,7 +343,7 @@ struct rtnexthop {
|
|
#define RTNH_F_OFFLOAD 8 /* offloaded route */
|
|
#define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */
|
|
|
|
-#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN)
|
|
+#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD)
|
|
|
|
/* Macros to handle hexthops */
|
|
|
|
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
|
|
index d2cd9de4b724..ad8d6e6b87ca 100644
|
|
--- a/net/8021q/vlan.c
|
|
+++ b/net/8021q/vlan.c
|
|
@@ -659,7 +659,7 @@ static struct sk_buff **vlan_gro_receive(struct sk_buff **head,
|
|
|
|
skb_gro_pull(skb, sizeof(*vhdr));
|
|
skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr));
|
|
- pp = ptype->callbacks.gro_receive(head, skb);
|
|
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
|
|
|
|
out_unlock:
|
|
rcu_read_unlock();
|
|
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
|
|
index 9542e84a9455..d80c15d028fe 100644
|
|
--- a/net/bridge/br_multicast.c
|
|
+++ b/net/bridge/br_multicast.c
|
|
@@ -951,13 +951,12 @@ static void br_multicast_enable(struct bridge_mcast_own_query *query)
|
|
mod_timer(&query->timer, jiffies);
|
|
}
|
|
|
|
-void br_multicast_enable_port(struct net_bridge_port *port)
|
|
+static void __br_multicast_enable_port(struct net_bridge_port *port)
|
|
{
|
|
struct net_bridge *br = port->br;
|
|
|
|
- spin_lock(&br->multicast_lock);
|
|
if (br->multicast_disabled || !netif_running(br->dev))
|
|
- goto out;
|
|
+ return;
|
|
|
|
br_multicast_enable(&port->ip4_own_query);
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
@@ -965,8 +964,14 @@ void br_multicast_enable_port(struct net_bridge_port *port)
|
|
#endif
|
|
if (port->multicast_router == 2 && hlist_unhashed(&port->rlist))
|
|
br_multicast_add_router(br, port);
|
|
+}
|
|
|
|
-out:
|
|
+void br_multicast_enable_port(struct net_bridge_port *port)
|
|
+{
|
|
+ struct net_bridge *br = port->br;
|
|
+
|
|
+ spin_lock(&br->multicast_lock);
|
|
+ __br_multicast_enable_port(port);
|
|
spin_unlock(&br->multicast_lock);
|
|
}
|
|
|
|
@@ -1905,8 +1910,9 @@ static void br_multicast_start_querier(struct net_bridge *br,
|
|
|
|
int br_multicast_toggle(struct net_bridge *br, unsigned long val)
|
|
{
|
|
- int err = 0;
|
|
struct net_bridge_mdb_htable *mdb;
|
|
+ struct net_bridge_port *port;
|
|
+ int err = 0;
|
|
|
|
spin_lock_bh(&br->multicast_lock);
|
|
if (br->multicast_disabled == !val)
|
|
@@ -1934,10 +1940,9 @@ rollback:
|
|
goto rollback;
|
|
}
|
|
|
|
- br_multicast_start_querier(br, &br->ip4_own_query);
|
|
-#if IS_ENABLED(CONFIG_IPV6)
|
|
- br_multicast_start_querier(br, &br->ip6_own_query);
|
|
-#endif
|
|
+ br_multicast_open(br);
|
|
+ list_for_each_entry(port, &br->port_list, list)
|
|
+ __br_multicast_enable_port(port);
|
|
|
|
unlock:
|
|
spin_unlock_bh(&br->multicast_lock);
|
|
diff --git a/net/core/dev.c b/net/core/dev.c
|
|
index 0989fea88c44..b3fa4b86ab4c 100644
|
|
--- a/net/core/dev.c
|
|
+++ b/net/core/dev.c
|
|
@@ -2836,6 +2836,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d
|
|
}
|
|
return head;
|
|
}
|
|
+EXPORT_SYMBOL_GPL(validate_xmit_skb_list);
|
|
|
|
static void qdisc_pkt_len_init(struct sk_buff *skb)
|
|
{
|
|
@@ -4240,6 +4241,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
|
|
NAPI_GRO_CB(skb)->flush = 0;
|
|
NAPI_GRO_CB(skb)->free = 0;
|
|
NAPI_GRO_CB(skb)->encap_mark = 0;
|
|
+ NAPI_GRO_CB(skb)->recursion_counter = 0;
|
|
NAPI_GRO_CB(skb)->gro_remcsum_start = 0;
|
|
|
|
/* Setup for GRO checksum validation */
|
|
@@ -5204,6 +5206,7 @@ static inline bool netdev_adjacent_is_neigh_list(struct net_device *dev,
|
|
|
|
static int __netdev_adjacent_dev_insert(struct net_device *dev,
|
|
struct net_device *adj_dev,
|
|
+ u16 ref_nr,
|
|
struct list_head *dev_list,
|
|
void *private, bool master)
|
|
{
|
|
@@ -5213,7 +5216,7 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev,
|
|
adj = __netdev_find_adj(adj_dev, dev_list);
|
|
|
|
if (adj) {
|
|
- adj->ref_nr++;
|
|
+ adj->ref_nr += ref_nr;
|
|
return 0;
|
|
}
|
|
|
|
@@ -5223,7 +5226,7 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev,
|
|
|
|
adj->dev = adj_dev;
|
|
adj->master = master;
|
|
- adj->ref_nr = 1;
|
|
+ adj->ref_nr = ref_nr;
|
|
adj->private = private;
|
|
dev_hold(adj_dev);
|
|
|
|
@@ -5262,6 +5265,7 @@ free_adj:
|
|
|
|
static void __netdev_adjacent_dev_remove(struct net_device *dev,
|
|
struct net_device *adj_dev,
|
|
+ u16 ref_nr,
|
|
struct list_head *dev_list)
|
|
{
|
|
struct netdev_adjacent *adj;
|
|
@@ -5274,10 +5278,10 @@ static void __netdev_adjacent_dev_remove(struct net_device *dev,
|
|
BUG();
|
|
}
|
|
|
|
- if (adj->ref_nr > 1) {
|
|
- pr_debug("%s to %s ref_nr-- = %d\n", dev->name, adj_dev->name,
|
|
- adj->ref_nr-1);
|
|
- adj->ref_nr--;
|
|
+ if (adj->ref_nr > ref_nr) {
|
|
+ pr_debug("%s to %s ref_nr-%d = %d\n", dev->name, adj_dev->name,
|
|
+ ref_nr, adj->ref_nr-ref_nr);
|
|
+ adj->ref_nr -= ref_nr;
|
|
return;
|
|
}
|
|
|
|
@@ -5296,21 +5300,22 @@ static void __netdev_adjacent_dev_remove(struct net_device *dev,
|
|
|
|
static int __netdev_adjacent_dev_link_lists(struct net_device *dev,
|
|
struct net_device *upper_dev,
|
|
+ u16 ref_nr,
|
|
struct list_head *up_list,
|
|
struct list_head *down_list,
|
|
void *private, bool master)
|
|
{
|
|
int ret;
|
|
|
|
- ret = __netdev_adjacent_dev_insert(dev, upper_dev, up_list, private,
|
|
- master);
|
|
+ ret = __netdev_adjacent_dev_insert(dev, upper_dev, ref_nr, up_list,
|
|
+ private, master);
|
|
if (ret)
|
|
return ret;
|
|
|
|
- ret = __netdev_adjacent_dev_insert(upper_dev, dev, down_list, private,
|
|
- false);
|
|
+ ret = __netdev_adjacent_dev_insert(upper_dev, dev, ref_nr, down_list,
|
|
+ private, false);
|
|
if (ret) {
|
|
- __netdev_adjacent_dev_remove(dev, upper_dev, up_list);
|
|
+ __netdev_adjacent_dev_remove(dev, upper_dev, ref_nr, up_list);
|
|
return ret;
|
|
}
|
|
|
|
@@ -5318,9 +5323,10 @@ static int __netdev_adjacent_dev_link_lists(struct net_device *dev,
|
|
}
|
|
|
|
static int __netdev_adjacent_dev_link(struct net_device *dev,
|
|
- struct net_device *upper_dev)
|
|
+ struct net_device *upper_dev,
|
|
+ u16 ref_nr)
|
|
{
|
|
- return __netdev_adjacent_dev_link_lists(dev, upper_dev,
|
|
+ return __netdev_adjacent_dev_link_lists(dev, upper_dev, ref_nr,
|
|
&dev->all_adj_list.upper,
|
|
&upper_dev->all_adj_list.lower,
|
|
NULL, false);
|
|
@@ -5328,17 +5334,19 @@ static int __netdev_adjacent_dev_link(struct net_device *dev,
|
|
|
|
static void __netdev_adjacent_dev_unlink_lists(struct net_device *dev,
|
|
struct net_device *upper_dev,
|
|
+ u16 ref_nr,
|
|
struct list_head *up_list,
|
|
struct list_head *down_list)
|
|
{
|
|
- __netdev_adjacent_dev_remove(dev, upper_dev, up_list);
|
|
- __netdev_adjacent_dev_remove(upper_dev, dev, down_list);
|
|
+ __netdev_adjacent_dev_remove(dev, upper_dev, ref_nr, up_list);
|
|
+ __netdev_adjacent_dev_remove(upper_dev, dev, ref_nr, down_list);
|
|
}
|
|
|
|
static void __netdev_adjacent_dev_unlink(struct net_device *dev,
|
|
- struct net_device *upper_dev)
|
|
+ struct net_device *upper_dev,
|
|
+ u16 ref_nr)
|
|
{
|
|
- __netdev_adjacent_dev_unlink_lists(dev, upper_dev,
|
|
+ __netdev_adjacent_dev_unlink_lists(dev, upper_dev, ref_nr,
|
|
&dev->all_adj_list.upper,
|
|
&upper_dev->all_adj_list.lower);
|
|
}
|
|
@@ -5347,17 +5355,17 @@ static int __netdev_adjacent_dev_link_neighbour(struct net_device *dev,
|
|
struct net_device *upper_dev,
|
|
void *private, bool master)
|
|
{
|
|
- int ret = __netdev_adjacent_dev_link(dev, upper_dev);
|
|
+ int ret = __netdev_adjacent_dev_link(dev, upper_dev, 1);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
- ret = __netdev_adjacent_dev_link_lists(dev, upper_dev,
|
|
+ ret = __netdev_adjacent_dev_link_lists(dev, upper_dev, 1,
|
|
&dev->adj_list.upper,
|
|
&upper_dev->adj_list.lower,
|
|
private, master);
|
|
if (ret) {
|
|
- __netdev_adjacent_dev_unlink(dev, upper_dev);
|
|
+ __netdev_adjacent_dev_unlink(dev, upper_dev, 1);
|
|
return ret;
|
|
}
|
|
|
|
@@ -5367,8 +5375,8 @@ static int __netdev_adjacent_dev_link_neighbour(struct net_device *dev,
|
|
static void __netdev_adjacent_dev_unlink_neighbour(struct net_device *dev,
|
|
struct net_device *upper_dev)
|
|
{
|
|
- __netdev_adjacent_dev_unlink(dev, upper_dev);
|
|
- __netdev_adjacent_dev_unlink_lists(dev, upper_dev,
|
|
+ __netdev_adjacent_dev_unlink(dev, upper_dev, 1);
|
|
+ __netdev_adjacent_dev_unlink_lists(dev, upper_dev, 1,
|
|
&dev->adj_list.upper,
|
|
&upper_dev->adj_list.lower);
|
|
}
|
|
@@ -5420,7 +5428,7 @@ static int __netdev_upper_dev_link(struct net_device *dev,
|
|
list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) {
|
|
pr_debug("Interlinking %s with %s, non-neighbour\n",
|
|
i->dev->name, j->dev->name);
|
|
- ret = __netdev_adjacent_dev_link(i->dev, j->dev);
|
|
+ ret = __netdev_adjacent_dev_link(i->dev, j->dev, i->ref_nr);
|
|
if (ret)
|
|
goto rollback_mesh;
|
|
}
|
|
@@ -5430,7 +5438,7 @@ static int __netdev_upper_dev_link(struct net_device *dev,
|
|
list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) {
|
|
pr_debug("linking %s's upper device %s with %s\n",
|
|
upper_dev->name, i->dev->name, dev->name);
|
|
- ret = __netdev_adjacent_dev_link(dev, i->dev);
|
|
+ ret = __netdev_adjacent_dev_link(dev, i->dev, i->ref_nr);
|
|
if (ret)
|
|
goto rollback_upper_mesh;
|
|
}
|
|
@@ -5439,7 +5447,7 @@ static int __netdev_upper_dev_link(struct net_device *dev,
|
|
list_for_each_entry(i, &dev->all_adj_list.lower, list) {
|
|
pr_debug("linking %s's lower device %s with %s\n", dev->name,
|
|
i->dev->name, upper_dev->name);
|
|
- ret = __netdev_adjacent_dev_link(i->dev, upper_dev);
|
|
+ ret = __netdev_adjacent_dev_link(i->dev, upper_dev, i->ref_nr);
|
|
if (ret)
|
|
goto rollback_lower_mesh;
|
|
}
|
|
@@ -5453,7 +5461,7 @@ rollback_lower_mesh:
|
|
list_for_each_entry(i, &dev->all_adj_list.lower, list) {
|
|
if (i == to_i)
|
|
break;
|
|
- __netdev_adjacent_dev_unlink(i->dev, upper_dev);
|
|
+ __netdev_adjacent_dev_unlink(i->dev, upper_dev, i->ref_nr);
|
|
}
|
|
|
|
i = NULL;
|
|
@@ -5463,7 +5471,7 @@ rollback_upper_mesh:
|
|
list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) {
|
|
if (i == to_i)
|
|
break;
|
|
- __netdev_adjacent_dev_unlink(dev, i->dev);
|
|
+ __netdev_adjacent_dev_unlink(dev, i->dev, i->ref_nr);
|
|
}
|
|
|
|
i = j = NULL;
|
|
@@ -5475,7 +5483,7 @@ rollback_mesh:
|
|
list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) {
|
|
if (i == to_i && j == to_j)
|
|
break;
|
|
- __netdev_adjacent_dev_unlink(i->dev, j->dev);
|
|
+ __netdev_adjacent_dev_unlink(i->dev, j->dev, i->ref_nr);
|
|
}
|
|
if (i == to_i)
|
|
break;
|
|
@@ -5559,16 +5567,16 @@ void netdev_upper_dev_unlink(struct net_device *dev,
|
|
*/
|
|
list_for_each_entry(i, &dev->all_adj_list.lower, list)
|
|
list_for_each_entry(j, &upper_dev->all_adj_list.upper, list)
|
|
- __netdev_adjacent_dev_unlink(i->dev, j->dev);
|
|
+ __netdev_adjacent_dev_unlink(i->dev, j->dev, i->ref_nr);
|
|
|
|
/* remove also the devices itself from lower/upper device
|
|
* list
|
|
*/
|
|
list_for_each_entry(i, &dev->all_adj_list.lower, list)
|
|
- __netdev_adjacent_dev_unlink(i->dev, upper_dev);
|
|
+ __netdev_adjacent_dev_unlink(i->dev, upper_dev, i->ref_nr);
|
|
|
|
list_for_each_entry(i, &upper_dev->all_adj_list.upper, list)
|
|
- __netdev_adjacent_dev_unlink(dev, i->dev);
|
|
+ __netdev_adjacent_dev_unlink(dev, i->dev, i->ref_nr);
|
|
|
|
call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, dev,
|
|
&changeupper_info.info);
|
|
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
|
|
index 4da4d51a2ccf..b6327601f979 100644
|
|
--- a/net/core/pktgen.c
|
|
+++ b/net/core/pktgen.c
|
|
@@ -215,8 +215,8 @@
|
|
#define M_NETIF_RECEIVE 1 /* Inject packets into stack */
|
|
|
|
/* If lock -- protects updating of if_list */
|
|
-#define if_lock(t) spin_lock(&(t->if_lock));
|
|
-#define if_unlock(t) spin_unlock(&(t->if_lock));
|
|
+#define if_lock(t) mutex_lock(&(t->if_lock));
|
|
+#define if_unlock(t) mutex_unlock(&(t->if_lock));
|
|
|
|
/* Used to help with determining the pkts on receive */
|
|
#define PKTGEN_MAGIC 0xbe9be955
|
|
@@ -422,7 +422,7 @@ struct pktgen_net {
|
|
};
|
|
|
|
struct pktgen_thread {
|
|
- spinlock_t if_lock; /* for list of devices */
|
|
+ struct mutex if_lock; /* for list of devices */
|
|
struct list_head if_list; /* All device here */
|
|
struct list_head th_list;
|
|
struct task_struct *tsk;
|
|
@@ -2002,11 +2002,13 @@ static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *d
|
|
{
|
|
struct pktgen_thread *t;
|
|
|
|
+ mutex_lock(&pktgen_thread_lock);
|
|
+
|
|
list_for_each_entry(t, &pn->pktgen_threads, th_list) {
|
|
struct pktgen_dev *pkt_dev;
|
|
|
|
- rcu_read_lock();
|
|
- list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
|
|
+ if_lock(t);
|
|
+ list_for_each_entry(pkt_dev, &t->if_list, list) {
|
|
if (pkt_dev->odev != dev)
|
|
continue;
|
|
|
|
@@ -2021,8 +2023,9 @@ static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *d
|
|
dev->name);
|
|
break;
|
|
}
|
|
- rcu_read_unlock();
|
|
+ if_unlock(t);
|
|
}
|
|
+ mutex_unlock(&pktgen_thread_lock);
|
|
}
|
|
|
|
static int pktgen_device_event(struct notifier_block *unused,
|
|
@@ -2278,7 +2281,7 @@ static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
|
|
|
|
static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev)
|
|
{
|
|
- pkt_dev->pkt_overhead = LL_RESERVED_SPACE(pkt_dev->odev);
|
|
+ pkt_dev->pkt_overhead = 0;
|
|
pkt_dev->pkt_overhead += pkt_dev->nr_labels*sizeof(u32);
|
|
pkt_dev->pkt_overhead += VLAN_TAG_SIZE(pkt_dev);
|
|
pkt_dev->pkt_overhead += SVLAN_TAG_SIZE(pkt_dev);
|
|
@@ -2769,13 +2772,13 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
|
|
}
|
|
|
|
static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
|
|
- struct pktgen_dev *pkt_dev,
|
|
- unsigned int extralen)
|
|
+ struct pktgen_dev *pkt_dev)
|
|
{
|
|
+ unsigned int extralen = LL_RESERVED_SPACE(dev);
|
|
struct sk_buff *skb = NULL;
|
|
- unsigned int size = pkt_dev->cur_pkt_size + 64 + extralen +
|
|
- pkt_dev->pkt_overhead;
|
|
+ unsigned int size;
|
|
|
|
+ size = pkt_dev->cur_pkt_size + 64 + extralen + pkt_dev->pkt_overhead;
|
|
if (pkt_dev->flags & F_NODE) {
|
|
int node = pkt_dev->node >= 0 ? pkt_dev->node : numa_node_id();
|
|
|
|
@@ -2791,8 +2791,9 @@ static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
|
|
skb = __netdev_alloc_skb(dev, size, GFP_NOWAIT);
|
|
}
|
|
|
|
+ /* the caller pre-fetches from skb->data and reserves for the mac hdr */
|
|
if (likely(skb))
|
|
- skb_reserve(skb, LL_RESERVED_SPACE(dev));
|
|
+ skb_reserve(skb, extralen - 16);
|
|
|
|
return skb;
|
|
}
|
|
@@ -2822,16 +2826,14 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
|
|
mod_cur_headers(pkt_dev);
|
|
queue_map = pkt_dev->cur_queue_map;
|
|
|
|
- datalen = (odev->hard_header_len + 16) & ~0xf;
|
|
-
|
|
- skb = pktgen_alloc_skb(odev, pkt_dev, datalen);
|
|
+ skb = pktgen_alloc_skb(odev, pkt_dev);
|
|
if (!skb) {
|
|
sprintf(pkt_dev->result, "No memory");
|
|
return NULL;
|
|
}
|
|
|
|
prefetchw(skb->data);
|
|
- skb_reserve(skb, datalen);
|
|
+ skb_reserve(skb, 16);
|
|
|
|
/* Reserve for ethernet and IP header */
|
|
eth = (__u8 *) skb_push(skb, 14);
|
|
@@ -2951,7 +2953,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
|
|
mod_cur_headers(pkt_dev);
|
|
queue_map = pkt_dev->cur_queue_map;
|
|
|
|
- skb = pktgen_alloc_skb(odev, pkt_dev, 16);
|
|
+ skb = pktgen_alloc_skb(odev, pkt_dev);
|
|
if (!skb) {
|
|
sprintf(pkt_dev->result, "No memory");
|
|
return NULL;
|
|
@@ -3727,7 +3729,7 @@ static int __net_init pktgen_create_thread(int cpu, struct pktgen_net *pn)
|
|
return -ENOMEM;
|
|
}
|
|
|
|
- spin_lock_init(&t->if_lock);
|
|
+ mutex_init(&t->if_lock);
|
|
t->cpu = cpu;
|
|
|
|
INIT_LIST_HEAD(&t->if_list);
|
|
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
|
|
index 9e63f252a89e..de85d4e1cf43 100644
|
|
--- a/net/ethernet/eth.c
|
|
+++ b/net/ethernet/eth.c
|
|
@@ -436,7 +436,7 @@ struct sk_buff **eth_gro_receive(struct sk_buff **head,
|
|
|
|
skb_gro_pull(skb, sizeof(*eh));
|
|
skb_gro_postpull_rcsum(skb, eh, sizeof(*eh));
|
|
- pp = ptype->callbacks.gro_receive(head, skb);
|
|
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
|
|
|
|
out_unlock:
|
|
rcu_read_unlock();
|
|
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
|
|
index 1a5c1ca3ad3c..afc18e9ca94a 100644
|
|
--- a/net/ipv4/af_inet.c
|
|
+++ b/net/ipv4/af_inet.c
|
|
@@ -1372,7 +1372,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
|
|
skb_gro_pull(skb, sizeof(*iph));
|
|
skb_set_transport_header(skb, skb_gro_offset(skb));
|
|
|
|
- pp = ops->callbacks.gro_receive(head, skb);
|
|
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
|
|
|
|
out_unlock:
|
|
rcu_read_unlock();
|
|
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
|
|
index 08d7de55e57e..08d8ee124538 100644
|
|
--- a/net/ipv4/fou.c
|
|
+++ b/net/ipv4/fou.c
|
|
@@ -201,7 +201,7 @@ static struct sk_buff **fou_gro_receive(struct sk_buff **head,
|
|
if (!ops || !ops->callbacks.gro_receive)
|
|
goto out_unlock;
|
|
|
|
- pp = ops->callbacks.gro_receive(head, skb);
|
|
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
|
|
|
|
out_unlock:
|
|
rcu_read_unlock();
|
|
@@ -360,7 +360,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
|
|
if (WARN_ON_ONCE(!ops || !ops->callbacks.gro_receive))
|
|
goto out_unlock;
|
|
|
|
- pp = ops->callbacks.gro_receive(head, skb);
|
|
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
|
|
|
|
out_unlock:
|
|
rcu_read_unlock();
|
|
diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c
|
|
index e603004c1af8..79ae0d7becbf 100644
|
|
--- a/net/ipv4/gre_offload.c
|
|
+++ b/net/ipv4/gre_offload.c
|
|
@@ -219,7 +219,7 @@ static struct sk_buff **gre_gro_receive(struct sk_buff **head,
|
|
/* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
|
|
skb_gro_postpull_rcsum(skb, greh, grehlen);
|
|
|
|
- pp = ptype->callbacks.gro_receive(head, skb);
|
|
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
|
|
|
|
out_unlock:
|
|
rcu_read_unlock();
|
|
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
|
|
index a50124260f5a..9ce202549e7a 100644
|
|
--- a/net/ipv4/ip_sockglue.c
|
|
+++ b/net/ipv4/ip_sockglue.c
|
|
@@ -98,7 +98,7 @@ static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
|
|
}
|
|
|
|
static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
|
|
- int offset)
|
|
+ int tlen, int offset)
|
|
{
|
|
__wsum csum = skb->csum;
|
|
|
|
@@ -106,7 +106,9 @@ static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
|
|
return;
|
|
|
|
if (offset != 0)
|
|
- csum = csum_sub(csum, csum_partial(skb->data, offset, 0));
|
|
+ csum = csum_sub(csum,
|
|
+ csum_partial(skb->data + tlen,
|
|
+ offset, 0));
|
|
|
|
put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum);
|
|
}
|
|
@@ -152,7 +154,7 @@ static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb)
|
|
}
|
|
|
|
void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb,
|
|
- int offset)
|
|
+ int tlen, int offset)
|
|
{
|
|
struct inet_sock *inet = inet_sk(skb->sk);
|
|
unsigned int flags = inet->cmsg_flags;
|
|
@@ -215,7 +217,7 @@ void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb,
|
|
}
|
|
|
|
if (flags & IP_CMSG_CHECKSUM)
|
|
- ip_cmsg_recv_checksum(msg, skb, offset);
|
|
+ ip_cmsg_recv_checksum(msg, skb, tlen, offset);
|
|
}
|
|
EXPORT_SYMBOL(ip_cmsg_recv_offset);
|
|
|
|
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
|
|
index 9d1e555496e3..8e77786549c6 100644
|
|
--- a/net/ipv4/ipmr.c
|
|
+++ b/net/ipv4/ipmr.c
|
|
@@ -2192,7 +2192,7 @@ static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
|
|
|
|
int ipmr_get_route(struct net *net, struct sk_buff *skb,
|
|
__be32 saddr, __be32 daddr,
|
|
- struct rtmsg *rtm, int nowait)
|
|
+ struct rtmsg *rtm, int nowait, u32 portid)
|
|
{
|
|
struct mfc_cache *cache;
|
|
struct mr_table *mrt;
|
|
@@ -2237,6 +2237,7 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb,
|
|
return -ENOMEM;
|
|
}
|
|
|
|
+ NETLINK_CB(skb2).portid = portid;
|
|
skb_push(skb2, sizeof(struct iphdr));
|
|
skb_reset_network_header(skb2);
|
|
iph = ip_hdr(skb2);
|
|
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
|
|
index b050cf980a57..8533a75a9328 100644
|
|
--- a/net/ipv4/route.c
|
|
+++ b/net/ipv4/route.c
|
|
@@ -2492,7 +2492,8 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, u32 table_id,
|
|
IPV4_DEVCONF_ALL(net, MC_FORWARDING)) {
|
|
int err = ipmr_get_route(net, skb,
|
|
fl4->saddr, fl4->daddr,
|
|
- r, nowait);
|
|
+ r, nowait, portid);
|
|
+
|
|
if (err <= 0) {
|
|
if (!nowait) {
|
|
if (err == 0)
|
|
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
|
|
index a0bd7a55193e..70fb352e317f 100644
|
|
--- a/net/ipv4/sysctl_net_ipv4.c
|
|
+++ b/net/ipv4/sysctl_net_ipv4.c
|
|
@@ -97,11 +97,11 @@ static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low
|
|
container_of(table->data, struct net, ipv4.ping_group_range.range);
|
|
unsigned int seq;
|
|
do {
|
|
- seq = read_seqbegin(&net->ipv4.ip_local_ports.lock);
|
|
+ seq = read_seqbegin(&net->ipv4.ping_group_range.lock);
|
|
|
|
*low = data[0];
|
|
*high = data[1];
|
|
- } while (read_seqretry(&net->ipv4.ip_local_ports.lock, seq));
|
|
+ } while (read_seqretry(&net->ipv4.ping_group_range.lock, seq));
|
|
}
|
|
|
|
/* Update system visible IP port range */
|
|
@@ -110,10 +110,10 @@ static void set_ping_group_range(struct ctl_table *table, kgid_t low, kgid_t hig
|
|
kgid_t *data = table->data;
|
|
struct net *net =
|
|
container_of(table->data, struct net, ipv4.ping_group_range.range);
|
|
- write_seqlock(&net->ipv4.ip_local_ports.lock);
|
|
+ write_seqlock(&net->ipv4.ping_group_range.lock);
|
|
data[0] = low;
|
|
data[1] = high;
|
|
- write_sequnlock(&net->ipv4.ip_local_ports.lock);
|
|
+ write_sequnlock(&net->ipv4.ping_group_range.lock);
|
|
}
|
|
|
|
/* Validate changes from /proc interface. */
|
|
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
|
|
index 12b98e257c5f..7cc0f8aac28f 100644
|
|
--- a/net/ipv4/tcp_input.c
|
|
+++ b/net/ipv4/tcp_input.c
|
|
@@ -2324,10 +2324,9 @@ static void DBGUNDO(struct sock *sk, const char *msg)
|
|
}
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
else if (sk->sk_family == AF_INET6) {
|
|
- struct ipv6_pinfo *np = inet6_sk(sk);
|
|
pr_debug("Undo %s %pI6/%u c%u l%u ss%u/%u p%u\n",
|
|
msg,
|
|
- &np->daddr, ntohs(inet->inet_dport),
|
|
+ &sk->sk_v6_daddr, ntohs(inet->inet_dport),
|
|
tp->snd_cwnd, tcp_left_out(tp),
|
|
tp->snd_ssthresh, tp->prior_ssthresh,
|
|
tp->packets_out);
|
|
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
|
|
index 660c967ba84a..0795647e94c6 100644
|
|
--- a/net/ipv4/tcp_output.c
|
|
+++ b/net/ipv4/tcp_output.c
|
|
@@ -1950,12 +1950,14 @@ static int tcp_mtu_probe(struct sock *sk)
|
|
len = 0;
|
|
tcp_for_write_queue_from_safe(skb, next, sk) {
|
|
copy = min_t(int, skb->len, probe_size - len);
|
|
- if (nskb->ip_summed)
|
|
+ if (nskb->ip_summed) {
|
|
skb_copy_bits(skb, 0, skb_put(nskb, copy), copy);
|
|
- else
|
|
- nskb->csum = skb_copy_and_csum_bits(skb, 0,
|
|
- skb_put(nskb, copy),
|
|
- copy, nskb->csum);
|
|
+ } else {
|
|
+ __wsum csum = skb_copy_and_csum_bits(skb, 0,
|
|
+ skb_put(nskb, copy),
|
|
+ copy, 0);
|
|
+ nskb->csum = csum_block_add(nskb->csum, csum, len);
|
|
+ }
|
|
|
|
if (skb->len <= copy) {
|
|
/* We've eaten all the data from this skb.
|
|
@@ -2569,7 +2571,8 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
|
|
* copying overhead: fragmentation, tunneling, mangling etc.
|
|
*/
|
|
if (atomic_read(&sk->sk_wmem_alloc) >
|
|
- min(sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2), sk->sk_sndbuf))
|
|
+ min_t(u32, sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2),
|
|
+ sk->sk_sndbuf))
|
|
return -EAGAIN;
|
|
|
|
if (skb_still_in_host_queue(sk, skb))
|
|
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
|
|
index 0b1ea5abcc04..e9513e397c4f 100644
|
|
--- a/net/ipv4/udp.c
|
|
+++ b/net/ipv4/udp.c
|
|
@@ -1342,7 +1342,7 @@ try_again:
|
|
*addr_len = sizeof(*sin);
|
|
}
|
|
if (inet->cmsg_flags)
|
|
- ip_cmsg_recv_offset(msg, skb, sizeof(struct udphdr));
|
|
+ ip_cmsg_recv_offset(msg, skb, sizeof(struct udphdr), off);
|
|
|
|
err = copied;
|
|
if (flags & MSG_TRUNC)
|
|
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
|
|
index 0e36e56dfd22..6396f1c80ae9 100644
|
|
--- a/net/ipv4/udp_offload.c
|
|
+++ b/net/ipv4/udp_offload.c
|
|
@@ -339,8 +339,8 @@ unflush:
|
|
skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */
|
|
skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr));
|
|
NAPI_GRO_CB(skb)->proto = uo_priv->offload->ipproto;
|
|
- pp = uo_priv->offload->callbacks.gro_receive(head, skb,
|
|
- uo_priv->offload);
|
|
+ pp = call_gro_receive_udp(uo_priv->offload->callbacks.gro_receive,
|
|
+ head, skb, uo_priv->offload);
|
|
|
|
out_unlock:
|
|
rcu_read_unlock();
|
|
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
|
|
index 036b39eb1220..cb8bb5988c03 100644
|
|
--- a/net/ipv6/addrconf.c
|
|
+++ b/net/ipv6/addrconf.c
|
|
@@ -2916,7 +2916,7 @@ static void init_loopback(struct net_device *dev)
|
|
* lo device down, release this obsolete dst and
|
|
* reallocate a new router for ifa.
|
|
*/
|
|
- if (sp_ifa->rt->dst.obsolete > 0) {
|
|
+ if (!atomic_read(&sp_ifa->rt->rt6i_ref)) {
|
|
ip6_rt_put(sp_ifa->rt);
|
|
sp_ifa->rt = NULL;
|
|
} else {
|
|
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
|
|
index 4650c6824783..17430f341073 100644
|
|
--- a/net/ipv6/ip6_gre.c
|
|
+++ b/net/ipv6/ip6_gre.c
|
|
@@ -886,7 +886,6 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
|
|
encap_limit = t->parms.encap_limit;
|
|
|
|
memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
|
|
- fl6.flowi6_proto = skb->protocol;
|
|
|
|
err = ip6gre_xmit2(skb, dev, 0, &fl6, encap_limit, &mtu);
|
|
|
|
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
|
|
index 82e9f3076028..efe6268b8bc3 100644
|
|
--- a/net/ipv6/ip6_offload.c
|
|
+++ b/net/ipv6/ip6_offload.c
|
|
@@ -247,7 +247,7 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
|
|
|
|
skb_gro_postpull_rcsum(skb, iph, nlen);
|
|
|
|
- pp = ops->callbacks.gro_receive(head, skb);
|
|
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
|
|
|
|
out_unlock:
|
|
rcu_read_unlock();
|
|
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
|
|
index 3991b21e24ad..e8878886eba4 100644
|
|
--- a/net/ipv6/ip6_tunnel.c
|
|
+++ b/net/ipv6/ip6_tunnel.c
|
|
@@ -246,6 +246,7 @@ ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_
|
|
hash = HASH(&any, local);
|
|
for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
|
|
if (ipv6_addr_equal(local, &t->parms.laddr) &&
|
|
+ ipv6_addr_any(&t->parms.raddr) &&
|
|
(t->dev->flags & IFF_UP))
|
|
return t;
|
|
}
|
|
@@ -253,6 +254,7 @@ ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_
|
|
hash = HASH(remote, &any);
|
|
for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
|
|
if (ipv6_addr_equal(remote, &t->parms.raddr) &&
|
|
+ ipv6_addr_any(&t->parms.laddr) &&
|
|
(t->dev->flags & IFF_UP))
|
|
return t;
|
|
}
|
|
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
|
|
index e207cb2468da..d9843e5a667f 100644
|
|
--- a/net/ipv6/ip6mr.c
|
|
+++ b/net/ipv6/ip6mr.c
|
|
@@ -2276,8 +2276,8 @@ static int __ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb,
|
|
return 1;
|
|
}
|
|
|
|
-int ip6mr_get_route(struct net *net,
|
|
- struct sk_buff *skb, struct rtmsg *rtm, int nowait)
|
|
+int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
|
|
+ int nowait, u32 portid)
|
|
{
|
|
int err;
|
|
struct mr6_table *mrt;
|
|
@@ -2322,6 +2322,7 @@ int ip6mr_get_route(struct net *net,
|
|
return -ENOMEM;
|
|
}
|
|
|
|
+ NETLINK_CB(skb2).portid = portid;
|
|
skb_reset_transport_header(skb2);
|
|
|
|
skb_put(skb2, sizeof(struct ipv6hdr));
|
|
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
|
|
index 5af2cca0a46d..dbffc9de184b 100644
|
|
--- a/net/ipv6/route.c
|
|
+++ b/net/ipv6/route.c
|
|
@@ -3140,7 +3140,9 @@ static int rt6_fill_node(struct net *net,
|
|
if (iif) {
|
|
#ifdef CONFIG_IPV6_MROUTE
|
|
if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
|
|
- int err = ip6mr_get_route(net, skb, rtm, nowait);
|
|
+ int err = ip6mr_get_route(net, skb, rtm, nowait,
|
|
+ portid);
|
|
+
|
|
if (err <= 0) {
|
|
if (!nowait) {
|
|
if (err == 0)
|
|
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
|
|
index 2d81e2f33ef2..fbd521fdae53 100644
|
|
--- a/net/ipv6/tcp_ipv6.c
|
|
+++ b/net/ipv6/tcp_ipv6.c
|
|
@@ -1179,6 +1179,16 @@ out:
|
|
return NULL;
|
|
}
|
|
|
|
+static void tcp_v6_restore_cb(struct sk_buff *skb)
|
|
+{
|
|
+ /* We need to move header back to the beginning if xfrm6_policy_check()
|
|
+ * and tcp_v6_fill_cb() are going to be called again.
|
|
+ * ip6_datagram_recv_specific_ctl() also expects IP6CB to be there.
|
|
+ */
|
|
+ memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6,
|
|
+ sizeof(struct inet6_skb_parm));
|
|
+}
|
|
+
|
|
/* The socket must have it's spinlock held when we get
|
|
* here, unless it is a TCP_LISTEN socket.
|
|
*
|
|
@@ -1308,6 +1318,7 @@ ipv6_pktoptions:
|
|
np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb));
|
|
if (ipv6_opt_accepted(sk, opt_skb, &TCP_SKB_CB(opt_skb)->header.h6)) {
|
|
skb_set_owner_r(opt_skb, sk);
|
|
+ tcp_v6_restore_cb(opt_skb);
|
|
opt_skb = xchg(&np->pktoptions, opt_skb);
|
|
} else {
|
|
__kfree_skb(opt_skb);
|
|
@@ -1341,15 +1352,6 @@ static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr,
|
|
TCP_SKB_CB(skb)->sacked = 0;
|
|
}
|
|
|
|
-static void tcp_v6_restore_cb(struct sk_buff *skb)
|
|
-{
|
|
- /* We need to move header back to the beginning if xfrm6_policy_check()
|
|
- * and tcp_v6_fill_cb() are going to be called again.
|
|
- */
|
|
- memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6,
|
|
- sizeof(struct inet6_skb_parm));
|
|
-}
|
|
-
|
|
static int tcp_v6_rcv(struct sk_buff *skb)
|
|
{
|
|
const struct tcphdr *th;
|
|
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
|
|
index e6092bd72ee2..dfa85e7264df 100644
|
|
--- a/net/ipv6/udp.c
|
|
+++ b/net/ipv6/udp.c
|
|
@@ -498,7 +498,8 @@ try_again:
|
|
|
|
if (is_udp4) {
|
|
if (inet->cmsg_flags)
|
|
- ip_cmsg_recv(msg, skb);
|
|
+ ip_cmsg_recv_offset(msg, skb,
|
|
+ sizeof(struct udphdr), off);
|
|
} else {
|
|
if (np->rxopt.all)
|
|
ip6_datagram_recv_specific_ctl(sk, msg, skb);
|
|
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
|
|
index 7a5fa0c98377..28fc283c1ec1 100644
|
|
--- a/net/netlink/af_netlink.c
|
|
+++ b/net/netlink/af_netlink.c
|
|
@@ -2557,7 +2557,7 @@ static int netlink_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
|
|
/* Record the max length of recvmsg() calls for future allocations */
|
|
nlk->max_recvmsg_len = max(nlk->max_recvmsg_len, len);
|
|
nlk->max_recvmsg_len = min_t(size_t, nlk->max_recvmsg_len,
|
|
- 16384);
|
|
+ SKB_WITH_OVERHEAD(32768));
|
|
|
|
copied = data_skb->len;
|
|
if (len < copied) {
|
|
@@ -2810,14 +2810,13 @@ static int netlink_dump(struct sock *sk)
|
|
if (alloc_min_size < nlk->max_recvmsg_len) {
|
|
alloc_size = nlk->max_recvmsg_len;
|
|
skb = netlink_alloc_skb(sk, alloc_size, nlk->portid,
|
|
- GFP_KERNEL |
|
|
- __GFP_NOWARN |
|
|
- __GFP_NORETRY);
|
|
+ (GFP_KERNEL & ~__GFP_DIRECT_RECLAIM) |
|
|
+ __GFP_NOWARN | __GFP_NORETRY);
|
|
}
|
|
if (!skb) {
|
|
alloc_size = alloc_min_size;
|
|
skb = netlink_alloc_skb(sk, alloc_size, nlk->portid,
|
|
- GFP_KERNEL);
|
|
+ (GFP_KERNEL & ~__GFP_DIRECT_RECLAIM));
|
|
}
|
|
if (!skb)
|
|
goto errout_skb;
|
|
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
|
|
index a86f26d05bc2..34e4fcfd240b 100644
|
|
--- a/net/packet/af_packet.c
|
|
+++ b/net/packet/af_packet.c
|
|
@@ -249,7 +249,7 @@ static void __fanout_link(struct sock *sk, struct packet_sock *po);
|
|
static int packet_direct_xmit(struct sk_buff *skb)
|
|
{
|
|
struct net_device *dev = skb->dev;
|
|
- netdev_features_t features;
|
|
+ struct sk_buff *orig_skb = skb;
|
|
struct netdev_queue *txq;
|
|
int ret = NETDEV_TX_BUSY;
|
|
|
|
@@ -257,9 +257,8 @@ static int packet_direct_xmit(struct sk_buff *skb)
|
|
!netif_carrier_ok(dev)))
|
|
goto drop;
|
|
|
|
- features = netif_skb_features(skb);
|
|
- if (skb_needs_linearize(skb, features) &&
|
|
- __skb_linearize(skb))
|
|
+ skb = validate_xmit_skb_list(skb, dev);
|
|
+ if (skb != orig_skb)
|
|
goto drop;
|
|
|
|
txq = skb_get_tx_queue(dev, skb);
|
|
@@ -279,7 +278,7 @@ static int packet_direct_xmit(struct sk_buff *skb)
|
|
return ret;
|
|
drop:
|
|
atomic_long_inc(&dev->tx_dropped);
|
|
- kfree_skb(skb);
|
|
+ kfree_skb_list(skb);
|
|
return NET_XMIT_DROP;
|
|
}
|
|
|
|
@@ -3855,6 +3854,7 @@ static int packet_notifier(struct notifier_block *this,
|
|
}
|
|
if (msg == NETDEV_UNREGISTER) {
|
|
packet_cached_dev_reset(po);
|
|
+ fanout_release(sk);
|
|
po->ifindex = -1;
|
|
if (po->prot_hook.dev)
|
|
dev_put(po->prot_hook.dev);
|
|
diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c
|
|
index 796785e0bf96..d7edba4536bd 100644
|
|
--- a/net/sched/act_vlan.c
|
|
+++ b/net/sched/act_vlan.c
|
|
@@ -33,6 +33,12 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
|
|
bstats_update(&v->tcf_bstats, skb);
|
|
action = v->tcf_action;
|
|
|
|
+ /* Ensure 'data' points at mac_header prior calling vlan manipulating
|
|
+ * functions.
|
|
+ */
|
|
+ if (skb_at_tc_ingress(skb))
|
|
+ skb_push_rcsum(skb, skb->mac_len);
|
|
+
|
|
switch (v->tcfv_action) {
|
|
case TCA_VLAN_ACT_POP:
|
|
err = skb_vlan_pop(skb);
|
|
@@ -54,6 +60,9 @@ drop:
|
|
action = TC_ACT_SHOT;
|
|
v->tcf_qstats.drops++;
|
|
unlock:
|
|
+ if (skb_at_tc_ingress(skb))
|
|
+ skb_pull_rcsum(skb, skb->mac_len);
|
|
+
|
|
spin_unlock(&v->tcf_lock);
|
|
return action;
|
|
}
|
|
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
|
|
index a75864d93142..ecc1904e454f 100644
|
|
--- a/net/sched/cls_api.c
|
|
+++ b/net/sched/cls_api.c
|
|
@@ -315,7 +315,8 @@ replay:
|
|
if (err == 0) {
|
|
struct tcf_proto *next = rtnl_dereference(tp->next);
|
|
|
|
- tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
|
|
+ tfilter_notify(net, skb, n, tp,
|
|
+ t->tcm_handle, RTM_DELTFILTER);
|
|
if (tcf_destroy(tp, false))
|
|
RCU_INIT_POINTER(*back, next);
|
|
}
|
|
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
|
|
index 22c2bf367d7e..29c7c43de108 100644
|
|
--- a/net/sctp/sm_statefuns.c
|
|
+++ b/net/sctp/sm_statefuns.c
|
|
@@ -3426,6 +3426,12 @@ sctp_disposition_t sctp_sf_ootb(struct net *net,
|
|
return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
|
|
commands);
|
|
|
|
+ /* Report violation if chunk len overflows */
|
|
+ ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
|
|
+ if (ch_end > skb_tail_pointer(skb))
|
|
+ return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
|
|
+ commands);
|
|
+
|
|
/* Now that we know we at least have a chunk header,
|
|
* do things that are type appropriate.
|
|
*/
|
|
@@ -3457,12 +3463,6 @@ sctp_disposition_t sctp_sf_ootb(struct net *net,
|
|
}
|
|
}
|
|
|
|
- /* Report violation if chunk len overflows */
|
|
- ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
|
|
- if (ch_end > skb_tail_pointer(skb))
|
|
- return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
|
|
- commands);
|
|
-
|
|
ch = (sctp_chunkhdr_t *) ch_end;
|
|
} while (ch_end < skb_tail_pointer(skb));
|
|
|
|
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
|
|
index be1489fc3234..402817be3873 100644
|
|
--- a/net/sctp/socket.c
|
|
+++ b/net/sctp/socket.c
|
|
@@ -4371,7 +4371,7 @@ static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
|
|
static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
|
|
int __user *optlen)
|
|
{
|
|
- if (len <= 0)
|
|
+ if (len == 0)
|
|
return -EINVAL;
|
|
if (len > sizeof(struct sctp_event_subscribe))
|
|
len = sizeof(struct sctp_event_subscribe);
|
|
@@ -5972,6 +5972,9 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
|
|
if (get_user(len, optlen))
|
|
return -EFAULT;
|
|
|
|
+ if (len < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
lock_sock(sk);
|
|
|
|
switch (optname) {
|