summaryrefslogtreecommitdiff
path: root/net/core
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-01-22 20:13:25 -0800
committerJakub Kicinski <kuba@kernel.org>2026-02-05 09:54:08 -0800
commita182a62ff77f705f7dd3d98cf05cb3d03751a8f0 (patch)
tree1bb1f12442bdb77d77acf067fed8ad84c16ec011 /net/core
parenta90f6dcefca6d5ad765435b3188a3a440ed193a1 (diff)
parent8fdb05de0e2db89d8f56144c60ab784812e8c3b7 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Cross-merge networking fixes after downstream PR (net-6.19-rc9). No adjacent changes, conflicts: drivers/net/ethernet/spacemit/k1_emac.c 3125fc1701694 ("net: spacemit: k1-emac: fix jumbo frame support") f66086798f91f ("net: spacemit: Remove broken flow control support") https://lore.kernel.org/aYIysFIE9ooavWia@sirena.org.uk Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/filter.c8
-rw-r--r--net/core/gro.c2
-rw-r--r--net/core/link_watch.c20
-rw-r--r--net/core/net-procfs.c50
4 files changed, 55 insertions, 25 deletions
diff --git a/net/core/filter.c b/net/core/filter.c
index bcd73d9bd764..029e560e32ce 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2289,12 +2289,12 @@ static int __bpf_redirect_neigh_v6(struct sk_buff *skb, struct net_device *dev,
err = bpf_out_neigh_v6(net, skb, dev, nh);
if (unlikely(net_xmit_eval(err)))
- DEV_STATS_INC(dev, tx_errors);
+ dev_core_stats_tx_dropped_inc(dev);
else
ret = NET_XMIT_SUCCESS;
goto out_xmit;
out_drop:
- DEV_STATS_INC(dev, tx_errors);
+ dev_core_stats_tx_dropped_inc(dev);
kfree_skb(skb);
out_xmit:
return ret;
@@ -2396,12 +2396,12 @@ static int __bpf_redirect_neigh_v4(struct sk_buff *skb, struct net_device *dev,
err = bpf_out_neigh_v4(net, skb, dev, nh);
if (unlikely(net_xmit_eval(err)))
- DEV_STATS_INC(dev, tx_errors);
+ dev_core_stats_tx_dropped_inc(dev);
else
ret = NET_XMIT_SUCCESS;
goto out_xmit;
out_drop:
- DEV_STATS_INC(dev, tx_errors);
+ dev_core_stats_tx_dropped_inc(dev);
kfree_skb(skb);
out_xmit:
return ret;
diff --git a/net/core/gro.c b/net/core/gro.c
index ad326c7cdc0a..ef61695fbdbb 100644
--- a/net/core/gro.c
+++ b/net/core/gro.c
@@ -265,6 +265,8 @@ static void gro_complete(struct gro_node *gro, struct sk_buff *skb)
goto out;
}
+ /* NICs can feed encapsulated packets into GRO */
+ skb->encapsulation = 0;
rcu_read_lock();
list_for_each_entry_rcu(ptype, head, list) {
if (ptype->type != type || !ptype->callbacks.gro_complete)
diff --git a/net/core/link_watch.c b/net/core/link_watch.c
index 212cde35affa..25c455c10a01 100644
--- a/net/core/link_watch.c
+++ b/net/core/link_watch.c
@@ -185,10 +185,6 @@ static void linkwatch_do_dev(struct net_device *dev)
netif_state_change(dev);
}
- /* Note: our callers are responsible for calling netdev_tracker_free().
- * This is the reason we use __dev_put() instead of dev_put().
- */
- __dev_put(dev);
}
static void __linkwatch_run_queue(int urgent_only)
@@ -243,6 +239,11 @@ static void __linkwatch_run_queue(int urgent_only)
netdev_lock_ops(dev);
linkwatch_do_dev(dev);
netdev_unlock_ops(dev);
+ /* Use __dev_put() because netdev_tracker_free() was already
+ * called above. Must be after netdev_unlock_ops() to prevent
+ * netdev_run_todo() from freeing the device while still in use.
+ */
+ __dev_put(dev);
do_dev--;
spin_lock_irq(&lweventlist_lock);
}
@@ -278,8 +279,13 @@ void __linkwatch_sync_dev(struct net_device *dev)
{
netdev_ops_assert_locked(dev);
- if (linkwatch_clean_dev(dev))
+ if (linkwatch_clean_dev(dev)) {
linkwatch_do_dev(dev);
+ /* Use __dev_put() because netdev_tracker_free() was already
+ * called inside linkwatch_clean_dev().
+ */
+ __dev_put(dev);
+ }
}
void linkwatch_sync_dev(struct net_device *dev)
@@ -288,6 +294,10 @@ void linkwatch_sync_dev(struct net_device *dev)
netdev_lock_ops(dev);
linkwatch_do_dev(dev);
netdev_unlock_ops(dev);
+ /* Use __dev_put() because netdev_tracker_free() was already
+ * called inside linkwatch_clean_dev().
+ */
+ __dev_put(dev);
}
}
diff --git a/net/core/net-procfs.c b/net/core/net-procfs.c
index 70e0e9a3b650..7dbfa6109f0b 100644
--- a/net/core/net-procfs.c
+++ b/net/core/net-procfs.c
@@ -170,8 +170,14 @@ static const struct seq_operations softnet_seq_ops = {
.show = softnet_seq_show,
};
+struct ptype_iter_state {
+ struct seq_net_private p;
+ struct net_device *dev;
+};
+
static void *ptype_get_idx(struct seq_file *seq, loff_t pos)
{
+ struct ptype_iter_state *iter = seq->private;
struct list_head *ptype_list = NULL;
struct packet_type *pt = NULL;
struct net_device *dev;
@@ -181,12 +187,16 @@ static void *ptype_get_idx(struct seq_file *seq, loff_t pos)
for_each_netdev_rcu(seq_file_net(seq), dev) {
ptype_list = &dev->ptype_all;
list_for_each_entry_rcu(pt, ptype_list, list) {
- if (i == pos)
+ if (i == pos) {
+ iter->dev = dev;
return pt;
+ }
++i;
}
}
+ iter->dev = NULL;
+
list_for_each_entry_rcu(pt, &seq_file_net(seq)->ptype_all, list) {
if (i == pos)
return pt;
@@ -218,6 +228,7 @@ static void *ptype_seq_start(struct seq_file *seq, loff_t *pos)
static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
+ struct ptype_iter_state *iter = seq->private;
struct net *net = seq_file_net(seq);
struct net_device *dev;
struct packet_type *pt;
@@ -229,19 +240,21 @@ static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
return ptype_get_idx(seq, 0);
pt = v;
- nxt = pt->list.next;
- if (pt->dev) {
- if (nxt != &pt->dev->ptype_all)
+ nxt = READ_ONCE(pt->list.next);
+ dev = iter->dev;
+ if (dev) {
+ if (nxt != &dev->ptype_all)
goto found;
- dev = pt->dev;
for_each_netdev_continue_rcu(seq_file_net(seq), dev) {
- if (!list_empty(&dev->ptype_all)) {
- nxt = dev->ptype_all.next;
+ nxt = READ_ONCE(dev->ptype_all.next);
+ if (nxt != &dev->ptype_all) {
+ iter->dev = dev;
goto found;
}
}
- nxt = net->ptype_all.next;
+ iter->dev = NULL;
+ nxt = READ_ONCE(net->ptype_all.next);
goto net_ptype_all;
}
@@ -252,20 +265,20 @@ net_ptype_all:
if (nxt == &net->ptype_all) {
/* continue with ->ptype_specific if it's not empty */
- nxt = net->ptype_specific.next;
+ nxt = READ_ONCE(net->ptype_specific.next);
if (nxt != &net->ptype_specific)
goto found;
}
hash = 0;
- nxt = ptype_base[0].next;
+ nxt = READ_ONCE(ptype_base[0].next);
} else
hash = ntohs(pt->type) & PTYPE_HASH_MASK;
while (nxt == &ptype_base[hash]) {
if (++hash >= PTYPE_HASH_SIZE)
return NULL;
- nxt = ptype_base[hash].next;
+ nxt = READ_ONCE(ptype_base[hash].next);
}
found:
return list_entry(nxt, struct packet_type, list);
@@ -279,19 +292,24 @@ static void ptype_seq_stop(struct seq_file *seq, void *v)
static int ptype_seq_show(struct seq_file *seq, void *v)
{
+ struct ptype_iter_state *iter = seq->private;
struct packet_type *pt = v;
+ struct net_device *dev;
- if (v == SEQ_START_TOKEN)
+ if (v == SEQ_START_TOKEN) {
seq_puts(seq, "Type Device Function\n");
- else if ((!pt->af_packet_net || net_eq(pt->af_packet_net, seq_file_net(seq))) &&
- (!pt->dev || net_eq(dev_net(pt->dev), seq_file_net(seq)))) {
+ return 0;
+ }
+ dev = iter->dev;
+ if ((!pt->af_packet_net || net_eq(pt->af_packet_net, seq_file_net(seq))) &&
+ (!dev || net_eq(dev_net(dev), seq_file_net(seq)))) {
if (pt->type == htons(ETH_P_ALL))
seq_puts(seq, "ALL ");
else
seq_printf(seq, "%04x", ntohs(pt->type));
seq_printf(seq, " %-8s %ps\n",
- pt->dev ? pt->dev->name : "", pt->func);
+ dev ? dev->name : "", pt->func);
}
return 0;
@@ -315,7 +333,7 @@ static int __net_init dev_proc_net_init(struct net *net)
&softnet_seq_ops))
goto out_dev;
if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
- sizeof(struct seq_net_private)))
+ sizeof(struct ptype_iter_state)))
goto out_softnet;
if (wext_proc_init(net))