summaryrefslogtreecommitdiff
path: root/net/core/dev.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-10-05 09:40:08 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-10-05 09:40:08 +0200
commit36844ea19656fb41278799ef3d5cd52120d89149 (patch)
tree6a545c2e4d8e8531b099adf1c2263c011854ebe2 /net/core/dev.c
parentc44a4925cdac02af781ebfae96df68a7bf3580b0 (diff)
parenta90ee4305c4a5df72c11b31dacfdc76e00fcf78a (diff)
Merge 7.3-rc6 into tty-next
Resolves a bunch of merge conflicts with serial driver changes and we need the fixes in here for testing and future patches. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'net/core/dev.c')
-rw-r--r--net/core/dev.c129
1 files changed, 92 insertions, 37 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index ecfbd72d5d1a..18dc88990510 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -789,7 +789,7 @@ int dev_fill_forward_path(struct net_device_path_ctx *ctx,
goto err_out;
stack->num_paths++;
- if (WARN_ON_ONCE(last_dev == ctx->dev))
+ if (last_dev == ctx->dev)
goto err_out;
}
@@ -2901,7 +2901,7 @@ int __netif_set_xps_queue(struct net_device *dev, const unsigned long *mask,
dev = netdev_get_tx_queue(dev, index)->sb_dev ? : dev;
tc = netdev_txq_to_tc(dev, index);
- if (tc < 0)
+ if (tc < 0 || tc >= num_tc)
return -EINVAL;
}
@@ -3818,20 +3818,36 @@ static netdev_features_t dflt_features_check(struct sk_buff *skb,
return vlan_features_check(skb, features);
}
-static bool skb_gso_has_extension_hdr(const struct sk_buff *skb)
+static bool __skb_has_ipv6_ext_hdr(const struct sk_buff *skb, int nhoff)
{
- if (!skb->encapsulation)
- return ((skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6 ||
- (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 &&
- vlan_get_protocol(skb) == htons(ETH_P_IPV6))) &&
- skb_transport_header_was_set(skb) &&
- skb_network_header_len(skb) != sizeof(struct ipv6hdr));
- else
- return (!skb_inner_network_header_was_set(skb) ||
- ((skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6 ||
- (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 &&
- inner_ip_hdr(skb)->version == 6)) &&
- skb_inner_network_header_len(skb) != sizeof(struct ipv6hdr)));
+ const struct ipv6hdr *ip6h;
+ struct ipv6hdr _ip6h;
+
+ ip6h = skb_header_pointer(skb, nhoff, sizeof(_ip6h), &_ip6h);
+ return ip6h && ip6h->version == 6 && ipv6_ext_hdr(ip6h->nexthdr);
+}
+
+static bool skb_has_ipv6_extension_hdr(const struct sk_buff *skb)
+{
+ if (vlan_get_protocol(skb) == htons(ETH_P_IPV6)) {
+ if (__skb_has_ipv6_ext_hdr(skb, skb_network_offset(skb)))
+ return true;
+
+ /* Catch tunnels without skb->encapsulation (e.g., virtio). */
+ if (!skb->encapsulation &&
+ skb_transport_header_was_set(skb) &&
+ skb_network_header_len(skb) != sizeof(struct ipv6hdr))
+ return true;
+ }
+
+ /* Tunnels without an inner network header, such as SCTP-in-UDP or
+ * PSP, have no inner IP header and thus no inner extension header.
+ */
+ if (skb->encapsulation && skb_inner_network_header_was_set(skb) &&
+ __skb_has_ipv6_ext_hdr(skb, skb_inner_network_offset(skb)))
+ return true;
+
+ return false;
}
static netdev_features_t gso_features_check(const struct sk_buff *skb,
@@ -3886,7 +3902,7 @@ static netdev_features_t gso_features_check(const struct sk_buff *skb,
* so neither does TSO that depends on it.
*/
if (features & NETIF_F_IPV6_CSUM &&
- skb_gso_has_extension_hdr(skb))
+ skb_has_ipv6_extension_hdr(skb))
features &= ~(NETIF_F_IPV6_CSUM | NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4);
return features;
@@ -3988,8 +4004,7 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
return 0;
if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) {
- if (vlan_get_protocol(skb) == htons(ETH_P_IPV6) &&
- skb_network_header_len(skb) != sizeof(struct ipv6hdr))
+ if (skb_has_ipv6_extension_hdr(skb))
goto sw_checksum;
switch (skb->csum_offset) {
@@ -4404,9 +4419,14 @@ EXPORT_SYMBOL(dev_loopback_xmit);
static struct netdev_queue *
netdev_tx_queue_mapping(struct net_device *dev, struct sk_buff *skb)
{
- int qm = skb_get_queue_mapping(skb);
+ int queue = skb_get_queue_mapping(skb);
+ int capped;
+
+ capped = netdev_cap_txqueue(dev, queue);
+ if (unlikely(capped != queue))
+ skb_set_queue_mapping(skb, capped);
- return netdev_get_tx_queue(dev, netdev_cap_txqueue(dev, qm));
+ return netdev_get_tx_queue(dev, capped);
}
#ifndef CONFIG_PREEMPT_RT
@@ -4415,9 +4435,13 @@ static bool netdev_xmit_txqueue_skipped(void)
return __this_cpu_read(softnet_data.xmit.skip_txqueue);
}
-void netdev_xmit_skip_txqueue(bool skip)
+bool netdev_xmit_skip_txqueue(bool skip)
{
+ bool prev = netdev_xmit_txqueue_skipped();
+
__this_cpu_write(softnet_data.xmit.skip_txqueue, skip);
+
+ return prev;
}
EXPORT_SYMBOL_GPL(netdev_xmit_skip_txqueue);
@@ -4427,9 +4451,13 @@ static bool netdev_xmit_txqueue_skipped(void)
return current->net_xmit.skip_txqueue;
}
-void netdev_xmit_skip_txqueue(bool skip)
+bool netdev_xmit_skip_txqueue(bool skip)
{
+ bool prev = netdev_xmit_txqueue_skipped();
+
current->net_xmit.skip_txqueue = skip;
+
+ return prev;
}
EXPORT_SYMBOL_GPL(netdev_xmit_skip_txqueue);
#endif
@@ -4848,21 +4876,25 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
tcx_set_ingress(skb, false);
#ifdef CONFIG_NET_EGRESS
if (static_branch_unlikely(&egress_needed_key)) {
+ bool skip_txq;
+
if (nf_hook_egress_active()) {
skb = nf_hook_egress(skb, &rc, dev);
if (!skb)
goto out;
}
- netdev_xmit_skip_txqueue(false);
+ skip_txq = netdev_xmit_skip_txqueue(false);
nf_skip_egress(skb, true);
skb = sch_handle_egress(skb, &rc, dev);
- if (!skb)
+ if (!skb) {
+ netdev_xmit_skip_txqueue(skip_txq);
goto out;
+ }
nf_skip_egress(skb, false);
- if (netdev_xmit_txqueue_skipped())
+ if (netdev_xmit_skip_txqueue(skip_txq))
txq = netdev_tx_queue_mapping(dev, skb);
}
#endif
@@ -5376,7 +5408,8 @@ void kick_defer_list_purge(unsigned int cpu)
backlog_unlock_irq_restore(sd, flags);
} else if (!cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) {
- smp_call_function_single_async(cpu, &sd->defer_csd);
+ if (smp_call_function_single_async(cpu, &sd->defer_csd))
+ WRITE_ONCE(sd->defer_ipi_scheduled, 0);
}
}
@@ -6900,25 +6933,35 @@ bool napi_complete_done(struct napi_struct *n, int work_done)
}
EXPORT_SYMBOL(napi_complete_done);
-static void skb_defer_free_flush(void)
+static void __skb_defer_free_flush(struct skb_defer_node *sdn, int budget)
{
struct llist_node *free_list;
struct sk_buff *skb, *next;
+
+ if (llist_empty(&sdn->defer_list))
+ return;
+ atomic_long_set(&sdn->defer_count, 0);
+ free_list = llist_del_all(&sdn->defer_list);
+
+ llist_for_each_entry_safe(skb, next, free_list, ll_node) {
+ prefetch(next);
+ napi_consume_skb(skb, budget);
+ }
+}
+
+void skb_defer_node_flush(struct skb_defer_node *sdn)
+{
+ __skb_defer_free_flush(sdn, 0);
+}
+
+static void skb_defer_free_flush(void)
+{
struct skb_defer_node *sdn;
int node;
for_each_node(node) {
sdn = this_cpu_ptr(net_hotdata.skb_defer_nodes) + node;
-
- if (llist_empty(&sdn->defer_list))
- continue;
- atomic_long_set(&sdn->defer_count, 0);
- free_list = llist_del_all(&sdn->defer_list);
-
- llist_for_each_entry_safe(skb, next, free_list, ll_node) {
- prefetch(next);
- napi_consume_skb(skb, 1);
- }
+ __skb_defer_free_flush(sdn, 1);
}
}
@@ -12897,6 +12940,7 @@ static int dev_cpu_dead(unsigned int oldcpu)
struct sk_buff **list_skb;
struct sk_buff *skb;
unsigned int cpu;
+ int node;
struct softnet_data *sd, *oldsd, *remsd = NULL;
local_irq_disable();
@@ -12957,6 +13001,17 @@ static int dev_cpu_dead(unsigned int oldcpu)
rps_input_queue_head_incr(oldsd);
}
+ for_each_node(node)
+ skb_defer_node_flush(per_cpu_ptr(net_hotdata.skb_defer_nodes,
+ oldcpu) + node);
+ node = cpu_to_node(oldcpu);
+ if (node_possible(node) &&
+ !cpumask_intersects(cpumask_of_node(node), cpu_online_mask)) {
+ for_each_possible_cpu(cpu)
+ skb_defer_node_flush(per_cpu_ptr(net_hotdata.skb_defer_nodes,
+ cpu) + node);
+ }
+
return 0;
}