* Linux 6.12.104
@ 2026-08-19 16:52 Greg Kroah-Hartman
2026-08-19 16:52 ` Greg Kroah-Hartman
0 siblings, 1 reply; 2+ messages in thread
From: Greg Kroah-Hartman @ 2026-08-19 16:52 UTC (permalink / raw)
To: linux-kernel, akpm, torvalds, stable; +Cc: lwn, jslaby, Greg Kroah-Hartman
I'm announcing the release of the 6.12.104 kernel.
All users of the 6.12 kernel series must upgrade.
The updated 6.12.y git tree can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git linux-6.12.y
and can be browsed at the normal kernel.org git web browser:
https://git.kernel.org/?p=linux/kernel/git/stable/linux-stable.git;a=summary
thanks,
greg k-h
------------
Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml | 16
MAINTAINERS | 5
Makefile | 2
arch/arm/boot/dts/broadcom/bcm-ns.dtsi | 2
arch/arm/mach-npcm/platsmp.c | 2
arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 2
arch/s390/kvm/pci.c | 86 ++
arch/s390/kvm/pci.h | 2
arch/x86/kvm/mmu/mmu.c | 3
arch/x86/kvm/svm/sev.c | 12
block/blk-mq.c | 38 -
drivers/ata/pata_sl82c105.c | 4
drivers/counter/microchip-tcb-capture.c | 4
drivers/crypto/ccp/sev-dev.c | 17
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 35 -
drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c | 24
drivers/gpu/drm/bridge/parade-ps8640.c | 27
drivers/hwmon/ads7828.c | 11
drivers/hwmon/corsair-psu.c | 4
drivers/hwmon/ltc4282.c | 20
drivers/hwmon/nzxt-smart2.c | 6
drivers/hwmon/pmbus/lm25066.c | 5
drivers/input/evdev.c | 27
drivers/misc/fastrpc.c | 19
drivers/misc/mei/client.c | 25
drivers/mtd/nand/spi/core.c | 74 ++
drivers/net/bonding/bond_alb.c | 10
drivers/net/ethernet/amd/pds_core/core.h | 1
drivers/net/ethernet/amd/pds_core/main.c | 14
drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 51 +
drivers/net/ethernet/aquantia/atlantic/aq_ring.h | 1
drivers/net/ethernet/aquantia/atlantic/aq_vec.c | 2
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 50 +
drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1
drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c | 7
drivers/net/ethernet/cisco/enic/enic_main.c | 5
drivers/net/ethernet/freescale/fec_main.c | 11
drivers/net/ethernet/hisilicon/hix5hd2_gmac.c | 1
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 6
drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c | 6
drivers/net/ethernet/marvell/prestera/prestera_pci.c | 3
drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c | 16
drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h | 1
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 4
drivers/net/thunderbolt/main.c | 33 -
drivers/net/usb/ax88179_178a.c | 4
drivers/net/usb/ipheth.c | 11
drivers/net/veth.c | 22
drivers/net/vxlan/vxlan_core.c | 2
drivers/nvmem/core.c | 24
drivers/nvmem/internals.h | 2
drivers/nvmem/layouts.c | 11
drivers/nvmem/layouts/Makefile | 1
drivers/nvmem/layouts/fixed-layout.c | 58 ++
drivers/pinctrl/renesas/pinctrl-rzg2l.c | 4
drivers/ptp/ptp_ocp.c | 4
drivers/s390/crypto/zcrypt_ccamisc.c | 20
drivers/scsi/scsi_debug.c | 4
drivers/spi/spi-fsl-dspi.c | 13
drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 3
drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 2
drivers/staging/rtl8723bs/core/rtw_wlan_util.c | 3
drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 4
drivers/thermal/thermal_hwmon.c | 3
drivers/thunderbolt/eeprom.c | 9
drivers/thunderbolt/icm.c | 2
drivers/thunderbolt/tb.c | 2
drivers/tty/serial/8250/8250_dma.c | 12
drivers/tty/serial/qcom_geni_serial.c | 43 -
drivers/tty/vt/keyboard.c | 5
drivers/tty/vt/vt_ioctl.c | 2
drivers/usb/atm/cxacru.c | 2
drivers/usb/cdns3/cdnsp-gadget.c | 4
drivers/usb/gadget/function/f_ncm.c | 2
drivers/vdpa/mlx5/core/mr.c | 3
drivers/vhost/vdpa.c | 9
drivers/vhost/vhost.c | 8
drivers/video/fbdev/core/bitblit.c | 9
drivers/watchdog/bd96801_wdt.c | 5
fs/binfmt_misc.c | 3
fs/btrfs/inode.c | 1
fs/crypto/policy.c | 2
fs/namespace.c | 5
fs/nfs/nfs4proc.c | 5
fs/overlayfs/super.c | 3
fs/smb/client/sess.c | 2
fs/tracefs/event_inode.c | 4
fs/verity/measure.c | 15
include/linux/netdevice.h | 11
include/linux/netfilter/ipset/ip_set.h | 2
include/linux/nvmem-provider.h | 6
include/linux/psp-sev.h | 3
include/net/act_api.h | 19
include/net/addrconf.h | 4
include/net/ip_vs.h | 41 -
include/net/route.h | 2
include/net/sch_generic.h | 1
kernel/bpf/verifier.c | 15
kernel/futex/core.c | 85 ++
kernel/sched/psi.c | 6
kernel/trace/ring_buffer.c | 4
kernel/trace/trace_events.c | 2
lib/fortify_kunit.c | 139 ++--
mm/huge_memory.c | 43 +
mm/swapfile.c | 2
mm/vmscan.c | 25
net/atm/common.c | 14
net/bridge/br_mrp.c | 6
net/bridge/netfilter/ebt_nflog.c | 17
net/bridge/netfilter/nf_conntrack_bridge.c | 1
net/core/dev.c | 10
net/core/sock.c | 1
net/core/xdp.c | 2
net/devlink/dev.c | 1
net/ipv4/fib_semantics.c | 101 ++-
net/ipv4/inet_connection_sock.c | 20
net/ipv4/route.c | 29 +
net/ipv4/tcp_bpf.c | 2
net/ipv4/tcp_input.c | 2
net/ipv4/tcp_ipv4.c | 289 +++++++---
net/ipv4/udp_offload.c | 4
net/ipv6/ip6_tunnel.c | 3
net/ipv6/route.c | 8
net/mac802154/scan.c | 4
net/ncsi/ncsi-netlink.c | 4
net/netfilter/ipset/ip_set_bitmap_gen.h | 4
net/netfilter/ipset/ip_set_core.c | 6
net/netfilter/ipset/ip_set_hash_gen.h | 2
net/netfilter/ipset/ip_set_list_set.c | 4
net/netfilter/ipvs/ip_vs_conn.c | 41 -
net/netfilter/ipvs/ip_vs_core.c | 68 +-
net/netfilter/ipvs/ip_vs_ctl.c | 77 ++
net/netfilter/ipvs/ip_vs_est.c | 10
net/netfilter/ipvs/ip_vs_lc.c | 4
net/netfilter/ipvs/ip_vs_proto_sctp.c | 4
net/netfilter/ipvs/ip_vs_proto_tcp.c | 2
net/netfilter/ipvs/ip_vs_sync.c | 7
net/netfilter/ipvs/ip_vs_xmit.c | 2
net/netfilter/nf_conntrack_proto.c | 6
net/netfilter/nf_conntrack_proto_sctp.c | 12
net/netfilter/nf_conntrack_proto_tcp.c | 132 +++-
net/openvswitch/datapath.c | 29 -
net/openvswitch/flow.c | 11
net/packet/af_packet.c | 72 +-
net/sched/act_ct.c | 29 -
net/sched/act_gact.c | 5
net/sched/act_police.c | 6
net/sched/cls_api.c | 17
net/sched/cls_route.c | 35 -
net/sched/sch_api.c | 9
net/sched/sch_cake.c | 1
net/sctp/associola.c | 11
net/sctp/outqueue.c | 1
net/sctp/sm_make_chunk.c | 3
net/sctp/sm_statefuns.c | 6
net/smc/af_smc.c | 3
net/smc/smc_llc.c | 3
net/smc/smc_rx.c | 19
net/tipc/node.c | 15
net/tls/tls_sw.c | 16
net/vmw_vsock/virtio_transport.c | 14
net/xdp/xdp_umem.c | 2
security/integrity/ima/ima_appraise.c | 9
security/integrity/ima/ima_main.c | 39 +
sound/usb/endpoint.c | 6
sound/usb/midi2.c | 9
sound/usb/usx2y/usX2Yhwdep.c | 2
sound/usb/usx2y/usx2yhwdeppcm.c | 2
tools/testing/selftests/bpf/prog_tests/sockmap_basic.c | 6
tools/testing/selftests/bpf/prog_tests/sockmap_listen.c | 21
tools/testing/selftests/bpf/test_maps.c | 13
tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc | 16
173 files changed, 1920 insertions(+), 868 deletions(-)
Ahmad Byagowi (1):
ptp: ocp: Fix board ID over-read
Al Viro (1):
mount: honour SB_NOUSER in the new mount API
Aleksandr Nogikh (1):
usb: atm: cxacru: properly kill rcv_urb on error in cxacru_cm()
Alexander Usyskin (1):
mei: pull kvfree out of spinlock
Anandu Krishnan E (1):
misc: fastrpc: fix channel ctx ref leak when session alloc fails
Anna Schumaker (1):
NFS: Pin the 'struct nfs_server' during a FREE_STATEID call
Asad Kamal (1):
Revert "drm/amdgpu: fix aperture mapping leak"
Ashish Kalra (3):
crypto: ccp - Add new SEV/SNP platform shutdown API
KVM: SVM: Add support to initialize SEV/SNP functionality in KVM
crypto: ccp - Abort doing SEV INIT if SNP INIT fails
Babanpreet Singh (1):
counter: microchip-tcb-capture: Fix DT channel validation
Bart Van Assche (1):
block: Reorder the request allocation code in blk_mq_submit_bio()
Baul Lee (5):
ALSA: usb-audio: fix OOB write on Type II inbound URBs
net: bridge: mrp: fix uninitialised bytes on the wire
ALSA: usx2y: bound the hwdep mmap fault offset
vxlan: do not arm the ageing timer on a device that is down
sctp: keep chunk->transport in step with the list it is queued on
Bryam Vargas (1):
thunderbolt: Bound the DROM dual link port number before indexing sw->ports
Cen Zhang (Microsoft) (1):
net/openvswitch: check Ethernet header length in key_extract()
Chengfeng Ye (3):
netfilter: ebt_nflog: pin the NFLOG backend
ipv4: fix use-after-free in fib_nhc_update_mtu()
bpf, sockmap: Fix sk_redir use-after-free in send verdict
Christian Borntraeger (1):
vdpa/mlx5: Fix buffer length in create_direct_keys()
Christian Brauner (2):
ovl: don't warn when the mount is completed from another user namespace
binfmt_misc: don't warn when the mount is completed from another user namespace
Claudiu Beznea (1):
pinctrl: renesas: rzg2l: Use -ENOTSUPP instead of -EOPNOTSUPP
Cunhao Lu (1):
serial: 8250_dma: Clear stale RX state on shutdown
Daming Li (1):
net: smc: fix splice entry lifetime imbalance in smc_rx_splice
Daniel Drake (1):
arm64: dts: broadcom: bcm2712: Remove non-functional EL2 virtual timer
Dmitry Antipov (1):
btrfs: fix memory leak in btrfs_do_encoded_write()
Dmitry Torokhov (2):
Input: evdev - sanitize event type index when fetching event masks
Input: evdev - fix information leak in evdev_pass_values()
Doruk Tan Ozturk (2):
net: usb: ipheth: fix carrier_work UAF on disconnect
net/packet: reset the MAC header on the packet-socket transmit path
Eddie Lin (1):
misc: fastrpc: fix memory leak in fastrpc_channel_ctx_free
Ekansh Gupta (1):
misc: fastrpc: Remove buffer from list prior to unmap operation
Eric Biggers (2):
fsverity: Fix bpf_get_fsverity_digest() dynptr assumptions
fsverity: Fix silent truncation in bpf_get_fsverity_digest()
Eric Dumazet (2):
net: remove WARN_ON_ONCE() from sk_mc_loop()
net/atm: fix slab-out-of-bounds read in vcc_setsockopt()
Fan XinRan (1):
net: thunderbolt: Tear down DMA paths before stopping the rings
Fan Ye (1):
Revert "net: thunderbolt: Enable end-to-end flow control also in transmit"
Farhan Ali (3):
KVM: s390: pci: Fix memory accounting for pinned/unpinned pages
KVM: s390: pci: Fix missing error codes and memory unaccounting
KVM: s390: pci: Fix resource leak on IRQ registration failure
Greg Kroah-Hartman (1):
Linux 6.12.104
Guenter Roeck (4):
hwmon: (pmbus/lm25066) Fix PMBus coefficient calculations
hwmon: (ltc4282) Avoid overflow in maximum power calculation
hwmon: (ltc4282) Clamp negative current limits
hwmon: (ltc4282) Fix parsing adi,current-limit-sense-microvolt
Guillaume Morin (1):
swapfile: call cond_resched() before locking si->lock
Harald Freudenberger (1):
s390/zcrypt: Fix missing mem scrub at clear key import in cca_clr2cipherkey()
Harshal Dev (1):
dt-bindings: crypto: qcom,ice: Fix missing power-domain and iface clk
Henry Martin (1):
net/ncsi: fix heap OOB read in NCSI_CMD_SEND_CMD payload length
Hongyan Xu (1):
ata: pata_sl82c105: fix bridge revision use-after-free
Hui Su (1):
ring-buffer: Fix crash passing ERR_PTR to kthread_stop()
Hyunjung Ko (2):
net/sched: act_ct: fix sk_buff leak when the header checks reject a packet
net/sched: act_gact, act_police: range check the fallback control action
Jamal Hadi Salim (2):
net/sched: cls_route: fix fastmap use-after-free on filter
net/sched: cls_api: Always acquire rtnl_lock when destroying locked classifiers
Jan Sebastian Götte (1):
serial: qcom-geni: fix TX DMA buffer flush
Jiawen Liu (1):
net: hisilicon: hix5hd2_gmac: remove redundant NAPI delete
Jiayuan Chen (1):
tcp: fix TFO max_qlen accounting across reuseport migration
Jijie Shao (1):
net: hns3: fix speed configuration residue after driver reload
Jordan Rife (5):
bpf: tcp: Make mem flags configurable through bpf_iter_tcp_realloc_batch
bpf: tcp: Make sure iter->batch always contains a full bucket snapshot
bpf: tcp: Get rid of st_bucket_done
bpf: tcp: Use bpf_tcp_iter_batch_item for bpf_tcp_iter_state batch items
bpf: tcp: Avoid socket skips and repeats during iteration
Jose Fernandez (Anthropic) (1):
bpf: tcp: Fix use-after-free in bpf_iter_tcp_established_batch()
Joshua Rogers (2):
vt: add permission check for KDSKBMETA ioctl
vt: stabilize tty reference in kbd_keycode with tty_port_tty_get
Jozsef Kadlecsik (1):
netfilter: ipset: switch ext_size to atomic64_t
Julian Anastasov (4):
ipvs: avoid out-of-bounds write in ip_vs_nat_icmp
ipvs: return the csum validation for forward hook
ipvs: add totalconns for dest
ipvs: properly update the overload flag on dest edit
Jun Yang (2):
vhost: reset the vring metadata cache on vring reconfiguration
tipc: read le->link under the node lock in tipc_node_link_down()
Junrui Luo (1):
misc: fastrpc: take fl->lock when moving mmaps on interrupted invoke
Keegan Freyhof (1):
bnxt_en: Fix PTP PPS setting bug
Kees Cook (2):
kunit/fortify: Replace "volatile" with OPTIMIZER_HIDE_VAR()
kunit/fortify: Add back "volatile" for sizeof() constants
Keith Busch (2):
blk-mq: pop cached request if it is usable
blk-mq: reinsert cached request to the list
Keno Fischer (1):
futex: Prevent robust futex exit race some more
Kyle Zeng (2):
ipvs: clear IPv4 options after rebasing tunnel ICMP errors
ipv6: prevent in6_dev_get() from resurrecting inet6_dev
Larisa Grigore (1):
spi: spi-fsl-dspi: Avoid setup_accel logic for DMA transfers
Lincoln Wallace (1):
ima: fix out-of-bounds read in xattr_verify()
Lorenzo Stoakes (ARM) (1):
mm/huge_memory: fix huge_zero_pfn race
Mahanta Jambigi (1):
net/smc: fix qentry overwrite for CONFIRM_LINK and ADD_LINK_CONT in smc_llc_event_handler()
Mariano Baragiola (1):
staging: rtl8723bs: validate monitor transmit frame lengths
Martin Kaiser (1):
selftests/ftrace: refactor eprobes test to fix argument checks
Mathieu Dubois-Briand (1):
nvmem: layouts: Add fixed-layout driver
Matthew Rosato (1):
KVM: s390: pci: Fix aisb calculation
Matti Vaittinen (1):
watchdog: bd96801_wdt: Fix timeout for enabled WDG
Maximilian Immanuel Brandtner (1):
tls: don't abort the connection on signal-interrupted sends
Mehmet Fide (1):
net: fec: do not release NULL pages when RX buffer allocation fails
Michael Guralnik (1):
net/mlx5: fw_tracer, return NULL on create error
Michael Roth (1):
crypto: ccp - Fix checks for SNP_VLEK_LOAD input buffer length
Michael Wu (1):
tracing: Fix race between update_event_fields and, event_define_fields
Michal Luczaj (3):
selftests/bpf: Fail unbound UDP on sockmap update
selftests/bpf: Ensure UDP sockets are bound
selftests/bpf: Adapt sockmap update error handling
Mikhail Kshevetskiy (3):
mtd: spinand: fix direct mapping creation sizes
mtd: spinand: try a regular dirmap if creating a dirmap for continuous reading fails
mtd: spinand: repeat reading in regular mode if continuous reading fails
Mimi Zohar (1):
ima: Instantiate file_truncate and path_truncate hooks
Muhammad Bilal (2):
staging: rtl8723bs: fix OOB read in rtw_get_wpa_ie()
staging: rtl8723bs: fix OOB read in WMM_param_handler()
Nathan Gao (1):
tcp: do not change rcv_ssthresh in tcp_measure_rcv_mss()
Nikhil P. Rao (2):
pds_core: keep the health thread stopped during reset
pds_core: cancel pending PCI reset work on AER recovery
Or Har-Toov (1):
devlink: fix net namespace reference leak in reload
Panagiotis Petrakopoulos (1):
staging: rtl8723bs: fix missing shared-key auth challenge length check
Pawel Laszczak (1):
usb: cdnsp: fix incorrect endian conversions for APB timeout register
Pengpeng Hou (2):
drm/bridge: ps8640: propagate AUX transfer register errors
net: prestera: validate firmware header length
Qihang Tang (3):
packet: use consistent hard_header_len in non-ring send paths
packet: use consistent hard_header_len in TX_RING send path
net: remove CAP_SYS_RAWIO zero-padding in dev_validate_header
Qing Luo (1):
sctp: fix addip_serial increment on ASCONF_ACK allocation failure
Qing Ming (1):
sctp: clear new_transport when removing a peer
Qingshuang Fu (2):
hwmon: (nzxt-smart2) Check return value of init_device() in probe
hwmon: (ads7828) Fix external VREF regulator handling
Rafael J. Wysocki (1):
Revert "thermal/drivers/hwmon: Cleanup coding style a bit"
Ray Wu (2):
drm/amd/display: Add AV mute wait frames to dce110_set_avmute
drm/amd/display: Check for tg ops in dce110_set_avmute
Rik van Riel (1):
fbdev: bitblit: bound-check glyph index in bit_cursor()
Rosen Penev (1):
ARM: dts: BCM5301X: fix PCIe controller 2 second interrupt
Satish Kharat (1):
enic: fix tx_hang_reset use-after-free on device removal
Sean Christopherson (1):
KVM: x86/mmu: WARN and clear role.invalid when creating a child shadow page
Sergey V. Frolov (1):
net: octeontx2-pf: Fix UB in shift operation
Shay Drory (1):
net/mlx5e: TC, Check if flow is PEER before acquiring devcom lock
Shravya KN (3):
bnxt_en: Move RSS table fill outside __bnxt_hwrm_vnic_set_rss()
bnxt_en: Determine and store default RX ring in vnic structure
bnxt_en: Refresh VNIC default ring on queue restart if needed
Shuangpeng Bai (2):
smb: client: Fix use-after-free in cifs_try_adding_channels()
eventfs: Fix use-after-free in eventfs_remove_rec()
Sidraya Jayagond (1):
net/smc: fix TOCTOU race between smc_listen_out() and listener close
Sonali Pradhan (1):
usb: gadget: f_ncm: Use unsigned int for ndp_index
Stanislav Fomichev (1):
xsk: require at least 16 bytes of TX metadata
Sun Jian (2):
net: fix skb length accounting after generic XDP frag adjustment
veth: fix skb length accounting after XDP frag adjustment
Takashi Iwai (1):
ALSA: usb: Fix UAF at delayed release of MIDI2 EPs
Tejun Heo (1):
sched/psi: Shut down rtpoll_timer in psi_cgroup_free()
Toke Høiland-Jørgensen (1):
net/sched: sch_cake: drop WARN_ON(1) for malformed packets in ACK filter
Vincent Donnefort (1):
ring-buffer: Prevent subbuf order change when resizing is disabled
Weiming Shi (2):
vsock/virtio: read virtqueues under worker locks
vsock/virtio: avoid refilling the RX queue after teardown
Wilken Gottwalt (1):
hwmon: (corsair-psu) fix possible out-of-bounds access on missing string termination
Xiang Mei (Microsoft) (2):
bonding: alb: re-check primary_is_promisc under RTNL in bond_alb_monitor
bpf: tcp: fix double sock release on batch realloc
Xin Long (1):
sctp: clear control chunk transport if it is being removed
Xu Rao (3):
thunderbolt: icm: Preserve USB4 proxy data-valid bit
scsi: scsi_debug: Negate wrapped memcmp() result
thunderbolt: Fix bandwidth group reservation indexing
Xuanqiang Luo (1):
udp: fix potential use-after-free in tunnel segmentation
Yangyu Chen (2):
net: atlantic: free stranded TX buffers on ring deinit
net: atlantic: free RX pages of consumed but not refilled buffers
Yi Cong (1):
net: usb: ax88179_178a: fix skb leak in ax88179_tx_fixup()
Yiyang Chen (1):
bpf: Preserve pointer state for commuted arithmetic
Yousef Alhouseen (1):
vhost/vdpa: reject overflowing PA map page counts on 32-bit
Yuejie Shi (1):
ipv6: fix Route Information option length validation
Yuho Choi (1):
ARM: npcm: Fix OF node refcount leaks in SMP setup
Yuxiang Yang (1):
sctp: fix use-after-free of cached ASCONF chunk
Zeng Jingxiang (1):
mm/vmscan: wake up flushers conditionally to avoid cgroup OOM
Zhan Xusheng (1):
fscrypt: use the mount idmap for the owner check in fscrypt_ioctl_set_policy()
Zhiling Zou (5):
ipvs: stop estimator after disabled calc phase
net: openvswitch: reallocate update replies for mismatched IDs
netfilter: bridge: release template ct on non-IP path
xdp: reject clones that overrun skb_shared_info tailroom
ip6_tunnel: clear skb2->cb[] in ip6ip6_err()
Zihan Xi (4):
packet: synchronize pressure clearing with ring reconfiguration
mac802154: fix netdev use-after-free in beacon worker
ipv4: Fix fib_nlmsg_size() for RTA_VIA nexthops
netfilter: nf_conntrack: defer invalid log until after unlock
Zijie Huang (1):
net/sched: reject overly deep qdisc hierarchies
chanyoung (1):
tls: don't leave a full plaintext sk_msg ring unpushed
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: Linux 6.12.104
2026-08-19 16:52 Linux 6.12.104 Greg Kroah-Hartman
@ 2026-08-19 16:52 ` Greg Kroah-Hartman
0 siblings, 0 replies; 2+ messages in thread
From: Greg Kroah-Hartman @ 2026-08-19 16:52 UTC (permalink / raw)
To: linux-kernel, akpm, torvalds, stable; +Cc: lwn, jslaby, Greg Kroah-Hartman
diff --git a/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml b/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml
index 0304f074cf08..2793afc7f85c 100644
--- a/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml
+++ b/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml
@@ -25,6 +25,16 @@ properties:
maxItems: 1
clocks:
+ minItems: 1
+ maxItems: 2
+
+ clock-names:
+ minItems: 1
+ items:
+ - const: core
+ - const: iface
+
+ power-domains:
maxItems: 1
required:
@@ -42,6 +52,10 @@ examples:
compatible = "qcom,sm8550-inline-crypto-engine",
"qcom,inline-crypto-engine";
reg = <0x01d88000 0x8000>;
- clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>;
+ clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>,
+ <&gcc GCC_UFS_PHY_AHB_CLK>;
+ clock-names = "core",
+ "iface";
+ power-domains = <&gcc UFS_PHY_GDSC>;
};
...
diff --git a/MAINTAINERS b/MAINTAINERS
index d765c62c80ea..d5d394a5ad6b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8849,6 +8849,11 @@ F: drivers/base/firmware_loader/
F: rust/kernel/firmware.rs
F: include/linux/firmware.h
+FIXED-LAYOUT NVMEM LAYOUT DRIVER
+M: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+S: Maintained
+F: drivers/nvmem/layouts/fixed-layout.c
+
FLEXTIMER FTM-QUADDEC DRIVER
M: Patrick Havelange <patrick.havelange@essensium.com>
L: linux-iio@vger.kernel.org
diff --git a/Makefile b/Makefile
index f3bacdc021a4..69eb37d9d488 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 6
PATCHLEVEL = 12
-SUBLEVEL = 103
+SUBLEVEL = 104
EXTRAVERSION =
NAME = Baby Opossum Posse
diff --git a/arch/arm/boot/dts/broadcom/bcm-ns.dtsi b/arch/arm/boot/dts/broadcom/bcm-ns.dtsi
index d0d5f7e52a91..f08d0a27de0b 100644
--- a/arch/arm/boot/dts/broadcom/bcm-ns.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm-ns.dtsi
@@ -138,7 +138,7 @@ axi@18000000 {
/* PCIe Controller 2 */
<0x00014000 0 &gic GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>,
- <0x00014000 1 &gic GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>,
+ <0x00014000 1 &gic GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>,
<0x00014000 2 &gic GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>,
<0x00014000 3 &gic GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>,
<0x00014000 4 &gic GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>,
diff --git a/arch/arm/mach-npcm/platsmp.c b/arch/arm/mach-npcm/platsmp.c
index 41891d3aa124..4c1fc9983746 100644
--- a/arch/arm/mach-npcm/platsmp.c
+++ b/arch/arm/mach-npcm/platsmp.c
@@ -32,6 +32,7 @@ static int npcm7xx_smp_boot_secondary(unsigned int cpu,
goto out;
}
gcr_base = of_iomap(gcr_np, 0);
+ of_node_put(gcr_np);
if (!gcr_base) {
pr_err("could not iomap gcr");
ret = -ENOMEM;
@@ -60,6 +61,7 @@ static void __init npcm7xx_smp_prepare_cpus(unsigned int max_cpus)
return;
}
scu_base = of_iomap(scu_np, 0);
+ of_node_put(scu_np);
if (!scu_base) {
pr_err("could not iomap scu");
return;
diff --git a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
index 209f99b1ceae..c8e809d77b3f 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
+++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
@@ -279,8 +279,6 @@ IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) |
IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) |
- IRQ_TYPE_LEVEL_LOW)>,
- <GIC_PPI 12 (GIC_CPU_MASK_SIMPLE(4) |
IRQ_TYPE_LEVEL_LOW)>;
};
};
diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c
index 17fedfc7bb99..d9fcbf41025e 100644
--- a/arch/s390/kvm/pci.c
+++ b/arch/s390/kvm/pci.c
@@ -167,7 +167,7 @@ static int kvm_zpci_set_airq(struct zpci_dev *zdev)
fib.fmt0.noi = airq_iv_end(zdev->aibv);
fib.fmt0.aibv = virt_to_phys(zdev->aibv->vector);
fib.fmt0.aibvo = 0;
- fib.fmt0.aisb = virt_to_phys(aift->sbv->vector + (zdev->aisb / 64) * 8);
+ fib.fmt0.aisb = virt_to_phys(aift->sbv->vector) + (zdev->aisb / 64) * 8;
fib.fmt0.aisbo = zdev->aisb & 63;
fib.gd = zdev->gisa;
@@ -191,34 +191,54 @@ static int kvm_zpci_clear_airq(struct zpci_dev *zdev)
return cc ? -EIO : 0;
}
-static inline void unaccount_mem(unsigned long nr_pages)
+static inline void unaccount_mem(struct kvm_zdev *kzdev, unsigned long nr_pages)
{
- struct user_struct *user = get_uid(current_user());
+ struct user_struct *user = kzdev->user_account;
+ struct mm_struct *mm_account = kzdev->mm_account;
- if (user)
+ if (user) {
atomic_long_sub(nr_pages, &user->locked_vm);
- if (current->mm)
- atomic64_sub(nr_pages, ¤t->mm->pinned_vm);
+ free_uid(user);
+ kzdev->user_account = NULL;
+ }
+
+ if (mm_account) {
+ atomic64_sub(nr_pages, &mm_account->pinned_vm);
+ mmdrop(mm_account);
+ kzdev->mm_account = NULL;
+ }
}
-static inline int account_mem(unsigned long nr_pages)
+static inline int account_mem(struct kvm_zdev *kzdev, unsigned long nr_pages)
{
struct user_struct *user = get_uid(current_user());
unsigned long page_limit, cur_pages, new_pages;
+ int rc = 0;
page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+ cur_pages = atomic_long_read(&user->locked_vm);
do {
- cur_pages = atomic_long_read(&user->locked_vm);
new_pages = cur_pages + nr_pages;
- if (new_pages > page_limit)
- return -ENOMEM;
- } while (atomic_long_cmpxchg(&user->locked_vm, cur_pages,
- new_pages) != cur_pages);
+ if (new_pages > page_limit) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ } while (!atomic_long_try_cmpxchg(&user->locked_vm, &cur_pages, new_pages));
- atomic64_add(nr_pages, ¤t->mm->pinned_vm);
+ if (current->mm) {
+ mmgrab(current->mm);
+ atomic64_add(nr_pages, ¤t->mm->pinned_vm);
+ }
+
+ kzdev->user_account = user;
+ kzdev->mm_account = current->mm;
return 0;
+
+out:
+ free_uid(user);
+ return rc;
}
static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
@@ -295,14 +315,17 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
}
/* Account for pinned pages, roll back on failure */
- if (account_mem(pcount))
+ rc = account_mem(zdev->kzdev, pcount);
+ if (rc)
goto unpin2;
/* AISB must be allocated before we can fill in GAITE */
mutex_lock(&aift->aift_lock);
bit = airq_iv_alloc_bit(aift->sbv);
- if (bit == -1UL)
+ if (bit == -1UL) {
+ rc = -ENOMEM;
goto unlock;
+ }
zdev->aisb = bit; /* store the summary bit number */
zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA |
AIRQ_IV_BITLOCK |
@@ -336,24 +359,39 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
aift->kzdev[zdev->aisb] = zdev->kzdev;
spin_unlock_irq(&aift->gait_lock);
- /* Update guest FIB for re-issue */
- fib->fmt0.aisbo = zdev->aisb & 63;
- fib->fmt0.aisb = virt_to_phys(aift->sbv->vector + (zdev->aisb / 64) * 8);
- fib->fmt0.isc = gisc;
-
/* Save some guest fib values in the host for later use */
- zdev->kzdev->fib.fmt0.isc = fib->fmt0.isc;
+ zdev->kzdev->fib.fmt0.isc = gisc;
zdev->kzdev->fib.fmt0.aibv = fib->fmt0.aibv;
- mutex_unlock(&aift->aift_lock);
/* Issue the clp to setup the irq now */
rc = kvm_zpci_set_airq(zdev);
- return rc;
+ if (!rc) {
+ mutex_unlock(&aift->aift_lock);
+ return rc;
+ }
+
+ /* Start cleanup */
+ zdev->kzdev->fib.fmt0.isc = 0;
+ zdev->kzdev->fib.fmt0.aibv = 0;
+
+ spin_lock_irq(&aift->gait_lock);
+ gaite->count--;
+ gaite->aisb = 0;
+ gaite->gisc = 0;
+ gaite->aisbo = 0;
+ gaite->gisa = 0;
+ aift->kzdev[zdev->aisb] = NULL;
+ spin_unlock_irq(&aift->gait_lock);
+
+ airq_iv_release(zdev->aibv);
+ zdev->aibv = NULL;
free_aisb:
airq_iv_free_bit(aift->sbv, zdev->aisb);
zdev->aisb = 0;
unlock:
+ if (pcount > 0)
+ unaccount_mem(zdev->kzdev, pcount);
mutex_unlock(&aift->aift_lock);
unpin2:
if (fib->fmt0.sum == 1)
@@ -424,7 +462,7 @@ static int kvm_s390_pci_aif_disable(struct zpci_dev *zdev, bool force)
pcount++;
}
if (pcount > 0)
- unaccount_mem(pcount);
+ unaccount_mem(kzdev, pcount);
out:
mutex_unlock(&aift->aift_lock);
diff --git a/arch/s390/kvm/pci.h b/arch/s390/kvm/pci.h
index ff0972dd5e71..fdf8c7bf4ed0 100644
--- a/arch/s390/kvm/pci.h
+++ b/arch/s390/kvm/pci.h
@@ -22,6 +22,8 @@ struct kvm_zdev {
struct kvm *kvm;
struct zpci_fib fib;
struct list_head entry;
+ struct user_struct *user_account;
+ struct mm_struct *mm_account;
};
struct zpci_gaite {
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 9be2b10ff4f0..b5a5e5c3cc5a 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -2291,6 +2291,9 @@ static union kvm_mmu_page_role kvm_mmu_child_role(u64 *sptep, bool direct,
role.direct = direct;
role.passthrough = 0;
+ WARN_ON_ONCE(role.invalid);
+ role.invalid = 0;
+
/*
* If the guest has 4-byte PTEs then that means it's using 32-bit,
* 2-level, non-PAE paging. KVM shadows such guests with PAE paging
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 6cec696461da..bd0344bacaf1 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -2972,6 +2972,7 @@ void __init sev_set_cpu_caps(void)
void __init sev_hardware_setup(void)
{
unsigned int eax, ebx, ecx, edx, sev_asid_count, sev_es_asid_count;
+ struct sev_platform_init_args init_args = {0};
bool sev_snp_supported = false;
bool sev_es_supported = false;
bool sev_supported = false;
@@ -3088,6 +3089,15 @@ void __init sev_hardware_setup(void)
sev_supported_vmsa_features = 0;
if (sev_es_debug_swap_enabled)
sev_supported_vmsa_features |= SVM_SEV_FEAT_DEBUG_SWAP;
+
+ if (!sev_enabled)
+ return;
+
+ /*
+ * Do both SNP and SEV initialization at KVM module load.
+ */
+ init_args.probe = true;
+ sev_platform_init(&init_args);
}
void sev_hardware_unsetup(void)
@@ -3103,6 +3113,8 @@ void sev_hardware_unsetup(void)
misc_cg_set_capacity(MISC_CG_RES_SEV, 0);
misc_cg_set_capacity(MISC_CG_RES_SEV_ES, 0);
+
+ sev_platform_shutdown();
}
int sev_cpu_init(struct svm_cpu_data *sd)
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 5bfaa8e4b9cf..6af42682af7d 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -2989,7 +2989,7 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q,
/*
* Check if there is a suitable cached request and return it.
*/
-static struct request *blk_mq_peek_cached_request(struct blk_plug *plug,
+static struct request *blk_mq_get_cached_request(struct blk_plug *plug,
struct request_queue *q, blk_opf_t opf)
{
enum hctx_type type = blk_mq_get_hctx_type(opf);
@@ -3005,27 +3005,10 @@ static struct request *blk_mq_peek_cached_request(struct blk_plug *plug,
return NULL;
if (op_is_flush(rq->cmd_flags) != op_is_flush(opf))
return NULL;
+ rq_list_pop(&plug->cached_rqs);
return rq;
}
-static void blk_mq_use_cached_rq(struct request *rq, struct blk_plug *plug,
- struct bio *bio)
-{
- if (rq_list_pop(&plug->cached_rqs) != rq)
- WARN_ON_ONCE(1);
-
- /*
- * If any qos ->throttle() end up blocking, we will have flushed the
- * plug and hence killed the cached_rq list as well. Pop this entry
- * before we throttle.
- */
- rq_qos_throttle(rq->q, bio);
-
- blk_mq_rq_time_init(rq, 0);
- rq->cmd_flags = bio->bi_opf;
- INIT_LIST_HEAD(&rq->queuelist);
-}
-
static bool bio_unaligned(const struct bio *bio, struct request_queue *q)
{
unsigned int bs_mask = queue_logical_block_size(q) - 1;
@@ -3063,7 +3046,7 @@ void blk_mq_submit_bio(struct bio *bio)
/*
* If the plug has a cached request for this queue, try to use it.
*/
- rq = blk_mq_peek_cached_request(plug, q, bio->bi_opf);
+ rq = blk_mq_get_cached_request(plug, q, bio->bi_opf);
/*
* A BIO that was released from a zone write plug has already been
@@ -3121,12 +3104,15 @@ void blk_mq_submit_bio(struct bio *bio)
}
new_request:
- if (!rq) {
+ if (rq) {
+ rq_qos_throttle(rq->q, bio);
+ blk_mq_rq_time_init(rq, blk_time_get_ns());
+ rq->cmd_flags = bio->bi_opf;
+ INIT_LIST_HEAD(&rq->queuelist);
+ } else {
rq = blk_mq_get_new_requests(q, plug, bio, nr_segs);
if (unlikely(!rq))
goto queue_exit;
- } else {
- blk_mq_use_cached_rq(rq, plug, bio);
}
trace_block_getrq(bio);
@@ -3165,12 +3151,10 @@ void blk_mq_submit_bio(struct bio *bio)
return;
queue_exit:
- /*
- * Don't drop the queue reference if we were trying to use a cached
- * request and thus didn't acquire one.
- */
if (!rq)
blk_queue_exit(q);
+ else
+ rq_list_add_head(&plug->cached_rqs, rq);
}
#ifdef CONFIG_BLK_MQ_STACKING
diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c
index 93882e976ede..2a5ce46af5a0 100644
--- a/drivers/ata/pata_sl82c105.c
+++ b/drivers/ata/pata_sl82c105.c
@@ -264,6 +264,7 @@ static struct ata_port_operations sl82c105_port_ops = {
static int sl82c105_bridge_revision(struct pci_dev *pdev)
{
struct pci_dev *bridge;
+ u8 revision;
/*
* The bridge should be part of the same device, but function 0.
@@ -285,8 +286,9 @@ static int sl82c105_bridge_revision(struct pci_dev *pdev)
/*
* We need to find function 0's revision, not function 1
*/
+ revision = bridge->revision;
pci_dev_put(bridge);
- return bridge->revision;
+ return revision;
}
static void sl82c105_fixup(struct pci_dev *pdev)
diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c
index 461f57f66631..fbd1afe178a3 100644
--- a/drivers/counter/microchip-tcb-capture.c
+++ b/drivers/counter/microchip-tcb-capture.c
@@ -309,7 +309,7 @@ static int mchp_tc_probe(struct platform_device *pdev)
char clk_name[7];
struct regmap *regmap;
struct clk *clk[3];
- int channel;
+ u32 channel;
int ret, i;
counter = devm_counter_alloc(&pdev->dev, sizeof(*priv));
@@ -343,7 +343,7 @@ static int mchp_tc_probe(struct platform_device *pdev)
priv->channel[i] = channel;
- snprintf(clk_name, sizeof(clk_name), "t%d_clk", channel);
+ snprintf(clk_name, sizeof(clk_name), "t%u_clk", channel);
clk[i] = of_clk_get_by_name(np->parent, clk_name);
if (IS_ERR(clk[i])) {
diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
index d668f9318c28..d6a0773d6da8 100644
--- a/drivers/crypto/ccp/sev-dev.c
+++ b/drivers/crypto/ccp/sev-dev.c
@@ -226,6 +226,7 @@ static int sev_cmd_buffer_len(int cmd)
case SEV_CMD_SNP_GUEST_REQUEST: return sizeof(struct sev_data_snp_guest_request);
case SEV_CMD_SNP_CONFIG: return sizeof(struct sev_user_data_snp_config);
case SEV_CMD_SNP_COMMIT: return sizeof(struct sev_data_snp_commit);
+ case SEV_CMD_SNP_VLEK_LOAD: return sizeof(struct sev_user_data_snp_vlek_load);
default: return 0;
}
@@ -1117,7 +1118,7 @@ static int __sev_snp_init_locked(int *error)
if (!sev_version_greater_or_equal(SNP_MIN_API_MAJOR, SNP_MIN_API_MINOR)) {
dev_dbg(sev->dev, "SEV-SNP support requires firmware version >= %d:%d\n",
SNP_MIN_API_MAJOR, SNP_MIN_API_MINOR);
- return 0;
+ return -EOPNOTSUPP;
}
/* SNP_INIT requires MSR_VM_HSAVE_PA to be cleared on all CPUs. */
@@ -1356,12 +1357,9 @@ static int _sev_platform_init_locked(struct sev_platform_init_args *args)
rc = __sev_snp_init_locked(&args->error);
if (rc && rc != -ENODEV) {
- /*
- * Don't abort the probe if SNP INIT failed,
- * continue to initialize the legacy SEV firmware.
- */
dev_err(sev->dev, "SEV-SNP: failed to INIT rc %d, error %#x\n",
rc, args->error);
+ return rc;
}
/* Defer legacy SEV/SEV-ES support if allowed by caller/module. */
@@ -2444,6 +2442,15 @@ static void sev_firmware_shutdown(struct sev_device *sev)
mutex_unlock(&sev_cmd_mutex);
}
+void sev_platform_shutdown(void)
+{
+ if (!psp_master || !psp_master->sev_data)
+ return;
+
+ sev_firmware_shutdown(psp_master->sev_data);
+}
+EXPORT_SYMBOL_GPL(sev_platform_shutdown);
+
void sev_dev_destroy(struct psp_device *psp)
{
struct sev_device *sev = psp->sev_data;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index df77afbd5462..2ec994a6dea6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -4635,6 +4635,8 @@ static void amdgpu_device_unmap_mmio(struct amdgpu_device *adev)
iounmap(adev->rmmio);
adev->rmmio = NULL;
+ if (adev->mman.aper_base_kaddr)
+ iounmap(adev->mman.aper_base_kaddr);
adev->mman.aper_base_kaddr = NULL;
/* Memory manager related */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 31f9fb3cfeaa..e161af6276b9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -1909,23 +1909,17 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
/* Change the size here instead of the init above so only lpfn is affected */
amdgpu_ttm_set_buffer_funcs_status(adev, false);
#ifdef CONFIG_64BIT
- if (adev->gmc.xgmi.connected_to_cpu) {
- void *kaddr = devm_memremap(adev->dev, adev->gmc.aper_base,
- adev->gmc.visible_vram_size,
- MEMREMAP_WB);
- if (IS_ERR(kaddr))
- return PTR_ERR(kaddr);
- adev->mman.aper_base_kaddr = (__force void __iomem *)kaddr;
- } else if (adev->gmc.is_app_apu) {
+#ifdef CONFIG_X86
+ if (adev->gmc.xgmi.connected_to_cpu)
+ adev->mman.aper_base_kaddr = ioremap_cache(adev->gmc.aper_base,
+ adev->gmc.visible_vram_size);
+ else if (adev->gmc.is_app_apu)
DRM_DEBUG_DRIVER(
"No need to ioremap when real vram size is 0\n");
- } else {
- adev->mman.aper_base_kaddr = devm_ioremap_wc(adev->dev,
- adev->gmc.aper_base,
- adev->gmc.visible_vram_size);
- if (!adev->mman.aper_base_kaddr)
- return -ENOMEM;
- }
+ else
+#endif
+ adev->mman.aper_base_kaddr = ioremap_wc(adev->gmc.aper_base,
+ adev->gmc.visible_vram_size);
#endif
/*
@@ -2072,6 +2066,8 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
*/
void amdgpu_ttm_fini(struct amdgpu_device *adev)
{
+ int idx;
+
if (!adev->mman.initialized)
return;
@@ -2094,7 +2090,14 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev)
amdgpu_ttm_fw_reserve_vram_fini(adev);
amdgpu_ttm_drv_reserve_vram_fini(adev);
- adev->mman.aper_base_kaddr = NULL;
+ if (drm_dev_enter(adev_to_drm(adev), &idx)) {
+
+ if (adev->mman.aper_base_kaddr)
+ iounmap(adev->mman.aper_base_kaddr);
+ adev->mman.aper_base_kaddr = NULL;
+
+ drm_dev_exit(idx);
+ }
amdgpu_vram_mgr_fini(adev);
amdgpu_gtt_mgr_fini(adev);
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
index 551638d9ff61..dcc78244dd7c 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
@@ -1256,8 +1256,30 @@ void dce110_blank_stream(struct pipe_ctx *pipe_ctx)
void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable)
{
- if (pipe_ctx != NULL && pipe_ctx->stream_res.stream_enc != NULL)
+ if (pipe_ctx == NULL || pipe_ctx->stream_res.stream_enc == NULL)
+ return;
+
+ if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) {
pipe_ctx->stream_res.stream_enc->funcs->set_avmute(pipe_ctx->stream_res.stream_enc, enable);
+
+ /* Wait for three frames to make sure AV mute is sent out.
+ * Some HDMI sinks need additional GCP packets to properly
+ * process the mute state, especially after link re-establishment
+ * with HDMI 2.0 scrambling enabled.
+ */
+ if (enable && pipe_ctx->stream_res.tg &&
+ pipe_ctx->stream_res.tg->funcs->is_tg_enabled &&
+ pipe_ctx->stream_res.tg->funcs->wait_for_state &&
+ pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) {
+ int i;
+
+ pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
+ for (i = 0; i < 3; i++) {
+ pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK);
+ pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
+ }
+ }
+ }
}
static enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id)
diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c
index 14d4dcf239da..61ea855e55d8 100644
--- a/drivers/gpu/drm/bridge/parade-ps8640.c
+++ b/drivers/gpu/drm/bridge/parade-ps8640.c
@@ -258,8 +258,14 @@ static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux,
addr_len[PAGE0_SWAUX_LENGTH - base] = (len == 0) ? SWAUX_NO_PAYLOAD :
((len - 1) & SWAUX_LENGTH_MASK);
- regmap_bulk_write(map, PAGE0_SWAUX_ADDR_7_0, addr_len,
- ARRAY_SIZE(addr_len));
+ ret = regmap_bulk_write(map, PAGE0_SWAUX_ADDR_7_0, addr_len,
+ ARRAY_SIZE(addr_len));
+ if (ret) {
+ DRM_DEV_ERROR(dev,
+ "failed to write AUX address %#x, len %zu: %d\n",
+ msg->address, len, ret);
+ return ret;
+ }
if (len && (request == DP_AUX_NATIVE_WRITE ||
request == DP_AUX_I2C_WRITE)) {
@@ -275,13 +281,22 @@ static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux,
}
}
- regmap_write(map, PAGE0_SWAUX_CTRL, SWAUX_SEND);
+ ret = regmap_write(map, PAGE0_SWAUX_CTRL, SWAUX_SEND);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to start AUX transfer: %d\n", ret);
+ return ret;
+ }
/* Zero delay loop because i2c transactions are slow already */
- regmap_read_poll_timeout(map, PAGE0_SWAUX_CTRL, data,
- !(data & SWAUX_SEND), 0, 50 * 1000);
+ ret = regmap_read_poll_timeout(map, PAGE0_SWAUX_CTRL, data,
+ !(data & SWAUX_SEND), 0, 50 * 1000);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to complete AUX transfer: %d\n",
+ ret);
+ return ret;
+ }
- regmap_read(map, PAGE0_SWAUX_STATUS, &data);
+ ret = regmap_read(map, PAGE0_SWAUX_STATUS, &data);
if (ret) {
DRM_DEV_ERROR(dev, "failed to read PAGE0_SWAUX_STATUS: %d\n",
ret);
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index 436637264056..20ef4992f5bb 100644
--- a/drivers/hwmon/ads7828.c
+++ b/drivers/hwmon/ads7828.c
@@ -106,12 +106,11 @@ static int ads7828_probe(struct i2c_client *client)
struct ads7828_data *data;
struct device *hwmon_dev;
unsigned int vref_mv = ADS7828_INT_VREF_MV;
- unsigned int vref_uv;
+ int vref_uv;
bool diff_input = false;
bool ext_vref = false;
unsigned int regval;
enum ads7828_chips chip;
- struct regulator *reg;
data = devm_kzalloc(dev, sizeof(struct ads7828_data), GFP_KERNEL);
if (!data)
@@ -125,9 +124,11 @@ static int ads7828_probe(struct i2c_client *client)
} else if (dev->of_node) {
diff_input = of_property_read_bool(dev->of_node,
"ti,differential-input");
- reg = devm_regulator_get_optional(dev, "vref");
- if (!IS_ERR(reg)) {
- vref_uv = regulator_get_voltage(reg);
+ vref_uv = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (vref_uv < 0) {
+ if (vref_uv != -ENODEV)
+ return vref_uv;
+ } else {
vref_mv = DIV_ROUND_CLOSEST(vref_uv, 1000);
if (vref_mv < ADS7828_EXT_VREF_MV_MIN ||
vref_mv > ADS7828_EXT_VREF_MV_MAX)
diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c
index 4ab73bcef1d1..61d1b22bc63e 100644
--- a/drivers/hwmon/corsair-psu.c
+++ b/drivers/hwmon/corsair-psu.c
@@ -709,7 +709,7 @@ static int vendor_show(struct seq_file *seqf, void *unused)
{
struct corsairpsu_data *priv = seqf->private;
- seq_printf(seqf, "%s\n", priv->vendor);
+ seq_printf(seqf, "%.*s\n", REPLY_SIZE, priv->vendor);
return 0;
}
@@ -719,7 +719,7 @@ static int product_show(struct seq_file *seqf, void *unused)
{
struct corsairpsu_data *priv = seqf->private;
- seq_printf(seqf, "%s\n", priv->product);
+ seq_printf(seqf, "%.*s\n", REPLY_SIZE, priv->product);
return 0;
}
diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c
index e1c0d02b564b..5f7ab813051c 100644
--- a/drivers/hwmon/ltc4282.c
+++ b/drivers/hwmon/ltc4282.c
@@ -15,6 +15,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/math.h>
+#include <linux/math64.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
@@ -142,7 +143,7 @@ struct ltc4282_state {
*/
struct ltc4282_cache in0_1_cache[LTC4282_CHAN_VGPIO];
u32 vsense_max;
- long power_max;
+ s64 power_max;
u32 rsense;
u16 vdd;
u16 vfs_out;
@@ -621,13 +622,12 @@ static int ltc4282_read(struct device *dev, enum hwmon_sensor_types type,
}
static int ltc4282_write_power_byte(const struct ltc4282_state *st, u32 reg,
- long val)
+ s64 val)
{
u32 power;
u64 temp;
- if (val > st->power_max)
- val = st->power_max;
+ val = clamp(val, 0, st->power_max);
temp = val * int_pow(U8_MAX, 2) * st->rsense;
power = DIV64_U64_ROUND_CLOSEST(temp,
@@ -637,7 +637,7 @@ static int ltc4282_write_power_byte(const struct ltc4282_state *st, u32 reg,
}
static int ltc4282_write_power_word(const struct ltc4282_state *st, u32 reg,
- long val)
+ u64 val)
{
u64 temp = int_pow(U16_MAX, 2) * st->rsense, temp_2;
__be16 __raw;
@@ -946,8 +946,11 @@ static int ltc4282_curr_reset_hist(struct ltc4282_state *st)
static int ltc4282_write_curr(struct ltc4282_state *st, u32 attr,
long val)
{
+ s32 ulimit = min_t(u64, INT_MAX,
+ div_u64((u64)INT_MAX * DECA * MICRO, st->rsense));
+ u64 val64 = clamp(val, 0, ulimit);
/* need to pass it in millivolt */
- u32 in = DIV_ROUND_CLOSEST_ULL((u64)val * st->rsense, DECA * MICRO);
+ u32 in = DIV_ROUND_CLOSEST_ULL(val64 * st->rsense, DECA * MICRO);
switch (attr) {
case hwmon_curr_max:
@@ -1254,7 +1257,8 @@ static int ltc4282_set_max_limits(struct ltc4282_state *st)
return ret;
/* Power is given by ISENSE * Vout. */
- st->power_max = DIV_ROUND_CLOSEST(st->vsense_max * DECA * MILLI, st->rsense) * st->vfs_out;
+ st->power_max = DIV_ROUND_CLOSEST_ULL((u64)st->vsense_max * DECA * MILLI,
+ st->rsense) * st->vfs_out;
ret = ltc4282_write_power_byte(st, LTC4282_POWER_MAX, st->power_max);
if (ret)
return ret;
@@ -1421,7 +1425,7 @@ static int ltc4282_setup(struct ltc4282_state *st, struct device *dev)
if (!ret) {
int reg_val;
- switch (val) {
+ switch (st->vsense_max) {
case 12500:
reg_val = 0;
break;
diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c
index 66c5886f411a..8ff3ebc1bb4c 100644
--- a/drivers/hwmon/nzxt-smart2.c
+++ b/drivers/hwmon/nzxt-smart2.c
@@ -760,7 +760,11 @@ static int nzxt_smart2_hid_probe(struct hid_device *hdev,
hid_device_io_start(hdev);
- init_device(drvdata, UPDATE_INTERVAL_DEFAULT_MS);
+ ret = init_device(drvdata, UPDATE_INTERVAL_DEFAULT_MS);
+ if (ret) {
+ dev_err(&hdev->dev, "init_device failed: %d\n", ret);
+ goto out_hw_close;
+ }
drvdata->hwmon =
hwmon_device_register_with_info(&hdev->dev, "nzxtsmart2", drvdata,
diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c
index c36c124d1a2d..f0bf3b153331 100644
--- a/drivers/hwmon/pmbus/lm25066.c
+++ b/drivers/hwmon/pmbus/lm25066.c
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/log2.h>
+#include <linux/math.h>
#include <linux/of.h>
#include "pmbus.h"
@@ -540,8 +541,8 @@ static int lm25066_probe(struct i2c_client *client)
if (of_property_read_u32(client->dev.of_node, "shunt-resistor-micro-ohms", &shunt))
shunt = 1000;
- info->m[PSC_CURRENT_IN] = info->m[PSC_CURRENT_IN] * shunt / 1000;
- info->m[PSC_POWER] = info->m[PSC_POWER] * shunt / 1000;
+ info->m[PSC_CURRENT_IN] = DIV_ROUND_CLOSEST_ULL((u64)info->m[PSC_CURRENT_IN] * shunt, 1000);
+ info->m[PSC_POWER] = DIV_ROUND_CLOSEST_ULL((u64)info->m[PSC_POWER] * shunt, 1000);
#if IS_ENABLED(CONFIG_SENSORS_LM25066_REGULATOR)
/* LM25056 doesn't support OPERATION */
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index b5cbb57ee5f6..2fa5b8a13131 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/input/mt.h>
#include <linux/major.h>
+#include <linux/nospec.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include "input-compat.h"
@@ -67,8 +68,10 @@ static size_t evdev_get_mask_cnt(unsigned int type)
[EV_SND] = SND_CNT,
[EV_FF] = FF_CNT,
};
+ unsigned long mask = array_index_mask_nospec(type, EV_CNT);
- return (type < EV_CNT) ? counts[type] : 0;
+ /* Returns 0 for out-of-bounds types, including speculatively */
+ return counts[type & mask] & mask;
}
/* requires the buffer lock to be held */
@@ -146,11 +149,11 @@ static void __evdev_queue_syn_dropped(struct evdev_client *client)
struct timespec64 ts = ktime_to_timespec64(ev_time[client->clk_type]);
struct input_event ev;
+ memset(&ev, 0, sizeof(ev));
ev.input_event_sec = ts.tv_sec;
ev.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;
ev.type = EV_SYN;
ev.code = SYN_DROPPED;
- ev.value = 0;
client->buffer[client->head++] = ev;
client->head &= client->bufsize - 1;
@@ -218,20 +221,20 @@ static void __pass_event(struct evdev_client *client,
client->head &= client->bufsize - 1;
if (unlikely(client->head == client->tail)) {
+ struct input_event ev;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.input_event_sec = event->input_event_sec;
+ ev.input_event_usec = event->input_event_usec;
+ ev.type = EV_SYN;
+ ev.code = SYN_DROPPED;
+
/*
* This effectively "drops" all unconsumed events, leaving
* EV_SYN/SYN_DROPPED plus the newest event in the queue.
*/
client->tail = (client->head - 2) & (client->bufsize - 1);
-
- client->buffer[client->tail] = (struct input_event) {
- .input_event_sec = event->input_event_sec,
- .input_event_usec = event->input_event_usec,
- .type = EV_SYN,
- .code = SYN_DROPPED,
- .value = 0,
- };
-
+ client->buffer[client->tail] = ev;
client->packet_head = client->tail;
}
@@ -253,6 +256,8 @@ static void evdev_pass_values(struct evdev_client *client,
if (client->revoked)
return;
+ memset(&event, 0, sizeof(event));
+
ts = ktime_to_timespec64(ev_time[client->clk_type]);
event.input_event_sec = ts.tv_sec;
event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index c0ef145c8297..576cd9e5b807 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -469,6 +469,7 @@ static void fastrpc_channel_ctx_free(struct kref *ref)
cctx = container_of(ref, struct fastrpc_channel_ctx, refcount);
+ idr_destroy(&cctx->ctx_idr);
kfree(cctx);
}
@@ -1279,10 +1280,12 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel,
}
if (err == -ERESTARTSYS) {
+ spin_lock(&fl->lock);
list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
list_del(&buf->node);
list_add_tail(&buf->node, &fl->cctx->invoke_interrupted_mmaps);
}
+ spin_unlock(&fl->lock);
}
if (err)
@@ -1643,7 +1646,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp)
dev_err(&cctx->rpdev->dev, "No session available\n");
mutex_destroy(&fl->mutex);
kfree(fl);
-
+ fastrpc_channel_ctx_put(cctx);
return -EBUSY;
}
@@ -1871,9 +1874,6 @@ static int fastrpc_req_munmap_impl(struct fastrpc_user *fl, struct fastrpc_buf *
&args[0]);
if (!err) {
dev_dbg(dev, "unmmap\tpt 0x%09lx OK\n", buf->raddr);
- spin_lock(&fl->lock);
- list_del(&buf->node);
- spin_unlock(&fl->lock);
fastrpc_buf_free(buf);
} else {
dev_err(dev, "unmmap\tpt 0x%09lx ERROR\n", buf->raddr);
@@ -1887,6 +1887,7 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp)
struct fastrpc_buf *buf = NULL, *iter, *b;
struct fastrpc_req_munmap req;
struct device *dev = fl->sctx->dev;
+ int err;
if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
@@ -1894,6 +1895,7 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp)
spin_lock(&fl->lock);
list_for_each_entry_safe(iter, b, &fl->mmaps, node) {
if ((iter->raddr == req.vaddrout) && (iter->size == req.size)) {
+ list_del(&iter->node);
buf = iter;
break;
}
@@ -1906,7 +1908,14 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp)
return -EINVAL;
}
- return fastrpc_req_munmap_impl(fl, buf);
+ err = fastrpc_req_munmap_impl(fl, buf);
+ if (err) {
+ spin_lock(&fl->lock);
+ list_add_tail(&buf->node, &fl->mmaps);
+ spin_unlock(&fl->lock);
+ }
+
+ return err;
}
static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index be011cef12e5..ccc40f900830 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -447,18 +447,24 @@ static void mei_io_tx_list_free_cl(struct list_head *head,
}
/**
- * mei_io_list_free_fp - free cb from a list that matches file pointer
+ * mei_io_rd_list_free_fp - free cb from a rd_completed list that matches file pointer
*
- * @head: io list
+ * @cl: host client
* @fp: file pointer (matching cb file object), may be NULL
*/
-static void mei_io_list_free_fp(struct list_head *head, const struct file *fp)
+static void mei_io_rd_list_free_fp(struct mei_cl *cl, const struct file *fp)
{
struct mei_cl_cb *cb, *next;
+ LIST_HEAD(cmpl_list);
- list_for_each_entry_safe(cb, next, head, list)
+ spin_lock(&cl->rd_completed_lock);
+ list_for_each_entry_safe(cb, next, &cl->rd_completed, list)
if (!fp || fp == cb->fp)
- mei_io_cb_free(cb);
+ list_move(&cb->list, &cmpl_list);
+ spin_unlock(&cl->rd_completed_lock);
+
+ list_for_each_entry_safe(cb, next, &cmpl_list, list)
+ mei_io_cb_free(cb);
}
/**
@@ -587,9 +593,7 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl);
mei_cl_free_pending(cl);
}
- spin_lock(&cl->rd_completed_lock);
- mei_io_list_free_fp(&cl->rd_completed, fp);
- spin_unlock(&cl->rd_completed_lock);
+ mei_io_rd_list_free_fp(cl, fp);
return 0;
}
@@ -1426,7 +1430,7 @@ void mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb)
}
/**
- * mei_cl_del_rd_completed - free read completed callback with lock
+ * mei_cl_del_rd_completed - unlink read completed callback with lock and free it
*
* @cl: host client
* @cb: callback block
@@ -1435,8 +1439,9 @@ void mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb)
void mei_cl_del_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb)
{
spin_lock(&cl->rd_completed_lock);
- mei_io_cb_free(cb);
+ list_del_init(&cb->list);
spin_unlock(&cl->rd_completed_lock);
+ mei_io_cb_free(cb);
}
/**
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 48ac009cbaad..bf16901b77b6 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -427,8 +427,16 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
* Dirmap accesses are allowed to toggle the CS.
* Toggling the CS during a continuous read is forbidden.
*/
- if (nbytes && req->continuous)
- return -EIO;
+ if (nbytes && req->continuous) {
+ /*
+ * Spi controller with broken support of continuous
+ * reading was detected. Disable future use of
+ * continuous reading and return -EAGAIN to retry
+ * reading within regular mode.
+ */
+ spinand->cont_read_possible = false;
+ return -EAGAIN;
+ }
}
if (req->datalen)
@@ -849,10 +857,19 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
old_stats = mtd->ecc_stats;
- if (spinand_use_cont_read(mtd, from, ops))
+ if (spinand_use_cont_read(mtd, from, ops)) {
ret = spinand_mtd_continuous_page_read(mtd, from, ops, &max_bitflips);
- else
+ if (ret == -EAGAIN && !spinand->cont_read_possible) {
+ /*
+ * Spi controller with broken support of continuous
+ * reading was detected (see spinand_read_from_cache_op()),
+ * repeat reading in regular mode.
+ */
+ ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips);
+ }
+ } else {
ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips);
+ }
if (ops->stats) {
ops->stats->uncorrectable_errors +=
@@ -1032,22 +1049,50 @@ static int spinand_mtd_block_isreserved(struct mtd_info *mtd, loff_t offs)
return ret;
}
+static struct spi_mem_dirmap_desc *spinand_create_rdesc(
+ struct spinand_device *spinand,
+ struct spi_mem_dirmap_info *info)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ struct spi_mem_dirmap_desc *desc = NULL;
+
+ if (spinand->cont_read_possible) {
+ /*
+ * spi controller may return an error if info->length is
+ * too large
+ */
+ info->length = nanddev_eraseblock_size(nand);
+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+ spinand->spimem, info);
+ }
+
+ if (IS_ERR_OR_NULL(desc)) {
+ /*
+ * continuous reading is not supported by flash or
+ * its spi controller, use regular reading
+ */
+ spinand->cont_read_possible = false;
+
+ info->length = nanddev_page_size(nand) +
+ nanddev_per_page_oobsize(nand);
+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+ spinand->spimem, info);
+ }
+
+ return desc;
+}
+
static int spinand_create_dirmap(struct spinand_device *spinand,
unsigned int plane)
{
struct nand_device *nand = spinand_to_nand(spinand);
- struct spi_mem_dirmap_info info = {
- .length = nanddev_page_size(nand) +
- nanddev_per_page_oobsize(nand),
- };
+ struct spi_mem_dirmap_info info = { 0 };
struct spi_mem_dirmap_desc *desc;
- if (spinand->cont_read_possible)
- info.length = nanddev_eraseblock_size(nand);
-
/* The plane number is passed in MSB just above the column address */
info.offset = plane << fls(nand->memorg.pagesize);
+ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
info.op_tmpl = *spinand->op_templates.update_cache;
desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
spinand->spimem, &info);
@@ -1057,8 +1102,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
spinand->dirmaps[plane].wdesc = desc;
info.op_tmpl = *spinand->op_templates.read_cache;
- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
- spinand->spimem, &info);
+ desc = spinand_create_rdesc(spinand, &info);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -1071,6 +1115,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
return 0;
}
+ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
info.op_tmpl = *spinand->op_templates.update_cache;
info.op_tmpl.data.ecc = true;
desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
@@ -1082,8 +1127,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
info.op_tmpl = *spinand->op_templates.read_cache;
info.op_tmpl.data.ecc = true;
- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
- spinand->spimem, &info);
+ desc = spinand_create_rdesc(spinand, &info);
if (IS_ERR(desc))
return PTR_ERR(desc);
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 7edf0fd58c34..ce6a3a0a2e15 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -1535,8 +1535,8 @@ void bond_alb_monitor(struct work_struct *work)
struct bonding *bond = container_of(work, struct bonding,
alb_work.work);
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
+ struct slave *slave, *curr;
struct list_head *iter;
- struct slave *slave;
if (!bond_has_slaves(bond)) {
atomic_set(&bond_info->tx_rebalance_counter, 0);
@@ -1598,9 +1598,11 @@ void bond_alb_monitor(struct work_struct *work)
* because a slave was disabled then
* it can now leave promiscuous mode.
*/
- dev_set_promiscuity(rtnl_dereference(bond->curr_active_slave)->dev,
- -1);
- bond_info->primary_is_promisc = 0;
+ curr = rtnl_dereference(bond->curr_active_slave);
+ if (bond_info->primary_is_promisc && curr) {
+ dev_set_promiscuity(curr->dev, -1);
+ bond_info->primary_is_promisc = 0;
+ }
rtnl_unlock();
rcu_read_lock();
diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h
index becd3104473c..4dbfe2373b3a 100644
--- a/drivers/net/ethernet/amd/pds_core/core.h
+++ b/drivers/net/ethernet/amd/pds_core/core.h
@@ -170,6 +170,7 @@ struct pdsc {
struct timer_list wdtimer;
unsigned int wdtimer_period;
struct work_struct health_work;
+ bool health_stopped;
struct devlink_health_reporter *fw_reporter;
u32 fw_recoveries;
diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c
index c78b17d230a8..9526223a7297 100644
--- a/drivers/net/ethernet/amd/pds_core/main.c
+++ b/drivers/net/ethernet/amd/pds_core/main.c
@@ -464,8 +464,10 @@ static void pdsc_stop_health_thread(struct pdsc *pdsc)
return;
timer_shutdown_sync(&pdsc->wdtimer);
- if (pdsc->health_work.func)
- cancel_work_sync(&pdsc->health_work);
+ if (pdsc->health_work.func && !pdsc->health_stopped) {
+ disable_work_sync(&pdsc->health_work);
+ pdsc->health_stopped = true;
+ }
}
static void pdsc_restart_health_thread(struct pdsc *pdsc)
@@ -473,6 +475,10 @@ static void pdsc_restart_health_thread(struct pdsc *pdsc)
if (pdsc->pdev->is_virtfn)
return;
+ if (pdsc->health_stopped) {
+ enable_work(&pdsc->health_work);
+ pdsc->health_stopped = false;
+ }
timer_setup(&pdsc->wdtimer, pdsc_wdtimer_cb, 0);
mod_timer(&pdsc->wdtimer, jiffies + 1);
}
@@ -544,7 +550,11 @@ static pci_ers_result_t pdsc_pci_error_detected(struct pci_dev *pdev,
pci_channel_state_t error)
{
if (error == pci_channel_io_frozen) {
+ struct pdsc *pdsc = pci_get_drvdata(pdev);
+
pdsc_reset_prepare(pdev);
+ if (!pdev->is_virtfn)
+ cancel_work_sync(&pdsc->pci_reset_work);
return PCI_ERS_RESULT_NEED_RESET;
}
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c
index d23d23bed39f..1579b19c36a7 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c
@@ -336,6 +336,35 @@ bool aq_ring_tx_clean(struct aq_ring_s *self)
return !!budget;
}
+void aq_ring_tx_deinit(struct aq_ring_s *self)
+{
+ if (!self)
+ return;
+
+ for (; self->sw_head != self->sw_tail;
+ self->sw_head = aq_ring_next_dx(self, self->sw_head)) {
+ struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head];
+ struct device *ndev = aq_nic_get_dev(self->aq_nic);
+
+ if (buff->is_mapped) {
+ if (buff->is_sop) {
+ dma_unmap_single(ndev, buff->pa, buff->len,
+ DMA_TO_DEVICE);
+ } else {
+ dma_unmap_page(ndev, buff->pa, buff->len,
+ DMA_TO_DEVICE);
+ }
+ }
+
+ if (buff->is_eop) {
+ if (buff->skb)
+ dev_kfree_skb_any(buff->skb);
+ else if (buff->xdpf)
+ xdp_return_frame(buff->xdpf);
+ }
+ }
+}
+
static void aq_rx_checksum(struct aq_ring_s *self,
struct aq_ring_buff_s *buff,
struct sk_buff *skb)
@@ -897,15 +926,29 @@ int aq_ring_rx_fill(struct aq_ring_s *self)
void aq_ring_rx_deinit(struct aq_ring_s *self)
{
- if (!self)
+ unsigned int i;
+
+ if (!self || !self->buff_ring)
return;
- for (; self->sw_head != self->sw_tail;
- self->sw_head = aq_ring_next_dx(self, self->sw_head)) {
- struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head];
+ /* Release every page still owned by the ring.
+ *
+ * Walking [sw_head, sw_tail) is not enough: refill is batched
+ * (aq_ring_rx_fill() waits for AQ_CFG_RX_REFILL_THRES free slots),
+ * so slots that were cleaned but not yet reposted accumulate in the
+ * [sw_tail, sw_head) gap, and they keep their page for reuse. Walk
+ * the whole ring and release whatever is left.
+ */
+ for (i = 0; i < self->size; i++) {
+ struct aq_ring_buff_s *buff = &self->buff_ring[i];
+
+ if (!buff->rxdata.page)
+ continue;
aq_free_rxpage(&buff->rxdata, aq_nic_get_dev(self->aq_nic));
}
+
+ self->sw_head = self->sw_tail;
}
void aq_ring_free(struct aq_ring_s *self)
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h
index d627ace850ff..67503c512676 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h
@@ -199,6 +199,7 @@ void aq_ring_update_queue_state(struct aq_ring_s *ring);
void aq_ring_queue_wake(struct aq_ring_s *ring);
void aq_ring_queue_stop(struct aq_ring_s *ring);
bool aq_ring_tx_clean(struct aq_ring_s *self);
+void aq_ring_tx_deinit(struct aq_ring_s *self);
int aq_xdp_xmit(struct net_device *dev, int num_frames,
struct xdp_frame **frames, u32 flags);
int aq_ring_rx_clean(struct aq_ring_s *self,
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c
index 9769ab4f9bef..62b5967b6dcc 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c
@@ -275,7 +275,7 @@ void aq_vec_deinit(struct aq_vec_s *self)
for (i = 0U; self->tx_rings > i; ++i) {
ring = self->ring[i];
- aq_ring_tx_clean(&ring[AQ_VEC_TX_ID]);
+ aq_ring_tx_deinit(&ring[AQ_VEC_TX_ID]);
aq_ring_rx_deinit(&ring[AQ_VEC_RX_ID]);
}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 4970e20b2cb1..2356b7ba7fcf 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -6365,22 +6365,36 @@ int bnxt_get_nr_rss_ctxs(struct bnxt *bp, int rx_rings)
static void bnxt_fill_hw_rss_tbl(struct bnxt *bp, struct bnxt_vnic_info *vnic)
{
bool no_rss = !(vnic->flags & BNXT_VNIC_RSS_FLAG);
- u16 i, j;
+ u16 i, j, min_j = bp->rx_nr_rings - 1;
+
+ if (!vnic->rss_table)
+ goto skip_rss_tbl;
/* Fill the RSS indirection table with ring group ids */
for (i = 0, j = 0; i < HW_HASH_INDEX_SIZE; i++) {
if (!no_rss)
j = bp->rss_indir_tbl[i];
+ min_j = min(j, min_j);
vnic->rss_table[i] = cpu_to_le16(vnic->fw_grp_ids[j]);
}
+
+skip_rss_tbl:
+ if (vnic->rss_table && !no_rss)
+ vnic->default_rx_ring = min_j;
+ else if (vnic->flags & BNXT_VNIC_RFS_FLAG)
+ vnic->default_rx_ring = vnic->vnic_id - 1;
+ else if ((vnic->vnic_id == 1) && BNXT_CHIP_TYPE_NITRO_A0(bp))
+ vnic->default_rx_ring = bp->rx_nr_rings - 1;
+ else
+ vnic->default_rx_ring = 0;
}
static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp,
struct bnxt_vnic_info *vnic)
{
+ u16 tbl_size, i, min_j = bp->rx_nr_rings - 1;
__le16 *ring_tbl = vnic->rss_table;
struct bnxt_rx_ring_info *rxr;
- u16 tbl_size, i;
tbl_size = bnxt_get_rxfh_indir_size(bp->dev);
@@ -6393,6 +6407,7 @@ static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp,
j = ethtool_rxfh_context_indir(vnic->rss_ctx)[i];
else
j = bp->rss_indir_tbl[i];
+ min_j = min(j, min_j);
rxr = &bp->rx_ring[j];
ring_id = rxr->rx_ring_struct.fw_ring_id;
@@ -6400,19 +6415,15 @@ static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp,
ring_id = bnxt_cp_ring_for_rx(bp, rxr);
*ring_tbl++ = cpu_to_le16(ring_id);
}
+ vnic->default_rx_ring = min_j;
}
static void
__bnxt_hwrm_vnic_set_rss(struct bnxt *bp, struct hwrm_vnic_rss_cfg_input *req,
struct bnxt_vnic_info *vnic)
{
- if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) {
- bnxt_fill_hw_rss_tbl_p5(bp, vnic);
- if (bp->flags & BNXT_FLAG_CHIP_P7)
- req->flags |= VNIC_RSS_CFG_REQ_FLAGS_IPSEC_HASH_TYPE_CFG_SUPPORT;
- } else {
- bnxt_fill_hw_rss_tbl(bp, vnic);
- }
+ if (bp->flags & BNXT_FLAG_CHIP_P7)
+ req->flags |= VNIC_RSS_CFG_REQ_FLAGS_IPSEC_HASH_TYPE_CFG_SUPPORT;
if (bp->rss_hash_delta) {
req->hash_type = cpu_to_le32(bp->rss_hash_delta);
@@ -6464,6 +6475,7 @@ static int bnxt_hwrm_vnic_set_rss_p5(struct bnxt *bp,
if (!set_rss)
return hwrm_req_send(bp, req);
+ bnxt_fill_hw_rss_tbl_p5(bp, vnic);
__bnxt_hwrm_vnic_set_rss(bp, req, vnic);
ring_tbl_map = vnic->rss_table_dma_addr;
nr_ctxs = bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings);
@@ -6601,8 +6613,9 @@ int bnxt_hwrm_vnic_cfg(struct bnxt *bp, struct bnxt_vnic_info *vnic)
return rc;
if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) {
- struct bnxt_rx_ring_info *rxr = &bp->rx_ring[0];
+ struct bnxt_rx_ring_info *rxr;
+ rxr = &bp->rx_ring[vnic->default_rx_ring];
req->default_rx_ring_id =
cpu_to_le16(rxr->rx_ring_struct.fw_ring_id);
req->default_cmpl_ring_id =
@@ -6635,13 +6648,7 @@ int bnxt_hwrm_vnic_cfg(struct bnxt *bp, struct bnxt_vnic_info *vnic)
req->cos_rule = cpu_to_le16(0xffff);
}
- if (vnic->flags & BNXT_VNIC_RSS_FLAG)
- ring = 0;
- else if (vnic->flags & BNXT_VNIC_RFS_FLAG)
- ring = vnic->vnic_id - 1;
- else if ((vnic->vnic_id == 1) && BNXT_CHIP_TYPE_NITRO_A0(bp))
- ring = bp->rx_nr_rings - 1;
-
+ ring = vnic->default_rx_ring;
grp_idx = bp->rx_ring[ring].bnapi->index;
req->dflt_ring_grp = cpu_to_le16(bp->grp_info[grp_idx].fw_grp_id);
req->lb_rule = cpu_to_le16(0xffff);
@@ -10299,6 +10306,7 @@ static int __bnxt_setup_vnic(struct bnxt *bp, struct bnxt_vnic_info *vnic)
}
skip_rss_ctx:
+ bnxt_fill_hw_rss_tbl(bp, vnic);
/* configure default vnic, ring grp */
rc = bnxt_hwrm_vnic_cfg(bp, vnic);
if (rc) {
@@ -10523,6 +10531,11 @@ static int bnxt_set_vnic_mru_p5(struct bnxt *bp, struct bnxt_vnic_info *vnic,
vnic->vnic_id, rc);
return rc;
}
+ if (rxr_id == vnic->default_rx_ring) {
+ rc = bnxt_hwrm_vnic_cfg(bp, vnic);
+ if (rc)
+ return rc;
+ }
}
vnic->mru = mru;
bnxt_hwrm_vnic_update(bp, vnic,
@@ -10604,6 +10617,9 @@ static int bnxt_setup_nitroa0_vnic(struct bnxt *bp)
return rc;
}
+ /* Setup the proper default RX ring */
+ bnxt_fill_hw_rss_tbl(bp, vnic);
+
rc = bnxt_hwrm_vnic_cfg(bp, vnic);
if (rc) {
netdev_err(bp->dev, "Cannot allocate special vnic for NS2 A0: %x\n",
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 37fb74f18eee..f50fe72f83ac 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -1300,6 +1300,7 @@ struct bnxt_vnic_info {
#define BNXT_VNIC_RSSCTX_FLAG 0x40
struct ethtool_rxfh_context *rss_ctx;
u32 vnic_id;
+ u16 default_rx_ring;
};
struct bnxt_rss_ctx {
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
index 6dfa0ab74c33..02276b9ac5c6 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
@@ -494,12 +494,15 @@ static int bnxt_ptp_enable(struct ptp_clock_info *ptp_info,
return rc;
case PTP_CLK_REQ_PPS:
/* Configure PHC PPS IN */
- rc = bnxt_ptp_cfg_pin(bp, 0, BNXT_PPS_PIN_PPS_IN);
+ pin_id = 0;
+ if (!on)
+ break;
+ rc = bnxt_ptp_cfg_pin(bp, pin_id, BNXT_PPS_PIN_PPS_IN);
if (rc)
return rc;
rc = bnxt_ptp_cfg_event(bp, BNXT_PPS_EVENT_INTERNAL);
if (!rc)
- ptp->pps_info.pins[0].event = BNXT_PPS_EVENT_INTERNAL;
+ ptp->pps_info.pins[pin_id].event = BNXT_PPS_EVENT_INTERNAL;
return rc;
default:
netdev_err(ptp->bp->dev, "Unrecognized PIN function\n");
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index a432783756d8..e474cef72063 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -3141,8 +3141,9 @@ static void enic_remove(struct pci_dev *pdev)
if (netdev) {
struct enic *enic = netdev_priv(netdev);
- cancel_work_sync(&enic->reset);
- cancel_work_sync(&enic->change_mtu_work);
+ disable_work_sync(&enic->reset);
+ disable_work_sync(&enic->tx_hang_reset);
+ disable_work_sync(&enic->change_mtu_work);
unregister_netdev(netdev);
enic_dev_deinit(enic);
vnic_dev_close(enic->vdev);
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index d8189c433847..22cd47d19ce2 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -3323,8 +3323,15 @@ static void fec_enet_free_buffers(struct net_device *ndev)
for (q = 0; q < fep->num_rx_queues; q++) {
rxq = fep->rx_queue[q];
- for (i = 0; i < rxq->bd.ring_size; i++)
- page_pool_put_full_page(rxq->page_pool, rxq->rx_skb_info[i].page, false);
+ for (i = 0; i < rxq->bd.ring_size; i++) {
+ struct page *page = rxq->rx_skb_info[i].page;
+
+ if (!page)
+ continue;
+
+ page_pool_put_full_page(rxq->page_pool, page, false);
+ rxq->rx_skb_info[i].page = NULL;
+ }
for (i = 0; i < XDP_STATS_TOTAL; i++)
rxq->stats[i] = 0;
diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
index 1a972b093a42..cc1d04d05e4b 100644
--- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
+++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
@@ -1282,7 +1282,6 @@ static void hix5hd2_dev_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct hix5hd2_priv *priv = netdev_priv(ndev);
- netif_napi_del(&priv->napi);
unregister_netdev(ndev);
mdiobus_unregister(priv->bus);
mdiobus_free(priv->bus);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 79858b06c97b..786aefc3d107 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -11930,12 +11930,8 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
if (ret)
goto err_ptp_uninit;
- if (hdev->hw.mac.media_type != HNAE3_MEDIA_TYPE_COPPER) {
+ if (hdev->hw.mac.media_type != HNAE3_MEDIA_TYPE_COPPER)
hdev->hw.mac.req_autoneg = hdev->hw.mac.autoneg;
- if (hdev->hw.mac.autoneg == AUTONEG_DISABLE &&
- hdev->hw.mac.speed != SPEED_UNKNOWN)
- hdev->hw.mac.req_speed = hdev->hw.mac.speed;
- }
ret = hclge_set_autoneg_speed_dup(hdev);
if (ret) {
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
index b26fc848430f..14f3d01e440c 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
@@ -78,10 +78,12 @@ static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst,
if (burst) {
*burst_exp = ilog2(burst) ? ilog2(burst) - 1 : 0;
tmp = burst - rounddown_pow_of_two(burst);
- if (burst < max_mantissa)
+ if (burst <= max_mantissa) {
*burst_mantissa = tmp * 2;
- else
+ } else {
+ WARN_ON(*burst_exp < 7);
*burst_mantissa = tmp / (1ULL << (*burst_exp - 7));
+ }
} else {
*burst_exp = MAX_BURST_EXPONENT;
*burst_mantissa = max_mantissa;
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_pci.c b/drivers/net/ethernet/marvell/prestera/prestera_pci.c
index 35857dc19542..5db2c9e5e077 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_pci.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_pci.c
@@ -684,6 +684,9 @@ static int prestera_fw_hdr_parse(struct prestera_fw *fw)
struct prestera_fw_header *hdr;
u32 magic;
+ if (fw->bin->size < sizeof(*hdr))
+ return -EINVAL;
+
hdr = (struct prestera_fw_header *)fw->bin->data;
magic = be32_to_cpu(hdr->magic_number);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
index 0b82a6a133d6..74f68b70ad1c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
@@ -1025,13 +1025,11 @@ struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev)
tracer = kvzalloc(sizeof(*tracer), GFP_KERNEL);
if (!tracer)
- return ERR_PTR(-ENOMEM);
+ return NULL;
tracer->work_queue = create_singlethread_workqueue("mlx5_fw_tracer");
- if (!tracer->work_queue) {
- err = -ENOMEM;
+ if (!tracer->work_queue)
goto free_tracer;
- }
tracer->dev = dev;
@@ -1073,7 +1071,7 @@ struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev)
destroy_workqueue(tracer->work_queue);
free_tracer:
kvfree(tracer);
- return ERR_PTR(err);
+ return NULL;
}
static int fw_tracer_event(struct notifier_block *nb, unsigned long action, void *data);
@@ -1084,7 +1082,7 @@ int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer)
struct mlx5_core_dev *dev;
int err;
- if (IS_ERR_OR_NULL(tracer))
+ if (!tracer)
return 0;
if (!tracer->str_db.loaded)
@@ -1134,7 +1132,7 @@ int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer)
/* Stop tracer + Cleanup HW resources */
void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer)
{
- if (IS_ERR_OR_NULL(tracer))
+ if (!tracer)
return;
mutex_lock(&tracer->state_lock);
@@ -1163,7 +1161,7 @@ void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer)
/* Free software resources (Buffers, etc ..) */
void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer)
{
- if (IS_ERR_OR_NULL(tracer))
+ if (!tracer)
return;
mlx5_core_dbg(tracer->dev, "FWTracer: Destroy\n");
@@ -1215,7 +1213,7 @@ int mlx5_fw_tracer_reload(struct mlx5_fw_tracer *tracer)
struct mlx5_core_dev *dev;
int err;
- if (IS_ERR_OR_NULL(tracer))
+ if (!tracer)
return 0;
dev = tracer->dev;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h
index efb34de4cb7a..692ac2f21d94 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h
@@ -30,6 +30,7 @@ enum {
MLX5E_TC_FLOW_FLAG_FAILED = MLX5E_TC_FLOW_BASE + 9,
MLX5E_TC_FLOW_FLAG_SAMPLE = MLX5E_TC_FLOW_BASE + 10,
MLX5E_TC_FLOW_FLAG_USE_ACT_STATS = MLX5E_TC_FLOW_BASE + 11,
+ MLX5E_TC_FLOW_FLAG_PEER = MLX5E_TC_FLOW_BASE + 12,
};
struct mlx5e_tc_flow_parse_attr {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index 8878990254f4..a5ae17931067 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -2161,7 +2161,8 @@ static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
if (mlx5e_is_eswitch_flow(flow)) {
struct mlx5_devcom_comp_dev *devcom = flow->priv->mdev->priv.eswitch->devcom;
- if (!mlx5_devcom_for_each_peer_begin(devcom)) {
+ if (flow_flag_test(flow, PEER) ||
+ !mlx5_devcom_for_each_peer_begin(devcom)) {
mlx5e_tc_del_fdb_flow(priv, flow);
return;
}
@@ -4605,6 +4606,7 @@ static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f,
else
in_mdev = priv->mdev;
+ flow_flags |= BIT(MLX5E_TC_FLOW_FLAG_PEER);
parse_attr = flow->attr->parse_attr;
peer_flow = __mlx5e_add_fdb_flow(peer_priv, f, flow_flags,
parse_attr->filter_dev,
diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c
index 5d7d11d75b0b..ceaba08cd378 100644
--- a/drivers/net/thunderbolt/main.c
+++ b/drivers/net/thunderbolt/main.c
@@ -389,11 +389,16 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout)
break;
}
- tb_ring_stop(net->rx_ring.ring);
- tb_ring_stop(net->tx_ring.ring);
- tbnet_free_buffers(&net->rx_ring);
- tbnet_free_buffers(&net->tx_ring);
-
+ /* Tear the paths down before stopping the rings. This mirrors
+ * tbnet_connected_work(), which enables the paths last so the
+ * Rx ring is primed before packets can arrive. Stopping a
+ * ring zeroes its descriptor base and tbnet_free_buffers()
+ * unmaps and frees the frame buffers, leaving anything still
+ * in flight with nowhere to drain to;
+ * __tb_path_deactivate_hop() then waits for the hop's
+ * 'pending' bit, which on some host routers never clears in
+ * that state.
+ */
ret = tb_xdomain_disable_paths(net->xd,
net->local_transmit_path,
net->tx_ring.ring->hop,
@@ -402,6 +407,11 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout)
if (ret)
netdev_warn(net->dev, "failed to disable DMA paths\n");
+ tb_ring_stop(net->rx_ring.ring);
+ tb_ring_stop(net->tx_ring.ring);
+ tbnet_free_buffers(&net->rx_ring);
+ tbnet_free_buffers(&net->tx_ring);
+
tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path);
net->remote_transmit_path = 0;
}
@@ -928,12 +938,8 @@ static int tbnet_open(struct net_device *dev)
netif_carrier_off(dev);
- flags = RING_FLAG_FRAME;
- /* Only enable full E2E if the other end supports it too */
- if (tbnet_e2e && net->svc->prtcstns & TBNET_E2E)
- flags |= RING_FLAG_E2E;
-
- ring = tb_ring_alloc_tx(xd->tb->nhi, -1, TBNET_RING_SIZE, flags);
+ ring = tb_ring_alloc_tx(xd->tb->nhi, -1, TBNET_RING_SIZE,
+ RING_FLAG_FRAME);
if (!ring) {
netdev_err(dev, "failed to allocate Tx ring\n");
return -ENOMEM;
@@ -952,6 +958,11 @@ static int tbnet_open(struct net_device *dev)
sof_mask = BIT(TBIP_PDF_FRAME_START);
eof_mask = BIT(TBIP_PDF_FRAME_END);
+ flags = RING_FLAG_FRAME;
+ /* Only enable full E2E if the other end supports it too */
+ if (tbnet_e2e && net->svc->prtcstns & TBNET_E2E)
+ flags |= RING_FLAG_E2E;
+
ring = tb_ring_alloc_rx(xd->tb->nhi, -1, TBNET_RING_SIZE, flags,
net->tx_ring.ring->hop, sof_mask,
eof_mask, tbnet_start_poll, net);
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index b034ef8a73ea..cf40b604f135 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -1493,8 +1493,10 @@ ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
headroom = skb_headroom(skb) - 8;
- if ((dev->net->features & NETIF_F_SG) && skb_linearize(skb))
+ if ((dev->net->features & NETIF_F_SG) && skb_linearize(skb)) {
+ dev_kfree_skb_any(skb);
return NULL;
+ }
if ((skb_header_cloned(skb) || headroom < 0) &&
pskb_expand_head(skb, headroom < 0 ? 8 : 0, 0, GFP_ATOMIC)) {
diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
index a19789b57190..a86d0a4dbd28 100644
--- a/drivers/net/usb/ipheth.c
+++ b/drivers/net/usb/ipheth.c
@@ -490,6 +490,7 @@ static int ipheth_open(struct net_device *net)
if (retval)
return retval;
+ enable_delayed_work(&dev->carrier_work);
schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);
return retval;
}
@@ -499,7 +500,11 @@ static int ipheth_close(struct net_device *net)
struct ipheth_device *dev = netdev_priv(net);
netif_stop_queue(net);
- cancel_delayed_work_sync(&dev->carrier_work);
+ /* A TX URB can still complete with an error after this point and
+ * try to re-arm the carrier work. Disable it instead of cancelling
+ * it, so that such a schedule_delayed_work() is a no-op.
+ */
+ disable_delayed_work_sync(&dev->carrier_work);
return 0;
}
@@ -633,6 +638,10 @@ static int ipheth_probe(struct usb_interface *intf,
}
INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work);
+ /* Armed only between ipheth_open() and ipheth_close(). Start out
+ * disabled so the enable/disable counts balance from the first open.
+ */
+ disable_delayed_work(&dev->carrier_work);
retval = ipheth_alloc_urbs(dev);
if (retval) {
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 918a7130011d..2feb694f58bb 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -865,18 +865,24 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq,
skb_reset_mac_header(skb);
- /* check if bpf_xdp_adjust_tail was used */
- off = xdp->data_end - orig_data_end;
- if (off != 0)
- __skb_put(skb, off); /* positive on grow, negative on shrink */
-
/* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers
- * (e.g. bpf_xdp_adjust_tail), we need to update data_len here.
+ * (e.g. bpf_xdp_adjust_tail). Remove the old fragment contribution
+ * from skb->len before updating data_len, then add the new one back.
*/
- if (xdp_buff_has_frags(xdp))
+ skb->len -= skb->data_len;
+ if (xdp_buff_has_frags(xdp)) {
skb->data_len = skb_shinfo(skb)->xdp_frags_size;
- else
+ skb->len += skb->data_len;
+ } else {
skb->data_len = 0;
+ }
+
+ /* Synchronize the skb tail with XDP's updated linear area. */
+ off = xdp->data_end - orig_data_end;
+ if (off != 0) {
+ skb_set_tail_pointer(skb, xdp->data_end - xdp->data);
+ skb->len += off; /* positive on grow, negative on shrink */
+ }
skb->protocol = eth_type_trans(skb, rq->dev);
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index fdc46f52c7fc..3c62957907ba 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -4462,7 +4462,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
if (change_igmp && vxlan_addr_multicast(&dst->remote_ip))
err = vxlan_multicast_leave(vxlan);
- if (conf.age_interval != vxlan->cfg.age_interval)
+ if (netif_running(dev) && conf.age_interval != vxlan->cfg.age_interval)
mod_timer(&vxlan->age_timer, jiffies);
netdev_adjacent_change_commit(dst->remote_dev, lowerdev, dev);
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 2edc9cea2bda..2ba16adaf731 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -811,7 +811,7 @@ static int nvmem_validate_keepouts(struct nvmem_device *nvmem)
return 0;
}
-static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
+int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
{
struct device *dev = &nvmem->dev;
struct device_node *child;
@@ -863,29 +863,13 @@ static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_nod
return 0;
}
+EXPORT_SYMBOL_GPL(nvmem_add_cells_from_dt);
static int nvmem_add_cells_from_legacy_of(struct nvmem_device *nvmem)
{
return nvmem_add_cells_from_dt(nvmem, nvmem->dev.of_node);
}
-static int nvmem_add_cells_from_fixed_layout(struct nvmem_device *nvmem)
-{
- struct device_node *layout_np;
- int err = 0;
-
- layout_np = of_nvmem_layout_get_container(nvmem);
- if (!layout_np)
- return 0;
-
- if (of_device_is_compatible(layout_np, "fixed-layout"))
- err = nvmem_add_cells_from_dt(nvmem, layout_np);
-
- of_node_put(layout_np);
-
- return err;
-}
-
int nvmem_layout_register(struct nvmem_layout *layout)
{
int ret;
@@ -1038,10 +1022,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
goto err_remove_cells;
}
- rval = nvmem_add_cells_from_fixed_layout(nvmem);
- if (rval)
- goto err_remove_cells;
-
dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
rval = device_add(&nvmem->dev);
diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h
index 18fed57270e5..f6d452b6a28f 100644
--- a/drivers/nvmem/internals.h
+++ b/drivers/nvmem/internals.h
@@ -35,6 +35,8 @@ struct nvmem_device {
bool sysfs_cells_populated;
};
+int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np);
+
#if IS_ENABLED(CONFIG_OF)
int nvmem_layout_bus_register(void);
void nvmem_layout_bus_unregister(void);
diff --git a/drivers/nvmem/layouts.c b/drivers/nvmem/layouts.c
index 7ebe53249035..0e84d65b42d4 100644
--- a/drivers/nvmem/layouts.c
+++ b/drivers/nvmem/layouts.c
@@ -125,11 +125,6 @@ static int nvmem_layout_create_device(struct nvmem_device *nvmem,
return 0;
}
-static const struct of_device_id of_nvmem_layout_skip_table[] = {
- { .compatible = "fixed-layout", },
- {}
-};
-
static int nvmem_layout_bus_populate(struct nvmem_device *nvmem,
struct device_node *layout_dn)
{
@@ -142,12 +137,6 @@ static int nvmem_layout_bus_populate(struct nvmem_device *nvmem,
return 0;
}
- /* Fixed layouts are parsed manually somewhere else for now */
- if (of_match_node(of_nvmem_layout_skip_table, layout_dn)) {
- pr_debug("%s() - skipping %pOF node\n", __func__, layout_dn);
- return 0;
- }
-
if (of_node_check_flag(layout_dn, OF_POPULATED_BUS)) {
pr_debug("%s() - skipping %pOF, already populated\n",
__func__, layout_dn);
diff --git a/drivers/nvmem/layouts/Makefile b/drivers/nvmem/layouts/Makefile
index 4940c9db0665..dd6c6c70b1a9 100644
--- a/drivers/nvmem/layouts/Makefile
+++ b/drivers/nvmem/layouts/Makefile
@@ -3,6 +3,7 @@
# Makefile for nvmem layouts.
#
+obj-$(CONFIG_NVMEM_LAYOUTS) += fixed-layout.o
obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o
diff --git a/drivers/nvmem/layouts/fixed-layout.c b/drivers/nvmem/layouts/fixed-layout.c
new file mode 100644
index 000000000000..635d448b3dd2
--- /dev/null
+++ b/drivers/nvmem/layouts/fixed-layout.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2026 Bootlin
+ *
+ * Authors: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+ */
+
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+
+#include "../internals.h"
+
+static int fixed_layout_add_cells(struct nvmem_layout *layout)
+{
+ struct device_node *np;
+ int ret;
+
+ np = of_nvmem_layout_get_container(layout->nvmem);
+ if (!np)
+ return -ENOENT;
+
+ ret = nvmem_add_cells_from_dt(layout->nvmem, np);
+ of_node_put(np);
+
+ return ret;
+}
+
+static int fixed_layout_probe(struct nvmem_layout *layout)
+{
+ layout->add_cells = fixed_layout_add_cells;
+
+ return nvmem_layout_register(layout);
+}
+
+static void fixed_layout_remove(struct nvmem_layout *layout)
+{
+ nvmem_layout_unregister(layout);
+}
+
+static const struct of_device_id fixed_layout_of_match_table[] = {
+ { .compatible = "fixed-layout", },
+ {},
+};
+
+static struct nvmem_layout_driver fixed_layout_layout = {
+ .driver = {
+ .name = "fixed-layout",
+ .of_match_table = fixed_layout_of_match_table,
+ },
+ .probe = fixed_layout_probe,
+ .remove = fixed_layout_remove,
+};
+module_nvmem_layout_driver(fixed_layout_layout);
+
+MODULE_AUTHOR("Mathieu Dubois-Briand");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(of, fixed_layout_of_match_table);
+MODULE_DESCRIPTION("NVMEM fixed-layout driver");
diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
index 17e27879fd62..6fd79907f17e 100644
--- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c
+++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
@@ -1478,7 +1478,7 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev,
break;
default:
- return -EOPNOTSUPP;
+ return -ENOTSUPP;
}
}
@@ -1560,7 +1560,7 @@ static int rzg2l_pinctrl_pinconf_group_get(struct pinctrl_dev *pctldev,
/* Check config matching between to pin */
if (i && prev_config != *config)
- return -EOPNOTSUPP;
+ return -ENOTSUPP;
prev_config = *config;
}
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index bd9919c01e50..fce46b88458f 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -1914,9 +1914,11 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
if (err)
return err;
+ snprintf(buf, sizeof(buf), "%.*s", OCP_BOARD_ID_LEN,
+ (const char *)bp->board_id);
err = devlink_info_version_fixed_put(req,
DEVLINK_INFO_VERSION_GENERIC_BOARD_ID,
- bp->board_id);
+ buf);
if (err)
return err;
diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c
index 6790afd517ec..48d36d5a8711 100644
--- a/drivers/s390/crypto/zcrypt_ccamisc.c
+++ b/drivers/s390/crypto/zcrypt_ccamisc.c
@@ -938,7 +938,8 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain,
const u8 *clr_key_value,
int clr_key_bit_size,
u8 *key_token,
- int *key_token_size)
+ int *key_token_size,
+ bool scrub)
{
int rc, n;
u8 *mem, *ptr;
@@ -1077,7 +1078,7 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain,
*key_token_size = t->len;
out:
- free_cprbmem(mem, PARMBSIZE, 0);
+ free_cprbmem(mem, PARMBSIZE, scrub);
return rc;
}
@@ -1120,28 +1121,32 @@ int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags,
* 4/4 COMPLETE the secure cipher key import
*/
rc = _ip_cprb_helper(card, dom, "AES ", "FIRST ", "MIN3PART",
- exorbuf, keybitsize, token, &tokensize);
+ exorbuf, keybitsize, token, &tokensize,
+ true);
if (rc) {
ZCRYPT_DBF_ERR("%s clear key import 1/4 with CSNBKPI2 failed, rc=%d\n",
__func__, rc);
goto out;
}
rc = _ip_cprb_helper(card, dom, "AES ", "ADD-PART", NULL,
- clrkey, keybitsize, token, &tokensize);
+ clrkey, keybitsize, token, &tokensize,
+ true);
if (rc) {
ZCRYPT_DBF_ERR("%s clear key import 2/4 with CSNBKPI2 failed, rc=%d\n",
__func__, rc);
goto out;
}
rc = _ip_cprb_helper(card, dom, "AES ", "ADD-PART", NULL,
- exorbuf, keybitsize, token, &tokensize);
+ exorbuf, keybitsize, token, &tokensize,
+ true);
if (rc) {
ZCRYPT_DBF_ERR("%s clear key import 3/4 with CSNBKPI2 failed, rc=%d\n",
__func__, rc);
goto out;
}
rc = _ip_cprb_helper(card, dom, "AES ", "COMPLETE", NULL,
- NULL, keybitsize, token, &tokensize);
+ NULL, keybitsize, token, &tokensize,
+ true);
if (rc) {
ZCRYPT_DBF_ERR("%s clear key import 4/4 with CSNBKPI2 failed, rc=%d\n",
__func__, rc);
@@ -1158,7 +1163,8 @@ int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags,
*keybufsize = tokensize;
out:
- kfree(token);
+ memzero_explicit(exorbuf, sizeof(exorbuf));
+ kfree_sensitive(token);
return rc;
}
EXPORT_SYMBOL(cca_clr2cipherkey);
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 9a194cf64528..de54596bbae9 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -3772,8 +3772,8 @@ static bool comp_write_worker(struct sdeb_store_info *sip, u64 lba, u32 num,
if (!res)
return res;
if (rest)
- res = memcmp(fsp, arr + ((num - rest) * lb_size),
- rest * lb_size);
+ res = !memcmp(fsp, arr + ((num - rest) * lb_size),
+ rest * lb_size);
if (!res)
return res;
if (compare_only)
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index e26363ae7489..bc7479c834ff 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -746,8 +746,12 @@ static void dspi_setup_accel(struct fsl_dspi *dspi)
struct spi_transfer *xfer = dspi->cur_transfer;
bool odd = !!(dspi->len & 1);
- /* No accel for frames not multiple of 8 bits at the moment */
- if (xfer->bits_per_word % 8)
+ /*
+ * No accel for DMA transfers or frames not multiples of 8 bits at the
+ * moment.
+ */
+ if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE ||
+ xfer->bits_per_word % 8)
goto no_accel;
if (!odd && dspi->len <= dspi->devtype_data->fifo_size * 2) {
@@ -756,10 +760,7 @@ static void dspi_setup_accel(struct fsl_dspi *dspi)
dspi->oper_bits_per_word = 8;
} else {
/* Start off with maximum supported by hardware */
- if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE)
- dspi->oper_bits_per_word = 32;
- else
- dspi->oper_bits_per_word = 16;
+ dspi->oper_bits_per_word = 32;
/*
* And go down only if the buffer can't be sent with
diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
index fe458912e0ec..56938ff47235 100644
--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
@@ -372,6 +372,9 @@ unsigned char *rtw_get_wpa_ie(unsigned char *pie, int *wpa_ie_len, int limit)
pbuf = rtw_get_ie(pbuf, WLAN_EID_VENDOR_SPECIFIC, &len, limit_new);
if (pbuf) {
+ if (len < 6)
+ goto check_next_ie;
+
/* check if oui matches... */
if (memcmp((pbuf + 2), wpa_oui_type, sizeof(wpa_oui_type)))
goto check_next_ie;
diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
index 41fda671036b..22c29d1955f5 100644
--- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
+++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
@@ -904,7 +904,7 @@ unsigned int OnAuthClient(struct adapter *padapter, union recv_frame *precv_fram
p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + _AUTH_IE_OFFSET_, WLAN_EID_CHALLENGE, (int *)&len,
pkt_len - WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_);
- if (!p)
+ if (!p || len != WLAN_AUTH_CHALLENGE_LEN)
goto authclnt_fail;
memcpy((void *)(pmlmeinfo->chg_txt), (void *)(p + 2), len);
diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c
index c71394214c5f..57fb1f2c1152 100644
--- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c
+++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c
@@ -730,6 +730,9 @@ int WMM_param_handler(struct adapter *padapter, struct ndis_80211_var_ie *pIE)
return false;
}
+ if (pIE->length != WLAN_WMM_LEN)
+ return false;
+
if (!memcmp(&(pmlmeinfo->WMM_param), (pIE->data + 6), sizeof(struct WMM_para_element)))
return false;
else
diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
index 96d4da546725..1b1b29e81de7 100644
--- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
+++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
@@ -2041,6 +2041,8 @@ static netdev_tx_t rtw_cfg80211_monitor_if_xmit_entry(struct sk_buff *skb, struc
/* Skip the ratio tap header */
skb_pull(skb, rtap_len);
+ if (skb->len < dot11_hdr_len)
+ goto fail;
dot11_hdr = (struct ieee80211_hdr *)skb->data;
frame_control = le16_to_cpu(dot11_hdr->frame_control);
@@ -2053,6 +2055,8 @@ static netdev_tx_t rtw_cfg80211_monitor_if_xmit_entry(struct sk_buff *skb, struc
qos_len = 2;
if ((frame_control & 0x0300) == 0x0300)
dot11_hdr_len += 6;
+ if (skb->len < dot11_hdr_len + qos_len + snap_len)
+ goto fail;
memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr));
memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr));
diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
index 112188c15c30..3162a1a8cbb6 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -215,7 +215,8 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
if (new_hwmon_device)
hwmon_device_unregister(hwmon->device);
free_mem:
- kfree(hwmon);
+ if (new_hwmon_device)
+ kfree(hwmon);
return result;
}
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c
index eb241b270f79..6e76c3432a9a 100644
--- a/drivers/thunderbolt/eeprom.c
+++ b/drivers/thunderbolt/eeprom.c
@@ -392,9 +392,16 @@ static int tb_drom_parse_entry_port(struct tb_switch *sw,
return -EIO;
}
port->link_nr = entry->link_nr;
- if (entry->has_dual_link_port)
+ if (entry->has_dual_link_port) {
+ if (entry->dual_link_port_nr > sw->config.max_port_number) {
+ tb_sw_warn(sw,
+ "port entry has invalid dual link port number %u\n",
+ entry->dual_link_port_nr);
+ return -EIO;
+ }
port->dual_link_port =
&port->sw->ports[entry->dual_link_port_nr];
+ }
}
return 0;
}
diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c
index 7859bccc592d..a29cd07f5187 100644
--- a/drivers/thunderbolt/icm.c
+++ b/drivers/thunderbolt/icm.c
@@ -2291,7 +2291,7 @@ static int icm_usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata,
if (tx_data_len) {
request.data_len_valid |= ICM_USB4_SWITCH_DATA_VALID;
if (tx_data_len < ARRAY_SIZE(request.data))
- request.data_len_valid =
+ request.data_len_valid |=
tx_data_len & ICM_USB4_SWITCH_DATA_LEN_MASK;
memcpy(request.data, tx_data, tx_data_len * sizeof(u32));
}
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 4ef8e67e9987..afcba22f5013 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -597,7 +597,7 @@ static int tb_consumed_dp_bandwidth(struct tb *tb,
int *consumed_up,
int *consumed_down)
{
- int group_reserved[MAX_GROUPS] = {};
+ int group_reserved[MAX_GROUPS + 1] = {};
struct tb_cm *tcm = tb_priv(tb);
struct tb_tunnel *tunnel;
bool downstream;
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 3b6452e759d5..5a83e5269b41 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -211,11 +211,12 @@ void serial8250_rx_dma_flush(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
- if (dma->rx_running) {
- dmaengine_pause(dma->rxchan);
- __dma_rx_complete(p);
- dmaengine_terminate_async(dma->rxchan);
- }
+ if (!dma || !dma->rxchan || !dma->rx_running)
+ return;
+
+ dmaengine_pause(dma->rxchan);
+ __dma_rx_complete(p);
+ dmaengine_terminate_async(dma->rxchan);
}
EXPORT_SYMBOL_GPL(serial8250_rx_dma_flush);
@@ -324,6 +325,7 @@ void serial8250_release_dma(struct uart_8250_port *p)
/* Release RX resources */
dmaengine_terminate_sync(dma->rxchan);
+ dma->rx_running = 0;
dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf,
dma->rx_addr);
dma_release_channel(dma->rxchan);
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index cf705b01e8bd..6e5b31ebd5d6 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -145,6 +145,7 @@ static const struct uart_ops qcom_geni_uart_pops;
static struct uart_driver qcom_geni_console_driver;
static struct uart_driver qcom_geni_uart_driver;
+static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport);
static void __qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport);
static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport);
static int qcom_geni_serial_port_setup(struct uart_port *uport);
@@ -600,35 +601,34 @@ static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
return !readl(uport->membase + SE_GENI_TX_FIFO_STATUS);
}
+static void qcom_geni_serial_flush_buffer_dma(struct uart_port *uport)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
+
+ qcom_geni_serial_stop_tx_dma(uport);
+ port->tx_remaining = 0;
+ port->tx_queued = 0;
+}
+
static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
- bool done;
- if (!qcom_geni_serial_main_active(uport))
- return;
+ if (qcom_geni_serial_main_active(uport))
+ __qcom_geni_serial_cancel_tx_cmd(uport);
if (port->tx_dma_addr) {
+ writel(1, uport->membase + SE_DMA_TX_FSM_RST);
+ if (!qcom_geni_serial_poll_bit(uport, SE_DMA_TX_IRQ_STAT,
+ TX_RESET_DONE, true))
+ dev_err_ratelimited(uport->dev, "TX DMA reset failed");
+ writel(TX_RESET_DONE | TX_DMA_DONE,
+ uport->membase + SE_DMA_TX_IRQ_CLR);
+
geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr,
port->tx_remaining);
port->tx_dma_addr = 0;
- port->tx_remaining = 0;
}
-
- geni_se_cancel_m_cmd(&port->se);
-
- done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
- M_CMD_CANCEL_EN, true);
- if (!done) {
- geni_se_abort_m_cmd(&port->se);
- done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
- M_CMD_ABORT_EN, true);
- if (!done)
- dev_err_ratelimited(uport->dev, "M_CMD_ABORT_EN not set");
- writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
- }
-
- writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
}
static void qcom_geni_serial_start_tx_dma(struct uart_port *uport)
@@ -1140,7 +1140,7 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport)
uart_port_unlock_irq(uport);
}
-static void qcom_geni_serial_flush_buffer(struct uart_port *uport)
+static void qcom_geni_serial_flush_buffer_fifo(struct uart_port *uport)
{
qcom_geni_serial_cancel_tx_cmd(uport);
}
@@ -1614,7 +1614,7 @@ static const struct uart_ops qcom_geni_console_pops = {
.request_port = qcom_geni_serial_request_port,
.config_port = qcom_geni_serial_config_port,
.shutdown = qcom_geni_serial_shutdown,
- .flush_buffer = qcom_geni_serial_flush_buffer,
+ .flush_buffer = qcom_geni_serial_flush_buffer_fifo,
.type = qcom_geni_serial_get_type,
.set_mctrl = qcom_geni_serial_set_mctrl,
.get_mctrl = qcom_geni_serial_get_mctrl,
@@ -1637,6 +1637,7 @@ static const struct uart_ops qcom_geni_uart_pops = {
.request_port = qcom_geni_serial_request_port,
.config_port = qcom_geni_serial_config_port,
.shutdown = qcom_geni_serial_shutdown,
+ .flush_buffer = qcom_geni_serial_flush_buffer_dma,
.type = qcom_geni_serial_get_type,
.set_mctrl = qcom_geni_serial_set_mctrl,
.get_mctrl = qcom_geni_serial_get_mctrl,
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 00caf1c2bcee..5d1e107574d1 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -1403,7 +1403,7 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw)
struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down };
int rc;
- tty = vc->port.tty;
+ tty = tty_port_tty_get(&vc->port);
if (tty && (!tty->driver_data)) {
/* No driver data? Strange. Okay we fix it then. */
@@ -1463,9 +1463,12 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw)
* characters get aren't echoed locally. This makes key repeat
* usable with slow applications and under heavy loads.
*/
+ tty_kref_put(tty);
return;
}
+ tty_kref_put(tty);
+
param.shift = shift_final = (shift_state | kbd->slockstate) ^ kbd->lockstate;
param.ledstate = kbd->ledflagstate;
key_map = key_maps[shift_final];
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index da82a79c1511..1e8e5c161124 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -408,6 +408,8 @@ static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd,
/* this could be folded into KDSKBMODE, but for compatibility
reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */
case KDSKBMETA:
+ if (!perm)
+ return -EPERM;
return vt_do_kdskbmeta(console, arg);
case KDGKBMETA:
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 08faf82ec31d..cf506afaaa0a 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -700,6 +700,8 @@ static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
ret = offd;
usb_dbg(instance->usbatm, "cm %#x\n", cm);
fail:
+ if (ret < 0)
+ usb_kill_urb(instance->rcv_urb);
mutex_unlock(&instance->cm_serialize);
err:
return ret;
diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c
index fb192b120d77..89d3d8167e59 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.c
+++ b/drivers/usb/cdns3/cdnsp-gadget.c
@@ -154,9 +154,9 @@ static void cdnsp_set_apb_timeout_value(struct cdnsp_device *pdev)
offset = cdnsp_find_next_ext_cap(base, offset, D_XEC_PRE_REGS_CAP);
reg = base + offset + REG_CHICKEN_BITS_3_OFFSET;
- val = le32_to_cpu(readl(reg));
+ val = readl(reg);
val = CHICKEN_APB_TIMEOUT_SET(val, cdns->override_apb_timeout);
- writel(cpu_to_le32(val), reg);
+ writel(val, reg);
}
static void cdnsp_set_chicken_bits_2(struct cdnsp_device *pdev, u32 bit)
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index a6a14500b650..1b47fbe989e1 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -1171,7 +1171,7 @@ static int ncm_unwrap_ntb(struct gether *port,
unsigned char *ntb_ptr = skb->data;
__le16 *tmp;
unsigned index, index2;
- int ndp_index;
+ unsigned int ndp_index;
unsigned dg_len, dg_len2;
unsigned ndp_len;
unsigned block_len;
diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c
index c7a20278bc3c..27abbe69b55c 100644
--- a/drivers/vdpa/mlx5/core/mr.c
+++ b/drivers/vdpa/mlx5/core/mr.c
@@ -234,7 +234,8 @@ static int create_direct_keys(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *
cmds[i].out = cmd_mem->out;
cmds[i].outlen = sizeof(cmd_mem->out);
cmds[i].in = cmd_mem->in;
- cmds[i].inlen = struct_size(cmd_mem, mtt, mttcount);
+ cmds[i].inlen = struct_size(cmd_mem, mtt, mttcount) -
+ offsetof(struct mlx5_create_mkey_mem, in);
fill_create_direct_mr(mvdev, dmr, cmd_mem);
diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index 2464c9de6771..a47fdcb61bf3 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -1102,6 +1102,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
unsigned int gup_flags = FOLL_LONGTERM;
unsigned long npages, cur_base, map_pfn, last_pfn = 0;
unsigned long lock_limit, sz2pin, nchunks, i;
+ unsigned long page_offset;
u64 start = iova;
long pinned;
int ret = 0;
@@ -1114,7 +1115,13 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
if (perm & VHOST_ACCESS_WO)
gup_flags |= FOLL_WRITE;
- npages = PFN_UP(size + (iova & ~PAGE_MASK));
+ page_offset = iova & ~PAGE_MASK;
+ if (size > ULONG_MAX - page_offset) {
+ ret = -EINVAL;
+ goto free;
+ }
+
+ npages = PFN_UP(size + page_offset);
if (!npages) {
ret = -EINVAL;
goto free;
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 68027fd993e3..f966fc094f12 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -2125,6 +2125,14 @@ static long vhost_vring_set_num_addr(struct vhost_dev *d,
BUG();
}
+ /*
+ * The metadata cache holds the IOTLB mapping that backed the previous
+ * desc/avail/used addresses and vring size, both of which are being
+ * replaced here. iotlb_access_ok() takes a cache hit as proof that the
+ * region was validated, so the stale entries have to go.
+ */
+ __vhost_vq_meta_reset(vq);
+
mutex_unlock(&vq->mutex);
return r;
diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c
index bed8ba18222b..8f6a2d1736d3 100644
--- a/drivers/video/fbdev/core/bitblit.c
+++ b/drivers/video/fbdev/core/bitblit.c
@@ -274,9 +274,14 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, bool enable,
if (!vc->vc_font.data)
return;
- c = scr_readw((u16 *) vc->vc_pos);
+ c = scr_readw((u16 *) vc->vc_pos);
attribute = get_attribute(info, c);
- src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
+ c &= charmask;
+
+ /* Clamp to font size, same as bit_putcs_aligned() */
+ if (c >= vc->vc_font.charcount)
+ c = 0;
+ src = vc->vc_font.data + (c * (w * vc->vc_font.height));
if (par->cursor_state.image.data != src ||
par->cursor_reset) {
diff --git a/drivers/watchdog/bd96801_wdt.c b/drivers/watchdog/bd96801_wdt.c
index 12b74fd2bc05..a25b7cf1488b 100644
--- a/drivers/watchdog/bd96801_wdt.c
+++ b/drivers/watchdog/bd96801_wdt.c
@@ -169,7 +169,6 @@ static int bd96801_set_wdt_mode(struct wdtbd96801 *w, unsigned int hw_margin,
int fastng, slowng, type, ret, reg, mask;
struct device *dev = w->dev;
-
if (hw_margin_min * 1000 > FASTNG_MAX_US) {
dev_err(dev, "Unsupported fast timeout %u uS [max %u]\n",
hw_margin_min * 1000, FASTNG_MAX_US);
@@ -258,10 +257,10 @@ static int bd96801_set_heartbeat_from_hw(struct wdtbd96801 *w,
fast = FASTNG_MIN << sel;
sel = (val & BD96801_WD_RATIO_MASK) + 1;
- w->wdt.max_hw_heartbeat_ms = (fast << sel) / USEC_PER_MSEC;
+ w->wdt.max_hw_heartbeat_ms = (fast << sel) / 10;
if ((conf_reg & BD96801_WD_TYPE_MASK) == BD96801_WD_TYPE_WIN)
- w->wdt.min_hw_heartbeat_ms = fast / USEC_PER_MSEC;
+ w->wdt.min_hw_heartbeat_ms = fast / 10;
return 0;
}
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index f85ee577c34d..3c4852bb8ccc 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -983,7 +983,8 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc)
/* last one */ {""}
};
- if (WARN_ON(user_ns != current_user_ns()))
+ /* The fscontext fd may have been passed to another user namespace. */
+ if (user_ns != current_user_ns())
return -EINVAL;
/* Never exec off this instance and never let anything stack on it. */
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index f60f8bf38dc5..ab7f19ca2145 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -9684,6 +9684,7 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
}
kvfree(folios);
out:
+ extent_changeset_free(data_reserved);
if (ret >= 0)
iocb->ki_pos += encoded->len;
return ret;
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index 701259991277..6fc526d7f424 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -532,7 +532,7 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
return -EFAULT;
policy.version = version;
- if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
+ if (!inode_owner_or_capable(file_mnt_idmap(filp), inode))
return -EACCES;
ret = mnt_want_write_file(filp);
diff --git a/fs/namespace.c b/fs/namespace.c
index 94c06c842902..a596381dba1c 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4229,6 +4229,11 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
ret = PTR_ERR(newmount.mnt);
goto err_unlock;
}
+ if (newmount.mnt->mnt_sb->s_flags & SB_NOUSER) {
+ mntput(newmount.mnt);
+ ret = -EINVAL;
+ goto err_unlock;
+ }
newmount.dentry = dget(fc->root);
newmount.mnt->mnt_flags = mnt_flags;
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 840789e182ef..facd4e28770b 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -10585,6 +10585,7 @@ static void nfs41_free_stateid_release(void *calldata)
struct nfs_free_stateid_data *data = calldata;
struct nfs_client *clp = data->server->nfs_client;
+ nfs_sb_deactive(data->server->super);
nfs_put_client(clp);
kfree(calldata);
}
@@ -10626,6 +10627,10 @@ static int nfs41_free_stateid(struct nfs_server *server,
if (!refcount_inc_not_zero(&clp->cl_count))
return -EIO;
+ if (!nfs_sb_active(server->super)) {
+ nfs_put_client(clp);
+ return -EIO;
+ }
nfs4_state_protect(server->nfs_client, NFS_SP4_MACH_CRED_STATEID,
&task_setup.rpc_client, &msg);
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index c3d84eafde9f..bd0c548cf1ee 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -1299,7 +1299,8 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
int err;
err = -EIO;
- if (WARN_ON(fc->user_ns != current_user_ns()))
+ /* The fscontext fd may have been passed to another user namespace. */
+ if (fc->user_ns != current_user_ns())
goto out_err;
sb->s_d_op = &ovl_dentry_operations;
diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
index a11a6ebae386..458d6922826f 100644
--- a/fs/smb/client/sess.c
+++ b/fs/smb/client/sess.c
@@ -259,9 +259,9 @@ int cifs_try_adding_channels(struct cifs_ses *ses)
cifs_dbg(VFS, "failed to open extra channel on iface:%pIS rc=%d\n",
&iface->sockaddr,
rc);
- kref_put(&iface->refcount, release_iface);
/* failure to add chan should increase weight */
iface->weight_fulfilled++;
+ kref_put(&iface->refcount, release_iface);
continue;
}
diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
index 02d56ed6ad20..ad182f9fbb48 100644
--- a/fs/tracefs/event_inode.c
+++ b/fs/tracefs/event_inode.c
@@ -849,7 +849,7 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry
*/
static void eventfs_remove_rec(struct eventfs_inode *ei, int level)
{
- struct eventfs_inode *ei_child;
+ struct eventfs_inode *ei_child, *tmp;
/*
* Check recursion depth. It should never be greater than 3:
@@ -862,7 +862,7 @@ static void eventfs_remove_rec(struct eventfs_inode *ei, int level)
return;
/* search for nested folders or files */
- list_for_each_entry(ei_child, &ei->children, list)
+ list_for_each_entry_safe(ei_child, tmp, &ei->children, list)
eventfs_remove_rec(ei_child, level + 1);
list_del_rcu(&ei->list);
diff --git a/fs/verity/measure.c b/fs/verity/measure.c
index 175d2f1bc089..91ce39ced77b 100644
--- a/fs/verity/measure.c
+++ b/fs/verity/measure.c
@@ -121,11 +121,11 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr *di
{
struct bpf_dynptr_kern *digest_ptr = (struct bpf_dynptr_kern *)digest_p;
const struct inode *inode = file_inode(file);
- u32 dynptr_sz = __bpf_dynptr_size(digest_ptr);
+ u64 dynptr_sz = __bpf_dynptr_size(digest_ptr);
struct fsverity_digest *arg;
const struct fsverity_info *vi;
const struct fsverity_hash_alg *hash_alg;
- int out_digest_sz;
+ u64 out_digest_sz;
if (dynptr_sz < sizeof(struct fsverity_digest))
return -EINVAL;
@@ -143,17 +143,20 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr *di
hash_alg = vi->tree_params.hash_alg;
+ out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest);
+ if (out_digest_sz < hash_alg->digest_size)
+ return -EOVERFLOW;
+
arg->digest_algorithm = hash_alg - fsverity_hash_algs;
arg->digest_size = hash_alg->digest_size;
- out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest);
-
/* copy digest */
- memcpy(arg->digest, vi->file_digest, min_t(int, hash_alg->digest_size, out_digest_sz));
+ memcpy(arg->digest, vi->file_digest, hash_alg->digest_size);
/* fill the extra buffer with zeros */
if (out_digest_sz > hash_alg->digest_size)
- memset(arg->digest + arg->digest_size, 0, out_digest_sz - hash_alg->digest_size);
+ memset(arg->digest + hash_alg->digest_size, 0,
+ out_digest_sz - hash_alg->digest_size);
return 0;
}
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index ea9b40b196de..5c5e1c814ff2 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -297,9 +297,11 @@ struct hh_cache {
* We could use other alignment values, but we must maintain the
* relationship HH alignment <= LL alignment.
*/
-#define LL_RESERVED_SPACE(dev) \
- ((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom)) \
+#define LL_RESERVED_SPACE_EX(dev, hlen) \
+ ((((hlen) + READ_ONCE((dev)->needed_headroom)) \
& ~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
+#define LL_RESERVED_SPACE(dev) \
+ LL_RESERVED_SPACE_EX(dev, (dev)->hard_header_len)
#define LL_RESERVED_SPACE_EXTRA(dev,extra) \
((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom) + (extra)) \
& ~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
@@ -3189,11 +3191,6 @@ static inline bool dev_validate_header(const struct net_device *dev,
if (len < dev->min_header_len)
return false;
- if (capable(CAP_SYS_RAWIO)) {
- memset(ll_header + len, 0, dev->hard_header_len - len);
- return true;
- }
-
if (dev->header_ops && dev->header_ops->validate)
return dev->header_ops->validate(ll_header, len);
diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
index b98331572ad2..cadae9b2578f 100644
--- a/include/linux/netfilter/ipset/ip_set.h
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -273,7 +273,7 @@ struct ip_set {
/* Number of elements (vs timeout) */
u32 elements;
/* Size of the dynamic extensions (vs timeout) */
- size_t ext_size;
+ atomic64_t ext_size;
/* Element data size */
size_t dsize;
/* Offsets to extensions in elements */
diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h
index 3ebeaa0ded00..2eb3edc638db 100644
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -238,6 +238,12 @@ static inline int nvmem_layout_register(struct nvmem_layout *layout)
static inline void nvmem_layout_unregister(struct nvmem_layout *layout) {}
+static inline int nvmem_add_cells_from_dt(struct nvmem_device *nvmem,
+ struct device_node *np)
+{
+ return -EOPNOTSUPP;
+}
+
#endif /* CONFIG_NVMEM */
#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
index 613a8209bed2..6a4435cfa977 100644
--- a/include/linux/psp-sev.h
+++ b/include/linux/psp-sev.h
@@ -947,6 +947,7 @@ int sev_do_cmd(int cmd, void *data, int *psp_ret);
void *psp_copy_user_blob(u64 uaddr, u32 len);
void *snp_alloc_firmware_page(gfp_t mask);
void snp_free_firmware_page(void *addr);
+void sev_platform_shutdown(void);
#else /* !CONFIG_CRYPTO_DEV_SP_PSP */
@@ -981,6 +982,8 @@ static inline void *snp_alloc_firmware_page(gfp_t mask)
static inline void snp_free_firmware_page(void *addr) { }
+static inline void sev_platform_shutdown(void) { }
+
#endif /* CONFIG_CRYPTO_DEV_SP_PSP */
#endif /* __PSP_SEV_H__ */
diff --git a/include/net/act_api.h b/include/net/act_api.h
index 539ea6693a24..76c31ce80fe5 100644
--- a/include/net/act_api.h
+++ b/include/net/act_api.h
@@ -270,6 +270,25 @@ int tcf_action_check_ctrlact(int action, struct tcf_proto *tp,
struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action,
struct tcf_chain *newchain);
+/* Range check for a control action supplied by user space.
+ *
+ * This is the same test tcf_action_check_ctrlact() applies to the primary
+ * control action, factored out for the *fallback* control actions
+ * (act_gact's TCA_GACT_PROB.paction and act_police's TCA_POLICE_RESULT),
+ * which must not reach tcf_action_check_ctrlact() because they have no
+ * goto_chain to allocate. Without it, user space can store kernel-internal
+ * verdicts such as TC_ACT_CONSUMED, which is TC_ACT_VALUE_MAX + 1 and is
+ * deliberately not part of the UAPI value range.
+ */
+static inline bool tcf_action_valid(int action)
+{
+ int opcode = TC_ACT_EXT_OPCODE(action);
+
+ if (!opcode)
+ return action <= TC_ACT_VALUE_MAX;
+ return opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC;
+}
+
#ifdef CONFIG_INET
DECLARE_STATIC_KEY_FALSE(tcf_frag_xmit_count);
#endif
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 3650adc635ad..e7dc5175e236 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -382,8 +382,8 @@ static inline struct inet6_dev *in6_dev_get(const struct net_device *dev)
rcu_read_lock();
idev = rcu_dereference(dev->ip6_ptr);
- if (idev)
- refcount_inc(&idev->refcnt);
+ if (idev && !refcount_inc_not_zero(&idev->refcnt))
+ idev = NULL;
rcu_read_unlock();
return idev;
}
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index 6935ec09af24..e0077fd28ad1 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -24,9 +24,7 @@
#include <linux/netfilter.h> /* for union nf_inet_addr */
#include <linux/ip.h>
#include <linux/ipv6.h> /* for struct ipv6hdr */
-#include <net/route.h>
#include <net/ipv6.h>
-#include <net/ip6_fib.h>
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
#include <net/netfilter/nf_conntrack.h>
#endif
@@ -738,10 +736,11 @@ struct ip_vs_dest {
/* connection counters and thresholds */
atomic_t activeconns; /* active connections */
- atomic_t inactconns; /* inactive connections */
+ atomic_t totalconns; /* total connections */
atomic_t persistconns; /* persistent connections */
__u32 u_threshold; /* upper threshold */
__u32 l_threshold; /* lower threshold */
+ __u32 l_threshold_val;/* used lower threshold */
/* for destination cache */
spinlock_t dst_lock; /* lock of dst_cache */
@@ -1569,6 +1568,8 @@ static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest)
kfree(dest);
}
+void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode);
+
/* IPVS sync daemon data and function prototypes
* (from ip_vs_sync.c)
*/
@@ -1707,7 +1708,7 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp)
void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_conn *cp, int dir, unsigned int toff,
- bool has_ports);
+ bool has_ports, struct ip_vs_iphdr *ciph);
#ifdef CONFIG_IP_VS_IPV6
void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
@@ -1740,30 +1741,23 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)
return csum_partial(diff, sizeof(diff), oldsum);
}
-static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af)
+static inline bool ip_vs_checksum_needed(struct sk_buff *skb)
{
/* Checksum unnecessary or already validated? */
if (skb_csum_unnecessary(skb))
return false;
- /* LOCAL_OUT ? */
- if (!skb->dev || skb->dev->flags & IFF_LOOPBACK)
+ /* Locally generated ? */
+ if (!skb->dev)
return false;
- /* !LOCAL_IN (FORWARD) ? */
- if (af == AF_INET6) {
- if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL))
- return false;
- } else {
- if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL))
- return false;
- }
return true;
}
static inline bool ip_vs_checksum_common_check(struct sk_buff *skb,
int offset, int proto, int af)
{
- if (!ip_vs_checksum_needed(skb, af))
+ if (!ip_vs_checksum_needed(skb))
return true;
+ /* Validate csum even for FORWARD */
return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af);
}
@@ -1874,14 +1868,21 @@ void ip_vs_unregister_hooks(struct netns_ipvs *ipvs, unsigned int af);
static inline int
ip_vs_dest_conn_overhead(struct ip_vs_dest *dest)
{
- /* We think the overhead of processing active connections is 256
+ /* We think the overhead of processing active connections is 257
* times higher than that of inactive connections in average. (This
- * 256 times might not be accurate, we will change it later) We
+ * 257 times might not be accurate, we will change it later) We
* use the following formula to estimate the overhead now:
- * dest->activeconns*256 + dest->inactconns
+ * dest->activeconns*256 + dest->totalconns
*/
return (atomic_read(&dest->activeconns) << 8) +
- atomic_read(&dest->inactconns);
+ atomic_read(&dest->totalconns);
+}
+
+static inline int
+ip_vs_dest_inactconns(const struct ip_vs_dest *dest)
+{
+ return max(atomic_read(&dest->totalconns) -
+ atomic_read(&dest->activeconns), 0);
}
#ifdef CONFIG_IP_VS_PROTO_TCP
diff --git a/include/net/route.h b/include/net/route.h
index cbb4d5523062..76f526e051aa 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -258,6 +258,8 @@ int fib_dump_info_fnhe(struct sk_buff *skb, struct netlink_callback *cb,
u32 table_id, struct fib_info *fi,
int *fa_index, int fa_start, unsigned int flags);
+void fnhe_update_pmtu(struct fib_nh_exception *fnhe, u32 new, u32 orig);
+
static inline void ip_rt_put(struct rtable *rt)
{
/* dst_release() accepts a NULL parameter.
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index d3e1f91f81cd..1b461d181647 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -101,6 +101,7 @@ struct Qdisc {
struct hlist_node hash;
u32 handle;
u32 parent;
+ int depth;
struct netdev_queue *dev_queue;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 77183b436da5..275c9fabc955 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -13565,11 +13565,12 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
return -EACCES;
}
- /* In case of 'scalar += pointer', dst_reg inherits pointer type and id.
- * The id may be overwritten later if we create a new variable offset.
+ /* For 'scalar += pointer', dst_reg inherits the complete pointer
+ * register state. Individual fields may be adjusted later by pointer
+ * arithmetic. Callers guarantee that below does not overwrite off_reg.
*/
- dst_reg->type = ptr_reg->type;
- dst_reg->id = ptr_reg->id;
+ if (dst_reg != ptr_reg)
+ *dst_reg = *ptr_reg;
if (!check_reg_sane_offset(env, off_reg, ptr_reg->type) ||
!check_reg_sane_offset(env, ptr_reg, ptr_reg->type))
@@ -13631,7 +13632,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
}
break;
case BPF_SUB:
- if (dst_reg == off_reg) {
+ if (dst_reg != ptr_reg) {
/* scalar -= pointer. Creates an unknown scalar */
verbose(env, "R%d tried to subtract pointer from scalar\n",
dst);
@@ -14490,8 +14491,8 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env,
err = mark_chain_precision(env, insn->dst_reg);
if (err)
return err;
- return adjust_ptr_min_max_vals(env, insn,
- src_reg, dst_reg);
+ off_reg = *dst_reg;
+ return adjust_ptr_min_max_vals(env, insn, src_reg, &off_reg);
}
} else if (ptr_reg) {
/* pointer += scalar */
diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index 010607a99194..c37d2263c21f 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -674,8 +674,11 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr,
return -1;
/*
- * Special case for regular (non PI) futexes. The unlock path in
- * user space has two race scenarios:
+ * Special case for regular (non PI) futexes. Ordinarily, we do
+ * not perform any processing here unless the current thread was
+ * the owner of the futex (by the TID check below).
+ *
+ * However, the unlock path has three race scenarios:
*
* 1. The unlock path releases the user space futex value and
* before it can execute the futex() syscall to wake up
@@ -684,42 +687,70 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr,
* 2. A woken up waiter is killed before it can acquire the
* futex in user space.
*
- * In the second case, the wake up notification could be generated
- * by the unlock path in user space after setting the futex value
- * to zero or by the kernel after setting the OWNER_DIED bit below.
+ * 3. A woken up waiter is killed in user space after another
+ * thread has acquired the futex, but before it can set
+ * FUTEX_WAITERS.
+ *
+ * Note that, if userspace uses the FUTEX_ROBUST_UNLOCK flag, we
+ * will not see case 1 here.
+ *
+ * In the second and third case, the wake up notification could
+ * be generated from any of:
+ *
+ * i. An ordinary futex wakeup after unlock (with or
+ * without FUTEX_ROBUST_UNLOCK)
+ * ii. A robust wakeup from another thread's death
+ * iii. A previous round through this special case
+ *
+ * As a result, the futex world will be in one of four states:
+ *
+ * A. The futex word is 0 (unlocked)
+ * B. The futex word is owned by another thread
+ * (FUTEX_WAITERS is not set)
+ * C. The futex word is owned by another thread
+ * (FUTEX_WAITERS set)
+ * D. The futex's owner died and OWNER_DIED is set
+ * (the owner part of the word is 0)
*
- * In both cases the TID validation below prevents a wakeup of
- * potential waiters which can cause these waiters to block
- * forever.
+ * The key issue is that the kernel usually (at least from
+ * sources ii. and iii. or when so requested by userspace from
+ * source i.) only ever wakes *one* waiter at a time. If this
+ * waiter dies before acquiring the futex (or setting the
+ * FUTEX_WAITERS bit), the kernel *must* still wake the next
+ * waiter down the line to uphold the futex invariants and
+ * avoid lost wakeups. Note we do not need to handle state C,
+ * as it does not matter to us whether *we* successfully set
+ * the bit or a third thread did so in the meantime.
*
- * In both cases the following conditions are met:
+ * Therefore, in these cases we must issue an additional
+ * futex_wake(). Note however that we *must not* set OWNER_DIED
+ * here. Our thread is *not* the owner of the futex.
*
- * 1) task->robust_list->list_op_pending != NULL
- * @pending_op == true
- * 2) The owner part of user space futex value == 0
+ * Thus to summarize, the conditions for needing the additional
+ * futex_wake() are:
+ *
+ * 1) @pending_op == true (the thread has not finished the
+ * mutex operation)
+ * 2) The futex word is in one of the states A, B or D
* 3) Regular futex: @pi == false
*
- * If these conditions are met, it is safe to attempt waking up a
- * potential waiter without touching the user space futex value and
- * trying to set the OWNER_DIED bit. If the futex value is zero,
- * the rest of the user space mutex state is consistent, so a woken
- * waiter will just take over the uncontended futex. Setting the
- * OWNER_DIED bit would create inconsistent state and malfunction
- * of the user space owner died handling. Otherwise, the OWNER_DIED
- * bit is already set, and the woken waiter is expected to deal with
- * this.
+ * Note in particular that in all of the states A-D the owner
+ * portion of the futex word differs from our thread's TID
+ * (unless the actual owner has the same TID in another PID
+ * namespace, but we cannot currently distinguish that
+ * scenario), so this can be a special-case wakeup in the bail
+ * path of the ordinary TID check.
*/
owner = uval & FUTEX_TID_MASK;
- if (pending_op && !pi && !owner) {
- futex_wake(uaddr, FLAGS_SIZE_32 | FLAGS_SHARED, 1,
- FUTEX_BITSET_MATCH_ANY);
+ if (owner != task_pid_vnr(curr)) {
+ if (pending_op && !pi && (!owner || !(uval & FUTEX_WAITERS))) {
+ futex_wake(uaddr, FLAGS_SIZE_32 | FLAGS_SHARED, 1,
+ FUTEX_BITSET_MATCH_ANY);
+ }
return 0;
}
- if (owner != task_pid_vnr(curr))
- return 0;
-
/*
* Ok, this dying thread is truly holding a futex
* of interest. Set the OWNER_DIED bit atomically
diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c
index 7d0f8fdd48a3..a6fc7ea51d2e 100644
--- a/kernel/sched/psi.c
+++ b/kernel/sched/psi.c
@@ -1130,6 +1130,12 @@ void psi_cgroup_free(struct cgroup *cgroup)
return;
cancel_delayed_work_sync(&cgroup->psi->avgs_work);
+ /*
+ * A psi_schedule_rtpoll_work() call racing the last trigger's
+ * destruction may have re-armed the timer after psi_trigger_destroy()
+ * deleted it. Spurious firing while the group is alive is harmless.
+ */
+ timer_shutdown_sync(&cgroup->psi->rtpoll_timer);
free_percpu(cgroup->psi->pcpu);
/* All triggers must be removed by now */
WARN_ONCE(cgroup->psi->rtpoll_states, "psi: trigger leak\n");
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index fcd2363d1296..3f90c7f752eb 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -6756,7 +6756,7 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
cpu_buffer = buffer->buffers[cpu];
- if (cpu_buffer->mapped) {
+ if (atomic_read(&cpu_buffer->resize_disabled)) {
err = -EBUSY;
goto error;
}
@@ -7604,7 +7604,7 @@ static __init int test_ringbuffer(void)
out_free:
for_each_online_cpu(cpu) {
- if (!rb_threads[cpu])
+ if (IS_ERR_OR_NULL(rb_threads[cpu]))
break;
kthread_stop(rb_threads[cpu]);
}
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 1630d2b7fbc4..ac50babaeea0 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -3072,6 +3072,7 @@ void trace_event_eval_update(struct trace_eval_map **map, int len)
int last_i;
int i;
+ mutex_lock(&event_mutex);
down_write(&trace_event_sem);
list_for_each_entry_safe(call, p, &ftrace_events, list) {
/* events are usually grouped together with systems */
@@ -3105,6 +3106,7 @@ void trace_event_eval_update(struct trace_eval_map **map, int len)
cond_resched();
}
up_write(&trace_event_sem);
+ mutex_unlock(&event_mutex);
}
static bool event_in_systems(struct trace_event_call *call,
diff --git a/lib/fortify_kunit.c b/lib/fortify_kunit.c
index ecb638d4cde1..d0810e4e65e5 100644
--- a/lib/fortify_kunit.c
+++ b/lib/fortify_kunit.c
@@ -394,8 +394,6 @@ struct fortify_padding {
char buf[32];
unsigned long bytes_after;
};
-/* Force compiler into not being able to resolve size at compile-time. */
-static volatile int unconst;
static void fortify_test_strlen(struct kunit *test)
{
@@ -520,57 +518,56 @@ static void fortify_test_strncpy(struct kunit *test)
{
struct fortify_padding pad = { };
char src[] = "Copy me fully into a small buffer and I will overflow!";
+ size_t sizeof_buf = sizeof(pad.buf);
+
+ OPTIMIZER_HIDE_VAR(sizeof_buf);
/* Destination is %NUL-filled to start with. */
KUNIT_EXPECT_EQ(test, pad.bytes_before, 0);
- KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
- KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
- KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0');
+ KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0');
+ KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 2], '\0');
+ KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Legitimate strncpy() 1 less than of max size. */
- KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src,
- sizeof(pad.buf) + unconst - 1)
+ KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf - 1)
== pad.buf);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Only last byte should be %NUL */
- KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
+ KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 3], '\0');
/* Legitimate (though unterminated) max-size strncpy. */
- KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src,
- sizeof(pad.buf) + unconst)
+ KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf)
== pad.buf);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* No trailing %NUL -- thanks strncpy API. */
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 1], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
/* But we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Now verify that FORTIFY is working... */
- KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src,
- sizeof(pad.buf) + unconst + 1)
+ KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf + 1)
== pad.buf);
/* Should catch the overflow. */
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1);
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 1], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
/* And we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* And further... */
- KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src,
- sizeof(pad.buf) + unconst + 2)
+ KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf + 2)
== pad.buf);
/* Should catch the overflow. */
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2);
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 1], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 1], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
/* And we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
@@ -579,55 +576,56 @@ static void fortify_test_strscpy(struct kunit *test)
{
struct fortify_padding pad = { };
char src[] = "Copy me fully into a small buffer and I will overflow!";
+ size_t sizeof_buf = sizeof(pad.buf);
+ size_t sizeof_src = sizeof(src);
+
+ OPTIMIZER_HIDE_VAR(sizeof_buf);
+ OPTIMIZER_HIDE_VAR(sizeof_src);
/* Destination is %NUL-filled to start with. */
KUNIT_EXPECT_EQ(test, pad.bytes_before, 0);
- KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
- KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
- KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 3], '\0');
+ KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0');
+ KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 2], '\0');
+ KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Legitimate strscpy() 1 less than of max size. */
- KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src,
- sizeof(pad.buf) + unconst - 1),
+ KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src, sizeof_buf - 1),
-E2BIG);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Keeping space for %NUL, last two bytes should be %NUL */
- KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
- KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 2], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 3], '\0');
+ KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0');
+ KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 2], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 3], '\0');
/* Legitimate max-size strscpy. */
- KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src,
- sizeof(pad.buf) + unconst),
+ KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src, sizeof_buf),
-E2BIG);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* A trailing %NUL will exist. */
- KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
+ KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
/* Now verify that FORTIFY is working... */
- KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src,
- sizeof(pad.buf) + unconst + 1),
+ KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src, sizeof_buf + 1),
-E2BIG);
/* Should catch the overflow. */
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1);
- KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
+ KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
/* And we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* And much further... */
- KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src,
- sizeof(src) * 2 + unconst),
+ KUNIT_ASSERT_EQ(test, strscpy(pad.buf, src, sizeof_src * 2),
-E2BIG);
/* Should catch the overflow. */
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2);
- KUNIT_EXPECT_EQ(test, pad.buf[sizeof(pad.buf) - 1], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
- KUNIT_EXPECT_NE(test, pad.buf[sizeof(pad.buf) - 2], '\0');
+ KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
+ KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
/* And we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
@@ -767,7 +765,9 @@ static void fortify_test_strlcat(struct kunit *test)
struct fortify_padding pad = { };
char src[sizeof(pad.buf)] = { };
int i, partial;
- int len = sizeof(pad.buf) + unconst;
+ int len = sizeof(pad.buf);
+
+ OPTIMIZER_HIDE_VAR(len);
/* Fill 15 bytes with valid characters. */
partial = sizeof(src) / 2 - 1;
@@ -857,28 +857,32 @@ struct fortify_zero_sized {
#define __fortify_test(memfunc) \
static void fortify_test_##memfunc(struct kunit *test) \
{ \
- struct fortify_zero_sized zero = { }; \
+ struct fortify_zero_sized empty = { }; \
struct fortify_padding pad = { }; \
char srcA[sizeof(pad.buf) + 2]; \
char srcB[sizeof(pad.buf) + 2]; \
- size_t len = sizeof(pad.buf) + unconst; \
+ size_t len = sizeof(pad.buf); \
+ size_t zero = 0; \
+ \
+ OPTIMIZER_HIDE_VAR(len); \
+ OPTIMIZER_HIDE_VAR(zero); \
\
memset(srcA, 'A', sizeof(srcA)); \
KUNIT_ASSERT_EQ(test, srcA[0], 'A'); \
memset(srcB, 'B', sizeof(srcB)); \
KUNIT_ASSERT_EQ(test, srcB[0], 'B'); \
\
- memfunc(pad.buf, srcA, 0 + unconst); \
+ memfunc(pad.buf, srcA, zero); \
KUNIT_EXPECT_EQ(test, pad.buf[0], '\0'); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
- memfunc(pad.buf + 1, srcB, 1 + unconst); \
+ memfunc(pad.buf + 1, srcB, zero + 1); \
KUNIT_EXPECT_EQ(test, pad.buf[0], '\0'); \
KUNIT_EXPECT_EQ(test, pad.buf[1], 'B'); \
KUNIT_EXPECT_EQ(test, pad.buf[2], '\0'); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
- memfunc(pad.buf, srcA, 1 + unconst); \
+ memfunc(pad.buf, srcA, zero + 1); \
KUNIT_EXPECT_EQ(test, pad.buf[0], 'A'); \
KUNIT_EXPECT_EQ(test, pad.buf[1], 'B'); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
@@ -904,10 +908,10 @@ static void fortify_test_##memfunc(struct kunit *test) \
/* Reset error counter. */ \
fortify_write_overflows = 0; \
/* Copy nothing into nothing: no errors. */ \
- memfunc(zero.buf, srcB, 0 + unconst); \
+ memfunc(empty.buf, srcB, zero); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
- memfunc(zero.buf, srcB, 1 + unconst); \
+ memfunc(empty.buf, srcB, zero + 1); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1); \
}
@@ -919,7 +923,9 @@ static void fortify_test_memscan(struct kunit *test)
char haystack[] = "Where oh where is my memory range?";
char *mem = haystack + strlen("Where oh where is ");
char needle = 'm';
- size_t len = sizeof(haystack) + unconst;
+ size_t len = sizeof(haystack);
+
+ OPTIMIZER_HIDE_VAR(len);
KUNIT_ASSERT_PTR_EQ(test, memscan(haystack, needle, len),
mem);
@@ -938,7 +944,9 @@ static void fortify_test_memchr(struct kunit *test)
char haystack[] = "Where oh where is my memory range?";
char *mem = haystack + strlen("Where oh where is ");
char needle = 'm';
- size_t len = sizeof(haystack) + unconst;
+ size_t len = sizeof(haystack);
+
+ OPTIMIZER_HIDE_VAR(len);
KUNIT_ASSERT_PTR_EQ(test, memchr(haystack, needle, len),
mem);
@@ -957,7 +965,9 @@ static void fortify_test_memchr_inv(struct kunit *test)
char haystack[] = "Where oh where is my memory range?";
char *mem = haystack + 1;
char needle = 'W';
- size_t len = sizeof(haystack) + unconst;
+ size_t len = sizeof(haystack);
+
+ OPTIMIZER_HIDE_VAR(len);
/* Normal search is okay. */
KUNIT_ASSERT_PTR_EQ(test, memchr_inv(haystack, needle, len),
@@ -976,8 +986,11 @@ static void fortify_test_memcmp(struct kunit *test)
{
char one[] = "My mind is going ...";
char two[] = "My mind is going ... I can feel it.";
- size_t one_len = sizeof(one) + unconst - 1;
- size_t two_len = sizeof(two) + unconst - 1;
+ volatile size_t one_len = sizeof(one) - 1;
+ volatile size_t two_len = sizeof(two) - 1;
+
+ OPTIMIZER_HIDE_VAR(one_len);
+ OPTIMIZER_HIDE_VAR(two_len);
/* We match the first string (ignoring the %NUL). */
KUNIT_ASSERT_EQ(test, memcmp(one, two, one_len), 0);
@@ -998,7 +1011,9 @@ static void fortify_test_kmemdup(struct kunit *test)
{
char src[] = "I got Doom running on it!";
char *copy;
- size_t len = sizeof(src) + unconst;
+ size_t len = sizeof(src);
+
+ OPTIMIZER_HIDE_VAR(len);
/* Copy is within bounds. */
copy = kmemdup(src, len, GFP_KERNEL);
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index b2daa1767d90..29cbe43b60a4 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -41,6 +41,7 @@
#include <linux/compat.h>
#include <linux/pgalloc_tag.h>
#include <linux/pagewalk.h>
+#include <linux/cleanup.h>
#include <asm/tlb.h>
#include <asm/pgalloc.h>
@@ -77,6 +78,7 @@ static unsigned long deferred_split_scan(struct shrinker *shrink,
static bool split_underused_thp = true;
static atomic_t huge_zero_refcount;
+static DEFINE_SPINLOCK(huge_zero_lock);
struct folio *huge_zero_folio __read_mostly;
unsigned long huge_zero_pfn __read_mostly = ~0UL;
unsigned long huge_anon_orders_always __read_mostly;
@@ -197,7 +199,8 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
static bool get_huge_zero_page(void)
{
struct folio *zero_folio;
-retry:
+
+ /* Paired with atomic_set_release(). */
if (likely(atomic_inc_not_zero(&huge_zero_refcount)))
return true;
@@ -209,17 +212,22 @@ static bool get_huge_zero_page(void)
}
/* Ensure zero folio won't have large_rmappable flag set. */
folio_clear_large_rmappable(zero_folio);
- preempt_disable();
- if (cmpxchg(&huge_zero_folio, NULL, zero_folio)) {
- preempt_enable();
+
+ /* Paired with critical section in shrink_huge_zero_folio_scan(). */
+ spin_lock(&huge_zero_lock);
+ if (huge_zero_folio) {
+ /* Somebody else already installed it. */
+ atomic_inc(&huge_zero_refcount);
+ spin_unlock(&huge_zero_lock);
folio_put(zero_folio);
- goto retry;
+ return true;
}
+ WRITE_ONCE(huge_zero_folio, zero_folio);
WRITE_ONCE(huge_zero_pfn, folio_pfn(zero_folio));
+ /* Paired with atomic_inc_not_zero(). +1 for shrinker pin. */
+ atomic_set_release(&huge_zero_refcount, 2);
+ spin_unlock(&huge_zero_lock);
- /* We take additional reference here. It will be put back by shrinker */
- atomic_set(&huge_zero_refcount, 2);
- preempt_enable();
count_vm_event(THP_ZERO_PAGE_ALLOC);
return true;
}
@@ -263,15 +271,22 @@ static unsigned long shrink_huge_zero_page_count(struct shrinker *shrink,
static unsigned long shrink_huge_zero_page_scan(struct shrinker *shrink,
struct shrink_control *sc)
{
- if (atomic_cmpxchg(&huge_zero_refcount, 1, 0) == 1) {
- struct folio *zero_folio = xchg(&huge_zero_folio, NULL);
- BUG_ON(zero_folio == NULL);
+ struct folio *zero_folio;
+
+ /* Paired with critical section in get_huge_zero_folio(). */
+ scoped_guard(spinlock, &huge_zero_lock) {
+ /* Paired with atomic_inc_not_zero() in get_huge_zero_folio(). */
+ if (atomic_cmpxchg(&huge_zero_refcount, 1, 0) != 1)
+ return 0;
+
+ zero_folio = huge_zero_folio;
+ VM_WARN_ON_ONCE(!zero_folio);
+ WRITE_ONCE(huge_zero_folio, NULL);
WRITE_ONCE(huge_zero_pfn, ~0UL);
- folio_put(zero_folio);
- return HPAGE_PMD_NR;
}
- return 0;
+ folio_put(zero_folio);
+ return HPAGE_PMD_NR;
}
static struct shrinker *huge_zero_page_shrinker;
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 552df432c37f..ed0a5ed25e65 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -770,11 +770,11 @@ static void swap_reclaim_full_clusters(struct swap_info_struct *si, bool force)
}
offset++;
}
+ cond_resched();
spin_lock(&si->lock);
if (to_scan <= 0)
break;
- cond_resched();
}
}
diff --git a/mm/vmscan.c b/mm/vmscan.c
index deeb4310fd54..20f4cb33d8e8 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4313,6 +4313,7 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c
int tier_idx)
{
bool success;
+ bool dirty, writeback;
int gen = folio_lru_gen(folio);
int type = folio_is_file_lru(folio);
int zone = folio_zonenum(folio);
@@ -4358,9 +4359,17 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c
return true;
}
+ dirty = folio_test_dirty(folio);
+ writeback = folio_test_writeback(folio);
+ if (type == LRU_GEN_FILE && dirty) {
+ sc->nr.file_taken += delta;
+ if (!writeback)
+ sc->nr.unqueued_dirty += delta;
+ }
+
/* waiting for writeback */
- if (folio_test_locked(folio) || folio_test_writeback(folio) ||
- (type == LRU_GEN_FILE && folio_test_dirty(folio))) {
+ if (folio_test_locked(folio) || writeback ||
+ (type == LRU_GEN_FILE && dirty)) {
gen = folio_inc_gen(lruvec, folio, true);
list_move(&folio->lru, &lrugen->folios[gen][type][zone]);
return true;
@@ -4476,7 +4485,8 @@ static int scan_folios(struct lruvec *lruvec, struct scan_control *sc,
trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, MAX_LRU_BATCH,
scanned, skipped, isolated,
type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON);
-
+ if (type == LRU_GEN_FILE)
+ sc->nr.file_taken += isolated;
/*
* There might not be eligible folios due to reclaim_idx. Check the
* remaining to prevent livelock if it's not making progress.
@@ -4610,6 +4620,7 @@ static int evict_folios(struct lruvec *lruvec, struct scan_control *sc, int swap
return scanned;
retry:
reclaimed = shrink_folio_list(&list, pgdat, sc, &stat, false);
+ sc->nr.unqueued_dirty += stat.nr_unqueued_dirty;
sc->nr_reclaimed += reclaimed;
trace_mm_vmscan_lru_shrink_inactive(pgdat->node_id,
scanned, reclaimed, &stat, sc->priority,
@@ -4821,6 +4832,13 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
cond_resched();
}
+ /*
+ * If too many file cache in the coldest generation can't be evicted
+ * due to being dirty, wake up the flusher.
+ */
+ if (sc->nr.unqueued_dirty && sc->nr.unqueued_dirty == sc->nr.file_taken)
+ wakeup_flusher_threads(WB_REASON_VMSCAN);
+
/* whether this lruvec should be rotated */
return nr_to_scan < 0;
}
@@ -5966,6 +5984,7 @@ static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)
bool reclaimable = false;
if (lru_gen_enabled() && root_reclaim(sc)) {
+ memset(&sc->nr, 0, sizeof(sc->nr));
lru_gen_shrink_node(pgdat, sc);
return;
}
diff --git a/net/atm/common.c b/net/atm/common.c
index 196c36269b34..413863861bca 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -760,7 +760,7 @@ int vcc_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int optlen)
{
struct atm_vcc *vcc;
- unsigned long value;
+ int value;
int error;
if (__SO_LEVEL_MATCH(optname, level) && optlen != __SO_SIZE(optname))
@@ -772,8 +772,10 @@ int vcc_setsockopt(struct socket *sock, int level, int optname,
{
struct atm_qos qos;
- if (copy_from_sockptr(&qos, optval, sizeof(qos)))
- return -EFAULT;
+ error = copy_safe_from_sockptr(&qos, sizeof(qos), optval,
+ optlen);
+ if (error)
+ return error;
error = check_qos(&qos);
if (error)
return error;
@@ -786,8 +788,10 @@ int vcc_setsockopt(struct socket *sock, int level, int optname,
return 0;
}
case SO_SETCLP:
- if (copy_from_sockptr(&value, optval, sizeof(value)))
- return -EFAULT;
+ error = copy_safe_from_sockptr(&value, sizeof(value), optval,
+ optlen);
+ if (error)
+ return error;
if (value)
vcc->atm_options |= ATM_ATMOPT_CLP;
else
diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
index 5fd22bb4f5b6..a837ff7d6f36 100644
--- a/net/bridge/br_mrp.c
+++ b/net/bridge/br_mrp.c
@@ -224,11 +224,9 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
sub_opt = skb_put(skb, sizeof(*sub_opt));
memset(sub_opt, 0x0, sizeof(*sub_opt));
- sub_tlv = skb_put(skb, sizeof(*sub_tlv));
- sub_tlv->type = BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR;
-
/* 32 bit alligment shall be ensured therefore add 2 bytes */
- skb_put(skb, MRP_OPT_PADDING);
+ sub_tlv = skb_put_zero(skb, sizeof(*sub_tlv) + MRP_OPT_PADDING);
+ sub_tlv->type = BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR;
}
br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_END, 0x0);
diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c
index 61bf8f4465ab..426f8adc912c 100644
--- a/net/bridge/netfilter/ebt_nflog.c
+++ b/net/bridge/netfilter/ebt_nflog.c
@@ -41,11 +41,25 @@ ebt_nflog_tg(struct sk_buff *skb, const struct xt_action_param *par)
static int ebt_nflog_tg_check(const struct xt_tgchk_param *par)
{
struct ebt_nflog_info *info = par->targinfo;
+ int ret;
if (info->flags & ~EBT_NFLOG_MASK)
return -EINVAL;
info->prefix[EBT_NFLOG_PREFIX_SIZE - 1] = '\0';
- return 0;
+
+ ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG);
+ if (ret != 0 && !par->nft_compat) {
+ request_module("%s", "nfnetlink_log");
+
+ ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG);
+ }
+
+ return ret;
+}
+
+static void ebt_nflog_tg_destroy(const struct xt_tgdtor_param *par)
+{
+ nf_logger_put(par->family, NF_LOG_TYPE_ULOG);
}
static struct xt_target ebt_nflog_tg_reg __read_mostly = {
@@ -54,6 +68,7 @@ static struct xt_target ebt_nflog_tg_reg __read_mostly = {
.family = NFPROTO_BRIDGE,
.target = ebt_nflog_tg,
.checkentry = ebt_nflog_tg_check,
+ .destroy = ebt_nflog_tg_destroy,
.targetsize = sizeof(struct ebt_nflog_info),
.me = THIS_MODULE,
};
diff --git a/net/bridge/netfilter/nf_conntrack_bridge.c b/net/bridge/netfilter/nf_conntrack_bridge.c
index 6482de4d8750..e4dae5fe0b0d 100644
--- a/net/bridge/netfilter/nf_conntrack_bridge.c
+++ b/net/bridge/netfilter/nf_conntrack_bridge.c
@@ -281,6 +281,7 @@ static unsigned int nf_ct_bridge_pre(void *priv, struct sk_buff *skb,
ret = nf_ct_br_defrag6(skb, &bridge_state);
break;
default:
+ nf_reset_ct(skb);
nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
return NF_ACCEPT;
}
diff --git a/net/core/dev.c b/net/core/dev.c
index ce5a1ab86ab5..5c037fe70d48 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5116,12 +5116,16 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp,
}
/* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers
- * (e.g. bpf_xdp_adjust_tail), we need to update data_len here.
+ * (e.g. bpf_xdp_adjust_tail). Remove the old fragment contribution
+ * from skb->len before updating data_len, then add the new one back.
*/
- if (xdp_buff_has_frags(xdp))
+ skb->len -= skb->data_len;
+ if (xdp_buff_has_frags(xdp)) {
skb->data_len = skb_shinfo(skb)->xdp_frags_size;
- else
+ skb->len += skb->data_len;
+ } else {
skb->data_len = 0;
+ }
/* check if XDP changed eth hdr such SKB needs update */
eth = (struct ethhdr *)xdp->data;
diff --git a/net/core/sock.c b/net/core/sock.c
index 4a09e780406f..e8b03cf3a428 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -773,7 +773,6 @@ bool sk_mc_loop(const struct sock *sk)
return inet6_test_bit(MC6_LOOP, sk);
#endif
}
- WARN_ON_ONCE(1);
return true;
}
EXPORT_SYMBOL(sk_mc_loop);
diff --git a/net/core/xdp.c b/net/core/xdp.c
index 8a3ea90e8cf9..251708f648e1 100644
--- a/net/core/xdp.c
+++ b/net/core/xdp.c
@@ -731,7 +731,7 @@ struct xdp_frame *xdpf_clone(struct xdp_frame *xdpf)
headroom = xdpf->headroom + sizeof(*xdpf);
totalsize = headroom + xdpf->len;
- if (unlikely(totalsize > PAGE_SIZE))
+ if (unlikely(totalsize > SKB_WITH_OVERHEAD(PAGE_SIZE)))
return NULL;
page = dev_alloc_page();
if (!page)
diff --git a/net/devlink/dev.c b/net/devlink/dev.c
index 13c73f50da3d..fc109acd4e84 100644
--- a/net/devlink/dev.c
+++ b/net/devlink/dev.c
@@ -579,6 +579,7 @@ int devlink_nl_reload_doit(struct sk_buff *skb, struct genl_info *info)
action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT) {
NL_SET_ERR_MSG_MOD(info->extack,
"Changing namespace is only supported for reinit action");
+ put_net(dest_net);
return -EOPNOTSUPP;
}
}
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index ba2df3d2ac15..687fe2c281c5 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -462,6 +462,34 @@ int ip_fib_check_default(__be32 gw, struct net_device *dev)
return -1;
}
+static size_t fib_nexthop_nlmsg_size(const struct fib_nh_common *nhc,
+ bool skip_oif)
+{
+ size_t nhsize = 0;
+
+ switch (nhc->nhc_gw_family) {
+ case AF_INET:
+ nhsize += nla_total_size(4); /* RTA_GATEWAY */
+ break;
+ case AF_INET6:
+ nhsize += nla_total_size(sizeof(struct rtvia) +
+ sizeof(struct in6_addr));
+ break;
+ }
+
+ if (!skip_oif && nhc->nhc_dev)
+ nhsize += nla_total_size(4); /* RTA_OIF */
+
+ if (nhc->nhc_lwtstate) {
+ /* RTA_ENCAP */
+ nhsize += lwtunnel_get_encap_size(nhc->nhc_lwtstate);
+ /* RTA_ENCAP_TYPE */
+ nhsize += nla_total_size(2);
+ }
+
+ return nhsize;
+}
+
size_t fib_nlmsg_size(struct fib_info *fi)
{
size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg))
@@ -479,32 +507,35 @@ size_t fib_nlmsg_size(struct fib_info *fi)
payload += nla_total_size(4); /* RTA_NH_ID */
if (nhs) {
- size_t nh_encapsize = 0;
- /* Also handles the special case nhs == 1 */
-
- /* each nexthop is packed in an attribute */
- size_t nhsize = nla_total_size(sizeof(struct rtnexthop));
+ size_t mpsize = 0;
unsigned int i;
- /* may contain flow and gateway attribute */
- nhsize += 2 * nla_total_size(4);
-
- /* grab encap info */
for (i = 0; i < fib_info_num_path(fi); i++) {
struct fib_nh_common *nhc = fib_info_nhc(fi, i);
+ size_t nhsize;
+
+ nhsize = fib_nexthop_nlmsg_size(nhc, nhs != 1);
+
+ if (nhs != 1)
+ nhsize += NLA_ALIGN(sizeof(struct rtnexthop));
+
+#ifdef CONFIG_IP_ROUTE_CLASSID
+ if (nhc->nhc_family == AF_INET) {
+ struct fib_nh *nh;
- if (nhc->nhc_lwtstate) {
- /* RTA_ENCAP_TYPE */
- nh_encapsize += lwtunnel_get_encap_size(
- nhc->nhc_lwtstate);
- /* RTA_ENCAP */
- nh_encapsize += nla_total_size(2);
+ nh = container_of(nhc, struct fib_nh, nh_common);
+ if (nh->nh_tclassid)
+ nhsize += nla_total_size(4);
}
+#endif
+ if (nhs == 1)
+ payload += nhsize;
+ else
+ mpsize += nhsize;
}
- /* all nexthops are packed in a nested attribute */
- payload += nla_total_size((nhs * nhsize) + nh_encapsize);
-
+ if (nhs != 1)
+ payload += nla_total_size(mpsize);
}
return payload;
@@ -1925,42 +1956,30 @@ static int call_fib_nh_notifiers(struct fib_nh *nh,
return NOTIFY_DONE;
}
-/* Update the PMTU of exceptions when:
- * - the new MTU of the first hop becomes smaller than the PMTU
- * - the old MTU was the same as the PMTU, and it limited discovery of
- * larger MTUs on the path. With that limit raised, we can now
- * discover larger MTUs
- * A special case is locked exceptions, for which the PMTU is smaller
- * than the minimal accepted PMTU:
- * - if the new MTU is greater than the PMTU, don't make any change
- * - otherwise, unlock and set PMTU
+/* Walk the exceptions of a nexthop after its first hop MTU changed. The
+ * chain is RCU protected here, while fnhe_update_pmtu() takes fnhe_lock
+ * for the update of each entry.
*/
void fib_nhc_update_mtu(struct fib_nh_common *nhc, u32 new, u32 orig)
{
struct fnhe_hash_bucket *bucket;
int i;
- bucket = rcu_dereference_protected(nhc->nhc_exceptions, 1);
+ rcu_read_lock();
+ bucket = rcu_dereference(nhc->nhc_exceptions);
if (!bucket)
- return;
+ goto out;
for (i = 0; i < FNHE_HASH_SIZE; i++) {
struct fib_nh_exception *fnhe;
- for (fnhe = rcu_dereference_protected(bucket[i].chain, 1);
+ for (fnhe = rcu_dereference(bucket[i].chain);
fnhe;
- fnhe = rcu_dereference_protected(fnhe->fnhe_next, 1)) {
- if (fnhe->fnhe_mtu_locked) {
- if (new <= fnhe->fnhe_pmtu) {
- fnhe->fnhe_pmtu = new;
- fnhe->fnhe_mtu_locked = false;
- }
- } else if (new < fnhe->fnhe_pmtu ||
- orig == fnhe->fnhe_pmtu) {
- fnhe->fnhe_pmtu = new;
- }
- }
+ fnhe = rcu_dereference(fnhe->fnhe_next))
+ fnhe_update_pmtu(fnhe, new, orig);
}
+out:
+ rcu_read_unlock();
}
void fib_sync_mtu(struct net_device *dev, u32 orig_mtu)
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 5ff45bc85442..91dcdb7567ac 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -987,11 +987,23 @@ static struct request_sock *inet_reqsk_clone(struct request_sock *req,
nreq->rsk_listener = sk;
- /* We need not acquire fastopenq->lock
- * because the child socket is locked in inet_csk_listen_stop().
- */
- if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(nreq)->tfo_listener)
+ if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(nreq)->tfo_listener) {
+ struct fastopen_queue *fastopenq;
+
+ /* reqsk_fastopen_remove() will uncharge nreq->rsk_listener,
+ * that is @sk, so charge it here. Unlike the listener
+ * being closed, @sk is live and needs its lock.
+ */
+ fastopenq = &inet_csk(sk)->icsk_accept_queue.fastopenq;
+ spin_lock_bh(&fastopenq->lock);
+ fastopenq->qlen++;
+ spin_unlock_bh(&fastopenq->lock);
+
+ /* We need not acquire fastopenq->lock
+ * because the child socket is locked in inet_csk_listen_stop().
+ */
rcu_assign_pointer(tcp_sk(nreq->sk)->fastopen_rsk, nreq);
+ }
return nreq;
}
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 2b8c29a29c4a..28d83b3baae3 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -739,6 +739,35 @@ static void update_or_create_fnhe(struct fib_nh_common *nhc, __be32 daddr,
spin_unlock_bh(&fnhe_lock);
}
+/* Update the PMTU of an exception when:
+ * - the new MTU of the first hop becomes smaller than the PMTU
+ * - the old MTU was the same as the PMTU, and it limited discovery of
+ * larger MTUs on the path. With that limit raised, we can now
+ * discover larger MTUs
+ * A special case is locked exceptions, for which the PMTU is smaller
+ * than the minimal accepted PMTU:
+ * - if the new MTU is greater than the PMTU, don't make any change
+ * - otherwise, unlock and set PMTU
+ *
+ * fnhe_lock keeps fnhe_pmtu and fnhe_mtu_locked consistent against
+ * update_or_create_fnhe(), which sets both under the same lock.
+ */
+void fnhe_update_pmtu(struct fib_nh_exception *fnhe, u32 new, u32 orig)
+{
+ spin_lock_bh(&fnhe_lock);
+
+ if (fnhe->fnhe_mtu_locked) {
+ if (new <= fnhe->fnhe_pmtu) {
+ fnhe->fnhe_pmtu = new;
+ fnhe->fnhe_mtu_locked = false;
+ }
+ } else if (new < fnhe->fnhe_pmtu || orig == fnhe->fnhe_pmtu) {
+ fnhe->fnhe_pmtu = new;
+ }
+
+ spin_unlock_bh(&fnhe_lock);
+}
+
static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flowi4 *fl4,
bool kill_route)
{
diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c
index 132ffc13087f..0e7e12117913 100644
--- a/net/ipv4/tcp_bpf.c
+++ b/net/ipv4/tcp_bpf.c
@@ -455,6 +455,7 @@ static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock,
case __SK_REDIRECT:
redir_ingress = psock->redir_ingress;
sk_redir = psock->sk_redir;
+ sock_hold(sk_redir);
sk_msg_apply_bytes(psock, tosend);
if (!psock->apply_bytes) {
/* Clean up before releasing the sock lock. */
@@ -475,6 +476,7 @@ static int tcp_bpf_send_verdict(struct sock *sk, struct sk_psock *psock,
if (eval == __SK_REDIRECT)
sock_put(sk_redir);
+ sock_put(sk_redir);
lock_sock(sk);
sk_mem_uncharge(sk, sent);
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index e57917aefd50..2fb68455679a 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -247,7 +247,7 @@ static void tcp_measure_rcv_mss(struct sock *sk, const struct sk_buff *skb)
struct tcp_sock *tp = tcp_sk(sk);
val = tcp_win_from_space(sk, sk->sk_rcvbuf);
- tcp_set_window_clamp(sk, val);
+ WRITE_ONCE(tp->window_clamp, val);
if (tp->window_clamp < tp->rcvq_space.space)
tp->rcvq_space.space = tp->window_clamp;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index a9f3ee89302c..40b73e97d5fb 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -58,6 +58,7 @@
#include <linux/times.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <linux/sock_diag.h>
#include <net/net_namespace.h>
#include <net/icmp.h>
@@ -2998,13 +2999,17 @@ static int tcp4_seq_show(struct seq_file *seq, void *v)
}
#ifdef CONFIG_BPF_SYSCALL
+union bpf_tcp_iter_batch_item {
+ struct sock *sk;
+ __u64 cookie;
+};
+
struct bpf_tcp_iter_state {
struct tcp_iter_state state;
unsigned int cur_sk;
unsigned int end_sk;
unsigned int max_sk;
- struct sock **batch;
- bool st_bucket_done;
+ union bpf_tcp_iter_batch_item *batch;
};
struct bpf_iter__tcp {
@@ -3027,21 +3032,32 @@ static int tcp_prog_seq_show(struct bpf_prog *prog, struct bpf_iter_meta *meta,
static void bpf_iter_tcp_put_batch(struct bpf_tcp_iter_state *iter)
{
- while (iter->cur_sk < iter->end_sk)
- sock_gen_put(iter->batch[iter->cur_sk++]);
+ union bpf_tcp_iter_batch_item *item;
+ unsigned int cur_sk = iter->cur_sk;
+ __u64 cookie;
+
+ /* Remember the cookies of the sockets we haven't seen yet, so we can
+ * pick up where we left off next time around.
+ */
+ while (cur_sk < iter->end_sk) {
+ item = &iter->batch[cur_sk++];
+ cookie = sock_gen_cookie(item->sk);
+ sock_gen_put(item->sk);
+ item->cookie = cookie;
+ }
}
static int bpf_iter_tcp_realloc_batch(struct bpf_tcp_iter_state *iter,
- unsigned int new_batch_sz)
+ unsigned int new_batch_sz, gfp_t flags)
{
- struct sock **new_batch;
+ union bpf_tcp_iter_batch_item *new_batch;
new_batch = kvmalloc(sizeof(*new_batch) * new_batch_sz,
- GFP_USER | __GFP_NOWARN);
+ flags | __GFP_NOWARN);
if (!new_batch)
return -ENOMEM;
- bpf_iter_tcp_put_batch(iter);
+ memcpy(new_batch, iter->batch, sizeof(*iter->batch) * iter->end_sk);
kvfree(iter->batch);
iter->batch = new_batch;
iter->max_sk = new_batch_sz;
@@ -3049,112 +3065,242 @@ static int bpf_iter_tcp_realloc_batch(struct bpf_tcp_iter_state *iter,
return 0;
}
-static unsigned int bpf_iter_tcp_listening_batch(struct seq_file *seq,
- struct sock *start_sk)
+static struct sock *bpf_iter_tcp_resume_bucket(struct sock *first_sk,
+ union bpf_tcp_iter_batch_item *cookies,
+ int n_cookies)
+{
+ struct hlist_nulls_node *node;
+ struct sock *sk;
+ int i;
+
+ for (i = 0; i < n_cookies; i++) {
+ sk = first_sk;
+ sk_nulls_for_each_from(sk, node)
+ if (cookies[i].cookie == atomic64_read(&sk->sk_cookie))
+ return sk;
+ }
+
+ return NULL;
+}
+
+static struct sock *bpf_iter_tcp_resume_listening(struct seq_file *seq)
{
struct inet_hashinfo *hinfo = seq_file_net(seq)->ipv4.tcp_death_row.hashinfo;
struct bpf_tcp_iter_state *iter = seq->private;
struct tcp_iter_state *st = &iter->state;
- struct hlist_nulls_node *node;
- unsigned int expected = 1;
+ unsigned int find_cookie = iter->cur_sk;
+ unsigned int end_cookie = iter->end_sk;
+ int resume_bucket = st->bucket;
struct sock *sk;
- sock_hold(start_sk);
- iter->batch[iter->end_sk++] = start_sk;
+ if (end_cookie && find_cookie == end_cookie)
+ ++st->bucket;
- sk = sk_nulls_next(start_sk);
- sk_nulls_for_each_from(sk, node) {
- if (seq_sk_match(seq, sk)) {
- if (iter->end_sk < iter->max_sk) {
- sock_hold(sk);
- iter->batch[iter->end_sk++] = sk;
- }
- expected++;
+ sk = listening_get_first(seq);
+ iter->cur_sk = 0;
+ iter->end_sk = 0;
+
+ if (sk && st->bucket == resume_bucket && end_cookie) {
+ sk = bpf_iter_tcp_resume_bucket(sk, &iter->batch[find_cookie],
+ end_cookie - find_cookie);
+ if (!sk) {
+ spin_unlock(&hinfo->lhash2[st->bucket].lock);
+ ++st->bucket;
+ sk = listening_get_first(seq);
}
}
- spin_unlock(&hinfo->lhash2[st->bucket].lock);
- return expected;
+ return sk;
}
-static unsigned int bpf_iter_tcp_established_batch(struct seq_file *seq,
- struct sock *start_sk)
+static struct sock *bpf_iter_tcp_resume_established(struct seq_file *seq)
{
struct inet_hashinfo *hinfo = seq_file_net(seq)->ipv4.tcp_death_row.hashinfo;
struct bpf_tcp_iter_state *iter = seq->private;
struct tcp_iter_state *st = &iter->state;
+ unsigned int find_cookie = iter->cur_sk;
+ unsigned int end_cookie = iter->end_sk;
+ int resume_bucket = st->bucket;
+ struct sock *sk;
+
+ if (end_cookie && find_cookie == end_cookie)
+ ++st->bucket;
+
+ sk = established_get_first(seq);
+ iter->cur_sk = 0;
+ iter->end_sk = 0;
+
+ if (sk && st->bucket == resume_bucket && end_cookie) {
+ sk = bpf_iter_tcp_resume_bucket(sk, &iter->batch[find_cookie],
+ end_cookie - find_cookie);
+ if (!sk) {
+ spin_unlock_bh(inet_ehash_lockp(hinfo, st->bucket));
+ ++st->bucket;
+ sk = established_get_first(seq);
+ }
+ }
+
+ return sk;
+}
+
+static struct sock *bpf_iter_tcp_resume(struct seq_file *seq)
+{
+ struct bpf_tcp_iter_state *iter = seq->private;
+ struct tcp_iter_state *st = &iter->state;
+ struct sock *sk = NULL;
+
+ switch (st->state) {
+ case TCP_SEQ_STATE_LISTENING:
+ sk = bpf_iter_tcp_resume_listening(seq);
+ if (sk)
+ break;
+ st->bucket = 0;
+ st->state = TCP_SEQ_STATE_ESTABLISHED;
+ fallthrough;
+ case TCP_SEQ_STATE_ESTABLISHED:
+ sk = bpf_iter_tcp_resume_established(seq);
+ break;
+ }
+
+ return sk;
+}
+
+static unsigned int bpf_iter_tcp_listening_batch(struct seq_file *seq,
+ struct sock **start_sk)
+{
+ struct bpf_tcp_iter_state *iter = seq->private;
struct hlist_nulls_node *node;
unsigned int expected = 1;
struct sock *sk;
- sock_hold(start_sk);
- iter->batch[iter->end_sk++] = start_sk;
+ sock_hold(*start_sk);
+ iter->batch[iter->end_sk++].sk = *start_sk;
- sk = sk_nulls_next(start_sk);
+ sk = sk_nulls_next(*start_sk);
+ *start_sk = NULL;
sk_nulls_for_each_from(sk, node) {
if (seq_sk_match(seq, sk)) {
if (iter->end_sk < iter->max_sk) {
sock_hold(sk);
- iter->batch[iter->end_sk++] = sk;
+ iter->batch[iter->end_sk++].sk = sk;
+ } else if (!*start_sk) {
+ /* Remember where we left off. */
+ *start_sk = sk;
}
expected++;
}
}
- spin_unlock_bh(inet_ehash_lockp(hinfo, st->bucket));
return expected;
}
-static struct sock *bpf_iter_tcp_batch(struct seq_file *seq)
+static unsigned int bpf_iter_tcp_established_batch(struct seq_file *seq,
+ struct sock **start_sk)
+{
+ struct bpf_tcp_iter_state *iter = seq->private;
+ struct hlist_nulls_node *node;
+ struct sock *sk = *start_sk;
+ unsigned int expected = 0;
+
+ *start_sk = NULL;
+ sk_nulls_for_each_from(sk, node) {
+ if (!seq_sk_match(seq, sk))
+ continue;
+ expected++;
+ if (iter->end_sk < iter->max_sk) {
+ /* reqsk_queue_hash_req() inserts with sk_refcnt == 0
+ * and refcount_set()s it after the bucket lock drops.
+ */
+ if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
+ continue;
+ iter->batch[iter->end_sk++].sk = sk;
+ } else if (!*start_sk) {
+ /* Remember where we left off. */
+ *start_sk = sk;
+ }
+ }
+
+ return expected;
+}
+
+static unsigned int bpf_iter_fill_batch(struct seq_file *seq,
+ struct sock **start_sk)
+{
+ struct bpf_tcp_iter_state *iter = seq->private;
+ struct tcp_iter_state *st = &iter->state;
+
+ if (st->state == TCP_SEQ_STATE_LISTENING)
+ return bpf_iter_tcp_listening_batch(seq, start_sk);
+ else
+ return bpf_iter_tcp_established_batch(seq, start_sk);
+}
+
+static void bpf_iter_tcp_unlock_bucket(struct seq_file *seq)
{
struct inet_hashinfo *hinfo = seq_file_net(seq)->ipv4.tcp_death_row.hashinfo;
struct bpf_tcp_iter_state *iter = seq->private;
struct tcp_iter_state *st = &iter->state;
+
+ if (st->state == TCP_SEQ_STATE_LISTENING)
+ spin_unlock(&hinfo->lhash2[st->bucket].lock);
+ else
+ spin_unlock_bh(inet_ehash_lockp(hinfo, st->bucket));
+}
+
+static struct sock *bpf_iter_tcp_batch(struct seq_file *seq)
+{
+ struct bpf_tcp_iter_state *iter = seq->private;
unsigned int expected;
- bool resized = false;
struct sock *sk;
-
- /* The st->bucket is done. Directly advance to the next
- * bucket instead of having the tcp_seek_last_pos() to skip
- * one by one in the current bucket and eventually find out
- * it has to advance to the next bucket.
- */
- if (iter->st_bucket_done) {
- st->offset = 0;
- st->bucket++;
- if (st->state == TCP_SEQ_STATE_LISTENING &&
- st->bucket > hinfo->lhash2_mask) {
- st->state = TCP_SEQ_STATE_ESTABLISHED;
- st->bucket = 0;
- }
- }
+ int err;
again:
- /* Get a new batch */
- iter->cur_sk = 0;
- iter->end_sk = 0;
- iter->st_bucket_done = false;
+ sk = bpf_iter_tcp_resume(seq);
+ if (!sk)
+ return NULL; /* Done */
- sk = tcp_seek_last_pos(seq);
+ expected = bpf_iter_fill_batch(seq, &sk);
+ if (likely(!sk))
+ goto done;
+
+ /* Batch size was too small. */
+ bpf_iter_tcp_unlock_bucket(seq);
+ bpf_iter_tcp_put_batch(iter);
+ err = bpf_iter_tcp_realloc_batch(iter, expected * 3 / 2,
+ GFP_USER);
+ if (err) {
+ iter->cur_sk = 0;
+ iter->end_sk = 0;
+ return ERR_PTR(err);
+ }
+
+ sk = bpf_iter_tcp_resume(seq);
if (!sk)
return NULL; /* Done */
- if (st->state == TCP_SEQ_STATE_LISTENING)
- expected = bpf_iter_tcp_listening_batch(seq, sk);
- else
- expected = bpf_iter_tcp_established_batch(seq, sk);
+ expected = bpf_iter_fill_batch(seq, &sk);
+ if (likely(!sk))
+ goto done;
- if (iter->end_sk == expected) {
- iter->st_bucket_done = true;
- return sk;
+ /* Batch size was still too small. Hold onto the lock while we try
+ * again with a larger batch to make sure the current bucket's size
+ * does not change in the meantime.
+ */
+ err = bpf_iter_tcp_realloc_batch(iter, expected, GFP_NOWAIT);
+ if (err) {
+ bpf_iter_tcp_unlock_bucket(seq);
+ return ERR_PTR(err);
}
- if (!resized && !bpf_iter_tcp_realloc_batch(iter, expected * 3 / 2)) {
- resized = true;
+ bpf_iter_fill_batch(seq, &sk);
+ WARN_ON_ONCE(sk);
+done:
+ bpf_iter_tcp_unlock_bucket(seq);
+ if (unlikely(!iter->end_sk)) {
+ ++iter->state.bucket;
goto again;
}
-
- return sk;
+ return iter->batch[0].sk;
}
static void *bpf_iter_tcp_seq_start(struct seq_file *seq, loff_t *pos)
@@ -3184,16 +3330,11 @@ static void *bpf_iter_tcp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
* meta.seq_num is used instead.
*/
st->num++;
- /* Move st->offset to the next sk in the bucket such that
- * the future start() will resume at st->offset in
- * st->bucket. See tcp_seek_last_pos().
- */
- st->offset++;
- sock_gen_put(iter->batch[iter->cur_sk++]);
+ sock_gen_put(iter->batch[iter->cur_sk++].sk);
}
if (iter->cur_sk < iter->end_sk)
- sk = iter->batch[iter->cur_sk];
+ sk = iter->batch[iter->cur_sk].sk;
else
sk = bpf_iter_tcp_batch(seq);
@@ -3259,10 +3400,8 @@ static void bpf_iter_tcp_seq_stop(struct seq_file *seq, void *v)
(void)tcp_prog_seq_show(prog, &meta, v, 0);
}
- if (iter->cur_sk < iter->end_sk) {
+ if (iter->cur_sk < iter->end_sk)
bpf_iter_tcp_put_batch(iter);
- iter->st_bucket_done = false;
- }
}
static const struct seq_operations bpf_iter_tcp_seq_ops = {
@@ -3578,7 +3717,7 @@ static int bpf_iter_init_tcp(void *priv_data, struct bpf_iter_aux_info *aux)
if (err)
return err;
- err = bpf_iter_tcp_realloc_batch(iter, INIT_BATCH_SZ);
+ err = bpf_iter_tcp_realloc_batch(iter, INIT_BATCH_SZ, GFP_USER);
if (err) {
bpf_iter_fini_seq_net(priv_data);
return err;
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 99688014901e..04246d1f8b3e 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -22,17 +22,19 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
bool remcsum, need_csum, offload_csum, gso_partial;
struct sk_buff *segs = ERR_PTR(-EINVAL);
- struct udphdr *uh = udp_hdr(skb);
u16 mac_offset = skb->mac_header;
__be16 protocol = skb->protocol;
u16 mac_len = skb->mac_len;
int udp_offset, outer_hlen;
+ struct udphdr *uh;
__wsum partial;
bool need_ipsec;
if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
goto out;
+ uh = udp_hdr(skb);
+
/* Adjust partial header checksum to negate old length.
* We cannot rely on the value contained in uh->len as it is
* possible that the actual value exceeds the boundaries of the
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index b788a06bd3a5..1446c4eae681 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -677,6 +677,9 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (!skb2)
return 0;
+ /* Remove debris left by outer IPv6 stack. */
+ memset(IP6CB(skb2), 0, sizeof(*IP6CB(skb2)));
+
skb_dst_drop(skb2);
skb_pull(skb2, offset);
skb_reset_network_header(skb2);
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 2ce05303e403..e602dd3e7ee3 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -988,13 +988,13 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
} else if (rinfo->prefix_len > 128) {
return -EINVAL;
} else if (rinfo->prefix_len > 64) {
- if (rinfo->length < 2) {
+ /* RFC 4191: Length MUST be 3 when Prefix Length > 64 */
+ if (rinfo->length < 3)
return -EINVAL;
- }
} else if (rinfo->prefix_len > 0) {
- if (rinfo->length < 1) {
+ /* RFC 4191: Length MUST be 2 or 3 when Prefix Length > 0 */
+ if (rinfo->length < 2)
return -EINVAL;
- }
}
pref = rinfo->route_pref;
diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c
index 775809ee0206..c6f3748936cc 100644
--- a/net/mac802154/scan.c
+++ b/net/mac802154/scan.c
@@ -415,6 +415,7 @@ void mac802154_beacon_worker(struct work_struct *work)
container_of(work, struct ieee802154_local, beacon_work.work);
struct cfg802154_beacon_request *beacon_req;
struct ieee802154_sub_if_data *sdata;
+ netdevice_tracker dev_tracker;
struct wpan_dev *wpan_dev;
u8 interval;
int ret;
@@ -427,12 +428,14 @@ void mac802154_beacon_worker(struct work_struct *work)
}
sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(beacon_req->wpan_dev);
+ netdev_hold(sdata->dev, &dev_tracker, GFP_ATOMIC);
/* Wait an arbitrary amount of time in case we cannot use the device */
if (local->suspended || !ieee802154_sdata_running(sdata)) {
rcu_read_unlock();
queue_delayed_work(local->mac_wq, &local->beacon_work,
msecs_to_jiffies(1000));
+ netdev_put(sdata->dev, &dev_tracker);
return;
}
@@ -450,6 +453,7 @@ void mac802154_beacon_worker(struct work_struct *work)
if (interval < IEEE802154_ACTIVE_SCAN_DURATION)
queue_delayed_work(local->mac_wq, &local->beacon_work,
local->beacon_interval);
+ netdev_put(sdata->dev, &dev_tracker);
}
int mac802154_stop_beacons_locked(struct ieee802154_local *local,
diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c
index 2f872d064396..8cc538358f6a 100644
--- a/net/ncsi/ncsi-netlink.c
+++ b/net/ncsi/ncsi-netlink.c
@@ -461,6 +461,10 @@ static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info)
nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN;
nca.info = info;
nca.payload = ntohs(hdr->length);
+ if (nca.payload > len - sizeof(*hdr)) {
+ ret = -EINVAL;
+ goto out_netlink;
+ }
nca.data = data + sizeof(*hdr);
ret = ncsi_xmit_cmd(&nca);
diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h
index cb48a2b9cb9f..cf8468bf987d 100644
--- a/net/netfilter/ipset/ip_set_bitmap_gen.h
+++ b/net/netfilter/ipset/ip_set_bitmap_gen.h
@@ -77,7 +77,7 @@ mtype_flush(struct ip_set *set)
mtype_ext_cleanup(set);
bitmap_zero(map->members, map->elements);
set->elements = 0;
- set->ext_size = 0;
+ atomic64_set(&set->ext_size, 0);
}
/* Calculate the actual memory size of the set data */
@@ -93,7 +93,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
{
const struct mtype *map = set->data;
struct nlattr *nested;
- size_t memsize = mtype_memsize(map, set->dsize) + set->ext_size;
+ size_t memsize = mtype_memsize(map, set->dsize) + atomic64_read(&set->ext_size);
nested = nla_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index f51a1af31513..29bf5ee74fe3 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -350,7 +350,7 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment,
size_t len = ext->comment ? strlen(ext->comment) : 0;
if (unlikely(c)) {
- set->ext_size -= sizeof(*c) + strlen(c->str) + 1;
+ atomic64_sub(sizeof(*c) + strlen(c->str) + 1, &set->ext_size);
rcu_assign_pointer(comment->c, NULL);
kfree_rcu(c, rcu);
}
@@ -362,7 +362,7 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment,
if (unlikely(!c))
return;
strscpy(c->str, ext->comment, len + 1);
- set->ext_size += sizeof(*c) + strlen(c->str) + 1;
+ atomic64_add(sizeof(*c) + strlen(c->str) + 1, &set->ext_size);
rcu_assign_pointer(comment->c, c);
}
EXPORT_SYMBOL_GPL(ip_set_init_comment);
@@ -392,7 +392,7 @@ ip_set_comment_free(struct ip_set *set, void *ptr)
c = rcu_dereference_protected(comment->c, 1);
if (unlikely(!c))
return;
- set->ext_size -= sizeof(*c) + strlen(c->str) + 1;
+ atomic64_sub(sizeof(*c) + strlen(c->str) + 1, &set->ext_size);
rcu_assign_pointer(comment->c, NULL);
kfree_rcu(c, rcu);
}
diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h
index 4c1082e38e3d..81d7f601ed06 100644
--- a/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/net/netfilter/ipset/ip_set_hash_gen.h
@@ -1289,7 +1289,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
rcu_read_lock_bh();
t = rcu_dereference_bh(h->table);
mtype_ext_size(set, &elements, &ext_size);
- memsize = mtype_ahash_memsize(h, t) + ext_size + set->ext_size;
+ memsize = mtype_ahash_memsize(h, t) + ext_size + atomic64_read(&set->ext_size);
htable_bits = t->htable_bits;
rcu_read_unlock_bh();
diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c
index 83e1fdcc752d..9d6ab69ca1a3 100644
--- a/net/netfilter/ipset/ip_set_list_set.c
+++ b/net/netfilter/ipset/ip_set_list_set.c
@@ -421,7 +421,7 @@ list_set_flush(struct ip_set *set)
list_for_each_entry_safe(e, n, &map->members, list)
list_set_del(set, e);
set->elements = 0;
- set->ext_size = 0;
+ atomic64_set(&set->ext_size, 0);
}
static void
@@ -455,7 +455,7 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
{
const struct list_set *map = set->data;
struct nlattr *nested;
- size_t memsize = list_set_memsize(map, set->dsize) + set->ext_size;
+ size_t memsize = list_set_memsize(map, set->dsize) + atomic64_read(&set->ext_size);
nested = nla_nest_start(skb, IPSET_ATTR_DATA);
if (!nested)
diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
index 5ed91a8a8fdf..f50fbcb13f89 100644
--- a/net/netfilter/ipvs/ip_vs_conn.c
+++ b/net/netfilter/ipvs/ip_vs_conn.c
@@ -570,12 +570,6 @@ static inline void ip_vs_bind_xmit_v6(struct ip_vs_conn *cp)
#endif
-static inline int ip_vs_dest_totalconns(struct ip_vs_dest *dest)
-{
- return atomic_read(&dest->activeconns)
- + atomic_read(&dest->inactconns);
-}
-
/*
* Bind a connection entry with a virtual service destination
* Called just after a new connection entry is created.
@@ -627,23 +621,22 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
/* Update the connection counters */
if (!(flags & IP_VS_CONN_F_TEMPLATE)) {
+ int tc;
+
/* It is a normal connection, so modify the counters
* according to the flags, later the protocol can
* update them on state change
*/
if (!(flags & IP_VS_CONN_F_INACTIVE))
atomic_inc(&dest->activeconns);
- else
- atomic_inc(&dest->inactconns);
+ tc = atomic_inc_return(&dest->totalconns);
+ if (tc == READ_ONCE(dest->u_threshold))
+ ip_vs_dest_update_overload(dest, 1);
} else {
/* It is a persistent connection/template, so increase
the persistent connection counter */
atomic_inc(&dest->persistconns);
}
-
- if (dest->u_threshold != 0 &&
- ip_vs_dest_totalconns(dest) >= dest->u_threshold)
- dest->flags |= IP_VS_DEST_F_OVERLOAD;
}
@@ -724,30 +717,20 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
/* Update the connection counters */
if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) {
- /* It is a normal connection, so decrease the inactconns
- or activeconns counter */
- if (cp->flags & IP_VS_CONN_F_INACTIVE) {
- atomic_dec(&dest->inactconns);
- } else {
+ int tc;
+
+ /* It is a normal connection, so decrease the counters */
+ if (!(cp->flags & IP_VS_CONN_F_INACTIVE))
atomic_dec(&dest->activeconns);
- }
+ tc = atomic_fetch_dec(&dest->totalconns);
+ if (tc == READ_ONCE(dest->l_threshold_val))
+ ip_vs_dest_update_overload(dest, -1);
} else {
/* It is a persistent connection/template, so decrease
the persistent connection counter */
atomic_dec(&dest->persistconns);
}
- if (dest->l_threshold != 0) {
- if (ip_vs_dest_totalconns(dest) < dest->l_threshold)
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
- } else if (dest->u_threshold != 0) {
- if (ip_vs_dest_totalconns(dest) * 4 < dest->u_threshold * 3)
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
- } else {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
- }
-
ip_vs_dest_put(dest);
}
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index 6207a91e93f3..d121ea3d16bc 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -747,28 +747,27 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
*/
void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_conn *cp, int inout, unsigned int toff,
- bool has_ports)
+ bool has_ports, struct ip_vs_iphdr *ciph)
{
struct iphdr *iph = ip_hdr(skb);
struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff);
- struct iphdr *ciph = (struct iphdr *)(icmph + 1);
- unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr);
+ struct iphdr *cih = (struct iphdr *)(icmph + 1);
if (inout) {
iph->saddr = cp->vaddr.ip;
ip_send_check(iph);
- ciph->daddr = cp->vaddr.ip;
- ip_send_check(ciph);
+ cih->daddr = cp->vaddr.ip;
+ ip_send_check(cih);
} else {
iph->daddr = cp->daddr.ip;
ip_send_check(iph);
- ciph->saddr = cp->daddr.ip;
- ip_send_check(ciph);
+ cih->saddr = cp->daddr.ip;
+ ip_send_check(cih);
}
/* the TCP/UDP/SCTP port */
if (has_ports) {
- __be16 *ports = (void *)ciph + ciph->ihl*4;
+ __be16 *ports = (void *)(skb->data + ciph->len);
if (inout)
ports[1] = cp->vport;
@@ -782,10 +781,10 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
skb->ip_summed = CHECKSUM_UNNECESSARY;
if (inout)
- IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
+ IP_VS_DBG_PKT(11, AF_INET, pp, skb, ciph->off,
"Forwarding altered outgoing ICMP");
else
- IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
+ IP_VS_DBG_PKT(11, AF_INET, pp, skb, ciph->off,
"Forwarding altered incoming ICMP");
}
@@ -878,7 +877,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph);
else
#endif
- ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports);
+ ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports, ciph);
if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
goto out;
@@ -914,7 +913,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
struct ip_vs_iphdr ciph;
struct ip_vs_conn *cp;
struct ip_vs_protocol *pp;
- unsigned int offset, ihl;
+ unsigned int offset;
union nf_inet_addr snet;
*related = 1;
@@ -927,7 +926,6 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
return NF_ACCEPT;
}
- ihl = ipvsh->len;
offset = ipvsh->len;
ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
if (ic == NULL)
@@ -953,11 +951,15 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
/* Now find the contained IP header */
offset += sizeof(_icmph);
+ if (!ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, true, &ciph))
+ return NF_ACCEPT; /* The packet looks wrong, ignore */
+
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
- if (!(cih && cih->version == 4 && cih->ihl >= 5))
+ if (!(cih && cih->version == 4 &&
+ ciph.len - ciph.off >= sizeof(struct iphdr)))
return NF_ACCEPT; /* The packet looks wrong, ignore */
- pp = ip_vs_proto_get(cih->protocol);
+ pp = ip_vs_proto_get(ciph.protocol);
if (!pp)
return NF_ACCEPT;
@@ -968,8 +970,6 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
"Checking outgoing ICMP for");
- ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, true, &ciph);
-
/* The embedded headers contain source and dest in reverse order */
cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto,
ipvs, AF_INET, skb, &ciph);
@@ -977,8 +977,8 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
return NF_ACCEPT;
snet.ip = ipvsh->saddr.ip;
- return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl,
- hooknum);
+ return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph,
+ ipvsh->len, hooknum);
}
#ifdef CONFIG_IP_VS_IPV6
@@ -1625,10 +1625,12 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
/* Now find the contained IP header */
offset += sizeof(_icmph);
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
- if (!(cih && cih->version == 4 && cih->ihl >= 5))
+ if (!cih)
return NF_ACCEPT; /* The packet looks wrong, ignore */
- raddr = (union nf_inet_addr *)&cih->daddr;
hlen_ipip = cih->ihl * 4;
+ if (!(cih->version == 4 && hlen_ipip >= sizeof(struct iphdr)))
+ return NF_ACCEPT; /* The packet looks wrong, ignore */
+ raddr = (union nf_inet_addr *)&cih->daddr;
/* Special case for errors for IPIP/UDP/GRE tunnel packets */
tunnel = false;
@@ -1645,9 +1647,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP)
return NF_ACCEPT;
offset += hlen_ipip;
- cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
- if (!(cih && cih->version == 4 && cih->ihl >= 5))
- return NF_ACCEPT; /* The packet looks wrong, ignore */
tunnel = true;
} else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */
cih->protocol == IPPROTO_GRE) && /* Can be GRE encap */
@@ -1672,21 +1671,25 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
/* Skip IP and UDP/GRE tunnel headers */
offset = offset2 + ulen;
/* Now we should be at the original IP header */
- cih = skb_header_pointer(skb, offset, sizeof(_ciph),
- &_ciph);
- if (cih && cih->version == 4 && cih->ihl >= 5 &&
- iproto == IPPROTO_IPIP)
+ if (iproto == IPPROTO_IPIP)
tunnel = true;
else
return NF_ACCEPT;
}
}
- pd = ip_vs_proto_data_get(ipvs, cih->protocol);
+ if (!ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph))
+ return NF_ACCEPT;
+ pd = ip_vs_proto_data_get(ipvs, ciph.protocol);
if (!pd)
return NF_ACCEPT;
pp = pd->pp;
+ cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
+ if (!(cih && cih->version == 4 &&
+ ciph.len - ciph.off >= sizeof(struct iphdr)))
+ return NF_ACCEPT; /* The packet looks wrong, ignore */
+
/* Is the embedded protocol header present? */
if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
return NF_ACCEPT;
@@ -1694,9 +1697,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
"Checking incoming ICMP for");
- offset2 = offset;
- ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph);
-
/* The embedded headers contain source and dest in reverse order.
* For IPIP/UDP/GRE tunnel this is error for request, not for reply.
*/
@@ -1726,11 +1726,12 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
}
if (tunnel) {
- unsigned int hlen_orig = cih->ihl * 4;
+ unsigned int hlen_orig = ciph.len - ciph.off;
__be32 info = ic->un.gateway;
__u8 type = ic->type;
__u8 code = ic->code;
+ offset2 = offset;
/* Update the MTU */
if (ic->type == ICMP_DEST_UNREACH &&
ic->code == ICMP_FRAG_NEEDED) {
@@ -1771,6 +1772,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
if (pskb_pull(skb, offset2) == NULL)
goto ignore_tunnel;
skb_reset_network_header(skb);
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
/* Ensure the IP header is present in headroom */
if (!pskb_may_pull(skb, hlen_orig))
goto ignore_tunnel;
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index fb638758594d..8aa29596b36f 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -960,6 +960,40 @@ void ip_vs_stats_free(struct ip_vs_stats *stats)
}
}
+/* Update overload flag based on number of dest conns and lower/upper
+ * connection thresholds:
+ * - conns reach u_threshold and exceed it: set the flag
+ * - conns go below l_threshold (or 75% of u_threshold): clear the flag
+ */
+static void __ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode)
+{
+ int conns;
+ u32 l, u;
+
+ lockdep_assert_held(&dest->dst_lock);
+ u = READ_ONCE(dest->u_threshold);
+ if (!u)
+ goto unset;
+ l = READ_ONCE(dest->l_threshold_val);
+ conns = atomic_read(&dest->totalconns);
+ if (conns >= (mode > 0 ? l : u)) {
+ dest->flags |= IP_VS_DEST_F_OVERLOAD;
+ return;
+ }
+ if (conns >= (mode < 0 ? u : l))
+ return;
+
+unset:
+ dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
+}
+
+void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode)
+{
+ spin_lock_bh(&dest->dst_lock);
+ __ip_vs_dest_update_overload(dest, mode);
+ spin_unlock_bh(&dest->dst_lock);
+}
+
/*
* Update a destination in the given service
*/
@@ -1026,10 +1060,19 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
/* set the dest status flags */
dest->flags |= IP_VS_DEST_F_AVAILABLE;
- if (udest->u_threshold == 0 || udest->u_threshold > dest->u_threshold)
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
- dest->u_threshold = udest->u_threshold;
- dest->l_threshold = udest->l_threshold;
+ if (READ_ONCE(dest->u_threshold) != udest->u_threshold ||
+ READ_ONCE(dest->l_threshold) != udest->l_threshold) {
+ spin_lock_bh(&dest->dst_lock);
+ WRITE_ONCE(dest->u_threshold, udest->u_threshold);
+ WRITE_ONCE(dest->l_threshold, udest->l_threshold);
+ /* Low threshold defaults to 75% of upper threshold */
+ WRITE_ONCE(dest->l_threshold_val,
+ udest->l_threshold ? :
+ (udest->u_threshold -
+ (udest->u_threshold >> 2)));
+ __ip_vs_dest_update_overload(dest, 0);
+ spin_unlock_bh(&dest->dst_lock);
+ }
dest->af = udest->af;
@@ -1101,7 +1144,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
dest->port = udest->port;
atomic_set(&dest->activeconns, 0);
- atomic_set(&dest->inactconns, 0);
+ atomic_set(&dest->totalconns, 0);
atomic_set(&dest->persistconns, 0);
refcount_set(&dest->refcnt, 1);
@@ -1142,6 +1185,9 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
return -ERANGE;
}
+ if (udest->u_threshold > INT_MAX)
+ return -EINVAL;
+
if (udest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
if (udest->tun_port == 0) {
pr_err("%s(): tunnel port is zero\n", __func__);
@@ -1212,6 +1258,9 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
return -ERANGE;
}
+ if (udest->u_threshold > INT_MAX)
+ return -EINVAL;
+
if (udest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
if (udest->tun_port == 0) {
pr_err("%s(): tunnel port is zero\n", __func__);
@@ -2456,7 +2505,7 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
ip_vs_fwd_name(atomic_read(&dest->conn_flags)),
atomic_read(&dest->weight),
atomic_read(&dest->activeconns),
- atomic_read(&dest->inactconns));
+ ip_vs_dest_inactconns(dest));
else
#endif
seq_printf(seq,
@@ -2467,7 +2516,7 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
ip_vs_fwd_name(atomic_read(&dest->conn_flags)),
atomic_read(&dest->weight),
atomic_read(&dest->activeconns),
- atomic_read(&dest->inactconns));
+ ip_vs_dest_inactconns(dest));
}
}
@@ -2950,10 +2999,10 @@ __ip_vs_get_dest_entries(struct netns_ipvs *ipvs, const struct ip_vs_get_dests *
entry.port = dest->port;
entry.conn_flags = atomic_read(&dest->conn_flags);
entry.weight = atomic_read(&dest->weight);
- entry.u_threshold = dest->u_threshold;
- entry.l_threshold = dest->l_threshold;
+ entry.u_threshold = READ_ONCE(dest->u_threshold);
+ entry.l_threshold = READ_ONCE(dest->l_threshold);
entry.activeconns = atomic_read(&dest->activeconns);
- entry.inactconns = atomic_read(&dest->inactconns);
+ entry.inactconns = ip_vs_dest_inactconns(dest);
entry.persistconns = atomic_read(&dest->persistconns);
ip_vs_copy_stats(&kstats, &dest->stats);
ip_vs_export_stats_user(&entry.stats, &kstats);
@@ -3552,12 +3601,14 @@ static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
dest->tun_port) ||
nla_put_u16(skb, IPVS_DEST_ATTR_TUN_FLAGS,
dest->tun_flags) ||
- nla_put_u32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold) ||
- nla_put_u32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold) ||
+ nla_put_u32(skb, IPVS_DEST_ATTR_U_THRESH,
+ READ_ONCE(dest->u_threshold)) ||
+ nla_put_u32(skb, IPVS_DEST_ATTR_L_THRESH,
+ READ_ONCE(dest->l_threshold)) ||
nla_put_u32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
atomic_read(&dest->activeconns)) ||
nla_put_u32(skb, IPVS_DEST_ATTR_INACT_CONNS,
- atomic_read(&dest->inactconns)) ||
+ ip_vs_dest_inactconns(dest)) ||
nla_put_u32(skb, IPVS_DEST_ATTR_PERSIST_CONNS,
atomic_read(&dest->persistconns)) ||
nla_put_u16(skb, IPVS_DEST_ATTR_ADDR_FAMILY, dest->af))
diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c
index 3492108bb3b9..64fbf2aa4cce 100644
--- a/net/netfilter/ipvs/ip_vs_est.c
+++ b/net/netfilter/ipvs/ip_vs_est.c
@@ -187,8 +187,11 @@ static int ip_vs_estimation_kthread(void *data)
}
/* kthread 0 will handle the calc phase */
- if (ipvs->est_calc_phase)
+ if (ipvs->est_calc_phase) {
ip_vs_est_calc_phase(ipvs);
+ if (kthread_should_stop() || !READ_ONCE(ipvs->enable))
+ return 0;
+ }
}
while (1) {
@@ -263,6 +266,7 @@ int ip_vs_est_kthread_start(struct netns_ipvs *ipvs,
kd->task = NULL;
goto out;
}
+ get_task_struct(kd->task);
set_user_nice(kd->task, sysctl_est_nice(ipvs));
set_cpus_allowed_ptr(kd->task, sysctl_est_cpulist(ipvs));
@@ -278,7 +282,7 @@ void ip_vs_est_kthread_stop(struct ip_vs_est_kt_data *kd)
{
if (kd->task) {
pr_info("stopping estimator thread %d...\n", kd->id);
- kthread_stop(kd->task);
+ kthread_stop_put(kd->task);
kd->task = NULL;
}
}
@@ -510,7 +514,7 @@ static void ip_vs_est_kthread_destroy(struct ip_vs_est_kt_data *kd)
if (kd) {
if (kd->task) {
pr_info("stop unused estimator thread %d...\n", kd->id);
- kthread_stop(kd->task);
+ kthread_stop_put(kd->task);
}
ip_vs_stats_free(kd->calc_stats);
kfree(kd);
diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c
index c2764505e380..cd1a860ebaec 100644
--- a/net/netfilter/ipvs/ip_vs_lc.c
+++ b/net/netfilter/ipvs/ip_vs_lc.c
@@ -31,7 +31,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
/*
* Simply select the server with the least number of
- * (activeconns<<5) + inactconns
+ * (activeconns*256) + totalconns
* Except whose weight is equal to zero.
* If the weight is equal to zero, it means that the server is
* quiesced, the existing connections to the server still get
@@ -57,7 +57,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
IP_VS_DBG_ADDR(least->af, &least->addr),
ntohs(least->port),
atomic_read(&least->activeconns),
- atomic_read(&least->inactconns));
+ ip_vs_dest_inactconns(least));
return least;
}
diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
index 3dbd3096e163..fb8af6b15a39 100644
--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
@@ -193,7 +193,7 @@ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
struct sctphdr *sh;
__le32 cmp, val;
- if (!ip_vs_checksum_needed(skb, af))
+ if (!ip_vs_checksum_needed(skb))
return 1;
sh = (struct sctphdr *)(skb->data + sctphoff);
cmp = sh->checksum;
@@ -446,12 +446,10 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
if (!(cp->flags & IP_VS_CONN_F_INACTIVE) &&
(next_state != IP_VS_SCTP_S_ESTABLISHED)) {
atomic_dec(&dest->activeconns);
- atomic_inc(&dest->inactconns);
cp->flags |= IP_VS_CONN_F_INACTIVE;
} else if ((cp->flags & IP_VS_CONN_F_INACTIVE) &&
(next_state == IP_VS_SCTP_S_ESTABLISHED)) {
atomic_inc(&dest->activeconns);
- atomic_dec(&dest->inactconns);
cp->flags &= ~IP_VS_CONN_F_INACTIVE;
}
}
diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
index 1ac9c233537d..944efd34290c 100644
--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
@@ -527,12 +527,10 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
if (!(cp->flags & IP_VS_CONN_F_INACTIVE) &&
!tcp_state_active(new_state)) {
atomic_dec(&dest->activeconns);
- atomic_inc(&dest->inactconns);
cp->flags |= IP_VS_CONN_F_INACTIVE;
} else if ((cp->flags & IP_VS_CONN_F_INACTIVE) &&
tcp_state_active(new_state)) {
atomic_inc(&dest->activeconns);
- atomic_dec(&dest->inactconns);
cp->flags &= ~IP_VS_CONN_F_INACTIVE;
}
}
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index 3402675bf521..4c6e25398536 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -879,13 +879,10 @@ static void ip_vs_proc_conn(struct netns_ipvs *ipvs, struct ip_vs_conn_param *pa
spin_lock_bh(&cp->lock);
if ((cp->flags ^ flags) & IP_VS_CONN_F_INACTIVE &&
!(flags & IP_VS_CONN_F_TEMPLATE) && dest) {
- if (flags & IP_VS_CONN_F_INACTIVE) {
+ if (flags & IP_VS_CONN_F_INACTIVE)
atomic_dec(&dest->activeconns);
- atomic_inc(&dest->inactconns);
- } else {
+ else
atomic_inc(&dest->activeconns);
- atomic_dec(&dest->inactconns);
- }
}
flags &= IP_VS_CONN_F_BACKUP_UPD_MASK;
flags |= cp->flags & ~IP_VS_CONN_F_BACKUP_UPD_MASK;
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index b85420a3a14c..240378e37741 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -1577,7 +1577,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
if (skb_cow(skb, rt->dst.dev->hard_header_len))
goto tx_error;
- ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports);
+ ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports, ciph);
/* Another hack: avoid icmp_send in ip_fragment */
skb->ignore_df = 1;
diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c
index f36727ed91e1..6ffa1f21d985 100644
--- a/net/netfilter/nf_conntrack_proto.c
+++ b/net/netfilter/nf_conntrack_proto.c
@@ -79,6 +79,12 @@ void nf_ct_l4proto_log_invalid(const struct sk_buff *skb,
struct net *net;
va_list args;
+ /* nfnetlink_log may re-enter conntrack attribute dumping and try to
+ * take ct->lock again via helpers such as tcp_to_nlattr(), so invalid
+ * conntrack logs must only be emitted after dropping ct->lock.
+ */
+ lockdep_assert_not_held(&ct->lock);
+
net = nf_ct_net(ct);
if (likely(net->ct.sysctl_log_invalid == 0))
return;
diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c
index 0dd55d3fba38..ef51f0babf94 100644
--- a/net/netfilter/nf_conntrack_proto_sctp.c
+++ b/net/netfilter/nf_conntrack_proto_sctp.c
@@ -341,10 +341,12 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
struct sctphdr _sctph;
const struct sctp_chunkhdr *sch;
struct sctp_chunkhdr _sch;
+ bool log_invalid = false;
u_int32_t offset, count;
unsigned int *timeouts;
unsigned long map[256 / sizeof(unsigned long)] = { 0 };
bool ignore = false;
+ u8 invalid_type = 0;
if (sctp_error(skb, dataoff, state))
return -NF_ACCEPT;
@@ -456,10 +458,8 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
/* Invalid */
if (new_state == SCTP_CONNTRACK_MAX) {
- nf_ct_l4proto_log_invalid(skb, ct, state,
- "Invalid, old_state %d, dir %d, type %d",
- old_state, dir, sch->type);
-
+ log_invalid = true;
+ invalid_type = sch->type;
goto out_unlock;
}
@@ -534,6 +534,10 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
out_unlock:
spin_unlock_bh(&ct->lock);
+ if (log_invalid)
+ nf_ct_l4proto_log_invalid(skb, ct, state,
+ "Invalid, old_state %d, dir %d, type %d",
+ old_state, dir, invalid_type);
out:
return -NF_ACCEPT;
}
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index e99ab1e88e9f..c289bec0c7c7 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -480,37 +480,81 @@ static void tcp_init_sender(struct ip_ct_tcp_state *sender,
}
}
-__printf(6, 7)
-static enum nf_ct_tcp_action nf_tcp_log_invalid(const struct sk_buff *skb,
- const struct nf_conn *ct,
- const struct nf_hook_state *state,
- const struct ip_ct_tcp_state *sender,
- enum nf_ct_tcp_action ret,
- const char *fmt, ...)
+enum nf_tcp_invalid_log_type {
+ NF_TCP_LOG_NONE,
+ NF_TCP_LOG_OVERSHOT,
+ NF_TCP_LOG_SEQ_OVER,
+ NF_TCP_LOG_ACK_OVER,
+ NF_TCP_LOG_SEQ_UNDER,
+ NF_TCP_LOG_ACK_UNDER,
+};
+
+struct nf_tcp_invalid_log {
+ enum nf_tcp_invalid_log_type type;
+ u32 value;
+};
+
+static enum nf_ct_tcp_action
+nf_tcp_store_invalid(const struct nf_conn *ct,
+ const struct ip_ct_tcp_state *sender,
+ struct nf_tcp_invalid_log *log,
+ enum nf_ct_tcp_action ret,
+ enum nf_tcp_invalid_log_type type,
+ u32 value)
{
const struct nf_tcp_net *tn = nf_tcp_pernet(nf_ct_net(ct));
- struct va_format vaf;
- va_list args;
bool be_liberal;
be_liberal = sender->flags & IP_CT_TCP_FLAG_BE_LIBERAL || tn->tcp_be_liberal;
if (be_liberal)
return NFCT_TCP_ACCEPT;
- va_start(args, fmt);
- vaf.fmt = fmt;
- vaf.va = &args;
- nf_ct_l4proto_log_invalid(skb, ct, state, "%pV", &vaf);
- va_end(args);
-
+ log->type = type;
+ log->value = value;
return ret;
}
+static void nf_tcp_log_invalid(const struct sk_buff *skb,
+ const struct nf_conn *ct,
+ const struct nf_hook_state *state,
+ const struct nf_tcp_invalid_log *log)
+{
+ switch (log->type) {
+ case NF_TCP_LOG_OVERSHOT:
+ nf_ct_l4proto_log_invalid(skb, ct, state,
+ "%u bytes more than expected",
+ log->value);
+ break;
+ case NF_TCP_LOG_SEQ_OVER:
+ nf_ct_l4proto_log_invalid(skb, ct, state,
+ "SEQ is over upper bound %u (over the window of the receiver)",
+ log->value);
+ break;
+ case NF_TCP_LOG_ACK_OVER:
+ nf_ct_l4proto_log_invalid(skb, ct, state,
+ "ACK is over upper bound %u (ACKed data not seen yet)",
+ log->value);
+ break;
+ case NF_TCP_LOG_SEQ_UNDER:
+ nf_ct_l4proto_log_invalid(skb, ct, state,
+ "SEQ is under lower bound %u (already ACKed data retransmitted)",
+ log->value);
+ break;
+ case NF_TCP_LOG_ACK_UNDER:
+ nf_ct_l4proto_log_invalid(skb, ct, state,
+ "ignored ACK under lower bound %u (possible overly delayed)",
+ log->value);
+ break;
+ case NF_TCP_LOG_NONE:
+ break;
+ }
+}
+
static enum nf_ct_tcp_action
tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir,
unsigned int index, const struct sk_buff *skb,
unsigned int dataoff, const struct tcphdr *tcph,
- const struct nf_hook_state *hook_state)
+ struct nf_tcp_invalid_log *log)
{
struct ip_ct_tcp *state = &ct->proto.tcp;
struct ip_ct_tcp_state *sender = &state->seen[dir];
@@ -640,31 +684,29 @@ tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir,
sender->td_end = end;
sender->flags |= IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED;
- return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_IGNORE,
- "%u bytes more than expected", overshot);
+ return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_IGNORE,
+ NF_TCP_LOG_OVERSHOT, overshot);
}
- return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_INVALID,
- "SEQ is over upper bound %u (over the window of the receiver)",
- sender->td_maxend + 1);
+ return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_INVALID,
+ NF_TCP_LOG_SEQ_OVER, sender->td_maxend + 1);
}
if (!before(sack, receiver->td_end + 1))
- return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_INVALID,
- "ACK is over upper bound %u (ACKed data not seen yet)",
- receiver->td_end + 1);
+ return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_INVALID,
+ NF_TCP_LOG_ACK_OVER, receiver->td_end + 1);
/* Is the ending sequence in the receive window (if available)? */
in_recv_win = !receiver->td_maxwin ||
after(end, sender->td_end - receiver->td_maxwin - 1);
if (!in_recv_win)
- return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_IGNORE,
- "SEQ is under lower bound %u (already ACKed data retransmitted)",
- sender->td_end - receiver->td_maxwin - 1);
+ return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_IGNORE,
+ NF_TCP_LOG_SEQ_UNDER,
+ sender->td_end - receiver->td_maxwin - 1);
if (!after(sack, receiver->td_end - MAXACKWINDOW(sender) - 1))
- return nf_tcp_log_invalid(skb, ct, hook_state, sender, NFCT_TCP_IGNORE,
- "ignored ACK under lower bound %u (possible overly delayed)",
- receiver->td_end - MAXACKWINDOW(sender) - 1);
+ return nf_tcp_store_invalid(ct, sender, log, NFCT_TCP_IGNORE,
+ NF_TCP_LOG_ACK_UNDER,
+ receiver->td_end - MAXACKWINDOW(sender) - 1);
/* Take into account window scaling (RFC 1323). */
if (!tcph->syn)
@@ -719,11 +761,8 @@ tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir,
return NFCT_TCP_ACCEPT;
}
-static void __cold nf_tcp_handle_invalid(struct nf_conn *ct,
- enum ip_conntrack_dir dir,
- int index,
- const struct sk_buff *skb,
- const struct nf_hook_state *hook_state)
+static bool __cold
+nf_tcp_handle_invalid(struct nf_conn *ct, enum ip_conntrack_dir dir, int index)
{
const unsigned int *timeouts;
const struct nf_tcp_net *tn;
@@ -732,7 +771,7 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct,
if (!test_bit(IPS_ASSURED_BIT, &ct->status) ||
test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status))
- return;
+ return false;
/* We don't want to have connections hanging around in ESTABLISHED
* state for long time 'just because' conntrack deemed a FIN/RST
@@ -747,7 +786,7 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct,
case TCP_FIN_SET:
break;
default:
- return;
+ return false;
}
if (ct->proto.tcp.last_dir != dir &&
@@ -755,7 +794,7 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct,
ct->proto.tcp.last_index == TCP_RST_SET)) {
expires = nf_ct_expires(ct);
if (expires < 120 * HZ)
- return;
+ return false;
tn = nf_tcp_pernet(nf_ct_net(ct));
timeouts = nf_ct_timeout_lookup(ct);
@@ -764,16 +803,15 @@ static void __cold nf_tcp_handle_invalid(struct nf_conn *ct,
timeout = READ_ONCE(timeouts[TCP_CONNTRACK_UNACK]);
if (expires > timeout) {
- nf_ct_l4proto_log_invalid(skb, ct, hook_state,
- "packet (index %d, dir %d) response for index %d lower timeout to %u",
- index, dir, ct->proto.tcp.last_index, timeout);
-
WRITE_ONCE(ct->timeout, timeout + nfct_time_stamp);
+ return true;
}
} else {
ct->proto.tcp.last_index = index;
ct->proto.tcp.last_dir = dir;
}
+
+ return false;
}
/* table of valid flag combinations - PUSH, ECE and CWR are always valid */
@@ -969,7 +1007,9 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
struct net *net = nf_ct_net(ct);
struct nf_tcp_net *tn = nf_tcp_pernet(net);
enum tcp_conntrack new_state, old_state;
+ struct nf_tcp_invalid_log log = {};
unsigned int index, *timeouts;
+ bool lowered_timeout = false;
enum nf_ct_tcp_action res;
enum ip_conntrack_dir dir;
const struct tcphdr *th;
@@ -1252,14 +1292,18 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
}
res = tcp_in_window(ct, dir, index,
- skb, dataoff, th, state);
+ skb, dataoff, th, &log);
switch (res) {
case NFCT_TCP_IGNORE:
spin_unlock_bh(&ct->lock);
+ nf_tcp_log_invalid(skb, ct, state, &log);
return NF_ACCEPT;
case NFCT_TCP_INVALID:
- nf_tcp_handle_invalid(ct, dir, index, skb, state);
+ lowered_timeout = nf_tcp_handle_invalid(ct, dir, index);
spin_unlock_bh(&ct->lock);
+ nf_tcp_log_invalid(skb, ct, state, &log);
+ if (lowered_timeout)
+ nf_ct_l4proto_log_invalid(skb, ct, state, "lowered timeout to UNACK");
return -NF_ACCEPT;
case NFCT_TCP_ACCEPT:
break;
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 7af539d63c06..8436a560ab1f 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -1083,9 +1083,8 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
error = -EEXIST;
goto err_unlock_ovs;
}
- /* The flow identifier has to be the same for flow updates.
- * Look for any overlapping flow.
- */
+
+ /* Look for any overlapping flow. */
if (unlikely(!ovs_flow_cmp(flow, &match))) {
if (ovs_identifier_is_key(&flow->id))
flow = ovs_flow_tbl_lookup_exact(&dp->table,
@@ -1097,6 +1096,30 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
goto err_unlock_ovs;
}
}
+
+ if (unlikely(reply)) {
+ size_t cur, req;
+
+ cur = ovs_flow_cmd_msg_size(acts, &new_flow->id,
+ ufid_flags);
+ req = ovs_flow_cmd_msg_size(acts, &flow->id,
+ ufid_flags);
+ if (cur < req) {
+ struct sk_buff *resized;
+
+ resized = ovs_flow_cmd_alloc_info(acts,
+ &flow->id,
+ info, false,
+ ufid_flags);
+ if (IS_ERR(resized)) {
+ error = PTR_ERR(resized);
+ goto err_unlock_ovs;
+ }
+ kfree_skb(reply);
+ reply = resized;
+ }
+ }
+
/* Update actions. */
old_acts = ovsl_dereference(flow->sf_acts);
rcu_assign_pointer(flow->sf_acts, acts);
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index b80bd3a90773..52e261ce91e8 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -893,8 +893,6 @@ static int key_extract_l3l4(struct sk_buff *skb, struct sw_flow_key *key)
* Ethernet header
* @key: output flow key
*
- * The caller must ensure that skb->len >= ETH_HLEN.
- *
* Initializes @skb header fields as follows:
*
* - skb->mac_header: the L2 header.
@@ -914,8 +912,6 @@ static int key_extract_l3l4(struct sk_buff *skb, struct sw_flow_key *key)
*/
static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
{
- struct ethhdr *eth;
-
/* Flags are always used as part of stats */
key->tp.flags = 0;
@@ -930,6 +926,13 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
skb_reset_network_header(skb);
key->eth.type = skb->protocol;
} else {
+ struct ethhdr *eth;
+ int err;
+
+ err = check_header(skb, ETH_HLEN);
+ if (unlikely(err))
+ return err;
+
eth = eth_hdr(skb);
ether_addr_copy(key->eth.src, eth->h_source);
ether_addr_copy(key->eth.dst, eth->h_dest);
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index c59007525226..102055a8c1d6 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1367,13 +1367,25 @@ static int packet_rcv_has_room(struct packet_sock *po, struct sk_buff *skb)
return ret;
}
-static void packet_rcv_try_clear_pressure(struct packet_sock *po)
+static void __packet_rcv_try_clear_pressure(struct packet_sock *po)
{
if (packet_sock_flag(po, PACKET_SOCK_PRESSURE) &&
__packet_rcv_has_room(po, NULL) == ROOM_NORMAL)
packet_sock_flag_set(po, PACKET_SOCK_PRESSURE, false);
}
+static void packet_rcv_try_clear_pressure(struct packet_sock *po)
+{
+ struct sock *sk = &po->sk;
+
+ if (!packet_sock_flag(po, PACKET_SOCK_PRESSURE))
+ return;
+
+ spin_lock_bh(&sk->sk_receive_queue.lock);
+ __packet_rcv_try_clear_pressure(po);
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
+}
+
static void packet_sock_destruct(struct sock *sk)
{
skb_queue_purge(&sk->sk_error_queue);
@@ -1976,11 +1988,12 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock)
{
int depth;
+ /* On TX skb->data is the L2 header; anchor it for all socket types. */
+ skb_reset_mac_header(skb);
+
if ((!skb->protocol || skb->protocol == htons(ETH_P_ALL)) &&
- sock->type == SOCK_RAW) {
- skb_reset_mac_header(skb);
+ sock->type == SOCK_RAW)
skb->protocol = dev_parse_header_protocol(skb);
- }
/* Move network header to the right position for VLAN tagged packets */
if (likely(skb->dev->type == ARPHRD_ETHER) &&
@@ -2005,8 +2018,9 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
struct net_device *dev;
struct sockcm_cookie sockc;
__be16 proto = 0;
- int err;
+ int hard_header_len;
int extra_len = 0;
+ int err;
/*
* Get and verify the address.
@@ -2049,14 +2063,18 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
extra_len = 4; /* We're doing our own CRC */
}
+ /* Keep the allocation-time header length across retry. */
+ if (!skb)
+ hard_header_len = READ_ONCE(dev->hard_header_len);
+
err = -EMSGSIZE;
- if (len > dev->mtu + dev->hard_header_len + VLAN_HLEN + extra_len)
+ if (len > dev->mtu + hard_header_len + VLAN_HLEN + extra_len)
goto out_unlock;
if (!skb) {
- size_t reserved = LL_RESERVED_SPACE(dev);
+ size_t reserved = LL_RESERVED_SPACE_EX(dev, hard_header_len);
int tlen = dev->needed_tailroom;
- unsigned int hhlen = dev->header_ops ? dev->hard_header_len : 0;
+ unsigned int hhlen = dev->header_ops ? hard_header_len : 0;
rcu_read_unlock();
skb = sock_wmalloc(sk, len + reserved + tlen, 0, GFP_KERNEL);
@@ -2086,7 +2104,7 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
err = -EINVAL;
goto out_unlock;
}
- if (len > (dev->mtu + dev->hard_header_len + extra_len) &&
+ if (len > (dev->mtu + hard_header_len + extra_len) &&
!packet_extra_vlan_len_allowed(dev, skb)) {
err = -EMSGSIZE;
goto out_unlock;
@@ -2621,6 +2639,7 @@ static int packet_snd_vnet_parse(struct msghdr *msg, size_t *len,
static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
void *frame, struct net_device *dev, void *data, int tp_len,
__be16 proto, unsigned char *addr, int hlen, int copylen,
+ int hard_header_len,
const struct sockcm_cookie *sockc)
{
union tpacket_uhdr ph;
@@ -2652,8 +2671,8 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
} else if (copylen) {
int hdrlen = min_t(int, copylen, tp_len);
- skb_push(skb, dev->hard_header_len);
- skb_put(skb, copylen - dev->hard_header_len);
+ skb_push(skb, hard_header_len);
+ skb_put(skb, copylen - hard_header_len);
err = skb_store_bits(skb, 0, data, hdrlen);
if (unlikely(err))
return err;
@@ -2784,7 +2803,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
void *data;
int len_sum = 0;
int status = TP_STATUS_AVAILABLE;
- int hlen, tlen, copylen = 0;
+ int hard_header_len, hlen, tlen, copylen = 0;
long timeo;
mutex_lock(&po->pg_vec_lock);
@@ -2831,8 +2850,9 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
goto out_put;
}
+ hard_header_len = READ_ONCE(dev->hard_header_len);
if (po->sk.sk_socket->type == SOCK_RAW)
- reserve = dev->hard_header_len;
+ reserve = hard_header_len;
size_max = po->tx_ring.frame_size
- (po->tp_hdrlen - sizeof(struct sockaddr_ll));
@@ -2869,7 +2889,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
goto tpacket_error;
status = TP_STATUS_SEND_REQUEST;
- hlen = LL_RESERVED_SPACE(dev);
+ hlen = LL_RESERVED_SPACE_EX(dev, hard_header_len);
tlen = dev->needed_tailroom;
if (vnet_hdr_sz) {
data += vnet_hdr_sz;
@@ -2887,10 +2907,10 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
vnet_hdr.hdr_len);
has_vnet_hdr = true;
}
- copylen = max_t(int, copylen, dev->hard_header_len);
+ copylen = max_t(int, copylen, hard_header_len);
skb = sock_alloc_send_skb(&po->sk,
hlen + tlen + sizeof(struct sockaddr_ll) +
- (copylen - dev->hard_header_len),
+ (copylen - hard_header_len),
!need_wait, &err);
if (unlikely(skb == NULL)) {
@@ -2900,7 +2920,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
goto out_status;
}
tp_len = tpacket_fill_skb(po, skb, ph, dev, data, tp_len, proto,
- addr, hlen, copylen, &sockc);
+ addr, hlen, copylen, hard_header_len,
+ &sockc);
if (likely(tp_len >= 0) &&
tp_len > dev->mtu + reserve &&
!vnet_hdr_sz &&
@@ -3008,7 +3029,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
int offset = 0;
struct packet_sock *po = pkt_sk(sk);
int vnet_hdr_sz = READ_ONCE(po->vnet_hdr_sz);
- int hlen, tlen, linear;
+ int hard_header_len, hlen, tlen, linear;
int extra_len = 0;
/*
@@ -3049,8 +3070,9 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
goto out_unlock;
}
+ hard_header_len = READ_ONCE(dev->hard_header_len);
if (sock->type == SOCK_RAW)
- reserve = dev->hard_header_len;
+ reserve = hard_header_len;
if (vnet_hdr_sz) {
err = packet_snd_vnet_parse(msg, &len, &vnet_hdr, vnet_hdr_sz);
if (err)
@@ -3071,10 +3093,10 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
goto out_unlock;
err = -ENOBUFS;
- hlen = LL_RESERVED_SPACE(dev);
+ hlen = LL_RESERVED_SPACE_EX(dev, hard_header_len);
tlen = dev->needed_tailroom;
linear = __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len);
- linear = max(linear, min_t(int, len, dev->hard_header_len));
+ linear = max(linear, min_t(int, len, hard_header_len));
skb = packet_alloc_skb(sk, hlen + tlen, hlen, len, linear,
msg->msg_flags & MSG_DONTWAIT, &err);
if (skb == NULL)
@@ -3090,7 +3112,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
} else if (reserve) {
skb_reserve(skb, -reserve);
if (len < reserve + sizeof(struct ipv6hdr) &&
- dev->min_header_len != dev->hard_header_len)
+ dev->min_header_len != hard_header_len)
skb_reset_network_header(skb);
}
@@ -4347,7 +4369,7 @@ static __poll_t packet_poll(struct file *file, struct socket *sock,
TP_STATUS_KERNEL))
mask |= EPOLLIN | EPOLLRDNORM;
}
- packet_rcv_try_clear_pressure(po);
+ __packet_rcv_try_clear_pressure(po);
spin_unlock_bh(&sk->sk_receive_queue.lock);
spin_lock_bh(&sk->sk_write_queue.lock);
if (po->tx_ring.pg_vec) {
@@ -4587,14 +4609,14 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
rb->frame_max = (req->tp_frame_nr - 1);
rb->head = 0;
rb->frame_size = req->tp_frame_size;
+ po->prot_hook.func = (po->rx_ring.pg_vec) ?
+ tpacket_rcv : packet_rcv;
spin_unlock_bh(&rb_queue->lock);
swap(rb->pg_vec_order, order);
swap(rb->pg_vec_len, req->tp_block_nr);
rb->pg_vec_pages = req->tp_block_size/PAGE_SIZE;
- po->prot_hook.func = (po->rx_ring.pg_vec) ?
- tpacket_rcv : packet_rcv;
skb_queue_purge(rb_queue);
if (atomic_long_read(&po->mapped))
pr_err("packet_mmap: vma is busy: %ld\n",
diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c
index a7bf52369cae..ba6d257abdc0 100644
--- a/net/sched/act_ct.c
+++ b/net/sched/act_ct.c
@@ -838,8 +838,15 @@ static int tcf_ct_ipv6_is_fragment(struct sk_buff *skb, bool *frag)
return 0;
}
+/* On error, tells the caller whether it still owns @skb and must free it
+ * itself. @skb is ours only when the header checks below reject the packet
+ * before it is handed to the defragmentation engine; once nf_ct_handle_
+ * fragments() has been called the skb is either queued (-EINPROGRESS) or has
+ * already been freed by it.
+ */
static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb,
- u8 family, u16 zone, bool *defrag)
+ u8 family, u16 zone, bool *defrag,
+ bool *skb_is_ours)
{
enum ip_conntrack_info ctinfo;
struct tc_skb_cb cb;
@@ -857,8 +864,12 @@ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb,
err = tcf_ct_ipv4_is_fragment(skb, &frag);
else
err = tcf_ct_ipv6_is_fragment(skb, &frag);
- if (err || !frag)
+ if (err) {
+ *skb_is_ours = true;
return err;
+ }
+ if (!frag)
+ return 0;
cb = *tc_skb_cb(skb);
err = nf_ct_handle_fragments(net, skb, zone, family, &proto, &cb.mru);
@@ -975,6 +986,7 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a,
int nh_ofs, err, retval;
struct tcf_ct_params *p;
bool add_helper = false;
+ bool skb_is_ours = false;
bool skip_add = false;
bool defrag = false;
struct nf_conn *ct;
@@ -1010,9 +1022,18 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a,
*/
nh_ofs = skb_network_offset(skb);
skb_pull_rcsum(skb, nh_ofs);
- err = tcf_ct_handle_fragments(net, skb, family, p->zone, &defrag);
- if (err)
+ err = tcf_ct_handle_fragments(net, skb, family, p->zone, &defrag,
+ &skb_is_ours);
+ if (err) {
+ /* The skb is still ours only when the header checks rejected
+ * it; returning TC_ACT_CONSUMED for such a packet would leak
+ * it, since no caller frees an skb it was told it no longer
+ * owns.
+ */
+ if (skb_is_ours)
+ goto drop;
goto out_frag;
+ }
err = nf_ct_skb_network_trim(skb, family);
if (err)
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index e949280eb800..565860cccba6 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -89,6 +89,11 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
p_parm = nla_data(tb[TCA_GACT_PROB]);
if (p_parm->ptype >= MAX_RAND)
return -EINVAL;
+ if (!tcf_action_valid(p_parm->paction)) {
+ NL_SET_ERR_MSG(extack,
+ "invalid fallback control action");
+ return -EINVAL;
+ }
if (TC_ACT_EXT_CMP(p_parm->paction, TC_ACT_GOTO_CHAIN)) {
NL_SET_ERR_MSG(extack,
"goto chain not allowed on fallback");
diff --git a/net/sched/act_police.c b/net/sched/act_police.c
index 8555125ed34d..81d5f61be17e 100644
--- a/net/sched/act_police.c
+++ b/net/sched/act_police.c
@@ -128,6 +128,12 @@ static int tcf_police_init(struct net *net, struct nlattr *nla,
if (tb[TCA_POLICE_RESULT]) {
tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]);
+ if (!tcf_action_valid(tcfp_result)) {
+ NL_SET_ERR_MSG(extack,
+ "invalid fallback control action");
+ err = -EINVAL;
+ goto failure;
+ }
if (TC_ACT_EXT_CMP(tcfp_result, TC_ACT_GOTO_CHAIN)) {
NL_SET_ERR_MSG(extack,
"goto chain not allowed on fallback");
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index c5aeff709ca6..51a09d555310 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -443,7 +443,22 @@ static void tcf_chain_put(struct tcf_chain *chain);
static void tcf_proto_destroy(struct tcf_proto *tp, bool rtnl_held,
bool sig_destroy, struct netlink_ext_ack *extack)
{
- tp->ops->destroy(tp, rtnl_held, extack);
+ /* A locked classifier's destroy callback (e.g. u32_destroy) uses
+ * rtnl_dereference() and mutates shared structures (e.g. the
+ * tc_u_common hash list) that are only safe under rtnl_lock. When an
+ * unlocked classifier's request (e.g. flower on ingress) loses the
+ * tcf_chain_tp_insert_unique() race and ends up dropping the last
+ * reference on a locked classifier's proto, destroy() would run
+ * without rtnl held. Take it here in that case.
+ */
+ bool not_lockless = !rtnl_held &&
+ !(tp->ops->flags & TCF_PROTO_OPS_DOIT_UNLOCKED);
+
+ if (not_lockless)
+ rtnl_lock();
+ tp->ops->destroy(tp, rtnl_held || not_lockless, extack);
+ if (not_lockless)
+ rtnl_unlock();
tcf_proto_count_usesw(tp, false);
if (sig_destroy)
tcf_proto_signal_destroyed(tp->chain, tp);
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index b9c58c040c30..38469cdcb22d 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -52,6 +52,7 @@ struct route4_filter {
struct tcf_result res;
struct tcf_exts exts;
u32 handle;
+ bool dying;
struct route4_bucket *bkt;
struct tcf_proto *tp;
struct rcu_work rwork;
@@ -66,9 +67,11 @@ static inline int route4_fastmap_hash(u32 id, int iif)
static DEFINE_SPINLOCK(fastmap_lock);
static void
-route4_reset_fastmap(struct route4_head *head)
+route4_reset_fastmap(struct route4_head *head, struct route4_filter *f)
{
spin_lock_bh(&fastmap_lock);
+ if (f)
+ f->dying = true;
memset(head->fastmap, 0, sizeof(head->fastmap));
spin_unlock_bh(&fastmap_lock);
}
@@ -81,9 +84,11 @@ route4_set_fastmap(struct route4_head *head, u32 id, int iif,
/* fastmap updates must look atomic to aling id, iff, filter */
spin_lock_bh(&fastmap_lock);
- head->fastmap[h].id = id;
- head->fastmap[h].iif = iif;
- head->fastmap[h].filter = f;
+ if (f == ROUTE4_FAILURE || !f->dying) {
+ head->fastmap[h].id = id;
+ head->fastmap[h].iif = iif;
+ head->fastmap[h].filter = f;
+ }
spin_unlock_bh(&fastmap_lock);
}
@@ -297,6 +302,13 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held,
next = rtnl_dereference(f->next);
RCU_INIT_POINTER(b->ht[h2], next);
tcf_unbind_filter(tp, &f->res);
+ /* Mark the filter dying under fastmap_lock so
+ * any in-flight reader that still holds it
+ * will skip the republish in route4_set_fastmap().
+ */
+ spin_lock_bh(&fastmap_lock);
+ f->dying = true;
+ spin_unlock_bh(&fastmap_lock);
if (tcf_exts_get_net(&f->exts))
route4_queue_work(f);
else
@@ -307,6 +319,11 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held,
kfree_rcu(b, rcu);
}
}
+
+ /* All filters are unlinked and marked dying, so no in-flight
+ * reader can republish a stale entry after this reset.
+ */
+ route4_reset_fastmap(head, NULL);
kfree_rcu(head, rcu);
}
@@ -334,11 +351,11 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,
/* unlink it */
RCU_INIT_POINTER(*fp, rtnl_dereference(f->next));
- /* Remove any fastmap lookups that might ref filter
- * notice we unlink'd the filter so we can't get it
- * back in the fastmap.
+ /* Clear any fastmap entries that may ref this filter and
+ * mark it dying so in-flight readers can't republish it
+ * after the reset.
*/
- route4_reset_fastmap(head);
+ route4_reset_fastmap(head, f);
/* Delete it */
tcf_unbind_filter(tp, &f->res);
@@ -558,7 +575,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
}
}
- route4_reset_fastmap(head);
+ route4_reset_fastmap(head, fold);
*arg = f;
if (fold) {
tcf_unbind_filter(tp, &fold->res);
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 709d851d0b1e..9a23fef527a2 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1095,6 +1095,9 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
unsigned int i, num_q, ingress;
struct netdev_queue *dev_queue;
+ if (new)
+ new->depth = 0;
+
ingress = 0;
num_q = dev->num_tx_queues;
if ((q && q->flags & TCQ_F_INGRESS) ||
@@ -1192,9 +1195,15 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
NL_SET_ERR_MSG(extack, "STAB not supported on a non root");
return -EINVAL;
}
+ if (new && parent->depth >= 7) {
+ NL_SET_ERR_MSG(extack, "Qdisc hierarchy is too deep");
+ return -E2BIG;
+ }
err = cops->graft(parent, cl, new, &old, extack);
if (err)
return err;
+ if (new)
+ new->depth = parent->depth + 1;
notify_and_destroy(net, skb, n, classid, old, new, extack);
}
return 0;
diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index 2f33739036bb..43908ec23169 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1265,7 +1265,6 @@ static struct sk_buff *cake_ack_filter(struct cake_sched_data *q,
seglen = ntohs(ipv6h_check->payload_len);
} else {
- WARN_ON(1); /* shouldn't happen */
continue;
}
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 44f06c5f1f25..f409b9f6b228 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -543,6 +543,9 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
asoc->addip_last_asconf->transport == peer)
asoc->addip_last_asconf->transport = NULL;
+ if (asoc->new_transport == peer)
+ asoc->new_transport = NULL;
+
/* If we have something on the transmitted list, we have to
* save it off. The best place is the active path.
*/
@@ -573,6 +576,10 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
if (ch->transport == peer)
ch->transport = NULL;
+ list_for_each_entry(ch, &asoc->outqueue.control_chunk_list, list)
+ if (ch->transport == peer)
+ ch->transport = NULL;
+
asoc->peer.transport_count--;
sctp_ulpevent_notify_peer_addr_change(peer, SCTP_ADDR_REMOVED, 0);
@@ -1727,6 +1734,8 @@ void sctp_asconf_queue_teardown(struct sctp_association *asoc)
sctp_assoc_free_asconf_queue(asoc);
/* Free any cached ASCONF chunk. */
- if (asoc->addip_last_asconf)
+ if (asoc->addip_last_asconf) {
sctp_chunk_free(asoc->addip_last_asconf);
+ asoc->addip_last_asconf = NULL;
+ }
}
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 0dc6b8ab9963..242282a26425 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -650,6 +650,7 @@ static int __sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
if (chunk->tsn_gap_acked) {
list_move_tail(&chunk->transmitted_list,
&transport->transmitted);
+ chunk->transport = transport;
continue;
}
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 27a7739a7d42..b6eb79ef6a55 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3354,12 +3354,11 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
goto done;
}
done:
- asoc->peer.addip_serial++;
-
/* If we are sending a new ASCONF_ACK hold a reference to it in assoc
* after freeing the reference to old asconf ack if any.
*/
if (asconf_ack) {
+ asoc->peer.addip_serial++;
sctp_chunk_hold(asconf_ack);
list_add_tail(&asconf_ack->transmitted_list,
&asoc->asconf_ack_list);
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 818878f484a7..b9c7d06544c1 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -6110,8 +6110,12 @@ enum sctp_disposition sctp_sf_t4_timer_expire(
struct sctp_cmd_seq *commands)
{
struct sctp_chunk *chunk = asoc->addip_last_asconf;
- struct sctp_transport *transport = chunk->transport;
+ struct sctp_transport *transport;
+
+ if (!chunk)
+ return SCTP_DISPOSITION_CONSUME;
+ transport = chunk->transport;
SCTP_INC_STATS(net, SCTP_MIB_T4_RTO_EXPIREDS);
/* ADDIP 4.1 B1) Increment the error counters and perform path failure
diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c
index 8d740f588a77..8e95839161aa 100644
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -1933,11 +1933,12 @@ static void smc_listen_out(struct smc_sock *new_smc)
atomic_dec(&lsmc->queued_smc_hs);
release_sock(newsmcsk); /* lock in smc_listen_work() */
+ lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING);
if (lsmc->sk.sk_state == SMC_LISTEN) {
- lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING);
smc_accept_enqueue(&lsmc->sk, newsmcsk);
release_sock(&lsmc->sk);
} else { /* no longer listening */
+ release_sock(&lsmc->sk);
smc_close_non_accepted(newsmcsk);
}
diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c
index 018ce8133b02..149d63cff667 100644
--- a/net/smc/smc_llc.c
+++ b/net/smc/smc_llc.c
@@ -1918,7 +1918,8 @@ static void smc_llc_event_handler(struct smc_llc_qentry *qentry)
return;
case SMC_LLC_CONFIRM_LINK:
case SMC_LLC_ADD_LINK_CONT:
- if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
+ if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE &&
+ !lgr->llc_flow_lcl.qentry) {
/* a flow is waiting for this message */
smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry);
wake_up(&lgr->llc_msg_waiter);
diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c
index e7a6c2602e78..c5d96b116b55 100644
--- a/net/smc/smc_rx.c
+++ b/net/smc/smc_rx.c
@@ -150,7 +150,12 @@ static const struct pipe_buf_operations smc_pipe_ops = {
static void smc_rx_spd_release(struct splice_pipe_desc *spd,
unsigned int i)
{
+ struct smc_spd_priv *priv = (struct smc_spd_priv *)spd->partial[i].private;
+ struct sock *sk = &priv->smc->sk;
+
+ kfree(priv);
put_page(spd->pages[i]);
+ sock_put(sk);
}
static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len,
@@ -209,6 +214,10 @@ static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len,
offset = 0;
}
}
+ for (i = 0; i < nr_pages; i++) {
+ get_page(pages[i]);
+ sock_hold(&smc->sk);
+ }
spd.nr_pages_max = nr_pages;
spd.nr_pages = nr_pages;
spd.pages = pages;
@@ -217,16 +226,8 @@ static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len,
spd.spd_release = smc_rx_spd_release;
bytes = splice_to_pipe(pipe, &spd);
- if (bytes > 0) {
- sock_hold(&smc->sk);
- if (!lgr->is_smcd && smc->conn.rmb_desc->is_vm) {
- for (i = 0; i < PAGE_ALIGN(bytes + offset) / PAGE_SIZE; i++)
- get_page(pages[i]);
- } else {
- get_page(smc->conn.rmb_desc->pages);
- }
+ if (bytes > 0)
atomic_add(bytes, &smc->conn.splice_pending);
- }
kfree(priv);
kfree(partial);
kfree(pages);
diff --git a/net/tipc/node.c b/net/tipc/node.c
index f3332d564c63..34c2c4e82517 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -1061,18 +1061,23 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
{
- struct tipc_link_entry *le = &n->links[bearer_id];
struct tipc_media_addr *maddr = NULL;
- struct tipc_link *l = le->link;
int old_bearer_id = bearer_id;
+ struct tipc_link_entry *le;
struct sk_buff_head xmitq;
-
- if (!l)
- return;
+ struct tipc_link *l;
__skb_queue_head_init(&xmitq);
+ /* Synchronize the link lookup with bearer teardown. */
tipc_node_write_lock(n);
+ le = &n->links[bearer_id];
+ l = le->link;
+ if (!l) {
+ tipc_node_write_unlock_fast(n);
+ return;
+ }
+
if (!tipc_link_is_establishing(l)) {
__tipc_node_link_down(n, &bearer_id, &xmitq, &maddr);
} else {
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 2b875369d523..ceca15b43f35 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -458,7 +458,7 @@ int tls_tx_records(struct sock *sk, int flags)
}
tx_err:
- if (rc < 0 && rc != -EAGAIN)
+ if (rc < 0 && rc != -EAGAIN && rc != -EINTR && rc != -ERESTARTSYS)
tls_err_abort(sk, rc);
return rc;
@@ -1116,6 +1116,14 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg,
if (!sk_stream_memory_free(sk))
goto wait_for_sndbuf;
+ /* open record may be full if we couldn't push it in the last sendmsg call */
+ if (sk_msg_full(msg_pl)) {
+ full_record = true;
+ sk_msg_trim(sk, msg_en,
+ msg_pl->sg.size + prot->overhead_size);
+ goto copied;
+ }
+
alloc_encrypted:
ret = tls_alloc_encrypted_msg(sk, required_size);
if (ret) {
@@ -1216,6 +1224,12 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg,
msg_pl, try_to_copy);
if (ret < 0)
goto trim_sgl;
+
+ if (sk_msg_full(msg_pl)) {
+ full_record = true;
+ sk_msg_trim(sk, msg_en,
+ msg_pl->sg.size + prot->overhead_size);
+ }
}
/* Open records defined only if successfully copied, otherwise
diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c
index b6569b0ca2bb..e7dff5c3d4ac 100644
--- a/net/vmw_vsock/virtio_transport.c
+++ b/net/vmw_vsock/virtio_transport.c
@@ -343,12 +343,13 @@ static void virtio_transport_tx_work(struct work_struct *work)
struct virtqueue *vq;
bool added = false;
- vq = vsock->vqs[VSOCK_VQ_TX];
mutex_lock(&vsock->tx_lock);
if (!vsock->tx_run)
goto out;
+ vq = vsock->vqs[VSOCK_VQ_TX];
+
do {
struct sk_buff *skb;
unsigned int len;
@@ -448,13 +449,13 @@ static void virtio_transport_event_work(struct work_struct *work)
container_of(work, struct virtio_vsock, event_work);
struct virtqueue *vq;
- vq = vsock->vqs[VSOCK_VQ_EVENT];
-
mutex_lock(&vsock->event_lock);
if (!vsock->event_run)
goto out;
+ vq = vsock->vqs[VSOCK_VQ_EVENT];
+
do {
struct virtio_vsock_event *event;
unsigned int len;
@@ -614,12 +615,12 @@ static void virtio_transport_rx_work(struct work_struct *work)
container_of(work, struct virtio_vsock, rx_work);
struct virtqueue *vq;
- vq = vsock->vqs[VSOCK_VQ_RX];
-
mutex_lock(&vsock->rx_lock);
if (!vsock->rx_run)
- goto out;
+ goto out_nofill;
+
+ vq = vsock->vqs[VSOCK_VQ_RX];
do {
virtqueue_disable_cb(vq);
@@ -667,6 +668,7 @@ static void virtio_transport_rx_work(struct work_struct *work)
out:
if (vsock->rx_buf_nr < vsock->rx_buf_max_nr / 2)
virtio_vsock_rx_fill(vsock);
+out_nofill:
mutex_unlock(&vsock->rx_lock);
}
diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
index 9ec7bd948acc..81ffda550107 100644
--- a/net/xdp/xdp_umem.c
+++ b/net/xdp/xdp_umem.c
@@ -209,6 +209,8 @@ static int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr)
if (mr->flags & XDP_UMEM_TX_METADATA_LEN) {
if (mr->tx_metadata_len >= 256 || mr->tx_metadata_len % 8)
return -EINVAL;
+ if (mr->tx_metadata_len < 16)
+ return -EINVAL;
umem->tx_metadata_len = mr->tx_metadata_len;
}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index f7770c24995b..b5c573332fa8 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -302,8 +302,13 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
} else {
set_bit(IMA_DIGSIG, &iint->atomic_flags);
}
- if (xattr_len - sizeof(xattr_value->type) - hash_start >=
- iint->ima_hash->length)
+ /*
+ * Use addition, not subtraction: sizeof() forces unsigned
+ * math and a short xattr_len would wrap around, bypassing
+ * this bounds check.
+ */
+ if (xattr_len >= (int)sizeof(xattr_value->type) + hash_start +
+ (int)iint->ima_hash->length)
/*
* xattr length may be longer. md5 hash in previous
* version occupied 20 bytes in xattr, instead of 16
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 2f3f267e7216..a0948057209e 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -587,6 +587,43 @@ static int ima_file_check(struct file *file, int mask)
MAY_APPEND), FILE_CHECK);
}
+/*
+ * ima_reset_action_flags - invalidate action flags after a content change
+ * @inode: inode of the file whose content is about to be truncated
+ *
+ * Clear IMA_DONE_MASK so the file is re-collected, re-measured,
+ * re-audited, and re-appraised on next access.
+ */
+static void ima_reset_action_flags(struct inode *inode)
+{
+ struct ima_iint_cache *iint;
+
+ if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+ return;
+
+ iint = ima_iint_find(inode);
+ if (!iint)
+ return;
+
+ mutex_lock(&iint->mutex);
+ iint->flags &= ~IMA_DONE_MASK;
+ iint->measured_pcrs = 0;
+ mutex_unlock(&iint->mutex);
+ return;
+}
+
+static int ima_path_truncate(const struct path *path)
+{
+ ima_reset_action_flags(path->dentry->d_inode);
+ return 0;
+}
+
+static int ima_file_truncate(struct file *file)
+{
+ ima_reset_action_flags(file_inode(file));
+ return 0;
+}
+
static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
size_t buf_size)
{
@@ -1195,11 +1232,13 @@ static struct security_hook_list ima_hooks[] __ro_after_init = {
LSM_HOOK_INIT(file_release, ima_file_free),
LSM_HOOK_INIT(mmap_file, ima_file_mmap),
LSM_HOOK_INIT(file_mprotect, ima_file_mprotect),
+ LSM_HOOK_INIT(file_truncate, ima_file_truncate),
LSM_HOOK_INIT(kernel_load_data, ima_load_data),
LSM_HOOK_INIT(kernel_post_load_data, ima_post_load_data),
LSM_HOOK_INIT(kernel_read_file, ima_read_file),
LSM_HOOK_INIT(kernel_post_read_file, ima_post_read_file),
LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod),
+ LSM_HOOK_INIT(path_truncate, ima_path_truncate),
#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
LSM_HOOK_INIT(key_post_create_or_update, ima_post_key_create_or_update),
#endif
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index eeee2886dc21..c573d74641d4 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -388,13 +388,15 @@ static int prepare_inbound_urb(struct snd_usb_endpoint *ep,
case SND_USB_ENDPOINT_TYPE_DATA:
offs = 0;
for (i = 0; i < urb_ctx->packets; i++) {
+ if (offs + ep->curpacksize > urb_ctx->buffer_size)
+ break;
urb->iso_frame_desc[i].offset = offs;
urb->iso_frame_desc[i].length = ep->curpacksize;
offs += ep->curpacksize;
}
urb->transfer_buffer_length = offs;
- urb->number_of_packets = urb_ctx->packets;
+ urb->number_of_packets = i;
break;
case SND_USB_ENDPOINT_TYPE_SYNC:
@@ -1263,10 +1265,10 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep)
u->index = i;
u->ep = ep;
u->packets = urb_packs;
- u->buffer_size = maxsize * u->packets;
if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
u->packets++; /* for transfer delimiter */
+ u->buffer_size = maxsize * u->packets;
u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
if (!u->urb)
goto out_of_memory;
diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c
index 19c979df36bb..e66b595ebde8 100644
--- a/sound/usb/midi2.c
+++ b/sound/usb/midi2.c
@@ -685,6 +685,14 @@ static int parse_midi_2_0_endpoints(struct snd_usb_midi2_interface *umidi)
return 0;
}
+static void free_ump_private_data(struct snd_ump_endpoint *ump)
+{
+ struct snd_usb_midi2_ump *rmidi = ump->private_data;
+
+ if (rmidi)
+ rmidi->ump = NULL;
+}
+
static void free_all_midi2_umps(struct snd_usb_midi2_interface *umidi)
{
struct snd_usb_midi2_ump *rmidi;
@@ -735,6 +743,7 @@ static int create_midi2_ump(struct snd_usb_midi2_interface *umidi,
ump->private_data = rmidi;
ump->ops = &snd_usb_midi_v2_ump_ops;
+ ump->private_free = free_ump_private_data;
rmidi->eps[STR_IN] = ep_in;
rmidi->eps[STR_OUT] = ep_out;
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 9fd6a86cc08e..2cc0f90a23be 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -25,6 +25,8 @@ static vm_fault_t snd_us428ctls_vm_fault(struct vm_fault *vmf)
void *vaddr;
offset = vmf->pgoff << PAGE_SHIFT;
+ if (offset >= US428_SHAREDMEM_PAGES)
+ return VM_FAULT_SIGBUS;
vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->us428ctls_sharedmem + offset;
page = virt_to_page(vaddr);
get_page(page);
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
index 1b1496adb47e..0b8dd9e3f984 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -681,6 +681,8 @@ static vm_fault_t snd_usx2y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
void *vaddr;
offset = vmf->pgoff << PAGE_SHIFT;
+ if (offset >= USX2Y_HWDEP_PCM_PAGES)
+ return VM_FAULT_SIGBUS;
vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
vmf->page = virt_to_page(vaddr);
get_page(vmf->page);
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
index fb08c565d6aa..99522cf68dc8 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
@@ -703,7 +703,7 @@ static void test_sockmap_many_socket(void)
return;
}
- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK);
if (udp < 0) {
close(dgram);
close(tcp);
@@ -772,7 +772,7 @@ static void test_sockmap_many_maps(void)
return;
}
- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK);
if (udp < 0) {
close(dgram);
close(tcp);
@@ -843,7 +843,7 @@ static void test_sockmap_same_sock(void)
return;
}
- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK);
if (udp < 0) {
close(dgram);
close(tcp);
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
index 1cfed83156b0..b04b8188b151 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
@@ -53,8 +53,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused,
int family, int sotype, int mapfd)
{
u32 key = 0;
- u64 value;
int err, s;
+ u64 value;
s = xsocket(family, sotype, 0);
if (s == -1)
@@ -63,11 +63,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused,
errno = 0;
value = s;
err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST);
- if (sotype == SOCK_STREAM) {
- if (!err || errno != EOPNOTSUPP)
- FAIL_ERRNO("map_update: expected EOPNOTSUPP");
- } else if (err)
- FAIL_ERRNO("map_update: expected success");
+ ASSERT_ERR(err, "map_update");
+ ASSERT_EQ(errno, EOPNOTSUPP, "errno");
xclose(s);
}
@@ -77,8 +74,8 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused,
struct sockaddr_storage addr;
socklen_t len = 0;
u32 key = 0;
- u64 value;
int err, s;
+ u64 value;
init_addr_loopback(family, &addr, &len);
@@ -93,8 +90,12 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused,
errno = 0;
value = s;
err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST);
- if (!err || errno != EOPNOTSUPP)
- FAIL_ERRNO("map_update: expected EOPNOTSUPP");
+ if (sotype == SOCK_STREAM) {
+ ASSERT_ERR(err, "map_update");
+ ASSERT_EQ(errno, EOPNOTSUPP, "errno");
+ } else {
+ ASSERT_OK(err, "map_update");
+ }
close:
xclose(s);
}
@@ -1289,7 +1290,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map,
/* insert */
TEST(test_insert_invalid),
TEST(test_insert_opened),
- TEST(test_insert_bound, SOCK_STREAM),
+ TEST(test_insert_bound),
TEST(test_insert),
/* delete */
TEST(test_delete_after_insert),
diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c
index 905d5981ace1..6f080cc7eb96 100644
--- a/tools/testing/selftests/bpf/test_maps.c
+++ b/tools/testing/selftests/bpf/test_maps.c
@@ -752,16 +752,15 @@ static void test_sockmap(unsigned int tasks, void *data)
goto out_sockmap;
}
- /* Test update with unsupported UDP socket */
+ /* Test update with unsupported unbound UDP socket */
udp = socket(AF_INET, SOCK_DGRAM, 0);
- i = 0;
- err = bpf_map_update_elem(fd, &i, &udp, BPF_ANY);
- if (err) {
- printf("Failed socket update SOCK_DGRAM '%i:%i'\n",
- i, udp);
+ CHECK(udp < 0, "socket(AF_INET, SOCK_DGRAM)", "errno:%d\n", errno);
+ err = bpf_map_update_elem(fd, &(int){0}, &udp, BPF_ANY);
+ close(udp);
+ if (!err) {
+ printf("Unexpectedly succeeded unbound UDP update '0:%i'\n", udp);
goto out_sockmap;
}
- close(udp);
/* Test update without programs */
for (i = 0; i < 6; i++) {
diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
index c300eb020262..e2322693d0c3 100644
--- a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
@@ -1,16 +1,16 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: Generic dynamic event - add/remove eprobe events
-# requires: dynamic_events events/syscalls/sys_enter_openat "<attached-group>.<attached-event> [<args>]":README
+# requires: dynamic_events events/syscalls/sys_enter_chdir "<attached-group>.<attached-event> [<args>]":README
echo 0 > events/enable
clear_dynamic_events
SYSTEM="syscalls"
-EVENT="sys_enter_openat"
+EVENT="sys_enter_chdir"
FIELD="filename"
-EPROBE="eprobe_open"
+EPROBE="eprobe_chdir"
OPTIONS="file=+0(\$filename):ustring"
echo "e:$EPROBE $SYSTEM/$EVENT $OPTIONS" >> dynamic_events
@@ -18,20 +18,14 @@ grep -q "$EPROBE" dynamic_events
test -d events/eprobes/$EPROBE
echo 1 > events/eprobes/$EPROBE/enable
-ls
+cd /sys/kernel/tracing
echo 0 > events/eprobes/$EPROBE/enable
-content=`grep '^ *ls-' trace | grep 'file='`
-nocontent=`grep '^ *ls-' trace | grep 'file=' | grep -v -e '"/' -e '"."' -e '(fault)' ` || true
-
+content=`grep -e 'file="/sys/kernel/tracing"\|(fault)' trace`
if [ -z "$content" ]; then
exit_fail
fi
-if [ ! -z "$nocontent" ]; then
- exit_fail
-fi
-
echo "-:$EPROBE" >> dynamic_events
! grep -q "$EPROBE" dynamic_events
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-19 16:52 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-19 16:52 Linux 6.12.104 Greg Kroah-Hartman
2026-08-19 16:52 ` Greg Kroah-Hartman
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.