Merge branches 'acpi-apei', 'acpi-pad' and 'acpi-misc'
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 26 Jun 2023 15:24:19 +0000 (17:24 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 26 Jun 2023 15:24:19 +0000 (17:24 +0200)
Merge ACPI APEI changes, an ACPI PAD driver update and an ACPI FFH
handling cleanup related to ARM64 for 6.5-rc1:

 - Make ghes_get_devices() return NULL to indicate that there are no
   GHES devices so as to allow vendor-specific EDAC drivers to probe
   then (Li Yang).

 - Mark bert_disable() as __initdata and drop an unused function from
   the APEI GHES code (Miaohe Lin).

 - Make the ACPI PAD (Processor Aggregator Device) driver realize that
   Zhaoxin CPUs support nonstop TSC (Tony W Wang-oc).

 - Drop the certainly unnecessary and likely incorrect inclusion of
   linux/arm-smccc.h from acpi_ffh.c (Sudeep Holla).

* acpi-apei:
  APEI: GHES: correctly return NULL for ghes_get_devices()
  ACPI: APEI: mark bert_disable as __initdata
  ACPI: APEI: GHES: Remove unused ghes_estatus_pool_size_request()

* acpi-pad:
  ACPI: PAD: mark Zhaoxin CPUs NONSTOP TSC correctly

* acpi-misc:
  ACPI: FFH: Drop the inclusion of linux/arm-smccc.h

270 files changed:
.mailmap
Documentation/devicetree/bindings/ata/ahci-common.yaml
Documentation/devicetree/bindings/clock/canaan,k210-clk.yaml
Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
Documentation/devicetree/bindings/mfd/canaan,k210-sysctl.yaml
Documentation/devicetree/bindings/net/realtek-bluetooth.yaml
Documentation/devicetree/bindings/pinctrl/canaan,k210-fpioa.yaml
Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.yaml
Documentation/devicetree/bindings/reset/canaan,k210-rst.yaml
Documentation/devicetree/bindings/riscv/canaan.yaml
Documentation/devicetree/usage-model.rst
Documentation/riscv/patch-acceptance.rst
Documentation/trace/user_events.rst
Documentation/translations/zh_CN/devicetree/usage-model.rst
MAINTAINERS
Makefile
arch/arm64/hyperv/mshyperv.c
arch/loongarch/include/asm/loongarch.h
arch/loongarch/include/asm/pgtable-bits.h
arch/loongarch/include/asm/pgtable.h
arch/loongarch/kernel/hw_breakpoint.c
arch/loongarch/kernel/perf_event.c
arch/loongarch/kernel/unaligned.c
arch/nios2/boot/dts/10m50_devboard.dts
arch/nios2/boot/dts/3c120_devboard.dts
arch/parisc/include/asm/assembly.h
arch/powerpc/purgatory/Makefile
arch/riscv/purgatory/Makefile
arch/x86/hyperv/hv_init.c
arch/x86/hyperv/hv_vtl.c
arch/x86/purgatory/Makefile
block/blk-cgroup.c
drivers/accel/qaic/qaic_drv.c
drivers/acpi/acpi_ffh.c
drivers/acpi/acpi_lpss.c
drivers/acpi/acpica/achware.h
drivers/acpi/apei/bert.c
drivers/acpi/apei/ghes.c
drivers/acpi/bus.c
drivers/acpi/button.c
drivers/acpi/ec.c
drivers/acpi/nfit/nfit.h
drivers/acpi/resource.c
drivers/acpi/scan.c
drivers/acpi/sleep.c
drivers/acpi/thermal.c
drivers/acpi/tiny-power-button.c
drivers/acpi/video_detect.c
drivers/acpi/x86/s2idle.c
drivers/acpi/x86/utils.c
drivers/ata/libata-core.c
drivers/ata/libata-eh.c
drivers/ata/libata-scsi.c
drivers/base/regmap/regcache.c
drivers/clk/clk-composite.c
drivers/clk/clk-loongson2.c
drivers/clk/mediatek/clk-mt8365.c
drivers/clk/pxa/clk-pxa3xx.c
drivers/firmware/cirrus/cs_dsp.c
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
drivers/gpu/drm/amd/amdgpu/amdgpu_ring_mux.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ring_mux.h
drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/dc/link/link_detection.c
drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
drivers/gpu/drm/bridge/ti-sn65dsi86.c
drivers/gpu/drm/nouveau/nouveau_acpi.c
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/radeon/radeon_fbdev.c
drivers/hv/channel_mgmt.c
drivers/hv/hv_common.c
drivers/hv/vmbus_drv.c
drivers/infiniband/core/cma.c
drivers/infiniband/core/uverbs_cmd.c
drivers/infiniband/core/uverbs_main.c
drivers/infiniband/hw/bnxt_re/bnxt_re.h
drivers/infiniband/hw/bnxt_re/ib_verbs.c
drivers/infiniband/hw/bnxt_re/main.c
drivers/infiniband/hw/mlx5/counters.c
drivers/infiniband/hw/mlx5/fs.c
drivers/infiniband/hw/mlx5/fs.h
drivers/infiniband/hw/mlx5/main.c
drivers/infiniband/hw/mlx5/mlx5_ib.h
drivers/infiniband/hw/mlx5/qp.c
drivers/infiniband/sw/rxe/rxe_cq.c
drivers/infiniband/sw/rxe/rxe_net.c
drivers/infiniband/sw/rxe/rxe_qp.c
drivers/infiniband/sw/rxe/rxe_resp.c
drivers/infiniband/ulp/isert/ib_isert.c
drivers/infiniband/ulp/rtrs/rtrs-clt.c
drivers/infiniband/ulp/rtrs/rtrs.c
drivers/md/dm-ioctl.c
drivers/md/dm-thin-metadata.c
drivers/md/dm-thin.c
drivers/md/dm.c
drivers/media/dvb-core/dvb_frontend.c
drivers/net/dsa/ocelot/felix_vsc9959.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/freescale/enetc/enetc_qos.c
drivers/net/ethernet/intel/iavf/iavf.h
drivers/net/ethernet/intel/iavf/iavf_main.c
drivers/net/ethernet/intel/iavf/iavf_register.h
drivers/net/ethernet/intel/ice/ice_gnss.c
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/igc/igc_main.c
drivers/net/ethernet/marvell/octeon_ep/octep_main.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c
drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
drivers/net/ethernet/renesas/rswitch.c
drivers/net/ethernet/sfc/efx_channels.c
drivers/net/ethernet/sfc/siena/efx_channels.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/ti/am65-cpsw-nuss.c
drivers/net/ipvlan/ipvlan_l3s.c
drivers/net/macsec.c
drivers/net/phy/phylink.c
drivers/net/usb/qmi_wwan.c
drivers/net/wan/lapbether.c
drivers/net/wireless/intel/iwlwifi/mvm/rs.c
drivers/of/overlay.c
drivers/pci/controller/pci-hyperv.c
drivers/regulator/qcom-rpmh-regulator.c
drivers/s390/net/ism_drv.c
drivers/scsi/aacraid/aacraid.h
drivers/scsi/aacraid/commsup.c
drivers/scsi/aacraid/linit.c
drivers/scsi/aacraid/src.c
drivers/scsi/lpfc/lpfc_bsg.c
drivers/scsi/storvsc_drv.c
drivers/spi/spi-cadence-quadspi.c
drivers/spi/spi-dw-mmio.c
drivers/spi/spi-fsl-dspi.c
drivers/staging/octeon/TODO
drivers/target/target_core_transport.c
drivers/thermal/intel/intel_soc_dts_iosf.c
drivers/thunderbolt/dma_test.c
drivers/thunderbolt/nhi.c
drivers/thunderbolt/tb.c
drivers/thunderbolt/tunnel.c
drivers/tty/serial/fsl_lpuart.c
drivers/tty/serial/lantiq.c
drivers/usb/dwc3/core.c
drivers/usb/dwc3/dwc3-qcom.c
drivers/usb/dwc3/gadget.c
drivers/usb/gadget/udc/core.c
drivers/usb/gadget/udc/renesas_usb3.c
drivers/usb/serial/option.c
drivers/usb/typec/pd.c
drivers/usb/typec/ucsi/ucsi.c
fs/afs/vl_probe.c
fs/afs/write.c
fs/btrfs/disk-io.c
fs/btrfs/inode.c
fs/btrfs/scrub.c
fs/btrfs/super.c
fs/btrfs/volumes.c
fs/eventpoll.c
fs/ext4/balloc.c
fs/nilfs2/btnode.c
fs/nilfs2/sufile.c
fs/nilfs2/the_nilfs.c
fs/ocfs2/file.c
fs/ocfs2/super.c
fs/smb/client/cifs_debug.c
fs/smb/client/cifsglob.h
fs/smb/client/cifsproto.h
fs/smb/client/connect.c
fs/smb/client/dfs.c
fs/smb/client/file.c
fs/smb/client/smb2ops.c
fs/smb/client/smb2pdu.c
fs/smb/client/transport.c
fs/smb/server/server.c
fs/smb/server/smb2misc.c
fs/smb/server/smb2pdu.c
fs/smb/server/smbacl.c
fs/smb/server/vfs.c
fs/smb/server/vfs.h
fs/smb/server/vfs_cache.c
fs/userfaultfd.c
include/acpi/acpi_bus.h
include/acpi/acpixf.h
include/linux/cpuhotplug.h
include/linux/libata.h
include/linux/mlx5/driver.h
include/linux/notifier.h
include/media/dvb_frontend.h
include/net/netfilter/nf_flow_table.h
include/net/netfilter/nf_tables.h
include/net/sch_generic.h
include/rdma/ib_addr.h
include/uapi/linux/ethtool_netlink.h
io_uring/io-wq.c
io_uring/net.c
kernel/kexec_file.c
kernel/trace/trace_events_user.c
kernel/trace/trace_output.c
lib/radix-tree.c
lib/radix-tree.h [new file with mode: 0644]
lib/test_vmalloc.c
lib/xarray.c
mm/damon/core.c
mm/filemap.c
mm/gup_test.c
mm/mmap.c
mm/zswap.c
net/dccp/proto.c
net/handshake/handshake.h
net/handshake/request.c
net/ipv4/udplite.c
net/ipv6/ping.c
net/ipv6/udplite.c
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/link.c
net/mac80211/mlme.c
net/mac80211/tx.c
net/mac80211/util.c
net/netfilter/nf_flow_table_core.c
net/netfilter/nf_flow_table_ip.c
net/netfilter/nf_tables_api.c
net/netfilter/nfnetlink.c
net/netfilter/nft_set_pipapo.c
net/netlabel/netlabel_kapi.c
net/sched/act_ct.c
net/sched/act_pedit.c
net/sched/cls_api.c
net/sched/cls_u32.c
net/sched/sch_api.c
net/sched/sch_generic.c
net/sched/sch_taprio.c
net/sctp/sm_sideeffect.c
net/sctp/sm_statefuns.c
net/tipc/bearer.c
net/wireless/rdev-ops.h
net/wireless/reg.c
net/wireless/util.c
sound/core/seq/oss/seq_oss_midi.c
sound/pci/hda/patch_realtek.c
sound/soc/tegra/tegra_pcm.c
sound/usb/pcm.c
sound/usb/quirks.c
tools/testing/radix-tree/Makefile
tools/testing/selftests/net/forwarding/hw_stats_l3.sh
tools/testing/selftests/net/mptcp/config
tools/testing/selftests/net/mptcp/diag.sh
tools/testing/selftests/net/mptcp/mptcp_connect.sh
tools/testing/selftests/net/mptcp/mptcp_join.sh
tools/testing/selftests/net/mptcp/mptcp_lib.sh
tools/testing/selftests/net/mptcp/mptcp_sockopt.c
tools/testing/selftests/net/mptcp/mptcp_sockopt.sh
tools/testing/selftests/net/mptcp/pm_netlink.sh
tools/testing/selftests/net/mptcp/userspace_pm.sh
tools/testing/selftests/ptp/testptp.c
tools/testing/selftests/tc-testing/config
tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfb.json
tools/testing/selftests/tc-testing/tdc.sh
tools/testing/selftests/user_events/dyn_test.c
tools/testing/selftests/user_events/ftrace_test.c
tools/testing/selftests/user_events/perf_test.c

index bf076bb..650689d 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -233,6 +233,7 @@ Jisheng Zhang <jszhang@kernel.org> <Jisheng.Zhang@synaptics.com>
 Johan Hovold <johan@kernel.org> <jhovold@gmail.com>
 Johan Hovold <johan@kernel.org> <johan@hovoldconsulting.com>
 John Crispin <john@phrozen.org> <blogic@openwrt.org>
+John Keeping <john@keeping.me.uk> <john@metanate.com>
 John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
 John Stultz <johnstul@us.ibm.com>
 <jon.toppins+linux@gmail.com> <jtoppins@cumulusnetworks.com>
index 7fdf409..38770c4 100644 (file)
@@ -8,7 +8,7 @@ title: Common Properties for Serial ATA AHCI controllers
 
 maintainers:
   - Hans de Goede <hdegoede@redhat.com>
-  - Damien Le Moal <damien.lemoal@opensource.wdc.com>
+  - Damien Le Moal <dlemoal@kernel.org>
 
 description:
   This document defines device tree properties for a common AHCI SATA
index 998e5cc..380cb6d 100644 (file)
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: Canaan Kendryte K210 Clock
 
 maintainers:
-  - Damien Le Moal <damien.lemoal@wdc.com>
+  - Damien Le Moal <dlemoal@kernel.org>
 
 description: |
   Canaan Kendryte K210 SoC clocks driver bindings. The clock
index 62f3ca6..32c821f 100644 (file)
@@ -44,7 +44,7 @@ required:
   - clock-names
   - clocks
 
-additionalProperties: true
+unevaluatedProperties: false
 
 examples:
   - |
index 8459d36..3b3beab 100644 (file)
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: Canaan Kendryte K210 System Controller
 
 maintainers:
-  - Damien Le Moal <damien.lemoal@wdc.com>
+  - Damien Le Moal <dlemoal@kernel.org>
 
 description:
   Canaan Inc. Kendryte K210 SoC system controller which provides a
index 8cc2b99..043e118 100644 (file)
@@ -11,7 +11,7 @@ maintainers:
   - Alistair Francis <alistair@alistair23.me>
 
 description:
-  RTL8723CS/RTL8723CS/RTL8821CS/RTL8822CS is a WiFi + BT chip. WiFi part
+  RTL8723BS/RTL8723CS/RTL8821CS/RTL8822CS is a WiFi + BT chip. WiFi part
   is connected over SDIO, while BT is connected over serial. It speaks
   H5 protocol with few extra commands to upload firmware and change
   module speed.
@@ -27,7 +27,7 @@ properties:
       - items:
           - enum:
               - realtek,rtl8821cs-bt
-          - const: realtek,rtl8822cs-bt
+          - const: realtek,rtl8723bs-bt
 
   device-wake-gpios:
     maxItems: 1
index 7f4f36a..739a08f 100644 (file)
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: Canaan Kendryte K210 FPIOA
 
 maintainers:
-  - Damien Le Moal <damien.lemoal@wdc.com>
+  - Damien Le Moal <dlemoal@kernel.org>
 
 description:
   The Canaan Kendryte K210 SoC Fully Programmable IO Array (FPIOA)
index c91d3e3..80f9606 100644 (file)
@@ -144,8 +144,9 @@ $defs:
         enum: [0, 1, 2, 3, 4, 5, 6, 7]
 
       qcom,paired:
-        - description:
-            Indicates that the pin should be operating in paired mode.
+        type: boolean
+        description:
+          Indicates that the pin should be operating in paired mode.
 
     required:
       - pins
index ee8a2dc..0c01359 100644 (file)
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: Canaan Kendryte K210 Reset Controller
 
 maintainers:
-  - Damien Le Moal <damien.lemoal@wdc.com>
+  - Damien Le Moal <dlemoal@kernel.org>
 
 description: |
   Canaan Kendryte K210 reset controller driver which supports the SoC
index f8f3f28..41fd11f 100644 (file)
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: Canaan SoC-based boards
 
 maintainers:
-  - Damien Le Moal <damien.lemoal@wdc.com>
+  - Damien Le Moal <dlemoal@kernel.org>
 
 description:
   Canaan Kendryte K210 SoC-based boards
index b6a2879..0717426 100644 (file)
@@ -415,6 +415,6 @@ When using the DT, this creates problems for of_platform_populate()
 because it must decide whether to register each node as either a
 platform_device or an amba_device.  This unfortunately complicates the
 device creation model a little bit, but the solution turns out not to
-be too invasive.  If a node is compatible with "arm,amba-primecell", then
+be too invasive.  If a node is compatible with "arm,primecell", then
 of_platform_populate() will register it as an amba_device instead of a
 platform_device.
index 07d5a56..634aa22 100644 (file)
@@ -16,6 +16,24 @@ tested code over experimental code.  We wish to extend these same
 principles to the RISC-V-related code that will be accepted for
 inclusion in the kernel.
 
+Patchwork
+---------
+
+RISC-V has a patchwork instance, where the status of patches can be checked:
+
+  https://patchwork.kernel.org/project/linux-riscv/list/
+
+If your patch does not appear in the default view, the RISC-V maintainers have
+likely either requested changes, or expect it to be applied to another tree.
+
+Automation runs against this patchwork instance, building/testing patches as
+they arrive. The automation applies patches against the current HEAD of the
+RISC-V `for-next` and `fixes` branches, depending on whether the patch has been
+detected as a fix. Failing those, it will use the RISC-V `master` branch.
+The exact commit to which a series has been applied will be noted on patchwork.
+Patches for which any of the checks fail are unlikely to be applied and in most
+cases will need to be resubmitted.
+
 Submit Checklist Addendum
 -------------------------
 We'll only accept patches for new modules or extensions if the
index f79987e..e7b0731 100644 (file)
@@ -14,10 +14,6 @@ Programs can view status of the events via
 /sys/kernel/tracing/user_events_status and can both register and write
 data out via /sys/kernel/tracing/user_events_data.
 
-Programs can also use /sys/kernel/tracing/dynamic_events to register and
-delete user based events via the u: prefix. The format of the command to
-dynamic_events is the same as the ioctl with the u: prefix applied.
-
 Typically programs will register a set of events that they wish to expose to
 tools that can read trace_events (such as ftrace and perf). The registration
 process tells the kernel which address and bit to reflect if any tool has
@@ -144,6 +140,9 @@ its name. Delete will only succeed if there are no references left to the
 event (in both user and kernel space). User programs should use a separate file
 to request deletes than the one used for registration due to this.
 
+**NOTE:** By default events will auto-delete when there are no references left
+to the event. Flags in the future may change this logic.
+
 Unregistering
 -------------
 If after registering an event it is no longer wanted to be updated then it can
index c6aee82..19ba4ae 100644 (file)
@@ -325,6 +325,6 @@ Primecell设备。然而,棘手的一点是,AMBA总线上的所有设备并
 
 当使用DT时,这给of_platform_populate()带来了问题,因为它必须决定是否将
 每个节点注册为platform_device或amba_device。不幸的是,这使设备创建模型
-变得有点复杂,但解决方案原来并不是太具有侵略性。如果一个节点与“arm,amba-primecell”
+变得有点复杂,但解决方案原来并不是太具有侵略性。如果一个节点与“arm,primecell”
 兼容,那么of_platform_populate()将把它注册为amba_device而不是
 platform_device。
index f794002..ad32db6 100644 (file)
@@ -17827,7 +17827,7 @@ F:      tools/testing/selftests/rtc/
 Real-time Linux Analysis (RTLA) tools
 M:     Daniel Bristot de Oliveira <bristot@kernel.org>
 M:     Steven Rostedt <rostedt@goodmis.org>
-L:     linux-trace-devel@vger.kernel.org
+L:     linux-trace-kernel@vger.kernel.org
 S:     Maintained
 F:     Documentation/tools/rtla/
 F:     tools/tracing/rtla/
@@ -18397,7 +18397,7 @@ F:      drivers/infiniband/ulp/rtrs/
 RUNTIME VERIFICATION (RV)
 M:     Daniel Bristot de Oliveira <bristot@kernel.org>
 M:     Steven Rostedt <rostedt@goodmis.org>
-L:     linux-trace-devel@vger.kernel.org
+L:     linux-trace-kernel@vger.kernel.org
 S:     Maintained
 F:     Documentation/trace/rv/
 F:     include/linux/rv.h
@@ -19140,6 +19140,9 @@ SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS
 M:     Karsten Graul <kgraul@linux.ibm.com>
 M:     Wenjia Zhang <wenjia@linux.ibm.com>
 M:     Jan Karcher <jaka@linux.ibm.com>
+R:     D. Wythe <alibuda@linux.alibaba.com>
+R:     Tony Lu <tonylu@linux.alibaba.com>
+R:     Wen Gu <guwen@linux.alibaba.com>
 L:     linux-s390@vger.kernel.org
 S:     Supported
 F:     net/smc/
index 0d3a9d3..b68b43c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 VERSION = 6
 PATCHLEVEL = 4
 SUBLEVEL = 0
-EXTRAVERSION = -rc6
+EXTRAVERSION = -rc7
 NAME = Hurr durr I'ma ninja sloth
 
 # *DOCUMENTATION*
index a406454..f1b8a04 100644 (file)
@@ -67,7 +67,7 @@ static int __init hyperv_init(void)
        if (ret)
                return ret;
 
-       ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "arm64/hyperv_init:online",
+       ret = cpuhp_setup_state(CPUHP_AP_HYPERV_ONLINE, "arm64/hyperv_init:online",
                                hv_common_cpu_init, hv_common_cpu_die);
        if (ret < 0) {
                hv_common_free();
index b3323ab..35e8a52 100644 (file)
@@ -1496,7 +1496,7 @@ __BUILD_CSR_OP(tlbidx)
 #define write_fcsr(dest, val) \
 do {   \
        __asm__ __volatile__(   \
-       "       movgr2fcsr      %0, "__stringify(dest)" \n"     \
+       "       movgr2fcsr      "__stringify(dest)", %0 \n"     \
        : : "r" (val)); \
 } while (0)
 
index 8b98d22..de46a6b 100644 (file)
 #define        _PAGE_PFN_SHIFT         12
 #define        _PAGE_SWP_EXCLUSIVE_SHIFT 23
 #define        _PAGE_PFN_END_SHIFT     48
+#define        _PAGE_PRESENT_INVALID_SHIFT 60
 #define        _PAGE_NO_READ_SHIFT     61
 #define        _PAGE_NO_EXEC_SHIFT     62
 #define        _PAGE_RPLV_SHIFT        63
 
 /* Used by software */
 #define _PAGE_PRESENT          (_ULCAST_(1) << _PAGE_PRESENT_SHIFT)
+#define _PAGE_PRESENT_INVALID  (_ULCAST_(1) << _PAGE_PRESENT_INVALID_SHIFT)
 #define _PAGE_WRITE            (_ULCAST_(1) << _PAGE_WRITE_SHIFT)
 #define _PAGE_ACCESSED         (_ULCAST_(1) << _PAGE_ACCESSED_SHIFT)
 #define _PAGE_MODIFIED         (_ULCAST_(1) << _PAGE_MODIFIED_SHIFT)
index d28fb9d..9a9f9ff 100644 (file)
@@ -213,7 +213,7 @@ static inline int pmd_bad(pmd_t pmd)
 static inline int pmd_present(pmd_t pmd)
 {
        if (unlikely(pmd_val(pmd) & _PAGE_HUGE))
-               return !!(pmd_val(pmd) & (_PAGE_PRESENT | _PAGE_PROTNONE));
+               return !!(pmd_val(pmd) & (_PAGE_PRESENT | _PAGE_PROTNONE | _PAGE_PRESENT_INVALID));
 
        return pmd_val(pmd) != (unsigned long)invalid_pte_table;
 }
@@ -558,6 +558,7 @@ static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
 
 static inline pmd_t pmd_mkinvalid(pmd_t pmd)
 {
+       pmd_val(pmd) |= _PAGE_PRESENT_INVALID;
        pmd_val(pmd) &= ~(_PAGE_PRESENT | _PAGE_VALID | _PAGE_DIRTY | _PAGE_PROTNONE);
 
        return pmd;
index 2406c95..021b59c 100644 (file)
@@ -396,6 +396,8 @@ int hw_breakpoint_arch_parse(struct perf_event *bp,
 
        if (hw->ctrl.type != LOONGARCH_BREAKPOINT_EXECUTE)
                alignment_mask = 0x7;
+       else
+               alignment_mask = 0x3;
        offset = hw->address & alignment_mask;
 
        hw->address &= ~alignment_mask;
index ff28f99..0491bf4 100644 (file)
@@ -271,7 +271,7 @@ static void loongarch_pmu_enable_event(struct hw_perf_event *evt, int idx)
        WARN_ON(idx < 0 || idx >= loongarch_pmu.num_counters);
 
        /* Make sure interrupt enabled. */
-       cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(evt->event_base & 0xff) |
+       cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(evt->event_base) |
                (evt->config_base & M_PERFCTL_CONFIG_MASK) | CSR_PERFCTRL_IE;
 
        cpu = (event->cpu >= 0) ? event->cpu : smp_processor_id();
@@ -594,7 +594,7 @@ static struct pmu pmu = {
 
 static unsigned int loongarch_pmu_perf_event_encode(const struct loongarch_perf_event *pev)
 {
-       return (pev->event_id & 0xff);
+       return M_PERFCTL_EVENT(pev->event_id);
 }
 
 static const struct loongarch_perf_event *loongarch_pmu_map_general_event(int idx)
@@ -849,7 +849,7 @@ static void resume_local_counters(void)
 
 static const struct loongarch_perf_event *loongarch_pmu_map_raw_event(u64 config)
 {
-       raw_event.event_id = config & 0xff;
+       raw_event.event_id = M_PERFCTL_EVENT(config);
 
        return &raw_event;
 }
index bdff825..85fae3d 100644 (file)
@@ -485,7 +485,7 @@ static int __init debugfs_unaligned(void)
        struct dentry *d;
 
        d = debugfs_create_dir("loongarch", NULL);
-       if (!d)
+       if (IS_ERR_OR_NULL(d))
                return -ENOMEM;
 
        debugfs_create_u32("unaligned_instructions_user",
index 56339be..0e7e5b0 100644 (file)
@@ -97,7 +97,7 @@
                        rx-fifo-depth = <8192>;
                        tx-fifo-depth = <8192>;
                        address-bits = <48>;
-                       max-frame-size = <1518>;
+                       max-frame-size = <1500>;
                        local-mac-address = [00 00 00 00 00 00];
                        altr,has-supplementary-unicast;
                        altr,enable-sup-addr = <1>;
index d10fb81..3ee3169 100644 (file)
                                interrupt-names = "rx_irq", "tx_irq";
                                rx-fifo-depth = <8192>;
                                tx-fifo-depth = <8192>;
-                               max-frame-size = <1518>;
+                               max-frame-size = <1500>;
                                local-mac-address = [ 00 00 00 00 00 00 ];
                                phy-mode = "rgmii-id";
                                phy-handle = <&phy0>;
index 0f0d4a4..75677b5 100644 (file)
 #include <asm/asmregs.h>
 #include <asm/psw.h>
 
-       sp      =       30
-       gp      =       27
-       ipsw    =       22
-
        /*
         * We provide two versions of each macro to convert from physical
         * to virtual and vice versa. The "_r1" versions take one argument
index 6f5e272..78473d6 100644 (file)
@@ -5,6 +5,11 @@ KCSAN_SANITIZE := n
 
 targets += trampoline_$(BITS).o purgatory.ro
 
+# When profile-guided optimization is enabled, llvm emits two different
+# overlapping text sections, which is not supported by kexec. Remove profile
+# optimization flags.
+KBUILD_CFLAGS := $(filter-out -fprofile-sample-use=% -fprofile-use=%,$(KBUILD_CFLAGS))
+
 LDFLAGS_purgatory.ro := -e purgatory_start -r --no-undefined
 
 $(obj)/purgatory.ro: $(obj)/trampoline_$(BITS).o FORCE
index 5730797..bd2e27f 100644 (file)
@@ -35,6 +35,11 @@ CFLAGS_sha256.o := -D__DISABLE_EXPORTS
 CFLAGS_string.o := -D__DISABLE_EXPORTS
 CFLAGS_ctype.o := -D__DISABLE_EXPORTS
 
+# When profile-guided optimization is enabled, llvm emits two different
+# overlapping text sections, which is not supported by kexec. Remove profile
+# optimization flags.
+KBUILD_CFLAGS := $(filter-out -fprofile-sample-use=% -fprofile-use=%,$(KBUILD_CFLAGS))
+
 # When linking purgatory.ro with -r unresolved symbols are not checked,
 # also link a purgatory.chk binary without -r to check for unresolved symbols.
 PURGATORY_LDFLAGS := -e purgatory_start -z nodefaultlib
index a5f9474..6c04b52 100644 (file)
@@ -416,7 +416,7 @@ void __init hyperv_init(void)
                        goto free_vp_assist_page;
        }
 
-       cpuhp = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/hyperv_init:online",
+       cpuhp = cpuhp_setup_state(CPUHP_AP_HYPERV_ONLINE, "x86/hyperv_init:online",
                                  hv_cpu_init, hv_cpu_die);
        if (cpuhp < 0)
                goto free_ghcb_page;
index 1ba5d3b..85d38b9 100644 (file)
@@ -20,6 +20,8 @@ void __init hv_vtl_init_platform(void)
 {
        pr_info("Linux runs in Hyper-V Virtual Trust Level\n");
 
+       x86_platform.realmode_reserve = x86_init_noop;
+       x86_platform.realmode_init = x86_init_noop;
        x86_init.irqs.pre_vector_init = x86_init_noop;
        x86_init.timers.timer_init = x86_init_noop;
 
index 82fec66..42abd6a 100644 (file)
@@ -14,6 +14,11 @@ $(obj)/sha256.o: $(srctree)/lib/crypto/sha256.c FORCE
 
 CFLAGS_sha256.o := -D__DISABLE_EXPORTS
 
+# When profile-guided optimization is enabled, llvm emits two different
+# overlapping text sections, which is not supported by kexec. Remove profile
+# optimization flags.
+KBUILD_CFLAGS := $(filter-out -fprofile-sample-use=% -fprofile-use=%,$(KBUILD_CFLAGS))
+
 # When linking purgatory.ro with -r unresolved symbols are not checked,
 # also link a purgatory.chk binary without -r to check for unresolved symbols.
 PURGATORY_LDFLAGS := -e purgatory_start -z nodefaultlib
index 0ce64dd..f0b5c9c 100644 (file)
@@ -34,6 +34,8 @@
 #include "blk-ioprio.h"
 #include "blk-throttle.h"
 
+static void __blkcg_rstat_flush(struct blkcg *blkcg, int cpu);
+
 /*
  * blkcg_pol_mutex protects blkcg_policy[] and policy [de]activation.
  * blkcg_pol_register_mutex nests outside of it and synchronizes entire
@@ -56,6 +58,8 @@ static LIST_HEAD(all_blkcgs);         /* protected by blkcg_pol_mutex */
 
 bool blkcg_debug_stats = false;
 
+static DEFINE_RAW_SPINLOCK(blkg_stat_lock);
+
 #define BLKG_DESTROY_BATCH_SIZE  64
 
 /*
@@ -163,10 +167,20 @@ static void blkg_free(struct blkcg_gq *blkg)
 static void __blkg_release(struct rcu_head *rcu)
 {
        struct blkcg_gq *blkg = container_of(rcu, struct blkcg_gq, rcu_head);
+       struct blkcg *blkcg = blkg->blkcg;
+       int cpu;
 
 #ifdef CONFIG_BLK_CGROUP_PUNT_BIO
        WARN_ON(!bio_list_empty(&blkg->async_bios));
 #endif
+       /*
+        * Flush all the non-empty percpu lockless lists before releasing
+        * us, given these stat belongs to us.
+        *
+        * blkg_stat_lock is for serializing blkg stat update
+        */
+       for_each_possible_cpu(cpu)
+               __blkcg_rstat_flush(blkcg, cpu);
 
        /* release the blkcg and parent blkg refs this blkg has been holding */
        css_put(&blkg->blkcg->css);
@@ -951,17 +965,12 @@ static void blkcg_iostat_update(struct blkcg_gq *blkg, struct blkg_iostat *cur,
        u64_stats_update_end_irqrestore(&blkg->iostat.sync, flags);
 }
 
-static void blkcg_rstat_flush(struct cgroup_subsys_state *css, int cpu)
+static void __blkcg_rstat_flush(struct blkcg *blkcg, int cpu)
 {
-       struct blkcg *blkcg = css_to_blkcg(css);
        struct llist_head *lhead = per_cpu_ptr(blkcg->lhead, cpu);
        struct llist_node *lnode;
        struct blkg_iostat_set *bisc, *next_bisc;
 
-       /* Root-level stats are sourced from system-wide IO stats */
-       if (!cgroup_parent(css->cgroup))
-               return;
-
        rcu_read_lock();
 
        lnode = llist_del_all(lhead);
@@ -969,6 +978,14 @@ static void blkcg_rstat_flush(struct cgroup_subsys_state *css, int cpu)
                goto out;
 
        /*
+        * For covering concurrent parent blkg update from blkg_release().
+        *
+        * When flushing from cgroup, cgroup_rstat_lock is always held, so
+        * this lock won't cause contention most of time.
+        */
+       raw_spin_lock(&blkg_stat_lock);
+
+       /*
         * Iterate only the iostat_cpu's queued in the lockless list.
         */
        llist_for_each_entry_safe(bisc, next_bisc, lnode, lnode) {
@@ -991,13 +1008,19 @@ static void blkcg_rstat_flush(struct cgroup_subsys_state *css, int cpu)
                if (parent && parent->parent)
                        blkcg_iostat_update(parent, &blkg->iostat.cur,
                                            &blkg->iostat.last);
-               percpu_ref_put(&blkg->refcnt);
        }
-
+       raw_spin_unlock(&blkg_stat_lock);
 out:
        rcu_read_unlock();
 }
 
+static void blkcg_rstat_flush(struct cgroup_subsys_state *css, int cpu)
+{
+       /* Root-level stats are sourced from system-wide IO stats */
+       if (cgroup_parent(css->cgroup))
+               __blkcg_rstat_flush(css_to_blkcg(css), cpu);
+}
+
 /*
  * We source root cgroup stats from the system-wide stats to avoid
  * tracking the same information twice and incurring overhead when no
@@ -2075,7 +2098,6 @@ void blk_cgroup_bio_start(struct bio *bio)
 
                llist_add(&bis->lnode, lhead);
                WRITE_ONCE(bis->lqueued, true);
-               percpu_ref_get(&bis->blkg->refcnt);
        }
 
        u64_stats_update_end_irqrestore(&bis->sync, flags);
index 2d0828d..b5ba550 100644 (file)
@@ -97,6 +97,7 @@ static int qaic_open(struct drm_device *dev, struct drm_file *file)
 
 cleanup_usr:
        cleanup_srcu_struct(&usr->qddev_lock);
+       ida_free(&qaic_usrs, usr->handle);
 free_usr:
        kfree(usr);
 dev_unlock:
@@ -224,6 +225,9 @@ static void qaic_destroy_drm_device(struct qaic_device *qdev, s32 partition_id)
        struct qaic_user *usr;
 
        qddev = qdev->qddev;
+       qdev->qddev = NULL;
+       if (!qddev)
+               return;
 
        /*
         * Existing users get unresolvable errors till they close FDs.
index 19aff80..8d51269 100644 (file)
@@ -9,8 +9,6 @@
 #include <linux/idr.h>
 #include <linux/io.h>
 
-#include <linux/arm-smccc.h>
-
 static struct acpi_ffh_info ffh_ctx;
 
 int __weak acpi_ffh_address_space_arch_setup(void *handler_ctxt,
index 77186f0..539e700 100644 (file)
@@ -201,11 +201,19 @@ static void byt_i2c_setup(struct lpss_private_data *pdata)
        writel(0, pdata->mmio_base + LPSS_I2C_ENABLE);
 }
 
-/* BSW PWM used for backlight control by the i915 driver */
+/*
+ * BSW PWM1 is used for backlight control by the i915 driver
+ * BSW PWM2 is used for backlight control for fixed (etched into the glass)
+ * touch controls on some models. These touch-controls have specialized
+ * drivers which know they need the "pwm_soc_lpss_2" con-id.
+ */
 static struct pwm_lookup bsw_pwm_lookup[] = {
        PWM_LOOKUP_WITH_MODULE("80862288:00", 0, "0000:00:02.0",
                               "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL,
                               "pwm-lpss-platform"),
+       PWM_LOOKUP_WITH_MODULE("80862289:00", 0, NULL,
+                              "pwm_soc_lpss_2", 0, PWM_POLARITY_NORMAL,
+                              "pwm-lpss-platform"),
 };
 
 static void bsw_pwm_setup(struct lpss_private_data *pdata)
index ebf8fd3..79bbfe0 100644 (file)
@@ -101,8 +101,6 @@ acpi_status
 acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info,
                       acpi_event_status *event_status);
 
-acpi_status acpi_hw_disable_all_gpes(void);
-
 acpi_status acpi_hw_enable_all_runtime_gpes(void);
 
 acpi_status acpi_hw_enable_all_wakeup_gpes(void);
index 7514e38..5427e49 100644 (file)
@@ -34,7 +34,7 @@
 #define ACPI_BERT_PRINT_MAX_RECORDS 5
 #define ACPI_BERT_PRINT_MAX_LEN 1024
 
-static int bert_disable;
+static int bert_disable __initdata;
 
 /*
  * Print "all" the error records in the BERT table, but avoid huge spam to
index 34ad071..ef59d6e 100644 (file)
@@ -152,7 +152,6 @@ struct ghes_vendor_record_entry {
 };
 
 static struct gen_pool *ghes_estatus_pool;
-static unsigned long ghes_estatus_pool_size_request;
 
 static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
 static atomic_t ghes_estatus_cache_alloced;
@@ -191,7 +190,6 @@ int ghes_estatus_pool_init(unsigned int num_ghes)
        len = GHES_ESTATUS_CACHE_AVG_SIZE * GHES_ESTATUS_CACHE_ALLOCED_MAX;
        len += (num_ghes * GHES_ESOURCE_PREALLOC_MAX_SIZE);
 
-       ghes_estatus_pool_size_request = PAGE_ALIGN(len);
        addr = (unsigned long)vmalloc(PAGE_ALIGN(len));
        if (!addr)
                goto err_pool_alloc;
@@ -1544,6 +1542,8 @@ struct list_head *ghes_get_devices(void)
 
                        pr_warn_once("Force-loading ghes_edac on an unsupported platform. You're on your own!\n");
                }
+       } else if (list_empty(&ghes_devs)) {
+               return NULL;
        }
 
        return &ghes_devs;
index d161ff7..b6ab360 100644 (file)
@@ -530,65 +530,30 @@ static void acpi_notify_device(acpi_handle handle, u32 event, void *data)
        acpi_drv->ops.notify(device, event);
 }
 
-static void acpi_notify_device_fixed(void *data)
-{
-       struct acpi_device *device = data;
-
-       /* Fixed hardware devices have no handles */
-       acpi_notify_device(NULL, ACPI_FIXED_HARDWARE_EVENT, device);
-}
-
-static u32 acpi_device_fixed_event(void *data)
-{
-       acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_notify_device_fixed, data);
-       return ACPI_INTERRUPT_HANDLED;
-}
-
 static int acpi_device_install_notify_handler(struct acpi_device *device,
                                              struct acpi_driver *acpi_drv)
 {
-       acpi_status status;
-
-       if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) {
-               status =
-                   acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
-                                                    acpi_device_fixed_event,
-                                                    device);
-       } else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) {
-               status =
-                   acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
-                                                    acpi_device_fixed_event,
-                                                    device);
-       } else {
-               u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ?
+       u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ?
                                ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY;
+       acpi_status status;
 
-               status = acpi_install_notify_handler(device->handle, type,
-                                                    acpi_notify_device,
-                                                    device);
-       }
-
+       status = acpi_install_notify_handler(device->handle, type,
+                                            acpi_notify_device, device);
        if (ACPI_FAILURE(status))
                return -EINVAL;
+
        return 0;
 }
 
 static void acpi_device_remove_notify_handler(struct acpi_device *device,
                                              struct acpi_driver *acpi_drv)
 {
-       if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) {
-               acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
-                                               acpi_device_fixed_event);
-       } else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) {
-               acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
-                                               acpi_device_fixed_event);
-       } else {
-               u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ?
+       u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ?
                                ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY;
 
-               acpi_remove_notify_handler(device->handle, type,
-                                          acpi_notify_device);
-       }
+       acpi_remove_notify_handler(device->handle, type,
+                                  acpi_notify_device);
+
        acpi_os_wait_events_complete();
 }
 
index 475e1ed..1e76a64 100644 (file)
@@ -78,6 +78,15 @@ static const struct dmi_system_id dmi_lid_quirks[] = {
                .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
        },
        {
+               /* Nextbook Ares 8A tablet, _LID device always reports lid closed */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"),
+                       DMI_MATCH(DMI_BIOS_VERSION, "M882"),
+               },
+               .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
+       },
+       {
                /*
                 * Lenovo Yoga 9 14ITL5, initial notification of the LID device
                 * never happens.
@@ -126,7 +135,6 @@ static const struct dmi_system_id dmi_lid_quirks[] = {
 
 static int acpi_button_add(struct acpi_device *device);
 static void acpi_button_remove(struct acpi_device *device);
-static void acpi_button_notify(struct acpi_device *device, u32 event);
 
 #ifdef CONFIG_PM_SLEEP
 static int acpi_button_suspend(struct device *dev);
@@ -144,7 +152,6 @@ static struct acpi_driver acpi_button_driver = {
        .ops = {
                .add = acpi_button_add,
                .remove = acpi_button_remove,
-               .notify = acpi_button_notify,
        },
        .drv.pm = &acpi_button_pm,
 };
@@ -400,45 +407,65 @@ static void acpi_lid_initialize_state(struct acpi_device *device)
        button->lid_state_initialized = true;
 }
 
-static void acpi_button_notify(struct acpi_device *device, u32 event)
+static void acpi_lid_notify(acpi_handle handle, u32 event, void *data)
 {
-       struct acpi_button *button = acpi_driver_data(device);
+       struct acpi_device *device = data;
+       struct acpi_button *button;
+
+       if (event != ACPI_BUTTON_NOTIFY_STATUS) {
+               acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
+                                 event);
+               return;
+       }
+
+       button = acpi_driver_data(device);
+       if (!button->lid_state_initialized)
+               return;
+
+       acpi_lid_update_state(device, true);
+}
+
+static void acpi_button_notify(acpi_handle handle, u32 event, void *data)
+{
+       struct acpi_device *device = data;
+       struct acpi_button *button;
        struct input_dev *input;
+       int keycode;
 
-       switch (event) {
-       case ACPI_FIXED_HARDWARE_EVENT:
-               event = ACPI_BUTTON_NOTIFY_STATUS;
-               fallthrough;
-       case ACPI_BUTTON_NOTIFY_STATUS:
-               input = button->input;
-               if (button->type == ACPI_BUTTON_TYPE_LID) {
-                       if (button->lid_state_initialized)
-                               acpi_lid_update_state(device, true);
-               } else {
-                       int keycode;
-
-                       acpi_pm_wakeup_event(&device->dev);
-                       if (button->suspended)
-                               break;
-
-                       keycode = test_bit(KEY_SLEEP, input->keybit) ?
-                                               KEY_SLEEP : KEY_POWER;
-                       input_report_key(input, keycode, 1);
-                       input_sync(input);
-                       input_report_key(input, keycode, 0);
-                       input_sync(input);
-
-                       acpi_bus_generate_netlink_event(
-                                       device->pnp.device_class,
-                                       dev_name(&device->dev),
-                                       event, ++button->pushed);
-               }
-               break;
-       default:
+       if (event != ACPI_BUTTON_NOTIFY_STATUS) {
                acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
                                  event);
-               break;
+               return;
        }
+
+       acpi_pm_wakeup_event(&device->dev);
+
+       button = acpi_driver_data(device);
+       if (button->suspended)
+               return;
+
+       input = button->input;
+       keycode = test_bit(KEY_SLEEP, input->keybit) ? KEY_SLEEP : KEY_POWER;
+
+       input_report_key(input, keycode, 1);
+       input_sync(input);
+       input_report_key(input, keycode, 0);
+       input_sync(input);
+
+       acpi_bus_generate_netlink_event(device->pnp.device_class,
+                                       dev_name(&device->dev),
+                                       event, ++button->pushed);
+}
+
+static void acpi_button_notify_run(void *data)
+{
+       acpi_button_notify(NULL, ACPI_BUTTON_NOTIFY_STATUS, data);
+}
+
+static u32 acpi_button_event(void *data)
+{
+       acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_button_notify_run, data);
+       return ACPI_INTERRUPT_HANDLED;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -480,11 +507,13 @@ static int acpi_lid_input_open(struct input_dev *input)
 
 static int acpi_button_add(struct acpi_device *device)
 {
+       acpi_notify_handler handler;
        struct acpi_button *button;
        struct input_dev *input;
        const char *hid = acpi_device_hid(device);
+       acpi_status status;
        char *name, *class;
-       int error;
+       int error = 0;
 
        if (!strcmp(hid, ACPI_BUTTON_HID_LID) &&
             lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED)
@@ -508,17 +537,20 @@ static int acpi_button_add(struct acpi_device *device)
        if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
            !strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
                button->type = ACPI_BUTTON_TYPE_POWER;
+               handler = acpi_button_notify;
                strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
                sprintf(class, "%s/%s",
                        ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
        } else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
                   !strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
                button->type = ACPI_BUTTON_TYPE_SLEEP;
+               handler = acpi_button_notify;
                strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP);
                sprintf(class, "%s/%s",
                        ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
        } else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
                button->type = ACPI_BUTTON_TYPE_LID;
+               handler = acpi_lid_notify;
                strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
                sprintf(class, "%s/%s",
                        ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
@@ -526,12 +558,15 @@ static int acpi_button_add(struct acpi_device *device)
        } else {
                pr_info("Unsupported hid [%s]\n", hid);
                error = -ENODEV;
-               goto err_free_input;
        }
 
-       error = acpi_button_add_fs(device);
-       if (error)
-               goto err_free_input;
+       if (!error)
+               error = acpi_button_add_fs(device);
+
+       if (error) {
+               input_free_device(input);
+               goto err_free_button;
+       }
 
        snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
 
@@ -559,6 +594,29 @@ static int acpi_button_add(struct acpi_device *device)
        error = input_register_device(input);
        if (error)
                goto err_remove_fs;
+
+       switch (device->device_type) {
+       case ACPI_BUS_TYPE_POWER_BUTTON:
+               status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
+                                                         acpi_button_event,
+                                                         device);
+               break;
+       case ACPI_BUS_TYPE_SLEEP_BUTTON:
+               status = acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
+                                                         acpi_button_event,
+                                                         device);
+               break;
+       default:
+               status = acpi_install_notify_handler(device->handle,
+                                                    ACPI_DEVICE_NOTIFY, handler,
+                                                    device);
+               break;
+       }
+       if (ACPI_FAILURE(status)) {
+               error = -ENODEV;
+               goto err_input_unregister;
+       }
+
        if (button->type == ACPI_BUTTON_TYPE_LID) {
                /*
                 * This assumes there's only one lid device, or if there are
@@ -571,11 +629,11 @@ static int acpi_button_add(struct acpi_device *device)
        pr_info("%s [%s]\n", name, acpi_device_bid(device));
        return 0;
 
- err_remove_fs:
+err_input_unregister:
+       input_unregister_device(input);
+err_remove_fs:
        acpi_button_remove_fs(device);
- err_free_input:
-       input_free_device(input);
- err_free_button:
+err_free_button:
        kfree(button);
        return error;
 }
@@ -584,6 +642,24 @@ static void acpi_button_remove(struct acpi_device *device)
 {
        struct acpi_button *button = acpi_driver_data(device);
 
+       switch (device->device_type) {
+       case ACPI_BUS_TYPE_POWER_BUTTON:
+               acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
+                                               acpi_button_event);
+               break;
+       case ACPI_BUS_TYPE_SLEEP_BUTTON:
+               acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
+                                               acpi_button_event);
+               break;
+       default:
+               acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+                                          button->type == ACPI_BUTTON_TYPE_LID ?
+                                               acpi_lid_notify :
+                                               acpi_button_notify);
+               break;
+       }
+       acpi_os_wait_events_complete();
+
        acpi_button_remove_fs(device);
        input_unregister_device(button->input);
        kfree(button);
index 928899a..8569f55 100644 (file)
@@ -662,21 +662,6 @@ static void advance_transaction(struct acpi_ec *ec, bool interrupt)
 
        ec_dbg_stm("%s (%d)", interrupt ? "IRQ" : "TASK", smp_processor_id());
 
-       /*
-        * Clear GPE_STS upfront to allow subsequent hardware GPE_STS 0->1
-        * changes to always trigger a GPE interrupt.
-        *
-        * GPE STS is a W1C register, which means:
-        *
-        * 1. Software can clear it without worrying about clearing the other
-        *    GPEs' STS bits when the hardware sets them in parallel.
-        *
-        * 2. As long as software can ensure only clearing it when it is set,
-        *    hardware won't set it in parallel.
-        */
-       if (ec->gpe >= 0 && acpi_ec_gpe_status_set(ec))
-               acpi_clear_gpe(NULL, ec->gpe);
-
        status = acpi_ec_read_status(ec);
 
        /*
@@ -1287,6 +1272,22 @@ static void acpi_ec_handle_interrupt(struct acpi_ec *ec)
        unsigned long flags;
 
        spin_lock_irqsave(&ec->lock, flags);
+
+       /*
+        * Clear GPE_STS upfront to allow subsequent hardware GPE_STS 0->1
+        * changes to always trigger a GPE interrupt.
+        *
+        * GPE STS is a W1C register, which means:
+        *
+        * 1. Software can clear it without worrying about clearing the other
+        *    GPEs' STS bits when the hardware sets them in parallel.
+        *
+        * 2. As long as software can ensure only clearing it when it is set,
+        *    hardware won't set it in parallel.
+        */
+       if (ec->gpe >= 0 && acpi_ec_gpe_status_set(ec))
+               acpi_clear_gpe(NULL, ec->gpe);
+
        advance_transaction(ec, true);
        spin_unlock_irqrestore(&ec->lock, flags);
 }
index 6023ad6..573bc0d 100644 (file)
@@ -347,4 +347,6 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
 void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev);
 bool intel_fwa_supported(struct nvdimm_bus *nvdimm_bus);
 extern struct device_attribute dev_attr_firmware_activate_noidle;
+void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem);
+
 #endif /* __NFIT_H__ */
index 0800a9d..1dd8d5a 100644 (file)
@@ -470,52 +470,6 @@ static const struct dmi_system_id asus_laptop[] = {
        { }
 };
 
-static const struct dmi_system_id lenovo_laptop[] = {
-       {
-               .ident = "LENOVO IdeaPad Flex 5 14ALC7",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "82R9"),
-               },
-       },
-       {
-               .ident = "LENOVO IdeaPad Flex 5 16ALC7",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "82RA"),
-               },
-       },
-       { }
-};
-
-static const struct dmi_system_id tongfang_gm_rg[] = {
-       {
-               .ident = "TongFang GMxRGxx/XMG CORE 15 (M22)/TUXEDO Stellaris 15 Gen4 AMD",
-               .matches = {
-                       DMI_MATCH(DMI_BOARD_NAME, "GMxRGxx"),
-               },
-       },
-       { }
-};
-
-static const struct dmi_system_id maingear_laptop[] = {
-       {
-               .ident = "MAINGEAR Vector Pro 2 15",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Micro Electronics Inc"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "MG-VCP2-15A3070T"),
-               }
-       },
-       {
-               .ident = "MAINGEAR Vector Pro 2 17",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Micro Electronics Inc"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "MG-VCP2-17A3070T"),
-               },
-       },
-       { }
-};
-
 static const struct dmi_system_id lg_laptop[] = {
        {
                .ident = "LG Electronics 17U70P",
@@ -539,10 +493,6 @@ struct irq_override_cmp {
 static const struct irq_override_cmp override_table[] = {
        { medion_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false },
        { asus_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false },
-       { lenovo_laptop, 6, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true },
-       { lenovo_laptop, 10, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true },
-       { tongfang_gm_rg, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true },
-       { maingear_laptop, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true },
        { lg_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false },
 };
 
@@ -562,16 +512,6 @@ static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity,
                        return entry->override;
        }
 
-#ifdef CONFIG_X86
-       /*
-        * IRQ override isn't needed on modern AMD Zen systems and
-        * this override breaks active low IRQs on AMD Ryzen 6000 and
-        * newer systems. Skip it.
-        */
-       if (boot_cpu_has(X86_FEATURE_ZEN))
-               return false;
-#endif
-
        return true;
 }
 
index 0c6f06a..1c3e1e2 100644 (file)
@@ -2029,8 +2029,6 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep)
        return count;
 }
 
-static bool acpi_bus_scan_second_pass;
-
 static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
                                      struct acpi_device **adev_p)
 {
@@ -2050,10 +2048,8 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
                        return AE_OK;
 
                /* Bail out if there are dependencies. */
-               if (acpi_scan_check_dep(handle, check_dep) > 0) {
-                       acpi_bus_scan_second_pass = true;
+               if (acpi_scan_check_dep(handle, check_dep) > 0)
                        return AE_CTRL_DEPTH;
-               }
 
                fallthrough;
        case ACPI_TYPE_ANY:     /* for ACPI_ROOT_OBJECT */
@@ -2301,6 +2297,12 @@ static bool acpi_scan_clear_dep_queue(struct acpi_device *adev)
        return true;
 }
 
+static void acpi_scan_delete_dep_data(struct acpi_dep_data *dep)
+{
+       list_del(&dep->node);
+       kfree(dep);
+}
+
 static int acpi_scan_clear_dep(struct acpi_dep_data *dep, void *data)
 {
        struct acpi_device *adev = acpi_get_acpi_dev(dep->consumer);
@@ -2311,8 +2313,10 @@ static int acpi_scan_clear_dep(struct acpi_dep_data *dep, void *data)
                        acpi_dev_put(adev);
        }
 
-       list_del(&dep->node);
-       kfree(dep);
+       if (dep->free_when_met)
+               acpi_scan_delete_dep_data(dep);
+       else
+               dep->met = true;
 
        return 0;
 }
@@ -2406,6 +2410,55 @@ struct acpi_device *acpi_dev_get_next_consumer_dev(struct acpi_device *supplier,
 }
 EXPORT_SYMBOL_GPL(acpi_dev_get_next_consumer_dev);
 
+static void acpi_scan_postponed_branch(acpi_handle handle)
+{
+       struct acpi_device *adev = NULL;
+
+       if (ACPI_FAILURE(acpi_bus_check_add(handle, false, &adev)))
+               return;
+
+       acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
+                           acpi_bus_check_add_2, NULL, NULL, (void **)&adev);
+       acpi_bus_attach(adev, NULL);
+}
+
+static void acpi_scan_postponed(void)
+{
+       struct acpi_dep_data *dep, *tmp;
+
+       mutex_lock(&acpi_dep_list_lock);
+
+       list_for_each_entry_safe(dep, tmp, &acpi_dep_list, node) {
+               acpi_handle handle = dep->consumer;
+
+               /*
+                * In case there are multiple acpi_dep_list entries with the
+                * same consumer, skip the current entry if the consumer device
+                * object corresponding to it is present already.
+                */
+               if (!acpi_fetch_acpi_dev(handle)) {
+                       /*
+                        * Even though the lock is released here, tmp is
+                        * guaranteed to be valid, because none of the list
+                        * entries following dep is marked as "free when met"
+                        * and so they cannot be deleted.
+                        */
+                       mutex_unlock(&acpi_dep_list_lock);
+
+                       acpi_scan_postponed_branch(handle);
+
+                       mutex_lock(&acpi_dep_list_lock);
+               }
+
+               if (dep->met)
+                       acpi_scan_delete_dep_data(dep);
+               else
+                       dep->free_when_met = true;
+       }
+
+       mutex_unlock(&acpi_dep_list_lock);
+}
+
 /**
  * acpi_bus_scan - Add ACPI device node objects in a given namespace scope.
  * @handle: Root of the namespace scope to scan.
@@ -2424,8 +2477,6 @@ int acpi_bus_scan(acpi_handle handle)
 {
        struct acpi_device *device = NULL;
 
-       acpi_bus_scan_second_pass = false;
-
        /* Pass 1: Avoid enumerating devices with missing dependencies. */
 
        if (ACPI_SUCCESS(acpi_bus_check_add(handle, true, &device)))
@@ -2438,19 +2489,9 @@ int acpi_bus_scan(acpi_handle handle)
 
        acpi_bus_attach(device, (void *)true);
 
-       if (!acpi_bus_scan_second_pass)
-               return 0;
-
        /* Pass 2: Enumerate all of the remaining devices. */
 
-       device = NULL;
-
-       if (ACPI_SUCCESS(acpi_bus_check_add(handle, false, &device)))
-               acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
-                                   acpi_bus_check_add_2, NULL, NULL,
-                                   (void **)&device);
-
-       acpi_bus_attach(device, NULL);
+       acpi_scan_postponed();
 
        return 0;
 }
index 72470b9..808484d 100644 (file)
@@ -636,11 +636,19 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
        }
 
        /*
-        * Disable and clear GPE status before interrupt is enabled. Some GPEs
-        * (like wakeup GPE) haven't handler, this can avoid such GPE misfire.
-        * acpi_leave_sleep_state will reenable specific GPEs later
+        * Disable all GPE and clear their status bits before interrupts are
+        * enabled. Some GPEs (like wakeup GPEs) have no handlers and this can
+        * prevent them from producing spurious interrups.
+        *
+        * acpi_leave_sleep_state() will reenable specific GPEs later.
+        *
+        * Because this code runs on one CPU with disabled interrupts (all of
+        * the other CPUs are offline at this time), it need not acquire any
+        * sleeping locks which may trigger an implicit preemption point even
+        * if there is no contention, so avoid doing that by using a low-level
+        * library routine here.
         */
-       acpi_disable_all_gpes();
+       acpi_hw_disable_all_gpes();
        /* Allow EC transactions to happen. */
        acpi_ec_unblock_transactions();
 
@@ -840,7 +848,7 @@ void __weak acpi_s2idle_setup(void)
        s2idle_set_ops(&acpi_s2idle_ops);
 }
 
-static void acpi_sleep_suspend_setup(void)
+static void __init acpi_sleep_suspend_setup(void)
 {
        bool suspend_ops_needed = false;
        int i;
index 4720a36..f9f6ebb 100644 (file)
 #define ACPI_THERMAL_NOTIFY_HOT                0xF1
 #define ACPI_THERMAL_MODE_ACTIVE       0x00
 
-#define ACPI_THERMAL_MAX_ACTIVE        10
-#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
+#define ACPI_THERMAL_MAX_ACTIVE                10
+#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
 
-MODULE_AUTHOR("Paul Diefenbaugh");
-MODULE_DESCRIPTION("ACPI Thermal Zone Driver");
-MODULE_LICENSE("GPL");
+#define ACPI_TRIPS_CRITICAL    BIT(0)
+#define ACPI_TRIPS_HOT         BIT(1)
+#define ACPI_TRIPS_PASSIVE     BIT(2)
+#define ACPI_TRIPS_ACTIVE      BIT(3)
+#define ACPI_TRIPS_DEVICES     BIT(4)
+
+#define ACPI_TRIPS_THRESHOLDS  (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)
+
+#define ACPI_TRIPS_INIT                (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \
+                                ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \
+                                ACPI_TRIPS_DEVICES)
+
+/*
+ * This exception is thrown out in two cases:
+ * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
+ *   when re-evaluating the AML code.
+ * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.
+ *   We need to re-bind the cooling devices of a thermal zone when this occurs.
+ */
+#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, tz, str) \
+do { \
+       if (flags != ACPI_TRIPS_INIT) \
+               acpi_handle_info(tz->device->handle, \
+                       "ACPI thermal trip point %s changed\n" \
+                       "Please report to linux-acpi@vger.kernel.org\n", str); \
+} while (0)
 
 static int act;
 module_param(act, int, 0644);
@@ -73,75 +96,30 @@ MODULE_PARM_DESC(psv, "Disable or override all passive trip points.");
 
 static struct workqueue_struct *acpi_thermal_pm_queue;
 
-static int acpi_thermal_add(struct acpi_device *device);
-static void acpi_thermal_remove(struct acpi_device *device);
-static void acpi_thermal_notify(struct acpi_device *device, u32 event);
-
-static const struct acpi_device_id  thermal_device_ids[] = {
-       {ACPI_THERMAL_HID, 0},
-       {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, thermal_device_ids);
-
-#ifdef CONFIG_PM_SLEEP
-static int acpi_thermal_suspend(struct device *dev);
-static int acpi_thermal_resume(struct device *dev);
-#else
-#define acpi_thermal_suspend NULL
-#define acpi_thermal_resume NULL
-#endif
-static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume);
-
-static struct acpi_driver acpi_thermal_driver = {
-       .name = "thermal",
-       .class = ACPI_THERMAL_CLASS,
-       .ids = thermal_device_ids,
-       .ops = {
-               .add = acpi_thermal_add,
-               .remove = acpi_thermal_remove,
-               .notify = acpi_thermal_notify,
-               },
-       .drv.pm = &acpi_thermal_pm,
-};
-
-struct acpi_thermal_state {
-       u8 critical:1;
-       u8 hot:1;
-       u8 passive:1;
-       u8 active:1;
-       u8 reserved:4;
-       int active_index;
-};
-
-struct acpi_thermal_state_flags {
-       u8 valid:1;
-       u8 enabled:1;
-       u8 reserved:6;
-};
-
 struct acpi_thermal_critical {
-       struct acpi_thermal_state_flags flags;
        unsigned long temperature;
+       bool valid;
 };
 
 struct acpi_thermal_hot {
-       struct acpi_thermal_state_flags flags;
        unsigned long temperature;
+       bool valid;
 };
 
 struct acpi_thermal_passive {
-       struct acpi_thermal_state_flags flags;
+       struct acpi_handle_list devices;
        unsigned long temperature;
        unsigned long tc1;
        unsigned long tc2;
        unsigned long tsp;
-       struct acpi_handle_list devices;
+       bool valid;
 };
 
 struct acpi_thermal_active {
-       struct acpi_thermal_state_flags flags;
-       unsigned long temperature;
        struct acpi_handle_list devices;
+       unsigned long temperature;
+       bool valid;
+       bool enabled;
 };
 
 struct acpi_thermal_trips {
@@ -151,12 +129,6 @@ struct acpi_thermal_trips {
        struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE];
 };
 
-struct acpi_thermal_flags {
-       u8 cooling_mode:1;      /* _SCP */
-       u8 devices:1;           /* _TZD */
-       u8 reserved:6;
-};
-
 struct acpi_thermal {
        struct acpi_device *device;
        acpi_bus_id name;
@@ -164,8 +136,6 @@ struct acpi_thermal {
        unsigned long last_temperature;
        unsigned long polling_frequency;
        volatile u8 zombie;
-       struct acpi_thermal_flags flags;
-       struct acpi_thermal_state state;
        struct acpi_thermal_trips trips;
        struct acpi_handle_list devices;
        struct thermal_zone_device *thermal_zone;
@@ -220,52 +190,12 @@ static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz)
        return 0;
 }
 
-static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode)
-{
-       if (!tz)
-               return -EINVAL;
-
-       if (ACPI_FAILURE(acpi_execute_simple_method(tz->device->handle,
-                                                   "_SCP", mode)))
-               return -ENODEV;
-
-       return 0;
-}
-
-#define ACPI_TRIPS_CRITICAL    0x01
-#define ACPI_TRIPS_HOT         0x02
-#define ACPI_TRIPS_PASSIVE     0x04
-#define ACPI_TRIPS_ACTIVE      0x08
-#define ACPI_TRIPS_DEVICES     0x10
-
-#define ACPI_TRIPS_REFRESH_THRESHOLDS  (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)
-#define ACPI_TRIPS_REFRESH_DEVICES     ACPI_TRIPS_DEVICES
-
-#define ACPI_TRIPS_INIT      (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT |   \
-                             ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE |  \
-                             ACPI_TRIPS_DEVICES)
-
-/*
- * This exception is thrown out in two cases:
- * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
- *   when re-evaluating the AML code.
- * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.
- *   We need to re-bind the cooling devices of a thermal zone when this occurs.
- */
-#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, tz, str)   \
-do {   \
-       if (flags != ACPI_TRIPS_INIT)   \
-               acpi_handle_info(tz->device->handle,    \
-               "ACPI thermal trip point %s changed\n"  \
-               "Please report to linux-acpi@vger.kernel.org\n", str); \
-} while (0)
-
 static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
 {
        acpi_status status;
        unsigned long long tmp;
        struct acpi_handle_list devices;
-       int valid = 0;
+       bool valid = false;
        int i;
 
        /* Critical Shutdown */
@@ -279,21 +209,21 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
                 * ... so lets discard those as invalid.
                 */
                if (ACPI_FAILURE(status)) {
-                       tz->trips.critical.flags.valid = 0;
+                       tz->trips.critical.valid = false;
                        acpi_handle_debug(tz->device->handle,
                                          "No critical threshold\n");
                } else if (tmp <= 2732) {
                        pr_info(FW_BUG "Invalid critical threshold (%llu)\n", tmp);
-                       tz->trips.critical.flags.valid = 0;
+                       tz->trips.critical.valid = false;
                } else {
-                       tz->trips.critical.flags.valid = 1;
+                       tz->trips.critical.valid = true;
                        acpi_handle_debug(tz->device->handle,
                                          "Found critical threshold [%lu]\n",
                                          tz->trips.critical.temperature);
                }
-               if (tz->trips.critical.flags.valid) {
+               if (tz->trips.critical.valid) {
                        if (crt == -1) {
-                               tz->trips.critical.flags.valid = 0;
+                               tz->trips.critical.valid = false;
                        } else if (crt > 0) {
                                unsigned long crt_k = celsius_to_deci_kelvin(crt);
 
@@ -312,12 +242,12 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
        if (flag & ACPI_TRIPS_HOT) {
                status = acpi_evaluate_integer(tz->device->handle, "_HOT", NULL, &tmp);
                if (ACPI_FAILURE(status)) {
-                       tz->trips.hot.flags.valid = 0;
+                       tz->trips.hot.valid = false;
                        acpi_handle_debug(tz->device->handle,
                                          "No hot threshold\n");
                } else {
                        tz->trips.hot.temperature = tmp;
-                       tz->trips.hot.flags.valid = 1;
+                       tz->trips.hot.valid = true;
                        acpi_handle_debug(tz->device->handle,
                                          "Found hot threshold [%lu]\n",
                                          tz->trips.hot.temperature);
@@ -325,9 +255,9 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
        }
 
        /* Passive (optional) */
-       if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.flags.valid) ||
+       if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.valid) ||
            flag == ACPI_TRIPS_INIT) {
-               valid = tz->trips.passive.flags.valid;
+               valid = tz->trips.passive.valid;
                if (psv == -1) {
                        status = AE_SUPPORT;
                } else if (psv > 0) {
@@ -339,44 +269,44 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
                }
 
                if (ACPI_FAILURE(status)) {
-                       tz->trips.passive.flags.valid = 0;
+                       tz->trips.passive.valid = false;
                } else {
                        tz->trips.passive.temperature = tmp;
-                       tz->trips.passive.flags.valid = 1;
+                       tz->trips.passive.valid = true;
                        if (flag == ACPI_TRIPS_INIT) {
                                status = acpi_evaluate_integer(tz->device->handle,
                                                               "_TC1", NULL, &tmp);
                                if (ACPI_FAILURE(status))
-                                       tz->trips.passive.flags.valid = 0;
+                                       tz->trips.passive.valid = false;
                                else
                                        tz->trips.passive.tc1 = tmp;
 
                                status = acpi_evaluate_integer(tz->device->handle,
                                                               "_TC2", NULL, &tmp);
                                if (ACPI_FAILURE(status))
-                                       tz->trips.passive.flags.valid = 0;
+                                       tz->trips.passive.valid = false;
                                else
                                        tz->trips.passive.tc2 = tmp;
 
                                status = acpi_evaluate_integer(tz->device->handle,
                                                               "_TSP", NULL, &tmp);
                                if (ACPI_FAILURE(status))
-                                       tz->trips.passive.flags.valid = 0;
+                                       tz->trips.passive.valid = false;
                                else
                                        tz->trips.passive.tsp = tmp;
                        }
                }
        }
-       if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) {
+       if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.valid) {
                memset(&devices, 0, sizeof(struct acpi_handle_list));
                status = acpi_evaluate_reference(tz->device->handle, "_PSL",
                                                 NULL, &devices);
                if (ACPI_FAILURE(status)) {
                        acpi_handle_info(tz->device->handle,
                                         "Invalid passive threshold\n");
-                       tz->trips.passive.flags.valid = 0;
+                       tz->trips.passive.valid = false;
                } else {
-                       tz->trips.passive.flags.valid = 1;
+                       tz->trips.passive.valid = true;
                }
 
                if (memcmp(&tz->trips.passive.devices, &devices,
@@ -387,24 +317,24 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
                }
        }
        if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) {
-               if (valid != tz->trips.passive.flags.valid)
+               if (valid != tz->trips.passive.valid)
                        ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "state");
        }
 
        /* Active (optional) */
        for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
                char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
-               valid = tz->trips.active[i].flags.valid;
+               valid = tz->trips.active[i].valid;
 
                if (act == -1)
                        break; /* disable all active trip points */
 
                if (flag == ACPI_TRIPS_INIT || ((flag & ACPI_TRIPS_ACTIVE) &&
-                   tz->trips.active[i].flags.valid)) {
+                   tz->trips.active[i].valid)) {
                        status = acpi_evaluate_integer(tz->device->handle,
                                                       name, NULL, &tmp);
                        if (ACPI_FAILURE(status)) {
-                               tz->trips.active[i].flags.valid = 0;
+                               tz->trips.active[i].valid = false;
                                if (i == 0)
                                        break;
 
@@ -426,21 +356,21 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
                                break;
                        } else {
                                tz->trips.active[i].temperature = tmp;
-                               tz->trips.active[i].flags.valid = 1;
+                               tz->trips.active[i].valid = true;
                        }
                }
 
                name[2] = 'L';
-               if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid) {
+               if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].valid) {
                        memset(&devices, 0, sizeof(struct acpi_handle_list));
                        status = acpi_evaluate_reference(tz->device->handle,
                                                         name, NULL, &devices);
                        if (ACPI_FAILURE(status)) {
                                acpi_handle_info(tz->device->handle,
                                                 "Invalid active%d threshold\n", i);
-                               tz->trips.active[i].flags.valid = 0;
+                               tz->trips.active[i].valid = false;
                        } else {
-                               tz->trips.active[i].flags.valid = 1;
+                               tz->trips.active[i].valid = true;
                        }
 
                        if (memcmp(&tz->trips.active[i].devices, &devices,
@@ -451,10 +381,10 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
                        }
                }
                if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES))
-                       if (valid != tz->trips.active[i].flags.valid)
+                       if (valid != tz->trips.active[i].valid)
                                ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "state");
 
-               if (!tz->trips.active[i].flags.valid)
+               if (!tz->trips.active[i].valid)
                        break;
        }
 
@@ -474,17 +404,18 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
 
 static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
 {
-       int i, valid, ret = acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);
+       int i, ret = acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);
+       bool valid;
 
        if (ret)
                return ret;
 
-       valid = tz->trips.critical.flags.valid |
-               tz->trips.hot.flags.valid |
-               tz->trips.passive.flags.valid;
+       valid = tz->trips.critical.valid |
+               tz->trips.hot.valid |
+               tz->trips.passive.valid;
 
        for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
-               valid |= tz->trips.active[i].flags.valid;
+               valid = valid || tz->trips.active[i].valid;
 
        if (!valid) {
                pr_warn(FW_BUG "No valid trip found\n");
@@ -521,7 +452,7 @@ static int thermal_get_trip_type(struct thermal_zone_device *thermal,
        if (!tz || trip < 0)
                return -EINVAL;
 
-       if (tz->trips.critical.flags.valid) {
+       if (tz->trips.critical.valid) {
                if (!trip) {
                        *type = THERMAL_TRIP_CRITICAL;
                        return 0;
@@ -529,7 +460,7 @@ static int thermal_get_trip_type(struct thermal_zone_device *thermal,
                trip--;
        }
 
-       if (tz->trips.hot.flags.valid) {
+       if (tz->trips.hot.valid) {
                if (!trip) {
                        *type = THERMAL_TRIP_HOT;
                        return 0;
@@ -537,7 +468,7 @@ static int thermal_get_trip_type(struct thermal_zone_device *thermal,
                trip--;
        }
 
-       if (tz->trips.passive.flags.valid) {
+       if (tz->trips.passive.valid) {
                if (!trip) {
                        *type = THERMAL_TRIP_PASSIVE;
                        return 0;
@@ -545,7 +476,7 @@ static int thermal_get_trip_type(struct thermal_zone_device *thermal,
                trip--;
        }
 
-       for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && tz->trips.active[i].flags.valid; i++) {
+       for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && tz->trips.active[i].valid; i++) {
                if (!trip) {
                        *type = THERMAL_TRIP_ACTIVE;
                        return 0;
@@ -565,7 +496,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
        if (!tz || trip < 0)
                return -EINVAL;
 
-       if (tz->trips.critical.flags.valid) {
+       if (tz->trips.critical.valid) {
                if (!trip) {
                        *temp = deci_kelvin_to_millicelsius_with_offset(
                                        tz->trips.critical.temperature,
@@ -575,7 +506,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
                trip--;
        }
 
-       if (tz->trips.hot.flags.valid) {
+       if (tz->trips.hot.valid) {
                if (!trip) {
                        *temp = deci_kelvin_to_millicelsius_with_offset(
                                        tz->trips.hot.temperature,
@@ -585,7 +516,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
                trip--;
        }
 
-       if (tz->trips.passive.flags.valid) {
+       if (tz->trips.passive.valid) {
                if (!trip) {
                        *temp = deci_kelvin_to_millicelsius_with_offset(
                                        tz->trips.passive.temperature,
@@ -596,7 +527,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
        }
 
        for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
-               tz->trips.active[i].flags.valid; i++) {
+               tz->trips.active[i].valid; i++) {
                if (!trip) {
                        *temp = deci_kelvin_to_millicelsius_with_offset(
                                        tz->trips.active[i].temperature,
@@ -614,7 +545,7 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
 {
        struct acpi_thermal *tz = thermal_zone_device_priv(thermal);
 
-       if (tz->trips.critical.flags.valid) {
+       if (tz->trips.critical.valid) {
                *temperature = deci_kelvin_to_millicelsius_with_offset(
                                        tz->trips.critical.temperature,
                                        tz->kelvin_offset);
@@ -700,13 +631,13 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
        int trip = -1;
        int result = 0;
 
-       if (tz->trips.critical.flags.valid)
+       if (tz->trips.critical.valid)
                trip++;
 
-       if (tz->trips.hot.flags.valid)
+       if (tz->trips.hot.valid)
                trip++;
 
-       if (tz->trips.passive.flags.valid) {
+       if (tz->trips.passive.valid) {
                trip++;
                for (i = 0; i < tz->trips.passive.devices.count; i++) {
                        handle = tz->trips.passive.devices.handles[i];
@@ -731,7 +662,7 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
        }
 
        for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
-               if (!tz->trips.active[i].flags.valid)
+               if (!tz->trips.active[i].valid)
                        break;
 
                trip++;
@@ -819,19 +750,19 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
        acpi_status status;
        int i;
 
-       if (tz->trips.critical.flags.valid)
+       if (tz->trips.critical.valid)
                trips++;
 
-       if (tz->trips.hot.flags.valid)
+       if (tz->trips.hot.valid)
                trips++;
 
-       if (tz->trips.passive.flags.valid)
+       if (tz->trips.passive.valid)
                trips++;
 
-       for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && tz->trips.active[i].flags.valid;
+       for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && tz->trips.active[i].valid;
             i++, trips++);
 
-       if (tz->trips.passive.flags.valid)
+       if (tz->trips.passive.valid)
                tz->thermal_zone = thermal_zone_device_register("acpitz", trips, 0, tz,
                                                                &acpi_thermal_zone_ops, NULL,
                                                                tz->trips.passive.tsp * 100,
@@ -906,13 +837,13 @@ static void acpi_thermal_notify(struct acpi_device *device, u32 event)
                acpi_queue_thermal_check(tz);
                break;
        case ACPI_THERMAL_NOTIFY_THRESHOLDS:
-               acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS);
+               acpi_thermal_trips_update(tz, ACPI_TRIPS_THRESHOLDS);
                acpi_queue_thermal_check(tz);
                acpi_bus_generate_netlink_event(device->pnp.device_class,
                                                dev_name(&device->dev), event, 0);
                break;
        case ACPI_THERMAL_NOTIFY_DEVICES:
-               acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES);
+               acpi_thermal_trips_update(tz, ACPI_TRIPS_DEVICES);
                acpi_queue_thermal_check(tz);
                acpi_bus_generate_netlink_event(device->pnp.device_class,
                                                dev_name(&device->dev), event, 0);
@@ -976,9 +907,8 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz)
                return result;
 
        /* Set the cooling mode [_SCP] to active cooling (default) */
-       result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE);
-       if (!result)
-               tz->flags.cooling_mode = 1;
+       acpi_execute_simple_method(tz->device->handle, "_SCP",
+                                  ACPI_THERMAL_MODE_ACTIVE);
 
        /* Get default polling frequency [_TZP] (optional) */
        if (tzp)
@@ -1001,7 +931,7 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz)
  */
 static void acpi_thermal_guess_offset(struct acpi_thermal *tz)
 {
-       if (tz->trips.critical.flags.valid &&
+       if (tz->trips.critical.valid &&
            (tz->trips.critical.temperature % 5) == 1)
                tz->kelvin_offset = 273100;
        else
@@ -1110,27 +1040,48 @@ static int acpi_thermal_resume(struct device *dev)
                return -EINVAL;
 
        for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
-               if (!tz->trips.active[i].flags.valid)
+               if (!tz->trips.active[i].valid)
                        break;
 
-               tz->trips.active[i].flags.enabled = 1;
+               tz->trips.active[i].enabled = true;
                for (j = 0; j < tz->trips.active[i].devices.count; j++) {
                        result = acpi_bus_update_power(
                                        tz->trips.active[i].devices.handles[j],
                                        &power_state);
                        if (result || (power_state != ACPI_STATE_D0)) {
-                               tz->trips.active[i].flags.enabled = 0;
+                               tz->trips.active[i].enabled = false;
                                break;
                        }
                }
-               tz->state.active |= tz->trips.active[i].flags.enabled;
        }
 
        acpi_queue_thermal_check(tz);
 
        return AE_OK;
 }
+#else
+#define acpi_thermal_suspend   NULL
+#define acpi_thermal_resume    NULL
 #endif
+static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume);
+
+static const struct acpi_device_id  thermal_device_ids[] = {
+       {ACPI_THERMAL_HID, 0},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, thermal_device_ids);
+
+static struct acpi_driver acpi_thermal_driver = {
+       .name = "thermal",
+       .class = ACPI_THERMAL_CLASS,
+       .ids = thermal_device_ids,
+       .ops = {
+               .add = acpi_thermal_add,
+               .remove = acpi_thermal_remove,
+               .notify = acpi_thermal_notify,
+               },
+       .drv.pm = &acpi_thermal_pm,
+};
 
 static int thermal_act(const struct dmi_system_id *d) {
        if (act == 0) {
@@ -1236,3 +1187,7 @@ static void __exit acpi_thermal_exit(void)
 
 module_init(acpi_thermal_init);
 module_exit(acpi_thermal_exit);
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION("ACPI Thermal Zone Driver");
+MODULE_LICENSE("GPL");
index 598f548..6353be6 100644 (file)
@@ -19,18 +19,52 @@ static const struct acpi_device_id tiny_power_button_device_ids[] = {
 };
 MODULE_DEVICE_TABLE(acpi, tiny_power_button_device_ids);
 
-static int acpi_noop_add(struct acpi_device *device)
+static void acpi_tiny_power_button_notify(acpi_handle handle, u32 event, void *data)
 {
-       return 0;
+       kill_cad_pid(power_signal, 1);
 }
 
-static void acpi_noop_remove(struct acpi_device *device)
+static void acpi_tiny_power_button_notify_run(void *not_used)
 {
+       acpi_tiny_power_button_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, NULL);
 }
 
-static void acpi_tiny_power_button_notify(struct acpi_device *device, u32 event)
+static u32 acpi_tiny_power_button_event(void *not_used)
 {
-       kill_cad_pid(power_signal, 1);
+       acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_tiny_power_button_notify_run, NULL);
+       return ACPI_INTERRUPT_HANDLED;
+}
+
+static int acpi_tiny_power_button_add(struct acpi_device *device)
+{
+       acpi_status status;
+
+       if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) {
+               status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
+                                                         acpi_tiny_power_button_event,
+                                                         NULL);
+       } else {
+               status = acpi_install_notify_handler(device->handle,
+                                                    ACPI_DEVICE_NOTIFY,
+                                                    acpi_tiny_power_button_notify,
+                                                    NULL);
+       }
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       return 0;
+}
+
+static void acpi_tiny_power_button_remove(struct acpi_device *device)
+{
+       if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) {
+               acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
+                                               acpi_tiny_power_button_event);
+       } else {
+               acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+                                          acpi_tiny_power_button_notify);
+       }
+       acpi_os_wait_events_complete();
 }
 
 static struct acpi_driver acpi_tiny_power_button_driver = {
@@ -38,9 +72,8 @@ static struct acpi_driver acpi_tiny_power_button_driver = {
        .class = "tiny-power-button",
        .ids = tiny_power_button_device_ids,
        .ops = {
-               .add = acpi_noop_add,
-               .remove = acpi_noop_remove,
-               .notify = acpi_tiny_power_button_notify,
+               .add = acpi_tiny_power_button_add,
+               .remove = acpi_tiny_power_button_remove,
        },
 };
 
index bcc25d4..18cc08c 100644 (file)
@@ -471,6 +471,22 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
                },
        },
        {
+        .callback = video_detect_force_native,
+        /* Lenovo ThinkPad X131e (3371 AMD version) */
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "3371"),
+               },
+       },
+       {
+        .callback = video_detect_force_native,
+        /* Apple iMac11,3 */
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+               DMI_MATCH(DMI_PRODUCT_NAME, "iMac11,3"),
+               },
+       },
+       {
         /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */
         .callback = video_detect_force_native,
         /* Apple MacBook Pro 12,1 */
@@ -514,6 +530,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
        },
        {
         .callback = video_detect_force_native,
+        /* Dell Studio 1569 */
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+               DMI_MATCH(DMI_PRODUCT_NAME, "Studio 1569"),
+               },
+       },
+       {
+        .callback = video_detect_force_native,
         /* Acer Aspire 3830TG */
         .matches = {
                DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
@@ -828,6 +852,27 @@ enum acpi_backlight_type __acpi_video_get_backlight_type(bool native, bool *auto
        if (native_available)
                return acpi_backlight_native;
 
+       /*
+        * The vendor specific BIOS interfaces are only necessary for
+        * laptops from before ~2008.
+        *
+        * For laptops from ~2008 till ~2023 this point is never reached
+        * because on those (video_caps & ACPI_VIDEO_BACKLIGHT) above is true.
+        *
+        * Laptops from after ~2023 no longer support ACPI_VIDEO_BACKLIGHT,
+        * if this point is reached on those, this likely means that
+        * the GPU kms driver which sets native_available has not loaded yet.
+        *
+        * Returning acpi_backlight_vendor in this case is known to sometimes
+        * cause a non working vendor specific /sys/class/backlight device to
+        * get registered.
+        *
+        * Return acpi_backlight_none on laptops with ACPI tables written
+        * for Windows 8 (laptops from after ~2012) to avoid this problem.
+        */
+       if (acpi_osi_is_win8())
+               return acpi_backlight_none;
+
        /* No ACPI video/native (old hw), use vendor specific fw methods. */
        return acpi_backlight_vendor;
 }
index e499c60..7214197 100644 (file)
@@ -485,11 +485,11 @@ int acpi_s2idle_prepare_late(void)
                                        ACPI_LPS0_ENTRY,
                                        lps0_dsm_func_mask, lps0_dsm_guid);
        if (lps0_dsm_func_mask_microsoft > 0) {
-               acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY,
-                               lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
                /* modern standby entry */
                acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY,
                                lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
+               acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY,
+                               lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
        }
 
        list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) {
@@ -524,11 +524,6 @@ void acpi_s2idle_restore_early(void)
                if (handler->restore)
                        handler->restore();
 
-       /* Modern standby exit */
-       if (lps0_dsm_func_mask_microsoft > 0)
-               acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT,
-                               lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
-
        /* LPS0 exit */
        if (lps0_dsm_func_mask > 0)
                acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
@@ -539,6 +534,11 @@ void acpi_s2idle_restore_early(void)
                acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT,
                                lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
 
+       /* Modern standby exit */
+       if (lps0_dsm_func_mask_microsoft > 0)
+               acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT,
+                               lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
+
        /* Screen on */
        if (lps0_dsm_func_mask_microsoft > 0)
                acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON,
index 9c2d6f3..c2b925f 100644 (file)
@@ -259,10 +259,11 @@ bool force_storage_d3(void)
  * drivers/platform/x86/x86-android-tablets.c kernel module.
  */
 #define ACPI_QUIRK_SKIP_I2C_CLIENTS                            BIT(0)
-#define ACPI_QUIRK_UART1_TTY_UART2_SKIP                                BIT(1)
-#define ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY                    BIT(2)
-#define ACPI_QUIRK_USE_ACPI_AC_AND_BATTERY                     BIT(3)
-#define ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS                    BIT(4)
+#define ACPI_QUIRK_UART1_SKIP                                  BIT(1)
+#define ACPI_QUIRK_UART1_TTY_UART2_SKIP                                BIT(2)
+#define ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY                    BIT(3)
+#define ACPI_QUIRK_USE_ACPI_AC_AND_BATTERY                     BIT(4)
+#define ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS                    BIT(5)
 
 static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
        /*
@@ -319,6 +320,7 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
                        DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"),
                },
                .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+                                       ACPI_QUIRK_UART1_SKIP |
                                        ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY |
                                        ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS),
        },
@@ -365,7 +367,7 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
                                        ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
        },
        {
-               /* Nextbook Ares 8 */
+               /* Nextbook Ares 8 (BYT version)*/
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "M890BAP"),
@@ -375,6 +377,16 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
                                        ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS),
        },
        {
+               /* Nextbook Ares 8A (CHT version)*/
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"),
+                       DMI_MATCH(DMI_BIOS_VERSION, "M882"),
+               },
+               .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+                                       ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
+       },
+       {
                /* Whitelabel (sold as various brands) TM800A550L */
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
@@ -392,6 +404,7 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
 #if IS_ENABLED(CONFIG_X86_ANDROID_TABLETS)
 static const struct acpi_device_id i2c_acpi_known_good_ids[] = {
        { "10EC5640", 0 }, /* RealTek ALC5640 audio codec */
+       { "10EC5651", 0 }, /* RealTek ALC5651 audio codec */
        { "INT33F4", 0 },  /* X-Powers AXP288 PMIC */
        { "INT33FD", 0 },  /* Intel Crystal Cove PMIC */
        { "INT34D3", 0 },  /* Intel Whiskey Cove PMIC */
@@ -438,6 +451,9 @@ int acpi_quirk_skip_serdev_enumeration(struct device *controller_parent, bool *s
        if (dmi_id)
                quirks = (unsigned long)dmi_id->driver_data;
 
+       if ((quirks & ACPI_QUIRK_UART1_SKIP) && uid == 1)
+               *skip = true;
+
        if (quirks & ACPI_QUIRK_UART1_TTY_UART2_SKIP) {
                if (uid == 1)
                        return -ENODEV; /* Create tty cdev instead of serdev */
index 8bf612b..b4f246f 100644 (file)
@@ -5348,7 +5348,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
 
        mutex_init(&ap->scsi_scan_mutex);
        INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug);
-       INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan);
+       INIT_DELAYED_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan);
        INIT_LIST_HEAD(&ap->eh_done_q);
        init_waitqueue_head(&ap->eh_wait_q);
        init_completion(&ap->park_req_pending);
@@ -5954,6 +5954,7 @@ static void ata_port_detach(struct ata_port *ap)
        WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED));
 
        cancel_delayed_work_sync(&ap->hotplug_task);
+       cancel_delayed_work_sync(&ap->scsi_rescan_task);
 
  skip_eh:
        /* clean up zpodd on port removal */
index a6c9018..6f8d141 100644 (file)
@@ -2984,7 +2984,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
                        ehc->i.flags |= ATA_EHI_SETMODE;
 
                        /* schedule the scsi_rescan_device() here */
-                       schedule_work(&(ap->scsi_rescan_task));
+                       schedule_delayed_work(&ap->scsi_rescan_task, 0);
                } else if (dev->class == ATA_DEV_UNKNOWN &&
                           ehc->tries[dev->devno] &&
                           ata_class_enabled(ehc->classes[dev->devno])) {
index 8ce9028..551077c 100644 (file)
@@ -4597,10 +4597,11 @@ int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
 void ata_scsi_dev_rescan(struct work_struct *work)
 {
        struct ata_port *ap =
-               container_of(work, struct ata_port, scsi_rescan_task);
+               container_of(work, struct ata_port, scsi_rescan_task.work);
        struct ata_link *link;
        struct ata_device *dev;
        unsigned long flags;
+       bool delay_rescan = false;
 
        mutex_lock(&ap->scsi_scan_mutex);
        spin_lock_irqsave(ap->lock, flags);
@@ -4614,6 +4615,21 @@ void ata_scsi_dev_rescan(struct work_struct *work)
                        if (scsi_device_get(sdev))
                                continue;
 
+                       /*
+                        * If the rescan work was scheduled because of a resume
+                        * event, the port is already fully resumed, but the
+                        * SCSI device may not yet be fully resumed. In such
+                        * case, executing scsi_rescan_device() may cause a
+                        * deadlock with the PM code on device_lock(). Prevent
+                        * this by giving up and retrying rescan after a short
+                        * delay.
+                        */
+                       delay_rescan = sdev->sdev_gendev.power.is_suspended;
+                       if (delay_rescan) {
+                               scsi_device_put(sdev);
+                               break;
+                       }
+
                        spin_unlock_irqrestore(ap->lock, flags);
                        scsi_rescan_device(&(sdev->sdev_gendev));
                        scsi_device_put(sdev);
@@ -4623,4 +4639,8 @@ void ata_scsi_dev_rescan(struct work_struct *work)
 
        spin_unlock_irqrestore(ap->lock, flags);
        mutex_unlock(&ap->scsi_scan_mutex);
+
+       if (delay_rescan)
+               schedule_delayed_work(&ap->scsi_rescan_task,
+                                     msecs_to_jiffies(5));
 }
index 0295646..97c681f 100644 (file)
@@ -284,6 +284,9 @@ static bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg,
 {
        int ret;
 
+       if (!regmap_writeable(map, reg))
+               return false;
+
        /* If we don't know the chip just got reset, then sync everything. */
        if (!map->no_sync_defaults)
                return true;
index edfa946..66759fe 100644 (file)
@@ -119,7 +119,10 @@ static int clk_composite_determine_rate(struct clk_hw *hw,
                        if (ret)
                                continue;
 
-                       rate_diff = abs(req->rate - tmp_req.rate);
+                       if (req->rate >= tmp_req.rate)
+                               rate_diff = req->rate - tmp_req.rate;
+                       else
+                               rate_diff = tmp_req.rate - req->rate;
 
                        if (!rate_diff || !req->best_parent_hw
                                       || best_rate_diff > rate_diff) {
index 70ae1dd..bacdcbb 100644 (file)
@@ -40,7 +40,7 @@ static struct clk_hw *loongson2_clk_register(struct device *dev,
 {
        int ret;
        struct clk_hw *hw;
-       struct clk_init_data init;
+       struct clk_init_data init = { };
 
        hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
        if (!hw)
index 6b4e193..c87a6c4 100644 (file)
@@ -23,6 +23,7 @@
 static DEFINE_SPINLOCK(mt8365_clk_lock);
 
 static const struct mtk_fixed_clk top_fixed_clks[] = {
+       FIXED_CLK(CLK_TOP_CLK_NULL, "clk_null", NULL, 0),
        FIXED_CLK(CLK_TOP_I2S0_BCK, "i2s0_bck", NULL, 26000000),
        FIXED_CLK(CLK_TOP_DSI0_LNTC_DSICK, "dsi0_lntc_dsick", "clk26m",
                  75000000),
@@ -559,6 +560,14 @@ static const struct mtk_clk_divider top_adj_divs[] = {
                  0x324, 16, 8, CLK_DIVIDER_ROUND_CLOSEST),
        DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV3, "apll12_ck_div3", "apll_i2s3_sel",
                  0x324, 24, 8, CLK_DIVIDER_ROUND_CLOSEST),
+       DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV4, "apll12_ck_div4", "apll_tdmout_sel",
+                 0x328, 0, 8, CLK_DIVIDER_ROUND_CLOSEST),
+       DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV4B, "apll12_ck_div4b", "apll_tdmout_sel",
+                 0x328, 8, 8, CLK_DIVIDER_ROUND_CLOSEST),
+       DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV5, "apll12_ck_div5", "apll_tdmin_sel",
+                 0x328, 16, 8, CLK_DIVIDER_ROUND_CLOSEST),
+       DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV5B, "apll12_ck_div5b", "apll_tdmin_sel",
+                 0x328, 24, 8, CLK_DIVIDER_ROUND_CLOSEST),
        DIV_ADJ_F(CLK_TOP_APLL12_CK_DIV6, "apll12_ck_div6", "apll_spdif_sel",
                  0x32c, 0, 8, CLK_DIVIDER_ROUND_CLOSEST),
 };
@@ -583,15 +592,15 @@ static const struct mtk_gate_regs top2_cg_regs = {
 
 #define GATE_TOP0(_id, _name, _parent, _shift)                 \
        GATE_MTK(_id, _name, _parent, &top0_cg_regs,            \
-                _shift, &mtk_clk_gate_ops_no_setclr_inv)
+                _shift, &mtk_clk_gate_ops_no_setclr)
 
 #define GATE_TOP1(_id, _name, _parent, _shift)                 \
        GATE_MTK(_id, _name, _parent, &top1_cg_regs,            \
-                _shift, &mtk_clk_gate_ops_no_setclr)
+                _shift, &mtk_clk_gate_ops_no_setclr_inv)
 
 #define GATE_TOP2(_id, _name, _parent, _shift)                 \
        GATE_MTK(_id, _name, _parent, &top2_cg_regs,            \
-                _shift, &mtk_clk_gate_ops_no_setclr)
+                _shift, &mtk_clk_gate_ops_no_setclr_inv)
 
 static const struct mtk_gate top_clk_gates[] = {
        GATE_TOP0(CLK_TOP_CONN_32K, "conn_32k", "clk32k", 10),
@@ -696,6 +705,7 @@ static const struct mtk_gate ifr_clks[] = {
        GATE_IFR3(CLK_IFR_GCPU, "ifr_gcpu", "axi_sel", 8),
        GATE_IFR3(CLK_IFR_TRNG, "ifr_trng", "axi_sel", 9),
        GATE_IFR3(CLK_IFR_AUXADC, "ifr_auxadc", "clk26m", 10),
+       GATE_IFR3(CLK_IFR_CPUM, "ifr_cpum", "clk26m", 11),
        GATE_IFR3(CLK_IFR_AUXADC_MD, "ifr_auxadc_md", "clk26m", 14),
        GATE_IFR3(CLK_IFR_AP_DMA, "ifr_ap_dma", "axi_sel", 18),
        GATE_IFR3(CLK_IFR_DEBUGSYS, "ifr_debugsys", "axi_sel", 24),
@@ -717,6 +727,8 @@ static const struct mtk_gate ifr_clks[] = {
        GATE_IFR5(CLK_IFR_PWRAP_TMR, "ifr_pwrap_tmr", "clk26m", 12),
        GATE_IFR5(CLK_IFR_PWRAP_SPI, "ifr_pwrap_spi", "clk26m", 13),
        GATE_IFR5(CLK_IFR_PWRAP_SYS, "ifr_pwrap_sys", "clk26m", 14),
+       GATE_MTK_FLAGS(CLK_IFR_MCU_PM_BK, "ifr_mcu_pm_bk", NULL, &ifr5_cg_regs,
+                       17, &mtk_clk_gate_ops_setclr, CLK_IGNORE_UNUSED),
        GATE_IFR5(CLK_IFR_IRRX_26M, "ifr_irrx_26m", "clk26m", 22),
        GATE_IFR5(CLK_IFR_IRRX_32K, "ifr_irrx_32k", "clk32k", 23),
        GATE_IFR5(CLK_IFR_I2C0_AXI, "ifr_i2c0_axi", "i2c_sel", 24),
index 42958a5..621e298 100644 (file)
@@ -164,7 +164,7 @@ void pxa3xx_clk_update_accr(u32 disable, u32 enable, u32 xclkcfg, u32 mask)
        accr &= ~disable;
        accr |= enable;
 
-       writel(accr, ACCR);
+       writel(accr, clk_regs + ACCR);
        if (xclkcfg)
                __asm__("mcr p14, 0, %0, c6, c0, 0\n" : : "r"(xclkcfg));
 
index e4ccfb6..ec056f6 100644 (file)
@@ -2124,6 +2124,7 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
                                   file, blocks, le32_to_cpu(blk->len),
                                   type, le32_to_cpu(blk->id));
 
+                       region_name = cs_dsp_mem_region_name(type);
                        mem = cs_dsp_find_region(dsp, type);
                        if (!mem) {
                                cs_dsp_err(dsp, "No base for region %x\n", type);
@@ -2147,8 +2148,8 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
                                reg = dsp->ops->region_to_reg(mem, reg);
                                reg += offset;
                        } else {
-                               cs_dsp_err(dsp, "No %x for algorithm %x\n",
-                                          type, le32_to_cpu(blk->id));
+                               cs_dsp_err(dsp, "No %s for algorithm %x\n",
+                                          region_name, le32_to_cpu(blk->id));
                        }
                        break;
 
index b1ca1ab..393b6fb 100644 (file)
@@ -1615,6 +1615,7 @@ static const u16 amdgpu_unsupported_pciidlist[] = {
        0x5874,
        0x5940,
        0x5941,
+       0x5b70,
        0x5b72,
        0x5b73,
        0x5b74,
index 3b225be..a70103a 100644 (file)
@@ -140,7 +140,7 @@ void amdgpu_bo_placement_from_domain(struct amdgpu_bo *abo, u32 domain)
 
                if (flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED)
                        places[c].lpfn = visible_pfn;
-               else if (adev->gmc.real_vram_size != adev->gmc.visible_vram_size)
+               else
                        places[c].flags |= TTM_PL_FLAG_TOPDOWN;
 
                if (flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS)
index 9d7e6e0..a150b7a 100644 (file)
@@ -3548,6 +3548,9 @@ static ssize_t amdgpu_psp_vbflash_read(struct file *filp, struct kobject *kobj,
        void *fw_pri_cpu_addr;
        int ret;
 
+       if (adev->psp.vbflash_image_size == 0)
+               return -EINVAL;
+
        dev_info(adev->dev, "VBIOS flash to PSP started");
 
        ret = amdgpu_bo_create_kernel(adev, adev->psp.vbflash_image_size,
@@ -3599,13 +3602,13 @@ static ssize_t amdgpu_psp_vbflash_status(struct device *dev,
 }
 
 static const struct bin_attribute psp_vbflash_bin_attr = {
-       .attr = {.name = "psp_vbflash", .mode = 0664},
+       .attr = {.name = "psp_vbflash", .mode = 0660},
        .size = 0,
        .write = amdgpu_psp_vbflash_write,
        .read = amdgpu_psp_vbflash_read,
 };
 
-static DEVICE_ATTR(psp_vbflash_status, 0444, amdgpu_psp_vbflash_status, NULL);
+static DEVICE_ATTR(psp_vbflash_status, 0440, amdgpu_psp_vbflash_status, NULL);
 
 int amdgpu_psp_sysfs_init(struct amdgpu_device *adev)
 {
index dc474b8..49de3a3 100644 (file)
@@ -581,3 +581,21 @@ void amdgpu_ring_ib_end(struct amdgpu_ring *ring)
        if (ring->is_sw_ring)
                amdgpu_sw_ring_ib_end(ring);
 }
+
+void amdgpu_ring_ib_on_emit_cntl(struct amdgpu_ring *ring)
+{
+       if (ring->is_sw_ring)
+               amdgpu_sw_ring_ib_mark_offset(ring, AMDGPU_MUX_OFFSET_TYPE_CONTROL);
+}
+
+void amdgpu_ring_ib_on_emit_ce(struct amdgpu_ring *ring)
+{
+       if (ring->is_sw_ring)
+               amdgpu_sw_ring_ib_mark_offset(ring, AMDGPU_MUX_OFFSET_TYPE_CE);
+}
+
+void amdgpu_ring_ib_on_emit_de(struct amdgpu_ring *ring)
+{
+       if (ring->is_sw_ring)
+               amdgpu_sw_ring_ib_mark_offset(ring, AMDGPU_MUX_OFFSET_TYPE_DE);
+}
index d874944..2474cb7 100644 (file)
@@ -227,6 +227,9 @@ struct amdgpu_ring_funcs {
        int (*preempt_ib)(struct amdgpu_ring *ring);
        void (*emit_mem_sync)(struct amdgpu_ring *ring);
        void (*emit_wave_limit)(struct amdgpu_ring *ring, bool enable);
+       void (*patch_cntl)(struct amdgpu_ring *ring, unsigned offset);
+       void (*patch_ce)(struct amdgpu_ring *ring, unsigned offset);
+       void (*patch_de)(struct amdgpu_ring *ring, unsigned offset);
 };
 
 struct amdgpu_ring {
@@ -318,10 +321,16 @@ struct amdgpu_ring {
 #define amdgpu_ring_init_cond_exec(r) (r)->funcs->init_cond_exec((r))
 #define amdgpu_ring_patch_cond_exec(r,o) (r)->funcs->patch_cond_exec((r),(o))
 #define amdgpu_ring_preempt_ib(r) (r)->funcs->preempt_ib(r)
+#define amdgpu_ring_patch_cntl(r, o) ((r)->funcs->patch_cntl((r), (o)))
+#define amdgpu_ring_patch_ce(r, o) ((r)->funcs->patch_ce((r), (o)))
+#define amdgpu_ring_patch_de(r, o) ((r)->funcs->patch_de((r), (o)))
 
 int amdgpu_ring_alloc(struct amdgpu_ring *ring, unsigned ndw);
 void amdgpu_ring_ib_begin(struct amdgpu_ring *ring);
 void amdgpu_ring_ib_end(struct amdgpu_ring *ring);
+void amdgpu_ring_ib_on_emit_cntl(struct amdgpu_ring *ring);
+void amdgpu_ring_ib_on_emit_ce(struct amdgpu_ring *ring);
+void amdgpu_ring_ib_on_emit_de(struct amdgpu_ring *ring);
 
 void amdgpu_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count);
 void amdgpu_ring_generic_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib);
index 62079f0..73516ab 100644 (file)
@@ -105,6 +105,16 @@ static void amdgpu_mux_resubmit_chunks(struct amdgpu_ring_mux *mux)
                                amdgpu_fence_update_start_timestamp(e->ring,
                                                                    chunk->sync_seq,
                                                                    ktime_get());
+                               if (chunk->sync_seq ==
+                                       le32_to_cpu(*(e->ring->fence_drv.cpu_addr + 2))) {
+                                       if (chunk->cntl_offset <= e->ring->buf_mask)
+                                               amdgpu_ring_patch_cntl(e->ring,
+                                                                      chunk->cntl_offset);
+                                       if (chunk->ce_offset <= e->ring->buf_mask)
+                                               amdgpu_ring_patch_ce(e->ring, chunk->ce_offset);
+                                       if (chunk->de_offset <= e->ring->buf_mask)
+                                               amdgpu_ring_patch_de(e->ring, chunk->de_offset);
+                               }
                                amdgpu_ring_mux_copy_pkt_from_sw_ring(mux, e->ring,
                                                                      chunk->start,
                                                                      chunk->end);
@@ -407,6 +417,17 @@ void amdgpu_sw_ring_ib_end(struct amdgpu_ring *ring)
        amdgpu_ring_mux_end_ib(mux, ring);
 }
 
+void amdgpu_sw_ring_ib_mark_offset(struct amdgpu_ring *ring, enum amdgpu_ring_mux_offset_type type)
+{
+       struct amdgpu_device *adev = ring->adev;
+       struct amdgpu_ring_mux *mux = &adev->gfx.muxer;
+       unsigned offset;
+
+       offset = ring->wptr & ring->buf_mask;
+
+       amdgpu_ring_mux_ib_mark_offset(mux, ring, offset, type);
+}
+
 void amdgpu_ring_mux_start_ib(struct amdgpu_ring_mux *mux, struct amdgpu_ring *ring)
 {
        struct amdgpu_mux_entry *e;
@@ -429,6 +450,10 @@ void amdgpu_ring_mux_start_ib(struct amdgpu_ring_mux *mux, struct amdgpu_ring *r
        }
 
        chunk->start = ring->wptr;
+       /* the initialized value used to check if they are set by the ib submission*/
+       chunk->cntl_offset = ring->buf_mask + 1;
+       chunk->de_offset = ring->buf_mask + 1;
+       chunk->ce_offset = ring->buf_mask + 1;
        list_add_tail(&chunk->entry, &e->list);
 }
 
@@ -454,6 +479,41 @@ static void scan_and_remove_signaled_chunk(struct amdgpu_ring_mux *mux, struct a
        }
 }
 
+void amdgpu_ring_mux_ib_mark_offset(struct amdgpu_ring_mux *mux,
+                                   struct amdgpu_ring *ring, u64 offset,
+                                   enum amdgpu_ring_mux_offset_type type)
+{
+       struct amdgpu_mux_entry *e;
+       struct amdgpu_mux_chunk *chunk;
+
+       e = amdgpu_ring_mux_sw_entry(mux, ring);
+       if (!e) {
+               DRM_ERROR("cannot find entry!\n");
+               return;
+       }
+
+       chunk = list_last_entry(&e->list, struct amdgpu_mux_chunk, entry);
+       if (!chunk) {
+               DRM_ERROR("cannot find chunk!\n");
+               return;
+       }
+
+       switch (type) {
+       case AMDGPU_MUX_OFFSET_TYPE_CONTROL:
+               chunk->cntl_offset = offset;
+               break;
+       case AMDGPU_MUX_OFFSET_TYPE_DE:
+               chunk->de_offset = offset;
+               break;
+       case AMDGPU_MUX_OFFSET_TYPE_CE:
+               chunk->ce_offset = offset;
+               break;
+       default:
+               DRM_ERROR("invalid type (%d)\n", type);
+               break;
+       }
+}
+
 void amdgpu_ring_mux_end_ib(struct amdgpu_ring_mux *mux, struct amdgpu_ring *ring)
 {
        struct amdgpu_mux_entry *e;
index 4be45fc..b22d4fb 100644 (file)
@@ -50,6 +50,12 @@ struct amdgpu_mux_entry {
        struct list_head        list;
 };
 
+enum amdgpu_ring_mux_offset_type {
+       AMDGPU_MUX_OFFSET_TYPE_CONTROL,
+       AMDGPU_MUX_OFFSET_TYPE_DE,
+       AMDGPU_MUX_OFFSET_TYPE_CE,
+};
+
 struct amdgpu_ring_mux {
        struct amdgpu_ring      *real_ring;
 
@@ -72,12 +78,18 @@ struct amdgpu_ring_mux {
  * @sync_seq: the fence seqno related with the saved IB.
  * @start:- start location on the software ring.
  * @end:- end location on the software ring.
+ * @control_offset:- the PRE_RESUME bit position used for resubmission.
+ * @de_offset:- the anchor in write_data for de meta of resubmission.
+ * @ce_offset:- the anchor in write_data for ce meta of resubmission.
  */
 struct amdgpu_mux_chunk {
        struct list_head        entry;
        uint32_t                sync_seq;
        u64                     start;
        u64                     end;
+       u64                     cntl_offset;
+       u64                     de_offset;
+       u64                     ce_offset;
 };
 
 int amdgpu_ring_mux_init(struct amdgpu_ring_mux *mux, struct amdgpu_ring *ring,
@@ -89,6 +101,8 @@ u64 amdgpu_ring_mux_get_wptr(struct amdgpu_ring_mux *mux, struct amdgpu_ring *ri
 u64 amdgpu_ring_mux_get_rptr(struct amdgpu_ring_mux *mux, struct amdgpu_ring *ring);
 void amdgpu_ring_mux_start_ib(struct amdgpu_ring_mux *mux, struct amdgpu_ring *ring);
 void amdgpu_ring_mux_end_ib(struct amdgpu_ring_mux *mux, struct amdgpu_ring *ring);
+void amdgpu_ring_mux_ib_mark_offset(struct amdgpu_ring_mux *mux, struct amdgpu_ring *ring,
+                                   u64 offset, enum amdgpu_ring_mux_offset_type type);
 bool amdgpu_mcbp_handle_trailing_fence_irq(struct amdgpu_ring_mux *mux);
 
 u64 amdgpu_sw_ring_get_rptr_gfx(struct amdgpu_ring *ring);
@@ -97,6 +111,7 @@ void amdgpu_sw_ring_set_wptr_gfx(struct amdgpu_ring *ring);
 void amdgpu_sw_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count);
 void amdgpu_sw_ring_ib_begin(struct amdgpu_ring *ring);
 void amdgpu_sw_ring_ib_end(struct amdgpu_ring *ring);
+void amdgpu_sw_ring_ib_mark_offset(struct amdgpu_ring *ring, enum amdgpu_ring_mux_offset_type type);
 const char *amdgpu_sw_ring_name(int idx);
 unsigned int amdgpu_sw_ring_priority(int idx);
 
index e7f2b7b..a674c8a 100644 (file)
@@ -755,7 +755,7 @@ static void gfx_v9_0_set_rlc_funcs(struct amdgpu_device *adev);
 static int gfx_v9_0_get_cu_info(struct amdgpu_device *adev,
                                struct amdgpu_cu_info *cu_info);
 static uint64_t gfx_v9_0_get_gpu_clock_counter(struct amdgpu_device *adev);
-static void gfx_v9_0_ring_emit_de_meta(struct amdgpu_ring *ring, bool resume);
+static void gfx_v9_0_ring_emit_de_meta(struct amdgpu_ring *ring, bool resume, bool usegds);
 static u64 gfx_v9_0_ring_get_rptr_compute(struct amdgpu_ring *ring);
 static void gfx_v9_0_query_ras_error_count(struct amdgpu_device *adev,
                                          void *ras_error_status);
@@ -5127,7 +5127,8 @@ static void gfx_v9_0_ring_emit_ib_gfx(struct amdgpu_ring *ring,
                        gfx_v9_0_ring_emit_de_meta(ring,
                                                   (!amdgpu_sriov_vf(ring->adev) &&
                                                   flags & AMDGPU_IB_PREEMPTED) ?
-                                                  true : false);
+                                                  true : false,
+                                                  job->gds_size > 0 && job->gds_base != 0);
        }
 
        amdgpu_ring_write(ring, header);
@@ -5138,9 +5139,83 @@ static void gfx_v9_0_ring_emit_ib_gfx(struct amdgpu_ring *ring,
 #endif
                lower_32_bits(ib->gpu_addr));
        amdgpu_ring_write(ring, upper_32_bits(ib->gpu_addr));
+       amdgpu_ring_ib_on_emit_cntl(ring);
        amdgpu_ring_write(ring, control);
 }
 
+static void gfx_v9_0_ring_patch_cntl(struct amdgpu_ring *ring,
+                                    unsigned offset)
+{
+       u32 control = ring->ring[offset];
+
+       control |= INDIRECT_BUFFER_PRE_RESUME(1);
+       ring->ring[offset] = control;
+}
+
+static void gfx_v9_0_ring_patch_ce_meta(struct amdgpu_ring *ring,
+                                       unsigned offset)
+{
+       struct amdgpu_device *adev = ring->adev;
+       void *ce_payload_cpu_addr;
+       uint64_t payload_offset, payload_size;
+
+       payload_size = sizeof(struct v9_ce_ib_state);
+
+       if (ring->is_mes_queue) {
+               payload_offset = offsetof(struct amdgpu_mes_ctx_meta_data,
+                                         gfx[0].gfx_meta_data) +
+                       offsetof(struct v9_gfx_meta_data, ce_payload);
+               ce_payload_cpu_addr =
+                       amdgpu_mes_ctx_get_offs_cpu_addr(ring, payload_offset);
+       } else {
+               payload_offset = offsetof(struct v9_gfx_meta_data, ce_payload);
+               ce_payload_cpu_addr = adev->virt.csa_cpu_addr + payload_offset;
+       }
+
+       if (offset + (payload_size >> 2) <= ring->buf_mask + 1) {
+               memcpy((void *)&ring->ring[offset], ce_payload_cpu_addr, payload_size);
+       } else {
+               memcpy((void *)&ring->ring[offset], ce_payload_cpu_addr,
+                      (ring->buf_mask + 1 - offset) << 2);
+               payload_size -= (ring->buf_mask + 1 - offset) << 2;
+               memcpy((void *)&ring->ring[0],
+                      ce_payload_cpu_addr + ((ring->buf_mask + 1 - offset) << 2),
+                      payload_size);
+       }
+}
+
+static void gfx_v9_0_ring_patch_de_meta(struct amdgpu_ring *ring,
+                                       unsigned offset)
+{
+       struct amdgpu_device *adev = ring->adev;
+       void *de_payload_cpu_addr;
+       uint64_t payload_offset, payload_size;
+
+       payload_size = sizeof(struct v9_de_ib_state);
+
+       if (ring->is_mes_queue) {
+               payload_offset = offsetof(struct amdgpu_mes_ctx_meta_data,
+                                         gfx[0].gfx_meta_data) +
+                       offsetof(struct v9_gfx_meta_data, de_payload);
+               de_payload_cpu_addr =
+                       amdgpu_mes_ctx_get_offs_cpu_addr(ring, payload_offset);
+       } else {
+               payload_offset = offsetof(struct v9_gfx_meta_data, de_payload);
+               de_payload_cpu_addr = adev->virt.csa_cpu_addr + payload_offset;
+       }
+
+       if (offset + (payload_size >> 2) <= ring->buf_mask + 1) {
+               memcpy((void *)&ring->ring[offset], de_payload_cpu_addr, payload_size);
+       } else {
+               memcpy((void *)&ring->ring[offset], de_payload_cpu_addr,
+                      (ring->buf_mask + 1 - offset) << 2);
+               payload_size -= (ring->buf_mask + 1 - offset) << 2;
+               memcpy((void *)&ring->ring[0],
+                      de_payload_cpu_addr + ((ring->buf_mask + 1 - offset) << 2),
+                      payload_size);
+       }
+}
+
 static void gfx_v9_0_ring_emit_ib_compute(struct amdgpu_ring *ring,
                                          struct amdgpu_job *job,
                                          struct amdgpu_ib *ib,
@@ -5336,6 +5411,8 @@ static void gfx_v9_0_ring_emit_ce_meta(struct amdgpu_ring *ring, bool resume)
        amdgpu_ring_write(ring, lower_32_bits(ce_payload_gpu_addr));
        amdgpu_ring_write(ring, upper_32_bits(ce_payload_gpu_addr));
 
+       amdgpu_ring_ib_on_emit_ce(ring);
+
        if (resume)
                amdgpu_ring_write_multiple(ring, ce_payload_cpu_addr,
                                           sizeof(ce_payload) >> 2);
@@ -5369,10 +5446,6 @@ static int gfx_v9_0_ring_preempt_ib(struct amdgpu_ring *ring)
        amdgpu_ring_alloc(ring, 13);
        gfx_v9_0_ring_emit_fence(ring, ring->trail_fence_gpu_addr,
                                 ring->trail_seq, AMDGPU_FENCE_FLAG_EXEC | AMDGPU_FENCE_FLAG_INT);
-       /*reset the CP_VMID_PREEMPT after trailing fence*/
-       amdgpu_ring_emit_wreg(ring,
-                             SOC15_REG_OFFSET(GC, 0, mmCP_VMID_PREEMPT),
-                             0x0);
 
        /* assert IB preemption, emit the trailing fence */
        kiq->pmf->kiq_unmap_queues(kiq_ring, ring, PREEMPT_QUEUES_NO_UNMAP,
@@ -5395,6 +5468,10 @@ static int gfx_v9_0_ring_preempt_ib(struct amdgpu_ring *ring)
                DRM_WARN("ring %d timeout to preempt ib\n", ring->idx);
        }
 
+       /*reset the CP_VMID_PREEMPT after trailing fence*/
+       amdgpu_ring_emit_wreg(ring,
+                             SOC15_REG_OFFSET(GC, 0, mmCP_VMID_PREEMPT),
+                             0x0);
        amdgpu_ring_commit(ring);
 
        /* deassert preemption condition */
@@ -5402,7 +5479,7 @@ static int gfx_v9_0_ring_preempt_ib(struct amdgpu_ring *ring)
        return r;
 }
 
-static void gfx_v9_0_ring_emit_de_meta(struct amdgpu_ring *ring, bool resume)
+static void gfx_v9_0_ring_emit_de_meta(struct amdgpu_ring *ring, bool resume, bool usegds)
 {
        struct amdgpu_device *adev = ring->adev;
        struct v9_de_ib_state de_payload = {0};
@@ -5433,8 +5510,10 @@ static void gfx_v9_0_ring_emit_de_meta(struct amdgpu_ring *ring, bool resume)
                                 PAGE_SIZE);
        }
 
-       de_payload.gds_backup_addrlo = lower_32_bits(gds_addr);
-       de_payload.gds_backup_addrhi = upper_32_bits(gds_addr);
+       if (usegds) {
+               de_payload.gds_backup_addrlo = lower_32_bits(gds_addr);
+               de_payload.gds_backup_addrhi = upper_32_bits(gds_addr);
+       }
 
        cnt = (sizeof(de_payload) >> 2) + 4 - 2;
        amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, cnt));
@@ -5445,6 +5524,7 @@ static void gfx_v9_0_ring_emit_de_meta(struct amdgpu_ring *ring, bool resume)
        amdgpu_ring_write(ring, lower_32_bits(de_payload_gpu_addr));
        amdgpu_ring_write(ring, upper_32_bits(de_payload_gpu_addr));
 
+       amdgpu_ring_ib_on_emit_de(ring);
        if (resume)
                amdgpu_ring_write_multiple(ring, de_payload_cpu_addr,
                                           sizeof(de_payload) >> 2);
@@ -6855,6 +6935,9 @@ static const struct amdgpu_ring_funcs gfx_v9_0_sw_ring_funcs_gfx = {
        .emit_reg_write_reg_wait = gfx_v9_0_ring_emit_reg_write_reg_wait,
        .soft_recovery = gfx_v9_0_ring_soft_recovery,
        .emit_mem_sync = gfx_v9_0_emit_mem_sync,
+       .patch_cntl = gfx_v9_0_ring_patch_cntl,
+       .patch_de = gfx_v9_0_ring_patch_de_meta,
+       .patch_ce = gfx_v9_0_ring_patch_ce_meta,
 };
 
 static const struct amdgpu_ring_funcs gfx_v9_0_ring_funcs_compute = {
index e5fd1e0..da126ff 100644 (file)
@@ -129,7 +129,11 @@ static int vcn_v4_0_sw_init(void *handle)
                if (adev->vcn.harvest_config & (1 << i))
                        continue;
 
-               atomic_set(&adev->vcn.inst[i].sched_score, 0);
+               /* Init instance 0 sched_score to 1, so it's scheduled after other instances */
+               if (i == 0)
+                       atomic_set(&adev->vcn.inst[i].sched_score, 1);
+               else
+                       atomic_set(&adev->vcn.inst[i].sched_score, 0);
 
                /* VCN UNIFIED TRAP */
                r = amdgpu_irq_add_id(adev, amdgpu_ih_clientid_vcns[i],
index d5cec03..7acd73e 100644 (file)
@@ -7196,7 +7196,13 @@ static int amdgpu_dm_connector_get_modes(struct drm_connector *connector)
                                drm_add_modes_noedid(connector, 1920, 1080);
        } else {
                amdgpu_dm_connector_ddc_get_modes(connector, edid);
-               amdgpu_dm_connector_add_common_modes(encoder, connector);
+               /* most eDP supports only timings from its edid,
+                * usually only detailed timings are available
+                * from eDP edid. timings which are not from edid
+                * may damage eDP
+                */
+               if (connector->connector_type != DRM_MODE_CONNECTOR_eDP)
+                       amdgpu_dm_connector_add_common_modes(encoder, connector);
                amdgpu_dm_connector_add_freesync_modes(connector, edid);
        }
        amdgpu_dm_fbc_init(connector);
@@ -8198,6 +8204,12 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
                if (acrtc_state->abm_level != dm_old_crtc_state->abm_level)
                        bundle->stream_update.abm_level = &acrtc_state->abm_level;
 
+               mutex_lock(&dm->dc_lock);
+               if ((acrtc_state->update_type > UPDATE_TYPE_FAST) &&
+                               acrtc_state->stream->link->psr_settings.psr_allow_active)
+                       amdgpu_dm_psr_disable(acrtc_state->stream);
+               mutex_unlock(&dm->dc_lock);
+
                /*
                 * If FreeSync state on the stream has changed then we need to
                 * re-adjust the min/max bounds now that DC doesn't handle this
@@ -8211,10 +8223,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
                        spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
                }
                mutex_lock(&dm->dc_lock);
-               if ((acrtc_state->update_type > UPDATE_TYPE_FAST) &&
-                               acrtc_state->stream->link->psr_settings.psr_allow_active)
-                       amdgpu_dm_psr_disable(acrtc_state->stream);
-
                update_planes_and_stream_adapter(dm->dc,
                                         acrtc_state->update_type,
                                         planes_count,
index a131e30..d471d58 100644 (file)
@@ -980,6 +980,11 @@ static bool detect_link_and_local_sink(struct dc_link *link,
                                        (link->dpcd_caps.dongle_type !=
                                                        DISPLAY_DONGLE_DP_HDMI_CONVERTER))
                                converter_disable_audio = true;
+
+                       /* limited link rate to HBR3 for DPIA until we implement USB4 V2 */
+                       if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
+                                       link->reported_link_cap.link_rate > LINK_RATE_HIGH3)
+                               link->reported_link_cap.link_rate = LINK_RATE_HIGH3;
                        break;
                }
 
index 09405ef..08577d1 100644 (file)
@@ -1696,10 +1696,39 @@ static int smu_v13_0_0_set_power_profile_mode(struct smu_context *smu,
                }
        }
 
-       /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
-       workload_type = smu_cmn_to_asic_specific_index(smu,
+       if (smu->power_profile_mode == PP_SMC_POWER_PROFILE_COMPUTE &&
+               (((smu->adev->pdev->device == 0x744C) && (smu->adev->pdev->revision == 0xC8)) ||
+               ((smu->adev->pdev->device == 0x744C) && (smu->adev->pdev->revision == 0xCC)))) {
+               ret = smu_cmn_update_table(smu,
+                                          SMU_TABLE_ACTIVITY_MONITOR_COEFF,
+                                          WORKLOAD_PPLIB_COMPUTE_BIT,
+                                          (void *)(&activity_monitor_external),
+                                          false);
+               if (ret) {
+                       dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
+                       return ret;
+               }
+
+               ret = smu_cmn_update_table(smu,
+                                          SMU_TABLE_ACTIVITY_MONITOR_COEFF,
+                                          WORKLOAD_PPLIB_CUSTOM_BIT,
+                                          (void *)(&activity_monitor_external),
+                                          true);
+               if (ret) {
+                       dev_err(smu->adev->dev, "[%s] Failed to set activity monitor!", __func__);
+                       return ret;
+               }
+
+               workload_type = smu_cmn_to_asic_specific_index(smu,
+                                                      CMN2ASIC_MAPPING_WORKLOAD,
+                                                      PP_SMC_POWER_PROFILE_CUSTOM);
+       } else {
+               /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
+               workload_type = smu_cmn_to_asic_specific_index(smu,
                                                       CMN2ASIC_MAPPING_WORKLOAD,
                                                       smu->power_profile_mode);
+       }
+
        if (workload_type < 0)
                return -EINVAL;
 
index 7a74878..4676cf2 100644 (file)
@@ -298,6 +298,10 @@ static void ti_sn_bridge_set_refclk_freq(struct ti_sn65dsi86 *pdata)
                if (refclk_lut[i] == refclk_rate)
                        break;
 
+       /* avoid buffer overflow and "1" is the default rate in the datasheet. */
+       if (i >= refclk_lut_size)
+               i = 1;
+
        regmap_update_bits(pdata->regmap, SN_DPPLL_SRC_REG, REFCLK_FREQ_MASK,
                           REFCLK_FREQ(i));
 
index 8cf096f..a2ae8c2 100644 (file)
@@ -220,6 +220,9 @@ static void nouveau_dsm_pci_probe(struct pci_dev *pdev, acpi_handle *dhandle_out
        int optimus_funcs;
        struct pci_dev *parent_pdev;
 
+       if (pdev->vendor != PCI_VENDOR_ID_NVIDIA)
+               return;
+
        *has_pr3 = false;
        parent_pdev = pci_upstream_bridge(pdev);
        if (parent_pdev) {
index 086b66b..f75c6f0 100644 (file)
@@ -730,7 +730,8 @@ out:
 #endif
 
        nouveau_connector_set_edid(nv_connector, edid);
-       nouveau_connector_set_encoder(connector, nv_encoder);
+       if (nv_encoder)
+               nouveau_connector_set_encoder(connector, nv_encoder);
        return status;
 }
 
@@ -966,7 +967,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
        /* Determine display colour depth for everything except LVDS now,
         * DP requires this before mode_valid() is called.
         */
-       if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS)
+       if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && nv_connector->native_mode)
                nouveau_connector_detect_depth(connector);
 
        /* Find the native mode if this is a digital panel, if we didn't
@@ -987,7 +988,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
         * "native" mode as some VBIOS tables require us to use the
         * pixel clock as part of the lookup...
         */
-       if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
+       if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS && nv_connector->native_mode)
                nouveau_connector_detect_depth(connector);
 
        if (nv_encoder->dcb->type == DCB_OUTPUT_TV)
index cc7c5b4..7aac938 100644 (file)
@@ -137,10 +137,16 @@ nouveau_name(struct drm_device *dev)
 static inline bool
 nouveau_cli_work_ready(struct dma_fence *fence)
 {
-       if (!dma_fence_is_signaled(fence))
-               return false;
-       dma_fence_put(fence);
-       return true;
+       bool ret = true;
+
+       spin_lock_irq(fence->lock);
+       if (!dma_fence_is_signaled_locked(fence))
+               ret = false;
+       spin_unlock_irq(fence->lock);
+
+       if (ret == true)
+               dma_fence_put(fence);
+       return ret;
 }
 
 static void
index fe76e29..8f6c3ae 100644 (file)
@@ -307,6 +307,7 @@ static void radeon_fbdev_client_unregister(struct drm_client_dev *client)
 
        if (fb_helper->info) {
                vga_switcheroo_client_fb_set(rdev->pdev, NULL);
+               drm_helper_force_disable_all(dev);
                drm_fb_helper_unregister_info(fb_helper);
        } else {
                drm_client_release(&fb_helper->client);
index 007f26d..2f4d09c 100644 (file)
@@ -829,11 +829,22 @@ static void vmbus_wait_for_unload(void)
                if (completion_done(&vmbus_connection.unload_event))
                        goto completed;
 
-               for_each_online_cpu(cpu) {
+               for_each_present_cpu(cpu) {
                        struct hv_per_cpu_context *hv_cpu
                                = per_cpu_ptr(hv_context.cpu_context, cpu);
 
+                       /*
+                        * In a CoCo VM the synic_message_page is not allocated
+                        * in hv_synic_alloc(). Instead it is set/cleared in
+                        * hv_synic_enable_regs() and hv_synic_disable_regs()
+                        * such that it is set only when the CPU is online. If
+                        * not all present CPUs are online, the message page
+                        * might be NULL, so skip such CPUs.
+                        */
                        page_addr = hv_cpu->synic_message_page;
+                       if (!page_addr)
+                               continue;
+
                        msg = (struct hv_message *)page_addr
                                + VMBUS_MESSAGE_SINT;
 
@@ -867,11 +878,14 @@ completed:
         * maybe-pending messages on all CPUs to be able to receive new
         * messages after we reconnect.
         */
-       for_each_online_cpu(cpu) {
+       for_each_present_cpu(cpu) {
                struct hv_per_cpu_context *hv_cpu
                        = per_cpu_ptr(hv_context.cpu_context, cpu);
 
                page_addr = hv_cpu->synic_message_page;
+               if (!page_addr)
+                       continue;
+
                msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
                msg->header.message_type = HVMSG_NONE;
        }
index 64f9cec..542a1d5 100644 (file)
@@ -364,13 +364,20 @@ int hv_common_cpu_init(unsigned int cpu)
        flags = irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL;
 
        inputarg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);
-       *inputarg = kmalloc(pgcount * HV_HYP_PAGE_SIZE, flags);
-       if (!(*inputarg))
-               return -ENOMEM;
 
-       if (hv_root_partition) {
-               outputarg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg);
-               *outputarg = (char *)(*inputarg) + HV_HYP_PAGE_SIZE;
+       /*
+        * hyperv_pcpu_input_arg and hyperv_pcpu_output_arg memory is already
+        * allocated if this CPU was previously online and then taken offline
+        */
+       if (!*inputarg) {
+               *inputarg = kmalloc(pgcount * HV_HYP_PAGE_SIZE, flags);
+               if (!(*inputarg))
+                       return -ENOMEM;
+
+               if (hv_root_partition) {
+                       outputarg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg);
+                       *outputarg = (char *)(*inputarg) + HV_HYP_PAGE_SIZE;
+               }
        }
 
        msr_vp_index = hv_get_register(HV_REGISTER_VP_INDEX);
@@ -385,24 +392,17 @@ int hv_common_cpu_init(unsigned int cpu)
 
 int hv_common_cpu_die(unsigned int cpu)
 {
-       unsigned long flags;
-       void **inputarg, **outputarg;
-       void *mem;
-
-       local_irq_save(flags);
-
-       inputarg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);
-       mem = *inputarg;
-       *inputarg = NULL;
-
-       if (hv_root_partition) {
-               outputarg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg);
-               *outputarg = NULL;
-       }
-
-       local_irq_restore(flags);
-
-       kfree(mem);
+       /*
+        * The hyperv_pcpu_input_arg and hyperv_pcpu_output_arg memory
+        * is not freed when the CPU goes offline as the hyperv_pcpu_input_arg
+        * may be used by the Hyper-V vPCI driver in reassigning interrupts
+        * as part of the offlining process.  The interrupt reassignment
+        * happens *after* the CPUHP_AP_HYPERV_ONLINE state has run and
+        * called this function.
+        *
+        * If a previously offlined CPU is brought back online again, the
+        * originally allocated memory is reused in hv_common_cpu_init().
+        */
 
        return 0;
 }
index 1c65a6d..67f95a2 100644 (file)
@@ -1372,7 +1372,7 @@ static int vmbus_bus_init(void)
        ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hyperv/vmbus:online",
                                hv_synic_init, hv_synic_cleanup);
        if (ret < 0)
-               goto err_cpuhp;
+               goto err_alloc;
        hyperv_cpuhp_online = ret;
 
        ret = vmbus_connect();
@@ -1392,9 +1392,8 @@ static int vmbus_bus_init(void)
 
 err_connect:
        cpuhp_remove_state(hyperv_cpuhp_online);
-err_cpuhp:
-       hv_synic_free();
 err_alloc:
+       hv_synic_free();
        if (vmbus_irq == -1) {
                hv_remove_vmbus_handler();
        } else {
index 93a1c48..6b3f438 100644 (file)
@@ -3295,7 +3295,7 @@ static int cma_resolve_iboe_route(struct rdma_id_private *id_priv)
        route->path_rec->traffic_class = tos;
        route->path_rec->mtu = iboe_get_mtu(ndev->mtu);
        route->path_rec->rate_selector = IB_SA_EQ;
-       route->path_rec->rate = iboe_get_rate(ndev);
+       route->path_rec->rate = IB_RATE_PORT_CURRENT;
        dev_put(ndev);
        route->path_rec->packet_life_time_selector = IB_SA_EQ;
        /* In case ACK timeout is set, use this value to calculate
@@ -4964,7 +4964,7 @@ static int cma_iboe_join_multicast(struct rdma_id_private *id_priv,
        if (!ndev)
                return -ENODEV;
 
-       ib.rec.rate = iboe_get_rate(ndev);
+       ib.rec.rate = IB_RATE_PORT_CURRENT;
        ib.rec.hop_limit = 1;
        ib.rec.mtu = iboe_get_mtu(ndev->mtu);
 
index 4796f6a..e836c9c 100644 (file)
@@ -1850,8 +1850,13 @@ static int modify_qp(struct uverbs_attr_bundle *attrs,
                attr->path_mtu = cmd->base.path_mtu;
        if (cmd->base.attr_mask & IB_QP_PATH_MIG_STATE)
                attr->path_mig_state = cmd->base.path_mig_state;
-       if (cmd->base.attr_mask & IB_QP_QKEY)
+       if (cmd->base.attr_mask & IB_QP_QKEY) {
+               if (cmd->base.qkey & IB_QP_SET_QKEY && !capable(CAP_NET_RAW)) {
+                       ret = -EPERM;
+                       goto release_qp;
+               }
                attr->qkey = cmd->base.qkey;
+       }
        if (cmd->base.attr_mask & IB_QP_RQ_PSN)
                attr->rq_psn = cmd->base.rq_psn;
        if (cmd->base.attr_mask & IB_QP_SQ_PSN)
index fbace69..7c9c79c 100644 (file)
@@ -222,8 +222,12 @@ static ssize_t ib_uverbs_event_read(struct ib_uverbs_event_queue *ev_queue,
        spin_lock_irq(&ev_queue->lock);
 
        while (list_empty(&ev_queue->event_list)) {
-               spin_unlock_irq(&ev_queue->lock);
+               if (ev_queue->is_closed) {
+                       spin_unlock_irq(&ev_queue->lock);
+                       return -EIO;
+               }
 
+               spin_unlock_irq(&ev_queue->lock);
                if (filp->f_flags & O_NONBLOCK)
                        return -EAGAIN;
 
@@ -233,12 +237,6 @@ static ssize_t ib_uverbs_event_read(struct ib_uverbs_event_queue *ev_queue,
                        return -ERESTARTSYS;
 
                spin_lock_irq(&ev_queue->lock);
-
-               /* If device was disassociated and no event exists set an error */
-               if (list_empty(&ev_queue->event_list) && ev_queue->is_closed) {
-                       spin_unlock_irq(&ev_queue->lock);
-                       return -EIO;
-               }
        }
 
        event = list_entry(ev_queue->event_list.next, struct ib_uverbs_event, list);
index 5a2baf4..2c95e6f 100644 (file)
@@ -135,8 +135,6 @@ struct bnxt_re_dev {
 
        struct delayed_work             worker;
        u8                              cur_prio_map;
-       u16                             active_speed;
-       u8                              active_width;
 
        /* FP Notification Queue (CQ & SRQ) */
        struct tasklet_struct           nq_task;
index b1c3641..952811c 100644 (file)
@@ -199,6 +199,7 @@ int bnxt_re_query_port(struct ib_device *ibdev, u32 port_num,
 {
        struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev);
        struct bnxt_qplib_dev_attr *dev_attr = &rdev->dev_attr;
+       int rc;
 
        memset(port_attr, 0, sizeof(*port_attr));
 
@@ -228,10 +229,10 @@ int bnxt_re_query_port(struct ib_device *ibdev, u32 port_num,
        port_attr->sm_sl = 0;
        port_attr->subnet_timeout = 0;
        port_attr->init_type_reply = 0;
-       port_attr->active_speed = rdev->active_speed;
-       port_attr->active_width = rdev->active_width;
+       rc = ib_get_eth_speed(&rdev->ibdev, port_num, &port_attr->active_speed,
+                             &port_attr->active_width);
 
-       return 0;
+       return rc;
 }
 
 int bnxt_re_get_port_immutable(struct ib_device *ibdev, u32 port_num,
index e34eccd..3073398 100644 (file)
@@ -1077,8 +1077,6 @@ static int bnxt_re_ib_init(struct bnxt_re_dev *rdev)
                return rc;
        }
        dev_info(rdev_to_dev(rdev), "Device registered with IB successfully");
-       ib_get_eth_speed(&rdev->ibdev, 1, &rdev->active_speed,
-                        &rdev->active_width);
        set_bit(BNXT_RE_FLAG_ISSUE_ROCE_STATS, &rdev->flags);
 
        event = netif_running(rdev->netdev) && netif_carrier_ok(rdev->netdev) ?
index 1c06920..93257fa 100644 (file)
@@ -209,7 +209,8 @@ static const struct mlx5_ib_counters *get_counters(struct mlx5_ib_dev *dev,
             !vport_qcounters_supported(dev)) || !port_num)
                return &dev->port[0].cnts;
 
-       return &dev->port[port_num - 1].cnts;
+       return is_mdev_switchdev_mode(dev->mdev) ?
+              &dev->port[1].cnts : &dev->port[port_num - 1].cnts;
 }
 
 /**
@@ -262,7 +263,7 @@ static struct rdma_hw_stats *
 mlx5_ib_alloc_hw_port_stats(struct ib_device *ibdev, u32 port_num)
 {
        struct mlx5_ib_dev *dev = to_mdev(ibdev);
-       const struct mlx5_ib_counters *cnts = &dev->port[port_num - 1].cnts;
+       const struct mlx5_ib_counters *cnts = get_counters(dev, port_num);
 
        return do_alloc_stats(cnts);
 }
@@ -329,6 +330,7 @@ static int mlx5_ib_query_q_counters_vport(struct mlx5_ib_dev *dev,
 {
        u32 out[MLX5_ST_SZ_DW(query_q_counter_out)] = {};
        u32 in[MLX5_ST_SZ_DW(query_q_counter_in)] = {};
+       struct mlx5_core_dev *mdev;
        __be32 val;
        int ret, i;
 
@@ -336,12 +338,16 @@ static int mlx5_ib_query_q_counters_vport(struct mlx5_ib_dev *dev,
            dev->port[port_num].rep->vport == MLX5_VPORT_UPLINK)
                return 0;
 
+       mdev = mlx5_eswitch_get_core_dev(dev->port[port_num].rep->esw);
+       if (!mdev)
+               return -EOPNOTSUPP;
+
        MLX5_SET(query_q_counter_in, in, opcode, MLX5_CMD_OP_QUERY_Q_COUNTER);
        MLX5_SET(query_q_counter_in, in, other_vport, 1);
        MLX5_SET(query_q_counter_in, in, vport_number,
                 dev->port[port_num].rep->vport);
        MLX5_SET(query_q_counter_in, in, aggregate, 1);
-       ret = mlx5_cmd_exec_inout(dev->mdev, query_q_counter, in, out);
+       ret = mlx5_cmd_exec_inout(mdev, query_q_counter, in, out);
        if (ret)
                return ret;
 
@@ -575,43 +581,53 @@ static void mlx5_ib_fill_counters(struct mlx5_ib_dev *dev,
        bool is_vport = is_mdev_switchdev_mode(dev->mdev) &&
                        port_num != MLX5_VPORT_PF;
        const struct mlx5_ib_counter *names;
-       int j = 0, i;
+       int j = 0, i, size;
 
        names = is_vport ? vport_basic_q_cnts : basic_q_cnts;
-       for (i = 0; i < ARRAY_SIZE(basic_q_cnts); i++, j++) {
+       size = is_vport ? ARRAY_SIZE(vport_basic_q_cnts) :
+                         ARRAY_SIZE(basic_q_cnts);
+       for (i = 0; i < size; i++, j++) {
                descs[j].name = names[i].name;
-               offsets[j] = basic_q_cnts[i].offset;
+               offsets[j] = names[i].offset;
        }
 
        names = is_vport ? vport_out_of_seq_q_cnts : out_of_seq_q_cnts;
+       size = is_vport ? ARRAY_SIZE(vport_out_of_seq_q_cnts) :
+                         ARRAY_SIZE(out_of_seq_q_cnts);
        if (MLX5_CAP_GEN(dev->mdev, out_of_seq_cnt)) {
-               for (i = 0; i < ARRAY_SIZE(out_of_seq_q_cnts); i++, j++) {
+               for (i = 0; i < size; i++, j++) {
                        descs[j].name = names[i].name;
-                       offsets[j] = out_of_seq_q_cnts[i].offset;
+                       offsets[j] = names[i].offset;
                }
        }
 
        names = is_vport ? vport_retrans_q_cnts : retrans_q_cnts;
+       size = is_vport ? ARRAY_SIZE(vport_retrans_q_cnts) :
+                         ARRAY_SIZE(retrans_q_cnts);
        if (MLX5_CAP_GEN(dev->mdev, retransmission_q_counters)) {
-               for (i = 0; i < ARRAY_SIZE(retrans_q_cnts); i++, j++) {
+               for (i = 0; i < size; i++, j++) {
                        descs[j].name = names[i].name;
-                       offsets[j] = retrans_q_cnts[i].offset;
+                       offsets[j] = names[i].offset;
                }
        }
 
        names = is_vport ? vport_extended_err_cnts : extended_err_cnts;
+       size = is_vport ? ARRAY_SIZE(vport_extended_err_cnts) :
+                         ARRAY_SIZE(extended_err_cnts);
        if (MLX5_CAP_GEN(dev->mdev, enhanced_error_q_counters)) {
-               for (i = 0; i < ARRAY_SIZE(extended_err_cnts); i++, j++) {
+               for (i = 0; i < size; i++, j++) {
                        descs[j].name = names[i].name;
-                       offsets[j] = extended_err_cnts[i].offset;
+                       offsets[j] = names[i].offset;
                }
        }
 
        names = is_vport ? vport_roce_accl_cnts : roce_accl_cnts;
+       size = is_vport ? ARRAY_SIZE(vport_roce_accl_cnts) :
+                         ARRAY_SIZE(roce_accl_cnts);
        if (MLX5_CAP_GEN(dev->mdev, roce_accl)) {
-               for (i = 0; i < ARRAY_SIZE(roce_accl_cnts); i++, j++) {
+               for (i = 0; i < size; i++, j++) {
                        descs[j].name = names[i].name;
-                       offsets[j] = roce_accl_cnts[i].offset;
+                       offsets[j] = names[i].offset;
                }
        }
 
@@ -661,25 +677,37 @@ static void mlx5_ib_fill_counters(struct mlx5_ib_dev *dev,
 static int __mlx5_ib_alloc_counters(struct mlx5_ib_dev *dev,
                                    struct mlx5_ib_counters *cnts, u32 port_num)
 {
-       u32 num_counters, num_op_counters = 0;
+       bool is_vport = is_mdev_switchdev_mode(dev->mdev) &&
+                       port_num != MLX5_VPORT_PF;
+       u32 num_counters, num_op_counters = 0, size;
 
-       num_counters = ARRAY_SIZE(basic_q_cnts);
+       size = is_vport ? ARRAY_SIZE(vport_basic_q_cnts) :
+                         ARRAY_SIZE(basic_q_cnts);
+       num_counters = size;
 
+       size = is_vport ? ARRAY_SIZE(vport_out_of_seq_q_cnts) :
+                         ARRAY_SIZE(out_of_seq_q_cnts);
        if (MLX5_CAP_GEN(dev->mdev, out_of_seq_cnt))
-               num_counters += ARRAY_SIZE(out_of_seq_q_cnts);
+               num_counters += size;
 
+       size = is_vport ? ARRAY_SIZE(vport_retrans_q_cnts) :
+                         ARRAY_SIZE(retrans_q_cnts);
        if (MLX5_CAP_GEN(dev->mdev, retransmission_q_counters))
-               num_counters += ARRAY_SIZE(retrans_q_cnts);
+               num_counters += size;
 
+       size = is_vport ? ARRAY_SIZE(vport_extended_err_cnts) :
+                         ARRAY_SIZE(extended_err_cnts);
        if (MLX5_CAP_GEN(dev->mdev, enhanced_error_q_counters))
-               num_counters += ARRAY_SIZE(extended_err_cnts);
+               num_counters += size;
 
+       size = is_vport ? ARRAY_SIZE(vport_roce_accl_cnts) :
+                         ARRAY_SIZE(roce_accl_cnts);
        if (MLX5_CAP_GEN(dev->mdev, roce_accl))
-               num_counters += ARRAY_SIZE(roce_accl_cnts);
+               num_counters += size;
 
        cnts->num_q_counters = num_counters;
 
-       if (is_mdev_switchdev_mode(dev->mdev) && port_num != MLX5_VPORT_PF)
+       if (is_vport)
                goto skip_non_qcounters;
 
        if (MLX5_CAP_GEN(dev->mdev, cc_query_allowed)) {
@@ -725,11 +753,11 @@ err:
 static void mlx5_ib_dealloc_counters(struct mlx5_ib_dev *dev)
 {
        u32 in[MLX5_ST_SZ_DW(dealloc_q_counter_in)] = {};
-       int num_cnt_ports;
+       int num_cnt_ports = dev->num_ports;
        int i, j;
 
-       num_cnt_ports = (!is_mdev_switchdev_mode(dev->mdev) ||
-                        vport_qcounters_supported(dev)) ? dev->num_ports : 1;
+       if (is_mdev_switchdev_mode(dev->mdev))
+               num_cnt_ports = min(2, num_cnt_ports);
 
        MLX5_SET(dealloc_q_counter_in, in, opcode,
                 MLX5_CMD_OP_DEALLOC_Q_COUNTER);
@@ -761,15 +789,22 @@ static int mlx5_ib_alloc_counters(struct mlx5_ib_dev *dev)
 {
        u32 out[MLX5_ST_SZ_DW(alloc_q_counter_out)] = {};
        u32 in[MLX5_ST_SZ_DW(alloc_q_counter_in)] = {};
-       int num_cnt_ports;
+       int num_cnt_ports = dev->num_ports;
        int err = 0;
        int i;
        bool is_shared;
 
        MLX5_SET(alloc_q_counter_in, in, opcode, MLX5_CMD_OP_ALLOC_Q_COUNTER);
        is_shared = MLX5_CAP_GEN(dev->mdev, log_max_uctx) != 0;
-       num_cnt_ports = (!is_mdev_switchdev_mode(dev->mdev) ||
-                        vport_qcounters_supported(dev)) ? dev->num_ports : 1;
+
+       /*
+        * In switchdev we need to allocate two ports, one that is used for
+        * the device Q_counters and it is essentially the real Q_counters of
+        * this device, while the other is used as a helper for PF to be able to
+        * query all other vports.
+        */
+       if (is_mdev_switchdev_mode(dev->mdev))
+               num_cnt_ports = min(2, num_cnt_ports);
 
        for (i = 0; i < num_cnt_ports; i++) {
                err = __mlx5_ib_alloc_counters(dev, &dev->port[i].cnts, i);
index 3008632..1e419e0 100644 (file)
@@ -695,8 +695,6 @@ static struct mlx5_ib_flow_prio *_get_prio(struct mlx5_ib_dev *dev,
        struct mlx5_flow_table_attr ft_attr = {};
        struct mlx5_flow_table *ft;
 
-       if (mlx5_ib_shared_ft_allowed(&dev->ib_dev))
-               ft_attr.uid = MLX5_SHARED_RESOURCE_UID;
        ft_attr.prio = priority;
        ft_attr.max_fte = num_entries;
        ft_attr.flags = flags;
@@ -2025,6 +2023,237 @@ static int flow_matcher_cleanup(struct ib_uobject *uobject,
        return 0;
 }
 
+static int steering_anchor_create_ft(struct mlx5_ib_dev *dev,
+                                    struct mlx5_ib_flow_prio *ft_prio,
+                                    enum mlx5_flow_namespace_type ns_type)
+{
+       struct mlx5_flow_table_attr ft_attr = {};
+       struct mlx5_flow_namespace *ns;
+       struct mlx5_flow_table *ft;
+
+       if (ft_prio->anchor.ft)
+               return 0;
+
+       ns = mlx5_get_flow_namespace(dev->mdev, ns_type);
+       if (!ns)
+               return -EOPNOTSUPP;
+
+       ft_attr.flags = MLX5_FLOW_TABLE_UNMANAGED;
+       ft_attr.uid = MLX5_SHARED_RESOURCE_UID;
+       ft_attr.prio = 0;
+       ft_attr.max_fte = 2;
+       ft_attr.level = 1;
+
+       ft = mlx5_create_flow_table(ns, &ft_attr);
+       if (IS_ERR(ft))
+               return PTR_ERR(ft);
+
+       ft_prio->anchor.ft = ft;
+
+       return 0;
+}
+
+static void steering_anchor_destroy_ft(struct mlx5_ib_flow_prio *ft_prio)
+{
+       if (ft_prio->anchor.ft) {
+               mlx5_destroy_flow_table(ft_prio->anchor.ft);
+               ft_prio->anchor.ft = NULL;
+       }
+}
+
+static int
+steering_anchor_create_fg_drop(struct mlx5_ib_flow_prio *ft_prio)
+{
+       int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+       struct mlx5_flow_group *fg;
+       void *flow_group_in;
+       int err = 0;
+
+       if (ft_prio->anchor.fg_drop)
+               return 0;
+
+       flow_group_in = kvzalloc(inlen, GFP_KERNEL);
+       if (!flow_group_in)
+               return -ENOMEM;
+
+       MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 1);
+       MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1);
+
+       fg = mlx5_create_flow_group(ft_prio->anchor.ft, flow_group_in);
+       if (IS_ERR(fg)) {
+               err = PTR_ERR(fg);
+               goto out;
+       }
+
+       ft_prio->anchor.fg_drop = fg;
+
+out:
+       kvfree(flow_group_in);
+
+       return err;
+}
+
+static void
+steering_anchor_destroy_fg_drop(struct mlx5_ib_flow_prio *ft_prio)
+{
+       if (ft_prio->anchor.fg_drop) {
+               mlx5_destroy_flow_group(ft_prio->anchor.fg_drop);
+               ft_prio->anchor.fg_drop = NULL;
+       }
+}
+
+static int
+steering_anchor_create_fg_goto_table(struct mlx5_ib_flow_prio *ft_prio)
+{
+       int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+       struct mlx5_flow_group *fg;
+       void *flow_group_in;
+       int err = 0;
+
+       if (ft_prio->anchor.fg_goto_table)
+               return 0;
+
+       flow_group_in = kvzalloc(inlen, GFP_KERNEL);
+       if (!flow_group_in)
+               return -ENOMEM;
+
+       fg = mlx5_create_flow_group(ft_prio->anchor.ft, flow_group_in);
+       if (IS_ERR(fg)) {
+               err = PTR_ERR(fg);
+               goto out;
+       }
+       ft_prio->anchor.fg_goto_table = fg;
+
+out:
+       kvfree(flow_group_in);
+
+       return err;
+}
+
+static void
+steering_anchor_destroy_fg_goto_table(struct mlx5_ib_flow_prio *ft_prio)
+{
+       if (ft_prio->anchor.fg_goto_table) {
+               mlx5_destroy_flow_group(ft_prio->anchor.fg_goto_table);
+               ft_prio->anchor.fg_goto_table = NULL;
+       }
+}
+
+static int
+steering_anchor_create_rule_drop(struct mlx5_ib_flow_prio *ft_prio)
+{
+       struct mlx5_flow_act flow_act = {};
+       struct mlx5_flow_handle *handle;
+
+       if (ft_prio->anchor.rule_drop)
+               return 0;
+
+       flow_act.fg = ft_prio->anchor.fg_drop;
+       flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
+
+       handle = mlx5_add_flow_rules(ft_prio->anchor.ft, NULL, &flow_act,
+                                    NULL, 0);
+       if (IS_ERR(handle))
+               return PTR_ERR(handle);
+
+       ft_prio->anchor.rule_drop = handle;
+
+       return 0;
+}
+
+static void steering_anchor_destroy_rule_drop(struct mlx5_ib_flow_prio *ft_prio)
+{
+       if (ft_prio->anchor.rule_drop) {
+               mlx5_del_flow_rules(ft_prio->anchor.rule_drop);
+               ft_prio->anchor.rule_drop = NULL;
+       }
+}
+
+static int
+steering_anchor_create_rule_goto_table(struct mlx5_ib_flow_prio *ft_prio)
+{
+       struct mlx5_flow_destination dest = {};
+       struct mlx5_flow_act flow_act = {};
+       struct mlx5_flow_handle *handle;
+
+       if (ft_prio->anchor.rule_goto_table)
+               return 0;
+
+       flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+       flow_act.flags |= FLOW_ACT_IGNORE_FLOW_LEVEL;
+       flow_act.fg = ft_prio->anchor.fg_goto_table;
+
+       dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+       dest.ft = ft_prio->flow_table;
+
+       handle = mlx5_add_flow_rules(ft_prio->anchor.ft, NULL, &flow_act,
+                                    &dest, 1);
+       if (IS_ERR(handle))
+               return PTR_ERR(handle);
+
+       ft_prio->anchor.rule_goto_table = handle;
+
+       return 0;
+}
+
+static void
+steering_anchor_destroy_rule_goto_table(struct mlx5_ib_flow_prio *ft_prio)
+{
+       if (ft_prio->anchor.rule_goto_table) {
+               mlx5_del_flow_rules(ft_prio->anchor.rule_goto_table);
+               ft_prio->anchor.rule_goto_table = NULL;
+       }
+}
+
+static int steering_anchor_create_res(struct mlx5_ib_dev *dev,
+                                     struct mlx5_ib_flow_prio *ft_prio,
+                                     enum mlx5_flow_namespace_type ns_type)
+{
+       int err;
+
+       err = steering_anchor_create_ft(dev, ft_prio, ns_type);
+       if (err)
+               return err;
+
+       err = steering_anchor_create_fg_drop(ft_prio);
+       if (err)
+               goto destroy_ft;
+
+       err = steering_anchor_create_fg_goto_table(ft_prio);
+       if (err)
+               goto destroy_fg_drop;
+
+       err = steering_anchor_create_rule_drop(ft_prio);
+       if (err)
+               goto destroy_fg_goto_table;
+
+       err = steering_anchor_create_rule_goto_table(ft_prio);
+       if (err)
+               goto destroy_rule_drop;
+
+       return 0;
+
+destroy_rule_drop:
+       steering_anchor_destroy_rule_drop(ft_prio);
+destroy_fg_goto_table:
+       steering_anchor_destroy_fg_goto_table(ft_prio);
+destroy_fg_drop:
+       steering_anchor_destroy_fg_drop(ft_prio);
+destroy_ft:
+       steering_anchor_destroy_ft(ft_prio);
+
+       return err;
+}
+
+static void mlx5_steering_anchor_destroy_res(struct mlx5_ib_flow_prio *ft_prio)
+{
+       steering_anchor_destroy_rule_goto_table(ft_prio);
+       steering_anchor_destroy_rule_drop(ft_prio);
+       steering_anchor_destroy_fg_goto_table(ft_prio);
+       steering_anchor_destroy_fg_drop(ft_prio);
+       steering_anchor_destroy_ft(ft_prio);
+}
+
 static int steering_anchor_cleanup(struct ib_uobject *uobject,
                                   enum rdma_remove_reason why,
                                   struct uverbs_attr_bundle *attrs)
@@ -2035,6 +2264,9 @@ static int steering_anchor_cleanup(struct ib_uobject *uobject,
                return -EBUSY;
 
        mutex_lock(&obj->dev->flow_db->lock);
+       if (!--obj->ft_prio->anchor.rule_goto_table_ref)
+               steering_anchor_destroy_rule_goto_table(obj->ft_prio);
+
        put_flow_table(obj->dev, obj->ft_prio, true);
        mutex_unlock(&obj->dev->flow_db->lock);
 
@@ -2042,6 +2274,24 @@ static int steering_anchor_cleanup(struct ib_uobject *uobject,
        return 0;
 }
 
+static void fs_cleanup_anchor(struct mlx5_ib_flow_prio *prio,
+                             int count)
+{
+       while (count--)
+               mlx5_steering_anchor_destroy_res(&prio[count]);
+}
+
+void mlx5_ib_fs_cleanup_anchor(struct mlx5_ib_dev *dev)
+{
+       fs_cleanup_anchor(dev->flow_db->prios, MLX5_IB_NUM_FLOW_FT);
+       fs_cleanup_anchor(dev->flow_db->egress_prios, MLX5_IB_NUM_FLOW_FT);
+       fs_cleanup_anchor(dev->flow_db->sniffer, MLX5_IB_NUM_SNIFFER_FTS);
+       fs_cleanup_anchor(dev->flow_db->egress, MLX5_IB_NUM_EGRESS_FTS);
+       fs_cleanup_anchor(dev->flow_db->fdb, MLX5_IB_NUM_FDB_FTS);
+       fs_cleanup_anchor(dev->flow_db->rdma_rx, MLX5_IB_NUM_FLOW_FT);
+       fs_cleanup_anchor(dev->flow_db->rdma_tx, MLX5_IB_NUM_FLOW_FT);
+}
+
 static int mlx5_ib_matcher_ns(struct uverbs_attr_bundle *attrs,
                              struct mlx5_ib_flow_matcher *obj)
 {
@@ -2182,21 +2432,31 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_STEERING_ANCHOR_CREATE)(
                return -ENOMEM;
 
        mutex_lock(&dev->flow_db->lock);
+
        ft_prio = _get_flow_table(dev, priority, ns_type, 0);
        if (IS_ERR(ft_prio)) {
-               mutex_unlock(&dev->flow_db->lock);
                err = PTR_ERR(ft_prio);
                goto free_obj;
        }
 
        ft_prio->refcount++;
-       ft_id = mlx5_flow_table_id(ft_prio->flow_table);
-       mutex_unlock(&dev->flow_db->lock);
+
+       if (!ft_prio->anchor.rule_goto_table_ref) {
+               err = steering_anchor_create_res(dev, ft_prio, ns_type);
+               if (err)
+                       goto put_flow_table;
+       }
+
+       ft_prio->anchor.rule_goto_table_ref++;
+
+       ft_id = mlx5_flow_table_id(ft_prio->anchor.ft);
 
        err = uverbs_copy_to(attrs, MLX5_IB_ATTR_STEERING_ANCHOR_FT_ID,
                             &ft_id, sizeof(ft_id));
        if (err)
-               goto put_flow_table;
+               goto destroy_res;
+
+       mutex_unlock(&dev->flow_db->lock);
 
        uobj->object = obj;
        obj->dev = dev;
@@ -2205,8 +2465,10 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_STEERING_ANCHOR_CREATE)(
 
        return 0;
 
+destroy_res:
+       --ft_prio->anchor.rule_goto_table_ref;
+       mlx5_steering_anchor_destroy_res(ft_prio);
 put_flow_table:
-       mutex_lock(&dev->flow_db->lock);
        put_flow_table(dev, ft_prio, true);
        mutex_unlock(&dev->flow_db->lock);
 free_obj:
index ad320ad..b973490 100644 (file)
@@ -10,6 +10,7 @@
 
 #if IS_ENABLED(CONFIG_INFINIBAND_USER_ACCESS)
 int mlx5_ib_fs_init(struct mlx5_ib_dev *dev);
+void mlx5_ib_fs_cleanup_anchor(struct mlx5_ib_dev *dev);
 #else
 static inline int mlx5_ib_fs_init(struct mlx5_ib_dev *dev)
 {
@@ -21,9 +22,24 @@ static inline int mlx5_ib_fs_init(struct mlx5_ib_dev *dev)
        mutex_init(&dev->flow_db->lock);
        return 0;
 }
+
+inline void mlx5_ib_fs_cleanup_anchor(struct mlx5_ib_dev *dev) {}
 #endif
+
 static inline void mlx5_ib_fs_cleanup(struct mlx5_ib_dev *dev)
 {
+       /* When a steering anchor is created, a special flow table is also
+        * created for the user to reference. Since the user can reference it,
+        * the kernel cannot trust that when the user destroys the steering
+        * anchor, they no longer reference the flow table.
+        *
+        * To address this issue, when a user destroys a steering anchor, only
+        * the flow steering rule in the table is destroyed, but the table
+        * itself is kept to deal with the above scenario. The remaining
+        * resources are only removed when the RDMA device is destroyed, which
+        * is a safe assumption that all references are gone.
+        */
+       mlx5_ib_fs_cleanup_anchor(dev);
        kfree(dev->flow_db);
 }
 #endif /* _MLX5_IB_FS_H */
index 5d45de2..f0b394e 100644 (file)
@@ -4275,6 +4275,9 @@ const struct mlx5_ib_profile raw_eth_profile = {
        STAGE_CREATE(MLX5_IB_STAGE_POST_IB_REG_UMR,
                     mlx5_ib_stage_post_ib_reg_umr_init,
                     NULL),
+       STAGE_CREATE(MLX5_IB_STAGE_DELAY_DROP,
+                    mlx5_ib_stage_delay_drop_init,
+                    mlx5_ib_stage_delay_drop_cleanup),
        STAGE_CREATE(MLX5_IB_STAGE_RESTRACK,
                     mlx5_ib_restrack_init,
                     NULL),
index efa4dc6..2dfa6f4 100644 (file)
@@ -237,8 +237,19 @@ enum {
 #define MLX5_IB_NUM_SNIFFER_FTS                2
 #define MLX5_IB_NUM_EGRESS_FTS         1
 #define MLX5_IB_NUM_FDB_FTS            MLX5_BY_PASS_NUM_REGULAR_PRIOS
+
+struct mlx5_ib_anchor {
+       struct mlx5_flow_table *ft;
+       struct mlx5_flow_group *fg_goto_table;
+       struct mlx5_flow_group *fg_drop;
+       struct mlx5_flow_handle *rule_goto_table;
+       struct mlx5_flow_handle *rule_drop;
+       unsigned int rule_goto_table_ref;
+};
+
 struct mlx5_ib_flow_prio {
        struct mlx5_flow_table          *flow_table;
+       struct mlx5_ib_anchor           anchor;
        unsigned int                    refcount;
 };
 
@@ -1587,6 +1598,9 @@ static inline bool mlx5_ib_lag_should_assign_affinity(struct mlx5_ib_dev *dev)
            MLX5_CAP_PORT_SELECTION(dev->mdev, port_select_flow_table_bypass))
                return 0;
 
+       if (mlx5_lag_is_lacp_owner(dev->mdev) && !dev->lag_active)
+               return 0;
+
        return dev->lag_active ||
                (MLX5_CAP_GEN(dev->mdev, num_lag_ports) > 1 &&
                 MLX5_CAP_GEN(dev->mdev, lag_tx_port_affinity));
index 70ca8ff..78b96bf 100644 (file)
@@ -1237,6 +1237,9 @@ static int create_raw_packet_qp_tis(struct mlx5_ib_dev *dev,
 
        MLX5_SET(create_tis_in, in, uid, to_mpd(pd)->uid);
        MLX5_SET(tisc, tisc, transport_domain, tdn);
+       if (!mlx5_ib_lag_should_assign_affinity(dev) &&
+           mlx5_lag_is_lacp_owner(dev->mdev))
+               MLX5_SET(tisc, tisc, strict_lag_tx_port_affinity, 1);
        if (qp->flags & IB_QP_CREATE_SOURCE_QPN)
                MLX5_SET(tisc, tisc, underlay_qpn, qp->underlay_qpn);
 
index 20ff0c0..6ca2a05 100644 (file)
@@ -113,8 +113,6 @@ int rxe_cq_post(struct rxe_cq *cq, struct rxe_cqe *cqe, int solicited)
 
        queue_advance_producer(cq->queue, QUEUE_TYPE_TO_CLIENT);
 
-       spin_unlock_irqrestore(&cq->cq_lock, flags);
-
        if ((cq->notify == IB_CQ_NEXT_COMP) ||
            (cq->notify == IB_CQ_SOLICITED && solicited)) {
                cq->notify = 0;
@@ -122,6 +120,8 @@ int rxe_cq_post(struct rxe_cq *cq, struct rxe_cqe *cqe, int solicited)
                cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
        }
 
+       spin_unlock_irqrestore(&cq->cq_lock, flags);
+
        return 0;
 }
 
index a38fab1..cd59666 100644 (file)
@@ -159,6 +159,9 @@ static int rxe_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
        pkt->mask = RXE_GRH_MASK;
        pkt->paylen = be16_to_cpu(udph->len) - sizeof(*udph);
 
+       /* remove udp header */
+       skb_pull(skb, sizeof(struct udphdr));
+
        rxe_rcv(skb);
 
        return 0;
@@ -401,6 +404,9 @@ static int rxe_loopback(struct sk_buff *skb, struct rxe_pkt_info *pkt)
                return -EIO;
        }
 
+       /* remove udp header */
+       skb_pull(skb, sizeof(struct udphdr));
+
        rxe_rcv(skb);
 
        return 0;
index 61a2eb7..a0f2064 100644 (file)
@@ -176,6 +176,9 @@ static void rxe_qp_init_misc(struct rxe_dev *rxe, struct rxe_qp *qp,
        spin_lock_init(&qp->rq.producer_lock);
        spin_lock_init(&qp->rq.consumer_lock);
 
+       skb_queue_head_init(&qp->req_pkts);
+       skb_queue_head_init(&qp->resp_pkts);
+
        atomic_set(&qp->ssn, 0);
        atomic_set(&qp->skb_out, 0);
 }
@@ -234,8 +237,6 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp,
        qp->req.opcode          = -1;
        qp->comp.opcode         = -1;
 
-       skb_queue_head_init(&qp->req_pkts);
-
        rxe_init_task(&qp->req.task, qp, rxe_requester);
        rxe_init_task(&qp->comp.task, qp, rxe_completer);
 
@@ -279,8 +280,6 @@ static int rxe_qp_init_resp(struct rxe_dev *rxe, struct rxe_qp *qp,
                }
        }
 
-       skb_queue_head_init(&qp->resp_pkts);
-
        rxe_init_task(&qp->resp.task, qp, rxe_responder);
 
        qp->resp.opcode         = OPCODE_NONE;
index 1da044f..ee68306 100644 (file)
@@ -489,8 +489,9 @@ static enum resp_states check_rkey(struct rxe_qp *qp,
                if (mw->access & IB_ZERO_BASED)
                        qp->resp.offset = mw->addr;
 
-               rxe_put(mw);
                rxe_get(mr);
+               rxe_put(mw);
+               mw = NULL;
        } else {
                mr = lookup_mr(qp->pd, access, rkey, RXE_LOOKUP_REMOTE);
                if (!mr) {
index f290cd4..92e1e75 100644 (file)
@@ -657,9 +657,13 @@ static int
 isert_connect_error(struct rdma_cm_id *cma_id)
 {
        struct isert_conn *isert_conn = cma_id->qp->qp_context;
+       struct isert_np *isert_np = cma_id->context;
 
        ib_drain_qp(isert_conn->qp);
+
+       mutex_lock(&isert_np->mutex);
        list_del_init(&isert_conn->node);
+       mutex_unlock(&isert_np->mutex);
        isert_conn->cm_id = NULL;
        isert_put_conn(isert_conn);
 
@@ -2431,6 +2435,7 @@ isert_free_np(struct iscsi_np *np)
 {
        struct isert_np *isert_np = np->np_context;
        struct isert_conn *isert_conn, *n;
+       LIST_HEAD(drop_conn_list);
 
        if (isert_np->cm_id)
                rdma_destroy_id(isert_np->cm_id);
@@ -2450,7 +2455,7 @@ isert_free_np(struct iscsi_np *np)
                                         node) {
                        isert_info("cleaning isert_conn %p state (%d)\n",
                                   isert_conn, isert_conn->state);
-                       isert_connect_release(isert_conn);
+                       list_move_tail(&isert_conn->node, &drop_conn_list);
                }
        }
 
@@ -2461,11 +2466,16 @@ isert_free_np(struct iscsi_np *np)
                                         node) {
                        isert_info("cleaning isert_conn %p state (%d)\n",
                                   isert_conn, isert_conn->state);
-                       isert_connect_release(isert_conn);
+                       list_move_tail(&isert_conn->node, &drop_conn_list);
                }
        }
        mutex_unlock(&isert_np->mutex);
 
+       list_for_each_entry_safe(isert_conn, n, &drop_conn_list, node) {
+               list_del_init(&isert_conn->node);
+               isert_connect_release(isert_conn);
+       }
+
        np->np_context = NULL;
        kfree(isert_np);
 }
@@ -2560,8 +2570,6 @@ static void isert_wait_conn(struct iscsit_conn *conn)
        isert_put_unsol_pending_cmds(conn);
        isert_wait4cmds(conn);
        isert_wait4logout(isert_conn);
-
-       queue_work(isert_release_wq, &isert_conn->release_work);
 }
 
 static void isert_free_conn(struct iscsit_conn *conn)
index edb2e3a..cfb50bf 100644 (file)
@@ -2040,6 +2040,7 @@ static int rtrs_clt_rdma_cm_handler(struct rdma_cm_id *cm_id,
        return 0;
 }
 
+/* The caller should do the cleanup in case of error */
 static int create_cm(struct rtrs_clt_con *con)
 {
        struct rtrs_path *s = con->c.path;
@@ -2062,14 +2063,14 @@ static int create_cm(struct rtrs_clt_con *con)
        err = rdma_set_reuseaddr(cm_id, 1);
        if (err != 0) {
                rtrs_err(s, "Set address reuse failed, err: %d\n", err);
-               goto destroy_cm;
+               return err;
        }
        err = rdma_resolve_addr(cm_id, (struct sockaddr *)&clt_path->s.src_addr,
                                (struct sockaddr *)&clt_path->s.dst_addr,
                                RTRS_CONNECT_TIMEOUT_MS);
        if (err) {
                rtrs_err(s, "Failed to resolve address, err: %d\n", err);
-               goto destroy_cm;
+               return err;
        }
        /*
         * Combine connection status and session events. This is needed
@@ -2084,29 +2085,15 @@ static int create_cm(struct rtrs_clt_con *con)
                if (err == 0)
                        err = -ETIMEDOUT;
                /* Timedout or interrupted */
-               goto errr;
-       }
-       if (con->cm_err < 0) {
-               err = con->cm_err;
-               goto errr;
+               return err;
        }
-       if (READ_ONCE(clt_path->state) != RTRS_CLT_CONNECTING) {
+       if (con->cm_err < 0)
+               return con->cm_err;
+       if (READ_ONCE(clt_path->state) != RTRS_CLT_CONNECTING)
                /* Device removal */
-               err = -ECONNABORTED;
-               goto errr;
-       }
+               return -ECONNABORTED;
 
        return 0;
-
-errr:
-       stop_cm(con);
-       mutex_lock(&con->con_mutex);
-       destroy_con_cq_qp(con);
-       mutex_unlock(&con->con_mutex);
-destroy_cm:
-       destroy_cm(con);
-
-       return err;
 }
 
 static void rtrs_clt_path_up(struct rtrs_clt_path *clt_path)
@@ -2334,7 +2321,7 @@ static void rtrs_clt_close_work(struct work_struct *work)
 static int init_conns(struct rtrs_clt_path *clt_path)
 {
        unsigned int cid;
-       int err;
+       int err, i;
 
        /*
         * On every new session connections increase reconnect counter
@@ -2350,10 +2337,8 @@ static int init_conns(struct rtrs_clt_path *clt_path)
                        goto destroy;
 
                err = create_cm(to_clt_con(clt_path->s.con[cid]));
-               if (err) {
-                       destroy_con(to_clt_con(clt_path->s.con[cid]));
+               if (err)
                        goto destroy;
-               }
        }
        err = alloc_path_reqs(clt_path);
        if (err)
@@ -2364,15 +2349,21 @@ static int init_conns(struct rtrs_clt_path *clt_path)
        return 0;
 
 destroy:
-       while (cid--) {
-               struct rtrs_clt_con *con = to_clt_con(clt_path->s.con[cid]);
+       /* Make sure we do the cleanup in the order they are created */
+       for (i = 0; i <= cid; i++) {
+               struct rtrs_clt_con *con;
 
-               stop_cm(con);
+               if (!clt_path->s.con[i])
+                       break;
 
-               mutex_lock(&con->con_mutex);
-               destroy_con_cq_qp(con);
-               mutex_unlock(&con->con_mutex);
-               destroy_cm(con);
+               con = to_clt_con(clt_path->s.con[i]);
+               if (con->c.cm_id) {
+                       stop_cm(con);
+                       mutex_lock(&con->con_mutex);
+                       destroy_con_cq_qp(con);
+                       mutex_unlock(&con->con_mutex);
+                       destroy_cm(con);
+               }
                destroy_con(con);
        }
        /*
index 4bf9d86..3696f36 100644 (file)
@@ -37,8 +37,10 @@ struct rtrs_iu *rtrs_iu_alloc(u32 iu_num, size_t size, gfp_t gfp_mask,
                        goto err;
 
                iu->dma_addr = ib_dma_map_single(dma_dev, iu->buf, size, dir);
-               if (ib_dma_mapping_error(dma_dev, iu->dma_addr))
+               if (ib_dma_mapping_error(dma_dev, iu->dma_addr)) {
+                       kfree(iu->buf);
                        goto err;
+               }
 
                iu->cqe.done  = done;
                iu->size      = size;
index cc77cf3..7d5c9c5 100644 (file)
@@ -1168,13 +1168,10 @@ static int do_resume(struct dm_ioctl *param)
        /* Do we need to load a new map ? */
        if (new_map) {
                sector_t old_size, new_size;
-               int srcu_idx;
 
                /* Suspend if it isn't already suspended */
-               old_map = dm_get_live_table(md, &srcu_idx);
-               if ((param->flags & DM_SKIP_LOCKFS_FLAG) || !old_map)
+               if (param->flags & DM_SKIP_LOCKFS_FLAG)
                        suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
-               dm_put_live_table(md, srcu_idx);
                if (param->flags & DM_NOFLUSH_FLAG)
                        suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
                if (!dm_suspended_md(md))
index 9f5cb52..b9461fa 100644 (file)
@@ -1756,13 +1756,15 @@ int dm_thin_remove_range(struct dm_thin_device *td,
 
 int dm_pool_block_is_shared(struct dm_pool_metadata *pmd, dm_block_t b, bool *result)
 {
-       int r;
+       int r = -EINVAL;
        uint32_t ref_count;
 
        down_read(&pmd->root_lock);
-       r = dm_sm_get_count(pmd->data_sm, b, &ref_count);
-       if (!r)
-               *result = (ref_count > 1);
+       if (!pmd->fail_io) {
+               r = dm_sm_get_count(pmd->data_sm, b, &ref_count);
+               if (!r)
+                       *result = (ref_count > 1);
+       }
        up_read(&pmd->root_lock);
 
        return r;
@@ -1770,10 +1772,11 @@ int dm_pool_block_is_shared(struct dm_pool_metadata *pmd, dm_block_t b, bool *re
 
 int dm_pool_inc_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_t e)
 {
-       int r = 0;
+       int r = -EINVAL;
 
        pmd_write_lock(pmd);
-       r = dm_sm_inc_blocks(pmd->data_sm, b, e);
+       if (!pmd->fail_io)
+               r = dm_sm_inc_blocks(pmd->data_sm, b, e);
        pmd_write_unlock(pmd);
 
        return r;
@@ -1781,10 +1784,11 @@ int dm_pool_inc_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_
 
 int dm_pool_dec_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_t e)
 {
-       int r = 0;
+       int r = -EINVAL;
 
        pmd_write_lock(pmd);
-       r = dm_sm_dec_blocks(pmd->data_sm, b, e);
+       if (!pmd->fail_io)
+               r = dm_sm_dec_blocks(pmd->data_sm, b, e);
        pmd_write_unlock(pmd);
 
        return r;
index 2b13c94..39410bf 100644 (file)
@@ -401,8 +401,7 @@ static int issue_discard(struct discard_op *op, dm_block_t data_b, dm_block_t da
        sector_t s = block_to_sectors(tc->pool, data_b);
        sector_t len = block_to_sectors(tc->pool, data_e - data_b);
 
-       return __blkdev_issue_discard(tc->pool_dev->bdev, s, len, GFP_NOWAIT,
-                                     &op->bio);
+       return __blkdev_issue_discard(tc->pool_dev->bdev, s, len, GFP_NOIO, &op->bio);
 }
 
 static void end_discard(struct discard_op *op, int r)
index 3b694ba..fffb0cb 100644 (file)
@@ -1172,7 +1172,8 @@ static inline sector_t max_io_len_target_boundary(struct dm_target *ti,
 }
 
 static sector_t __max_io_len(struct dm_target *ti, sector_t sector,
-                            unsigned int max_granularity)
+                            unsigned int max_granularity,
+                            unsigned int max_sectors)
 {
        sector_t target_offset = dm_target_offset(ti, sector);
        sector_t len = max_io_len_target_boundary(ti, target_offset);
@@ -1186,13 +1187,13 @@ static sector_t __max_io_len(struct dm_target *ti, sector_t sector,
        if (!max_granularity)
                return len;
        return min_t(sector_t, len,
-               min(queue_max_sectors(ti->table->md->queue),
+               min(max_sectors ? : queue_max_sectors(ti->table->md->queue),
                    blk_chunk_sectors_left(target_offset, max_granularity)));
 }
 
 static inline sector_t max_io_len(struct dm_target *ti, sector_t sector)
 {
-       return __max_io_len(ti, sector, ti->max_io_len);
+       return __max_io_len(ti, sector, ti->max_io_len, 0);
 }
 
 int dm_set_target_max_io_len(struct dm_target *ti, sector_t len)
@@ -1581,12 +1582,13 @@ static void __send_empty_flush(struct clone_info *ci)
 
 static void __send_changing_extent_only(struct clone_info *ci, struct dm_target *ti,
                                        unsigned int num_bios,
-                                       unsigned int max_granularity)
+                                       unsigned int max_granularity,
+                                       unsigned int max_sectors)
 {
        unsigned int len, bios;
 
        len = min_t(sector_t, ci->sector_count,
-                   __max_io_len(ti, ci->sector, max_granularity));
+                   __max_io_len(ti, ci->sector, max_granularity, max_sectors));
 
        atomic_add(num_bios, &ci->io->io_count);
        bios = __send_duplicate_bios(ci, ti, num_bios, &len);
@@ -1623,23 +1625,27 @@ static blk_status_t __process_abnormal_io(struct clone_info *ci,
 {
        unsigned int num_bios = 0;
        unsigned int max_granularity = 0;
+       unsigned int max_sectors = 0;
        struct queue_limits *limits = dm_get_queue_limits(ti->table->md);
 
        switch (bio_op(ci->bio)) {
        case REQ_OP_DISCARD:
                num_bios = ti->num_discard_bios;
+               max_sectors = limits->max_discard_sectors;
                if (ti->max_discard_granularity)
-                       max_granularity = limits->max_discard_sectors;
+                       max_granularity = max_sectors;
                break;
        case REQ_OP_SECURE_ERASE:
                num_bios = ti->num_secure_erase_bios;
+               max_sectors = limits->max_secure_erase_sectors;
                if (ti->max_secure_erase_granularity)
-                       max_granularity = limits->max_secure_erase_sectors;
+                       max_granularity = max_sectors;
                break;
        case REQ_OP_WRITE_ZEROES:
                num_bios = ti->num_write_zeroes_bios;
+               max_sectors = limits->max_write_zeroes_sectors;
                if (ti->max_write_zeroes_granularity)
-                       max_granularity = limits->max_write_zeroes_sectors;
+                       max_granularity = max_sectors;
                break;
        default:
                break;
@@ -1654,7 +1660,8 @@ static blk_status_t __process_abnormal_io(struct clone_info *ci,
        if (unlikely(!num_bios))
                return BLK_STS_NOTSUPP;
 
-       __send_changing_extent_only(ci, ti, num_bios, max_granularity);
+       __send_changing_extent_only(ci, ti, num_bios,
+                                   max_granularity, max_sectors);
        return BLK_STS_OK;
 }
 
@@ -2808,6 +2815,10 @@ retry:
        }
 
        map = rcu_dereference_protected(md->map, lockdep_is_held(&md->suspend_lock));
+       if (!map) {
+               /* avoid deadlock with fs/namespace.c:do_mount() */
+               suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
+       }
 
        r = __dm_suspend(md, map, suspend_flags, TASK_INTERRUPTIBLE, DMF_SUSPENDED);
        if (r)
index bc6950a..9293b05 100644 (file)
@@ -817,26 +817,15 @@ static void dvb_frontend_stop(struct dvb_frontend *fe)
 
        dev_dbg(fe->dvb->device, "%s:\n", __func__);
 
-       mutex_lock(&fe->remove_mutex);
-
        if (fe->exit != DVB_FE_DEVICE_REMOVED)
                fe->exit = DVB_FE_NORMAL_EXIT;
        mb();
 
-       if (!fepriv->thread) {
-               mutex_unlock(&fe->remove_mutex);
+       if (!fepriv->thread)
                return;
-       }
 
        kthread_stop(fepriv->thread);
 
-       mutex_unlock(&fe->remove_mutex);
-
-       if (fepriv->dvbdev->users < -1) {
-               wait_event(fepriv->dvbdev->wait_queue,
-                          fepriv->dvbdev->users == -1);
-       }
-
        sema_init(&fepriv->sem, 1);
        fepriv->state = FESTATE_IDLE;
 
@@ -2780,13 +2769,9 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
        struct dvb_adapter *adapter = fe->dvb;
        int ret;
 
-       mutex_lock(&fe->remove_mutex);
-
        dev_dbg(fe->dvb->device, "%s:\n", __func__);
-       if (fe->exit == DVB_FE_DEVICE_REMOVED) {
-               ret = -ENODEV;
-               goto err_remove_mutex;
-       }
+       if (fe->exit == DVB_FE_DEVICE_REMOVED)
+               return -ENODEV;
 
        if (adapter->mfe_shared == 2) {
                mutex_lock(&adapter->mfe_lock);
@@ -2794,8 +2779,7 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
                        if (adapter->mfe_dvbdev &&
                            !adapter->mfe_dvbdev->writers) {
                                mutex_unlock(&adapter->mfe_lock);
-                               ret = -EBUSY;
-                               goto err_remove_mutex;
+                               return -EBUSY;
                        }
                        adapter->mfe_dvbdev = dvbdev;
                }
@@ -2818,10 +2802,8 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
                        while (mferetry-- && (mfedev->users != -1 ||
                                              mfepriv->thread)) {
                                if (msleep_interruptible(500)) {
-                                       if (signal_pending(current)) {
-                                               ret = -EINTR;
-                                               goto err_remove_mutex;
-                                       }
+                                       if (signal_pending(current))
+                                               return -EINTR;
                                }
                        }
 
@@ -2833,8 +2815,7 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
                                if (mfedev->users != -1 ||
                                    mfepriv->thread) {
                                        mutex_unlock(&adapter->mfe_lock);
-                                       ret = -EBUSY;
-                                       goto err_remove_mutex;
+                                       return -EBUSY;
                                }
                                adapter->mfe_dvbdev = dvbdev;
                        }
@@ -2893,8 +2874,6 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
 
        if (adapter->mfe_shared)
                mutex_unlock(&adapter->mfe_lock);
-
-       mutex_unlock(&fe->remove_mutex);
        return ret;
 
 err3:
@@ -2916,9 +2895,6 @@ err1:
 err0:
        if (adapter->mfe_shared)
                mutex_unlock(&adapter->mfe_lock);
-
-err_remove_mutex:
-       mutex_unlock(&fe->remove_mutex);
        return ret;
 }
 
@@ -2929,8 +2905,6 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
        int ret;
 
-       mutex_lock(&fe->remove_mutex);
-
        dev_dbg(fe->dvb->device, "%s:\n", __func__);
 
        if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
@@ -2952,18 +2926,10 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
                }
                mutex_unlock(&fe->dvb->mdev_lock);
 #endif
+               if (fe->exit != DVB_FE_NO_EXIT)
+                       wake_up(&dvbdev->wait_queue);
                if (fe->ops.ts_bus_ctrl)
                        fe->ops.ts_bus_ctrl(fe, 0);
-
-               if (fe->exit != DVB_FE_NO_EXIT) {
-                       mutex_unlock(&fe->remove_mutex);
-                       wake_up(&dvbdev->wait_queue);
-               } else {
-                       mutex_unlock(&fe->remove_mutex);
-               }
-
-       } else {
-               mutex_unlock(&fe->remove_mutex);
        }
 
        dvb_frontend_put(fe);
@@ -3064,7 +3030,6 @@ int dvb_register_frontend(struct dvb_adapter *dvb,
        fepriv = fe->frontend_priv;
 
        kref_init(&fe->refcount);
-       mutex_init(&fe->remove_mutex);
 
        /*
         * After initialization, there need to be two references: one
index cfb3fae..d172a3e 100644 (file)
@@ -1263,7 +1263,7 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port)
        /* Consider the standard Ethernet overhead of 8 octets preamble+SFD,
         * 4 octets FCS, 12 octets IFG.
         */
-       needed_bit_time_ps = (maxlen + 24) * picos_per_byte;
+       needed_bit_time_ps = (u64)(maxlen + 24) * picos_per_byte;
 
        dev_dbg(ocelot->dev,
                "port %d: max frame size %d needs %llu ps at speed %d\n",
index 637d162..1e7a6f1 100644 (file)
@@ -14294,11 +14294,16 @@ static void bnx2x_io_resume(struct pci_dev *pdev)
        bp->fw_seq = SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) &
                                                        DRV_MSG_SEQ_NUMBER_MASK;
 
-       if (netif_running(dev))
-               bnx2x_nic_load(bp, LOAD_NORMAL);
+       if (netif_running(dev)) {
+               if (bnx2x_nic_load(bp, LOAD_NORMAL)) {
+                       netdev_err(bp->dev, "Error during driver initialization, try unloading/reloading the driver\n");
+                       goto done;
+               }
+       }
 
        netif_device_attach(dev);
 
+done:
        rtnl_unlock();
 }
 
index 83c27bb..126007a 100644 (file)
@@ -181,8 +181,8 @@ int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data)
        int bw_sum = 0;
        u8 bw;
 
-       prio_top = netdev_get_prio_tc_map(ndev, tc_nums - 1);
-       prio_next = netdev_get_prio_tc_map(ndev, tc_nums - 2);
+       prio_top = tc_nums - 1;
+       prio_next = tc_nums - 2;
 
        /* Support highest prio and second prio tc in cbs mode */
        if (tc != prio_top && tc != prio_next)
index 9abaff1..39d0fe7 100644 (file)
@@ -525,7 +525,7 @@ void iavf_set_ethtool_ops(struct net_device *netdev);
 void iavf_update_stats(struct iavf_adapter *adapter);
 void iavf_reset_interrupt_capability(struct iavf_adapter *adapter);
 int iavf_init_interrupt_scheme(struct iavf_adapter *adapter);
-void iavf_irq_enable_queues(struct iavf_adapter *adapter, u32 mask);
+void iavf_irq_enable_queues(struct iavf_adapter *adapter);
 void iavf_free_all_tx_resources(struct iavf_adapter *adapter);
 void iavf_free_all_rx_resources(struct iavf_adapter *adapter);
 
index 2de4baf..4a66873 100644 (file)
@@ -359,21 +359,18 @@ static void iavf_irq_disable(struct iavf_adapter *adapter)
 }
 
 /**
- * iavf_irq_enable_queues - Enable interrupt for specified queues
+ * iavf_irq_enable_queues - Enable interrupt for all queues
  * @adapter: board private structure
- * @mask: bitmap of queues to enable
  **/
-void iavf_irq_enable_queues(struct iavf_adapter *adapter, u32 mask)
+void iavf_irq_enable_queues(struct iavf_adapter *adapter)
 {
        struct iavf_hw *hw = &adapter->hw;
        int i;
 
        for (i = 1; i < adapter->num_msix_vectors; i++) {
-               if (mask & BIT(i - 1)) {
-                       wr32(hw, IAVF_VFINT_DYN_CTLN1(i - 1),
-                            IAVF_VFINT_DYN_CTLN1_INTENA_MASK |
-                            IAVF_VFINT_DYN_CTLN1_ITR_INDX_MASK);
-               }
+               wr32(hw, IAVF_VFINT_DYN_CTLN1(i - 1),
+                    IAVF_VFINT_DYN_CTLN1_INTENA_MASK |
+                    IAVF_VFINT_DYN_CTLN1_ITR_INDX_MASK);
        }
 }
 
@@ -387,7 +384,7 @@ void iavf_irq_enable(struct iavf_adapter *adapter, bool flush)
        struct iavf_hw *hw = &adapter->hw;
 
        iavf_misc_irq_enable(adapter);
-       iavf_irq_enable_queues(adapter, ~0);
+       iavf_irq_enable_queues(adapter);
 
        if (flush)
                iavf_flush(hw);
index bf79333..a19e888 100644 (file)
@@ -40,7 +40,7 @@
 #define IAVF_VFINT_DYN_CTL01_INTENA_MASK IAVF_MASK(0x1, IAVF_VFINT_DYN_CTL01_INTENA_SHIFT)
 #define IAVF_VFINT_DYN_CTL01_ITR_INDX_SHIFT 3
 #define IAVF_VFINT_DYN_CTL01_ITR_INDX_MASK IAVF_MASK(0x3, IAVF_VFINT_DYN_CTL01_ITR_INDX_SHIFT)
-#define IAVF_VFINT_DYN_CTLN1(_INTVF) (0x00003800 + ((_INTVF) * 4)) /* _i=0...15 */ /* Reset: VFR */
+#define IAVF_VFINT_DYN_CTLN1(_INTVF) (0x00003800 + ((_INTVF) * 4)) /* _i=0...63 */ /* Reset: VFR */
 #define IAVF_VFINT_DYN_CTLN1_INTENA_SHIFT 0
 #define IAVF_VFINT_DYN_CTLN1_INTENA_MASK IAVF_MASK(0x1, IAVF_VFINT_DYN_CTLN1_INTENA_SHIFT)
 #define IAVF_VFINT_DYN_CTLN1_SWINT_TRIG_SHIFT 2
index bd0ed15..75c9de6 100644 (file)
@@ -96,12 +96,7 @@ static void ice_gnss_read(struct kthread_work *work)
        int err = 0;
 
        pf = gnss->back;
-       if (!pf) {
-               err = -EFAULT;
-               goto exit;
-       }
-
-       if (!test_bit(ICE_FLAG_GNSS, pf->flags))
+       if (!pf || !test_bit(ICE_FLAG_GNSS, pf->flags))
                return;
 
        hw = &pf->hw;
@@ -159,7 +154,6 @@ free_buf:
        free_page((unsigned long)buf);
 requeue:
        kthread_queue_delayed_work(gnss->kworker, &gnss->read_work, delay);
-exit:
        if (err)
                dev_dbg(ice_pf_to_dev(pf), "GNSS failed to read err=%d\n", err);
 }
index a1f7c8e..42c318c 100644 (file)
@@ -4802,9 +4802,13 @@ err_init_pf:
 static void ice_deinit_dev(struct ice_pf *pf)
 {
        ice_free_irq_msix_misc(pf);
-       ice_clear_interrupt_scheme(pf);
        ice_deinit_pf(pf);
        ice_deinit_hw(&pf->hw);
+
+       /* Service task is already stopped, so call reset directly. */
+       ice_reset(&pf->hw, ICE_RESET_PFR);
+       pci_wait_for_pending_transaction(pf->pdev);
+       ice_clear_interrupt_scheme(pf);
 }
 
 static void ice_init_features(struct ice_pf *pf)
@@ -5094,10 +5098,6 @@ int ice_load(struct ice_pf *pf)
        struct ice_vsi *vsi;
        int err;
 
-       err = ice_reset(&pf->hw, ICE_RESET_PFR);
-       if (err)
-               return err;
-
        err = ice_init_dev(pf);
        if (err)
                return err;
@@ -5354,12 +5354,6 @@ static void ice_remove(struct pci_dev *pdev)
        ice_setup_mc_magic_wake(pf);
        ice_set_wake(pf);
 
-       /* Issue a PFR as part of the prescribed driver unload flow.  Do not
-        * do it via ice_schedule_reset() since there is no need to rebuild
-        * and the service task is already stopped.
-        */
-       ice_reset(&pf->hw, ICE_RESET_PFR);
-       pci_wait_for_pending_transaction(pdev);
        pci_disable_device(pdev);
 }
 
@@ -7056,6 +7050,10 @@ int ice_down(struct ice_vsi *vsi)
        ice_for_each_txq(vsi, i)
                ice_clean_tx_ring(vsi->tx_rings[i]);
 
+       if (ice_is_xdp_ena_vsi(vsi))
+               ice_for_each_xdp_txq(vsi, i)
+                       ice_clean_tx_ring(vsi->xdp_rings[i]);
+
        ice_for_each_rxq(vsi, i)
                ice_clean_rx_ring(vsi->rx_rings[i]);
 
index 7d60da1..319ed60 100644 (file)
@@ -822,6 +822,8 @@ static int igb_set_eeprom(struct net_device *netdev,
                 */
                ret_val = hw->nvm.ops.read(hw, last_word, 1,
                                   &eeprom_buff[last_word - first_word]);
+               if (ret_val)
+                       goto out;
        }
 
        /* Device's eeprom is always little-endian, word addressable */
@@ -841,6 +843,7 @@ static int igb_set_eeprom(struct net_device *netdev,
                hw->nvm.ops.update(hw);
 
        igb_set_fw_version(adapter);
+out:
        kfree(eeprom_buff);
        return ret_val;
 }
index 58872a4..bb3db38 100644 (file)
@@ -6947,6 +6947,7 @@ static void igb_extts(struct igb_adapter *adapter, int tsintr_tt)
        struct e1000_hw *hw = &adapter->hw;
        struct ptp_clock_event event;
        struct timespec64 ts;
+       unsigned long flags;
 
        if (pin < 0 || pin >= IGB_N_SDP)
                return;
@@ -6954,9 +6955,12 @@ static void igb_extts(struct igb_adapter *adapter, int tsintr_tt)
        if (hw->mac.type == e1000_82580 ||
            hw->mac.type == e1000_i354 ||
            hw->mac.type == e1000_i350) {
-               s64 ns = rd32(auxstmpl);
+               u64 ns = rd32(auxstmpl);
 
-               ns += ((s64)(rd32(auxstmph) & 0xFF)) << 32;
+               ns += ((u64)(rd32(auxstmph) & 0xFF)) << 32;
+               spin_lock_irqsave(&adapter->tmreg_lock, flags);
+               ns = timecounter_cyc2time(&adapter->tc, ns);
+               spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
                ts = ns_to_timespec64(ns);
        } else {
                ts.tv_nsec = rd32(auxstmpl);
index 1c46768..fa76419 100644 (file)
@@ -254,6 +254,13 @@ static void igc_clean_tx_ring(struct igc_ring *tx_ring)
        /* reset BQL for queue */
        netdev_tx_reset_queue(txring_txq(tx_ring));
 
+       /* Zero out the buffer ring */
+       memset(tx_ring->tx_buffer_info, 0,
+              sizeof(*tx_ring->tx_buffer_info) * tx_ring->count);
+
+       /* Zero out the descriptor ring */
+       memset(tx_ring->desc, 0, tx_ring->size);
+
        /* reset next_to_use and next_to_clean */
        tx_ring->next_to_use = 0;
        tx_ring->next_to_clean = 0;
@@ -267,7 +274,7 @@ static void igc_clean_tx_ring(struct igc_ring *tx_ring)
  */
 void igc_free_tx_resources(struct igc_ring *tx_ring)
 {
-       igc_clean_tx_ring(tx_ring);
+       igc_disable_tx_ring(tx_ring);
 
        vfree(tx_ring->tx_buffer_info);
        tx_ring->tx_buffer_info = NULL;
@@ -6723,6 +6730,9 @@ static void igc_remove(struct pci_dev *pdev)
 
        igc_ptp_stop(adapter);
 
+       pci_disable_ptm(pdev);
+       pci_clear_master(pdev);
+
        set_bit(__IGC_DOWN, &adapter->state);
 
        del_timer_sync(&adapter->watchdog_timer);
index e1853da..43eb6e8 100644 (file)
@@ -981,6 +981,9 @@ int octep_device_setup(struct octep_device *oct)
                oct->mmio[i].hw_addr =
                        ioremap(pci_resource_start(oct->pdev, i * 2),
                                pci_resource_len(oct->pdev, i * 2));
+               if (!oct->mmio[i].hw_addr)
+                       goto unmap_prev;
+
                oct->mmio[i].mapped = 1;
        }
 
@@ -1015,7 +1018,9 @@ int octep_device_setup(struct octep_device *oct)
        return 0;
 
 unsupported_dev:
-       for (i = 0; i < OCTEP_MMIO_REGIONS; i++)
+       i = OCTEP_MMIO_REGIONS;
+unmap_prev:
+       while (i--)
                iounmap(oct->mmio[i].hw_addr);
 
        kfree(oct->conf);
index 4ad707e..f01d057 100644 (file)
@@ -1878,7 +1878,8 @@ static int nix_check_txschq_alloc_req(struct rvu *rvu, int lvl, u16 pcifunc,
                free_cnt = rvu_rsrc_free_count(&txsch->schq);
        }
 
-       if (free_cnt < req_schq || req_schq > MAX_TXSCHQ_PER_FUNC)
+       if (free_cnt < req_schq || req->schq[lvl] > MAX_TXSCHQ_PER_FUNC ||
+           req->schq_contig[lvl] > MAX_TXSCHQ_PER_FUNC)
                return NIX_AF_ERR_TLX_ALLOC_FAIL;
 
        /* If contiguous queues are needed, check for availability */
@@ -4080,10 +4081,6 @@ int rvu_mbox_handler_nix_set_rx_cfg(struct rvu *rvu, struct nix_rx_cfg *req,
 
 static u64 rvu_get_lbk_link_credits(struct rvu *rvu, u16 lbk_max_frs)
 {
-       /* CN10k supports 72KB FIFO size and max packet size of 64k */
-       if (rvu->hw->lbk_bufsize == 0x12000)
-               return (rvu->hw->lbk_bufsize - lbk_max_frs) / 16;
-
        return 1600; /* 16 * max LBK datarate = 16 * 100Gbps */
 }
 
index 5120911..9f11c1e 100644 (file)
@@ -1164,10 +1164,8 @@ static u16 __rvu_npc_exact_cmd_rules_cnt_update(struct rvu *rvu, int drop_mcam_i
 {
        struct npc_exact_table *table;
        u16 *cnt, old_cnt;
-       bool promisc;
 
        table = rvu->hw->table;
-       promisc = table->promisc_mode[drop_mcam_idx];
 
        cnt = &table->cnt_cmd_rules[drop_mcam_idx];
        old_cnt = *cnt;
@@ -1179,16 +1177,13 @@ static u16 __rvu_npc_exact_cmd_rules_cnt_update(struct rvu *rvu, int drop_mcam_i
 
        *enable_or_disable_cam = false;
 
-       if (promisc)
-               goto done;
-
-       /* If all rules are deleted and not already in promisc mode; disable cam */
+       /* If all rules are deleted, disable cam */
        if (!*cnt && val < 0) {
                *enable_or_disable_cam = true;
                goto done;
        }
 
-       /* If rule got added and not already in promisc mode; enable cam */
+       /* If rule got added, enable cam */
        if (!old_cnt && val > 0) {
                *enable_or_disable_cam = true;
                goto done;
@@ -1443,7 +1438,6 @@ int rvu_npc_exact_promisc_disable(struct rvu *rvu, u16 pcifunc)
        u32 drop_mcam_idx;
        bool *promisc;
        bool rc;
-       u32 cnt;
 
        table = rvu->hw->table;
 
@@ -1466,17 +1460,8 @@ int rvu_npc_exact_promisc_disable(struct rvu *rvu, u16 pcifunc)
                return LMAC_AF_ERR_INVALID_PARAM;
        }
        *promisc = false;
-       cnt = __rvu_npc_exact_cmd_rules_cnt_update(rvu, drop_mcam_idx, 0, NULL);
        mutex_unlock(&table->lock);
 
-       /* If no dmac filter entries configured, disable drop rule */
-       if (!cnt)
-               rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, false);
-       else
-               rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, !*promisc);
-
-       dev_dbg(rvu->dev, "%s: disabled  promisc mode (cgx=%d lmac=%d, cnt=%d)\n",
-               __func__, cgx_id, lmac_id, cnt);
        return 0;
 }
 
@@ -1494,7 +1479,6 @@ int rvu_npc_exact_promisc_enable(struct rvu *rvu, u16 pcifunc)
        u32 drop_mcam_idx;
        bool *promisc;
        bool rc;
-       u32 cnt;
 
        table = rvu->hw->table;
 
@@ -1517,17 +1501,8 @@ int rvu_npc_exact_promisc_enable(struct rvu *rvu, u16 pcifunc)
                return LMAC_AF_ERR_INVALID_PARAM;
        }
        *promisc = true;
-       cnt = __rvu_npc_exact_cmd_rules_cnt_update(rvu, drop_mcam_idx, 0, NULL);
        mutex_unlock(&table->lock);
 
-       /* If no dmac filter entries configured, disable drop rule */
-       if (!cnt)
-               rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, false);
-       else
-               rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, !*promisc);
-
-       dev_dbg(rvu->dev, "%s: Enabled promisc mode (cgx=%d lmac=%d cnt=%d)\n",
-               __func__, cgx_id, lmac_id, cnt);
        return 0;
 }
 
index 1d87937..2295204 100644 (file)
@@ -276,18 +276,6 @@ static inline bool mlx5_sriov_is_enabled(struct mlx5_core_dev *dev)
        return pci_num_vf(dev->pdev) ? true : false;
 }
 
-static inline int mlx5_lag_is_lacp_owner(struct mlx5_core_dev *dev)
-{
-       /* LACP owner conditions:
-        * 1) Function is physical.
-        * 2) LAG is supported by FW.
-        * 3) LAG is managed by driver (currently the only option).
-        */
-       return  MLX5_CAP_GEN(dev, vport_group_manager) &&
-                  (MLX5_CAP_GEN(dev, num_lag_ports) > 1) &&
-                   MLX5_CAP_GEN(dev, lag_master);
-}
-
 int mlx5_rescan_drivers_locked(struct mlx5_core_dev *dev);
 static inline int mlx5_rescan_drivers(struct mlx5_core_dev *dev)
 {
index aace871..fa6d620 100644 (file)
@@ -347,17 +347,6 @@ out:
        return -ENOMEM;
 }
 
-static int rswitch_gwca_ts_queue_alloc(struct rswitch_private *priv)
-{
-       struct rswitch_gwca_queue *gq = &priv->gwca.ts_queue;
-
-       gq->ring_size = TS_RING_SIZE;
-       gq->ts_ring = dma_alloc_coherent(&priv->pdev->dev,
-                                        sizeof(struct rswitch_ts_desc) *
-                                        (gq->ring_size + 1), &gq->ring_dma, GFP_KERNEL);
-       return !gq->ts_ring ? -ENOMEM : 0;
-}
-
 static void rswitch_desc_set_dptr(struct rswitch_desc *desc, dma_addr_t addr)
 {
        desc->dptrl = cpu_to_le32(lower_32_bits(addr));
@@ -533,6 +522,28 @@ static void rswitch_gwca_linkfix_free(struct rswitch_private *priv)
        gwca->linkfix_table = NULL;
 }
 
+static int rswitch_gwca_ts_queue_alloc(struct rswitch_private *priv)
+{
+       struct rswitch_gwca_queue *gq = &priv->gwca.ts_queue;
+       struct rswitch_ts_desc *desc;
+
+       gq->ring_size = TS_RING_SIZE;
+       gq->ts_ring = dma_alloc_coherent(&priv->pdev->dev,
+                                        sizeof(struct rswitch_ts_desc) *
+                                        (gq->ring_size + 1), &gq->ring_dma, GFP_KERNEL);
+
+       if (!gq->ts_ring)
+               return -ENOMEM;
+
+       rswitch_gwca_ts_queue_fill(priv, 0, TS_RING_SIZE);
+       desc = &gq->ts_ring[gq->ring_size];
+       desc->desc.die_dt = DT_LINKFIX;
+       rswitch_desc_set_dptr(&desc->desc, gq->ring_dma);
+       INIT_LIST_HEAD(&priv->gwca.ts_info_list);
+
+       return 0;
+}
+
 static struct rswitch_gwca_queue *rswitch_gwca_get(struct rswitch_private *priv)
 {
        struct rswitch_gwca_queue *gq;
@@ -1780,9 +1791,6 @@ static int rswitch_init(struct rswitch_private *priv)
        if (err < 0)
                goto err_ts_queue_alloc;
 
-       rswitch_gwca_ts_queue_fill(priv, 0, TS_RING_SIZE);
-       INIT_LIST_HEAD(&priv->gwca.ts_info_list);
-
        for (i = 0; i < RSWITCH_NUM_PORTS; i++) {
                err = rswitch_device_alloc(priv, i);
                if (err < 0) {
index fcea3ea..41b33a7 100644 (file)
@@ -301,6 +301,7 @@ int efx_probe_interrupts(struct efx_nic *efx)
                efx->tx_channel_offset = 0;
                efx->n_xdp_channels = 0;
                efx->xdp_channel_offset = efx->n_channels;
+               efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_BORROWED;
                rc = pci_enable_msi(efx->pci_dev);
                if (rc == 0) {
                        efx_get_channel(efx, 0)->irq = efx->pci_dev->irq;
@@ -322,6 +323,7 @@ int efx_probe_interrupts(struct efx_nic *efx)
                efx->tx_channel_offset = efx_separate_tx_channels ? 1 : 0;
                efx->n_xdp_channels = 0;
                efx->xdp_channel_offset = efx->n_channels;
+               efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_BORROWED;
                efx->legacy_irq = efx->pci_dev->irq;
        }
 
index 06ed749..1776f7f 100644 (file)
@@ -302,6 +302,7 @@ int efx_siena_probe_interrupts(struct efx_nic *efx)
                efx->tx_channel_offset = 0;
                efx->n_xdp_channels = 0;
                efx->xdp_channel_offset = efx->n_channels;
+               efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_BORROWED;
                rc = pci_enable_msi(efx->pci_dev);
                if (rc == 0) {
                        efx_get_channel(efx, 0)->irq = efx->pci_dev->irq;
@@ -323,6 +324,7 @@ int efx_siena_probe_interrupts(struct efx_nic *efx)
                efx->tx_channel_offset = efx_siena_separate_tx_channels ? 1 : 0;
                efx->n_xdp_channels = 0;
                efx->xdp_channel_offset = efx->n_channels;
+               efx->xdp_txq_queues_mode = EFX_XDP_TX_QUEUES_BORROWED;
                efx->legacy_irq = efx->pci_dev->irq;
        }
 
index 52cab9d..8751095 100644 (file)
@@ -3873,7 +3873,6 @@ irq_error:
 
        stmmac_hw_teardown(dev);
 init_error:
-       free_dma_desc_resources(priv, &priv->dma_conf);
        phylink_disconnect_phy(priv->phylink);
 init_phy_error:
        pm_runtime_put(priv->device);
@@ -3891,6 +3890,9 @@ static int stmmac_open(struct net_device *dev)
                return PTR_ERR(dma_conf);
 
        ret = __stmmac_open(dev, dma_conf);
+       if (ret)
+               free_dma_desc_resources(priv, dma_conf);
+
        kfree(dma_conf);
        return ret;
 }
@@ -5633,12 +5635,15 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
                stmmac_release(dev);
 
                ret = __stmmac_open(dev, dma_conf);
-               kfree(dma_conf);
                if (ret) {
+                       free_dma_desc_resources(priv, dma_conf);
+                       kfree(dma_conf);
                        netdev_err(priv->dev, "failed reopening the interface after MTU change\n");
                        return ret;
                }
 
+               kfree(dma_conf);
+
                stmmac_set_rx_mode(dev);
        }
 
index 11cbcd9..bebcfd5 100644 (file)
@@ -2068,7 +2068,7 @@ static int am65_cpsw_nuss_init_slave_ports(struct am65_cpsw_common *common)
                /* Initialize the Serdes PHY for the port */
                ret = am65_cpsw_init_serdes_phy(dev, port_np, port);
                if (ret)
-                       return ret;
+                       goto of_node_put;
 
                port->slave.mac_only =
                                of_property_read_bool(port_np, "ti,mac-only");
index 71712ea..d5b05e8 100644 (file)
@@ -102,6 +102,10 @@ static unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb,
 
        skb->dev = addr->master->dev;
        skb->skb_iif = skb->dev->ifindex;
+#if IS_ENABLED(CONFIG_IPV6)
+       if (addr->atype == IPVL_IPV6)
+               IP6CB(skb)->iif = skb->dev->ifindex;
+#endif
        len = skb->len + ETH_HLEN;
        ipvlan_count_rx(addr->master, len, true, false);
 out:
index 3427993..984dfa5 100644 (file)
@@ -3997,17 +3997,15 @@ static int macsec_add_dev(struct net_device *dev, sci_t sci, u8 icv_len)
                return -ENOMEM;
 
        secy->tx_sc.stats = netdev_alloc_pcpu_stats(struct pcpu_tx_sc_stats);
-       if (!secy->tx_sc.stats) {
-               free_percpu(macsec->stats);
+       if (!secy->tx_sc.stats)
                return -ENOMEM;
-       }
 
        secy->tx_sc.md_dst = metadata_dst_alloc(0, METADATA_MACSEC, GFP_KERNEL);
-       if (!secy->tx_sc.md_dst) {
-               free_percpu(secy->tx_sc.stats);
-               free_percpu(macsec->stats);
+       if (!secy->tx_sc.md_dst)
+               /* macsec and secy percpu stats will be freed when unregistering
+                * net_device in macsec_free_netdev()
+                */
                return -ENOMEM;
-       }
 
        if (sci == MACSEC_UNDEF_SCI)
                sci = dev_to_sci(dev, MACSEC_PORT_ES);
index b483111..5efdeb5 100644 (file)
@@ -188,6 +188,7 @@ static int phylink_interface_max_speed(phy_interface_t interface)
        case PHY_INTERFACE_MODE_RGMII_ID:
        case PHY_INTERFACE_MODE_RGMII:
        case PHY_INTERFACE_MODE_QSGMII:
+       case PHY_INTERFACE_MODE_QUSGMII:
        case PHY_INTERFACE_MODE_SGMII:
        case PHY_INTERFACE_MODE_GMII:
                return SPEED_1000;
@@ -204,7 +205,6 @@ static int phylink_interface_max_speed(phy_interface_t interface)
        case PHY_INTERFACE_MODE_10GBASER:
        case PHY_INTERFACE_MODE_10GKR:
        case PHY_INTERFACE_MODE_USXGMII:
-       case PHY_INTERFACE_MODE_QUSGMII:
                return SPEED_10000;
 
        case PHY_INTERFACE_MODE_25GBASER:
@@ -3299,6 +3299,41 @@ void phylink_decode_usxgmii_word(struct phylink_link_state *state,
 EXPORT_SYMBOL_GPL(phylink_decode_usxgmii_word);
 
 /**
+ * phylink_decode_usgmii_word() - decode the USGMII word from a MAC PCS
+ * @state: a pointer to a struct phylink_link_state.
+ * @lpa: a 16 bit value which stores the USGMII auto-negotiation word
+ *
+ * Helper for MAC PCS supporting the USGMII protocol and the auto-negotiation
+ * code word.  Decode the USGMII code word and populate the corresponding fields
+ * (speed, duplex) into the phylink_link_state structure. The structure for this
+ * word is the same as the USXGMII word, except it only supports speeds up to
+ * 1Gbps.
+ */
+static void phylink_decode_usgmii_word(struct phylink_link_state *state,
+                                      uint16_t lpa)
+{
+       switch (lpa & MDIO_USXGMII_SPD_MASK) {
+       case MDIO_USXGMII_10:
+               state->speed = SPEED_10;
+               break;
+       case MDIO_USXGMII_100:
+               state->speed = SPEED_100;
+               break;
+       case MDIO_USXGMII_1000:
+               state->speed = SPEED_1000;
+               break;
+       default:
+               state->link = false;
+               return;
+       }
+
+       if (lpa & MDIO_USXGMII_FULL_DUPLEX)
+               state->duplex = DUPLEX_FULL;
+       else
+               state->duplex = DUPLEX_HALF;
+}
+
+/**
  * phylink_mii_c22_pcs_decode_state() - Decode MAC PCS state from MII registers
  * @state: a pointer to a &struct phylink_link_state.
  * @bmsr: The value of the %MII_BMSR register
@@ -3335,9 +3370,11 @@ void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state,
 
        case PHY_INTERFACE_MODE_SGMII:
        case PHY_INTERFACE_MODE_QSGMII:
-       case PHY_INTERFACE_MODE_QUSGMII:
                phylink_decode_sgmii_word(state, lpa);
                break;
+       case PHY_INTERFACE_MODE_QUSGMII:
+               phylink_decode_usgmii_word(state, lpa);
+               break;
 
        default:
                state->link = false;
index f1865d0..2e7c7b0 100644 (file)
@@ -1220,7 +1220,9 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x05c6, 0x9080, 8)},
        {QMI_FIXED_INTF(0x05c6, 0x9083, 3)},
        {QMI_FIXED_INTF(0x05c6, 0x9084, 4)},
+       {QMI_QUIRK_SET_DTR(0x05c6, 0x9091, 2)}, /* Compal RXM-G1 */
        {QMI_FIXED_INTF(0x05c6, 0x90b2, 3)},    /* ublox R410M */
+       {QMI_QUIRK_SET_DTR(0x05c6, 0x90db, 2)}, /* Compal RXM-G1 */
        {QMI_FIXED_INTF(0x05c6, 0x920d, 0)},
        {QMI_FIXED_INTF(0x05c6, 0x920d, 5)},
        {QMI_QUIRK_SET_DTR(0x05c6, 0x9625, 4)}, /* YUGA CLM920-NC5 */
index d62a904..56326f3 100644 (file)
@@ -384,6 +384,9 @@ static int lapbeth_new_device(struct net_device *dev)
 
        ASSERT_RTNL();
 
+       if (dev->type != ARPHRD_ETHER)
+               return -EINVAL;
+
        ndev = alloc_netdev(sizeof(*lapbeth), "lapb%d", NET_NAME_UNKNOWN,
                            lapbeth_setup);
        if (!ndev)
index 23266d0..9a20468 100644 (file)
@@ -2692,7 +2692,7 @@ static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta,
 
        lq_sta = mvm_sta;
 
-       spin_lock(&lq_sta->pers.lock);
+       spin_lock_bh(&lq_sta->pers.lock);
        iwl_mvm_hwrate_to_tx_rate_v1(lq_sta->last_rate_n_flags,
                                     info->band, &info->control.rates[0]);
        info->control.rates[0].count = 1;
@@ -2707,7 +2707,7 @@ static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta,
                iwl_mvm_hwrate_to_tx_rate_v1(last_ucode_rate, info->band,
                                             &txrc->reported_rate);
        }
-       spin_unlock(&lq_sta->pers.lock);
+       spin_unlock_bh(&lq_sta->pers.lock);
 }
 
 static void *rs_drv_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
@@ -3264,11 +3264,11 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        /* If it's locked we are in middle of init flow
         * just wait for next tx status to update the lq_sta data
         */
-       if (!spin_trylock(&mvmsta->deflink.lq_sta.rs_drv.pers.lock))
+       if (!spin_trylock_bh(&mvmsta->deflink.lq_sta.rs_drv.pers.lock))
                return;
 
        __iwl_mvm_rs_tx_status(mvm, sta, tid, info, ndp);
-       spin_unlock(&mvmsta->deflink.lq_sta.rs_drv.pers.lock);
+       spin_unlock_bh(&mvmsta->deflink.lq_sta.rs_drv.pers.lock);
 }
 
 #ifdef CONFIG_MAC80211_DEBUGFS
@@ -4117,9 +4117,9 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm,
        } else {
                struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
 
-               spin_lock(&mvmsta->deflink.lq_sta.rs_drv.pers.lock);
+               spin_lock_bh(&mvmsta->deflink.lq_sta.rs_drv.pers.lock);
                rs_drv_rate_init(mvm, sta, band);
-               spin_unlock(&mvmsta->deflink.lq_sta.rs_drv.pers.lock);
+               spin_unlock_bh(&mvmsta->deflink.lq_sta.rs_drv.pers.lock);
        }
 }
 
index 2e01960..7feb643 100644 (file)
@@ -811,6 +811,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs)
                if (!fragment->target) {
                        pr_err("symbols in overlay, but not in live tree\n");
                        ret = -EINVAL;
+                       of_node_put(node);
                        goto err_out;
                }
 
index bc32662..2d93d0c 100644 (file)
@@ -489,7 +489,10 @@ struct hv_pcibus_device {
        struct fwnode_handle *fwnode;
        /* Protocol version negotiated with the host */
        enum pci_protocol_version_t protocol_version;
+
+       struct mutex state_lock;
        enum hv_pcibus_state state;
+
        struct hv_device *hdev;
        resource_size_t low_mmio_space;
        resource_size_t high_mmio_space;
@@ -545,19 +548,10 @@ struct hv_dr_state {
        struct hv_pcidev_description func[];
 };
 
-enum hv_pcichild_state {
-       hv_pcichild_init = 0,
-       hv_pcichild_requirements,
-       hv_pcichild_resourced,
-       hv_pcichild_ejecting,
-       hv_pcichild_maximum
-};
-
 struct hv_pci_dev {
        /* List protected by pci_rescan_remove_lock */
        struct list_head list_entry;
        refcount_t refs;
-       enum hv_pcichild_state state;
        struct pci_slot *pci_slot;
        struct hv_pcidev_description desc;
        bool reported_missing;
@@ -635,6 +629,11 @@ static void hv_arch_irq_unmask(struct irq_data *data)
        pbus = pdev->bus;
        hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata);
        int_desc = data->chip_data;
+       if (!int_desc) {
+               dev_warn(&hbus->hdev->device, "%s() can not unmask irq %u\n",
+                        __func__, data->irq);
+               return;
+       }
 
        local_irq_save(flags);
 
@@ -2004,12 +2003,6 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
                hv_pci_onchannelcallback(hbus);
                spin_unlock_irqrestore(&channel->sched_lock, flags);
 
-               if (hpdev->state == hv_pcichild_ejecting) {
-                       dev_err_once(&hbus->hdev->device,
-                                    "the device is being ejected\n");
-                       goto enable_tasklet;
-               }
-
                udelay(100);
        }
 
@@ -2615,6 +2608,8 @@ static void pci_devices_present_work(struct work_struct *work)
        if (!dr)
                return;
 
+       mutex_lock(&hbus->state_lock);
+
        /* First, mark all existing children as reported missing. */
        spin_lock_irqsave(&hbus->device_list_lock, flags);
        list_for_each_entry(hpdev, &hbus->children, list_entry) {
@@ -2696,6 +2691,8 @@ static void pci_devices_present_work(struct work_struct *work)
                break;
        }
 
+       mutex_unlock(&hbus->state_lock);
+
        kfree(dr);
 }
 
@@ -2844,7 +2841,7 @@ static void hv_eject_device_work(struct work_struct *work)
        hpdev = container_of(work, struct hv_pci_dev, wrk);
        hbus = hpdev->hbus;
 
-       WARN_ON(hpdev->state != hv_pcichild_ejecting);
+       mutex_lock(&hbus->state_lock);
 
        /*
         * Ejection can come before or after the PCI bus has been set up, so
@@ -2882,6 +2879,8 @@ static void hv_eject_device_work(struct work_struct *work)
        put_pcichild(hpdev);
        put_pcichild(hpdev);
        /* hpdev has been freed. Do not use it any more. */
+
+       mutex_unlock(&hbus->state_lock);
 }
 
 /**
@@ -2902,7 +2901,6 @@ static void hv_pci_eject_device(struct hv_pci_dev *hpdev)
                return;
        }
 
-       hpdev->state = hv_pcichild_ejecting;
        get_pcichild(hpdev);
        INIT_WORK(&hpdev->wrk, hv_eject_device_work);
        queue_work(hbus->wq, &hpdev->wrk);
@@ -3331,8 +3329,10 @@ static int hv_pci_enter_d0(struct hv_device *hdev)
        struct pci_bus_d0_entry *d0_entry;
        struct hv_pci_compl comp_pkt;
        struct pci_packet *pkt;
+       bool retry = true;
        int ret;
 
+enter_d0_retry:
        /*
         * Tell the host that the bus is ready to use, and moved into the
         * powered-on state.  This includes telling the host which region
@@ -3359,6 +3359,38 @@ static int hv_pci_enter_d0(struct hv_device *hdev)
        if (ret)
                goto exit;
 
+       /*
+        * In certain case (Kdump) the pci device of interest was
+        * not cleanly shut down and resource is still held on host
+        * side, the host could return invalid device status.
+        * We need to explicitly request host to release the resource
+        * and try to enter D0 again.
+        */
+       if (comp_pkt.completion_status < 0 && retry) {
+               retry = false;
+
+               dev_err(&hdev->device, "Retrying D0 Entry\n");
+
+               /*
+                * Hv_pci_bus_exit() calls hv_send_resource_released()
+                * to free up resources of its child devices.
+                * In the kdump kernel we need to set the
+                * wslot_res_allocated to 255 so it scans all child
+                * devices to release resources allocated in the
+                * normal kernel before panic happened.
+                */
+               hbus->wslot_res_allocated = 255;
+
+               ret = hv_pci_bus_exit(hdev, true);
+
+               if (ret == 0) {
+                       kfree(pkt);
+                       goto enter_d0_retry;
+               }
+               dev_err(&hdev->device,
+                       "Retrying D0 failed with ret %d\n", ret);
+       }
+
        if (comp_pkt.completion_status < 0) {
                dev_err(&hdev->device,
                        "PCI Pass-through VSP failed D0 Entry with status %x\n",
@@ -3401,6 +3433,24 @@ static int hv_pci_query_relations(struct hv_device *hdev)
        if (!ret)
                ret = wait_for_response(hdev, &comp);
 
+       /*
+        * In the case of fast device addition/removal, it's possible that
+        * vmbus_sendpacket() or wait_for_response() returns -ENODEV but we
+        * already got a PCI_BUS_RELATIONS* message from the host and the
+        * channel callback already scheduled a work to hbus->wq, which can be
+        * running pci_devices_present_work() -> survey_child_resources() ->
+        * complete(&hbus->survey_event), even after hv_pci_query_relations()
+        * exits and the stack variable 'comp' is no longer valid; as a result,
+        * a hang or a page fault may happen when the complete() calls
+        * raw_spin_lock_irqsave(). Flush hbus->wq before we exit from
+        * hv_pci_query_relations() to avoid the issues. Note: if 'ret' is
+        * -ENODEV, there can't be any more work item scheduled to hbus->wq
+        * after the flush_workqueue(): see vmbus_onoffer_rescind() ->
+        * vmbus_reset_channel_cb(), vmbus_rescind_cleanup() ->
+        * channel->rescind = true.
+        */
+       flush_workqueue(hbus->wq);
+
        return ret;
 }
 
@@ -3586,7 +3636,6 @@ static int hv_pci_probe(struct hv_device *hdev,
        struct hv_pcibus_device *hbus;
        u16 dom_req, dom;
        char *name;
-       bool enter_d0_retry = true;
        int ret;
 
        bridge = devm_pci_alloc_host_bridge(&hdev->device, 0);
@@ -3598,6 +3647,7 @@ static int hv_pci_probe(struct hv_device *hdev,
                return -ENOMEM;
 
        hbus->bridge = bridge;
+       mutex_init(&hbus->state_lock);
        hbus->state = hv_pcibus_init;
        hbus->wslot_res_allocated = -1;
 
@@ -3703,49 +3753,15 @@ static int hv_pci_probe(struct hv_device *hdev,
        if (ret)
                goto free_fwnode;
 
-retry:
        ret = hv_pci_query_relations(hdev);
        if (ret)
                goto free_irq_domain;
 
-       ret = hv_pci_enter_d0(hdev);
-       /*
-        * In certain case (Kdump) the pci device of interest was
-        * not cleanly shut down and resource is still held on host
-        * side, the host could return invalid device status.
-        * We need to explicitly request host to release the resource
-        * and try to enter D0 again.
-        * Since the hv_pci_bus_exit() call releases structures
-        * of all its child devices, we need to start the retry from
-        * hv_pci_query_relations() call, requesting host to send
-        * the synchronous child device relations message before this
-        * information is needed in hv_send_resources_allocated()
-        * call later.
-        */
-       if (ret == -EPROTO && enter_d0_retry) {
-               enter_d0_retry = false;
-
-               dev_err(&hdev->device, "Retrying D0 Entry\n");
-
-               /*
-                * Hv_pci_bus_exit() calls hv_send_resources_released()
-                * to free up resources of its child devices.
-                * In the kdump kernel we need to set the
-                * wslot_res_allocated to 255 so it scans all child
-                * devices to release resources allocated in the
-                * normal kernel before panic happened.
-                */
-               hbus->wslot_res_allocated = 255;
-               ret = hv_pci_bus_exit(hdev, true);
-
-               if (ret == 0)
-                       goto retry;
+       mutex_lock(&hbus->state_lock);
 
-               dev_err(&hdev->device,
-                       "Retrying D0 failed with ret %d\n", ret);
-       }
+       ret = hv_pci_enter_d0(hdev);
        if (ret)
-               goto free_irq_domain;
+               goto release_state_lock;
 
        ret = hv_pci_allocate_bridge_windows(hbus);
        if (ret)
@@ -3763,12 +3779,15 @@ retry:
        if (ret)
                goto free_windows;
 
+       mutex_unlock(&hbus->state_lock);
        return 0;
 
 free_windows:
        hv_pci_free_bridge_windows(hbus);
 exit_d0:
        (void) hv_pci_bus_exit(hdev, true);
+release_state_lock:
+       mutex_unlock(&hbus->state_lock);
 free_irq_domain:
        irq_domain_remove(hbus->irq_domain);
 free_fwnode:
@@ -4018,20 +4037,26 @@ static int hv_pci_resume(struct hv_device *hdev)
        if (ret)
                goto out;
 
+       mutex_lock(&hbus->state_lock);
+
        ret = hv_pci_enter_d0(hdev);
        if (ret)
-               goto out;
+               goto release_state_lock;
 
        ret = hv_send_resources_allocated(hdev);
        if (ret)
-               goto out;
+               goto release_state_lock;
 
        prepopulate_bars(hbus);
 
        hv_pci_restore_msi_state(hbus);
 
        hbus->state = hv_pcibus_installed;
+       mutex_unlock(&hbus->state_lock);
        return 0;
+
+release_state_lock:
+       mutex_unlock(&hbus->state_lock);
 out:
        vmbus_close(hdev->channel);
        return ret;
index b0a58c6..f3b280a 100644 (file)
@@ -1057,21 +1057,21 @@ static const struct rpmh_vreg_init_data pm8450_vreg_data[] = {
 };
 
 static const struct rpmh_vreg_init_data pm8550_vreg_data[] = {
-       RPMH_VREG("ldo1",   "ldo%s1",  &pmic5_pldo,    "vdd-l1-l4-l10"),
+       RPMH_VREG("ldo1",   "ldo%s1",  &pmic5_nldo515,    "vdd-l1-l4-l10"),
        RPMH_VREG("ldo2",   "ldo%s2",  &pmic5_pldo,    "vdd-l2-l13-l14"),
-       RPMH_VREG("ldo3",   "ldo%s3",  &pmic5_nldo,    "vdd-l3"),
-       RPMH_VREG("ldo4",   "ldo%s4",  &pmic5_nldo,    "vdd-l1-l4-l10"),
+       RPMH_VREG("ldo3",   "ldo%s3",  &pmic5_nldo515,    "vdd-l3"),
+       RPMH_VREG("ldo4",   "ldo%s4",  &pmic5_nldo515,    "vdd-l1-l4-l10"),
        RPMH_VREG("ldo5",   "ldo%s5",  &pmic5_pldo,    "vdd-l5-l16"),
-       RPMH_VREG("ldo6",   "ldo%s6",  &pmic5_pldo_lv, "vdd-l6-l7"),
-       RPMH_VREG("ldo7",   "ldo%s7",  &pmic5_pldo_lv, "vdd-l6-l7"),
-       RPMH_VREG("ldo8",   "ldo%s8",  &pmic5_pldo_lv, "vdd-l8-l9"),
+       RPMH_VREG("ldo6",   "ldo%s6",  &pmic5_pldo, "vdd-l6-l7"),
+       RPMH_VREG("ldo7",   "ldo%s7",  &pmic5_pldo, "vdd-l6-l7"),
+       RPMH_VREG("ldo8",   "ldo%s8",  &pmic5_pldo, "vdd-l8-l9"),
        RPMH_VREG("ldo9",   "ldo%s9",  &pmic5_pldo,    "vdd-l8-l9"),
-       RPMH_VREG("ldo10",  "ldo%s10", &pmic5_nldo,    "vdd-l1-l4-l10"),
-       RPMH_VREG("ldo11",  "ldo%s11", &pmic5_nldo,    "vdd-l11"),
+       RPMH_VREG("ldo10",  "ldo%s10", &pmic5_nldo515,    "vdd-l1-l4-l10"),
+       RPMH_VREG("ldo11",  "ldo%s11", &pmic5_nldo515,    "vdd-l11"),
        RPMH_VREG("ldo12",  "ldo%s12", &pmic5_pldo,    "vdd-l12"),
        RPMH_VREG("ldo13",  "ldo%s13", &pmic5_pldo,    "vdd-l2-l13-l14"),
        RPMH_VREG("ldo14",  "ldo%s14", &pmic5_pldo,    "vdd-l2-l13-l14"),
-       RPMH_VREG("ldo15",  "ldo%s15", &pmic5_pldo,    "vdd-l15"),
+       RPMH_VREG("ldo15",  "ldo%s15", &pmic5_nldo515,    "vdd-l15"),
        RPMH_VREG("ldo16",  "ldo%s16", &pmic5_pldo,    "vdd-l5-l16"),
        RPMH_VREG("ldo17",  "ldo%s17", &pmic5_pldo,    "vdd-l17"),
        RPMH_VREG("bob1",   "bob%s1",  &pmic5_bob,     "vdd-bob1"),
@@ -1086,9 +1086,9 @@ static const struct rpmh_vreg_init_data pm8550vs_vreg_data[] = {
        RPMH_VREG("smps4",  "smp%s4",  &pmic5_ftsmps525_lv, "vdd-s4"),
        RPMH_VREG("smps5",  "smp%s5",  &pmic5_ftsmps525_lv, "vdd-s5"),
        RPMH_VREG("smps6",  "smp%s6",  &pmic5_ftsmps525_mv, "vdd-s6"),
-       RPMH_VREG("ldo1",   "ldo%s1",  &pmic5_nldo,   "vdd-l1"),
-       RPMH_VREG("ldo2",   "ldo%s2",  &pmic5_nldo,   "vdd-l2"),
-       RPMH_VREG("ldo3",   "ldo%s3",  &pmic5_nldo,   "vdd-l3"),
+       RPMH_VREG("ldo1",   "ldo%s1",  &pmic5_nldo515,   "vdd-l1"),
+       RPMH_VREG("ldo2",   "ldo%s2",  &pmic5_nldo515,   "vdd-l2"),
+       RPMH_VREG("ldo3",   "ldo%s3",  &pmic5_nldo515,   "vdd-l3"),
        {}
 };
 
@@ -1101,9 +1101,9 @@ static const struct rpmh_vreg_init_data pm8550ve_vreg_data[] = {
        RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_lv, "vdd-s6"),
        RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525_lv, "vdd-s7"),
        RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525_lv, "vdd-s8"),
-       RPMH_VREG("ldo1",  "ldo%s1", &pmic5_nldo,   "vdd-l1"),
-       RPMH_VREG("ldo2",  "ldo%s2", &pmic5_nldo,   "vdd-l2"),
-       RPMH_VREG("ldo3",  "ldo%s3", &pmic5_nldo,   "vdd-l3"),
+       RPMH_VREG("ldo1",  "ldo%s1", &pmic5_nldo515,   "vdd-l1"),
+       RPMH_VREG("ldo2",  "ldo%s2", &pmic5_nldo515,   "vdd-l2"),
+       RPMH_VREG("ldo3",  "ldo%s3", &pmic5_nldo515,   "vdd-l3"),
        {}
 };
 
index 8acb9eb..c2096e4 100644 (file)
@@ -771,14 +771,6 @@ static int __init ism_init(void)
 
 static void __exit ism_exit(void)
 {
-       struct ism_dev *ism;
-
-       mutex_lock(&ism_dev_list.mutex);
-       list_for_each_entry(ism, &ism_dev_list.list, list) {
-               ism_dev_exit(ism);
-       }
-       mutex_unlock(&ism_dev_list.mutex);
-
        pci_unregister_driver(&ism_driver);
        debug_unregister(ism_debug_info);
 }
index 5e115e8..7c6efde 100644 (file)
@@ -1678,6 +1678,7 @@ struct aac_dev
        u32                     handle_pci_error;
        bool                    init_reset;
        u8                      soft_reset_support;
+       u8                      use_map_queue;
 };
 
 #define aac_adapter_interrupt(dev) \
index deb32c9..3f062e4 100644 (file)
@@ -223,8 +223,12 @@ int aac_fib_setup(struct aac_dev * dev)
 struct fib *aac_fib_alloc_tag(struct aac_dev *dev, struct scsi_cmnd *scmd)
 {
        struct fib *fibptr;
+       u32 blk_tag;
+       int i;
 
-       fibptr = &dev->fibs[scsi_cmd_to_rq(scmd)->tag];
+       blk_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
+       i = blk_mq_unique_tag_to_tag(blk_tag);
+       fibptr = &dev->fibs[i];
        /*
         *      Null out fields that depend on being zero at the start of
         *      each I/O
index 68f4dbc..c4a36c0 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <linux/compat.h>
 #include <linux/blkdev.h>
+#include <linux/blk-mq-pci.h>
 #include <linux/completion.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -504,6 +505,15 @@ common_config:
        return 0;
 }
 
+static void aac_map_queues(struct Scsi_Host *shost)
+{
+       struct aac_dev *aac = (struct aac_dev *)shost->hostdata;
+
+       blk_mq_pci_map_queues(&shost->tag_set.map[HCTX_TYPE_DEFAULT],
+                             aac->pdev, 0);
+       aac->use_map_queue = true;
+}
+
 /**
  *     aac_change_queue_depth          -       alter queue depths
  *     @sdev:  SCSI device we are considering
@@ -1488,6 +1498,7 @@ static const struct scsi_host_template aac_driver_template = {
        .bios_param                     = aac_biosparm,
        .shost_groups                   = aac_host_groups,
        .slave_configure                = aac_slave_configure,
+       .map_queues                     = aac_map_queues,
        .change_queue_depth             = aac_change_queue_depth,
        .sdev_groups                    = aac_dev_groups,
        .eh_abort_handler               = aac_eh_abort,
@@ -1775,6 +1786,8 @@ static int aac_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        shost->max_lun = AAC_MAX_LUN;
 
        pci_set_drvdata(pdev, shost);
+       shost->nr_hw_queues = aac->max_msix;
+       shost->host_tagset = 1;
 
        error = scsi_add_host(shost, &pdev->dev);
        if (error)
@@ -1906,6 +1919,7 @@ static void aac_remove_one(struct pci_dev *pdev)
        struct aac_dev *aac = (struct aac_dev *)shost->hostdata;
 
        aac_cancel_rescan_worker(aac);
+       aac->use_map_queue = false;
        scsi_remove_host(shost);
 
        __aac_shutdown(aac);
index 11ef582..61949f3 100644 (file)
@@ -493,6 +493,10 @@ static int aac_src_deliver_message(struct fib *fib)
 #endif
 
        u16 vector_no;
+       struct scsi_cmnd *scmd;
+       u32 blk_tag;
+       struct Scsi_Host *shost = dev->scsi_host_ptr;
+       struct blk_mq_queue_map *qmap;
 
        atomic_inc(&q->numpending);
 
@@ -505,8 +509,25 @@ static int aac_src_deliver_message(struct fib *fib)
                if ((dev->comm_interface == AAC_COMM_MESSAGE_TYPE3)
                        && dev->sa_firmware)
                        vector_no = aac_get_vector(dev);
-               else
-                       vector_no = fib->vector_no;
+               else {
+                       if (!fib->vector_no || !fib->callback_data) {
+                               if (shost && dev->use_map_queue) {
+                                       qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT];
+                                       vector_no = qmap->mq_map[raw_smp_processor_id()];
+                               }
+                               /*
+                                *      We hardcode the vector_no for
+                                *      reserved commands as a valid shost is
+                                *      absent during the init
+                                */
+                               else
+                                       vector_no = 0;
+                       } else {
+                               scmd = (struct scsi_cmnd *)fib->callback_data;
+                               blk_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
+                               vector_no = blk_mq_unique_tag_to_hwq(blk_tag);
+                       }
+               }
 
                if (native_hba) {
                        if (fib->flags & FIB_CONTEXT_FLAG_NATIVE_HBA_TMF) {
index 9a322a3..595dca9 100644 (file)
@@ -889,7 +889,7 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
                        struct lpfc_iocbq *piocbq)
 {
        uint32_t evt_req_id = 0;
-       uint32_t cmd;
+       u16 cmd;
        struct lpfc_dmabuf *dmabuf = NULL;
        struct lpfc_bsg_event *evt;
        struct event_data *evt_dat = NULL;
@@ -915,7 +915,7 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
 
        ct_req = (struct lpfc_sli_ct_request *)bdeBuf1->virt;
        evt_req_id = ct_req->FsType;
-       cmd = ct_req->CommandResponse.bits.CmdRsp;
+       cmd = be16_to_cpu(ct_req->CommandResponse.bits.CmdRsp);
 
        spin_lock_irqsave(&phba->ct_ev_lock, flags);
        list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
@@ -3186,8 +3186,8 @@ lpfc_bsg_diag_loopback_run(struct bsg_job *job)
                        ctreq->RevisionId.bits.InId = 0;
                        ctreq->FsType = SLI_CT_ELX_LOOPBACK;
                        ctreq->FsSubType = 0;
-                       ctreq->CommandResponse.bits.CmdRsp = ELX_LOOPBACK_DATA;
-                       ctreq->CommandResponse.bits.Size   = size;
+                       ctreq->CommandResponse.bits.CmdRsp = cpu_to_be16(ELX_LOOPBACK_DATA);
+                       ctreq->CommandResponse.bits.Size   = cpu_to_be16(size);
                        segment_offset = ELX_LOOPBACK_HEADER_SZ;
                } else
                        segment_offset = 0;
index e6bc622..659196a 100644 (file)
@@ -1567,6 +1567,8 @@ static int storvsc_device_configure(struct scsi_device *sdevice)
 {
        blk_queue_rq_timeout(sdevice->request_queue, (storvsc_timeout * HZ));
 
+       /* storvsc devices don't support MAINTENANCE_IN SCSI cmd */
+       sdevice->no_report_opcodes = 1;
        sdevice->no_write_same = 1;
 
        /*
index 6ddb2df..32449be 100644 (file)
@@ -1756,8 +1756,11 @@ static int cqspi_probe(struct platform_device *pdev)
                        cqspi->slow_sram = true;
 
                if (of_device_is_compatible(pdev->dev.of_node,
-                                           "xlnx,versal-ospi-1.0"))
-                       dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
+                                           "xlnx,versal-ospi-1.0")) {
+                       ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
+                       if (ret)
+                               goto probe_reset_failed;
+               }
        }
 
        ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0,
index 5f2aee6..15f5e9c 100644 (file)
@@ -274,7 +274,7 @@ static void dw_spi_elba_set_cs(struct spi_device *spi, bool enable)
         */
        spi_set_chipselect(spi, 0, 0);
        dw_spi_set_cs(spi, enable);
-       spi_get_chipselect(spi, cs);
+       spi_set_chipselect(spi, 0, cs);
 }
 
 static int dw_spi_elba_init(struct platform_device *pdev,
index 4339485..674cfe0 100644 (file)
@@ -1002,7 +1002,9 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr,
 static int dspi_setup(struct spi_device *spi)
 {
        struct fsl_dspi *dspi = spi_controller_get_devdata(spi->controller);
+       u32 period_ns = DIV_ROUND_UP(NSEC_PER_SEC, spi->max_speed_hz);
        unsigned char br = 0, pbr = 0, pcssck = 0, cssck = 0;
+       u32 quarter_period_ns = DIV_ROUND_UP(period_ns, 4);
        u32 cs_sck_delay = 0, sck_cs_delay = 0;
        struct fsl_dspi_platform_data *pdata;
        unsigned char pasc = 0, asc = 0;
@@ -1031,6 +1033,19 @@ static int dspi_setup(struct spi_device *spi)
                sck_cs_delay = pdata->sck_cs_delay;
        }
 
+       /* Since tCSC and tASC apply to continuous transfers too, avoid SCK
+        * glitches of half a cycle by never allowing tCSC + tASC to go below
+        * half a SCK period.
+        */
+       if (cs_sck_delay < quarter_period_ns)
+               cs_sck_delay = quarter_period_ns;
+       if (sck_cs_delay < quarter_period_ns)
+               sck_cs_delay = quarter_period_ns;
+
+       dev_dbg(&spi->dev,
+               "DSPI controller timing params: CS-to-SCK delay %u ns, SCK-to-CS delay %u ns\n",
+               cs_sck_delay, sck_cs_delay);
+
        clkrate = clk_get_rate(dspi->clk);
        hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate);
 
index 67a0a1f..044e48e 100644 (file)
@@ -6,4 +6,3 @@ TODO:
        - make driver self-contained instead of being split between staging and
          arch/mips/cavium-octeon.
 
-Contact: Aaro Koskinen <aaro.koskinen@iki.fi>
index 86adff2..687adc9 100644 (file)
@@ -504,6 +504,8 @@ target_setup_session(struct se_portal_group *tpg,
 
 free_sess:
        transport_free_session(sess);
+       return ERR_PTR(rc);
+
 free_cnt:
        target_free_cmd_counter(cmd_cnt);
        return ERR_PTR(rc);
index f99dc7e..db97499 100644 (file)
@@ -398,7 +398,7 @@ struct intel_soc_dts_sensors *intel_soc_dts_iosf_init(
        spin_lock_init(&sensors->intr_notify_lock);
        mutex_init(&sensors->dts_update_lock);
        sensors->intr_type = intr_type;
-       sensors->tj_max = tj_max;
+       sensors->tj_max = tj_max * 1000;
        if (intr_type == INTEL_SOC_DTS_INTERRUPT_NONE)
                notification = false;
        else
index 3bedecb..14bb6de 100644 (file)
@@ -192,9 +192,9 @@ static int dma_test_start_rings(struct dma_test *dt)
        }
 
        ret = tb_xdomain_enable_paths(dt->xd, dt->tx_hopid,
-                                     dt->tx_ring ? dt->tx_ring->hop : 0,
+                                     dt->tx_ring ? dt->tx_ring->hop : -1,
                                      dt->rx_hopid,
-                                     dt->rx_ring ? dt->rx_ring->hop : 0);
+                                     dt->rx_ring ? dt->rx_ring->hop : -1);
        if (ret) {
                dma_test_free_rings(dt);
                return ret;
@@ -218,9 +218,9 @@ static void dma_test_stop_rings(struct dma_test *dt)
                tb_ring_stop(dt->tx_ring);
 
        ret = tb_xdomain_disable_paths(dt->xd, dt->tx_hopid,
-                                      dt->tx_ring ? dt->tx_ring->hop : 0,
+                                      dt->tx_ring ? dt->tx_ring->hop : -1,
                                       dt->rx_hopid,
-                                      dt->rx_ring ? dt->rx_ring->hop : 0);
+                                      dt->rx_ring ? dt->rx_ring->hop : -1);
        if (ret)
                dev_warn(&dt->svc->dev, "failed to disable DMA paths\n");
 
index c0aee5d..e58beac 100644 (file)
@@ -56,9 +56,14 @@ static int ring_interrupt_index(const struct tb_ring *ring)
 
 static void nhi_mask_interrupt(struct tb_nhi *nhi, int mask, int ring)
 {
-       if (nhi->quirks & QUIRK_AUTO_CLEAR_INT)
-               return;
-       iowrite32(mask, nhi->iobase + REG_RING_INTERRUPT_MASK_CLEAR_BASE + ring);
+       if (nhi->quirks & QUIRK_AUTO_CLEAR_INT) {
+               u32 val;
+
+               val = ioread32(nhi->iobase + REG_RING_INTERRUPT_BASE + ring);
+               iowrite32(val & ~mask, nhi->iobase + REG_RING_INTERRUPT_BASE + ring);
+       } else {
+               iowrite32(mask, nhi->iobase + REG_RING_INTERRUPT_MASK_CLEAR_BASE + ring);
+       }
 }
 
 static void nhi_clear_interrupt(struct tb_nhi *nhi, int ring)
index 7bfbc9c..c1af712 100644 (file)
@@ -737,6 +737,7 @@ static void tb_scan_port(struct tb_port *port)
 {
        struct tb_cm *tcm = tb_priv(port->sw->tb);
        struct tb_port *upstream_port;
+       bool discovery = false;
        struct tb_switch *sw;
        int ret;
 
@@ -804,8 +805,10 @@ static void tb_scan_port(struct tb_port *port)
         * tunnels and know which switches were authorized already by
         * the boot firmware.
         */
-       if (!tcm->hotplug_active)
+       if (!tcm->hotplug_active) {
                dev_set_uevent_suppress(&sw->dev, true);
+               discovery = true;
+       }
 
        /*
         * At the moment Thunderbolt 2 and beyond (devices with LC) we
@@ -835,10 +838,14 @@ static void tb_scan_port(struct tb_port *port)
         * CL0s and CL1 are enabled and supported together.
         * Silently ignore CLx enabling in case CLx is not supported.
         */
-       ret = tb_switch_enable_clx(sw, TB_CL1);
-       if (ret && ret != -EOPNOTSUPP)
-               tb_sw_warn(sw, "failed to enable %s on upstream port\n",
-                          tb_switch_clx_name(TB_CL1));
+       if (discovery) {
+               tb_sw_dbg(sw, "discovery, not touching CL states\n");
+       } else {
+               ret = tb_switch_enable_clx(sw, TB_CL1);
+               if (ret && ret != -EOPNOTSUPP)
+                       tb_sw_warn(sw, "failed to enable %s on upstream port\n",
+                                  tb_switch_clx_name(TB_CL1));
+       }
 
        if (tb_switch_is_clx_enabled(sw, TB_CL1))
                /*
index 9099ae7..4f22267 100644 (file)
@@ -526,7 +526,7 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
         * Perform connection manager handshake between IN and OUT ports
         * before capabilities exchange can take place.
         */
-       ret = tb_dp_cm_handshake(in, out, 1500);
+       ret = tb_dp_cm_handshake(in, out, 3000);
        if (ret)
                return ret;
 
index 7486a2b..7fd30fc 100644 (file)
@@ -310,7 +310,7 @@ static const struct lpuart_soc_data ls1021a_data = {
 static const struct lpuart_soc_data ls1028a_data = {
        .devtype = LS1028A_LPUART,
        .iotype = UPIO_MEM32,
-       .rx_watermark = 1,
+       .rx_watermark = 0,
 };
 
 static struct lpuart_soc_data imx7ulp_data = {
index a58e927..f1387f1 100644 (file)
@@ -250,6 +250,7 @@ lqasc_err_int(int irq, void *_port)
        struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
 
        spin_lock_irqsave(&ltq_port->lock, flags);
+       __raw_writel(ASC_IRNCR_EIR, port->membase + LTQ_ASC_IRNCR);
        /* clear any pending interrupts */
        asc_update_bits(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE |
                ASCWHBSTATE_CLRROE, port->membase + LTQ_ASC_WHBSTATE);
index 7b2ce01..d68958e 100644 (file)
@@ -1929,6 +1929,11 @@ static int dwc3_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
        pm_runtime_dont_use_autosuspend(&pdev->dev);
        pm_runtime_put_noidle(&pdev->dev);
+       /*
+        * HACK: Clear the driver data, which is currently accessed by parent
+        * glue drivers, before allowing the parent to suspend.
+        */
+       platform_set_drvdata(pdev, NULL);
        pm_runtime_set_suspended(&pdev->dev);
 
        dwc3_free_event_buffers(dwc);
index 959fc92..79b22ab 100644 (file)
@@ -308,7 +308,16 @@ static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom)
 /* Only usable in contexts where the role can not change. */
 static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom)
 {
-       struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3);
+       struct dwc3 *dwc;
+
+       /*
+        * FIXME: Fix this layering violation.
+        */
+       dwc = platform_get_drvdata(qcom->dwc3);
+
+       /* Core driver may not have probed yet. */
+       if (!dwc)
+               return false;
 
        return dwc->xhci;
 }
index d831f5a..b78599d 100644 (file)
@@ -198,6 +198,7 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
        list_del(&req->list);
        req->remaining = 0;
        req->needs_extra_trb = false;
+       req->num_trbs = 0;
 
        if (req->request.status == -EINPROGRESS)
                req->request.status = status;
index 52e6d2e..83fd1de 100644 (file)
@@ -37,6 +37,14 @@ static const struct bus_type gadget_bus_type;
  * @vbus: for udcs who care about vbus status, this value is real vbus status;
  * for udcs who do not care about vbus status, this value is always true
  * @started: the UDC's started state. True if the UDC had started.
+ * @allow_connect: Indicates whether UDC is allowed to be pulled up.
+ * Set/cleared by gadget_(un)bind_driver() after gadget driver is bound or
+ * unbound.
+ * @connect_lock: protects udc->started, gadget->connect,
+ * gadget->allow_connect and gadget->deactivate. The routines
+ * usb_gadget_connect_locked(), usb_gadget_disconnect_locked(),
+ * usb_udc_connect_control_locked(), usb_gadget_udc_start_locked() and
+ * usb_gadget_udc_stop_locked() are called with this lock held.
  *
  * This represents the internal data structure which is used by the UDC-class
  * to hold information about udc driver and gadget together.
@@ -48,6 +56,9 @@ struct usb_udc {
        struct list_head                list;
        bool                            vbus;
        bool                            started;
+       bool                            allow_connect;
+       struct work_struct              vbus_work;
+       struct mutex                    connect_lock;
 };
 
 static struct class *udc_class;
@@ -687,17 +698,8 @@ out:
 }
 EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect);
 
-/**
- * usb_gadget_connect - software-controlled connect to USB host
- * @gadget:the peripheral being connected
- *
- * Enables the D+ (or potentially D-) pullup.  The host will start
- * enumerating this gadget when the pullup is active and a VBUS session
- * is active (the link is powered).
- *
- * Returns zero on success, else negative errno.
- */
-int usb_gadget_connect(struct usb_gadget *gadget)
+static int usb_gadget_connect_locked(struct usb_gadget *gadget)
+       __must_hold(&gadget->udc->connect_lock)
 {
        int ret = 0;
 
@@ -706,10 +708,12 @@ int usb_gadget_connect(struct usb_gadget *gadget)
                goto out;
        }
 
-       if (gadget->deactivated) {
+       if (gadget->deactivated || !gadget->udc->allow_connect || !gadget->udc->started) {
                /*
-                * If gadget is deactivated we only save new state.
-                * Gadget will be connected automatically after activation.
+                * If the gadget isn't usable (because it is deactivated,
+                * unbound, or not yet started), we only save the new state.
+                * The gadget will be connected automatically when it is
+                * activated/bound/started.
                 */
                gadget->connected = true;
                goto out;
@@ -724,22 +728,31 @@ out:
 
        return ret;
 }
-EXPORT_SYMBOL_GPL(usb_gadget_connect);
 
 /**
- * usb_gadget_disconnect - software-controlled disconnect from USB host
- * @gadget:the peripheral being disconnected
- *
- * Disables the D+ (or potentially D-) pullup, which the host may see
- * as a disconnect (when a VBUS session is active).  Not all systems
- * support software pullup controls.
+ * usb_gadget_connect - software-controlled connect to USB host
+ * @gadget:the peripheral being connected
  *
- * Following a successful disconnect, invoke the ->disconnect() callback
- * for the current gadget driver so that UDC drivers don't need to.
+ * Enables the D+ (or potentially D-) pullup.  The host will start
+ * enumerating this gadget when the pullup is active and a VBUS session
+ * is active (the link is powered).
  *
  * Returns zero on success, else negative errno.
  */
-int usb_gadget_disconnect(struct usb_gadget *gadget)
+int usb_gadget_connect(struct usb_gadget *gadget)
+{
+       int ret;
+
+       mutex_lock(&gadget->udc->connect_lock);
+       ret = usb_gadget_connect_locked(gadget);
+       mutex_unlock(&gadget->udc->connect_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_connect);
+
+static int usb_gadget_disconnect_locked(struct usb_gadget *gadget)
+       __must_hold(&gadget->udc->connect_lock)
 {
        int ret = 0;
 
@@ -751,7 +764,7 @@ int usb_gadget_disconnect(struct usb_gadget *gadget)
        if (!gadget->connected)
                goto out;
 
-       if (gadget->deactivated) {
+       if (gadget->deactivated || !gadget->udc->started) {
                /*
                 * If gadget is deactivated we only save new state.
                 * Gadget will stay disconnected after activation.
@@ -774,6 +787,30 @@ out:
 
        return ret;
 }
+
+/**
+ * usb_gadget_disconnect - software-controlled disconnect from USB host
+ * @gadget:the peripheral being disconnected
+ *
+ * Disables the D+ (or potentially D-) pullup, which the host may see
+ * as a disconnect (when a VBUS session is active).  Not all systems
+ * support software pullup controls.
+ *
+ * Following a successful disconnect, invoke the ->disconnect() callback
+ * for the current gadget driver so that UDC drivers don't need to.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_gadget_disconnect(struct usb_gadget *gadget)
+{
+       int ret;
+
+       mutex_lock(&gadget->udc->connect_lock);
+       ret = usb_gadget_disconnect_locked(gadget);
+       mutex_unlock(&gadget->udc->connect_lock);
+
+       return ret;
+}
 EXPORT_SYMBOL_GPL(usb_gadget_disconnect);
 
 /**
@@ -791,13 +828,14 @@ int usb_gadget_deactivate(struct usb_gadget *gadget)
 {
        int ret = 0;
 
+       mutex_lock(&gadget->udc->connect_lock);
        if (gadget->deactivated)
-               goto out;
+               goto unlock;
 
        if (gadget->connected) {
-               ret = usb_gadget_disconnect(gadget);
+               ret = usb_gadget_disconnect_locked(gadget);
                if (ret)
-                       goto out;
+                       goto unlock;
 
                /*
                 * If gadget was being connected before deactivation, we want
@@ -807,7 +845,8 @@ int usb_gadget_deactivate(struct usb_gadget *gadget)
        }
        gadget->deactivated = true;
 
-out:
+unlock:
+       mutex_unlock(&gadget->udc->connect_lock);
        trace_usb_gadget_deactivate(gadget, ret);
 
        return ret;
@@ -827,8 +866,9 @@ int usb_gadget_activate(struct usb_gadget *gadget)
 {
        int ret = 0;
 
+       mutex_lock(&gadget->udc->connect_lock);
        if (!gadget->deactivated)
-               goto out;
+               goto unlock;
 
        gadget->deactivated = false;
 
@@ -837,9 +877,11 @@ int usb_gadget_activate(struct usb_gadget *gadget)
         * while it was being deactivated, we call usb_gadget_connect().
         */
        if (gadget->connected)
-               ret = usb_gadget_connect(gadget);
+               ret = usb_gadget_connect_locked(gadget);
+       mutex_unlock(&gadget->udc->connect_lock);
 
-out:
+unlock:
+       mutex_unlock(&gadget->udc->connect_lock);
        trace_usb_gadget_activate(gadget, ret);
 
        return ret;
@@ -1078,12 +1120,22 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state);
 
 /* ------------------------------------------------------------------------- */
 
-static void usb_udc_connect_control(struct usb_udc *udc)
+/* Acquire connect_lock before calling this function. */
+static void usb_udc_connect_control_locked(struct usb_udc *udc) __must_hold(&udc->connect_lock)
 {
        if (udc->vbus)
-               usb_gadget_connect(udc->gadget);
+               usb_gadget_connect_locked(udc->gadget);
        else
-               usb_gadget_disconnect(udc->gadget);
+               usb_gadget_disconnect_locked(udc->gadget);
+}
+
+static void vbus_event_work(struct work_struct *work)
+{
+       struct usb_udc *udc = container_of(work, struct usb_udc, vbus_work);
+
+       mutex_lock(&udc->connect_lock);
+       usb_udc_connect_control_locked(udc);
+       mutex_unlock(&udc->connect_lock);
 }
 
 /**
@@ -1094,6 +1146,14 @@ static void usb_udc_connect_control(struct usb_udc *udc)
  *
  * The udc driver calls it when it wants to connect or disconnect gadget
  * according to vbus status.
+ *
+ * This function can be invoked from interrupt context by irq handlers of
+ * the gadget drivers, however, usb_udc_connect_control() has to run in
+ * non-atomic context due to the following:
+ * a. Some of the gadget driver implementations expect the ->pullup
+ * callback to be invoked in non-atomic context.
+ * b. usb_gadget_disconnect() acquires udc_lock which is a mutex.
+ * Hence offload invocation of usb_udc_connect_control() to workqueue.
  */
 void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
 {
@@ -1101,7 +1161,7 @@ void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
 
        if (udc) {
                udc->vbus = status;
-               usb_udc_connect_control(udc);
+               schedule_work(&udc->vbus_work);
        }
 }
 EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
@@ -1124,7 +1184,7 @@ void usb_gadget_udc_reset(struct usb_gadget *gadget,
 EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
 
 /**
- * usb_gadget_udc_start - tells usb device controller to start up
+ * usb_gadget_udc_start_locked - tells usb device controller to start up
  * @udc: The UDC to be started
  *
  * This call is issued by the UDC Class driver when it's about
@@ -1135,8 +1195,11 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
  * necessary to have it powered on.
  *
  * Returns zero on success, else negative errno.
+ *
+ * Caller should acquire connect_lock before invoking this function.
  */
-static inline int usb_gadget_udc_start(struct usb_udc *udc)
+static inline int usb_gadget_udc_start_locked(struct usb_udc *udc)
+       __must_hold(&udc->connect_lock)
 {
        int ret;
 
@@ -1153,7 +1216,7 @@ static inline int usb_gadget_udc_start(struct usb_udc *udc)
 }
 
 /**
- * usb_gadget_udc_stop - tells usb device controller we don't need it anymore
+ * usb_gadget_udc_stop_locked - tells usb device controller we don't need it anymore
  * @udc: The UDC to be stopped
  *
  * This call is issued by the UDC Class driver after calling
@@ -1162,8 +1225,11 @@ static inline int usb_gadget_udc_start(struct usb_udc *udc)
  * The details are implementation specific, but it can go as
  * far as powering off UDC completely and disable its data
  * line pullups.
+ *
+ * Caller should acquire connect lock before invoking this function.
  */
-static inline void usb_gadget_udc_stop(struct usb_udc *udc)
+static inline void usb_gadget_udc_stop_locked(struct usb_udc *udc)
+       __must_hold(&udc->connect_lock)
 {
        if (!udc->started) {
                dev_err(&udc->dev, "UDC had already stopped\n");
@@ -1322,12 +1388,14 @@ int usb_add_gadget(struct usb_gadget *gadget)
 
        udc->gadget = gadget;
        gadget->udc = udc;
+       mutex_init(&udc->connect_lock);
 
        udc->started = false;
 
        mutex_lock(&udc_lock);
        list_add_tail(&udc->list, &udc_list);
        mutex_unlock(&udc_lock);
+       INIT_WORK(&udc->vbus_work, vbus_event_work);
 
        ret = device_add(&udc->dev);
        if (ret)
@@ -1459,6 +1527,7 @@ void usb_del_gadget(struct usb_gadget *gadget)
        flush_work(&gadget->work);
        device_del(&gadget->dev);
        ida_free(&gadget_id_numbers, gadget->id_number);
+       cancel_work_sync(&udc->vbus_work);
        device_unregister(&udc->dev);
 }
 EXPORT_SYMBOL_GPL(usb_del_gadget);
@@ -1523,11 +1592,16 @@ static int gadget_bind_driver(struct device *dev)
        if (ret)
                goto err_bind;
 
-       ret = usb_gadget_udc_start(udc);
-       if (ret)
+       mutex_lock(&udc->connect_lock);
+       ret = usb_gadget_udc_start_locked(udc);
+       if (ret) {
+               mutex_unlock(&udc->connect_lock);
                goto err_start;
+       }
        usb_gadget_enable_async_callbacks(udc);
-       usb_udc_connect_control(udc);
+       udc->allow_connect = true;
+       usb_udc_connect_control_locked(udc);
+       mutex_unlock(&udc->connect_lock);
 
        kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
        return 0;
@@ -1558,12 +1632,16 @@ static void gadget_unbind_driver(struct device *dev)
 
        kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
 
-       usb_gadget_disconnect(gadget);
+       udc->allow_connect = false;
+       cancel_work_sync(&udc->vbus_work);
+       mutex_lock(&udc->connect_lock);
+       usb_gadget_disconnect_locked(gadget);
        usb_gadget_disable_async_callbacks(udc);
        if (gadget->irq)
                synchronize_irq(gadget->irq);
        udc->driver->unbind(gadget);
-       usb_gadget_udc_stop(udc);
+       usb_gadget_udc_stop_locked(udc);
+       mutex_unlock(&udc->connect_lock);
 
        mutex_lock(&udc_lock);
        driver->is_bound = false;
@@ -1649,11 +1727,15 @@ static ssize_t soft_connect_store(struct device *dev,
        }
 
        if (sysfs_streq(buf, "connect")) {
-               usb_gadget_udc_start(udc);
-               usb_gadget_connect(udc->gadget);
+               mutex_lock(&udc->connect_lock);
+               usb_gadget_udc_start_locked(udc);
+               usb_gadget_connect_locked(udc->gadget);
+               mutex_unlock(&udc->connect_lock);
        } else if (sysfs_streq(buf, "disconnect")) {
-               usb_gadget_disconnect(udc->gadget);
-               usb_gadget_udc_stop(udc);
+               mutex_lock(&udc->connect_lock);
+               usb_gadget_disconnect_locked(udc->gadget);
+               usb_gadget_udc_stop_locked(udc);
+               mutex_unlock(&udc->connect_lock);
        } else {
                dev_err(dev, "unsupported command '%s'\n", buf);
                ret = -EINVAL;
index aac8bc1..eb008e8 100644 (file)
@@ -2877,9 +2877,9 @@ static int renesas_usb3_probe(struct platform_device *pdev)
                struct rzv2m_usb3drd *ddata = dev_get_drvdata(pdev->dev.parent);
 
                usb3->drd_reg = ddata->reg;
-               ret = devm_request_irq(ddata->dev, ddata->drd_irq,
+               ret = devm_request_irq(&pdev->dev, ddata->drd_irq,
                                       renesas_usb3_otg_irq, 0,
-                                      dev_name(ddata->dev), usb3);
+                                      dev_name(&pdev->dev), usb3);
                if (ret < 0)
                        return ret;
        }
index 644a554..fd42e3a 100644 (file)
@@ -248,6 +248,8 @@ static void option_instat_callback(struct urb *urb);
 #define QUECTEL_VENDOR_ID                      0x2c7c
 /* These Quectel products use Quectel's vendor ID */
 #define QUECTEL_PRODUCT_EC21                   0x0121
+#define QUECTEL_PRODUCT_EM061K_LTA             0x0123
+#define QUECTEL_PRODUCT_EM061K_LMS             0x0124
 #define QUECTEL_PRODUCT_EC25                   0x0125
 #define QUECTEL_PRODUCT_EG91                   0x0191
 #define QUECTEL_PRODUCT_EG95                   0x0195
@@ -266,6 +268,8 @@ static void option_instat_callback(struct urb *urb);
 #define QUECTEL_PRODUCT_RM520N                 0x0801
 #define QUECTEL_PRODUCT_EC200U                 0x0901
 #define QUECTEL_PRODUCT_EC200S_CN              0x6002
+#define QUECTEL_PRODUCT_EM061K_LWW             0x6008
+#define QUECTEL_PRODUCT_EM061K_LCN             0x6009
 #define QUECTEL_PRODUCT_EC200T                 0x6026
 #define QUECTEL_PRODUCT_RM500K                 0x7001
 
@@ -1189,6 +1193,18 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0x00, 0x40) },
        { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0xff, 0x30) },
        { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0xff, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LCN, 0xff, 0xff, 0x30) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LCN, 0xff, 0x00, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LCN, 0xff, 0xff, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LMS, 0xff, 0xff, 0x30) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LMS, 0xff, 0x00, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LMS, 0xff, 0xff, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LTA, 0xff, 0xff, 0x30) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LTA, 0xff, 0x00, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LTA, 0xff, 0xff, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LWW, 0xff, 0xff, 0x30) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LWW, 0xff, 0x00, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LWW, 0xff, 0xff, 0x40) },
        { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0xff, 0xff),
          .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
        { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0, 0) },
index 0bcde1f..8cc66e4 100644 (file)
@@ -95,7 +95,7 @@ peak_current_show(struct device *dev, struct device_attribute *attr, char *buf)
 static ssize_t
 fast_role_swap_current_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
-       return sysfs_emit(buf, "%u\n", to_pdo(dev)->pdo >> PDO_FIXED_FRS_CURR_SHIFT) & 3;
+       return sysfs_emit(buf, "%u\n", (to_pdo(dev)->pdo >> PDO_FIXED_FRS_CURR_SHIFT) & 3);
 }
 static DEVICE_ATTR_RO(fast_role_swap_current);
 
index 2b472ec..b664ecb 100644 (file)
@@ -132,10 +132,8 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
        if (ret)
                return ret;
 
-       if (cci & UCSI_CCI_BUSY) {
-               ucsi->ops->async_write(ucsi, UCSI_CANCEL, NULL, 0);
-               return -EBUSY;
-       }
+       if (cmd != UCSI_CANCEL && cci & UCSI_CCI_BUSY)
+               return ucsi_exec_command(ucsi, UCSI_CANCEL);
 
        if (!(cci & UCSI_CCI_COMMAND_COMPLETE))
                return -EIO;
@@ -149,6 +147,11 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
                return ucsi_read_error(ucsi);
        }
 
+       if (cmd == UCSI_CANCEL && cci & UCSI_CCI_CANCEL_COMPLETE) {
+               ret = ucsi_acknowledge_command(ucsi);
+               return ret ? ret : -EBUSY;
+       }
+
        return UCSI_CCI_LENGTH(cci);
 }
 
index d1c7068..58452b8 100644 (file)
@@ -115,8 +115,8 @@ responded:
                }
        }
 
-       if (rxrpc_kernel_get_srtt(call->net->socket, call->rxcall, &rtt_us) &&
-           rtt_us < server->probe.rtt) {
+       rxrpc_kernel_get_srtt(call->net->socket, call->rxcall, &rtt_us);
+       if (rtt_us < server->probe.rtt) {
                server->probe.rtt = rtt_us;
                server->rtt = rtt_us;
                alist->preferred = index;
index c822d60..8750b99 100644 (file)
@@ -731,6 +731,7 @@ static int afs_writepages_region(struct address_space *mapping,
                         * (changing page->mapping to NULL), or even swizzled
                         * back from swapper_space to tmpfs file mapping
                         */
+try_again:
                        if (wbc->sync_mode != WB_SYNC_NONE) {
                                ret = folio_lock_killable(folio);
                                if (ret < 0) {
@@ -757,12 +758,14 @@ static int afs_writepages_region(struct address_space *mapping,
 #ifdef CONFIG_AFS_FSCACHE
                                        folio_wait_fscache(folio);
 #endif
-                               } else {
-                                       start += folio_size(folio);
+                                       goto try_again;
                                }
+
+                               start += folio_size(folio);
                                if (wbc->sync_mode == WB_SYNC_NONE) {
                                        if (skips >= 5 || need_resched()) {
                                                *_next = start;
+                                               folio_batch_release(&fbatch);
                                                _leave(" = 0 [%llx]", *_next);
                                                return 0;
                                        }
index 2b1b227..dabc79c 100644 (file)
@@ -242,7 +242,6 @@ static int btrfs_repair_eb_io_failure(const struct extent_buffer *eb,
                                      int mirror_num)
 {
        struct btrfs_fs_info *fs_info = eb->fs_info;
-       u64 start = eb->start;
        int i, num_pages = num_extent_pages(eb);
        int ret = 0;
 
@@ -251,12 +250,14 @@ static int btrfs_repair_eb_io_failure(const struct extent_buffer *eb,
 
        for (i = 0; i < num_pages; i++) {
                struct page *p = eb->pages[i];
+               u64 start = max_t(u64, eb->start, page_offset(p));
+               u64 end = min_t(u64, eb->start + eb->len, page_offset(p) + PAGE_SIZE);
+               u32 len = end - start;
 
-               ret = btrfs_repair_io_failure(fs_info, 0, start, PAGE_SIZE,
-                               start, p, start - page_offset(p), mirror_num);
+               ret = btrfs_repair_io_failure(fs_info, 0, start, len,
+                               start, p, offset_in_page(start), mirror_num);
                if (ret)
                        break;
-               start += PAGE_SIZE;
        }
 
        return ret;
@@ -995,13 +996,18 @@ int btrfs_global_root_insert(struct btrfs_root *root)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
        struct rb_node *tmp;
+       int ret = 0;
 
        write_lock(&fs_info->global_root_lock);
        tmp = rb_find_add(&root->rb_node, &fs_info->global_root_tree, global_root_cmp);
        write_unlock(&fs_info->global_root_lock);
-       ASSERT(!tmp);
 
-       return tmp ? -EEXIST : 0;
+       if (tmp) {
+               ret = -EEXIST;
+               btrfs_warn(fs_info, "global root %llu %llu already exists",
+                               root->root_key.objectid, root->root_key.offset);
+       }
+       return ret;
 }
 
 void btrfs_global_root_delete(struct btrfs_root *root)
@@ -2841,6 +2847,7 @@ static int __cold init_tree_roots(struct btrfs_fs_info *fs_info)
                        /* We can't trust the free space cache either */
                        btrfs_set_opt(fs_info->mount_opt, CLEAR_CACHE);
 
+                       btrfs_warn(fs_info, "try to load backup roots slot %d", i);
                        ret = read_backup_root(fs_info, i);
                        backup_index = ret;
                        if (ret < 0)
index 19c707b..7fcafcc 100644 (file)
@@ -1864,7 +1864,7 @@ static int can_nocow_file_extent(struct btrfs_path *path,
 
        ret = btrfs_cross_ref_exist(root, btrfs_ino(inode),
                                    key->offset - args->extent_offset,
-                                   args->disk_bytenr, false, path);
+                                   args->disk_bytenr, args->strict, path);
        WARN_ON_ONCE(ret > 0 && is_freespace_inode);
        if (ret != 0)
                goto out;
@@ -7264,7 +7264,7 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
 static int btrfs_get_blocks_direct_write(struct extent_map **map,
                                         struct inode *inode,
                                         struct btrfs_dio_data *dio_data,
-                                        u64 start, u64 len,
+                                        u64 start, u64 *lenp,
                                         unsigned int iomap_flags)
 {
        const bool nowait = (iomap_flags & IOMAP_NOWAIT);
@@ -7275,6 +7275,7 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map,
        struct btrfs_block_group *bg;
        bool can_nocow = false;
        bool space_reserved = false;
+       u64 len = *lenp;
        u64 prev_len;
        int ret = 0;
 
@@ -7345,15 +7346,19 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map,
                free_extent_map(em);
                *map = NULL;
 
-               if (nowait)
-                       return -EAGAIN;
+               if (nowait) {
+                       ret = -EAGAIN;
+                       goto out;
+               }
 
                /*
                 * If we could not allocate data space before locking the file
                 * range and we can't do a NOCOW write, then we have to fail.
                 */
-               if (!dio_data->data_space_reserved)
-                       return -ENOSPC;
+               if (!dio_data->data_space_reserved) {
+                       ret = -ENOSPC;
+                       goto out;
+               }
 
                /*
                 * We have to COW and we have already reserved data space before,
@@ -7394,6 +7399,7 @@ out:
                btrfs_delalloc_release_extents(BTRFS_I(inode), len);
                btrfs_delalloc_release_metadata(BTRFS_I(inode), len, true);
        }
+       *lenp = len;
        return ret;
 }
 
@@ -7570,7 +7576,7 @@ static int btrfs_dio_iomap_begin(struct inode *inode, loff_t start,
 
        if (write) {
                ret = btrfs_get_blocks_direct_write(&em, inode, dio_data,
-                                                   start, len, flags);
+                                                   start, &len, flags);
                if (ret < 0)
                        goto unlock_err;
                unlock_extents = true;
index 7c66651..bceaa8c 100644 (file)
@@ -134,8 +134,14 @@ struct scrub_stripe {
         * The errors hit during the initial read of the stripe.
         *
         * Would be utilized for error reporting and repair.
+        *
+        * The remaining init_nr_* records the number of errors hit, only used
+        * by error reporting.
         */
        unsigned long init_error_bitmap;
+       unsigned int init_nr_io_errors;
+       unsigned int init_nr_csum_errors;
+       unsigned int init_nr_meta_errors;
 
        /*
         * The following error bitmaps are all for the current status.
@@ -1003,12 +1009,9 @@ skip:
        sctx->stat.data_bytes_scrubbed += nr_data_sectors << fs_info->sectorsize_bits;
        sctx->stat.tree_bytes_scrubbed += nr_meta_sectors << fs_info->sectorsize_bits;
        sctx->stat.no_csum += nr_nodatacsum_sectors;
-       sctx->stat.read_errors +=
-               bitmap_weight(&stripe->io_error_bitmap, stripe->nr_sectors);
-       sctx->stat.csum_errors +=
-               bitmap_weight(&stripe->csum_error_bitmap, stripe->nr_sectors);
-       sctx->stat.verify_errors +=
-               bitmap_weight(&stripe->meta_error_bitmap, stripe->nr_sectors);
+       sctx->stat.read_errors += stripe->init_nr_io_errors;
+       sctx->stat.csum_errors += stripe->init_nr_csum_errors;
+       sctx->stat.verify_errors += stripe->init_nr_meta_errors;
        sctx->stat.uncorrectable_errors +=
                bitmap_weight(&stripe->error_bitmap, stripe->nr_sectors);
        sctx->stat.corrected_errors += nr_repaired_sectors;
@@ -1041,6 +1044,12 @@ static void scrub_stripe_read_repair_worker(struct work_struct *work)
        scrub_verify_one_stripe(stripe, stripe->extent_sector_bitmap);
        /* Save the initial failed bitmap for later repair and report usage. */
        stripe->init_error_bitmap = stripe->error_bitmap;
+       stripe->init_nr_io_errors = bitmap_weight(&stripe->io_error_bitmap,
+                                                 stripe->nr_sectors);
+       stripe->init_nr_csum_errors = bitmap_weight(&stripe->csum_error_bitmap,
+                                                   stripe->nr_sectors);
+       stripe->init_nr_meta_errors = bitmap_weight(&stripe->meta_error_bitmap,
+                                                   stripe->nr_sectors);
 
        if (bitmap_empty(&stripe->init_error_bitmap, stripe->nr_sectors))
                goto out;
@@ -1490,6 +1499,9 @@ static void scrub_stripe_reset_bitmaps(struct scrub_stripe *stripe)
 {
        stripe->extent_sector_bitmap = 0;
        stripe->init_error_bitmap = 0;
+       stripe->init_nr_io_errors = 0;
+       stripe->init_nr_csum_errors = 0;
+       stripe->init_nr_meta_errors = 0;
        stripe->error_bitmap = 0;
        stripe->io_error_bitmap = 0;
        stripe->csum_error_bitmap = 0;
@@ -1730,7 +1742,7 @@ static int flush_scrub_stripes(struct scrub_ctx *sctx)
                                break;
                        }
                }
-       } else {
+       } else if (!sctx->readonly) {
                for (int i = 0; i < nr_stripes; i++) {
                        unsigned long repaired;
 
@@ -2254,7 +2266,7 @@ next:
        }
 out:
        ret2 = flush_scrub_stripes(sctx);
-       if (!ret2)
+       if (!ret)
                ret = ret2;
        if (sctx->raid56_data_stripes) {
                for (int i = 0; i < nr_data_stripes(map); i++)
index ec18e22..efeb1a9 100644 (file)
@@ -1841,6 +1841,12 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
                btrfs_clear_sb_rdonly(sb);
 
                set_bit(BTRFS_FS_OPEN, &fs_info->flags);
+
+               /*
+                * If we've gone from readonly -> read/write, we need to get
+                * our sync/async discard lists in the right state.
+                */
+               btrfs_discard_resume(fs_info);
        }
 out:
        /*
index 841e799..e60beb1 100644 (file)
@@ -5975,12 +5975,12 @@ struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info,
        stripe_nr = offset >> BTRFS_STRIPE_LEN_SHIFT;
 
        /* stripe_offset is the offset of this block in its stripe */
-       stripe_offset = offset - (stripe_nr << BTRFS_STRIPE_LEN_SHIFT);
+       stripe_offset = offset - ((u64)stripe_nr << BTRFS_STRIPE_LEN_SHIFT);
 
        stripe_nr_end = round_up(offset + length, BTRFS_STRIPE_LEN) >>
                        BTRFS_STRIPE_LEN_SHIFT;
        stripe_cnt = stripe_nr_end - stripe_nr;
-       stripe_end_offset = (stripe_nr_end << BTRFS_STRIPE_LEN_SHIFT) -
+       stripe_end_offset = ((u64)stripe_nr_end << BTRFS_STRIPE_LEN_SHIFT) -
                            (offset + length);
        /*
         * after this, stripe_nr is the number of stripes on this
@@ -6023,7 +6023,7 @@ struct btrfs_discard_stripe *btrfs_map_discard(struct btrfs_fs_info *fs_info,
        for (i = 0; i < *num_stripes; i++) {
                stripes[i].physical =
                        map->stripes[stripe_index].physical +
-                       stripe_offset + (stripe_nr << BTRFS_STRIPE_LEN_SHIFT);
+                       stripe_offset + ((u64)stripe_nr << BTRFS_STRIPE_LEN_SHIFT);
                stripes[i].dev = map->stripes[stripe_index].dev;
 
                if (map->type & (BTRFS_BLOCK_GROUP_RAID0 |
@@ -6196,9 +6196,11 @@ static u64 btrfs_max_io_len(struct map_lookup *map, enum btrfs_map_op op,
                 * not ensured to be power of 2.
                 */
                *full_stripe_start =
-                       rounddown(*stripe_nr, nr_data_stripes(map)) <<
+                       (u64)rounddown(*stripe_nr, nr_data_stripes(map)) <<
                        BTRFS_STRIPE_LEN_SHIFT;
 
+               ASSERT(*full_stripe_start + full_stripe_len > offset);
+               ASSERT(*full_stripe_start <= offset);
                /*
                 * For writes to RAID56, allow to write a full stripe set, but
                 * no straddling of stripe sets.
@@ -6221,7 +6223,7 @@ static void set_io_stripe(struct btrfs_io_stripe *dst, const struct map_lookup *
 {
        dst->dev = map->stripes[stripe_index].dev;
        dst->physical = map->stripes[stripe_index].physical +
-                       stripe_offset + (stripe_nr << BTRFS_STRIPE_LEN_SHIFT);
+                       stripe_offset + ((u64)stripe_nr << BTRFS_STRIPE_LEN_SHIFT);
 }
 
 int __btrfs_map_block(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
index 9804834..266d45c 100644 (file)
@@ -1805,7 +1805,11 @@ static int ep_autoremove_wake_function(struct wait_queue_entry *wq_entry,
 {
        int ret = default_wake_function(wq_entry, mode, sync, key);
 
-       list_del_init(&wq_entry->entry);
+       /*
+        * Pairs with list_empty_careful in ep_poll, and ensures future loop
+        * iterations see the cause of this wakeup.
+        */
+       list_del_init_careful(&wq_entry->entry);
        return ret;
 }
 
index c1edde8..1f72f97 100644 (file)
@@ -324,17 +324,15 @@ static ext4_fsblk_t ext4_valid_block_bitmap_padding(struct super_block *sb,
 struct ext4_group_info *ext4_get_group_info(struct super_block *sb,
                                            ext4_group_t group)
 {
-        struct ext4_group_info **grp_info;
-        long indexv, indexh;
-
-        if (unlikely(group >= EXT4_SB(sb)->s_groups_count)) {
-                ext4_error(sb, "invalid group %u", group);
-                return NULL;
-        }
-        indexv = group >> (EXT4_DESC_PER_BLOCK_BITS(sb));
-        indexh = group & ((EXT4_DESC_PER_BLOCK(sb)) - 1);
-        grp_info = sbi_array_rcu_deref(EXT4_SB(sb), s_group_info, indexv);
-        return grp_info[indexh];
+       struct ext4_group_info **grp_info;
+       long indexv, indexh;
+
+       if (unlikely(group >= EXT4_SB(sb)->s_groups_count))
+               return NULL;
+       indexv = group >> (EXT4_DESC_PER_BLOCK_BITS(sb));
+       indexh = group & ((EXT4_DESC_PER_BLOCK(sb)) - 1);
+       grp_info = sbi_array_rcu_deref(EXT4_SB(sb), s_group_info, indexv);
+       return grp_info[indexh];
 }
 
 /*
@@ -886,7 +884,10 @@ static unsigned long ext4_bg_num_gdb_nometa(struct super_block *sb,
        if (!ext4_bg_has_super(sb, group))
                return 0;
 
-       return EXT4_SB(sb)->s_gdb_count;
+       if (ext4_has_feature_meta_bg(sb))
+               return le32_to_cpu(EXT4_SB(sb)->s_es->s_first_meta_bg);
+       else
+               return EXT4_SB(sb)->s_gdb_count;
 }
 
 /**
index e956f88..5710833 100644 (file)
@@ -285,6 +285,14 @@ void nilfs_btnode_abort_change_key(struct address_space *btnc,
        if (nbh == NULL) {      /* blocksize == pagesize */
                xa_erase_irq(&btnc->i_pages, newkey);
                unlock_page(ctxt->bh->b_page);
-       } else
-               brelse(nbh);
+       } else {
+               /*
+                * When canceling a buffer that a prepare operation has
+                * allocated to copy a node block to another location, use
+                * nilfs_btnode_delete() to initialize and release the buffer
+                * so that the buffer flags will not be in an inconsistent
+                * state when it is reallocated.
+                */
+               nilfs_btnode_delete(nbh);
+       }
 }
index dc359b5..2c6078a 100644 (file)
@@ -779,6 +779,15 @@ int nilfs_sufile_resize(struct inode *sufile, __u64 newnsegs)
                        goto out_header;
 
                sui->ncleansegs -= nsegs - newnsegs;
+
+               /*
+                * If the sufile is successfully truncated, immediately adjust
+                * the segment allocation space while locking the semaphore
+                * "mi_sem" so that nilfs_sufile_alloc() never allocates
+                * segments in the truncated space.
+                */
+               sui->allocmax = newnsegs - 1;
+               sui->allocmin = 0;
        }
 
        kaddr = kmap_atomic(header_bh->b_page);
index 2894152..0f06679 100644 (file)
@@ -405,6 +405,18 @@ unsigned long nilfs_nrsvsegs(struct the_nilfs *nilfs, unsigned long nsegs)
                                  100));
 }
 
+/**
+ * nilfs_max_segment_count - calculate the maximum number of segments
+ * @nilfs: nilfs object
+ */
+static u64 nilfs_max_segment_count(struct the_nilfs *nilfs)
+{
+       u64 max_count = U64_MAX;
+
+       do_div(max_count, nilfs->ns_blocks_per_segment);
+       return min_t(u64, max_count, ULONG_MAX);
+}
+
 void nilfs_set_nsegments(struct the_nilfs *nilfs, unsigned long nsegs)
 {
        nilfs->ns_nsegments = nsegs;
@@ -414,6 +426,8 @@ void nilfs_set_nsegments(struct the_nilfs *nilfs, unsigned long nsegs)
 static int nilfs_store_disk_layout(struct the_nilfs *nilfs,
                                   struct nilfs_super_block *sbp)
 {
+       u64 nsegments, nblocks;
+
        if (le32_to_cpu(sbp->s_rev_level) < NILFS_MIN_SUPP_REV) {
                nilfs_err(nilfs->ns_sb,
                          "unsupported revision (superblock rev.=%d.%d, current rev.=%d.%d). Please check the version of mkfs.nilfs(2).",
@@ -457,7 +471,34 @@ static int nilfs_store_disk_layout(struct the_nilfs *nilfs,
                return -EINVAL;
        }
 
-       nilfs_set_nsegments(nilfs, le64_to_cpu(sbp->s_nsegments));
+       nsegments = le64_to_cpu(sbp->s_nsegments);
+       if (nsegments > nilfs_max_segment_count(nilfs)) {
+               nilfs_err(nilfs->ns_sb,
+                         "segment count %llu exceeds upper limit (%llu segments)",
+                         (unsigned long long)nsegments,
+                         (unsigned long long)nilfs_max_segment_count(nilfs));
+               return -EINVAL;
+       }
+
+       nblocks = sb_bdev_nr_blocks(nilfs->ns_sb);
+       if (nblocks) {
+               u64 min_block_count = nsegments * nilfs->ns_blocks_per_segment;
+               /*
+                * To avoid failing to mount early device images without a
+                * second superblock, exclude that block count from the
+                * "min_block_count" calculation.
+                */
+
+               if (nblocks < min_block_count) {
+                       nilfs_err(nilfs->ns_sb,
+                                 "total number of segment blocks %llu exceeds device size (%llu blocks)",
+                                 (unsigned long long)min_block_count,
+                                 (unsigned long long)nblocks);
+                       return -EINVAL;
+               }
+       }
+
+       nilfs_set_nsegments(nilfs, nsegments);
        nilfs->ns_crc_seed = le32_to_cpu(sbp->s_crc_seed);
        return 0;
 }
index efb09de..b173c36 100644 (file)
@@ -2100,14 +2100,20 @@ static long ocfs2_fallocate(struct file *file, int mode, loff_t offset,
        struct ocfs2_space_resv sr;
        int change_size = 1;
        int cmd = OCFS2_IOC_RESVSP64;
+       int ret = 0;
 
        if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
                return -EOPNOTSUPP;
        if (!ocfs2_writes_unwritten_extents(osb))
                return -EOPNOTSUPP;
 
-       if (mode & FALLOC_FL_KEEP_SIZE)
+       if (mode & FALLOC_FL_KEEP_SIZE) {
                change_size = 0;
+       } else {
+               ret = inode_newsize_ok(inode, offset + len);
+               if (ret)
+                       return ret;
+       }
 
        if (mode & FALLOC_FL_PUNCH_HOLE)
                cmd = OCFS2_IOC_UNRESVSP64;
index 0b0e6a1..988d1c0 100644 (file)
@@ -952,8 +952,10 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb)
        for (type = 0; type < OCFS2_MAXQUOTAS; type++) {
                if (!sb_has_quota_loaded(sb, type))
                        continue;
-               oinfo = sb_dqinfo(sb, type)->dqi_priv;
-               cancel_delayed_work_sync(&oinfo->dqi_sync_work);
+               if (!sb_has_quota_suspended(sb, type)) {
+                       oinfo = sb_dqinfo(sb, type)->dqi_priv;
+                       cancel_delayed_work_sync(&oinfo->dqi_sync_work);
+               }
                inode = igrab(sb->s_dquot.files[type]);
                /* Turn off quotas. This will remove all dquot structures from
                 * memory and so they will be automatically synced to global
index 5034b86..b279f74 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/proc_fs.h>
 #include <linux/uaccess.h>
+#include <uapi/linux/ethtool.h>
 #include "cifspdu.h"
 #include "cifsglob.h"
 #include "cifsproto.h"
@@ -130,12 +131,14 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
        struct TCP_Server_Info *server = chan->server;
 
        seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx"
-                  "\n\t\tNumber of credits: %d Dialect 0x%x"
+                  "\n\t\tNumber of credits: %d,%d,%d Dialect 0x%x"
                   "\n\t\tTCP status: %d Instance: %d"
                   "\n\t\tLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d"
                   "\n\t\tIn Send: %d In MaxReq Wait: %d",
                   i+1, server->conn_id,
                   server->credits,
+                  server->echo_credits,
+                  server->oplock_credits,
                   server->dialect,
                   server->tcpStatus,
                   server->reconnect_instance,
@@ -146,18 +149,62 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
                   atomic_read(&server->num_waiters));
 }
 
+static inline const char *smb_speed_to_str(size_t bps)
+{
+       size_t mbps = bps / 1000 / 1000;
+
+       switch (mbps) {
+       case SPEED_10:
+               return "10Mbps";
+       case SPEED_100:
+               return "100Mbps";
+       case SPEED_1000:
+               return "1Gbps";
+       case SPEED_2500:
+               return "2.5Gbps";
+       case SPEED_5000:
+               return "5Gbps";
+       case SPEED_10000:
+               return "10Gbps";
+       case SPEED_14000:
+               return "14Gbps";
+       case SPEED_20000:
+               return "20Gbps";
+       case SPEED_25000:
+               return "25Gbps";
+       case SPEED_40000:
+               return "40Gbps";
+       case SPEED_50000:
+               return "50Gbps";
+       case SPEED_56000:
+               return "56Gbps";
+       case SPEED_100000:
+               return "100Gbps";
+       case SPEED_200000:
+               return "200Gbps";
+       case SPEED_400000:
+               return "400Gbps";
+       case SPEED_800000:
+               return "800Gbps";
+       default:
+               return "Unknown";
+       }
+}
+
 static void
 cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface)
 {
        struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
        struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
 
-       seq_printf(m, "\tSpeed: %zu bps\n", iface->speed);
+       seq_printf(m, "\tSpeed: %s\n", smb_speed_to_str(iface->speed));
        seq_puts(m, "\t\tCapabilities: ");
        if (iface->rdma_capable)
                seq_puts(m, "rdma ");
        if (iface->rss_capable)
                seq_puts(m, "rss ");
+       if (!iface->rdma_capable && !iface->rss_capable)
+               seq_puts(m, "None");
        seq_putc(m, '\n');
        if (iface->sockaddr.ss_family == AF_INET)
                seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr);
@@ -350,8 +397,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
                        atomic_read(&server->smbd_conn->mr_used_count));
 skip_rdma:
 #endif
-               seq_printf(m, "\nNumber of credits: %d Dialect 0x%x",
-                       server->credits,  server->dialect);
+               seq_printf(m, "\nNumber of credits: %d,%d,%d Dialect 0x%x",
+                       server->credits,
+                       server->echo_credits,
+                       server->oplock_credits,
+                       server->dialect);
                if (server->compress_algorithm == SMB3_COMPRESS_LZNT1)
                        seq_printf(m, " COMPRESS_LZNT1");
                else if (server->compress_algorithm == SMB3_COMPRESS_LZ77)
index 0d84bb1..b212a4e 100644 (file)
@@ -970,43 +970,6 @@ release_iface(struct kref *ref)
        kfree(iface);
 }
 
-/*
- * compare two interfaces a and b
- * return 0 if everything matches.
- * return 1 if a has higher link speed, or rdma capable, or rss capable
- * return -1 otherwise.
- */
-static inline int
-iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b)
-{
-       int cmp_ret = 0;
-
-       WARN_ON(!a || !b);
-       if (a->speed == b->speed) {
-               if (a->rdma_capable == b->rdma_capable) {
-                       if (a->rss_capable == b->rss_capable) {
-                               cmp_ret = memcmp(&a->sockaddr, &b->sockaddr,
-                                                sizeof(a->sockaddr));
-                               if (!cmp_ret)
-                                       return 0;
-                               else if (cmp_ret > 0)
-                                       return 1;
-                               else
-                                       return -1;
-                       } else if (a->rss_capable > b->rss_capable)
-                               return 1;
-                       else
-                               return -1;
-               } else if (a->rdma_capable > b->rdma_capable)
-                       return 1;
-               else
-                       return -1;
-       } else if (a->speed > b->speed)
-               return 1;
-       else
-               return -1;
-}
-
 struct cifs_chan {
        unsigned int in_reconnect : 1; /* if session setup in progress for this channel */
        struct TCP_Server_Info *server;
index c1c7049..d127ade 100644 (file)
@@ -87,6 +87,7 @@ extern int cifs_handle_standard(struct TCP_Server_Info *server,
                                struct mid_q_entry *mid);
 extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx);
 extern int smb3_parse_opt(const char *options, const char *key, char **val);
+extern int cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs);
 extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs);
 extern int cifs_discard_remaining_data(struct TCP_Server_Info *server);
 extern int cifs_call_async(struct TCP_Server_Info *server,
index 8e9a672..9d16626 100644 (file)
@@ -1288,6 +1288,56 @@ next_pdu:
        module_put_and_kthread_exit(0);
 }
 
+int
+cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs)
+{
+       struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr;
+       struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs;
+       struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
+       struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs;
+
+       switch (srcaddr->sa_family) {
+       case AF_UNSPEC:
+               switch (rhs->sa_family) {
+               case AF_UNSPEC:
+                       return 0;
+               case AF_INET:
+               case AF_INET6:
+                       return 1;
+               default:
+                       return -1;
+               }
+       case AF_INET: {
+               switch (rhs->sa_family) {
+               case AF_UNSPEC:
+                       return -1;
+               case AF_INET:
+                       return memcmp(saddr4, vaddr4,
+                                     sizeof(struct sockaddr_in));
+               case AF_INET6:
+                       return 1;
+               default:
+                       return -1;
+               }
+       }
+       case AF_INET6: {
+               switch (rhs->sa_family) {
+               case AF_UNSPEC:
+               case AF_INET:
+                       return -1;
+               case AF_INET6:
+                       return memcmp(saddr6,
+                                     vaddr6,
+                                     sizeof(struct sockaddr_in6));
+               default:
+                       return -1;
+               }
+       }
+       default:
+               return -1; /* don't expect to be here */
+       }
+}
+
 /*
  * Returns true if srcaddr isn't specified and rhs isn't specified, or
  * if srcaddr is specified and matches the IP address of the rhs argument
@@ -4086,16 +4136,17 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
 
        /* only send once per connect */
        spin_lock(&tcon->tc_lock);
+       if (tcon->status == TID_GOOD) {
+               spin_unlock(&tcon->tc_lock);
+               return 0;
+       }
+
        if (tcon->status != TID_NEW &&
            tcon->status != TID_NEED_TCON) {
                spin_unlock(&tcon->tc_lock);
                return -EHOSTDOWN;
        }
 
-       if (tcon->status == TID_GOOD) {
-               spin_unlock(&tcon->tc_lock);
-               return 0;
-       }
        tcon->status = TID_IN_TCON;
        spin_unlock(&tcon->tc_lock);
 
index 2f93bf8..2390b2f 100644 (file)
@@ -575,16 +575,17 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
 
        /* only send once per connect */
        spin_lock(&tcon->tc_lock);
+       if (tcon->status == TID_GOOD) {
+               spin_unlock(&tcon->tc_lock);
+               return 0;
+       }
+
        if (tcon->status != TID_NEW &&
            tcon->status != TID_NEED_TCON) {
                spin_unlock(&tcon->tc_lock);
                return -EHOSTDOWN;
        }
 
-       if (tcon->status == TID_GOOD) {
-               spin_unlock(&tcon->tc_lock);
-               return 0;
-       }
        tcon->status = TID_IN_TCON;
        spin_unlock(&tcon->tc_lock);
 
index df88b8c..0512833 100644 (file)
@@ -4942,9 +4942,13 @@ oplock_break_ack:
         * disconnected since oplock already released by the server
         */
        if (!oplock_break_cancelled) {
-               rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid,
+               /* check for server null since can race with kill_sb calling tree disconnect */
+               if (tcon->ses && tcon->ses->server) {
+                       rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid,
                                volatile_fid, net_fid, cinode);
-               cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
+                       cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
+               } else
+                       pr_warn_once("lease break not sent for unmounted share\n");
        }
 
        cifs_done_oplock_break(cinode);
index 6e3be58..a8bb9d0 100644 (file)
@@ -34,6 +34,8 @@ static int
 change_conf(struct TCP_Server_Info *server)
 {
        server->credits += server->echo_credits + server->oplock_credits;
+       if (server->credits > server->max_credits)
+               server->credits = server->max_credits;
        server->oplock_credits = server->echo_credits = 0;
        switch (server->credits) {
        case 0:
@@ -91,6 +93,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
                                            server->conn_id, server->hostname, *val,
                                            add, server->in_flight);
        }
+       WARN_ON_ONCE(server->in_flight == 0);
        server->in_flight--;
        if (server->in_flight == 0 &&
           ((optype & CIFS_OP_MASK) != CIFS_NEG_OP) &&
@@ -510,6 +513,43 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
        return rsize;
 }
 
+/*
+ * compare two interfaces a and b
+ * return 0 if everything matches.
+ * return 1 if a is rdma capable, or rss capable, or has higher link speed
+ * return -1 otherwise.
+ */
+static int
+iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b)
+{
+       int cmp_ret = 0;
+
+       WARN_ON(!a || !b);
+       if (a->rdma_capable == b->rdma_capable) {
+               if (a->rss_capable == b->rss_capable) {
+                       if (a->speed == b->speed) {
+                               cmp_ret = cifs_ipaddr_cmp((struct sockaddr *) &a->sockaddr,
+                                                         (struct sockaddr *) &b->sockaddr);
+                               if (!cmp_ret)
+                                       return 0;
+                               else if (cmp_ret > 0)
+                                       return 1;
+                               else
+                                       return -1;
+                       } else if (a->speed > b->speed)
+                               return 1;
+                       else
+                               return -1;
+               } else if (a->rss_capable > b->rss_capable)
+                       return 1;
+               else
+                       return -1;
+       } else if (a->rdma_capable > b->rdma_capable)
+               return 1;
+       else
+               return -1;
+}
+
 static int
 parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
                        size_t buf_len, struct cifs_ses *ses, bool in_mount)
index 7063b39..17fe212 100644 (file)
@@ -1305,7 +1305,12 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
        }
 
        /* enough to enable echos and oplocks and one max size write */
-       req->hdr.CreditRequest = cpu_to_le16(130);
+       if (server->credits >= server->max_credits)
+               req->hdr.CreditRequest = cpu_to_le16(0);
+       else
+               req->hdr.CreditRequest = cpu_to_le16(
+                       min_t(int, server->max_credits -
+                             server->credits, 130));
 
        /* only one of SMB2 signing flags may be set in SMB2 request */
        if (server->sign)
@@ -1899,7 +1904,12 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
        rqst.rq_nvec = 2;
 
        /* Need 64 for max size write so ask for more in case not there yet */
-       req->hdr.CreditRequest = cpu_to_le16(64);
+       if (server->credits >= server->max_credits)
+               req->hdr.CreditRequest = cpu_to_le16(0);
+       else
+               req->hdr.CreditRequest = cpu_to_le16(
+                       min_t(int, server->max_credits -
+                             server->credits, 64));
 
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags, &rsp_iov);
@@ -4227,6 +4237,7 @@ smb2_async_readv(struct cifs_readdata *rdata)
        struct TCP_Server_Info *server;
        struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
        unsigned int total_len;
+       int credit_request;
 
        cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
                 __func__, rdata->offset, rdata->bytes);
@@ -4258,7 +4269,13 @@ smb2_async_readv(struct cifs_readdata *rdata)
        if (rdata->credits.value > 0) {
                shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
                                                SMB2_MAX_BUFFER_SIZE));
-               shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8);
+               credit_request = le16_to_cpu(shdr->CreditCharge) + 8;
+               if (server->credits >= server->max_credits)
+                       shdr->CreditRequest = cpu_to_le16(0);
+               else
+                       shdr->CreditRequest = cpu_to_le16(
+                               min_t(int, server->max_credits -
+                                               server->credits, credit_request));
 
                rc = adjust_credits(server, &rdata->credits, rdata->bytes);
                if (rc)
@@ -4468,6 +4485,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
        unsigned int total_len;
        struct cifs_io_parms _io_parms;
        struct cifs_io_parms *io_parms = NULL;
+       int credit_request;
 
        if (!wdata->server)
                server = wdata->server = cifs_pick_channel(tcon->ses);
@@ -4572,7 +4590,13 @@ smb2_async_writev(struct cifs_writedata *wdata,
        if (wdata->credits.value > 0) {
                shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
                                                    SMB2_MAX_BUFFER_SIZE));
-               shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8);
+               credit_request = le16_to_cpu(shdr->CreditCharge) + 8;
+               if (server->credits >= server->max_credits)
+                       shdr->CreditRequest = cpu_to_le16(0);
+               else
+                       shdr->CreditRequest = cpu_to_le16(
+                               min_t(int, server->max_credits -
+                                               server->credits, credit_request));
 
                rc = adjust_credits(server, &wdata->credits, io_parms->length);
                if (rc)
index 24bdd5f..0474d0b 100644 (file)
@@ -55,7 +55,7 @@ alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
        temp->pid = current->pid;
        temp->command = cpu_to_le16(smb_buffer->Command);
        cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);
-       /*      do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
+       /* easier to use jiffies */
        /* when mid allocated can be before when sent */
        temp->when_alloc = jiffies;
        temp->server = server;
index f9b2e0f..ced7a9e 100644 (file)
@@ -185,24 +185,31 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
                goto send;
        }
 
-       if (conn->ops->check_user_session) {
-               rc = conn->ops->check_user_session(work);
-               if (rc < 0) {
-                       command = conn->ops->get_cmd_val(work);
-                       conn->ops->set_rsp_status(work,
-                                       STATUS_USER_SESSION_DELETED);
-                       goto send;
-               } else if (rc > 0) {
-                       rc = conn->ops->get_ksmbd_tcon(work);
+       do {
+               if (conn->ops->check_user_session) {
+                       rc = conn->ops->check_user_session(work);
                        if (rc < 0) {
-                               conn->ops->set_rsp_status(work,
-                                       STATUS_NETWORK_NAME_DELETED);
+                               if (rc == -EINVAL)
+                                       conn->ops->set_rsp_status(work,
+                                               STATUS_INVALID_PARAMETER);
+                               else
+                                       conn->ops->set_rsp_status(work,
+                                               STATUS_USER_SESSION_DELETED);
                                goto send;
+                       } else if (rc > 0) {
+                               rc = conn->ops->get_ksmbd_tcon(work);
+                               if (rc < 0) {
+                                       if (rc == -EINVAL)
+                                               conn->ops->set_rsp_status(work,
+                                                       STATUS_INVALID_PARAMETER);
+                                       else
+                                               conn->ops->set_rsp_status(work,
+                                                       STATUS_NETWORK_NAME_DELETED);
+                                       goto send;
+                               }
                        }
                }
-       }
 
-       do {
                rc = __process_request(work, conn, &command);
                if (rc == SERVER_HANDLER_ABORT)
                        break;
index 0ffe663..33b7e6c 100644 (file)
@@ -351,9 +351,16 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
        int command;
        __u32 clc_len;  /* calculated length */
        __u32 len = get_rfc1002_len(work->request_buf);
+       __u32 req_struct_size, next_cmd = le32_to_cpu(hdr->NextCommand);
 
-       if (le32_to_cpu(hdr->NextCommand) > 0)
-               len = le32_to_cpu(hdr->NextCommand);
+       if ((u64)work->next_smb2_rcv_hdr_off + next_cmd > len) {
+               pr_err("next command(%u) offset exceeds smb msg size\n",
+                               next_cmd);
+               return 1;
+       }
+
+       if (next_cmd > 0)
+               len = next_cmd;
        else if (work->next_smb2_rcv_hdr_off)
                len -= work->next_smb2_rcv_hdr_off;
 
@@ -373,17 +380,9 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
        }
 
        if (smb2_req_struct_sizes[command] != pdu->StructureSize2) {
-               if (command != SMB2_OPLOCK_BREAK_HE &&
-                   (hdr->Status == 0 || pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) {
-                       /* error packets have 9 byte structure size */
-                       ksmbd_debug(SMB,
-                                   "Illegal request size %u for command %d\n",
-                                   le16_to_cpu(pdu->StructureSize2), command);
-                       return 1;
-               } else if (command == SMB2_OPLOCK_BREAK_HE &&
-                          hdr->Status == 0 &&
-                          le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 &&
-                          le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) {
+               if (command == SMB2_OPLOCK_BREAK_HE &&
+                   le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 &&
+                   le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) {
                        /* special case for SMB2.1 lease break message */
                        ksmbd_debug(SMB,
                                    "Illegal request size %d for oplock break\n",
@@ -392,6 +391,14 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
                }
        }
 
+       req_struct_size = le16_to_cpu(pdu->StructureSize2) +
+               __SMB2_HEADER_STRUCTURE_SIZE;
+       if (command == SMB2_LOCK_HE)
+               req_struct_size -= sizeof(struct smb2_lock_element);
+
+       if (req_struct_size > len + 1)
+               return 1;
+
        if (smb2_calc_size(hdr, &clc_len))
                return 1;
 
index 25c0ba0..da1787c 100644 (file)
@@ -91,7 +91,6 @@ int smb2_get_ksmbd_tcon(struct ksmbd_work *work)
        unsigned int cmd = le16_to_cpu(req_hdr->Command);
        int tree_id;
 
-       work->tcon = NULL;
        if (cmd == SMB2_TREE_CONNECT_HE ||
            cmd ==  SMB2_CANCEL_HE ||
            cmd ==  SMB2_LOGOFF_HE) {
@@ -105,10 +104,28 @@ int smb2_get_ksmbd_tcon(struct ksmbd_work *work)
        }
 
        tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId);
+
+       /*
+        * If request is not the first in Compound request,
+        * Just validate tree id in header with work->tcon->id.
+        */
+       if (work->next_smb2_rcv_hdr_off) {
+               if (!work->tcon) {
+                       pr_err("The first operation in the compound does not have tcon\n");
+                       return -EINVAL;
+               }
+               if (work->tcon->id != tree_id) {
+                       pr_err("tree id(%u) is different with id(%u) in first operation\n",
+                                       tree_id, work->tcon->id);
+                       return -EINVAL;
+               }
+               return 1;
+       }
+
        work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id);
        if (!work->tcon) {
                pr_err("Invalid tid %d\n", tree_id);
-               return -EINVAL;
+               return -ENOENT;
        }
 
        return 1;
@@ -547,7 +564,6 @@ int smb2_check_user_session(struct ksmbd_work *work)
        unsigned int cmd = conn->ops->get_cmd_val(work);
        unsigned long long sess_id;
 
-       work->sess = NULL;
        /*
         * SMB2_ECHO, SMB2_NEGOTIATE, SMB2_SESSION_SETUP command do not
         * require a session id, so no need to validate user session's for
@@ -558,15 +574,33 @@ int smb2_check_user_session(struct ksmbd_work *work)
                return 0;
 
        if (!ksmbd_conn_good(conn))
-               return -EINVAL;
+               return -EIO;
 
        sess_id = le64_to_cpu(req_hdr->SessionId);
+
+       /*
+        * If request is not the first in Compound request,
+        * Just validate session id in header with work->sess->id.
+        */
+       if (work->next_smb2_rcv_hdr_off) {
+               if (!work->sess) {
+                       pr_err("The first operation in the compound does not have sess\n");
+                       return -EINVAL;
+               }
+               if (work->sess->id != sess_id) {
+                       pr_err("session id(%llu) is different with the first operation(%lld)\n",
+                                       sess_id, work->sess->id);
+                       return -EINVAL;
+               }
+               return 1;
+       }
+
        /* Check for validity of user session */
        work->sess = ksmbd_session_lookup_all(conn, sess_id);
        if (work->sess)
                return 1;
        ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id);
-       return -EINVAL;
+       return -ENOENT;
 }
 
 static void destroy_previous_session(struct ksmbd_conn *conn,
@@ -2249,7 +2283,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
                        /* delete the EA only when it exits */
                        if (rc > 0) {
                                rc = ksmbd_vfs_remove_xattr(idmap,
-                                                           path->dentry,
+                                                           path,
                                                            attr_name);
 
                                if (rc < 0) {
@@ -2263,8 +2297,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
                        /* if the EA doesn't exist, just do nothing. */
                        rc = 0;
                } else {
-                       rc = ksmbd_vfs_setxattr(idmap,
-                                               path->dentry, attr_name, value,
+                       rc = ksmbd_vfs_setxattr(idmap, path, attr_name, value,
                                                le16_to_cpu(eabuf->EaValueLength), 0);
                        if (rc < 0) {
                                ksmbd_debug(SMB,
@@ -2321,8 +2354,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path,
                return -EBADF;
        }
 
-       rc = ksmbd_vfs_setxattr(idmap, path->dentry,
-                               xattr_stream_name, NULL, 0, 0);
+       rc = ksmbd_vfs_setxattr(idmap, path, xattr_stream_name, NULL, 0, 0);
        if (rc < 0)
                pr_err("Failed to store XATTR stream name :%d\n", rc);
        return 0;
@@ -2350,7 +2382,7 @@ static int smb2_remove_smb_xattrs(const struct path *path)
                if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
                    !strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX,
                             STREAM_PREFIX_LEN)) {
-                       err = ksmbd_vfs_remove_xattr(idmap, path->dentry,
+                       err = ksmbd_vfs_remove_xattr(idmap, path,
                                                     name);
                        if (err)
                                ksmbd_debug(SMB, "remove xattr failed : %s\n",
@@ -2397,8 +2429,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path *
        da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME |
                XATTR_DOSINFO_ITIME;
 
-       rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_idmap(path->mnt),
-                                           path->dentry, &da);
+       rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_idmap(path->mnt), path, &da);
        if (rc)
                ksmbd_debug(SMB, "failed to store file attribute into xattr\n");
 }
@@ -2972,7 +3003,7 @@ int smb2_open(struct ksmbd_work *work)
                struct inode *inode = d_inode(path.dentry);
 
                posix_acl_rc = ksmbd_vfs_inherit_posix_acl(idmap,
-                                                          path.dentry,
+                                                          &path,
                                                           d_inode(path.dentry->d_parent));
                if (posix_acl_rc)
                        ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc);
@@ -2988,7 +3019,7 @@ int smb2_open(struct ksmbd_work *work)
                        if (rc) {
                                if (posix_acl_rc)
                                        ksmbd_vfs_set_init_posix_acl(idmap,
-                                                                    path.dentry);
+                                                                    &path);
 
                                if (test_share_config_flag(work->tcon->share_conf,
                                                           KSMBD_SHARE_FLAG_ACL_XATTR)) {
@@ -3028,7 +3059,7 @@ int smb2_open(struct ksmbd_work *work)
 
                                        rc = ksmbd_vfs_set_sd_xattr(conn,
                                                                    idmap,
-                                                                   path.dentry,
+                                                                   &path,
                                                                    pntsd,
                                                                    pntsd_size);
                                        kfree(pntsd);
@@ -5464,7 +5495,7 @@ static int smb2_rename(struct ksmbd_work *work,
                        goto out;
 
                rc = ksmbd_vfs_setxattr(file_mnt_idmap(fp->filp),
-                                       fp->filp->f_path.dentry,
+                                       &fp->filp->f_path,
                                        xattr_stream_name,
                                        NULL, 0, 0);
                if (rc < 0) {
@@ -5629,8 +5660,7 @@ static int set_file_basic_info(struct ksmbd_file *fp,
                da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME |
                        XATTR_DOSINFO_ITIME;
 
-               rc = ksmbd_vfs_set_dos_attrib_xattr(idmap,
-                                                   filp->f_path.dentry, &da);
+               rc = ksmbd_vfs_set_dos_attrib_xattr(idmap, &filp->f_path, &da);
                if (rc)
                        ksmbd_debug(SMB,
                                    "failed to restore file attribute in EA\n");
@@ -7485,7 +7515,7 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id,
 
                da.attr = le32_to_cpu(fp->f_ci->m_fattr);
                ret = ksmbd_vfs_set_dos_attrib_xattr(idmap,
-                                                    fp->filp->f_path.dentry, &da);
+                                                    &fp->filp->f_path, &da);
                if (ret)
                        fp->f_ci->m_fattr = old_fattr;
        }
index 0a5862a..ad919a4 100644 (file)
@@ -1162,8 +1162,7 @@ pass:
                        pntsd_size += sizeof(struct smb_acl) + nt_size;
                }
 
-               ksmbd_vfs_set_sd_xattr(conn, idmap,
-                                      path->dentry, pntsd, pntsd_size);
+               ksmbd_vfs_set_sd_xattr(conn, idmap, path, pntsd, pntsd_size);
                kfree(pntsd);
        }
 
@@ -1383,7 +1382,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
        newattrs.ia_valid |= ATTR_MODE;
        newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777);
 
-       ksmbd_vfs_remove_acl_xattrs(idmap, path->dentry);
+       ksmbd_vfs_remove_acl_xattrs(idmap, path);
        /* Update posix acls */
        if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) {
                rc = set_posix_acl(idmap, path->dentry,
@@ -1414,9 +1413,8 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
 
        if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) {
                /* Update WinACL in xattr */
-               ksmbd_vfs_remove_sd_xattrs(idmap, path->dentry);
-               ksmbd_vfs_set_sd_xattr(conn, idmap,
-                                      path->dentry, pntsd, ntsd_len);
+               ksmbd_vfs_remove_sd_xattrs(idmap, path);
+               ksmbd_vfs_set_sd_xattr(conn, idmap, path, pntsd, ntsd_len);
        }
 
 out:
index f9fb778..81489fd 100644 (file)
@@ -170,6 +170,10 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
                return err;
        }
 
+       err = mnt_want_write(path.mnt);
+       if (err)
+               goto out_err;
+
        mode |= S_IFREG;
        err = vfs_create(mnt_idmap(path.mnt), d_inode(path.dentry),
                         dentry, mode, true);
@@ -179,6 +183,9 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
        } else {
                pr_err("File(%s): creation failed (err:%d)\n", name, err);
        }
+       mnt_drop_write(path.mnt);
+
+out_err:
        done_path_create(&path, dentry);
        return err;
 }
@@ -209,30 +216,35 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
                return err;
        }
 
+       err = mnt_want_write(path.mnt);
+       if (err)
+               goto out_err2;
+
        idmap = mnt_idmap(path.mnt);
        mode |= S_IFDIR;
        err = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode);
-       if (err) {
-               goto out;
-       } else if (d_unhashed(dentry)) {
+       if (!err && d_unhashed(dentry)) {
                struct dentry *d;
 
                d = lookup_one(idmap, dentry->d_name.name, dentry->d_parent,
                               dentry->d_name.len);
                if (IS_ERR(d)) {
                        err = PTR_ERR(d);
-                       goto out;
+                       goto out_err1;
                }
                if (unlikely(d_is_negative(d))) {
                        dput(d);
                        err = -ENOENT;
-                       goto out;
+                       goto out_err1;
                }
 
                ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d));
                dput(d);
        }
-out:
+
+out_err1:
+       mnt_drop_write(path.mnt);
+out_err2:
        done_path_create(&path, dentry);
        if (err)
                pr_err("mkdir(%s): creation failed (err:%d)\n", name, err);
@@ -443,7 +455,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
        memcpy(&stream_buf[*pos], buf, count);
 
        err = ksmbd_vfs_setxattr(idmap,
-                                fp->filp->f_path.dentry,
+                                &fp->filp->f_path,
                                 fp->stream.name,
                                 (void *)stream_buf,
                                 size,
@@ -589,6 +601,10 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path)
                goto out_err;
        }
 
+       err = mnt_want_write(path->mnt);
+       if (err)
+               goto out_err;
+
        idmap = mnt_idmap(path->mnt);
        if (S_ISDIR(d_inode(path->dentry)->i_mode)) {
                err = vfs_rmdir(idmap, d_inode(parent), path->dentry);
@@ -599,6 +615,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path)
                if (err)
                        ksmbd_debug(VFS, "unlink failed, err %d\n", err);
        }
+       mnt_drop_write(path->mnt);
 
 out_err:
        ksmbd_revert_fsids(work);
@@ -644,11 +661,16 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname,
                goto out3;
        }
 
+       err = mnt_want_write(newpath.mnt);
+       if (err)
+               goto out3;
+
        err = vfs_link(oldpath.dentry, mnt_idmap(newpath.mnt),
                       d_inode(newpath.dentry),
                       dentry, NULL);
        if (err)
                ksmbd_debug(VFS, "vfs_link failed err %d\n", err);
+       mnt_drop_write(newpath.mnt);
 
 out3:
        done_path_create(&newpath, dentry);
@@ -694,6 +716,10 @@ retry:
                goto out2;
        }
 
+       err = mnt_want_write(old_path->mnt);
+       if (err)
+               goto out2;
+
        trap = lock_rename_child(old_child, new_path.dentry);
 
        old_parent = dget(old_child->d_parent);
@@ -757,6 +783,7 @@ out4:
 out3:
        dput(old_parent);
        unlock_rename(old_parent, new_path.dentry);
+       mnt_drop_write(old_path->mnt);
 out2:
        path_put(&new_path);
 
@@ -897,19 +924,24 @@ ssize_t ksmbd_vfs_getxattr(struct mnt_idmap *idmap,
  * Return:     0 on success, otherwise error
  */
 int ksmbd_vfs_setxattr(struct mnt_idmap *idmap,
-                      struct dentry *dentry, const char *attr_name,
+                      const struct path *path, const char *attr_name,
                       void *attr_value, size_t attr_size, int flags)
 {
        int err;
 
+       err = mnt_want_write(path->mnt);
+       if (err)
+               return err;
+
        err = vfs_setxattr(idmap,
-                          dentry,
+                          path->dentry,
                           attr_name,
                           attr_value,
                           attr_size,
                           flags);
        if (err)
                ksmbd_debug(VFS, "setxattr failed, err %d\n", err);
+       mnt_drop_write(path->mnt);
        return err;
 }
 
@@ -1013,9 +1045,18 @@ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
 }
 
 int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
-                          struct dentry *dentry, char *attr_name)
+                          const struct path *path, char *attr_name)
 {
-       return vfs_removexattr(idmap, dentry, attr_name);
+       int err;
+
+       err = mnt_want_write(path->mnt);
+       if (err)
+               return err;
+
+       err = vfs_removexattr(idmap, path->dentry, attr_name);
+       mnt_drop_write(path->mnt);
+
+       return err;
 }
 
 int ksmbd_vfs_unlink(struct file *filp)
@@ -1024,6 +1065,10 @@ int ksmbd_vfs_unlink(struct file *filp)
        struct dentry *dir, *dentry = filp->f_path.dentry;
        struct mnt_idmap *idmap = file_mnt_idmap(filp);
 
+       err = mnt_want_write(filp->f_path.mnt);
+       if (err)
+               return err;
+
        dir = dget_parent(dentry);
        err = ksmbd_vfs_lock_parent(dir, dentry);
        if (err)
@@ -1041,6 +1086,7 @@ int ksmbd_vfs_unlink(struct file *filp)
                ksmbd_debug(VFS, "failed to delete, err %d\n", err);
 out:
        dput(dir);
+       mnt_drop_write(filp->f_path.mnt);
 
        return err;
 }
@@ -1244,13 +1290,13 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
 }
 
 int ksmbd_vfs_remove_acl_xattrs(struct mnt_idmap *idmap,
-                               struct dentry *dentry)
+                               const struct path *path)
 {
        char *name, *xattr_list = NULL;
        ssize_t xattr_list_len;
        int err = 0;
 
-       xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list);
+       xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
        if (xattr_list_len < 0) {
                goto out;
        } else if (!xattr_list_len) {
@@ -1258,6 +1304,10 @@ int ksmbd_vfs_remove_acl_xattrs(struct mnt_idmap *idmap,
                goto out;
        }
 
+       err = mnt_want_write(path->mnt);
+       if (err)
+               goto out;
+
        for (name = xattr_list; name - xattr_list < xattr_list_len;
             name += strlen(name) + 1) {
                ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name));
@@ -1266,25 +1316,26 @@ int ksmbd_vfs_remove_acl_xattrs(struct mnt_idmap *idmap,
                             sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) ||
                    !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT,
                             sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) {
-                       err = vfs_remove_acl(idmap, dentry, name);
+                       err = vfs_remove_acl(idmap, path->dentry, name);
                        if (err)
                                ksmbd_debug(SMB,
                                            "remove acl xattr failed : %s\n", name);
                }
        }
+       mnt_drop_write(path->mnt);
+
 out:
        kvfree(xattr_list);
        return err;
 }
 
-int ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap,
-                              struct dentry *dentry)
+int ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap, const struct path *path)
 {
        char *name, *xattr_list = NULL;
        ssize_t xattr_list_len;
        int err = 0;
 
-       xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list);
+       xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
        if (xattr_list_len < 0) {
                goto out;
        } else if (!xattr_list_len) {
@@ -1297,7 +1348,7 @@ int ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap,
                ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name));
 
                if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) {
-                       err = ksmbd_vfs_remove_xattr(idmap, dentry, name);
+                       err = ksmbd_vfs_remove_xattr(idmap, path, name);
                        if (err)
                                ksmbd_debug(SMB, "remove xattr failed : %s\n", name);
                }
@@ -1374,13 +1425,14 @@ out:
 
 int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
                           struct mnt_idmap *idmap,
-                          struct dentry *dentry,
+                          const struct path *path,
                           struct smb_ntsd *pntsd, int len)
 {
        int rc;
        struct ndr sd_ndr = {0}, acl_ndr = {0};
        struct xattr_ntacl acl = {0};
        struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL;
+       struct dentry *dentry = path->dentry;
        struct inode *inode = d_inode(dentry);
 
        acl.version = 4;
@@ -1432,7 +1484,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
                goto out;
        }
 
-       rc = ksmbd_vfs_setxattr(idmap, dentry,
+       rc = ksmbd_vfs_setxattr(idmap, path,
                                XATTR_NAME_SD, sd_ndr.data,
                                sd_ndr.offset, 0);
        if (rc < 0)
@@ -1522,7 +1574,7 @@ free_n_data:
 }
 
 int ksmbd_vfs_set_dos_attrib_xattr(struct mnt_idmap *idmap,
-                                  struct dentry *dentry,
+                                  const struct path *path,
                                   struct xattr_dos_attrib *da)
 {
        struct ndr n;
@@ -1532,7 +1584,7 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct mnt_idmap *idmap,
        if (err)
                return err;
 
-       err = ksmbd_vfs_setxattr(idmap, dentry, XATTR_NAME_DOS_ATTRIBUTE,
+       err = ksmbd_vfs_setxattr(idmap, path, XATTR_NAME_DOS_ATTRIBUTE,
                                 (void *)n.data, n.offset, 0);
        if (err)
                ksmbd_debug(SMB, "failed to store dos attribute in xattr\n");
@@ -1769,10 +1821,11 @@ void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock)
 }
 
 int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap,
-                                struct dentry *dentry)
+                                struct path *path)
 {
        struct posix_acl_state acl_state;
        struct posix_acl *acls;
+       struct dentry *dentry = path->dentry;
        struct inode *inode = d_inode(dentry);
        int rc;
 
@@ -1802,6 +1855,11 @@ int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap,
                return -ENOMEM;
        }
        posix_state_to_acl(&acl_state, acls->a_entries);
+
+       rc = mnt_want_write(path->mnt);
+       if (rc)
+               goto out_err;
+
        rc = set_posix_acl(idmap, dentry, ACL_TYPE_ACCESS, acls);
        if (rc < 0)
                ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n",
@@ -1813,16 +1871,20 @@ int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap,
                        ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n",
                                    rc);
        }
+       mnt_drop_write(path->mnt);
+
+out_err:
        free_acl_state(&acl_state);
        posix_acl_release(acls);
        return rc;
 }
 
 int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
-                               struct dentry *dentry, struct inode *parent_inode)
+                               struct path *path, struct inode *parent_inode)
 {
        struct posix_acl *acls;
        struct posix_acl_entry *pace;
+       struct dentry *dentry = path->dentry;
        struct inode *inode = d_inode(dentry);
        int rc, i;
 
@@ -1841,6 +1903,10 @@ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
                }
        }
 
+       rc = mnt_want_write(path->mnt);
+       if (rc)
+               goto out_err;
+
        rc = set_posix_acl(idmap, dentry, ACL_TYPE_ACCESS, acls);
        if (rc < 0)
                ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n",
@@ -1852,6 +1918,9 @@ int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
                        ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n",
                                    rc);
        }
+       mnt_drop_write(path->mnt);
+
+out_err:
        posix_acl_release(acls);
        return rc;
 }
index a4ae89f..8c0931d 100644 (file)
@@ -108,12 +108,12 @@ ssize_t ksmbd_vfs_casexattr_len(struct mnt_idmap *idmap,
                                struct dentry *dentry, char *attr_name,
                                int attr_name_len);
 int ksmbd_vfs_setxattr(struct mnt_idmap *idmap,
-                      struct dentry *dentry, const char *attr_name,
+                      const struct path *path, const char *attr_name,
                       void *attr_value, size_t attr_size, int flags);
 int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
                                size_t *xattr_stream_name_size, int s_type);
 int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
-                          struct dentry *dentry, char *attr_name);
+                          const struct path *path, char *attr_name);
 int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
                               unsigned int flags, struct path *path,
                               bool caseless);
@@ -139,26 +139,25 @@ void ksmbd_vfs_posix_lock_wait(struct file_lock *flock);
 int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout);
 void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock);
 int ksmbd_vfs_remove_acl_xattrs(struct mnt_idmap *idmap,
-                               struct dentry *dentry);
-int ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap,
-                              struct dentry *dentry);
+                               const struct path *path);
+int ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap, const struct path *path);
 int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
                           struct mnt_idmap *idmap,
-                          struct dentry *dentry,
+                          const struct path *path,
                           struct smb_ntsd *pntsd, int len);
 int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
                           struct mnt_idmap *idmap,
                           struct dentry *dentry,
                           struct smb_ntsd **pntsd);
 int ksmbd_vfs_set_dos_attrib_xattr(struct mnt_idmap *idmap,
-                                  struct dentry *dentry,
+                                  const struct path *path,
                                   struct xattr_dos_attrib *da);
 int ksmbd_vfs_get_dos_attrib_xattr(struct mnt_idmap *idmap,
                                   struct dentry *dentry,
                                   struct xattr_dos_attrib *da);
 int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap,
-                                struct dentry *dentry);
+                                struct path *path);
 int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
-                               struct dentry *dentry,
+                               struct path *path,
                                struct inode *parent_inode);
 #endif /* __KSMBD_VFS_H__ */
index 2d0138e..f41f8d6 100644 (file)
@@ -252,7 +252,7 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
        if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) {
                ci->m_flags &= ~S_DEL_ON_CLS_STREAM;
                err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp),
-                                            filp->f_path.dentry,
+                                            &filp->f_path,
                                             fp->stream.name);
                if (err)
                        pr_err("remove xattr failed : %s\n",
index 0fd96d6..4e800bb 100644 (file)
@@ -1332,6 +1332,7 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
        bool basic_ioctls;
        unsigned long start, end, vma_end;
        struct vma_iterator vmi;
+       pgoff_t pgoff;
 
        user_uffdio_register = (struct uffdio_register __user *) arg;
 
@@ -1459,6 +1460,8 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
 
        vma_iter_set(&vmi, start);
        prev = vma_prev(&vmi);
+       if (vma->vm_start < start)
+               prev = vma;
 
        ret = 0;
        for_each_vma_range(vmi, vma, end) {
@@ -1482,8 +1485,9 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
                vma_end = min(end, vma->vm_end);
 
                new_flags = (vma->vm_flags & ~__VM_UFFD_FLAGS) | vm_flags;
+               pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
                prev = vma_merge(&vmi, mm, prev, start, vma_end, new_flags,
-                                vma->anon_vma, vma->vm_file, vma->vm_pgoff,
+                                vma->anon_vma, vma->vm_file, pgoff,
                                 vma_policy(vma),
                                 ((struct vm_userfaultfd_ctx){ ctx }),
                                 anon_vma_name(vma));
@@ -1563,6 +1567,7 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
        unsigned long start, end, vma_end;
        const void __user *buf = (void __user *)arg;
        struct vma_iterator vmi;
+       pgoff_t pgoff;
 
        ret = -EFAULT;
        if (copy_from_user(&uffdio_unregister, buf, sizeof(uffdio_unregister)))
@@ -1625,6 +1630,9 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
 
        vma_iter_set(&vmi, start);
        prev = vma_prev(&vmi);
+       if (vma->vm_start < start)
+               prev = vma;
+
        ret = 0;
        for_each_vma_range(vmi, vma, end) {
                cond_resched();
@@ -1662,8 +1670,9 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
                        uffd_wp_range(vma, start, vma_end - start, false);
 
                new_flags = vma->vm_flags & ~__VM_UFFD_FLAGS;
+               pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
                prev = vma_merge(&vmi, mm, prev, start, vma_end, new_flags,
-                                vma->anon_vma, vma->vm_file, vma->vm_pgoff,
+                                vma->anon_vma, vma->vm_file, pgoff,
                                 vma_policy(vma),
                                 NULL_VM_UFFD_CTX, anon_vma_name(vma));
                if (prev) {
index a6affc0..c941d99 100644 (file)
@@ -289,6 +289,8 @@ struct acpi_dep_data {
        acpi_handle supplier;
        acpi_handle consumer;
        bool honor_dep;
+       bool met;
+       bool free_when_met;
 };
 
 /* Performance Management */
index e6098a0..9ffdc04 100644 (file)
@@ -761,6 +761,7 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
                                                     acpi_event_status
                                                     *event_status))
 ACPI_HW_DEPENDENT_RETURN_UINT32(u32 acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_hw_disable_all_gpes(void))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable_all_gpes(void))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_runtime_gpes(void))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_wakeup_gpes(void))
index 0f1001d..3ceb9df 100644 (file)
@@ -200,6 +200,7 @@ enum cpuhp_state {
 
        /* Online section invoked on the hotplugged CPU from the hotplug thread */
        CPUHP_AP_ONLINE_IDLE,
+       CPUHP_AP_HYPERV_ONLINE,
        CPUHP_AP_KVM_ONLINE,
        CPUHP_AP_SCHED_WAIT_EMPTY,
        CPUHP_AP_SMPBOOT_THREADS,
index 311cd93..dd5797f 100644 (file)
@@ -836,7 +836,7 @@ struct ata_port {
 
        struct mutex            scsi_scan_mutex;
        struct delayed_work     hotplug_task;
-       struct work_struct      scsi_rescan_task;
+       struct delayed_work     scsi_rescan_task;
 
        unsigned int            hsm_task_state;
 
index 94d2be5..4b9626c 100644 (file)
@@ -1238,6 +1238,18 @@ static inline u16 mlx5_core_max_vfs(const struct mlx5_core_dev *dev)
        return dev->priv.sriov.max_vfs;
 }
 
+static inline int mlx5_lag_is_lacp_owner(struct mlx5_core_dev *dev)
+{
+       /* LACP owner conditions:
+        * 1) Function is physical.
+        * 2) LAG is supported by FW.
+        * 3) LAG is managed by driver (currently the only option).
+        */
+       return  MLX5_CAP_GEN(dev, vport_group_manager) &&
+                  (MLX5_CAP_GEN(dev, num_lag_ports) > 1) &&
+                   MLX5_CAP_GEN(dev, lag_master);
+}
+
 static inline int mlx5_get_gid_table_len(u16 param)
 {
        if (param > 4) {
index 2aba751..8654470 100644 (file)
@@ -106,12 +106,22 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
 #define RAW_NOTIFIER_INIT(name)        {                               \
                .head = NULL }
 
+#ifdef CONFIG_TREE_SRCU
 #define SRCU_NOTIFIER_INIT(name, pcpu)                         \
        {                                                       \
                .mutex = __MUTEX_INITIALIZER(name.mutex),       \
                .head = NULL,                                   \
+               .srcuu = __SRCU_USAGE_INIT(name.srcuu),         \
                .srcu = __SRCU_STRUCT_INIT(name.srcu, name.srcuu, pcpu), \
        }
+#else
+#define SRCU_NOTIFIER_INIT(name, pcpu)                         \
+       {                                                       \
+               .mutex = __MUTEX_INITIALIZER(name.mutex),       \
+               .head = NULL,                                   \
+               .srcu = __SRCU_STRUCT_INIT(name.srcu, name.srcuu, pcpu), \
+       }
+#endif
 
 #define ATOMIC_NOTIFIER_HEAD(name)                             \
        struct atomic_notifier_head name =                      \
index 367d538..e7c4487 100644 (file)
@@ -686,10 +686,7 @@ struct dtv_frontend_properties {
  * @id:                        Frontend ID
  * @exit:              Used to inform the DVB core that the frontend
  *                     thread should exit (usually, means that the hardware
- *                     got disconnected).
- * @remove_mutex:      mutex that avoids a race condition between a callback
- *                     called when the hardware is disconnected and the
- *                     file_operations of dvb_frontend.
+ *                     got disconnected.
  */
 
 struct dvb_frontend {
@@ -707,7 +704,6 @@ struct dvb_frontend {
        int (*callback)(void *adapter_priv, int component, int cmd, int arg);
        int id;
        unsigned int exit;
-       struct mutex remove_mutex;
 };
 
 /**
index ebb28ec..f37f9f3 100644 (file)
@@ -268,7 +268,7 @@ int flow_offload_route_init(struct flow_offload *flow,
 
 int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow);
 void flow_offload_refresh(struct nf_flowtable *flow_table,
-                         struct flow_offload *flow);
+                         struct flow_offload *flow, bool force);
 
 struct flow_offload_tuple_rhash *flow_offload_lookup(struct nf_flowtable *flow_table,
                                                     struct flow_offload_tuple *tuple);
index 2e24ea1..83db182 100644 (file)
@@ -462,7 +462,8 @@ struct nft_set_ops {
                                               const struct nft_set *set,
                                               const struct nft_set_elem *elem,
                                               unsigned int flags);
-
+       void                            (*commit)(const struct nft_set *set);
+       void                            (*abort)(const struct nft_set *set);
        u64                             (*privsize)(const struct nlattr * const nla[],
                                                    const struct nft_set_desc *desc);
        bool                            (*estimate)(const struct nft_set_desc *desc,
@@ -557,6 +558,7 @@ struct nft_set {
        u16                             policy;
        u16                             udlen;
        unsigned char                   *udata;
+       struct list_head                pending_update;
        /* runtime data below here */
        const struct nft_set_ops        *ops ____cacheline_aligned;
        u16                             flags:14,
index 27271f2..12eadec 100644 (file)
@@ -137,6 +137,13 @@ static inline void qdisc_refcount_inc(struct Qdisc *qdisc)
        refcount_inc(&qdisc->refcnt);
 }
 
+static inline bool qdisc_refcount_dec_if_one(struct Qdisc *qdisc)
+{
+       if (qdisc->flags & TCQ_F_BUILTIN)
+               return true;
+       return refcount_dec_if_one(&qdisc->refcnt);
+}
+
 /* Intended to be used by unlocked users, when concurrent qdisc release is
  * possible.
  */
@@ -652,6 +659,7 @@ void dev_deactivate_many(struct list_head *head);
 struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue,
                              struct Qdisc *qdisc);
 void qdisc_reset(struct Qdisc *qdisc);
+void qdisc_destroy(struct Qdisc *qdisc);
 void qdisc_put(struct Qdisc *qdisc);
 void qdisc_put_unlocked(struct Qdisc *qdisc);
 void qdisc_tree_reduce_backlog(struct Qdisc *qdisc, int n, int len);
index d808dc3..811a0f1 100644 (file)
@@ -194,29 +194,6 @@ static inline enum ib_mtu iboe_get_mtu(int mtu)
                return 0;
 }
 
-static inline int iboe_get_rate(struct net_device *dev)
-{
-       struct ethtool_link_ksettings cmd;
-       int err;
-
-       rtnl_lock();
-       err = __ethtool_get_link_ksettings(dev, &cmd);
-       rtnl_unlock();
-       if (err)
-               return IB_RATE_PORT_CURRENT;
-
-       if (cmd.base.speed >= 40000)
-               return IB_RATE_40_GBPS;
-       else if (cmd.base.speed >= 30000)
-               return IB_RATE_30_GBPS;
-       else if (cmd.base.speed >= 20000)
-               return IB_RATE_20_GBPS;
-       else if (cmd.base.speed >= 10000)
-               return IB_RATE_10_GBPS;
-       else
-               return IB_RATE_PORT_CURRENT;
-}
-
 static inline int rdma_link_local_addr(struct in6_addr *addr)
 {
        if (addr->s6_addr32[0] == htonl(0xfe800000) &&
index 1ebf8d4..73e2c10 100644 (file)
@@ -783,7 +783,7 @@ enum {
 
        /* add new constants above here */
        __ETHTOOL_A_STATS_GRP_CNT,
-       ETHTOOL_A_STATS_GRP_MAX = (__ETHTOOL_A_STATS_CNT - 1)
+       ETHTOOL_A_STATS_GRP_MAX = (__ETHTOOL_A_STATS_GRP_CNT - 1)
 };
 
 enum {
index b271598..399e9a1 100644 (file)
@@ -220,10 +220,12 @@ static void io_worker_exit(struct io_worker *worker)
        list_del_rcu(&worker->all_list);
        raw_spin_unlock(&wq->lock);
        io_wq_dec_running(worker);
-       worker->flags = 0;
-       preempt_disable();
-       current->flags &= ~PF_IO_WORKER;
-       preempt_enable();
+       /*
+        * this worker is a goner, clear ->worker_private to avoid any
+        * inc/dec running calls that could happen as part of exit from
+        * touching 'worker'.
+        */
+       current->worker_private = NULL;
 
        kfree_rcu(worker, rcu);
        io_worker_ref_put(wq);
index 89e8390..51b0f7f 100644 (file)
@@ -65,6 +65,7 @@ struct io_sr_msg {
        u16                             addr_len;
        u16                             buf_group;
        void __user                     *addr;
+       void __user                     *msg_control;
        /* used only for send zerocopy */
        struct io_kiocb                 *notif;
 };
@@ -195,11 +196,15 @@ static int io_sendmsg_copy_hdr(struct io_kiocb *req,
                               struct io_async_msghdr *iomsg)
 {
        struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);
+       int ret;
 
        iomsg->msg.msg_name = &iomsg->addr;
        iomsg->free_iov = iomsg->fast_iov;
-       return sendmsg_copy_msghdr(&iomsg->msg, sr->umsg, sr->msg_flags,
+       ret = sendmsg_copy_msghdr(&iomsg->msg, sr->umsg, sr->msg_flags,
                                        &iomsg->free_iov);
+       /* save msg_control as sys_sendmsg() overwrites it */
+       sr->msg_control = iomsg->msg.msg_control;
+       return ret;
 }
 
 int io_send_prep_async(struct io_kiocb *req)
@@ -297,6 +302,7 @@ int io_sendmsg(struct io_kiocb *req, unsigned int issue_flags)
 
        if (req_has_async_data(req)) {
                kmsg = req->async_data;
+               kmsg->msg.msg_control = sr->msg_control;
        } else {
                ret = io_sendmsg_copy_hdr(req, &iomsg);
                if (ret)
index f989f5f..69ee4a2 100644 (file)
@@ -901,10 +901,22 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
                }
 
                offset = ALIGN(offset, align);
+
+               /*
+                * Check if the segment contains the entry point, if so,
+                * calculate the value of image->start based on it.
+                * If the compiler has produced more than one .text section
+                * (Eg: .text.hot), they are generally after the main .text
+                * section, and they shall not be used to calculate
+                * image->start. So do not re-calculate image->start if it
+                * is not set to the initial value, and warn the user so they
+                * have a chance to fix their purgatory's linker script.
+                */
                if (sechdrs[i].sh_flags & SHF_EXECINSTR &&
                    pi->ehdr->e_entry >= sechdrs[i].sh_addr &&
                    pi->ehdr->e_entry < (sechdrs[i].sh_addr
-                                        + sechdrs[i].sh_size)) {
+                                        + sechdrs[i].sh_size) &&
+                   !WARN_ON(kbuf->image->start != pi->ehdr->e_entry)) {
                        kbuf->image->start -= sechdrs[i].sh_addr;
                        kbuf->image->start += kbuf->mem + offset;
                }
index dbb1470..8df0550 100644 (file)
 #define EVENT_STATUS_OTHER BIT(7)
 
 /*
+ * User register flags are not allowed yet, keep them here until we are
+ * ready to expose them out to the user ABI.
+ */
+enum user_reg_flag {
+       /* Event will not delete upon last reference closing */
+       USER_EVENT_REG_PERSIST          = 1U << 0,
+
+       /* This value or above is currently non-ABI */
+       USER_EVENT_REG_MAX              = 1U << 1,
+};
+
+/*
  * Stores the system name, tables, and locks for a group of events. This
  * allows isolation for events by various means.
  */
@@ -85,8 +97,10 @@ struct user_event {
        struct hlist_node               node;
        struct list_head                fields;
        struct list_head                validators;
+       struct work_struct              put_work;
        refcount_t                      refcnt;
        int                             min_size;
+       int                             reg_flags;
        char                            status;
 };
 
@@ -165,76 +179,151 @@ typedef void (*user_event_func_t) (struct user_event *user, struct iov_iter *i,
 
 static int user_event_parse(struct user_event_group *group, char *name,
                            char *args, char *flags,
-                           struct user_event **newuser);
+                           struct user_event **newuser, int reg_flags);
 
 static struct user_event_mm *user_event_mm_get(struct user_event_mm *mm);
 static struct user_event_mm *user_event_mm_get_all(struct user_event *user);
 static void user_event_mm_put(struct user_event_mm *mm);
+static int destroy_user_event(struct user_event *user);
 
 static u32 user_event_key(char *name)
 {
        return jhash(name, strlen(name), 0);
 }
 
-static void user_event_group_destroy(struct user_event_group *group)
+static struct user_event *user_event_get(struct user_event *user)
 {
-       kfree(group->system_name);
-       kfree(group);
+       refcount_inc(&user->refcnt);
+
+       return user;
 }
 
-static char *user_event_group_system_name(struct user_namespace *user_ns)
+static void delayed_destroy_user_event(struct work_struct *work)
 {
-       char *system_name;
-       int len = sizeof(USER_EVENTS_SYSTEM) + 1;
+       struct user_event *user = container_of(
+               work, struct user_event, put_work);
 
-       if (user_ns != &init_user_ns) {
+       mutex_lock(&event_mutex);
+
+       if (!refcount_dec_and_test(&user->refcnt))
+               goto out;
+
+       if (destroy_user_event(user)) {
                /*
-                * Unexpected at this point:
-                * We only currently support init_user_ns.
-                * When we enable more, this will trigger a failure so log.
+                * The only reason this would fail here is if we cannot
+                * update the visibility of the event. In this case the
+                * event stays in the hashtable, waiting for someone to
+                * attempt to delete it later.
                 */
-               pr_warn("user_events: Namespace other than init_user_ns!\n");
-               return NULL;
+               pr_warn("user_events: Unable to delete event\n");
+               refcount_set(&user->refcnt, 1);
        }
+out:
+       mutex_unlock(&event_mutex);
+}
 
-       system_name = kmalloc(len, GFP_KERNEL);
+static void user_event_put(struct user_event *user, bool locked)
+{
+       bool delete;
 
-       if (!system_name)
-               return NULL;
+       if (unlikely(!user))
+               return;
 
-       snprintf(system_name, len, "%s", USER_EVENTS_SYSTEM);
+       /*
+        * When the event is not enabled for auto-delete there will always
+        * be at least 1 reference to the event. During the event creation
+        * we initially set the refcnt to 2 to achieve this. In those cases
+        * the caller must acquire event_mutex and after decrement check if
+        * the refcnt is 1, meaning this is the last reference. When auto
+        * delete is enabled, there will only be 1 ref, IE: refcnt will be
+        * only set to 1 during creation to allow the below checks to go
+        * through upon the last put. The last put must always be done with
+        * the event mutex held.
+        */
+       if (!locked) {
+               lockdep_assert_not_held(&event_mutex);
+               delete = refcount_dec_and_mutex_lock(&user->refcnt, &event_mutex);
+       } else {
+               lockdep_assert_held(&event_mutex);
+               delete = refcount_dec_and_test(&user->refcnt);
+       }
 
-       return system_name;
+       if (!delete)
+               return;
+
+       /*
+        * We now have the event_mutex in all cases, which ensures that
+        * no new references will be taken until event_mutex is released.
+        * New references come through find_user_event(), which requires
+        * the event_mutex to be held.
+        */
+
+       if (user->reg_flags & USER_EVENT_REG_PERSIST) {
+               /* We should not get here when persist flag is set */
+               pr_alert("BUG: Auto-delete engaged on persistent event\n");
+               goto out;
+       }
+
+       /*
+        * Unfortunately we have to attempt the actual destroy in a work
+        * queue. This is because not all cases handle a trace_event_call
+        * being removed within the class->reg() operation for unregister.
+        */
+       INIT_WORK(&user->put_work, delayed_destroy_user_event);
+
+       /*
+        * Since the event is still in the hashtable, we have to re-inc
+        * the ref count to 1. This count will be decremented and checked
+        * in the work queue to ensure it's still the last ref. This is
+        * needed because a user-process could register the same event in
+        * between the time of event_mutex release and the work queue
+        * running the delayed destroy. If we removed the item now from
+        * the hashtable, this would result in a timing window where a
+        * user process would fail a register because the trace_event_call
+        * register would fail in the tracing layers.
+        */
+       refcount_set(&user->refcnt, 1);
+
+       if (WARN_ON_ONCE(!schedule_work(&user->put_work))) {
+               /*
+                * If we fail we must wait for an admin to attempt delete or
+                * another register/close of the event, whichever is first.
+                */
+               pr_warn("user_events: Unable to queue delayed destroy\n");
+       }
+out:
+       /* Ensure if we didn't have event_mutex before we unlock it */
+       if (!locked)
+               mutex_unlock(&event_mutex);
 }
 
-static inline struct user_event_group
-*user_event_group_from_user_ns(struct user_namespace *user_ns)
+static void user_event_group_destroy(struct user_event_group *group)
 {
-       if (user_ns == &init_user_ns)
-               return init_group;
-
-       return NULL;
+       kfree(group->system_name);
+       kfree(group);
 }
 
-static struct user_event_group *current_user_event_group(void)
+static char *user_event_group_system_name(void)
 {
-       struct user_namespace *user_ns = current_user_ns();
-       struct user_event_group *group = NULL;
+       char *system_name;
+       int len = sizeof(USER_EVENTS_SYSTEM) + 1;
 
-       while (user_ns) {
-               group = user_event_group_from_user_ns(user_ns);
+       system_name = kmalloc(len, GFP_KERNEL);
 
-               if (group)
-                       break;
+       if (!system_name)
+               return NULL;
 
-               user_ns = user_ns->parent;
-       }
+       snprintf(system_name, len, "%s", USER_EVENTS_SYSTEM);
 
-       return group;
+       return system_name;
 }
 
-static struct user_event_group
-*user_event_group_create(struct user_namespace *user_ns)
+static struct user_event_group *current_user_event_group(void)
+{
+       return init_group;
+}
+
+static struct user_event_group *user_event_group_create(void)
 {
        struct user_event_group *group;
 
@@ -243,7 +332,7 @@ static struct user_event_group
        if (!group)
                return NULL;
 
-       group->system_name = user_event_group_system_name(user_ns);
+       group->system_name = user_event_group_system_name();
 
        if (!group->system_name)
                goto error;
@@ -259,12 +348,13 @@ error:
        return NULL;
 };
 
-static void user_event_enabler_destroy(struct user_event_enabler *enabler)
+static void user_event_enabler_destroy(struct user_event_enabler *enabler,
+                                      bool locked)
 {
        list_del_rcu(&enabler->mm_enablers_link);
 
        /* No longer tracking the event via the enabler */
-       refcount_dec(&enabler->event->refcnt);
+       user_event_put(enabler->event, locked);
 
        kfree(enabler);
 }
@@ -326,7 +416,7 @@ static void user_event_enabler_fault_fixup(struct work_struct *work)
 
        /* User asked for enabler to be removed during fault */
        if (test_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler))) {
-               user_event_enabler_destroy(enabler);
+               user_event_enabler_destroy(enabler, true);
                goto out;
        }
 
@@ -501,14 +591,12 @@ static bool user_event_enabler_dup(struct user_event_enabler *orig,
        if (!enabler)
                return false;
 
-       enabler->event = orig->event;
+       enabler->event = user_event_get(orig->event);
        enabler->addr = orig->addr;
 
        /* Only dup part of value (ignore future flags, etc) */
        enabler->values = orig->values & ENABLE_VAL_DUP_MASK;
 
-       refcount_inc(&enabler->event->refcnt);
-
        /* Enablers not exposed yet, RCU not required */
        list_add(&enabler->mm_enablers_link, &mm->enablers);
 
@@ -625,7 +713,7 @@ static void user_event_mm_destroy(struct user_event_mm *mm)
        struct user_event_enabler *enabler, *next;
 
        list_for_each_entry_safe(enabler, next, &mm->enablers, mm_enablers_link)
-               user_event_enabler_destroy(enabler);
+               user_event_enabler_destroy(enabler, false);
 
        mmdrop(mm->mm);
        kfree(mm);
@@ -780,7 +868,7 @@ retry:
         * exit or run exec(), which includes forks and clones.
         */
        if (!*write_result) {
-               refcount_inc(&enabler->event->refcnt);
+               user_event_get(user);
                list_add_rcu(&enabler->mm_enablers_link, &user_mm->enablers);
        }
 
@@ -803,7 +891,12 @@ out:
 static __always_inline __must_check
 bool user_event_last_ref(struct user_event *user)
 {
-       return refcount_read(&user->refcnt) == 1;
+       int last = 0;
+
+       if (user->reg_flags & USER_EVENT_REG_PERSIST)
+               last = 1;
+
+       return refcount_read(&user->refcnt) == last;
 }
 
 static __always_inline __must_check
@@ -842,7 +935,8 @@ static struct list_head *user_event_get_fields(struct trace_event_call *call)
  * Upon success user_event has its ref count increased by 1.
  */
 static int user_event_parse_cmd(struct user_event_group *group,
-                               char *raw_command, struct user_event **newuser)
+                               char *raw_command, struct user_event **newuser,
+                               int reg_flags)
 {
        char *name = raw_command;
        char *args = strpbrk(name, " ");
@@ -856,7 +950,7 @@ static int user_event_parse_cmd(struct user_event_group *group,
        if (flags)
                *flags++ = '\0';
 
-       return user_event_parse(group, name, args, flags, newuser);
+       return user_event_parse(group, name, args, flags, newuser, reg_flags);
 }
 
 static int user_field_array_size(const char *type)
@@ -1367,10 +1461,8 @@ static struct user_event *find_user_event(struct user_event_group *group,
        *outkey = key;
 
        hash_for_each_possible(group->register_table, user, node, key)
-               if (!strcmp(EVENT_NAME(user), name)) {
-                       refcount_inc(&user->refcnt);
-                       return user;
-               }
+               if (!strcmp(EVENT_NAME(user), name))
+                       return user_event_get(user);
 
        return NULL;
 }
@@ -1432,7 +1524,7 @@ static void user_event_ftrace(struct user_event *user, struct iov_iter *i,
        if (unlikely(!entry))
                return;
 
-       if (unlikely(!copy_nofault(entry + 1, i->count, i)))
+       if (unlikely(i->count != 0 && !copy_nofault(entry + 1, i->count, i)))
                goto discard;
 
        if (!list_empty(&user->validators) &&
@@ -1473,7 +1565,7 @@ static void user_event_perf(struct user_event *user, struct iov_iter *i,
 
                perf_fetch_caller_regs(regs);
 
-               if (unlikely(!copy_nofault(perf_entry + 1, i->count, i)))
+               if (unlikely(i->count != 0 && !copy_nofault(perf_entry + 1, i->count, i)))
                        goto discard;
 
                if (!list_empty(&user->validators) &&
@@ -1584,12 +1676,12 @@ static int user_event_reg(struct trace_event_call *call,
 
        return ret;
 inc:
-       refcount_inc(&user->refcnt);
+       user_event_get(user);
        update_enable_bit_for(user);
        return 0;
 dec:
        update_enable_bit_for(user);
-       refcount_dec(&user->refcnt);
+       user_event_put(user, true);
        return 0;
 }
 
@@ -1620,10 +1712,11 @@ static int user_event_create(const char *raw_command)
 
        mutex_lock(&group->reg_mutex);
 
-       ret = user_event_parse_cmd(group, name, &user);
+       /* Dyn events persist, otherwise they would cleanup immediately */
+       ret = user_event_parse_cmd(group, name, &user, USER_EVENT_REG_PERSIST);
 
        if (!ret)
-               refcount_dec(&user->refcnt);
+               user_event_put(user, false);
 
        mutex_unlock(&group->reg_mutex);
 
@@ -1745,6 +1838,8 @@ static bool user_event_match(const char *system, const char *event,
 
        if (match && argc > 0)
                match = user_fields_match(user, argc, argv);
+       else if (match && argc == 0)
+               match = list_empty(&user->fields);
 
        return match;
 }
@@ -1781,11 +1876,17 @@ static int user_event_trace_register(struct user_event *user)
  */
 static int user_event_parse(struct user_event_group *group, char *name,
                            char *args, char *flags,
-                           struct user_event **newuser)
+                           struct user_event **newuser, int reg_flags)
 {
        int ret;
        u32 key;
        struct user_event *user;
+       int argc = 0;
+       char **argv;
+
+       /* User register flags are not ready yet */
+       if (reg_flags != 0 || flags != NULL)
+               return -EINVAL;
 
        /* Prevent dyn_event from racing */
        mutex_lock(&event_mutex);
@@ -1793,13 +1894,35 @@ static int user_event_parse(struct user_event_group *group, char *name,
        mutex_unlock(&event_mutex);
 
        if (user) {
-               *newuser = user;
-               /*
-                * Name is allocated by caller, free it since it already exists.
-                * Caller only worries about failure cases for freeing.
-                */
-               kfree(name);
+               if (args) {
+                       argv = argv_split(GFP_KERNEL, args, &argc);
+                       if (!argv) {
+                               ret = -ENOMEM;
+                               goto error;
+                       }
+
+                       ret = user_fields_match(user, argc, (const char **)argv);
+                       argv_free(argv);
+
+               } else
+                       ret = list_empty(&user->fields);
+
+               if (ret) {
+                       *newuser = user;
+                       /*
+                        * Name is allocated by caller, free it since it already exists.
+                        * Caller only worries about failure cases for freeing.
+                        */
+                       kfree(name);
+               } else {
+                       ret = -EADDRINUSE;
+                       goto error;
+               }
+
                return 0;
+error:
+               user_event_put(user, false);
+               return ret;
        }
 
        user = kzalloc(sizeof(*user), GFP_KERNEL_ACCOUNT);
@@ -1852,8 +1975,15 @@ static int user_event_parse(struct user_event_group *group, char *name,
        if (ret)
                goto put_user_lock;
 
-       /* Ensure we track self ref and caller ref (2) */
-       refcount_set(&user->refcnt, 2);
+       user->reg_flags = reg_flags;
+
+       if (user->reg_flags & USER_EVENT_REG_PERSIST) {
+               /* Ensure we track self ref and caller ref (2) */
+               refcount_set(&user->refcnt, 2);
+       } else {
+               /* Ensure we track only caller ref (1) */
+               refcount_set(&user->refcnt, 1);
+       }
 
        dyn_event_init(&user->devent, &user_event_dops);
        dyn_event_add(&user->devent, &user->call);
@@ -1885,7 +2015,7 @@ static int delete_user_event(struct user_event_group *group, char *name)
        if (!user)
                return -ENOENT;
 
-       refcount_dec(&user->refcnt);
+       user_event_put(user, true);
 
        if (!user_event_last_ref(user))
                return -EBUSY;
@@ -2044,9 +2174,7 @@ static int user_events_ref_add(struct user_event_file_info *info,
        for (i = 0; i < count; ++i)
                new_refs->events[i] = refs->events[i];
 
-       new_refs->events[i] = user;
-
-       refcount_inc(&user->refcnt);
+       new_refs->events[i] = user_event_get(user);
 
        rcu_assign_pointer(info->refs, new_refs);
 
@@ -2077,8 +2205,8 @@ static long user_reg_get(struct user_reg __user *ureg, struct user_reg *kreg)
        if (ret)
                return ret;
 
-       /* Ensure no flags, since we don't support any yet */
-       if (kreg->flags != 0)
+       /* Ensure only valid flags */
+       if (kreg->flags & ~(USER_EVENT_REG_MAX-1))
                return -EINVAL;
 
        /* Ensure supported size */
@@ -2150,7 +2278,7 @@ static long user_events_ioctl_reg(struct user_event_file_info *info,
                return ret;
        }
 
-       ret = user_event_parse_cmd(info->group, name, &user);
+       ret = user_event_parse_cmd(info->group, name, &user, reg.flags);
 
        if (ret) {
                kfree(name);
@@ -2160,7 +2288,7 @@ static long user_events_ioctl_reg(struct user_event_file_info *info,
        ret = user_events_ref_add(info, user);
 
        /* No longer need parse ref, ref_add either worked or not */
-       refcount_dec(&user->refcnt);
+       user_event_put(user, false);
 
        /* Positive number is index and valid */
        if (ret < 0)
@@ -2309,7 +2437,7 @@ static long user_events_ioctl_unreg(unsigned long uarg)
                        set_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler));
 
                        if (!test_bit(ENABLE_VAL_FAULTING_BIT, ENABLE_BITOPS(enabler)))
-                               user_event_enabler_destroy(enabler);
+                               user_event_enabler_destroy(enabler, true);
 
                        /* Removed at least one */
                        ret = 0;
@@ -2367,7 +2495,6 @@ static int user_events_release(struct inode *node, struct file *file)
        struct user_event_file_info *info = file->private_data;
        struct user_event_group *group;
        struct user_event_refs *refs;
-       struct user_event *user;
        int i;
 
        if (!info)
@@ -2391,12 +2518,9 @@ static int user_events_release(struct inode *node, struct file *file)
         * The underlying user_events are ref counted, and cannot be freed.
         * After this decrement, the user_events may be freed elsewhere.
         */
-       for (i = 0; i < refs->count; ++i) {
-               user = refs->events[i];
+       for (i = 0; i < refs->count; ++i)
+               user_event_put(refs->events[i], false);
 
-               if (user)
-                       refcount_dec(&user->refcnt);
-       }
 out:
        file->private_data = NULL;
 
@@ -2577,7 +2701,7 @@ static int __init trace_events_user_init(void)
        if (!fault_cache)
                return -ENOMEM;
 
-       init_group = user_event_group_create(&init_user_ns);
+       init_group = user_event_group_create();
 
        if (!init_group) {
                kmem_cache_destroy(fault_cache);
index 15f05fa..1e33f36 100644 (file)
@@ -847,7 +847,7 @@ static void print_fields(struct trace_iterator *iter, struct trace_event_call *c
        int ret;
        void *pos;
 
-       list_for_each_entry(field, head, link) {
+       list_for_each_entry_reverse(field, head, link) {
                trace_seq_printf(&iter->seq, " %s=", field->name);
                if (field->offset + field->size > iter->ent_size) {
                        trace_seq_puts(&iter->seq, "<OVERFLOW>");
index 049ba13..1a31065 100644 (file)
@@ -27,6 +27,8 @@
 #include <linux/string.h>
 #include <linux/xarray.h>
 
+#include "radix-tree.h"
+
 /*
  * Radix tree node cache.
  */
diff --git a/lib/radix-tree.h b/lib/radix-tree.h
new file mode 100644 (file)
index 0000000..40d5c03
--- /dev/null
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* radix-tree helpers that are only shared with xarray */
+
+struct kmem_cache;
+struct rcu_head;
+
+extern struct kmem_cache *radix_tree_node_cachep;
+extern void radix_tree_node_rcu_free(struct rcu_head *head);
index 9dd9745..3718d98 100644 (file)
@@ -369,7 +369,7 @@ vm_map_ram_test(void)
        int i;
 
        map_nr_pages = nr_pages > 0 ? nr_pages:1;
-       pages = kmalloc(map_nr_pages * sizeof(struct page), GFP_KERNEL);
+       pages = kcalloc(map_nr_pages, sizeof(struct page *), GFP_KERNEL);
        if (!pages)
                return -1;
 
index ea9ce1f..2071a37 100644 (file)
@@ -12,6 +12,8 @@
 #include <linux/slab.h>
 #include <linux/xarray.h>
 
+#include "radix-tree.h"
+
 /*
  * Coding conventions in this file:
  *
@@ -247,10 +249,6 @@ void *xas_load(struct xa_state *xas)
 }
 EXPORT_SYMBOL_GPL(xas_load);
 
-/* Move the radix tree node cache here */
-extern struct kmem_cache *radix_tree_node_cachep;
-extern void radix_tree_node_rcu_free(struct rcu_head *head);
-
 #define XA_RCU_FREE    ((struct xarray *)1)
 
 static void xa_node_free(struct xa_node *node)
index d9ef620..91cff7f 100644 (file)
@@ -551,6 +551,8 @@ int damon_set_attrs(struct damon_ctx *ctx, struct damon_attrs *attrs)
                return -EINVAL;
        if (attrs->min_nr_regions > attrs->max_nr_regions)
                return -EINVAL;
+       if (attrs->sample_interval > attrs->aggr_interval)
+               return -EINVAL;
 
        damon_update_monitoring_results(ctx, attrs);
        ctx->attrs = *attrs;
index b4c9bd3..83dda76 100644 (file)
@@ -1760,7 +1760,9 @@ bool __folio_lock_or_retry(struct folio *folio, struct mm_struct *mm,
  *
  * Return: The index of the gap if found, otherwise an index outside the
  * range specified (in which case 'return - index >= max_scan' will be true).
- * In the rare case of index wrap-around, 0 will be returned.
+ * In the rare case of index wrap-around, 0 will be returned.  0 will also
+ * be returned if index == 0 and there is a gap at the index.  We can not
+ * wrap-around if passed index == 0.
  */
 pgoff_t page_cache_next_miss(struct address_space *mapping,
                             pgoff_t index, unsigned long max_scan)
@@ -1770,12 +1772,13 @@ pgoff_t page_cache_next_miss(struct address_space *mapping,
        while (max_scan--) {
                void *entry = xas_next(&xas);
                if (!entry || xa_is_value(entry))
-                       break;
-               if (xas.xa_index == 0)
-                       break;
+                       return xas.xa_index;
+               if (xas.xa_index == 0 && index != 0)
+                       return xas.xa_index;
        }
 
-       return xas.xa_index;
+       /* No gaps in range and no wrap-around, return index beyond range */
+       return xas.xa_index + 1;
 }
 EXPORT_SYMBOL(page_cache_next_miss);
 
@@ -1796,7 +1799,9 @@ EXPORT_SYMBOL(page_cache_next_miss);
  *
  * Return: The index of the gap if found, otherwise an index outside the
  * range specified (in which case 'index - return >= max_scan' will be true).
- * In the rare case of wrap-around, ULONG_MAX will be returned.
+ * In the rare case of wrap-around, ULONG_MAX will be returned.  ULONG_MAX
+ * will also be returned if index == ULONG_MAX and there is a gap at the
+ * index.  We can not wrap-around if passed index == ULONG_MAX.
  */
 pgoff_t page_cache_prev_miss(struct address_space *mapping,
                             pgoff_t index, unsigned long max_scan)
@@ -1806,12 +1811,13 @@ pgoff_t page_cache_prev_miss(struct address_space *mapping,
        while (max_scan--) {
                void *entry = xas_prev(&xas);
                if (!entry || xa_is_value(entry))
-                       break;
-               if (xas.xa_index == ULONG_MAX)
-                       break;
+                       return xas.xa_index;
+               if (xas.xa_index == ULONG_MAX && index != ULONG_MAX)
+                       return xas.xa_index;
        }
 
-       return xas.xa_index;
+       /* No gaps in range and no wrap-around, return index beyond range */
+       return xas.xa_index - 1;
 }
 EXPORT_SYMBOL(page_cache_prev_miss);
 
index 8ae7307..c0421b7 100644 (file)
@@ -381,6 +381,7 @@ static int gup_test_release(struct inode *inode, struct file *file)
 static const struct file_operations gup_test_fops = {
        .open = nonseekable_open,
        .unlocked_ioctl = gup_test_ioctl,
+       .compat_ioctl = compat_ptr_ioctl,
        .release = gup_test_release,
 };
 
index 13678ed..d600404 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2318,21 +2318,6 @@ int split_vma(struct vma_iterator *vmi, struct vm_area_struct *vma,
        return __split_vma(vmi, vma, addr, new_below);
 }
 
-static inline int munmap_sidetree(struct vm_area_struct *vma,
-                                  struct ma_state *mas_detach)
-{
-       vma_start_write(vma);
-       mas_set_range(mas_detach, vma->vm_start, vma->vm_end - 1);
-       if (mas_store_gfp(mas_detach, vma, GFP_KERNEL))
-               return -ENOMEM;
-
-       vma_mark_detached(vma, true);
-       if (vma->vm_flags & VM_LOCKED)
-               vma->vm_mm->locked_vm -= vma_pages(vma);
-
-       return 0;
-}
-
 /*
  * do_vmi_align_munmap() - munmap the aligned region from @start to @end.
  * @vmi: The vma iterator
@@ -2354,6 +2339,7 @@ do_vmi_align_munmap(struct vma_iterator *vmi, struct vm_area_struct *vma,
        struct maple_tree mt_detach;
        int count = 0;
        int error = -ENOMEM;
+       unsigned long locked_vm = 0;
        MA_STATE(mas_detach, &mt_detach, 0, 0);
        mt_init_flags(&mt_detach, vmi->mas.tree->ma_flags & MT_FLAGS_LOCK_MASK);
        mt_set_external_lock(&mt_detach, &mm->mmap_lock);
@@ -2399,9 +2385,13 @@ do_vmi_align_munmap(struct vma_iterator *vmi, struct vm_area_struct *vma,
                        if (error)
                                goto end_split_failed;
                }
-               error = munmap_sidetree(next, &mas_detach);
-               if (error)
-                       goto munmap_sidetree_failed;
+               vma_start_write(next);
+               mas_set_range(&mas_detach, next->vm_start, next->vm_end - 1);
+               if (mas_store_gfp(&mas_detach, next, GFP_KERNEL))
+                       goto munmap_gather_failed;
+               vma_mark_detached(next, true);
+               if (next->vm_flags & VM_LOCKED)
+                       locked_vm += vma_pages(next);
 
                count++;
 #ifdef CONFIG_DEBUG_VM_MAPLE_TREE
@@ -2447,10 +2437,12 @@ do_vmi_align_munmap(struct vma_iterator *vmi, struct vm_area_struct *vma,
        }
 #endif
        /* Point of no return */
+       error = -ENOMEM;
        vma_iter_set(vmi, start);
        if (vma_iter_clear_gfp(vmi, start, end, GFP_KERNEL))
-               return -ENOMEM;
+               goto clear_tree_failed;
 
+       mm->locked_vm -= locked_vm;
        mm->map_count -= count;
        /*
         * Do not downgrade mmap_lock if we are next to VM_GROWSDOWN or
@@ -2480,9 +2472,14 @@ do_vmi_align_munmap(struct vma_iterator *vmi, struct vm_area_struct *vma,
        validate_mm(mm);
        return downgrade ? 1 : 0;
 
+clear_tree_failed:
 userfaultfd_error:
-munmap_sidetree_failed:
+munmap_gather_failed:
 end_split_failed:
+       mas_set(&mas_detach, 0);
+       mas_for_each(&mas_detach, next, end)
+               vma_mark_detached(next, false);
+
        __mt_destroy(&mt_detach);
 start_split_failed:
 map_count_exceeded:
index 59da2a4..30092d9 100644 (file)
@@ -1174,9 +1174,16 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
                goto reject;
        }
 
+       /*
+        * XXX: zswap reclaim does not work with cgroups yet. Without a
+        * cgroup-aware entry LRU, we will push out entries system-wide based on
+        * local cgroup limits.
+        */
        objcg = get_obj_cgroup_from_page(page);
-       if (objcg && !obj_cgroup_may_zswap(objcg))
-               goto shrink;
+       if (objcg && !obj_cgroup_may_zswap(objcg)) {
+               ret = -ENOMEM;
+               goto reject;
+       }
 
        /* reclaim space if needed */
        if (zswap_is_full()) {
index a06b564..b0ebf85 100644 (file)
@@ -191,6 +191,9 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
        struct dccp_sock *dp = dccp_sk(sk);
        struct inet_connection_sock *icsk = inet_csk(sk);
 
+       pr_warn_once("DCCP is deprecated and scheduled to be removed in 2025, "
+                    "please contact the netdev mailing list\n");
+
        icsk->icsk_rto          = DCCP_TIMEOUT_INIT;
        icsk->icsk_syn_retries  = sysctl_dccp_request_retries;
        sk->sk_state            = DCCP_CLOSED;
index 8aeaadc..4dac965 100644 (file)
@@ -31,7 +31,6 @@ struct handshake_req {
        struct list_head                hr_list;
        struct rhash_head               hr_rhash;
        unsigned long                   hr_flags;
-       struct file                     *hr_file;
        const struct handshake_proto    *hr_proto;
        struct sock                     *hr_sk;
        void                            (*hr_odestruct)(struct sock *sk);
index d78d41a..94d5cef 100644 (file)
@@ -239,7 +239,6 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req,
        }
        req->hr_odestruct = req->hr_sk->sk_destruct;
        req->hr_sk->sk_destruct = handshake_sk_destruct;
-       req->hr_file = sock->file;
 
        ret = -EOPNOTSUPP;
        net = sock_net(req->hr_sk);
@@ -335,9 +334,6 @@ bool handshake_req_cancel(struct sock *sk)
                return false;
        }
 
-       /* Request accepted and waiting for DONE */
-       fput(req->hr_file);
-
 out_true:
        trace_handshake_cancel(net, req, sk);
 
index 56d94d2..143f93a 100644 (file)
@@ -22,6 +22,8 @@ static int udplite_sk_init(struct sock *sk)
 {
        udp_init_sock(sk);
        udp_sk(sk)->pcflag = UDPLITE_BIT;
+       pr_warn_once("UDP-Lite is deprecated and scheduled to be removed in 2025, "
+                    "please contact the netdev mailing list\n");
        return 0;
 }
 
index c4835db..f804c11 100644 (file)
@@ -114,7 +114,8 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        addr_type = ipv6_addr_type(daddr);
        if ((__ipv6_addr_needs_scope_id(addr_type) && !oif) ||
            (addr_type & IPV6_ADDR_MAPPED) ||
-           (oif && sk->sk_bound_dev_if && oif != sk->sk_bound_dev_if))
+           (oif && sk->sk_bound_dev_if && oif != sk->sk_bound_dev_if &&
+            l3mdev_master_ifindex_by_index(sock_net(sk), oif) != sk->sk_bound_dev_if))
                return -EINVAL;
 
        ipcm6_init_sk(&ipc6, np);
index 3bab0cc..8e010d0 100644 (file)
@@ -8,6 +8,8 @@
  *  Changes:
  *  Fixes:
  */
+#define pr_fmt(fmt) "UDPLite6: " fmt
+
 #include <linux/export.h>
 #include <linux/proc_fs.h>
 #include "udp_impl.h"
@@ -16,6 +18,8 @@ static int udplitev6_sk_init(struct sock *sk)
 {
        udpv6_init_sock(sk);
        udp_sk(sk)->pcflag = UDPLITE_BIT;
+       pr_warn_once("UDP-Lite is deprecated and scheduled to be removed in 2025, "
+                    "please contact the netdev mailing list\n");
        return 0;
 }
 
index 86b2036..f2d08db 100644 (file)
@@ -4865,11 +4865,16 @@ static int ieee80211_add_intf_link(struct wiphy *wiphy,
                                   unsigned int link_id)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+       int res;
 
        if (wdev->use_4addr)
                return -EOPNOTSUPP;
 
-       return ieee80211_vif_set_links(sdata, wdev->valid_links);
+       mutex_lock(&sdata->local->mtx);
+       res = ieee80211_vif_set_links(sdata, wdev->valid_links);
+       mutex_unlock(&sdata->local->mtx);
+
+       return res;
 }
 
 static void ieee80211_del_intf_link(struct wiphy *wiphy,
@@ -4878,7 +4883,9 @@ static void ieee80211_del_intf_link(struct wiphy *wiphy,
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
 
+       mutex_lock(&sdata->local->mtx);
        ieee80211_vif_set_links(sdata, wdev->valid_links);
+       mutex_unlock(&sdata->local->mtx);
 }
 
 static int sta_add_link_station(struct ieee80211_local *local,
index b0372e7..4159fb6 100644 (file)
@@ -2312,7 +2312,7 @@ ieee802_11_parse_elems(const u8 *start, size_t len, bool action,
        return ieee802_11_parse_elems_crc(start, len, action, 0, 0, bss);
 }
 
-void ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos);
+void ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos, u8 frag_id);
 
 extern const int ieee802_1d_to_ac[8];
 
index e82db88..40f030b 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * MLO link handling
  *
- * Copyright (C) 2022 Intel Corporation
+ * Copyright (C) 2022-2023 Intel Corporation
  */
 #include <linux/slab.h>
 #include <linux/kernel.h>
@@ -409,6 +409,7 @@ static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata,
                                                 IEEE80211_CHANCTX_SHARED);
                WARN_ON_ONCE(ret);
 
+               ieee80211_mgd_set_link_qos_params(link);
                ieee80211_link_info_change_notify(sdata, link,
                                                  BSS_CHANGED_ERP_CTS_PROT |
                                                  BSS_CHANGED_ERP_PREAMBLE |
@@ -423,7 +424,6 @@ static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata,
                                                  BSS_CHANGED_TWT |
                                                  BSS_CHANGED_HE_OBSS_PD |
                                                  BSS_CHANGED_HE_BSS_COLOR);
-               ieee80211_mgd_set_link_qos_params(link);
        }
 
        old_active = sdata->vif.active_links;
index bd8d6f9..5a43031 100644 (file)
@@ -1372,10 +1372,11 @@ static void ieee80211_assoc_add_ml_elem(struct ieee80211_sub_if_data *sdata,
                ieee80211_add_non_inheritance_elem(skb, outer_present_elems,
                                                   link_present_elems);
 
-               ieee80211_fragment_element(skb, subelem_len);
+               ieee80211_fragment_element(skb, subelem_len,
+                                          IEEE80211_MLE_SUBELEM_FRAGMENT);
        }
 
-       ieee80211_fragment_element(skb, ml_elem_len);
+       ieee80211_fragment_element(skb, ml_elem_len, WLAN_EID_FRAGMENT);
 }
 
 static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
index 7f1c7f6..13b522d 100644 (file)
@@ -4445,7 +4445,7 @@ static void ieee80211_mlo_multicast_tx(struct net_device *dev,
                                       struct sk_buff *skb)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       unsigned long links = sdata->vif.valid_links;
+       unsigned long links = sdata->vif.active_links;
        unsigned int link;
        u32 ctrl_flags = IEEE80211_TX_CTRL_MCAST_MLO_FIRST_TX;
 
@@ -6040,7 +6040,7 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
                rcu_read_unlock();
 
                if (WARN_ON_ONCE(link == ARRAY_SIZE(sdata->vif.link_conf)))
-                       link = ffs(sdata->vif.valid_links) - 1;
+                       link = ffs(sdata->vif.active_links) - 1;
        }
 
        IEEE80211_SKB_CB(skb)->control.flags |=
@@ -6076,7 +6076,7 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
                band = chanctx_conf->def.chan->band;
        } else {
                WARN_ON(link_id >= 0 &&
-                       !(sdata->vif.valid_links & BIT(link_id)));
+                       !(sdata->vif.active_links & BIT(link_id)));
                /* MLD transmissions must not rely on the band */
                band = 0;
        }
index 4bf7615..3bd07a0 100644 (file)
@@ -5049,7 +5049,7 @@ u8 *ieee80211_ie_build_eht_cap(u8 *pos,
        return pos;
 }
 
-void ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos)
+void ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos, u8 frag_id)
 {
        unsigned int elem_len;
 
@@ -5069,7 +5069,7 @@ void ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos)
                memmove(len_pos + 255 + 3, len_pos + 255 + 1, elem_len);
                /* place the fragment ID */
                len_pos += 255 + 1;
-               *len_pos = WLAN_EID_FRAGMENT;
+               *len_pos = frag_id;
                /* and point to fragment length to update later */
                len_pos++;
        }
index 04bd0ed..b0ef48b 100644 (file)
@@ -317,12 +317,12 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
 EXPORT_SYMBOL_GPL(flow_offload_add);
 
 void flow_offload_refresh(struct nf_flowtable *flow_table,
-                         struct flow_offload *flow)
+                         struct flow_offload *flow, bool force)
 {
        u32 timeout;
 
        timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow);
-       if (timeout - READ_ONCE(flow->timeout) > HZ)
+       if (force || timeout - READ_ONCE(flow->timeout) > HZ)
                WRITE_ONCE(flow->timeout, timeout);
        else
                return;
@@ -334,6 +334,12 @@ void flow_offload_refresh(struct nf_flowtable *flow_table,
 }
 EXPORT_SYMBOL_GPL(flow_offload_refresh);
 
+static bool nf_flow_is_outdated(const struct flow_offload *flow)
+{
+       return test_bit(IPS_SEEN_REPLY_BIT, &flow->ct->status) &&
+               !test_bit(NF_FLOW_HW_ESTABLISHED, &flow->flags);
+}
+
 static inline bool nf_flow_has_expired(const struct flow_offload *flow)
 {
        return nf_flow_timeout_delta(flow->timeout) <= 0;
@@ -423,7 +429,8 @@ static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table,
                                    struct flow_offload *flow, void *data)
 {
        if (nf_flow_has_expired(flow) ||
-           nf_ct_is_dying(flow->ct))
+           nf_ct_is_dying(flow->ct) ||
+           nf_flow_is_outdated(flow))
                flow_offload_teardown(flow);
 
        if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
index 19efba1..3bbaf9c 100644 (file)
@@ -384,7 +384,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
        if (skb_try_make_writable(skb, thoff + hdrsize))
                return NF_DROP;
 
-       flow_offload_refresh(flow_table, flow);
+       flow_offload_refresh(flow_table, flow, false);
 
        nf_flow_encap_pop(skb, tuplehash);
        thoff -= offset;
@@ -650,7 +650,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
        if (skb_try_make_writable(skb, thoff + hdrsize))
                return NF_DROP;
 
-       flow_offload_refresh(flow_table, flow);
+       flow_offload_refresh(flow_table, flow, false);
 
        nf_flow_encap_pop(skb, tuplehash);
 
index 0519d45..69bceef 100644 (file)
@@ -3844,7 +3844,8 @@ err_destroy_flow_rule:
        if (flow)
                nft_flow_rule_destroy(flow);
 err_release_rule:
-       nf_tables_rule_release(&ctx, rule);
+       nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE);
+       nf_tables_rule_destroy(&ctx, rule);
 err_release_expr:
        for (i = 0; i < n; i++) {
                if (expr_info[i].ops) {
@@ -4919,6 +4920,7 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
 
        set->num_exprs = num_exprs;
        set->handle = nf_tables_alloc_handle(table);
+       INIT_LIST_HEAD(&set->pending_update);
 
        err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
        if (err < 0)
@@ -9275,10 +9277,25 @@ static void nf_tables_commit_audit_log(struct list_head *adl, u32 generation)
        }
 }
 
+static void nft_set_commit_update(struct list_head *set_update_list)
+{
+       struct nft_set *set, *next;
+
+       list_for_each_entry_safe(set, next, set_update_list, pending_update) {
+               list_del_init(&set->pending_update);
+
+               if (!set->ops->commit)
+                       continue;
+
+               set->ops->commit(set);
+       }
+}
+
 static int nf_tables_commit(struct net *net, struct sk_buff *skb)
 {
        struct nftables_pernet *nft_net = nft_pernet(net);
        struct nft_trans *trans, *next;
+       LIST_HEAD(set_update_list);
        struct nft_trans_elem *te;
        struct nft_chain *chain;
        struct nft_table *table;
@@ -9453,6 +9470,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                        nf_tables_setelem_notify(&trans->ctx, te->set,
                                                 &te->elem,
                                                 NFT_MSG_NEWSETELEM);
+                       if (te->set->ops->commit &&
+                           list_empty(&te->set->pending_update)) {
+                               list_add_tail(&te->set->pending_update,
+                                             &set_update_list);
+                       }
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELSETELEM:
@@ -9467,6 +9489,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                                atomic_dec(&te->set->nelems);
                                te->set->ndeact--;
                        }
+                       if (te->set->ops->commit &&
+                           list_empty(&te->set->pending_update)) {
+                               list_add_tail(&te->set->pending_update,
+                                             &set_update_list);
+                       }
                        break;
                case NFT_MSG_NEWOBJ:
                        if (nft_trans_obj_update(trans)) {
@@ -9529,6 +9556,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                }
        }
 
+       nft_set_commit_update(&set_update_list);
+
        nft_commit_notify(net, NETLINK_CB(skb).portid);
        nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
        nf_tables_commit_audit_log(&adl, nft_net->base_seq);
@@ -9588,10 +9617,25 @@ static void nf_tables_abort_release(struct nft_trans *trans)
        kfree(trans);
 }
 
+static void nft_set_abort_update(struct list_head *set_update_list)
+{
+       struct nft_set *set, *next;
+
+       list_for_each_entry_safe(set, next, set_update_list, pending_update) {
+               list_del_init(&set->pending_update);
+
+               if (!set->ops->abort)
+                       continue;
+
+               set->ops->abort(set);
+       }
+}
+
 static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
 {
        struct nftables_pernet *nft_net = nft_pernet(net);
        struct nft_trans *trans, *next;
+       LIST_HEAD(set_update_list);
        struct nft_trans_elem *te;
 
        if (action == NFNL_ABORT_VALIDATE &&
@@ -9701,6 +9745,12 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                        nft_setelem_remove(net, te->set, &te->elem);
                        if (!nft_setelem_is_catchall(te->set, &te->elem))
                                atomic_dec(&te->set->nelems);
+
+                       if (te->set->ops->abort &&
+                           list_empty(&te->set->pending_update)) {
+                               list_add_tail(&te->set->pending_update,
+                                             &set_update_list);
+                       }
                        break;
                case NFT_MSG_DELSETELEM:
                case NFT_MSG_DESTROYSETELEM:
@@ -9711,6 +9761,11 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                        if (!nft_setelem_is_catchall(te->set, &te->elem))
                                te->set->ndeact--;
 
+                       if (te->set->ops->abort &&
+                           list_empty(&te->set->pending_update)) {
+                               list_add_tail(&te->set->pending_update,
+                                             &set_update_list);
+                       }
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWOBJ:
@@ -9753,6 +9808,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                }
        }
 
+       nft_set_abort_update(&set_update_list);
+
        synchronize_rcu();
 
        list_for_each_entry_safe_reverse(trans, next,
index ae71464..c9fbe0f 100644 (file)
@@ -533,7 +533,8 @@ ack:
                         * processed, this avoids that the same error is
                         * reported several times when replaying the batch.
                         */
-                       if (nfnl_err_add(&err_list, nlh, err, &extack) < 0) {
+                       if (err == -ENOMEM ||
+                           nfnl_err_add(&err_list, nlh, err, &extack) < 0) {
                                /* We failed to enqueue an error, reset the
                                 * list of errors and send OOM to userspace
                                 * pointing to the batch header.
index 06d46d1..15e451d 100644 (file)
@@ -1600,17 +1600,10 @@ static void pipapo_free_fields(struct nft_pipapo_match *m)
        }
 }
 
-/**
- * pipapo_reclaim_match - RCU callback to free fields from old matching data
- * @rcu:       RCU head
- */
-static void pipapo_reclaim_match(struct rcu_head *rcu)
+static void pipapo_free_match(struct nft_pipapo_match *m)
 {
-       struct nft_pipapo_match *m;
        int i;
 
-       m = container_of(rcu, struct nft_pipapo_match, rcu);
-
        for_each_possible_cpu(i)
                kfree(*per_cpu_ptr(m->scratch, i));
 
@@ -1625,7 +1618,19 @@ static void pipapo_reclaim_match(struct rcu_head *rcu)
 }
 
 /**
- * pipapo_commit() - Replace lookup data with current working copy
+ * pipapo_reclaim_match - RCU callback to free fields from old matching data
+ * @rcu:       RCU head
+ */
+static void pipapo_reclaim_match(struct rcu_head *rcu)
+{
+       struct nft_pipapo_match *m;
+
+       m = container_of(rcu, struct nft_pipapo_match, rcu);
+       pipapo_free_match(m);
+}
+
+/**
+ * nft_pipapo_commit() - Replace lookup data with current working copy
  * @set:       nftables API set representation
  *
  * While at it, check if we should perform garbage collection on the working
@@ -1635,7 +1640,7 @@ static void pipapo_reclaim_match(struct rcu_head *rcu)
  * We also need to create a new working copy for subsequent insertions and
  * deletions.
  */
-static void pipapo_commit(const struct nft_set *set)
+static void nft_pipapo_commit(const struct nft_set *set)
 {
        struct nft_pipapo *priv = nft_set_priv(set);
        struct nft_pipapo_match *new_clone, *old;
@@ -1660,6 +1665,26 @@ static void pipapo_commit(const struct nft_set *set)
        priv->clone = new_clone;
 }
 
+static void nft_pipapo_abort(const struct nft_set *set)
+{
+       struct nft_pipapo *priv = nft_set_priv(set);
+       struct nft_pipapo_match *new_clone, *m;
+
+       if (!priv->dirty)
+               return;
+
+       m = rcu_dereference(priv->match);
+
+       new_clone = pipapo_clone(m);
+       if (IS_ERR(new_clone))
+               return;
+
+       priv->dirty = false;
+
+       pipapo_free_match(priv->clone);
+       priv->clone = new_clone;
+}
+
 /**
  * nft_pipapo_activate() - Mark element reference as active given key, commit
  * @net:       Network namespace
@@ -1667,8 +1692,7 @@ static void pipapo_commit(const struct nft_set *set)
  * @elem:      nftables API element representation containing key data
  *
  * On insertion, elements are added to a copy of the matching data currently
- * in use for lookups, and not directly inserted into current lookup data, so
- * we'll take care of that by calling pipapo_commit() here. Both
+ * in use for lookups, and not directly inserted into current lookup data. Both
  * nft_pipapo_insert() and nft_pipapo_activate() are called once for each
  * element, hence we can't purpose either one as a real commit operation.
  */
@@ -1684,8 +1708,6 @@ static void nft_pipapo_activate(const struct net *net,
 
        nft_set_elem_change_active(net, set, &e->ext);
        nft_set_elem_clear_busy(&e->ext);
-
-       pipapo_commit(set);
 }
 
 /**
@@ -1931,7 +1953,6 @@ static void nft_pipapo_remove(const struct net *net, const struct nft_set *set,
                if (i == m->field_count) {
                        priv->dirty = true;
                        pipapo_drop(m, rulemap);
-                       pipapo_commit(set);
                        return;
                }
 
@@ -2230,6 +2251,8 @@ const struct nft_set_type nft_set_pipapo_type = {
                .init           = nft_pipapo_init,
                .destroy        = nft_pipapo_destroy,
                .gc_init        = nft_pipapo_gc_init,
+               .commit         = nft_pipapo_commit,
+               .abort          = nft_pipapo_abort,
                .elemsize       = offsetof(struct nft_pipapo_elem, ext),
        },
 };
@@ -2252,6 +2275,8 @@ const struct nft_set_type nft_set_pipapo_avx2_type = {
                .init           = nft_pipapo_init,
                .destroy        = nft_pipapo_destroy,
                .gc_init        = nft_pipapo_gc_init,
+               .commit         = nft_pipapo_commit,
+               .abort          = nft_pipapo_abort,
                .elemsize       = offsetof(struct nft_pipapo_elem, ext),
        },
 };
index 54c0830..27511c9 100644 (file)
@@ -857,7 +857,8 @@ int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap,
 
        offset -= iter->startbit;
        idx = offset / NETLBL_CATMAP_MAPSIZE;
-       iter->bitmap[idx] |= bitmap << (offset % NETLBL_CATMAP_MAPSIZE);
+       iter->bitmap[idx] |= (NETLBL_CATMAP_MAPTYPE)bitmap
+                            << (offset % NETLBL_CATMAP_MAPSIZE);
 
        return 0;
 }
index 9cc0bc7..abc71a0 100644 (file)
@@ -610,6 +610,7 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p,
        struct flow_offload_tuple tuple = {};
        enum ip_conntrack_info ctinfo;
        struct tcphdr *tcph = NULL;
+       bool force_refresh = false;
        struct flow_offload *flow;
        struct nf_conn *ct;
        u8 dir;
@@ -647,6 +648,7 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p,
                         * established state, then don't refresh.
                         */
                        return false;
+               force_refresh = true;
        }
 
        if (tcph && (unlikely(tcph->fin || tcph->rst))) {
@@ -660,7 +662,12 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p,
        else
                ctinfo = IP_CT_ESTABLISHED_REPLY;
 
-       flow_offload_refresh(nf_ft, flow);
+       flow_offload_refresh(nf_ft, flow, force_refresh);
+       if (!test_bit(IPS_ASSURED_BIT, &ct->status)) {
+               /* Process this flow in SW to allow promoting to ASSURED */
+               return false;
+       }
+
        nf_conntrack_get(&ct->ct_general);
        nf_ct_set(skb, ct, ctinfo);
        if (nf_ft->flags & NF_FLOWTABLE_COUNTER)
index fc945c7..c819b81 100644 (file)
 #include <linux/rtnetlink.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
 #include <linux/slab.h>
+#include <net/ipv6.h>
 #include <net/netlink.h>
 #include <net/pkt_sched.h>
 #include <linux/tc_act/tc_pedit.h>
@@ -327,28 +330,58 @@ static bool offset_valid(struct sk_buff *skb, int offset)
        return true;
 }
 
-static void pedit_skb_hdr_offset(struct sk_buff *skb,
+static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type)
+{
+       const int noff = skb_network_offset(skb);
+       int ret = -EINVAL;
+       struct iphdr _iph;
+
+       switch (skb->protocol) {
+       case htons(ETH_P_IP): {
+               const struct iphdr *iph = skb_header_pointer(skb, noff, sizeof(_iph), &_iph);
+
+               if (!iph)
+                       goto out;
+               *hoffset = noff + iph->ihl * 4;
+               ret = 0;
+               break;
+       }
+       case htons(ETH_P_IPV6):
+               ret = ipv6_find_hdr(skb, hoffset, header_type, NULL, NULL) == header_type ? 0 : -EINVAL;
+               break;
+       }
+out:
+       return ret;
+}
+
+static int pedit_skb_hdr_offset(struct sk_buff *skb,
                                 enum pedit_header_type htype, int *hoffset)
 {
+       int ret = -EINVAL;
        /* 'htype' is validated in the netlink parsing */
        switch (htype) {
        case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
-               if (skb_mac_header_was_set(skb))
+               if (skb_mac_header_was_set(skb)) {
                        *hoffset = skb_mac_offset(skb);
+                       ret = 0;
+               }
                break;
        case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
        case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
        case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
                *hoffset = skb_network_offset(skb);
+               ret = 0;
                break;
        case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
+               ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_TCP);
+               break;
        case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
-               if (skb_transport_header_was_set(skb))
-                       *hoffset = skb_transport_offset(skb);
+               ret = pedit_l4_skb_offset(skb, hoffset, IPPROTO_UDP);
                break;
        default:
                break;
        }
+       return ret;
 }
 
 TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb,
@@ -384,6 +417,7 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb,
                int hoffset = 0;
                u32 *ptr, hdata;
                u32 val;
+               int rc;
 
                if (tkey_ex) {
                        htype = tkey_ex->htype;
@@ -392,7 +426,11 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb,
                        tkey_ex++;
                }
 
-               pedit_skb_hdr_offset(skb, htype, &hoffset);
+               rc = pedit_skb_hdr_offset(skb, htype, &hoffset);
+               if (rc) {
+                       pr_info_ratelimited("tc action pedit unable to extract header offset for header type (0x%x)\n", htype);
+                       goto bad;
+               }
 
                if (tkey->offmask) {
                        u8 *d, _d;
index c877a63..a193cc7 100644 (file)
@@ -657,8 +657,8 @@ static void __tcf_chain_put(struct tcf_chain *chain, bool by_act,
 {
        struct tcf_block *block = chain->block;
        const struct tcf_proto_ops *tmplt_ops;
+       unsigned int refcnt, non_act_refcnt;
        bool free_block = false;
-       unsigned int refcnt;
        void *tmplt_priv;
 
        mutex_lock(&block->lock);
@@ -678,13 +678,15 @@ static void __tcf_chain_put(struct tcf_chain *chain, bool by_act,
         * save these to temporary variables.
         */
        refcnt = --chain->refcnt;
+       non_act_refcnt = refcnt - chain->action_refcnt;
        tmplt_ops = chain->tmplt_ops;
        tmplt_priv = chain->tmplt_priv;
 
-       /* The last dropped non-action reference will trigger notification. */
-       if (refcnt - chain->action_refcnt == 0 && !by_act) {
-               tc_chain_notify_delete(tmplt_ops, tmplt_priv, chain->index,
-                                      block, NULL, 0, 0, false);
+       if (non_act_refcnt == chain->explicitly_created && !by_act) {
+               if (non_act_refcnt == 0)
+                       tc_chain_notify_delete(tmplt_ops, tmplt_priv,
+                                              chain->index, block, NULL, 0, 0,
+                                              false);
                /* Last reference to chain, no need to lock. */
                chain->flushing = false;
        }
index 4e2e269..d15d50d 100644 (file)
@@ -718,13 +718,19 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp,
                         struct nlattr *est, u32 flags, u32 fl_flags,
                         struct netlink_ext_ack *extack)
 {
-       int err;
+       int err, ifindex = -1;
 
        err = tcf_exts_validate_ex(net, tp, tb, est, &n->exts, flags,
                                   fl_flags, extack);
        if (err < 0)
                return err;
 
+       if (tb[TCA_U32_INDEV]) {
+               ifindex = tcf_change_indev(net, tb[TCA_U32_INDEV], extack);
+               if (ifindex < 0)
+                       return -EINVAL;
+       }
+
        if (tb[TCA_U32_LINK]) {
                u32 handle = nla_get_u32(tb[TCA_U32_LINK]);
                struct tc_u_hnode *ht_down = NULL, *ht_old;
@@ -759,13 +765,9 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp,
                tcf_bind_filter(tp, &n->res, base);
        }
 
-       if (tb[TCA_U32_INDEV]) {
-               int ret;
-               ret = tcf_change_indev(net, tb[TCA_U32_INDEV], extack);
-               if (ret < 0)
-                       return -EINVAL;
-               n->ifindex = ret;
-       }
+       if (ifindex >= 0)
+               n->ifindex = ifindex;
+
        return 0;
 }
 
index e4b6452..aa6b1fe 100644 (file)
@@ -1079,17 +1079,29 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
 
        if (parent == NULL) {
                unsigned int i, num_q, ingress;
+               struct netdev_queue *dev_queue;
 
                ingress = 0;
                num_q = dev->num_tx_queues;
                if ((q && q->flags & TCQ_F_INGRESS) ||
                    (new && new->flags & TCQ_F_INGRESS)) {
-                       num_q = 1;
                        ingress = 1;
-                       if (!dev_ingress_queue(dev)) {
+                       dev_queue = dev_ingress_queue(dev);
+                       if (!dev_queue) {
                                NL_SET_ERR_MSG(extack, "Device does not have an ingress queue");
                                return -ENOENT;
                        }
+
+                       q = rtnl_dereference(dev_queue->qdisc_sleeping);
+
+                       /* This is the counterpart of that qdisc_refcount_inc_nz() call in
+                        * __tcf_qdisc_find() for filter requests.
+                        */
+                       if (!qdisc_refcount_dec_if_one(q)) {
+                               NL_SET_ERR_MSG(extack,
+                                              "Current ingress or clsact Qdisc has ongoing filter requests");
+                               return -EBUSY;
+                       }
                }
 
                if (dev->flags & IFF_UP)
@@ -1100,18 +1112,26 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
                if (new && new->ops->attach && !ingress)
                        goto skip;
 
-               for (i = 0; i < num_q; i++) {
-                       struct netdev_queue *dev_queue = dev_ingress_queue(dev);
-
-                       if (!ingress)
+               if (!ingress) {
+                       for (i = 0; i < num_q; i++) {
                                dev_queue = netdev_get_tx_queue(dev, i);
+                               old = dev_graft_qdisc(dev_queue, new);
 
-                       old = dev_graft_qdisc(dev_queue, new);
-                       if (new && i > 0)
-                               qdisc_refcount_inc(new);
-
-                       if (!ingress)
+                               if (new && i > 0)
+                                       qdisc_refcount_inc(new);
                                qdisc_put(old);
+                       }
+               } else {
+                       old = dev_graft_qdisc(dev_queue, NULL);
+
+                       /* {ingress,clsact}_destroy() @old before grafting @new to avoid
+                        * unprotected concurrent accesses to net_device::miniq_{in,e}gress
+                        * pointer(s) in mini_qdisc_pair_swap().
+                        */
+                       qdisc_notify(net, skb, n, classid, old, new, extack);
+                       qdisc_destroy(old);
+
+                       dev_graft_qdisc(dev_queue, new);
                }
 
 skip:
@@ -1125,8 +1145,6 @@ skip:
 
                        if (new && new->ops->attach)
                                new->ops->attach(new);
-               } else {
-                       notify_and_destroy(net, skb, n, classid, old, new, extack);
                }
 
                if (dev->flags & IFF_UP)
index 3248259..5d7e23f 100644 (file)
@@ -1046,7 +1046,7 @@ static void qdisc_free_cb(struct rcu_head *head)
        qdisc_free(q);
 }
 
-static void qdisc_destroy(struct Qdisc *qdisc)
+static void __qdisc_destroy(struct Qdisc *qdisc)
 {
        const struct Qdisc_ops  *ops = qdisc->ops;
 
@@ -1070,6 +1070,14 @@ static void qdisc_destroy(struct Qdisc *qdisc)
        call_rcu(&qdisc->rcu, qdisc_free_cb);
 }
 
+void qdisc_destroy(struct Qdisc *qdisc)
+{
+       if (qdisc->flags & TCQ_F_BUILTIN)
+               return;
+
+       __qdisc_destroy(qdisc);
+}
+
 void qdisc_put(struct Qdisc *qdisc)
 {
        if (!qdisc)
@@ -1079,7 +1087,7 @@ void qdisc_put(struct Qdisc *qdisc)
            !refcount_dec_and_test(&qdisc->refcnt))
                return;
 
-       qdisc_destroy(qdisc);
+       __qdisc_destroy(qdisc);
 }
 EXPORT_SYMBOL(qdisc_put);
 
@@ -1094,7 +1102,7 @@ void qdisc_put_unlocked(struct Qdisc *qdisc)
            !refcount_dec_and_rtnl_lock(&qdisc->refcnt))
                return;
 
-       qdisc_destroy(qdisc);
+       __qdisc_destroy(qdisc);
        rtnl_unlock();
 }
 EXPORT_SYMBOL(qdisc_put_unlocked);
index dd7dea2..cf0e61e 100644 (file)
@@ -797,6 +797,9 @@ static struct sk_buff *taprio_dequeue_tc_priority(struct Qdisc *sch,
 
                        taprio_next_tc_txq(dev, tc, &q->cur_txq[tc]);
 
+                       if (q->cur_txq[tc] >= dev->num_tx_queues)
+                               q->cur_txq[tc] = first_txq;
+
                        if (skb)
                                return skb;
                } while (q->cur_txq[tc] != first_txq);
index 7fbeb99..23d6633 100644 (file)
@@ -1250,7 +1250,10 @@ static int sctp_side_effects(enum sctp_event_type event_type,
        default:
                pr_err("impossible disposition %d in state %d, event_type %d, event_id %d\n",
                       status, state, event_type, subtype.chunk);
-               BUG();
+               error = status;
+               if (error >= 0)
+                       error = -EINVAL;
+               WARN_ON_ONCE(1);
                break;
        }
 
index 97f1155..08fdf12 100644 (file)
@@ -4482,7 +4482,7 @@ enum sctp_disposition sctp_sf_eat_auth(struct net *net,
                                    SCTP_AUTH_NEW_KEY, GFP_ATOMIC);
 
                if (!ev)
-                       return -ENOMEM;
+                       return SCTP_DISPOSITION_NOMEM;
 
                sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
                                SCTP_ULPEVENT(ev));
index 5388140..cdcd273 100644 (file)
@@ -1258,7 +1258,7 @@ int tipc_nl_media_get(struct sk_buff *skb, struct genl_info *info)
        struct tipc_nl_msg msg;
        struct tipc_media *media;
        struct sk_buff *rep;
-       struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1];
+       struct nlattr *attrs[TIPC_NLA_MEDIA_MAX + 1];
 
        if (!info->attrs[TIPC_NLA_MEDIA])
                return -EINVAL;
@@ -1307,7 +1307,7 @@ int __tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info)
        int err;
        char *name;
        struct tipc_media *m;
-       struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1];
+       struct nlattr *attrs[TIPC_NLA_MEDIA_MAX + 1];
 
        if (!info->attrs[TIPC_NLA_MEDIA])
                return -EINVAL;
index 2e497cf..69b5087 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Portions of this file
  * Copyright(c) 2016-2017 Intel Deutschland GmbH
- * Copyright (C) 2018, 2021-2022 Intel Corporation
+ * Copyright (C) 2018, 2021-2023 Intel Corporation
  */
 #ifndef __CFG80211_RDEV_OPS
 #define __CFG80211_RDEV_OPS
@@ -1441,8 +1441,8 @@ rdev_del_intf_link(struct cfg80211_registered_device *rdev,
                   unsigned int link_id)
 {
        trace_rdev_del_intf_link(&rdev->wiphy, wdev, link_id);
-       if (rdev->ops->add_intf_link)
-               rdev->ops->add_intf_link(&rdev->wiphy, wdev, link_id);
+       if (rdev->ops->del_intf_link)
+               rdev->ops->del_intf_link(&rdev->wiphy, wdev, link_id);
        trace_rdev_return_void(&rdev->wiphy);
 }
 
index 949e1fb..26f11e4 100644 (file)
@@ -2404,11 +2404,8 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
                case NL80211_IFTYPE_P2P_GO:
                case NL80211_IFTYPE_ADHOC:
                case NL80211_IFTYPE_MESH_POINT:
-                       wiphy_lock(wiphy);
                        ret = cfg80211_reg_can_beacon_relax(wiphy, &chandef,
                                                            iftype);
-                       wiphy_unlock(wiphy);
-
                        if (!ret)
                                return ret;
                        break;
index 3bc0c30..9755ef2 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright 2017      Intel Deutschland GmbH
- * Copyright (C) 2018-2022 Intel Corporation
+ * Copyright (C) 2018-2023 Intel Corporation
  */
 #include <linux/export.h>
 #include <linux/bitops.h>
@@ -2558,6 +2558,13 @@ void cfg80211_remove_links(struct wireless_dev *wdev)
 {
        unsigned int link_id;
 
+       /*
+        * links are controlled by upper layers (userspace/cfg)
+        * only for AP mode, so only remove them here for AP
+        */
+       if (wdev->iftype != NL80211_IFTYPE_AP)
+               return;
+
        wdev_lock(wdev);
        if (wdev->valid_links) {
                for_each_valid_link(wdev, link_id)
index 07efb38..f2940b2 100644 (file)
@@ -37,6 +37,7 @@ struct seq_oss_midi {
        struct snd_midi_event *coder;   /* MIDI event coder */
        struct seq_oss_devinfo *devinfo;        /* assigned OSSseq device */
        snd_use_lock_t use_lock;
+       struct mutex open_mutex;
 };
 
 
@@ -172,6 +173,7 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
        mdev->flags = pinfo->capability;
        mdev->opened = 0;
        snd_use_lock_init(&mdev->use_lock);
+       mutex_init(&mdev->open_mutex);
 
        /* copy and truncate the name of synth device */
        strscpy(mdev->name, pinfo->name, sizeof(mdev->name));
@@ -322,15 +324,17 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
        int perm;
        struct seq_oss_midi *mdev;
        struct snd_seq_port_subscribe subs;
+       int err;
 
        mdev = get_mididev(dp, dev);
        if (!mdev)
                return -ENODEV;
 
+       mutex_lock(&mdev->open_mutex);
        /* already used? */
        if (mdev->opened && mdev->devinfo != dp) {
-               snd_use_lock_free(&mdev->use_lock);
-               return -EBUSY;
+               err = -EBUSY;
+               goto unlock;
        }
 
        perm = 0;
@@ -340,14 +344,14 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
                perm |= PERM_READ;
        perm &= mdev->flags;
        if (perm == 0) {
-               snd_use_lock_free(&mdev->use_lock);
-               return -ENXIO;
+               err = -ENXIO;
+               goto unlock;
        }
 
        /* already opened? */
        if ((mdev->opened & perm) == perm) {
-               snd_use_lock_free(&mdev->use_lock);
-               return 0;
+               err = 0;
+               goto unlock;
        }
 
        perm &= ~mdev->opened;
@@ -372,13 +376,17 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
        }
 
        if (! mdev->opened) {
-               snd_use_lock_free(&mdev->use_lock);
-               return -ENXIO;
+               err = -ENXIO;
+               goto unlock;
        }
 
        mdev->devinfo = dp;
+       err = 0;
+
+ unlock:
+       mutex_unlock(&mdev->open_mutex);
        snd_use_lock_free(&mdev->use_lock);
-       return 0;
+       return err;
 }
 
 /*
@@ -393,10 +401,9 @@ snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
        mdev = get_mididev(dp, dev);
        if (!mdev)
                return -ENODEV;
-       if (! mdev->opened || mdev->devinfo != dp) {
-               snd_use_lock_free(&mdev->use_lock);
-               return 0;
-       }
+       mutex_lock(&mdev->open_mutex);
+       if (!mdev->opened || mdev->devinfo != dp)
+               goto unlock;
 
        memset(&subs, 0, sizeof(subs));
        if (mdev->opened & PERM_WRITE) {
@@ -415,6 +422,8 @@ snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
        mdev->opened = 0;
        mdev->devinfo = NULL;
 
+ unlock:
+       mutex_unlock(&mdev->open_mutex);
        snd_use_lock_free(&mdev->use_lock);
        return 0;
 }
index a5d55a7..308ec70 100644 (file)
@@ -11740,6 +11740,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON),
        SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26),
        SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
+       SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB),
 
 #if 0
        /* Below is a quirk table taken from the old code.
index 468c8e7..0b69ceb 100644 (file)
@@ -117,6 +117,9 @@ int tegra_pcm_open(struct snd_soc_component *component,
                return ret;
        }
 
+       /* Set wait time to 500ms by default */
+       substream->wait_time = 500;
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(tegra_pcm_open);
index eec5232..08bf535 100644 (file)
@@ -650,6 +650,10 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
                goto unlock;
        }
 
+       ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0);
+       if (ret < 0)
+               goto unlock;
+
  again:
        if (subs->sync_endpoint) {
                ret = snd_usb_endpoint_prepare(chip, subs->sync_endpoint);
index 3ecd1ba..6cf55b7 100644 (file)
@@ -2191,6 +2191,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
                   QUIRK_FLAG_DSD_RAW),
        VENDOR_FLG(0x2ab6, /* T+A devices */
                   QUIRK_FLAG_DSD_RAW),
+       VENDOR_FLG(0x3336, /* HEM devices */
+                  QUIRK_FLAG_DSD_RAW),
        VENDOR_FLG(0x3353, /* Khadas devices */
                   QUIRK_FLAG_DSD_RAW),
        VENDOR_FLG(0x3842, /* EVGA */
index caf32a9..7527f73 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 
-CFLAGS += -I. -I../../include -g -Og -Wall -D_LGPL_SOURCE -fsanitize=address \
-         -fsanitize=undefined
+CFLAGS += -I. -I../../include -I../../../lib -g -Og -Wall \
+         -D_LGPL_SOURCE -fsanitize=address -fsanitize=undefined
 LDFLAGS += -fsanitize=address -fsanitize=undefined
 LDLIBS+= -lpthread -lurcu
 TARGETS = main idr-test multiorder xarray maple
@@ -49,6 +49,7 @@ $(OFILES): Makefile *.h */*.h generated/map-shift.h generated/bit-length.h \
        ../../../include/linux/xarray.h \
        ../../../include/linux/maple_tree.h \
        ../../../include/linux/radix-tree.h \
+       ../../../lib/radix-tree.h \
        ../../../include/linux/idr.h
 
 radix-tree.c: ../../../lib/radix-tree.c
index 432fe84..48584a5 100755 (executable)
@@ -84,8 +84,9 @@ h2_destroy()
 
 router_rp1_200_create()
 {
-       ip link add name $rp1.200 up \
-               link $rp1 addrgenmode eui64 type vlan id 200
+       ip link add name $rp1.200 link $rp1 type vlan id 200
+       ip link set dev $rp1.200 addrgenmode eui64
+       ip link set dev $rp1.200 up
        ip address add dev $rp1.200 192.0.2.2/28
        ip address add dev $rp1.200 2001:db8:1::2/64
        ip stats set dev $rp1.200 l3_stats on
@@ -256,9 +257,11 @@ reapply_config()
 
        router_rp1_200_destroy
 
-       ip link add name $rp1.200 link $rp1 addrgenmode none type vlan id 200
+       ip link add name $rp1.200 link $rp1 type vlan id 200
+       ip link set dev $rp1.200 addrgenmode none
        ip stats set dev $rp1.200 l3_stats on
-       ip link set dev $rp1.200 up addrgenmode eui64
+       ip link set dev $rp1.200 addrgenmode eui64
+       ip link set dev $rp1.200 up
        ip address add dev $rp1.200 192.0.2.2/28
        ip address add dev $rp1.200 2001:db8:1::2/64
 }
index 38021a0..6032f9b 100644 (file)
@@ -1,3 +1,4 @@
+CONFIG_KALLSYMS=y
 CONFIG_MPTCP=y
 CONFIG_IPV6=y
 CONFIG_MPTCP_IPV6=y
index 4eacdb1..fa9e09a 100755 (executable)
@@ -55,16 +55,20 @@ __chk_nr()
 {
        local command="$1"
        local expected=$2
-       local msg nr
+       local msg="$3"
+       local skip="${4:-SKIP}"
+       local nr
 
-       shift 2
-       msg=$*
        nr=$(eval $command)
 
        printf "%-50s" "$msg"
        if [ $nr != $expected ]; then
-               echo "[ fail ] expected $expected found $nr"
-               ret=$test_cnt
+               if [ $nr = "$skip" ] && ! mptcp_lib_expect_all_features; then
+                       echo "[ skip ] Feature probably not supported"
+               else
+                       echo "[ fail ] expected $expected found $nr"
+                       ret=$test_cnt
+               fi
        else
                echo "[  ok  ]"
        fi
@@ -76,12 +80,12 @@ __chk_msk_nr()
        local condition=$1
        shift 1
 
-       __chk_nr "ss -inmHMN $ns | $condition" $*
+       __chk_nr "ss -inmHMN $ns | $condition" "$@"
 }
 
 chk_msk_nr()
 {
-       __chk_msk_nr "grep -c token:" $*
+       __chk_msk_nr "grep -c token:" "$@"
 }
 
 wait_msk_nr()
@@ -119,37 +123,26 @@ wait_msk_nr()
 
 chk_msk_fallback_nr()
 {
-               __chk_msk_nr "grep -c fallback" $*
+       __chk_msk_nr "grep -c fallback" "$@"
 }
 
 chk_msk_remote_key_nr()
 {
-               __chk_msk_nr "grep -c remote_key" $*
+       __chk_msk_nr "grep -c remote_key" "$@"
 }
 
 __chk_listen()
 {
        local filter="$1"
        local expected=$2
+       local msg="$3"
 
-       shift 2
-       msg=$*
-
-       nr=$(ss -N $ns -Ml "$filter" | grep -c LISTEN)
-       printf "%-50s" "$msg"
-
-       if [ $nr != $expected ]; then
-               echo "[ fail ] expected $expected found $nr"
-               ret=$test_cnt
-       else
-               echo "[  ok  ]"
-       fi
+       __chk_nr "ss -N $ns -Ml '$filter' | grep -c LISTEN" "$expected" "$msg" 0
 }
 
 chk_msk_listen()
 {
        lport=$1
-       local msg="check for listen socket"
 
        # destination port search should always return empty list
        __chk_listen "dport $lport" 0 "listen match for dport $lport"
@@ -167,10 +160,9 @@ chk_msk_listen()
 chk_msk_inuse()
 {
        local expected=$1
+       local msg="$2"
        local listen_nr
 
-       shift 1
-
        listen_nr=$(ss -N "${ns}" -Ml | grep -c LISTEN)
        expected=$((expected + listen_nr))
 
@@ -181,7 +173,7 @@ chk_msk_inuse()
                sleep 0.1
        done
 
-       __chk_nr get_msk_inuse $expected $*
+       __chk_nr get_msk_inuse $expected "$msg" 0
 }
 
 # $1: ns, $2: port
index c1f7bac..773dd77 100755 (executable)
@@ -144,6 +144,7 @@ cleanup()
 }
 
 mptcp_lib_check_mptcp
+mptcp_lib_check_kallsyms
 
 ip -Version > /dev/null 2>&1
 if [ $? -ne 0 ];then
@@ -695,6 +696,15 @@ run_test_transparent()
                return 0
        fi
 
+       # IP(V6)_TRANSPARENT has been added after TOS support which came with
+       # the required infrastructure in MPTCP sockopt code. To support TOS, the
+       # following function has been exported (T). Not great but better than
+       # checking for a specific kernel version.
+       if ! mptcp_lib_kallsyms_has "T __ip_sock_set_tos$"; then
+               echo "INFO: ${msg} not supported by the kernel: SKIP"
+               return
+       fi
+
 ip netns exec "$listener_ns" nft -f /dev/stdin <<"EOF"
 flush ruleset
 table inet mangle {
@@ -767,6 +777,11 @@ run_tests_peekmode()
 
 run_tests_mptfo()
 {
+       if ! mptcp_lib_kallsyms_has "mptcp_fastopen_"; then
+               echo "INFO: TFO not supported by the kernel: SKIP"
+               return
+       fi
+
        echo "INFO: with MPTFO start"
        ip netns exec "$ns1" sysctl -q net.ipv4.tcp_fastopen=2
        ip netns exec "$ns2" sysctl -q net.ipv4.tcp_fastopen=1
@@ -787,6 +802,11 @@ run_tests_disconnect()
        local old_cin=$cin
        local old_sin=$sin
 
+       if ! mptcp_lib_kallsyms_has "mptcp_pm_data_reset$"; then
+               echo "INFO: Full disconnect not supported: SKIP"
+               return
+       fi
+
        cat $cin $cin $cin > "$cin".disconnect
 
        # force do_transfer to cope with the multiple tranmissions
index 29f0c99..0ae8caf 100755 (executable)
@@ -25,6 +25,8 @@ capout=""
 ns1=""
 ns2=""
 ksft_skip=4
+iptables="iptables"
+ip6tables="ip6tables"
 timeout_poll=30
 timeout_test=$((timeout_poll * 2 + 1))
 capture=0
@@ -82,7 +84,7 @@ init_partial()
                ip netns add $netns || exit $ksft_skip
                ip -net $netns link set lo up
                ip netns exec $netns sysctl -q net.mptcp.enabled=1
-               ip netns exec $netns sysctl -q net.mptcp.pm_type=0
+               ip netns exec $netns sysctl -q net.mptcp.pm_type=0 2>/dev/null || true
                ip netns exec $netns sysctl -q net.ipv4.conf.all.rp_filter=0
                ip netns exec $netns sysctl -q net.ipv4.conf.default.rp_filter=0
                if [ $checksum -eq 1 ]; then
@@ -140,13 +142,18 @@ cleanup_partial()
 check_tools()
 {
        mptcp_lib_check_mptcp
+       mptcp_lib_check_kallsyms
 
        if ! ip -Version &> /dev/null; then
                echo "SKIP: Could not run test without ip tool"
                exit $ksft_skip
        fi
 
-       if ! iptables -V &> /dev/null; then
+       # Use the legacy version if available to support old kernel versions
+       if iptables-legacy -V &> /dev/null; then
+               iptables="iptables-legacy"
+               ip6tables="ip6tables-legacy"
+       elif ! iptables -V &> /dev/null; then
                echo "SKIP: Could not run all tests without iptables tool"
                exit $ksft_skip
        fi
@@ -185,6 +192,32 @@ cleanup()
        cleanup_partial
 }
 
+# $1: msg
+print_title()
+{
+       printf "%03u %-36s %s" "${TEST_COUNT}" "${TEST_NAME}" "${1}"
+}
+
+# [ $1: fail msg ]
+mark_as_skipped()
+{
+       local msg="${1:-"Feature not supported"}"
+
+       mptcp_lib_fail_if_expected_feature "${msg}"
+
+       print_title "[ skip ] ${msg}"
+       printf "\n"
+}
+
+# $@: condition
+continue_if()
+{
+       if ! "${@}"; then
+               mark_as_skipped
+               return 1
+       fi
+}
+
 skip_test()
 {
        if [ "${#only_tests_ids[@]}" -eq 0 ] && [ "${#only_tests_names[@]}" -eq 0 ]; then
@@ -228,6 +261,19 @@ reset()
        return 0
 }
 
+# $1: test name ; $2: counter to check
+reset_check_counter()
+{
+       reset "${1}" || return 1
+
+       local counter="${2}"
+
+       if ! nstat -asz "${counter}" | grep -wq "${counter}"; then
+               mark_as_skipped "counter '${counter}' is not available"
+               return 1
+       fi
+}
+
 # $1: test name
 reset_with_cookies()
 {
@@ -247,17 +293,21 @@ reset_with_add_addr_timeout()
 
        reset "${1}" || return 1
 
-       tables="iptables"
+       tables="${iptables}"
        if [ $ip -eq 6 ]; then
-               tables="ip6tables"
+               tables="${ip6tables}"
        fi
 
        ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=1
-       ip netns exec $ns2 $tables -A OUTPUT -p tcp \
-               -m tcp --tcp-option 30 \
-               -m bpf --bytecode \
-               "$CBPF_MPTCP_SUBOPTION_ADD_ADDR" \
-               -j DROP
+
+       if ! ip netns exec $ns2 $tables -A OUTPUT -p tcp \
+                       -m tcp --tcp-option 30 \
+                       -m bpf --bytecode \
+                       "$CBPF_MPTCP_SUBOPTION_ADD_ADDR" \
+                       -j DROP; then
+               mark_as_skipped "unable to set the 'add addr' rule"
+               return 1
+       fi
 }
 
 # $1: test name
@@ -301,22 +351,17 @@ reset_with_allow_join_id0()
 #     tc action pedit offset 162 out of bounds
 #
 # Netfilter is used to mark packets with enough data.
-reset_with_fail()
+setup_fail_rules()
 {
-       reset "${1}" || return 1
-
-       ip netns exec $ns1 sysctl -q net.mptcp.checksum_enabled=1
-       ip netns exec $ns2 sysctl -q net.mptcp.checksum_enabled=1
-
        check_invert=1
        validate_checksum=1
-       local i="$2"
-       local ip="${3:-4}"
+       local i="$1"
+       local ip="${2:-4}"
        local tables
 
-       tables="iptables"
+       tables="${iptables}"
        if [ $ip -eq 6 ]; then
-               tables="ip6tables"
+               tables="${ip6tables}"
        fi
 
        ip netns exec $ns2 $tables \
@@ -326,15 +371,32 @@ reset_with_fail()
                -p tcp \
                -m length --length 150:9999 \
                -m statistic --mode nth --packet 1 --every 99999 \
-               -j MARK --set-mark 42 || exit 1
+               -j MARK --set-mark 42 || return ${ksft_skip}
 
-       tc -n $ns2 qdisc add dev ns2eth$i clsact || exit 1
+       tc -n $ns2 qdisc add dev ns2eth$i clsact || return ${ksft_skip}
        tc -n $ns2 filter add dev ns2eth$i egress \
                protocol ip prio 1000 \
                handle 42 fw \
                action pedit munge offset 148 u8 invert \
                pipe csum tcp \
-               index 100 || exit 1
+               index 100 || return ${ksft_skip}
+}
+
+reset_with_fail()
+{
+       reset_check_counter "${1}" "MPTcpExtInfiniteMapTx" || return 1
+       shift
+
+       ip netns exec $ns1 sysctl -q net.mptcp.checksum_enabled=1
+       ip netns exec $ns2 sysctl -q net.mptcp.checksum_enabled=1
+
+       local rc=0
+       setup_fail_rules "${@}" || rc=$?
+
+       if [ ${rc} -eq ${ksft_skip} ]; then
+               mark_as_skipped "unable to set the 'fail' rules"
+               return 1
+       fi
 }
 
 reset_with_events()
@@ -349,6 +411,25 @@ reset_with_events()
        evts_ns2_pid=$!
 }
 
+reset_with_tcp_filter()
+{
+       reset "${1}" || return 1
+       shift
+
+       local ns="${!1}"
+       local src="${2}"
+       local target="${3}"
+
+       if ! ip netns exec "${ns}" ${iptables} \
+                       -A INPUT \
+                       -s "${src}" \
+                       -p tcp \
+                       -j "${target}"; then
+               mark_as_skipped "unable to set the filter rules"
+               return 1
+       fi
+}
+
 fail_test()
 {
        ret=1
@@ -467,11 +548,25 @@ wait_local_port_listen()
        done
 }
 
-rm_addr_count()
+# $1: ns ; $2: counter
+get_counter()
 {
-       local ns=${1}
+       local ns="${1}"
+       local counter="${2}"
+       local count
 
-       ip netns exec ${ns} nstat -as | grep MPTcpExtRmAddr | awk '{print $2}'
+       count=$(ip netns exec ${ns} nstat -asz "${counter}" | awk 'NR==1 {next} {print $2}')
+       if [ -z "${count}" ]; then
+               mptcp_lib_fail_if_expected_feature "${counter} counter"
+               return 1
+       fi
+
+       echo "${count}"
+}
+
+rm_addr_count()
+{
+       get_counter "${1}" "MPTcpExtRmAddr"
 }
 
 # $1: ns, $2: old rm_addr counter in $ns
@@ -494,11 +589,11 @@ wait_mpj()
        local ns="${1}"
        local cnt old_cnt
 
-       old_cnt=$(ip netns exec ${ns} nstat -as | grep MPJoinAckRx | awk '{print $2}')
+       old_cnt=$(get_counter ${ns} "MPTcpExtMPJoinAckRx")
 
        local i
        for i in $(seq 10); do
-               cnt=$(ip netns exec ${ns} nstat -as | grep MPJoinAckRx | awk '{print $2}')
+               cnt=$(get_counter ${ns} "MPTcpExtMPJoinAckRx")
                [ "$cnt" = "${old_cnt}" ] || break
                sleep 0.1
        done
@@ -698,15 +793,6 @@ pm_nl_check_endpoint()
        fi
 }
 
-filter_tcp_from()
-{
-       local ns="${1}"
-       local src="${2}"
-       local target="${3}"
-
-       ip netns exec "${ns}" iptables -A INPUT -s "${src}" -p tcp -j "${target}"
-}
-
 do_transfer()
 {
        local listener_ns="$1"
@@ -1157,12 +1243,13 @@ chk_csum_nr()
        fi
 
        printf "%-${nr_blank}s %s" " " "sum"
-       count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtDataCsumErr | awk '{print $2}')
-       [ -z "$count" ] && count=0
+       count=$(get_counter ${ns1} "MPTcpExtDataCsumErr")
        if [ "$count" != "$csum_ns1" ]; then
                extra_msg="$extra_msg ns1=$count"
        fi
-       if { [ "$count" != $csum_ns1 ] && [ $allow_multi_errors_ns1 -eq 0 ]; } ||
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif { [ "$count" != $csum_ns1 ] && [ $allow_multi_errors_ns1 -eq 0 ]; } ||
           { [ "$count" -lt $csum_ns1 ] && [ $allow_multi_errors_ns1 -eq 1 ]; }; then
                echo "[fail] got $count data checksum error[s] expected $csum_ns1"
                fail_test
@@ -1171,12 +1258,13 @@ chk_csum_nr()
                echo -n "[ ok ]"
        fi
        echo -n " - csum  "
-       count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtDataCsumErr | awk '{print $2}')
-       [ -z "$count" ] && count=0
+       count=$(get_counter ${ns2} "MPTcpExtDataCsumErr")
        if [ "$count" != "$csum_ns2" ]; then
                extra_msg="$extra_msg ns2=$count"
        fi
-       if { [ "$count" != $csum_ns2 ] && [ $allow_multi_errors_ns2 -eq 0 ]; } ||
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif { [ "$count" != $csum_ns2 ] && [ $allow_multi_errors_ns2 -eq 0 ]; } ||
           { [ "$count" -lt $csum_ns2 ] && [ $allow_multi_errors_ns2 -eq 1 ]; }; then
                echo "[fail] got $count data checksum error[s] expected $csum_ns2"
                fail_test
@@ -1218,12 +1306,13 @@ chk_fail_nr()
        fi
 
        printf "%-${nr_blank}s %s" " " "ftx"
-       count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPFailTx | awk '{print $2}')
-       [ -z "$count" ] && count=0
+       count=$(get_counter ${ns_tx} "MPTcpExtMPFailTx")
        if [ "$count" != "$fail_tx" ]; then
                extra_msg="$extra_msg,tx=$count"
        fi
-       if { [ "$count" != "$fail_tx" ] && [ $allow_tx_lost -eq 0 ]; } ||
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif { [ "$count" != "$fail_tx" ] && [ $allow_tx_lost -eq 0 ]; } ||
           { [ "$count" -gt "$fail_tx" ] && [ $allow_tx_lost -eq 1 ]; }; then
                echo "[fail] got $count MP_FAIL[s] TX expected $fail_tx"
                fail_test
@@ -1233,12 +1322,13 @@ chk_fail_nr()
        fi
 
        echo -n " - failrx"
-       count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPFailRx | awk '{print $2}')
-       [ -z "$count" ] && count=0
+       count=$(get_counter ${ns_rx} "MPTcpExtMPFailRx")
        if [ "$count" != "$fail_rx" ]; then
                extra_msg="$extra_msg,rx=$count"
        fi
-       if { [ "$count" != "$fail_rx" ] && [ $allow_rx_lost -eq 0 ]; } ||
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif { [ "$count" != "$fail_rx" ] && [ $allow_rx_lost -eq 0 ]; } ||
           { [ "$count" -gt "$fail_rx" ] && [ $allow_rx_lost -eq 1 ]; }; then
                echo "[fail] got $count MP_FAIL[s] RX expected $fail_rx"
                fail_test
@@ -1270,10 +1360,11 @@ chk_fclose_nr()
        fi
 
        printf "%-${nr_blank}s %s" " " "ctx"
-       count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPFastcloseTx | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       [ "$count" != "$fclose_tx" ] && extra_msg="$extra_msg,tx=$count"
-       if [ "$count" != "$fclose_tx" ]; then
+       count=$(get_counter ${ns_tx} "MPTcpExtMPFastcloseTx")
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif [ "$count" != "$fclose_tx" ]; then
+               extra_msg="$extra_msg,tx=$count"
                echo "[fail] got $count MP_FASTCLOSE[s] TX expected $fclose_tx"
                fail_test
                dump_stats=1
@@ -1282,10 +1373,11 @@ chk_fclose_nr()
        fi
 
        echo -n " - fclzrx"
-       count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPFastcloseRx | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       [ "$count" != "$fclose_rx" ] && extra_msg="$extra_msg,rx=$count"
-       if [ "$count" != "$fclose_rx" ]; then
+       count=$(get_counter ${ns_rx} "MPTcpExtMPFastcloseRx")
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif [ "$count" != "$fclose_rx" ]; then
+               extra_msg="$extra_msg,rx=$count"
                echo "[fail] got $count MP_FASTCLOSE[s] RX expected $fclose_rx"
                fail_test
                dump_stats=1
@@ -1316,9 +1408,10 @@ chk_rst_nr()
        fi
 
        printf "%-${nr_blank}s %s" " " "rtx"
-       count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPRstTx | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       if [ $count -lt $rst_tx ]; then
+       count=$(get_counter ${ns_tx} "MPTcpExtMPRstTx")
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif [ $count -lt $rst_tx ]; then
                echo "[fail] got $count MP_RST[s] TX expected $rst_tx"
                fail_test
                dump_stats=1
@@ -1327,9 +1420,10 @@ chk_rst_nr()
        fi
 
        echo -n " - rstrx "
-       count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPRstRx | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       if [ "$count" -lt "$rst_rx" ]; then
+       count=$(get_counter ${ns_rx} "MPTcpExtMPRstRx")
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif [ "$count" -lt "$rst_rx" ]; then
                echo "[fail] got $count MP_RST[s] RX expected $rst_rx"
                fail_test
                dump_stats=1
@@ -1350,9 +1444,10 @@ chk_infi_nr()
        local dump_stats
 
        printf "%-${nr_blank}s %s" " " "itx"
-       count=$(ip netns exec $ns2 nstat -as | grep InfiniteMapTx | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       if [ "$count" != "$infi_tx" ]; then
+       count=$(get_counter ${ns2} "MPTcpExtInfiniteMapTx")
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif [ "$count" != "$infi_tx" ]; then
                echo "[fail] got $count infinite map[s] TX expected $infi_tx"
                fail_test
                dump_stats=1
@@ -1361,9 +1456,10 @@ chk_infi_nr()
        fi
 
        echo -n " - infirx"
-       count=$(ip netns exec $ns1 nstat -as | grep InfiniteMapRx | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       if [ "$count" != "$infi_rx" ]; then
+       count=$(get_counter ${ns1} "MPTcpExtInfiniteMapRx")
+       if [ -z "$count" ]; then
+               echo "[skip]"
+       elif [ "$count" != "$infi_rx" ]; then
                echo "[fail] got $count infinite map[s] RX expected $infi_rx"
                fail_test
                dump_stats=1
@@ -1395,9 +1491,10 @@ chk_join_nr()
        fi
 
        printf "%03u %-36s %s" "${TEST_COUNT}" "${title}" "syn"
-       count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinSynRx | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       if [ "$count" != "$syn_nr" ]; then
+       count=$(get_counter ${ns1} "MPTcpExtMPJoinSynRx")
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif [ "$count" != "$syn_nr" ]; then
                echo "[fail] got $count JOIN[s] syn expected $syn_nr"
                fail_test
                dump_stats=1
@@ -1407,9 +1504,10 @@ chk_join_nr()
 
        echo -n " - synack"
        with_cookie=$(ip netns exec $ns2 sysctl -n net.ipv4.tcp_syncookies)
-       count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtMPJoinSynAckRx | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       if [ "$count" != "$syn_ack_nr" ]; then
+       count=$(get_counter ${ns2} "MPTcpExtMPJoinSynAckRx")
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif [ "$count" != "$syn_ack_nr" ]; then
                # simult connections exceeding the limit with cookie enabled could go up to
                # synack validation as the conn limit can be enforced reliably only after
                # the subflow creation
@@ -1425,9 +1523,10 @@ chk_join_nr()
        fi
 
        echo -n " - ack"
-       count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinAckRx | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       if [ "$count" != "$ack_nr" ]; then
+       count=$(get_counter ${ns1} "MPTcpExtMPJoinAckRx")
+       if [ -z "$count" ]; then
+               echo "[skip]"
+       elif [ "$count" != "$ack_nr" ]; then
                echo "[fail] got $count JOIN[s] ack expected $ack_nr"
                fail_test
                dump_stats=1
@@ -1459,12 +1558,12 @@ chk_stale_nr()
        local recover_nr
 
        printf "%-${nr_blank}s %-18s" " " "stale"
-       stale_nr=$(ip netns exec $ns nstat -as | grep MPTcpExtSubflowStale | awk '{print $2}')
-       [ -z "$stale_nr" ] && stale_nr=0
-       recover_nr=$(ip netns exec $ns nstat -as | grep MPTcpExtSubflowRecover | awk '{print $2}')
-       [ -z "$recover_nr" ] && recover_nr=0
 
-       if [ $stale_nr -lt $stale_min ] ||
+       stale_nr=$(get_counter ${ns} "MPTcpExtSubflowStale")
+       recover_nr=$(get_counter ${ns} "MPTcpExtSubflowRecover")
+       if [ -z "$stale_nr" ] || [ -z "$recover_nr" ]; then
+               echo "[skip]"
+       elif [ $stale_nr -lt $stale_min ] ||
           { [ $stale_max -gt 0 ] && [ $stale_nr -gt $stale_max ]; } ||
           [ $((stale_nr - recover_nr)) -ne $stale_delta ]; then
                echo "[fail] got $stale_nr stale[s] $recover_nr recover[s], " \
@@ -1500,12 +1599,12 @@ chk_add_nr()
        timeout=$(ip netns exec $ns1 sysctl -n net.mptcp.add_addr_timeout)
 
        printf "%-${nr_blank}s %s" " " "add"
-       count=$(ip netns exec $ns2 nstat -as MPTcpExtAddAddr | grep MPTcpExtAddAddr | awk '{print $2}')
-       [ -z "$count" ] && count=0
-
+       count=$(get_counter ${ns2} "MPTcpExtAddAddr")
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
        # if the test configured a short timeout tolerate greater then expected
        # add addrs options, due to retransmissions
-       if [ "$count" != "$add_nr" ] && { [ "$timeout" -gt 1 ] || [ "$count" -lt "$add_nr" ]; }; then
+       elif [ "$count" != "$add_nr" ] && { [ "$timeout" -gt 1 ] || [ "$count" -lt "$add_nr" ]; }; then
                echo "[fail] got $count ADD_ADDR[s] expected $add_nr"
                fail_test
                dump_stats=1
@@ -1514,9 +1613,10 @@ chk_add_nr()
        fi
 
        echo -n " - echo  "
-       count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtEchoAdd | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       if [ "$count" != "$echo_nr" ]; then
+       count=$(get_counter ${ns1} "MPTcpExtEchoAdd")
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif [ "$count" != "$echo_nr" ]; then
                echo "[fail] got $count ADD_ADDR echo[s] expected $echo_nr"
                fail_test
                dump_stats=1
@@ -1526,9 +1626,10 @@ chk_add_nr()
 
        if [ $port_nr -gt 0 ]; then
                echo -n " - pt "
-               count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtPortAdd | awk '{print $2}')
-               [ -z "$count" ] && count=0
-               if [ "$count" != "$port_nr" ]; then
+               count=$(get_counter ${ns2} "MPTcpExtPortAdd")
+               if [ -z "$count" ]; then
+                       echo "[skip]"
+               elif [ "$count" != "$port_nr" ]; then
                        echo "[fail] got $count ADD_ADDR[s] with a port-number expected $port_nr"
                        fail_test
                        dump_stats=1
@@ -1537,10 +1638,10 @@ chk_add_nr()
                fi
 
                printf "%-${nr_blank}s %s" " " "syn"
-               count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinPortSynRx |
-                       awk '{print $2}')
-               [ -z "$count" ] && count=0
-               if [ "$count" != "$syn_nr" ]; then
+               count=$(get_counter ${ns1} "MPTcpExtMPJoinPortSynRx")
+               if [ -z "$count" ]; then
+                       echo -n "[skip]"
+               elif [ "$count" != "$syn_nr" ]; then
                        echo "[fail] got $count JOIN[s] syn with a different \
                                port-number expected $syn_nr"
                        fail_test
@@ -1550,10 +1651,10 @@ chk_add_nr()
                fi
 
                echo -n " - synack"
-               count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtMPJoinPortSynAckRx |
-                       awk '{print $2}')
-               [ -z "$count" ] && count=0
-               if [ "$count" != "$syn_ack_nr" ]; then
+               count=$(get_counter ${ns2} "MPTcpExtMPJoinPortSynAckRx")
+               if [ -z "$count" ]; then
+                       echo -n "[skip]"
+               elif [ "$count" != "$syn_ack_nr" ]; then
                        echo "[fail] got $count JOIN[s] synack with a different \
                                port-number expected $syn_ack_nr"
                        fail_test
@@ -1563,10 +1664,10 @@ chk_add_nr()
                fi
 
                echo -n " - ack"
-               count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinPortAckRx |
-                       awk '{print $2}')
-               [ -z "$count" ] && count=0
-               if [ "$count" != "$ack_nr" ]; then
+               count=$(get_counter ${ns1} "MPTcpExtMPJoinPortAckRx")
+               if [ -z "$count" ]; then
+                       echo "[skip]"
+               elif [ "$count" != "$ack_nr" ]; then
                        echo "[fail] got $count JOIN[s] ack with a different \
                                port-number expected $ack_nr"
                        fail_test
@@ -1576,10 +1677,10 @@ chk_add_nr()
                fi
 
                printf "%-${nr_blank}s %s" " " "syn"
-               count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMismatchPortSynRx |
-                       awk '{print $2}')
-               [ -z "$count" ] && count=0
-               if [ "$count" != "$mis_syn_nr" ]; then
+               count=$(get_counter ${ns1} "MPTcpExtMismatchPortSynRx")
+               if [ -z "$count" ]; then
+                       echo -n "[skip]"
+               elif [ "$count" != "$mis_syn_nr" ]; then
                        echo "[fail] got $count JOIN[s] syn with a mismatched \
                                port-number expected $mis_syn_nr"
                        fail_test
@@ -1589,10 +1690,10 @@ chk_add_nr()
                fi
 
                echo -n " - ack   "
-               count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMismatchPortAckRx |
-                       awk '{print $2}')
-               [ -z "$count" ] && count=0
-               if [ "$count" != "$mis_ack_nr" ]; then
+               count=$(get_counter ${ns1} "MPTcpExtMismatchPortAckRx")
+               if [ -z "$count" ]; then
+                       echo "[skip]"
+               elif [ "$count" != "$mis_ack_nr" ]; then
                        echo "[fail] got $count JOIN[s] ack with a mismatched \
                                port-number expected $mis_ack_nr"
                        fail_test
@@ -1636,9 +1737,10 @@ chk_rm_nr()
        fi
 
        printf "%-${nr_blank}s %s" " " "rm "
-       count=$(ip netns exec $addr_ns nstat -as | grep MPTcpExtRmAddr | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       if [ "$count" != "$rm_addr_nr" ]; then
+       count=$(get_counter ${addr_ns} "MPTcpExtRmAddr")
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif [ "$count" != "$rm_addr_nr" ]; then
                echo "[fail] got $count RM_ADDR[s] expected $rm_addr_nr"
                fail_test
                dump_stats=1
@@ -1647,29 +1749,27 @@ chk_rm_nr()
        fi
 
        echo -n " - rmsf  "
-       count=$(ip netns exec $subflow_ns nstat -as | grep MPTcpExtRmSubflow | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       if [ -n "$simult" ]; then
+       count=$(get_counter ${subflow_ns} "MPTcpExtRmSubflow")
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif [ -n "$simult" ]; then
                local cnt suffix
 
-               cnt=$(ip netns exec $addr_ns nstat -as | grep MPTcpExtRmSubflow | awk '{print $2}')
+               cnt=$(get_counter ${addr_ns} "MPTcpExtRmSubflow")
 
                # in case of simult flush, the subflow removal count on each side is
                # unreliable
-               [ -z "$cnt" ] && cnt=0
                count=$((count + cnt))
                [ "$count" != "$rm_subflow_nr" ] && suffix="$count in [$rm_subflow_nr:$((rm_subflow_nr*2))]"
                if [ $count -ge "$rm_subflow_nr" ] && \
                   [ "$count" -le "$((rm_subflow_nr *2 ))" ]; then
-                       echo "[ ok ] $suffix"
+                       echo -n "[ ok ] $suffix"
                else
                        echo "[fail] got $count RM_SUBFLOW[s] expected in range [$rm_subflow_nr:$((rm_subflow_nr*2))]"
                        fail_test
                        dump_stats=1
                fi
-               return
-       fi
-       if [ "$count" != "$rm_subflow_nr" ]; then
+       elif [ "$count" != "$rm_subflow_nr" ]; then
                echo "[fail] got $count RM_SUBFLOW[s] expected $rm_subflow_nr"
                fail_test
                dump_stats=1
@@ -1690,9 +1790,10 @@ chk_prio_nr()
        local dump_stats
 
        printf "%-${nr_blank}s %s" " " "ptx"
-       count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioTx | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       if [ "$count" != "$mp_prio_nr_tx" ]; then
+       count=$(get_counter ${ns1} "MPTcpExtMPPrioTx")
+       if [ -z "$count" ]; then
+               echo -n "[skip]"
+       elif [ "$count" != "$mp_prio_nr_tx" ]; then
                echo "[fail] got $count MP_PRIO[s] TX expected $mp_prio_nr_tx"
                fail_test
                dump_stats=1
@@ -1701,9 +1802,10 @@ chk_prio_nr()
        fi
 
        echo -n " - prx   "
-       count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioRx | awk '{print $2}')
-       [ -z "$count" ] && count=0
-       if [ "$count" != "$mp_prio_nr_rx" ]; then
+       count=$(get_counter ${ns1} "MPTcpExtMPPrioRx")
+       if [ -z "$count" ]; then
+               echo "[skip]"
+       elif [ "$count" != "$mp_prio_nr_rx" ]; then
                echo "[fail] got $count MP_PRIO[s] RX expected $mp_prio_nr_rx"
                fail_test
                dump_stats=1
@@ -1819,7 +1921,7 @@ wait_attempt_fail()
        while [ $time -lt $timeout_ms ]; do
                local cnt
 
-               cnt=$(ip netns exec $ns nstat -as TcpAttemptFails | grep TcpAttemptFails | awk '{print $2}')
+               cnt=$(get_counter ${ns} "TcpAttemptFails")
 
                [ "$cnt" = 1 ] && return 1
                time=$((time + 100))
@@ -1912,23 +2014,23 @@ subflows_error_tests()
        fi
 
        # multiple subflows, with subflow creation error
-       if reset "multi subflows, with failing subflow"; then
+       if reset_with_tcp_filter "multi subflows, with failing subflow" ns1 10.0.3.2 REJECT &&
+          continue_if mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then
                pm_nl_set_limits $ns1 0 2
                pm_nl_set_limits $ns2 0 2
                pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow
                pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow
-               filter_tcp_from $ns1 10.0.3.2 REJECT
                run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow
                chk_join_nr 1 1 1
        fi
 
        # multiple subflows, with subflow timeout on MPJ
-       if reset "multi subflows, with subflow timeout"; then
+       if reset_with_tcp_filter "multi subflows, with subflow timeout" ns1 10.0.3.2 DROP &&
+          continue_if mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then
                pm_nl_set_limits $ns1 0 2
                pm_nl_set_limits $ns2 0 2
                pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow
                pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow
-               filter_tcp_from $ns1 10.0.3.2 DROP
                run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow
                chk_join_nr 1 1 1
        fi
@@ -1936,11 +2038,11 @@ subflows_error_tests()
        # multiple subflows, check that the endpoint corresponding to
        # closed subflow (due to reset) is not reused if additional
        # subflows are added later
-       if reset "multi subflows, fair usage on close"; then
+       if reset_with_tcp_filter "multi subflows, fair usage on close" ns1 10.0.3.2 REJECT &&
+          continue_if mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then
                pm_nl_set_limits $ns1 0 1
                pm_nl_set_limits $ns2 0 1
                pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow
-               filter_tcp_from $ns1 10.0.3.2 REJECT
                run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow &
 
                # mpj subflow will be in TW after the reset
@@ -2040,11 +2142,18 @@ signal_address_tests()
                # the peer could possibly miss some addr notification, allow retransmission
                ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=1
                run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow
-               chk_join_nr 3 3 3
 
-               # the server will not signal the address terminating
-               # the MPC subflow
-               chk_add_nr 3 3
+               # It is not directly linked to the commit introducing this
+               # symbol but for the parent one which is linked anyway.
+               if ! mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then
+                       chk_join_nr 3 3 2
+                       chk_add_nr 4 4
+               else
+                       chk_join_nr 3 3 3
+                       # the server will not signal the address terminating
+                       # the MPC subflow
+                       chk_add_nr 3 3
+               fi
        fi
 }
 
@@ -2285,7 +2394,12 @@ remove_tests()
                pm_nl_add_endpoint $ns2 10.0.4.2 flags subflow
                run_tests $ns1 $ns2 10.0.1.1 0 -8 -8 slow
                chk_join_nr 3 3 3
-               chk_rm_nr 0 3 simult
+
+               if mptcp_lib_kversion_ge 5.18; then
+                       chk_rm_nr 0 3 simult
+               else
+                       chk_rm_nr 3 3
+               fi
        fi
 
        # addresses flush
@@ -2523,7 +2637,8 @@ v4mapped_tests()
 
 mixed_tests()
 {
-       if reset "IPv4 sockets do not use IPv6 addresses"; then
+       if reset "IPv4 sockets do not use IPv6 addresses" &&
+          continue_if mptcp_lib_kversion_ge 6.3; then
                pm_nl_set_limits $ns1 0 1
                pm_nl_set_limits $ns2 1 1
                pm_nl_add_endpoint $ns1 dead:beef:2::1 flags signal
@@ -2532,7 +2647,8 @@ mixed_tests()
        fi
 
        # Need an IPv6 mptcp socket to allow subflows of both families
-       if reset "simult IPv4 and IPv6 subflows"; then
+       if reset "simult IPv4 and IPv6 subflows" &&
+          continue_if mptcp_lib_kversion_ge 6.3; then
                pm_nl_set_limits $ns1 0 1
                pm_nl_set_limits $ns2 1 1
                pm_nl_add_endpoint $ns1 10.0.1.1 flags signal
@@ -2541,7 +2657,8 @@ mixed_tests()
        fi
 
        # cross families subflows will not be created even in fullmesh mode
-       if reset "simult IPv4 and IPv6 subflows, fullmesh 1x1"; then
+       if reset "simult IPv4 and IPv6 subflows, fullmesh 1x1" &&
+          continue_if mptcp_lib_kversion_ge 6.3; then
                pm_nl_set_limits $ns1 0 4
                pm_nl_set_limits $ns2 1 4
                pm_nl_add_endpoint $ns2 dead:beef:2::2 flags subflow,fullmesh
@@ -2552,7 +2669,8 @@ mixed_tests()
 
        # fullmesh still tries to create all the possibly subflows with
        # matching family
-       if reset "simult IPv4 and IPv6 subflows, fullmesh 2x2"; then
+       if reset "simult IPv4 and IPv6 subflows, fullmesh 2x2" &&
+          continue_if mptcp_lib_kversion_ge 6.3; then
                pm_nl_set_limits $ns1 0 4
                pm_nl_set_limits $ns2 2 4
                pm_nl_add_endpoint $ns1 10.0.2.1 flags signal
@@ -2565,7 +2683,8 @@ mixed_tests()
 backup_tests()
 {
        # single subflow, backup
-       if reset "single subflow, backup"; then
+       if reset "single subflow, backup" &&
+          continue_if mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then
                pm_nl_set_limits $ns1 0 1
                pm_nl_set_limits $ns2 0 1
                pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow,backup
@@ -2575,7 +2694,8 @@ backup_tests()
        fi
 
        # single address, backup
-       if reset "single address, backup"; then
+       if reset "single address, backup" &&
+          continue_if mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then
                pm_nl_set_limits $ns1 0 1
                pm_nl_add_endpoint $ns1 10.0.2.1 flags signal
                pm_nl_set_limits $ns2 1 1
@@ -2586,7 +2706,8 @@ backup_tests()
        fi
 
        # single address with port, backup
-       if reset "single address with port, backup"; then
+       if reset "single address with port, backup" &&
+          continue_if mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then
                pm_nl_set_limits $ns1 0 1
                pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100
                pm_nl_set_limits $ns2 1 1
@@ -2596,14 +2717,16 @@ backup_tests()
                chk_prio_nr 1 1
        fi
 
-       if reset "mpc backup"; then
+       if reset "mpc backup" &&
+          continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then
                pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow,backup
                run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow
                chk_join_nr 0 0 0
                chk_prio_nr 0 1
        fi
 
-       if reset "mpc backup both sides"; then
+       if reset "mpc backup both sides" &&
+          continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then
                pm_nl_add_endpoint $ns1 10.0.1.1 flags subflow,backup
                pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow,backup
                run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow
@@ -2611,14 +2734,16 @@ backup_tests()
                chk_prio_nr 1 1
        fi
 
-       if reset "mpc switch to backup"; then
+       if reset "mpc switch to backup" &&
+          continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then
                pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow
                run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup
                chk_join_nr 0 0 0
                chk_prio_nr 0 1
        fi
 
-       if reset "mpc switch to backup both sides"; then
+       if reset "mpc switch to backup both sides" &&
+          continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then
                pm_nl_add_endpoint $ns1 10.0.1.1 flags subflow
                pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow
                run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup
@@ -2644,38 +2769,41 @@ verify_listener_events()
        local family
        local saddr
        local sport
+       local name
 
        if [ $e_type = $LISTENER_CREATED ]; then
-               stdbuf -o0 -e0 printf "\t\t\t\t\t CREATE_LISTENER %s:%s"\
-                       $e_saddr $e_sport
+               name="LISTENER_CREATED"
        elif [ $e_type = $LISTENER_CLOSED ]; then
-               stdbuf -o0 -e0 printf "\t\t\t\t\t CLOSE_LISTENER %s:%s "\
-                       $e_saddr $e_sport
+               name="LISTENER_CLOSED"
+       else
+               name="$e_type"
        fi
 
-       type=$(grep "type:$e_type," $evt |
-              sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q')
-       family=$(grep "type:$e_type," $evt |
-                sed --unbuffered -n 's/.*\(family:\)\([[:digit:]]*\).*$/\2/p;q')
-       sport=$(grep "type:$e_type," $evt |
-               sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q')
+       printf "%-${nr_blank}s %s %s:%s " " " "$name" "$e_saddr" "$e_sport"
+
+       if ! mptcp_lib_kallsyms_has "mptcp_event_pm_listener$"; then
+               printf "[skip]: event not supported\n"
+               return
+       fi
+
+       type=$(grep "type:$e_type," $evt | sed -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q')
+       family=$(grep "type:$e_type," $evt | sed -n 's/.*\(family:\)\([[:digit:]]*\).*$/\2/p;q')
+       sport=$(grep "type:$e_type," $evt | sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q')
        if [ $family ] && [ $family = $AF_INET6 ]; then
-               saddr=$(grep "type:$e_type," $evt |
-                       sed --unbuffered -n 's/.*\(saddr6:\)\([0-9a-f:.]*\).*$/\2/p;q')
+               saddr=$(grep "type:$e_type," $evt | sed -n 's/.*\(saddr6:\)\([0-9a-f:.]*\).*$/\2/p;q')
        else
-               saddr=$(grep "type:$e_type," $evt |
-                       sed --unbuffered -n 's/.*\(saddr4:\)\([0-9.]*\).*$/\2/p;q')
+               saddr=$(grep "type:$e_type," $evt | sed -n 's/.*\(saddr4:\)\([0-9.]*\).*$/\2/p;q')
        fi
 
        if [ $type ] && [ $type = $e_type ] &&
           [ $family ] && [ $family = $e_family ] &&
           [ $saddr ] && [ $saddr = $e_saddr ] &&
           [ $sport ] && [ $sport = $e_sport ]; then
-               stdbuf -o0 -e0 printf "[ ok ]\n"
+               echo "[ ok ]"
                return 0
        fi
        fail_test
-       stdbuf -o0 -e0 printf "[fail]\n"
+       echo "[fail]"
 }
 
 add_addr_ports_tests()
@@ -2981,7 +3109,8 @@ fullmesh_tests()
        fi
 
        # set fullmesh flag
-       if reset "set fullmesh flag test"; then
+       if reset "set fullmesh flag test" &&
+          continue_if mptcp_lib_kversion_ge 5.18; then
                pm_nl_set_limits $ns1 4 4
                pm_nl_add_endpoint $ns1 10.0.2.1 flags subflow
                pm_nl_set_limits $ns2 4 4
@@ -2991,7 +3120,8 @@ fullmesh_tests()
        fi
 
        # set nofullmesh flag
-       if reset "set nofullmesh flag test"; then
+       if reset "set nofullmesh flag test" &&
+          continue_if mptcp_lib_kversion_ge 5.18; then
                pm_nl_set_limits $ns1 4 4
                pm_nl_add_endpoint $ns1 10.0.2.1 flags subflow,fullmesh
                pm_nl_set_limits $ns2 4 4
@@ -3001,7 +3131,8 @@ fullmesh_tests()
        fi
 
        # set backup,fullmesh flags
-       if reset "set backup,fullmesh flags test"; then
+       if reset "set backup,fullmesh flags test" &&
+          continue_if mptcp_lib_kversion_ge 5.18; then
                pm_nl_set_limits $ns1 4 4
                pm_nl_add_endpoint $ns1 10.0.2.1 flags subflow
                pm_nl_set_limits $ns2 4 4
@@ -3012,7 +3143,8 @@ fullmesh_tests()
        fi
 
        # set nobackup,nofullmesh flags
-       if reset "set nobackup,nofullmesh flags test"; then
+       if reset "set nobackup,nofullmesh flags test" &&
+          continue_if mptcp_lib_kversion_ge 5.18; then
                pm_nl_set_limits $ns1 4 4
                pm_nl_set_limits $ns2 4 4
                pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow,backup,fullmesh
@@ -3025,14 +3157,14 @@ fullmesh_tests()
 
 fastclose_tests()
 {
-       if reset "fastclose test"; then
+       if reset_check_counter "fastclose test" "MPTcpExtMPFastcloseTx"; then
                run_tests $ns1 $ns2 10.0.1.1 1024 0 fastclose_client
                chk_join_nr 0 0 0
                chk_fclose_nr 1 1
                chk_rst_nr 1 1 invert
        fi
 
-       if reset "fastclose server test"; then
+       if reset_check_counter "fastclose server test" "MPTcpExtMPFastcloseRx"; then
                run_tests $ns1 $ns2 10.0.1.1 1024 0 fastclose_server
                chk_join_nr 0 0 0
                chk_fclose_nr 1 1 invert
@@ -3070,7 +3202,8 @@ fail_tests()
 userspace_tests()
 {
        # userspace pm type prevents add_addr
-       if reset "userspace pm type prevents add_addr"; then
+       if reset "userspace pm type prevents add_addr" &&
+          continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then
                set_userspace_pm $ns1
                pm_nl_set_limits $ns1 0 2
                pm_nl_set_limits $ns2 0 2
@@ -3081,7 +3214,8 @@ userspace_tests()
        fi
 
        # userspace pm type does not echo add_addr without daemon
-       if reset "userspace pm no echo w/o daemon"; then
+       if reset "userspace pm no echo w/o daemon" &&
+          continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then
                set_userspace_pm $ns2
                pm_nl_set_limits $ns1 0 2
                pm_nl_set_limits $ns2 0 2
@@ -3092,7 +3226,8 @@ userspace_tests()
        fi
 
        # userspace pm type rejects join
-       if reset "userspace pm type rejects join"; then
+       if reset "userspace pm type rejects join" &&
+          continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then
                set_userspace_pm $ns1
                pm_nl_set_limits $ns1 1 1
                pm_nl_set_limits $ns2 1 1
@@ -3102,7 +3237,8 @@ userspace_tests()
        fi
 
        # userspace pm type does not send join
-       if reset "userspace pm type does not send join"; then
+       if reset "userspace pm type does not send join" &&
+          continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then
                set_userspace_pm $ns2
                pm_nl_set_limits $ns1 1 1
                pm_nl_set_limits $ns2 1 1
@@ -3112,7 +3248,8 @@ userspace_tests()
        fi
 
        # userspace pm type prevents mp_prio
-       if reset "userspace pm type prevents mp_prio"; then
+       if reset "userspace pm type prevents mp_prio" &&
+          continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then
                set_userspace_pm $ns1
                pm_nl_set_limits $ns1 1 1
                pm_nl_set_limits $ns2 1 1
@@ -3123,7 +3260,8 @@ userspace_tests()
        fi
 
        # userspace pm type prevents rm_addr
-       if reset "userspace pm type prevents rm_addr"; then
+       if reset "userspace pm type prevents rm_addr" &&
+          continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then
                set_userspace_pm $ns1
                set_userspace_pm $ns2
                pm_nl_set_limits $ns1 0 1
@@ -3135,7 +3273,8 @@ userspace_tests()
        fi
 
        # userspace pm add & remove address
-       if reset_with_events "userspace pm add & remove address"; then
+       if reset_with_events "userspace pm add & remove address" &&
+          continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then
                set_userspace_pm $ns1
                pm_nl_set_limits $ns2 1 1
                run_tests $ns1 $ns2 10.0.1.1 0 userspace_1 0 slow
@@ -3146,7 +3285,8 @@ userspace_tests()
        fi
 
        # userspace pm create destroy subflow
-       if reset_with_events "userspace pm create destroy subflow"; then
+       if reset_with_events "userspace pm create destroy subflow" &&
+          continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then
                set_userspace_pm $ns2
                pm_nl_set_limits $ns1 0 1
                run_tests $ns1 $ns2 10.0.1.1 0 0 userspace_1 slow
@@ -3158,8 +3298,10 @@ userspace_tests()
 
 endpoint_tests()
 {
+       # subflow_rebuild_header is needed to support the implicit flag
        # userspace pm type prevents add_addr
-       if reset "implicit EP"; then
+       if reset "implicit EP" &&
+          mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then
                pm_nl_set_limits $ns1 2 2
                pm_nl_set_limits $ns2 2 2
                pm_nl_add_endpoint $ns1 10.0.2.1 flags signal
@@ -3179,7 +3321,8 @@ endpoint_tests()
                kill_tests_wait
        fi
 
-       if reset "delete and re-add"; then
+       if reset "delete and re-add" &&
+          mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then
                pm_nl_set_limits $ns1 1 1
                pm_nl_set_limits $ns2 1 1
                pm_nl_add_endpoint $ns2 10.0.2.2 id 2 dev ns2eth2 flags subflow
index 3286536..f32045b 100644 (file)
@@ -38,3 +38,67 @@ mptcp_lib_check_mptcp() {
                exit ${KSFT_SKIP}
        fi
 }
+
+mptcp_lib_check_kallsyms() {
+       if ! mptcp_lib_has_file "/proc/kallsyms"; then
+               echo "SKIP: CONFIG_KALLSYMS is missing"
+               exit ${KSFT_SKIP}
+       fi
+}
+
+# Internal: use mptcp_lib_kallsyms_has() instead
+__mptcp_lib_kallsyms_has() {
+       local sym="${1}"
+
+       mptcp_lib_check_kallsyms
+
+       grep -q " ${sym}" /proc/kallsyms
+}
+
+# $1: part of a symbol to look at, add '$' at the end for full name
+mptcp_lib_kallsyms_has() {
+       local sym="${1}"
+
+       if __mptcp_lib_kallsyms_has "${sym}"; then
+               return 0
+       fi
+
+       mptcp_lib_fail_if_expected_feature "${sym} symbol not found"
+}
+
+# $1: part of a symbol to look at, add '$' at the end for full name
+mptcp_lib_kallsyms_doesnt_have() {
+       local sym="${1}"
+
+       if ! __mptcp_lib_kallsyms_has "${sym}"; then
+               return 0
+       fi
+
+       mptcp_lib_fail_if_expected_feature "${sym} symbol has been found"
+}
+
+# !!!AVOID USING THIS!!!
+# Features might not land in the expected version and features can be backported
+#
+# $1: kernel version, e.g. 6.3
+mptcp_lib_kversion_ge() {
+       local exp_maj="${1%.*}"
+       local exp_min="${1#*.}"
+       local v maj min
+
+       # If the kernel has backported features, set this env var to 1:
+       if [ "${SELFTESTS_MPTCP_LIB_NO_KVERSION_CHECK:-}" = "1" ]; then
+               return 0
+       fi
+
+       v=$(uname -r | cut -d'.' -f1,2)
+       maj=${v%.*}
+       min=${v#*.}
+
+       if   [ "${maj}" -gt "${exp_maj}" ] ||
+          { [ "${maj}" -eq "${exp_maj}" ] && [ "${min}" -ge "${exp_min}" ]; }; then
+               return 0
+       fi
+
+       mptcp_lib_fail_if_expected_feature "kernel version ${1} lower than ${v}"
+}
index ae61f39..b35148e 100644 (file)
@@ -87,6 +87,10 @@ struct so_state {
        uint64_t tcpi_rcv_delta;
 };
 
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
 static void die_perror(const char *msg)
 {
        perror(msg);
@@ -349,13 +353,14 @@ static void do_getsockopt_tcp_info(struct so_state *s, int fd, size_t r, size_t
                        xerror("getsockopt MPTCP_TCPINFO (tries %d, %m)");
 
                assert(olen <= sizeof(ti));
-               assert(ti.d.size_user == ti.d.size_kernel);
-               assert(ti.d.size_user == sizeof(struct tcp_info));
+               assert(ti.d.size_kernel > 0);
+               assert(ti.d.size_user ==
+                      MIN(ti.d.size_kernel, sizeof(struct tcp_info)));
                assert(ti.d.num_subflows == 1);
 
                assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
                olen -= sizeof(struct mptcp_subflow_data);
-               assert(olen == sizeof(struct tcp_info));
+               assert(olen == ti.d.size_user);
 
                if (ti.ti[0].tcpi_bytes_sent == w &&
                    ti.ti[0].tcpi_bytes_received == r)
@@ -401,13 +406,14 @@ static void do_getsockopt_subflow_addrs(int fd)
                die_perror("getsockopt MPTCP_SUBFLOW_ADDRS");
 
        assert(olen <= sizeof(addrs));
-       assert(addrs.d.size_user == addrs.d.size_kernel);
-       assert(addrs.d.size_user == sizeof(struct mptcp_subflow_addrs));
+       assert(addrs.d.size_kernel > 0);
+       assert(addrs.d.size_user ==
+              MIN(addrs.d.size_kernel, sizeof(struct mptcp_subflow_addrs)));
        assert(addrs.d.num_subflows == 1);
 
        assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
        olen -= sizeof(struct mptcp_subflow_data);
-       assert(olen == sizeof(struct mptcp_subflow_addrs));
+       assert(olen == addrs.d.size_user);
 
        llen = sizeof(local);
        ret = getsockname(fd, (struct sockaddr *)&local, &llen);
index ff5adbb..f295a37 100755 (executable)
@@ -87,6 +87,7 @@ cleanup()
 }
 
 mptcp_lib_check_mptcp
+mptcp_lib_check_kallsyms
 
 ip -Version > /dev/null 2>&1
 if [ $? -ne 0 ];then
@@ -186,9 +187,14 @@ do_transfer()
                local_addr="0.0.0.0"
        fi
 
+       cmsg="TIMESTAMPNS"
+       if mptcp_lib_kallsyms_has "mptcp_ioctl$"; then
+               cmsg+=",TCPINQ"
+       fi
+
        timeout ${timeout_test} \
                ip netns exec ${listener_ns} \
-                       $mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c TIMESTAMPNS,TCPINQ \
+                       $mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c "${cmsg}" \
                                ${local_addr} < "$sin" > "$sout" &
        local spid=$!
 
@@ -196,7 +202,7 @@ do_transfer()
 
        timeout ${timeout_test} \
                ip netns exec ${connector_ns} \
-                       $mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c TIMESTAMPNS,TCPINQ \
+                       $mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c "${cmsg}" \
                                $connect_addr < "$cin" > "$cout" &
 
        local cpid=$!
@@ -253,6 +259,11 @@ do_mptcp_sockopt_tests()
 {
        local lret=0
 
+       if ! mptcp_lib_kallsyms_has "mptcp_diag_fill_info$"; then
+               echo "INFO: MPTCP sockopt not supported: SKIP"
+               return
+       fi
+
        ip netns exec "$ns_sbox" ./mptcp_sockopt
        lret=$?
 
@@ -307,6 +318,11 @@ do_tcpinq_tests()
 {
        local lret=0
 
+       if ! mptcp_lib_kallsyms_has "mptcp_ioctl$"; then
+               echo "INFO: TCP_INQ not supported: SKIP"
+               return
+       fi
+
        local args
        for args in "-t tcp" "-r tcp"; do
                do_tcpinq_test $args
index 32f7533..d02e0d6 100755 (executable)
@@ -73,8 +73,12 @@ check()
 }
 
 check "ip netns exec $ns1 ./pm_nl_ctl dump" "" "defaults addr list"
-check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0
+
+default_limits="$(ip netns exec $ns1 ./pm_nl_ctl limits)"
+if mptcp_lib_expect_all_features; then
+       check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0
 subflows 2" "defaults limits"
+fi
 
 ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.1
 ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.2 flags subflow dev lo
@@ -121,12 +125,10 @@ ip netns exec $ns1 ./pm_nl_ctl flush
 check "ip netns exec $ns1 ./pm_nl_ctl dump" "" "flush addrs"
 
 ip netns exec $ns1 ./pm_nl_ctl limits 9 1
-check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0
-subflows 2" "rcv addrs above hard limit"
+check "ip netns exec $ns1 ./pm_nl_ctl limits" "$default_limits" "rcv addrs above hard limit"
 
 ip netns exec $ns1 ./pm_nl_ctl limits 1 9
-check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0
-subflows 2" "subflows above hard limit"
+check "ip netns exec $ns1 ./pm_nl_ctl limits" "$default_limits" "subflows above hard limit"
 
 ip netns exec $ns1 ./pm_nl_ctl limits 8 8
 check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 8
@@ -176,14 +178,19 @@ subflow,backup 10.0.1.1" "set flags (backup)"
 ip netns exec $ns1 ./pm_nl_ctl set 10.0.1.1 flags nobackup
 check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
 subflow 10.0.1.1" "          (nobackup)"
+
+# fullmesh support has been added later
 ip netns exec $ns1 ./pm_nl_ctl set id 1 flags fullmesh
-check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
+if ip netns exec $ns1 ./pm_nl_ctl dump | grep -q "fullmesh" ||
+   mptcp_lib_expect_all_features; then
+       check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
 subflow,fullmesh 10.0.1.1" "          (fullmesh)"
-ip netns exec $ns1 ./pm_nl_ctl set id 1 flags nofullmesh
-check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
+       ip netns exec $ns1 ./pm_nl_ctl set id 1 flags nofullmesh
+       check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
 subflow 10.0.1.1" "          (nofullmesh)"
-ip netns exec $ns1 ./pm_nl_ctl set id 1 flags backup,fullmesh
-check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
+       ip netns exec $ns1 ./pm_nl_ctl set id 1 flags backup,fullmesh
+       check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
 subflow,backup,fullmesh 10.0.1.1" "          (backup,fullmesh)"
+fi
 
 exit $ret
index 8092399..98d9e4d 100755 (executable)
@@ -4,11 +4,17 @@
 . "$(dirname "${0}")/mptcp_lib.sh"
 
 mptcp_lib_check_mptcp
+mptcp_lib_check_kallsyms
+
+if ! mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then
+       echo "userspace pm tests are not supported by the kernel: SKIP"
+       exit ${KSFT_SKIP}
+fi
 
 ip -Version > /dev/null 2>&1
 if [ $? -ne 0 ];then
        echo "SKIP: Cannot not run test without ip tool"
-       exit 1
+       exit ${KSFT_SKIP}
 fi
 
 ANNOUNCED=6        # MPTCP_EVENT_ANNOUNCED
@@ -909,6 +915,11 @@ test_listener()
 {
        print_title "Listener tests"
 
+       if ! mptcp_lib_kallsyms_has "mptcp_event_pm_listener$"; then
+               stdbuf -o0 -e0 printf "LISTENER events                                            \t[SKIP] Not supported\n"
+               return
+       fi
+
        # Capture events on the network namespace running the client
        :>$client_evts
 
index 198ad5f..cfa9562 100644 (file)
@@ -502,11 +502,11 @@ int main(int argc, char *argv[])
                        interval = t2 - t1;
                        offset = (t2 + t1) / 2 - tp;
 
-                       printf("system time: %lld.%u\n",
+                       printf("system time: %lld.%09u\n",
                                (pct+2*i)->sec, (pct+2*i)->nsec);
-                       printf("phc    time: %lld.%u\n",
+                       printf("phc    time: %lld.%09u\n",
                                (pct+2*i+1)->sec, (pct+2*i+1)->nsec);
-                       printf("system time: %lld.%u\n",
+                       printf("system time: %lld.%09u\n",
                                (pct+2*i+2)->sec, (pct+2*i+2)->nsec);
                        printf("system/phc clock time offset is %" PRId64 " ns\n"
                               "system     clock time delay  is %" PRId64 " ns\n",
index 4638c63..6e73b09 100644 (file)
@@ -6,20 +6,18 @@ CONFIG_NF_CONNTRACK_MARK=y
 CONFIG_NF_CONNTRACK_ZONES=y
 CONFIG_NF_CONNTRACK_LABELS=y
 CONFIG_NF_NAT=m
+CONFIG_NETFILTER_XT_TARGET_LOG=m
 
 CONFIG_NET_SCHED=y
 
 #
 # Queueing/Scheduling
 #
-CONFIG_NET_SCH_ATM=m
 CONFIG_NET_SCH_CAKE=m
-CONFIG_NET_SCH_CBQ=m
 CONFIG_NET_SCH_CBS=m
 CONFIG_NET_SCH_CHOKE=m
 CONFIG_NET_SCH_CODEL=m
 CONFIG_NET_SCH_DRR=m
-CONFIG_NET_SCH_DSMARK=m
 CONFIG_NET_SCH_ETF=m
 CONFIG_NET_SCH_FQ=m
 CONFIG_NET_SCH_FQ_CODEL=m
@@ -57,8 +55,6 @@ CONFIG_NET_CLS_FLOW=m
 CONFIG_NET_CLS_FLOWER=m
 CONFIG_NET_CLS_MATCHALL=m
 CONFIG_NET_CLS_ROUTE4=m
-CONFIG_NET_CLS_RSVP=m
-CONFIG_NET_CLS_TCINDEX=m
 CONFIG_NET_EMATCH=y
 CONFIG_NET_EMATCH_STACK=32
 CONFIG_NET_EMATCH_CMP=m
index ba2f5e7..e21c7f2 100644 (file)
         "setup": [
             "$IP link add dev $DUMMY type dummy || /bin/true"
         ],
-        "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb db 10",
+        "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb db 100",
         "expExitCode": "0",
         "verifyCmd": "$TC qdisc show dev $DUMMY",
-        "matchPattern": "qdisc sfb 1: root refcnt [0-9]+ rehash 600s db 10ms",
+        "matchPattern": "qdisc sfb 1: root refcnt [0-9]+ rehash 600s db 100ms",
         "matchCount": "1",
         "teardown": [
             "$TC qdisc del dev $DUMMY handle 1: root",
index afb0cd8..eb357bd 100755 (executable)
@@ -2,5 +2,6 @@
 # SPDX-License-Identifier: GPL-2.0
 
 modprobe netdevsim
+modprobe sch_teql
 ./tdc.py -c actions --nobuildebpf
 ./tdc.py -c qdisc
index 8879a7b..d6979a4 100644 (file)
 
 #include "../kselftest_harness.h"
 
-const char *dyn_file = "/sys/kernel/tracing/dynamic_events";
-const char *clear = "!u:__test_event";
+const char *abi_file = "/sys/kernel/tracing/user_events_data";
+const char *enable_file = "/sys/kernel/tracing/events/user_events/__test_event/enable";
 
-static int Append(const char *value)
+static bool wait_for_delete(void)
 {
-       int fd = open(dyn_file, O_RDWR | O_APPEND);
-       int ret = write(fd, value, strlen(value));
+       int i;
+
+       for (i = 0; i < 1000; ++i) {
+               int fd = open(enable_file, O_RDONLY);
+
+               if (fd == -1)
+                       return true;
+
+               close(fd);
+               usleep(1000);
+       }
+
+       return false;
+}
+
+static int reg_event(int fd, int *check, int bit, const char *value)
+{
+       struct user_reg reg = {0};
+
+       reg.size = sizeof(reg);
+       reg.name_args = (__u64)value;
+       reg.enable_bit = bit;
+       reg.enable_addr = (__u64)check;
+       reg.enable_size = sizeof(*check);
+
+       if (ioctl(fd, DIAG_IOCSREG, &reg) == -1)
+               return -1;
+
+       return 0;
+}
+
+static int unreg_event(int fd, int *check, int bit)
+{
+       struct user_unreg unreg = {0};
+
+       unreg.size = sizeof(unreg);
+       unreg.disable_bit = bit;
+       unreg.disable_addr = (__u64)check;
+
+       return ioctl(fd, DIAG_IOCSUNREG, &unreg);
+}
+
+static int parse(int *check, const char *value)
+{
+       int fd = open(abi_file, O_RDWR);
+       int ret;
+
+       if (fd == -1)
+               return -1;
+
+       /* Until we have persist flags via dynamic events, use the base name */
+       if (value[0] != 'u' || value[1] != ':') {
+               close(fd);
+               return -1;
+       }
+
+       ret = reg_event(fd, check, 31, value + 2);
+
+       if (ret != -1) {
+               if (unreg_event(fd, check, 31) == -1)
+                       printf("WARN: Couldn't unreg event\n");
+       }
 
        close(fd);
+
        return ret;
 }
 
-#define CLEAR() \
+static int check_match(int *check, const char *first, const char *second, bool *match)
+{
+       int fd = open(abi_file, O_RDWR);
+       int ret = -1;
+
+       if (fd == -1)
+               return -1;
+
+       if (reg_event(fd, check, 31, first) == -1)
+               goto cleanup;
+
+       if (reg_event(fd, check, 30, second) == -1) {
+               if (errno == EADDRINUSE) {
+                       /* Name is in use, with different fields */
+                       *match = false;
+                       ret = 0;
+               }
+
+               goto cleanup;
+       }
+
+       *match = true;
+       ret = 0;
+cleanup:
+       unreg_event(fd, check, 31);
+       unreg_event(fd, check, 30);
+
+       close(fd);
+
+       wait_for_delete();
+
+       return ret;
+}
+
+#define TEST_MATCH(x, y) \
 do { \
-       int ret = Append(clear); \
-       if (ret == -1) \
-               ASSERT_EQ(ENOENT, errno); \
+       bool match; \
+       ASSERT_NE(-1, check_match(&self->check, x, y, &match)); \
+       ASSERT_EQ(true, match); \
 } while (0)
 
-#define TEST_PARSE(x) \
+#define TEST_NMATCH(x, y) \
 do { \
-       ASSERT_NE(-1, Append(x)); \
-       CLEAR(); \
+       bool match; \
+       ASSERT_NE(-1, check_match(&self->check, x, y, &match)); \
+       ASSERT_EQ(false, match); \
 } while (0)
 
-#define TEST_NPARSE(x) ASSERT_EQ(-1, Append(x))
+#define TEST_PARSE(x) ASSERT_NE(-1, parse(&self->check, x))
+
+#define TEST_NPARSE(x) ASSERT_EQ(-1, parse(&self->check, x))
 
 FIXTURE(user) {
+       int check;
 };
 
 FIXTURE_SETUP(user) {
-       CLEAR();
 }
 
 FIXTURE_TEARDOWN(user) {
-       CLEAR();
+       wait_for_delete();
 }
 
 TEST_F(user, basic_types) {
@@ -95,33 +193,30 @@ TEST_F(user, size_types) {
        TEST_NPARSE("u:__test_event char a 20");
 }
 
-TEST_F(user, flags) {
-       /* Should work */
-       TEST_PARSE("u:__test_event:BPF_ITER u32 a");
-       /* Forward compat */
-       TEST_PARSE("u:__test_event:BPF_ITER,FLAG_FUTURE u32 a");
-}
-
 TEST_F(user, matching) {
-       /* Register */
-       ASSERT_NE(-1, Append("u:__test_event struct custom a 20"));
-       /* Should not match */
-       TEST_NPARSE("!u:__test_event struct custom b");
-       /* Should match */
-       TEST_PARSE("!u:__test_event struct custom a");
-       /* Multi field reg */
-       ASSERT_NE(-1, Append("u:__test_event u32 a; u32 b"));
-       /* Non matching cases */
-       TEST_NPARSE("!u:__test_event u32 a");
-       TEST_NPARSE("!u:__test_event u32 b");
-       TEST_NPARSE("!u:__test_event u32 a; u32 ");
-       TEST_NPARSE("!u:__test_event u32 a; u32 a");
-       /* Matching case */
-       TEST_PARSE("!u:__test_event u32 a; u32 b");
-       /* Register */
-       ASSERT_NE(-1, Append("u:__test_event u32 a; u32 b"));
-       /* Ensure trailing semi-colon case */
-       TEST_PARSE("!u:__test_event u32 a; u32 b;");
+       /* Single name matches */
+       TEST_MATCH("__test_event u32 a",
+                  "__test_event u32 a");
+
+       /* Multiple names match */
+       TEST_MATCH("__test_event u32 a; u32 b",
+                  "__test_event u32 a; u32 b");
+
+       /* Multiple names match with dangling ; */
+       TEST_MATCH("__test_event u32 a; u32 b",
+                  "__test_event u32 a; u32 b;");
+
+       /* Single name doesn't match */
+       TEST_NMATCH("__test_event u32 a",
+                   "__test_event u32 b");
+
+       /* Multiple names don't match */
+       TEST_NMATCH("__test_event u32 a; u32 b",
+                   "__test_event u32 b; u32 a");
+
+       /* Types don't match */
+       TEST_NMATCH("__test_event u64 a; u64 b",
+                   "__test_event u32 a; u32 b");
 }
 
 int main(int argc, char **argv)
index 7c99cef..eb6904d 100644 (file)
@@ -102,30 +102,56 @@ err:
        return -1;
 }
 
+static bool wait_for_delete(void)
+{
+       int i;
+
+       for (i = 0; i < 1000; ++i) {
+               int fd = open(enable_file, O_RDONLY);
+
+               if (fd == -1)
+                       return true;
+
+               close(fd);
+               usleep(1000);
+       }
+
+       return false;
+}
+
 static int clear(int *check)
 {
        struct user_unreg unreg = {0};
+       int fd;
 
        unreg.size = sizeof(unreg);
        unreg.disable_bit = 31;
        unreg.disable_addr = (__u64)check;
 
-       int fd = open(data_file, O_RDWR);
+       fd = open(data_file, O_RDWR);
 
        if (fd == -1)
                return -1;
 
        if (ioctl(fd, DIAG_IOCSUNREG, &unreg) == -1)
                if (errno != ENOENT)
-                       return -1;
-
-       if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1)
-               if (errno != ENOENT)
-                       return -1;
+                       goto fail;
+
+       if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1) {
+               if (errno == EBUSY) {
+                       if (!wait_for_delete())
+                               goto fail;
+               } else if (errno != ENOENT)
+                       goto fail;
+       }
 
        close(fd);
 
        return 0;
+fail:
+       close(fd);
+
+       return -1;
 }
 
 static int check_print_fmt(const char *event, const char *expected, int *check)
@@ -155,9 +181,8 @@ static int check_print_fmt(const char *event, const char *expected, int *check)
        /* Register should work */
        ret = ioctl(fd, DIAG_IOCSREG, &reg);
 
-       close(fd);
-
        if (ret != 0) {
+               close(fd);
                printf("Reg failed in fmt\n");
                return ret;
        }
@@ -165,6 +190,8 @@ static int check_print_fmt(const char *event, const char *expected, int *check)
        /* Ensure correct print_fmt */
        ret = get_print_fmt(print_fmt, sizeof(print_fmt));
 
+       close(fd);
+
        if (ret != 0)
                return ret;
 
@@ -228,6 +255,12 @@ TEST_F(user, register_events) {
        ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
        ASSERT_EQ(0, reg.write_index);
 
+       /* Multiple registers to same name but different args should fail */
+       reg.enable_bit = 29;
+       reg.name_args = (__u64)"__test_event u32 field1;";
+       ASSERT_EQ(-1, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
+       ASSERT_EQ(EADDRINUSE, errno);
+
        /* Ensure disabled */
        self->enable_fd = open(enable_file, O_RDWR);
        ASSERT_NE(-1, self->enable_fd);
@@ -250,10 +283,10 @@ TEST_F(user, register_events) {
        unreg.disable_bit = 30;
        ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSUNREG, &unreg));
 
-       /* Delete should work only after close and unregister */
+       /* Delete should have been auto-done after close and unregister */
        close(self->data_fd);
-       self->data_fd = open(data_file, O_RDWR);
-       ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSDEL, "__test_event"));
+
+       ASSERT_EQ(true, wait_for_delete());
 }
 
 TEST_F(user, write_events) {
@@ -310,6 +343,39 @@ TEST_F(user, write_events) {
        ASSERT_EQ(EINVAL, errno);
 }
 
+TEST_F(user, write_empty_events) {
+       struct user_reg reg = {0};
+       struct iovec io[1];
+       int before = 0, after = 0;
+
+       reg.size = sizeof(reg);
+       reg.name_args = (__u64)"__test_event";
+       reg.enable_bit = 31;
+       reg.enable_addr = (__u64)&self->check;
+       reg.enable_size = sizeof(self->check);
+
+       io[0].iov_base = &reg.write_index;
+       io[0].iov_len = sizeof(reg.write_index);
+
+       /* Register should work */
+       ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
+       ASSERT_EQ(0, reg.write_index);
+       ASSERT_EQ(0, self->check);
+
+       /* Enable event */
+       self->enable_fd = open(enable_file, O_RDWR);
+       ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1")))
+
+       /* Event should now be enabled */
+       ASSERT_EQ(1 << reg.enable_bit, self->check);
+
+       /* Write should make it out to ftrace buffers */
+       before = trace_bytes();
+       ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 1));
+       after = trace_bytes();
+       ASSERT_GT(after, before);
+}
+
 TEST_F(user, write_fault) {
        struct user_reg reg = {0};
        struct iovec io[2];
index a070258..8b09be5 100644 (file)
@@ -81,6 +81,32 @@ static int get_offset(void)
        return offset;
 }
 
+static int clear(int *check)
+{
+       struct user_unreg unreg = {0};
+
+       unreg.size = sizeof(unreg);
+       unreg.disable_bit = 31;
+       unreg.disable_addr = (__u64)check;
+
+       int fd = open(data_file, O_RDWR);
+
+       if (fd == -1)
+               return -1;
+
+       if (ioctl(fd, DIAG_IOCSUNREG, &unreg) == -1)
+               if (errno != ENOENT)
+                       return -1;
+
+       if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1)
+               if (errno != ENOENT)
+                       return -1;
+
+       close(fd);
+
+       return 0;
+}
+
 FIXTURE(user) {
        int data_fd;
        int check;
@@ -93,6 +119,9 @@ FIXTURE_SETUP(user) {
 
 FIXTURE_TEARDOWN(user) {
        close(self->data_fd);
+
+       if (clear(&self->check) != 0)
+               printf("WARNING: Clear didn't work!\n");
 }
 
 TEST_F(user, perf_write) {
@@ -160,6 +189,59 @@ TEST_F(user, perf_write) {
        ASSERT_EQ(0, self->check);
 }
 
+TEST_F(user, perf_empty_events) {
+       struct perf_event_attr pe = {0};
+       struct user_reg reg = {0};
+       struct perf_event_mmap_page *perf_page;
+       int page_size = sysconf(_SC_PAGESIZE);
+       int id, fd;
+       __u32 *val;
+
+       reg.size = sizeof(reg);
+       reg.name_args = (__u64)"__test_event";
+       reg.enable_bit = 31;
+       reg.enable_addr = (__u64)&self->check;
+       reg.enable_size = sizeof(self->check);
+
+       /* Register should work */
+       ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
+       ASSERT_EQ(0, reg.write_index);
+       ASSERT_EQ(0, self->check);
+
+       /* Id should be there */
+       id = get_id();
+       ASSERT_NE(-1, id);
+
+       pe.type = PERF_TYPE_TRACEPOINT;
+       pe.size = sizeof(pe);
+       pe.config = id;
+       pe.sample_type = PERF_SAMPLE_RAW;
+       pe.sample_period = 1;
+       pe.wakeup_events = 1;
+
+       /* Tracepoint attach should work */
+       fd = perf_event_open(&pe, 0, -1, -1, 0);
+       ASSERT_NE(-1, fd);
+
+       perf_page = mmap(NULL, page_size * 2, PROT_READ, MAP_SHARED, fd, 0);
+       ASSERT_NE(MAP_FAILED, perf_page);
+
+       /* Status should be updated */
+       ASSERT_EQ(1 << reg.enable_bit, self->check);
+
+       /* Ensure write shows up at correct offset */
+       ASSERT_NE(-1, write(self->data_fd, &reg.write_index,
+                                       sizeof(reg.write_index)));
+       val = (void *)(((char *)perf_page) + perf_page->data_offset);
+       ASSERT_EQ(PERF_RECORD_SAMPLE, *val);
+
+       munmap(perf_page, page_size * 2);
+       close(fd);
+
+       /* Status should be updated */
+       ASSERT_EQ(0, self->check);
+}
+
 int main(int argc, char **argv)
 {
        return test_harness_run(argc, argv);