* Linux 7.1.9
@ 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 7.1.9 kernel.
All users of the 7.1 kernel series must upgrade.
The updated 7.1.y git tree can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git linux-7.1.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/admin-guide/kernel-parameters.txt | 9
Documentation/hwmon/hwmon-kernel-api.rst | 7
Documentation/sound/alsa-configuration.rst | 12
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/arm64/boot/dts/qcom/glymur.dtsi | 32 -
arch/arm64/boot/dts/qcom/monaco.dtsi | 1
arch/arm64/boot/dts/qcom/purwa.dtsi | 2
arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 4
arch/arm64/boot/dts/qcom/sm8650.dtsi | 4
arch/arm64/include/asm/ptdump.h | 2
arch/arm64/mm/mmu.c | 43 --
arch/arm64/mm/ptdump.c | 11
arch/x86/kernel/cpu/common.c | 10
arch/x86/kernel/cpu/mce/core.c | 2
arch/x86/kvm/mmu/mmu.c | 3
arch/x86/kvm/svm/sev.c | 34 +
arch/x86/kvm/svm/svm.h | 1
drivers/accel/amdxdna/amdxdna_gem.c | 47 +-
drivers/android/binder/process.rs | 12
drivers/ata/pata_sl82c105.c | 4
drivers/bluetooth/btrtl.c | 13
drivers/bluetooth/btusb.c | 2
drivers/counter/microchip-tcb-capture.c | 4
drivers/dibs/dibs_loopback.c | 1
drivers/dibs/dibs_main.c | 16
drivers/gpio/gpio-pca953x.c | 19 -
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 35 +-
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6
drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c | 24 +
drivers/gpu/drm/bridge/parade-ps8640.c | 27 +
drivers/gpu/drm/v3d/v3d_drv.h | 6
drivers/gpu/drm/v3d/v3d_sched.c | 9
drivers/gpu/drm/xe/xe_exec_queue.c | 3
drivers/gpu/drm/xe/xe_uc.c | 7
drivers/hwmon/ads7828.c | 11
drivers/hwmon/corsair-psu.c | 8
drivers/hwmon/ltc4282.c | 20 -
drivers/hwmon/nzxt-smart2.c | 6
drivers/hwmon/pmbus/lm25066.c | 5
drivers/hwmon/pmbus/pmbus_core.c | 65 ++-
drivers/input/evdev.c | 27 -
drivers/misc/fastrpc.c | 27 +
drivers/misc/mei/client.c | 25 -
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/bnge/bnge_auxr.c | 9
drivers/net/ethernet/broadcom/bnge/bnge_netdev.c | 2
drivers/net/ethernet/broadcom/bnge/bnge_resc.c | 3
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 64 ++-
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/hisilicon/hix5hd2_gmac.c | 1
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 6
drivers/net/ethernet/intel/igc/igc_main.c | 10
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.h | 2
drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h | 1
drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c | 15
drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h | 4
drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c | 2
drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 4
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 13
drivers/net/ntb_netdev.c | 15
drivers/net/ovpn/main.c | 64 ++-
drivers/net/ovpn/netlink.c | 6
drivers/net/ovpn/peer.c | 172 +++++++---
drivers/net/ovpn/peer.h | 1
drivers/net/ovpn/socket.c | 9
drivers/net/phy/mediatek/mtk.h | 8
drivers/net/tap.c | 24 -
drivers/net/thunderbolt/main.c | 33 +
drivers/net/usb/ax88179_178a.c | 4
drivers/net/usb/ipheth.c | 11
drivers/net/usb/usbnet.c | 3
drivers/net/veth.c | 22 -
drivers/net/vxlan/vxlan_core.c | 2
drivers/nvmem/apple-spmi-nvmem.c | 20 +
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/qcom/pinctrl-ipq8064.c | 8
drivers/pinctrl/qcom/pinctrl-msm.h | 5
drivers/ptp/ptp_ocp.c | 4
drivers/s390/net/ism_drv.c | 19 -
drivers/scsi/scsi_debug.c | 4
drivers/soc/aspeed/Makefile | 1
drivers/soc/aspeed/aspeed-lpc-snoop.c | 38 +-
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 | 154 ++++++--
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/8250/8250_of.c | 38 ++
drivers/tty/serial/amba-pl011.c | 68 ++-
drivers/tty/serial/qcom_geni_serial.c | 43 +-
drivers/tty/serial/sc16is7xx.c | 3
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/core/config.c | 32 +
drivers/usb/core/quirks.c | 7
drivers/usb/gadget/function/f_ncm.c | 2
drivers/usb/host/xhci.h | 12
drivers/usb/misc/usbio.c | 2
drivers/vdpa/mlx5/core/mlx5_vdpa.h | 2
drivers/vdpa/mlx5/core/mr.c | 8
drivers/vdpa/mlx5/core/resources.c | 11
drivers/vdpa/vdpa_sim/vdpa_sim.c | 10
drivers/vdpa/vdpa_user/iova_domain.c | 11
drivers/vhost/iotlb.c | 47 +-
drivers/vhost/scsi.c | 17
drivers/vhost/vdpa.c | 18 -
drivers/vhost/vhost.c | 16
drivers/video/fbdev/core/bitblit.c | 9
drivers/watchdog/at91sam9_wdt.c | 4
drivers/watchdog/bd96801_wdt.c | 5
fs/attr.c | 2
fs/binfmt_misc.c | 3
fs/btrfs/inode.c | 4
fs/btrfs/lzo.c | 16
fs/crypto/policy.c | 2
fs/namespace.c | 4
fs/nfs/nfs4proc.c | 17
fs/overlayfs/super.c | 3
fs/smb/client/sess.c | 2
fs/smb/client/smb1transport.c | 28 +
fs/tracefs/event_inode.c | 28 +
fs/tracefs/internal.h | 4
fs/verity/measure.c | 15
fs/xfs/xfs_buf.c | 2
include/linux/dibs.h | 2
include/linux/hwmon.h | 3
include/linux/mmap_lock.h | 1
include/linux/netdevice.h | 11
include/linux/netfilter/ipset/ip_set.h | 2
include/linux/nvmem-provider.h | 6
include/linux/psi.h | 4
include/linux/sched/ext.h | 10
include/linux/usb/quirks.h | 3
include/net/act_api.h | 19 +
include/net/addrconf.h | 4
include/net/ip_vs.h | 48 +-
include/net/libeth/xsk.h | 2
include/net/route.h | 2
include/net/sch_generic.h | 1
include/net/xdp_sock.h | 45 --
include/net/xdp_sock_drv.h | 77 +++-
include/uapi/linux/ip_vs.h | 6
kernel/bpf/rqspinlock.c | 5
kernel/bpf/verifier.c | 29 -
kernel/cgroup/cgroup.c | 23 +
kernel/events/core.c | 67 ++-
kernel/futex/core.c | 85 +++-
kernel/sched/ext.c | 47 ++
kernel/sched/psi.c | 75 +++-
kernel/trace/ring_buffer.c | 15
kernel/trace/trace_events.c | 4
mm/damon/lru_sort.c | 2
mm/damon/ops-common.c | 13
mm/damon/paddr.c | 2
mm/damon/vaddr.c | 3
mm/filemap.c | 6
mm/huge_memory.c | 47 +-
mm/memory.c | 2
mm/page_table_check.c | 20 -
mm/pagewalk.c | 36 +-
mm/ptdump.c | 7
mm/vmalloc.c | 40 +-
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/datagram.c | 3
net/core/dev.c | 10
net/core/filter.c | 4
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/inet_fragment.c | 10
net/ipv4/route.c | 29 +
net/ipv4/tcp_bpf.c | 2
net/ipv4/tcp_input.c | 2
net/ipv4/tcp_ipv4.c | 43 +-
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 | 45 --
net/netfilter/ipvs/ip_vs_core.c | 74 ++--
net/netfilter/ipvs/ip_vs_ctl.c | 81 +++-
net/netfilter/ipvs/ip_vs_dh.c | 4
net/netfilter/ipvs/ip_vs_est.c | 10
net/netfilter/ipvs/ip_vs_lblc.c | 2
net/netfilter/ipvs/ip_vs_lblcr.c | 8
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 | 6
net/netfilter/nf_conntrack_bpf.c | 72 ++--
net/netfilter/nf_conntrack_proto.c | 6
net/netfilter/nf_conntrack_proto_sctp.c | 12
net/netfilter/nf_conntrack_proto_tcp.c | 132 +++++--
net/netfilter/nf_flow_table_ip.c | 3
net/openvswitch/datapath.c | 29 +
net/openvswitch/flow.c | 11
net/packet/af_packet.c | 72 ++--
net/qrtr/ns.c | 2
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 | 18 -
net/vmw_vsock/virtio_transport.c | 14
net/x25/af_x25.c | 4
net/x25/x25_timer.c | 25 -
net/xdp/xdp_umem.c | 2
net/xdp/xsk.c | 11
net/xdp/xsk_buff_pool.c | 6
samples/damon/mtier.c | 3
security/integrity/ima/ima_appraise.c | 9
security/integrity/ima/ima_main.c | 39 ++
sound/hda/codecs/side-codecs/tas2781_hda_spi.c | 3
sound/usb/endpoint.c | 6
sound/usb/fcp.c | 10
sound/usb/midi2.c | 9
sound/usb/mixer.c | 60 ++-
sound/usb/mixer.h | 1
sound/usb/quirks.c | 7
sound/usb/usbaudio.h | 13
sound/usb/usx2y/us144mkii_capture.c | 1
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/prog_tests/test_xsk.c | 50 +-
tools/testing/selftests/bpf/test_maps.c | 13
tools/testing/selftests/bpf/verifier/sleepable.c | 17
tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc | 16
tools/testing/selftests/net/tls.c | 57 +++
tools/testing/selftests/sched_ext/numa.bpf.c | 13
279 files changed, 3072 insertions(+), 1340 deletions(-)
Aditya Chillara (1):
perf/core: Fix group leader use-after-free after sibling detach
Aelin Reidel (1):
nvmem: apple-spmi-nvmem: wrap regmap calls to satisfy CFI
Ahmad Byagowi (1):
ptp: ocp: Fix board ID over-read
Ahmed Naseef (1):
net: phy: mediatek: fix TX blink masks using the RX bits
Akhil P Oommen (1):
arm64: dts: qcom: purwa: Fix GPU IOMMU property
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()
Alex Deucher (1):
Revert "drm/amd/display: Fix backlight max_brightness to match exported range"
Alexander Koskovich (1):
arm64: dts: qcom: sm8650: Fix IPA IMEM slice
Alexander Usyskin (1):
mei: pull kvfree out of spinlock
Alexandra Winter (2):
s390/ism: Fix UAF of sba and ieq during ism_dev_exit()
net/dibs: Correct freeing of dmb_clientid_arr
Alexej Sidorenko (1):
Bluetooth: btrtl: fix RTL8761B/BU broken LE extended scan
Ali Ahmet Memis (1):
hwmon: (corsair-psu) serialize debugfs access against hwmon
Alice Ryhl (1):
rust_binder: do not query current thread for all ioctls
Alok Tiwari (2):
bnge: Fix NULL pointer dereference in aux device release
bnge: use int for bnge_fix_rings_count() return value
Anandu Krishnan E (1):
misc: fastrpc: fix channel ctx ref leak when session alloc fails
Andrey Albershteyn (1):
fs,fsverity: remove check for fsverity being enabled in setattr_prepare()
Andy Lutomirski (1):
mm: fix incorrect flush address in direct page table reclaim
Anna Schumaker (2):
NFS: Pin the 'struct nfs_server' during a FREE_STATEID call
NFS: Decrement refcounts if allocating nfs_free_stateid_data fails
Antonio Quartulli (7):
ovpn: skip rehash for peers already removed from by_id
ovpn: rehash peer in by_transp_addr table on CMD_PEER_SET
ovpn: ensure socket is owned by ovpn before deref sk_user_data
ovpn: zero-initialize sockaddr before learning a floated endpoint
ovpn: hash floated peer by transport identity only
ovpn: disable IPv4 redirects on MP interfaces
ovpn: ensure TCP vars are initialized first
Asad Kamal (1):
Revert "drm/amdgpu: fix aperture mapping leak"
Babanpreet Singh (1):
counter: microchip-tcb-capture: Fix DT channel validation
Baul Lee (8):
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
ALSA: FCP: fix OOB write in fcp_meter_ctl_get()
ALSA: us144mkii: re-anchor capture URBs on resubmission
net/x25: fix use-after-free of the socket by its timers
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
Bhargava Marreddy (1):
bnge: Fix resource leak in bnge_init_nic() error path
Bjorn Andersson (2):
arm64: dts: qcom: glymur: fix PCIe SMMU interrupts
arm64: dts: qcom: glymur: fix QUP serial engine IRQs
Bobby Eshleman (1):
net/mlx5e: fix BQL reset on SQ re-activation
Breno Leitao (1):
x86/mce: Set up the polling timer before CMCI discovery
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 (4):
bpf: Fix netns reference imbalance in conntrack kfuncs
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
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
David Lee (1):
btrfs: lzo: reject inline extents without valid headers
Dmitry Antipov (1):
btrfs: fix memory leak in btrfs_do_encoded_write()
Dmitry Baryshkov (1):
arm64: dts: qcom: sdm850-lenovo-yoga-c630: lower PSCI cluster idle
Dmitry Torokhov (2):
Input: evdev - sanitize event type index when fetching event masks
Input: evdev - fix information leak in evdev_pass_values()
Dongli Zhang (1):
net: tap: set skb->dev before parsing virtio net header in tap_get_user_xdp()
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 (2):
misc: fastrpc: Fix initial memory allocation for Audio PD memory pool
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 (3):
netfilter: nf_flow_table: drop existing skb dst before skb_dst_set_noref()
net: remove WARN_ON_ONCE() from sk_mc_loop()
net/atm: fix slab-out-of-bounds read in vcc_setsockopt()
Fan Wu (3):
serial: amba-pl011: fix indefinite RS485 post-send delay
serial: amba-pl011: cancel RS485 hrtimers after freeing IRQ
serial: amba-pl011: synchronize DMA teardown
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"
Frank Sorenson (1):
smb: client: fix SMB1 TRANS2 multi-response truncation in SendReceive()
Greg Kroah-Hartman (1):
Linux 7.1.9
Guenter Roeck (7):
hwmon: (pmbus/core) Avoid race condition during probe
hwmon: (pmbus) Fix type confusion in notification logic
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
hwmon: Support guard() and scoped_guard for subsystem locks
Hans Ulli Kroll (2):
pinctrl: qcom: ipq806x: mark gpio as a GPIO pin function
pinctrl: qcom: ipq806x: mark pci reset as a GPIO pin function
Henry Martin (1):
net/ncsi: fix heap OOB read in NCSI_CMD_SEND_CMD payload length
Hidayath Khan (1):
dibs: initialise dibs->lock in dibs_dev_alloc()
Hongyan Xu (2):
ata: pata_sl82c105: fix bridge revision use-after-free
watchdog: at91sam9_wdt: prevent timer rearm during teardown
Hugh Dickins (1):
mm/filemap: __filemap_add_folio() restore index before retrying
Hui Su (2):
ring-buffer: Fix crash passing ERR_PTR to kthread_stop()
tracing: Fix NULL pointer dereference in module event cache removal
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
Ishaan Dandekar (1):
usb: quirks: Add ShanWan gamepad to quirk list
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
Jia Jia (1):
vhost-scsi: reject feature changes after endpoint
Jiangshan Yi (1):
usb: misc: usbio: check ibuf_len against rxbuf_len in bulk msg
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
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
Jérémy Jean (1):
tls: rx: restore msg_iter before TLS 1.3 optimistic retry
Karthikeyan KS (1):
soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read
Keegan Freyhof (1):
bnxt_en: Fix PTP PPS setting bug
Keno Fischer (1):
futex: Prevent robust futex exit race some more
Koichiro Den (1):
NTB: ntb_netdev: Preserve RX queue depth on allocation failure
Krzysztof Kozlowski (1):
arm64: dts: qcom: monaco: Add default GIC address cells
Kuba Piecuch (1):
selftests/sched_ext: Handle sleeping task affinity changes in numa test
Kumar Kartikeya Dwivedi (1):
rqspinlock: Reset tail when preserving queue on deadlock
Kyle Zeng (2):
ipvs: clear IPv4 options after rebasing tunnel ICMP errors
ipv6: prevent in6_dev_get() from resurrecting inet6_dev
Lachlan Hodges (1):
usb: xhci: use BIT_ULL for CRCR bits to fix incorrect 64bit mask
Laurent Vivier (1):
usbnet: cap max_mtu for drivers without bind callback
Lincoln Wallace (1):
ima: fix out-of-bounds read in xattr_verify()
Linfeng Sun (1):
vhost-scsi: Validate T10 PI scatterlist counts
Linfeng Sun (1):
vhost_iotlb: bound map allocation in add_range
Lizhi Hou (2):
accel/amxdna: Fix page-insertion errors in amdxdna_insert_pages()
accel/amdxdna: Fix locally exploitable BUG_ON in amdxdna_insert_pages()
Lorenzo Stoakes (ARM) (4):
mm/vmalloc: acquire init_mm lock on huge vmap to avoid ptdump UAF
mm/huge_memory: fix huge_zero_pfn race
arm64: remove redundant concurrent ptdump UAF mitigation
mm/ptdump: always stabilise against page table freeing using init_mm
Luca Fresi (1):
serial: sc16is7xx: enable THRI before filling TX FIFO
Luxiao Xu (1):
bpf: Check sk_state before sk_protocol in bpf_tcp_*_syncookie
Maciej Fijalkowski (2):
selftests/xsk: fix too-many-frags multi-buffer Tx test
selftests/xsk: account reclaimed invalid Tx descriptors
Mahanta Jambigi (1):
net/smc: fix qentry overwrite for CONFIRM_LINK and ADD_LINK_CONT in smc_llc_event_handler()
Marcin Bernatowicz (1):
drm/xe/uc: Apply RCS/CCS yield policy to SR-IOV VFs
Mariano Baragiola (1):
staging: rtl8723bs: validate monitor transmit frame lengths
Mark Tomlinson (1):
gpio: pca953x: fix pca953x_irq_bus_sync_unlock regmap lock
Martin Kaiser (1):
selftests/ftrace: refactor eprobes test to fix argument checks
Mathieu Dubois-Briand (1):
nvmem: layouts: Add fixed-layout driver
Matt Fleming (1):
mm/huge_memory: initialise workingset state before folio split
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
Maíra Canal (1):
drm/v3d: Serialize the scheduler timeout handlers
Michael Chan (1):
bnxt_en: Disable EOP for TPA on all chips to prevent data corruption
Michael Guralnik (1):
net/mlx5: fw_tracer, return NULL on create error
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
Michał Winiarski (1):
drm/xe: Fix memory leak in exec_queue_set_hang_replay_state()
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()
Mykyta Yatsenko (1):
selftests/bpf: Add tests for sleepable tracepoint programs
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
Nikhil Solanke (1):
usb: core: Add quirk for 255-bytes initial config read
Nils Helmig (1):
Bluetooth: btusb: Add TP-Link UB600 for Realtek 8761BUV
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
Paolo Bonzini (1):
KVM: SVM: Serialize accesses to the owner and mirror list with separate lock
Pavel Begunkov (1):
net: devmem: prevent net-iov / page mixing
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
Philipp David (1):
igc: fix netdev not re-attached after resume if interface is down
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
Qingfang Deng (1):
ovpn: fix incorrect use of rcu_access_pointer()
Qingshuang Fu (2):
hwmon: (nzxt-smart2) Check return value of init_device() in probe
hwmon: (ads7828) Fix external VREF regulator handling
Qu Wenruo (2):
btrfs: lzo: add error message for invalid headers
btrfs: initialize inode mapping flags for cached inodes
Rafael J. Wysocki (2):
Revert "thermal/drivers/hwmon: Cleanup coding style a bit"
Revert "thermal: hwmon: Register a hwmon device for each thermal zone"
Ralf Lici (1):
ovpn: add missing rtnl_link_ops->get_size callback
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 (2):
fbdev: bitblit: bound-check glyph index in bit_cursor()
x86/CPU: Add a tlbi= cmdline switch
Rong Zhang (2):
ALSA: usb-audio: Add QUIRK_FLAG_MIXER_GET_CUR_BROKEN
ALSA: usb-audio: Add QUIRK_FLAG_MIXER_GET_CUR_BROKEN for Logitech PRO X 2 LIGHTSPEED
Rosen Penev (1):
ARM: dts: BCM5301X: fix PCIe controller 2 second interrupt
Ryan Wilbur (1):
serial: 8250_of: clear stuck empty-FIFO RX-timeout on LPC32xx
SJ Park (3):
mm/damon/lru_sort: error out for >10000 active_mem_bp
samples/damon/mtier: error out for zero quota goal target values
mm/damon: adjust isolated pages stat for DAMOS_MIGRATE_{HOT,COLD}
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 (6):
xsk: require at least 16 bytes of TX metadata
xsk: pass TX metadata pointer by reference
xsk: clear metadata pointer when no timestamp is requested
xsk: validate launch-time metadata size
xsk: move xsk_tx_metadata_request() to xdp_sock_drv.h
xsk: validate metadata when processing requests
Stefan Agner (1):
net: stmmac: resume PHY before hardware setup when opening the interface
Steven Rostedt (1):
eventfs: Use children field for rcu head and add memory barriers
Sun Jian (2):
net: fix skb length accounting after generic XDP frag adjustment
veth: fix skb length accounting after XDP frag adjustment
Takashi Iwai (2):
ALSA: usb-audio: Fix sticky mixer regressions on M-Audio Fast Track Ultra
ALSA: usb: Fix UAF at delayed release of MIDI2 EPs
Tejun Heo (6):
sched_ext: Reject setting disallow from init_task outside the enable path
sched_ext: Skip sub-disable teardown for never-linked sub-schedulers
sched_ext: Don't enable non-ext tasks in the sub-sched task loops
sched/psi: Shut down rtpoll_timer in psi_cgroup_free()
sched/psi: Create the psimon kthread outside of cgroup_mutex
sched_ext: Take cgroup_lock() first in scx_cgroup_lock()
Tengda Wu (1):
ring-buffer: Use current_context for safe per-CPU buffer swap
Toke Høiland-Jørgensen (1):
net/sched: sch_cake: drop WARN_ON(1) for malformed packets in ACK filter
Vincent Donnefort (3):
ring-buffer: Prevent subbuf order change when resizing is disabled
ring-buffer: Prevent resizing of persistent ring buffer
ring-buffer: Initialise reader page order in rb_allocate_cpu_buffer()
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
Will Chen (1):
bnxt: fix memory leak in bnxt_queue_mem_alloc error cases
Xiang Mei (Microsoft) (1):
bonding: alb: re-check primary_is_promisc under RTNL in bond_alb_monitor
Xin Long (1):
sctp: clear control chunk transport if it is being removed
Xu Rao (4):
thunderbolt: icm: Preserve USB4 proxy data-valid bit
ALSA: hda/tas2781: fix ACPI reference handling
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 (2):
bpf: Preserve pointer state for commuted arithmetic
bpf: Propagate untrusted pointer state in commuted arithmetic
Yizhou Zhao (1):
ipvs: separate destination availability state
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
Yun Zhou (1):
xfs: handle NULL b_addr in xfs_buf_free
Yuxiang Yang (1):
sctp: fix use-after-free of cached ASCONF chunk
Zhan Xusheng (1):
fscrypt: use the mount idmap for the owner check in fscrypt_ioctl_set_policy()
Zhiling Zou (7):
ipvs: stop estimator after disabled calc phase
net: openvswitch: reallocate update replies for mismatched IDs
inet: frags: publish queues before arming timer
netfilter: bridge: release template ct on non-IP path
xdp: reject clones that overrun skb_shared_info tailroom
mm/page_table_check: skip special zero mappings
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
liyouhong (1):
mm/damon/ops-common: putback folios on invalid migrate nid
Łukasz Patron (1):
net: qrtr: ns: Raise lookup limit to 128
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: Linux 7.1.9
2026-08-19 16:52 Linux 7.1.9 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/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 97007f4f69d4..f7f37de60b12 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -7573,6 +7573,10 @@ Kernel parameters
See Documentation/admin-guide/mm/transhuge.rst for more
details.
+ tlbi= [X86-64]
+ Format: {ipi}
+ ipi: switch to IPI-based TLB flushing
+
topology= [S390,EARLY]
Format: {off | on}
Specify if the kernel should make use of the cpu
@@ -8158,6 +8162,11 @@ Kernel parameters
q = USB_QUIRK_FORCE_ONE_CONFIG (Device
claims zero configurations,
forcing to 1);
+ r = USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE (Device
+ fails during initialization when asked for
+ 9-bytes configuration descriptor request.
+ Ask for 255-bytes request instead to mirror
+ Windows' behavior);
Example: quirks=0781:5580:bk,0a5c:5834:gij
usbhid.mousepoll=
diff --git a/Documentation/hwmon/hwmon-kernel-api.rst b/Documentation/hwmon/hwmon-kernel-api.rst
index 1d7f1397a827..9fcde32a140d 100644
--- a/Documentation/hwmon/hwmon-kernel-api.rst
+++ b/Documentation/hwmon/hwmon-kernel-api.rst
@@ -85,9 +85,10 @@ removal.
When using ``[devm_]hwmon_device_register_with_info()`` to register the
hardware monitoring device, accesses using the associated access functions
are serialised by the hardware monitoring core. If a driver needs locking
-for other functions such as interrupt handlers or for attributes which are
-fully implemented in the driver, hwmon_lock() and hwmon_unlock() can be used
-to ensure that calls to those functions are serialized.
+for other functions such as interrupt handlers, attributes which are fully
+implemented in the driver, or debugfs functions, hwmon_lock() and hwmon_unlock()
+can be used to ensure that calls to those functions are serialized. Those
+functions also support guard() and scoped_guard() variants.
Using devm_hwmon_device_register_with_info()
--------------------------------------------
diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst
index 4b30cd63c5a5..78fb484e8b04 100644
--- a/Documentation/sound/alsa-configuration.rst
+++ b/Documentation/sound/alsa-configuration.rst
@@ -2389,6 +2389,18 @@ quirk_flags
from snd_usb_handle_sync_urb. Instead fall through and enqueue a
packet_info containing only size-0 packets, so the OUT ring keeps
moving (emits silence). Needed by Behringer Flow 8 (1397:050c).
+ * bit 30: ``mixer_get_cur_broken``
+ Some mixers are sticky, which means that setting their current volume
+ is a no-op, and reading the current volume returns a constant value.
+ The sticky check disables these mixers to prevent confusing userspace.
+ However, some devices do have a tunable volume despite the reported
+ current volume being constant. As the sticky check can't distinguish
+ between the two categories, setting this flag tells that the device
+ should fall into the second category when GET_CUR returns a constant
+ value, resulting in the sticky check being non-fatal and only
+ disabling GET_CUR instead of the whole mixer. The current volume will
+ then be provided by the internal cache that stores the last set
+ volume
This module supports multiple devices, autoprobe and hotplugging.
diff --git a/MAINTAINERS b/MAINTAINERS
index f74da642290a..624cfb6cf1d4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10031,6 +10031,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 fa62d2054cd4..d7cca59812d8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 7
PATCHLEVEL = 1
-SUBLEVEL = 8
+SUBLEVEL = 9
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 392a25713669..bd52de0faa3e 100644
--- a/arch/arm/boot/dts/broadcom/bcm-ns.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm-ns.dtsi
@@ -141,7 +141,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 761c59d90ffc..09ff5e9959d3 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
+++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
@@ -678,8 +678,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/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi
index 63df60b7e010..25df2bd06735 100644
--- a/arch/arm64/boot/dts/qcom/glymur.dtsi
+++ b/arch/arm64/boot/dts/qcom/glymur.dtsi
@@ -1843,7 +1843,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
spi0: spi@b80000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b80000 0x0 0x4000>;
- interrupts = <GIC_SPI 1052 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 92 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -1870,7 +1870,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
i2c1: i2c@b84000 {
compatible = "qcom,geni-i2c";
reg = <0x0 0x00b84000 0x0 0x4000>;
- interrupts = <GIC_SPI 1053 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 93 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -1897,7 +1897,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
spi1: spi@b84000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b84000 0x0 0x4000>;
- interrupts = <GIC_SPI 1053 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 93 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -1924,7 +1924,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
i2c2: i2c@b88000 {
compatible = "qcom,geni-i2c";
reg = <0x0 0x00b88000 0x0 0x4000>;
- interrupts = <GIC_SPI 1054 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 94 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -1951,7 +1951,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
spi2: spi@b88000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b88000 0x0 0x4000>;
- interrupts = <GIC_SPI 1054 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 94 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -1978,7 +1978,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
uart2: serial@b88000 {
compatible = "qcom,geni-uart";
reg = <0x0 0x00b88000 0x0 0x4000>;
- interrupts = <GIC_SPI 1054 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 94 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2023,7 +2023,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
spi3: spi@b8c000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b8c000 0x0 0x4000>;
- interrupts = <GIC_SPI 1055 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 95 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S3_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2077,7 +2077,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
spi4: spi@b90000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b90000 0x0 0x4000>;
- interrupts = <GIC_SPI 1056 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 96 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S4_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2131,7 +2131,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
spi5: spi@b94000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b94000 0x0 0x4000>;
- interrupts = <GIC_SPI 1057 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 97 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S5_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2158,7 +2158,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
i2c6: i2c@b98000 {
compatible = "qcom,geni-i2c";
reg = <0x0 0x00b98000 0x0 0x4000>;
- interrupts = <GIC_SPI 1058 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 98 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2185,7 +2185,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
spi6: spi@b98000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b98000 0x0 0x4000>;
- interrupts = <GIC_SPI 1058 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 98 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2212,7 +2212,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
i2c7: i2c@b9c000 {
compatible = "qcom,geni-i2c";
reg = <0x0 0x00b9c000 0x0 0x4000>;
- interrupts = <GIC_SPI 1059 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 99 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2239,7 +2239,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>,
spi7: spi@b9c000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b9c000 0x0 0x4000>;
- interrupts = <GIC_SPI 1059 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 99 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -5626,9 +5626,9 @@ apps_smmu: iommu@15000000 {
pcie_smmu: iommu@15480000 {
compatible = "arm,smmu-v3";
reg = <0x0 0x15480000 0x0 0x20000>;
- interrupts = <GIC_SPI 964 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 962 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 960 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 4 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_ESPI 2 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_ESPI 0 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "eventq", "cmdq-sync", "gerror";
dma-coherent;
#iommu-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi
index fa13210fc539..62e6b60087ee 100644
--- a/arch/arm64/boot/dts/qcom/monaco.dtsi
+++ b/arch/arm64/boot/dts/qcom/monaco.dtsi
@@ -7384,6 +7384,7 @@ intc: interrupt-controller@17a00000 {
interrupt-controller;
#redistributor-regions = <1>;
redistributor-stride = <0x0 0x20000>;
+ #address-cells = <0>;
};
watchdog@17c10000 {
diff --git a/arch/arm64/boot/dts/qcom/purwa.dtsi b/arch/arm64/boot/dts/qcom/purwa.dtsi
index 9ab4f26b35f2..5b17840fb62f 100644
--- a/arch/arm64/boot/dts/qcom/purwa.dtsi
+++ b/arch/arm64/boot/dts/qcom/purwa.dtsi
@@ -47,6 +47,8 @@ &gmu {
&gpu {
compatible = "qcom,adreno-43030c00", "qcom,adreno";
+ iommus = <&adreno_smmu 0 0x0>;
+
nvmem-cells = <&gpu_speed_bin>;
nvmem-cell-names = "speed_bin";
diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
index e41200839dbe..8cdaef5360d1 100644
--- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
+++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
@@ -347,6 +347,10 @@ &cdsp_pas {
status = "okay";
};
+&cluster_sleep_0 {
+ arm,psci-suspend-param = <0x41008244>;
+};
+
&gcc {
protected-clocks = <GCC_QSPI_CORE_CLK>,
<GCC_QSPI_CORE_CLK_SRC>,
diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi
index e2d98cf6adca..ecfa3478cbdd 100644
--- a/arch/arm64/boot/dts/qcom/sm8650.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi
@@ -7091,8 +7091,8 @@ sram@14680000 {
ranges = <0 0 0x14680000 0x2c000>;
- ipa_modem_tables: modem-tables@8000 {
- reg = <0x8000 0x2000>;
+ ipa_modem_tables: modem-tables@3000 {
+ reg = <0x3000 0x2000>;
};
};
diff --git a/arch/arm64/include/asm/ptdump.h b/arch/arm64/include/asm/ptdump.h
index baff24004459..fded5358641f 100644
--- a/arch/arm64/include/asm/ptdump.h
+++ b/arch/arm64/include/asm/ptdump.h
@@ -7,8 +7,6 @@
#include <linux/ptdump.h>
-DECLARE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
-
#ifdef CONFIG_PTDUMP
#include <linux/mm_types.h>
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 92fedf4db927..09fd9da4eeed 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -49,8 +49,6 @@
#define NO_CONT_MAPPINGS BIT(1)
#define NO_EXEC_MAPPINGS BIT(2) /* assumes FEAT_HPDS is not used */
-DEFINE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
-
u64 kimage_voffset __ro_after_init;
EXPORT_SYMBOL(kimage_voffset);
@@ -1872,8 +1870,7 @@ int pmd_clear_huge(pmd_t *pmdp)
return 1;
}
-static int __pmd_free_pte_page(pmd_t *pmdp, unsigned long addr,
- bool acquire_mmap_lock)
+int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
{
pte_t *table;
pmd_t pmd;
@@ -1885,25 +1882,13 @@ static int __pmd_free_pte_page(pmd_t *pmdp, unsigned long addr,
return 1;
}
- /* See comment in pud_free_pmd_page for static key logic */
table = pte_offset_kernel(pmdp, addr);
pmd_clear(pmdp);
__flush_tlb_kernel_pgtable(addr);
- if (static_branch_unlikely(&arm64_ptdump_lock_key) && acquire_mmap_lock) {
- mmap_read_lock(&init_mm);
- mmap_read_unlock(&init_mm);
- }
-
pte_free_kernel(NULL, table);
return 1;
}
-int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
-{
- /* If ptdump is walking the pagetables, acquire init_mm.mmap_lock */
- return __pmd_free_pte_page(pmdp, addr, /* acquire_mmap_lock = */ true);
-}
-
int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
{
pmd_t *table;
@@ -1919,36 +1904,16 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
}
table = pmd_offset(pudp, addr);
-
- /*
- * Our objective is to prevent ptdump from reading a PMD table which has
- * been freed. In this race, if pud_free_pmd_page observes the key on
- * (which got flipped by ptdump) then the mmap lock sequence here will,
- * as a result of the mmap write lock/unlock sequence in ptdump, give
- * us the correct synchronization. If not, this means that ptdump has
- * yet not started walking the pagetables - the sequence of barriers
- * issued by __flush_tlb_kernel_pgtable() guarantees that ptdump will
- * observe an empty PUD.
- */
- pud_clear(pudp);
- __flush_tlb_kernel_pgtable(addr);
- if (static_branch_unlikely(&arm64_ptdump_lock_key)) {
- mmap_read_lock(&init_mm);
- mmap_read_unlock(&init_mm);
- }
-
pmdp = table;
next = addr;
end = addr + PUD_SIZE;
do {
if (pmd_present(pmdp_get(pmdp)))
- /*
- * PMD has been isolated, so ptdump won't see it. No
- * need to acquire init_mm.mmap_lock.
- */
- __pmd_free_pte_page(pmdp, next, /* acquire_mmap_lock = */ false);
+ pmd_free_pte_page(pmdp, next);
} while (pmdp++, next += PMD_SIZE, next != end);
+ pud_clear(pudp);
+ __flush_tlb_kernel_pgtable(addr);
pmd_free(NULL, table);
return 1;
}
diff --git a/arch/arm64/mm/ptdump.c b/arch/arm64/mm/ptdump.c
index ab9899ca1e5f..421a5de806c6 100644
--- a/arch/arm64/mm/ptdump.c
+++ b/arch/arm64/mm/ptdump.c
@@ -283,13 +283,6 @@ void note_page_flush(struct ptdump_state *pt_st)
note_page(pt_st, 0, -1, pte_val(pte_zero));
}
-static void arm64_ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm)
-{
- static_branch_inc(&arm64_ptdump_lock_key);
- ptdump_walk_pgd(st, mm, NULL);
- static_branch_dec(&arm64_ptdump_lock_key);
-}
-
void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
{
unsigned long end = ~0UL;
@@ -318,7 +311,7 @@ void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
}
};
- arm64_ptdump_walk_pgd(&st.ptdump, info->mm);
+ ptdump_walk_pgd(&st.ptdump, info->mm, NULL);
}
static void __init ptdump_initialize(void)
@@ -360,7 +353,7 @@ bool ptdump_check_wx(void)
}
};
- arm64_ptdump_walk_pgd(&st.ptdump, &init_mm);
+ ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
if (st.wx_pages || st.uxn_pages) {
pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\n",
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index a4268c47f2bc..558a253c92fa 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -2668,3 +2668,13 @@ void __init arch_cpu_finalize_init(void)
*/
mem_encrypt_init();
}
+
+/* Control TLB flushing methods */
+static int __init tlbi_setup(char *str)
+{
+ if (!strcmp(str, "ipi"))
+ setup_clear_cpu_cap(X86_FEATURE_INVLPGB);
+
+ return 1;
+}
+__setup("tlbi=", tlbi_setup);
diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c
index f3a793e3a6c8..1848c54bae85 100644
--- a/arch/x86/kernel/cpu/mce/core.c
+++ b/arch/x86/kernel/cpu/mce/core.c
@@ -2269,10 +2269,10 @@ void mcheck_cpu_init(struct cpuinfo_x86 *c)
mca_cfg.initialized = 1;
+ __mcheck_cpu_setup_timer();
__mcheck_cpu_init_generic();
__mcheck_cpu_init_vendor(c);
__mcheck_cpu_init_prepare_banks();
- __mcheck_cpu_setup_timer();
cr4_set_bits(X86_CR4_MCE);
}
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 34a312705253..0e918f35bfbe 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -2415,6 +2415,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 996f398a577f..e1d73ccaa547 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -96,6 +96,8 @@ static u64 sev_supported_vmsa_features __ro_after_init;
static u8 sev_enc_bit;
static DECLARE_RWSEM(sev_deactivate_lock);
static DEFINE_MUTEX(sev_bitmap_lock);
+/* Protects kvm_sev_info's enc_context_owner, mirror_vms and mirror_entry. */
+static DEFINE_MUTEX(sev_mirror_lock);
unsigned int max_sev_asid;
static unsigned int min_sev_asid;
static unsigned int max_sev_es_asid;
@@ -2031,7 +2033,6 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm)
dst->asid = src->asid;
dst->handle = src->handle;
dst->pages_locked = src->pages_locked;
- dst->enc_context_owner = src->enc_context_owner;
dst->es_active = src->es_active;
dst->vmsa_features = src->vmsa_features;
@@ -2039,11 +2040,12 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm)
src->active = false;
src->handle = 0;
src->pages_locked = 0;
- src->enc_context_owner = NULL;
src->es_active = false;
list_cut_before(&dst->regions_list, &src->regions_list, &src->regions_list);
+ mutex_lock(&sev_mirror_lock);
+
/*
* If this VM has mirrors, "transfer" each mirror's refcount of the
* source to the destination (this KVM). The caller holds a reference
@@ -2060,12 +2062,15 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm)
* If this VM is a mirror, remove the old mirror from the owners list
* and add the new mirror to the list.
*/
- if (is_mirroring_enc_context(dst_kvm)) {
- struct kvm_sev_info *owner_sev_info = to_kvm_sev_info(dst->enc_context_owner);
+ if (is_mirroring_enc_context(src_kvm)) {
+ struct kvm_sev_info *owner_sev_info = to_kvm_sev_info(src->enc_context_owner);
+ dst->enc_context_owner = src->enc_context_owner;
+ src->enc_context_owner = NULL;
list_del(&src->mirror_entry);
list_add_tail(&dst->mirror_entry, &owner_sev_info->mirror_vms);
}
+ mutex_unlock(&sev_mirror_lock);
kvm_for_each_vcpu(i, dst_vcpu, dst_kvm) {
dst_svm = to_svm(dst_vcpu);
@@ -2885,11 +2890,14 @@ int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd)
* disappear until we're done with it
*/
source_sev = to_kvm_sev_info(source_kvm);
- kvm_get_kvm(source_kvm);
- list_add_tail(&mirror_sev->mirror_entry, &source_sev->mirror_vms);
/* Set enc_context_owner and copy its encryption context over */
+ mutex_lock(&sev_mirror_lock);
+ kvm_get_kvm(source_kvm);
+ list_add_tail(&mirror_sev->mirror_entry, &source_sev->mirror_vms);
mirror_sev->enc_context_owner = source_kvm;
+ mutex_unlock(&sev_mirror_lock);
+
mirror_sev->active = true;
mirror_sev->asid = source_sev->asid;
mirror_sev->fd = source_sev->fd;
@@ -2977,11 +2985,19 @@ void sev_vm_destroy(struct kvm *kvm)
* Note, mirror VMs don't support registering encrypted regions.
*/
if (is_mirroring_enc_context(kvm)) {
- struct kvm *owner_kvm = sev->enc_context_owner;
+ struct kvm *owner_kvm;
- mutex_lock(&owner_kvm->lock);
+ mutex_lock(&sev_mirror_lock);
+ owner_kvm = sev->enc_context_owner;
list_del(&sev->mirror_entry);
- mutex_unlock(&owner_kvm->lock);
+ sev->enc_context_owner = NULL;
+
+ /*
+ * The reference to owner_kvm cannot move after sev_mirror_lock is
+ * released. Release it before kvm_put_kvm() so that owner_kvm is
+ * never destroyed inside sev_mirror_lock.
+ */
+ mutex_unlock(&sev_mirror_lock);
kvm_put_kvm(owner_kvm);
return;
}
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 5137416be593..0632ff940eab 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -106,6 +106,7 @@ struct kvm_sev_info {
u64 ap_jump_table; /* SEV-ES AP Jump Table address */
u64 vmsa_features;
u16 ghcb_version; /* Highest guest GHCB protocol version allowed */
+ /* The three fields below are protected by sev_mirror_lock */
struct kvm *enc_context_owner; /* Owner of copied encryption context */
struct list_head mirror_vms; /* List of VMs mirroring */
struct list_head mirror_entry; /* Use as a list entry of mirrors */
diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c
index cdf113dadc4d..fff1c707d040 100644
--- a/drivers/accel/amdxdna/amdxdna_gem.c
+++ b/drivers/accel/amdxdna/amdxdna_gem.c
@@ -371,6 +371,23 @@ static void amdxdna_gem_dev_obj_free(struct drm_gem_object *gobj)
amdxdna_gem_destroy_obj(abo);
}
+static void amdxdna_mark_mapp_invalid(struct amdxdna_gem_obj *abo,
+ struct vm_area_struct *vma)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
+ struct amdxdna_umap *mapp;
+
+ down_write(&xdna->notifier_lock);
+ abo->mem.map_invalid = true;
+ list_for_each_entry(mapp, &abo->mem.umap_list, node) {
+ if (compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end)) {
+ mapp->invalid = true;
+ break;
+ }
+ }
+ up_write(&xdna->notifier_lock);
+}
+
static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo,
struct vm_area_struct *vma)
{
@@ -385,26 +402,17 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo,
XDNA_ERR(xdna, "Failed shmem mmap %d", ret);
return ret;
}
-
- /* The buffer is based on memory pages. Fix the flag. */
- vm_flags_mod(vma, VM_MIXEDMAP, VM_PFNMAP);
- ret = vm_insert_pages(vma, vma->vm_start, abo->base.pages,
- &num_pages);
+ } else {
+ vma->vm_private_data = NULL;
+ vma->vm_ops = NULL;
+ ret = dma_buf_mmap(abo->dma_buf, vma, 0);
if (ret) {
- XDNA_ERR(xdna, "Failed insert pages %d", ret);
- vma->vm_ops->close(vma);
+ XDNA_ERR(xdna, "Failed to mmap dma buf %d", ret);
return ret;
}
- return 0;
- }
-
- vma->vm_private_data = NULL;
- vma->vm_ops = NULL;
- ret = dma_buf_mmap(abo->dma_buf, vma, 0);
- if (ret) {
- XDNA_ERR(xdna, "Failed to mmap dma buf %d", ret);
- return ret;
+ /* Drop the reference drm_gem_mmap_obj() acquired.*/
+ drm_gem_object_put(to_gobj(abo));
}
do {
@@ -413,17 +421,14 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo,
fault_ret = handle_mm_fault(vma, vma->vm_start + offset,
FAULT_FLAG_WRITE, NULL);
if (fault_ret & VM_FAULT_ERROR) {
- vma->vm_ops->close(vma);
XDNA_ERR(xdna, "Fault in page failed");
- return -EFAULT;
+ amdxdna_mark_mapp_invalid(abo, vma);
+ break;
}
offset += PAGE_SIZE;
} while (--num_pages);
- /* Drop the reference drm_gem_mmap_obj() acquired.*/
- drm_gem_object_put(to_gobj(abo));
-
return 0;
}
diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs
index cdd1a9079726..5b8f73ec1931 100644
--- a/drivers/android/binder/process.rs
+++ b/drivers/android/binder/process.rs
@@ -1586,6 +1586,10 @@ fn ioctl_write_only(
cmd: u32,
reader: &mut UserSliceReader,
) -> Result {
+ if cmd == uapi::BINDER_FREEZE {
+ return ioctl_freeze(reader);
+ }
+
let thread = this.get_current_thread()?;
match cmd {
uapi::BINDER_SET_MAX_THREADS => this.set_max_threads(reader.read()?),
@@ -1597,7 +1601,6 @@ fn ioctl_write_only(
uapi::BINDER_ENABLE_ONEWAY_SPAM_DETECTION => {
this.set_oneway_spam_detection_enabled(reader.read()?)
}
- uapi::BINDER_FREEZE => ioctl_freeze(reader)?,
_ => return Err(EINVAL),
}
Ok(())
@@ -1612,15 +1615,16 @@ fn ioctl_write_read(
cmd: u32,
data: UserSlice,
) -> Result {
- let thread = this.get_current_thread()?;
let blocking = (file.flags() & file::flags::O_NONBLOCK) == 0;
match cmd {
- uapi::BINDER_WRITE_READ => thread.write_read(data, blocking)?,
+ uapi::BINDER_WRITE_READ => this.get_current_thread()?.write_read(data, blocking)?,
uapi::BINDER_GET_NODE_DEBUG_INFO => this.get_node_debug_info(data)?,
uapi::BINDER_GET_NODE_INFO_FOR_REF => this.get_node_info_from_ref(data)?,
uapi::BINDER_VERSION => this.version(data)?,
uapi::BINDER_GET_FROZEN_INFO => get_frozen_status(data)?,
- uapi::BINDER_GET_EXTENDED_ERROR => thread.get_extended_error(data)?,
+ uapi::BINDER_GET_EXTENDED_ERROR => {
+ this.get_current_thread()?.get_extended_error(data)?
+ }
_ => return Err(EINVAL),
}
Ok(())
diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c
index 2d24c6b3e9d9..25bce3fe2a10 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/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 03fa9409e3ee..7f54d2d2d13a 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -1343,6 +1343,19 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
if (!btrtl_dev->ic_info)
return;
+ switch (btrtl_dev->project_id) {
+ case CHIP_ID_8761B:
+ /* RTL8761B/BU reports HCI version 5.1 but does not support
+ * the LE Extended Scan commands (Opcode 0x2042), causing
+ * repeated -EBUSY failures when BlueZ attempts extended
+ * scanning while a connection is active.
+ */
+ hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN);
+ break;
+ default:
+ break;
+ }
+
switch (btrtl_dev->ic_info->lmp_subver) {
case RTL_ROM_LMP_8703B:
/* 8723CS reports two pages for local ext features,
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index a072e6f57f31..4a356f720e1f 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -832,6 +832,8 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x2b89, 0x6275), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x37ad, 0x0600), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Additional Realtek 8821AE Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c
index 19d457ae4c3b..e53a8390756b 100644
--- a/drivers/counter/microchip-tcb-capture.c
+++ b/drivers/counter/microchip-tcb-capture.c
@@ -483,7 +483,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));
@@ -517,7 +517,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/dibs/dibs_loopback.c b/drivers/dibs/dibs_loopback.c
index fd5caf1e19a8..649e4e375be3 100644
--- a/drivers/dibs/dibs_loopback.c
+++ b/drivers/dibs/dibs_loopback.c
@@ -335,7 +335,6 @@ static int dibs_lo_dev_probe(void)
return 0;
err_reg:
- kfree(dibs->dmb_clientid_arr);
/* pairs with dibs_dev_alloc() */
put_device(&dibs->dev);
kfree(ldev);
diff --git a/drivers/dibs/dibs_main.c b/drivers/dibs/dibs_main.c
index 14c3e2d84902..2b53a9d277dc 100644
--- a/drivers/dibs/dibs_main.c
+++ b/drivers/dibs/dibs_main.c
@@ -128,6 +128,7 @@ static void dibs_dev_release(struct device *dev)
dibs = container_of(dev, struct dibs_dev, dev);
+ kfree(dibs->dmb_clientid_arr);
kfree(dibs);
}
@@ -138,6 +139,7 @@ struct dibs_dev *dibs_dev_alloc(void)
dibs = kzalloc_obj(*dibs);
if (!dibs)
return dibs;
+ spin_lock_init(&dibs->lock);
dibs->dev.release = dibs_dev_release;
dibs->dev.class = &dibs_class;
device_initialize(&dibs->dev);
@@ -186,7 +188,6 @@ int dibs_dev_add(struct dibs_dev *dibs)
int i, ret;
max_dmbs = dibs->ops->max_dmbs();
- spin_lock_init(&dibs->lock);
dibs->dmb_clientid_arr = kzalloc(max_dmbs, GFP_KERNEL);
if (!dibs->dmb_clientid_arr)
return -ENOMEM;
@@ -194,12 +195,13 @@ int dibs_dev_add(struct dibs_dev *dibs)
ret = device_add(&dibs->dev);
if (ret)
- goto free_client_arr;
+ return ret;
ret = sysfs_create_group(&dibs->dev.kobj, &dibs_dev_attr_group);
if (ret) {
dev_err(&dibs->dev, "sysfs_create_group failed for dibs_dev\n");
- goto err_device_del;
+ device_del(&dibs->dev);
+ return ret;
}
mutex_lock(&dibs_dev_list.mutex);
mutex_lock(&clients_lock);
@@ -214,13 +216,6 @@ int dibs_dev_add(struct dibs_dev *dibs)
mutex_unlock(&dibs_dev_list.mutex);
return 0;
-
-err_device_del:
- device_del(&dibs->dev);
-free_client_arr:
- kfree(dibs->dmb_clientid_arr);
- return ret;
-
}
EXPORT_SYMBOL_GPL(dibs_dev_add);
@@ -247,7 +242,6 @@ void dibs_dev_del(struct dibs_dev *dibs)
mutex_unlock(&dibs_dev_list.mutex);
device_del(&dibs->dev);
- kfree(dibs->dmb_clientid_arr);
}
EXPORT_SYMBOL_GPL(dibs_dev_del);
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 69c5bd9aaee7..cb282b35fb58 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -605,20 +605,28 @@ static int pca953x_read_regs(struct pca953x_chip *chip, int reg, unsigned long *
return 0;
}
-static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
+static int pca953x_gpio_direction_input_unlocked(struct gpio_chip *gc,
+ unsigned int off)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off);
u8 bit = pca953x_get_bit_mask(chip, off);
- guard(mutex)(&chip->i2c_lock);
-
if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE)
return regmap_update_bits(chip->regmap, dirreg, bit, 0);
return regmap_update_bits(chip->regmap, dirreg, bit, bit);
}
+static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned int off)
+{
+ struct pca953x_chip *chip = gpiochip_get_data(gc);
+
+ guard(mutex)(&chip->i2c_lock);
+
+ return pca953x_gpio_direction_input_unlocked(gc, off);
+}
+
static int pca953x_gpio_direction_output(struct gpio_chip *gc,
unsigned off, int val)
{
@@ -856,9 +864,10 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
DECLARE_BITMAP(reg_direction, MAX_LINE);
int level;
+ guard(mutex)(&chip->i2c_lock);
+
if (chip->driver_data & PCA_PCAL) {
DECLARE_BITMAP(latched_inputs, MAX_LINE);
- guard(mutex)(&chip->i2c_lock);
/* Enable latch on edge-triggered interrupt-enabled inputs */
bitmap_or(latched_inputs, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio);
@@ -882,7 +891,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
/* Look for any newly setup interrupt */
for_each_set_bit(level, irq_mask, gc->ngpio)
- pca953x_gpio_direction_input(&chip->gpio_chip, level);
+ pca953x_gpio_direction_input_unlocked(&chip->gpio_chip, level);
mutex_unlock(&chip->irq_lock);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 3fad14824d1e..22e88644402e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -4169,6 +4169,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 81c8eba2b7a9..c064f8a1e5f3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -2121,23 +2121,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
amdgpu_ttm_init_vram_resv_regions(adev);
@@ -2266,6 +2260,8 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
*/
void amdgpu_ttm_fini(struct amdgpu_device *adev)
{
+ int idx;
+
if (!adev->mman.initialized)
return;
@@ -2288,7 +2284,14 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev)
amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_FW_VRAM_USAGE);
amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_DRV_VRAM_USAGE);
- 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);
+ }
if (!adev->gmc.is_app_apu)
amdgpu_vram_mgr_fini(adev);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 40d82a3eeec0..6f9d4bea3379 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -5401,11 +5401,11 @@ amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector)
caps = &dm->backlight_caps[aconnector->bl_idx];
if (get_brightness_range(caps, &min, &max)) {
if (power_supply_is_system_supplied() > 0)
- props.brightness = DIV_ROUND_CLOSEST(max * caps->ac_level, 100);
+ props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->ac_level, 100);
else
- props.brightness = DIV_ROUND_CLOSEST(max * caps->dc_level, 100);
+ props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->dc_level, 100);
/* min is zero, so max needs to be adjusted */
- props.max_brightness = max;
+ props.max_brightness = max - min;
drm_dbg(drm, "Backlight caps: min: %d, max: %d, ac %d, dc %d\n", min, max,
caps->ac_level, caps->dc_level);
} else
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 f0abbb7c2cb2..3b26229eff2b 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
@@ -1320,8 +1320,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);
+ }
+ }
+ }
}
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 825777a5758f..db8e5889dc7f 100644
--- a/drivers/gpu/drm/bridge/parade-ps8640.c
+++ b/drivers/gpu/drm/bridge/parade-ps8640.c
@@ -257,8 +257,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)) {
@@ -274,13 +280,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/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h
index 6a3cad933439..940946390793 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.h
+++ b/drivers/gpu/drm/v3d/v3d_drv.h
@@ -181,6 +181,12 @@ struct v3d_dev {
*/
struct mutex reset_lock;
+ /* Ordered workqueue shared by every queue's scheduler timeout work.
+ * V3D reset is global to all queues, so the timeout handlers must not
+ * run concurrently.
+ */
+ struct workqueue_struct *reset_wq;
+
/* Lock taken when creating and pushing the GPU scheduler
* jobs, to keep the sched-fence seqnos in order.
*/
diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c
index 8a635a9ec046..bea46298b69e 100644
--- a/drivers/gpu/drm/v3d/v3d_sched.c
+++ b/drivers/gpu/drm/v3d/v3d_sched.c
@@ -838,6 +838,7 @@ v3d_queue_sched_init(struct v3d_dev *v3d, const struct drm_sched_backend_ops *op
.num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = 1,
.timeout = msecs_to_jiffies(500),
+ .timeout_wq = v3d->reset_wq,
.dev = v3d->drm.dev,
};
@@ -852,9 +853,13 @@ v3d_sched_init(struct v3d_dev *v3d)
{
int ret;
+ v3d->reset_wq = alloc_ordered_workqueue("v3d_reset", 0);
+ if (!v3d->reset_wq)
+ return -ENOMEM;
+
ret = v3d_queue_sched_init(v3d, &v3d_bin_sched_ops, V3D_BIN, "v3d_bin");
if (ret)
- return ret;
+ goto fail;
ret = v3d_queue_sched_init(v3d, &v3d_render_sched_ops, V3D_RENDER,
"v3d_render");
@@ -897,4 +902,6 @@ v3d_sched_fini(struct v3d_dev *v3d)
if (v3d->queue[q].sched.ready)
drm_sched_fini(&v3d->queue[q].sched);
}
+
+ destroy_workqueue(v3d->reset_wq);
}
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index 071b8c41df43..85778958f580 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -801,6 +801,9 @@ static int exec_queue_set_hang_replay_state(struct xe_device *xe,
u64 __user *address = u64_to_user_ptr(value);
void *ptr;
+ if (q->replay_state)
+ return -EINVAL;
+
ptr = vmemdup_user(address, size);
if (XE_IOCTL_DBG(xe, IS_ERR(ptr)))
return PTR_ERR(ptr);
diff --git a/drivers/gpu/drm/xe/xe_uc.c b/drivers/gpu/drm/xe/xe_uc.c
index 75091bde0d50..65f59f06177f 100644
--- a/drivers/gpu/drm/xe/xe_uc.c
+++ b/drivers/gpu/drm/xe/xe_uc.c
@@ -15,6 +15,7 @@
#include "xe_guc_pc.h"
#include "xe_guc_rc.h"
#include "xe_guc_engine_activity.h"
+#include "xe_guc_submit.h"
#include "xe_huc.h"
#include "xe_sriov.h"
#include "xe_wopcm.h"
@@ -159,12 +160,14 @@ static int vf_uc_load_hw(struct xe_uc *uc)
if (err)
return err;
- uc->guc.submission_state.enabled = true;
-
err = xe_guc_opt_in_features_enable(&uc->guc);
if (err)
return err;
+ err = xe_guc_submit_enable(&uc->guc);
+ if (err)
+ return err;
+
err = xe_gt_record_default_lrcs(uc_to_gt(uc));
if (err)
return err;
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index 149cfcec78dc..f8f2f621dfea 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 ce958cdaef58..ebcaddf337d8 100644
--- a/drivers/hwmon/corsair-psu.c
+++ b/drivers/hwmon/corsair-psu.c
@@ -664,6 +664,8 @@ static void print_uptime(struct seq_file *seqf, u8 cmd)
long val;
int ret;
+ guard(hwmon_lock)(priv->hwmon_dev);
+
ret = corsairpsu_get_value(priv, cmd, 0, &val);
if (ret < 0) {
seq_puts(seqf, "N/A\n");
@@ -701,7 +703,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;
}
@@ -711,7 +713,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;
}
@@ -723,6 +725,8 @@ static int ocpmode_show(struct seq_file *seqf, void *unused)
long val;
int ret;
+ guard(hwmon_lock)(priv->hwmon_dev);
+
/*
* The rail mode is switchable on the fly. The RAW interface can be used for this. But it
* will not be included here, because I consider it somewhat dangerous for the health of the
diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c
index bdbf370233d7..d7c622608144 100644
--- a/drivers/hwmon/ltc4282.c
+++ b/drivers/hwmon/ltc4282.c
@@ -14,6 +14,7 @@
#include <linux/hwmon.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>
@@ -138,7 +139,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;
@@ -614,13 +615,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,
@@ -630,7 +630,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;
@@ -931,8 +931,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:
@@ -1223,7 +1226,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;
@@ -1391,7 +1395,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 ff0c0bee0e83..efc78a8f58c8 100644
--- a/drivers/hwmon/nzxt-smart2.c
+++ b/drivers/hwmon/nzxt-smart2.c
@@ -754,7 +754,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 0f7df7e2c9c5..cb42832cebf0 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/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 5567d37f13fe..3b219ed5386f 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -45,7 +45,7 @@ module_param(wp, int, 0444);
struct pmbus_sensor {
struct pmbus_sensor *next;
char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */
- struct device_attribute attribute;
+ struct sensor_device_attribute attribute;
u8 page; /* page number */
u8 phase; /* phase number, 0xff for all phases */
u16 reg; /* register */
@@ -68,7 +68,7 @@ struct pmbus_boolean {
struct pmbus_label {
char name[PMBUS_NAME_SIZE]; /* sysfs label name */
- struct device_attribute attribute;
+ struct sensor_device_attribute attribute;
char label[PMBUS_NAME_SIZE]; /* label */
};
#define to_pmbus_label(_attr) \
@@ -1241,7 +1241,8 @@ static ssize_t pmbus_show_sensor(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev->parent);
- struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct pmbus_sensor *sensor = to_pmbus_sensor(attr);
struct pmbus_data *data = i2c_get_clientdata(client);
s64 val;
@@ -1261,7 +1262,8 @@ static ssize_t pmbus_set_sensor(struct device *dev,
{
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_data *data = i2c_get_clientdata(client);
- struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct pmbus_sensor *sensor = to_pmbus_sensor(attr);
s64 val;
int ret;
u16 regval;
@@ -1283,7 +1285,8 @@ static ssize_t pmbus_set_sensor(struct device *dev,
static ssize_t pmbus_show_label(struct device *dev,
struct device_attribute *da, char *buf)
{
- struct pmbus_label *label = to_pmbus_label(da);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct pmbus_label *label = to_pmbus_label(attr);
return sysfs_emit(buf, "%s\n", label->label);
}
@@ -1436,8 +1439,8 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
bool update, bool readonly,
bool writeonly, bool convert)
{
+ struct sensor_device_attribute *a;
struct pmbus_sensor *sensor;
- struct device_attribute *a;
sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
@@ -1461,12 +1464,11 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
sensor->update = update;
sensor->convert = convert;
sensor->data = -ENODATA;
- pmbus_dev_attr_init(a, sensor->name,
- readonly ? 0444 : 0644,
- writeonly ? pmbus_show_zero : pmbus_show_sensor,
- pmbus_set_sensor);
+ pmbus_attr_init(a, sensor->name, readonly ? 0444 : 0644,
+ writeonly ? pmbus_show_zero : pmbus_show_sensor,
+ pmbus_set_sensor, -1);
- if (pmbus_add_attribute(data, &a->attr))
+ if (pmbus_add_attribute(data, &a->dev_attr.attr))
return NULL;
sensor->next = data->sensors;
@@ -1483,8 +1485,8 @@ static int pmbus_add_label(struct pmbus_data *data,
const char *name, int seq,
const char *lstring, int index, int phase)
{
+ struct sensor_device_attribute *a;
struct pmbus_label *label;
- struct device_attribute *a;
label = devm_kzalloc(data->dev, sizeof(*label), GFP_KERNEL);
if (!label)
@@ -1508,8 +1510,8 @@ static int pmbus_add_label(struct pmbus_data *data,
lstring, index, phase);
}
- pmbus_dev_attr_init(a, label->name, 0444, pmbus_show_label, NULL);
- return pmbus_add_attribute(data, &a->attr);
+ pmbus_attr_init(a, label->name, 0444, pmbus_show_label, NULL, -1);
+ return pmbus_add_attribute(data, &a->dev_attr.attr);
}
/*
@@ -2397,7 +2399,7 @@ struct pmbus_samples_attr {
struct pmbus_samples_reg {
int page;
struct pmbus_samples_attr *attr;
- struct device_attribute dev_attr;
+ struct sensor_device_attribute attribute;
};
static struct pmbus_samples_attr pmbus_samples_registers[] = {
@@ -2419,14 +2421,15 @@ static struct pmbus_samples_attr pmbus_samples_registers[] = {
}
};
-#define to_samples_reg(x) container_of(x, struct pmbus_samples_reg, dev_attr)
+#define to_samples_reg(x) container_of(x, struct pmbus_samples_reg, attribute)
static ssize_t pmbus_show_samples(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int val;
struct i2c_client *client = to_i2c_client(dev->parent);
- struct pmbus_samples_reg *reg = to_samples_reg(devattr);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct pmbus_samples_reg *reg = to_samples_reg(attr);
scoped_guard(pmbus_lock, client) {
val = _pmbus_read_word_data(client, reg->page, 0xff, reg->attr->reg);
@@ -2444,7 +2447,8 @@ static ssize_t pmbus_set_samples(struct device *dev,
int ret;
long val;
struct i2c_client *client = to_i2c_client(dev->parent);
- struct pmbus_samples_reg *reg = to_samples_reg(devattr);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct pmbus_samples_reg *reg = to_samples_reg(attr);
if (kstrtol(buf, 0, &val) < 0)
return -EINVAL;
@@ -2459,6 +2463,7 @@ static ssize_t pmbus_set_samples(struct device *dev,
static int pmbus_add_samples_attr(struct pmbus_data *data, int page,
struct pmbus_samples_attr *attr)
{
+ struct sensor_device_attribute *a;
struct pmbus_samples_reg *reg;
reg = devm_kzalloc(data->dev, sizeof(*reg), GFP_KERNEL);
@@ -2468,10 +2473,12 @@ static int pmbus_add_samples_attr(struct pmbus_data *data, int page,
reg->attr = attr;
reg->page = page;
- pmbus_dev_attr_init(®->dev_attr, attr->name, 0644,
- pmbus_show_samples, pmbus_set_samples);
+ a = ®->attribute;
+
+ pmbus_attr_init(a, attr->name, 0644,
+ pmbus_show_samples, pmbus_set_samples, -1);
- return pmbus_add_attribute(data, ®->dev_attr.attr);
+ return pmbus_add_attribute(data, &a->dev_attr.attr);
}
static int pmbus_add_samples_attributes(struct i2c_client *client,
@@ -2979,9 +2986,15 @@ static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags)
struct device_attribute *da = to_dev_attr(data->group.attrs[i]);
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
int index = attr->index;
- u16 smask = pb_index_to_mask(index);
- u8 spage = pb_index_to_page(index);
- u16 sreg = pb_index_to_reg(index);
+ u16 smask, sreg;
+ u8 spage;
+
+ if (index == -1)
+ continue;
+
+ smask = pb_index_to_mask(index);
+ spage = pb_index_to_page(index);
+ sreg = pb_index_to_reg(index);
if (reg == sreg && page == spage && (smask & flags)) {
dev_dbg(data->dev, "sysfs notify: %s", da->attr.name);
@@ -3427,6 +3440,8 @@ static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg,
{
int ret;
+ guard(pmbus_lock)(client);
+
ret = _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
/*
@@ -3662,6 +3677,8 @@ static void pmbus_init_debugfs(struct i2c_client *client,
if (!entries)
return;
+ guard(pmbus_lock)(client);
+
/*
* Add device-specific entries.
* Please note that the PMBUS standard allows all registers to be
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index c7325226cb86..3a718d600006 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 f3a49384586d..eb6c2a78d3c7 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -492,6 +492,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);
}
@@ -1305,10 +1306,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)
@@ -1370,7 +1373,9 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
err = PTR_ERR(name);
goto err;
}
-
+ inbuf.client_id = fl->client_id;
+ inbuf.namelen = init.namelen;
+ inbuf.pageslen = 0;
if (!fl->cctx->remote_heap) {
err = fastrpc_remote_heap_alloc(fl, fl->sctx->dev, init.memlen,
&fl->cctx->remote_heap);
@@ -1393,12 +1398,10 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
goto err_map;
}
scm_done = true;
+ inbuf.pageslen = 1;
}
}
- inbuf.client_id = fl->client_id;
- inbuf.namelen = init.namelen;
- inbuf.pageslen = 0;
fl->pd = USER_PD;
args[0].ptr = (u64)(uintptr_t)&inbuf;
@@ -1673,7 +1676,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;
}
@@ -1889,9 +1892,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);
@@ -1905,6 +1905,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;
@@ -1912,6 +1913,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;
}
@@ -1924,7 +1926,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 643b0039cc72..26d2b2742d50 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -425,18 +425,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);
}
/**
@@ -565,9 +571,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;
}
@@ -1401,7 +1405,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
@@ -1410,8 +1414,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/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 2d37b07c8215..839f7482dc18 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -1534,8 +1534,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);
@@ -1597,9 +1597,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 4a6b35c84dab..2449e19321f7 100644
--- a/drivers/net/ethernet/amd/pds_core/core.h
+++ b/drivers/net/ethernet/amd/pds_core/core.h
@@ -171,6 +171,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 8d94a4d70395..9a2c64198d03 100644
--- a/drivers/net/ethernet/amd/pds_core/main.c
+++ b/drivers/net/ethernet/amd/pds_core/main.c
@@ -470,8 +470,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)
@@ -479,6 +481,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);
}
@@ -555,7 +561,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 e270327e47fd..9820b9f8c210 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 2f9033ceed8c..05814fea0f5f 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/bnge/bnge_auxr.c b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
index 67e93e17d4d9..0955b488b6fe 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
@@ -141,12 +141,15 @@ static void bnge_aux_dev_release(struct device *dev)
{
struct bnge_auxr_priv *aux_priv =
container_of(dev, struct bnge_auxr_priv, aux_dev.dev);
- struct bnge_dev *bd = pci_get_drvdata(aux_priv->auxr_dev->pdev);
+ struct bnge_auxr_dev *auxr_dev = aux_priv->auxr_dev;
+ struct bnge_dev *bd = pci_get_drvdata(to_pci_dev(dev->parent));
ida_free(&bnge_aux_dev_ids, aux_priv->id);
- kfree(aux_priv->auxr_dev->auxr_info);
+ if (auxr_dev) {
+ kfree(auxr_dev->auxr_info);
+ kfree(auxr_dev);
+ }
bd->auxr_dev = NULL;
- kfree(aux_priv->auxr_dev);
kfree(aux_priv);
bd->aux_priv = NULL;
}
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
index 6f7ef506d4e1..ac4c93e5b634 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
@@ -2768,8 +2768,6 @@ static int bnge_init_nic(struct bnge_net *bn)
err_free_ring_grps:
bnge_free_ring_grps(bn);
- return rc;
-
err_free_rx_ring_pair_bufs:
bnge_free_rx_ring_pair_bufs(bn);
return rc;
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
index 0e94f092813e..4711dd4945ff 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
@@ -163,7 +163,8 @@ static int bnge_adjust_rings(struct bnge_dev *bd, u16 *rx,
u16 tx_chunks = bnge_num_tx_to_cp(bd, *tx);
if (tx_chunks != *tx) {
- u16 tx_saved = tx_chunks, rc;
+ u16 tx_saved = tx_chunks;
+ int rc;
rc = bnge_fix_rings_count(rx, &tx_chunks, max_nq, sh);
if (rc)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index d44d3ac255aa..1cd6e88d4e81 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -4611,11 +4611,14 @@ static void bnxt_init_one_rx_agg_ring_rxbd(struct bnxt *bp,
type = ((u32)rxr->rx_page_size << RX_BD_LEN_SHIFT) |
RX_BD_TYPE_RX_AGG_BD;
- /* On P7, setting EOP will cause the chip to disable
- * Relaxed Ordering (RO) for TPA data. Disable EOP for
- * potentially higher performance with RO.
+ /* Disable EOP if TPA is enabled to prevent overlapping zero
+ * padding with the next segment's data. On P7_PLUS, EOP will
+ * automatically disable Relaxed Ordering (RO) to prevent
+ * potential data corruption (and may degrade performance). On
+ * older chips, RO will not be automatically disabled and may
+ * cause corruption.
*/
- if (BNXT_CHIP_P5_AND_MINUS(bp) || !(bp->flags & BNXT_FLAG_TPA))
+ if (!(bp->flags & BNXT_FLAG_TPA))
type |= RX_BD_FLAGS_AGG_EOP;
bnxt_init_rxbd_pages(ring, type);
@@ -6704,22 +6707,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);
@@ -6732,6 +6749,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;
@@ -6739,19 +6757,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);
@@ -6803,6 +6817,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);
@@ -6939,8 +6954,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 =
@@ -6973,13 +6989,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);
@@ -10866,6 +10876,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) {
@@ -11090,6 +11101,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,
@@ -11171,6 +11187,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",
@@ -16225,6 +16244,7 @@ static int bnxt_queue_mem_alloc(struct net_device *dev,
clone->rx_next_cons = 0;
clone->need_head_pool = false;
clone->rx_page_size = qcfg->rx_page_size;
+ clone->rx_agg_bmap = NULL;
rc = bnxt_alloc_rx_page_pool(bp, clone, rxr->page_pool->p.nid);
if (rc)
@@ -16277,6 +16297,8 @@ static int bnxt_queue_mem_alloc(struct net_device *dev,
bnxt_free_one_tpa_info(bp, clone);
err_free_rx_agg_ring:
bnxt_free_ring(bp, &clone->rx_agg_ring_struct.ring_mem);
+ kfree(clone->rx_agg_bmap);
+ clone->rx_agg_bmap = NULL;
err_free_rx_ring:
bnxt_free_ring(bp, &clone->rx_ring_struct.ring_mem);
err_rxq_info_unreg:
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index fa13cee55779..8e04cc934c32 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -1334,6 +1334,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 5d41dc1bc782..b89293dfd2d1 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
@@ -495,12 +495,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 e7125b818087..d98f7e7ccab9 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -3011,8 +3011,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/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
index e3e7f2270560..02282dc86faf 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 2a6e4d17452c..13c20a8ca3b0 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -11926,12 +11926,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/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index c0508d261592..e606bfb211cb 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -3082,7 +3082,7 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring)
meta_req.tx_buffer = bi;
meta_req.meta = meta;
meta_req.used_desc = 0;
- xsk_tx_metadata_request(meta, &igc_xsk_tx_metadata_ops,
+ xsk_tx_metadata_request(pool, &meta, &igc_xsk_tx_metadata_ops,
&meta_req);
/* xsk_tx_metadata_request() may have updated next_to_use */
@@ -7585,11 +7585,13 @@ static int __igc_resume(struct device *dev, bool rpm)
err = __igc_open(netdev, true);
if (!rpm)
rtnl_unlock();
- if (!err)
- netif_device_attach(netdev);
+ if (err)
+ return err;
}
- return err;
+ netif_device_attach(netdev);
+
+ return 0;
}
static int igc_resume(struct device *dev)
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
index 0b46ec29e64e..039fd47ebf52 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
@@ -54,10 +54,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 2989a77e3b42..1ad0e62a8433 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 adcc73e2a5b3..1493d8106c1a 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_obj(*tracer);
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.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index 6867a5aed42c..822859c360e7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -483,7 +483,7 @@ typedef int (*mlx5e_fp_xmit_xdp_frame_check)(struct mlx5e_xdpsq *);
typedef bool (*mlx5e_fp_xmit_xdp_frame)(struct mlx5e_xdpsq *,
struct mlx5e_xmit_data *,
int,
- struct xsk_tx_metadata *);
+ struct xsk_tx_metadata **);
struct mlx5e_xdpsq {
/* data path */
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/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
index d8c7cb8837d7..77ea51bfbaae 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
@@ -452,11 +452,11 @@ INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check_mpwqe(struct mlx5e_xdpsq
INDIRECT_CALLABLE_SCOPE bool
mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd,
- int check_result, struct xsk_tx_metadata *meta);
+ int check_result, struct xsk_tx_metadata **meta);
INDIRECT_CALLABLE_SCOPE bool
mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd,
- int check_result, struct xsk_tx_metadata *meta)
+ int check_result, struct xsk_tx_metadata **meta)
{
struct mlx5e_tx_mpwqe *session = &sq->mpwqe;
struct mlx5e_xdpsq_stats *stats = sq->stats;
@@ -504,7 +504,10 @@ mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptx
* and it's safe to complete it at any time.
*/
mlx5e_xdp_mpwqe_session_start(sq);
- xsk_tx_metadata_request(meta, &mlx5e_xsk_tx_metadata_ops, &session->wqe->eth);
+ if (meta)
+ xsk_tx_metadata_request(sq->xsk_pool, meta,
+ &mlx5e_xsk_tx_metadata_ops,
+ &session->wqe->eth);
}
mlx5e_xdp_mpwqe_add_dseg(sq, p, stats);
@@ -535,7 +538,7 @@ INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check(struct mlx5e_xdpsq *sq)
INDIRECT_CALLABLE_SCOPE bool
mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd,
- int check_result, struct xsk_tx_metadata *meta)
+ int check_result, struct xsk_tx_metadata **meta)
{
struct mlx5e_xmit_data_frags *xdptxdf =
container_of(xdptxd, struct mlx5e_xmit_data_frags, xd);
@@ -649,7 +652,9 @@ mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd,
sq->pc += num_wqebbs;
- xsk_tx_metadata_request(meta, &mlx5e_xsk_tx_metadata_ops, eseg);
+ if (meta)
+ xsk_tx_metadata_request(sq->xsk_pool, meta,
+ &mlx5e_xsk_tx_metadata_ops, eseg);
sq->doorbell_cseg = cseg;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h
index 3c54f8962664..5b8f4094d553 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h
@@ -114,11 +114,11 @@ extern const struct xsk_tx_metadata_ops mlx5e_xsk_tx_metadata_ops;
INDIRECT_CALLABLE_DECLARE(bool mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq,
struct mlx5e_xmit_data *xdptxd,
int check_result,
- struct xsk_tx_metadata *meta));
+ struct xsk_tx_metadata **meta));
INDIRECT_CALLABLE_DECLARE(bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq,
struct mlx5e_xmit_data *xdptxd,
int check_result,
- struct xsk_tx_metadata *meta));
+ struct xsk_tx_metadata **meta));
INDIRECT_CALLABLE_DECLARE(int mlx5e_xmit_xdp_frame_check_mpwqe(struct mlx5e_xdpsq *sq));
INDIRECT_CALLABLE_DECLARE(int mlx5e_xmit_xdp_frame_check(struct mlx5e_xdpsq *sq));
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c
index 8aeab4b21035..3d19dad8f868 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c
@@ -105,7 +105,7 @@ bool mlx5e_xsk_tx(struct mlx5e_xdpsq *sq, unsigned int budget)
ret = INDIRECT_CALL_2(sq->xmit_xdp_frame, mlx5e_xmit_xdp_frame_mpwqe,
mlx5e_xmit_xdp_frame, sq, &xdptxd,
- check_result, meta);
+ check_result, &meta);
if (unlikely(!ret)) {
if (sq->mpwqe.wqe)
mlx5e_xdp_mpwqe_complete(sq);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 7d1063c7bf64..22ae49af38cd 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -1939,8 +1939,10 @@ int mlx5e_open_txqsq(struct mlx5e_channel *c, u32 tisn, int txq_ix,
void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq)
{
sq->txq = netdev_get_tx_queue(sq->netdev, sq->txq_ix);
+ /* Reset BQL only when the SQ has no bytes in flight. */
+ if (sq->cc == sq->pc)
+ netdev_tx_reset_queue(sq->txq);
set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
- netdev_tx_reset_queue(sq->txq);
netif_tx_start_queue(sq->txq);
netif_queue_set_napi(sq->netdev, sq->txq_ix, NETDEV_QUEUE_TYPE_TX, sq->cq.napi);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index 8b5a421a22a3..f3d4586e09e3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -2165,7 +2165,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/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index f28367fbcaf8..84e783f87b3d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2748,8 +2748,8 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
meta_req.set_ic = &set_ic;
meta_req.tbs = tx_q->tbs;
meta_req.edesc = &tx_q->dma_entx[entry];
- xsk_tx_metadata_request(meta, &stmmac_xsk_tx_metadata_ops,
- &meta_req);
+ xsk_tx_metadata_request(pool, &meta,
+ &stmmac_xsk_tx_metadata_ops, &meta_req);
if (set_ic) {
tx_q->tx_count_frames = 0;
stmmac_set_tx_ic(priv, tx_desc);
@@ -4132,6 +4132,15 @@ static int __stmmac_open(struct net_device *dev,
dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs;
memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf));
+ /* The PHY is suspended when the interface is reopened without
+ * disconnecting the PHY, e.g. on MTU change. IEEE 802.3 allows PHYs
+ * to stop their receive clock while powered down, but the DMA
+ * software reset in stmmac_hw_setup() requires a running receive
+ * clock, and phylink_start() below resumes the PHY only after the
+ * hardware setup. Resume a suspended PHY here first.
+ */
+ phylink_prepare_resume(priv->phylink);
+
stmmac_reset_queues_param(priv);
ret = stmmac_hw_setup(dev);
diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c
index c3a6ba96fc8a..029a4a532a10 100644
--- a/drivers/net/ntb_netdev.c
+++ b/drivers/net/ntb_netdev.c
@@ -127,8 +127,8 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
{
struct ntb_netdev_queue *q = qp_data;
struct ntb_netdev *dev = q->ntdev;
+ struct sk_buff *skb, *new_skb;
struct net_device *ndev;
- struct sk_buff *skb;
int rc;
ndev = dev->ndev;
@@ -144,6 +144,12 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
goto enqueue_again;
}
+ new_skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN);
+ if (!new_skb) {
+ ndev->stats.rx_dropped++;
+ goto enqueue_again;
+ }
+
skb_put(skb, len);
skb->protocol = eth_type_trans(skb, ndev);
skb->ip_summed = CHECKSUM_NONE;
@@ -157,12 +163,7 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
ndev->stats.rx_bytes += len;
}
- skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN);
- if (!skb) {
- ndev->stats.rx_errors++;
- ndev->stats.rx_frame_errors++;
- return;
- }
+ skb = new_skb;
enqueue_again:
rc = ntb_transport_rx_enqueue(qp, skb, skb->data, ndev->mtu + ETH_HLEN);
diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c
index 9993c1dfe471..168cfe9b59a9 100644
--- a/drivers/net/ovpn/main.c
+++ b/drivers/net/ovpn/main.c
@@ -35,25 +35,11 @@ static void ovpn_priv_free(struct net_device *net)
static int ovpn_mp_alloc(struct ovpn_priv *ovpn)
{
- struct in_device *dev_v4;
int i;
if (ovpn->mode != OVPN_MODE_MP)
return 0;
- dev_v4 = __in_dev_get_rtnl(ovpn->dev);
- if (dev_v4) {
- /* disable redirects as Linux gets confused by ovpn
- * handling same-LAN routing.
- * This happens because a multipeer interface is used as
- * relay point between hosts in the same subnet, while
- * in a classic LAN this would not be needed because the
- * two hosts would be able to talk directly.
- */
- IN_DEV_CONF_SET(dev_v4, SEND_REDIRECTS, false);
- IPV4_DEVCONF_ALL(dev_net(ovpn->dev), SEND_REDIRECTS) = false;
- }
-
/* the peer container is fairly large, therefore we allocate it only in
* MP mode
*/
@@ -97,9 +83,38 @@ static void ovpn_net_uninit(struct net_device *dev)
gro_cells_destroy(&ovpn->gro_cells);
}
+static int ovpn_net_open(struct net_device *dev)
+{
+ struct ovpn_priv *ovpn = netdev_priv(dev);
+ struct in_device *dev_v4;
+
+ /* the IPv4 in_device (and thus its config) is recreated whenever the
+ * interface is moved to a new netns, so redirects must be disabled on
+ * every bring-up rather than once at creation time, otherwise the
+ * setting is silently lost after such a move
+ */
+ if (ovpn->mode == OVPN_MODE_MP) {
+ dev_v4 = __in_dev_get_rtnl(dev);
+ if (dev_v4) {
+ /* disable redirects as Linux gets confused by ovpn
+ * handling same-LAN routing.
+ * This happens because a multipeer interface is used as
+ * relay point between hosts in the same subnet, while
+ * in a classic LAN this would not be needed because the
+ * two hosts would be able to talk directly.
+ */
+ IN_DEV_CONF_SET(dev_v4, SEND_REDIRECTS, false);
+ IPV4_DEVCONF_ALL(dev_net(dev), SEND_REDIRECTS) = false;
+ }
+ }
+
+ return 0;
+}
+
static const struct net_device_ops ovpn_netdev_ops = {
.ndo_init = ovpn_net_init,
.ndo_uninit = ovpn_net_uninit,
+ .ndo_open = ovpn_net_open,
.ndo_start_xmit = ovpn_net_xmit,
};
@@ -183,6 +198,7 @@ static int ovpn_newlink(struct net_device *dev,
struct ovpn_priv *ovpn = netdev_priv(dev);
struct nlattr **data = params->data;
enum ovpn_mode mode = OVPN_MODE_P2P;
+ int ret;
if (data && data[IFLA_OVPN_MODE]) {
mode = nla_get_u8(data[IFLA_OVPN_MODE]);
@@ -207,7 +223,17 @@ static int ovpn_newlink(struct net_device *dev,
else
netif_carrier_off(dev);
- return register_netdevice(dev);
+ ret = register_netdevice(dev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static size_t ovpn_get_size(const struct net_device *dev)
+{
+ /* IFLA_OVPN_MODE */
+ return nla_total_size(sizeof(u8));
}
static int ovpn_fill_info(struct sk_buff *skb, const struct net_device *dev)
@@ -228,13 +254,17 @@ static struct rtnl_link_ops ovpn_link_ops = {
.policy = ovpn_policy,
.maxtype = IFLA_OVPN_MAX,
.newlink = ovpn_newlink,
+ .get_size = ovpn_get_size,
.fill_info = ovpn_fill_info,
};
static int __init ovpn_init(void)
{
- int err = rtnl_link_register(&ovpn_link_ops);
+ int err;
+
+ ovpn_tcp_init();
+ err = rtnl_link_register(&ovpn_link_ops);
if (err) {
pr_err("ovpn: can't register rtnl link ops: %d\n", err);
return err;
@@ -246,8 +276,6 @@ static int __init ovpn_init(void)
goto unreg_rtnl;
}
- ovpn_tcp_init();
-
return 0;
unreg_rtnl:
diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c
index 4c66c1ec497e..4dad85294198 100644
--- a/drivers/net/ovpn/netlink.c
+++ b/drivers/net/ovpn/netlink.c
@@ -534,6 +534,12 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info)
*/
if (ret > 0)
ovpn_peer_hash_vpn_ip(peer);
+ /* if the remote endpoint was updated, the by_transp_addr hash bucket
+ * also needs to be refreshed, otherwise incoming packets from the new
+ * remote address would fail the lockless lookup
+ */
+ if (attrs[OVPN_A_PEER_REMOTE_IPV4] || attrs[OVPN_A_PEER_REMOTE_IPV6])
+ ovpn_peer_hash_transp_addr(peer);
spin_unlock_bh(&ovpn->lock);
ovpn_peer_put(peer);
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index a21d02ac715e..b0519f9840d8 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -189,6 +189,9 @@ int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer,
&(*__tbl1)[ovpn_get_hash_slot(*__tbl1, _key, _key_len)];\
})
+static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer,
+ const struct ovpn_bind *bind);
+
/**
* ovpn_peer_endpoints_update - update remote or local endpoint for peer
* @peer: peer to update the remote endpoint for
@@ -196,7 +199,6 @@ int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer,
*/
void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
{
- struct hlist_nulls_head *nhead;
struct sockaddr_storage ss;
struct sockaddr_in6 *sa6;
bool reset_cache = false;
@@ -220,9 +222,16 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
*/
local_ip = &ip_hdr(skb)->daddr;
sa = (struct sockaddr_in *)&ss;
- sa->sin_family = AF_INET;
- sa->sin_addr.s_addr = ip_hdr(skb)->saddr;
- sa->sin_port = udp_hdr(skb)->source;
+ /* use a designated initializer so the sin_zero padding
+ * is zeroed (it ends up in the by_transp_addr hash key)
+ * without memset-ing the whole sockaddr_storage on the
+ * RX fast path
+ */
+ *sa = (struct sockaddr_in) {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = ip_hdr(skb)->saddr,
+ .sin_port = udp_hdr(skb)->source,
+ };
salen = sizeof(*sa);
reset_cache = true;
break;
@@ -248,11 +257,19 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
*/
local_ip = &ipv6_hdr(skb)->daddr;
sa6 = (struct sockaddr_in6 *)&ss;
- sa6->sin6_family = AF_INET6;
- sa6->sin6_addr = ipv6_hdr(skb)->saddr;
- sa6->sin6_port = udp_hdr(skb)->source;
- sa6->sin6_scope_id = ipv6_iface_scope_id(&ipv6_hdr(skb)->saddr,
- skb->skb_iif);
+ /* use a designated initializer so the sin6_flowinfo
+ * padding is zeroed (it ends up in the by_transp_addr
+ * hash key) without memset-ing the whole
+ * sockaddr_storage on the RX fast path
+ */
+ *sa6 = (struct sockaddr_in6) {
+ .sin6_family = AF_INET6,
+ .sin6_addr = ipv6_hdr(skb)->saddr,
+ .sin6_port = udp_hdr(skb)->source,
+ .sin6_scope_id =
+ ipv6_iface_scope_id(&ipv6_hdr(skb)->saddr,
+ skb->skb_iif),
+ };
salen = sizeof(*sa6);
reset_cache = true;
break;
@@ -295,42 +312,25 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
ovpn_nl_peer_float_notify(peer, &ss);
/* rehashing is required only in MP mode as P2P has one peer
- * only and thus there is no hashtable
+ * only and thus there is no hashtable.
+ *
+ * This function may be invoked concurrently, so re-read peer->bind
+ * under the proper locks and rehash against its current value.
*/
- if (peer->ovpn->mode == OVPN_MODE_MP) {
- spin_lock_bh(&peer->ovpn->lock);
- spin_lock_bh(&peer->lock);
- bind = rcu_dereference_protected(peer->bind,
- lockdep_is_held(&peer->lock));
- if (unlikely(!bind)) {
- spin_unlock_bh(&peer->lock);
- spin_unlock_bh(&peer->ovpn->lock);
- return;
- }
-
- /* This function may be invoked concurrently, therefore another
- * float may have happened in parallel: perform rehashing
- * using the peer->bind->remote directly as key
- */
-
- switch (bind->remote.in4.sin_family) {
- case AF_INET:
- salen = sizeof(*sa);
- break;
- case AF_INET6:
- salen = sizeof(*sa6);
- break;
- }
+ if (peer->ovpn->mode != OVPN_MODE_MP)
+ return;
- /* remove old hashing */
- hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr);
- /* re-add with new transport address */
- nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr,
- &bind->remote, salen);
- hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead);
- spin_unlock_bh(&peer->lock);
- spin_unlock_bh(&peer->ovpn->lock);
- }
+ /* This function may be invoked concurrently, therefore another
+ * float may have happened in parallel: re-acquire the locks and
+ * rehash using the peer->bind->remote directly as key
+ */
+ spin_lock_bh(&peer->ovpn->lock);
+ spin_lock_bh(&peer->lock);
+ bind = rcu_dereference_protected(peer->bind,
+ lockdep_is_held(&peer->lock));
+ __ovpn_peer_hash_transp_addr(peer, bind);
+ spin_unlock_bh(&peer->lock);
+ spin_unlock_bh(&peer->ovpn->lock);
return;
unlock:
spin_unlock_bh(&peer->lock);
@@ -896,6 +896,83 @@ bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb,
return match;
}
+/* Move @peer to the by_transp_addr bucket matching its current bind.
+ *
+ * Caller must hold both peer->ovpn->lock and peer->lock, and must have
+ * already dereferenced a valid (non-NULL) peer->bind, passed in as @bind.
+ */
+static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer,
+ const struct ovpn_bind *bind)
+{
+ struct sockaddr_storage sa = {};
+ struct hlist_nulls_head *nhead;
+ struct sockaddr_in6 *sa6;
+ struct sockaddr_in *sa4;
+ size_t salen;
+
+ lockdep_assert_held(&peer->ovpn->lock);
+ lockdep_assert_held(&peer->lock);
+
+ if (WARN_ON_ONCE(!bind))
+ return;
+
+ /* peer may have been concurrently removed between the caller's
+ * initial lookup and our acquisition of ovpn->lock; skip the
+ * rehash so we don't re-insert a removed peer
+ */
+ if (unlikely(hlist_unhashed(&peer->hash_entry_id)))
+ return;
+
+ /* Build the hash key from the transport identity only
+ * (family/address/port), matching ovpn_peer_add_mp() and the lookup
+ * in ovpn_peer_get_by_transp_addr(). Hashing bind->remote directly
+ * would fold in sin6_scope_id (set on the float path but never by the
+ * lookup), scattering the peer into a bucket lookups cannot reach.
+ */
+ switch (bind->remote.in4.sin_family) {
+ case AF_INET:
+ sa4 = (struct sockaddr_in *)&sa;
+ sa4->sin_family = AF_INET;
+ sa4->sin_addr.s_addr = bind->remote.in4.sin_addr.s_addr;
+ sa4->sin_port = bind->remote.in4.sin_port;
+ salen = sizeof(*sa4);
+ break;
+ case AF_INET6:
+ sa6 = (struct sockaddr_in6 *)&sa;
+ sa6->sin6_family = AF_INET6;
+ sa6->sin6_addr = bind->remote.in6.sin6_addr;
+ sa6->sin6_port = bind->remote.in6.sin6_port;
+ salen = sizeof(*sa6);
+ break;
+ default:
+ return;
+ }
+
+ /* remove old hashing (no-op if entry is not currently linked) */
+ hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr);
+ /* re-add with current transport address */
+ nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, &sa,
+ salen);
+ hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead);
+}
+
+void ovpn_peer_hash_transp_addr(struct ovpn_peer *peer)
+{
+ struct ovpn_bind *bind;
+
+ lockdep_assert_held(&peer->ovpn->lock);
+
+ /* rehashing makes sense only in multipeer mode */
+ if (peer->ovpn->mode != OVPN_MODE_MP)
+ return;
+
+ spin_lock_bh(&peer->lock);
+ bind = rcu_dereference_protected(peer->bind,
+ lockdep_is_held(&peer->lock));
+ __ovpn_peer_hash_transp_addr(peer, bind);
+ spin_unlock_bh(&peer->lock);
+}
+
void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer)
{
struct hlist_nulls_head *nhead;
@@ -906,6 +983,13 @@ void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer)
if (peer->ovpn->mode != OVPN_MODE_MP)
return;
+ /* peer may have been concurrently removed between the caller's
+ * initial lookup and our acquisition of ovpn->lock; skip the
+ * rehash so we don't re-insert a removed peer
+ */
+ if (hlist_unhashed(&peer->hash_entry_id))
+ return;
+
if (peer->vpn_addrs.ipv4.s_addr != htonl(INADDR_ANY)) {
/* remove potential old hashing */
hlist_nulls_del_init_rcu(&peer->hash_entry_addr4);
@@ -1165,7 +1249,7 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk,
}
if (sk) {
- ovpn_sock = rcu_access_pointer(peer->sock);
+ ovpn_sock = rcu_dereference_bh(peer->sock);
if (!ovpn_sock || ovpn_sock->sk != sk) {
spin_unlock_bh(&ovpn->lock);
return;
diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h
index 86c8cffada6d..dfa5c0037e02 100644
--- a/drivers/net/ovpn/peer.h
+++ b/drivers/net/ovpn/peer.h
@@ -150,6 +150,7 @@ struct ovpn_peer *ovpn_peer_get_by_id(struct ovpn_priv *ovpn, u32 peer_id);
struct ovpn_peer *ovpn_peer_get_by_dst(struct ovpn_priv *ovpn,
struct sk_buff *skb);
void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer);
+void ovpn_peer_hash_transp_addr(struct ovpn_peer *peer);
bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb,
struct ovpn_peer *peer);
diff --git a/drivers/net/ovpn/socket.c b/drivers/net/ovpn/socket.c
index 517caa64a4fe..6cbeb2caaeec 100644
--- a/drivers/net/ovpn/socket.c
+++ b/drivers/net/ovpn/socket.c
@@ -162,6 +162,15 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
rcu_read_lock();
ovpn_sock = rcu_dereference_sk_user_data(sk);
if (ovpn_sock) {
+ /* something else filled the sk_user_data without
+ * setting the encap_type. Reject the socket.
+ */
+ if (!type) {
+ ovpn_sock = ERR_PTR(-EBUSY);
+ rcu_read_unlock();
+ goto sock_release;
+ }
+
/* socket owned by another ovpn instance, we can't use it */
if (ovpn_sock->ovpn != peer->ovpn) {
ovpn_sock = ERR_PTR(-EBUSY);
diff --git a/drivers/net/phy/mediatek/mtk.h b/drivers/net/phy/mediatek/mtk.h
index 320f76ffa81f..79e09727b81b 100644
--- a/drivers/net/phy/mediatek/mtk.h
+++ b/drivers/net/phy/mediatek/mtk.h
@@ -53,15 +53,15 @@
#define MTK_GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \
MTK_PHY_LED_BLINK_100RX | \
MTK_PHY_LED_BLINK_10RX)
-#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \
- MTK_PHY_LED_BLINK_100RX | \
- MTK_PHY_LED_BLINK_10RX)
+#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000TX | \
+ MTK_PHY_LED_BLINK_100TX | \
+ MTK_PHY_LED_BLINK_10TX)
#define MTK_2P5GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK2500 | \
MTK_GPHY_LED_ON_SET)
#define MTK_2P5GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \
MTK_GPHY_LED_RX_BLINK_SET)
-#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \
+#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500TX | \
MTK_GPHY_LED_TX_BLINK_SET)
#define MTK_PHY_LED_STATE_FORCE_ON 0
diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index fae115915c8e..5d2d34d24ce8 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -1074,10 +1074,21 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
skb_reset_mac_header(skb);
skb->protocol = eth_hdr(skb)->h_proto;
+ rcu_read_lock();
+ tap = rcu_dereference(q->tap);
+ if (!tap) {
+ kfree_skb(skb);
+ rcu_read_unlock();
+ return 0;
+ }
+ skb->dev = tap->dev;
+
if (vnet_hdr_len) {
err = tun_vnet_hdr_to_skb(q->flags, skb, gso);
- if (err)
+ if (err) {
+ rcu_read_unlock();
goto err_kfree;
+ }
}
/* Move network header to the right position for VLAN tagged packets */
@@ -1085,15 +1096,8 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0)
skb_set_network_header(skb, depth);
- rcu_read_lock();
- tap = rcu_dereference(q->tap);
- if (tap) {
- skb->dev = tap->dev;
- skb_probe_transport_header(skb);
- dev_queue_xmit(skb);
- } else {
- kfree_skb(skb);
- }
+ skb_probe_transport_header(skb);
+ dev_queue_xmit(skb);
rcu_read_unlock();
return 0;
diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c
index ac016890646c..fb569c0abf8e 100644
--- a/drivers/net/thunderbolt/main.c
+++ b/drivers/net/thunderbolt/main.c
@@ -390,11 +390,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,
@@ -403,6 +408,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;
}
@@ -929,12 +939,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;
@@ -953,6 +959,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 98f899ea2e94..81d8412ce8e2 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -1487,8 +1487,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 bb1364f85bd1..2b490114d232 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;
}
@@ -629,6 +634,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/usb/usbnet.c b/drivers/net/usb/usbnet.c
index b72ba0803392..6b5f44cbb561 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1798,7 +1798,7 @@ usbnet_probe(struct usb_interface *udev, const struct usb_device_id *prod)
*/
dev->hard_mtu = net->mtu + net->hard_header_len;
net->min_mtu = 0;
- net->max_mtu = ETH_MAX_MTU;
+ net->max_mtu = net->mtu;
net->netdev_ops = &usbnet_netdev_ops;
net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
@@ -1808,6 +1808,7 @@ usbnet_probe(struct usb_interface *udev, const struct usb_device_id *prod)
// allow device-specific bind/init procedures
// NOTE net->name still not usable ...
if (info->bind) {
+ net->max_mtu = ETH_MAX_MTU;
status = info->bind(dev, udev);
if (status < 0)
goto out1;
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 00e34afd858e..f80f6584a6ff 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 32613e40b3ba..608087f78d02 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -4501,7 +4501,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/apple-spmi-nvmem.c b/drivers/nvmem/apple-spmi-nvmem.c
index 88614005d5ce..7acb0c07d6ab 100644
--- a/drivers/nvmem/apple-spmi-nvmem.c
+++ b/drivers/nvmem/apple-spmi-nvmem.c
@@ -18,6 +18,22 @@ static const struct regmap_config apple_spmi_regmap_config = {
.max_register = 0xffff,
};
+static int apple_spmi_nvmem_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct regmap *map = priv;
+
+ return regmap_bulk_read(map, offset, val, bytes);
+}
+
+static int apple_spmi_nvmem_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct regmap *map = priv;
+
+ return regmap_bulk_write(map, offset, val, bytes);
+}
+
static int apple_spmi_nvmem_probe(struct spmi_device *sdev)
{
struct regmap *regmap;
@@ -28,8 +44,8 @@ static int apple_spmi_nvmem_probe(struct spmi_device *sdev)
.word_size = 1,
.stride = 1,
.size = 0xffff,
- .reg_read = (void *)regmap_bulk_read,
- .reg_write = (void *)regmap_bulk_write,
+ .reg_read = apple_spmi_nvmem_read,
+ .reg_write = apple_spmi_nvmem_write,
};
regmap = devm_regmap_init_spmi_ext(sdev, &apple_spmi_regmap_config);
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index e871181751f3..2f971b059704 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -786,7 +786,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;
const __be32 *addr;
@@ -834,29 +834,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;
@@ -1005,10 +989,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 b90584e1b99e..07a34be9669c 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/qcom/pinctrl-ipq8064.c b/drivers/pinctrl/qcom/pinctrl-ipq8064.c
index 0a9e357e64c6..f35dbe07ab6f 100644
--- a/drivers/pinctrl/qcom/pinctrl-ipq8064.c
+++ b/drivers/pinctrl/qcom/pinctrl-ipq8064.c
@@ -480,7 +480,7 @@ static const char * const ps_hold_groups[] = {
};
static const struct pinfunction ipq8064_functions[] = {
- IPQ_PIN_FUNCTION(gpio),
+ IPQ_GPIO_PIN_FUNCTION(gpio),
IPQ_PIN_FUNCTION(mdio),
IPQ_PIN_FUNCTION(ssbi),
IPQ_PIN_FUNCTION(spmi),
@@ -507,19 +507,19 @@ static const struct pinfunction ipq8064_functions[] = {
IPQ_PIN_FUNCTION(usb2_hsic),
IPQ_PIN_FUNCTION(rgmii2),
IPQ_PIN_FUNCTION(sata),
- IPQ_PIN_FUNCTION(pcie1_rst),
+ IPQ_GPIO_PIN_FUNCTION(pcie1_rst),
IPQ_PIN_FUNCTION(pcie1_prsnt),
IPQ_PIN_FUNCTION(pcie1_pwren_n),
IPQ_PIN_FUNCTION(pcie1_pwren),
IPQ_PIN_FUNCTION(pcie1_pwrflt),
IPQ_PIN_FUNCTION(pcie1_clk_req),
- IPQ_PIN_FUNCTION(pcie2_rst),
+ IPQ_GPIO_PIN_FUNCTION(pcie2_rst),
IPQ_PIN_FUNCTION(pcie2_prsnt),
IPQ_PIN_FUNCTION(pcie2_pwren_n),
IPQ_PIN_FUNCTION(pcie2_pwren),
IPQ_PIN_FUNCTION(pcie2_pwrflt),
IPQ_PIN_FUNCTION(pcie2_clk_req),
- IPQ_PIN_FUNCTION(pcie3_rst),
+ IPQ_GPIO_PIN_FUNCTION(pcie3_rst),
IPQ_PIN_FUNCTION(pcie3_prsnt),
IPQ_PIN_FUNCTION(pcie3_pwren_n),
IPQ_PIN_FUNCTION(pcie3_pwren),
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h
index 4fbff61de6bb..b94ba1a4177e 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.h
+++ b/drivers/pinctrl/qcom/pinctrl-msm.h
@@ -24,6 +24,11 @@ struct pinctrl_pin_desc;
fname##_groups, \
ARRAY_SIZE(fname##_groups))
+#define IPQ_GPIO_PIN_FUNCTION(fname) \
+ [IPQ_MUX_##fname] = PINCTRL_GPIO_PINFUNCTION(#fname, \
+ fname##_groups, \
+ ARRAY_SIZE(fname##_groups))
+
#define MSM_PIN_FUNCTION(fname) \
[msm_mux_##fname] = PINCTRL_PINFUNCTION(#fname, \
fname##_groups, \
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 735385539b9f..a93633c450b5 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -2180,9 +2180,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/net/ism_drv.c b/drivers/s390/net/ism_drv.c
index 7d0479e4e095..369aa21b1ff7 100644
--- a/drivers/s390/net/ism_drv.c
+++ b/drivers/s390/net/ism_drv.c
@@ -148,13 +148,16 @@ static int unregister_sba(struct ism_dev *ism)
if (ret && ret != ISM_ERROR)
return -EIO;
+ return 0;
+}
+
+static void ism_free_sba(struct ism_dev *ism)
+{
dma_free_coherent(&ism->pdev->dev, PAGE_SIZE,
ism->sba, ism->sba_dma_addr);
ism->sba = NULL;
ism->sba_dma_addr = 0;
-
- return 0;
}
static int unregister_ieq(struct ism_dev *ism)
@@ -168,13 +171,16 @@ static int unregister_ieq(struct ism_dev *ism)
if (ret && ret != ISM_ERROR)
return -EIO;
+ return 0;
+}
+
+static void ism_free_ieq(struct ism_dev *ism)
+{
dma_free_coherent(&ism->pdev->dev, PAGE_SIZE,
ism->ieq, ism->ieq_dma_addr);
ism->ieq = NULL;
ism->ieq_dma_addr = 0;
-
- return 0;
}
static int ism_read_local_gid(struct dibs_dev *dibs)
@@ -573,6 +579,7 @@ static int ism_dev_init(struct ism_dev *ism)
unreg_sba:
unregister_sba(ism);
+ ism_free_sba(ism);
free_irq:
free_irq(pci_irq_vector(pdev, 0), ism);
free_vectors:
@@ -585,9 +592,13 @@ static void ism_dev_exit(struct ism_dev *ism)
{
struct pci_dev *pdev = ism->pdev;
+ /* ism will only generate new IRQs while ieq & sba are registered */
unregister_ieq(ism);
unregister_sba(ism);
+ /* drain ongoing irpt handlers */
free_irq(pci_irq_vector(pdev, 0), ism);
+ ism_free_ieq(ism);
+ ism_free_sba(ism);
pci_free_irq_vectors(pdev);
}
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 615618aa11f4..87975d3d1d08 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -4318,8 +4318,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/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
index b35d74592964..b5188dcde37a 100644
--- a/drivers/soc/aspeed/Makefile
+++ b/drivers/soc/aspeed/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o
obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o
+CONTEXT_ANALYSIS_aspeed-lpc-snoop.o := y
diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
index b03310c0830d..7fa1a345acac 100644
--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
@@ -11,6 +11,7 @@
*/
#include <linux/bitops.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/dev_printk.h>
#include <linux/interrupt.h>
@@ -74,7 +75,8 @@ struct aspeed_lpc_snoop_channel_cfg {
struct aspeed_lpc_snoop_channel {
const struct aspeed_lpc_snoop_channel_cfg *cfg;
bool enabled;
- struct kfifo fifo;
+ spinlock_t lock;
+ struct kfifo fifo __guarded_by(&lock);
wait_queue_head_t wq;
struct miscdevice miscdev;
};
@@ -114,6 +116,7 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file);
+ u8 *buf __free(kfree) = NULL;
unsigned int copied;
int ret = 0;
@@ -125,9 +128,16 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
if (ret == -ERESTARTSYS)
return -EINTR;
}
- ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
- if (ret)
- return ret;
+
+ count = min_t(size_t, count, SNOOP_FIFO_SIZE);
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ copied = kfifo_out_spinlocked(&chan->fifo, buf, count, &chan->lock);
+ if (copied && copy_to_user(buffer, buf, copied))
+ return -EFAULT;
return copied;
}
@@ -151,11 +161,13 @@ static const struct file_operations snoop_fops = {
/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */
static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
{
- if (!kfifo_initialized(&chan->fifo))
- return;
- if (kfifo_is_full(&chan->fifo))
- kfifo_skip(&chan->fifo);
- kfifo_put(&chan->fifo, val);
+ scoped_guard(spinlock, &chan->lock) {
+ if (!kfifo_initialized(&chan->fifo))
+ return;
+ if (kfifo_is_full(&chan->fifo))
+ kfifo_skip(&chan->fifo);
+ kfifo_put(&chan->fifo, val);
+ }
wake_up_interruptible(&chan->wq);
}
@@ -239,9 +251,11 @@ static int aspeed_lpc_enable_snoop(struct device *dev,
if (!channel->miscdev.name)
return -ENOMEM;
- rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL);
- if (rc)
- return rc;
+ scoped_guard(spinlock_init, &channel->lock) {
+ rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL);
+ if (rc)
+ return rc;
+ }
rc = misc_register(&channel->miscdev);
if (rc)
diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
index 82ccdec2f365..a0dd19cb404f 100644
--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
@@ -371,6 +371,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 e78450402d19..812edc4fe37c 100644
--- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
+++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
@@ -897,7 +897,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(pmlmeinfo->chg_txt, p + 2, len);
diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c
index 94bbe7ac13ac..119b61baa475 100644
--- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c
+++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c
@@ -693,6 +693,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;
diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
index a0ef1e7e50a0..e65932dc6585 100644
--- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
+++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
@@ -2032,6 +2032,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);
@@ -2044,6 +2046,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 223ae1571655..ec73d03a1e60 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -19,33 +19,30 @@
#include "thermal_hwmon.h"
#include "thermal_core.h"
-/*
- * Needs to be large enough to hold a thermal zone type string followed by an
- * underline character and a 32-bit integer in decimal representation.
- */
-#define THERMAL_HWMON_NAME_LENGTH (THERMAL_NAME_LENGTH + 11)
+/* hwmon sys I/F */
+/* thermal zone devices with the same type share one hwmon device */
+struct thermal_hwmon_device {
+ char type[THERMAL_NAME_LENGTH];
+ struct device *device;
+ int count;
+ struct list_head tz_list;
+ struct list_head node;
+};
struct thermal_hwmon_attr {
struct device_attribute attr;
+ char name[16];
};
/* one temperature input for each thermal zone */
struct thermal_hwmon_temp {
+ struct list_head hwmon_node;
struct thermal_zone_device *tz;
struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
bool temp_crit_present;
};
-/* hwmon sys I/F */
-/* thermal zone devices with the same type share one hwmon device */
-struct thermal_hwmon_device {
- char name[THERMAL_HWMON_NAME_LENGTH];
- struct device *device;
- struct list_head node;
- struct thermal_hwmon_temp tz_temp;
-};
-
static LIST_HEAD(thermal_hwmon_list);
static DEFINE_MUTEX(thermal_hwmon_list_lock);
@@ -91,6 +88,45 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
return sysfs_emit(buf, "%d\n", temperature);
}
+
+static struct thermal_hwmon_device *
+thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
+{
+ struct thermal_hwmon_device *hwmon;
+ char type[THERMAL_NAME_LENGTH];
+
+ mutex_lock(&thermal_hwmon_list_lock);
+ list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
+ strscpy(type, tz->type);
+ strreplace(type, '-', '_');
+ if (!strcmp(hwmon->type, type)) {
+ mutex_unlock(&thermal_hwmon_list_lock);
+ return hwmon;
+ }
+ }
+ mutex_unlock(&thermal_hwmon_list_lock);
+
+ return NULL;
+}
+
+/* Find the temperature input matching a given thermal zone */
+static struct thermal_hwmon_temp *
+thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
+ const struct thermal_zone_device *tz)
+{
+ struct thermal_hwmon_temp *temp;
+
+ mutex_lock(&thermal_hwmon_list_lock);
+ list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
+ if (temp->tz == tz) {
+ mutex_unlock(&thermal_hwmon_list_lock);
+ return temp;
+ }
+ mutex_unlock(&thermal_hwmon_list_lock);
+
+ return NULL;
+}
+
static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz)
{
int temp;
@@ -101,39 +137,54 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
{
struct thermal_hwmon_device *hwmon;
struct thermal_hwmon_temp *temp;
+ int new_hwmon_device = 1;
int result;
+ hwmon = thermal_hwmon_lookup_by_type(tz);
+ if (hwmon) {
+ new_hwmon_device = 0;
+ goto register_sys_interface;
+ }
+
hwmon = kzalloc_obj(*hwmon);
if (!hwmon)
return -ENOMEM;
- /*
- * Append the thermal zone ID preceded by an underline character to the
- * type to disambiguate the sensors command output.
- */
- scnprintf(hwmon->name, THERMAL_HWMON_NAME_LENGTH, "%s_%d", tz->type, tz->id);
- strreplace(hwmon->name, '-', '_');
+ INIT_LIST_HEAD(&hwmon->tz_list);
+ strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
+ strreplace(hwmon->type, '-', '_');
hwmon->device = hwmon_device_register_for_thermal(&tz->device,
- hwmon->name, hwmon);
+ hwmon->type, hwmon);
if (IS_ERR(hwmon->device)) {
result = PTR_ERR(hwmon->device);
goto free_mem;
}
- temp = &hwmon->tz_temp;
+ register_sys_interface:
+ temp = kzalloc_obj(*temp);
+ if (!temp) {
+ result = -ENOMEM;
+ goto unregister_name;
+ }
temp->tz = tz;
+ hwmon->count++;
- temp->temp_input.attr.attr.name = "temp1_input";
+ snprintf(temp->temp_input.name, sizeof(temp->temp_input.name),
+ "temp%d_input", hwmon->count);
+ temp->temp_input.attr.attr.name = temp->temp_input.name;
temp->temp_input.attr.attr.mode = 0444;
temp->temp_input.attr.show = temp_input_show;
sysfs_attr_init(&temp->temp_input.attr.attr);
result = device_create_file(hwmon->device, &temp->temp_input.attr);
if (result)
- goto unregister_name;
+ goto free_temp_mem;
if (thermal_zone_crit_temp_valid(tz)) {
- temp->temp_crit.attr.attr.name = "temp1_crit";
+ snprintf(temp->temp_crit.name,
+ sizeof(temp->temp_crit.name),
+ "temp%d_crit", hwmon->count);
+ temp->temp_crit.attr.attr.name = temp->temp_crit.name;
temp->temp_crit.attr.attr.mode = 0444;
temp->temp_crit.attr.show = temp_crit_show;
sysfs_attr_init(&temp->temp_crit.attr.attr);
@@ -145,55 +196,62 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
temp->temp_crit_present = true;
}
- /* The list is needed for hwmon lookup during removal. */
mutex_lock(&thermal_hwmon_list_lock);
- list_add_tail(&hwmon->node, &thermal_hwmon_list);
+ if (new_hwmon_device)
+ list_add_tail(&hwmon->node, &thermal_hwmon_list);
+ list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
mutex_unlock(&thermal_hwmon_list_lock);
return 0;
unregister_input:
device_remove_file(hwmon->device, &temp->temp_input.attr);
+ free_temp_mem:
+ kfree(temp);
unregister_name:
- hwmon_device_unregister(hwmon->device);
+ if (new_hwmon_device)
+ hwmon_device_unregister(hwmon->device);
free_mem:
- kfree(hwmon);
+ if (new_hwmon_device)
+ kfree(hwmon);
return result;
}
EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs);
-static struct thermal_hwmon_device *
-thermal_hwmon_lookup(const struct thermal_zone_device *tz)
-{
- struct thermal_hwmon_device *hwmon;
-
- list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
- if (hwmon->tz_temp.tz == tz)
- return hwmon;
- }
- return NULL;
-}
-
void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
{
struct thermal_hwmon_device *hwmon;
struct thermal_hwmon_temp *temp;
- scoped_guard(mutex, &thermal_hwmon_list_lock) {
- hwmon = thermal_hwmon_lookup(tz);
- if (!hwmon)
- return;
-
- list_del(&hwmon->node);
+ hwmon = thermal_hwmon_lookup_by_type(tz);
+ if (unlikely(!hwmon)) {
+ /* Should never happen... */
+ dev_dbg(&tz->device, "hwmon device lookup failed!\n");
+ return;
}
- temp = &hwmon->tz_temp;
+ temp = thermal_hwmon_lookup_temp(hwmon, tz);
+ if (unlikely(!temp)) {
+ /* Should never happen... */
+ dev_dbg(&tz->device, "temperature input lookup failed!\n");
+ return;
+ }
device_remove_file(hwmon->device, &temp->temp_input.attr);
if (temp->temp_crit_present)
device_remove_file(hwmon->device, &temp->temp_crit.attr);
+ mutex_lock(&thermal_hwmon_list_lock);
+ list_del(&temp->hwmon_node);
+ kfree(temp);
+ if (!list_empty(&hwmon->tz_list)) {
+ mutex_unlock(&thermal_hwmon_list_lock);
+ return;
+ }
+ list_del(&hwmon->node);
+ mutex_unlock(&thermal_hwmon_list_lock);
+
hwmon_device_unregister(hwmon->device);
kfree(hwmon);
}
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c
index 5477b9437048..e50503bb20dc 100644
--- a/drivers/thunderbolt/eeprom.c
+++ b/drivers/thunderbolt/eeprom.c
@@ -394,9 +394,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 9d95bf3ab44c..136516432f56 100644
--- a/drivers/thunderbolt/icm.c
+++ b/drivers/thunderbolt/icm.c
@@ -2325,7 +2325,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 c69c323e6952..9907966556e7 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -609,7 +609,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/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
index 81644d40b09a..160f4c71de06 100644
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -81,6 +81,40 @@ static int of_platform_serial_clk_notifier_cb(struct notifier_block *nb, unsigne
return NOTIFY_DONE;
}
+static int lpc32xx_handle_irq(struct uart_port *port)
+{
+ struct uart_8250_port *up = up_to_u8250p(port);
+ unsigned int iir;
+ u16 status;
+
+ guard(serial8250_rpm)(up);
+
+ iir = serial_port_in(port, UART_IIR);
+ if (iir & UART_IIR_NO_INT)
+ return 0;
+
+ guard(uart_port_lock_check_sysrq_irqsave)(port);
+
+ /*
+ * The LPC32xx UART can assert an RX character-timeout interrupt while
+ * the RX FIFO is empty: IIR reports UART_IIR_RX_TIMEOUT but LSR.DR is
+ * clear. The timeout is only cleared by reading RHR, but the core RX
+ * path skips that read when the FIFO is empty, so the level-triggered
+ * IRQ re-fires forever and livelocks this single-core SoC. Do one
+ * throwaway RHR read to clear it; a healthy UART never reports a
+ * timeout with DR/BI clear, so no received data is ever discarded.
+ */
+ if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) {
+ status = serial_lsr_in(up);
+ if (!(status & (UART_LSR_DR | UART_LSR_BI)))
+ serial_port_in(port, UART_RX);
+ }
+
+ serial8250_handle_irq_locked(port, iir);
+
+ return 1;
+}
+
/*
* Fill a struct uart_port for a given device node
*/
@@ -173,6 +207,9 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
case PORT_NPCM:
ret = npcm_setup(port);
break;
+ case PORT_LPC3220:
+ port->handle_irq = lpc32xx_handle_irq;
+ break;
default:
/* Nothing to do */
ret = 0;
@@ -369,6 +406,7 @@ static struct platform_driver of_platform_serial_driver = {
module_platform_driver(of_platform_serial_driver);
+MODULE_IMPORT_NS("SERIAL_8250");
MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices");
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 028e37ad8d79..b863d0a328f5 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -277,6 +277,7 @@ enum pl011_rs485_tx_state {
WAIT_AFTER_RTS,
SEND,
WAIT_AFTER_SEND,
+ WAIT_AFTER_SEND_DELAY,
};
/*
@@ -1214,7 +1215,7 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap)
if (uap->using_tx_dma) {
/* In theory, this should already be done by pl011_dma_flush_buffer */
- dmaengine_terminate_all(uap->dmatx.chan);
+ dmaengine_terminate_sync(uap->dmatx.chan);
if (uap->dmatx.queued) {
dma_unmap_single(uap->dmatx.chan->device->dev,
uap->dmatx.dma, uap->dmatx.len,
@@ -1227,12 +1228,12 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap)
}
if (uap->using_rx_dma) {
- dmaengine_terminate_all(uap->dmarx.chan);
+ if (uap->dmarx.poll_rate)
+ timer_delete_sync(&uap->dmarx.timer);
+ dmaengine_terminate_sync(uap->dmarx.chan);
/* Clean up the RX DMA */
pl011_dmabuf_free(uap->dmarx.chan, &uap->dmarx.dbuf_a, DMA_FROM_DEVICE);
pl011_dmabuf_free(uap->dmarx.chan, &uap->dmarx.dbuf_b, DMA_FROM_DEVICE);
- if (uap->dmarx.poll_rate)
- timer_delete_sync(&uap->dmarx.timer);
uap->using_rx_dma = false;
}
}
@@ -1301,11 +1302,30 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
#define pl011_dma_flush_buffer NULL
#endif
-static void pl011_rs485_tx_stop(struct uart_amba_port *uap)
+static void pl011_rs485_tx_stop_now(struct uart_amba_port *uap)
{
struct uart_port *port = &uap->port;
u32 cr;
+ cr = pl011_read(uap, REG_CR);
+
+ if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ cr &= ~UART011_CR_RTS;
+ else
+ cr |= UART011_CR_RTS;
+
+ /* Disable the transmitter and reenable the transceiver */
+ cr &= ~UART011_CR_TXE;
+ cr |= UART011_CR_RXE;
+ pl011_write(cr, uap, REG_CR);
+
+ uap->rs485_tx_state = OFF;
+}
+
+static void pl011_rs485_tx_stop(struct uart_amba_port *uap)
+{
+ struct uart_port *port = &uap->port;
+
if (uap->rs485_tx_state == SEND)
uap->rs485_tx_state = WAIT_AFTER_SEND;
@@ -1318,6 +1338,7 @@ static void pl011_rs485_tx_stop(struct uart_amba_port *uap)
return;
}
if (port->rs485.delay_rts_after_send > 0) {
+ uap->rs485_tx_state = WAIT_AFTER_SEND_DELAY;
hrtimer_start(&uap->trigger_stop_tx,
ms_to_ktime(port->rs485.delay_rts_after_send),
HRTIMER_MODE_REL);
@@ -1328,19 +1349,7 @@ static void pl011_rs485_tx_stop(struct uart_amba_port *uap)
hrtimer_try_to_cancel(&uap->trigger_start_tx);
}
- cr = pl011_read(uap, REG_CR);
-
- if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
- cr &= ~UART011_CR_RTS;
- else
- cr |= UART011_CR_RTS;
-
- /* Disable the transmitter and reenable the transceiver */
- cr &= ~UART011_CR_TXE;
- cr |= UART011_CR_RXE;
- pl011_write(cr, uap, REG_CR);
-
- uap->rs485_tx_state = OFF;
+ pl011_rs485_tx_stop_now(uap);
}
static void pl011_stop_tx(struct uart_port *port)
@@ -1383,7 +1392,8 @@ static void pl011_rs485_tx_start(struct uart_amba_port *uap)
uap->rs485_tx_state = SEND;
return;
}
- if (uap->rs485_tx_state == WAIT_AFTER_SEND) {
+ if (uap->rs485_tx_state == WAIT_AFTER_SEND ||
+ uap->rs485_tx_state == WAIT_AFTER_SEND_DELAY) {
hrtimer_try_to_cancel(&uap->trigger_stop_tx);
uap->rs485_tx_state = SEND;
return;
@@ -1450,7 +1460,8 @@ static enum hrtimer_restart pl011_trigger_stop_tx(struct hrtimer *t)
unsigned long flags;
uart_port_lock_irqsave(&uap->port, &flags);
- if (uap->rs485_tx_state == WAIT_AFTER_SEND)
+ if (uap->rs485_tx_state == WAIT_AFTER_SEND ||
+ uap->rs485_tx_state == WAIT_AFTER_SEND_DELAY)
pl011_rs485_tx_stop(uap);
uart_port_unlock_irqrestore(&uap->port, flags);
@@ -2048,11 +2059,20 @@ static void pl011_shutdown(struct uart_port *port)
pl011_dma_shutdown(uap);
- if ((port->rs485.flags & SER_RS485_ENABLED && uap->rs485_tx_state != OFF))
- pl011_rs485_tx_stop(uap);
-
free_irq(uap->port.irq, uap);
+ /*
+ * free_irq() drains the UART interrupt handler, which can arm either
+ * timer. Cancel the timers afterwards to drain their callbacks too.
+ */
+ hrtimer_cancel(&uap->trigger_start_tx);
+ hrtimer_cancel(&uap->trigger_stop_tx);
+
+ uart_port_lock_irq(port);
+ if (uap->rs485_tx_state != OFF)
+ pl011_rs485_tx_stop_now(uap);
+ uart_port_unlock_irq(port);
+
pl011_disable_uart(uap);
/*
@@ -3031,6 +3051,8 @@ static void pl011_remove(struct amba_device *dev)
struct uart_amba_port *uap = amba_get_drvdata(dev);
uart_remove_one_port(&amba_reg, &uap->port);
+ hrtimer_cancel(&uap->trigger_start_tx);
+ hrtimer_cancel(&uap->trigger_stop_tx);
pl011_unregister_port(uap);
}
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 3b0f836a9822..7ae3bb207dc1 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -158,6 +158,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);
@@ -636,35 +637,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)
@@ -1180,7 +1180,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);
}
@@ -1769,7 +1769,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,
@@ -1792,6 +1792,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/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index b323d1510a67..3d776cfe1113 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -828,6 +828,9 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws)
msleep(port->rs485.delay_rts_before_send);
guard(mutex)(&one->lock);
+ sc16is7xx_port_update(port, SC16IS7XX_IER_REG,
+ SC16IS7XX_IER_THRI_BIT,
+ SC16IS7XX_IER_THRI_BIT);
sc16is7xx_handle_tx(port);
}
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 763a3f1b7be0..c41d850b29c6 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -1437,7 +1437,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. */
@@ -1497,9 +1497,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 28993a3d0acb..deb3b3f93461 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -406,6 +406,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 f1900c567ba4..429ac20a8999 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 6b3815f8a6e5..5c712bff1d2b 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.c
+++ b/drivers/usb/cdns3/cdnsp-gadget.c
@@ -155,9 +155,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/core/config.c b/drivers/usb/core/config.c
index 45e20c6d76c0..346a2faa9bb8 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -912,6 +912,18 @@ int usb_get_configuration(struct usb_device *dev)
unsigned char *bigbuffer;
struct usb_config_descriptor *desc;
int result;
+ size_t usb_config_req_size;
+
+ /*
+ * We usually start by grabbing the first 9-bytes descriptor so we know
+ * how long the whole configuration is. Some devices with quirky
+ * firmware will fail enumeration, so if the quirk is set, use 255 instead,
+ * mirroring the behavior of Windows.
+ */
+ if (dev->quirks & USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE)
+ usb_config_req_size = 255;
+ else
+ usb_config_req_size = USB_DT_CONFIG_SIZE;
if (ncfg > USB_MAXCONFIG) {
dev_notice(ddev, "too many configurations: %d, "
@@ -938,15 +950,13 @@ int usb_get_configuration(struct usb_device *dev)
if (!dev->rawdescriptors)
return -ENOMEM;
- desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
+ desc = kmalloc(usb_config_req_size, GFP_KERNEL);
if (!desc)
return -ENOMEM;
for (cfgno = 0; cfgno < ncfg; cfgno++) {
- /* We grab just the first descriptor so we know how long
- * the whole configuration is */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
- desc, USB_DT_CONFIG_SIZE);
+ desc, usb_config_req_size);
if (result < 0) {
dev_err(ddev, "unable to read config index %d "
"descriptor/%s: %d\n", cfgno, "start", result);
@@ -956,16 +966,14 @@ int usb_get_configuration(struct usb_device *dev)
dev->descriptor.bNumConfigurations = cfgno;
break;
} else if (result < 4) {
- dev_err(ddev, "config index %d descriptor too short "
- "(expected %i, got %i)\n", cfgno,
- USB_DT_CONFIG_SIZE, result);
+ dev_err(ddev, "config index %d descriptor too short (asked for %zu, got %i)\n",
+ cfgno, usb_config_req_size, result);
result = -EINVAL;
goto err;
}
length = max_t(int, le16_to_cpu(desc->wTotalLength),
USB_DT_CONFIG_SIZE);
- /* Now that we know the length, get the whole thing */
bigbuffer = kmalloc(length, GFP_KERNEL);
if (!bigbuffer) {
result = -ENOMEM;
@@ -975,6 +983,13 @@ int usb_get_configuration(struct usb_device *dev)
if (dev->quirks & USB_QUIRK_DELAY_INIT)
msleep(200);
+ /* Skip the second read if we already got everything */
+ if (result >= length) {
+ memcpy(bigbuffer, desc, length);
+ goto store_and_parse;
+ }
+
+ /* Get the whole thing */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
bigbuffer, length);
if (result < 0) {
@@ -989,6 +1004,7 @@ int usb_get_configuration(struct usb_device *dev)
length = result;
}
+store_and_parse:
dev->rawdescriptors[cfgno] = bigbuffer;
result = usb_parse_configuration(dev, cfgno,
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 87ee2d938bc0..b5b577f0b931 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -142,6 +142,10 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp)
break;
case 'q':
flags |= USB_QUIRK_FORCE_ONE_CONFIG;
+ break;
+ case 'r':
+ flags |= USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE;
+ break;
/* Ignore unrecognized flag characters */
}
}
@@ -589,6 +593,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x2386, 0x350e), .driver_info = USB_QUIRK_NO_LPM },
+ /* ShanWan Wireless Gamepad */
+ { USB_DEVICE(0x2563, 0x0575), .driver_info = USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE },
+
/* UGREEN 35871 - BOS descriptor fetch hangs at SuperSpeed Plus */
{ USB_DEVICE(0x2b89, 0x5871), .driver_info = USB_QUIRK_NO_BOS },
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 64eabda2f546..bf02545b37a2 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/usb/host/xhci.h b/drivers/usb/host/xhci.h
index d0f14fc23245..fd7420cfbf64 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -187,13 +187,13 @@ struct xhci_op_regs {
/* CRCR - Command Ring Control Register - cmd_ring bitmasks */
/* bit 0 - Cycle bit indicates the ownership of the command ring */
-#define CMD_RING_CYCLE BIT(0)
+#define CMD_RING_CYCLE BIT_ULL(0)
/* stop ring operation after completion of the currently executing command */
-#define CMD_RING_PAUSE BIT(1)
+#define CMD_RING_PAUSE BIT_ULL(1)
/* stop ring immediately - abort the currently executing command */
-#define CMD_RING_ABORT BIT(2)
+#define CMD_RING_ABORT BIT_ULL(2)
/* true: command ring is running */
-#define CMD_RING_RUNNING BIT(3)
+#define CMD_RING_RUNNING BIT_ULL(3)
/* bits 63:6 - Command Ring pointer */
#define CMD_RING_PTR_MASK GENMASK_ULL(63, 6)
@@ -268,7 +268,7 @@ struct xhci_intr_reg {
* bit 3 - Event Handler Busy (EHB), whether the event ring is scheduled to be serviced by
* a work queue (or delayed service routine)?
*/
-#define ERST_EHB BIT(3)
+#define ERST_EHB BIT_ULL(3)
/* bits 63:4 - Event Ring Dequeue Pointer */
#define ERST_PTR_MASK GENMASK_ULL(63, 4)
@@ -499,7 +499,7 @@ struct xhci_ep_ctx {
#define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff)
/* deq bitmasks */
-#define EP_CTX_CYCLE_MASK BIT(0)
+#define EP_CTX_CYCLE_MASK BIT_ULL(0)
/* bits 63:4 - TR Dequeue Pointer */
#define TR_DEQ_PTR_MASK GENMASK_ULL(63, 4)
diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c
index 3c2474dca810..fe093e7760d5 100644
--- a/drivers/usb/misc/usbio.c
+++ b/drivers/usb/misc/usbio.c
@@ -265,7 +265,7 @@ int usbio_bulk_msg(struct auxiliary_device *adev, u8 type, u8 cmd, bool last,
lockdep_assert_held(&usbio->bulk_mutex);
if ((obuf_len > (usbio->txbuf_len - sizeof(*bpkt))) ||
- (ibuf_len > (usbio->txbuf_len - sizeof(*bpkt))))
+ (ibuf_len > (usbio->rxbuf_len - sizeof(*bpkt))))
return -EMSGSIZE;
if (ibuf_len)
diff --git a/drivers/vdpa/mlx5/core/mlx5_vdpa.h b/drivers/vdpa/mlx5/core/mlx5_vdpa.h
index 2cedf7e2dbc4..42f2f44b383c 100644
--- a/drivers/vdpa/mlx5/core/mlx5_vdpa.h
+++ b/drivers/vdpa/mlx5/core/mlx5_vdpa.h
@@ -11,6 +11,8 @@
#define MLX5V_ETH_HARD_MTU (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
+extern int mlx5_vdpa_max_iotlb_entries;
+
struct mlx5_vdpa_direct_mr {
u64 start;
u64 end;
diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c
index 42c2705077a6..ce204ae3dfda 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);
@@ -777,6 +778,9 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev,
{
int err;
+ if (mlx5_vdpa_max_iotlb_entries < 2)
+ return -EINVAL;
+
if (iotlb)
err = create_user_mr(mvdev, mr, iotlb);
else
@@ -785,7 +789,7 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev,
if (err)
return err;
- mr->iotlb = vhost_iotlb_alloc(0, 0);
+ mr->iotlb = vhost_iotlb_alloc(mlx5_vdpa_max_iotlb_entries, 0);
if (!mr->iotlb) {
err = -ENOMEM;
goto err_mr;
diff --git a/drivers/vdpa/mlx5/core/resources.c b/drivers/vdpa/mlx5/core/resources.c
index aeae31d0cefa..28a4d7a35bf4 100644
--- a/drivers/vdpa/mlx5/core/resources.c
+++ b/drivers/vdpa/mlx5/core/resources.c
@@ -3,8 +3,14 @@
#include <linux/iova.h>
#include <linux/mlx5/driver.h>
+#include <linux/moduleparam.h>
#include "mlx5_vdpa.h"
+int mlx5_vdpa_max_iotlb_entries = 2048;
+module_param_named(max_iotlb_entries, mlx5_vdpa_max_iotlb_entries, int, 0444);
+MODULE_PARM_DESC(max_iotlb_entries,
+ "Maximum number of iotlb entries. (default: 2048)");
+
static int alloc_pd(struct mlx5_vdpa_dev *dev, u32 *pdn, u16 uid)
{
struct mlx5_core_dev *mdev = dev->mdev;
@@ -229,7 +235,10 @@ int mlx5_vdpa_destroy_mkey(struct mlx5_vdpa_dev *mvdev, u32 mkey)
static int init_ctrl_vq(struct mlx5_vdpa_dev *mvdev)
{
- mvdev->cvq.iotlb = vhost_iotlb_alloc(0, 0);
+ if (mlx5_vdpa_max_iotlb_entries < 2)
+ return -EINVAL;
+
+ mvdev->cvq.iotlb = vhost_iotlb_alloc(mlx5_vdpa_max_iotlb_entries, 0);
if (!mvdev->cvq.iotlb)
return -ENOMEM;
diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c
index 8cb1cc2ea139..4d116644851d 100644
--- a/drivers/vdpa/vdpa_sim/vdpa_sim.c
+++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c
@@ -34,7 +34,7 @@ MODULE_PARM_DESC(batch_mapping, "Batched mapping 1 -Enable; 0 - Disable");
static int max_iotlb_entries = 2048;
module_param(max_iotlb_entries, int, 0444);
MODULE_PARM_DESC(max_iotlb_entries,
- "Maximum number of iotlb entries for each address space. 0 means unlimited. (default: 2048)");
+ "Maximum number of iotlb entries for each address space. (default: 2048)");
static bool use_va = true;
module_param(use_va, bool, 0444);
@@ -201,6 +201,8 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr,
if (!dev_attr->alloc_size)
return ERR_PTR(-EINVAL);
+ if (max_iotlb_entries < 2)
+ return ERR_PTR(-EINVAL);
if (config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) {
if (config->device_features &
@@ -261,8 +263,10 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr,
for (i = 0; i < vdpasim->dev_attr.nas; i++) {
vhost_iotlb_init(&vdpasim->iommu[i], max_iotlb_entries, 0);
- vhost_iotlb_add_range(&vdpasim->iommu[i], 0, ULONG_MAX, 0,
- VHOST_MAP_RW);
+ ret = vhost_iotlb_add_range(&vdpasim->iommu[i], 0, ULONG_MAX,
+ 0, VHOST_MAP_RW);
+ if (ret)
+ goto err_iommu;
vdpasim->iommu_pt[i] = true;
}
diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c
index 4dc76c0d0d13..b6c958224b7c 100644
--- a/drivers/vdpa/vdpa_user/iova_domain.c
+++ b/drivers/vdpa/vdpa_user/iova_domain.c
@@ -12,11 +12,17 @@
#include <linux/file.h>
#include <linux/anon_inodes.h>
#include <linux/highmem.h>
+#include <linux/moduleparam.h>
#include <linux/vmalloc.h>
#include <linux/vdpa.h>
#include "iova_domain.h"
+static int max_iotlb_entries = 2048;
+module_param(max_iotlb_entries, int, 0444);
+MODULE_PARM_DESC(max_iotlb_entries,
+ "Maximum number of iotlb entries. (default: 2048)");
+
static int vduse_iotlb_add_range(struct vduse_iova_domain *domain,
u64 start, u64 last,
u64 addr, unsigned int perm,
@@ -622,11 +628,14 @@ vduse_domain_create(unsigned long iova_limit, size_t bounce_size)
if (iova_limit <= bounce_size)
return NULL;
+ if (max_iotlb_entries <= 0)
+ return NULL;
+
domain = kzalloc_obj(*domain);
if (!domain)
return NULL;
- domain->iotlb = vhost_iotlb_alloc(0, 0);
+ domain->iotlb = vhost_iotlb_alloc(max_iotlb_entries, 0);
if (!domain->iotlb)
goto err_iotlb;
diff --git a/drivers/vhost/iotlb.c b/drivers/vhost/iotlb.c
index e1414c774c34..a1d4376a5b87 100644
--- a/drivers/vhost/iotlb.c
+++ b/drivers/vhost/iotlb.c
@@ -20,6 +20,14 @@ INTERVAL_TREE_DEFINE(struct vhost_iotlb_map,
rb, __u64, __subtree_last,
START, LAST, static inline, vhost_iotlb_itree);
+static void vhost_iotlb_map_unlink(struct vhost_iotlb *iotlb,
+ struct vhost_iotlb_map *map)
+{
+ vhost_iotlb_itree_remove(map, &iotlb->root);
+ list_del(&map->link);
+ iotlb->nmaps--;
+}
+
/**
* vhost_iotlb_map_free - remove a map node and free it
* @iotlb: the IOTLB
@@ -28,10 +36,8 @@ INTERVAL_TREE_DEFINE(struct vhost_iotlb_map,
void vhost_iotlb_map_free(struct vhost_iotlb *iotlb,
struct vhost_iotlb_map *map)
{
- vhost_iotlb_itree_remove(map, &iotlb->root);
- list_del(&map->link);
+ vhost_iotlb_map_unlink(iotlb, map);
kfree(map);
- iotlb->nmaps--;
}
EXPORT_SYMBOL_GPL(vhost_iotlb_map_free);
@@ -57,14 +63,25 @@ int vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb,
if (last < start)
return -EFAULT;
+ if (!iotlb->limit)
+ return -EINVAL;
+
/* If the range being mapped is [0, ULONG_MAX], split it into two entries
* otherwise its size would overflow u64.
*/
if (start == 0 && last == ULONG_MAX) {
u64 mid = last / 2;
- int err = vhost_iotlb_add_range_ctx(iotlb, start, mid, addr,
- perm, opaque);
+ int err;
+
+ if (iotlb->limit < 2)
+ return -ENOSPC;
+ if (!(iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) &&
+ iotlb->nmaps > iotlb->limit - 2)
+ return -ENOSPC;
+
+ err = vhost_iotlb_add_range_ctx(iotlb, start, mid, addr,
+ perm, opaque);
if (err)
return err;
@@ -72,17 +89,19 @@ int vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb,
start = mid + 1;
}
- if (iotlb->limit &&
- iotlb->nmaps == iotlb->limit &&
- iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) {
- map = list_first_entry(&iotlb->list, typeof(*map), link);
- vhost_iotlb_map_free(iotlb, map);
+ if (iotlb->nmaps >= iotlb->limit) {
+ if (iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) {
+ map = list_first_entry(&iotlb->list, typeof(*map), link);
+ vhost_iotlb_map_unlink(iotlb, map);
+ } else {
+ return -ENOSPC;
+ }
+ } else {
+ map = kmalloc_obj(*map, GFP_ATOMIC);
+ if (!map)
+ return -ENOMEM;
}
- map = kmalloc_obj(*map, GFP_ATOMIC);
- if (!map)
- return -ENOMEM;
-
map->start = start;
map->size = last - start + 1;
map->last = last;
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index 9a1253b9d8c5..904bd842cdd8 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -972,6 +972,9 @@ vhost_scsi_mapal(struct vhost_scsi *vs, struct vhost_scsi_cmd *cmd,
if (prot_bytes) {
sgl_count = vhost_scsi_calc_sgls(prot_iter, prot_bytes,
VHOST_SCSI_PREALLOC_PROT_SGLS);
+ if (sgl_count < 0)
+ return sgl_count;
+
cmd->prot_table.sgl = cmd->prot_sgl;
ret = sg_alloc_table_chained(&cmd->prot_table, sgl_count,
cmd->prot_table.sgl,
@@ -1416,6 +1419,11 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
* actual data payload length.
*/
if (prot_bytes) {
+ if (prot_bytes >= exp_data_len) {
+ vq_err(vq, "Protection data exceeds payload length\n");
+ goto err;
+ }
+
exp_data_len -= prot_bytes;
prot_iter = data_iter;
iov_iter_truncate(&prot_iter, prot_bytes);
@@ -2219,6 +2227,7 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
{
struct vhost_virtqueue *vq;
bool is_log, was_log;
+ u64 old_features;
int i;
if (features & ~VHOST_SCSI_FEATURES)
@@ -2234,6 +2243,14 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
if (!vs->dev.nvqs)
goto out;
+ old_features = vs->vqs[0].vq.acked_features;
+ if (vs->vs_tpg &&
+ ((features ^ old_features) &
+ ~(1ULL << VHOST_F_LOG_ALL))) {
+ mutex_unlock(&vs->dev.mutex);
+ return -EBUSY;
+ }
+
is_log = features & (1 << VHOST_F_LOG_ALL);
/*
* All VQs should have same feature.
diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index ac55275fa0d0..c3d913bd7cac 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -34,6 +34,11 @@ enum {
#define VHOST_VDPA_DEV_MAX (1U << MINORBITS)
+static int max_iotlb_entries = 2048;
+module_param(max_iotlb_entries, int, 0444);
+MODULE_PARM_DESC(max_iotlb_entries,
+ "Maximum number of iotlb entries. (default: 2048)");
+
#define VHOST_VDPA_IOTLB_BUCKETS 16
struct vhost_vdpa_as {
@@ -109,12 +114,14 @@ static struct vhost_vdpa_as *vhost_vdpa_alloc_as(struct vhost_vdpa *v, u32 asid)
if (asid >= v->vdpa->nas)
return NULL;
+ if (max_iotlb_entries <= 0)
+ return NULL;
as = kmalloc_obj(*as);
if (!as)
return NULL;
- vhost_iotlb_init(&as->iotlb, 0, 0);
+ vhost_iotlb_init(&as->iotlb, max_iotlb_entries, 0);
as->id = asid;
hlist_add_head(&as->hash_link, head);
@@ -1102,6 +1109,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 +1122,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 db329a6f6145..e4c44f2ea413 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -1137,6 +1137,9 @@ EXPORT_SYMBOL_GPL(vhost_dev_set_owner);
static struct vhost_iotlb *iotlb_alloc(void)
{
+ if (max_iotlb_entries <= 0)
+ return NULL;
+
return vhost_iotlb_alloc(max_iotlb_entries,
VHOST_IOTLB_FLAG_RETIRE);
}
@@ -1981,6 +1984,8 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
return -EOPNOTSUPP;
if (mem.nregions > max_mem_regions)
return -E2BIG;
+ if (max_iotlb_entries <= 0)
+ return -EINVAL;
newmem = kvzalloc_flex(*newmem, regions, mem.nregions);
if (!newmem)
return -ENOMEM;
@@ -2126,6 +2131,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;
@@ -2275,6 +2288,9 @@ int vhost_init_device_iotlb(struct vhost_dev *d)
struct vhost_iotlb *niotlb, *oiotlb;
int i;
+ if (max_iotlb_entries <= 0)
+ return -EINVAL;
+
niotlb = iotlb_alloc();
if (!niotlb)
return -ENOMEM;
diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c
index 65681dcc5930..39f44258d793 100644
--- a/drivers/video/fbdev/core/bitblit.c
+++ b/drivers/video/fbdev/core/bitblit.c
@@ -273,9 +273,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 != (const char *)src ||
par->cursor_reset) {
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
index aba66b8e9d03..80ba04df54ad 100644
--- a/drivers/watchdog/at91sam9_wdt.c
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -242,7 +242,7 @@ static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt)
return 0;
out_stop_timer:
- timer_delete(&wdt->timer);
+ timer_shutdown_sync(&wdt->timer);
return err;
}
@@ -378,7 +378,7 @@ static void at91wdt_remove(struct platform_device *pdev)
watchdog_unregister_device(&wdt->wdd);
pr_warn("I quit now, hardware will probably reboot!\n");
- timer_delete(&wdt->timer);
+ timer_shutdown_sync(&wdt->timer);
}
#if defined(CONFIG_OF)
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/attr.c b/fs/attr.c
index ded221defae6..fc5817c9f4e8 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -176,7 +176,7 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry,
* covered by the open-time check because sys_truncate() takes a
* path, not an open file.
*/
- if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode))
+ if (IS_VERITY(inode))
return -EPERM;
error = inode_newsize_ok(inode, attr->ia_size);
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 725b48ac5873..b7075e4df46f 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -937,7 +937,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 dc5148f176e7..1271be0fbfcf 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4167,10 +4167,11 @@ static int btrfs_read_locked_inode(struct btrfs_inode *inode, struct btrfs_path
btrfs_inode_split_flags(btrfs_inode_flags(leaf, inode_item),
&inode->flags, &inode->ro_flags);
+
+cache_index:
btrfs_update_inode_mapping_flags(inode);
btrfs_set_inode_mapping_order(inode);
-cache_index:
/*
* If we were modified in the current generation and evicted from memory
* and then re-read we need to do a full sync since we don't have any
@@ -10193,6 +10194,7 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
if (cb)
cleanup_compressed_bio(cb);
out:
+ extent_changeset_free(data_reserved);
if (ret >= 0)
iocb->ki_pos += encoded->len;
return ret;
diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c
index 6e4aa22853ab..2f0996692da0 100644
--- a/fs/btrfs/lzo.c
+++ b/fs/btrfs/lzo.c
@@ -552,17 +552,27 @@ int lzo_decompress(struct list_head *ws, const u8 *data_in,
size_t max_segment_len = workspace_buf_length(fs_info);
int ret;
- if (unlikely(srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2))
+ if (unlikely(srclen <= LZO_LEN * 2 ||
+ srclen > max_segment_len + LZO_LEN * 2)) {
+ btrfs_err(fs_info, "invalid lzo header length, has %zu expect (%u, %zu)",
+ srclen, LZO_LEN * 2, max_segment_len + LZO_LEN * 2);
return -EUCLEAN;
+ }
in_len = get_unaligned_le32(data_in);
- if (unlikely(in_len != srclen))
+ if (unlikely(in_len != srclen)) {
+ btrfs_err(fs_info, "invalid lzo header length, has %zu expect %zu",
+ in_len, srclen);
return -EUCLEAN;
+ }
data_in += LZO_LEN;
in_len = get_unaligned_le32(data_in);
- if (unlikely(in_len != srclen - LZO_LEN * 2))
+ if (unlikely(in_len != srclen - LZO_LEN * 2)) {
+ btrfs_err(fs_info, "invalid lzo segment length, has %zu expect %zu",
+ in_len, srclen - LZO_LEN * 2);
return -EUCLEAN;
+ }
data_in += LZO_LEN;
out_len = sectorsize;
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index 9915e39362db..c80b24a941ad 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -534,7 +534,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 341ddd353b3a..841ef2612786 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4502,6 +4502,10 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
new_mnt = vfs_create_mount(fc);
if (IS_ERR(new_mnt))
return PTR_ERR(new_mnt);
+ if (new_mnt->mnt_sb->s_flags & SB_NOUSER) {
+ mntput(new_mnt);
+ return -EINVAL;
+ }
new_mnt->mnt_flags = mnt_flags;
new_path.dentry = dget(fc->root);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 4db27f4eb01e..6142a7daf983 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -10330,6 +10330,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);
}
@@ -10368,17 +10369,22 @@ static int nfs41_free_stateid(struct nfs_server *server,
struct nfs_free_stateid_data *data;
struct rpc_task *task;
struct nfs_client *clp = server->nfs_client;
+ int ret = -EIO;
if (!refcount_inc_not_zero(&clp->cl_count))
- return -EIO;
+ return ret;
+ if (!nfs_sb_active(server->super))
+ goto out_put_clp;
nfs4_state_protect(clp, NFS_SP4_MACH_CRED_STATEID,
&task_setup.rpc_client, &msg);
dprintk("NFS call free_stateid %p\n", stateid);
data = kmalloc_obj(*data);
- if (!data)
- return -ENOMEM;
+ if (!data) {
+ ret = -ENOMEM;
+ goto out_put_server;
+ }
data->server = server;
nfs4_stateid_copy(&data->args.stateid, stateid);
@@ -10394,6 +10400,11 @@ static int nfs41_free_stateid(struct nfs_server *server,
rpc_put_task(task);
stateid->type = NFS4_FREED_STATEID_TYPE;
return 0;
+out_put_server:
+ nfs_sb_deactive(server->super);
+out_put_clp:
+ nfs_put_client(clp);
+ return ret;
}
static void
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 60f0b7ceef0a..60b808b85fc4 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -1544,7 +1544,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;
ovl_set_d_op(sb);
diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
index de2012cc9cf3..7cf7dd104f7c 100644
--- a/fs/smb/client/sess.c
+++ b/fs/smb/client/sess.c
@@ -233,9 +233,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/smb/client/smb1transport.c b/fs/smb/client/smb1transport.c
index 53abb29fe71b..966f2cf83a51 100644
--- a/fs/smb/client/smb1transport.c
+++ b/fs/smb/client/smb1transport.c
@@ -260,9 +260,23 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
goto out;
if (out_buf) {
- *pbytes_returned = resp_iov.iov_len;
- if (resp_iov.iov_len)
- memcpy(out_buf, resp_iov.iov_base, resp_iov.iov_len);
+ /* Use smbCalcSize() for both single- and multi-part T2 responses,
+ * both here and in coalesce_t2().
+ */
+ unsigned int copy_len;
+ if (WARN_ON_ONCE(!resp_iov.iov_base)) {
+ rc = -EIO;
+ goto out;
+ }
+ copy_len = smbCalcSize(resp_iov.iov_base);
+ if (copy_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cifs_dbg(VFS, "response size %u exceeds buffer\n",
+ copy_len);
+ rc = -ENOBUFS;
+ goto out;
+ }
+ *pbytes_returned = copy_len;
+ memcpy(out_buf, resp_iov.iov_base, copy_len);
}
out:
@@ -386,11 +400,13 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len)
}
put_bcc(byte_count, target_hdr);
- byte_count = *pdu_len;
- byte_count += total_in_src;
+ /* use smbCalcSize() rather than *pdu_len: the demux loop resets
+ * *pdu_len to each secondary's pdu_length, making it unreliable.
+ */
+ byte_count = smbCalcSize(target_hdr);
/* don't allow buffer to overflow */
if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
- cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n",
+ cifs_dbg(FYI, "coalesced size exceeds buffer size (%u)\n",
byte_count);
return -ENOBUFS;
}
diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
index 26b6453de30e..498d316bf5d7 100644
--- a/fs/tracefs/event_inode.c
+++ b/fs/tracefs/event_inode.c
@@ -124,7 +124,17 @@ static inline void put_ei(struct eventfs_inode *ei)
static inline void free_ei(struct eventfs_inode *ei)
{
if (ei) {
+ /* The ei should have no children if it is being freed. */
+ WARN_ON_ONCE(!list_empty(&ei->children));
ei->is_freed = 1;
+ /*
+ * The SRCU iteration has a smp_rmb() to make sure it
+ * sees a child (that may have already been freed)
+ * before it reads is_free. If is_free is set, it must
+ * not use the child it acquired from ei->children, as
+ * the list may be used for SRCU.
+ */
+ smp_wmb();
put_ei(ei);
}
}
@@ -629,6 +639,20 @@ static int eventfs_iterate(struct file *file, struct dir_context *ctx)
list_for_each_entry_srcu(ei_child, &ei->children, list,
srcu_read_lock_held(&eventfs_srcu)) {
+ /*
+ * If the ei is being freed, then the ei->children may be
+ * being used as the rcu list, which means the next element
+ * may be garbage. The ei->is_free is set before switching
+ * the ei->children over to ei->rcu. The read memory barrier
+ * here makes sure the ei_child is read before is_free is
+ * updated.
+ *
+ * Matches the smp_wmb() in free_ei()
+ */
+ smp_rmb();
+ if (ei->is_freed)
+ return -EINVAL;
+
if (c > 0) {
c--;
continue;
@@ -824,7 +848,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:
@@ -837,7 +861,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/tracefs/internal.h b/fs/tracefs/internal.h
index a4a7f8431aff..c61481d04c8e 100644
--- a/fs/tracefs/internal.h
+++ b/fs/tracefs/internal.h
@@ -46,11 +46,11 @@ struct eventfs_attr {
* @ino: The saved inode number
*/
struct eventfs_inode {
+ struct list_head list;
union {
- struct list_head list;
+ struct list_head children;
struct rcu_head rcu;
};
- struct list_head children;
const struct eventfs_entry *entries;
const char *name;
struct eventfs_attr *entry_attrs;
diff --git a/fs/verity/measure.c b/fs/verity/measure.c
index 6a35623ebdf0..465ec3733a8a 100644
--- a/fs/verity/measure.c
+++ b/fs/verity/measure.c
@@ -122,11 +122,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;
@@ -144,17 +144,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/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index 0cea458f1353..23bd81fbc278 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -114,7 +114,7 @@ xfs_buf_free(
vfree(bp->b_addr);
else if (bp->b_flags & _XBF_KMEM)
kfree(bp->b_addr);
- else
+ else if (bp->b_addr)
folio_put(virt_to_folio(bp->b_addr));
call_rcu(&bp->b_rcu, xfs_buf_free_callback);
diff --git a/include/linux/dibs.h b/include/linux/dibs.h
index c75607f8a5cf..d3e0777f25ae 100644
--- a/include/linux/dibs.h
+++ b/include/linux/dibs.h
@@ -439,7 +439,7 @@ static inline void *dibs_get_priv(struct dibs_dev *dev,
/**
* dibs_dev_alloc() - allocate and reference device structure
*
- * The following fields will be valid upon successful return: dev
+ * The following fields will be valid upon successful return: dev, lock
* NOTE: Use put_device(dibs_get_dev(@dibs)) to give up your reference instead
* of freeing @dibs @dev directly once you have successfully called this
* function.
diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
index 301a83afbd66..f5330e7ae247 100644
--- a/include/linux/hwmon.h
+++ b/include/linux/hwmon.h
@@ -13,6 +13,7 @@
#define _HWMON_H_
#include <linux/bitops.h>
+#include <linux/cleanup.h>
struct device;
struct attribute_group;
@@ -495,6 +496,8 @@ char *devm_hwmon_sanitize_name(struct device *dev, const char *name);
void hwmon_lock(struct device *dev);
void hwmon_unlock(struct device *dev);
+DEFINE_GUARD(hwmon_lock, struct device *, hwmon_lock(_T), hwmon_unlock(_T))
+
/**
* hwmon_is_bad_char - Is the char invalid in a hwmon name
* @ch: the char to be considered
diff --git a/include/linux/mmap_lock.h b/include/linux/mmap_lock.h
index 04b8f61ece5d..6b5c2390cc30 100644
--- a/include/linux/mmap_lock.h
+++ b/include/linux/mmap_lock.h
@@ -621,6 +621,7 @@ static inline void mmap_read_unlock(struct mm_struct *mm)
DEFINE_GUARD(mmap_read_lock, struct mm_struct *,
mmap_read_lock(_T), mmap_read_unlock(_T))
+DEFINE_GUARD_COND(mmap_read_lock, _try, mmap_read_trylock(_T))
static inline void mmap_read_unlock_non_owner(struct mm_struct *mm)
{
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 4a0e83709f29..7bd4d989d2eb 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -300,9 +300,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)
@@ -3507,11 +3509,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 f3b13da78aac..6063fe5b7784 100644
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -214,6 +214,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/psi.h b/include/linux/psi.h
index e0745873e3f2..7966e3ac03b9 100644
--- a/include/linux/psi.h
+++ b/include/linux/psi.h
@@ -25,7 +25,9 @@ void psi_memstall_leave(unsigned long *flags);
int psi_show(struct seq_file *s, struct psi_group *group, enum psi_res res);
struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf,
enum psi_res res, struct file *file,
- struct kernfs_open_file *of);
+ struct kernfs_open_file *of,
+ bool *need_rtpoll_worker);
+int psi_trigger_create_rtpoll_worker(struct psi_group *group);
void psi_trigger_destroy(struct psi_trigger *t);
__poll_t psi_trigger_poll(void **trigger_ptr, struct file *file,
diff --git a/include/linux/sched/ext.h b/include/linux/sched/ext.h
index 2129e18ada58..31c810a3b808 100644
--- a/include/linux/sched/ext.h
+++ b/include/linux/sched/ext.h
@@ -235,11 +235,11 @@ struct sched_ext_entity {
* to %SCHED_EXT with -%EACCES.
*
* Can be set from ops.init_task() while the BPF scheduler is being
- * loaded (!scx_init_task_args->fork). If set and the task's policy is
- * already %SCHED_EXT, the task's policy is rejected and forcefully
- * reverted to %SCHED_NORMAL. The number of such events are reported
- * through /sys/kernel/debug/sched_ext::nr_rejected. Setting this flag
- * during fork is not allowed.
+ * loaded. If set and the task's policy is already %SCHED_EXT, the
+ * task's policy is rejected and forcefully reverted to %SCHED_NORMAL.
+ * The number of such events are reported through
+ * /sys/kernel/sched_ext/nr_rejected. Setting this flag from any other
+ * ops.init_task() invocation, such as during fork, fails the scheduler.
*/
bool disallow; /* reject switching into SCX */
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index b3cc7beab4a3..a4043b33c2c2 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -81,4 +81,7 @@
/* Device claims zero configurations, forcing to 1 */
#define USB_QUIRK_FORCE_ONE_CONFIG BIT(18)
+/* Use a 255 bytes config descriptor request mirroring windows behavior */
+#define USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE BIT(19)
+
#endif /* __LINUX_USB_QUIRKS_H */
diff --git a/include/net/act_api.h b/include/net/act_api.h
index fd2967ee08f7..ee29d25d8df2 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 8ced27a8229b..e6764245995f 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -405,8 +405,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 8d98f7e0a9fb..7f7fa4484492 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -25,9 +25,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
@@ -38,6 +36,12 @@
#define IP_VS_HDR_INVERSE 1
#define IP_VS_HDR_ICMP 2
+/* Destination Server Flags */
+#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */
+
+/* Destination Server Config Flags */
+#define IP_VS_DEST_CF_AVAILABLE 0x0001 /* server is available */
+
/* conn_tab limits (as per Kconfig) */
#define IP_VS_CONN_TAB_MIN_BITS 8
#if BITS_PER_LONG > 32
@@ -970,6 +974,7 @@ struct ip_vs_dest {
volatile unsigned int flags; /* dest status flags */
atomic_t conn_flags; /* flags to copy to conn */
atomic_t weight; /* server weight */
+ unsigned long cflags; /* config flags */
atomic_t last_weight; /* server latest weight */
__u16 tun_type; /* tunnel type */
__be16 tun_port; /* tunnel port */
@@ -981,10 +986,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 */
@@ -1887,6 +1893,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)
*/
@@ -2040,7 +2048,7 @@ static inline bool ip_vs_conn_use_hash2(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,
@@ -2073,30 +2081,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);
}
@@ -2207,14 +2208,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/libeth/xsk.h b/include/net/libeth/xsk.h
index 82b5d21aae87..5dcc0d7f65b7 100644
--- a/include/net/libeth/xsk.h
+++ b/include/net/libeth/xsk.h
@@ -205,7 +205,7 @@ __libeth_xsk_xmit_fill_buf_md(const struct xdp_desc *xdesc,
BUILD_BUG_ON(!__builtin_constant_p(tmo == libeth_xsktmo));
tmo = tmo == libeth_xsktmo ? &__libeth_xsktmo : tmo;
- xsk_tx_metadata_request(ctx.meta, tmo, &desc);
+ xsk_tx_metadata_request(sq->pool, &ctx.meta, tmo, &desc);
return desc;
}
diff --git a/include/net/route.h b/include/net/route.h
index f90106f383c5..45290177a33c 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -276,6 +276,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 195e22a83566..ca51b2c3b042 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -99,6 +99,7 @@ struct Qdisc {
struct hlist_node hash;
u32 handle;
u32 parent;
+ int depth;
struct netdev_queue *dev_queue;
diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h
index 8b51876efbed..6e70b320b399 100644
--- a/include/net/xdp_sock.h
+++ b/include/net/xdp_sock.h
@@ -141,45 +141,16 @@ INDIRECT_CALLABLE_DECLARE(void xsk_destruct_skb(struct sk_buff *));
static inline void xsk_tx_metadata_to_compl(struct xsk_tx_metadata *meta,
struct xsk_tx_metadata_compl *compl)
{
- if (!meta)
- return;
+ compl->tx_timestamp = NULL;
- if (meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)
- compl->tx_timestamp = &meta->completion.tx_timestamp;
- else
- compl->tx_timestamp = NULL;
-}
-
-/**
- * xsk_tx_metadata_request - Evaluate AF_XDP TX metadata at submission
- * and call appropriate xsk_tx_metadata_ops operation.
- * @meta: pointer to AF_XDP metadata area
- * @ops: pointer to struct xsk_tx_metadata_ops
- * @priv: pointer to driver-private aread
- *
- * This function should be called by the networking device when
- * it prepares AF_XDP egress packet.
- */
-static inline void xsk_tx_metadata_request(const struct xsk_tx_metadata *meta,
- const struct xsk_tx_metadata_ops *ops,
- void *priv)
-{
if (!meta)
return;
- if (ops->tmo_request_launch_time)
- if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME)
- ops->tmo_request_launch_time(meta->request.launch_time,
- priv);
-
- if (ops->tmo_request_timestamp)
- if (meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)
- ops->tmo_request_timestamp(priv);
+ /* we can only arrive here if the completion timestamp has been
+ * requested via XDP_TXMD_FLAGS_TIMESTAMP, see xsk_tx_metadata_request
+ */
- if (ops->tmo_request_checksum)
- if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM)
- ops->tmo_request_checksum(meta->request.csum_start,
- meta->request.csum_offset, priv);
+ compl->tx_timestamp = &meta->completion.tx_timestamp;
}
/**
@@ -231,12 +202,6 @@ static inline void xsk_tx_metadata_to_compl(struct xsk_tx_metadata *meta,
{
}
-static inline void xsk_tx_metadata_request(struct xsk_tx_metadata *meta,
- const struct xsk_tx_metadata_ops *ops,
- void *priv)
-{
-}
-
static inline void xsk_tx_metadata_complete(struct xsk_tx_metadata_compl *compl,
const struct xsk_tx_metadata_ops *ops,
void *priv)
diff --git a/include/net/xdp_sock_drv.h b/include/net/xdp_sock_drv.h
index 46797645a0c2..b344789f5df8 100644
--- a/include/net/xdp_sock_drv.h
+++ b/include/net/xdp_sock_drv.h
@@ -245,7 +245,7 @@ static inline void *xsk_buff_raw_get_data(struct xsk_buff_pool *pool, u64 addr)
* details.
*
* Return: new &xdp_desc_ctx struct containing desc's DMA address and metadata
- * pointer, if it is present and valid (initialized to %NULL otherwise).
+ * pointer, if it is present (initialized to %NULL otherwise).
*/
static inline struct xdp_desc_ctx
xsk_buff_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr)
@@ -260,24 +260,70 @@ xsk_buff_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr)
0)
static inline bool
-xsk_buff_valid_tx_metadata(const struct xsk_tx_metadata *meta)
+xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool,
+ const struct xsk_tx_metadata *meta, u64 *flags)
{
- return !(meta->flags & ~XDP_TXMD_FLAGS_VALID);
+ *flags = READ_ONCE(meta->flags);
+ if (*flags & XDP_TXMD_FLAGS_LAUNCH_TIME)
+ if (pool->tx_metadata_len <
+ offsetofend(struct xsk_tx_metadata, request.launch_time))
+ return false;
+ return !(*flags & ~XDP_TXMD_FLAGS_VALID);
+}
+
+/**
+ * xsk_tx_metadata_request - Evaluate AF_XDP TX metadata at submission
+ * and call appropriate xsk_tx_metadata_ops operation.
+ * @pool: pointer to AF_XDP buffer pool, used to validate the metadata
+ * @pmeta: pointer to pointer to AF_XDP metadata area
+ * @ops: pointer to struct xsk_tx_metadata_ops
+ * @priv: pointer to driver-private area
+ *
+ * This function should be called by the networking device when
+ * it prepares AF_XDP egress packet.
+ */
+static inline void
+xsk_tx_metadata_request(const struct xsk_buff_pool *pool,
+ struct xsk_tx_metadata **pmeta,
+ const struct xsk_tx_metadata_ops *ops, void *priv)
+{
+ const struct xsk_tx_metadata *meta = *pmeta;
+ u64 flags;
+
+ if (!meta)
+ return;
+
+ if (unlikely(!xsk_buff_valid_tx_metadata(pool, meta, &flags))) {
+ *pmeta = NULL;
+ return; /* no way to signal the error to the user */
+ }
+
+ if (ops->tmo_request_launch_time)
+ if (flags & XDP_TXMD_FLAGS_LAUNCH_TIME)
+ ops->tmo_request_launch_time(
+ READ_ONCE(meta->request.launch_time), priv);
+
+ if (ops->tmo_request_timestamp)
+ if (flags & XDP_TXMD_FLAGS_TIMESTAMP)
+ ops->tmo_request_timestamp(priv);
+
+ if (ops->tmo_request_checksum)
+ if (flags & XDP_TXMD_FLAGS_CHECKSUM)
+ ops->tmo_request_checksum(
+ READ_ONCE(meta->request.csum_start),
+ READ_ONCE(meta->request.csum_offset), priv);
+
+ if (!(flags & XDP_TXMD_FLAGS_TIMESTAMP))
+ *pmeta = NULL;
}
static inline struct xsk_tx_metadata *
__xsk_buff_get_metadata(const struct xsk_buff_pool *pool, void *data)
{
- struct xsk_tx_metadata *meta;
-
if (!pool->tx_metadata_len)
return NULL;
- meta = data - pool->tx_metadata_len;
- if (unlikely(!xsk_buff_valid_tx_metadata(meta)))
- return NULL; /* no way to signal the error to the user */
-
- return meta;
+ return data - pool->tx_metadata_len;
}
static inline struct xsk_tx_metadata *
@@ -469,11 +515,20 @@ xsk_buff_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr)
return (struct xdp_desc_ctx){ };
}
-static inline bool xsk_buff_valid_tx_metadata(struct xsk_tx_metadata *meta)
+static inline bool
+xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool,
+ const struct xsk_tx_metadata *meta, u64 *flags)
{
return false;
}
+static inline void
+xsk_tx_metadata_request(const struct xsk_buff_pool *pool,
+ struct xsk_tx_metadata **pmeta,
+ const struct xsk_tx_metadata_ops *ops, void *priv)
+{
+}
+
static inline struct xsk_tx_metadata *
__xsk_buff_get_metadata(const struct xsk_buff_pool *pool, void *data)
{
diff --git a/include/uapi/linux/ip_vs.h b/include/uapi/linux/ip_vs.h
index 1ed234e7f251..2c37c6ac7525 100644
--- a/include/uapi/linux/ip_vs.h
+++ b/include/uapi/linux/ip_vs.h
@@ -28,12 +28,6 @@
#define IP_VS_SVC_F_SCHED_SH_FALLBACK IP_VS_SVC_F_SCHED1 /* SH fallback */
#define IP_VS_SVC_F_SCHED_SH_PORT IP_VS_SVC_F_SCHED2 /* SH use port */
-/*
- * Destination Server Flags
- */
-#define IP_VS_DEST_F_AVAILABLE 0x0001 /* server is available */
-#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */
-
/*
* IPVS sync daemon states
*/
diff --git a/kernel/bpf/rqspinlock.c b/kernel/bpf/rqspinlock.c
index e4e338cdb437..2129defc4a9a 100644
--- a/kernel/bpf/rqspinlock.c
+++ b/kernel/bpf/rqspinlock.c
@@ -572,9 +572,10 @@ int __lockfunc resilient_queued_spin_lock_slowpath(rqspinlock_t *lock, u32 val)
/* Disable queue destruction when we detect deadlocks. */
if (ret == -EDEADLK) {
- if (!next)
+ if (!try_cmpxchg_tail(lock, tail, 0)) {
next = smp_cond_load_relaxed(&node->next, (VAL));
- arch_mcs_spin_unlock_contended(&next->locked);
+ arch_mcs_spin_unlock_contended(&next->locked);
+ }
goto err_release_node;
}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 523f00e609e2..5bad71f003dc 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -13801,13 +13801,6 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
return -EACCES;
}
- /*
- * Accesses to untrusted PTR_TO_MEM are done through probe
- * instructions, hence no need to track offsets.
- */
- if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type & PTR_UNTRUSTED))
- return 0;
-
switch (base_type(ptr_reg->type)) {
case PTR_TO_CTX:
case PTR_TO_MAP_VALUE:
@@ -13837,11 +13830,19 @@ 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;
+
+ /*
+ * Accesses to untrusted PTR_TO_MEM are done through probe
+ * instructions, hence no need to track offsets.
+ */
+ if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type & PTR_UNTRUSTED))
+ return 0;
if (!check_reg_sane_offset_scalar(env, off_reg, ptr_reg->type) ||
!check_reg_sane_offset_ptr(env, ptr_reg, ptr_reg->type))
@@ -13893,7 +13894,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);
@@ -15137,8 +15138,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/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 6152add0c5eb..25e2367b3e0b 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -3961,6 +3961,7 @@ static ssize_t pressure_write(struct kernfs_open_file *of, char *buf,
struct psi_trigger *new;
struct cgroup *cgrp;
struct psi_group *psi;
+ bool need_rtpoll_worker;
ssize_t ret = 0;
cgrp = cgroup_kn_lock_live(of->kn, false);
@@ -3980,12 +3981,32 @@ static ssize_t pressure_write(struct kernfs_open_file *of, char *buf,
}
psi = cgroup_psi(cgrp);
- new = psi_trigger_create(psi, buf, res, of->file, of);
+ new = psi_trigger_create(psi, buf, res, of->file, of,
+ &need_rtpoll_worker);
if (IS_ERR(new)) {
ret = PTR_ERR(new);
goto out_unlock;
}
+ /*
+ * The worker fork must run with neither cgroup_mutex nor the file's
+ * kernfs active reference held. The latter is broken since
+ * cgroup_kn_lock_live(). @of->priv may be released while unlocked, so
+ * recheck before publishing @new.
+ */
+ if (need_rtpoll_worker) {
+ cgroup_unlock();
+ ret = psi_trigger_create_rtpoll_worker(psi);
+ cgroup_lock();
+
+ if (!ret && !of->priv)
+ ret = -ENODEV;
+ if (ret) {
+ psi_trigger_destroy(new);
+ goto out_unlock;
+ }
+ }
+
smp_store_release(&ctx->psi.trigger, new);
out_unlock:
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 240684f4b18c..3b4c43203210 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -2342,6 +2342,34 @@ static inline struct list_head *get_event_list(struct perf_event *event)
&event->pmu_ctx->flexible_active;
}
+/* @sibling must already be unlinked from its old leader's sibling_list. */
+static void perf_promote_sibling_to_leader(struct perf_event *sibling,
+ struct perf_event_context *ctx,
+ int group_caps)
+{
+ /*
+ * Events that have PERF_EV_CAP_SIBLING require being part of
+ * a group and cannot exist on their own, schedule them out
+ * and move them into the ERROR state. Also see
+ * _perf_event_enable(), it will not be able to recover this
+ * ERROR state.
+ */
+ if (sibling->event_caps & PERF_EV_CAP_SIBLING)
+ __event_disable(sibling, ctx, PERF_EVENT_STATE_ERROR);
+
+ sibling->group_leader = sibling;
+ sibling->group_caps = group_caps;
+
+ if (sibling->attach_state & PERF_ATTACH_CONTEXT) {
+ add_event_to_groups(sibling, ctx);
+
+ if (sibling->state == PERF_EVENT_STATE_ACTIVE)
+ list_add_tail(&sibling->active_list, get_event_list(sibling));
+ }
+
+ perf_event__header_size(sibling);
+}
+
static void perf_group_detach(struct perf_event *event)
{
struct perf_event *leader = event->group_leader;
@@ -2365,8 +2393,9 @@ static void perf_group_detach(struct perf_event *event)
*/
if (leader != event) {
list_del_init(&event->sibling_list);
- event->group_leader->nr_siblings--;
- event->group_leader->group_generation++;
+ leader->nr_siblings--;
+ leader->group_generation++;
+ perf_promote_sibling_to_leader(event, ctx, event->event_caps);
goto out;
}
@@ -2376,32 +2405,14 @@ static void perf_group_detach(struct perf_event *event)
* to whatever list we are on.
*/
list_for_each_entry_safe(sibling, tmp, &event->sibling_list, sibling_list) {
-
- /*
- * Events that have PERF_EV_CAP_SIBLING require being part of
- * a group and cannot exist on their own, schedule them out
- * and move them into the ERROR state. Also see
- * _perf_event_enable(), it will not be able to recover this
- * ERROR state.
- */
- if (sibling->event_caps & PERF_EV_CAP_SIBLING)
- __event_disable(sibling, ctx, PERF_EVENT_STATE_ERROR);
-
- sibling->group_leader = sibling;
list_del_init(&sibling->sibling_list);
/* Inherit group flags from the previous leader */
- sibling->group_caps = event->group_caps;
-
- if (sibling->attach_state & PERF_ATTACH_CONTEXT) {
- add_event_to_groups(sibling, event->ctx);
-
- if (sibling->state == PERF_EVENT_STATE_ACTIVE)
- list_add_tail(&sibling->active_list, get_event_list(sibling));
- }
+ perf_promote_sibling_to_leader(sibling, ctx, event->group_caps);
WARN_ON_ONCE(sibling->ctx != event->ctx);
}
+ event->nr_siblings = 0;
out:
for_each_sibling_event(tmp, leader)
@@ -2591,12 +2602,7 @@ __perf_remove_from_context(struct perf_event *event,
if (flags & DETACH_DEAD)
state = PERF_EVENT_STATE_DEAD;
- event_sched_out(event, ctx);
-
- if (event->state > PERF_EVENT_STATE_OFF)
- perf_cgroup_event_disable(event, ctx);
-
- perf_event_set_state(event, min(event->state, state));
+ __event_disable(event, ctx, state);
if (flags & DETACH_GROUP)
perf_group_detach(event);
@@ -2665,8 +2671,9 @@ static void __event_disable(struct perf_event *event,
enum perf_event_state state)
{
event_sched_out(event, ctx);
- perf_cgroup_event_disable(event, ctx);
- perf_event_set_state(event, state);
+ if (event->state > PERF_EVENT_STATE_OFF)
+ perf_cgroup_event_disable(event, ctx);
+ perf_event_set_state(event, min(event->state, state));
}
/*
diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index ff2a4fb2993f..6b3121c734bb 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -1027,8 +1027,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
@@ -1037,42 +1040,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/ext.c b/kernel/sched/ext.c
index 5c780756e362..d46662365a69 100644
--- a/kernel/sched/ext.c
+++ b/kernel/sched/ext.c
@@ -3581,6 +3581,9 @@ static int __scx_init_task(struct scx_sched *sch, struct task_struct *p, bool fo
} else if (unlikely(fork)) {
scx_error(sch, "ops.init_task() set task->scx.disallow for %s[%d] during fork",
p->comm, p->pid);
+ } else if (unlikely(scx_enable_state() != SCX_ENABLING)) {
+ scx_error(sch, "ops.init_task() set task->scx.disallow for %s[%d] outside the enable path",
+ p->comm, p->pid);
} else {
struct rq *rq;
struct rq_flags rf;
@@ -4512,20 +4515,25 @@ static struct cgroup *root_cgroup(void)
return &cgrp_dfl_root.cgrp;
}
+/*
+ * cgroup_lock() must nest outside the rwsem write side: a writer waiting
+ * for cgroup_mutex deadlocks with cgroup teardown, which holds it while
+ * draining a set_* file write blocked on the rwsem behind the writer.
+ */
static void scx_cgroup_lock(void)
{
+ cgroup_lock();
#ifdef CONFIG_EXT_GROUP_SCHED
percpu_down_write(&scx_cgroup_ops_rwsem);
#endif
- cgroup_lock();
}
static void scx_cgroup_unlock(void)
{
- cgroup_unlock();
#ifdef CONFIG_EXT_GROUP_SCHED
percpu_up_write(&scx_cgroup_ops_rwsem);
#endif
+ cgroup_unlock();
}
#else /* CONFIG_EXT_GROUP_SCHED || CONFIG_EXT_SUB_SCHED */
static struct cgroup *root_cgroup(void) { return NULL; }
@@ -5795,6 +5803,15 @@ static void scx_sub_disable(struct scx_sched *sch)
percpu_down_write(&scx_fork_rwsem);
scx_cgroup_lock();
+ /*
+ * An enable that failed before scx_link_sched() never owned a cgroup or
+ * task and won't be waited on by an ancestor's drain_descendants().
+ * Nothing to reparent and walking the tasks can misbehave as the task
+ * ownership invariant (either owned by self or parent) does not hold.
+ */
+ if (list_empty(&sch->sibling))
+ goto dump;
+
set_cgroup_sched(sch_cgroup(sch), parent);
scx_task_iter_start(&sti, sch->cgrp);
@@ -5807,8 +5824,8 @@ static void scx_sub_disable(struct scx_sched *sch)
continue;
/*
- * By the time control reaches here, all descendant schedulers
- * should already have been disabled.
+ * By the time control reaches here, all linked descendant
+ * schedulers should have been disabled.
*/
WARN_ON_ONCE(!scx_task_on_sched(sch, p));
@@ -5862,15 +5879,22 @@ static void scx_sub_disable(struct scx_sched *sch)
/*
* $p is initialized for $parent and still attached to
* @sch. Disable and exit for @sch, switch over to
- * $parent, override the state to READY to account for
- * $p having already been initialized, and then enable.
+ * $parent and override the state to READY to account
+ * for $p having already been initialized.
*/
scx_disable_and_exit_task(sch, p);
scx_set_task_state(p, SCX_TASK_INIT_BEGIN);
scx_set_task_state(p, SCX_TASK_INIT);
scx_set_task_sched(p, parent);
scx_set_task_state(p, SCX_TASK_READY);
- scx_enable_task(parent, p);
+
+ /*
+ * A task on a non-ext class, possible under an
+ * %SCX_OPS_SWITCH_PARTIAL root, stays READY and is
+ * enabled by switching_to_scx() if it switches over.
+ */
+ if (p->sched_class == &ext_sched_class)
+ scx_enable_task(parent, p);
}
task_rq_unlock(rq, p, &rf);
@@ -5878,6 +5902,7 @@ static void scx_sub_disable(struct scx_sched *sch)
}
scx_task_iter_stop(&sti);
+dump:
scx_disable_dump(sch);
scx_cgroup_unlock();
@@ -7364,10 +7389,14 @@ static void scx_sub_enable_workfn(struct kthread_work *work)
/*
* $p is now only initialized for @sch and READY, which
- * is what we want. Assign it to @sch and enable.
+ * is what we want. Assign it to @sch and, if it's on
+ * the ext class, enable. A non-ext task, possible under
+ * an %SCX_OPS_SWITCH_PARTIAL root, stays READY and is
+ * enabled by switching_to_scx() if it switches over.
*/
scx_set_task_sched(p, sch);
- scx_enable_task(sch, p);
+ if (p->sched_class == &ext_sched_class)
+ scx_enable_task(sch, p);
p->scx.flags &= ~SCX_TASK_SUB_INIT;
}
diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c
index d9c9d9480a45..e2e825dcd088 100644
--- a/kernel/sched/psi.c
+++ b/kernel/sched/psi.c
@@ -1134,6 +1134,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");
@@ -1292,9 +1298,44 @@ int psi_show(struct seq_file *m, struct psi_group *group, enum psi_res res)
return 0;
}
+/*
+ * Create @group's rtpoll worker after psi_trigger_create() reported the need
+ * for one. kthread creation depends on the whole fork path and we don't want
+ * all of that nested inside cgroup_mutex, so the caller must drop it and any
+ * other lock that forks can wait behind. If two callers race, the loser stops
+ * its never-woken kthread.
+ */
+int psi_trigger_create_rtpoll_worker(struct psi_group *group)
+{
+ struct task_struct *task;
+
+ task = kthread_create(psi_rtpoll_worker, group, "psimon");
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+
+ scoped_guard(mutex, &group->rtpoll_trigger_lock) {
+ if (!rcu_access_pointer(group->rtpoll_task)) {
+ atomic_set(&group->rtpoll_wakeup, 0);
+ wake_up_process(task);
+ rcu_assign_pointer(group->rtpoll_task, task);
+
+ /*
+ * Poll once to catch up on scheduling attempts dropped
+ * while there was no rtpoll worker.
+ */
+ psi_schedule_rtpoll_work(group, 1, true);
+ return 0;
+ }
+ }
+
+ kthread_stop(task);
+ return 0;
+}
+
struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf,
enum psi_res res, struct file *file,
- struct kernfs_open_file *of)
+ struct kernfs_open_file *of,
+ bool *need_rtpoll_worker)
{
struct psi_trigger *t;
enum psi_states state;
@@ -1302,6 +1343,8 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf,
bool privileged;
u32 window_us;
+ *need_rtpoll_worker = false;
+
if (static_branch_likely(&psi_disabled))
return ERR_PTR(-EOPNOTSUPP);
@@ -1362,26 +1405,14 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf,
if (privileged) {
mutex_lock(&group->rtpoll_trigger_lock);
- if (!rcu_access_pointer(group->rtpoll_task)) {
- struct task_struct *task;
-
- task = kthread_create(psi_rtpoll_worker, group, "psimon");
- if (IS_ERR(task)) {
- kfree(t);
- mutex_unlock(&group->rtpoll_trigger_lock);
- return ERR_CAST(task);
- }
- atomic_set(&group->rtpoll_wakeup, 0);
- wake_up_process(task);
- rcu_assign_pointer(group->rtpoll_task, task);
- }
-
list_add(&t->node, &group->rtpoll_triggers);
group->rtpoll_min_period = min(group->rtpoll_min_period,
div_u64(t->win.size, UPDATES_PER_WINDOW));
group->rtpoll_nr_triggers[t->state]++;
group->rtpoll_states |= (1 << t->state);
+ *need_rtpoll_worker = !rcu_access_pointer(group->rtpoll_task);
+
mutex_unlock(&group->rtpoll_trigger_lock);
} else {
mutex_lock(&group->avgs_lock);
@@ -1541,6 +1572,8 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf,
size_t buf_size;
struct seq_file *seq;
struct psi_trigger *new;
+ bool need_rtpoll_worker;
+ int ret;
if (static_branch_likely(&psi_disabled))
return -EOPNOTSUPP;
@@ -1565,12 +1598,22 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf,
return -EBUSY;
}
- new = psi_trigger_create(&psi_system, buf, res, file, NULL);
+ new = psi_trigger_create(&psi_system, buf, res, file, NULL,
+ &need_rtpoll_worker);
if (IS_ERR(new)) {
mutex_unlock(&seq->lock);
return PTR_ERR(new);
}
+ if (need_rtpoll_worker) {
+ ret = psi_trigger_create_rtpoll_worker(&psi_system);
+ if (ret) {
+ psi_trigger_destroy(new);
+ mutex_unlock(&seq->lock);
+ return ret;
+ }
+ }
+
smp_store_release(&seq->private, new);
mutex_unlock(&seq->lock);
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index d2e2cf18bfdb..ae48017c45dd 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -2400,6 +2400,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)
bpage = alloc_cpu_page(cpu);
if (!bpage)
return NULL;
+ bpage->order = cpu_buffer->buffer->subbuf_order;
rb_check_bpage(cpu_buffer, bpage);
@@ -2418,6 +2419,8 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)
if (cpu_buffer->ring_meta->head_buffer)
rb_meta_buffer_update(cpu_buffer, bpage);
bpage->range = 1;
+
+ atomic_inc(&cpu_buffer->resize_disabled);
} else if (buffer->remote) {
struct ring_buffer_desc *desc = ring_buffer_desc(buffer->remote->desc, cpu);
@@ -6680,7 +6683,7 @@ int ring_buffer_swap_cpu(struct trace_buffer *buffer_a,
{
struct ring_buffer_per_cpu *cpu_buffer_a;
struct ring_buffer_per_cpu *cpu_buffer_b;
- int ret = -EINVAL;
+ int ret = -EBUSY;
if (!cpumask_test_cpu(cpu, buffer_a->cpumask) ||
!cpumask_test_cpu(cpu, buffer_b->cpumask))
@@ -6721,10 +6724,10 @@ int ring_buffer_swap_cpu(struct trace_buffer *buffer_a,
atomic_inc(&cpu_buffer_a->record_disabled);
atomic_inc(&cpu_buffer_b->record_disabled);
- ret = -EBUSY;
- if (local_read(&cpu_buffer_a->committing))
+ /* Do not swap if either buffer is in the process of writing */
+ if (cpu_buffer_a->current_context)
goto out_dec;
- if (local_read(&cpu_buffer_b->committing))
+ if (cpu_buffer_b->current_context)
goto out_dec;
/*
@@ -7168,7 +7171,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;
}
@@ -8024,7 +8027,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 c01b10b99f67..3650d84d4f16 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -945,7 +945,7 @@ static int remove_cache_mod(struct trace_array *tr, const char *mod,
if (strcmp(event_mod->module, mod) != 0)
continue;
- if (match && strcmp(event_mod->match, match) != 0)
+ if (match && (!event_mod->match || strcmp(event_mod->match, match) != 0))
continue;
if (system &&
@@ -3566,6 +3566,7 @@ void trace_event_update_all(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 */
@@ -3604,6 +3605,7 @@ void trace_event_update_all(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/mm/damon/lru_sort.c b/mm/damon/lru_sort.c
index 8cfe7bd3dc1d..c61c3d3a5ffd 100644
--- a/mm/damon/lru_sort.c
+++ b/mm/damon/lru_sort.c
@@ -234,6 +234,8 @@ static int damon_lru_sort_add_quota_goals(struct damos *hot_scheme,
if (!active_mem_bp)
return 0;
+ if (10000 < active_mem_bp)
+ return -EINVAL;
goal = damos_new_quota_goal(DAMOS_QUOTA_ACTIVE_MEM_BP, active_mem_bp);
if (!goal)
return -ENOMEM;
diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c
index 4752d6118eb9..cca7014ab5f1 100644
--- a/mm/damon/ops-common.c
+++ b/mm/damon/ops-common.c
@@ -374,6 +374,8 @@ static unsigned int damon_migrate_folio_list(struct list_head *folio_list,
while (!list_empty(folio_list)) {
folio = lru_to_folio(folio_list);
list_del(&folio->lru);
+ node_stat_sub_folio(folio, NR_ISOLATED_ANON +
+ folio_is_file_lru(folio));
folio_putback_lru(folio);
}
@@ -391,8 +393,17 @@ unsigned long damon_migrate_pages(struct list_head *folio_list, int target_nid)
return nr_migrated;
if (target_nid < 0 || target_nid >= MAX_NUMNODES ||
- !node_state(target_nid, N_MEMORY))
+ !node_state(target_nid, N_MEMORY)) {
+ while (!list_empty(folio_list)) {
+ struct folio *folio = lru_to_folio(folio_list);
+
+ list_del(&folio->lru);
+ node_stat_sub_folio(folio, NR_ISOLATED_ANON +
+ folio_is_file_lru(folio));
+ folio_putback_lru(folio);
+ }
return nr_migrated;
+ }
noreclaim_flag = memalloc_noreclaim_save();
diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c
index 5cdcc5037cbc..e701ffa8506b 100644
--- a/mm/damon/paddr.c
+++ b/mm/damon/paddr.c
@@ -277,6 +277,8 @@ static unsigned long damon_pa_migrate(struct damon_region *r,
if (!folio_isolate_lru(folio))
goto put_folio;
+ node_stat_add_folio(folio, NR_ISOLATED_ANON +
+ folio_is_file_lru(folio));
list_add(&folio->lru, &folio_list);
put_folio:
addr += folio_size(folio);
diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c
index b069dbc7e3d2..9845ceeedc0b 100644
--- a/mm/damon/vaddr.c
+++ b/mm/damon/vaddr.c
@@ -627,7 +627,8 @@ static void damos_va_migrate_dests_add(struct folio *folio,
isolate:
if (!folio_isolate_lru(folio))
return;
-
+ node_stat_add_folio(folio, NR_ISOLATED_ANON +
+ folio_is_file_lru(folio));
list_add(&folio->lru, &migration_lists[i]);
}
diff --git a/mm/filemap.c b/mm/filemap.c
index b72f8c23c322..bbd400d893aa 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -932,6 +932,12 @@ noinline int __filemap_add_folio(struct address_space *mapping,
if (!xas_nomem(&xas, gfp))
break;
+
+ /*
+ * Lock has been dropped: start again with the original index
+ * and order (but now with the memory reserved by xas_nomem()).
+ */
+ xas_set_order(&xas, index, forder);
}
if (xas_error(&xas))
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 7a44c428d187..6916def3ce57 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -40,6 +40,7 @@
#include <linux/pgalloc.h>
#include <linux/pgalloc_tag.h>
#include <linux/pagewalk.h>
+#include <linux/cleanup.h>
#include <asm/tlb.h>
#include "internal.h"
@@ -75,6 +76,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;
@@ -221,7 +223,8 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
static bool get_huge_zero_folio(void)
{
struct folio *zero_folio;
-retry:
+
+ /* Paired with atomic_set_release(). */
if (likely(atomic_inc_not_zero(&huge_zero_refcount)))
return true;
@@ -234,17 +237,22 @@ static bool get_huge_zero_folio(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;
}
@@ -294,15 +302,22 @@ static unsigned long shrink_huge_zero_folio_count(struct shrinker *shrink,
static unsigned long shrink_huge_zero_folio_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_folio_shrinker;
@@ -4108,7 +4123,7 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
gfp_t gfp;
mapping = folio->mapping;
- min_order = mapping_min_folio_order(folio->mapping);
+ min_order = mapping_min_folio_order(mapping);
if (new_order < min_order) {
ret = -EINVAL;
goto out;
@@ -4122,6 +4137,8 @@ static int __folio_split(struct folio *folio, unsigned int new_order,
goto out;
}
+ mapping_set_update(&xas, mapping);
+
if (split_type == SPLIT_TYPE_UNIFORM) {
xas_set_order(&xas, folio->index, new_order);
xas_split_alloc(&xas, folio, old_order, gfp);
diff --git a/mm/memory.c b/mm/memory.c
index 86a973119bd4..13b70861c8a3 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1981,7 +1981,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
if (can_reclaim_pt) {
if (direct_reclaim || zap_pte_table_if_empty(mm, pmd, start, &pmdval)) {
- pte_free_tlb(tlb, pmd_pgtable(pmdval), addr);
+ pte_free_tlb(tlb, pmd_pgtable(pmdval), start);
mm_dec_nr_ptes(mm);
}
}
diff --git a/mm/page_table_check.c b/mm/page_table_check.c
index 53a8997ec043..2403f5a11410 100644
--- a/mm/page_table_check.c
+++ b/mm/page_table_check.c
@@ -151,18 +151,29 @@ void __page_table_check_pte_clear(struct mm_struct *mm, unsigned long addr,
if (&init_mm == mm)
return;
- if (pte_user_accessible_page(mm, addr, pte))
+ if (pte_user_accessible_page(mm, addr, pte) && !pte_special(pte))
page_table_check_clear(pte_pfn(pte), PAGE_SIZE >> PAGE_SHIFT);
}
EXPORT_SYMBOL(__page_table_check_pte_clear);
+static inline bool page_table_check_huge_zero_pmd(pmd_t pmd)
+{
+ unsigned long pfn = pmd_pfn(pmd);
+
+ if (!pfn_valid(pfn))
+ return false;
+
+ return is_huge_zero_folio(page_folio(pfn_to_page(pfn)));
+}
+
void __page_table_check_pmd_clear(struct mm_struct *mm, unsigned long addr,
pmd_t pmd)
{
if (&init_mm == mm)
return;
- if (pmd_user_accessible_page(mm, addr, pmd))
+ if (pmd_user_accessible_page(mm, addr, pmd) &&
+ !page_table_check_huge_zero_pmd(pmd))
page_table_check_clear(pmd_pfn(pmd), PMD_SIZE >> PAGE_SHIFT);
}
EXPORT_SYMBOL(__page_table_check_pmd_clear);
@@ -208,7 +219,7 @@ void __page_table_check_ptes_set(struct mm_struct *mm, unsigned long addr,
for (i = 0; i < nr; i++)
__page_table_check_pte_clear(mm, addr + PAGE_SIZE * i, ptep_get(ptep + i));
- if (pte_user_accessible_page(mm, addr, pte))
+ if (pte_user_accessible_page(mm, addr, pte) && !pte_special(pte))
page_table_check_set(pte_pfn(pte), nr, pte_write(pte));
}
EXPORT_SYMBOL(__page_table_check_ptes_set);
@@ -238,7 +249,8 @@ void __page_table_check_pmds_set(struct mm_struct *mm, unsigned long addr,
for (i = 0; i < nr; i++)
__page_table_check_pmd_clear(mm, addr + PMD_SIZE * i, *(pmdp + i));
- if (pmd_user_accessible_page(mm, addr, pmd))
+ if (pmd_user_accessible_page(mm, addr, pmd) &&
+ !page_table_check_huge_zero_pmd(pmd))
page_table_check_set(pmd_pfn(pmd), stride * nr, pmd_write(pmd));
}
EXPORT_SYMBOL(__page_table_check_pmds_set);
diff --git a/mm/pagewalk.c b/mm/pagewalk.c
index 3ae2586ff45b..5d87c632a255 100644
--- a/mm/pagewalk.c
+++ b/mm/pagewalk.c
@@ -678,6 +678,8 @@ int walk_kernel_page_table_range_lockless(unsigned long start, unsigned long end
* will also not lock the PTEs for the pte_entry() callback.
*
* This is for debugging purposes ONLY.
+ *
+ * The mmap write lock must be held.
*/
int walk_page_range_debug(struct mm_struct *mm, unsigned long start,
unsigned long end, const struct mm_walk_ops *ops,
@@ -691,24 +693,28 @@ int walk_page_range_debug(struct mm_struct *mm, unsigned long start,
.no_vma = true
};
- /* For convenience, we allow traversal of kernel mappings. */
- if (mm == &init_mm)
- return walk_kernel_page_table_range(start, end, ops,
- pgd, private);
- if (start >= end || !walk.mm)
- return -EINVAL;
- if (!check_ops_safe(ops))
- return -EINVAL;
-
/*
- * The mmap lock protects the page walker from changes to the page
- * tables during the walk. However a read lock is insufficient to
- * protect those areas which don't have a VMA as munmap() detaches
- * the VMAs before downgrading to a read lock and actually tearing
- * down PTEs/page tables. In which case, the mmap write lock should
- * be held.
+ * When walking userland page tables, an mmap write lock must be held to
+ * account for munmap() downgrading to an mmap read lock when tearing
+ * down page tables.
+ *
+ * When walking kernel page tables, an mmap write lock must also be held
+ * to account for page table freeing on vmap huge page mapping.
*/
mmap_assert_write_locked(mm);
+ /*
+ * x86, arm64 ptdump allow walks of efi mm's and x86 ptdump allows walks
+ * of arbitrary mm's.
+ *
+ * However, they both must also hold the init_mm lock to account for
+ * concurrent kernel page table freeing.
+ */
+ mmap_assert_write_locked(&init_mm);
+
+ if (start >= end)
+ return -EINVAL;
+ if (!check_ops_safe(ops))
+ return -EINVAL;
return walk_pgd_range(start, end, &walk);
}
diff --git a/mm/ptdump.c b/mm/ptdump.c
index 973020000096..5851096e6f65 100644
--- a/mm/ptdump.c
+++ b/mm/ptdump.c
@@ -178,11 +178,18 @@ void ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm, pgd_t *pgd)
get_online_mems();
mmap_write_lock(mm);
+ /* To stabilise kernel page tables we must hold the init_mm lock too. */
+ if (mm != &init_mm)
+ mmap_write_lock_nested(&init_mm, SINGLE_DEPTH_NESTING);
+
while (range->start != range->end) {
walk_page_range_debug(mm, range->start, range->end,
&ptdump_ops, pgd, st);
range++;
}
+
+ if (mm != &init_mm)
+ mmap_write_unlock(&init_mm);
mmap_write_unlock(mm);
put_online_mems();
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index bb6ae08d18f5..a22358f8cc2f 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -43,6 +43,7 @@
#include <asm/tlbflush.h>
#include <asm/shmparam.h>
#include <linux/page_owner.h>
+#include <linux/cleanup.h>
#define CREATE_TRACE_POINTS
#include <trace/events/vmalloc.h>
@@ -158,10 +159,21 @@ static int vmap_try_huge_pmd(pmd_t *pmd, unsigned long addr, unsigned long end,
if (!IS_ALIGNED(phys_addr, PMD_SIZE))
return 0;
- if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
- return 0;
+ if (!pmd_present(*pmd))
+ return pmd_set_huge(pmd, phys_addr, prot);
- return pmd_set_huge(pmd, phys_addr, prot);
+ /*
+ * Acquire the mmap read lock to exclude ptdump, which walks
+ * kernel page tables it does not own under the mmap write lock.
+ *
+ * Concurrent read lock holders are safe: each exclusively owns
+ * the range it operates on and cannot reach this page table.
+ */
+ scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) {
+ if (!pmd_free_pte_page(pmd, addr))
+ return 0;
+ return pmd_set_huge(pmd, phys_addr, prot);
+ }
}
static int vmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
@@ -210,10 +222,15 @@ static int vmap_try_huge_pud(pud_t *pud, unsigned long addr, unsigned long end,
if (!IS_ALIGNED(phys_addr, PUD_SIZE))
return 0;
- if (pud_present(*pud) && !pud_free_pmd_page(pud, addr))
- return 0;
+ if (!pud_present(*pud))
+ return pud_set_huge(pud, phys_addr, prot);
- return pud_set_huge(pud, phys_addr, prot);
+ /* See comment in vmap_try_huge_pmd(). */
+ scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) {
+ if (!pud_free_pmd_page(pud, addr))
+ return 0;
+ return pud_set_huge(pud, phys_addr, prot);
+ }
}
static int vmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
@@ -262,10 +279,15 @@ static int vmap_try_huge_p4d(p4d_t *p4d, unsigned long addr, unsigned long end,
if (!IS_ALIGNED(phys_addr, P4D_SIZE))
return 0;
- if (p4d_present(*p4d) && !p4d_free_pud_page(p4d, addr))
- return 0;
+ if (!p4d_present(*p4d))
+ return p4d_set_huge(p4d, phys_addr, prot);
- return p4d_set_huge(p4d, phys_addr, prot);
+ /* See comment in vmap_try_huge_pmd(). */
+ scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) {
+ if (!p4d_free_pud_page(p4d, addr))
+ return 0;
+ return p4d_set_huge(p4d, phys_addr, prot);
+ }
}
static int vmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
diff --git a/net/atm/common.c b/net/atm/common.c
index 6eb78c34c284..c3fbbb63c5d8 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 460be392af47..31160bc09ee7 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 58a33d0380b0..7ecb8a26bfa3 100644
--- a/net/bridge/netfilter/nf_conntrack_bridge.c
+++ b/net/bridge/netfilter/nf_conntrack_bridge.c
@@ -280,6 +280,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/datagram.c b/net/core/datagram.c
index c285c6465923..173b5d97bd40 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -712,6 +712,9 @@ zerocopy_fill_skb_from_devmem(struct sk_buff *skb, struct iov_iter *from,
size_t virt_addr, size, off;
struct net_iov *niov;
+ if (i && skb_frags_readable(skb))
+ return -EFAULT;
+
/* Devmem filling works by taking an IOVEC from the user where the
* iov_addrs are interpreted as an offset in bytes into the dma-buf to
* send from. We do not support other iter types.
diff --git a/net/core/dev.c b/net/core/dev.c
index 31741169cb0b..58ec448e8357 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5515,12 +5515,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/filter.c b/net/core/filter.c
index 66837e93d3f2..233adfdc1c34 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -7645,7 +7645,7 @@ BPF_CALL_5(bpf_tcp_check_syncookie, struct sock *, sk, void *, iph, u32, iph_len
return -EINVAL;
/* sk_listener() allows TCP_NEW_SYN_RECV, which makes no sense here. */
- if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN)
+ if (sk->sk_state != TCP_LISTEN || sk->sk_protocol != IPPROTO_TCP)
return -EINVAL;
if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies))
@@ -7718,7 +7718,7 @@ BPF_CALL_5(bpf_tcp_gen_syncookie, struct sock *, sk, void *, iph, u32, iph_len,
if (unlikely(!sk || th_len < sizeof(*th) || th_len != th->doff * 4))
return -EINVAL;
- if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN)
+ if (sk->sk_state != TCP_LISTEN || sk->sk_protocol != IPPROTO_TCP)
return -EINVAL;
if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies))
diff --git a/net/core/sock.c b/net/core/sock.c
index 5714436b0871..aaa3bb214eff 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -779,7 +779,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 9890a30584ba..0194e69da339 100644
--- a/net/core/xdp.c
+++ b/net/core/xdp.c
@@ -871,7 +871,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 57b2b8f03543..fd5633fa88ec 100644
--- a/net/devlink/dev.c
+++ b/net/devlink/dev.c
@@ -578,6 +578,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 3e8fadc28798..49ffb8db2350 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -491,6 +491,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))
@@ -508,32 +536,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;
@@ -1868,42 +1899,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 690f7fb3f029..935b46db6154 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -946,11 +946,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/inet_fragment.c b/net/ipv4/inet_fragment.c
index 1127519b8416..8fdd19310efd 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -395,8 +395,8 @@ static struct inet_frag_queue *inet_frag_create(struct fqdir *fqdir,
*prev = ERR_PTR(-ENOMEM);
return NULL;
}
- mod_timer(&q->timer, jiffies + fqdir->timeout);
+ spin_lock_bh(&q->lock);
*prev = rhashtable_lookup_get_insert_key(&fqdir->rhashtable, &q->key,
&q->node, f->rhash_params);
if (*prev) {
@@ -404,13 +404,13 @@ static struct inet_frag_queue *inet_frag_create(struct fqdir *fqdir,
* we need to cancel what inet_frag_alloc()
* anticipated.
*/
- int refs = 1;
-
q->flags |= INET_FRAG_COMPLETE;
- inet_frag_kill(q, &refs);
- inet_frag_putn(q, refs);
+ spin_unlock_bh(&q->lock);
+ inet_frag_putn(q, 2);
return NULL;
}
+ mod_timer(&q->timer, jiffies + fqdir->timeout);
+ spin_unlock_bh(&q->lock);
return q;
}
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 7cc9a7336c30..5c6d7e6995b2 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -741,6 +741,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 b9f036633365..18b45950d1ac 100644
--- a/net/ipv4/tcp_bpf.c
+++ b/net/ipv4/tcp_bpf.c
@@ -454,6 +454,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. */
@@ -474,6 +475,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 eed808c2adbb..e2c022f02c64 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -252,7 +252,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 ce572025eeeb..4b59d258c266 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -3079,24 +3079,24 @@ static unsigned int bpf_iter_tcp_established_batch(struct seq_file *seq,
{
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++].sk = *start_sk;
+ struct sock *sk = *start_sk;
+ unsigned int expected = 0;
- 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 = sk;
- } else if (!*start_sk) {
- /* Remember where we left off. */
- *start_sk = sk;
- }
- expected++;
+ 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;
}
}
@@ -3134,12 +3134,13 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq)
struct sock *sk;
int err;
+again:
sk = bpf_iter_tcp_resume(seq);
if (!sk)
return NULL; /* Done */
expected = bpf_iter_fill_batch(seq, &sk);
- if (likely(iter->end_sk == expected))
+ if (likely(!sk))
goto done;
/* Batch size was too small. */
@@ -3158,7 +3159,7 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq)
return NULL; /* Done */
expected = bpf_iter_fill_batch(seq, &sk);
- if (likely(iter->end_sk == expected))
+ if (likely(!sk))
goto done;
/* Batch size was still too small. Hold onto the lock while we try
@@ -3171,10 +3172,14 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq)
return ERR_PTR(err);
}
- expected = bpf_iter_fill_batch(seq, &sk);
- WARN_ON_ONCE(iter->end_sk != expected);
+ 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 iter->batch[0].sk;
}
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 29651b1a0bc7..abcd3cc2d9a3 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -178,17 +178,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 95da1f087294..49f719ab55a9 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -684,6 +684,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 d4edc133c045..f2cc5476553f 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -987,13 +987,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 65089826ff59..005338f89b75 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 bb9b5bed10e1..226fdf17b683 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 6cfad152d7d1..822a53a7f502 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 a3ed683cc47e..3072b4e28853 100644
--- a/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/net/netfilter/ipset/ip_set_hash_gen.h
@@ -1288,7 +1288,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 1cef84f15e8c..ca3ef9479e83 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 fc1dd4c463b1..e4ca8fd4b658 100644
--- a/net/netfilter/ipvs/ip_vs_conn.c
+++ b/net/netfilter/ipvs/ip_vs_conn.c
@@ -1081,12 +1081,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.
@@ -1147,23 +1141,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;
}
@@ -1244,30 +1237,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);
}
@@ -1296,7 +1279,7 @@ int ip_vs_check_template(struct ip_vs_conn *ct, struct ip_vs_dest *cdest)
* Checking the dest server status.
*/
if ((dest == NULL) ||
- !(dest->flags & IP_VS_DEST_F_AVAILABLE) ||
+ !(dest->cflags & IP_VS_DEST_CF_AVAILABLE) ||
expire_quiescent_template(ipvs, dest) ||
(cdest && (dest != cdest))) {
IP_VS_DBG_BUF(9, "check_template: dest not available for "
@@ -2029,7 +2012,7 @@ void ip_vs_expire_nodest_conn_flush(struct netns_ipvs *ipvs)
cp = ip_vs_hn0_to_conn(hn);
resched_score++;
dest = cp->dest;
- if (!dest || (dest->flags & IP_VS_DEST_F_AVAILABLE))
+ if (!dest || (dest->cflags & IP_VS_DEST_CF_AVAILABLE))
continue;
if (atomic_read(&cp->n_control))
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index 6b79e0c4d9e2..a46e7acdd8e1 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -302,7 +302,7 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
struct ip_vs_dest *dest = cp->dest;
struct netns_ipvs *ipvs = cp->ipvs;
- if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+ if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
struct ip_vs_cpu_stats *s;
struct ip_vs_service *svc;
@@ -338,7 +338,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
struct ip_vs_dest *dest = cp->dest;
struct netns_ipvs *ipvs = cp->ipvs;
- if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+ if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
struct ip_vs_cpu_stats *s;
struct ip_vs_service *svc;
@@ -925,28 +925,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;
@@ -960,10 +959,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");
}
@@ -1056,7 +1055,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;
@@ -1092,7 +1091,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;
@@ -1105,7 +1104,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)
@@ -1131,11 +1129,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;
@@ -1146,8 +1148,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);
@@ -1155,8 +1155,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
@@ -1803,10 +1803,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;
@@ -1823,9 +1825,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 */
@@ -1850,21 +1849,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;
@@ -1872,9 +1875,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.
*/
@@ -1904,11 +1904,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) {
@@ -1949,6 +1950,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;
@@ -2209,7 +2211,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
}
/* Check the server status */
- if (cp && cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+ if (cp && cp->dest && !(cp->dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
/* the destination server is not available */
if (sysctl_expire_nodest_conn(ipvs)) {
bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb);
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 16daba8cac83..ad3583d8174b 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -1304,6 +1304,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
*/
@@ -1368,12 +1402,21 @@ __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;
+ dest->cflags |= IP_VS_DEST_CF_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;
@@ -1445,7 +1488,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);
@@ -1486,6 +1529,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__);
@@ -1559,6 +1605,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__);
@@ -1613,7 +1662,7 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc,
struct ip_vs_dest *dest,
int svcupd)
{
- dest->flags &= ~IP_VS_DEST_F_AVAILABLE;
+ dest->cflags &= ~IP_VS_DEST_CF_AVAILABLE;
spin_lock_bh(&dest->dst_lock);
__ip_vs_dst_cache_reset(dest);
@@ -2986,7 +3035,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,
@@ -2997,7 +3046,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));
}
}
@@ -3622,10 +3671,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);
@@ -4232,12 +4281,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_dh.c b/net/netfilter/ipvs/ip_vs_dh.c
index e1f62f6b25e2..43abed7a26a6 100644
--- a/net/netfilter/ipvs/ip_vs_dh.c
+++ b/net/netfilter/ipvs/ip_vs_dh.c
@@ -219,8 +219,8 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
s = (struct ip_vs_dh_state *) svc->sched_data;
dest = ip_vs_dh_get(svc->af, s, &iph->daddr);
- if (!dest
- || !(dest->flags & IP_VS_DEST_F_AVAILABLE)
+ if (!dest ||
+ !(dest->cflags & IP_VS_DEST_CF_AVAILABLE)
|| atomic_read(&dest->weight) <= 0
|| is_overloaded(dest)) {
ip_vs_scheduler_err(svc, "no destination available");
diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c
index ab09f5182951..05a216a47b45 100644
--- a/net/netfilter/ipvs/ip_vs_est.c
+++ b/net/netfilter/ipvs/ip_vs_est.c
@@ -191,8 +191,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) {
@@ -270,6 +273,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));
if (sysctl_est_preferred_cpulist(ipvs))
@@ -286,7 +290,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;
}
}
@@ -526,7 +530,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_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c
index 15ccb2b2fa1f..693bcc82ccb7 100644
--- a/net/netfilter/ipvs/ip_vs_lblc.c
+++ b/net/netfilter/ipvs/ip_vs_lblc.c
@@ -502,7 +502,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
dest = en->dest;
- if ((dest->flags & IP_VS_DEST_F_AVAILABLE) &&
+ if ((dest->cflags & IP_VS_DEST_CF_AVAILABLE) &&
atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc))
goto out;
}
diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c
index c90ea897c3f7..f53f05ceea36 100644
--- a/net/netfilter/ipvs/ip_vs_lblcr.c
+++ b/net/netfilter/ipvs/ip_vs_lblcr.c
@@ -169,8 +169,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
if (least->flags & IP_VS_DEST_F_OVERLOAD)
continue;
- if ((atomic_read(&least->weight) > 0)
- && (least->flags & IP_VS_DEST_F_AVAILABLE)) {
+ if ((atomic_read(&least->weight) > 0) &&
+ (least->cflags & IP_VS_DEST_CF_AVAILABLE)) {
loh = ip_vs_dest_conn_overhead(least);
goto nextstage;
}
@@ -186,8 +186,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
doh = ip_vs_dest_conn_overhead(dest);
if (((__s64)loh * atomic_read(&dest->weight) >
- (__s64)doh * atomic_read(&least->weight))
- && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+ (__s64)doh * atomic_read(&least->weight)) &&
+ (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
least = dest;
loh = doh;
}
diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c
index 38cc38c5d8bb..18b976a7c4d2 100644
--- a/net/netfilter/ipvs/ip_vs_lc.c
+++ b/net/netfilter/ipvs/ip_vs_lc.c
@@ -30,7 +30,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
@@ -56,7 +56,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 99a286fdc90c..fec0e8b47b71 100644
--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
@@ -526,12 +526,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 93038abbf5e0..ea5fdd4f4ce7 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -878,13 +878,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 0b0c5304993a..fc7403186394 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -351,7 +351,7 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
* stored in dest_trash.
*/
if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)) &&
- dest->flags & IP_VS_DEST_F_AVAILABLE)
+ dest->cflags & IP_VS_DEST_CF_AVAILABLE)
__ip_vs_dst_set(dest, dest_dst, &rt->dst, 0);
else
noref = 0;
@@ -530,7 +530,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
* stored in dest_trash.
*/
if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)) &&
- dest->flags & IP_VS_DEST_F_AVAILABLE)
+ dest->cflags & IP_VS_DEST_CF_AVAILABLE)
__ip_vs_dst_set(dest, dest_dst, &rt->dst, cookie);
else
noref = 0;
@@ -1580,7 +1580,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_bpf.c b/net/netfilter/nf_conntrack_bpf.c
index f98d1d4b42c3..c2df7c948281 100644
--- a/net/netfilter/nf_conntrack_bpf.c
+++ b/net/netfilter/nf_conntrack_bpf.c
@@ -122,42 +122,54 @@ __bpf_nf_ct_alloc_entry(struct net *net, struct bpf_sock_tuple *bpf_tuple,
struct nf_conntrack_tuple otuple, rtuple;
struct nf_conntrack_zone ct_zone;
struct nf_conn *ct;
+ u8 ct_zone_dir = 0;
+ u16 ct_zone_id;
+ s32 netns_id;
+ u8 l4proto;
int err;
if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12))
return ERR_PTR(-EINVAL);
+
+ netns_id = READ_ONCE(opts->netns_id);
+ l4proto = READ_ONCE(opts->l4proto);
+ ct_zone_id = READ_ONCE(opts->ct_zone_id);
if (opts_len == NF_BPF_CT_OPTS_SZ) {
- if (opts->reserved[0] || opts->reserved[1] || opts->reserved[2])
+ ct_zone_dir = READ_ONCE(opts->ct_zone_dir);
+ if (READ_ONCE(opts->reserved[0]) ||
+ READ_ONCE(opts->reserved[1]) ||
+ READ_ONCE(opts->reserved[2]))
return ERR_PTR(-EINVAL);
} else {
- if (opts->ct_zone_id)
+ if (ct_zone_id)
return ERR_PTR(-EINVAL);
}
- if (unlikely(opts->netns_id < BPF_F_CURRENT_NETNS))
+ if (unlikely(netns_id < BPF_F_CURRENT_NETNS))
return ERR_PTR(-EINVAL);
- err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, opts->l4proto,
+ err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, l4proto,
IP_CT_DIR_ORIGINAL, &otuple);
if (err < 0)
return ERR_PTR(err);
- err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, opts->l4proto,
+ err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, l4proto,
IP_CT_DIR_REPLY, &rtuple);
if (err < 0)
return ERR_PTR(err);
- if (opts->netns_id >= 0) {
- net = get_net_ns_by_id(net, opts->netns_id);
+ if (netns_id >= 0) {
+ net = get_net_ns_by_id(net, netns_id);
if (unlikely(!net))
return ERR_PTR(-ENONET);
}
if (opts_len == NF_BPF_CT_OPTS_SZ) {
- if (opts->ct_zone_dir == 0)
- opts->ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR;
- nf_ct_zone_init(&ct_zone,
- opts->ct_zone_id, opts->ct_zone_dir, 0);
+ if (ct_zone_dir == 0) {
+ ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR;
+ opts->ct_zone_dir = ct_zone_dir;
+ }
+ nf_ct_zone_init(&ct_zone, ct_zone_id, ct_zone_dir, 0);
} else {
ct_zone = nf_ct_zone_dflt;
}
@@ -171,7 +183,7 @@ __bpf_nf_ct_alloc_entry(struct net *net, struct bpf_sock_tuple *bpf_tuple,
__nf_ct_set_timeout(ct, timeout * HZ);
out:
- if (opts->netns_id >= 0)
+ if (netns_id >= 0)
put_net(net);
return ct;
@@ -186,46 +198,58 @@ static struct nf_conn *__bpf_nf_ct_lookup(struct net *net,
struct nf_conntrack_tuple tuple;
struct nf_conntrack_zone ct_zone;
struct nf_conn *ct;
+ u8 ct_zone_dir = 0;
+ u16 ct_zone_id;
+ s32 netns_id;
+ u8 l4proto;
int err;
if (!opts || !bpf_tuple)
return ERR_PTR(-EINVAL);
if (!(opts_len == NF_BPF_CT_OPTS_SZ || opts_len == 12))
return ERR_PTR(-EINVAL);
+
+ netns_id = READ_ONCE(opts->netns_id);
+ l4proto = READ_ONCE(opts->l4proto);
+ ct_zone_id = READ_ONCE(opts->ct_zone_id);
if (opts_len == NF_BPF_CT_OPTS_SZ) {
- if (opts->reserved[0] || opts->reserved[1] || opts->reserved[2])
+ ct_zone_dir = READ_ONCE(opts->ct_zone_dir);
+ if (READ_ONCE(opts->reserved[0]) ||
+ READ_ONCE(opts->reserved[1]) ||
+ READ_ONCE(opts->reserved[2]))
return ERR_PTR(-EINVAL);
} else {
- if (opts->ct_zone_id)
+ if (ct_zone_id)
return ERR_PTR(-EINVAL);
}
- if (unlikely(opts->l4proto != IPPROTO_TCP && opts->l4proto != IPPROTO_UDP))
+ if (unlikely(l4proto != IPPROTO_TCP && l4proto != IPPROTO_UDP))
return ERR_PTR(-EPROTO);
- if (unlikely(opts->netns_id < BPF_F_CURRENT_NETNS))
+ if (unlikely(netns_id < BPF_F_CURRENT_NETNS))
return ERR_PTR(-EINVAL);
- err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, opts->l4proto,
+ err = bpf_nf_ct_tuple_parse(bpf_tuple, tuple_len, l4proto,
IP_CT_DIR_ORIGINAL, &tuple);
if (err < 0)
return ERR_PTR(err);
- if (opts->netns_id >= 0) {
- net = get_net_ns_by_id(net, opts->netns_id);
+ if (netns_id >= 0) {
+ net = get_net_ns_by_id(net, netns_id);
if (unlikely(!net))
return ERR_PTR(-ENONET);
}
if (opts_len == NF_BPF_CT_OPTS_SZ) {
- if (opts->ct_zone_dir == 0)
- opts->ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR;
- nf_ct_zone_init(&ct_zone,
- opts->ct_zone_id, opts->ct_zone_dir, 0);
+ if (ct_zone_dir == 0) {
+ ct_zone_dir = NF_CT_DEFAULT_ZONE_DIR;
+ opts->ct_zone_dir = ct_zone_dir;
+ }
+ nf_ct_zone_init(&ct_zone, ct_zone_id, ct_zone_dir, 0);
} else {
ct_zone = nf_ct_zone_dflt;
}
hash = nf_conntrack_find_get(net, &ct_zone, &tuple);
- if (opts->netns_id >= 0)
+ if (netns_id >= 0)
put_net(net);
if (!hash)
return ERR_PTR(-ENOENT);
diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c
index 50ddd3d613e1..e00957719351 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 7e10fa65cbdd..71cc920f6856 100644
--- a/net/netfilter/nf_conntrack_proto_sctp.c
+++ b/net/netfilter/nf_conntrack_proto_sctp.c
@@ -336,10 +336,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;
@@ -451,10 +453,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;
}
@@ -529,6 +529,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/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index 0b78decce8a9..c9e332fafcb5 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -310,6 +310,7 @@ static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb,
struct dst_entry *dst)
{
skb_orphan(skb);
+ skb_dst_drop(skb);
skb_dst_set_noref(skb, dst);
dst_output(state->net, state->sk, skb);
return NF_STOLEN;
@@ -861,6 +862,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
return NF_DROP;
}
xmit.dest = neigh->ha;
+ skb_dst_drop(skb);
skb_dst_set_noref(skb, &rt->dst);
break;
case FLOW_OFFLOAD_XMIT_DIRECT:
@@ -1178,6 +1180,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
return NF_DROP;
}
xmit.dest = neigh->ha;
+ skb_dst_drop(skb);
skb_dst_set_noref(skb, &rt->dst);
break;
case FLOW_OFFLOAD_XMIT_DIRECT:
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index eaf332b156d7..ae69b2cabab9 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -1113,9 +1113,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,
@@ -1127,6 +1126,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 66366982f604..46c1d66aad8c 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -889,8 +889,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.
@@ -910,8 +908,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;
@@ -926,6 +922,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 e75d2932475a..435756877aba 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1315,13 +1315,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);
@@ -1924,11 +1936,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) &&
@@ -1953,8 +1966,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.
@@ -1997,14 +2011,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);
@@ -2034,7 +2052,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;
@@ -2569,6 +2587,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;
@@ -2600,8 +2619,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;
@@ -2732,7 +2751,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);
@@ -2779,8 +2798,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));
@@ -2817,7 +2837,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;
@@ -2835,10 +2855,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)) {
@@ -2848,7 +2868,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 &&
@@ -2956,7 +2977,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;
/*
@@ -2996,8 +3017,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)
@@ -3018,10 +3040,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)
@@ -3037,7 +3059,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);
}
@@ -4304,7 +4326,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) {
@@ -4544,14 +4566,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/qrtr/ns.c b/net/qrtr/ns.c
index e5b2adb161d9..c5e7e01db249 100644
--- a/net/qrtr/ns.c
+++ b/net/qrtr/ns.c
@@ -78,7 +78,7 @@ struct qrtr_node {
*/
#define QRTR_NS_MAX_NODES 512
#define QRTR_NS_MAX_SERVERS 256
-#define QRTR_NS_MAX_LOOKUPS 64
+#define QRTR_NS_MAX_LOOKUPS 128
static u16 node_count;
diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c
index be535a261fa0..e250969c84ac 100644
--- a/net/sched/act_ct.c
+++ b/net/sched/act_ct.c
@@ -840,8 +840,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;
@@ -859,8 +866,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);
@@ -977,6 +988,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;
@@ -1012,9 +1024,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 12ea9e5a6005..837dc42457a6 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 fee4524adc98..4e6a2812a4f3 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 bd6f945bd388..eded7aacd3f7 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 487cbfdc5655..d90f41817b11 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1110,6 +1110,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) ||
@@ -1207,9 +1210,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 8965fe252471..755c42c32bcf 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1287,7 +1287,6 @@ static struct sk_buff *cake_ack_filter(struct cake_sched_data *q,
seglen = ipv6_payload_len(skb, ipv6h_check);
} else {
- WARN_ON(1); /* shouldn't happen */
continue;
}
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index b6ac0966420a..5be0bed2685e 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);
@@ -1709,6 +1716,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 f6b8c13dafa4..e92fb9da4647 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 0ae30c3c8913..e25612e9d082 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3336,12 +3336,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 708fa07d5fff..3a8e16b29660 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -6145,8 +6145,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 b5db69073e20..00403175b740 100644
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -1931,11 +1931,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 954b2ff1815c..aa6d83af55ed 100644
--- a/net/smc/smc_llc.c
+++ b/net/smc/smc_llc.c
@@ -1927,7 +1927,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 c1d9b923938d..5c9e4d8b57de 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 8e4ef2630ae4..683a136e53ef 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 ba95f9d927a3..e9aea371331b 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
@@ -1731,6 +1745,8 @@ tls_decrypt_sw(struct sock *sk, struct tls_context *tls_ctx,
/* If opportunistic TLS 1.3 ZC failed retry without ZC */
if (unlikely(darg->zc && prot->version == TLS_1_3_VERSION &&
darg->tail != TLS_RECORD_TYPE_DATA)) {
+ iov_iter_revert(&msg->msg_iter, strp_msg(darg->skb)->full_len -
+ prot->overhead_size);
darg->zc = false;
if (!darg->tail)
TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXNOPADVIOL);
diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c
index 57f2d6ec3ffc..96c9fe8d357c 100644
--- a/net/vmw_vsock/virtio_transport.c
+++ b/net/vmw_vsock/virtio_transport.c
@@ -346,12 +346,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;
@@ -451,13 +452,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;
@@ -634,12 +635,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);
@@ -691,6 +692,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/x25/af_x25.c b/net/x25/af_x25.c
index f9758c140ba4..e8c4f51035f6 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -362,6 +362,7 @@ static void x25_destroy_timer(struct timer_list *t)
struct sock *sk = timer_container_of(sk, t, sk_timer);
x25_destroy_socket_from_timer(sk);
+ sock_put(sk);
}
/*
@@ -397,9 +398,8 @@ static void __x25_destroy_socket(struct sock *sk)
if (sk_has_allocations(sk)) {
/* Defer: outstanding buffers */
- sk->sk_timer.expires = jiffies + 10 * HZ;
sk->sk_timer.function = x25_destroy_timer;
- add_timer(&sk->sk_timer);
+ sk_reset_timer(sk, &sk->sk_timer, jiffies + 10 * HZ);
} else {
/* drop last reference so sock_put will free */
__sock_put(sk);
diff --git a/net/x25/x25_timer.c b/net/x25/x25_timer.c
index 2ec63a1f4c6d..7896cd43f1cc 100644
--- a/net/x25/x25_timer.c
+++ b/net/x25/x25_timer.c
@@ -36,45 +36,45 @@ void x25_init_timers(struct sock *sk)
void x25_start_heartbeat(struct sock *sk)
{
- mod_timer(&sk->sk_timer, jiffies + 5 * HZ);
+ sk_reset_timer(sk, &sk->sk_timer, jiffies + 5 * HZ);
}
void x25_stop_heartbeat(struct sock *sk)
{
- timer_delete(&sk->sk_timer);
+ sk_stop_timer(sk, &sk->sk_timer);
}
void x25_start_t2timer(struct sock *sk)
{
struct x25_sock *x25 = x25_sk(sk);
- mod_timer(&x25->timer, jiffies + x25->t2);
+ sk_reset_timer(sk, &x25->timer, jiffies + x25->t2);
}
void x25_start_t21timer(struct sock *sk)
{
struct x25_sock *x25 = x25_sk(sk);
- mod_timer(&x25->timer, jiffies + x25->t21);
+ sk_reset_timer(sk, &x25->timer, jiffies + x25->t21);
}
void x25_start_t22timer(struct sock *sk)
{
struct x25_sock *x25 = x25_sk(sk);
- mod_timer(&x25->timer, jiffies + x25->t22);
+ sk_reset_timer(sk, &x25->timer, jiffies + x25->t22);
}
void x25_start_t23timer(struct sock *sk)
{
struct x25_sock *x25 = x25_sk(sk);
- mod_timer(&x25->timer, jiffies + x25->t23);
+ sk_reset_timer(sk, &x25->timer, jiffies + x25->t23);
}
void x25_stop_timer(struct sock *sk)
{
- timer_delete(&x25_sk(sk)->timer);
+ sk_stop_timer(sk, &x25_sk(sk)->timer);
}
unsigned long x25_display_timer(struct sock *sk)
@@ -108,7 +108,7 @@ static void x25_heartbeat_expiry(struct timer_list *t)
sock_flag(sk, SOCK_DEAD))) {
bh_unlock_sock(sk);
x25_destroy_socket_from_timer(sk);
- return;
+ goto out;
}
break;
@@ -120,8 +120,14 @@ static void x25_heartbeat_expiry(struct timer_list *t)
break;
}
restart_heartbeat:
- x25_start_heartbeat(sk);
+ /* Do not rearm once __x25_destroy_socket() has unlinked the socket:
+ * it is past its cancel point and owns the teardown from there on.
+ */
+ if (sk_hashed(sk))
+ x25_start_heartbeat(sk);
bh_unlock_sock(sk);
+out:
+ sock_put(sk);
}
/*
@@ -166,4 +172,5 @@ static void x25_timer_expiry(struct timer_list *t)
} else
x25_do_timer_expiry(sk);
bh_unlock_sock(sk);
+ sock_put(sk);
}
diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
index 58da2f4f4397..cd8643360eb3 100644
--- a/net/xdp/xdp_umem.c
+++ b/net/xdp/xdp_umem.c
@@ -210,6 +210,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/net/xdp/xsk.c b/net/xdp/xsk.c
index 855fe92358ba..95a9ff3d0bdb 100644
--- a/net/xdp/xsk.c
+++ b/net/xdp/xsk.c
@@ -966,15 +966,16 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer,
{
struct xsk_tx_metadata *meta = NULL;
u16 csum_start, csum_offset;
+ u64 flags;
if (unlikely(pool->tx_metadata_len == 0))
return -EINVAL;
meta = buffer - pool->tx_metadata_len;
- if (unlikely(!xsk_buff_valid_tx_metadata(meta)))
+ if (unlikely(!xsk_buff_valid_tx_metadata(pool, meta, &flags)))
return -EINVAL;
- if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) {
+ if (flags & XDP_TXMD_FLAGS_CHECKSUM) {
csum_start = READ_ONCE(meta->request.csum_start);
csum_offset = READ_ONCE(meta->request.csum_offset);
@@ -995,8 +996,10 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer,
}
}
- if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME)
- skb->skb_mstamp_ns = meta->request.launch_time;
+ if (flags & XDP_TXMD_FLAGS_LAUNCH_TIME)
+ skb->skb_mstamp_ns = READ_ONCE(meta->request.launch_time);
+ if (!(flags & XDP_TXMD_FLAGS_TIMESTAMP))
+ meta = NULL;
xsk_tx_metadata_to_compl(meta, &skb_shinfo(skb)->xsk_meta);
return 0;
diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c
index 4538223f4408..f72042260777 100644
--- a/net/xdp/xsk_buff_pool.c
+++ b/net/xdp/xsk_buff_pool.c
@@ -765,11 +765,11 @@ EXPORT_SYMBOL(xp_raw_get_dma);
* @addr: desc address (from userspace)
*
* Helper for getting desc's DMA address and metadata pointer, if present.
- * Saves one call on hotpath, double calculation of the actual address,
- * and inline checks for metadata presence and sanity.
+ * Saves one call on hotpath and double calculation of the actual address.
+ * Metadata is validated later by xsk_tx_metadata_request().
*
* Return: new &xdp_desc_ctx struct containing desc's DMA address and metadata
- * pointer, if it is present and valid (initialized to %NULL otherwise).
+ * pointer, if it is present (initialized to %NULL otherwise).
*/
struct xdp_desc_ctx xp_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr)
{
diff --git a/samples/damon/mtier.c b/samples/damon/mtier.c
index 7f84367ad3e5..a26cdfa73f1d 100644
--- a/samples/damon/mtier.c
+++ b/samples/damon/mtier.c
@@ -154,6 +154,9 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote)
if (!scheme)
goto free_out;
damon_set_schemes(ctx, &scheme, 1);
+ /* zero target value causes division by zero in damos_quota_store() */
+ if (!node0_mem_used_bp || !node0_mem_free_bp)
+ goto free_out;
quota_goal = damos_new_quota_goal(
promote ? DAMOS_QUOTA_NODE_MEM_USED_BP :
DAMOS_QUOTA_NODE_MEM_FREE_BP,
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 2dd231567710..c509abd75fbe 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -269,8 +269,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 5cea53fc36df..ff52becc3031 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -687,6 +687,43 @@ static int ima_file_check(struct file *file, int mask)
MAY_APPEND), FILE_CHECK, 0, false);
}
+/*
+ * 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)
{
@@ -1300,11 +1337,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/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
index d243baff95a7..5b38e4e9f228 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
@@ -344,6 +344,8 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda,
strscpy(p->dev_name, hid, sizeof(p->dev_name));
physdev = get_device(acpi_get_first_physical_node(adev));
acpi_dev_put(adev);
+ if (!physdev)
+ return -ENODEV;
property = "ti,dev-index";
ret = device_property_count_u32(physdev, property);
@@ -386,7 +388,6 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda,
err:
dev_err(p->dev, "read acpi error, ret: %d\n", ret);
put_device(physdev);
- acpi_dev_put(adev);
return ret;
}
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index dca06ba1c67e..a1d449f2a342 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -385,13 +385,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:
@@ -1243,10 +1245,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/fcp.c b/sound/usb/fcp.c
index c45dbe4d4532..ec1ddfa4d0ce 100644
--- a/sound/usb/fcp.c
+++ b/sound/usb/fcp.c
@@ -129,6 +129,10 @@ struct fcp_data {
#define FCP_SEGMENT_APP_GOLD 0
+#define FCP_MAX_METER_MAP_SIZE \
+ (sizeof_field(struct snd_ctl_elem_value, value.integer.value) / \
+ sizeof(long))
+
/* Forward declarations */
static int fcp_init(struct usb_mixer_interface *mixer,
void *step0_resp, void *step2_resp);
@@ -410,6 +414,9 @@ static int fcp_meter_ctl_get(struct snd_kcontrol *kctl,
if (err < 0)
return err;
+ if (WARN_ON_ONCE(elem->channels > FCP_MAX_METER_MAP_SIZE))
+ return -EINVAL;
+
/* copy & translate from resp[] using meter_level_map[] */
for (i = 0; i < elem->channels; i++) {
int idx = private->meter_level_map[i];
@@ -636,7 +643,8 @@ static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer,
}
/* Validate the map size */
- if (map.map_size < 1 || map.map_size > 255 ||
+ if (map.map_size < 1 ||
+ map.map_size > FCP_MAX_METER_MAP_SIZE ||
map.meter_slots < 1 || map.meter_slots > 255)
return -EINVAL;
diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c
index 83980fb83ac8..1dc78f536ef9 100644
--- a/sound/usb/midi2.c
+++ b/sound/usb/midi2.c
@@ -677,6 +677,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;
@@ -727,6 +735,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/mixer.c b/sound/usb/mixer.c
index dbcefcaf7c36..142f5ca7a72d 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -434,6 +434,11 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval,
*value = cval->cache_val[index];
return 0;
}
+
+ /* The current value is always provided by the cache after initialization. */
+ if (cval->get_cur_broken)
+ return -ENXIO;
+
err = get_cur_mix_raw(cval, channel, value);
if (err < 0) {
if (!cval->head.mixer->ignore_ctl_error)
@@ -1234,7 +1239,7 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx)
err = snd_usb_get_cur_mix_value(cval, ch, idx, &val);
if (!err)
return;
- if (!cval->head.mixer->ignore_ctl_error)
+ if (!cval->head.mixer->ignore_ctl_error && !cval->get_cur_broken)
usb_audio_warn(cval->head.mixer->chip,
"%d:%d: failed to get current value for ch %d (%d)\n",
cval->head.id, mixer_ctrl_intf(cval->head.mixer),
@@ -1248,8 +1253,16 @@ static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx)
* Some devices' volume control mixers are sticky, which accept SET_CUR but
* do absolutely nothing.
*
- * Prevent sticky mixers from being registered, otherwise they confuses
- * userspace and results in ineffective volume control.
+ * Check the return values of GET_CUR with different SET_CUR values. Consider
+ * the mixer as sticky if GET_CUR always returns a constant value.
+ *
+ * Some devices have effective SET_CUR despite GET_CUR being constant. Do not
+ * consider the mixer as sticky if a quirk flag indicates that.
+ *
+ * Gate the registration of sticky mixers to prevent confusing userspace, so
+ * that they won't cause ineffective volume control. However, for mixers with
+ * effective SET_CUR but broken GET_CUR, the registration can continue normally
+ * but further GET_CUR requests will be gated.
*/
static int check_sticky_volume_control(struct usb_mixer_elem_info *cval,
int channel, int saved)
@@ -1269,10 +1282,22 @@ static int check_sticky_volume_control(struct usb_mixer_elem_info *cval,
return 0;
}
+ if (cval->head.mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_GET_CUR_BROKEN) {
+ usb_audio_info(cval->head.mixer->chip,
+ "%d:%d: broken mixer GET_CUR (%d/%d/%d => %d)\n",
+ cval->head.id, mixer_ctrl_intf(cval->head.mixer),
+ cval->min, cval->max, cval->res, saved);
+
+ cval->get_cur_broken = 1;
+ return -ENXIO;
+ }
+
usb_audio_err(cval->head.mixer->chip,
"%d:%d: sticky mixer values (%d/%d/%d => %d), disabling\n",
cval->head.id, mixer_ctrl_intf(cval->head.mixer),
cval->min, cval->max, cval->res, saved);
+ usb_audio_info(cval->head.mixer->chip,
+ "check MIXER_GET_CUR_BROKEN if you believe the mixer is non-sticky");
return -ENODEV;
}
@@ -1315,7 +1340,7 @@ static void check_volume_control_res(struct usb_mixer_elem_info *cval,
static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
int default_min, struct snd_kcontrol *kctl)
{
- int i, idx, ret;
+ int i, idx, ret = 0;
/* for failsafe */
cval->min = default_min;
@@ -1371,8 +1396,10 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
goto no_checks;
ret = check_sticky_volume_control(cval, minchn, saved);
- if (ret < 0)
+ if (ret == -ENODEV)
goto sticky;
+ if (ret)
+ goto no_checks;
if (cval->min + cval->res < cval->max)
check_volume_control_res(cval, minchn, saved);
@@ -1381,6 +1408,16 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
}
no_checks:
+ /*
+ * Got a non-fatal failure during sanity checks.
+ *
+ * Do not propagate mixer values written by sanity checks.
+ * Instead, rely on init_cur_mix_raw() to initialize the mixer
+ * properly.
+ */
+ if (ret)
+ cval->cached = 0;
+
cval->initialized = 1;
}
@@ -3538,7 +3575,8 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
continue;
info = mixer_elem_list_to_info(list);
/* invalidate cache, so the value is read from the device */
- info->cached = 0;
+ if (!info->get_cur_broken)
+ info->cached = 0;
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&list->kctl->id);
}
@@ -3635,10 +3673,12 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
switch (attribute) {
case UAC2_CS_CUR:
/* invalidate cache, so the value is read from the device */
- if (channel)
- info->cached &= ~BIT(channel);
- else /* master channel */
- info->cached = 0;
+ if (!info->get_cur_broken) {
+ if (channel)
+ info->cached &= ~BIT(channel);
+ else /* master channel */
+ info->cached = 0;
+ }
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
&info->head.kctl->id);
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index afbb3dd9f177..3fa1bd96f858 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -94,6 +94,7 @@ struct usb_mixer_elem_info {
int cache_val[MAX_CHANNELS];
u8 initialized;
u8 min_mute;
+ u8 get_cur_broken;
void *private_data;
};
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 33eb8be48af8..354866a80605 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2249,6 +2249,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
DEVICE_FLG(0x046d, 0x0a8f, /* Logitech H390 headset */
QUIRK_FLAG_CTL_MSG_DELAY_1M |
QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
+ DEVICE_FLG(0x046d, 0x0af7, /* Logitech PRO X 2 LIGHTSPEED */
+ QUIRK_FLAG_MIXER_GET_CUR_BROKEN),
DEVICE_FLG(0x0499, 0x1506, /* Yamaha THR5 */
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */
@@ -2318,6 +2320,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x0763, 0x2031, /* M-Audio Fast Track C600 */
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
+ DEVICE_FLG(0x0763, 0x2080, /* M-Audio Fast Track Ultra */
+ QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB),
+ DEVICE_FLG(0x0763, 0x2081, /* M-Audio Fast Track Ultra */
+ QUIRK_FLAG_MIXER_GET_CUR_BROKEN | QUIRK_FLAG_GENERIC_IMPLICIT_FB),
DEVICE_FLG(0x07fd, 0x000b, /* MOTU M Series 2nd hardware revision */
QUIRK_FLAG_CTL_MSG_DELAY_1M),
DEVICE_FLG(0x08bb, 0x2702, /* LineX FM Transmitter */
@@ -2611,6 +2617,7 @@ static const char *const snd_usb_audio_quirk_flag_names[] = {
QUIRK_STRING_ENTRY(MIXER_PLAYBACK_LINEAR_VOL),
QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL),
QUIRK_STRING_ENTRY(IFB_SILENCE_ON_EMPTY),
+ QUIRK_STRING_ENTRY(MIXER_GET_CUR_BROKEN),
NULL
};
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 9afcad8f143a..e472aef6eb87 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -242,6 +242,17 @@ extern bool snd_usb_skip_validation;
* from snd_usb_handle_sync_urb. Instead fall through and enqueue a
* packet_info containing only size-0 packets, so the OUT ring keeps
* moving (emits silence). Needed by Behringer Flow 8 (1397:050c).
+ * QUIRK_FLAG_MIXER_GET_CUR_BROKEN
+ * Some mixers are sticky, which means that setting their current volume is a
+ * no-op, and reading the current volume returns a constant value. The sticky
+ * check disables these mixers to prevent confusing userspace. However, some
+ * devices do have a tunable volume despite the reported current volume being
+ * constant. As the sticky check can't distinguish between the two categories,
+ * setting this flag tells that the device should fall into the second
+ * category when GET_CUR returns a constant value, resulting in the sticky
+ * check being non-fatal and only disabling GET_CUR instead of the whole mixer.
+ * The current volume will then be provided by the internal cache that stores
+ * the last set volume
*/
enum {
@@ -275,6 +286,7 @@ enum {
QUIRK_TYPE_MIXER_PLAYBACK_LINEAR_VOL = 27,
QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28,
QUIRK_TYPE_IFB_SILENCE_ON_EMPTY = 29,
+ QUIRK_TYPE_MIXER_GET_CUR_BROKEN = 30,
/* Please also edit snd_usb_audio_quirk_flag_names */
};
@@ -310,5 +322,6 @@ enum {
#define QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL QUIRK_FLAG(MIXER_PLAYBACK_LINEAR_VOL)
#define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL)
#define QUIRK_FLAG_IFB_SILENCE_ON_EMPTY QUIRK_FLAG(IFB_SILENCE_ON_EMPTY)
+#define QUIRK_FLAG_MIXER_GET_CUR_BROKEN QUIRK_FLAG(MIXER_GET_CUR_BROKEN)
#endif /* __USBAUDIO_H */
diff --git a/sound/usb/usx2y/us144mkii_capture.c b/sound/usb/usx2y/us144mkii_capture.c
index fa01da98151a..d7cdf9e0f685 100644
--- a/sound/usb/usx2y/us144mkii_capture.c
+++ b/sound/usb/usx2y/us144mkii_capture.c
@@ -302,6 +302,7 @@ void capture_urb_complete(struct urb *urb)
}
usb_get_urb(urb);
+ usb_anchor_urb(urb, &tascam->capture_anchor);
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0) {
dev_err_ratelimited(tascam->card->dev,
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 4d7925184826..7f153a2fcdd8 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 7c90214485d9..f1d5b4b797ad 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -672,6 +672,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 d2846579285f..b4019d1a5575 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
@@ -807,7 +807,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);
@@ -876,7 +876,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);
@@ -947,7 +947,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 cc0c68bab907..1c96a3cf4b97 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/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
index 7950c504ed28..31bf8a3fcd66 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
@@ -414,14 +414,14 @@ static u32 pkt_nb_frags(u32 frame_size, struct pkt_stream *pkt_stream, struct pk
}
/* Search for the end of the packet in verbatim mode */
- if (!pkt_continues(pkt->options) || !pkt->valid)
+ if (!pkt_continues(pkt->options))
return nb_frags;
next_frag = pkt_stream->current_pkt_nb;
pkt++;
while (next_frag++ < pkt_stream->nb_pkts) {
nb_frags++;
- if (!pkt_continues(pkt->options) || !pkt->valid)
+ if (!pkt_continues(pkt->options))
break;
pkt++;
}
@@ -652,11 +652,11 @@ static struct pkt_stream *__pkt_stream_generate_custom(struct ifobject *ifobj, s
if (!frame->valid || !pkt_continues(frame->options))
payload++;
} else {
- if (frame->valid)
+ if (frame->valid) {
len += frame->len;
- if (frame->valid && pkt_continues(frame->options))
- continue;
-
+ if (pkt_continues(frame->options))
+ continue;
+ }
pkt->pkt_nb = pkt_nb;
pkt->len = len;
pkt->valid = frame->valid;
@@ -1236,10 +1236,9 @@ static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk, b
}
}
- if (pkt && pkt->valid) {
+ if (pkt && pkt->valid)
valid_pkts++;
- valid_frags += nb_frags;
- }
+ valid_frags += nb_frags;
}
pthread_mutex_lock(&pacing_mutex);
@@ -2075,13 +2074,16 @@ int testapp_invalid_desc_mb(struct test_spec *test)
{0, 0, 0, false, 0},
/* Invalid address in the second frame */
{0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD},
- {umem_size, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD},
+ {umem_size * 2, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD},
+ {0, MIN_PKT_SIZE, 0, false, 0},
/* Invalid len in the middle */
{0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD},
{0, XSK_UMEM__INVALID_FRAME_SIZE, 0, false, XDP_PKT_CONTD},
+ {0, MIN_PKT_SIZE, 0, false, 0},
/* Invalid options in the middle */
{0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XDP_PKT_CONTD},
{0, XSK_UMEM__LARGE_FRAME_SIZE, 0, false, XSK_DESC__INVALID_OPTION},
+ {0, MIN_PKT_SIZE, 0, false, 0},
/* Transmit 2 frags, receive 3 */
{0, XSK_UMEM__MAX_FRAME_SIZE, 0, true, XDP_PKT_CONTD},
{0, XSK_UMEM__MAX_FRAME_SIZE, 0, true, 0},
@@ -2093,8 +2095,8 @@ int testapp_invalid_desc_mb(struct test_spec *test)
if (umem->unaligned_mode) {
/* Crossing a chunk boundary allowed */
- pkts[12].valid = true;
- pkts[13].valid = true;
+ pkts[15].valid = true;
+ pkts[16].valid = true;
}
test->mtu = MAX_ETH_JUMBO_SIZE;
@@ -2229,7 +2231,7 @@ int testapp_too_many_frags(struct test_spec *test)
max_frags += 1;
}
- pkts = calloc(2 * max_frags + 2, sizeof(struct pkt));
+ pkts = calloc(2 * max_frags + 3, sizeof(struct pkt));
if (!pkts)
return TEST_FAILURE;
@@ -2247,24 +2249,30 @@ int testapp_too_many_frags(struct test_spec *test)
}
pkts[max_frags].options = 0;
- /* An invalid packet with the max amount of frags but signals packet
- * continues on the last frag
- */
- for (i = max_frags + 1; i < 2 * max_frags + 1; i++) {
+ /* An invalid packet with the max + 1 amount of frags */
+ for (i = max_frags + 1; i < 2 * max_frags + 2; i++) {
pkts[i].len = MIN_PKT_SIZE;
pkts[i].options = XDP_PKT_CONTD;
- pkts[i].valid = false;
+ pkts[i].valid = true;
}
+ pkts[2 * max_frags + 1].options = 0;
/* Valid packet for synch */
- pkts[2 * max_frags + 1].len = MIN_PKT_SIZE;
- pkts[2 * max_frags + 1].valid = true;
+ pkts[2 * max_frags + 2].len = MIN_PKT_SIZE;
+ pkts[2 * max_frags + 2].valid = true;
- if (pkt_stream_generate_custom(test, pkts, 2 * max_frags + 2)) {
+ if (pkt_stream_generate_custom(test, pkts, 2 * max_frags + 3)) {
free(pkts);
return TEST_FAILURE;
}
+ /* The generated Tx stream must keep the too-big packet valid so that
+ * __send_pkts() accounts its descriptors in outstanding_tx. The Rx
+ * stream, however, must not expect this packet on the wire.
+ */
+ test->ifobj_rx->xsk->pkt_stream->pkts[2].valid = false;
+ test->ifobj_rx->xsk->pkt_stream->nb_valid_entries--;
+
ret = testapp_validate_traffic(test);
free(pkts);
return ret;
diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c
index ccc5acd55ff9..d194b44ff8be 100644
--- a/tools/testing/selftests/bpf/test_maps.c
+++ b/tools/testing/selftests/bpf/test_maps.c
@@ -748,16 +748,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/bpf/verifier/sleepable.c b/tools/testing/selftests/bpf/verifier/sleepable.c
index c2b7f5ebf168..6dabc5522945 100644
--- a/tools/testing/selftests/bpf/verifier/sleepable.c
+++ b/tools/testing/selftests/bpf/verifier/sleepable.c
@@ -76,7 +76,20 @@
.runs = -1,
},
{
- "sleepable raw tracepoint reject",
+ "sleepable raw tracepoint accept",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .prog_type = BPF_PROG_TYPE_TRACING,
+ .expected_attach_type = BPF_TRACE_RAW_TP,
+ .kfunc = "sys_enter",
+ .result = ACCEPT,
+ .flags = BPF_F_SLEEPABLE,
+ .runs = -1,
+},
+{
+ "sleepable raw tracepoint reject non-faultable",
.insns = {
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
@@ -85,7 +98,7 @@
.expected_attach_type = BPF_TRACE_RAW_TP,
.kfunc = "sched_switch",
.result = REJECT,
- .errstr = "Only fentry/fexit/fsession/fmod_ret, lsm, iter, uprobe, and struct_ops programs can be sleepable",
+ .errstr = "Sleepable program cannot attach to non-faultable tracepoint",
.flags = BPF_F_SLEEPABLE,
.runs = -1,
},
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
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index d805a7dfbdd5..d7741ef944bb 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -1802,6 +1802,63 @@ TEST_F(tls, recv_efault)
}
#define TLS_RECORD_TYPE_HANDSHAKE 0x16
+
+TEST_F(tls_basic, recvmsg_nopad_retry_iov)
+{
+ char payload[32];
+ char first_iov[sizeof(payload)];
+ char later_iov[sizeof(payload) * 2];
+ char expected_later_iov[sizeof(later_iov)];
+ char cbuf[CMSG_SPACE(sizeof(char))];
+ struct tls_crypto_info_keys tls13;
+ struct iovec iov[] = {
+ { .iov_base = first_iov, .iov_len = sizeof(first_iov) },
+ { .iov_base = later_iov, .iov_len = sizeof(later_iov) },
+ };
+ struct msghdr msg = {
+ .msg_iov = iov,
+ .msg_iovlen = ARRAY_SIZE(iov),
+ .msg_control = cbuf,
+ .msg_controllen = sizeof(cbuf),
+ };
+ int one = 1;
+ int ret;
+ int i;
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128,
+ &tls13, 0);
+
+ ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls13, tls13.len);
+ ASSERT_EQ(ret, 0);
+
+ ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls13, tls13.len);
+ ASSERT_EQ(ret, 0);
+
+ ret = setsockopt(self->cfd, SOL_TLS, TLS_RX_EXPECT_NO_PAD,
+ &one, sizeof(one));
+ ASSERT_EQ(ret, 0);
+
+ for (i = 0; i < sizeof(payload); i++)
+ payload[i] = 0x40 + i;
+ memset(first_iov, 0xa5, sizeof(first_iov));
+ memset(later_iov, 0x5a, sizeof(later_iov));
+ memset(expected_later_iov, 0x5a, sizeof(expected_later_iov));
+
+ /* A control record forces optimistic TLS 1.3 RX to retry. */
+ ret = tls_send_cmsg(self->fd, TLS_RECORD_TYPE_HANDSHAKE,
+ payload, sizeof(payload), 0);
+ ASSERT_EQ(ret, sizeof(payload));
+
+ ret = recvmsg(self->cfd, &msg, 0);
+ ASSERT_EQ(ret, sizeof(payload));
+ EXPECT_EQ(memcmp(first_iov, payload, sizeof(payload)), 0);
+ EXPECT_EQ(memcmp(later_iov, expected_later_iov,
+ sizeof(later_iov)), 0);
+}
+
/* key_update, length 1, update_not_requested */
static const char key_update_msg[] = "\x18\x00\x00\x01\x00";
static void tls_send_keyupdate(struct __test_metadata *_metadata, int fd)
diff --git a/tools/testing/selftests/sched_ext/numa.bpf.c b/tools/testing/selftests/sched_ext/numa.bpf.c
index 78cc49a7f9a6..6b4515c28aa0 100644
--- a/tools/testing/selftests/sched_ext/numa.bpf.c
+++ b/tools/testing/selftests/sched_ext/numa.bpf.c
@@ -34,7 +34,8 @@ static bool is_cpu_idle(s32 cpu, int node)
s32 BPF_STRUCT_OPS(numa_select_cpu,
struct task_struct *p, s32 prev_cpu, u64 wake_flags)
{
- int node = __COMPAT_scx_bpf_cpu_node(scx_bpf_task_cpu(p));
+ s32 task_cpu = scx_bpf_task_cpu(p);
+ int node = __COMPAT_scx_bpf_cpu_node(task_cpu);
s32 cpu;
/*
@@ -48,6 +49,16 @@ s32 BPF_STRUCT_OPS(numa_select_cpu,
cpu = __COMPAT_scx_bpf_pick_any_cpu_node(p->cpus_ptr, node,
__COMPAT_SCX_PICK_IDLE_IN_NODE);
+ /*
+ * @task_cpu may be outside of p->cpus_ptr if @p's affinity
+ * changed while it was sleeping. This means it's possible for
+ * p->cpus_ptr to not include any CPUs from @node.
+ * If we failed to find a cpu in @node, check if @task_cpu
+ * is outside of p->cpus_ptr and just return @prev_cpu if it is.
+ */
+ if (cpu < 0 && !bpf_cpumask_test_cpu(task_cpu, p->cpus_ptr))
+ return prev_cpu;
+
if (is_cpu_idle(cpu, node))
scx_bpf_error("CPU %d should be marked as busy", cpu);
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-19 16:53 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 7.1.9 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.