diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-20 08:16:04 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-20 08:16:04 -0700 |
| commit | 91ec2035134982b98fab0609a9fd8480e8217dc1 (patch) | |
| tree | fed1207d0ad121a52ea1da4b4b8a2c52e3f2c15b /net/core | |
| parent | 5a8cd539ac19f7a68e68e1d25ef9ca2ff55b8500 (diff) | |
| parent | 61eb236c41c2a4717015dff18016a75a5eb90052 (diff) | |
Merge tag 'net-next-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from Jakub Kicinski:
"One of the 'small improvements all over the place' releases for us.
It's hard to draw any direct comparisons because summer vacations
disrupted our patch processing (and presumably - generation) quite a
bit.
Quick and dirty count suggests we (Paolo and I) merged a very similar
number of net (632) and net-next (648) patches. This is not telling
the full story either because 1/3 to 1/2 of the net-next patches also
*seem* like AI-driven low priority fixes, cleanups and clarifications.
We are completely overwhelmed, of course. The glimmer of hope is that
we secured sufficient LLM budget and access (thank you Meta!) to run
reviews with multiple frontier models on each patch. This eliminates
some hallucinations. That said, in terms of review, the LLMs can only
do so much.
The sad truth is that our APIs (especially for rare events like PCIe
errors, timeouts etc) have always been racy, and now LLMs don't let us
ignore that. I expect our direction for the next release will be to
tweak the reviews a little bit more, but start shifting focus to
letting the LLMs take care of the busy work - managing patchwork,
automating common process complaints, editing commit messages, and
maybe applying patches which already got "reviewed-by" tags from
people we trust...
Core & protocols:
- A few steps lowering rtnl_lock dependence:
- per-netns netdev unregistration for select SW drivers (e.g.
veth, ipvlan, tunnels)
- rtnl_lock-less FIB rule changes (RTM_NEWRULE and RTM_DELRULE)
- prepare software drivers and TC qdiscs for rtnl_lock-less GET
- Support BIG TCP (>64kB TSO) in UDP tunnels (vxlan, geneve)
- Support buffers larger than PAGE_SIZE in devmem zero-copy API
- Improve MPTCP handling of extreme memory pressure handling, when
out-of-order queue had to be pruned
- Report the per-group user count via RTM_GETMULTICAST
- Expose the route deletion reason in RTM_DELROUTE
- Add a SO_RIGHTS_NOTRUNC option to UNIX sockets to enable more
useful handling of LSM denials when receiving SCM_RIGHTS messages:
instead of truncating the message at the first blocked fd, keep
every fd slot and store the LSM errno in the blocked slot
- IPv6 Segment Routing - support looking up the post-encap SID
(address) in a different/specified routing table
- Support PRP RedBox (interlink) creation
- Support per-nexthop UDP dst port in VXLAN
- Continue converting getsockopt callbacks in a number of protocols
to iov_iter
Ethernet:
- Merge initial CXL support for AMD/Solarflare NICs (shared branch
with the CXL tree)
- New drivers:
- ADIN1140 10BASE-T1S MACPHY
- Initial skeleton of Intel iXD and ZTE Dinghai drivers
- High-speed NICs:
- AMD/Pensando:
- support firmware flashing
- Cisco (enic):
- SR-IOV V2 admin channel and MBOX protocol
- Huawei (hns3):
- support for ethtool pfc_prevention_tout
- nVidia/Mellanox:
- support sharing bandwidth control across interfaces
of the same device
- Marvell (octeontx2-pf):
- link RQ page pools to netdev for Netlink stats
- Google vNIC:
- XDP metadata support for DQ RDA
- Microsoft vNIC:
- support forcing full-page RX buffers
- Other NICs:
- Synopsys IP:
- eic7700: support for eth1
- Microchip (lan743x):
- support for RMII interface
- Wangxun:
- support for ethtool -G and -C for VFs
- add Tx timeout and PCIe error handling
- Intel (igb/igc):
- RSS key get/set support
- support for forcing link speed without auto-negotiation
- Switches:
- NXP (dpaa2):
- support bonding/LAG offload
- Mediatek:
- mt7530: EN7528 support
- initial support for MT7628
- Micrel (ksz8/9):
- refactoring work to move towards library model
- PTP support for KSZ8463
- nVidia/Mellanox:
- support rtnl-lock-less ethtool callbacks
- Realtek:
- rtl8366rb: use generic RTL83xx code
- support SGMII and HSGMII for RTL8367S
- PHYs:
- Airoha:
- EcoNet EN7528 PHY support
- DAPU Telecom
- DAPU Telecom DAP8211R(I) Gigabit PHY support
- Realtek:
- support RTL8261C_CG
- support RTL8261D
Wireless:
- nl80211: per-link statistics support for multi-link operation
- mac80211: AQL/airtime-fairness support for multicast
- Merge Peripheral Authentication Service (PAS) / TEE support for
ath12k (shared branch with the firmware/qcom tree)
- New drivers:
- mm81x for Morse Micro Long-Range S1G devices
- nxpwifi for NXP devices (mostly forked off from mwifiex)
- Driver changes:
- Broadcom (brcmfmac):
- DPP support, some Cypress part update
- MediaTek (mt76):
- mt7928 support
- mt7925 NAN support
- mt7996 AP powersave improvements
- Qualcomm (ath12k):
- much kernel infrastructure integration work
- AHB platform MultiPD support
- Realtek (rt89):
- LED support
- RTL8922DE support
- dual-BT coex for RTL8922D
- Intel:
- new FW version support
Bluetooth:
- HCI: add support for Shorter Connection Interval (SCI) feature
- af_bluetooth: add minimal context analysis annotations
- Driver changes:
- Intel:
- add Bluetooth SAR revision 2 support
- add vendor_reset PCI sysfs for PLDR
- Mediatek:
- add USB IDs for MT7902 and MT7922 devices
- Realtek:
- add USB IDs for 8761CU and 8852BE devices
- NXP:
- add M.2 Bluetooth device support using pwrseq
Misc:
- DPLL support for manual/numerical oscillator control (NCO)
(implement in zl3073x)
- MCTP support for MCTP over USB v1.1 (DMTF DSP0283)
- Power-over-Ethernet: support Realtek PSE controllers
- Remove the IBM EHEA driver
- Remove tulip/xircom_cb driver"
* tag 'net-next-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1433 commits)
net/mlx5e: do not HW-GRO coalesce small frames
net: openvswitch: fix nf_connlabels leak in ovs_ct_init
net: add missing ref_tracker_dir_exit() to alloc_netdev_mqs()
net: openvswitch: fix flow mask use-after-free on flow deletion
sctp: stop processing a packet once its association is deleted
dpll: zl3073x: add PTP clock support
dpll: zl3073x: add channel ToD, phase step and TIE operations
dpll: zl3073x: scale poll interval proportionally to timeout
ptp: vmclock: prevent read-only mappings from becoming writable
ipv4: reject undersized MTUs in ip_do_fragment()
bonding: initialize err for empty target lists
net: dsa: initial support for MT7628 embedded switch
net: dsa: initial MT7628 tagging driver
net: phy: mediatek: add phy driver for MT7628 built-in Fast Ethernet PHYs
dt-bindings: net: dsa: add MT7628 ESW
net: pse-pd: realtek-pse-mcu: add UART transport
net: pse-pd: realtek-pse-mcu: add I2C transport
net: pse-pd: add Realtek PSE MCU core
dt-bindings: net: pse-pd: add bindings for Realtek PSE MCU
vsock: use sock_error() to consume sk_err after a failed connect
...
Diffstat (limited to 'net/core')
| -rw-r--r-- | net/core/dev.c | 339 | ||||
| -rw-r--r-- | net/core/devmem.c | 37 | ||||
| -rw-r--r-- | net/core/devmem.h | 13 | ||||
| -rw-r--r-- | net/core/failover.c | 4 | ||||
| -rw-r--r-- | net/core/fib_rules.c | 83 | ||||
| -rw-r--r-- | net/core/lock_debug.c | 4 | ||||
| -rw-r--r-- | net/core/net-sysfs.c | 2 | ||||
| -rw-r--r-- | net/core/net_namespace.c | 4 | ||||
| -rw-r--r-- | net/core/netdev-genl-gen.c | 11 | ||||
| -rw-r--r-- | net/core/netdev-genl-gen.h | 1 | ||||
| -rw-r--r-- | net/core/netdev-genl.c | 30 | ||||
| -rw-r--r-- | net/core/netpoll.c | 259 | ||||
| -rw-r--r-- | net/core/page_pool.c | 66 | ||||
| -rw-r--r-- | net/core/pktgen.c | 29 | ||||
| -rw-r--r-- | net/core/rtnetlink.c | 96 | ||||
| -rw-r--r-- | net/core/scm.c | 40 | ||||
| -rw-r--r-- | net/core/selftests.c | 4 | ||||
| -rw-r--r-- | net/core/skbuff.c | 16 | ||||
| -rw-r--r-- | net/core/sysctl_net_core.c | 38 | ||||
| -rw-r--r-- | net/core/tso.c | 3 |
20 files changed, 592 insertions, 487 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index ece6700536d9..38336858c168 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -742,51 +742,73 @@ EXPORT_SYMBOL_GPL(dev_fill_metadata_dst); static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack) { - int k = stack->num_paths++; - - if (k >= NET_DEVICE_PATH_STACK_MAX) + if (stack->num_paths + 1 > NET_DEVICE_PATH_STACK_MAX) return NULL; - return &stack->path[k]; + return &stack->path[stack->num_paths]; +} + +void dev_fill_forward_path_release(struct net_device_path_stack *stack) +{ + struct net_device_path *path; + int k; + + if (stack->num_paths == 0) + return; + + for (k = stack->num_paths - 1; k >= 0; k--) { + path = &stack->path[k]; + switch (path->type) { + case DEV_PATH_TUN: + dst_release(path->tun.dst); + break; + default: + break; + } + } } +EXPORT_SYMBOL_GPL(dev_fill_forward_path_release); -int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr, +int dev_fill_forward_path(struct net_device_path_ctx *ctx, struct net_device_path_stack *stack) { const struct net_device *last_dev; - struct net_device_path_ctx ctx = { - .dev = dev, - }; struct net_device_path *path; int ret = 0; - memcpy(ctx.daddr, daddr, sizeof(ctx.daddr)); stack->num_paths = 0; - while (ctx.dev && ctx.dev->netdev_ops->ndo_fill_forward_path) { - last_dev = ctx.dev; + while (ctx->dev && ctx->dev->netdev_ops->ndo_fill_forward_path) { + last_dev = ctx->dev; path = dev_fwd_path(stack); if (!path) - return -1; + goto err_out; memset(path, 0, sizeof(struct net_device_path)); - ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path); + ret = ctx->dev->netdev_ops->ndo_fill_forward_path(ctx, path); if (ret < 0) - return -1; + goto err_out; - if (WARN_ON_ONCE(last_dev == ctx.dev)) - return -1; + stack->num_paths++; + if (WARN_ON_ONCE(last_dev == ctx->dev)) + goto err_out; } - if (!ctx.dev) + if (!ctx->dev) return ret; path = dev_fwd_path(stack); if (!path) - return -1; + goto err_out; + path->type = DEV_PATH_ETHERNET; - path->dev = ctx.dev; + path->dev = ctx->dev; + stack->num_paths++; - return ret; + return 0; +err_out: + dev_fill_forward_path_release(stack); + + return -1; } EXPORT_SYMBOL_GPL(dev_fill_forward_path); @@ -1802,6 +1824,7 @@ void netif_close_many(struct list_head *head, bool unlink) __dev_close_many(head); list_for_each_entry_safe(dev, tmp, head, close_list) { + netdev_assert_locked_ops_compat(dev); rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP | IFF_RUNNING, GFP_KERNEL, 0, NULL); call_netdevice_notifiers(NETDEV_DOWN, dev); if (unlink) @@ -1912,9 +1935,11 @@ static void call_netdevice_unregister_notifiers(struct notifier_block *nb, struct net_device *dev) { if (dev->flags & IFF_UP) { + netdev_lock_ops(dev); call_netdevice_notifier(nb, NETDEV_GOING_DOWN, dev); call_netdevice_notifier(nb, NETDEV_DOWN, dev); + netdev_unlock_ops(dev); } call_netdevice_notifier(nb, NETDEV_UNREGISTER, dev); } @@ -2627,13 +2652,15 @@ EXPORT_SYMBOL_GPL(dev_queue_xmit_nit); */ static void netif_setup_tc(struct net_device *dev, unsigned int txq) { + struct netdev_tc_txq res; int i; - struct netdev_tc_txq *tc = &dev->tc_to_txq[0]; + + res.combined = READ_ONCE(dev->tc_to_txq[0].combined); /* If TC0 is invalidated disable TC mapping */ - if (tc->offset + tc->count > txq) { + if (res.offset + res.count > txq) { netdev_warn(dev, "Number of in use tx queues changed invalidating tc mappings. Priority traffic classification disabled!\n"); - dev->num_tc = 0; + WRITE_ONCE(dev->num_tc, 0); return; } @@ -2641,8 +2668,8 @@ static void netif_setup_tc(struct net_device *dev, unsigned int txq) for (i = 1; i < TC_BITMASK + 1; i++) { int q = netdev_get_prio_tc_map(dev, i); - tc = &dev->tc_to_txq[q]; - if (tc->offset + tc->count > txq) { + res.combined = READ_ONCE(dev->tc_to_txq[q].combined); + if (res.offset + res.count > txq) { netdev_warn(dev, "Number of in use tx queues changed. Priority %i to tc mapping %i is no longer valid. Setting map to 0\n", i, q); netdev_set_prio_tc_map(dev, i, 0); @@ -2652,13 +2679,16 @@ static void netif_setup_tc(struct net_device *dev, unsigned int txq) int netdev_txq_to_tc(struct net_device *dev, unsigned int txq) { - if (dev->num_tc) { + if (READ_ONCE(dev->num_tc)) { struct netdev_tc_txq *tc = &dev->tc_to_txq[0]; int i; /* walk through the TCs and see if it falls into any of them */ for (i = 0; i < TC_MAX_QUEUE; i++, tc++) { - if ((txq - tc->offset) < tc->count) + struct netdev_tc_txq res; + + res.combined = READ_ONCE(tc->combined); + if ((txq - res.offset) < res.count) return i; } @@ -2851,18 +2881,19 @@ int __netif_set_xps_queue(struct net_device *dev, const unsigned long *mask, u16 index, enum xps_map_type type) { struct xps_dev_maps *dev_maps, *new_dev_maps = NULL, *old_dev_maps = NULL; + int maps_sz, num_tc = 1, tc = 0, dev_num_tc; const unsigned long *online_mask = NULL; bool active = false, copy = false; int i, j, tci, numa_node_id = -2; - int maps_sz, num_tc = 1, tc = 0; struct xps_map *map, *new_map; unsigned int nr_ids; WARN_ON_ONCE(index >= dev->num_tx_queues); - if (dev->num_tc) { + dev_num_tc = READ_ONCE(dev->num_tc); + if (dev_num_tc) { /* Do not allow XPS on subordinate device directly */ - num_tc = dev->num_tc; + num_tc = dev_num_tc; if (num_tc < 0) return -EINVAL; @@ -3078,28 +3109,36 @@ static void netdev_unbind_all_sb_channels(struct net_device *dev) void netdev_reset_tc(struct net_device *dev) { + int i; + #ifdef CONFIG_XPS netif_reset_xps_queues_gt(dev, 0); #endif netdev_unbind_all_sb_channels(dev); /* Reset TC configuration of device */ - dev->num_tc = 0; - memset(dev->tc_to_txq, 0, sizeof(dev->tc_to_txq)); - memset(dev->prio_tc_map, 0, sizeof(dev->prio_tc_map)); + WRITE_ONCE(dev->num_tc, 0); + for (i = 0; i < TC_MAX_QUEUE; i++) + WRITE_ONCE(dev->tc_to_txq[i].combined, 0); + for (i = 0; i <= TC_BITMASK; i++) + WRITE_ONCE(dev->prio_tc_map[i], 0); } EXPORT_SYMBOL(netdev_reset_tc); int netdev_set_tc_queue(struct net_device *dev, u8 tc, u16 count, u16 offset) { - if (tc >= dev->num_tc) + struct netdev_tc_txq res = { + .count = count, + .offset = offset, + }; + + if (tc >= READ_ONCE(dev->num_tc)) return -EINVAL; #ifdef CONFIG_XPS netif_reset_xps_queues(dev, offset, count); #endif - dev->tc_to_txq[tc].count = count; - dev->tc_to_txq[tc].offset = offset; + WRITE_ONCE(dev->tc_to_txq[tc].combined, res.combined); return 0; } EXPORT_SYMBOL(netdev_set_tc_queue); @@ -3114,7 +3153,7 @@ int netdev_set_num_tc(struct net_device *dev, u8 num_tc) #endif netdev_unbind_all_sb_channels(dev); - dev->num_tc = num_tc; + WRITE_ONCE(dev->num_tc, num_tc); return 0; } EXPORT_SYMBOL(netdev_set_num_tc); @@ -3123,12 +3162,15 @@ void netdev_unbind_sb_channel(struct net_device *dev, struct net_device *sb_dev) { struct netdev_queue *txq = &dev->_tx[dev->num_tx_queues]; + int i; #ifdef CONFIG_XPS netif_reset_xps_queues_gt(sb_dev, 0); #endif - memset(sb_dev->tc_to_txq, 0, sizeof(sb_dev->tc_to_txq)); - memset(sb_dev->prio_tc_map, 0, sizeof(sb_dev->prio_tc_map)); + for (i = 0; i < TC_MAX_QUEUE; i++) + WRITE_ONCE(sb_dev->tc_to_txq[i].combined, 0); + for (i = 0; i <= TC_BITMASK; i++) + WRITE_ONCE(sb_dev->prio_tc_map[i], 0); while (txq-- != &dev->_tx[0]) { if (txq->sb_dev == sb_dev) @@ -3142,7 +3184,7 @@ int netdev_bind_sb_channel_queue(struct net_device *dev, u8 tc, u16 count, u16 offset) { /* Make certain the sb_dev and dev are already configured */ - if (sb_dev->num_tc >= 0 || tc >= dev->num_tc) + if (READ_ONCE(sb_dev->num_tc) >= 0 || tc >= READ_ONCE(dev->num_tc)) return -EINVAL; /* We cannot hand out queues we don't have */ @@ -3150,8 +3192,12 @@ int netdev_bind_sb_channel_queue(struct net_device *dev, return -EINVAL; /* Record the mapping */ - sb_dev->tc_to_txq[tc].count = count; - sb_dev->tc_to_txq[tc].offset = offset; + struct netdev_tc_txq res = { + .count = count, + .offset = offset, + }; + + WRITE_ONCE(sb_dev->tc_to_txq[tc].combined, res.combined); /* Provide a way for Tx queue to find the tc_to_txq map or * XPS map for itself. @@ -3177,7 +3223,7 @@ int netdev_set_sb_channel(struct net_device *dev, u16 channel) if (channel > S16_MAX) return -EINVAL; - dev->num_tc = -channel; + WRITE_ONCE(dev->num_tc, -channel); return 0; } @@ -3206,7 +3252,7 @@ int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq) if (rc) return rc; - if (dev->num_tc) + if (READ_ONCE(dev->num_tc)) netif_setup_tc(dev, txq); net_shaper_set_real_num_tx_queues(dev, txq); @@ -3515,11 +3561,13 @@ static u16 skb_tx_hash(const struct net_device *dev, u16 qoffset = 0; u16 qcount = dev->real_num_tx_queues; - if (dev->num_tc) { + if (READ_ONCE(dev->num_tc)) { u8 tc = netdev_get_prio_tc_map(dev, skb->priority); + struct netdev_tc_txq res; - qoffset = sb_dev->tc_to_txq[tc].offset; - qcount = sb_dev->tc_to_txq[tc].count; + res.combined = READ_ONCE(sb_dev->tc_to_txq[tc].combined); + qoffset = res.offset; + qcount = res.count; if (unlikely(!qcount)) { net_warn_ratelimited("%s: invalid qcount, qoffset %u for tc %u\n", sb_dev->name, qoffset, tc); @@ -9795,6 +9843,8 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags, { unsigned int changes = dev->flags ^ old_flags; + netdev_assert_locked_ops_compat(dev); + if (gchanges) rtmsg_ifinfo(RTM_NEWLINK, dev, gchanges, GFP_ATOMIC, portid, nlh); @@ -10333,6 +10383,37 @@ static int dev_xdp_install(struct net_device *dev, enum bpf_xdp_mode mode, netdev_assert_locked_ops_compat(dev); + if (prog) { + enum bpf_xdp_mode other_mode = mode == XDP_MODE_SKB + ? XDP_MODE_DRV : XDP_MODE_SKB; + bool offload = mode == XDP_MODE_HW; + + if (!offload && dev_xdp_prog(dev, other_mode)) { + NL_SET_ERR_MSG(extack, "Native and generic XDP can't be active at the same time"); + return -EEXIST; + } + if (!offload && bpf_prog_is_offloaded(prog->aux)) { + NL_SET_ERR_MSG(extack, "Using offloaded program without HW_MODE flag is not supported"); + return -EINVAL; + } + if (bpf_prog_is_dev_bound(prog->aux) && !bpf_offload_dev_match(prog, dev)) { + NL_SET_ERR_MSG(extack, "Program bound to different device"); + return -EINVAL; + } + if (bpf_prog_is_dev_bound(prog->aux) && mode == XDP_MODE_SKB) { + NL_SET_ERR_MSG(extack, "Can't attach device-bound programs in generic mode"); + return -EINVAL; + } + if (prog->expected_attach_type == BPF_XDP_DEVMAP) { + NL_SET_ERR_MSG(extack, "BPF_XDP_DEVMAP programs can not be attached to a device"); + return -EINVAL; + } + if (prog->expected_attach_type == BPF_XDP_CPUMAP) { + NL_SET_ERR_MSG(extack, "BPF_XDP_CPUMAP programs can not be attached to a device"); + return -EINVAL; + } + } + if (dev->cfg->hds_config == ETHTOOL_TCP_DATA_SPLIT_ENABLED && prog && !prog->aux->xdp_has_frags) { NL_SET_ERR_MSG(extack, "unable to install XDP to device using tcp-data-split"); @@ -10472,38 +10553,10 @@ static int dev_xdp_attach(struct net_device *dev, struct netlink_ext_ack *extack new_prog = link->link.prog; if (new_prog) { - bool offload = mode == XDP_MODE_HW; - enum bpf_xdp_mode other_mode = mode == XDP_MODE_SKB - ? XDP_MODE_DRV : XDP_MODE_SKB; - if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && cur_prog) { NL_SET_ERR_MSG(extack, "XDP program already attached"); return -EBUSY; } - if (!offload && dev_xdp_prog(dev, other_mode)) { - NL_SET_ERR_MSG(extack, "Native and generic XDP can't be active at the same time"); - return -EEXIST; - } - if (!offload && bpf_prog_is_offloaded(new_prog->aux)) { - NL_SET_ERR_MSG(extack, "Using offloaded program without HW_MODE flag is not supported"); - return -EINVAL; - } - if (bpf_prog_is_dev_bound(new_prog->aux) && !bpf_offload_dev_match(new_prog, dev)) { - NL_SET_ERR_MSG(extack, "Program bound to different device"); - return -EINVAL; - } - if (bpf_prog_is_dev_bound(new_prog->aux) && mode == XDP_MODE_SKB) { - NL_SET_ERR_MSG(extack, "Can't attach device-bound programs in generic mode"); - return -EINVAL; - } - if (new_prog->expected_attach_type == BPF_XDP_DEVMAP) { - NL_SET_ERR_MSG(extack, "BPF_XDP_DEVMAP programs can not be attached to a device"); - return -EINVAL; - } - if (new_prog->expected_attach_type == BPF_XDP_CPUMAP) { - NL_SET_ERR_MSG(extack, "BPF_XDP_CPUMAP programs can not be attached to a device"); - return -EINVAL; - } } /* don't call drivers if the effective program didn't change */ @@ -11619,8 +11672,13 @@ static struct net_device *netdev_wait_allrefs_any(struct list_head *list) rtnl_lock(); /* Rebroadcast unregister notification */ - list_for_each_entry(dev, list, todo_list) + list_for_each_entry(dev, list, todo_list) { + struct net *net = dev_net(dev); + + __rtnl_net_lock(net); call_netdevice_notifiers(NETDEV_UNREGISTER, dev); + __rtnl_net_unlock(net); + } __rtnl_unlock(); rcu_barrier(); @@ -12098,6 +12156,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, INIT_LIST_HEAD(&dev->napi_list); INIT_LIST_HEAD(&dev->unreg_list); +#ifdef CONFIG_DEBUG_NET_SMALL_RTNL + INIT_LIST_HEAD(&dev->unreg_list_net); +#endif INIT_LIST_HEAD(&dev->close_list); INIT_LIST_HEAD(&dev->link_watch_list); INIT_LIST_HEAD(&dev->adj_list.upper); @@ -12164,6 +12225,7 @@ free_pcpu: free_percpu(dev->pcpu_refcnt); free_dev: #endif + ref_tracker_dir_exit(&dev->refcnt_tracker); kvfree(dev); return NULL; } @@ -12315,6 +12377,10 @@ void unregister_netdevice_queue(struct net_device *dev, struct list_head *head) { ASSERT_RTNL(); +#ifdef CONFIG_DEBUG_NET_SMALL_RTNL + DEBUG_NET_WARN_ON_ONCE(!list_empty(&dev->unreg_list_net)); +#endif + if (head) { list_move_tail(&dev->unreg_list, head); } else { @@ -12492,6 +12558,16 @@ void unregister_netdevice_many_notify(struct list_head *head, synchronize_net(); list_for_each_entry(dev, head, unreg_list) { +#ifdef CONFIG_DEBUG_NET_SMALL_RTNL + struct net *net = dev_net(dev); + + /* spin_lock() can be moved outside of the loop + * once the per-netns RTNL conversion completes. + */ + spin_lock(&net->dev_unreg_lock); + list_del(&dev->unreg_list_net); + spin_unlock(&net->dev_unreg_lock); +#endif netdev_put(dev, &dev->dev_registered_tracker); net_set_todo(dev); cnt++; @@ -12514,6 +12590,96 @@ void unregister_netdevice_many(struct list_head *head) } EXPORT_SYMBOL(unregister_netdevice_many); +#ifdef CONFIG_DEBUG_NET_SMALL_RTNL +void unregister_netdevice_queue_net(struct net *net, struct net_device *dev, + struct list_head *head) +{ + netdev_lock(dev); + + if (net_eq(dev_net(dev), net)) { + netdev_unlock(dev); + unregister_netdevice_queue(dev, head); + return; + } + + net = dev_net(dev); + + spin_lock(&net->dev_unreg_lock); + + DEBUG_NET_WARN_ON_ONCE(!list_empty(&dev->unreg_list)); + DEBUG_NET_WARN_ON_ONCE(!list_empty(&dev->unreg_list_net)); + + list_add_tail(&dev->unreg_list_net, &net->dev_unreg_head); + rtnl_net_queue_work(net); + + spin_unlock(&net->dev_unreg_lock); + + netdev_unlock(dev); +} +EXPORT_SYMBOL(unregister_netdevice_queue_net); + +void unregister_netdevice_queue_many_net(struct net *net, struct list_head *head) +{ + struct net_device *dev, *tmp; + + spin_lock(&net->dev_unreg_lock); + list_for_each_entry_safe(dev, tmp, head, unreg_list) { + /* Once all cross-netns unregister_netdevice_queue() is + * converted to _net() (or for debugging), remove this check. + */ + if (!net_eq(dev_net(dev), net)) + continue; + + DEBUG_NET_WARN_ONCE(!net_eq(dev_net(dev), net), + "%s was unregistered from a different netns.\n", + dev->name); + + list_del_init(&dev->unreg_list); + list_move_tail(&dev->unreg_list_net, &net->dev_unreg_head); + } + spin_unlock(&net->dev_unreg_lock); +} + +static void unregister_netdevice_move_net(struct net *net_old, + struct net *net, + struct net_device *dev) +{ + if (net_old > net) { + spin_lock(&net->dev_unreg_lock); + spin_lock_nested(&net_old->dev_unreg_lock, SINGLE_DEPTH_NESTING); + } else { + spin_lock(&net_old->dev_unreg_lock); + spin_lock_nested(&net->dev_unreg_lock, SINGLE_DEPTH_NESTING); + } + + if (!list_empty(&dev->unreg_list_net)) { + list_del(&dev->unreg_list_net); + list_add_tail(&dev->unreg_list_net, &net->dev_unreg_head); + } + + spin_unlock(&net_old->dev_unreg_lock); + spin_unlock(&net->dev_unreg_lock); +} + +void unregister_netdevice_many_net(struct net *net) +{ + struct net_device *dev, *tmp; + LIST_HEAD(unreg_head_net); + LIST_HEAD(unreg_head); + + spin_lock(&net->dev_unreg_lock); + list_splice_init(&net->dev_unreg_head, &unreg_head_net); + spin_unlock(&net->dev_unreg_lock); + + list_for_each_entry_safe(dev, tmp, &unreg_head_net, unreg_list_net) { + list_del_init(&dev->unreg_list_net); + list_add_tail(&dev->unreg_list, &unreg_head); + } + + unregister_netdevice_many(&unreg_head); +} +#endif + /** * unregister_netdev - remove device from the kernel * @dev: device @@ -12670,6 +12836,10 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net, netdev_unlock(dev); dev->ifindex = new_ifindex; +#ifdef CONFIG_DEBUG_NET_SMALL_RTNL + unregister_netdevice_move_net(net_old, net, dev); +#endif + if (new_name[0]) { /* Rename the netdev to prepared name */ write_seqlock_bh(&netdev_rename_lock); @@ -13046,7 +13216,7 @@ static void __net_exit default_device_exit_net(struct net *net) * Push all migratable network devices back to the * initial network namespace */ - ASSERT_RTNL(); + for_each_netdev_safe(net, dev, aux) { int err; char fb_name[IFNAMSIZ]; @@ -13089,21 +13259,36 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list) LIST_HEAD(dev_kill_list); rtnl_lock(); + + __rtnl_net_lock(&init_net); + list_for_each_entry(net, net_list, exit_list) { + __rtnl_net_lock(net); default_device_exit_net(net); + __rtnl_net_unlock(net); + cond_resched(); } + __rtnl_net_unlock(&init_net); + list_for_each_entry(net, net_list, exit_list) { + __rtnl_net_lock(net); + for_each_netdev_reverse(net, dev) { if (dev->rtnl_link_ops && dev->rtnl_link_ops->dellink) dev->rtnl_link_ops->dellink(dev, &dev_kill_list); else unregister_netdevice_queue(dev, &dev_kill_list); } + + unregister_netdevice_queue_many_net(net, &dev_kill_list); + __rtnl_net_unlock(net); } unregister_netdevice_many(&dev_kill_list); rtnl_unlock(); + + rtnl_net_flush_workqueue(); } static struct pernet_operations __net_initdata default_device_ops = { diff --git a/net/core/devmem.c b/net/core/devmem.c index 957d6b96216b..f4d60654ce7f 100644 --- a/net/core/devmem.c +++ b/net/core/devmem.c @@ -46,7 +46,7 @@ static dma_addr_t net_devmem_get_dma_addr(const struct net_iov *niov) owner = net_devmem_iov_to_chunk_owner(niov); return owner->base_dma_addr + - ((dma_addr_t)net_iov_idx(niov) << PAGE_SHIFT); + ((dma_addr_t)net_iov_idx(niov) << owner->binding->niov_shift); } static void net_devmem_dmabuf_binding_release(struct percpu_ref *ref) @@ -93,13 +93,14 @@ net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding) ssize_t offset; ssize_t index; - dma_addr = gen_pool_alloc_owner(binding->chunk_pool, PAGE_SIZE, + dma_addr = gen_pool_alloc_owner(binding->chunk_pool, + 1UL << binding->niov_shift, (void **)&owner); if (!dma_addr) return NULL; offset = dma_addr - owner->base_dma_addr; - index = offset / PAGE_SIZE; + index = offset >> binding->niov_shift; niov = &owner->area.niovs[index]; niov->desc.pp_magic = 0; @@ -113,12 +114,13 @@ void net_devmem_free_dmabuf(struct net_iov *niov) { struct net_devmem_dmabuf_binding *binding = net_devmem_iov_binding(niov); unsigned long dma_addr = net_devmem_get_dma_addr(niov); + size_t niov_size = 1UL << binding->niov_shift; if (WARN_ON(!gen_pool_has_addr(binding->chunk_pool, dma_addr, - PAGE_SIZE))) + niov_size))) return; - gen_pool_free(binding->chunk_pool, dma_addr, PAGE_SIZE); + gen_pool_free(binding->chunk_pool, dma_addr, niov_size); } void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding) @@ -163,6 +165,9 @@ int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx, u32 xa_idx; int err; + if (binding->niov_shift != PAGE_SHIFT) + mp_params.rx_page_size = 1U << binding->niov_shift; + err = netif_mp_open_rxq(dev, rxq_idx, &mp_params, extack); if (err) return err; @@ -184,10 +189,12 @@ struct net_devmem_dmabuf_binding * net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, struct device *dma_dev, enum dma_data_direction direction, - unsigned int dmabuf_fd, struct netdev_nl_sock *priv, + unsigned int dmabuf_fd, unsigned int niov_shift, + struct netdev_nl_sock *priv, struct netlink_ext_ack *extack) { struct net_devmem_dmabuf_binding *binding; + size_t niov_size = 1UL << niov_shift; static u32 id_alloc_next; struct scatterlist *sg; struct dma_buf *dmabuf; @@ -213,6 +220,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, binding->dev = dev; binding->vdev = vdev; + binding->niov_shift = niov_shift; xa_init_flags(&binding->bound_rxqs, XA_FLAGS_ALLOC); err = percpu_ref_init(&binding->ref, @@ -255,11 +263,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, } } - /* For simplicity we expect to make PAGE_SIZE allocations, but the - * binding can be much more flexible than that. We may be able to - * allocate MTU sized chunks here. Leave that for future work... - */ - binding->chunk_pool = gen_pool_create(PAGE_SHIFT, + binding->chunk_pool = gen_pool_create(niov_shift, dev_to_node(&dev->dev)); if (!binding->chunk_pool) { err = -ENOMEM; @@ -273,9 +277,12 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, size_t len = sg_dma_len(sg); struct net_iov *niov; - if (!IS_ALIGNED(len, PAGE_SIZE)) { + if (!IS_ALIGNED(dma_addr, niov_size) || + !IS_ALIGNED(len, niov_size)) { err = -EINVAL; - NL_SET_ERR_MSG(extack, "dma-buf SG length must be PAGE_SIZE aligned"); + NL_SET_ERR_MSG_FMT(extack, + "dmabuf sg entry (addr=%pad, len=%zu) not aligned to niov size %zu", + &dma_addr, len, niov_size); goto err_free_chunks; } @@ -288,7 +295,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, owner->area.base_virtual = virtual; owner->base_dma_addr = dma_addr; - owner->area.num_niovs = len / PAGE_SIZE; + owner->area.num_niovs = len >> niov_shift; owner->binding = binding; err = gen_pool_add_owner(binding->chunk_pool, dma_addr, @@ -454,7 +461,7 @@ int mp_dmabuf_devmem_init(struct page_pool *pool) pool->dma_sync = false; pool->dma_sync_for_cpu = false; - if (pool->p.order != 0) + if (pool->p.order != binding->niov_shift - PAGE_SHIFT) return -E2BIG; net_devmem_dmabuf_binding_get(binding); diff --git a/net/core/devmem.h b/net/core/devmem.h index 3852a56036cb..4a293a7d1149 100644 --- a/net/core/devmem.h +++ b/net/core/devmem.h @@ -71,6 +71,8 @@ struct net_devmem_dmabuf_binding { */ struct net_iov **tx_vec; + unsigned int niov_shift; + struct work_struct unbind_w; }; @@ -93,7 +95,8 @@ struct net_devmem_dmabuf_binding * net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, struct device *dma_dev, enum dma_data_direction direction, - unsigned int dmabuf_fd, struct netdev_nl_sock *priv, + unsigned int dmabuf_fd, unsigned int niov_shift, + struct netdev_nl_sock *priv, struct netlink_ext_ack *extack); struct net_devmem_dmabuf_binding *net_devmem_lookup_dmabuf(u32 id); void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding); @@ -122,10 +125,11 @@ static inline u32 net_devmem_iov_binding_id(const struct net_iov *niov) static inline unsigned long net_iov_virtual_addr(const struct net_iov *niov) { - struct net_iov_area *owner = net_iov_owner(niov); + struct dmabuf_genpool_chunk_owner *co = + net_devmem_iov_to_chunk_owner(niov); - return owner->base_virtual + - ((unsigned long)net_iov_idx(niov) << PAGE_SHIFT); + return net_iov_owner(niov)->base_virtual + + ((unsigned long)net_iov_idx(niov) << co->binding->niov_shift); } static inline bool @@ -175,6 +179,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev, struct device *dma_dev, enum dma_data_direction direction, unsigned int dmabuf_fd, + unsigned int niov_shift, struct netdev_nl_sock *priv, struct netlink_ext_ack *extack) { diff --git a/net/core/failover.c b/net/core/failover.c index e43c59cd6868..4c3894a02cb7 100644 --- a/net/core/failover.c +++ b/net/core/failover.c @@ -302,9 +302,7 @@ EXPORT_SYMBOL_GPL(failover_unregister); static __init int failover_init(void) { - register_netdevice_notifier(&failover_notifier); - - return 0; + return register_netdevice_notifier(&failover_notifier); } module_init(failover_init); diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index cf374c208732..7df216c17c67 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -51,7 +51,6 @@ bool fib_rule_matchall(const struct fib_rule *rule) return false; return true; } -EXPORT_SYMBOL_GPL(fib_rule_matchall); int fib_default_rule_add(struct fib_rules_ops *ops, u32 pref, u32 table) @@ -78,7 +77,6 @@ int fib_default_rule_add(struct fib_rules_ops *ops, list_add_tail(&r->list, &ops->rules_list); return 0; } -EXPORT_SYMBOL(fib_default_rule_add); static u32 fib_default_rule_pref(struct fib_rules_ops *ops) { @@ -172,6 +170,7 @@ fib_rules_register(const struct fib_rules_ops *tmpl, struct net *net) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&ops->rules_list); + mutex_init(&ops->lock); ops->fro_net = net; err = __fib_rules_register(ops); @@ -182,7 +181,6 @@ fib_rules_register(const struct fib_rules_ops *tmpl, struct net *net) return ops; } -EXPORT_SYMBOL_GPL(fib_rules_register); static void fib_rules_cleanup_ops(struct fib_rules_ops *ops) { @@ -205,9 +203,9 @@ void fib_rules_unregister(struct fib_rules_ops *ops) spin_unlock(&net->rules_mod_lock); fib_rules_cleanup_ops(ops); + mutex_destroy(&ops->lock); kfree_rcu(ops, rcu); } -EXPORT_SYMBOL_GPL(fib_rules_unregister); static int uid_range_set(struct fib_kuid_range *range) { @@ -363,7 +361,6 @@ out: return err; } -EXPORT_SYMBOL_GPL(fib_rules_lookup); static int call_fib_rule_notifier(struct notifier_block *nb, enum fib_event_type event_type, @@ -391,7 +388,7 @@ static int call_fib_rule_notifiers(struct net *net, .rule = rule, }; - ASSERT_RTNL_NET(net); + lockdep_assert_held(&ops->lock); /* Paired with READ_ONCE() in fib_rules_seq() */ WRITE_ONCE(ops->fib_rules_seq, ops->fib_rules_seq + 1); @@ -423,7 +420,6 @@ int fib_rules_dump(struct net *net, struct notifier_block *nb, int family, return err; } -EXPORT_SYMBOL_GPL(fib_rules_dump); unsigned int fib_rules_seq_read(const struct net *net, int family) { @@ -439,7 +435,6 @@ unsigned int fib_rules_seq_read(const struct net *net, int family) return fib_rules_seq; } -EXPORT_SYMBOL_GPL(fib_rules_seq_read); static struct fib_rule *rule_find(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, @@ -740,10 +735,10 @@ errout: return err; } -static int fib_nl2rule_rtnl(struct fib_rule *nlrule, - struct fib_rules_ops *ops, - struct nlattr *tb[], - struct netlink_ext_ack *extack) +static int fib_nl2rule_locked(struct fib_rule *nlrule, + struct fib_rules_ops *ops, + struct nlattr *tb[], + struct netlink_ext_ack *extack) { if (!tb[FRA_PRIORITY]) nlrule->pref = fib_default_rule_pref(ops); @@ -754,12 +749,14 @@ static int fib_nl2rule_rtnl(struct fib_rule *nlrule, return -EINVAL; } + rcu_read_lock(); + if (tb[FRA_IIFNAME]) { struct net_device *dev; - dev = __dev_get_by_name(nlrule->fr_net, nlrule->iifname); + dev = dev_get_by_name_rcu(nlrule->fr_net, nlrule->iifname); if (dev) { - nlrule->iifindex = dev->ifindex; + nlrule->iifindex = READ_ONCE(dev->ifindex); nlrule->iif_is_l3_master = netif_is_l3_master(dev); } } @@ -767,13 +764,15 @@ static int fib_nl2rule_rtnl(struct fib_rule *nlrule, if (tb[FRA_OIFNAME]) { struct net_device *dev; - dev = __dev_get_by_name(nlrule->fr_net, nlrule->oifname); + dev = dev_get_by_name_rcu(nlrule->fr_net, nlrule->oifname); if (dev) { - nlrule->oifindex = dev->ifindex; + nlrule->oifindex = READ_ONCE(dev->ifindex); nlrule->oif_is_l3_master = netif_is_l3_master(dev); } } + rcu_read_unlock(); + return 0; } @@ -883,6 +882,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr *tb[FRA_MAX + 1]; bool user_priority = false; struct fib_rule_hdr *frh; + bool unlock_rtnl = false; frh = nlmsg_payload(nlh, sizeof(*frh)); if (!frh) { @@ -908,10 +908,13 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, if (err) goto errout; - if (!rtnl_held) + if (!rtnl_held && ops->need_rtnl && ops->need_rtnl(net)) { + unlock_rtnl = true; rtnl_net_lock(net); + } + mutex_lock(&ops->lock); - err = fib_nl2rule_rtnl(rule, ops, tb, extack); + err = fib_nl2rule_locked(rule, ops, tb, extack); if (err) goto errout_free; @@ -959,7 +962,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, list_for_each_entry(r, &ops->rules_list, list) { if (r->action == FR_ACT_GOTO && r->target == rule->pref && - rtnl_dereference(r->ctarget) == NULL) { + !rcu_access_pointer(r->ctarget)) { rcu_assign_pointer(r->ctarget, rule); if (--ops->unresolved_rules == 0) break; @@ -978,7 +981,8 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, fib_rule_get(rule); - if (!rtnl_held) + mutex_unlock(&ops->lock); + if (unlock_rtnl) rtnl_net_unlock(net); notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid); @@ -988,7 +992,8 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, return 0; errout_free: - if (!rtnl_held) + mutex_unlock(&ops->lock); + if (unlock_rtnl) rtnl_net_unlock(net); kfree(rule); errout: @@ -1037,10 +1042,9 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, if (err) goto errout; - if (!rtnl_held) - rtnl_net_lock(net); + mutex_lock(&ops->lock); - err = fib_nl2rule_rtnl(nlrule, ops, tb, extack); + err = fib_nl2rule_locked(nlrule, ops, tb, extack); if (err) goto errout_free; @@ -1055,11 +1059,8 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, goto errout_free; } - if (ops->delete) { - err = ops->delete(rule); - if (err) - goto errout_free; - } + if (ops->delete) + ops->delete(rule); if (rule->tun_id) ip_tunnel_unneed_metadata(); @@ -1068,7 +1069,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, if (rule->action == FR_ACT_GOTO) { ops->nr_goto_rules--; - if (rtnl_dereference(rule->ctarget) == NULL) + if (!rcu_access_pointer(rule->ctarget)) ops->unresolved_rules--; } @@ -1086,7 +1087,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, if (&n->list == &ops->rules_list || n->pref != rule->pref) n = NULL; list_for_each_entry(r, &ops->rules_list, list) { - if (rtnl_dereference(r->ctarget) != rule) + if (rcu_access_pointer(r->ctarget) != rule) continue; rcu_assign_pointer(r->ctarget, n); if (!n) @@ -1096,8 +1097,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops, NULL); - if (!rtnl_held) - rtnl_net_unlock(net); + mutex_unlock(&ops->lock); notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).portid); fib_rule_put(rule); @@ -1107,8 +1107,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh, return 0; errout_free: - if (!rtnl_held) - rtnl_net_unlock(net); + mutex_unlock(&ops->lock); kfree(nlrule); errout: rules_ops_put(ops); @@ -1402,24 +1401,30 @@ static int fib_rules_event(struct notifier_block *this, unsigned long event, struct net *net = dev_net(dev); struct fib_rules_ops *ops; - ASSERT_RTNL(); - switch (event) { case NETDEV_REGISTER: - list_for_each_entry(ops, &net->rules_ops, list) + list_for_each_entry(ops, &net->rules_ops, list) { + mutex_lock(&ops->lock); attach_rules(&ops->rules_list, dev); + mutex_unlock(&ops->lock); + } break; case NETDEV_CHANGENAME: list_for_each_entry(ops, &net->rules_ops, list) { + mutex_lock(&ops->lock); detach_rules(&ops->rules_list, dev); attach_rules(&ops->rules_list, dev); + mutex_unlock(&ops->lock); } break; case NETDEV_UNREGISTER: - list_for_each_entry(ops, &net->rules_ops, list) + list_for_each_entry(ops, &net->rules_ops, list) { + mutex_lock(&ops->lock); detach_rules(&ops->rules_list, dev); + mutex_unlock(&ops->lock); + } break; } diff --git a/net/core/lock_debug.c b/net/core/lock_debug.c index 8a81c5430705..abc4c00728b1 100644 --- a/net/core/lock_debug.c +++ b/net/core/lock_debug.c @@ -24,15 +24,15 @@ int netdev_debug_event(struct notifier_block *nb, unsigned long event, case NETDEV_CHANGE: case NETDEV_REGISTER: case NETDEV_UP: + case NETDEV_DOWN: + case NETDEV_GOING_DOWN: netdev_assert_locked_ops_compat(dev); fallthrough; - case NETDEV_DOWN: case NETDEV_REBOOT: case NETDEV_UNREGISTER: case NETDEV_CHANGEMTU: case NETDEV_CHANGEADDR: case NETDEV_PRE_CHANGEADDR: - case NETDEV_GOING_DOWN: case NETDEV_FEAT_CHANGE: case NETDEV_BONDING_FAILOVER: case NETDEV_PRE_UP: diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 25546deacec8..352173df7578 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1432,7 +1432,7 @@ static ssize_t traffic_class_show(struct kobject *kobj, struct attribute *attr, /* If queue belongs to subordinate dev use its TC mapping */ dev = netdev_get_tx_queue(dev, index)->sb_dev ? : dev; - num_tc = dev->num_tc; + num_tc = READ_ONCE(dev->num_tc); tc = netdev_txq_to_tc(dev, index); rtnl_unlock(); diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index d9dafe24f57e..a91d2b58aadd 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -181,6 +181,7 @@ static void ops_exit_rtnl_list(const struct list_head *ops_list, ops->exit_rtnl(net, &dev_kill_list); } + unregister_netdevice_queue_many_net(net, &dev_kill_list); __rtnl_net_unlock(net); } @@ -422,6 +423,9 @@ static __net_init int preinit_net(struct net *net, struct user_namespace *user_n #ifdef CONFIG_DEBUG_NET_SMALL_RTNL mutex_init(&net->rtnl_mutex); lock_set_cmp_fn(&net->rtnl_mutex, rtnl_net_lock_cmp_fn, NULL); + INIT_WORK(&net->rtnl_work, rtnl_net_work_func); + INIT_LIST_HEAD(&net->dev_unreg_head); + spin_lock_init(&net->dev_unreg_lock); #endif INIT_LIST_HEAD(&net->ptype_all); diff --git a/net/core/netdev-genl-gen.c b/net/core/netdev-genl-gen.c index d18c89b5a6c7..f83790341eae 100644 --- a/net/core/netdev-genl-gen.c +++ b/net/core/netdev-genl-gen.c @@ -11,6 +11,7 @@ #include <uapi/linux/netdev.h> #include <net/netdev_netlink.h> +#include <asm/page.h> /* Integer value ranges */ static const struct netlink_range_validation netdev_a_page_pool_id_range = { @@ -27,6 +28,11 @@ static const struct netlink_range_validation netdev_a_napi_defer_hard_irqs_range .max = S32_MAX, }; +static const struct netlink_range_validation netdev_a_dmabuf_rx_page_size_range = { + .min = PAGE_SIZE, + .max = U32_MAX, +}; + /* Common nested types */ const struct nla_policy netdev_lease_nl_policy[NETDEV_A_LEASE_NETNS_ID + 1] = { [NETDEV_A_LEASE_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1), @@ -106,10 +112,11 @@ static const struct nla_policy netdev_qstats_get_nl_policy[NETDEV_A_QSTATS_SCOPE }; /* NETDEV_CMD_BIND_RX - do */ -static const struct nla_policy netdev_bind_rx_nl_policy[NETDEV_A_DMABUF_FD + 1] = { +static const struct nla_policy netdev_bind_rx_nl_policy[NETDEV_A_DMABUF_RX_PAGE_SIZE + 1] = { [NETDEV_A_DMABUF_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1), [NETDEV_A_DMABUF_FD] = { .type = NLA_U32, }, [NETDEV_A_DMABUF_QUEUES] = NLA_POLICY_NESTED(netdev_queue_id_nl_policy), + [NETDEV_A_DMABUF_RX_PAGE_SIZE] = NLA_POLICY_FULL_RANGE(NLA_U32, &netdev_a_dmabuf_rx_page_size_range), }; /* NETDEV_CMD_NAPI_SET - do */ @@ -219,7 +226,7 @@ static const struct genl_split_ops netdev_nl_ops[] = { .cmd = NETDEV_CMD_BIND_RX, .doit = netdev_nl_bind_rx_doit, .policy = netdev_bind_rx_nl_policy, - .maxattr = NETDEV_A_DMABUF_FD, + .maxattr = NETDEV_A_DMABUF_RX_PAGE_SIZE, .flags = GENL_UNS_ADMIN_PERM | GENL_CMD_CAP_DO, }, { diff --git a/net/core/netdev-genl-gen.h b/net/core/netdev-genl-gen.h index d71b435d72c1..3fae88e8f5c5 100644 --- a/net/core/netdev-genl-gen.h +++ b/net/core/netdev-genl-gen.h @@ -12,6 +12,7 @@ #include <uapi/linux/netdev.h> #include <net/netdev_netlink.h> +#include <asm/page.h> /* Common nested types */ extern const struct nla_policy netdev_lease_nl_policy[NETDEV_A_LEASE_NETNS_ID + 1]; diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c index c15d8d4ca1f8..cb18db681640 100644 --- a/net/core/netdev-genl.c +++ b/net/core/netdev-genl.c @@ -1013,6 +1013,7 @@ netdev_nl_get_dma_dev(struct net_device *netdev, unsigned long *rxq_bitmap, int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info) { struct net_devmem_dmabuf_binding *binding; + unsigned int niov_shift = PAGE_SHIFT; u32 ifindex, dmabuf_fd, rxq_idx; struct netdev_nl_sock *priv; struct net_device *netdev; @@ -1030,6 +1031,18 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info) ifindex = nla_get_u32(info->attrs[NETDEV_A_DEV_IFINDEX]); dmabuf_fd = nla_get_u32(info->attrs[NETDEV_A_DMABUF_FD]); + if (info->attrs[NETDEV_A_DMABUF_RX_PAGE_SIZE]) { + u32 rx_page_size = nla_get_u32(info->attrs[NETDEV_A_DMABUF_RX_PAGE_SIZE]); + + if (!is_power_of_2(rx_page_size)) { + NL_SET_ERR_MSG_ATTR(info->extack, + info->attrs[NETDEV_A_DMABUF_RX_PAGE_SIZE], + "rx-page-size must be a power of 2"); + return -EINVAL; + } + niov_shift = ilog2(rx_page_size); + } + priv = genl_sk_priv_get(&netdev_nl_family, NETLINK_CB(skb).sk); if (IS_ERR(priv)) return PTR_ERR(priv); @@ -1080,7 +1093,8 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info) } binding = net_devmem_bind_dmabuf(netdev, NULL, dma_dev, DMA_FROM_DEVICE, - dmabuf_fd, priv, info->extack); + dmabuf_fd, niov_shift, priv, + info->extack); if (IS_ERR(binding)) { err = PTR_ERR(binding); goto err_rxq_bitmap; @@ -1093,7 +1107,9 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info) goto err_unbind; } - nla_put_u32(rsp, NETDEV_A_DMABUF_ID, binding->id); + /* rsp was allocated large enough */ + WARN_ON_ONCE(nla_put_u32(rsp, NETDEV_A_DMABUF_ID, binding->id)); + genlmsg_end(rsp, hdr); err = genlmsg_reply(rsp, info); @@ -1221,13 +1237,15 @@ int netdev_nl_bind_tx_doit(struct sk_buff *skb, struct genl_info *info) binding = net_devmem_bind_dmabuf(bind_dev, bind_dev != netdev ? netdev : NULL, dma_dev, DMA_TO_DEVICE, dmabuf_fd, - priv, info->extack); + PAGE_SHIFT, priv, info->extack); if (IS_ERR(binding)) { err = PTR_ERR(binding); goto err_unlock_bind_dev; } - nla_put_u32(rsp, NETDEV_A_DMABUF_ID, binding->id); + /* rsp was allocated large enough */ + WARN_ON_ONCE(nla_put_u32(rsp, NETDEV_A_DMABUF_ID, binding->id)); + genlmsg_end(rsp, hdr); if (bind_dev != netdev) @@ -1394,7 +1412,9 @@ int netdev_nl_queue_create_doit(struct sk_buff *skb, struct genl_info *info) netdev_rx_queue_lease(rxq, rxq_lease); - nla_put_u32(rsp, NETDEV_A_QUEUE_ID, queue_id); + /* rsp was allocated large enough */ + WARN_ON_ONCE(nla_put_u32(rsp, NETDEV_A_QUEUE_ID, queue_id)); + genlmsg_end(rsp, hdr); netdev_unlock(dev_lease); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 96d5945e6a30..fe1e0cda5d6b 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -36,17 +36,22 @@ #include <trace/events/napi.h> #include <linux/kconfig.h> -/* - * We maintain a small pool of fully-sized skbs, to make sure the - * message gets out even in extreme OOM situations. - */ - -#define MAX_SKBS 32 #define USEC_PER_POLL 50 +/* + * carrier_timeout is netconsole-specific and only kept here to preserve the + * netpoll.carrier_timeout module-parameter ABI. Its value is exposed to + * netconsole through netpoll_get_carrier_timeout(). + */ static unsigned int carrier_timeout = 4; module_param(carrier_timeout, uint, 0644); +unsigned int netpoll_get_carrier_timeout(void) +{ + return carrier_timeout; +} +EXPORT_SYMBOL_GPL(netpoll_get_carrier_timeout); + static netdev_tx_t netpoll_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq) @@ -213,22 +218,6 @@ void netpoll_poll_enable(struct net_device *dev) up(&ni->dev_lock); } -static void refill_skbs(struct netpoll *np) -{ - struct sk_buff_head *skb_pool; - struct sk_buff *skb; - - skb_pool = &np->skb_pool; - - while (READ_ONCE(skb_pool->qlen) < MAX_SKBS) { - skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC); - if (!skb) - break; - - skb_queue_tail(skb_pool, skb); - } -} - void netpoll_zap_completion_queue(void) { unsigned long flags; @@ -351,32 +340,12 @@ netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) } EXPORT_SYMBOL(netpoll_send_skb); -static void skb_pool_flush(struct netpoll *np) -{ - struct sk_buff_head *skb_pool; - - cancel_work_sync(&np->refill_wq); - skb_pool = &np->skb_pool; - skb_queue_purge_reason(skb_pool, SKB_CONSUMED); -} - -static void refill_skbs_work_handler(struct work_struct *work) -{ - struct netpoll *np = - container_of(work, struct netpoll, refill_wq); - - refill_skbs(np); -} - int __netpoll_setup(struct netpoll *np, struct net_device *ndev) { struct netpoll_info *npinfo; const struct net_device_ops *ops; int err; - skb_queue_head_init(&np->skb_pool); - INIT_WORK(&np->refill_wq, refill_skbs_work_handler); - if (ndev->priv_flags & IFF_DISABLE_NETPOLL) { np_err(np, "%s doesn't support polling, aborting\n", ndev->name); @@ -411,9 +380,6 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) np->dev = ndev; strscpy(np->dev_name, ndev->name, IFNAMSIZ); - /* fill up the skb queue */ - refill_skbs(np); - /* last thing to do is link it to the net device structure */ rcu_assign_pointer(ndev->npinfo, npinfo); @@ -426,207 +392,6 @@ out: } EXPORT_SYMBOL_GPL(__netpoll_setup); -/* - * Returns a pointer to a string representation of the identifier used - * to select the egress interface for the given netpoll instance. buf - * is used to format np->dev_mac when np->dev_name is empty; bufsz must - * be at least MAC_ADDR_STR_LEN + 1 to fit the formatted MAC address - * and its NUL terminator. - */ -static char *egress_dev(struct netpoll *np, char *buf, size_t bufsz) -{ - if (np->dev_name[0]) - return np->dev_name; - - snprintf(buf, bufsz, "%pM", np->dev_mac); - return buf; -} - -static void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev, - unsigned int timeout) -{ - unsigned long atmost; - - atmost = jiffies + timeout * HZ; - while (!netif_carrier_ok(ndev)) { - if (time_after(jiffies, atmost)) { - np_notice(np, "timeout waiting for carrier\n"); - break; - } - msleep(1); - } -} - -/* - * Take the IPv6 from ndev and populate local_ip structure in netpoll - */ -static int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev) -{ - char buf[MAC_ADDR_STR_LEN + 1]; - int err = -EDESTADDRREQ; - struct inet6_dev *idev; - - if (!IS_ENABLED(CONFIG_IPV6)) { - np_err(np, "IPv6 is not supported %s, aborting\n", - egress_dev(np, buf, sizeof(buf))); - return -EINVAL; - } - - idev = __in6_dev_get(ndev); - if (idev) { - struct inet6_ifaddr *ifp; - - read_lock_bh(&idev->lock); - list_for_each_entry(ifp, &idev->addr_list, if_list) { - if (!!(ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) != - !!(ipv6_addr_type(&np->remote_ip.in6) & IPV6_ADDR_LINKLOCAL)) - continue; - /* Got the IP, let's return */ - np->local_ip.in6 = ifp->addr; - err = 0; - break; - } - read_unlock_bh(&idev->lock); - } - if (err) { - np_err(np, "no IPv6 address for %s, aborting\n", - egress_dev(np, buf, sizeof(buf))); - return err; - } - - np_info(np, "local IPv6 %pI6c\n", &np->local_ip.in6); - return 0; -} - -/* - * Take the IPv4 from ndev and populate local_ip structure in netpoll - */ -static int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev) -{ - char buf[MAC_ADDR_STR_LEN + 1]; - const struct in_ifaddr *ifa; - struct in_device *in_dev; - - in_dev = __in_dev_get_rtnl(ndev); - if (!in_dev) { - np_err(np, "no IP address for %s, aborting\n", - egress_dev(np, buf, sizeof(buf))); - return -EDESTADDRREQ; - } - - ifa = rtnl_dereference(in_dev->ifa_list); - if (!ifa) { - np_err(np, "no IP address for %s, aborting\n", - egress_dev(np, buf, sizeof(buf))); - return -EDESTADDRREQ; - } - - np->local_ip.ip = ifa->ifa_local; - np_info(np, "local IP %pI4\n", &np->local_ip.ip); - - return 0; -} - -/* - * Test whether the caller left np->local_ip unset, so that - * netpoll_setup() should auto-populate it from the egress device. - * - * np->local_ip is a union of __be32 (IPv4) and struct in6_addr (IPv6), - * so an IPv6 address whose first 4 bytes are zero (e.g. ::1, ::2, - * IPv4-mapped ::ffff:a.b.c.d) must not be tested via the IPv4 arm — - * doing so would misclassify a caller-supplied address as unset and - * silently overwrite it with whatever address the device exposes. - */ -static bool netpoll_local_ip_unset(const struct netpoll *np) -{ - if (np->ipv6) - return ipv6_addr_any(&np->local_ip.in6); - return !np->local_ip.ip; -} - -int netpoll_setup(struct netpoll *np) -{ - struct net *net = current->nsproxy->net_ns; - char buf[MAC_ADDR_STR_LEN + 1]; - struct net_device *ndev = NULL; - bool ip_overwritten = false; - int err; - - rtnl_lock(); - if (np->dev_name[0]) - ndev = __dev_get_by_name(net, np->dev_name); - else if (is_valid_ether_addr(np->dev_mac)) - ndev = dev_getbyhwaddr(net, ARPHRD_ETHER, np->dev_mac); - - if (!ndev) { - np_err(np, "%s doesn't exist, aborting\n", - egress_dev(np, buf, sizeof(buf))); - err = -ENODEV; - goto unlock; - } - netdev_hold(ndev, &np->dev_tracker, GFP_KERNEL); - - if (netdev_master_upper_dev_get(ndev)) { - np_err(np, "%s is a slave device, aborting\n", - egress_dev(np, buf, sizeof(buf))); - err = -EBUSY; - goto put; - } - - if (!netif_running(ndev)) { - np_info(np, "device %s not up yet, forcing it\n", - egress_dev(np, buf, sizeof(buf))); - - err = dev_open(ndev, NULL); - if (err) { - np_err(np, "failed to open %s\n", ndev->name); - goto put; - } - - rtnl_unlock(); - netpoll_wait_carrier(np, ndev, carrier_timeout); - rtnl_lock(); - } - - if (netpoll_local_ip_unset(np)) { - if (!np->ipv6) { - err = netpoll_take_ipv4(np, ndev); - if (err) - goto put; - } else { - err = netpoll_take_ipv6(np, ndev); - if (err) - goto put; - } - ip_overwritten = true; - } - - err = __netpoll_setup(np, ndev); - if (err) - goto flush; - rtnl_unlock(); - - /* Make sure all NAPI polls which started before dev->npinfo - * was visible have exited before we start calling NAPI poll. - * NAPI skips locking if dev->npinfo is NULL. - */ - synchronize_rcu(); - - return 0; - -flush: - skb_pool_flush(np); -put: - DEBUG_NET_WARN_ON_ONCE(np->dev); - if (ip_overwritten) - memset(&np->local_ip, 0, sizeof(np->local_ip)); - netdev_put(ndev, &np->dev_tracker); -unlock: - rtnl_unlock(); - return err; -} -EXPORT_SYMBOL(netpoll_setup); - static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) { struct netpoll_info *npinfo = @@ -659,8 +424,6 @@ static void __netpoll_cleanup(struct netpoll *np) disable_delayed_work_sync(&npinfo->tx_work); call_rcu(&npinfo->rcu, rcu_cleanup_netpoll_info); } - - skb_pool_flush(np); } void __netpoll_free(struct netpoll *np) diff --git a/net/core/page_pool.c b/net/core/page_pool.c index 21dc4a9c8714..50ee550fef73 100644 --- a/net/core/page_pool.c +++ b/net/core/page_pool.c @@ -500,29 +500,40 @@ out: return err; } -static int page_pool_release_dma_index(struct page_pool *pool, - netmem_ref netmem) +static void __page_pool_unmap_netmem_dma(struct page_pool *pool, + netmem_ref netmem) { struct page *old, *page = netmem_to_page(netmem); unsigned long id; + dma_addr_t dma; - if (unlikely(!PP_DMA_INDEX_BITS)) - return 0; - - id = netmem_get_dma_index(netmem); - if (!id) - return -1; + if (!pool->dma_map) + return; - if (in_softirq()) - old = xa_cmpxchg(&pool->dma_mapped, id, page, NULL, 0); - else - old = xa_cmpxchg_bh(&pool->dma_mapped, id, page, NULL, 0); - if (old != page) - return -1; + /* Cache dma_addr before xa_cmpxchg. The scrub path holds no page ref; + * the unref path calls put_page() regardless of cmpxchg outcome, so + * after the cmpxchg we cannot safely touch netmem fields. + */ + dma = page_pool_get_dma_addr_netmem(netmem); - netmem_set_dma_index(netmem, 0); + if (likely(PP_DMA_INDEX_BITS)) { + id = netmem_get_dma_index(netmem); + if (!id) + return; + + if (in_softirq()) + old = xa_cmpxchg(&pool->dma_mapped, + id, page, NULL, 0); + else + old = xa_cmpxchg_bh(&pool->dma_mapped, + id, page, NULL, 0); + if (old != page) + return; + } - return 0; + dma_unmap_page_attrs(pool->p.dev, dma, + PAGE_SIZE << pool->p.order, pool->p.dma_dir, + DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING); } static bool page_pool_dma_map(struct page_pool *pool, netmem_ref netmem, gfp_t gfp) @@ -728,24 +739,16 @@ void page_pool_clear_pp_info(netmem_ref netmem) static __always_inline void __page_pool_release_netmem_dma(struct page_pool *pool, netmem_ref netmem) { - dma_addr_t dma; - + /* Caller must hold a page ref: __page_pool_unmap_netmem_dma() is + * safe without a ref, but the field clears below require it. + */ if (!pool->dma_map) - /* Always account for inflight pages, even if we didn't - * map them - */ return; - if (page_pool_release_dma_index(pool, netmem)) - return; - - dma = page_pool_get_dma_addr_netmem(netmem); - - /* When page is unmapped, it cannot be returned to our pool */ - dma_unmap_page_attrs(pool->p.dev, dma, - PAGE_SIZE << pool->p.order, pool->p.dma_dir, - DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING); + __page_pool_unmap_netmem_dma(pool, netmem); page_pool_set_dma_addr_netmem(netmem, 0); + if (likely(PP_DMA_INDEX_BITS)) + netmem_set_dma_index(netmem, 0); } /* Disconnects a page (from a page_pool). API users can have a need @@ -1171,8 +1174,9 @@ static void page_pool_scrub(struct page_pool *pool) synchronize_net(); } + /* No page ref, dma-unmap only. */ xa_for_each(&pool->dma_mapped, id, ptr) - __page_pool_release_netmem_dma(pool, page_to_netmem((struct page *)ptr)); + __page_pool_unmap_netmem_dma(pool, page_to_netmem((struct page *)ptr)); } /* No more consumers should exist, but producers could still diff --git a/net/core/pktgen.c b/net/core/pktgen.c index ee64f3012321..7f81aed46672 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -566,6 +566,7 @@ static const struct proc_ops pktgen_proc_ops = { static int pktgen_if_show(struct seq_file *seq, void *v) { const struct pktgen_dev *pkt_dev = seq->private; + unsigned int cflows = READ_ONCE(pkt_dev->cflows); ktime_t stopped; unsigned int i; u64 idle; @@ -590,7 +591,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v) pkt_dev->nfrags, (unsigned long long) pkt_dev->delay, pkt_dev->clone_skb, pkt_dev->odevname); - seq_printf(seq, " flows: %u flowlen: %u\n", pkt_dev->cflows, + seq_printf(seq, " flows: %u flowlen: %u\n", cflows, pkt_dev->lflow); seq_printf(seq, @@ -675,7 +676,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v) for (i = 0; i < NR_PKT_FLAGS; i++) { if (i == FLOW_SEQ_SHIFT) - if (!pkt_dev->cflows) + if (!cflows) continue; if (pkt_dev->flags & (1 << i)) { @@ -1632,8 +1633,8 @@ static ssize_t pktgen_if_write(struct file *file, if (value > MAX_CFLOWS) value = MAX_CFLOWS; - pkt_dev->cflows = value; - sprintf(pg_result, "OK: flows=%u", pkt_dev->cflows); + WRITE_ONCE(pkt_dev->cflows, value); + sprintf(pg_result, "OK: flows=%u", (unsigned int)value); return count; } #ifdef CONFIG_XFRM @@ -2373,7 +2374,7 @@ static inline int f_seen(const struct pktgen_dev *pkt_dev, int flow) return !!(pkt_dev->flows[flow].flags & F_INIT); } -static inline int f_pick(struct pktgen_dev *pkt_dev) +static inline int f_pick(struct pktgen_dev *pkt_dev, unsigned int cflows) { int flow = pkt_dev->curfl; @@ -2383,11 +2384,11 @@ static inline int f_pick(struct pktgen_dev *pkt_dev) pkt_dev->flows[flow].count = 0; pkt_dev->flows[flow].flags = 0; pkt_dev->curfl += 1; - if (pkt_dev->curfl >= pkt_dev->cflows) + if (pkt_dev->curfl >= cflows) pkt_dev->curfl = 0; /*reset */ } } else { - flow = get_random_u32_below(pkt_dev->cflows); + flow = get_random_u32_below(cflows); pkt_dev->curfl = flow; if (pkt_dev->flows[flow].count > pkt_dev->lflow) { @@ -2461,12 +2462,14 @@ static void set_cur_queue_map(struct pktgen_dev *pkt_dev) */ static void mod_cur_headers(struct pktgen_dev *pkt_dev) { + unsigned int cflows; __u32 imn; __u32 imx; int flow = 0; - if (pkt_dev->cflows) - flow = f_pick(pkt_dev); + cflows = READ_ONCE(pkt_dev->cflows); + if (cflows) + flow = f_pick(pkt_dev, cflows); /* Deal with source MAC */ if (pkt_dev->src_mac_count > 1) { @@ -2582,7 +2585,7 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev) pkt_dev->cur_saddr = htonl(t); } - if (pkt_dev->cflows && f_seen(pkt_dev, flow)) { + if (cflows && f_seen(pkt_dev, flow)) { pkt_dev->cur_daddr = pkt_dev->flows[flow].cur_daddr; } else { imn = ntohl(pkt_dev->daddr_min); @@ -2611,7 +2614,7 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev) pkt_dev->cur_daddr = htonl(t); } } - if (pkt_dev->cflows) { + if (cflows) { pkt_dev->flows[flow].flags |= F_INIT; pkt_dev->flows[flow].cur_daddr = pkt_dev->cur_daddr; @@ -3005,7 +3008,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, udph->source = htons(pkt_dev->cur_udp_src); udph->dest = htons(pkt_dev->cur_udp_dst); - udph->len = htons(datalen + 8); /* DATA + udphdr */ + udp_set_len_short(udph, datalen + 8); /* DATA + udphdr */ udph->check = 0; iph->ihl = 5; @@ -3138,7 +3141,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, udplen = datalen + sizeof(struct udphdr); udph->source = htons(pkt_dev->cur_udp_src); udph->dest = htons(pkt_dev->cur_udp_dst); - udph->len = htons(udplen); + udp_set_len_short(udph, udplen); udph->check = 0; *(__be32 *) iph = htonl(0x60000000); /* Version + flow */ diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 12aa3aa1688b..81c5a6104dea 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -197,6 +197,7 @@ void __rtnl_net_unlock(struct net *net) { ASSERT_RTNL(); + unregister_netdevice_many_net(net); mutex_unlock(&net->rtnl_mutex); } EXPORT_SYMBOL(__rtnl_net_unlock); @@ -273,6 +274,29 @@ bool lockdep_rtnl_net_is_held(struct net *net) return lockdep_rtnl_is_held() && lockdep_is_held(&net->rtnl_mutex); } EXPORT_SYMBOL(lockdep_rtnl_net_is_held); + +static struct workqueue_struct *rtnl_net_wq; + +void rtnl_net_queue_work(struct net *net) +{ + queue_work(rtnl_net_wq, &net->rtnl_work); +} + +void rtnl_net_flush_workqueue(void) +{ + flush_workqueue(rtnl_net_wq); +} + +void rtnl_net_work_func(struct work_struct *work) +{ + struct net *net = container_of(work, struct net, rtnl_work); + + if (list_empty(&net->dev_unreg_head)) + return; + + rtnl_net_lock(net); + rtnl_net_unlock(net); +} #else static int rtnl_net_cmp_locks(const struct net *net_a, const struct net *net_b) { @@ -282,10 +306,11 @@ static int rtnl_net_cmp_locks(const struct net *net_a, const struct net *net_b) #endif struct rtnl_nets { - /* ->newlink() needs to freeze 3 netns at most; - * 2 for the new device, 1 for its peer. + /* ->newlink() needs to freeze 4 netns at most; + * 2 for the new device, 1 for its peer, 1 for + * an existing device (do_setlink() path). */ - struct net *net[3]; + struct net *net[4]; unsigned char len; }; @@ -636,16 +661,15 @@ unlock: } EXPORT_SYMBOL_GPL(rtnl_link_register); -static void __rtnl_kill_links(struct net *net, struct rtnl_link_ops *ops) +static void __rtnl_kill_links(struct net *net, struct rtnl_link_ops *ops, + struct list_head *dev_kill_list) { struct net_device *dev; - LIST_HEAD(list_kill); for_each_netdev(net, dev) { if (dev->rtnl_link_ops == ops) - ops->dellink(dev, &list_kill); + ops->dellink(dev, dev_kill_list); } - unregister_netdevice_many(&list_kill); } /* Return with the rtnl_lock held when there are no network @@ -676,6 +700,7 @@ static void rtnl_lock_unregistering_all(void) */ void rtnl_link_unregister(struct rtnl_link_ops *ops) { + LIST_HEAD(dev_kill_list); struct net *net; mutex_lock(&link_ops_mutex); @@ -689,8 +714,14 @@ void rtnl_link_unregister(struct rtnl_link_ops *ops) down_write(&pernet_ops_rwsem); rtnl_lock_unregistering_all(); - for_each_net(net) - __rtnl_kill_links(net, ops); + for_each_net(net) { + __rtnl_net_lock(net); + __rtnl_kill_links(net, ops, &dev_kill_list); + unregister_netdevice_queue_many_net(net, &dev_kill_list); + __rtnl_net_unlock(net); + } + + unregister_netdevice_many(&dev_kill_list); rtnl_unlock(); up_write(&pernet_ops_rwsem); @@ -1143,12 +1174,27 @@ static void copy_rtnl_link_stats(struct rtnl_link_stats *a, a->rx_nohandler = b->rx_nohandler; } +/* Cap the number of VFs that IFLA_VFINFO_LIST describes. The nest is one + * netlink attribute, so everything inside it has to fit in the u16 nla_len. + * The cap is a fixed number rather than whatever happens to fit, so that the + * limit is a property of the interface instead of one of the requested + * attribute set and the host's alignment requirements. The largest per-VF + * encoding is 368 bytes with statistics and GUIDs and 236 bytes without + * statistics, so both values keep the nest well inside U16_MAX. + */ +static int rtnl_vfinfo_cap(int num_vfs, u32 ext_filter_mask) +{ + return min(num_vfs, + ext_filter_mask & RTEXT_FILTER_SKIP_STATS ? 256 : 128); +} + /* All VF info */ static inline int rtnl_vfinfo_size(const struct net_device *dev, u32 ext_filter_mask) { if (dev->dev.parent && (ext_filter_mask & RTEXT_FILTER_VF)) { - int num_vfs = dev_num_vf(dev->dev.parent); + int num_vfs = rtnl_vfinfo_cap(dev_num_vf(dev->dev.parent), + ext_filter_mask); size_t size = nla_total_size(0); size += num_vfs * (nla_total_size(0) + @@ -1686,6 +1732,11 @@ static noinline_for_stack int rtnl_fill_vf(struct sk_buff *skb, if (!vfinfo) return -EMSGSIZE; + /* IFLA_NUM_VF above stays the device's VF count; the list itself is + * capped so that its length cannot overflow nla_len. + */ + num_vfs = rtnl_vfinfo_cap(num_vfs, ext_filter_mask); + for (i = 0; i < num_vfs; i++) { if (rtnl_fill_vfinfo(skb, dev, i, ext_filter_mask)) { nla_nest_cancel(skb, vfinfo); @@ -3660,14 +3711,16 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm, u32 portid, const struct nlmsghdr *nlh) { unsigned int old_flags, changed; - int err; + int err = 0; + + netdev_lock_ops(dev); old_flags = dev->flags; if (ifm && (ifm->ifi_flags || ifm->ifi_change)) { err = __dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm), NULL); if (err < 0) - return err; + goto out; } changed = old_flags ^ dev->flags; @@ -3677,7 +3730,10 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm, } __dev_notify_flags(dev, old_flags, changed, portid, nlh); - return 0; + +out: + netdev_unlock_ops(dev); + return err; } EXPORT_SYMBOL(rtnl_configure_link); @@ -3918,22 +3974,20 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm, goto out; } - netdev_lock_ops(dev); - err = rtnl_configure_link(dev, ifm, portid, nlh); if (err < 0) goto out_unregister; if (tb[IFLA_MASTER]) { + netdev_lock_ops(dev); err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]), extack); + netdev_unlock_ops(dev); if (err) goto out_unregister; } - netdev_unlock_ops(dev); out: return err; out_unregister: - netdev_unlock_ops(dev); if (ops->newlink) { LIST_HEAD(list_kill); @@ -4155,6 +4209,8 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, } } + rtnl_nets_add(&rtnl_nets, get_net(sock_net(skb->sk))); + rtnl_nets_lock(&rtnl_nets); ret = __rtnl_newlink(skb, nlh, ops, tgt_net, link_net, peer_net, tbs, data, extack); rtnl_nets_unlock(&rtnl_nets); @@ -7221,4 +7277,10 @@ void __init rtnetlink_init(void) register_netdevice_notifier(&rtnetlink_dev_notifier); rtnl_register_many(rtnetlink_rtnl_msg_handlers); + +#ifdef CONFIG_DEBUG_NET_SMALL_RTNL + rtnl_net_wq = create_workqueue("rtnl_net"); + if (!rtnl_net_wq) + panic("Could not create rtnl_net workq"); +#endif } diff --git a/net/core/scm.c b/net/core/scm.c index eec13f50ecaf..f0d44ecdb11f 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -351,7 +351,31 @@ static int scm_max_fds(struct msghdr *msg) return (msg->msg_controllen - sizeof(struct cmsghdr)) / sizeof(int); } -void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) +int scm_recv_one_fd(struct file *f, int __user *ufd, unsigned int flags, + bool notrunc) +{ + int error; + + if (!ufd) + return -EFAULT; + + error = security_file_receive(f); + if (error) + return notrunc ? put_user(error, ufd) : error; + + FD_PREPARE(fdf, flags, get_file(f)); + if (fdf.err) + return fdf.err; + + error = put_user(fd_prepare_fd(fdf), ufd); + if (error) + return error; + + __receive_sock(fd_prepare_file(fdf)); + return fd_publish(fdf); +} + +void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm, bool notrunc) { struct cmsghdr __user *cm = (__force struct cmsghdr __user *)msg->msg_control_user; @@ -365,12 +389,12 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) return; if (msg->msg_flags & MSG_CMSG_COMPAT) { - scm_detach_fds_compat(msg, scm); + scm_detach_fds_compat(msg, scm, notrunc); return; } for (i = 0; i < fdmax; i++) { - err = scm_recv_one_fd(scm->fp->fp[i], cmsg_data + i, o_flags); + err = scm_recv_one_fd(scm->fp->fp[i], cmsg_data + i, o_flags, notrunc); if (err < 0) break; } @@ -523,9 +547,6 @@ static bool __scm_recv_common(struct sock *sk, struct msghdr *msg, scm_passec(sk, msg, scm); - if (scm->fp) - scm_detach_fds(msg, scm); - return true; } @@ -545,6 +566,13 @@ void scm_recv_unix(struct socket *sock, struct msghdr *msg, if (!__scm_recv_common(sock->sk, msg, scm, flags)) return; + if (scm->fp) { + struct unix_sock *u; + + u = unix_sk(sock->sk); + scm_detach_fds(msg, scm, READ_ONCE(u->scm_rights_notrunc)); + } + if (sock->sk->sk_scm_pidfd) scm_pidfd_recv(msg, scm); diff --git a/net/core/selftests.c b/net/core/selftests.c index 0a203d3fb9dc..36b949ae520b 100644 --- a/net/core/selftests.c +++ b/net/core/selftests.c @@ -72,9 +72,9 @@ struct sk_buff *net_test_get_skb(struct net_device *ndev, u8 id, } else { uhdr->source = htons(attr->sport); uhdr->dest = htons(attr->dport); - uhdr->len = htons(sizeof(*shdr) + sizeof(*uhdr) + attr->size); + udp_set_len_short(uhdr, sizeof(*shdr) + sizeof(*uhdr) + attr->size); if (attr->max_size) - uhdr->len = htons(attr->max_size - + udp_set_len_short(uhdr, attr->max_size - (sizeof(*ihdr) + sizeof(*ehdr))); uhdr->check = 0; } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ba3dbac80fb4..c82a1472a5ea 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2332,7 +2332,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, if (skb_orphan_frags(skb, gfp_mask)) goto nofrags; if (skb_zcopy(skb)) - refcount_inc(&skb_uarg(skb)->refcnt); + net_zcopy_get(skb_uarg(skb)); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) skb_frag_ref(skb, i); @@ -4781,6 +4781,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, struct sk_buff *tail = NULL; struct sk_buff *list_skb = skb_shinfo(head_skb)->frag_list; unsigned int mss = skb_shinfo(head_skb)->gso_size; + bool gso_by_frags = mss == GSO_BY_FRAGS; unsigned int doffset = head_skb->data - skb_mac_header(head_skb); unsigned int offset = doffset; unsigned int tnl_hlen = skb_tnl_header_len(head_skb); @@ -4796,7 +4797,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, int nfrags, pos; if ((skb_shinfo(head_skb)->gso_type & SKB_GSO_DODGY) && - mss != GSO_BY_FRAGS && mss != skb_headlen(head_skb)) { + !gso_by_frags && mss != skb_headlen(head_skb)) { struct sk_buff *check_skb; for (check_skb = list_skb; check_skb; check_skb = check_skb->next) { @@ -4824,7 +4825,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, sg = !!(features & NETIF_F_SG); csum = !!can_checksum_protocol(features, proto); - if (sg && csum && (mss != GSO_BY_FRAGS)) { + if (sg && csum && !gso_by_frags) { if (!(features & NETIF_F_GSO_PARTIAL)) { struct sk_buff *iter; unsigned int frag_len; @@ -4858,9 +4859,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, /* GSO partial only requires that we trim off any excess that * doesn't fit into an MSS sized block, so take care of that * now. - * Cap len to not accidentally hit GSO_BY_FRAGS. */ - partial_segs = min(len, GSO_BY_FRAGS - 1) / mss; + partial_segs = len / mss; if (partial_segs > 1) mss *= partial_segs; else @@ -4884,7 +4884,7 @@ normal: int hsize; int size; - if (unlikely(mss == GSO_BY_FRAGS)) { + if (unlikely(gso_by_frags)) { len = list_skb->len; } else { len = head_skb->len - offset; @@ -6848,7 +6848,7 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off, return -ENOMEM; } if (skb_zcopy(skb)) - net_zcopy_get(skb_zcopy(skb)); + net_zcopy_get(skb_uarg(skb)); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) skb_frag_ref(skb, i); if (skb_has_frag_list(skb)) @@ -6998,7 +6998,7 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off, return -ENOMEM; } if (skb_zcopy(skb)) - net_zcopy_get(skb_zcopy(skb)); + net_zcopy_get(skb_uarg(skb)); skb_release_data(skb, SKB_CONSUMED); skb->head = data; diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index b508618bfc12..eb35da3556f4 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -678,7 +678,7 @@ static struct ctl_table net_core_table[] = { }, }; -static struct ctl_table netns_core_table[] = { +static const struct ctl_table netns_core_table[] = { #if IS_ENABLED(CONFIG_RPS) { .procname = "rps_default_mask", @@ -787,26 +787,38 @@ static int __init fb_tunnels_only_for_init_net_sysctl_setup(char *str) } __setup("fb_tunnels=", fb_tunnels_only_for_init_net_sysctl_setup); -static __net_init int sysctl_core_net_init(struct net *net) +static const struct ctl_table *netns_core_table_dup(struct net *net) { size_t table_size = ARRAY_SIZE(netns_core_table); struct ctl_table *tbl; + int i; + + tbl = kmemdup(netns_core_table, sizeof(netns_core_table), GFP_KERNEL); + if (!tbl) + return NULL; + + for (i = 0; i < table_size; ++i) { + if (tbl[i].data == &sysctl_wmem_max) + break; + + tbl[i].data += (char *)net - (char *)&init_net; + } + for (; i < table_size; ++i) + tbl[i].mode &= ~0222; + + return tbl; +} + +static __net_init int sysctl_core_net_init(struct net *net) +{ + size_t table_size = ARRAY_SIZE(netns_core_table); + const struct ctl_table *tbl; tbl = netns_core_table; if (!net_eq(net, &init_net)) { - int i; - tbl = kmemdup(tbl, sizeof(netns_core_table), GFP_KERNEL); + tbl = netns_core_table_dup(net); if (tbl == NULL) goto err_dup; - - for (i = 0; i < table_size; ++i) { - if (tbl[i].data == &sysctl_wmem_max) - break; - - tbl[i].data += (char *)net - (char *)&init_net; - } - for (; i < table_size; ++i) - tbl[i].mode &= ~0222; } net->core.sysctl_hdr = register_net_sysctl_sz(net, "net/core", tbl, table_size); diff --git a/net/core/tso.c b/net/core/tso.c index 347b3856ddb9..d2934bcfa795 100644 --- a/net/core/tso.c +++ b/net/core/tso.c @@ -39,7 +39,8 @@ void tso_build_hdr(const struct sk_buff *skb, char *hdr, struct tso_t *tso, } else { struct udphdr *uh = (struct udphdr *)hdr; - uh->len = htons(sizeof(*uh) + size); + /* size is after segmentation. */ + udp_set_len_short(uh, sizeof(*uh) + size); } } EXPORT_SYMBOL(tso_build_hdr); |
