Merge tag 'armsoc-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 6 Dec 2019 22:19:37 +0000 (14:19 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 6 Dec 2019 22:19:37 +0000 (14:19 -0800)
Pull ARM SoC fixes from Olof Johansson:
 "A set of fixes that we've merged late, but for the most part that have
  been sitting in -next for a while through platform maintainer trees:

   - Fixes to suspend/resume on Tegra, caused by the added features this
     merge window

   - Cleanups and minor fixes to TI additions this merge window

   - Tee fixes queued up late before the merge window, included here.

   - A handful of other fixlets

  There's also a refresh of the shareed config files (multi_v* on
  32-bit, and defconfig on 64-bit), to avoid conflicts when we get new
  contributions"

* tag 'armsoc-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (32 commits)
  ARM: multi_v7_defconfig: Restore debugfs support
  ARM: defconfig: re-run savedefconfig on multi_v* configs
  arm64: defconfig: re-run savedefconfig
  ARM: pxa: Fix resource properties
  soc: mediatek: cmdq: fixup wrong input order of write api
  soc: aspeed: Fix snoop_file_poll()'s return type
  MAINTAINERS: Switch to Marvell addresses
  MAINTAINERS: update Cavium ThunderX drivers
  Revert "arm64: dts: juno: add dma-ranges property"
  MAINTAINERS: Make Nicolas Saenz Julienne the new bcm2835 maintainer
  firmware: arm_scmi: Avoid double free in error flow
  arm64: dts: juno: Fix UART frequency
  ARM: dts: Fix sgx sysconfig register for omap4
  arm: socfpga: execute cold reboot by default
  ARM: dts: Fix vcsi regulator to be always-on for droid4 to prevent hangs
  ARM: dts: dra7: fix cpsw mdio fck clock
  ARM: dts: am57xx-beagle-x15: Update pinmux name to ddr_3_3v
  ARM: dts: omap3-tao3530: Fix incorrect MMC card detection GPIO polarity
  soc/tegra: pmc: Add reset sources and levels on Tegra194
  soc/tegra: pmc: Add missing IRQ callbacks on Tegra194
  ...

243 files changed:
Documentation/core-api/kernel-api.rst
Documentation/devicetree/bindings/display/msm/gmu.txt
Documentation/devicetree/bindings/display/msm/mdp5.txt
Documentation/devicetree/bindings/sram/qcom,ocmem.yaml [new file with mode: 0644]
MAINTAINERS
arch/arm64/include/asm/kvm_mmu.h
arch/arm64/include/asm/sections.h
arch/arm64/include/asm/uaccess.h
arch/arm64/kernel/entry-ftrace.S
arch/arm64/kernel/entry.S
arch/arm64/kernel/insn.c
arch/arm64/kernel/smp.c
arch/arm64/kernel/vmlinux.lds.S
arch/arm64/kvm/va_layout.c
arch/arm64/mm/dump.c
arch/arm64/mm/init.c
arch/ia64/include/asm/agp.h
arch/m68k/coldfire/entry.S
arch/powerpc/include/asm/archrandom.h
arch/powerpc/include/asm/bitops.h
arch/powerpc/include/asm/vdso_datapage.h
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/head_fsl_booke.S
arch/powerpc/kernel/time.c
arch/powerpc/kernel/vdso32/gettimeofday.S
arch/powerpc/kernel/vdso64/gettimeofday.S
arch/powerpc/lib/pmem.c
arch/powerpc/mm/mem.c
arch/powerpc/platforms/powernv/opal-imc.c
arch/powerpc/sysdev/xive/spapr.c
arch/s390/include/asm/bitops.h
arch/x86/include/asm/bitops.h
block/bfq-cgroup.c
block/bio-integrity.c
block/bio.c
block/blk-zoned.c
block/blk.h
block/ioctl.c
drivers/block/brd.c
drivers/block/null_blk_main.c
drivers/block/rbd.c
drivers/block/xen-blkback/blkback.c
drivers/char/agp/frontend.c
drivers/char/agp/generic.c
drivers/firmware/qcom_scm-32.c
drivers/firmware/qcom_scm-64.c
drivers/firmware/qcom_scm.c
drivers/firmware/qcom_scm.h
drivers/gpu/drm/Kconfig
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c
drivers/gpu/drm/amd/amdgpu/cik.c
drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
drivers/gpu/drm/amd/amdgpu/gfxhub_v1_1.c
drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c
drivers/gpu/drm/amd/amdgpu/vi.c
drivers/gpu/drm/amd/amdkfd/Kconfig
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c
drivers/gpu/drm/amd/powerplay/amdgpu_smu.c
drivers/gpu/drm/amd/powerplay/arcturus_ppt.c
drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h
drivers/gpu/drm/amd/powerplay/inc/smu_v11_0.h
drivers/gpu/drm/amd/powerplay/inc/smu_v12_0.h
drivers/gpu/drm/amd/powerplay/navi10_ppt.c
drivers/gpu/drm/amd/powerplay/renoir_ppt.c
drivers/gpu/drm/amd/powerplay/smu_internal.h
drivers/gpu/drm/amd/powerplay/smu_v11_0.c
drivers/gpu/drm/amd/powerplay/smu_v12_0.c
drivers/gpu/drm/amd/powerplay/vega20_ppt.c
drivers/gpu/drm/drm_dp_mst_topology.c
drivers/gpu/drm/i915/Kconfig.profile
drivers/gpu/drm/i915/display/intel_cdclk.c
drivers/gpu/drm/i915/display/intel_ddi.c
drivers/gpu/drm/i915/display/intel_dp.c
drivers/gpu/drm/i915/gem/i915_gem_context.c
drivers/gpu/drm/i915/gt/intel_context.c
drivers/gpu/drm/i915/gt/intel_engine.h
drivers/gpu/drm/i915/gt/intel_engine_cs.c
drivers/gpu/drm/i915/gt/intel_engine_pm.c
drivers/gpu/drm/i915/gt/intel_engine_pm.h
drivers/gpu/drm/i915/gt/intel_engine_types.h
drivers/gpu/drm/i915/gt/intel_gt_pm.c
drivers/gpu/drm/i915/gt/intel_gt_pm.h
drivers/gpu/drm/i915/gt/intel_gt_requests.c
drivers/gpu/drm/i915/gt/intel_gt_requests.h
drivers/gpu/drm/i915/gt/intel_lrc.c
drivers/gpu/drm/i915/gt/intel_reset.c
drivers/gpu/drm/i915/gt/intel_ring.c
drivers/gpu/drm/i915/gt/intel_timeline.c
drivers/gpu/drm/i915/gt/intel_timeline_types.h
drivers/gpu/drm/i915/gt/selftest_engine_pm.c
drivers/gpu/drm/i915/gvt/cmd_parser.c
drivers/gpu/drm/i915/gvt/handlers.c
drivers/gpu/drm/i915/i915_active.c
drivers/gpu/drm/i915/i915_pmu.c
drivers/gpu/drm/i915/i915_query.c
drivers/gpu/drm/i915/intel_wakeref.c
drivers/gpu/drm/i915/intel_wakeref.h
drivers/gpu/drm/mgag200/mgag200_drv.c
drivers/gpu/drm/mgag200/mgag200_drv.h
drivers/gpu/drm/mgag200/mgag200_main.c
drivers/gpu/drm/msm/Kconfig
drivers/gpu/drm/msm/adreno/a3xx_gpu.c
drivers/gpu/drm/msm/adreno/a3xx_gpu.h
drivers/gpu/drm/msm/adreno/a4xx_gpu.c
drivers/gpu/drm/msm/adreno/a4xx_gpu.h
drivers/gpu/drm/msm/adreno/a5xx_gpu.c
drivers/gpu/drm/msm/adreno/a5xx_power.c
drivers/gpu/drm/msm/adreno/adreno_device.c
drivers/gpu/drm/msm/adreno/adreno_gpu.c
drivers/gpu/drm/msm/adreno/adreno_gpu.h
drivers/gpu/drm/msm/disp/dpu1/dpu_core_irq.c
drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c
drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h
drivers/gpu/drm/msm/disp/dpu1/dpu_vbif.c
drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
drivers/gpu/drm/msm/disp/mdp5/mdp5_cfg.c
drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h
drivers/gpu/drm/msm/disp/mdp5/mdp5_smp.c
drivers/gpu/drm/msm/dsi/dsi_cfg.c
drivers/gpu/drm/msm/dsi/dsi_cfg.h
drivers/gpu/drm/msm/dsi/dsi_host.c
drivers/gpu/drm/msm/dsi/phy/dsi_phy.c
drivers/gpu/drm/msm/dsi/phy/dsi_phy.h
drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c
drivers/gpu/drm/msm/hdmi/hdmi_phy.c
drivers/gpu/drm/msm/msm_gpu.c
drivers/gpu/drm/msm/msm_gpummu.c
drivers/gpu/drm/msm/msm_iommu.c
drivers/gpu/drm/msm/msm_mmu.h
drivers/gpu/drm/msm/msm_rd.c
drivers/gpu/drm/omapdrm/omap_gem.c
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/r200.c
drivers/gpu/drm/tegra/dc.c
drivers/gpu/drm/tegra/drm.c
drivers/gpu/drm/tegra/gem.c
drivers/gpu/drm/tegra/hub.c
drivers/gpu/drm/tegra/plane.c
drivers/gpu/drm/tegra/sor.c
drivers/gpu/drm/tegra/vic.c
drivers/md/dm-table.c
drivers/md/dm-zoned-target.c
drivers/scsi/sd_zbc.c
drivers/soc/qcom/Kconfig
drivers/soc/qcom/Makefile
drivers/soc/qcom/ocmem.c [new file with mode: 0644]
fs/autofs/autofs_i.h
fs/autofs/expire.c
fs/autofs/root.c
fs/block_dev.c
fs/ceph/cache.c
fs/ceph/cache.h
fs/ceph/mds_client.c
fs/ceph/mdsmap.c
fs/ceph/super.c
fs/ceph/super.h
fs/cifs/cifsfs.c
fs/dcache.c
fs/debugfs/inode.c
fs/fuse/Kconfig
fs/fuse/dev.c
fs/fuse/dir.c
fs/fuse/file.c
fs/fuse/fuse_i.h
fs/fuse/readdir.c
fs/fuse/virtio_fs.c
fs/gfs2/aops.c
fs/gfs2/bmap.c
fs/gfs2/file.c
fs/gfs2/glock.c
fs/gfs2/glops.c
fs/gfs2/inode.c
fs/gfs2/log.c
fs/gfs2/log.h
fs/gfs2/lops.c
fs/gfs2/lops.h
fs/gfs2/meta_io.c
fs/gfs2/ops_fstype.c
fs/gfs2/quota.c
fs/gfs2/recovery.c
fs/gfs2/super.c
fs/gfs2/sys.c
fs/gfs2/trans.c
fs/gfs2/util.c
fs/gfs2/util.h
fs/io-wq.c
fs/io-wq.h
fs/io_uring.c
fs/kernfs/mount.c
fs/namei.c
fs/nfsd/nfs3xdr.c
fs/nfsd/nfs4xdr.c
fs/overlayfs/namei.c
fs/pipe.c
fs/quota/dquot.c
fs/splice.c
include/asm-generic/bitops-instrumented.h [deleted file]
include/asm-generic/bitops/instrumented-atomic.h [new file with mode: 0644]
include/asm-generic/bitops/instrumented-lock.h [new file with mode: 0644]
include/asm-generic/bitops/instrumented-non-atomic.h [new file with mode: 0644]
include/linux/agpgart.h
include/linux/blkdev.h
include/linux/bvec.h
include/linux/ceph/libceph.h
include/linux/dcache.h
include/linux/export.h
include/linux/miscdevice.h
include/linux/moduleparam.h
include/linux/namei.h
include/linux/qcom_scm.h
include/linux/socket.h
include/soc/qcom/ocmem.h [new file with mode: 0644]
include/sound/hdaudio.h
include/uapi/linux/io_uring.h
kernel/module.c
net/ceph/ceph_common.c
net/ceph/messenger.c
net/ceph/mon_client.c
net/socket.c
sound/core/oss/linear.c
sound/core/oss/mulaw.c
sound/core/oss/route.c
sound/drivers/aloop.c
sound/hda/hdac_stream.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c

index f77de49..4ac53a1 100644 (file)
@@ -57,7 +57,13 @@ The Linux kernel provides more basic utility functions.
 Bit Operations
 --------------
 
-.. kernel-doc:: include/asm-generic/bitops-instrumented.h
+.. kernel-doc:: include/asm-generic/bitops/instrumented-atomic.h
+   :internal:
+
+.. kernel-doc:: include/asm-generic/bitops/instrumented-non-atomic.h
+   :internal:
+
+.. kernel-doc:: include/asm-generic/bitops/instrumented-lock.h
    :internal:
 
 Bitmap Operations
index 90af5b0..bf9c7a2 100644 (file)
@@ -31,6 +31,10 @@ Required properties:
 - iommus: phandle to the adreno iommu
 - operating-points-v2: phandle to the OPP operating points
 
+Optional properties:
+- sram: phandle to the On Chip Memory (OCMEM) that's present on some Snapdragon
+        SoCs. See Documentation/devicetree/bindings/sram/qcom,ocmem.yaml.
+
 Example:
 
 / {
@@ -63,3 +67,50 @@ Example:
                operating-points-v2 = <&gmu_opp_table>;
        };
 };
+
+a3xx example with OCMEM support:
+
+/ {
+       ...
+
+       gpu: adreno@fdb00000 {
+               compatible = "qcom,adreno-330.2",
+                            "qcom,adreno";
+               reg = <0xfdb00000 0x10000>;
+               reg-names = "kgsl_3d0_reg_memory";
+               interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+               interrupt-names = "kgsl_3d0_irq";
+               clock-names = "core",
+                             "iface",
+                             "mem_iface";
+               clocks = <&mmcc OXILI_GFX3D_CLK>,
+                        <&mmcc OXILICX_AHB_CLK>,
+                        <&mmcc OXILICX_AXI_CLK>;
+               sram = <&gmu_sram>;
+               power-domains = <&mmcc OXILICX_GDSC>;
+               operating-points-v2 = <&gpu_opp_table>;
+               iommus = <&gpu_iommu 0>;
+       };
+
+       ocmem@fdd00000 {
+               compatible = "qcom,msm8974-ocmem";
+
+               reg = <0xfdd00000 0x2000>,
+                     <0xfec00000 0x180000>;
+               reg-names = "ctrl",
+                            "mem";
+
+               clocks = <&rpmcc RPM_SMD_OCMEMGX_CLK>,
+                        <&mmcc OCMEMCX_OCMEMNOC_CLK>;
+               clock-names = "core",
+                             "iface";
+
+               #address-cells = <1>;
+               #size-cells = <1>;
+
+               gmu_sram: gmu-sram@0 {
+                       reg = <0x0 0x100000>;
+                       ranges = <0 0 0xfec00000 0x100000>;
+               };
+       };
+};
index 4e11338..43d1127 100644 (file)
@@ -76,6 +76,8 @@ Required properties:
 Optional properties:
 - clock-names: the following clocks are optional:
   * "lut"
+  * "tbu"
+  * "tbu_rt"
 
 Example:
 
diff --git a/Documentation/devicetree/bindings/sram/qcom,ocmem.yaml b/Documentation/devicetree/bindings/sram/qcom,ocmem.yaml
new file mode 100644 (file)
index 0000000..222990f
--- /dev/null
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sram/qcom,ocmem.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: On Chip Memory (OCMEM) that is present on some Qualcomm Snapdragon SoCs.
+
+maintainers:
+  - Brian Masney <masneyb@onstation.org>
+
+description: |
+  The On Chip Memory (OCMEM) is typically used by the GPU, camera/video, and
+  audio components on some Snapdragon SoCs.
+
+properties:
+  compatible:
+    const: qcom,msm8974-ocmem
+
+  reg:
+    items:
+      - description: Control registers
+      - description: OCMEM address range
+
+  reg-names:
+    items:
+      - const: ctrl
+      - const: mem
+
+  clocks:
+    items:
+      - description: Core clock
+      - description: Interface clock
+
+  clock-names:
+    items:
+      - const: core
+      - const: iface
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - clocks
+  - clock-names
+  - '#address-cells'
+  - '#size-cells'
+
+patternProperties:
+  "^.+-sram$":
+    type: object
+    description: A region of reserved memory.
+
+    properties:
+      reg:
+        maxItems: 1
+
+      ranges:
+        maxItems: 1
+
+    required:
+      - reg
+      - ranges
+
+examples:
+  - |
+      #include <dt-bindings/clock/qcom,rpmcc.h>
+      #include <dt-bindings/clock/qcom,mmcc-msm8974.h>
+
+      ocmem: ocmem@fdd00000 {
+        compatible = "qcom,msm8974-ocmem";
+
+        reg = <0xfdd00000 0x2000>,
+              <0xfec00000 0x180000>;
+        reg-names = "ctrl",
+                    "mem";
+
+        clocks = <&rpmcc RPM_SMD_OCMEMGX_CLK>,
+                 <&mmcc OCMEMCX_OCMEMNOC_CLK>;
+        clock-names = "core",
+                      "iface";
+
+        #address-cells = <1>;
+        #size-cells = <1>;
+
+        gmu-sram@0 {
+                reg = <0x0 0x100000>;
+                ranges = <0 0 0xfec00000 0x100000>;
+        };
+      };
index 99c9a55..0fd82e6 100644 (file)
@@ -862,7 +862,6 @@ S:  Maintained
 F:     drivers/i2c/busses/i2c-amd-mp2*
 
 AMD POWERPLAY
-M:     Rex Zhu <rex.zhu@amd.com>
 M:     Evan Quan <evan.quan@amd.com>
 L:     amd-gfx@lists.freedesktop.org
 S:     Supported
@@ -13768,7 +13767,7 @@ F:      drivers/media/radio/radio-tea5777.c
 RADOS BLOCK DEVICE (RBD)
 M:     Ilya Dryomov <idryomov@gmail.com>
 M:     Sage Weil <sage@redhat.com>
-M:     Alex Elder <elder@kernel.org>
+R:     Dongsheng Yang <dongsheng.yang@easystack.cn>
 L:     ceph-devel@vger.kernel.org
 W:     http://ceph.com/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git
index befe37d..53d846f 100644 (file)
@@ -91,6 +91,7 @@ alternative_cb_end
 
 void kvm_update_va_mask(struct alt_instr *alt,
                        __le32 *origptr, __le32 *updptr, int nr_inst);
+void kvm_compute_layout(void);
 
 static inline unsigned long __kern_hyp_va(unsigned long v)
 {
index 788ae97..25a73aa 100644 (file)
@@ -15,6 +15,7 @@ extern char __hyp_text_start[], __hyp_text_end[];
 extern char __idmap_text_start[], __idmap_text_end[];
 extern char __initdata_begin[], __initdata_end[];
 extern char __inittext_begin[], __inittext_end[];
+extern char __exittext_begin[], __exittext_end[];
 extern char __irqentry_text_start[], __irqentry_text_end[];
 extern char __mmuoff_data_start[], __mmuoff_data_end[];
 extern char __entry_tramp_text_start[], __entry_tramp_text_end[];
index 127712b..32fc806 100644 (file)
@@ -62,8 +62,13 @@ static inline unsigned long __range_ok(const void __user *addr, unsigned long si
 {
        unsigned long ret, limit = current_thread_info()->addr_limit;
 
+       /*
+        * Asynchronous I/O running in a kernel thread does not have the
+        * TIF_TAGGED_ADDR flag of the process owning the mm, so always untag
+        * the user address before checking.
+        */
        if (IS_ENABLED(CONFIG_ARM64_TAGGED_ADDR_ABI) &&
-           test_thread_flag(TIF_TAGGED_ADDR))
+           (current->flags & PF_KTHREAD || test_thread_flag(TIF_TAGGED_ADDR)))
                addr = untagged_addr(addr);
 
        __chk_user_ptr(addr);
index 4fe1514..7d02f99 100644 (file)
@@ -133,7 +133,6 @@ ENTRY(ftrace_graph_caller)
        bl      prepare_ftrace_return
        b       ftrace_common_return
 ENDPROC(ftrace_graph_caller)
-#else
 #endif
 
 #else /* CONFIG_DYNAMIC_FTRACE_WITH_REGS */
@@ -287,6 +286,7 @@ GLOBAL(ftrace_graph_call)           // ftrace_graph_caller();
 
        mcount_exit
 ENDPROC(ftrace_caller)
+#endif /* CONFIG_DYNAMIC_FTRACE */
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 /*
@@ -307,7 +307,6 @@ ENTRY(ftrace_graph_caller)
        mcount_exit
 ENDPROC(ftrace_graph_caller)
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
-#endif /* CONFIG_DYNAMIC_FTRACE */
 #endif /* CONFIG_DYNAMIC_FTRACE_WITH_REGS */
 
 ENTRY(ftrace_stub)
index 583f71a..7c6a0a4 100644 (file)
@@ -76,7 +76,8 @@ alternative_else_nop_endif
 #ifdef CONFIG_VMAP_STACK
        /*
         * Test whether the SP has overflowed, without corrupting a GPR.
-        * Task and IRQ stacks are aligned to (1 << THREAD_SHIFT).
+        * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT)
+        * should always be zero.
         */
        add     sp, sp, x0                      // sp' = sp + x0
        sub     x0, sp, x0                      // x0' = sp' - x0 = (sp + x0) - x0 = sp
index 513b29c..4a9e773 100644 (file)
@@ -21,6 +21,7 @@
 #include <asm/fixmap.h>
 #include <asm/insn.h>
 #include <asm/kprobes.h>
+#include <asm/sections.h>
 
 #define AARCH64_INSN_SF_BIT    BIT(31)
 #define AARCH64_INSN_N_BIT     BIT(22)
@@ -78,16 +79,29 @@ bool aarch64_insn_is_branch_imm(u32 insn)
 
 static DEFINE_RAW_SPINLOCK(patch_lock);
 
+static bool is_exit_text(unsigned long addr)
+{
+       /* discarded with init text/data */
+       return system_state < SYSTEM_RUNNING &&
+               addr >= (unsigned long)__exittext_begin &&
+               addr < (unsigned long)__exittext_end;
+}
+
+static bool is_image_text(unsigned long addr)
+{
+       return core_kernel_text(addr) || is_exit_text(addr);
+}
+
 static void __kprobes *patch_map(void *addr, int fixmap)
 {
        unsigned long uintaddr = (uintptr_t) addr;
-       bool module = !core_kernel_text(uintaddr);
+       bool image = is_image_text(uintaddr);
        struct page *page;
 
-       if (module && IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
-               page = vmalloc_to_page(addr);
-       else if (!module)
+       if (image)
                page = phys_to_page(__pa_symbol(addr));
+       else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
+               page = vmalloc_to_page(addr);
        else
                return addr;
 
index ab149bc..d4ed9a1 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/of.h>
 #include <linux/irq_work.h>
 #include <linux/kexec.h>
+#include <linux/kvm_host.h>
 
 #include <asm/alternative.h>
 #include <asm/atomic.h>
@@ -39,6 +40,7 @@
 #include <asm/cputype.h>
 #include <asm/cpu_ops.h>
 #include <asm/daifflags.h>
+#include <asm/kvm_mmu.h>
 #include <asm/mmu_context.h>
 #include <asm/numa.h>
 #include <asm/pgtable.h>
@@ -407,6 +409,8 @@ static void __init hyp_mode_check(void)
                           "CPU: CPUs started in inconsistent modes");
        else
                pr_info("CPU: All CPU(s) started at EL1\n");
+       if (IS_ENABLED(CONFIG_KVM_ARM_HOST))
+               kvm_compute_layout();
 }
 
 void __init smp_cpus_done(unsigned int max_cpus)
index 841a8b4..497f967 100644 (file)
@@ -158,9 +158,12 @@ SECTIONS
        __inittext_begin = .;
 
        INIT_TEXT_SECTION(8)
+
+       __exittext_begin = .;
        .exit.text : {
                ARM_EXIT_KEEP(EXIT_TEXT)
        }
+       __exittext_end = .;
 
        . = ALIGN(4);
        .altinstructions : {
index 2cf7d4b..dab1fea 100644 (file)
@@ -22,7 +22,7 @@ static u8 tag_lsb;
 static u64 tag_val;
 static u64 va_mask;
 
-static void compute_layout(void)
+__init void kvm_compute_layout(void)
 {
        phys_addr_t idmap_addr = __pa_symbol(__hyp_idmap_text_start);
        u64 hyp_va_msb;
@@ -110,9 +110,6 @@ void __init kvm_update_va_mask(struct alt_instr *alt,
 
        BUG_ON(nr_inst != 5);
 
-       if (!has_vhe() && !va_mask)
-               compute_layout();
-
        for (i = 0; i < nr_inst; i++) {
                u32 rd, rn, insn, oinsn;
 
@@ -156,9 +153,6 @@ void kvm_patch_vector_branch(struct alt_instr *alt,
                return;
        }
 
-       if (!va_mask)
-               compute_layout();
-
        /*
         * Compute HYP VA by using the same computation as kern_hyp_va()
         */
index 93f9f77..0a920b5 100644 (file)
@@ -142,6 +142,7 @@ static const struct prot_bits pte_bits[] = {
                .mask   = PTE_UXN,
                .val    = PTE_UXN,
                .set    = "UXN",
+               .clear  = "   ",
        }, {
                .mask   = PTE_ATTRINDX_MASK,
                .val    = PTE_ATTRINDX(MT_DEVICE_nGnRnE),
index be9481c..b65dffd 100644 (file)
@@ -214,15 +214,14 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
 {
        struct memblock_region *reg;
        unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
-       unsigned long max_dma32 = min;
-       unsigned long __maybe_unused max_dma = min;
+       unsigned long __maybe_unused max_dma, max_dma32;
 
        memset(zone_size, 0, sizeof(zone_size));
 
+       max_dma = max_dma32 = min;
 #ifdef CONFIG_ZONE_DMA
-       max_dma = PFN_DOWN(arm64_dma_phys_limit);
+       max_dma = max_dma32 = PFN_DOWN(arm64_dma_phys_limit);
        zone_size[ZONE_DMA] = max_dma - min;
-       max_dma32 = max_dma;
 #endif
 #ifdef CONFIG_ZONE_DMA32
        max_dma32 = PFN_DOWN(arm64_dma32_phys_limit);
@@ -236,25 +235,23 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
                unsigned long start = memblock_region_memory_base_pfn(reg);
                unsigned long end = memblock_region_memory_end_pfn(reg);
 
-               if (start >= max)
-                       continue;
 #ifdef CONFIG_ZONE_DMA
-               if (start < max_dma) {
-                       unsigned long dma_end = min_not_zero(end, max_dma);
+               if (start >= min && start < max_dma) {
+                       unsigned long dma_end = min(end, max_dma);
                        zhole_size[ZONE_DMA] -= dma_end - start;
+                       start = dma_end;
                }
 #endif
 #ifdef CONFIG_ZONE_DMA32
-               if (start < max_dma32) {
+               if (start >= max_dma && start < max_dma32) {
                        unsigned long dma32_end = min(end, max_dma32);
-                       unsigned long dma32_start = max(start, max_dma);
-                       zhole_size[ZONE_DMA32] -= dma32_end - dma32_start;
+                       zhole_size[ZONE_DMA32] -= dma32_end - start;
+                       start = dma32_end;
                }
 #endif
-               if (end > max_dma32) {
+               if (start >= max_dma32 && start < max) {
                        unsigned long normal_end = min(end, max);
-                       unsigned long normal_start = max(start, max_dma32);
-                       zhole_size[ZONE_NORMAL] -= normal_end - normal_start;
+                       zhole_size[ZONE_NORMAL] -= normal_end - start;
                }
        }
 
index 2b451c4..0261507 100644 (file)
@@ -14,8 +14,8 @@
  * in coherent mode, which lets us map the AGP memory as normal (write-back) memory
  * (unlike x86, where it gets mapped "write-coalescing").
  */
-#define map_page_into_agp(page)                /* nothing */
-#define unmap_page_from_agp(page)      /* nothing */
+#define map_page_into_agp(page)                do { } while (0)
+#define unmap_page_from_agp(page)      do { } while (0)
 #define flush_agp_cache()              mb()
 
 /* GATT allocation. Returns/accepts GATT kernel virtual address. */
index 52d312d..d43a027 100644 (file)
@@ -108,7 +108,7 @@ ret_from_exception:
        btst    #5,%sp@(PT_OFF_SR)      /* check if returning to kernel */
        jeq     Luser_return            /* if so, skip resched, signals */
 
-#ifdef CONFIG_PREEMPT
+#ifdef CONFIG_PREEMPTION
        movel   %sp,%d1                 /* get thread_info pointer */
        andl    #-THREAD_SIZE,%d1       /* at base of kernel stack */
        movel   %d1,%a0
index 9c63b59..a09595f 100644 (file)
@@ -28,7 +28,7 @@ static inline int arch_get_random_seed_int(unsigned int *v)
        unsigned long val;
        int rc;
 
-       rc = arch_get_random_long(&val);
+       rc = arch_get_random_seed_long(&val);
        if (rc)
                *v = val;
 
index 603aed2..28dcf82 100644 (file)
@@ -64,7 +64,7 @@
 
 /* Macro for generating the ***_bits() functions */
 #define DEFINE_BITOP(fn, op, prefix)           \
-static __inline__ void fn(unsigned long mask,  \
+static inline void fn(unsigned long mask,      \
                volatile unsigned long *_p)     \
 {                                              \
        unsigned long old;                      \
@@ -86,22 +86,22 @@ DEFINE_BITOP(clear_bits, andc, "")
 DEFINE_BITOP(clear_bits_unlock, andc, PPC_RELEASE_BARRIER)
 DEFINE_BITOP(change_bits, xor, "")
 
-static __inline__ void set_bit(int nr, volatile unsigned long *addr)
+static inline void arch_set_bit(int nr, volatile unsigned long *addr)
 {
        set_bits(BIT_MASK(nr), addr + BIT_WORD(nr));
 }
 
-static __inline__ void clear_bit(int nr, volatile unsigned long *addr)
+static inline void arch_clear_bit(int nr, volatile unsigned long *addr)
 {
        clear_bits(BIT_MASK(nr), addr + BIT_WORD(nr));
 }
 
-static __inline__ void clear_bit_unlock(int nr, volatile unsigned long *addr)
+static inline void arch_clear_bit_unlock(int nr, volatile unsigned long *addr)
 {
        clear_bits_unlock(BIT_MASK(nr), addr + BIT_WORD(nr));
 }
 
-static __inline__ void change_bit(int nr, volatile unsigned long *addr)
+static inline void arch_change_bit(int nr, volatile unsigned long *addr)
 {
        change_bits(BIT_MASK(nr), addr + BIT_WORD(nr));
 }
@@ -109,7 +109,7 @@ static __inline__ void change_bit(int nr, volatile unsigned long *addr)
 /* Like DEFINE_BITOP(), with changes to the arguments to 'op' and the output
  * operands. */
 #define DEFINE_TESTOP(fn, op, prefix, postfix, eh)     \
-static __inline__ unsigned long fn(                    \
+static inline unsigned long fn(                        \
                unsigned long mask,                     \
                volatile unsigned long *_p)             \
 {                                                      \
@@ -138,34 +138,34 @@ DEFINE_TESTOP(test_and_clear_bits, andc, PPC_ATOMIC_ENTRY_BARRIER,
 DEFINE_TESTOP(test_and_change_bits, xor, PPC_ATOMIC_ENTRY_BARRIER,
              PPC_ATOMIC_EXIT_BARRIER, 0)
 
-static __inline__ int test_and_set_bit(unsigned long nr,
-                                      volatile unsigned long *addr)
+static inline int arch_test_and_set_bit(unsigned long nr,
+                                       volatile unsigned long *addr)
 {
        return test_and_set_bits(BIT_MASK(nr), addr + BIT_WORD(nr)) != 0;
 }
 
-static __inline__ int test_and_set_bit_lock(unsigned long nr,
-                                      volatile unsigned long *addr)
+static inline int arch_test_and_set_bit_lock(unsigned long nr,
+                                            volatile unsigned long *addr)
 {
        return test_and_set_bits_lock(BIT_MASK(nr),
                                addr + BIT_WORD(nr)) != 0;
 }
 
-static __inline__ int test_and_clear_bit(unsigned long nr,
-                                        volatile unsigned long *addr)
+static inline int arch_test_and_clear_bit(unsigned long nr,
+                                         volatile unsigned long *addr)
 {
        return test_and_clear_bits(BIT_MASK(nr), addr + BIT_WORD(nr)) != 0;
 }
 
-static __inline__ int test_and_change_bit(unsigned long nr,
-                                         volatile unsigned long *addr)
+static inline int arch_test_and_change_bit(unsigned long nr,
+                                          volatile unsigned long *addr)
 {
        return test_and_change_bits(BIT_MASK(nr), addr + BIT_WORD(nr)) != 0;
 }
 
 #ifdef CONFIG_PPC64
-static __inline__ unsigned long clear_bit_unlock_return_word(int nr,
-                                               volatile unsigned long *addr)
+static inline unsigned long
+clear_bit_unlock_return_word(int nr, volatile unsigned long *addr)
 {
        unsigned long old, t;
        unsigned long *p = (unsigned long *)addr + BIT_WORD(nr);
@@ -185,15 +185,18 @@ static __inline__ unsigned long clear_bit_unlock_return_word(int nr,
        return old;
 }
 
-/* This is a special function for mm/filemap.c */
-#define clear_bit_unlock_is_negative_byte(nr, addr)                    \
-       (clear_bit_unlock_return_word(nr, addr) & BIT_MASK(PG_waiters))
+/*
+ * This is a special function for mm/filemap.c
+ * Bit 7 corresponds to PG_waiters.
+ */
+#define arch_clear_bit_unlock_is_negative_byte(nr, addr)               \
+       (clear_bit_unlock_return_word(nr, addr) & BIT_MASK(7))
 
 #endif /* CONFIG_PPC64 */
 
 #include <asm-generic/bitops/non-atomic.h>
 
-static __inline__ void __clear_bit_unlock(int nr, volatile unsigned long *addr)
+static inline void arch___clear_bit_unlock(int nr, volatile unsigned long *addr)
 {
        __asm__ __volatile__(PPC_RELEASE_BARRIER "" ::: "memory");
        __clear_bit(nr, addr);
@@ -215,14 +218,14 @@ static __inline__ void __clear_bit_unlock(int nr, volatile unsigned long *addr)
  * fls: find last (most-significant) bit set.
  * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
  */
-static __inline__ int fls(unsigned int x)
+static inline int fls(unsigned int x)
 {
        return 32 - __builtin_clz(x);
 }
 
 #include <asm-generic/bitops/builtin-__fls.h>
 
-static __inline__ int fls64(__u64 x)
+static inline int fls64(__u64 x)
 {
        return 64 - __builtin_clzll(x);
 }
@@ -239,6 +242,10 @@ unsigned long __arch_hweight64(__u64 w);
 
 #include <asm-generic/bitops/find.h>
 
+/* wrappers that deal with KASAN instrumentation */
+#include <asm-generic/bitops/instrumented-atomic.h>
+#include <asm-generic/bitops/instrumented-lock.h>
+
 /* Little-endian versions */
 #include <asm-generic/bitops/le.h>
 
index a115970..40f13f3 100644 (file)
@@ -83,6 +83,7 @@ struct vdso_data {
        __s64 wtom_clock_sec;                   /* Wall to monotonic clock sec */
        __s64 stamp_xtime_sec;                  /* xtime secs as at tb_orig_stamp */
        __s64 stamp_xtime_nsec;                 /* xtime nsecs as at tb_orig_stamp */
+       __u32 hrtimer_res;                      /* hrtimer resolution */
        __u32 syscall_map_64[SYSCALL_MAP_SIZE]; /* map of syscalls  */
        __u32 syscall_map_32[SYSCALL_MAP_SIZE]; /* map of syscalls */
 };
@@ -105,6 +106,7 @@ struct vdso_data {
        __s32 stamp_xtime_sec;          /* xtime seconds as at tb_orig_stamp */
        __s32 stamp_xtime_nsec;         /* xtime nsecs as at tb_orig_stamp */
        __u32 stamp_sec_fraction;       /* fractional seconds of stamp_xtime */
+       __u32 hrtimer_res;              /* hrtimer resolution */
        __u32 syscall_map_32[SYSCALL_MAP_SIZE]; /* map of syscalls */
        __u32 dcache_block_size;        /* L1 d-cache block size     */
        __u32 icache_block_size;        /* L1 i-cache block size     */
index f22bd6d..3d47aec 100644 (file)
@@ -388,6 +388,7 @@ int main(void)
        OFFSET(STAMP_XTIME_SEC, vdso_data, stamp_xtime_sec);
        OFFSET(STAMP_XTIME_NSEC, vdso_data, stamp_xtime_nsec);
        OFFSET(STAMP_SEC_FRAC, vdso_data, stamp_sec_fraction);
+       OFFSET(CLOCK_HRTIMER_RES, vdso_data, hrtimer_res);
        OFFSET(CFG_ICACHE_BLOCKSZ, vdso_data, icache_block_size);
        OFFSET(CFG_DCACHE_BLOCKSZ, vdso_data, dcache_block_size);
        OFFSET(CFG_ICACHE_LOGBLOCKSZ, vdso_data, icache_log_block_size);
@@ -413,7 +414,6 @@ int main(void)
        DEFINE(CLOCK_REALTIME_COARSE, CLOCK_REALTIME_COARSE);
        DEFINE(CLOCK_MONOTONIC_COARSE, CLOCK_MONOTONIC_COARSE);
        DEFINE(NSEC_PER_SEC, NSEC_PER_SEC);
-       DEFINE(CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC);
 
 #ifdef CONFIG_BUG
        DEFINE(BUG_ENTRY_SIZE, sizeof(struct bug_entry));
index 838d9d4..6f7a3a7 100644 (file)
@@ -240,6 +240,9 @@ set_ivor:
 
        bl      early_init
 
+#ifdef CONFIG_KASAN
+       bl      kasan_early_init
+#endif
 #ifdef CONFIG_RELOCATABLE
        mr      r3,r30
        mr      r4,r31
@@ -266,9 +269,6 @@ set_ivor:
 /*
  * Decide what sort of machine this is and initialize the MMU.
  */
-#ifdef CONFIG_KASAN
-       bl      kasan_early_init
-#endif
        mr      r3,r30
        mr      r4,r31
        bl      machine_init
index 2d13cea..1168e8b 100644 (file)
@@ -960,6 +960,7 @@ void update_vsyscall(struct timekeeper *tk)
        vdso_data->stamp_xtime_sec = xt.tv_sec;
        vdso_data->stamp_xtime_nsec = xt.tv_nsec;
        vdso_data->stamp_sec_fraction = frac_sec;
+       vdso_data->hrtimer_res = hrtimer_resolution;
        smp_wmb();
        ++(vdso_data->tb_update_count);
 }
index c8e6902..3306672 100644 (file)
@@ -154,12 +154,15 @@ V_FUNCTION_BEGIN(__kernel_clock_getres)
        cror    cr0*4+eq,cr0*4+eq,cr1*4+eq
        bne     cr0,99f
 
+       mflr    r12
+  .cfi_register lr,r12
+       bl      __get_datapage@local    /* get data page */
+       lwz     r5, CLOCK_HRTIMER_RES(r3)
+       mtlr    r12
        li      r3,0
        cmpli   cr0,r4,0
        crclr   cr0*4+so
        beqlr
-       lis     r5,CLOCK_REALTIME_RES@h
-       ori     r5,r5,CLOCK_REALTIME_RES@l
        stw     r3,TSPC32_TV_SEC(r4)
        stw     r5,TSPC32_TV_NSEC(r4)
        blr
index 1f24e41..1c9a047 100644 (file)
@@ -186,12 +186,15 @@ V_FUNCTION_BEGIN(__kernel_clock_getres)
        cror    cr0*4+eq,cr0*4+eq,cr1*4+eq
        bne     cr0,99f
 
+       mflr    r12
+  .cfi_register lr,r12
+       bl      V_LOCAL_FUNC(__get_datapage)
+       lwz     r5, CLOCK_HRTIMER_RES(r3)
+       mtlr    r12
        li      r3,0
        cmpldi  cr0,r4,0
        crclr   cr0*4+so
        beqlr
-       lis     r5,CLOCK_REALTIME_RES@h
-       ori     r5,r5,CLOCK_REALTIME_RES@l
        std     r3,TSPC64_TV_SEC(r4)
        std     r5,TSPC64_TV_NSEC(r4)
        blr
index 377712e..0666a8d 100644 (file)
@@ -17,14 +17,14 @@ void arch_wb_cache_pmem(void *addr, size_t size)
        unsigned long start = (unsigned long) addr;
        flush_dcache_range(start, start + size);
 }
-EXPORT_SYMBOL(arch_wb_cache_pmem);
+EXPORT_SYMBOL_GPL(arch_wb_cache_pmem);
 
 void arch_invalidate_pmem(void *addr, size_t size)
 {
        unsigned long start = (unsigned long) addr;
        flush_dcache_range(start, start + size);
 }
-EXPORT_SYMBOL(arch_invalidate_pmem);
+EXPORT_SYMBOL_GPL(arch_invalidate_pmem);
 
 /*
  * CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE symbols
index ad299e7..9488b63 100644 (file)
@@ -121,7 +121,7 @@ static void flush_dcache_range_chunked(unsigned long start, unsigned long stop,
        unsigned long i;
 
        for (i = start; i < stop; i += chunk) {
-               flush_dcache_range(i, min(stop, start + chunk));
+               flush_dcache_range(i, min(stop, i + chunk));
                cond_resched();
        }
 }
index e04b206..000b350 100644 (file)
@@ -59,10 +59,6 @@ static void export_imc_mode_and_cmd(struct device_node *node,
 
        imc_debugfs_parent = debugfs_create_dir("imc", powerpc_debugfs_root);
 
-       /*
-        * Return here, either because 'imc' directory already exists,
-        * Or failed to create a new one.
-        */
        if (!imc_debugfs_parent)
                return;
 
@@ -135,7 +131,6 @@ static int imc_get_mem_addr_nest(struct device_node *node,
        }
 
        pmu_ptr->imc_counter_mmaped = true;
-       export_imc_mode_and_cmd(node, pmu_ptr);
        kfree(base_addr_arr);
        kfree(chipid_arr);
        return 0;
@@ -151,7 +146,7 @@ error:
  *                 and domain as the inputs.
  * Allocates memory for the struct imc_pmu, sets up its domain, size and offsets
  */
-static int imc_pmu_create(struct device_node *parent, int pmu_index, int domain)
+static struct imc_pmu *imc_pmu_create(struct device_node *parent, int pmu_index, int domain)
 {
        int ret = 0;
        struct imc_pmu *pmu_ptr;
@@ -159,27 +154,23 @@ static int imc_pmu_create(struct device_node *parent, int pmu_index, int domain)
 
        /* Return for unknown domain */
        if (domain < 0)
-               return -EINVAL;
+               return NULL;
 
        /* memory for pmu */
        pmu_ptr = kzalloc(sizeof(*pmu_ptr), GFP_KERNEL);
        if (!pmu_ptr)
-               return -ENOMEM;
+               return NULL;
 
        /* Set the domain */
        pmu_ptr->domain = domain;
 
        ret = of_property_read_u32(parent, "size", &pmu_ptr->counter_mem_size);
-       if (ret) {
-               ret = -EINVAL;
+       if (ret)
                goto free_pmu;
-       }
 
        if (!of_property_read_u32(parent, "offset", &offset)) {
-               if (imc_get_mem_addr_nest(parent, pmu_ptr, offset)) {
-                       ret = -EINVAL;
+               if (imc_get_mem_addr_nest(parent, pmu_ptr, offset))
                        goto free_pmu;
-               }
        }
 
        /* Function to register IMC pmu */
@@ -190,14 +181,14 @@ static int imc_pmu_create(struct device_node *parent, int pmu_index, int domain)
                if (pmu_ptr->domain == IMC_DOMAIN_NEST)
                        kfree(pmu_ptr->mem_info);
                kfree(pmu_ptr);
-               return ret;
+               return NULL;
        }
 
-       return 0;
+       return pmu_ptr;
 
 free_pmu:
        kfree(pmu_ptr);
-       return ret;
+       return NULL;
 }
 
 static void disable_nest_pmu_counters(void)
@@ -254,6 +245,7 @@ int get_max_nest_dev(void)
 static int opal_imc_counters_probe(struct platform_device *pdev)
 {
        struct device_node *imc_dev = pdev->dev.of_node;
+       struct imc_pmu *pmu;
        int pmu_count = 0, domain;
        bool core_imc_reg = false, thread_imc_reg = false;
        u32 type;
@@ -269,6 +261,7 @@ static int opal_imc_counters_probe(struct platform_device *pdev)
        }
 
        for_each_compatible_node(imc_dev, NULL, IMC_DTB_UNIT_COMPAT) {
+               pmu = NULL;
                if (of_property_read_u32(imc_dev, "type", &type)) {
                        pr_warn("IMC Device without type property\n");
                        continue;
@@ -285,7 +278,14 @@ static int opal_imc_counters_probe(struct platform_device *pdev)
                        domain = IMC_DOMAIN_THREAD;
                        break;
                case IMC_TYPE_TRACE:
-                       domain = IMC_DOMAIN_TRACE;
+                       /*
+                        * FIXME. Using trace_imc events to monitor application
+                        * or KVM thread performance can cause a checkstop
+                        * (system crash).
+                        * Disable it for now.
+                        */
+                       pr_info_once("IMC: disabling trace_imc PMU\n");
+                       domain = -1;
                        break;
                default:
                        pr_warn("IMC Unknown Device type \n");
@@ -293,9 +293,13 @@ static int opal_imc_counters_probe(struct platform_device *pdev)
                        break;
                }
 
-               if (!imc_pmu_create(imc_dev, pmu_count, domain)) {
-                       if (domain == IMC_DOMAIN_NEST)
+               pmu = imc_pmu_create(imc_dev, pmu_count, domain);
+               if (pmu != NULL) {
+                       if (domain == IMC_DOMAIN_NEST) {
+                               if (!imc_debugfs_parent)
+                                       export_imc_mode_and_cmd(imc_dev, pmu);
                                pmu_count++;
+                       }
                        if (domain == IMC_DOMAIN_CORE)
                                core_imc_reg = true;
                        if (domain == IMC_DOMAIN_THREAD)
@@ -303,10 +307,6 @@ static int opal_imc_counters_probe(struct platform_device *pdev)
                }
        }
 
-       /* If none of the nest units are registered, remove debugfs interface */
-       if (pmu_count == 0)
-               debugfs_remove_recursive(imc_debugfs_parent);
-
        /* If core imc is not registered, unregister thread-imc */
        if (!core_imc_reg && thread_imc_reg)
                unregister_thread_imc();
index 33c1074..55dc61c 100644 (file)
@@ -392,20 +392,28 @@ static int xive_spapr_populate_irq_data(u32 hw_irq, struct xive_irq_data *data)
        data->esb_shift = esb_shift;
        data->trig_page = trig_page;
 
+       data->hw_irq = hw_irq;
+
        /*
         * No chip-id for the sPAPR backend. This has an impact how we
         * pick a target. See xive_pick_irq_target().
         */
        data->src_chip = XIVE_INVALID_CHIP_ID;
 
+       /*
+        * When the H_INT_ESB flag is set, the H_INT_ESB hcall should
+        * be used for interrupt management. Skip the remapping of the
+        * ESB pages which are not available.
+        */
+       if (data->flags & XIVE_IRQ_FLAG_H_INT_ESB)
+               return 0;
+
        data->eoi_mmio = ioremap(data->eoi_page, 1u << data->esb_shift);
        if (!data->eoi_mmio) {
                pr_err("Failed to map EOI page for irq 0x%x\n", hw_irq);
                return -ENOMEM;
        }
 
-       data->hw_irq = hw_irq;
-
        /* Full function page supports trigger */
        if (flags & XIVE_SRC_TRIGGER) {
                data->trig_mmio = data->eoi_mmio;
index eb7eed4..431e208 100644 (file)
@@ -241,7 +241,9 @@ static inline void arch___clear_bit_unlock(unsigned long nr,
        arch___clear_bit(nr, ptr);
 }
 
-#include <asm-generic/bitops-instrumented.h>
+#include <asm-generic/bitops/instrumented-atomic.h>
+#include <asm-generic/bitops/instrumented-non-atomic.h>
+#include <asm-generic/bitops/instrumented-lock.h>
 
 /*
  * Functions which use MSB0 bit numbering.
index 7d1f6a4..062cdec 100644 (file)
@@ -388,7 +388,9 @@ static __always_inline int fls64(__u64 x)
 
 #include <asm-generic/bitops/const_hweight.h>
 
-#include <asm-generic/bitops-instrumented.h>
+#include <asm-generic/bitops/instrumented-atomic.h>
+#include <asm-generic/bitops/instrumented-non-atomic.h>
+#include <asm-generic/bitops/instrumented-lock.h>
 
 #include <asm-generic/bitops/le.h>
 
index cea0ae1..e1419ed 100644 (file)
@@ -351,6 +351,9 @@ void bfqg_stats_update_legacy_io(struct request_queue *q, struct request *rq)
 {
        struct bfq_group *bfqg = blkg_to_bfqg(rq->bio->bi_blkg);
 
+       if (!bfqg)
+               return;
+
        blkg_rwstat_add(&bfqg->stats.bytes, rq->cmd_flags, blk_rq_bytes(rq));
        blkg_rwstat_add(&bfqg->stats.ios, rq->cmd_flags, 1);
 }
index fb95dbb..bf62c25 100644 (file)
@@ -87,7 +87,7 @@ EXPORT_SYMBOL(bio_integrity_alloc);
  * Description: Used to free the integrity portion of a bio. Usually
  * called from bio_free().
  */
-static void bio_integrity_free(struct bio *bio)
+void bio_integrity_free(struct bio *bio)
 {
        struct bio_integrity_payload *bip = bio_integrity(bio);
        struct bio_set *bs = bio->bi_pool;
index b1170ec..9d54aa3 100644 (file)
@@ -233,6 +233,9 @@ fallback:
 void bio_uninit(struct bio *bio)
 {
        bio_disassociate_blkg(bio);
+
+       if (bio_integrity(bio))
+               bio_integrity_free(bio);
 }
 EXPORT_SYMBOL(bio_uninit);
 
index 6fad6f3..d00fcfd 100644 (file)
@@ -70,30 +70,20 @@ void __blk_req_zone_write_unlock(struct request *rq)
 }
 EXPORT_SYMBOL_GPL(__blk_req_zone_write_unlock);
 
-static inline unsigned int __blkdev_nr_zones(struct request_queue *q,
-                                            sector_t nr_sectors)
-{
-       sector_t zone_sectors = blk_queue_zone_sectors(q);
-
-       return (nr_sectors + zone_sectors - 1) >> ilog2(zone_sectors);
-}
-
 /**
  * blkdev_nr_zones - Get number of zones
- * @bdev:      Target block device
+ * @disk:      Target gendisk
  *
- * Description:
- *    Return the total number of zones of a zoned block device.
- *    For a regular block device, the number of zones is always 0.
+ * Return the total number of zones of a zoned block device.  For a block
+ * device without zone capabilities, the number of zones is always 0.
  */
-unsigned int blkdev_nr_zones(struct block_device *bdev)
+unsigned int blkdev_nr_zones(struct gendisk *disk)
 {
-       struct request_queue *q = bdev_get_queue(bdev);
+       sector_t zone_sectors = blk_queue_zone_sectors(disk->queue);
 
-       if (!blk_queue_is_zoned(q))
+       if (!blk_queue_is_zoned(disk->queue))
                return 0;
-
-       return __blkdev_nr_zones(q, get_capacity(bdev->bd_disk));
+       return (get_capacity(disk) + zone_sectors - 1) >> ilog2(zone_sectors);
 }
 EXPORT_SYMBOL_GPL(blkdev_nr_zones);
 
@@ -342,16 +332,18 @@ static inline unsigned long *blk_alloc_zone_bitmap(int node,
 
 void blk_queue_free_zone_bitmaps(struct request_queue *q)
 {
-       kfree(q->seq_zones_bitmap);
-       q->seq_zones_bitmap = NULL;
+       kfree(q->conv_zones_bitmap);
+       q->conv_zones_bitmap = NULL;
        kfree(q->seq_zones_wlock);
        q->seq_zones_wlock = NULL;
 }
 
 struct blk_revalidate_zone_args {
        struct gendisk  *disk;
-       unsigned long   *seq_zones_bitmap;
+       unsigned long   *conv_zones_bitmap;
        unsigned long   *seq_zones_wlock;
+       unsigned int    nr_zones;
+       sector_t        zone_sectors;
        sector_t        sector;
 };
 
@@ -364,25 +356,33 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
        struct blk_revalidate_zone_args *args = data;
        struct gendisk *disk = args->disk;
        struct request_queue *q = disk->queue;
-       sector_t zone_sectors = blk_queue_zone_sectors(q);
        sector_t capacity = get_capacity(disk);
 
        /*
         * All zones must have the same size, with the exception on an eventual
         * smaller last zone.
         */
-       if (zone->start + zone_sectors < capacity &&
-           zone->len != zone_sectors) {
-               pr_warn("%s: Invalid zoned device with non constant zone size\n",
-                       disk->disk_name);
-               return false;
-       }
+       if (zone->start == 0) {
+               if (zone->len == 0 || !is_power_of_2(zone->len)) {
+                       pr_warn("%s: Invalid zoned device with non power of two zone size (%llu)\n",
+                               disk->disk_name, zone->len);
+                       return -ENODEV;
+               }
 
-       if (zone->start + zone->len >= capacity &&
-           zone->len > zone_sectors) {
-               pr_warn("%s: Invalid zoned device with larger last zone size\n",
-                       disk->disk_name);
-               return -ENODEV;
+               args->zone_sectors = zone->len;
+               args->nr_zones = (capacity + zone->len - 1) >> ilog2(zone->len);
+       } else if (zone->start + args->zone_sectors < capacity) {
+               if (zone->len != args->zone_sectors) {
+                       pr_warn("%s: Invalid zoned device with non constant zone size\n",
+                               disk->disk_name);
+                       return -ENODEV;
+               }
+       } else {
+               if (zone->len > args->zone_sectors) {
+                       pr_warn("%s: Invalid zoned device with larger last zone size\n",
+                               disk->disk_name);
+                       return -ENODEV;
+               }
        }
 
        /* Check for holes in the zone report */
@@ -395,8 +395,22 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
        /* Check zone type */
        switch (zone->type) {
        case BLK_ZONE_TYPE_CONVENTIONAL:
+               if (!args->conv_zones_bitmap) {
+                       args->conv_zones_bitmap =
+                               blk_alloc_zone_bitmap(q->node, args->nr_zones);
+                       if (!args->conv_zones_bitmap)
+                               return -ENOMEM;
+               }
+               set_bit(idx, args->conv_zones_bitmap);
+               break;
        case BLK_ZONE_TYPE_SEQWRITE_REQ:
        case BLK_ZONE_TYPE_SEQWRITE_PREF:
+               if (!args->seq_zones_wlock) {
+                       args->seq_zones_wlock =
+                               blk_alloc_zone_bitmap(q->node, args->nr_zones);
+                       if (!args->seq_zones_wlock)
+                               return -ENOMEM;
+               }
                break;
        default:
                pr_warn("%s: Invalid zone type 0x%x at sectors %llu\n",
@@ -404,78 +418,54 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
                return -ENODEV;
        }
 
-       if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL)
-               set_bit(idx, args->seq_zones_bitmap);
-
        args->sector += zone->len;
        return 0;
 }
 
-static int blk_update_zone_info(struct gendisk *disk, unsigned int nr_zones,
-                               struct blk_revalidate_zone_args *args)
-{
-       /*
-        * Ensure that all memory allocations in this context are done as
-        * if GFP_NOIO was specified.
-        */
-       unsigned int noio_flag = memalloc_noio_save();
-       struct request_queue *q = disk->queue;
-       int ret;
-
-       args->seq_zones_wlock = blk_alloc_zone_bitmap(q->node, nr_zones);
-       if (!args->seq_zones_wlock)
-               return -ENOMEM;
-       args->seq_zones_bitmap = blk_alloc_zone_bitmap(q->node, nr_zones);
-       if (!args->seq_zones_bitmap)
-               return -ENOMEM;
-
-       ret = disk->fops->report_zones(disk, 0, nr_zones,
-                                      blk_revalidate_zone_cb, args);
-       memalloc_noio_restore(noio_flag);
-       return ret;
-}
-
 /**
  * blk_revalidate_disk_zones - (re)allocate and initialize zone bitmaps
  * @disk:      Target disk
  *
  * Helper function for low-level device drivers to (re) allocate and initialize
  * a disk request queue zone bitmaps. This functions should normally be called
- * within the disk ->revalidate method. For BIO based queues, no zone bitmap
- * is allocated.
+ * within the disk ->revalidate method for blk-mq based drivers.  For BIO based
+ * drivers only q->nr_zones needs to be updated so that the sysfs exposed value
+ * is correct.
  */
 int blk_revalidate_disk_zones(struct gendisk *disk)
 {
        struct request_queue *q = disk->queue;
-       unsigned int nr_zones = __blkdev_nr_zones(q, get_capacity(disk));
-       struct blk_revalidate_zone_args args = { .disk = disk };
-       int ret = 0;
+       struct blk_revalidate_zone_args args = {
+               .disk           = disk,
+       };
+       unsigned int noio_flag;
+       int ret;
 
        if (WARN_ON_ONCE(!blk_queue_is_zoned(q)))
                return -EIO;
+       if (WARN_ON_ONCE(!queue_is_mq(q)))
+               return -EIO;
 
        /*
-        * BIO based queues do not use a scheduler so only q->nr_zones
-        * needs to be updated so that the sysfs exposed value is correct.
+        * Ensure that all memory allocations in this context are done as if
+        * GFP_NOIO was specified.
         */
-       if (!queue_is_mq(q)) {
-               q->nr_zones = nr_zones;
-               return 0;
-       }
-
-       if (nr_zones)
-               ret = blk_update_zone_info(disk, nr_zones, &args);
+       noio_flag = memalloc_noio_save();
+       ret = disk->fops->report_zones(disk, 0, UINT_MAX,
+                                      blk_revalidate_zone_cb, &args);
+       memalloc_noio_restore(noio_flag);
 
        /*
-        * Install the new bitmaps, making sure the queue is stopped and
-        * all I/Os are completed (i.e. a scheduler is not referencing the
-        * bitmaps).
+        * Install the new bitmaps and update nr_zones only once the queue is
+        * stopped and all I/Os are completed (i.e. a scheduler is not
+        * referencing the bitmaps).
         */
        blk_mq_freeze_queue(q);
        if (ret >= 0) {
-               q->nr_zones = nr_zones;
+               blk_queue_chunk_sectors(q, args.zone_sectors);
+               q->nr_zones = args.nr_zones;
                swap(q->seq_zones_wlock, args.seq_zones_wlock);
-               swap(q->seq_zones_bitmap, args.seq_zones_bitmap);
+               swap(q->conv_zones_bitmap, args.conv_zones_bitmap);
                ret = 0;
        } else {
                pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
@@ -484,8 +474,7 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
        blk_mq_unfreeze_queue(q);
 
        kfree(args.seq_zones_wlock);
-       kfree(args.seq_zones_bitmap);
+       kfree(args.conv_zones_bitmap);
        return ret;
 }
 EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);
-
index 2bea401..6842f28 100644 (file)
@@ -121,6 +121,7 @@ static inline void blk_rq_bio_prep(struct request *rq, struct bio *bio,
 #ifdef CONFIG_BLK_DEV_INTEGRITY
 void blk_flush_integrity(void);
 bool __bio_integrity_endio(struct bio *);
+void bio_integrity_free(struct bio *bio);
 static inline bool bio_integrity_endio(struct bio *bio)
 {
        if (bio_integrity(bio))
@@ -166,6 +167,9 @@ static inline bool bio_integrity_endio(struct bio *bio)
 {
        return true;
 }
+static inline void bio_integrity_free(struct bio *bio)
+{
+}
 #endif /* CONFIG_BLK_DEV_INTEGRITY */
 
 unsigned long blk_rq_timeout(unsigned long timeout);
index 7ac8a66..5de98b9 100644 (file)
@@ -512,7 +512,7 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
        case BLKGETZONESZ:
                return put_uint(arg, bdev_zone_sectors(bdev));
        case BLKGETNRZONES:
-               return put_uint(arg, blkdev_nr_zones(bdev));
+               return put_uint(arg, blkdev_nr_zones(bdev->bd_disk));
        case HDIO_GETGEO:
                return blkdev_getgeo(bdev, argp);
        case BLKRAGET:
index c548a5a..a8730cc 100644 (file)
@@ -297,6 +297,10 @@ static blk_qc_t brd_make_request(struct request_queue *q, struct bio *bio)
                unsigned int len = bvec.bv_len;
                int err;
 
+               /* Don't support un-aligned buffer */
+               WARN_ON_ONCE((bvec.bv_offset & (SECTOR_SIZE - 1)) ||
+                               (len & (SECTOR_SIZE - 1)));
+
                err = brd_do_bvec(brd, bvec.bv_page, len, bvec.bv_offset,
                                  bio_op(bio), sector);
                if (err)
@@ -382,7 +386,6 @@ static struct brd_device *brd_alloc(int i)
                goto out_free_dev;
 
        blk_queue_make_request(brd->brd_queue, brd_make_request);
-       blk_queue_max_hw_sectors(brd->brd_queue, 1024);
 
        /* This is so fdisk will align partitions on 4k, because of
         * direct_access API needing 4k alignment, returning a PFN
index 795fda5..ae8d4bc 100644 (file)
@@ -1559,14 +1559,13 @@ static int init_driver_queues(struct nullb *nullb)
 
 static int null_gendisk_register(struct nullb *nullb)
 {
+       sector_t size = ((sector_t)nullb->dev->size * SZ_1M) >> SECTOR_SHIFT;
        struct gendisk *disk;
-       sector_t size;
 
        disk = nullb->disk = alloc_disk_node(1, nullb->dev->home_node);
        if (!disk)
                return -ENOMEM;
-       size = (sector_t)nullb->dev->size * 1024 * 1024ULL;
-       set_capacity(disk, size >> 9);
+       set_capacity(disk, size);
 
        disk->flags |= GENHD_FL_EXT_DEVT | GENHD_FL_SUPPRESS_PARTITION_INFO;
        disk->major             = null_major;
@@ -1576,12 +1575,19 @@ static int null_gendisk_register(struct nullb *nullb)
        disk->queue             = nullb->q;
        strncpy(disk->disk_name, nullb->disk_name, DISK_NAME_LEN);
 
+#ifdef CONFIG_BLK_DEV_ZONED
        if (nullb->dev->zoned) {
-               int ret = blk_revalidate_disk_zones(disk);
-
-               if (ret != 0)
-                       return ret;
+               if (queue_is_mq(nullb->q)) {
+                       int ret = blk_revalidate_disk_zones(disk);
+                       if (ret)
+                               return ret;
+               } else {
+                       blk_queue_chunk_sectors(nullb->q,
+                                       nullb->dev->zone_size_sects);
+                       nullb->q->nr_zones = blkdev_nr_zones(disk);
+               }
        }
+#endif
 
        add_disk(disk);
        return 0;
@@ -1607,7 +1613,7 @@ static int null_init_tag_set(struct nullb *nullb, struct blk_mq_tag_set *set)
        return blk_mq_alloc_tag_set(set);
 }
 
-static void null_validate_conf(struct nullb_device *dev)
+static int null_validate_conf(struct nullb_device *dev)
 {
        dev->blocksize = round_down(dev->blocksize, 512);
        dev->blocksize = clamp_t(unsigned int, dev->blocksize, 512, 4096);
@@ -1634,6 +1640,14 @@ static void null_validate_conf(struct nullb_device *dev)
        /* can not stop a queue */
        if (dev->queue_mode == NULL_Q_BIO)
                dev->mbps = 0;
+
+       if (dev->zoned &&
+           (!dev->zone_size || !is_power_of_2(dev->zone_size))) {
+               pr_err("zone_size must be power-of-two\n");
+               return -EINVAL;
+       }
+
+       return 0;
 }
 
 #ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
@@ -1666,7 +1680,9 @@ static int null_add_dev(struct nullb_device *dev)
        struct nullb *nullb;
        int rv;
 
-       null_validate_conf(dev);
+       rv = null_validate_conf(dev);
+       if (rv)
+               return rv;
 
        nullb = kzalloc_node(sizeof(*nullb), GFP_KERNEL, dev->home_node);
        if (!nullb) {
@@ -1731,7 +1747,6 @@ static int null_add_dev(struct nullb_device *dev)
                if (rv)
                        goto out_cleanup_blk_queue;
 
-               blk_queue_chunk_sectors(nullb->q, dev->zone_size_sects);
                nullb->q->limits.zoned = BLK_ZONED_HM;
                blk_queue_flag_set(QUEUE_FLAG_ZONE_RESETALL, nullb->q);
                blk_queue_required_elevator_features(nullb->q,
@@ -1792,11 +1807,6 @@ static int __init null_init(void)
                g_bs = PAGE_SIZE;
        }
 
-       if (!is_power_of_2(g_zone_size)) {
-               pr_err("zone_size must be power-of-two\n");
-               return -EINVAL;
-       }
-
        if (g_home_node != NUMA_NO_NODE && g_home_node >= nr_online_nodes) {
                pr_err("invalid home_node value\n");
                g_home_node = NUMA_NO_NODE;
index 13527a0..2b18456 100644 (file)
@@ -34,7 +34,7 @@
 #include <linux/ceph/cls_lock_client.h>
 #include <linux/ceph/striper.h>
 #include <linux/ceph/decode.h>
-#include <linux/parser.h>
+#include <linux/fs_parser.h>
 #include <linux/bsearch.h>
 
 #include <linux/kernel.h>
@@ -377,7 +377,6 @@ struct rbd_client_id {
 
 struct rbd_mapping {
        u64                     size;
-       u64                     features;
 };
 
 /*
@@ -462,8 +461,9 @@ struct rbd_device {
  *   by rbd_dev->lock
  */
 enum rbd_dev_flags {
-       RBD_DEV_FLAG_EXISTS,    /* mapped snapshot has not been deleted */
+       RBD_DEV_FLAG_EXISTS,    /* rbd_dev_device_setup() ran */
        RBD_DEV_FLAG_REMOVING,  /* this mapping is being removed */
+       RBD_DEV_FLAG_READONLY,  /* -o ro or snapshot */
 };
 
 static DEFINE_MUTEX(client_mutex);     /* Serialize client creation */
@@ -514,6 +514,16 @@ static int minor_to_rbd_dev_id(int minor)
        return minor >> RBD_SINGLE_MAJOR_PART_SHIFT;
 }
 
+static bool rbd_is_ro(struct rbd_device *rbd_dev)
+{
+       return test_bit(RBD_DEV_FLAG_READONLY, &rbd_dev->flags);
+}
+
+static bool rbd_is_snap(struct rbd_device *rbd_dev)
+{
+       return rbd_dev->spec->snap_id != CEPH_NOSNAP;
+}
+
 static bool __rbd_is_lock_owner(struct rbd_device *rbd_dev)
 {
        lockdep_assert_held(&rbd_dev->lock_rwsem);
@@ -633,8 +643,6 @@ static const char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev,
                                        u64 snap_id);
 static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id,
                                u8 *order, u64 *snap_size);
-static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
-               u64 *snap_features);
 static int rbd_dev_v2_get_flags(struct rbd_device *rbd_dev);
 
 static void rbd_obj_handle_request(struct rbd_obj_request *obj_req, int result);
@@ -695,9 +703,16 @@ static int rbd_ioctl_set_ro(struct rbd_device *rbd_dev, unsigned long arg)
        if (get_user(ro, (int __user *)arg))
                return -EFAULT;
 
-       /* Snapshots can't be marked read-write */
-       if (rbd_dev->spec->snap_id != CEPH_NOSNAP && !ro)
-               return -EROFS;
+       /*
+        * Both images mapped read-only and snapshots can't be marked
+        * read-write.
+        */
+       if (!ro) {
+               if (rbd_is_ro(rbd_dev))
+                       return -EROFS;
+
+               rbd_assert(!rbd_is_snap(rbd_dev));
+       }
 
        /* Let blkdev_roset() handle it */
        return -ENOTTY;
@@ -823,34 +838,34 @@ enum {
        Opt_queue_depth,
        Opt_alloc_size,
        Opt_lock_timeout,
-       Opt_last_int,
        /* int args above */
        Opt_pool_ns,
-       Opt_last_string,
        /* string args above */
        Opt_read_only,
        Opt_read_write,
        Opt_lock_on_read,
        Opt_exclusive,
        Opt_notrim,
-       Opt_err
 };
 
-static match_table_t rbd_opts_tokens = {
-       {Opt_queue_depth, "queue_depth=%d"},
-       {Opt_alloc_size, "alloc_size=%d"},
-       {Opt_lock_timeout, "lock_timeout=%d"},
-       /* int args above */
-       {Opt_pool_ns, "_pool_ns=%s"},
-       /* string args above */
-       {Opt_read_only, "read_only"},
-       {Opt_read_only, "ro"},          /* Alternate spelling */
-       {Opt_read_write, "read_write"},
-       {Opt_read_write, "rw"},         /* Alternate spelling */
-       {Opt_lock_on_read, "lock_on_read"},
-       {Opt_exclusive, "exclusive"},
-       {Opt_notrim, "notrim"},
-       {Opt_err, NULL}
+static const struct fs_parameter_spec rbd_param_specs[] = {
+       fsparam_u32     ("alloc_size",                  Opt_alloc_size),
+       fsparam_flag    ("exclusive",                   Opt_exclusive),
+       fsparam_flag    ("lock_on_read",                Opt_lock_on_read),
+       fsparam_u32     ("lock_timeout",                Opt_lock_timeout),
+       fsparam_flag    ("notrim",                      Opt_notrim),
+       fsparam_string  ("_pool_ns",                    Opt_pool_ns),
+       fsparam_u32     ("queue_depth",                 Opt_queue_depth),
+       fsparam_flag    ("read_only",                   Opt_read_only),
+       fsparam_flag    ("read_write",                  Opt_read_write),
+       fsparam_flag    ("ro",                          Opt_read_only),
+       fsparam_flag    ("rw",                          Opt_read_write),
+       {}
+};
+
+static const struct fs_parameter_description rbd_parameters = {
+       .name           = "rbd",
+       .specs          = rbd_param_specs,
 };
 
 struct rbd_options {
@@ -871,87 +886,12 @@ struct rbd_options {
 #define RBD_EXCLUSIVE_DEFAULT  false
 #define RBD_TRIM_DEFAULT       true
 
-struct parse_rbd_opts_ctx {
+struct rbd_parse_opts_ctx {
        struct rbd_spec         *spec;
+       struct ceph_options     *copts;
        struct rbd_options      *opts;
 };
 
-static int parse_rbd_opts_token(char *c, void *private)
-{
-       struct parse_rbd_opts_ctx *pctx = private;
-       substring_t argstr[MAX_OPT_ARGS];
-       int token, intval, ret;
-
-       token = match_token(c, rbd_opts_tokens, argstr);
-       if (token < Opt_last_int) {
-               ret = match_int(&argstr[0], &intval);
-               if (ret < 0) {
-                       pr_err("bad option arg (not int) at '%s'\n", c);
-                       return ret;
-               }
-               dout("got int token %d val %d\n", token, intval);
-       } else if (token > Opt_last_int && token < Opt_last_string) {
-               dout("got string token %d val %s\n", token, argstr[0].from);
-       } else {
-               dout("got token %d\n", token);
-       }
-
-       switch (token) {
-       case Opt_queue_depth:
-               if (intval < 1) {
-                       pr_err("queue_depth out of range\n");
-                       return -EINVAL;
-               }
-               pctx->opts->queue_depth = intval;
-               break;
-       case Opt_alloc_size:
-               if (intval < SECTOR_SIZE) {
-                       pr_err("alloc_size out of range\n");
-                       return -EINVAL;
-               }
-               if (!is_power_of_2(intval)) {
-                       pr_err("alloc_size must be a power of 2\n");
-                       return -EINVAL;
-               }
-               pctx->opts->alloc_size = intval;
-               break;
-       case Opt_lock_timeout:
-               /* 0 is "wait forever" (i.e. infinite timeout) */
-               if (intval < 0 || intval > INT_MAX / 1000) {
-                       pr_err("lock_timeout out of range\n");
-                       return -EINVAL;
-               }
-               pctx->opts->lock_timeout = msecs_to_jiffies(intval * 1000);
-               break;
-       case Opt_pool_ns:
-               kfree(pctx->spec->pool_ns);
-               pctx->spec->pool_ns = match_strdup(argstr);
-               if (!pctx->spec->pool_ns)
-                       return -ENOMEM;
-               break;
-       case Opt_read_only:
-               pctx->opts->read_only = true;
-               break;
-       case Opt_read_write:
-               pctx->opts->read_only = false;
-               break;
-       case Opt_lock_on_read:
-               pctx->opts->lock_on_read = true;
-               break;
-       case Opt_exclusive:
-               pctx->opts->exclusive = true;
-               break;
-       case Opt_notrim:
-               pctx->opts->trim = false;
-               break;
-       default:
-               /* libceph prints "bad option" msg */
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 static char* obj_op_name(enum obj_operation_type op_type)
 {
        switch (op_type) {
@@ -1302,51 +1242,23 @@ static int rbd_snap_size(struct rbd_device *rbd_dev, u64 snap_id,
        return 0;
 }
 
-static int rbd_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
-                       u64 *snap_features)
-{
-       rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
-       if (snap_id == CEPH_NOSNAP) {
-               *snap_features = rbd_dev->header.features;
-       } else if (rbd_dev->image_format == 1) {
-               *snap_features = 0;     /* No features for format 1 */
-       } else {
-               u64 features = 0;
-               int ret;
-
-               ret = _rbd_dev_v2_snap_features(rbd_dev, snap_id, &features);
-               if (ret)
-                       return ret;
-
-               *snap_features = features;
-       }
-       return 0;
-}
-
 static int rbd_dev_mapping_set(struct rbd_device *rbd_dev)
 {
        u64 snap_id = rbd_dev->spec->snap_id;
        u64 size = 0;
-       u64 features = 0;
        int ret;
 
        ret = rbd_snap_size(rbd_dev, snap_id, &size);
        if (ret)
                return ret;
-       ret = rbd_snap_features(rbd_dev, snap_id, &features);
-       if (ret)
-               return ret;
 
        rbd_dev->mapping.size = size;
-       rbd_dev->mapping.features = features;
-
        return 0;
 }
 
 static void rbd_dev_mapping_clear(struct rbd_device *rbd_dev)
 {
        rbd_dev->mapping.size = 0;
-       rbd_dev->mapping.features = 0;
 }
 
 static void zero_bvec(struct bio_vec *bv)
@@ -1832,6 +1744,17 @@ static u8 rbd_object_map_get(struct rbd_device *rbd_dev, u64 objno)
 
 static bool use_object_map(struct rbd_device *rbd_dev)
 {
+       /*
+        * An image mapped read-only can't use the object map -- it isn't
+        * loaded because the header lock isn't acquired.  Someone else can
+        * write to the image and update the object map behind our back.
+        *
+        * A snapshot can't be written to, so using the object map is always
+        * safe.
+        */
+       if (!rbd_is_snap(rbd_dev) && rbd_is_ro(rbd_dev))
+               return false;
+
        return ((rbd_dev->header.features & RBD_FEATURE_OBJECT_MAP) &&
                !(rbd_dev->object_map_flags & RBD_FLAG_OBJECT_MAP_INVALID));
 }
@@ -3555,7 +3478,7 @@ static bool need_exclusive_lock(struct rbd_img_request *img_req)
        if (!(rbd_dev->header.features & RBD_FEATURE_EXCLUSIVE_LOCK))
                return false;
 
-       if (rbd_dev->spec->snap_id != CEPH_NOSNAP)
+       if (rbd_is_ro(rbd_dev))
                return false;
 
        rbd_assert(!test_bit(IMG_REQ_CHILD, &img_req->flags));
@@ -4230,7 +4153,7 @@ again:
                 * lock owner acked, but resend if we don't see them
                 * release the lock
                 */
-               dout("%s rbd_dev %p requeueing lock_dwork\n", __func__,
+               dout("%s rbd_dev %p requeuing lock_dwork\n", __func__,
                     rbd_dev);
                mod_delayed_work(rbd_dev->task_wq, &rbd_dev->lock_dwork,
                    msecs_to_jiffies(2 * RBD_NOTIFY_TIMEOUT * MSEC_PER_SEC));
@@ -4826,24 +4749,14 @@ static void rbd_queue_workfn(struct work_struct *work)
                goto err_rq;
        }
 
-       if (op_type != OBJ_OP_READ && rbd_dev->spec->snap_id != CEPH_NOSNAP) {
-               rbd_warn(rbd_dev, "%s on read-only snapshot",
-                        obj_op_name(op_type));
-               result = -EIO;
-               goto err;
-       }
-
-       /*
-        * Quit early if the mapped snapshot no longer exists.  It's
-        * still possible the snapshot will have disappeared by the
-        * time our request arrives at the osd, but there's no sense in
-        * sending it if we already know.
-        */
-       if (!test_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags)) {
-               dout("request for non-existent snapshot");
-               rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP);
-               result = -ENXIO;
-               goto err_rq;
+       if (op_type != OBJ_OP_READ) {
+               if (rbd_is_ro(rbd_dev)) {
+                       rbd_warn(rbd_dev, "%s on read-only mapping",
+                                obj_op_name(op_type));
+                       result = -EIO;
+                       goto err;
+               }
+               rbd_assert(!rbd_is_snap(rbd_dev));
        }
 
        if (offset && length > U64_MAX - offset + 1) {
@@ -5025,25 +4938,6 @@ out:
        return ret;
 }
 
-/*
- * Clear the rbd device's EXISTS flag if the snapshot it's mapped to
- * has disappeared from the (just updated) snapshot context.
- */
-static void rbd_exists_validate(struct rbd_device *rbd_dev)
-{
-       u64 snap_id;
-
-       if (!test_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags))
-               return;
-
-       snap_id = rbd_dev->spec->snap_id;
-       if (snap_id == CEPH_NOSNAP)
-               return;
-
-       if (rbd_dev_snap_index(rbd_dev, snap_id) == BAD_SNAP_INDEX)
-               clear_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags);
-}
-
 static void rbd_dev_update_size(struct rbd_device *rbd_dev)
 {
        sector_t size;
@@ -5084,12 +4978,8 @@ static int rbd_dev_refresh(struct rbd_device *rbd_dev)
                        goto out;
        }
 
-       if (rbd_dev->spec->snap_id == CEPH_NOSNAP) {
-               rbd_dev->mapping.size = rbd_dev->header.image_size;
-       } else {
-               /* validate mapped snapshot's EXISTS flag */
-               rbd_exists_validate(rbd_dev);
-       }
+       rbd_assert(!rbd_is_snap(rbd_dev));
+       rbd_dev->mapping.size = rbd_dev->header.image_size;
 
 out:
        up_write(&rbd_dev->header_rwsem);
@@ -5211,17 +5101,12 @@ static ssize_t rbd_size_show(struct device *dev,
                (unsigned long long)rbd_dev->mapping.size);
 }
 
-/*
- * Note this shows the features for whatever's mapped, which is not
- * necessarily the base image.
- */
 static ssize_t rbd_features_show(struct device *dev,
                             struct device_attribute *attr, char *buf)
 {
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
 
-       return sprintf(buf, "0x%016llx\n",
-                       (unsigned long long)rbd_dev->mapping.features);
+       return sprintf(buf, "0x%016llx\n", rbd_dev->header.features);
 }
 
 static ssize_t rbd_major_show(struct device *dev,
@@ -5709,9 +5594,12 @@ out:
 }
 
 static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
-               u64 *snap_features)
+                                    bool read_only, u64 *snap_features)
 {
-       __le64 snapid = cpu_to_le64(snap_id);
+       struct {
+               __le64 snap_id;
+               u8 read_only;
+       } features_in;
        struct {
                __le64 features;
                __le64 incompat;
@@ -5719,9 +5607,12 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
        u64 unsup;
        int ret;
 
+       features_in.snap_id = cpu_to_le64(snap_id);
+       features_in.read_only = read_only;
+
        ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid,
                                  &rbd_dev->header_oloc, "get_features",
-                                 &snapid, sizeof(snapid),
+                                 &features_in, sizeof(features_in),
                                  &features_buf, sizeof(features_buf));
        dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
        if (ret < 0)
@@ -5749,7 +5640,8 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
 static int rbd_dev_v2_features(struct rbd_device *rbd_dev)
 {
        return _rbd_dev_v2_snap_features(rbd_dev, CEPH_NOSNAP,
-                                               &rbd_dev->header.features);
+                                        rbd_is_ro(rbd_dev),
+                                        &rbd_dev->header.features);
 }
 
 /*
@@ -6456,6 +6348,122 @@ static inline char *dup_token(const char **buf, size_t *lenp)
        return dup;
 }
 
+static int rbd_parse_param(struct fs_parameter *param,
+                           struct rbd_parse_opts_ctx *pctx)
+{
+       struct rbd_options *opt = pctx->opts;
+       struct fs_parse_result result;
+       int token, ret;
+
+       ret = ceph_parse_param(param, pctx->copts, NULL);
+       if (ret != -ENOPARAM)
+               return ret;
+
+       token = fs_parse(NULL, &rbd_parameters, param, &result);
+       dout("%s fs_parse '%s' token %d\n", __func__, param->key, token);
+       if (token < 0) {
+               if (token == -ENOPARAM) {
+                       return invalf(NULL, "rbd: Unknown parameter '%s'",
+                                     param->key);
+               }
+               return token;
+       }
+
+       switch (token) {
+       case Opt_queue_depth:
+               if (result.uint_32 < 1)
+                       goto out_of_range;
+               opt->queue_depth = result.uint_32;
+               break;
+       case Opt_alloc_size:
+               if (result.uint_32 < SECTOR_SIZE)
+                       goto out_of_range;
+               if (!is_power_of_2(result.uint_32)) {
+                       return invalf(NULL, "rbd: alloc_size must be a power of 2");
+               }
+               opt->alloc_size = result.uint_32;
+               break;
+       case Opt_lock_timeout:
+               /* 0 is "wait forever" (i.e. infinite timeout) */
+               if (result.uint_32 > INT_MAX / 1000)
+                       goto out_of_range;
+               opt->lock_timeout = msecs_to_jiffies(result.uint_32 * 1000);
+               break;
+       case Opt_pool_ns:
+               kfree(pctx->spec->pool_ns);
+               pctx->spec->pool_ns = param->string;
+               param->string = NULL;
+               break;
+       case Opt_read_only:
+               opt->read_only = true;
+               break;
+       case Opt_read_write:
+               opt->read_only = false;
+               break;
+       case Opt_lock_on_read:
+               opt->lock_on_read = true;
+               break;
+       case Opt_exclusive:
+               opt->exclusive = true;
+               break;
+       case Opt_notrim:
+               opt->trim = false;
+               break;
+       default:
+               BUG();
+       }
+
+       return 0;
+
+out_of_range:
+       return invalf(NULL, "rbd: %s out of range", param->key);
+}
+
+/*
+ * This duplicates most of generic_parse_monolithic(), untying it from
+ * fs_context and skipping standard superblock and security options.
+ */
+static int rbd_parse_options(char *options, struct rbd_parse_opts_ctx *pctx)
+{
+       char *key;
+       int ret = 0;
+
+       dout("%s '%s'\n", __func__, options);
+       while ((key = strsep(&options, ",")) != NULL) {
+               if (*key) {
+                       struct fs_parameter param = {
+                               .key    = key,
+                               .type   = fs_value_is_string,
+                       };
+                       char *value = strchr(key, '=');
+                       size_t v_len = 0;
+
+                       if (value) {
+                               if (value == key)
+                                       continue;
+                               *value++ = 0;
+                               v_len = strlen(value);
+                       }
+
+
+                       if (v_len > 0) {
+                               param.string = kmemdup_nul(value, v_len,
+                                                          GFP_KERNEL);
+                               if (!param.string)
+                                       return -ENOMEM;
+                       }
+                       param.size = v_len;
+
+                       ret = rbd_parse_param(&param, pctx);
+                       kfree(param.string);
+                       if (ret)
+                               break;
+               }
+       }
+
+       return ret;
+}
+
 /*
  * Parse the options provided for an "rbd add" (i.e., rbd image
  * mapping) request.  These arrive via a write to /sys/bus/rbd/add,
@@ -6507,8 +6515,7 @@ static int rbd_add_parse_args(const char *buf,
        const char *mon_addrs;
        char *snap_name;
        size_t mon_addrs_size;
-       struct parse_rbd_opts_ctx pctx = { 0 };
-       struct ceph_options *copts;
+       struct rbd_parse_opts_ctx pctx = { 0 };
        int ret;
 
        /* The first four tokens are required */
@@ -6519,7 +6526,7 @@ static int rbd_add_parse_args(const char *buf,
                return -EINVAL;
        }
        mon_addrs = buf;
-       mon_addrs_size = len + 1;
+       mon_addrs_size = len;
        buf += len;
 
        ret = -EINVAL;
@@ -6569,6 +6576,10 @@ static int rbd_add_parse_args(const char *buf,
        *(snap_name + len) = '\0';
        pctx.spec->snap_name = snap_name;
 
+       pctx.copts = ceph_alloc_options();
+       if (!pctx.copts)
+               goto out_mem;
+
        /* Initialize all rbd options to the defaults */
 
        pctx.opts = kzalloc(sizeof(*pctx.opts), GFP_KERNEL);
@@ -6583,27 +6594,27 @@ static int rbd_add_parse_args(const char *buf,
        pctx.opts->exclusive = RBD_EXCLUSIVE_DEFAULT;
        pctx.opts->trim = RBD_TRIM_DEFAULT;
 
-       copts = ceph_parse_options(options, mon_addrs,
-                                  mon_addrs + mon_addrs_size - 1,
-                                  parse_rbd_opts_token, &pctx);
-       if (IS_ERR(copts)) {
-               ret = PTR_ERR(copts);
+       ret = ceph_parse_mon_ips(mon_addrs, mon_addrs_size, pctx.copts, NULL);
+       if (ret)
+               goto out_err;
+
+       ret = rbd_parse_options(options, &pctx);
+       if (ret)
                goto out_err;
-       }
-       kfree(options);
 
-       *ceph_opts = copts;
+       *ceph_opts = pctx.copts;
        *opts = pctx.opts;
        *rbd_spec = pctx.spec;
-
+       kfree(options);
        return 0;
+
 out_mem:
        ret = -ENOMEM;
 out_err:
        kfree(pctx.opts);
+       ceph_destroy_options(pctx.copts);
        rbd_spec_put(pctx.spec);
        kfree(options);
-
        return ret;
 }
 
@@ -6632,7 +6643,7 @@ static int rbd_add_acquire_lock(struct rbd_device *rbd_dev)
                return -EINVAL;
        }
 
-       if (rbd_dev->spec->snap_id != CEPH_NOSNAP)
+       if (rbd_is_ro(rbd_dev))
                return 0;
 
        rbd_assert(!rbd_is_lock_owner(rbd_dev));
@@ -6838,6 +6849,8 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth)
        __rbd_get_client(rbd_dev->rbd_client);
        rbd_spec_get(rbd_dev->parent_spec);
 
+       __set_bit(RBD_DEV_FLAG_READONLY, &parent->flags);
+
        ret = rbd_dev_image_probe(parent, depth);
        if (ret < 0)
                goto out_err;
@@ -6889,7 +6902,7 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
                goto err_out_blkdev;
 
        set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE);
-       set_disk_ro(rbd_dev->disk, rbd_dev->opts->read_only);
+       set_disk_ro(rbd_dev->disk, rbd_is_ro(rbd_dev));
 
        ret = dev_set_name(&rbd_dev->dev, "%d", rbd_dev->dev_id);
        if (ret)
@@ -6927,6 +6940,24 @@ static int rbd_dev_header_name(struct rbd_device *rbd_dev)
        return ret;
 }
 
+static void rbd_print_dne(struct rbd_device *rbd_dev, bool is_snap)
+{
+       if (!is_snap) {
+               pr_info("image %s/%s%s%s does not exist\n",
+                       rbd_dev->spec->pool_name,
+                       rbd_dev->spec->pool_ns ?: "",
+                       rbd_dev->spec->pool_ns ? "/" : "",
+                       rbd_dev->spec->image_name);
+       } else {
+               pr_info("snap %s/%s%s%s@%s does not exist\n",
+                       rbd_dev->spec->pool_name,
+                       rbd_dev->spec->pool_ns ?: "",
+                       rbd_dev->spec->pool_ns ? "/" : "",
+                       rbd_dev->spec->image_name,
+                       rbd_dev->spec->snap_name);
+       }
+}
+
 static void rbd_dev_image_release(struct rbd_device *rbd_dev)
 {
        rbd_dev_unprobe(rbd_dev);
@@ -6945,6 +6976,7 @@ static void rbd_dev_image_release(struct rbd_device *rbd_dev)
  */
 static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
 {
+       bool need_watch = !rbd_is_ro(rbd_dev);
        int ret;
 
        /*
@@ -6961,22 +6993,21 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
        if (ret)
                goto err_out_format;
 
-       if (!depth) {
+       if (need_watch) {
                ret = rbd_register_watch(rbd_dev);
                if (ret) {
                        if (ret == -ENOENT)
-                               pr_info("image %s/%s%s%s does not exist\n",
-                                       rbd_dev->spec->pool_name,
-                                       rbd_dev->spec->pool_ns ?: "",
-                                       rbd_dev->spec->pool_ns ? "/" : "",
-                                       rbd_dev->spec->image_name);
+                               rbd_print_dne(rbd_dev, false);
                        goto err_out_format;
                }
        }
 
        ret = rbd_dev_header_info(rbd_dev);
-       if (ret)
+       if (ret) {
+               if (ret == -ENOENT && !need_watch)
+                       rbd_print_dne(rbd_dev, false);
                goto err_out_watch;
+       }
 
        /*
         * If this image is the one being mapped, we have pool name and
@@ -6990,12 +7021,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
                ret = rbd_spec_fill_names(rbd_dev);
        if (ret) {
                if (ret == -ENOENT)
-                       pr_info("snap %s/%s%s%s@%s does not exist\n",
-                               rbd_dev->spec->pool_name,
-                               rbd_dev->spec->pool_ns ?: "",
-                               rbd_dev->spec->pool_ns ? "/" : "",
-                               rbd_dev->spec->image_name,
-                               rbd_dev->spec->snap_name);
+                       rbd_print_dne(rbd_dev, true);
                goto err_out_probe;
        }
 
@@ -7003,7 +7029,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
        if (ret)
                goto err_out_probe;
 
-       if (rbd_dev->spec->snap_id != CEPH_NOSNAP &&
+       if (rbd_is_snap(rbd_dev) &&
            (rbd_dev->header.features & RBD_FEATURE_OBJECT_MAP)) {
                ret = rbd_object_map_load(rbd_dev);
                if (ret)
@@ -7027,7 +7053,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
 err_out_probe:
        rbd_dev_unprobe(rbd_dev);
 err_out_watch:
-       if (!depth)
+       if (need_watch)
                rbd_unregister_watch(rbd_dev);
 err_out_format:
        rbd_dev->image_format = 0;
@@ -7079,6 +7105,11 @@ static ssize_t do_rbd_add(struct bus_type *bus,
        spec = NULL;            /* rbd_dev now owns this */
        rbd_opts = NULL;        /* rbd_dev now owns this */
 
+       /* if we are mapping a snapshot it will be a read-only mapping */
+       if (rbd_dev->opts->read_only ||
+           strcmp(rbd_dev->spec->snap_name, RBD_SNAP_HEAD_NAME))
+               __set_bit(RBD_DEV_FLAG_READONLY, &rbd_dev->flags);
+
        rbd_dev->config_info = kstrdup(buf, GFP_KERNEL);
        if (!rbd_dev->config_info) {
                rc = -ENOMEM;
@@ -7092,10 +7123,6 @@ static ssize_t do_rbd_add(struct bus_type *bus,
                goto err_out_rbd_dev;
        }
 
-       /* If we are mapping a snapshot it must be marked read-only */
-       if (rbd_dev->spec->snap_id != CEPH_NOSNAP)
-               rbd_dev->opts->read_only = true;
-
        if (rbd_dev->opts->alloc_size > rbd_dev->layout.object_size) {
                rbd_warn(rbd_dev, "alloc_size adjusted to %u",
                         rbd_dev->layout.object_size);
index fd1e19f..3666afa 100644 (file)
@@ -936,6 +936,8 @@ next:
 out_of_memory:
        pr_alert("%s: out of memory\n", __func__);
        put_free_pages(ring, pages_to_gnt, segs_to_map);
+       for (i = last_map; i < num; i++)
+               pages[i]->handle = BLKBACK_INVALID_HANDLE;
        return -ENOMEM;
 }
 
index f695588..4709864 100644 (file)
@@ -102,14 +102,13 @@ agp_segment_priv *agp_find_seg_in_client(const struct agp_client *client,
                                            int size, pgprot_t page_prot)
 {
        struct agp_segment_priv *seg;
-       int num_segments, i;
+       int i;
        off_t pg_start;
        size_t pg_count;
 
        pg_start = offset / 4096;
        pg_count = size / 4096;
        seg = *(client->segments);
-       num_segments = client->num_segments;
 
        for (i = 0; i < client->num_segments; i++) {
                if ((seg[i].pg_start == pg_start) &&
index df1edb5..ab154a7 100644 (file)
@@ -207,6 +207,7 @@ EXPORT_SYMBOL(agp_free_memory);
 /**
  *     agp_allocate_memory  -  allocate a group of pages of a certain type.
  *
+ *     @bridge: an agp_bridge_data struct allocated for the AGP host bridge.
  *     @page_count:    size_t argument of the number of pages
  *     @type:  u32 argument of the type of memory to be allocated.
  *
@@ -355,6 +356,7 @@ EXPORT_SYMBOL_GPL(agp_num_entries);
 /**
  *     agp_copy_info  -  copy bridge state information
  *
+ *     @bridge: an agp_bridge_data struct allocated for the AGP host bridge.
  *     @info:          agp_kern_info pointer.  The caller should insure that this pointer is valid.
  *
  *     This function copies information about the agp bridge device and the state of
@@ -850,7 +852,6 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
 {
        char *table;
        char *table_end;
-       int size;
        int page_order;
        int num_entries;
        int i;
@@ -864,25 +865,22 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
        table = NULL;
        i = bridge->aperture_size_idx;
        temp = bridge->current_size;
-       size = page_order = num_entries = 0;
+       page_order = num_entries = 0;
 
        if (bridge->driver->size_type != FIXED_APER_SIZE) {
                do {
                        switch (bridge->driver->size_type) {
                        case U8_APER_SIZE:
-                               size = A_SIZE_8(temp)->size;
                                page_order =
                                    A_SIZE_8(temp)->page_order;
                                num_entries =
                                    A_SIZE_8(temp)->num_entries;
                                break;
                        case U16_APER_SIZE:
-                               size = A_SIZE_16(temp)->size;
                                page_order = A_SIZE_16(temp)->page_order;
                                num_entries = A_SIZE_16(temp)->num_entries;
                                break;
                        case U32_APER_SIZE:
-                               size = A_SIZE_32(temp)->size;
                                page_order = A_SIZE_32(temp)->page_order;
                                num_entries = A_SIZE_32(temp)->num_entries;
                                break;
@@ -890,7 +888,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
                        case FIXED_APER_SIZE:
                        case LVL2_APER_SIZE:
                        default:
-                               size = page_order = num_entries = 0;
+                               page_order = num_entries = 0;
                                break;
                        }
 
@@ -920,7 +918,6 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
                        }
                } while (!table && (i < bridge->driver->num_aperture_sizes));
        } else {
-               size = ((struct aper_size_info_fixed *) temp)->size;
                page_order = ((struct aper_size_info_fixed *) temp)->page_order;
                num_entries = ((struct aper_size_info_fixed *) temp)->num_entries;
                table = alloc_gatt_pages(page_order);
@@ -1282,6 +1279,7 @@ EXPORT_SYMBOL(agp_generic_destroy_page);
 /**
  * agp_enable  -  initialise the agp point-to-point connection.
  *
+ * @bridge: an agp_bridge_data struct allocated for the AGP host bridge.
  * @mode:      agp mode register value to configure with.
  */
 void agp_enable(struct agp_bridge_data *bridge, u32 mode)
index bee8729..48e2ef7 100644 (file)
@@ -442,6 +442,41 @@ int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req,
                req, req_cnt * sizeof(*req), resp, sizeof(*resp));
 }
 
+int __qcom_scm_ocmem_lock(struct device *dev, u32 id, u32 offset, u32 size,
+                         u32 mode)
+{
+       struct ocmem_tz_lock {
+               __le32 id;
+               __le32 offset;
+               __le32 size;
+               __le32 mode;
+       } request;
+
+       request.id = cpu_to_le32(id);
+       request.offset = cpu_to_le32(offset);
+       request.size = cpu_to_le32(size);
+       request.mode = cpu_to_le32(mode);
+
+       return qcom_scm_call(dev, QCOM_SCM_OCMEM_SVC, QCOM_SCM_OCMEM_LOCK_CMD,
+                            &request, sizeof(request), NULL, 0);
+}
+
+int __qcom_scm_ocmem_unlock(struct device *dev, u32 id, u32 offset, u32 size)
+{
+       struct ocmem_tz_unlock {
+               __le32 id;
+               __le32 offset;
+               __le32 size;
+       } request;
+
+       request.id = cpu_to_le32(id);
+       request.offset = cpu_to_le32(offset);
+       request.size = cpu_to_le32(size);
+
+       return qcom_scm_call(dev, QCOM_SCM_OCMEM_SVC, QCOM_SCM_OCMEM_UNLOCK_CMD,
+                            &request, sizeof(request), NULL, 0);
+}
+
 void __qcom_scm_init(void)
 {
 }
@@ -582,7 +617,22 @@ int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region,
 int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id,
                               u32 spare)
 {
-       return -ENODEV;
+       struct msm_scm_sec_cfg {
+               __le32 id;
+               __le32 ctx_bank_num;
+       } cfg;
+       int ret, scm_ret = 0;
+
+       cfg.id = cpu_to_le32(device_id);
+       cfg.ctx_bank_num = cpu_to_le32(spare);
+
+       ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP, QCOM_SCM_RESTORE_SEC_CFG,
+                           &cfg, sizeof(cfg), &scm_ret, sizeof(scm_ret));
+
+       if (ret || scm_ret)
+               return ret ? ret : -EINVAL;
+
+       return 0;
 }
 
 int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare,
index e1cd933..3c58503 100644 (file)
@@ -291,6 +291,18 @@ int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req,
        return ret;
 }
 
+int __qcom_scm_ocmem_lock(struct device *dev, uint32_t id, uint32_t offset,
+                         uint32_t size, uint32_t mode)
+{
+       return -ENOTSUPP;
+}
+
+int __qcom_scm_ocmem_unlock(struct device *dev, uint32_t id, uint32_t offset,
+                           uint32_t size)
+{
+       return -ENOTSUPP;
+}
+
 void __qcom_scm_init(void)
 {
        u64 cmd;
index a729e05..1ba0df4 100644 (file)
@@ -192,6 +192,46 @@ bool qcom_scm_pas_supported(u32 peripheral)
 EXPORT_SYMBOL(qcom_scm_pas_supported);
 
 /**
+ * qcom_scm_ocmem_lock_available() - is OCMEM lock/unlock interface available
+ */
+bool qcom_scm_ocmem_lock_available(void)
+{
+       return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_OCMEM_SVC,
+                                           QCOM_SCM_OCMEM_LOCK_CMD);
+}
+EXPORT_SYMBOL(qcom_scm_ocmem_lock_available);
+
+/**
+ * qcom_scm_ocmem_lock() - call OCMEM lock interface to assign an OCMEM
+ * region to the specified initiator
+ *
+ * @id:     tz initiator id
+ * @offset: OCMEM offset
+ * @size:   OCMEM size
+ * @mode:   access mode (WIDE/NARROW)
+ */
+int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, u32 size,
+                       u32 mode)
+{
+       return __qcom_scm_ocmem_lock(__scm->dev, id, offset, size, mode);
+}
+EXPORT_SYMBOL(qcom_scm_ocmem_lock);
+
+/**
+ * qcom_scm_ocmem_unlock() - call OCMEM unlock interface to release an OCMEM
+ * region from the specified initiator
+ *
+ * @id:     tz initiator id
+ * @offset: OCMEM offset
+ * @size:   OCMEM size
+ */
+int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size)
+{
+       return __qcom_scm_ocmem_unlock(__scm->dev, id, offset, size);
+}
+EXPORT_SYMBOL(qcom_scm_ocmem_unlock);
+
+/**
  * qcom_scm_pas_init_image() - Initialize peripheral authentication service
  *                            state machine for a given peripheral, using the
  *                            metadata
@@ -327,6 +367,19 @@ static const struct reset_control_ops qcom_scm_pas_reset_ops = {
        .deassert = qcom_scm_pas_reset_deassert,
 };
 
+/**
+ * qcom_scm_restore_sec_cfg_available() - Check if secure environment
+ * supports restore security config interface.
+ *
+ * Return true if restore-cfg interface is supported, false if not.
+ */
+bool qcom_scm_restore_sec_cfg_available(void)
+{
+       return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP,
+                                           QCOM_SCM_RESTORE_SEC_CFG);
+}
+EXPORT_SYMBOL(qcom_scm_restore_sec_cfg_available);
+
 int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare)
 {
        return __qcom_scm_restore_sec_cfg(__scm->dev, device_id, spare);
index baee744..81dcf5f 100644 (file)
@@ -42,6 +42,15 @@ extern int __qcom_scm_hdcp_req(struct device *dev,
 
 extern void __qcom_scm_init(void);
 
+#define QCOM_SCM_OCMEM_SVC                     0xf
+#define QCOM_SCM_OCMEM_LOCK_CMD                0x1
+#define QCOM_SCM_OCMEM_UNLOCK_CMD              0x2
+
+extern int __qcom_scm_ocmem_lock(struct device *dev, u32 id, u32 offset,
+                                u32 size, u32 mode);
+extern int __qcom_scm_ocmem_unlock(struct device *dev, u32 id, u32 offset,
+                                  u32 size);
+
 #define QCOM_SCM_SVC_PIL               0x2
 #define QCOM_SCM_PAS_INIT_IMAGE_CMD    0x1
 #define QCOM_SCM_PAS_MEM_SETUP_CMD     0x2
index 1168351..bfdadc3 100644 (file)
@@ -95,6 +95,7 @@ config DRM_KMS_FB_HELPER
 
 config DRM_DEBUG_DP_MST_TOPOLOGY_REFS
         bool "Enable refcount backtrace history in the DP MST helpers"
+       depends on STACKTRACE_SUPPORT
         select STACKDEPOT
         depends on DRM_KMS_HELPER
         depends on DEBUG_KERNEL
index 7d35b5b..888209e 100644 (file)
@@ -105,11 +105,24 @@ void amdgpu_amdkfd_gpuvm_init_mem_limits(void)
                (kfd_mem_limit.max_ttm_mem_limit >> 20));
 }
 
+/* Estimate page table size needed to represent a given memory size
+ *
+ * With 4KB pages, we need one 8 byte PTE for each 4KB of memory
+ * (factor 512, >> 9). With 2MB pages, we need one 8 byte PTE for 2MB
+ * of memory (factor 256K, >> 18). ROCm user mode tries to optimize
+ * for 2MB pages for TLB efficiency. However, small allocations and
+ * fragmented system memory still need some 4KB pages. We choose a
+ * compromise that should work in most cases without reserving too
+ * much memory for page tables unnecessarily (factor 16K, >> 14).
+ */
+#define ESTIMATE_PT_SIZE(mem_size) ((mem_size) >> 14)
+
 static int amdgpu_amdkfd_reserve_mem_limit(struct amdgpu_device *adev,
                uint64_t size, u32 domain, bool sg)
 {
+       uint64_t reserved_for_pt =
+               ESTIMATE_PT_SIZE(amdgpu_amdkfd_total_mem_size);
        size_t acc_size, system_mem_needed, ttm_mem_needed, vram_needed;
-       uint64_t reserved_for_pt = amdgpu_amdkfd_total_mem_size >> 9;
        int ret = 0;
 
        acc_size = ttm_bo_dma_acc_size(&adev->mman.bdev, size,
index 2770cba..44be3a4 100644 (file)
@@ -1487,8 +1487,8 @@ out:
                        return ret;
 
                /* Start rlc autoload after psp recieved all the gfx firmware */
-               if (psp->autoload_supported && ucode->ucode_id ==
-                       AMDGPU_UCODE_ID_RLC_RESTORE_LIST_SRM_MEM) {
+               if (psp->autoload_supported && ucode->ucode_id == (amdgpu_sriov_vf(adev) ?
+                   AMDGPU_UCODE_ID_CP_MEC2 : AMDGPU_UCODE_ID_RLC_RESTORE_LIST_SRM_MEM)) {
                        ret = psp_rlc_autoload(psp);
                        if (ret) {
                                DRM_ERROR("Failed to start rlc autoload\n");
index 7de16c0..2a8e048 100644 (file)
@@ -27,7 +27,8 @@
 #include <linux/bits.h>
 #include "smu_v11_0_i2c.h"
 
-#define EEPROM_I2C_TARGET_ADDR 0xA0
+#define EEPROM_I2C_TARGET_ADDR_ARCTURUS  0xA8
+#define EEPROM_I2C_TARGET_ADDR_VEGA20    0xA0
 
 /*
  * The 2 macros bellow represent the actual size in bytes that
@@ -83,7 +84,7 @@ static int __update_table_header(struct amdgpu_ras_eeprom_control *control,
 {
        int ret = 0;
        struct i2c_msg msg = {
-                       .addr   = EEPROM_I2C_TARGET_ADDR,
+                       .addr   = 0,
                        .flags  = 0,
                        .len    = EEPROM_ADDRESS_SIZE + EEPROM_TABLE_HEADER_SIZE,
                        .buf    = buff,
@@ -93,6 +94,8 @@ static int __update_table_header(struct amdgpu_ras_eeprom_control *control,
        *(uint16_t *)buff = EEPROM_HDR_START;
        __encode_table_header_to_buff(&control->tbl_hdr, buff + EEPROM_ADDRESS_SIZE);
 
+       msg.addr = control->i2c_address;
+
        ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
        if (ret < 1)
                DRM_ERROR("Failed to write EEPROM table header, ret:%d", ret);
@@ -203,7 +206,7 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
        unsigned char buff[EEPROM_ADDRESS_SIZE + EEPROM_TABLE_HEADER_SIZE] = { 0 };
        struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
        struct i2c_msg msg = {
-                       .addr   = EEPROM_I2C_TARGET_ADDR,
+                       .addr   = 0,
                        .flags  = I2C_M_RD,
                        .len    = EEPROM_ADDRESS_SIZE + EEPROM_TABLE_HEADER_SIZE,
                        .buf    = buff,
@@ -213,10 +216,12 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
 
        switch (adev->asic_type) {
        case CHIP_VEGA20:
+               control->i2c_address = EEPROM_I2C_TARGET_ADDR_VEGA20;
                ret = smu_v11_0_i2c_eeprom_control_init(&control->eeprom_accessor);
                break;
 
        case CHIP_ARCTURUS:
+               control->i2c_address = EEPROM_I2C_TARGET_ADDR_ARCTURUS;
                ret = smu_i2c_eeprom_init(&adev->smu, &control->eeprom_accessor);
                break;
 
@@ -229,6 +234,8 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
                return ret;
        }
 
+       msg.addr = control->i2c_address;
+
        /* Read/Create table header from EEPROM address 0 */
        ret = i2c_transfer(&control->eeprom_accessor, &msg, 1);
        if (ret < 1) {
@@ -408,8 +415,8 @@ int amdgpu_ras_eeprom_process_recods(struct amdgpu_ras_eeprom_control *control,
                 * Update bits 16,17 of EEPROM address in I2C address by setting them
                 * to bits 1,2 of Device address byte
                 */
-               msg->addr = EEPROM_I2C_TARGET_ADDR |
-                              ((control->next_addr & EEPROM_ADDR_MSB_MASK) >> 15);
+               msg->addr = control->i2c_address |
+                               ((control->next_addr & EEPROM_ADDR_MSB_MASK) >> 15);
                msg->flags      = write ? 0 : I2C_M_RD;
                msg->len        = EEPROM_ADDRESS_SIZE + EEPROM_TABLE_RECORD_SIZE;
                msg->buf        = buff;
index 6222699..ca78f81 100644 (file)
@@ -50,6 +50,7 @@ struct amdgpu_ras_eeprom_control {
        struct mutex tbl_mutex;
        bool bus_locked;
        uint32_t tbl_byte_sum;
+       uint16_t i2c_address; // 8-bit represented address
 };
 
 /*
index c8793e6..6373bfb 100644 (file)
@@ -124,13 +124,12 @@ int amdgpu_gfx_rlc_init_sr(struct amdgpu_device *adev, u32 dws)
  */
 int amdgpu_gfx_rlc_init_csb(struct amdgpu_device *adev)
 {
-       volatile u32 *dst_ptr;
        u32 dws;
        int r;
 
        /* allocate clear state block */
        adev->gfx.rlc.clear_state_size = dws = adev->gfx.rlc.funcs->get_csb_size(adev);
-       r = amdgpu_bo_create_reserved(adev, dws * 4, PAGE_SIZE,
+       r = amdgpu_bo_create_kernel(adev, dws * 4, PAGE_SIZE,
                                      AMDGPU_GEM_DOMAIN_VRAM,
                                      &adev->gfx.rlc.clear_state_obj,
                                      &adev->gfx.rlc.clear_state_gpu_addr,
@@ -141,13 +140,6 @@ int amdgpu_gfx_rlc_init_csb(struct amdgpu_device *adev)
                return r;
        }
 
-       /* set up the cs buffer */
-       dst_ptr = adev->gfx.rlc.cs_ptr;
-       adev->gfx.rlc.funcs->get_csb_buffer(adev, dst_ptr);
-       amdgpu_bo_kunmap(adev->gfx.rlc.clear_state_obj);
-       amdgpu_bo_unpin(adev->gfx.rlc.clear_state_obj);
-       amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
-
        return 0;
 }
 
index 7a43993..1befdee 100644 (file)
@@ -1346,10 +1346,13 @@ static int cik_asic_reset(struct amdgpu_device *adev)
 {
        int r;
 
-       if (cik_asic_reset_method(adev) == AMD_RESET_METHOD_BACO)
+       if (cik_asic_reset_method(adev) == AMD_RESET_METHOD_BACO) {
+               if (!adev->in_suspend)
+                       amdgpu_inc_vram_lost(adev);
                r = smu7_asic_baco_reset(adev);
-       else
+       } else {
                r = cik_asic_pci_config_reset(adev);
+       }
 
        return r;
 }
index ca5f0e7..f2c1b02 100644 (file)
@@ -690,59 +690,61 @@ static int gfx_v10_0_init_microcode(struct amdgpu_device *adev)
        adev->gfx.ce_fw_version = le32_to_cpu(cp_hdr->header.ucode_version);
        adev->gfx.ce_feature_version = le32_to_cpu(cp_hdr->ucode_feature_version);
 
-       snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_rlc.bin", chip_name);
-       err = request_firmware(&adev->gfx.rlc_fw, fw_name, adev->dev);
-       if (err)
-               goto out;
-       err = amdgpu_ucode_validate(adev->gfx.rlc_fw);
-       rlc_hdr = (const struct rlc_firmware_header_v2_0 *)adev->gfx.rlc_fw->data;
-       version_major = le16_to_cpu(rlc_hdr->header.header_version_major);
-       version_minor = le16_to_cpu(rlc_hdr->header.header_version_minor);
-       if (version_major == 2 && version_minor == 1)
-               adev->gfx.rlc.is_rlc_v2_1 = true;
-
-       adev->gfx.rlc_fw_version = le32_to_cpu(rlc_hdr->header.ucode_version);
-       adev->gfx.rlc_feature_version = le32_to_cpu(rlc_hdr->ucode_feature_version);
-       adev->gfx.rlc.save_and_restore_offset =
+       if (!amdgpu_sriov_vf(adev)) {
+               snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_rlc.bin", chip_name);
+               err = request_firmware(&adev->gfx.rlc_fw, fw_name, adev->dev);
+               if (err)
+                       goto out;
+               err = amdgpu_ucode_validate(adev->gfx.rlc_fw);
+               rlc_hdr = (const struct rlc_firmware_header_v2_0 *)adev->gfx.rlc_fw->data;
+               version_major = le16_to_cpu(rlc_hdr->header.header_version_major);
+               version_minor = le16_to_cpu(rlc_hdr->header.header_version_minor);
+               if (version_major == 2 && version_minor == 1)
+                       adev->gfx.rlc.is_rlc_v2_1 = true;
+
+               adev->gfx.rlc_fw_version = le32_to_cpu(rlc_hdr->header.ucode_version);
+               adev->gfx.rlc_feature_version = le32_to_cpu(rlc_hdr->ucode_feature_version);
+               adev->gfx.rlc.save_and_restore_offset =
                        le32_to_cpu(rlc_hdr->save_and_restore_offset);
-       adev->gfx.rlc.clear_state_descriptor_offset =
+               adev->gfx.rlc.clear_state_descriptor_offset =
                        le32_to_cpu(rlc_hdr->clear_state_descriptor_offset);
-       adev->gfx.rlc.avail_scratch_ram_locations =
+               adev->gfx.rlc.avail_scratch_ram_locations =
                        le32_to_cpu(rlc_hdr->avail_scratch_ram_locations);
-       adev->gfx.rlc.reg_restore_list_size =
+               adev->gfx.rlc.reg_restore_list_size =
                        le32_to_cpu(rlc_hdr->reg_restore_list_size);
-       adev->gfx.rlc.reg_list_format_start =
+               adev->gfx.rlc.reg_list_format_start =
                        le32_to_cpu(rlc_hdr->reg_list_format_start);
-       adev->gfx.rlc.reg_list_format_separate_start =
+               adev->gfx.rlc.reg_list_format_separate_start =
                        le32_to_cpu(rlc_hdr->reg_list_format_separate_start);
-       adev->gfx.rlc.starting_offsets_start =
+               adev->gfx.rlc.starting_offsets_start =
                        le32_to_cpu(rlc_hdr->starting_offsets_start);
-       adev->gfx.rlc.reg_list_format_size_bytes =
+               adev->gfx.rlc.reg_list_format_size_bytes =
                        le32_to_cpu(rlc_hdr->reg_list_format_size_bytes);
-       adev->gfx.rlc.reg_list_size_bytes =
+               adev->gfx.rlc.reg_list_size_bytes =
                        le32_to_cpu(rlc_hdr->reg_list_size_bytes);
-       adev->gfx.rlc.register_list_format =
+               adev->gfx.rlc.register_list_format =
                        kmalloc(adev->gfx.rlc.reg_list_format_size_bytes +
-                               adev->gfx.rlc.reg_list_size_bytes, GFP_KERNEL);
-       if (!adev->gfx.rlc.register_list_format) {
-               err = -ENOMEM;
-               goto out;
-       }
+                                       adev->gfx.rlc.reg_list_size_bytes, GFP_KERNEL);
+               if (!adev->gfx.rlc.register_list_format) {
+                       err = -ENOMEM;
+                       goto out;
+               }
 
-       tmp = (unsigned int *)((uintptr_t)rlc_hdr +
-                       le32_to_cpu(rlc_hdr->reg_list_format_array_offset_bytes));
-       for (i = 0 ; i < (rlc_hdr->reg_list_format_size_bytes >> 2); i++)
-               adev->gfx.rlc.register_list_format[i] = le32_to_cpu(tmp[i]);
+               tmp = (unsigned int *)((uintptr_t)rlc_hdr +
+                                                          le32_to_cpu(rlc_hdr->reg_list_format_array_offset_bytes));
+               for (i = 0 ; i < (rlc_hdr->reg_list_format_size_bytes >> 2); i++)
+                       adev->gfx.rlc.register_list_format[i] = le32_to_cpu(tmp[i]);
 
-       adev->gfx.rlc.register_restore = adev->gfx.rlc.register_list_format + i;
+               adev->gfx.rlc.register_restore = adev->gfx.rlc.register_list_format + i;
 
-       tmp = (unsigned int *)((uintptr_t)rlc_hdr +
-                       le32_to_cpu(rlc_hdr->reg_list_array_offset_bytes));
-       for (i = 0 ; i < (rlc_hdr->reg_list_size_bytes >> 2); i++)
-               adev->gfx.rlc.register_restore[i] = le32_to_cpu(tmp[i]);
+               tmp = (unsigned int *)((uintptr_t)rlc_hdr +
+                                                          le32_to_cpu(rlc_hdr->reg_list_array_offset_bytes));
+               for (i = 0 ; i < (rlc_hdr->reg_list_size_bytes >> 2); i++)
+                       adev->gfx.rlc.register_restore[i] = le32_to_cpu(tmp[i]);
 
-       if (adev->gfx.rlc.is_rlc_v2_1)
-               gfx_v10_0_init_rlc_ext_microcode(adev);
+               if (adev->gfx.rlc.is_rlc_v2_1)
+                       gfx_v10_0_init_rlc_ext_microcode(adev);
+       }
 
        snprintf(fw_name, sizeof(fw_name), "amdgpu/%s_mec%s.bin", chip_name, wks);
        err = request_firmware(&adev->gfx.mec_fw, fw_name, adev->dev);
@@ -993,39 +995,6 @@ static int gfx_v10_0_rlc_init(struct amdgpu_device *adev)
        return 0;
 }
 
-static int gfx_v10_0_csb_vram_pin(struct amdgpu_device *adev)
-{
-       int r;
-
-       r = amdgpu_bo_reserve(adev->gfx.rlc.clear_state_obj, false);
-       if (unlikely(r != 0))
-               return r;
-
-       r = amdgpu_bo_pin(adev->gfx.rlc.clear_state_obj,
-                       AMDGPU_GEM_DOMAIN_VRAM);
-       if (!r)
-               adev->gfx.rlc.clear_state_gpu_addr =
-                       amdgpu_bo_gpu_offset(adev->gfx.rlc.clear_state_obj);
-
-       amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
-
-       return r;
-}
-
-static void gfx_v10_0_csb_vram_unpin(struct amdgpu_device *adev)
-{
-       int r;
-
-       if (!adev->gfx.rlc.clear_state_obj)
-               return;
-
-       r = amdgpu_bo_reserve(adev->gfx.rlc.clear_state_obj, true);
-       if (likely(r == 0)) {
-               amdgpu_bo_unpin(adev->gfx.rlc.clear_state_obj);
-               amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
-       }
-}
-
 static void gfx_v10_0_mec_fini(struct amdgpu_device *adev)
 {
        amdgpu_bo_free_kernel(&adev->gfx.mec.hpd_eop_obj, NULL, NULL);
@@ -1787,25 +1756,7 @@ static void gfx_v10_0_enable_gui_idle_interrupt(struct amdgpu_device *adev,
 
 static int gfx_v10_0_init_csb(struct amdgpu_device *adev)
 {
-       int r;
-
-       if (adev->in_gpu_reset) {
-               r = amdgpu_bo_reserve(adev->gfx.rlc.clear_state_obj, false);
-               if (r)
-                       return r;
-
-               r = amdgpu_bo_kmap(adev->gfx.rlc.clear_state_obj,
-                                  (void **)&adev->gfx.rlc.cs_ptr);
-               if (!r) {
-                       adev->gfx.rlc.funcs->get_csb_buffer(adev,
-                                       adev->gfx.rlc.cs_ptr);
-                       amdgpu_bo_kunmap(adev->gfx.rlc.clear_state_obj);
-               }
-
-               amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
-               if (r)
-                       return r;
-       }
+       adev->gfx.rlc.funcs->get_csb_buffer(adev, adev->gfx.rlc.cs_ptr);
 
        /* csib */
        WREG32_SOC15(GC, 0, mmRLC_CSIB_ADDR_HI,
@@ -1817,22 +1768,6 @@ static int gfx_v10_0_init_csb(struct amdgpu_device *adev)
        return 0;
 }
 
-static int gfx_v10_0_init_pg(struct amdgpu_device *adev)
-{
-       int i;
-       int r;
-
-       r = gfx_v10_0_init_csb(adev);
-       if (r)
-               return r;
-
-       for (i = 0; i < adev->num_vmhubs; i++)
-               amdgpu_gmc_flush_gpu_tlb(adev, 0, i, 0);
-
-       /* TODO: init power gating */
-       return 0;
-}
-
 void gfx_v10_0_rlc_stop(struct amdgpu_device *adev)
 {
        u32 tmp = RREG32_SOC15(GC, 0, mmRLC_CNTL);
@@ -1925,21 +1860,16 @@ static int gfx_v10_0_rlc_resume(struct amdgpu_device *adev)
 {
        int r;
 
-       if (amdgpu_sriov_vf(adev))
-               return 0;
-
        if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
-               r = gfx_v10_0_wait_for_rlc_autoload_complete(adev);
-               if (r)
-                       return r;
 
-               r = gfx_v10_0_init_pg(adev);
+               r = gfx_v10_0_wait_for_rlc_autoload_complete(adev);
                if (r)
                        return r;
 
-               /* enable RLC SRM */
-               gfx_v10_0_rlc_enable_srm(adev);
+               gfx_v10_0_init_csb(adev);
 
+               if (!amdgpu_sriov_vf(adev)) /* enable RLC SRM */
+                       gfx_v10_0_rlc_enable_srm(adev);
        } else {
                adev->gfx.rlc.funcs->stop(adev);
 
@@ -1961,9 +1891,7 @@ static int gfx_v10_0_rlc_resume(struct amdgpu_device *adev)
                                return r;
                }
 
-               r = gfx_v10_0_init_pg(adev);
-               if (r)
-                       return r;
+               gfx_v10_0_init_csb(adev);
 
                adev->gfx.rlc.funcs->start(adev);
 
@@ -2825,7 +2753,7 @@ static int gfx_v10_0_cp_gfx_resume(struct amdgpu_device *adev)
        /* Init gfx ring 0 for pipe 0 */
        mutex_lock(&adev->srbm_mutex);
        gfx_v10_0_cp_gfx_switch_pipe(adev, PIPE_ID0);
-       mutex_unlock(&adev->srbm_mutex);
+
        /* Set ring buffer size */
        ring = &adev->gfx.gfx_ring[0];
        rb_bufsz = order_base_2(ring->ring_size / 8);
@@ -2863,11 +2791,11 @@ static int gfx_v10_0_cp_gfx_resume(struct amdgpu_device *adev)
        WREG32_SOC15(GC, 0, mmCP_RB_ACTIVE, 1);
 
        gfx_v10_0_cp_gfx_set_doorbell(adev, ring);
+       mutex_unlock(&adev->srbm_mutex);
 
        /* Init gfx ring 1 for pipe 1 */
        mutex_lock(&adev->srbm_mutex);
        gfx_v10_0_cp_gfx_switch_pipe(adev, PIPE_ID1);
-       mutex_unlock(&adev->srbm_mutex);
        ring = &adev->gfx.gfx_ring[1];
        rb_bufsz = order_base_2(ring->ring_size / 8);
        tmp = REG_SET_FIELD(0, CP_RB1_CNTL, RB_BUFSZ, rb_bufsz);
@@ -2897,6 +2825,7 @@ static int gfx_v10_0_cp_gfx_resume(struct amdgpu_device *adev)
        WREG32_SOC15(GC, 0, mmCP_RB1_ACTIVE, 1);
 
        gfx_v10_0_cp_gfx_set_doorbell(adev, ring);
+       mutex_unlock(&adev->srbm_mutex);
 
        /* Switch to pipe 0 */
        mutex_lock(&adev->srbm_mutex);
@@ -3775,10 +3704,6 @@ static int gfx_v10_0_hw_init(void *handle)
        int r;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       r = gfx_v10_0_csb_vram_pin(adev);
-       if (r)
-               return r;
-
        if (!amdgpu_emu_mode)
                gfx_v10_0_init_golden_registers(adev);
 
@@ -3861,12 +3786,11 @@ static int gfx_v10_0_hw_fini(void *handle)
        if (amdgpu_gfx_disable_kcq(adev))
                DRM_ERROR("KCQ disable failed\n");
        if (amdgpu_sriov_vf(adev)) {
-               pr_debug("For SRIOV client, shouldn't do anything.\n");
+               gfx_v10_0_cp_gfx_enable(adev, false);
                return 0;
        }
        gfx_v10_0_cp_enable(adev, false);
        gfx_v10_0_enable_gui_idle_interrupt(adev, false);
-       gfx_v10_0_csb_vram_unpin(adev);
 
        return 0;
 }
index 791ba39..d92e92e 100644 (file)
@@ -4554,6 +4554,8 @@ static int gfx_v7_0_hw_init(void *handle)
 
        gfx_v7_0_constants_init(adev);
 
+       /* init CSB */
+       adev->gfx.rlc.funcs->get_csb_buffer(adev, adev->gfx.rlc.cs_ptr);
        /* init rlc */
        r = adev->gfx.rlc.funcs->resume(adev);
        if (r)
index ffbde91..983db77 100644 (file)
@@ -1321,39 +1321,6 @@ static int gfx_v8_0_rlc_init(struct amdgpu_device *adev)
        return 0;
 }
 
-static int gfx_v8_0_csb_vram_pin(struct amdgpu_device *adev)
-{
-       int r;
-
-       r = amdgpu_bo_reserve(adev->gfx.rlc.clear_state_obj, false);
-       if (unlikely(r != 0))
-               return r;
-
-       r = amdgpu_bo_pin(adev->gfx.rlc.clear_state_obj,
-                       AMDGPU_GEM_DOMAIN_VRAM);
-       if (!r)
-               adev->gfx.rlc.clear_state_gpu_addr =
-                       amdgpu_bo_gpu_offset(adev->gfx.rlc.clear_state_obj);
-
-       amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
-
-       return r;
-}
-
-static void gfx_v8_0_csb_vram_unpin(struct amdgpu_device *adev)
-{
-       int r;
-
-       if (!adev->gfx.rlc.clear_state_obj)
-               return;
-
-       r = amdgpu_bo_reserve(adev->gfx.rlc.clear_state_obj, true);
-       if (likely(r == 0)) {
-               amdgpu_bo_unpin(adev->gfx.rlc.clear_state_obj);
-               amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
-       }
-}
-
 static void gfx_v8_0_mec_fini(struct amdgpu_device *adev)
 {
        amdgpu_bo_free_kernel(&adev->gfx.mec.hpd_eop_obj, NULL, NULL);
@@ -3917,6 +3884,7 @@ static void gfx_v8_0_enable_gui_idle_interrupt(struct amdgpu_device *adev,
 
 static void gfx_v8_0_init_csb(struct amdgpu_device *adev)
 {
+       adev->gfx.rlc.funcs->get_csb_buffer(adev, adev->gfx.rlc.cs_ptr);
        /* csib */
        WREG32(mmRLC_CSIB_ADDR_HI,
                        adev->gfx.rlc.clear_state_gpu_addr >> 32);
@@ -4837,10 +4805,6 @@ static int gfx_v8_0_hw_init(void *handle)
        gfx_v8_0_init_golden_registers(adev);
        gfx_v8_0_constants_init(adev);
 
-       r = gfx_v8_0_csb_vram_pin(adev);
-       if (r)
-               return r;
-
        r = adev->gfx.rlc.funcs->resume(adev);
        if (r)
                return r;
@@ -4958,8 +4922,6 @@ static int gfx_v8_0_hw_fini(void *handle)
                pr_err("rlc is busy, skip halt rlc\n");
        amdgpu_gfx_rlc_exit_safe_mode(adev);
 
-       gfx_v8_0_csb_vram_unpin(adev);
-
        return 0;
 }
 
index faf2ffc..66328ff 100644 (file)
@@ -1695,39 +1695,6 @@ static int gfx_v9_0_rlc_init(struct amdgpu_device *adev)
        return 0;
 }
 
-static int gfx_v9_0_csb_vram_pin(struct amdgpu_device *adev)
-{
-       int r;
-
-       r = amdgpu_bo_reserve(adev->gfx.rlc.clear_state_obj, false);
-       if (unlikely(r != 0))
-               return r;
-
-       r = amdgpu_bo_pin(adev->gfx.rlc.clear_state_obj,
-                       AMDGPU_GEM_DOMAIN_VRAM);
-       if (!r)
-               adev->gfx.rlc.clear_state_gpu_addr =
-                       amdgpu_bo_gpu_offset(adev->gfx.rlc.clear_state_obj);
-
-       amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
-
-       return r;
-}
-
-static void gfx_v9_0_csb_vram_unpin(struct amdgpu_device *adev)
-{
-       int r;
-
-       if (!adev->gfx.rlc.clear_state_obj)
-               return;
-
-       r = amdgpu_bo_reserve(adev->gfx.rlc.clear_state_obj, true);
-       if (likely(r == 0)) {
-               amdgpu_bo_unpin(adev->gfx.rlc.clear_state_obj);
-               amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
-       }
-}
-
 static void gfx_v9_0_mec_fini(struct amdgpu_device *adev)
 {
        amdgpu_bo_free_kernel(&adev->gfx.mec.hpd_eop_obj, NULL, NULL);
@@ -2415,6 +2382,7 @@ static void gfx_v9_0_enable_gui_idle_interrupt(struct amdgpu_device *adev,
 
 static void gfx_v9_0_init_csb(struct amdgpu_device *adev)
 {
+       adev->gfx.rlc.funcs->get_csb_buffer(adev, adev->gfx.rlc.cs_ptr);
        /* csib */
        WREG32_RLC(SOC15_REG_OFFSET(GC, 0, mmRLC_CSIB_ADDR_HI),
                        adev->gfx.rlc.clear_state_gpu_addr >> 32);
@@ -3706,10 +3674,6 @@ static int gfx_v9_0_hw_init(void *handle)
 
        gfx_v9_0_constants_init(adev);
 
-       r = gfx_v9_0_csb_vram_pin(adev);
-       if (r)
-               return r;
-
        r = adev->gfx.rlc.funcs->resume(adev);
        if (r)
                return r;
@@ -3791,8 +3755,6 @@ static int gfx_v9_0_hw_fini(void *handle)
        gfx_v9_0_cp_enable(adev, false);
        adev->gfx.rlc.funcs->stop(adev);
 
-       gfx_v9_0_csb_vram_unpin(adev);
-
        return 0;
 }
 
index 5e9ab8e..c0ab71d 100644 (file)
@@ -33,16 +33,31 @@ int gfxhub_v1_1_get_xgmi_info(struct amdgpu_device *adev)
        u32 xgmi_lfb_cntl = RREG32_SOC15(GC, 0, mmMC_VM_XGMI_LFB_CNTL);
        u32 max_region =
                REG_GET_FIELD(xgmi_lfb_cntl, MC_VM_XGMI_LFB_CNTL, PF_MAX_REGION);
+       u32 max_num_physical_nodes   = 0;
+       u32 max_physical_node_id     = 0;
+
+       switch (adev->asic_type) {
+       case CHIP_VEGA20:
+               max_num_physical_nodes   = 4;
+               max_physical_node_id     = 3;
+               break;
+       case CHIP_ARCTURUS:
+               max_num_physical_nodes   = 8;
+               max_physical_node_id     = 7;
+               break;
+       default:
+               return -EINVAL;
+       }
 
        /* PF_MAX_REGION=0 means xgmi is disabled */
        if (max_region) {
                adev->gmc.xgmi.num_physical_nodes = max_region + 1;
-               if (adev->gmc.xgmi.num_physical_nodes > 4)
+               if (adev->gmc.xgmi.num_physical_nodes > max_num_physical_nodes)
                        return -EINVAL;
 
                adev->gmc.xgmi.physical_node_id =
                        REG_GET_FIELD(xgmi_lfb_cntl, MC_VM_XGMI_LFB_CNTL, PF_LFB_REGION);
-               if (adev->gmc.xgmi.physical_node_id > 3)
+               if (adev->gmc.xgmi.physical_node_id > max_physical_node_id)
                        return -EINVAL;
                adev->gmc.xgmi.node_segment_size = REG_GET_FIELD(
                        RREG32_SOC15(GC, 0, mmMC_VM_XGMI_LFB_SIZE),
index 321f8a9..2324695 100644 (file)
@@ -326,7 +326,8 @@ static void gmc_v10_0_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid,
 
        if (!adev->mman.buffer_funcs_enabled ||
            !adev->ib_pool_ready ||
-           adev->in_gpu_reset) {
+           adev->in_gpu_reset ||
+           ring->sched.ready == false) {
                gmc_v10_0_flush_vm_hub(adev, vmid, AMDGPU_GFXHUB_0, 0);
                mutex_unlock(&adev->mman.gtt_window_lock);
                return;
index 78e5cdc..f1b171e 100644 (file)
@@ -783,10 +783,13 @@ static int vi_asic_reset(struct amdgpu_device *adev)
 {
        int r;
 
-       if (vi_asic_reset_method(adev) == AMD_RESET_METHOD_BACO)
+       if (vi_asic_reset_method(adev) == AMD_RESET_METHOD_BACO) {
+               if (!adev->in_suspend)
+                       amdgpu_inc_vram_lost(adev);
                r = smu7_asic_baco_reset(adev);
-       else
+       } else {
                r = vi_asic_pci_config_reset(adev);
+       }
 
        return r;
 }
index a1a35d4..ba0e680 100644 (file)
@@ -5,7 +5,7 @@
 
 config HSA_AMD
        bool "HSA kernel driver for AMD GPU devices"
-       depends on DRM_AMDGPU && (X86_64 || ARM64)
+       depends on DRM_AMDGPU && (X86_64 || ARM64 || PPC64)
        imply AMD_IOMMU_V2 if X86_64
        select MMU_NOTIFIER
        help
index 55a520a..778f186 100644 (file)
@@ -342,7 +342,8 @@ bool dm_pp_get_clock_levels_by_type(
        if (adev->powerplay.pp_funcs && adev->powerplay.pp_funcs->get_clock_by_type) {
                if (adev->powerplay.pp_funcs->get_clock_by_type(pp_handle,
                        dc_to_pp_clock_type(clk_type), &pp_clks)) {
-               /* Error in pplib. Provide default values. */
+                       /* Error in pplib. Provide default values. */
+                       get_default_clock_levels(clk_type, dc_clks);
                        return true;
                }
        } else if (adev->smu.ppt_funcs && adev->smu.ppt_funcs->get_clock_by_type) {
index 921a366..ac8c18f 100644 (file)
@@ -1037,6 +1037,25 @@ void dcn20_pipe_control_lock(
        if (pipe->plane_state != NULL)
                flip_immediate = pipe->plane_state->flip_immediate;
 
+       if (flip_immediate && lock) {
+               const int TIMEOUT_FOR_FLIP_PENDING = 100000;
+               int i;
+
+               for (i = 0; i < TIMEOUT_FOR_FLIP_PENDING; ++i) {
+                       if (!pipe->plane_res.hubp->funcs->hubp_is_flip_pending(pipe->plane_res.hubp))
+                               break;
+                       udelay(1);
+               }
+
+               if (pipe->bottom_pipe != NULL) {
+                       for (i = 0; i < TIMEOUT_FOR_FLIP_PENDING; ++i) {
+                               if (!pipe->bottom_pipe->plane_res.hubp->funcs->hubp_is_flip_pending(pipe->bottom_pipe->plane_res.hubp))
+                                       break;
+                               udelay(1);
+                       }
+               }
+       }
+
        /* In flip immediate and pipe splitting case, we need to use GSL
         * for synchronization. Only do setup on locking and on flip type change.
         */
index bbd1c98..0979333 100644 (file)
@@ -157,6 +157,74 @@ struct _vcs_dpi_ip_params_st dcn2_0_ip = {
        .xfc_fill_constant_bytes = 0,
 };
 
+struct _vcs_dpi_ip_params_st dcn2_0_nv14_ip = {
+       .odm_capable = 1,
+       .gpuvm_enable = 0,
+       .hostvm_enable = 0,
+       .gpuvm_max_page_table_levels = 4,
+       .hostvm_max_page_table_levels = 4,
+       .hostvm_cached_page_table_levels = 0,
+       .num_dsc = 5,
+       .rob_buffer_size_kbytes = 168,
+       .det_buffer_size_kbytes = 164,
+       .dpte_buffer_size_in_pte_reqs_luma = 84,
+       .dpte_buffer_size_in_pte_reqs_chroma = 42,//todo
+       .dpp_output_buffer_pixels = 2560,
+       .opp_output_buffer_lines = 1,
+       .pixel_chunk_size_kbytes = 8,
+       .pte_enable = 1,
+       .max_page_table_levels = 4,
+       .pte_chunk_size_kbytes = 2,
+       .meta_chunk_size_kbytes = 2,
+       .writeback_chunk_size_kbytes = 2,
+       .line_buffer_size_bits = 789504,
+       .is_line_buffer_bpp_fixed = 0,
+       .line_buffer_fixed_bpp = 0,
+       .dcc_supported = true,
+       .max_line_buffer_lines = 12,
+       .writeback_luma_buffer_size_kbytes = 12,
+       .writeback_chroma_buffer_size_kbytes = 8,
+       .writeback_chroma_line_buffer_width_pixels = 4,
+       .writeback_max_hscl_ratio = 1,
+       .writeback_max_vscl_ratio = 1,
+       .writeback_min_hscl_ratio = 1,
+       .writeback_min_vscl_ratio = 1,
+       .writeback_max_hscl_taps = 12,
+       .writeback_max_vscl_taps = 12,
+       .writeback_line_buffer_luma_buffer_size = 0,
+       .writeback_line_buffer_chroma_buffer_size = 14643,
+       .cursor_buffer_size = 8,
+       .cursor_chunk_size = 2,
+       .max_num_otg = 5,
+       .max_num_dpp = 5,
+       .max_num_wb = 1,
+       .max_dchub_pscl_bw_pix_per_clk = 4,
+       .max_pscl_lb_bw_pix_per_clk = 2,
+       .max_lb_vscl_bw_pix_per_clk = 4,
+       .max_vscl_hscl_bw_pix_per_clk = 4,
+       .max_hscl_ratio = 8,
+       .max_vscl_ratio = 8,
+       .hscl_mults = 4,
+       .vscl_mults = 4,
+       .max_hscl_taps = 8,
+       .max_vscl_taps = 8,
+       .dispclk_ramp_margin_percent = 1,
+       .underscan_factor = 1.10,
+       .min_vblank_lines = 32, //
+       .dppclk_delay_subtotal = 77, //
+       .dppclk_delay_scl_lb_only = 16,
+       .dppclk_delay_scl = 50,
+       .dppclk_delay_cnvc_formatter = 8,
+       .dppclk_delay_cnvc_cursor = 6,
+       .dispclk_delay_subtotal = 87, //
+       .dcfclk_cstate_latency = 10, // SRExitTime
+       .max_inter_dcn_tile_repeaters = 8,
+       .xfc_supported = true,
+       .xfc_fill_bw_overhead_percent = 10.0,
+       .xfc_fill_constant_bytes = 0,
+       .ptoi_supported = 0
+};
+
 struct _vcs_dpi_soc_bounding_box_st dcn2_0_soc = {
        /* Defaults that get patched on driver load from firmware. */
        .clock_limits = {
@@ -854,6 +922,8 @@ static const struct resource_caps res_cap_nv14 = {
                .num_pll = 5,
                .num_dwb = 1,
                .num_ddc = 5,
+               .num_vmid = 16,
+               .num_dsc = 5,
 };
 
 static const struct dc_debug_options debug_defaults_drv = {
@@ -3212,6 +3282,10 @@ static struct _vcs_dpi_soc_bounding_box_st *get_asic_rev_soc_bb(
 static struct _vcs_dpi_ip_params_st *get_asic_rev_ip_params(
        uint32_t hw_internal_rev)
 {
+       /* NV14 */
+       if (ASICREV_IS_NAVI14_M(hw_internal_rev))
+               return &dcn2_0_nv14_ip;
+
        /* NV12 and NV10 */
        return &dcn2_0_ip;
 }
index 40b546c..5ff7cce 100644 (file)
@@ -2548,3 +2548,12 @@ uint32_t smu_get_pptable_power_limit(struct smu_context *smu)
 
        return ret;
 }
+
+int smu_send_smc_msg(struct smu_context *smu,
+                    enum smu_message_type msg)
+{
+       int ret;
+
+       ret = smu_send_smc_msg_with_param(smu, msg, 0);
+       return ret;
+}
index 58c7c4a..ce3566c 100644 (file)
@@ -2130,7 +2130,6 @@ static const struct pptable_funcs arcturus_ppt_funcs = {
        .set_tool_table_location = smu_v11_0_set_tool_table_location,
        .notify_memory_pool_location = smu_v11_0_notify_memory_pool_location,
        .system_features_control = smu_v11_0_system_features_control,
-       .send_smc_msg = smu_v11_0_send_msg,
        .send_smc_msg_with_param = smu_v11_0_send_msg_with_param,
        .read_smc_arg = smu_v11_0_read_arg,
        .init_display_count = smu_v11_0_init_display_count,
index 031e0c2..ac97583 100644 (file)
@@ -497,8 +497,8 @@ struct pptable_funcs {
        int (*notify_memory_pool_location)(struct smu_context *smu);
        int (*set_last_dcef_min_deep_sleep_clk)(struct smu_context *smu);
        int (*system_features_control)(struct smu_context *smu, bool en);
-       int (*send_smc_msg)(struct smu_context *smu, uint16_t msg);
-       int (*send_smc_msg_with_param)(struct smu_context *smu, uint16_t msg, uint32_t param);
+       int (*send_smc_msg_with_param)(struct smu_context *smu,
+                                      enum smu_message_type msg, uint32_t param);
        int (*read_smc_arg)(struct smu_context *smu, uint32_t *arg);
        int (*init_display_count)(struct smu_context *smu, uint32_t count);
        int (*set_allowed_mask)(struct smu_context *smu);
index 6061490..7198442 100644 (file)
@@ -177,10 +177,9 @@ int smu_v11_0_notify_memory_pool_location(struct smu_context *smu);
 int smu_v11_0_system_features_control(struct smu_context *smu,
                                             bool en);
 
-int smu_v11_0_send_msg(struct smu_context *smu, uint16_t msg);
-
 int
-smu_v11_0_send_msg_with_param(struct smu_context *smu, uint16_t msg,
+smu_v11_0_send_msg_with_param(struct smu_context *smu,
+                             enum smu_message_type msg,
                              uint32_t param);
 
 int smu_v11_0_read_arg(struct smu_context *smu, uint32_t *arg);
index 9b9f5df..9d81d78 100644 (file)
@@ -44,10 +44,9 @@ int smu_v12_0_read_arg(struct smu_context *smu, uint32_t *arg);
 
 int smu_v12_0_wait_for_response(struct smu_context *smu);
 
-int smu_v12_0_send_msg(struct smu_context *smu, uint16_t msg);
-
 int
-smu_v12_0_send_msg_with_param(struct smu_context *smu, uint16_t msg,
+smu_v12_0_send_msg_with_param(struct smu_context *smu,
+                             enum smu_message_type msg,
                              uint32_t param);
 
 int smu_v12_0_check_fw_status(struct smu_context *smu);
index aaec884..4a14fd1 100644 (file)
@@ -2055,7 +2055,6 @@ static const struct pptable_funcs navi10_ppt_funcs = {
        .set_tool_table_location = smu_v11_0_set_tool_table_location,
        .notify_memory_pool_location = smu_v11_0_notify_memory_pool_location,
        .system_features_control = smu_v11_0_system_features_control,
-       .send_smc_msg = smu_v11_0_send_msg,
        .send_smc_msg_with_param = smu_v11_0_send_msg_with_param,
        .read_smc_arg = smu_v11_0_read_arg,
        .init_display_count = smu_v11_0_init_display_count,
index 04daf7e..977bdd9 100644 (file)
@@ -697,7 +697,6 @@ static const struct pptable_funcs renoir_ppt_funcs = {
        .check_fw_version = smu_v12_0_check_fw_version,
        .powergate_sdma = smu_v12_0_powergate_sdma,
        .powergate_vcn = smu_v12_0_powergate_vcn,
-       .send_smc_msg = smu_v12_0_send_msg,
        .send_smc_msg_with_param = smu_v12_0_send_msg_with_param,
        .read_smc_arg = smu_v12_0_read_arg,
        .set_gfx_cgpg = smu_v12_0_set_gfx_cgpg,
index 8bcda78..8872f8b 100644 (file)
@@ -75,8 +75,8 @@
 #define smu_set_default_od_settings(smu, initialize) \
        ((smu)->ppt_funcs->set_default_od_settings ? (smu)->ppt_funcs->set_default_od_settings((smu), (initialize)) : 0)
 
-#define smu_send_smc_msg(smu, msg) \
-       ((smu)->ppt_funcs->send_smc_msg? (smu)->ppt_funcs->send_smc_msg((smu), (msg)) : 0)
+int smu_send_smc_msg(struct smu_context *smu, enum smu_message_type msg);
+
 #define smu_send_smc_msg_with_param(smu, msg, param) \
        ((smu)->ppt_funcs->send_smc_msg_with_param? (smu)->ppt_funcs->send_smc_msg_with_param((smu), (msg), (param)) : 0)
 #define smu_read_smc_arg(smu, arg) \
index fc9679e..e4268a6 100644 (file)
@@ -90,36 +90,11 @@ static int smu_v11_0_wait_for_response(struct smu_context *smu)
        return RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90) == 0x1 ? 0 : -EIO;
 }
 
-int smu_v11_0_send_msg(struct smu_context *smu, uint16_t msg)
-{
-       struct amdgpu_device *adev = smu->adev;
-       int ret = 0, index = 0;
-
-       index = smu_msg_get_index(smu, msg);
-       if (index < 0)
-               return index;
-
-       smu_v11_0_wait_for_response(smu);
-
-       WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0);
-
-       smu_v11_0_send_msg_without_waiting(smu, (uint16_t)index);
-
-       ret = smu_v11_0_wait_for_response(smu);
-
-       if (ret)
-               pr_err("failed send message: %10s (%d) response %#x\n",
-                      smu_get_message_name(smu, msg), index, ret);
-
-       return ret;
-
-}
-
 int
-smu_v11_0_send_msg_with_param(struct smu_context *smu, uint16_t msg,
+smu_v11_0_send_msg_with_param(struct smu_context *smu,
+                             enum smu_message_type msg,
                              uint32_t param)
 {
-
        struct amdgpu_device *adev = smu->adev;
        int ret = 0, index = 0;
 
index 139dd73..094cfc4 100644 (file)
@@ -77,33 +77,9 @@ int smu_v12_0_wait_for_response(struct smu_context *smu)
        return RREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90) == 0x1 ? 0 : -EIO;
 }
 
-int smu_v12_0_send_msg(struct smu_context *smu, uint16_t msg)
-{
-       struct amdgpu_device *adev = smu->adev;
-       int ret = 0, index = 0;
-
-       index = smu_msg_get_index(smu, msg);
-       if (index < 0)
-               return index;
-
-       smu_v12_0_wait_for_response(smu);
-
-       WREG32_SOC15(MP1, 0, mmMP1_SMN_C2PMSG_90, 0);
-
-       smu_v12_0_send_msg_without_waiting(smu, (uint16_t)index);
-
-       ret = smu_v12_0_wait_for_response(smu);
-
-       if (ret)
-               pr_err("Failed to send message 0x%x, response 0x%x\n", index,
-                      ret);
-
-       return ret;
-
-}
-
 int
-smu_v12_0_send_msg_with_param(struct smu_context *smu, uint16_t msg,
+smu_v12_0_send_msg_with_param(struct smu_context *smu,
+                             enum smu_message_type msg,
                              uint32_t param)
 {
        struct amdgpu_device *adev = smu->adev;
index 0b48928..60b9ff0 100644 (file)
@@ -3231,7 +3231,6 @@ static const struct pptable_funcs vega20_ppt_funcs = {
        .set_tool_table_location = smu_v11_0_set_tool_table_location,
        .notify_memory_pool_location = smu_v11_0_notify_memory_pool_location,
        .system_features_control = smu_v11_0_system_features_control,
-       .send_smc_msg = smu_v11_0_send_msg,
        .send_smc_msg_with_param = smu_v11_0_send_msg_with_param,
        .read_smc_arg = smu_v11_0_read_arg,
        .init_display_count = smu_v11_0_init_display_count,
index ae5809a..273dd80 100644 (file)
@@ -3176,9 +3176,11 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
                        drm_dp_mst_topology_put_port(port);
        }
 
-       for (i = 0; i < mgr->max_payloads; i++) {
-               if (mgr->payloads[i].payload_state != DP_PAYLOAD_DELETE_LOCAL)
+       for (i = 0; i < mgr->max_payloads; /* do nothing */) {
+               if (mgr->payloads[i].payload_state != DP_PAYLOAD_DELETE_LOCAL) {
+                       i++;
                        continue;
+               }
 
                DRM_DEBUG_KMS("removing payload %d\n", i);
                for (j = i; j < mgr->max_payloads - 1; j++) {
index 1799537..c280b6a 100644 (file)
@@ -25,7 +25,7 @@ config DRM_I915_HEARTBEAT_INTERVAL
 
 config DRM_I915_PREEMPT_TIMEOUT
        int "Preempt timeout (ms, jiffy granularity)"
-       default 100 # milliseconds
+       default 640 # milliseconds
        help
          How long to wait (in milliseconds) for a preemption event to occur
          when submitting a new context via execlists. If the current context
index 0caef25..ed8c7ce 100644 (file)
@@ -1273,7 +1273,9 @@ static u8 icl_calc_voltage_level(int cdclk)
 
 static u8 ehl_calc_voltage_level(int cdclk)
 {
-       if (cdclk > 312000)
+       if (cdclk > 326400)
+               return 3;
+       else if (cdclk > 312000)
                return 2;
        else if (cdclk > 180000)
                return 1;
index 0d6e494..c7c2b34 100644 (file)
@@ -593,7 +593,7 @@ struct tgl_dkl_phy_ddi_buf_trans {
        u32 dkl_de_emphasis_control;
 };
 
-static const struct tgl_dkl_phy_ddi_buf_trans tgl_dkl_phy_ddi_translations[] = {
+static const struct tgl_dkl_phy_ddi_buf_trans tgl_dkl_phy_dp_ddi_trans[] = {
                                /* VS   pre-emp Non-trans mV    Pre-emph dB */
        { 0x7, 0x0, 0x00 },     /* 0    0       400mV           0 dB */
        { 0x5, 0x0, 0x03 },     /* 0    1       400mV           3.5 dB */
@@ -607,6 +607,20 @@ static const struct tgl_dkl_phy_ddi_buf_trans tgl_dkl_phy_ddi_translations[] = {
        { 0x0, 0x0, 0x00 },     /* 3    0       1200mV          0 dB HDMI default */
 };
 
+static const struct tgl_dkl_phy_ddi_buf_trans tgl_dkl_phy_hdmi_ddi_trans[] = {
+                               /* HDMI Preset  VS      Pre-emph */
+       { 0x7, 0x0, 0x0 },      /* 1            400mV   0dB */
+       { 0x6, 0x0, 0x0 },      /* 2            500mV   0dB */
+       { 0x4, 0x0, 0x0 },      /* 3            650mV   0dB */
+       { 0x2, 0x0, 0x0 },      /* 4            800mV   0dB */
+       { 0x0, 0x0, 0x0 },      /* 5            1000mV  0dB */
+       { 0x0, 0x0, 0x5 },      /* 6            Full    -1.5 dB */
+       { 0x0, 0x0, 0x6 },      /* 7            Full    -1.8 dB */
+       { 0x0, 0x0, 0x7 },      /* 8            Full    -2 dB */
+       { 0x0, 0x0, 0x8 },      /* 9            Full    -2.5 dB */
+       { 0x0, 0x0, 0xA },      /* 10           Full    -3 dB */
+};
+
 static const struct ddi_buf_trans *
 bdw_get_buf_trans_edp(struct drm_i915_private *dev_priv, int *n_entries)
 {
@@ -898,7 +912,7 @@ static int intel_ddi_hdmi_level(struct drm_i915_private *dev_priv, enum port por
                        icl_get_combo_buf_trans(dev_priv, INTEL_OUTPUT_HDMI,
                                                0, &n_entries);
                else
-                       n_entries = ARRAY_SIZE(tgl_dkl_phy_ddi_translations);
+                       n_entries = ARRAY_SIZE(tgl_dkl_phy_hdmi_ddi_trans);
                default_entry = n_entries - 1;
        } else if (INTEL_GEN(dev_priv) == 11) {
                if (intel_phy_is_combo(dev_priv, phy))
@@ -2371,7 +2385,7 @@ u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder)
                        icl_get_combo_buf_trans(dev_priv, encoder->type,
                                                intel_dp->link_rate, &n_entries);
                else
-                       n_entries = ARRAY_SIZE(tgl_dkl_phy_ddi_translations);
+                       n_entries = ARRAY_SIZE(tgl_dkl_phy_dp_ddi_trans);
        } else if (INTEL_GEN(dev_priv) == 11) {
                if (intel_phy_is_combo(dev_priv, phy))
                        icl_get_combo_buf_trans(dev_priv, encoder->type,
@@ -2823,8 +2837,13 @@ tgl_dkl_phy_ddi_vswing_sequence(struct intel_encoder *encoder, int link_clock,
        const struct tgl_dkl_phy_ddi_buf_trans *ddi_translations;
        u32 n_entries, val, ln, dpcnt_mask, dpcnt_val;
 
-       n_entries = ARRAY_SIZE(tgl_dkl_phy_ddi_translations);
-       ddi_translations = tgl_dkl_phy_ddi_translations;
+       if (encoder->type == INTEL_OUTPUT_HDMI) {
+               n_entries = ARRAY_SIZE(tgl_dkl_phy_hdmi_ddi_trans);
+               ddi_translations = tgl_dkl_phy_hdmi_ddi_trans;
+       } else {
+               n_entries = ARRAY_SIZE(tgl_dkl_phy_dp_ddi_trans);
+               ddi_translations = tgl_dkl_phy_dp_ddi_trans;
+       }
 
        if (level >= n_entries)
                level = n_entries - 1;
index c61ac0c..050655a 100644 (file)
@@ -5476,15 +5476,13 @@ static bool bxt_digital_port_connected(struct intel_encoder *encoder)
        return I915_READ(GEN8_DE_PORT_ISR) & bit;
 }
 
-static bool icl_combo_port_connected(struct drm_i915_private *dev_priv,
-                                    struct intel_digital_port *intel_dig_port)
+static bool intel_combo_phy_connected(struct drm_i915_private *dev_priv,
+                                     enum phy phy)
 {
-       enum port port = intel_dig_port->base.port;
-
-       if (HAS_PCH_MCC(dev_priv) && port == PORT_C)
+       if (HAS_PCH_MCC(dev_priv) && phy == PHY_C)
                return I915_READ(SDEISR) & SDE_TC_HOTPLUG_ICP(PORT_TC1);
 
-       return I915_READ(SDEISR) & SDE_DDI_HOTPLUG_ICP(port);
+       return I915_READ(SDEISR) & SDE_DDI_HOTPLUG_ICP(phy);
 }
 
 static bool icl_digital_port_connected(struct intel_encoder *encoder)
@@ -5494,7 +5492,7 @@ static bool icl_digital_port_connected(struct intel_encoder *encoder)
        enum phy phy = intel_port_to_phy(dev_priv, encoder->port);
 
        if (intel_phy_is_combo(dev_priv, phy))
-               return icl_combo_port_connected(dev_priv, dig_port);
+               return intel_combo_phy_connected(dev_priv, phy);
        else if (intel_phy_is_tc(dev_priv, phy))
                return intel_tc_port_connected(dig_port);
        else
index e553ca8..337ba17 100644 (file)
@@ -368,7 +368,7 @@ static struct intel_engine_cs *active_engine(struct intel_context *ce)
        if (!ce->timeline)
                return NULL;
 
-       rcu_read_lock();
+       mutex_lock(&ce->timeline->mutex);
        list_for_each_entry_reverse(rq, &ce->timeline->requests, link) {
                if (i915_request_completed(rq))
                        break;
@@ -378,7 +378,7 @@ static struct intel_engine_cs *active_engine(struct intel_context *ce)
                if (engine)
                        break;
        }
-       rcu_read_unlock();
+       mutex_unlock(&ce->timeline->mutex);
 
        return engine;
 }
index ee9d2bc..ef7bc41 100644 (file)
@@ -310,10 +310,23 @@ int intel_context_prepare_remote_request(struct intel_context *ce,
        GEM_BUG_ON(rq->hw_context == ce);
 
        if (rcu_access_pointer(rq->timeline) != tl) { /* timeline sharing! */
-               err = mutex_lock_interruptible_nested(&tl->mutex,
-                                                     SINGLE_DEPTH_NESTING);
-               if (err)
-                       return err;
+               /*
+                * Ideally, we just want to insert our foreign fence as
+                * a barrier into the remove context, such that this operation
+                * occurs after all current operations in that context, and
+                * all future operations must occur after this.
+                *
+                * Currently, the timeline->last_request tracking is guarded
+                * by its mutex and so we must obtain that to atomically
+                * insert our barrier. However, since we already hold our
+                * timeline->mutex, we must be careful against potential
+                * inversion if we are the kernel_context as the remote context
+                * will itself poke at the kernel_context when it needs to
+                * unpin. Ergo, if already locked, we drop both locks and
+                * try again (through the magic of userspace repeating EAGAIN).
+                */
+               if (!mutex_trylock(&tl->mutex))
+                       return -EAGAIN;
 
                /* Queue this switch after current activity by this context. */
                err = i915_active_fence_set(&tl->last_request, rq);
index bc3b72b..01765a7 100644 (file)
@@ -100,9 +100,7 @@ execlists_num_ports(const struct intel_engine_execlists * const execlists)
 static inline struct i915_request *
 execlists_active(const struct intel_engine_execlists *execlists)
 {
-       GEM_BUG_ON(execlists->active - execlists->inflight >
-                  execlists_num_ports(execlists));
-       return READ_ONCE(*execlists->active);
+       return *READ_ONCE(execlists->active);
 }
 
 static inline void
index 5ca3ec9..813bd3a 100644 (file)
 
 #include "i915_drv.h"
 
-#include "gt/intel_gt.h"
-
+#include "intel_context.h"
 #include "intel_engine.h"
 #include "intel_engine_pm.h"
 #include "intel_engine_pool.h"
 #include "intel_engine_user.h"
-#include "intel_context.h"
+#include "intel_gt.h"
+#include "intel_gt_requests.h"
 #include "intel_lrc.h"
 #include "intel_reset.h"
 #include "intel_ring.h"
@@ -616,6 +616,7 @@ static int intel_engine_setup_common(struct intel_engine_cs *engine)
        intel_engine_init_execlists(engine);
        intel_engine_init_cmd_parser(engine);
        intel_engine_init__pm(engine);
+       intel_engine_init_retire(engine);
 
        intel_engine_pool_init(&engine->pool);
 
@@ -838,6 +839,7 @@ void intel_engine_cleanup_common(struct intel_engine_cs *engine)
 
        cleanup_status_page(engine);
 
+       intel_engine_fini_retire(engine);
        intel_engine_pool_fini(&engine->pool);
        intel_engine_fini_breadcrumbs(engine);
        intel_engine_cleanup_cmd_parser(engine);
index 874d826..c1dd0cd 100644 (file)
@@ -73,8 +73,42 @@ static inline void __timeline_mark_unlock(struct intel_context *ce,
 
 #endif /* !IS_ENABLED(CONFIG_LOCKDEP) */
 
+static void
+__queue_and_release_pm(struct i915_request *rq,
+                      struct intel_timeline *tl,
+                      struct intel_engine_cs *engine)
+{
+       struct intel_gt_timelines *timelines = &engine->gt->timelines;
+
+       GEM_TRACE("%s\n", engine->name);
+
+       /*
+        * We have to serialise all potential retirement paths with our
+        * submission, as we don't want to underflow either the
+        * engine->wakeref.counter or our timeline->active_count.
+        *
+        * Equally, we cannot allow a new submission to start until
+        * after we finish queueing, nor could we allow that submitter
+        * to retire us before we are ready!
+        */
+       spin_lock(&timelines->lock);
+
+       /* Let intel_gt_retire_requests() retire us (acquired under lock) */
+       if (!atomic_fetch_inc(&tl->active_count))
+               list_add_tail(&tl->link, &timelines->active_list);
+
+       /* Hand the request over to HW and so engine_retire() */
+       __i915_request_queue(rq, NULL);
+
+       /* Let new submissions commence (and maybe retire this timeline) */
+       __intel_wakeref_defer_park(&engine->wakeref);
+
+       spin_unlock(&timelines->lock);
+}
+
 static bool switch_to_kernel_context(struct intel_engine_cs *engine)
 {
+       struct intel_context *ce = engine->kernel_context;
        struct i915_request *rq;
        unsigned long flags;
        bool result = true;
@@ -98,16 +132,31 @@ static bool switch_to_kernel_context(struct intel_engine_cs *engine)
         * This should hold true as we can only park the engine after
         * retiring the last request, thus all rings should be empty and
         * all timelines idle.
+        *
+        * For unlocking, there are 2 other parties and the GPU who have a
+        * stake here.
+        *
+        * A new gpu user will be waiting on the engine-pm to start their
+        * engine_unpark. New waiters are predicated on engine->wakeref.count
+        * and so intel_wakeref_defer_park() acts like a mutex_unlock of the
+        * engine->wakeref.
+        *
+        * The other party is intel_gt_retire_requests(), which is walking the
+        * list of active timelines looking for completions. Meanwhile as soon
+        * as we call __i915_request_queue(), the GPU may complete our request.
+        * Ergo, if we put ourselves on the timelines.active_list
+        * (se intel_timeline_enter()) before we increment the
+        * engine->wakeref.count, we may see the request completion and retire
+        * it causing an undeflow of the engine->wakeref.
         */
-       flags = __timeline_mark_lock(engine->kernel_context);
+       flags = __timeline_mark_lock(ce);
+       GEM_BUG_ON(atomic_read(&ce->timeline->active_count) < 0);
 
-       rq = __i915_request_create(engine->kernel_context, GFP_NOWAIT);
+       rq = __i915_request_create(ce, GFP_NOWAIT);
        if (IS_ERR(rq))
                /* Context switch failed, hope for the best! Maybe reset? */
                goto out_unlock;
 
-       intel_timeline_enter(i915_request_timeline(rq));
-
        /* Check again on the next retirement. */
        engine->wakeref_serial = engine->serial + 1;
        i915_request_add_active_barriers(rq);
@@ -116,13 +165,12 @@ static bool switch_to_kernel_context(struct intel_engine_cs *engine)
        rq->sched.attr.priority = I915_PRIORITY_BARRIER;
        __i915_request_commit(rq);
 
-       /* Release our exclusive hold on the engine */
-       __intel_wakeref_defer_park(&engine->wakeref);
-       __i915_request_queue(rq, NULL);
+       /* Expose ourselves to the world */
+       __queue_and_release_pm(rq, ce->timeline, engine);
 
        result = false;
 out_unlock:
-       __timeline_mark_unlock(engine->kernel_context, flags);
+       __timeline_mark_unlock(ce, flags);
        return result;
 }
 
@@ -177,7 +225,8 @@ static int __engine_park(struct intel_wakeref *wf)
 
        engine->execlists.no_priolist = false;
 
-       intel_gt_pm_put(engine->gt);
+       /* While gt calls i915_vma_parked(), we have to break the lock cycle */
+       intel_gt_pm_put_async(engine->gt);
        return 0;
 }
 
index 739c50f..24e2034 100644 (file)
@@ -31,6 +31,16 @@ static inline void intel_engine_pm_put(struct intel_engine_cs *engine)
        intel_wakeref_put(&engine->wakeref);
 }
 
+static inline void intel_engine_pm_put_async(struct intel_engine_cs *engine)
+{
+       intel_wakeref_put_async(&engine->wakeref);
+}
+
+static inline void intel_engine_pm_flush(struct intel_engine_cs *engine)
+{
+       intel_wakeref_unlock_wait(&engine->wakeref);
+}
+
 void intel_engine_init__pm(struct intel_engine_cs *engine);
 
 #endif /* INTEL_ENGINE_PM_H */
index 758f0e8..17f1f14 100644 (file)
@@ -451,6 +451,14 @@ struct intel_engine_cs {
 
        struct intel_engine_execlists execlists;
 
+       /*
+        * Keep track of completed timelines on this engine for early
+        * retirement with the goal of quickly enabling powersaving as
+        * soon as the engine is idle.
+        */
+       struct intel_timeline *retire;
+       struct work_struct retire_work;
+
        /* status_notifier: list of callbacks for context-switch changes */
        struct atomic_notifier_head context_status_notifier;
 
index 6187cdd..a459a42 100644 (file)
@@ -105,7 +105,6 @@ static int __gt_park(struct intel_wakeref *wf)
 static const struct intel_wakeref_ops wf_ops = {
        .get = __gt_unpark,
        .put = __gt_park,
-       .flags = INTEL_WAKEREF_PUT_ASYNC,
 };
 
 void intel_gt_pm_init_early(struct intel_gt *gt)
@@ -272,7 +271,7 @@ void intel_gt_suspend_prepare(struct intel_gt *gt)
 
 static suspend_state_t pm_suspend_target(void)
 {
-#if IS_ENABLED(CONFIG_PM_SLEEP)
+#if IS_ENABLED(CONFIG_SUSPEND) && IS_ENABLED(CONFIG_PM_SLEEP)
        return pm_suspend_target_state;
 #else
        return PM_SUSPEND_TO_IDLE;
index b3e1739..990efc2 100644 (file)
@@ -32,6 +32,11 @@ static inline void intel_gt_pm_put(struct intel_gt *gt)
        intel_wakeref_put(&gt->wakeref);
 }
 
+static inline void intel_gt_pm_put_async(struct intel_gt *gt)
+{
+       intel_wakeref_put_async(&gt->wakeref);
+}
+
 static inline int intel_gt_pm_wait_for_idle(struct intel_gt *gt)
 {
        return intel_wakeref_wait_for_idle(&gt->wakeref);
index 353809a..3dc13ec 100644 (file)
@@ -4,6 +4,8 @@
  * Copyright Â© 2019 Intel Corporation
  */
 
+#include <linux/workqueue.h>
+
 #include "i915_drv.h" /* for_each_engine() */
 #include "i915_request.h"
 #include "intel_gt.h"
@@ -29,6 +31,79 @@ static void flush_submission(struct intel_gt *gt)
                intel_engine_flush_submission(engine);
 }
 
+static void engine_retire(struct work_struct *work)
+{
+       struct intel_engine_cs *engine =
+               container_of(work, typeof(*engine), retire_work);
+       struct intel_timeline *tl = xchg(&engine->retire, NULL);
+
+       do {
+               struct intel_timeline *next = xchg(&tl->retire, NULL);
+
+               /*
+                * Our goal here is to retire _idle_ timelines as soon as
+                * possible (as they are idle, we do not expect userspace
+                * to be cleaning up anytime soon).
+                *
+                * If the timeline is currently locked, either it is being
+                * retired elsewhere or about to be!
+                */
+               if (mutex_trylock(&tl->mutex)) {
+                       retire_requests(tl);
+                       mutex_unlock(&tl->mutex);
+               }
+               intel_timeline_put(tl);
+
+               GEM_BUG_ON(!next);
+               tl = ptr_mask_bits(next, 1);
+       } while (tl);
+}
+
+static bool add_retire(struct intel_engine_cs *engine,
+                      struct intel_timeline *tl)
+{
+       struct intel_timeline *first;
+
+       /*
+        * We open-code a llist here to include the additional tag [BIT(0)]
+        * so that we know when the timeline is already on a
+        * retirement queue: either this engine or another.
+        *
+        * However, we rely on that a timeline can only be active on a single
+        * engine at any one time and that add_retire() is called before the
+        * engine releases the timeline and transferred to another to retire.
+        */
+
+       if (READ_ONCE(tl->retire)) /* already queued */
+               return false;
+
+       intel_timeline_get(tl);
+       first = READ_ONCE(engine->retire);
+       do
+               tl->retire = ptr_pack_bits(first, 1, 1);
+       while (!try_cmpxchg(&engine->retire, &first, tl));
+
+       return !first;
+}
+
+void intel_engine_add_retire(struct intel_engine_cs *engine,
+                            struct intel_timeline *tl)
+{
+       if (add_retire(engine, tl))
+               schedule_work(&engine->retire_work);
+}
+
+void intel_engine_init_retire(struct intel_engine_cs *engine)
+{
+       INIT_WORK(&engine->retire_work, engine_retire);
+}
+
+void intel_engine_fini_retire(struct intel_engine_cs *engine)
+{
+       flush_work(&engine->retire_work);
+       GEM_BUG_ON(engine->retire);
+}
+
 long intel_gt_retire_requests_timeout(struct intel_gt *gt, long timeout)
 {
        struct intel_gt_timelines *timelines = &gt->timelines;
@@ -52,8 +127,8 @@ long intel_gt_retire_requests_timeout(struct intel_gt *gt, long timeout)
                }
 
                intel_timeline_get(tl);
-               GEM_BUG_ON(!tl->active_count);
-               tl->active_count++; /* pin the list element */
+               GEM_BUG_ON(!atomic_read(&tl->active_count));
+               atomic_inc(&tl->active_count); /* pin the list element */
                spin_unlock_irqrestore(&timelines->lock, flags);
 
                if (timeout > 0) {
@@ -74,7 +149,7 @@ long intel_gt_retire_requests_timeout(struct intel_gt *gt, long timeout)
 
                /* Resume iteration after dropping lock */
                list_safe_reset_next(tl, tn, link);
-               if (!--tl->active_count)
+               if (atomic_dec_and_test(&tl->active_count))
                        list_del(&tl->link);
                else
                        active_count += !!rcu_access_pointer(tl->last_request.fence);
@@ -83,7 +158,7 @@ long intel_gt_retire_requests_timeout(struct intel_gt *gt, long timeout)
 
                /* Defer the final release to after the spinlock */
                if (refcount_dec_and_test(&tl->kref.refcount)) {
-                       GEM_BUG_ON(tl->active_count);
+                       GEM_BUG_ON(atomic_read(&tl->active_count));
                        list_add(&tl->link, &free);
                }
        }
index bd31cbc..d626fb1 100644 (file)
@@ -7,7 +7,9 @@
 #ifndef INTEL_GT_REQUESTS_H
 #define INTEL_GT_REQUESTS_H
 
+struct intel_engine_cs;
 struct intel_gt;
+struct intel_timeline;
 
 long intel_gt_retire_requests_timeout(struct intel_gt *gt, long timeout);
 static inline void intel_gt_retire_requests(struct intel_gt *gt)
@@ -15,6 +17,11 @@ static inline void intel_gt_retire_requests(struct intel_gt *gt)
        intel_gt_retire_requests_timeout(gt, 0);
 }
 
+void intel_engine_init_retire(struct intel_engine_cs *engine);
+void intel_engine_add_retire(struct intel_engine_cs *engine,
+                            struct intel_timeline *tl);
+void intel_engine_fini_retire(struct intel_engine_cs *engine);
+
 int intel_gt_wait_for_idle(struct intel_gt *gt, long timeout);
 
 void intel_gt_init_requests(struct intel_gt *gt);
index 0ac3b26..9fdefbd 100644 (file)
 #include "intel_engine_pm.h"
 #include "intel_gt.h"
 #include "intel_gt_pm.h"
+#include "intel_gt_requests.h"
 #include "intel_lrc_reg.h"
 #include "intel_mocs.h"
 #include "intel_reset.h"
@@ -1115,9 +1116,17 @@ __execlists_schedule_out(struct i915_request *rq,
         * refrain from doing non-trivial work here.
         */
 
+       /*
+        * If we have just completed this context, the engine may now be
+        * idle and we want to re-enter powersaving.
+        */
+       if (list_is_last(&rq->link, &ce->timeline->requests) &&
+           i915_request_completed(rq))
+               intel_engine_add_retire(engine, ce->timeline);
+
        intel_engine_context_out(engine);
        execlists_context_status_change(rq, INTEL_CONTEXT_SCHEDULE_OUT);
-       intel_gt_pm_put(engine->gt);
+       intel_gt_pm_put_async(engine->gt);
 
        /*
         * If this is part of a virtual engine, its next request may
@@ -1937,16 +1946,17 @@ skip_submit:
 static void
 cancel_port_requests(struct intel_engine_execlists * const execlists)
 {
-       struct i915_request * const *port, *rq;
+       struct i915_request * const *port;
 
-       for (port = execlists->pending; (rq = *port); port++)
-               execlists_schedule_out(rq);
+       for (port = execlists->pending; *port; port++)
+               execlists_schedule_out(*port);
        memset(execlists->pending, 0, sizeof(execlists->pending));
 
-       for (port = execlists->active; (rq = *port); port++)
-               execlists_schedule_out(rq);
-       execlists->active =
-               memset(execlists->inflight, 0, sizeof(execlists->inflight));
+       /* Mark the end of active before we overwrite *active */
+       for (port = xchg(&execlists->active, execlists->pending); *port; port++)
+               execlists_schedule_out(*port);
+       WRITE_ONCE(execlists->active,
+                  memset(execlists->inflight, 0, sizeof(execlists->inflight)));
 }
 
 static inline void
@@ -2099,23 +2109,27 @@ static void process_csb(struct intel_engine_cs *engine)
                else
                        promote = gen8_csb_parse(execlists, buf + 2 * head);
                if (promote) {
+                       struct i915_request * const *old = execlists->active;
+
+                       /* Point active to the new ELSP; prevent overwriting */
+                       WRITE_ONCE(execlists->active, execlists->pending);
+                       set_timeslice(engine);
+
                        if (!inject_preempt_hang(execlists))
                                ring_set_paused(engine, 0);
 
                        /* cancel old inflight, prepare for switch */
-                       trace_ports(execlists, "preempted", execlists->active);
-                       while (*execlists->active)
-                               execlists_schedule_out(*execlists->active++);
+                       trace_ports(execlists, "preempted", old);
+                       while (*old)
+                               execlists_schedule_out(*old++);
 
                        /* switch pending to inflight */
                        GEM_BUG_ON(!assert_pending_valid(execlists, "promote"));
-                       execlists->active =
-                               memcpy(execlists->inflight,
-                                      execlists->pending,
-                                      execlists_num_ports(execlists) *
-                                      sizeof(*execlists->pending));
-
-                       set_timeslice(engine);
+                       WRITE_ONCE(execlists->active,
+                                  memcpy(execlists->inflight,
+                                         execlists->pending,
+                                         execlists_num_ports(execlists) *
+                                         sizeof(*execlists->pending)));
 
                        WRITE_ONCE(execlists->pending[0], NULL);
                } else {
index f03e000..c97423a 100644 (file)
@@ -1114,7 +1114,7 @@ int intel_engine_reset(struct intel_engine_cs *engine, const char *msg)
 out:
        intel_engine_cancel_stop_cs(engine);
        reset_finish_engine(engine);
-       intel_engine_pm_put(engine);
+       intel_engine_pm_put_async(engine);
        return ret;
 }
 
index ece2050..374b28f 100644 (file)
@@ -57,9 +57,10 @@ int intel_ring_pin(struct intel_ring *ring)
 
        i915_vma_make_unshrinkable(vma);
 
-       GEM_BUG_ON(ring->vaddr);
-       ring->vaddr = addr;
+       /* Discard any unused bytes beyond that submitted to hw. */
+       intel_ring_reset(ring, ring->emit);
 
+       ring->vaddr = addr;
        return 0;
 
 err_ring:
@@ -85,20 +86,14 @@ void intel_ring_unpin(struct intel_ring *ring)
        if (!atomic_dec_and_test(&ring->pin_count))
                return;
 
-       /* Discard any unused bytes beyond that submitted to hw. */
-       intel_ring_reset(ring, ring->emit);
-
        i915_vma_unset_ggtt_write(vma);
        if (i915_vma_is_map_and_fenceable(vma))
                i915_vma_unpin_iomap(vma);
        else
                i915_gem_object_unpin_map(vma->obj);
 
-       GEM_BUG_ON(!ring->vaddr);
-       ring->vaddr = NULL;
-
-       i915_vma_unpin(vma);
        i915_vma_make_purgeable(vma);
+       i915_vma_unpin(vma);
 }
 
 static struct i915_vma *create_ring_vma(struct i915_ggtt *ggtt, int size)
index 14ad10a..649798c 100644 (file)
@@ -282,6 +282,7 @@ void intel_timeline_fini(struct intel_timeline *timeline)
 {
        GEM_BUG_ON(atomic_read(&timeline->pin_count));
        GEM_BUG_ON(!list_empty(&timeline->requests));
+       GEM_BUG_ON(timeline->retire);
 
        if (timeline->hwsp_cacheline)
                cacheline_free(timeline->hwsp_cacheline);
@@ -339,15 +340,33 @@ void intel_timeline_enter(struct intel_timeline *tl)
        struct intel_gt_timelines *timelines = &tl->gt->timelines;
        unsigned long flags;
 
+       /*
+        * Pretend we are serialised by the timeline->mutex.
+        *
+        * While generally true, there are a few exceptions to the rule
+        * for the engine->kernel_context being used to manage power
+        * transitions. As the engine_park may be called from under any
+        * timeline, it uses the power mutex as a global serialisation
+        * lock to prevent any other request entering its timeline.
+        *
+        * The rule is generally tl->mutex, otherwise engine->wakeref.mutex.
+        *
+        * However, intel_gt_retire_request() does not know which engine
+        * it is retiring along and so cannot partake in the engine-pm
+        * barrier, and there we use the tl->active_count as a means to
+        * pin the timeline in the active_list while the locks are dropped.
+        * Ergo, as that is outside of the engine-pm barrier, we need to
+        * use atomic to manipulate tl->active_count.
+        */
        lockdep_assert_held(&tl->mutex);
-
        GEM_BUG_ON(!atomic_read(&tl->pin_count));
-       if (tl->active_count++)
+
+       if (atomic_add_unless(&tl->active_count, 1, 0))
                return;
-       GEM_BUG_ON(!tl->active_count); /* overflow? */
 
        spin_lock_irqsave(&timelines->lock, flags);
-       list_add(&tl->link, &timelines->active_list);
+       if (!atomic_fetch_inc(&tl->active_count))
+               list_add_tail(&tl->link, &timelines->active_list);
        spin_unlock_irqrestore(&timelines->lock, flags);
 }
 
@@ -356,14 +375,16 @@ void intel_timeline_exit(struct intel_timeline *tl)
        struct intel_gt_timelines *timelines = &tl->gt->timelines;
        unsigned long flags;
 
+       /* See intel_timeline_enter() */
        lockdep_assert_held(&tl->mutex);
 
-       GEM_BUG_ON(!tl->active_count);
-       if (--tl->active_count)
+       GEM_BUG_ON(!atomic_read(&tl->active_count));
+       if (atomic_add_unless(&tl->active_count, -1, 1))
                return;
 
        spin_lock_irqsave(&timelines->lock, flags);
-       list_del(&tl->link);
+       if (atomic_dec_and_test(&tl->active_count))
+               list_del(&tl->link);
        spin_unlock_irqrestore(&timelines->lock, flags);
 
        /*
index 98d9ee1..aaf15cb 100644 (file)
@@ -42,7 +42,7 @@ struct intel_timeline {
         * from the intel_context caller plus internal atomicity.
         */
        atomic_t pin_count;
-       unsigned int active_count;
+       atomic_t active_count;
 
        const u32 *hwsp_seqno;
        struct i915_vma *hwsp_ggtt;
@@ -66,6 +66,9 @@ struct intel_timeline {
         */
        struct i915_active_fence last_request;
 
+       /** A chain of completed timelines ready for early retirement. */
+       struct intel_timeline *retire;
+
        /**
         * We track the most recent seqno that we wait on in every context so
         * that we only have to emit a new await and dependency on a more
index 20b9c83..cbf6b07 100644 (file)
@@ -51,11 +51,12 @@ static int live_engine_pm(void *arg)
                                pr_err("intel_engine_pm_get_if_awake(%s) failed under %s\n",
                                       engine->name, p->name);
                        else
-                               intel_engine_pm_put(engine);
-                       intel_engine_pm_put(engine);
+                               intel_engine_pm_put_async(engine);
+                       intel_engine_pm_put_async(engine);
                        p->critical_section_end();
 
-                       /* engine wakeref is sync (instant) */
+                       intel_engine_pm_flush(engine);
+
                        if (intel_engine_pm_is_awake(engine)) {
                                pr_err("%s is still awake after flushing pm\n",
                                       engine->name);
index 6a3ac8c..21a176c 100644 (file)
@@ -1599,9 +1599,9 @@ static int cmd_handler_mi_op_2f(struct parser_exec_state *s)
        if (!(cmd_val(s, 0) & (1 << 22)))
                return ret;
 
-       /* check if QWORD */
-       if (DWORD_FIELD(0, 20, 19) == 1)
-               valid_len += 8;
+       /* check inline data */
+       if (cmd_val(s, 0) & BIT(18))
+               valid_len = CMD_LEN(9);
        ret = gvt_check_valid_cmd_length(cmd_length(s),
                        valid_len);
        if (ret)
index bd12af3..bb9fe6b 100644 (file)
@@ -460,6 +460,7 @@ static int pipeconf_mmio_write(struct intel_vgpu *vgpu, unsigned int offset,
 static i915_reg_t force_nonpriv_white_list[] = {
        GEN9_CS_DEBUG_MODE1, //_MMIO(0x20ec)
        GEN9_CTX_PREEMPT_REG,//_MMIO(0x2248)
+       PS_INVOCATION_COUNT,//_MMIO(0x2348)
        GEN8_CS_CHICKEN1,//_MMIO(0x2580)
        _MMIO(0x2690),
        _MMIO(0x2694),
@@ -508,7 +509,7 @@ static inline bool in_whitelist(unsigned int reg)
 static int force_nonpriv_write(struct intel_vgpu *vgpu,
        unsigned int offset, void *p_data, unsigned int bytes)
 {
-       u32 reg_nonpriv = *(u32 *)p_data;
+       u32 reg_nonpriv = (*(u32 *)p_data) & REG_GENMASK(25, 2);
        int ring_id = intel_gvt_render_mmio_to_ring_id(vgpu->gvt, offset);
        u32 ring_base;
        struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
@@ -528,7 +529,7 @@ static int force_nonpriv_write(struct intel_vgpu *vgpu,
                        bytes);
        } else
                gvt_err("vgpu(%d) Invalid FORCE_NONPRIV write %x at offset %x\n",
-                       vgpu->id, reg_nonpriv, offset);
+                       vgpu->id, *(u32 *)p_data, offset);
 
        return 0;
 }
index 3c424cb..a19e7d8 100644 (file)
@@ -672,12 +672,13 @@ void i915_active_acquire_barrier(struct i915_active *ref)
         * populated by i915_request_add_active_barriers() to point to the
         * request that will eventually release them.
         */
-       spin_lock_irqsave_nested(&ref->tree_lock, flags, SINGLE_DEPTH_NESTING);
        llist_for_each_safe(pos, next, take_preallocated_barriers(ref)) {
                struct active_node *node = barrier_from_ll(pos);
                struct intel_engine_cs *engine = barrier_to_engine(node);
                struct rb_node **p, *parent;
 
+               spin_lock_irqsave_nested(&ref->tree_lock, flags,
+                                        SINGLE_DEPTH_NESTING);
                parent = NULL;
                p = &ref->tree.rb_node;
                while (*p) {
@@ -693,12 +694,12 @@ void i915_active_acquire_barrier(struct i915_active *ref)
                }
                rb_link_node(&node->node, parent, p);
                rb_insert_color(&node->node, &ref->tree);
+               spin_unlock_irqrestore(&ref->tree_lock, flags);
 
                GEM_BUG_ON(!intel_engine_pm_is_awake(engine));
                llist_add(barrier_to_ll(node), &engine->barrier_tasks);
                intel_engine_pm_put(engine);
        }
-       spin_unlock_irqrestore(&ref->tree_lock, flags);
 }
 
 void i915_request_add_active_barriers(struct i915_request *rq)
index 0d40dcc..2814218 100644 (file)
@@ -190,7 +190,7 @@ static u64 get_rc6(struct intel_gt *gt)
        val = 0;
        if (intel_gt_pm_get_if_awake(gt)) {
                val = __get_rc6(gt);
-               intel_gt_pm_put(gt);
+               intel_gt_pm_put_async(gt);
        }
 
        spin_lock_irqsave(&pmu->lock, flags);
@@ -343,7 +343,7 @@ engines_sample(struct intel_gt *gt, unsigned int period_ns)
 
 skip:
                spin_unlock_irqrestore(&engine->uncore->lock, flags);
-               intel_engine_pm_put(engine);
+               intel_engine_pm_put_async(engine);
        }
 }
 
@@ -368,7 +368,7 @@ frequency_sample(struct intel_gt *gt, unsigned int period_ns)
                if (intel_gt_pm_get_if_awake(gt)) {
                        val = intel_uncore_read_notrace(uncore, GEN6_RPSTAT1);
                        val = intel_get_cagf(rps, val);
-                       intel_gt_pm_put(gt);
+                       intel_gt_pm_put_async(gt);
                }
 
                add_sample_mult(&pmu->sample[__I915_SAMPLE_FREQ_ACT],
index c27cfef..ef25ce6 100644 (file)
@@ -103,15 +103,18 @@ query_engine_info(struct drm_i915_private *i915,
        struct drm_i915_engine_info __user *info_ptr;
        struct drm_i915_query_engine_info query;
        struct drm_i915_engine_info info = { };
+       unsigned int num_uabi_engines = 0;
        struct intel_engine_cs *engine;
        int len, ret;
 
        if (query_item->flags)
                return -EINVAL;
 
+       for_each_uabi_engine(engine, i915)
+               num_uabi_engines++;
+
        len = sizeof(struct drm_i915_query_engine_info) +
-             RUNTIME_INFO(i915)->num_engines *
-             sizeof(struct drm_i915_engine_info);
+             num_uabi_engines * sizeof(struct drm_i915_engine_info);
 
        ret = copy_query_item(&query, sizeof(query), len, query_item);
        if (ret != 0)
index 868cc78..59aa1b6 100644 (file)
@@ -54,7 +54,8 @@ int __intel_wakeref_get_first(struct intel_wakeref *wf)
 
 static void ____intel_wakeref_put_last(struct intel_wakeref *wf)
 {
-       if (!atomic_dec_and_test(&wf->count))
+       INTEL_WAKEREF_BUG_ON(atomic_read(&wf->count) <= 0);
+       if (unlikely(!atomic_dec_and_test(&wf->count)))
                goto unlock;
 
        /* ops->put() must reschedule its own release on error/deferral */
@@ -67,13 +68,12 @@ unlock:
        mutex_unlock(&wf->mutex);
 }
 
-void __intel_wakeref_put_last(struct intel_wakeref *wf)
+void __intel_wakeref_put_last(struct intel_wakeref *wf, unsigned long flags)
 {
        INTEL_WAKEREF_BUG_ON(work_pending(&wf->work));
 
        /* Assume we are not in process context and so cannot sleep. */
-       if (wf->ops->flags & INTEL_WAKEREF_PUT_ASYNC ||
-           !mutex_trylock(&wf->mutex)) {
+       if (flags & INTEL_WAKEREF_PUT_ASYNC || !mutex_trylock(&wf->mutex)) {
                schedule_work(&wf->work);
                return;
        }
@@ -109,8 +109,17 @@ void __intel_wakeref_init(struct intel_wakeref *wf,
 
 int intel_wakeref_wait_for_idle(struct intel_wakeref *wf)
 {
-       return wait_var_event_killable(&wf->wakeref,
-                                      !intel_wakeref_is_active(wf));
+       int err;
+
+       might_sleep();
+
+       err = wait_var_event_killable(&wf->wakeref,
+                                     !intel_wakeref_is_active(wf));
+       if (err)
+               return err;
+
+       intel_wakeref_unlock_wait(wf);
+       return 0;
 }
 
 static void wakeref_auto_timeout(struct timer_list *t)
index 5f0c972..da6e8fd 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/atomic.h>
 #include <linux/bits.h>
+#include <linux/lockdep.h>
 #include <linux/mutex.h>
 #include <linux/refcount.h>
 #include <linux/stackdepot.h>
@@ -29,9 +30,6 @@ typedef depot_stack_handle_t intel_wakeref_t;
 struct intel_wakeref_ops {
        int (*get)(struct intel_wakeref *wf);
        int (*put)(struct intel_wakeref *wf);
-
-       unsigned long flags;
-#define INTEL_WAKEREF_PUT_ASYNC BIT(0)
 };
 
 struct intel_wakeref {
@@ -57,7 +55,7 @@ void __intel_wakeref_init(struct intel_wakeref *wf,
 } while (0)
 
 int __intel_wakeref_get_first(struct intel_wakeref *wf);
-void __intel_wakeref_put_last(struct intel_wakeref *wf);
+void __intel_wakeref_put_last(struct intel_wakeref *wf, unsigned long flags);
 
 /**
  * intel_wakeref_get: Acquire the wakeref
@@ -100,10 +98,9 @@ intel_wakeref_get_if_active(struct intel_wakeref *wf)
 }
 
 /**
- * intel_wakeref_put: Release the wakeref
- * @i915: the drm_i915_private device
+ * intel_wakeref_put_flags: Release the wakeref
  * @wf: the wakeref
- * @fn: callback for releasing the wakeref, called only on final release.
+ * @flags: control flags
  *
  * Release our hold on the wakeref. When there are no more users,
  * the runtime pm wakeref will be released after the @fn callback is called
@@ -116,11 +113,25 @@ intel_wakeref_get_if_active(struct intel_wakeref *wf)
  * code otherwise.
  */
 static inline void
-intel_wakeref_put(struct intel_wakeref *wf)
+__intel_wakeref_put(struct intel_wakeref *wf, unsigned long flags)
+#define INTEL_WAKEREF_PUT_ASYNC BIT(0)
 {
        INTEL_WAKEREF_BUG_ON(atomic_read(&wf->count) <= 0);
        if (unlikely(!atomic_add_unless(&wf->count, -1, 1)))
-               __intel_wakeref_put_last(wf);
+               __intel_wakeref_put_last(wf, flags);
+}
+
+static inline void
+intel_wakeref_put(struct intel_wakeref *wf)
+{
+       might_sleep();
+       __intel_wakeref_put(wf, 0);
+}
+
+static inline void
+intel_wakeref_put_async(struct intel_wakeref *wf)
+{
+       __intel_wakeref_put(wf, INTEL_WAKEREF_PUT_ASYNC);
 }
 
 /**
@@ -152,6 +163,21 @@ intel_wakeref_unlock(struct intel_wakeref *wf)
 }
 
 /**
+ * intel_wakeref_unlock_wait: Wait until the active callback is complete
+ * @wf: the wakeref
+ *
+ * Waits for the active callback (under the @wf->mutex or another CPU) is
+ * complete.
+ */
+static inline void
+intel_wakeref_unlock_wait(struct intel_wakeref *wf)
+{
+       mutex_lock(&wf->mutex);
+       mutex_unlock(&wf->mutex);
+       flush_work(&wf->work);
+}
+
+/**
  * intel_wakeref_is_active: Query whether the wakeref is currently held
  * @wf: the wakeref
  *
@@ -170,6 +196,7 @@ intel_wakeref_is_active(const struct intel_wakeref *wf)
 static inline void
 __intel_wakeref_defer_park(struct intel_wakeref *wf)
 {
+       lockdep_assert_held(&wf->mutex);
        INTEL_WAKEREF_BUG_ON(atomic_read(&wf->count));
        atomic_set_release(&wf->count, 1);
 }
index 397f8b0..d43951c 100644 (file)
@@ -30,6 +30,8 @@ module_param_named(modeset, mgag200_modeset, int, 0400);
 static struct drm_driver driver;
 
 static const struct pci_device_id pciidlist[] = {
+       { PCI_VENDOR_ID_MATROX, 0x522, PCI_VENDOR_ID_SUN, 0x4852, 0, 0,
+               G200_SE_A | MGAG200_FLAG_HW_BUG_NO_STARTADD},
        { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A },
        { PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B },
        { PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV },
@@ -60,6 +62,35 @@ static void mga_pci_remove(struct pci_dev *pdev)
 
 DEFINE_DRM_GEM_FOPS(mgag200_driver_fops);
 
+static bool mgag200_pin_bo_at_0(const struct mga_device *mdev)
+{
+       return mdev->flags & MGAG200_FLAG_HW_BUG_NO_STARTADD;
+}
+
+int mgag200_driver_dumb_create(struct drm_file *file,
+                              struct drm_device *dev,
+                              struct drm_mode_create_dumb *args)
+{
+       struct mga_device *mdev = dev->dev_private;
+       unsigned long pg_align;
+
+       if (WARN_ONCE(!dev->vram_mm, "VRAM MM not initialized"))
+               return -EINVAL;
+
+       pg_align = 0ul;
+
+       /*
+        * Aligning scanout buffers to the size of the video ram forces
+        * placement at offset 0. Works around a bug where HW does not
+        * respect 'startadd' field.
+        */
+       if (mgag200_pin_bo_at_0(mdev))
+               pg_align = PFN_UP(mdev->mc.vram_size);
+
+       return drm_gem_vram_fill_create_dumb(file, dev, &dev->vram_mm->bdev,
+                                            pg_align, false, args);
+}
+
 static struct drm_driver driver = {
        .driver_features = DRIVER_GEM | DRIVER_MODESET,
        .load = mgag200_driver_load,
@@ -71,7 +102,10 @@ static struct drm_driver driver = {
        .major = DRIVER_MAJOR,
        .minor = DRIVER_MINOR,
        .patchlevel = DRIVER_PATCHLEVEL,
-       DRM_GEM_VRAM_DRIVER
+       .debugfs_init = drm_vram_mm_debugfs_init,
+       .dumb_create = mgag200_driver_dumb_create,
+       .dumb_map_offset = drm_gem_vram_driver_dumb_mmap_offset,
+       .gem_prime_mmap = drm_gem_prime_mmap,
 };
 
 static struct pci_driver mgag200_pci_driver = {
index 0ea9a52..aa32aad 100644 (file)
@@ -150,6 +150,12 @@ enum mga_type {
        G200_EW3,
 };
 
+/* HW does not handle 'startadd' field correct. */
+#define MGAG200_FLAG_HW_BUG_NO_STARTADD        (1ul << 8)
+
+#define MGAG200_TYPE_MASK      (0x000000ff)
+#define MGAG200_FLAG_MASK      (0x00ffff00)
+
 #define IS_G200_SE(mdev) (mdev->type == G200_SE_A || mdev->type == G200_SE_B)
 
 struct mga_device {
@@ -181,6 +187,18 @@ struct mga_device {
        u32 unique_rev_id;
 };
 
+static inline enum mga_type
+mgag200_type_from_driver_data(kernel_ulong_t driver_data)
+{
+       return (enum mga_type)(driver_data & MGAG200_TYPE_MASK);
+}
+
+static inline unsigned long
+mgag200_flags_from_driver_data(kernel_ulong_t driver_data)
+{
+       return driver_data & MGAG200_FLAG_MASK;
+}
+
                                /* mgag200_mode.c */
 int mgag200_modeset_init(struct mga_device *mdev);
 void mgag200_modeset_fini(struct mga_device *mdev);
index 5f74aab..e1bc5b0 100644 (file)
@@ -94,7 +94,8 @@ static int mgag200_device_init(struct drm_device *dev,
        struct mga_device *mdev = dev->dev_private;
        int ret, option;
 
-       mdev->type = flags;
+       mdev->flags = mgag200_flags_from_driver_data(flags);
+       mdev->type = mgag200_type_from_driver_data(flags);
 
        /* Hardcode the number of CRTCs to 1 */
        mdev->num_crtc = 1;
index e9160ce..6deaa7d 100644 (file)
@@ -7,6 +7,7 @@ config DRM_MSM
        depends on OF && COMMON_CLK
        depends on MMU
        depends on INTERCONNECT || !INTERCONNECT
+       depends on QCOM_OCMEM || QCOM_OCMEM=n
        select QCOM_MDT_LOADER if ARCH_QCOM
        select REGULATOR
        select DRM_KMS_HELPER
index 5f7e980..7ad1493 100644 (file)
@@ -6,10 +6,6 @@
  * Copyright (c) 2014 The Linux Foundation. All rights reserved.
  */
 
-#ifdef CONFIG_MSM_OCMEM
-#  include <mach/ocmem.h>
-#endif
-
 #include "a3xx_gpu.h"
 
 #define A3XX_INT0_MASK \
@@ -195,9 +191,9 @@ static int a3xx_hw_init(struct msm_gpu *gpu)
                gpu_write(gpu, REG_A3XX_RBBM_GPR0_CTL, 0x00000000);
 
        /* Set the OCMEM base address for A330, etc */
-       if (a3xx_gpu->ocmem_hdl) {
+       if (a3xx_gpu->ocmem.hdl) {
                gpu_write(gpu, REG_A3XX_RB_GMEM_BASE_ADDR,
-                       (unsigned int)(a3xx_gpu->ocmem_base >> 14));
+                       (unsigned int)(a3xx_gpu->ocmem.base >> 14));
        }
 
        /* Turn on performance counters: */
@@ -318,10 +314,7 @@ static void a3xx_destroy(struct msm_gpu *gpu)
 
        adreno_gpu_cleanup(adreno_gpu);
 
-#ifdef CONFIG_MSM_OCMEM
-       if (a3xx_gpu->ocmem_base)
-               ocmem_free(OCMEM_GRAPHICS, a3xx_gpu->ocmem_hdl);
-#endif
+       adreno_gpu_ocmem_cleanup(&a3xx_gpu->ocmem);
 
        kfree(a3xx_gpu);
 }
@@ -494,17 +487,10 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
 
        /* if needed, allocate gmem: */
        if (adreno_is_a330(adreno_gpu)) {
-#ifdef CONFIG_MSM_OCMEM
-               /* TODO this is different/missing upstream: */
-               struct ocmem_buf *ocmem_hdl =
-                               ocmem_allocate(OCMEM_GRAPHICS, adreno_gpu->gmem);
-
-               a3xx_gpu->ocmem_hdl = ocmem_hdl;
-               a3xx_gpu->ocmem_base = ocmem_hdl->addr;
-               adreno_gpu->gmem = ocmem_hdl->len;
-               DBG("using %dK of OCMEM at 0x%08x", adreno_gpu->gmem / 1024,
-                               a3xx_gpu->ocmem_base);
-#endif
+               ret = adreno_gpu_ocmem_init(&adreno_gpu->base.pdev->dev,
+                                           adreno_gpu, &a3xx_gpu->ocmem);
+               if (ret)
+                       goto fail;
        }
 
        if (!gpu->aspace) {
index 5dc33e5..c555fb1 100644 (file)
@@ -19,8 +19,7 @@ struct a3xx_gpu {
        struct adreno_gpu base;
 
        /* if OCMEM is used for GMEM: */
-       uint32_t ocmem_base;
-       void *ocmem_hdl;
+       struct adreno_ocmem ocmem;
 };
 #define to_a3xx_gpu(x) container_of(x, struct a3xx_gpu, base)
 
index ab2b752..b01388a 100644 (file)
@@ -2,9 +2,6 @@
 /* Copyright (c) 2014 The Linux Foundation. All rights reserved.
  */
 #include "a4xx_gpu.h"
-#ifdef CONFIG_MSM_OCMEM
-#  include <soc/qcom/ocmem.h>
-#endif
 
 #define A4XX_INT0_MASK \
        (A4XX_INT0_RBBM_AHB_ERROR |        \
@@ -188,7 +185,7 @@ static int a4xx_hw_init(struct msm_gpu *gpu)
                        (1 << 30) | 0xFFFF);
 
        gpu_write(gpu, REG_A4XX_RB_GMEM_BASE_ADDR,
-                       (unsigned int)(a4xx_gpu->ocmem_base >> 14));
+                       (unsigned int)(a4xx_gpu->ocmem.base >> 14));
 
        /* Turn on performance counters: */
        gpu_write(gpu, REG_A4XX_RBBM_PERFCTR_CTL, 0x01);
@@ -318,10 +315,7 @@ static void a4xx_destroy(struct msm_gpu *gpu)
 
        adreno_gpu_cleanup(adreno_gpu);
 
-#ifdef CONFIG_MSM_OCMEM
-       if (a4xx_gpu->ocmem_base)
-               ocmem_free(OCMEM_GRAPHICS, a4xx_gpu->ocmem_hdl);
-#endif
+       adreno_gpu_ocmem_cleanup(&a4xx_gpu->ocmem);
 
        kfree(a4xx_gpu);
 }
@@ -578,17 +572,10 @@ struct msm_gpu *a4xx_gpu_init(struct drm_device *dev)
 
        /* if needed, allocate gmem: */
        if (adreno_is_a4xx(adreno_gpu)) {
-#ifdef CONFIG_MSM_OCMEM
-               /* TODO this is different/missing upstream: */
-               struct ocmem_buf *ocmem_hdl =
-                               ocmem_allocate(OCMEM_GRAPHICS, adreno_gpu->gmem);
-
-               a4xx_gpu->ocmem_hdl = ocmem_hdl;
-               a4xx_gpu->ocmem_base = ocmem_hdl->addr;
-               adreno_gpu->gmem = ocmem_hdl->len;
-               DBG("using %dK of OCMEM at 0x%08x", adreno_gpu->gmem / 1024,
-                               a4xx_gpu->ocmem_base);
-#endif
+               ret = adreno_gpu_ocmem_init(dev->dev, adreno_gpu,
+                                           &a4xx_gpu->ocmem);
+               if (ret)
+                       goto fail;
        }
 
        if (!gpu->aspace) {
index d506311..a01448c 100644 (file)
@@ -16,8 +16,7 @@ struct a4xx_gpu {
        struct adreno_gpu base;
 
        /* if OCMEM is used for GMEM: */
-       uint32_t ocmem_base;
-       void *ocmem_hdl;
+       struct adreno_ocmem ocmem;
 };
 #define to_a4xx_gpu(x) container_of(x, struct a4xx_gpu, base)
 
index e9c55d1..b02e204 100644 (file)
@@ -353,6 +353,9 @@ static int a5xx_me_init(struct msm_gpu *gpu)
                 * 2D mode 3 draw
                 */
                OUT_RING(ring, 0x0000000B);
+       } else if (adreno_is_a510(adreno_gpu)) {
+               /* Workaround for token and syncs */
+               OUT_RING(ring, 0x00000001);
        } else {
                /* No workarounds enabled */
                OUT_RING(ring, 0x00000000);
@@ -568,15 +571,24 @@ static int a5xx_hw_init(struct msm_gpu *gpu)
                0x00100000 + adreno_gpu->gmem - 1);
        gpu_write(gpu, REG_A5XX_UCHE_GMEM_RANGE_MAX_HI, 0x00000000);
 
-       gpu_write(gpu, REG_A5XX_CP_MEQ_THRESHOLDS, 0x40);
-       if (adreno_is_a530(adreno_gpu))
-               gpu_write(gpu, REG_A5XX_CP_MERCIU_SIZE, 0x40);
-       if (adreno_is_a540(adreno_gpu))
-               gpu_write(gpu, REG_A5XX_CP_MERCIU_SIZE, 0x400);
-       gpu_write(gpu, REG_A5XX_CP_ROQ_THRESHOLDS_2, 0x80000060);
-       gpu_write(gpu, REG_A5XX_CP_ROQ_THRESHOLDS_1, 0x40201B16);
-
-       gpu_write(gpu, REG_A5XX_PC_DBG_ECO_CNTL, (0x400 << 11 | 0x300 << 22));
+       if (adreno_is_a510(adreno_gpu)) {
+               gpu_write(gpu, REG_A5XX_CP_MEQ_THRESHOLDS, 0x20);
+               gpu_write(gpu, REG_A5XX_CP_MERCIU_SIZE, 0x20);
+               gpu_write(gpu, REG_A5XX_CP_ROQ_THRESHOLDS_2, 0x40000030);
+               gpu_write(gpu, REG_A5XX_CP_ROQ_THRESHOLDS_1, 0x20100D0A);
+               gpu_write(gpu, REG_A5XX_PC_DBG_ECO_CNTL,
+                         (0x200 << 11 | 0x200 << 22));
+       } else {
+               gpu_write(gpu, REG_A5XX_CP_MEQ_THRESHOLDS, 0x40);
+               if (adreno_is_a530(adreno_gpu))
+                       gpu_write(gpu, REG_A5XX_CP_MERCIU_SIZE, 0x40);
+               if (adreno_is_a540(adreno_gpu))
+                       gpu_write(gpu, REG_A5XX_CP_MERCIU_SIZE, 0x400);
+               gpu_write(gpu, REG_A5XX_CP_ROQ_THRESHOLDS_2, 0x80000060);
+               gpu_write(gpu, REG_A5XX_CP_ROQ_THRESHOLDS_1, 0x40201B16);
+               gpu_write(gpu, REG_A5XX_PC_DBG_ECO_CNTL,
+                         (0x400 << 11 | 0x300 << 22));
+       }
 
        if (adreno_gpu->info->quirks & ADRENO_QUIRK_TWO_PASS_USE_WFI)
                gpu_rmw(gpu, REG_A5XX_PC_DBG_ECO_CNTL, 0, (1 << 8));
@@ -589,6 +601,19 @@ static int a5xx_hw_init(struct msm_gpu *gpu)
        /* Enable ME/PFP split notification */
        gpu_write(gpu, REG_A5XX_RBBM_AHB_CNTL1, 0xA6FFFFFF);
 
+       /*
+        *  In A5x, CCU can send context_done event of a particular context to
+        *  UCHE which ultimately reaches CP even when there is valid
+        *  transaction of that context inside CCU. This can let CP to program
+        *  config registers, which will make the "valid transaction" inside
+        *  CCU to be interpreted differently. This can cause gpu fault. This
+        *  bug is fixed in latest A510 revision. To enable this bug fix -
+        *  bit[11] of RB_DBG_ECO_CNTL need to be set to 0, default is 1
+        *  (disable). For older A510 version this bit is unused.
+        */
+       if (adreno_is_a510(adreno_gpu))
+               gpu_rmw(gpu, REG_A5XX_RB_DBG_ECO_CNTL, (1 << 11), 0);
+
        /* Enable HWCG */
        a5xx_set_hwcg(gpu, true);
 
@@ -635,7 +660,7 @@ static int a5xx_hw_init(struct msm_gpu *gpu)
        /* UCHE */
        gpu_write(gpu, REG_A5XX_CP_PROTECT(16), ADRENO_PROTECT_RW(0xE80, 16));
 
-       if (adreno_is_a530(adreno_gpu))
+       if (adreno_is_a530(adreno_gpu) || adreno_is_a510(adreno_gpu))
                gpu_write(gpu, REG_A5XX_CP_PROTECT(17),
                        ADRENO_PROTECT_RW(0x10000, 0x8000));
 
@@ -679,7 +704,8 @@ static int a5xx_hw_init(struct msm_gpu *gpu)
 
        a5xx_preempt_hw_init(gpu);
 
-       a5xx_gpmu_ucode_init(gpu);
+       if (!adreno_is_a510(adreno_gpu))
+               a5xx_gpmu_ucode_init(gpu);
 
        ret = a5xx_ucode_init(gpu);
        if (ret)
@@ -712,7 +738,8 @@ static int a5xx_hw_init(struct msm_gpu *gpu)
        }
 
        /*
-        * Try to load a zap shader into the secure world. If successful
+        * If the chip that we are using does support loading one, then
+        * try to load a zap shader into the secure world. If successful
         * we can use the CP to switch out of secure mode. If not then we
         * have no resource but to try to switch ourselves out manually. If we
         * guessed wrong then access to the RBBM_SECVID_TRUST_CNTL register will
@@ -1066,6 +1093,7 @@ static void a5xx_dump(struct msm_gpu *gpu)
 
 static int a5xx_pm_resume(struct msm_gpu *gpu)
 {
+       struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
        int ret;
 
        /* Turn on the core power */
@@ -1073,6 +1101,15 @@ static int a5xx_pm_resume(struct msm_gpu *gpu)
        if (ret)
                return ret;
 
+       if (adreno_is_a510(adreno_gpu)) {
+               /* Halt the sp_input_clk at HM level */
+               gpu_write(gpu, REG_A5XX_RBBM_CLOCK_CNTL, 0x00000055);
+               a5xx_set_hwcg(gpu, true);
+               /* Turn on sp_input_clk at HM level */
+               gpu_rmw(gpu, REG_A5XX_RBBM_CLOCK_CNTL, 0xff, 0);
+               return 0;
+       }
+
        /* Turn the RBCCU domain first to limit the chances of voltage droop */
        gpu_write(gpu, REG_A5XX_GPMU_RBCCU_POWER_CNTL, 0x778000);
 
@@ -1101,9 +1138,17 @@ static int a5xx_pm_resume(struct msm_gpu *gpu)
 
 static int a5xx_pm_suspend(struct msm_gpu *gpu)
 {
+       struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+       u32 mask = 0xf;
+
+       /* A510 has 3 XIN ports in VBIF */
+       if (adreno_is_a510(adreno_gpu))
+               mask = 0x7;
+
        /* Clear the VBIF pipe before shutting down */
-       gpu_write(gpu, REG_A5XX_VBIF_XIN_HALT_CTRL0, 0xF);
-       spin_until((gpu_read(gpu, REG_A5XX_VBIF_XIN_HALT_CTRL1) & 0xF) == 0xF);
+       gpu_write(gpu, REG_A5XX_VBIF_XIN_HALT_CTRL0, mask);
+       spin_until((gpu_read(gpu, REG_A5XX_VBIF_XIN_HALT_CTRL1) &
+                               mask) == mask);
 
        gpu_write(gpu, REG_A5XX_VBIF_XIN_HALT_CTRL0, 0);
 
@@ -1289,7 +1334,7 @@ static void a5xx_gpu_state_destroy(struct kref *kref)
        kfree(a5xx_state);
 }
 
-int a5xx_gpu_state_put(struct msm_gpu_state *state)
+static int a5xx_gpu_state_put(struct msm_gpu_state *state)
 {
        if (IS_ERR_OR_NULL(state))
                return 1;
@@ -1299,8 +1344,8 @@ int a5xx_gpu_state_put(struct msm_gpu_state *state)
 
 
 #if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
-void a5xx_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
-               struct drm_printer *p)
+static void a5xx_show(struct msm_gpu *gpu, struct msm_gpu_state *state,
+                     struct drm_printer *p)
 {
        int i, j;
        u32 pos = 0;
index a3a06db..321a806 100644 (file)
@@ -297,6 +297,10 @@ int a5xx_power_init(struct msm_gpu *gpu)
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
        int ret;
 
+       /* Not all A5xx chips have a GPMU */
+       if (adreno_is_a510(adreno_gpu))
+               return 0;
+
        /* Set up the limits management */
        if (adreno_is_a530(adreno_gpu))
                a530_lm_setup(gpu);
@@ -326,6 +330,9 @@ void a5xx_gpmu_ucode_init(struct msm_gpu *gpu)
        unsigned int *data, *ptr, *cmds;
        unsigned int cmds_size;
 
+       if (adreno_is_a510(adreno_gpu))
+               return;
+
        if (a5xx_gpu->gpmu_bo)
                return;
 
index 0888e0d..fbbdf86 100644 (file)
@@ -115,6 +115,21 @@ static const struct adreno_info gpulist[] = {
                .inactive_period = DRM_MSM_INACTIVE_PERIOD,
                .init  = a4xx_gpu_init,
        }, {
+               .rev   = ADRENO_REV(5, 1, 0, ANY_ID),
+               .revn = 510,
+               .name = "A510",
+               .fw = {
+                       [ADRENO_FW_PM4] = "a530_pm4.fw",
+                       [ADRENO_FW_PFP] = "a530_pfp.fw",
+               },
+               .gmem = SZ_256K,
+               /*
+                * Increase inactive period to 250 to avoid bouncing
+                * the GDSC which appears to make it grumpy
+                */
+               .inactive_period = 250,
+               .init = a5xx_gpu_init,
+       }, {
                .rev = ADRENO_REV(5, 3, 0, 2),
                .revn = 530,
                .name = "A530",
index 048c8be..0783e4b 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/pm_opp.h>
 #include <linux/slab.h>
 #include <linux/soc/qcom/mdt_loader.h>
+#include <soc/qcom/ocmem.h>
 #include "adreno_gpu.h"
 #include "msm_gem.h"
 #include "msm_mmu.h"
@@ -893,6 +894,45 @@ static int adreno_get_pwrlevels(struct device *dev,
        return 0;
 }
 
+int adreno_gpu_ocmem_init(struct device *dev, struct adreno_gpu *adreno_gpu,
+                         struct adreno_ocmem *adreno_ocmem)
+{
+       struct ocmem_buf *ocmem_hdl;
+       struct ocmem *ocmem;
+
+       ocmem = of_get_ocmem(dev);
+       if (IS_ERR(ocmem)) {
+               if (PTR_ERR(ocmem) == -ENODEV) {
+                       /*
+                        * Return success since either the ocmem property was
+                        * not specified in device tree, or ocmem support is
+                        * not compiled into the kernel.
+                        */
+                       return 0;
+               }
+
+               return PTR_ERR(ocmem);
+       }
+
+       ocmem_hdl = ocmem_allocate(ocmem, OCMEM_GRAPHICS, adreno_gpu->gmem);
+       if (IS_ERR(ocmem_hdl))
+               return PTR_ERR(ocmem_hdl);
+
+       adreno_ocmem->ocmem = ocmem;
+       adreno_ocmem->base = ocmem_hdl->addr;
+       adreno_ocmem->hdl = ocmem_hdl;
+       adreno_gpu->gmem = ocmem_hdl->len;
+
+       return 0;
+}
+
+void adreno_gpu_ocmem_cleanup(struct adreno_ocmem *adreno_ocmem)
+{
+       if (adreno_ocmem && adreno_ocmem->base)
+               ocmem_free(adreno_ocmem->ocmem, OCMEM_GRAPHICS,
+                          adreno_ocmem->hdl);
+}
+
 int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
                struct adreno_gpu *adreno_gpu,
                const struct adreno_gpu_funcs *funcs, int nr_rings)
index c7441fb..e71a757 100644 (file)
@@ -126,6 +126,12 @@ struct adreno_gpu {
 };
 #define to_adreno_gpu(x) container_of(x, struct adreno_gpu, base)
 
+struct adreno_ocmem {
+       struct ocmem *ocmem;
+       unsigned long base;
+       void *hdl;
+};
+
 /* platform config data (ie. from DT, or pdata) */
 struct adreno_platform_config {
        struct adreno_rev rev;
@@ -206,6 +212,11 @@ static inline int adreno_is_a430(struct adreno_gpu *gpu)
        return gpu->revn == 430;
 }
 
+static inline int adreno_is_a510(struct adreno_gpu *gpu)
+{
+       return gpu->revn == 510;
+}
+
 static inline int adreno_is_a530(struct adreno_gpu *gpu)
 {
        return gpu->revn == 530;
@@ -236,6 +247,10 @@ void adreno_dump(struct msm_gpu *gpu);
 void adreno_wait_ring(struct msm_ringbuffer *ring, uint32_t ndwords);
 struct msm_ringbuffer *adreno_active_ring(struct msm_gpu *gpu);
 
+int adreno_gpu_ocmem_init(struct device *dev, struct adreno_gpu *adreno_gpu,
+                         struct adreno_ocmem *ocmem);
+void adreno_gpu_ocmem_cleanup(struct adreno_ocmem *ocmem);
+
 int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
                struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,
                int nr_rings);
index cdbea38..f1bc6a1 100644 (file)
@@ -55,8 +55,7 @@ static void dpu_core_irq_callback_handler(void *arg, int irq_idx)
 int dpu_core_irq_idx_lookup(struct dpu_kms *dpu_kms,
                enum dpu_intr_type intr_type, u32 instance_idx)
 {
-       if (!dpu_kms || !dpu_kms->hw_intr ||
-                       !dpu_kms->hw_intr->ops.irq_idx_lookup)
+       if (!dpu_kms->hw_intr || !dpu_kms->hw_intr->ops.irq_idx_lookup)
                return -EINVAL;
 
        return dpu_kms->hw_intr->ops.irq_idx_lookup(intr_type,
@@ -73,7 +72,7 @@ static int _dpu_core_irq_enable(struct dpu_kms *dpu_kms, int irq_idx)
        unsigned long irq_flags;
        int ret = 0, enable_count;
 
-       if (!dpu_kms || !dpu_kms->hw_intr ||
+       if (!dpu_kms->hw_intr ||
                        !dpu_kms->irq_obj.enable_counts ||
                        !dpu_kms->irq_obj.irq_counts) {
                DPU_ERROR("invalid params\n");
@@ -114,7 +113,7 @@ int dpu_core_irq_enable(struct dpu_kms *dpu_kms, int *irq_idxs, u32 irq_count)
 {
        int i, ret = 0, counts;
 
-       if (!dpu_kms || !irq_idxs || !irq_count) {
+       if (!irq_idxs || !irq_count) {
                DPU_ERROR("invalid params\n");
                return -EINVAL;
        }
@@ -138,7 +137,7 @@ static int _dpu_core_irq_disable(struct dpu_kms *dpu_kms, int irq_idx)
 {
        int ret = 0, enable_count;
 
-       if (!dpu_kms || !dpu_kms->hw_intr || !dpu_kms->irq_obj.enable_counts) {
+       if (!dpu_kms->hw_intr || !dpu_kms->irq_obj.enable_counts) {
                DPU_ERROR("invalid params\n");
                return -EINVAL;
        }
@@ -169,7 +168,7 @@ int dpu_core_irq_disable(struct dpu_kms *dpu_kms, int *irq_idxs, u32 irq_count)
 {
        int i, ret = 0, counts;
 
-       if (!dpu_kms || !irq_idxs || !irq_count) {
+       if (!irq_idxs || !irq_count) {
                DPU_ERROR("invalid params\n");
                return -EINVAL;
        }
@@ -186,7 +185,7 @@ int dpu_core_irq_disable(struct dpu_kms *dpu_kms, int *irq_idxs, u32 irq_count)
 
 u32 dpu_core_irq_read(struct dpu_kms *dpu_kms, int irq_idx, bool clear)
 {
-       if (!dpu_kms || !dpu_kms->hw_intr ||
+       if (!dpu_kms->hw_intr ||
                        !dpu_kms->hw_intr->ops.get_interrupt_status)
                return 0;
 
@@ -205,7 +204,7 @@ int dpu_core_irq_register_callback(struct dpu_kms *dpu_kms, int irq_idx,
 {
        unsigned long irq_flags;
 
-       if (!dpu_kms || !dpu_kms->irq_obj.irq_cb_tbl) {
+       if (!dpu_kms->irq_obj.irq_cb_tbl) {
                DPU_ERROR("invalid params\n");
                return -EINVAL;
        }
@@ -240,7 +239,7 @@ int dpu_core_irq_unregister_callback(struct dpu_kms *dpu_kms, int irq_idx,
 {
        unsigned long irq_flags;
 
-       if (!dpu_kms || !dpu_kms->irq_obj.irq_cb_tbl) {
+       if (!dpu_kms->irq_obj.irq_cb_tbl) {
                DPU_ERROR("invalid params\n");
                return -EINVAL;
        }
@@ -274,8 +273,7 @@ int dpu_core_irq_unregister_callback(struct dpu_kms *dpu_kms, int irq_idx,
 
 static void dpu_clear_all_irqs(struct dpu_kms *dpu_kms)
 {
-       if (!dpu_kms || !dpu_kms->hw_intr ||
-                       !dpu_kms->hw_intr->ops.clear_all_irqs)
+       if (!dpu_kms->hw_intr || !dpu_kms->hw_intr->ops.clear_all_irqs)
                return;
 
        dpu_kms->hw_intr->ops.clear_all_irqs(dpu_kms->hw_intr);
@@ -283,8 +281,7 @@ static void dpu_clear_all_irqs(struct dpu_kms *dpu_kms)
 
 static void dpu_disable_all_irqs(struct dpu_kms *dpu_kms)
 {
-       if (!dpu_kms || !dpu_kms->hw_intr ||
-                       !dpu_kms->hw_intr->ops.disable_all_irqs)
+       if (!dpu_kms->hw_intr || !dpu_kms->hw_intr->ops.disable_all_irqs)
                return;
 
        dpu_kms->hw_intr->ops.disable_all_irqs(dpu_kms->hw_intr);
@@ -343,18 +340,8 @@ void dpu_debugfs_core_irq_init(struct dpu_kms *dpu_kms,
 
 void dpu_core_irq_preinstall(struct dpu_kms *dpu_kms)
 {
-       struct msm_drm_private *priv;
        int i;
 
-       if (!dpu_kms->dev) {
-               DPU_ERROR("invalid drm device\n");
-               return;
-       } else if (!dpu_kms->dev->dev_private) {
-               DPU_ERROR("invalid device private\n");
-               return;
-       }
-       priv = dpu_kms->dev->dev_private;
-
        pm_runtime_get_sync(&dpu_kms->pdev->dev);
        dpu_clear_all_irqs(dpu_kms);
        dpu_disable_all_irqs(dpu_kms);
@@ -379,18 +366,8 @@ void dpu_core_irq_preinstall(struct dpu_kms *dpu_kms)
 
 void dpu_core_irq_uninstall(struct dpu_kms *dpu_kms)
 {
-       struct msm_drm_private *priv;
        int i;
 
-       if (!dpu_kms->dev) {
-               DPU_ERROR("invalid drm device\n");
-               return;
-       } else if (!dpu_kms->dev->dev_private) {
-               DPU_ERROR("invalid device private\n");
-               return;
-       }
-       priv = dpu_kms->dev->dev_private;
-
        pm_runtime_get_sync(&dpu_kms->pdev->dev);
        for (i = 0; i < dpu_kms->irq_obj.total_irqs; i++)
                if (atomic_read(&dpu_kms->irq_obj.enable_counts[i]) ||
index 09a49b5..11f2beb 100644 (file)
@@ -32,18 +32,7 @@ enum dpu_perf_mode {
 static struct dpu_kms *_dpu_crtc_get_kms(struct drm_crtc *crtc)
 {
        struct msm_drm_private *priv;
-
-       if (!crtc->dev || !crtc->dev->dev_private) {
-               DPU_ERROR("invalid device\n");
-               return NULL;
-       }
-
        priv = crtc->dev->dev_private;
-       if (!priv || !priv->kms) {
-               DPU_ERROR("invalid kms\n");
-               return NULL;
-       }
-
        return to_dpu_kms(priv->kms);
 }
 
@@ -116,7 +105,7 @@ int dpu_core_perf_crtc_check(struct drm_crtc *crtc,
        }
 
        kms = _dpu_crtc_get_kms(crtc);
-       if (!kms || !kms->catalog) {
+       if (!kms->catalog) {
                DPU_ERROR("invalid parameters\n");
                return 0;
        }
@@ -215,7 +204,6 @@ static int _dpu_core_perf_crtc_update_bus(struct dpu_kms *kms,
 void dpu_core_perf_crtc_release_bw(struct drm_crtc *crtc)
 {
        struct dpu_crtc *dpu_crtc;
-       struct dpu_crtc_state *dpu_cstate;
        struct dpu_kms *kms;
 
        if (!crtc) {
@@ -224,13 +212,12 @@ void dpu_core_perf_crtc_release_bw(struct drm_crtc *crtc)
        }
 
        kms = _dpu_crtc_get_kms(crtc);
-       if (!kms || !kms->catalog) {
+       if (!kms->catalog) {
                DPU_ERROR("invalid kms\n");
                return;
        }
 
        dpu_crtc = to_dpu_crtc(crtc);
-       dpu_cstate = to_dpu_crtc_state(crtc->state);
 
        if (atomic_dec_return(&kms->bandwidth_ref) > 0)
                return;
@@ -287,7 +274,6 @@ int dpu_core_perf_crtc_update(struct drm_crtc *crtc,
        u64 clk_rate = 0;
        struct dpu_crtc *dpu_crtc;
        struct dpu_crtc_state *dpu_cstate;
-       struct msm_drm_private *priv;
        struct dpu_kms *kms;
        int ret;
 
@@ -297,11 +283,10 @@ int dpu_core_perf_crtc_update(struct drm_crtc *crtc,
        }
 
        kms = _dpu_crtc_get_kms(crtc);
-       if (!kms || !kms->catalog) {
+       if (!kms->catalog) {
                DPU_ERROR("invalid kms\n");
                return -EINVAL;
        }
-       priv = kms->dev->dev_private;
 
        dpu_crtc = to_dpu_crtc(crtc);
        dpu_cstate = to_dpu_crtc_state(crtc->state);
index ce59adf..f197dce 100644 (file)
@@ -266,11 +266,20 @@ enum dpu_intf_mode dpu_crtc_get_intf_mode(struct drm_crtc *crtc)
 {
        struct drm_encoder *encoder;
 
-       if (!crtc || !crtc->dev) {
+       if (!crtc) {
                DPU_ERROR("invalid crtc\n");
                return INTF_MODE_NONE;
        }
 
+       /*
+        * TODO: This function is called from dpu debugfs and as part of atomic
+        * check. When called from debugfs, the crtc->mutex must be held to
+        * read crtc->state. However reading crtc->state from atomic check isn't
+        * allowed (unless you have a good reason, a big comment, and a deep
+        * understanding of how the atomic/modeset locks work (<- and this is
+        * probably not possible)). So we'll keep the WARN_ON here for now, but
+        * really we need to figure out a better way to track our operating mode
+        */
        WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
 
        /* TODO: Returns the first INTF_MODE, could there be multiple values? */
@@ -694,7 +703,7 @@ static void dpu_crtc_disable(struct drm_crtc *crtc,
        unsigned long flags;
        bool release_bandwidth = false;
 
-       if (!crtc || !crtc->dev || !crtc->dev->dev_private || !crtc->state) {
+       if (!crtc || !crtc->state) {
                DPU_ERROR("invalid crtc\n");
                return;
        }
@@ -766,7 +775,7 @@ static void dpu_crtc_enable(struct drm_crtc *crtc,
        struct msm_drm_private *priv;
        bool request_bandwidth;
 
-       if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
+       if (!crtc) {
                DPU_ERROR("invalid crtc\n");
                return;
        }
@@ -1288,13 +1297,8 @@ struct drm_crtc *dpu_crtc_init(struct drm_device *dev, struct drm_plane *plane,
 {
        struct drm_crtc *crtc = NULL;
        struct dpu_crtc *dpu_crtc = NULL;
-       struct msm_drm_private *priv = NULL;
-       struct dpu_kms *kms = NULL;
        int i;
 
-       priv = dev->dev_private;
-       kms = to_dpu_kms(priv->kms);
-
        dpu_crtc = kzalloc(sizeof(*dpu_crtc), GFP_KERNEL);
        if (!dpu_crtc)
                return ERR_PTR(-ENOMEM);
index d82ea99..f96e142 100644 (file)
@@ -645,11 +645,6 @@ static void _dpu_encoder_update_vsync_source(struct dpu_encoder_virt *dpu_enc,
        priv = drm_enc->dev->dev_private;
 
        dpu_kms = to_dpu_kms(priv->kms);
-       if (!dpu_kms) {
-               DPU_ERROR("invalid dpu_kms\n");
-               return;
-       }
-
        hw_mdptop = dpu_kms->hw_mdp;
        if (!hw_mdptop) {
                DPU_ERROR("invalid mdptop\n");
@@ -735,8 +730,7 @@ static int dpu_encoder_resource_control(struct drm_encoder *drm_enc,
        struct msm_drm_private *priv;
        bool is_vid_mode = false;
 
-       if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private ||
-                       !drm_enc->crtc) {
+       if (!drm_enc || !drm_enc->dev || !drm_enc->crtc) {
                DPU_ERROR("invalid parameters\n");
                return -EINVAL;
        }
@@ -1092,17 +1086,13 @@ static void _dpu_encoder_virt_enable_helper(struct drm_encoder *drm_enc)
        struct msm_drm_private *priv;
        struct dpu_kms *dpu_kms;
 
-       if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private) {
+       if (!drm_enc || !drm_enc->dev) {
                DPU_ERROR("invalid parameters\n");
                return;
        }
 
        priv = drm_enc->dev->dev_private;
        dpu_kms = to_dpu_kms(priv->kms);
-       if (!dpu_kms) {
-               DPU_ERROR("invalid dpu_kms\n");
-               return;
-       }
 
        dpu_enc = to_dpu_encoder_virt(drm_enc);
        if (!dpu_enc || !dpu_enc->cur_master) {
@@ -1184,7 +1174,6 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
        struct dpu_encoder_virt *dpu_enc = NULL;
        struct msm_drm_private *priv;
        struct dpu_kms *dpu_kms;
-       struct drm_display_mode *mode;
        int i = 0;
 
        if (!drm_enc) {
@@ -1193,9 +1182,6 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
        } else if (!drm_enc->dev) {
                DPU_ERROR("invalid dev\n");
                return;
-       } else if (!drm_enc->dev->dev_private) {
-               DPU_ERROR("invalid dev_private\n");
-               return;
        }
 
        dpu_enc = to_dpu_encoder_virt(drm_enc);
@@ -1204,8 +1190,6 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
        mutex_lock(&dpu_enc->enc_lock);
        dpu_enc->enabled = false;
 
-       mode = &drm_enc->crtc->state->adjusted_mode;
-
        priv = drm_enc->dev->dev_private;
        dpu_kms = to_dpu_kms(priv->kms);
 
@@ -1734,8 +1718,7 @@ static void dpu_encoder_vsync_event_handler(struct timer_list *t)
        struct msm_drm_private *priv;
        struct msm_drm_thread *event_thread;
 
-       if (!drm_enc->dev || !drm_enc->dev->dev_private ||
-                       !drm_enc->crtc) {
+       if (!drm_enc->dev || !drm_enc->crtc) {
                DPU_ERROR("invalid parameters\n");
                return;
        }
@@ -1914,8 +1897,6 @@ static int _dpu_encoder_debugfs_status_open(struct inode *inode,
 static int _dpu_encoder_init_debugfs(struct drm_encoder *drm_enc)
 {
        struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
-       struct msm_drm_private *priv;
-       struct dpu_kms *dpu_kms;
        int i;
 
        static const struct file_operations debugfs_status_fops = {
@@ -1927,14 +1908,11 @@ static int _dpu_encoder_init_debugfs(struct drm_encoder *drm_enc)
 
        char name[DPU_NAME_SIZE];
 
-       if (!drm_enc->dev || !drm_enc->dev->dev_private) {
+       if (!drm_enc->dev) {
                DPU_ERROR("invalid encoder or kms\n");
                return -EINVAL;
        }
 
-       priv = drm_enc->dev->dev_private;
-       dpu_kms = to_dpu_kms(priv->kms);
-
        snprintf(name, DPU_NAME_SIZE, "encoder%u", drm_enc->base.id);
 
        /* create overall sub-directory for the encoder */
@@ -2042,9 +2020,8 @@ static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc,
        enum dpu_intf_type intf_type;
        struct dpu_enc_phys_init_params phys_params;
 
-       if (!dpu_enc || !dpu_kms) {
-               DPU_ERROR("invalid arg(s), enc %d kms %d\n",
-                               dpu_enc != 0, dpu_kms != 0);
+       if (!dpu_enc) {
+               DPU_ERROR("invalid arg(s), enc %d\n", dpu_enc != 0);
                return -EINVAL;
        }
 
@@ -2133,14 +2110,12 @@ static void dpu_encoder_frame_done_timeout(struct timer_list *t)
        struct dpu_encoder_virt *dpu_enc = from_timer(dpu_enc, t,
                        frame_done_timer);
        struct drm_encoder *drm_enc = &dpu_enc->base;
-       struct msm_drm_private *priv;
        u32 event;
 
-       if (!drm_enc->dev || !drm_enc->dev->dev_private) {
+       if (!drm_enc->dev) {
                DPU_ERROR("invalid parameters\n");
                return;
        }
-       priv = drm_enc->dev->dev_private;
 
        if (!dpu_enc->frame_busy_mask[0] || !dpu_enc->crtc_frame_event_cb) {
                DRM_DEBUG_KMS("id:%u invalid timeout frame_busy_mask=%lu\n",
index 2923b63..0479609 100644 (file)
@@ -124,13 +124,11 @@ static void dpu_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx)
 static void dpu_encoder_phys_cmd_ctl_start_irq(void *arg, int irq_idx)
 {
        struct dpu_encoder_phys *phys_enc = arg;
-       struct dpu_encoder_phys_cmd *cmd_enc;
 
        if (!phys_enc || !phys_enc->hw_ctl)
                return;
 
        DPU_ATRACE_BEGIN("ctl_start_irq");
-       cmd_enc = to_dpu_encoder_phys_cmd(phys_enc);
 
        atomic_add_unless(&phys_enc->pending_ctlstart_cnt, -1, 0);
 
@@ -316,13 +314,9 @@ end:
 static void dpu_encoder_phys_cmd_irq_control(struct dpu_encoder_phys *phys_enc,
                bool enable)
 {
-       struct dpu_encoder_phys_cmd *cmd_enc;
-
        if (!phys_enc)
                return;
 
-       cmd_enc = to_dpu_encoder_phys_cmd(phys_enc);
-
        trace_dpu_enc_phys_cmd_irq_ctrl(DRMID(phys_enc->parent),
                        phys_enc->hw_pp->idx - PINGPONG_0,
                        enable, atomic_read(&phys_enc->vblank_refcount));
@@ -355,7 +349,6 @@ static void dpu_encoder_phys_cmd_tearcheck_config(
        struct drm_display_mode *mode;
        bool tc_enable = true;
        u32 vsync_hz;
-       struct msm_drm_private *priv;
        struct dpu_kms *dpu_kms;
 
        if (!phys_enc || !phys_enc->hw_pp) {
@@ -373,11 +366,6 @@ static void dpu_encoder_phys_cmd_tearcheck_config(
        }
 
        dpu_kms = phys_enc->dpu_kms;
-       if (!dpu_kms || !dpu_kms->dev || !dpu_kms->dev->dev_private) {
-               DPU_ERROR("invalid device\n");
-               return;
-       }
-       priv = dpu_kms->dev->dev_private;
 
        /*
         * TE default: dsi byte clock calculated base on 70 fps;
@@ -650,13 +638,10 @@ static int dpu_encoder_phys_cmd_wait_for_tx_complete(
                struct dpu_encoder_phys *phys_enc)
 {
        int rc;
-       struct dpu_encoder_phys_cmd *cmd_enc;
 
        if (!phys_enc)
                return -EINVAL;
 
-       cmd_enc = to_dpu_encoder_phys_cmd(phys_enc);
-
        rc = _dpu_encoder_phys_cmd_wait_for_idle(phys_enc);
        if (rc) {
                DRM_ERROR("failed wait_for_idle: id:%u ret:%d intf:%d\n",
index b9c84fb..3123ef8 100644 (file)
@@ -374,7 +374,7 @@ static void dpu_encoder_phys_vid_mode_set(
                struct drm_display_mode *mode,
                struct drm_display_mode *adj_mode)
 {
-       if (!phys_enc || !phys_enc->dpu_kms) {
+       if (!phys_enc) {
                DPU_ERROR("invalid encoder/kms\n");
                return;
        }
@@ -566,16 +566,13 @@ static void dpu_encoder_phys_vid_prepare_for_kickoff(
 
 static void dpu_encoder_phys_vid_disable(struct dpu_encoder_phys *phys_enc)
 {
-       struct msm_drm_private *priv;
        unsigned long lock_flags;
        int ret;
 
-       if (!phys_enc || !phys_enc->parent || !phys_enc->parent->dev ||
-                       !phys_enc->parent->dev->dev_private) {
+       if (!phys_enc || !phys_enc->parent || !phys_enc->parent->dev) {
                DPU_ERROR("invalid encoder/device\n");
                return;
        }
-       priv = phys_enc->parent->dev->dev_private;
 
        if (!phys_enc->hw_intf || !phys_enc->hw_ctl) {
                DPU_ERROR("invalid hw_intf %d hw_ctl %d\n",
index 58b0485..6c92f0f 100644 (file)
 #define CREATE_TRACE_POINTS
 #include "dpu_trace.h"
 
-static const char * const iommu_ports[] = {
-               "mdp_0",
-};
-
 /*
  * To enable overall DRM driver logging
  * # echo 0x2 > /sys/module/drm/parameters/debug
@@ -68,16 +64,14 @@ static int _dpu_danger_signal_status(struct seq_file *s,
                bool danger_status)
 {
        struct dpu_kms *kms = (struct dpu_kms *)s->private;
-       struct msm_drm_private *priv;
        struct dpu_danger_safe_status status;
        int i;
 
-       if (!kms->dev || !kms->dev->dev_private || !kms->hw_mdp) {
+       if (!kms->hw_mdp) {
                DPU_ERROR("invalid arg(s)\n");
                return 0;
        }
 
-       priv = kms->dev->dev_private;
        memset(&status, 0, sizeof(struct dpu_danger_safe_status));
 
        pm_runtime_get_sync(&kms->pdev->dev);
@@ -153,13 +147,7 @@ static int _dpu_debugfs_show_regset32(struct seq_file *s, void *data)
                return 0;
 
        dev = dpu_kms->dev;
-       if (!dev)
-               return 0;
-
        priv = dev->dev_private;
-       if (!priv)
-               return 0;
-
        base = dpu_kms->mmio + regset->offset;
 
        /* insert padding spaces, if needed */
@@ -280,7 +268,6 @@ static void dpu_kms_prepare_commit(struct msm_kms *kms,
                struct drm_atomic_state *state)
 {
        struct dpu_kms *dpu_kms;
-       struct msm_drm_private *priv;
        struct drm_device *dev;
        struct drm_crtc *crtc;
        struct drm_crtc_state *crtc_state;
@@ -292,10 +279,6 @@ static void dpu_kms_prepare_commit(struct msm_kms *kms,
        dpu_kms = to_dpu_kms(kms);
        dev = dpu_kms->dev;
 
-       if (!dev || !dev->dev_private)
-               return;
-       priv = dev->dev_private;
-
        /* Call prepare_commit for all affected encoders */
        for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
                drm_for_each_encoder_mask(encoder, crtc->dev,
@@ -333,7 +316,6 @@ void dpu_kms_encoder_enable(struct drm_encoder *encoder)
        if (funcs && funcs->commit)
                funcs->commit(encoder);
 
-       WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
        drm_for_each_crtc(crtc, dev) {
                if (!(crtc->state->encoder_mask & drm_encoder_mask(encoder)))
                        continue;
@@ -464,16 +446,6 @@ static void _dpu_kms_drm_obj_destroy(struct dpu_kms *dpu_kms)
        struct msm_drm_private *priv;
        int i;
 
-       if (!dpu_kms) {
-               DPU_ERROR("invalid dpu_kms\n");
-               return;
-       } else if (!dpu_kms->dev) {
-               DPU_ERROR("invalid dev\n");
-               return;
-       } else if (!dpu_kms->dev->dev_private) {
-               DPU_ERROR("invalid dev_private\n");
-               return;
-       }
        priv = dpu_kms->dev->dev_private;
 
        for (i = 0; i < priv->num_crtcs; i++)
@@ -505,7 +477,6 @@ static int _dpu_kms_drm_obj_init(struct dpu_kms *dpu_kms)
 
        int primary_planes_idx = 0, cursor_planes_idx = 0, i, ret;
        int max_crtc_count;
-
        dev = dpu_kms->dev;
        priv = dev->dev_private;
        catalog = dpu_kms->catalog;
@@ -585,8 +556,6 @@ static void _dpu_kms_hw_destroy(struct dpu_kms *dpu_kms)
        int i;
 
        dev = dpu_kms->dev;
-       if (!dev)
-               return;
 
        if (dpu_kms->hw_intr)
                dpu_hw_intr_destroy(dpu_kms->hw_intr);
@@ -725,8 +694,7 @@ static void _dpu_kms_mmu_destroy(struct dpu_kms *dpu_kms)
 
        mmu = dpu_kms->base.aspace->mmu;
 
-       mmu->funcs->detach(mmu, (const char **)iommu_ports,
-                       ARRAY_SIZE(iommu_ports));
+       mmu->funcs->detach(mmu);
        msm_gem_address_space_put(dpu_kms->base.aspace);
 
        dpu_kms->base.aspace = NULL;
@@ -752,8 +720,7 @@ static int _dpu_kms_mmu_init(struct dpu_kms *dpu_kms)
                return PTR_ERR(aspace);
        }
 
-       ret = aspace->mmu->funcs->attach(aspace->mmu, iommu_ports,
-                       ARRAY_SIZE(iommu_ports));
+       ret = aspace->mmu->funcs->attach(aspace->mmu);
        if (ret) {
                DPU_ERROR("failed to attach iommu %d\n", ret);
                msm_gem_address_space_put(aspace);
@@ -803,16 +770,7 @@ static int dpu_kms_hw_init(struct msm_kms *kms)
 
        dpu_kms = to_dpu_kms(kms);
        dev = dpu_kms->dev;
-       if (!dev) {
-               DPU_ERROR("invalid device\n");
-               return rc;
-       }
-
        priv = dev->dev_private;
-       if (!priv) {
-               DPU_ERROR("invalid private data\n");
-               return rc;
-       }
 
        atomic_set(&dpu_kms->bandwidth_ref, 0);
 
@@ -974,7 +932,7 @@ struct msm_kms *dpu_kms_init(struct drm_device *dev)
        struct dpu_kms *dpu_kms;
        int irq;
 
-       if (!dev || !dev->dev_private) {
+       if (!dev) {
                DPU_ERROR("drm device node invalid\n");
                return ERR_PTR(-EINVAL);
        }
@@ -1064,11 +1022,6 @@ static int __maybe_unused dpu_runtime_suspend(struct device *dev)
        struct dss_module_power *mp = &dpu_kms->mp;
 
        ddev = dpu_kms->dev;
-       if (!ddev) {
-               DPU_ERROR("invalid drm_device\n");
-               return rc;
-       }
-
        rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, false);
        if (rc)
                DPU_ERROR("clock disable failed rc:%d\n", rc);
@@ -1086,11 +1039,6 @@ static int __maybe_unused dpu_runtime_resume(struct device *dev)
        struct dss_module_power *mp = &dpu_kms->mp;
 
        ddev = dpu_kms->dev;
-       if (!ddev) {
-               DPU_ERROR("invalid drm_device\n");
-               return rc;
-       }
-
        rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, true);
        if (rc) {
                DPU_ERROR("clock enable failed rc:%d\n", rc);
index 959d03e..c6169e7 100644 (file)
@@ -139,10 +139,6 @@ struct vsync_info {
 
 #define to_dpu_kms(x) container_of(x, struct dpu_kms, base)
 
-/* get struct msm_kms * from drm_device * */
-#define ddev_to_msm_kms(D) ((D) && (D)->dev_private ? \
-               ((struct msm_drm_private *)((D)->dev_private))->kms : NULL)
-
 /**
  * Debugfs functions - extra helper functions for debugfs support
  *
index 8d24b79..991f4c8 100644 (file)
@@ -154,10 +154,6 @@ void dpu_vbif_set_ot_limit(struct dpu_kms *dpu_kms,
        u32 ot_lim;
        int ret, i;
 
-       if (!dpu_kms) {
-               DPU_ERROR("invalid arguments\n");
-               return;
-       }
        mdp = dpu_kms->hw_mdp;
 
        for (i = 0; i < ARRAY_SIZE(dpu_kms->hw_vbif); i++) {
@@ -214,7 +210,7 @@ void dpu_vbif_set_qos_remap(struct dpu_kms *dpu_kms,
        const struct dpu_vbif_qos_tbl *qos_tbl;
        int i;
 
-       if (!dpu_kms || !params || !dpu_kms->hw_mdp) {
+       if (!params || !dpu_kms->hw_mdp) {
                DPU_ERROR("invalid arguments\n");
                return;
        }
index 50711cc..dda0543 100644 (file)
@@ -157,10 +157,6 @@ static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate,
        }
 }
 
-static const char * const iommu_ports[] = {
-       "mdp_port0_cb0", "mdp_port1_cb0",
-};
-
 static void mdp4_destroy(struct msm_kms *kms)
 {
        struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
@@ -172,8 +168,7 @@ static void mdp4_destroy(struct msm_kms *kms)
        drm_gem_object_put_unlocked(mdp4_kms->blank_cursor_bo);
 
        if (aspace) {
-               aspace->mmu->funcs->detach(aspace->mmu,
-                               iommu_ports, ARRAY_SIZE(iommu_ports));
+               aspace->mmu->funcs->detach(aspace->mmu);
                msm_gem_address_space_put(aspace);
        }
 
@@ -524,8 +519,7 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
 
                kms->aspace = aspace;
 
-               ret = aspace->mmu->funcs->attach(aspace->mmu, iommu_ports,
-                               ARRAY_SIZE(iommu_ports));
+               ret = aspace->mmu->funcs->attach(aspace->mmu);
                if (ret)
                        goto fail;
        } else {
index f6e71ff..1f48f64 100644 (file)
@@ -14,7 +14,7 @@ struct mdp5_cfg_handler {
 /* mdp5_cfg must be exposed (used in mdp5.xml.h) */
 const struct mdp5_cfg_hw *mdp5_cfg = NULL;
 
-const struct mdp5_cfg_hw msm8x74v1_config = {
+static const struct mdp5_cfg_hw msm8x74v1_config = {
        .name = "msm8x74v1",
        .mdp = {
                .count = 1,
@@ -98,7 +98,7 @@ const struct mdp5_cfg_hw msm8x74v1_config = {
        .max_clk = 200000000,
 };
 
-const struct mdp5_cfg_hw msm8x74v2_config = {
+static const struct mdp5_cfg_hw msm8x74v2_config = {
        .name = "msm8x74",
        .mdp = {
                .count = 1,
@@ -180,7 +180,7 @@ const struct mdp5_cfg_hw msm8x74v2_config = {
        .max_clk = 200000000,
 };
 
-const struct mdp5_cfg_hw apq8084_config = {
+static const struct mdp5_cfg_hw apq8084_config = {
        .name = "apq8084",
        .mdp = {
                .count = 1,
@@ -275,7 +275,7 @@ const struct mdp5_cfg_hw apq8084_config = {
        .max_clk = 320000000,
 };
 
-const struct mdp5_cfg_hw msm8x16_config = {
+static const struct mdp5_cfg_hw msm8x16_config = {
        .name = "msm8x16",
        .mdp = {
                .count = 1,
@@ -342,7 +342,7 @@ const struct mdp5_cfg_hw msm8x16_config = {
        .max_clk = 320000000,
 };
 
-const struct mdp5_cfg_hw msm8x94_config = {
+static const struct mdp5_cfg_hw msm8x94_config = {
        .name = "msm8x94",
        .mdp = {
                .count = 1,
@@ -437,7 +437,7 @@ const struct mdp5_cfg_hw msm8x94_config = {
        .max_clk = 400000000,
 };
 
-const struct mdp5_cfg_hw msm8x96_config = {
+static const struct mdp5_cfg_hw msm8x96_config = {
        .name = "msm8x96",
        .mdp = {
                .count = 1,
@@ -545,7 +545,104 @@ const struct mdp5_cfg_hw msm8x96_config = {
        .max_clk = 412500000,
 };
 
-const struct mdp5_cfg_hw msm8917_config = {
+const struct mdp5_cfg_hw msm8x76_config = {
+       .name = "msm8x76",
+       .mdp = {
+               .count = 1,
+               .caps = MDP_CAP_SMP |
+                       MDP_CAP_DSC |
+                       MDP_CAP_SRC_SPLIT |
+                       0,
+       },
+       .ctl = {
+               .count = 3,
+               .base = { 0x01000, 0x01200, 0x01400 },
+               .flush_hw_mask = 0xffffffff,
+       },
+       .smp = {
+               .mmb_count = 10,
+               .mmb_size = 10240,
+               .clients = {
+                       [SSPP_VIG0] = 1, [SSPP_VIG1] = 9,
+                       [SSPP_DMA0] = 4,
+                       [SSPP_RGB0] = 7, [SSPP_RGB1] = 8,
+               },
+       },
+       .pipe_vig = {
+               .count = 2,
+               .base = { 0x04000, 0x06000 },
+               .caps = MDP_PIPE_CAP_HFLIP      |
+                       MDP_PIPE_CAP_VFLIP      |
+                       MDP_PIPE_CAP_SCALE      |
+                       MDP_PIPE_CAP_CSC        |
+                       MDP_PIPE_CAP_DECIMATION |
+                       MDP_PIPE_CAP_SW_PIX_EXT |
+                       0,
+       },
+       .pipe_rgb = {
+               .count = 2,
+               .base = { 0x14000, 0x16000 },
+               .caps = MDP_PIPE_CAP_HFLIP      |
+                       MDP_PIPE_CAP_VFLIP      |
+                       MDP_PIPE_CAP_DECIMATION |
+                       MDP_PIPE_CAP_SW_PIX_EXT |
+                       0,
+       },
+       .pipe_dma = {
+               .count = 1,
+               .base = { 0x24000 },
+               .caps = MDP_PIPE_CAP_HFLIP      |
+                       MDP_PIPE_CAP_VFLIP      |
+                       MDP_PIPE_CAP_SW_PIX_EXT |
+                       0,
+       },
+       .pipe_cursor = {
+               .count = 1,
+               .base = { 0x440DC },
+               .caps = MDP_PIPE_CAP_HFLIP      |
+                       MDP_PIPE_CAP_VFLIP      |
+                       MDP_PIPE_CAP_SW_PIX_EXT |
+                       MDP_PIPE_CAP_CURSOR     |
+                       0,
+       },
+       .lm = {
+               .count = 2,
+               .base = { 0x44000, 0x45000 },
+               .instances = {
+                               { .id = 0, .pp = 0, .dspp = 0,
+                                 .caps = MDP_LM_CAP_DISPLAY, },
+                               { .id = 1, .pp = -1, .dspp = -1,
+                                 .caps = MDP_LM_CAP_WB },
+                            },
+               .nb_stages = 8,
+               .max_width = 2560,
+               .max_height = 0xFFFF,
+       },
+       .dspp = {
+               .count = 1,
+               .base = { 0x54000 },
+
+       },
+       .pp = {
+               .count = 3,
+               .base = { 0x70000, 0x70800, 0x72000 },
+       },
+       .dsc = {
+               .count = 2,
+               .base = { 0x80000, 0x80400 },
+       },
+       .intf = {
+               .base = { 0x6a000, 0x6a800, 0x6b000 },
+               .connect = {
+                       [0] = INTF_DISABLED,
+                       [1] = INTF_DSI,
+                       [2] = INTF_DSI,
+               },
+       },
+       .max_clk = 360000000,
+};
+
+static const struct mdp5_cfg_hw msm8917_config = {
        .name = "msm8917",
        .mdp = {
                .count = 1,
@@ -630,7 +727,7 @@ const struct mdp5_cfg_hw msm8917_config = {
        .max_clk = 320000000,
 };
 
-const struct mdp5_cfg_hw msm8998_config = {
+static const struct mdp5_cfg_hw msm8998_config = {
        .name = "msm8998",
        .mdp = {
                .count = 1,
@@ -745,6 +842,7 @@ static const struct mdp5_cfg_handler cfg_handlers_v1[] = {
        { .revision = 6, .config = { .hw = &msm8x16_config } },
        { .revision = 9, .config = { .hw = &msm8x94_config } },
        { .revision = 7, .config = { .hw = &msm8x96_config } },
+       { .revision = 11, .config = { .hw = &msm8x76_config } },
        { .revision = 15, .config = { .hw = &msm8917_config } },
 };
 
index eb0b4b7..05cc04f 100644 (file)
@@ -214,7 +214,6 @@ static void blend_setup(struct drm_crtc *crtc)
        struct mdp5_pipeline *pipeline = &mdp5_cstate->pipeline;
        struct mdp5_kms *mdp5_kms = get_kms(crtc);
        struct drm_plane *plane;
-       const struct mdp5_cfg_hw *hw_cfg;
        struct mdp5_plane_state *pstate, *pstates[STAGE_MAX + 1] = {NULL};
        const struct mdp_format *format;
        struct mdp5_hw_mixer *mixer = pipeline->mixer;
@@ -232,8 +231,6 @@ static void blend_setup(struct drm_crtc *crtc)
        u32 val;
 #define blender(stage) ((stage) - STAGE0)
 
-       hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg);
-
        spin_lock_irqsave(&mdp5_crtc->lm_lock, flags);
 
        /* ctl could be released already when we are shutting down: */
index 91cd76a..e43ecd4 100644 (file)
 #include "msm_mmu.h"
 #include "mdp5_kms.h"
 
-static const char *iommu_ports[] = {
-               "mdp_0",
-};
-
 static int mdp5_hw_init(struct msm_kms *kms)
 {
        struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
@@ -233,8 +229,7 @@ static void mdp5_kms_destroy(struct msm_kms *kms)
                mdp5_pipe_destroy(mdp5_kms->hwpipes[i]);
 
        if (aspace) {
-               aspace->mmu->funcs->detach(aspace->mmu,
-                               iommu_ports, ARRAY_SIZE(iommu_ports));
+               aspace->mmu->funcs->detach(aspace->mmu);
                msm_gem_address_space_put(aspace);
        }
 }
@@ -314,6 +309,10 @@ int mdp5_disable(struct mdp5_kms *mdp5_kms)
        mdp5_kms->enable_count--;
        WARN_ON(mdp5_kms->enable_count < 0);
 
+       if (mdp5_kms->tbu_rt_clk)
+               clk_disable_unprepare(mdp5_kms->tbu_rt_clk);
+       if (mdp5_kms->tbu_clk)
+               clk_disable_unprepare(mdp5_kms->tbu_clk);
        clk_disable_unprepare(mdp5_kms->ahb_clk);
        clk_disable_unprepare(mdp5_kms->axi_clk);
        clk_disable_unprepare(mdp5_kms->core_clk);
@@ -334,6 +333,10 @@ int mdp5_enable(struct mdp5_kms *mdp5_kms)
        clk_prepare_enable(mdp5_kms->core_clk);
        if (mdp5_kms->lut_clk)
                clk_prepare_enable(mdp5_kms->lut_clk);
+       if (mdp5_kms->tbu_clk)
+               clk_prepare_enable(mdp5_kms->tbu_clk);
+       if (mdp5_kms->tbu_rt_clk)
+               clk_prepare_enable(mdp5_kms->tbu_rt_clk);
 
        return 0;
 }
@@ -466,14 +469,11 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
 {
        struct drm_device *dev = mdp5_kms->dev;
        struct msm_drm_private *priv = dev->dev_private;
-       const struct mdp5_cfg_hw *hw_cfg;
        unsigned int num_crtcs;
        int i, ret, pi = 0, ci = 0;
        struct drm_plane *primary[MAX_BASES] = { NULL };
        struct drm_plane *cursor[MAX_BASES] = { NULL };
 
-       hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg);
-
        /*
         * Construct encoders and modeset initialize connector devices
         * for each external display interface.
@@ -737,8 +737,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
 
                kms->aspace = aspace;
 
-               ret = aspace->mmu->funcs->attach(aspace->mmu, iommu_ports,
-                               ARRAY_SIZE(iommu_ports));
+               ret = aspace->mmu->funcs->attach(aspace->mmu);
                if (ret) {
                        DRM_DEV_ERROR(&pdev->dev, "failed to attach iommu: %d\n",
                                ret);
@@ -974,6 +973,8 @@ static int mdp5_init(struct platform_device *pdev, struct drm_device *dev)
 
        /* optional clocks: */
        get_clk(pdev, &mdp5_kms->lut_clk, "lut", false);
+       get_clk(pdev, &mdp5_kms->tbu_clk, "tbu", false);
+       get_clk(pdev, &mdp5_kms->tbu_rt_clk, "tbu_rt", false);
 
        /* we need to set a default rate before enabling.  Set a safe
         * rate first, then figure out hw revision, and then set a
index d1bf4fd..1288667 100644 (file)
@@ -53,6 +53,8 @@ struct mdp5_kms {
        struct clk *ahb_clk;
        struct clk *core_clk;
        struct clk *lut_clk;
+       struct clk *tbu_clk;
+       struct clk *tbu_rt_clk;
        struct clk *vsync_clk;
 
        /*
index b31cfb5..d7fa2c4 100644 (file)
@@ -121,7 +121,6 @@ uint32_t mdp5_smp_calculate(struct mdp5_smp *smp,
        struct mdp5_kms *mdp5_kms = get_kms(smp);
        int rev = mdp5_cfg_get_hw_rev(mdp5_kms->cfg);
        int i, hsub, nplanes, nlines;
-       u32 fmt = format->base.pixel_format;
        uint32_t blkcfg = 0;
 
        nplanes = info->num_planes;
@@ -135,7 +134,6 @@ uint32_t mdp5_smp_calculate(struct mdp5_smp *smp,
         * them together, writes to SMP using a single client.
         */
        if ((rev > 0) && (format->chroma_sample > CHROMA_FULL)) {
-               fmt = DRM_FORMAT_NV24;
                nplanes = 2;
 
                /* if decimation is enabled, HW decimates less on the
index b7b7c1a..86ad3fd 100644 (file)
@@ -66,6 +66,26 @@ static const struct msm_dsi_config msm8916_dsi_cfg = {
        .num_dsi = 1,
 };
 
+static const char * const dsi_8976_bus_clk_names[] = {
+       "mdp_core", "iface", "bus",
+};
+
+static const struct msm_dsi_config msm8976_dsi_cfg = {
+       .io_offset = DSI_6G_REG_SHIFT,
+       .reg_cfg = {
+               .num = 3,
+               .regs = {
+                       {"gdsc", -1, -1},
+                       {"vdda", 100000, 100},  /* 1.2 V */
+                       {"vddio", 100000, 100}, /* 1.8 V */
+               },
+       },
+       .bus_clk_names = dsi_8976_bus_clk_names,
+       .num_bus_clks = ARRAY_SIZE(dsi_8976_bus_clk_names),
+       .io_start = { 0x1a94000, 0x1a96000 },
+       .num_dsi = 2,
+};
+
 static const struct msm_dsi_config msm8994_dsi_cfg = {
        .io_offset = DSI_6G_REG_SHIFT,
        .reg_cfg = {
@@ -147,7 +167,7 @@ static const struct msm_dsi_config sdm845_dsi_cfg = {
        .num_dsi = 2,
 };
 
-const static struct msm_dsi_host_cfg_ops msm_dsi_v2_host_ops = {
+static const struct msm_dsi_host_cfg_ops msm_dsi_v2_host_ops = {
        .link_clk_enable = dsi_link_clk_enable_v2,
        .link_clk_disable = dsi_link_clk_disable_v2,
        .clk_init_ver = dsi_clk_init_v2,
@@ -158,7 +178,7 @@ const static struct msm_dsi_host_cfg_ops msm_dsi_v2_host_ops = {
        .calc_clk_rate = dsi_calc_clk_rate_v2,
 };
 
-const static struct msm_dsi_host_cfg_ops msm_dsi_6g_host_ops = {
+static const struct msm_dsi_host_cfg_ops msm_dsi_6g_host_ops = {
        .link_clk_enable = dsi_link_clk_enable_6g,
        .link_clk_disable = dsi_link_clk_disable_6g,
        .clk_init_ver = NULL,
@@ -169,7 +189,7 @@ const static struct msm_dsi_host_cfg_ops msm_dsi_6g_host_ops = {
        .calc_clk_rate = dsi_calc_clk_rate_6g,
 };
 
-const static struct msm_dsi_host_cfg_ops msm_dsi_6g_v2_host_ops = {
+static const struct msm_dsi_host_cfg_ops msm_dsi_6g_v2_host_ops = {
        .link_clk_enable = dsi_link_clk_enable_6g,
        .link_clk_disable = dsi_link_clk_disable_6g,
        .clk_init_ver = dsi_clk_init_6g_v2,
@@ -197,6 +217,8 @@ static const struct msm_dsi_cfg_handler dsi_cfg_handlers[] = {
                &msm8916_dsi_cfg, &msm_dsi_6g_host_ops},
        {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_4_1,
                &msm8996_dsi_cfg, &msm_dsi_6g_host_ops},
+       {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_4_2,
+               &msm8976_dsi_cfg, &msm_dsi_6g_host_ops},
        {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_2_0,
                &msm8998_dsi_cfg, &msm_dsi_6g_v2_host_ops},
        {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_2_1,
index e2b7a7d..50a37ce 100644 (file)
@@ -17,6 +17,7 @@
 #define MSM_DSI_6G_VER_MINOR_V1_3      0x10030000
 #define MSM_DSI_6G_VER_MINOR_V1_3_1    0x10030001
 #define MSM_DSI_6G_VER_MINOR_V1_4_1    0x10040001
+#define MSM_DSI_6G_VER_MINOR_V1_4_2    0x10040002
 #define MSM_DSI_6G_VER_MINOR_V2_2_0    0x20000000
 #define MSM_DSI_6G_VER_MINOR_V2_2_1    0x20020001
 
index 1e7b1be..458cec8 100644 (file)
@@ -1293,14 +1293,13 @@ static int dsi_cmd_dma_tx(struct msm_dsi_host *msm_host, int len)
 static int dsi_cmd_dma_rx(struct msm_dsi_host *msm_host,
                        u8 *buf, int rx_byte, int pkt_size)
 {
-       u32 *lp, *temp, data;
+       u32 *temp, data;
        int i, j = 0, cnt;
        u32 read_cnt;
        u8 reg[16];
        int repeated_bytes = 0;
        int buf_offset = buf - msm_host->rx_buf;
 
-       lp = (u32 *)buf;
        temp = (u32 *)reg;
        cnt = (rx_byte + 3) >> 2;
        if (cnt > 4)
index 3522863..b0cfa67 100644 (file)
@@ -145,7 +145,7 @@ int msm_dsi_dphy_timing_calc_v2(struct msm_dsi_dphy_timing *timing,
 {
        const unsigned long bit_rate = clk_req->bitclk_rate;
        const unsigned long esc_rate = clk_req->escclk_rate;
-       s32 ui, ui_x8, lpx;
+       s32 ui, ui_x8;
        s32 tmax, tmin;
        s32 pcnt0 = 50;
        s32 pcnt1 = 50;
@@ -175,7 +175,6 @@ int msm_dsi_dphy_timing_calc_v2(struct msm_dsi_dphy_timing *timing,
 
        ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);
        ui_x8 = ui << 3;
-       lpx = mult_frac(NSEC_PER_MSEC, coeff, esc_rate / 1000);
 
        temp = S_DIV_ROUND_UP(38 * coeff - val_ckln * ui, ui_x8);
        tmin = max_t(s32, temp, 0);
@@ -262,7 +261,7 @@ int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing,
 {
        const unsigned long bit_rate = clk_req->bitclk_rate;
        const unsigned long esc_rate = clk_req->escclk_rate;
-       s32 ui, ui_x8, lpx;
+       s32 ui, ui_x8;
        s32 tmax, tmin;
        s32 pcnt0 = 50;
        s32 pcnt1 = 50;
@@ -284,7 +283,6 @@ int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing,
 
        ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);
        ui_x8 = ui << 3;
-       lpx = mult_frac(NSEC_PER_MSEC, coeff, esc_rate / 1000);
 
        temp = S_DIV_ROUND_UP(38 * coeff, ui_x8);
        tmin = max_t(s32, temp, 0);
@@ -485,6 +483,8 @@ static const struct of_device_id dsi_phy_dt_match[] = {
 #ifdef CONFIG_DRM_MSM_DSI_28NM_PHY
        { .compatible = "qcom,dsi-phy-28nm-hpm",
          .data = &dsi_phy_28nm_hpm_cfgs },
+       { .compatible = "qcom,dsi-phy-28nm-hpm-fam-b",
+         .data = &dsi_phy_28nm_hpm_famb_cfgs },
        { .compatible = "qcom,dsi-phy-28nm-lp",
          .data = &dsi_phy_28nm_lp_cfgs },
 #endif
index c4069ce..24b294e 100644 (file)
@@ -40,6 +40,7 @@ struct msm_dsi_phy_cfg {
 };
 
 extern const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs;
+extern const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_famb_cfgs;
 extern const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs;
 extern const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs;
 extern const struct msm_dsi_phy_cfg dsi_phy_28nm_8960_cfgs;
index b3f678f..c3c580c 100644 (file)
@@ -39,15 +39,10 @@ static void dsi_28nm_dphy_set_timing(struct msm_dsi_phy *phy,
                DSI_28nm_PHY_TIMING_CTRL_11_TRIG3_CMD(0));
 }
 
-static void dsi_28nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable)
+static void dsi_28nm_phy_regulator_enable_dcdc(struct msm_dsi_phy *phy)
 {
        void __iomem *base = phy->reg_base;
 
-       if (!enable) {
-               dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 0);
-               return;
-       }
-
        dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x0);
        dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 1);
        dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_5, 0);
@@ -56,6 +51,39 @@ static void dsi_28nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable)
        dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_1, 0x9);
        dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x7);
        dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_4, 0x20);
+       dsi_phy_write(phy->base + REG_DSI_28nm_PHY_LDO_CNTRL, 0x00);
+}
+
+static void dsi_28nm_phy_regulator_enable_ldo(struct msm_dsi_phy *phy)
+{
+       void __iomem *base = phy->reg_base;
+
+       dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x0);
+       dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 0);
+       dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_5, 0x7);
+       dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_3, 0);
+       dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_2, 0x1);
+       dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_1, 0x1);
+       dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_4, 0x20);
+
+       if (phy->cfg->type == MSM_DSI_PHY_28NM_LP)
+               dsi_phy_write(phy->base + REG_DSI_28nm_PHY_LDO_CNTRL, 0x05);
+       else
+               dsi_phy_write(phy->base + REG_DSI_28nm_PHY_LDO_CNTRL, 0x0d);
+}
+
+static void dsi_28nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable)
+{
+       if (!enable) {
+               dsi_phy_write(phy->reg_base +
+                             REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 0);
+               return;
+       }
+
+       if (phy->regulator_ldo_mode)
+               dsi_28nm_phy_regulator_enable_ldo(phy);
+       else
+               dsi_28nm_phy_regulator_enable_dcdc(phy);
 }
 
 static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id,
@@ -77,8 +105,6 @@ static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id,
 
        dsi_28nm_phy_regulator_ctrl(phy, true);
 
-       dsi_phy_write(base + REG_DSI_28nm_PHY_LDO_CNTRL, 0x00);
-
        dsi_28nm_dphy_set_timing(phy, timing);
 
        dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_1, 0x00);
@@ -142,6 +168,24 @@ const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs = {
        .num_dsi_phy = 2,
 };
 
+const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_famb_cfgs = {
+       .type = MSM_DSI_PHY_28NM_HPM,
+       .src_pll_truthtable = { {true, true}, {false, true} },
+       .reg_cfg = {
+               .num = 1,
+               .regs = {
+                       {"vddio", 100000, 100},
+               },
+       },
+       .ops = {
+               .enable = dsi_28nm_phy_enable,
+               .disable = dsi_28nm_phy_disable,
+               .init = msm_dsi_phy_init_common,
+       },
+       .io_start = { 0x1a94400, 0x1a96400 },
+       .num_dsi_phy = 2,
+};
+
 const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs = {
        .type = MSM_DSI_PHY_28NM_LP,
        .src_pll_truthtable = { {true, true}, {true, true} },
index 1697e61..8a38d4b 100644 (file)
@@ -29,8 +29,12 @@ static int msm_hdmi_phy_resource_init(struct hdmi_phy *phy)
                reg = devm_regulator_get(dev, cfg->reg_names[i]);
                if (IS_ERR(reg)) {
                        ret = PTR_ERR(reg);
-                       DRM_DEV_ERROR(dev, "failed to get phy regulator: %s (%d)\n",
-                               cfg->reg_names[i], ret);
+                       if (ret != -EPROBE_DEFER) {
+                               DRM_DEV_ERROR(dev,
+                                             "failed to get phy regulator: %s (%d)\n",
+                                             cfg->reg_names[i], ret);
+                       }
+
                        return ret;
                }
 
index a052364..18f3a5c 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/pm_opp.h>
 #include <linux/devfreq.h>
 #include <linux/devcoredump.h>
+#include <linux/sched/task.h>
 
 /*
  * Power Management:
@@ -838,7 +839,7 @@ msm_gpu_create_address_space(struct msm_gpu *gpu, struct platform_device *pdev,
                return ERR_CAST(aspace);
        }
 
-       ret = aspace->mmu->funcs->attach(aspace->mmu, NULL, 0);
+       ret = aspace->mmu->funcs->attach(aspace->mmu);
        if (ret) {
                msm_gem_address_space_put(aspace);
                return ERR_PTR(ret);
@@ -995,8 +996,7 @@ void msm_gpu_cleanup(struct msm_gpu *gpu)
        msm_gem_kernel_put(gpu->memptrs_bo, gpu->aspace, false);
 
        if (!IS_ERR_OR_NULL(gpu->aspace)) {
-               gpu->aspace->mmu->funcs->detach(gpu->aspace->mmu,
-                       NULL, 0);
+               gpu->aspace->mmu->funcs->detach(gpu->aspace->mmu);
                msm_gem_address_space_put(gpu->aspace);
        }
 }
index 34f643a..34980d8 100644 (file)
@@ -21,14 +21,12 @@ struct msm_gpummu {
 #define GPUMMU_PAGE_SIZE SZ_4K
 #define TABLE_SIZE (sizeof(uint32_t) * GPUMMU_VA_RANGE / GPUMMU_PAGE_SIZE)
 
-static int msm_gpummu_attach(struct msm_mmu *mmu, const char * const *names,
-               int cnt)
+static int msm_gpummu_attach(struct msm_mmu *mmu)
 {
        return 0;
 }
 
-static void msm_gpummu_detach(struct msm_mmu *mmu, const char * const *names,
-               int cnt)
+static void msm_gpummu_detach(struct msm_mmu *mmu)
 {
 }
 
index 8c95c31..ad58cfe 100644 (file)
@@ -23,16 +23,14 @@ static int msm_fault_handler(struct iommu_domain *domain, struct device *dev,
        return 0;
 }
 
-static int msm_iommu_attach(struct msm_mmu *mmu, const char * const *names,
-                           int cnt)
+static int msm_iommu_attach(struct msm_mmu *mmu)
 {
        struct msm_iommu *iommu = to_msm_iommu(mmu);
 
        return iommu_attach_device(iommu->domain, mmu->dev);
 }
 
-static void msm_iommu_detach(struct msm_mmu *mmu, const char * const *names,
-                            int cnt)
+static void msm_iommu_detach(struct msm_mmu *mmu)
 {
        struct msm_iommu *iommu = to_msm_iommu(mmu);
 
index 871d563..67a623f 100644 (file)
@@ -10,8 +10,8 @@
 #include <linux/iommu.h>
 
 struct msm_mmu_funcs {
-       int (*attach)(struct msm_mmu *mmu, const char * const *names, int cnt);
-       void (*detach)(struct msm_mmu *mmu, const char * const *names, int cnt);
+       int (*attach)(struct msm_mmu *mmu);
+       void (*detach)(struct msm_mmu *mmu);
        int (*map)(struct msm_mmu *mmu, uint64_t iova, struct sg_table *sgt,
                        unsigned len, int prot);
        int (*unmap)(struct msm_mmu *mmu, uint64_t iova, unsigned len);
index c7832a9..af7ceb2 100644 (file)
@@ -298,7 +298,7 @@ void msm_rd_debugfs_cleanup(struct msm_drm_private *priv)
 
 static void snapshot_buf(struct msm_rd_state *rd,
                struct msm_gem_submit *submit, int idx,
-               uint64_t iova, uint32_t size)
+               uint64_t iova, uint32_t size, bool full)
 {
        struct msm_gem_object *obj = submit->bos[idx].obj;
        unsigned offset = 0;
@@ -318,6 +318,9 @@ static void snapshot_buf(struct msm_rd_state *rd,
        rd_write_section(rd, RD_GPUADDR,
                        (uint32_t[3]){ iova, size, iova >> 32 }, 12);
 
+       if (!full)
+               return;
+
        /* But only dump the contents of buffers marked READ */
        if (!(submit->bos[idx].flags & MSM_SUBMIT_BO_READ))
                return;
@@ -381,18 +384,21 @@ void msm_rd_dump_submit(struct msm_rd_state *rd, struct msm_gem_submit *submit,
        rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4));
 
        for (i = 0; i < submit->nr_bos; i++)
-               if (should_dump(submit, i))
-                       snapshot_buf(rd, submit, i, 0, 0);
+               snapshot_buf(rd, submit, i, 0, 0, should_dump(submit, i));
 
        for (i = 0; i < submit->nr_cmds; i++) {
-               uint64_t iova = submit->cmd[i].iova;
                uint32_t szd  = submit->cmd[i].size; /* in dwords */
 
                /* snapshot cmdstream bo's (if we haven't already): */
                if (!should_dump(submit, i)) {
                        snapshot_buf(rd, submit, submit->cmd[i].idx,
-                                       submit->cmd[i].iova, szd * 4);
+                                       submit->cmd[i].iova, szd * 4, true);
                }
+       }
+
+       for (i = 0; i < submit->nr_cmds; i++) {
+               uint64_t iova = submit->cmd[i].iova;
+               uint32_t szd  = submit->cmd[i].size; /* in dwords */
 
                switch (submit->cmd[i].type) {
                case MSM_SUBMIT_CMD_IB_TARGET_BUF:
index e518d93..d08ae95 100644 (file)
@@ -843,9 +843,13 @@ fail:
  */
 static void omap_gem_unpin_locked(struct drm_gem_object *obj)
 {
+       struct omap_drm_private *priv = obj->dev->dev_private;
        struct omap_gem_object *omap_obj = to_omap_bo(obj);
        int ret;
 
+       if (omap_gem_is_contiguous(omap_obj) || !priv->has_dmm)
+               return;
+
        if (refcount_dec_and_test(&omap_obj->dma_addr_cnt)) {
                ret = tiler_unpin(omap_obj->block);
                if (ret) {
index 7089dfc..110fb38 100644 (file)
@@ -1826,8 +1826,8 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
                        track->textures[i].use_pitch = 1;
                } else {
                        track->textures[i].use_pitch = 0;
-                       track->textures[i].width = 1 << ((idx_value >> RADEON_TXFORMAT_WIDTH_SHIFT) & RADEON_TXFORMAT_WIDTH_MASK);
-                       track->textures[i].height = 1 << ((idx_value >> RADEON_TXFORMAT_HEIGHT_SHIFT) & RADEON_TXFORMAT_HEIGHT_MASK);
+                       track->textures[i].width = 1 << ((idx_value & RADEON_TXFORMAT_WIDTH_MASK) >> RADEON_TXFORMAT_WIDTH_SHIFT);
+                       track->textures[i].height = 1 << ((idx_value & RADEON_TXFORMAT_HEIGHT_MASK) >> RADEON_TXFORMAT_HEIGHT_SHIFT);
                }
                if (idx_value & RADEON_TXFORMAT_CUBIC_MAP_ENABLE)
                        track->textures[i].tex_coord_type = 2;
index 8404014..f5f2ffe 100644 (file)
@@ -476,8 +476,8 @@ int r200_packet0_check(struct radeon_cs_parser *p,
                        track->textures[i].use_pitch = 1;
                } else {
                        track->textures[i].use_pitch = 0;
-                       track->textures[i].width = 1 << ((idx_value >> RADEON_TXFORMAT_WIDTH_SHIFT) & RADEON_TXFORMAT_WIDTH_MASK);
-                       track->textures[i].height = 1 << ((idx_value >> RADEON_TXFORMAT_HEIGHT_SHIFT) & RADEON_TXFORMAT_HEIGHT_MASK);
+                       track->textures[i].width = 1 << ((idx_value & RADEON_TXFORMAT_WIDTH_MASK) >> RADEON_TXFORMAT_WIDTH_SHIFT);
+                       track->textures[i].height = 1 << ((idx_value & RADEON_TXFORMAT_HEIGHT_MASK) >> RADEON_TXFORMAT_HEIGHT_SHIFT);
                }
                if (idx_value & R200_TXFORMAT_LOOKUP_DISABLE)
                        track->textures[i].lookup_disable = true;
index 5b1f9ff..714af05 100644 (file)
@@ -837,16 +837,15 @@ static int tegra_cursor_atomic_check(struct drm_plane *plane,
 static void tegra_cursor_atomic_update(struct drm_plane *plane,
                                       struct drm_plane_state *old_state)
 {
-       struct tegra_bo *bo = tegra_fb_get_plane(plane->state->fb, 0);
+       struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
        struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
-       struct drm_plane_state *state = plane->state;
        u32 value = CURSOR_CLIP_DISPLAY;
 
        /* rien ne va plus */
        if (!plane->state->crtc || !plane->state->fb)
                return;
 
-       switch (state->crtc_w) {
+       switch (plane->state->crtc_w) {
        case 32:
                value |= CURSOR_SIZE_32x32;
                break;
@@ -864,16 +863,16 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
                break;
 
        default:
-               WARN(1, "cursor size %ux%u not supported\n", state->crtc_w,
-                    state->crtc_h);
+               WARN(1, "cursor size %ux%u not supported\n",
+                    plane->state->crtc_w, plane->state->crtc_h);
                return;
        }
 
-       value |= (bo->iova >> 10) & 0x3fffff;
+       value |= (state->iova[0] >> 10) & 0x3fffff;
        tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);
 
 #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
-       value = (bo->iova >> 32) & 0x3;
+       value = (state->iova[0] >> 32) & 0x3;
        tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
 #endif
 
@@ -892,7 +891,8 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
        tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
 
        /* position the cursor */
-       value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff);
+       value = (plane->state->crtc_y & 0x3fff) << 16 |
+               (plane->state->crtc_x & 0x3fff);
        tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
 }
 
@@ -2017,7 +2017,7 @@ static int tegra_dc_init(struct host1x_client *client)
                dev_warn(dc->dev, "failed to allocate syncpoint\n");
 
        err = host1x_client_iommu_attach(client);
-       if (err < 0) {
+       if (err < 0 && err != -ENODEV) {
                dev_err(client->dev, "failed to attach to domain: %d\n", err);
                return err;
        }
index 56e5e7a..f455ce7 100644 (file)
@@ -920,10 +920,8 @@ int host1x_client_iommu_attach(struct host1x_client *client)
 
        if (tegra->domain) {
                group = iommu_group_get(client->dev);
-               if (!group) {
-                       dev_err(client->dev, "failed to get IOMMU group\n");
+               if (!group)
                        return -ENODEV;
-               }
 
                if (domain != tegra->domain) {
                        err = iommu_attach_group(tegra->domain, group);
@@ -1243,6 +1241,9 @@ static int host1x_drm_remove(struct host1x_device *dev)
        drm_atomic_helper_shutdown(drm);
        drm_mode_config_cleanup(drm);
 
+       if (tegra->hub)
+               tegra_display_hub_cleanup(tegra->hub);
+
        err = host1x_device_exit(dev);
        if (err < 0)
                dev_err(&dev->dev, "host1x device cleanup failed: %d\n", err);
index 746dae3..bc15b43 100644 (file)
@@ -27,6 +27,29 @@ static void tegra_bo_put(struct host1x_bo *bo)
        drm_gem_object_put_unlocked(&obj->gem);
 }
 
+/* XXX move this into lib/scatterlist.c? */
+static int sg_alloc_table_from_sg(struct sg_table *sgt, struct scatterlist *sg,
+                                 unsigned int nents, gfp_t gfp_mask)
+{
+       struct scatterlist *dst;
+       unsigned int i;
+       int err;
+
+       err = sg_alloc_table(sgt, nents, gfp_mask);
+       if (err < 0)
+               return err;
+
+       dst = sgt->sgl;
+
+       for (i = 0; i < nents; i++) {
+               sg_set_page(dst, sg_page(sg), sg->length, 0);
+               dst = sg_next(dst);
+               sg = sg_next(sg);
+       }
+
+       return 0;
+}
+
 static struct sg_table *tegra_bo_pin(struct device *dev, struct host1x_bo *bo,
                                     dma_addr_t *phys)
 {
@@ -52,11 +75,31 @@ static struct sg_table *tegra_bo_pin(struct device *dev, struct host1x_bo *bo,
                return ERR_PTR(-ENOMEM);
 
        if (obj->pages) {
+               /*
+                * If the buffer object was allocated from the explicit IOMMU
+                * API code paths, construct an SG table from the pages.
+                */
                err = sg_alloc_table_from_pages(sgt, obj->pages, obj->num_pages,
                                                0, obj->gem.size, GFP_KERNEL);
                if (err < 0)
                        goto free;
+       } else if (obj->sgt) {
+               /*
+                * If the buffer object already has an SG table but no pages
+                * were allocated for it, it means the buffer was imported and
+                * the SG table needs to be copied to avoid overwriting any
+                * other potential users of the original SG table.
+                */
+               err = sg_alloc_table_from_sg(sgt, obj->sgt->sgl, obj->sgt->nents,
+                                            GFP_KERNEL);
+               if (err < 0)
+                       goto free;
        } else {
+               /*
+                * If the buffer object had no pages allocated and if it was
+                * not imported, it had to be allocated with the DMA API, so
+                * the DMA API helper can be used.
+                */
                err = dma_get_sgtable(dev, sgt, obj->vaddr, obj->iova,
                                      obj->gem.size);
                if (err < 0)
@@ -397,13 +440,6 @@ static struct tegra_bo *tegra_bo_import(struct drm_device *drm,
                err = tegra_bo_iommu_map(tegra, bo);
                if (err < 0)
                        goto detach;
-       } else {
-               if (bo->sgt->nents > 1) {
-                       err = -EINVAL;
-                       goto detach;
-               }
-
-               bo->iova = sg_dma_address(bo->sgt->sgl);
        }
 
        bo->gem.import_attach = attach;
index 2b4082d..47d985a 100644 (file)
@@ -605,11 +605,8 @@ static struct tegra_display_hub_state *
 tegra_display_hub_get_state(struct tegra_display_hub *hub,
                            struct drm_atomic_state *state)
 {
-       struct drm_device *drm = dev_get_drvdata(hub->client.parent);
        struct drm_private_state *priv;
 
-       WARN_ON(!drm_modeset_is_locked(&drm->mode_config.connection_mutex));
-
        priv = drm_atomic_get_private_obj_state(state, &hub->base);
        if (IS_ERR(priv))
                return ERR_CAST(priv);
index 163b590..cadcdd9 100644 (file)
@@ -129,6 +129,17 @@ static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state)
                                goto unpin;
                        }
 
+                       /*
+                        * The display controller needs contiguous memory, so
+                        * fail if the buffer is discontiguous and we fail to
+                        * map its SG table to a single contiguous chunk of
+                        * I/O virtual memory.
+                        */
+                       if (err > 1) {
+                               err = -EINVAL;
+                               goto unpin;
+                       }
+
                        state->iova[i] = sg_dma_address(sgt->sgl);
                        state->sgt[i] = sgt;
                } else {
index 615cb31..a68d3b3 100644 (file)
@@ -3912,8 +3912,7 @@ static int tegra_sor_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int tegra_sor_suspend(struct device *dev)
+static int tegra_sor_runtime_suspend(struct device *dev)
 {
        struct tegra_sor *sor = dev_get_drvdata(dev);
        int err;
@@ -3935,7 +3934,7 @@ static int tegra_sor_suspend(struct device *dev)
        return 0;
 }
 
-static int tegra_sor_resume(struct device *dev)
+static int tegra_sor_runtime_resume(struct device *dev)
 {
        struct tegra_sor *sor = dev_get_drvdata(dev);
        int err;
@@ -3967,10 +3966,39 @@ static int tegra_sor_resume(struct device *dev)
 
        return 0;
 }
-#endif
+
+static int tegra_sor_suspend(struct device *dev)
+{
+       struct tegra_sor *sor = dev_get_drvdata(dev);
+       int err;
+
+       if (sor->hdmi_supply) {
+               err = regulator_disable(sor->hdmi_supply);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int tegra_sor_resume(struct device *dev)
+{
+       struct tegra_sor *sor = dev_get_drvdata(dev);
+       int err;
+
+       if (sor->hdmi_supply) {
+               err = regulator_enable(sor->hdmi_supply);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
 
 static const struct dev_pm_ops tegra_sor_pm_ops = {
-       SET_RUNTIME_PM_OPS(tegra_sor_suspend, tegra_sor_resume, NULL)
+       SET_RUNTIME_PM_OPS(tegra_sor_runtime_suspend, tegra_sor_runtime_resume,
+                          NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(tegra_sor_suspend, tegra_sor_resume)
 };
 
 struct platform_driver tegra_sor_driver = {
index 9444ba1..3526c28 100644 (file)
@@ -167,7 +167,7 @@ static int vic_init(struct host1x_client *client)
        int err;
 
        err = host1x_client_iommu_attach(client);
-       if (err < 0) {
+       if (err < 0 && err != -ENODEV) {
                dev_err(vic->dev, "failed to attach to domain: %d\n", err);
                return err;
        }
@@ -386,13 +386,14 @@ static const struct vic_config vic_t194_config = {
        .supports_sid = true,
 };
 
-static const struct of_device_id vic_match[] = {
+static const struct of_device_id tegra_vic_of_match[] = {
        { .compatible = "nvidia,tegra124-vic", .data = &vic_t124_config },
        { .compatible = "nvidia,tegra210-vic", .data = &vic_t210_config },
        { .compatible = "nvidia,tegra186-vic", .data = &vic_t186_config },
        { .compatible = "nvidia,tegra194-vic", .data = &vic_t194_config },
        { },
 };
+MODULE_DEVICE_TABLE(of, tegra_vic_of_match);
 
 static int vic_probe(struct platform_device *pdev)
 {
@@ -516,7 +517,7 @@ static const struct dev_pm_ops vic_pm_ops = {
 struct platform_driver tegra_vic_driver = {
        .driver = {
                .name = "tegra-vic",
-               .of_match_table = vic_match,
+               .of_match_table = tegra_vic_of_match,
                .pm = &vic_pm_ops
        },
        .probe = vic_probe,
index 2ae0c19..0a2cc19 100644 (file)
@@ -1954,12 +1954,14 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
        /*
         * For a zoned target, the number of zones should be updated for the
         * correct value to be exposed in sysfs queue/nr_zones. For a BIO based
-        * target, this is all that is needed. For a request based target, the
-        * queue zone bitmaps must also be updated.
-        * Use blk_revalidate_disk_zones() to handle this.
+        * target, this is all that is needed.
         */
-       if (blk_queue_is_zoned(q))
-               blk_revalidate_disk_zones(t->md->disk);
+#ifdef CONFIG_BLK_DEV_ZONED
+       if (blk_queue_is_zoned(q)) {
+               WARN_ON_ONCE(queue_is_mq(q));
+               q->nr_zones = blkdev_nr_zones(t->md->disk);
+       }
+#endif
 
        /* Allow reads to exceed readahead limits */
        q->backing_dev_info->io_pages = limits->max_sectors >> (PAGE_SHIFT - 9);
index 4574e0d..70a1063 100644 (file)
@@ -727,7 +727,7 @@ static int dmz_get_zoned_device(struct dm_target *ti, char *path)
        dev->zone_nr_blocks = dmz_sect2blk(dev->zone_nr_sectors);
        dev->zone_nr_blocks_shift = ilog2(dev->zone_nr_blocks);
 
-       dev->nr_zones = blkdev_nr_zones(dev->bdev);
+       dev->nr_zones = blkdev_nr_zones(dev->bdev->bd_disk);
 
        dmz->dev = dev;
 
index 0e5ede4..27d72c1 100644 (file)
@@ -412,8 +412,6 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf)
                goto err;
 
        /* The drive satisfies the kernel restrictions: set it up */
-       blk_queue_chunk_sectors(sdkp->disk->queue,
-                       logical_to_sectors(sdkp->device, zone_blocks));
        blk_queue_flag_set(QUEUE_FLAG_ZONE_RESETALL, sdkp->disk->queue);
        blk_queue_required_elevator_features(sdkp->disk->queue,
                                             ELEVATOR_F_ZBD_SEQ_WRITE);
index c6df8b4..79d8265 100644 (file)
@@ -66,6 +66,16 @@ config QCOM_MDT_LOADER
        tristate
        select QCOM_SCM
 
+config QCOM_OCMEM
+       tristate "Qualcomm On Chip Memory (OCMEM) driver"
+       depends on ARCH_QCOM
+       select QCOM_SCM
+       help
+          The On Chip Memory (OCMEM) allocator allows various clients to
+          allocate memory from OCMEM based on performance, latency and power
+          requirements. This is typically used by the GPU, camera/video, and
+          audio components on some Snapdragon SoCs.
+
 config QCOM_PM
        bool "Qualcomm Power Management"
        depends on ARCH_QCOM && !ARM64
index 2559fe9..9fb35c8 100644 (file)
@@ -6,6 +6,7 @@ obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
 obj-$(CONFIG_QCOM_GLINK_SSR) +=        glink_ssr.o
 obj-$(CONFIG_QCOM_GSBI)        +=      qcom_gsbi.o
 obj-$(CONFIG_QCOM_MDT_LOADER)  += mdt_loader.o
+obj-$(CONFIG_QCOM_OCMEM)       += ocmem.o
 obj-$(CONFIG_QCOM_PM)  +=      spm.o
 obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o
 qmi_helpers-y  += qmi_encdec.o qmi_interface.o
diff --git a/drivers/soc/qcom/ocmem.c b/drivers/soc/qcom/ocmem.c
new file mode 100644 (file)
index 0000000..7f9e994
--- /dev/null
@@ -0,0 +1,433 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * The On Chip Memory (OCMEM) allocator allows various clients to allocate
+ * memory from OCMEM based on performance, latency and power requirements.
+ * This is typically used by the GPU, camera/video, and audio components on
+ * some Snapdragon SoCs.
+ *
+ * Copyright (C) 2019 Brian Masney <masneyb@onstation.org>
+ * Copyright (C) 2015 Red Hat. Author: Rob Clark <robdclark@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/qcom_scm.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <soc/qcom/ocmem.h>
+
+enum region_mode {
+       WIDE_MODE = 0x0,
+       THIN_MODE,
+       MODE_DEFAULT = WIDE_MODE,
+};
+
+enum ocmem_macro_state {
+       PASSTHROUGH = 0,
+       PERI_ON = 1,
+       CORE_ON = 2,
+       CLK_OFF = 4,
+};
+
+struct ocmem_region {
+       bool interleaved;
+       enum region_mode mode;
+       unsigned int num_macros;
+       enum ocmem_macro_state macro_state[4];
+       unsigned long macro_size;
+       unsigned long region_size;
+};
+
+struct ocmem_config {
+       uint8_t num_regions;
+       unsigned long macro_size;
+};
+
+struct ocmem {
+       struct device *dev;
+       const struct ocmem_config *config;
+       struct resource *memory;
+       void __iomem *mmio;
+       unsigned int num_ports;
+       unsigned int num_macros;
+       bool interleaved;
+       struct ocmem_region *regions;
+       unsigned long active_allocations;
+};
+
+#define OCMEM_MIN_ALIGN                                SZ_64K
+#define OCMEM_MIN_ALLOC                                SZ_64K
+
+#define OCMEM_REG_HW_VERSION                   0x00000000
+#define OCMEM_REG_HW_PROFILE                   0x00000004
+
+#define OCMEM_REG_REGION_MODE_CTL              0x00001000
+#define OCMEM_REGION_MODE_CTL_REG0_THIN                0x00000001
+#define OCMEM_REGION_MODE_CTL_REG1_THIN                0x00000002
+#define OCMEM_REGION_MODE_CTL_REG2_THIN                0x00000004
+#define OCMEM_REGION_MODE_CTL_REG3_THIN                0x00000008
+
+#define OCMEM_REG_GFX_MPU_START                        0x00001004
+#define OCMEM_REG_GFX_MPU_END                  0x00001008
+
+#define OCMEM_HW_PROFILE_NUM_PORTS(val)                FIELD_PREP(0x0000000f, (val))
+#define OCMEM_HW_PROFILE_NUM_MACROS(val)       FIELD_PREP(0x00003f00, (val))
+
+#define OCMEM_HW_PROFILE_LAST_REGN_HALFSIZE    0x00010000
+#define OCMEM_HW_PROFILE_INTERLEAVING          0x00020000
+#define OCMEM_REG_GEN_STATUS                   0x0000000c
+
+#define OCMEM_REG_PSGSC_STATUS                 0x00000038
+#define OCMEM_REG_PSGSC_CTL(i0)                        (0x0000003c + 0x1*(i0))
+
+#define OCMEM_PSGSC_CTL_MACRO0_MODE(val)       FIELD_PREP(0x00000007, (val))
+#define OCMEM_PSGSC_CTL_MACRO1_MODE(val)       FIELD_PREP(0x00000070, (val))
+#define OCMEM_PSGSC_CTL_MACRO2_MODE(val)       FIELD_PREP(0x00000700, (val))
+#define OCMEM_PSGSC_CTL_MACRO3_MODE(val)       FIELD_PREP(0x00007000, (val))
+
+#define OCMEM_CLK_CORE_IDX                     0
+static struct clk_bulk_data ocmem_clks[] = {
+       {
+               .id = "core",
+       },
+       {
+               .id = "iface",
+       },
+};
+
+static inline void ocmem_write(struct ocmem *ocmem, u32 reg, u32 data)
+{
+       writel(data, ocmem->mmio + reg);
+}
+
+static inline u32 ocmem_read(struct ocmem *ocmem, u32 reg)
+{
+       return readl(ocmem->mmio + reg);
+}
+
+static void update_ocmem(struct ocmem *ocmem)
+{
+       uint32_t region_mode_ctrl = 0x0;
+       int i;
+
+       if (!qcom_scm_ocmem_lock_available()) {
+               for (i = 0; i < ocmem->config->num_regions; i++) {
+                       struct ocmem_region *region = &ocmem->regions[i];
+
+                       if (region->mode == THIN_MODE)
+                               region_mode_ctrl |= BIT(i);
+               }
+
+               dev_dbg(ocmem->dev, "ocmem_region_mode_control %x\n",
+                       region_mode_ctrl);
+               ocmem_write(ocmem, OCMEM_REG_REGION_MODE_CTL, region_mode_ctrl);
+       }
+
+       for (i = 0; i < ocmem->config->num_regions; i++) {
+               struct ocmem_region *region = &ocmem->regions[i];
+               u32 data;
+
+               data = OCMEM_PSGSC_CTL_MACRO0_MODE(region->macro_state[0]) |
+                       OCMEM_PSGSC_CTL_MACRO1_MODE(region->macro_state[1]) |
+                       OCMEM_PSGSC_CTL_MACRO2_MODE(region->macro_state[2]) |
+                       OCMEM_PSGSC_CTL_MACRO3_MODE(region->macro_state[3]);
+
+               ocmem_write(ocmem, OCMEM_REG_PSGSC_CTL(i), data);
+       }
+}
+
+static unsigned long phys_to_offset(struct ocmem *ocmem,
+                                   unsigned long addr)
+{
+       if (addr < ocmem->memory->start || addr >= ocmem->memory->end)
+               return 0;
+
+       return addr - ocmem->memory->start;
+}
+
+static unsigned long device_address(struct ocmem *ocmem,
+                                   enum ocmem_client client,
+                                   unsigned long addr)
+{
+       WARN_ON(client != OCMEM_GRAPHICS);
+
+       /* TODO: gpu uses phys_to_offset, but others do not.. */
+       return phys_to_offset(ocmem, addr);
+}
+
+static void update_range(struct ocmem *ocmem, struct ocmem_buf *buf,
+                        enum ocmem_macro_state mstate, enum region_mode rmode)
+{
+       unsigned long offset = 0;
+       int i, j;
+
+       for (i = 0; i < ocmem->config->num_regions; i++) {
+               struct ocmem_region *region = &ocmem->regions[i];
+
+               if (buf->offset <= offset && offset < buf->offset + buf->len)
+                       region->mode = rmode;
+
+               for (j = 0; j < region->num_macros; j++) {
+                       if (buf->offset <= offset &&
+                           offset < buf->offset + buf->len)
+                               region->macro_state[j] = mstate;
+
+                       offset += region->macro_size;
+               }
+       }
+
+       update_ocmem(ocmem);
+}
+
+struct ocmem *of_get_ocmem(struct device *dev)
+{
+       struct platform_device *pdev;
+       struct device_node *devnode;
+
+       devnode = of_parse_phandle(dev->of_node, "sram", 0);
+       if (!devnode || !devnode->parent) {
+               dev_err(dev, "Cannot look up sram phandle\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       pdev = of_find_device_by_node(devnode->parent);
+       if (!pdev) {
+               dev_err(dev, "Cannot find device node %s\n", devnode->name);
+               return ERR_PTR(-EPROBE_DEFER);
+       }
+
+       return platform_get_drvdata(pdev);
+}
+EXPORT_SYMBOL(of_get_ocmem);
+
+struct ocmem_buf *ocmem_allocate(struct ocmem *ocmem, enum ocmem_client client,
+                                unsigned long size)
+{
+       struct ocmem_buf *buf;
+       int ret;
+
+       /* TODO: add support for other clients... */
+       if (WARN_ON(client != OCMEM_GRAPHICS))
+               return ERR_PTR(-ENODEV);
+
+       if (size < OCMEM_MIN_ALLOC || !IS_ALIGNED(size, OCMEM_MIN_ALIGN))
+               return ERR_PTR(-EINVAL);
+
+       if (test_and_set_bit_lock(BIT(client), &ocmem->active_allocations))
+               return ERR_PTR(-EBUSY);
+
+       buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto err_unlock;
+       }
+
+       buf->offset = 0;
+       buf->addr = device_address(ocmem, client, buf->offset);
+       buf->len = size;
+
+       update_range(ocmem, buf, CORE_ON, WIDE_MODE);
+
+       if (qcom_scm_ocmem_lock_available()) {
+               ret = qcom_scm_ocmem_lock(QCOM_SCM_OCMEM_GRAPHICS_ID,
+                                         buf->offset, buf->len, WIDE_MODE);
+               if (ret) {
+                       dev_err(ocmem->dev, "could not lock: %d\n", ret);
+                       ret = -EINVAL;
+                       goto err_kfree;
+               }
+       } else {
+               ocmem_write(ocmem, OCMEM_REG_GFX_MPU_START, buf->offset);
+               ocmem_write(ocmem, OCMEM_REG_GFX_MPU_END,
+                           buf->offset + buf->len);
+       }
+
+       dev_dbg(ocmem->dev, "using %ldK of OCMEM at 0x%08lx for client %d\n",
+               size / 1024, buf->addr, client);
+
+       return buf;
+
+err_kfree:
+       kfree(buf);
+err_unlock:
+       clear_bit_unlock(BIT(client), &ocmem->active_allocations);
+
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(ocmem_allocate);
+
+void ocmem_free(struct ocmem *ocmem, enum ocmem_client client,
+               struct ocmem_buf *buf)
+{
+       /* TODO: add support for other clients... */
+       if (WARN_ON(client != OCMEM_GRAPHICS))
+               return;
+
+       update_range(ocmem, buf, CLK_OFF, MODE_DEFAULT);
+
+       if (qcom_scm_ocmem_lock_available()) {
+               int ret;
+
+               ret = qcom_scm_ocmem_unlock(QCOM_SCM_OCMEM_GRAPHICS_ID,
+                                           buf->offset, buf->len);
+               if (ret)
+                       dev_err(ocmem->dev, "could not unlock: %d\n", ret);
+       } else {
+               ocmem_write(ocmem, OCMEM_REG_GFX_MPU_START, 0x0);
+               ocmem_write(ocmem, OCMEM_REG_GFX_MPU_END, 0x0);
+       }
+
+       kfree(buf);
+
+       clear_bit_unlock(BIT(client), &ocmem->active_allocations);
+}
+EXPORT_SYMBOL(ocmem_free);
+
+static int ocmem_dev_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       unsigned long reg, region_size;
+       int i, j, ret, num_banks;
+       struct resource *res;
+       struct ocmem *ocmem;
+
+       if (!qcom_scm_is_available())
+               return -EPROBE_DEFER;
+
+       ocmem = devm_kzalloc(dev, sizeof(*ocmem), GFP_KERNEL);
+       if (!ocmem)
+               return -ENOMEM;
+
+       ocmem->dev = dev;
+       ocmem->config = device_get_match_data(dev);
+
+       ret = devm_clk_bulk_get(dev, ARRAY_SIZE(ocmem_clks), ocmem_clks);
+       if (ret) {
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Unable to get clocks\n");
+
+               return ret;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
+       ocmem->mmio = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ocmem->mmio)) {
+               dev_err(&pdev->dev, "Failed to ioremap ocmem_ctrl resource\n");
+               return PTR_ERR(ocmem->mmio);
+       }
+
+       ocmem->memory = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                    "mem");
+       if (!ocmem->memory) {
+               dev_err(dev, "Could not get mem region\n");
+               return -ENXIO;
+       }
+
+       /* The core clock is synchronous with graphics */
+       WARN_ON(clk_set_rate(ocmem_clks[OCMEM_CLK_CORE_IDX].clk, 1000) < 0);
+
+       ret = clk_bulk_prepare_enable(ARRAY_SIZE(ocmem_clks), ocmem_clks);
+       if (ret) {
+               dev_info(ocmem->dev, "Failed to enable clocks\n");
+               return ret;
+       }
+
+       if (qcom_scm_restore_sec_cfg_available()) {
+               dev_dbg(dev, "configuring scm\n");
+               ret = qcom_scm_restore_sec_cfg(QCOM_SCM_OCMEM_DEV_ID, 0);
+               if (ret) {
+                       dev_err(dev, "Could not enable secure configuration\n");
+                       goto err_clk_disable;
+               }
+       }
+
+       reg = ocmem_read(ocmem, OCMEM_REG_HW_PROFILE);
+       ocmem->num_ports = OCMEM_HW_PROFILE_NUM_PORTS(reg);
+       ocmem->num_macros = OCMEM_HW_PROFILE_NUM_MACROS(reg);
+       ocmem->interleaved = !!(reg & OCMEM_HW_PROFILE_INTERLEAVING);
+
+       num_banks = ocmem->num_ports / 2;
+       region_size = ocmem->config->macro_size * num_banks;
+
+       dev_info(dev, "%u ports, %u regions, %u macros, %sinterleaved\n",
+                ocmem->num_ports, ocmem->config->num_regions,
+                ocmem->num_macros, ocmem->interleaved ? "" : "not ");
+
+       ocmem->regions = devm_kcalloc(dev, ocmem->config->num_regions,
+                                     sizeof(struct ocmem_region), GFP_KERNEL);
+       if (!ocmem->regions) {
+               ret = -ENOMEM;
+               goto err_clk_disable;
+       }
+
+       for (i = 0; i < ocmem->config->num_regions; i++) {
+               struct ocmem_region *region = &ocmem->regions[i];
+
+               if (WARN_ON(num_banks > ARRAY_SIZE(region->macro_state))) {
+                       ret = -EINVAL;
+                       goto err_clk_disable;
+               }
+
+               region->mode = MODE_DEFAULT;
+               region->num_macros = num_banks;
+
+               if (i == (ocmem->config->num_regions - 1) &&
+                   reg & OCMEM_HW_PROFILE_LAST_REGN_HALFSIZE) {
+                       region->macro_size = ocmem->config->macro_size / 2;
+                       region->region_size = region_size / 2;
+               } else {
+                       region->macro_size = ocmem->config->macro_size;
+                       region->region_size = region_size;
+               }
+
+               for (j = 0; j < ARRAY_SIZE(region->macro_state); j++)
+                       region->macro_state[j] = CLK_OFF;
+       }
+
+       platform_set_drvdata(pdev, ocmem);
+
+       return 0;
+
+err_clk_disable:
+       clk_bulk_disable_unprepare(ARRAY_SIZE(ocmem_clks), ocmem_clks);
+       return ret;
+}
+
+static int ocmem_dev_remove(struct platform_device *pdev)
+{
+       clk_bulk_disable_unprepare(ARRAY_SIZE(ocmem_clks), ocmem_clks);
+
+       return 0;
+}
+
+static const struct ocmem_config ocmem_8974_config = {
+       .num_regions = 3,
+       .macro_size = SZ_128K,
+};
+
+static const struct of_device_id ocmem_of_match[] = {
+       { .compatible = "qcom,msm8974-ocmem", .data = &ocmem_8974_config },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, ocmem_of_match);
+
+static struct platform_driver ocmem_driver = {
+       .probe = ocmem_dev_probe,
+       .remove = ocmem_dev_remove,
+       .driver = {
+               .name = "ocmem",
+               .of_match_table = ocmem_of_match,
+       },
+};
+
+module_platform_driver(ocmem_driver);
+
+MODULE_DESCRIPTION("On Chip Memory (OCMEM) allocator for some Snapdragon SoCs");
+MODULE_LICENSE("GPL v2");
index 8bcec8d..054f97b 100644 (file)
@@ -63,7 +63,7 @@ struct autofs_info {
 
        struct autofs_sb_info *sbi;
        unsigned long last_used;
-       atomic_t count;
+       int count;
 
        kuid_t uid;
        kgid_t gid;
index 91f5787..a1c7701 100644 (file)
@@ -211,7 +211,7 @@ static int autofs_tree_busy(struct vfsmount *mnt,
                        }
                } else {
                        struct autofs_info *ino = autofs_dentry_ino(p);
-                       unsigned int ino_count = atomic_read(&ino->count);
+                       unsigned int ino_count = READ_ONCE(ino->count);
 
                        /* allow for dget above and top is already dgot */
                        if (p == top)
@@ -379,7 +379,7 @@ static struct dentry *should_expire(struct dentry *dentry,
                /* Not a forced expire? */
                if (!(how & AUTOFS_EXP_FORCED)) {
                        /* ref-walk currently on this dentry? */
-                       ino_count = atomic_read(&ino->count) + 1;
+                       ino_count = READ_ONCE(ino->count) + 1;
                        if (d_count(dentry) > ino_count)
                                return NULL;
                }
@@ -396,7 +396,7 @@ static struct dentry *should_expire(struct dentry *dentry,
                /* Not a forced expire? */
                if (!(how & AUTOFS_EXP_FORCED)) {
                        /* ref-walk currently on this dentry? */
-                       ino_count = atomic_read(&ino->count) + 1;
+                       ino_count = READ_ONCE(ino->count) + 1;
                        if (d_count(dentry) > ino_count)
                                return NULL;
                }
index 29abafc..5aaa173 100644 (file)
@@ -569,10 +569,9 @@ static int autofs_dir_symlink(struct inode *dir,
        d_add(dentry, inode);
 
        dget(dentry);
-       atomic_inc(&ino->count);
+       ino->count++;
        p_ino = autofs_dentry_ino(dentry->d_parent);
-       if (p_ino && !IS_ROOT(dentry))
-               atomic_inc(&p_ino->count);
+       p_ino->count++;
 
        dir->i_mtime = current_time(dir);
 
@@ -610,11 +609,9 @@ static int autofs_dir_unlink(struct inode *dir, struct dentry *dentry)
        if (sbi->flags & AUTOFS_SBI_CATATONIC)
                return -EACCES;
 
-       if (atomic_dec_and_test(&ino->count)) {
-               p_ino = autofs_dentry_ino(dentry->d_parent);
-               if (p_ino && !IS_ROOT(dentry))
-                       atomic_dec(&p_ino->count);
-       }
+       ino->count--;
+       p_ino = autofs_dentry_ino(dentry->d_parent);
+       p_ino->count--;
        dput(ino->dentry);
 
        d_inode(dentry)->i_size = 0;
@@ -660,7 +657,6 @@ static void autofs_set_leaf_automount_flags(struct dentry *dentry)
 
 static void autofs_clear_leaf_automount_flags(struct dentry *dentry)
 {
-       struct list_head *d_child;
        struct dentry *parent;
 
        /* flags for dentrys in the root are handled elsewhere */
@@ -673,10 +669,7 @@ static void autofs_clear_leaf_automount_flags(struct dentry *dentry)
        /* only consider parents below dentrys in the root */
        if (IS_ROOT(parent->d_parent))
                return;
-       d_child = &dentry->d_child;
-       /* Set parent managed if it's becoming empty */
-       if (d_child->next == &parent->d_subdirs &&
-           d_child->prev == &parent->d_subdirs)
+       if (autofs_dentry_ino(parent)->count == 2)
                managed_dentry_set_managed(parent);
 }
 
@@ -698,11 +691,10 @@ static int autofs_dir_rmdir(struct inode *dir, struct dentry *dentry)
        if (sbi->flags & AUTOFS_SBI_CATATONIC)
                return -EACCES;
 
-       spin_lock(&sbi->lookup_lock);
-       if (!simple_empty(dentry)) {
-               spin_unlock(&sbi->lookup_lock);
+       if (ino->count != 1)
                return -ENOTEMPTY;
-       }
+
+       spin_lock(&sbi->lookup_lock);
        __autofs_add_expiring(dentry);
        d_drop(dentry);
        spin_unlock(&sbi->lookup_lock);
@@ -710,11 +702,9 @@ static int autofs_dir_rmdir(struct inode *dir, struct dentry *dentry)
        if (sbi->version < 5)
                autofs_clear_leaf_automount_flags(dentry);
 
-       if (atomic_dec_and_test(&ino->count)) {
-               p_ino = autofs_dentry_ino(dentry->d_parent);
-               if (p_ino && dentry->d_parent != dentry)
-                       atomic_dec(&p_ino->count);
-       }
+       ino->count--;
+       p_ino = autofs_dentry_ino(dentry->d_parent);
+       p_ino->count--;
        dput(ino->dentry);
        d_inode(dentry)->i_size = 0;
        clear_nlink(d_inode(dentry));
@@ -760,10 +750,9 @@ static int autofs_dir_mkdir(struct inode *dir,
                autofs_set_leaf_automount_flags(dentry);
 
        dget(dentry);
-       atomic_inc(&ino->count);
+       ino->count++;
        p_ino = autofs_dentry_ino(dentry->d_parent);
-       if (p_ino && !IS_ROOT(dentry))
-               atomic_inc(&p_ino->count);
+       p_ino->count++;
        inc_nlink(dir);
        dir->i_mtime = current_time(dir);
 
index ee63c27..69bf2fb 100644 (file)
@@ -1531,7 +1531,7 @@ rescan:
                ret = blk_add_partitions(disk, bdev);
                if (ret == -EAGAIN)
                        goto rescan;
-       } else {
+       } else if (invalidate) {
                /*
                 * Tell userspace that the media / partition table may have
                 * changed.
index b2ec29e..73f24f3 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/ceph/ceph_debug.h>
 
+#include <linux/fs_context.h>
 #include "super.h"
 #include "cache.h"
 
@@ -49,7 +50,7 @@ void ceph_fscache_unregister(void)
        fscache_unregister_netfs(&ceph_cache_netfs);
 }
 
-int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
+int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context *fc)
 {
        const struct ceph_fsid *fsid = &fsc->client->fsid;
        const char *fscache_uniq = fsc->mount_options->fscache_uniq;
@@ -66,8 +67,8 @@ int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
                if (uniq_len && memcmp(ent->uniquifier, fscache_uniq, uniq_len))
                        continue;
 
-               pr_err("fscache cookie already registered for fsid %pU\n", fsid);
-               pr_err("  use fsc=%%s mount option to specify a uniquifier\n");
+               errorf(fc, "ceph: fscache cookie already registered for fsid %pU, use fsc=<uniquifier> option",
+                      fsid);
                err = -EBUSY;
                goto out_unlock;
        }
@@ -95,7 +96,7 @@ int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
                list_add_tail(&ent->list, &ceph_fscache_list);
        } else {
                kfree(ent);
-               pr_err("unable to register fscache cookie for fsid %pU\n",
+               errorf(fc, "ceph: unable to register fscache cookie for fsid %pU",
                       fsid);
                /* all other fs ignore this error */
        }
index e486fac..89dbdd1 100644 (file)
@@ -16,7 +16,7 @@ extern struct fscache_netfs ceph_cache_netfs;
 int ceph_fscache_register(void);
 void ceph_fscache_unregister(void);
 
-int ceph_fscache_register_fs(struct ceph_fs_client* fsc);
+int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context *fc);
 void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc);
 
 void ceph_fscache_register_inode_cookie(struct inode *inode);
@@ -88,7 +88,8 @@ static inline void ceph_fscache_unregister(void)
 {
 }
 
-static inline int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
+static inline int ceph_fscache_register_fs(struct ceph_fs_client* fsc,
+                                          struct fs_context *fc)
 {
        return 0;
 }
index a516329..068b029 100644 (file)
@@ -2182,13 +2182,17 @@ retry:
        }
        base = ceph_ino(d_inode(temp));
        rcu_read_unlock();
-       if (pos < 0 || read_seqretry(&rename_lock, seq)) {
-               pr_err("build_path did not end path lookup where "
-                      "expected, pos is %d\n", pos);
-               /* presumably this is only possible if racing with a
-                  rename of one of the parent directories (we can not
-                  lock the dentries above us to prevent this, but
-                  retrying should be harmless) */
+
+       if (read_seqretry(&rename_lock, seq))
+               goto retry;
+
+       if (pos < 0) {
+               /*
+                * A rename didn't occur, but somehow we didn't end up where
+                * we thought we would. Throw a warning and try again.
+                */
+               pr_warn("build_path did not end path lookup where "
+                       "expected, pos is %d\n", pos);
                goto retry;
        }
 
@@ -2345,6 +2349,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
        head->op = cpu_to_le32(req->r_op);
        head->caller_uid = cpu_to_le32(from_kuid(&init_user_ns, req->r_uid));
        head->caller_gid = cpu_to_le32(from_kgid(&init_user_ns, req->r_gid));
+       head->ino = 0;
        head->args = req->r_args;
 
        ceph_encode_filepath(&p, end, ino1, path1);
index ce2d00d..aeec1d6 100644 (file)
@@ -20,7 +20,7 @@
 int ceph_mdsmap_get_random_mds(struct ceph_mdsmap *m)
 {
        int n = 0;
-       int i;
+       int i, j;
 
        /* special case for one mds */
        if (1 == m->m_num_mds && m->m_info[0].state > 0)
@@ -35,9 +35,12 @@ int ceph_mdsmap_get_random_mds(struct ceph_mdsmap *m)
 
        /* pick */
        n = prandom_u32() % n;
-       for (i = 0; n > 0; i++, n--)
-               while (m->m_info[i].state <= 0)
-                       i++;
+       for (j = 0, i = 0; i < m->m_num_mds; i++) {
+               if (m->m_info[i].state > 0)
+                       j++;
+               if (j > n)
+                       break;
+       }
 
        return i;
 }
index b47f43f..9c9a7c6 100644 (file)
@@ -9,7 +9,8 @@
 #include <linux/in6.h>
 #include <linux/module.h>
 #include <linux/mount.h>
-#include <linux/parser.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/sched.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
@@ -138,280 +139,308 @@ enum {
        Opt_readdir_max_entries,
        Opt_readdir_max_bytes,
        Opt_congestion_kb,
-       Opt_last_int,
        /* int args above */
        Opt_snapdirname,
        Opt_mds_namespace,
-       Opt_fscache_uniq,
        Opt_recover_session,
-       Opt_last_string,
+       Opt_source,
        /* string args above */
        Opt_dirstat,
-       Opt_nodirstat,
        Opt_rbytes,
-       Opt_norbytes,
        Opt_asyncreaddir,
-       Opt_noasyncreaddir,
        Opt_dcache,
-       Opt_nodcache,
        Opt_ino32,
-       Opt_noino32,
        Opt_fscache,
-       Opt_nofscache,
        Opt_poolperm,
-       Opt_nopoolperm,
        Opt_require_active_mds,
-       Opt_norequire_active_mds,
-#ifdef CONFIG_CEPH_FS_POSIX_ACL
        Opt_acl,
-#endif
-       Opt_noacl,
        Opt_quotadf,
-       Opt_noquotadf,
        Opt_copyfrom,
-       Opt_nocopyfrom,
 };
 
-static match_table_t fsopt_tokens = {
-       {Opt_wsize, "wsize=%d"},
-       {Opt_rsize, "rsize=%d"},
-       {Opt_rasize, "rasize=%d"},
-       {Opt_caps_wanted_delay_min, "caps_wanted_delay_min=%d"},
-       {Opt_caps_wanted_delay_max, "caps_wanted_delay_max=%d"},
-       {Opt_caps_max, "caps_max=%d"},
-       {Opt_readdir_max_entries, "readdir_max_entries=%d"},
-       {Opt_readdir_max_bytes, "readdir_max_bytes=%d"},
-       {Opt_congestion_kb, "write_congestion_kb=%d"},
-       /* int args above */
-       {Opt_snapdirname, "snapdirname=%s"},
-       {Opt_mds_namespace, "mds_namespace=%s"},
-       {Opt_recover_session, "recover_session=%s"},
-       {Opt_fscache_uniq, "fsc=%s"},
-       /* string args above */
-       {Opt_dirstat, "dirstat"},
-       {Opt_nodirstat, "nodirstat"},
-       {Opt_rbytes, "rbytes"},
-       {Opt_norbytes, "norbytes"},
-       {Opt_asyncreaddir, "asyncreaddir"},
-       {Opt_noasyncreaddir, "noasyncreaddir"},
-       {Opt_dcache, "dcache"},
-       {Opt_nodcache, "nodcache"},
-       {Opt_ino32, "ino32"},
-       {Opt_noino32, "noino32"},
-       {Opt_fscache, "fsc"},
-       {Opt_nofscache, "nofsc"},
-       {Opt_poolperm, "poolperm"},
-       {Opt_nopoolperm, "nopoolperm"},
-       {Opt_require_active_mds, "require_active_mds"},
-       {Opt_norequire_active_mds, "norequire_active_mds"},
-#ifdef CONFIG_CEPH_FS_POSIX_ACL
-       {Opt_acl, "acl"},
-#endif
-       {Opt_noacl, "noacl"},
-       {Opt_quotadf, "quotadf"},
-       {Opt_noquotadf, "noquotadf"},
-       {Opt_copyfrom, "copyfrom"},
-       {Opt_nocopyfrom, "nocopyfrom"},
-       {-1, NULL}
+enum ceph_recover_session_mode {
+       ceph_recover_session_no,
+       ceph_recover_session_clean
+};
+
+static const struct fs_parameter_enum ceph_mount_param_enums[] = {
+       { Opt_recover_session,  "no",           ceph_recover_session_no },
+       { Opt_recover_session,  "clean",        ceph_recover_session_clean },
+       {}
+};
+
+static const struct fs_parameter_spec ceph_mount_param_specs[] = {
+       fsparam_flag_no ("acl",                         Opt_acl),
+       fsparam_flag_no ("asyncreaddir",                Opt_asyncreaddir),
+       fsparam_u32     ("caps_max",                    Opt_caps_max),
+       fsparam_u32     ("caps_wanted_delay_max",       Opt_caps_wanted_delay_max),
+       fsparam_u32     ("caps_wanted_delay_min",       Opt_caps_wanted_delay_min),
+       fsparam_s32     ("write_congestion_kb",         Opt_congestion_kb),
+       fsparam_flag_no ("copyfrom",                    Opt_copyfrom),
+       fsparam_flag_no ("dcache",                      Opt_dcache),
+       fsparam_flag_no ("dirstat",                     Opt_dirstat),
+       __fsparam       (fs_param_is_string, "fsc",     Opt_fscache,
+                        fs_param_neg_with_no | fs_param_v_optional),
+       fsparam_flag_no ("ino32",                       Opt_ino32),
+       fsparam_string  ("mds_namespace",               Opt_mds_namespace),
+       fsparam_flag_no ("poolperm",                    Opt_poolperm),
+       fsparam_flag_no ("quotadf",                     Opt_quotadf),
+       fsparam_u32     ("rasize",                      Opt_rasize),
+       fsparam_flag_no ("rbytes",                      Opt_rbytes),
+       fsparam_s32     ("readdir_max_bytes",           Opt_readdir_max_bytes),
+       fsparam_s32     ("readdir_max_entries",         Opt_readdir_max_entries),
+       fsparam_enum    ("recover_session",             Opt_recover_session),
+       fsparam_flag_no ("require_active_mds",          Opt_require_active_mds),
+       fsparam_u32     ("rsize",                       Opt_rsize),
+       fsparam_string  ("snapdirname",                 Opt_snapdirname),
+       fsparam_string  ("source",                      Opt_source),
+       fsparam_u32     ("wsize",                       Opt_wsize),
+       {}
+};
+
+static const struct fs_parameter_description ceph_mount_parameters = {
+       .name           = "ceph",
+       .specs          = ceph_mount_param_specs,
+       .enums          = ceph_mount_param_enums,
 };
 
-static int parse_fsopt_token(char *c, void *private)
+struct ceph_parse_opts_ctx {
+       struct ceph_options             *copts;
+       struct ceph_mount_options       *opts;
+};
+
+/*
+ * Parse the source parameter.  Distinguish the server list from the path.
+ * Internally we do not include the leading '/' in the path.
+ *
+ * The source will look like:
+ *     <server_spec>[,<server_spec>...]:[<path>]
+ * where
+ *     <server_spec> is <ip>[:<port>]
+ *     <path> is optional, but if present must begin with '/'
+ */
+static int ceph_parse_source(struct fs_parameter *param, struct fs_context *fc)
 {
-       struct ceph_mount_options *fsopt = private;
-       substring_t argstr[MAX_OPT_ARGS];
-       int token, intval, ret;
+       struct ceph_parse_opts_ctx *pctx = fc->fs_private;
+       struct ceph_mount_options *fsopt = pctx->opts;
+       char *dev_name = param->string, *dev_name_end;
+       int ret;
 
-       token = match_token((char *)c, fsopt_tokens, argstr);
-       if (token < 0)
-               return -EINVAL;
+       dout("%s '%s'\n", __func__, dev_name);
+       if (!dev_name || !*dev_name)
+               return invalf(fc, "ceph: Empty source");
 
-       if (token < Opt_last_int) {
-               ret = match_int(&argstr[0], &intval);
-               if (ret < 0) {
-                       pr_err("bad option arg (not int) at '%s'\n", c);
-                       return ret;
+       dev_name_end = strchr(dev_name, '/');
+       if (dev_name_end) {
+               if (strlen(dev_name_end) > 1) {
+                       kfree(fsopt->server_path);
+                       fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
+                       if (!fsopt->server_path)
+                               return -ENOMEM;
                }
-               dout("got int token %d val %d\n", token, intval);
-       } else if (token > Opt_last_int && token < Opt_last_string) {
-               dout("got string token %d val %s\n", token,
-                    argstr[0].from);
        } else {
-               dout("got token %d\n", token);
+               dev_name_end = dev_name + strlen(dev_name);
        }
 
+       dev_name_end--;         /* back up to ':' separator */
+       if (dev_name_end < dev_name || *dev_name_end != ':')
+               return invalf(fc, "ceph: No path or : separator in source");
+
+       dout("device name '%.*s'\n", (int)(dev_name_end - dev_name), dev_name);
+       if (fsopt->server_path)
+               dout("server path '%s'\n", fsopt->server_path);
+
+       ret = ceph_parse_mon_ips(param->string, dev_name_end - dev_name,
+                                pctx->copts, fc);
+       if (ret)
+               return ret;
+
+       fc->source = param->string;
+       param->string = NULL;
+       return 0;
+}
+
+static int ceph_parse_mount_param(struct fs_context *fc,
+                                 struct fs_parameter *param)
+{
+       struct ceph_parse_opts_ctx *pctx = fc->fs_private;
+       struct ceph_mount_options *fsopt = pctx->opts;
+       struct fs_parse_result result;
+       unsigned int mode;
+       int token, ret;
+
+       ret = ceph_parse_param(param, pctx->copts, fc);
+       if (ret != -ENOPARAM)
+               return ret;
+
+       token = fs_parse(fc, &ceph_mount_parameters, param, &result);
+       dout("%s fs_parse '%s' token %d\n", __func__, param->key, token);
+       if (token < 0)
+               return token;
+
        switch (token) {
        case Opt_snapdirname:
                kfree(fsopt->snapdir_name);
-               fsopt->snapdir_name = kstrndup(argstr[0].from,
-                                              argstr[0].to-argstr[0].from,
-                                              GFP_KERNEL);
-               if (!fsopt->snapdir_name)
-                       return -ENOMEM;
+               fsopt->snapdir_name = param->string;
+               param->string = NULL;
                break;
        case Opt_mds_namespace:
                kfree(fsopt->mds_namespace);
-               fsopt->mds_namespace = kstrndup(argstr[0].from,
-                                               argstr[0].to-argstr[0].from,
-                                               GFP_KERNEL);
-               if (!fsopt->mds_namespace)
-                       return -ENOMEM;
+               fsopt->mds_namespace = param->string;
+               param->string = NULL;
                break;
        case Opt_recover_session:
-               if (!strncmp(argstr[0].from, "no",
-                            argstr[0].to - argstr[0].from)) {
+               mode = result.uint_32;
+               if (mode == ceph_recover_session_no)
                        fsopt->flags &= ~CEPH_MOUNT_OPT_CLEANRECOVER;
-               } else if (!strncmp(argstr[0].from, "clean",
-                                   argstr[0].to - argstr[0].from)) {
+               else if (mode == ceph_recover_session_clean)
                        fsopt->flags |= CEPH_MOUNT_OPT_CLEANRECOVER;
-               } else {
-                       return -EINVAL;
-               }
-               break;
-       case Opt_fscache_uniq:
-#ifdef CONFIG_CEPH_FSCACHE
-               kfree(fsopt->fscache_uniq);
-               fsopt->fscache_uniq = kstrndup(argstr[0].from,
-                                              argstr[0].to-argstr[0].from,
-                                              GFP_KERNEL);
-               if (!fsopt->fscache_uniq)
-                       return -ENOMEM;
-               fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
+               else
+                       BUG();
                break;
-#else
-               pr_err("fscache support is disabled\n");
-               return -EINVAL;
-#endif
+       case Opt_source:
+               if (fc->source)
+                       return invalf(fc, "ceph: Multiple sources specified");
+               return ceph_parse_source(param, fc);
        case Opt_wsize:
-               if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_WRITE_SIZE)
-                       return -EINVAL;
-               fsopt->wsize = ALIGN(intval, PAGE_SIZE);
+               if (result.uint_32 < PAGE_SIZE ||
+                   result.uint_32 > CEPH_MAX_WRITE_SIZE)
+                       goto out_of_range;
+               fsopt->wsize = ALIGN(result.uint_32, PAGE_SIZE);
                break;
        case Opt_rsize:
-               if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_READ_SIZE)
-                       return -EINVAL;
-               fsopt->rsize = ALIGN(intval, PAGE_SIZE);
+               if (result.uint_32 < PAGE_SIZE ||
+                   result.uint_32 > CEPH_MAX_READ_SIZE)
+                       goto out_of_range;
+               fsopt->rsize = ALIGN(result.uint_32, PAGE_SIZE);
                break;
        case Opt_rasize:
-               if (intval < 0)
-                       return -EINVAL;
-               fsopt->rasize = ALIGN(intval, PAGE_SIZE);
+               fsopt->rasize = ALIGN(result.uint_32, PAGE_SIZE);
                break;
        case Opt_caps_wanted_delay_min:
-               if (intval < 1)
-                       return -EINVAL;
-               fsopt->caps_wanted_delay_min = intval;
+               if (result.uint_32 < 1)
+                       goto out_of_range;
+               fsopt->caps_wanted_delay_min = result.uint_32;
                break;
        case Opt_caps_wanted_delay_max:
-               if (intval < 1)
-                       return -EINVAL;
-               fsopt->caps_wanted_delay_max = intval;
+               if (result.uint_32 < 1)
+                       goto out_of_range;
+               fsopt->caps_wanted_delay_max = result.uint_32;
                break;
        case Opt_caps_max:
-               if (intval < 0)
-                       return -EINVAL;
-               fsopt->caps_max = intval;
+               fsopt->caps_max = result.uint_32;
                break;
        case Opt_readdir_max_entries:
-               if (intval < 1)
-                       return -EINVAL;
-               fsopt->max_readdir = intval;
+               if (result.uint_32 < 1)
+                       goto out_of_range;
+               fsopt->max_readdir = result.uint_32;
                break;
        case Opt_readdir_max_bytes:
-               if (intval < (int)PAGE_SIZE && intval != 0)
-                       return -EINVAL;
-               fsopt->max_readdir_bytes = intval;
+               if (result.uint_32 < PAGE_SIZE && result.uint_32 != 0)
+                       goto out_of_range;
+               fsopt->max_readdir_bytes = result.uint_32;
                break;
        case Opt_congestion_kb:
-               if (intval < 1024) /* at least 1M */
-                       return -EINVAL;
-               fsopt->congestion_kb = intval;
+               if (result.uint_32 < 1024) /* at least 1M */
+                       goto out_of_range;
+               fsopt->congestion_kb = result.uint_32;
                break;
        case Opt_dirstat:
-               fsopt->flags |= CEPH_MOUNT_OPT_DIRSTAT;
-               break;
-       case Opt_nodirstat:
-               fsopt->flags &= ~CEPH_MOUNT_OPT_DIRSTAT;
+               if (!result.negated)
+                       fsopt->flags |= CEPH_MOUNT_OPT_DIRSTAT;
+               else
+                       fsopt->flags &= ~CEPH_MOUNT_OPT_DIRSTAT;
                break;
        case Opt_rbytes:
-               fsopt->flags |= CEPH_MOUNT_OPT_RBYTES;
-               break;
-       case Opt_norbytes:
-               fsopt->flags &= ~CEPH_MOUNT_OPT_RBYTES;
+               if (!result.negated)
+                       fsopt->flags |= CEPH_MOUNT_OPT_RBYTES;
+               else
+                       fsopt->flags &= ~CEPH_MOUNT_OPT_RBYTES;
                break;
        case Opt_asyncreaddir:
-               fsopt->flags &= ~CEPH_MOUNT_OPT_NOASYNCREADDIR;
-               break;
-       case Opt_noasyncreaddir:
-               fsopt->flags |= CEPH_MOUNT_OPT_NOASYNCREADDIR;
+               if (!result.negated)
+                       fsopt->flags &= ~CEPH_MOUNT_OPT_NOASYNCREADDIR;
+               else
+                       fsopt->flags |= CEPH_MOUNT_OPT_NOASYNCREADDIR;
                break;
        case Opt_dcache:
-               fsopt->flags |= CEPH_MOUNT_OPT_DCACHE;
-               break;
-       case Opt_nodcache:
-               fsopt->flags &= ~CEPH_MOUNT_OPT_DCACHE;
+               if (!result.negated)
+                       fsopt->flags |= CEPH_MOUNT_OPT_DCACHE;
+               else
+                       fsopt->flags &= ~CEPH_MOUNT_OPT_DCACHE;
                break;
        case Opt_ino32:
-               fsopt->flags |= CEPH_MOUNT_OPT_INO32;
-               break;
-       case Opt_noino32:
-               fsopt->flags &= ~CEPH_MOUNT_OPT_INO32;
+               if (!result.negated)
+                       fsopt->flags |= CEPH_MOUNT_OPT_INO32;
+               else
+                       fsopt->flags &= ~CEPH_MOUNT_OPT_INO32;
                break;
+
        case Opt_fscache:
 #ifdef CONFIG_CEPH_FSCACHE
-               fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
                kfree(fsopt->fscache_uniq);
                fsopt->fscache_uniq = NULL;
+               if (result.negated) {
+                       fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE;
+               } else {
+                       fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
+                       fsopt->fscache_uniq = param->string;
+                       param->string = NULL;
+               }
                break;
 #else
-               pr_err("fscache support is disabled\n");
-               return -EINVAL;
+               return invalf(fc, "ceph: fscache support is disabled");
 #endif
-       case Opt_nofscache:
-               fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE;
-               kfree(fsopt->fscache_uniq);
-               fsopt->fscache_uniq = NULL;
-               break;
        case Opt_poolperm:
-               fsopt->flags &= ~CEPH_MOUNT_OPT_NOPOOLPERM;
-               break;
-       case Opt_nopoolperm:
-               fsopt->flags |= CEPH_MOUNT_OPT_NOPOOLPERM;
+               if (!result.negated)
+                       fsopt->flags &= ~CEPH_MOUNT_OPT_NOPOOLPERM;
+               else
+                       fsopt->flags |= CEPH_MOUNT_OPT_NOPOOLPERM;
                break;
        case Opt_require_active_mds:
-               fsopt->flags &= ~CEPH_MOUNT_OPT_MOUNTWAIT;
-               break;
-       case Opt_norequire_active_mds:
-               fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT;
+               if (!result.negated)
+                       fsopt->flags &= ~CEPH_MOUNT_OPT_MOUNTWAIT;
+               else
+                       fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT;
                break;
        case Opt_quotadf:
-               fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF;
-               break;
-       case Opt_noquotadf:
-               fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF;
+               if (!result.negated)
+                       fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF;
+               else
+                       fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF;
                break;
        case Opt_copyfrom:
-               fsopt->flags &= ~CEPH_MOUNT_OPT_NOCOPYFROM;
-               break;
-       case Opt_nocopyfrom:
-               fsopt->flags |= CEPH_MOUNT_OPT_NOCOPYFROM;
+               if (!result.negated)
+                       fsopt->flags &= ~CEPH_MOUNT_OPT_NOCOPYFROM;
+               else
+                       fsopt->flags |= CEPH_MOUNT_OPT_NOCOPYFROM;
                break;
-#ifdef CONFIG_CEPH_FS_POSIX_ACL
        case Opt_acl:
-               fsopt->sb_flags |= SB_POSIXACL;
-               break;
+               if (!result.negated) {
+#ifdef CONFIG_CEPH_FS_POSIX_ACL
+                       fc->sb_flags |= SB_POSIXACL;
+#else
+                       return invalf(fc, "ceph: POSIX ACL support is disabled");
 #endif
-       case Opt_noacl:
-               fsopt->sb_flags &= ~SB_POSIXACL;
+               } else {
+                       fc->sb_flags &= ~SB_POSIXACL;
+               }
                break;
        default:
-               BUG_ON(token);
+               BUG();
        }
        return 0;
+
+out_of_range:
+       return invalf(fc, "ceph: %s out of range", param->key);
 }
 
 static void destroy_mount_options(struct ceph_mount_options *args)
 {
        dout("destroy_mount_options %p\n", args);
+       if (!args)
+               return;
+
        kfree(args->snapdir_name);
        kfree(args->mds_namespace);
        kfree(args->server_path);
@@ -459,91 +488,6 @@ static int compare_mount_options(struct ceph_mount_options *new_fsopt,
        return ceph_compare_options(new_opt, fsc->client);
 }
 
-static int parse_mount_options(struct ceph_mount_options **pfsopt,
-                              struct ceph_options **popt,
-                              int flags, char *options,
-                              const char *dev_name)
-{
-       struct ceph_mount_options *fsopt;
-       const char *dev_name_end;
-       int err;
-
-       if (!dev_name || !*dev_name)
-               return -EINVAL;
-
-       fsopt = kzalloc(sizeof(*fsopt), GFP_KERNEL);
-       if (!fsopt)
-               return -ENOMEM;
-
-       dout("parse_mount_options %p, dev_name '%s'\n", fsopt, dev_name);
-
-       fsopt->sb_flags = flags;
-       fsopt->flags = CEPH_MOUNT_OPT_DEFAULT;
-
-       fsopt->wsize = CEPH_MAX_WRITE_SIZE;
-       fsopt->rsize = CEPH_MAX_READ_SIZE;
-       fsopt->rasize = CEPH_RASIZE_DEFAULT;
-       fsopt->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL);
-       if (!fsopt->snapdir_name) {
-               err = -ENOMEM;
-               goto out;
-       }
-
-       fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT;
-       fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT;
-       fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT;
-       fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT;
-       fsopt->congestion_kb = default_congestion_kb();
-
-       /*
-        * Distinguish the server list from the path in "dev_name".
-        * Internally we do not include the leading '/' in the path.
-        *
-        * "dev_name" will look like:
-        *     <server_spec>[,<server_spec>...]:[<path>]
-        * where
-        *     <server_spec> is <ip>[:<port>]
-        *     <path> is optional, but if present must begin with '/'
-        */
-       dev_name_end = strchr(dev_name, '/');
-       if (dev_name_end) {
-               if (strlen(dev_name_end) > 1) {
-                       fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
-                       if (!fsopt->server_path) {
-                               err = -ENOMEM;
-                               goto out;
-                       }
-               }
-       } else {
-               dev_name_end = dev_name + strlen(dev_name);
-       }
-       err = -EINVAL;
-       dev_name_end--;         /* back up to ':' separator */
-       if (dev_name_end < dev_name || *dev_name_end != ':') {
-               pr_err("device name is missing path (no : separator in %s)\n",
-                               dev_name);
-               goto out;
-       }
-       dout("device name '%.*s'\n", (int)(dev_name_end - dev_name), dev_name);
-       if (fsopt->server_path)
-               dout("server path '%s'\n", fsopt->server_path);
-
-       *popt = ceph_parse_options(options, dev_name, dev_name_end,
-                                parse_fsopt_token, (void *)fsopt);
-       if (IS_ERR(*popt)) {
-               err = PTR_ERR(*popt);
-               goto out;
-       }
-
-       /* success */
-       *pfsopt = fsopt;
-       return 0;
-
-out:
-       destroy_mount_options(fsopt);
-       return err;
-}
-
 /**
  * ceph_show_options - Show mount options in /proc/mounts
  * @m: seq_file to write to
@@ -587,7 +531,7 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root)
                seq_puts(m, ",noquotadf");
 
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
-       if (fsopt->sb_flags & SB_POSIXACL)
+       if (root->d_sb->s_flags & SB_POSIXACL)
                seq_puts(m, ",acl");
        else
                seq_puts(m, ",noacl");
@@ -860,12 +804,6 @@ static void ceph_umount_begin(struct super_block *sb)
        fsc->filp_gen++; // invalidate open files
 }
 
-static int ceph_remount(struct super_block *sb, int *flags, char *data)
-{
-       sync_filesystem(sb);
-       return 0;
-}
-
 static const struct super_operations ceph_super_ops = {
        .alloc_inode    = ceph_alloc_inode,
        .free_inode     = ceph_free_inode,
@@ -874,7 +812,6 @@ static const struct super_operations ceph_super_ops = {
        .evict_inode    = ceph_evict_inode,
        .sync_fs        = ceph_sync_fs,
        .put_super      = ceph_put_super,
-       .remount_fs     = ceph_remount,
        .show_options   = ceph_show_options,
        .statfs         = ceph_statfs,
        .umount_begin   = ceph_umount_begin,
@@ -935,7 +872,8 @@ out:
 /*
  * mount: join the ceph cluster, and open root directory.
  */
-static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
+static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc,
+                                     struct fs_context *fc)
 {
        int err;
        unsigned long started = jiffies;  /* note the start time */
@@ -952,7 +890,7 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
 
                /* setup fscache */
                if (fsc->mount_options->flags & CEPH_MOUNT_OPT_FSCACHE) {
-                       err = ceph_fscache_register_fs(fsc);
+                       err = ceph_fscache_register_fs(fsc, fc);
                        if (err < 0)
                                goto out;
                }
@@ -987,18 +925,16 @@ out:
        return ERR_PTR(err);
 }
 
-static int ceph_set_super(struct super_block *s, void *data)
+static int ceph_set_super(struct super_block *s, struct fs_context *fc)
 {
-       struct ceph_fs_client *fsc = data;
+       struct ceph_fs_client *fsc = s->s_fs_info;
        int ret;
 
-       dout("set_super %p data %p\n", s, data);
+       dout("set_super %p\n", s);
 
-       s->s_flags = fsc->mount_options->sb_flags;
        s->s_maxbytes = MAX_LFS_FILESIZE;
 
        s->s_xattr = ceph_xattr_handlers;
-       s->s_fs_info = fsc;
        fsc->sb = s;
        fsc->max_file_size = 1ULL << 40; /* temp value until we get mdsmap */
 
@@ -1010,24 +946,18 @@ static int ceph_set_super(struct super_block *s, void *data)
        s->s_time_min = 0;
        s->s_time_max = U32_MAX;
 
-       ret = set_anon_super(s, NULL);  /* what is that second arg for? */
+       ret = set_anon_super_fc(s, fc);
        if (ret != 0)
-               goto fail;
-
-       return ret;
-
-fail:
-       s->s_fs_info = NULL;
-       fsc->sb = NULL;
+               fsc->sb = NULL;
        return ret;
 }
 
 /*
  * share superblock if same fs AND options
  */
-static int ceph_compare_super(struct super_block *sb, void *data)
+static int ceph_compare_super(struct super_block *sb, struct fs_context *fc)
 {
-       struct ceph_fs_client *new = data;
+       struct ceph_fs_client *new = fc->s_fs_info;
        struct ceph_mount_options *fsopt = new->mount_options;
        struct ceph_options *opt = new->client->options;
        struct ceph_fs_client *other = ceph_sb_to_client(sb);
@@ -1043,7 +973,7 @@ static int ceph_compare_super(struct super_block *sb, void *data)
                dout("fsid doesn't match\n");
                return 0;
        }
-       if (fsopt->sb_flags != other->mount_options->sb_flags) {
+       if (fc->sb_flags != (sb->s_flags & ~SB_BORN)) {
                dout("flags differ\n");
                return 0;
        }
@@ -1073,46 +1003,46 @@ static int ceph_setup_bdi(struct super_block *sb, struct ceph_fs_client *fsc)
        return 0;
 }
 
-static struct dentry *ceph_mount(struct file_system_type *fs_type,
-                      int flags, const char *dev_name, void *data)
+static int ceph_get_tree(struct fs_context *fc)
 {
+       struct ceph_parse_opts_ctx *pctx = fc->fs_private;
        struct super_block *sb;
        struct ceph_fs_client *fsc;
        struct dentry *res;
+       int (*compare_super)(struct super_block *, struct fs_context *) =
+               ceph_compare_super;
        int err;
-       int (*compare_super)(struct super_block *, void *) = ceph_compare_super;
-       struct ceph_mount_options *fsopt = NULL;
-       struct ceph_options *opt = NULL;
 
-       dout("ceph_mount\n");
+       dout("ceph_get_tree\n");
+
+       if (!fc->source)
+               return invalf(fc, "ceph: No source");
 
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
-       flags |= SB_POSIXACL;
+       fc->sb_flags |= SB_POSIXACL;
 #endif
-       err = parse_mount_options(&fsopt, &opt, flags, data, dev_name);
-       if (err < 0) {
-               res = ERR_PTR(err);
-               goto out_final;
-       }
 
        /* create client (which we may/may not use) */
-       fsc = create_fs_client(fsopt, opt);
+       fsc = create_fs_client(pctx->opts, pctx->copts);
+       pctx->opts = NULL;
+       pctx->copts = NULL;
        if (IS_ERR(fsc)) {
-               res = ERR_CAST(fsc);
+               err = PTR_ERR(fsc);
                goto out_final;
        }
 
        err = ceph_mdsc_init(fsc);
-       if (err < 0) {
-               res = ERR_PTR(err);
+       if (err < 0)
                goto out;
-       }
 
        if (ceph_test_opt(fsc->client, NOSHARE))
                compare_super = NULL;
-       sb = sget(fs_type, compare_super, ceph_set_super, flags, fsc);
+
+       fc->s_fs_info = fsc;
+       sb = sget_fc(fc, compare_super, ceph_set_super);
+       fc->s_fs_info = NULL;
        if (IS_ERR(sb)) {
-               res = ERR_CAST(sb);
+               err = PTR_ERR(sb);
                goto out;
        }
 
@@ -1123,18 +1053,19 @@ static struct dentry *ceph_mount(struct file_system_type *fs_type,
        } else {
                dout("get_sb using new client %p\n", fsc);
                err = ceph_setup_bdi(sb, fsc);
-               if (err < 0) {
-                       res = ERR_PTR(err);
+               if (err < 0)
                        goto out_splat;
-               }
        }
 
-       res = ceph_real_mount(fsc);
-       if (IS_ERR(res))
+       res = ceph_real_mount(fsc, fc);
+       if (IS_ERR(res)) {
+               err = PTR_ERR(res);
                goto out_splat;
+       }
        dout("root %p inode %p ino %llx.%llx\n", res,
             d_inode(res), ceph_vinop(d_inode(res)));
-       return res;
+       fc->root = fsc->sb->s_root;
+       return 0;
 
 out_splat:
        ceph_mdsc_close_sessions(fsc->mdsc);
@@ -1144,8 +1075,79 @@ out_splat:
 out:
        destroy_fs_client(fsc);
 out_final:
-       dout("ceph_mount fail %ld\n", PTR_ERR(res));
-       return res;
+       dout("ceph_get_tree fail %d\n", err);
+       return err;
+}
+
+static void ceph_free_fc(struct fs_context *fc)
+{
+       struct ceph_parse_opts_ctx *pctx = fc->fs_private;
+
+       if (pctx) {
+               destroy_mount_options(pctx->opts);
+               ceph_destroy_options(pctx->copts);
+               kfree(pctx);
+       }
+}
+
+static int ceph_reconfigure_fc(struct fs_context *fc)
+{
+       sync_filesystem(fc->root->d_sb);
+       return 0;
+}
+
+static const struct fs_context_operations ceph_context_ops = {
+       .free           = ceph_free_fc,
+       .parse_param    = ceph_parse_mount_param,
+       .get_tree       = ceph_get_tree,
+       .reconfigure    = ceph_reconfigure_fc,
+};
+
+/*
+ * Set up the filesystem mount context.
+ */
+static int ceph_init_fs_context(struct fs_context *fc)
+{
+       struct ceph_parse_opts_ctx *pctx;
+       struct ceph_mount_options *fsopt;
+
+       pctx = kzalloc(sizeof(*pctx), GFP_KERNEL);
+       if (!pctx)
+               return -ENOMEM;
+
+       pctx->copts = ceph_alloc_options();
+       if (!pctx->copts)
+               goto nomem;
+
+       pctx->opts = kzalloc(sizeof(*pctx->opts), GFP_KERNEL);
+       if (!pctx->opts)
+               goto nomem;
+
+       fsopt = pctx->opts;
+       fsopt->flags = CEPH_MOUNT_OPT_DEFAULT;
+
+       fsopt->wsize = CEPH_MAX_WRITE_SIZE;
+       fsopt->rsize = CEPH_MAX_READ_SIZE;
+       fsopt->rasize = CEPH_RASIZE_DEFAULT;
+       fsopt->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL);
+       if (!fsopt->snapdir_name)
+               goto nomem;
+
+       fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT;
+       fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT;
+       fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT;
+       fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT;
+       fsopt->congestion_kb = default_congestion_kb();
+
+       fc->fs_private = pctx;
+       fc->ops = &ceph_context_ops;
+       return 0;
+
+nomem:
+       destroy_mount_options(pctx->opts);
+       ceph_destroy_options(pctx->copts);
+       kfree(pctx);
+       return -ENOMEM;
 }
 
 static void ceph_kill_sb(struct super_block *s)
@@ -1172,7 +1174,7 @@ static void ceph_kill_sb(struct super_block *s)
 static struct file_system_type ceph_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "ceph",
-       .mount          = ceph_mount,
+       .init_fs_context = ceph_init_fs_context,
        .kill_sb        = ceph_kill_sb,
        .fs_flags       = FS_RENAME_DOES_D_MOVE,
 };
index f98d924..f0f9cb7 100644 (file)
@@ -74,7 +74,6 @@
 
 struct ceph_mount_options {
        int flags;
-       int sb_flags;
 
        int wsize;            /* max write size */
        int rsize;            /* max read size */
@@ -407,22 +406,26 @@ struct ceph_inode_info {
        struct inode vfs_inode; /* at end */
 };
 
-static inline struct ceph_inode_info *ceph_inode(struct inode *inode)
+static inline struct ceph_inode_info *
+ceph_inode(const struct inode *inode)
 {
        return container_of(inode, struct ceph_inode_info, vfs_inode);
 }
 
-static inline struct ceph_fs_client *ceph_inode_to_client(struct inode *inode)
+static inline struct ceph_fs_client *
+ceph_inode_to_client(const struct inode *inode)
 {
        return (struct ceph_fs_client *)inode->i_sb->s_fs_info;
 }
 
-static inline struct ceph_fs_client *ceph_sb_to_client(struct super_block *sb)
+static inline struct ceph_fs_client *
+ceph_sb_to_client(const struct super_block *sb)
 {
        return (struct ceph_fs_client *)sb->s_fs_info;
 }
 
-static inline struct ceph_vino ceph_vino(struct inode *inode)
+static inline struct ceph_vino
+ceph_vino(const struct inode *inode)
 {
        return ceph_inode(inode)->i_vino;
 }
index 1d1051d..5492b98 100644 (file)
@@ -730,11 +730,6 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
                struct inode *dir = d_inode(dentry);
                struct dentry *child;
 
-               if (!dir) {
-                       dput(dentry);
-                       dentry = ERR_PTR(-ENOENT);
-                       break;
-               }
                if (!S_ISDIR(dir->i_mode)) {
                        dput(dentry);
                        dentry = ERR_PTR(-ENOTDIR);
@@ -751,7 +746,7 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
                while (*s && *s != sep)
                        s++;
 
-               child = lookup_one_len_unlocked(p, dentry, s - p);
+               child = lookup_positive_unlocked(p, dentry, s - p);
                dput(dentry);
                dentry = child;
        } while (!IS_ERR(dentry));
index f7931b6..a2749a7 100644 (file)
@@ -319,7 +319,7 @@ static inline void __d_set_inode_and_type(struct dentry *dentry,
        flags = READ_ONCE(dentry->d_flags);
        flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU);
        flags |= type_flags;
-       WRITE_ONCE(dentry->d_flags, flags);
+       smp_store_release(&dentry->d_flags, flags);
 }
 
 static inline void __d_clear_type_and_inode(struct dentry *dentry)
@@ -903,17 +903,19 @@ struct dentry *dget_parent(struct dentry *dentry)
 {
        int gotref;
        struct dentry *ret;
+       unsigned seq;
 
        /*
         * Do optimistic parent lookup without any
         * locking.
         */
        rcu_read_lock();
+       seq = raw_seqcount_begin(&dentry->d_seq);
        ret = READ_ONCE(dentry->d_parent);
        gotref = lockref_get_not_zero(&ret->d_lockref);
        rcu_read_unlock();
        if (likely(gotref)) {
-               if (likely(ret == READ_ONCE(dentry->d_parent)))
+               if (!read_seqcount_retry(&dentry->d_seq, seq))
                        return ret;
                dput(ret);
        }
index 7b975db..f4d8df5 100644 (file)
@@ -299,13 +299,9 @@ struct dentry *debugfs_lookup(const char *name, struct dentry *parent)
        if (!parent)
                parent = debugfs_mount->mnt_root;
 
-       dentry = lookup_one_len_unlocked(name, parent, strlen(name));
+       dentry = lookup_positive_unlocked(name, parent, strlen(name));
        if (IS_ERR(dentry))
                return NULL;
-       if (!d_really_is_positive(dentry)) {
-               dput(dentry);
-               return NULL;
-       }
        return dentry;
 }
 EXPORT_SYMBOL_GPL(debugfs_lookup);
index 0635cba..eb2a585 100644 (file)
@@ -34,7 +34,7 @@ config VIRTIO_FS
        select VIRTIO
        help
          The Virtio Filesystem allows guests to mount file systems from the
-          host.
+         host.
 
          If you want to share files between guests or with the host, answer Y
-          or M.
+         or M.
index d4e6691..8e02d76 100644 (file)
@@ -1965,7 +1965,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
 
        nbuf = 0;
        rem = 0;
-       for (idx = tail; idx < head && rem < len; idx++)
+       for (idx = tail; idx != head && rem < len; idx++)
                rem += pipe->bufs[idx & mask].len;
 
        ret = -EINVAL;
index 54d638f..ee19011 100644 (file)
@@ -248,7 +248,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
                kfree(forget);
                if (ret == -ENOMEM)
                        goto out;
-               if (ret || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
+               if (ret || fuse_invalid_attr(&outarg.attr) ||
+                   (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
                        goto invalid;
 
                forget_all_cached_acls(inode);
@@ -319,6 +320,12 @@ int fuse_valid_type(int m)
                S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m);
 }
 
+bool fuse_invalid_attr(struct fuse_attr *attr)
+{
+       return !fuse_valid_type(attr->mode) ||
+               attr->size > LLONG_MAX;
+}
+
 int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name,
                     struct fuse_entry_out *outarg, struct inode **inode)
 {
@@ -350,7 +357,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
        err = -EIO;
        if (!outarg->nodeid)
                goto out_put_forget;
-       if (!fuse_valid_type(outarg->attr.mode))
+       if (fuse_invalid_attr(&outarg->attr))
                goto out_put_forget;
 
        *inode = fuse_iget(sb, outarg->nodeid, outarg->generation,
@@ -475,7 +482,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
                goto out_free_ff;
 
        err = -EIO;
-       if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid))
+       if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid) ||
+           fuse_invalid_attr(&outentry.attr))
                goto out_free_ff;
 
        ff->fh = outopen.fh;
@@ -583,7 +591,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args,
                goto out_put_forget_req;
 
        err = -EIO;
-       if (invalid_nodeid(outarg.nodeid))
+       if (invalid_nodeid(outarg.nodeid) || fuse_invalid_attr(&outarg.attr))
                goto out_put_forget_req;
 
        if ((outarg.attr.mode ^ mode) & S_IFMT)
@@ -862,7 +870,8 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
 
                spin_lock(&fi->lock);
                fi->attr_version = atomic64_inc_return(&fc->attr_version);
-               inc_nlink(inode);
+               if (likely(inode->i_nlink < UINT_MAX))
+                       inc_nlink(inode);
                spin_unlock(&fi->lock);
                fuse_invalidate_attr(inode);
                fuse_update_ctime(inode);
@@ -942,7 +951,8 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
        args.out_args[0].value = &outarg;
        err = fuse_simple_request(fc, &args);
        if (!err) {
-               if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
+               if (fuse_invalid_attr(&outarg.attr) ||
+                   (inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
                        make_bad_inode(inode);
                        err = -EIO;
                } else {
@@ -1563,7 +1573,8 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
                goto error;
        }
 
-       if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
+       if (fuse_invalid_attr(&outarg.attr) ||
+           (inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
                make_bad_inode(inode);
                err = -EIO;
                goto error;
index db48a5c..a63d779 100644 (file)
@@ -713,8 +713,10 @@ static ssize_t fuse_async_req_send(struct fuse_conn *fc,
 
        ia->ap.args.end = fuse_aio_complete_req;
        err = fuse_simple_background(fc, &ia->ap.args, GFP_KERNEL);
+       if (err)
+               fuse_aio_complete_req(fc, &ia->ap.args, err);
 
-       return err ?: num_bytes;
+       return num_bytes;
 }
 
 static ssize_t fuse_send_read(struct fuse_io_args *ia, loff_t pos, size_t count,
@@ -1096,6 +1098,8 @@ static ssize_t fuse_send_write_pages(struct fuse_io_args *ia,
        ia->write.in.flags = fuse_write_flags(iocb);
 
        err = fuse_simple_request(fc, &ap->args);
+       if (!err && ia->write.out.size > count)
+               err = -EIO;
 
        offset = ap->descs[0].offset;
        count = ia->write.out.size;
index d148188..aa75e23 100644 (file)
@@ -989,6 +989,8 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc);
  */
 int fuse_valid_type(int m);
 
+bool fuse_invalid_attr(struct fuse_attr *attr);
+
 /**
  * Is current process allowed to perform filesystem operation?
  */
index 5c38b9d..6a40f75 100644 (file)
@@ -184,7 +184,7 @@ static int fuse_direntplus_link(struct file *file,
 
        if (invalid_nodeid(o->nodeid))
                return -EIO;
-       if (!fuse_valid_type(o->attr.mode))
+       if (fuse_invalid_attr(&o->attr))
                return -EIO;
 
        fc = get_fuse_conn(dir);
index a5c8604..bade747 100644 (file)
@@ -35,6 +35,7 @@ struct virtio_fs_vq {
        struct fuse_dev *fud;
        bool connected;
        long in_flight;
+       struct completion in_flight_zero; /* No inflight requests */
        char name[24];
 } ____cacheline_aligned_in_smp;
 
@@ -48,11 +49,15 @@ struct virtio_fs {
        unsigned int num_request_queues; /* number of request queues */
 };
 
-struct virtio_fs_forget {
+struct virtio_fs_forget_req {
        struct fuse_in_header ih;
        struct fuse_forget_in arg;
+};
+
+struct virtio_fs_forget {
        /* This request can be temporarily queued on virt queue */
        struct list_head list;
+       struct virtio_fs_forget_req req;
 };
 
 static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
@@ -81,6 +86,8 @@ static inline void dec_in_flight_req(struct virtio_fs_vq *fsvq)
 {
        WARN_ON(fsvq->in_flight <= 0);
        fsvq->in_flight--;
+       if (!fsvq->in_flight)
+               complete(&fsvq->in_flight_zero);
 }
 
 static void release_virtio_fs_obj(struct kref *ref)
@@ -111,22 +118,23 @@ static void virtio_fs_drain_queue(struct virtio_fs_vq *fsvq)
        WARN_ON(fsvq->in_flight < 0);
 
        /* Wait for in flight requests to finish.*/
-       while (1) {
-               spin_lock(&fsvq->lock);
-               if (!fsvq->in_flight) {
-                       spin_unlock(&fsvq->lock);
-                       break;
-               }
+       spin_lock(&fsvq->lock);
+       if (fsvq->in_flight) {
+               /* We are holding virtio_fs_mutex. There should not be any
+                * waiters waiting for completion.
+                */
+               reinit_completion(&fsvq->in_flight_zero);
+               spin_unlock(&fsvq->lock);
+               wait_for_completion(&fsvq->in_flight_zero);
+       } else {
                spin_unlock(&fsvq->lock);
-               /* TODO use completion instead of timeout */
-               usleep_range(1000, 2000);
        }
 
        flush_work(&fsvq->done_work);
        flush_delayed_work(&fsvq->dispatch_work);
 }
 
-static void virtio_fs_drain_all_queues(struct virtio_fs *fs)
+static void virtio_fs_drain_all_queues_locked(struct virtio_fs *fs)
 {
        struct virtio_fs_vq *fsvq;
        int i;
@@ -137,6 +145,19 @@ static void virtio_fs_drain_all_queues(struct virtio_fs *fs)
        }
 }
 
+static void virtio_fs_drain_all_queues(struct virtio_fs *fs)
+{
+       /* Provides mutual exclusion between ->remove and ->kill_sb
+        * paths. We don't want both of these draining queue at the
+        * same time. Current completion logic reinits completion
+        * and that means there should not be any other thread
+        * doing reinit or waiting for completion already.
+        */
+       mutex_lock(&virtio_fs_mutex);
+       virtio_fs_drain_all_queues_locked(fs);
+       mutex_unlock(&virtio_fs_mutex);
+}
+
 static void virtio_fs_start_all_queues(struct virtio_fs *fs)
 {
        struct virtio_fs_vq *fsvq;
@@ -313,17 +334,72 @@ static void virtio_fs_request_dispatch_work(struct work_struct *work)
        }
 }
 
+/*
+ * Returns 1 if queue is full and sender should wait a bit before sending
+ * next request, 0 otherwise.
+ */
+static int send_forget_request(struct virtio_fs_vq *fsvq,
+                              struct virtio_fs_forget *forget,
+                              bool in_flight)
+{
+       struct scatterlist sg;
+       struct virtqueue *vq;
+       int ret = 0;
+       bool notify;
+       struct virtio_fs_forget_req *req = &forget->req;
+
+       spin_lock(&fsvq->lock);
+       if (!fsvq->connected) {
+               if (in_flight)
+                       dec_in_flight_req(fsvq);
+               kfree(forget);
+               goto out;
+       }
+
+       sg_init_one(&sg, req, sizeof(*req));
+       vq = fsvq->vq;
+       dev_dbg(&vq->vdev->dev, "%s\n", __func__);
+
+       ret = virtqueue_add_outbuf(vq, &sg, 1, forget, GFP_ATOMIC);
+       if (ret < 0) {
+               if (ret == -ENOMEM || ret == -ENOSPC) {
+                       pr_debug("virtio-fs: Could not queue FORGET: err=%d. Will try later\n",
+                                ret);
+                       list_add_tail(&forget->list, &fsvq->queued_reqs);
+                       schedule_delayed_work(&fsvq->dispatch_work,
+                                             msecs_to_jiffies(1));
+                       if (!in_flight)
+                               inc_in_flight_req(fsvq);
+                       /* Queue is full */
+                       ret = 1;
+               } else {
+                       pr_debug("virtio-fs: Could not queue FORGET: err=%d. Dropping it.\n",
+                                ret);
+                       kfree(forget);
+                       if (in_flight)
+                               dec_in_flight_req(fsvq);
+               }
+               goto out;
+       }
+
+       if (!in_flight)
+               inc_in_flight_req(fsvq);
+       notify = virtqueue_kick_prepare(vq);
+       spin_unlock(&fsvq->lock);
+
+       if (notify)
+               virtqueue_notify(vq);
+       return ret;
+out:
+       spin_unlock(&fsvq->lock);
+       return ret;
+}
+
 static void virtio_fs_hiprio_dispatch_work(struct work_struct *work)
 {
        struct virtio_fs_forget *forget;
        struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq,
                                                 dispatch_work.work);
-       struct virtqueue *vq = fsvq->vq;
-       struct scatterlist sg;
-       struct scatterlist *sgs[] = {&sg};
-       bool notify;
-       int ret;
-
        pr_debug("virtio-fs: worker %s called.\n", __func__);
        while (1) {
                spin_lock(&fsvq->lock);
@@ -335,43 +411,9 @@ static void virtio_fs_hiprio_dispatch_work(struct work_struct *work)
                }
 
                list_del(&forget->list);
-               if (!fsvq->connected) {
-                       dec_in_flight_req(fsvq);
-                       spin_unlock(&fsvq->lock);
-                       kfree(forget);
-                       continue;
-               }
-
-               sg_init_one(&sg, forget, sizeof(*forget));
-
-               /* Enqueue the request */
-               dev_dbg(&vq->vdev->dev, "%s\n", __func__);
-               ret = virtqueue_add_sgs(vq, sgs, 1, 0, forget, GFP_ATOMIC);
-               if (ret < 0) {
-                       if (ret == -ENOMEM || ret == -ENOSPC) {
-                               pr_debug("virtio-fs: Could not queue FORGET: err=%d. Will try later\n",
-                                        ret);
-                               list_add_tail(&forget->list,
-                                               &fsvq->queued_reqs);
-                               schedule_delayed_work(&fsvq->dispatch_work,
-                                               msecs_to_jiffies(1));
-                       } else {
-                               pr_debug("virtio-fs: Could not queue FORGET: err=%d. Dropping it.\n",
-                                        ret);
-                               dec_in_flight_req(fsvq);
-                               kfree(forget);
-                       }
-                       spin_unlock(&fsvq->lock);
-                       return;
-               }
-
-               notify = virtqueue_kick_prepare(vq);
                spin_unlock(&fsvq->lock);
-
-               if (notify)
-                       virtqueue_notify(vq);
-               pr_debug("virtio-fs: worker %s dispatched one forget request.\n",
-                        __func__);
+               if (send_forget_request(fsvq, forget, true))
+                       return;
        }
 }
 
@@ -556,6 +598,7 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
        INIT_LIST_HEAD(&fs->vqs[VQ_HIPRIO].end_reqs);
        INIT_DELAYED_WORK(&fs->vqs[VQ_HIPRIO].dispatch_work,
                        virtio_fs_hiprio_dispatch_work);
+       init_completion(&fs->vqs[VQ_HIPRIO].in_flight_zero);
        spin_lock_init(&fs->vqs[VQ_HIPRIO].lock);
 
        /* Initialize the requests virtqueues */
@@ -566,6 +609,7 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
                                  virtio_fs_request_dispatch_work);
                INIT_LIST_HEAD(&fs->vqs[i].queued_reqs);
                INIT_LIST_HEAD(&fs->vqs[i].end_reqs);
+               init_completion(&fs->vqs[i].in_flight_zero);
                snprintf(fs->vqs[i].name, sizeof(fs->vqs[i].name),
                         "requests.%u", i - VQ_REQUEST);
                callbacks[i] = virtio_fs_vq_done;
@@ -659,7 +703,7 @@ static void virtio_fs_remove(struct virtio_device *vdev)
        /* This device is going away. No one should get new reference */
        list_del_init(&fs->list);
        virtio_fs_stop_all_queues(fs);
-       virtio_fs_drain_all_queues(fs);
+       virtio_fs_drain_all_queues_locked(fs);
        vdev->config->reset(vdev);
        virtio_fs_cleanup_vqs(vdev, fs);
 
@@ -684,12 +728,12 @@ static int virtio_fs_restore(struct virtio_device *vdev)
 }
 #endif /* CONFIG_PM_SLEEP */
 
-const static struct virtio_device_id id_table[] = {
+static const struct virtio_device_id id_table[] = {
        { VIRTIO_ID_FS, VIRTIO_DEV_ANY_ID },
        {},
 };
 
-const static unsigned int feature_table[] = {};
+static const unsigned int feature_table[] = {};
 
 static struct virtio_driver virtio_fs_driver = {
        .driver.name            = KBUILD_MODNAME,
@@ -710,14 +754,10 @@ __releases(fiq->lock)
 {
        struct fuse_forget_link *link;
        struct virtio_fs_forget *forget;
-       struct scatterlist sg;
-       struct scatterlist *sgs[] = {&sg};
+       struct virtio_fs_forget_req *req;
        struct virtio_fs *fs;
-       struct virtqueue *vq;
        struct virtio_fs_vq *fsvq;
-       bool notify;
        u64 unique;
-       int ret;
 
        link = fuse_dequeue_forget(fiq, 1, NULL);
        unique = fuse_get_unique(fiq);
@@ -728,57 +768,19 @@ __releases(fiq->lock)
 
        /* Allocate a buffer for the request */
        forget = kmalloc(sizeof(*forget), GFP_NOFS | __GFP_NOFAIL);
+       req = &forget->req;
 
-       forget->ih = (struct fuse_in_header){
+       req->ih = (struct fuse_in_header){
                .opcode = FUSE_FORGET,
                .nodeid = link->forget_one.nodeid,
                .unique = unique,
-               .len = sizeof(*forget),
+               .len = sizeof(*req),
        };
-       forget->arg = (struct fuse_forget_in){
+       req->arg = (struct fuse_forget_in){
                .nlookup = link->forget_one.nlookup,
        };
 
-       sg_init_one(&sg, forget, sizeof(*forget));
-
-       /* Enqueue the request */
-       spin_lock(&fsvq->lock);
-
-       if (!fsvq->connected) {
-               kfree(forget);
-               spin_unlock(&fsvq->lock);
-               goto out;
-       }
-
-       vq = fsvq->vq;
-       dev_dbg(&vq->vdev->dev, "%s\n", __func__);
-
-       ret = virtqueue_add_sgs(vq, sgs, 1, 0, forget, GFP_ATOMIC);
-       if (ret < 0) {
-               if (ret == -ENOMEM || ret == -ENOSPC) {
-                       pr_debug("virtio-fs: Could not queue FORGET: err=%d. Will try later.\n",
-                                ret);
-                       list_add_tail(&forget->list, &fsvq->queued_reqs);
-                       schedule_delayed_work(&fsvq->dispatch_work,
-                                       msecs_to_jiffies(1));
-                       inc_in_flight_req(fsvq);
-               } else {
-                       pr_debug("virtio-fs: Could not queue FORGET: err=%d. Dropping it.\n",
-                                ret);
-                       kfree(forget);
-               }
-               spin_unlock(&fsvq->lock);
-               goto out;
-       }
-
-       inc_in_flight_req(fsvq);
-       notify = virtqueue_kick_prepare(vq);
-
-       spin_unlock(&fsvq->lock);
-
-       if (notify)
-               virtqueue_notify(vq);
-out:
+       send_forget_request(fsvq, forget, false);
        kfree(link);
 }
 
@@ -1026,7 +1028,7 @@ __releases(fiq->lock)
        }
 }
 
-const static struct fuse_iqueue_ops virtio_fs_fiq_ops = {
+static const struct fuse_iqueue_ops virtio_fs_fiq_ops = {
        .wake_forget_and_unlock         = virtio_fs_wake_forget_and_unlock,
        .wake_interrupt_and_unlock      = virtio_fs_wake_interrupt_and_unlock,
        .wake_pending_and_unlock        = virtio_fs_wake_pending_and_unlock,
index b9fe975..9c6df72 100644 (file)
@@ -133,7 +133,7 @@ static int gfs2_write_full_page(struct page *page, get_block_t *get_block,
         * the  page size, the remaining memory is zeroed when mapped, and
         * writes to that region are not written out to the file."
         */
-       offset = i_size & (PAGE_SIZE-1);
+       offset = i_size & (PAGE_SIZE - 1);
        if (page->index == end_index && offset)
                zero_user_segment(page, offset, PAGE_SIZE);
 
@@ -497,7 +497,7 @@ static int __gfs2_readpage(void *file, struct page *page)
                error = mpage_readpage(page, gfs2_block_map);
        }
 
-       if (unlikely(test_bit(SDF_WITHDRAWN, &sdp->sd_flags)))
+       if (unlikely(gfs2_withdrawn(sdp)))
                return -EIO;
 
        return error;
@@ -614,7 +614,7 @@ static int gfs2_readpages(struct file *file, struct address_space *mapping,
        gfs2_glock_dq(&gh);
 out_uninit:
        gfs2_holder_uninit(&gh);
-       if (unlikely(test_bit(SDF_WITHDRAWN, &sdp->sd_flags)))
+       if (unlikely(gfs2_withdrawn(sdp)))
                ret = -EIO;
        return ret;
 }
index 5161032..08f6fbb 100644 (file)
@@ -2441,8 +2441,16 @@ int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length)
        struct inode *inode = file_inode(file);
        struct gfs2_inode *ip = GFS2_I(inode);
        struct gfs2_sbd *sdp = GFS2_SB(inode);
+       unsigned int blocksize = i_blocksize(inode);
+       loff_t start, end;
        int error;
 
+       start = round_down(offset, blocksize);
+       end = round_up(offset + length, blocksize) - 1;
+       error = filemap_write_and_wait_range(inode->i_mapping, start, end);
+       if (error)
+               return error;
+
        if (gfs2_is_jdata(ip))
                error = gfs2_trans_begin(sdp, RES_DINODE + 2 * RES_JDATA,
                                         GFS2_JTRUNC_REVOKES);
@@ -2456,9 +2464,8 @@ int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length)
                if (error)
                        goto out;
        } else {
-               unsigned int start_off, end_len, blocksize;
+               unsigned int start_off, end_len;
 
-               blocksize = i_blocksize(inode);
                start_off = offset & (blocksize - 1);
                end_len = (offset + length) & (blocksize - 1);
                if (start_off) {
index d07a295..9d58295 100644 (file)
@@ -407,27 +407,28 @@ static void gfs2_size_hint(struct file *filep, loff_t offset, size_t size)
 /**
  * gfs2_allocate_page_backing - Allocate blocks for a write fault
  * @page: The (locked) page to allocate backing for
+ * @length: Size of the allocation
  *
  * We try to allocate all the blocks required for the page in one go.  This
  * might fail for various reasons, so we keep trying until all the blocks to
  * back this page are allocated.  If some of the blocks are already allocated,
  * that is ok too.
  */
-static int gfs2_allocate_page_backing(struct page *page)
+static int gfs2_allocate_page_backing(struct page *page, unsigned int length)
 {
        u64 pos = page_offset(page);
-       u64 size = PAGE_SIZE;
 
        do {
                struct iomap iomap = { };
 
-               if (gfs2_iomap_get_alloc(page->mapping->host, pos, 1, &iomap))
+               if (gfs2_iomap_get_alloc(page->mapping->host, pos, length, &iomap))
                        return -EIO;
 
-               iomap.length = min(iomap.length, size);
-               size -= iomap.length;
+               if (length < iomap.length)
+                       iomap.length = length;
+               length -= iomap.length;
                pos += iomap.length;
-       } while (size > 0);
+       } while (length > 0);
 
        return 0;
 }
@@ -448,10 +449,10 @@ static vm_fault_t gfs2_page_mkwrite(struct vm_fault *vmf)
        struct gfs2_inode *ip = GFS2_I(inode);
        struct gfs2_sbd *sdp = GFS2_SB(inode);
        struct gfs2_alloc_parms ap = { .aflags = 0, };
-       unsigned long last_index;
-       u64 pos = page_offset(page);
+       u64 offset = page_offset(page);
        unsigned int data_blocks, ind_blocks, rblocks;
        struct gfs2_holder gh;
+       unsigned int length;
        loff_t size;
        int ret;
 
@@ -461,20 +462,39 @@ static vm_fault_t gfs2_page_mkwrite(struct vm_fault *vmf)
        if (ret)
                goto out;
 
-       gfs2_size_hint(vmf->vma->vm_file, pos, PAGE_SIZE);
-
        gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
        ret = gfs2_glock_nq(&gh);
        if (ret)
                goto out_uninit;
 
+       /* Check page index against inode size */
+       size = i_size_read(inode);
+       if (offset >= size) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
        /* Update file times before taking page lock */
        file_update_time(vmf->vma->vm_file);
 
+       /* page is wholly or partially inside EOF */
+       if (offset > size - PAGE_SIZE)
+               length = offset_in_page(size);
+       else
+               length = PAGE_SIZE;
+
+       gfs2_size_hint(vmf->vma->vm_file, offset, length);
+
        set_bit(GLF_DIRTY, &ip->i_gl->gl_flags);
        set_bit(GIF_SW_PAGED, &ip->i_flags);
 
-       if (!gfs2_write_alloc_required(ip, pos, PAGE_SIZE)) {
+       /*
+        * iomap_writepage / iomap_writepages currently don't support inline
+        * files, so always unstuff here.
+        */
+
+       if (!gfs2_is_stuffed(ip) &&
+           !gfs2_write_alloc_required(ip, offset, length)) {
                lock_page(page);
                if (!PageUptodate(page) || page->mapping != inode->i_mapping) {
                        ret = -EAGAIN;
@@ -487,7 +507,7 @@ static vm_fault_t gfs2_page_mkwrite(struct vm_fault *vmf)
        if (ret)
                goto out_unlock;
 
-       gfs2_write_calc_reserv(ip, PAGE_SIZE, &data_blocks, &ind_blocks);
+       gfs2_write_calc_reserv(ip, length, &data_blocks, &ind_blocks);
        ap.target = data_blocks + ind_blocks;
        ret = gfs2_quota_lock_check(ip, &ap);
        if (ret)
@@ -508,13 +528,6 @@ static vm_fault_t gfs2_page_mkwrite(struct vm_fault *vmf)
                goto out_trans_fail;
 
        lock_page(page);
-       ret = -EINVAL;
-       size = i_size_read(inode);
-       last_index = (size - 1) >> PAGE_SHIFT;
-       /* Check page index against inode size */
-       if (size == 0 || (page->index > last_index))
-               goto out_trans_end;
-
        ret = -EAGAIN;
        /* If truncated, we must retry the operation, we may have raced
         * with the glock demotion code.
@@ -527,7 +540,7 @@ static vm_fault_t gfs2_page_mkwrite(struct vm_fault *vmf)
        if (gfs2_is_stuffed(ip))
                ret = gfs2_unstuff_dinode(ip, page);
        if (ret == 0)
-               ret = gfs2_allocate_page_backing(page);
+               ret = gfs2_allocate_page_backing(page, length);
 
 out_trans_end:
        if (ret)
@@ -961,6 +974,7 @@ out:
        brelse(dibh);
        return error;
 }
+
 /**
  * calc_max_reserv() - Reverse of write_calc_reserv. Given a number of
  *                     blocks, determine how many bytes can be written.
@@ -1208,7 +1222,7 @@ static int gfs2_lock(struct file *file, int cmd, struct file_lock *fl)
                cmd = F_SETLK;
                fl->fl_type = F_UNLCK;
        }
-       if (unlikely(test_bit(SDF_WITHDRAWN, &sdp->sd_flags))) {
+       if (unlikely(gfs2_withdrawn(sdp))) {
                if (fl->fl_type == F_UNLCK)
                        locks_lock_file_wait(file, fl);
                return -EIO;
index 0290a22..b7123de 100644 (file)
@@ -549,7 +549,7 @@ __acquires(&gl->gl_lockref.lock)
        unsigned int lck_flags = (unsigned int)(gh ? gh->gh_flags : 0);
        int ret;
 
-       if (unlikely(test_bit(SDF_WITHDRAWN, &sdp->sd_flags)) &&
+       if (unlikely(gfs2_withdrawn(sdp)) &&
            target != LM_ST_UNLOCKED)
                return;
        lck_flags &= (LM_FLAG_TRY | LM_FLAG_TRY_1CB | LM_FLAG_NOEXP |
@@ -558,7 +558,14 @@ __acquires(&gl->gl_lockref.lock)
        GLOCK_BUG_ON(gl, gl->gl_state == gl->gl_target);
        if ((target == LM_ST_UNLOCKED || target == LM_ST_DEFERRED) &&
            glops->go_inval) {
-               set_bit(GLF_INVALIDATE_IN_PROGRESS, &gl->gl_flags);
+               /*
+                * If another process is already doing the invalidate, let that
+                * finish first.  The glock state machine will get back to this
+                * holder again later.
+                */
+               if (test_and_set_bit(GLF_INVALIDATE_IN_PROGRESS,
+                                    &gl->gl_flags))
+                       return;
                do_error(gl, 0); /* Fail queued try locks */
        }
        gl->gl_req = target;
@@ -586,8 +593,7 @@ __acquires(&gl->gl_lockref.lock)
                }
                else if (ret) {
                        fs_err(sdp, "lm_lock ret %d\n", ret);
-                       GLOCK_BUG_ON(gl, !test_bit(SDF_WITHDRAWN,
-                                                  &sdp->sd_flags));
+                       GLOCK_BUG_ON(gl, !gfs2_withdrawn(sdp));
                }
        } else { /* lock_nolock */
                finish_xmote(gl, target);
@@ -1191,7 +1197,7 @@ int gfs2_glock_nq(struct gfs2_holder *gh)
        struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
        int error = 0;
 
-       if (unlikely(test_bit(SDF_WITHDRAWN, &sdp->sd_flags)))
+       if (unlikely(gfs2_withdrawn(sdp)))
                return -EIO;
 
        if (test_bit(GLF_LRU, &gl->gl_flags))
index ff21369..4ede1f1 100644 (file)
@@ -350,7 +350,7 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
                ip->i_inode.i_rdev = MKDEV(be32_to_cpu(str->di_major),
                                           be32_to_cpu(str->di_minor));
                break;
-       };
+       }
 
        i_uid_write(&ip->i_inode, be32_to_cpu(str->di_uid));
        i_gid_write(&ip->i_inode, be32_to_cpu(str->di_gid));
@@ -540,7 +540,7 @@ static int freeze_go_xmote_bh(struct gfs2_glock *gl, struct gfs2_holder *gh)
                        gfs2_consist(sdp);
 
                /*  Initialize some head of the log stuff  */
-               if (!test_bit(SDF_WITHDRAWN, &sdp->sd_flags)) {
+               if (!gfs2_withdrawn(sdp)) {
                        sdp->sd_log_sequence = head.lh_sequence + 1;
                        gfs2_log_pointers_init(sdp, head.lh_blkno);
                }
index e1e18fb..dafef10 100644 (file)
@@ -656,7 +656,6 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
        inode->i_rdev = dev;
        inode->i_size = size;
        inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
-       gfs2_set_inode_blocks(inode, 1);
        munge_mode_uid_gid(dip, inode);
        check_and_update_goal(dip);
        ip->i_goal = dip->i_goal;
@@ -712,7 +711,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
 
        error = gfs2_trans_begin(sdp, blocks, 0);
        if (error)
-               goto fail_gunlock2;
+               goto fail_free_inode;
 
        if (blocks > 1) {
                ip->i_eattr = ip->i_no_addr + 1;
@@ -723,7 +722,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
 
        error = gfs2_glock_get(sdp, ip->i_no_addr, &gfs2_iopen_glops, CREATE, &io_gl);
        if (error)
-               goto fail_gunlock2;
+               goto fail_free_inode;
 
        BUG_ON(test_and_set_bit(GLF_INODE_CREATING, &io_gl->gl_flags));
 
@@ -732,7 +731,6 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
                goto fail_gunlock2;
 
        glock_set_object(ip->i_iopen_gh.gh_gl, ip);
-       gfs2_glock_put(io_gl);
        gfs2_set_iop(inode);
        insert_inode_hash(inode);
 
@@ -765,6 +763,8 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
 
        mark_inode_dirty(inode);
        d_instantiate(dentry, inode);
+       /* After instantiate, errors should result in evict which will destroy
+        * both inode and iopen glocks properly. */
        if (file) {
                file->f_mode |= FMODE_CREATED;
                error = finish_open(file, dentry, gfs2_open_common);
@@ -772,15 +772,15 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
        gfs2_glock_dq_uninit(ghs);
        gfs2_glock_dq_uninit(ghs + 1);
        clear_bit(GLF_INODE_CREATING, &io_gl->gl_flags);
+       gfs2_glock_put(io_gl);
        return error;
 
 fail_gunlock3:
        glock_clear_object(io_gl, ip);
        gfs2_glock_dq_uninit(&ip->i_iopen_gh);
-       gfs2_glock_put(io_gl);
 fail_gunlock2:
-       if (io_gl)
-               clear_bit(GLF_INODE_CREATING, &io_gl->gl_flags);
+       clear_bit(GLF_INODE_CREATING, &io_gl->gl_flags);
+       gfs2_glock_put(io_gl);
 fail_free_inode:
        if (ip->i_gl) {
                glock_clear_object(ip->i_gl, ip);
@@ -1475,7 +1475,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
                        error = -EEXIST;
                default:
                        goto out_gunlock;
-               };
+               }
 
                if (odip != ndip) {
                        if (!ndip->i_inode.i_nlink) {
index 58e237f..eb3f2e7 100644 (file)
@@ -31,6 +31,8 @@
 #include "dir.h"
 #include "trace_gfs2.h"
 
+static void gfs2_log_shutdown(struct gfs2_sbd *sdp);
+
 /**
  * gfs2_struct2blk - compute stuff
  * @sdp: the filesystem
@@ -159,7 +161,8 @@ restart:
        list_for_each_entry_reverse(tr, head, tr_list) {
                if (wbc->nr_to_write <= 0)
                        break;
-               if (gfs2_ail1_start_one(sdp, wbc, tr, &withdraw))
+               if (gfs2_ail1_start_one(sdp, wbc, tr, &withdraw) &&
+                   !gfs2_withdrawn(sdp))
                        goto restart;
        }
        spin_unlock(&sdp->sd_ail_lock);
@@ -609,6 +612,14 @@ void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd)
        list_add(&bd->bd_list, &sdp->sd_log_revokes);
 }
 
+void gfs2_glock_remove_revoke(struct gfs2_glock *gl)
+{
+       if (atomic_dec_return(&gl->gl_revokes) == 0) {
+               clear_bit(GLF_LFLUSH, &gl->gl_flags);
+               gfs2_glock_queue_put(gl);
+       }
+}
+
 void gfs2_write_revokes(struct gfs2_sbd *sdp)
 {
        struct gfs2_trans *tr;
@@ -682,12 +693,16 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
 {
        struct gfs2_log_header *lh;
        u32 hash, crc;
-       struct page *page = mempool_alloc(gfs2_page_pool, GFP_NOIO);
+       struct page *page;
        struct gfs2_statfs_change_host *l_sc = &sdp->sd_statfs_local;
        struct timespec64 tv;
        struct super_block *sb = sdp->sd_vfs;
        u64 dblock;
 
+       if (gfs2_withdrawn(sdp))
+               goto out;
+
+       page = mempool_alloc(gfs2_page_pool, GFP_NOIO);
        lh = page_address(page);
        clear_page(lh);
 
@@ -707,7 +722,7 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
        lh->lh_nsec = cpu_to_be32(tv.tv_nsec);
        lh->lh_sec = cpu_to_be64(tv.tv_sec);
        if (!list_empty(&jd->extent_list))
-               dblock = gfs2_log_bmap(sdp);
+               dblock = gfs2_log_bmap(jd, lblock);
        else {
                int ret = gfs2_lblk_to_dblk(jd->jd_inode, lblock, &dblock);
                if (gfs2_assert_withdraw(sdp, ret == 0))
@@ -740,6 +755,7 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
 
        gfs2_log_write(sdp, page, sb->s_blocksize, 0, dblock);
        gfs2_log_submit_bio(&sdp->sd_log_bio, REQ_OP_WRITE | op_flags);
+out:
        log_flush_wait(sdp);
 }
 
@@ -768,6 +784,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags)
        sdp->sd_log_idle = (tail == sdp->sd_log_flush_head);
        gfs2_write_log_header(sdp, sdp->sd_jdesc, sdp->sd_log_sequence++, tail,
                              sdp->sd_log_flush_head, flags, op_flags);
+       gfs2_log_incr_head(sdp);
 
        if (sdp->sd_log_tail != tail)
                log_pull_tail(sdp, tail);
@@ -948,7 +965,7 @@ void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
  *
  */
 
-void gfs2_log_shutdown(struct gfs2_sbd *sdp)
+static void gfs2_log_shutdown(struct gfs2_sbd *sdp)
 {
        gfs2_assert_withdraw(sdp, !sdp->sd_log_blks_reserved);
        gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke);
index 2315fca..2ff163a 100644 (file)
@@ -74,9 +74,9 @@ extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl,
 extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans);
 extern void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc);
 
-extern void gfs2_log_shutdown(struct gfs2_sbd *sdp);
 extern int gfs2_logd(void *data);
 extern void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd);
+extern void gfs2_glock_remove_revoke(struct gfs2_glock *gl);
 extern void gfs2_write_revokes(struct gfs2_sbd *sdp);
 
 #endif /* __LOG_DOT_H__ */
index 5b17979..55fed7d 100644 (file)
@@ -129,7 +129,7 @@ static void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh,
        atomic_dec(&sdp->sd_log_pinned);
 }
 
-static void gfs2_log_incr_head(struct gfs2_sbd *sdp)
+void gfs2_log_incr_head(struct gfs2_sbd *sdp)
 {
        BUG_ON((sdp->sd_log_flush_head == sdp->sd_log_tail) &&
               (sdp->sd_log_flush_head != sdp->sd_log_head));
@@ -138,18 +138,13 @@ static void gfs2_log_incr_head(struct gfs2_sbd *sdp)
                sdp->sd_log_flush_head = 0;
 }
 
-u64 gfs2_log_bmap(struct gfs2_sbd *sdp)
+u64 gfs2_log_bmap(struct gfs2_jdesc *jd, unsigned int lblock)
 {
-       unsigned int lbn = sdp->sd_log_flush_head;
        struct gfs2_journal_extent *je;
-       u64 block;
 
-       list_for_each_entry(je, &sdp->sd_jdesc->extent_list, list) {
-               if ((lbn >= je->lblock) && (lbn < (je->lblock + je->blocks))) {
-                       block = je->dblock + lbn - je->lblock;
-                       gfs2_log_incr_head(sdp);
-                       return block;
-               }
+       list_for_each_entry(je, &jd->extent_list, list) {
+               if (lblock >= je->lblock && lblock < je->lblock + je->blocks)
+                       return je->dblock + lblock - je->lblock;
        }
 
        return -1;
@@ -351,8 +346,11 @@ void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
 
 static void gfs2_log_write_bh(struct gfs2_sbd *sdp, struct buffer_head *bh)
 {
-       gfs2_log_write(sdp, bh->b_page, bh->b_size, bh_offset(bh),
-                      gfs2_log_bmap(sdp));
+       u64 dblock;
+
+       dblock = gfs2_log_bmap(sdp->sd_jdesc, sdp->sd_log_flush_head);
+       gfs2_log_incr_head(sdp);
+       gfs2_log_write(sdp, bh->b_page, bh->b_size, bh_offset(bh), dblock);
 }
 
 /**
@@ -369,8 +367,11 @@ static void gfs2_log_write_bh(struct gfs2_sbd *sdp, struct buffer_head *bh)
 void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page)
 {
        struct super_block *sb = sdp->sd_vfs;
-       gfs2_log_write(sdp, page, sb->s_blocksize, 0,
-                      gfs2_log_bmap(sdp));
+       u64 dblock;
+
+       dblock = gfs2_log_bmap(sdp->sd_jdesc, sdp->sd_log_flush_head);
+       gfs2_log_incr_head(sdp);
+       gfs2_log_write(sdp, page, sb->s_blocksize, 0, dblock);
 }
 
 /**
@@ -882,10 +883,7 @@ static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
                bd = list_entry(head->next, struct gfs2_bufdata, bd_list);
                list_del_init(&bd->bd_list);
                gl = bd->bd_gl;
-               if (atomic_dec_return(&gl->gl_revokes) == 0) {
-                       clear_bit(GLF_LFLUSH, &gl->gl_flags);
-                       gfs2_glock_queue_put(gl);
-               }
+               gfs2_glock_remove_revoke(gl);
                kmem_cache_free(gfs2_bufdata_cachep, bd);
        }
 }
index 9c05995..9c5e4e4 100644 (file)
@@ -18,7 +18,8 @@
         ~(2 * sizeof(__be64) - 1))
 
 extern const struct gfs2_log_operations *gfs2_log_ops[];
-extern u64 gfs2_log_bmap(struct gfs2_sbd *sdp);
+extern void gfs2_log_incr_head(struct gfs2_sbd *sdp);
+extern u64 gfs2_log_bmap(struct gfs2_jdesc *jd, unsigned int lbn);
 extern void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
                           unsigned size, unsigned offset, u64 blkno);
 extern void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page);
index 662ef36..0c37729 100644 (file)
@@ -251,7 +251,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
        struct buffer_head *bh, *bhs[2];
        int num = 0;
 
-       if (unlikely(test_bit(SDF_WITHDRAWN, &sdp->sd_flags))) {
+       if (unlikely(gfs2_withdrawn(sdp))) {
                *bhp = NULL;
                return -EIO;
        }
@@ -309,7 +309,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
 
 int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh)
 {
-       if (unlikely(test_bit(SDF_WITHDRAWN, &sdp->sd_flags)))
+       if (unlikely(gfs2_withdrawn(sdp)))
                return -EIO;
 
        wait_on_buffer(bh);
@@ -320,7 +320,7 @@ int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh)
                        gfs2_io_error_bh_wd(sdp, bh);
                return -EIO;
        }
-       if (unlikely(test_bit(SDF_WITHDRAWN, &sdp->sd_flags)))
+       if (unlikely(gfs2_withdrawn(sdp)))
                return -EIO;
 
        return 0;
index 18daf49..e8b7b0c 100644 (file)
@@ -1006,8 +1006,7 @@ hostdata_error:
 void gfs2_lm_unmount(struct gfs2_sbd *sdp)
 {
        const struct lm_lockops *lm = sdp->sd_lockstruct.ls_ops;
-       if (likely(!test_bit(SDF_WITHDRAWN, &sdp->sd_flags)) &&
-           lm->lm_unmount)
+       if (likely(!gfs2_withdrawn(sdp)) && lm->lm_unmount)
                lm->lm_unmount(sdp);
 }
 
@@ -1328,7 +1327,7 @@ static const struct fs_parameter_enum gfs2_param_enums[] = {
        {}
 };
 
-const struct fs_parameter_description gfs2_fs_parameters = {
+static const struct fs_parameter_description gfs2_fs_parameters = {
        .name = "gfs2",
        .specs = gfs2_param_specs,
        .enums = gfs2_param_enums,
index 7c016a0..e9f9304 100644 (file)
@@ -1273,7 +1273,7 @@ int gfs2_quota_sync(struct super_block *sb, int type)
 {
        struct gfs2_sbd *sdp = sb->s_fs_info;
        struct gfs2_quota_data **qda;
-       unsigned int max_qd = PAGE_SIZE/sizeof(struct gfs2_holder);
+       unsigned int max_qd = PAGE_SIZE / sizeof(struct gfs2_holder);
        unsigned int num_qd;
        unsigned int x;
        int error = 0;
@@ -1475,7 +1475,7 @@ static void quotad_error(struct gfs2_sbd *sdp, const char *msg, int error)
 {
        if (error == 0 || error == -EROFS)
                return;
-       if (!test_bit(SDF_WITHDRAWN, &sdp->sd_flags)) {
+       if (!gfs2_withdrawn(sdp)) {
                fs_err(sdp, "gfs2_quotad: %s error %d\n", msg, error);
                sdp->sd_log_error = error;
                wake_up(&sdp->sd_logd_waitq);
index c529f87..85f830e 100644 (file)
@@ -263,11 +263,13 @@ static void clean_journal(struct gfs2_jdesc *jd,
        u32 lblock = head->lh_blkno;
 
        gfs2_replay_incr_blk(jd, &lblock);
-       if (jd->jd_jid == sdp->sd_lockstruct.ls_jid)
-               sdp->sd_log_flush_head = lblock;
        gfs2_write_log_header(sdp, jd, head->lh_sequence + 1, 0, lblock,
                              GFS2_LOG_HEAD_UNMOUNT | GFS2_LOG_HEAD_RECOVERY,
                              REQ_PREFLUSH | REQ_FUA | REQ_META | REQ_SYNC);
+       if (jd->jd_jid == sdp->sd_lockstruct.ls_jid) {
+               sdp->sd_log_flush_head = lblock;
+               gfs2_log_incr_head(sdp);
+       }
 }
 
 
@@ -326,7 +328,7 @@ void gfs2_recover_func(struct work_struct *work)
 
                default:
                        goto fail;
-               };
+               }
 
                error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
                                           LM_FLAG_NOEXP | GL_NOCACHE, &ji_gh);
index 5fa1eec..68cc7c2 100644 (file)
@@ -399,8 +399,7 @@ struct lfcc {
  * Returns: errno
  */
 
-static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp,
-                                   struct gfs2_holder *freeze_gh)
+static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp)
 {
        struct gfs2_inode *ip;
        struct gfs2_jdesc *jd;
@@ -425,7 +424,9 @@ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp,
        }
 
        error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_EXCLUSIVE,
-                                  GL_NOCACHE, freeze_gh);
+                                  GL_NOCACHE, &sdp->sd_freeze_gh);
+       if (error)
+               goto out;
 
        list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
                error = gfs2_jdesc_check(jd);
@@ -441,7 +442,7 @@ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp,
        }
 
        if (error)
-               gfs2_glock_dq_uninit(freeze_gh);
+               gfs2_glock_dq_uninit(&sdp->sd_freeze_gh);
 
 out:
        while (!list_empty(&list)) {
@@ -553,7 +554,7 @@ static void gfs2_dirty_inode(struct inode *inode, int flags)
 
        if (!(flags & I_DIRTY_INODE))
                return;
-       if (unlikely(test_bit(SDF_WITHDRAWN, &sdp->sd_flags)))
+       if (unlikely(gfs2_withdrawn(sdp)))
                return;
        if (!gfs2_glock_is_locked_by_me(ip->i_gl)) {
                ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
@@ -602,7 +603,7 @@ int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
 
        error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_SHARED, GL_NOCACHE,
                                   &freeze_gh);
-       if (error && !test_bit(SDF_WITHDRAWN, &sdp->sd_flags))
+       if (error && !gfs2_withdrawn(sdp))
                return error;
 
        flush_workqueue(gfs2_delete_workqueue);
@@ -761,21 +762,25 @@ static int gfs2_freeze(struct super_block *sb)
        if (atomic_read(&sdp->sd_freeze_state) != SFS_UNFROZEN)
                goto out;
 
-       if (test_bit(SDF_WITHDRAWN, &sdp->sd_flags)) {
-               error = -EINVAL;
-               goto out;
-       }
-
        for (;;) {
-               error = gfs2_lock_fs_check_clean(sdp, &sdp->sd_freeze_gh);
+               if (gfs2_withdrawn(sdp)) {
+                       error = -EINVAL;
+                       goto out;
+               }
+
+               error = gfs2_lock_fs_check_clean(sdp);
                if (!error)
                        break;
 
                if (error == -EBUSY)
                        fs_err(sdp, "waiting for recovery before freeze\n");
-               else
+               else if (error == -EIO) {
+                       fs_err(sdp, "Fatal IO error: cannot freeze gfs2 due "
+                              "to recovery error.\n");
+                       goto out;
+               } else {
                        fs_err(sdp, "error freezing FS: %d\n", error);
-
+               }
                fs_err(sdp, "retrying...\n");
                msleep(1000);
        }
index dd15b8e..8ccb68f 100644 (file)
@@ -118,7 +118,7 @@ static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
 
 static ssize_t withdraw_show(struct gfs2_sbd *sdp, char *buf)
 {
-       unsigned int b = test_bit(SDF_WITHDRAWN, &sdp->sd_flags);
+       unsigned int b = gfs2_withdrawn(sdp);
        return snprintf(buf, PAGE_SIZE, "%u\n", b);
 }
 
index 35e3059..9d42273 100644 (file)
@@ -262,6 +262,8 @@ void gfs2_trans_remove_revoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len)
                        list_del_init(&bd->bd_list);
                        gfs2_assert_withdraw(sdp, sdp->sd_log_num_revoke);
                        sdp->sd_log_num_revoke--;
+                       if (bd->bd_gl)
+                               gfs2_glock_remove_revoke(bd->bd_gl);
                        kmem_cache_free(gfs2_bufdata_cachep, bd);
                        tr->tr_num_revoke--;
                        if (--n == 0)
index c451591..ec600b4 100644 (file)
@@ -258,7 +258,7 @@ void gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh,
                        const char *function, char *file, unsigned int line,
                        bool withdraw)
 {
-       if (!test_bit(SDF_WITHDRAWN, &sdp->sd_flags))
+       if (!gfs2_withdrawn(sdp))
                fs_err(sdp,
                       "fatal: I/O error\n"
                       "  block = %llu\n"
index 4b68b2c..f2702bc 100644 (file)
@@ -164,6 +164,15 @@ static inline unsigned int gfs2_tune_get_i(struct gfs2_tune *gt,
        return x;
 }
 
+/**
+ * gfs2_withdrawn - test whether the file system is withdrawing or withdrawn
+ * @sdp: the superblock
+ */
+static inline bool gfs2_withdrawn(struct gfs2_sbd *sdp)
+{
+       return test_bit(SDF_WITHDRAWN, &sdp->sd_flags);
+}
+
 #define gfs2_tune_get(sdp, field) \
 gfs2_tune_get_i(&(sdp)->sd_tune, &(sdp)->sd_tune.field)
 
index 91b85df..74b4050 100644 (file)
@@ -111,7 +111,7 @@ struct io_wq {
 
        struct task_struct *manager;
        struct user_struct *user;
-       struct cred *creds;
+       const struct cred *creds;
        struct mm_struct *mm;
        refcount_t refs;
        struct completion done;
index 600e015..7c333a2 100644 (file)
@@ -52,6 +52,7 @@ static inline void wq_node_del(struct io_wq_work_list *list,
                list->last = prev;
        if (prev)
                prev->next = node->next;
+       node->next = NULL;
 }
 
 #define wq_list_for_each(pos, prv, head)                       \
@@ -87,7 +88,7 @@ typedef void (put_work_fn)(struct io_wq_work *);
 struct io_wq_data {
        struct mm_struct *mm;
        struct user_struct *user;
-       struct cred *creds;
+       const struct cred *creds;
 
        get_work_fn *get_work;
        put_work_fn *put_work;
@@ -118,10 +119,6 @@ static inline void io_wq_worker_sleeping(struct task_struct *tsk)
 static inline void io_wq_worker_running(struct task_struct *tsk)
 {
 }
-#endif
+#endif /* CONFIG_IO_WQ */
 
-static inline bool io_wq_current_is_worker(void)
-{
-       return in_task() && (current->flags & PF_IO_WORKER);
-}
-#endif
+#endif /* INTERNAL_IO_WQ_H */
index ec53aa7..405be10 100644 (file)
@@ -145,7 +145,7 @@ struct io_rings {
        /*
         * Number of completion events lost because the queue was full;
         * this should be avoided by the application by making sure
-        * there are not more requests pending thatn there is space in
+        * there are not more requests pending than there is space in
         * the completion queue.
         *
         * Written by the kernel, shouldn't be modified by the
@@ -238,7 +238,7 @@ struct io_ring_ctx {
 
        struct user_struct      *user;
 
-       struct cred             *creds;
+       const struct cred       *creds;
 
        /* 0 is for ctx quiesce/reinit/free, 1 is for sqo_thread started */
        struct completion       *completions;
@@ -275,7 +275,8 @@ struct io_ring_ctx {
                 * manipulate the list, hence no extra locking is needed there.
                 */
                struct list_head        poll_list;
-               struct rb_root          cancel_tree;
+               struct hlist_head       *cancel_hash;
+               unsigned                cancel_hash_bits;
 
                spinlock_t              inflight_lock;
                struct list_head        inflight_list;
@@ -303,9 +304,32 @@ struct io_timeout_data {
        u32                             seq_offset;
 };
 
-struct io_timeout {
-       struct file                     *file;
-       struct io_timeout_data          *data;
+struct io_async_connect {
+       struct sockaddr_storage         address;
+};
+
+struct io_async_msghdr {
+       struct iovec                    fast_iov[UIO_FASTIOV];
+       struct iovec                    *iov;
+       struct sockaddr __user          *uaddr;
+       struct msghdr                   msg;
+};
+
+struct io_async_rw {
+       struct iovec                    fast_iov[UIO_FASTIOV];
+       struct iovec                    *iov;
+       ssize_t                         nr_segs;
+       ssize_t                         size;
+};
+
+struct io_async_ctx {
+       struct io_uring_sqe             sqe;
+       union {
+               struct io_async_rw      rw;
+               struct io_async_msghdr  msg;
+               struct io_async_connect connect;
+               struct io_timeout_data  timeout;
+       };
 };
 
 /*
@@ -319,10 +343,10 @@ struct io_kiocb {
                struct file             *file;
                struct kiocb            rw;
                struct io_poll_iocb     poll;
-               struct io_timeout       timeout;
        };
 
        const struct io_uring_sqe       *sqe;
+       struct io_async_ctx             *io;
        struct file                     *ring_file;
        int                             ring_fd;
        bool                            has_user;
@@ -332,7 +356,7 @@ struct io_kiocb {
        struct io_ring_ctx      *ctx;
        union {
                struct list_head        list;
-               struct rb_node          rb_node;
+               struct hlist_node       hash_node;
        };
        struct list_head        link_list;
        unsigned int            flags;
@@ -353,7 +377,6 @@ struct io_kiocb {
 #define REQ_F_TIMEOUT_NOSEQ    8192    /* no timeout sequence */
 #define REQ_F_INFLIGHT         16384   /* on inflight list */
 #define REQ_F_COMP_LOCKED      32768   /* completion under lock */
-#define REQ_F_FREE_SQE         65536   /* free sqe if not async queued */
        u64                     user_data;
        u32                     result;
        u32                     sequence;
@@ -422,6 +445,7 @@ static void io_ring_ctx_ref_free(struct percpu_ref *ref)
 static struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
 {
        struct io_ring_ctx *ctx;
+       int hash_bits;
 
        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
        if (!ctx)
@@ -435,6 +459,21 @@ static struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
        if (!ctx->completions)
                goto err;
 
+       /*
+        * Use 5 bits less than the max cq entries, that should give us around
+        * 32 entries per hash list if totally full and uniformly spread.
+        */
+       hash_bits = ilog2(p->cq_entries);
+       hash_bits -= 5;
+       if (hash_bits <= 0)
+               hash_bits = 1;
+       ctx->cancel_hash_bits = hash_bits;
+       ctx->cancel_hash = kmalloc((1U << hash_bits) * sizeof(struct hlist_head),
+                                       GFP_KERNEL);
+       if (!ctx->cancel_hash)
+               goto err;
+       __hash_init(ctx->cancel_hash, 1U << hash_bits);
+
        if (percpu_ref_init(&ctx->refs, io_ring_ctx_ref_free,
                            PERCPU_REF_ALLOW_REINIT, GFP_KERNEL))
                goto err;
@@ -448,7 +487,6 @@ static struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
        init_waitqueue_head(&ctx->wait);
        spin_lock_init(&ctx->completion_lock);
        INIT_LIST_HEAD(&ctx->poll_list);
-       ctx->cancel_tree = RB_ROOT;
        INIT_LIST_HEAD(&ctx->defer_list);
        INIT_LIST_HEAD(&ctx->timeout_list);
        init_waitqueue_head(&ctx->inflight_wait);
@@ -459,6 +497,7 @@ err:
        if (ctx->fallback_req)
                kmem_cache_free(req_cachep, ctx->fallback_req);
        kfree(ctx->completions);
+       kfree(ctx->cancel_hash);
        kfree(ctx);
        return NULL;
 }
@@ -592,7 +631,7 @@ static void io_kill_timeout(struct io_kiocb *req)
 {
        int ret;
 
-       ret = hrtimer_try_to_cancel(&req->timeout.data->timer);
+       ret = hrtimer_try_to_cancel(&req->io->timeout.timer);
        if (ret != -1) {
                atomic_inc(&req->ctx->cq_timeouts);
                list_del_init(&req->list);
@@ -806,6 +845,7 @@ static struct io_kiocb *io_get_req(struct io_ring_ctx *ctx,
        }
 
 got_it:
+       req->io = NULL;
        req->ring_file = NULL;
        req->file = NULL;
        req->ctx = ctx;
@@ -836,8 +876,8 @@ static void __io_free_req(struct io_kiocb *req)
 {
        struct io_ring_ctx *ctx = req->ctx;
 
-       if (req->flags & REQ_F_FREE_SQE)
-               kfree(req->sqe);
+       if (req->io)
+               kfree(req->io);
        if (req->file && !(req->flags & REQ_F_FIXED_FILE))
                fput(req->file);
        if (req->flags & REQ_F_INFLIGHT) {
@@ -849,8 +889,6 @@ static void __io_free_req(struct io_kiocb *req)
                        wake_up(&ctx->inflight_wait);
                spin_unlock_irqrestore(&ctx->inflight_lock, flags);
        }
-       if (req->flags & REQ_F_TIMEOUT)
-               kfree(req->timeout.data);
        percpu_ref_put(&ctx->refs);
        if (likely(!io_is_fallback_req(req)))
                kmem_cache_free(req_cachep, req);
@@ -863,7 +901,7 @@ static bool io_link_cancel_timeout(struct io_kiocb *req)
        struct io_ring_ctx *ctx = req->ctx;
        int ret;
 
-       ret = hrtimer_try_to_cancel(&req->timeout.data->timer);
+       ret = hrtimer_try_to_cancel(&req->io->timeout.timer);
        if (ret != -1) {
                io_cqring_fill_event(req, -ECANCELED);
                io_commit_cqring(ctx);
@@ -878,7 +916,6 @@ static bool io_link_cancel_timeout(struct io_kiocb *req)
 static void io_req_link_next(struct io_kiocb *req, struct io_kiocb **nxtptr)
 {
        struct io_ring_ctx *ctx = req->ctx;
-       struct io_kiocb *nxt;
        bool wake_ev = false;
 
        /* Already got next link */
@@ -890,24 +927,21 @@ static void io_req_link_next(struct io_kiocb *req, struct io_kiocb **nxtptr)
         * potentially happen if the chain is messed up, check to be on the
         * safe side.
         */
-       nxt = list_first_entry_or_null(&req->link_list, struct io_kiocb, list);
-       while (nxt) {
-               list_del_init(&nxt->list);
+       while (!list_empty(&req->link_list)) {
+               struct io_kiocb *nxt = list_first_entry(&req->link_list,
+                                               struct io_kiocb, link_list);
 
-               if ((req->flags & REQ_F_LINK_TIMEOUT) &&
-                   (nxt->flags & REQ_F_TIMEOUT)) {
+               if (unlikely((req->flags & REQ_F_LINK_TIMEOUT) &&
+                            (nxt->flags & REQ_F_TIMEOUT))) {
+                       list_del_init(&nxt->link_list);
                        wake_ev |= io_link_cancel_timeout(nxt);
-                       nxt = list_first_entry_or_null(&req->link_list,
-                                                       struct io_kiocb, list);
                        req->flags &= ~REQ_F_LINK_TIMEOUT;
                        continue;
                }
-               if (!list_empty(&req->link_list)) {
-                       INIT_LIST_HEAD(&nxt->link_list);
-                       list_splice(&req->link_list, &nxt->link_list);
-                       nxt->flags |= REQ_F_LINK;
-               }
 
+               list_del_init(&req->link_list);
+               if (!list_empty(&nxt->link_list))
+                       nxt->flags |= REQ_F_LINK;
                *nxtptr = nxt;
                break;
        }
@@ -923,15 +957,15 @@ static void io_req_link_next(struct io_kiocb *req, struct io_kiocb **nxtptr)
 static void io_fail_links(struct io_kiocb *req)
 {
        struct io_ring_ctx *ctx = req->ctx;
-       struct io_kiocb *link;
        unsigned long flags;
 
        spin_lock_irqsave(&ctx->completion_lock, flags);
 
        while (!list_empty(&req->link_list)) {
-               link = list_first_entry(&req->link_list, struct io_kiocb, list);
-               list_del_init(&link->list);
+               struct io_kiocb *link = list_first_entry(&req->link_list,
+                                               struct io_kiocb, link_list);
 
+               list_del_init(&link->link_list);
                trace_io_uring_fail_link(req, link);
 
                if ((req->flags & REQ_F_LINK_TIMEOUT) &&
@@ -1079,9 +1113,9 @@ static void io_iopoll_complete(struct io_ring_ctx *ctx, unsigned int *nr_events,
                         * completions for those, only batch free for fixed
                         * file and non-linked commands.
                         */
-                       if (((req->flags &
-                               (REQ_F_FIXED_FILE|REQ_F_LINK|REQ_F_FREE_SQE)) ==
-                           REQ_F_FIXED_FILE) && !io_is_fallback_req(req)) {
+                       if (((req->flags & (REQ_F_FIXED_FILE|REQ_F_LINK)) ==
+                           REQ_F_FIXED_FILE) && !io_is_fallback_req(req) &&
+                           !req->io) {
                                reqs[to_free++] = req;
                                if (to_free == ARRAY_SIZE(reqs))
                                        io_free_req_many(ctx, reqs, &to_free);
@@ -1410,15 +1444,6 @@ static int io_prep_rw(struct io_kiocb *req, bool force_nonblock)
        if (S_ISREG(file_inode(req->file)->i_mode))
                req->flags |= REQ_F_ISREG;
 
-       /*
-        * If the file doesn't support async, mark it as REQ_F_MUST_PUNT so
-        * we know to async punt it even if it was opened O_NONBLOCK
-        */
-       if (force_nonblock && !io_file_supports_async(req->file)) {
-               req->flags |= REQ_F_MUST_PUNT;
-               return -EAGAIN;
-       }
-
        kiocb->ki_pos = READ_ONCE(sqe->off);
        kiocb->ki_flags = iocb_flags(kiocb->ki_filp);
        kiocb->ki_hint = ki_hint_validate(file_write_hint(kiocb->ki_filp));
@@ -1587,6 +1612,16 @@ static ssize_t io_import_iovec(int rw, struct io_kiocb *req,
                return io_import_fixed(req->ctx, rw, sqe, iter);
        }
 
+       if (req->io) {
+               struct io_async_rw *iorw = &req->io->rw;
+
+               *iovec = iorw->iov;
+               iov_iter_init(iter, rw, *iovec, iorw->nr_segs, iorw->size);
+               if (iorw->iov == iorw->fast_iov)
+                       *iovec = NULL;
+               return iorw->size;
+       }
+
        if (!req->has_user)
                return -EFAULT;
 
@@ -1657,6 +1692,50 @@ static ssize_t loop_rw_iter(int rw, struct file *file, struct kiocb *kiocb,
        return ret;
 }
 
+static void io_req_map_io(struct io_kiocb *req, ssize_t io_size,
+                         struct iovec *iovec, struct iovec *fast_iov,
+                         struct iov_iter *iter)
+{
+       req->io->rw.nr_segs = iter->nr_segs;
+       req->io->rw.size = io_size;
+       req->io->rw.iov = iovec;
+       if (!req->io->rw.iov) {
+               req->io->rw.iov = req->io->rw.fast_iov;
+               memcpy(req->io->rw.iov, fast_iov,
+                       sizeof(struct iovec) * iter->nr_segs);
+       }
+}
+
+static int io_setup_async_io(struct io_kiocb *req, ssize_t io_size,
+                            struct iovec *iovec, struct iovec *fast_iov,
+                            struct iov_iter *iter)
+{
+       req->io = kmalloc(sizeof(*req->io), GFP_KERNEL);
+       if (req->io) {
+               io_req_map_io(req, io_size, iovec, fast_iov, iter);
+               memcpy(&req->io->sqe, req->sqe, sizeof(req->io->sqe));
+               req->sqe = &req->io->sqe;
+               return 0;
+       }
+
+       return -ENOMEM;
+}
+
+static int io_read_prep(struct io_kiocb *req, struct iovec **iovec,
+                       struct iov_iter *iter, bool force_nonblock)
+{
+       ssize_t ret;
+
+       ret = io_prep_rw(req, force_nonblock);
+       if (ret)
+               return ret;
+
+       if (unlikely(!(req->file->f_mode & FMODE_READ)))
+               return -EBADF;
+
+       return io_import_iovec(READ, req, iovec, iter);
+}
+
 static int io_read(struct io_kiocb *req, struct io_kiocb **nxt,
                   bool force_nonblock)
 {
@@ -1665,23 +1744,31 @@ static int io_read(struct io_kiocb *req, struct io_kiocb **nxt,
        struct iov_iter iter;
        struct file *file;
        size_t iov_count;
-       ssize_t read_size, ret;
-
-       ret = io_prep_rw(req, force_nonblock);
-       if (ret)
-               return ret;
-       file = kiocb->ki_filp;
+       ssize_t io_size, ret;
 
-       if (unlikely(!(file->f_mode & FMODE_READ)))
-               return -EBADF;
-
-       ret = io_import_iovec(READ, req, &iovec, &iter);
-       if (ret < 0)
-               return ret;
+       if (!req->io) {
+               ret = io_read_prep(req, &iovec, &iter, force_nonblock);
+               if (ret < 0)
+                       return ret;
+       } else {
+               ret = io_import_iovec(READ, req, &iovec, &iter);
+               if (ret < 0)
+                       return ret;
+       }
 
-       read_size = ret;
+       file = req->file;
+       io_size = ret;
        if (req->flags & REQ_F_LINK)
-               req->result = read_size;
+               req->result = io_size;
+
+       /*
+        * If the file doesn't support async, mark it as REQ_F_MUST_PUNT so
+        * we know to async punt it even if it was opened O_NONBLOCK
+        */
+       if (force_nonblock && !io_file_supports_async(file)) {
+               req->flags |= REQ_F_MUST_PUNT;
+               goto copy_iov;
+       }
 
        iov_count = iov_iter_count(&iter);
        ret = rw_verify_area(READ, file, &kiocb->ki_pos, iov_count);
@@ -1703,18 +1790,40 @@ static int io_read(struct io_kiocb *req, struct io_kiocb **nxt,
                 */
                if (force_nonblock && !(req->flags & REQ_F_NOWAIT) &&
                    (req->flags & REQ_F_ISREG) &&
-                   ret2 > 0 && ret2 < read_size)
+                   ret2 > 0 && ret2 < io_size)
                        ret2 = -EAGAIN;
                /* Catch -EAGAIN return for forced non-blocking submission */
-               if (!force_nonblock || ret2 != -EAGAIN)
+               if (!force_nonblock || ret2 != -EAGAIN) {
                        kiocb_done(kiocb, ret2, nxt, req->in_async);
-               else
-                       ret = -EAGAIN;
+               } else {
+copy_iov:
+                       ret = io_setup_async_io(req, io_size, iovec,
+                                               inline_vecs, &iter);
+                       if (ret)
+                               goto out_free;
+                       return -EAGAIN;
+               }
        }
+out_free:
        kfree(iovec);
        return ret;
 }
 
+static int io_write_prep(struct io_kiocb *req, struct iovec **iovec,
+                        struct iov_iter *iter, bool force_nonblock)
+{
+       ssize_t ret;
+
+       ret = io_prep_rw(req, force_nonblock);
+       if (ret)
+               return ret;
+
+       if (unlikely(!(req->file->f_mode & FMODE_WRITE)))
+               return -EBADF;
+
+       return io_import_iovec(WRITE, req, iovec, iter);
+}
+
 static int io_write(struct io_kiocb *req, struct io_kiocb **nxt,
                    bool force_nonblock)
 {
@@ -1723,29 +1832,36 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt,
        struct iov_iter iter;
        struct file *file;
        size_t iov_count;
-       ssize_t ret;
+       ssize_t ret, io_size;
 
-       ret = io_prep_rw(req, force_nonblock);
-       if (ret)
-               return ret;
+       if (!req->io) {
+               ret = io_write_prep(req, &iovec, &iter, force_nonblock);
+               if (ret < 0)
+                       return ret;
+       } else {
+               ret = io_import_iovec(WRITE, req, &iovec, &iter);
+               if (ret < 0)
+                       return ret;
+       }
 
        file = kiocb->ki_filp;
-       if (unlikely(!(file->f_mode & FMODE_WRITE)))
-               return -EBADF;
-
-       ret = io_import_iovec(WRITE, req, &iovec, &iter);
-       if (ret < 0)
-               return ret;
-
+       io_size = ret;
        if (req->flags & REQ_F_LINK)
-               req->result = ret;
+               req->result = io_size;
 
-       iov_count = iov_iter_count(&iter);
+       /*
+        * If the file doesn't support async, mark it as REQ_F_MUST_PUNT so
+        * we know to async punt it even if it was opened O_NONBLOCK
+        */
+       if (force_nonblock && !io_file_supports_async(req->file)) {
+               req->flags |= REQ_F_MUST_PUNT;
+               goto copy_iov;
+       }
 
-       ret = -EAGAIN;
        if (force_nonblock && !(kiocb->ki_flags & IOCB_DIRECT))
-               goto out_free;
+               goto copy_iov;
 
+       iov_count = iov_iter_count(&iter);
        ret = rw_verify_area(WRITE, file, &kiocb->ki_pos, iov_count);
        if (!ret) {
                ssize_t ret2;
@@ -1769,10 +1885,16 @@ static int io_write(struct io_kiocb *req, struct io_kiocb **nxt,
                        ret2 = call_write_iter(file, kiocb, &iter);
                else
                        ret2 = loop_rw_iter(WRITE, file, kiocb, &iter);
-               if (!force_nonblock || ret2 != -EAGAIN)
+               if (!force_nonblock || ret2 != -EAGAIN) {
                        kiocb_done(kiocb, ret2, nxt, req->in_async);
-               else
-                       ret = -EAGAIN;
+               } else {
+copy_iov:
+                       ret = io_setup_async_io(req, io_size, iovec,
+                                               inline_vecs, &iter);
+                       if (ret)
+                               goto out_free;
+                       return -EAGAIN;
+               }
        }
 out_free:
        kfree(iovec);
@@ -1888,12 +2010,25 @@ static int io_sync_file_range(struct io_kiocb *req,
        return 0;
 }
 
+static int io_sendmsg_prep(struct io_kiocb *req, struct io_async_ctx *io)
+{
 #if defined(CONFIG_NET)
-static int io_send_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe,
-                          struct io_kiocb **nxt, bool force_nonblock,
-                  long (*fn)(struct socket *, struct user_msghdr __user *,
-                               unsigned int))
+       const struct io_uring_sqe *sqe = req->sqe;
+       struct user_msghdr __user *msg;
+       unsigned flags;
+
+       flags = READ_ONCE(sqe->msg_flags);
+       msg = (struct user_msghdr __user *)(unsigned long) READ_ONCE(sqe->addr);
+       return sendmsg_copy_msghdr(&io->msg.msg, msg, flags, &io->msg.iov);
+#else
+       return 0;
+#endif
+}
+
+static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe,
+                     struct io_kiocb **nxt, bool force_nonblock)
 {
+#if defined(CONFIG_NET)
        struct socket *sock;
        int ret;
 
@@ -1902,7 +2037,9 @@ static int io_send_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe,
 
        sock = sock_from_file(req->file, &ret);
        if (sock) {
-               struct user_msghdr __user *msg;
+               struct io_async_ctx io, *copy;
+               struct sockaddr_storage addr;
+               struct msghdr *kmsg;
                unsigned flags;
 
                flags = READ_ONCE(sqe->msg_flags);
@@ -1911,30 +2048,59 @@ static int io_send_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe,
                else if (force_nonblock)
                        flags |= MSG_DONTWAIT;
 
-               msg = (struct user_msghdr __user *) (unsigned long)
-                       READ_ONCE(sqe->addr);
+               if (req->io) {
+                       kmsg = &req->io->msg.msg;
+                       kmsg->msg_name = &addr;
+               } else {
+                       kmsg = &io.msg.msg;
+                       kmsg->msg_name = &addr;
+                       io.msg.iov = io.msg.fast_iov;
+                       ret = io_sendmsg_prep(req, &io);
+                       if (ret)
+                               goto out;
+               }
 
-               ret = fn(sock, msg, flags);
-               if (force_nonblock && ret == -EAGAIN)
+               ret = __sys_sendmsg_sock(sock, kmsg, flags);
+               if (force_nonblock && ret == -EAGAIN) {
+                       copy = kmalloc(sizeof(*copy), GFP_KERNEL);
+                       if (!copy) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+                       memcpy(&copy->msg, &io.msg, sizeof(copy->msg));
+                       req->io = copy;
+                       memcpy(&req->io->sqe, req->sqe, sizeof(*req->sqe));
+                       req->sqe = &req->io->sqe;
                        return ret;
+               }
+               if (ret == -ERESTARTSYS)
+                       ret = -EINTR;
        }
 
+out:
        io_cqring_add_event(req, ret);
        if (ret < 0 && (req->flags & REQ_F_LINK))
                req->flags |= REQ_F_FAIL_LINK;
        io_put_req_find_next(req, nxt);
        return 0;
-}
+#else
+       return -EOPNOTSUPP;
 #endif
+}
 
-static int io_sendmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe,
-                     struct io_kiocb **nxt, bool force_nonblock)
+static int io_recvmsg_prep(struct io_kiocb *req, struct io_async_ctx *io)
 {
 #if defined(CONFIG_NET)
-       return io_send_recvmsg(req, sqe, nxt, force_nonblock,
-                               __sys_sendmsg_sock);
+       const struct io_uring_sqe *sqe = req->sqe;
+       struct user_msghdr __user *msg;
+       unsigned flags;
+
+       flags = READ_ONCE(sqe->msg_flags);
+       msg = (struct user_msghdr __user *)(unsigned long) READ_ONCE(sqe->addr);
+       return recvmsg_copy_msghdr(&io->msg.msg, msg, flags, &io->msg.uaddr,
+                                       &io->msg.iov);
 #else
-       return -EOPNOTSUPP;
+       return 0;
 #endif
 }
 
@@ -1942,8 +2108,63 @@ static int io_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe,
                      struct io_kiocb **nxt, bool force_nonblock)
 {
 #if defined(CONFIG_NET)
-       return io_send_recvmsg(req, sqe, nxt, force_nonblock,
-                               __sys_recvmsg_sock);
+       struct socket *sock;
+       int ret;
+
+       if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
+               return -EINVAL;
+
+       sock = sock_from_file(req->file, &ret);
+       if (sock) {
+               struct user_msghdr __user *msg;
+               struct io_async_ctx io, *copy;
+               struct sockaddr_storage addr;
+               struct msghdr *kmsg;
+               unsigned flags;
+
+               flags = READ_ONCE(sqe->msg_flags);
+               if (flags & MSG_DONTWAIT)
+                       req->flags |= REQ_F_NOWAIT;
+               else if (force_nonblock)
+                       flags |= MSG_DONTWAIT;
+
+               msg = (struct user_msghdr __user *) (unsigned long)
+                       READ_ONCE(sqe->addr);
+               if (req->io) {
+                       kmsg = &req->io->msg.msg;
+                       kmsg->msg_name = &addr;
+               } else {
+                       kmsg = &io.msg.msg;
+                       kmsg->msg_name = &addr;
+                       io.msg.iov = io.msg.fast_iov;
+                       ret = io_recvmsg_prep(req, &io);
+                       if (ret)
+                               goto out;
+               }
+
+               ret = __sys_recvmsg_sock(sock, kmsg, msg, io.msg.uaddr, flags);
+               if (force_nonblock && ret == -EAGAIN) {
+                       copy = kmalloc(sizeof(*copy), GFP_KERNEL);
+                       if (!copy) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+                       memcpy(copy, &io, sizeof(*copy));
+                       req->io = copy;
+                       memcpy(&req->io->sqe, req->sqe, sizeof(*req->sqe));
+                       req->sqe = &req->io->sqe;
+                       return ret;
+               }
+               if (ret == -ERESTARTSYS)
+                       ret = -EINTR;
+       }
+
+out:
+       io_cqring_add_event(req, ret);
+       if (ret < 0 && (req->flags & REQ_F_LINK))
+               req->flags |= REQ_F_FAIL_LINK;
+       io_put_req_find_next(req, nxt);
+       return 0;
 #else
        return -EOPNOTSUPP;
 #endif
@@ -1985,11 +2206,26 @@ static int io_accept(struct io_kiocb *req, const struct io_uring_sqe *sqe,
 #endif
 }
 
+static int io_connect_prep(struct io_kiocb *req, struct io_async_ctx *io)
+{
+#if defined(CONFIG_NET)
+       const struct io_uring_sqe *sqe = req->sqe;
+       struct sockaddr __user *addr;
+       int addr_len;
+
+       addr = (struct sockaddr __user *) (unsigned long) READ_ONCE(sqe->addr);
+       addr_len = READ_ONCE(sqe->addr2);
+       return move_addr_to_kernel(addr, addr_len, &io->connect.address);
+#else
+       return 0;
+#endif
+}
+
 static int io_connect(struct io_kiocb *req, const struct io_uring_sqe *sqe,
                      struct io_kiocb **nxt, bool force_nonblock)
 {
 #if defined(CONFIG_NET)
-       struct sockaddr __user *addr;
+       struct io_async_ctx __io, *io;
        unsigned file_flags;
        int addr_len, ret;
 
@@ -1998,15 +2234,35 @@ static int io_connect(struct io_kiocb *req, const struct io_uring_sqe *sqe,
        if (sqe->ioprio || sqe->len || sqe->buf_index || sqe->rw_flags)
                return -EINVAL;
 
-       addr = (struct sockaddr __user *) (unsigned long) READ_ONCE(sqe->addr);
        addr_len = READ_ONCE(sqe->addr2);
        file_flags = force_nonblock ? O_NONBLOCK : 0;
 
-       ret = __sys_connect_file(req->file, addr, addr_len, file_flags);
-       if (ret == -EAGAIN && force_nonblock)
+       if (req->io) {
+               io = req->io;
+       } else {
+               ret = io_connect_prep(req, &__io);
+               if (ret)
+                       goto out;
+               io = &__io;
+       }
+
+       ret = __sys_connect_file(req->file, &io->connect.address, addr_len,
+                                       file_flags);
+       if ((ret == -EAGAIN || ret == -EINPROGRESS) && force_nonblock) {
+               io = kmalloc(sizeof(*io), GFP_KERNEL);
+               if (!io) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               memcpy(&io->connect, &__io.connect, sizeof(io->connect));
+               req->io = io;
+               memcpy(&io->sqe, req->sqe, sizeof(*req->sqe));
+               req->sqe = &io->sqe;
                return -EAGAIN;
+       }
        if (ret == -ERESTARTSYS)
                ret = -EINTR;
+out:
        if (ret < 0 && (req->flags & REQ_F_LINK))
                req->flags |= REQ_F_FAIL_LINK;
        io_cqring_add_event(req, ret);
@@ -2017,14 +2273,6 @@ static int io_connect(struct io_kiocb *req, const struct io_uring_sqe *sqe,
 #endif
 }
 
-static inline void io_poll_remove_req(struct io_kiocb *req)
-{
-       if (!RB_EMPTY_NODE(&req->rb_node)) {
-               rb_erase(&req->rb_node, &req->ctx->cancel_tree);
-               RB_CLEAR_NODE(&req->rb_node);
-       }
-}
-
 static void io_poll_remove_one(struct io_kiocb *req)
 {
        struct io_poll_iocb *poll = &req->poll;
@@ -2036,36 +2284,34 @@ static void io_poll_remove_one(struct io_kiocb *req)
                io_queue_async_work(req);
        }
        spin_unlock(&poll->head->lock);
-       io_poll_remove_req(req);
+       hash_del(&req->hash_node);
 }
 
 static void io_poll_remove_all(struct io_ring_ctx *ctx)
 {
-       struct rb_node *node;
+       struct hlist_node *tmp;
        struct io_kiocb *req;
+       int i;
 
        spin_lock_irq(&ctx->completion_lock);
-       while ((node = rb_first(&ctx->cancel_tree)) != NULL) {
-               req = rb_entry(node, struct io_kiocb, rb_node);
-               io_poll_remove_one(req);
+       for (i = 0; i < (1U << ctx->cancel_hash_bits); i++) {
+               struct hlist_head *list;
+
+               list = &ctx->cancel_hash[i];
+               hlist_for_each_entry_safe(req, tmp, list, hash_node)
+                       io_poll_remove_one(req);
        }
        spin_unlock_irq(&ctx->completion_lock);
 }
 
 static int io_poll_cancel(struct io_ring_ctx *ctx, __u64 sqe_addr)
 {
-       struct rb_node *p, *parent = NULL;
+       struct hlist_head *list;
        struct io_kiocb *req;
 
-       p = ctx->cancel_tree.rb_node;
-       while (p) {
-               parent = p;
-               req = rb_entry(parent, struct io_kiocb, rb_node);
-               if (sqe_addr < req->user_data) {
-                       p = p->rb_left;
-               } else if (sqe_addr > req->user_data) {
-                       p = p->rb_right;
-               } else {
+       list = &ctx->cancel_hash[hash_long(sqe_addr, ctx->cancel_hash_bits)];
+       hlist_for_each_entry(req, list, hash_node) {
+               if (sqe_addr == req->user_data) {
                        io_poll_remove_one(req);
                        return 0;
                }
@@ -2147,7 +2393,7 @@ static void io_poll_complete_work(struct io_wq_work **workptr)
                spin_unlock_irq(&ctx->completion_lock);
                return;
        }
-       io_poll_remove_req(req);
+       hash_del(&req->hash_node);
        io_poll_complete(req, mask, ret);
        spin_unlock_irq(&ctx->completion_lock);
 
@@ -2182,7 +2428,7 @@ static int io_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync,
         * for finalizing the request, mark us as having grabbed that already.
         */
        if (mask && spin_trylock_irqsave(&ctx->completion_lock, flags)) {
-               io_poll_remove_req(req);
+               hash_del(&req->hash_node);
                io_poll_complete(req, mask, 0);
                req->flags |= REQ_F_COMP_LOCKED;
                io_put_req(req);
@@ -2220,20 +2466,10 @@ static void io_poll_queue_proc(struct file *file, struct wait_queue_head *head,
 static void io_poll_req_insert(struct io_kiocb *req)
 {
        struct io_ring_ctx *ctx = req->ctx;
-       struct rb_node **p = &ctx->cancel_tree.rb_node;
-       struct rb_node *parent = NULL;
-       struct io_kiocb *tmp;
-
-       while (*p) {
-               parent = *p;
-               tmp = rb_entry(parent, struct io_kiocb, rb_node);
-               if (req->user_data < tmp->user_data)
-                       p = &(*p)->rb_left;
-               else
-                       p = &(*p)->rb_right;
-       }
-       rb_link_node(&req->rb_node, parent, p);
-       rb_insert_color(&req->rb_node, &ctx->cancel_tree);
+       struct hlist_head *list;
+
+       list = &ctx->cancel_hash[hash_long(req->user_data, ctx->cancel_hash_bits)];
+       hlist_add_head(&req->hash_node, list);
 }
 
 static int io_poll_add(struct io_kiocb *req, const struct io_uring_sqe *sqe,
@@ -2257,11 +2493,11 @@ static int io_poll_add(struct io_kiocb *req, const struct io_uring_sqe *sqe,
        if (!poll->wait)
                return -ENOMEM;
 
-       req->sqe = NULL;
+       req->io = NULL;
        INIT_IO_WORK(&req->work, io_poll_complete_work);
        events = READ_ONCE(sqe->poll_events);
        poll->events = demangle_poll(events) | EPOLLERR | EPOLLHUP;
-       RB_CLEAR_NODE(&req->rb_node);
+       INIT_HLIST_NODE(&req->hash_node);
 
        poll->head = NULL;
        poll->done = false;
@@ -2368,7 +2604,7 @@ static int io_timeout_cancel(struct io_ring_ctx *ctx, __u64 user_data)
        if (ret == -ENOENT)
                return ret;
 
-       ret = hrtimer_try_to_cancel(&req->timeout.data->timer);
+       ret = hrtimer_try_to_cancel(&req->io->timeout.timer);
        if (ret == -1)
                return -EALREADY;
 
@@ -2410,7 +2646,8 @@ static int io_timeout_remove(struct io_kiocb *req,
        return 0;
 }
 
-static int io_timeout_setup(struct io_kiocb *req)
+static int io_timeout_prep(struct io_kiocb *req, struct io_async_ctx *io,
+                          bool is_timeout_link)
 {
        const struct io_uring_sqe *sqe = req->sqe;
        struct io_timeout_data *data;
@@ -2420,15 +2657,14 @@ static int io_timeout_setup(struct io_kiocb *req)
                return -EINVAL;
        if (sqe->ioprio || sqe->buf_index || sqe->len != 1)
                return -EINVAL;
+       if (sqe->off && is_timeout_link)
+               return -EINVAL;
        flags = READ_ONCE(sqe->timeout_flags);
        if (flags & ~IORING_TIMEOUT_ABS)
                return -EINVAL;
 
-       data = kzalloc(sizeof(struct io_timeout_data), GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
+       data = &io->timeout;
        data->req = req;
-       req->timeout.data = data;
        req->flags |= REQ_F_TIMEOUT;
 
        if (get_timespec64(&data->ts, u64_to_user_ptr(sqe->addr)))
@@ -2440,6 +2676,7 @@ static int io_timeout_setup(struct io_kiocb *req)
                data->mode = HRTIMER_MODE_REL;
 
        hrtimer_init(&data->timer, CLOCK_MONOTONIC, data->mode);
+       req->io = io;
        return 0;
 }
 
@@ -2448,16 +2685,24 @@ static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe)
        unsigned count;
        struct io_ring_ctx *ctx = req->ctx;
        struct io_timeout_data *data;
+       struct io_async_ctx *io;
        struct list_head *entry;
        unsigned span = 0;
-       int ret;
 
-       ret = io_timeout_setup(req);
-       /* common setup allows flags (like links) set, we don't */
-       if (!ret && sqe->flags)
-               ret = -EINVAL;
-       if (ret)
-               return ret;
+       io = req->io;
+       if (!io) {
+               int ret;
+
+               io = kmalloc(sizeof(*io), GFP_KERNEL);
+               if (!io)
+                       return -ENOMEM;
+               ret = io_timeout_prep(req, io, false);
+               if (ret) {
+                       kfree(io);
+                       return ret;
+               }
+       }
+       data = &req->io->timeout;
 
        /*
         * sqe->off holds how many events that need to occur for this
@@ -2473,7 +2718,7 @@ static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe)
        }
 
        req->sequence = ctx->cached_sq_head + count - 1;
-       req->timeout.data->seq_offset = count;
+       data->seq_offset = count;
 
        /*
         * Insertion sort, ensuring the first entry in the list is always
@@ -2484,7 +2729,7 @@ static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe)
                struct io_kiocb *nxt = list_entry(entry, struct io_kiocb, list);
                unsigned nxt_sq_head;
                long long tmp, tmp_nxt;
-               u32 nxt_offset = nxt->timeout.data->seq_offset;
+               u32 nxt_offset = nxt->io->timeout.seq_offset;
 
                if (nxt->flags & REQ_F_TIMEOUT_NOSEQ)
                        continue;
@@ -2517,7 +2762,6 @@ static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe)
        req->sequence -= span;
 add:
        list_add(&req->list, entry);
-       data = req->timeout.data;
        data->timer.function = io_timeout_fn;
        hrtimer_start(&data->timer, timespec64_to_ktime(data->ts), data->mode);
        spin_unlock_irq(&ctx->completion_lock);
@@ -2598,30 +2842,76 @@ static int io_async_cancel(struct io_kiocb *req, const struct io_uring_sqe *sqe,
        return 0;
 }
 
+static int io_req_defer_prep(struct io_kiocb *req, struct io_async_ctx *io)
+{
+       struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs;
+       struct iov_iter iter;
+       ssize_t ret;
+
+       memcpy(&io->sqe, req->sqe, sizeof(io->sqe));
+       req->sqe = &io->sqe;
+
+       switch (io->sqe.opcode) {
+       case IORING_OP_READV:
+       case IORING_OP_READ_FIXED:
+               ret = io_read_prep(req, &iovec, &iter, true);
+               break;
+       case IORING_OP_WRITEV:
+       case IORING_OP_WRITE_FIXED:
+               ret = io_write_prep(req, &iovec, &iter, true);
+               break;
+       case IORING_OP_SENDMSG:
+               ret = io_sendmsg_prep(req, io);
+               break;
+       case IORING_OP_RECVMSG:
+               ret = io_recvmsg_prep(req, io);
+               break;
+       case IORING_OP_CONNECT:
+               ret = io_connect_prep(req, io);
+               break;
+       case IORING_OP_TIMEOUT:
+               return io_timeout_prep(req, io, false);
+       case IORING_OP_LINK_TIMEOUT:
+               return io_timeout_prep(req, io, true);
+       default:
+               req->io = io;
+               return 0;
+       }
+
+       if (ret < 0)
+               return ret;
+
+       req->io = io;
+       io_req_map_io(req, ret, iovec, inline_vecs, &iter);
+       return 0;
+}
+
 static int io_req_defer(struct io_kiocb *req)
 {
-       struct io_uring_sqe *sqe_copy;
        struct io_ring_ctx *ctx = req->ctx;
+       struct io_async_ctx *io;
+       int ret;
 
        /* Still need defer if there is pending req in defer list. */
        if (!req_need_defer(req) && list_empty(&ctx->defer_list))
                return 0;
 
-       sqe_copy = kmalloc(sizeof(*sqe_copy), GFP_KERNEL);
-       if (!sqe_copy)
+       io = kmalloc(sizeof(*io), GFP_KERNEL);
+       if (!io)
                return -EAGAIN;
 
+       ret = io_req_defer_prep(req, io);
+       if (ret < 0) {
+               kfree(io);
+               return ret;
+       }
+
        spin_lock_irq(&ctx->completion_lock);
        if (!req_need_defer(req) && list_empty(&ctx->defer_list)) {
                spin_unlock_irq(&ctx->completion_lock);
-               kfree(sqe_copy);
                return 0;
        }
 
-       memcpy(sqe_copy, req->sqe, sizeof(*sqe_copy));
-       req->flags |= REQ_F_FREE_SQE;
-       req->sqe = sqe_copy;
-
        trace_io_uring_defer(ctx, req, req->user_data);
        list_add_tail(&req->list, &ctx->defer_list);
        spin_unlock_irq(&ctx->completion_lock);
@@ -2876,10 +3166,11 @@ static enum hrtimer_restart io_link_timeout_fn(struct hrtimer *timer)
         * We don't expect the list to be empty, that will only happen if we
         * race with the completion of the linked work.
         */
-       if (!list_empty(&req->list)) {
-               prev = list_entry(req->list.prev, struct io_kiocb, link_list);
+       if (!list_empty(&req->link_list)) {
+               prev = list_entry(req->link_list.prev, struct io_kiocb,
+                                 link_list);
                if (refcount_inc_not_zero(&prev->refs)) {
-                       list_del_init(&req->list);
+                       list_del_init(&req->link_list);
                        prev->flags &= ~REQ_F_LINK_TIMEOUT;
                } else
                        prev = NULL;
@@ -2909,8 +3200,8 @@ static void io_queue_linked_timeout(struct io_kiocb *req)
         * we got a chance to setup the timer
         */
        spin_lock_irq(&ctx->completion_lock);
-       if (!list_empty(&req->list)) {
-               struct io_timeout_data *data = req->timeout.data;
+       if (!list_empty(&req->link_list)) {
+               struct io_timeout_data *data = &req->io->timeout;
 
                data->timer.function = io_link_timeout_fn;
                hrtimer_start(&data->timer, timespec64_to_ktime(data->ts),
@@ -2929,7 +3220,8 @@ static struct io_kiocb *io_prep_linked_timeout(struct io_kiocb *req)
        if (!(req->flags & REQ_F_LINK))
                return NULL;
 
-       nxt = list_first_entry_or_null(&req->link_list, struct io_kiocb, list);
+       nxt = list_first_entry_or_null(&req->link_list, struct io_kiocb,
+                                       link_list);
        if (!nxt || nxt->sqe->opcode != IORING_OP_LINK_TIMEOUT)
                return NULL;
 
@@ -2953,15 +3245,6 @@ static void __io_queue_sqe(struct io_kiocb *req)
         */
        if (ret == -EAGAIN && (!(req->flags & REQ_F_NOWAIT) ||
            (req->flags & REQ_F_MUST_PUNT))) {
-               struct io_uring_sqe *sqe_copy;
-
-               sqe_copy = kmemdup(req->sqe, sizeof(*sqe_copy), GFP_KERNEL);
-               if (!sqe_copy)
-                       goto err;
-
-               req->sqe = sqe_copy;
-               req->flags |= REQ_F_FREE_SQE;
-
                if (req->work.flags & IO_WQ_WORK_NEEDS_FILES) {
                        ret = io_grab_files(req);
                        if (ret)
@@ -3030,7 +3313,7 @@ static inline void io_queue_link_head(struct io_kiocb *req)
 
 #define SQE_VALID_FLAGS        (IOSQE_FIXED_FILE|IOSQE_IO_DRAIN|IOSQE_IO_LINK)
 
-static void io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
+static bool io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
                          struct io_kiocb **link)
 {
        struct io_ring_ctx *ctx = req->ctx;
@@ -3049,7 +3332,7 @@ static void io_submit_sqe(struct io_kiocb *req, struct io_submit_state *state,
 err_req:
                io_cqring_add_event(req, ret);
                io_double_put_req(req);
-               return;
+               return false;
        }
 
        /*
@@ -3061,32 +3344,25 @@ err_req:
         */
        if (*link) {
                struct io_kiocb *prev = *link;
-               struct io_uring_sqe *sqe_copy;
+               struct io_async_ctx *io;
 
                if (req->sqe->flags & IOSQE_IO_DRAIN)
                        (*link)->flags |= REQ_F_DRAIN_LINK | REQ_F_IO_DRAIN;
 
-               if (READ_ONCE(req->sqe->opcode) == IORING_OP_LINK_TIMEOUT) {
-                       ret = io_timeout_setup(req);
-                       /* common setup allows offset being set, we don't */
-                       if (!ret && req->sqe->off)
-                               ret = -EINVAL;
-                       if (ret) {
-                               prev->flags |= REQ_F_FAIL_LINK;
-                               goto err_req;
-                       }
-               }
-
-               sqe_copy = kmemdup(req->sqe, sizeof(*sqe_copy), GFP_KERNEL);
-               if (!sqe_copy) {
+               io = kmalloc(sizeof(*io), GFP_KERNEL);
+               if (!io) {
                        ret = -EAGAIN;
                        goto err_req;
                }
 
-               req->sqe = sqe_copy;
-               req->flags |= REQ_F_FREE_SQE;
+               ret = io_req_defer_prep(req, io);
+               if (ret) {
+                       kfree(io);
+                       prev->flags |= REQ_F_FAIL_LINK;
+                       goto err_req;
+               }
                trace_io_uring_link(ctx, req, prev);
-               list_add_tail(&req->list, &prev->link_list);
+               list_add_tail(&req->link_list, &prev->link_list);
        } else if (req->sqe->flags & IOSQE_IO_LINK) {
                req->flags |= REQ_F_LINK;
 
@@ -3095,6 +3371,8 @@ err_req:
        } else {
                io_queue_sqe(req);
        }
+
+       return true;
 }
 
 /*
@@ -3113,7 +3391,7 @@ static void io_submit_state_end(struct io_submit_state *state)
  * Start submission side cache.
  */
 static void io_submit_state_start(struct io_submit_state *state,
-                                 struct io_ring_ctx *ctx, unsigned max_ios)
+                                 unsigned int max_ios)
 {
        blk_start_plug(&state->plug);
        state->free_reqs = 0;
@@ -3197,7 +3475,7 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
                return -EBUSY;
 
        if (nr > IO_PLUG_THRESHOLD) {
-               io_submit_state_start(&state, ctx, nr);
+               io_submit_state_start(&state, nr);
                statep = &state;
        }
 
@@ -3224,6 +3502,7 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
                        }
                }
 
+               submitted++;
                sqe_flags = req->sqe->flags;
 
                req->ring_file = ring_file;
@@ -3233,9 +3512,8 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
                req->needs_fixed_file = async;
                trace_io_uring_submit_sqe(ctx, req->sqe->user_data,
                                          true, async);
-               io_submit_sqe(req, statep, &link);
-               submitted++;
-
+               if (!io_submit_sqe(req, statep, &link))
+                       break;
                /*
                 * If previous wasn't linked and we have a linked command,
                 * that's the end of the chain. Submit the previous link.
@@ -4363,6 +4641,7 @@ static void io_ring_ctx_free(struct io_ring_ctx *ctx)
        free_uid(ctx->user);
        put_cred(ctx->creds);
        kfree(ctx->completions);
+       kfree(ctx->cancel_hash);
        kmem_cache_free(req_cachep, ctx->fallback_req);
        kfree(ctx);
 }
@@ -4759,7 +5038,7 @@ static int io_uring_create(unsigned entries, struct io_uring_params *p)
        ctx->compat = in_compat_syscall();
        ctx->account_mem = account_mem;
        ctx->user = user;
-       ctx->creds = prepare_creds();
+       ctx->creds = get_current_cred();
 
        ret = io_allocate_scq_urings(ctx, p);
        if (ret)
@@ -4794,7 +5073,8 @@ static int io_uring_create(unsigned entries, struct io_uring_params *p)
        if (ret < 0)
                goto err;
 
-       p->features = IORING_FEAT_SINGLE_MMAP | IORING_FEAT_NODROP;
+       p->features = IORING_FEAT_SINGLE_MMAP | IORING_FEAT_NODROP |
+                       IORING_FEAT_SUBMIT_STABLE;
        trace_io_uring_create(ret, ctx, p->sq_entries, p->cq_entries, p->flags);
        return ret;
 err:
index 4d31503..9dc7e7a 100644 (file)
@@ -223,7 +223,7 @@ struct dentry *kernfs_node_dentry(struct kernfs_node *kn,
                        dput(dentry);
                        return ERR_PTR(-EINVAL);
                }
-               dtmp = lookup_one_len_unlocked(kntmp->name, dentry,
+               dtmp = lookup_positive_unlocked(kntmp->name, dentry,
                                               strlen(kntmp->name));
                dput(dentry);
                if (IS_ERR(dtmp))
index 2dda552..d6c91d1 100644 (file)
@@ -1210,25 +1210,25 @@ static int follow_automount(struct path *path, struct nameidata *nd,
  * - Flagged as automount point
  *
  * This may only be called in refwalk mode.
+ * On success path->dentry is known positive.
  *
  * Serialization is taken care of in namespace.c
  */
 static int follow_managed(struct path *path, struct nameidata *nd)
 {
        struct vfsmount *mnt = path->mnt; /* held by caller, must be left alone */
-       unsigned managed;
+       unsigned flags;
        bool need_mntput = false;
        int ret = 0;
 
        /* Given that we're not holding a lock here, we retain the value in a
         * local variable for each dentry as we look at it so that we don't see
         * the components of that value change under us */
-       while (managed = READ_ONCE(path->dentry->d_flags),
-              managed &= DCACHE_MANAGED_DENTRY,
-              unlikely(managed != 0)) {
+       while (flags = smp_load_acquire(&path->dentry->d_flags),
+              unlikely(flags & DCACHE_MANAGED_DENTRY)) {
                /* Allow the filesystem to manage the transit without i_mutex
                 * being held. */
-               if (managed & DCACHE_MANAGE_TRANSIT) {
+               if (flags & DCACHE_MANAGE_TRANSIT) {
                        BUG_ON(!path->dentry->d_op);
                        BUG_ON(!path->dentry->d_op->d_manage);
                        ret = path->dentry->d_op->d_manage(path, false);
@@ -1237,7 +1237,7 @@ static int follow_managed(struct path *path, struct nameidata *nd)
                }
 
                /* Transit to a mounted filesystem. */
-               if (managed & DCACHE_MOUNTED) {
+               if (flags & DCACHE_MOUNTED) {
                        struct vfsmount *mounted = lookup_mnt(path);
                        if (mounted) {
                                dput(path->dentry);
@@ -1256,7 +1256,7 @@ static int follow_managed(struct path *path, struct nameidata *nd)
                }
 
                /* Handle an automount point */
-               if (managed & DCACHE_NEED_AUTOMOUNT) {
+               if (flags & DCACHE_NEED_AUTOMOUNT) {
                        ret = follow_automount(path, nd, &need_mntput);
                        if (ret < 0)
                                break;
@@ -1269,10 +1269,12 @@ static int follow_managed(struct path *path, struct nameidata *nd)
 
        if (need_mntput && path->mnt == mnt)
                mntput(path->mnt);
-       if (ret == -EISDIR || !ret)
-               ret = 1;
        if (need_mntput)
                nd->flags |= LOOKUP_JUMPED;
+       if (ret == -EISDIR || !ret)
+               ret = 1;
+       if (ret > 0 && unlikely(d_flags_negative(flags)))
+               ret = -ENOENT;
        if (unlikely(ret < 0))
                path_put_conditional(path, nd);
        return ret;
@@ -1621,10 +1623,6 @@ static int lookup_fast(struct nameidata *nd,
                dput(dentry);
                return status;
        }
-       if (unlikely(d_is_negative(dentry))) {
-               dput(dentry);
-               return -ENOENT;
-       }
 
        path->mnt = mnt;
        path->dentry = dentry;
@@ -1811,11 +1809,6 @@ static int walk_component(struct nameidata *nd, int flags)
                if (unlikely(err < 0))
                        return err;
 
-               if (unlikely(d_is_negative(path.dentry))) {
-                       path_to_nameidata(&path, nd);
-                       return -ENOENT;
-               }
-
                seq = 0;        /* we are already out of RCU mode */
                inode = d_backing_inode(path.dentry);
        }
@@ -2568,6 +2561,26 @@ struct dentry *lookup_one_len_unlocked(const char *name,
 }
 EXPORT_SYMBOL(lookup_one_len_unlocked);
 
+/*
+ * Like lookup_one_len_unlocked(), except that it yields ERR_PTR(-ENOENT)
+ * on negatives.  Returns known positive or ERR_PTR(); that's what
+ * most of the users want.  Note that pinned negative with unlocked parent
+ * _can_ become positive at any time, so callers of lookup_one_len_unlocked()
+ * need to be very careful; pinned positives have ->d_inode stable, so
+ * this one avoids such problems.
+ */
+struct dentry *lookup_positive_unlocked(const char *name,
+                                      struct dentry *base, int len)
+{
+       struct dentry *ret = lookup_one_len_unlocked(name, base, len);
+       if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
+               dput(ret);
+               ret = ERR_PTR(-ENOENT);
+       }
+       return ret;
+}
+EXPORT_SYMBOL(lookup_positive_unlocked);
+
 #ifdef CONFIG_UNIX98_PTYS
 int path_pts(struct path *path)
 {
@@ -2662,7 +2675,7 @@ mountpoint_last(struct nameidata *nd)
                                return PTR_ERR(path.dentry);
                }
        }
-       if (d_is_negative(path.dentry)) {
+       if (d_flags_negative(smp_load_acquire(&path.dentry->d_flags))) {
                dput(path.dentry);
                return -ENOENT;
        }
@@ -3356,11 +3369,6 @@ static int do_last(struct nameidata *nd,
        if (unlikely(error < 0))
                return error;
 
-       if (unlikely(d_is_negative(path.dentry))) {
-               path_to_nameidata(&path, nd);
-               return -ENOENT;
-       }
-
        /*
         * create/update audit record if it already exists.
         */
index 86e5658..195ab7a 100644 (file)
@@ -863,13 +863,11 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
                } else
                        dchild = dget(dparent);
        } else
-               dchild = lookup_one_len_unlocked(name, dparent, namlen);
+               dchild = lookup_positive_unlocked(name, dparent, namlen);
        if (IS_ERR(dchild))
                return rv;
        if (d_mountpoint(dchild))
                goto out;
-       if (d_really_is_negative(dchild))
-               goto out;
        if (dchild->d_inode->i_ino != ino)
                goto out;
        rv = fh_compose(fhp, exp, dchild, &cd->fh);
index 533d0fc..b092374 100644 (file)
@@ -2991,18 +2991,9 @@ nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd,
        __be32 nfserr;
        int ignore_crossmnt = 0;
 
-       dentry = lookup_one_len_unlocked(name, cd->rd_fhp->fh_dentry, namlen);
+       dentry = lookup_positive_unlocked(name, cd->rd_fhp->fh_dentry, namlen);
        if (IS_ERR(dentry))
                return nfserrno(PTR_ERR(dentry));
-       if (d_really_is_negative(dentry)) {
-               /*
-                * we're not holding the i_mutex here, so there's
-                * a window where this directory entry could have gone
-                * away.
-                */
-               dput(dentry);
-               return nfserr_noent;
-       }
 
        exp_get(exp);
        /*
index e9717c2..c269d60 100644 (file)
@@ -200,7 +200,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
        int err;
        bool last_element = !post[0];
 
-       this = lookup_one_len_unlocked(name, base, namelen);
+       this = lookup_positive_unlocked(name, base, namelen);
        if (IS_ERR(this)) {
                err = PTR_ERR(this);
                this = NULL;
@@ -208,8 +208,6 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
                        goto out;
                goto out_err;
        }
-       if (!this->d_inode)
-               goto put_and_out;
 
        if (ovl_dentry_weird(this)) {
                /* Don't support traversing automounts and other weirdness */
@@ -651,7 +649,7 @@ struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh)
        if (err)
                return ERR_PTR(err);
 
-       index = lookup_one_len_unlocked(name.name, ofs->indexdir, name.len);
+       index = lookup_positive_unlocked(name.name, ofs->indexdir, name.len);
        kfree(name.name);
        if (IS_ERR(index)) {
                if (PTR_ERR(index) == -ENOENT)
@@ -659,9 +657,7 @@ struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh)
                return index;
        }
 
-       if (d_is_negative(index))
-               err = 0;
-       else if (ovl_is_whiteout(index))
+       if (ovl_is_whiteout(index))
                err = -ESTALE;
        else if (ovl_dentry_weird(index))
                err = -EIO;
@@ -685,7 +681,7 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
        if (err)
                return ERR_PTR(err);
 
-       index = lookup_one_len_unlocked(name.name, ofs->indexdir, name.len);
+       index = lookup_positive_unlocked(name.name, ofs->indexdir, name.len);
        if (IS_ERR(index)) {
                err = PTR_ERR(index);
                if (err == -ENOENT) {
@@ -700,9 +696,7 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
        }
 
        inode = d_inode(index);
-       if (d_is_negative(index)) {
-               goto out_dput;
-       } else if (ovl_is_whiteout(index) && !verify) {
+       if (ovl_is_whiteout(index) && !verify) {
                /*
                 * When index lookup is called with !verify for decoding an
                 * overlay file handle, a whiteout index implies that decode
@@ -1131,7 +1125,7 @@ bool ovl_lower_positive(struct dentry *dentry)
                struct dentry *this;
                struct dentry *lowerdir = poe->lowerstack[i].dentry;
 
-               this = lookup_one_len_unlocked(name->name, lowerdir,
+               this = lookup_positive_unlocked(name->name, lowerdir,
                                               name->len);
                if (IS_ERR(this)) {
                        switch (PTR_ERR(this)) {
@@ -1148,10 +1142,8 @@ bool ovl_lower_positive(struct dentry *dentry)
                                break;
                        }
                } else {
-                       if (this->d_inode) {
-                               positive = !ovl_is_whiteout(this);
-                               done = true;
-                       }
+                       positive = !ovl_is_whiteout(this);
+                       done = true;
                        dput(this);
                }
        }
index 648ce44..b901c8e 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -389,7 +389,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *filp = iocb->ki_filp;
        struct pipe_inode_info *pipe = filp->private_data;
-       unsigned int head, max_usage, mask;
+       unsigned int head;
        ssize_t ret = 0;
        int do_wakeup = 0;
        size_t total_len = iov_iter_count(from);
@@ -408,12 +408,11 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
        }
 
        head = pipe->head;
-       max_usage = pipe->max_usage;
-       mask = pipe->ring_size - 1;
 
        /* We try to merge small writes */
        chars = total_len & (PAGE_SIZE-1); /* size of the last buffer */
        if (!pipe_empty(head, pipe->tail) && chars != 0) {
+               unsigned int mask = pipe->ring_size - 1;
                struct pipe_buffer *buf = &pipe->bufs[(head - 1) & mask];
                int offset = buf->offset + buf->len;
 
@@ -443,7 +442,8 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
                }
 
                head = pipe->head;
-               if (!pipe_full(head, pipe->tail, max_usage)) {
+               if (!pipe_full(head, pipe->tail, pipe->max_usage)) {
+                       unsigned int mask = pipe->ring_size - 1;
                        struct pipe_buffer *buf = &pipe->bufs[head & mask];
                        struct page *page = pipe->tmp_page;
                        int copied;
@@ -465,7 +465,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
                        spin_lock_irq(&pipe->wait.lock);
 
                        head = pipe->head;
-                       if (pipe_full(head, pipe->tail, max_usage)) {
+                       if (pipe_full(head, pipe->tail, pipe->max_usage)) {
                                spin_unlock_irq(&pipe->wait.lock);
                                continue;
                        }
@@ -510,7 +510,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
                                break;
                }
 
-               if (!pipe_full(head, pipe->tail, max_usage))
+               if (!pipe_full(head, pipe->tail, pipe->max_usage))
                        continue;
 
                /* Wait for buffer space to become available. */
@@ -579,8 +579,6 @@ pipe_poll(struct file *filp, poll_table *wait)
 
        poll_wait(filp, &pipe->wait, wait);
 
-       BUG_ON(pipe_occupancy(head, tail) > pipe->ring_size);
-
        /* Reading only -- no need for acquiring the semaphore.  */
        mask = 0;
        if (filp->f_mode & FMODE_READ) {
@@ -1176,6 +1174,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
        pipe->max_usage = nr_slots;
        pipe->tail = tail;
        pipe->head = head;
+       wake_up_interruptible_all(&pipe->wait);
        return pipe->max_usage * PAGE_SIZE;
 
 out_revert_acct:
index 4639d53..b0688c0 100644 (file)
@@ -2487,21 +2487,15 @@ int dquot_quota_on_mount(struct super_block *sb, char *qf_name,
        struct dentry *dentry;
        int error;
 
-       dentry = lookup_one_len_unlocked(qf_name, sb->s_root, strlen(qf_name));
+       dentry = lookup_positive_unlocked(qf_name, sb->s_root, strlen(qf_name));
        if (IS_ERR(dentry))
                return PTR_ERR(dentry);
 
-       if (d_really_is_negative(dentry)) {
-               error = -ENOENT;
-               goto out;
-       }
-
        error = security_quota_on(dentry);
        if (!error)
                error = dquot_load_quota_inode(d_inode(dentry), type, format_id,
                                DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
 
-out:
        dput(dentry);
        return error;
 }
index f2400ce..fa1f377 100644 (file)
@@ -495,7 +495,7 @@ static int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_des
        unsigned int mask = pipe->ring_size - 1;
        int ret;
 
-       while (!pipe_empty(tail, head)) {
+       while (!pipe_empty(head, tail)) {
                struct pipe_buffer *buf = &pipe->bufs[tail & mask];
 
                sd->len = buf->len;
@@ -711,9 +711,7 @@ iter_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
        splice_from_pipe_begin(&sd);
        while (sd.total_len) {
                struct iov_iter from;
-               unsigned int head = pipe->head;
-               unsigned int tail = pipe->tail;
-               unsigned int mask = pipe->ring_size - 1;
+               unsigned int head, tail, mask;
                size_t left;
                int n;
 
@@ -732,6 +730,10 @@ iter_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
                        }
                }
 
+               head = pipe->head;
+               tail = pipe->tail;
+               mask = pipe->ring_size - 1;
+
                /* build the vector */
                left = sd.total_len;
                for (n = 0; !pipe_empty(head, tail) && left && n < nbufs; tail++, n++) {
diff --git a/include/asm-generic/bitops-instrumented.h b/include/asm-generic/bitops-instrumented.h
deleted file mode 100644 (file)
index ddd1c6d..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-
-/*
- * This file provides wrappers with sanitizer instrumentation for bit
- * operations.
- *
- * To use this functionality, an arch's bitops.h file needs to define each of
- * the below bit operations with an arch_ prefix (e.g. arch_set_bit(),
- * arch___set_bit(), etc.).
- */
-#ifndef _ASM_GENERIC_BITOPS_INSTRUMENTED_H
-#define _ASM_GENERIC_BITOPS_INSTRUMENTED_H
-
-#include <linux/kasan-checks.h>
-
-/**
- * set_bit - Atomically set a bit in memory
- * @nr: the bit to set
- * @addr: the address to start counting from
- *
- * This is a relaxed atomic operation (no implied memory barriers).
- *
- * Note that @nr may be almost arbitrarily large; this function is not
- * restricted to acting on a single-word quantity.
- */
-static inline void set_bit(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       arch_set_bit(nr, addr);
-}
-
-/**
- * __set_bit - Set a bit in memory
- * @nr: the bit to set
- * @addr: the address to start counting from
- *
- * Unlike set_bit(), this function is non-atomic. If it is called on the same
- * region of memory concurrently, the effect may be that only one operation
- * succeeds.
- */
-static inline void __set_bit(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       arch___set_bit(nr, addr);
-}
-
-/**
- * clear_bit - Clears a bit in memory
- * @nr: Bit to clear
- * @addr: Address to start counting from
- *
- * This is a relaxed atomic operation (no implied memory barriers).
- */
-static inline void clear_bit(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       arch_clear_bit(nr, addr);
-}
-
-/**
- * __clear_bit - Clears a bit in memory
- * @nr: the bit to clear
- * @addr: the address to start counting from
- *
- * Unlike clear_bit(), this function is non-atomic. If it is called on the same
- * region of memory concurrently, the effect may be that only one operation
- * succeeds.
- */
-static inline void __clear_bit(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       arch___clear_bit(nr, addr);
-}
-
-/**
- * clear_bit_unlock - Clear a bit in memory, for unlock
- * @nr: the bit to set
- * @addr: the address to start counting from
- *
- * This operation is atomic and provides release barrier semantics.
- */
-static inline void clear_bit_unlock(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       arch_clear_bit_unlock(nr, addr);
-}
-
-/**
- * __clear_bit_unlock - Clears a bit in memory
- * @nr: Bit to clear
- * @addr: Address to start counting from
- *
- * This is a non-atomic operation but implies a release barrier before the
- * memory operation. It can be used for an unlock if no other CPUs can
- * concurrently modify other bits in the word.
- */
-static inline void __clear_bit_unlock(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       arch___clear_bit_unlock(nr, addr);
-}
-
-/**
- * change_bit - Toggle a bit in memory
- * @nr: Bit to change
- * @addr: Address to start counting from
- *
- * This is a relaxed atomic operation (no implied memory barriers).
- *
- * Note that @nr may be almost arbitrarily large; this function is not
- * restricted to acting on a single-word quantity.
- */
-static inline void change_bit(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       arch_change_bit(nr, addr);
-}
-
-/**
- * __change_bit - Toggle a bit in memory
- * @nr: the bit to change
- * @addr: the address to start counting from
- *
- * Unlike change_bit(), this function is non-atomic. If it is called on the same
- * region of memory concurrently, the effect may be that only one operation
- * succeeds.
- */
-static inline void __change_bit(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       arch___change_bit(nr, addr);
-}
-
-/**
- * test_and_set_bit - Set a bit and return its old value
- * @nr: Bit to set
- * @addr: Address to count from
- *
- * This is an atomic fully-ordered operation (implied full memory barrier).
- */
-static inline bool test_and_set_bit(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       return arch_test_and_set_bit(nr, addr);
-}
-
-/**
- * __test_and_set_bit - Set a bit and return its old value
- * @nr: Bit to set
- * @addr: Address to count from
- *
- * This operation is non-atomic. If two instances of this operation race, one
- * can appear to succeed but actually fail.
- */
-static inline bool __test_and_set_bit(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       return arch___test_and_set_bit(nr, addr);
-}
-
-/**
- * test_and_set_bit_lock - Set a bit and return its old value, for lock
- * @nr: Bit to set
- * @addr: Address to count from
- *
- * This operation is atomic and provides acquire barrier semantics if
- * the returned value is 0.
- * It can be used to implement bit locks.
- */
-static inline bool test_and_set_bit_lock(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       return arch_test_and_set_bit_lock(nr, addr);
-}
-
-/**
- * test_and_clear_bit - Clear a bit and return its old value
- * @nr: Bit to clear
- * @addr: Address to count from
- *
- * This is an atomic fully-ordered operation (implied full memory barrier).
- */
-static inline bool test_and_clear_bit(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       return arch_test_and_clear_bit(nr, addr);
-}
-
-/**
- * __test_and_clear_bit - Clear a bit and return its old value
- * @nr: Bit to clear
- * @addr: Address to count from
- *
- * This operation is non-atomic. If two instances of this operation race, one
- * can appear to succeed but actually fail.
- */
-static inline bool __test_and_clear_bit(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       return arch___test_and_clear_bit(nr, addr);
-}
-
-/**
- * test_and_change_bit - Change a bit and return its old value
- * @nr: Bit to change
- * @addr: Address to count from
- *
- * This is an atomic fully-ordered operation (implied full memory barrier).
- */
-static inline bool test_and_change_bit(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       return arch_test_and_change_bit(nr, addr);
-}
-
-/**
- * __test_and_change_bit - Change a bit and return its old value
- * @nr: Bit to change
- * @addr: Address to count from
- *
- * This operation is non-atomic. If two instances of this operation race, one
- * can appear to succeed but actually fail.
- */
-static inline bool __test_and_change_bit(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       return arch___test_and_change_bit(nr, addr);
-}
-
-/**
- * test_bit - Determine whether a bit is set
- * @nr: bit number to test
- * @addr: Address to start counting from
- */
-static inline bool test_bit(long nr, const volatile unsigned long *addr)
-{
-       kasan_check_read(addr + BIT_WORD(nr), sizeof(long));
-       return arch_test_bit(nr, addr);
-}
-
-#if defined(arch_clear_bit_unlock_is_negative_byte)
-/**
- * clear_bit_unlock_is_negative_byte - Clear a bit in memory and test if bottom
- *                                     byte is negative, for unlock.
- * @nr: the bit to clear
- * @addr: the address to start counting from
- *
- * This operation is atomic and provides release barrier semantics.
- *
- * This is a bit of a one-trick-pony for the filemap code, which clears
- * PG_locked and tests PG_waiters,
- */
-static inline bool
-clear_bit_unlock_is_negative_byte(long nr, volatile unsigned long *addr)
-{
-       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
-       return arch_clear_bit_unlock_is_negative_byte(nr, addr);
-}
-/* Let everybody know we have it. */
-#define clear_bit_unlock_is_negative_byte clear_bit_unlock_is_negative_byte
-#endif
-
-#endif /* _ASM_GENERIC_BITOPS_INSTRUMENTED_H */
diff --git a/include/asm-generic/bitops/instrumented-atomic.h b/include/asm-generic/bitops/instrumented-atomic.h
new file mode 100644 (file)
index 0000000..18ce3c9
--- /dev/null
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * This file provides wrappers with sanitizer instrumentation for atomic bit
+ * operations.
+ *
+ * To use this functionality, an arch's bitops.h file needs to define each of
+ * the below bit operations with an arch_ prefix (e.g. arch_set_bit(),
+ * arch___set_bit(), etc.).
+ */
+#ifndef _ASM_GENERIC_BITOPS_INSTRUMENTED_ATOMIC_H
+#define _ASM_GENERIC_BITOPS_INSTRUMENTED_ATOMIC_H
+
+#include <linux/kasan-checks.h>
+
+/**
+ * set_bit - Atomically set a bit in memory
+ * @nr: the bit to set
+ * @addr: the address to start counting from
+ *
+ * This is a relaxed atomic operation (no implied memory barriers).
+ *
+ * Note that @nr may be almost arbitrarily large; this function is not
+ * restricted to acting on a single-word quantity.
+ */
+static inline void set_bit(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       arch_set_bit(nr, addr);
+}
+
+/**
+ * clear_bit - Clears a bit in memory
+ * @nr: Bit to clear
+ * @addr: Address to start counting from
+ *
+ * This is a relaxed atomic operation (no implied memory barriers).
+ */
+static inline void clear_bit(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       arch_clear_bit(nr, addr);
+}
+
+/**
+ * change_bit - Toggle a bit in memory
+ * @nr: Bit to change
+ * @addr: Address to start counting from
+ *
+ * This is a relaxed atomic operation (no implied memory barriers).
+ *
+ * Note that @nr may be almost arbitrarily large; this function is not
+ * restricted to acting on a single-word quantity.
+ */
+static inline void change_bit(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       arch_change_bit(nr, addr);
+}
+
+/**
+ * test_and_set_bit - Set a bit and return its old value
+ * @nr: Bit to set
+ * @addr: Address to count from
+ *
+ * This is an atomic fully-ordered operation (implied full memory barrier).
+ */
+static inline bool test_and_set_bit(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       return arch_test_and_set_bit(nr, addr);
+}
+
+/**
+ * test_and_clear_bit - Clear a bit and return its old value
+ * @nr: Bit to clear
+ * @addr: Address to count from
+ *
+ * This is an atomic fully-ordered operation (implied full memory barrier).
+ */
+static inline bool test_and_clear_bit(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       return arch_test_and_clear_bit(nr, addr);
+}
+
+/**
+ * test_and_change_bit - Change a bit and return its old value
+ * @nr: Bit to change
+ * @addr: Address to count from
+ *
+ * This is an atomic fully-ordered operation (implied full memory barrier).
+ */
+static inline bool test_and_change_bit(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       return arch_test_and_change_bit(nr, addr);
+}
+
+#endif /* _ASM_GENERIC_BITOPS_INSTRUMENTED_NON_ATOMIC_H */
diff --git a/include/asm-generic/bitops/instrumented-lock.h b/include/asm-generic/bitops/instrumented-lock.h
new file mode 100644 (file)
index 0000000..ec53fde
--- /dev/null
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * This file provides wrappers with sanitizer instrumentation for bit
+ * locking operations.
+ *
+ * To use this functionality, an arch's bitops.h file needs to define each of
+ * the below bit operations with an arch_ prefix (e.g. arch_set_bit(),
+ * arch___set_bit(), etc.).
+ */
+#ifndef _ASM_GENERIC_BITOPS_INSTRUMENTED_LOCK_H
+#define _ASM_GENERIC_BITOPS_INSTRUMENTED_LOCK_H
+
+#include <linux/kasan-checks.h>
+
+/**
+ * clear_bit_unlock - Clear a bit in memory, for unlock
+ * @nr: the bit to set
+ * @addr: the address to start counting from
+ *
+ * This operation is atomic and provides release barrier semantics.
+ */
+static inline void clear_bit_unlock(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       arch_clear_bit_unlock(nr, addr);
+}
+
+/**
+ * __clear_bit_unlock - Clears a bit in memory
+ * @nr: Bit to clear
+ * @addr: Address to start counting from
+ *
+ * This is a non-atomic operation but implies a release barrier before the
+ * memory operation. It can be used for an unlock if no other CPUs can
+ * concurrently modify other bits in the word.
+ */
+static inline void __clear_bit_unlock(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       arch___clear_bit_unlock(nr, addr);
+}
+
+/**
+ * test_and_set_bit_lock - Set a bit and return its old value, for lock
+ * @nr: Bit to set
+ * @addr: Address to count from
+ *
+ * This operation is atomic and provides acquire barrier semantics if
+ * the returned value is 0.
+ * It can be used to implement bit locks.
+ */
+static inline bool test_and_set_bit_lock(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       return arch_test_and_set_bit_lock(nr, addr);
+}
+
+#if defined(arch_clear_bit_unlock_is_negative_byte)
+/**
+ * clear_bit_unlock_is_negative_byte - Clear a bit in memory and test if bottom
+ *                                     byte is negative, for unlock.
+ * @nr: the bit to clear
+ * @addr: the address to start counting from
+ *
+ * This operation is atomic and provides release barrier semantics.
+ *
+ * This is a bit of a one-trick-pony for the filemap code, which clears
+ * PG_locked and tests PG_waiters,
+ */
+static inline bool
+clear_bit_unlock_is_negative_byte(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       return arch_clear_bit_unlock_is_negative_byte(nr, addr);
+}
+/* Let everybody know we have it. */
+#define clear_bit_unlock_is_negative_byte clear_bit_unlock_is_negative_byte
+#endif
+
+#endif /* _ASM_GENERIC_BITOPS_INSTRUMENTED_LOCK_H */
diff --git a/include/asm-generic/bitops/instrumented-non-atomic.h b/include/asm-generic/bitops/instrumented-non-atomic.h
new file mode 100644 (file)
index 0000000..95ff28d
--- /dev/null
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * This file provides wrappers with sanitizer instrumentation for non-atomic
+ * bit operations.
+ *
+ * To use this functionality, an arch's bitops.h file needs to define each of
+ * the below bit operations with an arch_ prefix (e.g. arch_set_bit(),
+ * arch___set_bit(), etc.).
+ */
+#ifndef _ASM_GENERIC_BITOPS_INSTRUMENTED_NON_ATOMIC_H
+#define _ASM_GENERIC_BITOPS_INSTRUMENTED_NON_ATOMIC_H
+
+#include <linux/kasan-checks.h>
+
+/**
+ * __set_bit - Set a bit in memory
+ * @nr: the bit to set
+ * @addr: the address to start counting from
+ *
+ * Unlike set_bit(), this function is non-atomic. If it is called on the same
+ * region of memory concurrently, the effect may be that only one operation
+ * succeeds.
+ */
+static inline void __set_bit(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       arch___set_bit(nr, addr);
+}
+
+/**
+ * __clear_bit - Clears a bit in memory
+ * @nr: the bit to clear
+ * @addr: the address to start counting from
+ *
+ * Unlike clear_bit(), this function is non-atomic. If it is called on the same
+ * region of memory concurrently, the effect may be that only one operation
+ * succeeds.
+ */
+static inline void __clear_bit(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       arch___clear_bit(nr, addr);
+}
+
+/**
+ * __change_bit - Toggle a bit in memory
+ * @nr: the bit to change
+ * @addr: the address to start counting from
+ *
+ * Unlike change_bit(), this function is non-atomic. If it is called on the same
+ * region of memory concurrently, the effect may be that only one operation
+ * succeeds.
+ */
+static inline void __change_bit(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       arch___change_bit(nr, addr);
+}
+
+/**
+ * __test_and_set_bit - Set a bit and return its old value
+ * @nr: Bit to set
+ * @addr: Address to count from
+ *
+ * This operation is non-atomic. If two instances of this operation race, one
+ * can appear to succeed but actually fail.
+ */
+static inline bool __test_and_set_bit(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       return arch___test_and_set_bit(nr, addr);
+}
+
+/**
+ * __test_and_clear_bit - Clear a bit and return its old value
+ * @nr: Bit to clear
+ * @addr: Address to count from
+ *
+ * This operation is non-atomic. If two instances of this operation race, one
+ * can appear to succeed but actually fail.
+ */
+static inline bool __test_and_clear_bit(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       return arch___test_and_clear_bit(nr, addr);
+}
+
+/**
+ * __test_and_change_bit - Change a bit and return its old value
+ * @nr: Bit to change
+ * @addr: Address to count from
+ *
+ * This operation is non-atomic. If two instances of this operation race, one
+ * can appear to succeed but actually fail.
+ */
+static inline bool __test_and_change_bit(long nr, volatile unsigned long *addr)
+{
+       kasan_check_write(addr + BIT_WORD(nr), sizeof(long));
+       return arch___test_and_change_bit(nr, addr);
+}
+
+/**
+ * test_bit - Determine whether a bit is set
+ * @nr: bit number to test
+ * @addr: Address to start counting from
+ */
+static inline bool test_bit(long nr, const volatile unsigned long *addr)
+{
+       kasan_check_read(addr + BIT_WORD(nr), sizeof(long));
+       return arch_test_bit(nr, addr);
+}
+
+#endif /* _ASM_GENERIC_BITOPS_INSTRUMENTED_NON_ATOMIC_H */
index c6b61ca..21b34a9 100644 (file)
@@ -30,8 +30,6 @@
 #include <linux/agp_backend.h>
 #include <uapi/linux/agpgart.h>
 
-#define AGPGART_MINOR 175
-
 struct agp_info {
        struct agp_version version;     /* version of the driver        */
        u32 bridge_id;          /* bridge vendor/device         */
index 6012e25..47eb22a 100644 (file)
@@ -357,8 +357,7 @@ typedef int (*report_zones_cb)(struct blk_zone *zone, unsigned int idx,
 #define BLK_ALL_ZONES  ((unsigned int)-1)
 int blkdev_report_zones(struct block_device *bdev, sector_t sector,
                        unsigned int nr_zones, report_zones_cb cb, void *data);
-
-extern unsigned int blkdev_nr_zones(struct block_device *bdev);
+unsigned int blkdev_nr_zones(struct gendisk *disk);
 extern int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
                            sector_t sectors, sector_t nr_sectors,
                            gfp_t gfp_mask);
@@ -371,12 +370,7 @@ extern int blkdev_zone_mgmt_ioctl(struct block_device *bdev, fmode_t mode,
 
 #else /* CONFIG_BLK_DEV_ZONED */
 
-static inline unsigned int blkdev_nr_zones(struct block_device *bdev)
-{
-       return 0;
-}
-
-static inline int blk_revalidate_disk_zones(struct gendisk *disk)
+static inline unsigned int blkdev_nr_zones(struct gendisk *disk)
 {
        return 0;
 }
@@ -504,9 +498,9 @@ struct request_queue {
        /*
         * Zoned block device information for request dispatch control.
         * nr_zones is the total number of zones of the device. This is always
-        * 0 for regular block devices. seq_zones_bitmap is a bitmap of nr_zones
-        * bits which indicates if a zone is conventional (bit clear) or
-        * sequential (bit set). seq_zones_wlock is a bitmap of nr_zones
+        * 0 for regular block devices. conv_zones_bitmap is a bitmap of nr_zones
+        * bits which indicates if a zone is conventional (bit set) or
+        * sequential (bit clear). seq_zones_wlock is a bitmap of nr_zones
         * bits which indicates if a zone is write locked, that is, if a write
         * request targeting the zone was dispatched. All three fields are
         * initialized by the low level device driver (e.g. scsi/sd.c).
@@ -519,7 +513,7 @@ struct request_queue {
         * blk_mq_unfreeze_queue().
         */
        unsigned int            nr_zones;
-       unsigned long           *seq_zones_bitmap;
+       unsigned long           *conv_zones_bitmap;
        unsigned long           *seq_zones_wlock;
 #endif /* CONFIG_BLK_DEV_ZONED */
 
@@ -724,9 +718,11 @@ static inline unsigned int blk_queue_zone_no(struct request_queue *q,
 static inline bool blk_queue_zone_is_seq(struct request_queue *q,
                                         sector_t sector)
 {
-       if (!blk_queue_is_zoned(q) || !q->seq_zones_bitmap)
+       if (!blk_queue_is_zoned(q))
                return false;
-       return test_bit(blk_queue_zone_no(q, sector), q->seq_zones_bitmap);
+       if (!q->conv_zones_bitmap)
+               return true;
+       return !test_bit(blk_queue_zone_no(q, sector), q->conv_zones_bitmap);
 }
 #else /* CONFIG_BLK_DEV_ZONED */
 static inline unsigned int blk_queue_nr_zones(struct request_queue *q)
index a032f01..679a422 100644 (file)
@@ -87,26 +87,24 @@ struct bvec_iter_all {
 static inline bool bvec_iter_advance(const struct bio_vec *bv,
                struct bvec_iter *iter, unsigned bytes)
 {
+       unsigned int idx = iter->bi_idx;
+
        if (WARN_ONCE(bytes > iter->bi_size,
                     "Attempted to advance past end of bvec iter\n")) {
                iter->bi_size = 0;
                return false;
        }
 
-       while (bytes) {
-               const struct bio_vec *cur = bv + iter->bi_idx;
-               unsigned len = min3(bytes, iter->bi_size,
-                                   cur->bv_len - iter->bi_bvec_done);
-
-               bytes -= len;
-               iter->bi_size -= len;
-               iter->bi_bvec_done += len;
+       iter->bi_size -= bytes;
+       bytes += iter->bi_bvec_done;
 
-               if (iter->bi_bvec_done == cur->bv_len) {
-                       iter->bi_bvec_done = 0;
-                       iter->bi_idx++;
-               }
+       while (bytes && bytes >= bv[idx].bv_len) {
+               bytes -= bv[idx].bv_len;
+               idx++;
        }
+
+       iter->bi_idx = idx;
+       iter->bi_bvec_done = bytes;
        return true;
 }
 
index b9dbda1..8fe9b80 100644 (file)
@@ -280,10 +280,12 @@ extern const char *ceph_msg_type_name(int type);
 extern int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid);
 extern void *ceph_kvmalloc(size_t size, gfp_t flags);
 
-extern struct ceph_options *ceph_parse_options(char *options,
-                             const char *dev_name, const char *dev_name_end,
-                             int (*parse_extra_token)(char *c, void *private),
-                             void *private);
+struct fs_parameter;
+struct ceph_options *ceph_alloc_options(void);
+int ceph_parse_mon_ips(const char *buf, size_t len, struct ceph_options *opt,
+                      struct fs_context *fc);
+int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt,
+                    struct fs_context *fc);
 int ceph_print_client_options(struct seq_file *m, struct ceph_client *client,
                              bool show_all);
 extern void ceph_destroy_options(struct ceph_options *opt);
index 10090f1..c1488cc 100644 (file)
@@ -440,6 +440,11 @@ static inline bool d_is_negative(const struct dentry *dentry)
        return d_is_miss(dentry);
 }
 
+static inline bool d_flags_negative(unsigned flags)
+{
+       return (flags & DCACHE_ENTRY_TYPE) == DCACHE_MISS_TYPE;
+}
+
 static inline bool d_is_positive(const struct dentry *dentry)
 {
        return !d_is_negative(dentry);
index aee5c86..6278414 100644 (file)
@@ -47,7 +47,7 @@ extern struct module __this_module;
  * absolute relocations that require runtime processing on relocatable
  * kernels.
  */
-#define __KSYMTAB_ENTRY_NS(sym, sec)                                   \
+#define __KSYMTAB_ENTRY(sym, sec)                                      \
        __ADDRESSABLE(sym)                                              \
        asm("   .section \"___ksymtab" sec "+" #sym "\", \"a\"  \n"     \
            "   .balign 4                                       \n"     \
@@ -57,33 +57,17 @@ extern struct module __this_module;
            "   .long   __kstrtabns_" #sym "- .                 \n"     \
            "   .previous                                       \n")
 
-#define __KSYMTAB_ENTRY(sym, sec)                                      \
-       __ADDRESSABLE(sym)                                              \
-       asm("   .section \"___ksymtab" sec "+" #sym "\", \"a\"  \n"     \
-           "   .balign 4                                       \n"     \
-           "__ksymtab_" #sym ":                                \n"     \
-           "   .long   " #sym "- .                             \n"     \
-           "   .long   __kstrtab_" #sym "- .                   \n"     \
-           "   .long   0                                       \n"     \
-           "   .previous                                       \n")
-
 struct kernel_symbol {
        int value_offset;
        int name_offset;
        int namespace_offset;
 };
 #else
-#define __KSYMTAB_ENTRY_NS(sym, sec)                                   \
-       static const struct kernel_symbol __ksymtab_##sym               \
-       __attribute__((section("___ksymtab" sec "+" #sym), used))       \
-       __aligned(sizeof(void *))                                       \
-       = { (unsigned long)&sym, __kstrtab_##sym, __kstrtabns_##sym }
-
 #define __KSYMTAB_ENTRY(sym, sec)                                      \
        static const struct kernel_symbol __ksymtab_##sym               \
        __attribute__((section("___ksymtab" sec "+" #sym), used))       \
        __aligned(sizeof(void *))                                       \
-       = { (unsigned long)&sym, __kstrtab_##sym, NULL }
+       = { (unsigned long)&sym, __kstrtab_##sym, __kstrtabns_##sym }
 
 struct kernel_symbol {
        unsigned long value;
@@ -94,28 +78,20 @@ struct kernel_symbol {
 
 #ifdef __GENKSYMS__
 
-#define ___EXPORT_SYMBOL(sym,sec)      __GENKSYMS_EXPORT_SYMBOL(sym)
-#define ___EXPORT_SYMBOL_NS(sym,sec,ns)        __GENKSYMS_EXPORT_SYMBOL(sym)
+#define ___EXPORT_SYMBOL(sym, sec, ns) __GENKSYMS_EXPORT_SYMBOL(sym)
 
 #else
 
-#define ___export_symbol_common(sym, sec)                              \
+/* For every exported symbol, place a struct in the __ksymtab section */
+#define ___EXPORT_SYMBOL(sym, sec, ns)                                 \
        extern typeof(sym) sym;                                         \
        __CRC_SYMBOL(sym, sec);                                         \
        static const char __kstrtab_##sym[]                             \
        __attribute__((section("__ksymtab_strings"), used, aligned(1))) \
-       = #sym                                                          \
-
-/* For every exported symbol, place a struct in the __ksymtab section */
-#define ___EXPORT_SYMBOL_NS(sym, sec, ns)                              \
-       ___export_symbol_common(sym, sec);                              \
+       = #sym;                                                         \
        static const char __kstrtabns_##sym[]                           \
        __attribute__((section("__ksymtab_strings"), used, aligned(1))) \
-       = #ns;                                                          \
-       __KSYMTAB_ENTRY_NS(sym, sec)
-
-#define ___EXPORT_SYMBOL(sym, sec)                                     \
-       ___export_symbol_common(sym, sec);                              \
+       = ns;                                                           \
        __KSYMTAB_ENTRY(sym, sec)
 
 #endif
@@ -127,8 +103,7 @@ struct kernel_symbol {
  * be reused in other execution contexts such as the UEFI stub or the
  * decompressor.
  */
-#define __EXPORT_SYMBOL_NS(sym, sec, ns)
-#define __EXPORT_SYMBOL(sym, sec)
+#define __EXPORT_SYMBOL(sym, sec, ns)
 
 #elif defined(CONFIG_TRIM_UNUSED_KSYMS)
 
@@ -144,48 +119,38 @@ struct kernel_symbol {
 #define __ksym_marker(sym)     \
        static int __ksym_marker_##sym[0] __section(".discard.ksym") __used
 
-#define __EXPORT_SYMBOL(sym, sec)                              \
-       __ksym_marker(sym);                                     \
-       __cond_export_sym(sym, sec, __is_defined(__KSYM_##sym))
-#define __cond_export_sym(sym, sec, conf)                      \
-       ___cond_export_sym(sym, sec, conf)
-#define ___cond_export_sym(sym, sec, enabled)                  \
-       __cond_export_sym_##enabled(sym, sec)
-#define __cond_export_sym_1(sym, sec) ___EXPORT_SYMBOL(sym, sec)
-#define __cond_export_sym_0(sym, sec) /* nothing */
-
-#define __EXPORT_SYMBOL_NS(sym, sec, ns)                               \
+#define __EXPORT_SYMBOL(sym, sec, ns)                                  \
        __ksym_marker(sym);                                             \
-       __cond_export_ns_sym(sym, sec, ns, __is_defined(__KSYM_##sym))
-#define __cond_export_ns_sym(sym, sec, ns, conf)                       \
-       ___cond_export_ns_sym(sym, sec, ns, conf)
-#define ___cond_export_ns_sym(sym, sec, ns, enabled)                   \
-       __cond_export_ns_sym_##enabled(sym, sec, ns)
-#define __cond_export_ns_sym_1(sym, sec, ns) ___EXPORT_SYMBOL_NS(sym, sec, ns)
-#define __cond_export_ns_sym_0(sym, sec, ns) /* nothing */
+       __cond_export_sym(sym, sec, ns, __is_defined(__KSYM_##sym))
+#define __cond_export_sym(sym, sec, ns, conf)                          \
+       ___cond_export_sym(sym, sec, ns, conf)
+#define ___cond_export_sym(sym, sec, ns, enabled)                      \
+       __cond_export_sym_##enabled(sym, sec, ns)
+#define __cond_export_sym_1(sym, sec, ns) ___EXPORT_SYMBOL(sym, sec, ns)
+#define __cond_export_sym_0(sym, sec, ns) /* nothing */
 
 #else
 
-#define __EXPORT_SYMBOL_NS(sym,sec,ns) ___EXPORT_SYMBOL_NS(sym,sec,ns)
-#define __EXPORT_SYMBOL(sym,sec)       ___EXPORT_SYMBOL(sym,sec)
+#define __EXPORT_SYMBOL(sym, sec, ns)  ___EXPORT_SYMBOL(sym, sec, ns)
 
 #endif /* CONFIG_MODULES */
 
 #ifdef DEFAULT_SYMBOL_NAMESPACE
-#undef __EXPORT_SYMBOL
-#define __EXPORT_SYMBOL(sym, sec)                              \
-       __EXPORT_SYMBOL_NS(sym, sec, DEFAULT_SYMBOL_NAMESPACE)
+#include <linux/stringify.h>
+#define _EXPORT_SYMBOL(sym, sec)       __EXPORT_SYMBOL(sym, sec, __stringify(DEFAULT_SYMBOL_NAMESPACE))
+#else
+#define _EXPORT_SYMBOL(sym, sec)       __EXPORT_SYMBOL(sym, sec, "")
 #endif
 
-#define EXPORT_SYMBOL(sym)             __EXPORT_SYMBOL(sym, "")
-#define EXPORT_SYMBOL_GPL(sym)         __EXPORT_SYMBOL(sym, "_gpl")
-#define EXPORT_SYMBOL_GPL_FUTURE(sym)  __EXPORT_SYMBOL(sym, "_gpl_future")
-#define EXPORT_SYMBOL_NS(sym, ns)      __EXPORT_SYMBOL_NS(sym, "", ns)
-#define EXPORT_SYMBOL_NS_GPL(sym, ns)  __EXPORT_SYMBOL_NS(sym, "_gpl", ns)
+#define EXPORT_SYMBOL(sym)             _EXPORT_SYMBOL(sym, "")
+#define EXPORT_SYMBOL_GPL(sym)         _EXPORT_SYMBOL(sym, "_gpl")
+#define EXPORT_SYMBOL_GPL_FUTURE(sym)  _EXPORT_SYMBOL(sym, "_gpl_future")
+#define EXPORT_SYMBOL_NS(sym, ns)      __EXPORT_SYMBOL(sym, "", #ns)
+#define EXPORT_SYMBOL_NS_GPL(sym, ns)  __EXPORT_SYMBOL(sym, "_gpl", #ns)
 
 #ifdef CONFIG_UNUSED_SYMBOLS
-#define EXPORT_UNUSED_SYMBOL(sym)      __EXPORT_SYMBOL(sym, "_unused")
-#define EXPORT_UNUSED_SYMBOL_GPL(sym)  __EXPORT_SYMBOL(sym, "_unused_gpl")
+#define EXPORT_UNUSED_SYMBOL(sym)      _EXPORT_SYMBOL(sym, "_unused")
+#define EXPORT_UNUSED_SYMBOL_GPL(sym)  _EXPORT_SYMBOL(sym, "_unused_gpl")
 #else
 #define EXPORT_UNUSED_SYMBOL(sym)
 #define EXPORT_UNUSED_SYMBOL_GPL(sym)
index b06b757..becde69 100644 (file)
@@ -33,6 +33,7 @@
 #define SGI_MMTIMER            153
 #define STORE_QUEUE_MINOR      155     /* unused */
 #define I2O_MINOR              166
+#define AGPGART_MINOR          175
 #define HWRNG_MINOR            183
 #define MICROCODE_MINOR                184
 #define IRNET_MINOR            187
index 5ba250d..e5c3e23 100644 (file)
@@ -100,11 +100,11 @@ struct kparam_array
 
 /**
  * module_param - typesafe helper for a module/cmdline parameter
- * @value: the variable to alter, and exposed parameter name.
+ * @name: the variable to alter, and exposed parameter name.
  * @type: the type of the parameter
  * @perm: visibility in sysfs.
  *
- * @value becomes the module parameter, or (prefixed by KBUILD_MODNAME and a
+ * @name becomes the module parameter, or (prefixed by KBUILD_MODNAME and a
  * ".") the kernel commandline parameter.  Note that - is changed to _, so
  * the user can use "foo-bar=1" even for variable "foo_bar".
  *
index 397a08a..7fe7b87 100644 (file)
@@ -60,6 +60,7 @@ extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
 extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
 extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
 extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int);
+extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int);
 
 extern int follow_down_one(struct path *);
 extern int follow_down(struct path *);
index ffd72b3..d05ddac 100644 (file)
@@ -24,6 +24,26 @@ struct qcom_scm_vmperm {
        int perm;
 };
 
+enum qcom_scm_ocmem_client {
+       QCOM_SCM_OCMEM_UNUSED_ID = 0x0,
+       QCOM_SCM_OCMEM_GRAPHICS_ID,
+       QCOM_SCM_OCMEM_VIDEO_ID,
+       QCOM_SCM_OCMEM_LP_AUDIO_ID,
+       QCOM_SCM_OCMEM_SENSORS_ID,
+       QCOM_SCM_OCMEM_OTHER_OS_ID,
+       QCOM_SCM_OCMEM_DEBUG_ID,
+};
+
+enum qcom_scm_sec_dev_id {
+       QCOM_SCM_MDSS_DEV_ID    = 1,
+       QCOM_SCM_OCMEM_DEV_ID   = 5,
+       QCOM_SCM_PCIE0_DEV_ID   = 11,
+       QCOM_SCM_PCIE1_DEV_ID   = 12,
+       QCOM_SCM_GFX_DEV_ID     = 18,
+       QCOM_SCM_UFS_DEV_ID     = 19,
+       QCOM_SCM_ICE_DEV_ID     = 20,
+};
+
 #define QCOM_SCM_VMID_HLOS       0x3
 #define QCOM_SCM_VMID_MSS_MSA    0xF
 #define QCOM_SCM_VMID_WLAN       0x18
@@ -41,6 +61,11 @@ extern bool qcom_scm_is_available(void);
 extern bool qcom_scm_hdcp_available(void);
 extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
                             u32 *resp);
+extern bool qcom_scm_ocmem_lock_available(void);
+extern int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset,
+                              u32 size, u32 mode);
+extern int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset,
+                                u32 size);
 extern bool qcom_scm_pas_supported(u32 peripheral);
 extern int qcom_scm_pas_init_image(u32 peripheral, const void *metadata,
                                   size_t size);
@@ -55,6 +80,7 @@ extern int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
 extern void qcom_scm_cpu_power_down(u32 flags);
 extern u32 qcom_scm_get_version(void);
 extern int qcom_scm_set_remote_state(u32 state, u32 id);
+extern bool qcom_scm_restore_sec_cfg_available(void);
 extern int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare);
 extern int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size);
 extern int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare);
index 4bde630..2d23134 100644 (file)
@@ -378,12 +378,19 @@ extern int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg,
 extern int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg,
                          unsigned int vlen, unsigned int flags,
                          bool forbid_cmsg_compat);
-extern long __sys_sendmsg_sock(struct socket *sock,
-                              struct user_msghdr __user *msg,
+extern long __sys_sendmsg_sock(struct socket *sock, struct msghdr *msg,
                               unsigned int flags);
-extern long __sys_recvmsg_sock(struct socket *sock,
-                              struct user_msghdr __user *msg,
+extern long __sys_recvmsg_sock(struct socket *sock, struct msghdr *msg,
+                              struct user_msghdr __user *umsg,
+                              struct sockaddr __user *uaddr,
                               unsigned int flags);
+extern int sendmsg_copy_msghdr(struct msghdr *msg,
+                              struct user_msghdr __user *umsg, unsigned flags,
+                              struct iovec **iov);
+extern int recvmsg_copy_msghdr(struct msghdr *msg,
+                              struct user_msghdr __user *umsg, unsigned flags,
+                              struct sockaddr __user **uaddr,
+                              struct iovec **iov);
 
 /* helpers which do the actual work for syscalls */
 extern int __sys_recvfrom(int fd, void __user *ubuf, size_t size,
@@ -399,9 +406,8 @@ extern int __sys_accept4(int fd, struct sockaddr __user *upeer_sockaddr,
                         int __user *upeer_addrlen, int flags);
 extern int __sys_socket(int family, int type, int protocol);
 extern int __sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen);
-extern int __sys_connect_file(struct file *file,
-                       struct sockaddr __user *uservaddr, int addrlen,
-                       int file_flags);
+extern int __sys_connect_file(struct file *file, struct sockaddr_storage *addr,
+                             int addrlen, int file_flags);
 extern int __sys_connect(int fd, struct sockaddr __user *uservaddr,
                         int addrlen);
 extern int __sys_listen(int fd, int backlog);
diff --git a/include/soc/qcom/ocmem.h b/include/soc/qcom/ocmem.h
new file mode 100644 (file)
index 0000000..02a8bc2
--- /dev/null
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * The On Chip Memory (OCMEM) allocator allows various clients to allocate
+ * memory from OCMEM based on performance, latency and power requirements.
+ * This is typically used by the GPU, camera/video, and audio components on
+ * some Snapdragon SoCs.
+ *
+ * Copyright (C) 2019 Brian Masney <masneyb@onstation.org>
+ * Copyright (C) 2015 Red Hat. Author: Rob Clark <robdclark@gmail.com>
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+
+#ifndef __OCMEM_H__
+#define __OCMEM_H__
+
+enum ocmem_client {
+       /* GMEM clients */
+       OCMEM_GRAPHICS = 0x0,
+       /*
+        * TODO add more once ocmem_allocate() is clever enough to
+        * deal with multiple clients.
+        */
+       OCMEM_CLIENT_MAX,
+};
+
+struct ocmem;
+
+struct ocmem_buf {
+       unsigned long offset;
+       unsigned long addr;
+       unsigned long len;
+};
+
+#if IS_ENABLED(CONFIG_QCOM_OCMEM)
+
+struct ocmem *of_get_ocmem(struct device *dev);
+struct ocmem_buf *ocmem_allocate(struct ocmem *ocmem, enum ocmem_client client,
+                                unsigned long size);
+void ocmem_free(struct ocmem *ocmem, enum ocmem_client client,
+               struct ocmem_buf *buf);
+
+#else /* IS_ENABLED(CONFIG_QCOM_OCMEM) */
+
+static inline struct ocmem *of_get_ocmem(struct device *dev)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+static inline struct ocmem_buf *ocmem_allocate(struct ocmem *ocmem,
+                                              enum ocmem_client client,
+                                              unsigned long size)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+static inline void ocmem_free(struct ocmem *ocmem, enum ocmem_client client,
+                             struct ocmem_buf *buf)
+{
+}
+
+#endif /* IS_ENABLED(CONFIG_QCOM_OCMEM) */
+
+#endif /* __OCMEM_H__ */
index b260c5f..e05b95e 100644 (file)
@@ -493,6 +493,7 @@ struct hdac_stream {
        bool prepared:1;
        bool no_period_wakeup:1;
        bool locked:1;
+       bool stripe:1;                  /* apply stripe control */
 
        /* timestamp */
        unsigned long start_wallclk;    /* start + minimum wallclk */
index 4637ed1..eabccb4 100644 (file)
@@ -157,6 +157,7 @@ struct io_uring_params {
  */
 #define IORING_FEAT_SINGLE_MMAP                (1U << 0)
 #define IORING_FEAT_NODROP             (1U << 1)
+#define IORING_FEAT_SUBMIT_STABLE      (1U << 2)
 
 /*
  * io_uring_register(2) opcodes and arguments
index 052a402..3a486f8 100644 (file)
@@ -1033,6 +1033,8 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
        strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));
 
        free_module(mod);
+       /* someone could wait for the module in add_unformed_module() */
+       wake_up_all(&module_wq);
        return 0;
 out:
        mutex_unlock(&module_mutex);
@@ -1400,7 +1402,7 @@ static int verify_namespace_is_imported(const struct load_info *info,
        char *imported_namespace;
 
        namespace = kernel_symbol_namespace(sym);
-       if (namespace) {
+       if (namespace && namespace[0]) {
                imported_namespace = get_modinfo(info, "import_ns");
                while (imported_namespace) {
                        if (strcmp(namespace, imported_namespace) == 0)
index 2d56824..a9d6c97 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/module.h>
 #include <linux/mount.h>
 #include <linux/nsproxy.h>
-#include <linux/parser.h>
+#include <linux/fs_parser.h>
 #include <linux/sched.h>
 #include <linux/sched/mm.h>
 #include <linux/seq_file.h>
@@ -254,58 +254,77 @@ enum {
        Opt_mount_timeout,
        Opt_osd_idle_ttl,
        Opt_osd_request_timeout,
-       Opt_last_int,
        /* int args above */
        Opt_fsid,
        Opt_name,
        Opt_secret,
        Opt_key,
        Opt_ip,
-       Opt_last_string,
        /* string args above */
        Opt_share,
-       Opt_noshare,
        Opt_crc,
-       Opt_nocrc,
        Opt_cephx_require_signatures,
-       Opt_nocephx_require_signatures,
        Opt_cephx_sign_messages,
-       Opt_nocephx_sign_messages,
        Opt_tcp_nodelay,
-       Opt_notcp_nodelay,
        Opt_abort_on_full,
 };
 
-static match_table_t opt_tokens = {
-       {Opt_osdtimeout, "osdtimeout=%d"},
-       {Opt_osdkeepalivetimeout, "osdkeepalive=%d"},
-       {Opt_mount_timeout, "mount_timeout=%d"},
-       {Opt_osd_idle_ttl, "osd_idle_ttl=%d"},
-       {Opt_osd_request_timeout, "osd_request_timeout=%d"},
-       /* int args above */
-       {Opt_fsid, "fsid=%s"},
-       {Opt_name, "name=%s"},
-       {Opt_secret, "secret=%s"},
-       {Opt_key, "key=%s"},
-       {Opt_ip, "ip=%s"},
-       /* string args above */
-       {Opt_share, "share"},
-       {Opt_noshare, "noshare"},
-       {Opt_crc, "crc"},
-       {Opt_nocrc, "nocrc"},
-       {Opt_cephx_require_signatures, "cephx_require_signatures"},
-       {Opt_nocephx_require_signatures, "nocephx_require_signatures"},
-       {Opt_cephx_sign_messages, "cephx_sign_messages"},
-       {Opt_nocephx_sign_messages, "nocephx_sign_messages"},
-       {Opt_tcp_nodelay, "tcp_nodelay"},
-       {Opt_notcp_nodelay, "notcp_nodelay"},
-       {Opt_abort_on_full, "abort_on_full"},
-       {-1, NULL}
+static const struct fs_parameter_spec ceph_param_specs[] = {
+       fsparam_flag    ("abort_on_full",               Opt_abort_on_full),
+       fsparam_flag_no ("cephx_require_signatures",    Opt_cephx_require_signatures),
+       fsparam_flag_no ("cephx_sign_messages",         Opt_cephx_sign_messages),
+       fsparam_flag_no ("crc",                         Opt_crc),
+       fsparam_string  ("fsid",                        Opt_fsid),
+       fsparam_string  ("ip",                          Opt_ip),
+       fsparam_string  ("key",                         Opt_key),
+       fsparam_u32     ("mount_timeout",               Opt_mount_timeout),
+       fsparam_string  ("name",                        Opt_name),
+       fsparam_u32     ("osd_idle_ttl",                Opt_osd_idle_ttl),
+       fsparam_u32     ("osd_request_timeout",         Opt_osd_request_timeout),
+       fsparam_u32     ("osdkeepalive",                Opt_osdkeepalivetimeout),
+       __fsparam       (fs_param_is_s32, "osdtimeout", Opt_osdtimeout,
+                        fs_param_deprecated),
+       fsparam_string  ("secret",                      Opt_secret),
+       fsparam_flag_no ("share",                       Opt_share),
+       fsparam_flag_no ("tcp_nodelay",                 Opt_tcp_nodelay),
+       {}
+};
+
+static const struct fs_parameter_description ceph_parameters = {
+        .name           = "libceph",
+        .specs          = ceph_param_specs,
 };
 
+struct ceph_options *ceph_alloc_options(void)
+{
+       struct ceph_options *opt;
+
+       opt = kzalloc(sizeof(*opt), GFP_KERNEL);
+       if (!opt)
+               return NULL;
+
+       opt->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*opt->mon_addr),
+                               GFP_KERNEL);
+       if (!opt->mon_addr) {
+               kfree(opt);
+               return NULL;
+       }
+
+       opt->flags = CEPH_OPT_DEFAULT;
+       opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT;
+       opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT;
+       opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT;
+       opt->osd_request_timeout = CEPH_OSD_REQUEST_TIMEOUT_DEFAULT;
+       return opt;
+}
+EXPORT_SYMBOL(ceph_alloc_options);
+
 void ceph_destroy_options(struct ceph_options *opt)
 {
        dout("destroy_options %p\n", opt);
+       if (!opt)
+               return;
+
        kfree(opt->name);
        if (opt->key) {
                ceph_crypto_key_destroy(opt->key);
@@ -317,7 +336,9 @@ void ceph_destroy_options(struct ceph_options *opt)
 EXPORT_SYMBOL(ceph_destroy_options);
 
 /* get secret from key store */
-static int get_secret(struct ceph_crypto_key *dst, const char *name) {
+static int get_secret(struct ceph_crypto_key *dst, const char *name,
+                     struct fs_context *fc)
+{
        struct key *ukey;
        int key_err;
        int err = 0;
@@ -330,20 +351,20 @@ static int get_secret(struct ceph_crypto_key *dst, const char *name) {
                key_err = PTR_ERR(ukey);
                switch (key_err) {
                case -ENOKEY:
-                       pr_warn("ceph: Mount failed due to key not found: %s\n",
-                               name);
+                       errorf(fc, "libceph: Failed due to key not found: %s",
+                              name);
                        break;
                case -EKEYEXPIRED:
-                       pr_warn("ceph: Mount failed due to expired key: %s\n",
-                               name);
+                       errorf(fc, "libceph: Failed due to expired key: %s",
+                              name);
                        break;
                case -EKEYREVOKED:
-                       pr_warn("ceph: Mount failed due to revoked key: %s\n",
-                               name);
+                       errorf(fc, "libceph: Failed due to revoked key: %s",
+                              name);
                        break;
                default:
-                       pr_warn("ceph: Mount failed due to unknown key error %d: %s\n",
-                               key_err, name);
+                       errorf(fc, "libceph: Failed due to key error %d: %s",
+                              key_err, name);
                }
                err = -EPERM;
                goto out;
@@ -361,217 +382,157 @@ out:
        return err;
 }
 
-struct ceph_options *
-ceph_parse_options(char *options, const char *dev_name,
-                       const char *dev_name_end,
-                       int (*parse_extra_token)(char *c, void *private),
-                       void *private)
+int ceph_parse_mon_ips(const char *buf, size_t len, struct ceph_options *opt,
+                      struct fs_context *fc)
 {
-       struct ceph_options *opt;
-       const char *c;
-       int err = -ENOMEM;
-       substring_t argstr[MAX_OPT_ARGS];
-
-       opt = kzalloc(sizeof(*opt), GFP_KERNEL);
-       if (!opt)
-               return ERR_PTR(-ENOMEM);
-       opt->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*opt->mon_addr),
-                               GFP_KERNEL);
-       if (!opt->mon_addr)
-               goto out;
-
-       dout("parse_options %p options '%s' dev_name '%s'\n", opt, options,
-            dev_name);
-
-       /* start with defaults */
-       opt->flags = CEPH_OPT_DEFAULT;
-       opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT;
-       opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT;
-       opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT;
-       opt->osd_request_timeout = CEPH_OSD_REQUEST_TIMEOUT_DEFAULT;
+       int ret;
 
-       /* get mon ip(s) */
        /* ip1[:port1][,ip2[:port2]...] */
-       err = ceph_parse_ips(dev_name, dev_name_end, opt->mon_addr,
-                            CEPH_MAX_MON, &opt->num_mon);
-       if (err < 0)
-               goto out;
+       ret = ceph_parse_ips(buf, buf + len, opt->mon_addr, CEPH_MAX_MON,
+                            &opt->num_mon);
+       if (ret) {
+               errorf(fc, "libceph: Failed to parse monitor IPs: %d", ret);
+               return ret;
+       }
 
-       /* parse mount options */
-       while ((c = strsep(&options, ",")) != NULL) {
-               int token, intval;
-               if (!*c)
-                       continue;
-               err = -EINVAL;
-               token = match_token((char *)c, opt_tokens, argstr);
-               if (token < 0 && parse_extra_token) {
-                       /* extra? */
-                       err = parse_extra_token((char *)c, private);
-                       if (err < 0) {
-                               pr_err("bad option at '%s'\n", c);
-                               goto out;
-                       }
-                       continue;
-               }
-               if (token < Opt_last_int) {
-                       err = match_int(&argstr[0], &intval);
-                       if (err < 0) {
-                               pr_err("bad option arg (not int) at '%s'\n", c);
-                               goto out;
-                       }
-                       dout("got int token %d val %d\n", token, intval);
-               } else if (token > Opt_last_int && token < Opt_last_string) {
-                       dout("got string token %d val %s\n", token,
-                            argstr[0].from);
-               } else {
-                       dout("got token %d\n", token);
+       return 0;
+}
+EXPORT_SYMBOL(ceph_parse_mon_ips);
+
+int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt,
+                    struct fs_context *fc)
+{
+       struct fs_parse_result result;
+       int token, err;
+
+       token = fs_parse(fc, &ceph_parameters, param, &result);
+       dout("%s fs_parse '%s' token %d\n", __func__, param->key, token);
+       if (token < 0)
+               return token;
+
+       switch (token) {
+       case Opt_ip:
+               err = ceph_parse_ips(param->string,
+                                    param->string + param->size,
+                                    &opt->my_addr,
+                                    1, NULL);
+               if (err) {
+                       errorf(fc, "libceph: Failed to parse ip: %d", err);
+                       return err;
                }
-               switch (token) {
-               case Opt_ip:
-                       err = ceph_parse_ips(argstr[0].from,
-                                            argstr[0].to,
-                                            &opt->my_addr,
-                                            1, NULL);
-                       if (err < 0)
-                               goto out;
-                       opt->flags |= CEPH_OPT_MYIP;
-                       break;
+               opt->flags |= CEPH_OPT_MYIP;
+               break;
 
-               case Opt_fsid:
-                       err = parse_fsid(argstr[0].from, &opt->fsid);
-                       if (err == 0)
-                               opt->flags |= CEPH_OPT_FSID;
-                       break;
-               case Opt_name:
-                       kfree(opt->name);
-                       opt->name = kstrndup(argstr[0].from,
-                                             argstr[0].to-argstr[0].from,
-                                             GFP_KERNEL);
-                       if (!opt->name) {
-                               err = -ENOMEM;
-                               goto out;
-                       }
-                       break;
-               case Opt_secret:
-                       ceph_crypto_key_destroy(opt->key);
-                       kfree(opt->key);
-
-                       opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
-                       if (!opt->key) {
-                               err = -ENOMEM;
-                               goto out;
-                       }
-                       err = ceph_crypto_key_unarmor(opt->key, argstr[0].from);
-                       if (err < 0)
-                               goto out;
-                       break;
-               case Opt_key:
-                       ceph_crypto_key_destroy(opt->key);
-                       kfree(opt->key);
-
-                       opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
-                       if (!opt->key) {
-                               err = -ENOMEM;
-                               goto out;
-                       }
-                       err = get_secret(opt->key, argstr[0].from);
-                       if (err < 0)
-                               goto out;
-                       break;
+       case Opt_fsid:
+               err = parse_fsid(param->string, &opt->fsid);
+               if (err) {
+                       errorf(fc, "libceph: Failed to parse fsid: %d", err);
+                       return err;
+               }
+               opt->flags |= CEPH_OPT_FSID;
+               break;
+       case Opt_name:
+               kfree(opt->name);
+               opt->name = param->string;
+               param->string = NULL;
+               break;
+       case Opt_secret:
+               ceph_crypto_key_destroy(opt->key);
+               kfree(opt->key);
 
-                       /* misc */
-               case Opt_osdtimeout:
-                       pr_warn("ignoring deprecated osdtimeout option\n");
-                       break;
-               case Opt_osdkeepalivetimeout:
-                       /* 0 isn't well defined right now, reject it */
-                       if (intval < 1 || intval > INT_MAX / 1000) {
-                               pr_err("osdkeepalive out of range\n");
-                               err = -EINVAL;
-                               goto out;
-                       }
-                       opt->osd_keepalive_timeout =
-                                       msecs_to_jiffies(intval * 1000);
-                       break;
-               case Opt_osd_idle_ttl:
-                       /* 0 isn't well defined right now, reject it */
-                       if (intval < 1 || intval > INT_MAX / 1000) {
-                               pr_err("osd_idle_ttl out of range\n");
-                               err = -EINVAL;
-                               goto out;
-                       }
-                       opt->osd_idle_ttl = msecs_to_jiffies(intval * 1000);
-                       break;
-               case Opt_mount_timeout:
-                       /* 0 is "wait forever" (i.e. infinite timeout) */
-                       if (intval < 0 || intval > INT_MAX / 1000) {
-                               pr_err("mount_timeout out of range\n");
-                               err = -EINVAL;
-                               goto out;
-                       }
-                       opt->mount_timeout = msecs_to_jiffies(intval * 1000);
-                       break;
-               case Opt_osd_request_timeout:
-                       /* 0 is "wait forever" (i.e. infinite timeout) */
-                       if (intval < 0 || intval > INT_MAX / 1000) {
-                               pr_err("osd_request_timeout out of range\n");
-                               err = -EINVAL;
-                               goto out;
-                       }
-                       opt->osd_request_timeout = msecs_to_jiffies(intval * 1000);
-                       break;
+               opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
+               if (!opt->key)
+                       return -ENOMEM;
+               err = ceph_crypto_key_unarmor(opt->key, param->string);
+               if (err) {
+                       errorf(fc, "libceph: Failed to parse secret: %d", err);
+                       return err;
+               }
+               break;
+       case Opt_key:
+               ceph_crypto_key_destroy(opt->key);
+               kfree(opt->key);
 
-               case Opt_share:
+               opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
+               if (!opt->key)
+                       return -ENOMEM;
+               return get_secret(opt->key, param->string, fc);
+
+       case Opt_osdtimeout:
+               warnf(fc, "libceph: Ignoring osdtimeout");
+               break;
+       case Opt_osdkeepalivetimeout:
+               /* 0 isn't well defined right now, reject it */
+               if (result.uint_32 < 1 || result.uint_32 > INT_MAX / 1000)
+                       goto out_of_range;
+               opt->osd_keepalive_timeout =
+                   msecs_to_jiffies(result.uint_32 * 1000);
+               break;
+       case Opt_osd_idle_ttl:
+               /* 0 isn't well defined right now, reject it */
+               if (result.uint_32 < 1 || result.uint_32 > INT_MAX / 1000)
+                       goto out_of_range;
+               opt->osd_idle_ttl = msecs_to_jiffies(result.uint_32 * 1000);
+               break;
+       case Opt_mount_timeout:
+               /* 0 is "wait forever" (i.e. infinite timeout) */
+               if (result.uint_32 > INT_MAX / 1000)
+                       goto out_of_range;
+               opt->mount_timeout = msecs_to_jiffies(result.uint_32 * 1000);
+               break;
+       case Opt_osd_request_timeout:
+               /* 0 is "wait forever" (i.e. infinite timeout) */
+               if (result.uint_32 > INT_MAX / 1000)
+                       goto out_of_range;
+               opt->osd_request_timeout =
+                   msecs_to_jiffies(result.uint_32 * 1000);
+               break;
+
+       case Opt_share:
+               if (!result.negated)
                        opt->flags &= ~CEPH_OPT_NOSHARE;
-                       break;
-               case Opt_noshare:
+               else
                        opt->flags |= CEPH_OPT_NOSHARE;
-                       break;
-
-               case Opt_crc:
+               break;
+       case Opt_crc:
+               if (!result.negated)
                        opt->flags &= ~CEPH_OPT_NOCRC;
-                       break;
-               case Opt_nocrc:
+               else
                        opt->flags |= CEPH_OPT_NOCRC;
-                       break;
-
-               case Opt_cephx_require_signatures:
+               break;
+       case Opt_cephx_require_signatures:
+               if (!result.negated)
                        opt->flags &= ~CEPH_OPT_NOMSGAUTH;
-                       break;
-               case Opt_nocephx_require_signatures:
+               else
                        opt->flags |= CEPH_OPT_NOMSGAUTH;
-                       break;
-               case Opt_cephx_sign_messages:
+               break;
+       case Opt_cephx_sign_messages:
+               if (!result.negated)
                        opt->flags &= ~CEPH_OPT_NOMSGSIGN;
-                       break;
-               case Opt_nocephx_sign_messages:
+               else
                        opt->flags |= CEPH_OPT_NOMSGSIGN;
-                       break;
-
-               case Opt_tcp_nodelay:
+               break;
+       case Opt_tcp_nodelay:
+               if (!result.negated)
                        opt->flags |= CEPH_OPT_TCP_NODELAY;
-                       break;
-               case Opt_notcp_nodelay:
+               else
                        opt->flags &= ~CEPH_OPT_TCP_NODELAY;
-                       break;
+               break;
 
-               case Opt_abort_on_full:
-                       opt->flags |= CEPH_OPT_ABORT_ON_FULL;
-                       break;
+       case Opt_abort_on_full:
+               opt->flags |= CEPH_OPT_ABORT_ON_FULL;
+               break;
 
-               default:
-                       BUG_ON(token);
-               }
+       default:
+               BUG();
        }
 
-       /* success */
-       return opt;
+       return 0;
 
-out:
-       ceph_destroy_options(opt);
-       return ERR_PTR(err);
+out_of_range:
+       return invalf(fc, "libceph: %s out of range", param->key);
 }
-EXPORT_SYMBOL(ceph_parse_options);
+EXPORT_SYMBOL(ceph_parse_param);
 
 int ceph_print_client_options(struct seq_file *m, struct ceph_client *client,
                              bool show_all)
index e4cb3db..5b4bd82 100644 (file)
@@ -2004,10 +2004,8 @@ int ceph_parse_ips(const char *c, const char *end,
        return 0;
 
 bad:
-       pr_err("parse_ips bad ip '%.*s'\n", (int)(end - c), c);
        return ret;
 }
-EXPORT_SYMBOL(ceph_parse_ips);
 
 static int process_banner(struct ceph_connection *con)
 {
index 7256c40..9d9e4e4 100644 (file)
@@ -1233,9 +1233,6 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg)
        struct ceph_mon_client *monc = con->private;
        int type = le16_to_cpu(msg->hdr.type);
 
-       if (!monc)
-               return;
-
        switch (type) {
        case CEPH_MSG_AUTH_REPLY:
                handle_auth_reply(monc, msg);
index ea28cbb..b343db1 100644 (file)
@@ -1826,26 +1826,22 @@ SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,
  *     include the -EINPROGRESS status for such sockets.
  */
 
-int __sys_connect_file(struct file *file, struct sockaddr __user *uservaddr,
+int __sys_connect_file(struct file *file, struct sockaddr_storage *address,
                       int addrlen, int file_flags)
 {
        struct socket *sock;
-       struct sockaddr_storage address;
        int err;
 
        sock = sock_from_file(file, &err);
        if (!sock)
                goto out;
-       err = move_addr_to_kernel(uservaddr, addrlen, &address);
-       if (err < 0)
-               goto out;
 
        err =
-           security_socket_connect(sock, (struct sockaddr *)&address, addrlen);
+           security_socket_connect(sock, (struct sockaddr *)address, addrlen);
        if (err)
                goto out;
 
-       err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,
+       err = sock->ops->connect(sock, (struct sockaddr *)address, addrlen,
                                 sock->file->f_flags | file_flags);
 out:
        return err;
@@ -1858,7 +1854,11 @@ int __sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen)
 
        f = fdget(fd);
        if (f.file) {
-               ret = __sys_connect_file(f.file, uservaddr, addrlen, 0);
+               struct sockaddr_storage address;
+
+               ret = move_addr_to_kernel(uservaddr, addrlen, &address);
+               if (!ret)
+                       ret = __sys_connect_file(f.file, &address, addrlen, 0);
                if (f.flags)
                        fput(f.file);
        }
@@ -2346,9 +2346,9 @@ out:
        return err;
 }
 
-static int sendmsg_copy_msghdr(struct msghdr *msg,
-                              struct user_msghdr __user *umsg, unsigned flags,
-                              struct iovec **iov)
+int sendmsg_copy_msghdr(struct msghdr *msg,
+                       struct user_msghdr __user *umsg, unsigned flags,
+                       struct iovec **iov)
 {
        int err;
 
@@ -2390,27 +2390,14 @@ static int ___sys_sendmsg(struct socket *sock, struct user_msghdr __user *msg,
 /*
  *     BSD sendmsg interface
  */
-long __sys_sendmsg_sock(struct socket *sock, struct user_msghdr __user *umsg,
+long __sys_sendmsg_sock(struct socket *sock, struct msghdr *msg,
                        unsigned int flags)
 {
-       struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
-       struct sockaddr_storage address;
-       struct msghdr msg = { .msg_name = &address };
-       ssize_t err;
-
-       err = sendmsg_copy_msghdr(&msg, umsg, flags, &iov);
-       if (err)
-               return err;
        /* disallow ancillary data requests from this path */
-       if (msg.msg_control || msg.msg_controllen) {
-               err = -EINVAL;
-               goto out;
-       }
+       if (msg->msg_control || msg->msg_controllen)
+               return -EINVAL;
 
-       err = ____sys_sendmsg(sock, &msg, flags, NULL, 0);
-out:
-       kfree(iov);
-       return err;
+       return ____sys_sendmsg(sock, msg, flags, NULL, 0);
 }
 
 long __sys_sendmsg(int fd, struct user_msghdr __user *msg, unsigned int flags,
@@ -2516,10 +2503,10 @@ SYSCALL_DEFINE4(sendmmsg, int, fd, struct mmsghdr __user *, mmsg,
        return __sys_sendmmsg(fd, mmsg, vlen, flags, true);
 }
 
-static int recvmsg_copy_msghdr(struct msghdr *msg,
-                              struct user_msghdr __user *umsg, unsigned flags,
-                              struct sockaddr __user **uaddr,
-                              struct iovec **iov)
+int recvmsg_copy_msghdr(struct msghdr *msg,
+                       struct user_msghdr __user *umsg, unsigned flags,
+                       struct sockaddr __user **uaddr,
+                       struct iovec **iov)
 {
        ssize_t err;
 
@@ -2609,28 +2596,15 @@ static int ___sys_recvmsg(struct socket *sock, struct user_msghdr __user *msg,
  *     BSD recvmsg interface
  */
 
-long __sys_recvmsg_sock(struct socket *sock, struct user_msghdr __user *umsg,
-                       unsigned int flags)
+long __sys_recvmsg_sock(struct socket *sock, struct msghdr *msg,
+                       struct user_msghdr __user *umsg,
+                       struct sockaddr __user *uaddr, unsigned int flags)
 {
-       struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
-       struct sockaddr_storage address;
-       struct msghdr msg = { .msg_name = &address };
-       struct sockaddr __user *uaddr;
-       ssize_t err;
-
-       err = recvmsg_copy_msghdr(&msg, umsg, flags, &uaddr, &iov);
-       if (err)
-               return err;
        /* disallow ancillary data requests from this path */
-       if (msg.msg_control || msg.msg_controllen) {
-               err = -EINVAL;
-               goto out;
-       }
+       if (msg->msg_control || msg->msg_controllen)
+               return -EINVAL;
 
-       err = ____sys_recvmsg(sock, &msg, umsg, uaddr, flags, 0);
-out:
-       kfree(iov);
-       return err;
+       return ____sys_recvmsg(sock, msg, umsg, uaddr, flags, 0);
 }
 
 long __sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned int flags,
index 2045697..797d838 100644 (file)
@@ -107,6 +107,8 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
                }
        }
 #endif
+       if (frames > dst_channels[0].frames)
+               frames = dst_channels[0].frames;
        convert(plugin, src_channels, dst_channels, frames);
        return frames;
 }
index 7915564..3788906 100644 (file)
@@ -269,6 +269,8 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin,
                }
        }
 #endif
+       if (frames > dst_channels[0].frames)
+               frames = dst_channels[0].frames;
        data = (struct mulaw_priv *)plugin->extra_data;
        data->func(plugin, src_channels, dst_channels, frames);
        return frames;
index c8171f5..72dea04 100644 (file)
@@ -57,6 +57,8 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin,
                return -ENXIO;
        if (frames == 0)
                return 0;
+       if (frames > dst_channels[0].frames)
+               frames = dst_channels[0].frames;
 
        nsrcs = plugin->src_format.channels;
        ndsts = plugin->dst_format.channels;
index 0ebfbe7..6bb4642 100644 (file)
@@ -727,10 +727,6 @@ static void loopback_snd_timer_period_elapsed(struct loopback_cable *cable,
 
        dpcm_play = cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
        dpcm_capt = cable->streams[SNDRV_PCM_STREAM_CAPTURE];
-       substream_play = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
-                       dpcm_play->substream : NULL;
-       substream_capt = (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) ?
-                       dpcm_capt->substream : NULL;
 
        if (event == SNDRV_TIMER_EVENT_MSTOP) {
                if (!dpcm_play ||
@@ -741,6 +737,10 @@ static void loopback_snd_timer_period_elapsed(struct loopback_cable *cable,
                }
        }
 
+       substream_play = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
+                       dpcm_play->substream : NULL;
+       substream_capt = (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) ?
+                       dpcm_capt->substream : NULL;
        valid_runtime = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
                                dpcm_play->substream->runtime :
                                dpcm_capt->substream->runtime;
index d8fe7ff..f9707fb 100644 (file)
@@ -96,12 +96,14 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start)
                              1 << azx_dev->index,
                              1 << azx_dev->index);
        /* set stripe control */
-       if (azx_dev->substream)
-               stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream);
-       else
-               stripe_ctl = 0;
-       snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
-                               stripe_ctl);
+       if (azx_dev->stripe) {
+               if (azx_dev->substream)
+                       stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream);
+               else
+                       stripe_ctl = 0;
+               snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
+                                       stripe_ctl);
+       }
        /* set DMA start and interrupt mask */
        snd_hdac_stream_updateb(azx_dev, SD_CTL,
                                0, SD_CTL_DMA_START | SD_INT_MASK);
@@ -118,7 +120,10 @@ void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
        snd_hdac_stream_updateb(azx_dev, SD_CTL,
                                SD_CTL_DMA_START | SD_INT_MASK, 0);
        snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
-       snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
+       if (azx_dev->stripe) {
+               snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
+               azx_dev->stripe = 0;
+       }
        azx_dev->running = false;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_stream_clear);
index e76a0bb..35b4526 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/clocksource.h>
 #include <linux/time.h>
 #include <linux/completion.h>
+#include <linux/acpi.h>
 
 #ifdef CONFIG_X86
 /* for snoop control */
@@ -1401,6 +1402,34 @@ static int azx_dev_free(struct snd_device *device)
 }
 
 #ifdef SUPPORT_VGA_SWITCHEROO
+#ifdef CONFIG_ACPI
+/* ATPX is in the integrated GPU's namespace */
+static bool atpx_present(void)
+{
+       struct pci_dev *pdev = NULL;
+       acpi_handle dhandle, atpx_handle;
+       acpi_status status;
+
+       while ((pdev = pci_get_class(PCI_BASE_CLASS_DISPLAY << 16, pdev)) != NULL) {
+               dhandle = ACPI_HANDLE(&pdev->dev);
+               if (dhandle) {
+                       status = acpi_get_handle(dhandle, "ATPX", &atpx_handle);
+                       if (!ACPI_FAILURE(status)) {
+                               pci_dev_put(pdev);
+                               return true;
+                       }
+               }
+               pci_dev_put(pdev);
+       }
+       return false;
+}
+#else
+static bool atpx_present(void)
+{
+       return false;
+}
+#endif
+
 /*
  * Check of disabled HDMI controller by vga_switcheroo
  */
@@ -1412,6 +1441,22 @@ static struct pci_dev *get_bound_vga(struct pci_dev *pci)
        switch (pci->vendor) {
        case PCI_VENDOR_ID_ATI:
        case PCI_VENDOR_ID_AMD:
+               if (pci->devfn == 1) {
+                       p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus),
+                                                       pci->bus->number, 0);
+                       if (p) {
+                               /* ATPX is in the integrated GPU's ACPI namespace
+                                * rather than the dGPU's namespace. However,
+                                * the dGPU is the one who is involved in
+                                * vgaswitcheroo.
+                                */
+                               if (((p->class >> 16) == PCI_BASE_CLASS_DISPLAY) &&
+                                   atpx_present())
+                                       return p;
+                               pci_dev_put(p);
+                       }
+               }
+               break;
        case PCI_VENDOR_ID_NVIDIA:
                if (pci->devfn == 1) {
                        p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus),
@@ -2547,13 +2592,38 @@ static const struct pci_device_id azx_ids[] = {
        { PCI_DEVICE(0x1002, 0xaac8),
          .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
        { PCI_DEVICE(0x1002, 0xaad8),
-         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
-       { PCI_DEVICE(0x1002, 0xaae8),
-         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
        { PCI_DEVICE(0x1002, 0xaae0),
-         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
+       { PCI_DEVICE(0x1002, 0xaae8),
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
        { PCI_DEVICE(0x1002, 0xaaf0),
-         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
+       { PCI_DEVICE(0x1002, 0xaaf8),
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
+       { PCI_DEVICE(0x1002, 0xab00),
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
+       { PCI_DEVICE(0x1002, 0xab08),
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
+       { PCI_DEVICE(0x1002, 0xab10),
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
+       { PCI_DEVICE(0x1002, 0xab18),
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
+       { PCI_DEVICE(0x1002, 0xab20),
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
+       { PCI_DEVICE(0x1002, 0xab38),
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
        /* VIA VT8251/VT8237A */
        { PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA },
        /* VIA GFX VT7122/VX900 */
index bffde59..78647ee 100644 (file)
@@ -32,6 +32,7 @@
 #include <sound/hda_codec.h>
 #include "hda_local.h"
 #include "hda_jack.h"
+#include "hda_controller.h"
 
 static bool static_hdmi_pcm;
 module_param(static_hdmi_pcm, bool, 0644);
@@ -1249,6 +1250,10 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
        per_pin->cvt_nid = per_cvt->cvt_nid;
        hinfo->nid = per_cvt->cvt_nid;
 
+       /* flip stripe flag for the assigned stream if supported */
+       if (get_wcaps(codec, per_cvt->cvt_nid) & AC_WCAP_STRIPE)
+               azx_stream(get_azx_dev(substream))->stripe = 1;
+
        snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id);
        snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
                            AC_VERB_SET_CONNECT_SEL,
@@ -1302,6 +1307,7 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
        struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
        hda_nid_t pin_nid = per_pin->pin_nid;
        int dev_id = per_pin->dev_id;
+       int conns;
 
        if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
                codec_warn(codec,
@@ -1312,10 +1318,18 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
 
        snd_hda_set_dev_select(codec, pin_nid, dev_id);
 
+       if (spec->intel_hsw_fixup) {
+               conns = spec->num_cvts;
+               memcpy(per_pin->mux_nids, spec->cvt_nids,
+                      sizeof(hda_nid_t) * conns);
+       } else {
+               conns = snd_hda_get_raw_connections(codec, pin_nid,
+                                                   per_pin->mux_nids,
+                                                   HDA_MAX_CONNECTIONS);
+       }
+
        /* all the device entries on the same pin have the same conn list */
-       per_pin->num_mux_nids =
-               snd_hda_get_raw_connections(codec, pin_nid, per_pin->mux_nids,
-                                           HDA_MAX_CONNECTIONS);
+       per_pin->num_mux_nids = conns;
 
        return 0;
 }
@@ -1326,24 +1340,26 @@ static int hdmi_find_pcm_slot(struct hdmi_spec *spec,
        int i;
 
        /*
-        * generic_hdmi_build_pcms() allocates (num_nids + dev_num - 1)
-        * number of pcms.
+        * generic_hdmi_build_pcms() may allocate extra PCMs on some
+        * platforms (with maximum of 'num_nids + dev_num - 1')
         *
         * The per_pin of pin_nid_idx=n and dev_id=m prefers to get pcm-n
         * if m==0. This guarantees that dynamic pcm assignments are compatible
-        * with the legacy static per_pin-pmc assignment that existed in the
+        * with the legacy static per_pin-pcm assignment that existed in the
         * days before DP-MST.
         *
+        * Intel DP-MST prefers this legacy behavior for compatibility, too.
+        *
         * per_pin of m!=0 prefers to get pcm=(num_nids + (m - 1)).
         */
-       if (per_pin->dev_id == 0 &&
-           !test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
-               return per_pin->pin_nid_idx;
-
-       if (per_pin->dev_id != 0 &&
-           !(test_bit(spec->num_nids + (per_pin->dev_id - 1),
-               &spec->pcm_bitmap))) {
-               return spec->num_nids + (per_pin->dev_id - 1);
+
+       if (per_pin->dev_id == 0 || spec->intel_hsw_fixup) {
+               if (!test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
+                       return per_pin->pin_nid_idx;
+       } else {
+               i = spec->num_nids + (per_pin->dev_id - 1);
+               if (i < spec->pcm_used && !(test_bit(i, &spec->pcm_bitmap)))
+                       return i;
        }
 
        /* have a second try; check the area over num_nids */
@@ -1713,9 +1729,6 @@ static void hdmi_repoll_eld(struct work_struct *work)
        mutex_unlock(&spec->pcm_lock);
 }
 
-static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
-                                            hda_nid_t nid);
-
 static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
 {
        struct hdmi_spec *spec = codec->spec;
@@ -1790,8 +1803,6 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
                per_pin->dev_id = i;
                per_pin->non_pcm = false;
                snd_hda_set_dev_select(codec, pin_nid, i);
-               if (spec->intel_hsw_fixup)
-                       intel_haswell_fixup_connect_list(codec, pin_nid);
                err = hdmi_read_pin_conn(codec, pin_idx);
                if (err < 0)
                        return err;
@@ -2603,24 +2614,6 @@ static void generic_acomp_init(struct hda_codec *codec,
  * Intel codec parsers and helpers
  */
 
-static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
-                                            hda_nid_t nid)
-{
-       struct hdmi_spec *spec = codec->spec;
-       hda_nid_t conns[4];
-       int nconns;
-
-       nconns = snd_hda_get_raw_connections(codec, nid, conns,
-                                            ARRAY_SIZE(conns));
-       if (nconns == spec->num_cvts &&
-           !memcmp(conns, spec->cvt_nids, spec->num_cvts * sizeof(hda_nid_t)))
-               return;
-
-       /* override pins connection list */
-       codec_dbg(codec, "hdmi: haswell: override pin connection 0x%x\n", nid);
-       snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids);
-}
-
 #define INTEL_GET_VENDOR_VERB  0xf81
 #define INTEL_SET_VENDOR_VERB  0x781
 #define INTEL_EN_DP12          0x02    /* enable DP 1.2 features */
@@ -4063,6 +4056,7 @@ static int atihdmi_init(struct hda_codec *codec)
                                            ATI_VERB_SET_MULTICHANNEL_MODE,
                                            ATI_MULTICHANNEL_MODE_SINGLE);
        }
+       codec->auto_runtime_pm = 1;
 
        return 0;
 }
index d2bf70a..6d6e34b 100644 (file)
@@ -367,9 +367,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
        case 0x10ec0215:
        case 0x10ec0233:
        case 0x10ec0235:
-       case 0x10ec0236:
        case 0x10ec0255:
-       case 0x10ec0256:
        case 0x10ec0257:
        case 0x10ec0282:
        case 0x10ec0283:
@@ -381,6 +379,11 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
        case 0x10ec0300:
                alc_update_coef_idx(codec, 0x10, 1<<9, 0);
                break;
+       case 0x10ec0236:
+       case 0x10ec0256:
+               alc_write_coef_idx(codec, 0x36, 0x5757);
+               alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+               break;
        case 0x10ec0275:
                alc_update_coef_idx(codec, 0xe, 0, 1<<0);
                break;
@@ -5544,6 +5547,16 @@ static void alc295_fixup_disable_dac3(struct hda_codec *codec,
        }
 }
 
+/* force NID 0x17 (Bass Speaker) to DAC1 to share it with the main speaker */
+static void alc285_fixup_speaker2_to_dac1(struct hda_codec *codec,
+                                         const struct hda_fixup *fix, int action)
+{
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+               hda_nid_t conn[1] = { 0x02 };
+               snd_hda_override_conn_list(codec, 0x17, 1, conn);
+       }
+}
+
 /* Hook to update amp GPIO4 for automute */
 static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec,
                                          struct hda_jack_callback *jack)
@@ -5846,6 +5859,7 @@ enum {
        ALC225_FIXUP_DISABLE_MIC_VREF,
        ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
        ALC295_FIXUP_DISABLE_DAC3,
+       ALC285_FIXUP_SPEAKER2_TO_DAC1,
        ALC280_FIXUP_HP_HEADSET_MIC,
        ALC221_FIXUP_HP_FRONT_MIC,
        ALC292_FIXUP_TPT460,
@@ -6646,6 +6660,10 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc295_fixup_disable_dac3,
        },
+       [ALC285_FIXUP_SPEAKER2_TO_DAC1] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc285_fixup_speaker2_to_dac1,
+       },
        [ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = {
                .type = HDA_FIXUP_PINS,
                .v.pins = (const struct hda_pintbl[]) {
@@ -7221,6 +7239,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x2293, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_SPEAKER2_TO_DAC1),
        SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
        SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
        SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
@@ -7405,6 +7424,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
        {.id = ALC255_FIXUP_DELL_SPK_NOISE, .name = "dell-spk-noise"},
        {.id = ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc225-dell1"},
        {.id = ALC295_FIXUP_DISABLE_DAC3, .name = "alc295-disable-dac3"},
+       {.id = ALC285_FIXUP_SPEAKER2_TO_DAC1, .name = "alc285-speaker2-to-dac1"},
        {.id = ALC280_FIXUP_HP_HEADSET_MIC, .name = "alc280-hp-headset"},
        {.id = ALC221_FIXUP_HP_FRONT_MIC, .name = "alc221-hp-mic"},
        {.id = ALC298_FIXUP_SPK_VOLUME, .name = "alc298-spk-volume"},
@@ -8424,6 +8444,8 @@ static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec,
        case HDA_FIXUP_ACT_PRE_PROBE:
                snd_hda_jack_detect_enable_callback(codec, 0x1b,
                                alc662_aspire_ethos_mute_speakers);
+               /* subwoofer needs an extra GPIO setting to become audible */
+               alc_setup_gpio(codec, 0x02);
                break;
        case HDA_FIXUP_ACT_INIT:
                /* Make sure to start in a correct state, i.e. if
@@ -8506,7 +8528,6 @@ enum {
        ALC662_FIXUP_USI_HEADSET_MODE,
        ALC662_FIXUP_LENOVO_MULTI_CODECS,
        ALC669_FIXUP_ACER_ASPIRE_ETHOS,
-       ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER,
        ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET,
 };
 
@@ -8838,18 +8859,6 @@ static const struct hda_fixup alc662_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc662_fixup_aspire_ethos_hp,
        },
-       [ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER] = {
-               .type = HDA_FIXUP_VERBS,
-               /* subwoofer needs an extra GPIO setting to become audible */
-               .v.verbs = (const struct hda_verb[]) {
-                       {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
-                       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
-                       {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
-                       { }
-               },
-               .chained = true,
-               .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET
-       },
        [ALC669_FIXUP_ACER_ASPIRE_ETHOS] = {
                .type = HDA_FIXUP_PINS,
                .v.pins = (const struct hda_pintbl[]) {
@@ -8859,7 +8868,7 @@ static const struct hda_fixup alc662_fixups[] = {
                        { }
                },
                .chained = true,
-               .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER
+               .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET
        },
 };